Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/node/extHostCLIServer.ts
3296 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 { createRandomIPCHandle } from '../../../base/parts/ipc/node/ipc.net.js';
7
import * as http from 'http';
8
import * as fs from 'fs';
9
import { IExtHostCommands } from '../common/extHostCommands.js';
10
import { IWindowOpenable, IOpenWindowOptions } from '../../../platform/window/common/window.js';
11
import { URI } from '../../../base/common/uri.js';
12
import { ILogService } from '../../../platform/log/common/log.js';
13
import { hasWorkspaceFileExtension } from '../../../platform/workspace/common/workspace.js';
14
15
export interface OpenCommandPipeArgs {
16
type: 'open';
17
fileURIs?: string[];
18
folderURIs?: string[];
19
forceNewWindow?: boolean;
20
diffMode?: boolean;
21
mergeMode?: boolean;
22
addMode?: boolean;
23
removeMode?: boolean;
24
gotoLineMode?: boolean;
25
forceReuseWindow?: boolean;
26
waitMarkerFilePath?: string;
27
remoteAuthority?: string | null;
28
}
29
30
export interface OpenExternalCommandPipeArgs {
31
type: 'openExternal';
32
uris: string[];
33
}
34
35
export interface StatusPipeArgs {
36
type: 'status';
37
}
38
39
export interface ExtensionManagementPipeArgs {
40
type: 'extensionManagement';
41
list?: { showVersions?: boolean; category?: string };
42
install?: string[];
43
uninstall?: string[];
44
force?: boolean;
45
}
46
47
export type PipeCommand = OpenCommandPipeArgs | StatusPipeArgs | OpenExternalCommandPipeArgs | ExtensionManagementPipeArgs;
48
49
export interface ICommandsExecuter {
50
executeCommand<T>(id: string, ...args: any[]): Promise<T>;
51
}
52
53
export class CLIServerBase {
54
private readonly _server: http.Server;
55
56
constructor(
57
private readonly _commands: ICommandsExecuter,
58
private readonly logService: ILogService,
59
private readonly _ipcHandlePath: string,
60
) {
61
this._server = http.createServer((req, res) => this.onRequest(req, res));
62
this.setup().catch(err => {
63
logService.error(err);
64
return '';
65
});
66
}
67
68
public get ipcHandlePath() {
69
return this._ipcHandlePath;
70
}
71
72
private async setup(): Promise<string> {
73
try {
74
this._server.listen(this.ipcHandlePath);
75
this._server.on('error', err => this.logService.error(err));
76
} catch (err) {
77
this.logService.error('Could not start open from terminal server.');
78
}
79
80
return this._ipcHandlePath;
81
}
82
83
private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
84
const sendResponse = (statusCode: number, returnObj: string | undefined) => {
85
res.writeHead(statusCode, { 'content-type': 'application/json' });
86
res.end(JSON.stringify(returnObj || null), (err?: any) => err && this.logService.error(err)); // CodeQL [SM01524] Only the message portion of errors are passed in.
87
};
88
89
const chunks: string[] = [];
90
req.setEncoding('utf8');
91
req.on('data', (d: string) => chunks.push(d));
92
req.on('end', async () => {
93
try {
94
const data: PipeCommand | any = JSON.parse(chunks.join(''));
95
let returnObj: string | undefined;
96
switch (data.type) {
97
case 'open':
98
returnObj = await this.open(data);
99
break;
100
case 'openExternal':
101
returnObj = await this.openExternal(data);
102
break;
103
case 'status':
104
returnObj = await this.getStatus(data);
105
break;
106
case 'extensionManagement':
107
returnObj = await this.manageExtensions(data);
108
break;
109
default:
110
sendResponse(404, `Unknown message type: ${data.type}`);
111
break;
112
}
113
sendResponse(200, returnObj);
114
} catch (e) {
115
const message = e instanceof Error ? e.message : JSON.stringify(e);
116
sendResponse(500, message);
117
this.logService.error('Error while processing pipe request', e);
118
}
119
});
120
}
121
122
private async open(data: OpenCommandPipeArgs): Promise<undefined> {
123
const { fileURIs, folderURIs, forceNewWindow, diffMode, mergeMode, addMode, removeMode, forceReuseWindow, gotoLineMode, waitMarkerFilePath, remoteAuthority } = data;
124
const urisToOpen: IWindowOpenable[] = [];
125
if (Array.isArray(folderURIs)) {
126
for (const s of folderURIs) {
127
try {
128
urisToOpen.push({ folderUri: URI.parse(s) });
129
} catch (e) {
130
// ignore
131
}
132
}
133
}
134
if (Array.isArray(fileURIs)) {
135
for (const s of fileURIs) {
136
try {
137
if (hasWorkspaceFileExtension(s)) {
138
urisToOpen.push({ workspaceUri: URI.parse(s) });
139
} else {
140
urisToOpen.push({ fileUri: URI.parse(s) });
141
}
142
} catch (e) {
143
// ignore
144
}
145
}
146
}
147
const waitMarkerFileURI = waitMarkerFilePath ? URI.file(waitMarkerFilePath) : undefined;
148
const preferNewWindow = !forceReuseWindow && !waitMarkerFileURI && !addMode && !removeMode;
149
const windowOpenArgs: IOpenWindowOptions = { forceNewWindow, diffMode, mergeMode, addMode, removeMode, gotoLineMode, forceReuseWindow, preferNewWindow, waitMarkerFileURI, remoteAuthority };
150
this._commands.executeCommand('_remoteCLI.windowOpen', urisToOpen, windowOpenArgs);
151
}
152
153
private async openExternal(data: OpenExternalCommandPipeArgs): Promise<undefined> {
154
for (const uriString of data.uris) {
155
const uri = URI.parse(uriString);
156
const urioOpen = uri.scheme === 'file' ? uri : uriString; // workaround for #112577
157
await this._commands.executeCommand('_remoteCLI.openExternal', urioOpen);
158
}
159
}
160
161
private async manageExtensions(data: ExtensionManagementPipeArgs): Promise<string | undefined> {
162
const toExtOrVSIX = (inputs: string[] | undefined) => inputs?.map(input => /\.vsix$/i.test(input) ? URI.parse(input) : input);
163
const commandArgs = {
164
list: data.list,
165
install: toExtOrVSIX(data.install),
166
uninstall: toExtOrVSIX(data.uninstall),
167
force: data.force
168
};
169
return await this._commands.executeCommand<string | undefined>('_remoteCLI.manageExtensions', commandArgs);
170
}
171
172
private async getStatus(data: StatusPipeArgs): Promise<string | undefined> {
173
return await this._commands.executeCommand<string | undefined>('_remoteCLI.getSystemStatus');
174
}
175
176
dispose(): void {
177
this._server.close();
178
179
if (this._ipcHandlePath && process.platform !== 'win32' && fs.existsSync(this._ipcHandlePath)) {
180
fs.unlinkSync(this._ipcHandlePath);
181
}
182
}
183
}
184
185
export class CLIServer extends CLIServerBase {
186
constructor(
187
@IExtHostCommands commands: IExtHostCommands,
188
@ILogService logService: ILogService
189
) {
190
super(commands, logService, createRandomIPCHandle());
191
}
192
}
193
194