style: configure prettier (no semicolons, trailing commas)

This commit is contained in:
Patrick Wozniak
2026-05-05 14:02:41 +02:00
parent e9853eb4a4
commit af2b316d84
14 changed files with 1247 additions and 1446 deletions
+122 -130
View File
@@ -1,4 +1,4 @@
import { createServer, type IncomingHttpHeaders, type Server } from "node:http";
import { createServer, type IncomingHttpHeaders, type Server } from "node:http"
import {
createStreamCommandCode,
@@ -8,74 +8,69 @@ import {
type CoreDependencies,
type ModelLike,
type Usage,
} from "../src/core.ts";
} from "../src/core.ts"
export function createTestEventStream(): AssistantMessageEventStreamLike {
const events: AssistantMessageEvent[] = [];
const waiters: Array<() => void> = [];
let ended = false;
const events: AssistantMessageEvent[] = []
const waiters: Array<() => void> = []
let ended = false
const wake = () => {
const waiter = waiters.shift();
if (waiter) waiter();
};
const waiter = waiters.shift()
if (waiter) waiter()
}
return {
push(event: AssistantMessageEvent) {
events.push(event);
wake();
events.push(event)
wake()
},
end() {
ended = true;
while (waiters.length > 0) wake();
ended = true
while (waiters.length > 0) wake()
},
[Symbol.asyncIterator]() {
let index = 0;
let index = 0
return {
async next(): Promise<IteratorResult<AssistantMessageEvent>> {
while (index >= events.length && !ended) {
await new Promise<void>((resolve) => waiters.push(resolve));
await new Promise<void>((resolve) => waiters.push(resolve))
}
if (index < events.length) {
const value = events[index];
index += 1;
return { done: false, value };
const value = events[index]
index += 1
return { done: false, value }
}
return { done: true, value: undefined };
return { done: true, value: undefined }
},
};
}
},
};
}
}
export async function collectEvents(
stream: AssistantMessageEventStreamLike,
timeoutMs = 2_000,
): Promise<AssistantMessageEvent[]> {
const events: AssistantMessageEvent[] = [];
const events: AssistantMessageEvent[] = []
const collect = async () => {
for await (const event of stream) {
events.push(event);
if (event.type === "done" || event.type === "error") break;
events.push(event)
if (event.type === "done" || event.type === "error") break
}
return events;
};
return events
}
return await Promise.race([
collect(),
new Promise<AssistantMessageEvent[]>((_, reject) => {
setTimeout(
() =>
reject(
new Error(
`Timed out collecting stream events after ${timeoutMs}ms`,
),
),
() => reject(new Error(`Timed out collecting stream events after ${timeoutMs}ms`)),
timeoutMs,
);
)
}),
]);
])
}
export function makeModel(overrides: Partial<ModelLike> = {}): ModelLike {
@@ -85,7 +80,7 @@ export function makeModel(overrides: Partial<ModelLike> = {}): ModelLike {
provider: "commandcode",
maxTokens: 384_000,
...overrides,
};
}
}
export function makeContext(overrides: Partial<ContextLike> = {}): ContextLike {
@@ -94,25 +89,23 @@ export function makeContext(overrides: Partial<ContextLike> = {}): ContextLike {
messages: [{ role: "user", content: "hello" }],
tools: [],
...overrides,
};
}
}
export interface TestDepsResult {
streamCommandCode: ReturnType<typeof createStreamCommandCode>;
calculatedUsages: Usage[];
streamCommandCode: ReturnType<typeof createStreamCommandCode>
calculatedUsages: Usage[]
}
export function createTestDeps(
overrides: Partial<CoreDependencies> = {},
): TestDepsResult {
const calculatedUsages: Usage[] = [];
export function createTestDeps(overrides: Partial<CoreDependencies> = {}): TestDepsResult {
const calculatedUsages: Usage[] = []
const streamCommandCode = createStreamCommandCode({
createStream: createTestEventStream,
calculateCost: (_model, usage) => {
calculatedUsages.push({
...usage,
cost: { ...usage.cost },
});
})
},
env: {},
authPaths: [],
@@ -120,162 +113,161 @@ export function createTestDeps(
uuid: () => "00000000-0000-4000-8000-000000000000",
cwd: () => "/repo",
...overrides,
});
return { streamCommandCode, calculatedUsages };
})
return { streamCommandCode, calculatedUsages }
}
type SuccessPlan = {
type: "success";
status?: number;
events?: string[];
chunks?: string[];
delays?: number[];
hangAfterLast?: boolean;
};
type: "success"
status?: number
events?: string[]
chunks?: string[]
delays?: number[]
hangAfterLast?: boolean
}
type ErrorPlan = {
type: "error";
status: number;
body: string;
};
type: "error"
status: number
body: string
}
export type ResponsePlan = SuccessPlan | ErrorPlan;
export type ResponsePlan = SuccessPlan | ErrorPlan
function headersToRecord(headers: IncomingHttpHeaders): Record<string, string> {
const out: Record<string, string> = {};
const out: Record<string, string> = {}
for (const [key, value] of Object.entries(headers)) {
if (typeof value === "string") out[key] = value;
else if (Array.isArray(value)) out[key] = value.join(", ");
if (typeof value === "string") out[key] = value
else if (Array.isArray(value)) out[key] = value.join(", ")
}
return out;
return out
}
export interface MockCommandCodeServer {
baseUrl(): string;
mockResponse(plan: ResponsePlan): void;
reset(): void;
close(): Promise<void>;
lastRequestBody(): unknown;
lastRequestHeaders(): Record<string, string>;
requestCount(): number;
responseClosedBeforeEnd(): boolean;
baseUrl(): string
mockResponse(plan: ResponsePlan): void
reset(): void
close(): Promise<void>
lastRequestBody(): unknown
lastRequestHeaders(): Record<string, string>
requestCount(): number
responseClosedBeforeEnd(): boolean
}
export async function startMockCommandCodeServer(): Promise<MockCommandCodeServer> {
let nextPlan: ResponsePlan = { type: "success", events: [] };
let lastBody: unknown;
let lastHeaders: Record<string, string> = {};
let requests = 0;
let closedBeforeEnd = false;
let port = 0;
let nextPlan: ResponsePlan = { type: "success", events: [] }
let lastBody: unknown
let lastHeaders: Record<string, string> = {}
let requests = 0
let closedBeforeEnd = false
let port = 0
const server: Server = createServer((req, res) => {
if (req.method !== "POST" || req.url !== "/alpha/generate") {
res.writeHead(404);
res.end("Not found");
return;
res.writeHead(404)
res.end("Not found")
return
}
requests += 1;
lastHeaders = headersToRecord(req.headers);
let body = "";
requests += 1
lastHeaders = headersToRecord(req.headers)
let body = ""
req.on("data", (chunk: Buffer) => {
body += chunk.toString("utf-8");
});
body += chunk.toString("utf-8")
})
req.on("end", () => {
try {
const parsed: unknown = JSON.parse(body);
lastBody = parsed;
const parsed: unknown = JSON.parse(body)
lastBody = parsed
} catch {
lastBody = undefined;
lastBody = undefined
}
const plan = nextPlan;
const plan = nextPlan
if (plan.type === "error") {
res.writeHead(plan.status, { "Content-Type": "text/plain" });
res.end(plan.body);
return;
res.writeHead(plan.status, { "Content-Type": "text/plain" })
res.end(plan.body)
return
}
res.writeHead(plan.status ?? 200, {
"Content-Type": "text/plain; charset=utf-8",
"Transfer-Encoding": "chunked",
});
})
let ended = false;
let ended = false
res.on("close", () => {
if (!ended) closedBeforeEnd = true;
});
if (!ended) closedBeforeEnd = true
})
const chunks =
plan.chunks ?? (plan.events ?? []).map((event) => `${event}\n`);
const delays = plan.delays ?? chunks.map(() => 0);
let index = 0;
const chunks = plan.chunks ?? (plan.events ?? []).map((event) => `${event}\n`)
const delays = plan.delays ?? chunks.map(() => 0)
let index = 0
const sendNext = () => {
if (index >= chunks.length) {
if (!plan.hangAfterLast) {
ended = true;
res.end();
ended = true
res.end()
}
return;
return
}
res.write(chunks[index]);
index += 1;
res.write(chunks[index])
index += 1
if (index < chunks.length) {
setTimeout(sendNext, delays[index] ?? 0);
setTimeout(sendNext, delays[index] ?? 0)
} else if (!plan.hangAfterLast) {
ended = true;
res.end();
ended = true
res.end()
}
};
}
sendNext();
});
});
sendNext()
})
})
await new Promise<void>((resolve) => {
server.listen(0, () => {
const address = server.address();
if (typeof address === "object" && address) port = address.port;
resolve();
});
});
const address = server.address()
if (typeof address === "object" && address) port = address.port
resolve()
})
})
return {
baseUrl: () => `http://127.0.0.1:${port}`,
mockResponse(plan: ResponsePlan) {
nextPlan = plan;
nextPlan = plan
},
reset() {
nextPlan = { type: "success", events: [] };
lastBody = undefined;
lastHeaders = {};
requests = 0;
closedBeforeEnd = false;
nextPlan = { type: "success", events: [] }
lastBody = undefined
lastHeaders = {}
requests = 0
closedBeforeEnd = false
},
close() {
return new Promise<void>((resolve) => server.close(() => resolve()));
return new Promise<void>((resolve) => server.close(() => resolve()))
},
lastRequestBody: () => lastBody,
lastRequestHeaders: () => lastHeaders,
requestCount: () => requests,
responseClosedBeforeEnd: () => closedBeforeEnd,
};
}
}
export function objectAt(value: unknown, path: readonly string[]): unknown {
let current = value;
let current = value
for (const key of path) {
if (Array.isArray(current)) {
const index = Number(key);
if (!Number.isInteger(index)) return undefined;
current = current[index];
continue;
const index = Number(key)
if (!Number.isInteger(index)) return undefined
current = current[index]
continue
}
if (typeof current !== "object" || current === null) return undefined;
current = Object.getOwnPropertyDescriptor(current, key)?.value;
if (typeof current !== "object" || current === null) return undefined
current = Object.getOwnPropertyDescriptor(current, key)?.value
}
return current;
return current
}
+38 -41
View File
@@ -2,8 +2,8 @@
* Abort tests against the real streamCommandCode core.
*/
import assert from "node:assert/strict";
import { after, before, beforeEach, describe, it } from "node:test";
import assert from "node:assert/strict"
import { after, before, beforeEach, describe, it } from "node:test"
import {
collectEvents,
@@ -12,77 +12,74 @@ import {
makeModel,
startMockCommandCodeServer,
type MockCommandCodeServer,
} from "./helpers.ts";
} from "./helpers.ts"
let server: MockCommandCodeServer;
let server: MockCommandCodeServer
before(async () => {
server = await startMockCommandCodeServer();
});
server = await startMockCommandCodeServer()
})
after(async () => {
await server.close();
});
await server.close()
})
beforeEach(() => {
server.reset();
});
server.reset()
})
describe("streamCommandCode — abort behavior", () => {
it("emits aborted error when signal is already aborted", async () => {
const controller = new AbortController();
controller.abort();
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() });
const controller = new AbortController()
controller.abort()
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const events = await collectEvents(
streamCommandCode(makeModel(), makeContext(), {
apiKey: "mock-key",
signal: controller.signal,
}),
);
)
assert.deepEqual(
events.map((event) => event.type),
["start", "error"],
);
const error = events.at(-1);
assert.equal(error?.type, "error");
if (error?.type !== "error") throw new Error("expected error");
assert.equal(error.reason, "aborted");
assert.equal(error.error.stopReason, "aborted");
assert.equal(server.requestCount(), 0);
});
)
const error = events.at(-1)
assert.equal(error?.type, "error")
if (error?.type !== "error") throw new Error("expected error")
assert.equal(error.reason, "aborted")
assert.equal(error.error.stopReason, "aborted")
assert.equal(server.requestCount(), 0)
})
it("emits aborted error and cancels the response reader mid-stream", async () => {
server.mockResponse({
type: "success",
events: [JSON.stringify({ type: "text-delta", text: "first" })],
hangAfterLast: true,
});
const controller = new AbortController();
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() });
})
const controller = new AbortController()
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const stream = streamCommandCode(makeModel(), makeContext(), {
apiKey: "mock-key",
signal: controller.signal,
});
})
setTimeout(() => controller.abort(), 50);
const events = await collectEvents(stream, 2_000);
setTimeout(() => controller.abort(), 50)
const events = await collectEvents(stream, 2_000)
assert.ok(
events.some((event) => event.type === "text_delta"),
"stream should process data before abort",
);
const error = events.at(-1);
assert.equal(error?.type, "error");
if (error?.type !== "error") throw new Error("expected error");
assert.equal(error.reason, "aborted");
assert.equal(error.error.errorMessage, "Request aborted");
await new Promise((resolve) => setTimeout(resolve, 50));
assert.ok(
server.responseClosedBeforeEnd(),
"abort should close the hanging upstream response",
);
});
});
)
const error = events.at(-1)
assert.equal(error?.type, "error")
if (error?.type !== "error") throw new Error("expected error")
assert.equal(error.reason, "aborted")
assert.equal(error.error.errorMessage, "Request aborted")
await new Promise((resolve) => setTimeout(resolve, 50))
assert.ok(server.responseClosedBeforeEnd(), "abort should close the hanging upstream response")
})
})
+95 -96
View File
@@ -5,15 +5,15 @@
* integration functions (src/oauth.ts).
*/
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import assert from "node:assert/strict"
import { describe, it } from "node:test"
import { startAuthServer, type AuthCallback } from "../src/auth-server.ts";
import { getApiKey, login, refreshToken } from "../src/oauth.ts";
import { startAuthServer, type AuthCallback } from "../src/auth-server.ts"
import { getApiKey, login, refreshToken } from "../src/oauth.ts"
describe("startAuthServer()", () => {
it("starts on a random port and accepts a valid callback POST", async () => {
const { server, port, waitForCallback } = await startAuthServer();
const { server, port, waitForCallback } = await startAuthServer()
const callbackData: AuthCallback = {
apiKey: "user_testKey123",
@@ -21,7 +21,7 @@ describe("startAuthServer()", () => {
userId: "user_123",
userName: "Test User",
keyName: "test-key",
};
}
// Simulate the Command Code Studio posting the API key back
const response = await fetch(`http://127.0.0.1:${port}/callback`, {
@@ -31,25 +31,28 @@ describe("startAuthServer()", () => {
Origin: "https://commandcode.ai",
},
body: JSON.stringify(callbackData),
});
})
assert.equal(response.status, 200);
const body = (await response.json()) as { success: boolean };
assert.equal(body.success, true);
assert.equal(response.status, 200)
const body = (await response.json()) as { success: boolean }
assert.equal(body.success, true)
const result = await waitForCallback;
assert.equal(result.apiKey, "user_testKey123");
assert.equal(result.state, "test-state-token");
assert.equal(result.userId, "user_123");
assert.equal(result.userName, "Test User");
assert.equal(result.keyName, "test-key");
const result = await waitForCallback
assert.equal(result.apiKey, "user_testKey123")
assert.equal(result.state, "test-state-token")
assert.equal(result.userId, "user_123")
assert.equal(result.userName, "Test User")
assert.equal(result.keyName, "test-key")
// Server should close after successful callback
await new Promise((resolve) => server.on("close", resolve));
});
// Server closes itself after callback; ensure it's done
await new Promise((resolve) => {
if (!server.listening) return resolve(undefined)
server.on("close", resolve)
})
})
it("rejects when the callback indicates access_denied", async () => {
const { server, port, waitForCallback } = await startAuthServer();
const { server, port, waitForCallback } = await startAuthServer()
const response = await fetch(`http://127.0.0.1:${port}/callback`, {
method: "POST",
@@ -61,17 +64,20 @@ describe("startAuthServer()", () => {
error: "access_denied",
error_description: "User cancelled",
}),
});
})
assert.equal(response.status, 200);
assert.equal(response.status, 200)
await assert.rejects(() => waitForCallback, /User cancelled/);
await assert.rejects(() => waitForCallback, /User cancelled/)
await new Promise((resolve) => server.on("close", resolve));
});
await new Promise((resolve) => {
if (!server.listening) return resolve(undefined)
server.on("close", resolve)
})
})
it("returns 400 for missing required fields", async () => {
const { server, port } = await startAuthServer();
const { server, port } = await startAuthServer()
const response = await fetch(`http://127.0.0.1:${port}/callback`, {
method: "POST",
@@ -80,57 +86,57 @@ describe("startAuthServer()", () => {
Origin: "https://commandcode.ai",
},
body: JSON.stringify({ apiKey: "key", state: "s" }),
});
})
assert.equal(response.status, 400);
assert.equal(response.status, 400)
await new Promise((resolve) => setTimeout(resolve, 100));
server.close();
});
await new Promise((resolve) => setTimeout(resolve, 100))
server.close()
})
it("handles CORS preflight OPTIONS request", async () => {
const { server, port } = await startAuthServer();
const { server, port } = await startAuthServer()
const response = await fetch(`http://127.0.0.1:${port}/callback`, {
method: "OPTIONS",
headers: { Origin: "https://commandcode.ai" },
});
})
assert.equal(response.status, 204);
assert.equal(response.status, 204)
await new Promise((resolve) => setTimeout(resolve, 100));
server.close();
});
await new Promise((resolve) => setTimeout(resolve, 100))
server.close()
})
it("returns 404 for non-callback paths", async () => {
const { server, port } = await startAuthServer();
const { server, port } = await startAuthServer()
const response = await fetch(`http://127.0.0.1:${port}/other`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: "{}",
});
})
assert.equal(response.status, 404);
assert.equal(response.status, 404)
await new Promise((resolve) => setTimeout(resolve, 100));
server.close();
});
await new Promise((resolve) => setTimeout(resolve, 100))
server.close()
})
it("returns 405 for GET on /callback", async () => {
const { server, port } = await startAuthServer();
const { server, port } = await startAuthServer()
const response = await fetch(`http://127.0.0.1:${port}/callback`, {
method: "GET",
headers: { Origin: "https://commandcode.ai" },
});
})
assert.equal(response.status, 405);
assert.equal(response.status, 405)
await new Promise((resolve) => setTimeout(resolve, 100));
server.close();
});
});
await new Promise((resolve) => setTimeout(resolve, 100))
server.close()
})
})
describe("OAuth functions", () => {
it("getApiKey returns the access token", () => {
@@ -138,53 +144,51 @@ describe("OAuth functions", () => {
refresh: "refresh-key",
access: "access-key",
expires: Date.now() + 3600000,
};
assert.equal(getApiKey(creds), "access-key");
});
}
assert.equal(getApiKey(creds), "access-key")
})
it("refreshToken returns updated far-future expiry", async () => {
const creds = {
refresh: "my-api-key",
access: "my-api-key",
expires: Date.now() - 1000, // already expired
};
const result = await refreshToken(creds);
assert.equal(result.access, "my-api-key");
assert.equal(result.refresh, "my-api-key");
assert.ok(result.expires > Date.now(), "expiry should be in the future");
});
});
}
const result = await refreshToken(creds)
assert.equal(result.access, "my-api-key")
assert.equal(result.refresh, "my-api-key")
assert.ok(result.expires > Date.now(), "expiry should be in the future")
})
})
describe("login()", () => {
it("completes the full browser login flow via the local server", async () => {
let authUrl = "";
let authUrl = ""
const callbacks = {
onAuth(params: { url: string }) {
authUrl = params.url;
authUrl = params.url
},
onPrompt(params: { message: string }): Promise<string> {
throw new Error("onPrompt should not be called in browser flow");
throw new Error("onPrompt should not be called in browser flow")
},
};
}
// Start login in the background
const loginPromise = login(callbacks);
const loginPromise = login(callbacks)
// Verify the auth URL was passed to callbacks
assert.match(
authUrl,
/^https:\/\/commandcode\.ai\/studio\/auth\/cli\?callback=http:\/\/localhost:\d+\/callback&state=/,
);
)
// Extract port and state from the URL
const url = new URL(authUrl);
const port = parseInt(
url.searchParams.get("callback")?.match(/localhost:(\d+)/)?.[1] ?? "0",
);
const state = url.searchParams.get("state") ?? "";
const url = new URL(authUrl)
const port = parseInt(url.searchParams.get("callback")?.match(/localhost:(\d+)/)?.[1] ?? "0")
const state = url.searchParams.get("state") ?? ""
assert.ok(port > 0, "auth server should be on a non-zero port");
assert.ok(state.length > 0, "state token should not be empty");
assert.ok(port > 0, "auth server should be on a non-zero port")
assert.ok(state.length > 0, "state token should not be empty")
// Simulate the Command Code Studio posting the API key back
const response = await fetch(`http://127.0.0.1:${port}/callback`, {
@@ -200,36 +204,31 @@ describe("login()", () => {
userName: "Browser User",
keyName: "browser-key",
}),
});
})
assert.equal(response.status, 200);
assert.equal(response.status, 200)
const result = await loginPromise;
assert.equal(result.access, "user_browserApiKey");
assert.equal(result.refresh, "user_browserApiKey");
assert.ok(
result.expires > Date.now(),
"expiry should be far in the future",
);
});
const result = await loginPromise
assert.equal(result.access, "user_browserApiKey")
assert.equal(result.refresh, "user_browserApiKey")
assert.ok(result.expires > Date.now(), "expiry should be far in the future")
})
it("rejects on state token mismatch", async () => {
let authUrl = "";
let authUrl = ""
const callbacks = {
onAuth(params: { url: string }) {
authUrl = params.url;
authUrl = params.url
},
onPrompt(params: { message: string }): Promise<string> {
throw new Error("should not prompt");
throw new Error("should not prompt")
},
};
}
const loginPromise = login(callbacks);
const loginPromise = login(callbacks)
const url = new URL(authUrl);
const port = parseInt(
url.searchParams.get("callback")?.match(/localhost:(\d+)/)?.[1] ?? "0",
);
const url = new URL(authUrl)
const port = parseInt(url.searchParams.get("callback")?.match(/localhost:(\d+)/)?.[1] ?? "0")
// Post back with a wrong state token
await fetch(`http://127.0.0.1:${port}/callback`, {
@@ -245,8 +244,8 @@ describe("login()", () => {
userName: "Attacker",
keyName: "evil-key",
}),
});
})
await assert.rejects(() => loginPromise, /State token mismatch/);
});
});
await assert.rejects(() => loginPromise, /State token mismatch/)
})
})
+137 -167
View File
@@ -4,8 +4,8 @@
* Command Code API is replaced by a deterministic local mock server.
*/
import assert from "node:assert/strict";
import { spawn, spawnSync } from "node:child_process";
import assert from "node:assert/strict"
import { spawn, spawnSync } from "node:child_process"
import {
accessSync,
constants,
@@ -14,123 +14,118 @@ import {
mkdtempSync,
rmSync,
writeFileSync,
} from "node:fs";
import { createServer } from "node:http";
import { homedir, tmpdir } from "node:os";
import { delimiter, dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
} from "node:fs"
import { createServer } from "node:http"
import { homedir, tmpdir } from "node:os"
import { delimiter, dirname, join, resolve } from "node:path"
import { fileURLToPath } from "node:url"
const __dirname = dirname(fileURLToPath(import.meta.url));
const PROJECT_DIR = resolve(__dirname, "..");
const EXT_PATH = resolve(PROJECT_DIR, "index.ts");
const TEST_MODEL = "deepseek/deepseek-v4-flash";
const __dirname = dirname(fileURLToPath(import.meta.url))
const PROJECT_DIR = resolve(__dirname, "..")
const EXT_PATH = resolve(PROJECT_DIR, "index.ts")
const TEST_MODEL = "deepseek/deepseek-v4-flash"
function findPiBinary() {
if (process.env.PI_BIN) return process.env.PI_BIN;
const localBin = resolve(PROJECT_DIR, "node_modules", ".bin");
if (process.env.PI_BIN) return process.env.PI_BIN
const localBin = resolve(PROJECT_DIR, "node_modules", ".bin")
const candidates = (process.env.PATH ?? "")
.split(delimiter)
.map((entry) => resolve(entry, "pi"))
.filter((candidate) => !candidate.startsWith(localBin));
.filter((candidate) => !candidate.startsWith(localBin))
for (const candidate of candidates) {
try {
accessSync(candidate, constants.X_OK);
return candidate;
accessSync(candidate, constants.X_OK)
return candidate
} catch {
// Try next PATH entry.
}
}
return undefined;
return undefined
}
const PI_BIN = findPiBinary();
const PI_BIN = findPiBinary()
if (!PI_BIN) {
console.log("[pi-local] SKIP — pi is not on PATH");
process.exit(0);
console.log("[pi-local] SKIP — pi is not on PATH")
process.exit(0)
}
const piCheck = spawnSync(PI_BIN, ["--help"], { stdio: "ignore" });
const piCheck = spawnSync(PI_BIN, ["--help"], { stdio: "ignore" })
if (piCheck.error) {
console.log(`[pi-local] SKIP — pi failed to start: ${piCheck.error.message}`);
process.exit(0);
console.log(`[pi-local] SKIP — pi failed to start: ${piCheck.error.message}`)
process.exit(0)
}
let requestCount = 0;
let lastRequestBody;
let lastRequestHeaders = {};
let requestCount = 0
let lastRequestBody
let lastRequestHeaders = {}
const server = createServer((req, res) => {
if (req.method !== "POST" || req.url !== "/alpha/generate") {
res.writeHead(404);
res.end("Not found");
return;
res.writeHead(404)
res.end("Not found")
return
}
requestCount += 1;
requestCount += 1
lastRequestHeaders = Object.fromEntries(
Object.entries(req.headers).map(([key, value]) => [
key,
Array.isArray(value) ? value.join(", ") : (value ?? ""),
]),
);
)
let body = "";
let body = ""
req.on("data", (chunk) => {
body += chunk.toString("utf-8");
});
body += chunk.toString("utf-8")
})
req.on("end", () => {
try {
lastRequestBody = JSON.parse(body);
lastRequestBody = JSON.parse(body)
} catch {
lastRequestBody = undefined;
lastRequestBody = undefined
}
res.writeHead(200, {
"Content-Type": "text/plain; charset=utf-8",
"Transfer-Encoding": "chunked",
});
res.write(
`${JSON.stringify({ type: "text-delta", text: "mock-pi-ok" })}\n`,
);
})
res.write(`${JSON.stringify({ type: "text-delta", text: "mock-pi-ok" })}\n`)
res.write(
`${JSON.stringify({ type: "finish", finishReason: "stop", totalUsage: { inputTokens: 1, outputTokens: 1 } })}\n`,
);
res.end();
});
});
)
res.end()
})
})
await new Promise((resolve) => server.listen(0, resolve));
const address = server.address();
const port = typeof address === "object" && address ? address.port : 0;
const apiBase = `http://127.0.0.1:${port}`;
await new Promise((resolve) => server.listen(0, resolve))
const address = server.address()
const port = typeof address === "object" && address ? address.port : 0
const apiBase = `http://127.0.0.1:${port}`
function hasLivePiAuth() {
return (
!!process.env.COMMANDCODE_API_KEY ||
existsSync(join(homedir(), ".commandcode", "auth.json")) ||
existsSync(join(homedir(), ".pi", "agent", "auth.json"))
);
)
}
let tempHome;
let tempHome
const env = {
...process.env,
COMMANDCODE_API_BASE: apiBase,
};
}
if (hasLivePiAuth()) {
console.log("[pi-local] using live pi auth");
console.log("[pi-local] using live pi auth")
} else {
console.log("[pi-local] live pi auth not found; using mock auth fallback");
tempHome = mkdtempSync(join(tmpdir(), "pi-cc-home-"));
mkdirSync(join(tempHome, ".commandcode"), { recursive: true });
writeFileSync(
join(tempHome, ".commandcode", "auth.json"),
JSON.stringify({ apiKey: "mock-key" }),
);
env.HOME = tempHome;
env.USERPROFILE = tempHome;
env.COMMANDCODE_API_KEY = "mock-key";
console.log("[pi-local] live pi auth not found; using mock auth fallback")
tempHome = mkdtempSync(join(tmpdir(), "pi-cc-home-"))
mkdirSync(join(tempHome, ".commandcode"), { recursive: true })
writeFileSync(join(tempHome, ".commandcode", "auth.json"), JSON.stringify({ apiKey: "mock-key" }))
env.HOME = tempHome
env.USERPROFILE = tempHome
env.COMMANDCODE_API_KEY = "mock-key"
}
function runPi(args, timeoutMs = 30_000) {
@@ -139,125 +134,109 @@ function runPi(args, timeoutMs = 30_000) {
cwd: PROJECT_DIR,
env,
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
})
let stdout = ""
let stderr = ""
const timer = setTimeout(() => {
child.kill();
child.kill()
resolve({
code: -1,
stdout,
stderr: `${stderr}\nTIMEOUT after ${timeoutMs}ms`,
});
}, timeoutMs);
})
}, timeoutMs)
child.stdout.on("data", (chunk) => {
stdout += chunk.toString("utf-8");
});
stdout += chunk.toString("utf-8")
})
child.stderr.on("data", (chunk) => {
stderr += chunk.toString("utf-8");
});
stderr += chunk.toString("utf-8")
})
child.on("close", (code) => {
clearTimeout(timer);
resolve({ code, stdout, stderr });
});
});
clearTimeout(timer)
resolve({ code, stdout, stderr })
})
})
}
async function runRpcQuery(timeoutMs = 30_000) {
const child = spawn(
PI_BIN,
[
"--mode",
"rpc",
"-e",
EXT_PATH,
"--provider",
"commandcode",
"--model",
TEST_MODEL,
],
["--mode", "rpc", "-e", EXT_PATH, "--provider", "commandcode", "--model", TEST_MODEL],
{
cwd: PROJECT_DIR,
env,
stdio: ["pipe", "pipe", "pipe"],
},
);
)
let stdout = "";
let stderr = "";
let buffer = "";
let sawPromptAccepted = false;
let sawAssistantMessage = false;
let sawTextDelta = false;
const events = [];
let stdout = ""
let stderr = ""
let buffer = ""
let sawPromptAccepted = false
let sawAssistantMessage = false
let sawTextDelta = false
const events = []
const done = new Promise((resolve) => {
const timer = setTimeout(() => {
child.kill();
resolve(false);
}, timeoutMs);
child.kill()
resolve(false)
}, timeoutMs)
const finish = (ok) => {
clearTimeout(timer);
clearTimeout(timer)
try {
child.stdin.write(`${JSON.stringify({ type: "quit" })}\n`);
child.stdin.write(`${JSON.stringify({ type: "quit" })}\n`)
} catch {
// ignore shutdown race
}
child.kill();
resolve(ok);
};
child.kill()
resolve(ok)
}
child.stdin.write(
`${JSON.stringify({ id: "prompt-1", type: "prompt", message: "say mock token" })}\n`,
);
)
child.stdout.on("data", (chunk) => {
const text = chunk.toString("utf-8");
stdout += text;
buffer += text;
const lines = buffer.split("\n");
buffer = lines.pop() ?? "";
const text = chunk.toString("utf-8")
stdout += text
buffer += text
const lines = buffer.split("\n")
buffer = lines.pop() ?? ""
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
const trimmed = line.trim()
if (!trimmed) continue
try {
const event = JSON.parse(trimmed);
events.push(event);
if (
event.type === "response" &&
event.id === "prompt-1" &&
event.success === true
) {
sawPromptAccepted = true;
const event = JSON.parse(trimmed)
events.push(event)
if (event.type === "response" && event.id === "prompt-1" && event.success === true) {
sawPromptAccepted = true
}
if (
event.type === "message_update" &&
event.assistantMessageEvent?.type === "text_delta"
) {
sawTextDelta = true;
sawTextDelta = true
}
if (
event.type === "message_end" &&
event.message?.role === "assistant"
) {
sawAssistantMessage = true;
finish(true);
if (event.type === "message_end" && event.message?.role === "assistant") {
sawAssistantMessage = true
finish(true)
}
} catch {
// ignore non-JSON output
}
}
});
})
child.stderr.on("data", (chunk) => {
stderr += chunk.toString("utf-8");
});
stderr += chunk.toString("utf-8")
})
child.on("close", () => {
if (!sawAssistantMessage) finish(false);
});
});
if (!sawAssistantMessage) finish(false)
})
})
const ok = await done;
const ok = await done
return {
ok,
stdout,
@@ -266,44 +245,35 @@ async function runRpcQuery(timeoutMs = 30_000) {
sawPromptAccepted,
sawAssistantMessage,
sawTextDelta,
};
}
}
try {
console.log("[pi-local] list models through real extension");
const list = await runPi(["-e", EXT_PATH, "--list-models"], 20_000);
assert.equal(list.code, 0, list.stderr);
assert.match(list.stdout, /commandcode/);
assert.match(list.stdout, /deepseek\/deepseek-v4-flash/);
console.log("[pi-local] list models through real extension")
const list = await runPi(["-e", EXT_PATH, "--list-models"], 20_000)
assert.equal(list.code, 0, list.stderr)
assert.match(list.stdout, /commandcode/)
assert.match(list.stdout, /deepseek\/deepseek-v4-flash/)
console.log("[pi-local] print mode through real extension and mock API");
requestCount = 0;
console.log("[pi-local] print mode through real extension and mock API")
requestCount = 0
const print = await runPi(
[
"-e",
EXT_PATH,
"-p",
"say mock token",
"--provider",
"commandcode",
"--model",
TEST_MODEL,
],
["-e", EXT_PATH, "-p", "say mock token", "--provider", "commandcode", "--model", TEST_MODEL],
30_000,
);
assert.equal(print.code, 0, print.stderr);
assert.match(print.stdout, /mock-pi-ok/);
assert.equal(requestCount, 1);
)
assert.equal(print.code, 0, print.stderr)
assert.match(print.stdout, /mock-pi-ok/)
assert.equal(requestCount, 1)
assert.ok(
typeof lastRequestHeaders.authorization === "string" &&
lastRequestHeaders.authorization.startsWith("Bearer "),
"should send a bearer Authorization header",
);
assert.equal(lastRequestBody?.params?.model, TEST_MODEL);
)
assert.equal(lastRequestBody?.params?.model, TEST_MODEL)
console.log("[pi-local] RPC prompt through real extension and mock API");
requestCount = 0;
const rpc = await runRpcQuery();
console.log("[pi-local] RPC prompt through real extension and mock API")
requestCount = 0
const rpc = await runRpcQuery()
assert.equal(
rpc.ok,
true,
@@ -312,14 +282,14 @@ try {
null,
2,
),
);
assert.equal(rpc.sawPromptAccepted, true);
assert.equal(rpc.sawAssistantMessage, true);
assert.equal(rpc.sawTextDelta, true);
assert.equal(requestCount, 1);
)
assert.equal(rpc.sawPromptAccepted, true)
assert.equal(rpc.sawAssistantMessage, true)
assert.equal(rpc.sawTextDelta, true)
assert.equal(requestCount, 1)
console.log("[pi-local] PASS");
console.log("[pi-local] PASS")
} finally {
await new Promise((resolve) => server.close(resolve));
if (tempHome) rmSync(tempHome, { recursive: true, force: true });
await new Promise((resolve) => server.close(resolve))
if (tempHome) rmSync(tempHome, { recursive: true, force: true })
}
+105 -128
View File
@@ -3,11 +3,11 @@
* These are hermetic: no pi runtime and no network.
*/
import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, it } from "node:test";
import assert from "node:assert/strict"
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"
import { tmpdir } from "node:os"
import { join } from "node:path"
import { describe, it } from "node:test"
import {
getApiKey,
@@ -18,26 +18,23 @@ import {
textContent,
toJsonSchema,
toolsToJson,
} from "../src/core.ts";
} from "../src/core.ts"
import { objectAt } from "./helpers.ts";
import { objectAt } from "./helpers.ts"
describe("getApiKey()", () => {
it("uses COMMANDCODE_API_KEY from provided env", () => {
assert.equal(
getApiKey({ env: { COMMANDCODE_API_KEY: "env-key" }, authPaths: [] }),
"env-key",
);
});
assert.equal(getApiKey({ env: { COMMANDCODE_API_KEY: "env-key" }, authPaths: [] }), "env-key")
})
it("reads apiKey, commandcode, and pi OAuth credential fields from explicit auth paths", () => {
const dir = mkdtempSync(join(tmpdir(), "cc-auth-"));
const dir = mkdtempSync(join(tmpdir(), "cc-auth-"))
try {
const first = join(dir, "first.json");
const second = join(dir, "second.json");
const oauth = join(dir, "oauth.json");
writeFileSync(first, JSON.stringify({ apiKey: "file-key" }));
writeFileSync(second, JSON.stringify({ commandcode: "fallback-key" }));
const first = join(dir, "first.json")
const second = join(dir, "second.json")
const oauth = join(dir, "oauth.json")
writeFileSync(first, JSON.stringify({ apiKey: "file-key" }))
writeFileSync(second, JSON.stringify({ commandcode: "fallback-key" }))
writeFileSync(
oauth,
JSON.stringify({
@@ -48,47 +45,38 @@ describe("getApiKey()", () => {
expires: Date.now() + 3600000,
},
}),
);
assert.equal(
getApiKey({ env: {}, authPaths: [first, second] }),
"file-key",
);
assert.equal(getApiKey({ env: {}, authPaths: [second] }), "fallback-key");
assert.equal(
getApiKey({ env: {}, authPaths: [oauth] }),
"oauth-access-key",
);
)
assert.equal(getApiKey({ env: {}, authPaths: [first, second] }), "file-key")
assert.equal(getApiKey({ env: {}, authPaths: [second] }), "fallback-key")
assert.equal(getApiKey({ env: {}, authPaths: [oauth] }), "oauth-access-key")
} finally {
rmSync(dir, { recursive: true, force: true });
rmSync(dir, { recursive: true, force: true })
}
});
})
it("ignores malformed auth files", () => {
const dir = mkdtempSync(join(tmpdir(), "cc-auth-bad-"));
const dir = mkdtempSync(join(tmpdir(), "cc-auth-bad-"))
try {
const bad = join(dir, "bad.json");
writeFileSync(bad, "not json");
assert.equal(getApiKey({ env: {}, authPaths: [bad] }), undefined);
const bad = join(dir, "bad.json")
writeFileSync(bad, "not json")
assert.equal(getApiKey({ env: {}, authPaths: [bad] }), undefined)
} finally {
rmSync(dir, { recursive: true, force: true });
rmSync(dir, { recursive: true, force: true })
}
});
})
it("uses injected homeDir for default auth paths", () => {
const dir = mkdtempSync(join(tmpdir(), "cc-home-"));
const dir = mkdtempSync(join(tmpdir(), "cc-home-"))
try {
const authDir = join(dir, ".pi", "agent");
mkdirSync(authDir, { recursive: true });
writeFileSync(
join(authDir, "auth.json"),
JSON.stringify({ commandcode: "pi-key" }),
);
assert.equal(getApiKey({ env: {}, homeDir: () => dir }), "pi-key");
const authDir = join(dir, ".pi", "agent")
mkdirSync(authDir, { recursive: true })
writeFileSync(join(authDir, "auth.json"), JSON.stringify({ commandcode: "pi-key" }))
assert.equal(getApiKey({ env: {}, homeDir: () => dir }), "pi-key")
} finally {
rmSync(dir, { recursive: true, force: true });
rmSync(dir, { recursive: true, force: true })
}
});
});
})
})
describe("textContent()", () => {
it("extracts and joins text blocks", () => {
@@ -101,35 +89,32 @@ describe("textContent()", () => {
],
}),
"hello\nworld",
);
});
)
})
it("handles empty or missing content", () => {
assert.equal(textContent({ content: [] }), "");
assert.equal(textContent({}), "");
});
});
assert.equal(textContent({ content: [] }), "")
assert.equal(textContent({}), "")
})
})
describe("getEnvironmentInfo()", () => {
it("returns platform, arch, and Node version", () => {
const info = getEnvironmentInfo();
assert.match(info, /^(darwin|linux|win32)-/);
assert.ok(info.includes("Node.js"));
});
});
const info = getEnvironmentInfo()
assert.match(info, /^(darwin|linux|win32)-/)
assert.ok(info.includes("Node.js"))
})
})
describe("toJsonSchema()", () => {
it("converts scalar, enum, object, optional, array, and union schema shapes", () => {
assert.deepEqual(toJsonSchema({ kind: "string" }), { type: "string" });
assert.deepEqual(toJsonSchema({ kind: "Number" }), { type: "number" });
assert.deepEqual(toJsonSchema({ kind: "boolean" }), { type: "boolean" });
assert.deepEqual(
toJsonSchema({ kind: "string", enum: ["left", "right"] }),
{
type: "string",
enum: ["left", "right"],
},
);
assert.deepEqual(toJsonSchema({ kind: "string" }), { type: "string" })
assert.deepEqual(toJsonSchema({ kind: "Number" }), { type: "number" })
assert.deepEqual(toJsonSchema({ kind: "boolean" }), { type: "boolean" })
assert.deepEqual(toJsonSchema({ kind: "string", enum: ["left", "right"] }), {
type: "string",
enum: ["left", "right"],
})
assert.deepEqual(
toJsonSchema({
kind: "object",
@@ -146,16 +131,14 @@ describe("toJsonSchema()", () => {
},
required: ["name"],
},
);
assert.deepEqual(
toJsonSchema({ kind: "optional", wrapped: { kind: "string" } }),
{ type: "string" },
);
assert.deepEqual(
toJsonSchema({ kind: "union", variants: [{}, { kind: "number" }] }),
{ type: "number" },
);
});
)
assert.deepEqual(toJsonSchema({ kind: "optional", wrapped: { kind: "string" } }), {
type: "string",
})
assert.deepEqual(toJsonSchema({ kind: "union", variants: [{}, { kind: "number" }] }), {
type: "number",
})
})
it("preserves explicit required arrays and handles unknown values", () => {
assert.deepEqual(
@@ -169,11 +152,11 @@ describe("toJsonSchema()", () => {
properties: { name: { type: "string" }, nickname: { type: "string" } },
required: ["name"],
},
);
assert.deepEqual(toJsonSchema(undefined), {});
assert.deepEqual(toJsonSchema({ kind: "wat" }), {});
});
});
)
assert.deepEqual(toJsonSchema(undefined), {})
assert.deepEqual(toJsonSchema({ kind: "wat" }), {})
})
})
describe("toolsToJson()", () => {
it("converts pi tools to Command Code tool JSON", () => {
@@ -200,13 +183,13 @@ describe("toolsToJson()", () => {
},
},
],
);
});
)
})
it("returns an empty array for missing tools", () => {
assert.deepEqual(toolsToJson(), []);
});
});
assert.deepEqual(toolsToJson(), [])
})
})
describe("messagesToCC()", () => {
it("converts user, assistant, and tool result messages", () => {
@@ -235,18 +218,15 @@ describe("messagesToCC()", () => {
{ type: "text", text: "world" },
],
},
]);
])
assert.equal(objectAt(result, ["0", "role"]), "user");
assert.equal(objectAt(result, ["1", "role"]), "assistant");
assert.equal(objectAt(result, ["1", "content", "0", "type"]), "reasoning");
assert.equal(objectAt(result, ["1", "content", "2", "type"]), "tool-call");
assert.equal(objectAt(result, ["2", "role"]), "tool");
assert.equal(
objectAt(result, ["2", "content", "0", "output", "value"]),
"hello\nworld",
);
});
assert.equal(objectAt(result, ["0", "role"]), "user")
assert.equal(objectAt(result, ["1", "role"]), "assistant")
assert.equal(objectAt(result, ["1", "content", "0", "type"]), "reasoning")
assert.equal(objectAt(result, ["1", "content", "2", "type"]), "tool-call")
assert.equal(objectAt(result, ["2", "role"]), "tool")
assert.equal(objectAt(result, ["2", "content", "0", "output", "value"]), "hello\nworld")
})
it("drops orphaned tool calls that have no matching tool result", () => {
const result = messagesToCC([
@@ -263,46 +243,43 @@ describe("messagesToCC()", () => {
},
],
},
]);
])
assert.equal(objectAt(result, ["1", "role"]), "assistant");
assert.equal(objectAt(result, ["1", "content", "0", "type"]), "text");
assert.equal(objectAt(result, ["1", "content", "1"]), undefined);
});
assert.equal(objectAt(result, ["1", "role"]), "assistant")
assert.equal(objectAt(result, ["1", "content", "0", "type"]), "text")
assert.equal(objectAt(result, ["1", "content", "1"]), undefined)
})
it("handles empty conversations", () => {
assert.deepEqual(messagesToCC([]), []);
});
});
assert.deepEqual(messagesToCC([]), [])
})
})
describe("parseStreamEventLine()", () => {
it("parses plain JSON and SSE data lines", () => {
assert.deepEqual(parseStreamEventLine('{"type":"text-delta","text":"x"}'), {
type: "text-delta",
text: "x",
});
assert.deepEqual(
parseStreamEventLine('data: {"type":"finish","finishReason":"stop"}'),
{
type: "finish",
finishReason: "stop",
},
);
});
})
assert.deepEqual(parseStreamEventLine('data: {"type":"finish","finishReason":"stop"}'), {
type: "finish",
finishReason: "stop",
})
})
it("ignores comments, event labels, done markers, and malformed JSON", () => {
assert.equal(parseStreamEventLine(":"), undefined);
assert.equal(parseStreamEventLine("event: message"), undefined);
assert.equal(parseStreamEventLine("data: [DONE]"), undefined);
assert.equal(parseStreamEventLine("not-json"), undefined);
});
});
assert.equal(parseStreamEventLine(":"), undefined)
assert.equal(parseStreamEventLine("event: message"), undefined)
assert.equal(parseStreamEventLine("data: [DONE]"), undefined)
assert.equal(parseStreamEventLine("not-json"), undefined)
})
})
describe("mapFinishReason()", () => {
it("maps provider finish reasons to pi stop reasons", () => {
assert.equal(mapFinishReason("stop"), "stop");
assert.equal(mapFinishReason("tool-calls"), "toolUse");
assert.equal(mapFinishReason("max_tokens"), "length");
assert.equal(mapFinishReason("max_output_tokens"), "length");
});
});
assert.equal(mapFinishReason("stop"), "stop")
assert.equal(mapFinishReason("tool-calls"), "toolUse")
assert.equal(mapFinishReason("max_tokens"), "length")
assert.equal(mapFinishReason("max_output_tokens"), "length")
})
})
+178 -225
View File
@@ -10,55 +10,55 @@
* Requires: pi on PATH plus COMMANDCODE_API_KEY or live pi auth files.
*/
import { spawn } from "node:child_process";
import { accessSync, constants, existsSync } from "node:fs";
import { homedir } from "node:os";
import { delimiter, resolve, dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { spawn } from "node:child_process"
import { accessSync, constants, existsSync } from "node:fs"
import { homedir } from "node:os"
import { delimiter, resolve, dirname, join } from "node:path"
import { fileURLToPath } from "node:url"
const __dirname = dirname(fileURLToPath(import.meta.url));
const PROJECT_DIR = resolve(__dirname, "..");
const EXT_PATH = resolve(PROJECT_DIR, "index.ts");
const TEST_MODEL = "deepseek/deepseek-v4-flash";
const __dirname = dirname(fileURLToPath(import.meta.url))
const PROJECT_DIR = resolve(__dirname, "..")
const EXT_PATH = resolve(PROJECT_DIR, "index.ts")
const TEST_MODEL = "deepseek/deepseek-v4-flash"
function findPiBinary() {
if (process.env.PI_BIN) return process.env.PI_BIN;
const localBin = resolve(PROJECT_DIR, "node_modules", ".bin");
if (process.env.PI_BIN) return process.env.PI_BIN
const localBin = resolve(PROJECT_DIR, "node_modules", ".bin")
const candidates = (process.env.PATH ?? "")
.split(delimiter)
.map((entry) => resolve(entry, "pi"))
.filter((candidate) => !candidate.startsWith(localBin));
.filter((candidate) => !candidate.startsWith(localBin))
for (const candidate of candidates) {
try {
accessSync(candidate, constants.X_OK);
return candidate;
accessSync(candidate, constants.X_OK)
return candidate
} catch {
// Try next PATH entry.
}
}
return undefined;
return undefined
}
const PI_BIN = findPiBinary();
const HAS_PI = !!PI_BIN;
const PI_BIN = findPiBinary()
const HAS_PI = !!PI_BIN
const PRINT_MODE_TIMEOUT = 120_000; // 2 minutes for print mode
const RPC_START_TIMEOUT = 15_000;
const RPC_QUERY_TIMEOUT = 60_000;
const PRINT_MODE_TIMEOUT = 120_000 // 2 minutes for print mode
const RPC_START_TIMEOUT = 15_000
const RPC_QUERY_TIMEOUT = 60_000
function hasCommandCodeAuth() {
return (
!!process.env.COMMANDCODE_API_KEY ||
existsSync(join(homedir(), ".commandcode", "auth.json")) ||
existsSync(join(homedir(), ".pi", "agent", "auth.json"))
);
)
}
const HAS_AUTH = hasCommandCodeAuth();
const HAS_AUTH = hasCommandCodeAuth()
let passed = 0;
let failed = 0;
let skipped = 0;
let passed = 0
let failed = 0
let skipped = 0
// -------------------------------------------------------------------------
// Helpers
@@ -66,7 +66,7 @@ let skipped = 0;
function kill(child) {
try {
child.kill();
child.kill()
} catch {
// ignore
}
@@ -78,22 +78,20 @@ function kill(child) {
async function runPrintMode() {
if (!HAS_AUTH) {
console.log(
"[smoke] SKIP — Command Code auth not found, skipping print mode test\n",
);
skipped++;
return;
console.log("[smoke] SKIP — Command Code auth not found, skipping print mode test\n")
skipped++
return
}
if (!HAS_PI) {
console.log("[smoke] SKIP — pi is not on PATH, skipping print mode test\n");
skipped++;
return;
console.log("[smoke] SKIP — pi is not on PATH, skipping print mode test\n")
skipped++
return
}
console.log(`[smoke] Running pi in print mode with extension: ${EXT_PATH}`);
console.log(`[smoke] Running pi in print mode with extension: ${EXT_PATH}`)
console.log(
`[smoke] ${PI_BIN} -e ${EXT_PATH} -p "say hi" --provider commandcode --model ${TEST_MODEL}\n`,
);
)
const child = spawn(
PI_BIN,
@@ -111,47 +109,41 @@ async function runPrintMode() {
env: { ...process.env },
stdio: ["ignore", "pipe", "pipe"],
},
);
)
let stdout = "";
let stderr = "";
let stdout = ""
let stderr = ""
child.stdout.on("data", (d) => {
stdout += d.toString();
});
stdout += d.toString()
})
child.stderr.on("data", (d) => {
stderr += d.toString();
});
stderr += d.toString()
})
const done = new Promise((resolve) => {
const timer = setTimeout(() => {
kill(child);
console.log("[smoke] TIMEOUT — pi print mode took too long");
resolve(false);
}, PRINT_MODE_TIMEOUT);
kill(child)
console.log("[smoke] TIMEOUT — pi print mode took too long")
resolve(false)
}, PRINT_MODE_TIMEOUT)
child.on("close", (code) => {
clearTimeout(timer);
clearTimeout(timer)
if (code === 0) {
console.log(
"[smoke] PASS — extension loaded and agent ran without crash",
);
console.log(
`[smoke] stdout (last 300 chars): ${stdout.slice(-300).trim()}`,
);
console.log("[smoke] PASS — extension loaded and agent ran without crash")
console.log(`[smoke] stdout (last 300 chars): ${stdout.slice(-300).trim()}`)
} else {
console.log(`[smoke] FAIL — exit code ${code}`);
console.log(
`[smoke] stderr (last 500 chars): ${stderr.slice(-500).trim()}`,
);
console.log(`[smoke] FAIL — exit code ${code}`)
console.log(`[smoke] stderr (last 500 chars): ${stderr.slice(-500).trim()}`)
}
resolve(code === 0);
});
});
resolve(code === 0)
})
})
const ok = await done;
if (ok) passed++;
else failed++;
const ok = await done
if (ok) passed++
else failed++
}
// -------------------------------------------------------------------------
@@ -160,59 +152,51 @@ async function runPrintMode() {
async function runListModels() {
if (!HAS_AUTH) {
console.log(
"[smoke] SKIP — Command Code auth not found, skipping model list test\n",
);
skipped++;
return;
console.log("[smoke] SKIP — Command Code auth not found, skipping model list test\n")
skipped++
return
}
if (!HAS_PI) {
console.log("[smoke] SKIP — pi is not on PATH, skipping model list test\n");
skipped++;
return;
console.log("[smoke] SKIP — pi is not on PATH, skipping model list test\n")
skipped++
return
}
console.log(
`[smoke] Checking that models are discoverable via pi --list-models\n`,
);
console.log(`[smoke] Checking that models are discoverable via pi --list-models\n`)
const child = spawn(PI_BIN, ["-e", EXT_PATH, "--list-models"], {
env: { ...process.env },
stdio: ["ignore", "pipe", "pipe"],
});
})
let stdout = "";
let stdout = ""
child.stdout.on("data", (d) => {
stdout += d.toString();
});
stdout += d.toString()
})
const done = new Promise((resolve) => {
const timer = setTimeout(() => {
kill(child);
console.log("[smoke] TIMEOUT — model listing took too long");
resolve(false);
}, 15_000);
kill(child)
console.log("[smoke] TIMEOUT — model listing took too long")
resolve(false)
}, 15_000)
child.on("close", (code) => {
clearTimeout(timer);
clearTimeout(timer)
if (code === 0 && stdout.includes("commandcode")) {
console.log("[smoke] PASS — commandcode provider models are listed");
console.log("[smoke] PASS — commandcode provider models are listed")
} else {
console.log(
"[smoke] FAIL — commandcode models not found or error listing",
);
console.log(
`[smoke] stdout (last 500 chars): ${stdout.slice(-500).trim()}`,
);
console.log("[smoke] FAIL — commandcode models not found or error listing")
console.log(`[smoke] stdout (last 500 chars): ${stdout.slice(-500).trim()}`)
}
resolve(code === 0 && stdout.includes("commandcode"));
});
});
resolve(code === 0 && stdout.includes("commandcode"))
})
})
const ok = await done;
if (ok) passed++;
else failed++;
const ok = await done
if (ok) passed++
else failed++
}
// -------------------------------------------------------------------------
@@ -221,98 +205,88 @@ async function runListModels() {
async function runRpcStartup() {
if (!HAS_AUTH) {
console.log(
"[smoke] SKIP — Command Code auth not found, skipping RPC startup test\n",
);
skipped++;
return;
console.log("[smoke] SKIP — Command Code auth not found, skipping RPC startup test\n")
skipped++
return
}
if (!HAS_PI) {
console.log(
"[smoke] SKIP — pi is not on PATH, skipping RPC startup test\n",
);
skipped++;
return;
console.log("[smoke] SKIP — pi is not on PATH, skipping RPC startup test\n")
skipped++
return
}
console.log(`[smoke] Testing RPC mode startup with extension\n`);
console.log(`[smoke] ${PI_BIN} --mode rpc -e ${EXT_PATH}\n`);
console.log(`[smoke] Testing RPC mode startup with extension\n`)
console.log(`[smoke] ${PI_BIN} --mode rpc -e ${EXT_PATH}\n`)
const child = spawn(PI_BIN, ["--mode", "rpc", "-e", EXT_PATH], {
env: { ...process.env },
stdio: ["pipe", "pipe", "pipe"],
});
})
let sawStateResponse = false;
let sawError = false;
const events = [];
let sawStateResponse = false
let sawError = false
const events = []
let buf = "";
let buf = ""
child.stdout.on("data", (chunk) => {
buf += chunk.toString("utf-8");
const lines = buf.split("\n");
buf = lines.pop() ?? "";
buf += chunk.toString("utf-8")
const lines = buf.split("\n")
buf = lines.pop() ?? ""
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
const trimmed = line.trim()
if (!trimmed) continue
try {
const msg = JSON.parse(trimmed);
events.push(msg);
const msg = JSON.parse(trimmed)
events.push(msg)
if (
msg.type === "response" &&
msg.id === "state-1" &&
msg.command === "get_state" &&
msg.success === true
) {
sawStateResponse = true;
console.log("[smoke] RPC received get_state response");
sawStateResponse = true
console.log("[smoke] RPC received get_state response")
}
if (msg.type === "error" || msg.type === "fatal") {
sawError = true;
console.error(
`[smoke] RPC error: ${JSON.stringify(msg).slice(0, 300)}`,
);
sawError = true
console.error(`[smoke] RPC error: ${JSON.stringify(msg).slice(0, 300)}`)
}
} catch {
// ignore non-JSON
}
}
});
})
const result = new Promise((resolve) => {
child.stdin.write(
JSON.stringify({ id: "state-1", type: "get_state" }) + "\n",
);
child.stdin.write(JSON.stringify({ id: "state-1", type: "get_state" }) + "\n")
const timer = setTimeout(async () => {
if (sawStateResponse) {
console.log("[smoke] PASS — extension loaded and RPC get_state works");
resolve(true);
console.log("[smoke] PASS — extension loaded and RPC get_state works")
resolve(true)
} else {
console.log("[smoke] FAIL — get_state response not received");
resolve(false);
console.log("[smoke] FAIL — get_state response not received")
resolve(false)
}
// Send quit
try {
child.stdin.write(JSON.stringify({ type: "quit" }) + "\n");
child.stdin.write(JSON.stringify({ type: "quit" }) + "\n")
} catch {}
kill(child);
}, RPC_START_TIMEOUT);
kill(child)
}, RPC_START_TIMEOUT)
child.on("close", (code) => {
clearTimeout(timer);
clearTimeout(timer)
if (!sawStateResponse && !sawError) {
console.log(
`[smoke] FAIL — pi exited with code ${code} before get_state response`,
);
resolve(false);
console.log(`[smoke] FAIL — pi exited with code ${code} before get_state response`)
resolve(false)
}
});
});
})
})
const ok = await result;
if (ok) passed++;
else failed++;
const ok = await result
if (ok) passed++
else failed++
}
// -------------------------------------------------------------------------
@@ -321,73 +295,58 @@ async function runRpcStartup() {
async function runRpcQuery() {
if (!HAS_AUTH) {
console.log(
"[smoke] SKIP — Command Code auth not found, skipping RPC prompt test\n",
);
skipped++;
return;
console.log("[smoke] SKIP — Command Code auth not found, skipping RPC prompt test\n")
skipped++
return
}
if (!HAS_PI) {
console.log("[smoke] SKIP — pi is not on PATH, skipping RPC prompt test\n");
skipped++;
return;
console.log("[smoke] SKIP — pi is not on PATH, skipping RPC prompt test\n")
skipped++
return
}
console.log(`[smoke] Testing RPC prompt flow\n`);
console.log(
`[smoke] pi --mode rpc -e ${EXT_PATH} → prompt "say hi" → expect response\n`,
);
console.log(`[smoke] Testing RPC prompt flow\n`)
console.log(`[smoke] pi --mode rpc -e ${EXT_PATH} → prompt "say hi" → expect response\n`)
const child = spawn(
PI_BIN,
[
"--mode",
"rpc",
"-e",
EXT_PATH,
"--provider",
"commandcode",
"--model",
TEST_MODEL,
],
["--mode", "rpc", "-e", EXT_PATH, "--provider", "commandcode", "--model", TEST_MODEL],
{
env: { ...process.env },
stdio: ["pipe", "pipe", "pipe"],
},
);
)
let sawPromptAccepted = false;
let sawAssistantMessage = false;
let sawPromptAccepted = false
let sawAssistantMessage = false
let buf = "";
let buf = ""
child.stdout.on("data", (chunk) => {
buf += chunk.toString("utf-8");
const lines = buf.split("\n");
buf = lines.pop() ?? "";
buf += chunk.toString("utf-8")
const lines = buf.split("\n")
buf = lines.pop() ?? ""
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
const trimmed = line.trim()
if (!trimmed) continue
try {
const msg = JSON.parse(trimmed);
const msg = JSON.parse(trimmed)
if (
msg.type === "response" &&
msg.id === "prompt-1" &&
msg.command === "prompt" &&
msg.success === true
) {
sawPromptAccepted = true;
sawPromptAccepted = true
}
if (msg.type === "message_end" && msg.message?.role === "assistant") {
sawAssistantMessage = true;
console.log(
"[smoke] PASS — received assistant message_end in RPC mode",
);
sawAssistantMessage = true
console.log("[smoke] PASS — received assistant message_end in RPC mode")
}
} catch {
// ignore
}
}
});
})
const result = new Promise((resolve) => {
child.stdin.write(
@@ -396,63 +355,57 @@ async function runRpcQuery() {
type: "prompt",
message: "say hi in one word",
}) + "\n",
);
console.log("[smoke] Sent RPC prompt");
)
console.log("[smoke] Sent RPC prompt")
const timer = setTimeout(() => {
if (sawPromptAccepted && sawAssistantMessage) {
console.log("[smoke] PASS — full RPC prompt/response cycle works");
resolve(true);
console.log("[smoke] PASS — full RPC prompt/response cycle works")
resolve(true)
} else {
console.log(
"[smoke] WARN — no assistant message_end received (may still be streaming)",
);
resolve(false);
console.log("[smoke] WARN — no assistant message_end received (may still be streaming)")
resolve(false)
}
try {
child.stdin.write(JSON.stringify({ type: "quit" }) + "\n");
child.stdin.write(JSON.stringify({ type: "quit" }) + "\n")
} catch {}
kill(child);
}, RPC_QUERY_TIMEOUT);
kill(child)
}, RPC_QUERY_TIMEOUT)
child.on("close", (code) => {
clearTimeout(timer);
clearTimeout(timer)
if (!sawAssistantMessage) {
console.log(`[smoke] FAIL — pi exited before assistant message_end`);
resolve(false);
console.log(`[smoke] FAIL — pi exited before assistant message_end`)
resolve(false)
}
});
});
})
})
const ok = await result;
if (ok) passed++;
else failed++;
const ok = await result
if (ok) passed++
else failed++
}
// -------------------------------------------------------------------------
// Main
// -------------------------------------------------------------------------
console.log("=".repeat(60));
console.log(" pi-commandcode-provider Integration Smoke Test");
console.log("=".repeat(60));
console.log(
` Auth: ${HAS_AUTH ? "✓ found" : "✗ not found (tests will be skipped)"}`,
);
console.log(` Extension: ${EXT_PATH}`);
console.log("=".repeat(60));
console.log("");
console.log("=".repeat(60))
console.log(" pi-commandcode-provider Integration Smoke Test")
console.log("=".repeat(60))
console.log(` Auth: ${HAS_AUTH ? "✓ found" : "✗ not found (tests will be skipped)"}`)
console.log(` Extension: ${EXT_PATH}`)
console.log("=".repeat(60))
console.log("")
await runPrintMode();
await runListModels();
await runRpcStartup();
await runRpcQuery();
await runPrintMode()
await runListModels()
await runRpcStartup()
await runRpcQuery()
console.log("");
console.log("=".repeat(60));
console.log(
` SUITE RESULT: ${passed} passed, ${failed} failed, ${skipped} skipped`,
);
console.log("=".repeat(60));
console.log("")
console.log("=".repeat(60))
console.log(` SUITE RESULT: ${passed} passed, ${failed} failed, ${skipped} skipped`)
console.log("=".repeat(60))
process.exit(failed > 0 ? 1 : 0);
process.exit(failed > 0 ? 1 : 0)
+129 -159
View File
@@ -3,10 +3,10 @@
* Command Code server. No real API key or pi runtime required.
*/
import assert from "node:assert/strict";
import { after, before, beforeEach, describe, it } from "node:test";
import assert from "node:assert/strict"
import { after, before, beforeEach, describe, it } from "node:test"
import type { AssistantMessageEvent } from "../src/core.ts";
import type { AssistantMessageEvent } from "../src/core.ts"
import {
collectEvents,
createTestDeps,
@@ -15,24 +15,24 @@ import {
objectAt,
startMockCommandCodeServer,
type MockCommandCodeServer,
} from "./helpers.ts";
} from "./helpers.ts"
let server: MockCommandCodeServer;
let server: MockCommandCodeServer
before(async () => {
server = await startMockCommandCodeServer();
});
server = await startMockCommandCodeServer()
})
after(async () => {
await server.close();
});
await server.close()
})
beforeEach(() => {
server.reset();
});
server.reset()
})
function eventTypes(events: readonly AssistantMessageEvent[]): string[] {
return events.map((event) => event.type);
return events.map((event) => event.type)
}
describe("streamCommandCode — auth", () => {
@@ -41,39 +41,34 @@ describe("streamCommandCode — auth", () => {
apiBase: server.baseUrl(),
env: {},
authPaths: [],
});
})
const stream = streamCommandCode(makeModel(), makeContext(), {
apiKey: "",
});
const events = await collectEvents(stream);
})
const events = await collectEvents(stream)
assert.deepEqual(eventTypes(events), ["error"]);
assert.equal(events[0].type, "error");
assert.equal(events[0].reason, "error");
assert.match(events[0].error.errorMessage ?? "", /No Command Code API key/);
assert.equal(server.requestCount(), 0);
});
assert.deepEqual(eventTypes(events), ["error"])
assert.equal(events[0].type, "error")
assert.equal(events[0].reason, "error")
assert.match(events[0].error.errorMessage ?? "", /No Command Code API key/)
assert.equal(server.requestCount(), 0)
})
it("uses options.apiKey in the Authorization header", async () => {
server.mockResponse({
type: "success",
events: [JSON.stringify({ type: "finish", finishReason: "stop" })],
});
})
const { streamCommandCode } = createTestDeps({
apiBase: server.baseUrl(),
env: { COMMANDCODE_API_KEY: "env-key" },
});
})
await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "option-key" }),
);
await collectEvents(streamCommandCode(makeModel(), makeContext(), { apiKey: "option-key" }))
assert.equal(
server.lastRequestHeaders().authorization,
"Bearer option-key",
);
});
});
assert.equal(server.lastRequestHeaders().authorization, "Bearer option-key")
})
})
describe("streamCommandCode — successful streams", () => {
it("emits start → text events → done and accumulates usage", async () => {
@@ -92,14 +87,14 @@ describe("streamCommandCode — successful streams", () => {
},
}),
],
});
})
const { streamCommandCode, calculatedUsages } = createTestDeps({
apiBase: server.baseUrl(),
});
})
const events = await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }),
);
)
assert.deepEqual(eventTypes(events), [
"start",
@@ -108,21 +103,19 @@ describe("streamCommandCode — successful streams", () => {
"text_delta",
"text_end",
"done",
]);
const done = events.at(-1);
assert.equal(done?.type, "done");
if (done?.type !== "done") throw new Error("expected done");
assert.equal(done.reason, "stop");
assert.equal(done.message.content[0]?.type, "text");
])
const done = events.at(-1)
assert.equal(done?.type, "done")
if (done?.type !== "done") throw new Error("expected done")
assert.equal(done.reason, "stop")
assert.equal(done.message.content[0]?.type, "text")
assert.equal(
done.message.content[0]?.type === "text"
? done.message.content[0].text
: "",
done.message.content[0]?.type === "text" ? done.message.content[0].text : "",
"Hello",
);
assert.equal(done.message.usage.totalTokens, 11);
assert.equal(calculatedUsages.length, 1);
});
)
assert.equal(done.message.usage.totalTokens, 11)
assert.equal(calculatedUsages.length, 1)
})
it("ends on finish without waiting for an open upstream connection", async () => {
server.mockResponse({
@@ -132,21 +125,18 @@ describe("streamCommandCode — successful streams", () => {
JSON.stringify({ type: "finish", finishReason: "stop" }),
],
hangAfterLast: true,
});
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() });
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const events = await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }),
500,
);
)
assert.equal(events.at(-1)?.type, "done");
await new Promise((resolve) => setTimeout(resolve, 50));
assert.ok(
server.responseClosedBeforeEnd(),
"client should cancel the still-open response body",
);
});
assert.equal(events.at(-1)?.type, "done")
await new Promise((resolve) => setTimeout(resolve, 50))
assert.ok(server.responseClosedBeforeEnd(), "client should cancel the still-open response body")
})
it("emits reasoning and tool-call blocks in order", async () => {
server.mockResponse({
@@ -163,12 +153,12 @@ describe("streamCommandCode — successful streams", () => {
}),
JSON.stringify({ type: "finish", finishReason: "tool-calls" }),
],
});
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() });
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const events = await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }),
);
)
assert.deepEqual(eventTypes(events), [
"start",
@@ -181,20 +171,17 @@ describe("streamCommandCode — successful streams", () => {
"toolcall_start",
"toolcall_end",
"done",
]);
const done = events.at(-1);
if (done?.type !== "done") throw new Error("expected done");
assert.equal(done.reason, "toolUse");
])
const done = events.at(-1)
if (done?.type !== "done") throw new Error("expected done")
assert.equal(done.reason, "toolUse")
assert.deepEqual(
done.message.content.map((content) => content.type),
["thinking", "text", "toolCall"],
);
const toolCall = done.message.content[2];
assert.equal(
toolCall?.type === "toolCall" ? toolCall.name : "",
"read_file",
);
});
)
const toolCall = done.message.content[2]
assert.equal(toolCall?.type === "toolCall" ? toolCall.name : "", "read_file")
})
it("flushes reasoning if finish arrives without reasoning-end", async () => {
server.mockResponse({
@@ -203,26 +190,26 @@ describe("streamCommandCode — successful streams", () => {
JSON.stringify({ type: "reasoning-delta", text: "unfinished thought" }),
JSON.stringify({ type: "finish", finishReason: "stop" }),
],
});
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() });
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const events = await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }),
);
)
const done = events.at(-1);
if (done?.type !== "done") throw new Error("expected done");
assert.equal(done.message.content[0]?.type, "thinking");
});
});
const done = events.at(-1)
if (done?.type !== "done") throw new Error("expected done")
assert.equal(done.message.content[0]?.type, "thinking")
})
})
describe("streamCommandCode — request serialization", () => {
it("sends the expected request body and default headers", async () => {
server.mockResponse({
type: "success",
events: [JSON.stringify({ type: "finish", finishReason: "stop" })],
});
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() });
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const context = makeContext({
messages: [
{ role: "user", content: "first" },
@@ -242,52 +229,40 @@ describe("streamCommandCode — request serialization", () => {
},
},
],
});
})
await collectEvents(
streamCommandCode(makeModel(), context, {
apiKey: "mock-key",
maxTokens: 500,
}),
);
)
const body = server.lastRequestBody();
assert.equal(objectAt(body, ["config", "workingDir"]), "/repo");
assert.equal(objectAt(body, ["config", "date"]), "2026-05-05");
assert.equal(
objectAt(body, ["params", "model"]),
"deepseek/deepseek-v4-flash",
);
assert.equal(objectAt(body, ["params", "stream"]), true);
assert.equal(objectAt(body, ["params", "max_tokens"]), 500);
assert.equal(
objectAt(body, ["params", "system"]),
"You are a test assistant.",
);
const body = server.lastRequestBody()
assert.equal(objectAt(body, ["config", "workingDir"]), "/repo")
assert.equal(objectAt(body, ["config", "date"]), "2026-05-05")
assert.equal(objectAt(body, ["params", "model"]), "deepseek/deepseek-v4-flash")
assert.equal(objectAt(body, ["params", "stream"]), true)
assert.equal(objectAt(body, ["params", "max_tokens"]), 500)
assert.equal(objectAt(body, ["params", "system"]), "You are a test assistant.")
assert.equal(
objectAt(body, ["params", "messages", "1", "content", "0", "text"]),
"first response",
);
assert.equal(
objectAt(body, ["params", "tools", "0", "name"]),
"get_weather",
);
)
assert.equal(objectAt(body, ["params", "tools", "0", "name"]), "get_weather")
const headers = server.lastRequestHeaders();
assert.equal(headers.authorization, "Bearer mock-key");
assert.equal(headers["x-command-code-version"], "0.24.1");
assert.equal(
headers["x-session-id"],
"00000000-0000-4000-8000-000000000000",
);
});
const headers = server.lastRequestHeaders()
assert.equal(headers.authorization, "Bearer mock-key")
assert.equal(headers["x-command-code-version"], "0.24.1")
assert.equal(headers["x-session-id"], "00000000-0000-4000-8000-000000000000")
})
it("caps maxTokens and passes custom headers", async () => {
server.mockResponse({
type: "success",
events: [JSON.stringify({ type: "finish", finishReason: "stop" })],
});
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() });
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
await collectEvents(
streamCommandCode(makeModel({ maxTokens: 500_000 }), makeContext(), {
@@ -295,53 +270,50 @@ describe("streamCommandCode — request serialization", () => {
maxTokens: 500_000,
headers: { "x-custom": "value" },
}),
);
)
assert.equal(
objectAt(server.lastRequestBody(), ["params", "max_tokens"]),
200_000,
);
assert.equal(server.lastRequestHeaders()["x-custom"], "value");
});
assert.equal(objectAt(server.lastRequestBody(), ["params", "max_tokens"]), 200_000)
assert.equal(server.lastRequestHeaders()["x-custom"], "value")
})
it("runs onPayload and onResponse hooks", async () => {
server.mockResponse({
type: "success",
events: [JSON.stringify({ type: "finish", finishReason: "stop" })],
});
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() });
let responseStatus = 0;
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
let responseStatus = 0
await collectEvents(
streamCommandCode(makeModel(), makeContext(), {
apiKey: "mock-key",
onPayload: () => ({ replaced: true }),
onResponse: (response) => {
responseStatus = response.status;
responseStatus = response.status
},
}),
);
)
assert.equal(objectAt(server.lastRequestBody(), ["replaced"]), true);
assert.equal(responseStatus, 200);
});
});
assert.equal(objectAt(server.lastRequestBody(), ["replaced"]), true)
assert.equal(responseStatus, 200)
})
})
describe("streamCommandCode — upstream errors and malformed streams", () => {
it("emits error for HTTP failures", async () => {
server.mockResponse({ type: "error", status: 429, body: "rate limited" });
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() });
server.mockResponse({ type: "error", status: 429, body: "rate limited" })
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const events = await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }),
);
)
assert.deepEqual(eventTypes(events), ["start", "error"]);
const error = events.at(-1);
assert.equal(error?.type, "error");
if (error?.type !== "error") throw new Error("expected error");
assert.match(error.error.errorMessage ?? "", /429/);
});
assert.deepEqual(eventTypes(events), ["start", "error"])
const error = events.at(-1)
assert.equal(error?.type, "error")
if (error?.type !== "error") throw new Error("expected error")
assert.match(error.error.errorMessage ?? "", /429/)
})
it("emits error for provider error events", async () => {
server.mockResponse({
@@ -352,25 +324,25 @@ describe("streamCommandCode — upstream errors and malformed streams", () => {
error: { message: "provider failed" },
}),
],
});
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() });
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const events = await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }),
);
)
const error = events.at(-1);
assert.equal(error?.type, "error");
if (error?.type !== "error") throw new Error("expected error");
assert.equal(error.error.errorMessage, "provider failed");
});
const error = events.at(-1)
assert.equal(error?.type, "error")
if (error?.type !== "error") throw new Error("expected error")
assert.equal(error.error.errorMessage, "provider failed")
})
it("handles SSE lines, malformed lines, split chunks, and final line without newline", async () => {
const textEvent = `data: ${JSON.stringify({ type: "text-delta", text: "split" })}\n`;
const textEvent = `data: ${JSON.stringify({ type: "text-delta", text: "split" })}\n`
const finishEvent = JSON.stringify({
type: "finish",
finishReason: "max_tokens",
});
})
server.mockResponse({
type: "success",
chunks: [
@@ -381,21 +353,19 @@ describe("streamCommandCode — upstream errors and malformed streams", () => {
"data: [DONE]\n",
finishEvent,
],
});
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() });
})
const { streamCommandCode } = createTestDeps({ apiBase: server.baseUrl() })
const events = await collectEvents(
streamCommandCode(makeModel(), makeContext(), { apiKey: "mock-key" }),
);
)
const done = events.at(-1);
if (done?.type !== "done") throw new Error("expected done");
assert.equal(done.reason, "length");
const done = events.at(-1)
if (done?.type !== "done") throw new Error("expected done")
assert.equal(done.reason, "length")
assert.equal(
done.message.content[0]?.type === "text"
? done.message.content[0].text
: "",
done.message.content[0]?.type === "text" ? done.message.content[0].text : "",
"split",
);
});
});
)
})
})