3e65e22775
壓測四橫向問題修正(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>
102 lines
2.3 KiB
TypeScript
102 lines
2.3 KiB
TypeScript
/**
|
||
* 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,
|
||
),
|
||
},
|
||
],
|
||
};
|
||
}
|