497f92a268
Three new platform capabilities + one component (kbdb_get) to enable
real AI workflow execution through cypher binding YAML.
## Recipe System (容器 + Recipe 模式)
SDD: .agents/specs/recipe-system/
- prompt_recipe schema (Zod): fragments + inputs + assembly + output
- recipe-expander.ts: expand recipe ref → real prompt by fetching KBDB blocks
+ pulling context fields with transforms (pluck_content / extract_field / etc)
- 7 transform whitelist: json_array / to_string / join / markdown_list /
extract_field / first / pluck_content
- graph-executor hooks: detect node.data.recipe → expand → inject into ctx
- output JSON parser (with markdown fence stripping for Claude-wrapped JSON)
- Stored in RECIPES KV under prompt_recipe:{name}
## Resumable Workflow (webhook callback resume)
SDD: .agents/specs/resumable-workflow/
- WorkflowPaused class + paused-runs.ts (persist/load/consume in EXEC_CONTEXT KV, 24h TTL)
- graph-executor: detect {pending:true, task_id} → persist state → throw WorkflowPaused
- cypher-handlers: catch → return {success:true, paused:true, task_id, run_id}
- POST /workflows/resume route: consume KV state → resumeFromPaused()
- Auto-inject callback_url for claude_api nodes (PUBLIC_BASE_URL or default cypher.arcrun.dev)
- claude_api/main.go: forward callback_url to Mira daemon, default timeout 25s→120s
- Idempotent (consume = load+delete)
## Component Registry Canon
SDD: .agents/specs/component-registry-canon/
- Add POST /components/index-only endpoint (metadata-only, no wasm/sandbox)
- Backfill script (mjs): scan registry/components/*/contract.yaml → submit to KV
- register-component.sh: SSOT for local + CI hook (deploy.yml change in next commit)
- Drop R2 dead storage from submitComponent + types + wrangler
- Schema relaxed: category enum + auth/ai/platform; cold_start 50→500ms; size 2→8MB
## kbdb_get component
- registry/components/kbdb_get/: TinyGo WASM, two modes (block_id / page_name list)
- .component-builds/kbdb_get/: WASI shim worker (kbdb-get.arcrun.dev)
End-to-end validation: AI uses MCP execute_workflow with recipe ref →
cypher-executor expands prompt from KBDB schema/skill blocks + drafts →
claude_api calls Mira daemon → daemon callback fires resume route →
workflow continues. Verified with real 2KB+ Karpathy LLM Wiki draft.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
115 lines
4.8 KiB
TypeScript
115 lines
4.8 KiB
TypeScript
// Component Registry Worker 型別定義
|
||
|
||
import { z } from 'zod';
|
||
|
||
// ── Cloudflare Bindings ──────────────────────────────────────────────────────
|
||
|
||
export type Bindings = {
|
||
AI: Ai;
|
||
// KV key 格式:
|
||
// comp:{hash_id}:{version} → 零件元數據(hash_id = cmp_ + sha256 前 8 碼)
|
||
// idx:{canonical_id} → canonical_id → hash_id 反查索引
|
||
SUBMISSIONS_KV: KVNamespace;
|
||
ANALYTICS_KV: KVNamespace; // 執行統計匯總(key = stats:{hash_id}:{version})
|
||
ENVIRONMENT: string;
|
||
};
|
||
|
||
// ── Component Contract Schema(Zod)─────────────────────────────────────────
|
||
|
||
// max_cold_start_ms 上限放寬至 500(從 50):實測 auth/ai 類零件含 crypto/init 步驟通常 100-300ms
|
||
// no_network_syscall / no_filesystem_syscall 都改 optional:auth/api 類零件需要網路 syscall
|
||
export const ConstraintsSchema = z.object({
|
||
max_size_kb: z.number().positive().max(8192),
|
||
max_cold_start_ms: z.number().positive().max(500),
|
||
no_network_syscall: z.boolean().optional(),
|
||
no_filesystem_syscall: z.boolean().optional(),
|
||
io_model: z.literal('stdin_stdout_json'),
|
||
});
|
||
|
||
export const GherkinTestSchema = z.object({
|
||
scenario: z.string().min(1),
|
||
given: z.string().min(1),
|
||
then_contains: z.string().min(1),
|
||
});
|
||
|
||
export const ComponentContractSchema = z.object({
|
||
// canonical_id:提交者填寫的可讀名稱(小寫底線),用於搜尋與 workflow 引用
|
||
// component_hash_id:由 Registry 在提交時派發,格式 cmp_{8碼hex},workflow 引用此 id 才能保證永久不壞
|
||
// 兩者都可以在 workflow 中引用,Registry 會互相解析
|
||
canonical_id: z.string().min(1).regex(/^[a-z][a-z0-9_]*$/, 'canonical_id 必須為小寫底線格式'),
|
||
display_name: z.string().min(1),
|
||
// category 擴充:auth (auth primitive)、ai (Claude/AI 推論)、platform (平台底層 crypto/system)
|
||
category: z.enum(['logic', 'api', 'ui', 'style', 'anim', 'data', 'auth', 'ai', 'platform']),
|
||
version: z.string().min(1).regex(/^v\d+$/, 'version 格式必須為 vN'),
|
||
wasi_target: z.literal('preview1'),
|
||
stability: z.enum(['floating', 'stable', 'pinned']),
|
||
runtime_compat: z.array(z.enum(['cf-workers', 'workerd', 'wazero'])).min(1),
|
||
constraints: ConstraintsSchema,
|
||
input_schema: z.record(z.unknown()),
|
||
output_schema: z.record(z.unknown()),
|
||
gherkin_tests: z.array(GherkinTestSchema).min(2, '至少需要一個 happy path 和一個 error path'),
|
||
// 選填欄位
|
||
component_type: z.enum(['wasm', 'service_binding']).optional(),
|
||
max_size_kb: z.number().optional(),
|
||
max_cold_start_ms: z.number().optional(),
|
||
no_network_syscall: z.boolean().optional(),
|
||
service_binding_key: z.string().optional(),
|
||
description: z.string().optional(),
|
||
// aliases:搜尋同義詞,不作為識別符使用
|
||
// 從 registry/aliases.yaml 的 scope 同義詞表自動合併,也可在 contract 內手動補充
|
||
// 未來接入 KBDB 後,canonical_id 將獲得系統派發的唯一 hash id
|
||
aliases: z.array(z.string()).optional(),
|
||
tags: z.array(z.string()).optional(),
|
||
});
|
||
|
||
export type ComponentContract = z.infer<typeof ComponentContractSchema>;
|
||
|
||
// ── 沙盒驗收步驟 ─────────────────────────────────────────────────────────────
|
||
|
||
export type SandboxStep = 'size_check' | 'cold_start' | 'syscall_scan' | 'gherkin_tests' | 'runtime_compat';
|
||
|
||
export interface SandboxResult {
|
||
success: boolean;
|
||
failed_step?: SandboxStep;
|
||
reason?: string;
|
||
guide_anchor?: string;
|
||
// 驗收通過後回傳兩個 id:
|
||
component_hash_id: string; // cmp_{8碼hex},workflow 引用用,永久不變
|
||
canonical_id: string; // 可讀名稱,搜尋用
|
||
version: string;
|
||
}
|
||
|
||
// ── KBDB Block 格式 ──────────────────────────────────────────────────────────
|
||
|
||
export interface KbdbBlock {
|
||
block_id: string;
|
||
template_id: string;
|
||
user_id?: string;
|
||
page_name?: string;
|
||
}
|
||
|
||
export interface KbdbSlots {
|
||
[key: string]: string;
|
||
}
|
||
|
||
// ── 禁止的 WASM syscall(網路 + 檔案系統)────────────────────────────────────
|
||
|
||
export const FORBIDDEN_SYSCALLS = [
|
||
'sock_connect',
|
||
'sock_accept',
|
||
'sock_recv',
|
||
'sock_send',
|
||
'sock_shutdown',
|
||
'fd_open',
|
||
'path_open',
|
||
'path_create_directory',
|
||
'path_remove_directory',
|
||
'path_rename',
|
||
'path_unlink_file',
|
||
'path_filestat_get',
|
||
'path_filestat_set_times',
|
||
'path_link',
|
||
'path_readlink',
|
||
'path_symlink',
|
||
] as const;
|