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,88 @@
|
||||
/**
|
||||
* workflow.yaml 解析與三元組驗證
|
||||
*/
|
||||
import yaml from 'js-yaml';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
export interface WorkflowYaml {
|
||||
name: string;
|
||||
description?: string;
|
||||
flow: string[];
|
||||
config?: Record<string, Record<string, unknown>>;
|
||||
}
|
||||
|
||||
export interface ParsedTriplet {
|
||||
subject: string;
|
||||
relation: string;
|
||||
object: string;
|
||||
}
|
||||
|
||||
/** 合法關係詞(拒絕 PIPE)*/
|
||||
const VALID_RELATIONS = new Set([
|
||||
'完成後', '失敗時', '對每個', '條件滿足時',
|
||||
'ON_SUCCESS', 'ON_FAIL', 'FOREACH', 'IF', 'ON_CLICK', 'CALLS_SUBFLOW',
|
||||
]);
|
||||
|
||||
const BANNED_RELATIONS = new Set(['PIPE']);
|
||||
|
||||
export function loadWorkflowYaml(filePath: string): WorkflowYaml {
|
||||
const raw = readFileSync(filePath, 'utf8');
|
||||
const doc = yaml.load(raw) as WorkflowYaml;
|
||||
|
||||
if (!doc.name) throw new Error('workflow.yaml 缺少 name 欄位');
|
||||
if (!Array.isArray(doc.flow) || doc.flow.length === 0) {
|
||||
throw new Error('workflow.yaml 的 flow 欄位必須為非空陣列');
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
export function parseTriplets(flow: string[]): ParsedTriplet[] {
|
||||
const triplets: ParsedTriplet[] = [];
|
||||
|
||||
for (const line of flow) {
|
||||
const parts = line.split('>>').map(s => s.trim());
|
||||
if (parts.length !== 3) {
|
||||
throw new Error(
|
||||
`三元組格式錯誤:「${line}」\n` +
|
||||
`正確格式:「A >> 關係詞 >> B」`
|
||||
);
|
||||
}
|
||||
const [subject, relation, object] = parts;
|
||||
triplets.push({ subject, relation, object });
|
||||
}
|
||||
|
||||
return triplets;
|
||||
}
|
||||
|
||||
export function validateRelations(triplets: ParsedTriplet[]): void {
|
||||
for (const t of triplets) {
|
||||
if (BANNED_RELATIONS.has(t.relation)) {
|
||||
throw new Error(
|
||||
`不允許使用關係詞「${t.relation}」。\n` +
|
||||
`「PIPE」已棄用,請改用「完成後」或「ON_SUCCESS」。`
|
||||
);
|
||||
}
|
||||
// 容許 FOREACH iterator 命名變體:「對每個 paragraph」/「FOREACH item」
|
||||
// graph-builder.ts 已支援這個 regex(commit e8fca33 2026-05-07)
|
||||
const foreachMatch = t.relation.match(/^(?:對每個|FOREACH)\s+\w+$/i);
|
||||
if (foreachMatch) continue;
|
||||
|
||||
if (!VALID_RELATIONS.has(t.relation)) {
|
||||
throw new Error(
|
||||
`未知關係詞「${t.relation}」。\n` +
|
||||
`合法關係詞:${[...VALID_RELATIONS].join('、')}\n` +
|
||||
`(FOREACH 支援 iterator 命名:「對每個 X」/「FOREACH X」)`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getNodeNames(triplets: ParsedTriplet[]): string[] {
|
||||
const nodes = new Set<string>();
|
||||
for (const t of triplets) {
|
||||
nodes.add(t.subject);
|
||||
nodes.add(t.object);
|
||||
}
|
||||
return [...nodes];
|
||||
}
|
||||
Reference in New Issue
Block a user