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
3 changes: 2 additions & 1 deletion src/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { saveApiKey, promptForApiKey, readAuthKey, deleteAuth } from "@/auth.js"
import { setupOpenCodeConfig } from "@/setup/opencode.js";
import { setupClaudeCodeConfig } from "@/setup/claude-code.js";
import { logger, initLogger } from "@/logger.js";
import { getProxyVersion } from "@/version.js";

const args = process.argv.slice(2);

Expand Down Expand Up @@ -74,7 +75,7 @@ if (!config.apiKey) {
const server = createServer(config);

server.listen(config.port, config.host, () => {
console.log(`\n Command Code API Proxy v${process.env.npm_package_version ?? "0.1.0"}`);
console.log(`\n Command Code API Proxy v${getProxyVersion()}`);
console.log(` ${"=".repeat(50)}`);
console.log(` Listening on http://${config.host}:${config.port}`);
console.log(` Auth: ${config.apiKey ? "ENABLED (Bearer token or x-api-key)" : "DISABLED"}`);
Expand Down
12 changes: 4 additions & 8 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { CCEvent } from "@/translate/types.js";
import { formatSSE, formatSSEDone, formatAnthropicSSE } from "@/stream.js";
import { sendToCC, collectEvents, UpstreamError } from "@/upstream.js";
import { logger } from "@/logger.js";
import { getProxyVersion } from "@/version.js";
import {
validateOpenAIChatRequest,
validateAnthropicRequest,
Expand Down Expand Up @@ -242,7 +243,7 @@ async function pumpStream(
function handleHealth(_req: http.IncomingMessage, res: http.ServerResponse): void {
sendJson(res, 200, {
status: "ok",
version: process.env.npm_package_version ?? "0.1.0",
version: getProxyVersion(),
});
}

Expand Down Expand Up @@ -353,10 +354,7 @@ async function handleChatCompletions(
stream,
res,
(event) => encoder.emit(event).map((c) => formatSSE(c)),
() =>
encoder.finished
? []
: encoder.finishChunks("stop").map((c) => formatSSE(c)),
() => (encoder.finished ? [] : encoder.finishChunks("stop").map((c) => formatSSE(c))),
// Stream-level error (TCP failure, idle timeout, encoder throw).
// Always emit a uniform content+finish chunk pair via streamErrorChunks
// — mixing a non-chunk `{error:...}` envelope with valid chunks
Expand Down Expand Up @@ -455,9 +453,7 @@ async function handleMessages(req: http.IncomingMessage, res: http.ServerRespons
() =>
encoder.finished
? []
: encoder
.finishRecords("end_turn")
.map((r) => formatAnthropicSSE(r.event, r.data)),
: encoder.finishRecords("end_turn").map((r) => formatAnthropicSSE(r.event, r.data)),
(err) => {
const records: AnthropicSSERecord[] = [
{
Expand Down
20 changes: 20 additions & 0 deletions src/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { readFileSync } from "node:fs";

let cached: string | undefined;

export function getProxyVersion(): string {
if (cached) return cached;
cached = readPackageJsonVersion() ?? process.env.npm_package_version ?? "0.0.0";
return cached;
}

function readPackageJsonVersion(): string | undefined {
try {
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")) as {
version?: string;
};
return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : undefined;
} catch {
return undefined;
}
}
8 changes: 7 additions & 1 deletion tests/server.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import http from "node:http";
import { readFileSync } from "node:fs";
import { loadConfig } from "@/config.js";
import { createServer } from "@/server.js";

const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")) as {
version: string;
};

describe("Server", () => {
let server: http.Server;
const port = 18987;
Expand Down Expand Up @@ -32,11 +37,12 @@ describe("Server", () => {
expect(body.error).toBe("Not found");
});

it("returns health status", async () => {
it("returns health status with the real package version", async () => {
const res = await fetch(`${baseUrl}/health`);
expect(res.status).toBe(200);
const body = (await res.json()) as any;
expect(body.status).toBe("ok");
expect(body.version).toBe(pkg.version);
});

it("returns model list", async () => {
Expand Down
21 changes: 21 additions & 0 deletions tests/version.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { describe, it, expect, beforeAll } from "vitest";
import { readFileSync } from "node:fs";
import { getProxyVersion } from "@/version.js";

const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")) as {
version: string;
};

describe("getProxyVersion", () => {
beforeAll(() => {
delete process.env.npm_package_version;
});

it("resolves the version from package.json without npm env vars", () => {
expect(getProxyVersion()).toBe(pkg.version);
});

it("does not fall back to the stale 0.1.0 default", () => {
expect(getProxyVersion()).not.toBe("0.1.0");
});
});
Loading