fix: remote server node_modules lookup (#325233)

This commit is contained in:
Robo
2026-07-10 04:24:25 +00:00
committed by GitHub
parent ef400fc69f
commit e7b83d5455
4 changed files with 46 additions and 17 deletions
@@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AppResourcePath, nodeModulesAsarUnpackedPath, nodeModulesPath } from '../../../base/common/network.js';
import product from '../../product/common/product.js';
function hasUnpackedNodeModulesArchive(): boolean {
return !!process.versions['electron'] && !!product.commit && !process.env['VSCODE_DEV'];
}
/**
* The {@link AppResourcePath} of the `node_modules` root that actually holds VS
* Code's bundled modules, suitable for passing to `FileAccess.asFileUri`.
*/
export function getAppNodeModulesPath(): AppResourcePath {
return hasUnpackedNodeModulesArchive() ? nodeModulesAsarUnpackedPath : nodeModulesPath;
}
/**
* The bare directory name (`node_modules` or `node_modules.asar.unpacked`) of the
* resolved root, for callers that build paths from an app root themselves.
*/
export function getAppNodeModulesDirName(): 'node_modules' | 'node_modules.asar.unpacked' {
return hasUnpackedNodeModulesArchive() ? 'node_modules.asar.unpacked' : 'node_modules';
}
@@ -6,10 +6,10 @@
import type { Language, Parser, Query, QueryCapture } from '@vscode/tree-sitter-wasm';
import * as fs from 'fs';
import { Disposable, toDisposable } from '../../../base/common/lifecycle.js';
import { FileAccess, nodeModulesAsarUnpackedPath, nodeModulesPath } from '../../../base/common/network.js';
import { FileAccess } from '../../../base/common/network.js';
import { escapeRegExpCharacters, regExpLeadsToEndlessLoop } from '../../../base/common/strings.js';
import { URI } from '../../../base/common/uri.js';
import product from '../../product/common/product.js';
import { getAppNodeModulesPath } from './appNodeModules.js';
import { ILogService } from '../../log/common/log.js';
import type { AgentHostTerminalAutoApproveRuleValue, AgentHostTerminalAutoApproveRules } from '../common/agentHostSchema.js';
@@ -299,11 +299,11 @@ export class CommandAutoApprover extends Disposable {
return;
}
// Resolve WASM files from node_modules. In a built app the `.wasm` files
// are unpacked next to the ASAR archive (`node_modules.asar.unpacked`),
// while in dev they live in `node_modules`.
const moduleRootPath = product.commit ? nodeModulesAsarUnpackedPath : nodeModulesPath;
const moduleRoot = URI.joinPath(FileAccess.asFileUri(moduleRootPath), '@vscode', 'tree-sitter-wasm', 'wasm');
// Resolve WASM files from node_modules. In the desktop app the `.wasm`
// files are unpacked next to the ASAR archive (`node_modules.asar.unpacked`),
// while in dev and on the server (which has no ASAR) they live in a plain
// `node_modules`.
const moduleRoot = URI.joinPath(FileAccess.asFileUri(getAppNodeModulesPath()), '@vscode', 'tree-sitter-wasm', 'wasm');
const wasmPath = URI.joinPath(moduleRoot, 'tree-sitter.wasm').fsPath;
await TreeSitter.Parser.init({
@@ -15,6 +15,7 @@ import { IProductService } from '../../../product/common/productService.js';
import { ISandboxHelperService, type ISandboxDependencyStatus, type IWindowsMxcPolicyContainment, type IWindowsMxcSandboxPolicy } from '../../../sandbox/common/sandboxHelperService.js';
import { ITerminalSandboxEngineHost, ITerminalSandboxRuntimeInfo, TerminalSandboxEngine } from '../../../sandbox/common/terminalSandboxEngine.js';
import { IAgentConfigurationService } from '../agentConfigurationService.js';
import { getAppNodeModulesDirName } from '../appNodeModules.js';
import { AgentHostSandboxConfigKey, sandboxConfigSchema, sandboxSettingIdToAgentHostKey } from '../../common/sandboxConfigSchema.js';
/** Subdirectory under the user home + product data folder where the engine creates its temp dir. */
@@ -49,10 +50,11 @@ class AgentHostTerminalSandboxHost implements ITerminalSandboxEngineHost {
async getRuntimeInfo(): Promise<ITerminalSandboxRuntimeInfo> {
const appRoot = dirname(FileAccess.asFileUri('').path);
const runAsNode = !!process.versions['electron'];
// In a packaged build the native binaries (ripgrep-universal, mxc-sdk) are
// unpacked from the archive into `node_modules.asar.unpacked`; in dev they
// remain in plain `node_modules`.
const nativeModulesDir = this._environmentService.isBuilt ? 'node_modules.asar.unpacked' : 'node_modules';
// In the desktop app the native binaries (ripgrep-universal, mxc-sdk) are
// unpacked from the ASAR archive into `node_modules.asar.unpacked`; in dev
// and on the server (which has no ASAR) they remain in a plain
// `node_modules`.
const nativeModulesDir = getAppNodeModulesDirName();
return { appRoot, execPath: process.execPath, runAsNode, nativeModulesDir };
}
@@ -13,7 +13,7 @@ import { Emitter, Event } from '../../../../base/common/event.js';
import { appendEscapedMarkdownInlineCode } from '../../../../base/common/htmlContent.js';
import { combinedDisposable, Disposable, DisposableMap, MutableDisposable } from '../../../../base/common/lifecycle.js';
import { ResourceMap } from '../../../../base/common/map.js';
import { FileAccess, nodeModulesAsarUnpackedPath, nodeModulesPath, Schemas } from '../../../../base/common/network.js';
import { FileAccess, Schemas } from '../../../../base/common/network.js';
import { formatTokenCount } from '../../../../base/common/numbers.js';
import { equals } from '../../../../base/common/objects.js';
import { autorun, observableValue, type ISettableObservable } from '../../../../base/common/observable.js';
@@ -69,7 +69,7 @@ import { ICopilotApiService } from '../shared/copilotApiService.js';
import { CopilotSlashCommandCompletionProvider } from './copilotSlashCommandCompletionProvider.js';
import { DiscoveredType, SessionCustomizationDiscovery, areDiscoveredDirectoriesEqual, type IDiscoveredDirectory } from './sessionCustomizationDiscovery.js';
import { COPILOT_INTEGRATION_ID } from '../../../endpoint/common/licenseAgreement.js';
import product from '../../../product/common/product.js';
import { getAppNodeModulesPath } from '../appNodeModules.js';
const RUNTIME_SLASH_COMMAND_COMPLETION_WAIT_MS = 300;
const COPILOT_CAPI_URL = 'https://api.githubcopilot.com';
@@ -1011,14 +1011,14 @@ export class CopilotAgent extends Disposable implements IAgent {
}
// Resolve the CLI entry point and native SDK binaries from node_modules.
// In a built app these live next to the ASAR archive in
// In the desktop app these live next to the ASAR archive in
// `node_modules.asar.unpacked` (the `@github/copilot-<platform>` CLI and
// the `@microsoft/mxc-sdk/bin` executables are unpacked so they can be
// spawned), while in dev they live in `node_modules`.
// spawned), while in dev and on the server (which has no ASAR) they live
// in a plain `node_modules`.
// We can't use require.resolve() because @github/copilot's exports map
// blocks direct subpath access.
const moduleRootPath = product.commit ? nodeModulesAsarUnpackedPath : nodeModulesPath;
const nodeModulesUri = FileAccess.asFileUri(moduleRootPath);
const nodeModulesUri = FileAccess.asFileUri(getAppNodeModulesPath());
const cliPath = await resolveCopilotCliPath(nodeModulesUri);
// The SDK's sandbox auto-detection looks for `<MXC_BIN_DIR>/<arch>/wxc-exec.exe`