arcrun — AI workflow execution engine (clean history)
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>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import { Hono } from 'hono';
|
||||
import type { Bindings } from '../types';
|
||||
import { handleCypherSearch, handleCypherExecute } from '../actions/cypher-handlers';
|
||||
|
||||
export const cypherRouter = new Hono<{ Bindings: Bindings }>();
|
||||
|
||||
// POST /cypher/search — 三元組 → 解析節點 → 語意搜尋零件 → 回傳 Cypher JSON (開發友善格式)
|
||||
cypherRouter.post('/cypher/search', async (c) => {
|
||||
const body = await c.req.json() as { triplets?: unknown };
|
||||
const rawTriplets = body?.triplets;
|
||||
|
||||
if (!Array.isArray(rawTriplets) || rawTriplets.length === 0) {
|
||||
return c.json({ error: 'triplets 必須為非空字串陣列' }, 400);
|
||||
}
|
||||
|
||||
try {
|
||||
const now = new Date();
|
||||
const timestamp = now.toISOString();
|
||||
const versionId = `search-v1-${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}-${String(now.getHours()).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}${String(now.getSeconds()).padStart(2, '0')}`;
|
||||
|
||||
const result = await handleCypherSearch(rawTriplets, c.env);
|
||||
|
||||
const response = {
|
||||
version: versionId,
|
||||
timestamp,
|
||||
triplets: rawTriplets,
|
||||
nodes: result.nodes,
|
||||
cypher: result.cypher,
|
||||
missing: result.missing,
|
||||
};
|
||||
|
||||
return c.json(response);
|
||||
} catch (err) {
|
||||
const errMsg = err instanceof Error ? err.message : String(err);
|
||||
return c.json({ error: errMsg }, 400);
|
||||
}
|
||||
});
|
||||
|
||||
// POST /cypher/execute — 三元組 → 一步執行(search + execute 合一)
|
||||
cypherRouter.post('/cypher/execute', async (c) => {
|
||||
const body = await c.req.json() as {
|
||||
triplets?: unknown;
|
||||
context?: Record<string, unknown>;
|
||||
config?: Record<string, Record<string, unknown>>; // node_name → {component, ...params}
|
||||
graph_id?: string;
|
||||
graph_name?: string;
|
||||
};
|
||||
|
||||
if (!Array.isArray(body?.triplets) || body.triplets.length === 0) {
|
||||
return c.json({ error: 'triplets 必須為非空字串陣列' }, 400);
|
||||
}
|
||||
|
||||
const graphId = typeof body.graph_id === 'string' ? body.graph_id : `triplet-exec-${Date.now()}`;
|
||||
const graphName = typeof body.graph_name === 'string' ? body.graph_name : 'Triplet Execution';
|
||||
const now = new Date();
|
||||
const timestamp = now.toISOString();
|
||||
// 版本號格式:execute-v1-20260327-143022
|
||||
const versionId = `execute-v1-${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}-${String(now.getHours()).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}${String(now.getSeconds()).padStart(2, '0')}`;
|
||||
|
||||
const apiKey = c.req.header('X-Arcrun-API-Key') ?? undefined;
|
||||
|
||||
try {
|
||||
const result = await handleCypherExecute(
|
||||
body.triplets as unknown[],
|
||||
body.context,
|
||||
graphId,
|
||||
graphName,
|
||||
body.config,
|
||||
c.env,
|
||||
(p) => c.executionCtx.waitUntil(p),
|
||||
apiKey,
|
||||
);
|
||||
// 包裝成開發友善格式(execute 成功時)
|
||||
const response = {
|
||||
version: versionId,
|
||||
timestamp,
|
||||
...result,
|
||||
};
|
||||
return c.json(response);
|
||||
} catch (err) {
|
||||
const errMsg = err instanceof Error ? err.message : String(err);
|
||||
try {
|
||||
const parsed = JSON.parse(errMsg);
|
||||
const response = {
|
||||
version: versionId,
|
||||
timestamp,
|
||||
...parsed,
|
||||
};
|
||||
return c.json(response, 500);
|
||||
} catch {
|
||||
return c.json({ version: versionId, timestamp, success: false, error: errMsg, duration_ms: 0 }, 500);
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user