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
13 changes: 11 additions & 2 deletions src/opencode/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,20 @@ export function resolveLocalOpencodeTarget(apiUrl: string): LocalOpencodeTarget
}

function resolveWindowsOpencodeExe(): string {
// npm on Windows usually puts opencode.cmd on PATH (not opencode.exe).
// We locate the shim and derive the real exe path from its directory.
const pathEnv = process.env.PATH ?? "";
const pathEntries = pathEnv.split(path.delimiter).filter(Boolean);

// First pass: look for opencode.exe directly on PATH.
// Covers non-npm installations (install script, scoop, choco, manual download, etc.).
for (const entry of pathEntries) {
const candidateExe = path.join(entry, "opencode.exe");
if (existsSync(candidateExe)) {
return candidateExe;
}
}

// Second pass: look for opencode.cmd (npm global install).
// Derive the real exe path from the shim location.
for (const entry of pathEntries) {
const opencodeCmd = path.join(entry, "opencode.cmd");
if (!existsSync(opencodeCmd)) {
Expand Down
29 changes: 29 additions & 0 deletions tests/opencode/process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,35 @@ describe("opencode/process", () => {
}
});

it("resolves opencode.exe directly from PATH when no .cmd shim exists", () => {
if (process.platform !== "win32") {
return;
}

const originalPath = process.env.PATH;
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "opencode-telegram-bot-"));
const binDir = path.join(tempRoot, "bin");
const exePath = path.join(binDir, "opencode.exe");

try {
fs.mkdirSync(binDir, { recursive: true });
fs.writeFileSync(exePath, "", "utf8");

// Isolate PATH to only the temp dir — no npm .cmd shim on PATH
process.env.PATH = binDir;

const command = createOpencodeServeSpawnCommand({ host: "localhost", port: 4987 });
expect(command).toEqual({
command: exePath,
args: ["serve", "--port", "4987"],
windowsHide: true,
});
} finally {
process.env.PATH = originalPath;
fs.rmSync(tempRoot, { recursive: true, force: true });
}
});

it("uses resolved opencode.exe on Windows when opencode.cmd is on PATH and exe exists", () => {
if (process.platform !== "win32") {
return;
Expand Down
Loading