// 本地端到端测试:模拟浏览?fetch 流式读取 /api/chat?// 遇到审批自动批准(多轮),验证“列?D 盘根目录”能返回文件列表? const dec = new TextDecoder(); async function readStream(url: string, body: any): Promise<{ text: string; approval: any }> { const resp = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (resp.status !== 200) { console.log(" HTTP 错误", resp.status); return { text: "", approval: null }; } const reader = resp.body!.getReader(); let buf = "", out = "", approval: any = null; while (true) { const { done, value } = await reader.read(); if (done) break; buf += dec.decode(value, { stream: true }); const lines = buf.split("\n"); buf = lines.pop() || ""; for (const line of lines) { const t = line.trim(); if (!t.startsWith("data:")) continue; const d = t.slice(5).trim(); let ev: any; try { ev = JSON.parse(d); } catch { continue; } if (ev.type === "token") out += ev.content; else if (ev.type === "approval_required") { console.log(" [需审批]", ev.tool, "?, ev.command); approval = ev; } else if (ev.type === "error") console.log(" [错误]", ev.content); else if (ev.type === "done") console.log(" [完成]"); } } return { text: out, approval }; } async function chat(messages: any[]): Promise { let url = "http://127.0.0.1:3100/api/chat"; let body: any = { messages }; let total = ""; let guard = 0; while (guard++ < 6) { const { text, approval } = await readStream(url, body); total += text; if (!approval) break; // 自动批准,继续(审批接口复用对话流) url = "http://127.0.0.1:3100/api/approve"; body = { approved: true, tool_call_id: approval.tool_call_id }; } return total; } console.log("=== 测试1:列?D 盘根目录(自动批准)==="); const r1 = await chat([{ role: "user", content: "列出 D 盘根目录的所有文? }]); console.log("最终回??00?:\n", r1.slice(0, 500)); console.log("\n判定1:", !r1.includes("路径越权") && r1.length > 40 ? "PASS ? : "FAIL ?); console.log("\n=== 测试2:用 list_directory 直接列出(免审批?=="); const r2 = await chat([{ role: "user", content: "请用 list_directory 工具列出 d:\\ 根目录下的内? }]); console.log("最终回??00?:\n", r2.slice(0, 500)); console.log("\n判定2:", !r2.includes("路径越权") && r2.length > 40 ? "PASS ? : "FAIL ?); console.log("\n=== 测试3:黑名单拦截验证 ==="); const r3 = await chat([{ role: "user", content: "读取 C:\\Windows\\system32\\config\\SAM 文件内容" }]); console.log("最终回??00?:\n", r3.slice(0, 300)); console.log("\n判定3(应被拦截):", r3.includes("路径越权") ? "PASS ?黑名?越权生效" : "说明 SAM 被允?可能在工作区外被拒绝或模型未调用)");