@mars-sea/dsh-commandcode-provider 0.10.0-alpha.1 → 0.10.0-alpha.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/lib/index.d.ts CHANGED
@@ -24,7 +24,7 @@ declare const KNOWN_EFFORTS: Readonly<Record<string, readonly string[]>>;
24
24
  */
25
25
  declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
26
26
  /**
27
- * Models the official CLI's model table (command-code@1.37.0) marks
27
+ * Models the official CLI's model table (command-code@1.38.2) marks
28
28
  * `reasoning:!0` but defines no selectable `reasoning_effort` levels — they
29
29
  * think automatically, with Command Code driving the depth. This is the
30
30
  * authoritative "thinks, effort not adjustable" set: `KNOWN_EFFORTS` (which
@@ -32,13 +32,15 @@ declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
32
32
  * effort levels, and this snapshot is not surfaced in the picker's compact
33
33
  * description — it exists for programmatic consumers.
34
34
  *
35
- * Source: the command-code@1.37.0 bundled model table (dist/cli.mjs),
35
+ * Source: the command-code@1.38.2 bundled model table (dist/cli.mjs),
36
36
  * cross-checked with https://commandcode.ai/docs/reference/cli/models.
37
37
  * (`stealth/ox-alpha` left this set in command-code@1.32.1, which gave it
38
38
  * selectable `['low', 'high', 'max']` efforts; the preview then ended in
39
39
  * 1.34.0, removing the model from the catalog entirely. `tencent/hy4-preview`
40
40
  * joined this set in command-code@1.37.0 — reasoning:!0, no efforts, 1M
41
- * context, routed through OpenRouter.)
41
+ * context, routed through OpenRouter — then gained selectable
42
+ * `['low', 'medium', 'high']` efforts in command-code@1.38.0 and moved to
43
+ * `KNOWN_EFFORTS`.)
42
44
  * Keep in sync via the dsh-commandcode-upstream skill.
43
45
  */
44
46
  declare const KNOWN_THINKING_MODELS: ReadonlySet<string>;
@@ -189,7 +191,7 @@ declare function peakPricingState(modelId: string, now?: number): 'peak' | 'off-
189
191
  * for models without time-of-day pricing.
190
192
  */
191
193
  declare function peakPricingLabel(modelId: string, now?: number): string | undefined;
192
- declare const COMMAND_CODE_CLI_VERSION = "1.37.0";
194
+ declare const COMMAND_CODE_CLI_VERSION = "1.38.2";
193
195
  declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
194
196
  declare const DEFAULT_GENERATE_MAX_TOKENS = 64000;
195
197
  declare const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
@@ -263,8 +265,12 @@ type ResolveAttachments = () => AttachmentStore | undefined;
263
265
  interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {
264
266
  /** Resolve the current connection facts (fresh per request, settings-aware). */
265
267
  options: () => C;
266
- /** Resolve a usable API key for the given connection facts, or throw `MISSING_CREDENTIAL`. */
267
- resolveApiKey: (connection: C) => Promise<string>;
268
+ /**
269
+ * Resolve a usable API key for the given connection facts and the request's
270
+ * model id, or throw `MISSING_CREDENTIAL`. The model is optional: hosts
271
+ * without model-aware routing ignore it.
272
+ */
273
+ resolveApiKey: (connection: C, model?: string) => Promise<string>;
268
274
  /**
269
275
  * Multi-account rotation hook: the request sent with `rejectedKey` was
270
276
  * refused with 429 (`rate-limit`) or 401 (`invalid-credential`) before
@@ -273,7 +279,7 @@ interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = Comman
273
279
  * Only pre-stream rejections rotate — a mid-stream failure never replays a
274
280
  * partially consumed generation against another account.
275
281
  */
276
- rotateApiKey?: (rejectedKey: string, rejection: 'rate-limit' | 'invalid-credential', connection: C) => Promise<string | undefined>;
282
+ rotateApiKey?: (rejectedKey: string, rejection: 'rate-limit' | 'invalid-credential', connection: C, model?: string) => Promise<string | undefined>;
277
283
  /** HTTP transport override (tests); defaults to the global `fetch`. */
278
284
  fetchImpl?: typeof fetch;
279
285
  /** Resolve the optional durable attachment service for image input (tests); defaults to none. */
@@ -502,6 +508,27 @@ interface CommandCodeAccountPoolDeps {
502
508
  * account falls back to the first usable slot.
503
509
  */
504
510
  preferredId?(): string | undefined;
511
+ /**
512
+ * Model → account routing rules, re-read per resolution so settings changes
513
+ * apply live. Each rule lists catalog model ids (see
514
+ * {@link CommandCodeModelAccountRule}) to an account slot id. When the
515
+ * request's model matches a rule and that account is usable, it serves
516
+ * before the preferred/rotation selection; an unusable routed account falls
517
+ * back to the normal selection (the router is a hint, never a hard gate).
518
+ */
519
+ modelAccountRules?(): readonly CommandCodeModelAccountRule[];
520
+ }
521
+ /**
522
+ * One "route these models to that account" rule. `models` lists catalog ids
523
+ * (`deepseek/deepseek-v4-pro`, …); `account` is a slot id (`default` or an
524
+ * extra account's credential reference). A request whose model id is in the
525
+ * list routes to that account. The first matching rule in list order wins.
526
+ */
527
+ interface CommandCodeModelAccountRule {
528
+ /** Catalog model ids to match against the request's model. */
529
+ models: string[];
530
+ /** Account slot id to prefer for matching models. */
531
+ account: string;
505
532
  }
506
533
  /**
507
534
  * Whether an account with this rotation state can serve a request right now.
@@ -517,6 +544,18 @@ declare function accountUsable(state: CommandCodeAccountState | undefined): bool
517
544
  * entry (the usage view's active badge) so both always agree.
518
545
  */
519
546
  declare function selectActiveAccount(accounts: readonly ResolvedAccount[], preferredId: string | undefined): ResolvedAccount | undefined;
547
+ /**
548
+ * The first routing rule whose model list contains the request's model id.
549
+ * Undefined when no rule matches.
550
+ */
551
+ declare function matchModelRule(model: string, rules: readonly CommandCodeModelAccountRule[] | undefined): CommandCodeModelAccountRule | undefined;
552
+ /**
553
+ * The routed account for a request's model: the first usable account whose
554
+ * slot id matches the first matching rule's target. Undefined when no rule
555
+ * matches or the routed account is not usable (the caller then falls back to
556
+ * the normal preferred/rotation selection).
557
+ */
558
+ declare function selectAccountForModel(accounts: readonly ResolvedAccount[], model: string, rules: readonly CommandCodeModelAccountRule[] | undefined): ResolvedAccount | undefined;
520
559
  /**
521
560
  * The account pool. Rotation state is keyed by API key (never logged), so two
522
561
  * slots resolving to the same credential share one mark, and a key changed in
@@ -541,12 +580,17 @@ declare class CommandCodeAccountPool {
541
580
  */
542
581
  describeAccounts(): Promise<ResolvedAccount[]>;
543
582
  /**
544
- * Hand out the first usable account's key (the manually preferred account
545
- * when usable, else rotation order). Returns `undefined` when no account
546
- * resolves any key at all (the caller then reports the missing credential).
547
- * Throws `RATE_LIMIT` naming the earliest window reset — or
583
+ * Hand out the key for a request: the model-routed account when the
584
+ * request's model matches a rule (and that account is usable), else the
585
+ * manually preferred account when usable, else the first usable account in
586
+ * rotation order. Returns `undefined` when no account resolves any key at
587
+ * all (the caller then reports the missing credential). Throws
588
+ * `RATE_LIMIT` — naming the earliest window reset — or
548
589
  * `INVALID_CREDENTIAL` when accounts exist but none can serve.
549
590
  *
591
+ * `options.model` is the request's model id; routing rules re-read per
592
+ * resolution, so a settings change applies live.
593
+ *
550
594
  * `options.exclude` skips one key during the probe-revival pass: the
551
595
  * rotation hook excludes the just-rejected key so a probe that clears its
552
596
  * window cannot re-offer the same key within the same request (the adapter
@@ -554,6 +598,7 @@ declare class CommandCodeAccountPool {
554
598
  */
555
599
  resolveKey(options?: {
556
600
  exclude?: string;
601
+ model?: string;
557
602
  }): Promise<{
558
603
  key: string;
559
604
  slot: CommandCodeAccountSlot;
@@ -601,6 +646,17 @@ declare const USAGE_REPORT_ENDPOINT = "commandcode/report";
601
646
  * deliberately minimal so one `parse` function satisfies it.
602
647
  */
603
648
  declare const usageReportSchema: TypertSchema<CommandCodeAccountsReport>;
649
+ /** One catalog entry the settings page's routing-rule editor offers. */
650
+ interface CommandCodeCatalogModel {
651
+ /** Catalog model id (e.g. `deepseek/deepseek-v4-pro`). */
652
+ id: string;
653
+ /** Display name from the catalog. */
654
+ name: string;
655
+ }
656
+ /** The model-catalog Remote result: the full catalog, sorted for picking. */
657
+ interface CommandCodeCatalog {
658
+ models: CommandCodeCatalogModel[];
659
+ }
604
660
  //#endregion
605
661
  //#region src/command-locales.d.ts
606
662
  /**
@@ -718,6 +774,12 @@ interface CommandCodeUsageDeps<C extends CommandCodeConnectionOptions = CommandC
718
774
  * entry around `adapter.getUsage()`.
719
775
  */
720
776
  reports?: () => Promise<CommandCodeAccountsReport>;
777
+ /**
778
+ * Model-catalog source for the routing-rule editor (wired by the plugin
779
+ * entry). Absent, the `models` endpoint answers an empty list — the page's
780
+ * rule editor degrades to the empty state.
781
+ */
782
+ listModels?: () => Promise<CommandCodeCatalog>;
721
783
  /**
722
784
  * The browser-login flow (wired by the plugin entry). Absent means the
723
785
  * login endpoints answer `idle` / reject with a plain message — the page's
@@ -744,6 +806,13 @@ declare class CommandCodeUsageService<C extends CommandCodeConnectionOptions = C
744
806
  * the failure branch the page renders as a hint.
745
807
  */
746
808
  report(): Promise<CommandCodeAccountsReport>;
809
+ /**
810
+ * The model catalog for the settings page's routing-rule editor. The
811
+ * browser never calls the Command Code API directly — the Host serves the
812
+ * catalog (already fetched/cached by the adapter) so rules can be picked
813
+ * from the live model list instead of typed by hand.
814
+ */
815
+ models(): Promise<CommandCodeCatalog>;
747
816
  /**
748
817
  * Start (or rejoin) a browser-login attempt and return its fresh status —
749
818
  * `waiting` carrying the Studio URL. Rejects when the flow cannot start
@@ -933,6 +1002,16 @@ interface Config {
933
1002
  * rotation still applies). Unset means "first usable account".
934
1003
  */
935
1004
  activeAccount?: string;
1005
+ /**
1006
+ * Model → account routing rules. Each rule lists catalog model ids to an
1007
+ * account slot id (`default`, or an extra account's credential reference).
1008
+ * When a request's model is in a rule's list and the routed account is
1009
+ * usable, that account serves — before the manual {@link activeAccount} and
1010
+ * the passive rotation order. A routed account that is exhausted or invalid
1011
+ * falls back to the normal selection, so the router is a hint, never a hard
1012
+ * gate. The first matching rule wins.
1013
+ */
1014
+ modelAccountRules?: CommandCodeModelAccountRule[];
936
1015
  /**
937
1016
  * Language override for the `/commandcode` Host-side command's user-facing
938
1017
  * copy. Host commands cannot read the client's `ctx.locale`, so this is
@@ -960,5 +1039,5 @@ interface ResolvedCommandCodeOptions extends CommandCodeConnectionOptions {
960
1039
  declare function resolveAdapterOptions(config: Config): ResolvedCommandCodeOptions;
961
1040
  declare function apply(ctx: Context, config: Config): void;
962
1041
  //#endregion
963
- export { type ApiKeyValidation, BILLING_ACCESS_TTL_MS, COMMAND_CODE_CLI_VERSION, type CommandCodeAccountConfig, CommandCodeAccountPool, type CommandCodeAccountSlot, type CommandCodeAccountState, type CommandCodeAccountUsage, type CommandCodeAccountsReport, CommandCodeAdapter, type CommandCodeAdapterDeps, type CommandCodeBillingAccess, type CommandCodeCommandDeps, type CommandCodeConnectionOptions, type CommandCodeLoginCredentials, type CommandCodeLoginFailureReason, CommandCodeLoginFlow, type CommandCodeLoginFlowDeps, type CommandCodeLoginStatus, type CommandCodeUsageDeps, type CommandCodeUsageReport, CommandCodeUsageService, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_SUBSCRIPTION_PLANS, KNOWN_THINKING_MODELS, LOGIN_ALLOWED_ORIGINS, LOGIN_BEGIN_ENDPOINT, LOGIN_BODY_LIMIT_BYTES, LOGIN_CANCEL_ENDPOINT, LOGIN_MAX_PORT_ATTEMPTS, LOGIN_START_PORT, LOGIN_STATUS_ENDPOINT, LOGIN_TIMEOUT_MS, type LoginFlowFacade, PLAN_LABELS, PLAN_ORDER, PROVIDER, type ResolveAttachments, ResolvedCommandCodeOptions, USAGE_REPORT_ENDPOINT, accountUsable, apply, applyCommands, applyUsageRemote, buildCommandAuthUrl, capabilityDescription, commandDefinition, compareByPlan, dealLabel, formatContext, inject, loginStatusSchema, modelVisibleInPlan, name, parseLoginStatus, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey, selectActiveAccount, studioBaseForApiBase, subscriptionPlanInfo, usageReportSchema, validateCommandApiKey };
1042
+ export { type ApiKeyValidation, BILLING_ACCESS_TTL_MS, COMMAND_CODE_CLI_VERSION, type CommandCodeAccountConfig, CommandCodeAccountPool, type CommandCodeAccountSlot, type CommandCodeAccountState, type CommandCodeAccountUsage, type CommandCodeAccountsReport, CommandCodeAdapter, type CommandCodeAdapterDeps, type CommandCodeBillingAccess, type CommandCodeCommandDeps, type CommandCodeConnectionOptions, type CommandCodeLoginCredentials, type CommandCodeLoginFailureReason, CommandCodeLoginFlow, type CommandCodeLoginFlowDeps, type CommandCodeLoginStatus, type CommandCodeModelAccountRule, type CommandCodeUsageDeps, type CommandCodeUsageReport, CommandCodeUsageService, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_SUBSCRIPTION_PLANS, KNOWN_THINKING_MODELS, LOGIN_ALLOWED_ORIGINS, LOGIN_BEGIN_ENDPOINT, LOGIN_BODY_LIMIT_BYTES, LOGIN_CANCEL_ENDPOINT, LOGIN_MAX_PORT_ATTEMPTS, LOGIN_START_PORT, LOGIN_STATUS_ENDPOINT, LOGIN_TIMEOUT_MS, type LoginFlowFacade, PLAN_LABELS, PLAN_ORDER, PROVIDER, type ResolveAttachments, ResolvedCommandCodeOptions, USAGE_REPORT_ENDPOINT, accountUsable, apply, applyCommands, applyUsageRemote, buildCommandAuthUrl, capabilityDescription, commandDefinition, compareByPlan, dealLabel, formatContext, inject, loginStatusSchema, matchModelRule, modelVisibleInPlan, name, parseLoginStatus, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey, selectAccountForModel, selectActiveAccount, studioBaseForApiBase, subscriptionPlanInfo, usageReportSchema, validateCommandApiKey };
964
1043
  //# sourceMappingURL=index.d.ts.map
package/lib/index.js CHANGED
@@ -21,9 +21,11 @@ import { createServer as createServer$1 } from "node:net";
21
21
  * This module owns that rotation:
22
22
  *
23
23
  * - {@link CommandCodeAccountPool.resolveKey} hands out the first account
24
- * whose key is not currently marked exhausted, resolving each slot's key
25
- * lazily (literal config key credential seam launch environment → the
26
- * official CLI auth file for the default slot only).
24
+ * whose key is not currently marked exhausted — or, when the request's
25
+ * model matches a {@link CommandCodeModelAccountRule} and that account is
26
+ * usable, the routed account resolving each slot's key lazily (literal
27
+ * config key → credential seam → launch environment → the official CLI
28
+ * auth file for the default slot only).
27
29
  * - {@link CommandCodeAccountPool.markRejected} records a 429 (rate limit,
28
30
  * window unknown) or 401 (invalid key, disabled until the config changes)
29
31
  * against the exact API key, so several slots sharing one key share one
@@ -79,6 +81,25 @@ function selectActiveAccount(accounts, preferredId) {
79
81
  return usable[0];
80
82
  }
81
83
  /**
84
+ * The first routing rule whose model list contains the request's model id.
85
+ * Undefined when no rule matches.
86
+ */
87
+ function matchModelRule(model, rules) {
88
+ if (model === "" || rules === void 0 || rules.length === 0) return void 0;
89
+ for (const rule of rules) if (rule.models.includes(model)) return rule;
90
+ }
91
+ /**
92
+ * The routed account for a request's model: the first usable account whose
93
+ * slot id matches the first matching rule's target. Undefined when no rule
94
+ * matches or the routed account is not usable (the caller then falls back to
95
+ * the normal preferred/rotation selection).
96
+ */
97
+ function selectAccountForModel(accounts, model, rules) {
98
+ const rule = matchModelRule(model, rules);
99
+ if (rule === void 0) return void 0;
100
+ return accounts.find((account) => account.slot.id === rule.account && accountUsable(account.state));
101
+ }
102
+ /**
82
103
  * The account pool. Rotation state is keyed by API key (never logged), so two
83
104
  * slots resolving to the same credential share one mark, and a key changed in
84
105
  * the credentials service starts with a clean slate.
@@ -130,12 +151,17 @@ var CommandCodeAccountPool = class {
130
151
  return out;
131
152
  }
132
153
  /**
133
- * Hand out the first usable account's key (the manually preferred account
134
- * when usable, else rotation order). Returns `undefined` when no account
135
- * resolves any key at all (the caller then reports the missing credential).
136
- * Throws `RATE_LIMIT` naming the earliest window reset — or
154
+ * Hand out the key for a request: the model-routed account when the
155
+ * request's model matches a rule (and that account is usable), else the
156
+ * manually preferred account when usable, else the first usable account in
157
+ * rotation order. Returns `undefined` when no account resolves any key at
158
+ * all (the caller then reports the missing credential). Throws
159
+ * `RATE_LIMIT` — naming the earliest window reset — or
137
160
  * `INVALID_CREDENTIAL` when accounts exist but none can serve.
138
161
  *
162
+ * `options.model` is the request's model id; routing rules re-read per
163
+ * resolution, so a settings change applies live.
164
+ *
139
165
  * `options.exclude` skips one key during the probe-revival pass: the
140
166
  * rotation hook excludes the just-rejected key so a probe that clears its
141
167
  * window cannot re-offer the same key within the same request (the adapter
@@ -144,6 +170,8 @@ var CommandCodeAccountPool = class {
144
170
  async resolveKey(options) {
145
171
  const accounts = await this.resolvedAccounts();
146
172
  if (accounts.length === 0) return;
173
+ const routed = selectAccountForModel(accounts, options?.model ?? "", this.deps.modelAccountRules?.());
174
+ if (routed !== void 0) return this.pick(routed);
147
175
  const chosen = selectActiveAccount(accounts, this.deps.preferredId?.());
148
176
  if (chosen !== void 0) return this.pick(chosen);
149
177
  await Promise.all(accounts.map(async (account) => {
@@ -215,7 +243,7 @@ var CommandCodeAccountPool = class {
215
243
  * and API key or subscription, and Command Code's terms apply.
216
244
  *
217
245
  * Wire protocol (reverse-engineered by the pi plugin, command-code@1.28.4;
218
- * re-verified against command-code@1.37.0 — endpoints, request shape, and
246
+ * re-verified against command-code@1.38.2 — endpoints, request shape, and
219
247
  * stream events unchanged):
220
248
  * POST {apiBase}/alpha/generate
221
249
  * body: { config, memory, taste, skills, params: { model, messages, tools,
@@ -359,6 +387,11 @@ const KNOWN_EFFORTS = {
359
387
  "max"
360
388
  ],
361
389
  "sakana/fugu-ultra": ["high", "xhigh"],
390
+ "tencent/hy4-preview": [
391
+ "low",
392
+ "medium",
393
+ "high"
394
+ ],
362
395
  "xai/grok-4.5": [
363
396
  "low",
364
397
  "medium",
@@ -443,7 +476,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
443
476
  "z-ai/glm-5.3-flash"
444
477
  ]);
445
478
  /**
446
- * Models the official CLI's model table (command-code@1.37.0) marks
479
+ * Models the official CLI's model table (command-code@1.38.2) marks
447
480
  * `reasoning:!0` but defines no selectable `reasoning_effort` levels — they
448
481
  * think automatically, with Command Code driving the depth. This is the
449
482
  * authoritative "thinks, effort not adjustable" set: `KNOWN_EFFORTS` (which
@@ -451,13 +484,15 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
451
484
  * effort levels, and this snapshot is not surfaced in the picker's compact
452
485
  * description — it exists for programmatic consumers.
453
486
  *
454
- * Source: the command-code@1.37.0 bundled model table (dist/cli.mjs),
487
+ * Source: the command-code@1.38.2 bundled model table (dist/cli.mjs),
455
488
  * cross-checked with https://commandcode.ai/docs/reference/cli/models.
456
489
  * (`stealth/ox-alpha` left this set in command-code@1.32.1, which gave it
457
490
  * selectable `['low', 'high', 'max']` efforts; the preview then ended in
458
491
  * 1.34.0, removing the model from the catalog entirely. `tencent/hy4-preview`
459
492
  * joined this set in command-code@1.37.0 — reasoning:!0, no efforts, 1M
460
- * context, routed through OpenRouter.)
493
+ * context, routed through OpenRouter — then gained selectable
494
+ * `['low', 'medium', 'high']` efforts in command-code@1.38.0 and moved to
495
+ * `KNOWN_EFFORTS`.)
461
496
  * Keep in sync via the dsh-commandcode-upstream skill.
462
497
  */
463
498
  const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
@@ -474,7 +509,6 @@ const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
474
509
  "stepfun/Step-3.5-Flash",
475
510
  "stepfun/Step-3.7-Flash",
476
511
  "tencent/hy3-paid",
477
- "tencent/hy4-preview",
478
512
  "nvidia/nemotron-3-ultra-550b-a55b",
479
513
  "thinkingmachines/inkling",
480
514
  "thinkingmachines/inkling-small",
@@ -514,6 +548,7 @@ const KNOWN_PLANS = {
514
548
  "deepseek/deepseek-v4-flash-vision-exp": "go",
515
549
  "deepseek/deepseek-v4-pro": "go",
516
550
  "gpt-5.6-luna": "go",
551
+ "inclusionai/ling-3.0-flash-free": "go",
517
552
  "meta/muse-spark-1.2-contributor": "go",
518
553
  "minimax/minimax-m2.7-free": "go",
519
554
  "minimax/minimax-m3-free": "go",
@@ -526,6 +561,7 @@ const KNOWN_PLANS = {
526
561
  "poolside/laguna-s-2.1-free": "go",
527
562
  "stepfun/Step-3.5-Flash": "go",
528
563
  "stepfun/Step-3.7-Flash": "go",
564
+ "tencent/Hy3": "go",
529
565
  "tencent/hy3-paid": "go",
530
566
  "tencent/hy4-preview": "go",
531
567
  "thinkingmachines/inkling": "go",
@@ -690,10 +726,6 @@ function modelVisibleInPlan(modelId, access) {
690
726
  return weight <= access.tierWeight;
691
727
  }
692
728
  const KNOWN_DEALS = {
693
- "google/gemini-3.7-flash": {
694
- label: "50% off",
695
- expiresAt: "2026-12-31T23:59:59Z"
696
- },
697
729
  "MiniMaxAI/MiniMax-M3": { label: "50% off" },
698
730
  "xiaomi/mimo-v2.5-pro": { label: "99% off" },
699
731
  "xiaomi/mimo-v2.5": { label: "98% off" },
@@ -762,7 +794,7 @@ function peakPricingLabel(modelId, now = Date.now()) {
762
794
  if (state === void 0) return void 0;
763
795
  return state === "peak" ? "Peak" : "Half";
764
796
  }
765
- const COMMAND_CODE_CLI_VERSION = "1.37.0";
797
+ const COMMAND_CODE_CLI_VERSION = "1.38.2";
766
798
  const DEFAULT_API_BASE = "https://api.commandcode.ai";
767
799
  const DEFAULT_GENERATE_MAX_TOKENS = 64e3;
768
800
  const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
@@ -1405,7 +1437,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
1405
1437
  readImage = (ref) => attachments.readImage(ref).then((stored) => stored.data);
1406
1438
  }
1407
1439
  const connection = this.deps.options();
1408
- let apiKey = await this.deps.resolveApiKey(connection);
1440
+ let apiKey = await this.deps.resolveApiKey(connection, options.model);
1409
1441
  const modelMax = this.catalog.find((m) => m.id === options.model)?.maxTokens ?? 65536;
1410
1442
  const maxTokens = Math.min(options.maxTokens ?? modelMax, modelMax, DEFAULT_GENERATE_MAX_TOKENS);
1411
1443
  const effort = options.reasoningEffort;
@@ -1515,7 +1547,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
1515
1547
  }
1516
1548
  const rotate = this.deps.rotateApiKey;
1517
1549
  if ((attempt.status === 429 || attempt.status === 401) && rotate !== void 0 && options.signal?.aborted !== true && tried.size < MAX_ACCOUNT_ROTATIONS) {
1518
- const next = await rotate(apiKey, attempt.status === 429 ? "rate-limit" : "invalid-credential", connection);
1550
+ const next = await rotate(apiKey, attempt.status === 429 ? "rate-limit" : "invalid-credential", connection, options.model);
1519
1551
  if (next !== void 0 && !tried.has(next)) {
1520
1552
  apiKey = next;
1521
1553
  continue;
@@ -2142,6 +2174,39 @@ const USAGE_HOST_CONTRIBUTION = {
2142
2174
  }
2143
2175
  }]
2144
2176
  };
2177
+ /** Canonical `<namespace>/<method>` endpoint of the model-catalog Remote. */
2178
+ const MODELS_ENDPOINT = "commandcode/models";
2179
+ /** Parse one untrusted boundary value into a {@link CommandCodeCatalogModel}. */
2180
+ function parseCatalogModel(value) {
2181
+ const source = record(value, "model");
2182
+ return {
2183
+ id: stringField(source, "id", "model.id"),
2184
+ name: stringField(source, "name", "model.name")
2185
+ };
2186
+ }
2187
+ /** Parse the wire result into a {@link CommandCodeCatalog}. */
2188
+ function parseCatalog(value) {
2189
+ const models = record(value, "result").models;
2190
+ if (!Array.isArray(models)) reject$1("models");
2191
+ return { models: models.map(parseCatalogModel) };
2192
+ }
2193
+ /**
2194
+ * The model-catalog invocation descriptor, sharing the same `commandcodeUsage`
2195
+ * service and `commandcode` namespace as the usage report.
2196
+ */
2197
+ const MODELS_DESCRIPTOR = {
2198
+ id: `${USAGE_REMOTE_PACKAGE}#${MODELS_ENDPOINT}`,
2199
+ service: "commandcodeUsage",
2200
+ namespace: "commandcode",
2201
+ method: "models",
2202
+ invocation: { kind: "direct" },
2203
+ parameters: [],
2204
+ result: {
2205
+ mode: "strict",
2206
+ typeSymbol: `${USAGE_REMOTE_PACKAGE}#CommandCodeCatalog`,
2207
+ schema: { parse: parseCatalog }
2208
+ }
2209
+ };
2145
2210
  //#endregion
2146
2211
  //#region src/login-wire.ts
2147
2212
  /** The canonical endpoint paths of the three login Remotes. */
@@ -2254,6 +2319,15 @@ var CommandCodeUsageService = class extends TypertRemoteService {
2254
2319
  }] };
2255
2320
  }
2256
2321
  /**
2322
+ * The model catalog for the settings page's routing-rule editor. The
2323
+ * browser never calls the Command Code API directly — the Host serves the
2324
+ * catalog (already fetched/cached by the adapter) so rules can be picked
2325
+ * from the live model list instead of typed by hand.
2326
+ */
2327
+ async models() {
2328
+ return this.deps.listModels?.() ?? { models: [] };
2329
+ }
2330
+ /**
2257
2331
  * Start (or rejoin) a browser-login attempt and return its fresh status —
2258
2332
  * `waiting` carrying the Studio URL. Rejects when the flow cannot start
2259
2333
  * (no free loopback port, disposed plugin); the Gateway folds the throw
@@ -2287,7 +2361,11 @@ function applyUsageRemote(ctx, deps) {
2287
2361
  new CommandCodeUsageService(remoteCtx, deps);
2288
2362
  const unregister = remoteCtx.typert.register({
2289
2363
  ...USAGE_HOST_CONTRIBUTION,
2290
- invocations: [...USAGE_HOST_CONTRIBUTION.invocations, ...LOGIN_DESCRIPTORS]
2364
+ invocations: [
2365
+ ...USAGE_HOST_CONTRIBUTION.invocations,
2366
+ MODELS_DESCRIPTOR,
2367
+ ...LOGIN_DESCRIPTORS
2368
+ ]
2291
2369
  });
2292
2370
  remoteCtx.effect(() => () => void unregister(), "dsh-commandcode-provider: usage remote");
2293
2371
  });
@@ -2725,6 +2803,10 @@ const Config = z.object({
2725
2803
  apiKey: z.string()
2726
2804
  })),
2727
2805
  activeAccount: z.string(),
2806
+ modelAccountRules: z.array(z.object({
2807
+ models: z.array(z.string()),
2808
+ account: z.string()
2809
+ })),
2728
2810
  lang: z.string().pattern(/^(zh|en)$/).default("zh")
2729
2811
  });
2730
2812
  /**
@@ -2795,10 +2877,11 @@ function apply(ctx, config) {
2795
2877
  resolveRef,
2796
2878
  authFileKey: resolveAuthFileApiKey,
2797
2879
  probeWindow: (apiKey) => adapter.probeFiveHourWindow(apiKey),
2798
- preferredId
2880
+ preferredId,
2881
+ modelAccountRules: () => current().modelAccountRules ?? []
2799
2882
  });
2800
- const resolveApiKey = async (connection) => {
2801
- const resolved = await pool.resolveKey();
2883
+ const resolveApiKey = async (connection, model) => {
2884
+ const resolved = await pool.resolveKey(model === void 0 ? {} : { model });
2802
2885
  if (resolved !== void 0) return assertUsableApiKey(resolved.key, "llm-commandcode", resolved.slot.ref ?? `${resolved.slot.label} (config.apiKey)`);
2803
2886
  const ref = connection.apiKeyEnv;
2804
2887
  throw new LlmError(`llm-commandcode: no API key for provider route "${PROVIDER}"; store ${ref} through the credentials service (the web Models page writes it), export it in the launching environment, set config.apiKey, or run \`command-code login\` to write ~/.commandcode/auth.json`, "MISSING_CREDENTIAL");
@@ -2806,9 +2889,12 @@ function apply(ctx, config) {
2806
2889
  const adapter = new CommandCodeAdapter({
2807
2890
  options,
2808
2891
  resolveApiKey,
2809
- rotateApiKey: async (rejectedKey, rejection) => {
2892
+ rotateApiKey: async (rejectedKey, rejection, _connection, model) => {
2810
2893
  pool.markRejected(rejectedKey, rejection);
2811
- const resolved = await pool.resolveKey({ exclude: rejectedKey });
2894
+ const resolved = await pool.resolveKey(model === void 0 ? { exclude: rejectedKey } : {
2895
+ exclude: rejectedKey,
2896
+ model
2897
+ });
2812
2898
  return resolved === void 0 ? void 0 : assertUsableApiKey(resolved.key, "llm-commandcode", resolved.slot.ref ?? `${resolved.slot.label} (config.apiKey)`);
2813
2899
  },
2814
2900
  resolveAttachments: () => {
@@ -2867,10 +2953,17 @@ function apply(ctx, config) {
2867
2953
  }
2868
2954
  });
2869
2955
  ctx.effect(() => () => loginFlow.dispose(), "dsh-commandcode-provider: login flow");
2956
+ const catalogForRules = async () => {
2957
+ return { models: (await adapter.listModels(PROVIDER)).map((model) => ({
2958
+ id: model.id,
2959
+ name: model.name.replace(/\s*\(CC\)$/, "")
2960
+ })) };
2961
+ };
2870
2962
  applyUsageRemote(ctx, {
2871
2963
  adapter,
2872
2964
  reports: usageReports,
2873
- login: loginFlow
2965
+ login: loginFlow,
2966
+ listModels: catalogForRules
2874
2967
  });
2875
2968
  ctx.inject(["settings"], (settingsCtx) => {
2876
2969
  settingsCtx.settings.installSection(ctx, NS, Config, config, {
@@ -2882,6 +2975,6 @@ function apply(ctx, config) {
2882
2975
  });
2883
2976
  }
2884
2977
  //#endregion
2885
- export { BILLING_ACCESS_TTL_MS, COMMAND_CODE_CLI_VERSION, CommandCodeAccountPool, CommandCodeAdapter, CommandCodeLoginFlow, CommandCodeUsageService, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_SUBSCRIPTION_PLANS, KNOWN_THINKING_MODELS, LOGIN_ALLOWED_ORIGINS, LOGIN_BEGIN_ENDPOINT, LOGIN_BODY_LIMIT_BYTES, LOGIN_CANCEL_ENDPOINT, LOGIN_MAX_PORT_ATTEMPTS, LOGIN_START_PORT, LOGIN_STATUS_ENDPOINT, LOGIN_TIMEOUT_MS, PLAN_LABELS, PLAN_ORDER, PROVIDER, USAGE_REPORT_ENDPOINT, accountUsable, apply, applyCommands, applyUsageRemote, buildCommandAuthUrl, capabilityDescription, commandDefinition, compareByPlan, dealLabel, formatContext, inject, loginStatusSchema, modelVisibleInPlan, name, parseLoginStatus, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey, selectActiveAccount, studioBaseForApiBase, subscriptionPlanInfo, usageReportSchema, validateCommandApiKey };
2978
+ export { BILLING_ACCESS_TTL_MS, COMMAND_CODE_CLI_VERSION, CommandCodeAccountPool, CommandCodeAdapter, CommandCodeLoginFlow, CommandCodeUsageService, Config, DEFAULT_API_BASE, DEFAULT_GENERATE_MAX_TOKENS, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MODELS_CACHE_PATH, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_STREAM_IDLE_TIMEOUT_MS, KNOWN_DEALS, KNOWN_EFFORTS, KNOWN_IMAGE_MODELS, KNOWN_PEAK_PRICING, KNOWN_PLANS, KNOWN_SUBSCRIPTION_PLANS, KNOWN_THINKING_MODELS, LOGIN_ALLOWED_ORIGINS, LOGIN_BEGIN_ENDPOINT, LOGIN_BODY_LIMIT_BYTES, LOGIN_CANCEL_ENDPOINT, LOGIN_MAX_PORT_ATTEMPTS, LOGIN_START_PORT, LOGIN_STATUS_ENDPOINT, LOGIN_TIMEOUT_MS, PLAN_LABELS, PLAN_ORDER, PROVIDER, USAGE_REPORT_ENDPOINT, accountUsable, apply, applyCommands, applyUsageRemote, buildCommandAuthUrl, capabilityDescription, commandDefinition, compareByPlan, dealLabel, formatContext, inject, loginStatusSchema, matchModelRule, modelVisibleInPlan, name, parseLoginStatus, peakPricingLabel, peakPricingState, planLabel, projectSlugFromPath, resolveAdapterOptions, resolveAuthFileApiKey, selectAccountForModel, selectActiveAccount, studioBaseForApiBase, subscriptionPlanInfo, usageReportSchema, validateCommandApiKey };
2886
2979
 
2887
2980
  //# sourceMappingURL=index.js.map