From 5244cf8b90cc7ec17810bf4ed01d23f4c4537236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=A9=98?= Date: Fri, 3 Apr 2026 23:34:45 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20AMD=20deps=20not=20injected=20=E2=80=94?= =?UTF-8?q?=20router=20was=20pre-codegen'ing,=20bypassing=20requires?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: router.ts called generateWorkerCode() before passing to backend, so backend always received 'code' (mode A) and never entered the requires branch (mode B). Also stored original execute body in meta for clean dep resolution instead of fragile regex extraction. Fix: - Router passes execute directly to backend, lets backend handle codegen - Meta now stores execute field for dependency resolution - resolveDependencies uses meta.execute (falls back to regex extraction) Verified end-to-end: - formal-greet(requires greet-name) → '[Formal] Hello, Scott!' - xiaoju-github-repos(requires xiaoju-github-token) → real GitHub API response 小橘 🍊 (NEKO Team) --- AMD_DEMO.md | 217 +++++++++++++++++++++++++++++++++++++ src/backend/worker-pool.ts | 16 +-- src/kv.ts | 1 + src/router.ts | 7 +- 4 files changed, 231 insertions(+), 10 deletions(-) create mode 100644 AMD_DEMO.md diff --git a/AMD_DEMO.md b/AMD_DEMO.md new file mode 100644 index 0000000..152d651 --- /dev/null +++ b/AMD_DEMO.md @@ -0,0 +1,217 @@ +# AMD 风格 Capability 组合功能演示 + +本演示展示 Sigil 新增的 AMD 风格依赖管理功能。 + +## 1. 基础依赖注入 + +### 部署基础 capability + +```bash +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "github-token", + "execute": "return \"ghp_example_token\";", + "type": "normal", + "description": "获取 GitHub API token" + }' +``` + +### 部署依赖该 capability 的服务 + +```bash +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "github-api", + "schema": { + "type": "object", + "properties": { + "endpoint": {"type": "string"}, + "method": {"type": "string", "default": "GET"} + }, + "required": ["endpoint"] + }, + "execute": "const token = await deps[\"github-token\"](); const response = await fetch(`https://api.github.com${input.endpoint}`, { method: input.method, headers: { \"Authorization\": `Bearer ${token}` } }); return response.json();", + "type": "normal", + "description": "GitHub API 客户端", + "requires": ["github-token"] + }' +``` + +## 2. 多依赖组合 + +### 部署翻译服务 + +```bash +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "translate", + "schema": { + "type": "object", + "properties": { + "text": {"type": "string"}, + "lang": {"type": "string", "default": "zh"} + }, + "required": ["text"] + }, + "execute": "return `[${input.lang}] ${input.text}`;", + "type": "normal" + }' +``` + +### 部署使用多个依赖的服务 + +```bash +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "repo-summary", + "schema": { + "type": "object", + "properties": { + "repo": {"type": "string"} + }, + "required": ["repo"] + }, + "execute": "const repoData = await deps[\"github-api\"]({endpoint: `/repos/${input.repo}`}); const summary = await deps[\"translate\"]({text: repoData.description, lang: \"zh\"}); return {name: repoData.name, description_cn: summary, stars: repoData.stargazers_count};", + "type": "normal", + "description": "获取仓库信息并翻译描述", + "requires": ["github-api", "translate"] + }' +``` + +## 3. 链式依赖 + +### A depends on B, B depends on C + +```bash +# 部署 C (基础服务) +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "base-logger", + "execute": "return `[LOG] ${new Date().toISOString()}`;", + "type": "normal" + }' + +# 部署 B (中间层,依赖 C) +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "middleware", + "schema": { + "type": "object", + "properties": { + "action": {"type": "string"} + } + }, + "execute": "const log = await deps[\"base-logger\"](); return `${log} [MIDDLEWARE] ${input.action || \"unknown\"}`;", + "type": "normal", + "requires": ["base-logger"] + }' + +# 部署 A (顶层,依赖 B) +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "api-handler", + "schema": { + "type": "object", + "properties": { + "request": {"type": "string"} + } + }, + "execute": "const mid = await deps[\"middleware\"]({action: \"handle_request\"}); return `${mid} [API] Processing: ${input.request}`;", + "type": "normal", + "requires": ["middleware"] + }' +``` + +## 4. 调用示例 + +```bash +# 调用 repo-summary (会自动使用其所有依赖) +curl "https://sigil.example.com/run/repo-summary?repo=microsoft/vscode" + +# 调用 api-handler (会自动使用链式依赖) +curl "https://sigil.example.com/run/api-handler?request=get_user_data" +``` + +## 5. 生成的代码结构 + +当使用 `requires` 时,生成的 Worker 代码包含: + +```javascript +export default { + async fetch(request) { + // ... 输入解析 ... + + // AMD deps - 每个依赖内联为函数 + const deps = { + 'github-token': async (params = {}) => { + const input = params; + return "ghp_example_token"; + }, + 'translate': async (params = {}) => { + const input = {}; + if (params.text !== undefined) input.text = params.text; + if (params.lang !== undefined) input.lang = params.lang; + if (input.lang === undefined) input.lang = "zh"; + return `[${input.lang}] ${input.text}`; + } + }; + + // Execute user function (with deps) + const __result = await (async (input, deps) => { + const repoData = await deps["github-api"]({endpoint: `/repos/${input.repo}`}); + const summary = await deps["translate"]({text: repoData.description, lang: "zh"}); + return {name: repoData.name, description_cn: summary, stars: repoData.stargazers_count}; + })(input, deps); + + // ... 输出处理 ... + } +}; +``` + +## 6. 循环依赖检测 + +尝试创建循环依赖会被自动检测并阻止: + +```bash +# 这会失败,因为会创建 A -> A 循环 +curl -X POST https://sigil.example.com/_api/deploy \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "self-ref", + "execute": "const self = await deps[\"self-ref\"](); return self;", + "type": "normal", + "requires": ["self-ref"] + }' +``` + +错误响应: +```json +{ + "error": "Failed to resolve dependencies: Circular dependency detected: self-ref -> self-ref" +} +``` + +## 特性总结 + +✅ **依赖声明**: `requires` 字段声明依赖关系 +✅ **自动 bundle**: 依赖代码自动内联到主 capability +✅ **循环检测**: 防止无限递归的循环依赖 +✅ **参数解析**: 依赖支持 schema 参数验证 +✅ **向后兼容**: 现有 capability 不受影响 +✅ **递归解析**: 支持多层依赖链(A->B->C) +✅ **类型安全**: TypeScript 严格模式支持 \ No newline at end of file diff --git a/src/backend/worker-pool.ts b/src/backend/worker-pool.ts index 2bf0da4..0b6e68d 100644 --- a/src/backend/worker-pool.ts +++ b/src/backend/worker-pool.ts @@ -71,18 +71,17 @@ export class WorkerPool implements SigilBackend { Object.assign(deps, nestedDeps) } - // 对于 schema+execute 模式的依赖,从 code 中提取 execute body + // Use stored execute body if available, fall back to extraction from code + const executeBody = depMeta.execute || this.extractExecuteBodyFromWorkerCode(depCode) + if (depMeta.schema) { - // 从生成的 worker code 中提取用户的 execute body - // 这个比较 hacky,实际应该存储原始的 execute body - // 但为了兼容现有代码,我们从生成的代码中反向提取 deps[depName] = { - code: this.extractExecuteBodyFromWorkerCode(depCode), + code: executeBody, schema: depMeta.schema } - } else { - // 完整代码模式,直接使用 - deps[depName] = { code: depCode } + } else { + // Code mode or no schema — use stored execute or full code + deps[depName] = { code: depMeta.execute || depCode } } } @@ -169,6 +168,7 @@ export class WorkerPool implements SigilBackend { await this.kv.setCode(capability, finalCode) await this.kv.setMeta(capability, { type, ttl, created_at: now, bindings, description, tags, examples, schema, requires, + execute: execute || undefined, }) await this.kv.setLru(capability, { last_access: now, access_count: 0, deployed: true }) await this.kv.setRoute(capability, { worker_name: capability, subdomain: '' }) diff --git a/src/kv.ts b/src/kv.ts index 7bab160..86a9262 100644 --- a/src/kv.ts +++ b/src/kv.ts @@ -16,6 +16,7 @@ export interface KvMetaValue { examples?: string[] schema?: InputSchema requires?: string[] + execute?: string // original execute body, stored for AMD dependency resolution } export interface KvLruValue { diff --git a/src/router.ts b/src/router.ts index a27345d..5e426e2 100644 --- a/src/router.ts +++ b/src/router.ts @@ -85,8 +85,9 @@ async function handleDeploy(request: Request, env: RouterEnv): Promise return jsonError(400, 'Must specify either code or schema+execute') } - let code: string + let code: string | undefined let schema: InputSchema | undefined + let execute: string | undefined if (body.code) { code = body.code @@ -95,7 +96,8 @@ async function handleDeploy(request: Request, env: RouterEnv): Promise return jsonError(400, 'execute is required when using schema mode') } schema = body.schema || { type: 'object', properties: {} } - code = generateWorkerCode(schema, body.execute) + execute = body.execute + // Don't codegen here — let backend handle it (supports AMD requires) } // Check deploy cooldown @@ -104,6 +106,7 @@ async function handleDeploy(request: Request, env: RouterEnv): Promise const result = await env.backend.deploy({ name: body.name, code, + execute, schema, type: body.type, ttl: body.ttl,