diff --git a/packages/cli-workflow/src/commands/serve/tunnel.ts b/packages/cli-workflow/src/commands/serve/tunnel.ts index 97be797..2c83e12 100644 --- a/packages/cli-workflow/src/commands/serve/tunnel.ts +++ b/packages/cli-workflow/src/commands/serve/tunnel.ts @@ -42,7 +42,7 @@ export async function registerWithGateway( agentToken: string, ): Promise { try { - const resp = await fetch(`${gatewayUrl}/register`, { + const resp = await fetch(`${gatewayUrl}/api/gateway/register`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name, url: tunnelUrl, secret, agentToken }), @@ -65,7 +65,7 @@ export async function unregisterFromGateway( secret: string, ): Promise { try { - await fetch(`${gatewayUrl}/register/${name}`, { + await fetch(`${gatewayUrl}/api/gateway/register/${name}`, { method: "DELETE", headers: { Authorization: `Bearer ${secret}` }, }); diff --git a/packages/workflow-dashboard/src/api.ts b/packages/workflow-dashboard/src/api.ts index 9f7732d..64211ff 100644 --- a/packages/workflow-dashboard/src/api.ts +++ b/packages/workflow-dashboard/src/api.ts @@ -28,7 +28,7 @@ function authHeaders(): Record { function agentBase(agent: string): string { if (GATEWAY_URL) { - return `${GATEWAY_URL}/api/${agent}`; + return `${GATEWAY_URL}/api/agents/${agent}`; } // Local dev: proxy via vite, no agent prefix return "/api"; @@ -108,7 +108,7 @@ export type ThreadRecord = ThreadStartRecord | RoleRecord | WorkflowResultRecord export function listAgents(): Promise { const url = GATEWAY_URL || ""; - return fetchJson(url, "/endpoints"); + return fetchJson(url, "/api/gateway/endpoints"); } // ── Agent-scoped endpoints ────────────────────────────────────────── diff --git a/packages/workflow-gateway/src/index.ts b/packages/workflow-gateway/src/index.ts index 273f514..e303790 100644 --- a/packages/workflow-gateway/src/index.ts +++ b/packages/workflow-gateway/src/index.ts @@ -23,16 +23,6 @@ const app = new Hono(); app.use("*", cors()); -// ── Dashboard API key auth (skip healthz + register) ───────────── -app.use("/endpoints", async (c, next) => { - if (!checkDashboardAuth(c)) return c.json({ error: "unauthorized" }, 401); - await next(); -}); -app.use("/api/*", async (c, next) => { - if (!checkDashboardAuth(c)) return c.json({ error: "unauthorized" }, 401); - await next(); -}); - function checkDashboardAuth(c: { req: { header: (n: string) => string | undefined; query: (n: string) => string | undefined }; env: Env["Bindings"]; @@ -46,8 +36,10 @@ function checkDashboardAuth(c: { // ── Health ────────────────────────────────────────────────────────── app.get("/healthz", (c) => c.json({ ok: true })); -// ── Register / heartbeat ──────────────────────────────────────────── -app.post("/register", async (c) => { +// ── Gateway management (GATEWAY_SECRET auth) ──────────────────────── +const gateway = new Hono(); + +gateway.post("/register", async (c) => { const body = await c.req.json<{ name?: string; url?: string; @@ -82,8 +74,7 @@ app.post("/register", async (c) => { return c.json({ registered: name }, status); }); -// ── Unregister ────────────────────────────────────────────────────── -app.delete("/register/:name", async (c) => { +gateway.delete("/register/:name", async (c) => { const auth = c.req.header("Authorization"); if (auth !== `Bearer ${c.env.GATEWAY_SECRET}`) { return c.json({ error: "unauthorized" }, 401); @@ -94,8 +85,10 @@ app.delete("/register/:name", async (c) => { return c.json({ unregistered: name }); }); -// ── List endpoints ────────────────────────────────────────────────── -app.get("/endpoints", async (c) => { +// endpoints requires dashboard auth +gateway.get("/endpoints", async (c) => { + if (!checkDashboardAuth(c)) return c.json({ error: "unauthorized" }, 401); + const list = await c.env.ENDPOINTS.list(); const endpoints: Array<{ name: string; url: string; status: string; lastHeartbeat: number }> = []; @@ -115,8 +108,11 @@ app.get("/endpoints", async (c) => { return c.json(endpoints); }); -// ── API proxy: /api/:agent/* → agent's tunnel URL ─────────────────── -app.all("/api/:agent/*", async (c) => { +app.route("/api/gateway", gateway); + +// ── API proxy: /api/agents/:agent/* → agent's tunnel URL (dashboard auth) ── +app.all("/api/agents/:agent/*", async (c) => { + if (!checkDashboardAuth(c)) return c.json({ error: "unauthorized" }, 401); const agent = c.req.param("agent"); const record = await c.env.ENDPOINTS.get(agent, "json"); @@ -126,7 +122,7 @@ app.all("/api/:agent/*", async (c) => { // Build target URL: strip /api/:agent prefix, forward the rest const url = new URL(c.req.url); - const pathAfterAgent = url.pathname.replace(`/api/${agent}`, ""); + const pathAfterAgent = url.pathname.replace(`/api/agents/${agent}`, ""); const targetUrl = `${record.url}/api${pathAfterAgent}${url.search}`; const headers = new Headers(c.req.raw.headers);