diff --git a/AGENTS.md b/AGENTS.md index 635db70..5047fef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -75,6 +75,7 @@ Do not: * Introduce new dependencies without clear justification. * Treat existing code as more authoritative than OpenSpec for intended behavior. * Decide product behavior silently when the spec is unclear. +* Run `docker compose` commands (build, up, down, etc.) without explicit user approval and proper isolation (e.g., feature branches, separate worktrees, or staged rollouts). Docker Compose operations are deployment-level changes that can affect running services, shared volumes, and network state. Always ask first. If scope must change, propose an OpenSpec update first. diff --git a/apps/api/src/services/terminal_session.py b/apps/api/src/services/terminal_session.py index a824407..dff40cd 100644 --- a/apps/api/src/services/terminal_session.py +++ b/apps/api/src/services/terminal_session.py @@ -187,9 +187,7 @@ class TerminalSession: try: data = os.read(self._master_fd, 4096) except (OSError, IOError) as exc: - logger.debug( - "PTY read error for session %s: %s", self.session_id, exc - ) + logger.debug("PTY read error for session %s: %s", self.session_id, exc) self._handle_eof() return @@ -320,7 +318,9 @@ class TerminalSession: self._websockets.clear() for ws in dead_sockets: try: - asyncio.create_task(ws.close(code=4001, reason="Session process exited")) + asyncio.create_task( + ws.close(code=4001, reason="Session process exited") + ) except Exception: pass logger.info("Session %s EOF handled, websockets closed", self.session_id) @@ -333,9 +333,7 @@ class TerminalSession: os.write(self._master_fd, data) self.last_activity = time.time() except (OSError, IOError) as exc: - logger.debug( - "PTY write error for session %s: %s", self.session_id, exc - ) + logger.debug("PTY write error for session %s: %s", self.session_id, exc) self._handle_eof() def _set_terminal_size(self, cols: int, rows: int) -> None: diff --git a/apps/web/src/components/tool-starter.tsx b/apps/web/src/components/tool-starter.tsx index 80840f6..99caf99 100644 --- a/apps/web/src/components/tool-starter.tsx +++ b/apps/web/src/components/tool-starter.tsx @@ -31,6 +31,7 @@ export function ToolStarter({ const [sshKeys, setSshKeys] = useState([]); const [sshKeysLoading, setSshKeysLoading] = useState(true); + const [selectedSshKeyIds, setSelectedSshKeyIds] = useState([]); const [starting, setStarting] = useState(false); const [error, setError] = useState(null); @@ -89,14 +90,18 @@ export function ToolStarter({ try { const data = await listSSHKeys(); setSshKeys(data); - } catch { - // ignore + // Auto-select the repository's SSH key if available + if (workspace.repo_ssh_key_id) { + setSelectedSshKeyIds([workspace.repo_ssh_key_id]); + } + } catch (err) { + console.error("Failed to load SSH keys:", err); } finally { setSshKeysLoading(false); } }; void load(); - }, []); + }, [workspace.repo_ssh_key_id]); const repoHasSshKey = !!workspace.repo_ssh_key_id; const repoSshKey = sshKeys.find((k) => k.id === workspace.repo_ssh_key_id); @@ -119,7 +124,7 @@ export function ToolStarter({ undefined, undefined, selectedProfileId || undefined, - [], + selectedSshKeyIds.length > 0 ? selectedSshKeyIds : undefined, workspace.id, ); await startInstance( @@ -127,6 +132,7 @@ export function ToolStarter({ workspace.repo_id, instance.id, selectedProfileId || undefined, + selectedSshKeyIds.length > 0 ? selectedSshKeyIds : undefined, ); onStarted(instance); } catch (err) { @@ -208,20 +214,53 @@ export function ToolStarter({ )} - {/* SSH Key Status */} -
- + {/* SSH Key Selection */} +
+ {sshKeysLoading ? ( - Checking... - ) : repoHasSshKey ? ( - - {" "} - {repoSshKey?.name || "SSH key assigned"} - + Loading SSH keys... + ) : sshKeys.length === 0 ? ( + No SSH keys configured. ) : ( - - No SSH key assigned to repository - +
+ {sshKeys.map((key) => ( + + ))} +
+ )} + {!sshKeysLoading && repoHasSshKey && repoSshKey && ( +
+ Repository key {repoSshKey.name} is pre-selected. +
)}