- Backend: action_coordinator, capability_snapshot, event_normalizer, tool_executor updates - Backend: approved_tool_executor with write guard, action leases, migration script - Frontend: maf-roundtable module (API client, event timeline, approval cards, fact flow panel) - Frontend: MafRoutes, MafSessionsPage, MafRunDetailPage - Tests: action_leases, migration_phase5, phase5_entry_e2e, real_write_e2e/http_e2e - Docs: update implementation plan Co-authored-by: Cursor <cursoragent@cursor.com>
59 lines
2.0 KiB
TypeScript
59 lines
2.0 KiB
TypeScript
/**
|
||
* MAF 多智能体会商 API 客户端地基。
|
||
*
|
||
* 复用全局 `apiFetch`(自动注入 Authorization + credentials:include),这里只做:
|
||
* - 拼 URL(`${getBackendBaseURL()}/api/maf-roundtable/...`)
|
||
* - 统一 `res.ok` 检查 + detail 提取(仿 task-buttons 的 readJson)
|
||
* - JSON 解析
|
||
*
|
||
* 不在此做 snake_case ⇄ camelCase 转换——后端即 snake_case,类型层直传(见 types.ts)。
|
||
*/
|
||
|
||
import { apiFetch } from "@/core/api/fetch-client";
|
||
import { getBackendBaseURL } from "@/core/config";
|
||
|
||
/** MAF roundtable 后端根路径(无尾斜杠)。 */
|
||
export const BASE = (): string => `${getBackendBaseURL()}/api/maf-roundtable`;
|
||
|
||
/**
|
||
* 检查 Response 并解析 JSON;失败时抛出带后端 `detail` 的 Error。
|
||
*
|
||
* - 404 = 不存在或非本人(后端统一返回 404 防止泄漏他人资源存在性)
|
||
* - 403 = 非所有者写(admin 也不能写别人的)
|
||
* - 503 = 功能关闭 / store 未就绪
|
||
*
|
||
* 这里不区分状态码语义,统一把 detail 透传给上层 toast/错误展示。
|
||
*/
|
||
export async function readJson<T>(res: Response, ctx: string): Promise<T> {
|
||
if (!res.ok) {
|
||
const data = await res.json().catch(() => null);
|
||
const detail =
|
||
data && typeof data === "object" && "detail" in data
|
||
? String((data as { detail: unknown }).detail)
|
||
: `${ctx} 失败(HTTP ${res.status})`;
|
||
throw new Error(detail);
|
||
}
|
||
return (await res.json()) as T;
|
||
}
|
||
|
||
/** GET JSON 的快捷封装。 */
|
||
export async function getJson<T>(url: string, ctx: string): Promise<T> {
|
||
const res = await apiFetch(url, { method: "GET" });
|
||
return readJson<T>(res, ctx);
|
||
}
|
||
|
||
/** 发送 JSON body 的快捷封装。 */
|
||
export async function sendJson<T>(
|
||
url: string,
|
||
method: "POST" | "PUT" | "DELETE" | "PATCH",
|
||
body: unknown,
|
||
ctx: string,
|
||
): Promise<T> {
|
||
const res = await apiFetch(url, {
|
||
method,
|
||
headers: { "Content-Type": "application/json" },
|
||
body: body === undefined ? undefined : JSON.stringify(body),
|
||
});
|
||
return readJson<T>(res, ctx);
|
||
}
|