@@ -41,7 +41,7 @@ export class CopilotCLITerminalIntegration extends Disposable implements ICopilo
4141 @IEnvService private readonly envService : IEnvService ,
4242 ) {
4343 super ( ) ;
44- this . updateGHTokenInTerminalEnvVars ( ) ;
44+ void this . updateGHTokenInTerminalEnvVars ( ) . catch ( console . error ) ;
4545 this . initialization = this . initialize ( ) ;
4646 }
4747
@@ -62,13 +62,13 @@ export class CopilotCLITerminalIntegration extends Disposable implements ICopilo
6262 await fs . mkdir ( storageLocation , { recursive : true } ) ;
6363
6464 if ( process . platform === 'win32' ) {
65- this . shellScriptPath = path . join ( storageLocation , `${ COPILOT_CLI_COMMAND } .ps1` ) ;
66- this . powershellScriptPath = this . shellScriptPath ;
67- await fs . writeFile ( this . shellScriptPath , powershellScript ) ;
65+ this . powershellScriptPath = path . join ( storageLocation , `${ COPILOT_CLI_COMMAND } .ps1` ) ;
66+ await fs . writeFile ( this . powershellScriptPath , powershellScript ) ;
6867 const copilotPowershellScript = `@echo off
69- powershell -ExecutionPolicy Bypass -File "${ this . shellScriptPath } " %*
68+ powershell -ExecutionPolicy Bypass -File "${ this . powershellScriptPath } " %*
7069` ;
71- await fs . writeFile ( path . join ( storageLocation , `${ COPILOT_CLI_COMMAND } .bat` ) , copilotPowershellScript ) ;
70+ this . shellScriptPath = path . join ( storageLocation , `${ COPILOT_CLI_COMMAND } .bat` ) ;
71+ await fs . writeFile ( this . shellScriptPath , copilotPowershellScript ) ;
7272 } else {
7373 const copilotShellScript = `#!/bin/sh
7474unset NODE_OPTIONS
@@ -96,7 +96,7 @@ ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${path.join(storageLocation, COPIL
9696 await this . updateGHTokenInTerminalEnvVars ( ) ;
9797 await this . initialization ;
9898
99- const shellPathAndArgs = this . getShellInfo ( cliArgs ) ;
99+ const shellPathAndArgs = await this . getShellInfo ( cliArgs ) ;
100100 if ( shellPathAndArgs ) {
101101 const options = getCommonTerminalOptions ( name ) ;
102102 options . shellPath = shellPathAndArgs . shellPath ;
@@ -144,7 +144,7 @@ ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${path.join(storageLocation, COPIL
144144 }
145145 }
146146
147- private getShellInfo ( cliArgs : string [ ] ) : { shellPath : string ; shellArgs : string [ ] ; iconPath ?: ThemeIcon } | undefined {
147+ private async getShellInfo ( cliArgs : string [ ] ) : Promise < { shellPath : string ; shellArgs : string [ ] ; iconPath ?: ThemeIcon } | undefined > {
148148 const configPlatform = process . platform === 'win32' ? 'windows' : process . platform === 'darwin' ? 'osx' : 'linux' ;
149149 const defaultProfile = this . getDefaultShellProfile ( ) ;
150150 if ( ! defaultProfile ) {
@@ -164,7 +164,8 @@ ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${path.join(storageLocation, COPIL
164164 //
165165 }
166166 const shellArgs = Array . isArray ( profile . args ) ? profile . args : [ ] ;
167- const shellPath = ( ( Array . isArray ( profile . path ) && profile . path . length ) ? profile . path [ 0 ] : ! Array . isArray ( profile . path ) ? profile . path : undefined ) || this . envService . shell ;
167+ const paths = profile . path ? ( Array . isArray ( profile . path ) ? profile . path : [ profile . path ] ) : [ ] ;
168+ const shellPath = ( await getFirstAvailablePath ( paths ) ) || this . envService . shell ;
168169 if ( defaultProfile === 'zsh' && this . shellScriptPath ) {
169170 return {
170171 shellPath : shellPath || 'zsh' ,
@@ -190,14 +191,14 @@ ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${path.join(storageLocation, COPIL
190191 iconPath
191192 } ;
192193 } else if ( defaultProfile === 'Command Prompt' && this . shellScriptPath && configPlatform === 'windows' ) {
193- // return {
194- // shellPath: this.shellScriptPath,
195- // shellArgs: cliArgs,
196- // iconPath
197- // };
198- return ;
194+ return {
195+ shellPath : shellPath || 'cmd.exe' ,
196+ shellArgs : [ '/c' , this . shellScriptPath , ...cliArgs ] ,
197+ iconPath
198+ } ;
199199 }
200200 }
201+
201202 private getDefaultShellProfile ( ) : string | undefined {
202203 const configPlatform = process . platform === 'win32' ? 'windows' : process . platform === 'darwin' ? 'osx' : 'linux' ;
203204 const defaultProfile = workspace . getConfiguration ( 'terminal' ) . get < string | undefined > ( `integrated.defaultProfile.${ configPlatform } ` ) ;
@@ -238,3 +239,39 @@ function getCommonTerminalOptions(name: string): TerminalOptions {
238239 } ;
239240}
240241
242+ const pathValidations = new Map < string , boolean > ( ) ;
243+ async function getFirstAvailablePath ( paths : string [ ] ) : Promise < string | undefined > {
244+ for ( const p of paths ) {
245+ // Sometimes we can have paths like `${env:HOME}\Systemycmd.exe` which need to be resolved
246+ const resolvedPath = resolveEnvVariables ( p ) ;
247+ if ( pathValidations . get ( resolvedPath ) === true ) {
248+ return resolvedPath ;
249+ }
250+ if ( pathValidations . get ( resolvedPath ) === false ) {
251+ continue ;
252+ }
253+ // Possible its just a command name without path
254+ if ( path . basename ( p ) === p ) {
255+ return p ;
256+ }
257+ try {
258+ const stat = await fs . stat ( resolvedPath ) ;
259+ if ( stat . isFile ( ) ) {
260+ pathValidations . set ( resolvedPath , true ) ;
261+ return resolvedPath ;
262+ }
263+ pathValidations . set ( resolvedPath , false ) ;
264+ } catch {
265+ // Ignore errors and continue checking other paths
266+ pathValidations . set ( resolvedPath , false ) ;
267+ }
268+ }
269+ return undefined ;
270+ }
271+
272+ function resolveEnvVariables ( value : string ) : string {
273+ return value . replace ( / \$ \{ e n v : ( [ ^ } ] + ) \} / g, ( match , envVarName ) => {
274+ const envValue = process . env [ envVarName ] ;
275+ return envValue !== undefined ? envValue : match ;
276+ } ) ;
277+ }
0 commit comments