Dileep y/291793 (#292201)

Updating srt paths to work in remote env
This commit is contained in:
dileepyavan 2026-02-02 09:05:40 -08:00 committed by GitHub
parent 238adc8bc6
commit 01d7420295
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 140 additions and 21 deletions

View File

@ -8,6 +8,7 @@
"name": "vscode-reh",
"version": "0.0.0",
"dependencies": {
"@anthropic-ai/sandbox-runtime": "0.0.23",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "^2.5.6",
@ -47,6 +48,35 @@
"yazl": "^2.4.3"
}
},
"node_modules/@anthropic-ai/sandbox-runtime": {
"version": "0.0.23",
"resolved": "https://registry.npmjs.org/@anthropic-ai/sandbox-runtime/-/sandbox-runtime-0.0.23.tgz",
"integrity": "sha512-Np0VRH6D71cGoJZvd8hCz1LMfwg9ERJovrOJSCz5aSQSQJPWPNIFPV1wfc8oAhJpStOuYkot+EmXOkRRxuGMCQ==",
"license": "Apache-2.0",
"dependencies": {
"@pondwader/socks5-server": "^1.0.10",
"@types/lodash-es": "^4.17.12",
"commander": "^12.1.0",
"lodash-es": "^4.17.21",
"shell-quote": "^1.8.3",
"zod": "^3.24.1"
},
"bin": {
"srt": "dist/cli.js"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@anthropic-ai/sandbox-runtime/node_modules/commander": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/@microsoft/1ds-core-js": {
"version": "3.2.13",
"resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz",
@ -384,6 +414,12 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@pondwader/socks5-server": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@pondwader/socks5-server/-/socks5-server-1.0.10.tgz",
"integrity": "sha512-bQY06wzzR8D2+vVCUoBsr5QS2U6UgPUQRmErNwtsuI6vLcyRKkafjkr3KxbtGFf9aBBIV2mcvlsKD1UYaIV+sg==",
"license": "MIT"
},
"node_modules/@tootallnate/once": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-3.0.0.tgz",
@ -392,6 +428,21 @@
"node": ">= 10"
}
},
"node_modules/@types/lodash": {
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==",
"license": "MIT"
},
"node_modules/@types/lodash-es": {
"version": "4.17.12",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"license": "MIT",
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@vscode/deviceid": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@vscode/deviceid/-/deviceid-0.1.4.tgz",
@ -975,6 +1026,12 @@
"node": ">=12.9.0"
}
},
"node_modules/lodash-es": {
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz",
"integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
"license": "MIT"
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@ -1198,6 +1255,18 @@
"node": ">=10"
}
},
"node_modules/shell-quote": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
"integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
@ -1435,6 +1504,15 @@
"dependencies": {
"buffer-crc32": "~0.2.3"
}
},
"node_modules/zod": {
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View File

@ -3,6 +3,7 @@
"version": "0.0.0",
"private": true,
"dependencies": {
"@anthropic-ai/sandbox-runtime": "0.0.23",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@parcel/watcher": "^2.5.6",

View File

@ -12,6 +12,7 @@ export interface IRemoteAgentEnvironment {
pid: number;
connectionToken: string;
appRoot: URI;
tmpDir: URI;
settingsPath: URI;
mcpResource: URI;
logsPath: URI;

View File

@ -112,6 +112,7 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel {
pid: process.pid,
connectionToken: (this._connectionToken.type !== ServerConnectionTokenType.None ? this._connectionToken.value : ''),
appRoot: URI.file(this._environmentService.appRoot),
tmpDir: this._environmentService.tmpDir,
settingsPath: this._environmentService.machineSettingsResource,
mcpResource: this._environmentService.mcpResource,
logsPath: this._environmentService.logsHome,

View File

@ -7,9 +7,8 @@ import { VSBuffer } from '../../../../../base/common/buffer.js';
import { Event } from '../../../../../base/common/event.js';
import { Disposable } from '../../../../../base/common/lifecycle.js';
import { FileAccess } from '../../../../../base/common/network.js';
import { dirname, join } from '../../../../../base/common/path.js';
import { dirname, posix, win32 } from '../../../../../base/common/path.js';
import { OperatingSystem, OS } from '../../../../../base/common/platform.js';
import { joinPath } from '../../../../../base/common/resources.js';
import { URI } from '../../../../../base/common/uri.js';
import { generateUuid } from '../../../../../base/common/uuid.js';
import { IConfigurationChangeEvent, IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
@ -20,6 +19,7 @@ import { ILogService } from '../../../../../platform/log/common/log.js';
import { ITerminalSandboxSettings } from './terminalSandbox.js';
import { IRemoteAgentService } from '../../../../services/remote/common/remoteAgentService.js';
import { TerminalChatAgentToolsSettingId } from './terminalChatAgentToolsConfiguration.js';
import { IRemoteAgentEnvironment } from '../../../../../platform/remote/common/remoteAgentEnvironment.js';
export const ITerminalSandboxService = createDecorator<ITerminalSandboxService>('terminalSandboxService');
@ -34,13 +34,17 @@ export interface ITerminalSandboxService {
export class TerminalSandboxService extends Disposable implements ITerminalSandboxService {
readonly _serviceBrand: undefined;
private _srtPath: string;
private _srtPath: string | undefined;
private _srtPathResolved = false;
private _execPath?: string;
private _sandboxConfigPath: string | undefined;
private _needsForceUpdateConfigFile = true;
private _tempDir: URI | undefined;
private _sandboxSettingsId: string | undefined;
private _os: Promise<OperatingSystem>;
private _remoteEnvDetailsPromise: Promise<IRemoteAgentEnvironment | null>;
private _remoteEnvDetails: IRemoteAgentEnvironment | null = null;
private _appRoot: string;
private _os: OperatingSystem = OS;
constructor(
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ -50,14 +54,12 @@ export class TerminalSandboxService extends Disposable implements ITerminalSandb
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
) {
super();
const appRoot = dirname(FileAccess.asFileUri('').fsPath);
// srt path is dist/cli.js inside the sandbox-runtime package.
this._srtPath = join(appRoot, 'node_modules', '@anthropic-ai', 'sandbox-runtime', 'dist', 'cli.js');
this._appRoot = dirname(FileAccess.asFileUri('').path);
// Get the node executable path from native environment service if available (Electron's execPath with ELECTRON_RUN_AS_NODE)
const nativeEnv = this._environmentService as IEnvironmentService & { execPath?: string };
this._execPath = nativeEnv.execPath;
this._sandboxSettingsId = generateUuid();
this._os = this._remoteAgentService.getEnvironment().then(remoteEnv => remoteEnv?.os ?? OS);
this._remoteEnvDetailsPromise = this._remoteAgentService.getEnvironment();
this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, (e: IConfigurationChangeEvent | undefined) => {
// If terminal sandbox settings changed, update sandbox config.
@ -73,8 +75,9 @@ export class TerminalSandboxService extends Disposable implements ITerminalSandb
}
public async isEnabled(): Promise<boolean> {
const os = await this._os;
if (os === OperatingSystem.Windows) {
this._remoteEnvDetails = await this._remoteEnvDetailsPromise;
this._os = this._remoteEnvDetails ? this._remoteEnvDetails.os : OS;
if (this._os === OperatingSystem.Windows) {
return false;
}
return this._configurationService.getValue<boolean>(TerminalChatAgentToolsSettingId.TerminalSandboxEnabled);
@ -87,10 +90,16 @@ export class TerminalSandboxService extends Disposable implements ITerminalSandb
if (!this._execPath) {
throw new Error('Executable path not set to run sandbox commands');
}
if (!this._srtPath) {
throw new Error('Sandbox runtime path not resolved');
}
// Use ELECTRON_RUN_AS_NODE=1 to make Electron executable behave as Node.js
// TMPDIR must be set as environment variable before the command
// Use -c to pass the command string directly (like sh -c), avoiding argument parsing issues
const wrappedCommand = `"${this._execPath}" "${this._srtPath}" TMPDIR=${this._tempDir.fsPath} --settings "${this._sandboxConfigPath}" -c "${command}"`;
const wrappedCommand = `"${this._execPath}" "${this._srtPath}" TMPDIR=${this._tempDir.path} --settings "${this._sandboxConfigPath}" -c "${command}"`;
if (this._remoteEnvDetails) {
return `${wrappedCommand}`;
}
return `ELECTRON_RUN_AS_NODE=1 ${wrappedCommand}`;
}
@ -103,6 +112,7 @@ export class TerminalSandboxService extends Disposable implements ITerminalSandb
}
public async getSandboxConfigPath(forceRefresh: boolean = false): Promise<string | undefined> {
await this._resolveSrtPath();
if (!this._sandboxConfigPath || forceRefresh || this._needsForceUpdateConfigFile) {
this._sandboxConfigPath = await this._createSandboxConfig();
this._needsForceUpdateConfigFile = false;
@ -110,44 +120,70 @@ export class TerminalSandboxService extends Disposable implements ITerminalSandb
return this._sandboxConfigPath;
}
private async _resolveSrtPath(): Promise<void> {
if (this._srtPathResolved) {
return;
}
this._srtPathResolved = true;
const remoteEnv = this._remoteEnvDetails || await this._remoteEnvDetailsPromise;
if (!remoteEnv) {
// srt path is dist/cli.js inside the sandbox-runtime package.
this._srtPath = this._pathJoin(this._appRoot, 'node_modules', '@anthropic-ai', 'sandbox-runtime', 'dist', 'cli.js');
return;
}
this._appRoot = remoteEnv.appRoot.path;
this._execPath = this._pathJoin(this._appRoot, 'node');
this._srtPath = this._pathJoin(this._appRoot, 'node_modules', '@anthropic-ai', 'sandbox-runtime', 'dist', 'cli.js');
}
private async _createSandboxConfig(): Promise<string | undefined> {
if (await this.isEnabled() && !this._tempDir) {
await this._initTempDir();
}
if (this._tempDir) {
const os = await this._os;
const networkSetting = this._configurationService.getValue<ITerminalSandboxSettings['network']>(TerminalChatAgentToolsSettingId.TerminalSandboxNetwork) ?? {};
const linuxFileSystemSetting = os === OperatingSystem.Linux
const linuxFileSystemSetting = this._os === OperatingSystem.Linux
? this._configurationService.getValue<ITerminalSandboxSettings['filesystem']>(TerminalChatAgentToolsSettingId.TerminalSandboxLinuxFileSystem) ?? {}
: {};
const macFileSystemSetting = os === OperatingSystem.Macintosh
const macFileSystemSetting = this._os === OperatingSystem.Macintosh
? this._configurationService.getValue<ITerminalSandboxSettings['filesystem']>(TerminalChatAgentToolsSettingId.TerminalSandboxMacFileSystem) ?? {}
: {};
const configFileUri = joinPath(this._tempDir, `vscode-sandbox-settings-${this._sandboxSettingsId}.json`);
const configFileUri = URI.joinPath(this._tempDir, `vscode-sandbox-settings-${this._sandboxSettingsId}.json`);
const sandboxSettings = {
network: {
allowedDomains: networkSetting.allowedDomains ?? [],
deniedDomains: networkSetting.deniedDomains ?? []
},
filesystem: {
denyRead: os === OperatingSystem.Macintosh ? macFileSystemSetting.denyRead : linuxFileSystemSetting.denyRead,
allowWrite: os === OperatingSystem.Macintosh ? macFileSystemSetting.allowWrite : linuxFileSystemSetting.allowWrite,
denyWrite: os === OperatingSystem.Macintosh ? macFileSystemSetting.denyWrite : linuxFileSystemSetting.denyWrite,
denyRead: this._os === OperatingSystem.Macintosh ? macFileSystemSetting.denyRead : linuxFileSystemSetting.denyRead,
allowWrite: this._os === OperatingSystem.Macintosh ? macFileSystemSetting.allowWrite : linuxFileSystemSetting.allowWrite,
denyWrite: this._os === OperatingSystem.Macintosh ? macFileSystemSetting.denyWrite : linuxFileSystemSetting.denyWrite,
}
};
this._sandboxConfigPath = configFileUri.fsPath;
this._sandboxConfigPath = configFileUri.path;
await this._fileService.createFile(configFileUri, VSBuffer.fromString(JSON.stringify(sandboxSettings, null, '\t')), { overwrite: true });
return this._sandboxConfigPath;
}
return undefined;
}
// Joins path segments according to the current OS.
private _pathJoin = (...segments: string[]) => {
const path = this._os === OperatingSystem.Windows ? win32 : posix;
return path.join(...segments);
};
private async _initTempDir(): Promise<void> {
if (await this.isEnabled()) {
this._needsForceUpdateConfigFile = true;
const environmentService = this._environmentService as IEnvironmentService & { tmpDir?: URI };
this._tempDir = environmentService.tmpDir;
const remoteEnv = this._remoteEnvDetails || await this._remoteEnvDetailsPromise;
if (remoteEnv) {
this._tempDir = remoteEnv.tmpDir;
} else {
const environmentService = this._environmentService as IEnvironmentService & { tmpDir?: URI };
this._tempDir = environmentService.tmpDir;
}
if (!this._tempDir) {
this._logService.warn('TerminalSandboxService: Cannot create sandbox settings file because no tmpDir is available in this environment');
}

View File

@ -29,6 +29,7 @@ export interface IRemoteAgentEnvironmentDTO {
pid: number;
connectionToken: string;
appRoot: UriComponents;
tmpDir: UriComponents;
settingsPath: UriComponents;
mcpResource: UriComponents;
logsPath: UriComponents;
@ -66,6 +67,7 @@ export class RemoteExtensionEnvironmentChannelClient {
pid: data.pid,
connectionToken: data.connectionToken,
appRoot: URI.revive(data.appRoot),
tmpDir: URI.revive(data.tmpDir),
settingsPath: URI.revive(data.settingsPath),
mcpResource: URI.revive(data.mcpResource),
logsPath: URI.revive(data.logsPath),