922a57fe34
Self-hosted 開源:WASM 零件 + recipe + cypher-executor,跑在你自己的 Cloudflare。 此為重建的乾淨歷史起點(移除曾誤 commit 的 GCP SA 金鑰,舊歷史保留在 richblack/arcrun 與本地 backup 分支)。含: - acr init --self-hosted installer(建 KV/R2 + codeload 拉預編譯 wasm + wrangler deploy + seed recipe) - recipe push 把關(資料外流提醒 + 打通檢查) - 19 個正當零件預編譯 wasm(claude_api/km_writer/kbdb_upsert_block 排除:違反 DECISIONS §1) - CLI / cypher-executor / registry / 完整 SDD Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
57 lines
2.2 KiB
TypeScript
57 lines
2.2 KiB
TypeScript
import { Hono } from 'hono';
|
||
import type { Bindings, ExecutionGraph } from '../types';
|
||
import { ExecutionError } from '../types';
|
||
import { GraphExecutor } from '../graph-executor';
|
||
import { executeSchema } from '../lib/schemas';
|
||
import { createComponentLoader } from '../lib/component-loader';
|
||
import { writeExecutionVerdict } from '../actions/execution-logger';
|
||
|
||
export const executeRouter = new Hono<{ Bindings: Bindings }>();
|
||
|
||
// POST /execute — 執行一個完整的圖
|
||
executeRouter.post('/execute', async (c) => {
|
||
const body = await c.req.json();
|
||
const parsed = executeSchema.safeParse(body);
|
||
if (!parsed.success) {
|
||
return c.json({ error: '圖定義驗證失敗', details: parsed.error.issues }, 400);
|
||
}
|
||
|
||
const { graph, context } = parsed.data;
|
||
const apiKey = c.req.header('x-arcrun-api-key') ?? undefined;
|
||
const loader = createComponentLoader(c.env);
|
||
const executor = new GraphExecutor(loader, undefined, c.env, apiKey);
|
||
const start = Date.now();
|
||
|
||
try {
|
||
// BUILD-006:傳入 KV namespace(若不存在則 fallback 到記憶體 merge)
|
||
const result = await executor.execute(graph as ExecutionGraph, context, c.env.EXEC_CONTEXT);
|
||
const duration_ms = Date.now() - start;
|
||
c.executionCtx.waitUntil(
|
||
writeExecutionVerdict(c.env, graph.id, graph.nodes, 'success', duration_ms, '執行完成')
|
||
);
|
||
return c.json({ success: true, data: result.data, trace: result.trace, duration_ms });
|
||
} catch (err) {
|
||
const duration_ms = Date.now() - start;
|
||
const errMsg = err instanceof Error ? err.message : String(err);
|
||
c.executionCtx.waitUntil(
|
||
writeExecutionVerdict(c.env, graph.id, graph.nodes, 'failed', duration_ms, errMsg.slice(0, 100))
|
||
);
|
||
if (err instanceof ExecutionError) {
|
||
const traceFormatted = err.trace.map(s => ({
|
||
node: s.nodeId,
|
||
status: s.error ? 'failed' : 'success',
|
||
...(s.error ? { error: s.error } : {}),
|
||
}));
|
||
return c.json({
|
||
success: false,
|
||
error: errMsg,
|
||
failed_node: err.failed_node,
|
||
failed_input: err.failed_input,
|
||
trace: traceFormatted,
|
||
duration_ms,
|
||
}, 500);
|
||
}
|
||
return c.json({ success: false, error: errMsg, failed_node: null, trace: [], duration_ms }, 500);
|
||
}
|
||
});
|