mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-01 04:42:10 +03:00
* chore(release): open v3.8.13 development cycle Bump 3.8.12 → 3.8.13 across package.json, lockfile, electron/, open-sse/, and docs/reference/openapi.yaml; add the [3.8.13] cycle placeholder to the root CHANGELOG and the 41 i18n mirrors. Integration branch for the v3.8.13 cycle — fixes/features land here via per-issue PRs and it merges to main at release time. * fix(ci): skip auto-deploy when VPS host is unreachable from the runner (#3299) Integrated into release/v3.8.13 * fix(dev): auto-rebuild better-sqlite3 on Node ABI mismatch at dev startup (#3301) Integrated into release/v3.8.13 * feat(api): accept path-scoped API keys on client API routes (#3300) Integrated into release/v3.8.13 * fix(sse): harden against empty responses causing Copilot Chat failures (#3297) Integrated into release/v3.8.13 * fix(api): remove Completions.me rickroll provider (discussion #3293) (#3302) Integrated into release/v3.8.13 * fix(opencode-provider): extract contextLength from live model catalog (#3298) Integrated into release/v3.8.13 * feat(web-cookie): self-service login infrastructure + auto-refresh daemon (#3292) Integrated into release/v3.8.13 * docs(changelog): record the v3.8.13 PRs merged this round (#3292/#3300/#3297/#3298/#3301/#3302/#3299) * fix(auth): harden URL token extraction — drop query-string fallback, gate to client routes (security follow-up to #3300) (#3309) Security follow-up to #3300 — integrated into release/v3.8.13 * docs: rename resolve-issues → review-issues skill references * fix(dashboard): keep no-auth providers visible under 'Show configured only' (#3290) (#3312) no-auth providers (opencode, duckduckgo-web, theoldllm, veoaifree-web) never create a DB connection row so stats.total stays 0, which the configured-only filter treated as 'unconfigured' and hid them — even though they are always usable and appear unconditionally in /v1/models. filterConfiguredProviderEntries now treats displayAuthType === 'no-auth' as configured. Co-authored-by: uniQta <uniQta@users.noreply.github.com> * fix(cli): resolve update paths relative to script + recursive backup (#3295) (#3313) omniroute update always failed on a global install: - getCurrentVersion() read package.json from process.cwd(), which on a global npm/brew install is the user's working dir, not the package root → null → 'Could not determine current version'. - createBackup() resolved bin/ from cwd too, and passed the 'cli' directory to copyFileSync → EISDIR, swallowed by the catch → 'Failed to create backup'. Both now resolve package.json/bin relative to the script via import.meta.url, and the backup uses cpSync({recursive:true}) so the cli/ directory is copied. Co-authored-by: uniQta <uniQta@users.noreply.github.com> * fix(theoldllm): read upstream body once to avoid [502] body-already-read (#3296) (#3314) On the cached-token path the executor never enters the refresh branch, so the same upstream Response was read with .text() twice (token-rejection check + final body). A Response body is single-use, so the second read threw 'Body is unusable: Body has already been read', caught and surfaced as [502]. Read the body once into finalBody and only re-read after a token-rejection refetch. Co-authored-by: onizukashonan14-png <onizukashonan14-png@users.noreply.github.com> * fix(sse): strip leaked internal tool envelopes from streaming output (#3311) Integrated into release/v3.8.13 * fix(sse): expose Claude + Gemini budget tiers in the antigravity catalog (#3184) (#3303) Integrated into release/v3.8.13 (#3184) * fix(catalog): compute combo context_length from known targets only (#3304) Integrated into release/v3.8.13 — live contextLength + known-targets combo context (#3298 follow-up) * chore(i18n): add message keys for proxy UI + vscode/ollama endpoint (#3307) Integrated into release/v3.8.13 — i18n message keys for proxy UI + vscode/ollama * feat(dashboard): i18n the proxy settings UI (#3310) Integrated into release/v3.8.13 — i18n the proxy settings UI * feat(api): model catalog enrichment + MCP model-catalog tools (#3306) Integrated into release/v3.8.13 — model catalog enrichment + MCP model-catalog tools, reconciled with #3309 URL-token hardening * test(catalog): align Antigravity preview-alias test with #3303 budget tiers #3303 added the Gemini `-high`/`-low` budget tiers to ANTIGRAVITY_PUBLIC_MODELS (user-callable on the Antigravity OAuth backend, verified via #3184), but did not update the catalog-route test that asserted `antigravity/gemini-3.1-pro-high` must NOT be exposed. The assertion now reflects the intended behavior — the client-visible budget alias IS surfaced — while keeping the legacy `gemini-claude-*` alias keys unexposed. Caught running the full catalog suite on the merged release HEAD (the #3303 round only ran the antigravity-aliases and usage-hardening files). * docs(changelog): record the 6 PRs merged this review round into v3.8.13 #3306/#3307/#3310 (New Features — VS Code split: catalog+MCP, i18n keys, proxy UI i18n), #3311/#3303/#3304 (Bug Fixes — SSE envelope sanitizer, antigravity budget tiers, combo known-targets context_length). * chore(release): finalize v3.8.13 changelog and cleanup Finalize the v3.8.13 changelog with release date, maintenance notes, and contributor credits. Update MCP docs to reference the correct tool inventory diagram, exclude nested .claude worktrees from ESLint scans, and tighten a response sanitizer type guard. * fix(dashboard): refresh connections after provider auth import (#3320) Integrated into release/v3.8.13 — refresh connections after provider auth import * fix(codex): strip client-only params on native /responses passthrough (#3317) (#3325) A /v1/responses request against the built-in codex/ provider does an openai-responses -> openai-responses passthrough (CodexExecutor.transformRequest returns the body early for _nativeCodexPassthrough). It forwarded client-only fields verbatim and the Codex upstream rejected them with 400 Unsupported parameter: prompt_cache_retention / safety_identifier / user — breaking Factory Droid (which injects all three). The chat-completions path already strips these (base.ts #1884, openai-responses translator #2770) but the passthrough skips translation. Strip the three fields in the shared block before the passthrough return; user is removed unconditionally since Codex /responses always rejects it. Co-authored-by: tycronk20 <tycronk20@users.noreply.github.com> * fix(dashboard): normalize agent-bridge /state response to stop page crash (#3318) (#3326) The Agent Bridge page seeded a well-shaped initialData default then replaced it wholesale with the raw /api/tools/agent-bridge/state response. The route returns { server, agents } but the UI reads { serverState, agentStates, bypassPatterns, mappings }, so serverState became undefined and AgentBridgeServerCard crashed on serverState.running — surfaced as the full-page 'Internal Server Error' boundary (client render error, not a real 5xx). Add a shared normalizeAgentBridgeState() that maps the route shape into the page contract (server.running/certExists -> serverState) and always returns safe defaults (never undefined serverState). Wired into both the SSR loader (page.tsx) and the polling hook. The legacy 'agents' entry shape differs from AgentStateEntry so it is not coerced; full route<->page contract reconciliation (port, upstreamCa, bypassPatterns, mappings, agentStates) is a follow-up. Co-authored-by: tycronk20 <tycronk20@users.noreply.github.com> * docs: VS Code/Ollama endpoints + env & i18n tooling (#3319) Integrated into release/v3.8.13 — VS Code/Ollama docs + env & i18n tooling * feat(provider): test-all endpoint, rate-limit overrides, visibility f… (#3267) Integrated into release/v3.8.13 — provider test-all endpoint, rate-limit overrides, model visibility * feat: auto-combo optimization, playground model dropdown, only-configured toggle (#3322) Integrated into release/v3.8.13 — auto-combo candidate expansion + playground dropdown + only-configured toggle * feat(api): VS Code Copilot Ollama-compatible BYOK endpoint (#3316) Integrated into release/v3.8.13 — VS Code Copilot Ollama-compatible BYOK endpoint (reconciled with #3306/#3309 auth hardening) * chore(release): document #3320 in the v3.8.13 changelog + contributor credits --------- Co-authored-by: Felipe Almeman <4226997+zhiru@users.noreply.github.com> Co-authored-by: Wilson <pedbookmed@gmail.com> Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com> Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com> Co-authored-by: uniQta <uniQta@users.noreply.github.com> Co-authored-by: onizukashonan14-png <onizukashonan14-png@users.noreply.github.com> Co-authored-by: tycronk20 <tycronk20@users.noreply.github.com> Co-authored-by: Vinayrnani <vinayrnani@gmail.com>
412 lines
15 KiB
TypeScript
412 lines
15 KiB
TypeScript
/**
|
|
* TokenExtractionConfig — Login & cookie extraction configs for web-cookie providers
|
|
*
|
|
* Each config describes how to:
|
|
* 1. Open a browser window/navigate to the provider's login page
|
|
* 2. Detect successful login (URL change + token presence)
|
|
* 3. Extract session cookies / tokens from the browser context
|
|
*
|
|
* Used by InAppLoginService (Electron BrowserWindow path) and
|
|
* the Playwright-based login flow (dashboard API).
|
|
*/
|
|
|
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
|
|
/** Describes where to extract credential data from after login */
|
|
export type TokenSource =
|
|
| { type: "cookie"; name: string; domain?: string }
|
|
| { type: "localStorage"; key: string }
|
|
| { type: "sessionStorage"; key: string }
|
|
| { type: "header"; name: string };
|
|
|
|
export interface PollingConfig {
|
|
/** Milliseconds between extraction polls (default 1000) */
|
|
pollInterval: number;
|
|
/** Total timeout in ms (default 300000 = 5 min) */
|
|
timeout: number;
|
|
/** Minimum time in ms before first extraction attempt (default 5000) */
|
|
minLoginTime: number;
|
|
}
|
|
|
|
export interface TokenExtractionConfig {
|
|
/** Matches the executor's provider ID (e.g. "claude-web", "gemini-web") */
|
|
providerId: string;
|
|
/** Human-readable name shown in dashboard UI */
|
|
displayName: string;
|
|
/** The URL to navigate to for login */
|
|
loginUrl: string;
|
|
/** The provider's home page URL (for cookie domain binding) */
|
|
homeUrl: string;
|
|
/** Optional regex. If current URL matches → login is likely complete */
|
|
successUrlPattern?: RegExp;
|
|
/** Sources to extract credentials from after login */
|
|
tokenSources: TokenSource[];
|
|
/** Polling behaviour */
|
|
pollingConfig: PollingConfig;
|
|
/** Short instructions shown to the user in the login modal */
|
|
instructions: string;
|
|
/** Optional: cookie domain override for cookie injection */
|
|
cookieDomain?: string;
|
|
}
|
|
|
|
// ─── Defaults ───────────────────────────────────────────────────────────────
|
|
|
|
const DEFAULT_POLLING: PollingConfig = {
|
|
pollInterval: 1000,
|
|
timeout: 300_000,
|
|
minLoginTime: 5000,
|
|
};
|
|
|
|
const QUICK_POLLING: PollingConfig = {
|
|
pollInterval: 800,
|
|
timeout: 120_000,
|
|
minLoginTime: 3000,
|
|
};
|
|
|
|
// ─── Helper ──────────────────────────────────────────────────────────────────
|
|
|
|
function config(
|
|
providerId: string,
|
|
displayName: string,
|
|
loginUrl: string,
|
|
homeUrl: string,
|
|
tokenSources: TokenSource[],
|
|
instructions: string,
|
|
opts?: {
|
|
successUrlPattern?: RegExp;
|
|
pollingConfig?: Partial<PollingConfig>;
|
|
cookieDomain?: string;
|
|
}
|
|
): TokenExtractionConfig {
|
|
return {
|
|
providerId,
|
|
displayName,
|
|
loginUrl,
|
|
homeUrl,
|
|
tokenSources,
|
|
instructions,
|
|
pollingConfig: { ...DEFAULT_POLLING, ...opts?.pollingConfig },
|
|
successUrlPattern: opts?.successUrlPattern,
|
|
cookieDomain: opts?.cookieDomain,
|
|
};
|
|
}
|
|
|
|
// ─── Configuration Map ──────────────────────────────────────────────────────
|
|
|
|
const RAW_CONFIGS: TokenExtractionConfig[] = [
|
|
// ── Claude Web ────────────────────────────────────────────
|
|
config(
|
|
"claude-web",
|
|
"Claude Web",
|
|
"https://claude.ai/login",
|
|
"https://claude.ai",
|
|
[{ type: "cookie", name: "sessionKey", domain: ".claude.ai" }],
|
|
"Log in to your Claude account at claude.ai. After login, the session cookie will be extracted automatically."
|
|
),
|
|
|
|
// ── ChatGPT Web ───────────────────────────────────────────
|
|
config(
|
|
"chatgpt-web",
|
|
"ChatGPT Web",
|
|
"https://chatgpt.com/auth/login",
|
|
"https://chatgpt.com",
|
|
[
|
|
{ type: "cookie", name: "__Secure-next-auth.session-token", domain: ".chatgpt.com" },
|
|
],
|
|
"Log in to ChatGPT. The __Secure-next-auth.session-token cookie will be extracted after login."
|
|
),
|
|
|
|
// ── Gemini Web ────────────────────────────────────────────
|
|
config(
|
|
"gemini-web",
|
|
"Gemini Web",
|
|
"https://gemini.google.com/app",
|
|
"https://gemini.google.com",
|
|
[
|
|
{ type: "cookie", name: "__Secure-1PSID", domain: ".google.com" },
|
|
{ type: "cookie", name: "__Secure-1PSIDTS", domain: ".google.com" },
|
|
],
|
|
"Log in to your Google account at gemini.google.com. Both __Secure-1PSID and __Secure-1PSIDTS cookies will be extracted.",
|
|
{ cookieDomain: ".google.com" }
|
|
),
|
|
|
|
// ── Grok Web ──────────────────────────────────────────────
|
|
config(
|
|
"grok-web",
|
|
"Grok Web",
|
|
"https://grok.com/login",
|
|
"https://grok.com",
|
|
[{ type: "cookie", name: "sso", domain: ".grok.com" }],
|
|
"Log in to your xAI account at grok.com. The sso session cookie will be extracted."
|
|
),
|
|
|
|
// ── Perplexity Web ────────────────────────────────────────
|
|
config(
|
|
"perplexity-web",
|
|
"Perplexity Web",
|
|
"https://www.perplexity.ai/login",
|
|
"https://www.perplexity.ai",
|
|
[
|
|
{ type: "cookie", name: "__Secure-next-auth.session-token", domain: ".perplexity.ai" },
|
|
],
|
|
"Log in to Perplexity. The __Secure-next-auth.session-token cookie will be extracted.",
|
|
{ cookieDomain: ".perplexity.ai" }
|
|
),
|
|
|
|
// ── DeepSeek Web ──────────────────────────────────────────
|
|
config(
|
|
"deepseek-web",
|
|
"DeepSeek Web",
|
|
"https://chat.deepseek.com/sign_in",
|
|
"https://chat.deepseek.com",
|
|
[
|
|
{ type: "cookie", name: "user-token", domain: ".deepseek.com" },
|
|
{ type: "localStorage", key: "userToken" },
|
|
],
|
|
"Log in to DeepSeek at chat.deepseek.com. The user-token cookie will be extracted.",
|
|
{ cookieDomain: ".deepseek.com" }
|
|
),
|
|
|
|
// ── Qwen Web ──────────────────────────────────────────────
|
|
config(
|
|
"qwen-web",
|
|
"Qwen Web (Tongyi)",
|
|
"https://chat.qwen.ai/",
|
|
"https://chat.qwen.ai",
|
|
[
|
|
{ type: "cookie", name: "XSRF_TOKEN", domain: ".chat.qwen.ai" },
|
|
{ type: "localStorage", key: "token" },
|
|
],
|
|
"Log in to Qwen at chat.qwen.ai using your Alibaba account. The session token will be extracted.",
|
|
{ cookieDomain: ".chat.qwen.ai" }
|
|
),
|
|
|
|
// ── Kimi Web ──────────────────────────────────────────────
|
|
config(
|
|
"kimi-web",
|
|
"Kimi (Moonshot)",
|
|
"https://kimi.moonshot.cn/",
|
|
"https://kimi.moonshot.cn",
|
|
[
|
|
{ type: "cookie", name: "kimi_token", domain: ".kimi.moonshot.cn" },
|
|
{ type: "localStorage", key: "kimi_token" },
|
|
],
|
|
"Log in to Kimi at kimi.moonshot.cn via phone/WeChat. The session token will be extracted.",
|
|
{ cookieDomain: ".kimi.moonshot.cn" }
|
|
),
|
|
|
|
// ── Blackbox Web ──────────────────────────────────────────
|
|
config(
|
|
"blackbox-web",
|
|
"Blackbox AI",
|
|
"https://app.blackbox.ai/login",
|
|
"https://app.blackbox.ai",
|
|
[
|
|
{ type: "cookie", name: "connect.sid", domain: ".blackbox.ai" },
|
|
{ type: "localStorage", key: "token" },
|
|
],
|
|
"Log in to Blackbox AI at app.blackbox.ai using Google/GitHub. The session cookie will be extracted.",
|
|
{ cookieDomain: ".blackbox.ai" }
|
|
),
|
|
|
|
// ── Poe Web ───────────────────────────────────────────────
|
|
config(
|
|
"poe-web",
|
|
"Poe (Quora)",
|
|
"https://poe.com/login",
|
|
"https://poe.com",
|
|
[
|
|
{ type: "cookie", name: "p-b", domain: ".poe.com" },
|
|
],
|
|
"Log in to Poe at poe.com. The session cookie will be extracted.",
|
|
{ cookieDomain: ".poe.com" }
|
|
),
|
|
|
|
// ── Copilot Web ───────────────────────────────────────────
|
|
config(
|
|
"copilot-web",
|
|
"Microsoft Copilot",
|
|
"https://copilot.microsoft.com/",
|
|
"https://copilot.microsoft.com",
|
|
[
|
|
{ type: "cookie", name: "RPSCAuth", domain: ".microsoft.com" },
|
|
],
|
|
"Log in with your Microsoft account at copilot.microsoft.com. The session auth cookie will be extracted.",
|
|
{ cookieDomain: ".microsoft.com" }
|
|
),
|
|
|
|
// ── DuckDuckGo Web ────────────────────────────────────────
|
|
config(
|
|
"duckduckgo-web",
|
|
"DuckDuckGo AI Chat",
|
|
"https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1",
|
|
"https://duckduckgo.com",
|
|
[
|
|
{ type: "cookie", name: "duckai", domain: ".duckduckgo.com" },
|
|
],
|
|
"Open DuckDuckGo AI Chat. Some models may require a free account. The duckai cookie will be extracted.",
|
|
{
|
|
cookieDomain: ".duckduckgo.com",
|
|
pollingConfig: QUICK_POLLING,
|
|
}
|
|
),
|
|
|
|
// ── DouBao Web ────────────────────────────────────────────
|
|
config(
|
|
"doubao-web",
|
|
"DouBao (ByteDance)",
|
|
"https://www.doubao.com/",
|
|
"https://www.doubao.com",
|
|
[
|
|
{ type: "cookie", name: "sessionid", domain: ".doubao.com" },
|
|
],
|
|
"Log in to DouBao at doubao.com with your ByteDance account. The sessionid will be extracted.",
|
|
{ cookieDomain: ".doubao.com" }
|
|
),
|
|
|
|
// ── T3 Chat Web ───────────────────────────────────────────
|
|
config(
|
|
"t3-chat-web",
|
|
"T3 Chat",
|
|
"https://t3.chat/login",
|
|
"https://t3.chat",
|
|
[
|
|
{ type: "localStorage", key: "token" },
|
|
],
|
|
"Log in to T3 Chat at t3.chat using Google/GitHub. The token from localStorage will be extracted.",
|
|
{ pollingConfig: QUICK_POLLING }
|
|
),
|
|
|
|
// ── Venice Web ────────────────────────────────────────────
|
|
config(
|
|
"venice-web",
|
|
"Venice AI",
|
|
"https://venice.ai/login",
|
|
"https://venice.ai",
|
|
[
|
|
{ type: "cookie", name: "venice_session", domain: ".venice.ai" },
|
|
{ type: "localStorage", key: "token" },
|
|
],
|
|
"Log in to Venice AI at venice.ai. The session cookie will be extracted.",
|
|
{ cookieDomain: ".venice.ai" }
|
|
),
|
|
|
|
// ── v0 Dev Web ────────────────────────────────────────────
|
|
config(
|
|
"v0-vercel-web",
|
|
"v0 by Vercel",
|
|
"https://v0.dev/login",
|
|
"https://v0.dev",
|
|
[
|
|
{ type: "cookie", name: "__Secure-next-auth.session-token", domain: ".v0.dev" },
|
|
],
|
|
"Log in to v0.dev with your Vercel/Google/GitHub account. The session cookie will be extracted.",
|
|
{ cookieDomain: ".v0.dev" }
|
|
),
|
|
|
|
// ── Muse / Spark Web ──────────────────────────────────────
|
|
config(
|
|
"muse-spark-web",
|
|
"Meta AI (Muse)",
|
|
"https://www.meta.ai/",
|
|
"https://www.meta.ai",
|
|
[
|
|
{ type: "cookie", name: "session", domain: ".meta.ai" },
|
|
],
|
|
"Log in to Meta AI at meta.ai with your Facebook/Instagram account. The session cookie will be extracted.",
|
|
{ cookieDomain: ".meta.ai" }
|
|
),
|
|
|
|
// ── Adapta Web ────────────────────────────────────────────
|
|
config(
|
|
"adapta-web",
|
|
"Adapta AI",
|
|
"https://agent.adapta.one/login",
|
|
"https://agent.adapta.one",
|
|
[
|
|
{ type: "cookie", name: "__session", domain: ".adapta.one" },
|
|
],
|
|
"Log in to Adapta at agent.adapta.one. The session token will be extracted.",
|
|
{ cookieDomain: ".adapta.one" }
|
|
),
|
|
|
|
// ── VeoAI Free Web ────────────────────────────────────────
|
|
config(
|
|
"veoaifree-web",
|
|
"VeoAI Free",
|
|
"https://veoaifree.com/",
|
|
"https://veoaifree.com",
|
|
[
|
|
{ type: "cookie", name: "wordpress_logged_in", domain: ".veoaifree.com" },
|
|
],
|
|
"Log in to VeoAI Free at veoaifree.com. The WordPress session cookie will be extracted.",
|
|
{
|
|
cookieDomain: ".veoaifree.com",
|
|
pollingConfig: QUICK_POLLING,
|
|
}
|
|
),
|
|
|
|
// ── Missing Provider: ChatGLM (Zhipu) ──────────────────────
|
|
config(
|
|
"chatglm-web",
|
|
"ChatGLM (Zhipu AI)",
|
|
"https://chatglm.cn/",
|
|
"https://chatglm.cn",
|
|
[
|
|
{ type: "cookie", name: "chatglm_session", domain: ".chatglm.cn" },
|
|
{ type: "localStorage", key: "token" },
|
|
],
|
|
"Log in to ChatGLM at chatglm.cn with your phone number. The session token will be extracted.",
|
|
{ cookieDomain: ".chatglm.cn" }
|
|
),
|
|
|
|
// ── Missing Provider: Xiaomi MiMo ──────────────────────────
|
|
config(
|
|
"xiaomimimo-web",
|
|
"Xiaomi MiMo AI Studio",
|
|
"https://aistudio.xiaomimimo.com/login",
|
|
"https://aistudio.xiaomimimo.com",
|
|
[
|
|
{ type: "cookie", name: "session", domain: ".xiaomimimo.com" },
|
|
{ type: "localStorage", key: "access_token" },
|
|
],
|
|
"Log in to Xiaomi MiMo AI Studio at aistudio.xiaomimimo.com. The session token will be extracted.",
|
|
{ cookieDomain: ".xiaomimimo.com" }
|
|
),
|
|
|
|
// ── Missing Provider: Manus ────────────────────────────────
|
|
config(
|
|
"manus-web",
|
|
"Manus AI",
|
|
"https://manus.im/login",
|
|
"https://manus.im",
|
|
[
|
|
{ type: "cookie", name: "manus_session", domain: ".manus.im" },
|
|
{ type: "localStorage", key: "auth_token" },
|
|
],
|
|
"Log in to Manus at manus.im. The session cookie will be extracted.",
|
|
{ cookieDomain: ".manus.im" }
|
|
),
|
|
];
|
|
|
|
// ─── Registry ───────────────────────────────────────────────────────────────
|
|
|
|
const CONFIG_MAP = new Map<string, TokenExtractionConfig>();
|
|
|
|
for (const cfg of RAW_CONFIGS) {
|
|
CONFIG_MAP.set(cfg.providerId, cfg);
|
|
}
|
|
|
|
/** Get extraction config for a specific provider */
|
|
export function getExtractionConfig(providerId: string): TokenExtractionConfig | undefined {
|
|
return CONFIG_MAP.get(providerId);
|
|
}
|
|
|
|
/** List all registered extraction configs */
|
|
export function listExtractionConfigs(): TokenExtractionConfig[] {
|
|
return [...RAW_CONFIGS];
|
|
}
|
|
|
|
/** The shared config map — used by LoginManager and InAppLoginService */
|
|
export const TOKEN_EXTRACTION_CONFIGS = CONFIG_MAP;
|