Fleet tools: one-click per-node update (Phase 2)
The ↑ badge on each tool card is now a button: confirm → POST
/api/nodes/{id}/tools/{tool}/update → daemon runs the tool's own updater + re-probes.
- daemon: tool_update op (spawned task so the 170s update can't stall the WS loop;
re-probes + re-sends node_tools after). Fixed command allow-list (no arbitrary
shell): claude/glm → `claude update`; kimi → `uv tool upgrade kimi-cli`; ollama →
brew upgrade (mac) / install.sh (linux); else unsupported. 4KB output cap.
- cm-api: call_timeout/request_timeout (long ops); POST .../tools/{tool}/update
(workspace-scoped, allow-list) → {ok,output}.
- frontend: ↑latest becomes an Update button → confirm → spinner → refresh/err.
Note: claude/kimi/glm are user-space (no sudo); ollama on Linux uses install.sh
(needs sudo — works on passwordless nodes, returns an error otherwise; surfaced in UI).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9263418fcb
commit
ed339c2121
@@ -219,6 +219,31 @@ pub async fn tools(
|
||||
Ok(Json(json!({ "tools": out })))
|
||||
}
|
||||
|
||||
/// `POST /api/nodes/{id}/tools/{tool}/update` — run the daemon's fixed update for a
|
||||
/// tool (claude/glm/kimi/ollama), then it re-probes so the version refreshes.
|
||||
pub async fn tool_update(
|
||||
State(state): State<AppState>,
|
||||
Authed(user): Authed,
|
||||
Path((id, tool)): Path<(Uuid, String)>,
|
||||
) -> Result<Json<Value>, ApiError> {
|
||||
let node_id = NodeId::from(id);
|
||||
nodes::get(&state.pool, node_id, user.workspace_id)
|
||||
.await?
|
||||
.ok_or(ApiError::NotFound)?;
|
||||
if !["claude", "glm", "kimi", "ollama"].contains(&tool.as_str()) {
|
||||
return Ok(Json(json!({ "ok": false, "output": "tool not updatable" })));
|
||||
}
|
||||
// ~180s: tool updates (npm/uv/brew/installer) legitimately run long.
|
||||
match state
|
||||
.node_hub
|
||||
.call_timeout(node_id, "tool_update", json!({ "tool": tool }), 180)
|
||||
.await
|
||||
{
|
||||
Ok(out) => Ok(Json(json!({ "ok": out.ok, "output": out.output }))),
|
||||
Err(e) => Ok(Json(json!({ "ok": false, "output": e }))),
|
||||
}
|
||||
}
|
||||
|
||||
/// `POST /api/nodes/{id}/terminal/ticket` — mint a single-use terminal ticket
|
||||
/// (the browser WS handshake can't carry a bearer header).
|
||||
pub async fn terminal_ticket(
|
||||
|
||||
Reference in New Issue
Block a user