From 45ff8d4de04fc6e03d69e2b5eaac36423b7bb10d Mon Sep 17 00:00:00 2001 From: tkgo11 <71800574+tkgo11@users.noreply.github.com> Date: Tue, 18 Aug 2026 22:49:43 +0900 Subject: [PATCH] fix(services): use CLIProxy executable on Windows (#10371) * fix(services): use CLIProxy executable on Windows * fix(services): align Windows CLIProxy artifact path --------- Co-authored-by: tkgo11 <7.1800574e+07+tkgo11@users.noreply.github.com> --- src/lib/services/installers/cliproxy.ts | 3 +- src/lib/versionManager/binaryManager.ts | 10 ++++-- tests/unit/binaryManager.test.ts | 26 ++++++++++++++ .../cliproxy-resolve-spawn-args-6877.test.ts | 36 ++++++++++++++----- 4 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/lib/services/installers/cliproxy.ts b/src/lib/services/installers/cliproxy.ts index 64c3c01294..6ffb7e8196 100644 --- a/src/lib/services/installers/cliproxy.ts +++ b/src/lib/services/installers/cliproxy.ts @@ -101,7 +101,8 @@ export async function update(): Promise { * async file I/O is not available here. */ export function resolveSpawnArgs(port: number): SpawnArgs { - const symlinkPath = path.join(BIN_DIR, "cliproxyapi"); + const executableName = process.platform === "win32" ? "cliproxyapi.exe" : "cliproxyapi"; + const symlinkPath = path.join(BIN_DIR, executableName); fs.mkdirSync(CONFIG_DIR, { recursive: true }); const configPath = path.join(CONFIG_DIR, "config.yaml"); diff --git a/src/lib/versionManager/binaryManager.ts b/src/lib/versionManager/binaryManager.ts index eec36ab3cb..e657f08a2c 100644 --- a/src/lib/versionManager/binaryManager.ts +++ b/src/lib/versionManager/binaryManager.ts @@ -97,6 +97,10 @@ async function verifyChecksum(filePath: string, expectedSha256: string): Promise return hash.digest("hex").toLowerCase() === expectedSha256.toLowerCase(); } +function managedBinaryName(): string { + return process.platform === "win32" ? "cliproxyapi.exe" : "cliproxyapi"; +} + function findBinaryInDir(dir: string): string | null { const candidates = ["cli-proxy-api", "cli-proxy-api.exe", "CLIProxyAPI", "CLIProxyAPI.exe"]; for (const name of candidates) { @@ -161,7 +165,7 @@ export async function installVersion(version: string, dataDir?: string): Promise const binary = await downloadRelease(version, binDir); - const symlinkPath = path.join(binDir, "cliproxyapi"); + const symlinkPath = path.join(binDir, managedBinaryName()); try { await fs.unlink(symlinkPath); } catch {} @@ -176,7 +180,7 @@ export async function installVersion(version: string, dataDir?: string): Promise export async function getCurrentBinaryPath(dataDir?: string): Promise { const dir = dataDir || DEFAULT_DATA_DIR; - const symlinkPath = path.join(dir, "bin", "cliproxyapi"); + const symlinkPath = path.join(dir, "bin", managedBinaryName()); try { const real = await fs.realpath(symlinkPath); return fsSync.existsSync(/* turbopackIgnore: true */ real) ? real : null; @@ -215,7 +219,7 @@ export async function rollbackVersion(dataDir?: string): Promise const oldBinary = findBinaryInDir(path.join(binDir, `cliproxyapi-${previous}`)); if (!oldBinary) return null; - const symlinkPath = path.join(binDir, "cliproxyapi"); + const symlinkPath = path.join(binDir, managedBinaryName()); try { await fs.unlink(symlinkPath); } catch {} diff --git a/tests/unit/binaryManager.test.ts b/tests/unit/binaryManager.test.ts index dd0acba78b..16cdee8488 100644 --- a/tests/unit/binaryManager.test.ts +++ b/tests/unit/binaryManager.test.ts @@ -139,6 +139,32 @@ describe("binaryManager", () => { assert.ok(real.includes("1.0.0")); } }); + + it("writes the Windows rollback artifact at the CLIProxy spawn path", async () => { + const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform"); + Object.defineProperty(process, "platform", { value: "win32", configurable: true }); + + try { + const binDir = path.join(tmpDir, "bin"); + for (const ver of ["1.0.0", "2.0.0"]) { + const versionDir = path.join(binDir, `cliproxyapi-${ver}`); + fs.mkdirSync(versionDir, { recursive: true }); + fs.writeFileSync(path.join(versionDir, "cli-proxy-api"), `bin-${ver}`); + } + + assert.equal(await mod.rollbackVersion(tmpDir), "1.0.0"); + const { resolveSpawnArgs } = await import("../../src/lib/services/installers/cliproxy.ts"); + const spawn = resolveSpawnArgs(8317); + + assert.equal(spawn.command, path.join(binDir, "cliproxyapi.exe")); + assert.equal(fs.existsSync(spawn.command), true); + assert.equal(await mod.getCurrentBinaryPath(tmpDir), spawn.command); + } finally { + if (originalPlatformDescriptor) { + Object.defineProperty(process, "platform", originalPlatformDescriptor); + } + } + }); }); describe("removeVersion", () => { diff --git a/tests/unit/services/installers/cliproxy-resolve-spawn-args-6877.test.ts b/tests/unit/services/installers/cliproxy-resolve-spawn-args-6877.test.ts index db14e2d854..a5e9d5ae6e 100644 --- a/tests/unit/services/installers/cliproxy-resolve-spawn-args-6877.test.ts +++ b/tests/unit/services/installers/cliproxy-resolve-spawn-args-6877.test.ts @@ -51,9 +51,8 @@ describe("resolveSpawnArgs (#6877 — real filesystem)", () => { }); it("uses the --config long flag and never the -c short flag", async () => { - const { resolveSpawnArgs } = await import( - "../../../../src/lib/services/installers/cliproxy.ts" - ); + const { resolveSpawnArgs } = + await import("../../../../src/lib/services/installers/cliproxy.ts"); const port = 8317; const result = resolveSpawnArgs(port); @@ -61,13 +60,33 @@ describe("resolveSpawnArgs (#6877 — real filesystem)", () => { const configPath = path.join(dataDir, "services", "cliproxy", "config.yaml"); assert.deepEqual(result.args, ["--config", configPath]); + assert.equal( + result.command, + path.join(dataDir, "bin", process.platform === "win32" ? "cliproxyapi.exe" : "cliproxyapi") + ); assert.ok(!result.args.includes("-c"), "args must never contain the short -c flag"); }); + it("uses the .exe command name on Windows", async () => { + const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, "platform"); + Object.defineProperty(process, "platform", { value: "win32", configurable: true }); + + try { + const { resolveSpawnArgs } = + await import("../../../../src/lib/services/installers/cliproxy.ts"); + const result = resolveSpawnArgs(8317); + + assert.equal(result.command, path.join(dataDir, "bin", "cliproxyapi.exe")); + } finally { + if (originalPlatformDescriptor) { + Object.defineProperty(process, "platform", originalPlatformDescriptor); + } + } + }); + it("writes the default config.yaml template when none exists yet", async () => { - const { resolveSpawnArgs } = await import( - "../../../../src/lib/services/installers/cliproxy.ts" - ); + const { resolveSpawnArgs } = + await import("../../../../src/lib/services/installers/cliproxy.ts"); const port = 9123; const result = resolveSpawnArgs(port); @@ -81,9 +100,8 @@ describe("resolveSpawnArgs (#6877 — real filesystem)", () => { }); it("preserves a pre-existing config.yaml byte-for-byte instead of overwriting it", async () => { - const { resolveSpawnArgs } = await import( - "../../../../src/lib/services/installers/cliproxy.ts" - ); + const { resolveSpawnArgs } = + await import("../../../../src/lib/services/installers/cliproxy.ts"); const configDir = path.join(dataDir, "services", "cliproxy"); fs.mkdirSync(configDir, { recursive: true });