import assert from "node:assert/strict"; import { mkdtemp } from "node:fs/promises"; import { createServer } from "node:http"; import { test } from "bun:test"; import { createAccessController } from "./access-control.ts"; import { createCodeAgentTraceStore } from "./code-agent-trace-store.ts"; import { createCloudApiServer } from "./server.ts"; const INTERNAL_TOKEN = "test-internal-token"; test("Postgres workspace update query preserves parameter numbering", async () => { const calls = []; const accessController = createAccessController({ now: () => "2026-06-01T00:00:00.000Z", runtimeStore: { query: async (sql, params = []) => { calls.push({ sql, params }); if (sql.startsWith("CREATE ") || sql.startsWith("ALTER ")) return { rows: [] }; if (sql.startsWith("SELECT * FROM account_workspaces")) { return { rows: [{ id: "wsp_pg_param_guard", owner_user_id: "usr_pg_param_guard", project_id: "prj_device_pod_workbench", name: "Default Workbench", status: "active", is_default: true, selected_conversation_id: null, selected_agent_session_id: null, selected_device_pod_id: null, active_trace_id: null, provider_profile: null, workspace_json: "{}", revision: 1, updated_by_session_id: null, updated_by_client: null, created_at: "2026-06-01T00:00:00.000Z", updated_at: "2026-06-01T00:00:00.000Z" }] }; } if (sql.startsWith("UPDATE account_workspaces")) return { rows: [] }; return { rows: [] }; } } }); await accessController.store.updateWorkspace({ workspaceId: "wsp_pg_param_guard", ownerUserId: "usr_pg_param_guard", actorRole: "user", providerProfile: "deepseek", patch: { issue655AcceptanceMarker: "postgres-param-guard" }, updatedByClient: "test-suite" }); const update = calls.find((call) => call.sql.startsWith("UPDATE account_workspaces")); assert.ok(update); assert.match(update.sql, /created_at=\$16, updated_at=\$17/u); assert.match(update.sql, /\$18::text = 'admin'/u); assert.equal(update.params.length, 18); assert.equal(update.params[15], "2026-06-01T00:00:00.000Z"); assert.equal(update.params[16], "2026-06-01T00:00:00.000Z"); assert.equal(update.params[17], "user"); }); test("workbench workspace is account-scoped, persistent, and gates active agent turns", async () => { const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" }, now: () => "2026-06-01T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const adminCookie = adminLogin.cookie; const alice = await postJson(port, "/v1/admin/users", { username: "alice-ws", password: "alice-pass" }, adminCookie); const bob = await postJson(port, "/v1/admin/users", { username: "bob-ws", password: "bob-pass" }, adminCookie); assert.equal(alice.status, 201); assert.equal(bob.status, 201); const aliceLogin = await postJson(port, "/auth/login", { username: "alice-ws", password: "alice-pass" }); const bobLogin = await postJson(port, "/auth/login", { username: "bob-ws", password: "bob-pass" }); const workspace = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", aliceLogin.cookie); assert.equal(workspace.status, 200); assert.equal(workspace.body.contractVersion, "workbench-workspace-v1"); assert.match(workspace.body.workspace.workspaceId, /^wsp_/u); assert.equal(workspace.body.workspace.revision, 1); assert.equal(workspace.body.workspace.selectedConversationId, null); const conversation = await putJson(port, "/v1/agent/conversations/cnv_issue655_shared", { projectId: "prj_device_pod_workbench", sessionId: "ses_issue655_shared", threadId: "thread-issue-655", sessionStatus: "running", lastTraceId: "trc_issue655_previous", messages: [{ role: "agent", text: "persist me", status: "running", traceId: "trc_issue655_active" }] }, aliceLogin.cookie); assert.equal(conversation.status, 200); assert.equal(conversation.body.conversation.conversationId, "cnv_issue655_shared"); const update = await patchJson(port, `/v1/workbench/workspace/${workspace.body.workspace.workspaceId}`, { projectId: "prj_device_pod_workbench", expectedRevision: 1, selectedConversationId: "cnv_issue655_shared", selectedAgentSessionId: "ses_issue655_shared", activeTraceId: "trc_issue655_active", providerProfile: "deepseek", sessionStatus: "running", threadId: "thread-issue-655", messages: [{ role: "agent", status: "running", traceId: "trc_issue655_active" }], updatedByClient: "test-suite" }, aliceLogin.cookie); assert.equal(update.status, 200); assert.equal(update.body.workspace.revision, 2); assert.equal(update.body.workspace.selectedConversationId, "cnv_issue655_shared"); assert.equal(update.body.workspace.selectedConversation.conversationId, "cnv_issue655_shared"); assert.equal(update.body.workspace.activeTraceId, "trc_issue655_active"); assert.equal(update.body.workspace.secretMaterialStored, false); const stale = await patchJson(port, `/v1/workbench/workspace/${workspace.body.workspace.workspaceId}`, { expectedRevision: 1, selectedConversationId: "cnv_issue655_shared", updatedByClient: "test-suite" }, aliceLogin.cookie); assert.equal(stale.status, 409); assert.equal(stale.body.error.code, "workspace_revision_conflict"); assert.equal(stale.body.workspace.revision, 2); const bobRead = await getJson(port, `/v1/workbench/workspace/${workspace.body.workspace.workspaceId}/events?afterRevision=0`, bobLogin.cookie); assert.equal(bobRead.status, 404); assert.equal(bobRead.body.error.code, "workbench_workspace_not_found"); const aliceRelogin = await postJson(port, "/auth/login", { username: "alice-ws", password: "alice-pass" }); const restored = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", aliceRelogin.cookie); assert.equal(restored.status, 200); assert.equal(restored.body.workspace.workspaceId, workspace.body.workspace.workspaceId); assert.equal(restored.body.workspace.selectedConversationId, "cnv_issue655_shared"); assert.equal(restored.body.workspace.revision, 2); const events = await getJson(port, `/v1/workbench/workspace/${workspace.body.workspace.workspaceId}/events?afterRevision=1`, aliceRelogin.cookie); assert.equal(events.status, 200); assert.equal(events.body.status, "changed"); assert.equal(events.body.latestRevision, 2); const busy = await postJson(port, "/v1/agent/chat", { message: "should be blocked by active workspace turn", traceId: "trc_issue655_next", conversationId: "cnv_issue655_shared", sessionId: "ses_issue655_shared", workspaceId: workspace.body.workspace.workspaceId, expectedWorkspaceRevision: 2, shortConnection: true }, aliceRelogin.cookie, { prefer: "respond-async", "x-trace-id": "trc_issue655_next" }); assert.equal(busy.status, 409); assert.equal(busy.body.error.code, "workspace_agent_busy"); assert.equal(busy.body.activeTraceId, "trc_issue655_active"); assert.equal(busy.body.workspaceRevision, 2); const reset = await postJson(port, `/v1/workbench/workspace/${workspace.body.workspace.workspaceId}/reset`, { confirm: true, updatedByClient: "test-suite" }, aliceRelogin.cookie); assert.equal(reset.status, 200); assert.equal(reset.body.reset, true); assert.equal(reset.body.workspace.activeTraceId, null); assert.equal(reset.body.workspace.selectedConversationId, null); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("workbench workspace permits a new turn after AgentRun active trace reaches terminal status", async () => { const agentRunCalls = []; const agentSessions = new Map(); const agentRunServer = createServer(async (request, response) => { const url = new URL(request.url || "/", "http://127.0.0.1"); agentRunCalls.push({ method: request.method, path: url.pathname, search: url.search }); const send = (data) => { response.writeHead(200, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: true, data })}\n`); }; if (request.method === "GET" && url.pathname === "/api/v1/runs/run_workspace_done/events") { return send({ items: [ { id: "evt_done", runId: "run_workspace_done", seq: 1, type: "terminal_status", payload: { commandId: "cmd_workspace_done", terminalStatus: "completed" }, createdAt: "2026-06-01T00:00:01.000Z" } ] }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_workspace_done/commands/cmd_workspace_done/result") { return send({ runId: "run_workspace_done", commandId: "cmd_workspace_done", status: "completed", runStatus: "completed", commandState: "completed", terminalStatus: "completed", completed: true, reply: "previous turn completed", lastSeq: 1, sessionRef: { sessionId: "ses_issue655_shared", conversationId: "cnv_issue655_shared", threadId: "thread-issue-655" } }); } if (request.method === "POST" && url.pathname === "/api/v1/runs") { return send({ id: "run_workspace_next", status: "pending", backendProfile: "deepseek", sessionRef: { sessionId: "ses_issue655_shared", conversationId: "cnv_issue655_shared" } }); } if (request.method === "POST" && url.pathname === "/api/v1/runs/run_workspace_next/commands") { return send({ id: "cmd_workspace_next", runId: "run_workspace_next", state: "pending", type: "turn", seq: 1 }); } if (request.method === "POST" && url.pathname === "/api/v1/runs/run_workspace_next/runner-jobs") { return send({ action: "create-kubernetes-job", runId: "run_workspace_next", commandId: "cmd_workspace_next", attemptId: "attempt_workspace_next", runnerId: "runner_workspace_next", namespace: "agentrun-v01", jobName: "agentrun-v01-runner-workspace-next" }); } response.writeHead(404, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, message: `unexpected ${request.method} ${url.pathname}` })}\n`); }); await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve)); const agentRunPort = agentRunServer.address().port; const accessController = createAccessController({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" }, now: () => "2026-06-01T00:00:00.000Z" }); accessController.getAgentSessionByTraceId = async (traceId) => agentSessions.get(traceId) ?? null; const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass", HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`, HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "0123456789abcdef0123456789abcdef01234567", HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "deepseek" }, accessController, now: () => "2026-06-01T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const alice = await postJson(port, "/v1/admin/users", { username: "alice-ws-terminal", password: "alice-pass" }, adminLogin.cookie); assert.equal(alice.status, 201); const aliceLogin = await postJson(port, "/auth/login", { username: "alice-ws-terminal", password: "alice-pass" }); const workspace = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", aliceLogin.cookie); assert.equal(workspace.status, 200); const conversation = await putJson(port, "/v1/agent/conversations/cnv_issue655_shared", { projectId: "prj_device_pod_workbench", sessionId: "ses_issue655_shared", threadId: "thread-issue-655", sessionStatus: "active", lastTraceId: "trc_issue655_previous", messages: [{ role: "agent", text: "previous completed turn", traceId: "trc_issue655_previous" }] }, aliceLogin.cookie); assert.equal(conversation.status, 200); const previousPayload = { messageId: "msg_workspace_done", agentRun: { runId: "run_workspace_done", commandId: "cmd_workspace_done", backendProfile: "deepseek", managerUrl: `http://127.0.0.1:${agentRunPort}`, lastSeq: 0, sessionId: "ses_issue655_shared", conversationId: "cnv_issue655_shared", threadId: "thread-issue-655" } }; agentSessions.set("trc_issue655_active_done", { id: "ses_issue655_shared", ownerUserId: alice.body.user.id, conversationId: "cnv_issue655_shared", threadId: "thread-issue-655", status: "active", session: previousPayload, updatedAt: "2026-06-01T00:00:00.000Z" }); const update = await patchJson(port, `/v1/workbench/workspace/${workspace.body.workspace.workspaceId}`, { expectedRevision: 1, selectedConversationId: "cnv_issue655_shared", selectedAgentSessionId: "ses_issue655_shared", activeTraceId: "trc_issue655_active_done", providerProfile: "deepseek", sessionStatus: "running", updatedByClient: "test-suite" }, aliceLogin.cookie); assert.equal(update.status, 200); assert.equal(update.body.workspace.activeTraceId, "trc_issue655_active_done"); const next = await postJson(port, "/v1/agent/chat", { message: "new turn after completed active trace", traceId: "trc_issue655_after_done", conversationId: "cnv_issue655_shared", sessionId: "ses_issue655_shared", workspaceId: workspace.body.workspace.workspaceId, expectedWorkspaceRevision: 2, shortConnection: true }, aliceLogin.cookie, { prefer: "respond-async", "x-trace-id": "trc_issue655_after_done" }); assert.equal(next.status, 202); assert.equal(next.body.status, "running"); assert.equal(next.body.traceId, "trc_issue655_after_done"); assert.ok(agentRunCalls.some((call) => call.path === "/api/v1/runs/run_workspace_done/commands/cmd_workspace_done/result")); const restored = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", aliceLogin.cookie); assert.equal(restored.body.workspace.activeTraceId, "trc_issue655_after_done"); assert.equal(restored.body.workspace.workspace.sessionStatus, "running"); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve()))); } }); test("workbench workspace ignores stale active trace when selected conversation already failed", async () => { const workspaceDir = await mkdtemp("/tmp/hwlab-stale-active-"); const server = createCloudApiServer({ env: { PATH: process.env.PATH, HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass", HWLAB_CODE_AGENT_PROVIDER: "codex-stdio", HWLAB_CODE_AGENT_CODEX_WORKSPACE: workspaceDir, HWLAB_CODE_AGENT_CODEX_SANDBOX: "danger-full-access", OPENAI_API_KEY: "test-openai-key-material" }, codexStdioManager: { describe: () => ({ available: true, ready: true, workspace: workspaceDir, sandbox: "danger-full-access" }), probe: async () => ({ available: true, ready: true, workspace: workspaceDir, sandbox: "danger-full-access" }), chat: async (params = {}) => ({ status: "completed", traceId: params.traceId, conversationId: params.conversationId, sessionId: params.sessionId, reply: { role: "assistant", content: "ok" }, runnerTrace: { traceId: params.traceId, status: "completed", events: [], eventCount: 0 }, session: { sessionId: params.sessionId, conversationId: params.conversationId, status: "idle" } }), cancel() {}, reapIdle() {} } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const workspace = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", adminLogin.cookie); const conversation = await putJson(port, "/v1/agent/conversations/cnv_stale_active_failed", { projectId: "prj_device_pod_workbench", sessionId: "ses_stale_active_failed", threadId: "thread-stale-active", status: "failed", sessionStatus: "failed", lastTraceId: "trc_stale_active_missing", messages: [{ role: "agent", text: "failed", status: "failed", traceId: "trc_stale_active_missing" }] }, adminLogin.cookie); assert.equal(conversation.status, 200); const update = await patchJson(port, `/v1/workbench/workspace/${workspace.body.workspace.workspaceId}`, { expectedRevision: 1, selectedConversationId: "cnv_stale_active_failed", selectedAgentSessionId: "ses_stale_active_failed", activeTraceId: "trc_stale_active_missing", sessionStatus: "running", updatedByClient: "test-suite" }, adminLogin.cookie); assert.equal(update.status, 200); const next = await postJson(port, "/v1/agent/chat", { message: "new turn after stale active trace", traceId: "trc_stale_active_next", conversationId: "cnv_stale_active_failed", sessionId: "ses_stale_active_failed", workspaceId: workspace.body.workspace.workspaceId, expectedWorkspaceRevision: 2, shortConnection: true }, adminLogin.cookie, { prefer: "respond-async", "x-trace-id": "trc_stale_active_next" }); assert.equal(next.status, 202); assert.equal(next.body.traceId, "trc_stale_active_next"); const restored = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", adminLogin.cookie); assert.equal(restored.body.workspace.activeTraceId, "trc_stale_active_next"); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("workbench workspace releases orphan cancel-blocked active trace on new turn", async () => { const traceStore = createCodeAgentTraceStore(); traceStore.append("trc_issue664_orphan_active", { type: "cancel", stage: "cancel", status: "blocked", label: "cancel:not_cancelable", message: "Cancel request did not include a bound Codex stdio sessionId.", errorCode: "cancel_session_missing", waitingFor: "session-binding" }); const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass", HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: "http://127.0.0.1:9", HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "deepseek" }, traceStore, now: () => "2026-06-01T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const alice = await postJson(port, "/v1/admin/users", { username: "alice-ws-orphan", password: "alice-pass" }, adminLogin.cookie); assert.equal(alice.status, 201); const aliceLogin = await postJson(port, "/auth/login", { username: "alice-ws-orphan", password: "alice-pass" }); const workspace = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", aliceLogin.cookie); assert.equal(workspace.status, 200); const update = await patchJson(port, `/v1/workbench/workspace/${workspace.body.workspace.workspaceId}`, { expectedRevision: 1, activeTraceId: "trc_issue664_orphan_active", providerProfile: "deepseek", sessionStatus: "running", updatedByClient: "test-suite" }, aliceLogin.cookie); assert.equal(update.status, 200); assert.equal(update.body.workspace.activeTraceId, "trc_issue664_orphan_active"); const next = await postJson(port, "/v1/agent/chat", { message: "new turn should replace orphan active trace", traceId: "trc_issue664_after_orphan", conversationId: "cnv_issue664_after_orphan", workspaceId: workspace.body.workspace.workspaceId, expectedWorkspaceRevision: 2, shortConnection: true }, aliceLogin.cookie, { prefer: "respond-async", "x-trace-id": "trc_issue664_after_orphan" }); assert.equal(next.status, 202); assert.equal(next.body.status, "running"); assert.equal(next.body.traceId, "trc_issue664_after_orphan"); const restored = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", aliceLogin.cookie); assert.equal(restored.body.workspace.activeTraceId, "trc_issue664_after_orphan"); assert.equal(restored.body.workspace.workspace.sessionStatus, "running"); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("workbench workspace permits continuation when stale active trace has idle active conversation", async () => { const workspaceDir = await mkdtemp("/tmp/hwlab-stale-active-idle-"); const server = createCloudApiServer({ env: { PATH: process.env.PATH, HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass", HWLAB_CODE_AGENT_PROVIDER: "codex-stdio", HWLAB_CODE_AGENT_CODEX_WORKSPACE: workspaceDir, HWLAB_CODE_AGENT_CODEX_SANDBOX: "danger-full-access", OPENAI_API_KEY: "test-openai-key-material" }, codexStdioManager: { describe: () => ({ available: true, ready: true, workspace: workspaceDir, sandbox: "danger-full-access" }), probe: async () => ({ available: true, ready: true, workspace: workspaceDir, sandbox: "danger-full-access" }), chat: async (params = {}) => ({ status: "completed", traceId: params.traceId, conversationId: params.conversationId, sessionId: params.sessionId, threadId: params.threadId, reply: { role: "assistant", content: "ok" }, runnerTrace: { traceId: params.traceId, status: "completed", events: [], eventCount: 0 }, session: { sessionId: params.sessionId, conversationId: params.conversationId, threadId: params.threadId, status: "idle" } }), cancel() {}, reapIdle() {} } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const workspace = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", adminLogin.cookie); const conversation = await putJson(port, "/v1/agent/conversations/cnv_stale_active_idle", { projectId: "prj_device_pod_workbench", sessionId: "ses_stale_active_idle", threadId: "thread-stale-active-idle", status: "active", sessionStatus: "active", lastTraceId: "trc_stale_active_idle_missing", messages: [{ role: "agent", text: "previous completed turn", status: "completed", traceId: "trc_stale_active_idle_missing" }] }, adminLogin.cookie); assert.equal(conversation.status, 200); const update = await patchJson(port, `/v1/workbench/workspace/${workspace.body.workspace.workspaceId}`, { expectedRevision: 1, selectedConversationId: "cnv_stale_active_idle", selectedAgentSessionId: "ses_stale_active_idle", activeTraceId: "trc_stale_active_idle_missing", sessionStatus: "running", updatedByClient: "test-suite" }, adminLogin.cookie); assert.equal(update.status, 200); const next = await postJson(port, "/v1/agent/chat", { message: "new turn after stale active idle trace", traceId: "trc_stale_active_idle_next", conversationId: "cnv_stale_active_idle", sessionId: "ses_stale_active_idle", threadId: "thread-stale-active-idle", workspaceId: workspace.body.workspace.workspaceId, expectedWorkspaceRevision: 2, shortConnection: true }, adminLogin.cookie, { prefer: "respond-async", "x-trace-id": "trc_stale_active_idle_next" }); assert.equal(next.status, 202); assert.equal(next.body.traceId, "trc_stale_active_idle_next"); assert.equal(next.body.conversationId, "cnv_stale_active_idle"); assert.equal(next.body.sessionId, "ses_stale_active_idle"); const restored = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", adminLogin.cookie); assert.equal(restored.body.workspace.activeTraceId, "trc_stale_active_idle_next"); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("workbench workspace status clears completed AgentRun active trace on read", async () => { const agentRunCalls = []; const agentSessions = new Map(); const agentRunServer = createServer(async (request, response) => { const url = new URL(request.url || "/", "http://127.0.0.1"); agentRunCalls.push({ method: request.method, path: url.pathname, search: url.search }); const send = (data) => { response.writeHead(200, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: true, data })}\n`); }; if (request.method === "GET" && url.pathname === "/api/v1/runs/run_workspace_status/events") { return send({ items: [ { id: "evt_status_done", runId: "run_workspace_status", seq: 1, type: "terminal_status", payload: { commandId: "cmd_workspace_status", terminalStatus: "completed" }, createdAt: "2026-06-01T00:00:01.000Z" } ] }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_workspace_status/commands/cmd_workspace_status/result") { return send({ runId: "run_workspace_status", commandId: "cmd_workspace_status", status: "completed", runStatus: "completed", commandState: "completed", terminalStatus: "completed", completed: true, reply: "previous turn completed", lastSeq: 1, sessionRef: { sessionId: "ses_issue664_status", conversationId: "cnv_issue664_status", threadId: "thread-issue-664" } }); } response.writeHead(404, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, message: `unexpected ${request.method} ${url.pathname}` })}\n`); }); await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve)); const agentRunPort = agentRunServer.address().port; const accessController = createAccessController({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" }, now: () => "2026-06-01T00:00:00.000Z" }); accessController.getAgentSessionByTraceId = async (traceId) => agentSessions.get(traceId) ?? null; const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass", HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`, HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "deepseek" }, accessController, now: () => "2026-06-01T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const alice = await postJson(port, "/v1/admin/users", { username: "alice-ws-status", password: "alice-pass" }, adminLogin.cookie); assert.equal(alice.status, 201); const aliceLogin = await postJson(port, "/auth/login", { username: "alice-ws-status", password: "alice-pass" }); const workspace = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", aliceLogin.cookie); assert.equal(workspace.status, 200); const conversation = await putJson(port, "/v1/agent/conversations/cnv_issue664_status", { projectId: "prj_device_pod_workbench", sessionId: "ses_issue664_status", threadId: "thread-issue-664", sessionStatus: "active", lastTraceId: "trc_issue664_previous", messages: [{ role: "agent", text: "previous turn still running", status: "running", traceId: "trc_issue664_status_done" }] }, aliceLogin.cookie); assert.equal(conversation.status, 200); const previousPayload = { messageId: "msg_workspace_status_done", agentRun: { runId: "run_workspace_status", commandId: "cmd_workspace_status", backendProfile: "deepseek", managerUrl: `http://127.0.0.1:${agentRunPort}`, lastSeq: 0, sessionId: "ses_issue664_status", conversationId: "cnv_issue664_status", threadId: "thread-issue-664" } }; agentSessions.set("trc_issue664_status_done", { id: "ses_issue664_status", ownerUserId: alice.body.user.id, conversationId: "cnv_issue664_status", threadId: "thread-issue-664", status: "active", session: previousPayload, updatedAt: "2026-06-01T00:00:00.000Z" }); const update = await patchJson(port, `/v1/workbench/workspace/${workspace.body.workspace.workspaceId}`, { expectedRevision: 1, selectedConversationId: "cnv_issue664_status", selectedAgentSessionId: "ses_issue664_status", activeTraceId: "trc_issue664_status_done", providerProfile: "deepseek", sessionStatus: "running", updatedByClient: "test-suite" }, aliceLogin.cookie); assert.equal(update.status, 200); assert.equal(update.body.workspace.activeTraceId, "trc_issue664_status_done"); const restored = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", aliceLogin.cookie); assert.equal(restored.status, 200); assert.equal(restored.body.workspace.activeTraceId, null); assert.equal(restored.body.workspace.workspace.lastTraceId, "trc_issue664_status_done"); assert.equal(restored.body.workspace.workspace.sessionStatus, "idle"); assert.equal(restored.body.workspace.selectedConversation.status, "idle"); assert.equal(restored.body.workspace.selectedConversation.lastTraceId, "trc_issue664_status_done"); assert.equal(restored.body.workspace.selectedConversation.messages.length, 1); assert.equal(restored.body.workspace.selectedConversation.messages[0].status, "idle"); assert.equal(restored.body.workspace.selectedConversation.messages[0].text, "previous turn completed"); assert.equal(restored.body.workspace.revision, 3); assert.ok(agentRunCalls.some((call) => call.path === "/api/v1/runs/run_workspace_status/commands/cmd_workspace_status/result")); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve()))); } }); test("workbench workspace status repairs terminal selected conversation after active trace was cleared", async () => { const agentRunCalls = []; const agentSessions = new Map(); const agentRunServer = createServer(async (request, response) => { const url = new URL(request.url || "/", "http://127.0.0.1"); agentRunCalls.push({ method: request.method, path: url.pathname, search: url.search }); const send = (data) => { response.writeHead(200, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: true, data })}\n`); }; if (request.method === "GET" && url.pathname === "/api/v1/runs/run_workspace_repair/events") { return send({ items: [] }); } if (request.method === "GET" && url.pathname === "/api/v1/runs/run_workspace_repair/commands/cmd_workspace_repair/result") { return send({ runId: "run_workspace_repair", commandId: "cmd_workspace_repair", status: "completed", runStatus: "completed", commandState: "completed", terminalStatus: "completed", completed: true, reply: "repair completed", lastSeq: 2, sessionRef: { sessionId: "ses_issue664_repair", conversationId: "cnv_issue664_repair", threadId: "thread-issue-664-repair" } }); } response.writeHead(404, { "content-type": "application/json" }); response.end(`${JSON.stringify({ ok: false, message: `unexpected ${request.method} ${url.pathname}` })}\n`); }); await new Promise((resolve) => agentRunServer.listen(0, "127.0.0.1", resolve)); const agentRunPort = agentRunServer.address().port; const accessController = createAccessController({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" }, now: () => "2026-06-01T00:00:00.000Z" }); accessController.getAgentSessionByTraceId = async (traceId) => agentSessions.get(traceId) ?? null; const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass", HWLAB_CODE_AGENT_ADAPTER: "agentrun-v01", AGENTRUN_MGR_URL: `http://127.0.0.1:${agentRunPort}`, HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1", HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "deepseek" }, accessController, now: () => "2026-06-01T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const alice = await postJson(port, "/v1/admin/users", { username: "alice-ws-repair", password: "alice-pass" }, adminLogin.cookie); assert.equal(alice.status, 201); const aliceLogin = await postJson(port, "/auth/login", { username: "alice-ws-repair", password: "alice-pass" }); const workspace = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", aliceLogin.cookie); assert.equal(workspace.status, 200); const conversation = await putJson(port, "/v1/agent/conversations/cnv_issue664_repair", { projectId: "prj_device_pod_workbench", sessionId: "ses_issue664_repair", threadId: "thread-issue-664-repair", sessionStatus: "running", lastTraceId: "trc_issue664_repair_done", messages: [] }, aliceLogin.cookie); assert.equal(conversation.status, 200); agentSessions.set("trc_issue664_repair_done", { id: "ses_issue664_repair", ownerUserId: alice.body.user.id, conversationId: "cnv_issue664_repair", threadId: "thread-issue-664-repair", status: "active", session: { messageId: "msg_workspace_repair_done", agentRun: { runId: "run_workspace_repair", commandId: "cmd_workspace_repair", backendProfile: "deepseek", managerUrl: `http://127.0.0.1:${agentRunPort}`, lastSeq: 0, sessionId: "ses_issue664_repair", conversationId: "cnv_issue664_repair", threadId: "thread-issue-664-repair" } }, updatedAt: "2026-06-01T00:00:00.000Z" }); const update = await patchJson(port, `/v1/workbench/workspace/${workspace.body.workspace.workspaceId}`, { expectedRevision: 1, selectedConversationId: "cnv_issue664_repair", selectedAgentSessionId: "ses_issue664_repair", activeTraceId: null, providerProfile: "deepseek", sessionStatus: "idle", lastTraceId: "trc_issue664_repair_done", updatedByClient: "test-suite" }, aliceLogin.cookie); assert.equal(update.status, 200); assert.equal(update.body.workspace.activeTraceId, null); assert.equal(update.body.workspace.selectedConversation.status, "running"); assert.equal(update.body.workspace.selectedConversation.messages.length, 0); const restored = await getJson(port, "/v1/workbench/workspace?projectId=prj_device_pod_workbench", aliceLogin.cookie); assert.equal(restored.status, 200); assert.equal(restored.body.workspace.activeTraceId, null); assert.equal(restored.body.workspace.workspace.lastTraceId, "trc_issue664_repair_done"); assert.equal(restored.body.workspace.workspace.sessionStatus, "idle"); assert.equal(restored.body.workspace.selectedConversation.status, "idle"); assert.equal(restored.body.workspace.selectedConversation.lastTraceId, "trc_issue664_repair_done"); assert.equal(restored.body.workspace.selectedConversation.messages.length, 1); assert.equal(restored.body.workspace.selectedConversation.messages[0].status, "idle"); assert.equal(restored.body.workspace.selectedConversation.messages[0].text, "repair completed"); assert.ok(agentRunCalls.some((call) => call.path === "/api/v1/runs/run_workspace_repair/commands/cmd_workspace_repair/result")); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); await new Promise((resolve, reject) => agentRunServer.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api access control grants visible device pods and requires device-pod executor", async () => { let directGatewayDispatches = 0; const gatewayRegistry = { isOnline: () => true, enqueue: async () => { directGatewayDispatches += 1; throw new Error("cloud-api must not bypass hwlab-device-pod executor"); }, describe: () => ({ sessions: [] }) }; const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" }, gatewayRegistry, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); assert.equal(adminLogin.status, 200); assert.equal(adminLogin.body.actor.role, "admin"); const adminCookie = adminLogin.cookie; const userCreate = await postJson(port, "/v1/admin/users", { username: "alice", password: "alice-pass", displayName: "Alice" }, adminCookie); assert.equal(userCreate.status, 201); assert.equal(userCreate.body.user.username, "alice"); assert.equal(JSON.stringify(userCreate.body).includes("alice-pass"), false); const bobCreate = await postJson(port, "/v1/admin/users", { username: "bob", password: "bob-pass", displayName: "Bob" }, adminCookie); assert.equal(bobCreate.status, 201); const podCreate = await postJson(port, "/v1/admin/device-pods", { devicePodId: "device-pod-71-freq", name: "71-FREQ", profile: { schemaVersion: 1, devicePodId: "device-pod-local-spoof", target: { id: "target-71-freq" }, projectWorkspace: { projectPath: "FirmWare/MDK-ARM/app.uvprojx", targetName: "app" }, route: { gatewaySessionId: "gws_missing", resourceId: "res_windows_host", capabilityId: "cap_device_host_cli", hostCli: "node tools/device-host-cli.mjs" } } }, adminCookie); assert.equal(podCreate.status, 201); assert.equal(podCreate.body.devicePod.devicePodId, "device-pod-71-freq"); assert.notEqual(podCreate.body.devicePod.profileHash, "sha256:a-local-profile-hash"); assert.equal(podCreate.body.devicePod.profile.route.gatewaySessionId, "redacted"); assert.equal(JSON.stringify(podCreate.body).includes("gws_missing"), false); const secretProfile = await postJson(port, "/v1/admin/device-pods", { devicePodId: "device-pod-secret", profile: { schemaVersion: 1, target: { id: "target-secret" }, route: { gatewaySessionId: "gws_secret", cloudToken: "should-not-be-stored" } } }, adminCookie); assert.equal(secretProfile.status, 400); assert.equal(secretProfile.body.error.code, "device_pod_profile_secret_forbidden"); assert.equal(JSON.stringify(secretProfile.body).includes("should-not-be-stored"), false); const emptyLogin = await postJson(port, "/auth/login", { username: "alice", password: "alice-pass" }); assert.equal(emptyLogin.status, 200); const aliceCookie = emptyLogin.cookie; const emptyList = await getJson(port, "/v1/device-pods", aliceCookie); assert.equal(emptyList.status, 200); assert.deepEqual(emptyList.body.devicePods, []); const grant = await postJson(port, "/v1/admin/device-pod-grants", { devicePodId: "device-pod-71-freq", userId: userCreate.body.user.id }, adminCookie); assert.equal(grant.status, 201); const bobGrant = await postJson(port, "/v1/admin/device-pod-grants", { devicePodId: "device-pod-71-freq", userId: bobCreate.body.user.id }, adminCookie); assert.equal(bobGrant.status, 201); const visible = await getJson(port, "/v1/device-pods", aliceCookie); assert.equal(visible.status, 200); assert.equal(visible.body.contractVersion, "device-pod-authority-v1"); assert.equal(visible.body.source.kind, "CLOUD_API_PROFILE_AUTHORITY"); assert.equal(visible.body.source.fake, false); assert.equal(visible.body.devicePods.length, 1); assert.equal(visible.body.devicePods[0].devicePodId, "device-pod-71-freq"); assert.match(visible.body.devicePods[0].profileHash, /^sha256:/u); const status = await getJson(port, "/v1/device-pods/device-pod-71-freq/status", aliceCookie); assert.equal(status.status, 200); assert.equal(status.body.devicePodId, "device-pod-71-freq"); assert.equal(status.body.targetId, "target-71-freq"); assert.equal(status.body.profileHash, visible.body.devicePods[0].profileHash); assert.match(status.body.traceId, /^trc_devicepod_/u); assert.match(status.body.operationId, /^op_devicepod_/u); assert.equal(status.body.status, "ok"); assert.equal(status.body.freshness.stale, false); assert.equal(status.body.blocker, null); assert.equal(status.body.truncation.truncated, false); assert.equal(status.body.output.summary, "device-pod status ok"); const job = await postJson(port, "/v1/device-pods/device-pod-71-freq/jobs", { intent: "workspace.ls", args: { path: "." } }, aliceCookie); assert.equal(job.status, 409); assert.equal(job.body.status, "blocked"); assert.equal(job.body.blocker.code, "device_pod_executor_unavailable"); assert.equal(job.body.blocker.summary, "HWLAB_DEVICE_POD_URL is not configured; cloud-api will not bypass hwlab-device-pod executor"); assert.equal(job.body.devicePodId, "device-pod-71-freq"); assert.equal(job.body.profileHash, visible.body.devicePods[0].profileHash); assert.equal(directGatewayDispatches, 0); const mutatingWithoutLease = await postJson(port, "/v1/device-pods/device-pod-71-freq/jobs", { intent: "debug.reset", reason: "reset smoke" }, aliceCookie); assert.equal(mutatingWithoutLease.status, 409); assert.equal(mutatingWithoutLease.body.error.code, "device_lease_required"); assert.equal(mutatingWithoutLease.body.status, "blocked"); assert.equal(mutatingWithoutLease.body.devicePodId, "device-pod-71-freq"); assert.equal(mutatingWithoutLease.body.profileHash, visible.body.devicePods[0].profileHash); assert.equal(mutatingWithoutLease.body.blocker.code, "device_lease_required"); assert.equal(mutatingWithoutLease.body.freshness.stale, true); const rejectedJob = await getJson(port, `/v1/device-pods/device-pod-71-freq/jobs/${mutatingWithoutLease.body.job.id}`, aliceCookie); assert.equal(rejectedJob.status, 200); assert.equal(rejectedJob.body.job.id, mutatingWithoutLease.body.job.id); assert.equal(rejectedJob.body.status, "blocked"); assert.equal(rejectedJob.body.blocker.code, "device_lease_required"); const rejectedOutput = await getJson(port, `/v1/device-pods/device-pod-71-freq/jobs/${mutatingWithoutLease.body.job.id}/output`, aliceCookie); assert.equal(rejectedOutput.status, 200); assert.equal(rejectedOutput.body.truncation.truncated, false); assert.match(rejectedOutput.body.output.error, /requires an active device lease/u); const mutatingWithoutReason = await postJson(port, "/v1/device-pods/device-pod-71-freq/jobs", { intent: "debug.reset" }, aliceCookie); assert.equal(mutatingWithoutReason.status, 400); assert.equal(mutatingWithoutReason.body.error.code, "device_job_reason_required"); assert.equal(mutatingWithoutReason.body.status, "blocked"); assert.equal(mutatingWithoutReason.body.devicePodId, "device-pod-71-freq"); assert.equal(mutatingWithoutReason.body.profileHash, visible.body.devicePods[0].profileHash); assert.equal(mutatingWithoutReason.body.blocker.code, "device_job_reason_required"); assert.equal(mutatingWithoutReason.body.freshness.stale, true); const mutatingWithInvalidLease = await postJson(port, "/v1/device-pods/device-pod-71-freq/jobs", { intent: "debug.reset", reason: "reset smoke", leaseToken: "invalid-lease-token" }, aliceCookie); assert.equal(mutatingWithInvalidLease.status, 409); assert.equal(mutatingWithInvalidLease.body.error.code, "device_lease_invalid"); assert.equal(mutatingWithInvalidLease.body.status, "blocked"); assert.equal(mutatingWithInvalidLease.body.devicePodId, "device-pod-71-freq"); assert.equal(mutatingWithInvalidLease.body.profileHash, visible.body.devicePods[0].profileHash); assert.equal(mutatingWithInvalidLease.body.blocker.code, "device_lease_invalid"); assert.equal(mutatingWithInvalidLease.body.freshness.stale, true); const lease = await postJson(port, "/v1/device-pods/device-pod-71-freq/leases", { reason: "exclusive reset smoke", ttlSeconds: 60 }, aliceCookie); assert.equal(lease.status, 201); assert.equal(lease.body.contractVersion, "device-pod-lease-v1"); assert.equal(lease.body.lease.devicePodId, "device-pod-71-freq"); assert.equal(lease.body.lease.holderUserId, userCreate.body.user.id); assert.equal(typeof lease.body.leaseToken, "string"); assert.equal(JSON.stringify(lease.body.lease).includes(lease.body.leaseToken), false); const currentLease = await getJson(port, "/v1/device-pods/device-pod-71-freq/leases/current", aliceCookie); assert.equal(currentLease.status, 200); assert.equal(currentLease.body.lease.holderUserId, userCreate.body.user.id); const releaseWithoutToken = await fetch(`http://127.0.0.1:${port}/v1/device-pods/device-pod-71-freq/leases/current`, { method: "DELETE", headers: { cookie: aliceCookie } }); assert.equal(releaseWithoutToken.status, 404); assert.equal((await releaseWithoutToken.json()).released, false); const currentAfterMissingToken = await getJson(port, "/v1/device-pods/device-pod-71-freq/leases/current", aliceCookie); assert.equal(currentAfterMissingToken.status, 200); assert.equal(currentAfterMissingToken.body.lease.holderUserId, userCreate.body.user.id); const bobLogin = await postJson(port, "/auth/login", { username: "bob", password: "bob-pass" }); assert.equal(bobLogin.status, 200); const bobLeaseConflict = await postJson(port, "/v1/device-pods/device-pod-71-freq/leases", { reason: "exclusive reset smoke" }, bobLogin.cookie); assert.equal(bobLeaseConflict.status, 409); assert.equal(bobLeaseConflict.body.error.code, "device_lease_conflict"); assert.equal(bobLeaseConflict.body.lease.holderUserId, userCreate.body.user.id); const bobListWithoutPrivateGrant = await getJson(port, "/v1/device-pods", bobLogin.cookie); assert.equal(bobListWithoutPrivateGrant.status, 200); assert.equal(bobListWithoutPrivateGrant.body.devicePods.length, 1); const privatePodCreate = await postJson(port, "/v1/admin/device-pods", { devicePodId: "device-pod-private", profile: { schemaVersion: 1, target: { id: "target-private" }, route: { gatewaySessionId: "gws_private" } } }, adminCookie); assert.equal(privatePodCreate.status, 201); const bobPrivateStatus = await getJson(port, "/v1/device-pods/device-pod-private/status", bobLogin.cookie); assert.equal(bobPrivateStatus.status, 403); assert.equal(bobPrivateStatus.body.error.code, "device_pod_forbidden"); const bobMissingStatus = await getJson(port, "/v1/device-pods/device-pod-missing/status", bobLogin.cookie); assert.equal(bobMissingStatus.status, 404); assert.equal(bobMissingStatus.body.error.code, "device_pod_not_found"); const mutatingWithLease = await postJson(port, "/v1/device-pods/device-pod-71-freq/jobs", { intent: "debug.reset", reason: "reset smoke with lease", leaseToken: lease.body.leaseToken }, aliceCookie); assert.equal(mutatingWithLease.status, 409); assert.equal(mutatingWithLease.body.status, "blocked"); assert.equal(mutatingWithLease.body.lease.holderUserId, userCreate.body.user.id); assert.equal(mutatingWithLease.body.blocker.code, "device_pod_executor_unavailable"); assert.equal(directGatewayDispatches, 0); const release = await fetch(`http://127.0.0.1:${port}/v1/device-pods/device-pod-71-freq/leases/current`, { method: "DELETE", headers: { cookie: aliceCookie, "x-hwlab-device-lease-token": lease.body.leaseToken } }); assert.equal(release.status, 200); assert.equal((await release.json()).released, true); const bobLease = await postJson(port, "/v1/device-pods/device-pod-71-freq/leases", { reason: "exclusive reset smoke after release" }, bobLogin.cookie); assert.equal(bobLease.status, 201); assert.equal(bobLease.body.lease.holderUserId, bobCreate.body.user.id); const events = await getJson(port, "/v1/device-pods/device-pod-71-freq/events", aliceCookie); assert.equal(events.status, 200); assert.equal(events.body.events[0].blocker.code, "device_pod_executor_unavailable"); const unsupported = await postJson(port, "/v1/device-pods/device-pod-71-freq/jobs", { intent: "debug.erase-all", args: {} }, aliceCookie); assert.equal(unsupported.status, 400); assert.equal(unsupported.body.error.code, "unsupported_device_job_intent"); const revoke = await fetch(`http://127.0.0.1:${port}/v1/admin/device-pod-grants/device-pod-71-freq/${bobCreate.body.user.id}`, { method: "DELETE", headers: { cookie: adminCookie } }); assert.equal(revoke.status, 200); const revoked = await revoke.json(); assert.equal(revoked.devicePodId, "device-pod-71-freq"); assert.equal(revoked.userId, bobCreate.body.user.id); assert.equal(revoked.releasedLease.holderUserId, bobCreate.body.user.id); assert.equal(revoked.releasedLease.active, false); const afterRevoke = await getJson(port, "/v1/device-pods", bobLogin.cookie); assert.equal(afterRevoke.status, 200); assert.deepEqual(afterRevoke.body.devicePods, []); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api stores and restores Code Agent conversations by authenticated account", async () => { const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" }, now: () => "2026-05-31T07:30:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const adminCookie = adminLogin.cookie; const aliceCreate = await postJson(port, "/v1/admin/users", { username: "alice", password: "alice-pass" }, adminCookie); const bobCreate = await postJson(port, "/v1/admin/users", { username: "bob", password: "bob-pass" }, adminCookie); assert.equal(aliceCreate.status, 201); assert.equal(bobCreate.status, 201); const aliceLogin = await postJson(port, "/auth/login", { username: "alice", password: "alice-pass" }); const bobLogin = await postJson(port, "/auth/login", { username: "bob", password: "bob-pass" }); const stored = await putJson(port, "/v1/agent/conversations/cnv_account_sync", { projectId: "prj_device_pod_workbench", sessionId: "ses_account_sync", threadId: "thread-account-sync", lastTraceId: "trc_account_sync", sessionStatus: "idle", messages: [ { id: "msg_user", role: "user", title: "用户", text: "在吗", status: "sent", conversationId: "cnv_account_sync", sessionId: "ses_account_sync", threadId: "thread-account-sync" }, { id: "msg_agent", role: "agent", title: "Agent", text: "在", status: "completed", traceId: "trc_account_sync", conversationId: "cnv_account_sync", sessionId: "ses_account_sync", threadId: "thread-account-sync" } ] }, aliceLogin.cookie); assert.equal(stored.status, 200); assert.equal(stored.body.conversation.conversationId, "cnv_account_sync"); assert.equal(stored.body.conversation.sessionId, "ses_account_sync"); assert.equal(stored.body.conversation.threadId, "thread-account-sync"); assert.equal(stored.body.conversation.status, "idle"); assert.equal(stored.body.conversation.messages.length, 2); const aliceList = await getJson(port, "/v1/agent/conversations?projectId=prj_device_pod_workbench", aliceLogin.cookie); assert.equal(aliceList.status, 200); assert.equal(aliceList.body.count, 1); assert.equal(aliceList.body.defaultConversation.conversationId, "cnv_account_sync"); assert.equal(aliceList.body.defaultConversation.messages[1].text, "在"); assert.equal(aliceList.body.defaultConversation.valuesRedacted, true); const bobList = await getJson(port, "/v1/agent/conversations?projectId=prj_device_pod_workbench", bobLogin.cookie); assert.equal(bobList.status, 200); assert.deepEqual(bobList.body.conversations, []); const bobDirect = await getJson(port, "/v1/agent/conversations/cnv_account_sync", bobLogin.cookie); assert.equal(bobDirect.status, 404); assert.equal(bobDirect.body.error.code, "agent_conversation_not_found"); const logout = await postJson(port, "/auth/logout", {}, aliceLogin.cookie); assert.equal(logout.status, 200); const relogin = await postJson(port, "/auth/login", { username: "alice", password: "alice-pass" }); const restored = await getJson(port, "/v1/agent/conversations/cnv_account_sync", relogin.cookie); assert.equal(restored.status, 200); assert.equal(restored.body.conversation.threadId, "thread-account-sync"); assert.equal(restored.body.conversation.messages[0].text, "在吗"); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }); test("access controller restores AgentRun mapping by Code Agent traceId", async () => { const accessController = createAccessController({ now: () => "2026-06-01T00:00:00.000Z" }); await accessController.recordAgentSessionOwner({ ownerUserId: "usr_agent_owner", sessionId: "ses_agentrun_trace_lookup", projectId: "prj_v02_code_agent", conversationId: "cnv_agentrun_trace_lookup", threadId: "thread-agentrun-trace-lookup", traceId: "trc_agentrun_trace_lookup", status: "running", session: { source: "hwlab-cloud-api-agentrun-v01-adapter", agentRun: { adapter: "agentrun-v01", runId: "run_trace_lookup", commandId: "cmd_trace_lookup", jobName: "agentrun-v01-runner-trace-lookup", namespace: "agentrun-v01", backendProfile: "deepseek", valuesPrinted: false }, valuesRedacted: true } }); const restored = await accessController.getAgentSessionByTraceId("trc_agentrun_trace_lookup"); assert.equal(restored.id, "ses_agentrun_trace_lookup"); assert.equal(restored.lastTraceId, "trc_agentrun_trace_lookup"); assert.equal(restored.session.agentRun.runId, "run_trace_lookup"); assert.equal(restored.session.agentRun.commandId, "cmd_trace_lookup"); assert.equal(restored.session.agentRun.namespace, "agentrun-v01"); }); test("access controller preserves AgentRun runner mapping across conversation snapshot updates", async () => { const accessController = createAccessController({ now: () => "2026-06-01T00:00:00.000Z" }); await accessController.recordAgentSessionOwner({ ownerUserId: "usr_agent_owner", sessionId: "ses_agentrun_reuse_mapping", projectId: "prj_v02_code_agent", conversationId: "cnv_agentrun_reuse_mapping", threadId: "thread-agentrun-requested", traceId: "trc_agentrun_reuse_first", status: "running", session: { source: "hwlab-cloud-api-agentrun-v01-adapter", agentRun: { adapter: "agentrun-v01", runId: "run_reuse_mapping", commandId: "cmd_reuse_mapping_first", runnerId: "runner_reuse_mapping", jobName: "agentrun-v01-runner-reuse-mapping", namespace: "agentrun-v01", backendProfile: "deepseek", reuseEligible: true, valuesPrinted: false }, valuesRedacted: true } }); await accessController.recordAgentSessionOwner({ ownerUserId: "usr_agent_owner", sessionId: "ses_agentrun_reuse_mapping", projectId: "prj_v02_code_agent", conversationId: "cnv_agentrun_reuse_mapping", threadId: "thread-agentrun-requested", traceId: "trc_agentrun_reuse_second", status: "active", session: { source: "workbench-conversation-snapshot", messages: [{ role: "assistant", content: "done" }], valuesRedacted: true } }); const restored = await accessController.getAgentSession("ses_agentrun_reuse_mapping"); assert.equal(restored.lastTraceId, "trc_agentrun_reuse_second"); assert.equal(restored.session.source, "workbench-conversation-snapshot"); assert.equal(restored.session.agentRun.runId, "run_reuse_mapping"); assert.equal(restored.session.agentRun.commandId, "cmd_reuse_mapping_first"); assert.equal(restored.session.agentRun.jobName, "agentrun-v01-runner-reuse-mapping"); assert.equal(restored.session.agentRun.reuseEligible, true); }); test("cloud api protects device-pod routes when access control is required", async () => { const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1" }, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/device-pods`); assert.equal(response.status, 401); const payload = await response.json(); assert.equal(payload.error.code, "auth_required"); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api issues a redacted Code Agent device-pod session with admin device access", async () => { const accessController = createAccessController({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_ID: "usr_v02_admin", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" }, now: () => "2026-05-28T00:00:00.000Z" }); const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_ID: "usr_v02_admin", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" }, accessController, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); await postJson(port, "/v1/admin/device-pods", { devicePodId: "device-pod-71-freq", profile: { schemaVersion: 1, target: { id: "target-71-freq" }, route: { gatewaySessionId: "gws_unused" } } }, adminLogin.cookie); const agentAuth = await accessController.createCodeAgentDevicePodSession(); assert.equal(agentAuth.actor.id, "usr_v02_admin"); assert.equal(agentAuth.actor.role, "admin"); assert.equal(agentAuth.tokenSource, "code-agent-device-pod-session"); assert.equal(agentAuth.valuesRedacted, true); assert.equal(typeof agentAuth.token, "string"); assert.equal(JSON.stringify(agentAuth.session).includes(agentAuth.token), false); const status = await getJson(port, "/v1/device-pods/device-pod-71-freq/status", null, { "x-hwlab-session-token": agentAuth.token }); assert.equal(status.status, 200); assert.equal(status.body.devicePodId, "device-pod-71-freq"); assert.equal(status.body.actor.id, "usr_v02_admin"); assert.equal(status.body.actor.role, "admin"); assert.equal(JSON.stringify(status.body).includes(agentAuth.token), false); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api creates device lease holder session before lease insert", async () => { const accessController = createAccessController({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" }, now: () => "2026-05-28T00:00:00.000Z" }); const store = accessController.store; const leaseOrder = []; const holderSessions = new Set(); const recordAgentSessionOwner = store.recordAgentSessionOwner.bind(store); store.recordAgentSessionOwner = async (input) => { leaseOrder.push(`record:${input.sessionId}`); holderSessions.add(input.sessionId); return recordAgentSessionOwner(input); }; const acquireDeviceLease = store.acquireDeviceLease.bind(store); store.acquireDeviceLease = async (input) => { leaseOrder.push(`acquire:${input.holderSessionId}`); if (!holderSessions.has(input.holderSessionId)) throw new Error("holder_session_fk_missing"); return acquireDeviceLease(input); }; const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass" }, accessController, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const userCreate = await postJson(port, "/v1/admin/users", { username: "alice", password: "alice-pass" }, adminLogin.cookie); await postJson(port, "/v1/admin/device-pods", { devicePodId: "device-pod-71-freq", profile: { schemaVersion: 1, target: { id: "target-71-freq" }, route: { gatewaySessionId: "gws_unused" } } }, adminLogin.cookie); await postJson(port, "/v1/admin/device-pod-grants", { devicePodId: "device-pod-71-freq", userId: userCreate.body.user.id }, adminLogin.cookie); const aliceLogin = await postJson(port, "/auth/login", { username: "alice", password: "alice-pass" }); const lease = await postJson(port, "/v1/device-pods/device-pod-71-freq/leases", { agentSessionId: "ses_fk_order_test", reason: "fk order smoke", ttlSeconds: 60 }, aliceLogin.cookie); assert.equal(lease.status, 201); assert.equal(lease.body.acquired, true); assert.equal(lease.body.lease.holderSessionId, "ses_fk_order_test"); assert.deepEqual(leaseOrder, ["record:ses_fk_order_test", "acquire:ses_fk_order_test"]); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api dispatches authorized device jobs to the internal device-pod executor", async () => { const executorRequests = []; const executor = createServer(async (request, response) => { const body = await requestJson(request); executorRequests.push({ method: request.method, url: request.url, internalService: request.headers["x-hwlab-internal-service"], internalToken: request.headers["x-hwlab-internal-token"], body }); if (request.method === "POST" && body.args?.path === "src") { response.writeHead(202, { "content-type": "application/json; charset=utf-8" }); response.end(JSON.stringify({ accepted: true, status: "running", contractVersion: "device-pod-executor-v1", devicePodId: "device-pod-71-freq", traceId: body.traceId, operationId: body.operationId, job: { id: body.jobId, devicePodId: body.devicePodId, status: "running", intent: body.intent, reason: body.reason, traceId: body.traceId, operationId: body.operationId }, blocker: null })); return; } if (request.method === "GET" && request.url.endsWith("/output")) { response.writeHead(200, { "content-type": "application/json; charset=utf-8" }); response.end(JSON.stringify({ accepted: true, status: "completed", contractVersion: "device-pod-executor-v1", devicePodId: "device-pod-71-freq", traceId: "trc_executor_refresh", operationId: "op_executor_refresh", job: { id: request.url.split("/").at(-2), devicePodId: "device-pod-71-freq", status: "completed", intent: "workspace.ls" }, output: { executor: { status: "completed" }, output: { dispatch: { exitCode: 0, stdoutBytes: 15 } }, text: "executor output", bytes: 15, truncation: { maxBytes: 12000, truncated: false } } })); return; } response.writeHead(409, { "content-type": "application/json; charset=utf-8" }); response.end(JSON.stringify({ accepted: false, status: "blocked", contractVersion: "device-pod-executor-v1", devicePodId: "device-pod-71-freq", traceId: body.traceId, operationId: body.operationId, job: { id: body.jobId, devicePodId: body.devicePodId, status: "blocked", intent: body.intent, reason: body.reason, traceId: body.traceId, operationId: body.operationId }, blocker: { code: "gateway_dispatch_unavailable", layer: "device-pod", retryable: true, summary: "executor test blocker" }, output: { text: "executor output", bytes: 15, truncation: { maxBytes: 12000, truncated: false } } })); }); await new Promise((resolve) => executor.listen(0, "127.0.0.1", resolve)); const executorPort = executor.address().port; const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass", HWLAB_DEVICE_POD_INTERNAL_TOKEN: INTERNAL_TOKEN, HWLAB_DEVICE_POD_URL: `http://127.0.0.1:${executorPort}` }, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const userCreate = await postJson(port, "/v1/admin/users", { username: "alice", password: "alice-pass" }, adminLogin.cookie); await postJson(port, "/v1/admin/device-pods", { devicePodId: "device-pod-71-freq", profile: { schemaVersion: 1, target: { id: "target-71-freq" }, route: { gatewaySessionId: "gws_unused" } } }, adminLogin.cookie); await postJson(port, "/v1/admin/device-pod-grants", { devicePodId: "device-pod-71-freq", userId: userCreate.body.user.id }, adminLogin.cookie); const aliceLogin = await postJson(port, "/auth/login", { username: "alice", password: "alice-pass" }); const job = await postJson(port, "/v1/device-pods/device-pod-71-freq/jobs", { intent: "workspace.ls", args: { path: "." } }, aliceLogin.cookie); assert.equal(job.status, 409); assert.equal(job.body.blocker.code, "gateway_dispatch_unavailable"); assert.equal(job.body.job.status, "blocked"); assert.equal(executorRequests.length, 1); assert.equal(executorRequests[0].internalService, "hwlab-cloud-api"); assert.equal(executorRequests[0].internalToken, INTERNAL_TOKEN); assert.equal(executorRequests[0].body.profileHash, job.body.profileHash); assert.equal(executorRequests[0].body.ownerUserId, userCreate.body.user.id); assert.equal(executorRequests[0].body.intent, "workspace.ls"); const stored = await getJson(port, `/v1/device-pods/device-pod-71-freq/jobs/${job.body.job.id}`, aliceLogin.cookie); assert.equal(stored.status, 200); assert.equal(stored.body.job.id, job.body.job.id); assert.equal(stored.body.blocker.code, "gateway_dispatch_unavailable"); const runningJob = await postJson(port, "/v1/device-pods/device-pod-71-freq/jobs", { intent: "workspace.ls", args: { path: "src" } }, aliceLogin.cookie); assert.equal(runningJob.status, 202); assert.equal(runningJob.body.status, "running"); const output = await getJson(port, `/v1/device-pods/device-pod-71-freq/jobs/${runningJob.body.job.id}/output`, aliceLogin.cookie); assert.equal(output.status, 200); assert.equal(output.body.status, "completed"); assert.equal(output.body.blocker, null); assert.equal(output.body.freshness.stale, false); assert.equal(output.body.output.text, "executor output"); assert.deepEqual(output.body.output.output.dispatch, { exitCode: 0, stdoutBytes: 15 }); assert.equal(output.body.truncation.truncated, false); assert.ok(executorRequests.some((item) => item.method === "GET" && item.url.endsWith(`/jobs/${runningJob.body.job.id}/output`))); const events = await getJson(port, "/v1/device-pods/device-pod-71-freq/events", aliceLogin.cookie); const completedEvent = events.body.events.find((event) => event.refs.jobId === runningJob.body.job.id); assert.equal(completedEvent.status, "completed"); assert.equal(completedEvent.blocker, null); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); await new Promise((resolve, reject) => executor.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api bounds device-pod job output payloads", async () => { const longText = "x".repeat(13000); const executor = createServer(async (request, response) => { const body = await requestJson(request); response.writeHead(200, { "content-type": "application/json; charset=utf-8" }); response.end(JSON.stringify({ accepted: true, status: "completed", contractVersion: "device-pod-executor-v1", devicePodId: "device-pod-71-freq", traceId: body.traceId, operationId: body.operationId, job: { id: body.jobId, devicePodId: body.devicePodId, status: "completed", intent: body.intent }, output: { text: longText, nested: { kept: true } }, text: longText })); }); await new Promise((resolve) => executor.listen(0, "127.0.0.1", resolve)); const executorPort = executor.address().port; const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass", HWLAB_DEVICE_POD_INTERNAL_TOKEN: INTERNAL_TOKEN, HWLAB_DEVICE_POD_URL: `http://127.0.0.1:${executorPort}` }, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const userCreate = await postJson(port, "/v1/admin/users", { username: "alice", password: "alice-pass" }, adminLogin.cookie); await postJson(port, "/v1/admin/device-pods", { devicePodId: "device-pod-71-freq", profile: { schemaVersion: 1, target: { id: "target-71-freq" }, route: { gatewaySessionId: "gws_unused" } } }, adminLogin.cookie); await postJson(port, "/v1/admin/device-pod-grants", { devicePodId: "device-pod-71-freq", userId: userCreate.body.user.id }, adminLogin.cookie); const aliceLogin = await postJson(port, "/auth/login", { username: "alice", password: "alice-pass" }); const job = await postJson(port, "/v1/device-pods/device-pod-71-freq/jobs", { intent: "workspace.ls", args: { path: "." } }, aliceLogin.cookie); assert.equal(job.status, 200); const output = await getJson(port, `/v1/device-pods/device-pod-71-freq/jobs/${job.body.job.id}/output`, aliceLogin.cookie); assert.equal(output.status, 200); assert.equal(output.body.bytes, 12000); assert.equal(output.body.text.length, 12000); assert.equal(output.body.output.text.length, 12000); assert.equal(output.body.truncation.truncated, true); assert.equal(output.body.truncation.originalBytes, 13000); assert.equal(output.body.output.omitted.reason, "device_job_output_truncated"); assert.equal(JSON.stringify(output.body).includes(longText), false); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); await new Promise((resolve, reject) => executor.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api routes device-pod probe GET requests through executor jobs", async () => { const executorRequests = []; const executor = createServer(async (request, response) => { const body = await requestJson(request); executorRequests.push({ method: request.method, url: request.url, body }); const text = body.intent === "debug.chip-id" ? "chip-id: 0x12345678" : "uart tail output"; response.writeHead(200, { "content-type": "application/json; charset=utf-8" }); response.end(JSON.stringify({ accepted: true, status: "completed", contractVersion: "device-pod-executor-v1", devicePodId: body.devicePodId, traceId: body.traceId, operationId: body.operationId, job: { id: body.jobId, devicePodId: body.devicePodId, status: "completed", intent: body.intent }, output: { text }, text })); }); await new Promise((resolve) => executor.listen(0, "127.0.0.1", resolve)); const executorPort = executor.address().port; const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "admin-pass", HWLAB_DEVICE_POD_INTERNAL_TOKEN: INTERNAL_TOKEN, HWLAB_DEVICE_POD_URL: `http://127.0.0.1:${executorPort}` }, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const adminLogin = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); const userCreate = await postJson(port, "/v1/admin/users", { username: "alice", password: "alice-pass" }, adminLogin.cookie); await postJson(port, "/v1/admin/device-pods", { devicePodId: "device-pod-71-freq", profile: { schemaVersion: 1, target: { id: "target-71-freq" }, route: { gatewaySessionId: "gws_unused" } } }, adminLogin.cookie); await postJson(port, "/v1/admin/device-pod-grants", { devicePodId: "device-pod-71-freq", userId: userCreate.body.user.id }, adminLogin.cookie); const aliceLogin = await postJson(port, "/auth/login", { username: "alice", password: "alice-pass" }); const chip = await getJson(port, "/v1/device-pods/device-pod-71-freq/debug-probe/chip-id", aliceLogin.cookie); assert.equal(chip.status, 200); assert.equal(chip.body.interface, "debug-probe"); assert.equal(chip.body.intent, "debug.chip-id"); assert.equal(chip.body.job.intent, "debug.chip-id"); assert.equal(chip.body.output.text, "chip-id: 0x12345678"); assert.equal(chip.body.source.fake, false); const uart = await getJson(port, "/v1/device-pods/device-pod-71-freq/io-probe/uart/1/tail?durationMs=250&maxBytes=20", aliceLogin.cookie); assert.equal(uart.status, 200); assert.equal(uart.body.interface, "io-probe"); assert.equal(uart.body.intent, "io.uart.read"); assert.equal(uart.body.output.text, "uart tail output"); assert.equal(uart.body.truncation.maxBytes, 20); assert.equal(executorRequests.length, 2); assert.equal(executorRequests[0].method, "POST"); assert.equal(executorRequests[0].body.intent, "debug.chip-id"); assert.equal(executorRequests[0].body.ownerUserId, userCreate.body.user.id); assert.equal(executorRequests[1].body.intent, "io.uart.read"); assert.deepEqual(executorRequests[1].body.args, { uartId: "uart/1", durationMs: 250, maxBytes: 20 }); const events = await getJson(port, "/v1/device-pods/device-pod-71-freq/events", aliceLogin.cookie); assert.equal(events.status, 200); assert.ok(events.body.events.some((event) => event.intent === "debug.chip-id")); assert.ok(events.body.events.some((event) => event.intent === "io.uart.read")); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); await new Promise((resolve, reject) => executor.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api internal device-pod gateway dispatch is service-only and fail-closed without online gateway", async () => { const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_DEVICE_POD_INTERNAL_TOKEN: INTERNAL_TOKEN }, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const external = await postJson(port, "/v1/internal/device-pod/gateway-dispatch", { params: {} }); assert.equal(external.status, 403); assert.equal(external.body.error.code, "device_pod_internal_authority_required"); const headerOnly = await postJson(port, "/v1/internal/device-pod/gateway-dispatch", { id: "req_devicepod_dispatch_test", params: { gatewaySessionId: "gws_missing", resourceId: "res_devicepod_test", capabilityId: "cap_device_host_cli", operationId: "op_devicepod_dispatch_test", traceId: "trc_devicepod_dispatch_test", input: { command: "node tools/device-host-cli.mjs health" } } }, null, { "x-hwlab-internal-service": "hwlab-device-pod" }); assert.equal(headerOnly.status, 403); assert.equal(headerOnly.body.error.code, "device_pod_internal_authority_required"); const blocked = await postJson(port, "/v1/internal/device-pod/gateway-dispatch", { id: "req_devicepod_dispatch_test", params: { gatewaySessionId: "gws_missing", resourceId: "res_devicepod_test", capabilityId: "cap_device_host_cli", operationId: "op_devicepod_dispatch_test", traceId: "trc_devicepod_dispatch_test", input: { command: "node tools/device-host-cli.mjs health" } } }, null, devicePodInternalHeaders()); assert.equal(blocked.status, 409); assert.equal(blocked.body.blocker.code, "gateway_dispatch_unavailable"); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api internal device-pod gateway dispatch uses gateway poll result", async () => { const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_ENVIRONMENT: "v02", HWLAB_DEVICE_POD_INTERNAL_TOKEN: INTERNAL_TOKEN }, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const poll = await postJson(port, "/v1/gateway/poll", { serviceId: "hwlab-gateway", gatewayId: "gtw_devicepod_test", gatewaySessionId: "gws_devicepod_test", resourceId: "res_devicepod_test", capabilities: [{ capabilityId: "cap_device_host_cli", resourceId: "res_devicepod_test" }] }); assert.equal(poll.status, 200); const dispatchPromise = postJson(port, "/v1/internal/device-pod/gateway-dispatch", { id: "req_devicepod_dispatch_test", params: { gatewaySessionId: "gws_devicepod_test", resourceId: "res_devicepod_test", capabilityId: "cap_device_host_cli", operationId: "op_devicepod_dispatch_test", traceId: "trc_devicepod_dispatch_test", input: { command: "node tools/device-host-cli.mjs health", timeoutMs: 1000 } } }, null, devicePodInternalHeaders()); const queued = await postJson(port, "/v1/gateway/poll", { gatewayId: "gtw_devicepod_test", gatewaySessionId: "gws_devicepod_test" }); assert.equal(queued.status, 200); assert.equal(queued.body.type, "request"); assert.equal(queued.body.request.meta.environment, "v02"); assert.equal(queued.body.request.method, "hardware.invoke.shell"); assert.equal(queued.body.request.params.input.command, "node tools/device-host-cli.mjs health"); const result = await postJson(port, "/v1/gateway/result", { gatewayId: "gtw_devicepod_test", gatewaySessionId: "gws_devicepod_test", response: { jsonrpc: "2.0", id: queued.body.request.id, result: { accepted: true, status: "succeeded", shellExecuted: true, dispatchStatus: "succeeded", stdout: "host cli ok", exitCode: 0, gatewaySessionId: "gws_devicepod_test" } } }); assert.equal(result.status, 200); const dispatch = await dispatchPromise; assert.equal(dispatch.status, 200); assert.equal(dispatch.body.meta.environment, "v02"); assert.equal(dispatch.body.result.status, "completed"); assert.equal(dispatch.body.result.dispatch.stdout, "host cli ok"); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api exposes v1 access status routes and returns structured REST errors", async () => { const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1" }, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const session = await getJson(port, "/v1/auth/session"); assert.equal(session.status, 200); assert.equal(session.body.authenticated, false); assert.equal(session.body.setupRequired, true); assert.equal(session.body.contractVersion, "user-access-v1"); const access = await getJson(port, "/v1/access/status"); assert.equal(access.status, 200); assert.equal(access.body.accessControlRequired, true); assert.equal(access.body.routes.currentUser, "/v1/users/me"); const setup = await getJson(port, "/v1/setup/status"); assert.equal(setup.status, 200); assert.equal(setup.body.setupRequired, true); assert.equal(setup.body.bootstrapAdminConfigured, false); const me = await getJson(port, "/v1/users/me"); assert.equal(me.status, 401); assert.equal(me.body.error.code, "auth_required"); const missing = await getJson(port, "/v1/not-implemented"); assert.equal(missing.status, 404); assert.equal(missing.body.error.code, "not_found"); assert.equal(missing.body.error.audit.source.serviceId, "hwlab-cloud-api"); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api first-admin setup opens access when bootstrap secret is absent", async () => { const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1" }, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const before = await getJson(port, "/v1/setup/status"); assert.equal(before.status, 200); assert.equal(before.body.setupRequired, true); assert.equal(before.body.bootstrapAdminConfigured, false); assert.equal(before.body.routes.firstAdminSetup, "/v1/setup/first-admin"); const setup = await postJson(port, "/v1/setup/first-admin", { username: "admin", password: "admin-pass", displayName: "Initial Admin", devicePod: { devicePodId: "device-pod-71-freq", name: "71-FREQ", profile: { schemaVersion: 1, devicePodId: "device-pod-seed-spoof", target: { id: "target-71-freq" }, route: { gatewaySessionId: "gws_first_admin_seed", resourceId: "res_windows_host", capabilityId: "cap_device_host_cli", hostWorkspaceRoot: "F:\\Work\\Project", hostCli: "node tools/device-host-cli.mjs" } } } }); assert.equal(setup.status, 201); assert.equal(setup.body.created, true); assert.equal(setup.body.authenticated, true); assert.equal(setup.body.actor.role, "admin"); assert.equal(setup.body.actor.username, "admin"); assert.equal(setup.body.setupRequired, false); assert.deepEqual(setup.body.devicePodBootstrap, { requested: 1, initialized: 1 }); assert.equal(setup.body.devicePodsInitialized[0].devicePod.devicePodId, "device-pod-71-freq"); assert.equal(setup.body.devicePodsInitialized[0].grant.userId, setup.body.actor.id); assert.match(setup.body.devicePodsInitialized[0].devicePod.profileHash, /^sha256:/u); assert.equal(setup.body.devicePodsInitialized[0].devicePod.profile.route.gatewaySessionId, "redacted"); assert.equal(JSON.stringify(setup.body).includes("device-pod-seed-spoof"), false); assert.equal(JSON.stringify(setup.body).includes("admin-pass"), false); assert.equal(JSON.stringify(setup.body).includes("gws_first_admin_seed"), false); assert.equal(JSON.stringify(setup.body).includes("F:\\Work\\Project"), false); assert.equal(typeof setup.cookie, "string"); const session = await getJson(port, "/v1/users/me", setup.cookie); assert.equal(session.status, 200); assert.equal(session.body.actor.role, "admin"); assert.equal(JSON.stringify(session.body).includes("passwordHash"), false); const second = await postJson(port, "/v1/setup/first-admin", { username: "other-admin", password: "other-pass" }); assert.equal(second.status, 409); assert.equal(second.body.error.code, "setup_already_completed"); const pods = await getJson(port, "/v1/device-pods", setup.cookie); assert.equal(pods.status, 200); assert.equal(pods.body.devicePods.length, 1); assert.equal(pods.body.devicePods[0].devicePodId, "device-pod-71-freq"); assert.match(pods.body.devicePods[0].profileHash, /^sha256:/u); const job = await postJson(port, "/v1/device-pods/device-pod-71-freq/jobs", { intent: "workspace.ls", args: { path: "." } }, setup.cookie); assert.equal(job.status, 409); assert.equal(job.body.devicePodId, "device-pod-71-freq"); assert.equal(job.body.blocker.code, "device_pod_executor_unavailable"); const login = await postJson(port, "/auth/login", { username: "admin", password: "admin-pass" }); assert.equal(login.status, 200); assert.equal(login.body.actor.role, "admin"); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api bootstrap password synchronizes existing admin", async () => { const now = () => "2026-05-28T00:00:00.000Z"; const firstAccessController = createAccessController({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1" }, now }); const firstServer = createCloudApiServer({ accessController: firstAccessController, env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1" }, now }); await new Promise((resolve) => firstServer.listen(0, "127.0.0.1", resolve)); try { const { port } = firstServer.address(); const setup = await postJson(port, "/v1/setup/first-admin", { username: "admin", password: "old-pass" }); assert.equal(setup.status, 201); } finally { await new Promise((resolve, reject) => firstServer.close((error) => (error ? reject(error) : resolve()))); } const syncAccessController = createAccessController({ store: firstAccessController.store, env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1", HWLAB_BOOTSTRAP_ADMIN_ID: "usr_v02_admin", HWLAB_BOOTSTRAP_ADMIN_USERNAME: "admin", HWLAB_BOOTSTRAP_ADMIN_PASSWORD: "rotated-pass" }, now }); const server = createCloudApiServer({ accessController: syncAccessController, env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1" }, now }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const oldLogin = await postJson(port, "/auth/login", { username: "admin", password: "old-pass" }); assert.equal(oldLogin.status, 401); const rotatedLogin = await postJson(port, "/auth/login", { username: "admin", password: "rotated-pass" }); assert.equal(rotatedLogin.status, 200); assert.equal(rotatedLogin.body.actor.username, "admin"); assert.equal(rotatedLogin.body.actor.role, "admin"); assert.equal(JSON.stringify(rotatedLogin.body).includes("rotated-pass"), false); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); test("cloud api first-admin setup validates device-pod seed before creating admin", async () => { const server = createCloudApiServer({ env: { HWLAB_ACCESS_CONTROL_REQUIRED: "1" }, now: () => "2026-05-28T00:00:00.000Z" }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const invalid = await postJson(port, "/v1/setup/first-admin", { username: "admin", password: "admin-pass", devicePod: { devicePodId: "device-pod-71-freq" } }); assert.equal(invalid.status, 400); assert.equal(invalid.body.error.code, "invalid_params"); const before = await getJson(port, "/v1/setup/status"); assert.equal(before.status, 200); assert.equal(before.body.setupRequired, true); const ambiguous = await postJson(port, "/v1/setup/first-admin", { username: "admin", password: "admin-pass", devicePod: { devicePodId: "device-pod-71-freq", profile: { schemaVersion: 1 } }, devicePods: [{ devicePodId: "device-pod-alt", profile: { schemaVersion: 1 } }] }); assert.equal(ambiguous.status, 400); assert.equal(ambiguous.body.error.code, "invalid_params"); const secretSeed = await postJson(port, "/v1/setup/first-admin", { username: "admin", password: "admin-pass", devicePod: { devicePodId: "device-pod-71-freq", profile: { schemaVersion: 1, target: { id: "target-71-freq" }, route: { gatewaySessionId: "gws_seed" }, databaseUrl: "postgres://user:pass@example.invalid/hwlab" } } }); assert.equal(secretSeed.status, 400); assert.equal(secretSeed.body.error.code, "device_pod_profile_secret_forbidden"); assert.equal(JSON.stringify(secretSeed.body).includes("postgres://"), false); const afterSecretSeed = await getJson(port, "/v1/setup/status"); assert.equal(afterSecretSeed.status, 200); assert.equal(afterSecretSeed.body.setupRequired, true); const setup = await postJson(port, "/v1/setup/first-admin", { username: "admin", password: "admin-pass" }); assert.equal(setup.status, 201); assert.equal(setup.body.actor.role, "admin"); assert.deepEqual(setup.body.devicePodBootstrap, { requested: 0, initialized: 0 }); } finally { await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); } }); async function postJson(port, path, body, cookie = null, extraHeaders = {}) { const response = await fetch(`http://127.0.0.1:${port}${path}`, { method: "POST", headers: { "content-type": "application/json", ...(cookie ? { cookie } : {}), ...extraHeaders }, body: JSON.stringify(body) }); return { status: response.status, cookie: response.headers.get("set-cookie"), body: await response.json() }; } function devicePodInternalHeaders(extra = {}) { return { ...extra, "x-hwlab-internal-service": "hwlab-device-pod", "x-hwlab-internal-token": INTERNAL_TOKEN }; } async function putJson(port, path, body, cookie = null, extraHeaders = {}) { const response = await fetch(`http://127.0.0.1:${port}${path}`, { method: "PUT", headers: { "content-type": "application/json", ...(cookie ? { cookie } : {}), ...extraHeaders }, body: JSON.stringify(body) }); return { status: response.status, body: await response.json(), cookie: response.headers.get("set-cookie") }; } async function patchJson(port, path, body, cookie = null, extraHeaders = {}) { const response = await fetch(`http://127.0.0.1:${port}${path}`, { method: "PATCH", headers: { "content-type": "application/json", ...(cookie ? { cookie } : {}), ...extraHeaders }, body: JSON.stringify(body) }); return { status: response.status, body: await response.json(), cookie: response.headers.get("set-cookie") }; } async function getJson(port, path, cookie = null, extraHeaders = {}) { const response = await fetch(`http://127.0.0.1:${port}${path}`, { headers: { ...(cookie ? { cookie } : {}), ...extraHeaders } }); return { status: response.status, body: await response.json() }; } async function requestJson(request) { const chunks = []; for await (const chunk of request) chunks.push(chunk); const text = Buffer.concat(chunks).toString("utf8").trim(); return text ? JSON.parse(text) : {}; }