Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/agentHost/electron-main/electronAgentHostStarter.ts
13394 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Disposable, DisposableStore, toDisposable } from '../../../base/common/lifecycle.js';
7
import { DeferredPromise } from '../../../base/common/async.js';
8
import { Emitter } from '../../../base/common/event.js';
9
import { IpcMainEvent } from 'electron';
10
import { validatedIpcMain } from '../../../base/parts/ipc/electron-main/ipcMain.js';
11
import { Client as MessagePortClient } from '../../../base/parts/ipc/electron-main/ipc.mp.js';
12
import { IConfigurationService } from '../../configuration/common/configuration.js';
13
import { IEnvironmentMainService } from '../../environment/electron-main/environmentMainService.js';
14
import { parseAgentHostDebugPort } from '../../environment/node/environmentService.js';
15
import { ILifecycleMainService } from '../../lifecycle/electron-main/lifecycleMainService.js';
16
import { ILogService } from '../../log/common/log.js';
17
import { Schemas } from '../../../base/common/network.js';
18
import { getResolvedShellEnv } from '../../shell/node/shellEnv.js';
19
import { NullTelemetryService } from '../../telemetry/common/telemetryUtils.js';
20
import { UtilityProcess } from '../../utilityProcess/electron-main/utilityProcess.js';
21
import { IAgentHostConnection, IAgentHostStarter } from '../common/agent.js';
22
import { deepClone } from '../../../base/common/objects.js';
23
24
export class ElectronAgentHostStarter extends Disposable implements IAgentHostStarter {
25
26
private utilityProcess: UtilityProcess | undefined = undefined;
27
private utilityProcessStarted: DeferredPromise<void> | undefined = undefined;
28
29
private readonly _onRequestConnection = this._register(new Emitter<void>());
30
readonly onRequestConnection = this._onRequestConnection.event;
31
private readonly _onWillShutdown = this._register(new Emitter<void>());
32
readonly onWillShutdown = this._onWillShutdown.event;
33
34
constructor(
35
@IConfigurationService private readonly _configurationService: IConfigurationService,
36
@IEnvironmentMainService private readonly _environmentMainService: IEnvironmentMainService,
37
@ILifecycleMainService private readonly _lifecycleMainService: ILifecycleMainService,
38
@ILogService private readonly _logService: ILogService,
39
) {
40
super();
41
42
this._register(this._lifecycleMainService.onWillShutdown(() => this._onWillShutdown.fire()));
43
44
// Listen for new windows to establish a direct MessagePort connection to the agent host
45
const onWindowConnection = (e: IpcMainEvent, nonce: string) => this._onWindowConnection(e, nonce);
46
validatedIpcMain.on('vscode:createAgentHostMessageChannel', onWindowConnection);
47
this._register(toDisposable(() => {
48
validatedIpcMain.removeListener('vscode:createAgentHostMessageChannel', onWindowConnection);
49
}));
50
}
51
52
async start(): Promise<IAgentHostConnection> {
53
this.utilityProcess = new UtilityProcess(this._logService, NullTelemetryService, this._lifecycleMainService);
54
this.utilityProcessStarted = new DeferredPromise<void>();
55
56
const inspectParams = parseAgentHostDebugPort(this._environmentMainService.args, this._environmentMainService.isBuilt);
57
const execArgv = inspectParams.port ? [
58
'--nolazy',
59
`--inspect${inspectParams.break ? '-brk' : ''}=${inspectParams.port}`
60
] : undefined;
61
62
// Resolve user shell environment so spawned tools/terminals inherit
63
// PATH and other vars from the user's login shell (macOS/Linux GUI launches).
64
const shellEnv = await this._resolveShellEnv();
65
66
this.utilityProcess.start({
67
type: 'agentHost',
68
name: 'agent-host',
69
entryPoint: 'vs/platform/agentHost/node/agentHostMain',
70
execArgv,
71
args: ['--logsPath', this._environmentMainService.logsHome.with({ scheme: Schemas.file }).fsPath],
72
env: {
73
...deepClone(process.env),
74
...shellEnv,
75
VSCODE_ESM_ENTRYPOINT: 'vs/platform/agentHost/node/agentHostMain',
76
VSCODE_PIPE_LOGGING: 'true',
77
VSCODE_VERBOSE_LOGGING: 'true',
78
}
79
});
80
81
this.utilityProcessStarted.complete();
82
83
const port = this.utilityProcess.connect();
84
const client = new MessagePortClient(port, 'agentHost');
85
86
const store = new DisposableStore();
87
store.add(client);
88
store.add(this.utilityProcess.onStderr(data => {
89
if (this._isExpectedStderr(data)) {
90
return;
91
}
92
this._logService.error(`[AgentHost:stderr] ${data}`);
93
}));
94
store.add(toDisposable(() => {
95
this.utilityProcess?.kill();
96
this.utilityProcess?.dispose();
97
this.utilityProcess = undefined;
98
this.utilityProcessStarted = undefined;
99
}));
100
101
return {
102
client,
103
store,
104
onDidProcessExit: this.utilityProcess.onExit,
105
};
106
}
107
108
private async _resolveShellEnv(): Promise<typeof process.env> {
109
try {
110
return await getResolvedShellEnv(this._configurationService, this._logService, this._environmentMainService.args, process.env);
111
} catch (error) {
112
this._logService.error('AgentHostStarter was unable to resolve shell environment', error);
113
return {};
114
}
115
}
116
117
private async _onWindowConnection(e: IpcMainEvent, nonce: string): Promise<void> {
118
this._onRequestConnection.fire();
119
120
// Wait for utilityProcess.start() to actually run before calling connect(),
121
// otherwise the MessagePort posted via connect() is silently dropped.
122
await this.utilityProcessStarted?.p;
123
124
if (!this.utilityProcess) {
125
this._logService.error('AgentHostStarter: cannot create window connection, agent host process is not running');
126
return;
127
}
128
129
const port = this.utilityProcess.connect();
130
131
if (e.sender.isDestroyed()) {
132
port.close();
133
return;
134
}
135
136
e.sender.postMessage('vscode:createAgentHostMessageChannelResult', nonce, [port]);
137
}
138
139
private static readonly _expectedStderrPatterns = [
140
'Most NODE_OPTIONs are not supported in packaged apps',
141
'Debugger listening on ws://',
142
'For help, see: https://nodejs.org/en/docs/inspector',
143
'ExperimentalWarning: SQLite is an experimental feature',
144
];
145
146
private _isExpectedStderr(data: string): boolean {
147
return ElectronAgentHostStarter._expectedStderrPatterns.some(pattern => data.includes(pattern));
148
}
149
}
150
151