-
Notifications
You must be signed in to change notification settings - Fork 887
Description
Summary
getBundledCliPath() in nodejs/src/client.ts uses import.meta.resolve("@github/copilot/sdk"), which is an ESM-only API. VS Code extensions must bundle to CJS format using esbuild, which replaces import.meta with {} in CJS output. This causes a runtime crash when the SDK tries to locate the CLI.
Affected Versions
- 0.1.23, 0.1.24, 0.1.25, 0.1.26-preview.0 (all contain the same
getBundledCliPath()implementation) - 0.1.22 and earlier are not affected — they used
cliPath: options.cliPath || "copilot"(simple PATH lookup)
Reproduction
- Create a VS Code extension that uses
@github/copilot-sdk - Bundle with esbuild in CJS format (
format: 'cjs') - Package as VSIX and install
- Attempt to start a session
Error 1 (without any polyfill):
TypeError: Zo.resolve is not a function
esbuild replaces import.meta with {}, so import.meta.resolve becomes {}.resolve → undefined.
Error 2 (with import.meta.resolve polyfilled to require.resolve):
Error: Cannot find module '@github/copilot/sdk'
require.resolve("@github/copilot/sdk") fails because @github/copilot only exports ./sdk under the ESM import condition, not require. Additionally, the VSIX does not include node_modules.
Root Cause
The "Bundling (#382)" PR changed the default CLI resolution from a simple PATH lookup to:
function getBundledCliPath(): string {
const sdkUrl = import.meta.resolve("@github/copilot/sdk");
const sdkPath = fileURLToPath(sdkUrl);
return join(dirname(dirname(sdkPath)), "index.js");
}This function uses two ESM-only features:
import.meta.resolve()— not available in CJS- The
@github/copilotpackage's./sdkexport — only exposed under ESMimportcondition
Workaround
Pass an explicit cliPath to the CopilotClient constructor, which short-circuits getBundledCliPath() via options.cliPath || getBundledCliPath():
import { execFileSync } from 'child_process';
function resolveCliFromPath(): string {
const cmd = process.platform === 'win32' ? 'where' : 'which';
return execFileSync(cmd, ['copilot'], { encoding: 'utf-8' }).trim().split(/\r?\n/)[0];
}
const client = new CopilotClient({
cliPath: resolveCliFromPath(),
// ...
});This restores the pre-0.1.23 behavior of resolving copilot from PATH.
Suggestion
The unreleased commit 736b17e ("Remove @github/copilot dependency, point all SDKs at copilot-core") changes getBundledCliPath() to return "copilot-core", which would resolve this issue. However, SDK 0.1.23+ also added an existsSync(cliPath) check that rejects bare command names. If the intent is to support PATH-based resolution, the existsSync guard would need to handle bare command names (or use which/where internally).
Alternatively, a simpler fix would be to catch getBundledCliPath() failures and fall back to "copilot" on PATH, similar to the 0.1.22 behavior:
cliPath: options.cliPath || tryGetBundledCliPath() || "copilot"Environment
- Node.js 24
- esbuild 0.25.x (CJS output format)
- VS Code extension host (CJS module system)
- Linux x86_64 (also affects Windows/macOS — any CJS environment)