dsh-plugin-subscription-oauth 0.8.15 → 0.8.16

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.
@@ -300,20 +300,26 @@ export class ProviderController {
300
300
  // every startup would be noise.
301
301
  if (!await this.options.store.hasAny())
302
302
  return this.status();
303
- // Discovery resolves tokens to read the model list, but a resolve must never
304
- // open the browser: a transient network failure, or a token the endpoint refuses
305
- // while still being within its stored expiry, is not a signal to start a login.
306
- // Only an explicit user action (the connect button) opens the consent page.
303
+ // Discovery reads the live catalog on startup so the picker shows what the account
304
+ // actually serves without a manual refresh. A resolve must never open the browser:
305
+ // a refresh failure is recorded as the route's last error, never as a login prompt.
307
306
  const discovered = [];
308
307
  for (const account of Object.keys(await this.options.store.list())) {
308
+ // An unexpired token is used as-is; a lapsed one is refreshed in memory so the
309
+ // catalog still loads on startup. The refreshed token is not written back to the
310
+ // store here — a durable renewal still happens on the next real request.
309
311
  let session = await this.options.store.get(account);
310
312
  if (session === undefined)
311
313
  continue;
312
- // A token that is still within its stored expiry is used as-is; a lapsed one
313
- // is a silent skip (the route works, the catalog is just not refreshed).
314
314
  if (session.expiresAt <= Date.now()) {
315
- this.options.onWarn?.(`${spec.id} model discovery for ${account} skipped: token expired`);
316
- continue;
315
+ try {
316
+ const refreshed = await this.options.tokens.resolve(account, true);
317
+ session = { ...session, accessToken: refreshed.accessToken, expiresAt: refreshed.expiresAt };
318
+ }
319
+ catch {
320
+ this.options.onWarn?.(`${spec.id} model discovery for ${account} skipped: token refresh failed`);
321
+ continue;
322
+ }
317
323
  }
318
324
  const ids = await spec.discover(session.accessToken);
319
325
  if (ids !== undefined) {
@@ -446,23 +446,22 @@ export class GrokAdapter extends LlmAdapter {
446
446
  return [];
447
447
  await this.syncUserModels();
448
448
  const hidden = await this.hidden();
449
- // A fast variant is not a picker row, but the host validates a GUI selection
450
- // against the exact list `listModels` returns, so the variant must appear there
451
- // or selecting it is refused with `session/model-unavailable`. The picker's
452
- // menu collapses entries that share one `group` value into a single choice,
453
- // so the variant rides with its base id as the group: the host can name and
454
- // select it, and the picker still shows one row per model.
449
+ // The host validates a GUI selection against the exact ids `listModels` returns
450
+ // and refuses an unknown one with `session/model-unavailable`, and its picker
451
+ // lists every returned id as its own row. So a fast variant is published under
452
+ // its base id: selecting it names the variant the request must send, and the
453
+ // base's own id is withheld so the variant does not appear twice.
455
454
  const imageless = this.catalog().filter((entry) => !isImageModel(entry.id));
456
- const listed = resolveCatalog(imageless, [], hidden).entries.flatMap((entry) => {
455
+ const listed = resolveCatalog(imageless, [], hidden).entries.map((entry) => {
457
456
  const fast = this.fastVariants.variantOf(entry.id);
458
- const base = {
457
+ const id = fast ?? entry.id;
458
+ return {
459
459
  provider: GROK_PROVIDER_ID,
460
- id: entry.id,
460
+ id,
461
461
  name: entry.name,
462
462
  ...entry.description === undefined ? {} : { description: entry.description },
463
463
  ...entry.inputModalities === undefined ? {} : { inputModalities: entry.inputModalities },
464
464
  };
465
- return fast === undefined ? [base] : [base, { provider: GROK_PROVIDER_ID, id: fast, name: entry.name, group: entry.id, description: 'Fast variant', ...entry.inputModalities === undefined ? {} : { inputModalities: entry.inputModalities } }];
466
465
  });
467
466
  return listed;
468
467
  }
@@ -498,10 +497,13 @@ export class GrokAdapter extends LlmAdapter {
498
497
  };
499
498
  }
500
499
  const reasoning = reasoningInfo(entry);
500
+ // The fast selection names itself "Fast" so the picker trigger reads the mode, not
501
+ // the base model it replaces; the id stays the fast wire id the request must send.
502
+ const fast = pair !== undefined && pair.wireId === model;
501
503
  return {
502
504
  provider: GROK_PROVIDER_ID,
503
505
  id: model,
504
- name: entry.name,
506
+ name: fast ? `${entry.name} Fast` : entry.name,
505
507
  ...entry.description === undefined ? {} : { description: entry.description },
506
508
  ...entry.inputModalities === undefined ? {} : { inputModalities: entry.inputModalities },
507
509
  context: { contextWindow: entry.contextWindow },
@@ -206,23 +206,22 @@ export class KimiAdapter extends LlmAdapter {
206
206
  return [];
207
207
  await this.syncUserModels();
208
208
  const hidden = await this.hidden();
209
- // A fast variant is not a picker row, but the host validates a GUI selection
210
- // against the exact list `listModels` returns, so the variant must appear there
211
- // or selecting it is refused with `session/model-unavailable`. The picker's
212
- // menu collapses entries that share one `group` value into a single choice,
213
- // so the variant rides with its base id as the group: the host can name and
214
- // select it, and the picker still shows one row per model.
209
+ // The host validates a GUI selection against the exact ids `listModels` returns
210
+ // and refuses an unknown one with `session/model-unavailable`, and its picker
211
+ // lists every returned id as its own row. So a fast variant is published under
212
+ // its base id: selecting it names the variant the request must send, and the
213
+ // base's own id is withheld so the variant does not appear twice.
215
214
  const imageless = this.catalog().filter((entry) => !isImageModel(entry.id));
216
- const listed = resolveCatalog(imageless, [], hidden).entries.flatMap((entry) => {
215
+ const listed = resolveCatalog(imageless, [], hidden).entries.map((entry) => {
217
216
  const fast = this.fastVariants.variantOf(entry.id);
218
- const base = {
217
+ const id = fast ?? entry.id;
218
+ return {
219
219
  provider: KIMI_PROVIDER_ID,
220
- id: entry.id,
220
+ id,
221
221
  name: entry.name,
222
222
  ...entry.description === undefined ? {} : { description: entry.description },
223
223
  ...entry.inputModalities === undefined ? {} : { inputModalities: entry.inputModalities },
224
224
  };
225
- return fast === undefined ? [base] : [base, { provider: KIMI_PROVIDER_ID, id: fast, name: entry.name, group: entry.id, description: 'Fast variant', ...entry.inputModalities === undefined ? {} : { inputModalities: entry.inputModalities } }];
226
225
  });
227
226
  return listed;
228
227
  }
@@ -268,12 +267,14 @@ export class KimiAdapter extends LlmAdapter {
268
267
  const reasoning = reasoningInfo(entry);
269
268
  // A fast variant is a wire id of its own — the button switches to it and back, so the
270
269
  // selection must keep that id — while an ordinary alias is just another spelling of
271
- // the row and resolves onto it.
270
+ // the row and resolves onto it. The fast selection names itself "Fast" so the picker
271
+ // trigger reads the mode, not the base model it replaces.
272
272
  const pair = this.fastVariants.pairOf(model);
273
+ const fast = pair !== undefined && pair.wireId === model;
273
274
  return {
274
275
  provider: KIMI_PROVIDER_ID,
275
- id: pair !== undefined && pair.wireId === model ? model : entry.id,
276
- name: entry.name,
276
+ id: fast ? model : entry.id,
277
+ name: fast ? `${entry.name} Fast` : entry.name,
277
278
  ...entry.description === undefined ? {} : { description: entry.description },
278
279
  ...entry.inputModalities === undefined ? {} : { inputModalities: entry.inputModalities },
279
280
  context: { contextWindow: entry.contextWindow },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-subscription-oauth",
3
- "version": "0.8.15",
3
+ "version": "0.8.16",
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",