@mars-sea/dsh-commandcode-provider 0.12.2 → 0.12.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
@@ -1,11 +1,26 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
- import { GenerateOptions, LlmAdapter, LlmImageRequestPricing, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, ResolvedRetryPolicy, StreamChunk, projectOffloadedImages, requiredImageOffload } from "@deepseek-ai/dsh-llm";
2
+ import { GenerateOptions, LlmAdapter, LlmError, LlmImageRequestPricing, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, ResolvedRetryPolicy, StreamChunk, projectOffloadedImages, requiredImageOffload } from "@deepseek-ai/dsh-llm";
3
3
  import { CredentialRef } from "@deepseek-ai/dsh-credentials";
4
4
  import { TypertRemoteService, TypertSchema } from "@deepseek-ai/dsh-typert-protocol";
5
5
  import { WebRuntime, WebSearchProvider, WebSearchRequest, WebSearchResult } from "@deepseek-ai/dsh-web";
6
6
  import { Context } from "@deepseek-ai/cordis";
7
7
  import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
8
8
  import { CommandDefinition } from "@deepseek-ai/dsh-commands";
9
+ //#region src/provider-errors.d.ts
10
+ /**
11
+ * Why a pre-stream rejection rotates to another account. `rate-limit` (a usage
12
+ * window the provider NAMED), `throttled` (a 429 that named no window) and
13
+ * `invalid-credential` are the three the pool records as marks; `unavailable`
14
+ * is an account-scoped rejection that must NOT become a mark — the account's
15
+ * key is valid and its windows may be open, the ACCOUNT just cannot serve THIS
16
+ * request — so the pool moves on without remembering anything.
17
+ *
18
+ * The window/throttle split decides what the pool may CLAIM, never whether it
19
+ * rotates (issue #54); the evidence that separates them is in
20
+ * {@link classifyAccountRejection}.
21
+ */
22
+ type AccountRotationReason = 'rate-limit' | 'throttled' | 'invalid-credential' | 'unavailable';
23
+ //#endregion
9
24
  //#region src/request-timing.d.ts
10
25
  type Phase = 'credentials' | 'images' | 'body' | 'serialization' | 'headers';
11
26
  type Outcome = 'finished' | 'error' | 'aborted' | 'cancelled';
@@ -28,8 +43,25 @@ interface RequestTimingSummary {
28
43
  }
29
44
  type RequestTimingSink = (summary: RequestTimingSummary) => void | Promise<void>;
30
45
  //#endregion
46
+ //#region src/stream-response.d.ts
47
+ /**
48
+ * Endpoint protocol selected for one generate call.
49
+ *
50
+ * `messages` is the Anthropic Messages surface at
51
+ * `{apiBase}/provider/v1/messages`, the only Provider API route the Claude
52
+ * family answers (posted to `/chat/completions` they refuse with `400 Model
53
+ * "<id>" must be called via /provider/v1/messages`). `openai` is the
54
+ * documented Chat Completions surface; `cli` is Command Code's private
55
+ * `/alpha/generate` transport, kept for Go-plan keys (the one plan without
56
+ * Provider API access) and as the `upgrade_required` fallback.
57
+ */
58
+ type CommandCodeProtocol = 'cli' | 'openai' | 'messages';
59
+ //#endregion
60
+ //#region src/gateway-facts.d.ts
61
+ declare const DEFAULT_MAX_OUTPUT_TOKENS = 131072;
62
+ //#endregion
31
63
  //#region src/adapter.d.ts
32
- declare const COMMAND_CODE_CLI_VERSION = "1.73.0";
64
+ declare const COMMAND_CODE_CLI_VERSION = "1.74.1";
33
65
  declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
34
66
  /**
35
67
  * The output budget this bundle asks for, and the ceiling it will never exceed.
@@ -38,8 +70,9 @@ declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
38
70
  * Probing `/alpha/generate` directly on 2026-09-28: 64 000 → 200, 131 072 → 200,
39
71
  * 196 608 → 200, **200 000 → 200, 200 704 → 400**, 1 000 000 → 400. The server
40
72
  * therefore caps `max_tokens` at 200 000, and it does so identically for a
41
- * 262 144-window model (`stealth/pixel-canary`) and a 1 000 000-window one
42
- * (`stealth/space-bunny-alpha`) — the cap is global, not per model. 131 072
73
+ * 262 144-window model (`stealth/pixel-canary`, retired by Command Code on
74
+ * 2026-10-01 and named here only as the probe's subject) and a 1 000 000-window
75
+ * one (`stealth/space-bunny-alpha`) — the cap is global, not per model. 131 072
43
76
  * keeps a 35 % margin under it while covering the real output ceiling of the
44
77
  * models that have one (Claude 64 K, Gemini 64 K, several at 128 K).
45
78
  *
@@ -59,30 +92,8 @@ declare const DEFAULT_API_BASE = "https://api.commandcode.ai";
59
92
  * [决策记录](../docs/决策记录.md).
60
93
  */
61
94
  declare const DEFAULT_GENERATE_MAX_TOKENS = 131072;
62
- /**
63
- * The fallback when the catalog carries no output ceiling for a model.
64
- *
65
- * `/provider/v1/models` publishes only `context_length` — no output limit — so
66
- * the catalog cannot learn one per model and derives a stand-in from the
67
- * window instead. Kept equal to {@link DEFAULT_GENERATE_MAX_TOKENS} on purpose:
68
- * a lower value here would silently re-cap every request through
69
- * `Math.min(contextLength, this)` before the ceiling above is ever consulted.
70
- */
71
- declare const DEFAULT_MAX_OUTPUT_TOKENS = 131072;
72
95
  /** How long the picker's plan-filter billing facts stay cached before refetching. */
73
96
  declare const BILLING_ACCESS_TTL_MS: number;
74
- /**
75
- * Endpoint protocol selected for one generate call.
76
- *
77
- * `messages` is the Anthropic Messages surface at
78
- * `{apiBase}/provider/v1/messages`, the only Provider API route the Claude
79
- * family answers (posted to `/chat/completions` they refuse with `400 Model
80
- * "<id>" must be called via /provider/v1/messages`). `openai` is the
81
- * documented Chat Completions surface; `cli` is Command Code's private
82
- * `/alpha/generate` transport, kept for Go-plan keys (the one plan without
83
- * Provider API access) and as the `upgrade_required` fallback.
84
- */
85
- type CommandCodeProtocol = 'cli' | 'openai' | 'messages';
86
97
  /**
87
98
  * Head-of-request timeout: how long to wait for the first response byte.
88
99
  *
@@ -198,19 +209,6 @@ interface CommandCodeConnectionOptions {
198
209
  * text-only request never depends on the attachment seam.
199
210
  */
200
211
  type ResolveAttachments = () => AttachmentStore | undefined;
201
- /**
202
- * Why a pre-stream rejection rotates to another account. `rate-limit` (a usage
203
- * window the provider NAMED), `throttled` (a 429 that named no window) and
204
- * `invalid-credential` are the three the pool records as marks; `unavailable`
205
- * is an account-scoped rejection that must NOT become a mark — the account's
206
- * key is valid and its windows may be open, the ACCOUNT just cannot serve THIS
207
- * request — so the pool moves on without remembering anything.
208
- *
209
- * The window/throttle split decides what the pool may CLAIM, never whether it
210
- * rotates (issue #54); the evidence that separates them is in
211
- * {@link classifyAccountRejection}.
212
- */
213
- type AccountRotationReason = 'rate-limit' | 'throttled' | 'invalid-credential' | 'unavailable';
214
212
  /** What the rotation hook knows about the request it is rotating within. */
215
213
  interface AccountRotationContext {
216
214
  /** Every API key this request has already used, just-rejected key included. */
@@ -257,7 +255,7 @@ interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = Comman
257
255
  * a host without a pool omits this seam and the filter falls back to the key
258
256
  * that would serve the current request.
259
257
  */
260
- resolveAccountKeys?: () => Promise<readonly string[]>;
258
+ resolveAccountKeys?: (connection: C) => Promise<readonly string[]>;
261
259
  /** HTTP transport override (tests); defaults to the global `fetch`. */
262
260
  fetchImpl?: typeof fetch;
263
261
  /** Resolve the optional durable attachment service for image input (tests); defaults to none. */
@@ -366,8 +364,7 @@ interface CommandCodeUsageReport {
366
364
  }
367
365
  declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> extends LlmAdapter {
368
366
  private readonly deps;
369
- private catalog;
370
- private catalogState;
367
+ private readonly gatewayFacts;
371
368
  private readonly fetchImpl;
372
369
  private readonly resolveAttachments;
373
370
  /**
@@ -446,12 +443,6 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
446
443
  * requirement — the meter throws when the counts differ.
447
444
  */
448
445
  imageRequestPricing(_provider: string, model: string): LlmImageRequestPricing | undefined;
449
- /** 地址或缓存文件变化时丢弃旧目录,防止旧网关的路由声明影响新请求。 */
450
- private currentCatalogState;
451
- /** 直接生成也使用本地可信目录,不额外联网;并发冷请求共用一次文件读取。 */
452
- private catalogForGenerate;
453
- /** 共享一次刷新;调用者取消只结束自己的等待,不取消其他调用者的请求。 */
454
- private loadCatalog;
455
446
  listModels(provider: string, opts?: {
456
447
  unfiltered?: boolean;
457
448
  }): Promise<readonly LlmModelInfo[]>;
@@ -570,7 +561,7 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
570
561
  * 60 s), not the catalog's 10 s probe budget, because on a slow link a 10 s
571
562
  * cap made every account query time out while chat kept working.
572
563
  */
573
- getUsage(apiKey?: string): Promise<CommandCodeUsageReport>;
564
+ getUsage(apiKey?: string, capturedConnection?: C): Promise<CommandCodeUsageReport>;
574
565
  /**
575
566
  * Probe one account's real usage windows from `/alpha/billing/credits`. The
576
567
  * multi-account pool calls this when every account is marked exhausted: an
@@ -593,11 +584,12 @@ declare class CommandCodeAdapter<C extends CommandCodeConnectionOptions = Comman
593
584
  * state. The credit balances are deliberately NOT part of this answer: they
594
585
  * carry no reset time, and the server remains the final gate on them.
595
586
  */
596
- probeWindowLimits(apiKey: string): Promise<{
587
+ probeWindowLimits(apiKey: string, connection?: C): Promise<{
597
588
  exceeded: boolean;
598
589
  resetAt: number;
599
590
  } | undefined>;
600
591
  stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
592
+ private streamWithTiming;
601
593
  /**
602
594
  * One turn, with at most one output-ceiling downshift.
603
595
  *
@@ -765,18 +757,26 @@ declare function matchModelRule(model: string, rules: readonly CommandCodeModelA
765
757
  * the normal preferred/rotation selection).
766
758
  */
767
759
  declare function selectAccountForModel(accounts: readonly ResolvedAccount[], model: string, rules: readonly CommandCodeModelAccountRule[] | undefined): ResolvedAccount | undefined;
768
- /**
769
- * The account pool. Rotation state is keyed by API key (never logged), so two
770
- * slots resolving to the same credential share one mark, and a key changed in
771
- * the credentials service starts with a clean slate.
772
- */
760
+ /** 不包含解析秘密的账号定义;作用域独立复制列表及模型规则。 */
761
+ interface CommandCodeAccountSelection {
762
+ slots: readonly CommandCodeAccountSlot[];
763
+ preferredId?: string | undefined;
764
+ modelAccountRules: readonly CommandCodeModelAccountRule[];
765
+ }
766
+ /** 账号选择与展示共用恢复规则;宿主作用域内的状态按网关与密钥共享。 */
773
767
  declare class CommandCodeAccountPool {
774
768
  private readonly deps;
775
- /** Rotation state by API key. */
776
- private readonly states;
777
- /** Last explicit-revival probe attempt by API key (throttles a failing probe). */
778
- private readonly explicitProbes;
769
+ /** 当前作用域内按密钥共享的健康状态;不记录到日志。 */
770
+ private states;
771
+ /** 当前网关与密钥的显式探测时间,限制失败探测的频率。 */
772
+ private explicitProbes;
773
+ private scopedStates;
779
774
  constructor(deps: CommandCodeAccountPoolDeps);
775
+ /** 每个调用持有自己的定义和探测,只有同网关的健康事实共享;不临时改写全局依赖。 */
776
+ scope(context: CommandCodeAccountSelection & {
777
+ apiBase: string;
778
+ probeWindow: CommandCodeAccountPoolDeps['probeWindow'];
779
+ }): CommandCodeAccountPool;
780
780
  /**
781
781
  * Resolve every slot's key, deduplicated by key (first slot wins). Slots
782
782
  * without any resolvable key are omitted — they still appear in the
@@ -802,7 +802,7 @@ declare class CommandCodeAccountPool {
802
802
  * from the resolution entirely, which is what lets one request walk a
803
803
  * four-account pool. An explicit selection (pin or model rule) that a
804
804
  * rate-limit mark would demote is probed first, so a fallback never becomes
805
- * permanent (issue #51). Rules re-read per resolution, so settings apply live.
805
+ * permanent (issue #51). Scoped rules stay fixed for the call; credential values re-resolve.
806
806
  */
807
807
  resolveKey(options?: {
808
808
  tried?: readonly string[];
@@ -920,6 +920,40 @@ declare class CommandCodeAccountPool {
920
920
  private pick;
921
921
  }
922
922
  //#endregion
923
+ //#region src/enrollment-wire.d.ts
924
+ interface EnrollmentInput {
925
+ id: string;
926
+ pageId: string;
927
+ mode: 'browser' | 'manual';
928
+ label: string;
929
+ /** 用户未填写名称时,允许使用浏览器登录返回的账号名。 */
930
+ automaticName: boolean;
931
+ key?: string;
932
+ }
933
+ type EnrollmentPhase = 'creating' | 'waiting' | 'writing' | 'naming' | 'finished' | 'cancelling' | 'cancelled' | 'failed' | 'cleanup-needed';
934
+ interface EnrollmentState {
935
+ id: string;
936
+ ref: string;
937
+ phase: EnrollmentPhase;
938
+ message: string;
939
+ authUrl?: string;
940
+ suggestedName?: string;
941
+ }
942
+ /** 配置中的恢复日志:先登记,再写凭据;只保留引用与阶段,不保留密钥或连接身份。 */
943
+ interface EnrollmentRecord {
944
+ id: string;
945
+ ref: string;
946
+ phase: 'pending' | 'naming' | 'cleanup';
947
+ label: string;
948
+ }
949
+ interface EnrollmentPage {
950
+ pageId: string;
951
+ }
952
+ interface EnrollmentAction extends EnrollmentPage {
953
+ id: string;
954
+ name?: string;
955
+ }
956
+ //#endregion
923
957
  //#region src/plan-tiers.d.ts
924
958
  declare const PLAN_LABELS: Readonly<Record<string, string>>;
925
959
  declare const PLAN_ORDER: Readonly<Record<string, number>>;
@@ -944,14 +978,14 @@ declare const KNOWN_EFFORTS: Readonly<Record<string, readonly string[]>>;
944
978
  declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
945
979
  /**
946
980
  * Models WITHOUT a zero-data-retention upstream, per the official CLI's own
947
- * registry (`command-code@1.73.0` `dist/cli.mjs`): `modelSupportsZdr(id)` is
981
+ * registry (`command-code@1.74.1` `dist/cli.mjs`): `modelSupportsZdr(id)` is
948
982
  * exactly `!nonZdrSet.has(canonicalize(id))`, and `knownModelSupportsZdr`
949
983
  * carries the same membership in the sibling route table — the UNION of both
950
- * is this set. Reading only the sibling route table would drop `meituan/
951
- * LongCat-2.0`, which appears in `modelSupportsZdr` alone (re-read from the
952
- * 1.66.0 / 1.67.0 / 1.68.0 artifacts on 2026-09-29: the two stealth-preview
953
- * rows sit in BOTH sets, so LongCat is the only divergence). The official docs (commandcode.ai/docs/resources/
954
- * zdr) put it in prose — "99% of our models have ZDR-capable upstreams … only
984
+ * is this set. Reading only the sibling route table dropped
985
+ * `meituan/LongCat-2.0`, which sat in `modelSupportsZdr` alone through
986
+ * 1.73.0; the 1.74.1 table lists it in NEITHER set, so as of this snapshot it
987
+ * is no longer excluded. The official docs (commandcode.ai/docs/resources/
988
+ * zdr) put coverage in prose — "99% of our models have ZDR-capable upstreams … only
955
989
  * a small handful of models are affected" — so the CLI's exclusion list is
956
990
  * the only per-model evidence there is; a ZDR request naming one of these
957
991
  * fails with HTTP 422 `cmd_zdr_no_providers` instead of routing through a
@@ -974,8 +1008,14 @@ declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
974
1008
  * is the snapshot of the exclusion set and nothing more. It is a rare change:
975
1009
  * 20 members held across 1.62.0 → 1.64.0, 1.65.0 and 1.66.0 each added exactly
976
1010
  * one (the two stealth-preview models below), 1.67.0 added one
977
- * (`deepseek/deepseek-v4.1-flash-fast`), and 1.68.0 changed nothing — 23
978
- * members as of 2026-09-29.
1011
+ * (`deepseek/deepseek-v4.1-flash-fast`), 1.68.0 changed nothing, and 1.74.1
1012
+ * removed one (`meituan/LongCat-2.0`) — 22 members as of 2026-10-03.
1013
+ *
1014
+ * `stealth/pixel-canary` is the one member whose model itself is retired (see
1015
+ * `KNOWN_EFFORTS`): the CLI keeps naming it in the ZDR anchors even though it
1016
+ * hides the row and dropped it from the catalog, so it stays listed here rather
1017
+ * than being pruned with the rest of the tables — `supportsZeroDataRetention`
1018
+ * stays truthful for any id a stale session still names.
979
1019
  */
980
1020
  declare const KNOWN_NON_ZDR_MODELS: ReadonlySet<string>;
981
1021
  /**
@@ -1395,98 +1435,6 @@ declare function parseLoginStatus(value: unknown): CommandCodeLoginStatus;
1395
1435
  /** The strict result codec shared by all three login endpoints. */
1396
1436
  declare const loginStatusSchema: TypertSchema<CommandCodeLoginStatus>;
1397
1437
  //#endregion
1398
- //#region src/usage-remote.d.ts
1399
- /**
1400
- * The browser-login face the usage service exposes (`commandcode/login*`).
1401
- * Backed by the Host-half {@link !CommandCodeLoginFlow} when the plugin entry
1402
- * wired one; absent, `status`/`cancel` degrade to the idle status and `begin`
1403
- * rejects with a plain message, so the page's manual paste path stays the
1404
- * fallback instead of hanging.
1405
- */
1406
- interface LoginFlowFacade {
1407
- /** Start (or rejoin) an attempt; rejects when it cannot start at all. */
1408
- begin(targetRef?: string): Promise<CommandCodeLoginStatus>;
1409
- /** The current attempt's status. */
1410
- status(): CommandCodeLoginStatus;
1411
- /** Cancel a waiting attempt. */
1412
- cancel(): void;
1413
- }
1414
- /** Everything the usage service needs beyond its Cordis context. */
1415
- interface CommandCodeUsageDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {
1416
- /** The registered adapter (for getUsage). */
1417
- adapter: CommandCodeAdapter<C>;
1418
- /**
1419
- * Multi-account report source (wired by the plugin entry). Absent in
1420
- * programmatic setups, the service falls back to a single default-account
1421
- * entry around `adapter.getUsage()`.
1422
- */
1423
- reports?: () => Promise<CommandCodeAccountsReport>;
1424
- /**
1425
- * Model-catalog source for the settings page's model editors. Absent, the
1426
- * `models` endpoint answers an empty list — the page's editors degrade to
1427
- * the empty state.
1428
- */
1429
- listModels?: () => Promise<CommandCodeCatalog>;
1430
- /**
1431
- * Price-table source for the composer's session-cost figure. Defaults to the
1432
- * vendored snapshot, so the endpoint can never silently serve an empty table
1433
- * — an unpriced cost is the failure this feature is meant to remove. Override
1434
- * only to stub it in a test.
1435
- */
1436
- prices?: () => CommandCodePriceTable;
1437
- /** The browser-login flow (wired by the plugin entry); see {@link LoginFlowFacade}. */
1438
- login?: LoginFlowFacade;
1439
- }
1440
- /**
1441
- * The Remote receiver: a Cordis service the Gateway resolves by key
1442
- * (`commandcodeUsage`) and binds to the wire namespace (`commandcode`). The
1443
- * base class stamps the `typertRemote` binding the Gateway validates on every
1444
- * dispatch; no decorators are needed because the descriptor is registered
1445
- * explicitly (strict path) rather than discovered from source markers.
1446
- */
1447
- declare class CommandCodeUsageService<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> extends TypertRemoteService {
1448
- private readonly deps;
1449
- constructor(ctx: Context, deps: CommandCodeUsageDeps<C>);
1450
- /**
1451
- * Account, usage, and credit state for the settings page's account card.
1452
- * Degrades per endpoint like the `/commandcode` command (failures land in
1453
- * `report.failures`); throws `MISSING_CREDENTIAL` when no key resolves, which
1454
- * the Gateway folds into the failure branch the page renders as a hint.
1455
- */
1456
- report(): Promise<CommandCodeAccountsReport>;
1457
- /**
1458
- * The full model catalog for the settings page's model editors. The browser
1459
- * never calls the Command Code API directly — the Host serves the catalog
1460
- * (already fetched/cached by the adapter) so models can be picked from the
1461
- * live list instead of typed by hand.
1462
- */
1463
- models(): Promise<CommandCodeCatalog>;
1464
- /**
1465
- * The model price table the composer prices an in-progress session with.
1466
- * Static vendored data, served Host-side so the browser bundle never carries
1467
- * a copy that could drift from the snapshot, and so a price update reaches an
1468
- * open page without a rebuild.
1469
- */
1470
- prices(): Promise<CommandCodePriceTable>;
1471
- /**
1472
- * Start (or rejoin) a browser-login attempt and return its fresh status —
1473
- * `waiting` carrying the Studio URL. Rejects when the flow cannot start
1474
- * (no free loopback port, disposed plugin).
1475
- */
1476
- loginBegin(targetRef?: string): Promise<CommandCodeLoginStatus>;
1477
- /** Poll a login attempt's status. */
1478
- loginStatus(): Promise<CommandCodeLoginStatus>;
1479
- /** Cancel a waiting attempt; returns the post-cancel status. */
1480
- loginCancel(): Promise<CommandCodeLoginStatus>;
1481
- private requireLogin;
1482
- }
1483
- /**
1484
- * Provide the usage service and register its Remote descriptor. The registry
1485
- * contribution is tied to this fiber's lifetime: the registry's own
1486
- * `register()` effect would otherwise outlive the plugin.
1487
- */
1488
- declare function applyUsageRemote<C extends CommandCodeConnectionOptions>(ctx: Context, deps: CommandCodeUsageDeps<C>): void;
1489
- //#endregion
1490
1438
  //#region src/login.d.ts
1491
1439
  /** 登录总期限包含浏览器回调、密钥验证与存储,不能在收到回调后撤掉保护。 */
1492
1440
  declare const LOGIN_TIMEOUT_MS = 120000;
@@ -1589,6 +1537,8 @@ declare class CommandCodeLoginFlow {
1589
1537
  private targetRef;
1590
1538
  /** A `cancel()` that arrived while a start was still binding (see {@link CommandCodeLoginFlow.cancel}). */
1591
1539
  private cancelPending;
1540
+ /** 取消只能阻止尚未发出的写入;补偿者须等这些实际存储任务结束。 */
1541
+ private readonly pendingStores;
1592
1542
  private disposed;
1593
1543
  constructor(deps: CommandCodeLoginFlowDeps);
1594
1544
  /** Subscribe to state transitions. @returns the disposer. */
@@ -1614,6 +1564,8 @@ declare class CommandCodeLoginFlow {
1614
1564
  * the flag once the bind settles and retires it.
1615
1565
  */
1616
1566
  cancel(): void;
1567
+ /** 结束验证/监听并等待已开始的写入,供账号开通过程安全补偿。 */
1568
+ cancelAndDrain(): Promise<void>;
1617
1569
  /** Stop everything; a waiting attempt ends cancelled. Idempotent. */
1618
1570
  dispose(): void;
1619
1571
  private readApiBase;
@@ -1636,9 +1588,10 @@ declare class CommandCodeLoginFlow {
1636
1588
  * Every step re-checks {@link ownsAttempt} first: the whoami round-trip and
1637
1589
  * the credential write are awaits, and the user may cancel (or start another
1638
1590
  * attempt) while one is in flight. A completion that no longer owns the
1639
- * attempt must not write the key or publish a status — otherwise cancel
1640
- * would report "cancelled" while the credential landed anyway, and the page
1641
- * would silently flip to success.
1591
+ * attempt must not START a key write or publish a status. A write already
1592
+ * handed to storage is tracked separately: cancelAndDrain() lets the owning
1593
+ * enrollment wait for it before compensation, without pretending it can be
1594
+ * aborted halfway through.
1642
1595
  */
1643
1596
  private complete;
1644
1597
  /** Whether one attempt still owns the status face (not cancelled, replaced, or disposed). */
@@ -1652,6 +1605,189 @@ declare class CommandCodeLoginFlow {
1652
1605
  private teardown;
1653
1606
  }
1654
1607
  //#endregion
1608
+ //#region src/enrollment.d.ts
1609
+ interface Account {
1610
+ label?: string;
1611
+ apiKeyEnv?: string;
1612
+ apiKey?: string;
1613
+ }
1614
+ interface EnrollmentConfig {
1615
+ apiKeyEnv?: string;
1616
+ accounts?: Account[];
1617
+ accountEnrollmentTasks?: EnrollmentRecord[];
1618
+ credentialCleanupRefs?: string[];
1619
+ modelAccountRules?: {
1620
+ models: string[];
1621
+ account: string;
1622
+ }[];
1623
+ activeAccount?: string;
1624
+ }
1625
+ interface EnrollmentSettings {
1626
+ writable: boolean;
1627
+ read(): {
1628
+ value: EnrollmentConfig;
1629
+ revision: number;
1630
+ base?: EnrollmentConfig;
1631
+ };
1632
+ mutate(ops: {
1633
+ op: 'set';
1634
+ path: string[];
1635
+ value: unknown;
1636
+ }[], revision: number): Promise<void>;
1637
+ }
1638
+ interface EnrollmentLogin {
1639
+ begin(ref?: string): Promise<CommandCodeLoginStatus>;
1640
+ status(): CommandCodeLoginStatus;
1641
+ onChange(listener: () => void): () => void;
1642
+ cancelAndDrain(): Promise<void>;
1643
+ dispose(): void;
1644
+ }
1645
+ interface EnrollmentDeps {
1646
+ settings(): EnrollmentSettings | undefined;
1647
+ describe(ref: string): Promise<{
1648
+ configured: boolean;
1649
+ writable: boolean;
1650
+ }>;
1651
+ set(ref: string, key: string): Promise<void>;
1652
+ unset(ref: string): Promise<void>;
1653
+ login(store: (credentials: CommandCodeLoginCredentials) => Promise<void>): EnrollmentLogin;
1654
+ }
1655
+ declare class AccountEnrollmentManager {
1656
+ private readonly deps;
1657
+ private readonly tasks;
1658
+ private writes;
1659
+ private closed;
1660
+ private readonly pages;
1661
+ constructor(deps: EnrollmentDeps);
1662
+ /** 每页独立的持续调用才代表页面生命期;宿主的 operator Peer 由所有浏览器共享。 */
1663
+ watch(owner: string): () => void;
1664
+ hasPage(owner: string): boolean;
1665
+ /** begin 不等待整个过程;客户端预先生成的 id 让早到的取消也能生效。 */
1666
+ begin(owner: string, input: EnrollmentInput): EnrollmentState;
1667
+ status(owner: string, id: string): EnrollmentState;
1668
+ pending(owner: string): EnrollmentState[];
1669
+ /** 离开已登录的待命名过程等同接受已有名称,其余阶段等待写入结束再补偿。 */
1670
+ cancel(owner: string, id: string): Promise<EnrollmentState>;
1671
+ disconnect(owner: string): void;
1672
+ dispose(): void;
1673
+ name(owner: string, id: string, name?: string): Promise<EnrollmentState>;
1674
+ retry(owner: string, id: string, name?: string): Promise<EnrollmentState>;
1675
+ private run;
1676
+ private store;
1677
+ private cleanup;
1678
+ private settings;
1679
+ private records;
1680
+ private change;
1681
+ private owned;
1682
+ private publish;
1683
+ /** 只淘汰已完成且没有恢复日志的旧状态,活跃过程和未收尾事实始终保留。 */
1684
+ private prune;
1685
+ private recovered;
1686
+ }
1687
+ //#endregion
1688
+ //#region src/usage-remote.d.ts
1689
+ /**
1690
+ * The browser-login face the usage service exposes (`commandcode/login*`).
1691
+ * Backed by the Host-half {@link !CommandCodeLoginFlow} when the plugin entry
1692
+ * wired one; absent, `status`/`cancel` degrade to the idle status and `begin`
1693
+ * rejects with a plain message, so the page's manual paste path stays the
1694
+ * fallback instead of hanging.
1695
+ */
1696
+ interface LoginFlowFacade {
1697
+ /** Start (or rejoin) an attempt; rejects when it cannot start at all. */
1698
+ begin(targetRef?: string): Promise<CommandCodeLoginStatus>;
1699
+ /** The current attempt's status. */
1700
+ status(): CommandCodeLoginStatus;
1701
+ /** Cancel a waiting attempt. */
1702
+ cancel(): void;
1703
+ }
1704
+ /** Everything the usage service needs beyond its Cordis context. */
1705
+ interface CommandCodeUsageDeps<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> {
1706
+ /** The registered adapter (for getUsage). */
1707
+ adapter: CommandCodeAdapter<C>;
1708
+ /**
1709
+ * Multi-account report source (wired by the plugin entry). Absent in
1710
+ * programmatic setups, the service falls back to a single default-account
1711
+ * entry around `adapter.getUsage()`.
1712
+ */
1713
+ reports?: () => Promise<CommandCodeAccountsReport>;
1714
+ /**
1715
+ * Model-catalog source for the settings page's model editors. Absent, the
1716
+ * `models` endpoint answers an empty list — the page's editors degrade to
1717
+ * the empty state.
1718
+ */
1719
+ listModels?: () => Promise<CommandCodeCatalog>;
1720
+ /**
1721
+ * Price-table source for the composer's session-cost figure. Defaults to the
1722
+ * vendored snapshot, so the endpoint can never silently serve an empty table
1723
+ * — an unpriced cost is the failure this feature is meant to remove. Override
1724
+ * only to stub it in a test.
1725
+ */
1726
+ prices?: () => CommandCodePriceTable;
1727
+ /** The browser-login flow (wired by the plugin entry); see {@link LoginFlowFacade}. */
1728
+ login?: LoginFlowFacade;
1729
+ enrollment?: AccountEnrollmentManager;
1730
+ }
1731
+ /**
1732
+ * The Remote receiver: a Cordis service the Gateway resolves by key
1733
+ * (`commandcodeUsage`) and binds to the wire namespace (`commandcode`). The
1734
+ * base class stamps the `typertRemote` binding the Gateway validates on every
1735
+ * dispatch; no decorators are needed because the descriptor is registered
1736
+ * explicitly (strict path) rather than discovered from source markers.
1737
+ */
1738
+ declare class CommandCodeUsageService<C extends CommandCodeConnectionOptions = CommandCodeConnectionOptions> extends TypertRemoteService {
1739
+ private readonly deps;
1740
+ constructor(ctx: Context, deps: CommandCodeUsageDeps<C>);
1741
+ /**
1742
+ * Account, usage, and credit state for the settings page's account card.
1743
+ * Degrades per endpoint like the `/commandcode` command (failures land in
1744
+ * `report.failures`); throws `MISSING_CREDENTIAL` when no key resolves, which
1745
+ * the Gateway folds into the failure branch the page renders as a hint.
1746
+ */
1747
+ report(): Promise<CommandCodeAccountsReport>;
1748
+ /**
1749
+ * The full model catalog for the settings page's model editors. The browser
1750
+ * never calls the Command Code API directly — the Host serves the catalog
1751
+ * (already fetched/cached by the adapter) so models can be picked from the
1752
+ * live list instead of typed by hand.
1753
+ */
1754
+ models(): Promise<CommandCodeCatalog>;
1755
+ /**
1756
+ * The model price table the composer prices an in-progress session with.
1757
+ * Static vendored data, served Host-side so the browser bundle never carries
1758
+ * a copy that could drift from the snapshot, and so a price update reaches an
1759
+ * open page without a rebuild.
1760
+ */
1761
+ prices(): Promise<CommandCodePriceTable>;
1762
+ /**
1763
+ * Start (or rejoin) a browser-login attempt and return its fresh status —
1764
+ * `waiting` carrying the Studio URL. Rejects when the flow cannot start
1765
+ * (no free loopback port, disposed plugin).
1766
+ */
1767
+ loginBegin(targetRef?: string): Promise<CommandCodeLoginStatus>;
1768
+ /** Poll a login attempt's status. */
1769
+ loginStatus(): Promise<CommandCodeLoginStatus>;
1770
+ /** Cancel a waiting attempt; returns the post-cancel status. */
1771
+ loginCancel(): Promise<CommandCodeLoginStatus>;
1772
+ enrollmentBegin(input: EnrollmentInput): Promise<EnrollmentState>;
1773
+ enrollmentStatus(input: EnrollmentAction): Promise<EnrollmentState>;
1774
+ enrollmentCancel(input: EnrollmentAction): Promise<EnrollmentState>;
1775
+ enrollmentName(input: EnrollmentAction): Promise<EnrollmentState>;
1776
+ enrollmentRetry(input: EnrollmentAction): Promise<EnrollmentState>;
1777
+ enrollmentPending(input: EnrollmentPage): Promise<EnrollmentState[]>;
1778
+ enrollmentWatch(input: EnrollmentPage): AsyncIterable<boolean>;
1779
+ private enrollmentOwner;
1780
+ private enrollmentPeer;
1781
+ private requireEnrollment;
1782
+ private requireLogin;
1783
+ }
1784
+ /**
1785
+ * Provide the usage service and register its Remote descriptor. The registry
1786
+ * contribution is tied to this fiber's lifetime: the registry's own
1787
+ * `register()` effect would otherwise outlive the plugin.
1788
+ */
1789
+ declare function applyUsageRemote<C extends CommandCodeConnectionOptions>(ctx: Context, deps: CommandCodeUsageDeps<C>): void;
1790
+ //#endregion
1655
1791
  //#region src/web-search.d.ts
1656
1792
  /** Stable id this provider registers under in `ctx.web`. */
1657
1793
  declare const COMMANDCODE_SEARCH_PROVIDER_ID = "commandcode";
@@ -1706,8 +1842,8 @@ declare function commandCodeSearchSelection(): CommandCodeSearchSelection;
1706
1842
  declare function applyCommandCodeSearchSelection(web: WebRuntime, state: CommandCodeSearchSelection, enable: boolean): void;
1707
1843
  /** Per-request facts the provider needs, all injected so the class stays cordis-free and testable. */
1708
1844
  interface CommandCodeSearchProviderDeps {
1709
- /** Resolve one usable Command Code key (credential seam → env → auth file), or undefined when none. */
1710
- resolveKey(): Promise<string | undefined>;
1845
+ /** 使用搜索已捕获的网关解析账号,探测不能在等待后转到另一个网关。 */
1846
+ resolveKey(apiBase?: string): Promise<string | undefined>;
1711
1847
  /** The API base host (defaults to `https://api.commandcode.ai`). */
1712
1848
  apiBase(): string;
1713
1849
  /** Injectable fetch for tests; defaults to the global fetch. */
@@ -1959,6 +2095,8 @@ interface Config {
1959
2095
  * the key and the next account's key is retried transparently.
1960
2096
  */
1961
2097
  accounts?: CommandCodeAccountConfig[];
2098
+ /** 账号开通恢复日志,只保留引用与阶段。 */
2099
+ accountEnrollmentTasks?: EnrollmentRecord[];
1962
2100
  /**
1963
2101
  * Manually selected active account: a slot id — `default`, or an extra
1964
2102
  * account's credential reference (e.g. `COMMANDCODE_API_KEY_2`). It serves
@@ -2029,6 +2167,8 @@ declare const Config: z<Config>;
2029
2167
  /** One resolution's complete request facts: connection plus credential reference. */
2030
2168
  interface ResolvedCommandCodeOptions extends CommandCodeConnectionOptions {
2031
2169
  apiKeyEnv: CredentialRef;
2170
+ /** 本次调用的账号定义,与连接从同一次易变配置读取。 */
2171
+ accountSelection?: CommandCodeAccountSelection;
2032
2172
  }
2033
2173
  /**
2034
2174
  * The one explicit resolve step from raw config to validated connection