import assert from "node:assert/strict"; import { mkdtemp, readFile, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { test } from "bun:test"; import { runHwlabCli } from "../src/hwlab-cli-lib.ts"; test("hwlab-cli client login uses Cloud Web auth and stores only cookie session", async () => { const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-")); const calls: any[] = []; const result = await runHwlabCli([ "client", "auth", "login", "--base-url", "http://web.test", "--username", "admin", "--password-env", "HWLAB_PASSWORD" ], { cwd, env: { HWLAB_PASSWORD: "secret-password" }, fetchImpl: async (url, init) => { calls.push({ url: String(url), init, body: JSON.parse(String(init?.body ?? "{}")) }); return new Response(JSON.stringify({ authenticated: true, user: { id: "usr_admin", username: "admin", role: "admin" }, expiresAt: "2026-05-30T12:00:00.000Z" }), { status: 200, headers: { "content-type": "application/json", "set-cookie": "hwlab_session=session-a; Path=/; HttpOnly" } }); }, now: () => "2026-05-30T00:00:00.000Z" }); assert.equal(result.exitCode, 0); assert.equal(result.payload.action, "client.auth.login"); assert.equal(result.payload.cookieStored, true); assert.equal(calls[0].url, "http://web.test/auth/login"); assert.deepEqual(calls[0].body, { username: "admin", password: "secret-password" }); assert.equal(JSON.stringify(result.payload).includes("secret-password"), false); const session = JSON.parse(await readFile(path.join(cwd, ".state/hwlab-cli/session.json"), "utf8")); assert.equal(session.baseUrl, "http://web.test"); assert.equal(session.cookie, "hwlab_session=session-a"); assert.equal(JSON.stringify(session).includes("secret-password"), false); }); test("hwlab-cli client device-pods uses saved cookie and same Web API paths", async () => { const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-")); await runHwlabCli(["client", "auth", "login", "--base-url", "http://web.test", "--username", "admin", "--password", "pw"], { cwd, fetchImpl: async () => new Response(JSON.stringify({ authenticated: true, user: { username: "admin" } }), { status: 200, headers: { "set-cookie": "hwlab_session=session-a; Path=/" } }) }); const seen: any[] = []; const result = await runHwlabCli(["client", "device-pods", "status", "device-pod-71-freq", "--base-url", "http://web.test"], { cwd, fetchImpl: async (url, init) => { seen.push({ url: String(url), headers: init?.headers }); return new Response(JSON.stringify({ ok: true, devicePodId: "device-pod-71-freq", contractVersion: "device-pod-authority-v1", profileHash: "sha256:abc" }), { status: 200 }); } }); assert.equal(result.exitCode, 0); assert.equal(seen[0].url, "http://web.test/v1/device-pods/device-pod-71-freq/status"); assert.equal(seen[0].headers.cookie, "hwlab_session=session-a"); assert.equal(result.payload.route.path, "/v1/device-pods/device-pod-71-freq/status"); assert.equal(result.payload.body.devicePodId, "device-pod-71-freq"); assert.equal(result.payload.body.fullBodyAvailable, true); }); test("hwlab-cli client request reports auth username from the selected session", async () => { const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-profile-user-")); const calls: any[] = []; await runHwlabCli(["client", "auth", "login", "--base-url", "http://web.test", "--profile", "alice", "--username", "alice", "--password", "pw"], { cwd, fetchImpl: async () => new Response(JSON.stringify({ authenticated: true, user: { id: "usr_alice", username: "alice", role: "user" } }), { status: 200, headers: { "set-cookie": "hwlab_session=session-alice; Path=/" } }) }); const result = await runHwlabCli(["client", "request", "GET", "/v1/access/status", "--base-url", "http://web.test", "--profile", "alice"], { cwd, fetchImpl: async (url, init) => { calls.push({ url: String(url), init }); return new Response(JSON.stringify({ ok: true, authenticated: true, actor: { username: "alice" } }), { status: 200 }); } }); assert.equal(result.exitCode, 0); assert.equal(calls[0].init.headers.cookie, "hwlab_session=session-alice"); assert.equal(result.payload.auth.username, "alice"); assert.equal(result.payload.auth.localSession.user.username, "alice"); }); test("hwlab-cli client auth status exposes local session state and next commands", async () => { const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-auth-status-")); const missing = await runHwlabCli(["client", "auth", "status", "--base-url", "http://web.test"], { cwd }); assert.equal(missing.exitCode, 0); assert.equal(missing.payload.action, "client.auth.status"); assert.equal(missing.payload.localSession.exists, false); assert.equal(missing.payload.localSession.usable, false); assert.equal(missing.payload.localSession.ignoredReason, "state_file_missing"); assert.match(missing.payload.nextCommands[1], /client auth login/u); await runHwlabCli(["client", "auth", "login", "--base-url", "http://old-web.test", "--username", "admin", "--password", "pw"], { cwd, fetchImpl: async () => new Response(JSON.stringify({ authenticated: true, user: { username: "admin" } }), { status: 200, headers: { "set-cookie": "hwlab_session=session-old; Path=/" } }) }); const mismatch = await runHwlabCli(["client", "auth", "status", "--base-url", "http://web.test"], { cwd }); assert.equal(mismatch.exitCode, 0); assert.equal(mismatch.payload.localSession.exists, true); assert.equal(mismatch.payload.localSession.usable, false); assert.equal(mismatch.payload.localSession.ignoredReason, "base_url_mismatch"); assert.equal(JSON.stringify(mismatch.payload).includes("session-old"), false); assert.match(mismatch.payload.nextCommands[1], /^bun tools\/hwlab-cli\/bin\/hwlab-cli\.ts client auth login/u); assert.equal(JSON.stringify(mismatch.payload.nextCommands).includes("run-bun.mjs"), false); }); test("hwlab-cli reuses public v02 session when runtime namespace resolves host endpoint", async () => { const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-runtime-scope-")); await runHwlabCli(["client", "auth", "login", "--base-url", "http://74.48.78.17:19666", "--username", "admin", "--password", "pw"], { cwd, fetchImpl: async () => new Response(JSON.stringify({ authenticated: true, user: { username: "admin" } }), { status: 200, headers: { "set-cookie": "hwlab_session=session-v02; Path=/" } }) }); const status = await runHwlabCli(["client", "auth", "status"], { cwd, env: { HWLAB_RUNTIME_NAMESPACE: "hwlab-v02", HWLAB_RUNTIME_LANE: "v02", HWLAB_RUNTIME_ENDPOINT_LOCKED: "1", HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME: "1" } }); assert.equal(status.exitCode, 0); assert.equal(status.payload.baseUrl, "http://74.48.78.17:19666"); assert.equal(status.payload.localSession.usable, true); assert.equal(status.payload.localSession.ignoredReason, null); assert.equal(status.payload.localSession.baseUrlMatch, "exact"); assert.equal(status.payload.localSession.baseUrl, "http://74.48.78.17:19666"); }); test("hwlab-cli resolves runtime namespace to service DNS inside kubernetes", async () => { const result = await runHwlabCli(["client", "auth", "status"], { env: { HWLAB_RUNTIME_NAMESPACE: "hwlab-v02", HWLAB_RUNTIME_LANE: "v02", KUBERNETES_SERVICE_HOST: "10.43.0.1", POD_NAMESPACE: "hwlab-v02" } }); assert.equal(result.exitCode, 0); assert.equal(result.payload.baseUrl, "http://hwlab-cloud-web.hwlab-v02.svc.cluster.local:8080"); assert.equal(result.payload.runtimeEndpoint.source, "runtime-namespace"); }); test("hwlab-cli client group help is visible and does not issue HTTP requests", async () => { let fetchCount = 0; const fetchImpl = async () => { fetchCount += 1; return new Response(JSON.stringify({ ok: false }), { status: 500 }); }; const auth = await runHwlabCli(["client", "auth", "--help", "--base-url", "http://web.test"], { fetchImpl }); assert.equal(auth.exitCode, 0); assert.equal(auth.payload.action, "client.auth.help"); assert.equal(auth.payload.serviceRuntime, false); assert.ok(auth.payload.commands.some((command: string) => command.startsWith("login "))); const agent = await runHwlabCli(["client", "agent", "--help", "--base-url", "http://web.test"], { fetchImpl }); assert.equal(agent.exitCode, 0); assert.equal(agent.payload.action, "client.agent.help"); assert.equal(agent.payload.serviceRuntime, false); assert.ok(agent.payload.commands.some((command: string) => command.startsWith("send "))); assert.equal(fetchCount, 0); }); test("hwlab-cli rejects manual endpoint override in locked assembled runtime", async () => { const result = await runHwlabCli(["client", "auth", "status", "--base-url", "http://74.48.78.17:17666"], { env: { HWLAB_RUNTIME_WEB_URL: "http://cloud-v02.test", HWLAB_RUNTIME_NAMESPACE: "hwlab-v02", HWLAB_RUNTIME_LANE: "v02", HWLAB_RUNTIME_ENDPOINT_LOCKED: "1", HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME: "1" } }); assert.equal(result.exitCode, 1); assert.equal(result.payload.error.code, "runtime_endpoint_manual_url_forbidden"); assert.equal(result.payload.error.details.kind, "web"); }); test("hwlab-cli client device-pods events compacts event stream by default", async () => { const result = await runHwlabCli(["client", "device-pods", "events", "pod-1", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a"], { fetchImpl: async () => new Response(JSON.stringify({ ok: true, status: "ok", events: [{ eventId: "evt-1", level: "info", scope: "job", intent: "debug.chip-id", summary: "running", refs: { traceId: "trc_1" } }], lines: ["line-1"] }), { status: 200 }) }); assert.equal(result.exitCode, 0); assert.equal(result.payload.body.eventsCount, 1); assert.equal(result.payload.body.events[0].eventId, "evt-1"); assert.equal(result.payload.body.lines[0], "line-1"); assert.equal(result.payload.body.fullBodyAvailable, true); }); test("hwlab-cli client runtime routes reports UniDesk pod passthrough route", async () => { const result = await runHwlabCli(["client", "runtime", "routes", "--base-url", "http://web.test", "--service-id", "hwlab-cloud-api"], { fetchImpl: async () => new Response(JSON.stringify({ ok: true, status: "ok", services: [{ serviceId: "hwlab-cloud-api", runtime: { pod: { name: "hwlab-cloud-api-abc", namespace: "hwlab-v02", container: "hwlab-cloud-api" } } }] }), { status: 200 }) }); assert.equal(result.exitCode, 0); assert.equal(result.payload.items[0].unideskRoute, "G14:k3s:hwlab-v02:pod:hwlab-cloud-api-abc:hwlab-cloud-api"); assert.match(result.payload.items[0].useWith, /bun scripts\/cli\.ts ssh/u); assert.equal(result.payload.discovery.ok, true); assert.equal(result.payload.discovery.source, "cloud-web:/v1/live-builds"); assert.equal(result.payload.discovery.readyRouteCount, 1); }); test("hwlab-cli client gateway sessions uses direct Cloud API short connection", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "gateway", "sessions", "--api-base-url", "http://api.test", "--no-auth", "--full" ], { fetchImpl: async (url, init) => { calls.push({ url: String(url), init }); return new Response(JSON.stringify({ ok: true, sessions: [{ gatewaySessionId: "gws_D601_F103", inflightCount: 0 }] }), { status: 200 }); } }); assert.equal(result.exitCode, 0); assert.equal(calls[0].url, "http://api.test/v1/gateway/sessions"); assert.equal(calls[0].init.headers.cookie, undefined); assert.equal(result.payload.action, "client.gateway.sessions"); assert.equal(result.payload.baseUrl, "http://api.test"); assert.equal(result.payload.body.sessions[0].gatewaySessionId, "gws_D601_F103"); }); test("hwlab-cli client gateway pressure reports byte/truncation visibility", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "gateway", "pressure", "--api-base-url", "http://api.test", "--gateway-session-id", "gws_test", "--large-bytes", "8192", "--parallel", "2", "--request-timeout-ms", "10000", "--timeout-scenario-ms", "500" ], { fetchImpl: async (url, init) => { const callIndex = calls.length; const body = JSON.parse(String(init?.body ?? "{}")); calls.push({ url: String(url), init, body }); const dispatch: any = { dispatchStatus: "succeeded", shellExecuted: true, exitCode: 0, stdout: "", stderr: "" }; if (callIndex === 0) dispatch.stdout = "hwlab-gateway-pressure-small-ok\r\n"; else if (callIndex === 1 || callIndex === 2) Object.assign(dispatch, { stdout: "O".repeat(65536), stdoutTruncated: true }); else if (callIndex === 3) Object.assign(dispatch, { stderr: "E".repeat(65536), stderrTruncated: true }); else if (callIndex === 4) Object.assign(dispatch, { dispatchStatus: "timed_out", timedOut: true, exitCode: null }); else dispatch.stdout = `parallel-${callIndex - 4}-ok\r\n`; return new Response(JSON.stringify({ ok: true, result: { status: "succeeded", dispatch } }), { status: 200 }); } }); assert.equal(result.exitCode, 0); assert.equal(result.payload.action, "client.gateway.pressure"); assert.equal(result.payload.status, "succeeded"); assert.equal(result.payload.scenarioCount, 7); assert.equal(result.payload.failedCount, 0); assert.equal(calls[0].url, "http://api.test/v1/rpc/hardware.invoke.shell"); assert.equal(calls[0].init.headers.cookie, undefined); assert.equal(calls[0].body.gatewaySessionId, "gws_test"); assert.match(calls[1].body.input.command, /-EncodedCommand/u); assert.equal(result.payload.results.find((item: any) => item.name === "large-stdout").stdoutTruncated, true); assert.equal(result.payload.results.find((item: any) => item.name === "stderr-flood").stderrTruncated, true); assert.equal(result.payload.results.find((item: any) => item.name === "timeout-kill").reason, "timeout_structured"); assert.match(result.payload.results.find((item: any) => item.name === "large-stdout").stdoutSha256, /^[a-f0-9]{64}$/u); }); test("hwlab-cli client agent send submits async and returns trace without waiting by default", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "agent", "send", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--message", "hello", "--trace-id", "trc_test", "--conversation-id", "cnv_test" ], { fetchImpl: async (url, init) => { calls.push({ url: String(url), init, body: init?.body ? JSON.parse(String(init.body)) : null }); if (String(url).endsWith("/v1/workbench/workspace?projectId=prj_device_pod_workbench")) { return new Response(JSON.stringify({ ok: true, workspace: { workspaceId: "wsp_test", revision: 3, selectedConversationId: "cnv_test", selectedAgentSessionId: "ses_test", selectedConversation: { conversationId: "cnv_test", sessionId: "ses_test", threadId: "thread-test" }, workspace: {} } }), { status: 200 }); } if (String(url).endsWith("/v1/agent/chat")) { return new Response(JSON.stringify({ accepted: true, status: "running", traceId: "trc_test", resultUrl: "/v1/agent/chat/result/trc_test" }), { status: 202 }); } return new Response(JSON.stringify({ status: "completed", traceId: "trc_test", conversationId: "cnv_test", reply: { role: "assistant", content: "hi" } }), { status: 200 }); }, sleep: async () => {} }); assert.equal(result.exitCode, 0); assert.equal(calls[0].url, "http://web.test/v1/workbench/workspace?projectId=prj_device_pod_workbench"); assert.equal(calls[1].url, "http://web.test/v1/agent/chat"); assert.equal(calls[1].init.headers.prefer, "respond-async"); assert.equal(calls[1].body.shortConnection, true); assert.equal(calls[1].body.workspaceId, "wsp_test"); assert.equal(calls[1].body.expectedWorkspaceRevision, 3); assert.equal(calls[1].body.sessionId, "ses_test"); assert.equal(calls[1].body.threadId, "thread-test"); assert.equal(Object.hasOwn(calls[1].body, "conversationContext"), false); assert.equal(Object.hasOwn(calls[1].body, "messages"), false); assert.equal(calls.length, 2); assert.equal(result.payload.traceId, "trc_test"); assert.equal(result.payload.continuation.threadId, "thread-test"); assert.equal(result.payload.waited, false); assert.equal(result.payload.waitPolicy.defaultWait, false); assert.equal(result.payload.waitPolicy.webEquivalent, true); assert.ok(result.payload.waitPolicy.nextCommands.some((command: string) => command.includes("agent result trc_test"))); }); test("hwlab-cli client agent send waits only when --wait is explicit", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "agent", "send", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--message", "hello", "--trace-id", "trc_wait", "--conversation-id", "cnv_wait", "--wait", "--poll-interval-ms", "1", "--timeout-ms", "1000" ], { fetchImpl: async (url, init) => { calls.push({ url: String(url), init, body: init?.body ? JSON.parse(String(init.body)) : null }); if (String(url).endsWith("/v1/workbench/workspace?projectId=prj_device_pod_workbench")) { return new Response(JSON.stringify({ ok: true, workspace: { workspaceId: "wsp_wait", revision: 3, selectedConversationId: "cnv_wait", selectedAgentSessionId: "ses_wait", selectedConversation: { conversationId: "cnv_wait", sessionId: "ses_wait", threadId: "thread-wait" }, workspace: {} } }), { status: 200 }); } if (String(url).endsWith("/v1/agent/chat")) { return new Response(JSON.stringify({ accepted: true, status: "running", traceId: "trc_wait", resultUrl: "/v1/agent/chat/result/trc_wait" }), { status: 202 }); } if (String(url).endsWith("/v1/workbench/workspace/wsp_wait")) { return new Response(JSON.stringify({ ok: true, workspace: { workspaceId: "wsp_wait", revision: 5, selectedConversationId: "cnv_wait", selectedAgentSessionId: "ses_wait", selectedConversation: { conversationId: "cnv_wait", sessionId: "ses_wait", threadId: "thread-wait" }, workspace: { sessionStatus: "completed" } } }), { status: 200 }); } return new Response(JSON.stringify({ status: "completed", traceId: "trc_wait", conversationId: "cnv_wait", reply: { role: "assistant", content: "hi" } }), { status: 200 }); }, sleep: async () => {} }); assert.equal(result.exitCode, 0); assert.equal(calls[1].body.threadId, "thread-wait"); assert.equal(calls[2].url, "http://web.test/v1/agent/chat/result/trc_wait"); assert.equal(calls[3].url, "http://web.test/v1/workbench/workspace/wsp_wait"); assert.equal(Object.hasOwn(calls[3].body, "activeTraceId"), true); assert.equal(calls[3].body.activeTraceId, null); assert.equal(calls[3].body.threadId, "thread-wait"); assert.equal(result.payload.traceId, "trc_wait"); assert.equal(result.payload.continuation.threadId, "thread-wait"); assert.equal(result.payload.result.body.status, "completed"); assert.equal(result.payload.result.body.assistantText, "hi"); assert.equal(result.payload.result.body.reply.content, "hi"); }); test("hwlab-cli client agent send preserves Web continuation fields", async () => { const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-agent-continuation-")); await writeFile(path.join(cwd, "prompt.txt"), "看看有什么device-pod能用?", "utf8"); const calls: any[] = []; const result = await runHwlabCli([ "client", "agent", "send", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--message-file", "prompt.txt", "--trace-id", "trc_web_continue", "--conversation-id", "cnv_web_continue", "--session-id", "ses_web_continue", "--thread-id", "019e8078-db67-7750-a5d9-1a99f3abd445", "--retry-of", "trc_previous" ], { cwd, fetchImpl: async (url, init) => { calls.push({ url: String(url), init, body: init?.body ? JSON.parse(String(init.body)) : null }); if (String(url).includes("/v1/workbench/workspace?")) { return new Response(JSON.stringify({ ok: true, workspace: { workspaceId: "wsp_continue", revision: 7 } }), { status: 200 }); } return new Response(JSON.stringify({ accepted: true, status: "running", traceId: "trc_web_continue", resultUrl: "/v1/agent/chat/result/trc_web_continue" }), { status: 202 }); }, stdinText: "fallback stdin", sleep: async () => {} }); assert.equal(result.exitCode, 0); assert.equal(calls[0].url, "http://web.test/v1/workbench/workspace?projectId=prj_device_pod_workbench"); assert.equal(calls[1].url, "http://web.test/v1/agent/chat"); assert.equal(calls[1].body.message, "看看有什么device-pod能用?"); assert.equal(calls[1].body.conversationId, "cnv_web_continue"); assert.equal(calls[1].body.sessionId, "ses_web_continue"); assert.equal(calls[1].body.threadId, "019e8078-db67-7750-a5d9-1a99f3abd445"); assert.equal(calls[1].body.retryOf, "trc_previous"); assert.equal(result.payload.continuation.webEquivalent, true); assert.equal(result.payload.continuation.threadId, "019e8078-db67-7750-a5d9-1a99f3abd445"); assert.equal(result.payload.continuation.retryOf, "trc_previous"); assert.equal(JSON.stringify(result.payload).includes("hwlab_session=session-a"), false); }); test("hwlab-cli client agent send can replay continuation from an existing trace", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "agent", "send", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--message", "retry device pod question", "--from-trace", "trc_source_web", "--trace-id", "trc_replay_web" ], { fetchImpl: async (url, init) => { calls.push({ url: String(url), init, body: init?.body ? JSON.parse(String(init.body)) : null }); if (String(url).endsWith("/v1/agent/chat/inspect?traceId=trc_source_web")) { return new Response(JSON.stringify({ ok: true, status: "found", latestTraceId: "trc_source_web", query: { traceId: "trc_source_web" }, conversationFacts: { conversationId: "cnv_source", sessionId: "ses_source" }, session: { conversationId: "cnv_source", sessionId: "ses_source", threadId: "019e8078-db67-7750-a5d9-1a99f3abd445" } }), { status: 200 }); } if (String(url).endsWith("/v1/workbench/workspace?projectId=prj_device_pod_workbench")) { return new Response(JSON.stringify({ ok: true, workspace: { workspaceId: "wsp_replay", revision: 4 } }), { status: 200 }); } return new Response(JSON.stringify({ accepted: true, status: "running", traceId: "trc_replay_web", resultUrl: "/v1/agent/chat/result/trc_replay_web" }), { status: 202 }); }, sleep: async () => {} }); assert.equal(result.exitCode, 0); assert.equal(calls[0].url, "http://web.test/v1/agent/chat/inspect?traceId=trc_source_web"); assert.equal(calls[1].url, "http://web.test/v1/workbench/workspace?projectId=prj_device_pod_workbench"); assert.equal(calls[2].url, "http://web.test/v1/agent/chat"); assert.equal(calls[2].body.conversationId, "cnv_source"); assert.equal(calls[2].body.sessionId, "ses_source"); assert.equal(calls[2].body.threadId, "019e8078-db67-7750-a5d9-1a99f3abd445"); assert.equal(calls[2].body.retryOf, "trc_source_web"); assert.equal(calls[2].body.workspaceId, "wsp_replay"); assert.equal(calls[2].body.expectedWorkspaceRevision, 4); assert.equal(result.payload.continuation.replayedFromTrace, "trc_source_web"); assert.equal(result.payload.replay.source, "cloud-web:/v1/agent/chat/inspect"); assert.equal(JSON.stringify(result.payload).includes("hwlab_session=session-a"), false); }); test("hwlab-cli client agent steer posts to Web-equivalent steer route", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "agent", "steer", "trc_target_steer", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--message", "please include STEER_MARK", "--steer-trace-id", "trc_steer_cli", "--conversation-id", "cnv_steer", "--session-id", "ses_steer", "--thread-id", "thread-steer" ], { fetchImpl: async (url, init) => { calls.push({ url: String(url), init, body: init?.body ? JSON.parse(String(init.body)) : null }); return new Response(JSON.stringify({ ok: true, accepted: true, status: "running", route: "/v1/agent/chat/steer", traceId: "trc_target_steer", steerTraceId: "trc_steer_cli", conversationId: "cnv_steer", sessionId: "ses_steer", agentRun: { runId: "run_steer", targetCommandId: "cmd_turn", steerCommandId: "cmd_steer" }, traceUrl: "/v1/agent/chat/trace/trc_target_steer", resultUrl: "/v1/agent/chat/result/trc_target_steer" }), { status: 202 }); }, sleep: async () => {} }); assert.equal(result.exitCode, 0); assert.equal(calls[0].url, "http://web.test/v1/agent/chat/steer"); assert.equal(calls[0].init.headers["x-trace-id"], "trc_target_steer"); assert.equal(calls[0].init.headers.prefer, "respond-async"); assert.equal(calls[0].body.traceId, "trc_target_steer"); assert.equal(calls[0].body.targetTraceId, "trc_target_steer"); assert.equal(calls[0].body.steerTraceId, "trc_steer_cli"); assert.equal(calls[0].body.message, "please include STEER_MARK"); assert.equal(calls[0].body.conversationId, "cnv_steer"); assert.equal(calls[0].body.sessionId, "ses_steer"); assert.equal(calls[0].body.threadId, "thread-steer"); assert.equal(result.payload.action, "client.agent.steer"); assert.equal(result.payload.route.path, "/v1/agent/chat/steer"); assert.equal(result.payload.traceId, "trc_target_steer"); assert.equal(result.payload.steerTraceId, "trc_steer_cli"); assert.equal(result.payload.body.agentRun.steerCommandId, "cmd_steer"); assert.equal(result.payload.waitPolicy.webEquivalent, true); }); test("hwlab-cli agent composer status detects Web unlocked steer mode", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "agent", "composer", "status", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a" ], { fetchImpl: async (url, init) => { calls.push({ url: String(url), init }); return new Response(JSON.stringify({ ok: true, workspace: { workspaceId: "wsp_composer", revision: 9, selectedConversationId: "cnv_composer", selectedAgentSessionId: "ses_composer", workspace: { activeTraceId: "trc_composer_active", threadId: "thread-composer", sessionStatus: "running", messages: [{ role: "agent", status: "running", traceId: "trc_composer_active" }] } } }), { status: 200 }); } }); assert.equal(result.exitCode, 0); assert.equal(calls[0].url, "http://web.test/v1/workbench/workspace?projectId=prj_device_pod_workbench"); assert.equal(result.payload.action, "client.agent.composer.status"); assert.equal(result.payload.composer.webEquivalent, true); assert.equal(result.payload.composer.locked, false); assert.equal(result.payload.composer.disabled, false); assert.equal(result.payload.composer.submitMode, "steer"); assert.equal(result.payload.composer.route, "/v1/agent/chat/steer"); assert.equal(result.payload.composer.targetTraceId, "trc_composer_active"); assert.equal(result.payload.route.path, "/v1/agent/chat/steer"); }); test("hwlab-cli agent composer submit auto routes active Web turn to steer", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "agent", "composer", "submit", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--message", "please steer from composer", "--steer-trace-id", "trc_steer_composer" ], { fetchImpl: async (url, init) => { calls.push({ url: String(url), init, body: init?.body ? JSON.parse(String(init.body)) : null }); if (String(url).endsWith("/v1/workbench/workspace?projectId=prj_device_pod_workbench")) { return new Response(JSON.stringify({ ok: true, workspace: { workspaceId: "wsp_composer", revision: 9, selectedConversationId: "cnv_composer", selectedAgentSessionId: "ses_composer", workspace: { activeTraceId: "trc_composer_active", threadId: "thread-composer", sessionStatus: "running", messages: [{ role: "agent", status: "running", traceId: "trc_composer_active" }] } } }), { status: 200 }); } return new Response(JSON.stringify({ ok: true, accepted: true, status: "running", traceId: "trc_composer_active", steerTraceId: "trc_steer_composer", agentRun: { steerCommandId: "cmd_composer_steer" } }), { status: 202 }); }, sleep: async () => {} }); assert.equal(result.exitCode, 0); assert.equal(calls[1].url, "http://web.test/v1/agent/chat/steer"); assert.equal(calls[1].init.headers["x-trace-id"], "trc_composer_active"); assert.equal(calls[1].body.traceId, "trc_composer_active"); assert.equal(calls[1].body.targetTraceId, "trc_composer_active"); assert.equal(calls[1].body.steerTraceId, "trc_steer_composer"); assert.equal(calls[1].body.message, "please steer from composer"); assert.equal(calls[1].body.conversationId, "cnv_composer"); assert.equal(calls[1].body.sessionId, "ses_composer"); assert.equal(calls[1].body.threadId, "thread-composer"); assert.equal(result.payload.action, "client.agent.composer.submit"); assert.equal(result.payload.composer.locked, false); assert.equal(result.payload.composer.submitMode, "steer"); assert.equal(result.payload.route.path, "/v1/agent/chat/steer"); assert.equal(result.payload.body.agentRun.steerCommandId, "cmd_composer_steer"); }); test("hwlab-cli client agent trace shows assistant stream text in compact output", async () => { const result = await runHwlabCli(["client", "agent", "trace", "trc_test", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a"], { fetchImpl: async () => new Response(JSON.stringify({ status: "completed", traceId: "trc_test", assistantStreams: [{ status: "streaming", text: "hello from agent", chunkCount: 3 }], events: [{ status: "completed" }] }), { status: 200 }) }); assert.equal(result.exitCode, 0); assert.equal(result.payload.body.assistantText, "hello from agent"); assert.equal(result.payload.body.assistantStreams[0].text, "hello from agent"); assert.equal(result.payload.body.eventsCount, 1); }); test("hwlab-cli client agent trace can render with the Web trace row path", async () => { const result = await runHwlabCli(["client", "agent", "trace", "trc_render", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--render", "web"], { fetchImpl: async () => new Response(JSON.stringify({ status: "completed", traceId: "trc_render", events: [ { traceId: "trc_render", seq: 1, label: "request:accepted", status: "accepted", createdAt: "2026-06-01T13:00:00.000Z", promptSummary: "看看有什么device-pod能用?" }, { traceId: "trc_render", seq: 2, label: "session:reused", status: "observed", createdAt: "2026-06-01T13:00:01.000Z" }, { traceId: "trc_render", seq: 3, label: "assistant:completed", type: "assistant_message", terminal: true, status: "completed", itemId: "assistant-final", createdAt: "2026-06-01T13:00:02.000Z", message: "当前有两个 device-pod 可用。" } ] }), { status: 200 }) }); assert.equal(result.exitCode, 0); assert.equal(result.payload.body.render, "web"); assert.equal(result.payload.body.renderer, "web/hwlab-cloud-web/app-trace:traceDisplayRows"); assert.equal(result.payload.body.sourceEventCount, 3); assert.ok(result.payload.body.renderedRowCount >= 1); assert.ok(result.payload.body.rows.some((row: any) => /助手最后一条消息/u.test(row.header))); assert.equal(JSON.stringify(result.payload.body.rows).includes("session:reused"), false); }); test("hwlab-cli Web trace render reports suppressed noise and command tool details", async () => { const result = await runHwlabCli(["client", "agent", "trace", "trc_render_agentrun", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--render", "web"], { fetchImpl: async () => new Response(JSON.stringify({ status: "completed", traceId: "trc_render_agentrun", events: [ { traceId: "trc_render_agentrun", seq: 1, label: "agentrun:backend:command-created", status: "running", type: "backend", createdAt: "2026-06-01T13:00:00.000Z" }, { traceId: "trc_render_agentrun", seq: 2, label: "agentrun:backend:thread/status/changed", status: "running", type: "backend", createdAt: "2026-06-01T13:00:01.000Z" }, { traceId: "trc_render_agentrun", seq: 3, label: "item/commandExecution:completed", type: "tool_call", toolName: "commandExecution", status: "completed", itemId: "call_1", command: "/bin/sh -lc 'hwpod profile list'", exitCode: 0, stdoutSummary: '{"ok":true,"action":"profile.list"}', createdAt: "2026-06-01T13:00:02.000Z" }, { traceId: "trc_render_agentrun", seq: 4, label: "agentrun:tool:item/started", type: "tool_call", toolName: "commandExecution", status: "started", itemId: "call_2", command: "/bin/sh -lc 'printf TOOL_RENDER_OK'", createdAt: "2026-06-01T13:00:02.100Z" }, { traceId: "trc_render_agentrun", seq: 5, label: "agentrun:tool:item/completed", type: "tool_call", toolName: "commandExecution", status: "completed", itemId: "call_2", command: "/bin/sh -lc 'printf TOOL_RENDER_OK'", exitCode: 0, stdoutSummary: "TOOL_RENDER_OK", outputBytes: 14, createdAt: "2026-06-01T13:00:02.200Z" }, { traceId: "trc_render_agentrun", seq: 6, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "当前可见 device-pod。", createdAt: "2026-06-01T13:00:03.000Z" } ] }), { status: 200 }) }); assert.equal(result.exitCode, 0); assert.equal(result.payload.body.render, "web"); assert.equal(result.payload.body.sourceEventCount, 6); assert.equal(result.payload.body.noiseEventCount, 2); const text = JSON.stringify(result.payload.body.rows); assert.equal(text.includes("thread/status/changed"), false); assert.match(text, /hwpod profile list/u); assert.match(text, /profile\.list/u); assert.match(text, /TOOL_RENDER_OK/u); assert.equal(text.includes("toolName=item/started"), false); assert.equal(text.includes("toolName=item/completed"), false); assert.match(text, /当前可见 device-pod/u); }); test("hwlab-cli Web trace render keeps AgentRun middle assistant message", async () => { const result = await runHwlabCli(["client", "agent", "trace", "trc_render_agentrun_middle", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--render", "web"], { fetchImpl: async () => new Response(JSON.stringify({ status: "completed", traceId: "trc_render_agentrun_middle", events: [ { traceId: "trc_render_agentrun_middle", seq: 1, label: "agentrun:request:accepted", status: "running", type: "request", createdAt: "2026-06-01T13:00:00.000Z" }, { traceId: "trc_render_agentrun_middle", seq: 2, label: "agentrun:assistant:message", type: "assistant", status: "running", itemId: "msg_progress", messageIndex: 1, messageCount: 2, replyAuthority: false, final: false, message: "我先检查当前 device-pod 列表。", createdAt: "2026-06-01T13:00:01.000Z" }, { traceId: "trc_render_agentrun_middle", seq: 3, label: "agentrun:assistant:message", type: "assistant", status: "completed", itemId: "msg_final", messageIndex: 2, messageCount: 2, replyAuthority: true, final: true, terminal: true, message: "当前有 2 个 device-pod 可用。", createdAt: "2026-06-01T13:00:02.000Z" }, { traceId: "trc_render_agentrun_middle", seq: 4, label: "agentrun:terminal:completed", type: "result", terminal: true, status: "completed", createdAt: "2026-06-01T13:00:03.000Z" } ] }), { status: 200 }) }); assert.equal(result.exitCode, 0); assert.equal(result.payload.body.render, "web"); const text = JSON.stringify(result.payload.body.rows); assert.match(text, /我先检查当前 device-pod 列表/u); assert.match(text, /当前有 2 个 device-pod 可用/u); assert.match(text, /助手最后一条消息/u); }); test("hwlab-cli client agent trace auto logs in when protected trace has no local session", async () => { const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-auth-")); const calls: any[] = []; const result = await runHwlabCli([ "client", "agent", "trace", "trc_protected", "--base-url", "http://web.test", "--username", "admin" ], { cwd, env: { HWLAB_PASSWORD: "secret-password" }, fetchImpl: async (url, init) => { calls.push({ url: String(url), init, body: init?.body ? JSON.parse(String(init.body)) : null }); if (String(url).endsWith("/auth/login")) { return new Response(JSON.stringify({ authenticated: true, user: { username: "admin" } }), { status: 200, headers: { "set-cookie": "hwlab_session=session-auto; Path=/; HttpOnly" } }); } return new Response(JSON.stringify({ status: "completed", traceId: "trc_protected", events: [] }), { status: 200 }); }, now: () => "2026-05-31T07:20:00.000Z" }); assert.equal(result.exitCode, 0); assert.deepEqual(calls.map((call) => call.url), [ "http://web.test/auth/login", "http://web.test/v1/agent/chat/trace/trc_protected" ]); assert.equal(calls[1].init.headers.cookie, "hwlab_session=session-auto"); assert.equal(JSON.stringify(result.payload).includes("secret-password"), false); const session = JSON.parse(await readFile(path.join(cwd, ".state/hwlab-cli/session.json"), "utf8")); assert.equal(session.cookie, "hwlab_session=session-auto"); }); test("hwlab-cli client agent result refreshes expired session after 401 and reports compact result", async () => { const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-auth-refresh-")); await runHwlabCli(["client", "auth", "login", "--base-url", "http://web.test", "--username", "admin", "--password", "old"], { cwd, fetchImpl: async () => new Response(JSON.stringify({ authenticated: true, user: { username: "admin" } }), { status: 200, headers: { "set-cookie": "hwlab_session=session-old; Path=/" } }) }); const calls: any[] = []; const result = await runHwlabCli([ "client", "agent", "result", "trc_refresh", "--base-url", "http://web.test", "--username", "admin" ], { cwd, env: { HWLAB_PASSWORD: "new-password" }, fetchImpl: async (url, init) => { calls.push({ url: String(url), init, body: init?.body ? JSON.parse(String(init.body)) : null }); if (String(url).endsWith("/auth/login")) { return new Response(JSON.stringify({ authenticated: true, user: { username: "admin" } }), { status: 200, headers: { "set-cookie": "hwlab_session=session-new; Path=/" } }); } if (init?.headers?.cookie === "hwlab_session=session-old") { return new Response(JSON.stringify({ error: { code: "auth_required" } }), { status: 401 }); } return new Response(JSON.stringify({ status: "completed", traceId: "trc_refresh", reply: { role: "assistant", content: "done" } }), { status: 200 }); }, sleep: async () => {}, now: () => "2026-05-31T07:21:00.000Z" }); assert.equal(result.exitCode, 0); assert.deepEqual(calls.map((call) => call.url), [ "http://web.test/v1/agent/chat/result/trc_refresh", "http://web.test/auth/login", "http://web.test/v1/agent/chat/result/trc_refresh" ]); assert.equal(calls[0].init.headers.cookie, "hwlab_session=session-old"); assert.equal(calls[2].init.headers.cookie, "hwlab_session=session-new"); assert.equal(result.payload.action, "client.agent.result"); assert.equal(result.payload.body.assistantText, "done"); }); test("hwlab-cli client protected agent commands return structured auth diagnosis for forbidden trace", async () => { const result = await runHwlabCli(["client", "agent", "trace", "trc_forbidden", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a"], { fetchImpl: async () => new Response(JSON.stringify({ error: { code: "agent_session_owner_required" } }), { status: 403 }) }); assert.equal(result.exitCode, 1); assert.equal(result.payload.httpStatus, 403); assert.equal(result.payload.authDiagnosis.code, "auth_forbidden"); assert.equal(result.payload.authDiagnosis.cookieSource, "explicit"); assert.match(result.payload.authDiagnosis.message, /无权访问/u); assert.equal(result.payload.request.url, "http://web.test/v1/agent/chat/trace/trc_forbidden"); assert.match(result.payload.authDiagnosis.nextCommands[1], /client auth login/u); }); test("hwlab-cli client protected agent command reports missing credentials with state visibility", async () => { const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-cli-client-auth-missing-")); const result = await runHwlabCli(["client", "agent", "result", "trc_missing_auth", "--base-url", "http://web.test"], { cwd, fetchImpl: async () => new Response(JSON.stringify({ ok: false, error: { code: "auth_required" } }), { status: 401 }) }); assert.equal(result.exitCode, 1); assert.equal(result.payload.action, "client.agent.result"); assert.equal(result.payload.auth.required, true); assert.equal(result.payload.auth.localSession.exists, false); assert.equal(result.payload.authDiagnosis.code, "auth_credentials_missing"); assert.equal(result.payload.authDiagnosis.localSession.ignoredReason, "state_file_missing"); assert.match(result.payload.authDiagnosis.nextCommands[1], /--password-env HWLAB_PASSWORD/u); assert.equal(result.payload.request.httpStatus, 401); }); test("hwlab-cli client agent inspect builds protected inspect query", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "agent", "inspect", "--trace-id", "trc_inspect", "--conversation-id", "cnv_inspect", "--session-id", "ses_inspect", "--thread-id", "thread-1", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a" ], { fetchImpl: async (url, init) => { calls.push({ url: String(url), init }); return new Response(JSON.stringify({ ok: true, status: "found", latestTraceId: "trc_inspect" }), { status: 200 }); } }); assert.equal(result.exitCode, 0); assert.equal(calls[0].url, "http://web.test/v1/agent/chat/inspect?traceId=trc_inspect&conversationId=cnv_inspect&sessionId=ses_inspect&threadId=thread-1"); assert.equal(calls[0].init.headers.cookie, "hwlab_session=session-a"); assert.equal(result.payload.action, "client.agent.inspect"); assert.equal(result.payload.body.status, "found"); }); test("hwlab-cli client harness submits waits and audits trace friction", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "harness", "submit", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--message", "hello", "--trace-id", "trc_harness", "--conversation-id", "cnv_harness" ], { fetchImpl: async (url, init) => { calls.push({ url: String(url), init, body: JSON.parse(String(init?.body ?? "{}")) }); return new Response(JSON.stringify({ accepted: true, status: "running", resultUrl: "/v1/agent/chat/result/trc_harness", traceUrl: "/v1/agent/chat/trace/trc_harness" }), { status: 202 }); } }); assert.equal(result.exitCode, 0); assert.equal(result.payload.action, "client.harness.submit"); assert.equal(calls[0].url, "http://web.test/v1/agent/chat"); assert.equal(calls[0].init.headers.prefer, "respond-async"); assert.equal(calls[0].body.shortConnection, true); let polls = 0; const wait = await runHwlabCli([ "client", "harness-opt", "wait", "trc_harness", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--poll-interval-ms", "1", "--timeout-ms", "1000" ], { fetchImpl: async () => { polls += 1; return new Response(JSON.stringify(polls === 1 ? { status: "running", traceId: "trc_harness" } : { status: "completed", traceId: "trc_harness", reply: { content: "done" } }), { status: 200 }); }, sleep: async () => {} }); assert.equal(wait.exitCode, 0); assert.equal(wait.payload.timedOut, false); assert.equal(wait.payload.timeoutMsRequested, 1000); assert.equal(wait.payload.timeoutMsEffective, 1000); assert.equal(wait.payload.waitPolicy.remotePassthroughSafe, true); assert.equal(wait.payload.body.assistantText, "done"); const cappedWait = await runHwlabCli([ "client", "harness", "wait", "trc_harness", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--poll-interval-ms", "1000", "--timeout-ms", "60000" ], { fetchImpl: async () => new Response(JSON.stringify({ status: "completed", traceId: "trc_harness", reply: { content: "done" } }), { status: 200 }), sleep: async () => {} }); assert.equal(cappedWait.exitCode, 0); assert.equal(cappedWait.payload.status, "succeeded"); assert.equal(cappedWait.payload.timeoutMsRequested, 60000); assert.equal(cappedWait.payload.timeoutMsEffective, 50000); assert.equal(cappedWait.payload.waitPolicy.capped, true); assert.ok(cappedWait.payload.waitPolicy.nextCommands.some((command) => command.includes("harness result trc_harness"))); const audit = await runHwlabCli([ "client", "harness", "audit", "trc_harness", "--require-bootsharp", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a" ], { fetchImpl: async () => new Response(JSON.stringify({ traceId: "trc_harness", events: [ { type: "tool_call", command: "node /app/tools/device-pod-cli.mjs device-pod-a:workspace:/ put User/main.c" } ] }), { status: 200 }) }); assert.equal(audit.exitCode, 0); assert.equal(audit.payload.status, "friction_detected"); assert.ok(audit.payload.frictionSignals.some((signal) => signal.kind === "long_device_pod_cli_path")); assert.ok(audit.payload.frictionSignals.some((signal) => signal.kind === "workspace_put_text_edit")); assert.ok(audit.payload.frictionSignals.some((signal) => signal.kind === "missing_bootsharp")); }); test("hwlab-cli client harness audit treats hwpod bootsharp flow as clean", async () => { const audit = await runHwlabCli([ "client", "harness", "audit", "trc_hwpod", "--require-bootsharp", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a" ], { fetchImpl: async () => new Response(JSON.stringify({ traceId: "trc_hwpod", events: [ { type: "tool_call", command: "hwpod bootsharp --pod-id D601-F103-V2" }, { type: "tool_call", command: "hwpod D601-F103-V2:workspace:/ build status job_123" } ] }), { status: 200 }) }); assert.equal(audit.exitCode, 0); assert.equal(audit.payload.status, "clean"); assert.equal(audit.payload.summary.hasHwpod, true); assert.equal(audit.payload.summary.hasBootsharp, true); }); test("hwlab-cli client workbench summary probes Cloud Web non-visual surfaces", async () => { const seen: string[] = []; const result = await runHwlabCli(["client", "workbench", "summary", "--base-url", "http://web.test", "--cookie", "session-a", "--pod-id", "device-pod-71-freq"], { fetchImpl: async (url) => { seen.push(String(url)); return new Response(JSON.stringify({ ok: true, status: "ok" }), { status: 200 }); } }); assert.equal(result.exitCode, 0); assert.equal(seen.includes("http://web.test/health/live"), true); assert.equal(seen.includes("http://web.test/v1"), true); assert.equal(seen.includes("http://web.test/v1/device-pods"), true); assert.equal(seen.includes("http://web.test/v1/device-pods/device-pod-71-freq/status"), true); assert.equal(result.payload.serviceRuntime, undefined); }); test("hwlab-cli client request covers arbitrary Cloud Web same-origin API routes", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "request", "GET", "/v1/access/status", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--trace-id", "trc_request" ], { fetchImpl: async (url, init) => { calls.push({ url: String(url), init }); return new Response(JSON.stringify({ ok: true, authenticated: true, actor: { username: "admin" }, roles: ["admin"] }), { status: 200 }); } }); assert.equal(result.exitCode, 0); assert.equal(calls[0].url, "http://web.test/v1/access/status"); assert.equal(calls[0].init.headers.cookie, "hwlab_session=session-a"); assert.equal(calls[0].init.headers["x-trace-id"], "trc_request"); assert.equal(result.payload.action, "client.request"); assert.equal(result.payload.route.path, "/v1/access/status"); assert.equal(result.payload.body.actor.username, "admin"); assert.equal(result.payload.body.fullBodyAvailable, true); }); test("hwlab-cli client request compacts array responses by default", async () => { const result = await runHwlabCli([ "client", "request", "GET", "/v1/device-pods", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a" ], { fetchImpl: async () => new Response(JSON.stringify([{ id: "pod-1", status: "ready" }, { id: "pod-2", status: "ready" }]), { status: 200 }) }); assert.equal(result.exitCode, 0); assert.equal(result.payload.body.itemCount, 2); assert.equal(result.payload.body.items[0].id, "pod-1"); assert.equal(result.payload.body.fullBodyAvailable, true); }); test("hwlab-cli client rpc mirrors Cloud Web JSON-RPC envelope metadata", async () => { const calls: any[] = []; const result = await runHwlabCli([ "client", "rpc", "system.health", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--trace-id", "trc_rpc", "--id", "req_rpc", "--params-json", "{}" ], { fetchImpl: async (url, init) => { calls.push({ url: String(url), init, body: JSON.parse(String(init?.body ?? "{}")) }); return new Response(JSON.stringify({ jsonrpc: "2.0", id: "req_rpc", result: { status: "ok" }, meta: { traceId: "trc_rpc", serviceId: "hwlab-cloud-api", environment: "dev" } }), { status: 200 }); } }); assert.equal(result.exitCode, 0); assert.equal(calls[0].url, "http://web.test/json-rpc"); assert.equal(calls[0].init.headers.cookie, "hwlab_session=session-a"); assert.equal(calls[0].body.method, "system.health"); assert.deepEqual(calls[0].body.params, {}); assert.deepEqual(calls[0].body.meta, { traceId: "trc_rpc", serviceId: "hwlab-cloud-web", environment: "dev" }); assert.equal(result.payload.action, "client.rpc"); assert.equal(result.payload.rpcMethod, "system.health"); assert.equal(result.payload.body.result.status, "ok"); assert.equal(result.payload.body.fullBodyAvailable, true); }); test("hwlab-cli client request rejects absolute URLs", async () => { const result = await runHwlabCli(["client", "request", "GET", "http://internal.test/v1", "--base-url", "http://web.test"], { fetchImpl: async () => new Response("{}", { status: 200 }) }); assert.equal(result.exitCode, 1); assert.equal(result.payload.error.code, "invalid_request_path"); }); test("hwlab-cli client request keeps command visibility on transport failure", async () => { const result = await runHwlabCli(["client", "request", "GET", "/v1/access/status", "--base-url", "http://web.test", "--public"], { fetchImpl: async () => { throw Object.assign(new Error("connect ECONNREFUSED"), { name: "FetchError" }); } }); assert.equal(result.exitCode, 1); assert.equal(result.payload.action, "client.request"); assert.equal(result.payload.httpStatus, 0); assert.equal(result.payload.request.url, "http://web.test/v1/access/status"); assert.equal(result.payload.request.transportError.code, "request_failed"); assert.equal(result.payload.body.error.code, "request_failed"); });