@@ -24,7 +24,10 @@ import {
2424 setProviderContextWindowOverrides ,
2525 setReservedCompletionTokens ,
2626} from "../provider/context-window.js" ;
27- import { bootstrapPricingMetadata } from "../cost/pricing-metadata.js" ;
27+ import {
28+ bootstrapPricingMetadata ,
29+ startPricingCacheRead ,
30+ } from "../cost/pricing-metadata.js" ;
2831import {
2932 defaultPricingCachePath ,
3033 type PricingFetcherOptions ,
@@ -89,6 +92,8 @@ import {
8992 loadLocalSettingsResult ,
9093 type SettingsLoadDiagnostic ,
9194 loadSettingsRecoveringClobberedOAuthSelection ,
95+ readSettingsDocument ,
96+ rememberBootGlobalSettings ,
9297 resolveLocalSettingsPath ,
9398 normalizeOpenAICompatibleBaseURL ,
9499 resolveProvider ,
@@ -1075,21 +1080,6 @@ export async function loadConfig(
10751080 positional . push ( arg ) ;
10761081 }
10771082
1078- if ( permissionsPath !== undefined ) {
1079- try {
1080- await loadPermissionsFile ( permissionsPath ) ;
1081- } catch ( err ) {
1082- throw new CliUserError ( err instanceof Error ? err . message : String ( err ) ) ;
1083- }
1084- }
1085-
1086- const pricingCachePath = defaultPricingCachePath ( ) ;
1087-
1088- await bootstrapPricingMetadata ( {
1089- cachePath : pricingCachePath ,
1090- ...options . pricing ,
1091- } ) ;
1092-
10931083 // Resolve both settings targets from the same active source. The local schema
10941084 // must never be read from or written to the active global-schema target.
10951085 const effectiveSettingsPath =
@@ -1106,22 +1096,74 @@ export async function loadConfig(
11061096 // unauthenticated. Only the programmatic `globalSettingsPath` test override
11071097 // (never a CLI flag) opts out — tests want no home-directory reads.
11081098 const useOAuthProfiles = options . globalSettingsPath === undefined ;
1109- const [ codexProfiles , xaiProfiles ] : [ CodexProfile [ ] , XaiProfile [ ] ] =
1099+
1100+ // These files do not decide precedence for each other. Overlap the reads,
1101+ // then apply results in the previous order: permissions error, pricing
1102+ // install, OAuth error, settings warnings, local settings, profile.
1103+ const permissionsTask : Promise < void > =
1104+ permissionsPath !== undefined
1105+ ? loadPermissionsFile ( permissionsPath ) . then (
1106+ ( ) => undefined ,
1107+ ( err : unknown ) => {
1108+ throw new CliUserError (
1109+ err instanceof Error ? err . message : String ( err ) ,
1110+ ) ;
1111+ } ,
1112+ )
1113+ : Promise . resolve ( ) ;
1114+ const pricingOptions : PricingFetcherOptions = {
1115+ cachePath : defaultPricingCachePath ( ) ,
1116+ ...options . pricing ,
1117+ } ;
1118+ startPricingCacheRead ( pricingOptions . cachePath ) ;
1119+ const oauthTask : Promise < readonly [ CodexProfile [ ] , XaiProfile [ ] ] > =
11101120 useOAuthProfiles
1111- ? await Promise . all ( [ listCodexProfiles ( ) , listXaiProfiles ( ) ] )
1112- : [ [ ] , [ ] ] ;
1121+ ? Promise . all ( [ listCodexProfiles ( ) , listXaiProfiles ( ) ] )
1122+ : Promise . resolve ( [ [ ] , [ ] ] ) ;
1123+ const settingsPath = configPath ?? effectiveSettingsPath ;
1124+ const settingsDocumentTask = readSettingsDocument ( settingsPath ) ;
1125+ const localTask =
1126+ localSettingsFile === null
1127+ ? Promise . resolve ( {
1128+ settings : null ,
1129+ diagnostics : [ ] as SettingsLoadDiagnostic [ ] ,
1130+ } )
1131+ : loadLocalSettingsResult ( localSettingsFile ) ;
1132+ const profileTask = resolveProfile ( cwd , profileFlag ) ;
1133+
1134+ const [
1135+ permissionsResult ,
1136+ oauthResult ,
1137+ settingsDocumentResult ,
1138+ localResultSettled ,
1139+ profileResult ,
1140+ ] = await Promise . allSettled ( [
1141+ permissionsTask ,
1142+ oauthTask ,
1143+ settingsDocumentTask ,
1144+ localTask ,
1145+ profileTask ,
1146+ ] ) ;
1147+
1148+ if ( permissionsResult . status === "rejected" ) throw permissionsResult . reason ;
1149+ await bootstrapPricingMetadata ( pricingOptions ) ;
1150+ if ( oauthResult . status === "rejected" ) throw oauthResult . reason ;
1151+ if ( settingsDocumentResult . status === "rejected" ) {
1152+ throw settingsDocumentResult . reason ;
1153+ }
1154+
1155+ const [ codexProfiles , xaiProfiles ] = oauthResult . value ;
11131156 let projectedOAuthProviders = {
11141157 ...codexProvidersAsSettings ( codexProfiles ) ,
11151158 ...xaiProvidersAsSettings ( xaiProfiles ) ,
11161159 } ;
1160+ const parsedDocument = { value : settingsDocumentResult . value } ;
11171161 const settings =
11181162 configPath !== undefined
11191163 ? await loadSettingsRecoveringClobberedOAuthSelection (
11201164 configPath ,
11211165 projectedOAuthProviders ,
1122- {
1123- persist : false ,
1124- } ,
1166+ { persist : false , parsedDocument } ,
11251167 ) . then ( ( s ) => {
11261168 if ( s === null )
11271169 throw new Error ( `--config file not found or empty: ${ configPath } ` ) ;
@@ -1130,8 +1172,15 @@ export async function loadConfig(
11301172 : await loadSettingsRecoveringClobberedOAuthSelection (
11311173 effectiveSettingsPath ,
11321174 projectedOAuthProviders ,
1133- { persist : true } ,
1175+ { persist : true , parsedDocument } ,
11341176 ) ;
1177+ // --config does not load the global file, so do not reuse that document for
1178+ // the later global telemetry read. Do not overlap that read with the heal
1179+ // write above: both target settings.json and the later read must see the
1180+ // persisted document, which is what this snapshot is.
1181+ if ( configPath === undefined ) {
1182+ rememberBootGlobalSettings ( effectiveSettingsPath , settings ) ;
1183+ }
11351184
11361185 // True when the value came from settings, not this invocation's
11371186 // --dangerously-skip-permissions flag (Config.skipPermissionsFromSettings).
@@ -1156,14 +1205,12 @@ export async function loadConfig(
11561205 // supplies provider definitions, .corbits/settings.json supplies the
11571206 // provider/model selection; CLI --provider/--model override both. Fail open
11581207 // on unknown/invalid local keys — never crash startup.
1159- const localResult =
1160- localSettingsFile === null
1161- ? { settings : null , diagnostics : [ ] }
1162- : await loadLocalSettingsResult ( localSettingsFile ) ;
1208+ if ( localResultSettled . status === "rejected" ) throw localResultSettled . reason ;
1209+ if ( profileResult . status === "rejected" ) throw profileResult . reason ;
1210+ const localResult = localResultSettled . value ;
11631211 const local = localResult . settings ;
11641212 const settingsDiagnostics = localResult . diagnostics ;
1165-
1166- const profile = await resolveProfile ( cwd , profileFlag ) ;
1213+ const profile = profileResult . value ;
11671214
11681215 // Apply profile as a fallback layer: profile.model fills in when neither CLI
11691216 // nor local settings specify a model. This sits below local in precedence.
0 commit comments