dsh-subscriptions 0.1.5 → 0.2.2
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/README.md +212 -154
- package/lib/auth/claude-code-creds.js +31 -9
- package/lib/auth/device-flow.js +16 -9
- package/lib/auth/oauth-flow.js +13 -10
- package/lib/auth/rpc.d.ts +73 -2
- package/lib/auth/rpc.js +124 -23
- package/lib/auth/store.js +80 -31
- package/lib/client/CursorCard.d.ts +1 -1
- package/lib/client/CursorCard.js +50 -16
- package/lib/client/ExternalUsageCards.d.ts +1 -1
- package/lib/client/ExternalUsageCards.js +37 -16
- package/lib/client/ImageGallery.d.ts +2 -2
- package/lib/client/ImageGallery.js +92 -33
- package/lib/client/ImageGenerateToolview.js +50 -14
- package/lib/client/OllamaCard.d.ts +13 -0
- package/lib/client/OllamaCard.js +94 -0
- package/lib/client/OllamaSettingsManager.d.ts +24 -0
- package/lib/client/OllamaSettingsManager.js +329 -0
- package/lib/client/ProviderAccountManager.d.ts +1 -1
- package/lib/client/ProviderAccountManager.js +164 -49
- package/lib/client/ProviderModelEditor.js +109 -26
- package/lib/client/SpeedSelect.d.ts +62 -14
- package/lib/client/SpeedSelect.js +257 -46
- package/lib/client/SubscriptionUsageBadge.d.ts +5 -11
- package/lib/client/SubscriptionUsageBadge.js +301 -112
- package/lib/client/SubscriptionsSection.d.ts +2 -0
- package/lib/client/SubscriptionsSection.js +259 -85
- package/lib/client/UsageBadgeDisplaySetting.js +35 -12
- package/lib/client/UsageMeter.d.ts +1 -1
- package/lib/client/UsageMeter.js +69 -27
- package/lib/client/VideoGenerateToolview.js +69 -17
- package/lib/client/account-preferences.d.ts +8 -0
- package/lib/client/account-preferences.js +20 -5
- package/lib/client/host-contract.d.ts +11 -2
- package/lib/client/host-contract.js +20 -3
- package/lib/client/index.d.ts +5 -5
- package/lib/client/index.js +34 -12
- package/lib/client/locales.d.ts +78 -0
- package/lib/client/locales.js +121 -34
- package/lib/client/ollama-draft.d.ts +229 -0
- package/lib/client/ollama-draft.js +255 -0
- package/lib/client/reset-action.js +6 -4
- package/lib/client/reset-credits-view.d.ts +1 -1
- package/lib/client/reset-credits-view.js +66 -17
- package/lib/client/subscription-card-styles.js +94 -24
- package/lib/client/usage-color-preferences.js +4 -2
- package/lib/client/usage-pace.d.ts +7 -0
- package/lib/client/usage-pace.js +43 -10
- package/lib/client.js +1647 -266
- package/lib/client.js.map +1 -1
- package/lib/http.js +1 -1
- package/lib/index.d.ts +28 -3
- package/lib/index.js +9329 -8217
- package/lib/model-defaults.js +4 -2
- package/lib/provider-settings.d.ts +14 -1
- package/lib/provider-settings.js +64 -20
- package/lib/providers/account-preferences.js +51 -22
- package/lib/providers/accounts.js +15 -9
- package/lib/providers/antigravity-oauth-client.js +2 -2
- package/lib/providers/antigravity.js +141 -68
- package/lib/providers/catalog-store.js +120 -41
- package/lib/providers/claude-cli-version.js +3 -3
- package/lib/providers/claude.d.ts +23 -3
- package/lib/providers/claude.js +167 -92
- package/lib/providers/codex-client-version.js +3 -3
- package/lib/providers/codex-search.js +40 -13
- package/lib/providers/codex.js +196 -112
- package/lib/providers/common.d.ts +14 -0
- package/lib/providers/common.js +118 -38
- package/lib/providers/copilot.js +143 -82
- package/lib/providers/cursor-adapter.js +42 -18
- package/lib/providers/cursor-auth.d.ts +1 -1
- package/lib/providers/cursor-auth.js +69 -24
- package/lib/providers/cursor-model-settings.js +14 -5
- package/lib/providers/cursor-usage.js +27 -13
- package/lib/providers/external-usage-controller.d.ts +9 -2
- package/lib/providers/external-usage-controller.js +30 -7
- package/lib/providers/external-usage.d.ts +8 -0
- package/lib/providers/external-usage.js +140 -20
- package/lib/providers/grok.js +152 -89
- package/lib/providers/image-pool.js +29 -11
- package/lib/providers/minimax-usage.js +31 -11
- package/lib/providers/npm-cli-version.js +10 -3
- package/lib/providers/ollama-settings.d.ts +90 -0
- package/lib/providers/ollama-settings.js +213 -0
- package/lib/providers/ollama-web.d.ts +46 -0
- package/lib/providers/ollama-web.js +231 -0
- package/lib/providers/ollama.d.ts +107 -0
- package/lib/providers/ollama.js +608 -0
- package/lib/providers/pool-family.js +11 -3
- package/lib/providers/pool-health.js +28 -8
- package/lib/providers/pool-usage.js +16 -9
- package/lib/providers/pool.d.ts +8 -0
- package/lib/providers/pool.js +81 -42
- package/lib/providers/rate-limit.js +8 -2
- package/lib/providers/replay.js +40 -17
- package/lib/providers/reset-redemption.js +18 -6
- package/lib/tools/image-generate.js +123 -65
- package/lib/tools/registration.js +5 -2
- package/lib/tools/video-generate.js +51 -31
- package/lib/tools/x-search.js +50 -22
- package/lib/translate/anthropic.js +85 -37
- package/lib/translate/antigravity-schema.js +71 -28
- package/lib/translate/antigravity-thinking.js +22 -9
- package/lib/translate/antigravity.js +141 -50
- package/lib/translate/chat-completions.js +67 -23
- package/lib/translate/resolved.js +55 -21
- package/lib/translate/responses.js +69 -29
- package/lib/translate/sse.js +4 -1
- package/package.json +26 -4
|
@@ -46,6 +46,13 @@ export declare function validateModels(models: readonly ModelEntry[], label: str
|
|
|
46
46
|
* @throws SyntaxError with the local origin and a fixed omission marker.
|
|
47
47
|
*/
|
|
48
48
|
export declare function parseProviderJson<T = unknown>(input: Response | string, origin: string): Promise<T>;
|
|
49
|
+
/**
|
|
50
|
+
* Read a bounded JSON body; oversized or malformed payloads fail loudly.
|
|
51
|
+
* Bytes accumulate per chunk and the stream is cancelled past the cap, so
|
|
52
|
+
* the bound limits memory even when the body has no end; a declared
|
|
53
|
+
* over-cap `content-length` fails before the first read.
|
|
54
|
+
*/
|
|
55
|
+
export declare function readBoundedJson(response: Response, label: string, maxBytes: number): Promise<unknown>;
|
|
49
56
|
/** Optional per-call hooks {@link httpLlmError} uses to read a rate-limit window. */
|
|
50
57
|
export interface HttpLlmErrorOptions {
|
|
51
58
|
/**
|
|
@@ -212,6 +219,13 @@ export interface UsageWindow {
|
|
|
212
219
|
windowDurationMs?: number;
|
|
213
220
|
/** Provider-verified fixed interval; permits deriving start from end and duration. */
|
|
214
221
|
fixedWindow?: boolean;
|
|
222
|
+
/**
|
|
223
|
+
* Display-only start the reader assumed (kind or label based, clamped
|
|
224
|
+
* to now). The time marker may ride it; pace and color must never —
|
|
225
|
+
* it is not evidence of when the window opened. Mutually exclusive
|
|
226
|
+
* with `startsAt`.
|
|
227
|
+
*/
|
|
228
|
+
displayStartsAt?: number;
|
|
215
229
|
}
|
|
216
230
|
/** One banked reset credit (Codex), or one grant containing resets (Claude). */
|
|
217
231
|
export interface ResetCredit {
|
package/lib/providers/common.js
CHANGED
|
@@ -22,19 +22,22 @@ export function validateModels(models, label) {
|
|
|
22
22
|
if (model.name !== undefined && model.name.length === 0) {
|
|
23
23
|
throw new Error(`${label}: catalog model "${model.id}" has an empty name`);
|
|
24
24
|
}
|
|
25
|
-
if (model.contextWindow !== undefined
|
|
26
|
-
|
|
25
|
+
if (model.contextWindow !== undefined &&
|
|
26
|
+
(!Number.isInteger(model.contextWindow) || model.contextWindow <= 0)) {
|
|
27
27
|
throw new Error(`${label}: catalog model "${model.id}" contextWindow must be a positive integer`);
|
|
28
28
|
}
|
|
29
|
-
if (model.maxTokens !== undefined &&
|
|
29
|
+
if (model.maxTokens !== undefined &&
|
|
30
|
+
(!Number.isInteger(model.maxTokens) || model.maxTokens <= 0)) {
|
|
30
31
|
throw new Error(`${label}: catalog model "${model.id}" maxTokens must be a positive integer`);
|
|
31
32
|
}
|
|
32
|
-
if (model.inputModalities !== undefined
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
if (model.inputModalities !== undefined &&
|
|
34
|
+
(model.inputModalities.length === 0 ||
|
|
35
|
+
model.inputModalities.some((modality) => modality !== 'text' && modality !== 'image'))) {
|
|
35
36
|
throw new Error(`${label}: catalog model "${model.id}" inputModalities must be a non-empty list of "text"/"image"`);
|
|
36
37
|
}
|
|
37
|
-
if (model.wire !== undefined &&
|
|
38
|
+
if (model.wire !== undefined &&
|
|
39
|
+
model.wire !== 'chat-completions' &&
|
|
40
|
+
model.wire !== 'responses') {
|
|
38
41
|
throw new Error(`${label}: catalog model "${model.id}" wire must be "chat-completions" or "responses"`);
|
|
39
42
|
}
|
|
40
43
|
if (seen.has(model.id))
|
|
@@ -42,11 +45,15 @@ export function validateModels(models, label) {
|
|
|
42
45
|
seen.add(model.id);
|
|
43
46
|
return {
|
|
44
47
|
id: model.id,
|
|
45
|
-
...model.name === undefined ? {} : { name: model.name },
|
|
46
|
-
...model.contextWindow === undefined
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
...model.
|
|
48
|
+
...(model.name === undefined ? {} : { name: model.name }),
|
|
49
|
+
...(model.contextWindow === undefined
|
|
50
|
+
? {}
|
|
51
|
+
: { contextWindow: model.contextWindow }),
|
|
52
|
+
...(model.maxTokens === undefined ? {} : { maxTokens: model.maxTokens }),
|
|
53
|
+
...(model.inputModalities === undefined
|
|
54
|
+
? {}
|
|
55
|
+
: { inputModalities: [...model.inputModalities] }),
|
|
56
|
+
...(model.wire === undefined ? {} : { wire: model.wire }),
|
|
50
57
|
};
|
|
51
58
|
});
|
|
52
59
|
}
|
|
@@ -80,9 +87,61 @@ export async function parseProviderJson(input, origin) {
|
|
|
80
87
|
catch (error) {
|
|
81
88
|
if (!(error instanceof SyntaxError))
|
|
82
89
|
throw error;
|
|
90
|
+
// Do not attach the original SyntaxError: Node's message quotes the input, and the host renders cause.
|
|
91
|
+
// eslint-disable-next-line preserve-caught-error -- the replacement omits the provider excerpt on purpose
|
|
83
92
|
throw new SyntaxError(providerDiagnostic(`${origin}: invalid JSON`, true));
|
|
84
93
|
}
|
|
85
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Read a bounded JSON body; oversized or malformed payloads fail loudly.
|
|
97
|
+
* Bytes accumulate per chunk and the stream is cancelled past the cap, so
|
|
98
|
+
* the bound limits memory even when the body has no end; a declared
|
|
99
|
+
* over-cap `content-length` fails before the first read.
|
|
100
|
+
*/
|
|
101
|
+
export async function readBoundedJson(response, label, maxBytes) {
|
|
102
|
+
const declared = Number(response.headers.get('content-length'));
|
|
103
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
104
|
+
// Stop the socket too: the headers arrived, but the body must never flow.
|
|
105
|
+
await response.body?.cancel().catch(() => undefined);
|
|
106
|
+
throw new Error(`${label} response exceeds ${String(maxBytes)} bytes`);
|
|
107
|
+
}
|
|
108
|
+
if (response.body === null)
|
|
109
|
+
throw new Error(`${label} response has no body`);
|
|
110
|
+
const reader = response.body.getReader();
|
|
111
|
+
const decoder = new TextDecoder();
|
|
112
|
+
let bytes = 0;
|
|
113
|
+
let text = '';
|
|
114
|
+
try {
|
|
115
|
+
for (;;) {
|
|
116
|
+
const chunk = await reader.read();
|
|
117
|
+
if (chunk.done)
|
|
118
|
+
break;
|
|
119
|
+
bytes += chunk.value.byteLength;
|
|
120
|
+
if (bytes > maxBytes) {
|
|
121
|
+
// Cancel before throwing so the live stream does not linger behind
|
|
122
|
+
// a rejection the caller already stopped listening to.
|
|
123
|
+
await reader.cancel().catch(() => undefined);
|
|
124
|
+
throw new Error(`${label} response exceeds ${String(maxBytes)} bytes`);
|
|
125
|
+
}
|
|
126
|
+
text += decoder.decode(chunk.value, { stream: true });
|
|
127
|
+
}
|
|
128
|
+
text += decoder.decode();
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
try {
|
|
132
|
+
reader.releaseLock();
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
/* best effort */
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
return JSON.parse(text);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
throw new Error(`${label} response is not JSON`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
86
145
|
/**
|
|
87
146
|
* Build an LlmError from a non-2xx provider response, mapping the status to a
|
|
88
147
|
* stable code and, for a rate-limited request, the disclosed reset instant to
|
|
@@ -139,7 +198,9 @@ export async function httpLlmError(response, label, options = {}) {
|
|
|
139
198
|
// re-hitting the same closed window.
|
|
140
199
|
const rateLimited = response.status === 429;
|
|
141
200
|
const reset = rateLimited
|
|
142
|
-
? options.rateLimitReset?.(response, body, now) ??
|
|
201
|
+
? (options.rateLimitReset?.(response, body, now) ??
|
|
202
|
+
googleQuotaReset(body, now) ??
|
|
203
|
+
retryAfterInstant(response, now))
|
|
143
204
|
: retryAfterInstant(response, now);
|
|
144
205
|
if (reset === undefined && rateLimited) {
|
|
145
206
|
// Header names and values are provider-controlled too; none are displayed.
|
|
@@ -147,7 +208,9 @@ export async function httpLlmError(response, label, options = {}) {
|
|
|
147
208
|
}
|
|
148
209
|
return new LlmError(message, code, {
|
|
149
210
|
status: response.status,
|
|
150
|
-
...reset === undefined
|
|
211
|
+
...(reset === undefined
|
|
212
|
+
? {}
|
|
213
|
+
: { providerRetryAfterMs: waitFromReset(reset, now) }),
|
|
151
214
|
});
|
|
152
215
|
}
|
|
153
216
|
/**
|
|
@@ -236,9 +299,13 @@ export function idleWatchdog(caller, timeoutMs) {
|
|
|
236
299
|
*/
|
|
237
300
|
export function mapFetchFailure(label, error, watchdog, caller) {
|
|
238
301
|
if (watchdog.timedOut())
|
|
239
|
-
return new LlmError(`${label} stream idle timeout`, 'TIMEOUT', {
|
|
302
|
+
return new LlmError(`${label} stream idle timeout`, 'TIMEOUT', {
|
|
303
|
+
cause: error,
|
|
304
|
+
});
|
|
240
305
|
if (caller?.aborted === true)
|
|
241
|
-
return new LlmError(`${label} request aborted by caller`, 'ABORTED', {
|
|
306
|
+
return new LlmError(`${label} request aborted by caller`, 'ABORTED', {
|
|
307
|
+
cause: error,
|
|
308
|
+
});
|
|
242
309
|
if (error instanceof LlmError)
|
|
243
310
|
return error;
|
|
244
311
|
return new LlmError(`${label} request failed`, 'TRANSPORT', { cause: error });
|
|
@@ -339,10 +406,11 @@ export class TokenManager {
|
|
|
339
406
|
async session(forceRefresh = false) {
|
|
340
407
|
const session = await this.options.load();
|
|
341
408
|
if (session === undefined) {
|
|
342
|
-
throw new LlmError(`dsh-subscriptions: not logged in to ${this.options.displayName}; `
|
|
343
|
-
|
|
409
|
+
throw new LlmError(`dsh-subscriptions: not logged in to ${this.options.displayName}; ` +
|
|
410
|
+
'log in via Settings → Subscriptions in the dsh web app', 'MISSING_CREDENTIAL');
|
|
344
411
|
}
|
|
345
|
-
if (!forceRefresh &&
|
|
412
|
+
if (!forceRefresh &&
|
|
413
|
+
session.expiresAt - Date.now() > this.options.preemptMs) {
|
|
346
414
|
return session;
|
|
347
415
|
}
|
|
348
416
|
this.inflight ??= this.doRefresh(session).finally(() => {
|
|
@@ -358,7 +426,9 @@ export class TokenManager {
|
|
|
358
426
|
// log out an account that just signed in. Serve it instead.
|
|
359
427
|
const attempted = this.attempted;
|
|
360
428
|
const stored = await this.options.load();
|
|
361
|
-
if (stored !== undefined &&
|
|
429
|
+
if (stored !== undefined &&
|
|
430
|
+
attempted !== undefined &&
|
|
431
|
+
stored.refreshToken !== attempted.refreshToken) {
|
|
362
432
|
return stored;
|
|
363
433
|
}
|
|
364
434
|
await this.options.remove();
|
|
@@ -378,9 +448,9 @@ export class TokenManager {
|
|
|
378
448
|
// A concurrent caller may have refreshed while this one waited: re-read
|
|
379
449
|
// the store and skip the round trip when the stored session is fresh.
|
|
380
450
|
const current = await this.options.load();
|
|
381
|
-
if (current !== undefined
|
|
382
|
-
|
|
383
|
-
|
|
451
|
+
if (current !== undefined &&
|
|
452
|
+
current.accessToken !== session.accessToken &&
|
|
453
|
+
current.expiresAt - Date.now() > this.options.preemptMs) {
|
|
384
454
|
return current;
|
|
385
455
|
}
|
|
386
456
|
const attempted = current ?? session;
|
|
@@ -398,14 +468,14 @@ export const DISCOVERY_TIMEOUT_MS = 10_000;
|
|
|
398
468
|
*/
|
|
399
469
|
export function withTimeout(work, timeoutMs) {
|
|
400
470
|
const signal = AbortSignal.timeout(timeoutMs);
|
|
401
|
-
const aborted = new Promise(resolve => {
|
|
471
|
+
const aborted = new Promise((resolve) => {
|
|
402
472
|
if (signal.aborted)
|
|
403
473
|
resolve(undefined);
|
|
404
474
|
else
|
|
405
475
|
signal.addEventListener('abort', () => resolve(undefined), { once: true });
|
|
406
476
|
});
|
|
407
477
|
return Promise.race([
|
|
408
|
-
work(signal).then(value => (signal.aborted ? undefined : value), (error) => {
|
|
478
|
+
work(signal).then((value) => (signal.aborted ? undefined : value), (error) => {
|
|
409
479
|
if (signal.aborted)
|
|
410
480
|
return undefined;
|
|
411
481
|
throw error;
|
|
@@ -415,7 +485,9 @@ export function withTimeout(work, timeoutMs) {
|
|
|
415
485
|
}
|
|
416
486
|
/** Display name for a wire reasoning-effort identifier. */
|
|
417
487
|
export function effortDisplayName(effort) {
|
|
418
|
-
return effort === 'xhigh'
|
|
488
|
+
return effort === 'xhigh'
|
|
489
|
+
? 'Extra High'
|
|
490
|
+
: effort.charAt(0).toUpperCase() + effort.slice(1);
|
|
419
491
|
}
|
|
420
492
|
/**
|
|
421
493
|
* Fold a configured per-model default effort into a reasoning block, keeping
|
|
@@ -444,7 +516,9 @@ export function mergeReasoning(configuredDefault, base, options) {
|
|
|
444
516
|
? undefined
|
|
445
517
|
: {
|
|
446
518
|
efforts: [...base.efforts],
|
|
447
|
-
...(base.defaultEffort === undefined
|
|
519
|
+
...(base.defaultEffort === undefined
|
|
520
|
+
? {}
|
|
521
|
+
: { defaultEffort: base.defaultEffort }),
|
|
448
522
|
};
|
|
449
523
|
if (configuredDefault === undefined)
|
|
450
524
|
return detached;
|
|
@@ -454,10 +528,13 @@ export function mergeReasoning(configuredDefault, base, options) {
|
|
|
454
528
|
// catalog does not cover). Inventing a reasoning block here would claim a
|
|
455
529
|
// capability nobody advertised; only a fallback-based provider may.
|
|
456
530
|
return options?.extendable === true
|
|
457
|
-
? {
|
|
531
|
+
? {
|
|
532
|
+
efforts: [{ id: effort, name: effortDisplayName(effort) }],
|
|
533
|
+
defaultEffort: effort,
|
|
534
|
+
}
|
|
458
535
|
: undefined;
|
|
459
536
|
}
|
|
460
|
-
if (base.efforts.some(entry => entry.id === effort)) {
|
|
537
|
+
if (base.efforts.some((entry) => entry.id === effort)) {
|
|
461
538
|
return { efforts: [...base.efforts], defaultEffort: effort };
|
|
462
539
|
}
|
|
463
540
|
if (options?.extendable !== true)
|
|
@@ -536,7 +613,9 @@ export class ModelCatalogCache {
|
|
|
536
613
|
if (this.persistence === undefined)
|
|
537
614
|
return Promise.resolve();
|
|
538
615
|
this.seeded ??= this.persistence.load().then((snapshot) => {
|
|
539
|
-
if (snapshot !== undefined &&
|
|
616
|
+
if (snapshot !== undefined &&
|
|
617
|
+
this.entry === undefined &&
|
|
618
|
+
!this.seedDisabled) {
|
|
540
619
|
this.entry = snapshot;
|
|
541
620
|
}
|
|
542
621
|
}, () => undefined);
|
|
@@ -612,8 +691,8 @@ export class ModelCatalogCache {
|
|
|
612
691
|
}
|
|
613
692
|
/** Whether discovery failed because the stored login is gone. */
|
|
614
693
|
export function isMissingOrInvalidCredential(error) {
|
|
615
|
-
return error instanceof LlmError
|
|
616
|
-
|
|
694
|
+
return (error instanceof LlmError &&
|
|
695
|
+
(error.code === 'MISSING_CREDENTIAL' || error.code === 'INVALID_CREDENTIAL'));
|
|
617
696
|
}
|
|
618
697
|
/** Whether discovery stopped because the caller cancelled or the timeout fired. */
|
|
619
698
|
export function isDiscoveryAborted(error, signal) {
|
|
@@ -621,9 +700,9 @@ export function isDiscoveryAborted(error, signal) {
|
|
|
621
700
|
return true;
|
|
622
701
|
// Only treat abort-shaped errors as cancellation when this call had a signal;
|
|
623
702
|
// a refresh TimeoutError must not fail the whole picker union.
|
|
624
|
-
return signal !== undefined
|
|
625
|
-
|
|
626
|
-
|
|
703
|
+
return (signal !== undefined &&
|
|
704
|
+
error instanceof Error &&
|
|
705
|
+
(error.name === 'AbortError' || error.name === 'TimeoutError'));
|
|
627
706
|
}
|
|
628
707
|
/**
|
|
629
708
|
* Whether discovery failed because the access token was rejected. After
|
|
@@ -632,8 +711,8 @@ export function isDiscoveryAborted(error, signal) {
|
|
|
632
711
|
* the refresh grant still answers, so the store keeps the session.
|
|
633
712
|
*/
|
|
634
713
|
export function isDiscoveryAuthFailure(error) {
|
|
635
|
-
return (error instanceof OAuthEndpointError && error.status === 401)
|
|
636
|
-
|
|
714
|
+
return ((error instanceof OAuthEndpointError && error.status === 401) ||
|
|
715
|
+
(error instanceof LlmError && error.code === 'AUTH'));
|
|
637
716
|
}
|
|
638
717
|
/**
|
|
639
718
|
* Run a catalog fetch, retrying once after a forced token refresh when the
|
|
@@ -653,7 +732,8 @@ export async function discoverOrRetryAuth(session, catalog, run) {
|
|
|
653
732
|
return await run();
|
|
654
733
|
}
|
|
655
734
|
catch (retryError) {
|
|
656
|
-
if (!isMissingOrInvalidCredential(retryError) &&
|
|
735
|
+
if (!isMissingOrInvalidCredential(retryError) &&
|
|
736
|
+
isDiscoveryAuthFailure(retryError)) {
|
|
657
737
|
catalog.invalidate();
|
|
658
738
|
}
|
|
659
739
|
throw retryError;
|