151 lines
6.5 KiB
TypeScript
151 lines
6.5 KiB
TypeScript
import * as readline from "node:readline";
|
|
|
|
const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
const mode = process.env.AGENTRUN_FAKE_CODEX_MODE ?? "success";
|
|
let threadCounter = 0;
|
|
let turnCounter = 0;
|
|
let observedThreadModel = false;
|
|
|
|
for await (const line of rl) {
|
|
const trimmed = String(line).trim();
|
|
if (trimmed.length === 0) continue;
|
|
const message = JSON.parse(trimmed) as { id?: number; method?: string; params?: Record<string, unknown> };
|
|
if (message.method === "initialize") {
|
|
if (mode === "invalid-json") {
|
|
process.stdout.write('{"token":"test-token-material"\n');
|
|
process.exit(0);
|
|
}
|
|
respond(message.id, { serverInfo: { name: "fake-codex-app-server", version: "self-test" } });
|
|
continue;
|
|
}
|
|
if (message.method === "thread/start") {
|
|
observedThreadModel = Object.hasOwn(message.params ?? {}, "model");
|
|
if (mode === "reject-unexpected-model" && observedThreadModel) {
|
|
respond(message.id, null, { code: -32000, message: "thread/start unexpectedly included model" });
|
|
continue;
|
|
}
|
|
if (mode === "require-explicit-model" && message.params?.model !== "gpt-5.5") {
|
|
respond(message.id, null, { code: -32000, message: "thread/start did not include expected model" });
|
|
continue;
|
|
}
|
|
threadCounter += 1;
|
|
const thread = { id: `thread_selftest_${threadCounter}` };
|
|
notify("thread/started", { thread });
|
|
respond(message.id, { thread });
|
|
continue;
|
|
}
|
|
if (message.method === "thread/resume") {
|
|
observedThreadModel = Object.hasOwn(message.params ?? {}, "model");
|
|
if (mode === "reject-unexpected-model" && observedThreadModel) {
|
|
respond(message.id, null, { code: -32000, message: "thread/resume unexpectedly included model" });
|
|
continue;
|
|
}
|
|
if (mode === "require-explicit-model" && message.params?.model !== "gpt-5.5") {
|
|
respond(message.id, null, { code: -32000, message: "thread/resume did not include expected model" });
|
|
continue;
|
|
}
|
|
const thread = { id: String(message.params?.threadId ?? "thread_selftest_resumed") };
|
|
notify("thread/started", { thread });
|
|
respond(message.id, { thread });
|
|
continue;
|
|
}
|
|
if (message.method === "turn/start") {
|
|
if (mode === "reject-unexpected-model" && (observedThreadModel || Object.hasOwn(message.params ?? {}, "model"))) {
|
|
respond(message.id, null, { code: -32000, message: "turn/start unexpectedly included model" });
|
|
continue;
|
|
}
|
|
if (mode === "require-explicit-model" && message.params?.model !== "gpt-5.5") {
|
|
respond(message.id, null, { code: -32000, message: "turn/start did not include expected model" });
|
|
continue;
|
|
}
|
|
if (mode === "missing-turn-result") {
|
|
respond(message.id, {});
|
|
continue;
|
|
}
|
|
if (mode === "provider-503-rpc-error") {
|
|
respond(message.id, null, { code: -32000, message: "responseStreamDisconnected: HTTP 503 Service Unavailable from provider" });
|
|
continue;
|
|
}
|
|
if (mode === "provider-401-rpc-error") {
|
|
respond(message.id, null, { code: -32000, message: "HTTP 401 Unauthorized: invalid api key" });
|
|
continue;
|
|
}
|
|
if (mode === "missing-terminal") {
|
|
turnCounter += 1;
|
|
const turn = { id: `turn_selftest_${turnCounter}`, status: "running" };
|
|
notify("turn/started", { turn });
|
|
respond(message.id, { turn });
|
|
continue;
|
|
}
|
|
if (mode === "provider-503-terminal") {
|
|
turnCounter += 1;
|
|
const turn = { id: `turn_selftest_${turnCounter}`, status: "failed", error: { message: "HTTP 503 Service Unavailable" } };
|
|
notify("turn/started", { turn: { id: turn.id, status: "running" } });
|
|
notify("turn/completed", { turn });
|
|
respond(message.id, { turn });
|
|
continue;
|
|
}
|
|
if (mode === "provider-429-terminal") {
|
|
turnCounter += 1;
|
|
const turn = { id: `turn_selftest_${turnCounter}`, status: "failed", error: { message: "HTTP 429 Too Many Requests: rate limit exceeded" } };
|
|
notify("turn/started", { turn: { id: turn.id, status: "running" } });
|
|
notify("turn/completed", { turn });
|
|
respond(message.id, { turn });
|
|
continue;
|
|
}
|
|
if (mode === "provider-503-retry-event") {
|
|
turnCounter += 1;
|
|
const turn = {
|
|
id: `turn_selftest_${turnCounter}`,
|
|
status: "failed",
|
|
error: {
|
|
message: "unexpected status 503 Service Unavailable: Service temporarily unavailable",
|
|
codexErrorInfo: { responseStreamDisconnected: { httpStatusCode: 503 } },
|
|
},
|
|
};
|
|
notify("turn/started", { turn: { id: turn.id, status: "running" } });
|
|
notify("error", {
|
|
willRetry: true,
|
|
error: {
|
|
message: "Reconnecting... 1/5",
|
|
codexErrorInfo: { responseStreamDisconnected: { httpStatusCode: 503 } },
|
|
additionalDetails: "unexpected status 503 Service Unavailable: Service temporarily unavailable, url: https://hyueapi.com/responses",
|
|
},
|
|
});
|
|
notify("turn/completed", { turn });
|
|
respond(message.id, { turn });
|
|
continue;
|
|
}
|
|
if (mode === "multi-agent-message-final") {
|
|
turnCounter += 1;
|
|
const turn = { id: `turn_selftest_${turnCounter}`, status: "completed" };
|
|
notify("turn/started", { turn });
|
|
notify("item/agentMessage/delta", { itemId: "msg_progress", delta: "I am checking the workspace. " });
|
|
notify("item/completed", { item: { id: "msg_progress", type: "agentMessage", text: "I am checking the workspace." } });
|
|
notify("item/agentMessage/delta", { itemId: "msg_final", delta: "Final answer only." });
|
|
notify("item/completed", { item: { id: "msg_final", type: "agentMessage", text: "Final answer only." } });
|
|
notify("turn/completed", { turn });
|
|
respond(message.id, { turn });
|
|
continue;
|
|
}
|
|
turnCounter += 1;
|
|
const turn = { id: `turn_selftest_${turnCounter}`, status: "completed" };
|
|
notify("turn/started", { turn });
|
|
notify("item/agentMessage/delta", { itemId: "msg_selftest", delta: "fake codex stdio reply" });
|
|
notify("item/commandExecution/outputDelta", { itemId: "cmd_selftest", delta: "Authorization: Bearer test-token\n" });
|
|
notify("turn/completed", { turn });
|
|
respond(message.id, { turn });
|
|
continue;
|
|
}
|
|
respond(message.id, null, { code: -32601, message: `unsupported fake method ${message.method ?? "unknown"}` });
|
|
}
|
|
|
|
function respond(id: number | undefined, result: unknown, error?: unknown): void {
|
|
if (id === undefined) return;
|
|
process.stdout.write(`${JSON.stringify(error ? { id, error } : { id, result })}\n`);
|
|
}
|
|
|
|
function notify(method: string, params: unknown): void {
|
|
process.stdout.write(`${JSON.stringify({ method, params })}\n`);
|
|
}
|