diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3917e56..45733fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,4 +33,3 @@ jobs: - name: Build run: pnpm build - diff --git a/README.md b/README.md index f3ffb27..65b8747 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Equivalent env vars (lower priority than CLI flags): | `CC_API_BASE` | Upstream API base URL | | `CC_CLI_VERSION` | CLI version sent upstream | | `CC_UPSTREAM_TIMEOUT_MS` | Max ms for upstream to return response headers + first byte (default `600000` / 10 min). Bump for slow reasoning models | -| `CC_IDLE_TIMEOUT_MS` | Max ms between consecutive stream chunks (default `120000` / 2 min). `0` disables — detects stalled upstreams | +| `CC_IDLE_TIMEOUT_MS` | Max ms between consecutive stream chunks (default `120000` / 2 min). `0` disables — detects stalled upstreams | | `LOG_LEVEL` | Log level (`info`, `debug`, etc.) | | `CORS_ORIGIN` | `Access-Control-Allow-Origin` value. `*` by default; empty string disables CORS. Restrict before exposing on a network. | diff --git a/src/translate/openai.ts b/src/translate/openai.ts index 2a4c2b5..3c501a2 100644 --- a/src/translate/openai.ts +++ b/src/translate/openai.ts @@ -242,7 +242,10 @@ export class OpenAIStreamEncoder { * a new one on first sighting. Falls back to the upstream-provided index * (if any) so we don't fight a bridge that already numbers them. */ - private resolveToolCallIndex(toolCallId: string | undefined, upstreamIndex: number | undefined): number { + private resolveToolCallIndex( + toolCallId: string | undefined, + upstreamIndex: number | undefined, + ): number { if (toolCallId) { const known = this.toolCallIdToIndex.get(toolCallId); if (known !== undefined) return known; diff --git a/src/upstream.ts b/src/upstream.ts index ec8f02c..f4a45ef 100644 --- a/src/upstream.ts +++ b/src/upstream.ts @@ -97,13 +97,7 @@ export async function sendToCC( options: UpstreamOptions, signal?: AbortSignal, ): Promise<{ stream: NodeJS.ReadableStream }> { - const { - apiBase, - apiKey, - ccVersion, - timeoutMs = 600_000, - idleTimeoutMs = 120_000, - } = options; + const { apiBase, apiKey, ccVersion, timeoutMs = 600_000, idleTimeoutMs = 120_000 } = options; const url = `${apiBase}/alpha/generate`; // CC's API is always streaming — force it on so the upstream stays a stream. @@ -249,9 +243,7 @@ function nodeReaderToStream( if (idleMs <= 0) return; disarmIdle(); idleTimer = setTimeout(() => { - const err = new Error( - `CC upstream idle timeout: no data for ${idleMs}ms`, - ); + const err = new Error(`CC upstream idle timeout: no data for ${idleMs}ms`); err.name = "IdleTimeoutError"; // Cancel the reader — pending read() will reject with this reason. const cancel = (reader as { cancel?: (reason?: unknown) => Promise }).cancel; diff --git a/tests/e2e.test.ts b/tests/e2e.test.ts index c48d6d4..5a93476 100644 --- a/tests/e2e.test.ts +++ b/tests/e2e.test.ts @@ -266,9 +266,10 @@ describe("E2E: OpenAI /v1/chat/completions", () => { } // The error text should appear as delta.content (visible to the user) // and the stream should still terminate with finish_reason:"stop". - const errorChunk = parsed.find((c) => - typeof c.choices?.[0]?.delta?.content === "string" && - c.choices[0].delta.content.includes("simulated TCP RST"), + const errorChunk = parsed.find( + (c) => + typeof c.choices?.[0]?.delta?.content === "string" && + c.choices[0].delta.content.includes("simulated TCP RST"), ); expect(errorChunk).toBeDefined(); const finishChunks = parsed.filter((c) => c.choices?.[0]?.finish_reason); diff --git a/tests/setup-opencode.test.ts b/tests/setup-opencode.test.ts index 2237b72..3ab7f0c 100644 --- a/tests/setup-opencode.test.ts +++ b/tests/setup-opencode.test.ts @@ -4,11 +4,10 @@ import fs from "node:fs"; describe("setupOpenCodeConfig", () => { let written: string | null = null; - let consoleLogSpy: ReturnType; beforeEach(() => { written = null; - consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + vi.spyOn(console, "log").mockImplementation(() => {}); vi.spyOn(fs, "existsSync").mockImplementation(() => false); vi.spyOn(fs, "mkdirSync").mockImplementation(() => undefined as unknown as string); vi.spyOn(fs, "writeFileSync").mockImplementation(((_path, data) => { diff --git a/tests/translate.test.ts b/tests/translate.test.ts index 2bcca86..36106e4 100644 --- a/tests/translate.test.ts +++ b/tests/translate.test.ts @@ -440,7 +440,10 @@ describe("buildNonStreamingResponse", () => { it("merges tool-call-delta + tool-call with the same id into one entry", () => { const events: CCEvent[] = [ { type: "start", data: {} }, - { type: "tool-call-delta", data: { toolCallId: "call_X", name: "search", arguments: '{"q":' } }, + { + type: "tool-call-delta", + data: { toolCallId: "call_X", name: "search", arguments: '{"q":' }, + }, { type: "tool-call-delta", data: { toolCallId: "call_X", arguments: '"hi"}' } }, { type: "tool-call", data: { toolCallId: "call_X", toolName: "search", input: { q: "hi" } } }, { type: "finish", data: { finishReason: "tool-call" } }, diff --git a/tests/upstream.test.ts b/tests/upstream.test.ts index d697fd2..d284a58 100644 --- a/tests/upstream.test.ts +++ b/tests/upstream.test.ts @@ -177,9 +177,7 @@ describe("sendToCC retry", () => { } bodyLines.push(JSON.stringify({ type: "finish", data: { finishReason: "stop" } }) + "\n"); - const mock = vi.fn().mockResolvedValue( - fakeResponse({ ok: true, status: 200, bodyLines }), - ); + const mock = vi.fn().mockResolvedValue(fakeResponse({ ok: true, status: 200, bodyLines })); globalThis.fetch = mock as unknown as typeof fetch; const { stream } = await sendToCC(sampleBody(), {