dsh-plugin-subscription-oauth 0.11.0 → 0.12.0

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/lib/index.js CHANGED
@@ -167,7 +167,7 @@ export function apply(ctx, config = {}) {
167
167
  break;
168
168
  }
169
169
  }
170
- const registry = new SubscriptionRegistry(routes);
170
+ const registry = new SubscriptionRegistry(routes, preferences);
171
171
  // `auto` stands in for the other routes, so it is built after them and added
172
172
  // to the same registry it reads. Its list spans routes, which is why it is a
173
173
  // provider of its own rather than a model inside each one.
package/lib/protocol.d.ts CHANGED
@@ -12,7 +12,7 @@
12
12
  /** Prefix of every RPC method this plugin serves. */
13
13
  export declare const RPC_PREFIX = "subscription-oauth.";
14
14
  /** Endpoints the Settings page may call. */
15
- export declare const RPC_ENDPOINTS: readonly ["status", "login", "cancel", "logout", "setDefault", "refresh", "discover", "setModelVisible", "setModelOrder", "addModel", "removeModel", "usage", "reload", "deepseek-balance", "ccswitch-balance", "model-variants"];
15
+ export declare const RPC_ENDPOINTS: readonly ["status", "login", "cancel", "logout", "setDefault", "refresh", "discover", "setModelVisible", "setModelOrder", "addModel", "removeModel", "usage", "reload", "setAutoOn", "deepseek-balance", "ccswitch-balance", "model-variants"];
16
16
  /** One endpoint name. */
17
17
  export type RpcEndpoint = typeof RPC_ENDPOINTS[number];
18
18
  /** Client → host envelope. */
@@ -315,6 +315,10 @@ export interface RpcMap {
315
315
  payload: undefined;
316
316
  value: SubscriptionStatus;
317
317
  };
318
+ setAutoOn: {
319
+ payload: SetAutoOnRequest;
320
+ value: AutoStateResult;
321
+ };
318
322
  'deepseek-balance': {
319
323
  payload: DeepSeekBalanceRequest;
320
324
  value: DeepSeekBalanceResult;
@@ -382,6 +386,14 @@ export interface ModelVariantsRequest {
382
386
  provider: string;
383
387
  id: string;
384
388
  }
389
+ /** Payload of `setAutoOn`: whether the `auto` provider is offered at all. */
390
+ export interface SetAutoOnRequest {
391
+ enabled: boolean;
392
+ }
393
+ /** Result of `setAutoOn`: the `auto` provider's live enabled state. */
394
+ export interface AutoStateResult {
395
+ enabled: boolean;
396
+ }
385
397
  /** Payload of `deepseek-balance`. */
386
398
  export interface DeepSeekBalanceRequest {
387
399
  /** Client metadata the host's account service expects. */
package/lib/protocol.js CHANGED
@@ -26,6 +26,7 @@ export const RPC_ENDPOINTS = [
26
26
  'removeModel',
27
27
  'usage',
28
28
  'reload',
29
+ 'setAutoOn',
29
30
  'deepseek-balance',
30
31
  'ccswitch-balance',
31
32
  'model-variants',
@@ -64,6 +64,9 @@ export class AutoAdapter extends LlmAdapter {
64
64
  return undefined;
65
65
  }
66
66
  async listModels(_provider) {
67
+ // A switched-off `auto` is not offered at all, so the picker shows the routes directly.
68
+ if (!(await this.options.preferences.autoPreferences()).enabled)
69
+ return [];
67
70
  const chain = await this.chain();
68
71
  // An `auto` with nothing to try is not offered at all, rather than offered
69
72
  // and guaranteed to fail.
@@ -116,6 +116,8 @@ interface GrokCliMetadata {
116
116
  export declare function isImageModel(id: string): boolean;
117
117
  /** True when a model id names a fast/lightspeed variant of a base model. */
118
118
  export declare function isFastVariant(id: string): boolean;
119
+ /** The fast word a variant's own id names (`grok-4.7-build-fast` → `fast`), lowercased. */
120
+ export declare function fastWordOf(id: string): string;
119
121
  /**
120
122
  * Every prefix of one model id, longest first, down to its first token.
121
123
  *
@@ -52,18 +52,18 @@ export const GROK_PREEMPT_MS = 2 * 60 * 1000;
52
52
  /** Hosts an authorization server endpoint may live on. */
53
53
  const TRUSTED_AUTH_HOSTS = new Set(['auth.x.ai', 'accounts.x.ai']);
54
54
  /** The rung id the composer's quick-mode button selects. */
55
- const FAST_RUNG = 'lightspeed';
55
+ const FAST_RUNG = 'fast';
56
56
  /** Display metadata for the reasoning ladder xAI documents. */
57
57
  const EFFORT_LABELS = {
58
58
  none: { name: 'Off', description: 'Answer without spending thinking tokens' },
59
59
  low: { name: 'Low', description: 'Short chain of thought' },
60
60
  medium: { name: 'Medium', description: 'Balanced thinking budget' },
61
61
  high: { name: 'High', description: 'Deep chain of thought' },
62
- // Not a thinking budget: this rung is the model's fast/lightspeed variant, which the
63
- // composer's quick-mode button selects. It rides the effort channel so the row keeps
64
- // its own id — a swapped model id would leave the picker with no way back. xAI's own
65
- // word for it is "lightspeed", which is what the rung is named here.
66
- [FAST_RUNG]: { name: 'Lightspeed', description: 'xAI lightspeed variant of this model' },
62
+ // Not a thinking budget: this rung is the model's fast variant, which the
63
+ // composer's quick-mode button selects. It rides the effort channel so the row
64
+ // keeps its own id — a swapped model id would leave the picker with no way back.
65
+ // xAI's own word for it is "fast" (grok-4.7-build-fast), which names the rung.
66
+ [FAST_RUNG]: { name: 'Fast', description: 'xAI fast variant of this model' },
67
67
  };
68
68
  /**
69
69
  * Curated catalog entries.
@@ -127,18 +127,21 @@ export function parseGrokSession(value) {
127
127
  }
128
128
  /** One `reasoning.efforts` entry. */
129
129
  function effortInfo(effort) {
130
- const label = EFFORT_LABELS[effort] ?? { name: effort, description: '' };
130
+ const known = EFFORT_LABELS[effort];
131
+ const label = known ?? { name: effort[0].toUpperCase() + effort.slice(1), description: `${effort} variant of this model` };
131
132
  return { id: ReasoningEffortId(effort), name: label.name, description: label.description };
132
133
  }
133
134
  /**
134
135
  * The reasoning block for a catalog entry, or undefined when it supports none.
135
136
  *
136
137
  * @param entry - the catalog row.
137
- * @param fast - add the `fast` rung when this row has a fast variant.
138
+ * @param fastWireId - the row's fast variant wire id, when it has one; the rung is the
139
+ * variant's own fast word (`grok-4.7-build-fast` → `fast`).
138
140
  */
139
- function reasoningInfo(entry, fast = false) {
141
+ function reasoningInfo(entry, fastWireId) {
140
142
  const efforts = entry.reasoningEfforts;
141
- const ids = fast ? [...efforts ?? [], FAST_RUNG] : efforts;
143
+ const rung = fastWireId === undefined ? undefined : fastWordOf(fastWireId);
144
+ const ids = rung === undefined ? efforts : [...efforts ?? [], rung];
142
145
  if (ids === undefined || ids.length === 0)
143
146
  return undefined;
144
147
  return {
@@ -289,11 +292,15 @@ export function isImageModel(id) {
289
292
  * (`kimi-for-coding-highspeed`). A word has to stand on its own, so `highspeed` never
290
293
  * matches inside another word.
291
294
  */
292
- const FAST_VARIANT = /(?:^|[-_@])(?:fast|lightspeed|turbo|quick|highspeed)(?=[-_@]|$)/i;
295
+ const FAST_VARIANT = /(?:^|[-_@])(fast|lightspeed|turbo|quick|highspeed)(?=[-_@]|$)/i;
293
296
  /** True when a model id names a fast/lightspeed variant of a base model. */
294
297
  export function isFastVariant(id) {
295
298
  return FAST_VARIANT.test(id);
296
299
  }
300
+ /** The fast word a variant's own id names (`grok-4.7-build-fast` → `fast`), lowercased. */
301
+ export function fastWordOf(id) {
302
+ return FAST_VARIANT.exec(id)?.[1]?.toLowerCase() ?? 'fast';
303
+ }
297
304
  /**
298
305
  * Every prefix of one model id, longest first, down to its first token.
299
306
  *
@@ -503,9 +510,10 @@ export class GrokAdapter extends LlmAdapter {
503
510
  ...reasoning === undefined ? {} : { reasoning },
504
511
  };
505
512
  }
506
- // A row with a fast variant offers the `fast` rung; the picker shows the row under
507
- // its own name and the mode as the effort, which is what the trigger already reads.
508
- const reasoning = reasoningInfo(entry, this.fastVariants.variantOf(entry.id) !== undefined);
513
+ // A row with a fast variant offers a rung named for the variant's own word; the
514
+ // picker shows the row under its own name and the mode as the effort.
515
+ const fastWireId = this.fastVariants.variantOf(entry.id);
516
+ const reasoning = reasoningInfo(entry, fastWireId);
509
517
  return {
510
518
  provider: GROK_PROVIDER_ID,
511
519
  id: model,
@@ -520,13 +528,15 @@ export class GrokAdapter extends LlmAdapter {
520
528
  stream(options) {
521
529
  // Resolve @Nk context suffixes to the largest variant the endpoint offers.
522
530
  const base = this.resolveModelId(options.model);
523
- // Fast mode rides the reasoning channel: `fast` is a rung of the row, not a model id
524
- // of its own, so the row the picker selected stays dispatchable and the user can
525
- // always select the standard model again. Only the wire id changes.
526
- const fast = String(options.reasoningEffort ?? '') === FAST_RUNG;
527
- const model = fast ? this.fastVariants.variantOf(base) ?? base : base;
531
+ // Fast mode rides the reasoning channel: the rung is the variant's own fast word, not
532
+ // a model id of its own, so the row the picker selected stays dispatchable and the user
533
+ // can always select the standard model again. Only the wire id changes.
534
+ const effort = String(options.reasoningEffort ?? '');
535
+ const fastWireId = this.fastVariants.variantOf(base);
536
+ const fast = fastWireId !== undefined && effort === fastWordOf(fastWireId);
537
+ const model = fast ? fastWireId : base;
528
538
  const call = { ...options, model };
529
- // xAI has no `fast` effort; the variant already carries the behaviour.
539
+ // xAI has no fast-variant effort; the variant already carries the behaviour.
530
540
  if (fast)
531
541
  delete call.reasoningEffort;
532
542
  return streamProviderResponses(call, {
@@ -1003,7 +1013,7 @@ export function createGrokProvider(options) {
1003
1013
  const pair = adapter.fastPairOf(id);
1004
1014
  const image = adapter.imageModelId();
1005
1015
  return {
1006
- ...pair === undefined ? {} : { fast: pair.wireId, fastBase: pair.baseId, fastRung: 'lightspeed' },
1016
+ ...pair === undefined ? {} : { fast: pair.wireId, fastBase: pair.baseId, fastRung: fastWordOf(pair.wireId) },
1007
1017
  ...image === undefined ? {} : { image },
1008
1018
  };
1009
1019
  },
@@ -13,6 +13,7 @@ import { loadOrCreateDeviceId } from '../auth/device-id.js';
13
13
  import { kimiIdentityHeaders } from '../auth/headers.js';
14
14
  import { KimiAdapter, parseKimiSession } from './kimi.js';
15
15
  import { readKimiUsage } from './usage.js';
16
+ import { fastWordOf } from './grok.js';
16
17
  import { ProviderController } from '../auth/provider-controller.js';
17
18
  import { proxiedFetch } from '../http.js';
18
19
  /**
@@ -154,7 +155,7 @@ export function createKimiProvider(options) {
154
155
  const pair = adapter.fastPairOf(id);
155
156
  const image = adapter.imageModelId();
156
157
  return {
157
- ...pair === undefined ? {} : { fast: pair.wireId, fastBase: pair.baseId, fastRung: 'highspeed' },
158
+ ...pair === undefined ? {} : { fast: pair.wireId, fastBase: pair.baseId, fastRung: fastWordOf(pair.wireId) },
158
159
  ...image === undefined ? {} : { image },
159
160
  };
160
161
  },
@@ -15,7 +15,7 @@ import { streamProviderResponses, accountPoolHooks } from './responses-stream.js
15
15
  import { fallbackEntry, findEntry, resolveCatalog } from './catalog.js';
16
16
  import { streamResponses, toResponsesInput, toResponsesTools } from '../translate/responses.js';
17
17
  import { prepareMoonshotTool } from '../translate/moonshot-schema.js';
18
- import { isImageModel, isFastVariant, baseModelOf, FastVariantIndex } from './grok.js';
18
+ import { isImageModel, isFastVariant, baseModelOf, fastWordOf, FastVariantIndex } from './grok.js';
19
19
  /** Model ids that are clearly not chat models. */
20
20
  const NON_CHAT_ID = /(?:^|[-_/])(?:image|video|imagine|embed|embedding|whisper|tts|vision-only)/i;
21
21
  /**
@@ -127,11 +127,13 @@ function effortInfo(effort) {
127
127
  * The reasoning block for a catalog entry, or undefined when it supports none.
128
128
  *
129
129
  * @param entry - the catalog row.
130
- * @param fast - add the `fast` rung when this row has a fast variant.
130
+ * @param fastWireId - the row's fast variant wire id, when it has one; the rung is the
131
+ * variant's own fast word (`kimi-for-coding-highspeed` → `highspeed`).
131
132
  */
132
- function reasoningInfo(entry, fast = false) {
133
+ function reasoningInfo(entry, fastWireId) {
133
134
  const efforts = entry.reasoningEfforts;
134
- const ids = fast ? [...efforts ?? [], FAST_RUNG] : efforts;
135
+ const rung = fastWireId === undefined ? undefined : fastWordOf(fastWireId);
136
+ const ids = rung === undefined ? efforts : [...efforts ?? [], rung];
135
137
  if (ids === undefined || ids.length === 0)
136
138
  return undefined;
137
139
  const defaultEffort = entry.defaultEffort ?? ids[ids.length - 1];
@@ -271,9 +273,10 @@ export class KimiAdapter extends LlmAdapter {
271
273
  ...reasoning === undefined ? {} : { reasoning },
272
274
  };
273
275
  }
274
- // A row with a fast variant offers the `fast` rung; the picker shows the row under
275
- // its own name and the mode as the effort, which is what the trigger already reads.
276
- const reasoning = reasoningInfo(entry, this.fastVariants.variantOf(entry.id) !== undefined);
276
+ // A row with a fast variant offers a rung named for the variant's own word; the
277
+ // picker shows the row under its own name and the mode as the effort.
278
+ const fastWireId = this.fastVariants.variantOf(entry.id);
279
+ const reasoning = reasoningInfo(entry, fastWireId);
277
280
  // An alias is just another spelling of the row, so it resolves onto the row's own id:
278
281
  // that is what keeps a selection dispatchable and the picker able to show it.
279
282
  return {
@@ -288,13 +291,15 @@ export class KimiAdapter extends LlmAdapter {
288
291
  };
289
292
  }
290
293
  stream(options) {
291
- // Fast mode rides the reasoning channel: `fast` is a rung of the row, not a model id
292
- // of its own, so the row the picker selected stays dispatchable and the user can
293
- // always select the standard model again. Only the wire id changes.
294
- const fast = String(options.reasoningEffort ?? '') === FAST_RUNG;
295
- const model = fast ? this.fastVariants.variantOf(options.model) ?? options.model : options.model;
294
+ // Fast mode rides the reasoning channel: the rung is the variant's own fast word, not
295
+ // a model id of its own, so the row the picker selected stays dispatchable and the user
296
+ // can always select the standard model again. Only the wire id changes.
297
+ const fastWireId = this.fastVariants.variantOf(options.model);
298
+ const effort = String(options.reasoningEffort ?? '');
299
+ const fast = fastWireId !== undefined && effort === fastWordOf(fastWireId);
300
+ const model = fast ? fastWireId : options.model;
296
301
  const call = { ...options, model };
297
- // Kimi has no `fast` effort; the variant already carries the behaviour.
302
+ // Kimi has no fast-variant effort; the variant already carries the behaviour.
298
303
  if (fast)
299
304
  delete call.reasoningEffort;
300
305
  return streamProviderResponses(call, {
@@ -82,6 +82,16 @@ export declare class MimoAdapter extends LlmAdapter {
82
82
  /** The ids the route itself serves, without hand-added extras. */
83
83
  ownCatalogEntries(): readonly MimoModelEntry[];
84
84
  stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
85
+ /** The app's names for its models, as the Token Plan's endpoint names them. */
86
+ private static readonly TP_MODELS;
87
+ /**
88
+ * Re-ask a membership_required chat at the Token Plan's endpoint.
89
+ *
90
+ * Reads the account's tp- API key from the open platform, then posts the chat to the
91
+ * plan's OpenAI base with that key. Undefined when the account has no live plan or no
92
+ * readable key.
93
+ */
94
+ private tokenPlanRequest;
85
95
  /**
86
96
  * The session cookies as one Cookie header, in a steady order.
87
97
  *
@@ -11,7 +11,7 @@
11
11
  import { LlmAdapter, errorChain, } from '@deepseek-ai/dsh-llm';
12
12
  import { httpLlmError, isMissingOrInvalidCredential, mapFetchFailure, idleWatchdog } from './common.js';
13
13
  import { proxiedFetch } from '../http.js';
14
- import { refreshMimoSession } from '../auth/mimo-flow.js';
14
+ import { readMimoPlanKey, refreshMimoSession } from '../auth/mimo-flow.js';
15
15
  /** The `mimo` provider route key. */
16
16
  export const MIMO_PROVIDER_ID = 'mimo';
17
17
  /** Human name for the route and its cards. */
@@ -145,6 +145,28 @@ export class MimoAdapter extends LlmAdapter {
145
145
  body: JSON.stringify(body),
146
146
  signal: watchdog.signal,
147
147
  });
148
+ // A 403 with code membership_required means the account has no app plan but a
149
+ // Token Plan at the open platform: re-ask at its endpoint with its tp- key.
150
+ if (response.status === 403) {
151
+ const text = await response.text();
152
+ let code;
153
+ try {
154
+ code = JSON.parse(text).error !== undefined
155
+ ? JSON.parse(text).error.code
156
+ : JSON.parse(text).code;
157
+ }
158
+ catch {
159
+ code = undefined;
160
+ }
161
+ if (code === 'membership_required') {
162
+ const plan = await this.tokenPlanRequest(session, body, watchdog);
163
+ if (plan !== undefined) {
164
+ yield* this.readStream(plan, watchdog);
165
+ return;
166
+ }
167
+ }
168
+ throw new Error(`Xiaomi MiMo: ${text.slice(0, 200)}`);
169
+ }
148
170
  if (!response.ok || response.body === null) {
149
171
  throw await httpLlmError(response, 'Xiaomi MiMo');
150
172
  }
@@ -157,6 +179,48 @@ export class MimoAdapter extends LlmAdapter {
157
179
  watchdog.stop();
158
180
  }
159
181
  }
182
+ /** The app's names for its models, as the Token Plan's endpoint names them. */
183
+ static TP_MODELS = {
184
+ 'mimo-pro': 'mimo-v2.6-pro',
185
+ 'mimo-flash': 'mimo-v2.6-flash',
186
+ };
187
+ /**
188
+ * Re-ask a membership_required chat at the Token Plan's endpoint.
189
+ *
190
+ * Reads the account's tp- API key from the open platform, then posts the chat to the
191
+ * plan's OpenAI base with that key. Undefined when the account has no live plan or no
192
+ * readable key.
193
+ */
194
+ async tokenPlanRequest(session, body, watchdog) {
195
+ let creds;
196
+ try {
197
+ creds = JSON.parse(session.mimoCreds);
198
+ }
199
+ catch {
200
+ return undefined;
201
+ }
202
+ try {
203
+ const plan = await readMimoPlanKey(creds, this.fetchFn);
204
+ if (plan === null)
205
+ return undefined;
206
+ const model = MimoAdapter.TP_MODELS[String(body.model)] ?? String(body.model);
207
+ const path = new URL(session.apiBase.replace(/\/+$/, '') + '/route/chat/completions').pathname;
208
+ const rest = path.replace(/^.*?\/route(?=\/|$)/, '');
209
+ return await this.fetchFn(`${plan.base}${rest}`, {
210
+ method: 'POST',
211
+ headers: {
212
+ 'Content-Type': 'application/json',
213
+ 'Accept': 'text/event-stream',
214
+ 'Authorization': `Bearer ${plan.key}`,
215
+ },
216
+ body: JSON.stringify({ ...body, model }),
217
+ signal: watchdog.signal,
218
+ });
219
+ }
220
+ catch {
221
+ return undefined;
222
+ }
223
+ }
160
224
  /**
161
225
  * The session cookies as one Cookie header, in a steady order.
162
226
  *
package/lib/settings.d.ts CHANGED
@@ -33,6 +33,8 @@ export interface AutoPreferences {
33
33
  order: readonly string[];
34
34
  /** Keys `auto` must not use, whatever their own route offers. */
35
35
  off: readonly string[];
36
+ /** Whether the `auto` provider is offered at all. Defaults to true. */
37
+ enabled: boolean;
36
38
  }
37
39
  /**
38
40
  * Owns the model preference document.
@@ -89,6 +91,12 @@ export declare class ModelPreferences {
89
91
  removeExtra(provider: string, id: string): Promise<boolean>;
90
92
  /** The `auto` provider's arrangement. */
91
93
  autoPreferences(): Promise<AutoPreferences>;
94
+ /**
95
+ * Record whether the `auto` provider is offered at all.
96
+ * @param enabled - whether `auto` appears in the picker.
97
+ * @returns whether the stored state actually changed.
98
+ */
99
+ setAutoOn(enabled: boolean): Promise<boolean>;
92
100
  /**
93
101
  * Replace the `auto` provider's order.
94
102
  * @param keys - `provider/model` keys, most preferred first.
package/lib/settings.js CHANGED
@@ -26,10 +26,11 @@ export const LEGACY_FLAT_PROVIDER = 'kimi';
26
26
  /** Read the `auto` section, keeping only usable keys. */
27
27
  function readAuto(document) {
28
28
  if (!isRecord(document.auto))
29
- return { order: [], off: [] };
29
+ return { order: [], off: [], enabled: true };
30
30
  return {
31
31
  order: readIds(document.auto.order),
32
32
  off: readIds(document.auto.off),
33
+ enabled: document.auto.enabled !== false,
33
34
  };
34
35
  }
35
36
  /** True for a plain object usable as a JSON record. */
@@ -102,7 +103,7 @@ export class ModelPreferences {
102
103
  file;
103
104
  hidden = {};
104
105
  order = {};
105
- auto = { order: [], off: [] };
106
+ auto = { order: [], off: [], enabled: true };
106
107
  extra = {};
107
108
  loaded = false;
108
109
  /** The in-flight first read, so concurrent callers share one. */
@@ -137,7 +138,7 @@ export class ModelPreferences {
137
138
  catch {
138
139
  this.hidden = {};
139
140
  this.order = {};
140
- this.auto = { order: [], off: [] };
141
+ this.auto = { order: [], off: [], enabled: true };
141
142
  this.extra = {};
142
143
  this.loaded = true;
143
144
  return;
@@ -153,7 +154,7 @@ export class ModelPreferences {
153
154
  catch {
154
155
  this.hidden = {};
155
156
  this.order = {};
156
- this.auto = { order: [], off: [] };
157
+ this.auto = { order: [], off: [], enabled: true };
157
158
  this.extra = {};
158
159
  }
159
160
  this.loaded = true;
@@ -220,7 +221,20 @@ export class ModelPreferences {
220
221
  /** The `auto` provider's arrangement. */
221
222
  async autoPreferences() {
222
223
  await this.ensureLoaded();
223
- return { order: [...this.auto.order], off: [...this.auto.off] };
224
+ return { order: [...this.auto.order], off: [...this.auto.off], enabled: this.auto.enabled };
225
+ }
226
+ /**
227
+ * Record whether the `auto` provider is offered at all.
228
+ * @param enabled - whether `auto` appears in the picker.
229
+ * @returns whether the stored state actually changed.
230
+ */
231
+ async setAutoOn(enabled) {
232
+ await this.ensureLoaded();
233
+ if (this.auto.enabled === enabled)
234
+ return false;
235
+ this.auto = { ...this.auto, enabled };
236
+ await this.persist();
237
+ return true;
224
238
  }
225
239
  /**
226
240
  * Replace the `auto` provider's order.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-subscription-oauth",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
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",