feat: hwlab-code-agent tool and skill for agent scheduling
- tools/hwlab-code-agent-cli.ts: thin wrapper importing from skills/ - skills/hwlab-code-agent/: SKILL.md, config.json, scripts/src/client.ts - toolAliases: hwlab-code-agent (spawn/poll/result/trace) - skillRefs: hwlab-code-agent SKILL.md - hwpod spec auto-inheritance: --spec-path, CWD .hwlab/, HWPOD_SPEC_CONTENT - Leader agent can spawn Coder/Reviewer with inherited hwpod spec
This commit is contained in:
@@ -36,6 +36,7 @@ const HWLAB_RESOURCE_TOOL_ALIASES = Object.freeze([
|
||||
Object.freeze({ name: "hwpod-ctl", path: "tools/hwpod-ctl.ts", kind: "bun-script" }),
|
||||
Object.freeze({ name: "hwpod-compiler", path: "tools/hwpod-compiler-cli.ts", kind: "bun-script" }),
|
||||
Object.freeze({ name: "unidesk-ssh", path: "tools/unidesk-ssh.mjs", kind: "bun-script" })
|
||||
Object.freeze({ name: "hwlab-code-agent", path: "tools/hwlab-code-agent-cli.ts", kind: "bun-script" })
|
||||
]);
|
||||
const HWLAB_RESOURCE_PROMPT_REFS = Object.freeze([
|
||||
Object.freeze({ name: "hwlab-v02-runtime", path: "internal/agent/prompts/hwlab-v02-runtime.md", inject: "thread-start", required: true })
|
||||
@@ -44,6 +45,7 @@ const HWLAB_RESOURCE_SKILL_REFS = Object.freeze([
|
||||
Object.freeze({ name: "hwpod-cli", path: "skills/hwpod-cli/SKILL.md", required: true, aggregateAs: "hwpod-cli" }),
|
||||
Object.freeze({ name: "hwpod-ctl", path: "skills/hwpod-ctl/SKILL.md", required: true, aggregateAs: "hwpod-ctl" }),
|
||||
Object.freeze({ name: "hwlab-agent-runtime", path: "skills/hwlab-agent-runtime/SKILL.md", required: true, aggregateAs: "hwlab-agent-runtime" })
|
||||
Object.freeze({ name: "hwlab-code-agent", path: "skills/hwlab-code-agent/SKILL.md", required: true, aggregateAs: "hwlab-code-agent" })
|
||||
]);
|
||||
|
||||
export function codeAgentAgentRunAdapterEnabled(env = process.env) {
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
---
|
||||
name: hwlab-code-agent
|
||||
description: HWLAB Code Agent CLI — `hwlab-cli client agent` 的全部操作,包括 session 管理、send、trace/result/inspect、steer、Web 等价路径、auth;以及 spawn/poll/result/trace 直连 cloud-api。用户提到 code agent、hwlab-cli client agent、agent send、agent trace、agent result、agent inspect、agent session、agent steer、拉 trace、查 session、spawn agent、poll agent 时使用。
|
||||
---
|
||||
|
||||
# HWLAB Code Agent CLI
|
||||
|
||||
Skill(cli-spec)
|
||||
|
||||
**Skill 根目录**: `~/.agents/skills/hwlab-code-agent/`
|
||||
**CLI 入口**: `bun scripts/hwlab-code-agent-cli.ts`
|
||||
**配置**: `config.json`(单一来源,启动校验)
|
||||
|
||||
## 直接客户端(master server 直连 cloud-api)
|
||||
|
||||
无需透传到 G14,自动识别 `HWLAB_API_KEY` 鉴权调用 HWLAB v0.2 cloud-api。
|
||||
|
||||
```bash
|
||||
source /root/.config/hwlab-v02/master-server-admin-api-key.env
|
||||
cd ~/.agents/skills/hwlab-code-agent
|
||||
|
||||
bun scripts/hwlab-code-agent-cli.ts spawn --message "say hello" [--profile deepseek]
|
||||
bun scripts/hwlab-code-agent-cli.ts spawn --message "..." --spec-path /path/to/hwpod-spec.yaml
|
||||
bun scripts/hwlab-code-agent-cli.ts poll <traceId> [--timeout 600000]
|
||||
bun scripts/hwlab-code-agent-cli.ts result <traceId>
|
||||
bun scripts/hwlab-code-agent-cli.ts trace <traceId> [--full]
|
||||
```
|
||||
|
||||
输出均为 JSON。`spawn` 创建 session + 提交 prompt,立即返回 `sessionId` + `traceId`。`poll` 阻塞轮询至 terminal 或超时。
|
||||
|
||||
### hwpod spec 自动继承
|
||||
|
||||
子 agent 需要 `.hwlab/hwpod-spec.yaml` 才能使用 hwpod。spec 按优先级自动注入:
|
||||
|
||||
1. `--spec-path <file>` — 显式指定本地 spec 文件
|
||||
2. CWD 下的 `.hwlab/hwpod-spec.yaml` — caserun 注入后自动检测
|
||||
3. 环境变量 `HWPOD_SPEC_CONTENT` — yaml 或 base64
|
||||
|
||||
Caserun -> Leader -> Coder/Reviewer 继承链:
|
||||
|
||||
```
|
||||
caserun 注入 .hwlab/hwpod-spec.yaml 到 Leader workspace
|
||||
-> Leader CWD 自动有该文件
|
||||
-> Leader 调用 spawn 时 auto-detect -> 注入子 agent workspaceFiles
|
||||
-> Coder/Reviewer 拿到相同 spec
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 外部管理 CLI(G14 workspace 透传)
|
||||
|
||||
通过 G14 `/root/hwlab-v02` workspace 的 `hwlab-cli client agent` 管理 Code Agent 的 session、提交、trace 和纠偏。
|
||||
|
||||
**固定入口**: `bun tools/hwlab-cli/bin/hwlab-cli.ts client agent ...`
|
||||
**锁定 env**:
|
||||
```bash
|
||||
export HWLAB_RUNTIME_WEB_URL=http://74.48.78.17:19666
|
||||
export HWLAB_RUNTIME_ENDPOINT_LOCKED=1
|
||||
```
|
||||
**API key**: `HWLAB_API_KEY` 从 `/root/.config/hwlab-v02/master-server-admin-api-key.env` 加载。
|
||||
|
||||
---
|
||||
|
||||
## Auth
|
||||
|
||||
```bash
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client auth status
|
||||
```
|
||||
|
||||
CLI 默认从 `HWLAB_API_KEY` 读取 key,发送 `Authorization: Bearer hwl_live_...`。不支持 `--api-key` / `--bearer-token` 别名。缺少 key 时返回 `api_key_required`。
|
||||
|
||||
---
|
||||
|
||||
## Session 管理
|
||||
|
||||
Code Agent session 是显式资源,必须先创建再发送 turn。
|
||||
|
||||
```bash
|
||||
# 创建 session
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent session create \
|
||||
[--provider-profile deepseek|codex-api|minimax-m3]
|
||||
|
||||
# 查看状态
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent session status <sessionId>
|
||||
|
||||
# 选择当前 workspace session
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent session select <sessionId>
|
||||
```
|
||||
|
||||
输出直接暴露 `sessionId`、`conversationId`、`threadId`、`providerProfile`、`sessionStatus`。
|
||||
|
||||
---
|
||||
|
||||
## 发送 Turn
|
||||
|
||||
```bash
|
||||
# 短消息
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent send \
|
||||
--session-id <sessionId> --message "Hello"
|
||||
|
||||
# 长 prompt(文件)
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent send \
|
||||
--session-id <sessionId> --message-file /tmp/prompt.md
|
||||
|
||||
# 从 trace 恢复上下文续跑
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent send \
|
||||
--from-trace <traceId>
|
||||
|
||||
# 显式 provider(优先于 session 默认)
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent send \
|
||||
--session-id <sessionId> --message "..." --provider-profile deepseek
|
||||
```
|
||||
|
||||
必须携带显式 `--session-id` 或已 select 的 workspace session。未提供时返回 `session_required`。不传 `--provider-profile` 时继承 session 的 provider profile。
|
||||
|
||||
---
|
||||
|
||||
## Trace 查询
|
||||
|
||||
```bash
|
||||
# 渲染 trace(与 Cloud Web 共享同一 renderer)
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent trace <traceId> --render web
|
||||
|
||||
# 完整 trace
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent trace <traceId> --render web --full
|
||||
```
|
||||
|
||||
trace rows 包含 `role`(user/assistant/tool)、`status`、`text`。
|
||||
|
||||
---
|
||||
|
||||
## Result 查询
|
||||
|
||||
```bash
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent result <traceId>
|
||||
```
|
||||
|
||||
返回 Agent 的 `assistantText`(最终回复)、`agentRun` 信息(runId/commandId/runnerId/namespace)、`traceSummary`。
|
||||
|
||||
---
|
||||
|
||||
## Inspect
|
||||
|
||||
```bash
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent inspect --trace-id <traceId>
|
||||
```
|
||||
|
||||
从 Cloud Web `/v1/agent/chat/inspect` 读取 trace 所属的 `conversationId/sessionId/threadId`、session 状态和 `retryOf` 建议。session 缺失或 failed 时返回结构化 blocker。
|
||||
|
||||
---
|
||||
|
||||
## Steer(纠偏)
|
||||
|
||||
```bash
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent steer <traceId> --message "..."
|
||||
```
|
||||
|
||||
向运行中的 Code Agent turn 注入纠偏指令。
|
||||
|
||||
---
|
||||
|
||||
## 典型流程
|
||||
|
||||
### CaseRun 失败调试
|
||||
|
||||
```bash
|
||||
# 1. status 拿 sessionId/traceId
|
||||
trans G14:/root/hwlab-v02 script -- \
|
||||
'hwlab-cli case run status <RUN_ID> --state-dir .state/hwlab-cli/caserun'
|
||||
|
||||
# 2. 拉 trace 分析 Agent 行为
|
||||
trans G14:/root/hwlab-v02 script -- \
|
||||
'export HWLAB_RUNTIME_WEB_URL=http://74.48.78.17:19666 HWLAB_RUNTIME_ENDPOINT_LOCKED=1 && \
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent trace <traceId> --render web'
|
||||
|
||||
# 3. 看 Agent 最终回复
|
||||
trans G14:/root/hwlab-v02 script -- \
|
||||
'export HWLAB_RUNTIME_WEB_URL=http://74.48.78.17:19666 HWLAB_RUNTIME_ENDPOINT_LOCKED=1 && \
|
||||
bun tools/hwlab-cli/bin/hwlab-cli.ts client agent result <traceId>'
|
||||
```
|
||||
|
||||
### 完整交互链路
|
||||
|
||||
```bash
|
||||
# 1. 登录
|
||||
source /root/.config/hwlab-v02/master-server-admin-api-key.env
|
||||
|
||||
# 2. 显式创建 session
|
||||
hwlab-cli client agent session create --provider-profile deepseek
|
||||
|
||||
# 3. 发送 turn(继承 session provider)
|
||||
hwlab-cli client agent send --session-id <sessionId> --message "在吗?"
|
||||
|
||||
# 4. 等待完成,查 result
|
||||
hwlab-cli client agent result <traceId>
|
||||
|
||||
# 5. 拉 trace 看详细过程
|
||||
hwlab-cli client agent trace <traceId> --render web
|
||||
```
|
||||
|
||||
### Web 等价验证
|
||||
|
||||
Web 暴露 Code Agent 问题后,先用 CLI 对同一 Cloud Web origin 和同一 `/v1/agent/chat*` 路径复现:
|
||||
|
||||
```bash
|
||||
# inspect 取 session 上下文
|
||||
hwlab-cli client agent inspect --trace-id <traceId>
|
||||
|
||||
# 从 trace 恢复上下文续跑
|
||||
hwlab-cli client agent send --from-trace <traceId>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 环境变量
|
||||
|
||||
| 变量 | 值 | 说明 |
|
||||
|---|---|---|
|
||||
| `HWLAB_API_KEY` | `hwl_live_...` | API key,从 key env 文件加载 |
|
||||
| `HWLAB_RUNTIME_WEB_URL` | `http://74.48.78.17:19666` | v0.2 Cloud Web origin |
|
||||
| `HWLAB_RUNTIME_API_URL` | `http://74.48.78.17:19667` | v0.2 Cloud API origin |
|
||||
| `HWLAB_RUNTIME_ENDPOINT_LOCKED` | `1` | 锁定 endpoint,不自动探测 |
|
||||
|
||||
## 相关文档
|
||||
|
||||
- **hwlab-cli 规格**: `docs/reference/spec-v02-hwlab-cli.md`(auth/session/Web 等价架构)
|
||||
- **trace 规格**: `docs/reference/spec-v02-code-agent-trace.md`(共享 renderer 语义)
|
||||
- **dispatch 规格**: `docs/reference/agentrun-code-agent-dispatch.md`(AgentRun 调度)
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"apiBaseUrl": "http://74.48.78.17:19666",
|
||||
"defaultProviderProfile": "deepseek",
|
||||
"defaultProjectId": "prj_hwpod_workbench",
|
||||
"defaultPollTimeoutMs": 300000
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/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";
|
||||
|
||||
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;
|
||||
}
|
||||
default:
|
||||
process.stderr.write(JSON.stringify({
|
||||
ok: false,
|
||||
usage: {
|
||||
spawn: "bun scripts/hwlab-code-agent-cli.ts spawn --message '...' [--profile deepseek]",
|
||||
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]"
|
||||
}
|
||||
}, null, 2) + "\n");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
process.stderr.write(JSON.stringify({ ok: false, error: { code: "unhandled", message: e.message } }) + "\n");
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* hwlab-code-agent 客户端核心逻辑
|
||||
* 直接调 HWLAB v0.2 cloud-api,自动识别环境变量 HWLAB_API_KEY
|
||||
* 遵循 cli-spec:配置从 config.json 单一来源
|
||||
*/
|
||||
import { readFileSync } from "node:fs";
|
||||
import { existsSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
// ---- config ----
|
||||
const SKILL_ROOT = path.resolve(import.meta.dir, "..", "..");
|
||||
const configPath = path.join(SKILL_ROOT, "config.json");
|
||||
|
||||
interface Config {
|
||||
apiBaseUrl: string;
|
||||
defaultProviderProfile: string;
|
||||
defaultProjectId: string;
|
||||
defaultPollTimeoutMs: number;
|
||||
}
|
||||
|
||||
let _cfg: Config | null = null;
|
||||
function config(): Config {
|
||||
if (!_cfg) {
|
||||
try {
|
||||
_cfg = JSON.parse(readFileSync(configPath, "utf-8")) as Config;
|
||||
} catch {
|
||||
throw new Error(`hwlab-code-agent: failed to load config from ${configPath}`);
|
||||
}
|
||||
if (!_cfg.apiBaseUrl) throw new Error("hwlab-code-agent: config.apiBaseUrl is required");
|
||||
if (!_cfg.defaultProviderProfile) throw new Error("hwlab-code-agent: config.defaultProviderProfile is required");
|
||||
}
|
||||
return _cfg;
|
||||
}
|
||||
|
||||
// ---- workspace files auto-inheritance ----
|
||||
type WorkspaceFile = { path: string; content: string; encoding: "utf8" };
|
||||
|
||||
function collectWorkspaceFiles(args: Record<string, string | undefined>): WorkspaceFile[] {
|
||||
const files: WorkspaceFile[] = [];
|
||||
const specPath = args.specPath;
|
||||
|
||||
// 1. explicit --spec-path
|
||||
if (specPath) {
|
||||
if (!existsSync(specPath)) throw new Error(`spec file not found: ${specPath}`);
|
||||
files.push({ path: ".hwlab/hwpod-spec.yaml", content: readFileSync(specPath, "utf-8"), encoding: "utf8" });
|
||||
}
|
||||
|
||||
// 2. auto-detect: .hwlab/hwpod-spec.yaml in CWD (casrun-injected)
|
||||
const cwdSpec = ".hwlab/hwpod-spec.yaml";
|
||||
if (!specPath && existsSync(cwdSpec)) {
|
||||
files.push({ path: cwdSpec, content: readFileSync(cwdSpec, "utf-8"), encoding: "utf8" });
|
||||
}
|
||||
|
||||
// 3. env var HWPOD_SPEC_CONTENT (plain yaml or base64)
|
||||
const envSpec = process.env.HWPOD_SPEC_CONTENT || process.env.HWPOD_SPEC;
|
||||
if (!specPath && !existsSync(cwdSpec) && envSpec) {
|
||||
const content = envSpec.startsWith("eyJ") || envSpec.startsWith("YXBp")
|
||||
? Buffer.from(envSpec, "base64").toString("utf-8")
|
||||
: envSpec;
|
||||
files.push({ path: ".hwlab/hwpod-spec.yaml", content, encoding: "utf8" });
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
function apiKey(): string {
|
||||
const key = process.env.HWLAB_API_KEY;
|
||||
if (!key) throw new Error("HWLAB_API_KEY not set in environment");
|
||||
return key;
|
||||
}
|
||||
|
||||
function authHeaders(): Record<string, string> {
|
||||
return { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey()}` };
|
||||
}
|
||||
|
||||
// ---- JSON out ----
|
||||
function out(data: Record<string, unknown>, exitCode = 0): void {
|
||||
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
||||
if (exitCode !== 0) process.exit(exitCode);
|
||||
}
|
||||
|
||||
function fail(code: string, msg: string, exitCode = 1): never {
|
||||
process.stderr.write(JSON.stringify({ ok: false, error: { code, message: msg } }) + "\n");
|
||||
process.exit(exitCode);
|
||||
}
|
||||
|
||||
// ---- API ----
|
||||
export interface SpawnResult {
|
||||
ok: boolean;
|
||||
action: string;
|
||||
sessionId: string;
|
||||
traceId: string;
|
||||
conversationId: string;
|
||||
accepted: boolean;
|
||||
acceptedBody: unknown;
|
||||
}
|
||||
|
||||
export async function spawn(args: Record<string, string | undefined>): Promise<void> {
|
||||
const cfg = config();
|
||||
const profile = args.profile || cfg.defaultProviderProfile;
|
||||
const projectId = args.projectId || cfg.defaultProjectId;
|
||||
const message = args.message || (args.messageFile ? readFileSync(args.messageFile, "utf-8") : null);
|
||||
if (!message) fail("message_required", "--message or --message-file required");
|
||||
|
||||
// session
|
||||
let sessionId = args.sessionId || "";
|
||||
let conversationId = args.conversationId || "";
|
||||
if (!sessionId) {
|
||||
const r = await fetch(`${cfg.apiBaseUrl}/v1/agent/sessions`, {
|
||||
method: "POST", headers: authHeaders(),
|
||||
body: JSON.stringify({ providerProfile: profile, projectId })
|
||||
});
|
||||
const b = await r.json() as Record<string, unknown>;
|
||||
if (!r.ok) fail("session_create_failed", JSON.stringify(b));
|
||||
const s = (b.session || b) as Record<string, unknown>;
|
||||
sessionId = s.sessionId as string;
|
||||
conversationId = conversationId || (s.conversationId as string) || "";
|
||||
}
|
||||
if (!conversationId) conversationId = `cnv_cli_${Date.now()}`;
|
||||
|
||||
// submit
|
||||
const workspaceFiles = collectWorkspaceFiles(args);
|
||||
const traceId = `trc_${profile}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
||||
const r = await fetch(`${cfg.apiBaseUrl}/v1/agent/chat`, {
|
||||
method: "POST",
|
||||
headers: { ...authHeaders(), "prefer": "respond-async", "x-trace-id": traceId, "x-hwlab-short-connection": "1" },
|
||||
body: JSON.stringify({ message, providerProfile: profile, sessionId, conversationId, projectId, traceId, shortConnection: true, ...(workspaceFiles.length > 0 ? { workspaceFiles } : {}) })
|
||||
});
|
||||
const b = await r.json();
|
||||
out({
|
||||
ok: true, action: "spawn",
|
||||
sessionId, traceId, conversationId,
|
||||
accepted: r.ok,
|
||||
acceptedBody: b,
|
||||
pollCommand: `bun scripts/hwlab-code-agent-cli.ts poll ${traceId}`,
|
||||
resultCommand: `bun scripts/hwlab-code-agent-cli.ts result ${traceId}`
|
||||
});
|
||||
}
|
||||
|
||||
export async function poll(traceId: string, args: Record<string, string | undefined>): Promise<void> {
|
||||
const cfg = config();
|
||||
const timeout = parseInt(args.timeout || String(cfg.defaultPollTimeoutMs), 10);
|
||||
const deadline = Date.now() + timeout;
|
||||
|
||||
while (Date.now() < deadline) {
|
||||
const r = await fetch(`${cfg.apiBaseUrl}/v1/agent/chat/result/${encodeURIComponent(traceId)}`, {
|
||||
headers: authHeaders()
|
||||
});
|
||||
const b = await r.json() as Record<string, unknown>;
|
||||
const s = (b.status || b.traceStatus) as string;
|
||||
if (s === "completed" || s === "result") {
|
||||
out({ ok: true, action: "poll", traceId, status: "completed", body: b });
|
||||
return;
|
||||
}
|
||||
if (s === "failed" || s === "error") {
|
||||
fail("poll_failed", JSON.stringify(b));
|
||||
}
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
}
|
||||
fail("poll_timeout", `timeout after ${timeout}ms`);
|
||||
}
|
||||
|
||||
export async function getResult(traceId: string): Promise<void> {
|
||||
const cfg = config();
|
||||
const r = await fetch(`${cfg.apiBaseUrl}/v1/agent/chat/result/${encodeURIComponent(traceId)}`, {
|
||||
headers: authHeaders()
|
||||
});
|
||||
out({ ok: r.ok, action: "result", traceId, body: await r.json() });
|
||||
}
|
||||
|
||||
export async function getTrace(traceId: string, args: Record<string, string | undefined>): Promise<void> {
|
||||
const cfg = config();
|
||||
const q = args.full ? "?full=true" : "";
|
||||
const r = await fetch(`${cfg.apiBaseUrl}/v1/agent/chat/trace/${encodeURIComponent(traceId)}${q}`, {
|
||||
headers: authHeaders()
|
||||
});
|
||||
out({ ok: r.ok, action: "trace", traceId, body: await r.json() });
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* hwlab-code-agent — Leader Agent 调度子 agent 的入口
|
||||
* 实际逻辑在 skills/hwlab-code-agent/scripts/hwlab-code-agent-cli.ts
|
||||
*/
|
||||
import "../skills/hwlab-code-agent/scripts/hwlab-code-agent-cli.ts";
|
||||
Reference in New Issue
Block a user