Path: blob/main/src/vs/platform/agentHost/node/nodeAgentHostStarter.ts
13394 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Emitter } from '../../../base/common/event.js';6import { Disposable, DisposableStore } from '../../../base/common/lifecycle.js';7import { FileAccess, Schemas } from '../../../base/common/network.js';8import { Client, IIPCOptions } from '../../../base/parts/ipc/node/ipc.cp.js';9import { IConfigurationService } from '../../configuration/common/configuration.js';10import { IEnvironmentService, INativeEnvironmentService } from '../../environment/common/environment.js';11import { parseAgentHostDebugPort } from '../../environment/node/environmentService.js';12import { ILogService } from '../../log/common/log.js';13import { getResolvedShellEnv } from '../../shell/node/shellEnv.js';14import { IAgentHostConnection, IAgentHostStarter } from '../common/agent.js';1516/**17* Options for configuring the agent host WebSocket server in the child process.18* When set, the agent host exposes a WebSocket endpoint for external clients.19*/20export interface IAgentHostWebSocketConfig {21/** TCP port to listen on. Mutually exclusive with `socketPath`. */22readonly port?: string;23/** Unix domain socket / named pipe path. Takes precedence over `port`. */24readonly socketPath?: string;25/** Host/IP to bind to. */26readonly host?: string;27/** Connection token value. When set, WebSocket clients must present this token. */28readonly connectionToken?: string;29}3031/**32* Spawns the agent host as a Node child process (fallback when33* Electron utility process is unavailable, e.g. dev/test).34*/35export class NodeAgentHostStarter extends Disposable implements IAgentHostStarter {3637private _wsConfig: IAgentHostWebSocketConfig | undefined;3839private readonly _onRequestConnection = this._register(new Emitter<void>());40readonly onRequestConnection = this._onRequestConnection.event;4142constructor(43@IConfigurationService private readonly _configurationService: IConfigurationService,44@IEnvironmentService private readonly _environmentService: INativeEnvironmentService,45@ILogService private readonly _logService: ILogService,46) {47super();48}4950/**51* Configures the child process to also start a WebSocket server.52* Must be called before {@link start}. Triggers eager process start53* via {@link onRequestConnection}.54*/55setWebSocketConfig(config: IAgentHostWebSocketConfig): void {56this._wsConfig = config;57// Signal the process manager to start immediately rather than58// waiting for a renderer window to connect.59this._onRequestConnection.fire();60}6162async start(): Promise<IAgentHostConnection> {63// Resolve user shell environment so spawned tools/terminals inherit64// PATH and other vars from the user's login shell (macOS/Linux).65const shellEnv = await this._resolveShellEnv();6667const env: Record<string, string> = {68...shellEnv as Record<string, string>,69VSCODE_ESM_ENTRYPOINT: 'vs/platform/agentHost/node/agentHostMain',70VSCODE_PIPE_LOGGING: 'true',71VSCODE_VERBOSE_LOGGING: 'true',72};7374// Forward WebSocket server configuration to the child process via env vars75if (this._wsConfig) {76if (this._wsConfig.port) {77env['VSCODE_AGENT_HOST_PORT'] = this._wsConfig.port;78}79if (this._wsConfig.socketPath) {80env['VSCODE_AGENT_HOST_SOCKET_PATH'] = this._wsConfig.socketPath;81}82if (this._wsConfig.host) {83env['VSCODE_AGENT_HOST_HOST'] = this._wsConfig.host;84}85if (this._wsConfig.connectionToken) {86env['VSCODE_AGENT_HOST_CONNECTION_TOKEN'] = this._wsConfig.connectionToken;87}88}8990const opts: IIPCOptions = {91serverName: 'Agent Host',92args: ['--type=agentHost', '--logsPath', this._environmentService.logsHome.with({ scheme: Schemas.file }).fsPath],93env,94};9596const agentHostDebug = parseAgentHostDebugPort(this._environmentService.args, this._environmentService.isBuilt);97if (agentHostDebug) {98if (agentHostDebug.break && agentHostDebug.port) {99opts.debugBrk = agentHostDebug.port;100} else if (!agentHostDebug.break && agentHostDebug.port) {101opts.debug = agentHostDebug.port;102}103}104105const client = new Client(FileAccess.asFileUri('bootstrap-fork').fsPath, opts);106107const store = new DisposableStore();108store.add(client);109110return {111client,112store,113onDidProcessExit: client.onDidProcessExit114};115}116117private async _resolveShellEnv(): Promise<typeof process.env> {118try {119return await getResolvedShellEnv(this._configurationService, this._logService, this._environmentService.args, process.env);120} catch (error) {121this._logService.error('AgentHostStarter was unable to resolve shell environment', error);122return {};123}124}125}126127128