From eba3c4a65b4f67b7e28c5c6e355bb239a4b3a156 Mon Sep 17 00:00:00 2001 From: James Pine Date: Fri, 3 Apr 2026 21:08:24 -0700 Subject: [PATCH 1/3] Fix remaining API route mismatches from OpenAPI migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `/channels/inspect` → `/channels/prompt/inspect` - `/channels/inspect/capture` → `/channels/prompt/capture` - `/channels/inspect/snapshots` → `/channels/prompt/snapshots` - `/channels/inspect/snapshot` → `/channels/prompt/snapshots/get` - `/channels/cancel` → `/channels/cancel-process` - `/update/check` → `/update-check` - `/update/apply` → `/update-apply` - `/groups` → `/links/groups` - `/humans` → `/links/humans` Also adds scripts/audit-routes.sh to diff frontend vs backend routes. Co-Authored-By: Claude Opus 4.6 (1M context) --- interface/src/api/client.ts | 32 +++++++-------- scripts/audit-routes.sh | 78 +++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 16 deletions(-) create mode 100755 scripts/audit-routes.sh diff --git a/interface/src/api/client.ts b/interface/src/api/client.ts index 3c2c610e9..6435b3e9c 100644 --- a/interface/src/api/client.ts +++ b/interface/src/api/client.ts @@ -1338,9 +1338,9 @@ export const api = { }, channelStatus: () => fetchJson("/channels/status"), inspectPrompt: (channelId: string) => - fetchJson(`/channels/inspect?channel_id=${encodeURIComponent(channelId)}`), + fetchJson(`/channels/prompt/inspect?channel_id=${encodeURIComponent(channelId)}`), setPromptCapture: async (channelId: string, enabled: boolean) => { - const response = await fetch(`${getApiBase()}/channels/inspect/capture`, { + const response = await fetch(`${getApiBase()}/channels/prompt/capture`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ channel_id: channelId, enabled }), @@ -1350,11 +1350,11 @@ export const api = { }, listPromptSnapshots: (channelId: string, limit = 50) => fetchJson( - `/channels/inspect/snapshots?channel_id=${encodeURIComponent(channelId)}&limit=${limit}`, + `/channels/prompt/snapshots?channel_id=${encodeURIComponent(channelId)}&limit=${limit}`, ), getPromptSnapshot: (channelId: string, timestampMs: number) => fetchJson( - `/channels/inspect/snapshot?channel_id=${encodeURIComponent(channelId)}×tamp_ms=${timestampMs}`, + `/channels/prompt/snapshots/get?channel_id=${encodeURIComponent(channelId)}×tamp_ms=${timestampMs}`, ), workersList: (agentId: string, params: { limit?: number; offset?: number; status?: string } = {}) => { const search = new URLSearchParams({ agent_id: agentId }); @@ -1580,7 +1580,7 @@ export const api = { }, cancelProcess: async (channelId: string, processType: "worker" | "branch", processId: string) => { - const response = await fetch(`${getApiBase()}/channels/cancel`, { + const response = await fetch(`${getApiBase()}/channels/cancel-process`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ channel_id: channelId, process_type: processType, process_id: processId }), @@ -1845,16 +1845,16 @@ export const api = { }, // Update API - updateCheck: () => fetchJson("/update/check"), + updateCheck: () => fetchJson("/update-check"), updateCheckNow: async () => { - const response = await fetch(`${getApiBase()}/update/check`, { method: "POST" }); + const response = await fetch(`${getApiBase()}/update-check`, { method: "POST" }); if (!response.ok) { throw new Error(`API error: ${response.status}`); } return response.json() as Promise; }, updateApply: async () => { - const response = await fetch(`${getApiBase()}/update/apply`, { method: "POST" }); + const response = await fetch(`${getApiBase()}/update-apply`, { method: "POST" }); if (!response.ok) { throw new Error(`API error: ${response.status}`); } @@ -1966,9 +1966,9 @@ export const api = { }, // Agent Groups API - groups: () => fetchJson<{ groups: TopologyGroup[] }>("/groups"), + groups: () => fetchJson<{ groups: TopologyGroup[] }>("/links/groups"), createGroup: async (request: CreateGroupRequest): Promise<{ group: TopologyGroup }> => { - const response = await fetch(`${getApiBase()}/groups`, { + const response = await fetch(`${getApiBase()}/links/groups`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(request), @@ -1980,7 +1980,7 @@ export const api = { }, updateGroup: async (name: string, request: UpdateGroupRequest): Promise<{ group: TopologyGroup }> => { const response = await fetch( - `${getApiBase()}/groups/${encodeURIComponent(name)}`, + `${getApiBase()}/links/groups/${encodeURIComponent(name)}`, { method: "PUT", headers: { "Content-Type": "application/json" }, @@ -1994,7 +1994,7 @@ export const api = { }, deleteGroup: async (name: string): Promise => { const response = await fetch( - `${getApiBase()}/groups/${encodeURIComponent(name)}`, + `${getApiBase()}/links/groups/${encodeURIComponent(name)}`, { method: "DELETE" }, ); if (!response.ok) { @@ -2003,9 +2003,9 @@ export const api = { }, // Humans API - humans: () => fetchJson<{ humans: TopologyHuman[] }>("/humans"), + humans: () => fetchJson<{ humans: TopologyHuman[] }>("/links/humans"), createHuman: async (request: CreateHumanRequest): Promise<{ human: TopologyHuman }> => { - const response = await fetch(`${getApiBase()}/humans`, { + const response = await fetch(`${getApiBase()}/links/humans`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(request), @@ -2017,7 +2017,7 @@ export const api = { }, updateHuman: async (id: string, request: UpdateHumanRequest): Promise<{ human: TopologyHuman }> => { const response = await fetch( - `${getApiBase()}/humans/${encodeURIComponent(id)}`, + `${getApiBase()}/links/humans/${encodeURIComponent(id)}`, { method: "PUT", headers: { "Content-Type": "application/json" }, @@ -2031,7 +2031,7 @@ export const api = { }, deleteHuman: async (id: string): Promise => { const response = await fetch( - `${getApiBase()}/humans/${encodeURIComponent(id)}`, + `${getApiBase()}/links/humans/${encodeURIComponent(id)}`, { method: "DELETE" }, ); if (!response.ok) { diff --git a/scripts/audit-routes.sh b/scripts/audit-routes.sh new file mode 100755 index 000000000..a5f9fce4e --- /dev/null +++ b/scripts/audit-routes.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# Extracts all API routes from the frontend client and backend handlers, +# then diffs them to find mismatches. +# +# Usage: ./scripts/audit-routes.sh + +set -euo pipefail +cd "$(dirname "$0")/.." + +FRONTEND_FILE="interface/src/api/client.ts" +BACKEND_DIR="src/api" + +extract_frontend_routes() { + # Two patterns to catch: + # 1. fetchJson<...>("/") — relative paths + # 2. `${getApiBase()}/` — template literal paths + { + # Pattern 1: fetchJson("/path...") + grep -oE 'fetchJson<[^>]*>\([^)]*"(/[^"?]+)' "$FRONTEND_FILE" \ + | grep -oE '"/[^"?]+' \ + | sed 's/^"//' + + # Pattern 2: ${getApiBase()}/path...` + grep -oE 'getApiBase\(\)\}/[^`"]+' "$FRONTEND_FILE" \ + | sed 's|getApiBase()}||' + } \ + | sed 's|\?.*||' \ + | sed 's/\${[^}]*}/{param}/g' \ + | sed '/^\s*$/d' \ + | sort -u +} + +extract_backend_routes() { + grep -ohE 'path\s*=\s*"(/[^"]+)"' "$BACKEND_DIR"/*.rs \ + | sed 's/.*path *= *"//' \ + | sed 's/".*//' \ + | sed 's/{[^}]*}/{param}/g' \ + | sort -u +} + +echo "=== Frontend routes ===" +echo "" +extract_frontend_routes | while read -r route; do + printf " %s\n" "$route" +done + +echo "" +echo "=== Backend routes ===" +echo "" +extract_backend_routes | while read -r route; do + printf " %s\n" "$route" +done + +FRONTEND_ROUTES=$(mktemp) +BACKEND_ROUTES=$(mktemp) +trap 'rm -f "$FRONTEND_ROUTES" "$BACKEND_ROUTES"' EXIT + +extract_frontend_routes > "$FRONTEND_ROUTES" +extract_backend_routes > "$BACKEND_ROUTES" + +echo "" +echo "=== Mismatches ===" +echo "" + +missing=0 +while read -r route; do + if ! grep -qF "$route" "$BACKEND_ROUTES"; then + if [ $missing -eq 0 ]; then + echo "Frontend routes with NO matching backend route:" + fi + echo " ❌ $route" + missing=$((missing + 1)) + fi +done < "$FRONTEND_ROUTES" + +if [ $missing -eq 0 ]; then + echo "✅ All frontend routes have a matching backend route." +fi From 10d683d2494baf5ccd2002a9d991bd8e61e04a25 Mon Sep 17 00:00:00 2001 From: James Pine Date: Fri, 3 Apr 2026 21:11:14 -0700 Subject: [PATCH 2/3] Make prompt inspector modal 80% viewport width Co-Authored-By: Claude Opus 4.6 (1M context) --- interface/src/components/PromptInspectModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/components/PromptInspectModal.tsx b/interface/src/components/PromptInspectModal.tsx index a1f6ceced..891df20a7 100644 --- a/interface/src/components/PromptInspectModal.tsx +++ b/interface/src/components/PromptInspectModal.tsx @@ -65,7 +65,7 @@ export function PromptInspectModal({ open, onOpenChange, channelId }: PromptInsp return ( - + Prompt Inspector {showContent && !contentLoading && systemPrompt != null && ( From a1677ba39510cf5241dce742b1f18a1100faac3a Mon Sep 17 00:00:00 2001 From: James Pine Date: Fri, 3 Apr 2026 21:13:45 -0700 Subject: [PATCH 3/3] Remove audit-routes.sh script Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/audit-routes.sh | 78 ----------------------------------------- 1 file changed, 78 deletions(-) delete mode 100755 scripts/audit-routes.sh diff --git a/scripts/audit-routes.sh b/scripts/audit-routes.sh deleted file mode 100755 index a5f9fce4e..000000000 --- a/scripts/audit-routes.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env bash -# Extracts all API routes from the frontend client and backend handlers, -# then diffs them to find mismatches. -# -# Usage: ./scripts/audit-routes.sh - -set -euo pipefail -cd "$(dirname "$0")/.." - -FRONTEND_FILE="interface/src/api/client.ts" -BACKEND_DIR="src/api" - -extract_frontend_routes() { - # Two patterns to catch: - # 1. fetchJson<...>("/") — relative paths - # 2. `${getApiBase()}/` — template literal paths - { - # Pattern 1: fetchJson("/path...") - grep -oE 'fetchJson<[^>]*>\([^)]*"(/[^"?]+)' "$FRONTEND_FILE" \ - | grep -oE '"/[^"?]+' \ - | sed 's/^"//' - - # Pattern 2: ${getApiBase()}/path...` - grep -oE 'getApiBase\(\)\}/[^`"]+' "$FRONTEND_FILE" \ - | sed 's|getApiBase()}||' - } \ - | sed 's|\?.*||' \ - | sed 's/\${[^}]*}/{param}/g' \ - | sed '/^\s*$/d' \ - | sort -u -} - -extract_backend_routes() { - grep -ohE 'path\s*=\s*"(/[^"]+)"' "$BACKEND_DIR"/*.rs \ - | sed 's/.*path *= *"//' \ - | sed 's/".*//' \ - | sed 's/{[^}]*}/{param}/g' \ - | sort -u -} - -echo "=== Frontend routes ===" -echo "" -extract_frontend_routes | while read -r route; do - printf " %s\n" "$route" -done - -echo "" -echo "=== Backend routes ===" -echo "" -extract_backend_routes | while read -r route; do - printf " %s\n" "$route" -done - -FRONTEND_ROUTES=$(mktemp) -BACKEND_ROUTES=$(mktemp) -trap 'rm -f "$FRONTEND_ROUTES" "$BACKEND_ROUTES"' EXIT - -extract_frontend_routes > "$FRONTEND_ROUTES" -extract_backend_routes > "$BACKEND_ROUTES" - -echo "" -echo "=== Mismatches ===" -echo "" - -missing=0 -while read -r route; do - if ! grep -qF "$route" "$BACKEND_ROUTES"; then - if [ $missing -eq 0 ]; then - echo "Frontend routes with NO matching backend route:" - fi - echo " ❌ $route" - missing=$((missing + 1)) - fi -done < "$FRONTEND_ROUTES" - -if [ $missing -eq 0 ]; then - echo "✅ All frontend routes have a matching backend route." -fi