huskies: merge 1140 story One-shot project-rebuild chat command: rebuild image, swap container, reconnect, preserve state
This commit is contained in:
@@ -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 } } }`.
|
||||
|
||||
Reference in New Issue
Block a user