feat(hub): workspace 文件工具 + cph 子进程工具(hub↔courseware 耦合点)

agent 的窄工具面补全:
- workspace.ts: read_file/write_file/list_files,路径全 confinement 到
  ctx.workspaceDir(ADR-0007 目录树);.. 与绝对路径逃逸返 error JSON 不中断 run
- cph.ts: cph_check/cph_build 子进程,cwd=workspace;ADR-0016 版本契约由 cph
  自身在 load 阶段校验(cphVersionMismatch),Hub 透传诊断不重复校验;
  CPH_BIN 可配,缺二进制返 exitCode 127 不抛
- server.ts: 注册全部 5 个工具
修 dynamic import(rule: ts-no-dynamic-import)——mkdir 改顶部静态 import;
三个 handler 的 confine 包进 try(操作性错误返 JSON 而非冒泡中断 run)。
tsc rc=0;smoke 通过(逃逸拒绝/读写回环/列举/cph ENOENT)。
This commit is contained in:
2026-07-06 23:19:28 +08:00
parent 313e890b6c
commit a4d52e1850
3 changed files with 273 additions and 0 deletions
+9
View File
@@ -16,6 +16,8 @@ import { prisma } from "./db.js";
import { OpenRouterProvider } from "./agent/openrouter-provider.js";
import { InMemoryModelRegistry } from "./agent/models.js";
import { ToolRegistry, feishuContextTool } from "./agent/tools.js";
import { readFileTool, writeFileTool, listFilesTool } from "./agent/workspace.js";
import { cphCheckTool, cphBuildTool } from "./agent/cph.js";
import { startFeishuListener } from "./feishu/client.js";
import { makeTriggerHandler } from "./feishu/trigger.js";
@@ -62,6 +64,13 @@ async function main(): Promise<void> {
return JSON.stringify({ stub: true, anchor: args.anchor, id: args.id, projectId: ctx.projectId });
}),
);
// Workspace file tools (ADR-0007 directory tree, path-confined).
tools.register(readFileTool());
tools.register(writeFileTool());
tools.register(listFilesTool());
// cph CLI tools (ADR-0016 version contract enforced by cph itself).
tools.register(cphCheckTool());
tools.register(cphBuildTool());
// --- Feishu listener ---
const trigger = makeTriggerHandler({ prisma, provider, tools, models, logger: app.log });