From df44b41aaa06b0a0c54a008740c11896708d9727 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 20 Mar 2026 10:33:53 -0400 Subject: [PATCH] fix: handle undefined from Npm.which in all callers Npm.which now returns undefined instead of throwing when binary not found. Update all callers to check for undefined and return early. Avoid explicit union types by using a resolved variable pattern. --- packages/opencode/src/format/formatter.ts | 8 +++-- packages/opencode/src/lsp/server.ts | 37 +++++++++++++++++------ packages/opencode/src/npm/index.ts | 2 +- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/packages/opencode/src/format/formatter.ts b/packages/opencode/src/format/formatter.ts index cdb4f09be7..d26a0f9943 100644 --- a/packages/opencode/src/format/formatter.ts +++ b/packages/opencode/src/format/formatter.ts @@ -92,7 +92,9 @@ export const oxfmt: Info = { devDependencies?: Record }>(item) if (json.dependencies?.oxfmt || json.devDependencies?.oxfmt) { - return [await Npm.which("oxfmt"), "$FILE"] + const bin = await Npm.which("oxfmt") + if (!bin) return false + return [bin, "$FILE"] } } return false @@ -134,7 +136,9 @@ export const biome: Info = { for (const config of configs) { const found = await Filesystem.findUp(config, Instance.directory, Instance.worktree) if (found.length > 0) { - return [await Npm.which("@biomejs/biome"), "check", "--write", "$FILE"] + const bin = await Npm.which("@biomejs/biome") + if (!bin) return false + return [bin, "check", "--write", "$FILE"] } } return false diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index e822642b05..aa9bc884a8 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -103,7 +103,9 @@ export namespace LSPServer { const tsserver = Module.resolve("typescript/lib/tsserver.js", Instance.directory) log.info("typescript server", { tsserver }) if (!tsserver) return - const proc = spawn(await Npm.which("typescript-language-server"), ["--stdio"], { + const bin = await Npm.which("typescript-language-server") + if (!bin) return + const proc = spawn(bin, ["--stdio"], { cwd: root, env: { ...process.env, @@ -129,7 +131,9 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return - binary = await Npm.which("@vue/language-server") + const resolved = await Npm.which("@vue/language-server") + if (!resolved) return + binary = resolved } args.push("--stdio") const proc = spawn(binary, args, { @@ -322,6 +326,7 @@ export namespace LSPServer { const resolved = Module.resolve("biome", root) if (!resolved) return bin = await Npm.which("biome") + if (!bin) return args = ["lsp-proxy", "--stdio"] } @@ -488,7 +493,9 @@ export namespace LSPServer { const args = [] if (!binary) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return - binary = await Npm.which("pyright") + const resolved = await Npm.which("pyright") + if (!resolved) return + binary = resolved } args.push("--stdio") @@ -1003,7 +1010,9 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return - binary = await Npm.which("svelte-language-server") + const resolved = await Npm.which("svelte-language-server") + if (!resolved) return + binary = resolved } args.push("--stdio") const proc = spawn(binary, args, { @@ -1035,7 +1044,9 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return - binary = await Npm.which("@astrojs/language-server") + const resolved = await Npm.which("@astrojs/language-server") + if (!resolved) return + binary = resolved } args.push("--stdio") const proc = spawn(binary, args, { @@ -1284,7 +1295,9 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return - binary = await Npm.which("yaml-language-server") + const resolved = await Npm.which("yaml-language-server") + if (!resolved) return + binary = resolved } args.push("--stdio") const proc = spawn(binary, args, { @@ -1449,7 +1462,9 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return - binary = await Npm.which("intelephense") + const resolved = await Npm.which("intelephense") + if (!resolved) return + binary = resolved } args.push("--stdio") const proc = spawn(binary, args, { @@ -1531,7 +1546,9 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return - binary = await Npm.which("bash-language-server") + const resolved = await Npm.which("bash-language-server") + if (!resolved) return + binary = resolved } args.push("start") const proc = spawn(binary, args, { @@ -1724,7 +1741,9 @@ export namespace LSPServer { const args: string[] = [] if (!binary) { if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return - binary = await Npm.which("dockerfile-language-server-nodejs") + const resolved = await Npm.which("dockerfile-language-server-nodejs") + if (!resolved) return + binary = resolved } args.push("--stdio") const proc = spawn(binary, args, { diff --git a/packages/opencode/src/npm/index.ts b/packages/opencode/src/npm/index.ts index 90e135367f..db95c6e1df 100644 --- a/packages/opencode/src/npm/index.ts +++ b/packages/opencode/src/npm/index.ts @@ -175,7 +175,7 @@ export namespace Npm { await rm(path.join(dir, "package-lock.json"), { force: true }) await add(pkg) const resolved = await pick() - if (!resolved) throw new Error(`No binary found for package "${pkg}" after install`) + if (!resolved) return return path.join(binDir, resolved) } }