feat: 薄殼原則落地 + seed 下沉 API + MCP 進主庫 + 部署一致性
壓測四橫向問題修正(docs 壓測報告):
① 薄殼原則成鐵律:能力長在 API,CLI/MCP/lib 只暴露
- seed 下沉成 API 行為:cypher-executor POST /init/seed(一次灌 API+auth recipe),
種子資料移到 server src/lib/api-recipe-seeds.ts,CLI 改薄殼一次呼叫
- 解除 deployFullyOk 連坐 + init 補 seed auth recipe + update 補 seed/全 KV
- registry SUBMISSIONS_KV 補進 REQUIRED_KV_NAMESPACES(修 20/21)
② MCP 統一帳號來源(單一 remote MCP + .env 切 MCP URL)
- MCP 從 sibling repo 搬進 arcrun/mcp/(remote Worker,route 改 mcp.arcrun.dev)
- config 加 mcp_url 三層解析 + getMcpUrl + DEFAULT_MCP_URL
- 新增 acr mcp-setup:依 config 寫專案 .mcp.json(接案切資料夾自動切 MCP)
- acr --version 改動態讀 package.json(根治漂移)
③ Deploy 一致性
- tests/release.feature + scripts/check-release.sh
- local-deploy.sh:CLI npm publish + auto patch bump + CHANGELOG
- local-deploy.sh bash 3.2 相容修正(mapfile / 空陣列 set -u)
- builtins/pnpm-lock.yaml
④ README self-hosted 同步現況(移除 R2 殘留、加 flag/env、多帳號)
CLI bump → 1.3.0
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Cypher-executor service binding wrapper — LI SDD M2.2
|
||||
*
|
||||
* 對應 .agents/specs/llm-interface/ Milestone 2.2。
|
||||
* 統一 arcrun-mcp 對 cypher-executor 的呼叫,預設 fetch 樣板 + auth header 注入。
|
||||
*
|
||||
* arcrun 平台「ak_」級 api_key 跟 MCP 「pk_live」級 token 是兩層 auth:
|
||||
* - pk_live (partner-auth middleware) → org_namespace(MCP 自己用)
|
||||
* - ak_xxx (X-Arcrun-API-Key) → cypher-executor workflow 操作
|
||||
*
|
||||
* 此 client 統一處理 ak_xxx 注入 + error contract 化(給 AI 看的 next_actions)。
|
||||
*/
|
||||
|
||||
import type { Env } from "../types.js";
|
||||
|
||||
export interface CypherCallOpts {
|
||||
apiKey: string;
|
||||
method?: string;
|
||||
body?: unknown;
|
||||
query?: Record<string, string | number>;
|
||||
}
|
||||
|
||||
export async function cypherFetch(
|
||||
env: Env,
|
||||
path: string,
|
||||
opts: CypherCallOpts,
|
||||
): Promise<Response> {
|
||||
if (!env.CYPHER_EXECUTOR) {
|
||||
throw new Error("CYPHER_EXECUTOR service binding not configured");
|
||||
}
|
||||
|
||||
const url = new URL(`http://cypher-executor${path}`);
|
||||
if (opts.query) {
|
||||
for (const [k, v] of Object.entries(opts.query)) {
|
||||
url.searchParams.set(k, String(v));
|
||||
}
|
||||
}
|
||||
|
||||
return env.CYPHER_EXECUTOR.fetch(url.toString(), {
|
||||
method: opts.method ?? "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Arcrun-API-Key": opts.apiKey,
|
||||
},
|
||||
body: opts.body ? JSON.stringify(opts.body) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 統一 error response 格式化(LI SDD §1.3)
|
||||
*
|
||||
* 用法:
|
||||
* const res = await cypherFetch(...);
|
||||
* if (!res.ok) return errorResponse('not_found', `...`, [...], await res.text());
|
||||
*/
|
||||
export function errorResponse(
|
||||
error_code: string,
|
||||
human_message: string,
|
||||
next_actions: string[],
|
||||
detail?: string,
|
||||
): {
|
||||
content: { type: "text"; text: string }[];
|
||||
isError: true;
|
||||
} {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{ ok: false, error_code, human_message, next_actions, detail },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功 response 格式化
|
||||
*/
|
||||
export function successResponse(
|
||||
data: unknown,
|
||||
hints?: string[],
|
||||
): {
|
||||
content: { type: "text"; text: string }[];
|
||||
} {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{ ok: true, data, ...(hints ? { hints } : {}) },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Env } from "../types.js";
|
||||
|
||||
/**
|
||||
* Wrapper around env.KBDB.fetch that automatically injects
|
||||
* the KBDB_INTERNAL_TOKEN Authorization header.
|
||||
*/
|
||||
export function kbdbFetch(env: Env, path: string, init?: RequestInit): Promise<Response> {
|
||||
const headers = new Headers((init?.headers as HeadersInit) || {});
|
||||
if (env.KBDB_INTERNAL_TOKEN) {
|
||||
headers.set("Authorization", `Bearer ${env.KBDB_INTERNAL_TOKEN}`);
|
||||
}
|
||||
return env.KBDB.fetch(`http://kbdb${path}`, { ...init, headers });
|
||||
}
|
||||
Reference in New Issue
Block a user