feat: bridge AgentRun Kafka events to HWLAB stream

This commit is contained in:
root
2026-07-09 19:59:32 +02:00
parent 99d3f98ebc
commit 479c0f0937
4 changed files with 537 additions and 1 deletions
@@ -12,6 +12,7 @@
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];
@@ -63,6 +64,15 @@ async function main() {
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,
@@ -70,13 +80,77 @@ async function main() {
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]"
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);