From 9aaaf5af94b004e00c189fbe13cb9a421edfbca5 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 17 Jul 2026 09:16:47 +0200 Subject: [PATCH] =?UTF-8?q?test:=20=E8=A6=86=E7=9B=96=20Workbench=20CLI=20?= =?UTF-8?q?=E5=8F=8C=E6=A8=A1=E5=BC=8F=20smoke?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/workbench/http.ts | 6 +++++- scripts/workbench-native-smoke.mjs | 32 +++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/internal/workbench/http.ts b/internal/workbench/http.ts index 52cdf3fd..fe369c58 100644 --- a/internal/workbench/http.ts +++ b/internal/workbench/http.ts @@ -21,7 +21,11 @@ export function createWorkbenchHttpApp(options: { dispatch: (command: WorkbenchC const turnMatch = /^\/v1\/workbench\/turns\/([^/]+)$/u.exec(url.pathname); if (turnMatch && request.method === "GET") return nativeTurn(options, decodeURIComponent(turnMatch[1])); } - if (url.pathname === "/v1/workbench/commands" && request.method === "POST") { requireAuthorization(request, options); return resultResponse(await options.dispatch(await bodyObject(request) as WorkbenchCommand), 202); } + if (url.pathname === "/v1/workbench/commands" && request.method === "POST") { + requireAuthorization(request, options); + const command = await bodyObject(request) as WorkbenchCommand; + return resultResponse(await options.dispatch(command), command.operation === "health" ? 200 : 202); + } const actor = actorFrom(request, Boolean(options.snapshot)); if (url.pathname === "/v1/agent/sessions" && request.method === "POST") { requireAuthorization(request, options); return legacyResponse(await options.dispatch({ operation: "session.create", actor, params: await bodyObject(request) }), 201); } if (url.pathname === "/v1/agent/chat" && request.method === "POST") { diff --git a/scripts/workbench-native-smoke.mjs b/scripts/workbench-native-smoke.mjs index cb57925d..943c5dd2 100755 --- a/scripts/workbench-native-smoke.mjs +++ b/scripts/workbench-native-smoke.mjs @@ -4,16 +4,21 @@ import { mkdtemp, rm } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import { runWorkbenchCli } from "../tools/src/workbench-cli.ts"; + const root = path.resolve(import.meta.dirname, ".."); const stateDir = await mkdtemp(path.join(os.tmpdir(), "hwlab-workbench-native-")); const children = []; const logs = []; try { - const api = start(["bun", "cmd/hwlab-workbench-api/main.ts"], { + const nativeEnv = { ...process.env, WORKBENCH_MODE: "native-test", - WORKBENCH_NATIVE_STATE_FILE: path.join(stateDir, "state.json"), + WORKBENCH_NATIVE_STATE_FILE: path.join(stateDir, "state.json") + }; + const api = start(["bun", "cmd/hwlab-workbench-api/main.ts"], { + ...nativeEnv, WORKBENCH_API_HOST: "127.0.0.1", WORKBENCH_API_PORT: "6677" }, "api"); @@ -27,6 +32,27 @@ try { const commandHealth = await requiredJson(await fetch("http://127.0.0.1:6677/v1/workbench/commands", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ operation: "health" }) }), "command.health"); if (commandHealth.mode !== "native-test") throw new Error("native command transport did not expose mode=native-test"); + const localSession = await runWorkbenchCli(["session", "create", "--actor-id", "usr_native", "--provider-profile", "native-test"], nativeEnv); + const localSessionId = localSession.identity?.sessionId; + if (localSession.transport !== "local" || !localSessionId) throw new Error("CLI local session transport contract failed"); + const localTraceId = "trc_native_cli_local"; + const localSubmit = await runWorkbenchCli(["turn", "submit", "--actor-id", "usr_native", "--session-id", localSessionId, "--trace-id", localTraceId, "--message", "native CLI local smoke"], nativeEnv); + if (localSubmit.transport !== "local" || localSubmit.data?.resultSynthesized !== false) throw new Error("CLI local submit authority contract failed"); + const localCancel = await runWorkbenchCli(["turn", "cancel", "--actor-id", "usr_native", "--trace-id", localTraceId], nativeEnv); + if (localCancel.data?.status !== "cancel_requested") throw new Error("CLI local cancel was not accepted"); + + const overApiArgs = ["--over-api", "--api-url", "http://127.0.0.1:6677"]; + const apiHealth = await runWorkbenchCli(["health", ...overApiArgs], nativeEnv); + if (apiHealth.transport !== "api" || apiHealth.httpStatus !== 200) throw new Error("CLI --over-api health transport contract failed"); + const apiSession = await runWorkbenchCli(["session", "create", "--actor-id", "usr_native", "--provider-profile", "native-test", ...overApiArgs], nativeEnv); + const apiSessionId = apiSession.identity?.sessionId; + if (apiSession.transport !== "api" || !apiSessionId) throw new Error("CLI --over-api session transport contract failed"); + const apiTraceId = "trc_native_cli_over_api"; + const apiSubmit = await runWorkbenchCli(["turn", "submit", "--actor-id", "usr_native", "--session-id", apiSessionId, "--trace-id", apiTraceId, "--message", "native CLI over API smoke", ...overApiArgs], nativeEnv); + if (apiSubmit.transport !== "api" || apiSubmit.data?.resultSynthesized !== false) throw new Error("CLI --over-api submit authority contract failed"); + const apiCancel = await runWorkbenchCli(["turn", "cancel", "--actor-id", "usr_native", "--trace-id", apiTraceId, ...overApiArgs], nativeEnv); + if (apiCancel.data?.status !== "cancel_requested") throw new Error("CLI --over-api cancel was not accepted"); + const actorHeaders = { "content-type": "application/json", cookie: "hwlab_session=native-test" }; const sessionResponse = await fetch("http://127.0.0.1:6677/v1/agent/sessions", { method: "POST", headers: actorHeaders, body: JSON.stringify({ providerProfile: "native-test" }) }); const session = await requiredJson(sessionResponse, "session.create"); @@ -39,7 +65,7 @@ try { const cancel = await requiredJson(await fetch("http://127.0.0.1:6677/v1/agent/chat/cancel", { method: "POST", headers: actorHeaders, body: JSON.stringify({ sessionId, traceId }) }), "turn.cancel"); if (cancel.status !== "cancel_requested") throw new Error("native cancel was not accepted"); - process.stdout.write(`${JSON.stringify({ ok: true, mode: "native-test", apiUrl: "http://127.0.0.1:6677", webUrl: "http://127.0.0.1:5173/workbench", sessionId, traceId, services: { api: "independent", web: "vite-hmr" }, terminalAuthority: "fixture-only", resultSynthesized: false })}\n`); + process.stdout.write(`${JSON.stringify({ ok: true, mode: "native-test", apiUrl: "http://127.0.0.1:6677", webUrl: "http://127.0.0.1:5173/workbench", sessionId, traceId, cli: { local: "application-dispatcher", overApi: "POST /v1/workbench/commands" }, services: { api: "independent", web: "vite-hmr" }, terminalAuthority: "fixture-only", resultSynthesized: false })}\n`); } catch (error) { process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "workbench_native_smoke_failed", message: error instanceof Error ? error.message : String(error) }, logs: logs.slice(-20) })}\n`); process.exitCode = 1;