mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 18:55:22 +00:00
一次性 `codegraph explore` CLI 不会同步索引,新增符号在手动 sync 前 无法被搜到。改为按索引根惰性启动常驻 `codegraph serve --mcp` 子进程, 由组合包自有的逐行 JSON-RPC 客户端(extensions/codegraph-client.ts, 不依赖 MCP adapter/SDK)收发,serve 内置文件监听自动同步索引,删除 mtime 结果缓存。子进程闲置 10 分钟或会话结束关闭,子进程死亡时透明 重试一次;用户拒绝初始化在会话内记住;用户中止调用不关闭子进程; 所有失败以文本打回引导改用 find/grep/read/LSP。新增模拟服务与真实 CLI 集成测试。 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
121 lines
3.3 KiB
JavaScript
121 lines
3.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Fake `codegraph serve --mcp`: newline-delimited JSON-RPC 2.0 on stdio, for
|
|
* tests of extensions/codegraph-client.ts.
|
|
*
|
|
* Env:
|
|
* FAKE_CG_MODE "ok" (default) | "no-tool" | "crash-first" | "crash" | "hang"
|
|
* no-tool: tools/list returns no codegraph_explore
|
|
* crash-first: on the first tools/call, write FAKE_CG_MARKER
|
|
* and exit(1) — a second spawned process sees
|
|
* the marker and serves normally
|
|
* crash: exit(1) on every tools/call
|
|
* hang: never reply to tools/call
|
|
* FAKE_CG_SCHEMA JSON array of inputSchema property names
|
|
* (default ["query","maxFiles","projectPath"])
|
|
* FAKE_CG_MARKER marker file path for crash-first
|
|
* FAKE_CG_LOG file to append every inbound message as a JSON line
|
|
*/
|
|
|
|
import fs from "node:fs";
|
|
import readline from "node:readline";
|
|
|
|
const mode = process.env.FAKE_CG_MODE || "ok";
|
|
const marker = process.env.FAKE_CG_MARKER;
|
|
const logFile = process.env.FAKE_CG_LOG;
|
|
const schemaProps = (() => {
|
|
try {
|
|
return JSON.parse(process.env.FAKE_CG_SCHEMA || "");
|
|
} catch {
|
|
return ["query", "maxFiles", "projectPath"];
|
|
}
|
|
})();
|
|
|
|
function log(entry) {
|
|
if (logFile) fs.appendFileSync(logFile, JSON.stringify(entry) + "\n");
|
|
}
|
|
|
|
function send(msg) {
|
|
process.stdout.write(JSON.stringify(msg) + "\n");
|
|
}
|
|
|
|
const rl = readline.createInterface({ input: process.stdin });
|
|
rl.on("line", (line) => {
|
|
const text = line.trim();
|
|
if (!text) return;
|
|
let msg;
|
|
try {
|
|
msg = JSON.parse(text);
|
|
} catch {
|
|
return;
|
|
}
|
|
if (!msg || typeof msg !== "object") return;
|
|
log(msg);
|
|
if (typeof msg.method !== "string") return;
|
|
|
|
switch (msg.method) {
|
|
case "initialize":
|
|
send({
|
|
jsonrpc: "2.0",
|
|
id: msg.id,
|
|
result: {
|
|
protocolVersion: "2025-06-18",
|
|
capabilities: {},
|
|
serverInfo: { name: "fake-codegraph", version: "0.0.0" },
|
|
},
|
|
});
|
|
return;
|
|
case "tools/list": {
|
|
const tools =
|
|
mode === "no-tool"
|
|
? []
|
|
: [
|
|
{
|
|
name: "codegraph_explore",
|
|
description: "fake explore",
|
|
inputSchema: {
|
|
type: "object",
|
|
properties: Object.fromEntries(
|
|
schemaProps.map((name) => [name, { type: "string" }]),
|
|
),
|
|
required: ["query"],
|
|
},
|
|
},
|
|
];
|
|
send({ jsonrpc: "2.0", id: msg.id, result: { tools } });
|
|
return;
|
|
}
|
|
case "tools/call": {
|
|
const shouldCrash =
|
|
mode === "crash" ||
|
|
(mode === "crash-first" && marker && !fs.existsSync(marker));
|
|
if (shouldCrash) {
|
|
if (mode === "crash-first") fs.writeFileSync(marker, "crashed");
|
|
// Synchronous write — process.exit() can truncate pending pipe output.
|
|
fs.writeSync(2, "fatal: codegraph index corrupted\n");
|
|
process.exit(1);
|
|
}
|
|
if (mode === "hang") return; // Never reply — timeout/abort test.
|
|
const args = (msg.params && msg.params.arguments) || {};
|
|
send({
|
|
jsonrpc: "2.0",
|
|
id: msg.id,
|
|
result: {
|
|
content: [{ type: "text", text: `explore:${JSON.stringify(args)}` }],
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
default:
|
|
// Notifications (initialized, cancelled) need no reply; unknown
|
|
// requests get -32601.
|
|
if (msg.id !== undefined) {
|
|
send({
|
|
jsonrpc: "2.0",
|
|
id: msg.id,
|
|
error: { code: -32601, message: "Method not found" },
|
|
});
|
|
}
|
|
}
|
|
});
|