Path: blob/main/extensions/copilot/src/extension/onboardDebug/node/copilotDebugWorker/index.ts
13405 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 { randomBytes } from 'crypto';6import { createServer } from 'node:net';7import { tmpdir } from 'os';8import * as readline from 'readline';9import * as path from '../../../../util/vs/base/common/path';10import { openVscodeUri } from './open';11import { SimpleRPC } from './rpc';12import { IStartOptions } from './shared';1314// ⚠️⚠️⚠️15// This file is built into a standlone bundle, executed in a worker.16// Avoid including unnecessary dependencies!17//18// This is used on macOS and Linux. On Windows, you'll need to make changes19// in copilotDebugWorker.ps1 instead. This is because Electron on Windows20// is not built with support for console stdin.21// ⚠️⚠️⚠️2223const [_node, _script, callbackUrl, remoteCommand, ...args] = process.argv;2425const enum Flags {26Print = '--print',27NoCache = '--no-cache',28Help = '--help',29Save = '--save',30Once = '--once',31}3233const flagConfig = {34[Flags.Print]: false,35[Flags.NoCache]: false,36[Flags.Help]: false,37[Flags.Save]: false,38[Flags.Once]: false,39};4041while (args.length && flagConfig.hasOwnProperty(args[0])) {42flagConfig[args.shift() as Flags] = true;43}4445if (!args.length || flagConfig[Flags.Help]) {46console.log(`Usage: copilot-debug [${Object.keys(flagConfig).join('] [')}] <command> <args...>`);47console.log('');48console.log('Options:');49console.log(' --print Print the generated configuration without running it');50console.log(' --no-cache Generate a new configuration without checking the cache.');51console.log(' --save Save the configuration to your launch.json.');52console.log(' --once Exit after the debug session ends.');53console.log(' --help Print this help.');54process.exit(flagConfig[Flags.Help] ? 0 : 1);55}5657const rl = readline.createInterface({58input: process.stdin,59output: process.stdout,60});6162readline.emitKeypressEvents(process.stdin);63process.stdin.setRawMode(true);6465const server = createServer(socket => {66clearInterval(waitingMessage);6768const rpc = new SimpleRPC(socket);69rpc.registerMethod('output', ({ category, output }) => {70if (category === 'stderr') {71process.stderr.write(output);72} else if (category === 'stdout') {73process.stdout.write(output);74} else if (category !== 'telemetry' && output) {75console.log(output); // so that a newline is added76}7778return Promise.resolve();79});8081rpc.registerMethod('exit', async ({ code, error }) => {82if (error && !triedToStop) {83console.error(error);84}8586await Promise.all([87new Promise<void>(resolve => process.stdout.end(resolve)),88new Promise<void>(resolve => process.stderr.end(resolve)),89]).then(() => process.exit(code));90});9192let triedToStop = false;93function onInterrupt() {94if (triedToStop) {95process.exit(1);96} else {97triedToStop = true;98socket.end(() => {99process.exit(1);100});101}102}103104process.on('SIGINT', onInterrupt);105process.stdin.on('keypress', (_str, key) => {106if (key.sequence === '\x03' || (key.name === 'c' && (key.ctrl || key.meta))) {107onInterrupt();108}109});110111rpc.registerMethod('question', (r: { message: string; defaultValue: string; singleKey?: boolean }) => {112return new Promise((resolve) => {113if (r.singleKey) {114console.log(r.message);115116const onKeyPress = (str: string | undefined) => {117if (str) {118process.stdout.write('\x08');119process.stdin.off('keypress', onKeyPress);120resolve(str === '\n' || str === '\r' ? 'Enter' : (str?.toUpperCase() || ''));121}122};123124process.stdin.on('keypress', onKeyPress);125} else {126rl.question(`${r.message} [${r.defaultValue}] `, resolve);127}128});129});130131rpc.registerMethod('confirm', (r: { message: string; defaultValue: boolean }) => {132return new Promise((resolve) => {133rl.question(`${r.message} [${r.defaultValue ? 'Y/n' : 'y/N'}] `, (answer) => {134resolve(answer === '' ? r.defaultValue : answer.toLowerCase()[0] === 'y');135});136});137});138139const opts: IStartOptions = {140cwd: process.cwd(),141args,142forceNew: flagConfig[Flags.NoCache],143printOnly: flagConfig[Flags.Print],144save: flagConfig[Flags.Save],145once: flagConfig[Flags.Once],146};147148rpc.callMethod('start', opts);149});150151const waitingMessage = setInterval(() => {152console.log('> Waiting for VS Code to connect...');153}, 2000);154155const pipeName = `copilot-dbg.${process.pid}-${randomBytes(4).toString('hex')}.sock`;156const pipePath = path.join(process.platform === 'win32' ? '\\\\.\\pipe\\' : tmpdir(), pipeName);157158server.listen(pipePath, () => {159openVscodeUri(remoteCommand, callbackUrl + (process.platform === 'win32' ? `/${pipeName}` : pipePath)).then(160() => {161// no-op162},163error => {164console.error('Failed to open the activation URI:', error);165process.exit(1);166}167);168});169170171