feat(arcrun): implement arcrun MVP — open-source AI workflow engine

Phase 1-5 complete per .agents/specs/u6u-core-mvp/:

**Phase 1 — Cherry-pick & cleanup**
- Create arcrun/ from cypher-executor, credentials, builtins, registry
- Remove 9 InkStone Service Bindings (KBDB, REGISTRY, CLINIC_*, AICEO, MINI_ME)
- Rewrite component-loader: 3-layer (builtin → WASM_BUCKET R2 → error)
- Remove autoPublishMissing.ts, proxy.ts (AICEO), execution-logger.ts (KBDB)
- Clean all KV namespace IDs and InkStone internal URLs from config files

**Phase 2 — contract.yaml completeness**
- Add credentials_required to gmail, google_sheets, telegram, line_notify
- Add config_example to all 21 components with annotated field descriptions

**Phase 3 — Credential injection**
- Add credential-injector.ts: AES-GCM decrypt from CREDENTIALS_KV
- Integrate into GraphExecutor before WASM execution
- Structured errors with repair instructions when credential missing

**Phase 4 — CLI (acr)**
- cli/package.json: arcrun package, bin: acr, deps: commander/js-yaml/chalk/ora
- 8 commands: init, creds push, push, run, validate, parts, list, logs
- Standard mode: writes directly to user's CF KV via CF REST API
- acr init: interactive setup with arcrun.dev API Key registration

**Phase 5 — Open source release prep**
- README.md: 5-minute quickstart, component table, workflow YAML syntax
- CONTRIBUTING.md: TinyGo dev env, component scaffolding, submission flow
- Security audit: no InkStone internal URLs/IDs in committed files
- .gitignore: exclude credentials.yaml, .wrangler, *.wasm

https://claude.ai/code/session_01BnCdSLVH8tUed9VrrPavgT
This commit is contained in:
Claude
2026-04-16 04:06:25 +00:00
commit 2707fca32b
155 changed files with 17413 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
// 沙盒驗收流程:五個步驟依序執行
// Requirements: 2.1, 2.2, 2.3
import { FORBIDDEN_SYSCALLS } from '../types';
import type { ComponentContract, SandboxResult, SandboxStep } from '../types';
// ── 步驟 (a):體積檢查 ────────────────────────────────────────────────────────
function checkSize(wasmBytes: Uint8Array, contract: ComponentContract): string | null {
const maxSizeKb = contract.constraints.max_size_kb;
const actualKb = wasmBytes.byteLength / 1024;
if (actualKb > maxSizeKb) {
return `體積 ${actualKb.toFixed(1)}KB 超過上限 ${maxSizeKb}KB`;
}
return null;
}
// ── 步驟 (b):冷啟動時間(Phase 0 mock 0ms)────────────────────────────────────
function checkColdStart(_wasmBytes: Uint8Array, _contract: ComponentContract): string | null {
// Phase 0mock 通過,記錄 0ms
// Phase 2 再實作真實測量
return null;
}
// ── 步驟 (c):syscall 掃描 ────────────────────────────────────────────────────
function scanSyscalls(wasmBytes: Uint8Array): string | null {
// 將 .wasm binary 轉為文字,搜尋禁止的 import 字串
// WASM binary 中 import section 的函數名稱以 UTF-8 字串形式存在
const text = new TextDecoder('utf-8', { fatal: false }).decode(wasmBytes);
for (const syscall of FORBIDDEN_SYSCALLS) {
if (text.includes(syscall)) {
return `發現禁止的 syscall${syscall}`;
}
}
return null;
}
// ── 步驟 (d)Gherkin 測試(Phase 0 mock 通過)────────────────────────────────
function runGherkinTests(_wasmBytes: Uint8Array, _contract: ComponentContract): string | null {
// Phase 0mock 通過
// Phase 1 再實作真實 Gherkin 執行
return null;
}
// ── 步驟 (e)runtime 相容測試(Phase 0 mock 通過)────────────────────────────
function checkRuntimeCompat(_wasmBytes: Uint8Array, _contract: ComponentContract): string | null {
// Phase 0mock 通過
// Phase 2 再實作真實多 runtime 測試
return null;
}
// ── 主流程 ────────────────────────────────────────────────────────────────────
interface StepDef {
name: SandboxStep;
run: (wasmBytes: Uint8Array, contract: ComponentContract) => string | null;
guideAnchor: string;
}
const STEPS: StepDef[] = [
{ name: 'size_check', run: checkSize, guideAnchor: '#common-errors' },
{ name: 'cold_start', run: checkColdStart, guideAnchor: '#common-errors' },
{ name: 'syscall_scan', run: scanSyscalls, guideAnchor: '#syscall-constraints' },
{ name: 'gherkin_tests', run: runGherkinTests, guideAnchor: '#local-testing' },
{ name: 'runtime_compat', run: checkRuntimeCompat, guideAnchor: '#contract-example' },
];
export function runSandboxAcceptance(
wasmBytes: Uint8Array,
contract: ComponentContract,
): SandboxResult {
for (const step of STEPS) {
const error = step.run(wasmBytes, contract);
if (error !== null) {
return {
success: false,
failed_step: step.name,
reason: error,
guide_anchor: step.guideAnchor,
component_id: contract.canonical_id,
version: contract.version,
};
}
}
return {
success: true,
component_id: contract.canonical_id,
version: contract.version,
};
}