From e8a7c6d6dfaf63003de2b5b0a0aaee2f4d2b6904 Mon Sep 17 00:00:00 2001 From: Anthony Kim <62267334+anthonykim1@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:25:55 -0700 Subject: [PATCH] Stop copying node-pty into Copilot CLI SDK (#310925) * Stop copying node-pty into Copilot CLI SDK * reanem Co-authored-by: Copilot * resovle copilot comment * recognize other sdk prebuilds NOT node-pty Co-authored-by: Copilot --------- Co-authored-by: Copilot --- build/gulpfile.reh.ts | 9 +- build/gulpfile.vscode.ts | 9 +- build/lib/copilot.ts | 86 +++-------- extensions/copilot/eslint.config.mjs | 1 - .../chatSessions/copilotcli/AGENTS.md | 3 +- .../copilotcli/node/copilotCli.ts | 8 +- .../copilotcli/node/nodePtyShim.ts | 144 ------------------ .../test/copilotCLISDKUpgrade.spec.ts | 29 ++-- 8 files changed, 43 insertions(+), 246 deletions(-) delete mode 100644 extensions/copilot/src/extension/chatSessions/copilotcli/node/nodePtyShim.ts diff --git a/build/gulpfile.reh.ts b/build/gulpfile.reh.ts index 2c44fc9427f..3c0e3a16d02 100644 --- a/build/gulpfile.reh.ts +++ b/build/gulpfile.reh.ts @@ -34,7 +34,7 @@ import * as cp from 'child_process'; import log from 'fancy-log'; import buildfile from './buildfile.ts'; import { fetchUrls, fetchGithub } from './lib/fetch.ts'; -import { getCopilotExcludeFilter, copyCopilotNativeDeps, prepareBuiltInCopilotExtensionShims } from './lib/copilot.ts'; +import { getCopilotExcludeFilter, prepareBuiltInCopilotRipgrepShim } from './lib/copilot.ts'; import jsonEditor from 'gulp-json-editor'; @@ -463,14 +463,13 @@ function patchWin32DependenciesTask(destinationFolderName: string) { }; } -function copyCopilotNativeDepsTaskREH(platform: string, arch: string, destinationFolderName: string) { +function prepareCopilotRipgrepShimTaskREH(platform: string, arch: string, destinationFolderName: string) { return async () => { const outputDir = path.join(BUILD_ROOT, destinationFolderName); const nodeModulesDir = path.join(outputDir, 'node_modules'); - copyCopilotNativeDeps(platform, arch, nodeModulesDir); const builtInCopilotExtensionDir = path.join(outputDir, 'extensions', 'copilot'); - prepareBuiltInCopilotExtensionShims(platform, arch, builtInCopilotExtensionDir, nodeModulesDir); + prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, nodeModulesDir); }; } @@ -523,7 +522,7 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) { gulp.task(`node-${platform}-${arch}`) as task.Task, util.rimraf(path.join(BUILD_ROOT, destinationFolderName)), packageTask(type, platform, arch, sourceFolderName, destinationFolderName), - copyCopilotNativeDepsTaskREH(platform, arch, destinationFolderName) + prepareCopilotRipgrepShimTaskREH(platform, arch, destinationFolderName) ]; if (platform === 'win32') { diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts index 31188a4ce9e..75ccf64fe7e 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts @@ -31,7 +31,7 @@ import minimist from 'minimist'; import { compileBuildWithoutManglingTask, compileBuildWithManglingTask } from './gulpfile.compile.ts'; import { compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileAllExtensionsBuildTask, compileExtensionMediaBuildTask, cleanExtensionsBuildTask, compileCopilotExtensionBuildTask } from './gulpfile.extensions.ts'; import { copyCodiconsTask } from './lib/compilation.ts'; -import { getCopilotExcludeFilter, copyCopilotNativeDeps, prepareBuiltInCopilotExtensionShims } from './lib/copilot.ts'; +import { getCopilotExcludeFilter, prepareBuiltInCopilotRipgrepShim } from './lib/copilot.ts'; import type { EmbeddedProductInfo } from './lib/embeddedType.ts'; import { useEsbuildTranspile } from './buildConfig.ts'; import { promisify } from 'util'; @@ -700,7 +700,7 @@ function patchWin32DependenciesTask(destinationFolderName: string) { }; } -function copyCopilotNativeDepsTask(platform: string, arch: string, destinationFolderName: string) { +function prepareCopilotRipgrepShimTask(platform: string, arch: string, destinationFolderName: string) { const outputDir = path.join(path.dirname(root), destinationFolderName); return async () => { @@ -711,10 +711,9 @@ function copyCopilotNativeDepsTask(platform: string, arch: string, destinationFo ? path.join(outputDir, `${product.nameLong}.app`, 'Contents', 'Resources', 'app') : path.join(outputDir, versionedResourcesFolder, 'resources', 'app'); const appNodeModulesDir = path.join(appBase, 'node_modules'); - copyCopilotNativeDeps(platform, arch, appNodeModulesDir); const builtInCopilotExtensionDir = path.join(appBase, 'extensions', 'copilot'); - prepareBuiltInCopilotExtensionShims(platform, arch, builtInCopilotExtensionDir, appNodeModulesDir); + prepareBuiltInCopilotRipgrepShim(platform, arch, builtInCopilotExtensionDir, appNodeModulesDir); }; } @@ -743,7 +742,7 @@ BUILD_TARGETS.forEach(buildTarget => { compileNativeExtensionsBuildTask, util.rimraf(path.join(buildRoot, destinationFolderName)), packageTask(platform, arch, sourceFolderName, destinationFolderName, opts), - copyCopilotNativeDepsTask(platform, arch, destinationFolderName) + prepareCopilotRipgrepShimTask(platform, arch, destinationFolderName) ]; if (platform === 'win32') { diff --git a/build/lib/copilot.ts b/build/lib/copilot.ts index f0e50ce9a80..fec1c3d0d85 100644 --- a/build/lib/copilot.ts +++ b/build/lib/copilot.ts @@ -46,8 +46,9 @@ function toNodePlatformArch(platform: string, arch: string): { nodePlatform: str * for architectures other than the build target. * * For platforms the copilot SDK doesn't natively support (e.g. alpine, armhf), - * ALL platform packages are stripped - that's fine because the SDK doesn't ship - * binaries for those platforms anyway, and we replace them with VS Code's own. + * ALL platform packages are stripped - that's fine because the copilot CLI SDK + * resolves `node-pty` from the embedder (VS Code) first via `hostRequire`, + * falling back to its bundled copy only if the embedder can't provide it. */ export function getCopilotExcludeFilter(platform: string, arch: string): string[] { const { nodePlatform, nodeArch } = toNodePlatformArch(platform, arch); @@ -55,74 +56,30 @@ export function getCopilotExcludeFilter(platform: string, arch: string): string[ const nonTargetPlatforms = copilotPlatforms.filter(p => p !== targetPlatformArch); // Strip wrong-architecture @github/copilot-{platform} packages. - // All copilot prebuilds are stripped by .moduleignore; VS Code's own - // node-pty is copied into the prebuilds location by a post-packaging task. + // All copilot prebuilds are stripped by .moduleignore; the copilot CLI SDK + // resolves `node-pty` from VS Code's own node_modules via `hostRequire`. const excludes = nonTargetPlatforms.map(p => `!**/node_modules/@github/copilot-${p}/**`); return ['**', ...excludes]; } /** - * Copies VS Code's own node-pty binaries into the copilot SDK's - * expected locations so the copilot CLI subprocess can find them at runtime. - * The copilot-bundled prebuilds are stripped by .moduleignore; - * this replaces them with the same binaries VS Code already ships, avoiding - * new system dependency requirements. - * - * This works even for platforms the copilot SDK doesn't natively support - * (e.g. alpine, armhf) because the SDK's native module loader simply - * looks for `prebuilds/{process.platform}-{process.arch}/pty.node` - it - * doesn't validate the platform against a supported list. - * - * Failures are logged but do not throw, to avoid breaking the build on - * platforms where something unexpected happens. - * - * @param nodeModulesDir Absolute path to the node_modules directory that - * contains both the source binaries (node-pty) and the copilot SDK - * target directories. - */ -export function copyCopilotNativeDeps(platform: string, arch: string, nodeModulesDir: string): void { - const { nodePlatform, nodeArch } = toNodePlatformArch(platform, arch); - const platformArch = `${nodePlatform}-${nodeArch}`; - - const copilotBase = path.join(nodeModulesDir, '@github', 'copilot'); - if (!fs.existsSync(copilotBase)) { - console.warn(`[copyCopilotNativeDeps] @github/copilot not found at ${copilotBase}, skipping`); - return; - } - - const nodePtySource = path.join(nodeModulesDir, 'node-pty', 'build', 'Release'); - if (!fs.existsSync(nodePtySource)) { - console.warn(`[copyCopilotNativeDeps] node-pty source not found at ${nodePtySource}, skipping`); - return; - } - - try { - // Copy node-pty (pty.node + spawn-helper on Unix, conpty.node + conpty/ on Windows) - // into copilot prebuilds so the SDK finds them via loadNativeModule. - const copilotPrebuildsDir = path.join(copilotBase, 'prebuilds', platformArch); - fs.mkdirSync(copilotPrebuildsDir, { recursive: true }); - fs.cpSync(nodePtySource, copilotPrebuildsDir, { recursive: true }); - console.log(`[copyCopilotNativeDeps] Copied node-pty from ${nodePtySource} to ${copilotPrebuildsDir}`); - } catch (err) { - console.warn(`[copyCopilotNativeDeps] Failed to copy node-pty for ${platformArch}: ${err}`); - } -} - -/** - * Materializes copilot CLI shims directly inside the built-in copilot extension. + * Materializes the copilot CLI ripgrep shim directly inside the built-in copilot extension. * * This is used when copilot is shipped as a built-in extension so startup does - * not need to create shims at runtime. The destination layout matches the + * not need to create the shim at runtime. The destination layout matches the * runtime shim logic in the copilot extension: - * - node-pty: node_modules/@github/copilot/sdk/prebuilds/{platform-arch} * - ripgrep: node_modules/@github/copilot/sdk/ripgrep/bin/{platform-arch} * - marker: node_modules/@github/copilot/shims.txt * + * Note: `node-pty` is no longer shimmed. The copilot CLI SDK resolves + * `node-pty` from the embedder (VS Code) via `hostRequire` and falls back to + * its bundled copy only if that fails. + * * Failures throw to fail the build because built-in packaging must guarantee - * these artifacts are present. + * this artifact is present. */ -export function prepareBuiltInCopilotExtensionShims(platform: string, arch: string, builtInCopilotExtensionDir: string, appNodeModulesDir: string): void { +export function prepareBuiltInCopilotRipgrepShim(platform: string, arch: string, builtInCopilotExtensionDir: string, appNodeModulesDir: string): void { const { nodePlatform, nodeArch } = toNodePlatformArch(platform, arch); const platformArch = `${nodePlatform}-${nodeArch}`; @@ -130,33 +87,24 @@ export function prepareBuiltInCopilotExtensionShims(platform: string, arch: stri const copilotBase = path.join(extensionNodeModules, '@github', 'copilot'); const copilotSdkBase = path.join(copilotBase, 'sdk'); if (!fs.existsSync(copilotSdkBase)) { - throw new Error(`[prepareBuiltInCopilotExtensionShims] Copilot SDK directory not found at ${copilotSdkBase}`); - } - - const nodePtySource = path.join(appNodeModulesDir, 'node-pty', 'build', 'Release'); - if (!fs.existsSync(nodePtySource)) { - throw new Error(`[prepareBuiltInCopilotExtensionShims] node-pty source not found at ${nodePtySource}`); + throw new Error(`[prepareBuiltInCopilotRipgrepShim] Copilot SDK directory not found at ${copilotSdkBase}`); } const ripgrepSource = path.join(appNodeModulesDir, '@vscode', 'ripgrep', 'bin'); if (!fs.existsSync(ripgrepSource)) { - throw new Error(`[prepareBuiltInCopilotExtensionShims] ripgrep source not found at ${ripgrepSource}`); + throw new Error(`[prepareBuiltInCopilotRipgrepShim] ripgrep source not found at ${ripgrepSource}`); } - const nodePtyDest = path.join(copilotSdkBase, 'prebuilds', platformArch); const ripgrepDest = path.join(copilotSdkBase, 'ripgrep', 'bin', platformArch); const shimMarkerPath = path.join(copilotBase, 'shims.txt'); try { - fs.mkdirSync(nodePtyDest, { recursive: true }); - fs.cpSync(nodePtySource, nodePtyDest, { recursive: true }); - fs.mkdirSync(ripgrepDest, { recursive: true }); fs.cpSync(ripgrepSource, ripgrepDest, { recursive: true }); fs.writeFileSync(shimMarkerPath, 'Shims created successfully'); - console.log(`[prepareBuiltInCopilotExtensionShims] Materialized shims for ${platformArch} in ${builtInCopilotExtensionDir}`); + console.log(`[prepareBuiltInCopilotRipgrepShim] Materialized ripgrep shim for ${platformArch} in ${builtInCopilotExtensionDir}`); } catch (err) { - throw new Error(`[prepareBuiltInCopilotExtensionShims] Failed to materialize shims for ${platformArch}: ${err}`); + throw new Error(`[prepareBuiltInCopilotRipgrepShim] Failed to materialize ripgrep shim for ${platformArch}: ${err}`); } } diff --git a/extensions/copilot/eslint.config.mjs b/extensions/copilot/eslint.config.mjs index d5bbb8e4f27..7edfb1982d9 100644 --- a/extensions/copilot/eslint.config.mjs +++ b/extensions/copilot/eslint.config.mjs @@ -338,7 +338,6 @@ export default tseslint.config( ignores: [ 'src/util/vs/**/*.ts', // vendored code 'src/**/*.spec.ts', // allow in tests - './src/extension/agents/copilotcli/node/nodePtyShim.ts', './src/extension/byok/common/anthropicMessageConverter.ts', './src/extension/byok/common/geminiFunctionDeclarationConverter.ts', './src/extension/byok/common/geminiMessageConverter.ts', diff --git a/extensions/copilot/src/extension/chatSessions/copilotcli/AGENTS.md b/extensions/copilot/src/extension/chatSessions/copilotcli/AGENTS.md index eb343ccf15b..ebd7169ae23 100644 --- a/extensions/copilot/src/extension/chatSessions/copilotcli/AGENTS.md +++ b/extensions/copilot/src/extension/chatSessions/copilotcli/AGENTS.md @@ -78,7 +78,6 @@ copilotcli/ │ ├── copilotCLISkills.ts # Skills location resolution │ ├── copilotCLIImageSupport.ts # Image attachment handling │ ├── mcpHandler.ts # MCP server configuration for SDK sessions -│ ├── nodePtyShim.ts # Copies VS Code's node-pty for SDK use │ ├── userInputHelpers.ts # User question/input handling interface │ ├── exitPlanModeHandler.ts # Plan mode exit flow with user choice │ ├── ripgrepShim.ts # Copies VS Code's ripgrep for SDK use @@ -313,7 +312,7 @@ Orchestrates the start and end of each chat request turn, coordinating worktree ## Critical Pitfalls -- **Shims before SDK import**: `ensureNodePtyShim()` and `ensureRipgrepShim()` in `node/nodePtyShim.ts` / `node/ripgrepShim.ts` MUST be called before any `import('@github/copilot/sdk')`. They copy VS Code's bundled native binaries to the SDK's expected locations. See `node/copilotCli.ts` for the initialization order. +- **Shims before SDK import**: `ensureRipgrepShim()` in `node/ripgrepShim.ts` MUST be called before any `import('@github/copilot/sdk')`. It copies VS Code's bundled ripgrep binary to the SDK's expected location. `node-pty` is no longer shimmed: the copilot CLI SDK resolves it from VS Code's own `node_modules` via `hostRequire`, falling back to its bundled copy only if that fails. See `node/copilotCli.ts` for the initialization order. - **Delayed permission UI**: Tool invocation messages are held in `toolCallWaitingForPermissions` until permission resolves. `flushPendingInvocationMessageForToolCallId()` flushes only the specific approved tool, not all pending tools. This is intentional — don't bypass it. diff --git a/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotCli.ts b/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotCli.ts index 72308407e8c..5ba70073994 100644 --- a/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotCli.ts +++ b/extensions/copilot/src/extension/chatSessions/copilotcli/node/copilotCli.ts @@ -23,7 +23,6 @@ import { basename } from '../../../../util/vs/base/common/resources'; import { URI } from '../../../../util/vs/base/common/uri'; import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation'; import { getCopilotLogger } from './logger'; -import { ensureNodePtyShim } from './nodePtyShim'; import { ensureRipgrepShim } from './ripgrepShim'; import { CancellationToken } from '../../../../util/vs/base/common/cancellation'; @@ -460,7 +459,7 @@ export class CopilotCLISDK implements ICopilotCLISDK { public async getPackage(): Promise { try { - // Ensure the node-pty shim exists before importing the SDK (required for CLI sessions) + // Ensure the ripgrep shim exists before importing the SDK (required for CLI sessions) await this._ensureShimsPromise; return await import('@github/copilot/sdk'); } catch (error) { @@ -497,10 +496,7 @@ export class CopilotCLISDK implements ICopilotCLISDK { if (await checkFileExists(successfulPlaceholder)) { return; } - await Promise.all([ - ensureNodePtyShim(this.extensionContext.extensionPath, this.envService.appRoot, this.logService), - ensureRipgrepShim(this.extensionContext.extensionPath, this.envService.appRoot, this.logService) - ]); + await ensureRipgrepShim(this.extensionContext.extensionPath, this.envService.appRoot, this.logService); await fs.writeFile(successfulPlaceholder, 'Shims created successfully'); } diff --git a/extensions/copilot/src/extension/chatSessions/copilotcli/node/nodePtyShim.ts b/extensions/copilot/src/extension/chatSessions/copilotcli/node/nodePtyShim.ts deleted file mode 100644 index c7db274412a..00000000000 --- a/extensions/copilot/src/extension/chatSessions/copilotcli/node/nodePtyShim.ts +++ /dev/null @@ -1,144 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { promises as fs } from 'fs'; -import * as path from 'path'; -import { ILogService } from '../../../../platform/log/common/logService'; - -let shimCreated: Promise | undefined = undefined; - -const RETRIABLE_COPY_ERROR_CODES = new Set(['EPERM', 'EBUSY']); -const MAX_COPY_ATTEMPTS = 6; -const RETRY_DELAY_BASE_MS = 50; -const RETRY_DELAY_CAP_MS = 500; -const MATERIALIZATION_TIMEOUT_MS = 4000; -const MATERIALIZATION_POLL_INTERVAL_MS = 100; - -/** - * Copies the node-pty files from VS Code's installation into a @github/copilot location - * - * MUST be called before any `import('@github/copilot/sdk')` or `import('@github/copilot')`. - * - * @github/copilot bundles the node-pty code and its no longer possible to shim the package. - * - * @param extensionPath The extension's path (where to create the shim) - * @param vscodeAppRoot VS Code's installation path (where node-pty is located) - */ -export async function ensureNodePtyShim(extensionPath: string, vscodeAppRoot: string, logService: ILogService): Promise { - if (shimCreated) { - return shimCreated; - } - - const creation = _ensureNodePtyShim(extensionPath, vscodeAppRoot, logService); - shimCreated = creation.catch(error => { - shimCreated = undefined; - throw error; - }); - return shimCreated; -} - -async function _ensureNodePtyShim(extensionPath: string, vscodeAppRoot: string, logService: ILogService): Promise { - const vscodeNodePtyPath = path.join(vscodeAppRoot, 'node_modules', 'node-pty', 'build', 'Release'); - - await copyNodePtyFiles(extensionPath, vscodeNodePtyPath, logService); -} - -export async function copyNodePtyFiles(extensionPath: string, sourceNodePtyPath: string, logService: ILogService): Promise { - const nodePtyDir = path.join(extensionPath, 'node_modules', '@github', 'copilot', 'sdk', 'prebuilds', process.platform + '-' + process.arch); - logService.info(`Creating node-pty shim: source=${sourceNodePtyPath}, dest=${nodePtyDir}`); - - try { - await fs.mkdir(nodePtyDir, { recursive: true }); - const entries = await fs.readdir(sourceNodePtyPath); - const uniqueEntries = [...new Set(entries)]; - logService.info(`Found ${uniqueEntries.length} entries to copy${uniqueEntries.length !== entries.length ? ` (${entries.length - uniqueEntries.length} duplicates ignored)` : ''}: ${uniqueEntries.join(', ')}`); - - await copyNodePtyWithRetries(sourceNodePtyPath, nodePtyDir, uniqueEntries, logService); - } catch (error) { - logService.error(`Failed to create node-pty shim (source dir: ${sourceNodePtyPath}, extension dir: ${nodePtyDir})`, error); - throw error; - } -} - -async function copyNodePtyWithRetries(sourceDir: string, destDir: string, entries: string[], logService: ILogService): Promise { - const primaryBinary = entries.find(entry => entry.endsWith('.node')); - for (let attempt = 1; attempt <= MAX_COPY_ATTEMPTS; attempt++) { - try { - await fs.cp(sourceDir, destDir, { - recursive: true, - dereference: true, - force: true, - filter: async (srcPath) => shouldCopyEntry(srcPath, logService) - }); - logService.trace(`Copied node-pty prebuilds to ${destDir} (attempt ${attempt})`); - return; - } catch (error) { - if (await waitForMaterializedShim(destDir, primaryBinary, logService)) { - logService.trace(`Detected node-pty shim materialized at ${destDir} by another extension host`); - return; - } - - if (!RETRIABLE_COPY_ERROR_CODES.has(error?.code) || attempt === MAX_COPY_ATTEMPTS) { - throw error; - } - - const delayMs = Math.min(RETRY_DELAY_BASE_MS * Math.pow(2, attempt - 1), RETRY_DELAY_CAP_MS); - logService.warn(`Retryable error (${error.code}) copying node-pty shim. Retrying in ${delayMs}ms (attempt ${attempt + 1}/${MAX_COPY_ATTEMPTS})`); - await new Promise(resolve => setTimeout(resolve, delayMs)); - } - } -} - -async function shouldCopyEntry(srcPath: string, logService: ILogService): Promise { - try { - const stat = await fs.stat(srcPath); - if (stat.isDirectory()) { - return true; - } - - if (stat.size === 0) { - logService.trace(`Skipping ${path.basename(srcPath)}: zero-byte file (likely symlink or special file)`); - return false; - } - - return true; - } catch (error) { - logService.warn(`Failed to stat ${srcPath}: ${error?.message ?? error}`); - return false; - } -} - -async function waitForMaterializedShim(destDir: string, primaryBinary: string | undefined, logService: ILogService): Promise { - const deadline = Date.now() + MATERIALIZATION_TIMEOUT_MS; - while (Date.now() <= deadline) { - if (await isShimMaterialized(destDir, primaryBinary)) { - logService.trace(`Reusing node-pty shim that materialized at ${destDir}`); - return true; - } - - await new Promise(resolve => setTimeout(resolve, MATERIALIZATION_POLL_INTERVAL_MS)); - } - - return false; -} - -async function isShimMaterialized(destDir: string, primaryBinary: string | undefined): Promise { - if (primaryBinary) { - const binaryStat = await fs.stat(path.join(destDir, primaryBinary)).catch(() => undefined); - if (binaryStat && binaryStat.isFile() && binaryStat.size > 0) { - return true; - } - } - - const entries = await fs.readdir(destDir).catch(() => []); - for (const entry of entries) { - const stat = await fs.stat(path.join(destDir, entry)).catch(() => undefined); - if (stat && stat.isFile() && stat.size > 0) { - return true; - } - } - - return false; -} diff --git a/extensions/copilot/src/extension/chatSessions/vscode-node/test/copilotCLISDKUpgrade.spec.ts b/extensions/copilot/src/extension/chatSessions/vscode-node/test/copilotCLISDKUpgrade.spec.ts index a244f8f0f32..98cf628b661 100644 --- a/extensions/copilot/src/extension/chatSessions/vscode-node/test/copilotCLISDKUpgrade.spec.ts +++ b/extensions/copilot/src/extension/chatSessions/vscode-node/test/copilotCLISDKUpgrade.spec.ts @@ -8,7 +8,6 @@ import { isBinaryFile } from 'isbinaryfile'; import * as path from 'path'; import { beforeAll, describe, it } from 'vitest'; import { TestLogService } from '../../../../platform/testing/common/testLogService'; -import { copyNodePtyFiles } from '../../copilotcli/node/nodePtyShim'; import { copyRipgrepShim } from '../../copilotcli/node/ripgrepShim'; describe('CopilotCLI SDK Upgrade', function () { @@ -59,6 +58,16 @@ describe('CopilotCLI SDK Upgrade', function () { // win32 native module (formerly win_error_mode) path.join('prebuilds', 'win32-arm64', 'win32.node'), path.join('prebuilds', 'win32-x64', 'win32.node'), + // Second copy of computer.node / win32.node re-shipped by the @github/copilot/sdk subpackage + // (previously hidden by a broad sdk/prebuilds/** exclusion that masked the node-pty files we used to shim in at test setup). + path.join('sdk', 'prebuilds', 'darwin-arm64', 'computer.node'), + path.join('sdk', 'prebuilds', 'darwin-x64', 'computer.node'), + path.join('sdk', 'prebuilds', 'linux-arm64', 'computer.node'), + path.join('sdk', 'prebuilds', 'linux-x64', 'computer.node'), + path.join('sdk', 'prebuilds', 'win32-arm64', 'computer.node'), + path.join('sdk', 'prebuilds', 'win32-x64', 'computer.node'), + path.join('sdk', 'prebuilds', 'win32-arm64', 'win32.node'), + path.join('sdk', 'prebuilds', 'win32-x64', 'win32.node'), path.join('ripgrep', 'bin', 'darwin-arm64', 'rg'), path.join('ripgrep', 'bin', 'darwin-x64', 'rg'), path.join('ripgrep', 'bin', 'linux-x64', 'rg'), @@ -89,15 +98,13 @@ describe('CopilotCLI SDK Upgrade', function () { 'tree-sitter-scala.wasm', ].map(p => path.join(copilotSDKPath, p))); - // Exclude ripgrep files that we copy over in src/extension/agents/copilotcli/node/ripgrepShim.ts (until we get better API/solution from SDK) + // Exclude ripgrep files that we copy over in src/extension/chatSessions/copilotcli/node/ripgrepShim.ts (until we get better API/solution from SDK) const ripgrepFilesWeCopy = path.join(copilotSDKPath, 'sdk', 'ripgrep', 'bin'); - // Exclude nodepty files that we copy over in src/extension/agents/copilotcli/node/nodePtyShim.ts (until we get better API/solution from SDK) - const nodeptyFilesWeCopy = path.join(copilotSDKPath, 'sdk', 'prebuilds'); const errors: string[] = []; // Look for new binaries for (const binary of existingBinaries) { - if (binary.startsWith(ripgrepFilesWeCopy) || binary.startsWith(nodeptyFilesWeCopy)) { + if (binary.startsWith(ripgrepFilesWeCopy)) { continue; } const binaryName = path.basename(binary); @@ -110,7 +117,7 @@ describe('CopilotCLI SDK Upgrade', function () { } // Look for removed binaries. for (const binary of knownBinaries) { - if (binary.startsWith(ripgrepFilesWeCopy) || binary.startsWith(nodeptyFilesWeCopy)) { + if (binary.startsWith(ripgrepFilesWeCopy)) { continue; } if (!existingBinaries.has(binary)) { @@ -124,19 +131,13 @@ describe('CopilotCLI SDK Upgrade', function () { }); it('should be able to load the @github/copilot module without errors', async function () { - await copyNodePtyFiles( - extensionPath, - path.join(copilotSDKPath, 'prebuilds', process.platform + '-' + process.arch), - new TestLogService() - ); await import('@github/copilot/sdk'); }); }); async function copyBinaries(extensionPath: string) { - const nodePtyPrebuilds = path.join(extensionPath, 'node_modules', '@github', 'copilot', 'prebuilds', process.platform + '-' + process.arch); - const vscodeRipgrepPath = path.join(extensionPath, 'node_modules', '@github', 'copilot', 'ripgrep', 'bin', process.platform + '-' + process.arch); - await copyNodePtyFiles(extensionPath, nodePtyPrebuilds, new TestLogService()); + const copilotSDKPath = path.join(extensionPath, 'node_modules', '@github', 'copilot'); + const vscodeRipgrepPath = path.join(copilotSDKPath, 'ripgrep', 'bin', process.platform + '-' + process.arch); await copyRipgrepShim(extensionPath, vscodeRipgrepPath, new TestLogService()); } async function findAllBinaries(dir: string): Promise {