/** * 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 需要) * * 注意:暴露 consent 閘已移除(leo 2026-06-29,Arcrun#13),POST /recipes 不再需要 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, // 暴露 consent 閘已移除(leo 2026-06-29,Arcrun#13):不再帶 exposure_consent。 }), }); 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();