Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,3 @@ jobs:

- name: Build
run: pnpm build

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |

Expand Down
5 changes: 4 additions & 1 deletion src/translate/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
12 changes: 2 additions & 10 deletions src/upstream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<void> }).cancel;
Expand Down
7 changes: 4 additions & 3 deletions tests/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 1 addition & 2 deletions tests/setup-opencode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import fs from "node:fs";

describe("setupOpenCodeConfig", () => {
let written: string | null = null;
let consoleLogSpy: ReturnType<typeof vi.spyOn>;

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) => {
Expand Down
5 changes: 4 additions & 1 deletion tests/translate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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" } },
Expand Down
4 changes: 1 addition & 3 deletions tests/upstream.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(), {
Expand Down
Loading