Files
pikasTech-HWLAB/skills/hwlab-code-agent/scripts/hwlab-code-agent-cli.ts
T

158 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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<string, string | undefined> {
const args: Record<string, string | undefined> = {};
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 <traceId> [--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 <traceId>" } }) + "\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 <traceId> [--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 <traceId> [--timeout 600000]",
result: "bun scripts/hwlab-code-agent-cli.ts result <traceId>",
trace: "bun scripts/hwlab-code-agent-cli.ts trace <traceId> [--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<string, string | undefined>) {
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<string, unknown>) {
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);
});