mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-18 21:22:28 +03:00
* Bypass proxy compaction for native Codex context
* Add native ChatGPT Web provider pipeline
* Add managed browser and tunnel deployment
* Add ChatGPT Web setup and doctor UI
* Document and test ChatGPT Web integration
* fix(security): register chatgpt-web-codex-doctor in LOCAL_ONLY_API_PATTERNS
The diagnostic route under /api/providers/{id}/chatgpt-web-codex-doctor
was not registered in the spawn-capable route guard. Adding it for
parity with the existing /login pattern.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* fix(providers): route chatgpt-web-codex admin routes through a service boundary
The provider CRUD/doctor routes imported chatgpt-web-codex helpers
(finalizeValidatedChatGptWebCodexSecrets, encode/decodeChatGptWebCodexSecrets,
getChatGptWebCodexDoctorStatus) directly from open-sse/executors/**, which
no-restricted-imports (EXECUTOR_IMPORT_RESTRICTION) forbids for src/app/**
files — executor implementations must stay behind an open-sse handler or
service boundary.
Add open-sse/services/chatgptWebCodexAdmin.ts as a thin re-export boundary
(mirroring the existing tokenRefresh.ts re-export pattern) and import from
there instead. No behavior change.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
539 lines
21 KiB
TypeScript
539 lines
21 KiB
TypeScript
/**
|
||
* Provider catalog data — extracted from providers.ts (god-file decomposition).
|
||
* Pure data literal; re-exported by the providers.ts barrel. No behavior change.
|
||
*/
|
||
export const WEB_COOKIE_PROVIDERS = {
|
||
"chatgpt-web-codex": {
|
||
id: "chatgpt-web-codex",
|
||
alias: "cgpt-codex",
|
||
name: "ChatGPT Web (Codex)",
|
||
icon: "terminal",
|
||
color: "#10A37F",
|
||
textIcon: "CC",
|
||
website: "https://chatgpt.com",
|
||
authHint:
|
||
"Paste the full ChatGPT Cookie header. OmniRoute verifies it in an isolated headless browser profile.",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
toolCalling: "native",
|
||
},
|
||
"chatgpt-web": {
|
||
id: "chatgpt-web",
|
||
alias: "cgpt-web",
|
||
name: "ChatGPT Web (Plus/Pro)",
|
||
icon: "auto_awesome",
|
||
color: "#10A37F",
|
||
textIcon: "CG",
|
||
website: "https://chatgpt.com",
|
||
authHint: "Paste your __Secure-next-auth.session-token cookie value from chatgpt.com",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
toolCalling: "emulated",
|
||
},
|
||
"grok-web": {
|
||
id: "grok-web",
|
||
alias: "gw",
|
||
name: "Grok Web (Subscription)",
|
||
icon: "auto_awesome",
|
||
color: "#1DA1F2",
|
||
textIcon: "GW",
|
||
website: "https://grok.com",
|
||
authHint:
|
||
"Paste the full grok.com cookie line from DevTools → Application → Cookies. Include both `sso` and `sso-rw` (e.g. `sso=...; sso-rw=...`) — Grok's anti-bot rejects `sso` on its own.",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"gemini-web": {
|
||
id: "gemini-web",
|
||
alias: "gweb",
|
||
name: "Gemini Web (Free)",
|
||
icon: "auto_awesome",
|
||
color: "#4285F4",
|
||
textIcon: "GWeb",
|
||
website: "https://gemini.google.com",
|
||
authHint:
|
||
"Paste your __Secure-1PSID cookie value from gemini.google.com. Optionally add __Secure-1PSIDTS separated by semicolon.",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
// #7286 Level 2: tools[] is prompt-emulated via webTools.ts (parseToolCallsFromText).
|
||
toolCalling: "emulated",
|
||
},
|
||
"perplexity-web": {
|
||
id: "perplexity-web",
|
||
alias: "pplx-web",
|
||
name: "Perplexity Web (Pro/Max)",
|
||
icon: "search",
|
||
color: "#20808D",
|
||
textIcon: "PW",
|
||
website: "https://www.perplexity.ai",
|
||
authHint: "Paste your __Secure-next-auth.session-token cookie value from perplexity.ai",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
toolCalling: "emulated",
|
||
},
|
||
"blackbox-web": {
|
||
id: "blackbox-web",
|
||
alias: "bb-web",
|
||
name: "Blackbox Web (Subscription)",
|
||
icon: "view_in_ar",
|
||
color: "#1A1A2E",
|
||
textIcon: "BW",
|
||
website: "https://app.blackbox.ai",
|
||
authHint:
|
||
"Paste your __Secure-authjs.session-token value or full cookie header from app.blackbox.ai",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
toolCalling: "emulated",
|
||
},
|
||
"muse-spark-web": {
|
||
id: "muse-spark-web",
|
||
alias: "ms-web",
|
||
name: "Muse Spark Web (Meta AI)",
|
||
icon: "auto_awesome",
|
||
color: "#0866FF",
|
||
textIcon: "MS",
|
||
website: "https://www.meta.ai",
|
||
hasFree: true,
|
||
freeNote: "Free with login — Meta AI platform with Llama models.",
|
||
authHint:
|
||
"Paste your ecto_1_sess cookie AND the ecto1:... WS auth token from meta.ai. " +
|
||
"Capture the ecto1: token in DevTools → Network → WS → the clippy request's Authorization query param. " +
|
||
"Example: ecto_1_sess=4240a308...NVDg0; ecto1:ABCD...",
|
||
toolCalling: "emulated",
|
||
},
|
||
"claude-web": {
|
||
id: "claude-web",
|
||
alias: "cw",
|
||
name: "Claude Web",
|
||
icon: "auto_awesome",
|
||
color: "#D97757",
|
||
textIcon: "CW",
|
||
website: "https://claude.ai",
|
||
authHint: "Paste your session cookie from claude.ai",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
// #7286 Level 3 (deferred): still silently drops tools[] — getDefaultTools()
|
||
// is a fixed Claude.ai backend contract; needs an emulate-vs-native decision.
|
||
toolCalling: "none",
|
||
},
|
||
"deepseek-web": {
|
||
id: "deepseek-web",
|
||
alias: "ds-web",
|
||
name: "DeepSeek Web",
|
||
icon: "auto_awesome",
|
||
color: "#4D6BFE",
|
||
textIcon: "DS",
|
||
website: "https://chat.deepseek.com",
|
||
authHint:
|
||
"Paste your userToken from chat.deepseek.com — DevTools → Application → Local Storage → userToken",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
toolCalling: "emulated",
|
||
},
|
||
"copilot-web": {
|
||
id: "copilot-web",
|
||
alias: "copilot",
|
||
name: "Microsoft Copilot Web",
|
||
icon: "auto_awesome",
|
||
color: "#0078D4",
|
||
textIcon: "CP",
|
||
website: "https://copilot.microsoft.com",
|
||
authHint:
|
||
"Paste the access_token from an authenticated copilot.microsoft.com request (DevTools → Network → Authorization), or export a HAR while logged in",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"copilot-m365-web": {
|
||
id: "copilot-m365-web",
|
||
alias: "m365copilot",
|
||
name: "Microsoft 365 Copilot (BizChat)",
|
||
icon: "business_center",
|
||
color: "#0078D4",
|
||
textIcon: "M365",
|
||
website: "https://m365.cloud.microsoft/chat",
|
||
authHint:
|
||
"Sign in at m365.cloud.microsoft/chat, then open DevTools → Network → filter 'WS' → click the Chathub WebSocket connection. Copy both the access_token query parameter AND the account-specific Chathub path segment from its request URL (wss://…/Chathub/<path>?…&access_token=…). It is NOT an Authorization: Bearer header on an XHR/Fetch request. The token is short-lived; this is an unofficial integration.",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"microsoft-designer-web": {
|
||
id: "microsoft-designer-web",
|
||
alias: "msdesigner",
|
||
name: "Microsoft Designer (Image Generation)",
|
||
icon: "auto_awesome",
|
||
color: "#0078D4",
|
||
textIcon: "MSD",
|
||
website: "https://designer.microsoft.com",
|
||
authHint:
|
||
"Sign in at designer.microsoft.com, then open DevTools → Network, generate an image, and find the request to DallE.ashx?action=GetDallEImagesCogSci. Copy the value of its Authorization: Bearer header (the access_token — no 'Bearer ' prefix). The token is short-lived; this is an unofficial, reverse-engineered integration.",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"t3-web": {
|
||
id: "t3-web",
|
||
alias: "t3chat",
|
||
name: "t3.chat (Pro/Free)",
|
||
icon: "auto_awesome",
|
||
color: "#7C3AED",
|
||
textIcon: "T3",
|
||
website: "https://t3.chat",
|
||
hasFree: true,
|
||
freeNote: "Free tier gives limited model access. Pro ($8/month) unlocks 50+ models.",
|
||
authHint:
|
||
"Open t3.chat in your browser, log in, then open DevTools → Application → Local Storage → https://t3.chat. " +
|
||
"Copy the value of 'convex-session-id'. Also open DevTools → Network, copy the Cookie header from any request. " +
|
||
"Paste both values here. See provider setup docs for a step-by-step guide.",
|
||
toolCalling: "emulated",
|
||
},
|
||
"inner-ai": {
|
||
id: "inner-ai",
|
||
alias: "in-ai",
|
||
name: "Inner.ai (Subscription)",
|
||
icon: "auto_awesome",
|
||
color: "#1A56DB",
|
||
textIcon: "IA",
|
||
website: "https://app.innerai.com",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
authHint:
|
||
"Paste your token cookie and email separated by a space: open DevTools → Application → Cookies → .innerai.com, copy the token value, then append a space and your Inner.ai login email. Example: eyJhbG... user@example.com",
|
||
toolCalling: "emulated",
|
||
},
|
||
"adapta-web": {
|
||
id: "adapta-web",
|
||
alias: "adp-web",
|
||
name: "Adapta.org (Adapta One Web)",
|
||
icon: "auto_awesome",
|
||
color: "#6E3AD3",
|
||
textIcon: "AW",
|
||
website: "https://agent.adapta.one",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
authHint:
|
||
"Paste your __client cookie value from .clerk.agent.adapta.one (DevTools → Application → Cookies)",
|
||
toolCalling: "emulated",
|
||
},
|
||
lmarena: {
|
||
// Wire id stays `lmarena` for DB/combo/model-prefix back-compat.
|
||
// Product rebranded LMArena → Arena (arena.ai) in Jan 2026.
|
||
id: "lmarena",
|
||
alias: "lma",
|
||
name: "Arena (Free)",
|
||
icon: "auto_awesome",
|
||
color: "#FF6B6B",
|
||
textIcon: "AR",
|
||
website: "https://arena.ai",
|
||
hasFree: true,
|
||
freeNote:
|
||
"Free model comparison platform (formerly LMArena) at arena.ai — Direct-chat catalog of chat models (GPT, Claude, Gemini, Llama, …). No subscription required.",
|
||
authHint:
|
||
"Paste the full Cookie header from arena.ai (DevTools → Network → request → Cookie). Include arena-auth-prod-v1.0/.1… and cf_clearance/__cf_bm when present. OmniRoute uses Chrome TLS impersonation; if Arena still 403s, set providerSpecificData.recaptchaV3Token from a live browser session.",
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"yuanbao-web": {
|
||
id: "yuanbao-web",
|
||
alias: "ybw",
|
||
name: "Tencent Yuanbao (Free)",
|
||
icon: "auto_awesome",
|
||
color: "#0052D9",
|
||
textIcon: "YB",
|
||
website: "https://yuanbao.tencent.com",
|
||
hasFree: true,
|
||
freeNote:
|
||
"Free consumer web session — DeepSeek V3/R1 and Hunyuan / Hunyuan-T1, optional web search. No subscription required. Rate limits apply.",
|
||
authHint:
|
||
"Log in to yuanbao.tencent.com, then paste the full Cookie header (DevTools → Network → any /api request → Request Headers → Cookie). It must contain hy_user and hy_token.",
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
huggingchat: {
|
||
id: "huggingchat",
|
||
// "hc" belongs to the hackclub provider; huggingchat uses its own id as alias.
|
||
alias: "huggingchat",
|
||
name: "HuggingChat (Free)",
|
||
icon: "auto_awesome",
|
||
color: "#FFD21E",
|
||
textIcon: "HC",
|
||
website: "https://huggingface.co/chat",
|
||
hasFree: true,
|
||
freeNote: "Free LLM chat — no subscription required. Rate limits apply.",
|
||
authHint:
|
||
"Paste the full Cookie header from huggingface.co/chat (DevTools → Network → /chat/conversation → Request Headers → Cookie). It should include hf-chat and may also include token / aws-waf-token.",
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"poe-web": {
|
||
id: "poe-web",
|
||
alias: "poe",
|
||
name: "Poe Web (Subscription)",
|
||
icon: "auto_awesome",
|
||
color: "#6C3AED",
|
||
textIcon: "PW",
|
||
website: "https://poe.com",
|
||
authHint: "Paste your p-b cookie value from poe.com (DevTools → Application → Cookies → p-b)",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"venice-web": {
|
||
id: "venice-web",
|
||
alias: "ven",
|
||
name: "Venice Web (Privacy)",
|
||
icon: "auto_awesome",
|
||
color: "#22C55E",
|
||
textIcon: "VW",
|
||
website: "https://venice.ai",
|
||
authHint: "Paste your session cookie from venice.ai (DevTools → Application → Cookies)",
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"v0-vercel-web": {
|
||
id: "v0-vercel-web",
|
||
// #6343: was "v0", colliding with the unrelated "v0-vercel" API-key provider's
|
||
// alias. Aliases resolve 1:1 to a provider id, so the dashboard's model-string
|
||
// routing always picked v0-vercel, silently hiding this provider's own
|
||
// credentials. Follows the established secondary-web-variant convention (see
|
||
// kimi-web / qwen-web / huggingchat in tests/unit/provider-alias-uniqueness.test.ts):
|
||
// the web/secondary variant uses its own id as alias instead of a short prefix.
|
||
alias: "v0-vercel-web",
|
||
name: "v0 Vercel Web (Code Gen)",
|
||
icon: "auto_awesome",
|
||
color: "#000000",
|
||
textIcon: "V0",
|
||
website: "https://v0.dev",
|
||
authHint: "Paste your session cookie from v0.dev (DevTools → Application → Cookies)",
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"kimi-web": {
|
||
id: "kimi-web",
|
||
// Legacy "kimi" API provider keeps the short alias; web variant uses its own id.
|
||
alias: "kimi-web",
|
||
name: "Kimi Web",
|
||
icon: "auto_awesome",
|
||
color: "#2563EB",
|
||
textIcon: "KW",
|
||
// Kimi official-partnership aff link (2026-07) — the "Kimi Coding Plan"
|
||
// tracking link (same origin as the plain www.kimi.com login flow below,
|
||
// so the "Open {host}" credential guide in WebSessionCredentialGuide.tsx /
|
||
// AddApiKeyModal.tsx is unaffected: origin, not path, decides localStorage
|
||
// access). Was `https://www.kimi.com` (no aff attribution).
|
||
website: "https://www.kimi.com/code?aff=omniroute",
|
||
authHint:
|
||
"Paste access_token from www.kimi.com DevTools → Application → Local Storage. A legacy kimi-auth cookie is also accepted.",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"doubao-web": {
|
||
id: "doubao-web",
|
||
alias: "db",
|
||
name: "Dola Web (ByteDance)",
|
||
icon: "auto_awesome",
|
||
color: "#3B82F6",
|
||
textIcon: "DA",
|
||
website: "https://www.dola.com",
|
||
authHint:
|
||
"Paste the full Cookie header from www.dola.com. It should include sessionid, ttwid, and s_v_web_id. If s_v_web_id is unavailable, fp=verify_... from a chat/completion request URL can be used as a fallback.",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"hailuo-web": {
|
||
id: "hailuo-web",
|
||
// Distinct alias: avoid colliding with the existing API-key "minimax"/
|
||
// "minimax-cn" providers (src/shared/constants/providers/apikey/regional.ts).
|
||
alias: "hailuo-web",
|
||
name: "Hailuo Web (MiniMax)",
|
||
icon: "auto_awesome",
|
||
color: "#5B21B6",
|
||
textIcon: "HL",
|
||
website: "https://hailuo.ai",
|
||
authHint:
|
||
"Open hailuo.ai, log in, then open DevTools → Application → Local Storage → copy the " +
|
||
'"_token" value. device_id/uuid fingerprint fields are derived automatically; if ' +
|
||
"requests fail, re-capture _token (sessions can expire).",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
"qwen-web": {
|
||
id: "qwen-web",
|
||
// The web variant uses its own id; the retired `qw` alias is not reassigned.
|
||
alias: "qwen-web",
|
||
name: "Qwen Web (Free)",
|
||
icon: "auto_awesome",
|
||
color: "#10B981",
|
||
textIcon: "QW",
|
||
website: "https://chat.qwen.ai",
|
||
hasFree: true,
|
||
freeNote: "Free — Qwen models via chat.qwen.ai with login token. No subscription required.",
|
||
authHint:
|
||
"Open chat.qwen.ai, log in, then open DevTools → Application → Local Storage → " +
|
||
'copy the "token" value (or use tongyi_sso_ticket cookie as Bearer token).',
|
||
toolCalling: "emulated",
|
||
},
|
||
"gemini-business": {
|
||
id: "gemini-business",
|
||
alias: "gembiz",
|
||
name: "Gemini Business (Enterprise)",
|
||
icon: "business_center",
|
||
color: "#4285F4",
|
||
textIcon: "GB",
|
||
website: "https://business.gemini.google",
|
||
hasFree: true,
|
||
freeNote:
|
||
"Free for Google Workspace enterprise accounts — enterprise Gemini models (Pro, Flash, image, video) via direct StreamGenerate HTTP API. No subscription required, just enterprise SSO.",
|
||
authHint:
|
||
"From your enterprise account: open business.gemini.google/home/cid/{your-cid}, then copy __Secure-1PSID and __Secure-1PSIDTS cookies from DevTools → Application → Cookies. Paste as a cookie header below.",
|
||
},
|
||
"zenmux-free": {
|
||
id: "zenmux-free",
|
||
alias: "zmf",
|
||
name: "ZenMux Free (Web)",
|
||
icon: "bolt",
|
||
color: "#667eea",
|
||
textIcon: "ZF",
|
||
website: "https://zenmux.ai",
|
||
hasFree: true,
|
||
freeNote:
|
||
"Free tier (5 Flows/5h, 38.64 Flows/week) — DeepSeek V3.2, GLM 4.7 Flash Free and more. No subscription required.",
|
||
authHint:
|
||
"Login at zenmux.ai, then export all cookies using EditThisCookie or Cookie-Editor and paste the full Cookie header string here. Refresh every ~30 days.",
|
||
},
|
||
"tinycms-web": {
|
||
id: "tinycms-web",
|
||
alias: "tcw",
|
||
name: "TinyCMS Web (Free/Sub)",
|
||
icon: "layers",
|
||
color: "#ED8936",
|
||
textIcon: "TC",
|
||
website: "https://site.tinycms.xyz",
|
||
hasFree: true,
|
||
freeNote:
|
||
"Free tier has access to GPT 5.4, Gemini 3.5, and Grok 4.20 models. No login required. Subscription grants 300 requests/day for advanced models.",
|
||
authHint:
|
||
"Go to site.tinycms.xyz, open DevTools → Application → Local Storage, copy the value of 'app-config-uuid' (starts with 'R'), and paste it here.",
|
||
},
|
||
"zai-web": {
|
||
id: "zai-web",
|
||
alias: "zw",
|
||
name: "Z.ai Web (Free)",
|
||
icon: "auto_awesome",
|
||
color: "#2563EB",
|
||
textIcon: "ZW",
|
||
website: "https://chat.z.ai",
|
||
hasFree: true,
|
||
freeNote:
|
||
"Free consumer web session — GLM chat models via chat.z.ai. Distinct from the API-key zai/glm providers. No subscription required.",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
authHint: "Paste the full Cookie header from chat.z.ai (must include the token=<JWT> cookie)",
|
||
},
|
||
promptql: {
|
||
id: "promptql",
|
||
alias: "pql",
|
||
name: "PromptQL (Unofficial/Experimental)",
|
||
icon: "auto_awesome",
|
||
color: "#5B21B6",
|
||
textIcon: "PQL",
|
||
website: "https://prompt.ql.app",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
authHint:
|
||
"Paste the Bearer JWT from prompt.ql.app DevTools → Network → graphql → Authorization (token only). Optional projectId + session Cookie for refresh.",
|
||
},
|
||
"notion-web": {
|
||
id: "notion-web",
|
||
alias: "nw",
|
||
name: "Notion AI Web (Unofficial/Experimental)",
|
||
icon: "auto_awesome",
|
||
color: "#000000",
|
||
textIcon: "NW",
|
||
website: "https://www.notion.so",
|
||
// #6758: Notion has no public inference API (see closed request #3272) — this
|
||
// reverse-engineers the same undocumented internal endpoint two independent
|
||
// open-source projects already use. Undocumented endpoints can change without
|
||
// notice; label clearly so operators understand the risk before pasting a
|
||
// session cookie of an account they already pay for.
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
authHint:
|
||
"Paste only the token_v2 cookie VALUE from app.notion.com (DevTools → Application → Cookies → token_v2). " +
|
||
"Do not paste token_v2= or the full Cookie header. Workspace is auto-detected; space_id / notion_user_id are optional.",
|
||
},
|
||
"adobe-firefly": {
|
||
id: "adobe-firefly",
|
||
alias: "firefly",
|
||
name: "Adobe Firefly (Image/Video)",
|
||
icon: "auto_awesome",
|
||
color: "#EB1000",
|
||
textIcon: "FF",
|
||
website: "https://firefly.adobe.com",
|
||
authHint:
|
||
"RECOMMENDED: firefly.adobe.com signed-in → F12 → Network → click firefly-3p.ff.adobe.io (generate-async or models/discovery) → Request Headers → Authorization → copy the token AFTER 'Bearer ' (starts with eyJ…). Cookie-only from firefly.adobe.com mints a GUEST token → 401/403; only multi-domain IMS cookies (adobelogin.com) or that Bearer JWT work. Unofficial/experimental media + Limits.",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
},
|
||
hyperagent: {
|
||
id: "hyperagent",
|
||
alias: "ha",
|
||
name: "HyperAgent (Unofficial/Experimental)",
|
||
icon: "auto_awesome",
|
||
color: "#6C5CE7",
|
||
textIcon: "HA",
|
||
website: "https://hyperagent.com",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
authHint:
|
||
"Paste the full Cookie header from hyperagent.com (DevTools → Network → any request → Request Headers → Cookie). Session cookies power chat + billing usage.",
|
||
},
|
||
"conol-web": {
|
||
id: "conol-web",
|
||
alias: "cnl",
|
||
name: "Conol (Unofficial/Experimental)",
|
||
icon: "auto_awesome",
|
||
color: "#F6C945",
|
||
textIcon: "CO",
|
||
website: "https://conol.ai",
|
||
subscriptionRisk: true,
|
||
riskNoticeVariant: "webCookie",
|
||
authHint:
|
||
"Use browser sign-in, or paste the full Cookie header from conol.ai. The __Secure-better-auth.session_token cookie is required.",
|
||
},
|
||
};
|
||
|
||
/** Resolved public site for a web-session provider (href + display host). */
|
||
export interface WebProviderHostLink {
|
||
/** Full URL to open in a new tab (the provider's own `website`, or the origin
|
||
* derived from a registry baseUrl fallback). */
|
||
url: string;
|
||
/** Display host, e.g. `chatgpt.com` — used for the "Open ‹host› →" label. */
|
||
host: string;
|
||
}
|
||
|
||
/**
|
||
* Resolve the public website + display host for a web-session provider so the
|
||
* "Add session cookie" modal can render a prominent "Open ‹host› →" link.
|
||
*
|
||
* Primary source: `WEB_COOKIE_PROVIDERS[providerId].website`. When an entry has
|
||
* no `website` (or the provider is not in the catalog but the caller knows it is
|
||
* a web-session provider), the caller may pass its registry `baseUrl` as a
|
||
* fallback — only the origin is kept from it.
|
||
*
|
||
* Pure and React-free (unit-testable). Web-ness gating is the caller's
|
||
* responsibility: with no `fallbackBaseUrl`, a provider absent from
|
||
* `WEB_COOKIE_PROVIDERS` resolves to `null`.
|
||
*/
|
||
export function resolveWebProviderHost(
|
||
providerId: string | null | undefined,
|
||
fallbackBaseUrl?: string | null
|
||
): WebProviderHostLink | null {
|
||
if (!providerId) return null;
|
||
const entry = (WEB_COOKIE_PROVIDERS as Record<string, { website?: string }>)[providerId];
|
||
const website = entry?.website?.trim();
|
||
const fallback = fallbackBaseUrl?.trim();
|
||
const source = website || fallback;
|
||
if (!source) return null;
|
||
try {
|
||
const parsed = new URL(source);
|
||
// Keep the website URL verbatim (it may point at a specific path like
|
||
// `/chat`); for a registry baseUrl fallback, keep only the origin.
|
||
return { url: website ? source : parsed.origin, host: parsed.host };
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|