forked from EduCraft/curriculum-project-hub
3bca137b48
hub/ TS 新部件,平级于 spec/。本仓从零 TS 重写(非迁移旧服务),栈: Fastify+Prisma 待补;本提交落 provider-agnostic agent runtime 骨架: - provider.ts: AgentProvider seam,OpenAI-compatible messages+tools+tool_calls 形状(语言通解,非 OpenAI 承诺);@Claude 仅为触发品牌(ADR-0017) - openrouter-provider.ts: openai SDK 指向 OpenRouter 的具体适配器,仅做 Message↔SDK 翻译;agent loop 不 import SDK,换 provider 不改 loop - runner.ts: 自有 agent loop(dispatch tools/循环到 stop/budget),per-run model(ADR-0017 role-based routing,run 带其 model) - tools.ts: 窄工具面 + ADR-0003 McpReadRequest.Authorized 落代码: feishu_read_context 拒绝非本项目绑定 chat 的读取(实测错 chat 抛 UnauthorizedChatRead,对 chat 放行) - models.ts: ModelRegistry seam(role→default 映射策略 OPEN) - server.ts: 接线入口(stub Feishu read,无活凭证即可编译) tsc --noEmit rc=0;ADR-0003 不变式 smoke 通过。
61 lines
1.9 KiB
TypeScript
61 lines
1.9 KiB
TypeScript
/**
|
|
* Hub entry — skeleton.
|
|
*
|
|
* Wires the provider-agnostic agent seam end-to-end with a no-op Feishu read, so
|
|
* the invariant path is exercised without live credentials. Real wiring (Fastify
|
|
* server, Feishu OAuth/event callbacks, Prisma, admin-web) lands incrementally;
|
|
* this file proves the seam compiles and the pieces connect.
|
|
*/
|
|
import { OpenRouterProvider } from "./agent/openrouter-provider.js";
|
|
import { InMemoryModelRegistry } from "./agent/models.js";
|
|
import { ToolRegistry, feishuContextTool } from "./agent/tools.js";
|
|
import { runAgent } from "./agent/runner.js";
|
|
|
|
function main(): void {
|
|
const apiKey = process.env["OPENROUTER_API_KEY"];
|
|
if (apiKey === undefined || apiKey === "") {
|
|
console.error("[hub] OPENROUTER_API_KEY not set; nothing to run. Exiting.");
|
|
process.exit(1);
|
|
}
|
|
|
|
const provider = new OpenRouterProvider({ apiKey });
|
|
const models = new InMemoryModelRegistry(
|
|
[
|
|
{ id: "z-ai/glm-4.6", label: "GLM-4.6", toolCapable: true },
|
|
{ id: "anthropic/claude-3.5-sonnet", label: "Claude 3.5 Sonnet", toolCapable: true },
|
|
],
|
|
{ draft: "z-ai/glm-4.6", review: "anthropic/claude-3.5-sonnet" },
|
|
);
|
|
|
|
const tools = new ToolRegistry();
|
|
tools.register(
|
|
feishuContextTool(async (args) => {
|
|
// Skeleton: no live Feishu calls. Real impl wraps @larksuiteoapi/node-sdk.
|
|
return JSON.stringify({ stub: true, anchor: args.anchor, id: args.id });
|
|
}),
|
|
);
|
|
|
|
const model = models.resolve(undefined, "draft");
|
|
|
|
runAgent(provider, tools, {
|
|
prompt: "ping",
|
|
model,
|
|
role: "draft",
|
|
project: {
|
|
projectId: "proj-skeleton",
|
|
boundChatId: "chat-skeleton",
|
|
workspaceDir: "/tmp/cph-skeleton",
|
|
},
|
|
})
|
|
.then((r) => {
|
|
console.log("[hub] run finished", { status: r.status, model, usage: r.usage });
|
|
if (r.error !== undefined) console.error("[hub] error:", r.error);
|
|
})
|
|
.catch((e) => {
|
|
console.error("[hub] run threw:", e);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
main();
|