-
Notifications
You must be signed in to change notification settings - Fork 0
feat: サブエージェント応答と中間テキストのSlack送信対応(トランスクリプト・カーソル方式) #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9c914be
57b2c61
69f06fd
934db67
fb05fd1
ac14977
04d9490
b888eca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| Run `aloud-code disable` to deactivate Slack streaming for this session. | ||
| Slack ストリーミングが無効になりました。 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| Run `aloud-code enable` to activate Slack streaming for this session. | ||
| Slack ストリーミングが有効になりました。 |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use anyhow::Result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use fs2::FileExt; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use serde::Deserialize; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::path::PathBuf; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -24,6 +25,71 @@ impl Config { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// セッションごとのカーソルロックガード | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// acquire() でファイルロックを取得し、commit() で更新・解放する | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub struct CursorLockGuard { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lock_file: std::fs::File, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| session_id: String, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl CursorLockGuard { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// ファイルロックを排他取得してカーソル値を返す | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// カーソルファイルが存在しない(初回有効化・再有効化後)場合は None を返す | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn acquire(session_id: &str) -> Result<(Self, Option<u64>)> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let dir = sessions_dir()?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::fs::create_dir_all(&dir)?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let lock_path = dir.join(format!("{}.cursor.lock", session_id)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let lock_file = std::fs::OpenOptions::new() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .create(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .write(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .truncate(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .open(&lock_path)?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lock_file.lock_exclusive()?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let cursor = read_cursor_inner(session_id); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CursorLockGuard { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lock_file, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| session_id: session_id.to_string(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+35
to
+52
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl CursorLockGuard { | |
| /// ファイルロックを排他取得してカーソル値を返す | |
| pub fn acquire(session_id: &str) -> Result<(Self, u64)> { | |
| let dir = sessions_dir()?; | |
| std::fs::create_dir_all(&dir)?; | |
| let lock_path = dir.join(format!("{}.cursor.lock", session_id)); | |
| let lock_file = std::fs::OpenOptions::new() | |
| .create(true) | |
| .write(true) | |
| .truncate(false) | |
| .open(&lock_path)?; | |
| lock_file.lock_exclusive()?; | |
| let cursor = read_cursor_inner(session_id); | |
| Ok(( | |
| CursorLockGuard { | |
| lock_file, | |
| session_id: session_id.to_string(), | |
| /// セッションIDをファイル名として安全に使用できる形式に正規化する | |
| /// - ASCII英数字と `-` / `_` のみを許可し、それ以外は `_` に置換する | |
| /// - すべて置換された場合は `_` を返す | |
| fn sanitize_session_id(session_id: &str) -> String { | |
| let mut sanitized: String = session_id | |
| .chars() | |
| .map(|c| { | |
| if c.is_ascii_alphanumeric() || c == '-' || c == '_' { | |
| c | |
| } else { | |
| '_' | |
| } | |
| }) | |
| .collect(); | |
| if sanitized.is_empty() { | |
| sanitized.push('_'); | |
| } | |
| sanitized | |
| } | |
| impl CursorLockGuard { | |
| /// ファイルロックを排他取得してカーソル値を返す | |
| pub fn acquire(session_id: &str) -> Result<(Self, u64)> { | |
| let safe_session_id = sanitize_session_id(session_id); | |
| let dir = sessions_dir()?; | |
| std::fs::create_dir_all(&dir)?; | |
| let lock_path = dir.join(format!("{}.cursor.lock", safe_session_id)); | |
| let lock_file = std::fs::OpenOptions::new() | |
| .create(true) | |
| .write(true) | |
| .truncate(false) | |
| .open(&lock_path)?; | |
| lock_file.lock_exclusive()?; | |
| let cursor = read_cursor_inner(&safe_session_id); | |
| Ok(( | |
| CursorLockGuard { | |
| lock_file, | |
| session_id: safe_session_id, |
Copilot
AI
Mar 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
deactivate()で.cursor.lock(ロック用ファイル)まで削除していますが、別プロセス/別フックがロックファイルを開いたままのタイミングだと、特にWindowsでは開いているファイルを削除できずPermissionDenied等でsession-endが失敗する可能性があります。ロックファイルは削除対象から外すか、削除失敗時は警告に留めて処理を継続するなど、プラットフォーム差分と並行実行を考慮した挙動にしてください。
| for path in &paths { | |
| match std::fs::remove_file(path) { | |
| Ok(()) => {} | |
| Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} | |
| for path in &paths { | |
| let is_lock_file = path | |
| .file_name() | |
| .and_then(|name| name.to_str()) | |
| .map(|name| name.ends_with(".cursor.lock")) | |
| .unwrap_or(false); | |
| match std::fs::remove_file(path) { | |
| Ok(()) => {} | |
| Err(e) if e.kind() == std::io::ErrorKind::NotFound => {} | |
| Err(e) if is_lock_file && e.kind() == std::io::ErrorKind::PermissionDenied => { | |
| eprintln!( | |
| "warning: failed to remove lock file {:?}: {}", | |
| path, | |
| e | |
| ); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
session_id をそのままファイル名に埋め込んで
join(format!("{}.cursor.lock", session_id))しているため、入力が../やパス区切りを含む場合に sessions_dir 外のパスへ到達できます(パストラバーサル)。セッションIDを安全な文字種にバリデート/正規化するか、ハッシュ化した固定長のファイル名を使うなどでファイルパス生成を安全にしてください。