dsh-plugin-subscription-usage 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +50 -2
  3. package/cordis.patch.yml +5 -0
  4. package/icon.svg +9 -0
  5. package/lib/auth/claude-code-creds.d.ts +23 -0
  6. package/lib/auth/claude-code-creds.js +172 -0
  7. package/lib/auth/device-flow.d.ts +55 -0
  8. package/lib/auth/device-flow.js +177 -0
  9. package/lib/auth/jwt.d.ts +10 -0
  10. package/lib/auth/jwt.js +25 -0
  11. package/lib/auth/oauth-flow.d.ts +91 -0
  12. package/lib/auth/oauth-flow.js +227 -0
  13. package/lib/auth/pkce.d.ts +31 -0
  14. package/lib/auth/pkce.js +35 -0
  15. package/lib/auth/rpc.d.ts +233 -0
  16. package/lib/auth/rpc.js +501 -0
  17. package/lib/auth/store.d.ts +198 -0
  18. package/lib/auth/store.js +473 -0
  19. package/lib/client/Footer.d.ts +3 -0
  20. package/lib/client/Footer.js +73 -0
  21. package/lib/client/Gauge.d.ts +28 -0
  22. package/lib/client/Gauge.js +139 -0
  23. package/lib/client/ImageGallery.d.ts +54 -0
  24. package/lib/client/ImageGallery.js +112 -0
  25. package/lib/client/ImageGenerateToolview.d.ts +48 -0
  26. package/lib/client/ImageGenerateToolview.js +130 -0
  27. package/lib/client/Notice.d.ts +15 -0
  28. package/lib/client/Notice.js +93 -0
  29. package/lib/client/ProviderCard.d.ts +35 -0
  30. package/lib/client/ProviderCard.js +240 -0
  31. package/lib/client/SpeedSelect.d.ts +72 -0
  32. package/lib/client/SpeedSelect.js +214 -0
  33. package/lib/client/Usage.d.ts +7 -0
  34. package/lib/client/Usage.js +83 -0
  35. package/lib/client/VideoGenerateToolview.d.ts +42 -0
  36. package/lib/client/VideoGenerateToolview.js +172 -0
  37. package/lib/client/context.d.ts +16 -0
  38. package/lib/client/context.js +11 -0
  39. package/lib/client/fast-command.d.ts +2 -0
  40. package/lib/client/fast-command.js +4 -0
  41. package/lib/client/hooks.d.ts +2 -0
  42. package/lib/client/hooks.js +10 -0
  43. package/lib/client/index.d.ts +3 -0
  44. package/lib/client/index.js +113 -0
  45. package/lib/client/locales.d.ts +139 -0
  46. package/lib/client/locales.js +267 -0
  47. package/lib/client/meter.d.ts +91 -0
  48. package/lib/client/meter.js +196 -0
  49. package/lib/client/prefs.d.ts +51 -0
  50. package/lib/client/prefs.js +71 -0
  51. package/lib/client/rpc.d.ts +161 -0
  52. package/lib/client/rpc.js +31 -0
  53. package/lib/client/store.d.ts +67 -0
  54. package/lib/client/store.js +129 -0
  55. package/lib/client/styles.d.ts +7 -0
  56. package/lib/client/styles.js +176 -0
  57. package/lib/client.js +3933 -0
  58. package/lib/client.js.map +1 -0
  59. package/lib/compat.d.ts +55 -0
  60. package/lib/compat.js +20 -0
  61. package/lib/http.d.ts +134 -0
  62. package/lib/http.js +431 -0
  63. package/lib/index.d.ts +121 -0
  64. package/lib/index.js +13399 -0
  65. package/lib/json-text.d.ts +2 -0
  66. package/lib/json-text.js +4 -0
  67. package/lib/model-defaults.d.ts +23 -0
  68. package/lib/model-defaults.js +238 -0
  69. package/lib/model-speeds.d.ts +10 -0
  70. package/lib/model-speeds.js +48 -0
  71. package/lib/models-directory.d.ts +17 -0
  72. package/lib/models-directory.js +25 -0
  73. package/lib/provider-settings.d.ts +55 -0
  74. package/lib/provider-settings.js +215 -0
  75. package/lib/providers/account-preferences.d.ts +46 -0
  76. package/lib/providers/account-preferences.js +188 -0
  77. package/lib/providers/accounts.d.ts +113 -0
  78. package/lib/providers/accounts.js +142 -0
  79. package/lib/providers/antigravity-oauth-client.d.ts +7 -0
  80. package/lib/providers/antigravity-oauth-client.js +7 -0
  81. package/lib/providers/antigravity.d.ts +119 -0
  82. package/lib/providers/antigravity.js +552 -0
  83. package/lib/providers/catalog-refresh.d.ts +4 -0
  84. package/lib/providers/catalog-refresh.js +30 -0
  85. package/lib/providers/catalog-store.d.ts +47 -0
  86. package/lib/providers/catalog-store.js +283 -0
  87. package/lib/providers/claude-cli-version.d.ts +15 -0
  88. package/lib/providers/claude-cli-version.js +24 -0
  89. package/lib/providers/claude.d.ts +212 -0
  90. package/lib/providers/claude.js +726 -0
  91. package/lib/providers/codex-client-version.d.ts +8 -0
  92. package/lib/providers/codex-client-version.js +17 -0
  93. package/lib/providers/codex-search.d.ts +29 -0
  94. package/lib/providers/codex-search.js +198 -0
  95. package/lib/providers/codex.d.ts +253 -0
  96. package/lib/providers/codex.js +930 -0
  97. package/lib/providers/common.d.ts +415 -0
  98. package/lib/providers/common.js +679 -0
  99. package/lib/providers/copilot.d.ts +365 -0
  100. package/lib/providers/copilot.js +956 -0
  101. package/lib/providers/grok.d.ts +214 -0
  102. package/lib/providers/grok.js +750 -0
  103. package/lib/providers/image-pool.d.ts +30 -0
  104. package/lib/providers/image-pool.js +89 -0
  105. package/lib/providers/npm-cli-version.d.ts +56 -0
  106. package/lib/providers/npm-cli-version.js +98 -0
  107. package/lib/providers/pool-family.d.ts +56 -0
  108. package/lib/providers/pool-family.js +45 -0
  109. package/lib/providers/pool-health.d.ts +91 -0
  110. package/lib/providers/pool-health.js +164 -0
  111. package/lib/providers/pool-usage.d.ts +106 -0
  112. package/lib/providers/pool-usage.js +302 -0
  113. package/lib/providers/pool.d.ts +115 -0
  114. package/lib/providers/pool.js +461 -0
  115. package/lib/providers/rate-limit.d.ts +192 -0
  116. package/lib/providers/rate-limit.js +338 -0
  117. package/lib/providers/replay.d.ts +4 -0
  118. package/lib/providers/replay.js +37 -0
  119. package/lib/providers/reset-redemption.d.ts +20 -0
  120. package/lib/providers/reset-redemption.js +70 -0
  121. package/lib/tools/image-generate.d.ts +128 -0
  122. package/lib/tools/image-generate.js +438 -0
  123. package/lib/tools/registration.d.ts +16 -0
  124. package/lib/tools/registration.js +25 -0
  125. package/lib/tools/usage.d.ts +62 -0
  126. package/lib/tools/usage.js +89 -0
  127. package/lib/tools/video-generate.d.ts +89 -0
  128. package/lib/tools/video-generate.js +257 -0
  129. package/lib/tools/x-search.d.ts +58 -0
  130. package/lib/tools/x-search.js +197 -0
  131. package/lib/translate/anthropic.d.ts +180 -0
  132. package/lib/translate/anthropic.js +534 -0
  133. package/lib/translate/antigravity-images.d.ts +11 -0
  134. package/lib/translate/antigravity-images.js +62 -0
  135. package/lib/translate/antigravity-schema.d.ts +4 -0
  136. package/lib/translate/antigravity-schema.js +145 -0
  137. package/lib/translate/antigravity-thinking.d.ts +5 -0
  138. package/lib/translate/antigravity-thinking.js +44 -0
  139. package/lib/translate/antigravity.d.ts +120 -0
  140. package/lib/translate/antigravity.js +412 -0
  141. package/lib/translate/chat-completions.d.ts +120 -0
  142. package/lib/translate/chat-completions.js +382 -0
  143. package/lib/translate/resolved.d.ts +98 -0
  144. package/lib/translate/resolved.js +186 -0
  145. package/lib/translate/responses.d.ts +192 -0
  146. package/lib/translate/responses.js +443 -0
  147. package/lib/translate/sse.d.ts +21 -0
  148. package/lib/translate/sse.js +62 -0
  149. package/lib/usage/headers.d.ts +21 -0
  150. package/lib/usage/headers.js +64 -0
  151. package/lib/usage/pace.d.ts +53 -0
  152. package/lib/usage/pace.js +48 -0
  153. package/lib/usage/service.d.ts +64 -0
  154. package/lib/usage/service.js +104 -0
  155. package/lib/usage-providers/common.d.ts +23 -0
  156. package/lib/usage-providers/common.js +28 -0
  157. package/lib/usage-providers/index.d.ts +42 -0
  158. package/lib/usage-providers/index.js +71 -0
  159. package/lib/usage-providers/keys.d.ts +11 -0
  160. package/lib/usage-providers/keys.js +52 -0
  161. package/lib/usage-providers/kimi.d.ts +4 -0
  162. package/lib/usage-providers/kimi.js +52 -0
  163. package/lib/usage-providers/ollama-cloud.d.ts +4 -0
  164. package/lib/usage-providers/ollama-cloud.js +42 -0
  165. package/lib/usage-providers/zai.d.ts +5 -0
  166. package/lib/usage-providers/zai.js +53 -0
  167. package/locale/en.json +6 -0
  168. package/locale/zh.json +6 -0
  169. package/package.json +115 -4
@@ -0,0 +1,46 @@
1
+ import { LlmAdapter } from '@deepseek-ai/dsh-llm';
2
+ import type { GenerateOptions, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, PreparedAdapterCall, StreamChunk } from '@deepseek-ai/dsh-llm';
3
+ import type { ProviderId } from '../auth/store.js';
4
+ import type { ProviderSettingsStore, AccountPreferences } from '../provider-settings.js';
5
+ import type { AccountAwareAdapter } from './accounts.js';
6
+ import type { PoolAdapter } from './pool.js';
7
+ /** Reserved namespace, recognized even when malformed or no longer enabled. */
8
+ export declare const ACCOUNT_MODEL_PREFIX = "~account:";
9
+ export declare function accountModelId(account: string, model: string): string;
10
+ export declare function parseAccountModelId(id: string): {
11
+ account: string;
12
+ model: string;
13
+ } | undefined;
14
+ export declare function accountAllowsPool(preferences: AccountPreferences | undefined, model: string): boolean;
15
+ interface Options {
16
+ provider: ProviderId;
17
+ adapter: AccountAwareAdapter;
18
+ settings: ProviderSettingsStore;
19
+ accounts: () => Promise<readonly {
20
+ key: string;
21
+ label: string;
22
+ }[]>;
23
+ pool: () => PoolAdapter | undefined;
24
+ /** Per-account discovery bound (defaults to {@link DISCOVERY_TIMEOUT_MS}; tests shorten it). */
25
+ discoveryTimeoutMs?: number;
26
+ }
27
+ /** Keeps the registered route separate from raw adapters and pool member seams. */
28
+ export declare class AccountPreferencesAdapter extends LlmAdapter {
29
+ private readonly options;
30
+ constructor(options: Options);
31
+ providerInfo(provider: string): LlmProviderInfo;
32
+ providerRetryPolicy(provider: string): import("@deepseek-ai/dsh-llm").ResolvedRetryPolicy | undefined;
33
+ private preference;
34
+ private models;
35
+ private requireAccount;
36
+ private fallback;
37
+ /** Pool-only facade: explicit families/tiers must obey the same policy as auto pools. */
38
+ poolMember(): AccountAwareAdapter;
39
+ listModels(provider: string): Promise<readonly LlmModelInfo[]>;
40
+ resolveModel(provider: string, id: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
41
+ prepareCall(provider: string, id: string, signal?: AbortSignal): Promise<PreparedAdapterCall>;
42
+ /** Presentation only: routing still uses the original model id. */
43
+ private present;
44
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
45
+ }
46
+ export {};
@@ -0,0 +1,188 @@
1
+ import { LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm';
2
+ import { DISCOVERY_TIMEOUT_MS, withAbortSignal, withTimeout } from './common.js';
3
+ import { streamAccountWithReplay } from './replay.js';
4
+ /** Reserved namespace, recognized even when malformed or no longer enabled. */
5
+ export const ACCOUNT_MODEL_PREFIX = '~account:';
6
+ export function accountModelId(account, model) {
7
+ return `${ACCOUNT_MODEL_PREFIX}${encodeURIComponent(account)}:${encodeURIComponent(model)}`;
8
+ }
9
+ export function parseAccountModelId(id) {
10
+ if (!id.startsWith(ACCOUNT_MODEL_PREFIX))
11
+ return undefined;
12
+ const parts = id.slice(ACCOUNT_MODEL_PREFIX.length).split(':');
13
+ try {
14
+ if (parts.length !== 2)
15
+ throw new Error();
16
+ const account = decodeURIComponent(parts[0]);
17
+ const model = decodeURIComponent(parts[1]);
18
+ if (!account || !model || accountModelId(account, model) !== id)
19
+ throw new Error();
20
+ return { account, model };
21
+ }
22
+ catch {
23
+ throw new LlmError('Invalid independent account model id', 'NO_ADAPTER');
24
+ }
25
+ }
26
+ export function accountAllowsPool(preferences, model) {
27
+ return preferences?.poolEnabled !== false && (preferences?.poolModels?.includes(model) ?? true);
28
+ }
29
+ /** Keeps the registered route separate from raw adapters and pool member seams. */
30
+ export class AccountPreferencesAdapter extends LlmAdapter {
31
+ options;
32
+ constructor(options) {
33
+ super();
34
+ this.options = options;
35
+ }
36
+ providerInfo(provider) {
37
+ const info = this.options.adapter.providerInfo(provider);
38
+ // Native model groups read provider metadata, separately from model names.
39
+ return { ...info, name: this.options.settings.get(this.options.provider).displayName ?? info.name };
40
+ }
41
+ providerRetryPolicy(provider) { return this.options.adapter.providerRetryPolicy(provider); }
42
+ preference(account) {
43
+ const accounts = this.options.settings.get(this.options.provider).accounts;
44
+ return accounts && Object.hasOwn(accounts, account) ? accounts[account] : undefined;
45
+ }
46
+ async models(account, signal) {
47
+ const models = await withAbortSignal(() => withTimeout(signal => this.options.adapter.listOwnModels(this.options.provider, account, signal, true), this.options.discoveryTimeoutMs ?? DISCOVERY_TIMEOUT_MS), signal);
48
+ if (models !== undefined)
49
+ return models;
50
+ // Discovery timed out: the catalog this account listed last time beats
51
+ // "no models", which would fail the turn with "No eligible account".
52
+ return await withAbortSignal(async () => this.options.adapter.lastKnownOwnModels?.(this.options.provider, account), signal) ?? [];
53
+ }
54
+ async requireAccount(account, model, independent, signal) {
55
+ if (!(await withAbortSignal(this.options.accounts, signal)).some(entry => entry.key === account)
56
+ || (independent ? this.preference(account)?.independentEntry !== true : !accountAllowsPool(this.preference(account), model))
57
+ || !(await this.models(account, signal)).some(entry => entry.id === model)) {
58
+ throw new LlmError(`Account route unavailable: ${this.options.provider}/${account}/${model}`, 'NO_ADAPTER');
59
+ }
60
+ }
61
+ async fallback(model, signal) {
62
+ for (const { key } of await withAbortSignal(this.options.accounts, signal)) {
63
+ if (!accountAllowsPool(this.preference(key), model))
64
+ continue;
65
+ try {
66
+ await this.requireAccount(key, model, false, signal);
67
+ return key;
68
+ }
69
+ catch {
70
+ signal?.throwIfAborted();
71
+ }
72
+ }
73
+ throw new LlmError(`No eligible account for ${this.options.provider}/${model}`, 'NO_ADAPTER');
74
+ }
75
+ /** Pool-only facade: explicit families/tiers must obey the same policy as auto pools. */
76
+ poolMember() {
77
+ const raw = this.options.adapter;
78
+ const keyFor = async (account, model, signal) => {
79
+ if (model.startsWith(ACCOUNT_MODEL_PREFIX))
80
+ throw new LlmError('Independent entries cannot be pool members', 'NO_ADAPTER');
81
+ const key = account ?? (await withAbortSignal(this.options.accounts, signal))[0]?.key;
82
+ if (!key)
83
+ throw new LlmError('No account available', 'NO_ADAPTER');
84
+ await this.requireAccount(key, model, false, signal);
85
+ return key;
86
+ };
87
+ return new Proxy(raw, { get: (target, property) => {
88
+ if (property === 'streamAccount')
89
+ return async function* (options, account) {
90
+ let key;
91
+ try {
92
+ key = await keyFor(account, options.model, options.signal);
93
+ }
94
+ catch (cause) {
95
+ options.signal?.throwIfAborted();
96
+ // Skip policy-excluded members without poisoning account health. The
97
+ // pool treats TRANSPORT as switch-without-cooldown; raw is never called.
98
+ throw new LlmError('Pool member unavailable under account preferences', 'TRANSPORT', { cause });
99
+ }
100
+ yield* raw.streamAccount(options, key);
101
+ };
102
+ if (property === 'resolveOwnModel')
103
+ return async (provider, model, account) => raw.resolveOwnModel(provider, model, await keyFor(account, model));
104
+ const value = Reflect.get(target, property);
105
+ return typeof value === 'function' ? value.bind(target) : value;
106
+ } });
107
+ }
108
+ async listModels(provider) {
109
+ const result = new Map();
110
+ const catalogs = await Promise.all((await this.options.accounts()).map(async (account) => ({ ...account, models: await this.models(account.key).catch(() => []) })));
111
+ for (const { key, label, models } of catalogs) {
112
+ const preferences = this.preference(key);
113
+ for (const model of models) {
114
+ if (model.id.startsWith(ACCOUNT_MODEL_PREFIX))
115
+ continue;
116
+ if (accountAllowsPool(preferences, model.id) && this.options.settings.visible(this.options.provider, model.id) && !result.has(model.id))
117
+ result.set(model.id, model);
118
+ if (preferences?.independentEntry === true && this.options.settings.visible(this.options.provider, model.id)) {
119
+ const id = accountModelId(key, model.id);
120
+ result.set(id, { ...model, id, name: `${preferences.alias || label} · ${this.options.settings.modelName(this.options.provider, model.id, model.name)}` });
121
+ }
122
+ }
123
+ }
124
+ for (const model of await this.options.pool()?.modelsForProvider(this.options.provider) ?? []) {
125
+ if (model.id.startsWith(ACCOUNT_MODEL_PREFIX) || !this.options.settings.visible(this.options.provider, model.id))
126
+ continue;
127
+ try {
128
+ await this.options.pool().resolveModel(provider, model.id);
129
+ result.set(model.id, model);
130
+ }
131
+ catch { /* excluded tier */ }
132
+ }
133
+ const priority = (model) => model.priority ?? Number.MAX_SAFE_INTEGER;
134
+ return [...result.values()].map(model => ({ ...model,
135
+ name: this.options.settings.modelName(this.options.provider, model.id, model.name),
136
+ })).sort((left, right) => priority(left) - priority(right));
137
+ }
138
+ async resolveModel(provider, id, signal) {
139
+ return (await this.prepareCall(provider, id, signal)).model;
140
+ }
141
+ async prepareCall(provider, id, signal) {
142
+ signal?.throwIfAborted();
143
+ const independent = parseAccountModelId(id);
144
+ const pool = this.options.pool();
145
+ if (!independent && pool && await withAbortSignal(() => pool.owns(this.options.provider, id), signal)) {
146
+ const prepared = await pool.prepareCall(provider, id, signal);
147
+ return { ...prepared, model: this.present(id, prepared.model) };
148
+ }
149
+ const account = independent?.account ?? await this.fallback(id, signal);
150
+ const model = independent?.model ?? id;
151
+ if (independent)
152
+ await this.requireAccount(account, model, true, signal);
153
+ const info = await withAbortSignal(() => this.options.adapter.resolveOwnModel(provider, model, account), signal);
154
+ const label = independent ? (await withAbortSignal(this.options.accounts, signal)).find(entry => entry.key === account)?.label ?? account : undefined;
155
+ const owner = this;
156
+ const named = this.present(model, info);
157
+ return {
158
+ model: independent ? { ...named, id, name: `${this.preference(account)?.alias || label} · ${named.name}` } : named,
159
+ async *stream(options) {
160
+ // Keep the capability-bearing account; revoked permission must fail instead of rerouting.
161
+ await owner.requireAccount(account, model, independent !== undefined, options.signal);
162
+ options.signal?.throwIfAborted();
163
+ yield* streamAccountWithReplay(owner.options.adapter, options, account, model);
164
+ },
165
+ };
166
+ }
167
+ /** Presentation only: routing still uses the original model id. */
168
+ present(id, info) {
169
+ return { ...info, name: this.options.settings.modelName(this.options.provider, id, info.name) };
170
+ }
171
+ async *stream(options) {
172
+ const independent = parseAccountModelId(options.model);
173
+ if (independent) {
174
+ await this.requireAccount(independent.account, independent.model, true, options.signal);
175
+ options.signal?.throwIfAborted();
176
+ yield* streamAccountWithReplay(this.options.adapter, options, independent.account, independent.model);
177
+ return;
178
+ }
179
+ const pool = this.options.pool();
180
+ if (pool && await withAbortSignal(() => pool.owns(this.options.provider, options.model), options.signal)) {
181
+ yield* pool.stream(options);
182
+ return;
183
+ }
184
+ const account = await this.fallback(options.model, options.signal);
185
+ options.signal?.throwIfAborted();
186
+ yield* streamAccountWithReplay(this.options.adapter, options, account);
187
+ }
188
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Multi-account token plumbing: one {@link AccountTokenManager} per provider
3
+ * owns a lazily-built {@link TokenManager} per account, so refresh coalescing
4
+ * (`inflight`) and permanent-failure removal stay scoped to ONE account —
5
+ * a revoked account deletes itself without touching its siblings.
6
+ *
7
+ * {@link AccountAwareAdapter} is the internal interface the pool uses to
8
+ * stream through a specific account. A catalog model listed by several
9
+ * accounts failovers; one listed by a single account is pinned to it.
10
+ */
11
+ import { LlmAdapter } from '@deepseek-ai/dsh-llm';
12
+ import type { GenerateOptions, LlmModelInfo, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm';
13
+ import { TokenManager } from './common.js';
14
+ import type { TokenManagerOptions } from './common.js';
15
+ import type { AccountEntry, ProviderId } from '../auth/store.js';
16
+ export { DISCOVERY_TIMEOUT_MS } from './common.js';
17
+ /** Minimal session shape the token managers need (mirrors common.ts). */
18
+ interface TimedSession {
19
+ accessToken: string;
20
+ refreshToken: string;
21
+ expiresAt: number;
22
+ }
23
+ /** An adapter that can stream through a named account (the pool's seam). */
24
+ export interface AccountAwareAdapter extends LlmAdapter {
25
+ /** Stream using the given account's credentials instead of the default. */
26
+ streamAccount(options: GenerateOptions, account: string): AsyncIterable<StreamChunk>;
27
+ /**
28
+ * The provider's own catalog: one account when `account` is set, otherwise
29
+ * the union of every logged-in account (default first; later duplicates
30
+ * dropped). The picker uses the union; pool assembly lists each account.
31
+ * `preferCached` serves saved metadata without starting an interactive refresh.
32
+ */
33
+ listOwnModels(provider: string, account?: string, signal?: AbortSignal, preferCached?: boolean): Promise<readonly LlmModelInfo[]>;
34
+ /**
35
+ * The last catalog one account successfully listed, with no network.
36
+ * Routing falls back to it when a live `listOwnModels` times out.
37
+ */
38
+ lastKnownOwnModels?(provider: string, account: string): Promise<readonly LlmModelInfo[] | undefined>;
39
+ /**
40
+ * Capability resolution of the provider's OWN models, bypassing the pool
41
+ * delegation. The pool resolves its members through this — an account pool
42
+ * reuses the catalog wire id (e.g. `gpt-5.4`), so resolveModel would
43
+ * otherwise bounce straight back into the pool forever. The optional account
44
+ * lets adapters with account-specific limits resolve that member conservatively.
45
+ */
46
+ resolveOwnModel(provider: string, model: string, account?: string): Promise<LlmResolvedModelInfo>;
47
+ /** Drop cached catalogs: one account, or every account when omitted (login/logout). */
48
+ clearAccountCatalog(account?: string): void;
49
+ }
50
+ /** Options for {@link unionAccountCatalogs}. */
51
+ export interface UnionAccountCatalogsOptions {
52
+ /** Per-account bound; a hang sits that account out instead of blocking the picker. */
53
+ timeoutMs?: number;
54
+ /** Caller cancellation; aborting drops the whole union. */
55
+ signal?: AbortSignal;
56
+ }
57
+ /**
58
+ * Merge per-account catalogs, keeping the first occurrence of each model id.
59
+ * Rows that carry a numeric `priority` (Codex discovery) are then ordered by
60
+ * it so a model only the second account lists — e.g. `gpt-5.6-sol` — still
61
+ * sits with its generation instead of being appended after the default
62
+ * account's older ids.
63
+ */
64
+ export declare function unionAccountCatalogs(accounts: readonly string[], listOne: (account: string, signal?: AbortSignal) => Promise<readonly LlmModelInfo[]>, options?: UnionAccountCatalogsOptions): Promise<LlmModelInfo[]>;
65
+ /** Store I/O behind {@link AccountTokenManager} (injectable for tests). */
66
+ export interface AccountStoreIo<S> {
67
+ list(): Promise<AccountEntry<S>[]>;
68
+ get(account?: string): Promise<S | undefined>;
69
+ save(account: string, session: S): Promise<void>;
70
+ remove(account: string): Promise<void>;
71
+ /** Resolve a legacy alias to the canonical account key. */
72
+ resolve?(account: string): Promise<string>;
73
+ }
74
+ export interface AccountTokenManagerOptions<S extends TimedSession> {
75
+ provider: ProviderId;
76
+ /** Human-readable provider name for error messages. */
77
+ displayName: string;
78
+ /** Provider hooks shared by every account (load/save/remove are bound per account). */
79
+ makeOptions: (account: string) => Omit<TokenManagerOptions<S>, 'load' | 'save' | 'remove' | 'onRemoved' | 'displayName'>;
80
+ /** Called after a permanent refresh failure deleted one account's session. */
81
+ onAccountRemoved?: (account: string) => void;
82
+ /** Store backend; defaults to the durable auth store. */
83
+ io?: AccountStoreIo<S>;
84
+ }
85
+ export declare class AccountTokenManager<S extends TimedSession> {
86
+ private readonly options;
87
+ private readonly managers;
88
+ private readonly io;
89
+ constructor(options: AccountTokenManagerOptions<S>);
90
+ /** The provider's accounts, default first (straight from the store). */
91
+ list(): Promise<AccountEntry<S>[]>;
92
+ /** The default account's key, or undefined when logged out. */
93
+ defaultAccount(): Promise<string | undefined>;
94
+ /**
95
+ * Resolve a usable session for one account (default when omitted),
96
+ * refreshing proactively or on demand.
97
+ * @param account - the account key; the default account when undefined.
98
+ * @param forceRefresh - refresh regardless of expiry (used after a 401).
99
+ * @returns the persisted session to send.
100
+ * @throws LlmError MISSING_CREDENTIAL when the account is not logged in.
101
+ */
102
+ session(account?: string, forceRefresh?: boolean): Promise<S>;
103
+ /** Resolve a legacy alias before it reaches any per-account manager or cache. */
104
+ resolveAccount(account: string): Promise<string>;
105
+ /** Read an account's stored session without any refresh side effect. */
106
+ peek(account?: string): Promise<S | undefined>;
107
+ /** Whether a session is stored for the account (cheap; never refreshes). */
108
+ hasSession(account?: string): Promise<boolean>;
109
+ /** The TokenManager bound to one account (created lazily, then cached). */
110
+ tokensFor(account: string): TokenManager<S>;
111
+ /** The logged-out error, mirroring TokenManager's own message. */
112
+ private missingCredential;
113
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Multi-account token plumbing: one {@link AccountTokenManager} per provider
3
+ * owns a lazily-built {@link TokenManager} per account, so refresh coalescing
4
+ * (`inflight`) and permanent-failure removal stay scoped to ONE account —
5
+ * a revoked account deletes itself without touching its siblings.
6
+ *
7
+ * {@link AccountAwareAdapter} is the internal interface the pool uses to
8
+ * stream through a specific account. A catalog model listed by several
9
+ * accounts failovers; one listed by a single account is pinned to it.
10
+ */
11
+ import { LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm';
12
+ import { TokenManager, withTimeout } from './common.js';
13
+ import { deleteAccountSession, getAccountSession, listAccounts, saveAccountSession, resolveAccountKey, } from '../auth/store.js';
14
+ export { DISCOVERY_TIMEOUT_MS } from './common.js';
15
+ /** Catalog sort hint when the provider advertised one (Codex `priority`). */
16
+ function catalogPriority(model) {
17
+ const ranked = model;
18
+ return typeof ranked.priority === 'number' ? ranked.priority : Number.MAX_SAFE_INTEGER;
19
+ }
20
+ /**
21
+ * Merge per-account catalogs, keeping the first occurrence of each model id.
22
+ * Rows that carry a numeric `priority` (Codex discovery) are then ordered by
23
+ * it so a model only the second account lists — e.g. `gpt-5.6-sol` — still
24
+ * sits with its generation instead of being appended after the default
25
+ * account's older ids.
26
+ */
27
+ export async function unionAccountCatalogs(accounts, listOne, options) {
28
+ const timeoutMs = options?.timeoutMs;
29
+ const caller = options?.signal;
30
+ const catalogs = await Promise.all(accounts.map(async (account) => {
31
+ try {
32
+ if (timeoutMs === undefined)
33
+ return await listOne(account, caller);
34
+ const models = await withTimeout(timeoutSignal => listOne(account, caller === undefined ? timeoutSignal : AbortSignal.any([timeoutSignal, caller])), timeoutMs);
35
+ return models ?? [];
36
+ }
37
+ catch (error) {
38
+ // One expired or failing account must not hide models the others list.
39
+ if (caller?.aborted === true)
40
+ throw error;
41
+ return [];
42
+ }
43
+ }));
44
+ const seen = new Set();
45
+ const models = [];
46
+ for (const catalog of catalogs) {
47
+ for (const model of catalog) {
48
+ if (seen.has(model.id))
49
+ continue;
50
+ seen.add(model.id);
51
+ models.push(model);
52
+ }
53
+ }
54
+ models.sort((left, right) => catalogPriority(left) - catalogPriority(right));
55
+ return models;
56
+ }
57
+ export class AccountTokenManager {
58
+ options;
59
+ managers = new Map();
60
+ io;
61
+ constructor(options) {
62
+ this.options = options;
63
+ const provider = options.provider;
64
+ this.io = options.io ?? {
65
+ list: () => listAccounts(provider),
66
+ get: account => getAccountSession(provider, account),
67
+ save: (account, session) => saveAccountSession(provider, account, session),
68
+ remove: account => deleteAccountSession(provider, account),
69
+ resolve: account => resolveAccountKey(provider, account),
70
+ };
71
+ }
72
+ /** The provider's accounts, default first (straight from the store). */
73
+ list() {
74
+ return this.io.list();
75
+ }
76
+ /** The default account's key, or undefined when logged out. */
77
+ async defaultAccount() {
78
+ return (await this.list())[0]?.key;
79
+ }
80
+ /**
81
+ * Resolve a usable session for one account (default when omitted),
82
+ * refreshing proactively or on demand.
83
+ * @param account - the account key; the default account when undefined.
84
+ * @param forceRefresh - refresh regardless of expiry (used after a 401).
85
+ * @returns the persisted session to send.
86
+ * @throws LlmError MISSING_CREDENTIAL when the account is not logged in.
87
+ */
88
+ async session(account, forceRefresh = false) {
89
+ const requested = account ?? await this.defaultAccount();
90
+ if (requested === undefined)
91
+ throw this.missingCredential();
92
+ const key = await this.resolveAccount(requested);
93
+ return this.tokensFor(key).session(forceRefresh);
94
+ }
95
+ /** Resolve a legacy alias before it reaches any per-account manager or cache. */
96
+ resolveAccount(account) {
97
+ return this.io.resolve?.(account) ?? Promise.resolve(account);
98
+ }
99
+ /** Read an account's stored session without any refresh side effect. */
100
+ async peek(account) {
101
+ return this.io.get(account === undefined ? undefined : await this.resolveAccount(account));
102
+ }
103
+ /** Whether a session is stored for the account (cheap; never refreshes). */
104
+ async hasSession(account) {
105
+ return (await this.peek(account)) !== undefined;
106
+ }
107
+ /** The TokenManager bound to one account (created lazily, then cached). */
108
+ tokensFor(account) {
109
+ let manager = this.managers.get(account);
110
+ if (manager === undefined) {
111
+ const io = this.io;
112
+ let boundAccount = account;
113
+ manager = new TokenManager({
114
+ displayName: this.options.displayName,
115
+ ...this.options.makeOptions(account),
116
+ load: () => io.get(boundAccount),
117
+ save: async (session) => {
118
+ await io.save(boundAccount, session);
119
+ const canonical = await this.resolveAccount(boundAccount);
120
+ if (canonical !== boundAccount) {
121
+ // Move the binding as well as the cache entry: logout can delete
122
+ // aliases, so callbacks must not keep relying on the old key.
123
+ // Retain the manager itself to preserve its in-flight refresh.
124
+ this.managers.set(canonical, manager);
125
+ if (this.managers.get(boundAccount) === manager)
126
+ this.managers.delete(boundAccount);
127
+ boundAccount = canonical;
128
+ }
129
+ },
130
+ remove: () => io.remove(boundAccount),
131
+ onRemoved: () => { this.options.onAccountRemoved?.(boundAccount); },
132
+ });
133
+ this.managers.set(account, manager);
134
+ }
135
+ return manager;
136
+ }
137
+ /** The logged-out error, mirroring TokenManager's own message. */
138
+ missingCredential() {
139
+ return new LlmError(`dsh-plugin-subscription-usage: not logged in to ${this.options.displayName}; `
140
+ + 'log in via Settings → Subscriptions in the dsh web app', 'MISSING_CREDENTIAL');
141
+ }
142
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Public Antigravity desktop OAuth client identity, also distributed by pi-antigravity.
3
+ * These application constants are distinct from user access/refresh tokens.
4
+ * Source: https://github.com/Rahularya01/pi-antigravity/blob/697858cafcf1faddf2ae898d2f053b2ff26c05e6/src/auth/oauth.ts
5
+ */
6
+ export declare const ANTIGRAVITY_DEFAULT_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
7
+ export declare const ANTIGRAVITY_DEFAULT_CLIENT_SECRET = "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";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Public Antigravity desktop OAuth client identity, also distributed by pi-antigravity.
3
+ * These application constants are distinct from user access/refresh tokens.
4
+ * Source: https://github.com/Rahularya01/pi-antigravity/blob/697858cafcf1faddf2ae898d2f053b2ff26c05e6/src/auth/oauth.ts
5
+ */
6
+ export const ANTIGRAVITY_DEFAULT_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
7
+ export const ANTIGRAVITY_DEFAULT_CLIENT_SECRET = "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";
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Google Antigravity subscription provider. This is intentionally separate
3
+ * from Gemini CLI: it uses Antigravity OAuth scopes, project discovery, and
4
+ * the daily-cloudcode-pa v1internal request envelope.
5
+ */
6
+ import { LlmAdapter } from '@deepseek-ai/dsh-llm';
7
+ import type { GenerateOptions, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm';
8
+ import type { AttachmentStore } from '@deepseek-ai/dsh-attachment';
9
+ import type { FlowSpec } from '../auth/oauth-flow.js';
10
+ import type { AntigravitySession } from '../auth/store.js';
11
+ import type { AntigravityRequest } from '../translate/antigravity.js';
12
+ import type { CatalogPersistence, DiscoveredModel, FetchFn, ModelEntry, ProviderUsage } from './common.js';
13
+ import { AccountTokenManager } from './accounts.js';
14
+ import type { PoolAdapter } from './pool.js';
15
+ import type { RateLimitWait } from './rate-limit.js';
16
+ export declare const ANTIGRAVITY_AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/v2/auth";
17
+ export declare const ANTIGRAVITY_TOKEN_URL = "https://oauth2.googleapis.com/token";
18
+ export declare const ANTIGRAVITY_USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo";
19
+ export declare const ANTIGRAVITY_DEFAULT_BASE_URL = "https://daily-cloudcode-pa.googleapis.com";
20
+ export declare const ANTIGRAVITY_PROD_BASE_URL = "https://cloudcode-pa.googleapis.com";
21
+ export declare const ANTIGRAVITY_DEFAULT_USER_AGENT = "antigravity/1.104.0 dsh-plugin-subscription-usage";
22
+ export declare const ANTIGRAVITY_PREEMPT_MS: number;
23
+ /** Antigravity, not Gemini CLI, OAuth scopes from the local reference clients. */
24
+ export declare const ANTIGRAVITY_SCOPES: readonly ["openid", "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/cclog", "https://www.googleapis.com/auth/experimentsandconfigs"];
25
+ /** OAuth client configuration; explicit clients override the bundled desktop identity. */
26
+ export interface AntigravityOAuthConfig {
27
+ clientId: string;
28
+ clientSecret?: string;
29
+ }
30
+ /** Runtime endpoint configuration. */
31
+ export interface AntigravityRuntimeConfig {
32
+ /** Pin one origin; omitted uses daily with production fallback on endpoint failures. */
33
+ baseURL?: string;
34
+ userAgent?: string;
35
+ /** Activate an eligible account when loadCodeAssist has no project yet. */
36
+ onboard?: boolean;
37
+ }
38
+ /** Resolve a client pair: plugin config, environment, then the public desktop default. */
39
+ export declare function resolveAntigravityOAuthConfig(config?: Partial<AntigravityOAuthConfig>): AntigravityOAuthConfig;
40
+ /** Normalize the configured API origin and reject paths/credentials. */
41
+ export declare function antigravityBaseURL(value?: string): string;
42
+ /** Google authorization-code + PKCE flow for Antigravity. */
43
+ export declare function antigravityFlow(oauth: AntigravityOAuthConfig): FlowSpec;
44
+ interface AntigravityAccountInfo {
45
+ projectId: string;
46
+ account?: string;
47
+ plan?: string;
48
+ }
49
+ /** Shared Antigravity API headers. */
50
+ export declare function antigravityHeaders(accessToken: string, userAgent?: string): Record<string, string>;
51
+ /** Read (and, when enabled, initialize) the Antigravity project/account. */
52
+ export declare function discoverAntigravityAccount(accessToken: string, runtime?: AntigravityRuntimeConfig, fetchFn?: FetchFn): Promise<AntigravityAccountInfo>;
53
+ /** Exchange a Google OAuth authorization code and discover the Antigravity project. */
54
+ export declare function exchangeAntigravityCode(code: string, verifier: string, redirectUri: string, oauth: AntigravityOAuthConfig, runtime?: AntigravityRuntimeConfig, fetchFn?: FetchFn): Promise<AntigravitySession>;
55
+ /** Refresh a stored Antigravity Google token, preserving project/account metadata. */
56
+ export declare function refreshAntigravity(session: AntigravitySession, oauth: AntigravityOAuthConfig, fetchFn?: FetchFn): Promise<AntigravitySession>;
57
+ /** Refresh failures that require a fresh Google consent grant. */
58
+ export declare function isAntigravityPermanentRefreshError(error: unknown): boolean;
59
+ /** Fetch the authenticated account's live Antigravity model catalog. */
60
+ export declare function fetchAntigravityModels(session: AntigravitySession, runtime?: AntigravityRuntimeConfig, fetchFn?: FetchFn, signal?: AbortSignal): Promise<DiscoveredModel[]>;
61
+ /** Fetch plan and per-model quota windows when the upstream exposes them. */
62
+ export declare function fetchAntigravityUsage(session: AntigravitySession, runtime?: AntigravityRuntimeConfig, fetchFn?: FetchFn, signal?: AbortSignal): Promise<ProviderUsage>;
63
+ /** URL for either v1internal generation transport. */
64
+ export declare function antigravityGenerateURL(baseURL: string | undefined, stream: boolean): string;
65
+ /** Forward one already-built payload to generateContent or streamGenerateContent. */
66
+ export declare function requestAntigravityContent(session: AntigravitySession, payload: AntigravityRequest, stream: boolean, runtime?: AntigravityRuntimeConfig, fetchFn?: FetchFn, signal?: AbortSignal): Promise<Response>;
67
+ export interface AntigravityAdapterOptions {
68
+ /**
69
+ * Observes every upstream reply before its status is checked, with the
70
+ * resolved account key. Usage headers ride both 2xx and 429 replies.
71
+ */
72
+ onResponse?: (response: Response, account: string, sessionId: string | undefined) => void;
73
+ models: readonly ModelEntry[];
74
+ streamIdleTimeoutMs: number;
75
+ tokens: AccountTokenManager<AntigravitySession>;
76
+ pool?: () => PoolAdapter | undefined;
77
+ rateLimit?: RateLimitWait;
78
+ discovery: boolean;
79
+ runtime?: AntigravityRuntimeConfig;
80
+ onWarn?: (message: string) => void;
81
+ fetchFn?: FetchFn;
82
+ resolveAttachments?: () => AttachmentStore | undefined;
83
+ catalogStore?: CatalogPersistence;
84
+ onCatalogChanged?: () => void;
85
+ accountCatalogStore?: (account: string) => CatalogPersistence;
86
+ defaultEffortOf?: (model: string) => string | undefined;
87
+ }
88
+ /** DSH provider adapter for the `antigravity` route. */
89
+ export declare class AntigravityAdapter extends LlmAdapter {
90
+ private readonly options;
91
+ private readonly catalog;
92
+ private readonly accountCatalogs;
93
+ private accountCatalogsInvalidated;
94
+ private readonly invalidatedAccounts;
95
+ private catalogOwner;
96
+ constructor(options: AntigravityAdapterOptions);
97
+ providerInfo(provider: string): LlmProviderInfo;
98
+ /** Drop cached catalogs after login/logout so the next list does not reuse a stale plan. */
99
+ clearAccountCatalog(account?: string): void;
100
+ /** Default and secondary accounts each seed their own persisted catalog. */
101
+ private catalogFor;
102
+ providerRetryPolicy(provider: string): import("@deepseek-ai/dsh-llm").ResolvedRetryPolicy;
103
+ private staticModels;
104
+ private fetchCatalog;
105
+ listModels(provider: string): Promise<readonly LlmModelInfo[]>;
106
+ /** The provider's own catalog: union of every account, or one account when named. */
107
+ listOwnModels(provider: string, account?: string, signal?: AbortSignal, preferCached?: boolean): Promise<readonly LlmModelInfo[]>;
108
+ private discovered;
109
+ resolveModel(provider: string, model: string): Promise<LlmResolvedModelInfo>;
110
+ /** Capability resolution of the provider's own models (the pool resolves members here). */
111
+ resolveOwnModel(provider: string, model: string, account?: string): Promise<LlmResolvedModelInfo>;
112
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
113
+ /** Pool seam: stream through one specific account instead of the default. */
114
+ streamAccount(options: GenerateOptions, account: string): AsyncIterable<StreamChunk>;
115
+ private streamCore;
116
+ /** Non-stream forwarding seam used by tests and future DSH complete calls. */
117
+ generate(options: GenerateOptions): Promise<StreamChunk[]>;
118
+ }
119
+ export {};