@@ -307,13 +307,47 @@ export type ConfiguredProxyDiagnostic = {
307307 detail : string ;
308308} ;
309309
310- function envReferenceName ( value : string ) : string | null {
310+ export function envReferenceName ( value : string ) : string | null {
311311 const braced = value . match ( / ^ \$ \{ ( \w + ) \} $ / ) ;
312312 if ( braced ) return braced [ 1 ] ! ;
313313 const bare = value . match ( / ^ \$ ( \w + ) $ / ) ;
314314 return bare ? bare [ 1 ] ! : null ;
315315}
316316
317+ export type ProviderApiKeyDiagnostic = {
318+ provider : string ;
319+ envName : string ;
320+ detail : string ;
321+ } ;
322+
323+ /** Warn when a key-auth provider's apiKey env reference resolves empty in this process. */
324+ export function collectProviderApiKeyDiagnostics (
325+ providers : Record < string , { authMode ?: string ; apiKey ?: string } > = readConfigDiagnostics ( ) . config . providers ?? { } ,
326+ env : EnvMap = process . env ,
327+ ) : ProviderApiKeyDiagnostic [ ] {
328+ const resolveInEnv = ( value : string ) : string | undefined => {
329+ const name = envReferenceName ( value ) ;
330+ if ( ! name ) return value ;
331+ return env [ name ] ;
332+ } ;
333+ const rows : ProviderApiKeyDiagnostic [ ] = [ ] ;
334+ for ( const [ provider , config ] of Object . entries ( providers ) ) {
335+ if ( config . authMode !== "key" ) continue ;
336+ const raw = typeof config . apiKey === "string" ? config . apiKey . trim ( ) : "" ;
337+ if ( ! raw ) continue ;
338+ const envName = envReferenceName ( raw ) ;
339+ if ( ! envName ) continue ;
340+ const resolved = resolveInEnv ( raw ) ;
341+ if ( resolved ?. trim ( ) ) continue ;
342+ rows . push ( {
343+ provider,
344+ envName,
345+ detail : `provider ${ provider } : env reference ${ envName } is unset or empty in this process` ,
346+ } ) ;
347+ }
348+ return rows ;
349+ }
350+
317351export function collectConfiguredProxy ( ) : ConfiguredProxyDiagnostic {
318352 const diagnostics = readConfigDiagnostics ( ) ;
319353 const rawProxy = typeof diagnostics . config . proxy === "string" ? diagnostics . config . proxy . trim ( ) : "" ;
@@ -742,6 +776,16 @@ export async function runDoctor(args: string[] = []): Promise<void> {
742776 console . log ( "\nConfigured proxy (value hidden)" ) ;
743777 console . log ( ` ${ configuredProxy . present ? "set " : "unset " } ${ configuredProxy . key } (${ configuredProxy . source } ; ${ configuredProxy . detail } )` ) ;
744778
779+ const providerApiKeys = collectProviderApiKeyDiagnostics ( doctorConfig . providers ) ;
780+ console . log ( "\nProvider API keys (value hidden)" ) ;
781+ if ( providerApiKeys . length === 0 ) {
782+ console . log ( " ok no empty env-referenced provider keys detected in this process" ) ;
783+ } else {
784+ for ( const row of providerApiKeys ) {
785+ console . log ( ` !! ${ row . detail } ` ) ;
786+ }
787+ }
788+
745789 console . log ( "\nRunning proxy process proxy env (presence only)" ) ;
746790 if ( runningProxyEnv . status === "not_running" ) {
747791 console . log ( " -- no running ocx proxy process found" ) ;
@@ -825,6 +869,9 @@ export async function runDoctor(args: string[] = []): Promise<void> {
825869 serviceViable : startup . serviceViable ,
826870 } ) ;
827871 if ( proxyDown ) hints . push ( proxyDown ) ;
872+ for ( const row of providerApiKeys ) {
873+ hints . push ( `${ row . detail } . Set ${ row . envName } in the shell that starts the proxy, or store a literal key in config (value hidden here).` ) ;
874+ }
828875 const anyDrvfs = paths . some ( p => detectFsType ( p . path , mounts ) . isDrvfs || detectFsType ( p . path , mounts ) . isMntDrive ) ;
829876 const noProxy = currentProxyEnv . every ( p => ! p . present ) && ! configuredProxy . present ;
830877 if ( ! startup . rebootSafe ) {
0 commit comments