Remote deploy console: stream agent compose up to the browser (0.23.0)

Extends the live deploy console to remote/agent stacks. New agent WS endpoint
`/agent/ws/deploy/{stack_id}` runs `compose up -d` and streams its output; the
central app proxies it through `/ws/agent-deploy/{agent_id}/{stack_id}` (same
pattern + token URL-encoding as the agent-logs proxy) and records an
`agent.stack.start` audit entry. The editor's remote Deploy path now opens the
DeployConsole (agentId) instead of the blocking `agentsApi.action(start)`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
menzelj
2026-06-08 19:44:31 +00:00
co-authored by Claude Opus 4.8
parent 7592085ce9
commit d46a6c3576
7 changed files with 140 additions and 11 deletions
+16 -6
View File
@@ -42,6 +42,7 @@ export function StackEditor() {
const [checking, setChecking] = useState(false);
const [host, setHost] = useState("local");
const [deployId, setDeployId] = useState<string | null>(null);
const [deployAgentId, setDeployAgentId] = useState<number | undefined>(undefined);
const existing = useQuery({
queryKey: ["stack", id],
@@ -80,9 +81,10 @@ export function StackEditor() {
qc.invalidateQueries({ queryKey: ["agent-stacks", aid] });
toast.success("Saved");
if (deploy) {
const t = toast.loading("Deploying…");
await agentsApi.action(aid, created.id, "start");
toast.success("Deployed ✓", { id: t });
// Stream the remote deploy live through the agent-deploy WS proxy.
setDeployAgentId(aid);
setDeployId(created.id);
return;
}
navigate(`/hosts/${aid}/stacks/${created.id}`);
return;
@@ -251,12 +253,20 @@ export function StackEditor() {
{deployId && (
<DeployConsole
stackId={deployId}
agentId={deployAgentId}
onClose={() => {
const sid = deployId;
const aid = deployAgentId;
setDeployId(null);
qc.invalidateQueries({ queryKey: ["stacks"] });
qc.invalidateQueries({ queryKey: ["stack", sid] });
navigate(`/stacks/${sid}`);
setDeployAgentId(undefined);
if (aid != null) {
qc.invalidateQueries({ queryKey: ["agent-stacks", aid] });
navigate(`/hosts/${aid}/stacks/${sid}`);
} else {
qc.invalidateQueries({ queryKey: ["stacks"] });
qc.invalidateQueries({ queryKey: ["stack", sid] });
navigate(`/stacks/${sid}`);
}
}}
/>
)}