dsh-plugin-subscription-oauth 0.0.0-stage → 0.1.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 (82) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +400 -2
  3. package/cordis.patch.yml +13 -0
  4. package/lib/auth/device-flow.d.ts +112 -0
  5. package/lib/auth/device-flow.js +319 -0
  6. package/lib/auth/device-id.d.ts +31 -0
  7. package/lib/auth/device-id.js +58 -0
  8. package/lib/auth/flow.d.ts +33 -0
  9. package/lib/auth/flow.js +12 -0
  10. package/lib/auth/glm-flow.d.ts +99 -0
  11. package/lib/auth/glm-flow.js +194 -0
  12. package/lib/auth/headers.d.ts +61 -0
  13. package/lib/auth/headers.js +106 -0
  14. package/lib/auth/pkce-flow.d.ts +78 -0
  15. package/lib/auth/pkce-flow.js +205 -0
  16. package/lib/auth/provider-controller.d.ts +241 -0
  17. package/lib/auth/provider-controller.js +502 -0
  18. package/lib/auth/registry.d.ts +91 -0
  19. package/lib/auth/registry.js +149 -0
  20. package/lib/auth/rpc.d.ts +31 -0
  21. package/lib/auth/rpc.js +200 -0
  22. package/lib/auth/store.d.ts +99 -0
  23. package/lib/auth/store.js +213 -0
  24. package/lib/auth/tokens.d.ts +76 -0
  25. package/lib/auth/tokens.js +103 -0
  26. package/lib/client/SubscriptionsSection.d.ts +34 -0
  27. package/lib/client/SubscriptionsSection.js +742 -0
  28. package/lib/client/index.d.ts +36 -0
  29. package/lib/client/index.js +47 -0
  30. package/lib/client/locales.d.ts +187 -0
  31. package/lib/client/locales.js +185 -0
  32. package/lib/client/styles.d.ts +18 -0
  33. package/lib/client/styles.js +257 -0
  34. package/lib/client/subscriptions-rpc.d.ts +24 -0
  35. package/lib/client/subscriptions-rpc.js +44 -0
  36. package/lib/client.js +2078 -0
  37. package/lib/client.js.map +1 -0
  38. package/lib/constants.d.ts +79 -0
  39. package/lib/constants.js +91 -0
  40. package/lib/errors.d.ts +28 -0
  41. package/lib/errors.js +34 -0
  42. package/lib/http.d.ts +24 -0
  43. package/lib/http.js +98 -0
  44. package/lib/index.d.ts +57 -0
  45. package/lib/index.js +163 -0
  46. package/lib/protocol.d.ts +316 -0
  47. package/lib/protocol.js +29 -0
  48. package/lib/providers/antigravity-catalog.d.ts +53 -0
  49. package/lib/providers/antigravity-catalog.js +269 -0
  50. package/lib/providers/antigravity.d.ts +331 -0
  51. package/lib/providers/antigravity.js +817 -0
  52. package/lib/providers/auto.d.ts +105 -0
  53. package/lib/providers/auto.js +311 -0
  54. package/lib/providers/catalog.d.ts +69 -0
  55. package/lib/providers/catalog.js +78 -0
  56. package/lib/providers/common.d.ts +51 -0
  57. package/lib/providers/common.js +169 -0
  58. package/lib/providers/glm-provider.d.ts +41 -0
  59. package/lib/providers/glm-provider.js +165 -0
  60. package/lib/providers/glm.d.ts +159 -0
  61. package/lib/providers/glm.js +441 -0
  62. package/lib/providers/grok.d.ts +224 -0
  63. package/lib/providers/grok.js +696 -0
  64. package/lib/providers/kimi-provider.d.ts +45 -0
  65. package/lib/providers/kimi-provider.js +148 -0
  66. package/lib/providers/kimi.d.ts +151 -0
  67. package/lib/providers/kimi.js +396 -0
  68. package/lib/providers/responses-stream.d.ts +58 -0
  69. package/lib/providers/responses-stream.js +115 -0
  70. package/lib/providers/usage.d.ts +65 -0
  71. package/lib/providers/usage.js +309 -0
  72. package/lib/settings.d.ts +122 -0
  73. package/lib/settings.js +333 -0
  74. package/lib/translate/antigravity-schema.d.ts +24 -0
  75. package/lib/translate/antigravity-schema.js +174 -0
  76. package/lib/translate/antigravity.d.ts +184 -0
  77. package/lib/translate/antigravity.js +419 -0
  78. package/lib/translate/responses.d.ts +156 -0
  79. package/lib/translate/responses.js +395 -0
  80. package/lib/translate/sse.d.ts +27 -0
  81. package/lib/translate/sse.js +82 -0
  82. package/package.json +126 -4
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Access-token lifecycle for one provider's accounts.
3
+ *
4
+ * Every service issues short-lived access tokens, so a request resolves a
5
+ * usable token first: a token close to expiry is refreshed eagerly rather than
6
+ * after the request already failed. Concurrent resolutions share one in-flight
7
+ * refresh, so a burst of parallel requests cannot stampede the token endpoint.
8
+ *
9
+ * What a refresh *is* stays provider-owned — the manager takes a callback and
10
+ * persists whatever it returns.
11
+ *
12
+ * @module dsh-plugin-subscription-oauth/auth/tokens
13
+ */
14
+ import type { SessionBase, TokenStore } from './store.js';
15
+ /** Default refresh trigger: this long before expiry. */
16
+ export declare const DEFAULT_PREEMPT_MS: number;
17
+ /** One provider's token-endpoint answer, normalised. */
18
+ export interface TokenSet {
19
+ readonly accessToken: string;
20
+ readonly refreshToken?: string;
21
+ /** Epoch milliseconds after which the access token must not be sent. */
22
+ readonly expiresAt: number;
23
+ }
24
+ /** A usable access token plus the account it belongs to. */
25
+ export interface SessionToken {
26
+ readonly account: string;
27
+ readonly accessToken: string;
28
+ readonly expiresAt: number;
29
+ }
30
+ /** What the manager needs to resolve tokens for one provider. */
31
+ export interface TokenManagerOptions<S extends SessionBase> {
32
+ readonly store: TokenStore<S>;
33
+ /** Human name used in failure messages, e.g. `Kimi Code`. */
34
+ readonly displayName: string;
35
+ /**
36
+ * Redeem one account's refresh token.
37
+ * @throws {ProviderError} with {@link REAUTH_REQUIRED} when the account must sign in again.
38
+ */
39
+ readonly refresh: (session: S) => Promise<TokenSet>;
40
+ /** Refresh when the token has less than this much life left. */
41
+ readonly preemptMs?: number;
42
+ }
43
+ /** Resolves and refreshes one provider's access tokens. */
44
+ export declare class TokenManager<S extends SessionBase> {
45
+ private readonly options;
46
+ private readonly refreshes;
47
+ private readonly preemptMs;
48
+ constructor(options: TokenManagerOptions<S>);
49
+ /** Stored session for one account (default account when omitted). */
50
+ session(account?: string): Promise<{
51
+ account: string;
52
+ session: S;
53
+ } | undefined>;
54
+ /** Every stored account key. */
55
+ accounts(): Promise<string[]>;
56
+ /** The default account key, when one exists. */
57
+ defaultAccount(): Promise<string | undefined>;
58
+ /**
59
+ * Resolve a request-ready token for one account.
60
+ *
61
+ * @param account - account key; the default account is used when omitted.
62
+ * @param force - refresh even when the stored token still has life left.
63
+ * @returns the usable token.
64
+ * @throws {ProviderError} with {@link REAUTH_REQUIRED} when the account cannot be recovered.
65
+ */
66
+ resolve(account?: string, force?: boolean): Promise<SessionToken>;
67
+ /** Refresh one account, sharing one in-flight request per account. */
68
+ private refresh;
69
+ }
70
+ /**
71
+ * Fold a refreshed token set back onto the identity-bearing session fields.
72
+ * @param tokens - the refreshed tokens.
73
+ * @param previous - the session the refresh started from.
74
+ * @returns the session to persist.
75
+ */
76
+ export declare function foldTokens<S extends SessionBase>(tokens: TokenSet, previous: S): S;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Access-token lifecycle for one provider's accounts.
3
+ *
4
+ * Every service issues short-lived access tokens, so a request resolves a
5
+ * usable token first: a token close to expiry is refreshed eagerly rather than
6
+ * after the request already failed. Concurrent resolutions share one in-flight
7
+ * refresh, so a burst of parallel requests cannot stampede the token endpoint.
8
+ *
9
+ * What a refresh *is* stays provider-owned — the manager takes a callback and
10
+ * persists whatever it returns.
11
+ *
12
+ * @module dsh-plugin-subscription-oauth/auth/tokens
13
+ */
14
+ import { REAUTH_REQUIRED, ProviderError } from '../errors.js';
15
+ /** Default refresh trigger: this long before expiry. */
16
+ export const DEFAULT_PREEMPT_MS = 5 * 60 * 1000;
17
+ /** Resolves and refreshes one provider's access tokens. */
18
+ export class TokenManager {
19
+ options;
20
+ refreshes = new Map();
21
+ preemptMs;
22
+ constructor(options) {
23
+ this.options = options;
24
+ this.preemptMs = options.preemptMs ?? DEFAULT_PREEMPT_MS;
25
+ }
26
+ /** Stored session for one account (default account when omitted). */
27
+ async session(account) {
28
+ const key = account ?? await this.options.store.defaultAccount();
29
+ if (key === undefined)
30
+ return undefined;
31
+ const session = await this.options.store.get(key);
32
+ return session === undefined ? undefined : { account: key, session };
33
+ }
34
+ /** Every stored account key. */
35
+ async accounts() {
36
+ return Object.keys(await this.options.store.list());
37
+ }
38
+ /** The default account key, when one exists. */
39
+ async defaultAccount() {
40
+ return this.options.store.defaultAccount();
41
+ }
42
+ /**
43
+ * Resolve a request-ready token for one account.
44
+ *
45
+ * @param account - account key; the default account is used when omitted.
46
+ * @param force - refresh even when the stored token still has life left.
47
+ * @returns the usable token.
48
+ * @throws {ProviderError} with {@link REAUTH_REQUIRED} when the account cannot be recovered.
49
+ */
50
+ async resolve(account, force = false) {
51
+ const found = await this.session(account);
52
+ if (found === undefined) {
53
+ throw new ProviderError(account === undefined
54
+ ? `no ${this.options.displayName} account is signed in`
55
+ : `${this.options.displayName} account ${account} is not signed in`, REAUTH_REQUIRED);
56
+ }
57
+ const { account: key, session } = found;
58
+ if (!force && Date.now() + this.preemptMs < session.expiresAt) {
59
+ return { account: key, accessToken: session.accessToken, expiresAt: session.expiresAt };
60
+ }
61
+ const refreshed = await this.refresh(key, session);
62
+ return { account: key, accessToken: refreshed.accessToken, expiresAt: refreshed.expiresAt };
63
+ }
64
+ /** Refresh one account, sharing one in-flight request per account. */
65
+ async refresh(key, session) {
66
+ const existing = this.refreshes.get(key);
67
+ if (existing !== undefined)
68
+ return existing;
69
+ const pending = (async () => {
70
+ if (session.refreshToken === undefined) {
71
+ throw new ProviderError(`${this.options.displayName} account ${key} has no refresh token; sign in again`, REAUTH_REQUIRED);
72
+ }
73
+ const tokens = await this.options.refresh(session);
74
+ const stored = await this.options.store.get(key);
75
+ if (stored === undefined) {
76
+ throw new ProviderError(`${this.options.displayName} account ${key} was removed while refreshing`, REAUTH_REQUIRED);
77
+ }
78
+ await this.options.store.save(key, foldTokens(tokens, stored), { makeDefault: false });
79
+ return tokens;
80
+ })();
81
+ this.refreshes.set(key, pending);
82
+ try {
83
+ return await pending;
84
+ }
85
+ finally {
86
+ this.refreshes.delete(key);
87
+ }
88
+ }
89
+ }
90
+ /**
91
+ * Fold a refreshed token set back onto the identity-bearing session fields.
92
+ * @param tokens - the refreshed tokens.
93
+ * @param previous - the session the refresh started from.
94
+ * @returns the session to persist.
95
+ */
96
+ export function foldTokens(tokens, previous) {
97
+ return {
98
+ ...previous,
99
+ accessToken: tokens.accessToken,
100
+ expiresAt: tokens.expiresAt,
101
+ ...tokens.refreshToken === undefined ? {} : { refreshToken: tokens.refreshToken },
102
+ };
103
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Subscriptions settings page.
3
+ *
4
+ * One card per provider route — Kimi Code, Grok (X Premium), Google Antigravity
5
+ * — describing that route's connection, accounts, and model catalog. Every fact
6
+ * arrives from the node half's `subscription-oauth` RPC endpoints, so this
7
+ * component holds no credential state of its own. The status poll runs only
8
+ * while a login is pending, so an idle page performs no requests.
9
+ *
10
+ * Colors resolve through `--dsw-alias-*` design tokens, so the page follows the
11
+ * active theme; every user-visible string goes through the locale-bound `t`.
12
+ *
13
+ * @module dsh-plugin-subscription-oauth/client/SubscriptionsSection
14
+ */
15
+ import type { SubscriptionsKey } from './locales.js';
16
+ import type { SubscriptionsCall } from './subscriptions-rpc.js';
17
+ /** What the node half injects into this section. */
18
+ export interface SubscriptionsSectionInjected {
19
+ /** Typed caller for the plugin's RPC endpoints. */
20
+ call: SubscriptionsCall;
21
+ /** Translate a key of this plugin's settings namespace. */
22
+ t: (key: SubscriptionsKey, params?: Record<string, unknown>) => string;
23
+ }
24
+ /** Component props: the shell supplies `close`, the plugin supplies the rest. */
25
+ export type SubscriptionsSectionProps = Partial<SubscriptionsSectionInjected> & {
26
+ /** Close the settings panel (owned by the settings shell). */
27
+ close?: () => void;
28
+ };
29
+ /**
30
+ * The Subscriptions settings section.
31
+ * @param props - section props (inject face plus the shell's `close`).
32
+ * @returns the section content.
33
+ */
34
+ export declare function SubscriptionsSection(props: SubscriptionsSectionProps): import("react").JSX.Element;