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:
uncle6me-web
2026-06-03 15:52:38 +08:00
commit 922a57fe34
485 changed files with 89356 additions and 0 deletions
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/env node
// Backfill component metadata 進 registry index
// SDD: matrix/arcrun/.agents/specs/component-registry-canon/design.md Phase 1
//
// 用法:
// node scripts/backfill-index.mjs --dry-run # 看會送什麼
// node scripts/backfill-index.mjs # 真的灌
//
// 流程:
// 1. 掃 ../components/*/component.contract.yaml
// 2. 解析 YAML(用 zero-dep 簡易 parsercontract 是 well-formed YAML
// 3. 對每個 contract POST registry.arcrun.dev/components/index-only
// 4. 印 success / already_indexed / fail 統計
import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const COMPONENTS_DIR = join(__dirname, '..', 'components');
const REGISTRY_URL = process.env.REGISTRY_URL ?? 'https://registry.arcrun.dev';
const DRY_RUN = process.argv.includes('--dry-run');
// YAML 是 well-formed contract.yaml,用 js-yaml 解析最穩
async function parseYaml(text) {
const { load } = await import('js-yaml');
return load(text);
}
function listComponents() {
return readdirSync(COMPONENTS_DIR)
.filter((name) => {
const p = join(COMPONENTS_DIR, name);
return statSync(p).isDirectory();
})
.sort();
}
async function readContract(name) {
const path = join(COMPONENTS_DIR, name, 'component.contract.yaml');
const text = readFileSync(path, 'utf-8');
return parseYaml(text);
}
async function postIndexOnly(contract) {
const res = await fetch(`${REGISTRY_URL}/components/index-only`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ contract }),
});
const body = await res.text();
let parsed;
try {
parsed = JSON.parse(body);
} catch {
parsed = { raw: body };
}
return { status: res.status, body: parsed };
}
async function main() {
console.log('=== arcrun Component Registry Backfill ===');
console.log(`Registry: ${REGISTRY_URL}`);
console.log(`Mode: ${DRY_RUN ? 'DRY RUN' : 'LIVE'}`);
console.log();
const names = listComponents();
console.log(`Found ${names.length} components in ${COMPONENTS_DIR}`);
console.log();
const stats = { created: 0, already: 0, fail: 0 };
for (const name of names) {
let contract;
try {
contract = await readContract(name);
} catch (e) {
console.log(`${name.padEnd(28)} READ FAIL: ${e.message}`);
stats.fail++;
continue;
}
const cid = contract.canonical_id ?? '(no canonical_id)';
const ver = contract.version ?? '(no version)';
if (DRY_RUN) {
console.log(`${name.padEnd(28)} ${cid} ${ver}`);
continue;
}
try {
const { status, body } = await postIndexOnly(contract);
if (status === 200 && body.already_indexed) {
console.log(` = ${name.padEnd(28)} ${cid} ${ver} [already]`);
stats.already++;
} else if (status === 201) {
console.log(`${name.padEnd(28)} ${cid} ${ver} [${body.component_hash_id}]`);
stats.created++;
} else {
console.log(`${name.padEnd(28)} ${cid} ${ver} HTTP ${status}: ${JSON.stringify(body).slice(0, 200)}`);
stats.fail++;
}
} catch (e) {
console.log(`${name.padEnd(28)} POST FAIL: ${e.message}`);
stats.fail++;
}
}
console.log();
console.log('=== Summary ===');
console.log(`Created: ${stats.created}`);
console.log(`Already indexed: ${stats.already}`);
console.log(`Failed: ${stats.fail}`);
process.exit(stats.fail > 0 ? 1 : 0);
}
main().catch((e) => {
console.error('Fatal:', e);
process.exit(1);
});