Describe the bug
@github/copilot@1.0.34 has unconditional top-level imports of node:sea in both of its entrypoint files:
index.js:7 is the CLI main entry, used when npm-loader.js falls back to import("./index.js") (the non-SEA install path).
sdk/index.js:~4123 is exposed via the "./sdk" field in exports. That subpath is the resolve target @github/copilot-sdk's getBundledCliPath() uses to locate the CLI.
Both imports serve a single purpose: X.isSea() is called by the auto-update logic (ELt() in sdk/index.js, similar in index.js) to short-circuit with "Update not supported when running js directly" when the CLI is not running from a Single Executable Application.
Because the import is top-level, non-Node runtimes that don't implement node:sea fail at resolve time, before any code runs. A try/catch around the import does not help: this is a module-graph error, not a runtime exception. Bun's current nodejs-compat doc does not list node:sea (SEA is a Node-specific packaging API, so this is expected).
Affected version
@github/copilot@1.0.34 (latest as of 2026-04-22).
Reproduced on: Bun 1.3.13 Linux x64, Node subprocess chain working normally but index.js loaded under Bun.
Steps to reproduce
Minimum:
mkdir bun-copilot-repro && cd bun-copilot-repro
bun init -y
bun add @github/copilot
bun -e 'await import("./node_modules/@github/copilot/index.js")'
Output:
error: No such built-in module: node:sea
Bun v1.3.13 (Linux x64)
User-facing symptom from the wild: cassidoo/emoji-list-generator#1. Reporter runs bun start on a TUI project that depends on @github/copilot-sdk. When the CLI subprocess is invoked, stderr shows:
error: Could not resolve: "node:sea". Maybe you need to "bun install"?
at .../node_modules/@github/copilot/index.js:7:390
The SDK already contains a Bun workaround: getNodeExecPath() in @github/copilot-sdk's client.js returns "node" (as a PATH lookup) when process.versions.bun is set, so it tries to spawn actual Node. This works on systems where node in PATH really is Node. It breaks when node is aliased or shimmed to bun, which is common on Bun-first Mac setups, and leaves the CLI's index.js being parsed by Bun at the node:sea import.
Expected behavior
The node:sea import should not prevent the module from being resolved on runtimes where node:sea is not implemented. Under those runtimes, isSea() should return false (SEA is Node-only by definition) and the auto-updater should take its existing "not running SEA" branch.
Suggested fix
Three shapes, least invasive first:
-
Lazy createRequire. Replace the top-level import with a function-scoped resolver:
import { createRequire } from "node:module";
function getSeaModule() {
try { return createRequire(import.meta.url)("node:sea"); }
catch { return null; }
}
function isSea() { return getSeaModule()?.isSea() ?? false; }
node:module resolves fine on Bun; the inner require returns null on runtimes where node:sea isn't available.
-
Dynamic import inside the checker. async function isSea() { try { const sea = await import("node:sea"); return sea.isSea(); } catch { return false; } }. More invasive because callers become async.
-
Top-level runtime guard. if (!globalThis.Bun && !process.versions.bun) { /* import node:sea here */ } with conditional module-scope binding. Reads cleaner at the top of the file but doesn't cover hypothetical future non-Node runtimes that also lack SEA.
Option (1) is the smallest change and matches the createRequire pattern already used for vendored native modules (sharp, clipboard) in sdk/index.js.
Additional context
- Adjacent Bun-compat rough edge in the same bundle (separate issue, not covered by the above fix): the bundled
undici feature-detection at sdk/index.js:~211 wraps require("node:sqlite") in a try/catch that only recognizes ERR_UNKNOWN_BUILTIN_MODULE / ERR_NO_CRYPTO. Bun throws a different error shape ("No such built-in module") without those codes, so the catch doesn't degrade gracefully. Loading sdk/index.js under Bun fails on the node:sqlite layer first; fixing node:sea alone would surface the node:sqlite error next. Worth a second issue against undici upstream if the scope here is CLI-only.
- Reference pattern in the wild: mcp-use/mcp-use#1382 applies the
globalThis.Bun || process.versions.bun two-check guard for a different Node-only dependency (tsx/esm/api loader hooks); same shape, different builtin.
- OS: macOS arm64 (reporter) / Linux x86_64 (my repro). Both hit the same
index.js:7 failure.
Describe the bug
@github/copilot@1.0.34has unconditional top-level imports ofnode:seain both of its entrypoint files:index.js:7is the CLI main entry, used whennpm-loader.jsfalls back toimport("./index.js")(the non-SEA install path).sdk/index.js:~4123is exposed via the"./sdk"field inexports. That subpath is the resolve target@github/copilot-sdk'sgetBundledCliPath()uses to locate the CLI.Both imports serve a single purpose:
X.isSea()is called by the auto-update logic (ELt()insdk/index.js, similar inindex.js) to short-circuit with"Update not supported when running js directly"when the CLI is not running from a Single Executable Application.Because the import is top-level, non-Node runtimes that don't implement
node:seafail at resolve time, before any code runs. A try/catch around the import does not help: this is a module-graph error, not a runtime exception. Bun's current nodejs-compat doc does not listnode:sea(SEA is a Node-specific packaging API, so this is expected).Affected version
@github/copilot@1.0.34(latest as of 2026-04-22).Reproduced on: Bun 1.3.13 Linux x64, Node subprocess chain working normally but index.js loaded under Bun.
Steps to reproduce
Minimum:
Output:
User-facing symptom from the wild: cassidoo/emoji-list-generator#1. Reporter runs
bun starton a TUI project that depends on@github/copilot-sdk. When the CLI subprocess is invoked, stderr shows:The SDK already contains a Bun workaround:
getNodeExecPath()in@github/copilot-sdk'sclient.jsreturns"node"(as a PATH lookup) whenprocess.versions.bunis set, so it tries to spawn actual Node. This works on systems wherenodein PATH really is Node. It breaks whennodeis aliased or shimmed tobun, which is common on Bun-first Mac setups, and leaves the CLI's index.js being parsed by Bun at thenode:seaimport.Expected behavior
The
node:seaimport should not prevent the module from being resolved on runtimes wherenode:seais not implemented. Under those runtimes,isSea()should returnfalse(SEA is Node-only by definition) and the auto-updater should take its existing "not running SEA" branch.Suggested fix
Three shapes, least invasive first:
Lazy
createRequire. Replace the top-level import with a function-scoped resolver:node:moduleresolves fine on Bun; the inner require returnsnullon runtimes wherenode:seaisn't available.Dynamic import inside the checker.
async function isSea() { try { const sea = await import("node:sea"); return sea.isSea(); } catch { return false; } }. More invasive because callers become async.Top-level runtime guard.
if (!globalThis.Bun && !process.versions.bun) { /* import node:sea here */ }with conditional module-scope binding. Reads cleaner at the top of the file but doesn't cover hypothetical future non-Node runtimes that also lack SEA.Option (1) is the smallest change and matches the
createRequirepattern already used for vendored native modules (sharp,clipboard) insdk/index.js.Additional context
undicifeature-detection atsdk/index.js:~211wrapsrequire("node:sqlite")in a try/catch that only recognizesERR_UNKNOWN_BUILTIN_MODULE/ERR_NO_CRYPTO. Bun throws a different error shape ("No such built-in module") without those codes, so the catch doesn't degrade gracefully. Loadingsdk/index.jsunder Bun fails on thenode:sqlitelayer first; fixingnode:seaalone would surface thenode:sqliteerror next. Worth a second issue against undici upstream if the scope here is CLI-only.globalThis.Bun || process.versions.buntwo-check guard for a different Node-only dependency (tsx/esm/apiloader hooks); same shape, different builtin.index.js:7failure.