/** * seed-api-recipes.ts * * 將現役 API recipe 種子上傳至目標 cypher-executor(prod 或 self-host)。 * 種子資料的單一來源在 server 端(src/lib/api-recipe-seeds.ts,薄殼原則 rule 07),此腳本 import 它。 * 注意:self-host init 與 prod 補灌建議改打 POST /init/seed(API 行為,一次灌 API+auth recipe); * 本腳本保留作為 KV 直寫的備援路徑。 * * 執行: * npx tsx scripts/seed-api-recipes.ts * * 環境變數: * ARCRUN_API_URL - 目標 cypher-executor,預設 https://cypher.arcrun.dev * ARCRUN_API_KEY - X-Arcrun-API-Key(POST /recipes 需要) * * 注意:API recipe 帶 endpoint(資料去向)→ POST /recipes 會要 exposure_consent * (data-exfil-warning)。seed 是平台預建、非用戶 push,腳本帶種子層級的 consent。 * * 對應 SDD:.agents/specs/arcrun/sdk-and-website/self-hosted-init.md §5 */ // 種子資料的單一來源已移到 server 端(src/lib/api-recipe-seeds.ts,薄殼原則 rule 07)。 // 注意:現在 prod 補灌建議直接打 POST /init/seed(API 行為);本腳本保留作為 KV 直寫的備援。 import { API_RECIPE_SEEDS } from '../src/lib/api-recipe-seeds'; const BASE_URL = process.env.ARCRUN_API_URL ?? 'https://cypher.arcrun.dev'; const API_KEY = process.env.ARCRUN_API_KEY ?? ''; async function main() { console.log(`\n Seeding ${API_RECIPE_SEEDS.length} API recipes → ${BASE_URL}\n`); let ok = 0; let fail = 0; for (const recipe of API_RECIPE_SEEDS) { process.stdout.write(` ${recipe.canonical_id.padEnd(24)} `); try { const res = await fetch(`${BASE_URL}/recipes`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(API_KEY ? { 'X-Arcrun-API-Key': API_KEY } : {}), }, body: JSON.stringify({ canonical_id: recipe.canonical_id, display_name: recipe.display_name, description: recipe.description, endpoint: recipe.endpoint, method: recipe.method, auth_service: recipe.auth_service, // 種子層級的暴露同意:平台預建 recipe,非用戶互動 push。 // 格式須符合 cypher-executor ExposureConsent(confirmed_by_human + understood + confirmed_at)。 // 誠實標明來源是 seed,軌跡可審(mindset §7:機制價值是歸責+可審,非防偽)。 exposure_consent: { confirmed_by_human: true, understood: `platform seed recipe (api-recipe-seeds.ts): ${recipe.canonical_id} → ${recipe.endpoint}`, confirmed_at: new Date().toISOString(), }, }), }); if (res.ok) { console.log('✓'); ok++; } else { const err = await res.text().catch(() => ''); console.log(`✗ HTTP ${res.status}: ${err.slice(0, 120)}`); fail++; } } catch (e) { console.log(`✗ ${e instanceof Error ? e.message : String(e)}`); fail++; } } console.log(`\n 完成:${ok} 成功,${fail} 失敗\n`); if (fail > 0) process.exit(1); } main();