maxpool 1.5.66 → 1.5.68
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/package.json +1 -1
- package/src/account-manager.js +34 -2
- package/src/index.js +9 -2
- package/src/tui.js +16 -1
package/package.json
CHANGED
package/src/account-manager.js
CHANGED
|
@@ -2692,6 +2692,17 @@ export class AccountManager {
|
|
|
2692
2692
|
// ~60s forever (hammering Anthropic's OAuth endpoint). Cleared on re-login.
|
|
2693
2693
|
if (account.refreshDead) return false;
|
|
2694
2694
|
|
|
2695
|
+
// A DISABLED account never spends its single-use refresh token. The prober still
|
|
2696
|
+
// READS its quota by design (you disable an exhausted account and still want to
|
|
2697
|
+
// watch it recover — prober.js:73), but rotation is a WRITE that can permanently
|
|
2698
|
+
// brick the account: each rotation invalidates the previous token, so a refresh
|
|
2699
|
+
// that races a restart (or another writer) loses the new token and the account
|
|
2700
|
+
// dies with invalid_grant. Spending that risk on an account the user has
|
|
2701
|
+
// explicitly benched is never worth it — it serves its existing access token for
|
|
2702
|
+
// read-only probing, and re-enabling refreshes on the next real request.
|
|
2703
|
+
// `force` still wins: an explicit re-auth/enable path may need the rotation.
|
|
2704
|
+
if (!force && account.enabled === false) return true;
|
|
2705
|
+
|
|
2695
2706
|
if (!force && !isTokenExpiringSoon(account.expiresAt)) return true;
|
|
2696
2707
|
|
|
2697
2708
|
// Coalesce concurrent refreshes
|
|
@@ -2965,8 +2976,26 @@ export class AccountManager {
|
|
|
2965
2976
|
if (idx >= 0 && this.accounts[idx].inFlight === 0) this.removeAccount(idx);
|
|
2966
2977
|
}
|
|
2967
2978
|
}
|
|
2979
|
+
// Remove state-restored (header-based) providers whose token is now served by a
|
|
2980
|
+
// config provider — otherwise the same key shows up twice (once as the old
|
|
2981
|
+
// "glm-fallback"/"kimi-fallback" from state.json, once as the config provider).
|
|
2982
|
+
const configTokens = new Set(
|
|
2983
|
+
entries.map(e => e.token).filter(Boolean),
|
|
2984
|
+
);
|
|
2985
|
+
for (const a of [...this.accounts]) {
|
|
2986
|
+
if (!a.configSourced && a.type === 'provider' && a.credential && configTokens.has(a.credential)) {
|
|
2987
|
+
const idx = this.accounts.indexOf(a);
|
|
2988
|
+
if (idx >= 0 && this.accounts[idx].inFlight === 0) this.removeAccount(idx);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2968
2991
|
for (const entry of entries) {
|
|
2969
2992
|
if (!entry || !entry.name || !entry.provider) continue;
|
|
2993
|
+
// Default model maps — match what `cc all` sends via headers. Users can override
|
|
2994
|
+
// per-provider in config. Without these z.ai returns [1210 Invalid API parameter].
|
|
2995
|
+
const defaultModelMap = entry.provider === 'zai'
|
|
2996
|
+
? { opus: 'glm-5.2', sonnet: 'glm-5.2', haiku: 'glm-5.1', default: 'glm-5.2' }
|
|
2997
|
+
: null;
|
|
2998
|
+
const defaultModel = entry.provider === 'kimi' ? 'kimi-k3' : null;
|
|
2970
2999
|
this.upsertRuntimeAccount({
|
|
2971
3000
|
name: entry.name,
|
|
2972
3001
|
type: 'provider',
|
|
@@ -2978,10 +3007,13 @@ export class AccountManager {
|
|
|
2978
3007
|
authHeader: 'authorization',
|
|
2979
3008
|
profiles: ['all'],
|
|
2980
3009
|
priority: Number.isFinite(entry.priority) ? entry.priority : 10,
|
|
2981
|
-
modelMap: entry.modelMap,
|
|
2982
|
-
model: entry.model,
|
|
3010
|
+
modelMap: entry.modelMap || defaultModelMap,
|
|
3011
|
+
model: entry.model || defaultModel,
|
|
2983
3012
|
stripBetaHeaders: true,
|
|
2984
3013
|
configSourced: true,
|
|
3014
|
+
// An explicitly-disabled provider in config stays benched across restarts —
|
|
3015
|
+
// a second GLM account you keep configured but off (a teammate's, a spare).
|
|
3016
|
+
enabled: entry.enabled !== false,
|
|
2985
3017
|
});
|
|
2986
3018
|
if (!entry.token) {
|
|
2987
3019
|
const a = this.accounts.find(a => a.name === entry.name);
|
package/src/index.js
CHANGED
|
@@ -537,16 +537,23 @@ async function serverWorkerCommand() {
|
|
|
537
537
|
const logTo = argValue('--log-to');
|
|
538
538
|
if (logTo) config.logDir = logTo;
|
|
539
539
|
|
|
540
|
-
|
|
540
|
+
// A provider-only fleet is valid: a teammate may have a GLM/Kimi key and no Claude
|
|
541
|
+
// account at all. Counting only `accounts` refused to start for them — the exact
|
|
542
|
+
// onboarding case this config-provider feature exists to serve.
|
|
543
|
+
if (config.accounts.length === 0 && !(Array.isArray(config.providers) && config.providers.length > 0)) {
|
|
541
544
|
console.error('No accounts configured.\n');
|
|
542
545
|
console.error('Add an account first:');
|
|
543
546
|
console.error(' maxpool login OAuth login via browser');
|
|
544
547
|
console.error(' maxpool login --api Add an API key');
|
|
548
|
+
console.error(' (or add a GLM/Kimi provider from the TUI: press p → a)');
|
|
545
549
|
process.exit(1);
|
|
546
550
|
}
|
|
547
551
|
|
|
548
552
|
const accounts = await resolveAccounts(config);
|
|
549
|
-
|
|
553
|
+
// Config providers are loaded further below (they need an AccountManager first), so
|
|
554
|
+
// a provider-only fleet legitimately has zero accounts HERE. Only bail when there is
|
|
555
|
+
// nothing to load at all.
|
|
556
|
+
if (accounts.length === 0 && !(Array.isArray(config.providers) && config.providers.length > 0)) {
|
|
550
557
|
console.error('No valid accounts after initialization');
|
|
551
558
|
process.exit(1);
|
|
552
559
|
}
|
package/src/tui.js
CHANGED
|
@@ -585,6 +585,18 @@ export class TUI {
|
|
|
585
585
|
}
|
|
586
586
|
}
|
|
587
587
|
|
|
588
|
+
// Derive a human-readable account name from a GCP secret name.
|
|
589
|
+
// RESTRICTED_AL_MAXPOOL_ZAI → glm al | ZAI_API_KEY → glm primary
|
|
590
|
+
// RESTRICTED_MAX_KIMI_API_KEY → kimi max | KIMI_API_KEY → kimi primary
|
|
591
|
+
static deriveProviderName(provider, secretName) {
|
|
592
|
+
const provLabel = provider === 'kimi' ? 'kimi' : 'glm';
|
|
593
|
+
const s = String(secretName || '').toUpperCase();
|
|
594
|
+
// Try to extract a user from RESTRICTED_<USER>_...
|
|
595
|
+
const m = /^RESTRICTED_([A-Z]+)_/.exec(s);
|
|
596
|
+
const user = m ? m[1].toLowerCase() : null;
|
|
597
|
+
return user ? `${provLabel} ${user}` : `${provLabel} primary`;
|
|
598
|
+
}
|
|
599
|
+
|
|
588
600
|
_keyProviders(k) {
|
|
589
601
|
if (k === 'a') {
|
|
590
602
|
this._providerAddStep('name');
|
|
@@ -620,9 +632,12 @@ export class TUI {
|
|
|
620
632
|
this.inputCb = value => {
|
|
621
633
|
const provider = String(value || '').trim().toLowerCase();
|
|
622
634
|
if (provider !== 'zai' && provider !== 'kimi') { this._addLog('Type must be zai or kimi'); this.mode = 'providers'; return; }
|
|
623
|
-
|
|
635
|
+
// Suggest a name derived from the provider type
|
|
636
|
+
const suggested = provider === 'kimi' ? 'kimi' : 'glm';
|
|
637
|
+
this._providerAddStep('secret', { ...prev, provider, suggestedName: suggested });
|
|
624
638
|
};
|
|
625
639
|
} else if (step === 'secret') {
|
|
640
|
+
const suggested = TUI.deriveProviderName(prev.provider, '');
|
|
626
641
|
this.mode = 'input';
|
|
627
642
|
this.inputPrompt = `${prev.name}: GCP secret name OR paste API key directly`;
|
|
628
643
|
this.inputBuf = '';
|