fix: add SSH key selection to workspace tool starter + docker compose policy

tool-starter.tsx was hardcoding ssh_key_ids=[] and only showing a read-only
SSH key status. Users couldn't select keys when starting tools from workspaces.

Changes:
- tool-starter.tsx: add checkboxes for SSH key selection with repo key
  pre-selected, pass selected keys to createInstance/startInstance
- AGENTS.md: add explicit rule forbidding docker compose commands without
  user approval and proper isolation

The web container must be rebuilt to pick up the frontend changes:
  docker compose up -d --build web

Quality gates: tsc clean, pytest (19 passed, 1 skipped)
This commit is contained in:
Alex Blank
2026-06-02 15:40:20 +02:00
parent 37134b8c18
commit fc75eeb76d
3 changed files with 61 additions and 23 deletions
+55 -16
View File
@@ -31,6 +31,7 @@ export function ToolStarter({
const [sshKeys, setSshKeys] = useState<SSHKey[]>([]);
const [sshKeysLoading, setSshKeysLoading] = useState(true);
const [selectedSshKeyIds, setSelectedSshKeyIds] = useState<string[]>([]);
const [starting, setStarting] = useState(false);
const [error, setError] = useState<string | null>(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({
</div>
)}
{/* SSH Key Status */}
<div className="form-group ssh-key-status">
<label>SSH Key</label>
{/* SSH Key Selection */}
<div className="form-group ssh-key-selection">
<label>SSH Keys</label>
{sshKeysLoading ? (
<span className="muted">Checking...</span>
) : repoHasSshKey ? (
<span className="success-text">
<Icon name="success" size="sm" />{" "}
{repoSshKey?.name || "SSH key assigned"}
</span>
<span className="muted">Loading SSH keys...</span>
) : sshKeys.length === 0 ? (
<span className="muted">No SSH keys configured.</span>
) : (
<span className="warning-text">
<Icon name="warning" size="sm" /> No SSH key assigned to repository
</span>
<div style={{ display: "flex", flexWrap: "wrap", gap: "0.5rem" }}>
{sshKeys.map((key) => (
<label
key={key.id}
className="checkbox-label"
style={{
display: "flex",
alignItems: "center",
gap: "0.25rem",
padding: "0.375rem 0.75rem",
background: "var(--panel)",
borderRadius: "0.375rem",
border: "1px solid var(--border)",
cursor: "pointer",
}}
>
<input
type="checkbox"
checked={selectedSshKeyIds.includes(key.id)}
onChange={(e) => {
if (e.target.checked) {
setSelectedSshKeyIds((prev) => [...prev, key.id]);
} else {
setSelectedSshKeyIds((prev) =>
prev.filter((id) => id !== key.id),
);
}
}}
disabled={starting}
/>
{key.name}
</label>
))}
</div>
)}
{!sshKeysLoading && repoHasSshKey && repoSshKey && (
<div className="hint" style={{ marginTop: "0.5rem" }}>
Repository key <strong>{repoSshKey.name}</strong> is pre-selected.
</div>
)}
</div>