Allow dynamic AgentRun provider profiles
This commit is contained in:
@@ -81,7 +81,8 @@ test("hwlab-cli client access uses API key auth on Cloud Web admin access routes
|
||||
|
||||
test("hwlab-cli client provider-profiles uses Cloud Web admin routes", async () => {
|
||||
const calls: any[] = [];
|
||||
const result = await runHwlabCli(["client", "provider-profiles", "list", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-admin"], {
|
||||
const result = await runHwlabCli(["client", "provider-profiles", "list", "--base-url", "http://web.test"], {
|
||||
env: { HWLAB_API_KEY: "hwl_live_admin_test_key" },
|
||||
fetchImpl: async (url, init) => {
|
||||
calls.push({ url: String(url), init });
|
||||
return new Response(JSON.stringify({
|
||||
@@ -96,8 +97,10 @@ test("hwlab-cli client provider-profiles uses Cloud Web admin routes", async ()
|
||||
assert.equal(result.exitCode, 0);
|
||||
assert.equal(calls[0].url, "http://web.test/v1/admin/provider-profiles");
|
||||
assert.equal(calls[0].init.method, "GET");
|
||||
assert.equal(calls[0].init.headers.cookie, "hwlab_session=session-admin");
|
||||
assert.equal(calls[0].init.headers.authorization, "Bearer hwl_live_admin_test_key");
|
||||
assert.equal(calls[0].init.headers.cookie, undefined);
|
||||
assert.equal(result.payload.route.path, "/v1/admin/provider-profiles");
|
||||
assert.equal(result.payload.auth.authMethod, "api-key");
|
||||
assert.equal(result.payload.body.items[0].profile, "deepseek");
|
||||
assert.equal(result.payload.body.items[0].bridge.route, "HWLAB Moon Bridge -> DeepSeek official upstream");
|
||||
assert.equal(JSON.stringify(result.payload).includes("sk-"), false);
|
||||
@@ -1812,14 +1815,16 @@ test("hwlab-cli client agent result ignores stored Web session by default after
|
||||
});
|
||||
|
||||
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"], {
|
||||
const result = await runHwlabCli(["client", "agent", "trace", "trc_forbidden", "--base-url", "http://web.test"], {
|
||||
env: { HWLAB_API_KEY: "hwl_live_owner_test_key" },
|
||||
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.equal(result.payload.auth.authMethod, "api-key");
|
||||
assert.equal(result.payload.authDiagnosis.cookieSource, null);
|
||||
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 whoami/u);
|
||||
@@ -1829,6 +1834,7 @@ test("hwlab-cli client protected agent command reports missing credentials with
|
||||
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,
|
||||
env: {},
|
||||
fetchImpl: async () => new Response(JSON.stringify({ ok: false, error: { code: "auth_required" } }), { status: 401 })
|
||||
});
|
||||
|
||||
@@ -1858,10 +1864,9 @@ test("hwlab-cli client agent inspect builds protected inspect query", async () =
|
||||
"--thread-id",
|
||||
"thread-1",
|
||||
"--base-url",
|
||||
"http://web.test",
|
||||
"--cookie",
|
||||
"hwlab_session=session-a"
|
||||
"http://web.test"
|
||||
], {
|
||||
env: { HWLAB_API_KEY: "hwl_live_owner_test_key" },
|
||||
fetchImpl: async (url, init) => {
|
||||
calls.push({ url: String(url), init });
|
||||
return new Response(JSON.stringify({ ok: true, status: "found", latestTraceId: "trc_inspect" }), { status: 200 });
|
||||
@@ -1870,7 +1875,8 @@ test("hwlab-cli client agent inspect builds protected inspect query", async () =
|
||||
|
||||
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(calls[0].init.headers.authorization, "Bearer hwl_live_owner_test_key");
|
||||
assert.equal(calls[0].init.headers.cookie, undefined);
|
||||
assert.equal(result.payload.action, "client.agent.inspect");
|
||||
assert.equal(result.payload.body.status, "found");
|
||||
});
|
||||
@@ -2014,11 +2020,10 @@ test("hwlab-cli client request covers arbitrary Cloud Web same-origin API routes
|
||||
"/v1/access/status",
|
||||
"--base-url",
|
||||
"http://web.test",
|
||||
"--cookie",
|
||||
"hwlab_session=session-a",
|
||||
"--trace-id",
|
||||
"trc_request"
|
||||
], {
|
||||
env: { HWLAB_API_KEY: "hwl_live_admin_test_key" },
|
||||
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 });
|
||||
@@ -2027,7 +2032,8 @@ test("hwlab-cli client request covers arbitrary Cloud Web same-origin API routes
|
||||
|
||||
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.authorization, "Bearer hwl_live_admin_test_key");
|
||||
assert.equal(calls[0].init.headers.cookie, undefined);
|
||||
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");
|
||||
@@ -2063,8 +2069,6 @@ test("hwlab-cli client rpc mirrors Cloud Web JSON-RPC envelope metadata", async
|
||||
"system.health",
|
||||
"--base-url",
|
||||
"http://web.test",
|
||||
"--cookie",
|
||||
"hwlab_session=session-a",
|
||||
"--trace-id",
|
||||
"trc_rpc",
|
||||
"--id",
|
||||
@@ -2072,6 +2076,7 @@ test("hwlab-cli client rpc mirrors Cloud Web JSON-RPC envelope metadata", async
|
||||
"--params-json",
|
||||
"{}"
|
||||
], {
|
||||
env: { HWLAB_API_KEY: "hwl_live_admin_test_key" },
|
||||
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 });
|
||||
@@ -2080,7 +2085,8 @@ test("hwlab-cli client rpc mirrors Cloud Web JSON-RPC envelope metadata", async
|
||||
|
||||
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].init.headers.authorization, "Bearer hwl_live_admin_test_key");
|
||||
assert.equal(calls[0].init.headers.cookie, undefined);
|
||||
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" });
|
||||
|
||||
@@ -250,7 +250,7 @@ export function caseHelp() {
|
||||
`case prepare CASE_ID --case-repo ${STANDARD_CASE_REPO_PATH} [--run-id RUN_ID]`,
|
||||
`case build CASE_ID --case-repo ${STANDARD_CASE_REPO_PATH} [--run-dir DIR]`,
|
||||
`case collect CASE_ID --case-repo ${STANDARD_CASE_REPO_PATH} --run-dir DIR`,
|
||||
`case run CASE_ID --case-repo ${STANDARD_CASE_REPO_PATH} [--provider-profile deepseek|codex-api|minimax-m3]`,
|
||||
`case run CASE_ID --case-repo ${STANDARD_CASE_REPO_PATH} [--provider-profile PROFILE]`,
|
||||
`case run start CASE_ID --case-repo ${STANDARD_CASE_REPO_PATH} [--run-id RUN_ID]`,
|
||||
`case run status RUN_ID [--state-dir .state/hwlab-cli/caserun]`,
|
||||
`case run result RUN_ID [--state-dir .state/hwlab-cli/caserun]`,
|
||||
|
||||
+19
-15
@@ -149,16 +149,16 @@ function help() {
|
||||
"hwlab-cli client request GET /v1/access/status [--full]",
|
||||
"hwlab-cli client request GET /v1/web-performance/summary [--full]",
|
||||
"hwlab-cli client rpc system.health [--full]",
|
||||
"hwlab-cli client agent session create --provider-profile minimax-m3",
|
||||
"hwlab-cli client agent session create --provider-profile PROFILE",
|
||||
"hwlab-cli client agent session select SESSION_ID",
|
||||
"hwlab-cli client agent send --message TEXT|--message-file PATH [--from-trace TRACE] [--conversation-id ID] [--session-id ID] [--thread-id ID] [--retry-of TRACE] --provider-profile deepseek|codex-api|minimax-m3",
|
||||
"hwlab-cli client agent send --message TEXT|--message-file PATH [--from-trace TRACE] [--conversation-id ID] [--session-id ID] [--thread-id ID] [--retry-of TRACE] --provider-profile PROFILE",
|
||||
"hwlab-cli client agent send --message TEXT --wait --timeout-ms 50000",
|
||||
"hwlab-cli client agent result TRACE_ID",
|
||||
"hwlab-cli client agent trace TRACE_ID [--render web]",
|
||||
"hwlab-cli client agent inspect --trace-id TRACE_ID",
|
||||
"hwlab-cli client agent cancel TRACE_ID",
|
||||
"hwlab-cli client agent steer TRACE_ID --message TEXT|--message-file PATH",
|
||||
"hwlab-cli client harness submit --message TEXT --provider-profile deepseek|codex-api|minimax-m3",
|
||||
"hwlab-cli client harness submit --message TEXT --provider-profile PROFILE",
|
||||
"hwlab-cli client harness wait TRACE_ID --timeout-ms 50000",
|
||||
"hwlab-cli client harness audit TRACE_ID",
|
||||
"hwlab-cli case run CASE_ID --case-repo /root/hwlab-case-registry"
|
||||
@@ -428,8 +428,12 @@ async function providerProfileConfigFromStdin(context: any) {
|
||||
function normalizeProviderProfile(value: unknown) {
|
||||
const profile = text(value).toLowerCase();
|
||||
const normalized = profile === "codex" ? "codex-api" : profile;
|
||||
if (["deepseek", "codex-api", "minimax-m3"].includes(normalized)) return normalized;
|
||||
throw cliError("invalid_provider_profile", "provider profile must be deepseek, codex-api, or minimax-m3", { profile });
|
||||
if (/^[a-z0-9][a-z0-9-]{0,63}$/u.test(normalized) && normalized !== "runtime-default") return normalized;
|
||||
throw cliError("invalid_provider_profile", "provider profile must be a lowercase slug such as deepseek, minimax-m3, dsflash-go, or codex-api", {
|
||||
profile,
|
||||
alias: "codex -> codex-api",
|
||||
pattern: "^[a-z0-9][a-z0-9-]{0,63}$"
|
||||
});
|
||||
}
|
||||
|
||||
function validationTerminalStatus(body: any) {
|
||||
@@ -874,11 +878,11 @@ function agentHelp() {
|
||||
serviceRuntime: false,
|
||||
imagePublished: false,
|
||||
commands: [
|
||||
"session create [--provider-profile deepseek|codex-api|minimax-m3] [--conversation-id ID]",
|
||||
"session create [--provider-profile PROFILE] [--conversation-id ID]",
|
||||
"session select SESSION_ID [--workspace-id ID]",
|
||||
"session status SESSION_ID",
|
||||
"session list [--limit N]",
|
||||
"send --message TEXT|--message-file PATH [--from-trace TRACE] [--conversation-id ID] [--session-id ID] [--thread-id ID] [--retry-of TRACE] [--provider-profile deepseek|codex-api|minimax-m3] [default: short return]",
|
||||
"send --message TEXT|--message-file PATH [--from-trace TRACE] [--conversation-id ID] [--session-id ID] [--thread-id ID] [--retry-of TRACE] [--provider-profile PROFILE] [default: short return]",
|
||||
"composer status [--profile NAME] [--full]",
|
||||
"composer submit --message TEXT|--message-file PATH [--profile NAME] [default: auto turn/steer]",
|
||||
"send --message TEXT --wait [--timeout-ms N] [--poll-interval-ms N]",
|
||||
@@ -1177,7 +1181,7 @@ function agentSessionHelp() {
|
||||
serviceRuntime: false,
|
||||
imagePublished: false,
|
||||
commands: [
|
||||
"create [--provider-profile deepseek|codex-api|minimax-m3] [--conversation-id ID]",
|
||||
"create [--provider-profile PROFILE] [--conversation-id ID]",
|
||||
"select SESSION_ID [--workspace-id ID]",
|
||||
"status SESSION_ID",
|
||||
"list [--limit N]"
|
||||
@@ -1190,7 +1194,7 @@ async function agentSessionCreate(context: any) {
|
||||
const body = clean({
|
||||
conversationId: text(context.parsed.conversationId),
|
||||
sessionId: text(context.parsed.sessionId),
|
||||
providerProfile: text(context.parsed.providerProfile) || "minimax-m3",
|
||||
providerProfile: text(context.parsed.providerProfile) || text(workspaceState?.providerProfile) || "deepseek",
|
||||
projectId: text(context.parsed.projectId) || DEFAULT_WORKBENCH_PROJECT_ID,
|
||||
workspaceId: text(context.parsed.workspaceId) || text(workspaceState?.workspaceId),
|
||||
updatedByClient: "hwlab-cli"
|
||||
@@ -1444,7 +1448,7 @@ async function resolveAgentReplayContext(context: any) {
|
||||
sessionId: sessionId || null,
|
||||
threadId: threadId || null,
|
||||
sessionStatus,
|
||||
nextCommands: ["hwlab-cli client agent session create --provider-profile minimax-m3"]
|
||||
nextCommands: ["hwlab-cli client agent session create --provider-profile PROFILE"]
|
||||
});
|
||||
}
|
||||
return {
|
||||
@@ -1503,7 +1507,7 @@ function harnessHelp() {
|
||||
aliases: ["harness", "harness-ops", "harness-opt"],
|
||||
commands: [
|
||||
"health [--base-url URL]",
|
||||
"submit --message TEXT|--message-file PATH [--conversation-id ID] [--trace-id ID] [--provider-profile deepseek|codex-api|minimax-m3]",
|
||||
"submit --message TEXT|--message-file PATH [--conversation-id ID] [--trace-id ID] [--provider-profile PROFILE]",
|
||||
"result TRACE_ID [--full]",
|
||||
"trace TRACE_ID [--limit N] [--full]",
|
||||
"wait TRACE_ID [--timeout-ms N] [--poll-interval-ms N]",
|
||||
@@ -1707,8 +1711,8 @@ async function agentSend(context: any) {
|
||||
traceId,
|
||||
conversationId: conversationId || null,
|
||||
nextCommands: [
|
||||
"hwlab-cli client agent session create --provider-profile minimax-m3",
|
||||
"hwlab-cli client agent send --session-id <SESSION_ID> --message TEXT --provider-profile minimax-m3"
|
||||
"hwlab-cli client agent session create --provider-profile PROFILE",
|
||||
"hwlab-cli client agent send --session-id <SESSION_ID> --message TEXT --provider-profile PROFILE"
|
||||
]
|
||||
});
|
||||
}
|
||||
@@ -1719,14 +1723,14 @@ async function agentSend(context: any) {
|
||||
conversationId: conversationId || null,
|
||||
sessionId,
|
||||
sessionStatus,
|
||||
nextCommands: ["hwlab-cli client agent session create --provider-profile minimax-m3"]
|
||||
nextCommands: ["hwlab-cli client agent session create --provider-profile PROFILE"]
|
||||
});
|
||||
}
|
||||
if (!conversationId) {
|
||||
throw cliError("session_conversation_required", "selected Code Agent session does not expose conversationId; create a new session before sending", {
|
||||
traceId,
|
||||
sessionId,
|
||||
nextCommands: ["hwlab-cli client agent session create --provider-profile minimax-m3"]
|
||||
nextCommands: ["hwlab-cli client agent session create --provider-profile PROFILE"]
|
||||
});
|
||||
}
|
||||
const threadId = text(parsed.threadId) || replay.threadId || (explicitSessionRecord ? text(explicitSessionRecord.threadId) : "") || (workspaceMatchesSelectedSession ? text(workspaceState?.threadId) : "");
|
||||
|
||||
Reference in New Issue
Block a user