c1a06df68f
leo 2026-06-29 拍板:arcrun 是給 AI 用的系統,push/暴露不再需要人類確認。 - 刪 cypher-executor/src/lib/exposure-consent.ts(server 閘,MCP push 的真正擋點) - 刪 cli/src/lib/exposure-warning.ts(CLI 互動 + 非 TTY 拒絕) - recipes.ts / webhooks-named.ts:移除 checkExposureConsent 403 閘,直接放行 - recipe.ts / push.ts:移除 obtainExposureConsent 呼叫,不再 prompt/拒絕 - init-seed / seed-api-recipes:移除種子層級 consent - exposure_consent 欄位降為向後相容(讀舊 record 不報錯,不再寫入/檢查) 不補審計線索、不做替代防護(leo:先拿掉,出問題再設置)。 tsc 全綠(cypher-executor + cli)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
98 lines
3.8 KiB
TypeScript
98 lines
3.8 KiB
TypeScript
/**
|
||
* /init/seed — 一鍵把平台預建的 recipe 種子灌進 RECIPES KV(API 行為,非介面層職責)
|
||
*
|
||
* 薄殼原則(rule 07 + 壓測 §4.1/§5.5):
|
||
* 「裝好後預設有哪些 recipe」是 API 的能力。seed 由本端點完成,CLI/MCP 等薄殼只呼叫一次。
|
||
* 之前 seed 寫在 CLI init.ts(迴圈 POST + deployFullyOk gate),導致 registry 20/21 連坐 →
|
||
* seed 永遠被跳過、auth recipe 從不被 seed(壓測 §4.1)。本端點把 seed 下沉到 API,根除連坐。
|
||
*
|
||
* 行為:
|
||
* - 冪等:已存在的 recipe 直接覆寫(重跑安全)。
|
||
* - 一次灌「API recipe(API_RECIPE_SEEDS)+ auth recipe(AUTH_RECIPE_SEEDS)」兩者。
|
||
* - 直接寫 KV:種子是平台預建、非用戶互動 push(暴露 consent 閘已於 Arcrun#13 移除)。
|
||
* - 誠實回報:逐筆 ok/fail 計數,不假綠。
|
||
*
|
||
* 對應 SDD:.agents/specs/arcrun/sdk-and-website/self-hosted-init.md §5
|
||
*/
|
||
|
||
import { Hono } from 'hono';
|
||
import type { Bindings } from '../types';
|
||
import { deriveRecipeHash } from '../lib/hash';
|
||
import type { RecipeDefinition, AuthRecipeDefinition } from './recipes';
|
||
import { installRecipeRecord, resolveRecipe } from './recipes';
|
||
import { API_RECIPE_SEEDS } from '../lib/api-recipe-seeds';
|
||
import { AUTH_RECIPE_SEEDS } from '../lib/auth-recipe-seeds';
|
||
|
||
export const initSeedRouter = new Hono<{ Bindings: Bindings }>();
|
||
|
||
initSeedRouter.post('/init/seed', async (c) => {
|
||
const now = Date.now();
|
||
// 暴露 consent 閘已移除(leo 2026-06-29,Arcrun#13):種子不再帶 exposure_consent。
|
||
|
||
let apiOk = 0;
|
||
let apiFail = 0;
|
||
const apiErrors: string[] = [];
|
||
|
||
for (const seed of API_RECIPE_SEEDS) {
|
||
try {
|
||
const canonicalId = seed.canonical_id.trim().toLowerCase();
|
||
const hashId = await deriveRecipeHash(canonicalId);
|
||
// UUID 模型(§7.5.5):種子 author='system'。冪等:已安裝沿用其 uuid,否則新領。
|
||
const existing = await resolveRecipe(canonicalId, c.env.RECIPES);
|
||
const recipe: RecipeDefinition = {
|
||
uuid: existing?.uuid ?? crypto.randomUUID(),
|
||
author: existing?.author ?? 'system',
|
||
canonical_id: canonicalId,
|
||
hash_id: hashId,
|
||
display_name: seed.display_name,
|
||
description: seed.description,
|
||
endpoint: seed.endpoint,
|
||
method: (seed.method ?? 'POST').toUpperCase(),
|
||
auth_service: seed.auth_service,
|
||
created_at: existing?.created_at ?? now,
|
||
updated_at: now,
|
||
};
|
||
await installRecipeRecord(c.env.RECIPES, recipe);
|
||
apiOk++;
|
||
} catch (e) {
|
||
apiFail++;
|
||
apiErrors.push(`${seed.canonical_id}: ${e instanceof Error ? e.message : String(e)}`);
|
||
}
|
||
}
|
||
|
||
let authOk = 0;
|
||
let authFail = 0;
|
||
const authErrors: string[] = [];
|
||
|
||
for (const seed of AUTH_RECIPE_SEEDS) {
|
||
try {
|
||
const service = seed.service.trim().toLowerCase();
|
||
const existing = await c.env.RECIPES.get(`auth_recipe:${service}`, 'json') as AuthRecipeDefinition | null;
|
||
const recipe: AuthRecipeDefinition = {
|
||
...seed,
|
||
service,
|
||
created_at: existing?.created_at ?? now,
|
||
updated_at: now,
|
||
};
|
||
await c.env.RECIPES.put(`auth_recipe:${service}`, JSON.stringify(recipe));
|
||
authOk++;
|
||
} catch (e) {
|
||
authFail++;
|
||
authErrors.push(`${seed.service}: ${e instanceof Error ? e.message : String(e)}`);
|
||
}
|
||
}
|
||
|
||
const allOk = apiFail === 0 && authFail === 0;
|
||
return c.json(
|
||
{
|
||
success: allOk,
|
||
api_recipes: { seeded: apiOk, failed: apiFail, errors: apiErrors },
|
||
auth_recipes: { seeded: authOk, failed: authFail, errors: authErrors },
|
||
message: allOk
|
||
? `seed 完成:${apiOk} 個 API recipe + ${authOk} 個 auth recipe`
|
||
: `seed 部分失敗(誠實回報,未假綠):API ${apiOk}✓/${apiFail}✗,auth ${authOk}✓/${authFail}✗`,
|
||
},
|
||
allOk ? 200 : 207,
|
||
);
|
||
});
|