@@ -11,12 +11,21 @@ import { ProxyHttpProvider } from "./app/proxy"
1111import { StaticHttpProvider } from "./app/static"
1212import { UpdateHttpProvider } from "./app/update"
1313import { VscodeHttpProvider } from "./app/vscode"
14- import { Args , bindAddrFromAllSources , optionDescriptions , parse , readConfigFile , setDefaults } from "./cli"
14+ import {
15+ Args ,
16+ bindAddrFromAllSources ,
17+ optionDescriptions ,
18+ parse ,
19+ readConfigFile ,
20+ setDefaults ,
21+ shouldOpenInExistingInstance ,
22+ shouldRunVsCodeCli ,
23+ } from "./cli"
1524import { coderCloudBind } from "./coder-cloud"
1625import { AuthType , HttpServer , HttpServerOptions } from "./http"
1726import { loadPlugins } from "./plugin"
1827import { generateCertificate , hash , humanPath , open } from "./util"
19- import { ipcMain , wrap } from "./wrapper"
28+ import { ipcMain , WrapperProcess } from "./wrapper"
2029
2130let pkg : { version ?: string ; commit ?: string } = { }
2231try {
2837const version = pkg . version || "development"
2938const commit = pkg . commit || "development"
3039
40+ export const runVsCodeCli = ( args : Args ) : void => {
41+ logger . debug ( "forking vs code cli..." )
42+ const vscode = cp . fork ( path . resolve ( __dirname , "../../lib/vscode/out/vs/server/fork" ) , [ ] , {
43+ env : {
44+ ...process . env ,
45+ CODE_SERVER_PARENT_PID : process . pid . toString ( ) ,
46+ } ,
47+ } )
48+ vscode . once ( "message" , ( message : any ) => {
49+ logger . debug ( "got message from VS Code" , field ( "message" , message ) )
50+ if ( message . type !== "ready" ) {
51+ logger . error ( "Unexpected response waiting for ready response" , field ( "type" , message . type ) )
52+ process . exit ( 1 )
53+ }
54+ const send : CliMessage = { type : "cli" , args }
55+ vscode . send ( send )
56+ } )
57+ vscode . once ( "error" , ( error ) => {
58+ logger . error ( "Got error from VS Code" , field ( "error" , error ) )
59+ process . exit ( 1 )
60+ } )
61+ vscode . on ( "exit" , ( code ) => process . exit ( code || 0 ) )
62+ }
63+
64+ export const openInExistingInstance = async ( args : Args , socketPath : string ) : Promise < void > => {
65+ const pipeArgs : OpenCommandPipeArgs & { fileURIs : string [ ] } = {
66+ type : "open" ,
67+ folderURIs : [ ] ,
68+ fileURIs : [ ] ,
69+ forceReuseWindow : args [ "reuse-window" ] ,
70+ forceNewWindow : args [ "new-window" ] ,
71+ }
72+
73+ const isDir = async ( path : string ) : Promise < boolean > => {
74+ try {
75+ const st = await fs . stat ( path )
76+ return st . isDirectory ( )
77+ } catch ( error ) {
78+ return false
79+ }
80+ }
81+
82+ for ( let i = 0 ; i < args . _ . length ; i ++ ) {
83+ const fp = path . resolve ( args . _ [ i ] )
84+ if ( await isDir ( fp ) ) {
85+ pipeArgs . folderURIs . push ( fp )
86+ } else {
87+ pipeArgs . fileURIs . push ( fp )
88+ }
89+ }
90+
91+ if ( pipeArgs . forceNewWindow && pipeArgs . fileURIs . length > 0 ) {
92+ logger . error ( "--new-window can only be used with folder paths" )
93+ process . exit ( 1 )
94+ }
95+
96+ if ( pipeArgs . folderURIs . length === 0 && pipeArgs . fileURIs . length === 0 ) {
97+ logger . error ( "Please specify at least one file or folder" )
98+ process . exit ( 1 )
99+ }
100+
101+ const vscode = http . request (
102+ {
103+ path : "/" ,
104+ method : "POST" ,
105+ socketPath,
106+ } ,
107+ ( response ) => {
108+ response . on ( "data" , ( message ) => {
109+ logger . debug ( "got message from VS Code" , field ( "message" , message . toString ( ) ) )
110+ } )
111+ } ,
112+ )
113+ vscode . on ( "error" , ( error : unknown ) => {
114+ logger . error ( "got error from VS Code" , field ( "error" , error ) )
115+ } )
116+ vscode . write ( JSON . stringify ( pipeArgs ) )
117+ vscode . end ( )
118+ }
119+
31120const main = async ( args : Args , configArgs : Args ) : Promise < void > => {
32121 if ( args . link ) {
33122 // If we're being exposed to the cloud, we listen on a random address and disable auth.
@@ -92,7 +181,7 @@ const main = async (args: Args, configArgs: Args): Promise<void> => {
92181
93182 await loadPlugins ( httpServer , args )
94183
95- ipcMain ( ) . onDispose ( ( ) => {
184+ ipcMain . onDispose ( ( ) => {
96185 httpServer . dispose ( ) . then ( ( errors ) => {
97186 errors . forEach ( ( error ) => logger . error ( error . message ) )
98187 } )
@@ -132,7 +221,9 @@ const main = async (args: Args, configArgs: Args): Promise<void> => {
132221 if ( serverAddress && ! options . socket && args . open ) {
133222 // The web socket doesn't seem to work if browsing with 0.0.0.0.
134223 const openAddress = serverAddress . replace ( / : \/ \/ 0 .0 .0 .0 / , "://localhost" )
135- await open ( openAddress ) . catch ( console . error )
224+ await open ( openAddress ) . catch ( ( error : Error ) => {
225+ logger . error ( "Failed to open" , field ( "address" , openAddress ) , field ( "error" , error ) )
226+ } )
136227 logger . info ( `Opened ${ openAddress } ` )
137228 }
138229
@@ -141,27 +232,32 @@ const main = async (args: Args, configArgs: Args): Promise<void> => {
141232 await coderCloudBind ( serverAddress ! , args . link . value )
142233 } catch ( err ) {
143234 logger . error ( err . message )
144- ipcMain ( ) . exit ( 1 )
235+ ipcMain . exit ( 1 )
145236 }
146237 }
147238}
148239
149240async function entry ( ) : Promise < void > {
150- const tryParse = async ( ) : Promise < [ Args , Args ] > => {
151- try {
152- const cliArgs = parse ( process . argv . slice ( 2 ) )
153- const configArgs = await readConfigFile ( cliArgs . config )
154- // This prioritizes the flags set in args over the ones in the config file.
155- let args = Object . assign ( configArgs , cliArgs )
156- args = await setDefaults ( args )
157- return [ args , configArgs ]
158- } catch ( error ) {
159- console . error ( error . message )
160- process . exit ( 1 )
161- }
241+ const tryParse = async ( ) : Promise < [ Args , Args , Args ] > => {
242+ const cliArgs = parse ( process . argv . slice ( 2 ) )
243+ const configArgs = await readConfigFile ( cliArgs . config )
244+ // This prioritizes the flags set in args over the ones in the config file.
245+ let args = Object . assign ( configArgs , cliArgs )
246+ args = await setDefaults ( args )
247+ return [ args , cliArgs , configArgs ]
248+ }
249+
250+ const [ args , cliArgs , configArgs ] = await tryParse ( )
251+
252+ // There's no need to check flags like --help or to spawn in an existing
253+ // instance for the child process because these would have already happened in
254+ // the parent and the child wouldn't have been spawned.
255+ if ( ipcMain . isChild ) {
256+ await ipcMain . handshake ( )
257+ ipcMain . preventExit ( )
258+ return main ( args , configArgs )
162259 }
163260
164- const [ args , configArgs ] = await tryParse ( )
165261 if ( args . help ) {
166262 console . log ( "code-server" , version , commit )
167263 console . log ( "" )
@@ -171,7 +267,10 @@ async function entry(): Promise<void> {
171267 optionDescriptions ( ) . forEach ( ( description ) => {
172268 console . log ( "" , description )
173269 } )
174- } else if ( args . version ) {
270+ return
271+ }
272+
273+ if ( args . version ) {
175274 if ( args . json ) {
176275 console . log ( {
177276 codeServer : version ,
@@ -181,83 +280,23 @@ async function entry(): Promise<void> {
181280 } else {
182281 console . log ( version , commit )
183282 }
184- process . exit ( 0 )
185- } else if ( args [ "list-extensions" ] || args [ "install-extension" ] || args [ "uninstall-extension" ] ) {
186- logger . debug ( "forking vs code cli..." )
187- const vscode = cp . fork ( path . resolve ( __dirname , "../../lib/vscode/out/vs/server/fork" ) , [ ] , {
188- env : {
189- ...process . env ,
190- CODE_SERVER_PARENT_PID : process . pid . toString ( ) ,
191- } ,
192- } )
193- vscode . once ( "message" , ( message : any ) => {
194- logger . debug ( "Got message from VS Code" , field ( "message" , message ) )
195- if ( message . type !== "ready" ) {
196- logger . error ( "Unexpected response waiting for ready response" )
197- process . exit ( 1 )
198- }
199- const send : CliMessage = { type : "cli" , args }
200- vscode . send ( send )
201- } )
202- vscode . once ( "error" , ( error ) => {
203- logger . error ( error . message )
204- process . exit ( 1 )
205- } )
206- vscode . on ( "exit" , ( code ) => process . exit ( code || 0 ) )
207- } else if ( process . env . VSCODE_IPC_HOOK_CLI ) {
208- const pipeArgs : OpenCommandPipeArgs = {
209- type : "open" ,
210- folderURIs : [ ] ,
211- forceReuseWindow : args [ "reuse-window" ] ,
212- forceNewWindow : args [ "new-window" ] ,
213- }
214- const isDir = async ( path : string ) : Promise < boolean > => {
215- try {
216- const st = await fs . stat ( path )
217- return st . isDirectory ( )
218- } catch ( error ) {
219- return false
220- }
221- }
222- for ( let i = 0 ; i < args . _ . length ; i ++ ) {
223- const fp = path . resolve ( args . _ [ i ] )
224- if ( await isDir ( fp ) ) {
225- pipeArgs . folderURIs . push ( fp )
226- } else {
227- if ( ! pipeArgs . fileURIs ) {
228- pipeArgs . fileURIs = [ ]
229- }
230- pipeArgs . fileURIs . push ( fp )
231- }
232- }
233- if ( pipeArgs . forceNewWindow && pipeArgs . fileURIs && pipeArgs . fileURIs . length > 0 ) {
234- logger . error ( "new-window can only be used with folder paths" )
235- process . exit ( 1 )
236- }
237- if ( pipeArgs . folderURIs . length === 0 && ( ! pipeArgs . fileURIs || pipeArgs . fileURIs . length === 0 ) ) {
238- logger . error ( "Please specify at least one file or folder argument" )
239- process . exit ( 1 )
240- }
241- const vscode = http . request (
242- {
243- path : "/" ,
244- method : "POST" ,
245- socketPath : process . env [ "VSCODE_IPC_HOOK_CLI" ] ,
246- } ,
247- ( res ) => {
248- res . on ( "data" , ( message ) => {
249- logger . debug ( "Got message from VS Code" , field ( "message" , message . toString ( ) ) )
250- } )
251- } ,
252- )
253- vscode . on ( "error" , ( err ) => {
254- logger . debug ( "Got error from VS Code" , field ( "error" , err ) )
255- } )
256- vscode . write ( JSON . stringify ( pipeArgs ) )
257- vscode . end ( )
258- } else {
259- wrap ( ( ) => main ( args , configArgs ) )
283+ return
284+ }
285+
286+ if ( shouldRunVsCodeCli ( args ) ) {
287+ return runVsCodeCli ( args )
260288 }
289+
290+ const socketPath = await shouldOpenInExistingInstance ( cliArgs )
291+ if ( socketPath ) {
292+ return openInExistingInstance ( args , socketPath )
293+ }
294+
295+ const wrapper = new WrapperProcess ( require ( "../../package.json" ) . version )
296+ return wrapper . start ( )
261297}
262298
263- entry ( )
299+ entry ( ) . catch ( ( error ) => {
300+ logger . error ( error . message )
301+ ipcMain . exit ( error )
302+ } )
0 commit comments