项目文件夹

文件
2026-07-13 12:52:40 +08:00

179 行
4.5 KiB
TypeScript

/**
* Quick diagnostic: start sidecar, create session, send a prompt, capture logs.
* Run: bun run sidecar/chat-test.ts
*/
import { type ChildProcess, spawn } from "node:child_process";
import { join } from "node:path";
const TIMEOUT = 60_000;
const cwd = join(import.meta.dir, "..");
let child: ChildProcess | null = null;
let wsEndpoint = "";
const stderrLines: string[] = [];
function startSidecar(): Promise<string> {
return new Promise((resolve, reject) => {
child = spawn("bun", ["run", "sidecar/index.ts"], {
cwd,
stdio: ["ignore", "pipe", "pipe"],
env: { ...process.env },
});
const timer = setTimeout(
() => reject(new Error("sidecar did not become ready")),
20_000,
);
child.stdout?.on("data", (chunk) => {
for (const line of String(chunk).split("\n")) {
const trimmed = line.trim();
if (!trimmed) continue;
try {
const parsed = JSON.parse(trimmed);
if (parsed.type === "ready" && parsed.wsEndpoint) {
clearTimeout(timer);
resolve(parsed.wsEndpoint);
return;
}
} catch {}
}
});
child.stderr?.on("data", (chunk) => {
const text = String(chunk).trim();
if (text) {
stderrLines.push(text);
process.stderr.write(`[sidecar-stderr] ${text}\n`);
}
});
child.on("exit", (code) => {
clearTimeout(timer);
reject(new Error(`sidecar exited with code ${code}`));
});
});
}
function sendWsCommand<T>(
command: string,
args?: Record<string, unknown>,
): Promise<{ result: T; events: Array<Record<string, unknown>> }> {
return new Promise((resolve, reject) => {
const events: Array<Record<string, unknown>> = [];
const socket = new WebSocket(wsEndpoint);
const timer = setTimeout(() => {
socket.close();
reject(
new Error(`timed out waiting for ${command} (events=${events.length})`),
);
}, TIMEOUT);
socket.onopen = () => {
socket.send(
JSON.stringify({ type: "command", id: "test", command, args }),
);
};
socket.onmessage = (event) => {
const parsed = JSON.parse(String(event.data));
if (parsed.type === "event") {
events.push(parsed.event as Record<string, unknown>);
return;
}
if (parsed.type === "response" && parsed.id === "test") {
clearTimeout(timer);
socket.close();
if (!parsed.ok) {
reject(new Error(parsed.error || `${command} failed`));
return;
}
resolve({ result: parsed.result as T, events });
}
};
socket.onerror = () => {
clearTimeout(timer);
reject(new Error(`websocket error during ${command}`));
};
});
}
async function main() {
console.log("Starting sidecar...");
wsEndpoint = await startSidecar();
console.log(`Sidecar ready at ${wsEndpoint}`);
// Step 1: Start a session
console.log("\n=== Step 1: Start session ===");
const { result: startResult } = await sendWsCommand<{ sessionId: string }>(
"chat_session_command",
{
request: {
action: "start",
config: {
provider: "cline",
model: "claude-sonnet-4-20250514",
apiKey: process.env.CLINE_API_KEY || "",
workspaceRoot: cwd,
cwd: cwd,
mode: "act",
enableTools: false,
enableSpawn: false,
enableTeams: false,
},
},
},
);
console.log("Start result:", JSON.stringify(startResult, null, 2));
const sessionId = startResult.sessionId;
if (!sessionId) {
throw new Error("No sessionId returned from start");
}
// Step 2: Send a prompt
console.log("\n=== Step 2: Send prompt ===");
const { result: sendResult, events } = await sendWsCommand<unknown>(
"chat_session_command",
{
request: {
action: "send",
sessionId,
prompt: "Say hello in one short sentence.",
},
},
);
console.log("Send result:", JSON.stringify(sendResult, null, 2));
console.log(`Events received during send: ${events.length}`);
for (const evt of events.slice(0, 20)) {
const payload =
evt.payload && typeof evt.payload === "object"
? (evt.payload as Record<string, unknown>)
: {};
console.log(
` event: ${evt.name} payload.stream=${payload.stream ?? "N/A"} chunk=${String(payload.chunk ?? "").slice(0, 80)}`,
);
}
console.log("\n=== Stderr lines ===");
for (const line of stderrLines) {
if (line.includes("[sidecar:")) {
console.log(` ${line}`);
}
}
console.log("\nDone.");
}
main()
.catch((err) => {
console.error("FAILED:", err.message);
console.log("\n=== Stderr lines ===");
for (const line of stderrLines) {
if (
line.includes("[sidecar:") ||
line.includes("error") ||
line.includes("Error")
) {
console.log(` ${line}`);
}
}
})
.finally(() => {
child?.kill("SIGTERM");
process.exit(0);
});