dsh-plugin-subscriptions 0.5.1 → 0.5.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.
Files changed (50) hide show
  1. package/README.md +42 -1
  2. package/README.zh.md +42 -1
  3. package/lib/auth/device-flow.d.ts +0 -9
  4. package/lib/auth/device-flow.js +2 -1
  5. package/lib/auth/rpc.d.ts +44 -13
  6. package/lib/auth/rpc.js +127 -9
  7. package/lib/auth/store.d.ts +75 -17
  8. package/lib/auth/store.js +148 -27
  9. package/lib/client/SubscriptionsSection.d.ts +26 -3
  10. package/lib/client/SubscriptionsSection.js +263 -67
  11. package/lib/client/index.js +11 -0
  12. package/lib/client/locales.d.ts +82 -10
  13. package/lib/client/locales.js +82 -10
  14. package/lib/client.js +837 -223
  15. package/lib/client.js.map +1 -1
  16. package/lib/http.d.ts +114 -0
  17. package/lib/http.js +402 -0
  18. package/lib/index.d.ts +21 -0
  19. package/lib/index.js +1938 -208
  20. package/lib/providers/accounts.d.ts +102 -0
  21. package/lib/providers/accounts.js +123 -0
  22. package/lib/providers/antigravity.d.ts +90 -0
  23. package/lib/providers/antigravity.js +392 -0
  24. package/lib/providers/claude.d.ts +22 -4
  25. package/lib/providers/claude.js +97 -16
  26. package/lib/providers/codex.d.ts +24 -3
  27. package/lib/providers/codex.js +121 -21
  28. package/lib/providers/common.d.ts +17 -0
  29. package/lib/providers/common.js +67 -3
  30. package/lib/providers/copilot.d.ts +23 -4
  31. package/lib/providers/copilot.js +99 -19
  32. package/lib/providers/grok.d.ts +24 -4
  33. package/lib/providers/grok.js +106 -19
  34. package/lib/providers/pool-family.d.ts +56 -0
  35. package/lib/providers/pool-family.js +45 -0
  36. package/lib/providers/pool-health.d.ts +74 -0
  37. package/lib/providers/pool-health.js +148 -0
  38. package/lib/providers/pool-usage.d.ts +57 -0
  39. package/lib/providers/pool-usage.js +130 -0
  40. package/lib/providers/pool.d.ts +107 -0
  41. package/lib/providers/pool.js +371 -0
  42. package/lib/tools/image-generate.d.ts +3 -3
  43. package/lib/tools/image-generate.js +4 -2
  44. package/lib/tools/video-generate.d.ts +2 -2
  45. package/lib/tools/video-generate.js +4 -2
  46. package/lib/tools/x-search.d.ts +2 -2
  47. package/lib/tools/x-search.js +4 -2
  48. package/lib/translate/antigravity.d.ts +110 -0
  49. package/lib/translate/antigravity.js +303 -0
  50. package/package.json +14 -9
@@ -0,0 +1,102 @@
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
+ */
32
+ listOwnModels(provider: string, account?: string, signal?: AbortSignal): Promise<readonly LlmModelInfo[]>;
33
+ /**
34
+ * Capability resolution of the provider's OWN models, bypassing the pool
35
+ * delegation. The pool resolves its members through this — an account pool
36
+ * reuses the catalog wire id (e.g. `gpt-5.4`), so resolveModel would
37
+ * otherwise bounce straight back into the pool forever.
38
+ */
39
+ resolveOwnModel(provider: string, model: string): Promise<LlmResolvedModelInfo>;
40
+ /** Drop cached catalogs: one account, or every account when omitted (login/logout). */
41
+ clearAccountCatalog(account?: string): void;
42
+ }
43
+ /** Options for {@link unionAccountCatalogs}. */
44
+ export interface UnionAccountCatalogsOptions {
45
+ /** Per-account bound; a hang sits that account out instead of blocking the picker. */
46
+ timeoutMs?: number;
47
+ /** Caller cancellation; aborting drops the whole union. */
48
+ signal?: AbortSignal;
49
+ }
50
+ /**
51
+ * Merge per-account catalogs, keeping the first occurrence of each model id.
52
+ * Rows that carry a numeric `priority` (Codex discovery) are then ordered by
53
+ * it so a model only the second account lists — e.g. `gpt-5.6-sol` — still
54
+ * sits with its generation instead of being appended after the default
55
+ * account's older ids.
56
+ */
57
+ export declare function unionAccountCatalogs(accounts: readonly string[], listOne: (account: string, signal?: AbortSignal) => Promise<readonly LlmModelInfo[]>, options?: UnionAccountCatalogsOptions): Promise<LlmModelInfo[]>;
58
+ /** Store I/O behind {@link AccountTokenManager} (injectable for tests). */
59
+ export interface AccountStoreIo<S> {
60
+ list(): Promise<AccountEntry<S>[]>;
61
+ get(account?: string): Promise<S | undefined>;
62
+ save(account: string, session: S): Promise<void>;
63
+ remove(account: string): Promise<void>;
64
+ }
65
+ export interface AccountTokenManagerOptions<S extends TimedSession> {
66
+ provider: ProviderId;
67
+ /** Human-readable provider name for error messages. */
68
+ displayName: string;
69
+ /** Provider hooks shared by every account (load/save/remove are bound per account). */
70
+ makeOptions: (account: string) => Omit<TokenManagerOptions<S>, 'load' | 'save' | 'remove' | 'onRemoved' | 'displayName'>;
71
+ /** Called after a permanent refresh failure deleted one account's session. */
72
+ onAccountRemoved?: (account: string) => void;
73
+ /** Store backend; defaults to the durable auth store. */
74
+ io?: AccountStoreIo<S>;
75
+ }
76
+ export declare class AccountTokenManager<S extends TimedSession> {
77
+ private readonly options;
78
+ private readonly managers;
79
+ private readonly io;
80
+ constructor(options: AccountTokenManagerOptions<S>);
81
+ /** The provider's accounts, default first (straight from the store). */
82
+ list(): Promise<AccountEntry<S>[]>;
83
+ /** The default account's key, or undefined when logged out. */
84
+ defaultAccount(): Promise<string | undefined>;
85
+ /**
86
+ * Resolve a usable session for one account (default when omitted),
87
+ * refreshing proactively or on demand.
88
+ * @param account - the account key; the default account when undefined.
89
+ * @param forceRefresh - refresh regardless of expiry (used after a 401).
90
+ * @returns the persisted session to send.
91
+ * @throws LlmError MISSING_CREDENTIAL when the account is not logged in.
92
+ */
93
+ session(account?: string, forceRefresh?: boolean): Promise<S>;
94
+ /** Read an account's stored session without any refresh side effect. */
95
+ peek(account?: string): Promise<S | undefined>;
96
+ /** Whether a session is stored for the account (cheap; never refreshes). */
97
+ hasSession(account?: string): Promise<boolean>;
98
+ /** The TokenManager bound to one account (created lazily, then cached). */
99
+ tokensFor(account: string): TokenManager<S>;
100
+ /** The logged-out error, mirroring TokenManager's own message. */
101
+ private missingCredential;
102
+ }
@@ -0,0 +1,123 @@
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, } 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
+ };
70
+ }
71
+ /** The provider's accounts, default first (straight from the store). */
72
+ list() {
73
+ return this.io.list();
74
+ }
75
+ /** The default account's key, or undefined when logged out. */
76
+ async defaultAccount() {
77
+ return (await this.list())[0]?.key;
78
+ }
79
+ /**
80
+ * Resolve a usable session for one account (default when omitted),
81
+ * refreshing proactively or on demand.
82
+ * @param account - the account key; the default account when undefined.
83
+ * @param forceRefresh - refresh regardless of expiry (used after a 401).
84
+ * @returns the persisted session to send.
85
+ * @throws LlmError MISSING_CREDENTIAL when the account is not logged in.
86
+ */
87
+ async session(account, forceRefresh = false) {
88
+ const key = account ?? await this.defaultAccount();
89
+ if (key === undefined)
90
+ throw this.missingCredential();
91
+ return this.tokensFor(key).session(forceRefresh);
92
+ }
93
+ /** Read an account's stored session without any refresh side effect. */
94
+ peek(account) {
95
+ return this.io.get(account);
96
+ }
97
+ /** Whether a session is stored for the account (cheap; never refreshes). */
98
+ async hasSession(account) {
99
+ return (await this.peek(account)) !== undefined;
100
+ }
101
+ /** The TokenManager bound to one account (created lazily, then cached). */
102
+ tokensFor(account) {
103
+ let manager = this.managers.get(account);
104
+ if (manager === undefined) {
105
+ const io = this.io;
106
+ manager = new TokenManager({
107
+ displayName: this.options.displayName,
108
+ ...this.options.makeOptions(account),
109
+ load: () => io.get(account),
110
+ save: session => io.save(account, session),
111
+ remove: () => io.remove(account),
112
+ onRemoved: () => { this.options.onAccountRemoved?.(account); },
113
+ });
114
+ this.managers.set(account, manager);
115
+ }
116
+ return manager;
117
+ }
118
+ /** The logged-out error, mirroring TokenManager's own message. */
119
+ missingCredential() {
120
+ return new LlmError(`dsh-plugin-subscriptions: not logged in to ${this.options.displayName}; `
121
+ + 'log in via Settings → Subscriptions in the dsh web app', 'MISSING_CREDENTIAL');
122
+ }
123
+ }
@@ -0,0 +1,90 @@
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 { TokenManager } from './common.js';
13
+ import type { CatalogPersistence, DiscoveredModel, FetchFn, ModelEntry, ProviderUsage } from './common.js';
14
+ export declare const ANTIGRAVITY_AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/v2/auth";
15
+ export declare const ANTIGRAVITY_TOKEN_URL = "https://oauth2.googleapis.com/token";
16
+ export declare const ANTIGRAVITY_USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo";
17
+ export declare const ANTIGRAVITY_DEFAULT_BASE_URL = "https://daily-cloudcode-pa.googleapis.com";
18
+ export declare const ANTIGRAVITY_PROD_BASE_URL = "https://cloudcode-pa.googleapis.com";
19
+ export declare const ANTIGRAVITY_DEFAULT_USER_AGENT = "antigravity/1.104.0 dsh-plugin-subscriptions";
20
+ export declare const ANTIGRAVITY_PREEMPT_MS: number;
21
+ /** Antigravity, not Gemini CLI, OAuth scopes from the local reference clients. */
22
+ 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"];
23
+ /** OAuth client configuration. Values must come from config/environment. */
24
+ export interface AntigravityOAuthConfig {
25
+ clientId: string;
26
+ clientSecret?: string;
27
+ }
28
+ /** Runtime endpoint configuration. */
29
+ export interface AntigravityRuntimeConfig {
30
+ baseURL?: string;
31
+ userAgent?: string;
32
+ /** Activate an eligible account when loadCodeAssist has no project yet. */
33
+ onboard?: boolean;
34
+ }
35
+ /** Resolve and validate a user-supplied OAuth config without embedded credentials. */
36
+ export declare function resolveAntigravityOAuthConfig(config?: Partial<AntigravityOAuthConfig>): AntigravityOAuthConfig;
37
+ /** Normalize the configured API origin and reject paths/credentials. */
38
+ export declare function antigravityBaseURL(value?: string): string;
39
+ /** Google authorization-code + PKCE flow for Antigravity. */
40
+ export declare function antigravityFlow(oauth: AntigravityOAuthConfig): FlowSpec;
41
+ interface AntigravityAccountInfo {
42
+ projectId: string;
43
+ account?: string;
44
+ plan?: string;
45
+ }
46
+ /** Shared Antigravity API headers. */
47
+ export declare function antigravityHeaders(accessToken: string, userAgent?: string): Record<string, string>;
48
+ /** Read (and, when enabled, initialize) the Antigravity project/account. */
49
+ export declare function discoverAntigravityAccount(accessToken: string, runtime?: AntigravityRuntimeConfig, fetchFn?: FetchFn): Promise<AntigravityAccountInfo>;
50
+ /** Exchange a Google OAuth authorization code and discover the Antigravity project. */
51
+ export declare function exchangeAntigravityCode(code: string, verifier: string, redirectUri: string, oauth: AntigravityOAuthConfig, runtime?: AntigravityRuntimeConfig, fetchFn?: FetchFn): Promise<AntigravitySession>;
52
+ /** Refresh a stored Antigravity Google token, preserving project/account metadata. */
53
+ export declare function refreshAntigravity(session: AntigravitySession, oauth: AntigravityOAuthConfig, fetchFn?: FetchFn): Promise<AntigravitySession>;
54
+ /** Refresh failures that require a fresh Google consent grant. */
55
+ export declare function isAntigravityPermanentRefreshError(error: unknown): boolean;
56
+ /** Fetch the authenticated account's live Antigravity model catalog. */
57
+ export declare function fetchAntigravityModels(session: AntigravitySession, runtime?: AntigravityRuntimeConfig, fetchFn?: FetchFn): Promise<DiscoveredModel[]>;
58
+ /** Fetch plan and per-model quota windows when the upstream exposes them. */
59
+ export declare function fetchAntigravityUsage(session: AntigravitySession, runtime?: AntigravityRuntimeConfig, fetchFn?: FetchFn, signal?: AbortSignal): Promise<ProviderUsage>;
60
+ /** URL for either v1internal generation transport. */
61
+ export declare function antigravityGenerateURL(baseURL: string | undefined, stream: boolean): string;
62
+ /** Forward one already-built payload to generateContent or streamGenerateContent. */
63
+ export declare function requestAntigravityContent(session: AntigravitySession, payload: AntigravityRequest, stream: boolean, runtime?: AntigravityRuntimeConfig, fetchFn?: FetchFn, signal?: AbortSignal): Promise<Response>;
64
+ export interface AntigravityAdapterOptions {
65
+ models: readonly ModelEntry[];
66
+ streamIdleTimeoutMs: number;
67
+ tokens: TokenManager<AntigravitySession>;
68
+ discovery: boolean;
69
+ runtime?: AntigravityRuntimeConfig;
70
+ onWarn?: (message: string) => void;
71
+ fetchFn?: FetchFn;
72
+ resolveAttachments?: () => AttachmentStore | undefined;
73
+ catalogStore?: CatalogPersistence;
74
+ }
75
+ /** DSH provider adapter for the `antigravity` route. */
76
+ export declare class AntigravityAdapter extends LlmAdapter {
77
+ private readonly options;
78
+ private readonly catalog;
79
+ constructor(options: AntigravityAdapterOptions);
80
+ providerInfo(provider: string): LlmProviderInfo;
81
+ private staticModels;
82
+ private fetchCatalog;
83
+ listModels(provider: string): Promise<readonly LlmModelInfo[]>;
84
+ private discovered;
85
+ resolveModel(provider: string, model: string): Promise<LlmResolvedModelInfo>;
86
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
87
+ /** Non-stream forwarding seam used by tests and future DSH complete calls. */
88
+ generate(options: GenerateOptions): Promise<StreamChunk[]>;
89
+ }
90
+ export {};