@vybestack/llxprt-code 0.9.1 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +3 -3
- package/dist/src/auth/BucketFailoverHandlerImpl.d.ts +6 -2
- package/dist/src/auth/BucketFailoverHandlerImpl.js +128 -22
- package/dist/src/auth/BucketFailoverHandlerImpl.js.map +1 -1
- package/dist/src/auth/BucketFailoverHandlerImpl.spec.js +201 -1
- package/dist/src/auth/BucketFailoverHandlerImpl.spec.js.map +1 -1
- package/dist/src/auth/__tests__/oauth-manager.getToken-bucket-peek.spec.js +2 -1
- package/dist/src/auth/__tests__/oauth-manager.getToken-bucket-peek.spec.js.map +1 -1
- package/dist/src/auth/oauth-manager.auth-lock.spec.js +6 -1
- package/dist/src/auth/oauth-manager.auth-lock.spec.js.map +1 -1
- package/dist/src/auth/oauth-manager.d.ts +10 -9
- package/dist/src/auth/oauth-manager.failover-wiring.spec.js +74 -1
- package/dist/src/auth/oauth-manager.failover-wiring.spec.js.map +1 -1
- package/dist/src/auth/oauth-manager.issue1468.spec.js +527 -2
- package/dist/src/auth/oauth-manager.issue1468.spec.js.map +1 -1
- package/dist/src/auth/oauth-manager.js +184 -67
- package/dist/src/auth/oauth-manager.js.map +1 -1
- package/dist/src/auth/oauth-manager.logout.spec.js +137 -0
- package/dist/src/auth/oauth-manager.logout.spec.js.map +1 -1
- package/dist/src/auth/oauth-manager.spec.js +58 -0
- package/dist/src/auth/oauth-manager.spec.js.map +1 -1
- package/dist/src/config/config.js +4 -0
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +1 -1
- package/dist/src/generated/git-commit.js +1 -1
- package/dist/src/providers/aliases/chutes-ai.config +1 -1
- package/dist/src/providers/aliases/codex.config +1 -0
- package/dist/src/providers/aliases/deepseek.config +9 -0
- package/dist/src/providers/aliases/fireworks.config +1 -1
- package/dist/src/providers/aliases/gemini.config +1 -1
- package/dist/src/providers/aliases/openai.config +1 -1
- package/dist/src/providers/aliases/openrouter.config +1 -1
- package/dist/src/providers/aliases/synthetic.config +1 -1
- package/dist/src/providers/aliases/xai.config +1 -1
- package/dist/src/providers/aliases/zai.config +9 -0
- package/dist/src/providers/providerAliases.codex.test.js +2 -2
- package/dist/src/providers/providerAliases.codex.test.js.map +1 -1
- package/dist/src/providers/providerAliases.defaultModels.test.d.ts +6 -0
- package/dist/src/providers/providerAliases.defaultModels.test.js +57 -0
- package/dist/src/providers/providerAliases.defaultModels.test.js.map +1 -0
- package/dist/src/providers/providerManagerInstance.oauthRegistration.test.js +1 -1
- package/dist/src/providers/providerManagerInstance.oauthRegistration.test.js.map +1 -1
- package/dist/src/ui/AppContainer.js +1 -1
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/commands/diagnosticsCommand.js +16 -7
- package/dist/src/ui/commands/diagnosticsCommand.js.map +1 -1
- package/dist/src/ui/commands/diagnosticsCommand.spec.js +68 -0
- package/dist/src/ui/commands/diagnosticsCommand.spec.js.map +1 -1
- package/dist/src/ui/commands/statsCommand.js +63 -4
- package/dist/src/ui/commands/statsCommand.js.map +1 -1
- package/dist/src/ui/components/AuthDialog.js +1 -1
- package/dist/src/ui/components/AuthDialog.js.map +1 -1
- package/dist/src/ui/utils/terminalCapabilityManager.d.ts +1 -0
- package/dist/src/ui/utils/terminalCapabilityManager.js +25 -1
- package/dist/src/ui/utils/terminalCapabilityManager.js.map +1 -1
- package/dist/src/ui/utils/terminalCapabilityManager.test.js +34 -1
- package/dist/src/ui/utils/terminalCapabilityManager.test.js.map +1 -1
- package/dist/src/ui/utils/terminalProtocolCleanup.js +1 -1
- package/dist/src/ui/utils/terminalProtocolCleanup.js.map +1 -1
- package/dist/src/ui/utils/terminalProtocolCleanup.test.js +2 -2
- package/dist/src/ui/utils/terminalProtocolCleanup.test.js.map +1 -1
- package/dist/src/utils/sandbox.d.ts +13 -0
- package/dist/src/utils/sandbox.js +163 -7
- package/dist/src/utils/sandbox.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -29,6 +29,12 @@ function isLoggingWrapperCandidate(provider) {
|
|
|
29
29
|
typeof provider === 'object' &&
|
|
30
30
|
Object.prototype.hasOwnProperty.call(provider, 'wrappedProvider'));
|
|
31
31
|
}
|
|
32
|
+
function hasRequestMetadata(handler) {
|
|
33
|
+
return (!!handler &&
|
|
34
|
+
typeof handler === 'object' &&
|
|
35
|
+
typeof handler.getRequestMetadata ===
|
|
36
|
+
'function');
|
|
37
|
+
}
|
|
32
38
|
/**
|
|
33
39
|
* @plan PLAN-20251020-STATELESSPROVIDER3.P12
|
|
34
40
|
* @requirement REQ-SP3-003
|
|
@@ -455,8 +461,11 @@ export class OAuthManager {
|
|
|
455
461
|
if (!provider) {
|
|
456
462
|
throw new Error(`Unknown provider: ${providerName}`);
|
|
457
463
|
}
|
|
458
|
-
|
|
459
|
-
|
|
464
|
+
const sessionMetadata = await this.getCurrentProfileSessionMetadata(providerName);
|
|
465
|
+
// Resolve which bucket to act on (explicit bucket > current-profile session bucket > default)
|
|
466
|
+
const bucketToUse = bucket ??
|
|
467
|
+
(await this.getCurrentProfileSessionBucket(providerName, sessionMetadata)) ??
|
|
468
|
+
'default';
|
|
460
469
|
const tokenForLogout = await this.tokenStore.getToken(providerName, bucketToUse);
|
|
461
470
|
// Call provider logout if exists (best-effort remote revoke), but ALWAYS clear local token
|
|
462
471
|
if ('logout' in provider && typeof provider.logout === 'function') {
|
|
@@ -471,8 +480,13 @@ export class OAuthManager {
|
|
|
471
480
|
}
|
|
472
481
|
await this.tokenStore.removeToken(providerName, bucketToUse);
|
|
473
482
|
// If we just logged out the active session bucket, clear the in-memory override.
|
|
474
|
-
if (this.
|
|
475
|
-
this.
|
|
483
|
+
if ((await this.getCurrentProfileSessionBucket(providerName, sessionMetadata)) === bucketToUse) {
|
|
484
|
+
if (this.getSessionBucket(providerName, sessionMetadata) === bucketToUse) {
|
|
485
|
+
this.clearSessionBucket(providerName, sessionMetadata);
|
|
486
|
+
}
|
|
487
|
+
if (this.getSessionBucket(providerName) === bucketToUse) {
|
|
488
|
+
this.clearSessionBucket(providerName);
|
|
489
|
+
}
|
|
476
490
|
}
|
|
477
491
|
// CRITICAL FIX: Clear all provider auth caches after logout
|
|
478
492
|
// This ensures BaseProvider and specific provider caches are invalidated
|
|
@@ -522,8 +536,8 @@ export class OAuthManager {
|
|
|
522
536
|
// Lines 42-49: FOR EACH provider IN providers DO
|
|
523
537
|
for (const provider of providers) {
|
|
524
538
|
try {
|
|
525
|
-
// Line 44: AWAIT this.
|
|
526
|
-
await this.
|
|
539
|
+
// Line 44: AWAIT this.logoutAllBuckets(provider)
|
|
540
|
+
await this.logoutAllBuckets(provider);
|
|
527
541
|
}
|
|
528
542
|
catch (error) {
|
|
529
543
|
// Lines 45-47: LOG "Failed to logout from " + provider + ": " + error
|
|
@@ -565,6 +579,10 @@ export class OAuthManager {
|
|
|
565
579
|
// This fixes subagents created without LoadedSettings: they can reuse existing tokens
|
|
566
580
|
// instead of forcing unnecessary reauth loops.
|
|
567
581
|
logger.debug(() => `[FLOW] Attempting to get existing token for ${providerName}...`);
|
|
582
|
+
const explicitBucket = typeof bucket === 'string';
|
|
583
|
+
const requestMetadata = !explicitBucket && bucket && typeof bucket === 'object'
|
|
584
|
+
? bucket
|
|
585
|
+
: undefined;
|
|
568
586
|
const token = await this.getOAuthToken(providerName, bucket);
|
|
569
587
|
// Special handling for different providers
|
|
570
588
|
// @plan:PLAN-20250823-AUTHFIXES.P15
|
|
@@ -585,11 +603,11 @@ export class OAuthManager {
|
|
|
585
603
|
// handle API-error-driven failover. This peek loop only reads the token store
|
|
586
604
|
// directly without mutating any failover state.
|
|
587
605
|
{
|
|
588
|
-
const profileBuckets = await this.getProfileBuckets(providerName);
|
|
606
|
+
const profileBuckets = await this.getProfileBuckets(providerName, requestMetadata);
|
|
589
607
|
if (profileBuckets.length > 1) {
|
|
590
608
|
const nowInSeconds = Math.floor(Date.now() / 1000);
|
|
591
609
|
const thirtySecondsFromNow = nowInSeconds + 30;
|
|
592
|
-
const alreadyTriedBucket = this.getSessionBucket(providerName);
|
|
610
|
+
const alreadyTriedBucket = this.getSessionBucket(providerName, requestMetadata);
|
|
593
611
|
for (const peekBucket of profileBuckets) {
|
|
594
612
|
if (peekBucket === alreadyTriedBucket)
|
|
595
613
|
continue;
|
|
@@ -597,7 +615,7 @@ export class OAuthManager {
|
|
|
597
615
|
const peekToken = await this.tokenStore.getToken(providerName, peekBucket);
|
|
598
616
|
if (peekToken && peekToken.expiry > thirtySecondsFromNow) {
|
|
599
617
|
logger.debug(() => `[issue1616] Found valid token in bucket '${peekBucket}' for ${providerName}, switching session`);
|
|
600
|
-
this.setSessionBucket(providerName, peekBucket);
|
|
618
|
+
this.setSessionBucket(providerName, peekBucket, requestMetadata);
|
|
601
619
|
return peekToken.access_token;
|
|
602
620
|
}
|
|
603
621
|
}
|
|
@@ -620,10 +638,19 @@ export class OAuthManager {
|
|
|
620
638
|
return null;
|
|
621
639
|
}
|
|
622
640
|
logger.debug(() => `[FLOW] No existing token for ${providerName}, triggering OAuth flow...`);
|
|
641
|
+
const resolvedProfileBuckets = await this.getProfileBuckets(providerName, requestMetadata);
|
|
642
|
+
const scopedSessionBucket = explicitBucket
|
|
643
|
+
? undefined
|
|
644
|
+
: this.getSessionBucket(providerName, requestMetadata);
|
|
645
|
+
const bucketToCheck = explicitBucket
|
|
646
|
+
? bucket
|
|
647
|
+
: (scopedSessionBucket ??
|
|
648
|
+
(resolvedProfileBuckets.length === 1
|
|
649
|
+
? resolvedProfileBuckets[0]
|
|
650
|
+
: undefined));
|
|
623
651
|
// @fix issue1262 & issue1195: Before triggering OAuth, check disk with lock
|
|
624
652
|
// Another process or earlier run may have written a valid token that we missed
|
|
625
653
|
// Use the same locking pattern as PR #1258 to prevent race conditions
|
|
626
|
-
const bucketToCheck = typeof bucket === 'string' ? bucket : undefined;
|
|
627
654
|
const lockAcquired = await this.tokenStore.acquireRefreshLock(providerName, {
|
|
628
655
|
waitMs: 5000, // Wait up to 5 seconds for lock
|
|
629
656
|
staleMs: 30000,
|
|
@@ -680,7 +707,7 @@ export class OAuthManager {
|
|
|
680
707
|
// Authentication is handled at the turn boundary via ensureBucketsAuthenticated().
|
|
681
708
|
// For single-bucket or non-bucketed profiles, preserve existing auth behavior.
|
|
682
709
|
try {
|
|
683
|
-
const buckets =
|
|
710
|
+
const buckets = resolvedProfileBuckets;
|
|
684
711
|
if (buckets.length > 1) {
|
|
685
712
|
// Multi-bucket: pure lookup only — return null.
|
|
686
713
|
// ensureBucketsAuthenticated() at turn boundary handles auth,
|
|
@@ -700,14 +727,22 @@ export class OAuthManager {
|
|
|
700
727
|
logger.debug('Could not get ephemeral setting (runtime not initialized), using default', runtimeError);
|
|
701
728
|
}
|
|
702
729
|
if (showPrompt) {
|
|
703
|
-
const effectiveBuckets =
|
|
730
|
+
const effectiveBuckets = bucketToCheck ? [bucketToCheck] : ['default'];
|
|
704
731
|
logger.debug(`Single-bucket auth with prompt mode for ${providerName}, bucket: ${effectiveBuckets[0]}`);
|
|
705
|
-
await this.authenticateMultipleBuckets(providerName, effectiveBuckets);
|
|
732
|
+
await this.authenticateMultipleBuckets(providerName, effectiveBuckets, requestMetadata);
|
|
733
|
+
const authenticatedBucket = effectiveBuckets[0];
|
|
734
|
+
if (authenticatedBucket) {
|
|
735
|
+
this.setSessionBucket(providerName, authenticatedBucket, requestMetadata);
|
|
736
|
+
}
|
|
706
737
|
}
|
|
707
738
|
else {
|
|
708
|
-
|
|
739
|
+
const authenticatedBucket = bucketToCheck ?? 'default';
|
|
740
|
+
await this.authenticate(providerName, authenticatedBucket);
|
|
741
|
+
if (authenticatedBucket) {
|
|
742
|
+
this.setSessionBucket(providerName, authenticatedBucket, requestMetadata);
|
|
743
|
+
}
|
|
709
744
|
}
|
|
710
|
-
const newToken = await this.getOAuthToken(providerName);
|
|
745
|
+
const newToken = await this.getOAuthToken(providerName, explicitBucket ? bucketToCheck : requestMetadata);
|
|
711
746
|
return newToken ? newToken.access_token : null;
|
|
712
747
|
}
|
|
713
748
|
catch (error) {
|
|
@@ -756,6 +791,9 @@ export class OAuthManager {
|
|
|
756
791
|
throw new Error(`Unknown provider: ${providerName}`);
|
|
757
792
|
}
|
|
758
793
|
const explicitBucket = typeof bucket === 'string';
|
|
794
|
+
const requestMetadata = !explicitBucket && bucket && typeof bucket === 'object'
|
|
795
|
+
? bucket
|
|
796
|
+
: undefined;
|
|
759
797
|
// Determine the bucket to use: explicit bucket parameter or session bucket override
|
|
760
798
|
let bucketToUse;
|
|
761
799
|
if (explicitBucket) {
|
|
@@ -767,10 +805,11 @@ export class OAuthManager {
|
|
|
767
805
|
let failoverHandler;
|
|
768
806
|
if (!explicitBucket) {
|
|
769
807
|
await this.withBucketResolutionLock(providerName, async () => {
|
|
770
|
-
|
|
771
|
-
|
|
808
|
+
const sessionBucket = this.getSessionBucket(providerName, requestMetadata);
|
|
809
|
+
if (sessionBucket) {
|
|
810
|
+
bucketToUse = sessionBucket;
|
|
772
811
|
}
|
|
773
|
-
profileBuckets = await this.getProfileBuckets(providerName);
|
|
812
|
+
profileBuckets = await this.getProfileBuckets(providerName, requestMetadata);
|
|
774
813
|
const config = this.getConfig?.();
|
|
775
814
|
// @fix issue1029 - Enhanced debug logging for failover handler setup
|
|
776
815
|
logger.debug(() => `[issue1029] getOAuthToken: provider=${providerName}, buckets=${JSON.stringify(profileBuckets)}, hasConfig=${!!config}`);
|
|
@@ -779,9 +818,15 @@ export class OAuthManager {
|
|
|
779
818
|
const existingBuckets = failoverHandler?.getBuckets?.() ?? [];
|
|
780
819
|
const sameBuckets = existingBuckets.length === profileBuckets.length &&
|
|
781
820
|
existingBuckets.every((value, index) => value === profileBuckets[index]);
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
821
|
+
const requestedScopeKey = this.getSessionBucketScopeKey(providerName, requestMetadata);
|
|
822
|
+
const existingRequestMetadata = hasRequestMetadata(failoverHandler)
|
|
823
|
+
? failoverHandler.getRequestMetadata()
|
|
824
|
+
: undefined;
|
|
825
|
+
const existingScopeKey = this.getSessionBucketScopeKey(providerName, existingRequestMetadata);
|
|
826
|
+
const sameScope = existingScopeKey === requestedScopeKey;
|
|
827
|
+
logger.debug(() => `[issue1029] Failover handler check: hasExisting=${!!failoverHandler}, sameBuckets=${sameBuckets}, sameScope=${sameScope}, existingBuckets=${JSON.stringify(existingBuckets)}`);
|
|
828
|
+
if (!failoverHandler || !sameBuckets || !sameScope) {
|
|
829
|
+
const handler = new BucketFailoverHandlerImpl(profileBuckets, providerName, this, requestMetadata);
|
|
785
830
|
config.setBucketFailoverHandler(handler);
|
|
786
831
|
failoverHandler = handler;
|
|
787
832
|
logger.debug(() => `[issue1029] Created and set new BucketFailoverHandlerImpl on config for ${providerName} with buckets: ${JSON.stringify(profileBuckets)}`);
|
|
@@ -802,8 +847,8 @@ export class OAuthManager {
|
|
|
802
847
|
bucketToUse = profileBuckets[0];
|
|
803
848
|
}
|
|
804
849
|
// Establish a default session bucket for the duration of this CLI session.
|
|
805
|
-
if (
|
|
806
|
-
this.
|
|
850
|
+
if (!sessionBucket && bucketToUse) {
|
|
851
|
+
this.setSessionBucket(providerName, bucketToUse, requestMetadata);
|
|
807
852
|
}
|
|
808
853
|
}
|
|
809
854
|
});
|
|
@@ -1326,27 +1371,45 @@ export class OAuthManager {
|
|
|
1326
1371
|
* Set session bucket override for a provider
|
|
1327
1372
|
* Session state is in-memory only and not persisted
|
|
1328
1373
|
*/
|
|
1329
|
-
setSessionBucket(provider, bucket) {
|
|
1330
|
-
this.sessionBuckets.set(provider, bucket);
|
|
1374
|
+
setSessionBucket(provider, bucket, metadata) {
|
|
1375
|
+
this.sessionBuckets.set(this.getSessionBucketScopeKey(provider, metadata), bucket);
|
|
1331
1376
|
}
|
|
1332
1377
|
/**
|
|
1333
1378
|
* Get session bucket override for a provider
|
|
1334
1379
|
* Returns undefined if no session override set
|
|
1335
1380
|
*/
|
|
1336
|
-
getSessionBucket(provider) {
|
|
1337
|
-
return this.sessionBuckets.get(provider);
|
|
1381
|
+
getSessionBucket(provider, metadata) {
|
|
1382
|
+
return this.sessionBuckets.get(this.getSessionBucketScopeKey(provider, metadata));
|
|
1383
|
+
}
|
|
1384
|
+
async getCurrentProfileSessionBucket(provider, metadata) {
|
|
1385
|
+
const scopedSessionBucket = this.getSessionBucket(provider, metadata);
|
|
1386
|
+
if (scopedSessionBucket) {
|
|
1387
|
+
return scopedSessionBucket;
|
|
1388
|
+
}
|
|
1389
|
+
const profileBuckets = await this.getProfileBuckets(provider, metadata);
|
|
1390
|
+
if (profileBuckets.length === 1) {
|
|
1391
|
+
return profileBuckets[0];
|
|
1392
|
+
}
|
|
1393
|
+
const unscopedSessionBucket = this.getSessionBucket(provider);
|
|
1394
|
+
if (unscopedSessionBucket &&
|
|
1395
|
+
(profileBuckets.length === 0 ||
|
|
1396
|
+
profileBuckets.includes(unscopedSessionBucket))) {
|
|
1397
|
+
return unscopedSessionBucket;
|
|
1398
|
+
}
|
|
1399
|
+
return undefined;
|
|
1338
1400
|
}
|
|
1339
1401
|
/**
|
|
1340
1402
|
* Clear session bucket override for a provider
|
|
1341
1403
|
*/
|
|
1342
|
-
clearSessionBucket(provider) {
|
|
1343
|
-
this.sessionBuckets.delete(provider);
|
|
1404
|
+
clearSessionBucket(provider, metadata) {
|
|
1405
|
+
this.sessionBuckets.delete(this.getSessionBucketScopeKey(provider, metadata));
|
|
1344
1406
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1407
|
+
clearAllSessionBuckets(provider) {
|
|
1408
|
+
for (const key of Array.from(this.sessionBuckets.keys())) {
|
|
1409
|
+
if (key === provider || key.startsWith(`${provider}::`)) {
|
|
1410
|
+
this.sessionBuckets.delete(key);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1350
1413
|
}
|
|
1351
1414
|
/**
|
|
1352
1415
|
* Logout from all buckets for a provider
|
|
@@ -1361,14 +1424,18 @@ export class OAuthManager {
|
|
|
1361
1424
|
logger.warn(`Failed to logout from bucket ${bucket}:`, error);
|
|
1362
1425
|
}
|
|
1363
1426
|
}
|
|
1364
|
-
this.
|
|
1427
|
+
this.clearAllSessionBuckets(provider);
|
|
1428
|
+
}
|
|
1429
|
+
async listBuckets(provider) {
|
|
1430
|
+
return this.tokenStore.listBuckets(provider);
|
|
1365
1431
|
}
|
|
1366
1432
|
/**
|
|
1367
1433
|
* Get authentication status with bucket information
|
|
1368
1434
|
*/
|
|
1369
1435
|
async getAuthStatusWithBuckets(provider) {
|
|
1370
1436
|
const buckets = await this.tokenStore.listBuckets(provider);
|
|
1371
|
-
const
|
|
1437
|
+
const sessionMetadata = await this.getCurrentProfileSessionMetadata(provider);
|
|
1438
|
+
const sessionBucket = await this.getCurrentProfileSessionBucket(provider, sessionMetadata);
|
|
1372
1439
|
const statuses = [];
|
|
1373
1440
|
for (const bucket of buckets) {
|
|
1374
1441
|
const token = await this.tokenStore.getToken(provider, bucket);
|
|
@@ -1407,8 +1474,11 @@ export class OAuthManager {
|
|
|
1407
1474
|
if (!provider) {
|
|
1408
1475
|
return null;
|
|
1409
1476
|
}
|
|
1477
|
+
const sessionMetadata = await this.getCurrentProfileSessionMetadata('anthropic');
|
|
1410
1478
|
// Get the token for the specified bucket
|
|
1411
|
-
const bucketToUse = bucket ??
|
|
1479
|
+
const bucketToUse = bucket ??
|
|
1480
|
+
(await this.getCurrentProfileSessionBucket('anthropic', sessionMetadata)) ??
|
|
1481
|
+
'default';
|
|
1412
1482
|
const token = await this.tokenStore.getToken('anthropic', bucketToUse);
|
|
1413
1483
|
if (!token) {
|
|
1414
1484
|
return null;
|
|
@@ -1546,48 +1616,95 @@ export class OAuthManager {
|
|
|
1546
1616
|
}
|
|
1547
1617
|
return result;
|
|
1548
1618
|
}
|
|
1549
|
-
|
|
1619
|
+
getSessionBucketScopeKey(provider, metadata) {
|
|
1620
|
+
const profileId = typeof metadata?.profileId === 'string' &&
|
|
1621
|
+
metadata.profileId.trim() !== ''
|
|
1622
|
+
? metadata.profileId.trim()
|
|
1623
|
+
: undefined;
|
|
1624
|
+
return profileId ? `${provider}::${profileId}` : provider;
|
|
1625
|
+
}
|
|
1626
|
+
async getCurrentProfileSessionMetadata(providerName) {
|
|
1550
1627
|
try {
|
|
1551
|
-
// Try to get profile from runtime settings
|
|
1552
1628
|
const { getCliRuntimeServices } = await import('../runtime/runtimeSettings.js');
|
|
1553
1629
|
const { settingsService } = getCliRuntimeServices();
|
|
1554
|
-
// Get current profile name
|
|
1555
1630
|
const currentProfileName = typeof settingsService.getCurrentProfileName === 'function'
|
|
1556
1631
|
? settingsService.getCurrentProfileName()
|
|
1557
|
-
: settingsService.get('currentProfile');
|
|
1558
|
-
if (!currentProfileName) {
|
|
1559
|
-
return
|
|
1632
|
+
: (settingsService.get('currentProfile') ?? null);
|
|
1633
|
+
if (!currentProfileName || currentProfileName.trim() === '') {
|
|
1634
|
+
return undefined;
|
|
1560
1635
|
}
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1636
|
+
return {
|
|
1637
|
+
providerId: providerName,
|
|
1638
|
+
profileId: currentProfileName.trim(),
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
catch (error) {
|
|
1642
|
+
logger.debug(`Could not resolve current profile session metadata for ${providerName}:`, error);
|
|
1643
|
+
return undefined;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
async getProfileBuckets(providerName, metadata) {
|
|
1647
|
+
// Prefer the request-scoped profile identity supplied by the caller.
|
|
1648
|
+
const requestedProfileName = typeof metadata?.profileId === 'string' &&
|
|
1649
|
+
metadata.profileId.trim() !== ''
|
|
1650
|
+
? metadata.profileId.trim()
|
|
1651
|
+
: null;
|
|
1652
|
+
let currentProfileName = requestedProfileName;
|
|
1653
|
+
if (!currentProfileName) {
|
|
1654
|
+
try {
|
|
1655
|
+
// Fall back to the CLI runtime's current profile only when the request
|
|
1656
|
+
// did not provide an explicit profile identity.
|
|
1657
|
+
const { getCliRuntimeServices } = await import('../runtime/runtimeSettings.js');
|
|
1658
|
+
const { settingsService } = getCliRuntimeServices();
|
|
1659
|
+
currentProfileName =
|
|
1660
|
+
typeof settingsService.getCurrentProfileName === 'function'
|
|
1661
|
+
? settingsService.getCurrentProfileName()
|
|
1662
|
+
: (settingsService.get('currentProfile') ??
|
|
1663
|
+
null);
|
|
1574
1664
|
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
typeof profile.auth === 'object' &&
|
|
1579
|
-
'type' in profile.auth &&
|
|
1580
|
-
profile.auth.type === 'oauth' &&
|
|
1581
|
-
'buckets' in profile.auth &&
|
|
1582
|
-
Array.isArray(profile.auth.buckets)) {
|
|
1583
|
-
return profile.auth.buckets;
|
|
1665
|
+
catch (error) {
|
|
1666
|
+
logger.debug(`Could not resolve current profile for ${providerName}:`, error);
|
|
1667
|
+
return [];
|
|
1584
1668
|
}
|
|
1669
|
+
}
|
|
1670
|
+
if (!currentProfileName) {
|
|
1585
1671
|
return [];
|
|
1586
1672
|
}
|
|
1673
|
+
let profile;
|
|
1674
|
+
try {
|
|
1675
|
+
// Load the profile to check for auth.buckets
|
|
1676
|
+
const profileManager = await createProfileManager();
|
|
1677
|
+
profile = await profileManager.loadProfile(currentProfileName);
|
|
1678
|
+
}
|
|
1587
1679
|
catch (error) {
|
|
1588
1680
|
logger.debug(`Could not load profile buckets for ${providerName}:`, error);
|
|
1681
|
+
if (requestedProfileName) {
|
|
1682
|
+
throw error;
|
|
1683
|
+
}
|
|
1684
|
+
return [];
|
|
1685
|
+
}
|
|
1686
|
+
// Issue #1468: Verify the profile's provider matches the requested provider
|
|
1687
|
+
// Without this check, buckets from one provider's profile could be returned
|
|
1688
|
+
// when requesting buckets for a different provider, causing token storage
|
|
1689
|
+
// corruption (e.g., Anthropic tokens saved under codex:bucket keys)
|
|
1690
|
+
const profileProvider = 'provider' in profile && typeof profile.provider === 'string'
|
|
1691
|
+
? profile.provider
|
|
1692
|
+
: null;
|
|
1693
|
+
if (profileProvider !== providerName) {
|
|
1694
|
+
logger.debug(`Profile provider '${profileProvider}' does not match requested provider '${providerName}', returning empty buckets`);
|
|
1589
1695
|
return [];
|
|
1590
1696
|
}
|
|
1697
|
+
// Check if profile has auth.buckets for this provider
|
|
1698
|
+
if ('auth' in profile &&
|
|
1699
|
+
profile.auth &&
|
|
1700
|
+
typeof profile.auth === 'object' &&
|
|
1701
|
+
'type' in profile.auth &&
|
|
1702
|
+
profile.auth.type === 'oauth' &&
|
|
1703
|
+
'buckets' in profile.auth &&
|
|
1704
|
+
Array.isArray(profile.auth.buckets)) {
|
|
1705
|
+
return profile.auth.buckets;
|
|
1706
|
+
}
|
|
1707
|
+
return [];
|
|
1591
1708
|
}
|
|
1592
1709
|
/**
|
|
1593
1710
|
* Authenticate multiple OAuth buckets sequentially using MultiBucketAuthenticator
|
|
@@ -1596,7 +1713,7 @@ export class OAuthManager {
|
|
|
1596
1713
|
* Issue 913: This method now supports eager authentication of all buckets upfront,
|
|
1597
1714
|
* filtering out already-authenticated buckets to avoid unnecessary prompts.
|
|
1598
1715
|
*/
|
|
1599
|
-
async authenticateMultipleBuckets(providerName, buckets) {
|
|
1716
|
+
async authenticateMultipleBuckets(providerName, buckets, requestMetadata) {
|
|
1600
1717
|
const { MultiBucketAuthenticator } = await import('./MultiBucketAuthenticator.js');
|
|
1601
1718
|
// Get ephemeral settings for timing controls
|
|
1602
1719
|
const { getEphemeralSetting: getRuntimeEphemeralSetting } = await import('../runtime/runtimeSettings.js');
|
|
@@ -1851,7 +1968,7 @@ export class OAuthManager {
|
|
|
1851
1968
|
if (buckets.length > 1) {
|
|
1852
1969
|
const config = this.getConfig?.();
|
|
1853
1970
|
if (config) {
|
|
1854
|
-
const handler = new BucketFailoverHandlerImpl(buckets, providerName, this);
|
|
1971
|
+
const handler = new BucketFailoverHandlerImpl(buckets, providerName, this, requestMetadata);
|
|
1855
1972
|
config.setBucketFailoverHandler(handler);
|
|
1856
1973
|
logger.debug('Bucket failover handler configured', {
|
|
1857
1974
|
provider: providerName,
|