dsh-plugin-subscription-oauth 0.8.5 → 0.8.7

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.
@@ -163,6 +163,12 @@ export declare class ProviderController<S extends SessionBase> {
163
163
  * @throws {ProviderError} with `REAUTH_REQUIRED` when the account must sign in again.
164
164
  */
165
165
  refresh(account?: string): Promise<ProviderRefreshResult>;
166
+ /**
167
+ * Re-read the endpoint's live model list.
168
+ *
169
+ * @param force - fetch again even when a list was already read this session.
170
+ * @returns a fresh status.
171
+ */
166
172
  /**
167
173
  * Re-read the endpoint's live model list.
168
174
  *
@@ -271,6 +271,12 @@ export class ProviderController {
271
271
  this.lastError = undefined;
272
272
  return { account: token.account, expiresAt: token.expiresAt, status: await this.status() };
273
273
  }
274
+ /**
275
+ * Re-read the endpoint's live model list.
276
+ *
277
+ * @param force - fetch again even when a list was already read this session.
278
+ * @returns a fresh status.
279
+ */
274
280
  /**
275
281
  * Re-read the endpoint's live model list.
276
282
  *
@@ -287,23 +293,33 @@ export class ProviderController {
287
293
  // every startup would be noise.
288
294
  if (!await this.options.store.hasAny())
289
295
  return this.status();
290
- let accessToken;
291
- try {
292
- accessToken = (await this.options.tokens.resolve()).accessToken;
293
- }
294
- catch (error) {
295
- // Discovery is a convenience: an unusable session is reported by the
296
- // card's account list, not as a second failure here.
297
- this.options.onWarn?.(`${spec.id} model discovery skipped: ${messageOf(error)}`);
298
- return this.status();
296
+ // The catalog is the union of every signed-in account's live list, because one
297
+ // model id draws on the whole pool: a model any account can serve must appear, and
298
+ // which account answers at call time is the scheduler's choice. A model only the
299
+ // non-default account has is offered, then may fall through to it on dispatch.
300
+ const discovered = [];
301
+ for (const account of Object.keys(await this.options.store.list())) {
302
+ let accessToken;
303
+ try {
304
+ accessToken = (await this.options.tokens.resolve(account)).accessToken;
305
+ }
306
+ catch (error) {
307
+ this.options.onWarn?.(`${spec.id} model discovery for ${account} skipped: ${messageOf(error)}`);
308
+ continue;
309
+ }
310
+ const ids = await spec.discover(accessToken);
311
+ if (ids !== undefined) {
312
+ for (const id of ids)
313
+ if (!discovered.includes(id))
314
+ discovered.push(id);
315
+ }
299
316
  }
300
- const ids = await spec.discover(accessToken);
301
- if (ids !== undefined) {
302
- this.discovered = ids;
303
- spec.applyDiscovery?.(ids);
317
+ if (discovered.length > 0) {
318
+ this.discovered = discovered;
319
+ spec.applyDiscovery?.(discovered);
304
320
  // A model this route now serves is no longer hand-added: keeping the extra
305
321
  // entry would list it as manual and invite a pointless "remove" later.
306
- await this.forgetDiscovered(ids);
322
+ await this.forgetDiscovered(discovered);
307
323
  this.options.onCatalogChanged?.();
308
324
  }
309
325
  return this.status();
@@ -98,9 +98,14 @@ function agentIds(payload) {
98
98
  push(id);
99
99
  }
100
100
  }
101
- if (isRecord(payload.tieredModelIds) && Array.isArray(payload.tieredModelIds.flash)) {
102
- for (const id of payload.tieredModelIds.flash)
103
- push(id);
101
+ if (isRecord(payload.tieredModelIds)) {
102
+ for (const key of Object.keys(payload.tieredModelIds)) {
103
+ const list = payload.tieredModelIds[key];
104
+ if (Array.isArray(list)) {
105
+ for (const id of list)
106
+ push(id);
107
+ }
108
+ }
104
109
  }
105
110
  // No usable grouping: fall back to every entry, which is still better than nothing
106
111
  // as long as the non-agent surfaces are excluded.
@@ -111,6 +116,19 @@ function agentIds(payload) {
111
116
  push(id);
112
117
  }
113
118
  }
119
+ // The response also carries models under API_PROVIDER_GOOGLE_GEMINI /
120
+ // ANTHROPIC_VERTEX / OPENAI_VERTEX that the agent grouping does not name (3.1 Pro,
121
+ // 3 Flash, the flash family). Keep every one of those; NON_AGENT surfaces stay out.
122
+ for (const [id, value] of Object.entries(models)) {
123
+ if (NON_AGENT.test(id))
124
+ continue;
125
+ if (!isRecord(value))
126
+ continue;
127
+ const api = value.apiProvider;
128
+ if (api === 'API_PROVIDER_GOOGLE_GEMINI' || api === 'API_PROVIDER_ANTHROPIC_VERTEX' || api === 'API_PROVIDER_OPENAI_VERTEX') {
129
+ push(id);
130
+ }
131
+ }
114
132
  return ids;
115
133
  }
116
134
  /** Surfaces the agent envelope cannot call. */
@@ -131,6 +149,18 @@ export function parseAntigravityModels(payload) {
131
149
  if (!isRecord(payload) || !isRecord(payload.models))
132
150
  return [];
133
151
  const models = payload.models;
152
+ // TEMP diagnostics: write the raw model-id list to a file so an over-eager filter is
153
+ // inspectable after a discovery run, since the node half's stdout is unreachable in
154
+ // the desktop shell.
155
+ try {
156
+ const all = Object.keys(models);
157
+ const kept = agentIds(payload);
158
+ const { writeFileSync } = require('node:fs');
159
+ const { homedir } = require('node:os');
160
+ const { join } = require('node:path');
161
+ writeFileSync(join(homedir(), '.dsh', 'antigravity-models-debug.json'), JSON.stringify({ all, kept, dropped: all.filter((id) => !kept.includes(id)) }, null, 2), 'utf8');
162
+ }
163
+ catch { /* diagnostics never break parsing */ }
134
164
  const entries = [];
135
165
  for (const wireId of agentIds(payload)) {
136
166
  const info = modelInfo(models[wireId]);
@@ -170,6 +170,7 @@ export declare function fetchAntigravityModels(input: {
170
170
  remainingFraction?: number;
171
171
  resetTime?: string;
172
172
  }>;
173
+ raw: Record<string, unknown>;
173
174
  }>;
174
175
  /** Constructor dependencies for {@link AntigravityAdapter}. */
175
176
  export interface AntigravityAdapterOptions {
@@ -329,6 +329,16 @@ export async function fetchAntigravityModels(input) {
329
329
  if (typeof models !== 'object' || models === null) {
330
330
  throw new LlmError('Antigravity models endpoint returned no models object', 'MALFORMED_RESPONSE');
331
331
  }
332
+ // TEMP diagnostics: write the raw model-id list to a file so an over-eager filter is
333
+ // inspectable after a discovery run, since the node half's stdout is unreachable in
334
+ // the desktop shell.
335
+ try {
336
+ const { writeFileSync } = require('node:fs');
337
+ const { homedir } = require('node:os');
338
+ const { join } = require('node:path');
339
+ writeFileSync(join(homedir(), '.dsh', 'antigravity-raw-models.json'), JSON.stringify(Object.keys(models), null, 2), 'utf8');
340
+ }
341
+ catch { /* diagnostics never break parsing */ }
332
342
  // The response names far more than the agent may call; the filter keeps the
333
343
  // callable grouping and reports the per-model quota for the fallback path.
334
344
  const entries = parseAntigravityModels(payload);
@@ -341,7 +351,7 @@ export async function fetchAntigravityModels(input) {
341
351
  if (quota !== undefined)
342
352
  quotas.set(id, quota);
343
353
  }
344
- return { entries, quotas };
354
+ return { entries, quotas, raw: models };
345
355
  }
346
356
  /** One quota window in the shape the protocol uses. */
347
357
  function quotaWindow(scope, quota) {
@@ -522,8 +532,15 @@ export class AntigravityAdapter extends LlmAdapter {
522
532
  yield* streamAntigravity(response.body, () => { watchdog.pulse(); });
523
533
  return;
524
534
  }
525
- const exhausted = new LlmError(`Antigravity API: every account's allowance is spent [${diag.join(' | ')}]`, EMPTY_RESPONSE_CODE);
526
- throw exhausted;
535
+ // Every account was tried and none served. The headline names the most actionable
536
+ // refusal — a 403 "verify your account" is something the user can fix in the browser;
537
+ // a 429 "allowance spent" just means wait for the reset. The diagnostic lists each
538
+ // account and the status it returned.
539
+ const last403 = diag.reduce((acc, line) => (line.startsWith('403 on') ? line : acc), undefined);
540
+ const message = last403 === undefined
541
+ ? `Antigravity API: every account's allowance is spent [${diag.join(' | ')}]`
542
+ : `Antigravity API request failed: ${last403.replace(/^403 on /, 'account ')} refused (HTTP 403: Verify your account to continue) [${diag.join(' | ')}]`;
543
+ throw new LlmError(message, EMPTY_RESPONSE_CODE);
527
544
  }
528
545
  catch (error) {
529
546
  throw mapFetchFailure('Antigravity API', this.refreshHint(error), watchdog, options.signal);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-subscription-oauth",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "Use subscription accounts as DeepSeek Harness LLM providers — Kimi Code (OAuth device flow), Grok / X Premium (OIDC + PKCE), Google Antigravity (Google OAuth + Cloud Code Assist) and GLM / BigModel (ZCode's browser sign-in) — with per-model visibility, drag-to-reorder Auto fallback and plan quotas from the web Settings page",
5
5
  "license": "MIT",
6
6
  "author": "EricFL1998",