Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/onboardDebug/node/copilotDebugWorker/index.ts
13405 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 { randomBytes } from 'crypto';
7
import { createServer } from 'node:net';
8
import { tmpdir } from 'os';
9
import * as readline from 'readline';
10
import * as path from '../../../../util/vs/base/common/path';
11
import { openVscodeUri } from './open';
12
import { SimpleRPC } from './rpc';
13
import { IStartOptions } from './shared';
14
15
// ⚠️⚠️⚠️
16
// This file is built into a standlone bundle, executed in a worker.
17
// Avoid including unnecessary dependencies!
18
//
19
// This is used on macOS and Linux. On Windows, you'll need to make changes
20
// in copilotDebugWorker.ps1 instead. This is because Electron on Windows
21
// is not built with support for console stdin.
22
// ⚠️⚠️⚠️
23
24
const [_node, _script, callbackUrl, remoteCommand, ...args] = process.argv;
25
26
const enum Flags {
27
Print = '--print',
28
NoCache = '--no-cache',
29
Help = '--help',
30
Save = '--save',
31
Once = '--once',
32
}
33
34
const flagConfig = {
35
[Flags.Print]: false,
36
[Flags.NoCache]: false,
37
[Flags.Help]: false,
38
[Flags.Save]: false,
39
[Flags.Once]: false,
40
};
41
42
while (args.length && flagConfig.hasOwnProperty(args[0])) {
43
flagConfig[args.shift() as Flags] = true;
44
}
45
46
if (!args.length || flagConfig[Flags.Help]) {
47
console.log(`Usage: copilot-debug [${Object.keys(flagConfig).join('] [')}] <command> <args...>`);
48
console.log('');
49
console.log('Options:');
50
console.log(' --print Print the generated configuration without running it');
51
console.log(' --no-cache Generate a new configuration without checking the cache.');
52
console.log(' --save Save the configuration to your launch.json.');
53
console.log(' --once Exit after the debug session ends.');
54
console.log(' --help Print this help.');
55
process.exit(flagConfig[Flags.Help] ? 0 : 1);
56
}
57
58
const rl = readline.createInterface({
59
input: process.stdin,
60
output: process.stdout,
61
});
62
63
readline.emitKeypressEvents(process.stdin);
64
process.stdin.setRawMode(true);
65
66
const server = createServer(socket => {
67
clearInterval(waitingMessage);
68
69
const rpc = new SimpleRPC(socket);
70
rpc.registerMethod('output', ({ category, output }) => {
71
if (category === 'stderr') {
72
process.stderr.write(output);
73
} else if (category === 'stdout') {
74
process.stdout.write(output);
75
} else if (category !== 'telemetry' && output) {
76
console.log(output); // so that a newline is added
77
}
78
79
return Promise.resolve();
80
});
81
82
rpc.registerMethod('exit', async ({ code, error }) => {
83
if (error && !triedToStop) {
84
console.error(error);
85
}
86
87
await Promise.all([
88
new Promise<void>(resolve => process.stdout.end(resolve)),
89
new Promise<void>(resolve => process.stderr.end(resolve)),
90
]).then(() => process.exit(code));
91
});
92
93
let triedToStop = false;
94
function onInterrupt() {
95
if (triedToStop) {
96
process.exit(1);
97
} else {
98
triedToStop = true;
99
socket.end(() => {
100
process.exit(1);
101
});
102
}
103
}
104
105
process.on('SIGINT', onInterrupt);
106
process.stdin.on('keypress', (_str, key) => {
107
if (key.sequence === '\x03' || (key.name === 'c' && (key.ctrl || key.meta))) {
108
onInterrupt();
109
}
110
});
111
112
rpc.registerMethod('question', (r: { message: string; defaultValue: string; singleKey?: boolean }) => {
113
return new Promise((resolve) => {
114
if (r.singleKey) {
115
console.log(r.message);
116
117
const onKeyPress = (str: string | undefined) => {
118
if (str) {
119
process.stdout.write('\x08');
120
process.stdin.off('keypress', onKeyPress);
121
resolve(str === '\n' || str === '\r' ? 'Enter' : (str?.toUpperCase() || ''));
122
}
123
};
124
125
process.stdin.on('keypress', onKeyPress);
126
} else {
127
rl.question(`${r.message} [${r.defaultValue}] `, resolve);
128
}
129
});
130
});
131
132
rpc.registerMethod('confirm', (r: { message: string; defaultValue: boolean }) => {
133
return new Promise((resolve) => {
134
rl.question(`${r.message} [${r.defaultValue ? 'Y/n' : 'y/N'}] `, (answer) => {
135
resolve(answer === '' ? r.defaultValue : answer.toLowerCase()[0] === 'y');
136
});
137
});
138
});
139
140
const opts: IStartOptions = {
141
cwd: process.cwd(),
142
args,
143
forceNew: flagConfig[Flags.NoCache],
144
printOnly: flagConfig[Flags.Print],
145
save: flagConfig[Flags.Save],
146
once: flagConfig[Flags.Once],
147
};
148
149
rpc.callMethod('start', opts);
150
});
151
152
const waitingMessage = setInterval(() => {
153
console.log('> Waiting for VS Code to connect...');
154
}, 2000);
155
156
const pipeName = `copilot-dbg.${process.pid}-${randomBytes(4).toString('hex')}.sock`;
157
const pipePath = path.join(process.platform === 'win32' ? '\\\\.\\pipe\\' : tmpdir(), pipeName);
158
159
server.listen(pipePath, () => {
160
openVscodeUri(remoteCommand, callbackUrl + (process.platform === 'win32' ? `/${pipeName}` : pipePath)).then(
161
() => {
162
// no-op
163
},
164
error => {
165
console.error('Failed to open the activation URI:', error);
166
process.exit(1);
167
}
168
);
169
});
170
171