/** * 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; } export async function cypherFetch( env: Env, path: string, opts: CypherCallOpts, ): Promise { 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, ), }, ], }; }