huskies: merge 1140 story One-shot project-rebuild chat command: rebuild image, swap container, reconnect, preserve state

This commit is contained in:
dave
2026-05-18 14:50:04 +00:00
parent 4aaf7dbdc6
commit b1dec36e1c
6 changed files with 738 additions and 2 deletions
+74
View File
@@ -28,6 +28,8 @@ const GATEWAY_TOOLS: &[&str] = &[
"prompt_permission",
// Binary self-update: gateway serves its own binary and triggers upgrade on sleds.
"upgrade_sled",
// One-shot container rebuild: build fresh image, swap container, preserve state.
"project_rebuild",
];
/// Gateway tool definitions.
@@ -140,6 +142,28 @@ pub(crate) fn gateway_tool_definitions() -> Vec<Value> {
}
}
}),
json!({
"name": "project_rebuild",
"description": "Rebuild a project's Docker image from its Dockerfile.fragment, swap the container, and preserve all CRDT and pipeline state. In-flight coder/merge work is drained before the swap; if not drainable within the timeout the command refuses. On success returns the new image hash and container ID.",
"inputSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the project to rebuild (must exist in projects.toml with host_path set)."
},
"drain_timeout_secs": {
"type": "integer",
"description": "Seconds to wait for active agents to stop before rebuilding (default: 60). Pass 0 to skip the drain check."
},
"force": {
"type": "boolean",
"description": "If true, skip the drain check and rebuild immediately even if agents are running."
}
},
"required": ["name"]
}
}),
]
}
@@ -405,6 +429,7 @@ async fn handle_gateway_tool(
"agents.list" => handle_agents_list_tool(id),
"prompt_permission" => handle_prompt_permission_tool(params, state, id).await,
"upgrade_sled" => handle_upgrade_sled_tool(params, state, id).await,
"project_rebuild" => handle_project_rebuild_tool(params, state, id).await,
_ => JsonRpcResponse::error(id, -32601, format!("Unknown gateway tool: {tool_name}")),
}
}
@@ -876,6 +901,55 @@ async fn handle_upgrade_sled_tool(
}
}
/// Handle the `project_rebuild` gateway tool.
///
/// Rebuilds a project's Docker image, swaps the container, and preserves all
/// CRDT and pipeline state. Delegates to `handle_project_rebuild` in the chat
/// transport module so the logic is shared between the chat and MCP entry points.
async fn handle_project_rebuild_tool(
params: &Value,
state: &GatewayState,
id: Option<Value>,
) -> JsonRpcResponse {
use crate::chat::transport::matrix::project_rebuild::handle_project_rebuild;
let args = params.get("arguments").unwrap_or(params);
let name = args
.get("name")
.and_then(|v| v.as_str())
.unwrap_or("")
.trim();
if name.is_empty() {
return JsonRpcResponse::error(id, -32602, "missing required parameter: name".into());
}
let drain_timeout_secs = args
.get("drain_timeout_secs")
.and_then(|v| v.as_u64())
.unwrap_or(60);
let force = args.get("force").and_then(|v| v.as_bool()).unwrap_or(false);
let result = handle_project_rebuild(
name,
drain_timeout_secs,
force,
&state.projects,
&state.config_dir,
)
.await;
JsonRpcResponse::success(
id,
json!({
"content": [{
"type": "text",
"text": result
}]
}),
)
}
/// Handle the `pipeline.get` read-RPC — returns per-project item lists in the
/// shape expected by the gateway web UI:
/// `{ "active": "...", "projects": { "name": { "active": [...], "backlog_count": N } } }`.