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>
This commit is contained in:
tkgo11
2026-08-18 22:49:43 +09:00
committed by GitHub
parent 231b16ef18
commit 45ff8d4de0
4 changed files with 62 additions and 13 deletions

View File

@@ -101,7 +101,8 @@ export async function update(): Promise<InstallResult> {
* 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");

View File

@@ -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<string | null> {
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<string | null>
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 {}

View File

@@ -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", () => {

View File

@@ -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 });