@young1lin/dsh-gpt-sub 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/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +268 -0
- package/client.js +908 -0
- package/cordis.patch.yml +44 -0
- package/lib/index.d.ts +52 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +581 -0
- package/lib/index.js.map +1 -0
- package/lib/jwt.d.ts +15 -0
- package/lib/jwt.d.ts.map +1 -0
- package/lib/jwt.js +29 -0
- package/lib/jwt.js.map +1 -0
- package/lib/proxy-config.d.ts +91 -0
- package/lib/proxy-config.d.ts.map +1 -0
- package/lib/proxy-config.js +213 -0
- package/lib/proxy-config.js.map +1 -0
- package/lib/proxy-probe.d.ts +47 -0
- package/lib/proxy-probe.d.ts.map +1 -0
- package/lib/proxy-probe.js +51 -0
- package/lib/proxy-probe.js.map +1 -0
- package/lib/proxy-routing.d.ts +111 -0
- package/lib/proxy-routing.d.ts.map +1 -0
- package/lib/proxy-routing.js +171 -0
- package/lib/proxy-routing.js.map +1 -0
- package/lib/quota-route.d.ts +85 -0
- package/lib/quota-route.d.ts.map +1 -0
- package/lib/quota-route.js +105 -0
- package/lib/quota-route.js.map +1 -0
- package/lib/reset-credits.d.ts +64 -0
- package/lib/reset-credits.d.ts.map +1 -0
- package/lib/reset-credits.js +84 -0
- package/lib/reset-credits.js.map +1 -0
- package/lib/token-store.d.ts +107 -0
- package/lib/token-store.d.ts.map +1 -0
- package/lib/token-store.js +228 -0
- package/lib/token-store.js.map +1 -0
- package/lib/types.d.ts +18 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/lib/usage.d.ts +92 -0
- package/lib/usage.d.ts.map +1 -0
- package/lib/usage.js +106 -0
- package/lib/usage.js.map +1 -0
- package/package.json +82 -0
- package/src/index.ts +685 -0
- package/src/jwt.ts +26 -0
- package/src/proxy-config.ts +217 -0
- package/src/proxy-probe.ts +81 -0
- package/src/proxy-routing.ts +205 -0
- package/src/quota-route.ts +163 -0
- package/src/reset-credits.ts +128 -0
- package/src/token-store.ts +309 -0
- package/src/types.ts +17 -0
- package/src/usage.ts +154 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct ChatGPT/Codex subscription access for DeepSeek Harness.
|
|
3
|
+
*
|
|
4
|
+
* This plugin owns no transport. pi-ai already ships an `openai-codex`
|
|
5
|
+
* provider that knows the Codex wire format -- it sets `store`, derives
|
|
6
|
+
* `chatgpt-account-id` from the access token's own JWT claim, and speaks the
|
|
7
|
+
* codex-responses protocol -- but it authenticates only through OAuth, and
|
|
8
|
+
* `dsh-llm-pi-ai` runs no login flow and holds no OAuth store. Naming a
|
|
9
|
+
* credential on the route grafts an api-key method beside the provider's own,
|
|
10
|
+
* which is the seam this plugin fills: it keeps a live Codex access token in
|
|
11
|
+
* the harness credential store, refreshing it from `~/.codex/auth.json` before
|
|
12
|
+
* it expires.
|
|
13
|
+
*
|
|
14
|
+
* The result needs no loopback port, no shared local key, and no external
|
|
15
|
+
* proxy binary. The `codex` CLI still owns the login.
|
|
16
|
+
*
|
|
17
|
+
* @module dsh-gpt-sub
|
|
18
|
+
*/
|
|
19
|
+
import { credentialRef } from '@deepseek-ai/dsh-credentials';
|
|
20
|
+
import z from '@deepseek-ai/schemastery';
|
|
21
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
22
|
+
import { homedir } from 'node:os';
|
|
23
|
+
import { randomUUID } from 'node:crypto';
|
|
24
|
+
import { dirname, join } from 'node:path';
|
|
25
|
+
import { crossOrigin, loadStateOverride, readJsonObject, redactProxyUrl, saveStateOverride, validateProxyUrl, } from "./proxy-config.js";
|
|
26
|
+
import { probeProxy } from "./proxy-probe.js";
|
|
27
|
+
import { consumeResetCredit, listResetCredits } from "./reset-credits.js";
|
|
28
|
+
import { installProxyRouting } from "./proxy-routing.js";
|
|
29
|
+
import { QuotaSource } from "./quota-route.js";
|
|
30
|
+
import { TokenStore } from "./token-store.js";
|
|
31
|
+
import { fetchUsage, reportedWindows } from "./usage.js";
|
|
32
|
+
/** Cordis plugin name. */
|
|
33
|
+
export const name = 'gpt-sub';
|
|
34
|
+
/**
|
|
35
|
+
* Required services. Without this declaration cordis's context proxy throws
|
|
36
|
+
* `cannot get property "credentials" without inject` the moment the plugin
|
|
37
|
+
* reads `ctx.credentials`, and the plugin sits dead with no retry.
|
|
38
|
+
*/
|
|
39
|
+
export const inject = ['credentials', 'webServer'];
|
|
40
|
+
/** Where the browser half polls for the quota reading. */
|
|
41
|
+
const QUOTA_ROUTE = '/gpt-sub/quota';
|
|
42
|
+
/** Where the browser half reads auth/proxy status the panel renders. */
|
|
43
|
+
const STATUS_ROUTE = '/gpt-sub/status';
|
|
44
|
+
/** Where the browser half switches the live proxy at runtime. */
|
|
45
|
+
const PROXY_ROUTE = '/gpt-sub/proxy';
|
|
46
|
+
/** Where the browser half probes a candidate proxy before saving it. */
|
|
47
|
+
const PROXY_TEST_ROUTE = '/gpt-sub/proxy/test';
|
|
48
|
+
/** Where the browser half switches the credential file at runtime. */
|
|
49
|
+
const AUTH_ROUTE = '/gpt-sub/auth';
|
|
50
|
+
/** Where the browser half lists a directory to pick a credential file. */
|
|
51
|
+
const AUTH_BROWSE_ROUTE = '/gpt-sub/auth/browse';
|
|
52
|
+
/** Ceiling on one directory listing, so a huge folder cannot flood the page. */
|
|
53
|
+
const BROWSE_LIMIT = 500;
|
|
54
|
+
/** Ceiling on one connectivity probe. */
|
|
55
|
+
const PROBE_TIMEOUT_MS = 15_000;
|
|
56
|
+
/** Where the browser half lists rate-limit reset credits. */
|
|
57
|
+
const RESET_CREDITS_ROUTE = '/gpt-sub/reset-credits';
|
|
58
|
+
/** Where the browser half consumes one rate-limit reset credit. */
|
|
59
|
+
const RESET_CREDITS_CONSUME_ROUTE = '/gpt-sub/reset-credits/consume';
|
|
60
|
+
/** Ceiling on one reset-credit upstream call, matching the codex CLI's 10s. */
|
|
61
|
+
const RESET_CREDITS_TIMEOUT_MS = 10_000;
|
|
62
|
+
/**
|
|
63
|
+
* How a proxy URL appears in logs: credentials masked, direct named as such.
|
|
64
|
+
*
|
|
65
|
+
* @param url - the proxy URL as configured or saved.
|
|
66
|
+
* @returns the log-safe rendering.
|
|
67
|
+
*/
|
|
68
|
+
const displayProxy = (url) => (url === '' ? 'direct' : redactProxyUrl(url));
|
|
69
|
+
/**
|
|
70
|
+
* Plugin config schema.
|
|
71
|
+
*
|
|
72
|
+
* Typed input-then-output: every field has a default, so a config document may
|
|
73
|
+
* supply any subset, and what comes back out is a fully resolved `ConfigShape`.
|
|
74
|
+
*/
|
|
75
|
+
export const Config = z.object({
|
|
76
|
+
// Empty means a direct connection. The token endpoint is as unreachable as
|
|
77
|
+
// the model endpoint on a machine that needs a proxy, so this covers both.
|
|
78
|
+
proxyUrl: z.string().default(''),
|
|
79
|
+
authFile: z.string().default('~/.codex/auth.json'),
|
|
80
|
+
refreshMarginMinutes: z.number().default(30),
|
|
81
|
+
// The credential the provider route names in settings.yaml. Both sides must
|
|
82
|
+
// agree, so it is configurable rather than hard-coded.
|
|
83
|
+
tokenRef: z.string().role('credential-ref').default('CODEX_NATIVE_TOKEN'),
|
|
84
|
+
// How often to re-read auth.json and republish. Well under the refresh
|
|
85
|
+
// margin, so a token nearing expiry is always renewed before a request needs
|
|
86
|
+
// it. Each tick is a file read; it reaches the network only to refresh.
|
|
87
|
+
syncIntervalMinutes: z.number().default(10),
|
|
88
|
+
// How many times a single request may be re-attempted after the connection
|
|
89
|
+
// drops. The link to the proxy fails at random, and each failure otherwise
|
|
90
|
+
// reaches the user as a bare `fetch failed` mid-conversation.
|
|
91
|
+
bootstrapRetries: z.number().default(3),
|
|
92
|
+
// Where the settings panel's proxy edits are persisted. When this file
|
|
93
|
+
// exists, its proxyUrl wins over the config's -- a page edit survives
|
|
94
|
+
// restarts without touching cordis config layers.
|
|
95
|
+
stateFile: z.string().default('~/.dsh/gpt-sub.json'),
|
|
96
|
+
});
|
|
97
|
+
/** Expand a leading `~` against the current user's home directory. */
|
|
98
|
+
function expandHome(path) {
|
|
99
|
+
return path.startsWith('~/') ? join(homedir(), path.slice(2)) : path;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Publish the current Codex access token into the credential store, and keep
|
|
103
|
+
* publishing it for as long as the plugin is loaded.
|
|
104
|
+
*
|
|
105
|
+
* Async on purpose: cordis awaits a thenable returned from plugin startup and
|
|
106
|
+
* collects the function it resolves to as the fiber's disposer. A missing or
|
|
107
|
+
* unreadable `auth.json` therefore fails the fiber visibly at start instead of
|
|
108
|
+
* leaving a route configured against a credential nothing maintains.
|
|
109
|
+
*
|
|
110
|
+
* @param ctx - the cordis context.
|
|
111
|
+
* @param config - validated plugin configuration.
|
|
112
|
+
* @returns the teardown that stops the sync timer and closes the dispatcher.
|
|
113
|
+
*/
|
|
114
|
+
export async function apply(ctx, config) {
|
|
115
|
+
const stateFile = expandHome(config.stateFile);
|
|
116
|
+
// A panel-saved override wins over the config value; a corrupt file falls
|
|
117
|
+
// back to the config rather than failing the plugin over a status file.
|
|
118
|
+
// One state file carries both page-side choices; a field present in it wins
|
|
119
|
+
// over the config value, and a differing choice is logged at start.
|
|
120
|
+
const savedState = await loadStateOverride(stateFile);
|
|
121
|
+
const savedProxy = savedState?.proxyUrl;
|
|
122
|
+
if (savedProxy !== undefined && savedProxy !== config.proxyUrl) {
|
|
123
|
+
ctx.logger.info('gpt-sub: proxy override %s from %s replaces config proxyUrl %s', displayProxy(savedProxy), stateFile, displayProxy(config.proxyUrl));
|
|
124
|
+
}
|
|
125
|
+
let proxyUrl = savedProxy ?? config.proxyUrl;
|
|
126
|
+
let overridden = savedProxy !== undefined;
|
|
127
|
+
const savedAuthFile = savedState?.authFile;
|
|
128
|
+
if (savedAuthFile !== undefined && savedAuthFile !== config.authFile) {
|
|
129
|
+
ctx.logger.info('gpt-sub: credential file %s from %s replaces config authFile %s', savedAuthFile, stateFile, config.authFile);
|
|
130
|
+
}
|
|
131
|
+
let authFileDisplay = savedAuthFile ?? config.authFile;
|
|
132
|
+
let authOverridden = savedAuthFile !== undefined;
|
|
133
|
+
// Mirror of what the state file currently holds. Every save rewrites the
|
|
134
|
+
// whole object, so recording one choice never erases the other.
|
|
135
|
+
const persisted = {};
|
|
136
|
+
if (overridden)
|
|
137
|
+
persisted.proxyUrl = proxyUrl;
|
|
138
|
+
if (authOverridden)
|
|
139
|
+
persisted.authFile = authFileDisplay;
|
|
140
|
+
// pi-ai issues model calls through the global fetch, so the proxy has to be
|
|
141
|
+
// installed there rather than handed to a component. Scoped by hostname, so
|
|
142
|
+
// no other provider's traffic changes route.
|
|
143
|
+
let routing = proxyUrl === ''
|
|
144
|
+
? undefined
|
|
145
|
+
: installProxyRouting(proxyUrl, { maxRetries: config.bootstrapRetries });
|
|
146
|
+
let dispatcher = routing?.dispatcher;
|
|
147
|
+
if (routing !== undefined) {
|
|
148
|
+
ctx.logger.info('gpt-sub: routing chatgpt.com and auth.openai.com through %s, retrying dropped connections %d times', displayProxy(proxyUrl), config.bootstrapRetries);
|
|
149
|
+
}
|
|
150
|
+
const tokens = new TokenStore({
|
|
151
|
+
authFile: expandHome(authFileDisplay),
|
|
152
|
+
refreshMarginMs: config.refreshMarginMinutes * 60_000,
|
|
153
|
+
...(dispatcher === undefined ? {} : { dispatcher }),
|
|
154
|
+
});
|
|
155
|
+
const ref = credentialRef(config.tokenRef);
|
|
156
|
+
/** When the last successful sync finished; 0 before the first. */
|
|
157
|
+
let lastSyncAt = 0;
|
|
158
|
+
/**
|
|
159
|
+
* Read the current token -- refreshing first when it is inside the margin --
|
|
160
|
+
* and write it to the credential the provider route reads.
|
|
161
|
+
*
|
|
162
|
+
* @returns nothing; failures propagate to the caller.
|
|
163
|
+
*/
|
|
164
|
+
const sync = async () => {
|
|
165
|
+
const current = await tokens.getTokens();
|
|
166
|
+
await ctx.credentials.set(ref, current.accessToken);
|
|
167
|
+
lastSyncAt = Date.now();
|
|
168
|
+
};
|
|
169
|
+
// Fail here, naming the path, rather than leaving the route pointed at a
|
|
170
|
+
// credential that will never be populated. A rejected apply() means cordis
|
|
171
|
+
// never receives the disposer below, so the global routing installed above
|
|
172
|
+
// has to be undone here too: a failed plugin must not leave the process
|
|
173
|
+
// dispatching through its still-open proxy agent.
|
|
174
|
+
try {
|
|
175
|
+
await tokens.verify();
|
|
176
|
+
await sync();
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
await routing?.uninstall();
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
ctx.logger.info('gpt-sub: published Codex access token to %s', config.tokenRef);
|
|
183
|
+
// Reachability probe. An unproxied egress answers 403 with a Cloudflare
|
|
184
|
+
// block page, and that failure is otherwise invisible until the first model
|
|
185
|
+
// call, where it surfaces as unreadable HTML. Log it loudly here instead.
|
|
186
|
+
// A probe failure must not fail the plugin: the network may simply be down,
|
|
187
|
+
// and the credential is published either way.
|
|
188
|
+
try {
|
|
189
|
+
const current = await tokens.getTokens();
|
|
190
|
+
// Bounded like the panel's own probe: a black-holed route must delay
|
|
191
|
+
// startup by seconds, not by the operating system's connect timeout.
|
|
192
|
+
const usage = await fetchUsage(current.accessToken, dispatcher, undefined, AbortSignal.timeout(PROBE_TIMEOUT_MS));
|
|
193
|
+
const windows = reportedWindows(usage);
|
|
194
|
+
if (windows.length === 0) {
|
|
195
|
+
ctx.logger.info('gpt-sub: reachable; plan %s, no rate-limit window reported', usage.plan_type ?? 'unknown');
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
ctx.logger.info('gpt-sub: reachable; plan %s, %s', usage.plan_type ?? 'unknown', windows
|
|
199
|
+
.map((window) => window.used_percent + '% of the ' + Math.round(window.limit_window_seconds / 3600) + 'h window')
|
|
200
|
+
.join(', '));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
ctx.logger.warn('gpt-sub: reachability probe failed: %s', String(error));
|
|
205
|
+
}
|
|
206
|
+
// The quota panel's data source. Reads the same token the model calls use,
|
|
207
|
+
// through the same proxy routing, and throttles so one open settings page
|
|
208
|
+
// cannot turn into a stream of upstream requests.
|
|
209
|
+
const quota = new QuotaSource({
|
|
210
|
+
accessToken: async () => (await tokens.getTokens()).accessToken,
|
|
211
|
+
...(dispatcher === undefined ? {} : { dispatcher }),
|
|
212
|
+
});
|
|
213
|
+
/**
|
|
214
|
+
* Swap the live proxy routing, serializing concurrent switches.
|
|
215
|
+
*
|
|
216
|
+
* Restores the pre-plugin global dispatcher before installing the next
|
|
217
|
+
* routing, so no request ever dispatches into a closing agent, and points
|
|
218
|
+
* the token store and quota source at the new dispatcher.
|
|
219
|
+
*
|
|
220
|
+
* @param nextUrl - the proxy URL to route through; empty string for direct.
|
|
221
|
+
* @returns nothing; failures propagate to the caller.
|
|
222
|
+
*/
|
|
223
|
+
let switching = Promise.resolve();
|
|
224
|
+
const switchProxy = (nextUrl) => {
|
|
225
|
+
const run = async () => {
|
|
226
|
+
const previous = routing;
|
|
227
|
+
routing = undefined;
|
|
228
|
+
await previous?.uninstall();
|
|
229
|
+
routing =
|
|
230
|
+
nextUrl === '' ? undefined : installProxyRouting(nextUrl, { maxRetries: config.bootstrapRetries });
|
|
231
|
+
dispatcher = routing?.dispatcher;
|
|
232
|
+
tokens.setDispatcher(dispatcher);
|
|
233
|
+
quota.setDispatcher(dispatcher);
|
|
234
|
+
proxyUrl = nextUrl;
|
|
235
|
+
};
|
|
236
|
+
switching = switching.then(run, run);
|
|
237
|
+
return switching;
|
|
238
|
+
};
|
|
239
|
+
/**
|
|
240
|
+
* Swap the live credential file, serializing concurrent switches the way
|
|
241
|
+
* {@link switchProxy} does.
|
|
242
|
+
*
|
|
243
|
+
* The caller validates a non-empty candidate before calling; this persists,
|
|
244
|
+
* points the token store at the new file, and republishes immediately, so a
|
|
245
|
+
* model call cannot race the next sync tick with the previous file's token.
|
|
246
|
+
*
|
|
247
|
+
* @param rawPath - the path as typed on the page; empty restores the config.
|
|
248
|
+
* @returns the display path now in force, and whether the token republished.
|
|
249
|
+
*/
|
|
250
|
+
let switchingAuth = Promise.resolve({
|
|
251
|
+
display: '',
|
|
252
|
+
published: true,
|
|
253
|
+
});
|
|
254
|
+
const switchAuthFile = (rawPath) => {
|
|
255
|
+
const run = async () => {
|
|
256
|
+
if (rawPath === '') {
|
|
257
|
+
delete persisted.authFile;
|
|
258
|
+
await saveStateOverride(stateFile, persisted);
|
|
259
|
+
authOverridden = false;
|
|
260
|
+
authFileDisplay = config.authFile;
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
persisted.authFile = rawPath;
|
|
264
|
+
await saveStateOverride(stateFile, persisted);
|
|
265
|
+
authOverridden = true;
|
|
266
|
+
authFileDisplay = rawPath;
|
|
267
|
+
}
|
|
268
|
+
tokens.setAuthFile(expandHome(authFileDisplay));
|
|
269
|
+
try {
|
|
270
|
+
await sync();
|
|
271
|
+
return { display: authFileDisplay, published: true };
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
// The switch itself succeeded; only the publish failed, and the sync
|
|
275
|
+
// timer retries it. Say so rather than failing a choice already made.
|
|
276
|
+
ctx.logger.warn('gpt-sub: token sync failed after switching to %s, retrying next tick: %s', authFileDisplay, String(error));
|
|
277
|
+
return { display: authFileDisplay, published: false };
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
switchingAuth = switchingAuth.then(run, run);
|
|
281
|
+
return switchingAuth;
|
|
282
|
+
};
|
|
283
|
+
/** Reply with JSON, never cached. */
|
|
284
|
+
const replyJson = (response, status, body) => {
|
|
285
|
+
response.writeHead(status, { 'content-type': 'application/json', 'cache-control': 'no-store' });
|
|
286
|
+
response.end(JSON.stringify(body));
|
|
287
|
+
};
|
|
288
|
+
ctx.effect(() => ctx.webServer.register({
|
|
289
|
+
kind: 'exact',
|
|
290
|
+
path: QUOTA_ROUTE,
|
|
291
|
+
handler: async (request, response) => {
|
|
292
|
+
const force = new URL(request.url ?? '/', 'http://x').searchParams.get('refresh') === '1';
|
|
293
|
+
const state = await quota.read(force);
|
|
294
|
+
replyJson(response, 200, state);
|
|
295
|
+
},
|
|
296
|
+
}), `gpt-sub: GET ${QUOTA_ROUTE}`);
|
|
297
|
+
ctx.effect(() => ctx.webServer.register({
|
|
298
|
+
kind: 'exact',
|
|
299
|
+
path: STATUS_ROUTE,
|
|
300
|
+
handler: async (request, response) => {
|
|
301
|
+
if (request.method !== 'GET') {
|
|
302
|
+
replyJson(response, 405, { message: 'method not allowed' });
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
// inspect() is file-only, so a status poll never refreshes a token
|
|
306
|
+
// and never reaches the network.
|
|
307
|
+
const inspection = await tokens.inspect().then((value) => value, () => undefined);
|
|
308
|
+
const expiry = inspection?.accessTokenExpiresAt;
|
|
309
|
+
const syncIntervalMs = config.syncIntervalMinutes * 60_000;
|
|
310
|
+
replyJson(response, 200, {
|
|
311
|
+
authFile: authFileDisplay,
|
|
312
|
+
configAuthFile: config.authFile,
|
|
313
|
+
authOverridden,
|
|
314
|
+
proxyUrl,
|
|
315
|
+
configProxyUrl: config.proxyUrl,
|
|
316
|
+
overridden,
|
|
317
|
+
refreshMarginMinutes: config.refreshMarginMinutes,
|
|
318
|
+
syncIntervalMinutes: config.syncIntervalMinutes,
|
|
319
|
+
...(lastSyncAt === 0 ? {} : { lastSyncAt, nextSyncAt: lastSyncAt + syncIntervalMs }),
|
|
320
|
+
...(expiry === undefined
|
|
321
|
+
? {}
|
|
322
|
+
: { tokenExpiresAt: expiry, refreshAt: expiry - config.refreshMarginMinutes * 60_000 }),
|
|
323
|
+
});
|
|
324
|
+
},
|
|
325
|
+
}), `gpt-sub: GET ${STATUS_ROUTE}`);
|
|
326
|
+
ctx.effect(() => ctx.webServer.register({
|
|
327
|
+
kind: 'exact',
|
|
328
|
+
path: PROXY_ROUTE,
|
|
329
|
+
handler: async (request, response) => {
|
|
330
|
+
if (request.method !== 'POST') {
|
|
331
|
+
replyJson(response, 405, { message: 'method not allowed' });
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (crossOrigin(request)) {
|
|
335
|
+
replyJson(response, 403, { ok: false, message: 'cross-origin request refused' });
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
let nextUrl;
|
|
339
|
+
try {
|
|
340
|
+
nextUrl = validateProxyUrl((await readJsonObject(request))['proxyUrl']);
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
replyJson(response, 400, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
persisted.proxyUrl = nextUrl;
|
|
348
|
+
await saveStateOverride(stateFile, persisted);
|
|
349
|
+
overridden = true;
|
|
350
|
+
await switchProxy(nextUrl);
|
|
351
|
+
ctx.logger.info('gpt-sub: proxy switched to %s', displayProxy(nextUrl));
|
|
352
|
+
replyJson(response, 200, { ok: true, proxyUrl: nextUrl });
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
replyJson(response, 500, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
}), `gpt-sub: POST ${PROXY_ROUTE}`);
|
|
359
|
+
ctx.effect(() => ctx.webServer.register({
|
|
360
|
+
kind: 'exact',
|
|
361
|
+
path: PROXY_TEST_ROUTE,
|
|
362
|
+
handler: async (request, response) => {
|
|
363
|
+
if (request.method !== 'POST') {
|
|
364
|
+
replyJson(response, 405, { message: 'method not allowed' });
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (crossOrigin(request)) {
|
|
368
|
+
replyJson(response, 403, { ok: false, message: 'cross-origin request refused' });
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
let candidate = proxyUrl;
|
|
372
|
+
try {
|
|
373
|
+
const body = await readJsonObject(request);
|
|
374
|
+
if (body['proxyUrl'] !== undefined)
|
|
375
|
+
candidate = validateProxyUrl(body['proxyUrl']);
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
replyJson(response, 400, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
try {
|
|
382
|
+
const accessToken = (await tokens.getTokens()).accessToken;
|
|
383
|
+
const result = await probeProxy({
|
|
384
|
+
proxyUrl: candidate,
|
|
385
|
+
accessToken,
|
|
386
|
+
timeoutMs: PROBE_TIMEOUT_MS,
|
|
387
|
+
});
|
|
388
|
+
replyJson(response, 200, result);
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
replyJson(response, 500, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
}), `gpt-sub: POST ${PROXY_TEST_ROUTE}`);
|
|
395
|
+
ctx.effect(() => ctx.webServer.register({
|
|
396
|
+
kind: 'exact',
|
|
397
|
+
path: AUTH_ROUTE,
|
|
398
|
+
handler: async (request, response) => {
|
|
399
|
+
if (request.method !== 'POST') {
|
|
400
|
+
replyJson(response, 405, { message: 'method not allowed' });
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
if (crossOrigin(request)) {
|
|
404
|
+
replyJson(response, 403, { ok: false, message: 'cross-origin request refused' });
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
// Absent or empty means "back to the configured default"; otherwise
|
|
408
|
+
// the trimmed string is the candidate path.
|
|
409
|
+
let candidate;
|
|
410
|
+
try {
|
|
411
|
+
const body = await readJsonObject(request);
|
|
412
|
+
const value = body['authFile'];
|
|
413
|
+
if (value !== undefined && value !== null) {
|
|
414
|
+
if (typeof value !== 'string')
|
|
415
|
+
throw new Error('authFile must be a string');
|
|
416
|
+
candidate = value.trim();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
replyJson(response, 400, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
// Validate BEFORE persisting or switching: a path that cannot serve
|
|
424
|
+
// credentials must never become the live source, here or after a
|
|
425
|
+
// restart. verify() is file-only -- the check spends no refresh.
|
|
426
|
+
if (candidate !== undefined && candidate !== '') {
|
|
427
|
+
try {
|
|
428
|
+
await new TokenStore({
|
|
429
|
+
authFile: expandHome(candidate),
|
|
430
|
+
refreshMarginMs: config.refreshMarginMinutes * 60_000,
|
|
431
|
+
}).verify();
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
replyJson(response, 400, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
try {
|
|
439
|
+
const result = await switchAuthFile(candidate ?? '');
|
|
440
|
+
ctx.logger.info('gpt-sub: credential file %s', candidate === undefined || candidate === ''
|
|
441
|
+
? `restored to configured ${result.display}`
|
|
442
|
+
: `switched to ${result.display}`);
|
|
443
|
+
replyJson(response, 200, { ok: true, authFile: result.display, published: result.published });
|
|
444
|
+
}
|
|
445
|
+
catch (error) {
|
|
446
|
+
replyJson(response, 500, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
}), `gpt-sub: POST ${AUTH_ROUTE}`);
|
|
450
|
+
ctx.effect(() => ctx.webServer.register({
|
|
451
|
+
kind: 'exact',
|
|
452
|
+
path: AUTH_BROWSE_ROUTE,
|
|
453
|
+
handler: async (request, response) => {
|
|
454
|
+
if (request.method !== 'GET') {
|
|
455
|
+
replyJson(response, 405, { message: 'method not allowed' });
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const requested = new URL(request.url ?? '/', 'http://x').searchParams.get('path') ?? '';
|
|
459
|
+
// Empty means start from the home directory, where ~/.codex lives.
|
|
460
|
+
// A listing carries names and paths only -- never file contents.
|
|
461
|
+
const target = requested.trim() === '' ? homedir() : expandHome(requested.trim());
|
|
462
|
+
try {
|
|
463
|
+
if (!(await stat(target)).isDirectory())
|
|
464
|
+
throw new Error('not a directory');
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
replyJson(response, 400, { ok: false, message: `not a readable directory: ${target}` });
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
try {
|
|
471
|
+
const dirents = await readdir(target, { withFileTypes: true });
|
|
472
|
+
const entries = dirents
|
|
473
|
+
.slice(0, BROWSE_LIMIT)
|
|
474
|
+
.map((entry) => ({
|
|
475
|
+
name: entry.name,
|
|
476
|
+
path: join(target, entry.name),
|
|
477
|
+
dir: entry.isDirectory(),
|
|
478
|
+
}))
|
|
479
|
+
.sort((a, b) => (a.dir === b.dir ? a.name.localeCompare(b.name) : a.dir ? -1 : 1));
|
|
480
|
+
// The root's parent is itself, so the panel hides the up-row
|
|
481
|
+
// rather than offering a dead link.
|
|
482
|
+
const parent = dirname(target);
|
|
483
|
+
replyJson(response, 200, {
|
|
484
|
+
ok: true,
|
|
485
|
+
dir: target,
|
|
486
|
+
...(parent !== target ? { parent } : {}),
|
|
487
|
+
truncated: dirents.length > BROWSE_LIMIT,
|
|
488
|
+
entries,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
catch (error) {
|
|
492
|
+
replyJson(response, 400, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
}), `gpt-sub: GET ${AUTH_BROWSE_ROUTE}`);
|
|
496
|
+
ctx.effect(() => ctx.webServer.register({
|
|
497
|
+
kind: 'exact',
|
|
498
|
+
path: RESET_CREDITS_ROUTE,
|
|
499
|
+
handler: async (request, response) => {
|
|
500
|
+
if (request.method !== 'GET') {
|
|
501
|
+
replyJson(response, 405, { ok: false, message: 'method not allowed' });
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
try {
|
|
505
|
+
const accessToken = (await tokens.getTokens()).accessToken;
|
|
506
|
+
const details = await listResetCredits(accessToken, dispatcher, undefined, AbortSignal.timeout(RESET_CREDITS_TIMEOUT_MS));
|
|
507
|
+
replyJson(response, 200, { ok: true, ...details });
|
|
508
|
+
}
|
|
509
|
+
catch (error) {
|
|
510
|
+
replyJson(response, 200, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
}), `gpt-sub: GET ${RESET_CREDITS_ROUTE}`);
|
|
514
|
+
// Redeeming is destructive and idempotent only per key, so the host mints a
|
|
515
|
+
// fresh key per click; the page sends the credit id it means to spend.
|
|
516
|
+
ctx.effect(() => ctx.webServer.register({
|
|
517
|
+
kind: 'exact',
|
|
518
|
+
path: RESET_CREDITS_CONSUME_ROUTE,
|
|
519
|
+
handler: async (request, response) => {
|
|
520
|
+
if (request.method !== 'POST') {
|
|
521
|
+
replyJson(response, 405, { ok: false, message: 'method not allowed' });
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
if (crossOrigin(request)) {
|
|
525
|
+
replyJson(response, 403, { ok: false, message: 'cross-origin request refused' });
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
let creditId;
|
|
529
|
+
try {
|
|
530
|
+
const body = await readJsonObject(request);
|
|
531
|
+
const value = body['creditId'];
|
|
532
|
+
if (value === undefined || value === null)
|
|
533
|
+
creditId = undefined;
|
|
534
|
+
else if (typeof value !== 'string' || value.trim() === '') {
|
|
535
|
+
throw new Error('creditId must be a non-empty string when present');
|
|
536
|
+
}
|
|
537
|
+
else
|
|
538
|
+
creditId = value.trim();
|
|
539
|
+
}
|
|
540
|
+
catch (error) {
|
|
541
|
+
replyJson(response, 400, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
try {
|
|
545
|
+
const accessToken = (await tokens.getTokens()).accessToken;
|
|
546
|
+
const result = await consumeResetCredit(accessToken, randomUUID(), {
|
|
547
|
+
...(creditId === undefined ? {} : { creditId }),
|
|
548
|
+
...(dispatcher === undefined ? {} : { dispatcher }),
|
|
549
|
+
signal: AbortSignal.timeout(RESET_CREDITS_TIMEOUT_MS),
|
|
550
|
+
});
|
|
551
|
+
ctx.logger.info('gpt-sub: rate-limit reset credit consumed (code=%s, windows_reset=%d, credit=%s)', result.code, result.windows_reset ?? 0, creditId ?? 'any');
|
|
552
|
+
// The window just changed, so serve the next quota poll from a
|
|
553
|
+
// fresh upstream read instead of the throttle's cache.
|
|
554
|
+
await quota.read(true).catch(() => undefined);
|
|
555
|
+
replyJson(response, 200, { ok: true, ...result });
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
replyJson(response, 500, { ok: false, message: error instanceof Error ? error.message : String(error) });
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
}), `gpt-sub: POST ${RESET_CREDITS_CONSUME_ROUTE}`);
|
|
562
|
+
const timer = setInterval(() => {
|
|
563
|
+
// A rejected timer callback would be an unhandled rejection, and DSH's
|
|
564
|
+
// handler exits the process. A transient refresh failure must cost a log
|
|
565
|
+
// line, not the harness: the next tick tries again, and the token in the
|
|
566
|
+
// store stays valid until its own expiry regardless.
|
|
567
|
+
void sync().catch((error) => {
|
|
568
|
+
ctx.logger.warn('gpt-sub: token sync failed, retrying next tick: %s', String(error));
|
|
569
|
+
});
|
|
570
|
+
}, config.syncIntervalMinutes * 60_000);
|
|
571
|
+
// Never let this timer be the reason the process stays alive.
|
|
572
|
+
timer.unref?.();
|
|
573
|
+
return async () => {
|
|
574
|
+
clearInterval(timer);
|
|
575
|
+
// Restores the previous global dispatcher before closing the agent, so a
|
|
576
|
+
// fiber restart never leaves the host dispatching into a closing proxy.
|
|
577
|
+
await switching.catch(() => undefined);
|
|
578
|
+
await routing?.uninstall();
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAC5D,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAIxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,gBAAgB,GAEjB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EAAE,mBAAmB,EAAqB,MAAM,oBAAoB,CAAA;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAMxD,0BAA0B;AAC1B,MAAM,CAAC,MAAM,IAAI,GAAG,SAAS,CAAA;AAE7B;;;;GAIG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,aAAa,EAAE,WAAW,CAAC,CAAA;AAElD,0DAA0D;AAC1D,MAAM,WAAW,GAAG,gBAAgB,CAAA;AAEpC,wEAAwE;AACxE,MAAM,YAAY,GAAG,iBAAiB,CAAA;AAEtC,iEAAiE;AACjE,MAAM,WAAW,GAAG,gBAAgB,CAAA;AAEpC,wEAAwE;AACxE,MAAM,gBAAgB,GAAG,qBAAqB,CAAA;AAE9C,sEAAsE;AACtE,MAAM,UAAU,GAAG,eAAe,CAAA;AAElC,0EAA0E;AAC1E,MAAM,iBAAiB,GAAG,sBAAsB,CAAA;AAEhD,gFAAgF;AAChF,MAAM,YAAY,GAAG,GAAG,CAAA;AAExB,yCAAyC;AACzC,MAAM,gBAAgB,GAAG,MAAM,CAAA;AAE/B,6DAA6D;AAC7D,MAAM,mBAAmB,GAAG,wBAAwB,CAAA;AAEpD,mEAAmE;AACnE,MAAM,2BAA2B,GAAG,gCAAgC,CAAA;AAEpE,+EAA+E;AAC/E,MAAM,wBAAwB,GAAG,MAAM,CAAA;AAEvC;;;;;GAKG;AACH,MAAM,YAAY,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAA;AAE3F;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAyC,CAAC,CAAC,MAAM,CAAC;IACnE,2EAA2E;IAC3E,2EAA2E;IAC3E,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAChC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAC;IAClD,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5C,4EAA4E;IAC5E,uDAAuD;IACvD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC;IACzE,uEAAuE;IACvE,6EAA6E;IAC7E,wEAAwE;IACxE,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC3C,2EAA2E;IAC3E,2EAA2E;IAC3E,8DAA8D;IAC9D,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACvC,uEAAuE;IACvE,sEAAsE;IACtE,kDAAkD;IAClD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC;CACrD,CAAC,CAAA;AAEF,sEAAsE;AACtE,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACtE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAY,EAAE,MAAmB;IAC3D,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;IAE9C,0EAA0E;IAC1E,wEAAwE;IACxE,4EAA4E;IAC5E,oEAAoE;IACpE,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,SAAS,CAAC,CAAA;IACrD,MAAM,UAAU,GAAG,UAAU,EAAE,QAAQ,CAAA;IACvC,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC/D,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,gEAAgE,EAChE,YAAY,CAAC,UAAU,CAAC,EACxB,SAAS,EACT,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAC9B,CAAA;IACH,CAAC;IACD,IAAI,QAAQ,GAAG,UAAU,IAAI,MAAM,CAAC,QAAQ,CAAA;IAC5C,IAAI,UAAU,GAAG,UAAU,KAAK,SAAS,CAAA;IAEzC,MAAM,aAAa,GAAG,UAAU,EAAE,QAAQ,CAAA;IAC1C,IAAI,aAAa,KAAK,SAAS,IAAI,aAAa,KAAK,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrE,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,iEAAiE,EACjE,aAAa,EACb,SAAS,EACT,MAAM,CAAC,QAAQ,CAChB,CAAA;IACH,CAAC;IACD,IAAI,eAAe,GAAG,aAAa,IAAI,MAAM,CAAC,QAAQ,CAAA;IACtD,IAAI,cAAc,GAAG,aAAa,KAAK,SAAS,CAAA;IAEhD,yEAAyE;IACzE,gEAAgE;IAChE,MAAM,SAAS,GAAkB,EAAE,CAAA;IACnC,IAAI,UAAU;QAAE,SAAS,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC7C,IAAI,cAAc;QAAE,SAAS,CAAC,QAAQ,GAAG,eAAe,CAAA;IAExD,4EAA4E;IAC5E,4EAA4E;IAC5E,6CAA6C;IAC7C,IAAI,OAAO,GACT,QAAQ,KAAK,EAAE;QACb,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,mBAAmB,CAAC,QAAQ,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAA;IAC5E,IAAI,UAAU,GAA2B,OAAO,EAAE,UAAU,CAAA;IAC5D,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,oGAAoG,EACpG,YAAY,CAAC,QAAQ,CAAC,EACtB,MAAM,CAAC,gBAAgB,CACxB,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC;QAC5B,QAAQ,EAAE,UAAU,CAAC,eAAe,CAAC;QACrC,eAAe,EAAE,MAAM,CAAC,oBAAoB,GAAG,MAAM;QACrD,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;KACpD,CAAC,CAAA;IAEF,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAE1C,kEAAkE;IAClE,IAAI,UAAU,GAAG,CAAC,CAAA;IAElB;;;;;OAKG;IACH,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;QACrC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAA;QACxC,MAAM,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;QACnD,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACzB,CAAC,CAAA;IAED,yEAAyE;IACzE,2EAA2E;IAC3E,2EAA2E;IAC3E,wEAAwE;IACxE,kDAAkD;IAClD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,MAAM,EAAE,CAAA;QACrB,MAAM,IAAI,EAAE,CAAA;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,EAAE,SAAS,EAAE,CAAA;QAC1B,MAAM,KAAK,CAAA;IACb,CAAC;IACD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,6CAA6C,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;IAE/E,wEAAwE;IACxE,4EAA4E;IAC5E,0EAA0E;IAC1E,4EAA4E;IAC5E,8CAA8C;IAC9C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAA;QACxC,qEAAqE;QACrE,qEAAqE;QACrE,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAA;QACjH,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,CAAA;QACtC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,4DAA4D,EAAE,KAAK,CAAC,SAAS,IAAI,SAAS,CAAC,CAAA;QAC7G,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,iCAAiC,EACjC,KAAK,CAAC,SAAS,IAAI,SAAS,EAC5B,OAAO;iBACJ,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,GAAG,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,oBAAoB,GAAG,IAAI,CAAC,GAAG,UAAU,CAAC;iBAChH,IAAI,CAAC,IAAI,CAAC,CACd,CAAA;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,wCAAwC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;IAC1E,CAAC;IAED,2EAA2E;IAC3E,0EAA0E;IAC1E,kDAAkD;IAClD,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC;QAC5B,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,WAAW;QAC/D,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;KACpD,CAAC,CAAA;IAEF;;;;;;;;;OASG;IACH,IAAI,SAAS,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAA;IAChD,MAAM,WAAW,GAAG,CAAC,OAAe,EAAiB,EAAE;QACrD,MAAM,GAAG,GAAG,KAAK,IAAmB,EAAE;YACpC,MAAM,QAAQ,GAAG,OAAO,CAAA;YACxB,OAAO,GAAG,SAAS,CAAA;YACnB,MAAM,QAAQ,EAAE,SAAS,EAAE,CAAA;YAC3B,OAAO;gBACL,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAA;YACpG,UAAU,GAAG,OAAO,EAAE,UAAU,CAAA;YAChC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;YAChC,KAAK,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;YAC/B,QAAQ,GAAG,OAAO,CAAA;QACpB,CAAC,CAAA;QACD,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;QACpC,OAAO,SAAS,CAAA;IAClB,CAAC,CAAA;IAED;;;;;;;;;;OAUG;IACH,IAAI,aAAa,GAAqD,OAAO,CAAC,OAAO,CAAC;QACpF,OAAO,EAAE,EAAE;QACX,SAAS,EAAE,IAAI;KAChB,CAAC,CAAA;IACF,MAAM,cAAc,GAAG,CAAC,OAAe,EAAoD,EAAE;QAC3F,MAAM,GAAG,GAAG,KAAK,IAAsD,EAAE;YACvE,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;gBACnB,OAAO,SAAS,CAAC,QAAQ,CAAA;gBACzB,MAAM,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;gBAC7C,cAAc,GAAG,KAAK,CAAA;gBACtB,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAA;YACnC,CAAC;iBAAM,CAAC;gBACN,SAAS,CAAC,QAAQ,GAAG,OAAO,CAAA;gBAC5B,MAAM,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;gBAC7C,cAAc,GAAG,IAAI,CAAA;gBACrB,eAAe,GAAG,OAAO,CAAA;YAC3B,CAAC;YACD,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAA;YAC/C,IAAI,CAAC;gBACH,MAAM,IAAI,EAAE,CAAA;gBACZ,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;YACtD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,qEAAqE;gBACrE,sEAAsE;gBACtE,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,0EAA0E,EAC1E,eAAe,EACf,MAAM,CAAC,KAAK,CAAC,CACd,CAAA;gBACD,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;YACvD,CAAC;QACH,CAAC,CAAA;QACD,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;QAC5C,OAAO,aAAa,CAAA;IACtB,CAAC,CAAA;IAED,qCAAqC;IACrC,MAAM,SAAS,GAAG,CAAC,QAA4C,EAAE,MAAc,EAAE,IAAa,EAAQ,EAAE;QACtG,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC,CAAA;QAC/F,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;IACpC,CAAC,CAAA;IAED,GAAG,CAAC,MAAM,CACR,GAAG,EAAE,CACH,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC;QACrB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;YACnC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,UAAU,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,GAAG,CAAA;YACzF,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACrC,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;QACjC,CAAC;KACF,CAAC,EACJ,gBAAgB,WAAW,EAAE,CAC9B,CAAA;IAED,GAAG,CAAC,MAAM,CACR,GAAG,EAAE,CACH,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC;QACrB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;YACnC,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBAC7B,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAA;gBAC3D,OAAM;YACR,CAAC;YACD,mEAAmE;YACnE,iCAAiC;YACjC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAC5C,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAChB,GAAG,EAAE,CAAC,SAAS,CAChB,CAAA;YACD,MAAM,MAAM,GAAG,UAAU,EAAE,oBAAoB,CAAA;YAC/C,MAAM,cAAc,GAAG,MAAM,CAAC,mBAAmB,GAAG,MAAM,CAAA;YAC1D,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE;gBACvB,QAAQ,EAAE,eAAe;gBACzB,cAAc,EAAE,MAAM,CAAC,QAAQ;gBAC/B,cAAc;gBACd,QAAQ;gBACR,cAAc,EAAE,MAAM,CAAC,QAAQ;gBAC/B,UAAU;gBACV,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;gBACjD,mBAAmB,EAAE,MAAM,CAAC,mBAAmB;gBAC/C,GAAG,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,GAAG,cAAc,EAAE,CAAC;gBACpF,GAAG,CAAC,MAAM,KAAK,SAAS;oBACtB,CAAC,CAAC,EAAE;oBACJ,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,oBAAoB,GAAG,MAAM,EAAE,CAAC;aAC1F,CAAC,CAAA;QACJ,CAAC;KACF,CAAC,EACJ,gBAAgB,YAAY,EAAE,CAC/B,CAAA;IAED,GAAG,CAAC,MAAM,CACR,GAAG,EAAE,CACH,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC;QACrB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;YACnC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC9B,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAA;gBAC3D,OAAM;YACR,CAAC;YACD,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzB,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC,CAAA;gBAChF,OAAM;YACR,CAAC;YACD,IAAI,OAAe,CAAA;YACnB,IAAI,CAAC;gBACH,OAAO,GAAG,gBAAgB,CAAC,CAAC,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAA;YACzE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;gBACxG,OAAM;YACR,CAAC;YACD,IAAI,CAAC;gBACH,SAAS,CAAC,QAAQ,GAAG,OAAO,CAAA;gBAC5B,MAAM,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;gBAC7C,UAAU,GAAG,IAAI,CAAA;gBACjB,MAAM,WAAW,CAAC,OAAO,CAAC,CAAA;gBAC1B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;gBACvE,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAA;YAC3D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC1G,CAAC;QACH,CAAC;KACF,CAAC,EACJ,iBAAiB,WAAW,EAAE,CAC/B,CAAA;IAED,GAAG,CAAC,MAAM,CACR,GAAG,EAAE,CACH,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC;QACrB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;YACnC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC9B,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAA;gBAC3D,OAAM;YACR,CAAC;YACD,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzB,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC,CAAA;gBAChF,OAAM;YACR,CAAC;YACD,IAAI,SAAS,GAAG,QAAQ,CAAA;YACxB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAA;gBAC1C,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,SAAS;oBAAE,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAA;YACpF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;gBACxG,OAAM;YACR,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,WAAW,GAAG,CAAC,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,WAAW,CAAA;gBAC1D,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC;oBAC9B,QAAQ,EAAE,SAAS;oBACnB,WAAW;oBACX,SAAS,EAAE,gBAAgB;iBAC5B,CAAC,CAAA;gBACF,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;YAClC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC1G,CAAC;QACH,CAAC;KACF,CAAC,EACJ,iBAAiB,gBAAgB,EAAE,CACpC,CAAA;IAED,GAAG,CAAC,MAAM,CACR,GAAG,EAAE,CACH,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC;QACrB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;YACnC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC9B,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAA;gBAC3D,OAAM;YACR,CAAC;YACD,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzB,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC,CAAA;gBAChF,OAAM;YACR,CAAC;YACD,oEAAoE;YACpE,4CAA4C;YAC5C,IAAI,SAA6B,CAAA;YACjC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAA;gBAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAA;gBAC9B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;oBAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ;wBAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;oBAC3E,SAAS,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;gBAC1B,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;gBACxG,OAAM;YACR,CAAC;YACD,oEAAoE;YACpE,iEAAiE;YACjE,iEAAiE;YACjE,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,EAAE,EAAE,CAAC;gBAChD,IAAI,CAAC;oBACH,MAAM,IAAI,UAAU,CAAC;wBACnB,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC;wBAC/B,eAAe,EAAE,MAAM,CAAC,oBAAoB,GAAG,MAAM;qBACtD,CAAC,CAAC,MAAM,EAAE,CAAA;gBACb,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;oBACxG,OAAM;gBACR,CAAC;YACH,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,SAAS,IAAI,EAAE,CAAC,CAAA;gBACpD,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,6BAA6B,EAC7B,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,EAAE;oBACzC,CAAC,CAAC,0BAA0B,MAAM,CAAC,OAAO,EAAE;oBAC5C,CAAC,CAAC,eAAe,MAAM,CAAC,OAAO,EAAE,CACpC,CAAA;gBACD,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAA;YAC/F,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC1G,CAAC;QACH,CAAC;KACF,CAAC,EACJ,iBAAiB,UAAU,EAAE,CAC9B,CAAA;IAED,GAAG,CAAC,MAAM,CACR,GAAG,EAAE,CACH,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC;QACrB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;YACnC,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBAC7B,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAA;gBAC3D,OAAM;YACR,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,UAAU,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;YACxF,mEAAmE;YACnE,iEAAiE;YACjE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAA;YACjF,IAAI,CAAC;gBACH,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAA;YAC7E,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,6BAA6B,MAAM,EAAE,EAAE,CAAC,CAAA;gBACvF,OAAM;YACR,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;gBAC9D,MAAM,OAAO,GAAG,OAAO;qBACpB,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC;qBACtB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBACf,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC;oBAC9B,GAAG,EAAE,KAAK,CAAC,WAAW,EAAE;iBACzB,CAAC,CAAC;qBACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;gBACpF,6DAA6D;gBAC7D,oCAAoC;gBACpC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;gBAC9B,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE;oBACvB,EAAE,EAAE,IAAI;oBACR,GAAG,EAAE,MAAM;oBACX,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACxC,SAAS,EAAE,OAAO,CAAC,MAAM,GAAG,YAAY;oBACxC,OAAO;iBACR,CAAC,CAAA;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC1G,CAAC;QACH,CAAC;KACF,CAAC,EACJ,gBAAgB,iBAAiB,EAAE,CACpC,CAAA;IAED,GAAG,CAAC,MAAM,CACR,GAAG,EAAE,CACH,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC;QACrB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;YACnC,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBAC7B,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAA;gBACtE,OAAM;YACR,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,WAAW,GAAG,CAAC,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,WAAW,CAAA;gBAC1D,MAAM,OAAO,GAAG,MAAM,gBAAgB,CACpC,WAAW,EACX,UAAU,EACV,SAAS,EACT,WAAW,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAC9C,CAAA;gBACD,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,CAAC,CAAA;YACpD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC1G,CAAC;QACH,CAAC;KACF,CAAC,EACJ,gBAAgB,mBAAmB,EAAE,CACtC,CAAA;IAED,4EAA4E;IAC5E,uEAAuE;IACvE,GAAG,CAAC,MAAM,CACR,GAAG,EAAE,CACH,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC;QACrB,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,2BAA2B;QACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;YACnC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC9B,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAA;gBACtE,OAAM;YACR,CAAC;YACD,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzB,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC,CAAA;gBAChF,OAAM;YACR,CAAC;YACD,IAAI,QAA4B,CAAA;YAChC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAA;gBAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAA;gBAC9B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;oBAAE,QAAQ,GAAG,SAAS,CAAA;qBAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;oBAC1D,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAA;gBACrE,CAAC;;oBAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;YAChC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;gBACxG,OAAM;YACR,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,WAAW,GAAG,CAAC,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,WAAW,CAAA;gBAC1D,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,WAAW,EAAE,UAAU,EAAE,EAAE;oBACjE,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;oBAC/C,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;oBACnD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,wBAAwB,CAAC;iBACtD,CAAC,CAAA;gBACF,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,kFAAkF,EAClF,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,aAAa,IAAI,CAAC,EACzB,QAAQ,IAAI,KAAK,CAClB,CAAA;gBACD,+DAA+D;gBAC/D,uDAAuD;gBACvD,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;gBAC7C,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;YACnD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;YAC1G,CAAC;QACH,CAAC;KACF,CAAC,EACJ,iBAAiB,2BAA2B,EAAE,CAC/C,CAAA;IAED,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,uEAAuE;QACvE,yEAAyE;QACzE,yEAAyE;QACzE,qDAAqD;QACrD,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACnC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,oDAAoD,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACtF,CAAC,CAAC,CAAA;IACJ,CAAC,EAAE,MAAM,CAAC,mBAAmB,GAAG,MAAM,CAAC,CAAA;IACvC,8DAA8D;IAC9D,KAAK,CAAC,KAAK,EAAE,EAAE,CAAA;IAEf,OAAO,KAAK,IAAI,EAAE;QAChB,aAAa,CAAC,KAAK,CAAC,CAAA;QACpB,yEAAyE;QACzE,wEAAwE;QACxE,MAAM,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACtC,MAAM,OAAO,EAAE,SAAS,EAAE,CAAA;IAC5B,CAAC,CAAA;AACH,CAAC"}
|
package/lib/jwt.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal JWT claim reader. Only the `exp` claim is needed, and signature
|
|
3
|
+
* verification is deliberately absent: these tokens are read from the user's
|
|
4
|
+
* own credential file, never accepted from a remote party.
|
|
5
|
+
*
|
|
6
|
+
* @module dsh-gpt-sub/jwt
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Read a JWT's expiry.
|
|
10
|
+
*
|
|
11
|
+
* @param token - a candidate JWT; opaque strings are tolerated.
|
|
12
|
+
* @returns expiry in epoch milliseconds, or undefined when absent or undecodable.
|
|
13
|
+
*/
|
|
14
|
+
export declare function jwtExpiryMs(token: string): number | undefined;
|
|
15
|
+
//# sourceMappingURL=jwt.d.ts.map
|
package/lib/jwt.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../src/jwt.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW7D"}
|