@mars-sea/dsh-commandcode-provider 0.10.0-alpha.1 → 0.10.0-alpha.3

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.39.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.39.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.39.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.39.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,
@@ -286,6 +314,11 @@ const KNOWN_EFFORTS = {
286
314
  "xhigh",
287
315
  "max"
288
316
  ],
317
+ "deepseek/deepseek-v4-flash-fast": [
318
+ "low",
319
+ "high",
320
+ "max"
321
+ ],
289
322
  "deepseek/deepseek-v4-flash": ["high", "max"],
290
323
  "deepseek/deepseek-v4-flash-vision-exp": ["high", "max"],
291
324
  "deepseek/deepseek-v4-pro": ["high", "max"],
@@ -359,6 +392,11 @@ const KNOWN_EFFORTS = {
359
392
  "max"
360
393
  ],
361
394
  "sakana/fugu-ultra": ["high", "xhigh"],
395
+ "tencent/hy4-preview": [
396
+ "low",
397
+ "medium",
398
+ "high"
399
+ ],
362
400
  "xai/grok-4.5": [
363
401
  "low",
364
402
  "medium",
@@ -443,7 +481,7 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
443
481
  "z-ai/glm-5.3-flash"
444
482
  ]);
445
483
  /**
446
- * Models the official CLI's model table (command-code@1.37.0) marks
484
+ * Models the official CLI's model table (command-code@1.39.2) marks
447
485
  * `reasoning:!0` but defines no selectable `reasoning_effort` levels — they
448
486
  * think automatically, with Command Code driving the depth. This is the
449
487
  * authoritative "thinks, effort not adjustable" set: `KNOWN_EFFORTS` (which
@@ -451,13 +489,15 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
451
489
  * effort levels, and this snapshot is not surfaced in the picker's compact
452
490
  * description — it exists for programmatic consumers.
453
491
  *
454
- * Source: the command-code@1.37.0 bundled model table (dist/cli.mjs),
492
+ * Source: the command-code@1.39.2 bundled model table (dist/cli.mjs),
455
493
  * cross-checked with https://commandcode.ai/docs/reference/cli/models.
456
494
  * (`stealth/ox-alpha` left this set in command-code@1.32.1, which gave it
457
495
  * selectable `['low', 'high', 'max']` efforts; the preview then ended in
458
496
  * 1.34.0, removing the model from the catalog entirely. `tencent/hy4-preview`
459
497
  * joined this set in command-code@1.37.0 — reasoning:!0, no efforts, 1M
460
- * context, routed through OpenRouter.)
498
+ * context, routed through OpenRouter — then gained selectable
499
+ * `['low', 'medium', 'high']` efforts in command-code@1.38.0 and moved to
500
+ * `KNOWN_EFFORTS`.)
461
501
  * Keep in sync via the dsh-commandcode-upstream skill.
462
502
  */
463
503
  const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
@@ -474,7 +514,6 @@ const KNOWN_THINKING_MODELS = /* @__PURE__ */ new Set([
474
514
  "stepfun/Step-3.5-Flash",
475
515
  "stepfun/Step-3.7-Flash",
476
516
  "tencent/hy3-paid",
477
- "tencent/hy4-preview",
478
517
  "nvidia/nemotron-3-ultra-550b-a55b",
479
518
  "thinkingmachines/inkling",
480
519
  "thinkingmachines/inkling-small",
@@ -510,10 +549,12 @@ const KNOWN_PLANS = {
510
549
  "Qwen/Qwen3.8-27B": "go",
511
550
  "Qwen/Qwen3.8-Flash": "go",
512
551
  "Qwen/Qwen3.8-Max": "go",
552
+ "deepseek/deepseek-v4-flash-fast": "go",
513
553
  "deepseek/deepseek-v4-flash": "go",
514
554
  "deepseek/deepseek-v4-flash-vision-exp": "go",
515
555
  "deepseek/deepseek-v4-pro": "go",
516
556
  "gpt-5.6-luna": "go",
557
+ "inclusionai/ling-3.0-flash-free": "go",
517
558
  "meta/muse-spark-1.2-contributor": "go",
518
559
  "minimax/minimax-m2.7-free": "go",
519
560
  "minimax/minimax-m3-free": "go",
@@ -526,6 +567,7 @@ const KNOWN_PLANS = {
526
567
  "poolside/laguna-s-2.1-free": "go",
527
568
  "stepfun/Step-3.5-Flash": "go",
528
569
  "stepfun/Step-3.7-Flash": "go",
570
+ "tencent/Hy3": "go",
529
571
  "tencent/hy3-paid": "go",
530
572
  "tencent/hy4-preview": "go",
531
573
  "thinkingmachines/inkling": "go",
@@ -690,23 +732,9 @@ function modelVisibleInPlan(modelId, access) {
690
732
  return weight <= access.tierWeight;
691
733
  }
692
734
  const KNOWN_DEALS = {
693
- "google/gemini-3.7-flash": {
694
- label: "50% off",
695
- expiresAt: "2026-12-31T23:59:59Z"
696
- },
697
735
  "MiniMaxAI/MiniMax-M3": { label: "50% off" },
698
736
  "xiaomi/mimo-v2.5-pro": { label: "99% off" },
699
737
  "xiaomi/mimo-v2.5": { label: "98% off" },
700
- "minimax/minimax-m3-free": {
701
- label: "FREE",
702
- free: true,
703
- expiresAt: "2026-09-05T23:59:59Z"
704
- },
705
- "minimax/minimax-m2.7-free": {
706
- label: "FREE",
707
- free: true,
708
- expiresAt: "2026-09-05T23:59:59Z"
709
- },
710
738
  "poolside/laguna-s-2.1-free": {
711
739
  label: "FREE",
712
740
  free: true
@@ -737,7 +765,8 @@ const KNOWN_DEALS = {
737
765
  const KNOWN_PEAK_PRICING = /* @__PURE__ */ new Set([
738
766
  "deepseek/deepseek-v4-pro",
739
767
  "deepseek/deepseek-v4-flash",
740
- "deepseek/deepseek-v4-flash-vision-exp"
768
+ "deepseek/deepseek-v4-flash-vision-exp",
769
+ "deepseek/deepseek-v4-flash-fast"
741
770
  ]);
742
771
  /** Peak hours (UTC, hour-of-day range end-exclusive): 01–03 and 06–09. */
743
772
  const PEAK_HOUR_RANGES = [[1, 4], [6, 10]];
@@ -762,7 +791,7 @@ function peakPricingLabel(modelId, now = Date.now()) {
762
791
  if (state === void 0) return void 0;
763
792
  return state === "peak" ? "Peak" : "Half";
764
793
  }
765
- const COMMAND_CODE_CLI_VERSION = "1.37.0";
794
+ const COMMAND_CODE_CLI_VERSION = "1.39.2";
766
795
  const DEFAULT_API_BASE = "https://api.commandcode.ai";
767
796
  const DEFAULT_GENERATE_MAX_TOKENS = 64e3;
768
797
  const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
@@ -1405,7 +1434,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
1405
1434
  readImage = (ref) => attachments.readImage(ref).then((stored) => stored.data);
1406
1435
  }
1407
1436
  const connection = this.deps.options();
1408
- let apiKey = await this.deps.resolveApiKey(connection);
1437
+ let apiKey = await this.deps.resolveApiKey(connection, options.model);
1409
1438
  const modelMax = this.catalog.find((m) => m.id === options.model)?.maxTokens ?? 65536;
1410
1439
  const maxTokens = Math.min(options.maxTokens ?? modelMax, modelMax, DEFAULT_GENERATE_MAX_TOKENS);
1411
1440
  const effort = options.reasoningEffort;
@@ -1515,7 +1544,7 @@ var CommandCodeAdapter = class extends LlmAdapter {
1515
1544
  }
1516
1545
  const rotate = this.deps.rotateApiKey;
1517
1546
  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);
1547
+ const next = await rotate(apiKey, attempt.status === 429 ? "rate-limit" : "invalid-credential", connection, options.model);
1519
1548
  if (next !== void 0 && !tried.has(next)) {
1520
1549
  apiKey = next;
1521
1550
  continue;
@@ -2142,6 +2171,39 @@ const USAGE_HOST_CONTRIBUTION = {
2142
2171
  }
2143
2172
  }]
2144
2173
  };
2174
+ /** Canonical `<namespace>/<method>` endpoint of the model-catalog Remote. */
2175
+ const MODELS_ENDPOINT = "commandcode/models";
2176
+ /** Parse one untrusted boundary value into a {@link CommandCodeCatalogModel}. */
2177
+ function parseCatalogModel(value) {
2178
+ const source = record(value, "model");
2179
+ return {
2180
+ id: stringField(source, "id", "model.id"),
2181
+ name: stringField(source, "name", "model.name")
2182
+ };
2183
+ }
2184
+ /** Parse the wire result into a {@link CommandCodeCatalog}. */
2185
+ function parseCatalog(value) {
2186
+ const models = record(value, "result").models;
2187
+ if (!Array.isArray(models)) reject$1("models");
2188
+ return { models: models.map(parseCatalogModel) };
2189
+ }
2190
+ /**
2191
+ * The model-catalog invocation descriptor, sharing the same `commandcodeUsage`
2192
+ * service and `commandcode` namespace as the usage report.
2193
+ */
2194
+ const MODELS_DESCRIPTOR = {
2195
+ id: `${USAGE_REMOTE_PACKAGE}#${MODELS_ENDPOINT}`,
2196
+ service: "commandcodeUsage",
2197
+ namespace: "commandcode",
2198
+ method: "models",
2199
+ invocation: { kind: "direct" },
2200
+ parameters: [],
2201
+ result: {
2202
+ mode: "strict",
2203
+ typeSymbol: `${USAGE_REMOTE_PACKAGE}#CommandCodeCatalog`,
2204
+ schema: { parse: parseCatalog }
2205
+ }
2206
+ };
2145
2207
  //#endregion
2146
2208
  //#region src/login-wire.ts
2147
2209
  /** The canonical endpoint paths of the three login Remotes. */
@@ -2254,6 +2316,15 @@ var CommandCodeUsageService = class extends TypertRemoteService {
2254
2316
  }] };
2255
2317
  }
2256
2318
  /**
2319
+ * The model catalog for the settings page's routing-rule editor. The
2320
+ * browser never calls the Command Code API directly — the Host serves the
2321
+ * catalog (already fetched/cached by the adapter) so rules can be picked
2322
+ * from the live model list instead of typed by hand.
2323
+ */
2324
+ async models() {
2325
+ return this.deps.listModels?.() ?? { models: [] };
2326
+ }
2327
+ /**
2257
2328
  * Start (or rejoin) a browser-login attempt and return its fresh status —
2258
2329
  * `waiting` carrying the Studio URL. Rejects when the flow cannot start
2259
2330
  * (no free loopback port, disposed plugin); the Gateway folds the throw
@@ -2287,7 +2358,11 @@ function applyUsageRemote(ctx, deps) {
2287
2358
  new CommandCodeUsageService(remoteCtx, deps);
2288
2359
  const unregister = remoteCtx.typert.register({
2289
2360
  ...USAGE_HOST_CONTRIBUTION,
2290
- invocations: [...USAGE_HOST_CONTRIBUTION.invocations, ...LOGIN_DESCRIPTORS]
2361
+ invocations: [
2362
+ ...USAGE_HOST_CONTRIBUTION.invocations,
2363
+ MODELS_DESCRIPTOR,
2364
+ ...LOGIN_DESCRIPTORS
2365
+ ]
2291
2366
  });
2292
2367
  remoteCtx.effect(() => () => void unregister(), "dsh-commandcode-provider: usage remote");
2293
2368
  });
@@ -2725,6 +2800,10 @@ const Config = z.object({
2725
2800
  apiKey: z.string()
2726
2801
  })),
2727
2802
  activeAccount: z.string(),
2803
+ modelAccountRules: z.array(z.object({
2804
+ models: z.array(z.string()),
2805
+ account: z.string()
2806
+ })),
2728
2807
  lang: z.string().pattern(/^(zh|en)$/).default("zh")
2729
2808
  });
2730
2809
  /**
@@ -2795,10 +2874,11 @@ function apply(ctx, config) {
2795
2874
  resolveRef,
2796
2875
  authFileKey: resolveAuthFileApiKey,
2797
2876
  probeWindow: (apiKey) => adapter.probeFiveHourWindow(apiKey),
2798
- preferredId
2877
+ preferredId,
2878
+ modelAccountRules: () => current().modelAccountRules ?? []
2799
2879
  });
2800
- const resolveApiKey = async (connection) => {
2801
- const resolved = await pool.resolveKey();
2880
+ const resolveApiKey = async (connection, model) => {
2881
+ const resolved = await pool.resolveKey(model === void 0 ? {} : { model });
2802
2882
  if (resolved !== void 0) return assertUsableApiKey(resolved.key, "llm-commandcode", resolved.slot.ref ?? `${resolved.slot.label} (config.apiKey)`);
2803
2883
  const ref = connection.apiKeyEnv;
2804
2884
  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 +2886,12 @@ function apply(ctx, config) {
2806
2886
  const adapter = new CommandCodeAdapter({
2807
2887
  options,
2808
2888
  resolveApiKey,
2809
- rotateApiKey: async (rejectedKey, rejection) => {
2889
+ rotateApiKey: async (rejectedKey, rejection, _connection, model) => {
2810
2890
  pool.markRejected(rejectedKey, rejection);
2811
- const resolved = await pool.resolveKey({ exclude: rejectedKey });
2891
+ const resolved = await pool.resolveKey(model === void 0 ? { exclude: rejectedKey } : {
2892
+ exclude: rejectedKey,
2893
+ model
2894
+ });
2812
2895
  return resolved === void 0 ? void 0 : assertUsableApiKey(resolved.key, "llm-commandcode", resolved.slot.ref ?? `${resolved.slot.label} (config.apiKey)`);
2813
2896
  },
2814
2897
  resolveAttachments: () => {
@@ -2867,10 +2950,17 @@ function apply(ctx, config) {
2867
2950
  }
2868
2951
  });
2869
2952
  ctx.effect(() => () => loginFlow.dispose(), "dsh-commandcode-provider: login flow");
2953
+ const catalogForRules = async () => {
2954
+ return { models: (await adapter.listModels(PROVIDER)).map((model) => ({
2955
+ id: model.id,
2956
+ name: model.name.replace(/\s*\(CC\)$/, "")
2957
+ })) };
2958
+ };
2870
2959
  applyUsageRemote(ctx, {
2871
2960
  adapter,
2872
2961
  reports: usageReports,
2873
- login: loginFlow
2962
+ login: loginFlow,
2963
+ listModels: catalogForRules
2874
2964
  });
2875
2965
  ctx.inject(["settings"], (settingsCtx) => {
2876
2966
  settingsCtx.settings.installSection(ctx, NS, Config, config, {
@@ -2882,6 +2972,6 @@ function apply(ctx, config) {
2882
2972
  });
2883
2973
  }
2884
2974
  //#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 };
2975
+ 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
2976
 
2887
2977
  //# sourceMappingURL=index.js.map