加一套抓真实请求做预设对比的工具,并把三份预设的机制差异记进 README。
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
// 让本仓库发同一次真实请求,落到 capture-server 上。
|
||||
// 用法: node drive-wa.mjs --preset "C:/Users/zed/Downloads/linux_1.3.json" --message 你好 --capture-port 8802
|
||||
// 用独立 WRITING_AGENT_DATA_DIR,不碰 ~/.writing-agent 里的配置。
|
||||
// 预设走本仓库自己的 /api/presets/import(纯导入 json),之后走直聊的 ephemeral complete 真发一次。
|
||||
import { spawn } from "node:child_process";
|
||||
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { listCaptures, makeLogger, parseArgs, waitForNewCapture, waitHttp } from "./lib.mjs";
|
||||
|
||||
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
||||
const REPO = path.resolve(HERE, "../..");
|
||||
const log = makeLogger("drive-wa");
|
||||
|
||||
const DEFAULTS = {
|
||||
preset: "",
|
||||
message: "你好",
|
||||
capturePort: 8802,
|
||||
port: 23377,
|
||||
out: path.join(HERE, "captures"),
|
||||
dataRoot: path.join(HERE, ".wa-data"),
|
||||
timeoutMs: 120000,
|
||||
};
|
||||
|
||||
async function postJson(url, body) {
|
||||
const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
|
||||
const text = await res.text();
|
||||
let data = null;
|
||||
try { data = JSON.parse(text); } catch (e) { data = { raw: text.slice(0, 800) }; }
|
||||
if (!res.ok) throw new Error("HTTP " + res.status + " " + JSON.stringify(data).slice(0, 800));
|
||||
return data;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs(process.argv.slice(2), DEFAULTS);
|
||||
if (!args.preset) throw new Error("缺少 --preset");
|
||||
const presetName = path.basename(args.preset, ".json");
|
||||
const raw = JSON.parse(readFileSync(args.preset, "utf8"));
|
||||
mkdirSync(args.dataRoot, { recursive: true });
|
||||
mkdirSync(args.out, { recursive: true });
|
||||
|
||||
// 一条指向假上游的 API 配置。直接写盘,避免能力探测的额外请求混进抓包。
|
||||
const now = new Date().toISOString();
|
||||
const profiles = {
|
||||
version: 1,
|
||||
profiles: [
|
||||
{
|
||||
id: "probe-mock",
|
||||
name: "mock",
|
||||
baseUrl: "http://127.0.0.1:" + args.capturePort + "/v1",
|
||||
apiKey: "sk-mock",
|
||||
model: "mock-model",
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
],
|
||||
};
|
||||
writeFileSync(path.join(args.dataRoot, "profiles.json"), JSON.stringify(profiles, null, 2), "utf8");
|
||||
|
||||
try { rmSync(path.join(args.dataRoot, "presets"), { recursive: true, force: true }); } catch (e) {} const known = new Set(listCaptures(args.out, "wa"));
|
||||
const env = { ...process.env, PORT: String(args.port), WRITING_AGENT_DATA_DIR: args.dataRoot };
|
||||
const server = spawn(process.execPath, [path.join(REPO, "node_modules/tsx/dist/cli.mjs"), path.join(REPO, "src/server/web-server.ts")], { cwd: REPO, env, stdio: ["ignore", "pipe", "pipe"] });
|
||||
const logs = [];
|
||||
server.stdout.on("data", (d) => logs.push(String(d)));
|
||||
server.stderr.on("data", (d) => logs.push(String(d)));
|
||||
const base = "http://127.0.0.1:" + args.port;
|
||||
try {
|
||||
await waitHttp(base + "/api/health", args.timeoutMs, "本仓库服务起来");
|
||||
log("服务已启动: " + base);
|
||||
|
||||
const imported = await postJson(base + "/api/presets/import", { raw, name: presetName, activate: true });
|
||||
const presetId = imported.preset.id;
|
||||
log("预设已导入: " + presetName + " (id=" + presetId + ")");
|
||||
if (imported.warnings && imported.warnings.length) log("导入提示: " + JSON.stringify(imported.warnings).slice(0, 600));
|
||||
|
||||
const body = {
|
||||
text: args.message,
|
||||
presetId,
|
||||
complete: true,
|
||||
turns: [],
|
||||
windows: [{ id: "w1", profileId: "probe-mock" }],
|
||||
systemPrompt: "",
|
||||
loreBefore: "",
|
||||
seedHistory: "",
|
||||
loreAfter: "",
|
||||
postTurn: "",
|
||||
};
|
||||
const result = await postJson(base + "/api/direct-chat/ephemeral/complete", body);
|
||||
const first = result.multi && result.multi.results && result.multi.results[0];
|
||||
if (first && first.error) throw new Error("本轮请求失败: " + first.error);
|
||||
log("本轮完成, reply=" + JSON.stringify(String((first && first.reply) || "").slice(0, 40)));
|
||||
|
||||
const captureFile = await waitForNewCapture(args.out, "wa", known, args.timeoutMs);
|
||||
log("抓到请求: " + captureFile);
|
||||
console.log(JSON.stringify({ side: "wa", preset: presetName, capture: path.join(args.out, captureFile) }));
|
||||
} catch (err) {
|
||||
console.error("[drive-wa] 失败: " + err.message);
|
||||
if (logs.length) console.error(logs.join("").slice(-4000));
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
try { server.kill(); } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
await main();
|
||||
Reference in New Issue
Block a user