From a9826e8c75a6a24ebd7c11412b22fd873883923d Mon Sep 17 00:00:00 2001 From: EremesNG Date: Sun, 17 May 2026 14:02:49 -0600 Subject: [PATCH 01/33] chore(dependencies): remove package-lock.json file --- .gitignore | 3 + dist/auth-sync.d.ts | 3 - dist/auth-sync.d.ts.map | 1 - dist/auth-sync.js | 105 - dist/auth-sync.js.map | 1 - dist/auth.d.ts | 20 - dist/auth.d.ts.map | 1 - dist/auth.js | 283 --- dist/auth.js.map | 1 - dist/cli.d.ts | 3 - dist/cli.d.ts.map | 1 - dist/cli.js | 160 -- dist/cli.js.map | 1 - dist/codex-auth.d.ts | 46 - dist/codex-auth.d.ts.map | 1 - dist/codex-auth.js | 331 --- dist/codex-auth.js.map | 1 - dist/errors.d.ts | 17 - dist/errors.d.ts.map | 1 - dist/errors.js | 37 - dist/errors.js.map | 1 - dist/force-mode.d.ts | 24 - dist/force-mode.d.ts.map | 1 - dist/force-mode.js | 149 -- dist/force-mode.js.map | 1 - dist/index.d.ts | 10 - dist/index.d.ts.map | 1 - dist/index.js | 848 ------- dist/index.js.map | 1 - dist/limits-refresh.d.ts | 9 - dist/limits-refresh.d.ts.map | 1 - dist/limits-refresh.js | 120 - dist/limits-refresh.js.map | 1 - dist/logger.d.ts | 6 - dist/logger.d.ts.map | 1 - dist/logger.js | 52 - dist/logger.js.map | 1 - dist/models.d.ts | 7 - dist/models.d.ts.map | 1 - dist/models.js | 167 -- dist/models.js.map | 1 - dist/probe-limits.d.ts | 17 - dist/probe-limits.d.ts.map | 1 - dist/probe-limits.js | 348 --- dist/probe-limits.js.map | 1 - dist/rate-limits.d.ts | 14 - dist/rate-limits.d.ts.map | 1 - dist/rate-limits.js | 222 -- dist/rate-limits.js.map | 1 - dist/refresh-queue.d.ts | 21 - dist/refresh-queue.d.ts.map | 1 - dist/refresh-queue.js | 129 -- dist/refresh-queue.js.map | 1 - dist/rotation.d.ts | 28 - dist/rotation.d.ts.map | 1 - dist/rotation.js | 366 ---- dist/rotation.js.map | 1 - dist/sessions-limits.d.ts | 11 - dist/sessions-limits.d.ts.map | 1 - dist/sessions-limits.js | 128 -- dist/sessions-limits.js.map | 1 - dist/settings.d.ts | 28 - dist/settings.d.ts.map | 1 - dist/settings.js | 208 -- dist/settings.js.map | 1 - dist/store.d.ts | 24 - dist/store.d.ts.map | 1 - dist/store.js | 545 ----- dist/store.js.map | 1 - dist/systemd.d.ts | 10 - dist/systemd.d.ts.map | 1 - dist/systemd.js | 53 - dist/systemd.js.map | 1 - dist/types.d.ts | 144 -- dist/types.d.ts.map | 1 - dist/types.js | 130 -- dist/types.js.map | 1 - dist/usage-limits.d.ts | 22 - dist/usage-limits.d.ts.map | 1 - dist/usage-limits.js | 190 -- dist/usage-limits.js.map | 1 - dist/web.d.ts | 7 - dist/web.d.ts.map | 1 - dist/web.js | 3721 ------------------------------- dist/web.js.map | 1 - package-lock.json | 3889 --------------------------------- package.json | 1 + pnpm-lock.yaml | 2787 +++++++++++++++++++++++ pnpm-workspace.yaml | 2 + 89 files changed, 2793 insertions(+), 12694 deletions(-) delete mode 100644 dist/auth-sync.d.ts delete mode 100644 dist/auth-sync.d.ts.map delete mode 100644 dist/auth-sync.js delete mode 100644 dist/auth-sync.js.map delete mode 100644 dist/auth.d.ts delete mode 100644 dist/auth.d.ts.map delete mode 100644 dist/auth.js delete mode 100644 dist/auth.js.map delete mode 100644 dist/cli.d.ts delete mode 100644 dist/cli.d.ts.map delete mode 100644 dist/cli.js delete mode 100644 dist/cli.js.map delete mode 100644 dist/codex-auth.d.ts delete mode 100644 dist/codex-auth.d.ts.map delete mode 100644 dist/codex-auth.js delete mode 100644 dist/codex-auth.js.map delete mode 100644 dist/errors.d.ts delete mode 100644 dist/errors.d.ts.map delete mode 100644 dist/errors.js delete mode 100644 dist/errors.js.map delete mode 100644 dist/force-mode.d.ts delete mode 100644 dist/force-mode.d.ts.map delete mode 100644 dist/force-mode.js delete mode 100644 dist/force-mode.js.map delete mode 100644 dist/index.d.ts delete mode 100644 dist/index.d.ts.map delete mode 100644 dist/index.js delete mode 100644 dist/index.js.map delete mode 100644 dist/limits-refresh.d.ts delete mode 100644 dist/limits-refresh.d.ts.map delete mode 100644 dist/limits-refresh.js delete mode 100644 dist/limits-refresh.js.map delete mode 100644 dist/logger.d.ts delete mode 100644 dist/logger.d.ts.map delete mode 100644 dist/logger.js delete mode 100644 dist/logger.js.map delete mode 100644 dist/models.d.ts delete mode 100644 dist/models.d.ts.map delete mode 100644 dist/models.js delete mode 100644 dist/models.js.map delete mode 100644 dist/probe-limits.d.ts delete mode 100644 dist/probe-limits.d.ts.map delete mode 100644 dist/probe-limits.js delete mode 100644 dist/probe-limits.js.map delete mode 100644 dist/rate-limits.d.ts delete mode 100644 dist/rate-limits.d.ts.map delete mode 100644 dist/rate-limits.js delete mode 100644 dist/rate-limits.js.map delete mode 100644 dist/refresh-queue.d.ts delete mode 100644 dist/refresh-queue.d.ts.map delete mode 100644 dist/refresh-queue.js delete mode 100644 dist/refresh-queue.js.map delete mode 100644 dist/rotation.d.ts delete mode 100644 dist/rotation.d.ts.map delete mode 100644 dist/rotation.js delete mode 100644 dist/rotation.js.map delete mode 100644 dist/sessions-limits.d.ts delete mode 100644 dist/sessions-limits.d.ts.map delete mode 100644 dist/sessions-limits.js delete mode 100644 dist/sessions-limits.js.map delete mode 100644 dist/settings.d.ts delete mode 100644 dist/settings.d.ts.map delete mode 100644 dist/settings.js delete mode 100644 dist/settings.js.map delete mode 100644 dist/store.d.ts delete mode 100644 dist/store.d.ts.map delete mode 100644 dist/store.js delete mode 100644 dist/store.js.map delete mode 100644 dist/systemd.d.ts delete mode 100644 dist/systemd.d.ts.map delete mode 100644 dist/systemd.js delete mode 100644 dist/systemd.js.map delete mode 100644 dist/types.d.ts delete mode 100644 dist/types.d.ts.map delete mode 100644 dist/types.js delete mode 100644 dist/types.js.map delete mode 100644 dist/usage-limits.d.ts delete mode 100644 dist/usage-limits.d.ts.map delete mode 100644 dist/usage-limits.js delete mode 100644 dist/usage-limits.js.map delete mode 100644 dist/web.d.ts delete mode 100644 dist/web.d.ts.map delete mode 100644 dist/web.js delete mode 100644 dist/web.js.map delete mode 100644 package-lock.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml diff --git a/.gitignore b/.gitignore index a4886c1..797cc0a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ node auto-login/credentials.json auto-login/debug_*.png auto-login/__pycache__/ +/.idea/ +/dist/ +opencode.json diff --git a/dist/auth-sync.d.ts b/dist/auth-sync.d.ts deleted file mode 100644 index dd6ed90..0000000 --- a/dist/auth-sync.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { Auth } from '@opencode-ai/sdk'; -export declare function syncAuthFromOpenCode(getAuth: () => Promise): Promise; -//# sourceMappingURL=auth-sync.d.ts.map \ No newline at end of file diff --git a/dist/auth-sync.d.ts.map b/dist/auth-sync.d.ts.map deleted file mode 100644 index bb86b0c..0000000 --- a/dist/auth-sync.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth-sync.d.ts","sourceRoot":"","sources":["../src/auth-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAkD5C,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDtF"} \ No newline at end of file diff --git a/dist/auth-sync.js b/dist/auth-sync.js deleted file mode 100644 index 72b0ac1..0000000 --- a/dist/auth-sync.js +++ /dev/null @@ -1,105 +0,0 @@ -import { addAccount, loadStore, updateAccount } from './store.js'; -import { decodeJwtPayload, getAccountIdFromClaims, getEmailFromClaims } from './codex-auth.js'; -const OPENAI_ISSUER = 'https://auth.openai.com'; -const AUTH_SYNC_COOLDOWN_MS = 10_000; -let lastSyncedAccess = null; -let lastSyncAt = 0; -async function fetchEmail(accessToken) { - try { - const res = await fetch(`${OPENAI_ISSUER}/userinfo`, { - headers: { Authorization: `Bearer ${accessToken}` } - }); - if (!res.ok) - return undefined; - const user = (await res.json()); - return user.email; - } - catch { - return undefined; - } -} -function findAccountAliasByToken(access, refresh) { - const store = loadStore(); - for (const account of Object.values(store.accounts)) { - if (account.accessToken === access) - return account.alias; - if (refresh && account.refreshToken === refresh) - return account.alias; - } - return null; -} -function findAccountAliasByEmail(email, store) { - for (const account of Object.values(store.accounts)) { - if (account.email && account.email === email) - return account.alias; - } - return null; -} -function buildAlias(email, existingAliases) { - const base = email ? email.split('@')[0] : 'account'; - let candidate = base || 'account'; - let suffix = 1; - while (existingAliases.has(candidate)) { - candidate = `${base}-${suffix}`; - suffix += 1; - } - return candidate; -} -export async function syncAuthFromOpenCode(getAuth) { - const now = Date.now(); - if (now - lastSyncAt < AUTH_SYNC_COOLDOWN_MS) - return; - lastSyncAt = now; - let auth = null; - try { - auth = await getAuth(); - } - catch { - return; - } - if (!auth || auth.type !== 'oauth') - return; - if (!auth.access) - return; - if (auth.access === lastSyncedAccess) - return; - lastSyncedAccess = auth.access; - const existingAlias = findAccountAliasByToken(auth.access, auth.refresh); - const accessClaims = decodeJwtPayload(auth.access); - const derivedEmail = getEmailFromClaims(accessClaims); - const derivedAccountId = getAccountIdFromClaims(accessClaims); - if (existingAlias) { - updateAccount(existingAlias, { - accessToken: auth.access, - refreshToken: auth.refresh, - expiresAt: auth.expires, - email: derivedEmail, - accountId: derivedAccountId - }); - return; - } - const store = loadStore(); - const email = (await fetchEmail(auth.access)) || derivedEmail; - if (email) { - const existingByEmail = findAccountAliasByEmail(email, store); - if (existingByEmail) { - updateAccount(existingByEmail, { - accessToken: auth.access, - refreshToken: auth.refresh, - expiresAt: auth.expires, - email - }); - return; - } - } - const alias = buildAlias(email, new Set(Object.keys(store.accounts))); - addAccount(alias, { - accessToken: auth.access, - refreshToken: auth.refresh, - expiresAt: auth.expires, - email, - accountId: derivedAccountId, - source: 'opencode' - }); -} -//# sourceMappingURL=auth-sync.js.map \ No newline at end of file diff --git a/dist/auth-sync.js.map b/dist/auth-sync.js.map deleted file mode 100644 index 33d5579..0000000 --- a/dist/auth-sync.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth-sync.js","sourceRoot":"","sources":["../src/auth-sync.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/auth.d.ts b/dist/auth.d.ts deleted file mode 100644 index d2d51a6..0000000 --- a/dist/auth.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { AccountCredentials } from './types.js'; -interface AuthorizationFlow { - pkce: { - verifier: string; - challenge: string; - }; - state: string; - url: string; - redirectUri: string; - port: number; -} -export interface LoginAccountOptions { - timeoutMs?: number; -} -export declare function createAuthorizationFlow(port?: number): Promise; -export declare function loginAccount(alias: string, flow?: AuthorizationFlow, options?: LoginAccountOptions): Promise; -export declare function refreshToken(alias: string): Promise; -export declare function ensureValidToken(alias: string): Promise; -export {}; -//# sourceMappingURL=auth.d.ts.map \ No newline at end of file diff --git a/dist/auth.d.ts.map b/dist/auth.d.ts.map deleted file mode 100644 index 4ce50b3..0000000 --- a/dist/auth.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAqBpD,UAAU,iBAAiB;IACzB,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7C,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;CACb;AAQD,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAsB,uBAAuB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoBvF;AAkCD,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,iBAAiB,EACxB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAiK7B;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAiEpF;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAc5E"} \ No newline at end of file diff --git a/dist/auth.js b/dist/auth.js deleted file mode 100644 index dbd2ff3..0000000 --- a/dist/auth.js +++ /dev/null @@ -1,283 +0,0 @@ -import { createHash, randomBytes } from 'node:crypto'; -import * as http from 'http'; -import * as url from 'url'; -import { addAccount, updateAccount, loadStore } from './store.js'; -import { clearAuthInvalid } from './rotation.js'; -import { decodeJwtPayload, getAccountIdFromClaims, getEmailFromClaims, getExpiryFromClaims, getPlanTypeFromClaims } from './codex-auth.js'; -const OPENAI_ISSUER = 'https://auth.openai.com'; -const AUTHORIZE_URL = `${OPENAI_ISSUER}/oauth/authorize`; -const TOKEN_URL = `${OPENAI_ISSUER}/oauth/token`; -const CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann'; -const DEFAULT_REDIRECT_PORTS = [1455, 1456, 1457, 1458, 1459]; -const SCOPES = ['openid', 'profile', 'email', 'offline_access']; -function getRedirectUri(port) { - return `http://localhost:${port}/auth/callback`; -} -function generatePKCE() { - const verifier = randomBytes(32).toString('base64url'); - const challenge = createHash('sha256').update(verifier).digest('base64url'); - return { verifier, challenge }; -} -export async function createAuthorizationFlow(port) { - const pkce = generatePKCE(); - const state = randomBytes(16).toString('hex'); - const redirectPort = port || DEFAULT_REDIRECT_PORTS[0]; - const redirectUri = getRedirectUri(redirectPort); - const authUrl = new URL(AUTHORIZE_URL); - authUrl.searchParams.set('client_id', CLIENT_ID); - authUrl.searchParams.set('redirect_uri', redirectUri); - authUrl.searchParams.set('response_type', 'code'); - authUrl.searchParams.set('scope', SCOPES.join(' ')); - authUrl.searchParams.set('code_challenge', pkce.challenge); - authUrl.searchParams.set('code_challenge_method', 'S256'); - authUrl.searchParams.set('state', state); - authUrl.searchParams.set('audience', 'https://api.openai.com/v1'); - authUrl.searchParams.set('id_token_add_organizations', 'true'); - authUrl.searchParams.set('codex_cli_simplified_flow', 'true'); - authUrl.searchParams.set('originator', 'codex_cli_rs'); - return { pkce, state, url: authUrl.toString(), redirectUri, port: redirectPort }; -} -function tryListenOnPort(server, port) { - return new Promise((resolve, reject) => { - const onError = (err) => { - server.off('error', onError); - reject(err); - }; - server.on('error', onError); - server.listen(port, () => { - server.off('error', onError); - resolve(); - }); - }); -} -async function findAvailablePort(server, ports) { - for (const port of ports) { - try { - await tryListenOnPort(server, port); - return port; - } - catch (err) { - if (err.code === 'EADDRINUSE') { - continue; - } - throw err; - } - } - throw new Error(`All ports ${ports.join(', ')} are in use. Stop Codex CLI if running.`); -} -export async function loginAccount(alias, flow, options) { - const ports = DEFAULT_REDIRECT_PORTS; - let activeFlow = flow; - let server = null; - const timeoutMs = Math.max(30_000, options?.timeoutMs ?? 5 * 60 * 1000); - return new Promise(async (resolve, reject) => { - let finished = false; - let timeout = null; - const cleanup = () => { - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - if (server) { - server.close(); - server = null; - } - }; - const finish = (fn) => { - if (finished) - return; - finished = true; - cleanup(); - fn(); - }; - server = http.createServer(async (req, res) => { - if (!req.url?.startsWith('/auth/callback')) { - res.writeHead(404); - res.end('Not found'); - return; - } - if (!activeFlow) { - res.writeHead(500); - res.end('No active flow'); - finish(() => reject(new Error('No active flow'))); - return; - } - const parsedUrl = url.parse(req.url, true); - const code = parsedUrl.query.code; - const returnedState = parsedUrl.query.state; - if (!code) { - res.writeHead(400); - res.end('No authorization code received'); - finish(() => reject(new Error('No authorization code'))); - return; - } - if (returnedState && returnedState !== activeFlow.state) { - res.writeHead(400); - res.end('Invalid state'); - finish(() => reject(new Error('Invalid state'))); - return; - } - try { - const tokenRes = await fetch(TOKEN_URL, { - method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: new URLSearchParams({ - grant_type: 'authorization_code', - client_id: CLIENT_ID, - code, - code_verifier: activeFlow.pkce.verifier, - redirect_uri: activeFlow.redirectUri - }) - }); - if (!tokenRes.ok) { - throw new Error(`Token exchange failed: ${tokenRes.status}`); - } - const tokens = (await tokenRes.json()); - if (!tokens.refresh_token) { - throw new Error('Token exchange did not return a refresh_token'); - } - const now = Date.now(); - const accessClaims = decodeJwtPayload(tokens.access_token); - const idClaims = tokens.id_token ? decodeJwtPayload(tokens.id_token) : null; - const expiresAt = getExpiryFromClaims(accessClaims) || getExpiryFromClaims(idClaims) || now + tokens.expires_in * 1000; - let email = getEmailFromClaims(idClaims) || getEmailFromClaims(accessClaims); - try { - const userRes = await fetch(`${OPENAI_ISSUER}/userinfo`, { - headers: { Authorization: `Bearer ${tokens.access_token}` } - }); - if (userRes.ok) { - const user = (await userRes.json()); - email = user.email || email; - } - } - catch { - /* user info fetch is non-critical */ - } - const accountId = getAccountIdFromClaims(idClaims) || - getAccountIdFromClaims(accessClaims); - const planType = getPlanTypeFromClaims(idClaims) || - getPlanTypeFromClaims(accessClaims); - const store = addAccount(alias, { - accessToken: tokens.access_token, - refreshToken: tokens.refresh_token, - idToken: tokens.id_token, - accountId, - planType, - expiresAt, - email, - lastRefresh: new Date(now).toISOString(), - lastSeenAt: now, - source: 'opencode', - authInvalid: false, - authInvalidatedAt: undefined - }); - const account = store.accounts[alias]; - res.writeHead(200, { 'Content-Type': 'text/html' }); - res.end(` - - -

Account "${alias}" authenticated!

-

${email || 'Unknown email'}

-

You can close this window.

- - - `); - finish(() => resolve(account)); - } - catch (err) { - res.writeHead(500); - res.end('Authentication failed'); - finish(() => reject(err)); - } - }); - try { - const actualPort = await findAvailablePort(server, ports); - if (!activeFlow || activeFlow.port !== actualPort) { - activeFlow = await createAuthorizationFlow(actualPort); - } - console.log(`\n[multi-auth] Login for account "${alias}"`); - console.log(`[multi-auth] Open this URL in your browser:\n`); - console.log(` ${activeFlow.url}\n`); - console.log(`[multi-auth] Waiting for callback on port ${actualPort}...`); - } - catch (err) { - finish(() => reject(err)); - return; - } - timeout = setTimeout(() => { - finish(() => reject(new Error(`Login timeout after ${Math.round(timeoutMs / 1000)}s - no callback received`))); - }, timeoutMs); - }); -} -export async function refreshToken(alias) { - const store = loadStore(); - const account = store.accounts[alias]; - if (!account?.refreshToken) { - console.error(`[multi-auth] No refresh token for ${alias}`); - return null; - } - try { - const tokenRes = await fetch(TOKEN_URL, { - method: 'POST', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: new URLSearchParams({ - grant_type: 'refresh_token', - client_id: CLIENT_ID, - refresh_token: account.refreshToken - }) - }); - if (!tokenRes.ok) { - console.error(`[multi-auth] Refresh failed for ${alias}: ${tokenRes.status}`); - if (tokenRes.status === 401 || tokenRes.status === 403) { - try { - updateAccount(alias, { - authInvalid: true, - authInvalidatedAt: Date.now() - }); - } - catch { - // ignore - } - } - return null; - } - const tokens = (await tokenRes.json()); - const accessClaims = decodeJwtPayload(tokens.access_token); - const idClaims = tokens.id_token ? decodeJwtPayload(tokens.id_token) : null; - const expiresAt = getExpiryFromClaims(accessClaims) || getExpiryFromClaims(idClaims) || Date.now() + tokens.expires_in * 1000; - const updates = { - accessToken: tokens.access_token, - refreshToken: tokens.refresh_token || account.refreshToken, - expiresAt, - lastRefresh: new Date().toISOString(), - idToken: tokens.id_token || account.idToken, - accountId: getAccountIdFromClaims(idClaims) || - getAccountIdFromClaims(accessClaims) || - account.accountId, - planType: getPlanTypeFromClaims(idClaims) || - getPlanTypeFromClaims(accessClaims) || - account.planType - }; - const updatedStore = updateAccount(alias, updates); - clearAuthInvalid(alias); - return updatedStore.accounts[alias]; - } - catch (err) { - console.error(`[multi-auth] Refresh error for ${alias}:`, err); - return null; - } -} -export async function ensureValidToken(alias) { - const store = loadStore(); - const account = store.accounts[alias]; - if (!account) - return null; - const bufferMs = 5 * 60 * 1000; - if (account.expiresAt < Date.now() + bufferMs) { - console.log(`[multi-auth] Refreshing token for ${alias}`); - const refreshed = await refreshToken(alias); - return refreshed?.accessToken || null; - } - return account.accessToken; -} -//# sourceMappingURL=auth.js.map \ No newline at end of file diff --git a/dist/auth.js.map b/dist/auth.js.map deleted file mode 100644 index 9b43592..0000000 --- a/dist/auth.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/cli.d.ts b/dist/cli.d.ts deleted file mode 100644 index faaadd5..0000000 --- a/dist/cli.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env node -export {}; -//# sourceMappingURL=cli.d.ts.map \ No newline at end of file diff --git a/dist/cli.d.ts.map b/dist/cli.d.ts.map deleted file mode 100644 index f022439..0000000 --- a/dist/cli.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/cli.js b/dist/cli.js deleted file mode 100644 index 2b96a4f..0000000 --- a/dist/cli.js +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env node -import { fileURLToPath } from 'node:url'; -import { loginAccount } from './auth.js'; -import { removeAccount, listAccounts, getStorePath, loadStore } from './store.js'; -import { startWebConsole } from './web.js'; -import { disableService, installService, serviceStatus } from './systemd.js'; -const args = process.argv.slice(2); -const command = args[0]; -const alias = args[1]; -function getFlagValue(flag) { - const idx = args.indexOf(flag); - if (idx === -1) - return undefined; - return args[idx + 1]; -} -async function main() { - switch (command) { - case 'add': - case 'login': { - if (!alias) { - console.error('Usage: opencode-multi-auth add '); - console.error('Example: opencode-multi-auth add work'); - process.exit(1); - } - try { - const account = await loginAccount(alias); - console.log(`\nAccount "${alias}" added successfully!`); - console.log(`Email: ${account.email || 'unknown'}`); - } - catch (err) { - console.error(`Failed to add account: ${err}`); - process.exit(1); - } - break; - } - case 'remove': - case 'rm': { - if (!alias) { - console.error('Usage: opencode-multi-auth remove '); - process.exit(1); - } - removeAccount(alias); - console.log(`Account "${alias}" removed.`); - break; - } - case 'list': - case 'ls': { - const accounts = listAccounts(); - if (accounts.length === 0) { - console.log('No accounts configured.'); - console.log('Add one with: opencode-multi-auth add '); - } - else { - console.log('\nConfigured accounts:\n'); - for (const acc of accounts) { - console.log(` ${acc.alias}: ${acc.email || 'unknown email'} (uses: ${acc.usageCount})`); - } - console.log(); - } - break; - } - case 'status': { - const store = loadStore(); - const accounts = Object.values(store.accounts); - console.log('\n[multi-auth] Account Status\n'); - console.log('Strategy: round-robin'); - console.log(`Accounts: ${accounts.length}`); - console.log(`Active: ${store.activeAlias || 'none'}\n`); - if (accounts.length === 0) { - console.log('No accounts configured. Run: opencode-multi-auth add \n'); - return; - } - for (const acc of accounts) { - const isActive = acc.alias === store.activeAlias ? ' (active)' : ''; - const isRateLimited = acc.rateLimitedUntil && acc.rateLimitedUntil > Date.now() - ? ` [RATE LIMITED until ${new Date(acc.rateLimitedUntil).toLocaleTimeString()}]` - : ''; - const expiry = new Date(acc.expiresAt).toLocaleString(); - console.log(` ${acc.alias}${isActive}${isRateLimited}`); - console.log(` Email: ${acc.email || 'unknown'}`); - console.log(` Uses: ${acc.usageCount}`); - console.log(` Token expires: ${expiry}`); - console.log(); - } - break; - } - case 'path': { - console.log(getStorePath()); - break; - } - case 'web': { - const portArg = getFlagValue('--port'); - const hostArg = getFlagValue('--host'); - const port = portArg ? Number(portArg) : undefined; - if (portArg && Number.isNaN(port)) { - console.error('Invalid --port value'); - process.exit(1); - } - startWebConsole({ port, host: hostArg }); - break; - } - case 'service': { - const action = args[1] || 'status'; - const portArg = getFlagValue('--port'); - const hostArg = getFlagValue('--host'); - const port = portArg ? Number(portArg) : undefined; - if (portArg && Number.isNaN(port)) { - console.error('Invalid --port value'); - process.exit(1); - } - const cliPath = fileURLToPath(import.meta.url); - if (action === 'install') { - const file = installService({ cliPath, host: hostArg, port }); - console.log(`Installed systemd user service at ${file}`); - break; - } - if (action === 'disable') { - disableService(); - console.log('Disabled codex-soft systemd user service.'); - break; - } - serviceStatus(); - break; - } - case 'help': - case '--help': - case '-h': - default: { - console.log(` -opencode-multi-auth - Multi-account OAuth rotation for OpenAI Codex - -Commands: - add Add a new account (opens browser for OAuth) - remove Remove an account - list List all configured accounts - status Show detailed account status - path Show config file location - web Launch local Codex auth.json dashboard (use --port/--host) - service Install/disable systemd user service (install|disable|status) - help Show this help message - -Examples: - opencode-multi-auth add personal - opencode-multi-auth add work - opencode-multi-auth add backup - opencode-multi-auth status - opencode-multi-auth web --port 3434 --host 127.0.0.1 - opencode-multi-auth service install --port 3434 --host 127.0.0.1 - -After adding accounts, the plugin auto-rotates between them. -`); - break; - } - } -} -main().catch(err => { - console.error('Fatal error:', err); - process.exit(1); -}); -//# sourceMappingURL=cli.js.map \ No newline at end of file diff --git a/dist/cli.js.map b/dist/cli.js.map deleted file mode 100644 index af79dd4..0000000 --- a/dist/cli.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/codex-auth.d.ts b/dist/codex-auth.d.ts deleted file mode 100644 index 7af46ae..0000000 --- a/dist/codex-auth.d.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { loadStore } from './store.js'; -export interface CodexAuthTokens { - id_token?: string; - access_token?: string; - refresh_token?: string; - account_id?: string; -} -export interface CodexAuthFile { - OPENAI_API_KEY?: string | null; - tokens?: CodexAuthTokens; - last_refresh?: string; -} -export declare function getCodexAuthPath(): string; -export interface CodexAuthSummary { - email?: string; - accountId?: string; - accountUserId?: string; - userId?: string; - planType?: string; - expiresAt?: number; - lastRefresh?: string; - hasAccessToken: boolean; - hasRefreshToken: boolean; - hasIdToken: boolean; -} -export declare function loadCodexAuthFile(): CodexAuthFile | null; -export declare function writeCodexAuthFile(auth: CodexAuthFile): void; -export declare function decodeJwtPayload(token: string): Record | null; -export declare function getEmailFromClaims(claims: Record | null): string | undefined; -export declare function getAccountIdFromClaims(claims: Record | null): string | undefined; -export declare function getPlanTypeFromClaims(claims: Record | null): string | undefined; -export declare function getExpiryFromClaims(claims: Record | null): number | undefined; -export declare function getCodexAuthSummary(): CodexAuthSummary; -export declare function resolveAliasForCurrentAuth(store?: ReturnType): string | null; -export declare function syncCodexAuthFile(): { - alias: string | null; - added: boolean; - updated: boolean; - authEmail?: string; - authAccountId?: string; -}; -export declare function getCodexAuthStatus(): { - error: string | null; -}; -export declare function writeCodexAuthForAlias(alias: string): void; -//# sourceMappingURL=codex-auth.d.ts.map \ No newline at end of file diff --git a/dist/codex-auth.d.ts.map b/dist/codex-auth.d.ts.map deleted file mode 100644 index 0979e2a..0000000 --- a/dist/codex-auth.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"codex-auth.d.ts","sourceRoot":"","sources":["../src/codex-auth.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,SAAS,EAAiB,MAAM,YAAY,CAAA;AAGjE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAiBD,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,OAAO,CAAA;IACvB,eAAe,EAAE,OAAO,CAAA;IACxB,UAAU,EAAE,OAAO,CAAA;CACpB;AAQD,wBAAgB,iBAAiB,IAAI,aAAa,GAAG,IAAI,CAWxD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAK5D;AAqDD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAW1E;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAMzF;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAI7F;AAiBD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAI5F;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAK1F;AAmDD,wBAAgB,mBAAmB,IAAI,gBAAgB,CA0BtD;AAED,wBAAgB,0BAA0B,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,GAAG,MAAM,GAAG,IAAI,CAuB9F;AAED,wBAAgB,iBAAiB,IAAI;IACnC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAmEA;AAED,wBAAgB,kBAAkB,IAAI;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAE7D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAkC1D"} \ No newline at end of file diff --git a/dist/codex-auth.js b/dist/codex-auth.js deleted file mode 100644 index 485ce24..0000000 --- a/dist/codex-auth.js +++ /dev/null @@ -1,331 +0,0 @@ -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import { addAccount, loadStore, updateAccount } from './store.js'; -const CODEX_AUTH_FILE_ENV = 'OPENCODE_MULTI_AUTH_CODEX_AUTH_FILE'; -function getCodexAuthFilePath() { - const override = process.env[CODEX_AUTH_FILE_ENV]; - if (override && override.trim()) - return path.resolve(override.trim()); - const CODEX_DIR = path.join(os.homedir(), '.codex'); - return path.join(CODEX_DIR, 'auth.json'); -} -const CODEX_DIR = path.join(os.homedir(), '.codex'); -const CODEX_AUTH_FILE = getCodexAuthFilePath(); -let lastFingerprint = null; -let lastAuthError = null; -export function getCodexAuthPath() { - return CODEX_AUTH_FILE; -} -function ensureDir() { - if (!fs.existsSync(CODEX_DIR)) { - fs.mkdirSync(CODEX_DIR, { recursive: true, mode: 0o700 }); - } -} -export function loadCodexAuthFile() { - lastAuthError = null; - if (!fs.existsSync(CODEX_AUTH_FILE)) - return null; - try { - const raw = fs.readFileSync(CODEX_AUTH_FILE, 'utf-8'); - return JSON.parse(raw); - } - catch (err) { - lastAuthError = 'Failed to parse codex auth.json'; - console.error('[multi-auth] Failed to parse codex auth.json:', err); - return null; - } -} -export function writeCodexAuthFile(auth) { - ensureDir(); - fs.writeFileSync(CODEX_AUTH_FILE, JSON.stringify(auth, null, 2), { - mode: 0o600 - }); -} -function normalizeTokens(auth) { - if (!auth || typeof auth !== 'object') - return null; - const tokens = (auth.tokens && typeof auth.tokens === 'object') ? auth.tokens : auth; - const accessToken = tokens.access_token ?? - tokens.accessToken ?? - tokens.access ?? - auth.access_token ?? - auth.accessToken ?? - auth.access; - const refreshToken = tokens.refresh_token ?? - tokens.refreshToken ?? - tokens.refresh ?? - auth.refresh_token ?? - auth.refreshToken ?? - auth.refresh; - const idToken = tokens.id_token ?? - tokens.idToken ?? - tokens.id ?? - auth.id_token ?? - auth.idToken ?? - auth.id; - const accountId = tokens.account_id ?? - tokens.accountId ?? - auth.account_id ?? - auth.accountId; - const lastRefresh = auth.last_refresh ?? auth.lastRefresh; - const result = { - accessToken: typeof accessToken === 'string' ? accessToken : undefined, - refreshToken: typeof refreshToken === 'string' ? refreshToken : undefined, - idToken: typeof idToken === 'string' ? idToken : undefined, - accountId: typeof accountId === 'string' ? accountId : undefined, - lastRefresh: typeof lastRefresh === 'string' ? lastRefresh : undefined - }; - if (!result.accessToken && !result.refreshToken && !result.idToken && !result.accountId) { - return null; - } - return result; -} -export function decodeJwtPayload(token) { - try { - const parts = token.split('.'); - if (parts.length !== 3) - return null; - const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/'); - const padded = payload.padEnd(payload.length + ((4 - (payload.length % 4)) % 4), '='); - const decoded = Buffer.from(padded, 'base64').toString('utf-8'); - return JSON.parse(decoded); - } - catch { - return null; - } -} -export function getEmailFromClaims(claims) { - if (!claims) - return undefined; - if (typeof claims.email === 'string') - return claims.email; - const profile = claims['https://api.openai.com/profile']; - if (profile?.email) - return profile.email; - return undefined; -} -export function getAccountIdFromClaims(claims) { - if (!claims) - return undefined; - const auth = claims['https://api.openai.com/auth']; - return auth?.chatgpt_account_id; -} -function getAccountUserIdFromClaims(claims) { - if (!claims) - return undefined; - const auth = claims['https://api.openai.com/auth']; - if (typeof auth?.chatgpt_account_user_id === 'string') - return auth.chatgpt_account_user_id; - return undefined; -} -function getUserIdFromClaims(claims) { - if (!claims) - return undefined; - const auth = claims['https://api.openai.com/auth']; - if (typeof auth?.user_id === 'string') - return auth.user_id; - if (typeof auth?.chatgpt_user_id === 'string') - return auth.chatgpt_user_id; - return undefined; -} -export function getPlanTypeFromClaims(claims) { - if (!claims) - return undefined; - const auth = claims['https://api.openai.com/auth']; - return typeof auth?.chatgpt_plan_type === 'string' ? auth.chatgpt_plan_type : undefined; -} -export function getExpiryFromClaims(claims) { - if (!claims) - return undefined; - const exp = claims.exp; - if (typeof exp === 'number') - return exp * 1000; - return undefined; -} -function fingerprintTokens(tokens) { - return `${tokens.access_token || ''}:${tokens.refresh_token || ''}:${tokens.id_token || ''}`; -} -function buildAlias(email, accountId, store) { - const base = email?.split('@')[0] || accountId?.slice(0, 8) || `account-${Date.now()}`; - const existing = new Set(Object.keys(store.accounts)); - let candidate = base || `account-${Date.now()}`; - let suffix = 1; - while (existing.has(candidate)) { - candidate = `${base}-${suffix}`; - suffix += 1; - } - return candidate; -} -function findMatchingAlias(tokens, accountId, accountUserId, userId, email, store) { - for (const account of Object.values(store.accounts)) { - const existingAccountUserId = account.accountUserId || - getAccountUserIdFromClaims(decodeJwtPayload(account.accessToken)) || - (account.idToken ? getAccountUserIdFromClaims(decodeJwtPayload(account.idToken)) : undefined); - if (accountUserId) { - if (existingAccountUserId === accountUserId) - return account.alias; - continue; - } - const existingUserId = account.userId || - getUserIdFromClaims(decodeJwtPayload(account.accessToken)) || - (account.idToken ? getUserIdFromClaims(decodeJwtPayload(account.idToken)) : undefined); - if (userId && existingUserId === userId) - return account.alias; - if (tokens.access_token && account.accessToken === tokens.access_token) - return account.alias; - if (tokens.refresh_token && account.refreshToken === tokens.refresh_token) - return account.alias; - if (tokens.id_token && account.idToken === tokens.id_token) - return account.alias; - if (email && account.email === email) - return account.alias; - if (!userId && accountId && account.accountId === accountId) - return account.alias; - } - return null; -} -export function getCodexAuthSummary() { - const auth = loadCodexAuthFile(); - const normalized = normalizeTokens(auth); - const access = normalized?.accessToken; - const refresh = normalized?.refreshToken; - const idToken = normalized?.idToken; - const accessClaims = access ? decodeJwtPayload(access) : null; - const idClaims = idToken ? decodeJwtPayload(idToken) : null; - const email = getEmailFromClaims(idClaims) || getEmailFromClaims(accessClaims); - const accountId = normalized?.accountId || getAccountIdFromClaims(idClaims) || getAccountIdFromClaims(accessClaims); - const accountUserId = getAccountUserIdFromClaims(accessClaims) || getAccountUserIdFromClaims(idClaims); - const userId = getUserIdFromClaims(accessClaims) || getUserIdFromClaims(idClaims); - const planType = getPlanTypeFromClaims(accessClaims) || getPlanTypeFromClaims(idClaims); - const expiresAt = getExpiryFromClaims(accessClaims) || getExpiryFromClaims(idClaims); - return { - email, - accountId, - accountUserId, - userId, - planType, - expiresAt, - lastRefresh: normalized?.lastRefresh, - hasAccessToken: Boolean(access), - hasRefreshToken: Boolean(refresh), - hasIdToken: Boolean(idToken) - }; -} -export function resolveAliasForCurrentAuth(store) { - const auth = loadCodexAuthFile(); - const normalized = normalizeTokens(auth); - if (!normalized) - return null; - const accessClaims = normalized.accessToken ? decodeJwtPayload(normalized.accessToken) : null; - const idClaims = normalized.idToken ? decodeJwtPayload(normalized.idToken) : null; - const email = getEmailFromClaims(idClaims) || getEmailFromClaims(accessClaims); - const accountId = normalized.accountId || getAccountIdFromClaims(idClaims) || getAccountIdFromClaims(accessClaims); - const accountUserId = getAccountUserIdFromClaims(accessClaims) || getAccountUserIdFromClaims(idClaims); - const userId = getUserIdFromClaims(accessClaims) || getUserIdFromClaims(idClaims); - const targetStore = store ?? loadStore(); - return findMatchingAlias({ - access_token: normalized.accessToken, - refresh_token: normalized.refreshToken, - id_token: normalized.idToken - }, accountId, accountUserId, userId, email, targetStore); -} -export function syncCodexAuthFile() { - const auth = loadCodexAuthFile(); - const normalized = normalizeTokens(auth); - if (!normalized?.accessToken || !normalized.refreshToken) { - lastAuthError = 'Missing access_token/refresh_token in auth.json'; - const accessClaims = normalized?.accessToken ? decodeJwtPayload(normalized.accessToken) : null; - const idClaims = normalized?.idToken ? decodeJwtPayload(normalized.idToken) : null; - const email = getEmailFromClaims(idClaims) || getEmailFromClaims(accessClaims); - const accountId = normalized?.accountId || getAccountIdFromClaims(idClaims) || getAccountIdFromClaims(accessClaims); - return { - alias: null, - added: false, - updated: false, - authEmail: email, - authAccountId: accountId - }; - } - const tokens = { - access_token: normalized.accessToken, - refresh_token: normalized.refreshToken, - id_token: normalized.idToken, - account_id: normalized.accountId - }; - const fingerprint = fingerprintTokens(tokens); - const accessClaims = decodeJwtPayload(normalized.accessToken); - const idClaims = normalized.idToken ? decodeJwtPayload(normalized.idToken) : null; - const email = getEmailFromClaims(idClaims) || getEmailFromClaims(accessClaims); - const accountId = normalized.accountId || getAccountIdFromClaims(idClaims) || getAccountIdFromClaims(accessClaims); - const accountUserId = getAccountUserIdFromClaims(accessClaims) || getAccountUserIdFromClaims(idClaims); - const userId = getUserIdFromClaims(accessClaims) || getUserIdFromClaims(idClaims); - const planType = getPlanTypeFromClaims(idClaims) || getPlanTypeFromClaims(accessClaims); - const expiresAt = getExpiryFromClaims(accessClaims) || getExpiryFromClaims(idClaims) || Date.now(); - const store = loadStore(); - const now = Date.now(); - const alias = findMatchingAlias(tokens, accountId, accountUserId, userId, email, store); - if (lastFingerprint === fingerprint && alias) { - return { alias, added: false, updated: false, authEmail: email, authAccountId: accountId }; - } - lastFingerprint = fingerprint; - const update = { - accessToken: normalized.accessToken, - refreshToken: normalized.refreshToken, - accountId, - accountUserId, - userId, - planType, - expiresAt, - email, - lastRefresh: normalized.lastRefresh, - lastSeenAt: now, - source: 'codex' - }; - if (normalized.idToken) { - update.idToken = normalized.idToken; - } - if (alias) { - updateAccount(alias, update); - return { alias, added: false, updated: true, authEmail: email, authAccountId: accountId }; - } - const newAlias = buildAlias(email, accountId, store); - addAccount(newAlias, update); - return { alias: newAlias, added: true, updated: true, authEmail: email, authAccountId: accountId }; -} -export function getCodexAuthStatus() { - return { error: lastAuthError }; -} -export function writeCodexAuthForAlias(alias) { - const store = loadStore(); - const account = store.accounts[alias]; - if (!account) { - throw new Error(`Unknown alias: ${alias}`); - } - if (!account.accessToken || !account.refreshToken) { - throw new Error('Missing token data for alias'); - } - const current = loadCodexAuthFile(); - const baseTokens = { - access_token: account.accessToken, - refresh_token: account.refreshToken - }; - if (account.idToken) { - baseTokens.id_token = account.idToken; - } - if (account.accountId) { - baseTokens.account_id = account.accountId; - } - const auth = { - OPENAI_API_KEY: current?.OPENAI_API_KEY ?? null, - tokens: baseTokens, - last_refresh: new Date().toISOString() - }; - writeCodexAuthFile(auth); - updateAccount(alias, { - lastRefresh: auth.last_refresh, - lastSeenAt: Date.now(), - source: 'codex' - }); -} -//# sourceMappingURL=codex-auth.js.map \ No newline at end of file diff --git a/dist/codex-auth.js.map b/dist/codex-auth.js.map deleted file mode 100644 index 9a9967f..0000000 --- a/dist/codex-auth.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"codex-auth.js","sourceRoot":"","sources":["../src/codex-auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAgBjE,MAAM,mBAAmB,GAAG,qCAAqC,CAAA;AAEjE,SAAS,oBAAoB;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;IACjD,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;IACrE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAA;IACnD,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAA;AAC1C,CAAC;AAED,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAA;AACnD,MAAM,eAAe,GAAG,oBAAoB,EAAE,CAAA;AAE9C,IAAI,eAAe,GAAkB,IAAI,CAAA;AACzC,IAAI,aAAa,GAAkB,IAAI,CAAA;AAEvC,MAAM,UAAU,gBAAgB;IAC9B,OAAO,eAAe,CAAA;AACxB,CAAC;AAeD,SAAS,SAAS;IAChB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAC3D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,aAAa,GAAG,IAAI,CAAA;IACpB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC;QAAE,OAAO,IAAI,CAAA;IAChD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;QACrD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAA;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,aAAa,GAAG,iCAAiC,CAAA;QACjD,OAAO,CAAC,KAAK,CAAC,+CAA+C,EAAE,GAAG,CAAC,CAAA;QACnE,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAmB;IACpD,SAAS,EAAE,CAAA;IACX,EAAE,CAAC,aAAa,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QAC/D,IAAI,EAAE,KAAK;KACZ,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,IAAS;IAChC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAElD,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;IAEpF,MAAM,WAAW,GACf,MAAM,CAAC,YAAY;QACnB,MAAM,CAAC,WAAW;QAClB,MAAM,CAAC,MAAM;QACb,IAAI,CAAC,YAAY;QACjB,IAAI,CAAC,WAAW;QAChB,IAAI,CAAC,MAAM,CAAA;IAEb,MAAM,YAAY,GAChB,MAAM,CAAC,aAAa;QACpB,MAAM,CAAC,YAAY;QACnB,MAAM,CAAC,OAAO;QACd,IAAI,CAAC,aAAa;QAClB,IAAI,CAAC,YAAY;QACjB,IAAI,CAAC,OAAO,CAAA;IAEd,MAAM,OAAO,GACX,MAAM,CAAC,QAAQ;QACf,MAAM,CAAC,OAAO;QACd,MAAM,CAAC,EAAE;QACT,IAAI,CAAC,QAAQ;QACb,IAAI,CAAC,OAAO;QACZ,IAAI,CAAC,EAAE,CAAA;IAET,MAAM,SAAS,GACb,MAAM,CAAC,UAAU;QACjB,MAAM,CAAC,SAAS;QAChB,IAAI,CAAC,UAAU;QACf,IAAI,CAAC,SAAS,CAAA;IAEhB,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,WAAW,CAAA;IAEzD,MAAM,MAAM,GAAgH;QAC1H,WAAW,EAAE,OAAO,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;QACtE,YAAY,EAAE,OAAO,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;QACzE,OAAO,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;QAC1D,SAAS,EAAE,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QAChE,WAAW,EAAE,OAAO,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;KACvE,CAAA;IAED,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACxF,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QACnC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;QAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QACrF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;QAC/D,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAwB,CAAA;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAkC;IACnE,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAC7B,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAA;IACzD,MAAM,OAAO,GAAG,MAAM,CAAC,gCAAgC,CAAmC,CAAA;IAC1F,IAAI,OAAO,EAAE,KAAK;QAAE,OAAO,OAAO,CAAC,KAAK,CAAA;IACxC,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAkC;IACvE,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,6BAA6B,CAAgD,CAAA;IACjG,OAAO,IAAI,EAAE,kBAAkB,CAAA;AACjC,CAAC;AAED,SAAS,0BAA0B,CAAC,MAAkC;IACpE,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,6BAA6B,CAAqD,CAAA;IACtG,IAAI,OAAO,IAAI,EAAE,uBAAuB,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,uBAAuB,CAAA;IAC1F,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAkC;IAC7D,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,6BAA6B,CAA+D,CAAA;IAChH,IAAI,OAAO,IAAI,EAAE,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,OAAO,CAAA;IAC1D,IAAI,OAAO,IAAI,EAAE,eAAe,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,eAAe,CAAA;IAC1E,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAkC;IACtE,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,6BAA6B,CAA+C,CAAA;IAChG,OAAO,OAAO,IAAI,EAAE,iBAAiB,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAA;AACzF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAkC;IACpE,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAA;IACtB,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,GAAG,IAAI,CAAA;IAC9C,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAuB;IAChD,OAAO,GAAG,MAAM,CAAC,YAAY,IAAI,EAAE,IAAI,MAAM,CAAC,aAAa,IAAI,EAAE,IAAI,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAA;AAC9F,CAAC;AAED,SAAS,UAAU,CAAC,KAAyB,EAAE,SAA6B,EAAE,KAAmC;IAC/G,MAAM,IAAI,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,WAAW,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;IACtF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;IACrD,IAAI,SAAS,GAAG,IAAI,IAAI,WAAW,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;IAC/C,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,OAAO,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/B,SAAS,GAAG,GAAG,IAAI,IAAI,MAAM,EAAE,CAAA;QAC/B,MAAM,IAAI,CAAC,CAAA;IACb,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,iBAAiB,CACxB,MAA8F,EAC9F,SAA6B,EAC7B,aAAiC,EACjC,MAA0B,EAC1B,KAAyB,EACzB,KAAmC;IAEnC,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACpD,MAAM,qBAAqB,GACzB,OAAO,CAAC,aAAa;YACrB,0BAA0B,CAAC,gBAAgB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YACjE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QAC/F,IAAI,aAAa,EAAE,CAAC;YAClB,IAAI,qBAAqB,KAAK,aAAa;gBAAE,OAAO,OAAO,CAAC,KAAK,CAAA;YACjE,SAAQ;QACV,CAAC;QAED,MAAM,cAAc,GAClB,OAAO,CAAC,MAAM;YACd,mBAAmB,CAAC,gBAAgB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YAC1D,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QACxF,IAAI,MAAM,IAAI,cAAc,KAAK,MAAM;YAAE,OAAO,OAAO,CAAC,KAAK,CAAA;QAE7D,IAAI,MAAM,CAAC,YAAY,IAAI,OAAO,CAAC,WAAW,KAAK,MAAM,CAAC,YAAY;YAAE,OAAO,OAAO,CAAC,KAAK,CAAA;QAC5F,IAAI,MAAM,CAAC,aAAa,IAAI,OAAO,CAAC,YAAY,KAAK,MAAM,CAAC,aAAa;YAAE,OAAO,OAAO,CAAC,KAAK,CAAA;QAC/F,IAAI,MAAM,CAAC,QAAQ,IAAI,OAAO,CAAC,OAAO,KAAK,MAAM,CAAC,QAAQ;YAAE,OAAO,OAAO,CAAC,KAAK,CAAA;QAChF,IAAI,KAAK,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK;YAAE,OAAO,OAAO,CAAC,KAAK,CAAA;QAC1D,IAAI,CAAC,MAAM,IAAI,SAAS,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,KAAK,CAAA;IACnF,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAA;IAChC,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;IACxC,MAAM,MAAM,GAAG,UAAU,EAAE,WAAW,CAAA;IACtC,MAAM,OAAO,GAAG,UAAU,EAAE,YAAY,CAAA;IACxC,MAAM,OAAO,GAAG,UAAU,EAAE,OAAO,CAAA;IACnC,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC7D,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC3D,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,IAAI,kBAAkB,CAAC,YAAY,CAAC,CAAA;IAC9E,MAAM,SAAS,GAAG,UAAU,EAAE,SAAS,IAAI,sBAAsB,CAAC,QAAQ,CAAC,IAAI,sBAAsB,CAAC,YAAY,CAAC,CAAA;IACnH,MAAM,aAAa,GAAG,0BAA0B,CAAC,YAAY,CAAC,IAAI,0BAA0B,CAAC,QAAQ,CAAC,CAAA;IACtG,MAAM,MAAM,GAAG,mBAAmB,CAAC,YAAY,CAAC,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAA;IACjF,MAAM,QAAQ,GAAG,qBAAqB,CAAC,YAAY,CAAC,IAAI,qBAAqB,CAAC,QAAQ,CAAC,CAAA;IACvF,MAAM,SAAS,GAAG,mBAAmB,CAAC,YAAY,CAAC,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAA;IACpF,OAAO;QACL,KAAK;QACL,SAAS;QACT,aAAa;QACb,MAAM;QACN,QAAQ;QACR,SAAS;QACT,WAAW,EAAE,UAAU,EAAE,WAAW;QACpC,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC;QAC/B,eAAe,EAAE,OAAO,CAAC,OAAO,CAAC;QACjC,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC;KAC7B,CAAA;AACH,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,KAAoC;IAC7E,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAA;IAChC,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;IACxC,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAA;IAC5B,MAAM,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC7F,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACjF,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,IAAI,kBAAkB,CAAC,YAAY,CAAC,CAAA;IAC9E,MAAM,SAAS,GAAG,UAAU,CAAC,SAAS,IAAI,sBAAsB,CAAC,QAAQ,CAAC,IAAI,sBAAsB,CAAC,YAAY,CAAC,CAAA;IAClH,MAAM,aAAa,GAAG,0BAA0B,CAAC,YAAY,CAAC,IAAI,0BAA0B,CAAC,QAAQ,CAAC,CAAA;IACtG,MAAM,MAAM,GAAG,mBAAmB,CAAC,YAAY,CAAC,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAA;IACjF,MAAM,WAAW,GAAG,KAAK,IAAI,SAAS,EAAE,CAAA;IACxC,OAAO,iBAAiB,CACtB;QACE,YAAY,EAAE,UAAU,CAAC,WAAW;QACpC,aAAa,EAAE,UAAU,CAAC,YAAY;QACtC,QAAQ,EAAE,UAAU,CAAC,OAAO;KAC7B,EACD,SAAS,EACT,aAAa,EACb,MAAM,EACN,KAAK,EACL,WAAW,CACZ,CAAA;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB;IAO/B,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAA;IAChC,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;IACxC,IAAI,CAAC,UAAU,EAAE,WAAW,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC;QACzD,aAAa,GAAG,iDAAiD,CAAA;QACjE,MAAM,YAAY,GAAG,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAC9F,MAAM,QAAQ,GAAG,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAClF,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,IAAI,kBAAkB,CAAC,YAAY,CAAC,CAAA;QAC9E,MAAM,SAAS,GAAG,UAAU,EAAE,SAAS,IAAI,sBAAsB,CAAC,QAAQ,CAAC,IAAI,sBAAsB,CAAC,YAAY,CAAC,CAAA;QACnH,OAAO;YACL,KAAK,EAAE,IAAI;YACX,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,KAAK;YACd,SAAS,EAAE,KAAK;YAChB,aAAa,EAAE,SAAS;SACzB,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAoB;QAC9B,YAAY,EAAE,UAAU,CAAC,WAAW;QACpC,aAAa,EAAE,UAAU,CAAC,YAAY;QACtC,QAAQ,EAAE,UAAU,CAAC,OAAO;QAC5B,UAAU,EAAE,UAAU,CAAC,SAAS;KACjC,CAAA;IACD,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAA;IAE7C,MAAM,YAAY,GAAG,gBAAgB,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;IAC7D,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACjF,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,IAAI,kBAAkB,CAAC,YAAY,CAAC,CAAA;IAC9E,MAAM,SAAS,GAAG,UAAU,CAAC,SAAS,IAAI,sBAAsB,CAAC,QAAQ,CAAC,IAAI,sBAAsB,CAAC,YAAY,CAAC,CAAA;IAClH,MAAM,aAAa,GAAG,0BAA0B,CAAC,YAAY,CAAC,IAAI,0BAA0B,CAAC,QAAQ,CAAC,CAAA;IACtG,MAAM,MAAM,GAAG,mBAAmB,CAAC,YAAY,CAAC,IAAI,mBAAmB,CAAC,QAAQ,CAAC,CAAA;IACjF,MAAM,QAAQ,GAAG,qBAAqB,CAAC,QAAQ,CAAC,IAAI,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACvF,MAAM,SAAS,GAAG,mBAAmB,CAAC,YAAY,CAAC,IAAI,mBAAmB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAA;IAElG,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;IACzB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;IACvF,IAAI,eAAe,KAAK,WAAW,IAAI,KAAK,EAAE,CAAC;QAC7C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,CAAA;IAC5F,CAAC;IACD,eAAe,GAAG,WAAW,CAAA;IAC7B,MAAM,MAAM,GAAgC;QAC1C,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,YAAY,EAAE,UAAU,CAAC,YAAY;QACrC,SAAS;QACT,aAAa;QACb,MAAM;QACN,QAAQ;QACR,SAAS;QACT,KAAK;QACL,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,UAAU,EAAE,GAAG;QACf,MAAM,EAAE,OAAO;KAChB,CAAA;IACD,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACvB,MAAM,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,CAAA;IACrC,CAAC;IAED,IAAI,KAAK,EAAE,CAAC;QACV,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QAC5B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,CAAA;IAC3F,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,CAAA;IACpD,UAAU,CAAC,QAAQ,EAAE,MAA0D,CAAC,CAAA;IAChF,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,CAAA;AACpG,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA;AACjC,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;IACzB,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAErC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,EAAE,CAAC,CAAA;IAC5C,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAA;IACjD,CAAC;IAED,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAA;IACnC,MAAM,UAAU,GAAwB;QACtC,YAAY,EAAE,OAAO,CAAC,WAAW;QACjC,aAAa,EAAE,OAAO,CAAC,YAAY;KACpC,CAAA;IACD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,UAAU,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAA;IACvC,CAAC;IACD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,UAAU,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAA;IAC3C,CAAC;IACD,MAAM,IAAI,GAAkB;QAC1B,cAAc,EAAE,OAAO,EAAE,cAAc,IAAI,IAAI;QAC/C,MAAM,EAAE,UAA6B;QACrC,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACvC,CAAA;IAED,kBAAkB,CAAC,IAAI,CAAC,CAAA;IACxB,aAAa,CAAC,KAAK,EAAE;QACnB,WAAW,EAAE,IAAI,CAAC,YAAY;QAC9B,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,OAAO;KAChB,CAAC,CAAA;AACJ,CAAC"} \ No newline at end of file diff --git a/dist/errors.d.ts b/dist/errors.d.ts deleted file mode 100644 index 223cee1..0000000 --- a/dist/errors.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -export type ErrorCode = 'NO_ELIGIBLE_ACCOUNTS' | 'ACCOUNT_NOT_FOUND' | 'ACCOUNT_DISABLED' | 'TOKEN_REFRESH_FAILED' | 'STORE_LOCKED' | 'STORE_CORRUPTED' | 'STORE_MIGRATION_FAILED' | 'VALIDATION_ERROR' | 'RATE_LIMITED' | 'AUTH_INVALID' | 'MODEL_UNSUPPORTED' | 'WORKSPACE_DEACTIVATED' | 'MAX_RETRIES_EXCEEDED' | 'INVALID_REQUEST' | 'LOCALHOST_ONLY'; -export interface DeterministicError { - code: ErrorCode; - message: string; - details?: Record; -} -export declare function createError(code: ErrorCode, message: string, details?: Record): DeterministicError; -export declare function errorResponse(error: DeterministicError, status?: number): Response; -export declare const Errors: { - noEligibleAccounts: (reason?: string) => DeterministicError; - accountNotFound: (alias: string) => DeterministicError; - accountDisabled: (alias: string) => DeterministicError; - maxRetriesExceeded: (attempts: number, aliasesTried: string[]) => DeterministicError; - storeLocked: (reason?: string) => DeterministicError; - localhostOnly: (host: string) => DeterministicError; -}; -//# sourceMappingURL=errors.d.ts.map \ No newline at end of file diff --git a/dist/errors.d.ts.map b/dist/errors.d.ts.map deleted file mode 100644 index 6f13e42..0000000 --- a/dist/errors.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GACjB,sBAAsB,GACtB,mBAAmB,GACnB,kBAAkB,GAClB,sBAAsB,GACtB,cAAc,GACd,iBAAiB,GACjB,wBAAwB,GACxB,kBAAkB,GAClB,cAAc,GACd,cAAc,GACd,mBAAmB,GACnB,uBAAuB,GACvB,sBAAsB,GACtB,iBAAiB,GACjB,gBAAgB,CAAA;AAEpB,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,SAAS,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClC;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB,CAEnH;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,EAAE,MAAM,GAAE,MAAY,GAAG,QAAQ,CAKvF;AAED,eAAO,MAAM,MAAM;kCACa,MAAM,KAAG,kBAAkB;6BAIhC,MAAM,KAAG,kBAAkB;6BAK3B,MAAM,KAAG,kBAAkB;mCAKrB,MAAM,gBAAgB,MAAM,EAAE,KAAG,kBAAkB;2BAK3D,MAAM,KAAG,kBAAkB;0BAI5B,MAAM,KAAG,kBAAkB;CAKlD,CAAA"} \ No newline at end of file diff --git a/dist/errors.js b/dist/errors.js deleted file mode 100644 index cc2b886..0000000 --- a/dist/errors.js +++ /dev/null @@ -1,37 +0,0 @@ -export function createError(code, message, details) { - return { code, message, details }; -} -export function errorResponse(error, status = 500) { - return new Response(JSON.stringify({ error }), { status, headers: { 'Content-Type': 'application/json' } }); -} -export const Errors = { - noEligibleAccounts: (reason) => ({ - code: 'NO_ELIGIBLE_ACCOUNTS', - message: reason || 'No eligible accounts available for rotation', - }), - accountNotFound: (alias) => ({ - code: 'ACCOUNT_NOT_FOUND', - message: `Account not found: ${alias}`, - details: { alias }, - }), - accountDisabled: (alias) => ({ - code: 'ACCOUNT_DISABLED', - message: `Account is disabled: ${alias}`, - details: { alias }, - }), - maxRetriesExceeded: (attempts, aliasesTried) => ({ - code: 'MAX_RETRIES_EXCEEDED', - message: `Exhausted all ${attempts} retry attempts`, - details: { attempts, aliasesTried }, - }), - storeLocked: (reason) => ({ - code: 'STORE_LOCKED', - message: reason || 'Store is locked and cannot be modified', - }), - localhostOnly: (host) => ({ - code: 'LOCALHOST_ONLY', - message: 'Dashboard can only bind to localhost (127.0.0.1 or ::1)', - details: { attemptedHost: host }, - }), -}; -//# sourceMappingURL=errors.js.map \ No newline at end of file diff --git a/dist/errors.js.map b/dist/errors.js.map deleted file mode 100644 index e8a61cb..0000000 --- a/dist/errors.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAuBA,MAAM,UAAU,WAAW,CAAC,IAAe,EAAE,OAAe,EAAE,OAAiC;IAC7F,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAA;AACnC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAyB,EAAE,SAAiB,GAAG;IAC3E,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,EACzB,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CAC5D,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,kBAAkB,EAAE,CAAC,MAAe,EAAsB,EAAE,CAAC,CAAC;QAC5D,IAAI,EAAE,sBAAsB;QAC5B,OAAO,EAAE,MAAM,IAAI,6CAA6C;KACjE,CAAC;IACF,eAAe,EAAE,CAAC,KAAa,EAAsB,EAAE,CAAC,CAAC;QACvD,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,sBAAsB,KAAK,EAAE;QACtC,OAAO,EAAE,EAAE,KAAK,EAAE;KACnB,CAAC;IACF,eAAe,EAAE,CAAC,KAAa,EAAsB,EAAE,CAAC,CAAC;QACvD,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE,wBAAwB,KAAK,EAAE;QACxC,OAAO,EAAE,EAAE,KAAK,EAAE;KACnB,CAAC;IACF,kBAAkB,EAAE,CAAC,QAAgB,EAAE,YAAsB,EAAsB,EAAE,CAAC,CAAC;QACrF,IAAI,EAAE,sBAAsB;QAC5B,OAAO,EAAE,iBAAiB,QAAQ,iBAAiB;QACnD,OAAO,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE;KACpC,CAAC;IACF,WAAW,EAAE,CAAC,MAAe,EAAsB,EAAE,CAAC,CAAC;QACrD,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,MAAM,IAAI,wCAAwC;KAC5D,CAAC;IACF,aAAa,EAAE,CAAC,IAAY,EAAsB,EAAE,CAAC,CAAC;QACpD,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,yDAAyD;QAClE,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE;KACjC,CAAC;CACH,CAAA"} \ No newline at end of file diff --git a/dist/force-mode.d.ts b/dist/force-mode.d.ts deleted file mode 100644 index 94b70d2..0000000 --- a/dist/force-mode.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -export interface ForceState { - forcedAlias: string | null; - forcedUntil: number | null; - previousRotationStrategy: string | null; - forcedBy: string | null; -} -export declare function getForceState(): ForceState; -export declare function isForceActive(): boolean; -export declare function activateForce(alias: string, actor?: string): { - success: boolean; - error?: string; - state?: ForceState; -}; -export declare function clearForce(): { - success: boolean; - restoredStrategy?: string | null; -}; -export declare function checkAndAutoClearForce(): { - wasCleared: boolean; - reason?: string; -}; -export declare function getRemainingForceTimeMs(): number; -export declare function formatForceDuration(ms: number): string; -//# sourceMappingURL=force-mode.d.ts.map \ No newline at end of file diff --git a/dist/force-mode.d.ts.map b/dist/force-mode.d.ts.map deleted file mode 100644 index fd74deb..0000000 --- a/dist/force-mode.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"force-mode.d.ts","sourceRoot":"","sources":["../src/force-mode.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAcD,wBAAgB,aAAa,IAAI,UAAU,CAQ1C;AAED,wBAAgB,aAAa,IAAI,OAAO,CAwBvC;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAiB,GACvB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,UAAU,CAAA;CAAE,CA+C1D;AAED,wBAAgB,UAAU,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAiCnF;AAED,wBAAgB,sBAAsB,IAAI;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CA6BjF;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAOhD;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAQtD"} \ No newline at end of file diff --git a/dist/force-mode.js b/dist/force-mode.js deleted file mode 100644 index 9d09b4c..0000000 --- a/dist/force-mode.js +++ /dev/null @@ -1,149 +0,0 @@ -import { loadStore, saveStore } from './store.js'; -const FORCE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours -const ROTATION_STRATEGIES = new Set([ - 'round-robin', - 'least-used', - 'random', - 'weighted-round-robin' -]); -function isRotationStrategy(value) { - return typeof value === 'string' && ROTATION_STRATEGIES.has(value); -} -export function getForceState() { - const store = loadStore(); - return { - forcedAlias: store.forcedAlias ?? null, - forcedUntil: store.forcedUntil ?? null, - previousRotationStrategy: store.previousRotationStrategy ?? null, - forcedBy: store.forcedBy ?? null - }; -} -export function isForceActive() { - const state = getForceState(); - if (!state.forcedAlias || !state.forcedUntil) { - return false; - } - const now = Date.now(); - if (now > state.forcedUntil) { - return false; - } - // Check if forced alias still exists and is eligible - const store = loadStore(); - const forcedAccount = store.accounts[state.forcedAlias]; - if (!forcedAccount) { - return false; - } - // Check if account is disabled - if (forcedAccount.enabled === false) { - return false; - } - return true; -} -export function activateForce(alias, actor = 'system') { - const store = loadStore(); - // Validate alias exists - if (!store.accounts[alias]) { - return { success: false, error: `Account '${alias}' not found` }; - } - // Validate alias is enabled - if (store.accounts[alias].enabled === false) { - return { success: false, error: `Account '${alias}' is disabled` }; - } - const now = Date.now(); - const keepExistingTtl = store.forcedAlias === alias && - typeof store.forcedUntil === 'number' && - store.forcedUntil > now; - const forcedUntil = keepExistingTtl ? store.forcedUntil : now + FORCE_TTL_MS; - const currentStrategy = store.settings?.rotationStrategy || - store.rotationStrategy || - 'round-robin'; - // Store previous rotation strategy if not already forcing - const previousStrategy = (store.forcedAlias ? store.previousRotationStrategy : currentStrategy) ?? null; - const newStore = { - ...store, - forcedAlias: alias, - forcedUntil, - previousRotationStrategy: previousStrategy, - forcedBy: actor - }; - saveStore(newStore); - return { - success: true, - state: { - forcedAlias: alias, - forcedUntil, - previousRotationStrategy: previousStrategy, - forcedBy: actor - } - }; -} -export function clearForce() { - const store = loadStore(); - const restoredStrategy = store.previousRotationStrategy; - const currentStrategy = store.settings?.rotationStrategy || - store.rotationStrategy || - 'round-robin'; - const nextStrategy = isRotationStrategy(restoredStrategy) - ? restoredStrategy - : currentStrategy; - const newStore = { - ...store, - forcedAlias: null, - forcedUntil: null, - rotationStrategy: nextStrategy, - previousRotationStrategy: null, - forcedBy: null - }; - if (store.settings) { - newStore.settings = { - ...store.settings, - rotationStrategy: nextStrategy - }; - } - saveStore(newStore); - return { - success: true, - restoredStrategy - }; -} -export function checkAndAutoClearForce() { - const state = getForceState(); - if (!state.forcedAlias) { - return { wasCleared: false }; - } - const store = loadStore(); - const now = Date.now(); - // Check expiry - if (state.forcedUntil && now > state.forcedUntil) { - clearForce(); - return { wasCleared: true, reason: 'expired' }; - } - // Check if alias still exists - if (!store.accounts[state.forcedAlias]) { - clearForce(); - return { wasCleared: true, reason: 'account_removed' }; - } - // Check if alias is disabled - if (store.accounts[state.forcedAlias].enabled === false) { - clearForce(); - return { wasCleared: true, reason: 'account_disabled' }; - } - return { wasCleared: false }; -} -export function getRemainingForceTimeMs() { - const state = getForceState(); - if (!state.forcedUntil) { - return 0; - } - const remaining = state.forcedUntil - Date.now(); - return Math.max(0, remaining); -} -export function formatForceDuration(ms) { - const hours = Math.floor(ms / (60 * 60 * 1000)); - const minutes = Math.floor((ms % (60 * 60 * 1000)) / (60 * 1000)); - if (hours > 0) { - return `${hours}h ${minutes}m`; - } - return `${minutes}m`; -} -//# sourceMappingURL=force-mode.js.map \ No newline at end of file diff --git a/dist/force-mode.js.map b/dist/force-mode.js.map deleted file mode 100644 index 323af91..0000000 --- a/dist/force-mode.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"force-mode.js","sourceRoot":"","sources":["../src/force-mode.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts deleted file mode 100644 index 04e846a..0000000 --- a/dist/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { Plugin } from '@opencode-ai/plugin'; -export declare function isCyberPolicyError(payload: any, fallbackText?: string): boolean; -/** - * Multi-account OAuth plugin for OpenCode - * - * Rotates between multiple ChatGPT Plus/Pro accounts for rate limit resilience. - */ -declare const MultiAuthPlugin: Plugin; -export default MultiAuthPlugin; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/index.d.ts.map b/dist/index.d.ts.map deleted file mode 100644 index 7e1aaaa..0000000 --- a/dist/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAA;AA2M9D,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,GAAG,EAAE,YAAY,GAAE,MAAW,GAAG,OAAO,CAKnF;AAqED;;;;GAIG;AACH,QAAA,MAAM,eAAe,EAAE,MAyuBtB,CAAA;AAED,eAAe,eAAe,CAAA"} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js deleted file mode 100644 index b6efe84..0000000 --- a/dist/index.js +++ /dev/null @@ -1,848 +0,0 @@ -import fs from 'node:fs'; -import { syncAuthFromOpenCode } from './auth-sync.js'; -import { createAuthorizationFlow, loginAccount } from './auth.js'; -import { extractRateLimitUpdate, getBlockingRateLimitResetAt, mergeRateLimits, parseRateLimitResetFromError, parseRetryAfterHeader } from './rate-limits.js'; -import { getNextAccount, markAuthInvalid, markModelUnsupported, markRateLimited, markWorkspaceDeactivated } from './rotation.js'; -import { getDefaultModels } from './models.js'; -import { getForceState, isForceActive } from './force-mode.js'; -import { getRuntimeSettings } from './settings.js'; -import { listAccounts, updateAccount, loadStore } from './store.js'; -import { DEFAULT_CONFIG } from './types.js'; -import { Errors } from './errors.js'; -const PROVIDER_ID = 'openai'; -const CODEX_BASE_URL = 'https://chatgpt.com/backend-api'; -const REDIRECT_PORT = 1455; -const REDIRECT_URI = `http://localhost:${REDIRECT_PORT}/auth/callback`; -const URL_PATHS = { - RESPONSES: '/responses', - CODEX_RESPONSES: '/codex/responses' -}; -const OPENAI_HEADERS = { - BETA: 'OpenAI-Beta', - ACCOUNT_ID: 'chatgpt-account-id', - ORIGINATOR: 'originator', - SESSION_ID: 'session_id', - CONVERSATION_ID: 'conversation_id' -}; -const OPENAI_HEADER_VALUES = { - BETA_RESPONSES: 'responses=experimental', - ORIGINATOR_CODEX: 'codex_cli_rs' -}; -const JWT_CLAIM_PATH = 'https://api.openai.com/auth'; -const DEFAULT_LATEST_CODEX_MODEL = 'gpt-5.5'; -let pluginConfig = { ...DEFAULT_CONFIG }; -function configure(config) { - pluginConfig = { ...pluginConfig, ...config }; -} -function decodeJWT(token) { - try { - const parts = token.split('.'); - if (parts.length !== 3) - return null; - const payload = parts[1]; - const decoded = Buffer.from(payload, 'base64').toString('utf-8'); - return JSON.parse(decoded); - } - catch { - return null; - } -} -function extractRequestUrl(input) { - if (typeof input === 'string') - return input; - if (input instanceof URL) - return input.toString(); - return input.url; -} -function rewriteUrlForCodex(url) { - return url.replace(URL_PATHS.RESPONSES, URL_PATHS.CODEX_RESPONSES); -} -function extractPathAndSearch(url) { - // OpenCode sometimes passes relative paths (e.g. "/chat/completions") or even - // malformed strings when provider base_url is missing (e.g. "undefined/..."). - // We only need the path+query and then we force the ChatGPT backend base URL. - try { - const u = new URL(url); - return `${u.pathname}${u.search}`; - } - catch { - // best-effort fallback - } - const trimmed = String(url || '').trim(); - if (trimmed.startsWith('/')) - return trimmed; - const firstSlash = trimmed.indexOf('/'); - if (firstSlash >= 0) - return trimmed.slice(firstSlash); - return trimmed; -} -function toCodexBackendUrl(originalUrl) { - const pathAndSearch = extractPathAndSearch(originalUrl); - // Map OpenAI v1 endpoints to ChatGPT Codex endpoints. - let mapped = pathAndSearch; - if (mapped.includes(URL_PATHS.RESPONSES)) { - mapped = mapped.replace(URL_PATHS.RESPONSES, URL_PATHS.CODEX_RESPONSES); - } - else if (mapped.includes('/chat/completions')) { - mapped = mapped.replace('/chat/completions', '/codex/chat/completions'); - } - return new URL(mapped, CODEX_BASE_URL).toString(); -} -function filterInput(input) { - if (!Array.isArray(input)) - return input; - return input - .filter((item) => item?.type !== 'item_reference') - .map((item) => { - if (item && typeof item === 'object' && 'id' in item) { - const { id, ...rest } = item; - return rest; - } - return item; - }); -} -function normalizeModel(model) { - if (!model) - return 'gpt-5.1'; - const modelId = model.includes('/') ? model.split('/').pop() : model; - const baseModel = modelId.replace(/-(?:fast|none|minimal|low|medium|high|xhigh)$/, ''); - // OpenCode may lag behind the ChatGPT Codex model allowlist. Route known older - // Codex selections to the latest backend model when users opt in. - const preferLatestRaw = process.env.OPENCODE_MULTI_AUTH_PREFER_CODEX_LATEST; - const preferLatest = preferLatestRaw === '1' || preferLatestRaw === 'true'; - if (preferLatest && - (baseModel === 'gpt-5.4' || - baseModel === 'gpt-5.3-codex' || - baseModel === 'gpt-5.2-codex' || - baseModel === 'gpt-5-codex')) { - const latestModel = (process.env.OPENCODE_MULTI_AUTH_CODEX_LATEST_MODEL || DEFAULT_LATEST_CODEX_MODEL).trim(); - if (process.env.OPENCODE_MULTI_AUTH_DEBUG === '1') { - console.log(`[multi-auth] model map: ${baseModel} -> ${latestModel}`); - } - return latestModel; - } - return baseModel; -} -function isSparkModel(model) { - return typeof model === 'string' && model.startsWith('gpt-5.3-codex-spark'); -} -function supportsFastMode(model) { - return model === 'gpt-5.5' || model === 'gpt-5.4'; -} -function ensureContentType(headers) { - const responseHeaders = new Headers(headers); - if (!responseHeaders.has('content-type')) { - responseHeaders.set('content-type', 'text/event-stream; charset=utf-8'); - } - return responseHeaders; -} -function extractErrorMessage(payload, fallbackText = '') { - if (!payload || typeof payload !== 'object') { - return fallbackText; - } - const detailMessage = typeof payload?.detail?.message === 'string' - ? payload.detail.message - : typeof payload?.detail === 'string' - ? payload.detail - : ''; - const errorMessage = typeof payload?.error?.message === 'string' - ? payload.error.message - : ''; - const topLevelMessage = typeof payload?.message === 'string' - ? payload.message - : ''; - return detailMessage || errorMessage || topLevelMessage || fallbackText; -} -function extractErrorCode(payload) { - if (!payload || typeof payload !== 'object') - return ''; - return ((typeof payload?.detail?.code === 'string' && payload.detail.code) || - (typeof payload?.error?.code === 'string' && payload.error.code) || - (typeof payload?.code === 'string' && payload.code) || - ''); -} -export function isCyberPolicyError(payload, fallbackText = '') { - const code = extractErrorCode(payload).toLowerCase(); - const text = `${extractErrorMessage(payload, fallbackText)} ${fallbackText}`.toLowerCase(); - return code === 'cyber_policy' || text.includes('cyber_policy'); -} -function resolveRateLimitedUntil(rateLimits, headers, errorText, fallbackCooldownMs, now = Date.now()) { - const retryAfterUntil = parseRetryAfterHeader(headers.get('retry-after'), now) || 0; - const windowResetUntil = getBlockingRateLimitResetAt(rateLimits, now, { - conservativeWhenRemainingUnknown: true - }) || 0; - const messageResetUntil = parseRateLimitResetFromError(errorText, now) || 0; - const fallbackUntil = now + fallbackCooldownMs; - return Math.max(fallbackUntil, retryAfterUntil, windowResetUntil, messageResetUntil); -} -function parseSseStream(sseText) { - const lines = sseText.split('\n'); - for (const line of lines) { - if (!line.startsWith('data: ')) - continue; - try { - const data = JSON.parse(line.substring(6)); - if (data?.type === 'response.done' || data?.type === 'response.completed') { - return data.response; - } - } - catch { - // ignore malformed chunks - } - } - return null; -} -async function convertSseToJson(response, headers) { - if (!response.body) { - throw new Error('[multi-auth] Response has no body'); - } - const reader = response.body.getReader(); - const decoder = new TextDecoder(); - let fullText = ''; - while (true) { - const { done, value } = await reader.read(); - if (done) - break; - fullText += decoder.decode(value, { stream: true }); - } - const finalResponse = parseSseStream(fullText); - if (!finalResponse) { - return new Response(fullText, { - status: response.status, - statusText: response.statusText, - headers - }); - } - const jsonHeaders = new Headers(headers); - jsonHeaders.set('content-type', 'application/json; charset=utf-8'); - return new Response(JSON.stringify(finalResponse), { - status: response.status, - statusText: response.statusText, - headers: jsonHeaders - }); -} -/** - * Multi-account OAuth plugin for OpenCode - * - * Rotates between multiple ChatGPT Plus/Pro accounts for rate limit resilience. - */ -const MultiAuthPlugin = async ({ client, $, serverUrl, project, directory }) => { - const terminalNotifierPath = (() => { - const candidates = [ - '/opt/homebrew/bin/terminal-notifier', - '/usr/local/bin/terminal-notifier' - ]; - for (const c of candidates) { - try { - if (fs.existsSync(c)) - return c; - } - catch { - // ignore - } - } - return null; - })(); - const notifyEnabledRaw = process.env.OPENCODE_MULTI_AUTH_NOTIFY; - const notifyEnabled = notifyEnabledRaw === '1' || notifyEnabledRaw === 'true'; - const notifySound = (process.env.OPENCODE_MULTI_AUTH_NOTIFY_SOUND || '/System/Library/Sounds/Glass.aiff').trim(); - const lastStatusBySession = new Map(); - const lastNotifiedAtByKey = new Map(); - const lastRetryAttemptBySession = new Map(); - const escapeAppleScriptString = (value) => { - return String(value) - .replaceAll('\\', '\\\\') - .replaceAll('"', '\"') - .replaceAll(String.fromCharCode(10), '\n'); - }; - let didWarnTerminalNotifier = false; - const notifyMac = (title, message, clickUrl) => { - if (!notifyEnabled) - return; - if (process.platform !== 'darwin') - return; - const macOpenRaw = process.env.OPENCODE_MULTI_AUTH_NOTIFY_MAC_OPEN; - const macOpenEnabled = macOpenRaw !== '0' && macOpenRaw !== 'false'; - // Best effort: clickable notifications require terminal-notifier. - if (macOpenEnabled && clickUrl && terminalNotifierPath) { - try { - $ `${terminalNotifierPath} -title ${title} -message ${message} -open ${clickUrl}` - .nothrow() - .catch(() => { }); - } - catch { - // ignore - } - } - else { - if (macOpenEnabled && clickUrl && !terminalNotifierPath && !didWarnTerminalNotifier) { - didWarnTerminalNotifier = true; - if (process.env.OPENCODE_MULTI_AUTH_DEBUG === '1') { - console.log('[multi-auth] mac click-to-open requires terminal-notifier (brew install terminal-notifier)'); - } - } - try { - const osascript = '/usr/bin/osascript'; - const safeTitle = escapeAppleScriptString(title); - const safeMessage = escapeAppleScriptString(message); - const script = `display notification "${safeMessage}" with title "${safeTitle}"`; - // Fire-and-forget: never block OpenCode event processing. - $ `${osascript} -e ${script}`.nothrow().catch(() => { }); - } - catch { - // ignore - } - } - if (!notifySound) - return; - try { - const afplay = '/usr/bin/afplay'; - $ `${afplay} ${notifySound}`.nothrow().catch(() => { }); - } - catch { - // ignore - } - }; - const ntfyUrl = (process.env.OPENCODE_MULTI_AUTH_NOTIFY_NTFY_URL || '').trim(); - const ntfyToken = (process.env.OPENCODE_MULTI_AUTH_NOTIFY_NTFY_TOKEN || '').trim(); - const notifyUiBaseUrl = (process.env.OPENCODE_MULTI_AUTH_NOTIFY_UI_BASE_URL || '').trim(); - const getSessionUrl = (sessionID) => { - const base = (notifyUiBaseUrl || serverUrl?.origin || '').replace(/\/$/, ''); - if (!base) - return ''; - return `${base}/session/${sessionID}`; - }; - const projectLabel = (project?.name || project?.id || '').trim() || 'OpenCode'; - const sessionMetaCache = new Map(); - const formatTitle = (kind) => { - if (kind === 'error') - return `OpenCode - ${projectLabel} - Error`; - if (kind === 'retry') - return `OpenCode - ${projectLabel} - Retrying`; - return `OpenCode - ${projectLabel}`; - }; - const formatBody = (kind, sessionID, detail) => { - const meta = sessionMetaCache.get(sessionID) || {}; - const titleLine = meta.title ? `Task: ${meta.title}` : ''; - const url = getSessionUrl(sessionID); - if (kind === 'idle') { - return [titleLine, `Session finished: ${sessionID}`, detail || '', url].filter(Boolean).join('\n'); - } - if (kind === 'retry') { - return [titleLine, `Retrying: ${sessionID}`, detail || '', url].filter(Boolean).join('\n'); - } - return [titleLine, `Error: ${sessionID}`, detail || '', url].filter(Boolean).join('\n'); - }; - const notifyMacRich = (kind, sessionID, detail) => { - const body = formatBody(kind, sessionID, detail); - notifyMac(formatTitle(kind), body, getSessionUrl(sessionID) || undefined); - }; - const notifyNtfyRich = async (kind, sessionID, detail) => { - if (!notifyEnabled) - return; - if (!ntfyUrl) - return; - const sessionUrl = getSessionUrl(sessionID); - const title = formatTitle(kind); - const body = formatBody(kind, sessionID, detail); - // ntfy priority: 1=min, 3=default, 5=max - const priority = kind === 'error' ? '5' : kind === 'retry' ? '4' : '3'; - const headers = { - 'Content-Type': 'text/plain; charset=utf-8', - 'Title': title, - 'Priority': priority - }; - if (sessionUrl) - headers['Click'] = sessionUrl; - if (ntfyToken) - headers['Authorization'] = `Bearer ${ntfyToken}`; - try { - await fetch(ntfyUrl, { method: 'POST', headers, body }); - } - catch { - // ignore - } - }; - const shouldThrottle = (key, minMs) => { - const last = lastNotifiedAtByKey.get(key) || 0; - const now = Date.now(); - if (now - last < minMs) - return true; - lastNotifiedAtByKey.set(key, now); - return false; - }; - const formatRetryDetail = (status) => { - const attempt = typeof status?.attempt === 'number' ? status.attempt : undefined; - const message = typeof status?.message === 'string' ? status.message : ''; - const next = typeof status?.next === 'number' ? status.next : undefined; - const parts = []; - if (typeof attempt === 'number') - parts.push(`Attempt: ${attempt}`); - // OpenCode has emitted both "seconds-until-next" and "epoch ms" variants over time. - if (typeof next === 'number') { - const seconds = next > 1e12 ? Math.max(0, Math.round((next - Date.now()) / 1000)) : Math.max(0, Math.round(next)); - parts.push(`Next in: ${seconds}s`); - } - if (message) - parts.push(message); - return parts.join(' | '); - }; - const formatErrorDetail = (err) => { - if (!err || typeof err !== 'object') - return ''; - const name = typeof err.name === 'string' ? err.name : ''; - const code = typeof err.code === 'string' ? err.code : ''; - const message = (typeof err.message === 'string' && err.message) || - (typeof err.error?.message === 'string' && err.error.message) || - ''; - return [name, code, message].filter(Boolean).join(': '); - }; - const notifyRich = async (kind, sessionID, detail) => { - try { - notifyMacRich(kind, sessionID, detail); - } - catch { - // ignore - } - try { - await notifyNtfyRich(kind, sessionID, detail); - } - catch { - // ignore - } - }; - return { - event: async ({ event }) => { - if (!notifyEnabled) - return; - if (!event || !('type' in event)) - return; - if (event.type === 'session.created' || event.type === 'session.updated') { - const info = event.properties?.info; - const id = info?.id; - if (id) { - sessionMetaCache.set(id, { title: info?.title }); - } - return; - } - if (event.type === 'session.status') { - const sessionID = event.properties?.sessionID; - const status = event.properties?.status; - const statusType = status?.type; - if (!sessionID || !statusType) - return; - lastStatusBySession.set(sessionID, statusType); - if (statusType === 'retry') { - const attempt = typeof status?.attempt === 'number' ? status.attempt : undefined; - const prevAttempt = lastRetryAttemptBySession.get(sessionID); - if (typeof attempt === 'number') { - if (prevAttempt === attempt && shouldThrottle(`retry:${sessionID}:${attempt}`, 5000)) { - return; - } - lastRetryAttemptBySession.set(sessionID, attempt); - } - const key = `retry:${sessionID}:${typeof attempt === 'number' ? attempt : 'na'}`; - if (shouldThrottle(key, 2000)) - return; - void notifyRich('retry', sessionID, formatRetryDetail(status)); - } - return; - } - if (event.type === 'session.error') { - const sessionID = event.properties?.sessionID; - const id = sessionID || 'unknown'; - const err = event.properties?.error; - const detail = formatErrorDetail(err); - const key = `error:${id}:${detail}`; - if (shouldThrottle(key, 2000)) - return; - void notifyRich('error', id, detail); - return; - } - if (event.type === 'session.idle') { - const sessionID = event.properties?.sessionID; - if (!sessionID) - return; - const prev = lastStatusBySession.get(sessionID); - if (prev === 'busy' || prev === 'retry') { - if (shouldThrottle(`idle:${sessionID}`, 2000)) - return; - void notifyRich('idle', sessionID); - } - lastStatusBySession.set(sessionID, 'idle'); - } - }, - config: async (config) => { - const injectModelsRaw = process.env.OPENCODE_MULTI_AUTH_INJECT_MODELS; - const injectModels = injectModelsRaw !== '0' && injectModelsRaw !== 'false'; - if (!injectModels) - return; - const latestModel = (process.env.OPENCODE_MULTI_AUTH_CODEX_LATEST_MODEL || DEFAULT_LATEST_CODEX_MODEL).trim(); - try { - const openai = config.provider?.[PROVIDER_ID] || null; - if (!openai || typeof openai !== 'object') - return; - openai.models ||= {}; - openai.whitelist ||= []; - const defaultModels = getDefaultModels(); - const injectedModelIds = [latestModel]; - if (supportsFastMode(latestModel) && defaultModels[`${latestModel}-fast`]) { - injectedModelIds.push(`${latestModel}-fast`); - } - for (const sparkVariant of [ - 'gpt-5.3-codex-spark-low', - 'gpt-5.3-codex-spark-medium', - 'gpt-5.3-codex-spark-high', - 'gpt-5.3-codex-spark-xhigh' - ]) { - if (defaultModels[sparkVariant]) { - injectedModelIds.push(sparkVariant); - } - } - for (const modelID of injectedModelIds) { - const model = defaultModels[modelID]; - if (!model || openai.models[modelID]) - continue; - openai.models[modelID] = model; - } - for (const modelID of injectedModelIds) { - if (!openai.whitelist.includes(modelID)) { - openai.whitelist.unshift(modelID); - } - } - if (process.env.OPENCODE_MULTI_AUTH_DEBUG === '1') { - console.log(`[multi-auth] injected runtime models: ${injectedModelIds.join(', ')}`); - } - } - catch (err) { - if (process.env.OPENCODE_MULTI_AUTH_DEBUG === '1') { - console.log('[multi-auth] config injection failed:', err); - } - } - }, - auth: { - provider: PROVIDER_ID, - /** - * Loader configures the SDK with multi-account rotation - */ - async loader(getAuth, provider) { - await syncAuthFromOpenCode(getAuth); - const accounts = listAccounts(); - if (accounts.length === 0) { - console.log('[multi-auth] No accounts configured. Run: opencode-multi-auth add '); - return {}; - } - const customFetch = async (input, init) => { - await syncAuthFromOpenCode(getAuth); - let body = {}; - try { - body = init?.body ? JSON.parse(init.body) : {}; - } - catch { - body = {}; - } - const normalizedModel = normalizeModel(body.model); - const store = loadStore(); - const forceState = getForceState(); - const forcePinned = isForceActive() && !!forceState.forcedAlias; - const eligibleCount = Object.values(store.accounts).filter(acc => { - const now = Date.now(); - return (!acc.rateLimitedUntil || acc.rateLimitedUntil < now) && - (!acc.modelUnsupportedUntil || acc.modelUnsupportedUntil < now) && - (!acc.workspaceDeactivatedUntil || acc.workspaceDeactivatedUntil < now) && - !acc.authInvalid && - acc.enabled !== false; - }).length; - const maxAttempts = forcePinned ? 1 : Math.max(1, eligibleCount); - const triedAliases = new Set(); - let attempt = 0; - while (attempt < maxAttempts) { - attempt++; - const settings = getRuntimeSettings(); - const effectiveConfig = { - ...pluginConfig, - rotationStrategy: settings.settings.rotationStrategy - }; - const rotation = await getNextAccount(effectiveConfig, { - model: normalizedModel - }); - if (!rotation) { - if (forcePinned && forceState.forcedAlias) { - const forced = loadStore().accounts[forceState.forcedAlias]; - const now = Date.now(); - if (forced?.rateLimitedUntil && forced.rateLimitedUntil > now) { - return new Response(JSON.stringify({ - error: { - code: 'RATE_LIMITED', - message: `Forced account '${forced.alias}' is rate-limited until ${new Date(forced.rateLimitedUntil).toISOString()}`, - details: { alias: forced.alias, rateLimitedUntil: forced.rateLimitedUntil } - } - }), { status: 429, headers: { 'Content-Type': 'application/json' } }); - } - } - return new Response(JSON.stringify({ - error: Errors.noEligibleAccounts('No available accounts after filtering') - }), { status: 503, headers: { 'Content-Type': 'application/json' } }); - } - const { account, token } = rotation; - if (triedAliases.has(account.alias)) { - continue; - } - triedAliases.add(account.alias); - const decoded = decodeJWT(token); - const accountId = decoded?.[JWT_CLAIM_PATH]?.chatgpt_account_id; - if (!accountId) { - return new Response(JSON.stringify({ - error: { - code: 'TOKEN_PARSE_ERROR', - message: '[multi-auth] Failed to extract accountId from token' - } - }), { status: 401, headers: { 'Content-Type': 'application/json' } }); - } - const originalUrl = extractRequestUrl(input); - const url = toCodexBackendUrl(originalUrl); - const isStreaming = body?.stream === true; - const fastMode = /-fast$/.test(body.model || ''); - const supportedFastMode = fastMode && supportsFastMode(normalizedModel); - const reasoningMatch = body.model?.match(/-(none|low|medium|high|xhigh)$/); - const payload = { - ...body, - model: normalizedModel, - store: false - }; - if (payload.truncation === undefined) { - const truncationRaw = (process.env.OPENCODE_MULTI_AUTH_TRUNCATION || '').trim(); - if (truncationRaw && truncationRaw !== 'disabled' && truncationRaw !== 'false' && truncationRaw !== '0') { - payload.truncation = truncationRaw; - } - } - if (payload.input) { - payload.input = filterInput(payload.input); - } - if (reasoningMatch?.[1]) { - payload.reasoning = { - ...(payload.reasoning || {}), - effort: reasoningMatch[1] - }; - if (!isSparkModel(normalizedModel)) { - payload.reasoning.summary = payload.reasoning?.summary || 'auto'; - } - } - if (isSparkModel(normalizedModel) && payload.reasoning?.summary !== undefined) { - delete payload.reasoning.summary; - } - if (supportedFastMode) { - payload.service_tier = payload.service_tier || 'priority'; - if (process.env.OPENCODE_MULTI_AUTH_DEBUG === '1') { - console.log(`[multi-auth] fast mode enabled: ${normalizedModel} + service_tier=priority`); - } - } - else if (fastMode && process.env.OPENCODE_MULTI_AUTH_DEBUG === '1') { - console.log(`[multi-auth] fast mode ignored for unsupported model: ${normalizedModel}`); - } - if (process.env.OPENCODE_MULTI_AUTH_DEBUG === '1' && payload.service_tier === 'priority') { - console.log(`[multi-auth] priority service tier requested for ${normalizedModel}`); - } - delete payload.reasoning_effort; - try { - const headers = new Headers(init?.headers || {}); - headers.delete('x-api-key'); - headers.set('Content-Type', 'application/json'); - headers.set('Authorization', `Bearer ${token}`); - headers.set(OPENAI_HEADERS.ACCOUNT_ID, accountId); - headers.set(OPENAI_HEADERS.BETA, OPENAI_HEADER_VALUES.BETA_RESPONSES); - headers.set(OPENAI_HEADERS.ORIGINATOR, OPENAI_HEADER_VALUES.ORIGINATOR_CODEX); - const cacheKey = payload?.prompt_cache_key; - if (cacheKey) { - headers.set(OPENAI_HEADERS.CONVERSATION_ID, cacheKey); - headers.set(OPENAI_HEADERS.SESSION_ID, cacheKey); - } - else { - headers.delete(OPENAI_HEADERS.CONVERSATION_ID); - headers.delete(OPENAI_HEADERS.SESSION_ID); - } - headers.set('accept', 'text/event-stream'); - const sendPayload = async (requestPayload) => { - return fetch(url, { - method: init?.method || 'POST', - headers, - body: JSON.stringify(requestPayload) - }); - }; - const applyLimitUpdate = (response) => { - const limitUpdate = extractRateLimitUpdate(response.headers); - const mergedRateLimits = limitUpdate - ? mergeRateLimits(account.rateLimits, limitUpdate) - : account.rateLimits; - if (limitUpdate) { - const blockingResetAt = getBlockingRateLimitResetAt(mergedRateLimits); - updateAccount(account.alias, { - rateLimits: mergedRateLimits, - rateLimitedUntil: blockingResetAt - }); - } - return mergedRateLimits; - }; - let res = await sendPayload(payload); - let mergedRateLimits = applyLimitUpdate(res); - if (res.status === 400 && payload.service_tier === 'priority') { - const errorData = await res.clone().json().catch(() => ({})); - const errorText = await res.clone().text().catch(() => ''); - if (isCyberPolicyError(errorData, errorText)) { - if (process.env.OPENCODE_MULTI_AUTH_DEBUG === '1') { - console.log('[multi-auth] cyber_policy on priority tier; retrying once without service_tier'); - } - const standardTierPayload = { ...payload }; - delete standardTierPayload.service_tier; - res = await sendPayload(standardTierPayload); - mergedRateLimits = applyLimitUpdate(res); - } - } - if (res.status === 401 || res.status === 403) { - const errorData = await res.clone().json().catch(() => ({})); - const message = errorData?.error?.message || ''; - if (message.toLowerCase().includes('invalidated') || res.status === 401) { - markAuthInvalid(account.alias); - } - if (attempt < maxAttempts) { - continue; - } - return new Response(JSON.stringify({ - error: Errors.maxRetriesExceeded(attempt, Array.from(triedAliases)) - }), { status: res.status, headers: { 'Content-Type': 'application/json' } }); - } - if (res.status === 429) { - const errorData = await res.clone().json().catch(() => ({})); - const errorText = extractErrorMessage(errorData); - const rateLimitedUntil = resolveRateLimitedUntil(mergedRateLimits, res.headers, errorText, pluginConfig.rateLimitCooldownMs); - markRateLimited(account.alias, rateLimitedUntil); - if (attempt < maxAttempts) { - continue; - } - return new Response(JSON.stringify({ - error: Errors.maxRetriesExceeded(attempt, Array.from(triedAliases)) - }), { status: 429, headers: { 'Content-Type': 'application/json' } }); - } - if (res.status === 402) { - const errorData = await res.clone().json().catch(() => null); - const errorText = await res.clone().text().catch(() => ''); - const code = (typeof errorData?.detail?.code === 'string' && errorData.detail.code) || - (typeof errorData?.error?.code === 'string' && errorData.error.code) || - ''; - const message = (typeof errorData?.detail?.message === 'string' && errorData.detail.message) || - (typeof errorData?.detail === 'string' && errorData.detail) || - (typeof errorData?.error?.message === 'string' && errorData.error.message) || - (typeof errorData?.message === 'string' && errorData.message) || - errorText || - ''; - const isDeactivatedWorkspace = code === 'deactivated_workspace' || - message.toLowerCase().includes('deactivated_workspace') || - message.toLowerCase().includes('deactivated workspace'); - if (isDeactivatedWorkspace) { - markWorkspaceDeactivated(account.alias, pluginConfig.workspaceDeactivatedCooldownMs, { - error: message || code - }); - if (attempt < maxAttempts) { - continue; - } - return new Response(JSON.stringify({ - error: Errors.maxRetriesExceeded(attempt, Array.from(triedAliases)) - }), { status: 402, headers: { 'Content-Type': 'application/json' } }); - } - } - if (res.status === 400) { - const errorData = await res.clone().json().catch(() => ({})); - const message = (typeof errorData?.detail === 'string' && errorData.detail) || - (typeof errorData?.error?.message === 'string' && errorData.error.message) || - (typeof errorData?.message === 'string' && errorData.message) || - ''; - const isModelUnsupported = typeof message === 'string' && - message.toLowerCase().includes('model is not supported') && - message.toLowerCase().includes('chatgpt account'); - if (isModelUnsupported) { - markModelUnsupported(account.alias, pluginConfig.modelUnsupportedCooldownMs, { - model: normalizedModel, - error: message - }); - if (attempt < maxAttempts) { - continue; - } - return new Response(JSON.stringify({ - error: Errors.maxRetriesExceeded(attempt, Array.from(triedAliases)) - }), { status: 400, headers: { 'Content-Type': 'application/json' } }); - } - } - if (!res.ok) { - return res; - } - const responseHeaders = ensureContentType(res.headers); - if (!isStreaming && responseHeaders.get('content-type')?.includes('text/event-stream')) { - return await convertSseToJson(res, responseHeaders); - } - return res; - } - catch (err) { - return new Response(JSON.stringify({ error: { code: 'REQUEST_FAILED', message: `[multi-auth] Request failed: ${err}` } }), { status: 500, headers: { 'Content-Type': 'application/json' } }); - } - } - return new Response(JSON.stringify({ - error: Errors.maxRetriesExceeded(attempt, Array.from(triedAliases)) - }), { status: 503, headers: { 'Content-Type': 'application/json' } }); - }; - // Return SDK configuration with custom fetch for rotation - return { - apiKey: 'chatgpt-oauth', - baseURL: CODEX_BASE_URL, - fetch: customFetch - }; - }, - methods: [ - { - label: 'ChatGPT OAuth (Multi-Account)', - type: 'oauth', - prompts: [ - { - type: 'text', - key: 'alias', - message: 'Account alias (e.g., personal, work)', - placeholder: 'personal' - } - ], - /** - * OAuth flow - opens browser for ChatGPT login - */ - authorize: async (inputs) => { - const alias = inputs?.alias || `account-${Date.now()}`; - const flow = await createAuthorizationFlow(); - return { - url: flow.url, - method: 'auto', - instructions: `Login with your ChatGPT Plus/Pro account for "${alias}"`, - callback: async () => { - try { - const account = await loginAccount(alias, flow); - return { - type: 'success', - provider: PROVIDER_ID, - refresh: account.refreshToken, - access: account.accessToken, - expires: account.expiresAt - }; - } - catch { - return { type: 'failed' }; - } - } - }; - } - }, - { - label: 'Skip (use existing accounts)', - type: 'api' - } - ] - } - }; -}; -export default MultiAuthPlugin; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map deleted file mode 100644 index 444856b..0000000 --- a/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EACL,sBAAsB,EACtB,2BAA2B,EAC3B,eAAe,EACf,4BAA4B,EAC5B,qBAAqB,EACtB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,wBAAwB,EACzB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACnE,OAAO,EAAE,cAAc,EAA6C,MAAM,YAAY,CAAA;AACtF,OAAO,EAAE,MAAM,EAA2B,MAAM,aAAa,CAAA;AAE7D,MAAM,WAAW,GAAG,QAAQ,CAAA;AAC5B,MAAM,cAAc,GAAG,iCAAiC,CAAA;AACxD,MAAM,aAAa,GAAG,IAAI,CAAA;AAC1B,MAAM,YAAY,GAAG,oBAAoB,aAAa,gBAAgB,CAAA;AACtE,MAAM,SAAS,GAAG;IAChB,SAAS,EAAE,YAAY;IACvB,eAAe,EAAE,kBAAkB;CACpC,CAAA;AACD,MAAM,cAAc,GAAG;IACrB,IAAI,EAAE,aAAa;IACnB,UAAU,EAAE,oBAAoB;IAChC,UAAU,EAAE,YAAY;IACxB,UAAU,EAAE,YAAY;IACxB,eAAe,EAAE,iBAAiB;CACnC,CAAA;AACD,MAAM,oBAAoB,GAAG;IAC3B,cAAc,EAAE,wBAAwB;IACxC,gBAAgB,EAAE,cAAc;CACjC,CAAA;AACD,MAAM,cAAc,GAAG,6BAA6B,CAAA;AACpD,MAAM,0BAA0B,GAAG,SAAS,CAAA;AAE5C,IAAI,YAAY,GAAiB,EAAE,GAAG,cAAc,EAAE,CAAA;AAEtD,SAAS,SAAS,CAAC,MAA6B;IAC9C,YAAY,GAAG,EAAE,GAAG,YAAY,EAAE,GAAG,MAAM,EAAE,CAAA;AAC/C,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QACnC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACxB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;QAChE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAwB,CAAA;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,KAA6B;IACtD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3C,IAAI,KAAK,YAAY,GAAG;QAAE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAA;IACjD,OAAO,KAAK,CAAC,GAAG,CAAA;AAClB,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW;IACrC,OAAO,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,eAAe,CAAC,CAAA;AACpE,CAAC;AAED,SAAS,oBAAoB,CAAC,GAAW;IACvC,8EAA8E;IAC9E,8EAA8E;IAC9E,8EAA8E;IAC9E,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QACtB,OAAO,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,EAAE,CAAA;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,uBAAuB;IACzB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IACxC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAA;IAC3C,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACvC,IAAI,UAAU,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IACrD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,SAAS,iBAAiB,CAAC,WAAmB;IAC5C,MAAM,aAAa,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAA;IAEvD,sDAAsD;IACtD,IAAI,MAAM,GAAG,aAAa,CAAA;IAC1B,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;QACzC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,eAAe,CAAC,CAAA;IACzE,CAAC;SAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAChD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,EAAE,yBAAyB,CAAC,CAAA;IACzE,CAAC;IAED,OAAO,IAAI,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,QAAQ,EAAE,CAAA;AACnD,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACvC,OAAO,KAAK;SACT,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,gBAAgB,CAAC;SACjD,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACrD,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,IAA+B,CAAA;YACvD,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CAAA;AACN,CAAC;AAED,SAAS,cAAc,CAAC,KAAyB;IAC/C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAE5B,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,CAAC,CAAC,KAAK,CAAA;IACrE,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,+CAA+C,EAAE,EAAE,CAAC,CAAA;IAEtF,+EAA+E;IAC/E,kEAAkE;IAClE,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAA;IAC3E,MAAM,YAAY,GAAG,eAAe,KAAK,GAAG,IAAI,eAAe,KAAK,MAAM,CAAA;IAE1E,IACE,YAAY;QACZ,CACE,SAAS,KAAK,SAAS;YACvB,SAAS,KAAK,eAAe;YAC7B,SAAS,KAAK,eAAe;YAC7B,SAAS,KAAK,aAAa,CAC5B,EACD,CAAC;QACD,MAAM,WAAW,GAAG,CAClB,OAAO,CAAC,GAAG,CAAC,sCAAsC,IAAI,0BAA0B,CACjF,CAAC,IAAI,EAAE,CAAA;QAER,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,GAAG,EAAE,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,2BAA2B,SAAS,OAAO,WAAW,EAAE,CAAC,CAAA;QACvE,CAAC;QAED,OAAO,WAAW,CAAA;IACpB,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,YAAY,CAAC,KAAyB;IAC7C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAA;AAC7E,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAyB;IACjD,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,CAAA;AACnD,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAgB;IACzC,MAAM,eAAe,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5C,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;QACzC,eAAe,CAAC,GAAG,CAAC,cAAc,EAAE,kCAAkC,CAAC,CAAA;IACzE,CAAC;IACD,OAAO,eAAe,CAAA;AACxB,CAAC;AAED,SAAS,mBAAmB,CAAC,OAAY,EAAE,eAAuB,EAAE;IAClE,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,YAAY,CAAA;IACrB,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,QAAQ;QAChE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO;QACxB,CAAC,CAAC,OAAO,OAAO,EAAE,MAAM,KAAK,QAAQ;YACnC,CAAC,CAAC,OAAO,CAAC,MAAM;YAChB,CAAC,CAAC,EAAE,CAAA;IAER,MAAM,YAAY,GAAG,OAAO,OAAO,EAAE,KAAK,EAAE,OAAO,KAAK,QAAQ;QAC9D,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO;QACvB,CAAC,CAAC,EAAE,CAAA;IAEN,MAAM,eAAe,GAAG,OAAO,OAAO,EAAE,OAAO,KAAK,QAAQ;QAC1D,CAAC,CAAC,OAAO,CAAC,OAAO;QACjB,CAAC,CAAC,EAAE,CAAA;IAEN,OAAO,aAAa,IAAI,YAAY,IAAI,eAAe,IAAI,YAAY,CAAA;AACzE,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAY;IACpC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAA;IAEtD,OAAO,CACL,CAAC,OAAO,OAAO,EAAE,MAAM,EAAE,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;QAClE,CAAC,OAAO,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;QAChE,CAAC,OAAO,OAAO,EAAE,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;QACnD,EAAE,CACH,CAAA;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAY,EAAE,eAAuB,EAAE;IACxE,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAA;IACpD,MAAM,IAAI,GAAG,GAAG,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,IAAI,YAAY,EAAE,CAAC,WAAW,EAAE,CAAA;IAE1F,OAAO,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;AACjE,CAAC;AAED,SAAS,uBAAuB,CAC9B,UAAyC,EACzC,OAAgB,EAChB,SAAiB,EACjB,kBAA0B,EAC1B,MAAc,IAAI,CAAC,GAAG,EAAE;IAExB,MAAM,eAAe,GAAG,qBAAqB,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;IACnF,MAAM,gBAAgB,GACpB,2BAA2B,CAAC,UAAU,EAAE,GAAG,EAAE;QAC3C,gCAAgC,EAAE,IAAI;KACvC,CAAC,IAAI,CAAC,CAAA;IACT,MAAM,iBAAiB,GAAG,4BAA4B,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;IAC3E,MAAM,aAAa,GAAG,GAAG,GAAG,kBAAkB,CAAA;IAE9C,OAAO,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,CAAC,CAAA;AACtF,CAAC;AAED,SAAS,cAAc,CAAC,OAAe;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,SAAQ;QACxC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAA0C,CAAA;YACnF,IAAI,IAAI,EAAE,IAAI,KAAK,eAAe,IAAI,IAAI,EAAE,IAAI,KAAK,oBAAoB,EAAE,CAAC;gBAC1E,OAAO,IAAI,CAAC,QAAQ,CAAA;YACtB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,0BAA0B;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,QAAkB,EAAE,OAAgB;IAClE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;IACtD,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAA;IACxC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;IACjC,IAAI,QAAQ,GAAG,EAAE,CAAA;IAEjB,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;QAC3C,IAAI,IAAI;YAAE,MAAK;QACf,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACrD,CAAC;IAED,MAAM,aAAa,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAA;IAC9C,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE;YAC5B,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,OAAO;SACR,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IACxC,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,iCAAiC,CAAC,CAAA;IAElE,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,EAAE;QACjD,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,OAAO,EAAE,WAAW;KACrB,CAAC,CAAA;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,eAAe,GAAW,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAe,EAAE,EAAE;IAClG,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE;QACjC,MAAM,UAAU,GAAG;YACjB,qCAAqC;YACrC,kCAAkC;SACnC,CAAA;QACD,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,IAAI,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;oBAAE,OAAO,CAAC,CAAA;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,EAAE,CAAA;IAEJ,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAA;IAC/D,MAAM,aAAa,GAAG,gBAAgB,KAAK,GAAG,IAAI,gBAAgB,KAAK,MAAM,CAAA;IAC7E,MAAM,WAAW,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gCAAgC,IAAI,mCAAmC,CAAC,CAAC,IAAI,EAAE,CAAA;IAEhH,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAkB,CAAA;IACrD,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAkB,CAAA;IACrD,MAAM,yBAAyB,GAAG,IAAI,GAAG,EAAkB,CAAA;IAE3D,MAAM,uBAAuB,GAAG,CAAC,KAAa,EAAU,EAAE;QACxD,OAAO,MAAM,CAAC,KAAK,CAAC;aACjB,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;aACxB,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;aACrB,UAAU,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAA;IAC9C,CAAC,CAAA;IAED,IAAI,uBAAuB,GAAG,KAAK,CAAA;IAEnC,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,OAAe,EAAE,QAAiB,EAAQ,EAAE;QAC5E,IAAI,CAAC,aAAa;YAAE,OAAM;QAC1B,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAM;QAEzC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAA;QAClE,MAAM,cAAc,GAAG,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,OAAO,CAAA;QAEnE,kEAAkE;QAClE,IAAI,cAAc,IAAI,QAAQ,IAAI,oBAAoB,EAAE,CAAC;YACvD,IAAI,CAAC;gBACH,CAAC,CAAA,GAAG,oBAAoB,WAAW,KAAK,aAAa,OAAO,UAAU,QAAQ,EAAE;qBAC7E,OAAO,EAAE;qBACT,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YACpB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,cAAc,IAAI,QAAQ,IAAI,CAAC,oBAAoB,IAAI,CAAC,uBAAuB,EAAE,CAAC;gBACpF,uBAAuB,GAAG,IAAI,CAAA;gBAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,GAAG,EAAE,CAAC;oBAClD,OAAO,CAAC,GAAG,CAAC,4FAA4F,CAAC,CAAA;gBAC3G,CAAC;YACH,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,oBAAoB,CAAA;gBACtC,MAAM,SAAS,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAA;gBAChD,MAAM,WAAW,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAA;gBACpD,MAAM,MAAM,GAAG,yBAAyB,WAAW,iBAAiB,SAAS,GAAG,CAAA;gBAEhF,0DAA0D;gBAC1D,CAAC,CAAA,GAAG,SAAS,OAAO,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YACxD,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;QAED,IAAI,CAAC,WAAW;YAAE,OAAM;QAExB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,iBAAiB,CAAA;YAChC,CAAC,CAAA,GAAG,MAAM,IAAI,WAAW,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACvD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC,CAAA;IAGD,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,mCAAmC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAC9E,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAClF,MAAM,eAAe,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAEzF,MAAM,aAAa,GAAG,CAAC,SAAiB,EAAU,EAAE;QAClD,MAAM,IAAI,GAAG,CAAC,eAAe,IAAI,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAC5E,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,CAAA;QACpB,OAAO,GAAG,IAAI,YAAY,SAAS,EAAE,CAAA;IACvC,CAAC,CAAA;IAID,MAAM,YAAY,GAAG,CAAG,OAAe,EAAE,IAA2B,IAAI,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,UAAU,CAAA;IAG/G,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAuB,CAAA;IAEvD,MAAM,WAAW,GAAG,CAAC,IAAgC,EAAU,EAAE;QAC/D,IAAI,IAAI,KAAK,OAAO;YAAE,OAAO,cAAc,YAAY,UAAU,CAAA;QACjE,IAAI,IAAI,KAAK,OAAO;YAAE,OAAO,cAAc,YAAY,aAAa,CAAA;QACpE,OAAO,cAAc,YAAY,EAAE,CAAA;IACrC,CAAC,CAAA;IAED,MAAM,UAAU,GAAG,CAAC,IAAgC,EAAE,SAAiB,EAAE,MAAe,EAAU,EAAE;QAClG,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACzD,MAAM,GAAG,GAAG,aAAa,CAAC,SAAS,CAAC,CAAA;QAEpC,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,OAAO,CAAC,SAAS,EAAE,qBAAqB,SAAS,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACpG,CAAC;QAED,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACrB,OAAO,CAAC,SAAS,EAAE,aAAa,SAAS,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC5F,CAAC;QAED,OAAO,CAAC,SAAS,EAAE,UAAU,SAAS,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzF,CAAC,CAAA;IAED,MAAM,aAAa,GAAG,CAAC,IAAgC,EAAE,SAAiB,EAAE,MAAe,EAAQ,EAAE;QACnG,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;QAChD,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,CAAA;IAC3E,CAAC,CAAA;IAED,MAAM,cAAc,GAAG,KAAK,EAAE,IAAgC,EAAE,SAAiB,EAAE,MAAe,EAAiB,EAAE;QACnH,IAAI,CAAC,aAAa;YAAE,OAAM;QAC1B,IAAI,CAAC,OAAO;YAAE,OAAM;QAEpB,MAAM,UAAU,GAAG,aAAa,CAAC,SAAS,CAAC,CAAA;QAC3C,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAA;QAC/B,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;QAEhD,yCAAyC;QACzC,MAAM,QAAQ,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;QAEtE,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,2BAA2B;YAC3C,OAAO,EAAE,KAAK;YACd,UAAU,EAAE,QAAQ;SACrB,CAAA;QAED,IAAI,UAAU;YAAE,OAAO,CAAC,OAAO,CAAC,GAAG,UAAU,CAAA;QAC7C,IAAI,SAAS;YAAE,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,SAAS,EAAE,CAAA;QAE/D,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC,CAAA;IACD,MAAM,cAAc,GAAG,CAAC,GAAW,EAAE,KAAa,EAAW,EAAE;QAC7D,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,GAAG,GAAG,IAAI,GAAG,KAAK;YAAE,OAAO,IAAI,CAAA;QACnC,mBAAmB,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;QACjC,OAAO,KAAK,CAAA;IACd,CAAC,CAAA;IAED,MAAM,iBAAiB,GAAG,CAAC,MAAW,EAAU,EAAE;QAChD,MAAM,OAAO,GAAG,OAAO,MAAM,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;QAChF,MAAM,OAAO,GAAG,OAAO,MAAM,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;QACzE,MAAM,IAAI,GAAG,OAAO,MAAM,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;QAEvE,MAAM,KAAK,GAAa,EAAE,CAAA;QAC1B,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,YAAY,OAAO,EAAE,CAAC,CAAA;QAClE,oFAAoF;QACpF,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,OAAO,GACX,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;YACnG,KAAK,CAAC,IAAI,CAAC,YAAY,OAAO,GAAG,CAAC,CAAA;QACpC,CAAC;QACD,IAAI,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAChC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC,CAAA;IAED,MAAM,iBAAiB,GAAG,CAAC,GAAQ,EAAU,EAAE;QAC7C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAA;QAC9C,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;QACzD,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;QACzD,MAAM,OAAO,GACX,CAAC,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC;YAChD,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;YAC7D,EAAE,CAAA;QACJ,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzD,CAAC,CAAA;IAED,MAAM,UAAU,GAAG,KAAK,EACtB,IAAgC,EAChC,SAAiB,EACjB,MAAe,EACA,EAAE;QACjB,IAAI,CAAC;YACH,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,IAAI,CAAC;YACH,MAAM,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC,CAAA;IAED,OAAO;QACL,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YACzB,IAAI,CAAC,aAAa;gBAAE,OAAM;YAC1B,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;gBAAE,OAAM;YAExC,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBACzE,MAAM,IAAI,GAAI,KAAa,CAAC,UAAU,EAAE,IAE3B,CAAA;gBACb,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,CAAA;gBACnB,IAAI,EAAE,EAAE,CAAC;oBACP,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;gBAClD,CAAC;gBACD,OAAM;YACR,CAAC;YAED,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBACpC,MAAM,SAAS,GAAI,KAAa,CAAC,UAAU,EAAE,SAA+B,CAAA;gBAC5E,MAAM,MAAM,GAAI,KAAa,CAAC,UAAU,EAAE,MAAM,CAAA;gBAChD,MAAM,UAAU,GAAG,MAAM,EAAE,IAA0B,CAAA;gBACrD,IAAI,CAAC,SAAS,IAAI,CAAC,UAAU;oBAAE,OAAM;gBAErC,mBAAmB,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;gBAE9C,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;oBAC3B,MAAM,OAAO,GAAG,OAAO,MAAM,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;oBAChF,MAAM,WAAW,GAAG,yBAAyB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;oBAE5D,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;wBAChC,IAAI,WAAW,KAAK,OAAO,IAAI,cAAc,CAAC,SAAS,SAAS,IAAI,OAAO,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;4BACrF,OAAM;wBACR,CAAC;wBACD,yBAAyB,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;oBACnD,CAAC;oBAED,MAAM,GAAG,GAAG,SAAS,SAAS,IAAI,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;oBAChF,IAAI,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC;wBAAE,OAAM;oBAErC,KAAK,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAA;gBAChE,CAAC;gBAED,OAAM;YACR,CAAC;YAED,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;gBACnC,MAAM,SAAS,GAAI,KAAa,CAAC,UAAU,EAAE,SAA+B,CAAA;gBAC5E,MAAM,EAAE,GAAG,SAAS,IAAI,SAAS,CAAA;gBACjC,MAAM,GAAG,GAAI,KAAa,CAAC,UAAU,EAAE,KAAK,CAAA;gBAC5C,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAA;gBACrC,MAAM,GAAG,GAAG,SAAS,EAAE,IAAI,MAAM,EAAE,CAAA;gBACnC,IAAI,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC;oBAAE,OAAM;gBACrC,KAAK,UAAU,CAAC,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,CAAA;gBACpC,OAAM;YACR,CAAC;YAED,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBAClC,MAAM,SAAS,GAAI,KAAa,CAAC,UAAU,EAAE,SAA+B,CAAA;gBAC5E,IAAI,CAAC,SAAS;oBAAE,OAAM;gBAEtB,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;gBAC/C,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;oBACxC,IAAI,cAAc,CAAC,QAAQ,SAAS,EAAE,EAAE,IAAI,CAAC;wBAAE,OAAM;oBACrD,KAAK,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;gBACpC,CAAC;gBAED,mBAAmB,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;YAC5C,CAAC;QACH,CAAC;QACD,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YACvB,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAA;YACrE,MAAM,YAAY,GAAG,eAAe,KAAK,GAAG,IAAI,eAAe,KAAK,OAAO,CAAA;YAC3E,IAAI,CAAC,YAAY;gBAAE,OAAM;YAEzB,MAAM,WAAW,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,sCAAsC,IAAI,0BAA0B,CAAC,CAAC,IAAI,EAAE,CAAA;YAC7G,IAAI,CAAC;gBACH,MAAM,MAAM,GAAI,MAAM,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAS,IAAI,IAAI,CAAA;gBAC9D,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;oBAAE,OAAM;gBACjD,MAAM,CAAC,MAAM,KAAK,EAAE,CAAA;gBACpB,MAAM,CAAC,SAAS,KAAK,EAAE,CAAA;gBAEvB,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAA;gBACxC,MAAM,gBAAgB,GAAG,CAAC,WAAW,CAAC,CAAA;gBACtC,IAAI,gBAAgB,CAAC,WAAW,CAAC,IAAI,aAAa,CAAC,GAAG,WAAW,OAAO,CAAC,EAAE,CAAC;oBAC1E,gBAAgB,CAAC,IAAI,CAAC,GAAG,WAAW,OAAO,CAAC,CAAA;gBAC9C,CAAC;gBACD,KAAK,MAAM,YAAY,IAAI;oBACzB,yBAAyB;oBACzB,4BAA4B;oBAC5B,0BAA0B;oBAC1B,2BAA2B;iBAC5B,EAAE,CAAC;oBACF,IAAI,aAAa,CAAC,YAAY,CAAC,EAAE,CAAC;wBAChC,gBAAgB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;oBACrC,CAAC;gBACH,CAAC;gBAED,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;oBACvC,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,CAAA;oBACpC,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;wBAAE,SAAQ;oBAC9C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,CAAA;gBAChC,CAAC;gBAED,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;oBACvC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;wBACxC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;oBACnC,CAAC;gBACH,CAAC;gBAED,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,GAAG,EAAE,CAAC;oBAClD,OAAO,CAAC,GAAG,CAAC,yCAAyC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;gBACrF,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,GAAG,EAAE,CAAC;oBAClD,OAAO,CAAC,GAAG,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAA;gBAC3D,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,EAAE;YACJ,QAAQ,EAAE,WAAW;YAErB;;eAEG;YACH,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ;gBAC5B,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAA;gBACnC,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAA;gBAE/B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC1B,OAAO,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAA;oBACxF,OAAO,EAAE,CAAA;gBACX,CAAC;gBAED,MAAM,WAAW,GAAG,KAAK,EACvB,KAA6B,EAC7B,IAAkB,EACC,EAAE;oBACrB,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAA;oBAEnC,IAAI,IAAI,GAAwB,EAAE,CAAA;oBAClC,IAAI,CAAC;wBACH,IAAI,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;oBAC1D,CAAC;oBAAC,MAAM,CAAC;wBACP,IAAI,GAAG,EAAE,CAAA;oBACX,CAAC;oBAED,MAAM,eAAe,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;oBAElD,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;oBACzB,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;oBAClC,MAAM,WAAW,GAAG,aAAa,EAAE,IAAI,CAAC,CAAC,UAAU,CAAC,WAAW,CAAA;oBAC/D,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;wBAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;wBACtB,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,IAAI,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAC;4BACrD,CAAC,CAAC,GAAG,CAAC,qBAAqB,IAAI,GAAG,CAAC,qBAAqB,GAAG,GAAG,CAAC;4BAC/D,CAAC,CAAC,GAAG,CAAC,yBAAyB,IAAI,GAAG,CAAC,yBAAyB,GAAG,GAAG,CAAC;4BACvE,CAAC,GAAG,CAAC,WAAW;4BAChB,GAAG,CAAC,OAAO,KAAK,KAAK,CAAA;oBAC9B,CAAC,CAAC,CAAC,MAAM,CAAA;oBAET,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,CAAA;oBAChE,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAA;oBACtC,IAAI,OAAO,GAAG,CAAC,CAAA;oBAEf,OAAO,OAAO,GAAG,WAAW,EAAE,CAAC;wBAC7B,OAAO,EAAE,CAAA;wBAET,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAA;wBACrC,MAAM,eAAe,GAAiB;4BACpC,GAAG,YAAY;4BACf,gBAAgB,EAAE,QAAQ,CAAC,QAAQ,CAAC,gBAAgB;yBACrD,CAAA;wBAED,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,eAAe,EAAE;4BACrD,KAAK,EAAE,eAAe;yBACvB,CAAC,CAAA;wBAEF,IAAI,CAAC,QAAQ,EAAE,CAAC;4BACd,IAAI,WAAW,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;gCAC1C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;gCAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;gCACtB,IAAI,MAAM,EAAE,gBAAgB,IAAI,MAAM,CAAC,gBAAgB,GAAG,GAAG,EAAE,CAAC;oCAC9D,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;wCACb,KAAK,EAAE;4CACL,IAAI,EAAE,cAAc;4CACpB,OAAO,EAAE,mBAAmB,MAAM,CAAC,KAAK,2BAA2B,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,WAAW,EAAE,EAAE;4CACpH,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE;yCAC5E;qCACF,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACjE,CAAA;gCACH,CAAC;4BACH,CAAC;4BACD,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;gCACb,KAAK,EAAE,MAAM,CAAC,kBAAkB,CAAC,uCAAuC,CAAC;6BAC1E,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACjE,CAAA;wBACH,CAAC;wBAED,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAA;wBAEnC,IAAI,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;4BACpC,SAAQ;wBACV,CAAC;wBACD,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;wBAE/B,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAA;wBAChC,MAAM,SAAS,GAAG,OAAO,EAAE,CAAC,cAAc,CAAC,EAAE,kBAAkB,CAAA;wBAC/D,IAAI,CAAC,SAAS,EAAE,CAAC;4BACf,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;gCACb,KAAK,EAAE;oCACL,IAAI,EAAE,mBAAmB;oCACzB,OAAO,EAAE,qDAAqD;iCAC/D;6BACF,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACjE,CAAA;wBACH,CAAC;wBAED,MAAM,WAAW,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAA;wBAC5C,MAAM,GAAG,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAA;wBAE1C,MAAM,WAAW,GAAG,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;wBACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;wBAChD,MAAM,iBAAiB,GAAG,QAAQ,IAAI,gBAAgB,CAAC,eAAe,CAAC,CAAA;wBACvE,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,gCAAgC,CAAC,CAAA;wBAE1E,MAAM,OAAO,GAAwB;4BACnC,GAAG,IAAI;4BACP,KAAK,EAAE,eAAe;4BACtB,KAAK,EAAE,KAAK;yBACb,CAAA;wBAED,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;4BACrC,MAAM,aAAa,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,8BAA8B,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;4BAC/E,IAAI,aAAa,IAAI,aAAa,KAAK,UAAU,IAAI,aAAa,KAAK,OAAO,IAAI,aAAa,KAAK,GAAG,EAAE,CAAC;gCACxG,OAAO,CAAC,UAAU,GAAG,aAAa,CAAA;4BACpC,CAAC;wBACH,CAAC;wBAED,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;4BAClB,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;wBAC5C,CAAC;wBAED,IAAI,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;4BACxB,OAAO,CAAC,SAAS,GAAG;gCAClB,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;gCAC5B,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;6BAC1B,CAAA;4BAED,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC;gCACnC,OAAO,CAAC,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,OAAO,IAAI,MAAM,CAAA;4BAClE,CAAC;wBACH,CAAC;wBAED,IAAI,YAAY,CAAC,eAAe,CAAC,IAAI,OAAO,CAAC,SAAS,EAAE,OAAO,KAAK,SAAS,EAAE,CAAC;4BAC9E,OAAO,OAAO,CAAC,SAAS,CAAC,OAAO,CAAA;wBAClC,CAAC;wBAED,IAAI,iBAAiB,EAAE,CAAC;4BACtB,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,UAAU,CAAA;4BAEzD,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,GAAG,EAAE,CAAC;gCAClD,OAAO,CAAC,GAAG,CAAC,mCAAmC,eAAe,0BAA0B,CAAC,CAAA;4BAC3F,CAAC;wBACH,CAAC;6BAAM,IAAI,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,GAAG,EAAE,CAAC;4BACrE,OAAO,CAAC,GAAG,CAAC,yDAAyD,eAAe,EAAE,CAAC,CAAA;wBACzF,CAAC;wBAED,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,GAAG,IAAI,OAAO,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;4BACzF,OAAO,CAAC,GAAG,CAAC,oDAAoD,eAAe,EAAE,CAAC,CAAA;wBACpF,CAAC;wBAED,OAAO,OAAO,CAAC,gBAAgB,CAAA;wBAE/B,IAAI,CAAC;4BACH,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC,CAAA;4BAChD,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;4BAC3B,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAA;4BAC/C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAA;4BAC/C,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;4BACjD,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,oBAAoB,CAAC,cAAc,CAAC,CAAA;4BACrE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,UAAU,EAAE,oBAAoB,CAAC,gBAAgB,CAAC,CAAA;4BAE7E,MAAM,QAAQ,GAAG,OAAO,EAAE,gBAAgB,CAAA;4BAC1C,IAAI,QAAQ,EAAE,CAAC;gCACb,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAA;gCACrD,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;4BAClD,CAAC;iCAAM,CAAC;gCACN,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,eAAe,CAAC,CAAA;gCAC9C,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAA;4BAC3C,CAAC;4BAED,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAA;4BAE1C,MAAM,WAAW,GAAG,KAAK,EAAE,cAAmC,EAAqB,EAAE;gCACnF,OAAO,KAAK,CAAC,GAAG,EAAE;oCAChB,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,MAAM;oCAC9B,OAAO;oCACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;iCACrC,CAAC,CAAA;4BACJ,CAAC,CAAA;4BAED,MAAM,gBAAgB,GAAG,CAAC,QAAkB,EAAiC,EAAE;gCAC7E,MAAM,WAAW,GAAG,sBAAsB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;gCAC5D,MAAM,gBAAgB,GAAG,WAAW;oCAClC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,UAAU,EAAE,WAAW,CAAC;oCAClD,CAAC,CAAC,OAAO,CAAC,UAAU,CAAA;gCACtB,IAAI,WAAW,EAAE,CAAC;oCAChB,MAAM,eAAe,GAAG,2BAA2B,CAAC,gBAAgB,CAAC,CAAA;oCACrE,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE;wCAC3B,UAAU,EAAE,gBAAgB;wCAC5B,gBAAgB,EAAE,eAAe;qCAClC,CAAC,CAAA;gCACJ,CAAC;gCAED,OAAO,gBAAgB,CAAA;4BACzB,CAAC,CAAA;4BAED,IAAI,GAAG,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAA;4BAEpC,IAAI,gBAAgB,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;4BAE5C,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;gCAC9D,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAQ,CAAA;gCACnE,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;gCAE1D,IAAI,kBAAkB,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC;oCAC7C,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,GAAG,EAAE,CAAC;wCAClD,OAAO,CAAC,GAAG,CAAC,gFAAgF,CAAC,CAAA;oCAC/F,CAAC;oCAED,MAAM,mBAAmB,GAAG,EAAE,GAAG,OAAO,EAAE,CAAA;oCAC1C,OAAO,mBAAmB,CAAC,YAAY,CAAA;oCACvC,GAAG,GAAG,MAAM,WAAW,CAAC,mBAAmB,CAAC,CAAA;oCAC5C,gBAAgB,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;gCAC1C,CAAC;4BACH,CAAC;4BAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gCAC7C,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAqC,CAAA;gCAChG,MAAM,OAAO,GAAG,SAAS,EAAE,KAAK,EAAE,OAAO,IAAI,EAAE,CAAA;gCAC/C,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oCACxE,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;gCAChC,CAAC;gCAED,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;oCAC1B,SAAQ;gCACV,CAAC;gCAED,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oCACb,KAAK,EAAE,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;iCACpE,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACxE,CAAA;4BACH,CAAC;4BAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gCACvB,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAQ,CAAA;gCACnE,MAAM,SAAS,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAA;gCAChD,MAAM,gBAAgB,GAAG,uBAAuB,CAC9C,gBAAgB,EAChB,GAAG,CAAC,OAAO,EACX,SAAS,EACT,YAAY,CAAC,mBAAmB,CACjC,CAAA;gCACD,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAA;gCAEhD,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;oCAC1B,SAAQ;gCACV,CAAC;gCAED,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oCACb,KAAK,EAAE,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;iCACpE,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACjE,CAAA;4BACH,CAAC;4BAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gCACvB,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAQ,CAAA;gCACnE,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;gCAE1D,MAAM,IAAI,GACR,CAAC,OAAO,SAAS,EAAE,MAAM,EAAE,IAAI,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC;oCACtE,CAAC,OAAO,SAAS,EAAE,KAAK,EAAE,IAAI,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;oCACpE,EAAE,CAAA;gCACJ,MAAM,OAAO,GACX,CAAC,OAAO,SAAS,EAAE,MAAM,EAAE,OAAO,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC;oCAC5E,CAAC,OAAO,SAAS,EAAE,MAAM,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC;oCAC3D,CAAC,OAAO,SAAS,EAAE,KAAK,EAAE,OAAO,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC;oCAC1E,CAAC,OAAO,SAAS,EAAE,OAAO,KAAK,QAAQ,IAAI,SAAS,CAAC,OAAO,CAAC;oCAC7D,SAAS;oCACT,EAAE,CAAA;gCAEJ,MAAM,sBAAsB,GAC1B,IAAI,KAAK,uBAAuB;oCAChC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;oCACvD,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAA;gCAEzD,IAAI,sBAAsB,EAAE,CAAC;oCAC3B,wBAAwB,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,8BAA8B,EAAE;wCACnF,KAAK,EAAE,OAAO,IAAI,IAAI;qCACvB,CAAC,CAAA;oCAEF,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;wCAC1B,SAAQ;oCACV,CAAC;oCAED,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;wCACb,KAAK,EAAE,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;qCACpE,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACjE,CAAA;gCACH,CAAC;4BACH,CAAC;4BAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gCACvB,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAQ,CAAA;gCACnE,MAAM,OAAO,GACX,CAAC,OAAO,SAAS,EAAE,MAAM,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC;oCAC3D,CAAC,OAAO,SAAS,EAAE,KAAK,EAAE,OAAO,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC;oCAC1E,CAAC,OAAO,SAAS,EAAE,OAAO,KAAK,QAAQ,IAAI,SAAS,CAAC,OAAO,CAAC;oCAC7D,EAAE,CAAA;gCAEJ,MAAM,kBAAkB,GACtB,OAAO,OAAO,KAAK,QAAQ;oCAC3B,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;oCACxD,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAA;gCAEnD,IAAI,kBAAkB,EAAE,CAAC;oCACvB,oBAAoB,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,0BAA0B,EAAE;wCAC3E,KAAK,EAAE,eAAe;wCACtB,KAAK,EAAE,OAAO;qCACf,CAAC,CAAA;oCAEF,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;wCAC1B,SAAQ;oCACV,CAAC;oCAED,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;wCACb,KAAK,EAAE,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;qCACpE,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACjE,CAAA;gCACH,CAAC;4BACH,CAAC;4BAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gCACZ,OAAO,GAAG,CAAA;4BACZ,CAAC;4BAED,MAAM,eAAe,GAAG,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;4BACtD,IAAI,CAAC,WAAW,IAAI,eAAe,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;gCACvF,OAAO,MAAM,gBAAgB,CAAC,GAAG,EAAE,eAAe,CAAC,CAAA;4BACrD,CAAC;4BAED,OAAO,GAAG,CAAA;wBACZ,CAAC;wBAAC,OAAO,GAAG,EAAE,CAAC;4BACb,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,gCAAgC,GAAG,EAAE,EAAE,EAAE,CAAC,EACrG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACjE,CAAA;wBACH,CAAC;oBACH,CAAC;oBAED,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;wBACb,KAAK,EAAE,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;qBACpE,CAAC,EACF,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACjE,CAAA;gBACH,CAAC,CAAA;gBAED,0DAA0D;gBAC1D,OAAO;oBACL,MAAM,EAAE,eAAe;oBACvB,OAAO,EAAE,cAAc;oBACvB,KAAK,EAAE,WAAW;iBACnB,CAAA;YACH,CAAC;YAED,OAAO,EAAE;gBACP;oBACE,KAAK,EAAE,+BAA+B;oBACtC,IAAI,EAAE,OAAgB;oBAEtB,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,GAAG,EAAE,OAAO;4BACZ,OAAO,EAAE,sCAAsC;4BAC/C,WAAW,EAAE,UAAU;yBACxB;qBACF;oBAED;;uBAEG;oBACH,SAAS,EAAE,KAAK,EAAE,MAA+B,EAAE,EAAE;wBACnD,MAAM,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,WAAW,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;wBACtD,MAAM,IAAI,GAAG,MAAM,uBAAuB,EAAE,CAAA;wBAE5C,OAAO;4BACL,GAAG,EAAE,IAAI,CAAC,GAAG;4BACb,MAAM,EAAE,MAAe;4BACvB,YAAY,EAAE,iDAAiD,KAAK,GAAG;4BAEvE,QAAQ,EAAE,KAAK,IAAI,EAAE;gCACnB,IAAI,CAAC;oCACH,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;oCAC/C,OAAO;wCACL,IAAI,EAAE,SAAkB;wCACxB,QAAQ,EAAE,WAAW;wCACrB,OAAO,EAAE,OAAO,CAAC,YAAY;wCAC7B,MAAM,EAAE,OAAO,CAAC,WAAW;wCAC3B,OAAO,EAAE,OAAO,CAAC,SAAS;qCAC3B,CAAA;gCACH,CAAC;gCAAC,MAAM,CAAC;oCACP,OAAO,EAAE,IAAI,EAAE,QAAiB,EAAE,CAAA;gCACpC,CAAC;4BACH,CAAC;yBACF,CAAA;oBACH,CAAC;iBACF;gBACD;oBACE,KAAK,EAAE,8BAA8B;oBACrC,IAAI,EAAE,KAAc;iBACrB;aACF;SACF;KACF,CAAA;AACH,CAAC,CAAA;AAED,eAAe,eAAe,CAAA"} \ No newline at end of file diff --git a/dist/limits-refresh.d.ts b/dist/limits-refresh.d.ts deleted file mode 100644 index 1335eb2..0000000 --- a/dist/limits-refresh.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { AccountCredentials } from './types.js'; -export interface LimitRefreshResult { - alias: string; - updated: boolean; - error?: string; -} -export declare function refreshRateLimitsForAccount(account: AccountCredentials): Promise; -export declare function refreshRateLimits(accounts: AccountCredentials[], alias?: string): Promise; -//# sourceMappingURL=limits-refresh.d.ts.map \ No newline at end of file diff --git a/dist/limits-refresh.d.ts.map b/dist/limits-refresh.d.ts.map deleted file mode 100644 index ab18bf6..0000000 --- a/dist/limits-refresh.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"limits-refresh.d.ts","sourceRoot":"","sources":["../src/limits-refresh.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEpD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAsB,2BAA2B,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAkH1G;AAED,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,kBAAkB,EAAE,EAC9B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAkB/B"} \ No newline at end of file diff --git a/dist/limits-refresh.js b/dist/limits-refresh.js deleted file mode 100644 index 874eca6..0000000 --- a/dist/limits-refresh.js +++ /dev/null @@ -1,120 +0,0 @@ -import { getBlockingRateLimitResetAt, isRateLimitErrorText, mergeRateLimits, parseRateLimitResetFromError } from './rate-limits.js'; -import { markAuthInvalid, markWorkspaceDeactivated } from './rotation.js'; -import { loadStore, updateAccount } from './store.js'; -import { probeRateLimitsForAccount } from './probe-limits.js'; -import { logError, logInfo } from './logger.js'; -import { DEFAULT_CONFIG, calculateLimitsConfidence } from './types.js'; -import { fetchUsageRateLimitsForAccount } from './usage-limits.js'; -export async function refreshRateLimitsForAccount(account) { - updateAccount(account.alias, { limitStatus: 'running', limitError: undefined }); - logInfo(`Refreshing limits for ${account.alias}`); - const usage = await fetchUsageRateLimitsForAccount(account); - if (usage.rateLimits) { - const now = Date.now(); - const updates = { - rateLimits: mergeRateLimits(account.rateLimits, usage.rateLimits), - limitStatus: 'success', - limitError: undefined, - lastLimitProbeAt: now, - limitsConfidence: calculateLimitsConfidence(now, account.lastLimitErrorAt, 'success'), - authInvalid: false, - authInvalidatedAt: undefined - }; - if (usage.planType) { - updates.planType = usage.planType; - } - if (typeof usage.rateLimitedUntil === 'number' && usage.rateLimitedUntil > now) { - updates.rateLimitedUntil = usage.rateLimitedUntil; - } - updateAccount(account.alias, updates); - logInfo(`Limits refreshed for ${account.alias} via usage API`); - return { alias: account.alias, updated: true }; - } - if (usage.error) { - if (usage.shouldProbeFallback === false) { - const now = Date.now(); - if (usage.authInvalid) { - markAuthInvalid(account.alias); - } - if (usage.workspaceDeactivated) { - markWorkspaceDeactivated(account.alias, DEFAULT_CONFIG.workspaceDeactivatedCooldownMs, { error: usage.workspaceDeactivatedReason || usage.error }); - } - updateAccount(account.alias, { - limitStatus: 'error', - limitError: usage.error, - lastLimitErrorAt: now, - limitsConfidence: calculateLimitsConfidence(account.lastLimitProbeAt, now, 'error') - }); - logInfo(`Skipping limits probe for ${account.alias}: ${usage.error}`); - return { - alias: account.alias, - updated: false, - error: usage.error - }; - } - logInfo(`Usage API limits lookup failed for ${account.alias}, falling back to probe: ${usage.error}`); - } - const probe = await probeRateLimitsForAccount(account); - if (!probe.isAuthoritative || !probe.rateLimits) { - const now = Date.now(); - const errorText = usage.error || probe.error || 'Probe failed'; - logError(`Limit refresh failed for ${account.alias}: ${errorText}`); - const likelyRateLimit = isRateLimitErrorText(errorText); - const parsedResetAt = parseRateLimitResetFromError(errorText, now); - const fallbackResetAt = likelyRateLimit - ? getBlockingRateLimitResetAt(account.rateLimits, now, { - conservativeWhenRemainingUnknown: true - }) - : undefined; - const rateLimitedUntil = parsedResetAt ?? fallbackResetAt; - const updates = { - limitStatus: 'error', - limitError: errorText, - lastLimitErrorAt: now, - limitsConfidence: calculateLimitsConfidence(account.lastLimitProbeAt, now, 'error') - }; - if (typeof rateLimitedUntil === 'number' && rateLimitedUntil > now) { - updates.rateLimitedUntil = rateLimitedUntil; - } - updateAccount(account.alias, updates); - return { - alias: account.alias, - updated: false, - error: errorText - }; - } - const now = Date.now(); - const mergedRateLimits = mergeRateLimits(account.rateLimits, probe.rateLimits); - const blockingResetAt = getBlockingRateLimitResetAt(mergedRateLimits, now); - updateAccount(account.alias, { - rateLimits: mergedRateLimits, - limitStatus: 'success', - limitError: undefined, - lastLimitProbeAt: now, - limitsConfidence: calculateLimitsConfidence(now, account.lastLimitErrorAt, 'success'), - rateLimitedUntil: blockingResetAt, - authInvalid: false, - authInvalidatedAt: undefined - }); - logInfo(`Limits refreshed for ${account.alias} using model ${probe.probeModel || 'unknown'}, effort ${probe.probeEffort || 'default'}`); - return { alias: account.alias, updated: true }; -} -export async function refreshRateLimits(accounts, alias) { - if (alias) { - const account = accounts.find((acc) => acc.alias === alias); - if (!account) { - return [{ alias, updated: false, error: 'Unknown alias' }]; - } - return [await refreshRateLimitsForAccount(account)]; - } - const store = loadStore(); - const results = []; - for (const account of accounts) { - results.push(await refreshRateLimitsForAccount(account)); - } - if (results.length === 0 && !store.activeAlias) { - return [{ alias: 'active', updated: false, error: 'No accounts configured' }]; - } - return results; -} -//# sourceMappingURL=limits-refresh.js.map \ No newline at end of file diff --git a/dist/limits-refresh.js.map b/dist/limits-refresh.js.map deleted file mode 100644 index 2364626..0000000 --- a/dist/limits-refresh.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"limits-refresh.js","sourceRoot":"","sources":["../src/limits-refresh.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,oBAAoB,EACpB,eAAe,EACf,4BAA4B,EAC7B,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AACzE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACrD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AACtE,OAAO,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAA;AASlE,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,OAA2B;IAC3E,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAA;IAC/E,OAAO,CAAC,yBAAyB,OAAO,CAAC,KAAK,EAAE,CAAC,CAAA;IACjD,MAAM,KAAK,GAAG,MAAM,8BAA8B,CAAC,OAAO,CAAC,CAAA;IAE3D,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,MAAM,OAAO,GAAgC;YAC3C,UAAU,EAAE,eAAe,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC;YACjE,WAAW,EAAE,SAAS;YACtB,UAAU,EAAE,SAAS;YACrB,gBAAgB,EAAE,GAAG;YACrB,gBAAgB,EAAE,yBAAyB,CAAC,GAAG,EAAE,OAAO,CAAC,gBAAgB,EAAE,SAAS,CAAC;YACrF,WAAW,EAAE,KAAK;YAClB,iBAAiB,EAAE,SAAS;SAC7B,CAAA;QACD,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnB,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAA;QACnC,CAAC;QACD,IAAI,OAAO,KAAK,CAAC,gBAAgB,KAAK,QAAQ,IAAI,KAAK,CAAC,gBAAgB,GAAG,GAAG,EAAE,CAAC;YAC/E,OAAO,CAAC,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAA;QACnD,CAAC;QACD,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QACrC,OAAO,CAAC,wBAAwB,OAAO,CAAC,KAAK,gBAAgB,CAAC,CAAA;QAC9D,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;IAChD,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,IAAI,KAAK,CAAC,mBAAmB,KAAK,KAAK,EAAE,CAAC;YACxC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACtB,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;gBACtB,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YAChC,CAAC;YACD,IAAI,KAAK,CAAC,oBAAoB,EAAE,CAAC;gBAC/B,wBAAwB,CACtB,OAAO,CAAC,KAAK,EACb,cAAc,CAAC,8BAA8B,EAC7C,EAAE,KAAK,EAAE,KAAK,CAAC,0BAA0B,IAAI,KAAK,CAAC,KAAK,EAAE,CAC3D,CAAA;YACH,CAAC;YAED,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE;gBAC3B,WAAW,EAAE,OAAO;gBACpB,UAAU,EAAE,KAAK,CAAC,KAAK;gBACvB,gBAAgB,EAAE,GAAG;gBACrB,gBAAgB,EAAE,yBAAyB,CACzC,OAAO,CAAC,gBAAgB,EACxB,GAAG,EACH,OAAO,CACR;aACF,CAAC,CAAA;YACF,OAAO,CAAC,6BAA6B,OAAO,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA;YACrE,OAAO;gBACL,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,KAAK,CAAC,KAAK;aACnB,CAAA;QACH,CAAC;QAED,OAAO,CAAC,sCAAsC,OAAO,CAAC,KAAK,4BAA4B,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA;IACvG,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,yBAAyB,CAAC,OAAO,CAAC,CAAA;IAEtD,IAAI,CAAC,KAAK,CAAC,eAAe,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,IAAI,cAAc,CAAA;QAC9D,QAAQ,CAAC,4BAA4B,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC,CAAA;QACnE,MAAM,eAAe,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAA;QACvD,MAAM,aAAa,GAAG,4BAA4B,CAAC,SAAS,EAAE,GAAG,CAAC,CAAA;QAClE,MAAM,eAAe,GAAG,eAAe;YACrC,CAAC,CAAC,2BAA2B,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,EAAE;gBACnD,gCAAgC,EAAE,IAAI;aACvC,CAAC;YACJ,CAAC,CAAC,SAAS,CAAA;QACb,MAAM,gBAAgB,GAAG,aAAa,IAAI,eAAe,CAAA;QAEzD,MAAM,OAAO,GAAgC;YAC3C,WAAW,EAAE,OAAO;YACpB,UAAU,EAAE,SAAS;YACrB,gBAAgB,EAAE,GAAG;YACrB,gBAAgB,EAAE,yBAAyB,CACzC,OAAO,CAAC,gBAAgB,EACxB,GAAG,EACH,OAAO,CACR;SACF,CAAA;QACD,IAAI,OAAO,gBAAgB,KAAK,QAAQ,IAAI,gBAAgB,GAAG,GAAG,EAAE,CAAC;YACnE,OAAO,CAAC,gBAAgB,GAAG,gBAAgB,CAAA;QAC7C,CAAC;QACD,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QACrC,OAAO;YACL,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,SAAS;SACjB,CAAA;IACH,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,MAAM,gBAAgB,GAAG,eAAe,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;IAC9E,MAAM,eAAe,GAAG,2BAA2B,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAA;IAC1E,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE;QAC3B,UAAU,EAAE,gBAAgB;QAC5B,WAAW,EAAE,SAAS;QACtB,UAAU,EAAE,SAAS;QACrB,gBAAgB,EAAE,GAAG;QACrB,gBAAgB,EAAE,yBAAyB,CAAC,GAAG,EAAE,OAAO,CAAC,gBAAgB,EAAE,SAAS,CAAC;QACrF,gBAAgB,EAAE,eAAe;QACjC,WAAW,EAAE,KAAK;QAClB,iBAAiB,EAAE,SAAS;KAC7B,CAAC,CAAA;IAEF,OAAO,CAAC,wBAAwB,OAAO,CAAC,KAAK,gBAAgB,KAAK,CAAC,UAAU,IAAI,SAAS,YAAY,KAAK,CAAC,WAAW,IAAI,SAAS,EAAE,CAAC,CAAA;IACvI,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAA8B,EAC9B,KAAc;IAEd,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,CAAA;QAC3D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAA;QAC5D,CAAC;QACD,OAAO,CAAC,MAAM,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAA;IACrD,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;IACzB,MAAM,OAAO,GAAyB,EAAE,CAAA;IACxC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,CAAC,IAAI,CAAC,MAAM,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAA;IAC1D,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QAC/C,OAAO,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,CAAA;IAC/E,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"} \ No newline at end of file diff --git a/dist/logger.d.ts b/dist/logger.d.ts deleted file mode 100644 index bc852fd..0000000 --- a/dist/logger.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export declare function logInfo(message: string): void; -export declare function logWarn(message: string): void; -export declare function logError(message: string): void; -export declare function getLogPath(): string; -export declare function readLogTail(maxLines?: number): string[]; -//# sourceMappingURL=logger.d.ts.map \ No newline at end of file diff --git a/dist/logger.d.ts.map b/dist/logger.d.ts.map deleted file mode 100644 index 1ce3ba6..0000000 --- a/dist/logger.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AA+BA,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,WAAW,CAAC,QAAQ,SAAgB,GAAG,MAAM,EAAE,CAS9D"} \ No newline at end of file diff --git a/dist/logger.js b/dist/logger.js deleted file mode 100644 index c2119da..0000000 --- a/dist/logger.js +++ /dev/null @@ -1,52 +0,0 @@ -import * as fs from 'node:fs'; -import * as os from 'node:os'; -import * as path from 'node:path'; -const DEFAULT_LOG_DIR = path.join(os.homedir(), '.config', 'opencode-multi-auth', 'logs'); -const LOG_FILE = process.env.CODEX_SOFT_LOG_PATH || path.join(DEFAULT_LOG_DIR, 'codex-soft.log'); -const MAX_LOG_LINES = 400; -function ensureDir() { - const dir = path.dirname(LOG_FILE); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); - } -} -function sanitize(message) { - return message - .replace(/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g, '[jwt]') - .replace(/\bsk-[A-Za-z0-9]{10,}\b/g, '[token]'); -} -function append(level, message) { - try { - ensureDir(); - const line = `${new Date().toISOString()} [${level}] ${sanitize(message)}\n`; - fs.appendFileSync(LOG_FILE, line, { encoding: 'utf-8', mode: 0o600 }); - } - catch { - // Ignore log write failures - } -} -export function logInfo(message) { - append('info', message); -} -export function logWarn(message) { - append('warn', message); -} -export function logError(message) { - append('error', message); -} -export function getLogPath() { - return LOG_FILE; -} -export function readLogTail(maxLines = MAX_LOG_LINES) { - try { - if (!fs.existsSync(LOG_FILE)) - return []; - const data = fs.readFileSync(LOG_FILE, 'utf-8'); - const lines = data.split('\n').filter(Boolean); - return lines.slice(Math.max(0, lines.length - maxLines)); - } - catch { - return []; - } -} -//# sourceMappingURL=logger.js.map \ No newline at end of file diff --git a/dist/logger.js.map b/dist/logger.js.map deleted file mode 100644 index 03428e7..0000000 --- a/dist/logger.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AAEjC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,CAAC,CAAA;AACzF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,gBAAgB,CAAC,CAAA;AAChG,MAAM,aAAa,GAAG,GAAG,CAAA;AAEzB,SAAS,SAAS;IAChB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAClC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACrD,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,OAAe;IAC/B,OAAO,OAAO;SACX,OAAO,CAAC,oEAAoE,EAAE,OAAO,CAAC;SACtF,OAAO,CAAC,0BAA0B,EAAE,SAAS,CAAC,CAAA;AACnD,CAAC;AAED,SAAS,MAAM,CAAC,KAAa,EAAE,OAAe;IAC5C,IAAI,CAAC;QACH,SAAS,EAAE,CAAA;QACX,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK,KAAK,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAA;QAC5E,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,4BAA4B;IAC9B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAC1B,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAQ,GAAG,aAAa;IAClD,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,CAAA;QACvC,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAC9C,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAA;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/dist/models.d.ts b/dist/models.d.ts deleted file mode 100644 index 91f824e..0000000 --- a/dist/models.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { OpenAIModel, ProviderModel } from './types.js'; -export declare function fetchAvailableModels(token: string): Promise; -export declare function filterGPT5Models(models: OpenAIModel[]): OpenAIModel[]; -export declare function generateModelVariants(baseModels: OpenAIModel[]): Record; -export declare function getDefaultModels(): Record; -export declare function getModels(token?: string): Promise>; -//# sourceMappingURL=models.d.ts.map \ No newline at end of file diff --git a/dist/models.d.ts.map b/dist/models.d.ts.map deleted file mode 100644 index 995668f..0000000 --- a/dist/models.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAiF5D,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAiBhF;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAErE;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAwB9F;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAwChE;AAKD,wBAAsB,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAsBtF"} \ No newline at end of file diff --git a/dist/models.js b/dist/models.js deleted file mode 100644 index 32a9456..0000000 --- a/dist/models.js +++ /dev/null @@ -1,167 +0,0 @@ -const MODELS_ENDPOINT = 'https://api.openai.com/v1/models'; -const REASONING_LEVELS = ['none', 'low', 'medium', 'high', 'xhigh']; -const MODEL_LIMITS = { - 'gpt-5.5': { context: 530000, input: 400000, output: 130000 }, - 'gpt-5.4': { context: 1050000, input: 922000, output: 128000 }, - 'gpt-5.3': { context: 272000, output: 128000 }, - 'gpt-5.3-codex-spark': { context: 272000, output: 128000 }, - 'gpt-5.2': { context: 272000, output: 128000 }, - 'gpt-5.3-codex': { context: 272000, output: 128000 }, - 'gpt-5.2-codex': { context: 272000, output: 128000 }, - 'gpt-5.1': { context: 272000, output: 128000 }, - 'gpt-5.1-codex': { context: 272000, output: 128000 }, - 'gpt-5.1-codex-max': { context: 272000, output: 128000 }, - 'gpt-5.1-codex-mini': { context: 272000, output: 128000 }, -}; -function getModelLimits(modelId) { - for (const [prefix, limits] of Object.entries(MODEL_LIMITS)) { - if (modelId.startsWith(prefix)) - return limits; - } - return { context: 128000, output: 32000 }; -} -function buildProviderModel(baseId, reasoning) { - const limits = getModelLimits(baseId); - const displayName = `${baseId} ${reasoning.charAt(0).toUpperCase() + reasoning.slice(1)} (OAuth)`; - return { - name: displayName, - limit: limits, - modalities: { - input: ['text', 'image'], - output: ['text'] - }, - options: { - reasoningEffort: reasoning, - reasoningSummary: reasoning === 'high' || reasoning === 'xhigh' ? 'detailed' : 'auto', - textVerbosity: 'medium', - include: ['reasoning.encrypted_content'], - store: false - } - }; -} -function buildDefaultProviderModel(baseId) { - return { - ...buildProviderModel(baseId, 'medium'), - name: `${baseId} (OAuth)` - }; -} -function supportsFastMode(baseId) { - return baseId === 'gpt-5.5' || baseId === 'gpt-5.4'; -} -function buildFastProviderModel(baseId) { - const limits = getModelLimits(baseId); - return { - name: `${baseId} Fast (OAuth)`, - limit: limits, - modalities: { - input: ['text', 'image'], - output: ['text'] - }, - options: { - reasoningEffort: 'medium', - reasoningSummary: 'auto', - textVerbosity: 'medium', - include: ['reasoning.encrypted_content'], - store: false, - service_tier: 'priority' - } - }; -} -export async function fetchAvailableModels(token) { - try { - const res = await fetch(MODELS_ENDPOINT, { - headers: { Authorization: `Bearer ${token}` } - }); - if (!res.ok) { - console.error(`[multi-auth] Failed to fetch models: ${res.status}`); - return []; - } - const data = (await res.json()); - return data.data || []; - } - catch (err) { - console.error('[multi-auth] Error fetching models:', err); - return []; - } -} -export function filterGPT5Models(models) { - return models.filter(m => m.id.match(/^gpt-5/)); -} -export function generateModelVariants(baseModels) { - const result = {}; - for (const model of baseModels) { - const baseId = model.id; - const isCodex = baseId.includes('codex'); - result[baseId] = buildDefaultProviderModel(baseId); - const levels = isCodex - ? ['low', 'medium', 'high', 'xhigh'] - : ['none', 'low', 'medium', 'high', 'xhigh']; - for (const level of levels) { - const variantId = `${baseId}-${level}`; - result[variantId] = buildProviderModel(baseId, level); - } - if (supportsFastMode(baseId)) { - result[`${baseId}-fast`] = buildFastProviderModel(baseId); - } - } - return result; -} -export function getDefaultModels() { - const defaults = [ - 'gpt-5.5', - 'gpt-5.4', - 'gpt-5.3', - 'gpt-5.3-codex-spark', - 'gpt-5.3-codex', - 'gpt-5.2', - 'gpt-5.2-codex', - 'gpt-5.1', - 'gpt-5.1-codex', - 'gpt-5.1-codex-max', - 'gpt-5.1-codex-mini' - ]; - const result = {}; - for (const baseId of defaults) { - const isCodex = baseId.includes('codex'); - const levels = isCodex - ? ['low', 'medium', 'high', 'xhigh'] - : ['none', 'low', 'medium', 'high', 'xhigh']; - result[baseId] = buildDefaultProviderModel(baseId); - for (const level of levels) { - if (baseId === 'gpt-5.1-codex-mini' && !['medium', 'high'].includes(level)) - continue; - if (baseId === 'gpt-5.1-codex' && level === 'xhigh') - continue; - if (baseId === 'gpt-5.1' && level === 'xhigh') - continue; - const variantId = `${baseId}-${level}`; - result[variantId] = buildProviderModel(baseId, level); - } - if (supportsFastMode(baseId)) { - result[`${baseId}-fast`] = buildFastProviderModel(baseId); - } - } - return result; -} -let cachedModels = null; -let cacheExpiry = 0; -export async function getModels(token) { - const now = Date.now(); - const CACHE_TTL = 60 * 60 * 1000; - if (cachedModels && now < cacheExpiry) { - return cachedModels; - } - if (token) { - const fetched = await fetchAvailableModels(token); - const gpt5 = filterGPT5Models(fetched); - if (gpt5.length > 0) { - cachedModels = generateModelVariants(gpt5); - cacheExpiry = now + CACHE_TTL; - return cachedModels; - } - } - cachedModels = getDefaultModels(); - cacheExpiry = now + CACHE_TTL; - return cachedModels; -} -//# sourceMappingURL=models.js.map \ No newline at end of file diff --git a/dist/models.js.map b/dist/models.js.map deleted file mode 100644 index d14befa..0000000 --- a/dist/models.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/probe-limits.d.ts b/dist/probe-limits.d.ts deleted file mode 100644 index 3ff2c96..0000000 --- a/dist/probe-limits.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { AccountCredentials, AccountRateLimits } from './types.js'; -export interface ProbeResult { - rateLimits?: AccountRateLimits; - eventTs?: number; - sourceFile?: string; - probeModel?: string; - probeEffort?: string; - probeDurationMs?: number; - error?: string; - isAuthoritative?: boolean; -} -export declare function shouldRetryWithFallback(error?: string): boolean; -export declare function getProbeEffort(): string; -export declare function getProbeModels(): string[]; -export declare function probeRateLimitsForAccount(account: AccountCredentials): Promise; -export declare function getProbeHomeRoot(): string; -//# sourceMappingURL=probe-limits.d.ts.map \ No newline at end of file diff --git a/dist/probe-limits.d.ts.map b/dist/probe-limits.d.ts.map deleted file mode 100644 index af793f8..0000000 --- a/dist/probe-limits.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"probe-limits.d.ts","sourceRoot":"","sources":["../src/probe-limits.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAUvE,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAyLD,wBAAgB,uBAAuB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAa/D;AAGD,wBAAgB,cAAc,IAAI,MAAM,CAMvC;AAED,wBAAgB,cAAc,IAAI,MAAM,EAAE,CASzC;AAqED,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CA0FjG;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC"} \ No newline at end of file diff --git a/dist/probe-limits.js b/dist/probe-limits.js deleted file mode 100644 index c13b348..0000000 --- a/dist/probe-limits.js +++ /dev/null @@ -1,348 +0,0 @@ -import * as fs from 'node:fs'; -import * as os from 'node:os'; -import * as path from 'node:path'; -import { spawn } from 'node:child_process'; -import { findLatestSessionRateLimits } from './sessions-limits.js'; -import { loadStore, updateAccount } from './store.js'; -const CODEX_HOME_ROOT = path.join(os.homedir(), '.codex-multi'); -const CODEX_CONFIG_PATH = path.join(os.homedir(), '.codex', 'config.toml'); -const DEFAULT_PROMPT = 'Reply ONLY with OK. Do not run any commands.'; -const EXEC_TIMEOUT_MS = 120_000; -const DEFAULT_PROBE_MODELS = ['gpt-5.5', 'gpt-5.4', 'gpt-5.3-codex', 'gpt-5.2-codex', 'gpt-5-codex']; -const DEFAULT_PROBE_EFFORT = 'low'; -function asString(value) { - return typeof value === 'string' ? value : undefined; -} -function decodeJwtPayload(token) { - if (!token) - return null; - try { - const parts = token.split('.'); - if (parts.length !== 3) - return null; - const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/'); - const padded = payload.padEnd(payload.length + ((4 - (payload.length % 4)) % 4), '='); - const decoded = Buffer.from(padded, 'base64').toString('utf-8'); - return JSON.parse(decoded); - } - catch { - return null; - } -} -function getEmailFromClaims(claims) { - if (!claims) - return undefined; - if (typeof claims.email === 'string') - return claims.email; - const profile = claims['https://api.openai.com/profile']; - if (typeof profile?.email === 'string') - return profile.email; - return undefined; -} -function getAuthClaim(claims) { - if (!claims) - return undefined; - const auth = claims['https://api.openai.com/auth']; - if (!auth || typeof auth !== 'object') - return undefined; - return auth; -} -function readProbeAuthTokens(codexHome) { - const authPath = path.join(codexHome, 'auth.json'); - if (!fs.existsSync(authPath)) - return null; - let parsed; - try { - parsed = JSON.parse(fs.readFileSync(authPath, 'utf-8')); - } - catch { - return null; - } - const tokens = parsed?.tokens && typeof parsed.tokens === 'object' ? parsed.tokens : parsed; - const accessToken = asString(tokens?.access_token ?? tokens?.accessToken ?? parsed?.access_token ?? parsed?.accessToken); - const refreshToken = asString(tokens?.refresh_token ?? tokens?.refreshToken ?? parsed?.refresh_token ?? parsed?.refreshToken); - const idToken = asString(tokens?.id_token ?? tokens?.idToken ?? parsed?.id_token ?? parsed?.idToken); - const accountIdFromToken = asString(tokens?.account_id ?? tokens?.accountId ?? parsed?.account_id ?? parsed?.accountId); - if (!accessToken && !refreshToken) - return null; - const accessClaims = decodeJwtPayload(accessToken); - const idClaims = decodeJwtPayload(idToken); - const authAccess = getAuthClaim(accessClaims); - const authId = getAuthClaim(idClaims); - const accountId = accountIdFromToken || - asString(authAccess?.chatgpt_account_id) || - asString(authId?.chatgpt_account_id); - const accountUserId = asString(authAccess?.chatgpt_account_user_id) || - asString(authId?.chatgpt_account_user_id); - const userId = asString(authAccess?.user_id) || - asString(authAccess?.chatgpt_user_id) || - asString(authId?.user_id) || - asString(authId?.chatgpt_user_id); - const planType = asString(authAccess?.chatgpt_plan_type) || - asString(authId?.chatgpt_plan_type); - const email = getEmailFromClaims(idClaims) || getEmailFromClaims(accessClaims); - const exp = accessClaims?.exp ?? idClaims?.exp; - const expiresAt = typeof exp === 'number' ? exp * 1000 : undefined; - const lastRefresh = asString(parsed?.last_refresh ?? parsed?.lastRefresh); - return { - accessToken, - refreshToken, - idToken, - accountId, - accountUserId, - userId, - planType, - email, - expiresAt, - lastRefresh - }; -} -function syncAccountTokensFromProbeHome(alias, codexHome) { - const parsed = readProbeAuthTokens(codexHome); - if (!parsed?.accessToken || !parsed.refreshToken) - return; - const current = loadStore().accounts[alias]; - const tokenChanged = Boolean(current && - (current.accessToken !== parsed.accessToken || - current.refreshToken !== parsed.refreshToken || - (parsed.idToken && current.idToken !== parsed.idToken))); - const updates = { - accessToken: parsed.accessToken, - refreshToken: parsed.refreshToken, - lastSeenAt: Date.now(), - source: 'codex' - }; - if (parsed.idToken) - updates.idToken = parsed.idToken; - if (parsed.accountId) - updates.accountId = parsed.accountId; - if (parsed.accountUserId) - updates.accountUserId = parsed.accountUserId; - if (parsed.userId) - updates.userId = parsed.userId; - if (parsed.planType) - updates.planType = parsed.planType; - if (parsed.email) - updates.email = parsed.email; - if (typeof parsed.expiresAt === 'number' && Number.isFinite(parsed.expiresAt)) { - updates.expiresAt = parsed.expiresAt; - } - if (tokenChanged && parsed.lastRefresh) - updates.lastRefresh = parsed.lastRefresh; - updateAccount(alias, updates); -} -function ensureDir(dir) { - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); - } -} -function sanitizeAlias(alias) { - return alias.replace(/[^a-zA-Z0-9._-]/g, '_'); -} -function getAliasHome(alias) { - return path.join(CODEX_HOME_ROOT, sanitizeAlias(alias)); -} -function writeAuthJson(dir, account) { - if (!account.accessToken || !account.refreshToken) { - throw new Error('Missing tokens for alias'); - } - const tokens = { - access_token: account.accessToken, - refresh_token: account.refreshToken, - account_id: account.accountId - }; - if (account.idToken) { - tokens.id_token = account.idToken; - } - const auth = { - OPENAI_API_KEY: null, - tokens, - last_refresh: new Date().toISOString() - }; - const authPath = path.join(dir, 'auth.json'); - fs.writeFileSync(authPath, JSON.stringify(auth, null, 2), { mode: 0o600 }); -} -function copyConfigToml(dir) { - if (!fs.existsSync(CODEX_CONFIG_PATH)) - return; - const target = path.join(dir, 'config.toml'); - try { - fs.copyFileSync(CODEX_CONFIG_PATH, target); - } - catch { - // ignore config copy errors - } -} -export function shouldRetryWithFallback(error) { - if (!error) - return false; - const text = error.toLowerCase(); - return (text.includes('model_not_found') || - text.includes('model is not supported') || - text.includes('requested model') || - text.includes('does not exist') || - // Phase C: Handle reasoning.effort and unsupported_value errors - text.includes('unsupported_value') || - text.includes('reasoning.effort') || - text.includes('reasoning effort')); -} -// Phase C: Get probe effort from environment or default to 'low' -export function getProbeEffort() { - const envEffort = process.env.OPENCODE_MULTI_AUTH_PROBE_EFFORT; - if (envEffort && ['low', 'medium', 'high'].includes(envEffort.toLowerCase())) { - return envEffort.toLowerCase(); - } - return DEFAULT_PROBE_EFFORT; -} -export function getProbeModels() { - const raw = (process.env.OPENCODE_MULTI_AUTH_LIMITS_PROBE_MODELS || '').trim(); - const fromEnv = raw - .split(',') - .map((item) => item.trim()) - .filter(Boolean); - const candidates = fromEnv.length > 0 ? fromEnv : DEFAULT_PROBE_MODELS; - return Array.from(new Set(candidates)); -} -async function runCodexExec(codexHome, model, effort) { - return new Promise((resolve) => { - const args = [ - 'exec', - '--skip-git-repo-check', - '--cd', - codexHome, - '--sandbox', - 'read-only' - ]; - if (model) { - args.push('-m', model); - } - // Phase C: Add reasoning effort configuration - if (effort) { - args.push('-c', `model_reasoning_effort="${effort}"`); - } - args.push(DEFAULT_PROMPT); - let stderr = ''; - let stdout = ''; - const startTime = Date.now(); - const codexBin = process.env.CODEX_CLI_BIN?.trim() || 'codex'; - const child = spawn(codexBin, args, { - env: { ...process.env, CODEX_HOME: codexHome }, - stdio: ['ignore', 'pipe', 'pipe'] - }); - const timer = setTimeout(() => { - child.kill('SIGTERM'); - const durationMs = Date.now() - startTime; - resolve({ ok: false, error: 'codex exec timed out', durationMs }); - }, EXEC_TIMEOUT_MS); - child.stdout.on('data', (data) => { - stdout += data.toString(); - if (stdout.length > 4000) - stdout = stdout.slice(-4000); - }); - child.stderr.on('data', (data) => { - stderr += data.toString(); - if (stderr.length > 4000) - stderr = stderr.slice(-4000); - }); - child.on('error', (err) => { - clearTimeout(timer); - const durationMs = Date.now() - startTime; - resolve({ ok: false, error: String(err), durationMs }); - }); - child.on('close', (code) => { - clearTimeout(timer); - const durationMs = Date.now() - startTime; - if (code === 0) { - resolve({ ok: true, durationMs }); - } - else { - const message = stderr.trim() || stdout.trim() || `codex exec failed (code ${code})`; - resolve({ ok: false, error: message, durationMs }); - } - }); - }); -} -export async function probeRateLimitsForAccount(account) { - const codexHome = getAliasHome(account.alias); - ensureDir(codexHome); - writeAuthJson(codexHome, account); - copyConfigToml(codexHome); - const sessionsDir = path.join(codexHome, 'sessions'); - const probeModels = getProbeModels(); - const probeEffort = getProbeEffort(); - let lastError = 'No usable rate_limits data found in current Codex session output'; - const attemptErrors = []; - for (let idx = 0; idx < probeModels.length; idx++) { - const probeModel = probeModels[idx]; - const startedAt = Date.now(); - // Phase C: Pass effort config and track duration - const execResult = await runCodexExec(codexHome, probeModel, probeEffort); - syncAccountTokensFromProbeHome(account.alias, codexHome); - const latest = findLatestSessionRateLimits({ - sessionsDir, - sinceMs: startedAt - 5_000 - }); - // Phase C: Only accept authoritative data from successful completions - if (execResult.ok && latest?.rateLimits) { - return { - rateLimits: latest.rateLimits, - eventTs: latest.eventTs, - sourceFile: latest.sourceFile, - probeModel, - probeEffort, - probeDurationMs: execResult.durationMs, - isAuthoritative: true - }; - } - if (execResult.error) { - lastError = execResult.error; - attemptErrors.push(`[model=${probeModel}, effort=${probeEffort}] ${execResult.error}`); - } - const hasNext = idx < probeModels.length - 1; - if (!hasNext) - break; - // Phase C: Retry with fallback on unsupported_value / reasoning.effort errors - if (shouldRetryWithFallback(execResult.error)) { - // Try with 'low' effort explicitly if current effort failed - if (probeEffort !== 'low' && execResult.error?.toLowerCase().includes('reasoning')) { - const lowEffortResult = await runCodexExec(codexHome, probeModel, 'low'); - syncAccountTokensFromProbeHome(account.alias, codexHome); - const lowEffortLatest = findLatestSessionRateLimits({ - sessionsDir, - sinceMs: Date.now() - 5_000 - }); - if (lowEffortResult.ok && lowEffortLatest?.rateLimits) { - return { - rateLimits: lowEffortLatest.rateLimits, - eventTs: lowEffortLatest.eventTs, - sourceFile: lowEffortLatest.sourceFile, - probeModel, - probeEffort: 'low', - probeDurationMs: lowEffortResult.durationMs, - isAuthoritative: true - }; - } - if (lowEffortResult.error) { - attemptErrors.push(`[model=${probeModel}, effort=low] ${lowEffortResult.error}`); - } - } - continue; - } - // Don't retry if it's not a fallback-eligible error - break; - } - if (attemptErrors.length > 0) { - return { - error: attemptErrors[attemptErrors.length - 1], - isAuthoritative: false - }; - } - return { - error: lastError, - isAuthoritative: false - }; -} -export function getProbeHomeRoot() { - return CODEX_HOME_ROOT; -} -//# sourceMappingURL=probe-limits.js.map \ No newline at end of file diff --git a/dist/probe-limits.js.map b/dist/probe-limits.js.map deleted file mode 100644 index 8a4adf7..0000000 --- a/dist/probe-limits.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"probe-limits.js","sourceRoot":"","sources":["../src/probe-limits.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/rate-limits.d.ts b/dist/rate-limits.d.ts deleted file mode 100644 index effc3b8..0000000 --- a/dist/rate-limits.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { AccountRateLimits, RateLimitWindow } from './types.js'; -type RateLimitUpdate = AccountRateLimits; -export declare function hasMeaningfulRateLimitWindow(window: RateLimitWindow | undefined): boolean; -export declare function hasMeaningfulRateLimits(rateLimits: AccountRateLimits | undefined | null): boolean; -export declare function extractRateLimitUpdate(headers: Headers): RateLimitUpdate | null; -export declare function mergeRateLimits(existing: AccountRateLimits | undefined, update: RateLimitUpdate): AccountRateLimits; -export declare function parseRetryAfterHeader(retryAfter: string | null, now?: number): number | undefined; -export declare function parseRateLimitResetFromError(text: string, now?: number): number | undefined; -export declare function isRateLimitErrorText(text: string): boolean; -export declare function getBlockingRateLimitResetAt(rateLimits: AccountRateLimits | undefined, now?: number, opts?: { - conservativeWhenRemainingUnknown?: boolean; -}): number | undefined; -export {}; -//# sourceMappingURL=rate-limits.d.ts.map \ No newline at end of file diff --git a/dist/rate-limits.d.ts.map b/dist/rate-limits.d.ts.map deleted file mode 100644 index b044a77..0000000 --- a/dist/rate-limits.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"rate-limits.d.ts","sourceRoot":"","sources":["../src/rate-limits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEpE,KAAK,eAAe,GAAG,iBAAiB,CAAA;AA6DxC,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,GAAG,OAAO,CAMzF;AAED,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO,CAMjG;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CA0D/E;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,iBAAiB,GAAG,SAAS,EACvC,MAAM,EAAE,eAAe,GACtB,iBAAiB,CAQnB;AAED,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,GAAG,GAAE,MAAmB,GACvB,MAAM,GAAG,SAAS,CAapB;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,MAAmB,GACvB,MAAM,GAAG,SAAS,CAqBpB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAW1D;AAED,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,iBAAiB,GAAG,SAAS,EACzC,GAAG,GAAE,MAAmB,EACxB,IAAI,CAAC,EAAE;IAAE,gCAAgC,CAAC,EAAE,OAAO,CAAA;CAAE,GACpD,MAAM,GAAG,SAAS,CAkCpB"} \ No newline at end of file diff --git a/dist/rate-limits.js b/dist/rate-limits.js deleted file mode 100644 index bf37fe5..0000000 --- a/dist/rate-limits.js +++ /dev/null @@ -1,222 +0,0 @@ -const WINDOW_PATTERNS = [ - { key: 'fiveHour', patterns: ['5h', '5hr', '5hour', '5hours', '5-hour'] }, - { key: 'weekly', patterns: ['week', 'weekly', '1w', '7d', '7day', '7days', '7-day', '1-week'] } -]; -function parseNumber(value) { - const match = value.match(/-?\d+(?:\.\d+)?/); - if (!match) - return undefined; - const num = Number(match[0]); - if (Number.isNaN(num)) - return undefined; - return num; -} -function parseReset(value, now) { - const num = parseNumber(value); - if (num === undefined) - return undefined; - if (num > 1e12) - return num; - if (num > 1e9) - return num * 1000; - return now + num * 1000; -} -function parseTimestamp(value) { - const num = parseNumber(value); - if (num !== undefined) { - if (num > 1e12) - return num; - if (num > 1e9) - return num * 1000; - } - const parsed = Date.parse(value); - if (!Number.isNaN(parsed)) - return parsed; - return undefined; -} -function parseHumanDate(value) { - const normalized = value - .replace(/\b(\d{1,2})(st|nd|rd|th)\b/gi, '$1') - .trim(); - const parsed = Date.parse(normalized); - if (!Number.isNaN(parsed)) - return parsed; - return undefined; -} -function matchWindowKey(headerName) { - for (const entry of WINDOW_PATTERNS) { - if (entry.patterns.some((pattern) => headerName.includes(pattern))) { - return entry.key; - } - } - return null; -} -function ensureWindow(update, key, now) { - if (!update[key]) { - update[key] = { updatedAt: now }; - } - else if (!update[key]?.updatedAt) { - update[key] = { ...update[key], updatedAt: now }; - } - return update[key]; -} -export function hasMeaningfulRateLimitWindow(window) { - if (!window) - return false; - return (typeof window.remaining === 'number' || - typeof window.resetAt === 'number'); -} -export function hasMeaningfulRateLimits(rateLimits) { - if (!rateLimits) - return false; - return (hasMeaningfulRateLimitWindow(rateLimits.fiveHour) || - hasMeaningfulRateLimitWindow(rateLimits.weekly)); -} -export function extractRateLimitUpdate(headers) { - const update = {}; - const now = Date.now(); - for (const [rawName, value] of headers.entries()) { - const name = rawName.toLowerCase(); - if (name.startsWith('x-codex-')) { - if (name.startsWith('x-codex-primary-') || name.startsWith('x-codex-secondary-')) { - const windowKey = name.startsWith('x-codex-primary-') ? 'fiveHour' : 'weekly'; - const window = ensureWindow(update, windowKey, now); - if (name.endsWith('used-percent')) { - const usedPercent = parseNumber(value); - if (usedPercent !== undefined) { - const remaining = Math.max(0, 100 - usedPercent); - window.limit = 100; - window.remaining = remaining; - } - continue; - } - if (name.endsWith('reset-at')) { - const resetAt = parseTimestamp(value); - if (resetAt !== undefined) - window.resetAt = resetAt; - continue; - } - if (name.endsWith('window-minutes')) { - // Window length is informational; not shown in UI yet. - continue; - } - } - continue; - } - if (name.startsWith('x-ratelimit-')) { - const windowKey = matchWindowKey(name); - if (!windowKey) - continue; - const window = ensureWindow(update, windowKey, now); - if (name.includes('limit')) { - const limit = parseNumber(value); - if (limit !== undefined) - window.limit = limit; - continue; - } - if (name.includes('remaining')) { - const remaining = parseNumber(value); - if (remaining !== undefined) - window.remaining = remaining; - continue; - } - if (name.includes('reset')) { - const resetAt = parseReset(value, now); - if (resetAt !== undefined) - window.resetAt = resetAt; - } - } - } - return hasMeaningfulRateLimits(update) ? update : null; -} -export function mergeRateLimits(existing, update) { - if (!hasMeaningfulRateLimits(update)) { - return existing || {}; - } - return { - fiveHour: { ...(existing?.fiveHour || {}), ...(update.fiveHour || {}) }, - weekly: { ...(existing?.weekly || {}), ...(update.weekly || {}) } - }; -} -export function parseRetryAfterHeader(retryAfter, now = Date.now()) { - if (!retryAfter) - return undefined; - const trimmed = retryAfter.trim(); - if (!trimmed) - return undefined; - const asNumber = Number(trimmed); - if (Number.isFinite(asNumber) && asNumber >= 0) { - return now + asNumber * 1000; - } - const asDate = Date.parse(trimmed); - if (!Number.isNaN(asDate)) - return asDate; - return undefined; -} -export function parseRateLimitResetFromError(text, now = Date.now()) { - if (!text) - return undefined; - const retryAfterMatch = text.match(/(?:retry[\s-]*after|try again in)\s*(\d+)\s*(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h)\b/i); - if (retryAfterMatch) { - const amount = Number(retryAfterMatch[1]); - const unit = retryAfterMatch[2].toLowerCase(); - if (Number.isFinite(amount) && amount >= 0) { - if (unit.startsWith('h')) - return now + amount * 60 * 60 * 1000; - if (unit.startsWith('m')) - return now + amount * 60 * 1000; - return now + amount * 1000; - } - } - const tryAgainAtMatch = text.match(/try again at\s+([^\n.]+)/i); - if (tryAgainAtMatch?.[1]) { - const resetAt = parseHumanDate(tryAgainAtMatch[1]); - if (resetAt !== undefined) - return resetAt; - } - return undefined; -} -export function isRateLimitErrorText(text) { - if (!text) - return false; - const normalized = text.toLowerCase(); - return (normalized.includes('rate limit') || - normalized.includes('usage limit') || - normalized.includes("you've hit your usage limit") || - normalized.includes('too many requests') || - normalized.includes('try again at') || - normalized.includes('retry after')); -} -export function getBlockingRateLimitResetAt(rateLimits, now = Date.now(), opts) { - if (!rateLimits) - return undefined; - const windows = [ - rateLimits.fiveHour, - rateLimits.weekly - ]; - const exhaustedResets = []; - const futureResets = []; - let sawKnownRemaining = false; - for (const window of windows) { - if (!window || typeof window.resetAt !== 'number' || window.resetAt <= now) { - continue; - } - futureResets.push(window.resetAt); - if (typeof window.remaining === 'number') { - sawKnownRemaining = true; - if (window.remaining <= 0) { - exhaustedResets.push(window.resetAt); - } - } - } - if (exhaustedResets.length > 0) { - // If multiple windows are exhausted, wait for the last one to reset. - return Math.max(...exhaustedResets); - } - if (opts?.conservativeWhenRemainingUnknown && futureResets.length > 0 && !sawKnownRemaining) { - // Conservative fallback when backend says limited but remaining counters are absent. - return Math.max(...futureResets); - } - return undefined; -} -//# sourceMappingURL=rate-limits.js.map \ No newline at end of file diff --git a/dist/rate-limits.js.map b/dist/rate-limits.js.map deleted file mode 100644 index db050c3..0000000 --- a/dist/rate-limits.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"rate-limits.js","sourceRoot":"","sources":["../src/rate-limits.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/refresh-queue.d.ts b/dist/refresh-queue.d.ts deleted file mode 100644 index d912934..0000000 --- a/dist/refresh-queue.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { type LimitRefreshResult } from './limits-refresh.js'; -import type { AccountCredentials } from './types.js'; -export interface RefreshQueueState { - running: boolean; - startedAt: number; - finishedAt?: number; - total: number; - completed: number; - errors: number; - currentAlias?: string; - currentAliases: string[]; - active: number; - concurrency: number; - stopRequested: boolean; - stopped: boolean; - results: LimitRefreshResult[]; -} -export declare function getRefreshQueueState(): RefreshQueueState | null; -export declare function stopRefreshQueue(): void; -export declare function startRefreshQueue(accounts: AccountCredentials[], alias?: string): RefreshQueueState; -//# sourceMappingURL=refresh-queue.d.ts.map \ No newline at end of file diff --git a/dist/refresh-queue.d.ts.map b/dist/refresh-queue.d.ts.map deleted file mode 100644 index cb9b200..0000000 --- a/dist/refresh-queue.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"refresh-queue.d.ts","sourceRoot":"","sources":["../src/refresh-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAG1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAQpD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,OAAO,CAAA;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,kBAAkB,EAAE,CAAA;CAC9B;AAKD,wBAAgB,oBAAoB,IAAI,iBAAiB,GAAG,IAAI,CAE/D;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC;AA4FD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAuCnG"} \ No newline at end of file diff --git a/dist/refresh-queue.js b/dist/refresh-queue.js deleted file mode 100644 index a7c813a..0000000 --- a/dist/refresh-queue.js +++ /dev/null @@ -1,129 +0,0 @@ -import { refreshRateLimitsForAccount } from './limits-refresh.js'; -import { updateAccount } from './store.js'; -import { logInfo, logWarn } from './logger.js'; -// Keep the default below Cloudflare's per-IP burst threshold for -// /backend-api/codex/usage. The env override still allows higher concurrency. -const DEFAULT_REFRESH_QUEUE_CONCURRENCY = 5; -const MAX_REFRESH_QUEUE_CONCURRENCY = 20; -const REFRESH_QUEUE_CONCURRENCY_ENV = 'OPENCODE_MULTI_AUTH_REFRESH_QUEUE_CONCURRENCY'; -let queueState = null; -let stopRequested = false; -export function getRefreshQueueState() { - return queueState; -} -export function stopRefreshQueue() { - stopRequested = true; - if (queueState) { - queueState.stopRequested = true; - } -} -function resolveRefreshQueueConcurrency(targetCount) { - if (targetCount <= 1) { - return targetCount; - } - const raw = process.env[REFRESH_QUEUE_CONCURRENCY_ENV]; - const parsed = raw ? Number(raw) : DEFAULT_REFRESH_QUEUE_CONCURRENCY; - const normalized = Number.isFinite(parsed) ? Math.floor(parsed) : DEFAULT_REFRESH_QUEUE_CONCURRENCY; - return Math.max(1, Math.min(targetCount, Math.min(normalized, MAX_REFRESH_QUEUE_CONCURRENCY))); -} -function syncActiveAliases(activeAliases) { - if (!queueState) - return; - const currentAliases = Array.from(activeAliases.values()); - queueState.currentAliases = currentAliases; - queueState.active = currentAliases.length; - queueState.currentAlias = currentAliases[0]; -} -async function runWorker(targets, nextIndexRef, activeAliases) { - for (;;) { - if (!queueState || stopRequested) { - return; - } - const targetIndex = nextIndexRef.value; - nextIndexRef.value += 1; - if (targetIndex >= targets.length) { - return; - } - const account = targets[targetIndex]; - activeAliases.add(account.alias); - syncActiveAliases(activeAliases); - try { - const result = await refreshRateLimitsForAccount(account); - if (!queueState) { - return; - } - queueState.results.push(result); - queueState.completed += 1; - if (result.error) { - queueState.errors += 1; - } - } - finally { - activeAliases.delete(account.alias); - syncActiveAliases(activeAliases); - } - } -} -async function runQueue(targets) { - if (!queueState) - return; - const nextIndexRef = { value: 0 }; - const activeAliases = new Set(); - const workers = Array.from({ length: queueState.concurrency }, () => runWorker(targets, nextIndexRef, activeAliases)); - await Promise.all(workers); - if (queueState && stopRequested && nextIndexRef.value < targets.length) { - for (let idx = nextIndexRef.value; idx < targets.length; idx += 1) { - const account = targets[idx]; - updateAccount(account.alias, { limitStatus: 'stopped', limitError: 'Stopped by user' }); - queueState.results.push({ alias: account.alias, updated: false, error: 'Stopped' }); - queueState.completed += 1; - } - } - syncActiveAliases(activeAliases); - queueState.running = false; - queueState.finishedAt = Date.now(); - queueState.stopped = stopRequested; - queueState.stopRequested = stopRequested; - if (stopRequested) { - logWarn('Limit refresh queue stopped by user'); - } - else { - logInfo('Limit refresh queue completed'); - } - stopRequested = false; -} -export function startRefreshQueue(accounts, alias) { - if (queueState?.running) { - return queueState; - } - const targets = alias ? accounts.filter((acc) => acc.alias === alias) : accounts; - const startedAt = Date.now(); - const concurrency = resolveRefreshQueueConcurrency(targets.length); - queueState = { - running: true, - startedAt, - total: targets.length, - completed: 0, - errors: 0, - currentAliases: [], - active: 0, - concurrency, - stopRequested: false, - stopped: false, - results: [] - }; - stopRequested = false; - if (targets.length === 0) { - queueState.running = false; - queueState.finishedAt = Date.now(); - logWarn('Limit refresh queue requested with no targets'); - return queueState; - } - for (const account of targets) { - updateAccount(account.alias, { limitStatus: 'queued', limitError: undefined }); - } - logInfo(`Limit refresh queue started (${targets.length} accounts, concurrency ${concurrency})`); - void runQueue(targets); - return queueState; -} -//# sourceMappingURL=refresh-queue.js.map \ No newline at end of file diff --git a/dist/refresh-queue.js.map b/dist/refresh-queue.js.map deleted file mode 100644 index 93073de..0000000 --- a/dist/refresh-queue.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"refresh-queue.js","sourceRoot":"","sources":["../src/refresh-queue.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/rotation.d.ts b/dist/rotation.d.ts deleted file mode 100644 index e86baf4..0000000 --- a/dist/rotation.d.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { AccountCredentials, DEFAULT_CONFIG } from './types.js'; -export interface RotationResult { - account: AccountCredentials; - token: string; - forceState?: { - active: boolean; - alias: string | null; - remainingMs: number; - }; -} -export interface AccountSelectionContext { - model?: string; -} -export declare function getNextAccount(config: typeof DEFAULT_CONFIG, selection?: AccountSelectionContext): Promise; -export declare function markRateLimited(alias: string, rateLimitedUntil: number): void; -export declare function clearRateLimit(alias: string): void; -export declare function markModelUnsupported(alias: string, cooldownMs: number, info?: { - model?: string; - error?: string; -}): void; -export declare function clearModelUnsupported(alias: string): void; -export declare function markWorkspaceDeactivated(alias: string, cooldownMs: number, info?: { - error?: string; -}): void; -export declare function clearWorkspaceDeactivated(alias: string): void; -export declare function markAuthInvalid(alias: string): void; -export declare function clearAuthInvalid(alias: string): void; -//# sourceMappingURL=rotation.d.ts.map \ No newline at end of file diff --git a/dist/rotation.d.ts.map b/dist/rotation.d.ts.map deleted file mode 100644 index 040e230..0000000 --- a/dist/rotation.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"rotation.d.ts","sourceRoot":"","sources":["../src/rotation.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEpE,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,kBAAkB,CAAA;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE;QACX,MAAM,EAAE,OAAO,CAAA;QACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;QACpB,WAAW,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAkHD,wBAAsB,cAAc,CAClC,MAAM,EAAE,OAAO,cAAc,EAC7B,SAAS,CAAC,EAAE,uBAAuB,GAClC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA8OhC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAQ7E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAIlD;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,IAAI,CAWN;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAOzD;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACxB,IAAI,CASN;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAM7D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAMnD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAKpD"} \ No newline at end of file diff --git a/dist/rotation.js b/dist/rotation.js deleted file mode 100644 index 2f26dd0..0000000 --- a/dist/rotation.js +++ /dev/null @@ -1,366 +0,0 @@ -import { getStoreDiagnostics, loadStore, saveStore, updateAccount } from './store.js'; -import { ensureValidToken } from './auth.js'; -import { decodeJwtPayload, getPlanTypeFromClaims } from './codex-auth.js'; -import { isForceActive, checkAndAutoClearForce, getForceState, clearForce } from './force-mode.js'; -import { getRuntimeSettings, calculateWeightedSelection } from './settings.js'; -const HEALTH_HYSTERESIS_MS = 10_000; -const RECENT_FAILURE_WINDOW_MS = 60_000; -function shuffled(input) { - const a = [...input]; - for (let i = a.length - 1; i > 0; i -= 1) { - const j = Math.floor(Math.random() * (i + 1)); - [a[i], a[j]] = [a[j], a[i]]; - } - return a; -} -function normalizePlanType(value) { - const normalized = value?.trim().toLowerCase(); - return normalized ? normalized : undefined; -} -function getAccountPlanType(acc) { - const persisted = normalizePlanType(acc.planType); - if (persisted) - return persisted; - const idClaims = acc.idToken ? decodeJwtPayload(acc.idToken) : null; - const accessClaims = acc.accessToken ? decodeJwtPayload(acc.accessToken) : null; - return normalizePlanType(getPlanTypeFromClaims(idClaims) || getPlanTypeFromClaims(accessClaims)); -} -function isProAccount(acc) { - return getAccountPlanType(acc) === 'pro'; -} -function isSparkModel(model) { - return typeof model === 'string' && model.startsWith('gpt-5.3-codex-spark'); -} -function getPreferredPools(store, availableAliases, selection) { - const proAliases = availableAliases.filter((alias) => isProAccount(store.accounts[alias])); - const nonProAliases = availableAliases.filter((alias) => !isProAccount(store.accounts[alias])); - if (isSparkModel(selection?.model)) { - return { - primaryAliases: proAliases, - fallbackAliases: [] - }; - } - if (proAliases.length > 0) { - return { - primaryAliases: proAliases, - fallbackAliases: nonProAliases - }; - } - return { - primaryAliases: availableAliases, - fallbackAliases: [] - }; -} -function evaluateAccountHealth(acc, now) { - const wasRateLimited = !!(acc.rateLimitedUntil && acc.rateLimitedUntil > now - HEALTH_HYSTERESIS_MS); - const wasModelUnsupported = !!(acc.modelUnsupportedUntil && acc.modelUnsupportedUntil > now - HEALTH_HYSTERESIS_MS); - const wasWorkspaceDeactivated = !!(acc.workspaceDeactivatedUntil && acc.workspaceDeactivatedUntil > now - HEALTH_HYSTERESIS_MS); - // Phase D: Check if account is disabled - const isDisabled = acc.enabled === false; - const currentlyBlocked = !!(acc.rateLimitedUntil && acc.rateLimitedUntil > now) || - !!(acc.modelUnsupportedUntil && acc.modelUnsupportedUntil > now) || - !!(acc.workspaceDeactivatedUntil && acc.workspaceDeactivatedUntil > now) || - !!acc.authInvalid || - isDisabled; // Phase D: Exclude disabled accounts - const isInProbation = !currentlyBlocked && (wasRateLimited || wasModelUnsupported || wasWorkspaceDeactivated); - let recentFailures = 0; - if (acc.lastLimitErrorAt && acc.lastLimitErrorAt > now - RECENT_FAILURE_WINDOW_MS) { - recentFailures++; - } - if (acc.authInvalidatedAt && acc.authInvalidatedAt > now - RECENT_FAILURE_WINDOW_MS) { - recentFailures++; - } - let priority = 100; - if (isInProbation) - priority -= 30; - if (recentFailures > 0) - priority -= recentFailures * 10; - if (acc.usageCount === 0) - priority -= 5; - if (currentlyBlocked) - priority = 0; - // Phase D: Disabled accounts get lowest priority - if (isDisabled) - priority = -1; - return { - alias: acc.alias, - isHealthy: !currentlyBlocked && !acc.authInvalid && !isDisabled, - isInProbation, - recentFailures, - priority - }; -} -export async function getNextAccount(config, selection) { - // Phase E: Check and auto-clear expired/invalid force state - const autoClear = checkAndAutoClearForce(); - if (autoClear.wasCleared) { - console.log(`[multi-auth] Force mode auto-cleared: ${autoClear.reason}`); - } - // Phase E: Check if force mode is active - const forceActive = isForceActive(); - const forceState = getForceState(); - let store = loadStore(); - const aliases = Object.keys(store.accounts); - if (aliases.length === 0) { - const diag = getStoreDiagnostics(); - const extra = diag.error ? ` (${diag.error})` : ''; - console.error(`[multi-auth] No accounts configured. Run: opencode-multi-auth add ${extra}`); - if (process.env.OPENCODE_MULTI_AUTH_DEBUG === '1') { - console.error(`[multi-auth] store file: ${diag.storeFile}`); - } - return null; - } - const now = Date.now(); - // Phase E: If force mode is active, never fall back to another alias. - if (forceActive && forceState.forcedAlias) { - const forcedAlias = forceState.forcedAlias; - const forcedAccount = store.accounts[forcedAlias]; - if (forcedAccount) { - const health = evaluateAccountHealth(forcedAccount, now); - if (health.isHealthy) { - const token = await ensureValidToken(forcedAlias); - if (token) { - store = updateAccount(forcedAlias, { - usageCount: (forcedAccount.usageCount || 0) + 1, - lastUsed: now, - limitError: undefined - }); - store.activeAlias = forcedAlias; - store.lastRotation = now; - saveStore(store); - console.log(`[multi-auth] Force mode: using ${forcedAlias}`); - return { - account: store.accounts[forcedAlias], - token, - forceState: { - active: true, - alias: forcedAlias, - remainingMs: forceState.forcedUntil ? forceState.forcedUntil - now : 0 - } - }; - } - else { - console.warn(`[multi-auth] Force mode: ${forcedAlias} token unavailable; refusing fallback`); - return null; - } - } - else { - console.warn(`[multi-auth] Force mode: ${forcedAlias} currently blocked; refusing fallback`); - return null; - } - } - else { - // Forced account no longer exists - clear force and proceed normally. - console.warn(`[multi-auth] Force mode: ${forcedAlias} not found, clearing force`); - clearForce(); - } - } - const healthMap = new Map(); - for (const alias of aliases) { - const acc = store.accounts[alias]; - healthMap.set(alias, evaluateAccountHealth(acc, now)); - } - const availableAliases = aliases.filter(alias => { - const health = healthMap.get(alias); - return health?.isHealthy === true; - }); - if (availableAliases.length === 0) { - console.warn('[multi-auth] No available accounts (rate-limited or invalidated).'); - return null; - } - const tokenFailureCooldownMs = (() => { - const raw = process.env.OPENCODE_MULTI_AUTH_TOKEN_FAILURE_COOLDOWN_MS; - const parsed = raw ? Number(raw) : NaN; - if (Number.isFinite(parsed) && parsed > 0) - return parsed; - return 60_000; - })(); - const runtimeSettings = getRuntimeSettings(); - const rotationStrategy = runtimeSettings.settings.rotationStrategy || config.rotationStrategy; - const buildCandidates = (candidateAliases) => { - switch (rotationStrategy) { - case 'least-used': { - const sorted = [...candidateAliases].sort((a, b) => { - const aa = store.accounts[a]; - const bb = store.accounts[b]; - const healthA = healthMap.get(a); - const healthB = healthMap.get(b); - const priorityDiff = (healthB?.priority || 0) - (healthA?.priority || 0); - if (priorityDiff !== 0) - return priorityDiff; - const usageDiff = (aa?.usageCount || 0) - (bb?.usageCount || 0); - if (usageDiff !== 0) - return usageDiff; - const lastDiff = (aa?.lastUsed || 0) - (bb?.lastUsed || 0); - if (lastDiff !== 0) - return lastDiff; - return a.localeCompare(b); - }); - return { aliases: sorted }; - } - case 'random': { - const sorted = [...candidateAliases].sort((a, b) => { - const healthA = healthMap.get(a); - const healthB = healthMap.get(b); - return (healthB?.priority || 0) - (healthA?.priority || 0); - }); - const topPriority = sorted.slice(0, Math.ceil(sorted.length / 2)); - return { aliases: shuffled(topPriority.length > 0 ? topPriority : sorted) }; - } - // Phase F: Weighted round-robin - case 'weighted-round-robin': { - const weights = runtimeSettings.settings.accountWeights; - // Filter to healthy accounts with weights - const weightedAliases = candidateAliases.filter(alias => (weights[alias] || 0) > 0); - if (weightedAliases.length === 0) { - // Fallback to round-robin if no weights defined - const sorted = [...candidateAliases].sort((a, b) => { - const healthA = healthMap.get(a); - const healthB = healthMap.get(b); - return (healthB?.priority || 0) - (healthA?.priority || 0); - }); - const start = store.rotationIndex % sorted.length; - const rr = sorted.map((_, i) => sorted[(start + i) % sorted.length]); - const nextIndex = (selected) => { - const idx = sorted.indexOf(selected); - if (idx < 0) - return store.rotationIndex; - return (idx + 1) % sorted.length; - }; - return { aliases: rr, nextIndex }; - } - // Use weighted selection - const selected = calculateWeightedSelection(weightedAliases, weights); - if (!selected) { - // Fallback to round-robin - const sorted = [...candidateAliases].sort((a, b) => { - const healthA = healthMap.get(a); - const healthB = healthMap.get(b); - return (healthB?.priority || 0) - (healthA?.priority || 0); - }); - const start = store.rotationIndex % sorted.length; - const rr = sorted.map((_, i) => sorted[(start + i) % sorted.length]); - return { aliases: rr }; - } - return { aliases: [selected] }; - } - case 'round-robin': - default: { - const sorted = [...candidateAliases].sort((a, b) => { - const healthA = healthMap.get(a); - const healthB = healthMap.get(b); - return (healthB?.priority || 0) - (healthA?.priority || 0); - }); - const start = store.rotationIndex % sorted.length; - const rr = sorted.map((_, i) => sorted[(start + i) % sorted.length]); - const nextIndex = (selected) => { - const idx = sorted.indexOf(selected); - if (idx < 0) - return store.rotationIndex; - return (idx + 1) % sorted.length; - }; - return { aliases: rr, nextIndex }; - } - } - }; - const { primaryAliases, fallbackAliases } = getPreferredPools(store, availableAliases, selection); - const primary = buildCandidates(primaryAliases); - const fallback = fallbackAliases.length > 0 ? buildCandidates(fallbackAliases) : { aliases: [] }; - const candidates = [...primary.aliases, ...fallback.aliases]; - for (const candidate of candidates) { - const token = await ensureValidToken(candidate); - if (!token) { - store = updateAccount(candidate, { - rateLimitedUntil: now + tokenFailureCooldownMs, - limitError: '[multi-auth] Token unavailable (refresh failed?)', - lastLimitErrorAt: now - }); - continue; - } - store = updateAccount(candidate, { - usageCount: (store.accounts[candidate]?.usageCount || 0) + 1, - lastUsed: now, - limitError: undefined - }); - store.activeAlias = candidate; - store.lastRotation = now; - const nextIndex = primary.aliases.includes(candidate) ? primary.nextIndex : fallback.nextIndex; - if (nextIndex) { - store.rotationIndex = nextIndex(candidate); - } - saveStore(store); - const currentForceState = getForceState(); - return { - account: store.accounts[candidate], - token, - forceState: { - active: isForceActive(), - alias: currentForceState.forcedAlias, - remainingMs: currentForceState.forcedUntil ? currentForceState.forcedUntil - now : 0 - } - }; - } - console.error('[multi-auth] No available accounts (token refresh failed on all candidates).'); - return null; -} -export function markRateLimited(alias, rateLimitedUntil) { - const now = Date.now(); - const safeUntil = Math.max(rateLimitedUntil, now + 1000); - const seconds = Math.max(1, Math.ceil((safeUntil - now) / 1000)); - updateAccount(alias, { - rateLimitedUntil: safeUntil - }); - console.warn(`[multi-auth] Account ${alias} marked rate-limited for ${seconds}s`); -} -export function clearRateLimit(alias) { - updateAccount(alias, { - rateLimitedUntil: undefined - }); -} -export function markModelUnsupported(alias, cooldownMs, info) { - updateAccount(alias, { - modelUnsupportedUntil: Date.now() + cooldownMs, - modelUnsupportedAt: Date.now(), - modelUnsupportedModel: info?.model, - modelUnsupportedError: info?.error - }); - const extra = info?.model ? ` (model=${info.model})` : ''; - console.warn(`[multi-auth] Account ${alias} marked model-unsupported for ${cooldownMs / 1000}s${extra}`); -} -export function clearModelUnsupported(alias) { - updateAccount(alias, { - modelUnsupportedUntil: undefined, - modelUnsupportedAt: undefined, - modelUnsupportedModel: undefined, - modelUnsupportedError: undefined - }); -} -export function markWorkspaceDeactivated(alias, cooldownMs, info) { - updateAccount(alias, { - workspaceDeactivatedUntil: Date.now() + cooldownMs, - workspaceDeactivatedAt: Date.now(), - workspaceDeactivatedError: info?.error - }); - console.warn(`[multi-auth] Account ${alias} marked workspace-deactivated for ${cooldownMs / 1000}s`); -} -export function clearWorkspaceDeactivated(alias) { - updateAccount(alias, { - workspaceDeactivatedUntil: undefined, - workspaceDeactivatedAt: undefined, - workspaceDeactivatedError: undefined - }); -} -export function markAuthInvalid(alias) { - updateAccount(alias, { - authInvalid: true, - authInvalidatedAt: Date.now() - }); - console.warn(`[multi-auth] Account ${alias} marked invalidated`); -} -export function clearAuthInvalid(alias) { - updateAccount(alias, { - authInvalid: false, - authInvalidatedAt: undefined - }); -} -//# sourceMappingURL=rotation.js.map \ No newline at end of file diff --git a/dist/rotation.js.map b/dist/rotation.js.map deleted file mode 100644 index 95f584e..0000000 --- a/dist/rotation.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"rotation.js","sourceRoot":"","sources":["../src/rotation.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/sessions-limits.d.ts b/dist/sessions-limits.d.ts deleted file mode 100644 index 01aad89..0000000 --- a/dist/sessions-limits.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { AccountRateLimits } from './types.js'; -export declare function findLatestSessionRateLimits(options?: { - sinceMs?: number; - untilMs?: number; - sessionsDir?: string; -}): { - rateLimits: AccountRateLimits; - eventTs?: number; - sourceFile: string; -} | null; -//# sourceMappingURL=sessions-limits.d.ts.map \ No newline at end of file diff --git a/dist/sessions-limits.d.ts.map b/dist/sessions-limits.d.ts.map deleted file mode 100644 index 4a45fdc..0000000 --- a/dist/sessions-limits.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"sessions-limits.d.ts","sourceRoot":"","sources":["../src/sessions-limits.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAmB,MAAM,YAAY,CAAA;AAuIpE,wBAAgB,2BAA2B,CAAC,OAAO,CAAC,EAAE;IACpD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,GAAG;IACF,UAAU,EAAE,iBAAiB,CAAA;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB,GAAG,IAAI,CAcP"} \ No newline at end of file diff --git a/dist/sessions-limits.js b/dist/sessions-limits.js deleted file mode 100644 index a3f8ae9..0000000 --- a/dist/sessions-limits.js +++ /dev/null @@ -1,128 +0,0 @@ -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import * as os from 'node:os'; -import { hasMeaningfulRateLimits } from './rate-limits.js'; -const DEFAULT_SESSIONS_DIR = path.join(os.homedir(), '.codex', 'sessions'); -function toEpochMs(value) { - if (value === undefined) - return undefined; - if (value > 1e12) - return value; - if (value > 1e9) - return value * 1000; - return value * 1000; -} -function parseTimestamp(value) { - if (!value) - return undefined; - const parsed = Date.parse(value); - if (Number.isNaN(parsed)) - return undefined; - return parsed; -} -function isWeekly(limit) { - const minutes = limit?.window_minutes; - if (!minutes) - return false; - return minutes >= 1000; -} -function buildWindow(limit, eventTs) { - if (!limit) - return undefined; - const used = limit.used_percent; - const remaining = typeof used === 'number' ? Math.max(0, 100 - used) : undefined; - const resetAt = toEpochMs(limit.resets_at ?? limit.reset_at) ?? - (typeof limit.resets_in_seconds === 'number' && eventTs - ? eventTs + limit.resets_in_seconds * 1000 - : undefined); - return { - limit: 100, - remaining, - resetAt, - updatedAt: eventTs - }; -} -function listSessionFiles(dir) { - const entries = []; - if (!fs.existsSync(dir)) - return entries; - const stack = [dir]; - while (stack.length > 0) { - const current = stack.pop(); - if (!current) - continue; - const items = fs.readdirSync(current, { withFileTypes: true }); - for (const item of items) { - const fullPath = path.join(current, item.name); - if (item.isDirectory()) { - stack.push(fullPath); - } - else if (item.isFile() && item.name.endsWith('.jsonl')) { - const stat = fs.statSync(fullPath); - entries.push({ path: fullPath, mtimeMs: stat.mtimeMs }); - } - } - } - return entries.sort((a, b) => b.mtimeMs - a.mtimeMs); -} -function parseLatestTokenCountFromFile(filePath, options) { - let contents = ''; - try { - contents = fs.readFileSync(filePath, 'utf-8'); - } - catch { - return null; - } - const lines = contents.split('\n').filter(Boolean); - for (let i = lines.length - 1; i >= 0; i -= 1) { - let parsed = null; - try { - parsed = JSON.parse(lines[i]); - } - catch { - continue; - } - const payload = parsed?.payload; - if (payload?.type !== 'token_count') - continue; - const eventTs = parseTimestamp(parsed.timestamp); - if (!eventTs) - continue; - if (options?.sinceMs && eventTs < options.sinceMs) - continue; - if (options?.untilMs && eventTs > options.untilMs) - continue; - const primary = payload.rate_limits?.primary; - const secondary = payload.rate_limits?.secondary; - const fiveHour = isWeekly(primary) ? secondary : primary; - const weekly = isWeekly(primary) ? primary : secondary; - const rateLimits = { - fiveHour: buildWindow(fiveHour, eventTs), - weekly: buildWindow(weekly, eventTs) - }; - if (!hasMeaningfulRateLimits(rateLimits)) { - continue; - } - return { - eventTs, - rateLimits - }; - } - return null; -} -export function findLatestSessionRateLimits(options) { - const sessionsDir = options?.sessionsDir || DEFAULT_SESSIONS_DIR; - const files = listSessionFiles(sessionsDir); - for (const file of files) { - const parsed = parseLatestTokenCountFromFile(file.path, options); - if (parsed?.rateLimits) { - return { - rateLimits: parsed.rateLimits, - eventTs: parsed.eventTs, - sourceFile: file.path - }; - } - } - return null; -} -//# sourceMappingURL=sessions-limits.js.map \ No newline at end of file diff --git a/dist/sessions-limits.js.map b/dist/sessions-limits.js.map deleted file mode 100644 index 4f97fc3..0000000 --- a/dist/sessions-limits.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"sessions-limits.js","sourceRoot":"","sources":["../src/sessions-limits.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/settings.d.ts b/dist/settings.d.ts deleted file mode 100644 index 0815ebe..0000000 --- a/dist/settings.d.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { type RotationSettings, type WeightPreset, type SettingsValidationError } from './types.js'; -export interface SettingsResult { - settings: RotationSettings; - source: 'default' | 'persisted' | 'runtime' | 'env'; - errors?: SettingsValidationError[]; -} -export declare function getSettings(): SettingsResult; -export declare function getRuntimeSettings(): SettingsResult; -export declare function updateSettings(updates: Partial, actor?: string): { - success: boolean; - settings?: RotationSettings; - errors?: SettingsValidationError[]; -}; -export declare function resetSettings(actor?: string): RotationSettings; -export declare function applyPreset(preset: WeightPreset, actor?: string): { - success: boolean; - settings?: RotationSettings; - errors?: SettingsValidationError[]; -}; -export declare function calculateWeightedSelection(aliases: string[], weights: Record): string | null; -export declare function getSettingsWithInfo(): { - settings: RotationSettings; - source: string; - preset?: WeightPreset; - canReset: boolean; -}; -export declare function isFeatureEnabled(flag: keyof NonNullable): boolean; -//# sourceMappingURL=settings.d.ts.map \ No newline at end of file diff --git a/dist/settings.d.ts.map b/dist/settings.d.ts.map deleted file mode 100644 index 46ef79d..0000000 --- a/dist/settings.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC7B,MAAM,YAAY,CAAA;AAInB,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,KAAK,CAAA;IACnD,MAAM,CAAC,EAAE,uBAAuB,EAAE,CAAA;CACnC;AAmED,wBAAgB,WAAW,IAAI,cAAc,CAE5C;AAGD,wBAAgB,kBAAkB,IAAI,cAAc,CAEnD;AAGD,wBAAgB,cAAc,CAC5B,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,EAClC,KAAK,GAAE,MAAiB,GACvB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAAC,MAAM,CAAC,EAAE,uBAAuB,EAAE,CAAA;CAAE,CA2BvF;AAGD,wBAAgB,aAAa,CAAC,KAAK,GAAE,MAAiB,GAAG,gBAAgB,CAQxE;AAGD,wBAAgB,WAAW,CACzB,MAAM,EAAE,YAAY,EACpB,KAAK,GAAE,MAAiB,GACvB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAAC,MAAM,CAAC,EAAE,uBAAuB,EAAE,CAAA;CAAE,CAyDvF;AAGD,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,GAAG,IAAI,CAuBf;AAGD,wBAAgB,mBAAmB,IAAI;IACrC,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,QAAQ,EAAE,OAAO,CAAA;CAClB,CAwBA;AAGD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,WAAW,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC,GAAG,OAAO,CAGnG"} \ No newline at end of file diff --git a/dist/settings.js b/dist/settings.js deleted file mode 100644 index 4eea87a..0000000 --- a/dist/settings.js +++ /dev/null @@ -1,208 +0,0 @@ -import { loadStore, saveStore } from './store.js'; -import { logInfo, logError } from './logger.js'; -import { DEFAULT_ROTATION_SETTINGS, WEIGHTED_PRESETS, validateSettings } from './types.js'; -function resolveSettings(includeEnvOverrides) { - const store = loadStore(); - // Start with defaults - let settings = { ...DEFAULT_ROTATION_SETTINGS }; - let source = 'default'; - // Layer 1: Persisted settings from store - if (store.settings) { - settings = { - ...settings, - ...store.settings - }; - source = 'persisted'; - } - // Layer 2: Environment variables override (optional for runtime behavior) - if (includeEnvOverrides) { - const envStrategy = process.env.OPENCODE_MULTI_AUTH_ROTATION_STRATEGY; - if (envStrategy && ['round-robin', 'least-used', 'random', 'weighted-round-robin'].includes(envStrategy)) { - settings.rotationStrategy = envStrategy; - source = 'env'; - } - const envCriticalThreshold = process.env.OPENCODE_MULTI_AUTH_CRITICAL_THRESHOLD; - if (envCriticalThreshold) { - const parsed = parseFloat(envCriticalThreshold); - if (!isNaN(parsed) && parsed >= 0 && parsed <= 100) { - settings.criticalThreshold = parsed; - source = 'env'; - } - } - const envLowThreshold = process.env.OPENCODE_MULTI_AUTH_LOW_THRESHOLD; - if (envLowThreshold) { - const parsed = parseFloat(envLowThreshold); - if (!isNaN(parsed) && parsed >= 0 && parsed <= 100) { - settings.lowThreshold = parsed; - source = 'env'; - } - } - // Phase G: Feature flag environment overrides - const envAntigravity = process.env.OPENCODE_MULTI_AUTH_ANTIGRAVITY_ENABLED; - if (envAntigravity) { - const enabled = envAntigravity.toLowerCase() === 'true' || envAntigravity === '1'; - settings.featureFlags = { - ...(settings.featureFlags || {}), - antigravityEnabled: enabled - }; - source = 'env'; - } - } - // Validate final settings - const errors = validateSettings(settings); - if (errors.length > 0) { - logError(`Settings validation errors: ${errors.map(e => e.message).join(', ')}`); - } - return { settings, source, errors: errors.length > 0 ? errors : undefined }; -} -// Phase F: Get settings with proper precedence (including env overrides) -export function getSettings() { - return resolveSettings(true); -} -// Runtime behavior should use persisted settings so dashboard changes take effect immediately. -export function getRuntimeSettings() { - return resolveSettings(false); -} -// Phase F: Update settings with validation -export function updateSettings(updates, actor = 'system') { - const current = getRuntimeSettings(); - // Merge updates with current settings - const newSettings = { - ...current.settings, - ...updates, - updatedAt: Date.now(), - updatedBy: actor - }; - // Validate new settings - const errors = validateSettings(newSettings); - if (errors.length > 0) { - logError(`Settings update failed validation: ${errors.map(e => e.message).join(', ')}`); - return { success: false, errors }; - } - // Save to store - const store = loadStore(); - store.settings = newSettings; - // Keep legacy field in sync for force-mode compatibility. - store.rotationStrategy = newSettings.rotationStrategy; - saveStore(store); - logInfo(`Settings updated by ${actor}: ${JSON.stringify(updates)}`); - return { success: true, settings: newSettings }; -} -// Phase F: Reset settings to defaults -export function resetSettings(actor = 'system') { - const store = loadStore(); - delete store.settings; - store.rotationStrategy = DEFAULT_ROTATION_SETTINGS.rotationStrategy; - saveStore(store); - logInfo(`Settings reset to defaults by ${actor}`); - return { ...DEFAULT_ROTATION_SETTINGS }; -} -// Phase F: Apply a preset -export function applyPreset(preset, actor = 'system') { - const store = loadStore(); - const accounts = Object.keys(store.accounts); - const presetConfig = WEIGHTED_PRESETS[preset]; - let accountWeights = {}; - if (preset === 'balanced') { - // Equal weights for all accounts - const weight = accounts.length > 0 ? 1 / accounts.length : 0; - accounts.forEach(alias => { - accountWeights[alias] = weight; - }); - } - else if (preset === 'conservative') { - // Weights based on limit health - accounts.forEach(alias => { - const account = store.accounts[alias]; - const fiveHourRemaining = account.rateLimits?.fiveHour?.remaining ?? 50; - const weeklyRemaining = account.rateLimits?.weekly?.remaining ?? 50; - const health = (fiveHourRemaining + weeklyRemaining) / 2; - accountWeights[alias] = health / 100; - }); - // Normalize to sum to 1 - const total = Object.values(accountWeights).reduce((sum, w) => sum + w, 0); - if (total > 0) { - accounts.forEach(alias => { - accountWeights[alias] = accountWeights[alias] / total; - }); - } - } - else if (preset === 'aggressive') { - // Favor accounts with high usage (lower remaining) - accounts.forEach(alias => { - const account = store.accounts[alias]; - const fiveHourRemaining = account.rateLimits?.fiveHour?.remaining ?? 50; - const weeklyRemaining = account.rateLimits?.weekly?.remaining ?? 50; - const health = (fiveHourRemaining + weeklyRemaining) / 2; - // Inverse: lower health = higher weight - accountWeights[alias] = (100 - health) / 100; - }); - // Normalize to sum to 1 - const total = Object.values(accountWeights).reduce((sum, w) => sum + w, 0); - if (total > 0) { - accounts.forEach(alias => { - accountWeights[alias] = accountWeights[alias] / total; - }); - } - } - const updates = { - rotationStrategy: 'weighted-round-robin', - criticalThreshold: presetConfig.thresholds.critical, - lowThreshold: presetConfig.thresholds.low, - accountWeights - }; - return updateSettings(updates, actor); -} -// Phase F: Calculate weighted selection -export function calculateWeightedSelection(aliases, weights) { - if (aliases.length === 0) - return null; - // Filter to only available aliases - const available = aliases.filter(alias => weights[alias] > 0); - if (available.length === 0) - return null; - // Calculate total weight - const totalWeight = available.reduce((sum, alias) => sum + (weights[alias] || 0), 0); - if (totalWeight === 0) - return null; - // Weighted random selection - let random = Math.random() * totalWeight; - for (const alias of available) { - random -= weights[alias] || 0; - if (random <= 0) { - return alias; - } - } - // Fallback to last - return available[available.length - 1]; -} -// Phase F: Get settings with environment info -export function getSettingsWithInfo() { - const result = getSettings(); - const store = loadStore(); - // Detect if using a preset - let preset; - if (result.settings.rotationStrategy === 'weighted-round-robin') { - // Check if weights match a preset pattern - for (const [presetName, config] of Object.entries(WEIGHTED_PRESETS)) { - if (presetName !== 'custom' && - Math.abs(result.settings.criticalThreshold - config.thresholds.critical) < 0.01 && - Math.abs(result.settings.lowThreshold - config.thresholds.low) < 0.01) { - preset = presetName; - break; - } - } - } - return { - settings: result.settings, - source: result.source, - preset, - canReset: !!store.settings - }; -} -// Phase G: Check if a feature flag is enabled -export function isFeatureEnabled(flag) { - const settings = getSettings(); - return settings.settings.featureFlags?.[flag] ?? false; -} -//# sourceMappingURL=settings.js.map \ No newline at end of file diff --git a/dist/settings.js.map b/dist/settings.js.map deleted file mode 100644 index 08fc48d..0000000 --- a/dist/settings.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/store.d.ts b/dist/store.d.ts deleted file mode 100644 index 1fe1ab5..0000000 --- a/dist/store.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { AccountStore, AccountCredentials } from './types.js'; -export declare function loadStore(): AccountStore; -export declare function saveStore(store: AccountStore): void; -export declare function withWriteLock(fn: () => T): Promise; -export declare function getStoreDiagnostics(): { - storeDir: string; - storeFile: string; - locked: boolean; - encrypted: boolean; - error: string | null; -}; -export declare function addAccount(alias: string, creds: Omit): AccountStore; -export declare function removeAccount(alias: string): AccountStore; -export declare function updateAccount(alias: string, updates: Partial): AccountStore; -export declare function setActiveAlias(alias: string | null): AccountStore; -export declare function getActiveAccount(): AccountCredentials | null; -export declare function listAccounts(): AccountCredentials[]; -export declare function getStorePath(): string; -export declare function getStoreStatus(): { - locked: boolean; - encrypted: boolean; - error: string | null; -}; -//# sourceMappingURL=store.d.ts.map \ No newline at end of file diff --git a/dist/store.d.ts.map b/dist/store.d.ts.map deleted file mode 100644 index 0cd9bfc..0000000 --- a/dist/store.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAGnB,MAAM,YAAY,CAAA;AAoVnB,wBAAgB,SAAS,IAAI,YAAY,CAsExC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAiFnD;AAED,wBAAsB,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAO9D;AAED,wBAAgB,mBAAmB,IAAI;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB,CAQA;AAGD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,YAAY,CAAC,GAAG,YAAY,CAc/G;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CASzD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,YAAY,CAe/F;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,CA+BjE;AAED,wBAAgB,gBAAgB,IAAI,kBAAkB,GAAG,IAAI,CAI5D;AAED,wBAAgB,YAAY,IAAI,kBAAkB,EAAE,CAGnD;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,cAAc,IAAI;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAG9F"} \ No newline at end of file diff --git a/dist/store.js b/dist/store.js deleted file mode 100644 index ce9f496..0000000 --- a/dist/store.js +++ /dev/null @@ -1,545 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import * as os from 'os'; -import * as crypto from 'node:crypto'; -import { hasMeaningfulRateLimits } from './rate-limits.js'; -const STORE_DIR_ENV = 'OPENCODE_MULTI_AUTH_STORE_DIR'; -const STORE_FILE_ENV = 'OPENCODE_MULTI_AUTH_STORE_FILE'; -const DEFAULT_STORE_DIR = path.join(os.homedir(), '.config', 'opencode-multi-auth'); -const DEFAULT_STORE_FILE = 'accounts.json'; -function getStoreDir() { - const override = process.env[STORE_DIR_ENV]; - if (override && override.trim()) - return path.resolve(override.trim()); - return DEFAULT_STORE_DIR; -} -function getStoreFile() { - const override = process.env[STORE_FILE_ENV]; - if (override && override.trim()) - return path.resolve(override.trim()); - return path.join(getStoreDir(), DEFAULT_STORE_FILE); -} -const STORE_ENV_PASSPHRASE = 'CODEX_SOFT_STORE_PASSPHRASE'; -const CURRENT_STORE_VERSION = 2; -let storeLocked = false; -let lastStoreError = null; -let lastStoreEncrypted = false; -let writeLock = false; -let writeLockQueue = []; -function ensureDir() { - const dir = getStoreDir(); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); - } -} -function emptyStore() { - return { - version: CURRENT_STORE_VERSION, - accounts: {}, - activeAlias: null, - rotationIndex: 0, - lastRotation: Date.now() - }; -} -function getPassphrase() { - const value = process.env[STORE_ENV_PASSPHRASE]; - return value && value.trim().length > 0 ? value : null; -} -function isEncryptedFile(payload) { - return Boolean(payload && payload.encrypted === true && typeof payload.data === 'string'); -} -function deriveKey(passphrase, salt) { - return crypto.scryptSync(passphrase, salt, 32); -} -function encryptStore(store, passphrase) { - const salt = crypto.randomBytes(16); - const iv = crypto.randomBytes(12); - const key = deriveKey(passphrase, salt); - const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); - const serialized = JSON.stringify(store); - const encrypted = Buffer.concat([cipher.update(serialized, 'utf8'), cipher.final()]); - const tag = cipher.getAuthTag(); - return { - encrypted: true, - version: CURRENT_STORE_VERSION, - salt: salt.toString('base64'), - iv: iv.toString('base64'), - tag: tag.toString('base64'), - data: encrypted.toString('base64') - }; -} -function decryptStore(file, passphrase) { - const salt = Buffer.from(file.salt, 'base64'); - const iv = Buffer.from(file.iv, 'base64'); - const tag = Buffer.from(file.tag, 'base64'); - const data = Buffer.from(file.data, 'base64'); - const key = deriveKey(passphrase, salt); - const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv); - decipher.setAuthTag(tag); - const decrypted = Buffer.concat([decipher.update(data), decipher.final()]).toString('utf8'); - return JSON.parse(decrypted); -} -function validateAccount(acc, alias) { - if (!acc || typeof acc !== 'object') - return null; - if (typeof acc.accessToken !== 'string' || !acc.accessToken) - return null; - if (typeof acc.refreshToken !== 'string' || !acc.refreshToken) - return null; - if (typeof acc.expiresAt !== 'number') - return null; - const rateLimitHistory = Array.isArray(acc.rateLimitHistory) - ? acc.rateLimitHistory.filter((entry) => hasMeaningfulRateLimits({ fiveHour: entry?.fiveHour, weekly: entry?.weekly })) - : undefined; - const rateLimits = hasMeaningfulRateLimits(acc.rateLimits) ? acc.rateLimits : undefined; - return { - alias, - accessToken: acc.accessToken, - refreshToken: acc.refreshToken, - idToken: typeof acc.idToken === 'string' ? acc.idToken : undefined, - accountId: typeof acc.accountId === 'string' ? acc.accountId : undefined, - accountUserId: typeof acc.accountUserId === 'string' ? acc.accountUserId : undefined, - userId: typeof acc.userId === 'string' ? acc.userId : undefined, - planType: typeof acc.planType === 'string' ? acc.planType : undefined, - expiresAt: acc.expiresAt, - email: typeof acc.email === 'string' ? acc.email : undefined, - lastRefresh: typeof acc.lastRefresh === 'string' ? acc.lastRefresh : undefined, - lastSeenAt: typeof acc.lastSeenAt === 'number' ? acc.lastSeenAt : undefined, - lastActiveUntil: typeof acc.lastActiveUntil === 'number' ? acc.lastActiveUntil : undefined, - lastUsed: typeof acc.lastUsed === 'number' ? acc.lastUsed : undefined, - usageCount: typeof acc.usageCount === 'number' ? acc.usageCount : 0, - rateLimitedUntil: typeof acc.rateLimitedUntil === 'number' ? acc.rateLimitedUntil : undefined, - modelUnsupportedUntil: typeof acc.modelUnsupportedUntil === 'number' ? acc.modelUnsupportedUntil : undefined, - modelUnsupportedAt: typeof acc.modelUnsupportedAt === 'number' ? acc.modelUnsupportedAt : undefined, - modelUnsupportedModel: typeof acc.modelUnsupportedModel === 'string' ? acc.modelUnsupportedModel : undefined, - modelUnsupportedError: typeof acc.modelUnsupportedError === 'string' ? acc.modelUnsupportedError : undefined, - workspaceDeactivatedUntil: typeof acc.workspaceDeactivatedUntil === 'number' ? acc.workspaceDeactivatedUntil : undefined, - workspaceDeactivatedAt: typeof acc.workspaceDeactivatedAt === 'number' ? acc.workspaceDeactivatedAt : undefined, - workspaceDeactivatedError: typeof acc.workspaceDeactivatedError === 'string' ? acc.workspaceDeactivatedError : undefined, - authInvalid: typeof acc.authInvalid === 'boolean' ? acc.authInvalid : undefined, - authInvalidatedAt: typeof acc.authInvalidatedAt === 'number' ? acc.authInvalidatedAt : undefined, - // Phase D: Account availability fields - enabled: typeof acc.enabled === 'boolean' ? acc.enabled : undefined, - disabledAt: typeof acc.disabledAt === 'number' ? acc.disabledAt : undefined, - disabledBy: typeof acc.disabledBy === 'string' ? acc.disabledBy : undefined, - disableReason: typeof acc.disableReason === 'string' ? acc.disableReason : undefined, - rateLimits, - rateLimitHistory: rateLimitHistory && rateLimitHistory.length > 0 ? rateLimitHistory : undefined, - limitStatus: typeof acc.limitStatus === 'string' ? acc.limitStatus : undefined, - limitError: typeof acc.limitError === 'string' ? acc.limitError : undefined, - lastLimitProbeAt: typeof acc.lastLimitProbeAt === 'number' ? acc.lastLimitProbeAt : undefined, - lastLimitErrorAt: typeof acc.lastLimitErrorAt === 'number' ? acc.lastLimitErrorAt : undefined, - limitsConfidence: acc.limitsConfidence === 'fresh' || - acc.limitsConfidence === 'stale' || - acc.limitsConfidence === 'error' || - acc.limitsConfidence === 'unknown' - ? acc.limitsConfidence - : undefined, - tags: Array.isArray(acc.tags) ? acc.tags : undefined, - notes: typeof acc.notes === 'string' ? acc.notes : undefined, - source: acc.source === 'opencode' || acc.source === 'codex' ? acc.source : undefined - }; -} -function validateStore(data) { - if (!data || typeof data !== 'object') - return null; - const accounts = {}; - const rawAccounts = data.accounts; - if (rawAccounts && typeof rawAccounts === 'object') { - for (const [alias, acc] of Object.entries(rawAccounts)) { - const validated = validateAccount(acc, alias); - if (validated) { - accounts[alias] = validated; - } - } - } - return { - version: typeof data.version === 'number' ? data.version : undefined, - accounts, - activeAlias: typeof data.activeAlias === 'string' ? data.activeAlias : null, - rotationIndex: typeof data.rotationIndex === 'number' ? data.rotationIndex : 0, - lastRotation: typeof data.lastRotation === 'number' ? data.lastRotation : Date.now(), - // Phase E: Preserve force mode fields - forcedAlias: data.forcedAlias ?? null, - forcedUntil: data.forcedUntil ?? null, - previousRotationStrategy: data.previousRotationStrategy ?? null, - forcedBy: data.forcedBy ?? null, - // Phase F: Preserve rotation strategy and settings - rotationStrategy: data.rotationStrategy ?? 'round-robin', - settings: data.settings ?? undefined - }; -} -function migrateV1toV2(data) { - return { - ...data, - version: 2, - settings: { - rotationStrategy: 'round-robin' - }, - force: { - forcedAlias: null, - forcedUntil: null, - previousRotationStrategy: null, - forcedBy: null - } - }; -} -function migrateStore(data) { - if (!data || typeof data !== 'object') - return null; - const version = typeof data.version === 'number' ? data.version : 1; - if (version > CURRENT_STORE_VERSION) { - console.warn(`[multi-auth] Store version ${version} is newer than supported ${CURRENT_STORE_VERSION}. Proceeding with caution.`); - return validateStore(data); - } - let migrated = data; - if (version === 1) { - migrated = migrateV1toV2(data); - console.log('[multi-auth] Migrated store from v1 to v2'); - } - return validateStore(migrated); -} -function getLastKnownGoodPath() { - return `${getStoreFile()}.lkg`; -} -function saveLastKnownGood(store) { - // Avoid writing plaintext snapshots when store encryption is enabled. - if (getPassphrase()) { - return; - } - const lkgPath = getLastKnownGoodPath(); - try { - fs.writeFileSync(lkgPath, JSON.stringify(store, null, 2), { mode: 0o600 }); - } - catch { - // ignore - } -} -function loadLastKnownGood() { - const lkgPath = getLastKnownGoodPath(); - if (!fs.existsSync(lkgPath)) - return null; - try { - const data = fs.readFileSync(lkgPath, 'utf-8'); - const parsed = JSON.parse(data); - return validateStore(parsed); - } - catch { - return null; - } -} -async function acquireWriteLock() { - if (!writeLock) { - writeLock = true; - return; - } - return new Promise((resolve) => { - writeLockQueue.push(resolve); - }); -} -function releaseWriteLock() { - const next = writeLockQueue.shift(); - if (next) { - next(); - } - else { - writeLock = false; - } -} -function buildSnapshot(window) { - if (!window) - return undefined; - return { - remaining: window.remaining, - limit: window.limit, - resetAt: window.resetAt - }; -} -function buildHistoryEntry(rateLimits) { - if (!hasMeaningfulRateLimits(rateLimits)) - return null; - const updatedAtValues = [rateLimits?.fiveHour?.updatedAt, rateLimits?.weekly?.updatedAt].filter((value) => typeof value === 'number'); - const at = updatedAtValues.length > 0 ? Math.max(...updatedAtValues) : Date.now(); - return { - at, - fiveHour: buildSnapshot(rateLimits?.fiveHour), - weekly: buildSnapshot(rateLimits?.weekly) - }; -} -function appendHistory(history, entry) { - const next = history ? [...history] : []; - const last = next[next.length - 1]; - const same = last && - last.fiveHour?.remaining === entry.fiveHour?.remaining && - last.weekly?.remaining === entry.weekly?.remaining && - last.fiveHour?.resetAt === entry.fiveHour?.resetAt && - last.weekly?.resetAt === entry.weekly?.resetAt; - if (!same) { - next.push(entry); - } - if (next.length > 160) { - return next.slice(next.length - 160); - } - return next; -} -export function loadStore() { - storeLocked = false; - lastStoreError = null; - lastStoreEncrypted = false; - ensureDir(); - const file = getStoreFile(); - if (fs.existsSync(file)) { - try { - const data = fs.readFileSync(file, 'utf-8'); - const parsed = JSON.parse(data); - if (isEncryptedFile(parsed)) { - lastStoreEncrypted = true; - const passphrase = getPassphrase(); - if (!passphrase) { - storeLocked = true; - lastStoreError = `Store is encrypted. Set ${STORE_ENV_PASSPHRASE} to unlock.`; - return emptyStore(); - } - try { - const decrypted = decryptStore(parsed, passphrase); - const validated = validateStore(decrypted); - if (validated) { - saveLastKnownGood(validated); - return validated; - } - storeLocked = true; - lastStoreError = 'Store validation failed after decryption.'; - const lkg = loadLastKnownGood(); - if (lkg) { - console.warn('[multi-auth] Restored from last-known-good snapshot'); - return lkg; - } - return emptyStore(); - } - catch (err) { - storeLocked = true; - lastStoreError = 'Failed to decrypt store. Check passphrase.'; - console.error('[multi-auth] Failed to decrypt store:', err); - return emptyStore(); - } - } - const migrated = migrateStore(parsed); - if (migrated) { - saveLastKnownGood(migrated); - return migrated; - } - storeLocked = true; - lastStoreError = 'Store validation failed.'; - console.error('[multi-auth] Store validation failed'); - const lkg = loadLastKnownGood(); - if (lkg) { - console.warn('[multi-auth] Restored from last-known-good snapshot'); - return lkg; - } - return emptyStore(); - } - catch (err) { - storeLocked = true; - lastStoreError = 'Failed to parse store. Store locked until fixed.'; - console.error('[multi-auth] Failed to parse store:', err); - const lkg = loadLastKnownGood(); - if (lkg) { - console.warn('[multi-auth] Restored from last-known-good snapshot'); - return lkg; - } - } - } - return emptyStore(); -} -export function saveStore(store) { - ensureDir(); - if (storeLocked) { - console.error('[multi-auth] Store locked; refusing to overwrite encrypted file.'); - return; - } - const file = getStoreFile(); - const passphrase = getPassphrase(); - const payload = passphrase ? encryptStore(store, passphrase) : store; - const json = JSON.stringify(payload, null, 2); - try { - if (fs.existsSync(file)) { - fs.copyFileSync(file, `${file}.bak`); - fs.chmodSync(`${file}.bak`, 0o600); - } - } - catch { - // ignore backup failures - } - const tmp = `${file}.tmp-${process.pid}-${Date.now()}`; - let fd = null; - try { - fd = fs.openSync(tmp, 'w', 0o600); - fs.writeFileSync(fd, json, { encoding: 'utf-8' }); - try { - fs.fsyncSync(fd); - } - catch { - // fsync not supported everywhere; best-effort - } - } - finally { - if (fd !== null) { - try { - fs.closeSync(fd); - } - catch { - // ignore - } - } - } - try { - fs.renameSync(tmp, file); - } - catch (err) { - if (err?.code === 'EPERM' || err?.code === 'EEXIST') { - try { - fs.unlinkSync(file); - } - catch { - // ignore - } - fs.renameSync(tmp, file); - } - else { - try { - fs.unlinkSync(tmp); - } - catch { - // ignore - } - throw err; - } - } - try { - const dirFd = fs.openSync(getStoreDir(), 'r'); - try { - fs.fsyncSync(dirFd); - } - catch { - // ignore - } - fs.closeSync(dirFd); - } - catch { - // ignore - } - try { - fs.chmodSync(file, 0o600); - } - catch { - // ignore - } - saveLastKnownGood(store); -} -export async function withWriteLock(fn) { - await acquireWriteLock(); - try { - return fn(); - } - finally { - releaseWriteLock(); - } -} -export function getStoreDiagnostics() { - return { - storeDir: getStoreDir(), - storeFile: getStoreFile(), - locked: storeLocked, - encrypted: lastStoreEncrypted, - error: lastStoreError - }; -} -export function addAccount(alias, creds) { - const store = loadStore(); - const entry = buildHistoryEntry(creds.rateLimits); - store.accounts[alias] = { - ...creds, - alias, - usageCount: 0, - rateLimitHistory: entry ? [entry] : creds.rateLimitHistory - }; - if (!store.activeAlias) { - store.activeAlias = alias; - } - saveStore(store); - return store; -} -export function removeAccount(alias) { - const store = loadStore(); - delete store.accounts[alias]; - if (store.activeAlias === alias) { - const remaining = Object.keys(store.accounts); - store.activeAlias = remaining[0] || null; - } - saveStore(store); - return store; -} -export function updateAccount(alias, updates) { - const store = loadStore(); - if (store.accounts[alias]) { - const current = store.accounts[alias]; - const next = { ...current, ...updates }; - if (updates.rateLimits || next.rateLimits) { - const entry = buildHistoryEntry(next.rateLimits); - if (entry) { - next.rateLimitHistory = appendHistory(current.rateLimitHistory, entry); - } - } - store.accounts[alias] = next; - saveStore(store); - } - return store; -} -export function setActiveAlias(alias) { - const store = loadStore(); - const now = Date.now(); - const previousAlias = store.activeAlias; - if (alias === null) { - store.activeAlias = null; - } - else if (store.accounts[alias]) { - if (previousAlias && previousAlias !== alias && store.accounts[previousAlias]) { - store.accounts[previousAlias] = { - ...store.accounts[previousAlias], - lastActiveUntil: now - }; - } - store.activeAlias = alias; - store.accounts[alias] = { - ...store.accounts[alias], - lastSeenAt: now, - lastActiveUntil: undefined - }; - const aliases = Object.keys(store.accounts); - const idx = aliases.indexOf(alias); - if (idx >= 0) { - store.rotationIndex = idx; - } - store.lastRotation = now; - } - saveStore(store); - return store; -} -export function getActiveAccount() { - const store = loadStore(); - if (!store.activeAlias) - return null; - return store.accounts[store.activeAlias] || null; -} -export function listAccounts() { - const store = loadStore(); - return Object.values(store.accounts); -} -export function getStorePath() { - return getStoreFile(); -} -export function getStoreStatus() { - const diag = getStoreDiagnostics(); - return { locked: diag.locked, encrypted: diag.encrypted, error: diag.error }; -} -//# sourceMappingURL=store.js.map \ No newline at end of file diff --git a/dist/store.js.map b/dist/store.js.map deleted file mode 100644 index 0686678..0000000 --- a/dist/store.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AAQ1D,MAAM,aAAa,GAAG,+BAA+B,CAAA;AACrD,MAAM,cAAc,GAAG,gCAAgC,CAAA;AACvD,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,qBAAqB,CAAC,CAAA;AACnF,MAAM,kBAAkB,GAAG,eAAe,CAAA;AAE1C,SAAS,WAAW;IAClB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;IAC3C,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;IACrE,OAAO,iBAAiB,CAAA;AAC1B,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IAC5C,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;IACrE,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,kBAAkB,CAAC,CAAA;AACrD,CAAC;AAED,MAAM,oBAAoB,GAAG,6BAA6B,CAAA;AAC1D,MAAM,qBAAqB,GAAG,CAAC,CAAA;AAiC/B,IAAI,WAAW,GAAG,KAAK,CAAA;AACvB,IAAI,cAAc,GAAkB,IAAI,CAAA;AACxC,IAAI,kBAAkB,GAAG,KAAK,CAAA;AAC9B,IAAI,SAAS,GAAG,KAAK,CAAA;AACrB,IAAI,cAAc,GAAsB,EAAE,CAAA;AAE1C,SAAS,SAAS;IAChB,MAAM,GAAG,GAAG,WAAW,EAAE,CAAA;IACzB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACrD,CAAC;AACH,CAAC;AAED,SAAS,UAAU;IACjB,OAAO;QACL,OAAO,EAAE,qBAAqB;QAC9B,QAAQ,EAAE,EAAE;QACZ,WAAW,EAAE,IAAI;QACjB,aAAa,EAAE,CAAC;QAChB,YAAY,EAAE,IAAI,CAAC,GAAG,EAAE;KACzB,CAAA;AACH,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAA;IAC/C,OAAO,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;AACxD,CAAC;AAED,SAAS,eAAe,CAAC,OAAY;IACnC,OAAO,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;AAC3F,CAAC;AAED,SAAS,SAAS,CAAC,UAAkB,EAAE,IAAY;IACjD,OAAO,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;AAChD,CAAC;AAED,SAAS,YAAY,CAAC,KAAmB,EAAE,UAAkB;IAC3D,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;IACnC,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;IACjC,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;IAC5D,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IACxC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACpF,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,CAAA;IAC/B,OAAO;QACL,SAAS,EAAE,IAAI;QACf,OAAO,EAAE,qBAAqB;QAC9B,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC7B,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACzB,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC3B,IAAI,EAAE,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC;KACnC,CAAA;AACH,CAAC;AAED,SAAS,YAAY,CAAC,IAAwB,EAAE,UAAkB;IAChE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAC7C,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAA;IACzC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAC7C,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;IACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAA;IAChE,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;IACxB,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC3F,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAiB,CAAA;AAC9C,CAAC;AAED,SAAS,eAAe,CAAC,GAAQ,EAAE,KAAa;IAC9C,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAChD,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,WAAW;QAAE,OAAO,IAAI,CAAA;IACxE,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,YAAY;QAAE,OAAO,IAAI,CAAA;IAC1E,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAElD,MAAM,gBAAgB,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;QAC1D,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,KAAU,EAAE,EAAE,CACzC,uBAAuB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAC9E;QACH,CAAC,CAAC,SAAS,CAAA;IACb,MAAM,UAAU,GAAG,uBAAuB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAA;IAEvF,OAAO;QACL,KAAK;QACL,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,OAAO,EAAE,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;QAClE,SAAS,EAAE,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QACxE,aAAa,EAAE,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;QACpF,MAAM,EAAE,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QAC/D,QAAQ,EAAE,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;QACrE,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,KAAK,EAAE,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAC5D,WAAW,EAAE,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;QAC9E,UAAU,EAAE,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QAC3E,eAAe,EAAE,OAAO,GAAG,CAAC,eAAe,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS;QAC1F,QAAQ,EAAE,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;QACrE,UAAU,EAAE,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACnE,gBAAgB,EAAE,OAAO,GAAG,CAAC,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS;QAC7F,qBAAqB,EAAE,OAAO,GAAG,CAAC,qBAAqB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAC,SAAS;QAC5G,kBAAkB,EAAE,OAAO,GAAG,CAAC,kBAAkB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,SAAS;QACnG,qBAAqB,EAAE,OAAO,GAAG,CAAC,qBAAqB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAC,SAAS;QAC5G,qBAAqB,EAAE,OAAO,GAAG,CAAC,qBAAqB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAC,SAAS;QAC5G,yBAAyB,EAAE,OAAO,GAAG,CAAC,yBAAyB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAAC,SAAS;QACxH,sBAAsB,EAAE,OAAO,GAAG,CAAC,sBAAsB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC,SAAS;QAC/G,yBAAyB,EAAE,OAAO,GAAG,CAAC,yBAAyB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAAC,SAAS;QACxH,WAAW,EAAE,OAAO,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;QAC/E,iBAAiB,EAAE,OAAO,GAAG,CAAC,iBAAiB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS;QAChG,uCAAuC;QACvC,OAAO,EAAE,OAAO,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;QACnE,UAAU,EAAE,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QAC3E,UAAU,EAAE,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QAC3E,aAAa,EAAE,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;QACpF,UAAU;QACV,gBAAgB,EAAE,gBAAgB,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS;QAChG,WAAW,EAAE,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;QAC9E,UAAU,EAAE,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QAC3E,gBAAgB,EAAE,OAAO,GAAG,CAAC,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS;QAC7F,gBAAgB,EAAE,OAAO,GAAG,CAAC,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS;QAC7F,gBAAgB,EACd,GAAG,CAAC,gBAAgB,KAAK,OAAO;YAChC,GAAG,CAAC,gBAAgB,KAAK,OAAO;YAChC,GAAG,CAAC,gBAAgB,KAAK,OAAO;YAChC,GAAG,CAAC,gBAAgB,KAAK,SAAS;YAChC,CAAC,CAAC,GAAG,CAAC,gBAAgB;YACtB,CAAC,CAAC,SAAS;QACf,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;QACpD,KAAK,EAAE,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QAC5D,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,UAAU,IAAI,GAAG,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;KACrF,CAAA;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAS;IAC9B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAElD,MAAM,QAAQ,GAAuC,EAAE,CAAA;IACvD,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAA;IACjC,IAAI,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACnD,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YACvD,MAAM,SAAS,GAAG,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;YAC7C,IAAI,SAAS,EAAE,CAAC;gBACd,QAAQ,CAAC,KAAK,CAAC,GAAG,SAAS,CAAA;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;QACpE,QAAQ;QACR,WAAW,EAAE,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;QAC3E,aAAa,EAAE,OAAO,IAAI,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QAC9E,YAAY,EAAE,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;QACpF,sCAAsC;QACtC,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI;QACrC,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI;QACrC,wBAAwB,EAAE,IAAI,CAAC,wBAAwB,IAAI,IAAI;QAC/D,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI;QAC/B,mDAAmD;QACnD,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,aAAa;QACxD,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;KACrC,CAAA;AACH,CAAC;AAED,SAAS,aAAa,CAAC,IAAiB;IACtC,OAAO;QACL,GAAG,IAAI;QACP,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE;YACR,gBAAgB,EAAE,aAAa;SAChC;QACD,KAAK,EAAE;YACL,WAAW,EAAE,IAAI;YACjB,WAAW,EAAE,IAAI;YACjB,wBAAwB,EAAE,IAAI;YAC9B,QAAQ,EAAE,IAAI;SACf;KACF,CAAA;AACH,CAAC;AAED,SAAS,YAAY,CAAC,IAAS;IAC7B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAElD,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;IAEnE,IAAI,OAAO,GAAG,qBAAqB,EAAE,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,8BAA8B,OAAO,4BAA4B,qBAAqB,4BAA4B,CAAC,CAAA;QAChI,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IAED,IAAI,QAAQ,GAAQ,IAAI,CAAA;IACxB,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;QAClB,QAAQ,GAAG,aAAa,CAAC,IAAmB,CAAC,CAAA;QAC7C,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAA;IAC1D,CAAC;IAED,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAA;AAChC,CAAC;AAED,SAAS,oBAAoB;IAC3B,OAAO,GAAG,YAAY,EAAE,MAAM,CAAA;AAChC,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAmB;IAC5C,sEAAsE;IACtE,IAAI,aAAa,EAAE,EAAE,CAAC;QACpB,OAAM;IACR,CAAC;IAED,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAA;IACtC,IAAI,CAAC;QACH,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAC5E,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB;IACxB,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAA;IACtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAA;IACxC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC/B,OAAO,aAAa,CAAC,MAAM,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,KAAK,UAAU,gBAAgB;IAC7B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,IAAI,CAAA;QAChB,OAAM;IACR,CAAC;IACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC9B,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,gBAAgB;IACvB,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,EAAE,CAAA;IACnC,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,EAAE,CAAA;IACR,CAAC;SAAM,CAAC;QACN,SAAS,GAAG,KAAK,CAAA;IACnB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,MAAiE;IACtF,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAC7B,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAA;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,UAA6C;IACtE,IAAI,CAAC,uBAAuB,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAA;IACrD,MAAM,eAAe,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,MAAM,CAC7F,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CACtD,CAAA;IACD,MAAM,EAAE,GAAG,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;IACjF,OAAO;QACL,EAAE;QACF,QAAQ,EAAE,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC;QAC7C,MAAM,EAAE,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC;KAC1C,CAAA;AACH,CAAC;AAED,SAAS,aAAa,CACpB,OAA4C,EAC5C,KAA4B;IAE5B,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAClC,MAAM,IAAI,GACR,IAAI;QACJ,IAAI,CAAC,QAAQ,EAAE,SAAS,KAAK,KAAK,CAAC,QAAQ,EAAE,SAAS;QACtD,IAAI,CAAC,MAAM,EAAE,SAAS,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS;QAClD,IAAI,CAAC,QAAQ,EAAE,OAAO,KAAK,KAAK,CAAC,QAAQ,EAAE,OAAO;QAClD,IAAI,CAAC,MAAM,EAAE,OAAO,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,CAAA;IAChD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAClB,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAA;IACtC,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,WAAW,GAAG,KAAK,CAAA;IACnB,cAAc,GAAG,IAAI,CAAA;IACrB,kBAAkB,GAAG,KAAK,CAAA;IAC1B,SAAS,EAAE,CAAA;IACX,MAAM,IAAI,GAAG,YAAY,EAAE,CAAA;IAC3B,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YAC/B,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5B,kBAAkB,GAAG,IAAI,CAAA;gBACzB,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;gBAClC,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,WAAW,GAAG,IAAI,CAAA;oBAClB,cAAc,GAAG,2BAA2B,oBAAoB,aAAa,CAAA;oBAC7E,OAAO,UAAU,EAAE,CAAA;gBACrB,CAAC;gBACD,IAAI,CAAC;oBACH,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;oBAClD,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC,CAAA;oBAC1C,IAAI,SAAS,EAAE,CAAC;wBACd,iBAAiB,CAAC,SAAS,CAAC,CAAA;wBAC5B,OAAO,SAAS,CAAA;oBAClB,CAAC;oBACD,WAAW,GAAG,IAAI,CAAA;oBAClB,cAAc,GAAG,2CAA2C,CAAA;oBAC5D,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAA;oBAC/B,IAAI,GAAG,EAAE,CAAC;wBACR,OAAO,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAA;wBACnE,OAAO,GAAG,CAAA;oBACZ,CAAC;oBACD,OAAO,UAAU,EAAE,CAAA;gBACrB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,WAAW,GAAG,IAAI,CAAA;oBAClB,cAAc,GAAG,4CAA4C,CAAA;oBAC7D,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAA;oBAC3D,OAAO,UAAU,EAAE,CAAA;gBACrB,CAAC;YACH,CAAC;YAED,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;YACrC,IAAI,QAAQ,EAAE,CAAC;gBACb,iBAAiB,CAAC,QAAQ,CAAC,CAAA;gBAC3B,OAAO,QAAQ,CAAA;YACjB,CAAC;YAED,WAAW,GAAG,IAAI,CAAA;YAClB,cAAc,GAAG,0BAA0B,CAAA;YAC3C,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAA;YAErD,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAA;YAC/B,IAAI,GAAG,EAAE,CAAC;gBACR,OAAO,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAA;gBACnE,OAAO,GAAG,CAAA;YACZ,CAAC;YACD,OAAO,UAAU,EAAE,CAAA;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,WAAW,GAAG,IAAI,CAAA;YAClB,cAAc,GAAG,kDAAkD,CAAA;YACnE,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,GAAG,CAAC,CAAA;YAEzD,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAA;YAC/B,IAAI,GAAG,EAAE,CAAC;gBACR,OAAO,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAA;gBACnE,OAAO,GAAG,CAAA;YACZ,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,UAAU,EAAE,CAAA;AACrB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAmB;IAC3C,SAAS,EAAE,CAAA;IACX,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,kEAAkE,CAAC,CAAA;QACjF,OAAM;IACR,CAAC;IAED,MAAM,IAAI,GAAG,YAAY,EAAE,CAAA;IAC3B,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;IAClC,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IACpE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IAE7C,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,IAAI,MAAM,CAAC,CAAA;YACpC,EAAE,CAAC,SAAS,CAAC,GAAG,IAAI,MAAM,EAAE,KAAK,CAAC,CAAA;QACpC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yBAAyB;IAC3B,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,IAAI,QAAQ,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;IACtD,IAAI,EAAE,GAAkB,IAAI,CAAA;IAE5B,IAAI,CAAC;QACH,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;QACjC,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAA;QACjD,IAAI,CAAC;YACH,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAA;QAClB,CAAC;QAAC,MAAM,CAAC;YACP,8CAA8C;QAChD,CAAC;IACH,CAAC;YAAS,CAAC;QACT,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,IAAI,CAAC;gBACH,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAA;YAClB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAC1B,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,EAAE,IAAI,KAAK,OAAO,IAAI,GAAG,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YACpD,IAAI,CAAC;gBACH,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;YACrB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC1B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;YACpB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,MAAM,GAAG,CAAA;QACX,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,CAAA;QAC7C,IAAI,CAAC;YACH,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,iBAAiB,CAAC,KAAK,CAAC,CAAA;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAI,EAAW;IAChD,MAAM,gBAAgB,EAAE,CAAA;IACxB,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,CAAA;IACb,CAAC;YAAS,CAAC;QACT,gBAAgB,EAAE,CAAA;IACpB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB;IAOjC,OAAO;QACL,QAAQ,EAAE,WAAW,EAAE;QACvB,SAAS,EAAE,YAAY,EAAE;QACzB,MAAM,EAAE,WAAW;QACnB,SAAS,EAAE,kBAAkB;QAC7B,KAAK,EAAE,cAAc;KACtB,CAAA;AACH,CAAC;AAGD,MAAM,UAAU,UAAU,CAAC,KAAa,EAAE,KAAuD;IAC/F,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;IACzB,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IACjD,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;QACtB,GAAG,KAAK;QACR,KAAK;QACL,UAAU,EAAE,CAAC;QACb,gBAAgB,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,gBAAgB;KAC3D,CAAA;IACD,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QACvB,KAAK,CAAC,WAAW,GAAG,KAAK,CAAA;IAC3B,CAAC;IACD,SAAS,CAAC,KAAK,CAAC,CAAA;IAChB,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;IACzB,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC5B,IAAI,KAAK,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;QAChC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QAC7C,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;IAC1C,CAAC;IACD,SAAS,CAAC,KAAK,CAAC,CAAA;IAChB,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,OAAoC;IAC/E,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;IACzB,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QACrC,MAAM,IAAI,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,EAAE,CAAA;QACvC,IAAI,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;YAChD,IAAI,KAAK,EAAE,CAAC;gBACV,IAAI,CAAC,gBAAgB,GAAG,aAAa,CAAC,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAA;YACxE,CAAC;QACH,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,IAAI,CAAA;QAC5B,SAAS,CAAC,KAAK,CAAC,CAAA;IAClB,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAoB;IACjD,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;IACzB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,MAAM,aAAa,GAAG,KAAK,CAAC,WAAW,CAAA;IAEvC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,KAAK,CAAC,WAAW,GAAG,IAAI,CAAA;IAC1B,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,IAAI,aAAa,IAAI,aAAa,KAAK,KAAK,IAAI,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YAC9E,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG;gBAC9B,GAAG,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC;gBAChC,eAAe,EAAE,GAAG;aACrB,CAAA;QACH,CAAC;QAED,KAAK,CAAC,WAAW,GAAG,KAAK,CAAA;QACzB,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG;YACtB,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;YACxB,UAAU,EAAE,GAAG;YACf,eAAe,EAAE,SAAS;SAC3B,CAAA;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QAC3C,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAClC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;YACb,KAAK,CAAC,aAAa,GAAG,GAAG,CAAA;QAC3B,CAAC;QACD,KAAK,CAAC,YAAY,GAAG,GAAG,CAAA;IAC1B,CAAC;IACD,SAAS,CAAC,KAAK,CAAC,CAAA;IAChB,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;IACzB,IAAI,CAAC,KAAK,CAAC,WAAW;QAAE,OAAO,IAAI,CAAA;IACnC,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,IAAI,CAAA;AAClD,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;IACzB,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;AACtC,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,YAAY,EAAE,CAAA;AACvB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAA;IAClC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAA;AAC9E,CAAC"} \ No newline at end of file diff --git a/dist/systemd.d.ts b/dist/systemd.d.ts deleted file mode 100644 index eefe911..0000000 --- a/dist/systemd.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface ServiceOptions { - cliPath: string; - host?: string; - port?: number; -} -export declare function getServiceFilePath(): string; -export declare function installService(options: ServiceOptions): string; -export declare function disableService(): void; -export declare function serviceStatus(): void; -//# sourceMappingURL=systemd.d.ts.map \ No newline at end of file diff --git a/dist/systemd.d.ts.map b/dist/systemd.d.ts.map deleted file mode 100644 index 63ac4a4..0000000 --- a/dist/systemd.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"systemd.d.ts","sourceRoot":"","sources":["../src/systemd.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAMD,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAaD,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CA2B9D;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC"} \ No newline at end of file diff --git a/dist/systemd.js b/dist/systemd.js deleted file mode 100644 index 426af76..0000000 --- a/dist/systemd.js +++ /dev/null @@ -1,53 +0,0 @@ -import * as fs from 'node:fs'; -import * as os from 'node:os'; -import * as path from 'node:path'; -import { execFileSync } from 'node:child_process'; -const SERVICE_NAME = 'codex-soft'; -function getServiceDir() { - return path.join(os.homedir(), '.config', 'systemd', 'user'); -} -export function getServiceFilePath() { - return path.join(getServiceDir(), `${SERVICE_NAME}.service`); -} -function ensureDir() { - const dir = getServiceDir(); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); - } -} -function runSystemctl(args) { - execFileSync('systemctl', ['--user', ...args], { stdio: 'inherit' }); -} -export function installService(options) { - const host = options.host || '127.0.0.1'; - const port = options.port || 3434; - const serviceFile = getServiceFilePath(); - const workingDir = path.dirname(options.cliPath); - const execStart = `${process.execPath} ${options.cliPath} web --host ${host} --port ${port}`; - ensureDir(); - const unit = `[Unit] -Description=Codex Soft Dashboard -After=network.target - -[Service] -Type=simple -WorkingDirectory=${workingDir} -ExecStart=${execStart} -Restart=on-failure -RestartSec=3 - -[Install] -WantedBy=default.target -`; - fs.writeFileSync(serviceFile, unit, { mode: 0o600 }); - runSystemctl(['daemon-reload']); - runSystemctl(['enable', '--now', `${SERVICE_NAME}.service`]); - return serviceFile; -} -export function disableService() { - runSystemctl(['disable', '--now', `${SERVICE_NAME}.service`]); -} -export function serviceStatus() { - runSystemctl(['status', `${SERVICE_NAME}.service`, '--no-pager']); -} -//# sourceMappingURL=systemd.js.map \ No newline at end of file diff --git a/dist/systemd.js.map b/dist/systemd.js.map deleted file mode 100644 index 292843c..0000000 --- a/dist/systemd.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"systemd.js","sourceRoot":"","sources":["../src/systemd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,MAAM,YAAY,GAAG,YAAY,CAAA;AAQjC,SAAS,aAAa;IACpB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;AAC9D,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,GAAG,YAAY,UAAU,CAAC,CAAA;AAC9D,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,GAAG,GAAG,aAAa,EAAE,CAAA;IAC3B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACrD,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,IAAc;IAClC,YAAY,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;AACtE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAuB;IACpD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAA;IACxC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAA;IACjC,MAAM,WAAW,GAAG,kBAAkB,EAAE,CAAA;IACxC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAChD,MAAM,SAAS,GAAG,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,OAAO,eAAe,IAAI,WAAW,IAAI,EAAE,CAAA;IAE5F,SAAS,EAAE,CAAA;IACX,MAAM,IAAI,GAAG;;;;;;mBAMI,UAAU;YACjB,SAAS;;;;;;CAMpB,CAAA;IAEC,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IACpD,YAAY,CAAC,CAAC,eAAe,CAAC,CAAC,CAAA;IAC/B,YAAY,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,YAAY,UAAU,CAAC,CAAC,CAAA;IAC5D,OAAO,WAAW,CAAA;AACpB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,YAAY,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,YAAY,UAAU,CAAC,CAAC,CAAA;AAC/D,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,YAAY,CAAC,CAAC,QAAQ,EAAE,GAAG,YAAY,UAAU,EAAE,YAAY,CAAC,CAAC,CAAA;AACnE,CAAC"} \ No newline at end of file diff --git a/dist/types.d.ts b/dist/types.d.ts deleted file mode 100644 index 1cb55f6..0000000 --- a/dist/types.d.ts +++ /dev/null @@ -1,144 +0,0 @@ -export interface AccountCredentials { - alias: string; - accessToken: string; - refreshToken: string; - idToken?: string; - accountId?: string; - accountUserId?: string; - userId?: string; - planType?: string; - expiresAt: number; - email?: string; - lastRefresh?: string; - lastSeenAt?: number; - lastActiveUntil?: number; - lastUsed?: number; - usageCount: number; - rateLimitedUntil?: number; - modelUnsupportedUntil?: number; - modelUnsupportedAt?: number; - modelUnsupportedModel?: string; - modelUnsupportedError?: string; - workspaceDeactivatedUntil?: number; - workspaceDeactivatedAt?: number; - workspaceDeactivatedError?: string; - authInvalid?: boolean; - authInvalidatedAt?: number; - enabled?: boolean; - disabledAt?: number; - disabledBy?: string; - disableReason?: string; - rateLimits?: AccountRateLimits; - rateLimitHistory?: RateLimitHistoryEntry[]; - limitStatus?: LimitStatus; - limitError?: string; - lastLimitProbeAt?: number; - lastLimitErrorAt?: number; - limitsConfidence?: LimitsConfidence; - tags?: string[]; - notes?: string; - source?: 'opencode' | 'codex'; -} -export interface RateLimitWindow { - limit?: number; - remaining?: number; - resetAt?: number; - updatedAt?: number; -} -export interface AccountRateLimits { - fiveHour?: RateLimitWindow; - weekly?: RateLimitWindow; -} -export interface RateLimitSnapshot { - remaining?: number; - limit?: number; - resetAt?: number; -} -export interface RateLimitHistoryEntry { - at: number; - fiveHour?: RateLimitSnapshot; - weekly?: RateLimitSnapshot; -} -export type LimitStatus = 'idle' | 'queued' | 'running' | 'success' | 'error' | 'stopped'; -export type LimitsConfidence = 'fresh' | 'stale' | 'error' | 'unknown'; -export declare function calculateLimitsConfidence(lastProbeAt: number | undefined, lastErrorAt: number | undefined, limitStatus: LimitStatus | undefined): LimitsConfidence; -export interface AccountStore { - version?: number; - accounts: Record; - activeAlias: string | null; - rotationIndex: number; - lastRotation: number; - forcedAlias?: string | null; - forcedUntil?: number | null; - previousRotationStrategy?: string | null; - forcedBy?: string | null; - rotationStrategy?: 'round-robin' | 'least-used' | 'random' | 'weighted-round-robin'; - settings?: RotationSettings; -} -export interface OpenAIModel { - id: string; - object: string; - created: number; - owned_by: string; -} -export interface PluginConfig { - rotationStrategy: 'round-robin' | 'least-used' | 'random' | 'weighted-round-robin'; - autoRefreshTokens: boolean; - rateLimitCooldownMs: number; - modelUnsupportedCooldownMs: number; - workspaceDeactivatedCooldownMs: number; - modelFilter: RegExp; -} -export interface ProviderModel { - name: string; - limit: { - context: number; - input?: number; - output: number; - }; - modalities: { - input: string[]; - output: string[]; - }; - options: { - reasoningEffort: string; - reasoningSummary: string; - textVerbosity: string; - include: string[]; - store: boolean; - service_tier?: string; - }; -} -export declare const DEFAULT_CONFIG: PluginConfig; -export interface RotationSettings { - rotationStrategy: 'round-robin' | 'least-used' | 'random' | 'weighted-round-robin'; - criticalThreshold: number; - lowThreshold: number; - accountWeights: Record; - featureFlags?: FeatureFlags; - updatedAt?: number; - updatedBy?: string; -} -export interface FeatureFlags { - antigravityEnabled: boolean; -} -export declare const DEFAULT_FEATURE_FLAGS: FeatureFlags; -export type WeightPreset = 'balanced' | 'conservative' | 'aggressive' | 'custom'; -export interface WeightedPresetConfig { - name: WeightPreset; - description: string; - defaultWeights: Record; - thresholds: { - critical: number; - low: number; - }; -} -export declare const DEFAULT_ROTATION_SETTINGS: RotationSettings; -export declare const WEIGHTED_PRESETS: Record; -export interface SettingsValidationError { - field: string; - message: string; - constraint: string; -} -export declare function validateSettings(settings: Partial): SettingsValidationError[]; -//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/dist/types.d.ts.map b/dist/types.d.ts.map deleted file mode 100644 index 9e46893..0000000 --- a/dist/types.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/types.js b/dist/types.js deleted file mode 100644 index 1dc86f2..0000000 --- a/dist/types.js +++ /dev/null @@ -1,130 +0,0 @@ -// Phase C: Calculate limits confidence based on probe timestamps -export function calculateLimitsConfidence(lastProbeAt, lastErrorAt, limitStatus) { - const now = Date.now(); - const FRESH_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes - const STALE_THRESHOLD_MS = 60 * 60 * 1000; // 60 minutes - // If we have an error more recent than last success, show error - if (lastErrorAt && (!lastProbeAt || lastErrorAt > lastProbeAt)) { - // If we have some successful data, show stale with error - if (lastProbeAt && now - lastProbeAt < STALE_THRESHOLD_MS) { - return 'error'; - } - } - // No successful probe ever - if (!lastProbeAt) { - return 'unknown'; - } - const ageMs = now - lastProbeAt; - if (ageMs < FRESH_THRESHOLD_MS) { - return 'fresh'; - } - else if (ageMs < STALE_THRESHOLD_MS) { - return 'stale'; - } - else { - // Data is too old, treat as unknown - return 'unknown'; - } -} -export const DEFAULT_CONFIG = { - rotationStrategy: 'round-robin', - autoRefreshTokens: true, - rateLimitCooldownMs: 5 * 60 * 1000, // 5 minutes - modelUnsupportedCooldownMs: 30 * 60 * 1000, // 30 minutes - workspaceDeactivatedCooldownMs: 30 * 60 * 1000, // 30 minutes - modelFilter: /^gpt-5/ -}; -// Phase G: Default feature flags -export const DEFAULT_FEATURE_FLAGS = { - antigravityEnabled: false -}; -// Phase F: Default settings -export const DEFAULT_ROTATION_SETTINGS = { - rotationStrategy: 'round-robin', - criticalThreshold: 10, - lowThreshold: 30, - accountWeights: {}, - featureFlags: { ...DEFAULT_FEATURE_FLAGS } -}; -// Phase F: Preset configurations -export const WEIGHTED_PRESETS = { - balanced: { - name: 'balanced', - description: 'Equal distribution across all accounts', - defaultWeights: {}, // Calculated dynamically as 1/n - thresholds: { critical: 10, low: 30 } - }, - conservative: { - name: 'conservative', - description: 'Prefer accounts with higher remaining limits', - defaultWeights: {}, // Calculated based on limit health - thresholds: { critical: 20, low: 40 } - }, - aggressive: { - name: 'aggressive', - description: 'Maximize throughput, accept higher risk', - defaultWeights: {}, // Favor accounts with high usage - thresholds: { critical: 5, low: 20 } - }, - custom: { - name: 'custom', - description: 'User-defined weights and thresholds', - defaultWeights: {}, - thresholds: { critical: 10, low: 30 } - } -}; -export function validateSettings(settings) { - const errors = []; - // Validate thresholds are in 0-100 range - if (settings.criticalThreshold !== undefined) { - if (settings.criticalThreshold < 0 || settings.criticalThreshold > 100) { - errors.push({ - field: 'criticalThreshold', - message: 'Critical threshold must be between 0 and 100', - constraint: '0 <= criticalThreshold <= 100' - }); - } - } - if (settings.lowThreshold !== undefined) { - if (settings.lowThreshold < 0 || settings.lowThreshold > 100) { - errors.push({ - field: 'lowThreshold', - message: 'Low threshold must be between 0 and 100', - constraint: '0 <= lowThreshold <= 100' - }); - } - } - // Validate critical < low - if (settings.criticalThreshold !== undefined && settings.lowThreshold !== undefined) { - if (settings.criticalThreshold >= settings.lowThreshold) { - errors.push({ - field: 'thresholds', - message: 'Critical threshold must be less than low threshold', - constraint: 'criticalThreshold < lowThreshold' - }); - } - } - // Validate weights are in (0, 1] range - if (settings.accountWeights) { - for (const [alias, weight] of Object.entries(settings.accountWeights)) { - if (weight <= 0 || weight > 1) { - errors.push({ - field: `accountWeights.${alias}`, - message: `Weight for ${alias} must be between 0 and 1`, - constraint: '0 < weight <= 1' - }); - } - } - // Validate weights sum to approximately 1 - const totalWeight = Object.values(settings.accountWeights).reduce((sum, w) => sum + w, 0); - if (totalWeight > 0 && Math.abs(totalWeight - 1) > 0.01) { - errors.push({ - field: 'accountWeights', - message: 'Total weights must sum to 1.0', - constraint: 'sum(weights) ≈ 1.0' - }); - } - } - return errors; -} -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/dist/types.js.map b/dist/types.js.map deleted file mode 100644 index 6ab5731..0000000 --- a/dist/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/usage-limits.d.ts b/dist/usage-limits.d.ts deleted file mode 100644 index 08540da..0000000 --- a/dist/usage-limits.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { AccountCredentials, AccountRateLimits } from './types.js'; -export interface UsageRateLimitFetchResult { - rateLimits?: AccountRateLimits; - planType?: string; - rateLimitedUntil?: number; - error?: string; - shouldProbeFallback?: boolean; - authInvalid?: boolean; - workspaceDeactivated?: boolean; - workspaceDeactivatedReason?: string; - source: 'usage-api'; -} -interface UsageApiFailureClassification { - shouldProbeFallback: boolean; - authInvalid?: boolean; - workspaceDeactivated?: boolean; - workspaceDeactivatedReason?: string; -} -export declare function classifyUsageApiFailure(status: number, rawText: string): UsageApiFailureClassification; -export declare function fetchUsageRateLimitsForAccount(account: AccountCredentials): Promise; -export {}; -//# sourceMappingURL=usage-limits.d.ts.map \ No newline at end of file diff --git a/dist/usage-limits.d.ts.map b/dist/usage-limits.d.ts.map deleted file mode 100644 index c845721..0000000 --- a/dist/usage-limits.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"usage-limits.d.ts","sourceRoot":"","sources":["../src/usage-limits.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAmB,MAAM,YAAY,CAAA;AAoCxF,MAAM,WAAW,yBAAyB;IACxC,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,MAAM,EAAE,WAAW,CAAA;CACpB;AAED,UAAU,6BAA6B;IACrC,mBAAmB,EAAE,OAAO,CAAA;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,0BAA0B,CAAC,EAAE,MAAM,CAAA;CACpC;AAoED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,6BAA6B,CAuC/B;AAED,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,yBAAyB,CAAC,CAwGpC"} \ No newline at end of file diff --git a/dist/usage-limits.js b/dist/usage-limits.js deleted file mode 100644 index bc0bcc0..0000000 --- a/dist/usage-limits.js +++ /dev/null @@ -1,190 +0,0 @@ -import { getBlockingRateLimitResetAt, hasMeaningfulRateLimits } from './rate-limits.js'; -const DEFAULT_USAGE_BASE_URL = 'https://chatgpt.com/backend-api'; -const USAGE_BASE_URL_ENV = 'OPENCODE_MULTI_AUTH_USAGE_BASE_URL'; -function getUsageBaseUrl() { - const override = process.env[USAGE_BASE_URL_ENV]?.trim(); - const baseUrl = override || DEFAULT_USAGE_BASE_URL; - return baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; -} -function mapWindow(window, now) { - if (!window) - return undefined; - const usedPercent = typeof window.used_percent === 'number' ? window.used_percent : undefined; - const resetAt = typeof window.reset_at === 'number' - ? window.reset_at * 1000 - : typeof window.reset_after_seconds === 'number' - ? now + window.reset_after_seconds * 1000 - : undefined; - if (usedPercent === undefined && resetAt === undefined) { - return undefined; - } - return { - limit: 100, - remaining: typeof usedPercent === 'number' ? Math.max(0, 100 - usedPercent) : undefined, - resetAt, - updatedAt: now - }; -} -function pickRateLimitDetails(payload) { - if (payload.rate_limit) - return payload.rate_limit; - const additional = Array.isArray(payload.additional_rate_limits) ? payload.additional_rate_limits : []; - const preferred = additional.find((entry) => { - const feature = entry.metered_feature?.trim().toLowerCase(); - const limitName = entry.limit_name?.trim().toLowerCase(); - return feature === 'codex' || limitName === 'codex'; - }); - if (preferred?.rate_limit) - return preferred.rate_limit; - return additional.find((entry) => entry.rate_limit)?.rate_limit || null; -} -function parseUsageFailure(rawText) { - const trimmed = rawText.trim(); - if (!trimmed) { - return {}; - } - try { - const payload = JSON.parse(trimmed); - const code = (typeof payload?.detail?.code === 'string' && payload.detail.code) || - (typeof payload?.error?.code === 'string' && payload.error.code) || - undefined; - const message = (typeof payload?.detail?.message === 'string' && payload.detail.message) || - (typeof payload?.detail === 'string' && payload.detail) || - (typeof payload?.error?.message === 'string' && payload.error.message) || - (typeof payload?.message === 'string' && payload.message) || - undefined; - return { code, message }; - } - catch { - return { message: trimmed }; - } -} -export function classifyUsageApiFailure(status, rawText) { - const { code, message } = parseUsageFailure(rawText); - const normalized = [code, message, rawText.trim()] - .filter(Boolean) - .join(' ') - .toLowerCase(); - if (status === 401) { - return { - shouldProbeFallback: false, - authInvalid: true - }; - } - // 403 from the Codex usage endpoint is usually a Cloudflare gate, not proof - // that the OAuth token is invalid. Keep the account eligible and fall back to - // the probe path. - if (status === 403) { - return { - shouldProbeFallback: true, - authInvalid: false - }; - } - if (status === 402 && - (normalized.includes('deactivated_workspace') || - normalized.includes('deactivated workspace'))) { - return { - shouldProbeFallback: false, - workspaceDeactivated: true, - workspaceDeactivatedReason: message || code || rawText.trim() || undefined - }; - } - return { shouldProbeFallback: true }; -} -export async function fetchUsageRateLimitsForAccount(account) { - const token = account.accessToken?.trim(); - if (!token) { - return { - source: 'usage-api', - error: 'Missing access token' - }; - } - const url = `${getUsageBaseUrl()}/codex/usage`; - const headers = { - Authorization: `Bearer ${token}`, - 'User-Agent': 'codex_cli_rs/0.122.0 (linux)', - originator: 'codex_cli_rs' - }; - if (account.accountId) { - headers['ChatGPT-Account-Id'] = account.accountId; - } - const maxAttempts = 3; - let res; - let rawText = ''; - let lastErr; - for (let attempt = 0; attempt < maxAttempts; attempt++) { - try { - res = await fetch(url, { method: 'GET', headers }); - } - catch (err) { - lastErr = err; - if (attempt === maxAttempts - 1) { - return { - source: 'usage-api', - error: `Usage API request failed: ${err}` - }; - } - await new Promise((resolve) => setTimeout(resolve, 500 + attempt * 1500)); - continue; - } - try { - rawText = await res.text(); - } - catch { - rawText = ''; - } - const isCloudflareChallenge = res.status === 403 && - rawText.trimStart().slice(0, 16).toLowerCase().includes(' setTimeout(resolve, 1000 + attempt * 2000)); - } - if (!res) { - return { - source: 'usage-api', - error: `Usage API request failed: ${lastErr}` - }; - } - if (!res.ok) { - const trimmed = rawText.trim(); - const classification = classifyUsageApiFailure(res.status, rawText); - return { - source: 'usage-api', - error: `Usage API returned ${res.status}${trimmed ? `: ${trimmed.slice(0, 280)}` : ''}`, - ...classification - }; - } - let payload; - try { - payload = JSON.parse(rawText); - } - catch (err) { - return { - source: 'usage-api', - error: `Usage API returned invalid JSON: ${err}` - }; - } - const now = Date.now(); - const details = pickRateLimitDetails(payload); - const rateLimits = { - fiveHour: mapWindow(details?.primary_window, now), - weekly: mapWindow(details?.secondary_window, now) - }; - if (!hasMeaningfulRateLimits(rateLimits)) { - return { - source: 'usage-api', - planType: payload.plan_type, - error: 'Usage API response contained no usable rate limit windows' - }; - } - const rateLimitedUntil = details?.limit_reached || details?.allowed === false - ? getBlockingRateLimitResetAt(rateLimits, now) - : undefined; - return { - source: 'usage-api', - planType: payload.plan_type, - rateLimits, - rateLimitedUntil - }; -} -//# sourceMappingURL=usage-limits.js.map \ No newline at end of file diff --git a/dist/usage-limits.js.map b/dist/usage-limits.js.map deleted file mode 100644 index 05ef5bd..0000000 --- a/dist/usage-limits.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"usage-limits.js","sourceRoot":"","sources":["../src/usage-limits.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/web.d.ts b/dist/web.d.ts deleted file mode 100644 index b7205bf..0000000 --- a/dist/web.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as http from 'node:http'; -export declare function isLocalhostHost(host: string): boolean; -export declare function startWebConsole(options?: { - port?: number; - host?: string; -}): http.Server; -//# sourceMappingURL=web.d.ts.map \ No newline at end of file diff --git a/dist/web.d.ts.map b/dist/web.d.ts.map deleted file mode 100644 index ff885b0..0000000 --- a/dist/web.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AAoFjC,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AA0nGD,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,MAAM,CAyrBvF"} \ No newline at end of file diff --git a/dist/web.js b/dist/web.js deleted file mode 100644 index f41a7c4..0000000 --- a/dist/web.js +++ /dev/null @@ -1,3721 +0,0 @@ -import * as fs from 'node:fs'; -import * as http from 'node:http'; -import * as https from 'node:https'; -import * as os from 'node:os'; -import * as path from 'node:path'; -import { fileURLToPath, URL } from 'node:url'; -import { exec, spawn } from 'node:child_process'; -import { promisify } from 'node:util'; -import { createAuthorizationFlow, loginAccount, refreshToken } from './auth.js'; -import { getCodexAuthPath, getCodexAuthStatus, getCodexAuthSummary, resolveAliasForCurrentAuth, syncCodexAuthFile, writeCodexAuthForAlias } from './codex-auth.js'; -import { getStoreStatus, listAccounts, loadStore, removeAccount, updateAccount } from './store.js'; -import { getRefreshQueueState, startRefreshQueue, stopRefreshQueue } from './refresh-queue.js'; -import { getLogPath, logError, logInfo, readLogTail } from './logger.js'; -import { getForceState, activateForce, clearForce, isForceActive, getRemainingForceTimeMs, formatForceDuration } from './force-mode.js'; -import { getSettings, getRuntimeSettings, isFeatureEnabled } from './settings.js'; -import { Errors } from './errors.js'; -const DEFAULT_HOST = '127.0.0.1'; -const DEFAULT_PORT = 3434; -const LOCALHOST_HOST_PATTERN = /^(127\.0\.0\.1|::1|localhost)$/i; -const SYNC_INTERVAL_MS = 3000; -const SYNC_DEBOUNCE_MS = 600; -const ANTIGRAVITY_ACCOUNTS_FILE = path.join(os.homedir(), '.config', 'opencode', 'antigravity-accounts.json'); -const LOGIN_TIMEOUT_MS = 5 * 60 * 1000; -const AUTO_LOGIN_TIMEOUT_MS = 6 * 60 * 1000; -const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url)); -const AUTO_LOGIN_SCRIPT_ENV = 'OPENCODE_MULTI_AUTH_AUTO_LOGIN_SCRIPT'; -const AUTO_LOGIN_CREDENTIALS_ENV = 'OPENCODE_MULTI_AUTH_AUTO_LOGIN_CREDENTIALS_FILE'; -const AUTO_LOGIN_PYTHON_ENV = 'OPENCODE_MULTI_AUTH_AUTO_LOGIN_PYTHON'; -const DEFAULT_AUTO_LOGIN_CREDENTIALS_FILE = path.join(os.homedir(), '.config', 'opencode-multi-auth', 'credentials.json'); -const DEFAULT_AUTO_LOGIN_VENV_PYTHON = path.join(os.homedir(), '.config', 'opencode-multi-auth', '.venv', 'bin', 'python'); -export function isLocalhostHost(host) { - return LOCALHOST_HOST_PATTERN.test(host.trim()); -} -const execAsync = promisify(exec); -let lastSyncAt = 0; -let lastSyncError = null; -let lastSyncAlias = null; -let syncTimer = null; -let pendingLogin = null; -let lastLoginError = null; -let antigravityQuotaState = { status: 'idle', scope: 'active' }; -let antigravityQuotaInFlight = null; -let autoLoginChild = null; -const HTML = ` - - - - - Codex Token Dashboard - - - -
-
-
-
Codex Special Functions
-

Codex Token Dashboard

-
Add a new account from login + password, keep it in the local auto-login config, and watch the whole flow live.
-
- -
-
-
-
-
-
-
-
- - - - -
-
- - -
-
- - -
-
-
-
-
-
-
- - - - -
-
-
- - -
-
-
-
Force Mode
-
Pin rotation to a specific account for 24 hours
-
-
-
- - ? - -
-
- - - ? -
-
-
-
-
-
- - - -
-
-
-
Logs
-
-
- -
-

-      
-
- -
- - -`; -function sendJson(res, status, payload) { - const data = JSON.stringify(payload); - res.writeHead(status, { - 'Content-Type': 'application/json; charset=utf-8', - 'Content-Length': Buffer.byteLength(data) - }); - res.end(data); -} -function scrubAccount(account) { - const { accessToken, refreshToken, idToken, ...rest } = account; - return rest; -} -async function readJsonBody(req) { - return new Promise((resolve, reject) => { - let data = ''; - req.on('data', (chunk) => { - data += chunk; - if (data.length > 1_000_000) { - req.destroy(); - const payloadError = new Error('Payload too large'); - payloadError.code = 'PAYLOAD_TOO_LARGE'; - reject(payloadError); - } - }); - req.on('end', () => { - if (!data) { - resolve({}); - return; - } - try { - resolve(JSON.parse(data)); - } - catch { - const parseError = new Error('Invalid JSON payload'); - parseError.code = 'INVALID_JSON'; - reject(parseError); - } - }); - }); -} -function remainingPercent(window) { - if (!window || typeof window.remaining !== 'number' || typeof window.limit !== 'number') - return null; - if (window.limit === 0) - return null; - return Math.round((window.remaining / window.limit) * 100); -} -function recommendAlias(accounts) { - let best = null; - for (const account of accounts) { - if (account.enabled === false) { - continue; - } - const weeklyPercent = remainingPercent(account.rateLimits?.weekly) ?? -1; - const weeklyRemaining = typeof account.rateLimits?.weekly?.remaining === 'number' - ? account.rateLimits.weekly.remaining - : -1; - const fivePercent = remainingPercent(account.rateLimits?.fiveHour) ?? -1; - if (weeklyPercent < 0 && weeklyRemaining < 0 && fivePercent < 0) { - continue; - } - if (!best || - weeklyPercent > best.weeklyPercent || - (weeklyPercent === best.weeklyPercent && weeklyRemaining > best.weeklyRemaining) || - (weeklyPercent === best.weeklyPercent && - weeklyRemaining === best.weeklyRemaining && - fivePercent > best.fivePercent)) { - best = { alias: account.alias, weeklyPercent, weeklyRemaining, fivePercent }; - } - } - return best?.alias ?? null; -} -function getAutoLoginScriptPath() { - const override = process.env[AUTO_LOGIN_SCRIPT_ENV]; - if (override && override.trim()) - return path.resolve(override.trim()); - return path.resolve(MODULE_DIR, '..', 'auto-login', 'auto_login.py'); -} -function getAutoLoginCredentialsPath() { - const override = process.env[AUTO_LOGIN_CREDENTIALS_ENV]; - if (override && override.trim()) - return path.resolve(override.trim()); - return DEFAULT_AUTO_LOGIN_CREDENTIALS_FILE; -} -function getAutoLoginPythonPath() { - const override = process.env[AUTO_LOGIN_PYTHON_ENV]; - if (override && override.trim()) - return path.resolve(override.trim()); - if (fs.existsSync(DEFAULT_AUTO_LOGIN_VENV_PYTHON)) { - return DEFAULT_AUTO_LOGIN_VENV_PYTHON; - } - return 'python3'; -} -function sanitizeAliasSeed(value) { - const cleaned = value - .trim() - .toLowerCase() - .replace(/[^a-z0-9._-]+/g, '-') - .replace(/^-+|-+$/g, ''); - return cleaned || 'account'; -} -function ensureAutoLoginCredentialsDir(credentialsPath) { - const dir = path.dirname(credentialsPath); - fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); - try { - fs.chmodSync(dir, 0o700); - } - catch { - // Best effort on non-POSIX environments. - } -} -function readAutoLoginCredentialsFile(credentialsPath = getAutoLoginCredentialsPath()) { - if (!fs.existsSync(credentialsPath)) { - return { accounts: [] }; - } - const parsed = JSON.parse(fs.readFileSync(credentialsPath, 'utf-8')); - return { - defaults: parsed && typeof parsed.defaults === 'object' ? parsed.defaults : undefined, - accounts: Array.isArray(parsed?.accounts) ? parsed.accounts : [] - }; -} -function writeAutoLoginCredentialsFile(credentialsPath, data) { - ensureAutoLoginCredentialsDir(credentialsPath); - fs.writeFileSync(credentialsPath, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600 }); - try { - fs.chmodSync(credentialsPath, 0o600); - } - catch { - // Best effort on non-POSIX environments. - } -} -function upsertAutoLoginCredentials(input) { - const email = input.email.trim(); - if (!email) { - throw new Error('Missing login/email'); - } - const password = input.password.trim(); - if (!password) { - throw new Error('Missing password'); - } - const scriptPath = getAutoLoginScriptPath(); - if (!fs.existsSync(scriptPath)) { - throw new Error('auto_login.py not found'); - } - const credentialsPath = getAutoLoginCredentialsPath(); - const file = readAutoLoginCredentialsFile(credentialsPath); - const accounts = Array.isArray(file.accounts) ? [...file.accounts] : []; - const existingIndex = accounts.findIndex((entry) => typeof entry.email === 'string' && entry.email.toLowerCase() === email.toLowerCase()); - const existing = existingIndex >= 0 ? accounts[existingIndex] : undefined; - const aliasSource = input.alias?.trim() || - existing?.alias?.trim() || - existing?.id?.trim() || - email.split('@')[0] || - email; - const alias = sanitizeAliasSeed(aliasSource); - const chatgptPassword = input.chatgptPassword?.trim() || - existing?.chatgpt_password?.trim() || - file.defaults?.chatgpt_password?.trim() || - password; - const nextAccount = { - ...existing, - id: existing?.id || alias, - alias, - email, - outlook_password: password, - chatgpt_password: chatgptPassword, - enabled: true - }; - if (existingIndex >= 0) { - accounts[existingIndex] = nextAccount; - } - else { - accounts.push(nextAccount); - } - writeAutoLoginCredentialsFile(credentialsPath, { - ...file, - accounts - }); - return { - alias, - email, - enabled: true - }; -} -function loadAutoLoginConfig() { - const pathValue = getAutoLoginCredentialsPath(); - const scriptPath = getAutoLoginScriptPath(); - const result = { - path: pathValue, - scriptPath, - pythonPath: getAutoLoginPythonPath(), - configured: false, - accounts: [] - }; - if (!fs.existsSync(scriptPath)) { - return { ...result, error: 'auto_login.py not found' }; - } - if (!fs.existsSync(pathValue)) { - return { ...result, error: 'credentials.json not found' }; - } - try { - const parsed = JSON.parse(fs.readFileSync(pathValue, 'utf-8')); - const accounts = Array.isArray(parsed?.accounts) ? parsed.accounts : []; - const view = accounts - .map((entry) => { - const email = typeof entry.email === 'string' ? entry.email.trim() : ''; - if (!email) - return null; - const aliasSource = (typeof entry.alias === 'string' && entry.alias.trim()) || - (typeof entry.id === 'string' && entry.id.trim()) || - email.split('@')[0]; - return { - alias: sanitizeAliasSeed(aliasSource), - email, - enabled: entry.enabled !== false - }; - }) - .filter((entry) => Boolean(entry)) - .sort((a, b) => a.email.localeCompare(b.email)); - if (view.length === 0) { - return { ...result, error: 'No accounts in credentials.json' }; - } - return { - ...result, - configured: true, - accounts: view - }; - } - catch (err) { - return { ...result, error: `Failed to parse credentials.json: ${err}` }; - } -} -function findAutoLoginAccount(config, selector) { - const normalized = selector.trim().toLowerCase(); - if (!normalized) - return null; - return config.accounts.find((account) => account.email.toLowerCase() === normalized || account.alias.toLowerCase() === normalized) || null; -} -function resolveAutoLoginAlias(store, account) { - const existing = Object.values(store.accounts).find((entry) => typeof entry.email === 'string' && entry.email.toLowerCase() === account.email.toLowerCase()); - if (existing) { - return existing.alias; - } - const base = sanitizeAliasSeed(account.alias || account.email.split('@')[0]); - let candidate = base; - let index = 1; - while (store.accounts[candidate]) { - candidate = `${base}-${index}`; - index += 1; - } - return candidate; -} -function setPendingLogin(state) { - pendingLogin = state; -} -function updatePendingLogin(patch) { - if (!pendingLogin) - return; - pendingLogin = { - ...pendingLogin, - ...patch, - output: patch.output ?? pendingLogin.output - }; -} -function appendPendingLoginOutput(line) { - const normalized = line - .replace(/\x1b\[[0-9;]*m/g, '') - .trim(); - if (!normalized || !pendingLogin) - return; - const output = [...pendingLogin.output, normalized].slice(-6); - updatePendingLogin({ - output, - step: normalized, - status: normalized.toLowerCase().includes('callback') ? 'waiting-callback' : 'running' - }); -} -function stopAutoLoginChild() { - if (autoLoginChild && !autoLoginChild.killed) { - autoLoginChild.kill('SIGTERM'); - } - autoLoginChild = null; -} -function consumeProcessLines(stream, onLine) { - if (!stream) - return; - let buffered = ''; - stream.setEncoding('utf8'); - stream.on('data', (chunk) => { - buffered += chunk; - let newlineIndex = buffered.indexOf('\n'); - while (newlineIndex >= 0) { - const line = buffered.slice(0, newlineIndex).trim(); - buffered = buffered.slice(newlineIndex + 1); - if (line) - onLine(line); - newlineIndex = buffered.indexOf('\n'); - } - }); - stream.on('end', () => { - const line = buffered.trim(); - if (line) - onLine(line); - }); -} -function startManualLogin(alias) { - if (pendingLogin) { - throw new Error(`Login already in progress for ${pendingLogin.alias}`); - } - return createAuthorizationFlow().then((flow) => { - setPendingLogin({ - alias, - startedAt: Date.now(), - url: flow.url, - mode: 'manual', - status: 'running', - step: 'Waiting for browser login...', - output: [] - }); - lastLoginError = null; - loginAccount(alias, flow, { timeoutMs: LOGIN_TIMEOUT_MS }) - .then(() => { - logInfo(`Login completed for ${alias}`); - setPendingLogin(null); - }) - .catch((err) => { - lastLoginError = String(err); - logError(`Login failed for ${alias}: ${err}`); - setPendingLogin(null); - }); - return { ok: true, url: flow.url }; - }); -} -async function startAutoLogin(selector, visible = false) { - if (pendingLogin) { - throw new Error(`Login already in progress for ${pendingLogin.alias}`); - } - const config = loadAutoLoginConfig(); - if (!config.configured) { - throw new Error(config.error || 'Auto-login is not configured'); - } - const selected = findAutoLoginAccount(config, selector); - if (!selected) { - throw new Error('Unknown auto-login account'); - } - if (!selected.enabled) { - throw new Error('Selected auto-login account is disabled'); - } - const store = loadStore(); - const alias = resolveAutoLoginAlias(store, selected); - const flow = await createAuthorizationFlow(); - setPendingLogin({ - alias, - email: selected.email, - startedAt: Date.now(), - url: flow.url, - mode: 'auto', - status: 'starting', - step: 'Launching browser automation (a Chrome window may open)...', - output: [] - }); - lastLoginError = null; - let loginSettled = false; - const loginPromise = loginAccount(alias, flow, { timeoutMs: AUTO_LOGIN_TIMEOUT_MS }) - .then(() => { - loginSettled = true; - stopAutoLoginChild(); - logInfo(`Auto-login completed for ${alias} (${selected.email})`); - setPendingLogin(null); - }) - .catch((err) => { - loginSettled = true; - stopAutoLoginChild(); - lastLoginError = String(err); - logError(`Auto-login failed for ${alias} (${selected.email}): ${err}`); - setPendingLogin(null); - }); - autoLoginChild = spawn(config.pythonPath, [ - '-u', - config.scriptPath, - '--email', - selected.email, - '--auth-url', - flow.url, - '--credentials-file', - config.path, - ...(visible ? ['--visible'] : []) - ], { - stdio: ['ignore', 'pipe', 'pipe'] - }); - updatePendingLogin({ - pid: autoLoginChild.pid ?? undefined, - status: 'running' - }); - consumeProcessLines(autoLoginChild.stdout, (line) => { - appendPendingLoginOutput(line); - }); - consumeProcessLines(autoLoginChild.stderr, (line) => { - appendPendingLoginOutput(`[stderr] ${line}`); - }); - autoLoginChild.on('error', (err) => { - appendPendingLoginOutput(`Process error: ${err.message}`); - }); - autoLoginChild.on('exit', (code, signal) => { - if (!pendingLogin || pendingLogin.mode !== 'auto' || pendingLogin.alias !== alias) { - autoLoginChild = null; - return; - } - autoLoginChild = null; - if (loginSettled) { - return; - } - if (code === 0) { - updatePendingLogin({ - status: 'waiting-callback', - step: 'Browser automation finished. Waiting for local callback...' - }); - return; - } - const reason = signal ? `signal ${signal}` : `code ${code ?? 'unknown'}`; - appendPendingLoginOutput(`Browser automation exited with ${reason}.`); - lastLoginError = `Auto-login browser automation exited with ${reason}`; - updatePendingLogin({ - status: 'waiting-callback', - step: 'Browser automation stopped before callback. Complete login manually or wait for timeout.' - }); - }); - void loginPromise; - return { - ok: true, - alias, - email: selected.email, - url: flow.url - }; -} -async function saveAutoLoginAccountAndStart(input) { - const account = upsertAutoLoginCredentials(input); - return startAutoLogin(account.email); -} -function runSync() { - try { - const result = syncCodexAuthFile(); - const authStatus = getCodexAuthStatus(); - lastSyncAt = Date.now(); - lastSyncError = authStatus.error; - lastSyncAlias = result.alias ?? null; - if (result.updated || result.added) { - logInfo(`Synced auth.json (${result.alias ?? 'none'})`); - } - if (authStatus.error) { - logError(authStatus.error); - } - } - catch (err) { - lastSyncError = String(err); - logError(`Sync failed: ${lastSyncError}`); - } -} -function loadAntigravityAccounts() { - const result = { path: ANTIGRAVITY_ACCOUNTS_FILE, accounts: [], readAt: Date.now() }; - if (!fs.existsSync(ANTIGRAVITY_ACCOUNTS_FILE)) { - return { ...result, error: 'antigravity-accounts.json not found' }; - } - try { - const raw = JSON.parse(fs.readFileSync(ANTIGRAVITY_ACCOUNTS_FILE, 'utf-8')); - const activeIndex = typeof raw?.activeIndex === 'number' ? raw.activeIndex : undefined; - const accounts = Array.isArray(raw?.accounts) ? raw.accounts : []; - const view = accounts.map((acc, index) => ({ - index, - alias: acc?.projectId || acc?.managedProjectId, - projectId: acc?.projectId, - managedProjectId: acc?.managedProjectId, - addedAt: acc?.addedAt, - lastUsed: acc?.lastUsed, - hasRefreshToken: Boolean(acc?.refreshToken), - rateLimitResetTimes: acc?.rateLimitResetTimes && typeof acc.rateLimitResetTimes === 'object' - ? acc.rateLimitResetTimes - : undefined - })); - return { ...result, activeIndex, accounts: view }; - } - catch (err) { - return { ...result, error: `Failed to parse antigravity accounts: ${err}` }; - } -} -function isAntigravityProcessLine(line) { - const lower = line.toLowerCase(); - if (lower.includes('antigravity')) - return true; - return /--app_data_dir\s+antigravity\b/i.test(line); -} -function getAntigravityProcessName() { - if (process.platform === 'darwin') { - return `language_server_macos${process.arch === 'arm64' ? '_arm' : ''}`; - } - if (process.platform === 'linux') { - return `language_server_linux${process.arch === 'arm64' ? '_arm' : '_x64'}`; - } - if (process.platform === 'win32') { - return 'language_server_windows_x64.exe'; - } - return null; -} -async function detectAntigravityProcessInfo() { - const processName = getAntigravityProcessName(); - if (!processName) { - throw new Error('Unsupported platform for Antigravity quotas'); - } - if (process.platform === 'win32') { - throw new Error('Antigravity quota detection is not implemented for Windows yet'); - } - const cmd = process.platform === 'darwin' ? `pgrep -fl ${processName}` : `pgrep -af ${processName}`; - const { stdout } = await execAsync(cmd); - const lines = stdout - .split('\n') - .map((line) => line.trim()) - .filter(Boolean); - const tokenLines = lines.filter((line) => line.includes('--csrf_token')); - if (tokenLines.length === 0) { - return null; - } - const ordered = [ - ...tokenLines.filter(isAntigravityProcessLine), - ...tokenLines.filter((line) => !isAntigravityProcessLine(line)) - ]; - for (const line of ordered) { - const parts = line.split(/\s+/); - const pid = Number(parts[0]); - if (!Number.isFinite(pid)) { - continue; - } - const portMatch = line.match(/--extension_server_port[=\s]+(\d+)/); - const tokenMatch = line.match(/--csrf_token[=\s]+([a-zA-Z0-9\-]+)/); - if (!tokenMatch) { - continue; - } - const extensionPort = portMatch ? Number(portMatch[1]) : 0; - const csrfToken = tokenMatch[1]; - const ports = await listListeningPorts(pid); - const workingPort = await findWorkingPort(ports, csrfToken); - const connectPort = workingPort || (extensionPort > 0 ? extensionPort : 0); - if (!connectPort) { - return null; - } - return { pid, extensionPort, csrfToken, connectPort }; - } - return null; -} -async function listListeningPorts(pid) { - try { - const cmd = `lsof -nP -a -iTCP -sTCP:LISTEN -p ${pid}`; - const { stdout } = await execAsync(cmd); - const ports = new Set(); - const regex = /:(\d+)\s+\(LISTEN\)/g; - let match; - while ((match = regex.exec(stdout)) !== null) { - const port = Number(match[1]); - if (Number.isFinite(port)) { - ports.add(port); - } - } - return Array.from(ports.values()).sort((a, b) => a - b); - } - catch { - return []; - } -} -function antigravityRequest(port, csrfToken, pathName, body) { - return new Promise((resolve, reject) => { - const payload = JSON.stringify(body); - const req = https.request({ - hostname: '127.0.0.1', - port, - path: pathName, - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(payload), - 'Connect-Protocol-Version': '1', - 'X-Codeium-Csrf-Token': csrfToken - }, - rejectUnauthorized: false, - timeout: 5000 - }, (res) => { - let data = ''; - res.on('data', (chunk) => { data += chunk; }); - res.on('end', () => { - try { - resolve(JSON.parse(data)); - } - catch { - reject(new Error('Invalid JSON response')); - } - }); - }); - req.on('error', reject); - req.on('timeout', () => { - req.destroy(); - reject(new Error('Request timeout')); - }); - req.write(payload); - req.end(); - }); -} -async function testAntigravityPort(port, csrfToken) { - try { - await antigravityRequest(port, csrfToken, '/exa.language_server_pb.LanguageServerService/GetUnleashData', { wrapper_data: {} }); - return true; - } - catch { - return false; - } -} -async function findWorkingPort(ports, csrfToken) { - for (const port of ports) { - const ok = await testAntigravityPort(port, csrfToken); - if (ok) - return port; - } - return null; -} -function formatAntigravityDuration(ms, resetTime) { - if (!Number.isFinite(ms) || ms <= 0) - return 'Ready'; - const mins = Math.ceil(ms / 60000); - let duration = ''; - if (mins < 60) { - duration = `${mins}m`; - } - else { - const hours = Math.floor(mins / 60); - duration = `${hours}h ${mins % 60}m`; - } - if (!resetTime) - return duration; - const resetDate = new Date(resetTime); - const dateStr = resetDate.toLocaleDateString(undefined, { day: '2-digit', month: '2-digit', year: 'numeric' }); - const timeStr = resetDate.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', hour12: false }); - return `${duration} (${dateStr} ${timeStr})`; -} -function updateAntigravityActiveIndex(raw, index) { - const next = { ...raw }; - next.activeIndex = index; - if (next.activeIndexByFamily && typeof next.activeIndexByFamily === 'object') { - next.activeIndexByFamily = { ...next.activeIndexByFamily }; - for (const key of Object.keys(next.activeIndexByFamily)) { - next.activeIndexByFamily[key] = index; - } - } - return next; -} -async function refreshAntigravityQuotaAll() { - if (antigravityQuotaInFlight) { - return antigravityQuotaInFlight; - } - antigravityQuotaInFlight = (async () => { - let originalRaw = ''; - let parsed; - try { - if (!fs.existsSync(ANTIGRAVITY_ACCOUNTS_FILE)) { - throw new Error('antigravity-accounts.json not found'); - } - originalRaw = fs.readFileSync(ANTIGRAVITY_ACCOUNTS_FILE, 'utf-8'); - parsed = JSON.parse(originalRaw); - const accounts = Array.isArray(parsed?.accounts) ? parsed.accounts : []; - if (accounts.length === 0) { - throw new Error('No Antigravity accounts available'); - } - const perAccount = {}; - for (let index = 0; index < accounts.length; index += 1) { - const next = updateAntigravityActiveIndex(parsed, index); - fs.writeFileSync(ANTIGRAVITY_ACCOUNTS_FILE, JSON.stringify(next, null, 2)); - await new Promise((resolve) => setTimeout(resolve, 500)); - const snapshot = await fetchAntigravityQuota(); - perAccount[index] = snapshot; - } - antigravityQuotaState = { - status: 'ok', - scope: 'all', - fetchedAt: Date.now(), - snapshot: perAccount[parsed.activeIndex ?? 0] || perAccount[0], - perAccount - }; - } - catch (err) { - antigravityQuotaState = { - status: 'error', - scope: 'all', - fetchedAt: Date.now(), - error: String(err), - perAccount: antigravityQuotaState.perAccount - }; - } - finally { - if (originalRaw) { - try { - fs.writeFileSync(ANTIGRAVITY_ACCOUNTS_FILE, originalRaw); - } - catch (err) { - logError(`Failed to restore antigravity accounts file: ${err}`); - } - } - antigravityQuotaInFlight = null; - } - return antigravityQuotaState; - })(); - return antigravityQuotaInFlight; -} -function parseAntigravityQuota(data) { - const userStatus = data?.userStatus; - const planInfo = userStatus?.planStatus?.planInfo; - const availableCredits = userStatus?.planStatus?.availablePromptCredits; - let promptCredits; - if (planInfo && availableCredits !== undefined) { - const monthly = Number(planInfo.monthlyPromptCredits); - const available = Number(availableCredits); - if (Number.isFinite(monthly) && monthly > 0) { - promptCredits = { - available, - monthly, - usedPercentage: ((monthly - available) / monthly) * 100, - remainingPercentage: (available / monthly) * 100 - }; - } - } - const rawModels = userStatus?.cascadeModelConfigData?.clientModelConfigs || []; - const models = rawModels - .filter((model) => model?.quotaInfo) - .map((model) => { - const reset = model.quotaInfo?.resetTime ? new Date(model.quotaInfo.resetTime) : null; - const resetTime = reset ? reset.getTime() : undefined; - const remainingFraction = typeof model.quotaInfo?.remainingFraction === 'number' - ? model.quotaInfo.remainingFraction - : undefined; - const remainingPercentage = typeof remainingFraction === 'number' - ? remainingFraction * 100 - : undefined; - const diff = resetTime ? resetTime - Date.now() : undefined; - const label = model.label || model.modelOrAlias?.model || 'model'; - const modelId = model.modelOrAlias?.model || model.modelOrAlias?.alias || 'unknown'; - return { - label, - modelId, - remainingFraction, - remainingPercentage, - isExhausted: remainingFraction === 0, - resetTime, - timeUntilResetMs: diff, - timeUntilResetFormatted: typeof diff === 'number' ? formatAntigravityDuration(diff, resetTime) : undefined - }; - }); - return { - timestamp: Date.now(), - name: userStatus?.name, - email: userStatus?.email, - promptCredits, - models - }; -} -async function fetchAntigravityQuota() { - const info = await detectAntigravityProcessInfo(); - if (!info) { - throw new Error('Antigravity process not found'); - } - const data = await antigravityRequest(info.connectPort, info.csrfToken, '/exa.language_server_pb.LanguageServerService/GetUserStatus', { - metadata: { - ideName: 'antigravity', - extensionName: 'antigravity', - locale: 'en' - } - }); - return parseAntigravityQuota(data); -} -async function refreshAntigravityQuota() { - if (antigravityQuotaInFlight) { - return antigravityQuotaInFlight; - } - antigravityQuotaInFlight = (async () => { - try { - const snapshot = await fetchAntigravityQuota(); - antigravityQuotaState = { - status: 'ok', - scope: 'active', - fetchedAt: Date.now(), - snapshot - }; - } - catch (err) { - antigravityQuotaState = { - status: 'error', - scope: 'active', - fetchedAt: Date.now(), - error: String(err) - }; - } - finally { - antigravityQuotaInFlight = null; - } - return antigravityQuotaState; - })(); - return antigravityQuotaInFlight; -} -function scheduleSync() { - if (syncTimer) { - clearTimeout(syncTimer); - } - syncTimer = setTimeout(() => { - runSync(); - }, SYNC_DEBOUNCE_MS); -} -function startAuthWatcher() { - const authPath = getCodexAuthPath(); - fs.watchFile(authPath, { interval: SYNC_INTERVAL_MS }, () => { - scheduleSync(); - }); -} -export function startWebConsole(options) { - const host = options?.host || DEFAULT_HOST; - const port = options?.port || DEFAULT_PORT; - if (!isLocalhostHost(host)) { - const err = Errors.localhostOnly(host); - throw new Error(`${err.code}: ${err.message}`); - } - runSync(); - startAuthWatcher(); - const server = http.createServer(async (req, res) => { - const requestUrl = new URL(req.url || '/', `http://${host}:${port}`); - const path = requestUrl.pathname; - try { - if (req.method === 'GET' && path === '/') { - res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); - res.end(HTML); - return; - } - if (req.method === 'GET' && path === '/api/state') { - runSync(); - const store = loadStore(); - const rawAccounts = Object.values(store.accounts); - const accounts = rawAccounts.map(scrubAccount); - const deviceAlias = resolveAliasForCurrentAuth(store); - const authSummary = getCodexAuthSummary(); - const storeStatus = getStoreStatus(); - // Phase G: Only load antigravity if feature is enabled - const settings = getSettings(); - const runtimeSettings = getRuntimeSettings(); - const antigravityEnabled = settings.settings.featureFlags?.antigravityEnabled ?? false; - const antigravity = antigravityEnabled ? loadAntigravityAccounts() : { accounts: [], path: ANTIGRAVITY_ACCOUNTS_FILE }; - const forceState = getForceState(); - const forceActive = isForceActive(); - const autoLogin = loadAutoLoginConfig(); - sendJson(res, 200, { - authPath: getCodexAuthPath(), - deviceAlias, - rotationAlias: store.activeAlias, - accounts, - lastSyncAt, - lastSyncError, - lastSyncAlias, - authSummary, - storeStatus, - login: pendingLogin, - lastLoginError, - // Phase G: Only include antigravity data if feature is enabled - antigravity: antigravityEnabled ? { ...antigravity, quota: antigravityQuotaState } : { accounts: [], path: ANTIGRAVITY_ACCOUNTS_FILE, quota: { status: 'disabled', scope: 'active' } }, - queue: getRefreshQueueState(), - recommendedAlias: recommendAlias(rawAccounts), - logPath: getLogPath(), - autoLogin, - rotationStrategy: runtimeSettings.settings.rotationStrategy, - force: { - active: forceActive, - alias: forceState.forcedAlias, - forcedUntil: forceState.forcedUntil, - forcedBy: forceState.forcedBy, - remainingMs: getRemainingForceTimeMs(), - remainingTime: formatForceDuration(getRemainingForceTimeMs()) - }, - // Phase G: Include feature flags in state - featureFlags: settings.settings.featureFlags || { antigravityEnabled: false } - }); - return; - } - if (req.method === 'GET' && path === '/api/logs') { - const limitParam = requestUrl.searchParams.get('limit'); - const limit = limitParam ? Number(limitParam) : undefined; - const lines = readLogTail(Number.isFinite(limit) ? limit : undefined); - sendJson(res, 200, { path: getLogPath(), lines }); - return; - } - if (req.method === 'POST' && path === '/api/sync') { - try { - runSync(); - sendJson(res, 200, { ok: true }); - } - catch (err) { - sendJson(res, 500, { error: String(err) }); - } - return; - } - if (req.method === 'POST' && path === '/api/auth/start') { - const body = await readJsonBody(req); - const alias = typeof body.alias === 'string' ? body.alias.trim() : ''; - if (!alias) { - sendJson(res, 400, { error: 'Missing alias' }); - return; - } - if (pendingLogin) { - sendJson(res, 409, { error: `Login already in progress for ${pendingLogin.alias}` }); - return; - } - try { - const result = await startManualLogin(alias); - sendJson(res, 200, result); - } - catch (err) { - lastLoginError = String(err); - sendJson(res, 500, { error: String(err) }); - } - return; - } - if (req.method === 'POST' && path === '/api/auto-login/start') { - const body = await readJsonBody(req); - const selector = typeof body.selector === 'string' ? body.selector.trim() : ''; - if (!selector) { - sendJson(res, 400, { error: 'Missing selector' }); - return; - } - try { - const result = await startAutoLogin(selector, body.visible === true); - sendJson(res, 200, result); - } - catch (err) { - lastLoginError = String(err); - sendJson(res, 500, { error: String(err) }); - } - return; - } - if (req.method === 'POST' && path === '/api/auto-login/add') { - const body = await readJsonBody(req); - const email = typeof body.email === 'string' ? body.email.trim() : ''; - const password = typeof body.password === 'string' ? body.password : ''; - const alias = typeof body.alias === 'string' ? body.alias.trim() : ''; - const chatgptPassword = typeof body.chatgptPassword === 'string' ? body.chatgptPassword : ''; - if (!email) { - sendJson(res, 400, { error: 'Missing login/email' }); - return; - } - if (!password.trim()) { - sendJson(res, 400, { error: 'Missing password' }); - return; - } - try { - const result = await saveAutoLoginAccountAndStart({ - email, - password, - alias, - chatgptPassword - }); - sendJson(res, 200, result); - } - catch (err) { - lastLoginError = String(err); - sendJson(res, 500, { error: String(err) }); - } - return; - } - if (req.method === 'POST' && path === '/api/switch') { - const body = await readJsonBody(req); - if (!body.alias) { - sendJson(res, 400, { error: 'Missing alias' }); - return; - } - try { - writeCodexAuthForAlias(body.alias); - sendJson(res, 200, { ok: true }); - } - catch (err) { - sendJson(res, 400, { error: String(err) }); - } - return; - } - if (req.method === 'POST' && path === '/api/remove') { - const body = await readJsonBody(req); - if (!body.alias) { - sendJson(res, 400, { error: 'Missing alias' }); - return; - } - removeAccount(body.alias); - sendJson(res, 200, { ok: true }); - return; - } - if (req.method === 'POST' && path === '/api/account/meta') { - const body = await readJsonBody(req); - if (!body.alias) { - sendJson(res, 400, { error: 'Missing alias' }); - return; - } - const tags = typeof body.tags === 'string' - ? body.tags - .split(',') - .map((tag) => tag.trim().toLowerCase()) - .filter(Boolean) - : []; - const uniqueTags = Array.from(new Set(tags)); - const notes = typeof body.notes === 'string' ? body.notes.trim() : ''; - updateAccount(body.alias, { - tags: uniqueTags.length > 0 ? uniqueTags : undefined, - notes: notes || undefined - }); - sendJson(res, 200, { ok: true }); - return; - } - if (req.method === 'POST' && path === '/api/token/refresh') { - const body = await readJsonBody(req); - const store = loadStore(); - const candidates = Object.values(store.accounts); - const alias = typeof body.alias === 'string' ? body.alias : undefined; - const targets = alias ? candidates.filter((acc) => acc.alias === alias) : candidates; - if (alias && targets.length === 0) { - sendJson(res, 400, { error: 'Unknown alias' }); - return; - } - const deviceAlias = resolveAliasForCurrentAuth(store); - const results = []; - for (const account of targets) { - if (!account.refreshToken) { - results.push({ alias: account.alias, updated: false, error: 'No refresh token' }); - continue; - } - const refreshed = await refreshToken(account.alias); - if (!refreshed) { - results.push({ alias: account.alias, updated: false, error: 'Token refresh failed' }); - continue; - } - if (deviceAlias === account.alias) { - try { - writeCodexAuthForAlias(account.alias); - } - catch (err) { - results.push({ alias: account.alias, updated: true, error: `Refreshed, but failed to update auth.json: ${err}` }); - continue; - } - } - results.push({ alias: account.alias, updated: true }); - } - sendJson(res, 200, { ok: true, results }); - return; - } - if (req.method === 'POST' && path === '/api/limits/refresh') { - const body = await readJsonBody(req); - const accounts = listAccounts().filter((acc) => acc.refreshToken && acc.accessToken); - if (body.alias && !accounts.find((acc) => acc.alias === body.alias)) { - sendJson(res, 400, { error: 'Unknown alias' }); - return; - } - const queue = startRefreshQueue(accounts, body.alias); - sendJson(res, 200, { ok: true, queue }); - return; - } - if (req.method === 'POST' && path === '/api/limits/stop') { - stopRefreshQueue(); - sendJson(res, 200, { ok: true }); - return; - } - // Phase G: Antigravity endpoints - check feature flag - if (req.method === 'POST' && path === '/api/antigravity/refresh') { - // Check if antigravity feature is enabled - if (!isFeatureEnabled('antigravityEnabled')) { - sendJson(res, 403, { - error: 'Antigravity feature is disabled', - code: 'FEATURE_DISABLED', - feature: 'antigravity' - }); - return; - } - await refreshAntigravityQuota(); - sendJson(res, 200, { ok: true, quota: antigravityQuotaState }); - return; - } - if (req.method === 'POST' && path === '/api/antigravity/refresh-all') { - // Check if antigravity feature is enabled - if (!isFeatureEnabled('antigravityEnabled')) { - sendJson(res, 403, { - error: 'Antigravity feature is disabled', - code: 'FEATURE_DISABLED', - feature: 'antigravity' - }); - return; - } - await refreshAntigravityQuotaAll(); - sendJson(res, 200, { ok: true, quota: antigravityQuotaState }); - return; - } - // Phase D: Account Lifecycle API Endpoints - // GET /api/accounts - List all accounts with metadata - if (req.method === 'GET' && path === '/api/accounts') { - const store = loadStore(); - const accounts = Object.values(store.accounts).map(acc => ({ - alias: acc.alias, - email: acc.email, - enabled: acc.enabled !== false, // Defaults to true - disabledAt: acc.disabledAt, - disabledBy: acc.disabledBy, - disableReason: acc.disableReason, - usageCount: acc.usageCount, - rateLimits: acc.rateLimits, - limitsConfidence: acc.limitsConfidence, - limitStatus: acc.limitStatus, - limitError: acc.limitError, - lastLimitProbeAt: acc.lastLimitProbeAt, - lastLimitErrorAt: acc.lastLimitErrorAt, - tags: acc.tags, - notes: acc.notes - })); - sendJson(res, 200, { accounts }); - return; - } - // PUT /api/accounts/:alias/enabled - Enable/disable an account - if (req.method === 'PUT' && path.startsWith('/api/accounts/') && path.endsWith('/enabled')) { - const aliasMatch = path.match(/^\/api\/accounts\/([^\/]+)\/enabled$/); - if (!aliasMatch) { - sendJson(res, 400, { error: 'Invalid path format' }); - return; - } - const alias = aliasMatch[1]; - const store = loadStore(); - if (!store.accounts[alias]) { - sendJson(res, 404, { error: 'Unknown alias', code: 'ACCOUNT_NOT_FOUND' }); - return; - } - const body = await readJsonBody(req); - const enabled = body.enabled === true; - // Phase D: Prevent disabling the last enabled account - if (!enabled) { - const enabledCount = Object.values(store.accounts).filter(acc => acc.alias !== alias && acc.enabled !== false).length; - if (enabledCount === 0) { - sendJson(res, 409, { - error: 'Cannot disable the last enabled account', - code: 'LAST_ACCOUNT' - }); - return; - } - } - // Phase D: Double-submit protection - check if already in desired state - const currentEnabled = store.accounts[alias].enabled !== false; - if (currentEnabled === enabled) { - sendJson(res, 409, { - error: enabled ? 'Account is already enabled' : 'Account is already disabled', - code: 'ALREADY_IN_STATE' - }); - return; - } - const updates = { enabled }; - if (!enabled) { - updates.disabledAt = Date.now(); - updates.disabledBy = 'dashboard'; // Could be expanded to track actor - } - else { - // Clear disable metadata when enabling - updates.disabledAt = undefined; - updates.disabledBy = undefined; - updates.disableReason = undefined; - } - updateAccount(alias, updates); - logInfo(`Account ${alias} ${enabled ? 'enabled' : 'disabled'} via dashboard`); - sendJson(res, 200, { - ok: true, - alias, - enabled, - disabledAt: updates.disabledAt, - disabledBy: updates.disabledBy - }); - return; - } - // POST /api/accounts/:alias/reauth - Re-authenticate an account - if (req.method === 'POST' && path.startsWith('/api/accounts/') && path.endsWith('/reauth')) { - const aliasMatch = path.match(/^\/api\/accounts\/([^\/]+)\/reauth$/); - if (!aliasMatch) { - sendJson(res, 400, { error: 'Invalid path format' }); - return; - } - const alias = aliasMatch[1]; - const store = loadStore(); - if (!store.accounts[alias]) { - sendJson(res, 404, { error: 'Unknown alias', code: 'ACCOUNT_NOT_FOUND' }); - return; - } - // Phase D: Cannot re-auth a disabled account - if (store.accounts[alias].enabled === false) { - sendJson(res, 409, { - error: 'Cannot re-authenticate a disabled account', - code: 'ACCOUNT_DISABLED' - }); - return; - } - // Phase D: Only targeted alias credentials mutate - // Start OAuth flow for the specific alias - try { - if (pendingLogin) { - sendJson(res, 409, { error: `Login already in progress for ${pendingLogin.alias}` }); - return; - } - const flow = await createAuthorizationFlow(); - const body = await readJsonBody(req); - const actor = body.actor || 'dashboard'; - loginAccount(alias, flow, { timeoutMs: LOGIN_TIMEOUT_MS }) - .then(() => { - logInfo(`Re-auth completed for ${alias} by ${actor}`); - // Update account metadata - updateAccount(alias, { - lastRefresh: new Date().toISOString() - }); - }) - .catch((err) => { - logError(`Re-auth failed for ${alias}: ${err}`); - }); - sendJson(res, 200, { - ok: true, - alias, - url: flow.url, - message: 'OAuth flow started. Complete authentication in the browser.' - }); - } - catch (err) { - sendJson(res, 500, { error: String(err), code: 'AUTH_FLOW_ERROR' }); - } - return; - } - // Phase E: Force Mode API endpoints - // GET /api/force - Get current force state - if (req.method === 'GET' && path === '/api/force') { - const forceState = getForceState(); - const active = isForceActive(); - const remainingMs = getRemainingForceTimeMs(); - sendJson(res, 200, { - active, - alias: forceState.forcedAlias, - forcedAt: forceState.forcedAlias && forceState.forcedUntil - ? forceState.forcedUntil - (24 * 60 * 60 * 1000) - : null, - forcedUntil: forceState.forcedUntil, - forcedBy: forceState.forcedBy, - remainingMs, - remainingTime: formatForceDuration(remainingMs), - previousRotationStrategy: forceState.previousRotationStrategy - }); - return; - } - // POST /api/force - Activate force mode for an alias - if (req.method === 'POST' && path === '/api/force') { - const body = await readJsonBody(req); - const alias = typeof body.alias === 'string' ? body.alias.trim() : ''; - const actor = typeof body.actor === 'string' ? body.actor.trim() : 'api'; - if (!alias) { - sendJson(res, 400, { error: 'Missing alias', code: 'MISSING_ALIAS' }); - return; - } - const result = activateForce(alias, actor); - if (!result.success) { - const statusCode = result.error?.includes('not found') ? 404 - : result.error?.includes('disabled') ? 409 - : 400; - sendJson(res, statusCode, { error: result.error, code: 'FORCE_FAILED' }); - return; - } - logInfo(`Force mode activated for ${alias} by ${actor}`); - sendJson(res, 200, { - ok: true, - alias, - forcedUntil: result.state?.forcedUntil, - remainingMs: result.state?.forcedUntil ? result.state.forcedUntil - Date.now() : 0, - remainingTime: result.state?.forcedUntil - ? formatForceDuration(result.state.forcedUntil - Date.now()) - : '0m', - previousRotationStrategy: result.state?.previousRotationStrategy - }); - return; - } - // POST /api/force/clear - Deactivate force mode - if (req.method === 'POST' && path === '/api/force/clear') { - const result = clearForce(); - if (result.success) { - logInfo('Force mode cleared'); - sendJson(res, 200, { - ok: true, - restoredStrategy: result.restoredStrategy - }); - } - else { - sendJson(res, 500, { error: 'Failed to clear force mode', code: 'CLEAR_FAILED' }); - } - return; - } - // Phase F: Settings API Endpoints - // GET /api/settings - Get current settings - if (req.method === 'GET' && path === '/api/settings') { - const { getSettingsWithInfo } = await import('./settings.js'); - const info = getSettingsWithInfo(); - sendJson(res, 200, { - settings: info.settings, - source: info.source, - preset: info.preset, - canReset: info.canReset - }); - return; - } - // PUT /api/settings - Update settings - if (req.method === 'PUT' && path === '/api/settings') { - const body = await readJsonBody(req); - const { updateSettings } = await import('./settings.js'); - const actor = body.actor || 'dashboard'; - const updates = {}; - if (body.rotationStrategy) { - updates.rotationStrategy = body.rotationStrategy; - } - if (typeof body.criticalThreshold === 'number') { - updates.criticalThreshold = body.criticalThreshold; - } - if (typeof body.lowThreshold === 'number') { - updates.lowThreshold = body.lowThreshold; - } - if (body.accountWeights) { - updates.accountWeights = body.accountWeights; - } - // Phase G: Handle feature flags - if (body.featureFlags && typeof body.featureFlags === 'object') { - updates.featureFlags = body.featureFlags; - } - const result = updateSettings(updates, actor); - if (result.success) { - sendJson(res, 200, { - ok: true, - settings: result.settings - }); - } - else { - sendJson(res, 400, { - error: 'Validation failed', - code: 'VALIDATION_ERROR', - details: result.errors - }); - } - return; - } - // Phase G: GET /api/settings/feature-flags - Get feature flags - if (req.method === 'GET' && path === '/api/settings/feature-flags') { - const settings = getSettings(); - sendJson(res, 200, { - featureFlags: settings.settings.featureFlags || { antigravityEnabled: false } - }); - return; - } - // Phase G: PUT /api/settings/feature-flags - Update feature flags - if (req.method === 'PUT' && path === '/api/settings/feature-flags') { - const body = await readJsonBody(req); - const { updateSettings } = await import('./settings.js'); - const actor = body.actor || 'dashboard'; - const updates = {}; - if (body.featureFlags && typeof body.featureFlags === 'object') { - updates.featureFlags = body.featureFlags; - const result = updateSettings(updates, actor); - if (result.success && result.settings) { - logInfo(`Feature flags updated by ${actor}: ${JSON.stringify(body.featureFlags)}`); - sendJson(res, 200, { - ok: true, - featureFlags: result.settings.featureFlags || { antigravityEnabled: false } - }); - } - else { - sendJson(res, 400, { - error: 'Validation failed', - code: 'VALIDATION_ERROR', - details: result.errors - }); - } - } - else { - sendJson(res, 400, { - error: 'Invalid feature flags', - code: 'INVALID_FEATURE_FLAGS' - }); - } - return; - } - // POST /api/settings/reset - Reset to defaults - if (req.method === 'POST' && path === '/api/settings/reset') { - const { resetSettings } = await import('./settings.js'); - const body = await readJsonBody(req); - const actor = body.actor || 'dashboard'; - const settings = resetSettings(actor); - sendJson(res, 200, { - ok: true, - settings - }); - return; - } - // POST /api/settings/preset - Apply a preset - if (req.method === 'POST' && path === '/api/settings/preset') { - const body = await readJsonBody(req); - const { applyPreset } = await import('./settings.js'); - const preset = body.preset; - if (!preset || !['balanced', 'conservative', 'aggressive', 'custom'].includes(preset)) { - sendJson(res, 400, { - error: 'Invalid preset', - code: 'INVALID_PRESET', - validPresets: ['balanced', 'conservative', 'aggressive', 'custom'] - }); - return; - } - const actor = body.actor || 'dashboard'; - const result = applyPreset(preset, actor); - if (result.success) { - sendJson(res, 200, { - ok: true, - preset, - settings: result.settings - }); - } - else { - sendJson(res, 400, { - error: 'Failed to apply preset', - code: 'PRESET_ERROR', - details: result.errors - }); - } - return; - } - res.writeHead(404, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify({ error: 'Not found' })); - } - catch (err) { - if (res.writableEnded) { - return; - } - const errorCode = err?.code; - if (errorCode === 'INVALID_JSON') { - sendJson(res, 400, { error: 'Invalid JSON payload', code: 'INVALID_JSON' }); - return; - } - if (errorCode === 'PAYLOAD_TOO_LARGE') { - sendJson(res, 413, { error: 'Payload too large', code: 'PAYLOAD_TOO_LARGE' }); - return; - } - const errorMessage = err instanceof Error ? err.message : String(err); - logError(`Web request failed (${req.method} ${path}): ${errorMessage}`); - sendJson(res, 500, { error: 'Internal server error', code: 'INTERNAL_ERROR' }); - } - }); - server.listen(port, host, () => { - console.log(`[multi-auth] Codex dashboard running at http://${host}:${port}`); - logInfo(`Codex dashboard running at http://${host}:${port}`); - }); - return server; -} -//# sourceMappingURL=web.js.map \ No newline at end of file diff --git a/dist/web.js.map b/dist/web.js.map deleted file mode 100644 index a4022b5..0000000 --- a/dist/web.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"web.js","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index a55f7b4..0000000 --- a/package-lock.json +++ /dev/null @@ -1,3889 +0,0 @@ -{ - "name": "@guard22/opencode-multi-auth-codex", - "version": "1.4.3", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@guard22/opencode-multi-auth-codex", - "version": "1.4.3", - "license": "MIT", - "bin": { - "opencode-multi-auth": "dist/cli.js" - }, - "devDependencies": { - "@opencode-ai/plugin": "^1.0.193", - "@opencode-ai/sdk": "^1.0.193", - "@types/jest": "^29.5.14", - "@types/node": "^22.10.2", - "jest": "^29.7.0", - "ts-jest": "^29.4.6", - "typescript": "^5.7.2" - }, - "peerDependencies": { - "@opencode-ai/plugin": "^1.0.150" - }, - "peerDependenciesMeta": { - "@opencode-ai/plugin": { - "optional": true - } - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@opencode-ai/plugin": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.1.21.tgz", - "integrity": "sha512-oAWVlKG7LACGFYawfdHGMN6e+6lyN6F+zPVncFUB99BrTl/TjELE5gTZwU7MalGpjwfU77yslBOZm4BXVAYGvw==", - "dev": true, - "dependencies": { - "@opencode-ai/sdk": "1.1.21", - "zod": "4.1.8" - } - }, - "node_modules/@opencode-ai/sdk": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.1.21.tgz", - "integrity": "sha512-4M6lBjRPlPz99Rb5rS5ZqKrb0UDDxOT9VTG06JpNxvA7ynTd8C50ckc2NGzWtvjarmxfaAk1VeuBYN/cq2pIKQ==", - "dev": true - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/node": { - "version": "22.19.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", - "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", - "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001770", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", - "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", - "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", - "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.302", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz", - "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-jest": { - "version": "29.4.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", - "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.3", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz", - "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/package.json b/package.json index 31ae2b2..653f71e 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "oauth", "multi-account" ], + "packageManager": "pnpm@11.1.0", "author": "a3fckx", "license": "MIT" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..64f646d --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2787 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@opencode-ai/plugin': + specifier: ^1.0.193 + version: 1.15.0 + '@opencode-ai/sdk': + specifier: ^1.0.193 + version: 1.15.0 + '@types/jest': + specifier: ^29.5.14 + version: 29.5.14 + '@types/node': + specifier: ^22.10.2 + version: 22.19.19 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@22.19.19) + ts-jest: + specifier: ^29.4.6 + version: 29.4.9(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.19))(typescript@5.9.3) + typescript: + specifier: ^5.7.2 + version: 5.9.3 + +packages: + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.28.6': + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.28.6': + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} + engines: {node: '>=8'} + + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + + '@opencode-ai/plugin@1.15.0': + resolution: {integrity: sha512-97N985FDqNMpbBbsNotu0j3lkHwDyHX+2BnPFfnuNmBrTQNqLrn2UyA6wBfP6J5l4xUslwKNrH2IVrGIZeiSxQ==} + peerDependencies: + '@opentui/core': '>=0.2.10' + '@opentui/keymap': '>=0.2.10' + '@opentui/solid': '>=0.2.10' + peerDependenciesMeta: + '@opentui/core': + optional: true + '@opentui/keymap': + optional: true + '@opentui/solid': + optional: true + + '@opencode-ai/sdk@1.15.0': + resolution: {integrity: sha512-npmURKwd1HoV/i3CsGl2tdHpGhWSGVqTN1cp0s2gDH5gE6XduAtFmmVXBZ/ovJLh3Evu6sTVZ9n0IkJzFNeveQ==} + + '@sinclair/typebox@0.27.10': + resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + + '@types/node@22.19.19': + resolution: {integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + baseline-browser-mapping@2.10.29: + resolution: {integrity: sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001792: + resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + collect-v8-coverage@1.0.3: + resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + effect@4.0.0-beta.65: + resolution: {integrity: sha512-QYKvQPAj3CmtsvWkHQww15wX4KG2gNsszDWEcOO5sZCMknp66u6Si/Opmt3wwWCwsyvRmDAdIg+JIz5qzbbFIw==} + + electron-to-chromium@1.5.356: + resolution: {integrity: sha512-9NgFd7m5t5MCJ5rUSjJITUXAH9mEGlrlofnMf4YEr+pz6JlP7cWmTAH+JFmbPnaSW8koVTkuW7pacORWAnA5Yw==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + fast-check@4.8.0: + resolution: {integrity: sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg==} + engines: {node: '>=12.17.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-my-way-ts@0.1.6: + resolution: {integrity: sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + handlebars@4.7.9: + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + engines: {node: '>= 0.4'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kubernetes-types@1.30.0: + resolution: {integrity: sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.12: + resolution: {integrity: sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==} + + multipasta@0.2.7: + resolution: {integrity: sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.44: + resolution: {integrity: sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + pure-rand@8.4.0: + resolution: {integrity: sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toml@4.1.1: + resolution: {integrity: sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw==} + engines: {node: '>=20'} + + ts-jest@29.4.9: + resolution: {integrity: sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 || ^30.0.0 + '@jest/types': ^29.0.0 || ^30.0.0 + babel-jest: ^29.0.0 || ^30.0.0 + esbuild: '*' + jest: ^29.0.0 || ^30.0.0 + jest-util: ^29.0.0 || ^30.0.0 + typescript: '>=4.3 <7' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + jest-util: + optional: true + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uuid@13.0.2: + resolution: {integrity: sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==} + hasBin: true + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod@4.1.8: + resolution: {integrity: sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==} + +snapshots: + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.3': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bcoe/v8-coverage@0.2.3': {} + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.2 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.6': {} + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.19.19 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/core@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.19 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@22.19.19) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.19 + jest-mock: 29.7.0 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 22.19.19 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 22.19.19 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.2.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.10 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3 + + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.29.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.19.19 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + + '@opencode-ai/plugin@1.15.0': + dependencies: + '@opencode-ai/sdk': 1.15.0 + effect: 4.0.0-beta.65 + zod: 4.1.8 + + '@opencode-ai/sdk@1.15.0': + dependencies: + cross-spawn: 7.0.6 + + '@sinclair/typebox@0.27.10': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@standard-schema/spec@1.1.0': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 22.19.19 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/node@22.19.19': + dependencies: + undici-types: 6.21.0 + + '@types/stack-utils@2.0.3': {} + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.35': + dependencies: + '@types/yargs-parser': 21.0.3 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + babel-jest@29.7.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.29.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.28.6 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) + + babel-preset-jest@29.6.3(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + + balanced-match@1.0.2: {} + + baseline-browser-mapping@2.10.29: {} + + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.29 + caniuse-lite: 1.0.30001792 + electron-to-chromium: 1.5.356 + node-releases: 2.0.44 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-from@1.1.2: {} + + callsites@3.1.0: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001792: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + + ci-info@3.9.0: {} + + cjs-module-lexer@1.4.3: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + co@4.6.0: {} + + collect-v8-coverage@1.0.3: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + create-jest@29.7.0(@types/node@22.19.19): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@22.19.19) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + dedent@1.7.2: {} + + deepmerge@4.3.1: {} + + detect-libc@2.1.2: + optional: true + + detect-newline@3.1.0: {} + + diff-sequences@29.6.3: {} + + effect@4.0.0-beta.65: + dependencies: + '@standard-schema/spec': 1.1.0 + fast-check: 4.8.0 + find-my-way-ts: 0.1.6 + ini: 6.0.0 + kubernetes-types: 1.30.0 + msgpackr: 1.11.12 + multipasta: 0.2.7 + toml: 4.1.1 + uuid: 13.0.2 + yaml: 2.9.0 + + electron-to-chromium@1.5.356: {} + + emittery@0.13.1: {} + + emoji-regex@8.0.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-errors@1.3.0: {} + + escalade@3.2.0: {} + + escape-string-regexp@2.0.0: {} + + esprima@4.0.1: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit@0.1.2: {} + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + fast-check@4.8.0: + dependencies: + pure-rand: 8.4.0 + + fast-json-stable-stringify@2.1.0: {} + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-my-way-ts@0.1.6: {} + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-package-type@0.1.0: {} + + get-stream@6.0.1: {} + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + + graceful-fs@4.2.11: {} + + handlebars@4.7.9: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + has-flag@4.0.0: {} + + hasown@2.0.3: + dependencies: + function-bind: 1.1.2 + + html-escaper@2.0.2: {} + + human-signals@2.1.0: {} + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ini@6.0.0: {} + + is-arrayish@0.2.1: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.3 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-fn@2.1.0: {} + + is-number@7.0.0: {} + + is-stream@2.0.1: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.3 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.3 + '@istanbuljs/schema': 0.1.6 + istanbul-lib-coverage: 3.2.2 + semver: 7.8.0 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.19 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.2 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@29.7.0(@types/node@22.19.19): + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@22.19.19) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@22.19.19) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@22.19.19): + dependencies: + '@babel/core': 7.29.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.19.19 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.19 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-get-type@29.6.3: {} + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 22.19.19 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.29.0 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.19.19 + jest-util: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-regex-util@29.6.3: {} + + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.12 + resolve.exports: 2.0.3 + slash: 3.0.0 + + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.19 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.19 + chalk: 4.1.2 + cjs-module-lexer: 1.4.3 + collect-v8-coverage: 1.0.3 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.8.0 + transitivePeerDependencies: + - supports-color + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.19.19 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.2 + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.19.19 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + + jest-worker@29.7.0: + dependencies: + '@types/node': 22.19.19 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@29.7.0(@types/node@22.19.19): + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@22.19.19) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + js-tokens@4.0.0: {} + + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json5@2.2.3: {} + + kleur@3.0.3: {} + + kubernetes-types@1.30.0: {} + + leven@3.1.0: {} + + lines-and-columns@1.2.4: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + lodash.memoize@4.1.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + make-dir@4.0.0: + dependencies: + semver: 7.8.0 + + make-error@1.3.6: {} + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + merge-stream@2.0.0: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mimic-fn@2.1.0: {} + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.12: + optionalDependencies: + msgpackr-extract: 3.0.3 + + multipasta@0.2.7: {} + + natural-compare@1.4.0: {} + + neo-async@2.6.2: {} + + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.1.2 + optional: true + + node-int64@0.4.0: {} + + node-releases@2.0.44: {} + + normalize-path@3.0.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-try@2.2.0: {} + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + pirates@4.0.7: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + pure-rand@6.1.0: {} + + pure-rand@8.4.0: {} + + react-is@18.3.1: {} + + require-directory@2.1.1: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@5.0.0: {} + + resolve.exports@2.0.3: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + semver@6.3.1: {} + + semver@7.8.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@3.0.7: {} + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + sprintf-js@1.0.3: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.6 + glob: 7.2.3 + minimatch: 3.1.5 + + tmpl@1.0.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toml@4.1.1: {} + + ts-jest@29.4.9(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.19))(typescript@5.9.3): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + handlebars: 4.7.9 + jest: 29.7.0(@types/node@22.19.19) + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.8.0 + type-fest: 4.41.0 + typescript: 5.9.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.29.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + jest-util: 29.7.0 + + type-detect@4.0.8: {} + + type-fest@0.21.3: {} + + type-fest@4.41.0: {} + + typescript@5.9.3: {} + + uglify-js@3.19.3: + optional: true + + undici-types@6.21.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uuid@13.0.2: {} + + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wordwrap@1.0.0: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml@2.9.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + zod@4.1.8: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..10188a9 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + msgpackr-extract: true From 15fd296f2468f388626442dd835ce128e017e373 Mon Sep 17 00:00:00 2001 From: EremesNG Date: Sun, 17 May 2026 14:02:49 -0600 Subject: [PATCH 02/33] feat(sticky-sessions): Add persistent sticky session routing --- src/index.ts | 73 +++++- src/rotation.ts | 140 ++++++++++- src/settings.ts | 30 ++- src/sticky-sessions.ts | 335 +++++++++++++++++++++++++++ src/types.ts | 43 +++- tests/unit/feature-flags.test.ts | 21 ++ tests/unit/index-sticky.test.ts | 257 ++++++++++++++++++++ tests/unit/rotation-strategy.test.ts | 278 ++++++++++++++++++++++ tests/unit/sticky-sessions.test.ts | 277 ++++++++++++++++++++++ 9 files changed, 1442 insertions(+), 12 deletions(-) create mode 100644 src/sticky-sessions.ts create mode 100644 tests/unit/index-sticky.test.ts create mode 100644 tests/unit/sticky-sessions.test.ts diff --git a/src/index.ts b/src/index.ts index be8c610..7e3d2ec 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,7 +20,14 @@ import { getDefaultModels } from './models.js' import { getForceState, isForceActive } from './force-mode.js' import { getRuntimeSettings } from './settings.js' import { listAccounts, updateAccount, loadStore } from './store.js' -import { DEFAULT_CONFIG, type AccountRateLimits, type PluginConfig } from './types.js' +import { hashStickyIdentity } from './sticky-sessions.js' +import { + DEFAULT_CONFIG, + DEFAULT_STICKY_SESSION_SETTINGS, + type AccountRateLimits, + type PluginConfig +} from './types.js' +import type { ResolvedStickyIdentity, StickyIdentitySource } from './types.js' import { Errors, type DeterministicError } from './errors.js' const PROVIDER_ID = 'openai' @@ -160,6 +167,59 @@ function supportsFastMode(model: string | undefined): boolean { return model === 'gpt-5.5' || model === 'gpt-5.4' } +type ResolveStickyIdentityOptions = { + headers: Headers + body?: { + metadata?: { + session_id?: unknown + conversation_id?: unknown + } + session_id?: unknown + conversation_id?: unknown + prompt_cache_key?: unknown + } + allowPromptCacheKey: boolean +} + +function normalizeStickyIdentityValue(value: unknown): string | null { + if (typeof value !== 'string') return null + const normalized = value.trim().toLowerCase() + return normalized.length > 0 ? normalized : null +} + +function resolveStickyIdentitySource( + source: StickyIdentitySource, + value: unknown, + allowPromptCacheKey: boolean +): ResolvedStickyIdentity | null { + if (source === 'body:prompt_cache_key' && !allowPromptCacheKey) return null + + const canonical = normalizeStickyIdentityValue(value) + if (!canonical) return null + + return { + source, + canonical, + hash: hashStickyIdentity(canonical) + } +} + +export function resolveStickyIdentity(options: ResolveStickyIdentityOptions): ResolvedStickyIdentity | null { + const headerSessionId = options.headers.get('session_id') + const headerConversationId = options.headers.get('conversation_id') + + const body = options.body + const bodyMetadata = body?.metadata + + return ( + resolveStickyIdentitySource('header:session_id', headerSessionId, options.allowPromptCacheKey) || + resolveStickyIdentitySource('header:conversation_id', headerConversationId, options.allowPromptCacheKey) || + resolveStickyIdentitySource('body:metadata.session_id', bodyMetadata?.session_id, options.allowPromptCacheKey) || + resolveStickyIdentitySource('body:metadata.conversation_id', bodyMetadata?.conversation_id, options.allowPromptCacheKey) || + resolveStickyIdentitySource('body:prompt_cache_key', body?.prompt_cache_key, options.allowPromptCacheKey) + ) +} + function ensureContentType(headers: Headers): Headers { const responseHeaders = new Headers(headers) if (!responseHeaders.has('content-type')) { @@ -658,8 +718,17 @@ const MultiAuthPlugin: Plugin = async ({ client, $, serverUrl, project, director rotationStrategy: settings.settings.rotationStrategy } + const sticky = settings.settings.featureFlags?.stickySessionsEnabled + ? resolveStickyIdentity({ + headers: new Headers(init?.headers || {}), + body, + allowPromptCacheKey: DEFAULT_STICKY_SESSION_SETTINGS.allowPromptCacheKey + }) + : null + const rotation = await getNextAccount(effectiveConfig, { - model: normalizedModel + model: normalizedModel, + ...(sticky ? { sticky } : {}) }) if (!rotation) { diff --git a/src/rotation.ts b/src/rotation.ts index f877864..27b4b77 100644 --- a/src/rotation.ts +++ b/src/rotation.ts @@ -3,7 +3,13 @@ import { ensureValidToken } from './auth.js' import { decodeJwtPayload, getPlanTypeFromClaims } from './codex-auth.js' import { isForceActive, checkAndAutoClearForce, getForceState, clearForce } from './force-mode.js' import { getRuntimeSettings, calculateWeightedSelection } from './settings.js' -import type { AccountCredentials, DEFAULT_CONFIG } from './types.js' +import { getStickyAssignment, removeStickyAssignment, upsertStickyAssignment } from './sticky-sessions.js' +import { + DEFAULT_STICKY_SESSION_SETTINGS, + type AccountCredentials, + DEFAULT_CONFIG +} from './types.js' +import type { ResolvedStickyIdentity } from './types.js' export interface RotationResult { account: AccountCredentials @@ -17,6 +23,7 @@ export interface RotationResult { export interface AccountSelectionContext { model?: string + sticky?: ResolvedStickyIdentity | null } const HEALTH_HYSTERESIS_MS = 10_000 @@ -89,6 +96,8 @@ interface AccountHealth { priority: number } +type StickyFailureDisposition = 'temporary' | 'permanent' + function evaluateAccountHealth(acc: AccountCredentials, now: number): AccountHealth { const wasRateLimited: boolean = !!(acc.rateLimitedUntil && acc.rateLimitedUntil > now - HEALTH_HYSTERESIS_MS) const wasModelUnsupported: boolean = !!(acc.modelUnsupportedUntil && acc.modelUnsupportedUntil > now - HEALTH_HYSTERESIS_MS) @@ -131,6 +140,40 @@ function evaluateAccountHealth(acc: AccountCredentials, now: number): AccountHea } } +function classifyStickyFailure( + account: AccountCredentials | undefined, + now: number +): StickyFailureDisposition { + if (!account || account.enabled === false || account.authInvalid) { + return 'permanent' + } + + if ( + (account.rateLimitedUntil && account.rateLimitedUntil > now) || + (account.modelUnsupportedUntil && account.modelUnsupportedUntil > now) || + (account.workspaceDeactivatedUntil && account.workspaceDeactivatedUntil > now) + ) { + return 'temporary' + } + + return 'temporary' +} + +async function persistStickySelection( + sticky: ResolvedStickyIdentity | null | undefined, + alias: string, + now: number +): Promise { + if (!sticky) return + + await upsertStickyAssignment({ + canonicalIdentity: sticky.canonical, + alias, + now, + settings: DEFAULT_STICKY_SESSION_SETTINGS + }) +} + export async function getNextAccount( config: typeof DEFAULT_CONFIG, selection?: AccountSelectionContext @@ -219,11 +262,6 @@ export async function getNextAccount( return health?.isHealthy === true }) - if (availableAliases.length === 0) { - console.warn('[multi-auth] No available accounts (rate-limited or invalidated).') - return null - } - const tokenFailureCooldownMs = (() => { const raw = process.env.OPENCODE_MULTI_AUTH_TOKEN_FAILURE_COOLDOWN_MS const parsed = raw ? Number(raw) : NaN @@ -233,6 +271,81 @@ export async function getNextAccount( const runtimeSettings = getRuntimeSettings() const rotationStrategy = runtimeSettings.settings.rotationStrategy || config.rotationStrategy + const sticky = + runtimeSettings.settings.featureFlags?.stickySessionsEnabled === true ? selection?.sticky ?? null : null + let stickyAliasToExclude: string | null = null + + if (sticky) { + const mappedAlias = ( + await getStickyAssignment({ + stickyHash: sticky.hash, + now, + settings: DEFAULT_STICKY_SESSION_SETTINGS + }) + )?.alias + + if (mappedAlias) { + const mappedAccount = store.accounts[mappedAlias] + const mappedHealth = mappedAccount ? healthMap.get(mappedAlias) : undefined + + if (mappedAccount && mappedHealth?.isHealthy) { + const token = await ensureValidToken(mappedAlias) + if (token) { + store = loadStore() + const latestMapped = store.accounts[mappedAlias] + if (latestMapped) { + store.accounts[mappedAlias] = { + ...latestMapped, + usageCount: (latestMapped.usageCount || 0) + 1, + lastUsed: now, + limitError: undefined + } + store.activeAlias = mappedAlias + store.lastRotation = now + saveStore(store) + await persistStickySelection(sticky, mappedAlias, now) + + const currentForceState = getForceState() + return { + account: store.accounts[mappedAlias], + token, + forceState: { + active: isForceActive(), + alias: currentForceState.forcedAlias, + remainingMs: currentForceState.forcedUntil ? currentForceState.forcedUntil - now : 0 + } + } + } + } + + store = loadStore() + } + + stickyAliasToExclude = mappedAlias + const replacementAliases = availableAliases.filter((alias) => alias !== mappedAlias) + + if (replacementAliases.length === 0) { + if (classifyStickyFailure(store.accounts[mappedAlias], now) === 'permanent') { + await removeStickyAssignment({ + stickyHash: sticky.hash, + now, + settings: DEFAULT_STICKY_SESSION_SETTINGS + }) + } + console.warn('[multi-auth] No available accounts (rate-limited or invalidated).') + return null + } + } + } + + const selectionPool = stickyAliasToExclude + ? availableAliases.filter((alias) => alias !== stickyAliasToExclude) + : availableAliases + + if (selectionPool.length === 0) { + console.warn('[multi-auth] No available accounts (rate-limited or invalidated).') + return null + } const buildCandidates = (candidateAliases: string[]): { aliases: string[]; nextIndex?: (selected: string) => number } => { switch (rotationStrategy) { @@ -328,7 +441,7 @@ export async function getNextAccount( } } - const { primaryAliases, fallbackAliases } = getPreferredPools(store, availableAliases, selection) + const { primaryAliases, fallbackAliases } = getPreferredPools(store, selectionPool, selection) const primary = buildCandidates(primaryAliases) const fallback = fallbackAliases.length > 0 ? buildCandidates(fallbackAliases) : { aliases: [] as string[] } const candidates = [...primary.aliases, ...fallback.aliases] @@ -357,6 +470,7 @@ export async function getNextAccount( store.rotationIndex = nextIndex(candidate) } saveStore(store) + await persistStickySelection(sticky, candidate, now) const currentForceState = getForceState() return { @@ -370,6 +484,18 @@ export async function getNextAccount( } } + if ( + sticky && + stickyAliasToExclude && + classifyStickyFailure(loadStore().accounts[stickyAliasToExclude], now) === 'permanent' + ) { + await removeStickyAssignment({ + stickyHash: sticky.hash, + now, + settings: DEFAULT_STICKY_SESSION_SETTINGS + }) + } + console.error('[multi-auth] No available accounts (token refresh failed on all candidates).') return null } diff --git a/src/settings.ts b/src/settings.ts index b9987f5..f1c1ab5 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,6 +1,7 @@ import { loadStore, saveStore, updateAccount } from './store.js' import { logInfo, logError } from './logger.js' import { + DEFAULT_FEATURE_FLAGS, DEFAULT_ROTATION_SETTINGS, WEIGHTED_PRESETS, validateSettings, @@ -26,9 +27,17 @@ function resolveSettings(includeEnvOverrides: boolean): SettingsResult { // Layer 1: Persisted settings from store if (store.settings) { + const persistedFeatureFlags = store.settings.featureFlags settings = { ...settings, - ...store.settings + ...store.settings, + featureFlags: persistedFeatureFlags + ? { + ...DEFAULT_FEATURE_FLAGS, + ...settings.featureFlags, + ...persistedFeatureFlags + } + : settings.featureFlags } source = 'persisted' } @@ -64,11 +73,23 @@ function resolveSettings(includeEnvOverrides: boolean): SettingsResult { if (envAntigravity) { const enabled = envAntigravity.toLowerCase() === 'true' || envAntigravity === '1' settings.featureFlags = { + ...DEFAULT_FEATURE_FLAGS, ...(settings.featureFlags || {}), antigravityEnabled: enabled } source = 'env' } + + const envStickySessions = process.env.OPENCODE_MULTI_AUTH_STICKY_SESSIONS_ENABLED + if (envStickySessions) { + const enabled = envStickySessions.toLowerCase() === 'true' || envStickySessions === '1' + settings.featureFlags = { + ...DEFAULT_FEATURE_FLAGS, + ...(settings.featureFlags || {}), + stickySessionsEnabled: enabled + } + source = 'env' + } } // Validate final settings @@ -93,7 +114,7 @@ export function getRuntimeSettings(): SettingsResult { // Phase F: Update settings with validation export function updateSettings( - updates: Partial, + updates: Partial & { featureFlags?: Partial> }, actor: string = 'system' ): { success: boolean; settings?: RotationSettings; errors?: SettingsValidationError[] } { const current = getRuntimeSettings() @@ -102,6 +123,11 @@ export function updateSettings( const newSettings: RotationSettings = { ...current.settings, ...updates, + featureFlags: { + ...DEFAULT_FEATURE_FLAGS, + ...(current.settings.featureFlags || {}), + ...(updates.featureFlags || {}) + }, updatedAt: Date.now(), updatedBy: actor } diff --git a/src/sticky-sessions.ts b/src/sticky-sessions.ts new file mode 100644 index 0000000..5499fe5 --- /dev/null +++ b/src/sticky-sessions.ts @@ -0,0 +1,335 @@ +import * as crypto from 'node:crypto' +import * as fs from 'node:fs' +import * as path from 'node:path' +import { getStorePath, withWriteLock } from './store.js' + +export type StickySessionSettings = { + ttlMs: number + maxEntries: number + maxFileBytes: number +} + +export type StickySessionEntry = { + alias: string + createdAt: number + lastUsedAt: number +} + +export type StickySessionsFile = { + version: 1 + updatedAt: number + entries: Record +} + +const STICKY_SIDE_CAR_FILE = 'sticky-sessions.json' +const STICKY_SIDE_CAR_VERSION = 1 + +type LoadStickySessionsOptions = { + now: number + settings: StickySessionSettings +} + +type UpsertStickyAssignmentOptions = LoadStickySessionsOptions & { + canonicalIdentity: string + alias: string +} + +type GetStickyAssignmentOptions = LoadStickySessionsOptions & { + stickyHash: string +} + +function createEmptyStickySessions(now: number): StickySessionsFile { + return { + version: STICKY_SIDE_CAR_VERSION, + updatedAt: now, + entries: {} + } +} + +function getStickySessionsPath(): string { + return path.join(path.dirname(getStorePath()), STICKY_SIDE_CAR_FILE) +} + +function ensureStoreDir(): void { + fs.mkdirSync(path.dirname(getStickySessionsPath()), { recursive: true, mode: 0o700 }) +} + +function normalizeStickyIdentity(value: string): string { + return value.trim().toLowerCase() +} + +export function hashStickyIdentity(canonicalIdentity: string): string { + const normalized = normalizeStickyIdentity(canonicalIdentity) + return crypto.createHash('sha256').update(normalized, 'utf8').digest('hex') +} + +function isFiniteTimestamp(value: unknown): value is number { + return typeof value === 'number' && Number.isFinite(value) && value >= 0 +} + +function sanitizeStickyEntry(value: unknown): StickySessionEntry | null { + if (!value || typeof value !== 'object') return null + + const entry = value as Record + if (typeof entry.alias !== 'string' || entry.alias.trim().length === 0) return null + if (!isFiniteTimestamp(entry.createdAt) || !isFiniteTimestamp(entry.lastUsedAt)) return null + if (entry.lastUsedAt < entry.createdAt) return null + + return { + alias: entry.alias, + createdAt: entry.createdAt, + lastUsedAt: entry.lastUsedAt + } +} + +function sanitizeStickySessions(value: unknown, now: number, settings: StickySessionSettings): StickySessionsFile { + const fallback = createEmptyStickySessions(now) + if (!value || typeof value !== 'object') return fallback + + const file = value as Record + if (file.version !== STICKY_SIDE_CAR_VERSION) return fallback + + const entries: Record = {} + const rawEntries = file.entries + if (rawEntries && typeof rawEntries === 'object') { + for (const [key, rawEntry] of Object.entries(rawEntries)) { + const sanitized = sanitizeStickyEntry(rawEntry) + if (sanitized) { + entries[key] = sanitized + } + } + } + + return pruneStickySessions( + { + version: STICKY_SIDE_CAR_VERSION, + updatedAt: isFiniteTimestamp(file.updatedAt) ? file.updatedAt : now, + entries + }, + now, + settings + ) +} + +export function pruneStickySessions( + file: StickySessionsFile, + now: number, + settings: StickySessionSettings +): StickySessionsFile { + const ttlMs = Math.max(0, settings.ttlMs) + const maxEntries = Math.max(0, settings.maxEntries) + const maxFileBytes = Math.max(0, settings.maxFileBytes) + + let entries = Object.entries(file.entries) + .filter(([, entry]) => now - entry.lastUsedAt <= ttlMs) + .sort((a, b) => a[1].lastUsedAt - b[1].lastUsedAt) + + while (entries.length > maxEntries) { + entries.shift() + } + + let next = materializeStickySessions(entries, now) + while (entries.length > 0 && Buffer.byteLength(JSON.stringify(next), 'utf8') > maxFileBytes) { + entries.shift() + next = materializeStickySessions(entries, now) + } + + if (entries.length === 0 && Buffer.byteLength(JSON.stringify(next), 'utf8') > maxFileBytes) { + return createEmptyStickySessions(now) + } + + return next +} + +function materializeStickySessions( + sortedEntries: Array<[string, StickySessionEntry]>, + now: number +): StickySessionsFile { + return { + version: STICKY_SIDE_CAR_VERSION, + updatedAt: now, + entries: Object.fromEntries(sortedEntries) + } +} + +function readStickySessionsFromDisk(now: number, settings: StickySessionSettings): StickySessionsFile { + ensureStoreDir() + const sidecarPath = getStickySessionsPath() + if (!fs.existsSync(sidecarPath)) { + return createEmptyStickySessions(now) + } + + try { + const raw = fs.readFileSync(sidecarPath, 'utf8') + const parsed = JSON.parse(raw) as unknown + return sanitizeStickySessions(parsed, now, settings) + } catch { + return createEmptyStickySessions(now) + } +} + +function readStickySessionsSnapshot(now: number): StickySessionsFile { + ensureStoreDir() + const sidecarPath = getStickySessionsPath() + if (!fs.existsSync(sidecarPath)) { + return createEmptyStickySessions(now) + } + + try { + const raw = fs.readFileSync(sidecarPath, 'utf8') + const parsed = JSON.parse(raw) as Record + if (parsed.version !== STICKY_SIDE_CAR_VERSION) { + return createEmptyStickySessions(now) + } + + const entries: Record = {} + if (parsed.entries && typeof parsed.entries === 'object') { + for (const [key, value] of Object.entries(parsed.entries)) { + const sanitized = sanitizeStickyEntry(value) + if (sanitized) { + entries[key] = sanitized + } + } + } + + return { + version: STICKY_SIDE_CAR_VERSION, + updatedAt: isFiniteTimestamp(parsed.updatedAt) ? parsed.updatedAt : now, + entries + } + } catch { + return createEmptyStickySessions(now) + } +} + +export async function getStickyAssignment( + options: GetStickyAssignmentOptions +): Promise { + return withWriteLock(() => { + const sidecarPath = getStickySessionsPath() + if (!fs.existsSync(sidecarPath)) { + return null + } + + const loaded = readStickySessionsFromDisk(options.now, options.settings) + writeStickySessionsToDisk(loaded) + return loaded.entries[options.stickyHash] ?? null + }) +} + +function writeStickySessionsToDisk(file: StickySessionsFile): void { + ensureStoreDir() + const sidecarPath = getStickySessionsPath() + const json = JSON.stringify(file, null, 2) + const tmpPath = `${sidecarPath}.tmp-${process.pid}-${Date.now()}` + let fd: number | null = null + + try { + fd = fs.openSync(tmpPath, 'w', 0o600) + fs.writeFileSync(fd, json, { encoding: 'utf8' }) + try { + fs.fsyncSync(fd) + } catch { + // best effort + } + } finally { + if (fd !== null) { + try { + fs.closeSync(fd) + } catch { + // ignore + } + } + } + + try { + fs.renameSync(tmpPath, sidecarPath) + } catch (error: any) { + if (error?.code === 'EPERM' || error?.code === 'EEXIST') { + try { + fs.unlinkSync(sidecarPath) + } catch { + // ignore + } + fs.renameSync(tmpPath, sidecarPath) + } else { + try { + fs.unlinkSync(tmpPath) + } catch { + // ignore + } + throw error + } + } + + try { + const dirFd = fs.openSync(path.dirname(sidecarPath), 'r') + try { + fs.fsyncSync(dirFd) + } catch { + // best effort + } + fs.closeSync(dirFd) + } catch { + // ignore + } + + try { + fs.chmodSync(sidecarPath, 0o600) + } catch { + // ignore + } +} + +export async function loadStickySessions(options: LoadStickySessionsOptions): Promise { + return withWriteLock(() => { + const loaded = readStickySessionsFromDisk(options.now, options.settings) + const sidecarPath = getStickySessionsPath() + if (fs.existsSync(sidecarPath)) { + writeStickySessionsToDisk(loaded) + } + return loaded + }) +} + +export async function upsertStickyAssignment( + options: UpsertStickyAssignmentOptions +): Promise { + return withWriteLock(() => { + const normalizedIdentity = normalizeStickyIdentity(options.canonicalIdentity) + if (!normalizedIdentity) { + throw new Error('Sticky canonical identity must not be empty') + } + + const loaded = readStickySessionsFromDisk(options.now, options.settings) + const stickyKey = hashStickyIdentity(normalizedIdentity) + const existing = loaded.entries[stickyKey] + + loaded.entries[stickyKey] = { + alias: options.alias, + createdAt: existing?.createdAt ?? options.now, + lastUsedAt: options.now + } + + const pruned = pruneStickySessions(loaded, options.now, options.settings) + writeStickySessionsToDisk(pruned) + return pruned + }) +} + +export async function removeStickyAssignment( + options: LoadStickySessionsOptions & { stickyHash: string } +): Promise { + return withWriteLock(() => { + const loaded = readStickySessionsSnapshot(options.now) + + if (!(options.stickyHash in loaded.entries)) { + return loaded + } + + delete loaded.entries[options.stickyHash] + const pruned = pruneStickySessions(loaded, options.now, options.settings) + writeStickySessionsToDisk(pruned) + return pruned + }) +} diff --git a/src/types.ts b/src/types.ts index 7d1d83f..d79ce4e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -201,11 +201,52 @@ export interface RotationSettings { export interface FeatureFlags { // Antigravity integration (default: false) antigravityEnabled: boolean + // Sticky-session routing/persistence gate (default: false) + stickySessionsEnabled?: boolean } // Phase G: Default feature flags export const DEFAULT_FEATURE_FLAGS: FeatureFlags = { - antigravityEnabled: false + antigravityEnabled: false, + stickySessionsEnabled: false +} + +// Phase G: Sticky identity sources used for canonical request matching +export type StickyIdentitySource = + | 'header:session_id' + | 'header:conversation_id' + | 'body:metadata.session_id' + | 'body:metadata.conversation_id' + | 'body:prompt_cache_key' + +// Phase G: Sticky-session policy shape (kept separate from the enablement flag) +export interface StickySessionSettings { + identitySources: StickyIdentitySource[] + allowPromptCacheKey: boolean + ttlMs: number + maxEntries: number + maxFileBytes: number +} + +// Phase G: Canonical sticky identity resolved from a request +export interface ResolvedStickyIdentity { + source: StickyIdentitySource + canonical: string + hash: string +} + +// Phase G: Default sticky-session policy constants +export const DEFAULT_STICKY_SESSION_SETTINGS: StickySessionSettings = { + identitySources: [ + 'header:session_id', + 'header:conversation_id', + 'body:metadata.session_id', + 'body:metadata.conversation_id' + ], + allowPromptCacheKey: false, + ttlMs: 24 * 60 * 60 * 1000, + maxEntries: 1000, + maxFileBytes: 1024 * 1024 } // Phase F: Weighted rotation presets diff --git a/tests/unit/feature-flags.test.ts b/tests/unit/feature-flags.test.ts index 5b0da21..f0d2d01 100644 --- a/tests/unit/feature-flags.test.ts +++ b/tests/unit/feature-flags.test.ts @@ -57,11 +57,23 @@ describe('Phase G: Feature Flags', () => { expect(result.settings.featureFlags?.antigravityEnabled).toBe(false) }) + it('should default stickySessionsEnabled to false', () => { + const result = getSettings() + + expect((result.settings.featureFlags as any)?.stickySessionsEnabled).toBe(false) + }) + it('should return false for isFeatureEnabled when flag not set', () => { const result = isFeatureEnabled('antigravityEnabled') expect(result).toBe(false) }) + + it('should return false for isFeatureEnabled when stickySessionsEnabled is not set', () => { + const result = isFeatureEnabled('stickySessionsEnabled' as any) + + expect(result).toBe(false) + }) }) describe('L1: Feature Flag Environment Override', () => { @@ -97,6 +109,15 @@ describe('Phase G: Feature Flags', () => { expect(result.settings.featureFlags?.antigravityEnabled).toBe(false) }) + + it('should enable sticky sessions via environment variable', () => { + process.env.OPENCODE_MULTI_AUTH_STICKY_SESSIONS_ENABLED = 'true' + + const result = getSettings() + + expect((result.settings.featureFlags as any)?.stickySessionsEnabled).toBe(true) + expect(result.source).toBe('env') + }) }) describe('L1: Feature Flag Persistence', () => { diff --git a/tests/unit/index-sticky.test.ts b/tests/unit/index-sticky.test.ts new file mode 100644 index 0000000..b4bdc9c --- /dev/null +++ b/tests/unit/index-sticky.test.ts @@ -0,0 +1,257 @@ +import * as fs from 'node:fs' +import * as os from 'node:os' +import * as path from 'node:path' +// @ts-ignore - ESM Jest globals are available at runtime in the test environment. +import { jest } from '@jest/globals' + +describe('Sticky identity request plumbing', () => { + async function loadIndexModule(): Promise { + return import('../../src/index.js') as Promise + } + + it('resolves the canonical sticky identity when an allowlisted identity is explicitly allowed', async () => { + const { resolveStickyIdentity } = await loadIndexModule() + + const sticky = resolveStickyIdentity({ + headers: new Headers({ + session_id: ' Session-123 ', + conversation_id: 'ignored-conversation' + }), + body: { + metadata: { + session_id: 'body-session-should-not-win' + }, + prompt_cache_key: 'cache-123' + }, + allowPromptCacheKey: false + }) + + expect(sticky).toEqual({ + source: 'header:session_id', + canonical: 'session-123', + hash: expect.any(String) + }) + }) + + it('returns null when no canonical sticky identity can be derived', async () => { + const { resolveStickyIdentity } = await loadIndexModule() + + const sticky = resolveStickyIdentity({ + headers: new Headers(), + body: { + metadata: {}, + prompt_cache_key: undefined + }, + allowPromptCacheKey: false + }) + + expect(sticky).toBeNull() + }) + + it('does not derive sticky identity from prompt_cache_key without explicit authorization', async () => { + const { resolveStickyIdentity } = await loadIndexModule() + + const sticky = resolveStickyIdentity({ + headers: new Headers(), + body: { + prompt_cache_key: 'cache-only-123' + }, + allowPromptCacheKey: false + }) + + expect(sticky).toBeNull() + }) +}) + +describe('Sticky account-selection context plumbing', () => { + const originalEnv = process.env + const testDir = path.join(os.tmpdir(), `oma-index-sticky-${Date.now()}`) + const testStoreFile = path.join(testDir, 'accounts.json') + + function createAccessToken(accountId: string): string { + const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url') + const payload = Buffer.from(JSON.stringify({ + 'https://api.openai.com/auth': { + chatgpt_account_id: accountId + } + })).toString('base64url') + + return `${header}.${payload}.signature` + } + + function seedStore(): void { + fs.mkdirSync(testDir, { recursive: true }) + fs.writeFileSync( + testStoreFile, + JSON.stringify({ + version: 2, + accounts: { + alpha: { + alias: 'alpha', + accessToken: 'access-alpha', + refreshToken: 'refresh-alpha', + expiresAt: Date.now() + 60_000, + usageCount: 0, + enabled: true + } + }, + activeAlias: null, + rotationIndex: 0, + lastRotation: 0, + forcedAlias: null, + forcedUntil: null, + previousRotationStrategy: null, + forcedBy: null, + rotationStrategy: 'round-robin' + }, null, 2), + { mode: 0o600 } + ) + } + + beforeEach(() => { + jest.restoreAllMocks() + process.env = { + ...originalEnv, + OPENCODE_MULTI_AUTH_STORE_DIR: testDir, + OPENCODE_MULTI_AUTH_STORE_FILE: testStoreFile + } + seedStore() + }) + + afterEach(() => { + process.env = originalEnv + if (fs.existsSync(testDir)) { + fs.rmSync(testDir, { recursive: true, force: true }) + } + }) + + async function invokePluginFetch(options: { + stickyEnabled: boolean + headers?: Record + body?: Record + }): Promise<{ getNextAccount: jest.Mock; fetchSpy: jest.SpyInstance }> { + jest.resetModules() + + const getNextAccount = jest.fn().mockResolvedValue({ + account: { + alias: 'alpha', + accessToken: 'access-alpha', + refreshToken: 'refresh-alpha', + expiresAt: Date.now() + 60_000, + usageCount: 1, + enabled: true + }, + token: createAccessToken('acct-123') + } as any) + + jest.unstable_mockModule('../../src/rotation.js', () => ({ + getNextAccount, + clearAuthInvalid: jest.fn(), + markAuthInvalid: jest.fn(), + markModelUnsupported: jest.fn(), + markRateLimited: jest.fn(), + markWorkspaceDeactivated: jest.fn() + })) + + jest.unstable_mockModule('../../src/settings.js', () => ({ + getRuntimeSettings: () => ({ + settings: { + rotationStrategy: 'round-robin', + criticalThreshold: 10, + lowThreshold: 30, + accountWeights: {}, + featureFlags: { + antigravityEnabled: false, + stickySessionsEnabled: options.stickyEnabled + } + }, + source: 'persisted' + }) + })) + + const fetchSpy = jest.spyOn(globalThis, 'fetch').mockResolvedValue( + new Response(JSON.stringify({ ok: true }), { + status: 200, + headers: { 'content-type': 'application/json' } + }) + ) + + const { default: MultiAuthPlugin } = await import('../../src/index.js') + const hooks = await MultiAuthPlugin({ + client: {}, + $: (() => ({ nothrow: () => ({ catch: () => undefined }) })) as any, + serverUrl: new URL('http://localhost:3000'), + project: { id: 'test' }, + directory: testDir + } as any) + + const auth = await (hooks as any).auth.loader(async () => null as any, {} as any) + await auth.fetch('https://api.openai.com/v1/responses', { + method: 'POST', + headers: options.headers, + body: JSON.stringify({ + model: 'gpt-5.4', + stream: false, + ...options.body + }) + }) + + return { getNextAccount, fetchSpy } + } + + it('passes sticky context into getNextAccount only when the sticky flag is enabled and a canonical identity exists', async () => { + const { getNextAccount, fetchSpy } = await invokePluginFetch({ + stickyEnabled: true, + headers: { + session_id: ' Session-123 ' + }, + body: { + prompt_cache_key: 'cache-123' + } + }) + + expect(getNextAccount).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ + model: 'gpt-5.4', + sticky: { + source: 'header:session_id', + canonical: 'session-123', + hash: expect.any(String) + } + }) + ) + + const headers = fetchSpy.mock.calls[0]?.[1]?.headers as Headers + expect(headers.get('conversation_id')).toBe('cache-123') + expect(headers.get('session_id')).toBe('cache-123') + }) + + it('keeps non-sticky selection unchanged when the sticky flag is disabled or no canonical identity exists', async () => { + const disabled = await invokePluginFetch({ + stickyEnabled: false, + headers: { + session_id: 'Session-Disabled' + }, + body: { + prompt_cache_key: 'cache-disabled' + } + }) + + expect(disabled.getNextAccount).toHaveBeenCalledWith( + expect.any(Object), + { model: 'gpt-5.4' } + ) + + const noIdentity = await invokePluginFetch({ + stickyEnabled: true, + headers: {}, + body: {} + }) + + expect(noIdentity.getNextAccount).toHaveBeenCalledWith( + expect.any(Object), + { model: 'gpt-5.4' } + ) + }) +}) diff --git a/tests/unit/rotation-strategy.test.ts b/tests/unit/rotation-strategy.test.ts index d9ba6f6..b171af3 100644 --- a/tests/unit/rotation-strategy.test.ts +++ b/tests/unit/rotation-strategy.test.ts @@ -1,14 +1,24 @@ import * as fs from 'node:fs' import * as path from 'node:path' import * as os from 'node:os' +import { createHash } from 'node:crypto' import { getNextAccount } from '../../src/rotation.js' import { loadStore, saveStore } from '../../src/store.js' import { updateSettings } from '../../src/settings.js' +import { activateForce } from '../../src/force-mode.js' import { DEFAULT_CONFIG, type AccountCredentials } from '../../src/types.js' const TEST_DIR = path.join(os.tmpdir(), `oma-rotation-test-${Date.now()}`) const TEST_STORE_FILE = path.join(TEST_DIR, 'accounts.json') +const TEST_STICKY_FILE = path.join(TEST_DIR, 'sticky-sessions.json') const originalEnv = process.env +const originalFetch = global.fetch + +type StickySelection = { + source: 'header:session_id' + canonical: string + hash: string +} function createAccount(alias: string, usageCount: number): AccountCredentials { return { @@ -28,6 +38,58 @@ function createPlanAccount(alias: string, usageCount: number, planType: string): } } +function createStickySelection(rawIdentity: string): StickySelection { + const canonical = rawIdentity.trim().toLowerCase() + return { + source: 'header:session_id', + canonical, + hash: createHash('sha256').update(canonical).digest('hex') + } +} + +function writeStickyMappings(entries: Record): void { + fs.writeFileSync( + TEST_STICKY_FILE, + JSON.stringify( + { + version: 1, + updatedAt: 1_700_000_000_000, + entries + }, + null, + 2 + ), + 'utf8' + ) +} + +function readStickyMappings(): { + version: number + updatedAt: number + entries: Record +} { + return JSON.parse(fs.readFileSync(TEST_STICKY_FILE, 'utf8')) as { + version: number + updatedAt: number + entries: Record + } +} + +function enableStickySessions(rotationStrategy: 'round-robin' | 'least-used' = 'round-robin'): void { + const update = updateSettings( + { + rotationStrategy, + featureFlags: { + antigravityEnabled: false, + stickySessionsEnabled: true + } as any + }, + 'test' + ) + + expect(update.success).toBe(true) +} + describe('Rotation Strategy Runtime Behavior', () => { beforeEach(() => { process.env = { @@ -47,6 +109,7 @@ describe('Rotation Strategy Runtime Behavior', () => { afterEach(() => { process.env = originalEnv + global.fetch = originalFetch if (fs.existsSync(TEST_DIR)) { fs.rmSync(TEST_DIR, { recursive: true, force: true }) } @@ -141,4 +204,219 @@ describe('Rotation Strategy Runtime Behavior', () => { expect(rotation).toBeNull() }) + + describe('sticky session routing (RED)', () => { + it('uses the active strategy for the initial sticky assignment and persists the selected alias mapping', async () => { + const sticky = createStickySelection(' Session-001 ') + const store = loadStore() + store.accounts.alpha = createAccount('alpha', 10) + store.accounts.beta = createAccount('beta', 1) + saveStore(store) + enableStickySessions('least-used') + + const rotation = await getNextAccount(DEFAULT_CONFIG, { sticky } as any) + + expect(rotation?.account.alias).toBe('beta') + expect(fs.existsSync(TEST_STICKY_FILE)).toBe(true) + expect(readStickyMappings().entries[sticky.hash]).toEqual({ + alias: 'beta', + createdAt: expect.any(Number), + lastUsedAt: expect.any(Number) + }) + }) + + it('reuses a healthy sticky account without advancing rotationIndex', async () => { + const sticky = createStickySelection('session-healthy') + const stickyNow = Date.now() + const store = loadStore() + store.accounts.alpha = createAccount('alpha', 0) + store.accounts.beta = createAccount('beta', 0) + store.rotationIndex = 0 + saveStore(store) + writeStickyMappings({ + [sticky.hash]: { + alias: 'beta', + createdAt: stickyNow, + lastUsedAt: stickyNow + } + }) + enableStickySessions('round-robin') + + const rotation = await getNextAccount(DEFAULT_CONFIG, { sticky } as any) + const updatedStore = loadStore() + + expect(rotation?.account.alias).toBe('beta') + expect(updatedStore.rotationIndex).toBe(0) + }) + + it('falls back to another valid account and rewrites the sticky mapping when the mapped alias is exhausted', async () => { + const sticky = createStickySelection('session-fallback') + const now = Date.now() + const store = loadStore() + store.accounts.alpha = { + ...createAccount('alpha', 0), + rateLimitedUntil: now + 60_000 + } + store.accounts.beta = createAccount('beta', 0) + saveStore(store) + writeStickyMappings({ + [sticky.hash]: { + alias: 'alpha', + createdAt: now, + lastUsedAt: now + } + }) + enableStickySessions('round-robin') + + const rotation = await getNextAccount(DEFAULT_CONFIG, { sticky } as any) + + expect(rotation?.account.alias).toBe('beta') + expect(readStickyMappings().entries[sticky.hash]?.alias).toBe('beta') + }) + + it('ignores an expired sticky mapping at runtime and falls back to normal selection', async () => { + const sticky = createStickySelection('session-expired-runtime') + const now = Date.now() + const store = loadStore() + store.accounts.alpha = createAccount('alpha', 5) + store.accounts.beta = createAccount('beta', 0) + store.rotationIndex = 0 + saveStore(store) + writeStickyMappings({ + [sticky.hash]: { + alias: 'beta', + createdAt: now - (25 * 60 * 60 * 1000), + lastUsedAt: now - (25 * 60 * 60 * 1000) + } + }) + enableStickySessions('round-robin') + + const rotation = await getNextAccount(DEFAULT_CONFIG, { sticky } as any) + + expect(rotation?.account.alias).toBe('alpha') + expect(readStickyMappings().entries[sticky.hash]?.alias).toBe('alpha') + }) + + it('keeps the sticky mapping when the mapped alias fails temporarily and no replacement is available', async () => { + const sticky = createStickySelection('session-temp-failure') + const now = Date.now() + const store = loadStore() + store.accounts.alpha = { + ...createAccount('alpha', 0), + rateLimitedUntil: now + 60_000 + } + saveStore(store) + writeStickyMappings({ + [sticky.hash]: { + alias: 'alpha', + createdAt: now, + lastUsedAt: now + } + }) + enableStickySessions('round-robin') + + const rotation = await getNextAccount(DEFAULT_CONFIG, { sticky } as any) + + expect(rotation).toBeNull() + expect(readStickyMappings().entries[sticky.hash]?.alias).toBe('alpha') + }) + + it('removes the sticky mapping when the mapped alias has failed permanently and no replacement is available', async () => { + const sticky = createStickySelection('session-permanent-failure') + const store = loadStore() + store.accounts.alpha = { + ...createAccount('alpha', 0), + enabled: false + } + saveStore(store) + writeStickyMappings({ + [sticky.hash]: { + alias: 'alpha', + createdAt: Date.now(), + lastUsedAt: Date.now() + } + }) + enableStickySessions('round-robin') + + const rotation = await getNextAccount(DEFAULT_CONFIG, { sticky } as any) + + expect(rotation).toBeNull() + expect(readStickyMappings().entries[sticky.hash]).toBeUndefined() + }) + + it('removes the sticky mapping when the mapped alias has failed permanently and fallback candidates are unusable', async () => { + const sticky = createStickySelection('session-permanent-failure-with-bad-fallback') + const now = Date.now() + const store = loadStore() + store.accounts.alpha = { + ...createAccount('alpha', 0), + enabled: false + } + store.accounts.beta = { + ...createAccount('beta', 0), + expiresAt: now - 1_000 + } + saveStore(store) + writeStickyMappings({ + [sticky.hash]: { + alias: 'alpha', + createdAt: now, + lastUsedAt: now + } + }) + enableStickySessions('round-robin') + global.fetch = (async () => new Response('refresh failed', { status: 500 })) as typeof fetch + + const rotation = await getNextAccount(DEFAULT_CONFIG, { sticky } as any) + + expect(rotation).toBeNull() + expect(readStickyMappings().entries[sticky.hash]).toBeUndefined() + }) + + it('ignores an existing sticky sidecar when the sticky flag is disabled', async () => { + const sticky = createStickySelection('session-flag-disabled') + const store = loadStore() + store.accounts.alpha = createAccount('alpha', 5) + store.accounts.beta = createAccount('beta', 0) + store.rotationIndex = 0 + saveStore(store) + writeStickyMappings({ + [sticky.hash]: { + alias: 'beta', + createdAt: Date.now(), + lastUsedAt: Date.now() + } + }) + + const rotation = await getNextAccount(DEFAULT_CONFIG, { sticky } as any) + + expect(rotation?.account.alias).toBe('alpha') + expect(readStickyMappings().entries[sticky.hash]?.alias).toBe('beta') + }) + + it('bypasses sticky routing while force mode is active', async () => { + const sticky = createStickySelection('session-force') + const stickyNow = Date.now() + const store = loadStore() + store.accounts.alpha = createAccount('alpha', 0) + store.accounts.beta = createAccount('beta', 0) + saveStore(store) + writeStickyMappings({ + [sticky.hash]: { + alias: 'beta', + createdAt: stickyNow, + lastUsedAt: stickyNow + } + }) + enableStickySessions('round-robin') + + const force = activateForce('alpha', 'test') + expect(force.success).toBe(true) + + const rotation = await getNextAccount(DEFAULT_CONFIG, { sticky } as any) + + expect(rotation?.account.alias).toBe('alpha') + expect(readStickyMappings().entries[sticky.hash]?.alias).toBe('beta') + }) + }) }) diff --git a/tests/unit/sticky-sessions.test.ts b/tests/unit/sticky-sessions.test.ts new file mode 100644 index 0000000..9e2d831 --- /dev/null +++ b/tests/unit/sticky-sessions.test.ts @@ -0,0 +1,277 @@ +import * as fs from 'node:fs' +import * as os from 'node:os' +import * as path from 'node:path' + +type StickySessionSettings = { + ttlMs: number + maxEntries: number + maxFileBytes: number +} + +type StickySessionEntry = { + alias: string + createdAt: number + lastUsedAt: number +} + +type StickySessionsFile = { + version: 1 + updatedAt: number + entries: Record +} + +const DEFAULT_SETTINGS: StickySessionSettings = { + ttlMs: 60_000, + maxEntries: 2, + maxFileBytes: 512 +} + +async function loadStickyModule(): Promise { + return import('../../src/sticky-sessions.js') +} + +function createTestContext() { + const previousEnv = process.env + const storeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'oma-sticky-sidecar-')) + const storeFile = path.join(storeDir, 'accounts.json') + const sidecarFile = path.join(storeDir, 'sticky-sessions.json') + + process.env = { + ...previousEnv, + OPENCODE_MULTI_AUTH_STORE_DIR: storeDir, + OPENCODE_MULTI_AUTH_STORE_FILE: storeFile + } + + return { + storeDir, + storeFile, + sidecarFile, + restore() { + process.env = previousEnv + fs.rmSync(storeDir, { recursive: true, force: true }) + } + } +} + +function writeJson(filePath: string, value: unknown): void { + fs.writeFileSync(filePath, JSON.stringify(value, null, 2), 'utf8') +} + +function readSidecar(filePath: string): StickySessionsFile { + return JSON.parse(fs.readFileSync(filePath, 'utf8')) as StickySessionsFile +} + +describe('Sticky session sidecar persistence contract', () => { + it('persists a versioned sidecar with minimal sticky metadata only', async () => { + const ctx = createTestContext() + + try { + const { upsertStickyAssignment } = await loadStickyModule() + + await upsertStickyAssignment({ + canonicalIdentity: ' Session-123 ', + alias: 'alpha', + now: 1_700_000_000_000, + settings: DEFAULT_SETTINGS + }) + + const persisted = readSidecar(ctx.sidecarFile) + const [hashedKey, entry] = Object.entries(persisted.entries)[0] ?? [] + + expect(persisted.version).toBe(1) + expect(persisted.updatedAt).toBe(1_700_000_000_000) + expect(Object.keys(persisted.entries)).toHaveLength(1) + expect(hashedKey).toMatch(/^[a-f0-9]{64}$/) + expect(entry).toEqual({ + alias: 'alpha', + createdAt: 1_700_000_000_000, + lastUsedAt: 1_700_000_000_000 + }) + + const serialized = JSON.stringify(persisted) + expect(serialized).not.toContain('Session-123') + expect(serialized).not.toContain('session-123') + expect((entry as Record).canonicalIdentity).toBeUndefined() + } finally { + ctx.restore() + } + }) + + it('derives deterministic hashed keys from normalized identities without persisting raw values', async () => { + const ctx = createTestContext() + + try { + const { hashStickyIdentity, upsertStickyAssignment } = await loadStickyModule() + + expect(hashStickyIdentity(' Session-123 ')).toBe(hashStickyIdentity('session-123')) + + await upsertStickyAssignment({ + canonicalIdentity: ' Session-123 ', + alias: 'alpha', + now: 100, + settings: DEFAULT_SETTINGS + }) + await upsertStickyAssignment({ + canonicalIdentity: 'session-123', + alias: 'beta', + now: 200, + settings: DEFAULT_SETTINGS + }) + + const persisted = readSidecar(ctx.sidecarFile) + const keys = Object.keys(persisted.entries) + + expect(keys).toHaveLength(1) + expect(keys[0]).toBe(hashStickyIdentity('session-123')) + expect(persisted.entries[keys[0]]).toEqual({ + alias: 'beta', + createdAt: 100, + lastUsedAt: 200 + }) + expect(JSON.stringify(persisted)).not.toContain('session-123') + } finally { + ctx.restore() + } + }) + + it('prunes expired entries first and then evicts least recently used entries to satisfy bounds', async () => { + const ctx = createTestContext() + + try { + const { loadStickySessions } = await loadStickyModule() + + writeJson(ctx.sidecarFile, { + version: 1, + updatedAt: 50, + entries: { + expired: { alias: 'alpha', createdAt: 1, lastUsedAt: 10 }, + oldest: { alias: 'beta', createdAt: 2, lastUsedAt: 40_000 }, + newest: { alias: 'gamma', createdAt: 3, lastUsedAt: 59_500 } + } + }) + + const loaded = await loadStickySessions({ + now: 60_001, + settings: { ...DEFAULT_SETTINGS, ttlMs: 20_000, maxEntries: 1, maxFileBytes: 10_000 } + }) + + expect(loaded.entries).toEqual({ + newest: { alias: 'gamma', createdAt: 3, lastUsedAt: 59_500 } + }) + expect(readSidecar(ctx.sidecarFile).entries).toEqual({ + newest: { alias: 'gamma', createdAt: 3, lastUsedAt: 59_500 } + }) + } finally { + ctx.restore() + } + }) + + it('does not return expired sticky assignments during runtime lookup and self-heals the sidecar', async () => { + const ctx = createTestContext() + + try { + const { getStickyAssignment, hashStickyIdentity } = await loadStickyModule() + const stickyHash = hashStickyIdentity('session-expired-runtime') + + writeJson(ctx.sidecarFile, { + version: 1, + updatedAt: 50, + entries: { + [stickyHash]: { alias: 'beta', createdAt: 1, lastUsedAt: 10 } + } + }) + + const loaded = await getStickyAssignment({ + stickyHash, + now: 60_001, + settings: { ...DEFAULT_SETTINGS, ttlMs: 20_000, maxEntries: 10, maxFileBytes: 10_000 } + }) + + expect(loaded).toBeNull() + expect(readSidecar(ctx.sidecarFile).entries).toEqual({}) + } finally { + ctx.restore() + } + }) + + it('serializes concurrent updates into a valid sidecar and leaves no temporary files behind', async () => { + const ctx = createTestContext() + + try { + const { upsertStickyAssignment } = await loadStickyModule() + + await Promise.all([ + upsertStickyAssignment({ canonicalIdentity: 'session-a', alias: 'alpha', now: 10, settings: DEFAULT_SETTINGS }), + upsertStickyAssignment({ canonicalIdentity: 'session-b', alias: 'beta', now: 20, settings: DEFAULT_SETTINGS }), + upsertStickyAssignment({ canonicalIdentity: 'session-c', alias: 'gamma', now: 30, settings: DEFAULT_SETTINGS }) + ]) + + const persisted = readSidecar(ctx.sidecarFile) + const leftovers = fs.readdirSync(ctx.storeDir).filter((name) => name.startsWith('sticky-sessions.json.tmp-')) + + expect(persisted.version).toBe(1) + expect(() => JSON.parse(fs.readFileSync(ctx.sidecarFile, 'utf8'))).not.toThrow() + expect(leftovers).toEqual([]) + + if (process.platform !== 'win32') { + expect(fs.statSync(ctx.sidecarFile).mode & 0o777).toBe(0o600) + } + } finally { + ctx.restore() + } + }) + + it('falls back to an empty sticky view when the sidecar is malformed and does not touch accounts.json', async () => { + const ctx = createTestContext() + + try { + const { loadStickySessions } = await loadStickyModule() + + fs.writeFileSync(ctx.sidecarFile, '{not-valid-json', 'utf8') + writeJson(ctx.storeFile, { + version: 2, + accounts: { alpha: { accessToken: 'token', refreshToken: 'refresh', expiresAt: 123, usageCount: 0 } }, + activeAlias: 'alpha', + rotationIndex: 0, + lastRotation: 123 + }) + const accountsBefore = fs.readFileSync(ctx.storeFile, 'utf8') + + const loaded = await loadStickySessions({ now: 500, settings: DEFAULT_SETTINGS }) + + expect(loaded).toEqual({ version: 1, updatedAt: 500, entries: {} }) + expect(fs.readFileSync(ctx.storeFile, 'utf8')).toBe(accountsBefore) + } finally { + ctx.restore() + } + }) + + it('salvages valid sticky entries and prunes invalid ones during self-healing loads', async () => { + const ctx = createTestContext() + + try { + const { loadStickySessions } = await loadStickyModule() + + writeJson(ctx.sidecarFile, { + version: 1, + updatedAt: 100, + entries: { + validhash: { alias: 'alpha', createdAt: 10, lastUsedAt: 90 }, + missingAlias: { createdAt: 20, lastUsedAt: 80 }, + negativeTime: { alias: 'beta', createdAt: -1, lastUsedAt: 70 } + } + }) + + const loaded = await loadStickySessions({ now: 150, settings: DEFAULT_SETTINGS }) + + expect(loaded.entries).toEqual({ + validhash: { alias: 'alpha', createdAt: 10, lastUsedAt: 90 } + }) + expect(readSidecar(ctx.sidecarFile).entries).toEqual({ + validhash: { alias: 'alpha', createdAt: 10, lastUsedAt: 90 } + }) + } finally { + ctx.restore() + } + }) +}) From c9a3435e9732aa4bf842ce50b582cdf1b71bae39 Mon Sep 17 00:00:00 2001 From: EremesNG Date: Sun, 17 May 2026 14:02:49 -0600 Subject: [PATCH 03/33] docs(openspec): Archive sticky session change --- .../archive-report.md | 16 + .../design.md | 287 ++++++++++++++++++ .../proposal.md | 79 +++++ .../specs/sticky-session-persistence/spec.md | 78 +++++ .../specs/sticky-session-routing/spec.md | 119 ++++++++ .../tasks.md | 74 +++++ .../verification.md | 58 ++++ .../specs/sticky-session-persistence/spec.md | 74 +++++ openspec/specs/sticky-session-routing/spec.md | 115 +++++++ 9 files changed, 900 insertions(+) create mode 100644 openspec/changes/archive/2026-05-16-persistent-sticky-sessions/archive-report.md create mode 100644 openspec/changes/archive/2026-05-16-persistent-sticky-sessions/design.md create mode 100644 openspec/changes/archive/2026-05-16-persistent-sticky-sessions/proposal.md create mode 100644 openspec/changes/archive/2026-05-16-persistent-sticky-sessions/specs/sticky-session-persistence/spec.md create mode 100644 openspec/changes/archive/2026-05-16-persistent-sticky-sessions/specs/sticky-session-routing/spec.md create mode 100644 openspec/changes/archive/2026-05-16-persistent-sticky-sessions/tasks.md create mode 100644 openspec/changes/archive/2026-05-16-persistent-sticky-sessions/verification.md create mode 100644 openspec/specs/sticky-session-persistence/spec.md create mode 100644 openspec/specs/sticky-session-routing/spec.md diff --git a/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/archive-report.md b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/archive-report.md new file mode 100644 index 0000000..110f936 --- /dev/null +++ b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/archive-report.md @@ -0,0 +1,16 @@ +# Archive Report: persistent-sticky-sessions + +- Change: `persistent-sticky-sessions` +- Archive Path: `openspec/changes/archive/2026-05-16-persistent-sticky-sessions/` +- Pipeline: `full` +- Persistence Mode: `hybrid` +- Verification Lineage: `openspec/changes/persistent-sticky-sessions/verification.md` recorded **PASS**; focused matrix **58/58 PASS**; `npm run lint` **PASS**; build intentionally not run. +- Merged Specs: + - `openspec/specs/sticky-session-routing/spec.md` + - `openspec/specs/sticky-session-persistence/spec.md` +- Mode-based skips: none + +## Audit Summary +- Promoted both verified delta specs into canonical `openspec/specs/` because no prior canonical domain specs existed. +- Archived the completed change artifacts under the dated archive path. +- Preserved no-build constraint while carrying forward verification evidence from the passing report. diff --git a/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/design.md b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/design.md new file mode 100644 index 0000000..c92cd00 --- /dev/null +++ b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/design.md @@ -0,0 +1,287 @@ +# Design: Persistent Sticky Sessions + +## Technical Approach +Sticky sessions will be implemented as an opt-in pre-selection layer on top of the existing account rotation pipeline, not as a new rotation strategy. The runtime will: + +1. Resolve a canonical sticky identity in `src/index.ts` from an explicit allowlisted hierarchy. +2. Hash that normalized identity before it crosses the persistence boundary. +3. Pass the resolved sticky context into `getNextAccount()`. +4. Let `src/rotation.ts` try sticky reuse only after force-mode short-circuiting and before normal candidate iteration. +5. Persist sticky mappings in a dedicated sidecar file `sticky-sessions.json` stored next to `accounts.json`. +6. Keep `rotationIndex` unchanged on sticky reuse and only advance it when the existing rotation strategy actually selects a replacement. + +The implementation MUST preserve current behavior when the feature flag is off or when no canonical identity can be derived. + +## Architecture Decisions + +### Decision: Feature-flag gate with conservative defaults +**Choice**: Add `featureFlags.stickySessionsEnabled = false` to runtime settings defaults and keep sticky persistence entirely bypassed unless that flag is enabled. + +**Alternatives considered**: +- New top-level `stickySessions.enabled` boolean only. +- Environment-only flag with no persisted settings support. + +**Rationale**: +- The repo already exposes optional behavior through `featureFlags`. +- Default-OFF parity is a hard spec requirement. +- Persisted settings allow the dashboard/API to enable the feature later without changing app code. + +### Decision: Separate sticky configuration from the enablement flag +**Choice**: Keep enablement in `featureFlags`, and introduce a small `StickySessionSettings` type for internal/persisted policy values: +- `identitySources: StickyIdentitySource[]` +- `allowPromptCacheKey: boolean` +- `ttlMs: number` +- `maxEntries: number` +- `maxFileBytes: number` + +Default policy: +- `identitySources`: `['header:session_id', 'header:conversation_id', 'body:metadata.session_id', 'body:metadata.conversation_id']` +- `allowPromptCacheKey`: `false` +- bounded retention defaults held in code constants, mirrored into defaults only if a persisted settings shape is preferred. + +**Alternatives considered**: +- No sticky policy object, only hard-coded constants. +- Allow `prompt_cache_key` by default. + +**Rationale**: +- The spec requires an explicit allowlisted identity hierarchy. +- `prompt_cache_key` is already used for outbound backend headers, but the spec explicitly forbids treating it as canonical identity by default. +- Separating policy from the on/off flag keeps the first release small while leaving room for safe future tuning. + +### Decision: Dedicated sidecar module instead of expanding `store.ts` inline +**Choice**: Add a new module `src/sticky-sessions.ts` responsible for sticky-session persistence and identity hashing, while reusing store-path and write-lock primitives from `src/store.ts`. + +**Alternatives considered**: +- Put sticky logic directly in `store.ts`. +- Persist sticky mappings inside `accounts.json`. + +**Rationale**: +- The proposal/spec explicitly require a dedicated sidecar and corruption isolation from `accounts.json`. +- `store.ts` already has a lot of unrelated account-store concerns; a companion module keeps sticky persistence testable in isolation. + +### Decision: Reuse current health semantics, classify permanence pragmatically +**Choice**: Reuse the current account state as the source of truth for fallback decisions: +- **Permanent**: alias missing, `enabled === false`, `authInvalid === true`, or token refresh results in `authInvalid` being set. +- **Temporary**: `rateLimitedUntil > now`, `modelUnsupportedUntil > now`, `workspaceDeactivatedUntil > now`, or `ensureValidToken()` fails without setting `authInvalid`. + +**Alternatives considered**: +- Introduce a new explicit account-failure enum everywhere. +- Guess permanence from error strings. + +**Rationale**: +- The current codebase already materializes temporary/permanent signals in account state. +- This avoids a large auth/rotation redesign that the proposal explicitly excludes. + +### Decision: Update sticky mapping only after selection is confirmed usable +**Choice**: Persist sticky assignments only after the chosen account has passed health gating, returned a valid token, and the selection is being committed. + +**Alternatives considered**: +- Assign sticky mapping before token validation. +- Rewrite sticky mapping on every attempted candidate. + +**Rationale**: +- The spec requires first-assignment/reassignment only after successful selection. +- Pre-committing would pin identities to unusable aliases and create false stickiness. + +## Data Flow + +### Request path in `src/index.ts` +1. Parse request body as today. +2. Resolve `normalizedModel` as today. +3. Resolve sticky identity before calling `getNextAccount()`: + - inspect request headers/body against the allowlisted hierarchy, + - normalize the first explicit identity found, + - hash only inside the sticky module or a shared helper, + - if no canonical identity exists, pass `sticky: null`. +4. Call `getNextAccount(effectiveConfig, { model, sticky })`. +5. Keep all retry/error semantics unchanged outside sticky routing. + +### Selection path in `src/rotation.ts` +1. Auto-clear expired force state. +2. If force mode is active, execute the existing forced-account branch unchanged and return immediately. +3. Build current `healthMap` and healthy alias pools as today. +4. If sticky sessions are disabled or no sticky identity exists, execute the current selection pipeline unchanged. +5. If a sticky mapping exists: + - load mapped alias, + - validate existence/enabled/health/model/workspace conditions, + - call `ensureValidToken(mappedAlias)`. +6. On successful sticky reuse: + - increment `usageCount`, `lastUsed`, `activeAlias`, `lastRotation`, + - refresh sticky entry `lastUsedAt`, + - DO NOT advance `rotationIndex`. +7. On sticky miss/failure with replacement candidates available: + - exclude the unusable sticky alias from candidate aliases for this request, + - run the current active strategy over the remaining valid aliases, + - preserve the existing `nextIndex()` semantics for the replacement only, + - update sticky mapping to the replacement alias after commit. +8. On sticky failure with no replacement candidates: + - preserve current null/error semantics, + - keep or remove mapping based on temporary vs permanent failure classification. + +## File Changes + +### New file +- `src/sticky-sessions.ts` + - resolve sticky sidecar path from `path.dirname(getStorePath())` + - define sidecar schema/types + - normalize/hash identities + - load/validate/prune/save mappings + - expose `getStickyAssignment`, `upsertStickyAssignment`, `touchStickyAssignment`, `removeStickyAssignment`, `pruneStickySessions`, and read-only helpers for tests + +### Modified files +- `src/types.ts` + - add `stickySessionsEnabled` to `FeatureFlags` + - add `StickyIdentitySource`, `StickySessionSettings`, `ResolvedStickyIdentity`, `StickySessionEntry`, `StickySessionsFile` + - extend `RotationSettings` with optional sticky-session policy block if persisted policy is chosen +- `src/settings.ts` + - add sticky-session defaults to the feature-flag defaults + - optionally layer env overrides later without changing first implementation shape + - keep `isFeatureEnabled('stickySessionsEnabled')` available for routing/persistence bypass +- `src/index.ts` + - add canonical identity resolver helper(s) + - derive sticky context before `getNextAccount()` + - DO NOT infer identity from `prompt_cache_key` unless explicit policy authorizes it +- `src/rotation.ts` + - extend `AccountSelectionContext` with `sticky?: ResolvedStickyIdentity | null` + - wrap current candidate selection in a sticky-aware flow + - preserve force-mode precedence and `rotationIndex` semantics +- `src/store.ts` + - expose any minimal persistence helpers needed by the new sidecar module (prefer helper extraction over duplicating atomic-write code) + - keep `accounts.json` and sticky sidecar isolated + +### Test files +- `tests/unit/feature-flags.test.ts` +- `tests/unit/rotation-strategy.test.ts` +- `tests/unit/store.test.ts` +- `tests/unit/sticky-sessions.test.ts` (new, recommended) + +## Interfaces / Contracts + +### Settings/types +```ts +interface FeatureFlags { + antigravityEnabled: boolean + stickySessionsEnabled: boolean +} + +type StickyIdentitySource = + | 'header:session_id' + | 'header:conversation_id' + | 'body:metadata.session_id' + | 'body:metadata.conversation_id' + | 'body:prompt_cache_key' + +interface StickySessionSettings { + identitySources: StickyIdentitySource[] + allowPromptCacheKey: boolean + ttlMs: number + maxEntries: number + maxFileBytes: number +} + +interface ResolvedStickyIdentity { + source: StickyIdentitySource + canonical: string + hash: string +} +``` + +### Sidecar schema +```ts +interface StickySessionEntry { + alias: string + createdAt: number + lastUsedAt: number +} + +interface StickySessionsFile { + version: 1 + updatedAt: number + entries: Record +} +``` + +### Identity normalization +- trim whitespace +- lowercase header/body string identifiers +- collapse internal whitespace only if the source is known to be case-insensitive +- reject empty strings after normalization +- hash with `sha256(normalizedCanonicalIdentity)` and persist only the hex digest + +### Persistence responsibilities in `src/sticky-sessions.ts` +- **Path**: `path.join(path.dirname(getStorePath()), 'sticky-sessions.json')` +- **Load**: + - if feature flag is off, return an in-memory empty view and do not touch disk + - if file missing, return empty schema + - if file corrupt, salvage valid entries when possible; otherwise return empty schema without affecting `accounts.json` +- **Save**: + - prune expired entries first + - prune LRU until `maxEntries` and `maxFileBytes` are satisfied + - serialize through shared write lock + - write `tmp` file with `0o600`, `fsync`, rename, and directory `fsync` +- **Atomic write / permissions**: + - reuse the same replacement flow already used by `saveStore()` + - keep sidecar and temp files local-only (`0o600`) + - create store dir with existing `0o700` behavior + +## Testing Strategy + +Implementation should be TDD-first in this order. + +### Block 1 — RED: feature-flag parity +- add tests proving sticky bypass when `stickySessionsEnabled === false` +- verify existing sidecar file is ignored while flag is off +- suggested command: + - `pnpm exec jest tests/unit/feature-flags.test.ts --runInBand` + +### Block 2 — RED: sidecar persistence contract +- new `tests/unit/sticky-sessions.test.ts` +- cover schema versioning, hashed-only persistence, TTL pruning, LRU pruning, file-size cap, corruption isolation, and write-lock behavior +- suggested command: + - `pnpm exec jest tests/unit/sticky-sessions.test.ts --runInBand` + +### Block 3 — RED: routing integration +- extend `tests/unit/rotation-strategy.test.ts` +- cover: + - first assignment persists mapping + - healthy sticky reuse + - sticky reuse does not advance `rotationIndex` + - fallback replacement updates mapping + - no replacement + temporary failure keeps mapping + - no replacement + permanent failure removes mapping + - force mode bypasses sticky routing +- suggested command: + - `pnpm exec jest tests/unit/rotation-strategy.test.ts --runInBand` + +### Block 4 — GREEN / refactor +- implement minimum production code to satisfy each failing block +- keep helper extraction small and verify focused suites after each block +- final focused regression pass: + - `pnpm exec jest tests/unit/feature-flags.test.ts tests/unit/sticky-sessions.test.ts tests/unit/rotation-strategy.test.ts --runInBand` + +No build step is required or allowed for this design task. + +## Migration / Rollout + +1. Ship feature flag default OFF. +2. Do not create/read `sticky-sessions.json` until the flag is enabled. +3. On first enabled sticky assignment, create the sidecar lazily. +4. Rollback path is trivial: disable the feature flag; the sidecar becomes inert. + +## Open Questions + +1. Whether `StickySessionSettings` should be persisted in `store.settings` from v1 or kept as internal constants for the first implementation. The design supports either, but implementation should prefer the smallest surface compatible with the spec. +2. Whether request headers carrying `session_id` / `conversation_id` are already propagated by OpenCode in all provider flows. If not, body metadata sources should remain the primary canonical path. + +## What NOT to copy from PR #23 + +PR #23 could not be verified from this checkout or accessible remote metadata during design work, so implementation MUST use the spec as the source of truth and explicitly avoid these anti-patterns: + +- do **not** store sticky mappings inside `accounts.json` +- do **not** persist raw `session_id`, `conversation_id`, or `prompt_cache_key` +- do **not** treat `prompt_cache_key` as canonical identity by default +- do **not** introduce a new rotation strategy for sticky behavior +- do **not** let sticky reuse override active force mode +- do **not** advance `rotationIndex` on sticky hits +- do **not** delete mappings for temporary cooldown/token-refresh failures +- do **not** update sticky mappings before the replacement account is actually usable diff --git a/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/proposal.md b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/proposal.md new file mode 100644 index 0000000..a2e532e --- /dev/null +++ b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/proposal.md @@ -0,0 +1,79 @@ +# Proposal: Persistent Sticky Sessions + +## Intent + +Add opt-in sticky session reuse to the existing multi-account rotation flow so a session can keep using the same healthy account across requests, while still falling back to the current rotation strategy when that account becomes unavailable. The feature must persist from v1, default to OFF, and preserve current behavior for installations that do not enable it. + +## Scope + +### In Scope + +- Introduce sticky-session persistence as a dedicated sidecar file named `sticky-sessions.json` under the existing store directory, separate from `accounts.json`. +- Persist only minimal sticky metadata keyed by a hashed session identity rather than raw session IDs. +- Integrate sticky reuse into the current selection flow in `src/rotation.ts` and `src/index.ts` as a pre-selection optimization, not as a new rotation strategy. +- Reuse an already assigned healthy account when sticky mode is enabled. +- If the assigned account is blocked, invalid, missing, or otherwise unusable, fall back to the active rotation strategy, assign the replacement account to the session, and persist the new mapping. +- Preserve force mode precedence: when force mode is active, sticky reuse SHALL NOT override the forced account. +- Preserve rotation semantics: sticky reuse SHALL NOT advance `rotationIndex`; the index only advances when the existing rotation strategy actually selects a replacement. +- Add bounded persistence controls: TTL/LRU cleanup, maximum entries/size protections, atomic write behavior, and shared locking discipline consistent with current store writes. +- Add tests first and verify an initial RED state before implementation, covering reuse, fallback reassignment, force-mode precedence, cleanup, and flag-off behavior. + +### Out of Scope + +- Creating a new rotation strategy or mandatory sticky routing mode. +- Storing sticky mappings inside `accounts.json`. +- Persisting raw session identifiers, prompt bodies, or broad per-session telemetry. +- Changing current force-mode semantics beyond preserving its precedence. +- Redesigning rate-limit, auth-refresh, or account-health policies outside the sticky-session integration points. +- UI/UX expansion beyond the minimum configuration surface required to expose an opt-in feature flag. + +## Approach + +The change will extend the current runtime/settings model with a default-OFF feature flag for sticky sessions, then layer sticky lookup into the existing account selection pipeline. + +At a high level: + +1. Identify the session using stable request-level identity already present in the runtime flow, normalize it, and hash it before persistence. +2. Load sticky metadata from `sticky-sessions.json` using the same store directory conventions as `accounts.json`. +3. Before invoking the normal candidate selection path, attempt sticky reuse when the feature is enabled and force mode is not active. +4. If the sticky account is still present and healthy, reuse it without advancing `rotationIndex`. +5. If the sticky account is no longer usable, run the current rotation strategy unchanged, then update the sticky assignment to the replacement account selected by that strategy. +6. Apply TTL/LRU and entry-size enforcement during load/save/update so the sidecar remains bounded and self-healing. +7. Reuse the project’s atomic write and lock discipline so sticky persistence remains crash-safe and consistent with existing store behavior. + +## Affected Areas + +- `src/rotation.ts` — inject sticky reuse and fallback reassignment into the current account-selection flow. +- `src/index.ts` — provide the request/session identity needed for sticky lookup alongside existing request normalization. +- `src/settings.ts` / `src/types.ts` — add the opt-in sticky feature flag and any bounded configuration metadata. +- `src/store.ts` or a dedicated companion module — shared persistence helpers, path resolution, atomic write/lock reuse, and sidecar coordination. +- `tests/unit/rotation-strategy.test.ts` — strategy integration and sticky fallback behavior. +- `tests/unit/store.test.ts` and/or new sticky persistence tests — sidecar persistence, cleanup, and locking guarantees. +- `tests/unit/feature-flags.test.ts` — default-OFF and persisted flag behavior. + +## Risks + +- Session identity drift could break reuse if different request paths produce inconsistent identifiers. +- Incorrect fallback handling could pin sessions to invalid accounts or accidentally bypass existing health checks. +- Updating sticky mappings at the wrong time could incorrectly advance round-robin state or bias least-used metrics. +- Sidecar corruption or concurrent writes could create stale mappings unless atomic writes and locking are explicit. +- Unbounded sticky growth could create disk bloat or degraded lookup performance without TTL/LRU and hard limits. +- Overexposing sticky state in APIs or logs could leak session-derived identifiers if hashing/minimal metadata is not enforced. + +## Rollback Plan + +- Disable the sticky-session feature flag to restore current routing behavior immediately. +- Stop reading or writing `sticky-sessions.json` when the flag is off. +- If operational issues appear after release, remove the sidecar integration while leaving `accounts.json` and existing rotation strategies unchanged. +- Because sticky state is isolated in a sidecar file, rollback does not require account-store migration or destructive rewriting of account data. + +## Success Criteria + +- With sticky mode OFF, account selection behaves exactly as it does today. +- With sticky mode ON, repeated requests for the same session reuse the assigned healthy account. +- When the sticky account becomes unavailable and another valid account exists, the current rotation strategy selects the replacement and the sticky assignment is updated. +- Force mode always wins over sticky reuse. +- Sticky reuse does not advance `rotationIndex`; replacement rotation does. +- Sticky persistence survives restart using `sticky-sessions.json` and stores only hashed identity plus minimal metadata. +- Cleanup keeps the sidecar bounded via TTL/LRU and size/entry caps. +- The change is implemented TDD-first with failing tests written before production code. diff --git a/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/specs/sticky-session-persistence/spec.md b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/specs/sticky-session-persistence/spec.md new file mode 100644 index 0000000..b374cf9 --- /dev/null +++ b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/specs/sticky-session-persistence/spec.md @@ -0,0 +1,78 @@ +# Delta for Sticky Session Persistence + +## ADDED Requirements + +### Requirement: Versioned Sticky Sidecar Storage +When sticky sessions are enabled, the system MUST persist sticky mappings in a dedicated sidecar file named `sticky-sessions.json` under the existing store directory, separate from `accounts.json`. The sidecar MUST use a versioned schema and MUST store only minimal sticky metadata required for reuse and cleanup. + +Minimal sticky metadata MUST include the canonical identity hash key, mapped alias, creation timestamp, and last-used timestamp. The sidecar MUST NOT store raw identities, prompt bodies, raw `prompt_cache_key` values, account tokens, or unrelated telemetry. + +#### Scenario: Sticky mapping is stored in the sidecar +- GIVEN sticky sessions are enabled +- AND a sticky mapping is created or updated +- WHEN the mapping is persisted +- THEN the system MUST write it to `sticky-sessions.json` in the configured store directory +- AND the persisted record MUST use the versioned sticky schema with minimal metadata only + +### Requirement: Deterministic Hashed Identity Keys +The system MUST persist only deterministic one-way hashes of normalized canonical identities so the same canonical identity resolves to the same sticky key across process restarts without exposing the raw identifier on disk. + +#### Scenario: Equivalent identities map to one hashed key +- GIVEN two requests normalize to the same canonical identity +- WHEN sticky persistence resolves their storage key +- THEN the system MUST produce the same hashed key for both requests +- AND the persisted sidecar MUST NOT contain the raw identity value + +### Requirement: Bounded Retention and Cleanup +The sticky sidecar MUST remain bounded by TTL and LRU cleanup, maximum entry count, and maximum file-size protections. The system MUST remove expired entries and least-recently-used entries before persisting data that would exceed configured limits. + +#### Scenario: Cleanup prunes expired or least-recently-used entries +- GIVEN sticky sessions are enabled +- AND the sidecar contains expired entries or exceeds configured entry or size limits +- WHEN the sidecar is loaded or updated +- THEN the system MUST prune expired entries first +- AND the system MUST evict least-recently-used entries as needed to restore limits before completing the write + +### Requirement: Atomic and Secure Sticky Writes +Sticky sidecar updates MUST use atomic write behavior, safe replacement, and the same shared write-lock discipline used for the primary store so concurrent updates do not leave partial files behind. The sidecar file and its temporary replacements MUST use secure local permissions equivalent to the current store protections. + +#### Scenario: Sticky data is written atomically +- GIVEN sticky sessions are enabled +- AND a sticky mapping change must be persisted +- WHEN the sidecar is written +- THEN the system MUST serialize the update through the shared write-lock discipline +- AND the system MUST write through a temporary file and atomic replacement flow +- AND the resulting sidecar file MUST be stored with secure local permissions + +### Requirement: Disabled-Flag Persistence Bypass +When the sticky-session feature flag is OFF, the system MUST NOT read from or write to `sticky-sessions.json`, and the presence of an old sidecar file MUST NOT change current routing behavior. + +#### Scenario: Existing sidecar is ignored while flag is off +- GIVEN sticky sessions are disabled +- AND `sticky-sessions.json` already exists +- WHEN a request is routed +- THEN the system MUST ignore sticky-sidecar contents +- AND the request outcome MUST remain identical to the current non-sticky flow + +### Requirement: Corruption Isolation and Self-Healing Cleanup +Corruption or invalid entries in `sticky-sessions.json` MUST be isolated from `accounts.json` and MUST NOT corrupt normal account-store behavior. The system MUST discard invalid sticky entries during load, preserve valid sticky entries when possible, and continue using current routing/error semantics if sticky data cannot be recovered. + +#### Scenario: Sidecar corruption does not break account selection +- GIVEN sticky sessions are enabled +- AND `sticky-sessions.json` contains corrupt or schema-invalid content +- WHEN the sidecar is loaded +- THEN the system MUST avoid corrupting `accounts.json` +- AND the system MUST discard or repair invalid sticky content on a best-effort basis +- AND requests without recoverable sticky state MUST fall back to current routing semantics + +### Requirement: Sticky Persistence Verification Coverage +The change MUST include focused automated checks for sidecar schema versioning, hashed-only persistence, flag-off bypass, TTL/LRU cleanup, entry and size limit enforcement, corruption handling, and atomic-write or lock-coordination behavior. + +#### Scenario: Verification covers sticky-sidecar persistence +- GIVEN the sticky-sidecar change is implemented +- WHEN automated verification is prepared for the change +- THEN the test suite MUST include focused checks for the persistence guarantees that protect correctness, privacy, and bounded growth + +## MODIFIED Requirements + +## REMOVED Requirements diff --git a/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/specs/sticky-session-routing/spec.md b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/specs/sticky-session-routing/spec.md new file mode 100644 index 0000000..b57893b --- /dev/null +++ b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/specs/sticky-session-routing/spec.md @@ -0,0 +1,119 @@ +# Delta for Sticky Session Routing + +## ADDED Requirements + +### Requirement: Sticky Session Flag Gating +The system MUST keep sticky-session behavior disabled by default. When the sticky-session feature flag is OFF, account selection, persistence reads/writes, force-mode handling, rotation state, and error behavior MUST remain identical to the current non-sticky flow. + +#### Scenario: Flag off preserves current behavior +- GIVEN sticky sessions are disabled +- WHEN a request is routed through the existing selection pipeline +- THEN the system MUST skip sticky identity lookup and sticky persistence entirely +- AND the system MUST use the current account-selection flow unchanged + +### Requirement: Canonical Sticky Identity Resolution +The system MUST attempt sticky routing only when it can derive a canonical identity from an explicit, allowlisted request identity hierarchy. The identity resolution process MUST normalize equivalent inputs deterministically before hashing. The default hierarchy MUST NOT persist from `prompt_cache_key` alone unless the request path makes an explicit sticky-identity decision that authorizes that field as the canonical identity source. + +#### Scenario: Canonical identity is available +- GIVEN sticky sessions are enabled +- AND the request contains an allowlisted identity source that resolves to a canonical identity +- WHEN the request enters account selection +- THEN the system MUST normalize that identity deterministically +- AND the system MUST use only the normalized identity hash for sticky lookup and persistence + +#### Scenario: No canonical identity is available +- GIVEN sticky sessions are enabled +- AND the request does not resolve an explicit canonical identity +- WHEN the request enters account selection +- THEN the system MUST skip sticky lookup and sticky persistence +- AND the system MUST continue with the current rotation behavior unchanged + +#### Scenario: Prompt cache key is present without explicit identity authorization +- GIVEN sticky sessions are enabled +- AND the request includes `prompt_cache_key` +- AND no explicit sticky-identity decision authorizes `prompt_cache_key` as the canonical identity source +- WHEN the request enters account selection +- THEN the system MUST NOT persist or derive sticky identity from `prompt_cache_key` alone + +### Requirement: Initial Sticky Assignment +When sticky sessions are enabled, force mode is inactive, a canonical identity is available, and no reusable sticky assignment exists, the system MUST select an account using the currently active rotation strategy over the accounts that are valid for the request. After a successful selection, the system MUST persist a mapping from the canonical identity hash to the selected alias. + +#### Scenario: First request creates a sticky assignment +- GIVEN sticky sessions are enabled +- AND force mode is inactive +- AND a canonical identity is available +- AND no sticky mapping exists for that identity hash +- WHEN the request is routed successfully +- THEN the system MUST choose the alias using the active rotation strategy and current validity filters +- AND the system MUST persist the identity-hash-to-alias mapping after the selected account is confirmed usable + +### Requirement: Sticky Reuse Validation +For later requests with the same canonical identity hash, the system MUST reuse the mapped alias only if that account still exists, is enabled, is healthy for the requested model and workspace conditions, and yields a valid token at selection time. Successful sticky reuse MUST NOT advance `rotationIndex`. + +#### Scenario: Healthy sticky account is reused +- GIVEN sticky sessions are enabled +- AND a sticky mapping exists for the request identity hash +- AND the mapped alias still exists, is enabled, passes health checks, and provides a valid token +- WHEN another request with the same canonical identity is routed +- THEN the system MUST reuse the mapped alias +- AND the system MUST NOT advance `rotationIndex` + +### Requirement: Sticky Fallback and Replacement Selection +If a sticky mapping exists but the mapped alias is not currently reusable, and at least one other valid account remains, the system MUST run the active rotation strategy over the remaining valid accounts, select a replacement alias, and update the sticky mapping to that replacement. Sticky fallback MUST respect the same model, workspace, health, enablement, and token-validity rules as normal selection. Sticky reuse itself MUST NOT advance `rotationIndex`, but replacement selection MUST preserve the current strategy's index-advance semantics. + +#### Scenario: Sticky alias falls back to another valid account +- GIVEN sticky sessions are enabled +- AND a sticky mapping exists for the request identity hash +- AND the mapped alias is not reusable for the current request +- AND at least one other valid account remains +- WHEN the request is routed +- THEN the system MUST run the active rotation strategy over the remaining valid accounts +- AND the system MUST use the selected replacement alias for the request +- AND the system MUST update the sticky mapping to the replacement alias +- AND the system MUST preserve current `rotationIndex` advancement semantics for that replacement selection only + +### Requirement: Exhausted Sticky Fallback Semantics +If the mapped sticky alias is not reusable and no replacement account is available, the system MUST preserve the current non-sticky error semantics for the request. The system MUST retain the sticky mapping when the mapped alias failure is temporary, and MUST remove the sticky mapping when the failure is permanent. + +Temporary failures MUST include cooldown-based unavailability such as rate limiting, model unsupported cooldown, workspace deactivation cooldown, and transient token-refresh failure. Permanent failures MUST include missing aliases, explicitly disabled accounts, authentication invalidation, and irreversible token revocation. + +#### Scenario: Temporary failure keeps the mapping +- GIVEN sticky sessions are enabled +- AND a sticky mapping exists for the request identity hash +- AND the mapped alias is temporarily unavailable +- AND no replacement account is available +- WHEN the request fails using current selection semantics +- THEN the system MUST keep the sticky mapping for later retries +- AND the request outcome MUST remain consistent with current error behavior + +#### Scenario: Permanent failure removes the mapping +- GIVEN sticky sessions are enabled +- AND a sticky mapping exists for the request identity hash +- AND the mapped alias has failed permanently +- AND no replacement account is available +- WHEN the request fails using current selection semantics +- THEN the system MUST remove the sticky mapping +- AND the request outcome MUST remain consistent with current error behavior + +### Requirement: Force Mode Precedence Over Sticky Routing +Force mode MUST take precedence over sticky routing. When force mode is active, the system MUST follow the current forced-alias behavior and MUST NOT allow sticky lookup or sticky reassignment to override that forced choice. + +#### Scenario: Force mode bypasses sticky routing +- GIVEN sticky sessions are enabled +- AND force mode is active for a forced alias +- AND a sticky mapping exists for the request identity hash +- WHEN the request is routed +- THEN the system MUST follow force-mode selection semantics +- AND the system MUST NOT override the forced alias through sticky routing + +### Requirement: Sticky Session Verification Coverage +The change MUST be delivered TDD-first with focused automated checks that prove flag-off parity, canonical identity gating, first-assignment persistence, healthy sticky reuse, replacement reassignment, no-replacement temporary retention, no-replacement permanent cleanup, force-mode precedence, and the guarantee that sticky reuse does not advance `rotationIndex`. + +#### Scenario: Verification covers sticky-session behavior +- GIVEN the sticky-session change is implemented +- WHEN automated verification is prepared for the change +- THEN the test suite MUST include focused checks for all sticky-session routing branches that alter externally visible behavior or persisted state + +## MODIFIED Requirements + +## REMOVED Requirements diff --git a/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/tasks.md b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/tasks.md new file mode 100644 index 0000000..0f67d33 --- /dev/null +++ b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/tasks.md @@ -0,0 +1,74 @@ +# Tasks: Persistent Sticky Sessions + +## Phase 1: TDD Baseline and RED Coverage +- [x] 1.1 Extend `tests/unit/feature-flags.test.ts` for **Sticky Session Flag Gating** and **Disabled-Flag Persistence Bypass** from `specs/sticky-session-routing/spec.md` and `specs/sticky-session-persistence/spec.md`. + **Verification**: + - Run: `npm run test -- tests/unit/feature-flags.test.ts --runInBand` + - Expected: New sticky-session assertions fail first because the flag and bypass behavior are not implemented yet, while unrelated existing assertions remain green. + +- [x] 1.2 Add focused request-identity RED coverage in `tests/unit/index-sticky.test.ts` for **Canonical Sticky Identity Resolution**, **No canonical identity is available**, and **Prompt cache key is present without explicit identity authorization**. + **Verification**: + - Run: `npm run test -- tests/unit/index-sticky.test.ts --runInBand` + - Expected: The new suite fails first because canonical sticky identity resolution and request plumbing are not implemented yet. + +- [x] 1.3 Add sidecar-contract RED coverage in `tests/unit/sticky-sessions.test.ts` for **Versioned Sticky Sidecar Storage**, **Deterministic Hashed Identity Keys**, **Bounded Retention and Cleanup**, **Atomic and Secure Sticky Writes**, and **Corruption Isolation and Self-Healing Cleanup**. + **Verification**: + - Run: `npm run test -- tests/unit/sticky-sessions.test.ts --runInBand` + - Expected: The new suite fails first because the sticky sidecar module and persistence helpers do not exist yet. + +- [x] 1.4 Extend `tests/unit/rotation-strategy.test.ts` with RED scenarios for **Initial Sticky Assignment**, **Healthy sticky account is reused**, **Sticky alias falls back to another valid account**, **Temporary failure keeps the mapping**, **Permanent failure removes the mapping**, and **Force mode bypasses sticky routing**. + **Verification**: + - Run: `npm run test -- tests/unit/rotation-strategy.test.ts --runInBand` + - Expected: New sticky-routing assertions fail first, proving routing behavior is still non-sticky before production changes. + +## Phase 2: Settings and Sticky Persistence Foundations +- [x] 2.1 Implement sticky-session types and default-OFF settings in `src/types.ts` and `src/settings.ts`, including `stickySessionsEnabled`, sticky policy types, and persisted-settings support required by the new RED tests. + **Verification**: + - Run: `npm run test -- tests/unit/feature-flags.test.ts --runInBand` + - Expected: Sticky feature-flag defaults and persisted-settings assertions pass, with sticky behavior still inert outside the flag. + +- [x] 2.2 Extract or expose the minimum shared persistence primitives in `src/store.ts` and implement `src/sticky-sessions.ts` for path resolution, identity normalization/hashing, schema validation, TTL/LRU pruning, file-size/entry caps, corruption salvage, and atomic sidecar writes under the shared write lock. + **Verification**: + - Run: `npm run test -- tests/unit/sticky-sessions.test.ts --runInBand` + - Expected: Sidecar storage persists only hashed identity keys plus minimal metadata, prunes correctly, and reuses secure atomic-write coordination without touching `accounts.json` semantics. + +## Phase 3: Request Identity Plumbing +- [x] 3.1 Implement canonical sticky-identity resolution in `src/index.ts`, using only the allowlisted hierarchy from the design, rejecting empty identities, and excluding `prompt_cache_key` unless explicitly authorized by sticky policy. + **Verification**: + - Run: `npm run test -- tests/unit/index-sticky.test.ts --runInBand` + - Expected: Identity-resolution tests pass for header/body sources, missing identity fallback, and prompt-cache-key exclusion by default. + +- [x] 3.2 Wire resolved sticky context from `src/index.ts` into `getNextAccount()` without changing non-sticky request behavior when the flag is OFF or no canonical identity is available. + **Verification**: + - Run: `npm run test -- tests/unit/index-sticky.test.ts tests/unit/feature-flags.test.ts --runInBand` + - Expected: Request plumbing stays backward-compatible while sticky context is available only for opted-in requests. + +## Phase 4: Sticky Routing Integration +- [x] 4.1 Extend `src/rotation.ts` and related selection context types to perform sticky lookup only after force-mode short-circuiting, reuse healthy mapped aliases without advancing `rotationIndex`, and classify temporary vs permanent sticky failures. + **Verification**: + - Run: `npm run test -- tests/unit/rotation-strategy.test.ts --runInBand` + - Expected: Sticky-hit scenarios reuse the mapped alias, preserve force precedence, and keep `rotationIndex` unchanged on reuse. + +- [x] 4.2 Complete fallback reassignment and commit-time sticky updates in `src/rotation.ts` using `src/sticky-sessions.ts`, ensuring replacement selection reuses current strategy semantics and updates/removes mappings only after the chosen account is confirmed usable. + **Verification**: + - Run: `npm run test -- tests/unit/rotation-strategy.test.ts tests/unit/sticky-sessions.test.ts --runInBand` + - Expected: Replacement selection updates the sticky mapping correctly, temporary exhaustion retains mappings, permanent exhaustion removes them, and sidecar state remains bounded. + +## Phase 5: Focused Regression and Delivery Checks +- [x] 5.1 Add any narrow coordination regression needed in `tests/unit/store.test.ts` for shared write-lock or path-helper behavior that sticky persistence depends on, but only if the new sidecar tests expose uncovered store-helper seams. + **Verification**: + - Run: `npm run test -- tests/unit/store.test.ts tests/unit/sticky-sessions.test.ts --runInBand` + - Expected: Shared persistence helpers remain backward-compatible for `accounts.json` while supporting sticky-sidecar coordination. + +- [x] 5.2 Run the focused sticky-session regression matrix across feature flags, identity plumbing, sidecar persistence, routing, and helper coordination. + **Verification**: + - Run: `npm run test -- tests/unit/feature-flags.test.ts tests/unit/index-sticky.test.ts tests/unit/sticky-sessions.test.ts tests/unit/rotation-strategy.test.ts tests/unit/store.test.ts --runInBand` + - Expected: All sticky-session focused suites pass together with no regressions in the changed areas. + +- [x] 5.3 Run the repository type-check/lint gate after the focused suites pass. + **Verification**: + - Run: `npm run lint` + - Expected: TypeScript completes without errors for the sticky-session changes, with no build step executed. + +## Execution Order +Work strictly TDD-first: create RED tests for flags, identity plumbing, sidecar persistence, and routing before any production change; then implement settings/persistence primitives, then request plumbing, then routing integration, and finish with focused regression plus `npm run lint`. Do not run `npm run build`. diff --git a/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/verification.md b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/verification.md new file mode 100644 index 0000000..316e774 --- /dev/null +++ b/openspec/changes/archive/2026-05-16-persistent-sticky-sessions/verification.md @@ -0,0 +1,58 @@ +# Verification Report: Persistent Sticky Sessions + +## Completeness +- Reviewed artifacts: `proposal.md`, `design.md`, `tasks.md`, `specs/sticky-session-routing/spec.md`, `specs/sticky-session-persistence/spec.md`. +- Reviewed implementation: `src/index.ts`, `src/rotation.ts`, `src/settings.ts`, `src/types.ts`, `src/sticky-sessions.ts`. +- Reviewed focused verification suites: `tests/unit/feature-flags.test.ts`, `tests/unit/index-sticky.test.ts`, `tests/unit/sticky-sessions.test.ts`, `tests/unit/rotation-strategy.test.ts`, `tests/unit/store.test.ts`. +- Tasks file remains fully checked off. + +## Build and Test Evidence +- Re-ran focused regression matrix: + - `npm run test -- tests/unit/feature-flags.test.ts tests/unit/index-sticky.test.ts tests/unit/sticky-sessions.test.ts tests/unit/rotation-strategy.test.ts tests/unit/store.test.ts --runInBand` + - Result: **5/5 suites passed, 58/58 tests passed**. +- Re-ran lint gate: + - `npm run lint` + - Result: **passed**. +- Build was **not** run, per task constraints. +- LSP diagnostics could not be collected in this environment because the configured Deno LSP is not installed. + +## Compliance Matrix + +| Area | Scenario / requirement | Evidence | Status | +|---|---|---|---| +| Routing | Flag off preserves current behavior | `tests/unit/index-sticky.test.ts:230-255`, `tests/unit/rotation-strategy.test.ts:376-395`, `src/index.ts:721-732`, `src/rotation.ts:274-275` | PASS | +| Routing | Canonical identity is available | `tests/unit/index-sticky.test.ts:12-34`, `src/index.ts:184-220` | PASS | +| Routing | No canonical identity is available | `tests/unit/index-sticky.test.ts:36-49`, `src/index.ts:184-220` | PASS | +| Routing | `prompt_cache_key` not used by default | `tests/unit/index-sticky.test.ts:51-63`, `src/index.ts:195-220` | PASS | +| Routing | First request creates sticky assignment | `tests/unit/rotation-strategy.test.ts:209-226`, `src/rotation.ts:449-473` | PASS | +| Routing | Healthy sticky account is reused without advancing `rotationIndex` | `tests/unit/rotation-strategy.test.ts:228-250`, `src/rotation.ts:278-317` | PASS | +| Routing | Sticky alias falls back to another valid account | `tests/unit/rotation-strategy.test.ts:252-275`, `src/rotation.ts:324-343,444-473` | PASS | +| Routing | Expired runtime sticky mappings are ignored and replaced through normal selection | `tests/unit/rotation-strategy.test.ts:277-298`, `tests/unit/sticky-sessions.test.ts:169-195`, `src/sticky-sessions.ts:205-217` | PASS | +| Routing | Temporary failure keeps the mapping | `tests/unit/rotation-strategy.test.ts:300-322`, `src/rotation.ts:327-336` | PASS | +| Routing | Permanent failure removes the mapping | `tests/unit/rotation-strategy.test.ts:324-374`, `src/rotation.ts:327-334,487-497` | PASS | +| Routing | Force mode bypasses sticky routing | `tests/unit/rotation-strategy.test.ts:397-420`, `src/rotation.ts:240-252` | PASS | +| Routing | Verification coverage exists for sticky routing branches | Focused routing/identity suites above; matrix rerun passed end-to-end | PASS | +| Persistence | Mapping is stored in `sticky-sessions.json` with minimal metadata | `tests/unit/sticky-sessions.test.ts:65-98`, `src/sticky-sessions.ts:295-318` | PASS | +| Persistence | Equivalent identities map to one hashed key | `tests/unit/sticky-sessions.test.ts:100-135`, `src/sticky-sessions.ts:57-64,295-318` | PASS | +| Persistence | Cleanup prunes expired entries first and then LRU entries to satisfy bounds | `tests/unit/sticky-sessions.test.ts:137-167`, `src/sticky-sessions.ts:114-142,155-166,284-292` | PASS | +| Persistence | Atomic and secure sticky writes reuse the shared write lock discipline | `tests/unit/sticky-sessions.test.ts:197-222`, `src/sticky-sessions.ts:220-282`, `src/store.ts:506-513` | PASS | +| Persistence | Existing sidecar is ignored while flag is off | `tests/unit/rotation-strategy.test.ts:376-395`, `src/rotation.ts:274-275` | PASS | +| Persistence | Sidecar corruption does not break account selection and self-heals valid content | `tests/unit/sticky-sessions.test.ts:224-276`, `src/sticky-sessions.ts:85-112,155-168,171-203` | PASS | +| Persistence | Verification coverage exists for sidecar guarantees | Focused sidecar suite covers schema, hashing, TTL/LRU pruning, runtime read cleanup, corruption handling, and atomic writes | PASS | + +## Design Coherence +- Sticky routing remains a pre-selection layer over the current rotation strategies; no new rotation strategy was introduced. +- Force-mode precedence, post-validation persistence, hashed identity storage, sidecar isolation, and shared write-lock coordination all remain aligned with the design. +- The previous blocker is resolved because runtime lookup now loads the sidecar through pruning logic and rewrites the cleaned snapshot before returning an assignment. + +## Issues Found +- None blocking. +- Environment note: IDE/LSP diagnostics remain unavailable until Deno is installed, but CLI verification required by this task passed. + +## Verdict +**PASS** + +## Summary +- Re-verification confirms the prior TTL/LRU runtime lookup gap is fixed. +- Re-verification also confirms the flag-OFF sidecar bypass scenario is covered and passing. +- Compliance summary: **19 PASS / 0 FAIL / 0 WARN** across the reviewed matrix rows above. diff --git a/openspec/specs/sticky-session-persistence/spec.md b/openspec/specs/sticky-session-persistence/spec.md new file mode 100644 index 0000000..a76803e --- /dev/null +++ b/openspec/specs/sticky-session-persistence/spec.md @@ -0,0 +1,74 @@ +# Sticky Session Persistence + +## Requirements + +### Requirement: Versioned Sticky Sidecar Storage +When sticky sessions are enabled, the system MUST persist sticky mappings in a dedicated sidecar file named `sticky-sessions.json` under the existing store directory, separate from `accounts.json`. The sidecar MUST use a versioned schema and MUST store only minimal sticky metadata required for reuse and cleanup. + +Minimal sticky metadata MUST include the canonical identity hash key, mapped alias, creation timestamp, and last-used timestamp. The sidecar MUST NOT store raw identities, prompt bodies, raw `prompt_cache_key` values, account tokens, or unrelated telemetry. + +#### Scenario: Sticky mapping is stored in the sidecar +- GIVEN sticky sessions are enabled +- AND a sticky mapping is created or updated +- WHEN the mapping is persisted +- THEN the system MUST write it to `sticky-sessions.json` in the configured store directory +- AND the persisted record MUST use the versioned sticky schema with minimal metadata only + +### Requirement: Deterministic Hashed Identity Keys +The system MUST persist only deterministic one-way hashes of normalized canonical identities so the same canonical identity resolves to the same sticky key across process restarts without exposing the raw identifier on disk. + +#### Scenario: Equivalent identities map to one hashed key +- GIVEN two requests normalize to the same canonical identity +- WHEN sticky persistence resolves their storage key +- THEN the system MUST produce the same hashed key for both requests +- AND the persisted sidecar MUST NOT contain the raw identity value + +### Requirement: Bounded Retention and Cleanup +The sticky sidecar MUST remain bounded by TTL and LRU cleanup, maximum entry count, and maximum file-size protections. The system MUST remove expired entries and least-recently-used entries before persisting data that would exceed configured limits. + +#### Scenario: Cleanup prunes expired or least-recently-used entries +- GIVEN sticky sessions are enabled +- AND the sidecar contains expired entries or exceeds configured entry or size limits +- WHEN the sidecar is loaded or updated +- THEN the system MUST prune expired entries first +- AND the system MUST evict least-recently-used entries as needed to restore limits before completing the write + +### Requirement: Atomic and Secure Sticky Writes +Sticky sidecar updates MUST use atomic write behavior, safe replacement, and the same shared write-lock discipline used for the primary store so concurrent updates do not leave partial files behind. The sidecar file and its temporary replacements MUST use secure local permissions equivalent to the current store protections. + +#### Scenario: Sticky data is written atomically +- GIVEN sticky sessions are enabled +- AND a sticky mapping change must be persisted +- WHEN the sidecar is written +- THEN the system MUST serialize the update through the shared write-lock discipline +- AND the system MUST write through a temporary file and atomic replacement flow +- AND the resulting sidecar file MUST be stored with secure local permissions + +### Requirement: Disabled-Flag Persistence Bypass +When the sticky-session feature flag is OFF, the system MUST NOT read from or write to `sticky-sessions.json`, and the presence of an old sidecar file MUST NOT change current routing behavior. + +#### Scenario: Existing sidecar is ignored while flag is off +- GIVEN sticky sessions are disabled +- AND `sticky-sessions.json` already exists +- WHEN a request is routed +- THEN the system MUST ignore sticky-sidecar contents +- AND the request outcome MUST remain identical to the current non-sticky flow + +### Requirement: Corruption Isolation and Self-Healing Cleanup +Corruption or invalid entries in `sticky-sessions.json` MUST be isolated from `accounts.json` and MUST NOT corrupt normal account-store behavior. The system MUST discard invalid sticky entries during load, preserve valid sticky entries when possible, and continue using current routing/error semantics if sticky data cannot be recovered. + +#### Scenario: Sidecar corruption does not break account selection +- GIVEN sticky sessions are enabled +- AND `sticky-sessions.json` contains corrupt or schema-invalid content +- WHEN the sidecar is loaded +- THEN the system MUST avoid corrupting `accounts.json` +- AND the system MUST discard or repair invalid sticky content on a best-effort basis +- AND requests without recoverable sticky state MUST fall back to current routing semantics + +### Requirement: Sticky Persistence Verification Coverage +The change MUST include focused automated checks for sidecar schema versioning, hashed-only persistence, flag-off bypass, TTL/LRU cleanup, entry and size limit enforcement, corruption handling, and atomic-write or lock-coordination behavior. + +#### Scenario: Verification covers sticky-sidecar persistence +- GIVEN the sticky-sidecar change is implemented +- WHEN automated verification is prepared for the change +- THEN the test suite MUST include focused checks for the persistence guarantees that protect correctness, privacy, and bounded growth diff --git a/openspec/specs/sticky-session-routing/spec.md b/openspec/specs/sticky-session-routing/spec.md new file mode 100644 index 0000000..9f7d29b --- /dev/null +++ b/openspec/specs/sticky-session-routing/spec.md @@ -0,0 +1,115 @@ +# Sticky Session Routing + +## Requirements + +### Requirement: Sticky Session Flag Gating +The system MUST keep sticky-session behavior disabled by default. When the sticky-session feature flag is OFF, account selection, persistence reads/writes, force-mode handling, rotation state, and error behavior MUST remain identical to the current non-sticky flow. + +#### Scenario: Flag off preserves current behavior +- GIVEN sticky sessions are disabled +- WHEN a request is routed through the existing selection pipeline +- THEN the system MUST skip sticky identity lookup and sticky persistence entirely +- AND the system MUST use the current account-selection flow unchanged + +### Requirement: Canonical Sticky Identity Resolution +The system MUST attempt sticky routing only when it can derive a canonical identity from an explicit, allowlisted request identity hierarchy. The identity resolution process MUST normalize equivalent inputs deterministically before hashing. The default hierarchy MUST NOT persist from `prompt_cache_key` alone unless the request path makes an explicit sticky-identity decision that authorizes that field as the canonical identity source. + +#### Scenario: Canonical identity is available +- GIVEN sticky sessions are enabled +- AND the request contains an allowlisted identity source that resolves to a canonical identity +- WHEN the request enters account selection +- THEN the system MUST normalize that identity deterministically +- AND the system MUST use only the normalized identity hash for sticky lookup and persistence + +#### Scenario: No canonical identity is available +- GIVEN sticky sessions are enabled +- AND the request does not resolve an explicit canonical identity +- WHEN the request enters account selection +- THEN the system MUST skip sticky lookup and sticky persistence +- AND the system MUST continue with the current rotation behavior unchanged + +#### Scenario: Prompt cache key is present without explicit identity authorization +- GIVEN sticky sessions are enabled +- AND the request includes `prompt_cache_key` +- AND no explicit sticky-identity decision authorizes `prompt_cache_key` as the canonical identity source +- WHEN the request enters account selection +- THEN the system MUST NOT persist or derive sticky identity from `prompt_cache_key` alone + +### Requirement: Initial Sticky Assignment +When sticky sessions are enabled, force mode is inactive, a canonical identity is available, and no reusable sticky assignment exists, the system MUST select an account using the currently active rotation strategy over the accounts that are valid for the request. After a successful selection, the system MUST persist a mapping from the canonical identity hash to the selected alias. + +#### Scenario: First request creates a sticky assignment +- GIVEN sticky sessions are enabled +- AND force mode is inactive +- AND a canonical identity is available +- AND no sticky mapping exists for that identity hash +- WHEN the request is routed successfully +- THEN the system MUST choose the alias using the active rotation strategy and current validity filters +- AND the system MUST persist the identity-hash-to-alias mapping after the selected account is confirmed usable + +### Requirement: Sticky Reuse Validation +For later requests with the same canonical identity hash, the system MUST reuse the mapped alias only if that account still exists, is enabled, is healthy for the requested model and workspace conditions, and yields a valid token at selection time. Successful sticky reuse MUST NOT advance `rotationIndex`. + +#### Scenario: Healthy sticky account is reused +- GIVEN sticky sessions are enabled +- AND a sticky mapping exists for the request identity hash +- AND the mapped alias still exists, is enabled, passes health checks, and provides a valid token +- WHEN another request with the same canonical identity is routed +- THEN the system MUST reuse the mapped alias +- AND the system MUST NOT advance `rotationIndex` + +### Requirement: Sticky Fallback and Replacement Selection +If a sticky mapping exists but the mapped alias is not currently reusable, and at least one other valid account remains, the system MUST run the active rotation strategy over the remaining valid accounts, select a replacement alias, and update the sticky mapping to that replacement. Sticky fallback MUST respect the same model, workspace, health, enablement, and token-validity rules as normal selection. Sticky reuse itself MUST NOT advance `rotationIndex`, but replacement selection MUST preserve the current strategy's index-advance semantics. + +#### Scenario: Sticky alias falls back to another valid account +- GIVEN sticky sessions are enabled +- AND a sticky mapping exists for the request identity hash +- AND the mapped alias is not reusable for the current request +- AND at least one other valid account remains +- WHEN the request is routed +- THEN the system MUST run the active rotation strategy over the remaining valid accounts +- AND the system MUST use the selected replacement alias for the request +- AND the system MUST update the sticky mapping to the replacement alias +- AND the system MUST preserve current `rotationIndex` advancement semantics for that replacement selection only + +### Requirement: Exhausted Sticky Fallback Semantics +If the mapped sticky alias is not reusable and no replacement account is available, the system MUST preserve the current non-sticky error semantics for the request. The system MUST retain the sticky mapping when the mapped alias failure is temporary, and MUST remove the sticky mapping when the failure is permanent. + +Temporary failures MUST include cooldown-based unavailability such as rate limiting, model unsupported cooldown, workspace deactivation cooldown, and transient token-refresh failure. Permanent failures MUST include missing aliases, explicitly disabled accounts, authentication invalidation, and irreversible token revocation. + +#### Scenario: Temporary failure keeps the mapping +- GIVEN sticky sessions are enabled +- AND a sticky mapping exists for the request identity hash +- AND the mapped alias is temporarily unavailable +- AND no replacement account is available +- WHEN the request fails using current selection semantics +- THEN the system MUST keep the sticky mapping for later retries +- AND the request outcome MUST remain consistent with current error behavior + +#### Scenario: Permanent failure removes the mapping +- GIVEN sticky sessions are enabled +- AND a sticky mapping exists for the request identity hash +- AND the mapped alias has failed permanently +- AND no replacement account is available +- WHEN the request fails using current selection semantics +- THEN the system MUST remove the sticky mapping +- AND the request outcome MUST remain consistent with current error behavior + +### Requirement: Force Mode Precedence Over Sticky Routing +Force mode MUST take precedence over sticky routing. When force mode is active, the system MUST follow the current forced-alias behavior and MUST NOT allow sticky lookup or sticky reassignment to override that forced choice. + +#### Scenario: Force mode bypasses sticky routing +- GIVEN sticky sessions are enabled +- AND force mode is active for a forced alias +- AND a sticky mapping exists for the request identity hash +- WHEN the request is routed +- THEN the system MUST follow force-mode selection semantics +- AND the system MUST NOT override the forced alias through sticky routing + +### Requirement: Sticky Session Verification Coverage +The change MUST be delivered TDD-first with focused automated checks that prove flag-off parity, canonical identity gating, first-assignment persistence, healthy sticky reuse, replacement reassignment, no-replacement temporary retention, no-replacement permanent cleanup, force-mode precedence, and the guarantee that sticky reuse does not advance `rotationIndex`. + +#### Scenario: Verification covers sticky-session behavior +- GIVEN the sticky-session change is implemented +- WHEN automated verification is prepared for the change +- THEN the test suite MUST include focused checks for all sticky-session routing branches that alter externally visible behavior or persisted state From 2207d58741c6d0c70dab03991f6810fd90f28fcc Mon Sep 17 00:00:00 2001 From: EremesNG Date: Sun, 17 May 2026 14:02:50 -0600 Subject: [PATCH 04/33] chore(openspec): Add project config --- openspec/config.yaml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 openspec/config.yaml diff --git a/openspec/config.yaml b/openspec/config.yaml new file mode 100644 index 0000000..d266cfa --- /dev/null +++ b/openspec/config.yaml @@ -0,0 +1,33 @@ +schema: spec-driven + +context: | + Tech stack: TypeScript 5 + Node.js ESM CLI package (pnpm workspace metadata present). + Architecture: Single-package source layout under src/ with tests/ and a bundled Python helper in auto-login/. + Testing: Jest 29 with ts-jest; test files matched from tests/**/*.test.ts. + Style: TypeScript strict checks via ts-jest/tsconfig signals; dedicated formatter/linter config not detected. + CI: GitHub Actions workflow present for publish-npm. + +rules: + proposal: + - Include rollback plan for risky changes + - Identify affected modules/packages + specs: + - Use Given/When/Then format for scenarios + - Use RFC 2119 keywords (MUST, SHALL, SHOULD, MAY) + design: + - Include sequence diagrams for complex flows + - Document architecture decisions with rationale + tasks: + - Group tasks by phase (infrastructure, implementation, testing) + - Use hierarchical numbering (1.1, 1.2, etc.) + - Keep tasks small enough to complete in one session + apply: + - Follow existing code patterns and conventions + tdd: false + test_command: '' + verify: + test_command: '' + build_command: '' + coverage_threshold: 0 + archive: + - Warn before merging destructive deltas From 18baf7c6f4500211c9022779e90a15277e43af5d Mon Sep 17 00:00:00 2001 From: EremesNG Date: Sun, 17 May 2026 14:02:50 -0600 Subject: [PATCH 05/33] feat(server): add sticky sessions and SPA asset serving --- .gitignore | 2 + src/index.ts | 48 +- src/logger.ts | 24 +- src/rotation.ts | 22 +- src/settings.ts | 205 +- src/sticky-sessions.ts | 77 +- src/store.ts | 3 +- src/types.ts | 19 + src/web.ts | 2415 ++--------------- .../dashboard-api-contract.test.ts | 579 ++++ .../sticky-session-admin-api.test.ts | 330 +++ tests/integration/web-server.test.ts | 74 +- .../integration/weighted-presets-api.test.ts | 493 ++++ tests/unit/index-sticky.test.ts | 79 +- tests/unit/limits-refresh.test.ts | 19 +- tests/unit/refresh-queue.test.ts | 13 +- tests/unit/usage-limits.test.ts | 1 + tests/web-headless/dashboard-smoke.test.ts | 47 +- 18 files changed, 2152 insertions(+), 2298 deletions(-) create mode 100644 tests/integration/dashboard-api-contract.test.ts create mode 100644 tests/integration/sticky-session-admin-api.test.ts create mode 100644 tests/integration/weighted-presets-api.test.ts diff --git a/.gitignore b/.gitignore index 797cc0a..f2d390a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,6 @@ auto-login/debug_*.png auto-login/__pycache__/ /.idea/ /dist/ +/web/node_modules/ opencode.json +/.playwright-cli/ diff --git a/src/index.ts b/src/index.ts index 7e3d2ec..70c5e05 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,12 +18,11 @@ import { } from './rotation.js' import { getDefaultModels } from './models.js' import { getForceState, isForceActive } from './force-mode.js' -import { getRuntimeSettings } from './settings.js' +import { getRuntimeSettings, getStickySessionRuntimeSettings } from './settings.js' import { listAccounts, updateAccount, loadStore } from './store.js' import { hashStickyIdentity } from './sticky-sessions.js' import { DEFAULT_CONFIG, - DEFAULT_STICKY_SESSION_SETTINGS, type AccountRateLimits, type PluginConfig } from './types.js' @@ -179,6 +178,7 @@ type ResolveStickyIdentityOptions = { prompt_cache_key?: unknown } allowPromptCacheKey: boolean + identitySources: StickyIdentitySource[] } function normalizeStickyIdentityValue(value: unknown): string | null { @@ -205,19 +205,29 @@ function resolveStickyIdentitySource( } export function resolveStickyIdentity(options: ResolveStickyIdentityOptions): ResolvedStickyIdentity | null { - const headerSessionId = options.headers.get('session_id') - const headerConversationId = options.headers.get('conversation_id') - const body = options.body const bodyMetadata = body?.metadata - return ( - resolveStickyIdentitySource('header:session_id', headerSessionId, options.allowPromptCacheKey) || - resolveStickyIdentitySource('header:conversation_id', headerConversationId, options.allowPromptCacheKey) || - resolveStickyIdentitySource('body:metadata.session_id', bodyMetadata?.session_id, options.allowPromptCacheKey) || - resolveStickyIdentitySource('body:metadata.conversation_id', bodyMetadata?.conversation_id, options.allowPromptCacheKey) || - resolveStickyIdentitySource('body:prompt_cache_key', body?.prompt_cache_key, options.allowPromptCacheKey) - ) + for (const source of options.identitySources) { + const value = (() => { + switch (source) { + case 'header:session_id': + return options.headers.get('session_id') + case 'header:conversation_id': + return options.headers.get('conversation_id') + case 'body:metadata.session_id': + return bodyMetadata?.session_id + case 'body:metadata.conversation_id': + return bodyMetadata?.conversation_id + case 'body:prompt_cache_key': + return body?.prompt_cache_key + } + })() + const resolved = resolveStickyIdentitySource(source, value, options.allowPromptCacheKey) + if (resolved) return resolved + } + + return null } function ensureContentType(headers: Headers): Headers { @@ -719,11 +729,15 @@ const MultiAuthPlugin: Plugin = async ({ client, $, serverUrl, project, director } const sticky = settings.settings.featureFlags?.stickySessionsEnabled - ? resolveStickyIdentity({ - headers: new Headers(init?.headers || {}), - body, - allowPromptCacheKey: DEFAULT_STICKY_SESSION_SETTINGS.allowPromptCacheKey - }) + ? (() => { + const stickySettings = getStickySessionRuntimeSettings() + return resolveStickyIdentity({ + headers: new Headers(init?.headers || {}), + body, + allowPromptCacheKey: stickySettings.allowPromptCacheKey, + identitySources: stickySettings.identitySources + }) + })() : null const rotation = await getNextAccount(effectiveConfig, { diff --git a/src/logger.ts b/src/logger.ts index be68b13..1f63209 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -45,12 +45,30 @@ export function getLogPath(): string { return LOG_FILE } -export function readLogTail(maxLines = MAX_LOG_LINES): string[] { +export interface LogLine { + time: string + level: string + message: string +} + +const LOG_LINE_RE = /^(\S+)\s+\[(\w+)\]\s+(.*)$/ + +function parseLogLine(raw: string): LogLine { + const match = raw.match(LOG_LINE_RE) + if (match) { + return { time: match[1], level: match[2].toLowerCase(), message: match[3] } + } + // Fallback: treat entire line as message with unknown level + return { time: '', level: 'unknown', message: raw } +} + +export function readLogTail(maxLines = MAX_LOG_LINES): LogLine[] { try { if (!fs.existsSync(LOG_FILE)) return [] const data = fs.readFileSync(LOG_FILE, 'utf-8') - const lines = data.split('\n').filter(Boolean) - return lines.slice(Math.max(0, lines.length - maxLines)) + const rawLines = data.split('\n').filter(Boolean) + const tail = rawLines.slice(Math.max(0, rawLines.length - maxLines)) + return tail.map(parseLogLine) } catch { return [] } diff --git a/src/rotation.ts b/src/rotation.ts index 27b4b77..954f2ea 100644 --- a/src/rotation.ts +++ b/src/rotation.ts @@ -2,12 +2,12 @@ import { getStoreDiagnostics, loadStore, saveStore, updateAccount } from './stor import { ensureValidToken } from './auth.js' import { decodeJwtPayload, getPlanTypeFromClaims } from './codex-auth.js' import { isForceActive, checkAndAutoClearForce, getForceState, clearForce } from './force-mode.js' -import { getRuntimeSettings, calculateWeightedSelection } from './settings.js' +import { getRuntimeSettings, getStickySessionRuntimeSettings, calculateWeightedSelection } from './settings.js' import { getStickyAssignment, removeStickyAssignment, upsertStickyAssignment } from './sticky-sessions.js' import { - DEFAULT_STICKY_SESSION_SETTINGS, type AccountCredentials, - DEFAULT_CONFIG + DEFAULT_CONFIG, + type StickySessionSettings } from './types.js' import type { ResolvedStickyIdentity } from './types.js' @@ -162,7 +162,8 @@ function classifyStickyFailure( async function persistStickySelection( sticky: ResolvedStickyIdentity | null | undefined, alias: string, - now: number + now: number, + stickySettings: StickySessionSettings ): Promise { if (!sticky) return @@ -170,7 +171,7 @@ async function persistStickySelection( canonicalIdentity: sticky.canonical, alias, now, - settings: DEFAULT_STICKY_SESSION_SETTINGS + settings: stickySettings }) } @@ -270,6 +271,7 @@ export async function getNextAccount( })() const runtimeSettings = getRuntimeSettings() + const stickySettings = getStickySessionRuntimeSettings() const rotationStrategy = runtimeSettings.settings.rotationStrategy || config.rotationStrategy const sticky = runtimeSettings.settings.featureFlags?.stickySessionsEnabled === true ? selection?.sticky ?? null : null @@ -280,7 +282,7 @@ export async function getNextAccount( await getStickyAssignment({ stickyHash: sticky.hash, now, - settings: DEFAULT_STICKY_SESSION_SETTINGS + settings: stickySettings }) )?.alias @@ -303,7 +305,7 @@ export async function getNextAccount( store.activeAlias = mappedAlias store.lastRotation = now saveStore(store) - await persistStickySelection(sticky, mappedAlias, now) + await persistStickySelection(sticky, mappedAlias, now, stickySettings) const currentForceState = getForceState() return { @@ -329,7 +331,7 @@ export async function getNextAccount( await removeStickyAssignment({ stickyHash: sticky.hash, now, - settings: DEFAULT_STICKY_SESSION_SETTINGS + settings: stickySettings }) } console.warn('[multi-auth] No available accounts (rate-limited or invalidated).') @@ -470,7 +472,7 @@ export async function getNextAccount( store.rotationIndex = nextIndex(candidate) } saveStore(store) - await persistStickySelection(sticky, candidate, now) + await persistStickySelection(sticky, candidate, now, stickySettings) const currentForceState = getForceState() return { @@ -492,7 +494,7 @@ export async function getNextAccount( await removeStickyAssignment({ stickyHash: sticky.hash, now, - settings: DEFAULT_STICKY_SESSION_SETTINGS + settings: stickySettings }) } diff --git a/src/settings.ts b/src/settings.ts index f1c1ab5..e6764e8 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,11 +1,17 @@ import { loadStore, saveStore, updateAccount } from './store.js' import { logInfo, logError } from './logger.js' import { + ALL_STICKY_IDENTITY_SOURCES, DEFAULT_FEATURE_FLAGS, DEFAULT_ROTATION_SETTINGS, + DEFAULT_STICKY_SESSION_SETTINGS, WEIGHTED_PRESETS, validateSettings, type RotationSettings, + type StickyIdentitySource, + type StickySessionAdminConfig, + type StickySessionSettings, + type PersistedStickySessionSettings, type WeightPreset, type SettingsValidationError } from './types.js' @@ -18,6 +24,105 @@ export interface SettingsResult { errors?: SettingsValidationError[] } +export interface StickySessionConfigResult { + config: StickySessionAdminConfig + source: 'default' | 'persisted' + errors?: SettingsValidationError[] +} + +function getStoredStickySessionSettings(): PersistedStickySessionSettings | undefined { + const store = loadStore() + const sticky = store.stickySessions + if (!sticky) return undefined + + return { + ...DEFAULT_STICKY_SESSION_SETTINGS, + ...sticky, + identitySources: Array.isArray(sticky.identitySources) + ? sticky.identitySources + : DEFAULT_STICKY_SESSION_SETTINGS.identitySources + } +} + +function validateStickyIdentitySources(value: unknown): value is StickyIdentitySource[] { + return ( + Array.isArray(value) && + value.every((source) => typeof source === 'string' && ALL_STICKY_IDENTITY_SOURCES.includes(source as StickyIdentitySource)) + ) +} + +export function validateStickySessionConfig(config: StickySessionAdminConfig): SettingsValidationError[] { + const errors: SettingsValidationError[] = [] + + if (typeof config.enabled !== 'boolean') { + errors.push({ + field: 'enabled', + message: 'Enabled must be a boolean', + constraint: 'typeof enabled === boolean' + }) + } + + if (!validateStickyIdentitySources(config.identitySources)) { + errors.push({ + field: 'identitySources', + message: 'Identity sources must be a non-empty array of supported sticky identity sources', + constraint: `identitySources ∈ ${ALL_STICKY_IDENTITY_SOURCES.join(', ')}` + }) + } else { + if (config.identitySources.length === 0) { + errors.push({ + field: 'identitySources', + message: 'At least one identity source is required', + constraint: 'identitySources.length > 0' + }) + } + + if (new Set(config.identitySources).size !== config.identitySources.length) { + errors.push({ + field: 'identitySources', + message: 'Identity sources must not contain duplicates', + constraint: 'identitySources are unique' + }) + } + } + + if (typeof config.allowPromptCacheKey !== 'boolean') { + errors.push({ + field: 'allowPromptCacheKey', + message: 'allowPromptCacheKey must be a boolean', + constraint: 'typeof allowPromptCacheKey === boolean' + }) + } + + if ( + Array.isArray(config.identitySources) && + config.identitySources.includes('body:prompt_cache_key') && + config.allowPromptCacheKey !== true + ) { + errors.push({ + field: 'identitySources', + message: 'body:prompt_cache_key requires allowPromptCacheKey to be true', + constraint: 'allowPromptCacheKey === true when body:prompt_cache_key is configured' + }) + } + + for (const [field, value] of [ + ['ttlMs', config.ttlMs], + ['maxEntries', config.maxEntries], + ['maxFileBytes', config.maxFileBytes] + ] as const) { + if (!Number.isInteger(value) || value <= 0) { + errors.push({ + field, + message: `${field} must be a positive integer`, + constraint: `${field} ∈ positive integers` + }) + } + } + + return errors +} + function resolveSettings(includeEnvOverrides: boolean): SettingsResult { const store = loadStore() @@ -112,6 +217,84 @@ export function getRuntimeSettings(): SettingsResult { return resolveSettings(false) } +export function getStickySessionRuntimeSettings(): StickySessionSettings { + const stored = getStoredStickySessionSettings() + return { + ...DEFAULT_STICKY_SESSION_SETTINGS, + ...stored, + identitySources: stored?.identitySources || DEFAULT_STICKY_SESSION_SETTINGS.identitySources + } +} + +export function getStickySessionConfig(): StickySessionConfigResult { + const sticky = getStoredStickySessionSettings() + const enabled = getRuntimeSettings().settings.featureFlags?.stickySessionsEnabled === true + const config: StickySessionAdminConfig = { + enabled, + ...DEFAULT_STICKY_SESSION_SETTINGS, + ...sticky, + identitySources: sticky?.identitySources || DEFAULT_STICKY_SESSION_SETTINGS.identitySources + } + const errors = validateStickySessionConfig(config) + + return { + config, + source: sticky ? 'persisted' : 'default', + errors: errors.length > 0 ? errors : undefined + } +} + +export function updateStickySessionConfig( + updates: StickySessionAdminConfig, + actor: string = 'system' +): { success: boolean; config?: StickySessionAdminConfig; errors?: SettingsValidationError[] } { + const now = Date.now() + const config: StickySessionAdminConfig = { + enabled: updates.enabled, + identitySources: updates.identitySources, + allowPromptCacheKey: updates.allowPromptCacheKey, + ttlMs: updates.ttlMs, + maxEntries: updates.maxEntries, + maxFileBytes: updates.maxFileBytes, + updatedAt: now, + updatedBy: actor + } + + const errors = validateStickySessionConfig(config) + if (errors.length > 0) { + logError(`Sticky-session config update failed validation: ${errors.map(e => e.message).join(', ')}`) + return { success: false, errors } + } + + const store = loadStore() + const current = getRuntimeSettings().settings + store.settings = { + ...DEFAULT_ROTATION_SETTINGS, + ...current, + featureFlags: { + ...DEFAULT_FEATURE_FLAGS, + ...(current.featureFlags || {}), + stickySessionsEnabled: config.enabled + }, + updatedAt: now, + updatedBy: actor + } + store.rotationStrategy = store.settings.rotationStrategy + store.stickySessions = { + identitySources: [...config.identitySources], + allowPromptCacheKey: config.allowPromptCacheKey, + ttlMs: config.ttlMs, + maxEntries: config.maxEntries, + maxFileBytes: config.maxFileBytes, + updatedAt: now, + updatedBy: actor + } + saveStore(store) + + logInfo(`Sticky-session config updated by ${actor}: ${JSON.stringify({ ...config, updatedAt: now, updatedBy: actor })}`) + return { success: true, config } +} + // Phase F: Update settings with validation export function updateSettings( updates: Partial & { featureFlags?: Partial> }, @@ -180,12 +363,16 @@ export function applyPreset( accountWeights[alias] = weight }) } else if (preset === 'conservative') { - // Weights based on limit health + // Weights based on limit health (normalized to percentages) accounts.forEach(alias => { const account = store.accounts[alias] - const fiveHourRemaining = account.rateLimits?.fiveHour?.remaining ?? 50 - const weeklyRemaining = account.rateLimits?.weekly?.remaining ?? 50 - const health = (fiveHourRemaining + weeklyRemaining) / 2 + const fiveHourLimit = account.rateLimits?.fiveHour?.limit ?? 100 + const fiveHourRemaining = account.rateLimits?.fiveHour?.remaining ?? (fiveHourLimit / 2) + const weeklyLimit = account.rateLimits?.weekly?.limit ?? 1000 + const weeklyRemaining = account.rateLimits?.weekly?.remaining ?? (weeklyLimit / 2) + const fiveHourPct = fiveHourLimit > 0 ? (fiveHourRemaining / fiveHourLimit) * 100 : 50 + const weeklyPct = weeklyLimit > 0 ? (weeklyRemaining / weeklyLimit) * 100 : 50 + const health = (fiveHourPct + weeklyPct) / 2 accountWeights[alias] = health / 100 }) // Normalize to sum to 1 @@ -199,9 +386,13 @@ export function applyPreset( // Favor accounts with high usage (lower remaining) accounts.forEach(alias => { const account = store.accounts[alias] - const fiveHourRemaining = account.rateLimits?.fiveHour?.remaining ?? 50 - const weeklyRemaining = account.rateLimits?.weekly?.remaining ?? 50 - const health = (fiveHourRemaining + weeklyRemaining) / 2 + const fiveHourLimit = account.rateLimits?.fiveHour?.limit ?? 100 + const fiveHourRemaining = account.rateLimits?.fiveHour?.remaining ?? (fiveHourLimit / 2) + const weeklyLimit = account.rateLimits?.weekly?.limit ?? 1000 + const weeklyRemaining = account.rateLimits?.weekly?.remaining ?? (weeklyLimit / 2) + const fiveHourPct = fiveHourLimit > 0 ? (fiveHourRemaining / fiveHourLimit) * 100 : 50 + const weeklyPct = weeklyLimit > 0 ? (weeklyRemaining / weeklyLimit) * 100 : 50 + const health = (fiveHourPct + weeklyPct) / 2 // Inverse: lower health = higher weight accountWeights[alias] = (100 - health) / 100 }) diff --git a/src/sticky-sessions.ts b/src/sticky-sessions.ts index 5499fe5..6b55900 100644 --- a/src/sticky-sessions.ts +++ b/src/sticky-sessions.ts @@ -2,12 +2,9 @@ import * as crypto from 'node:crypto' import * as fs from 'node:fs' import * as path from 'node:path' import { getStorePath, withWriteLock } from './store.js' +import type { StickySessionSettings as StickySessionPolicy } from './types.js' -export type StickySessionSettings = { - ttlMs: number - maxEntries: number - maxFileBytes: number -} +export type StickySessionSettings = StickySessionPolicy export type StickySessionEntry = { alias: string @@ -21,6 +18,24 @@ export type StickySessionsFile = { entries: Record } +export type StickySessionCleanupResult = { + before: number + after: number + removed: number + prunedAt: number +} + +export type StickySessionStatus = StickySessionCleanupResult & { + path: string + exists: boolean + entries: number + sizeBytes: number + updatedAt: number | null + ttlMs: number + maxEntries: number + maxFileBytes: number +} + const STICKY_SIDE_CAR_FILE = 'sticky-sessions.json' const STICKY_SIDE_CAR_VERSION = 1 @@ -50,6 +65,10 @@ function getStickySessionsPath(): string { return path.join(path.dirname(getStorePath()), STICKY_SIDE_CAR_FILE) } +export function getStickySessionsFilePath(): string { + return getStickySessionsPath() +} + function ensureStoreDir(): void { fs.mkdirSync(path.dirname(getStickySessionsPath()), { recursive: true, mode: 0o700 }) } @@ -333,3 +352,51 @@ export async function removeStickyAssignment( return pruned }) } + +export async function cleanupStickySessions(options: LoadStickySessionsOptions): Promise { + return withWriteLock(() => { + const loaded = readStickySessionsSnapshot(options.now) + const before = Object.keys(loaded.entries).length + const pruned = pruneStickySessions(loaded, options.now, options.settings) + writeStickySessionsToDisk(pruned) + + const after = Object.keys(pruned.entries).length + return { + before, + after, + removed: before - after, + prunedAt: options.now + } + }) +} + +export async function getStickySessionsStatus(options: LoadStickySessionsOptions): Promise { + return withWriteLock(() => { + const sidecarPath = getStickySessionsPath() + const beforeSnapshot = readStickySessionsSnapshot(options.now) + const before = Object.keys(beforeSnapshot.entries).length + const loaded = readStickySessionsFromDisk(options.now, options.settings) + const after = Object.keys(loaded.entries).length + + if (fs.existsSync(sidecarPath)) { + writeStickySessionsToDisk(loaded) + } + + const exists = fs.existsSync(sidecarPath) + const sizeBytes = exists ? fs.statSync(sidecarPath).size : 0 + return { + path: sidecarPath, + exists, + entries: after, + sizeBytes, + updatedAt: exists ? loaded.updatedAt : null, + ttlMs: options.settings.ttlMs, + maxEntries: options.settings.maxEntries, + maxFileBytes: options.settings.maxFileBytes, + before, + after, + removed: before - after, + prunedAt: options.now + } + }) +} diff --git a/src/store.ts b/src/store.ts index 3c3a623..099789a 100644 --- a/src/store.ts +++ b/src/store.ts @@ -217,7 +217,8 @@ function validateStore(data: any): AccountStore | null { forcedBy: data.forcedBy ?? null, // Phase F: Preserve rotation strategy and settings rotationStrategy: data.rotationStrategy ?? 'round-robin', - settings: data.settings ?? undefined + settings: data.settings ?? undefined, + stickySessions: data.stickySessions ?? undefined } } diff --git a/src/types.ts b/src/types.ts index d79ce4e..ff79b65 100644 --- a/src/types.ts +++ b/src/types.ts @@ -126,6 +126,8 @@ export interface AccountStore { rotationStrategy?: 'round-robin' | 'least-used' | 'random' | 'weighted-round-robin' // Phase F: Settings settings?: RotationSettings + // Phase G: Sticky-session admin config persisted separately from /api/settings + stickySessions?: PersistedStickySessionSettings } // OpenAI model info @@ -219,6 +221,14 @@ export type StickyIdentitySource = | 'body:metadata.conversation_id' | 'body:prompt_cache_key' +export const ALL_STICKY_IDENTITY_SOURCES: StickyIdentitySource[] = [ + 'header:session_id', + 'header:conversation_id', + 'body:metadata.session_id', + 'body:metadata.conversation_id', + 'body:prompt_cache_key' +] + // Phase G: Sticky-session policy shape (kept separate from the enablement flag) export interface StickySessionSettings { identitySources: StickyIdentitySource[] @@ -228,6 +238,15 @@ export interface StickySessionSettings { maxFileBytes: number } +export interface PersistedStickySessionSettings extends StickySessionSettings { + updatedAt?: number + updatedBy?: string +} + +export interface StickySessionAdminConfig extends PersistedStickySessionSettings { + enabled: boolean +} + // Phase G: Canonical sticky identity resolved from a request export interface ResolvedStickyIdentity { source: StickyIdentitySource diff --git a/src/web.ts b/src/web.ts index 3346ff4..3fb08e3 100644 --- a/src/web.ts +++ b/src/web.ts @@ -12,9 +12,18 @@ import { getStoreStatus, listAccounts, loadStore, removeAccount, updateAccount } import { getRefreshQueueState, startRefreshQueue, stopRefreshQueue } from './refresh-queue.js' import { getLogPath, logError, logInfo, readLogTail } from './logger.js' import { getForceState, activateForce, clearForce, isForceActive, getRemainingForceTimeMs, formatForceDuration } from './force-mode.js' -import { getSettings, getRuntimeSettings, updateSettings, isFeatureEnabled } from './settings.js' +import { + getSettings, + getRuntimeSettings, + updateSettings, + isFeatureEnabled, + getStickySessionConfig, + getStickySessionRuntimeSettings, + updateStickySessionConfig +} from './settings.js' import { Errors } from './errors.js' import type { AccountCredentials, RateLimitWindow, LimitsConfidence, RotationSettings, WeightPreset } from './types.js' +import { cleanupStickySessions, getStickySessionsStatus } from './sticky-sessions.js' const DEFAULT_HOST = '127.0.0.1' const DEFAULT_PORT = 3434 @@ -30,6 +39,23 @@ const AUTO_LOGIN_CREDENTIALS_ENV = 'OPENCODE_MULTI_AUTH_AUTO_LOGIN_CREDENTIALS_F const AUTO_LOGIN_PYTHON_ENV = 'OPENCODE_MULTI_AUTH_AUTO_LOGIN_PYTHON' const DEFAULT_AUTO_LOGIN_CREDENTIALS_FILE = path.join(os.homedir(), '.config', 'opencode-multi-auth', 'credentials.json') const DEFAULT_AUTO_LOGIN_VENV_PYTHON = path.join(os.homedir(), '.config', 'opencode-multi-auth', '.venv', 'bin', 'python') +const WEB_DIST_DIR_ENV = 'OPENCODE_MULTI_AUTH_WEB_DIST_DIR' + +const STATIC_CONTENT_TYPES: Record = { + '.css': 'text/css; charset=utf-8', + '.gif': 'image/gif', + '.html': 'text/html; charset=utf-8', + '.ico': 'image/x-icon', + '.jpeg': 'image/jpeg', + '.jpg': 'image/jpeg', + '.js': 'application/javascript; charset=utf-8', + '.json': 'application/json; charset=utf-8', + '.map': 'application/json; charset=utf-8', + '.png': 'image/png', + '.svg': 'image/svg+xml', + '.txt': 'text/plain; charset=utf-8', + '.webp': 'image/webp' +} type AutoLoginMode = 'manual' | 'auto' @@ -89,2239 +115,93 @@ export function isLocalhostHost(host: string): boolean { const execAsync = promisify(exec) -let lastSyncAt = 0 -let lastSyncError: string | null = null -let lastSyncAlias: string | null = null -let syncTimer: NodeJS.Timeout | null = null -let pendingLogin: PendingLoginState | null = null -let lastLoginError: string | null = null -let antigravityQuotaState: AntigravityQuotaState = { status: 'idle', scope: 'active' } -let antigravityQuotaInFlight: Promise | null = null -let autoLoginChild: ReturnType | null = null - -const HTML = ` - - - - - Codex Token Dashboard - - - -
-
-
-
Codex Special Functions
-

Codex Token Dashboard

-
Add a new account from login + password, keep it in the local auto-login config, and watch the whole flow live.
-
- -
-
-
-
-
-
-
-
- - - - -
-
- - -
-
- - -
-
-
-
-
-
-
- - - - -
-
-
- - -
-
-
-
Force Mode
-
Pin rotation to a specific account for 24 hours
-
-
-
- - ? - -
-
- - - ? -
-
-
-
-
-
- - - -
-
-
-
Logs
-
-
- -
-

-      
-
- -
- - -` +let lastSyncAt = 0 +let lastSyncError: string | null = null +let lastSyncAlias: string | null = null +let syncTimer: NodeJS.Timeout | null = null +let pendingLogin: PendingLoginState | null = null +let lastLoginError: string | null = null +let antigravityQuotaState: AntigravityQuotaState = { status: 'idle', scope: 'active' } +let antigravityQuotaInFlight: Promise | null = null +let autoLoginChild: ReturnType | null = null function sendJson(res: http.ServerResponse, status: number, payload: unknown): void { const data = JSON.stringify(payload) @@ -3297,12 +1177,6 @@ export function startWebConsole(options?: { port?: number; host?: string }): htt try { - if (req.method === 'GET' && path === '/') { - res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) - res.end(HTML) - return - } - if (req.method === 'GET' && path === '/api/state') { runSync() const store = loadStore() @@ -3898,7 +1772,82 @@ export function startWebConsole(options?: { port?: number; host?: string }): htt } return } - + + const isStickySessionsEnabled = (): boolean => isFeatureEnabled('stickySessionsEnabled') + const sendStickyFeatureDisabled = (): void => { + sendJson(res, 403, { + error: 'Sticky sessions feature is disabled', + code: 'FEATURE_DISABLED', + feature: 'sticky-sessions' + }) + } + + if (req.method === 'GET' && path === '/api/sticky-sessions/config') { + if (!isStickySessionsEnabled()) { + sendStickyFeatureDisabled() + return + } + + const stickyConfig = getStickySessionConfig() + sendJson(res, 200, stickyConfig.config) + return + } + + if (req.method === 'PUT' && path === '/api/sticky-sessions/config') { + const body = await readJsonBody(req) + const actor = body.actor || 'dashboard' + const currentlyEnabled = isStickySessionsEnabled() + + if (!currentlyEnabled && body.enabled !== false) { + sendStickyFeatureDisabled() + return + } + + const result = updateStickySessionConfig({ + enabled: body.enabled, + identitySources: body.identitySources, + allowPromptCacheKey: body.allowPromptCacheKey, + ttlMs: body.ttlMs, + maxEntries: body.maxEntries, + maxFileBytes: body.maxFileBytes + }, actor) + + if (result.success) { + sendJson(res, 200, result.config) + } else { + sendJson(res, 400, { + error: 'Validation failed', + code: 'VALIDATION_ERROR', + details: result.errors + }) + } + return + } + + if (req.method === 'GET' && path === '/api/sticky-sessions/status') { + if (!isStickySessionsEnabled()) { + sendStickyFeatureDisabled() + return + } + + const stickySettings = getStickySessionRuntimeSettings() + const status = await getStickySessionsStatus({ now: Date.now(), settings: stickySettings }) + sendJson(res, 200, { ok: true, ...status }) + return + } + + if (req.method === 'POST' && path === '/api/sticky-sessions/cleanup') { + if (!isStickySessionsEnabled()) { + sendStickyFeatureDisabled() + return + } + + const stickySettings = getStickySessionRuntimeSettings() + const cleanup = await cleanupStickySessions({ now: Date.now(), settings: stickySettings }) + sendJson(res, 200, { ok: true, ...cleanup }) + return + } + // POST /api/settings/reset - Reset to defaults if (req.method === 'POST' && path === '/api/settings/reset') { const { resetSettings } = await import('./settings.js') @@ -3947,6 +1896,18 @@ export function startWebConsole(options?: { port?: number; host?: string }): htt return } + if (isApiRequestPath(path)) { + res.writeHead(404, { 'Content-Type': 'application/json' }) + res.end(JSON.stringify({ error: 'Not found' })) + return + } + + if (req.method === 'GET' || req.method === 'HEAD') { + if (await serveSpaRequest(res, path)) { + return + } + } + res.writeHead(404, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ error: 'Not found' })) } catch (err) { diff --git a/tests/integration/dashboard-api-contract.test.ts b/tests/integration/dashboard-api-contract.test.ts new file mode 100644 index 0000000..11d4ee4 --- /dev/null +++ b/tests/integration/dashboard-api-contract.test.ts @@ -0,0 +1,579 @@ +import * as fs from 'node:fs' +import * as net from 'node:net' +import * as os from 'node:os' +import * as path from 'node:path' +import type * as http from 'node:http' + +const SANDBOX_ROOT = path.join(os.tmpdir(), 'oma-dashboard-contract-sandbox') +const STORE_FILE = path.join(SANDBOX_ROOT, 'accounts.json') +const AUTH_FILE = path.join(SANDBOX_ROOT, 'auth.json') +const originalEnv = process.env + +let startWebConsole: typeof import('../../src/web.js').startWebConsole +let getCodexAuthPath: typeof import('../../src/codex-auth.js').getCodexAuthPath + +type JsonRecord = Record + +async function getFreePort(): Promise { + return new Promise((resolve, reject) => { + const server = net.createServer() + server.listen(0, '127.0.0.1', () => { + const address = server.address() + if (!address || typeof address === 'string') { + reject(new Error('Failed to resolve free port')) + return + } + const port = address.port + server.close((err) => { + if (err) { + reject(err) + return + } + resolve(port) + }) + }) + server.on('error', reject) + }) +} + +async function closeServer(server: http.Server): Promise { + await new Promise((resolve, reject) => { + server.close((err) => { + if (err) { + reject(err) + return + } + resolve() + }) + }) +} + +function seedSandbox(): void { + fs.rmSync(SANDBOX_ROOT, { recursive: true, force: true }) + fs.mkdirSync(SANDBOX_ROOT, { recursive: true }) + fs.writeFileSync(AUTH_FILE, JSON.stringify({ OPENAI_API_KEY: null, tokens: {} }, null, 2)) + fs.writeFileSync( + STORE_FILE, + JSON.stringify( + { + version: 2, + activeAlias: 'alpha', + rotationIndex: 0, + lastRotation: 1_700_000_000_000, + rotationStrategy: 'round-robin', + settings: { + rotationStrategy: 'round-robin', + criticalThreshold: 10, + lowThreshold: 30, + accountWeights: {}, + featureFlags: { + antigravityEnabled: false, + stickySessionsEnabled: false + } + }, + accounts: { + alpha: { + alias: 'alpha', + accessToken: 'token-alpha', + refreshToken: 'refresh-alpha', + expiresAt: Date.now() + 60_000, + email: 'alpha@example.com', + usageCount: 3, + enabled: true, + tags: ['core'], + notes: 'primary account', + source: 'opencode', + rateLimits: { + fiveHour: { limit: 100, remaining: 80, resetAt: Date.now() + 60_000, updatedAt: Date.now() }, + weekly: { limit: 1000, remaining: 700, resetAt: Date.now() + 120_000, updatedAt: Date.now() } + }, + limitsConfidence: 'fresh' + }, + beta: { + alias: 'beta', + accessToken: 'token-beta', + refreshToken: 'refresh-beta', + expiresAt: Date.now() + 120_000, + email: 'beta@example.com', + usageCount: 7, + enabled: true, + tags: ['backup'], + notes: 'secondary account', + source: 'codex', + rateLimits: { + fiveHour: { limit: 100, remaining: 50, resetAt: Date.now() + 60_000, updatedAt: Date.now() }, + weekly: { limit: 1000, remaining: 450, resetAt: Date.now() + 120_000, updatedAt: Date.now() } + }, + limitsConfidence: 'stale' + } + } + }, + null, + 2 + ) + ) +} + +function readStore(): JsonRecord { + return JSON.parse(fs.readFileSync(STORE_FILE, 'utf8')) as JsonRecord +} + +async function startServer(): Promise<{ server: http.Server; port: number }> { + const port = await getFreePort() + const server = startWebConsole({ host: '127.0.0.1', port }) + await new Promise((resolve) => server.once('listening', () => resolve())) + return { server, port } +} + +async function requestJson(port: number, pathname: string, init?: RequestInit): Promise<{ status: number; body: JsonRecord }> { + const response = await fetch(`http://127.0.0.1:${port}${pathname}`, { + headers: { 'Content-Type': 'application/json', ...(init?.headers ?? {}) }, + ...init + }) + return { + status: response.status, + body: (await response.json()) as JsonRecord + } +} + +beforeAll(async () => { + seedSandbox() + process.env = { + ...originalEnv, + OPENCODE_MULTI_AUTH_STORE_DIR: SANDBOX_ROOT, + OPENCODE_MULTI_AUTH_STORE_FILE: STORE_FILE, + OPENCODE_MULTI_AUTH_CODEX_AUTH_FILE: AUTH_FILE + } + + ;({ startWebConsole } = await import('../../src/web.js')) + ;({ getCodexAuthPath } = await import('../../src/codex-auth.js')) +}) + +beforeEach(() => { + seedSandbox() +}) + +afterEach(() => { + fs.unwatchFile(getCodexAuthPath()) +}) + +afterAll(() => { + fs.unwatchFile(getCodexAuthPath()) + process.env = originalEnv + fs.rmSync(SANDBOX_ROOT, { recursive: true, force: true }) +}) + +describe('dashboard API contract parity', () => { + it('preserves state and logs response shapes without leaking secrets', async () => { + const { server, port } = await startServer() + + try { + const state = await requestJson(port, '/api/state') + expect(state.status).toBe(200) + expect(state.body.authPath).toBe(AUTH_FILE) + expect(state.body.deviceAlias).toBeNull() + expect(state.body.rotationAlias).toBe('alpha') + expect(state.body.authSummary).toEqual({ + hasAccessToken: false, + hasIdToken: false, + hasRefreshToken: false + }) + expect(state.body.storeStatus).toEqual({ locked: false, encrypted: false, error: null }) + expect(state.body.login).toBeNull() + expect(state.body.lastSyncAt).toEqual(expect.any(Number)) + expect(state.body.lastSyncError).toBe('Missing access_token/refresh_token in auth.json') + expect(state.body.lastSyncAlias).toBeNull() + expect(state.body.antigravity).toEqual({ + accounts: [], + path: expect.any(String), + quota: { + status: 'disabled', + scope: 'active' + } + }) + expect(state.body.queue).toBeNull() + expect(state.body.recommendedAlias).toBe('alpha') + expect(state.body.logPath).toEqual(expect.any(String)) + expect(state.body.autoLogin).toEqual( + expect.objectContaining({ + configured: false, + accounts: [], + error: 'credentials.json not found' + }) + ) + expect(state.body.rotationStrategy).toBe('round-robin') + expect(state.body.force).toEqual({ + active: false, + alias: null, + forcedUntil: null, + forcedBy: null, + remainingMs: 0, + remainingTime: '0m' + }) + expect(state.body.featureFlags).toEqual({ antigravityEnabled: false, stickySessionsEnabled: false }) + expect(state.body.accounts).toHaveLength(2) + expect(state.body.accounts[0]).toEqual( + expect.objectContaining({ + alias: 'alpha', + email: 'alpha@example.com', + enabled: true, + usageCount: 3, + tags: ['core'], + notes: 'primary account' + }) + ) + expect(state.body.accounts[0]).not.toHaveProperty('accessToken') + expect(state.body.accounts[0]).not.toHaveProperty('refreshToken') + expect(state.body.accounts[0]).not.toHaveProperty('idToken') + + const logs = await requestJson(port, '/api/logs?limit=5') + expect(logs.status).toBe(200) + expect(typeof logs.body.path).toBe('string') + expect(Array.isArray(logs.body.lines)).toBe(true) + } finally { + await closeServer(server) + } + }) + + it('preserves account and login action validation and mutation payloads', async () => { + const { server, port } = await startServer() + + try { + const accounts = await requestJson(port, '/api/accounts') + expect(accounts.status).toBe(200) + expect(accounts.body).toEqual({ + accounts: [ + expect.objectContaining({ + alias: 'alpha', + email: 'alpha@example.com', + enabled: true, + usageCount: 3, + tags: ['core'], + notes: 'primary account' + }), + expect.objectContaining({ + alias: 'beta', + email: 'beta@example.com', + enabled: true, + usageCount: 7, + tags: ['backup'], + notes: 'secondary account' + }) + ] + }) + + const disableAlpha = await requestJson(port, '/api/accounts/alpha/enabled', { + method: 'PUT', + body: JSON.stringify({ enabled: false }) + }) + expect(disableAlpha.status).toBe(200) + expect(disableAlpha.body).toEqual( + expect.objectContaining({ + ok: true, + alias: 'alpha', + enabled: false, + disabledAt: expect.any(Number), + disabledBy: 'dashboard' + }) + ) + + const disableAlphaAgain = await requestJson(port, '/api/accounts/alpha/enabled', { + method: 'PUT', + body: JSON.stringify({ enabled: false }) + }) + expect(disableAlphaAgain.status).toBe(409) + expect(disableAlphaAgain.body).toEqual({ + error: 'Account is already disabled', + code: 'ALREADY_IN_STATE' + }) + + const disableLastEnabled = await requestJson(port, '/api/accounts/beta/enabled', { + method: 'PUT', + body: JSON.stringify({ enabled: false }) + }) + expect(disableLastEnabled.status).toBe(409) + expect(disableLastEnabled.body).toEqual({ + error: 'Cannot disable the last enabled account', + code: 'LAST_ACCOUNT' + }) + + const missingAuth = await requestJson(port, '/api/auth/start', { + method: 'POST', + body: JSON.stringify({}) + }) + expect(missingAuth).toEqual({ status: 400, body: { error: 'Missing alias' } }) + + const missingAutoLoginSelector = await requestJson(port, '/api/auto-login/start', { + method: 'POST', + body: JSON.stringify({}) + }) + expect(missingAutoLoginSelector).toEqual({ status: 400, body: { error: 'Missing selector' } }) + + const missingAutoLoginEmail = await requestJson(port, '/api/auto-login/add', { + method: 'POST', + body: JSON.stringify({ password: 'secret' }) + }) + expect(missingAutoLoginEmail).toEqual({ status: 400, body: { error: 'Missing login/email' } }) + + const missingAutoLoginPassword = await requestJson(port, '/api/auto-login/add', { + method: 'POST', + body: JSON.stringify({ email: 'bot@example.com', password: ' ' }) + }) + expect(missingAutoLoginPassword).toEqual({ status: 400, body: { error: 'Missing password' } }) + + const missingSwitchAlias = await requestJson(port, '/api/switch', { + method: 'POST', + body: JSON.stringify({}) + }) + expect(missingSwitchAlias).toEqual({ status: 400, body: { error: 'Missing alias' } }) + + const missingReauthAlias = await requestJson(port, '/api/accounts/ghost/reauth', { + method: 'POST', + body: JSON.stringify({ actor: 'test-suite' }) + }) + expect(missingReauthAlias.status).toBe(404) + expect(missingReauthAlias.body).toEqual({ error: 'Unknown alias', code: 'ACCOUNT_NOT_FOUND' }) + + const updateMeta = await requestJson(port, '/api/account/meta', { + method: 'POST', + body: JSON.stringify({ alias: 'beta', tags: ' Ops,ops, Team ', notes: ' updated note ' }) + }) + expect(updateMeta).toEqual({ status: 200, body: { ok: true } }) + expect(readStore().accounts.beta.tags).toEqual(['ops', 'team']) + expect(readStore().accounts.beta.notes).toBe('updated note') + + const removeBeta = await requestJson(port, '/api/remove', { + method: 'POST', + body: JSON.stringify({ alias: 'beta' }) + }) + expect(removeBeta).toEqual({ status: 200, body: { ok: true } }) + expect(readStore().accounts.beta).toBeUndefined() + } finally { + await closeServer(server) + } + }) + + it('preserves settings, force-mode, sync, refresh, and antigravity contracts', async () => { + const { server, port } = await startServer() + + try { + const sync = await requestJson(port, '/api/sync', { + method: 'POST', + body: JSON.stringify({}) + }) + expect(sync).toEqual({ status: 200, body: { ok: true } }) + + const tokenRefreshUnknown = await requestJson(port, '/api/token/refresh', { + method: 'POST', + body: JSON.stringify({ alias: 'ghost' }) + }) + expect(tokenRefreshUnknown).toEqual({ status: 400, body: { error: 'Unknown alias' } }) + + const limitsRefreshUnknown = await requestJson(port, '/api/limits/refresh', { + method: 'POST', + body: JSON.stringify({ alias: 'ghost' }) + }) + expect(limitsRefreshUnknown).toEqual({ status: 400, body: { error: 'Unknown alias' } }) + + const stopRefresh = await requestJson(port, '/api/limits/stop', { + method: 'POST', + body: JSON.stringify({}) + }) + expect(stopRefresh).toEqual({ status: 200, body: { ok: true } }) + + const forceState = await requestJson(port, '/api/force') + expect(forceState.status).toBe(200) + expect(forceState.body).toEqual( + expect.objectContaining({ + active: false, + alias: null, + forcedAt: null, + forcedUntil: null, + forcedBy: null, + remainingMs: 0, + remainingTime: '0m', + previousRotationStrategy: null + }) + ) + + const missingForceAlias = await requestJson(port, '/api/force', { + method: 'POST', + body: JSON.stringify({}) + }) + expect(missingForceAlias).toEqual({ + status: 400, + body: { error: 'Missing alias', code: 'MISSING_ALIAS' } + }) + + const activateForce = await requestJson(port, '/api/force', { + method: 'POST', + body: JSON.stringify({ alias: 'alpha', actor: 'test-suite' }) + }) + expect(activateForce.status).toBe(200) + expect(activateForce.body).toEqual( + expect.objectContaining({ + ok: true, + alias: 'alpha', + forcedUntil: expect.any(Number), + remainingMs: expect.any(Number), + remainingTime: expect.any(String), + previousRotationStrategy: 'round-robin' + }) + ) + + const forceAfterActivation = await requestJson(port, '/api/force') + expect(forceAfterActivation.status).toBe(200) + expect(forceAfterActivation.body).toEqual( + expect.objectContaining({ + active: true, + alias: 'alpha', + forcedBy: 'test-suite', + previousRotationStrategy: 'round-robin' + }) + ) + + const clearForce = await requestJson(port, '/api/force/clear', { + method: 'POST', + body: JSON.stringify({}) + }) + expect(clearForce).toEqual({ status: 200, body: { ok: true, restoredStrategy: 'round-robin' } }) + + const settings = await requestJson(port, '/api/settings') + expect(settings.status).toBe(200) + expect(settings.body).toEqual( + expect.objectContaining({ + settings: expect.objectContaining({ + rotationStrategy: 'round-robin', + criticalThreshold: 10, + lowThreshold: 30, + accountWeights: {}, + featureFlags: expect.objectContaining({ antigravityEnabled: false, stickySessionsEnabled: false }) + }), + source: 'persisted', + canReset: true + }) + ) + + const updateSettings = await requestJson(port, '/api/settings', { + method: 'PUT', + body: JSON.stringify({ + actor: 'test-suite', + rotationStrategy: 'least-used', + criticalThreshold: 15, + lowThreshold: 45, + featureFlags: { antigravityEnabled: true } + }) + }) + expect(updateSettings.status).toBe(200) + expect(updateSettings.body).toEqual( + expect.objectContaining({ + ok: true, + settings: expect.objectContaining({ + rotationStrategy: 'least-used', + criticalThreshold: 15, + lowThreshold: 45, + featureFlags: expect.objectContaining({ antigravityEnabled: true, stickySessionsEnabled: false }), + updatedBy: 'test-suite' + }) + }) + ) + + const invalidFeatureFlags = await requestJson(port, '/api/settings/feature-flags', { + method: 'PUT', + body: JSON.stringify({}) + }) + expect(invalidFeatureFlags).toEqual({ + status: 400, + body: { error: 'Invalid feature flags', code: 'INVALID_FEATURE_FLAGS' } + }) + + const featureFlags = await requestJson(port, '/api/settings/feature-flags') + expect(featureFlags).toEqual({ + status: 200, + body: { + featureFlags: { + antigravityEnabled: true, + stickySessionsEnabled: false + } + } + }) + + const resetSettings = await requestJson(port, '/api/settings/reset', { + method: 'POST', + body: JSON.stringify({ actor: 'test-suite' }) + }) + expect(resetSettings.status).toBe(200) + expect(resetSettings.body).toEqual( + expect.objectContaining({ + ok: true, + settings: expect.objectContaining({ + rotationStrategy: 'round-robin', + criticalThreshold: 10, + lowThreshold: 30, + accountWeights: {}, + featureFlags: expect.objectContaining({ antigravityEnabled: false, stickySessionsEnabled: false }) + }) + }) + ) + + const applyPreset = await requestJson(port, '/api/settings/preset', { + method: 'POST', + body: JSON.stringify({ preset: 'balanced', actor: 'test-suite' }) + }) + expect(applyPreset.status).toBe(200) + expect(applyPreset.body).toEqual( + expect.objectContaining({ + ok: true, + preset: 'balanced', + settings: expect.objectContaining({ + rotationStrategy: 'weighted-round-robin', + criticalThreshold: 10, + lowThreshold: 30, + accountWeights: expect.objectContaining({ alpha: 0.5, beta: 0.5 }) + }) + }) + ) + + const invalidPreset = await requestJson(port, '/api/settings/preset', { + method: 'POST', + body: JSON.stringify({ preset: 'nope' }) + }) + expect(invalidPreset).toEqual({ + status: 400, + body: { + error: 'Invalid preset', + code: 'INVALID_PRESET', + validPresets: ['balanced', 'conservative', 'aggressive', 'custom'] + } + }) + + const antigravityRefresh = await requestJson(port, '/api/antigravity/refresh', { + method: 'POST', + body: JSON.stringify({}) + }) + expect(antigravityRefresh).toEqual({ + status: 403, + body: { + error: 'Antigravity feature is disabled', + code: 'FEATURE_DISABLED', + feature: 'antigravity' + } + }) + + const antigravityRefreshAll = await requestJson(port, '/api/antigravity/refresh-all', { + method: 'POST', + body: JSON.stringify({}) + }) + expect(antigravityRefreshAll).toEqual({ + status: 403, + body: { + error: 'Antigravity feature is disabled', + code: 'FEATURE_DISABLED', + feature: 'antigravity' + } + }) + } finally { + await closeServer(server) + } + }) +}) diff --git a/tests/integration/sticky-session-admin-api.test.ts b/tests/integration/sticky-session-admin-api.test.ts new file mode 100644 index 0000000..42aba1d --- /dev/null +++ b/tests/integration/sticky-session-admin-api.test.ts @@ -0,0 +1,330 @@ +import * as fs from 'node:fs' +import * as net from 'node:net' +import * as os from 'node:os' +import * as path from 'node:path' +import type * as http from 'node:http' + +const SANDBOX_ROOT = path.join(os.tmpdir(), 'oma-sticky-admin-sandbox') +const STORE_FILE = path.join(SANDBOX_ROOT, 'accounts.json') +const AUTH_FILE = path.join(SANDBOX_ROOT, 'auth.json') +const STICKY_FILE = path.join(SANDBOX_ROOT, 'sticky-sessions.json') +const originalEnv = process.env + +let startWebConsole: typeof import('../../src/web.js').startWebConsole +let getCodexAuthPath: typeof import('../../src/codex-auth.js').getCodexAuthPath +let hashStickyIdentity: typeof import('../../src/sticky-sessions.js').hashStickyIdentity + +type JsonRecord = Record + +async function getFreePort(): Promise { + return new Promise((resolve, reject) => { + const server = net.createServer() + server.listen(0, '127.0.0.1', () => { + const address = server.address() + if (!address || typeof address === 'string') { + reject(new Error('Failed to resolve free port')) + return + } + const port = address.port + server.close((err) => { + if (err) { + reject(err) + return + } + resolve(port) + }) + }) + server.on('error', reject) + }) +} + +async function closeServer(server: http.Server): Promise { + await new Promise((resolve, reject) => { + server.close((err) => { + if (err) { + reject(err) + return + } + resolve() + }) + }) +} + +function seedSandbox(options?: { stickyEnabled?: boolean }): void { + fs.rmSync(SANDBOX_ROOT, { recursive: true, force: true }) + fs.mkdirSync(SANDBOX_ROOT, { recursive: true }) + fs.writeFileSync(AUTH_FILE, JSON.stringify({ OPENAI_API_KEY: null, tokens: {} }, null, 2)) + fs.writeFileSync( + STORE_FILE, + JSON.stringify( + { + version: 2, + activeAlias: 'alpha', + rotationIndex: 0, + lastRotation: 1_700_000_000_000, + rotationStrategy: 'round-robin', + settings: { + rotationStrategy: 'round-robin', + criticalThreshold: 10, + lowThreshold: 30, + accountWeights: {}, + featureFlags: { + antigravityEnabled: false, + stickySessionsEnabled: options?.stickyEnabled ?? true + } + }, + accounts: { + alpha: { + alias: 'alpha', + accessToken: 'token-alpha', + refreshToken: 'refresh-alpha', + expiresAt: Date.now() + 60_000, + email: 'alpha@example.com', + usageCount: 3, + enabled: true + } + } + }, + null, + 2 + ) + ) +} + +function seedStickySidecar(now: number): void { + fs.writeFileSync( + STICKY_FILE, + JSON.stringify( + { + version: 1, + updatedAt: now, + entries: { + [hashStickyIdentity('stale-session')]: { + alias: 'alpha', + createdAt: now - 120_000, + lastUsedAt: now - 120_000 + }, + [hashStickyIdentity('fresh-session')]: { + alias: 'alpha', + createdAt: now - 1000, + lastUsedAt: now - 1000 + } + } + }, + null, + 2 + ) + ) +} + +function readStore(): JsonRecord { + return JSON.parse(fs.readFileSync(STORE_FILE, 'utf8')) as JsonRecord +} + +async function startServer(): Promise<{ server: http.Server; port: number }> { + const port = await getFreePort() + const server = startWebConsole({ host: '127.0.0.1', port }) + await new Promise((resolve) => server.once('listening', () => resolve())) + return { server, port } +} + +async function requestJson(port: number, pathname: string, init?: RequestInit): Promise<{ status: number; body: JsonRecord }> { + const response = await fetch(`http://127.0.0.1:${port}${pathname}`, { + headers: { 'Content-Type': 'application/json', ...(init?.headers ?? {}) }, + ...init + }) + return { + status: response.status, + body: (await response.json()) as JsonRecord + } +} + +beforeAll(async () => { + seedSandbox() + process.env = { + ...originalEnv, + OPENCODE_MULTI_AUTH_STORE_DIR: SANDBOX_ROOT, + OPENCODE_MULTI_AUTH_STORE_FILE: STORE_FILE, + OPENCODE_MULTI_AUTH_CODEX_AUTH_FILE: AUTH_FILE + } + + ;({ startWebConsole } = await import('../../src/web.js')) + ;({ getCodexAuthPath } = await import('../../src/codex-auth.js')) + ;({ hashStickyIdentity } = await import('../../src/sticky-sessions.js')) +}) + +beforeEach(() => { + seedSandbox({ stickyEnabled: true }) +}) + +afterEach(() => { + fs.unwatchFile(getCodexAuthPath()) +}) + +afterAll(() => { + fs.unwatchFile(getCodexAuthPath()) + process.env = originalEnv + fs.rmSync(SANDBOX_ROOT, { recursive: true, force: true }) +}) + +describe('sticky-session admin API', () => { + it('exposes additive config, status, and synchronous cleanup endpoints without mutating /api/settings semantics', async () => { + const now = Date.now() + seedStickySidecar(now) + const { server, port } = await startServer() + + try { + const initialSettings = await requestJson(port, '/api/settings') + expect(initialSettings.status).toBe(200) + expect(initialSettings.body.settings.featureFlags).toEqual({ + antigravityEnabled: false, + stickySessionsEnabled: true + }) + expect(initialSettings.body.settings.stickySessions).toBeUndefined() + + const config = await requestJson(port, '/api/sticky-sessions/config') + expect(config.status).toBe(200) + expect(config.body).toEqual( + expect.objectContaining({ + enabled: true, + identitySources: [ + 'header:session_id', + 'header:conversation_id', + 'body:metadata.session_id', + 'body:metadata.conversation_id' + ], + allowPromptCacheKey: false, + ttlMs: 86_400_000, + maxEntries: 1000, + maxFileBytes: 1_048_576 + }) + ) + + const status = await requestJson(port, '/api/sticky-sessions/status') + expect(status.status).toBe(200) + expect(status.body).toEqual( + expect.objectContaining({ + ok: true, + entries: 2, + path: STICKY_FILE, + exists: true, + ttlMs: 86_400_000, + maxEntries: 1000, + maxFileBytes: 1_048_576, + sizeBytes: expect.any(Number), + updatedAt: expect.any(Number) + }) + ) + + const invalid = await requestJson(port, '/api/sticky-sessions/config', { + method: 'PUT', + body: JSON.stringify({ + enabled: true, + identitySources: ['header:session_id', 'body:prompt_cache_key'], + allowPromptCacheKey: false, + ttlMs: 0, + maxEntries: -1, + maxFileBytes: 0 + }) + }) + expect(invalid.status).toBe(400) + expect(invalid.body.code).toBe('VALIDATION_ERROR') + + const updateConfig = await requestJson(port, '/api/sticky-sessions/config', { + method: 'PUT', + body: JSON.stringify({ + enabled: true, + identitySources: ['header:conversation_id', 'body:prompt_cache_key'], + allowPromptCacheKey: true, + ttlMs: 60_000, + maxEntries: 50, + maxFileBytes: 4096, + actor: 'test-suite' + }) + }) + expect(updateConfig.status).toBe(200) + expect(updateConfig.body).toEqual( + expect.objectContaining({ + enabled: true, + identitySources: ['header:conversation_id', 'body:prompt_cache_key'], + allowPromptCacheKey: true, + ttlMs: 60_000, + maxEntries: 50, + maxFileBytes: 4096, + updatedAt: expect.any(Number), + updatedBy: 'test-suite' + }) + ) + + const cleanup = await requestJson(port, '/api/sticky-sessions/cleanup', { + method: 'POST', + body: JSON.stringify({}) + }) + expect(cleanup.status).toBe(200) + expect(cleanup.body).toEqual({ + ok: true, + before: 2, + after: 1, + removed: 1, + prunedAt: expect.any(Number) + }) + + const disable = await requestJson(port, '/api/sticky-sessions/config', { + method: 'PUT', + body: JSON.stringify({ + enabled: false, + identitySources: ['header:conversation_id'], + allowPromptCacheKey: false, + ttlMs: 60_000, + maxEntries: 50, + maxFileBytes: 4096, + actor: 'test-suite' + }) + }) + expect(disable.status).toBe(200) + expect(disable.body).toEqual( + expect.objectContaining({ + enabled: false, + identitySources: ['header:conversation_id'], + allowPromptCacheKey: false, + ttlMs: 60_000, + maxEntries: 50, + maxFileBytes: 4096, + updatedAt: expect.any(Number), + updatedBy: 'test-suite' + }) + ) + expect(readStore().settings.featureFlags.stickySessionsEnabled).toBe(false) + + const gatedConfig = await requestJson(port, '/api/sticky-sessions/config') + expect(gatedConfig).toEqual({ + status: 403, + body: { + error: 'Sticky sessions feature is disabled', + code: 'FEATURE_DISABLED', + feature: 'sticky-sessions' + } + }) + + const gatedStatus = await requestJson(port, '/api/sticky-sessions/status') + expect(gatedStatus).toEqual({ + status: 403, + body: { + error: 'Sticky sessions feature is disabled', + code: 'FEATURE_DISABLED', + feature: 'sticky-sessions' + } + }) + + const afterSettings = await requestJson(port, '/api/settings') + expect(afterSettings.status).toBe(200) + expect(afterSettings.body.settings.featureFlags).toEqual({ + antigravityEnabled: false, + stickySessionsEnabled: false + }) + expect(afterSettings.body.settings.stickySessions).toBeUndefined() + } finally { + await closeServer(server) + } + }) +}) diff --git a/tests/integration/web-server.test.ts b/tests/integration/web-server.test.ts index 01d5670..62ac1ed 100644 --- a/tests/integration/web-server.test.ts +++ b/tests/integration/web-server.test.ts @@ -8,6 +8,7 @@ import { once } from 'node:events' const SANDBOX_ROOT = path.join(os.tmpdir(), 'oma-web-integration-sandbox') const STORE_FILE = path.join(SANDBOX_ROOT, 'accounts.json') const AUTH_FILE = path.join(SANDBOX_ROOT, 'auth.json') +const WEB_DIST_DIR = path.resolve(process.cwd(), 'tests/fixtures/web-dist') const originalEnv = process.env let startWebConsole: typeof import('../../src/web.js').startWebConsole @@ -58,7 +59,8 @@ beforeAll(async () => { ...originalEnv, OPENCODE_MULTI_AUTH_STORE_DIR: SANDBOX_ROOT, OPENCODE_MULTI_AUTH_STORE_FILE: STORE_FILE, - OPENCODE_MULTI_AUTH_CODEX_AUTH_FILE: AUTH_FILE + OPENCODE_MULTI_AUTH_CODEX_AUTH_FILE: AUTH_FILE, + OPENCODE_MULTI_AUTH_WEB_DIST_DIR: WEB_DIST_DIR } ;({ startWebConsole } = await import('../../src/web.js')) @@ -113,4 +115,74 @@ describe('web server hardening', () => { fs.unwatchFile(getCodexAuthPath()) } }) + + it('serves static dist assets with SPA fallback and never lets /api routes hit the SPA', async () => { + const port = await getFreePort() + const server = startWebConsole({ host: '127.0.0.1', port }) + + try { + await once(server, 'listening') + + const homeResponse = await fetch(`http://127.0.0.1:${port}/`) + expect(homeResponse.status).toBe(200) + expect(homeResponse.headers.get('content-type')).toContain('text/html') + expect(await homeResponse.text()).toContain('fixture-spa-shell') + + const assetResponse = await fetch(`http://127.0.0.1:${port}/assets/app.js`) + expect(assetResponse.status).toBe(200) + expect(assetResponse.headers.get('content-type')).toContain('javascript') + expect(await assetResponse.text()).toContain('fixture-asset') + + const routeResponse = await fetch(`http://127.0.0.1:${port}/dashboard/accounts`) + expect(routeResponse.status).toBe(200) + expect(routeResponse.headers.get('content-type')).toContain('text/html') + expect(await routeResponse.text()).toContain('fixture-spa-shell') + + const apiResponse = await fetch(`http://127.0.0.1:${port}/api/state`) + expect(apiResponse.status).toBe(200) + expect(apiResponse.headers.get('content-type')).toContain('application/json') + expect((await apiResponse.json()) as { authPath?: string }).toEqual(expect.objectContaining({ authPath: AUTH_FILE })) + + const missingApiResponse = await fetch(`http://127.0.0.1:${port}/api/does-not-exist`) + expect(missingApiResponse.status).toBe(404) + expect(missingApiResponse.headers.get('content-type')).toContain('application/json') + expect((await missingApiResponse.json()) as { error?: string }).toEqual({ error: 'Not found' }) + + const missingAssetResponse = await fetch(`http://127.0.0.1:${port}/assets/missing.js`) + expect(missingAssetResponse.status).toBe(404) + } finally { + await closeServer(server) + fs.unwatchFile(getCodexAuthPath()) + } + }) + + it('returns 404 when SPA dist is missing and never falls back to inline dashboard HTML', async () => { + const port = await getFreePort() + const missingDistDir = path.join(SANDBOX_ROOT, 'missing-dist') + process.env.OPENCODE_MULTI_AUTH_WEB_DIST_DIR = missingDistDir + + const server = startWebConsole({ host: '127.0.0.1', port }) + + try { + await once(server, 'listening') + + const homeResponse = await fetch(`http://127.0.0.1:${port}/`) + expect(homeResponse.status).toBe(404) + expect(homeResponse.headers.get('content-type')).toContain('application/json') + expect((await homeResponse.json()) as { error?: string }).toEqual({ error: 'Not found' }) + + const routeResponse = await fetch(`http://127.0.0.1:${port}/dashboard/accounts`) + expect(routeResponse.status).toBe(404) + expect(routeResponse.headers.get('content-type')).toContain('application/json') + expect((await routeResponse.json()) as { error?: string }).toEqual({ error: 'Not found' }) + + const apiResponse = await fetch(`http://127.0.0.1:${port}/api/state`) + expect(apiResponse.status).toBe(200) + expect(apiResponse.headers.get('content-type')).toContain('application/json') + } finally { + await closeServer(server) + process.env.OPENCODE_MULTI_AUTH_WEB_DIST_DIR = WEB_DIST_DIR + fs.unwatchFile(getCodexAuthPath()) + } + }) }) diff --git a/tests/integration/weighted-presets-api.test.ts b/tests/integration/weighted-presets-api.test.ts new file mode 100644 index 0000000..8d15c9a --- /dev/null +++ b/tests/integration/weighted-presets-api.test.ts @@ -0,0 +1,493 @@ +import * as fs from 'node:fs' +import * as net from 'node:net' +import * as os from 'node:os' +import * as path from 'node:path' +import type * as http from 'node:http' + +const SANDBOX_ROOT = path.join(os.tmpdir(), 'oma-weighted-presets-sandbox') +const STORE_FILE = path.join(SANDBOX_ROOT, 'accounts.json') +const AUTH_FILE = path.join(SANDBOX_ROOT, 'auth.json') +const originalEnv = process.env + +let startWebConsole: typeof import('../../src/web.js').startWebConsole +let getCodexAuthPath: typeof import('../../src/codex-auth.js').getCodexAuthPath + +type JsonRecord = Record + +async function getFreePort(): Promise { + return new Promise((resolve, reject) => { + const server = net.createServer() + server.listen(0, '127.0.0.1', () => { + const address = server.address() + if (!address || typeof address === 'string') { + reject(new Error('Failed to resolve free port')) + return + } + const port = address.port + server.close((err) => { + if (err) { + reject(err) + return + } + resolve(port) + }) + }) + server.on('error', reject) + }) +} + +async function closeServer(server: http.Server): Promise { + await new Promise((resolve, reject) => { + server.close((err) => { + if (err) { + reject(err) + return + } + resolve() + }) + }) +} + +function seedSandbox(): void { + fs.rmSync(SANDBOX_ROOT, { recursive: true, force: true }) + fs.mkdirSync(SANDBOX_ROOT, { recursive: true }) + fs.writeFileSync(AUTH_FILE, JSON.stringify({ OPENAI_API_KEY: null, tokens: {} }, null, 2)) + fs.writeFileSync( + STORE_FILE, + JSON.stringify( + { + version: 2, + activeAlias: 'alpha', + rotationIndex: 0, + lastRotation: 1_700_000_000_000, + rotationStrategy: 'round-robin', + settings: { + rotationStrategy: 'round-robin', + criticalThreshold: 10, + lowThreshold: 30, + accountWeights: {}, + featureFlags: { + antigravityEnabled: false, + stickySessionsEnabled: false + } + }, + accounts: { + alpha: { + alias: 'alpha', + accessToken: 'token-alpha', + refreshToken: 'refresh-alpha', + expiresAt: Date.now() + 60_000, + email: 'alpha@example.com', + usageCount: 3, + enabled: true, + source: 'opencode', + rateLimits: { + fiveHour: { limit: 100, remaining: 80, resetAt: Date.now() + 60_000, updatedAt: Date.now() }, + weekly: { limit: 1000, remaining: 700, resetAt: Date.now() + 120_000, updatedAt: Date.now() } + }, + limitsConfidence: 'fresh' + }, + beta: { + alias: 'beta', + accessToken: 'token-beta', + refreshToken: 'refresh-beta', + expiresAt: Date.now() + 120_000, + email: 'beta@example.com', + usageCount: 7, + enabled: true, + source: 'codex', + rateLimits: { + fiveHour: { limit: 100, remaining: 40, resetAt: Date.now() + 60_000, updatedAt: Date.now() }, + weekly: { limit: 1000, remaining: 300, resetAt: Date.now() + 120_000, updatedAt: Date.now() } + }, + limitsConfidence: 'fresh' + }, + gamma: { + alias: 'gamma', + accessToken: 'token-gamma', + refreshToken: 'refresh-gamma', + expiresAt: Date.now() + 180_000, + email: 'gamma@example.com', + usageCount: 1, + enabled: false, + source: 'opencode', + rateLimits: { + fiveHour: { limit: 100, remaining: 100, resetAt: Date.now() + 60_000, updatedAt: Date.now() }, + weekly: { limit: 1000, remaining: 900, resetAt: Date.now() + 120_000, updatedAt: Date.now() } + }, + limitsConfidence: 'fresh' + } + } + }, + null, + 2 + ) + ) +} + +function readStore(): JsonRecord { + return JSON.parse(fs.readFileSync(STORE_FILE, 'utf8')) as JsonRecord +} + +async function startServer(): Promise<{ server: http.Server; port: number }> { + const port = await getFreePort() + const server = startWebConsole({ host: '127.0.0.1', port }) + await new Promise((resolve) => server.once('listening', () => resolve())) + return { server, port } +} + +async function requestJson(port: number, pathname: string, init?: RequestInit): Promise<{ status: number; body: JsonRecord }> { + const response = await fetch(`http://127.0.0.1:${port}${pathname}`, { + headers: { 'Content-Type': 'application/json', ...(init?.headers ?? {}) }, + ...init + }) + return { + status: response.status, + body: (await response.json()) as JsonRecord + } +} + +describe('Weighted Presets API', () => { + beforeAll(async () => { + seedSandbox() + process.env = { + ...originalEnv, + OPENCODE_MULTI_AUTH_STORE_DIR: SANDBOX_ROOT, + OPENCODE_MULTI_AUTH_STORE_FILE: STORE_FILE, + OPENCODE_MULTI_AUTH_CODEX_AUTH_FILE: AUTH_FILE + } + + ;({ startWebConsole } = await import('../../src/web.js')) + ;({ getCodexAuthPath } = await import('../../src/codex-auth.js')) + }) + + beforeEach(() => { + seedSandbox() + }) + + afterEach(() => { + fs.unwatchFile(getCodexAuthPath()) + }) + + afterAll(() => { + fs.unwatchFile(getCodexAuthPath()) + process.env = originalEnv + fs.rmSync(SANDBOX_ROOT, { recursive: true, force: true }) + }) + + describe('POST /api/settings/preset', () => { + test('applies balanced preset with equal weights and weighted-round-robin strategy', async () => { + const { server, port } = await startServer() + + try { + const { status, body } = await requestJson(port, '/api/settings/preset', { + method: 'POST', + body: JSON.stringify({ preset: 'balanced', actor: 'test-suite' }) + }) + + expect(status).toBe(200) + expect(body.ok).toBe(true) + expect(body.preset).toBe('balanced') + expect(body.settings.rotationStrategy).toBe('weighted-round-robin') + expect(body.settings.criticalThreshold).toBe(10) + expect(body.settings.lowThreshold).toBe(30) + expect(body.settings.accountWeights).toBeDefined() + + // Balanced preset: 3 accounts, each should get ~0.333 weight + const weights = body.settings.accountWeights as Record + expect(Object.keys(weights)).toHaveLength(3) + expect(weights.alpha).toBeCloseTo(1 / 3, 3) + expect(weights.beta).toBeCloseTo(1 / 3, 3) + expect(weights.gamma).toBeCloseTo(1 / 3, 3) + + // Verify weights sum to approximately 1 + const totalWeight = Object.values(weights).reduce((sum: number, w: number) => sum + w, 0) + expect(totalWeight).toBeCloseTo(1, 3) + } finally { + await closeServer(server) + } + }) + + test('applies conservative preset favoring healthy accounts', async () => { + const { server, port } = await startServer() + + try { + const { status, body } = await requestJson(port, '/api/settings/preset', { + method: 'POST', + body: JSON.stringify({ preset: 'conservative', actor: 'test-suite' }) + }) + + expect(status).toBe(200) + expect(body.ok).toBe(true) + expect(body.preset).toBe('conservative') + expect(body.settings.rotationStrategy).toBe('weighted-round-robin') + expect(body.settings.criticalThreshold).toBe(20) + expect(body.settings.lowThreshold).toBe(40) + expect(body.settings.accountWeights).toBeDefined() + + // Conservative preset: alpha has better limits (80/700) than beta (40/300) + // So alpha should get higher weight than beta + const weights = body.settings.accountWeights as Record + expect(weights.alpha).toBeGreaterThan(weights.beta) + + // Verify weights sum to approximately 1 + const totalWeight = Object.values(weights).reduce((sum: number, w: number) => sum + w, 0) + expect(totalWeight).toBeCloseTo(1, 3) + } finally { + await closeServer(server) + } + }) + + test('applies aggressive preset favoring heavily-used accounts', async () => { + const { server, port } = await startServer() + + try { + const { status, body } = await requestJson(port, '/api/settings/preset', { + method: 'POST', + body: JSON.stringify({ preset: 'aggressive', actor: 'test-suite' }) + }) + + expect(status).toBe(200) + expect(body.ok).toBe(true) + expect(body.preset).toBe('aggressive') + expect(body.settings.rotationStrategy).toBe('weighted-round-robin') + expect(body.settings.criticalThreshold).toBe(5) + expect(body.settings.lowThreshold).toBe(20) + expect(body.settings.accountWeights).toBeDefined() + + // Aggressive preset: beta has lower remaining limits (40/300) than alpha (80/700) + // So beta should get higher weight than alpha (inverse of health) + const weights = body.settings.accountWeights as Record + expect(weights.beta).toBeGreaterThan(weights.alpha) + + // Verify weights sum to approximately 1 + const totalWeight = Object.values(weights).reduce((sum: number, w: number) => sum + w, 0) + expect(totalWeight).toBeCloseTo(1, 3) + } finally { + await closeServer(server) + } + }) + + test('rejects invalid preset with 400 and validPresets list', async () => { + const { server, port } = await startServer() + + try { + const { status, body } = await requestJson(port, '/api/settings/preset', { + method: 'POST', + body: JSON.stringify({ preset: 'invalid-preset' }) + }) + + expect(status).toBe(400) + expect(body.error).toBe('Invalid preset') + expect(body.code).toBe('INVALID_PRESET') + expect(body.validPresets).toEqual(['balanced', 'conservative', 'aggressive', 'custom']) + } finally { + await closeServer(server) + } + }) + + test('persists preset settings to store', async () => { + const { server, port } = await startServer() + + try { + await requestJson(port, '/api/settings/preset', { + method: 'POST', + body: JSON.stringify({ preset: 'balanced', actor: 'test-suite' }) + }) + + const store = readStore() + expect(store.settings.rotationStrategy).toBe('weighted-round-robin') + expect(store.settings.accountWeights).toBeDefined() + expect(Object.keys(store.settings.accountWeights)).toHaveLength(3) + } finally { + await closeServer(server) + } + }) + }) + + describe('PUT /api/settings with accountWeights', () => { + test('updates per-account weights directly', async () => { + const { server, port } = await startServer() + + try { + const { status, body } = await requestJson(port, '/api/settings', { + method: 'PUT', + body: JSON.stringify({ + accountWeights: { + alpha: 0.5, + beta: 0.3, + gamma: 0.2 + }, + actor: 'test-suite' + }) + }) + + expect(status).toBe(200) + expect(body.ok).toBe(true) + expect(body.settings.accountWeights.alpha).toBe(0.5) + expect(body.settings.accountWeights.beta).toBe(0.3) + expect(body.settings.accountWeights.gamma).toBe(0.2) + } finally { + await closeServer(server) + } + }) + + test('rejects weights that do not sum to approximately 1', async () => { + const { server, port } = await startServer() + + try { + const { status, body } = await requestJson(port, '/api/settings', { + method: 'PUT', + body: JSON.stringify({ + accountWeights: { + alpha: 0.5, + beta: 0.2 + // Missing gamma, sum = 0.7 + }, + actor: 'test-suite' + }) + }) + + expect(status).toBe(400) + expect(body.error).toBe('Validation failed') + expect(body.code).toBe('VALIDATION_ERROR') + expect(body.details).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + field: 'accountWeights', + message: 'Total weights must sum to 1.0', + constraint: 'sum(weights) ≈ 1.0' + }) + ]) + ) + } finally { + await closeServer(server) + } + }) + + test('rejects individual weights outside (0, 1] range', async () => { + const { server, port } = await startServer() + + try { + const { status, body } = await requestJson(port, '/api/settings', { + method: 'PUT', + body: JSON.stringify({ + accountWeights: { + alpha: 1.5, + beta: -0.3, + gamma: -0.2 + }, + actor: 'test-suite' + }) + }) + + expect(status).toBe(400) + expect(body.error).toBe('Validation failed') + expect(body.code).toBe('VALIDATION_ERROR') + expect(body.details).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + field: 'accountWeights.alpha', + message: 'Weight for alpha must be between 0 and 1', + constraint: '0 < weight <= 1' + }), + expect.objectContaining({ + field: 'accountWeights.beta', + message: 'Weight for beta must be between 0 and 1', + constraint: '0 < weight <= 1' + }), + expect.objectContaining({ + field: 'accountWeights.gamma', + message: 'Weight for gamma must be between 0 and 1', + constraint: '0 < weight <= 1' + }) + ]) + ) + } finally { + await closeServer(server) + } + }) + + test('persists custom weights to store', async () => { + const { server, port } = await startServer() + + try { + await requestJson(port, '/api/settings', { + method: 'PUT', + body: JSON.stringify({ + accountWeights: { + alpha: 0.6, + beta: 0.3, + gamma: 0.1 + }, + actor: 'test-suite' + }) + }) + + const store = readStore() + expect(store.settings.accountWeights.alpha).toBe(0.6) + expect(store.settings.accountWeights.beta).toBe(0.3) + expect(store.settings.accountWeights.gamma).toBe(0.1) + } finally { + await closeServer(server) + } + }) + }) + + describe('GET /api/settings reflects weighted state', () => { + test('returns preset field when weighted-round-robin matches a preset', async () => { + const { server, port } = await startServer() + + try { + // Apply balanced preset first + await requestJson(port, '/api/settings/preset', { + method: 'POST', + body: JSON.stringify({ preset: 'balanced', actor: 'test-suite' }) + }) + + const { status, body } = await requestJson(port, '/api/settings') + + expect(status).toBe(200) + expect(body.settings.rotationStrategy).toBe('weighted-round-robin') + expect(body.preset).toBe('balanced') + expect(body.settings.accountWeights).toBeDefined() + } finally { + await closeServer(server) + } + }) + + test('returns custom weights without preset field', async () => { + const { server, port } = await startServer() + + try { + // Apply custom weights with non-matching thresholds + await requestJson(port, '/api/settings', { + method: 'PUT', + body: JSON.stringify({ + rotationStrategy: 'weighted-round-robin', + criticalThreshold: 15, + lowThreshold: 35, + accountWeights: { + alpha: 0.7, + beta: 0.2, + gamma: 0.1 + }, + actor: 'test-suite' + }) + }) + + const { status, body } = await requestJson(port, '/api/settings') + + expect(status).toBe(200) + expect(body.settings.rotationStrategy).toBe('weighted-round-robin') + // Custom weights don't match any preset thresholds, so preset should be undefined + expect(body.preset).toBeUndefined() + expect(body.settings.accountWeights.alpha).toBe(0.7) + expect(body.settings.accountWeights.beta).toBe(0.2) + expect(body.settings.accountWeights.gamma).toBe(0.1) + } finally { + await closeServer(server) + } + }) + }) +}) diff --git a/tests/unit/index-sticky.test.ts b/tests/unit/index-sticky.test.ts index b4bdc9c..7b5e6fd 100644 --- a/tests/unit/index-sticky.test.ts +++ b/tests/unit/index-sticky.test.ts @@ -1,8 +1,12 @@ +// @ts-ignore - ESM Jest globals are available at runtime in the test environment. +import { jest } from '@jest/globals' import * as fs from 'node:fs' import * as os from 'node:os' import * as path from 'node:path' -// @ts-ignore - ESM Jest globals are available at runtime in the test environment. -import { jest } from '@jest/globals' + +const esmJest = jest as typeof jest & { + unstable_mockModule: (moduleName: string, factory: () => Record) => void +} describe('Sticky identity request plumbing', () => { async function loadIndexModule(): Promise { @@ -23,7 +27,13 @@ describe('Sticky identity request plumbing', () => { }, prompt_cache_key: 'cache-123' }, - allowPromptCacheKey: false + allowPromptCacheKey: false, + identitySources: [ + 'header:session_id', + 'header:conversation_id', + 'body:metadata.session_id', + 'body:metadata.conversation_id' + ] }) expect(sticky).toEqual({ @@ -42,7 +52,8 @@ describe('Sticky identity request plumbing', () => { metadata: {}, prompt_cache_key: undefined }, - allowPromptCacheKey: false + allowPromptCacheKey: false, + identitySources: ['header:session_id'] }) expect(sticky).toBeNull() @@ -56,7 +67,8 @@ describe('Sticky identity request plumbing', () => { body: { prompt_cache_key: 'cache-only-123' }, - allowPromptCacheKey: false + allowPromptCacheKey: false, + identitySources: ['body:prompt_cache_key'] }) expect(sticky).toBeNull() @@ -129,6 +141,19 @@ describe('Sticky account-selection context plumbing', () => { stickyEnabled: boolean headers?: Record body?: Record + stickyConfig?: { + identitySources: Array< + 'header:session_id' | + 'header:conversation_id' | + 'body:metadata.session_id' | + 'body:metadata.conversation_id' | + 'body:prompt_cache_key' + > + allowPromptCacheKey: boolean + ttlMs?: number + maxEntries?: number + maxFileBytes?: number + } }): Promise<{ getNextAccount: jest.Mock; fetchSpy: jest.SpyInstance }> { jest.resetModules() @@ -144,7 +169,7 @@ describe('Sticky account-selection context plumbing', () => { token: createAccessToken('acct-123') } as any) - jest.unstable_mockModule('../../src/rotation.js', () => ({ + esmJest.unstable_mockModule('../../src/rotation.js', () => ({ getNextAccount, clearAuthInvalid: jest.fn(), markAuthInvalid: jest.fn(), @@ -153,7 +178,7 @@ describe('Sticky account-selection context plumbing', () => { markWorkspaceDeactivated: jest.fn() })) - jest.unstable_mockModule('../../src/settings.js', () => ({ + esmJest.unstable_mockModule('../../src/settings.js', () => ({ getRuntimeSettings: () => ({ settings: { rotationStrategy: 'round-robin', @@ -166,6 +191,18 @@ describe('Sticky account-selection context plumbing', () => { } }, source: 'persisted' + }), + getStickySessionRuntimeSettings: () => ({ + identitySources: options.stickyConfig?.identitySources || [ + 'header:session_id', + 'header:conversation_id', + 'body:metadata.session_id', + 'body:metadata.conversation_id' + ], + allowPromptCacheKey: options.stickyConfig?.allowPromptCacheKey ?? false, + ttlMs: options.stickyConfig?.ttlMs ?? 86_400_000, + maxEntries: options.stickyConfig?.maxEntries ?? 1000, + maxFileBytes: options.stickyConfig?.maxFileBytes ?? 1_048_576 }) })) @@ -254,4 +291,32 @@ describe('Sticky account-selection context plumbing', () => { { model: 'gpt-5.4' } ) }) + + it('uses persisted sticky identity source ordering and prompt-cache authorization at runtime', async () => { + const { getNextAccount } = await invokePluginFetch({ + stickyEnabled: true, + headers: { + session_id: 'session-should-be-ignored' + }, + body: { + prompt_cache_key: 'cache-123' + }, + stickyConfig: { + identitySources: ['body:prompt_cache_key'], + allowPromptCacheKey: true + } + }) + + expect(getNextAccount).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ + model: 'gpt-5.4', + sticky: { + source: 'body:prompt_cache_key', + canonical: 'cache-123', + hash: expect.any(String) + } + }) + ) + }) }) diff --git a/tests/unit/limits-refresh.test.ts b/tests/unit/limits-refresh.test.ts index 825b0a1..6e13232 100644 --- a/tests/unit/limits-refresh.test.ts +++ b/tests/unit/limits-refresh.test.ts @@ -1,34 +1,39 @@ +// @ts-ignore - ESM Jest globals are available at runtime in the test environment. import { jest } from '@jest/globals' import type { AccountCredentials } from '../../src/types.js' +const esmJest = jest as typeof jest & { + unstable_mockModule: (moduleName: string, factory: () => Record) => void +} + const updateAccount = jest.fn() const loadStore = jest.fn() -const probeRateLimitsForAccount = jest.fn<() => Promise>() -const fetchUsageRateLimitsForAccount = jest.fn<() => Promise>() +const probeRateLimitsForAccount = jest.fn() +const fetchUsageRateLimitsForAccount = jest.fn() const logError = jest.fn() const logInfo = jest.fn() const markAuthInvalid = jest.fn() const markWorkspaceDeactivated = jest.fn() -jest.unstable_mockModule('../../src/store.js', () => ({ +esmJest.unstable_mockModule('../../src/store.js', () => ({ loadStore, updateAccount })) -jest.unstable_mockModule('../../src/probe-limits.js', () => ({ +esmJest.unstable_mockModule('../../src/probe-limits.js', () => ({ probeRateLimitsForAccount })) -jest.unstable_mockModule('../../src/usage-limits.js', () => ({ +esmJest.unstable_mockModule('../../src/usage-limits.js', () => ({ fetchUsageRateLimitsForAccount })) -jest.unstable_mockModule('../../src/logger.js', () => ({ +esmJest.unstable_mockModule('../../src/logger.js', () => ({ logError, logInfo })) -jest.unstable_mockModule('../../src/rotation.js', () => ({ +esmJest.unstable_mockModule('../../src/rotation.js', () => ({ markAuthInvalid, markWorkspaceDeactivated })) diff --git a/tests/unit/refresh-queue.test.ts b/tests/unit/refresh-queue.test.ts index b26962e..419e0ca 100644 --- a/tests/unit/refresh-queue.test.ts +++ b/tests/unit/refresh-queue.test.ts @@ -1,20 +1,25 @@ +// @ts-ignore - ESM Jest globals are available at runtime in the test environment. import { jest } from '@jest/globals' import type { AccountCredentials } from '../../src/types.js' -const refreshRateLimitsForAccount = jest.fn<(account: AccountCredentials) => Promise>() +const esmJest = jest as typeof jest & { + unstable_mockModule: (moduleName: string, factory: () => Record) => void +} + +const refreshRateLimitsForAccount = jest.fn() const updateAccount = jest.fn() const logInfo = jest.fn() const logWarn = jest.fn() -jest.unstable_mockModule('../../src/limits-refresh.js', () => ({ +esmJest.unstable_mockModule('../../src/limits-refresh.js', () => ({ refreshRateLimitsForAccount })) -jest.unstable_mockModule('../../src/store.js', () => ({ +esmJest.unstable_mockModule('../../src/store.js', () => ({ updateAccount })) -jest.unstable_mockModule('../../src/logger.js', () => ({ +esmJest.unstable_mockModule('../../src/logger.js', () => ({ logInfo, logWarn })) diff --git a/tests/unit/usage-limits.test.ts b/tests/unit/usage-limits.test.ts index a7fe498..9303e0d 100644 --- a/tests/unit/usage-limits.test.ts +++ b/tests/unit/usage-limits.test.ts @@ -1,3 +1,4 @@ +// @ts-ignore - ESM Jest globals are available at runtime in the test environment. import { jest } from '@jest/globals' import { classifyUsageApiFailure, diff --git a/tests/web-headless/dashboard-smoke.test.ts b/tests/web-headless/dashboard-smoke.test.ts index 45deccb..98e28a2 100644 --- a/tests/web-headless/dashboard-smoke.test.ts +++ b/tests/web-headless/dashboard-smoke.test.ts @@ -8,6 +8,7 @@ import { once } from 'node:events' const SANDBOX_ROOT = path.join(os.tmpdir(), 'oma-web-headless-sandbox') const STORE_FILE = path.join(SANDBOX_ROOT, 'accounts.json') const AUTH_FILE = path.join(SANDBOX_ROOT, 'auth.json') +const WEB_DIST_DIR = path.resolve(process.cwd(), 'tests/fixtures/web-dist') const originalEnv = process.env let startWebConsole: typeof import('../../src/web.js').startWebConsole @@ -58,7 +59,8 @@ beforeAll(async () => { ...originalEnv, OPENCODE_MULTI_AUTH_STORE_DIR: SANDBOX_ROOT, OPENCODE_MULTI_AUTH_STORE_FILE: STORE_FILE, - OPENCODE_MULTI_AUTH_CODEX_AUTH_FILE: AUTH_FILE + OPENCODE_MULTI_AUTH_CODEX_AUTH_FILE: AUTH_FILE, + OPENCODE_MULTI_AUTH_WEB_DIST_DIR: WEB_DIST_DIR } ;({ startWebConsole } = await import('../../src/web.js')) @@ -80,23 +82,50 @@ afterAll(() => { }) describe('dashboard headless smoke', () => { - it('serves parsable client script', async () => { + it('serves the fixture-backed SPA entry while preserving backend JSON routes', async () => { const port = await getFreePort() const server = startWebConsole({ host: '127.0.0.1', port }) try { await once(server, 'listening') - const response = await fetch(`http://127.0.0.1:${port}/`) - expect(response.status).toBe(200) + const homeResponse = await fetch(`http://127.0.0.1:${port}/`) + expect(homeResponse.status).toBe(200) + expect(homeResponse.headers.get('content-type')).toContain('text/html') + expect(await homeResponse.text()).toContain('fixture-spa-shell') - const html = await response.text() - const scriptMatch = html.match(/ + + diff --git a/tests/web-headless/dashboard-account-quota-cards.test.ts b/tests/web-headless/dashboard-account-quota-cards.test.ts new file mode 100644 index 0000000..4e28c45 --- /dev/null +++ b/tests/web-headless/dashboard-account-quota-cards.test.ts @@ -0,0 +1,322 @@ +import * as fs from 'node:fs' +import * as net from 'node:net' +import * as os from 'node:os' +import * as path from 'node:path' +import type * as http from 'node:http' +import { once } from 'node:events' + +const SANDBOX_ROOT = path.join(os.tmpdir(), 'oma-dashboard-quota-cards-sandbox') +const STORE_FILE = path.join(SANDBOX_ROOT, 'accounts.json') +const AUTH_FILE = path.join(SANDBOX_ROOT, 'auth.json') +const WEB_DIST_DIR = path.resolve(process.cwd(), 'tests/fixtures/web-dist') +const originalEnv = process.env + +let startWebConsole: typeof import('../../src/web.js').startWebConsole +let getCodexAuthPath: typeof import('../../src/codex-auth.js').getCodexAuthPath + +async function getFreePort(): Promise { + return new Promise((resolve, reject) => { + const server = net.createServer() + server.listen(0, '127.0.0.1', () => { + const address = server.address() + if (!address || typeof address === 'string') { + reject(new Error('Failed to resolve free port')) + return + } + const port = address.port + server.close((err) => { + if (err) { + reject(err) + return + } + resolve(port) + }) + }) + server.on('error', reject) + }) +} + +async function closeServer(server: http.Server): Promise { + await new Promise((resolve, reject) => { + server.close((err) => { + if (err) { + reject(err) + return + } + resolve() + }) + }) +} + +function seedSandbox(): void { + fs.rmSync(SANDBOX_ROOT, { recursive: true, force: true }) + fs.mkdirSync(SANDBOX_ROOT, { recursive: true }) + fs.writeFileSync(AUTH_FILE, JSON.stringify({ OPENAI_API_KEY: null, tokens: {} }, null, 2)) + + fs.writeFileSync( + STORE_FILE, + JSON.stringify( + { + version: 2, + activeAlias: 'alpha', + rotationIndex: 0, + lastRotation: 1_700_000_000_000, + rotationStrategy: 'round-robin', + settings: { + rotationStrategy: 'round-robin', + criticalThreshold: 10, + lowThreshold: 30, + accountWeights: {}, + featureFlags: { + antigravityEnabled: false, + stickySessionsEnabled: false + } + }, + accounts: { + alpha: { + alias: 'alpha', + accessToken: 'token-alpha', + refreshToken: 'refresh-alpha', + expiresAt: Date.now() + 60_000, + email: 'alpha@example.com', + usageCount: 3, + enabled: true, + tags: ['core'], + notes: 'primary account', + source: 'opencode', + rateLimits: { + fiveHour: { limit: 100, remaining: 80, resetAt: Date.now() + 60_000, updatedAt: Date.now() }, + weekly: { limit: 1000, remaining: 700, resetAt: Date.now() + 120_000, updatedAt: Date.now() } + }, + limitsConfidence: 'fresh' + }, + beta: { + alias: 'beta', + accessToken: 'token-beta', + refreshToken: 'refresh-beta', + expiresAt: Date.now() + 120_000, + email: 'beta@example.com', + usageCount: 7, + enabled: false, + tags: ['backup'], + notes: 'secondary account', + source: 'codex', + rateLimits: { + fiveHour: { limit: 100, remaining: 15, resetAt: Date.now() + 60_000, updatedAt: Date.now() }, + weekly: { limit: 1000, remaining: 50, resetAt: Date.now() + 120_000, updatedAt: Date.now() } + }, + limitsConfidence: 'stale', + disabledAt: Date.now(), + disabledBy: 'operator' + }, + gamma: { + alias: 'gamma', + accessToken: 'token-gamma', + refreshToken: 'refresh-gamma', + expiresAt: Date.now() + 90_000, + email: 'gamma@example.com', + usageCount: 1, + enabled: true, + tags: [], + notes: '', + source: 'opencode', + limitsConfidence: 'unknown' + } + }, + login: null, + queue: null + }, + null, + 2 + ) + ) +} + +beforeAll(async () => { + if (fs.existsSync(SANDBOX_ROOT)) { + fs.rmSync(SANDBOX_ROOT, { recursive: true, force: true }) + } + fs.mkdirSync(SANDBOX_ROOT, { recursive: true }) + fs.writeFileSync(AUTH_FILE, JSON.stringify({ OPENAI_API_KEY: null, tokens: {} }, null, 2)) + + process.env = { + ...originalEnv, + OPENCODE_MULTI_AUTH_STORE_DIR: SANDBOX_ROOT, + OPENCODE_MULTI_AUTH_STORE_FILE: STORE_FILE, + OPENCODE_MULTI_AUTH_CODEX_AUTH_FILE: AUTH_FILE, + OPENCODE_MULTI_AUTH_WEB_DIST_DIR: WEB_DIST_DIR + } + + ;({ startWebConsole } = await import('../../src/web.js')) + ;({ getCodexAuthPath } = await import('../../src/codex-auth.js')) +}) + +afterAll(() => { + try { + if (getCodexAuthPath) { + fs.unwatchFile(getCodexAuthPath()) + } + } catch { + // ignore + } + process.env = originalEnv + if (fs.existsSync(SANDBOX_ROOT)) { + fs.rmSync(SANDBOX_ROOT, { recursive: true, force: true }) + } +}) + +describe('dashboard account quota cards', () => { + it('serves the SPA at / with dashboard data surface', async () => { + seedSandbox() + const port = await getFreePort() + const server = startWebConsole({ host: '127.0.0.1', port }) + + try { + await once(server, 'listening') + const response = await fetch(`http://127.0.0.1:${port}/`) + expect(response.status).toBe(200) + expect(response.headers.get('content-type')).toContain('text/html') + + const body = await response.text() + expect(body).toContain('fixture-spa-shell') + expect(body).toContain('') + expect(response.body).not.toContain('data-dashboard-surface="overview"') + expect(response.body).not.toContain('id="openAccountModalBtn"') + } finally { + await closeServer(server) + } + }) + + it('serves the fixture asset bundle and no longer exposes inline dashboard scripting', async () => { + const { server, port } = await startServer() + + try { + const response = await requestText(port, '/') + expect(response.status).toBe(200) + + expect(response.body).not.toMatch(/ + + diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..a779127 --- /dev/null +++ b/web/package.json @@ -0,0 +1,35 @@ +{ + "name": "@guard22/opencode-multi-auth-codex-web", + "private": true, + "version": "1.4.3", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "typecheck": "tsc --noEmit", + "verify:static": "pnpm run lint && pnpm run typecheck", + "test": "pnpm run test:unit && pnpm run test:component", + "test:unit": "vitest run src/hooks/__tests__/useDashboardState.test.tsx src/hooks/__tests__/useNotification.test.tsx", + "test:component": "vitest run src/components/__tests__", + "test:watch": "vitest", + "lint": "tsc --noEmit" + }, + "dependencies": { + "@tanstack/react-query": "^5.64.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.28.0", + "recharts": "^3.8.1" + }, + "devDependencies": { + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.1.0", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", + "@vitejs/plugin-react": "^4.3.4", + "jsdom": "^25.0.1", + "typescript": "^5.7.2", + "vite": "^6.0.7", + "vitest": "^2.1.8" + } +} diff --git a/web/src/App.tsx b/web/src/App.tsx new file mode 100644 index 0000000..3fade60 --- /dev/null +++ b/web/src/App.tsx @@ -0,0 +1,42 @@ +import { Routes, Route, Navigate } from 'react-router-dom' +import { AppShell } from './components/AppShell' +import { NotificationProvider } from './hooks/useNotification' +import { NotificationCenter } from './components/NotificationCenter' +import { DashboardPage } from './components/DashboardPage' +import { ConfigurationPage } from './components/ConfigurationPage' +import { AntigravityPage } from './components/AntigravityPage' +import { useDashboardState } from './hooks/useDashboardState' + +export function App(): JSX.Element { + const { data: state } = useDashboardState() + const featureFlags = state?.featureFlags ?? { antigravityEnabled: false } + + return ( + + + + } /> + } /> + {featureFlags.antigravityEnabled && ( + } /> + )} + + {/* Legacy route redirects: collapse 4-area model into 2-section IA */} + } /> + } /> + } /> + + } + /> + + + + + ) +} diff --git a/web/src/api/client.ts b/web/src/api/client.ts new file mode 100644 index 0000000..869c4c5 --- /dev/null +++ b/web/src/api/client.ts @@ -0,0 +1,247 @@ +/** + * Typed fetch wrapper for the dashboard API. + * + * All requests are unauthenticated (localhost-only dashboard). + * Errors are normalized to a consistent ApiError shape. + */ + +import type { ApiErrorResponse } from '../types/api.ts' + +export class ApiError extends Error { + status: number + code?: string + details?: ApiErrorResponse['details'] + + constructor(status: number, body: ApiErrorResponse) { + super(body.error) + this.name = 'ApiError' + this.status = status + this.code = body.code + this.details = body.details + } +} + +async function parseJsonResponse(response: Response): Promise { + const text = await response.text() + try { + return JSON.parse(text) as T + } catch { + throw new ApiError(response.status, { error: 'Invalid JSON response' }) + } +} + +async function handleResponse(response: Response): Promise { + if (!response.ok) { + const body = await parseJsonResponse(response) + throw new ApiError(response.status, body) + } + return parseJsonResponse(response) +} + +export interface RequestOptions { + method?: 'GET' | 'POST' | 'PUT' | 'DELETE' + body?: unknown + headers?: Record +} + +export async function apiRequest(pathname: string, options: RequestOptions = {}): Promise { + const response = await fetch(pathname, { + method: options.method ?? 'GET', + headers: { + 'Content-Type': 'application/json', + ...options.headers + }, + body: options.body !== undefined ? JSON.stringify(options.body) : undefined + }) + return handleResponse(response) +} + +// Convenience helpers aligned with frozen API contracts + +export function getState() { + return apiRequest('/api/state') +} + +export function getLogs(limit?: number) { + const qs = limit !== undefined ? `?limit=${limit}` : '' + return apiRequest(`/api/logs${qs}`) +} + +export function getAccounts() { + return apiRequest<{ accounts: import('../types/api.ts').AccountView[] }>('/api/accounts') +} + +export function syncAuth() { + return apiRequest('/api/sync', { method: 'POST' }) +} + +export function startAuth(alias: string) { + return apiRequest<{ ok: true; url: string }>('/api/auth/start', { + method: 'POST', + body: { alias } + }) +} + +export function startAutoLogin(selector: string, visible?: boolean) { + return apiRequest<{ ok: true; url?: string }>('/api/auto-login/start', { + method: 'POST', + body: { selector, visible } + }) +} + +export function addAutoLoginAccount(input: { + email: string + password: string + alias?: string + chatgptPassword?: string +}) { + return apiRequest<{ ok: true }>('/api/auto-login/add', { + method: 'POST', + body: input + }) +} + +export function switchAccount(alias: string) { + return apiRequest('/api/switch', { + method: 'POST', + body: { alias } + }) +} + +export function removeAccount(alias: string) { + return apiRequest('/api/remove', { + method: 'POST', + body: { alias } + }) +} + +export function updateAccountMeta(alias: string, tags?: string, notes?: string) { + return apiRequest('/api/account/meta', { + method: 'POST', + body: { alias, tags, notes } + }) +} + +export function refreshToken(alias?: string) { + return apiRequest('/api/token/refresh', { + method: 'POST', + body: { alias } + }) +} + +export function refreshLimits(alias?: string) { + return apiRequest('/api/limits/refresh', { + method: 'POST', + body: { alias } + }) +} + +export function stopRefreshQueue() { + return apiRequest('/api/limits/stop', { + method: 'POST' + }) +} + +export function getForceState() { + return apiRequest('/api/force') +} + +export function activateForce(alias: string, actor?: string) { + return apiRequest('/api/force', { + method: 'POST', + body: { alias, actor } + }) +} + +export function clearForce() { + return apiRequest('/api/force/clear', { + method: 'POST' + }) +} + +export function getSettings() { + return apiRequest('/api/settings') +} + +export function updateSettings( + updates: Partial & { actor?: string } +) { + return apiRequest('/api/settings', { + method: 'PUT', + body: updates + }) +} + +export function getFeatureFlags() { + return apiRequest('/api/settings/feature-flags') +} + +export function updateFeatureFlags(featureFlags: import('../types/api.ts').FeatureFlags, actor?: string) { + return apiRequest('/api/settings/feature-flags', { + method: 'PUT', + body: { featureFlags, actor } + }) +} + +export function resetSettings(actor?: string) { + return apiRequest('/api/settings/reset', { + method: 'POST', + body: { actor } + }) +} + +export function applyPreset(preset: import('../types/api.ts').WeightPreset, actor?: string) { + return apiRequest('/api/settings/preset', { + method: 'POST', + body: { preset, actor } + }) +} + +export function enableAccount(alias: string, enabled: boolean) { + return apiRequest(`/api/accounts/${encodeURIComponent(alias)}/enabled`, { + method: 'PUT', + body: { enabled } + }) +} + +export function reauthAccount(alias: string, actor?: string) { + return apiRequest(`/api/accounts/${encodeURIComponent(alias)}/reauth`, { + method: 'POST', + body: { actor } + }) +} + +export function refreshAntigravityQuota() { + return apiRequest<{ ok: true; quota: import('../types/api.ts').AntigravityQuota }>('/api/antigravity/refresh', { + method: 'POST' + }) +} + +export function refreshAntigravityQuotaAll() { + return apiRequest<{ ok: true; quota: import('../types/api.ts').AntigravityQuota }>('/api/antigravity/refresh-all', { + method: 'POST' + }) +} + +// Sticky-session additive contracts (Phase 4+) + +export function getStickySessionConfig() { + return apiRequest('/api/sticky-sessions/config') +} + +export function updateStickySessionConfig(config: import('../types/api.ts').StickySessionSettings) { + return apiRequest('/api/sticky-sessions/config', { + method: 'PUT', + body: config + }) +} + +export function getStickySessionStatus() { + return apiRequest('/api/sticky-sessions/status') +} + +export function cleanupStickySessions() { + return apiRequest('/api/sticky-sessions/cleanup', { + method: 'POST' + }) +} diff --git a/web/src/api/queries.ts b/web/src/api/queries.ts new file mode 100644 index 0000000..89e91c4 --- /dev/null +++ b/web/src/api/queries.ts @@ -0,0 +1,387 @@ +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import { + getState, + getLogs, + syncAuth, + refreshToken, + refreshLimits, + stopRefreshQueue, + startAuth, + startAutoLogin, + addAutoLoginAccount, + switchAccount, + enableAccount, + removeAccount, + updateAccountMeta, + reauthAccount, + getForceState, + activateForce, + clearForce, + refreshAntigravityQuota, + refreshAntigravityQuotaAll, + getSettings, + updateSettings, + getFeatureFlags, + updateFeatureFlags, + resetSettings, + applyPreset, + getStickySessionConfig, + updateStickySessionConfig, + getStickySessionStatus, + cleanupStickySessions +} from './client' +import type { DashboardState, LogsResponse, ForceState, SettingsInfo, FeatureFlagsResponse } from '../types/api' + +// Query keys +export const queryKeys = { + dashboardState: ['dashboardState'] as const, + logs: (limit?: number) => ['logs', limit] as const, + accounts: ['accounts'] as const, + settings: ['settings'] as const, + forceState: ['forceState'] as const, + featureFlags: ['featureFlags'] as const, + stickySessionConfig: ['stickySessionConfig'] as const, + stickySessionStatus: ['stickySessionStatus'] as const +} + +// Dashboard state query +export function useDashboardStateQuery(pollingInterval = 5000) { + return useQuery({ + queryKey: queryKeys.dashboardState, + queryFn: getState, + refetchInterval: pollingInterval, + staleTime: Math.min(pollingInterval / 2, 2000) + }) +} + +// Sync mutation +export function useSyncMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: syncAuth, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Refresh tokens mutation +export function useRefreshTokensMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: () => refreshToken(), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Refresh limits mutation +export function useRefreshLimitsMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: () => refreshLimits(), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Stop refresh queue mutation +export function useStopRefreshQueueMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: stopRefreshQueue, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Start auth mutation +export function useStartAuthMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: startAuth, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Start auto-login mutation +export function useStartAutoLoginMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (vars: { selector: string; visible?: boolean }) => startAutoLogin(vars.selector, vars.visible), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Add auto-login account mutation +export function useAddAutoLoginAccountMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: addAutoLoginAccount, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Switch account mutation +export function useSwitchAccountMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: switchAccount, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Enable/disable account mutation +export function useEnableAccountMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (vars: { alias: string; enabled: boolean }) => enableAccount(vars.alias, vars.enabled), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Remove account mutation +export function useRemoveAccountMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: removeAccount, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Update account meta mutation +export function useUpdateAccountMetaMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (vars: { alias: string; tags?: string; notes?: string }) => updateAccountMeta(vars.alias, vars.tags, vars.notes), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Reauth account mutation +export function useReauthAccountMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (vars: { alias: string; actor?: string }) => reauthAccount(vars.alias, vars.actor), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Logs query +export function useLogsQuery(limit = 50) { + return useQuery({ + queryKey: queryKeys.logs(limit), + queryFn: () => getLogs(limit), + refetchInterval: 5000, + staleTime: 2000 + }) +} + +// Force state query +export function useForceStateQuery() { + return useQuery({ + queryKey: queryKeys.forceState, + queryFn: getForceState, + refetchInterval: 5000, + staleTime: 2000 + }) +} + +// Activate force mutation +export function useActivateForceMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (vars: { alias: string; actor?: string }) => activateForce(vars.alias, vars.actor), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.forceState }) + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Clear force mutation +export function useClearForceMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: clearForce, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.forceState }) + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Refresh antigravity quota mutation +export function useRefreshAntigravityMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: refreshAntigravityQuota, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Refresh all antigravity quotas mutation +export function useRefreshAntigravityAllMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: refreshAntigravityQuotaAll, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Settings query +export function useSettingsQuery() { + return useQuery({ + queryKey: queryKeys.settings, + queryFn: getSettings, + refetchInterval: 5000, + staleTime: 2000 + }) +} + +// Update settings mutation +export function useUpdateSettingsMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (vars: { updates: Parameters[0] }) => updateSettings(vars.updates), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.settings }) + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Feature flags query +export function useFeatureFlagsQuery() { + return useQuery({ + queryKey: queryKeys.featureFlags, + queryFn: getFeatureFlags, + refetchInterval: 5000, + staleTime: 2000 + }) +} + +// Update feature flags mutation +export function useUpdateFeatureFlagsMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (vars: { featureFlags: import('../types/api.ts').FeatureFlags; actor?: string }) => + updateFeatureFlags(vars.featureFlags, vars.actor), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.featureFlags }) + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Reset settings mutation +export function useResetSettingsMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (actor?: string) => resetSettings(actor), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.settings }) + queryClient.invalidateQueries({ queryKey: queryKeys.featureFlags }) + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Apply preset mutation +export function useApplyPresetMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (vars: { preset: import('../types/api.ts').WeightPreset; actor?: string }) => applyPreset(vars.preset, vars.actor), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.settings }) + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Sticky session config query +export function useStickySessionConfigQuery() { + return useQuery({ + queryKey: queryKeys.stickySessionConfig, + queryFn: getStickySessionConfig, + enabled: false // manual fetch; panel triggers on mount + }) +} + +// Sticky session status query +export function useStickySessionStatusQuery() { + return useQuery({ + queryKey: queryKeys.stickySessionStatus, + queryFn: getStickySessionStatus, + enabled: false // manual refresh; no polling + }) +} + +// Update sticky session config mutation +export function useUpdateStickySessionConfigMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (config: import('../types/api.ts').StickySessionSettings) => updateStickySessionConfig(config), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.stickySessionConfig }) + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} + +// Cleanup sticky sessions mutation +export function useCleanupStickySessionsMutation() { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: cleanupStickySessions, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: queryKeys.stickySessionStatus }) + queryClient.invalidateQueries({ queryKey: queryKeys.dashboardState }) + } + }) +} diff --git a/web/src/components/AccountDrawer.css b/web/src/components/AccountDrawer.css new file mode 100644 index 0000000..065c2d4 --- /dev/null +++ b/web/src/components/AccountDrawer.css @@ -0,0 +1,152 @@ +.account-drawer-overlay { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(4px); + z-index: 50; + display: flex; + justify-content: flex-end; +} + +.account-drawer { + width: 420px; + max-width: 90vw; + height: 100%; + background-color: var(--bg-secondary); + border-left: 1px solid var(--border); + display: flex; + flex-direction: column; + overflow-y: auto; + box-shadow: var(--shadow-xl); +} + +.account-drawer-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--spacing-lg) var(--spacing-xl); + border-bottom: 1px solid var(--border); + background: var(--surface); +} + +.account-drawer-header h3 { + margin: 0; + font-size: 1.125rem; + font-weight: 600; + color: var(--text); + letter-spacing: -0.01em; +} + +.account-drawer-body { + padding: var(--spacing-xl); + display: flex; + flex-direction: column; + gap: var(--spacing-xl); +} + +.drawer-section { + display: flex; + flex-direction: column; + gap: var(--spacing-sm); +} + +.drawer-section h4 { + margin: 0 0 var(--spacing-xs) 0; + font-size: 0.875rem; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.drawer-field { + display: flex; + justify-content: space-between; + align-items: baseline; + gap: var(--spacing-md); + padding: var(--spacing-sm) 0; + border-bottom: 1px solid var(--border-subtle); +} + +.drawer-field:last-child { + border-bottom: none; +} + +.drawer-label { + color: var(--text-secondary); + font-size: 0.875rem; + min-width: 100px; + font-weight: 500; +} + +.drawer-value { + word-break: break-word; + text-align: right; + color: var(--text); + font-weight: 500; +} + +.drawer-actions { + display: flex; + flex-direction: column; + gap: var(--spacing-sm); +} + +.drawer-actions button { + width: 100%; + justify-content: center; + border-radius: var(--radius); + font-weight: 500; +} + +.icon-button { + background: var(--surface); + border: 1px solid var(--border); + font-size: 1.25rem; + line-height: 1; + width: 2.25rem; + height: 2.25rem; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + color: var(--text-secondary); + border-radius: var(--radius); + transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease; +} + +.icon-button:hover { + background: var(--surface-elevated); + color: var(--text); + border-color: var(--border-strong); +} + +.icon-button:focus-visible { + outline: none; + box-shadow: var(--focus-ring); +} + +input, +textarea { + padding: var(--spacing-sm) var(--spacing-md); + background: var(--bg); + border: 1px solid var(--border-strong); + border-radius: var(--radius); + color: var(--text); + width: 100%; + font-size: 0.875rem; + box-shadow: var(--shadow-inset); + transition: border-color 0.15s ease, box-shadow 0.15s ease; +} + +input:focus-visible, +textarea:focus-visible { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2), var(--shadow-inset); +} + +textarea { + resize: vertical; + min-height: 80px; +} diff --git a/web/src/components/AccountDrawer.tsx b/web/src/components/AccountDrawer.tsx new file mode 100644 index 0000000..fac7764 --- /dev/null +++ b/web/src/components/AccountDrawer.tsx @@ -0,0 +1,190 @@ +import { useState, useCallback, useEffect, useRef } from 'react' +import { isAccountEnabled } from '../lib/account-status' +import { formatResetTime } from '../lib/reset-time' +import type { AccountView } from '../types/api' +import './AccountDrawer.css' + +export interface AccountDrawerProps { + account: AccountView + isActive: boolean + onClose: () => void + onToggleEnable: () => void + onRemove: () => void + onUpdateMeta: (tags: string, notes: string) => void + onReauth: () => void + onRefreshTokens: () => void + onRefreshLimits: () => void + onSwitch: () => void + isBusy: boolean +} + +export function AccountDrawer({ + account, + isActive, + onClose, + onToggleEnable, + onRemove, + onUpdateMeta, + onReauth, + onRefreshTokens, + onRefreshLimits, + onSwitch, + isBusy +}: AccountDrawerProps): JSX.Element { + const [tags, setTags] = useState(account.tags?.join(', ') || '') + const [notes, setNotes] = useState(account.notes || '') + const closeButtonRef = useRef(null) + + const handleSaveMeta = useCallback(() => { + onUpdateMeta(tags, notes) + }, [onUpdateMeta, tags, notes]) + + const handleRemove = useCallback(() => { + if (window.confirm(`Remove account "${account.alias}" permanently?`)) { + onRemove() + } + }, [onRemove, account.alias]) + + useEffect(() => { + closeButtonRef.current?.focus() + }, []) + + useEffect(() => { + function handleKeyDown(e: KeyboardEvent) { + if (e.key === 'Escape') { + onClose() + } + } + document.addEventListener('keydown', handleKeyDown) + return () => document.removeEventListener('keydown', handleKeyDown) + }, [onClose]) + + return ( +
+
e.stopPropagation()} + role="dialog" + aria-label="Account details" + aria-modal="true" + > +
+

{account.alias}

+ +
+ +
+
+
+ Email + {account.email || '-'} +
+
+ Status + + {isAccountEnabled(account) ? ( + enabled + ) : ( + disabled + )} + +
+
+ Source + {account.source || '-'} +
+
+ Usage + {account.usageCount} +
+ {account.disabledAt && ( +
+ Disabled + + {new Date(account.disabledAt).toLocaleString()} by {account.disabledBy || 'unknown'} + +
+ )} + {account.lastRefresh && ( +
+ Last refresh + {new Date(account.lastRefresh).toLocaleString()} +
+ )} + {account.rateLimits?.fiveHour?.resetAt && ( +
+ 5h reset + {formatResetTime(account.rateLimits.fiveHour.resetAt)} +
+ )} + {account.rateLimits?.weekly?.resetAt && ( +
+ Weekly reset + {formatResetTime(account.rateLimits.weekly.resetAt)} +
+ )} +
+ +
+ {!isActive && isAccountEnabled(account) && ( + + )} + + + + + +
+ +
+

Tags

+ setTags(e.target.value)} + placeholder="Comma-separated tags" + aria-label="Tags" + /> +

Notes

+