@@ -348,79 +348,77 @@ export async function main() {
348348 }
349349 }
350350
351- const partialConfig = await loadCliConfig ( settings . merged , sessionId , argv , {
352- projectHooks : settings . workspace . settings . hooks ,
353- } ) ;
354- adminControlsListner . setConfig ( partialConfig ) ;
355-
356- // Refresh auth to fetch remote admin settings from CCPA and before entering
357- // the sandbox because the sandbox will interfere with the Oauth2 web
358- // redirect.
359- let initialAuthFailed = false ;
360- if ( ! settings . merged . security . auth . useExternal && ! argv . isCommand ) {
361- try {
362- if (
363- partialConfig . isInteractive ( ) &&
364- settings . merged . security . auth . selectedType
365- ) {
366- const err = validateAuthMethod (
367- settings . merged . security . auth . selectedType ,
368- ) ;
369- if ( err ) {
370- throw new Error ( err ) ;
371- }
351+ // Determine memory args and sandbox config early — these are needed
352+ // regardless of whether we take the fast (relaunch) or full (sandbox) path.
353+ const memoryArgs =
354+ ! process . env [ 'SANDBOX' ] &&
355+ ! argv . isCommand &&
356+ settings . merged . advanced . autoConfigureMemory
357+ ? getNodeMemoryArgs ( isDebugMode )
358+ : [ ] ;
372359
373- await partialConfig . refreshAuth (
374- settings . merged . security . auth . selectedType ,
375- ) ;
376- } else if ( ! partialConfig . isInteractive ( ) ) {
377- const authType = await validateNonInteractiveAuth (
378- settings . merged . security . auth . selectedType ,
379- settings . merged . security . auth . useExternal ,
380- partialConfig ,
381- settings ,
382- ) ;
383- await partialConfig . refreshAuth ( authType ) ;
384- }
385- } catch ( err ) {
386- if ( err instanceof ValidationCancelledError ) {
387- // User cancelled verification, exit immediately.
388- await runExitCleanup ( ) ;
389- process . exit ( ExitCodes . SUCCESS ) ;
390- }
360+ if ( ! process . env [ 'SANDBOX' ] && ! argv . isCommand ) {
361+ const sandboxConfig = await loadSandboxConfig ( settings . merged , argv ) ;
391362
392- // If validation is required, we don't treat it as a fatal failure.
393- // We allow the app to start, and the React-based ValidationDialog
394- // will handle it.
395- if ( ! ( err instanceof ValidationRequiredError ) ) {
396- debugLogger . error ( 'Error authenticating:' , err ) ;
397- initialAuthFailed = true ;
398- }
399- }
400- }
363+ if ( sandboxConfig ) {
364+ // Sandbox path: needs full config + auth before entering sandbox because
365+ // the sandbox will interfere with the OAuth2 web redirect.
366+ const partialConfig = await loadCliConfig (
367+ settings . merged ,
368+ sessionId ,
369+ argv ,
370+ {
371+ projectHooks : settings . workspace . settings . hooks ,
372+ } ,
373+ ) ;
374+ adminControlsListner . setConfig ( partialConfig ) ;
401375
402- const remoteAdminSettings = partialConfig . getRemoteAdminSettings ( ) ;
403- // Set remote admin settings if returned from CCPA.
404- if ( remoteAdminSettings ) {
405- settings . setRemoteAdminSettings ( remoteAdminSettings ) ;
406- }
376+ let initialAuthFailed = false ;
377+ if ( ! settings . merged . security . auth . useExternal ) {
378+ try {
379+ if (
380+ partialConfig . isInteractive ( ) &&
381+ settings . merged . security . auth . selectedType
382+ ) {
383+ const err = validateAuthMethod (
384+ settings . merged . security . auth . selectedType ,
385+ ) ;
386+ if ( err ) {
387+ throw new Error ( err ) ;
388+ }
389+
390+ await partialConfig . refreshAuth (
391+ settings . merged . security . auth . selectedType ,
392+ ) ;
393+ } else if ( ! partialConfig . isInteractive ( ) ) {
394+ const authType = await validateNonInteractiveAuth (
395+ settings . merged . security . auth . selectedType ,
396+ settings . merged . security . auth . useExternal ,
397+ partialConfig ,
398+ settings ,
399+ ) ;
400+ await partialConfig . refreshAuth ( authType ) ;
401+ }
402+ } catch ( err ) {
403+ if ( err instanceof ValidationCancelledError ) {
404+ await runExitCleanup ( ) ;
405+ process . exit ( ExitCodes . SUCCESS ) ;
406+ }
407407
408- // Run deferred command now that we have admin settings.
409- await runDeferredCommand ( settings . merged ) ;
408+ if ( ! ( err instanceof ValidationRequiredError ) ) {
409+ debugLogger . error ( 'Error authenticating:' , err ) ;
410+ initialAuthFailed = true ;
411+ }
412+ }
413+ }
410414
411- // hop into sandbox if we are outside and sandboxing is enabled
412- if ( ! process . env [ 'SANDBOX' ] && ! argv . isCommand ) {
413- const memoryArgs = settings . merged . advanced . autoConfigureMemory
414- ? getNodeMemoryArgs ( isDebugMode )
415- : [ ] ;
416- const sandboxConfig = await loadSandboxConfig ( settings . merged , argv ) ;
417- // We intentionally omit the list of extensions here because extensions
418- // should not impact auth or setting up the sandbox.
419- // TODO(jacobr): refactor loadCliConfig so there is a minimal version
420- // that only initializes enough config to enable refreshAuth or find
421- // another way to decouple refreshAuth from requiring a config.
415+ const remoteAdminSettings = partialConfig . getRemoteAdminSettings ( ) ;
416+ if ( remoteAdminSettings ) {
417+ settings . setRemoteAdminSettings ( remoteAdminSettings ) ;
418+ }
419+
420+ await runDeferredCommand ( settings . merged ) ;
422421
423- if ( sandboxConfig ) {
424422 if ( initialAuthFailed ) {
425423 await runExitCleanup ( ) ;
426424 process . exit ( ExitCodes . FATAL_AUTHENTICATION_ERROR ) ;
@@ -442,11 +440,9 @@ export async function main() {
442440 ( arg ) => arg === '--prompt' || arg === '-p' ,
443441 ) ;
444442 if ( promptIndex > - 1 && finalArgs . length > promptIndex + 1 ) {
445- // If there's a prompt argument, prepend stdin to it
446443 finalArgs [ promptIndex + 1 ] =
447444 `${ stdinData } \n\n${ finalArgs [ promptIndex + 1 ] } ` ;
448445 } else {
449- // If there's no prompt argument, add stdin as the prompt
450446 finalArgs . push ( '--prompt' , stdinData ) ;
451447 }
452448 }
@@ -461,10 +457,68 @@ export async function main() {
461457 await runExitCleanup ( ) ;
462458 process . exit ( ExitCodes . SUCCESS ) ;
463459 } else {
464- // Relaunch app so we always have a child process that can be internally
465- // restarted if needed.
466- await relaunchAppInChildProcess ( memoryArgs , [ ] , remoteAdminSettings ) ;
460+ // Non-sandbox path: skip loadCliConfig + refreshAuth entirely.
461+ // The child process handles all initialization on its own.
462+ // This eliminates ~500-1000ms of duplicated work (extensions loading,
463+ // hierarchical memory search, policy engine, and 3-5 network calls).
464+ await relaunchAppInChildProcess ( memoryArgs , [ ] ) ;
465+ }
466+ } else {
467+ // Non-relaunch path (sandbox env or command mode): auth is needed here
468+ // because we won't be relaunching.
469+ const partialConfig = await loadCliConfig (
470+ settings . merged ,
471+ sessionId ,
472+ argv ,
473+ {
474+ projectHooks : settings . workspace . settings . hooks ,
475+ } ,
476+ ) ;
477+ adminControlsListner . setConfig ( partialConfig ) ;
478+
479+ if ( ! settings . merged . security . auth . useExternal && ! argv . isCommand ) {
480+ try {
481+ if (
482+ partialConfig . isInteractive ( ) &&
483+ settings . merged . security . auth . selectedType
484+ ) {
485+ const err = validateAuthMethod (
486+ settings . merged . security . auth . selectedType ,
487+ ) ;
488+ if ( err ) {
489+ throw new Error ( err ) ;
490+ }
491+
492+ await partialConfig . refreshAuth (
493+ settings . merged . security . auth . selectedType ,
494+ ) ;
495+ } else if ( ! partialConfig . isInteractive ( ) ) {
496+ const authType = await validateNonInteractiveAuth (
497+ settings . merged . security . auth . selectedType ,
498+ settings . merged . security . auth . useExternal ,
499+ partialConfig ,
500+ settings ,
501+ ) ;
502+ await partialConfig . refreshAuth ( authType ) ;
503+ }
504+ } catch ( err ) {
505+ if ( err instanceof ValidationCancelledError ) {
506+ await runExitCleanup ( ) ;
507+ process . exit ( ExitCodes . SUCCESS ) ;
508+ }
509+
510+ if ( ! ( err instanceof ValidationRequiredError ) ) {
511+ debugLogger . error ( 'Error authenticating:' , err ) ;
512+ }
513+ }
467514 }
515+
516+ const remoteAdminSettings = partialConfig . getRemoteAdminSettings ( ) ;
517+ if ( remoteAdminSettings ) {
518+ settings . setRemoteAdminSettings ( remoteAdminSettings ) ;
519+ }
520+
521+ await runDeferredCommand ( settings . merged ) ;
468522 }
469523
470524 // We are now past the logic handling potentially launching a child process
0 commit comments