#!/usr/bin/env bun /** * hwlab-code-agent CLI — HWLAB Code Agent 客户端 * Skill(cli-spec) * * 通过 hwlab-code-agent 直接调 HWLAB v0.2 cloud-api 管理 agent turn: * spawn - 创建 session + 提交 prompt,立即返回 traceId * poll - 轮询等待 turn 完成 * result - 查询 turn 结果 * trace - 查询 turn trace */ import path from "node:path"; import { spawn, poll, getResult, getTrace } from "./src/client.ts"; import { queryKafkaEventStream } from "../../../internal/cloud/kafka-event-bridge.ts"; const argv = process.argv.slice(2); const cmd = argv[0]; function parseArgs(from: number): Record { const args: Record = {}; for (let i = from; i < argv.length; i++) { if (argv[i].startsWith("--")) { const key = argv[i].slice(2); const val = (i + 1 < argv.length && !argv[i + 1].startsWith("--")) ? argv[i + 1] : "true"; args[key] = val; if (val !== "true") i++; } } return args; } async function main() { switch (cmd) { case "spawn": { const args = parseArgs(1); await spawn(args); break; } case "poll": { const traceId = argv[1]; if (!traceId) { process.stderr.write(JSON.stringify({ ok: false, error: { code: "usage", message: "poll [--timeout N]" } }) + "\n"); process.exit(1); } await poll(traceId, parseArgs(2)); break; } case "result": { const traceId = argv[1]; if (!traceId) { process.stderr.write(JSON.stringify({ ok: false, error: { code: "usage", message: "result " } }) + "\n"); process.exit(1); } await getResult(traceId); break; } case "trace": { const traceId = argv[1]; if (!traceId) { process.stderr.write(JSON.stringify({ ok: false, error: { code: "usage", message: "trace [--full]" } }) + "\n"); process.exit(1); } await getTrace(traceId, parseArgs(2)); break; } case "kafka": { const subcommand = argv[1] || "tail"; if (subcommand !== "tail" && subcommand !== "query") { process.stderr.write(JSON.stringify({ ok: false, error: { code: "usage", message: "kafka tail [--stream hwlab|agentrun] [--trace-id TRACE_ID] [--session-id SESSION_ID] [--run-id RUN_ID] [--command-id COMMAND_ID]" } }) + "\n"); process.exit(1); } await kafkaTail(parseArgs(2)); break; } default: process.stderr.write(JSON.stringify({ ok: false, usage: { spawn: "HWLAB_CODE_AGENT_PROVIDER_PROFILE=PROFILE bun scripts/hwlab-code-agent-cli.ts spawn --message '...' [--profile PROFILE]", poll: "bun scripts/hwlab-code-agent-cli.ts poll [--timeout 600000]", result: "bun scripts/hwlab-code-agent-cli.ts result ", trace: "bun scripts/hwlab-code-agent-cli.ts trace [--full]", kafka: "bun tools/hwlab-code-agent kafka tail --stream hwlab --trace-id trc_... --limit 20" } }, null, 2) + "\n"); process.exit(1); } } async function kafkaTail(args: Record) { const result = await queryKafkaEventStream({ env: process.env, stream: args.stream || "hwlab", topic: args.topic || null, traceId: args["trace-id"] || args.traceId || null, sessionId: args["session-id"] || args.sessionId || null, runId: args["run-id"] || args.runId || null, commandId: args["command-id"] || args.commandId || null, limit: boundedInteger(args.limit, 20, 1, 500), timeoutMs: boundedInteger(args["timeout-ms"] || args.timeoutMs, 5000, 250, 60000), fromBeginning: args["from-end"] === "true" || args.fromEnd === "true" ? false : true }); process.stdout.write(JSON.stringify({ ok: true, action: "hwlab-code-agent.kafka.tail", stream: result.stream, topic: result.topic, count: result.count, limit: result.limit, timeoutMs: result.timeoutMs, filters: result.filters, rows: result.events.map(kafkaEventRow), events: args.full === "true" ? result.events : undefined, valuesPrinted: false }, null, 2) + "\n"); } function kafkaEventRow(record: any) { const value = record?.value && typeof record.value === "object" ? record.value : {}; const event = value.event && typeof value.event === "object" ? value.event : {}; const context = value.context && typeof value.context === "object" ? value.context : {}; return clean({ topic: record.topic, partition: record.partition, offset: record.offset, key: record.key, producedAt: text(value.producedAt), eventType: text(value.eventType ?? event.eventType), traceId: text(value.traceId ?? event.traceId), sessionId: text(value.sessionId ?? event.sessionId), runId: text(context.runId ?? event.runId ?? value.runId), commandId: text(context.commandId ?? event.commandId ?? value.commandId), label: text(event.label), status: text(event.status), sourceSeq: context.sourceSeq ?? event.sourceSeq }); } function boundedInteger(value: unknown, fallback: number, min: number, max: number) { const parsed = Number.parseInt(String(value ?? ""), 10); const selected = Number.isFinite(parsed) ? parsed : fallback; return Math.min(max, Math.max(min, selected)); } function text(value: unknown) { return String(value ?? "").trim(); } function clean(value: Record) { return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined && item !== "" && item !== null)); } main().catch((e) => { process.stderr.write(JSON.stringify({ ok: false, error: { code: "unhandled", message: e.message } }) + "\n"); process.exit(1); });