chore: add claw smoke runtime sample

This commit is contained in:
kris
2026-04-03 01:41:29 +08:00
parent 39b576cc42
commit 6c999fb951
6 changed files with 146 additions and 0 deletions

20
.env.server.example Normal file
View File

@@ -0,0 +1,20 @@
BOSS_AUTH_VERIFICATION_MODE=fixed
BOSS_AUTH_FIXED_CODE=000000
BOSS_STATE_FILE=/opt/boss/data/boss-state.json
# 切到真实邮件验证码时,改成:
# BOSS_AUTH_VERIFICATION_MODE=email
# BOSS_AUTH_FIXED_CODE=
BOSS_MAIL_DOMAIN=boss.hyzq.net
BOSS_MAIL_FROM_ADDRESS=verify@boss.hyzq.net
BOSS_MAIL_FROM_NAME=Boss Verify
BOSS_SENDMAIL_PATH=/usr/sbin/sendmail
# 可选:启用 ClawBackendAdapter默认关闭
# BOSS_CLAW_ENABLED=true
# BOSS_CLAW_COMMAND=node
# BOSS_CLAW_ARGS=scripts/claw-runtime-smoke.mjs
# BOSS_CLAW_WORKDIR=/opt/boss
# BOSS_CLAW_TIMEOUT_MS=45000
# BOSS_CLAW_DEFAULT_MODEL=gpt-5.4

View File

@@ -59,6 +59,7 @@
- 当前 Boss 已新增 `src/lib/execution/` 执行底座抽象层;当前生产主链仍然沿用 `local-agent -> codex exec resume`,只是执行责任已开始通过 `ExecutionBackend / PromptAssembler / PermissionPolicy / RemoteRuntimeAdapter / OrchestrationBackend` 默认实现收束
- 当前 `claw-code` 已以最小 `ClawBackendAdapter` 形式接入执行底座,但默认关闭;只有在显式配置 `BOSS_CLAW_*` 并在 `master-agent` 当前对话里显式选择 `claw-runtime` 时才会参与执行候选
- 当前 `oh-my-codex` 仍未正式接入生产执行链;当前状态是 orchestration-ready后续将通过独立 adapter 接入
- 当前仓库已自带一个本地 smoke runtime`scripts/claw-runtime-smoke.mjs`。在还没有真实 `claw-code` 可执行文件时,可以先用它验证 `ClawBackendAdapter -> backendOverride -> 异步回流` 整条链
- `GET http://127.0.0.1:4317/api/v1/skills` 正常,已返回本机扫描到的 Codex Skill
- `POST http://127.0.0.1:4317/api/v1/heartbeat` 正常,且会顺带触发 `thread-context` 上报
- `launchd` 已加载:`~/Library/LaunchAgents/com.hyzq.boss.local-agent.plist`

View File

@@ -178,6 +178,7 @@
- 已在生产代码中被 `boss-master-agent.ts``local-agent/server.mjs``master-agent task complete route` 使用
- 当前仍服务 Boss 自身执行链
- 当前已最小接入 `ClawBackendAdapter`,但默认关闭,仅在显式配置和显式选择时参与执行
- 当前仓库自带 `scripts/claw-runtime-smoke.mjs` 作为兼容 JSON 协议的 smoke runtime可用于本地和服务器验证 `ClawBackendAdapter`
- 当前尚未接入 `oh-my-codex`
### 3.2 认证相关

View File

@@ -29,6 +29,7 @@
- 当前执行底座抽象层已落地在 `src/lib/execution/`,并已补齐 `ExecutionBackend / PromptAssembler / PermissionPolicy / RemoteRuntimeAdapter / OrchestrationBackend` 默认实现
- 当前生产主链仍然沿用 `local-agent -> codex exec resume -> /api/v1/master-agent/tasks/[taskId]/complete`,执行底座重构以“先抽象、不改行为”为准
- 当前 `claw-code` 已以最小 `ClawBackendAdapter` 形式接入执行底座,但默认关闭;只有显式配置 `BOSS_CLAW_*` 并在 `master-agent` 当前对话中选择 `claw-runtime` 时才会参与执行候选
- 当前仓库已自带 `scripts/claw-runtime-smoke.mjs` 作为本地 smoke runtime在没有真实 `claw-code` 可执行文件时,可先用 `BOSS_CLAW_COMMAND=node``BOSS_CLAW_ARGS=scripts/claw-runtime-smoke.mjs` 验证整条链
- 当前 `oh-my-codex` 还未正式接入生产链,只是已经具备 orchestration adapter-ready 的 contract 基础
本地已知运行方式:

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env node
function writeJson(payload) {
process.stdout.write(`${JSON.stringify(payload)}\n`);
}
async function readStdin() {
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
}
return chunks.join("").trim();
}
function normalizePayload(raw) {
try {
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
return {
ok: false,
error: "INVALID_CLAW_PAYLOAD: expected object",
};
}
return {
ok: true,
payload: parsed,
};
} catch {
return {
ok: false,
error: "INVALID_CLAW_PAYLOAD: invalid json",
};
}
}
const raw = await readStdin();
const normalized = normalizePayload(raw);
if (!normalized.ok) {
writeJson({
status: "failed",
error: normalized.error,
});
process.exit(0);
}
const payload = normalized.payload;
const requestKind = typeof payload.requestKind === "string" ? payload.requestKind : "unknown";
const model = typeof payload.model === "string" && payload.model.trim() ? payload.model.trim() : "default";
const reasoningEffort =
typeof payload.reasoningEffort === "string" && payload.reasoningEffort.trim()
? payload.reasoningEffort.trim()
: "default";
const executionPrompt =
typeof payload.executionPrompt === "string" && payload.executionPrompt.trim()
? payload.executionPrompt.trim()
: "链路正常";
writeJson({
status: "completed",
output: `Claw smoke completed: ${executionPrompt} (kind=${requestKind}, model=${model}, reasoning=${reasoningEffort})`,
});

View File

@@ -0,0 +1,61 @@
import test from "node:test";
import assert from "node:assert/strict";
import path from "node:path";
import { spawn } from "node:child_process";
function runSmoke(payload: unknown) {
return new Promise<{
exitCode: number | null;
stdout: string;
stderr: string;
}>((resolve, reject) => {
const scriptPath = path.resolve("scripts/claw-runtime-smoke.mjs");
const child = spawn(process.execPath, [scriptPath], {
cwd: "/Users/kris/code/boss",
stdio: ["pipe", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.setEncoding("utf8");
child.stderr.setEncoding("utf8");
child.stdout.on("data", (chunk) => {
stdout += chunk;
});
child.stderr.on("data", (chunk) => {
stderr += chunk;
});
child.on("error", reject);
child.on("close", (exitCode) => {
resolve({ exitCode, stdout, stderr });
});
child.stdin.write(JSON.stringify(payload));
child.stdin.end();
});
}
test("claw runtime smoke script emits completed JSON for valid payload", async () => {
const result = await runSmoke({
requestKind: "master_agent_reply",
executionPrompt: "请回复链路正常",
model: "gpt-5.4",
reasoningEffort: "medium",
});
assert.equal(result.exitCode, 0);
assert.equal(result.stderr, "");
const parsed = JSON.parse(result.stdout);
assert.equal(parsed.status, "completed");
assert.match(parsed.output, /链路正常/);
assert.match(parsed.output, /gpt-5\.4/);
});
test("claw runtime smoke script emits failed JSON for invalid payload", async () => {
const result = await runSmoke("not-an-object");
assert.equal(result.exitCode, 0);
const parsed = JSON.parse(result.stdout);
assert.equal(parsed.status, "failed");
assert.match(parsed.error, /INVALID_CLAW_PAYLOAD/);
});