dsh-plugin-subscription-usage 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.
- package/LICENSE +22 -0
- package/README.md +38 -2
- package/cordis.patch.yml +5 -0
- package/icon.svg +9 -0
- package/lib/auth/claude-code-creds.d.ts +23 -0
- package/lib/auth/claude-code-creds.js +172 -0
- package/lib/auth/device-flow.d.ts +55 -0
- package/lib/auth/device-flow.js +177 -0
- package/lib/auth/jwt.d.ts +10 -0
- package/lib/auth/jwt.js +25 -0
- package/lib/auth/oauth-flow.d.ts +91 -0
- package/lib/auth/oauth-flow.js +227 -0
- package/lib/auth/pkce.d.ts +31 -0
- package/lib/auth/pkce.js +35 -0
- package/lib/auth/rpc.d.ts +233 -0
- package/lib/auth/rpc.js +501 -0
- package/lib/auth/store.d.ts +198 -0
- package/lib/auth/store.js +473 -0
- package/lib/client/Footer.d.ts +3 -0
- package/lib/client/Footer.js +73 -0
- package/lib/client/Gauge.d.ts +28 -0
- package/lib/client/Gauge.js +139 -0
- package/lib/client/ImageGallery.d.ts +54 -0
- package/lib/client/ImageGallery.js +112 -0
- package/lib/client/ImageGenerateToolview.d.ts +48 -0
- package/lib/client/ImageGenerateToolview.js +130 -0
- package/lib/client/Notice.d.ts +15 -0
- package/lib/client/Notice.js +93 -0
- package/lib/client/ProviderCard.d.ts +35 -0
- package/lib/client/ProviderCard.js +240 -0
- package/lib/client/SpeedSelect.d.ts +72 -0
- package/lib/client/SpeedSelect.js +214 -0
- package/lib/client/Usage.d.ts +7 -0
- package/lib/client/Usage.js +83 -0
- package/lib/client/VideoGenerateToolview.d.ts +42 -0
- package/lib/client/VideoGenerateToolview.js +172 -0
- package/lib/client/context.d.ts +16 -0
- package/lib/client/context.js +11 -0
- package/lib/client/fast-command.d.ts +2 -0
- package/lib/client/fast-command.js +4 -0
- package/lib/client/hooks.d.ts +2 -0
- package/lib/client/hooks.js +10 -0
- package/lib/client/index.d.ts +3 -0
- package/lib/client/index.js +113 -0
- package/lib/client/locales.d.ts +139 -0
- package/lib/client/locales.js +267 -0
- package/lib/client/meter.d.ts +91 -0
- package/lib/client/meter.js +196 -0
- package/lib/client/prefs.d.ts +51 -0
- package/lib/client/prefs.js +71 -0
- package/lib/client/rpc.d.ts +161 -0
- package/lib/client/rpc.js +31 -0
- package/lib/client/store.d.ts +67 -0
- package/lib/client/store.js +129 -0
- package/lib/client/styles.d.ts +7 -0
- package/lib/client/styles.js +176 -0
- package/lib/client.js +3933 -0
- package/lib/client.js.map +1 -0
- package/lib/compat.d.ts +55 -0
- package/lib/compat.js +20 -0
- package/lib/http.d.ts +134 -0
- package/lib/http.js +431 -0
- package/lib/index.d.ts +121 -0
- package/lib/index.js +13210 -0
- package/lib/json-text.d.ts +2 -0
- package/lib/json-text.js +4 -0
- package/lib/model-defaults.d.ts +23 -0
- package/lib/model-defaults.js +238 -0
- package/lib/model-speeds.d.ts +10 -0
- package/lib/model-speeds.js +48 -0
- package/lib/models-directory.d.ts +17 -0
- package/lib/models-directory.js +25 -0
- package/lib/provider-settings.d.ts +55 -0
- package/lib/provider-settings.js +215 -0
- package/lib/providers/account-preferences.d.ts +46 -0
- package/lib/providers/account-preferences.js +188 -0
- package/lib/providers/accounts.d.ts +113 -0
- package/lib/providers/accounts.js +142 -0
- package/lib/providers/antigravity-oauth-client.d.ts +7 -0
- package/lib/providers/antigravity-oauth-client.js +7 -0
- package/lib/providers/antigravity.d.ts +119 -0
- package/lib/providers/antigravity.js +552 -0
- package/lib/providers/catalog-refresh.d.ts +4 -0
- package/lib/providers/catalog-refresh.js +30 -0
- package/lib/providers/catalog-store.d.ts +47 -0
- package/lib/providers/catalog-store.js +283 -0
- package/lib/providers/claude-cli-version.d.ts +15 -0
- package/lib/providers/claude-cli-version.js +24 -0
- package/lib/providers/claude.d.ts +212 -0
- package/lib/providers/claude.js +726 -0
- package/lib/providers/codex-client-version.d.ts +8 -0
- package/lib/providers/codex-client-version.js +17 -0
- package/lib/providers/codex-search.d.ts +29 -0
- package/lib/providers/codex-search.js +198 -0
- package/lib/providers/codex.d.ts +253 -0
- package/lib/providers/codex.js +930 -0
- package/lib/providers/common.d.ts +415 -0
- package/lib/providers/common.js +679 -0
- package/lib/providers/copilot.d.ts +365 -0
- package/lib/providers/copilot.js +956 -0
- package/lib/providers/grok.d.ts +214 -0
- package/lib/providers/grok.js +750 -0
- package/lib/providers/image-pool.d.ts +30 -0
- package/lib/providers/image-pool.js +89 -0
- package/lib/providers/npm-cli-version.d.ts +56 -0
- package/lib/providers/npm-cli-version.js +98 -0
- package/lib/providers/pool-family.d.ts +56 -0
- package/lib/providers/pool-family.js +45 -0
- package/lib/providers/pool-health.d.ts +91 -0
- package/lib/providers/pool-health.js +164 -0
- package/lib/providers/pool-usage.d.ts +106 -0
- package/lib/providers/pool-usage.js +302 -0
- package/lib/providers/pool.d.ts +115 -0
- package/lib/providers/pool.js +461 -0
- package/lib/providers/rate-limit.d.ts +192 -0
- package/lib/providers/rate-limit.js +338 -0
- package/lib/providers/replay.d.ts +4 -0
- package/lib/providers/replay.js +37 -0
- package/lib/providers/reset-redemption.d.ts +20 -0
- package/lib/providers/reset-redemption.js +70 -0
- package/lib/tools/image-generate.d.ts +128 -0
- package/lib/tools/image-generate.js +438 -0
- package/lib/tools/registration.d.ts +15 -0
- package/lib/tools/registration.js +24 -0
- package/lib/tools/video-generate.d.ts +89 -0
- package/lib/tools/video-generate.js +257 -0
- package/lib/tools/x-search.d.ts +58 -0
- package/lib/tools/x-search.js +197 -0
- package/lib/translate/anthropic.d.ts +180 -0
- package/lib/translate/anthropic.js +534 -0
- package/lib/translate/antigravity-images.d.ts +11 -0
- package/lib/translate/antigravity-images.js +62 -0
- package/lib/translate/antigravity-schema.d.ts +4 -0
- package/lib/translate/antigravity-schema.js +145 -0
- package/lib/translate/antigravity-thinking.d.ts +5 -0
- package/lib/translate/antigravity-thinking.js +44 -0
- package/lib/translate/antigravity.d.ts +120 -0
- package/lib/translate/antigravity.js +412 -0
- package/lib/translate/chat-completions.d.ts +120 -0
- package/lib/translate/chat-completions.js +382 -0
- package/lib/translate/resolved.d.ts +98 -0
- package/lib/translate/resolved.js +186 -0
- package/lib/translate/responses.d.ts +192 -0
- package/lib/translate/responses.js +443 -0
- package/lib/translate/sse.d.ts +21 -0
- package/lib/translate/sse.js +62 -0
- package/lib/usage/headers.d.ts +21 -0
- package/lib/usage/headers.js +64 -0
- package/lib/usage/pace.d.ts +53 -0
- package/lib/usage/pace.js +48 -0
- package/lib/usage/service.d.ts +64 -0
- package/lib/usage/service.js +104 -0
- package/lib/usage-providers/common.d.ts +23 -0
- package/lib/usage-providers/common.js +28 -0
- package/lib/usage-providers/index.d.ts +42 -0
- package/lib/usage-providers/index.js +71 -0
- package/lib/usage-providers/keys.d.ts +11 -0
- package/lib/usage-providers/keys.js +52 -0
- package/lib/usage-providers/kimi.d.ts +4 -0
- package/lib/usage-providers/kimi.js +52 -0
- package/lib/usage-providers/ollama-cloud.d.ts +4 -0
- package/lib/usage-providers/ollama-cloud.js +42 -0
- package/lib/usage-providers/zai.d.ts +5 -0
- package/lib/usage-providers/zai.js +53 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +115 -4
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic OAuth authorization-code flow engine: one temporary loopback HTTP
|
|
3
|
+
* server per login attempt receives the provider's redirect, validates
|
|
4
|
+
* `state`, and yields the authorization `code`. A pasted callback URL or bare
|
|
5
|
+
* code can substitute for the browser redirect (`manual`), and the user can
|
|
6
|
+
* abort (`cancel`). At most one attempt per provider runs at a time.
|
|
7
|
+
*/
|
|
8
|
+
import { createServer } from 'node:http';
|
|
9
|
+
import { createPkce, randomHex, randomToken } from './pkce.js';
|
|
10
|
+
/** Default attempt lifetime: three minutes for the user to complete login. */
|
|
11
|
+
export const DEFAULT_FLOW_TIMEOUT_MS = 180_000;
|
|
12
|
+
const SUCCESS_PAGE = '<!doctype html><html><head><meta charset="utf-8"><title>Login successful</title></head>'
|
|
13
|
+
+ '<body style="font-family:sans-serif"><h1>Login successful</h1>'
|
|
14
|
+
+ '<p>You can close this tab and return to DeepSeek Harness.</p></body></html>';
|
|
15
|
+
function failurePage(detail) {
|
|
16
|
+
return '<!doctype html><html><head><meta charset="utf-8"><title>Login failed</title></head>'
|
|
17
|
+
+ `<body style="font-family:sans-serif"><h1>Login failed</h1><p>${detail.replace(/[<>&]/g, '')}</p></body></html>`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Loopback addresses one listen host covers. `localhost` resolves to ::1 or
|
|
21
|
+
* 127.0.0.1 depending on the client, and Node binds exactly one of them per
|
|
22
|
+
* listen call — a browser picking the other family gets connection-refused
|
|
23
|
+
* and the login times out, so both families must serve the callback.
|
|
24
|
+
*/
|
|
25
|
+
function listenHosts(host) {
|
|
26
|
+
return host === 'localhost' ? ['127.0.0.1', '::1'] : [host];
|
|
27
|
+
}
|
|
28
|
+
/** True when the address family does not exist on this machine (safe to skip), unlike a taken port. */
|
|
29
|
+
function familyUnavailable(error) {
|
|
30
|
+
const code = error.code;
|
|
31
|
+
return code === 'EADDRNOTAVAIL' || code === 'EPROTONOSUPPORT';
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Listen on the first port of the spec free on every loopback family;
|
|
35
|
+
* rejects when every port fails. Ephemeral ports (0) are retried so each
|
|
36
|
+
* family can be re-bound onto the first family's assigned port.
|
|
37
|
+
*/
|
|
38
|
+
async function listen(handler, spec) {
|
|
39
|
+
const hosts = listenHosts(spec.host);
|
|
40
|
+
const candidates = spec.ports.flatMap(port => (port === 0 ? [0, 0, 0] : [port]));
|
|
41
|
+
let lastError;
|
|
42
|
+
for (const candidate of candidates) {
|
|
43
|
+
const servers = [];
|
|
44
|
+
let port = candidate;
|
|
45
|
+
let unusable = false;
|
|
46
|
+
for (const host of hosts) {
|
|
47
|
+
const server = createServer(handler);
|
|
48
|
+
try {
|
|
49
|
+
await new Promise((resolve, reject) => {
|
|
50
|
+
const onError = (error) => reject(error);
|
|
51
|
+
server.once('error', onError);
|
|
52
|
+
server.listen(port, host, () => {
|
|
53
|
+
server.removeListener('error', onError);
|
|
54
|
+
resolve();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
const address = server.address();
|
|
58
|
+
if (address === null)
|
|
59
|
+
throw new Error(`callback server on ${host}:${port} has no address`);
|
|
60
|
+
if (port === 0)
|
|
61
|
+
port = address.port;
|
|
62
|
+
servers.push(server);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
server.close();
|
|
66
|
+
if (familyUnavailable(error))
|
|
67
|
+
continue;
|
|
68
|
+
lastError = error;
|
|
69
|
+
unusable = true;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (unusable || servers.length === 0) {
|
|
74
|
+
for (const server of servers)
|
|
75
|
+
server.close();
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
return { servers, port };
|
|
79
|
+
}
|
|
80
|
+
throw lastError instanceof Error
|
|
81
|
+
? lastError
|
|
82
|
+
: new Error(`callback server could not listen on ${spec.host} (ports ${spec.ports.join(', ')})`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Own the set of in-flight login attempts, keyed by provider. One attempt per
|
|
86
|
+
* provider at a time; an attempt removes itself when it settles.
|
|
87
|
+
*/
|
|
88
|
+
export class OAuthFlowManager {
|
|
89
|
+
attempts = new Map();
|
|
90
|
+
/**
|
|
91
|
+
* Whether a login attempt is running for one provider.
|
|
92
|
+
* @param provider - the provider route.
|
|
93
|
+
* @returns true while an attempt is waiting for its code.
|
|
94
|
+
*/
|
|
95
|
+
isBusy(provider) {
|
|
96
|
+
return this.attempts.has(provider);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The pending attempt for one provider, when any.
|
|
100
|
+
* @param provider - the provider route.
|
|
101
|
+
* @returns the in-flight attempt, or `undefined`.
|
|
102
|
+
*/
|
|
103
|
+
pending(provider) {
|
|
104
|
+
return this.attempts.get(provider);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Start a login attempt: mint PKCE/state, open the loopback callback
|
|
108
|
+
* server, and build the authorize URL.
|
|
109
|
+
* @param provider - the provider route (one attempt at a time).
|
|
110
|
+
* @param spec - static flow facts for this provider.
|
|
111
|
+
* @returns the live attempt; its `waitCode()` settles the login.
|
|
112
|
+
* @throws when an attempt is already running or no callback port is free.
|
|
113
|
+
*/
|
|
114
|
+
async start(provider, spec) {
|
|
115
|
+
if (this.attempts.has(provider)) {
|
|
116
|
+
throw new Error(`a ${provider} login attempt is already in progress`);
|
|
117
|
+
}
|
|
118
|
+
const input = {
|
|
119
|
+
redirectUri: '',
|
|
120
|
+
state: randomToken(32),
|
|
121
|
+
pkce: createPkce(),
|
|
122
|
+
nonce: randomHex(8),
|
|
123
|
+
};
|
|
124
|
+
const timeoutMs = spec.timeoutMs ?? DEFAULT_FLOW_TIMEOUT_MS;
|
|
125
|
+
let resolveCode;
|
|
126
|
+
let rejectCode;
|
|
127
|
+
const codePromise = new Promise((resolve, reject) => {
|
|
128
|
+
resolveCode = resolve;
|
|
129
|
+
rejectCode = reject;
|
|
130
|
+
});
|
|
131
|
+
let settled = false;
|
|
132
|
+
let timer;
|
|
133
|
+
let servers = [];
|
|
134
|
+
const handler = (request, response) => {
|
|
135
|
+
const url = new URL(request.url ?? '/', 'http://localhost');
|
|
136
|
+
if (url.pathname !== spec.callbackPath) {
|
|
137
|
+
response.writeHead(404, { 'content-type': 'text/plain' });
|
|
138
|
+
response.end('not found');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const errorDescription = url.searchParams.get('error_description') ?? url.searchParams.get('error');
|
|
142
|
+
if (errorDescription !== null) {
|
|
143
|
+
response.writeHead(200, { 'content-type': 'text/html' });
|
|
144
|
+
response.end(failurePage(errorDescription));
|
|
145
|
+
settle(new Error(`authorization failed: ${errorDescription}`));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (url.searchParams.get('state') !== input.state) {
|
|
149
|
+
// A stray or replayed redirect must not kill the real attempt.
|
|
150
|
+
response.writeHead(400, { 'content-type': 'text/plain' });
|
|
151
|
+
response.end('state mismatch');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const code = url.searchParams.get('code');
|
|
155
|
+
if (code === null || code.length === 0) {
|
|
156
|
+
response.writeHead(400, { 'content-type': 'text/plain' });
|
|
157
|
+
response.end('missing authorization code');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
response.writeHead(200, { 'content-type': 'text/html' });
|
|
161
|
+
response.end(SUCCESS_PAGE);
|
|
162
|
+
settle(undefined, code);
|
|
163
|
+
};
|
|
164
|
+
const settle = (error, code) => {
|
|
165
|
+
if (settled)
|
|
166
|
+
return;
|
|
167
|
+
settled = true;
|
|
168
|
+
if (timer !== undefined)
|
|
169
|
+
clearTimeout(timer);
|
|
170
|
+
for (const server of servers) {
|
|
171
|
+
server.close();
|
|
172
|
+
server.closeAllConnections();
|
|
173
|
+
}
|
|
174
|
+
this.attempts.delete(provider);
|
|
175
|
+
if (error !== undefined)
|
|
176
|
+
rejectCode(error);
|
|
177
|
+
else if (code !== undefined)
|
|
178
|
+
resolveCode(code);
|
|
179
|
+
};
|
|
180
|
+
const bound = await listen(handler, spec.listen);
|
|
181
|
+
servers = bound.servers;
|
|
182
|
+
input.redirectUri = `http://${spec.listen.host}:${bound.port}${spec.callbackPath}`;
|
|
183
|
+
timer = setTimeout(() => {
|
|
184
|
+
settle(new Error(`login timed out after ${Math.round(timeoutMs / 1000)}s`));
|
|
185
|
+
}, timeoutMs);
|
|
186
|
+
timer.unref();
|
|
187
|
+
const attempt = {
|
|
188
|
+
authorizeUrl: spec.buildAuthorizeUrl(input),
|
|
189
|
+
redirectUri: input.redirectUri,
|
|
190
|
+
pkce: input.pkce,
|
|
191
|
+
state: input.state,
|
|
192
|
+
waitCode: () => codePromise,
|
|
193
|
+
manual(rawInput) {
|
|
194
|
+
if (settled)
|
|
195
|
+
throw new Error(`the ${provider} login attempt already finished`);
|
|
196
|
+
const trimmed = rawInput.trim();
|
|
197
|
+
let code;
|
|
198
|
+
let pastedState;
|
|
199
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
200
|
+
const url = new URL(trimmed);
|
|
201
|
+
code = url.searchParams.get('code') ?? undefined;
|
|
202
|
+
pastedState = url.searchParams.get('state') ?? undefined;
|
|
203
|
+
}
|
|
204
|
+
else if (trimmed.includes('code=')) {
|
|
205
|
+
const params = new URLSearchParams(trimmed);
|
|
206
|
+
code = params.get('code') ?? undefined;
|
|
207
|
+
pastedState = params.get('state') ?? undefined;
|
|
208
|
+
}
|
|
209
|
+
else if (trimmed.length > 0 && !/\s/.test(trimmed)) {
|
|
210
|
+
code = trimmed;
|
|
211
|
+
}
|
|
212
|
+
if (code === undefined || code.length === 0) {
|
|
213
|
+
throw new Error('no authorization code found in the pasted input');
|
|
214
|
+
}
|
|
215
|
+
if (pastedState !== undefined && pastedState !== input.state) {
|
|
216
|
+
throw new Error('state mismatch: the pasted URL belongs to a different login attempt');
|
|
217
|
+
}
|
|
218
|
+
settle(undefined, code);
|
|
219
|
+
},
|
|
220
|
+
cancel() {
|
|
221
|
+
settle(new Error('login cancelled'));
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
this.attempts.set(provider, attempt);
|
|
225
|
+
return attempt;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** PKCE (RFC 7636) and random-token helpers for the OAuth login flows. */
|
|
2
|
+
/** One PKCE verifier/challenge pair; the verifier stays local until the token exchange. */
|
|
3
|
+
export interface PkcePair {
|
|
4
|
+
/** High-entropy secret sent as `code_verifier` at the token endpoint. */
|
|
5
|
+
verifier: string;
|
|
6
|
+
/** S256 digest of the verifier, sent as `code_challenge` at the authorize endpoint. */
|
|
7
|
+
challenge: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Base64url-encode without padding.
|
|
11
|
+
* @param buffer - raw bytes.
|
|
12
|
+
* @returns the RFC 4648 §5 encoding.
|
|
13
|
+
*/
|
|
14
|
+
export declare function base64url(buffer: Buffer): string;
|
|
15
|
+
/**
|
|
16
|
+
* Mint a fresh PKCE pair (32-byte verifier, S256 challenge).
|
|
17
|
+
* @returns the pair for one authorization attempt.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createPkce(): PkcePair;
|
|
20
|
+
/**
|
|
21
|
+
* Mint a URL-safe random token (default 16 bytes) for OAuth `state`.
|
|
22
|
+
* @param bytes - entropy length.
|
|
23
|
+
* @returns base64url-encoded random bytes.
|
|
24
|
+
*/
|
|
25
|
+
export declare function randomToken(bytes?: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* Mint lowercase-hex random bytes (for Grok's `nonce` parameter).
|
|
28
|
+
* @param bytes - entropy length; the hex string is twice as long.
|
|
29
|
+
* @returns hex-encoded random bytes.
|
|
30
|
+
*/
|
|
31
|
+
export declare function randomHex(bytes?: number): string;
|
package/lib/auth/pkce.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** PKCE (RFC 7636) and random-token helpers for the OAuth login flows. */
|
|
2
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
3
|
+
/**
|
|
4
|
+
* Base64url-encode without padding.
|
|
5
|
+
* @param buffer - raw bytes.
|
|
6
|
+
* @returns the RFC 4648 §5 encoding.
|
|
7
|
+
*/
|
|
8
|
+
export function base64url(buffer) {
|
|
9
|
+
return buffer.toString('base64url');
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Mint a fresh PKCE pair (32-byte verifier, S256 challenge).
|
|
13
|
+
* @returns the pair for one authorization attempt.
|
|
14
|
+
*/
|
|
15
|
+
export function createPkce() {
|
|
16
|
+
const verifier = base64url(randomBytes(32));
|
|
17
|
+
const challenge = base64url(createHash('sha256').update(verifier).digest());
|
|
18
|
+
return { verifier, challenge };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Mint a URL-safe random token (default 16 bytes) for OAuth `state`.
|
|
22
|
+
* @param bytes - entropy length.
|
|
23
|
+
* @returns base64url-encoded random bytes.
|
|
24
|
+
*/
|
|
25
|
+
export function randomToken(bytes = 32) {
|
|
26
|
+
return base64url(randomBytes(bytes));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Mint lowercase-hex random bytes (for Grok's `nonce` parameter).
|
|
30
|
+
* @param bytes - entropy length; the hex string is twice as long.
|
|
31
|
+
* @returns hex-encoded random bytes.
|
|
32
|
+
*/
|
|
33
|
+
export function randomHex(bytes = 8) {
|
|
34
|
+
return randomBytes(bytes).toString('hex');
|
|
35
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `subscriptions-auth` host RPC endpoints the web Settings page drives.
|
|
3
|
+
* They are mounted as exact POST Fetch routes on the shared `/api` channel
|
|
4
|
+
* (`/api/subscriptions-auth.<endpoint>`) and registered only when a host
|
|
5
|
+
* `connection` service exists (the web profile); headless compositions load
|
|
6
|
+
* the plugin without it. All business outcomes are returned as RpcResult
|
|
7
|
+
* values; handlers never throw.
|
|
8
|
+
*/
|
|
9
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
10
|
+
import type { RpcResult } from '../compat.js';
|
|
11
|
+
import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment';
|
|
12
|
+
import { type ProviderId } from './store.js';
|
|
13
|
+
import type { ProviderUsage } from '../providers/common.js';
|
|
14
|
+
import type { UsageReadReason } from '../usage/service.js';
|
|
15
|
+
import type { KeyProviderId, KeyProviderView, UsageProviderId } from '../usage-providers/index.js';
|
|
16
|
+
import type { ProxyConfigView, ProxyDraft, ProxyInput, ProxyTestResult } from '../http.js';
|
|
17
|
+
import type { CliVersion } from '../providers/npm-cli-version.js';
|
|
18
|
+
/** Internal endpoint dispatcher used after the host authenticates an exact Fetch route. */
|
|
19
|
+
export type SubscriptionsRpcHandler = (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<RpcResult<unknown>>;
|
|
20
|
+
/**
|
|
21
|
+
* Endpoint-name prefix under the shared `/api` channel: endpoint `status`
|
|
22
|
+
* is served at `/api/subscriptions-auth.status`. The browser half calls
|
|
23
|
+
* `rpc.call('/api', 'subscriptions-auth.status', payload)`.
|
|
24
|
+
*/
|
|
25
|
+
export declare const SUBSCRIPTIONS_AUTH_PREFIX = "subscriptions-auth.";
|
|
26
|
+
/**
|
|
27
|
+
* Every endpoint {@link dispatch} answers; each gets one exact Fetch route.
|
|
28
|
+
* Kept in one place so the route table and the switch cannot drift apart.
|
|
29
|
+
*/
|
|
30
|
+
export declare const SUBSCRIPTIONS_AUTH_ENDPOINTS: readonly ["providerSettings", "setProviderSettings", "status", "login", "manual", "cancel", "logout", "setDefault", "usage", "image", "video", "prepareReset", "consumeReset", "speed", "setSpeed", "proxyGet", "proxySet", "proxyTest", "keysSet", "keysRemove", "modelDefaults", "setModelDefault"];
|
|
31
|
+
/** Decoded image bytes returned by the `image` endpoint. */
|
|
32
|
+
export interface ImageBytesResult {
|
|
33
|
+
mediaType: string;
|
|
34
|
+
dataBase64: string;
|
|
35
|
+
}
|
|
36
|
+
/** Decoded video bytes returned by the `video` endpoint. */
|
|
37
|
+
export interface VideoBytesResult {
|
|
38
|
+
mediaType: string;
|
|
39
|
+
dataBase64: string;
|
|
40
|
+
}
|
|
41
|
+
/** One session's speed choice: standard routing or the fast (priority) tier. */
|
|
42
|
+
export type SpeedTier = 'standard' | 'fast';
|
|
43
|
+
/** `speed` endpoint value: the session's choice plus the visibility list. */
|
|
44
|
+
export interface SpeedState {
|
|
45
|
+
/** Explicit session speed, or the subagent's model default when supplied. */
|
|
46
|
+
tier: SpeedTier;
|
|
47
|
+
/** Codex model ids whose catalog advertises a fast tier. */
|
|
48
|
+
fastModels: string[];
|
|
49
|
+
}
|
|
50
|
+
/** Session overrides plus persisted subagent model defaults. */
|
|
51
|
+
export interface SpeedController {
|
|
52
|
+
/** Pass the current Codex model to resolve a subagent's default speed. */
|
|
53
|
+
speed(sessionId: string, model?: string): Promise<SpeedState>;
|
|
54
|
+
/** Set the session override and remember it for this model across restarts when supplied. */
|
|
55
|
+
setSpeed(sessionId: string, tier: SpeedTier, model?: string): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
/** One logged-in account, as rendered by the Settings page. */
|
|
58
|
+
export interface AccountStatus {
|
|
59
|
+
/** Stable account key (store identity). */
|
|
60
|
+
key: string;
|
|
61
|
+
/** Display identity (email / login), when known. */
|
|
62
|
+
account?: string;
|
|
63
|
+
/** Epoch milliseconds at which the stored access token expires. */
|
|
64
|
+
expiresAt?: number;
|
|
65
|
+
/** Plan name the session carries (codex planType / claude subscriptionType), when known. */
|
|
66
|
+
plan?: string;
|
|
67
|
+
/** Whether direct (non-pool) routes serve this account. */
|
|
68
|
+
isDefault: boolean;
|
|
69
|
+
}
|
|
70
|
+
/** Login state of one provider, as rendered by the Settings page. */
|
|
71
|
+
export interface ProviderStatus {
|
|
72
|
+
/** Short name for compact surfaces; attached by the status RPC. */
|
|
73
|
+
shortName?: string;
|
|
74
|
+
/** Whether the provider reports usage at all; attached by the status RPC. */
|
|
75
|
+
metered?: boolean;
|
|
76
|
+
/** The account that served this session's latest request, else the provider's latest. */
|
|
77
|
+
servingAccount?: string;
|
|
78
|
+
/** Model count of the saved catalog. */
|
|
79
|
+
catalogSize?: number;
|
|
80
|
+
/** Saved picker visibility, attached by the status RPC from local preferences. */
|
|
81
|
+
visibleModels?: string[];
|
|
82
|
+
displayName?: string;
|
|
83
|
+
/** Whether a login attempt is currently waiting for its code. */
|
|
84
|
+
busy: boolean;
|
|
85
|
+
/** Logged-in accounts, default first. */
|
|
86
|
+
accounts: AccountStatus[];
|
|
87
|
+
/** The last login error, shown until the next success. */
|
|
88
|
+
detail?: string;
|
|
89
|
+
/** The CLI version this route presents (Codex, Claude), and where it came from. */
|
|
90
|
+
clientVersion?: CliVersion;
|
|
91
|
+
}
|
|
92
|
+
/** How a Claude login should acquire credentials (other providers ignore it). */
|
|
93
|
+
export type LoginMethod = 'oauth' | 'keychain';
|
|
94
|
+
/** Proxy config operations behind the `proxyGet/proxySet/proxyTest` endpoints. */
|
|
95
|
+
export interface ProxyConfigController {
|
|
96
|
+
/** Current proxy configuration (secrets omitted). */
|
|
97
|
+
get(): Promise<ProxyConfigView>;
|
|
98
|
+
/** Validate, persist, and apply one config. */
|
|
99
|
+
set(input: ProxyInput): Promise<ProxyConfigView>;
|
|
100
|
+
/** Probe one destination through the draft (unsaved) or stored proxy. */
|
|
101
|
+
test(payload: {
|
|
102
|
+
url?: string;
|
|
103
|
+
proxy?: ProxyDraft;
|
|
104
|
+
}): Promise<ProxyTestResult>;
|
|
105
|
+
}
|
|
106
|
+
/** One model's default-effort picker state, as rendered by the Settings page. */
|
|
107
|
+
export interface ModelDefaultView {
|
|
108
|
+
/** Wire model id. */
|
|
109
|
+
id: string;
|
|
110
|
+
/** Human-readable display name. */
|
|
111
|
+
name: string;
|
|
112
|
+
/** Advertised effort levels, in catalog order (empty when the model has no reasoning). */
|
|
113
|
+
efforts: {
|
|
114
|
+
id: string;
|
|
115
|
+
name: string;
|
|
116
|
+
}[];
|
|
117
|
+
/** The user-configured default effort, when set. */
|
|
118
|
+
configured?: string;
|
|
119
|
+
}
|
|
120
|
+
/** One provider's default-effort picker state. */
|
|
121
|
+
export interface ModelDefaultsCatalog {
|
|
122
|
+
/** The subscription provider route. */
|
|
123
|
+
provider: ProviderId;
|
|
124
|
+
/** Models the picker can configure, in catalog order. */
|
|
125
|
+
models: ModelDefaultView[];
|
|
126
|
+
}
|
|
127
|
+
/** Per-account catalog returned by providerSettings, including providers without usage APIs. */
|
|
128
|
+
export interface ProviderAccountCatalog {
|
|
129
|
+
key: string;
|
|
130
|
+
/** Original identity; the editable alias lives in settings.accounts[key].alias. */
|
|
131
|
+
label: string;
|
|
132
|
+
models: {
|
|
133
|
+
id: string;
|
|
134
|
+
name: string;
|
|
135
|
+
}[];
|
|
136
|
+
unavailable?: boolean;
|
|
137
|
+
}
|
|
138
|
+
/** Provider and account preference operations behind providerSettings/setProviderSettings. */
|
|
139
|
+
export interface ProviderSettingsController {
|
|
140
|
+
/** Local preferences only: status must not fetch a model catalog. */
|
|
141
|
+
displayName?(provider: ProviderId): string | undefined;
|
|
142
|
+
/** Local picker selection, without triggering model discovery. */
|
|
143
|
+
visibleModels?(provider: ProviderId): string[] | undefined;
|
|
144
|
+
/** Model count of the saved catalog, read from disk without discovery. */
|
|
145
|
+
catalogSize?(provider: ProviderId): Promise<number | undefined>;
|
|
146
|
+
get(provider: ProviderId, force: boolean): Promise<unknown>;
|
|
147
|
+
set(provider: ProviderId, settings: unknown): Promise<void>;
|
|
148
|
+
}
|
|
149
|
+
export interface ModelDefaultsController {
|
|
150
|
+
/** Per-provider picker state for the Settings page. */
|
|
151
|
+
catalog(force?: boolean): Promise<ModelDefaultsCatalog[]>;
|
|
152
|
+
/** Set one model's configured default effort; undefined clears the override. */
|
|
153
|
+
set(provider: ProviderId, model: string, effort: string | undefined): Promise<void>;
|
|
154
|
+
}
|
|
155
|
+
/** Provider-agnostic auth operations the RPC handler delegates to. */
|
|
156
|
+
export interface AuthController {
|
|
157
|
+
prepareReset?(account: string, signal: AbortSignal): Promise<unknown>;
|
|
158
|
+
consumeReset?(account: string, ticket: string, signal: AbortSignal): Promise<void>;
|
|
159
|
+
/** Current status of one provider. */
|
|
160
|
+
status(provider: ProviderId, sessionId?: string): Promise<ProviderStatus>;
|
|
161
|
+
/** Bumped on every usage or serving-account change, so clients know to re-read. */
|
|
162
|
+
usageRevision?(): number;
|
|
163
|
+
/** Whether this provider has a usage endpoint in this composition. */
|
|
164
|
+
hasUsage?(provider: ProviderId): boolean;
|
|
165
|
+
/** Key providers (Z.ai, Ollama Cloud, Kimi Code) with whether a key is stored. */
|
|
166
|
+
keyProviders?(): Promise<KeyProviderView[]>;
|
|
167
|
+
/** Check a key with one usage read, then store it. */
|
|
168
|
+
setKey?(id: KeyProviderId, key: string): Promise<void>;
|
|
169
|
+
removeKey?(id: KeyProviderId): Promise<void>;
|
|
170
|
+
/**
|
|
171
|
+
* Start a background login attempt.
|
|
172
|
+
* @param provider - the provider route.
|
|
173
|
+
* @param method - Claude only: force the OAuth browser flow or the Claude
|
|
174
|
+
* Code credential import; omitted keeps the auto behavior (import when
|
|
175
|
+
* available, else OAuth).
|
|
176
|
+
* @returns the authorize URL for the user's browser; device-flow providers
|
|
177
|
+
* (copilot) also return the `userCode` the user types at that URL.
|
|
178
|
+
* @throws when an attempt is already running for this provider.
|
|
179
|
+
*/
|
|
180
|
+
login(provider: ProviderId, method?: LoginMethod): Promise<{
|
|
181
|
+
authorizeUrl: string;
|
|
182
|
+
userCode?: string;
|
|
183
|
+
}>;
|
|
184
|
+
/**
|
|
185
|
+
* Feed a pasted callback URL or bare code into the pending attempt.
|
|
186
|
+
* @throws when no attempt is pending or the input is unusable.
|
|
187
|
+
*/
|
|
188
|
+
manual(provider: ProviderId, input: string): Promise<void>;
|
|
189
|
+
/** Abort the pending attempt; a no-op when none is pending. */
|
|
190
|
+
cancel(provider: ProviderId): Promise<void>;
|
|
191
|
+
/** Delete one account's stored session. */
|
|
192
|
+
logout(provider: ProviderId, account: string): Promise<void>;
|
|
193
|
+
/** Pin the account direct (non-pool) routes serve. */
|
|
194
|
+
setDefault(provider: ProviderId, account: string): Promise<void>;
|
|
195
|
+
/**
|
|
196
|
+
* Current subscription usage of one account.
|
|
197
|
+
* @param signal - caller cancellation from the RPC transport.
|
|
198
|
+
* @param force - bypass a fresh cached snapshot for an honest re-check
|
|
199
|
+
* (the manual Refresh button); a live failure cooldown still applies.
|
|
200
|
+
* @returns `{ supported: false }` when the provider has no usage endpoint.
|
|
201
|
+
* @throws when logged out or the usage lookup fails.
|
|
202
|
+
*/
|
|
203
|
+
usage(provider: UsageProviderId, account: string, signal: AbortSignal, force?: boolean, reason?: UsageReadReason): Promise<ProviderUsage>;
|
|
204
|
+
/**
|
|
205
|
+
* Read one image attachment's bytes for inline display.
|
|
206
|
+
* @param ref - the full durable reference (`readImage` verifies against it).
|
|
207
|
+
* @param signal - caller cancellation from the RPC transport.
|
|
208
|
+
* @returns the media type and base64-encoded bytes.
|
|
209
|
+
* @throws when no attachment service is mounted or the read fails.
|
|
210
|
+
*/
|
|
211
|
+
readImage(ref: ImageAttachmentRef, signal: AbortSignal): Promise<ImageBytesResult>;
|
|
212
|
+
/**
|
|
213
|
+
* Read one generated video's bytes for inline playback.
|
|
214
|
+
* @param name - bare MP4 file name inside the plugin's videos directory
|
|
215
|
+
* (validated against {@link VIDEO_NAME_PATTERN}; never a path).
|
|
216
|
+
* @param signal - caller cancellation from the RPC transport.
|
|
217
|
+
* @returns the media type and base64-encoded bytes.
|
|
218
|
+
* @throws when the file does not exist or cannot be read.
|
|
219
|
+
*/
|
|
220
|
+
readVideo(name: string, signal: AbortSignal): Promise<VideoBytesResult>;
|
|
221
|
+
}
|
|
222
|
+
/** Payload carried no usable provider id — an RPC client bug, not a server failure. */
|
|
223
|
+
export declare class BadRequest extends Error {
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Register the `/subscriptions-auth` RPC channel when a host connection exists.
|
|
227
|
+
* @param ctx - the plugin context (headless profiles have no `connection`).
|
|
228
|
+
* @param controller - the auth operations backing the endpoints.
|
|
229
|
+
* @param speed - the per-session speed-tier state backing the Speed toggle.
|
|
230
|
+
* @param proxy - optional proxy-config controller backing `proxyGet`/`proxySet`/`proxyTest`.
|
|
231
|
+
* @param modelDefaults - optional per-model default-effort state backing `modelDefaults`/`setModelDefault`.
|
|
232
|
+
*/
|
|
233
|
+
export declare function registerAuthRpc(ctx: Context, controller: AuthController, speed: SpeedController, proxy?: ProxyConfigController | undefined, modelDefaults?: ModelDefaultsController | undefined, providerSettings?: ProviderSettingsController | undefined): void;
|