Files
Arcrun/registry/components/code/index.ts
Leo 08a79229a5 fix(code): Workers 實部署修正 —— wasmfile+Module 載入 + tick-budget timeout(CF 實測)
leo21c 實部署發現兩個 CF 限制並修正:
1. CF 禁 runtime 從 bytes 編譯 wasm(WebAssembly.instantiate(bytes) 被 embedder 擋)
   → singlefile(base64) 內嵌不可用。改 wasmfile variant + `import wasm from './vendor/quickjs.wasm'`
   (wrangler CompiledWasm rule 綁成預編 WebAssembly.Module),newVariant({wasmModule}) 注入。
   sandbox 改 variant 注入制(setVariant):Worker 注 wrangler Module、Node 由 bytes 建 Module,
   同一 production 路徑受測。vendor/quickjs.wasm 由 postinstall 自 node_modules 複製(gitignored)。
2. CF 凍結同步執行期 Date.now → wall-clock deadline 對純同步迴圈失效(撞 CF CPU 回 1102)。
   改指令計數 interrupt(max_ticks,CF-safe),wall-clock 留 Node 保護。校準:cadence≈5000
   指令/tick、真實 card 解析≈4 ticks、CF 門檻≈1000+ ticks → 預設 max_ticks=500。
   有 body 的迴圈(含 100M 迴圈)皆乾淨回 TimeoutError;空體 while(true){} 仍由 CF CPU guard 容納。

Worker live: arcrun-code.leo21c.workers.dev(cypher-executor 以此 workers.dev 慣例位址呼叫)。
Node 單測 12/12 綠(wasmfile variant + 注入 Module,同 production 路徑)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJiLCRUU2o3aSpPEzVCt2o
2026-07-06 05:15:50 +00:00

62 lines
2.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* arcrun `code` 零件 —— Worker host(可部署)
*
* POST / → { code, input?, limits? }
* → QuickJS-wasm 沙箱(./sandbox.mjs 的 runCode
* → { success:true, data } | { success:false, error, error_type }
*
* 封裝=Aquickjs-emscripten wasmfile variant)。關鍵:CF Workers 禁止 runtime 從 bytes
* 編譯 wasmWebAssembly.instantiate(bytes) 被 embedder 擋),故不能用 singlefile(base64) 內嵌。
* 改為 `import wasm from '.../wasm'` 讓 wrangler 在 build 時把 .wasm 綁成一個「已編好的
* WebAssembly.Module」,再以 newVariant({ wasmModule }) 注入沙箱 → 執行期只 instantiate 既有
* Module、不編譯,合 Workers 規則。無需 nodejs_compatsandbox 用 TextEncoder 計 bytes)。
*
* 與其他 logic 零件不同:`code` 是自足 Worker(自帶 index.ts + sandbox.mjs + quickjs variant),
* 不走 component-worker-template 的 TinyGo-wasm bundling 流程。部署見 DEPLOY.md。
*/
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { newVariant } from 'quickjs-emscripten-core';
import baseVariant from '@jitl/quickjs-wasmfile-release-sync';
// wrangler 把相對路徑 .wasm import 綁成 WebAssembly.Modulebuild 時編好,執行期不重編)。
// wasm 從 quickjs-wasmfile-release-sync vendored 進 vendor/(見 DEPLOY.md「vendor 步驟」)。
import wasmModule from './vendor/quickjs.wasm';
// @ts-expect-error —— sandbox.mjs 為 runtime-agnostic JS 核心(Node 測試與 Worker 共用同一份)
import { runCode, setVariant } from './sandbox.mjs';
// 注入預編 Module(模組載入時一次)。之後 runCode 只 instantiate、不編譯。
setVariant(newVariant(baseVariant, { wasmModule: wasmModule as WebAssembly.Module }));
const app = new Hono();
app.use('*', cors());
app.get('/', (c) => c.json({ ok: true, component: 'code' }));
app.post('/', async (c) => {
let body: { code?: unknown; input?: unknown; limits?: Record<string, number> };
try {
body = await c.req.json();
} catch {
return c.json({ success: false, error: 'request body must be JSON', error_type: 'ContractError' }, 400);
}
if (typeof body.code !== 'string') {
return c.json({ success: false, error: 'code (string) is required', error_type: 'ContractError' }, 400);
}
try {
const result = await runCode(body.code, body.input, { limits: body.limits });
// sandbox 永遠回結構化 envelopesuccess=false 仍以 200 帶 error_type 回(零件語義層錯,非 HTTP 錯)
return c.json(result);
} catch (e) {
// 理論上 runCode 自己 try/catch;這層是最後保險,Worker 絕不掛。
return c.json(
{ success: false, error: e instanceof Error ? e.message : String(e), error_type: 'SandboxError' },
500,
);
}
});
export default app;