huskies: merge 921

This commit is contained in:
dave
2026-05-12 21:04:33 +00:00
parent 69d91d7707
commit 93443e2ff1
3 changed files with 101 additions and 6 deletions
+4 -4
View File
@@ -547,8 +547,9 @@ fn handle_agents_list_tool(id: Option<Value>) -> JsonRpcResponse {
)
}
/// Handle the `pipeline.get` read-RPC — returns the same shape as the old
/// `GET /api/gateway/pipeline` endpoint: `{ "active": "...", "projects": {...} }`.
/// 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 } } }`.
async fn handle_pipeline_get(state: &GatewayState, id: Option<Value>) -> JsonRpcResponse {
let project_urls: BTreeMap<String, String> = state
.projects
@@ -558,8 +559,7 @@ async fn handle_pipeline_get(state: &GatewayState, id: Option<Value>) -> JsonRpc
.map(|(n, e)| (n.clone(), e.url.clone()))
.collect();
let results =
gateway::io::fetch_all_project_pipeline_statuses(&project_urls, &state.client).await;
let results = gateway::io::fetch_all_project_pipeline_items(&project_urls, &state.client).await;
let active = state.active_project.read().await.clone();
JsonRpcResponse::success(id, json!({ "active": active, "projects": results }))
+76
View File
@@ -215,6 +215,82 @@ pub async fn fetch_all_project_pipeline_statuses(
join_all(futures).await.into_iter().collect()
}
/// Fetch pipeline items for a single project URL.
///
/// Returns `{ "active": [...], "backlog_count": N }` preserving individual
/// story items so the gateway UI can render them. On error returns
/// `{ "error": "..." }`. This is distinct from
/// `fetch_one_project_pipeline_status` which discards items and returns
/// aggregated counts.
pub async fn fetch_one_project_pipeline_items(url: &str, client: &Client) -> Value {
let mcp_url = format!("{}/mcp", url.trim_end_matches('/'));
let rpc_body = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_pipeline_status",
"arguments": {}
}
});
match client.post(&mcp_url).json(&rpc_body).send().await {
Ok(resp) => match resp.json::<Value>().await {
Ok(upstream) => {
if let Some(text) = upstream
.get("result")
.and_then(|r| r.get("content"))
.and_then(|c| c.get(0))
.and_then(|c| c.get("text"))
.and_then(|t| t.as_str())
{
match serde_json::from_str::<Value>(text) {
Ok(pipeline) => {
let active = pipeline.get("active").cloned().unwrap_or(json!([]));
let backlog_count = pipeline
.get("backlog_count")
.and_then(|n| n.as_u64())
.unwrap_or(0);
json!({ "active": active, "backlog_count": backlog_count })
}
Err(_) => json!({ "error": "invalid pipeline JSON" }),
}
} else {
json!({ "error": "unexpected response shape" })
}
}
Err(e) => json!({ "error": format!("invalid response: {e}") }),
},
Err(e) => json!({ "error": format!("unreachable: {e}") }),
}
}
/// Fetch pipeline items from every registered project URL in parallel.
///
/// Returns per-project `{ "active": [...], "backlog_count": N }` objects
/// suitable for the gateway web UI.
pub async fn fetch_all_project_pipeline_items(
project_urls: &BTreeMap<String, String>,
client: &Client,
) -> BTreeMap<String, Value> {
use futures::future::join_all;
let futures: Vec<_> = project_urls
.iter()
.map(|(name, url)| {
let name = name.clone();
let url = url.clone();
let client = client.clone();
async move {
let result = fetch_one_project_pipeline_items(&url, &client).await;
(name, result)
}
})
.collect();
join_all(futures).await.into_iter().collect()
}
/// Fetch the pipeline status from a single project for the `gateway_status` tool.
pub async fn fetch_pipeline_status_for_project(
client: &Client,