blackriver-gateway 3.8.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2070 -0
- package/@omniroute/opencode-plugin/LICENSE +21 -0
- package/@omniroute/opencode-plugin/README.md +296 -0
- package/@omniroute/opencode-plugin/package-lock.json +1930 -0
- package/@omniroute/opencode-plugin/package.json +73 -0
- package/@omniroute/opencode-plugin/src/index.ts +4685 -0
- package/@omniroute/opencode-plugin/src/logger.ts +74 -0
- package/@omniroute/opencode-plugin/src/naming.ts +301 -0
- package/@omniroute/opencode-plugin/tsconfig.json +20 -0
- package/@omniroute/opencode-plugin/tsup.config.ts +20 -0
- package/@omniroute/opencode-provider/LICENSE +21 -0
- package/@omniroute/opencode-provider/README.md +156 -0
- package/@omniroute/opencode-provider/package-lock.json +1514 -0
- package/@omniroute/opencode-provider/package.json +63 -0
- package/@omniroute/opencode-provider/src/index.ts +908 -0
- package/@omniroute/opencode-provider/tsconfig.json +20 -0
- package/@omniroute/opencode-provider/tsup.config.ts +18 -0
- package/LICENSE +22 -0
- package/README.md +1265 -0
- package/bin/_ops-common.sh +70 -0
- package/bin/cli/CONVENTIONS.md +224 -0
- package/bin/cli/README.md +146 -0
- package/bin/cli/api-commands/agent-skills.mjs +70 -0
- package/bin/cli/api-commands/agentbridge.mjs +155 -0
- package/bin/cli/api-commands/api-keys.mjs +42 -0
- package/bin/cli/api-commands/audio.mjs +40 -0
- package/bin/cli/api-commands/chat.mjs +58 -0
- package/bin/cli/api-commands/cli-tools.mjs +361 -0
- package/bin/cli/api-commands/cloud.mjs +81 -0
- package/bin/cli/api-commands/combos.mjs +69 -0
- package/bin/cli/api-commands/compression.mjs +128 -0
- package/bin/cli/api-commands/embedded-services.mjs +202 -0
- package/bin/cli/api-commands/embeddings.mjs +42 -0
- package/bin/cli/api-commands/fallback.mjs +49 -0
- package/bin/cli/api-commands/images.mjs +42 -0
- package/bin/cli/api-commands/memory.mjs +251 -0
- package/bin/cli/api-commands/messages.mjs +40 -0
- package/bin/cli/api-commands/models.mjs +89 -0
- package/bin/cli/api-commands/moderations.mjs +24 -0
- package/bin/cli/api-commands/oauth.mjs +114 -0
- package/bin/cli/api-commands/playground.mjs +83 -0
- package/bin/cli/api-commands/pricing.mjs +44 -0
- package/bin/cli/api-commands/provider-nodes.mjs +62 -0
- package/bin/cli/api-commands/providers.mjs +148 -0
- package/bin/cli/api-commands/quota.mjs +154 -0
- package/bin/cli/api-commands/registry.mjs +75 -0
- package/bin/cli/api-commands/rerank.mjs +24 -0
- package/bin/cli/api-commands/responses.mjs +24 -0
- package/bin/cli/api-commands/settings.mjs +228 -0
- package/bin/cli/api-commands/system.mjs +320 -0
- package/bin/cli/api-commands/telemetry.mjs +26 -0
- package/bin/cli/api-commands/traffic-inspector.mjs +307 -0
- package/bin/cli/api-commands/translator.mjs +65 -0
- package/bin/cli/api-commands/usage.mjs +117 -0
- package/bin/cli/api.mjs +269 -0
- package/bin/cli/commands/a2a.mjs +323 -0
- package/bin/cli/commands/audit.mjs +203 -0
- package/bin/cli/commands/autostart.mjs +57 -0
- package/bin/cli/commands/backup.mjs +469 -0
- package/bin/cli/commands/batches.mjs +235 -0
- package/bin/cli/commands/cache.mjs +102 -0
- package/bin/cli/commands/chat.mjs +162 -0
- package/bin/cli/commands/cloud.mjs +233 -0
- package/bin/cli/commands/combo.mjs +361 -0
- package/bin/cli/commands/completion.mjs +346 -0
- package/bin/cli/commands/compression.mjs +247 -0
- package/bin/cli/commands/config.mjs +351 -0
- package/bin/cli/commands/configure.mjs +180 -0
- package/bin/cli/commands/connect.mjs +132 -0
- package/bin/cli/commands/context-eng.mjs +182 -0
- package/bin/cli/commands/contexts.mjs +271 -0
- package/bin/cli/commands/cost.mjs +144 -0
- package/bin/cli/commands/dashboard.mjs +65 -0
- package/bin/cli/commands/doctor.mjs +527 -0
- package/bin/cli/commands/env.mjs +100 -0
- package/bin/cli/commands/eval.mjs +278 -0
- package/bin/cli/commands/files.mjs +144 -0
- package/bin/cli/commands/health.mjs +123 -0
- package/bin/cli/commands/keys.mjs +599 -0
- package/bin/cli/commands/launch-codex.mjs +201 -0
- package/bin/cli/commands/launch.mjs +155 -0
- package/bin/cli/commands/login.mjs +192 -0
- package/bin/cli/commands/logs.mjs +183 -0
- package/bin/cli/commands/mcp.mjs +273 -0
- package/bin/cli/commands/memory.mjs +244 -0
- package/bin/cli/commands/models.mjs +92 -0
- package/bin/cli/commands/nodes.mjs +177 -0
- package/bin/cli/commands/oauth.mjs +258 -0
- package/bin/cli/commands/oneproxy.mjs +120 -0
- package/bin/cli/commands/open.mjs +75 -0
- package/bin/cli/commands/openapi.mjs +168 -0
- package/bin/cli/commands/plugin.mjs +219 -0
- package/bin/cli/commands/policy.mjs +216 -0
- package/bin/cli/commands/pricing.mjs +123 -0
- package/bin/cli/commands/provider-cmd.mjs +18 -0
- package/bin/cli/commands/providers.mjs +706 -0
- package/bin/cli/commands/quota.mjs +100 -0
- package/bin/cli/commands/redis.mjs +294 -0
- package/bin/cli/commands/registry.mjs +162 -0
- package/bin/cli/commands/repl.mjs +19 -0
- package/bin/cli/commands/reset-encrypted-columns.mjs +88 -0
- package/bin/cli/commands/resilience.mjs +208 -0
- package/bin/cli/commands/restart.mjs +25 -0
- package/bin/cli/commands/runtime.mjs +98 -0
- package/bin/cli/commands/serve.mjs +426 -0
- package/bin/cli/commands/sessions.mjs +108 -0
- package/bin/cli/commands/setup-aider.mjs +146 -0
- package/bin/cli/commands/setup-claude.mjs +219 -0
- package/bin/cli/commands/setup-cline.mjs +160 -0
- package/bin/cli/commands/setup-codex.mjs +387 -0
- package/bin/cli/commands/setup-continue.mjs +173 -0
- package/bin/cli/commands/setup-crush.mjs +148 -0
- package/bin/cli/commands/setup-cursor.mjs +108 -0
- package/bin/cli/commands/setup-goose.mjs +150 -0
- package/bin/cli/commands/setup-kilo.mjs +178 -0
- package/bin/cli/commands/setup-open-code.mjs +398 -0
- package/bin/cli/commands/setup-opencode.mjs +129 -0
- package/bin/cli/commands/setup-qwen.mjs +156 -0
- package/bin/cli/commands/setup-roo.mjs +172 -0
- package/bin/cli/commands/setup.mjs +211 -0
- package/bin/cli/commands/simulate.mjs +125 -0
- package/bin/cli/commands/skills.mjs +373 -0
- package/bin/cli/commands/status.mjs +95 -0
- package/bin/cli/commands/stop.mjs +96 -0
- package/bin/cli/commands/stream.mjs +166 -0
- package/bin/cli/commands/sync.mjs +230 -0
- package/bin/cli/commands/tags.mjs +116 -0
- package/bin/cli/commands/telemetry.mjs +74 -0
- package/bin/cli/commands/test-provider.mjs +242 -0
- package/bin/cli/commands/tokens.mjs +118 -0
- package/bin/cli/commands/translator.mjs +131 -0
- package/bin/cli/commands/tray.mjs +36 -0
- package/bin/cli/commands/tunnel.mjs +347 -0
- package/bin/cli/commands/update.mjs +194 -0
- package/bin/cli/commands/usage.mjs +331 -0
- package/bin/cli/commands/webhooks.mjs +196 -0
- package/bin/cli/contexts.mjs +60 -0
- package/bin/cli/data-dir.mjs +59 -0
- package/bin/cli/encryption.mjs +67 -0
- package/bin/cli/i18n.mjs +106 -0
- package/bin/cli/io.mjs +83 -0
- package/bin/cli/locales/ar.json +32 -0
- package/bin/cli/locales/az.json +32 -0
- package/bin/cli/locales/bg.json +32 -0
- package/bin/cli/locales/bn.json +5 -0
- package/bin/cli/locales/cs.json +32 -0
- package/bin/cli/locales/da.json +32 -0
- package/bin/cli/locales/de.json +32 -0
- package/bin/cli/locales/en.json +1289 -0
- package/bin/cli/locales/es.json +32 -0
- package/bin/cli/locales/fa.json +32 -0
- package/bin/cli/locales/fi.json +32 -0
- package/bin/cli/locales/fr.json +32 -0
- package/bin/cli/locales/gu.json +5 -0
- package/bin/cli/locales/he.json +5 -0
- package/bin/cli/locales/hi.json +32 -0
- package/bin/cli/locales/hu.json +32 -0
- package/bin/cli/locales/id.json +32 -0
- package/bin/cli/locales/in.json +5 -0
- package/bin/cli/locales/it.json +32 -0
- package/bin/cli/locales/ja.json +32 -0
- package/bin/cli/locales/ko.json +32 -0
- package/bin/cli/locales/mr.json +5 -0
- package/bin/cli/locales/ms.json +5 -0
- package/bin/cli/locales/nl.json +32 -0
- package/bin/cli/locales/no.json +32 -0
- package/bin/cli/locales/phi.json +5 -0
- package/bin/cli/locales/pl.json +32 -0
- package/bin/cli/locales/pt-BR.json +1286 -0
- package/bin/cli/locales/pt.json +32 -0
- package/bin/cli/locales/ro.json +32 -0
- package/bin/cli/locales/ru.json +32 -0
- package/bin/cli/locales/sk.json +32 -0
- package/bin/cli/locales/sv.json +32 -0
- package/bin/cli/locales/sw.json +5 -0
- package/bin/cli/locales/ta.json +5 -0
- package/bin/cli/locales/te.json +5 -0
- package/bin/cli/locales/th.json +32 -0
- package/bin/cli/locales/tr.json +32 -0
- package/bin/cli/locales/uk-UA.json +32 -0
- package/bin/cli/locales/ur.json +5 -0
- package/bin/cli/locales/vi.json +32 -0
- package/bin/cli/locales/zh-CN.json +1262 -0
- package/bin/cli/output.mjs +200 -0
- package/bin/cli/plugins.mjs +90 -0
- package/bin/cli/program.mjs +40 -0
- package/bin/cli/provider-catalog.mjs +175 -0
- package/bin/cli/provider-store.mjs +312 -0
- package/bin/cli/provider-test.mjs +181 -0
- package/bin/cli/runtime/index.mjs +19 -0
- package/bin/cli/runtime/magicBytes.mjs +47 -0
- package/bin/cli/runtime/nativeDeps.mjs +137 -0
- package/bin/cli/runtime/processSupervisor.mjs +136 -0
- package/bin/cli/runtime/sqliteRuntime.mjs +129 -0
- package/bin/cli/runtime/supervisorPolicy.mjs +56 -0
- package/bin/cli/runtime/trayRuntime.ts +94 -0
- package/bin/cli/runtime.mjs +61 -0
- package/bin/cli/schemas/output-schemas.mjs +56 -0
- package/bin/cli/scripts/generate-locales.mjs +911 -0
- package/bin/cli/settings-store.mjs +45 -0
- package/bin/cli/spinner.mjs +30 -0
- package/bin/cli/sqlite.mjs +158 -0
- package/bin/cli/tray/autostart.mjs +410 -0
- package/bin/cli/tray/autostart.ts +18 -0
- package/bin/cli/tray/icon.png +0 -0
- package/bin/cli/tray/index.mjs +28 -0
- package/bin/cli/tray/tray.ps1 +99 -0
- package/bin/cli/tray/tray.ts +186 -0
- package/bin/cli/tray/traySystray.mjs +135 -0
- package/bin/cli/tray/trayWin.ts +91 -0
- package/bin/cli/tray/trayWindows.mjs +75 -0
- package/bin/cli/tui/Dashboard.jsx +70 -0
- package/bin/cli/tui/EvalWatch.jsx +171 -0
- package/bin/cli/tui/InterfaceMenu.jsx +55 -0
- package/bin/cli/tui/OAuthFlow.jsx +193 -0
- package/bin/cli/tui/ProvidersTestAll.jsx +190 -0
- package/bin/cli/tui/Repl.jsx +392 -0
- package/bin/cli/tui/session.mjs +54 -0
- package/bin/cli/tui/tabs/Combos.jsx +47 -0
- package/bin/cli/tui/tabs/Cost.jsx +52 -0
- package/bin/cli/tui/tabs/Health.jsx +53 -0
- package/bin/cli/tui/tabs/Keys.jsx +48 -0
- package/bin/cli/tui/tabs/Logs.jsx +60 -0
- package/bin/cli/tui/tabs/Overview.jsx +80 -0
- package/bin/cli/tui/tabs/Providers.jsx +48 -0
- package/bin/cli/tui-components/CodeBlock.jsx +15 -0
- package/bin/cli/tui-components/ConfirmDialog.jsx +40 -0
- package/bin/cli/tui-components/DataTable.jsx +50 -0
- package/bin/cli/tui-components/HeaderSwr.jsx +25 -0
- package/bin/cli/tui-components/KeyMaskedDisplay.jsx +12 -0
- package/bin/cli/tui-components/MarkdownView.jsx +13 -0
- package/bin/cli/tui-components/MenuSelect.jsx +38 -0
- package/bin/cli/tui-components/MultilineInput.jsx +18 -0
- package/bin/cli/tui-components/ProgressBar.jsx +15 -0
- package/bin/cli/tui-components/Sparkline.jsx +15 -0
- package/bin/cli/tui-components/StatusBadge.jsx +17 -0
- package/bin/cli/tui-components/TokenCounter.jsx +18 -0
- package/bin/cli/tui-components/theme.jsx +11 -0
- package/bin/cli/utils/cliToken.mjs +22 -0
- package/bin/cli/utils/clipboard.mjs +35 -0
- package/bin/cli/utils/environment.mjs +50 -0
- package/bin/cli/utils/pid.mjs +110 -0
- package/bin/cli/utils/storageKeyProvision.mjs +31 -0
- package/bin/cold-start-bench.sh +82 -0
- package/bin/mcp-server.mjs +71 -0
- package/bin/nodeRuntimeSupport.mjs +84 -0
- package/bin/omniroute.mjs +237 -0
- package/bin/reset-password.mjs +137 -0
- package/bin/restore-data.sh +64 -0
- package/bin/restore-policies.sh +77 -0
- package/bin/rollback.sh +102 -0
- package/bin/snapshot-data.sh +61 -0
- package/open-sse/config/agyModels.ts +157 -0
- package/open-sse/config/anthropicHeaders.ts +127 -0
- package/open-sse/config/antigravityModelAliases.ts +309 -0
- package/open-sse/config/antigravityUpstream.ts +20 -0
- package/open-sse/config/audioRegistry.ts +547 -0
- package/open-sse/config/azureAi.ts +49 -0
- package/open-sse/config/bedrock.ts +196 -0
- package/open-sse/config/cliFingerprints.ts +405 -0
- package/open-sse/config/codexClient.ts +49 -0
- package/open-sse/config/codexIdentity.ts +87 -0
- package/open-sse/config/codexInstructions.ts +122 -0
- package/open-sse/config/codexQuotaScopes.ts +87 -0
- package/open-sse/config/constants.ts +281 -0
- package/open-sse/config/contextEditing.ts +156 -0
- package/open-sse/config/credentialLoader.ts +123 -0
- package/open-sse/config/datarobot.ts +69 -0
- package/open-sse/config/defaultThinkingSignature.ts +8 -0
- package/open-sse/config/embeddingRegistry.ts +434 -0
- package/open-sse/config/errorConfig.ts +201 -0
- package/open-sse/config/freeModelCatalog.data.ts +467 -0
- package/open-sse/config/freeModelCatalog.ts +142 -0
- package/open-sse/config/freeTierCatalog.ts +101 -0
- package/open-sse/config/geminiRateLimits.json +34 -0
- package/open-sse/config/glmProvider.ts +488 -0
- package/open-sse/config/imageRegistry.ts +752 -0
- package/open-sse/config/maritalk.ts +18 -0
- package/open-sse/config/mediaServiceKinds.ts +70 -0
- package/open-sse/config/moderationRegistry.ts +89 -0
- package/open-sse/config/musicRegistry.ts +115 -0
- package/open-sse/config/oci.ts +44 -0
- package/open-sse/config/ocrRegistry.ts +82 -0
- package/open-sse/config/ollamaModels.ts +28 -0
- package/open-sse/config/providerErrorRules.ts +218 -0
- package/open-sse/config/providerFieldStrips.ts +42 -0
- package/open-sse/config/providerHeaderProfiles.ts +188 -0
- package/open-sse/config/providerModels.ts +199 -0
- package/open-sse/config/providerPluginManifest.ts +186 -0
- package/open-sse/config/providerPluginManifestRegistry.ts +31 -0
- package/open-sse/config/providerPluginManifestUrl.ts +26 -0
- package/open-sse/config/providerRegistry.ts +257 -0
- package/open-sse/config/providers/index.ts +377 -0
- package/open-sse/config/providers/registry/adapta-web/index.ts +20 -0
- package/open-sse/config/providers/registry/agentrouter/index.ts +24 -0
- package/open-sse/config/providers/registry/agy/index.ts +28 -0
- package/open-sse/config/providers/registry/ai21/index.ts +13 -0
- package/open-sse/config/providers/registry/aimlapi/index.ts +21 -0
- package/open-sse/config/providers/registry/alibaba/cn/index.ts +15 -0
- package/open-sse/config/providers/registry/alibaba/index.ts +15 -0
- package/open-sse/config/providers/registry/anthropic/index.ts +52 -0
- package/open-sse/config/providers/registry/antigravity/index.ts +28 -0
- package/open-sse/config/providers/registry/api-airforce/index.ts +57 -0
- package/open-sse/config/providers/registry/auggie/index.ts +38 -0
- package/open-sse/config/providers/registry/bai/index.ts +14 -0
- package/open-sse/config/providers/registry/baichuan/index.ts +20 -0
- package/open-sse/config/providers/registry/baidu/index.ts +31 -0
- package/open-sse/config/providers/registry/bailian-coding-plan/index.ts +27 -0
- package/open-sse/config/providers/registry/baseten/index.ts +13 -0
- package/open-sse/config/providers/registry/bazaarlink/index.ts +48 -0
- package/open-sse/config/providers/registry/bedrock/index.ts +49 -0
- package/open-sse/config/providers/registry/blackbox/index.ts +25 -0
- package/open-sse/config/providers/registry/blackbox/web/index.ts +19 -0
- package/open-sse/config/providers/registry/bluesminds/index.ts +39 -0
- package/open-sse/config/providers/registry/byteplus/index.ts +21 -0
- package/open-sse/config/providers/registry/bytez/index.ts +17 -0
- package/open-sse/config/providers/registry/cerebras/index.ts +16 -0
- package/open-sse/config/providers/registry/charm-hyper/index.ts +14 -0
- package/open-sse/config/providers/registry/chatgpt-web/index.ts +23 -0
- package/open-sse/config/providers/registry/chipotle/index.ts +14 -0
- package/open-sse/config/providers/registry/chutes/index.ts +12 -0
- package/open-sse/config/providers/registry/claude/index.ts +102 -0
- package/open-sse/config/providers/registry/claude/web/index.ts +16 -0
- package/open-sse/config/providers/registry/cline/index.ts +45 -0
- package/open-sse/config/providers/registry/clinepass/index.ts +47 -0
- package/open-sse/config/providers/registry/cloudflare-ai/index.ts +33 -0
- package/open-sse/config/providers/registry/codebuddy-cn/index.ts +151 -0
- package/open-sse/config/providers/registry/codestral/index.ts +13 -0
- package/open-sse/config/providers/registry/codex/index.ts +115 -0
- package/open-sse/config/providers/registry/cohere/index.ts +28 -0
- package/open-sse/config/providers/registry/command-code/index.ts +143 -0
- package/open-sse/config/providers/registry/copilot-m365-web/index.ts +12 -0
- package/open-sse/config/providers/registry/copilot-web/index.ts +16 -0
- package/open-sse/config/providers/registry/coze/index.ts +12 -0
- package/open-sse/config/providers/registry/crof/index.ts +38 -0
- package/open-sse/config/providers/registry/cursor/index.ts +111 -0
- package/open-sse/config/providers/registry/databricks/index.ts +13 -0
- package/open-sse/config/providers/registry/deepinfra/index.ts +13 -0
- package/open-sse/config/providers/registry/deepseek/index.ts +15 -0
- package/open-sse/config/providers/registry/deepseek/web/index.ts +35 -0
- package/open-sse/config/providers/registry/devin-cli/index.ts +68 -0
- package/open-sse/config/providers/registry/dgrid/index.ts +15 -0
- package/open-sse/config/providers/registry/dify/index.ts +12 -0
- package/open-sse/config/providers/registry/digitalocean/index.ts +11 -0
- package/open-sse/config/providers/registry/dit/index.ts +41 -0
- package/open-sse/config/providers/registry/doubao/index.ts +28 -0
- package/open-sse/config/providers/registry/doubao/web/index.ts +15 -0
- package/open-sse/config/providers/registry/duckduckgo-web/index.ts +19 -0
- package/open-sse/config/providers/registry/factory/index.ts +33 -0
- package/open-sse/config/providers/registry/featherless-ai/index.ts +13 -0
- package/open-sse/config/providers/registry/fireworks/index.ts +35 -0
- package/open-sse/config/providers/registry/freeaiapikey/index.ts +38 -0
- package/open-sse/config/providers/registry/freemodel-dev/index.ts +19 -0
- package/open-sse/config/providers/registry/friendliai/index.ts +16 -0
- package/open-sse/config/providers/registry/galadriel/index.ts +13 -0
- package/open-sse/config/providers/registry/gemini/index.ts +63 -0
- package/open-sse/config/providers/registry/gemini/web/index.ts +16 -0
- package/open-sse/config/providers/registry/gigachat/index.ts +13 -0
- package/open-sse/config/providers/registry/github/index.ts +161 -0
- package/open-sse/config/providers/registry/github/models/index.ts +38 -0
- package/open-sse/config/providers/registry/gitlab-duo/index.ts +29 -0
- package/open-sse/config/providers/registry/gitlawb/gmi/index.ts +19 -0
- package/open-sse/config/providers/registry/gitlawb/index.ts +18 -0
- package/open-sse/config/providers/registry/glhf/index.ts +12 -0
- package/open-sse/config/providers/registry/glm/cn/index.ts +17 -0
- package/open-sse/config/providers/registry/glm/index.ts +16 -0
- package/open-sse/config/providers/registry/glm/t/index.ts +16 -0
- package/open-sse/config/providers/registry/grok-cli/index.ts +32 -0
- package/open-sse/config/providers/registry/grok-web/index.ts +18 -0
- package/open-sse/config/providers/registry/groq/index.ts +25 -0
- package/open-sse/config/providers/registry/hackclub/index.ts +19 -0
- package/open-sse/config/providers/registry/haiper/index.ts +15 -0
- package/open-sse/config/providers/registry/hcnsec/index.ts +11 -0
- package/open-sse/config/providers/registry/heroku/index.ts +13 -0
- package/open-sse/config/providers/registry/huggingchat/index.ts +143 -0
- package/open-sse/config/providers/registry/huggingface/index.ts +20 -0
- package/open-sse/config/providers/registry/hyperbolic/index.ts +21 -0
- package/open-sse/config/providers/registry/ideogram/index.ts +15 -0
- package/open-sse/config/providers/registry/iflytek/index.ts +21 -0
- package/open-sse/config/providers/registry/inclusionai/index.ts +12 -0
- package/open-sse/config/providers/registry/inference-net/index.ts +13 -0
- package/open-sse/config/providers/registry/inner-ai/index.ts +43 -0
- package/open-sse/config/providers/registry/kenari/index.ts +14 -0
- package/open-sse/config/providers/registry/kie/index.ts +27 -0
- package/open-sse/config/providers/registry/kilo-gateway/index.ts +21 -0
- package/open-sse/config/providers/registry/kilocode/index.ts +43 -0
- package/open-sse/config/providers/registry/kimi/coding/index.ts +17 -0
- package/open-sse/config/providers/registry/kimi/coding-apikey/index.ts +9 -0
- package/open-sse/config/providers/registry/kimi/index.ts +17 -0
- package/open-sse/config/providers/registry/kimi/web/index.ts +27 -0
- package/open-sse/config/providers/registry/kiro/index.ts +50 -0
- package/open-sse/config/providers/registry/kluster/index.ts +12 -0
- package/open-sse/config/providers/registry/lambda-ai/index.ts +13 -0
- package/open-sse/config/providers/registry/leonardo/index.ts +15 -0
- package/open-sse/config/providers/registry/liquid/index.ts +12 -0
- package/open-sse/config/providers/registry/llamagate/index.ts +13 -0
- package/open-sse/config/providers/registry/llm7/index.ts +27 -0
- package/open-sse/config/providers/registry/longcat/index.ts +24 -0
- package/open-sse/config/providers/registry/maritalk/index.ts +13 -0
- package/open-sse/config/providers/registry/meta-llama/index.ts +13 -0
- package/open-sse/config/providers/registry/mimocode/index.ts +16 -0
- package/open-sse/config/providers/registry/minimax/cn/index.ts +24 -0
- package/open-sse/config/providers/registry/minimax/index.ts +24 -0
- package/open-sse/config/providers/registry/mistral/index.ts +18 -0
- package/open-sse/config/providers/registry/modal/index.ts +12 -0
- package/open-sse/config/providers/registry/modelscope/index.ts +24 -0
- package/open-sse/config/providers/registry/monsterapi/index.ts +17 -0
- package/open-sse/config/providers/registry/moonshot/index.ts +13 -0
- package/open-sse/config/providers/registry/morph/index.ts +13 -0
- package/open-sse/config/providers/registry/muse-spark-web/index.ts +24 -0
- package/open-sse/config/providers/registry/nanogpt/index.ts +13 -0
- package/open-sse/config/providers/registry/nebius/index.ts +9 -0
- package/open-sse/config/providers/registry/nlpcloud/index.ts +19 -0
- package/open-sse/config/providers/registry/nous-research/index.ts +15 -0
- package/open-sse/config/providers/registry/novita/index.ts +15 -0
- package/open-sse/config/providers/registry/nscale/index.ts +13 -0
- package/open-sse/config/providers/registry/nube/index.ts +17 -0
- package/open-sse/config/providers/registry/nvidia/index.ts +36 -0
- package/open-sse/config/providers/registry/ollama-cloud/index.ts +27 -0
- package/open-sse/config/providers/registry/openadapter/index.ts +25 -0
- package/open-sse/config/providers/registry/openai/index.ts +43 -0
- package/open-sse/config/providers/registry/opencode/go/index.ts +61 -0
- package/open-sse/config/providers/registry/opencode/index.ts +51 -0
- package/open-sse/config/providers/registry/opencode/zen/index.ts +98 -0
- package/open-sse/config/providers/registry/openrouter/index.ts +17 -0
- package/open-sse/config/providers/registry/orcarouter/index.ts +87 -0
- package/open-sse/config/providers/registry/ovhcloud/index.ts +13 -0
- package/open-sse/config/providers/registry/perplexity/index.ts +22 -0
- package/open-sse/config/providers/registry/perplexity/web/index.ts +23 -0
- package/open-sse/config/providers/registry/pioneer/index.ts +41 -0
- package/open-sse/config/providers/registry/pollinations/index.ts +51 -0
- package/open-sse/config/providers/registry/predibase/index.ts +13 -0
- package/open-sse/config/providers/registry/publicai/index.ts +13 -0
- package/open-sse/config/providers/registry/puter/index.ts +70 -0
- package/open-sse/config/providers/registry/qianfan/index.ts +18 -0
- package/open-sse/config/providers/registry/qiniu/index.ts +15 -0
- package/open-sse/config/providers/registry/qoder/index.ts +37 -0
- package/open-sse/config/providers/registry/qwen/index.ts +25 -0
- package/open-sse/config/providers/registry/qwen/web/index.ts +24 -0
- package/open-sse/config/providers/registry/reka/index.ts +18 -0
- package/open-sse/config/providers/registry/requesty/index.ts +11 -0
- package/open-sse/config/providers/registry/sambanova/index.ts +13 -0
- package/open-sse/config/providers/registry/scaleway/index.ts +20 -0
- package/open-sse/config/providers/registry/sensenova/index.ts +27 -0
- package/open-sse/config/providers/registry/siliconflow/index.ts +84 -0
- package/open-sse/config/providers/registry/snowflake/index.ts +13 -0
- package/open-sse/config/providers/registry/sparkdesk/index.ts +20 -0
- package/open-sse/config/providers/registry/stepfun/index.ts +20 -0
- package/open-sse/config/providers/registry/sumopod/index.ts +15 -0
- package/open-sse/config/providers/registry/suno/index.ts +18 -0
- package/open-sse/config/providers/registry/synthetic/index.ts +21 -0
- package/open-sse/config/providers/registry/t3-web/index.ts +45 -0
- package/open-sse/config/providers/registry/tencent/index.ts +25 -0
- package/open-sse/config/providers/registry/theoldllm/index.ts +51 -0
- package/open-sse/config/providers/registry/together/index.ts +20 -0
- package/open-sse/config/providers/registry/tokenrouter/index.ts +44 -0
- package/open-sse/config/providers/registry/trae/index.ts +24 -0
- package/open-sse/config/providers/registry/udio/index.ts +12 -0
- package/open-sse/config/providers/registry/uncloseai/index.ts +19 -0
- package/open-sse/config/providers/registry/upstage/index.ts +13 -0
- package/open-sse/config/providers/registry/v0-vercel/index.ts +13 -0
- package/open-sse/config/providers/registry/venice/index.ts +13 -0
- package/open-sse/config/providers/registry/veoaifree-web/index.ts +15 -0
- package/open-sse/config/providers/registry/vercel-ai-gateway/index.ts +13 -0
- package/open-sse/config/providers/registry/vertex/index.ts +33 -0
- package/open-sse/config/providers/registry/vertex/partner/index.ts +22 -0
- package/open-sse/config/providers/registry/volcengine/index.ts +13 -0
- package/open-sse/config/providers/registry/wafer/index.ts +19 -0
- package/open-sse/config/providers/registry/wandb/index.ts +13 -0
- package/open-sse/config/providers/registry/windsurf/index.ts +119 -0
- package/open-sse/config/providers/registry/x5lab/index.ts +15 -0
- package/open-sse/config/providers/registry/xai/index.ts +18 -0
- package/open-sse/config/providers/registry/xiaomi-mimo/index.ts +13 -0
- package/open-sse/config/providers/registry/yi/index.ts +12 -0
- package/open-sse/config/providers/registry/yuanbao-web/index.ts +37 -0
- package/open-sse/config/providers/registry/zai/index.ts +28 -0
- package/open-sse/config/providers/registry/zed-hosted/index.ts +35 -0
- package/open-sse/config/providers/registry/zenmux/index.ts +87 -0
- package/open-sse/config/providers/registry/zenmux-free/index.ts +40 -0
- package/open-sse/config/providers/shared.ts +766 -0
- package/open-sse/config/registryUtils.ts +134 -0
- package/open-sse/config/rerankRegistry.ts +196 -0
- package/open-sse/config/runway.ts +39 -0
- package/open-sse/config/sap.ts +58 -0
- package/open-sse/config/searchRegistry.ts +317 -0
- package/open-sse/config/toolCloaking.ts +253 -0
- package/open-sse/config/videoRegistry.ts +229 -0
- package/open-sse/config/watsonx.ts +43 -0
- package/open-sse/executors/adapta-web.ts +564 -0
- package/open-sse/executors/antigravity/sseCollect.ts +148 -0
- package/open-sse/executors/antigravity.ts +1716 -0
- package/open-sse/executors/antigravityUpstreamError.ts +25 -0
- package/open-sse/executors/auggie.ts +573 -0
- package/open-sse/executors/azure-openai.ts +48 -0
- package/open-sse/executors/base/headers.ts +93 -0
- package/open-sse/executors/base/reasoningEffort.ts +160 -0
- package/open-sse/executors/base.ts +1315 -0
- package/open-sse/executors/bedrock.ts +714 -0
- package/open-sse/executors/blackbox-web.ts +708 -0
- package/open-sse/executors/chatgpt-web/models.ts +133 -0
- package/open-sse/executors/chatgpt-web.ts +3107 -0
- package/open-sse/executors/chatgptWebErrors.ts +18 -0
- package/open-sse/executors/chatgptWebTools.ts +127 -0
- package/open-sse/executors/chipotle.ts +432 -0
- package/open-sse/executors/claude-web/payload.ts +230 -0
- package/open-sse/executors/claude-web-with-auto-refresh.ts +117 -0
- package/open-sse/executors/claude-web.ts +833 -0
- package/open-sse/executors/claudeIdentity.ts +445 -0
- package/open-sse/executors/cliproxyapi.ts +466 -0
- package/open-sse/executors/cloudflare-ai.ts +97 -0
- package/open-sse/executors/codebuddy-cn.ts +53 -0
- package/open-sse/executors/codex/quota.ts +114 -0
- package/open-sse/executors/codex/tools.ts +165 -0
- package/open-sse/executors/codex.ts +1270 -0
- package/open-sse/executors/commandCode.ts +716 -0
- package/open-sse/executors/copilot-m365-connection.ts +271 -0
- package/open-sse/executors/copilot-m365-frames.ts +279 -0
- package/open-sse/executors/copilot-m365-web.ts +341 -0
- package/open-sse/executors/copilot-web.ts +722 -0
- package/open-sse/executors/cursor/composer.ts +53 -0
- package/open-sse/executors/cursor/prompt.ts +75 -0
- package/open-sse/executors/cursor.ts +1465 -0
- package/open-sse/executors/deepseek-web/stream-format.ts +44 -0
- package/open-sse/executors/deepseek-web-with-auto-refresh.ts +184 -0
- package/open-sse/executors/deepseek-web.ts +1123 -0
- package/open-sse/executors/default/urlNormalizers.ts +64 -0
- package/open-sse/executors/default.ts +873 -0
- package/open-sse/executors/devin-cli.ts +470 -0
- package/open-sse/executors/doubao-web.ts +665 -0
- package/open-sse/executors/duckduckgo-web/challenge.ts +151 -0
- package/open-sse/executors/duckduckgo-web.ts +782 -0
- package/open-sse/executors/firecrawl-fetch.ts +177 -0
- package/open-sse/executors/forceResponsesUpstream.ts +60 -0
- package/open-sse/executors/gemini-business.ts +446 -0
- package/open-sse/executors/gemini-web.ts +413 -0
- package/open-sse/executors/github.ts +376 -0
- package/open-sse/executors/gitlab.ts +793 -0
- package/open-sse/executors/gitlabResponses.ts +191 -0
- package/open-sse/executors/glm.ts +520 -0
- package/open-sse/executors/grok-cli.ts +253 -0
- package/open-sse/executors/grok-web/native-tools.ts +182 -0
- package/open-sse/executors/grok-web/text-cleanup.ts +137 -0
- package/open-sse/executors/grok-web/tool-bridge.ts +666 -0
- package/open-sse/executors/grok-web/types.ts +47 -0
- package/open-sse/executors/grok-web.ts +883 -0
- package/open-sse/executors/huggingchat/jsonlStream.ts +221 -0
- package/open-sse/executors/huggingchat.ts +593 -0
- package/open-sse/executors/index.ts +232 -0
- package/open-sse/executors/inner-ai.ts +764 -0
- package/open-sse/executors/jina-reader-fetch.ts +119 -0
- package/open-sse/executors/kie.ts +109 -0
- package/open-sse/executors/kimi-web.ts +455 -0
- package/open-sse/executors/kimi.ts +133 -0
- package/open-sse/executors/kiro/eventstream.ts +192 -0
- package/open-sse/executors/kiro.ts +821 -0
- package/open-sse/executors/kiroThinking.ts +116 -0
- package/open-sse/executors/lmarena.ts +511 -0
- package/open-sse/executors/mimocode.ts +560 -0
- package/open-sse/executors/muse-spark-web/response-parser.ts +383 -0
- package/open-sse/executors/muse-spark-web.ts +928 -0
- package/open-sse/executors/ninerouter.ts +212 -0
- package/open-sse/executors/nlpcloud.ts +546 -0
- package/open-sse/executors/opencode.ts +336 -0
- package/open-sse/executors/perplexity-web/protocol.ts +503 -0
- package/open-sse/executors/perplexity-web.ts +545 -0
- package/open-sse/executors/poe-web.ts +158 -0
- package/open-sse/executors/pollinations.ts +119 -0
- package/open-sse/executors/puter.ts +59 -0
- package/open-sse/executors/qoder.ts +416 -0
- package/open-sse/executors/qwen-web.ts +474 -0
- package/open-sse/executors/t3-chat-web.ts +582 -0
- package/open-sse/executors/tavily-fetch.ts +103 -0
- package/open-sse/executors/theoldllm.ts +354 -0
- package/open-sse/executors/tinyfish-fetch.ts +131 -0
- package/open-sse/executors/trae.ts +481 -0
- package/open-sse/executors/v0-vercel-web.ts +164 -0
- package/open-sse/executors/venice-web.ts +165 -0
- package/open-sse/executors/veoaifree-web.ts +397 -0
- package/open-sse/executors/vertex.ts +208 -0
- package/open-sse/executors/vertexMedia.ts +341 -0
- package/open-sse/executors/windsurf.ts +706 -0
- package/open-sse/executors/xai.ts +94 -0
- package/open-sse/executors/yuanbao-web.ts +504 -0
- package/open-sse/executors/zed-hosted.ts +364 -0
- package/open-sse/executors/zenmux-free.ts +283 -0
- package/open-sse/handlers/audioSpeech.ts +1060 -0
- package/open-sse/handlers/audioTranscription.ts +554 -0
- package/open-sse/handlers/audioTranslation.ts +135 -0
- package/open-sse/handlers/chatCore/attemptLogging.ts +210 -0
- package/open-sse/handlers/chatCore/backgroundRedirect.ts +41 -0
- package/open-sse/handlers/chatCore/cacheUsageMeta.ts +65 -0
- package/open-sse/handlers/chatCore/cavemanOutputAnalytics.ts +47 -0
- package/open-sse/handlers/chatCore/claudeClassifierCompat.ts +99 -0
- package/open-sse/handlers/chatCore/claudeEffortVariant.ts +62 -0
- package/open-sse/handlers/chatCore/claudeMessageTypes.ts +15 -0
- package/open-sse/handlers/chatCore/claudeSystemRole.ts +48 -0
- package/open-sse/handlers/chatCore/claudeToolDefaults.ts +27 -0
- package/open-sse/handlers/chatCore/claudeUpstreamMessages.ts +143 -0
- package/open-sse/handlers/chatCore/clientUsageBuffer.ts +54 -0
- package/open-sse/handlers/chatCore/clineResponseEnvelope.ts +25 -0
- package/open-sse/handlers/chatCore/codexFailover.ts +41 -0
- package/open-sse/handlers/chatCore/codexQuota.ts +85 -0
- package/open-sse/handlers/chatCore/comboContextCache.ts +63 -0
- package/open-sse/handlers/chatCore/compressionAnalyticsWrite.ts +149 -0
- package/open-sse/handlers/chatCore/compressionCacheStats.ts +53 -0
- package/open-sse/handlers/chatCore/compressionComboPredicates.ts +41 -0
- package/open-sse/handlers/chatCore/compressionSettings.ts +35 -0
- package/open-sse/handlers/chatCore/compressionUsageReceipt.ts +27 -0
- package/open-sse/handlers/chatCore/contextEditingTelemetry.ts +40 -0
- package/open-sse/handlers/chatCore/cooldownClassification.ts +26 -0
- package/open-sse/handlers/chatCore/executionCredentials.ts +72 -0
- package/open-sse/handlers/chatCore/executorClientHeaders.ts +36 -0
- package/open-sse/handlers/chatCore/executorHelpers.ts +151 -0
- package/open-sse/handlers/chatCore/executorProxy.ts +117 -0
- package/open-sse/handlers/chatCore/failureUsage.ts +41 -0
- package/open-sse/handlers/chatCore/gamificationEvent.ts +28 -0
- package/open-sse/handlers/chatCore/headers.ts +65 -0
- package/open-sse/handlers/chatCore/idempotency.ts +65 -0
- package/open-sse/handlers/chatCore/jsonBodyToSse.ts +161 -0
- package/open-sse/handlers/chatCore/keyHealth.ts +113 -0
- package/open-sse/handlers/chatCore/logTruncation.ts +93 -0
- package/open-sse/handlers/chatCore/memoryExtraction.ts +131 -0
- package/open-sse/handlers/chatCore/memorySkillsInjection.ts +165 -0
- package/open-sse/handlers/chatCore/nonStreamingJsonResponse.ts +16 -0
- package/open-sse/handlers/chatCore/nonStreamingResponseBody.ts +155 -0
- package/open-sse/handlers/chatCore/nonStreamingResponseHeaders.ts +46 -0
- package/open-sse/handlers/chatCore/nonStreamingResponseParse.ts +135 -0
- package/open-sse/handlers/chatCore/nonStreamingSse.ts +171 -0
- package/open-sse/handlers/chatCore/nonStreamingUsageStats.ts +90 -0
- package/open-sse/handlers/chatCore/outputStyleTelemetry.ts +53 -0
- package/open-sse/handlers/chatCore/passthroughHelpers.ts +83 -0
- package/open-sse/handlers/chatCore/passthroughToolNames.ts +65 -0
- package/open-sse/handlers/chatCore/pluginOnRequest.ts +65 -0
- package/open-sse/handlers/chatCore/pluginOnResponse.ts +34 -0
- package/open-sse/handlers/chatCore/postCallGuardrailContext.ts +47 -0
- package/open-sse/handlers/chatCore/quotaShareConsumption.ts +41 -0
- package/open-sse/handlers/chatCore/requestFormat.ts +111 -0
- package/open-sse/handlers/chatCore/requestSetup.ts +51 -0
- package/open-sse/handlers/chatCore/responseHeaders.ts +138 -0
- package/open-sse/handlers/chatCore/sanitization.ts +63 -0
- package/open-sse/handlers/chatCore/semanticCache.ts +98 -0
- package/open-sse/handlers/chatCore/semanticCacheStore.ts +73 -0
- package/open-sse/handlers/chatCore/serviceTier.ts +70 -0
- package/open-sse/handlers/chatCore/skillsFormat.ts +33 -0
- package/open-sse/handlers/chatCore/stageTrace.ts +30 -0
- package/open-sse/handlers/chatCore/streamErrorResult.ts +58 -0
- package/open-sse/handlers/chatCore/streamFinalize.ts +48 -0
- package/open-sse/handlers/chatCore/streamingCost.ts +37 -0
- package/open-sse/handlers/chatCore/streamingPipeline.ts +99 -0
- package/open-sse/handlers/chatCore/streamingQuotaShare.ts +54 -0
- package/open-sse/handlers/chatCore/streamingResponseHeaders.ts +39 -0
- package/open-sse/handlers/chatCore/streamingSemanticCacheStore.ts +95 -0
- package/open-sse/handlers/chatCore/streamingUsageStats.ts +79 -0
- package/open-sse/handlers/chatCore/targetFormat.ts +34 -0
- package/open-sse/handlers/chatCore/telemetryHelpers.ts +78 -0
- package/open-sse/handlers/chatCore/upstreamBody.ts +168 -0
- package/open-sse/handlers/chatCore/upstreamExecuteHeaders.ts +71 -0
- package/open-sse/handlers/chatCore/upstreamTimeouts.ts +158 -0
- package/open-sse/handlers/chatCore.ts +4452 -0
- package/open-sse/handlers/embeddings.ts +378 -0
- package/open-sse/handlers/imageGeneration/providers/chatgptWeb.ts +187 -0
- package/open-sse/handlers/imageGeneration/providers/comfyUI.ts +116 -0
- package/open-sse/handlers/imageGeneration/providers/haiper.ts +120 -0
- package/open-sse/handlers/imageGeneration/providers/huggingface.ts +90 -0
- package/open-sse/handlers/imageGeneration/providers/hyperbolic.ts +100 -0
- package/open-sse/handlers/imageGeneration/providers/ideogram.ts +96 -0
- package/open-sse/handlers/imageGeneration/providers/imagen3.ts +136 -0
- package/open-sse/handlers/imageGeneration/providers/leonardo.ts +140 -0
- package/open-sse/handlers/imageGeneration/providers/nvidiaNim.ts +286 -0
- package/open-sse/handlers/imageGeneration/providers/sdWebUI.ts +94 -0
- package/open-sse/handlers/imageGeneration.ts +2880 -0
- package/open-sse/handlers/moderations.ts +82 -0
- package/open-sse/handlers/musicGeneration.ts +640 -0
- package/open-sse/handlers/ocr.ts +79 -0
- package/open-sse/handlers/rerank.ts +210 -0
- package/open-sse/handlers/responseSanitizer/reasoning.ts +149 -0
- package/open-sse/handlers/responseSanitizer.ts +1086 -0
- package/open-sse/handlers/responseTranslator.ts +640 -0
- package/open-sse/handlers/responsesHandler.ts +92 -0
- package/open-sse/handlers/search.ts +1545 -0
- package/open-sse/handlers/sseParser.ts +825 -0
- package/open-sse/handlers/usageExtractor.ts +93 -0
- package/open-sse/handlers/videoGeneration/googleFlow.ts +219 -0
- package/open-sse/handlers/videoGeneration/googleFlowHandler.ts +145 -0
- package/open-sse/handlers/videoGeneration.ts +1264 -0
- package/open-sse/handlers/webFetch.ts +130 -0
- package/open-sse/index.ts +170 -0
- package/open-sse/lib/deepseek-pow-solver.cjs +3 -0
- package/open-sse/lib/deepseek-pow.ts +189 -0
- package/open-sse/lib/sha3_wasm_bg.wasm +0 -0
- package/open-sse/mcp-server/README.md +611 -0
- package/open-sse/mcp-server/audit.ts +478 -0
- package/open-sse/mcp-server/catalog.ts +290 -0
- package/open-sse/mcp-server/descriptionCompressor.ts +243 -0
- package/open-sse/mcp-server/httpAuthContext.ts +42 -0
- package/open-sse/mcp-server/httpTransport.ts +306 -0
- package/open-sse/mcp-server/index.ts +19 -0
- package/open-sse/mcp-server/mcpCallerIdentity.ts +53 -0
- package/open-sse/mcp-server/runtimeHeartbeat.ts +162 -0
- package/open-sse/mcp-server/schemas/a2a.ts +203 -0
- package/open-sse/mcp-server/schemas/audit.ts +121 -0
- package/open-sse/mcp-server/schemas/index.ts +116 -0
- package/open-sse/mcp-server/schemas/pickFastestModel.ts +110 -0
- package/open-sse/mcp-server/schemas/toolDefinition.ts +31 -0
- package/open-sse/mcp-server/schemas/toolSearch.ts +37 -0
- package/open-sse/mcp-server/schemas/tools.ts +1481 -0
- package/open-sse/mcp-server/scopeEnforcement.ts +135 -0
- package/open-sse/mcp-server/server.ts +1370 -0
- package/open-sse/mcp-server/toolCardinality.ts +254 -0
- package/open-sse/mcp-server/toolSearch/catalog.ts +98 -0
- package/open-sse/mcp-server/toolSearch/handler.ts +18 -0
- package/open-sse/mcp-server/toolSearch/index.ts +5 -0
- package/open-sse/mcp-server/toolSearch/register.ts +36 -0
- package/open-sse/mcp-server/toolSearch/search.ts +96 -0
- package/open-sse/mcp-server/toolSearch/signature.ts +92 -0
- package/open-sse/mcp-server/tools/advancedTools.ts +1119 -0
- package/open-sse/mcp-server/tools/agentSkillTools.ts +83 -0
- package/open-sse/mcp-server/tools/compressionTools.ts +451 -0
- package/open-sse/mcp-server/tools/gamificationTools.ts +148 -0
- package/open-sse/mcp-server/tools/githubSkillTools.ts +141 -0
- package/open-sse/mcp-server/tools/memoryTools.ts +137 -0
- package/open-sse/mcp-server/tools/notionTools.ts +106 -0
- package/open-sse/mcp-server/tools/obsidianTools.ts +327 -0
- package/open-sse/mcp-server/tools/pickFastestModel.ts +293 -0
- package/open-sse/mcp-server/tools/pluginTools.ts +216 -0
- package/open-sse/mcp-server/tools/poolTools.ts +205 -0
- package/open-sse/mcp-server/tools/skillTools.ts +119 -0
- package/open-sse/package.json +17 -0
- package/open-sse/services/AGENTS.md +78 -0
- package/open-sse/services/accountFallback.ts +1863 -0
- package/open-sse/services/accountSelector.ts +80 -0
- package/open-sse/services/accountSemaphore.ts +367 -0
- package/open-sse/services/antigravity429Engine.ts +178 -0
- package/open-sse/services/antigravityClientProfile.ts +167 -0
- package/open-sse/services/antigravityCredits.ts +68 -0
- package/open-sse/services/antigravityHeaderScrub.ts +75 -0
- package/open-sse/services/antigravityHeaders.ts +121 -0
- package/open-sse/services/antigravityIdentity.ts +127 -0
- package/open-sse/services/antigravityObfuscation.ts +64 -0
- package/open-sse/services/antigravityProjectBootstrap.ts +145 -0
- package/open-sse/services/antigravityQuotaFamily.ts +56 -0
- package/open-sse/services/antigravityVersion.ts +163 -0
- package/open-sse/services/apiKeyRotator.ts +385 -0
- package/open-sse/services/autoCombo/autoPrefix.ts +61 -0
- package/open-sse/services/autoCombo/builtinCatalog.ts +126 -0
- package/open-sse/services/autoCombo/complexityRouter.ts +111 -0
- package/open-sse/services/autoCombo/engine.ts +314 -0
- package/open-sse/services/autoCombo/index.ts +18 -0
- package/open-sse/services/autoCombo/modePacks.ts +105 -0
- package/open-sse/services/autoCombo/modelFamily.ts +95 -0
- package/open-sse/services/autoCombo/paidModelFilter.ts +44 -0
- package/open-sse/services/autoCombo/persistence.ts +124 -0
- package/open-sse/services/autoCombo/pipelineRouter.ts +418 -0
- package/open-sse/services/autoCombo/providerDiversity.ts +170 -0
- package/open-sse/services/autoCombo/providerRegistryAccessor.ts +9 -0
- package/open-sse/services/autoCombo/requestControls.ts +80 -0
- package/open-sse/services/autoCombo/routerStrategy.ts +375 -0
- package/open-sse/services/autoCombo/scoring.ts +242 -0
- package/open-sse/services/autoCombo/selfHealing.ts +167 -0
- package/open-sse/services/autoCombo/speedRanking.ts +327 -0
- package/open-sse/services/autoCombo/suffixComposition.ts +147 -0
- package/open-sse/services/autoCombo/taskFitness.ts +407 -0
- package/open-sse/services/autoCombo/virtualFactory.ts +446 -0
- package/open-sse/services/autoRefreshDaemon.ts +206 -0
- package/open-sse/services/backgroundTaskDetector.ts +263 -0
- package/open-sse/services/bailianQuotaFetcher.ts +333 -0
- package/open-sse/services/batchProcessor.ts +914 -0
- package/open-sse/services/bedrock.ts +160 -0
- package/open-sse/services/browserBackedChat.ts +849 -0
- package/open-sse/services/browserPool.ts +501 -0
- package/open-sse/services/ccBridgeTransforms.ts +581 -0
- package/open-sse/services/ccWireImageBuiltins.ts +26 -0
- package/open-sse/services/chatgptImageCache.ts +143 -0
- package/open-sse/services/chatgptTlsClient.ts +628 -0
- package/open-sse/services/claudeAdaptiveThinking.ts +52 -0
- package/open-sse/services/claudeCodeCCH.ts +49 -0
- package/open-sse/services/claudeCodeCompatible.ts +1198 -0
- package/open-sse/services/claudeCodeCompatibleBeta.ts +23 -0
- package/open-sse/services/claudeCodeCompatibleThinkingDisplay.ts +34 -0
- package/open-sse/services/claudeCodeConstraints.ts +144 -0
- package/open-sse/services/claudeCodeExtraRemap.ts +18 -0
- package/open-sse/services/claudeCodeFingerprint.ts +46 -0
- package/open-sse/services/claudeCodeObfuscation.ts +116 -0
- package/open-sse/services/claudeCodeToolRemapper.ts +339 -0
- package/open-sse/services/claudeHaikuConstraints.ts +88 -0
- package/open-sse/services/claudeTlsClient.ts +590 -0
- package/open-sse/services/claudeTurnstileSolver.ts +206 -0
- package/open-sse/services/claudeUsageCooldown.ts +51 -0
- package/open-sse/services/claudeWebAutoRefresh.ts +224 -0
- package/open-sse/services/clinepassModels.ts +76 -0
- package/open-sse/services/cloudCodeHeaders.ts +41 -0
- package/open-sse/services/cloudCodeThinking.ts +71 -0
- package/open-sse/services/codeAssistSubscription.ts +83 -0
- package/open-sse/services/codexQuotaFetcher.ts +523 -0
- package/open-sse/services/codexUsageQuotas.ts +270 -0
- package/open-sse/services/codexVerbosity.ts +59 -0
- package/open-sse/services/combo/applyStrategyOrdering.ts +226 -0
- package/open-sse/services/combo/autoConfig.ts +62 -0
- package/open-sse/services/combo/autoStrategy.ts +475 -0
- package/open-sse/services/combo/comboCompatFallback.ts +82 -0
- package/open-sse/services/combo/comboCooldownRetry.ts +237 -0
- package/open-sse/services/combo/comboData.ts +24 -0
- package/open-sse/services/combo/comboPredicates.ts +211 -0
- package/open-sse/services/combo/comboSetup.ts +135 -0
- package/open-sse/services/combo/comboStructure.ts +766 -0
- package/open-sse/services/combo/concurrencyCaps.ts +76 -0
- package/open-sse/services/combo/context.ts +46 -0
- package/open-sse/services/combo/fingerprintExpansion.ts +105 -0
- package/open-sse/services/combo/headroomRanking.ts +91 -0
- package/open-sse/services/combo/providerWildcard.ts +253 -0
- package/open-sse/services/combo/quotaExhaustionCutoff.ts +140 -0
- package/open-sse/services/combo/quotaScoring.ts +311 -0
- package/open-sse/services/combo/quotaShareConcurrency.ts +74 -0
- package/open-sse/services/combo/quotaShareInflight.ts +133 -0
- package/open-sse/services/combo/quotaShareStrategy.ts +349 -0
- package/open-sse/services/combo/quotaStrategies.ts +647 -0
- package/open-sse/services/combo/resolveAutoStrategy.ts +334 -0
- package/open-sse/services/combo/responseValidation.ts +206 -0
- package/open-sse/services/combo/rrState.ts +98 -0
- package/open-sse/services/combo/runtimeUnits.ts +269 -0
- package/open-sse/services/combo/sessionStickiness.ts +301 -0
- package/open-sse/services/combo/shadowRouting.ts +179 -0
- package/open-sse/services/combo/targetExhaustion.ts +160 -0
- package/open-sse/services/combo/targetSorters.ts +174 -0
- package/open-sse/services/combo/targetTimeoutRunner.ts +91 -0
- package/open-sse/services/combo/types.ts +175 -0
- package/open-sse/services/combo/validateQuality.ts +445 -0
- package/open-sse/services/combo.ts +3085 -0
- package/open-sse/services/comboAgentMiddleware.ts +214 -0
- package/open-sse/services/comboConfig.ts +248 -0
- package/open-sse/services/comboManifestMetrics.ts +13 -0
- package/open-sse/services/comboMetrics.ts +480 -0
- package/open-sse/services/compression/adaptiveCompression/computeTarget.ts +31 -0
- package/open-sse/services/compression/adaptiveCompression/ladder.ts +59 -0
- package/open-sse/services/compression/adaptiveCompression/resolveAdaptivePlan.ts +104 -0
- package/open-sse/services/compression/adaptiveCompression/types.ts +61 -0
- package/open-sse/services/compression/aggressive.ts +204 -0
- package/open-sse/services/compression/bodyAdapter.ts +354 -0
- package/open-sse/services/compression/cacheAwareConfig.ts +60 -0
- package/open-sse/services/compression/cachingAware.ts +130 -0
- package/open-sse/services/compression/caveman.ts +602 -0
- package/open-sse/services/compression/cavemanRules.ts +432 -0
- package/open-sse/services/compression/deriveDefaultPlan.ts +59 -0
- package/open-sse/services/compression/diffHelper.ts +219 -0
- package/open-sse/services/compression/engineBreakdown.ts +29 -0
- package/open-sse/services/compression/engineCatalog.ts +91 -0
- package/open-sse/services/compression/engines/cavemanAdapter.ts +454 -0
- package/open-sse/services/compression/engines/ccr/ccrQuery.ts +110 -0
- package/open-sse/services/compression/engines/ccr/index.ts +460 -0
- package/open-sse/services/compression/engines/headroom/encoderComparison.ts +72 -0
- package/open-sse/services/compression/engines/headroom/gcf/decode_generic.ts +711 -0
- package/open-sse/services/compression/engines/headroom/gcf/generic.ts +332 -0
- package/open-sse/services/compression/engines/headroom/gcf/index.ts +9 -0
- package/open-sse/services/compression/engines/headroom/gcf/scalar.ts +336 -0
- package/open-sse/services/compression/engines/headroom/index.ts +286 -0
- package/open-sse/services/compression/engines/headroom/smartcrusher.ts +255 -0
- package/open-sse/services/compression/engines/headroom/tabular.ts +263 -0
- package/open-sse/services/compression/engines/headroom/toon.ts +43 -0
- package/open-sse/services/compression/engines/index.ts +42 -0
- package/open-sse/services/compression/engines/ionizer/index.ts +124 -0
- package/open-sse/services/compression/engines/ionizer/sample.ts +200 -0
- package/open-sse/services/compression/engines/llm/index.ts +346 -0
- package/open-sse/services/compression/engines/llmlingua/constants.ts +55 -0
- package/open-sse/services/compression/engines/llmlingua/index.ts +474 -0
- package/open-sse/services/compression/engines/llmlingua/modelStore.ts +67 -0
- package/open-sse/services/compression/engines/llmlingua/onnxWorker.ts +117 -0
- package/open-sse/services/compression/engines/llmlingua/ultraEntry.ts +109 -0
- package/open-sse/services/compression/engines/llmlingua/worker.ts +373 -0
- package/open-sse/services/compression/engines/mcpAccessibility/collapseRepeated.ts +121 -0
- package/open-sse/services/compression/engines/mcpAccessibility/constants.ts +63 -0
- package/open-sse/services/compression/engines/mcpAccessibility/index.ts +52 -0
- package/open-sse/services/compression/engines/readLifecycle/index.ts +299 -0
- package/open-sse/services/compression/engines/registry.ts +87 -0
- package/open-sse/services/compression/engines/relevance/configSchema.ts +63 -0
- package/open-sse/services/compression/engines/relevance/index.ts +185 -0
- package/open-sse/services/compression/engines/relevance/scorer.ts +85 -0
- package/open-sse/services/compression/engines/rtk/codeStripper.ts +145 -0
- package/open-sse/services/compression/engines/rtk/commandDetector.ts +482 -0
- package/open-sse/services/compression/engines/rtk/configSchema.ts +117 -0
- package/open-sse/services/compression/engines/rtk/deduplicator.ts +37 -0
- package/open-sse/services/compression/engines/rtk/discover.ts +150 -0
- package/open-sse/services/compression/engines/rtk/filterLoader.ts +245 -0
- package/open-sse/services/compression/engines/rtk/filterSchema.ts +207 -0
- package/open-sse/services/compression/engines/rtk/filters/aws.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/biome.json +35 -0
- package/open-sse/services/compression/engines/rtk/filters/build-eslint.json +37 -0
- package/open-sse/services/compression/engines/rtk/filters/build-typescript.json +33 -0
- package/open-sse/services/compression/engines/rtk/filters/build-vite.json +41 -0
- package/open-sse/services/compression/engines/rtk/filters/build-webpack.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/bundle-install.json +35 -0
- package/open-sse/services/compression/engines/rtk/filters/composer.json +81 -0
- package/open-sse/services/compression/engines/rtk/filters/curl.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/df.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/docker-build.json +78 -0
- package/open-sse/services/compression/engines/rtk/filters/docker-logs.json +42 -0
- package/open-sse/services/compression/engines/rtk/filters/docker-ps.json +33 -0
- package/open-sse/services/compression/engines/rtk/filters/dotnet.json +47 -0
- package/open-sse/services/compression/engines/rtk/filters/du.json +33 -0
- package/open-sse/services/compression/engines/rtk/filters/error-stacktrace.json +41 -0
- package/open-sse/services/compression/engines/rtk/filters/gcloud.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/generic-output.json +32 -0
- package/open-sse/services/compression/engines/rtk/filters/gh.json +98 -0
- package/open-sse/services/compression/engines/rtk/filters/git-branch.json +45 -0
- package/open-sse/services/compression/engines/rtk/filters/git-diff.json +33 -0
- package/open-sse/services/compression/engines/rtk/filters/git-log.json +33 -0
- package/open-sse/services/compression/engines/rtk/filters/git-status.json +40 -0
- package/open-sse/services/compression/engines/rtk/filters/golangci-lint.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/gradle.json +47 -0
- package/open-sse/services/compression/engines/rtk/filters/json-output.json +40 -0
- package/open-sse/services/compression/engines/rtk/filters/kubectl.json +99 -0
- package/open-sse/services/compression/engines/rtk/filters/make.json +38 -0
- package/open-sse/services/compression/engines/rtk/filters/mypy.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/npm-audit.json +43 -0
- package/open-sse/services/compression/engines/rtk/filters/npm-install.json +41 -0
- package/open-sse/services/compression/engines/rtk/filters/nx.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/pip.json +41 -0
- package/open-sse/services/compression/engines/rtk/filters/playwright.json +42 -0
- package/open-sse/services/compression/engines/rtk/filters/poetry-install.json +35 -0
- package/open-sse/services/compression/engines/rtk/filters/prettier.json +40 -0
- package/open-sse/services/compression/engines/rtk/filters/ps.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/rsync.json +33 -0
- package/open-sse/services/compression/engines/rtk/filters/rubocop.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/ruff.json +40 -0
- package/open-sse/services/compression/engines/rtk/filters/shell-find.json +33 -0
- package/open-sse/services/compression/engines/rtk/filters/shell-grep.json +37 -0
- package/open-sse/services/compression/engines/rtk/filters/shell-ls.json +33 -0
- package/open-sse/services/compression/engines/rtk/filters/ssh.json +43 -0
- package/open-sse/services/compression/engines/rtk/filters/systemctl-status.json +43 -0
- package/open-sse/services/compression/engines/rtk/filters/terraform-plan.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/test-cargo.json +42 -0
- package/open-sse/services/compression/engines/rtk/filters/test-go.json +41 -0
- package/open-sse/services/compression/engines/rtk/filters/test-jest.json +43 -0
- package/open-sse/services/compression/engines/rtk/filters/test-pytest.json +41 -0
- package/open-sse/services/compression/engines/rtk/filters/test-vitest.json +43 -0
- package/open-sse/services/compression/engines/rtk/filters/tofu-plan.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/turbo.json +34 -0
- package/open-sse/services/compression/engines/rtk/filters/uv-sync.json +35 -0
- package/open-sse/services/compression/engines/rtk/filters/wget.json +34 -0
- package/open-sse/services/compression/engines/rtk/grouper.ts +99 -0
- package/open-sse/services/compression/engines/rtk/index.ts +706 -0
- package/open-sse/services/compression/engines/rtk/learn.ts +290 -0
- package/open-sse/services/compression/engines/rtk/lineFilter.ts +178 -0
- package/open-sse/services/compression/engines/rtk/rawOutput.ts +208 -0
- package/open-sse/services/compression/engines/rtk/renderers/gitDiff.ts +39 -0
- package/open-sse/services/compression/engines/rtk/renderers/index.ts +50 -0
- package/open-sse/services/compression/engines/rtk/renderers/structuredTable.ts +96 -0
- package/open-sse/services/compression/engines/rtk/renderers/terraformPlan.ts +40 -0
- package/open-sse/services/compression/engines/rtk/renderers/testGreen.ts +70 -0
- package/open-sse/services/compression/engines/rtk/renderers/types.ts +13 -0
- package/open-sse/services/compression/engines/rtk/smartTruncate.ts +67 -0
- package/open-sse/services/compression/engines/rtk/splitCompositeCommand.ts +111 -0
- package/open-sse/services/compression/engines/rtk/verify.ts +107 -0
- package/open-sse/services/compression/engines/session-dedup/fuzzy.ts +153 -0
- package/open-sse/services/compression/engines/session-dedup/index.ts +435 -0
- package/open-sse/services/compression/engines/types.ts +77 -0
- package/open-sse/services/compression/entrypointWrap.ts +43 -0
- package/open-sse/services/compression/eval/aggregate.ts +62 -0
- package/open-sse/services/compression/eval/corpus.ts +45 -0
- package/open-sse/services/compression/eval/costMeter.ts +31 -0
- package/open-sse/services/compression/eval/executorModelClient.ts +44 -0
- package/open-sse/services/compression/eval/fidelityCheck.ts +61 -0
- package/open-sse/services/compression/eval/grader.ts +27 -0
- package/open-sse/services/compression/eval/judge.ts +68 -0
- package/open-sse/services/compression/eval/report.ts +44 -0
- package/open-sse/services/compression/eval/runner.ts +135 -0
- package/open-sse/services/compression/eval/savings.ts +30 -0
- package/open-sse/services/compression/eval/seedCorpus.ts +60 -0
- package/open-sse/services/compression/eval/types.ts +65 -0
- package/open-sse/services/compression/fidelityGate.ts +124 -0
- package/open-sse/services/compression/fidelityGateStep.ts +44 -0
- package/open-sse/services/compression/hardBudget.ts +196 -0
- package/open-sse/services/compression/harness/benchmark.ts +318 -0
- package/open-sse/services/compression/harness/budgetGate.ts +62 -0
- package/open-sse/services/compression/harness/index.ts +52 -0
- package/open-sse/services/compression/harness/measure.ts +98 -0
- package/open-sse/services/compression/harness/replay.ts +72 -0
- package/open-sse/services/compression/harness/runner.ts +58 -0
- package/open-sse/services/compression/index.ts +165 -0
- package/open-sse/services/compression/languageDetector.ts +51 -0
- package/open-sse/services/compression/lite.ts +241 -0
- package/open-sse/services/compression/messageContent.ts +89 -0
- package/open-sse/services/compression/outputMode.ts +175 -0
- package/open-sse/services/compression/outputStyles/apply.ts +135 -0
- package/open-sse/services/compression/outputStyles/backCompat.ts +29 -0
- package/open-sse/services/compression/outputStyles/catalog.ts +76 -0
- package/open-sse/services/compression/outputStyles/telemetry.ts +50 -0
- package/open-sse/services/compression/pipelineEngineBreaker.ts +139 -0
- package/open-sse/services/compression/pipelineGuards.ts +45 -0
- package/open-sse/services/compression/planResolution.ts +128 -0
- package/open-sse/services/compression/prefixFreeze.ts +124 -0
- package/open-sse/services/compression/preservation.ts +0 -0
- package/open-sse/services/compression/preserveSystemPromptMode.ts +72 -0
- package/open-sse/services/compression/progressiveAging.ts +145 -0
- package/open-sse/services/compression/quantumLock/index.ts +17 -0
- package/open-sse/services/compression/quantumLock/quantumLock.ts +58 -0
- package/open-sse/services/compression/quantumLock/quantumLockStep.ts +72 -0
- package/open-sse/services/compression/quantumLock/quantumPatterns.ts +80 -0
- package/open-sse/services/compression/quantumLock/strategyWrap.ts +72 -0
- package/open-sse/services/compression/resolveCompressionPlan.ts +28 -0
- package/open-sse/services/compression/resultMemo.ts +83 -0
- package/open-sse/services/compression/riskGate/index.ts +3 -0
- package/open-sse/services/compression/riskGate/riskGate.ts +136 -0
- package/open-sse/services/compression/riskGate/riskGateStep.ts +115 -0
- package/open-sse/services/compression/riskGate/riskPatterns.ts +61 -0
- package/open-sse/services/compression/riskGate/strategyWrap.ts +44 -0
- package/open-sse/services/compression/ruleLoader.ts +273 -0
- package/open-sse/services/compression/rules/_schema.json +31 -0
- package/open-sse/services/compression/rules/de/context.json +38 -0
- package/open-sse/services/compression/rules/de/dedup.json +38 -0
- package/open-sse/services/compression/rules/de/filler.json +70 -0
- package/open-sse/services/compression/rules/de/structural.json +30 -0
- package/open-sse/services/compression/rules/de/ultra.json +107 -0
- package/open-sse/services/compression/rules/en/context.json +88 -0
- package/open-sse/services/compression/rules/en/dedup.json +38 -0
- package/open-sse/services/compression/rules/en/filler.json +129 -0
- package/open-sse/services/compression/rules/en/structural.json +102 -0
- package/open-sse/services/compression/rules/en/ultra.json +107 -0
- package/open-sse/services/compression/rules/es/context.json +87 -0
- package/open-sse/services/compression/rules/es/dedup.json +38 -0
- package/open-sse/services/compression/rules/es/filler.json +110 -0
- package/open-sse/services/compression/rules/es/structural.json +105 -0
- package/open-sse/services/compression/rules/es/ultra.json +107 -0
- package/open-sse/services/compression/rules/fr/context.json +38 -0
- package/open-sse/services/compression/rules/fr/dedup.json +38 -0
- package/open-sse/services/compression/rules/fr/filler.json +70 -0
- package/open-sse/services/compression/rules/fr/structural.json +30 -0
- package/open-sse/services/compression/rules/fr/ultra.json +107 -0
- package/open-sse/services/compression/rules/id/context.json +113 -0
- package/open-sse/services/compression/rules/id/dedup.json +38 -0
- package/open-sse/services/compression/rules/id/filler.json +138 -0
- package/open-sse/services/compression/rules/id/structural.json +111 -0
- package/open-sse/services/compression/rules/id/ultra.json +252 -0
- package/open-sse/services/compression/rules/ja/context.json +38 -0
- package/open-sse/services/compression/rules/ja/dedup.json +38 -0
- package/open-sse/services/compression/rules/ja/filler.json +70 -0
- package/open-sse/services/compression/rules/ja/structural.json +30 -0
- package/open-sse/services/compression/rules/ja/ultra.json +86 -0
- package/open-sse/services/compression/rules/pt-BR/context.json +69 -0
- package/open-sse/services/compression/rules/pt-BR/dedup.json +51 -0
- package/open-sse/services/compression/rules/pt-BR/filler.json +151 -0
- package/open-sse/services/compression/rules/pt-BR/structural.json +78 -0
- package/open-sse/services/compression/rules/pt-BR/ultra.json +192 -0
- package/open-sse/services/compression/rules/zh/dedup.json +38 -0
- package/open-sse/services/compression/rules/zh/filler.json +54 -0
- package/open-sse/services/compression/rules/zh/ultra.json +55 -0
- package/open-sse/services/compression/stackedStepCore.ts +87 -0
- package/open-sse/services/compression/stats.ts +59 -0
- package/open-sse/services/compression/strategySelector.ts +1007 -0
- package/open-sse/services/compression/summarizer.ts +244 -0
- package/open-sse/services/compression/toolResultCompressor.ts +283 -0
- package/open-sse/services/compression/types.ts +479 -0
- package/open-sse/services/compression/ultra.ts +321 -0
- package/open-sse/services/compression/ultraHeuristic.ts +157 -0
- package/open-sse/services/compression/validation.ts +432 -0
- package/open-sse/services/contextHandoff.ts +770 -0
- package/open-sse/services/contextManager.ts +617 -0
- package/open-sse/services/credentialGate.ts +84 -0
- package/open-sse/services/crofUsageFetcher.ts +182 -0
- package/open-sse/services/cursorSessionManager.ts +208 -0
- package/open-sse/services/deepseekQuotaFetcher.ts +244 -0
- package/open-sse/services/deviceTracker.ts +307 -0
- package/open-sse/services/emergencyFallback.ts +162 -0
- package/open-sse/services/errorClassifier.ts +198 -0
- package/open-sse/services/evalRouting.ts +276 -0
- package/open-sse/services/freeWebSearch.ts +154 -0
- package/open-sse/services/fusion.ts +337 -0
- package/open-sse/services/geminiRateLimitTracker.ts +145 -0
- package/open-sse/services/geminiThoughtSignatureStore.ts +329 -0
- package/open-sse/services/genericQuotaFetcher.ts +215 -0
- package/open-sse/services/gigachatAuth.ts +113 -0
- package/open-sse/services/githubCopilotModels.ts +162 -0
- package/open-sse/services/gpt5SamplingGuard.ts +81 -0
- package/open-sse/services/grokTlsClient.ts +607 -0
- package/open-sse/services/hfModelSuggestions.ts +63 -0
- package/open-sse/services/inAppLoginService.ts +256 -0
- package/open-sse/services/intentClassifier.ts +665 -0
- package/open-sse/services/ipFilter.ts +340 -0
- package/open-sse/services/keyGroupAuth.ts +84 -0
- package/open-sse/services/kiroModels.ts +195 -0
- package/open-sse/services/manifestAdapter.ts +134 -0
- package/open-sse/services/mimoThinking.ts +54 -0
- package/open-sse/services/model.ts +738 -0
- package/open-sse/services/modelCapabilities.ts +5 -0
- package/open-sse/services/modelDeprecation.ts +173 -0
- package/open-sse/services/modelFamilyFallback.ts +240 -0
- package/open-sse/services/modelStrip.ts +60 -0
- package/open-sse/services/modelscopePolicy.ts +97 -0
- package/open-sse/services/opencodeOllamaUsage.ts +562 -0
- package/open-sse/services/opencodeQuotaFetcher.ts +351 -0
- package/open-sse/services/payloadRules.ts +474 -0
- package/open-sse/services/perplexityTlsClient.ts +593 -0
- package/open-sse/services/pipeline.ts +189 -0
- package/open-sse/services/provider.ts +460 -0
- package/open-sse/services/providerCooldownTracker.ts +198 -0
- package/open-sse/services/providerCostData.ts +52 -0
- package/open-sse/services/providerDefaultRateLimit.ts +94 -0
- package/open-sse/services/providerRequestDefaults.ts +88 -0
- package/open-sse/services/proxyAutoSelector.ts +58 -0
- package/open-sse/services/qoderCli.ts +987 -0
- package/open-sse/services/qoderCliResolve.ts +111 -0
- package/open-sse/services/quotaFetchThrottle.ts +118 -0
- package/open-sse/services/quotaMonitor.ts +377 -0
- package/open-sse/services/quotaPreflight.ts +349 -0
- package/open-sse/services/qwenThinking.ts +44 -0
- package/open-sse/services/rateLimitManager/headers.ts +94 -0
- package/open-sse/services/rateLimitManager.ts +945 -0
- package/open-sse/services/rateLimitSemaphore.ts +231 -0
- package/open-sse/services/reasoningCache.ts +514 -0
- package/open-sse/services/reasoningTokenBuffer.ts +50 -0
- package/open-sse/services/refreshSerializer.ts +131 -0
- package/open-sse/services/requestDedup.ts +127 -0
- package/open-sse/services/responseModelEcho.ts +79 -0
- package/open-sse/services/responsesInputSanitizer.ts +145 -0
- package/open-sse/services/retryAfterJson.ts +44 -0
- package/open-sse/services/roleNormalizer.ts +283 -0
- package/open-sse/services/searchCache.ts +150 -0
- package/open-sse/services/sessionManager.ts +380 -0
- package/open-sse/services/sessionPool/fingerprintRotator.ts +126 -0
- package/open-sse/services/sessionPool/index.ts +28 -0
- package/open-sse/services/sessionPool/poolRegistry.ts +93 -0
- package/open-sse/services/sessionPool/session.ts +119 -0
- package/open-sse/services/sessionPool/sessionFactory.ts +57 -0
- package/open-sse/services/sessionPool/sessionPool.ts +326 -0
- package/open-sse/services/sessionPool/types.ts +100 -0
- package/open-sse/services/sessionPool/webExecutorWrapper.ts +132 -0
- package/open-sse/services/signatureCache.ts +189 -0
- package/open-sse/services/slidingWindowLimiter.ts +84 -0
- package/open-sse/services/specificityDetector.ts +89 -0
- package/open-sse/services/specificityRules.ts +257 -0
- package/open-sse/services/specificityTypes.ts +43 -0
- package/open-sse/services/streamRecovery.ts +545 -0
- package/open-sse/services/systemPrompt.ts +187 -0
- package/open-sse/services/systemTransforms.ts +532 -0
- package/open-sse/services/taskAwareRouter.ts +326 -0
- package/open-sse/services/taskAwareRouting.ts +553 -0
- package/open-sse/services/thinkingBudget.ts +391 -0
- package/open-sse/services/tierConfig.ts +124 -0
- package/open-sse/services/tierDefaults.json +26 -0
- package/open-sse/services/tierResolver.ts +144 -0
- package/open-sse/services/tierTypes.ts +40 -0
- package/open-sse/services/tlsClientProxy.ts +30 -0
- package/open-sse/services/tokenExtractionConfig.ts +398 -0
- package/open-sse/services/tokenLimitCounter.ts +342 -0
- package/open-sse/services/tokenRefresh.ts +2180 -0
- package/open-sse/services/toolLatencyTracker.ts +89 -0
- package/open-sse/services/toolLimitDetector.ts +87 -0
- package/open-sse/services/toolSchemaSanitizer.ts +165 -0
- package/open-sse/services/usage/antigravity.ts +794 -0
- package/open-sse/services/usage/claude.ts +216 -0
- package/open-sse/services/usage/codebuddy-cn.ts +199 -0
- package/open-sse/services/usage/codex.ts +75 -0
- package/open-sse/services/usage/cursor.ts +161 -0
- package/open-sse/services/usage/glm.ts +220 -0
- package/open-sse/services/usage/kimi.ts +209 -0
- package/open-sse/services/usage/kiro.ts +243 -0
- package/open-sse/services/usage/minimax.ts +346 -0
- package/open-sse/services/usage/quota.ts +85 -0
- package/open-sse/services/usage/scalars.ts +75 -0
- package/open-sse/services/usage.ts +1058 -0
- package/open-sse/services/volumeDetector.ts +224 -0
- package/open-sse/services/webSearchFallback.ts +246 -0
- package/open-sse/services/webSearchRouting.ts +68 -0
- package/open-sse/services/webSessionPoolHealth.ts +287 -0
- package/open-sse/services/wildcardRouter.ts +136 -0
- package/open-sse/services/workflowFSM.ts +340 -0
- package/open-sse/shared/zedAuth.ts +535 -0
- package/open-sse/transformer/responsesTransformer.ts +667 -0
- package/open-sse/translator/bootstrap.ts +27 -0
- package/open-sse/translator/deepseekWebTools.ts +486 -0
- package/open-sse/translator/formats.ts +12 -0
- package/open-sse/translator/helpers/claudeHelper.ts +563 -0
- package/open-sse/translator/helpers/geminiHelper.ts +657 -0
- package/open-sse/translator/helpers/geminiToolsSanitizer.ts +255 -0
- package/open-sse/translator/helpers/jsonUtil.ts +18 -0
- package/open-sse/translator/helpers/maxTokensHelper.ts +21 -0
- package/open-sse/translator/helpers/openaiHelper.ts +244 -0
- package/open-sse/translator/helpers/responsesApiHelper.ts +9 -0
- package/open-sse/translator/helpers/schemaCoercion.ts +408 -0
- package/open-sse/translator/helpers/toolCallHelper.ts +224 -0
- package/open-sse/translator/helpers/toolCallShim.ts +113 -0
- package/open-sse/translator/image/sizeMapper.ts +22 -0
- package/open-sse/translator/index.ts +614 -0
- package/open-sse/translator/paramSupport.ts +70 -0
- package/open-sse/translator/registry.ts +41 -0
- package/open-sse/translator/request/antigravity-to-openai.ts +357 -0
- package/open-sse/translator/request/claude-to-gemini.ts +236 -0
- package/open-sse/translator/request/claude-to-openai.ts +534 -0
- package/open-sse/translator/request/gemini-to-openai.ts +193 -0
- package/open-sse/translator/request/openai-responses/helpers.ts +69 -0
- package/open-sse/translator/request/openai-responses/toResponses.ts +357 -0
- package/open-sse/translator/request/openai-responses.ts +533 -0
- package/open-sse/translator/request/openai-to-claude/thinkingBudget.ts +89 -0
- package/open-sse/translator/request/openai-to-claude/toolResultAdjacency.ts +106 -0
- package/open-sse/translator/request/openai-to-claude.ts +747 -0
- package/open-sse/translator/request/openai-to-cursor.ts +226 -0
- package/open-sse/translator/request/openai-to-gemini/helpers.ts +142 -0
- package/open-sse/translator/request/openai-to-gemini.ts +753 -0
- package/open-sse/translator/request/openai-to-kiro/messageHelpers.ts +109 -0
- package/open-sse/translator/request/openai-to-kiro.ts +889 -0
- package/open-sse/translator/response/claude-to-openai.ts +327 -0
- package/open-sse/translator/response/cursor-to-openai.ts +30 -0
- package/open-sse/translator/response/gemini-to-claude.ts +199 -0
- package/open-sse/translator/response/gemini-to-openai.ts +765 -0
- package/open-sse/translator/response/kiro-to-openai.ts +211 -0
- package/open-sse/translator/response/openai-responses/pureHelpers.ts +92 -0
- package/open-sse/translator/response/openai-responses.ts +1011 -0
- package/open-sse/translator/response/openai-to-antigravity.ts +145 -0
- package/open-sse/translator/response/openai-to-claude.ts +289 -0
- package/open-sse/translator/response/openai-to-gemini-sse.ts +431 -0
- package/open-sse/translator/webTools.ts +531 -0
- package/open-sse/tsconfig.json +25 -0
- package/open-sse/types.d.ts +139 -0
- package/open-sse/utils/agentGoalPolicy.ts +112 -0
- package/open-sse/utils/aiSdkCompat.ts +179 -0
- package/open-sse/utils/anthropicHost.ts +25 -0
- package/open-sse/utils/awsSigV4.ts +139 -0
- package/open-sse/utils/bypassHandler.ts +300 -0
- package/open-sse/utils/cacheControlPolicy.ts +374 -0
- package/open-sse/utils/claudeCodeMetaRequests.ts +87 -0
- package/open-sse/utils/clinepassEnvelope.ts +59 -0
- package/open-sse/utils/comfyuiClient.ts +126 -0
- package/open-sse/utils/composerToolCalls.ts +307 -0
- package/open-sse/utils/cors.ts +13 -0
- package/open-sse/utils/cursorAgentProtobuf/wire.ts +143 -0
- package/open-sse/utils/cursorAgentProtobuf.ts +1400 -0
- package/open-sse/utils/cursorChecksum.ts +139 -0
- package/open-sse/utils/cursorImages.ts +354 -0
- package/open-sse/utils/cursorVersionDetector.ts +74 -0
- package/open-sse/utils/diagnostics.ts +288 -0
- package/open-sse/utils/earlyStreamKeepalive.ts +228 -0
- package/open-sse/utils/error.ts +515 -0
- package/open-sse/utils/estimateSize.ts +35 -0
- package/open-sse/utils/finishReason.ts +36 -0
- package/open-sse/utils/flattenToolHistory.ts +116 -0
- package/open-sse/utils/headers.ts +61 -0
- package/open-sse/utils/heapPressure.ts +81 -0
- package/open-sse/utils/jsonToSse.ts +127 -0
- package/open-sse/utils/keepaliveThreshold.ts +62 -0
- package/open-sse/utils/kieTask.ts +127 -0
- package/open-sse/utils/kiroSanitizer.ts +127 -0
- package/open-sse/utils/logger.ts +190 -0
- package/open-sse/utils/networkProxy.ts +75 -0
- package/open-sse/utils/noThinkingAlias.ts +153 -0
- package/open-sse/utils/number.ts +4 -0
- package/open-sse/utils/ollamaTransform.ts +122 -0
- package/open-sse/utils/opencodeHeaders.ts +98 -0
- package/open-sse/utils/passthroughTailProcessor.ts +299 -0
- package/open-sse/utils/progressTracker.ts +112 -0
- package/open-sse/utils/providerRequestLogging.ts +272 -0
- package/open-sse/utils/proxyDispatcher.ts +507 -0
- package/open-sse/utils/proxyDispatcherCache.ts +124 -0
- package/open-sse/utils/proxyFallback.ts +418 -0
- package/open-sse/utils/proxyFamily.ts +24 -0
- package/open-sse/utils/proxyFamilyResolve.ts +39 -0
- package/open-sse/utils/proxyFetch.ts +666 -0
- package/open-sse/utils/publicCreds.ts +215 -0
- package/open-sse/utils/reasoningContentInjector.ts +73 -0
- package/open-sse/utils/reasoningFields.ts +72 -0
- package/open-sse/utils/requestLogger.ts +386 -0
- package/open-sse/utils/responsesInputNormalization.ts +94 -0
- package/open-sse/utils/responsesStatePolicy.ts +75 -0
- package/open-sse/utils/responsesStreamHelpers.ts +166 -0
- package/open-sse/utils/setupPolyfill.ts +33 -0
- package/open-sse/utils/sha3-512.ts +164 -0
- package/open-sse/utils/sleep.ts +9 -0
- package/open-sse/utils/socksConnectorWithFamily.ts +89 -0
- package/open-sse/utils/sseHeartbeat.ts +119 -0
- package/open-sse/utils/stream.ts +2791 -0
- package/open-sse/utils/streamFailureFinalization.ts +174 -0
- package/open-sse/utils/streamHandler.ts +680 -0
- package/open-sse/utils/streamHelpers.ts +460 -0
- package/open-sse/utils/streamPayloadCollector.ts +700 -0
- package/open-sse/utils/streamReadiness.ts +385 -0
- package/open-sse/utils/streamReadinessPolicy.ts +132 -0
- package/open-sse/utils/textualToolCall.ts +112 -0
- package/open-sse/utils/thinkCloseMarker.ts +77 -0
- package/open-sse/utils/thinkTagParser.ts +146 -0
- package/open-sse/utils/tlsClient.ts +243 -0
- package/open-sse/utils/toolCallArguments.ts +32 -0
- package/open-sse/utils/toolSources.ts +69 -0
- package/open-sse/utils/upstreamResponseHeaders.ts +47 -0
- package/open-sse/utils/urlSanitize.ts +24 -0
- package/open-sse/utils/usageTracking.ts +596 -0
- package/package.json +393 -0
- package/scripts/build/build-next-isolated.mjs +322 -0
- package/scripts/build/colocateOptionals.mjs +144 -0
- package/scripts/build/native-binary-compat.mjs +167 -0
- package/scripts/build/postinstall.mjs +357 -0
- package/scripts/build/postinstallSupport.mjs +45 -0
- package/scripts/build/runtime-env.mjs +140 -0
- package/scripts/build/sync-env.mjs +3 -0
- package/scripts/check/check-supported-node-runtime.ts +20 -0
- package/scripts/dev/responses-ws-proxy.mjs +907 -0
- package/scripts/dev/sync-env.mjs +362 -0
- package/scripts/dev/tls-options.mjs +92 -0
- package/scripts/postinstall.mjs +27 -0
- package/src/domain/assessment/assessor.ts +206 -0
- package/src/domain/assessment/categorizer.ts +118 -0
- package/src/domain/assessment/index.ts +24 -0
- package/src/domain/assessment/migration.ts +111 -0
- package/src/domain/assessment/selfHealer.ts +261 -0
- package/src/domain/assessment/types.ts +361 -0
- package/src/domain/comboResolver.ts +106 -0
- package/src/domain/configAudit.ts +285 -0
- package/src/domain/connectionModelRules.ts +82 -0
- package/src/domain/costRules.ts +631 -0
- package/src/domain/degradation.ts +253 -0
- package/src/domain/fallbackPolicy.ts +160 -0
- package/src/domain/lockoutPolicy.ts +208 -0
- package/src/domain/modelAvailability.ts +41 -0
- package/src/domain/omnirouteResponseMeta.ts +202 -0
- package/src/domain/pipeline.ts +307 -0
- package/src/domain/policyEngine.ts +195 -0
- package/src/domain/prompts.ts +103 -0
- package/src/domain/providerExpiration.ts +255 -0
- package/src/domain/quotaCache.ts +553 -0
- package/src/domain/responses.ts +139 -0
- package/src/domain/tagRouter.ts +64 -0
- package/src/domain/types.ts +123 -0
- package/src/lib/a2a/README.md +748 -0
- package/src/lib/a2a/routingLogger.ts +67 -0
- package/src/lib/a2a/skills/costAnalysis.ts +162 -0
- package/src/lib/a2a/skills/healthReport.ts +155 -0
- package/src/lib/a2a/skills/listCapabilities.ts +72 -0
- package/src/lib/a2a/skills/providerDiscovery.ts +202 -0
- package/src/lib/a2a/skills/quotaManagement.ts +121 -0
- package/src/lib/a2a/skills/smartRouting.ts +89 -0
- package/src/lib/a2a/streaming.ts +149 -0
- package/src/lib/a2a/taskExecution.ts +64 -0
- package/src/lib/a2a/taskManager.ts +240 -0
- package/src/lib/accessTokens/scopes.ts +51 -0
- package/src/lib/acp/index.ts +11 -0
- package/src/lib/acp/manager.ts +202 -0
- package/src/lib/acp/registry.ts +384 -0
- package/src/lib/agentSkills/catalog.ts +256 -0
- package/src/lib/agentSkills/cliRegistryParser.ts +242 -0
- package/src/lib/agentSkills/generator.ts +367 -0
- package/src/lib/agentSkills/openapiParser.ts +201 -0
- package/src/lib/agentSkills/schemas.ts +36 -0
- package/src/lib/agentSkills/types.ts +101 -0
- package/src/lib/api/comboErrorResponse.ts +80 -0
- package/src/lib/api/errorResponse.ts +54 -0
- package/src/lib/api/modelTestRunner.ts +390 -0
- package/src/lib/api/proxyRegistryRouteHandlers.ts +142 -0
- package/src/lib/api/requireCliToolsAuth.ts +5 -0
- package/src/lib/api/requireManagementAuth.ts +118 -0
- package/src/lib/api/serverErrorMessage.ts +36 -0
- package/src/lib/apiBridgeServer.ts +233 -0
- package/src/lib/apiKeyExposure.ts +19 -0
- package/src/lib/arenaEloSync.ts +564 -0
- package/src/lib/audit/activityIcons.ts +69 -0
- package/src/lib/audit/highLevelActions.ts +57 -0
- package/src/lib/audit/timeline.ts +132 -0
- package/src/lib/auth/managementPassword.ts +117 -0
- package/src/lib/batches/costEstimator.ts +130 -0
- package/src/lib/batches/csvToJsonl.ts +248 -0
- package/src/lib/batches/dispatch.ts +53 -0
- package/src/lib/batches/retryFailed.ts +77 -0
- package/src/lib/batches/schemas.ts +38 -0
- package/src/lib/batches/types.ts +78 -0
- package/src/lib/batches/validateJsonl.ts +154 -0
- package/src/lib/benchmarks.ts +33 -0
- package/src/lib/build-profile/featureDisabled.ts +23 -0
- package/src/lib/cacheControlSettings.ts +25 -0
- package/src/lib/cacheLayer.ts +220 -0
- package/src/lib/catalog/openrouterCatalog.ts +177 -0
- package/src/lib/cli-helper/claudeProfileAutoSync.ts +92 -0
- package/src/lib/cli-helper/codexProfileAutoSync.ts +80 -0
- package/src/lib/cli-helper/config-generator/claude.ts +25 -0
- package/src/lib/cli-helper/config-generator/cline.ts +23 -0
- package/src/lib/cli-helper/config-generator/codex.ts +34 -0
- package/src/lib/cli-helper/config-generator/continue.ts +37 -0
- package/src/lib/cli-helper/config-generator/hermes-agent.ts +210 -0
- package/src/lib/cli-helper/config-generator/hermes.ts +47 -0
- package/src/lib/cli-helper/config-generator/hermesHome.ts +40 -0
- package/src/lib/cli-helper/config-generator/index.ts +124 -0
- package/src/lib/cli-helper/config-generator/kilocode.ts +23 -0
- package/src/lib/cli-helper/config-generator/opencode.ts +443 -0
- package/src/lib/cli-helper/doctor/checks.ts +41 -0
- package/src/lib/cli-helper/log-streamer.ts +78 -0
- package/src/lib/cli-helper/tool-detector.ts +165 -0
- package/src/lib/cliTools/batchStatusCache.ts +47 -0
- package/src/lib/cliTools/checkToolConfigStatus.ts +112 -0
- package/src/lib/cloudAgent/agents/codex.ts +148 -0
- package/src/lib/cloudAgent/agents/cursor.ts +205 -0
- package/src/lib/cloudAgent/agents/devin.ts +137 -0
- package/src/lib/cloudAgent/agents/jules.ts +354 -0
- package/src/lib/cloudAgent/api.ts +86 -0
- package/src/lib/cloudAgent/baseAgent.ts +98 -0
- package/src/lib/cloudAgent/credentials.ts +89 -0
- package/src/lib/cloudAgent/db.ts +145 -0
- package/src/lib/cloudAgent/index.ts +8 -0
- package/src/lib/cloudAgent/julesApi.ts +7 -0
- package/src/lib/cloudAgent/registry.ts +29 -0
- package/src/lib/cloudAgent/types.ts +90 -0
- package/src/lib/cloudSync.stub.ts +31 -0
- package/src/lib/cloudSync.ts +210 -0
- package/src/lib/cloudflaredTunnel.ts +933 -0
- package/src/lib/combos/autoPromote.ts +92 -0
- package/src/lib/combos/builderDraft.ts +242 -0
- package/src/lib/combos/builderOptions.ts +681 -0
- package/src/lib/combos/compositeTiers.ts +200 -0
- package/src/lib/combos/controlCenter.ts +271 -0
- package/src/lib/combos/intelligentRouting.ts +192 -0
- package/src/lib/combos/steps.ts +322 -0
- package/src/lib/combos/testHealth.ts +190 -0
- package/src/lib/compliance/index.ts +637 -0
- package/src/lib/compliance/noLog.ts +86 -0
- package/src/lib/compliance/providerAudit.ts +98 -0
- package/src/lib/compression/judgeModelClient.ts +43 -0
- package/src/lib/config/hotReload.ts +115 -0
- package/src/lib/config/runtimeSettings.ts +551 -0
- package/src/lib/consoleInterceptor.ts +136 -0
- package/src/lib/container.ts +122 -0
- package/src/lib/contextWindowResolver.ts +147 -0
- package/src/lib/copilot/codegraphKnowledge.ts +229 -0
- package/src/lib/copilot/engine.ts +372 -0
- package/src/lib/copilot/systemPrompt.ts +189 -0
- package/src/lib/copilot/tools.ts +415 -0
- package/src/lib/credentialHealth/cache.ts +206 -0
- package/src/lib/credentialHealth/scheduler.ts +328 -0
- package/src/lib/dataPaths.ts +122 -0
- package/src/lib/db/AGENTS.md +70 -0
- package/src/lib/db/_rowTypes.ts +45 -0
- package/src/lib/db/accessTokens.ts +183 -0
- package/src/lib/db/adapters/betterSqliteAdapter.ts +58 -0
- package/src/lib/db/adapters/driverFactory.ts +98 -0
- package/src/lib/db/adapters/nodeSqliteAdapter.ts +60 -0
- package/src/lib/db/adapters/nodeSqliteShared.ts +148 -0
- package/src/lib/db/adapters/sqljsAdapter.ts +232 -0
- package/src/lib/db/adapters/types.ts +34 -0
- package/src/lib/db/agentBridgeBypass.ts +81 -0
- package/src/lib/db/agentBridgeMappings.ts +47 -0
- package/src/lib/db/agentBridgeState.ts +115 -0
- package/src/lib/db/apiKeyColumnFallbacks.ts +47 -0
- package/src/lib/db/apiKeyContextSources.ts +107 -0
- package/src/lib/db/apiKeyGroups.ts +318 -0
- package/src/lib/db/apiKeyUsageLimitFields.ts +65 -0
- package/src/lib/db/apiKeys/modelPermissions.ts +122 -0
- package/src/lib/db/apiKeys/rowParsers.ts +161 -0
- package/src/lib/db/apiKeys/types.ts +21 -0
- package/src/lib/db/apiKeys.ts +1492 -0
- package/src/lib/db/backup.ts +708 -0
- package/src/lib/db/batches.ts +450 -0
- package/src/lib/db/callLogStats.ts +235 -0
- package/src/lib/db/caseMapping.ts +80 -0
- package/src/lib/db/cleanup.ts +584 -0
- package/src/lib/db/cliToolState.ts +159 -0
- package/src/lib/db/comboForecast.ts +119 -0
- package/src/lib/db/combos.ts +339 -0
- package/src/lib/db/commandCodeAuth.ts +209 -0
- package/src/lib/db/compression.ts +780 -0
- package/src/lib/db/compressionAnalytics.ts +661 -0
- package/src/lib/db/compressionCacheStats.ts +112 -0
- package/src/lib/db/compressionCombos.ts +491 -0
- package/src/lib/db/compressionRunTelemetry.ts +126 -0
- package/src/lib/db/contextHandoffs.ts +233 -0
- package/src/lib/db/core.ts +1520 -0
- package/src/lib/db/creditBalance.ts +92 -0
- package/src/lib/db/databaseSettings.ts +309 -0
- package/src/lib/db/detailedLogs.ts +170 -0
- package/src/lib/db/discovery.ts +97 -0
- package/src/lib/db/discoveryResults.ts +176 -0
- package/src/lib/db/domainState.ts +630 -0
- package/src/lib/db/encryption.ts +327 -0
- package/src/lib/db/evals.ts +786 -0
- package/src/lib/db/featureFlags.ts +82 -0
- package/src/lib/db/files.ts +170 -0
- package/src/lib/db/freeProxies.ts +360 -0
- package/src/lib/db/gamification.ts +613 -0
- package/src/lib/db/healthCheck.ts +565 -0
- package/src/lib/db/inspectorCustomHosts.ts +88 -0
- package/src/lib/db/inspectorSessions.ts +161 -0
- package/src/lib/db/jsonMigration.ts +321 -0
- package/src/lib/db/memoryVec.ts +138 -0
- package/src/lib/db/middleware.ts +239 -0
- package/src/lib/db/migrationRunner/constants.ts +118 -0
- package/src/lib/db/migrationRunner.ts +1069 -0
- package/src/lib/db/migrations/001_initial_schema.sql +226 -0
- package/src/lib/db/migrations/002_mcp_a2a_tables.sql +95 -0
- package/src/lib/db/migrations/003_provider_node_custom_paths.sql +5 -0
- package/src/lib/db/migrations/004_proxy_registry.sql +31 -0
- package/src/lib/db/migrations/005_combo_agent_fields.sql +19 -0
- package/src/lib/db/migrations/006_detailed_request_logs.sql +42 -0
- package/src/lib/db/migrations/007_search_request_type.sql +4 -0
- package/src/lib/db/migrations/008_registered_keys.sql +65 -0
- package/src/lib/db/migrations/009_requested_model.sql +9 -0
- package/src/lib/db/migrations/010_model_combo_mappings.sql +19 -0
- package/src/lib/db/migrations/011_webhooks.sql +15 -0
- package/src/lib/db/migrations/012_fix_token_input_cache_tokens.sql +15 -0
- package/src/lib/db/migrations/013_quota_snapshots.sql +19 -0
- package/src/lib/db/migrations/014_unified_log_artifacts.sql +15 -0
- package/src/lib/db/migrations/015_create_memories.sql +22 -0
- package/src/lib/db/migrations/016_create_skills.sql +37 -0
- package/src/lib/db/migrations/017_version_manager_upstream_proxy.sql +56 -0
- package/src/lib/db/migrations/018_call_logs_detailed_tokens.sql +6 -0
- package/src/lib/db/migrations/019_context_handoffs.sql +28 -0
- package/src/lib/db/migrations/020_combo_sort_order.sql +16 -0
- package/src/lib/db/migrations/021_combo_call_log_targets.sql +5 -0
- package/src/lib/db/migrations/022_add_memory_fts5.sql +33 -0
- package/src/lib/db/migrations/023_fix_memory_fts_uuid.sql +55 -0
- package/src/lib/db/migrations/024_create_sync_tokens.sql +15 -0
- package/src/lib/db/migrations/025_call_logs_summary_storage.sql +145 -0
- package/src/lib/db/migrations/027_skill_mode_and_metadata.sql +10 -0
- package/src/lib/db/migrations/028_create_files_and_batches.sql +51 -0
- package/src/lib/db/migrations/029_provider_connection_max_concurrent.sql +10 -0
- package/src/lib/db/migrations/030_create_eval_runs.sql +28 -0
- package/src/lib/db/migrations/031_create_eval_suites.sql +27 -0
- package/src/lib/db/migrations/032_apikey_lifecycle.sql +24 -0
- package/src/lib/db/migrations/033_create_reasoning_cache.sql +27 -0
- package/src/lib/db/migrations/034_compression_settings.sql +10 -0
- package/src/lib/db/migrations/035_standard_compression_config.sql +9 -0
- package/src/lib/db/migrations/036_aggressive_compression.sql +4 -0
- package/src/lib/db/migrations/037_ultra_compression.sql +6 -0
- package/src/lib/db/migrations/038_compression_analytics.sql +13 -0
- package/src/lib/db/migrations/039_compression_cache_stats.sql +15 -0
- package/src/lib/db/migrations/040_oneproxy_proxy_fields.sql +24 -0
- package/src/lib/db/migrations/041_compression_receipts.sql +18 -0
- package/src/lib/db/migrations/042_compression_combos.sql +43 -0
- package/src/lib/db/migrations/043_default_compression_combo_pipeline.sql +12 -0
- package/src/lib/db/migrations/044_usage_history_api_key_backfill.sql +27 -0
- package/src/lib/db/migrations/045_compression_tokens.sql +4 -0
- package/src/lib/db/migrations/046_database_settings.sql +44 -0
- package/src/lib/db/migrations/047_aggregation_tables.sql +44 -0
- package/src/lib/db/migrations/048_summary_indexes.sql +29 -0
- package/src/lib/db/migrations/049_compression_analytics_indexes.sql +10 -0
- package/src/lib/db/migrations/050_session_account_affinity.sql +11 -0
- package/src/lib/db/migrations/051_hot_path_db_indexes.sql +10 -0
- package/src/lib/db/migrations/052_api_key_combo_throttle.sql +4 -0
- package/src/lib/db/migrations/053_remove_status_from_files.sql +2 -0
- package/src/lib/db/migrations/054_usage_history_service_tier.sql +4 -0
- package/src/lib/db/migrations/056_mcp_accessibility_compression.sql +8 -0
- package/src/lib/db/migrations/057_provider_connection_quota_window_thresholds.sql +14 -0
- package/src/lib/db/migrations/058_command_code_auth_sessions.sql +19 -0
- package/src/lib/db/migrations/059_manifest_routing.sql +21 -0
- package/src/lib/db/migrations/060_create_gamification.sql +94 -0
- package/src/lib/db/migrations/061_cloud_agent_credentials.sql +9 -0
- package/src/lib/db/migrations/062_usage_history_combo_strategy.sql +4 -0
- package/src/lib/db/migrations/063_add_last_model_to_handoffs.sql +7 -0
- package/src/lib/db/migrations/064_create_session_model_history.sql +19 -0
- package/src/lib/db/migrations/065_middleware_hooks.sql +45 -0
- package/src/lib/db/migrations/066_api_key_groups.sql +47 -0
- package/src/lib/db/migrations/067_relay_proxies.sql +56 -0
- package/src/lib/db/migrations/068_free_proxies.sql +52 -0
- package/src/lib/db/migrations/069_webhook_deliveries.sql +15 -0
- package/src/lib/db/migrations/070_webhooks_kind_metadata.sql +3 -0
- package/src/lib/db/migrations/071_services.sql +19 -0
- package/src/lib/db/migrations/072_free_proxies_fk.sql +27 -0
- package/src/lib/db/migrations/073_per_model_token_limits.sql +61 -0
- package/src/lib/db/migrations/074_discovery_results.sql +20 -0
- package/src/lib/db/migrations/075_api_key_self_service_usage_scopes.sql +51 -0
- package/src/lib/db/migrations/076_create_plugins.sql +31 -0
- package/src/lib/db/migrations/077_api_key_stream_default_mode.sql +3 -0
- package/src/lib/db/migrations/078_quota_consumption.sql +24 -0
- package/src/lib/db/migrations/079_provider_plans.sql +19 -0
- package/src/lib/db/migrations/080_agent_bridge.sql +24 -0
- package/src/lib/db/migrations/081_inspector_custom_hosts.sql +11 -0
- package/src/lib/db/migrations/082_inspector_sessions.sql +18 -0
- package/src/lib/db/migrations/083_memory_vec.sql +26 -0
- package/src/lib/db/migrations/084_playground_presets.sql +15 -0
- package/src/lib/db/migrations/085_quota_pools.sql +39 -0
- package/src/lib/db/migrations/086_api_key_allowed_quotas.sql +8 -0
- package/src/lib/db/migrations/087_quota_pool_connections.sql +21 -0
- package/src/lib/db/migrations/088_quota_groups.sql +28 -0
- package/src/lib/db/migrations/089_add_disable_non_public_to_api_keys.sql +3 -0
- package/src/lib/db/migrations/090_plugin_metrics.sql +9 -0
- package/src/lib/db/migrations/091_plugin_analytics.sql +23 -0
- package/src/lib/db/migrations/092_api_key_context_sources.sql +13 -0
- package/src/lib/db/migrations/093_proxy_enable_toggles.sql +17 -0
- package/src/lib/db/migrations/094_per_key_proxy_toggles.sql +12 -0
- package/src/lib/db/migrations/095_provider_node_custom_headers.sql +5 -0
- package/src/lib/db/migrations/096_sync_context_cache_protection.sql +9 -0
- package/src/lib/db/migrations/097_model_intelligence.sql +25 -0
- package/src/lib/db/migrations/098_clear_semantic_cache_for_key_isolation.sql +4 -0
- package/src/lib/db/migrations/099_proxy_family.sql +5 -0
- package/src/lib/db/migrations/100_cli_access_tokens.sql +18 -0
- package/src/lib/db/migrations/101_api_key_usage_limits.sql +4 -0
- package/src/lib/db/migrations/102_compression_engines_map.sql +5 -0
- package/src/lib/db/migrations/103_strip_legacy_combo_config_keys.sql +54 -0
- package/src/lib/db/migrations/104_normalize_database_cache_size.sql +10 -0
- package/src/lib/db/migrations/105_usage_history_endpoint.sql +6 -0
- package/src/lib/db/migrations/106_quota_allocation_model_caps.sql +32 -0
- package/src/lib/db/migrations/107_quota_combos_quota_share_strategy.sql +25 -0
- package/src/lib/db/migrations/108_provider_quota_reset_events.sql +26 -0
- package/src/lib/db/migrations/109_call_logs_correlation_id.sql +2 -0
- package/src/lib/db/migrations/110_model_context_overrides.sql +17 -0
- package/src/lib/db/migrations/111_memory_typed_decay.sql +10 -0
- package/src/lib/db/migrations/112_batch_item_checkpoints.sql +18 -0
- package/src/lib/db/migrations/113_provider_node_icon_url.sql +5 -0
- package/src/lib/db/migrations/114_mux_service_seed.sql +12 -0
- package/src/lib/db/migrations/115_bifrost_service.sql +9 -0
- package/src/lib/db/migrations/116_call_logs_reasoning_source.sql +13 -0
- package/src/lib/db/migrations/117_proxy_pool_rotation.sql +53 -0
- package/src/lib/db/modelComboMappings.ts +255 -0
- package/src/lib/db/modelContextOverrides.ts +133 -0
- package/src/lib/db/modelIntelligence.ts +232 -0
- package/src/lib/db/models/aliases.ts +61 -0
- package/src/lib/db/models/compat.ts +249 -0
- package/src/lib/db/models/mitmAlias.ts +32 -0
- package/src/lib/db/models/shared.ts +19 -0
- package/src/lib/db/models.ts +936 -0
- package/src/lib/db/notion.ts +48 -0
- package/src/lib/db/obsidian.ts +283 -0
- package/src/lib/db/oneproxy.ts +293 -0
- package/src/lib/db/optimizationSettings.ts +284 -0
- package/src/lib/db/playgroundPresets.ts +167 -0
- package/src/lib/db/pluginMetrics.ts +88 -0
- package/src/lib/db/plugins.ts +280 -0
- package/src/lib/db/prompts.ts +302 -0
- package/src/lib/db/providerLimits.ts +131 -0
- package/src/lib/db/providerNodeSelect.ts +48 -0
- package/src/lib/db/providerPlans.ts +149 -0
- package/src/lib/db/providerStats.ts +68 -0
- package/src/lib/db/providers/columns.ts +116 -0
- package/src/lib/db/providers/nodes.ts +169 -0
- package/src/lib/db/providers/rateLimit.ts +198 -0
- package/src/lib/db/providers.ts +803 -0
- package/src/lib/db/proxies/mappers.ts +181 -0
- package/src/lib/db/proxies/types.ts +78 -0
- package/src/lib/db/proxies.ts +1176 -0
- package/src/lib/db/proxyLogs.ts +35 -0
- package/src/lib/db/quotaConsumption.ts +274 -0
- package/src/lib/db/quotaGroups.ts +147 -0
- package/src/lib/db/quotaModelCaps.ts +128 -0
- package/src/lib/db/quotaPools.ts +548 -0
- package/src/lib/db/quotaResetEvents.ts +334 -0
- package/src/lib/db/quotaSnapshots.ts +209 -0
- package/src/lib/db/readCache.ts +202 -0
- package/src/lib/db/reasoningCache.ts +276 -0
- package/src/lib/db/recovery.ts +33 -0
- package/src/lib/db/registeredKeys.ts +532 -0
- package/src/lib/db/relayProxies.ts +364 -0
- package/src/lib/db/schemaColumns.ts +226 -0
- package/src/lib/db/secrets.ts +28 -0
- package/src/lib/db/semanticCache.ts +117 -0
- package/src/lib/db/serviceModels.ts +79 -0
- package/src/lib/db/sessionAccountAffinity.ts +212 -0
- package/src/lib/db/settings/cacheMetrics.ts +235 -0
- package/src/lib/db/settings/lkgp.ts +49 -0
- package/src/lib/db/settings/pricing.ts +254 -0
- package/src/lib/db/settings/shared.ts +9 -0
- package/src/lib/db/settings.ts +661 -0
- package/src/lib/db/skills.ts +62 -0
- package/src/lib/db/stateReset.ts +30 -0
- package/src/lib/db/stats.ts +71 -0
- package/src/lib/db/syncTokens.ts +163 -0
- package/src/lib/db/tierConfig.ts +88 -0
- package/src/lib/db/tokenLimits.ts +295 -0
- package/src/lib/db/upstreamProxy.ts +247 -0
- package/src/lib/db/usageAnalytics/sources.ts +208 -0
- package/src/lib/db/usageAnalytics.ts +723 -0
- package/src/lib/db/usageLogs.ts +94 -0
- package/src/lib/db/usageSummary.ts +18 -0
- package/src/lib/db/vacuumScheduler.ts +326 -0
- package/src/lib/db/versionManager.ts +377 -0
- package/src/lib/db/webSessionDedup.ts +57 -0
- package/src/lib/db/webhookDeliveries.ts +77 -0
- package/src/lib/db/webhooks.ts +178 -0
- package/src/lib/discovery/index.ts +137 -0
- package/src/lib/display/names.ts +73 -0
- package/src/lib/display/useProviderNodeMap.ts +68 -0
- package/src/lib/docsI18nPath.ts +33 -0
- package/src/lib/docsSanitizer.ts +42 -0
- package/src/lib/embeddings/familyGuard.ts +24 -0
- package/src/lib/embeddings/service.ts +288 -0
- package/src/lib/env/runtimeEnv.ts +166 -0
- package/src/lib/evals/evalRunner/builtinSuites.ts +676 -0
- package/src/lib/evals/evalRunner.ts +301 -0
- package/src/lib/evals/runtime.ts +311 -0
- package/src/lib/events/eventBus.ts +148 -0
- package/src/lib/events/types.ts +181 -0
- package/src/lib/freeProviderRankings.ts +340 -0
- package/src/lib/freeProxyProviders/index.ts +24 -0
- package/src/lib/freeProxyProviders/iplocate.ts +107 -0
- package/src/lib/freeProxyProviders/oneproxy.ts +127 -0
- package/src/lib/freeProxyProviders/proxifly.ts +143 -0
- package/src/lib/freeProxyProviders/types.ts +33 -0
- package/src/lib/freeProxyProviders/webshare.ts +148 -0
- package/src/lib/gamification/antiCheat.ts +160 -0
- package/src/lib/gamification/badges.ts +542 -0
- package/src/lib/gamification/events.ts +187 -0
- package/src/lib/gamification/index.ts +37 -0
- package/src/lib/gamification/invites.ts +127 -0
- package/src/lib/gamification/leaderboard.ts +64 -0
- package/src/lib/gamification/notifications.ts +118 -0
- package/src/lib/gamification/servers.ts +179 -0
- package/src/lib/gamification/sharing.ts +56 -0
- package/src/lib/gamification/streaks.ts +164 -0
- package/src/lib/gamification/xp.ts +152 -0
- package/src/lib/gracefulShutdown.ts +151 -0
- package/src/lib/guardrails/base.ts +65 -0
- package/src/lib/guardrails/index.ts +4 -0
- package/src/lib/guardrails/piiMasker.ts +208 -0
- package/src/lib/guardrails/promptInjection.ts +285 -0
- package/src/lib/guardrails/registry.ts +282 -0
- package/src/lib/guardrails/visionBridge.ts +237 -0
- package/src/lib/guardrails/visionBridgeHelpers.ts +499 -0
- package/src/lib/guardrails/visionBridgeRouter.ts +271 -0
- package/src/lib/headroom/detect.ts +214 -0
- package/src/lib/headroom/process.ts +177 -0
- package/src/lib/idempotencyLayer.ts +105 -0
- package/src/lib/images/imageRouteModel.ts +168 -0
- package/src/lib/initCloudSync.ts +52 -0
- package/src/lib/inspector/agentBridgeMaintenanceApi.ts +70 -0
- package/src/lib/inspector/captureState.ts +90 -0
- package/src/lib/inspector/configPortability.ts +86 -0
- package/src/lib/inspector/harExport.ts +188 -0
- package/src/lib/inspector/matchesTrafficFilter.ts +37 -0
- package/src/lib/inspector/secretMask.ts +6 -0
- package/src/lib/inspector/tproxyCaptureApi.ts +60 -0
- package/src/lib/ipUtils.ts +94 -0
- package/src/lib/jobs/budgetResetJob.ts +40 -0
- package/src/lib/jobs/reasoningCacheCleanupJob.ts +40 -0
- package/src/lib/localDb.ts +797 -0
- package/src/lib/localHealthCheck.ts +267 -0
- package/src/lib/logEnv.ts +164 -0
- package/src/lib/logPayloads.ts +110 -0
- package/src/lib/logRotation.ts +206 -0
- package/src/lib/machineToken.ts +53 -0
- package/src/lib/memory/cache.ts +76 -0
- package/src/lib/memory/embedding/cache.ts +77 -0
- package/src/lib/memory/embedding/index.ts +281 -0
- package/src/lib/memory/embedding/remote.ts +95 -0
- package/src/lib/memory/embedding/staticPotion.ts +253 -0
- package/src/lib/memory/embedding/transformersLocal.ts +153 -0
- package/src/lib/memory/embedding/types.ts +40 -0
- package/src/lib/memory/extraction.ts +195 -0
- package/src/lib/memory/injection.ts +211 -0
- package/src/lib/memory/qdrant.ts +521 -0
- package/src/lib/memory/reindex.ts +102 -0
- package/src/lib/memory/retrieval/scoring.ts +104 -0
- package/src/lib/memory/retrieval.ts +1072 -0
- package/src/lib/memory/schemas.ts +39 -0
- package/src/lib/memory/settings.ts +184 -0
- package/src/lib/memory/store.ts +621 -0
- package/src/lib/memory/summarization.ts +202 -0
- package/src/lib/memory/typedDecay.ts +245 -0
- package/src/lib/memory/types.ts +45 -0
- package/src/lib/memory/vectorStore.ts +425 -0
- package/src/lib/memory/verify.ts +56 -0
- package/src/lib/middleware/cliTokenAuth.ts +91 -0
- package/src/lib/middleware/registry.ts +432 -0
- package/src/lib/middleware/types.ts +129 -0
- package/src/lib/modelAliasSeed.ts +82 -0
- package/src/lib/modelCapabilities.ts +481 -0
- package/src/lib/modelMetadataRegistry.ts +574 -0
- package/src/lib/modelsDevSync/transform.ts +279 -0
- package/src/lib/modelsDevSync.ts +677 -0
- package/src/lib/monitoring/comboHealthAutopilot.ts +494 -0
- package/src/lib/monitoring/observability.ts +336 -0
- package/src/lib/monitoring/providerHealthAutopilot.ts +743 -0
- package/src/lib/monitoring/providerHealthMatrix.ts +621 -0
- package/src/lib/ngrokTunnel.ts +142 -0
- package/src/lib/notion/api.ts +247 -0
- package/src/lib/oauth/codexDeviceFlow.ts +316 -0
- package/src/lib/oauth/config/index.ts +31 -0
- package/src/lib/oauth/connectionPersistence.ts +114 -0
- package/src/lib/oauth/constants/oauth.ts +494 -0
- package/src/lib/oauth/credentialBlob.ts +105 -0
- package/src/lib/oauth/deviceFlowTickets.ts +110 -0
- package/src/lib/oauth/gitlab.ts +103 -0
- package/src/lib/oauth/pasteCredentials.ts +51 -0
- package/src/lib/oauth/providers/agy.ts +15 -0
- package/src/lib/oauth/providers/antigravity.ts +159 -0
- package/src/lib/oauth/providers/claude.ts +170 -0
- package/src/lib/oauth/providers/cline.ts +91 -0
- package/src/lib/oauth/providers/codebuddy-cn.ts +123 -0
- package/src/lib/oauth/providers/codex.ts +214 -0
- package/src/lib/oauth/providers/cursor.ts +15 -0
- package/src/lib/oauth/providers/github.ts +89 -0
- package/src/lib/oauth/providers/gitlab-duo.ts +124 -0
- package/src/lib/oauth/providers/grok-cli.ts +155 -0
- package/src/lib/oauth/providers/index.ts +59 -0
- package/src/lib/oauth/providers/kilocode.ts +77 -0
- package/src/lib/oauth/providers/kimi-coding.ts +135 -0
- package/src/lib/oauth/providers/kiro.ts +185 -0
- package/src/lib/oauth/providers/qoder.ts +81 -0
- package/src/lib/oauth/providers/qwen.ts +82 -0
- package/src/lib/oauth/providers/trae.ts +78 -0
- package/src/lib/oauth/providers/windsurf.ts +64 -0
- package/src/lib/oauth/providers/zed-hosted.ts +89 -0
- package/src/lib/oauth/providers/zed.ts +35 -0
- package/src/lib/oauth/providers.ts +278 -0
- package/src/lib/oauth/services/codexImport.ts +222 -0
- package/src/lib/oauth/services/cursor.ts +219 -0
- package/src/lib/oauth/services/kiro.ts +441 -0
- package/src/lib/oauth/utils/agyAuthImport.ts +259 -0
- package/src/lib/oauth/utils/banner.ts +62 -0
- package/src/lib/oauth/utils/claudeAuthFile.ts +334 -0
- package/src/lib/oauth/utils/claudeAuthImport.ts +259 -0
- package/src/lib/oauth/utils/claudeAuthZipExtract.ts +5 -0
- package/src/lib/oauth/utils/cliProxyAuthImport.ts +142 -0
- package/src/lib/oauth/utils/codexAuthFile.ts +352 -0
- package/src/lib/oauth/utils/codexAuthImport.ts +294 -0
- package/src/lib/oauth/utils/codexAuthZipExtract.ts +5 -0
- package/src/lib/oauth/utils/jsonZipExtract.ts +89 -0
- package/src/lib/oauth/utils/pkce.ts +37 -0
- package/src/lib/oauth/utils/server.ts +122 -0
- package/src/lib/oauth/utils/ui.ts +47 -0
- package/src/lib/obsidian/api.ts +390 -0
- package/src/lib/obsidianSync.ts +110 -0
- package/src/lib/oneproxyRotator.ts +19 -0
- package/src/lib/oneproxySync.ts +182 -0
- package/src/lib/piiSanitizer.ts +416 -0
- package/src/lib/playground/codeExport.ts +408 -0
- package/src/lib/playground/promptImprover.ts +147 -0
- package/src/lib/playground/streamMetrics.ts +61 -0
- package/src/lib/playground/types.ts +114 -0
- package/src/lib/plugins/devMode.ts +65 -0
- package/src/lib/plugins/doctor.ts +138 -0
- package/src/lib/plugins/errors.ts +38 -0
- package/src/lib/plugins/hooks.ts +322 -0
- package/src/lib/plugins/index.ts +72 -0
- package/src/lib/plugins/loader.ts +326 -0
- package/src/lib/plugins/logger.ts +50 -0
- package/src/lib/plugins/manager.ts +579 -0
- package/src/lib/plugins/manifest.ts +213 -0
- package/src/lib/plugins/marketplace.ts +202 -0
- package/src/lib/plugins/pluginWorker.ts +246 -0
- package/src/lib/plugins/sandbox.ts +29 -0
- package/src/lib/plugins/scanner.ts +110 -0
- package/src/lib/plugins/sdk.ts +88 -0
- package/src/lib/plugins/signing.ts +34 -0
- package/src/lib/plugins/testRunner.ts +95 -0
- package/src/lib/plugins/watcher.ts +90 -0
- package/src/lib/pricingSync.ts +456 -0
- package/src/lib/promptCache/index.ts +1 -0
- package/src/lib/promptCache/prefixAnalyzer.ts +81 -0
- package/src/lib/providerModels/cursorAgent.ts +166 -0
- package/src/lib/providerModels/geminiModelsParser.ts +91 -0
- package/src/lib/providerModels/managedAvailableModels.ts +165 -0
- package/src/lib/providerModels/managedModelImport.ts +349 -0
- package/src/lib/providerModels/modelDiscovery.ts +151 -0
- package/src/lib/providers/catalog.ts +246 -0
- package/src/lib/providers/claudeExtraUsage.ts +160 -0
- package/src/lib/providers/claudeFastMode.ts +75 -0
- package/src/lib/providers/codexConnectionDefaults.ts +85 -0
- package/src/lib/providers/codexFastTier.ts +210 -0
- package/src/lib/providers/imageValidation.ts +160 -0
- package/src/lib/providers/managedAvailableModels.ts +21 -0
- package/src/lib/providers/modelListingCapability.ts +26 -0
- package/src/lib/providers/nvidiaValidationModel.ts +24 -0
- package/src/lib/providers/requestDefaults.ts +318 -0
- package/src/lib/providers/serviceKindIndex.ts +0 -0
- package/src/lib/providers/staticModels.ts +170 -0
- package/src/lib/providers/validation/anthropicFormat.ts +319 -0
- package/src/lib/providers/validation/audioMiscProviders.ts +582 -0
- package/src/lib/providers/validation/cloudProviders.ts +669 -0
- package/src/lib/providers/validation/cozeError.ts +56 -0
- package/src/lib/providers/validation/directChatProbe.ts +47 -0
- package/src/lib/providers/validation/embeddingProviders.ts +143 -0
- package/src/lib/providers/validation/headers.ts +122 -0
- package/src/lib/providers/validation/metaAi.ts +117 -0
- package/src/lib/providers/validation/openaiFormat.ts +500 -0
- package/src/lib/providers/validation/searchProviders.ts +199 -0
- package/src/lib/providers/validation/transport.ts +109 -0
- package/src/lib/providers/validation/urlHelpers.ts +114 -0
- package/src/lib/providers/validation/webProvidersA.ts +808 -0
- package/src/lib/providers/validation/webProvidersB.ts +536 -0
- package/src/lib/providers/validation.ts +772 -0
- package/src/lib/providers/webCookieAuth.ts +157 -0
- package/src/lib/providers/xai/thinking.ts +127 -0
- package/src/lib/providers/xai/translators/claude.ts +368 -0
- package/src/lib/providers/xai/translators/gemini.ts +371 -0
- package/src/lib/providers/xai/translators/openai-chat.ts +310 -0
- package/src/lib/providers/xai/translators/openai-responses.ts +77 -0
- package/src/lib/proxyEgress.ts +388 -0
- package/src/lib/proxyHealth/decision.ts +77 -0
- package/src/lib/proxyHealth/scheduler.ts +207 -0
- package/src/lib/proxyHealth/statusPolicy.ts +41 -0
- package/src/lib/proxyHealth.ts +173 -0
- package/src/lib/proxyLogger.ts +240 -0
- package/src/lib/proxyRelay/cloudflareWorkerScript.ts +107 -0
- package/src/lib/quota/QuotaStore.ts +23 -0
- package/src/lib/quota/accountBuckets.ts +225 -0
- package/src/lib/quota/burnRate.ts +111 -0
- package/src/lib/quota/connectionProvider.ts +32 -0
- package/src/lib/quota/connectionRecovery.ts +303 -0
- package/src/lib/quota/dimensions.ts +61 -0
- package/src/lib/quota/enforce.ts +401 -0
- package/src/lib/quota/fairShare.ts +186 -0
- package/src/lib/quota/planRegistry.ts +107 -0
- package/src/lib/quota/planResolver.ts +78 -0
- package/src/lib/quota/quotaCombos.ts +414 -0
- package/src/lib/quota/quotaKey.ts +202 -0
- package/src/lib/quota/quotaModelNaming.ts +84 -0
- package/src/lib/quota/redisQuotaStore.ts +352 -0
- package/src/lib/quota/saturationSignals.ts +559 -0
- package/src/lib/quota/spendRecorder.ts +131 -0
- package/src/lib/quota/sqliteQuotaStore.ts +282 -0
- package/src/lib/quota/storeFactory.ts +124 -0
- package/src/lib/quota/types.ts +94 -0
- package/src/lib/resilience/modelLockoutSettings.ts +95 -0
- package/src/lib/resilience/settings/normalize.ts +359 -0
- package/src/lib/resilience/settings/types.ts +182 -0
- package/src/lib/resilience/settings.ts +386 -0
- package/src/lib/runtime/ports.ts +32 -0
- package/src/lib/search/executeWebSearch.ts +270 -0
- package/src/lib/security/localEndpoints.ts +82 -0
- package/src/lib/semanticCache.ts +377 -0
- package/src/lib/services/ServiceSupervisor.ts +243 -0
- package/src/lib/services/apiKey.ts +40 -0
- package/src/lib/services/bootstrap.ts +133 -0
- package/src/lib/services/embedPath.ts +19 -0
- package/src/lib/services/embedWsProxy.ts +285 -0
- package/src/lib/services/healthCheck.ts +79 -0
- package/src/lib/services/htmlRewriter.ts +185 -0
- package/src/lib/services/installers/bifrost.ts +145 -0
- package/src/lib/services/installers/cliproxy.ts +116 -0
- package/src/lib/services/installers/mux.ts +178 -0
- package/src/lib/services/installers/ninerouter.stub.ts +17 -0
- package/src/lib/services/installers/ninerouter.ts +157 -0
- package/src/lib/services/installers/utils.ts +167 -0
- package/src/lib/services/modelSync.ts +105 -0
- package/src/lib/services/portProbe.ts +104 -0
- package/src/lib/services/registry.ts +42 -0
- package/src/lib/services/reverseProxy.ts +180 -0
- package/src/lib/services/ringBuffer.ts +91 -0
- package/src/lib/services/types.ts +50 -0
- package/src/lib/settingsCache.ts +62 -0
- package/src/lib/skills/a2a.ts +34 -0
- package/src/lib/skills/builtin/browser.ts +22 -0
- package/src/lib/skills/builtins.ts +493 -0
- package/src/lib/skills/custom.ts +41 -0
- package/src/lib/skills/executor.ts +196 -0
- package/src/lib/skills/githubCollector.ts +426 -0
- package/src/lib/skills/hybrid.ts +67 -0
- package/src/lib/skills/injection.ts +307 -0
- package/src/lib/skills/interception.ts +343 -0
- package/src/lib/skills/providerSettings.ts +14 -0
- package/src/lib/skills/registry.ts +398 -0
- package/src/lib/skills/sandbox.ts +178 -0
- package/src/lib/skills/schemas.ts +43 -0
- package/src/lib/skills/skillssh.ts +73 -0
- package/src/lib/skills/types.ts +61 -0
- package/src/lib/source.ts +7 -0
- package/src/lib/spend/batchWriter.ts +208 -0
- package/src/lib/sseTextTransform.ts +346 -0
- package/src/lib/streamingPiiTransform.ts +350 -0
- package/src/lib/sync/bundle.ts +208 -0
- package/src/lib/sync/tokens.ts +107 -0
- package/src/lib/system/autoUpdate.ts +400 -0
- package/src/lib/system/globalPackagePath.ts +49 -0
- package/src/lib/system/versionCheck.ts +163 -0
- package/src/lib/tailscaleTunnel.ts +1201 -0
- package/src/lib/tokenHealthCheck.ts +829 -0
- package/src/lib/toolPolicy.ts +150 -0
- package/src/lib/translator/streamTransform.ts +27 -0
- package/src/lib/translatorEvents.ts +40 -0
- package/src/lib/usage/aggregateHistory.ts +212 -0
- package/src/lib/usage/apiKeySelfService.ts +420 -0
- package/src/lib/usage/apiKeyUsageLimits.ts +578 -0
- package/src/lib/usage/callLogArtifacts.ts +337 -0
- package/src/lib/usage/callLogs/format.ts +129 -0
- package/src/lib/usage/callLogs.ts +928 -0
- package/src/lib/usage/callLogsBoundedQueries.ts +42 -0
- package/src/lib/usage/codexResetCredits.ts +355 -0
- package/src/lib/usage/comboForecast.ts +441 -0
- package/src/lib/usage/comboHealth.ts +545 -0
- package/src/lib/usage/comboHealthDashboard.ts +109 -0
- package/src/lib/usage/comboScoringInspector.ts +416 -0
- package/src/lib/usage/completedRequestDetails.ts +103 -0
- package/src/lib/usage/costCalculator.ts +279 -0
- package/src/lib/usage/fetcher.ts +483 -0
- package/src/lib/usage/flatRateProviders.ts +59 -0
- package/src/lib/usage/internalUsageCommand.ts +788 -0
- package/src/lib/usage/migrations.ts +471 -0
- package/src/lib/usage/pendingRequestScope.ts +26 -0
- package/src/lib/usage/providerLimits/quotaNormalize.ts +72 -0
- package/src/lib/usage/providerLimits.ts +997 -0
- package/src/lib/usage/providerWindowCosts.ts +571 -0
- package/src/lib/usage/resilienceExplain.ts +468 -0
- package/src/lib/usage/routeExplain.ts +747 -0
- package/src/lib/usage/tokenAccounting.ts +212 -0
- package/src/lib/usage/usageEvents.ts +91 -0
- package/src/lib/usage/usageHistory/helpers.ts +85 -0
- package/src/lib/usage/usageHistory.ts +916 -0
- package/src/lib/usage/usageStats.ts +581 -0
- package/src/lib/usageAnalytics.ts +372 -0
- package/src/lib/usageDb.ts +38 -0
- package/src/lib/versionManager/binaryManager.ts +240 -0
- package/src/lib/versionManager/healthMonitor.ts +71 -0
- package/src/lib/versionManager/index.ts +155 -0
- package/src/lib/versionManager/processManager.ts +155 -0
- package/src/lib/versionManager/releaseChecker.ts +98 -0
- package/src/lib/vscode/familyFirstModelIds.ts +92 -0
- package/src/lib/vscode/modelPresentation.ts +132 -0
- package/src/lib/vscode/reasoningMetadata.ts +183 -0
- package/src/lib/vscode/serviceTierVariants.ts +202 -0
- package/src/lib/vscode/tokenizedRequest.ts +79 -0
- package/src/lib/webhookDispatcher.ts +221 -0
- package/src/lib/webhooks/eventDescriptions.ts +76 -0
- package/src/lib/webhooks/integrations/discord.ts +49 -0
- package/src/lib/webhooks/integrations/slack.ts +45 -0
- package/src/lib/webhooks/integrations/telegram.ts +67 -0
- package/src/lib/ws/handshake.ts +129 -0
- package/src/lib/zed-oauth/confirmedAccounts.ts +38 -0
- package/src/lib/zed-oauth/credentialFingerprint.ts +23 -0
- package/src/lib/zed-oauth/dockerDetect.ts +38 -0
- package/src/lib/zed-oauth/importUtils.ts +46 -0
- package/src/lib/zed-oauth/keychain-reader.stub.ts +28 -0
- package/src/lib/zed-oauth/keychain-reader.ts +231 -0
- package/src/mitm/_internal/bypass.cjs +170 -0
- package/src/mitm/_internal/forwardTarget.cjs +55 -0
- package/src/mitm/_internal/ingest.cjs +82 -0
- package/src/mitm/cert/generate.ts +41 -0
- package/src/mitm/cert/install.stub.ts +23 -0
- package/src/mitm/cert/install.ts +440 -0
- package/src/mitm/dataDir.ts +41 -0
- package/src/mitm/detection/antigravity.ts +33 -0
- package/src/mitm/detection/claudeCode.ts +25 -0
- package/src/mitm/detection/codex.ts +25 -0
- package/src/mitm/detection/copilot.ts +39 -0
- package/src/mitm/detection/cursor.ts +31 -0
- package/src/mitm/detection/index.ts +53 -0
- package/src/mitm/detection/kiro.ts +30 -0
- package/src/mitm/detection/openCode.ts +32 -0
- package/src/mitm/detection/zed.ts +26 -0
- package/src/mitm/dns/dnsConfig.ts +242 -0
- package/src/mitm/dns/provision.ts +92 -0
- package/src/mitm/handlers/antigravity.ts +192 -0
- package/src/mitm/handlers/base.ts +333 -0
- package/src/mitm/handlers/claudeCode.ts +56 -0
- package/src/mitm/handlers/codex.ts +55 -0
- package/src/mitm/handlers/copilot.ts +55 -0
- package/src/mitm/handlers/cursor.ts +55 -0
- package/src/mitm/handlers/kiro.ts +58 -0
- package/src/mitm/handlers/openCode.ts +55 -0
- package/src/mitm/handlers/trae.ts +24 -0
- package/src/mitm/handlers/zed.ts +55 -0
- package/src/mitm/inspector/agentBridgeHook.ts +137 -0
- package/src/mitm/inspector/buffer.ts +201 -0
- package/src/mitm/inspector/contextKey.ts +98 -0
- package/src/mitm/inspector/conversationNormalizer.ts +393 -0
- package/src/mitm/inspector/diagnostics.ts +67 -0
- package/src/mitm/inspector/httpProxyServer.ts +272 -0
- package/src/mitm/inspector/kindDetector.ts +87 -0
- package/src/mitm/inspector/llmMetadataExtractor.ts +183 -0
- package/src/mitm/inspector/pricing.ts +57 -0
- package/src/mitm/inspector/processAttribution.ts +107 -0
- package/src/mitm/inspector/sseMerger.ts +316 -0
- package/src/mitm/inspector/systemProxyConfig.ts +316 -0
- package/src/mitm/inspector/types.ts +112 -0
- package/src/mitm/manager.runtime.ts +5 -0
- package/src/mitm/manager.stub.ts +44 -0
- package/src/mitm/manager.ts +762 -0
- package/src/mitm/maskSecrets.ts +31 -0
- package/src/mitm/passthrough.ts +76 -0
- package/src/mitm/sanitizeHeaders.ts +61 -0
- package/src/mitm/server.cjs +786 -0
- package/src/mitm/socketTimeouts.ts +24 -0
- package/src/mitm/systemCommands.ts +280 -0
- package/src/mitm/targets/antigravity.ts +85 -0
- package/src/mitm/targets/claudeCode.ts +37 -0
- package/src/mitm/targets/codex.ts +30 -0
- package/src/mitm/targets/copilot.ts +32 -0
- package/src/mitm/targets/cursor.ts +33 -0
- package/src/mitm/targets/index.ts +75 -0
- package/src/mitm/targets/kiro.ts +64 -0
- package/src/mitm/targets/openCode.ts +34 -0
- package/src/mitm/targets/trae.ts +32 -0
- package/src/mitm/targets/zed.ts +32 -0
- package/src/mitm/tproxy/caTrust.ts +120 -0
- package/src/mitm/tproxy/captureManager.ts +118 -0
- package/src/mitm/tproxy/captureMode.ts +226 -0
- package/src/mitm/tproxy/commands.ts +122 -0
- package/src/mitm/tproxy/dynamicCert.ts +119 -0
- package/src/mitm/tproxy/native/README.md +55 -0
- package/src/mitm/tproxy/native/binding.gyp +9 -0
- package/src/mitm/tproxy/native/transparent.c +147 -0
- package/src/mitm/tproxy/setup.ts +68 -0
- package/src/mitm/tproxy/tlsCapture.ts +368 -0
- package/src/mitm/tproxy/transparentSocket.ts +131 -0
- package/src/mitm/types.ts +75 -0
- package/src/mitm/upstreamTrust.ts +40 -0
- package/src/models/index.ts +29 -0
- package/src/server/auth/loginGuard.ts +130 -0
- package/src/server/authz/accessScopes.ts +62 -0
- package/src/server/authz/accessTokenAuth.ts +67 -0
- package/src/server/authz/assertAuth.ts +88 -0
- package/src/server/authz/classify.ts +135 -0
- package/src/server/authz/context.ts +44 -0
- package/src/server/authz/csrf.ts +85 -0
- package/src/server/authz/headers.ts +76 -0
- package/src/server/authz/peerStamp.ts +99 -0
- package/src/server/authz/pipeline.ts +370 -0
- package/src/server/authz/policies/clientApi.ts +72 -0
- package/src/server/authz/policies/management.ts +298 -0
- package/src/server/authz/policies/public.ts +9 -0
- package/src/server/authz/routeGuard.ts +233 -0
- package/src/server/authz/types.ts +77 -0
- package/src/server/cors/origins.ts +172 -0
- package/src/server/origin/publicOrigin.ts +270 -0
- package/src/server/ws/liveServer.ts +630 -0
- package/src/server/ws/liveServerAllowList.ts +106 -0
- package/src/server/ws/types.ts +57 -0
- package/src/shared/components/Avatar.tsx +81 -0
- package/src/shared/components/Badge.tsx +68 -0
- package/src/shared/components/Breadcrumbs.tsx +103 -0
- package/src/shared/components/Button.tsx +85 -0
- package/src/shared/components/Card.tsx +141 -0
- package/src/shared/components/Checkbox.tsx +38 -0
- package/src/shared/components/CloudSyncStatus.tsx +104 -0
- package/src/shared/components/Collapsible.tsx +93 -0
- package/src/shared/components/CollapsibleSection.tsx +66 -0
- package/src/shared/components/ColumnToggle.tsx +100 -0
- package/src/shared/components/CommandPalette.tsx +375 -0
- package/src/shared/components/ConsoleLogViewer.tsx +326 -0
- package/src/shared/components/CursorAuthModal.tsx +198 -0
- package/src/shared/components/DataTable.tsx +195 -0
- package/src/shared/components/DegradationBadge.tsx +41 -0
- package/src/shared/components/DistributeProxiesButton.tsx +74 -0
- package/src/shared/components/EmptyState.tsx +121 -0
- package/src/shared/components/ErrorPageScaffold.tsx +91 -0
- package/src/shared/components/FilterBar.tsx +197 -0
- package/src/shared/components/Footer.tsx +169 -0
- package/src/shared/components/Header.tsx +284 -0
- package/src/shared/components/InfoTooltip.tsx +34 -0
- package/src/shared/components/Input.tsx +155 -0
- package/src/shared/components/KiroAuthModal.tsx +425 -0
- package/src/shared/components/KiroOAuthWrapper.tsx +116 -0
- package/src/shared/components/KiroSocialOAuthModal.tsx +202 -0
- package/src/shared/components/LanguageSelector.tsx +106 -0
- package/src/shared/components/LinkifiedText.tsx +30 -0
- package/src/shared/components/Loading.tsx +128 -0
- package/src/shared/components/LocaleAutoDetect.tsx +43 -0
- package/src/shared/components/MaintenanceBanner.tsx +87 -0
- package/src/shared/components/ManualConfigModal.tsx +51 -0
- package/src/shared/components/Modal.tsx +240 -0
- package/src/shared/components/ModelRoutingSection.tsx +323 -0
- package/src/shared/components/ModelSelectModal.tsx +618 -0
- package/src/shared/components/MonacoEditor.tsx +23 -0
- package/src/shared/components/NavigationProgress.tsx +105 -0
- package/src/shared/components/NoAuthAccountCard.tsx +539 -0
- package/src/shared/components/NoAuthProviderCard.tsx +40 -0
- package/src/shared/components/NoAuthProviderToggle.tsx +36 -0
- package/src/shared/components/NotificationToast.tsx +206 -0
- package/src/shared/components/OAuthModal.tsx +992 -0
- package/src/shared/components/OmniRouteLogo.tsx +86 -0
- package/src/shared/components/PresetSlider.tsx +64 -0
- package/src/shared/components/PricingModal.tsx +211 -0
- package/src/shared/components/ProviderIcon.tsx +263 -0
- package/src/shared/components/ProviderTestSlideOver.tsx +560 -0
- package/src/shared/components/ProxyConfigModal.tsx +781 -0
- package/src/shared/components/ProxyLogDetail.tsx +192 -0
- package/src/shared/components/ProxyLogger.tsx +573 -0
- package/src/shared/components/PwaRegister.tsx +22 -0
- package/src/shared/components/RequestLoggerDetail.tsx +797 -0
- package/src/shared/components/RequestLoggerV2.tsx +1628 -0
- package/src/shared/components/RiskNoticeModal.tsx +81 -0
- package/src/shared/components/SegmentedControl.tsx +69 -0
- package/src/shared/components/Select.tsx +110 -0
- package/src/shared/components/Sidebar.tsx +726 -0
- package/src/shared/components/SkillsConceptCard.tsx +84 -0
- package/src/shared/components/SystemMonitor.tsx +179 -0
- package/src/shared/components/Textarea.tsx +29 -0
- package/src/shared/components/ThemeProvider.tsx +14 -0
- package/src/shared/components/ThemeToggle.tsx +52 -0
- package/src/shared/components/Toggle.tsx +102 -0
- package/src/shared/components/TokenHealthBadge.tsx +113 -0
- package/src/shared/components/Tooltip.tsx +193 -0
- package/src/shared/components/TraeAuthModal.tsx +334 -0
- package/src/shared/components/UsageAnalytics.tsx +414 -0
- package/src/shared/components/UsageStats.tsx +693 -0
- package/src/shared/components/analytics/ApiKeyFilterDropdown.tsx +205 -0
- package/src/shared/components/analytics/CustomRangePicker.tsx +195 -0
- package/src/shared/components/analytics/TimeRangeSelector.tsx +49 -0
- package/src/shared/components/analytics/chartColors.ts +12 -0
- package/src/shared/components/analytics/charts.tsx +1023 -0
- package/src/shared/components/analytics/index.tsx +28 -0
- package/src/shared/components/analytics/rechartsCore.tsx +85 -0
- package/src/shared/components/analytics/rechartsDonuts.tsx +195 -0
- package/src/shared/components/analytics/rechartsUsageCharts.tsx +312 -0
- package/src/shared/components/cli/CliComparisonCard.tsx +82 -0
- package/src/shared/components/cli/CliConceptCard.tsx +58 -0
- package/src/shared/components/cli/CliToolCard.tsx +152 -0
- package/src/shared/components/cli/index.ts +8 -0
- package/src/shared/components/compression/CompressionPipelineEditor.tsx +175 -0
- package/src/shared/components/compression/EngineConfigForm.tsx +78 -0
- package/src/shared/components/compression/EngineConfigPage.tsx +411 -0
- package/src/shared/components/compression/compressionPipelineModel.ts +78 -0
- package/src/shared/components/docs/APIReference.stories.tsx +35 -0
- package/src/shared/components/docs/APIReference.tsx +83 -0
- package/src/shared/components/docs/Callout.stories.tsx +40 -0
- package/src/shared/components/docs/Callout.tsx +38 -0
- package/src/shared/components/docs/CodeBlock.stories.tsx +25 -0
- package/src/shared/components/docs/CodeBlock.tsx +52 -0
- package/src/shared/components/docs/DocsBreadcrumbs.stories.tsx +20 -0
- package/src/shared/components/docs/DocsBreadcrumbs.tsx +46 -0
- package/src/shared/components/docs/DocsSidebar.stories.tsx +31 -0
- package/src/shared/components/docs/DocsSidebar.tsx +77 -0
- package/src/shared/components/docs/DocsThemeProvider.tsx +21 -0
- package/src/shared/components/docs/Tabs.stories.tsx +26 -0
- package/src/shared/components/docs/Tabs.tsx +50 -0
- package/src/shared/components/docs/tokens.ts +38 -0
- package/src/shared/components/flow/FlowCanvas.tsx +151 -0
- package/src/shared/components/flow/StatusDot.tsx +36 -0
- package/src/shared/components/flow/edgeStyles.ts +32 -0
- package/src/shared/components/index.tsx +49 -0
- package/src/shared/components/layouts/AuthLayout.tsx +28 -0
- package/src/shared/components/layouts/DashboardLayout.tsx +131 -0
- package/src/shared/components/layouts/index.tsx +3 -0
- package/src/shared/components/lobeProviderIcons.ts +488 -0
- package/src/shared/components/logTableStyles.ts +7 -0
- package/src/shared/components/modelSelectModalHelpers.ts +75 -0
- package/src/shared/components/oauthBlobSubmit.ts +42 -0
- package/src/shared/components/proxyAssignment.ts +40 -0
- package/src/shared/components/requestLoggerPreferences.ts +44 -0
- package/src/shared/components/requestLoggerSignature.ts +56 -0
- package/src/shared/constants/agentSkills.ts +457 -0
- package/src/shared/constants/antigravityClientProfile.ts +29 -0
- package/src/shared/constants/apiKeyPolicyScopes.ts +5 -0
- package/src/shared/constants/appConfig.ts +12 -0
- package/src/shared/constants/batch.ts +1 -0
- package/src/shared/constants/batchEndpoints.ts +11 -0
- package/src/shared/constants/bodySize.ts +39 -0
- package/src/shared/constants/cliCompatProviders.ts +88 -0
- package/src/shared/constants/cliTools.ts +862 -0
- package/src/shared/constants/clientIdentityProfiles.ts +89 -0
- package/src/shared/constants/colors.ts +147 -0
- package/src/shared/constants/comboConfigMode.ts +9 -0
- package/src/shared/constants/config.ts +44 -0
- package/src/shared/constants/dashboardCsrf.ts +1 -0
- package/src/shared/constants/endpointCategories.ts +133 -0
- package/src/shared/constants/errorCodes.ts +245 -0
- package/src/shared/constants/featureFlagDefinitions.ts +495 -0
- package/src/shared/constants/headers.ts +16 -0
- package/src/shared/constants/homeWidgets.ts +5 -0
- package/src/shared/constants/index.ts +4 -0
- package/src/shared/constants/managementScopes.ts +31 -0
- package/src/shared/constants/mcpScopes.ts +78 -0
- package/src/shared/constants/mitmToolHosts.ts +31 -0
- package/src/shared/constants/modal.ts +5 -0
- package/src/shared/constants/modelCompat.ts +9 -0
- package/src/shared/constants/modelSpecs.ts +607 -0
- package/src/shared/constants/models.ts +40 -0
- package/src/shared/constants/openRouterPreset.ts +11 -0
- package/src/shared/constants/pricing/default-pricing.ts +16 -0
- package/src/shared/constants/pricing/frontier-labs.ts +386 -0
- package/src/shared/constants/pricing/inference-hosts.ts +208 -0
- package/src/shared/constants/pricing/oauth-subscriptions.ts +599 -0
- package/src/shared/constants/pricing/regional.ts +168 -0
- package/src/shared/constants/pricing/shared-tiers.ts +162 -0
- package/src/shared/constants/pricing.ts +40 -0
- package/src/shared/constants/providers/apikey/enterprise-cloud.ts +214 -0
- package/src/shared/constants/providers/apikey/frontier-labs.ts +253 -0
- package/src/shared/constants/providers/apikey/gateways.ts +662 -0
- package/src/shared/constants/providers/apikey/index.ts +21 -0
- package/src/shared/constants/providers/apikey/inference-hosts.ts +328 -0
- package/src/shared/constants/providers/apikey/regional.ts +372 -0
- package/src/shared/constants/providers/apikey/specialty-media.ts +266 -0
- package/src/shared/constants/providers/audio.ts +71 -0
- package/src/shared/constants/providers/cloud-agent.ts +36 -0
- package/src/shared/constants/providers/local.ts +162 -0
- package/src/shared/constants/providers/noauth.ts +123 -0
- package/src/shared/constants/providers/oauth.ts +231 -0
- package/src/shared/constants/providers/search.ts +125 -0
- package/src/shared/constants/providers/system.ts +16 -0
- package/src/shared/constants/providers/upstream-proxy.ts +36 -0
- package/src/shared/constants/providers/web-cookie.ts +364 -0
- package/src/shared/constants/providers.ts +449 -0
- package/src/shared/constants/publicApiRoutes.ts +62 -0
- package/src/shared/constants/responsesPreviousResponseId.ts +5 -0
- package/src/shared/constants/routingStrategies.ts +216 -0
- package/src/shared/constants/selfServiceScopes.ts +20 -0
- package/src/shared/constants/serviceKinds.ts +34 -0
- package/src/shared/constants/sidebarGroupVisibility.ts +31 -0
- package/src/shared/constants/sidebarVisibility/sections.ts +769 -0
- package/src/shared/constants/sidebarVisibility/types.ts +161 -0
- package/src/shared/constants/sidebarVisibility.ts +291 -0
- package/src/shared/constants/spawnCapablePrefixes.ts +35 -0
- package/src/shared/constants/statusColors.ts +17 -0
- package/src/shared/constants/upstreamHeaders.ts +42 -0
- package/src/shared/constants/visionBridgeDefaults.ts +88 -0
- package/src/shared/constants/visionModels.ts +68 -0
- package/src/shared/contracts/quota.ts +138 -0
- package/src/shared/hooks/cli/useToolBatchStatuses.ts +54 -0
- package/src/shared/hooks/index.ts +3 -0
- package/src/shared/hooks/useCopyToClipboard.ts +38 -0
- package/src/shared/hooks/useDisplayBaseUrl.ts +49 -0
- package/src/shared/hooks/useElectron.ts +197 -0
- package/src/shared/hooks/useTheme.ts +59 -0
- package/src/shared/http/apiErrorMessage.ts +19 -0
- package/src/shared/lib/persistLocale.ts +19 -0
- package/src/shared/middleware/bodySizeGuard.ts +97 -0
- package/src/shared/middleware/chatBodyAdmission.ts +161 -0
- package/src/shared/middleware/correlationId.ts +43 -0
- package/src/shared/network/outboundUrlGuard.ts +276 -0
- package/src/shared/network/remoteImageFetch.ts +182 -0
- package/src/shared/network/safeOutboundFetch.ts +372 -0
- package/src/shared/providers/webSessionCredentials.ts +308 -0
- package/src/shared/reasoning/effortStandardization.ts +124 -0
- package/src/shared/schemas/agentBridge.ts +37 -0
- package/src/shared/schemas/cliCatalog.ts +66 -0
- package/src/shared/schemas/inspector.ts +47 -0
- package/src/shared/schemas/memory.ts +142 -0
- package/src/shared/schemas/playground.ts +67 -0
- package/src/shared/schemas/qdrant.ts +39 -0
- package/src/shared/schemas/quota.ts +61 -0
- package/src/shared/schemas/searchTools.ts +39 -0
- package/src/shared/schemas/validation.ts +154 -0
- package/src/shared/services/apiKeyResolver.ts +44 -0
- package/src/shared/services/backupService.ts +233 -0
- package/src/shared/services/claudeCliConfig.ts +17 -0
- package/src/shared/services/cliRuntime.ts +1099 -0
- package/src/shared/services/cloudSyncScheduler.ts +135 -0
- package/src/shared/services/droidCustomModels.ts +118 -0
- package/src/shared/services/initializeCloudSync.ts +31 -0
- package/src/shared/services/loginShellPath.ts +87 -0
- package/src/shared/services/modelSyncScheduler.ts +225 -0
- package/src/shared/services/opencodeConfig.ts +140 -0
- package/src/shared/services/providerLimitsSyncScheduler.ts +72 -0
- package/src/shared/types/cliBatchStatus.ts +18 -0
- package/src/shared/types/index.ts +3 -0
- package/src/shared/types/pagination.ts +61 -0
- package/src/shared/types/utilization.ts +405 -0
- package/src/shared/utils/a11yAudit.ts +140 -0
- package/src/shared/utils/api.ts +112 -0
- package/src/shared/utils/apiAuth.ts +354 -0
- package/src/shared/utils/apiKey.ts +92 -0
- package/src/shared/utils/apiKeyPolicy.ts +680 -0
- package/src/shared/utils/apiResponse.ts +79 -0
- package/src/shared/utils/bulkApiKeyParser.ts +113 -0
- package/src/shared/utils/circuitBreaker.ts +611 -0
- package/src/shared/utils/classify429.ts +253 -0
- package/src/shared/utils/cliCompat.ts +5 -0
- package/src/shared/utils/clineAuth.ts +62 -0
- package/src/shared/utils/clipboard.ts +49 -0
- package/src/shared/utils/cloud.ts +41 -0
- package/src/shared/utils/cn.ts +12 -0
- package/src/shared/utils/codexBaseUrl.ts +32 -0
- package/src/shared/utils/codexConfig.ts +30 -0
- package/src/shared/utils/compressionHeaderEcho.ts +59 -0
- package/src/shared/utils/cors.ts +24 -0
- package/src/shared/utils/costEstimator.ts +126 -0
- package/src/shared/utils/dashboardCsrf.ts +196 -0
- package/src/shared/utils/featureFlags.ts +113 -0
- package/src/shared/utils/fetchError.ts +26 -0
- package/src/shared/utils/fetchTimeout.ts +78 -0
- package/src/shared/utils/formatting.ts +183 -0
- package/src/shared/utils/freeModels.ts +112 -0
- package/src/shared/utils/index.ts +39 -0
- package/src/shared/utils/inputSanitizer.ts +339 -0
- package/src/shared/utils/linkify.ts +54 -0
- package/src/shared/utils/logRedaction.ts +108 -0
- package/src/shared/utils/logger.ts +173 -0
- package/src/shared/utils/machine.ts +5 -0
- package/src/shared/utils/machineId.ts +167 -0
- package/src/shared/utils/maskEmail.ts +77 -0
- package/src/shared/utils/modelCatalogSearch.ts +84 -0
- package/src/shared/utils/noAuthProviders.ts +79 -0
- package/src/shared/utils/nodeRuntimeSupport.ts +112 -0
- package/src/shared/utils/parseApiKeys.ts +41 -0
- package/src/shared/utils/providerDisplayLabel.ts +41 -0
- package/src/shared/utils/providerHints.ts +73 -0
- package/src/shared/utils/providerModelAliases.ts +68 -0
- package/src/shared/utils/rateLimiter.ts +243 -0
- package/src/shared/utils/releaseNotes.ts +53 -0
- package/src/shared/utils/requestId.ts +102 -0
- package/src/shared/utils/requestTelemetry.ts +189 -0
- package/src/shared/utils/requestTimeout.ts +64 -0
- package/src/shared/utils/resolveOmniRouteBaseUrl.ts +24 -0
- package/src/shared/utils/runtimeTimeouts.ts +257 -0
- package/src/shared/utils/secretsValidator.ts +135 -0
- package/src/shared/utils/secureRandom.ts +66 -0
- package/src/shared/utils/serviceTierLabels.ts +42 -0
- package/src/shared/utils/shuffleDeck.ts +147 -0
- package/src/shared/utils/sidebarRouteMatch.ts +43 -0
- package/src/shared/utils/streamTracker.ts +187 -0
- package/src/shared/utils/structuredLogger.ts +219 -0
- package/src/shared/utils/tiktokenCounter.ts +21 -0
- package/src/shared/utils/turkishText.ts +66 -0
- package/src/shared/utils/upstreamError.ts +112 -0
- package/src/shared/validation/compressionConfigSchemas.ts +234 -0
- package/src/shared/validation/freeProxySchemas.ts +111 -0
- package/src/shared/validation/helpers.ts +117 -0
- package/src/shared/validation/oneproxySchemas.ts +14 -0
- package/src/shared/validation/providerSchema.ts +51 -0
- package/src/shared/validation/providerSpecificData.ts +354 -0
- package/src/shared/validation/schemas/apiV1.ts +297 -0
- package/src/shared/validation/schemas/auth.ts +200 -0
- package/src/shared/validation/schemas/cli.ts +86 -0
- package/src/shared/validation/schemas/cloud.ts +53 -0
- package/src/shared/validation/schemas/combo.ts +373 -0
- package/src/shared/validation/schemas/evals.ts +86 -0
- package/src/shared/validation/schemas/gemini.ts +59 -0
- package/src/shared/validation/schemas/keys.ts +144 -0
- package/src/shared/validation/schemas/misc.ts +203 -0
- package/src/shared/validation/schemas/payloadRules.ts +64 -0
- package/src/shared/validation/schemas/pricing.ts +40 -0
- package/src/shared/validation/schemas/provider.ts +430 -0
- package/src/shared/validation/schemas/proxy.ts +243 -0
- package/src/shared/validation/schemas/routing.ts +88 -0
- package/src/shared/validation/schemas/settings.ts +267 -0
- package/src/shared/validation/schemas/translator.ts +44 -0
- package/src/shared/validation/schemas.ts +18 -0
- package/src/shared/validation/settingsSchemas.ts +396 -0
- package/src/sse/handlers/chat.ts +1750 -0
- package/src/sse/handlers/chatHelpers.ts +859 -0
- package/src/sse/handlers/requestBody.ts +24 -0
- package/src/sse/handlers/resolveRoutingModel.ts +17 -0
- package/src/sse/services/auth.ts +2446 -0
- package/src/sse/services/cooldownAwareRetry.ts +162 -0
- package/src/sse/services/model.ts +254 -0
- package/src/sse/services/noAuthProviderSettings.ts +18 -0
- package/src/sse/services/noAuthProxyResolution.ts +111 -0
- package/src/sse/services/sessionAffinityPin.ts +247 -0
- package/src/sse/services/streamState.ts +218 -0
- package/src/sse/services/tokenRefresh.ts +275 -0
- package/src/sse/utils/logger.ts +37 -0
- package/src/types/databaseSettings.ts +124 -0
- package/src/types/global.d.ts +122 -0
- package/src/types/index.ts +10 -0
- package/src/types/provider.ts +9 -0
- package/src/types/sqljs.d.ts +32 -0
|
@@ -0,0 +1,3107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatGptWebExecutor — ChatGPT Web Session Provider
|
|
3
|
+
*
|
|
4
|
+
* Routes requests through chatgpt.com's internal SSE API using a Plus/Pro
|
|
5
|
+
* subscription session cookie, translating between OpenAI chat completions
|
|
6
|
+
* format and ChatGPT's internal protocol.
|
|
7
|
+
*
|
|
8
|
+
* Auth pipeline (per request):
|
|
9
|
+
* 1. exchangeSession() GET /api/auth/session cookie → JWT accessToken (cached ~5min)
|
|
10
|
+
* 2. prepareChatRequirements() POST /backend-api/sentinel/chat-requirements
|
|
11
|
+
* → { proofofwork.seed, difficulty, persona }
|
|
12
|
+
* 3. solveProofOfWork() SHA3-512 hash loop → "gAAAAAB…" sentinel proof token
|
|
13
|
+
* 4. fetch /backend-api/conversation with Bearer + sentinel-proof-token + browser UA
|
|
14
|
+
*
|
|
15
|
+
* Response is the standard ChatGPT SSE format (cumulative `parts[0]` strings, not deltas).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { BaseExecutor, type ExecuteInput, type ProviderCredentials } from "./base.ts";
|
|
19
|
+
import { describeChatGptWebHttpError } from "./chatgptWebErrors.ts";
|
|
20
|
+
import { prepareToolMessages } from "../translator/webTools.ts";
|
|
21
|
+
import { buildToolModeResponse } from "./chatgptWebTools.ts";
|
|
22
|
+
import { createHash, randomUUID, randomBytes } from "node:crypto";
|
|
23
|
+
import { sha3_512Hex } from "../utils/sha3-512.ts";
|
|
24
|
+
import {
|
|
25
|
+
tlsFetchChatGpt,
|
|
26
|
+
TlsClientUnavailableError,
|
|
27
|
+
type TlsFetchResult,
|
|
28
|
+
} from "../services/chatgptTlsClient.ts";
|
|
29
|
+
import {
|
|
30
|
+
storeChatGptImage,
|
|
31
|
+
getChatGptImageConversationContext,
|
|
32
|
+
__resetChatGptImageCacheForTesting,
|
|
33
|
+
type ChatGptImageConversationContext,
|
|
34
|
+
} from "../services/chatgptImageCache.ts";
|
|
35
|
+
import { isThinkingCapableModel, resolveChatGptModel } from "./chatgpt-web/models.ts";
|
|
36
|
+
|
|
37
|
+
// ─── Constants ──────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
const CHATGPT_BASE = "https://chatgpt.com";
|
|
40
|
+
const SESSION_URL = `${CHATGPT_BASE}/api/auth/session`;
|
|
41
|
+
const SENTINEL_PREPARE_URL = `${CHATGPT_BASE}/backend-api/sentinel/chat-requirements/prepare`;
|
|
42
|
+
const SENTINEL_CR_URL = `${CHATGPT_BASE}/backend-api/sentinel/chat-requirements`;
|
|
43
|
+
const CONV_URL = `${CHATGPT_BASE}/backend-api/f/conversation`;
|
|
44
|
+
const USER_LAST_USED_MODEL_CONFIG_URL = `${CHATGPT_BASE}/backend-api/settings/user_last_used_model_config`;
|
|
45
|
+
|
|
46
|
+
const DEFAULT_PRO_POLL_TIMEOUT_MS = 20 * 60_000;
|
|
47
|
+
const DEFAULT_PRO_POLL_INTERVAL_MS = 4_000;
|
|
48
|
+
|
|
49
|
+
const CHATGPT_USER_AGENT =
|
|
50
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:152.0) Gecko/20100101 Firefox/152.0";
|
|
51
|
+
|
|
52
|
+
// Captured from a real chatgpt.com browser session (April 2026).
|
|
53
|
+
const OAI_CLIENT_VERSION = "prod-81e0c5cdf6140e8c5db714d613337f4aeab94029";
|
|
54
|
+
const OAI_CLIENT_BUILD_NUMBER = "6128297";
|
|
55
|
+
|
|
56
|
+
// Per-cookie device ID. The browser stores a persistent `oai-did` cookie that
|
|
57
|
+
// uniquely identifies the device for OpenAI's risk model — we derive a stable
|
|
58
|
+
// UUID from a hash of the session cookie so that each account/connection gets
|
|
59
|
+
// its own device id, but it doesn't change between requests.
|
|
60
|
+
const deviceIdCache = new Map<string, string>();
|
|
61
|
+
function deviceIdFor(cookie: string): string {
|
|
62
|
+
const key = cookieKey(cookie);
|
|
63
|
+
let id = deviceIdCache.get(key);
|
|
64
|
+
if (!id) {
|
|
65
|
+
// Synthesize a UUID v4-shaped string from a SHA-256 of the cookie. Stable,
|
|
66
|
+
// deterministic per cookie, no PII (the cookie's already secret).
|
|
67
|
+
// Not a password hash — SHA-256 is used to derive a stable UUID from the
|
|
68
|
+
// session cookie for device-id fingerprinting. The output is a cache key.
|
|
69
|
+
const h = createHash("sha256").update(cookie).digest("hex"); // lgtm[js/insufficient-password-hash]
|
|
70
|
+
id =
|
|
71
|
+
`${h.slice(0, 8)}-${h.slice(8, 12)}-4${h.slice(13, 16)}-` +
|
|
72
|
+
`${((parseInt(h.slice(16, 17), 16) & 0x3) | 0x8).toString(16)}${h.slice(17, 20)}-` +
|
|
73
|
+
h.slice(20, 32);
|
|
74
|
+
if (deviceIdCache.size >= 200) {
|
|
75
|
+
const first = deviceIdCache.keys().next().value;
|
|
76
|
+
if (first) deviceIdCache.delete(first);
|
|
77
|
+
}
|
|
78
|
+
deviceIdCache.set(key, id);
|
|
79
|
+
}
|
|
80
|
+
return id;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// OmniRoute model ID → ChatGPT internal slug. The public ChatGPT Web catalog
|
|
84
|
+
// keeps OmniRoute's historical dot-form IDs (e.g. "gpt-5.5-pro"), while
|
|
85
|
+
// ChatGPT's backend routes use dash-form slugs (e.g. "gpt-5-5-pro"). The slug
|
|
86
|
+
// catalog comes from /backend-api/models on a logged-in account;
|
|
87
|
+
// "gpt-5-4-t-mini" is ChatGPT's abbreviated slug for "GPT-5.4 Thinking Mini".
|
|
88
|
+
|
|
89
|
+
// ─── Browser-like default headers ──────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
function browserHeaders(): Record<string, string> {
|
|
92
|
+
return {
|
|
93
|
+
Accept: "*/*",
|
|
94
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
95
|
+
"Cache-Control": "no-cache",
|
|
96
|
+
Origin: CHATGPT_BASE,
|
|
97
|
+
Pragma: "no-cache",
|
|
98
|
+
Referer: `${CHATGPT_BASE}/`,
|
|
99
|
+
"Sec-Fetch-Dest": "empty",
|
|
100
|
+
"Sec-Fetch-Mode": "cors",
|
|
101
|
+
"Sec-Fetch-Site": "same-origin",
|
|
102
|
+
"User-Agent": CHATGPT_USER_AGENT,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Headers ChatGPT's web client sends on backend-api requests. */
|
|
107
|
+
function oaiHeaders(sessionId: string, deviceId: string): Record<string, string> {
|
|
108
|
+
return {
|
|
109
|
+
"OAI-Language": "en-US",
|
|
110
|
+
"OAI-Device-Id": deviceId,
|
|
111
|
+
"OAI-Client-Version": OAI_CLIENT_VERSION,
|
|
112
|
+
"OAI-Client-Build-Number": OAI_CLIENT_BUILD_NUMBER,
|
|
113
|
+
"OAI-Session-Id": sessionId,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ─── Session token cache ────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
interface TokenEntry {
|
|
120
|
+
accessToken: string;
|
|
121
|
+
accountId: string | null;
|
|
122
|
+
expiresAt: number;
|
|
123
|
+
refreshedCookie?: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const TOKEN_TTL_MS = 5 * 60 * 1000; // 5min — accessTokens are short-lived
|
|
127
|
+
const tokenCache = new Map<string, TokenEntry>();
|
|
128
|
+
|
|
129
|
+
function cookieKey(cookie: string): string {
|
|
130
|
+
// SHA-256 prefix (64 bits). Used as the Map key for tokenCache and
|
|
131
|
+
// warmupCache; the previous 32-bit FNV-1a was small enough that a
|
|
132
|
+
// birthday-paradox collision could surface one user's cached accessToken
|
|
133
|
+
// to another's request. 64 bits is overkill for the 200-entry cache but
|
|
134
|
+
// costs essentially nothing.
|
|
135
|
+
// Not a password hash — SHA-256 is used to derive a short, collision-resistant
|
|
136
|
+
// cache key from the session cookie. The output is a map lookup key.
|
|
137
|
+
return createHash("sha256").update(cookie).digest("hex").slice(0, 16); // lgtm[js/insufficient-password-hash]
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function tokenLookup(cookie: string): TokenEntry | null {
|
|
141
|
+
const entry = tokenCache.get(cookieKey(cookie));
|
|
142
|
+
if (!entry) return null;
|
|
143
|
+
if (Date.now() >= entry.expiresAt) {
|
|
144
|
+
tokenCache.delete(cookieKey(cookie));
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return entry;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const TOKEN_CACHE_MAX = 200;
|
|
151
|
+
|
|
152
|
+
function tokenStore(cookie: string, entry: TokenEntry): void {
|
|
153
|
+
// Bound the cache to TOKEN_CACHE_MAX entries (FIFO). Same shape as the
|
|
154
|
+
// image cache and warmup cache — drop the oldest before inserting.
|
|
155
|
+
if (tokenCache.size >= TOKEN_CACHE_MAX && !tokenCache.has(cookieKey(cookie))) {
|
|
156
|
+
const firstKey = tokenCache.keys().next().value;
|
|
157
|
+
if (firstKey) tokenCache.delete(firstKey);
|
|
158
|
+
}
|
|
159
|
+
tokenCache.set(cookieKey(cookie), entry);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Conversation continuity is intentionally not cached. Open WebUI and most
|
|
163
|
+
// OpenAI-API-style clients re-send the full history each turn, so each
|
|
164
|
+
// request just starts a fresh conversation. Temporary Chat mode is the
|
|
165
|
+
// default; it gets disabled per-request only for image-gen prompts, since
|
|
166
|
+
// that mode rejects the image_gen tool.
|
|
167
|
+
|
|
168
|
+
// ─── /api/auth/session — exchange cookie for JWT ────────────────────────────
|
|
169
|
+
|
|
170
|
+
interface SessionResponse {
|
|
171
|
+
accessToken?: string;
|
|
172
|
+
expires?: string;
|
|
173
|
+
user?: { id?: string };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Session-token family — NextAuth uses one of these depending on token size:
|
|
177
|
+
// __Secure-next-auth.session-token (unchunked, < 4KB)
|
|
178
|
+
// __Secure-next-auth.session-token.0 (chunked, first piece)
|
|
179
|
+
// __Secure-next-auth.session-token.N (chunked, additional pieces)
|
|
180
|
+
// Rotation can change the shape (unchunked → chunked or vice versa). When
|
|
181
|
+
// that happens, every old family member must be dropped — keeping the stale
|
|
182
|
+
// variant alongside the new one would send both, and depending on parser
|
|
183
|
+
// precedence the server could read the stale value and fail auth.
|
|
184
|
+
const SESSION_TOKEN_FAMILY_RE = /^__Secure-next-auth\.session-token(?:\.\d+)?$/;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Merge any rotated session-token chunks from a Set-Cookie response into the
|
|
188
|
+
* original cookie blob, preserving every other cookie the caller pasted
|
|
189
|
+
* (cf_clearance, __cf_bm, _cfuvid, _puid, ...). Returns null if no rotation
|
|
190
|
+
* occurred or the rotated chunks match what's already there.
|
|
191
|
+
*
|
|
192
|
+
* Returning only the matched session-token chunks here was a bug: when the
|
|
193
|
+
* caller pastes a full DevTools Cookie line (the recommended form), the
|
|
194
|
+
* Cloudflare cookies are required for subsequent requests, and dropping
|
|
195
|
+
* them re-triggers `cf-mitigated: challenge`.
|
|
196
|
+
*/
|
|
197
|
+
function mergeRefreshedCookie(
|
|
198
|
+
originalCookie: string,
|
|
199
|
+
setCookieHeader: string | null
|
|
200
|
+
): string | null {
|
|
201
|
+
if (!setCookieHeader) return null;
|
|
202
|
+
const matches = Array.from(
|
|
203
|
+
setCookieHeader.matchAll(/(__Secure-next-auth\.session-token(?:\.\d+)?)=([^;,\s]+)/g)
|
|
204
|
+
);
|
|
205
|
+
if (matches.length === 0) return null;
|
|
206
|
+
|
|
207
|
+
const refreshed = new Map<string, string>();
|
|
208
|
+
for (const m of matches) refreshed.set(m[1], m[2]);
|
|
209
|
+
|
|
210
|
+
let blob = originalCookie.trim();
|
|
211
|
+
if (/^cookie\s*:\s*/i.test(blob)) blob = blob.replace(/^cookie\s*:\s*/i, "");
|
|
212
|
+
|
|
213
|
+
// Bare value (no `=`): the original was just the session-token contents.
|
|
214
|
+
// Replace with the new chunked form.
|
|
215
|
+
if (!/=/.test(blob)) {
|
|
216
|
+
return Array.from(refreshed, ([k, v]) => `${k}=${v}`).join("; ");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const pairs = blob.split(/;\s*/).filter(Boolean);
|
|
220
|
+
const result: string[] = [];
|
|
221
|
+
let mutated = false;
|
|
222
|
+
let droppedStale = false;
|
|
223
|
+
for (const pair of pairs) {
|
|
224
|
+
const eqIdx = pair.indexOf("=");
|
|
225
|
+
if (eqIdx < 0) {
|
|
226
|
+
result.push(pair);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const name = pair.slice(0, eqIdx).trim();
|
|
230
|
+
const value = pair.slice(eqIdx + 1);
|
|
231
|
+
// Drop ALL session-token-family members from the original — we'll
|
|
232
|
+
// append the refreshed set below. This handles unchunked→chunked and
|
|
233
|
+
// chunked→unchunked rotations, where keeping the old name would leave
|
|
234
|
+
// the stale token visible alongside the new one.
|
|
235
|
+
if (SESSION_TOKEN_FAMILY_RE.test(name)) {
|
|
236
|
+
if (!refreshed.has(name) || refreshed.get(name) !== value) mutated = true;
|
|
237
|
+
droppedStale = true;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
result.push(`${name}=${value}`);
|
|
241
|
+
}
|
|
242
|
+
// Append the full refreshed family.
|
|
243
|
+
for (const [name, value] of refreshed) {
|
|
244
|
+
result.push(`${name}=${value}`);
|
|
245
|
+
}
|
|
246
|
+
if (!droppedStale) mutated = true; // refreshed chunks were entirely new
|
|
247
|
+
return mutated ? result.join("; ") : null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Build the Cookie header value from whatever the user pasted.
|
|
252
|
+
*
|
|
253
|
+
* Accepts:
|
|
254
|
+
* - A bare value: "eyJhbGc..." → prepended with __Secure-next-auth.session-token=
|
|
255
|
+
* - An unchunked cookie line: "__Secure-next-auth.session-token=eyJ..."
|
|
256
|
+
* - A chunked cookie line: "__Secure-next-auth.session-token.0=...; __Secure-next-auth.session-token.1=..."
|
|
257
|
+
* - The full DevTools cookie header: "Cookie: __Secure-next-auth.session-token.0=...; cf_clearance=..."
|
|
258
|
+
*
|
|
259
|
+
* If the user pastes a chunked token, we pass the cookies through verbatim —
|
|
260
|
+
* NextAuth's server reassembles them on its side.
|
|
261
|
+
*/
|
|
262
|
+
function buildSessionCookieHeader(rawInput: string): string {
|
|
263
|
+
let s = rawInput.trim();
|
|
264
|
+
if (/^cookie\s*:\s*/i.test(s)) s = s.replace(/^cookie\s*:\s*/i, "");
|
|
265
|
+
if (/__Secure-next-auth\.session-token(?:\.\d+)?\s*=/.test(s)) {
|
|
266
|
+
return s;
|
|
267
|
+
}
|
|
268
|
+
return `__Secure-next-auth.session-token=${s}`;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function exchangeSession(
|
|
272
|
+
cookie: string,
|
|
273
|
+
signal: AbortSignal | null | undefined
|
|
274
|
+
): Promise<TokenEntry> {
|
|
275
|
+
const cached = tokenLookup(cookie);
|
|
276
|
+
if (cached) return cached;
|
|
277
|
+
|
|
278
|
+
const headers: Record<string, string> = {
|
|
279
|
+
...browserHeaders(),
|
|
280
|
+
Accept: "application/json",
|
|
281
|
+
Cookie: buildSessionCookieHeader(cookie),
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const response = await tlsFetchChatGpt(SESSION_URL, {
|
|
285
|
+
method: "GET",
|
|
286
|
+
headers,
|
|
287
|
+
timeoutMs: 30_000,
|
|
288
|
+
signal,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (response.status === 401 || response.status === 403) {
|
|
292
|
+
throw new SessionAuthError("Invalid session cookie");
|
|
293
|
+
}
|
|
294
|
+
if (response.status >= 400) {
|
|
295
|
+
throw new Error(`Session exchange failed (HTTP ${response.status})`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const refreshed = mergeRefreshedCookie(cookie, response.headers.get("set-cookie"));
|
|
299
|
+
let data: SessionResponse = {};
|
|
300
|
+
try {
|
|
301
|
+
data = JSON.parse(response.text || "{}");
|
|
302
|
+
} catch {
|
|
303
|
+
console.warn("[chatgpt-web] session response JSON parse failed");
|
|
304
|
+
/* empty body or non-JSON */
|
|
305
|
+
}
|
|
306
|
+
if (!data.accessToken) {
|
|
307
|
+
throw new SessionAuthError("Session response missing accessToken — cookie likely expired");
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const expiresAt = data.expires ? new Date(data.expires).getTime() : Date.now() + TOKEN_TTL_MS;
|
|
311
|
+
const entry: TokenEntry = {
|
|
312
|
+
accessToken: data.accessToken,
|
|
313
|
+
accountId: data.user?.id ?? null,
|
|
314
|
+
expiresAt: Math.min(expiresAt, Date.now() + TOKEN_TTL_MS),
|
|
315
|
+
refreshedCookie: refreshed ?? undefined,
|
|
316
|
+
};
|
|
317
|
+
tokenStore(cookie, entry);
|
|
318
|
+
return entry;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
class SessionAuthError extends Error {
|
|
322
|
+
constructor(message: string) {
|
|
323
|
+
super(message);
|
|
324
|
+
this.name = "SessionAuthError";
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ─── /backend-api/sentinel/chat-requirements ────────────────────────────────
|
|
329
|
+
|
|
330
|
+
interface ChatRequirements {
|
|
331
|
+
/** Returned by /chat-requirements (the "real" chat requirements token). */
|
|
332
|
+
token?: string;
|
|
333
|
+
/** Returned by /chat-requirements/prepare (sent as a prerequisite header). */
|
|
334
|
+
prepare_token?: string;
|
|
335
|
+
persona?: string;
|
|
336
|
+
proofofwork?: {
|
|
337
|
+
required?: boolean;
|
|
338
|
+
seed?: string;
|
|
339
|
+
difficulty?: string;
|
|
340
|
+
};
|
|
341
|
+
turnstile?: {
|
|
342
|
+
required?: boolean;
|
|
343
|
+
dx?: string;
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ─── Session warmup ────────────────────────────────────────────────────────
|
|
348
|
+
// Mimics chatgpt.com's page-load fetch sequence so Sentinel sees a "warm"
|
|
349
|
+
// browsing session. Cached per (cookie, access-token) pair for 60s to avoid
|
|
350
|
+
// hammering the warmup endpoints on every chat completion.
|
|
351
|
+
|
|
352
|
+
const warmupCache = new Map<string, number>();
|
|
353
|
+
const WARMUP_TTL_MS = 60_000;
|
|
354
|
+
const WARMUP_CACHE_MAX = 200;
|
|
355
|
+
|
|
356
|
+
async function runSessionWarmup(
|
|
357
|
+
accessToken: string,
|
|
358
|
+
accountId: string | null,
|
|
359
|
+
sessionId: string,
|
|
360
|
+
deviceId: string,
|
|
361
|
+
cookie: string,
|
|
362
|
+
signal: AbortSignal | null | undefined,
|
|
363
|
+
log: { debug?: (tag: string, msg: string) => void } | null | undefined
|
|
364
|
+
): Promise<void> {
|
|
365
|
+
const key = cookieKey(cookie) + ":" + accessToken.slice(-8);
|
|
366
|
+
const now = Date.now();
|
|
367
|
+
const last = warmupCache.get(key);
|
|
368
|
+
if (last && now - last < WARMUP_TTL_MS) return;
|
|
369
|
+
// Bound the cache: drop the oldest entry once we hit the cap. Map iteration
|
|
370
|
+
// order is insertion order, so the first key is the oldest.
|
|
371
|
+
if (warmupCache.size >= WARMUP_CACHE_MAX && !warmupCache.has(key)) {
|
|
372
|
+
const first = warmupCache.keys().next().value;
|
|
373
|
+
if (first) warmupCache.delete(first);
|
|
374
|
+
}
|
|
375
|
+
warmupCache.set(key, now);
|
|
376
|
+
|
|
377
|
+
const headers: Record<string, string> = {
|
|
378
|
+
...browserHeaders(),
|
|
379
|
+
...oaiHeaders(sessionId, deviceId),
|
|
380
|
+
Accept: "*/*",
|
|
381
|
+
Authorization: `Bearer ${accessToken}`,
|
|
382
|
+
Cookie: buildSessionCookieHeader(cookie),
|
|
383
|
+
Priority: "u=1, i",
|
|
384
|
+
};
|
|
385
|
+
if (accountId) headers["chatgpt-account-id"] = accountId;
|
|
386
|
+
|
|
387
|
+
const urls = [
|
|
388
|
+
`${CHATGPT_BASE}/backend-api/me`,
|
|
389
|
+
`${CHATGPT_BASE}/backend-api/conversations?offset=0&limit=28&order=updated`,
|
|
390
|
+
`${CHATGPT_BASE}/backend-api/models?history_and_training_disabled=false`,
|
|
391
|
+
];
|
|
392
|
+
|
|
393
|
+
for (const url of urls) {
|
|
394
|
+
try {
|
|
395
|
+
const r = await tlsFetchChatGpt(url, {
|
|
396
|
+
method: "GET",
|
|
397
|
+
headers,
|
|
398
|
+
timeoutMs: 15_000,
|
|
399
|
+
signal,
|
|
400
|
+
});
|
|
401
|
+
log?.debug?.("CGPT-WEB", `warmup ${url.split("/backend-api/")[1]} → ${r.status}`);
|
|
402
|
+
} catch (err) {
|
|
403
|
+
log?.debug?.(
|
|
404
|
+
"CGPT-WEB",
|
|
405
|
+
`warmup ${url} failed: ${err instanceof Error ? err.message : String(err)}`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ─── Thinking-effort preference (PATCH user_last_used_model_config) ────────
|
|
412
|
+
// chatgpt.com has two thinking levels for its dedicated thinking-models:
|
|
413
|
+
// • standard — default, faster
|
|
414
|
+
// • extended — longer reasoning budget
|
|
415
|
+
// The browser sets the level by PATCHing `/backend-api/settings/user_last_used_model_config`
|
|
416
|
+
// once, then issues the conversation request — the conversation endpoint itself
|
|
417
|
+
// has no `thinking_effort` field; the server reads the user's stored preference
|
|
418
|
+
// at routing time. We mirror that handshake when an OpenAI-style request
|
|
419
|
+
// includes `reasoning_effort` (or a direct `providerSpecificData.thinkingEffort`
|
|
420
|
+
// override).
|
|
421
|
+
//
|
|
422
|
+
// Cached per (cookie, slug, effort): the preference persists server-side, so
|
|
423
|
+
// re-PATCHing the same combination is wasted bytes. Refreshed on TTL expiry or
|
|
424
|
+
// whenever the caller switches efforts.
|
|
425
|
+
|
|
426
|
+
const thinkingEffortCache = new Map<string, number>();
|
|
427
|
+
const THINKING_EFFORT_TTL_MS = 5 * 60 * 1000;
|
|
428
|
+
const THINKING_EFFORT_CACHE_MAX = 400;
|
|
429
|
+
|
|
430
|
+
function configuredProPollTimeoutMs(): number {
|
|
431
|
+
const raw = Number(process.env.OMNIROUTE_CGPT_WEB_PRO_TIMEOUT_MS);
|
|
432
|
+
if (!Number.isFinite(raw) || raw <= 0) return DEFAULT_PRO_POLL_TIMEOUT_MS;
|
|
433
|
+
return Math.floor(raw);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function configuredProPollIntervalMs(): number {
|
|
437
|
+
const raw = Number(process.env.OMNIROUTE_CGPT_WEB_PRO_POLL_INTERVAL_MS);
|
|
438
|
+
if (!Number.isFinite(raw) || raw <= 0) return DEFAULT_PRO_POLL_INTERVAL_MS;
|
|
439
|
+
return Math.floor(raw);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function setUserThinkingEffort(
|
|
443
|
+
modelSlug: string,
|
|
444
|
+
effort: "standard" | "extended",
|
|
445
|
+
accessToken: string,
|
|
446
|
+
accountId: string | null,
|
|
447
|
+
sessionId: string,
|
|
448
|
+
deviceId: string,
|
|
449
|
+
cookie: string,
|
|
450
|
+
signal: AbortSignal | null | undefined,
|
|
451
|
+
log:
|
|
452
|
+
| {
|
|
453
|
+
debug?: (tag: string, msg: string) => void;
|
|
454
|
+
warn?: (tag: string, msg: string) => void;
|
|
455
|
+
}
|
|
456
|
+
| null
|
|
457
|
+
| undefined
|
|
458
|
+
): Promise<void> {
|
|
459
|
+
const cacheKey = `${cookieKey(cookie)}:${modelSlug}:${effort}`;
|
|
460
|
+
const now = Date.now();
|
|
461
|
+
const last = thinkingEffortCache.get(cacheKey);
|
|
462
|
+
if (last && now - last < THINKING_EFFORT_TTL_MS) {
|
|
463
|
+
log?.debug?.("CGPT-WEB", `thinking_effort cached (${modelSlug}=${effort}) — skip PATCH`);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
if (thinkingEffortCache.size >= THINKING_EFFORT_CACHE_MAX && !thinkingEffortCache.has(cacheKey)) {
|
|
467
|
+
const first = thinkingEffortCache.keys().next().value;
|
|
468
|
+
if (first) thinkingEffortCache.delete(first);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const url =
|
|
472
|
+
`${USER_LAST_USED_MODEL_CONFIG_URL}` +
|
|
473
|
+
`?model_slug=${encodeURIComponent(modelSlug)}` +
|
|
474
|
+
`&thinking_effort=${encodeURIComponent(effort)}`;
|
|
475
|
+
const headers: Record<string, string> = {
|
|
476
|
+
...browserHeaders(),
|
|
477
|
+
...oaiHeaders(sessionId, deviceId),
|
|
478
|
+
Accept: "application/json",
|
|
479
|
+
Authorization: `Bearer ${accessToken}`,
|
|
480
|
+
Cookie: buildSessionCookieHeader(cookie),
|
|
481
|
+
Priority: "u=4",
|
|
482
|
+
};
|
|
483
|
+
if (accountId) headers["chatgpt-account-id"] = accountId;
|
|
484
|
+
|
|
485
|
+
try {
|
|
486
|
+
const r = await tlsFetchChatGpt(url, {
|
|
487
|
+
method: "PATCH",
|
|
488
|
+
headers,
|
|
489
|
+
timeoutMs: 15_000,
|
|
490
|
+
signal,
|
|
491
|
+
});
|
|
492
|
+
if (r.status >= 400) {
|
|
493
|
+
log?.warn?.(
|
|
494
|
+
"CGPT-WEB",
|
|
495
|
+
`thinking_effort PATCH ${r.status} for ${modelSlug}=${effort} (continuing)`
|
|
496
|
+
);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
thinkingEffortCache.set(cacheKey, now);
|
|
500
|
+
log?.debug?.("CGPT-WEB", `thinking_effort PATCH OK (${modelSlug}=${effort})`);
|
|
501
|
+
} catch (err) {
|
|
502
|
+
log?.warn?.(
|
|
503
|
+
"CGPT-WEB",
|
|
504
|
+
`thinking_effort PATCH failed: ${err instanceof Error ? err.message : String(err)}`
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
async function prepareChatRequirements(
|
|
510
|
+
accessToken: string,
|
|
511
|
+
accountId: string | null,
|
|
512
|
+
sessionId: string,
|
|
513
|
+
deviceId: string,
|
|
514
|
+
cookie: string,
|
|
515
|
+
dplInfo: { dpl: string; scriptSrc: string },
|
|
516
|
+
signal: AbortSignal | null | undefined,
|
|
517
|
+
log?: { warn?: (tag: string, msg: string) => void } | null
|
|
518
|
+
): Promise<ChatRequirements> {
|
|
519
|
+
const config = buildPrekeyConfig(CHATGPT_USER_AGENT, dplInfo.dpl, dplInfo.scriptSrc);
|
|
520
|
+
const prekey = await buildPrepareToken(config, log);
|
|
521
|
+
|
|
522
|
+
const headers: Record<string, string> = {
|
|
523
|
+
...browserHeaders(),
|
|
524
|
+
...oaiHeaders(sessionId, deviceId),
|
|
525
|
+
"Content-Type": "application/json",
|
|
526
|
+
Authorization: `Bearer ${accessToken}`,
|
|
527
|
+
Cookie: buildSessionCookieHeader(cookie),
|
|
528
|
+
Priority: "u=1, i",
|
|
529
|
+
};
|
|
530
|
+
if (accountId) headers["chatgpt-account-id"] = accountId;
|
|
531
|
+
|
|
532
|
+
// Stage 1: POST /chat-requirements/prepare → { prepare_token, ... }
|
|
533
|
+
const prepResp = await tlsFetchChatGpt(SENTINEL_PREPARE_URL, {
|
|
534
|
+
method: "POST",
|
|
535
|
+
headers,
|
|
536
|
+
body: JSON.stringify({ p: prekey }),
|
|
537
|
+
timeoutMs: 30_000,
|
|
538
|
+
signal,
|
|
539
|
+
});
|
|
540
|
+
if (prepResp.status === 401 || prepResp.status === 403) {
|
|
541
|
+
throw new SentinelBlockedError(`Sentinel /prepare blocked (HTTP ${prepResp.status})`);
|
|
542
|
+
}
|
|
543
|
+
if (prepResp.status >= 400) {
|
|
544
|
+
throw new Error(`Sentinel /prepare failed (HTTP ${prepResp.status})`);
|
|
545
|
+
}
|
|
546
|
+
let prepData: ChatRequirements = {};
|
|
547
|
+
try {
|
|
548
|
+
prepData = JSON.parse(prepResp.text || "{}") as ChatRequirements;
|
|
549
|
+
} catch {
|
|
550
|
+
console.warn("[chatgpt-web] chat requirements prep JSON parse failed");
|
|
551
|
+
/* keep empty */
|
|
552
|
+
}
|
|
553
|
+
// Stage 2: POST /chat-requirements with the prepare_token in the body. This
|
|
554
|
+
// is the call that actually returns the chat-requirements-token used on the
|
|
555
|
+
// conversation request.
|
|
556
|
+
if (!prepData.prepare_token) {
|
|
557
|
+
return prepData; // pass through whatever we got — caller handles missing fields
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const crBody: Record<string, unknown> = { p: prekey, prepare_token: prepData.prepare_token };
|
|
561
|
+
const crResp = await tlsFetchChatGpt(SENTINEL_CR_URL, {
|
|
562
|
+
method: "POST",
|
|
563
|
+
headers,
|
|
564
|
+
body: JSON.stringify(crBody),
|
|
565
|
+
timeoutMs: 30_000,
|
|
566
|
+
signal,
|
|
567
|
+
});
|
|
568
|
+
if (crResp.status === 401 || crResp.status === 403) {
|
|
569
|
+
throw new SentinelBlockedError(`Sentinel /chat-requirements blocked (HTTP ${crResp.status})`);
|
|
570
|
+
}
|
|
571
|
+
if (crResp.status >= 400) {
|
|
572
|
+
// Fall back to whatever /prepare returned — some accounts may not need stage 2.
|
|
573
|
+
return prepData;
|
|
574
|
+
}
|
|
575
|
+
try {
|
|
576
|
+
const crData = JSON.parse(crResp.text || "{}") as ChatRequirements;
|
|
577
|
+
// Merge: prepare_token from stage 1, everything else from stage 2.
|
|
578
|
+
return { ...crData, prepare_token: prepData.prepare_token };
|
|
579
|
+
} catch {
|
|
580
|
+
console.warn("[chatgpt-web] chat requirements response JSON parse failed");
|
|
581
|
+
return prepData;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
class SentinelBlockedError extends Error {
|
|
586
|
+
constructor(message: string) {
|
|
587
|
+
super(message);
|
|
588
|
+
this.name = "SentinelBlockedError";
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// ─── Proof-of-work solver ──────────────────────────────────────────────────
|
|
593
|
+
// Mimics the openai-sentinel / chat2api algorithm. The browser sends a base64-encoded
|
|
594
|
+
// JSON config string; the server combines it with a seed and expects a SHA3-512 hash
|
|
595
|
+
// whose hex-prefix is ≤ the difficulty target.
|
|
596
|
+
//
|
|
597
|
+
// Reference: github.com/leetanshaj/openai-sentinel, github.com/lanqian528/chat2api
|
|
598
|
+
// Returns "gAAAAAB" + base64 of the winning config (server-recognised prefix).
|
|
599
|
+
|
|
600
|
+
// ─── DPL / script-src cache (warmup) ────────────────────────────────────────
|
|
601
|
+
// Sentinel's prekey check inspects whether config[5]/config[6] reference a real
|
|
602
|
+
// chatgpt.com deployment (DPL hash + a script URL from the HTML). We GET / once
|
|
603
|
+
// per hour to scrape these — same trick chat2api uses.
|
|
604
|
+
|
|
605
|
+
interface DplInfo {
|
|
606
|
+
dpl: string;
|
|
607
|
+
scriptSrc: string;
|
|
608
|
+
expiresAt: number;
|
|
609
|
+
}
|
|
610
|
+
let dplCache: DplInfo | null = null;
|
|
611
|
+
const DPL_TTL_MS = 60 * 60 * 1000;
|
|
612
|
+
|
|
613
|
+
async function fetchDpl(
|
|
614
|
+
cookie: string,
|
|
615
|
+
signal: AbortSignal | null | undefined
|
|
616
|
+
): Promise<{ dpl: string; scriptSrc: string }> {
|
|
617
|
+
if (dplCache && Date.now() < dplCache.expiresAt) {
|
|
618
|
+
return { dpl: dplCache.dpl, scriptSrc: dplCache.scriptSrc };
|
|
619
|
+
}
|
|
620
|
+
const headers: Record<string, string> = {
|
|
621
|
+
...browserHeaders(),
|
|
622
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
|
623
|
+
Cookie: buildSessionCookieHeader(cookie),
|
|
624
|
+
};
|
|
625
|
+
const response = await tlsFetchChatGpt(`${CHATGPT_BASE}/`, {
|
|
626
|
+
method: "GET",
|
|
627
|
+
headers,
|
|
628
|
+
timeoutMs: 20_000,
|
|
629
|
+
signal,
|
|
630
|
+
});
|
|
631
|
+
const html = response.text || "";
|
|
632
|
+
const dplMatch = html.match(/data-build="([^"]+)"/);
|
|
633
|
+
const dpl = dplMatch ? `dpl=${dplMatch[1]}` : `dpl=${OAI_CLIENT_VERSION.replace(/^prod-/, "")}`;
|
|
634
|
+
const scriptMatch = html.match(/<script[^>]+src="(https?:\/\/[^"]*\.js[^"]*)"/);
|
|
635
|
+
const scriptSrc =
|
|
636
|
+
scriptMatch?.[1] ?? `${CHATGPT_BASE}/_next/static/chunks/webpack-${randomHex(16)}.js`;
|
|
637
|
+
dplCache = { dpl, scriptSrc, expiresAt: Date.now() + DPL_TTL_MS };
|
|
638
|
+
return { dpl, scriptSrc };
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function randomHex(n: number): string {
|
|
642
|
+
return randomBytes(Math.ceil(n / 2))
|
|
643
|
+
.toString("hex")
|
|
644
|
+
.slice(0, n);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// ─── Browser fingerprint key lists (used in prekey config[10..12]) ─────────
|
|
648
|
+
// Chosen to look like real navigator/document/window inspection. The unicode
|
|
649
|
+
// MINUS SIGN (U+2212) in the navigator strings matches what `Object.toString()`
|
|
650
|
+
// produces in real browsers — Sentinel checks for it.
|
|
651
|
+
|
|
652
|
+
const NAVIGATOR_KEYS = [
|
|
653
|
+
"webdriver−false",
|
|
654
|
+
"geolocation",
|
|
655
|
+
"languages",
|
|
656
|
+
"language",
|
|
657
|
+
"platform",
|
|
658
|
+
"userAgent",
|
|
659
|
+
"vendor",
|
|
660
|
+
"hardwareConcurrency",
|
|
661
|
+
"deviceMemory",
|
|
662
|
+
"permissions",
|
|
663
|
+
"plugins",
|
|
664
|
+
"mediaDevices",
|
|
665
|
+
];
|
|
666
|
+
|
|
667
|
+
const DOCUMENT_KEYS = [
|
|
668
|
+
"_reactListeningkfj3eavmks",
|
|
669
|
+
"_reactListeningo743lnnpvdg",
|
|
670
|
+
"location",
|
|
671
|
+
"scrollingElement",
|
|
672
|
+
"documentElement",
|
|
673
|
+
];
|
|
674
|
+
|
|
675
|
+
const WINDOW_KEYS = [
|
|
676
|
+
"webpackChunk_N_E",
|
|
677
|
+
"__NEXT_DATA__",
|
|
678
|
+
"chrome",
|
|
679
|
+
"history",
|
|
680
|
+
"screen",
|
|
681
|
+
"navigation",
|
|
682
|
+
"scrollX",
|
|
683
|
+
"scrollY",
|
|
684
|
+
];
|
|
685
|
+
|
|
686
|
+
function pick<T>(arr: readonly T[]): T {
|
|
687
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function buildPrekeyConfig(userAgent: string, dpl: string, scriptSrc: string): unknown[] {
|
|
691
|
+
const screenSizes = [3000, 4000, 3120, 4160] as const;
|
|
692
|
+
const cores = [8, 16, 24, 32] as const;
|
|
693
|
+
const dateStr = new Date().toString();
|
|
694
|
+
const perfNow = performance.now();
|
|
695
|
+
const epochOffset = Date.now() - perfNow;
|
|
696
|
+
|
|
697
|
+
return [
|
|
698
|
+
pick(screenSizes),
|
|
699
|
+
dateStr,
|
|
700
|
+
4294705152,
|
|
701
|
+
0, // mutated by solver
|
|
702
|
+
userAgent,
|
|
703
|
+
scriptSrc,
|
|
704
|
+
dpl,
|
|
705
|
+
"en-US",
|
|
706
|
+
"en-US,en",
|
|
707
|
+
0, // mutated by solver
|
|
708
|
+
pick(NAVIGATOR_KEYS),
|
|
709
|
+
pick(DOCUMENT_KEYS),
|
|
710
|
+
pick(WINDOW_KEYS),
|
|
711
|
+
perfNow,
|
|
712
|
+
randomUUID(),
|
|
713
|
+
"",
|
|
714
|
+
pick(cores),
|
|
715
|
+
epochOffset,
|
|
716
|
+
];
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Build the `p` (prekey) value sent in the chat-requirements POST body.
|
|
721
|
+
*
|
|
722
|
+
* Format: "<prefix>" + base64(JSON(config)), with a PoW solver loop mutating
|
|
723
|
+
* config[3] to find a hash whose hex prefix is ≤ the target difficulty.
|
|
724
|
+
* Mirrors chat2api / openai-sentinel.
|
|
725
|
+
* - prepare: prefix="gAAAAAC", seed="" (target "0fffff")
|
|
726
|
+
* - chat-requirements: prefix="gAAAAAB", seed=<server seed> (target=difficulty)
|
|
727
|
+
*
|
|
728
|
+
* Submitting an unsolved token still works on low-friction accounts, so we
|
|
729
|
+
* fall back to that after exhausting the iteration budget — but emit a warn
|
|
730
|
+
* log so production can see when it happens.
|
|
731
|
+
*/
|
|
732
|
+
// PoW solvers run up to 100k–500k SHA3-512 hashes. To avoid blocking the
|
|
733
|
+
// Node event loop on a busy server, we yield with `setImmediate` every
|
|
734
|
+
// POW_YIELD_EVERY iterations — roughly every ~5ms of work — so concurrent
|
|
735
|
+
// requests and I/O still get scheduled. Wall time is approximately the same
|
|
736
|
+
// as the synchronous version; what changes is fairness, not throughput.
|
|
737
|
+
const POW_YIELD_EVERY = 1000;
|
|
738
|
+
|
|
739
|
+
function yieldToEventLoop(): Promise<void> {
|
|
740
|
+
return new Promise((resolve) => setImmediate(resolve));
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
interface PowOptions {
|
|
744
|
+
config: unknown[];
|
|
745
|
+
seed: string;
|
|
746
|
+
target: string;
|
|
747
|
+
prefix: string;
|
|
748
|
+
maxIter: number;
|
|
749
|
+
label: string;
|
|
750
|
+
log?: { warn?: (tag: string, msg: string) => void } | null;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
async function solvePow(opts: PowOptions): Promise<string> {
|
|
754
|
+
const cfg = [...opts.config];
|
|
755
|
+
for (let i = 0; i < opts.maxIter; i++) {
|
|
756
|
+
if (i > 0 && i % POW_YIELD_EVERY === 0) await yieldToEventLoop();
|
|
757
|
+
cfg[3] = i;
|
|
758
|
+
const json = JSON.stringify(cfg);
|
|
759
|
+
const b64 = Buffer.from(json).toString("base64");
|
|
760
|
+
// Portable SHA3-512 — pure-JS fallback under Electron/BoringSSL (#5531).
|
|
761
|
+
const hash = sha3_512Hex(opts.seed + b64);
|
|
762
|
+
if (opts.target && hash.slice(0, opts.target.length) <= opts.target) {
|
|
763
|
+
return `${opts.prefix}${b64}`;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
opts.log?.warn?.(
|
|
767
|
+
"CGPT-WEB",
|
|
768
|
+
`PoW (${opts.label}) exhausted ${opts.maxIter} iterations against target=${opts.target || "<empty>"}; submitting unsolved token (Sentinel may reject)`
|
|
769
|
+
);
|
|
770
|
+
const b64 = Buffer.from(JSON.stringify(cfg)).toString("base64");
|
|
771
|
+
return `${opts.prefix}${b64}`;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
async function buildPrepareToken(
|
|
775
|
+
config: unknown[],
|
|
776
|
+
log?: { warn?: (tag: string, msg: string) => void } | null
|
|
777
|
+
): Promise<string> {
|
|
778
|
+
return solvePow({
|
|
779
|
+
config,
|
|
780
|
+
seed: "",
|
|
781
|
+
target: "0fffff",
|
|
782
|
+
prefix: "gAAAAAC",
|
|
783
|
+
maxIter: 100_000,
|
|
784
|
+
label: "prepare",
|
|
785
|
+
log,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
async function solveProofOfWork(
|
|
790
|
+
seed: string,
|
|
791
|
+
difficulty: string,
|
|
792
|
+
config: unknown[],
|
|
793
|
+
log?: { warn?: (tag: string, msg: string) => void } | null
|
|
794
|
+
): Promise<string> {
|
|
795
|
+
return solvePow({
|
|
796
|
+
config,
|
|
797
|
+
seed,
|
|
798
|
+
target: (difficulty || "").toLowerCase(),
|
|
799
|
+
prefix: "gAAAAAB",
|
|
800
|
+
maxIter: 500_000,
|
|
801
|
+
label: "conversation",
|
|
802
|
+
log,
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// ─── OpenAI → ChatGPT message translation ───────────────────────────────────
|
|
807
|
+
|
|
808
|
+
interface ParsedMessages {
|
|
809
|
+
systemMsg: string;
|
|
810
|
+
history: Array<{ role: string; content: string }>;
|
|
811
|
+
currentMsg: string;
|
|
812
|
+
latestImageContext: ChatGptImageConversationContext | null;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Strip embedded `data:image/...` URIs out of message content so prior
|
|
817
|
+
* generated images don't get fed back into chatgpt.com on the next turn.
|
|
818
|
+
*
|
|
819
|
+
* Why: when image generation succeeds we emit ``
|
|
820
|
+
* — frequently 2–4 MB. Chat clients (Open WebUI, OpenAI-style apps) replay
|
|
821
|
+
* the full conversation history on the next request, so without this strip
|
|
822
|
+
* we'd send megabytes of base64 back upstream. chatgpt.com responds with an
|
|
823
|
+
* empty body when that happens (verified: 502 "ChatGPT returned empty
|
|
824
|
+
* response body" on the very next turn after an image gen succeeds), and
|
|
825
|
+
* even if it didn't, a single inlined image is well past the model's context
|
|
826
|
+
* limit. Replacing with a short placeholder keeps semantic continuity
|
|
827
|
+
* without the bytes.
|
|
828
|
+
*/
|
|
829
|
+
const DATA_URI_IMAGE_RE = /!\[([^\]]*)\]\(data:image\/[^)]+\)/g;
|
|
830
|
+
const CACHED_IMAGE_URL_RE = /\/v1\/chatgpt-web\/image\/([a-f0-9]{16,64})(?=[)\s"'<>]|$)/gi;
|
|
831
|
+
|
|
832
|
+
function stripInlinedImages(content: string): string {
|
|
833
|
+
return content.replace(DATA_URI_IMAGE_RE, (_, alt) =>
|
|
834
|
+
alt ? `[${alt}: generated image]` : "[generated image]"
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function findCachedImageContext(content: string): ChatGptImageConversationContext | null {
|
|
839
|
+
let latest: ChatGptImageConversationContext | null = null;
|
|
840
|
+
// String.prototype.matchAll consumes a fresh iterator and ignores the
|
|
841
|
+
// regex's lastIndex, so no manual reset is required.
|
|
842
|
+
for (const match of content.matchAll(CACHED_IMAGE_URL_RE)) {
|
|
843
|
+
const id = match[1];
|
|
844
|
+
const context = getChatGptImageConversationContext(id);
|
|
845
|
+
if (context) latest = context;
|
|
846
|
+
}
|
|
847
|
+
return latest;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
function parseOpenAIMessages(messages: Array<Record<string, unknown>>): ParsedMessages {
|
|
851
|
+
let systemMsg = "";
|
|
852
|
+
const history: Array<{ role: string; content: string }> = [];
|
|
853
|
+
let latestImageContext: ChatGptImageConversationContext | null = null;
|
|
854
|
+
|
|
855
|
+
for (const msg of messages) {
|
|
856
|
+
let role = String(msg.role || "user");
|
|
857
|
+
if (role === "developer") role = "system";
|
|
858
|
+
|
|
859
|
+
let content = "";
|
|
860
|
+
if (typeof msg.content === "string") {
|
|
861
|
+
content = msg.content;
|
|
862
|
+
} else if (Array.isArray(msg.content)) {
|
|
863
|
+
content = (msg.content as Array<Record<string, unknown>>)
|
|
864
|
+
.filter((c) => c.type === "text")
|
|
865
|
+
.map((c) => String(c.text || ""))
|
|
866
|
+
.join(" ");
|
|
867
|
+
}
|
|
868
|
+
content = stripInlinedImages(content);
|
|
869
|
+
const imageContext = findCachedImageContext(content);
|
|
870
|
+
if (imageContext) latestImageContext = imageContext;
|
|
871
|
+
if (!content.trim()) continue;
|
|
872
|
+
|
|
873
|
+
if (role === "system") {
|
|
874
|
+
systemMsg += (systemMsg ? "\n" : "") + content;
|
|
875
|
+
} else if (role === "user" || role === "assistant") {
|
|
876
|
+
history.push({ role, content });
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
let currentMsg = "";
|
|
881
|
+
if (history.length > 0 && history[history.length - 1].role === "user") {
|
|
882
|
+
currentMsg = history.pop()!.content;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return { systemMsg, history, currentMsg, latestImageContext };
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
interface ChatGptMessage {
|
|
889
|
+
id: string;
|
|
890
|
+
author: { role: string };
|
|
891
|
+
content: { content_type: "text"; parts: string[] };
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Cheap heuristic: does the last user turn look like an image-generation
|
|
896
|
+
* request? Used to decide whether to disable Temporary Chat mode.
|
|
897
|
+
*
|
|
898
|
+
* Why a heuristic instead of always disabling Temporary Chat: when
|
|
899
|
+
* `history_and_training_disabled: false`, every conversation gets saved to
|
|
900
|
+
* the user's chatgpt.com history. For text-only chats that's noise — a
|
|
901
|
+
* dozen "OmniRoute" entries clutter the sidebar and can interact with
|
|
902
|
+
* ChatGPT's memory. We pay that cost only when the user actually wants an
|
|
903
|
+
* image, since Temporary Chat refuses image_gen with the message
|
|
904
|
+
* "I cannot generate images in this chat".
|
|
905
|
+
*
|
|
906
|
+
* False positives (text chat misclassified as image) → unnecessary history
|
|
907
|
+
* entry. False negatives (image request misclassified as text) → ChatGPT
|
|
908
|
+
* refuses image_gen and the user retries. Tuning leans toward false
|
|
909
|
+
* positives (we'd rather pollute history than refuse image generation).
|
|
910
|
+
*/
|
|
911
|
+
const IMAGE_GEN_REGEXES: RegExp[] = [
|
|
912
|
+
// verb + (anything within 40 chars) + image-noun
|
|
913
|
+
/\b(?:generate|create|make|draw|paint|render|produce|design|sketch|illustrate|show me)\b[\s\S]{0,40}\b(?:image|picture|photo|photograph|drawing|illustration|sketch|painting|portrait|logo|icon|art|artwork|wallpaper|render|graphic)\b/i,
|
|
914
|
+
// image-noun + "of" — "image of a kitten", "picture of mountains"
|
|
915
|
+
/\b(?:image|picture|photo|photograph|illustration|drawing|painting|render)\s+of\b/i,
|
|
916
|
+
// direct verb + a/an article — "draw a kitten", "paint an apple"
|
|
917
|
+
/\b(?:draw|paint|sketch|render|illustrate)\s+(?:me\s+)?(?:a|an|some|the)\s+\w+/i,
|
|
918
|
+
// explicit slash command users sometimes type — "/imagine ..."
|
|
919
|
+
/^\s*\/(?:image|imagine|img|draw|paint)\b/im,
|
|
920
|
+
];
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Markers Open WebUI uses for its background tool prompts (follow-up
|
|
924
|
+
* suggestions, title generation, tag categorization). These prompts embed
|
|
925
|
+
* the prior conversation in `<chat_history>` blocks and frequently quote
|
|
926
|
+
* the user's earlier "generate an image of..." request — which would
|
|
927
|
+
* trip the image-gen regex below. Skip them so we don't unnecessarily
|
|
928
|
+
* disable Temporary Chat and trigger image_gen on background tasks.
|
|
929
|
+
*
|
|
930
|
+
* Catching just one of these markers is enough; tool prompts always
|
|
931
|
+
* include several together.
|
|
932
|
+
*/
|
|
933
|
+
const OPENWEBUI_TOOL_PROMPT_MARKERS = [
|
|
934
|
+
/<chat_history>/i,
|
|
935
|
+
/^### Task:/im,
|
|
936
|
+
/\bJSON format:\s*\{/i,
|
|
937
|
+
/\bfollow_?ups\b.*\barray of strings\b/i,
|
|
938
|
+
];
|
|
939
|
+
|
|
940
|
+
const OPENWEBUI_IMAGE_CONTEXT_MARKERS = [
|
|
941
|
+
/<context>\s*The requested image has been (?:created|edited and created) by the system successfully/i,
|
|
942
|
+
/<context>\s*The requested image has been edited and created and is now being shown to the user/i,
|
|
943
|
+
/<context>\s*Image generation was attempted but failed/i,
|
|
944
|
+
];
|
|
945
|
+
|
|
946
|
+
function hasOpenWebUIImageContext(parsed: ParsedMessages): boolean {
|
|
947
|
+
return OPENWEBUI_IMAGE_CONTEXT_MARKERS.some((re) => re.test(parsed.systemMsg));
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
function looksLikeImageGenRequest(parsed: ParsedMessages): boolean {
|
|
951
|
+
// Inspect only the latest user turn — historical turns are irrelevant
|
|
952
|
+
// (and could trigger false positives if the user mentioned an image
|
|
953
|
+
// generated previously).
|
|
954
|
+
const text = parsed.currentMsg.trim();
|
|
955
|
+
if (!text) return false;
|
|
956
|
+
if (OPENWEBUI_TOOL_PROMPT_MARKERS.some((re) => re.test(text))) return false;
|
|
957
|
+
if (hasOpenWebUIImageContext(parsed)) return false;
|
|
958
|
+
return IMAGE_GEN_REGEXES.some((re) => re.test(text));
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
const IMAGE_EDIT_REGEXES: RegExp[] = [
|
|
962
|
+
/\b(?:edit|adjust|modify|change|update|alter|revise|retouch|fix)\b[\s\S]{0,120}\b(?:it|image|picture|photo|lighting|background|style|color|colour|composition|scene|time of day)\b/i,
|
|
963
|
+
/\b(?:make|turn|set|switch)\s+(?:it|the\s+(?:image|picture|photo|scene))\b[\s\S]{0,120}\b/i,
|
|
964
|
+
/\b(?:add|remove|replace)\b[\s\S]{0,120}\b(?:it|image|picture|photo|background|sky|person|object|text|logo)\b/i,
|
|
965
|
+
/\b(?:brighter|darker|night|daytime|time of day|sunset|sunrise|morning|evening|lighting|relight|background|style)\b/i,
|
|
966
|
+
/^\s*(?:now|then|also)\b[\s\S]{0,120}\b(?:make|turn|change|adjust|add|remove|replace|edit)\b/i,
|
|
967
|
+
];
|
|
968
|
+
|
|
969
|
+
function looksLikeImageEditRequest(parsed: ParsedMessages): boolean {
|
|
970
|
+
if (!parsed.latestImageContext) return false;
|
|
971
|
+
const text = parsed.currentMsg.trim();
|
|
972
|
+
if (!text) return false;
|
|
973
|
+
if (OPENWEBUI_TOOL_PROMPT_MARKERS.some((re) => re.test(text))) return false;
|
|
974
|
+
if (hasOpenWebUIImageContext(parsed)) return false;
|
|
975
|
+
return IMAGE_EDIT_REGEXES.some((re) => re.test(text));
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
function buildConversationBody(
|
|
979
|
+
parsed: ParsedMessages,
|
|
980
|
+
modelSlug: string,
|
|
981
|
+
parentMessageId: string,
|
|
982
|
+
options: {
|
|
983
|
+
// Keep text/API calls in Temporary Chat so they do not clutter the user's
|
|
984
|
+
// chatgpt.com history. Disable Temporary Chat only when ChatGPT needs a
|
|
985
|
+
// durable image conversation (image generation/editing).
|
|
986
|
+
persistConversation: boolean;
|
|
987
|
+
thinkingEffort: "standard" | "extended" | null;
|
|
988
|
+
continuation?: ChatGptImageConversationContext | null;
|
|
989
|
+
}
|
|
990
|
+
): Record<string, unknown> {
|
|
991
|
+
// Critical: do NOT send prior turns as separate `assistant` and `user`
|
|
992
|
+
// messages in the `messages` array. ChatGPT's web API ("action: next")
|
|
993
|
+
// treats those as in-progress turns and the model will literally CONTINUE
|
|
994
|
+
// a prior assistant response in the new generation — observed as
|
|
995
|
+
// `[1] -> [12] -> [1123]` across three turns.
|
|
996
|
+
//
|
|
997
|
+
// Instead, fold all prior history into the system message and send only
|
|
998
|
+
// the current user message as a single new turn. The model then sees a
|
|
999
|
+
// single prompt with full context and responds fresh.
|
|
1000
|
+
const systemParts: string[] = [];
|
|
1001
|
+
if (parsed.systemMsg.trim()) {
|
|
1002
|
+
systemParts.push(parsed.systemMsg.trim());
|
|
1003
|
+
}
|
|
1004
|
+
const continuation = options.continuation ?? null;
|
|
1005
|
+
|
|
1006
|
+
if (!continuation && parsed.history.length > 0) {
|
|
1007
|
+
const formatted = parsed.history
|
|
1008
|
+
.map((h) => `${h.role === "assistant" ? "Assistant" : "User"}: ${h.content}`)
|
|
1009
|
+
.join("\n\n");
|
|
1010
|
+
systemParts.push(
|
|
1011
|
+
`Prior conversation (for context — answer only the new user message below):\n\n${formatted}`
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
const messages: ChatGptMessage[] = [];
|
|
1016
|
+
if (systemParts.length > 0) {
|
|
1017
|
+
messages.push({
|
|
1018
|
+
id: randomUUID(),
|
|
1019
|
+
author: { role: "system" },
|
|
1020
|
+
content: { content_type: "text", parts: [systemParts.join("\n\n")] },
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
const currentUserContent = hasOpenWebUIImageContext(parsed)
|
|
1025
|
+
? "Briefly acknowledge the image result described in the system context. Do not generate, edit, or request another image."
|
|
1026
|
+
: parsed.currentMsg || "";
|
|
1027
|
+
|
|
1028
|
+
messages.push({
|
|
1029
|
+
id: randomUUID(),
|
|
1030
|
+
author: { role: "user" },
|
|
1031
|
+
content: { content_type: "text", parts: [currentUserContent] },
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
return {
|
|
1035
|
+
action: "next",
|
|
1036
|
+
messages,
|
|
1037
|
+
model: modelSlug,
|
|
1038
|
+
// Text-only API-style requests start fresh because clients replay full
|
|
1039
|
+
// history. Generated-image edits are the exception: ChatGPT needs the
|
|
1040
|
+
// original conversation node to adjust the actual image, not just a
|
|
1041
|
+
// markdown URL echoed back in a synthetic history block.
|
|
1042
|
+
conversation_id: continuation?.conversationId ?? null,
|
|
1043
|
+
parent_message_id: continuation?.parentMessageId ?? parentMessageId,
|
|
1044
|
+
timezone_offset_min: -new Date().getTimezoneOffset(),
|
|
1045
|
+
// Temporary Chat is the default. Disable it only for image generation /
|
|
1046
|
+
// image edits, where ChatGPT needs durable conversation state for tools.
|
|
1047
|
+
history_and_training_disabled: !options.persistConversation,
|
|
1048
|
+
suggestions: [],
|
|
1049
|
+
websocket_request_id: randomUUID(),
|
|
1050
|
+
conversation_mode: { kind: "primary_assistant" },
|
|
1051
|
+
supports_buffering: true,
|
|
1052
|
+
force_parallel_switch: "auto",
|
|
1053
|
+
paragen_cot_summary_display_override: "allow",
|
|
1054
|
+
...(options.thinkingEffort ? { thinking_effort: options.thinkingEffort } : {}),
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// ─── ChatGPT SSE parsing ────────────────────────────────────────────────────
|
|
1059
|
+
|
|
1060
|
+
interface ChatGptStreamEvent {
|
|
1061
|
+
message?: {
|
|
1062
|
+
id?: string;
|
|
1063
|
+
author?: { role?: string };
|
|
1064
|
+
content?: { content_type?: string; parts?: unknown[] };
|
|
1065
|
+
status?: string;
|
|
1066
|
+
metadata?: Record<string, unknown>;
|
|
1067
|
+
};
|
|
1068
|
+
conversation_id?: string;
|
|
1069
|
+
error?: string | { message?: string; code?: string };
|
|
1070
|
+
type?: string;
|
|
1071
|
+
v?: unknown;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
/**
|
|
1075
|
+
* A part inside `content.parts` for a `multimodal_text` content_type.
|
|
1076
|
+
* ChatGPT puts image references in a part with content_type "image_asset_pointer"
|
|
1077
|
+
* and an asset_pointer like "file-service://file-XXXX" (final) or
|
|
1078
|
+
* "sediment://..." (in-progress preview).
|
|
1079
|
+
*/
|
|
1080
|
+
interface ImageAssetPart {
|
|
1081
|
+
content_type?: string;
|
|
1082
|
+
asset_pointer?: string;
|
|
1083
|
+
width?: number;
|
|
1084
|
+
height?: number;
|
|
1085
|
+
metadata?: Record<string, unknown>;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
async function* readChatGptSseEvents(
|
|
1089
|
+
body: ReadableStream<Uint8Array>,
|
|
1090
|
+
signal?: AbortSignal | null
|
|
1091
|
+
): AsyncGenerator<ChatGptStreamEvent> {
|
|
1092
|
+
const reader = body.getReader();
|
|
1093
|
+
const decoder = new TextDecoder();
|
|
1094
|
+
let buffer = "";
|
|
1095
|
+
let dataLines: string[] = [];
|
|
1096
|
+
let eventName: string | null = null;
|
|
1097
|
+
|
|
1098
|
+
function flush(): ChatGptStreamEvent | null | "done" {
|
|
1099
|
+
if (dataLines.length === 0) {
|
|
1100
|
+
eventName = null;
|
|
1101
|
+
return null;
|
|
1102
|
+
}
|
|
1103
|
+
const payload = dataLines.join("\n");
|
|
1104
|
+
dataLines = [];
|
|
1105
|
+
const sseEventName = eventName;
|
|
1106
|
+
eventName = null;
|
|
1107
|
+
const trimmed = payload.trim();
|
|
1108
|
+
if (!trimmed || trimmed === "[DONE]") return "done";
|
|
1109
|
+
try {
|
|
1110
|
+
const parsed = JSON.parse(trimmed) as ChatGptStreamEvent;
|
|
1111
|
+
if (sseEventName && !parsed.type) parsed.type = sseEventName;
|
|
1112
|
+
return parsed;
|
|
1113
|
+
} catch {
|
|
1114
|
+
console.warn("[chatgpt-web] stream event JSON parse failed");
|
|
1115
|
+
return null;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
try {
|
|
1120
|
+
while (true) {
|
|
1121
|
+
if (signal?.aborted) return;
|
|
1122
|
+
const { value, done } = await reader.read();
|
|
1123
|
+
if (done) break;
|
|
1124
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1125
|
+
|
|
1126
|
+
while (true) {
|
|
1127
|
+
const idx = buffer.indexOf("\n");
|
|
1128
|
+
if (idx < 0) break;
|
|
1129
|
+
const rawLine = buffer.slice(0, idx);
|
|
1130
|
+
buffer = buffer.slice(idx + 1);
|
|
1131
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
1132
|
+
|
|
1133
|
+
if (line === "") {
|
|
1134
|
+
const parsed = flush();
|
|
1135
|
+
if (parsed === "done") return;
|
|
1136
|
+
if (parsed) yield parsed;
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
if (line.startsWith("event:")) {
|
|
1140
|
+
eventName = line.slice(6).trim();
|
|
1141
|
+
} else if (line.startsWith("data:")) {
|
|
1142
|
+
dataLines.push(line.slice(5).trimStart());
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
buffer += decoder.decode();
|
|
1148
|
+
if (buffer.trim().startsWith("data:")) {
|
|
1149
|
+
dataLines.push(buffer.trim().slice(5).trimStart());
|
|
1150
|
+
}
|
|
1151
|
+
const tail = flush();
|
|
1152
|
+
if (tail && tail !== "done") yield tail;
|
|
1153
|
+
} finally {
|
|
1154
|
+
reader.releaseLock();
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// ─── Content extraction ─────────────────────────────────────────────────────
|
|
1159
|
+
// ChatGPT SSE chunks contain CUMULATIVE content (full text so far in `parts[0]`),
|
|
1160
|
+
// not deltas. Diff against the emitted length to produce incremental tokens —
|
|
1161
|
+
// same pattern perplexity-web.ts uses for markdown blocks (lines 386-397).
|
|
1162
|
+
|
|
1163
|
+
interface ContentChunk {
|
|
1164
|
+
delta?: string;
|
|
1165
|
+
answer?: string;
|
|
1166
|
+
conversationId?: string;
|
|
1167
|
+
messageId?: string;
|
|
1168
|
+
error?: string;
|
|
1169
|
+
done?: boolean;
|
|
1170
|
+
/** Image asset pointers seen on the current message (e.g. file-service://file-abc). */
|
|
1171
|
+
imagePointers?: ImagePointerRef[];
|
|
1172
|
+
/**
|
|
1173
|
+
* True if the assistant invoked the async image_gen tool (we saw a task id
|
|
1174
|
+
* in metadata or `turn_use_case: "image gen"` in server_ste_metadata).
|
|
1175
|
+
* Set on the final `done: true` chunk so the caller can decide to poll the
|
|
1176
|
+
* conversation endpoint for the actual image.
|
|
1177
|
+
*/
|
|
1178
|
+
imageGenAsync?: boolean;
|
|
1179
|
+
/** True when ChatGPT handed the turn off to a long-running worker. */
|
|
1180
|
+
handoff?: boolean;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
interface ImagePointerRef {
|
|
1184
|
+
pointer: string;
|
|
1185
|
+
messageId?: string;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
/**
|
|
1189
|
+
* Pull image asset pointers out of a multimodal_text parts array.
|
|
1190
|
+
*
|
|
1191
|
+
* For text-only messages parts is `["text..."]` and this returns `[]`. For
|
|
1192
|
+
* `image_gen` tool output, parts looks like:
|
|
1193
|
+
* [
|
|
1194
|
+
* { content_type: "image_asset_pointer",
|
|
1195
|
+
* asset_pointer: "file-service://file-abc..." or "sediment://..." }
|
|
1196
|
+
* ]
|
|
1197
|
+
* We collect every asset_pointer seen so the caller can resolve them once
|
|
1198
|
+
* the stream terminates.
|
|
1199
|
+
*/
|
|
1200
|
+
function extractImagePointers(parts: unknown[]): string[] {
|
|
1201
|
+
const out: string[] = [];
|
|
1202
|
+
for (const p of parts) {
|
|
1203
|
+
if (!p || typeof p !== "object") continue;
|
|
1204
|
+
const obj = p as ImageAssetPart;
|
|
1205
|
+
if (obj.content_type === "image_asset_pointer" && typeof obj.asset_pointer === "string") {
|
|
1206
|
+
out.push(obj.asset_pointer);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
return out;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
async function* extractContent(
|
|
1213
|
+
eventStream: ReadableStream<Uint8Array>,
|
|
1214
|
+
signal?: AbortSignal | null
|
|
1215
|
+
): AsyncGenerator<ContentChunk> {
|
|
1216
|
+
// ChatGPT may echo prior assistant turns at the start of the stream with
|
|
1217
|
+
// status: "finished_successfully" and full content, before sending the new
|
|
1218
|
+
// generation. If we emit those bytes downstream, streaming consumers see
|
|
1219
|
+
// the previous answer prepended to the new one (visible in Open WebUI as
|
|
1220
|
+
// run-on output across turns). Strategy: only emit deltas after we've seen
|
|
1221
|
+
// status === "in_progress" for the current message id (i.e., it's being
|
|
1222
|
+
// generated live in this stream). Echoes always arrive already finished
|
|
1223
|
+
// and never transition through in_progress, so they get suppressed. An
|
|
1224
|
+
// end-of-stream fallback handles the rare case where a real turn arrives
|
|
1225
|
+
// as a single already-finished event (instant/cached responses).
|
|
1226
|
+
let conversationId: string | null = null;
|
|
1227
|
+
let currentId: string | null = null;
|
|
1228
|
+
let currentParts = "";
|
|
1229
|
+
let emittedLen = 0;
|
|
1230
|
+
let isLive = false;
|
|
1231
|
+
// Dedupe pointers across echoes / repeated events. Order-preserving Set.
|
|
1232
|
+
const imagePointers = new Map<string, ImagePointerRef>();
|
|
1233
|
+
// True if we observed signals the assistant kicked off the async image_gen
|
|
1234
|
+
// tool (see ContentChunk.imageGenAsync). The actual image arrives later via
|
|
1235
|
+
// WebSocket / polling — caller handles that.
|
|
1236
|
+
let imageGenAsync = false;
|
|
1237
|
+
let handoff = false;
|
|
1238
|
+
|
|
1239
|
+
for await (const event of readChatGptSseEvents(eventStream, signal)) {
|
|
1240
|
+
if (event.error) {
|
|
1241
|
+
const msg =
|
|
1242
|
+
typeof event.error === "string"
|
|
1243
|
+
? event.error
|
|
1244
|
+
: event.error.message || "ChatGPT stream error";
|
|
1245
|
+
yield { error: msg, done: true };
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
if (event.conversation_id) conversationId = event.conversation_id;
|
|
1250
|
+
|
|
1251
|
+
if (event.type === "stream_handoff") {
|
|
1252
|
+
handoff = true;
|
|
1253
|
+
yield {
|
|
1254
|
+
conversationId: conversationId ?? undefined,
|
|
1255
|
+
handoff: true,
|
|
1256
|
+
};
|
|
1257
|
+
continue;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// Detect image_gen on top-level "server_ste_metadata" events. These don't
|
|
1261
|
+
// have a `message` field so the post-message guard would skip them, but
|
|
1262
|
+
// they're the most reliable signal — `turn_use_case: "image gen"`.
|
|
1263
|
+
//
|
|
1264
|
+
// Originally we also accepted `meta.tool_invoked === true`, but ChatGPT
|
|
1265
|
+
// sets that flag for ANY internal tool the assistant uses (reasoning
|
|
1266
|
+
// chains, web search, calc, file_search, etc.). That made plain text
|
|
1267
|
+
// turns spuriously emit the "Generating image…" placeholder + 30s
|
|
1268
|
+
// WebSocket wait. Image gen has a more specific signal we can rely on:
|
|
1269
|
+
// either `turn_use_case === "image gen"` here, or an `image_gen_task_id`
|
|
1270
|
+
// on a tool-role message (handled below).
|
|
1271
|
+
if (event.type === "server_ste_metadata") {
|
|
1272
|
+
const meta = (event as Record<string, unknown>).metadata as
|
|
1273
|
+
Record<string, unknown> | undefined;
|
|
1274
|
+
if (meta && meta.turn_use_case === "image gen") {
|
|
1275
|
+
imageGenAsync = true;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
const m = event.message;
|
|
1280
|
+
if (!m) continue;
|
|
1281
|
+
|
|
1282
|
+
// Tool messages with `image_gen_task_id` in metadata (the "Processing
|
|
1283
|
+
// image..." card) confirm the async image_gen flow. We don't surface the
|
|
1284
|
+
// tool message itself as text — it's just a placeholder — but we mark
|
|
1285
|
+
// imageGenAsync so the executor knows to poll for the final image.
|
|
1286
|
+
if (m.metadata && typeof m.metadata.image_gen_task_id === "string") {
|
|
1287
|
+
imageGenAsync = true;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
if (m.author?.role !== "assistant") continue;
|
|
1291
|
+
|
|
1292
|
+
const id = m.id ?? null;
|
|
1293
|
+
const status = m.status ?? "";
|
|
1294
|
+
|
|
1295
|
+
if (id && id !== currentId) {
|
|
1296
|
+
currentId = id;
|
|
1297
|
+
currentParts = "";
|
|
1298
|
+
emittedLen = 0;
|
|
1299
|
+
isLive = false;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
if (status === "in_progress") {
|
|
1303
|
+
isLive = true;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
const parts = m.content?.parts ?? [];
|
|
1307
|
+
if (parts.length === 0) continue;
|
|
1308
|
+
|
|
1309
|
+
// Image asset pointers: only collect once the message is finalized
|
|
1310
|
+
// (status === "finished_successfully"). The same pointer may also appear
|
|
1311
|
+
// on echoed prior turns at the head of the stream; that's fine — the Set
|
|
1312
|
+
// dedupes, and the resolver in the executor produces the same URL either
|
|
1313
|
+
// way. We could restrict to isLive-only to avoid resolving echoes, but
|
|
1314
|
+
// that makes single-event instant responses (no in_progress phase) lose
|
|
1315
|
+
// their image. Letting echoes through is harmless for correctness; the
|
|
1316
|
+
// executor resolves each unique pointer at most once.
|
|
1317
|
+
if (status === "finished_successfully" || status === "" || isLive) {
|
|
1318
|
+
for (const ptr of extractImagePointers(parts)) {
|
|
1319
|
+
const existing = imagePointers.get(ptr);
|
|
1320
|
+
imagePointers.set(
|
|
1321
|
+
ptr,
|
|
1322
|
+
existing?.messageId ? existing : { pointer: ptr, ...(id ? { messageId: id } : {}) }
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
const cumulative = parts.map((p) => (typeof p === "string" ? p : "")).join("");
|
|
1328
|
+
if (cumulative.length > currentParts.length) {
|
|
1329
|
+
currentParts = cumulative;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
if (isLive && currentParts.length > emittedLen) {
|
|
1333
|
+
const delta = currentParts.slice(emittedLen);
|
|
1334
|
+
emittedLen = currentParts.length;
|
|
1335
|
+
yield {
|
|
1336
|
+
delta,
|
|
1337
|
+
answer: currentParts,
|
|
1338
|
+
conversationId: conversationId ?? undefined,
|
|
1339
|
+
messageId: currentId ?? undefined,
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
// End-of-stream fallback: if we never observed status === "in_progress"
|
|
1345
|
+
// for the current id (single-event reply, cached/instant response), emit
|
|
1346
|
+
// the accumulated content now so the consumer doesn't get an empty stream.
|
|
1347
|
+
if (!isLive && currentParts.length > emittedLen) {
|
|
1348
|
+
yield {
|
|
1349
|
+
delta: currentParts.slice(emittedLen),
|
|
1350
|
+
answer: currentParts,
|
|
1351
|
+
conversationId: conversationId ?? undefined,
|
|
1352
|
+
messageId: currentId ?? undefined,
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
yield {
|
|
1357
|
+
delta: "",
|
|
1358
|
+
answer: currentParts,
|
|
1359
|
+
conversationId: conversationId ?? undefined,
|
|
1360
|
+
messageId: currentId ?? undefined,
|
|
1361
|
+
imagePointers: imagePointers.size > 0 ? Array.from(imagePointers.values()) : undefined,
|
|
1362
|
+
imageGenAsync,
|
|
1363
|
+
handoff,
|
|
1364
|
+
done: true,
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
// ─── Long-running Pro handoff polling ──────────────────────────────────────
|
|
1369
|
+
|
|
1370
|
+
interface ChatGptDetailMessage {
|
|
1371
|
+
id?: string;
|
|
1372
|
+
author?: { role?: string };
|
|
1373
|
+
content?: {
|
|
1374
|
+
content_type?: string;
|
|
1375
|
+
parts?: unknown[];
|
|
1376
|
+
text?: string;
|
|
1377
|
+
};
|
|
1378
|
+
status?: string;
|
|
1379
|
+
end_turn?: boolean;
|
|
1380
|
+
create_time?: number;
|
|
1381
|
+
update_time?: number;
|
|
1382
|
+
metadata?: Record<string, unknown>;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
interface ChatGptConversationDetail {
|
|
1386
|
+
mapping?: Record<string, { message?: ChatGptDetailMessage | null }>;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
interface FinalAssistantAnswer {
|
|
1390
|
+
text: string;
|
|
1391
|
+
messageId?: string;
|
|
1392
|
+
finished: boolean;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
function textFromContentPart(part: unknown): string {
|
|
1396
|
+
if (typeof part === "string") return part;
|
|
1397
|
+
if (!part || typeof part !== "object") return "";
|
|
1398
|
+
const obj = part as Record<string, unknown>;
|
|
1399
|
+
for (const key of ["text", "content", "summary"]) {
|
|
1400
|
+
const value = obj[key];
|
|
1401
|
+
if (typeof value === "string") return value;
|
|
1402
|
+
}
|
|
1403
|
+
return "";
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
function detailMessageText(message: ChatGptDetailMessage): string {
|
|
1407
|
+
const content = message.content;
|
|
1408
|
+
if (!content) return "";
|
|
1409
|
+
if (typeof content.text === "string") return content.text;
|
|
1410
|
+
const parts = content.parts ?? [];
|
|
1411
|
+
return parts.map(textFromContentPart).join("");
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
function extractFinalAssistantAnswer(
|
|
1415
|
+
detail: ChatGptConversationDetail
|
|
1416
|
+
): FinalAssistantAnswer | null {
|
|
1417
|
+
const nodes = Object.values(detail.mapping ?? {});
|
|
1418
|
+
let best: (FinalAssistantAnswer & { sort: number }) | null = null;
|
|
1419
|
+
|
|
1420
|
+
for (const node of nodes) {
|
|
1421
|
+
const message = node.message;
|
|
1422
|
+
if (!message || message.author?.role !== "assistant") continue;
|
|
1423
|
+
if (message.metadata?.is_visually_hidden === true) continue;
|
|
1424
|
+
const contentType = message.content?.content_type ?? "";
|
|
1425
|
+
if (contentType.includes("thought") || contentType.includes("reasoning")) continue;
|
|
1426
|
+
|
|
1427
|
+
const text = detailMessageText(message).trim();
|
|
1428
|
+
if (!text) continue;
|
|
1429
|
+
const finished = message.status === "finished_successfully" && message.end_turn !== false;
|
|
1430
|
+
const sort = message.update_time ?? message.create_time ?? 0;
|
|
1431
|
+
if (
|
|
1432
|
+
!best ||
|
|
1433
|
+
(finished && (!best.finished || sort >= best.sort)) ||
|
|
1434
|
+
(!finished && !best.finished && sort >= best.sort)
|
|
1435
|
+
) {
|
|
1436
|
+
best = { text, messageId: message.id, finished, sort };
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
if (!best) return null;
|
|
1441
|
+
return { text: best.text, messageId: best.messageId, finished: best.finished };
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
function delayWithAbort(ms: number, signal?: AbortSignal | null): Promise<void> {
|
|
1445
|
+
if (ms <= 0) return Promise.resolve();
|
|
1446
|
+
if (signal?.aborted) return Promise.resolve();
|
|
1447
|
+
return new Promise((resolve) => {
|
|
1448
|
+
const timer = setTimeout(() => {
|
|
1449
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1450
|
+
resolve();
|
|
1451
|
+
}, ms);
|
|
1452
|
+
const onAbort = () => {
|
|
1453
|
+
clearTimeout(timer);
|
|
1454
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1455
|
+
resolve();
|
|
1456
|
+
};
|
|
1457
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
function decodeUtf8DataUrl(text: string): string {
|
|
1462
|
+
const marker = ";base64,";
|
|
1463
|
+
if (!text.startsWith("data:") || !text.includes(marker)) return text;
|
|
1464
|
+
const base64 = text.slice(text.indexOf(marker) + marker.length);
|
|
1465
|
+
return new TextDecoder().decode(Buffer.from(base64, "base64"));
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
interface ConversationDetailFetchResult {
|
|
1469
|
+
detail: ChatGptConversationDetail | null;
|
|
1470
|
+
terminal: boolean;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
async function fetchConversationDetail(
|
|
1474
|
+
conversationId: string,
|
|
1475
|
+
ctx: ResolverContext
|
|
1476
|
+
): Promise<ConversationDetailFetchResult> {
|
|
1477
|
+
const url = `${CHATGPT_BASE}/backend-api/conversation/${encodeURIComponent(conversationId)}`;
|
|
1478
|
+
const headers: Record<string, string> = {
|
|
1479
|
+
...browserHeaders(),
|
|
1480
|
+
...oaiHeaders(ctx.sessionId, ctx.deviceId),
|
|
1481
|
+
Accept: "application/json",
|
|
1482
|
+
Authorization: `Bearer ${ctx.accessToken}`,
|
|
1483
|
+
Cookie: buildSessionCookieHeader(ctx.cookie),
|
|
1484
|
+
};
|
|
1485
|
+
if (ctx.accountId) headers["chatgpt-account-id"] = ctx.accountId;
|
|
1486
|
+
|
|
1487
|
+
try {
|
|
1488
|
+
const response = await tlsFetchChatGpt(url, {
|
|
1489
|
+
method: "GET",
|
|
1490
|
+
headers,
|
|
1491
|
+
timeoutMs: 30_000,
|
|
1492
|
+
signal: ctx.signal,
|
|
1493
|
+
// The native tls-client text path can surface UTF-8 JSON as mojibake
|
|
1494
|
+
// (e.g. 👉 becomes 👉). Ask for raw bytes and decode as UTF-8 here so
|
|
1495
|
+
// the final answer appended after Pro stream_handoff preserves Unicode.
|
|
1496
|
+
byteResponse: true,
|
|
1497
|
+
});
|
|
1498
|
+
if (response.status >= 400) {
|
|
1499
|
+
ctx.log?.warn?.(
|
|
1500
|
+
"CGPT-WEB",
|
|
1501
|
+
`conversation poll ${response.status}: ${(response.text || "").slice(0, 300)}`
|
|
1502
|
+
);
|
|
1503
|
+
return { detail: null, terminal: [401, 403, 404].includes(response.status) };
|
|
1504
|
+
}
|
|
1505
|
+
if (!response.text) return { detail: null, terminal: false };
|
|
1506
|
+
return {
|
|
1507
|
+
detail: JSON.parse(decodeUtf8DataUrl(response.text)) as ChatGptConversationDetail,
|
|
1508
|
+
terminal: false,
|
|
1509
|
+
};
|
|
1510
|
+
} catch (err) {
|
|
1511
|
+
ctx.log?.warn?.(
|
|
1512
|
+
"CGPT-WEB",
|
|
1513
|
+
`conversation poll failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1514
|
+
);
|
|
1515
|
+
return { detail: null, terminal: false };
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
async function pollForFinalAssistantAnswer(
|
|
1520
|
+
conversationId: string,
|
|
1521
|
+
ctx: ResolverContext
|
|
1522
|
+
): Promise<FinalAssistantAnswer | null> {
|
|
1523
|
+
const started = Date.now();
|
|
1524
|
+
const timeoutMs = configuredProPollTimeoutMs();
|
|
1525
|
+
const intervalMs = configuredProPollIntervalMs();
|
|
1526
|
+
let last: FinalAssistantAnswer | null = null;
|
|
1527
|
+
let terminalPollFailure = false;
|
|
1528
|
+
|
|
1529
|
+
while (!ctx.signal?.aborted && Date.now() - started < timeoutMs) {
|
|
1530
|
+
const { detail, terminal } = await fetchConversationDetail(conversationId, ctx);
|
|
1531
|
+
if (detail) {
|
|
1532
|
+
const answer = extractFinalAssistantAnswer(detail);
|
|
1533
|
+
if (answer) {
|
|
1534
|
+
last = answer;
|
|
1535
|
+
if (answer.finished) return answer;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
if (terminal) {
|
|
1539
|
+
terminalPollFailure = true;
|
|
1540
|
+
break;
|
|
1541
|
+
}
|
|
1542
|
+
const remaining = timeoutMs - (Date.now() - started);
|
|
1543
|
+
if (remaining <= 0) break;
|
|
1544
|
+
await delayWithAbort(Math.min(intervalMs, remaining), ctx.signal);
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
if (last) {
|
|
1548
|
+
ctx.log?.warn?.(
|
|
1549
|
+
"CGPT-WEB",
|
|
1550
|
+
terminalPollFailure
|
|
1551
|
+
? `conversation poll stopped before finished_successfully; returning latest assistant text for ${conversationId}`
|
|
1552
|
+
: `conversation poll timed out before finished_successfully; returning latest assistant text for ${conversationId}`
|
|
1553
|
+
);
|
|
1554
|
+
} else {
|
|
1555
|
+
ctx.log?.warn?.(
|
|
1556
|
+
"CGPT-WEB",
|
|
1557
|
+
terminalPollFailure
|
|
1558
|
+
? `conversation poll stopped without assistant text for ${conversationId}`
|
|
1559
|
+
: `conversation poll timed out without assistant text for ${conversationId}`
|
|
1560
|
+
);
|
|
1561
|
+
}
|
|
1562
|
+
return last;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
// ─── OpenAI SSE format ──────────────────────────────────────────────────────
|
|
1566
|
+
|
|
1567
|
+
function sseChunk(data: unknown): string {
|
|
1568
|
+
return `data: ${JSON.stringify(data)}\n\n`;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
/**
|
|
1572
|
+
* Resolves a ChatGPT asset_pointer to a downloadable URL, given the live
|
|
1573
|
+
* conversation_id (needed for sediment:// pointers). Returns null on failure
|
|
1574
|
+
* so the caller can decide whether to surface a placeholder or skip silently.
|
|
1575
|
+
*/
|
|
1576
|
+
type ImageResolver = (
|
|
1577
|
+
assetPointer: string,
|
|
1578
|
+
conversationId: string | null,
|
|
1579
|
+
parentMessageId?: string | null
|
|
1580
|
+
) => Promise<string | null>;
|
|
1581
|
+
|
|
1582
|
+
/**
|
|
1583
|
+
* True when ChatGPT emitted an image asset pointer (the image WAS generated
|
|
1584
|
+
* upstream) but none of the pointers could be resolved to a downloadable URL
|
|
1585
|
+
* — so the assistant text carries no image markdown. Lets callers surface an
|
|
1586
|
+
* accurate "generated but not retrievable" error instead of the misleading
|
|
1587
|
+
* "no image was produced". Escalated mesh report: image visible in the ChatGPT
|
|
1588
|
+
* chat but returned to OmniRoute as a bare "completed without image markdown".
|
|
1589
|
+
*/
|
|
1590
|
+
export function detectImageResolutionFailure(
|
|
1591
|
+
pointerCount: number,
|
|
1592
|
+
resolvedCount: number
|
|
1593
|
+
): boolean {
|
|
1594
|
+
return pointerCount > 0 && resolvedCount === 0;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/** Build the final markdown block for a list of resolved image URLs. */
|
|
1598
|
+
function imageMarkdown(urls: string[]): string {
|
|
1599
|
+
if (urls.length === 0) return "";
|
|
1600
|
+
// Two leading newlines → ensure separation from any prior text the model
|
|
1601
|
+
// produced ("Here is your kitten:\n\n"). One image per line.
|
|
1602
|
+
return "\n\n" + urls.map((u) => ``).join("\n\n");
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
async function resolveImagePointers(
|
|
1606
|
+
pointers: ImagePointerRef[] | undefined,
|
|
1607
|
+
conversationId: string | null,
|
|
1608
|
+
resolver: ImageResolver | null,
|
|
1609
|
+
log?: { warn?: (tag: string, msg: string) => void } | null,
|
|
1610
|
+
fallbackParentMessageId?: string | null
|
|
1611
|
+
): Promise<string[]> {
|
|
1612
|
+
if (!pointers || pointers.length === 0 || !resolver) return [];
|
|
1613
|
+
const urls: string[] = [];
|
|
1614
|
+
for (const ref of pointers) {
|
|
1615
|
+
try {
|
|
1616
|
+
const url = await resolver(
|
|
1617
|
+
ref.pointer,
|
|
1618
|
+
conversationId,
|
|
1619
|
+
ref.messageId ?? fallbackParentMessageId
|
|
1620
|
+
);
|
|
1621
|
+
if (url) urls.push(url);
|
|
1622
|
+
} catch (err) {
|
|
1623
|
+
log?.warn?.(
|
|
1624
|
+
"CGPT-WEB",
|
|
1625
|
+
`Image resolve failed (${ref.pointer}): ${err instanceof Error ? err.message : String(err)}`
|
|
1626
|
+
);
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
return urls;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
function buildStreamingResponse(
|
|
1633
|
+
eventStream: ReadableStream<Uint8Array>,
|
|
1634
|
+
model: string,
|
|
1635
|
+
cid: string,
|
|
1636
|
+
created: number,
|
|
1637
|
+
resolver: ImageResolver | null,
|
|
1638
|
+
// Optional poller for async image_gen — when ChatGPT processes the request
|
|
1639
|
+
// out-of-band ("Lots of people are creating images right now"), the SSE
|
|
1640
|
+
// stream finishes without an image_asset_pointer. The executor passes a
|
|
1641
|
+
// closure here that knows how to poll the conversation endpoint.
|
|
1642
|
+
pollAsyncImage: ((conversationId: string) => Promise<ImagePointerRef[]>) | null,
|
|
1643
|
+
// Optional poller for GPT-5.5 Pro's stream_handoff path. Inline text keeps
|
|
1644
|
+
// streaming as-is; once ChatGPT hands off, we append the final assistant
|
|
1645
|
+
// answer fetched from the conversation detail endpoint. Text requests stay
|
|
1646
|
+
// in Temporary Chat, so these polls should not create sidebar/history items.
|
|
1647
|
+
pollFinalAnswer: ((conversationId: string) => Promise<FinalAssistantAnswer | null>) | null,
|
|
1648
|
+
log: { warn?: (tag: string, msg: string) => void } | null,
|
|
1649
|
+
signal?: AbortSignal | null
|
|
1650
|
+
): ReadableStream<Uint8Array> {
|
|
1651
|
+
const encoder = new TextEncoder();
|
|
1652
|
+
|
|
1653
|
+
return new ReadableStream(
|
|
1654
|
+
{
|
|
1655
|
+
async start(controller) {
|
|
1656
|
+
try {
|
|
1657
|
+
controller.enqueue(
|
|
1658
|
+
encoder.encode(
|
|
1659
|
+
sseChunk({
|
|
1660
|
+
id: cid,
|
|
1661
|
+
object: "chat.completion.chunk",
|
|
1662
|
+
created,
|
|
1663
|
+
model,
|
|
1664
|
+
system_fingerprint: null,
|
|
1665
|
+
choices: [
|
|
1666
|
+
{ index: 0, delta: { role: "assistant" }, finish_reason: null, logprobs: null },
|
|
1667
|
+
],
|
|
1668
|
+
})
|
|
1669
|
+
)
|
|
1670
|
+
);
|
|
1671
|
+
|
|
1672
|
+
let conversationId: string | null = null;
|
|
1673
|
+
let imagePointers: ImagePointerRef[] | undefined;
|
|
1674
|
+
let imageGenAsync = false;
|
|
1675
|
+
let handoff = false;
|
|
1676
|
+
let emittedText = "";
|
|
1677
|
+
let polledFinalAnswer = "";
|
|
1678
|
+
let parentCandidateMessageId: string | null = null;
|
|
1679
|
+
|
|
1680
|
+
const emitTextDelta = (content: string): void => {
|
|
1681
|
+
const cleaned = cleanChatGptText(content);
|
|
1682
|
+
if (!cleaned) return;
|
|
1683
|
+
emittedText += cleaned;
|
|
1684
|
+
controller.enqueue(
|
|
1685
|
+
encoder.encode(
|
|
1686
|
+
sseChunk({
|
|
1687
|
+
id: cid,
|
|
1688
|
+
object: "chat.completion.chunk",
|
|
1689
|
+
created,
|
|
1690
|
+
model,
|
|
1691
|
+
system_fingerprint: null,
|
|
1692
|
+
choices: [
|
|
1693
|
+
{
|
|
1694
|
+
index: 0,
|
|
1695
|
+
delta: { content: cleaned },
|
|
1696
|
+
finish_reason: null,
|
|
1697
|
+
logprobs: null,
|
|
1698
|
+
},
|
|
1699
|
+
],
|
|
1700
|
+
})
|
|
1701
|
+
)
|
|
1702
|
+
);
|
|
1703
|
+
};
|
|
1704
|
+
|
|
1705
|
+
const appendFinalAnswer = (text: string): void => {
|
|
1706
|
+
const cleaned = cleanChatGptText(text);
|
|
1707
|
+
const finalTrimmed = cleaned.trim();
|
|
1708
|
+
if (!finalTrimmed) return;
|
|
1709
|
+
const emittedTrimmed = emittedText.trim();
|
|
1710
|
+
if (emittedTrimmed === finalTrimmed || emittedTrimmed.endsWith(finalTrimmed)) return;
|
|
1711
|
+
const prefix = emittedTrimmed && !emittedText.endsWith("\n") ? "\n\n" : "";
|
|
1712
|
+
emitTextDelta(`${prefix}${cleaned}`);
|
|
1713
|
+
};
|
|
1714
|
+
|
|
1715
|
+
// Heartbeat: long async work (Pro polling, WebSocket image-gen,
|
|
1716
|
+
// 2-3 MB image fetch) leaves the SSE quiet and Open WebUI times out
|
|
1717
|
+
// at ~30s (`disconnect: ResponseAborted`). SSE comments and empty
|
|
1718
|
+
// `delta:{}` chunks are both filtered upstream
|
|
1719
|
+
// (`hasValuableContent` in open-sse/utils/streamHelpers.ts), so
|
|
1720
|
+
// heartbeats are zero-width-space content deltas (`""`): they pass
|
|
1721
|
+
// the filter and render invisibly.
|
|
1722
|
+
const startHeartbeat = (intervalMs = 5_000): (() => void) => {
|
|
1723
|
+
const heartbeatChunk = sseChunk({
|
|
1724
|
+
id: cid,
|
|
1725
|
+
object: "chat.completion.chunk",
|
|
1726
|
+
created,
|
|
1727
|
+
model,
|
|
1728
|
+
system_fingerprint: null,
|
|
1729
|
+
choices: [{ index: 0, delta: { content: "" }, finish_reason: null, logprobs: null }],
|
|
1730
|
+
});
|
|
1731
|
+
const timer = setInterval(() => {
|
|
1732
|
+
try {
|
|
1733
|
+
controller.enqueue(encoder.encode(heartbeatChunk));
|
|
1734
|
+
} catch {
|
|
1735
|
+
// Controller may already be closed if the client disconnected
|
|
1736
|
+
// — just stop firing.
|
|
1737
|
+
console.warn("[chatgpt-web] heartbeat enqueue failed - controller closed");
|
|
1738
|
+
clearInterval(timer);
|
|
1739
|
+
}
|
|
1740
|
+
}, intervalMs);
|
|
1741
|
+
return () => clearInterval(timer);
|
|
1742
|
+
};
|
|
1743
|
+
|
|
1744
|
+
for await (const chunk of extractContent(eventStream, signal)) {
|
|
1745
|
+
if (chunk.conversationId) conversationId = chunk.conversationId;
|
|
1746
|
+
if (chunk.messageId) parentCandidateMessageId = chunk.messageId;
|
|
1747
|
+
if (chunk.handoff) handoff = true;
|
|
1748
|
+
if (chunk.error) {
|
|
1749
|
+
controller.enqueue(
|
|
1750
|
+
encoder.encode(
|
|
1751
|
+
sseChunk({
|
|
1752
|
+
id: cid,
|
|
1753
|
+
object: "chat.completion.chunk",
|
|
1754
|
+
created,
|
|
1755
|
+
model,
|
|
1756
|
+
system_fingerprint: null,
|
|
1757
|
+
choices: [
|
|
1758
|
+
{
|
|
1759
|
+
index: 0,
|
|
1760
|
+
delta: { content: `[Error: ${chunk.error}]` },
|
|
1761
|
+
finish_reason: null,
|
|
1762
|
+
logprobs: null,
|
|
1763
|
+
},
|
|
1764
|
+
],
|
|
1765
|
+
})
|
|
1766
|
+
)
|
|
1767
|
+
);
|
|
1768
|
+
break;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
if (chunk.done) {
|
|
1772
|
+
imagePointers = chunk.imagePointers;
|
|
1773
|
+
imageGenAsync = chunk.imageGenAsync ?? false;
|
|
1774
|
+
handoff = handoff || (chunk.handoff ?? false);
|
|
1775
|
+
if (chunk.messageId) parentCandidateMessageId = chunk.messageId;
|
|
1776
|
+
break;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
if (chunk.delta) {
|
|
1780
|
+
emitTextDelta(chunk.delta);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
if (pollFinalAnswer && conversationId && handoff) {
|
|
1785
|
+
const stopHb = startHeartbeat();
|
|
1786
|
+
try {
|
|
1787
|
+
const polled = await pollFinalAnswer(conversationId);
|
|
1788
|
+
if (polled?.text) {
|
|
1789
|
+
polledFinalAnswer = polled.text;
|
|
1790
|
+
if (polled.messageId) parentCandidateMessageId = polled.messageId;
|
|
1791
|
+
}
|
|
1792
|
+
} finally {
|
|
1793
|
+
stopHb();
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
if (polledFinalAnswer) {
|
|
1798
|
+
appendFinalAnswer(polledFinalAnswer);
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
// Async image_gen ends the SSE with a "Processing image..."
|
|
1802
|
+
// placeholder; poll the conversation endpoint in the background for
|
|
1803
|
+
// the final pointer (only when in-stream pointers are empty).
|
|
1804
|
+
if (
|
|
1805
|
+
imageGenAsync &&
|
|
1806
|
+
conversationId &&
|
|
1807
|
+
(!imagePointers || imagePointers.length === 0) &&
|
|
1808
|
+
pollAsyncImage
|
|
1809
|
+
) {
|
|
1810
|
+
// Tell the user something is happening — long polls otherwise
|
|
1811
|
+
// look like a hang on the client side. The "..." plus a typing
|
|
1812
|
+
// cue renders nicely in Open WebUI.
|
|
1813
|
+
controller.enqueue(
|
|
1814
|
+
encoder.encode(
|
|
1815
|
+
sseChunk({
|
|
1816
|
+
id: cid,
|
|
1817
|
+
object: "chat.completion.chunk",
|
|
1818
|
+
created,
|
|
1819
|
+
model,
|
|
1820
|
+
system_fingerprint: null,
|
|
1821
|
+
choices: [
|
|
1822
|
+
{
|
|
1823
|
+
index: 0,
|
|
1824
|
+
delta: { content: "_Generating image…_\n\n" },
|
|
1825
|
+
finish_reason: null,
|
|
1826
|
+
logprobs: null,
|
|
1827
|
+
},
|
|
1828
|
+
],
|
|
1829
|
+
})
|
|
1830
|
+
)
|
|
1831
|
+
);
|
|
1832
|
+
const stopHb = startHeartbeat();
|
|
1833
|
+
try {
|
|
1834
|
+
const polled = await pollAsyncImage(conversationId);
|
|
1835
|
+
if (polled.length > 0) imagePointers = polled;
|
|
1836
|
+
} catch (err) {
|
|
1837
|
+
log?.warn?.(
|
|
1838
|
+
"CGPT-WEB",
|
|
1839
|
+
`Async image poll failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1840
|
+
);
|
|
1841
|
+
} finally {
|
|
1842
|
+
stopHb();
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
// Resolve and append any image markdown after the text deltas finish
|
|
1847
|
+
// streaming. Downloading and caching the image bytes can take 1-3
|
|
1848
|
+
// seconds for big images, so keep the heartbeat running here too.
|
|
1849
|
+
const stopHb2 = startHeartbeat();
|
|
1850
|
+
let urls: string[] = [];
|
|
1851
|
+
try {
|
|
1852
|
+
urls = await resolveImagePointers(
|
|
1853
|
+
imagePointers,
|
|
1854
|
+
conversationId,
|
|
1855
|
+
resolver,
|
|
1856
|
+
log,
|
|
1857
|
+
parentCandidateMessageId
|
|
1858
|
+
);
|
|
1859
|
+
} finally {
|
|
1860
|
+
stopHb2();
|
|
1861
|
+
}
|
|
1862
|
+
// Bail out cleanly if the client disconnected during the wait —
|
|
1863
|
+
// any further enqueue throws "Invalid state: Controller is
|
|
1864
|
+
// already closed". Better to no-op than to surface that as a
|
|
1865
|
+
// server error.
|
|
1866
|
+
if (signal?.aborted) return;
|
|
1867
|
+
const mdBlock = imageMarkdown(urls);
|
|
1868
|
+
const safeEnqueue = (bytes: Uint8Array): boolean => {
|
|
1869
|
+
try {
|
|
1870
|
+
controller.enqueue(bytes);
|
|
1871
|
+
return true;
|
|
1872
|
+
} catch {
|
|
1873
|
+
console.warn("[chatgpt-web] controller enqueue failed");
|
|
1874
|
+
return false;
|
|
1875
|
+
}
|
|
1876
|
+
};
|
|
1877
|
+
// The image markdown is now a small URL (we cache the bytes in
|
|
1878
|
+
// memory and serve them at /v1/chatgpt-web/image/<id>), so a
|
|
1879
|
+
// single SSE chunk is fine — no aiohttp LineTooLong concerns
|
|
1880
|
+
// and the markdown renderer in Open WebUI sees the URL whole
|
|
1881
|
+
// and renders an `<img>` immediately.
|
|
1882
|
+
if (mdBlock) {
|
|
1883
|
+
if (
|
|
1884
|
+
!safeEnqueue(
|
|
1885
|
+
encoder.encode(
|
|
1886
|
+
sseChunk({
|
|
1887
|
+
id: cid,
|
|
1888
|
+
object: "chat.completion.chunk",
|
|
1889
|
+
created,
|
|
1890
|
+
model,
|
|
1891
|
+
system_fingerprint: null,
|
|
1892
|
+
choices: [
|
|
1893
|
+
{
|
|
1894
|
+
index: 0,
|
|
1895
|
+
delta: { content: mdBlock },
|
|
1896
|
+
finish_reason: null,
|
|
1897
|
+
logprobs: null,
|
|
1898
|
+
},
|
|
1899
|
+
],
|
|
1900
|
+
})
|
|
1901
|
+
)
|
|
1902
|
+
)
|
|
1903
|
+
)
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
if (
|
|
1908
|
+
!safeEnqueue(
|
|
1909
|
+
encoder.encode(
|
|
1910
|
+
sseChunk({
|
|
1911
|
+
id: cid,
|
|
1912
|
+
object: "chat.completion.chunk",
|
|
1913
|
+
created,
|
|
1914
|
+
model,
|
|
1915
|
+
system_fingerprint: null,
|
|
1916
|
+
choices: [{ index: 0, delta: {}, finish_reason: "stop", logprobs: null }],
|
|
1917
|
+
})
|
|
1918
|
+
)
|
|
1919
|
+
)
|
|
1920
|
+
)
|
|
1921
|
+
return;
|
|
1922
|
+
safeEnqueue(encoder.encode("data: [DONE]\n\n"));
|
|
1923
|
+
} catch (err) {
|
|
1924
|
+
controller.enqueue(
|
|
1925
|
+
encoder.encode(
|
|
1926
|
+
sseChunk({
|
|
1927
|
+
id: cid,
|
|
1928
|
+
object: "chat.completion.chunk",
|
|
1929
|
+
created,
|
|
1930
|
+
model,
|
|
1931
|
+
system_fingerprint: null,
|
|
1932
|
+
choices: [
|
|
1933
|
+
{
|
|
1934
|
+
index: 0,
|
|
1935
|
+
delta: {
|
|
1936
|
+
content: `[Stream error: ${err instanceof Error ? err.message : String(err)}]`,
|
|
1937
|
+
},
|
|
1938
|
+
finish_reason: "stop",
|
|
1939
|
+
logprobs: null,
|
|
1940
|
+
},
|
|
1941
|
+
],
|
|
1942
|
+
})
|
|
1943
|
+
)
|
|
1944
|
+
);
|
|
1945
|
+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
1946
|
+
} finally {
|
|
1947
|
+
try {
|
|
1948
|
+
controller.close();
|
|
1949
|
+
} catch {}
|
|
1950
|
+
}
|
|
1951
|
+
},
|
|
1952
|
+
},
|
|
1953
|
+
{ highWaterMark: 16384 }
|
|
1954
|
+
);
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
async function buildNonStreamingResponse(
|
|
1958
|
+
eventStream: ReadableStream<Uint8Array>,
|
|
1959
|
+
model: string,
|
|
1960
|
+
cid: string,
|
|
1961
|
+
created: number,
|
|
1962
|
+
currentMsg: string,
|
|
1963
|
+
resolver: ImageResolver | null,
|
|
1964
|
+
pollAsyncImage: ((conversationId: string) => Promise<ImagePointerRef[]>) | null,
|
|
1965
|
+
pollFinalAnswer: ((conversationId: string) => Promise<FinalAssistantAnswer | null>) | null,
|
|
1966
|
+
log: { warn?: (tag: string, msg: string) => void } | null,
|
|
1967
|
+
signal?: AbortSignal | null
|
|
1968
|
+
): Promise<Response> {
|
|
1969
|
+
let fullAnswer = "";
|
|
1970
|
+
let conversationId: string | null = null;
|
|
1971
|
+
let imagePointers: ImagePointerRef[] | undefined;
|
|
1972
|
+
let imageGenAsync = false;
|
|
1973
|
+
let handoff = false;
|
|
1974
|
+
let parentCandidateMessageId: string | null = null;
|
|
1975
|
+
|
|
1976
|
+
for await (const chunk of extractContent(eventStream, signal)) {
|
|
1977
|
+
if (chunk.conversationId) conversationId = chunk.conversationId;
|
|
1978
|
+
if (chunk.messageId) parentCandidateMessageId = chunk.messageId;
|
|
1979
|
+
if (chunk.handoff) handoff = true;
|
|
1980
|
+
if (chunk.error) {
|
|
1981
|
+
return new Response(
|
|
1982
|
+
JSON.stringify({
|
|
1983
|
+
error: { message: chunk.error, type: "upstream_error", code: "CHATGPT_ERROR" },
|
|
1984
|
+
}),
|
|
1985
|
+
{ status: 502, headers: { "Content-Type": "application/json" } }
|
|
1986
|
+
);
|
|
1987
|
+
}
|
|
1988
|
+
if (chunk.done) {
|
|
1989
|
+
fullAnswer = chunk.answer || fullAnswer;
|
|
1990
|
+
imagePointers = chunk.imagePointers;
|
|
1991
|
+
imageGenAsync = chunk.imageGenAsync ?? false;
|
|
1992
|
+
handoff = handoff || (chunk.handoff ?? false);
|
|
1993
|
+
if (chunk.messageId) parentCandidateMessageId = chunk.messageId;
|
|
1994
|
+
break;
|
|
1995
|
+
}
|
|
1996
|
+
if (chunk.answer) fullAnswer = chunk.answer;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
if (pollFinalAnswer && conversationId && (handoff || !fullAnswer.trim())) {
|
|
2000
|
+
const polled = await pollFinalAnswer(conversationId);
|
|
2001
|
+
if (polled?.text) {
|
|
2002
|
+
fullAnswer = polled.text;
|
|
2003
|
+
if (polled.messageId) parentCandidateMessageId = polled.messageId;
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
fullAnswer = cleanChatGptText(fullAnswer);
|
|
2008
|
+
|
|
2009
|
+
// Async image gen: SSE ended with "Processing image..." — poll for the
|
|
2010
|
+
// final pointer the same way the streaming path does.
|
|
2011
|
+
if (
|
|
2012
|
+
imageGenAsync &&
|
|
2013
|
+
conversationId &&
|
|
2014
|
+
(!imagePointers || imagePointers.length === 0) &&
|
|
2015
|
+
pollAsyncImage
|
|
2016
|
+
) {
|
|
2017
|
+
try {
|
|
2018
|
+
const polled = await pollAsyncImage(conversationId);
|
|
2019
|
+
if (polled.length > 0) imagePointers = polled;
|
|
2020
|
+
} catch (err) {
|
|
2021
|
+
log?.warn?.(
|
|
2022
|
+
"CGPT-WEB",
|
|
2023
|
+
`Async image poll failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
const urls = await resolveImagePointers(
|
|
2029
|
+
imagePointers,
|
|
2030
|
+
conversationId,
|
|
2031
|
+
resolver,
|
|
2032
|
+
log,
|
|
2033
|
+
parentCandidateMessageId
|
|
2034
|
+
);
|
|
2035
|
+
// The image genuinely exists upstream but no pointer resolved to a URL
|
|
2036
|
+
// (unknown asset scheme, download 403/expired, oversize). Flag it so the
|
|
2037
|
+
// image-generation handler can report an accurate "generated but not
|
|
2038
|
+
// retrievable" error instead of the misleading "no image markdown" 502.
|
|
2039
|
+
const imageResolutionFailed = detectImageResolutionFailure(
|
|
2040
|
+
imagePointers?.length ?? 0,
|
|
2041
|
+
urls.length
|
|
2042
|
+
);
|
|
2043
|
+
if (imageResolutionFailed && log?.warn) {
|
|
2044
|
+
const schemes = (imagePointers ?? [])
|
|
2045
|
+
.map((p) => p.pointer.split("://")[0] || p.pointer.slice(0, 24))
|
|
2046
|
+
.join(", ");
|
|
2047
|
+
log.warn(
|
|
2048
|
+
"CGPT-WEB",
|
|
2049
|
+
`Image generated upstream but no asset pointer resolved (schemes: ${schemes}) — surfacing as unretrievable`
|
|
2050
|
+
);
|
|
2051
|
+
}
|
|
2052
|
+
fullAnswer += imageMarkdown(urls);
|
|
2053
|
+
const promptTokens = Math.ceil(currentMsg.length / 4);
|
|
2054
|
+
const completionTokens = Math.ceil(fullAnswer.length / 4);
|
|
2055
|
+
|
|
2056
|
+
return new Response(
|
|
2057
|
+
JSON.stringify({
|
|
2058
|
+
id: cid,
|
|
2059
|
+
object: "chat.completion",
|
|
2060
|
+
created,
|
|
2061
|
+
model,
|
|
2062
|
+
system_fingerprint: null,
|
|
2063
|
+
...(imageResolutionFailed ? { x_image_resolution_failed: true } : {}),
|
|
2064
|
+
choices: [
|
|
2065
|
+
{
|
|
2066
|
+
index: 0,
|
|
2067
|
+
message: { role: "assistant", content: fullAnswer },
|
|
2068
|
+
finish_reason: "stop",
|
|
2069
|
+
logprobs: null,
|
|
2070
|
+
},
|
|
2071
|
+
],
|
|
2072
|
+
usage: {
|
|
2073
|
+
prompt_tokens: promptTokens,
|
|
2074
|
+
completion_tokens: completionTokens,
|
|
2075
|
+
total_tokens: promptTokens + completionTokens,
|
|
2076
|
+
},
|
|
2077
|
+
}),
|
|
2078
|
+
{ status: 200, headers: { "Content-Type": "application/json" } }
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
// ─── Error response helpers ─────────────────────────────────────────────────
|
|
2083
|
+
|
|
2084
|
+
function errorResponse(status: number, message: string, code?: string): Response {
|
|
2085
|
+
return new Response(
|
|
2086
|
+
JSON.stringify({ error: { message, type: "upstream_error", ...(code ? { code } : {}) } }),
|
|
2087
|
+
{ status, headers: { "Content-Type": "application/json" } }
|
|
2088
|
+
);
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
function normalizePublicBaseUrl(value?: string | null): string | null {
|
|
2092
|
+
const trimmed = value?.trim();
|
|
2093
|
+
if (!trimmed) return null;
|
|
2094
|
+
return trimmed.replace(/\/+$/, "").replace(/\/v1$/i, "");
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
function firstForwardedValue(value?: string | null): string | null {
|
|
2098
|
+
const first = value?.split(",")[0]?.trim();
|
|
2099
|
+
return first || null;
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
function isLocalBaseUrl(baseUrl: string): boolean {
|
|
2103
|
+
try {
|
|
2104
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
2105
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "0.0.0.0";
|
|
2106
|
+
} catch {
|
|
2107
|
+
console.warn("[chatgpt-web] URL parse failed, falling back to regex");
|
|
2108
|
+
return /\b(?:localhost|127\.0\.0\.1|0\.0\.0\.0)\b/i.test(baseUrl);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
function deriveHeaderBaseUrl(clientHeaders?: Record<string, string> | null): string | null {
|
|
2113
|
+
const headers = clientHeaders ?? {};
|
|
2114
|
+
const lower: Record<string, string> = {};
|
|
2115
|
+
for (const [k, v] of Object.entries(headers)) lower[k.toLowerCase()] = v;
|
|
2116
|
+
|
|
2117
|
+
const forwardedHost = firstForwardedValue(lower["x-forwarded-host"]);
|
|
2118
|
+
const forwardedProto = firstForwardedValue(lower["x-forwarded-proto"]);
|
|
2119
|
+
const host = forwardedHost || firstForwardedValue(lower["host"]);
|
|
2120
|
+
if (!host) return null;
|
|
2121
|
+
|
|
2122
|
+
// Default to http for IPs, localhost, and explicit host:port values where
|
|
2123
|
+
// TLS is not a safe assumption. Reverse proxies can override via
|
|
2124
|
+
// x-forwarded-proto, and deployments can force the exact value with
|
|
2125
|
+
// OMNIROUTE_PUBLIC_BASE_URL.
|
|
2126
|
+
const isPlain =
|
|
2127
|
+
host.includes("localhost") ||
|
|
2128
|
+
/^\d+\.\d+\.\d+\.\d+(:\d+)?$/.test(host) ||
|
|
2129
|
+
host.endsWith(".local") ||
|
|
2130
|
+
host.includes(":");
|
|
2131
|
+
const proto = forwardedProto || (isPlain ? "http" : "https");
|
|
2132
|
+
return `${proto}://${host}`;
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
/**
|
|
2136
|
+
* Build the absolute base URL the client should use to fetch our cached
|
|
2137
|
+
* images at /v1/chatgpt-web/image/<id>. The most reliable value is an
|
|
2138
|
+
* explicit browser-facing origin because relay clients such as Open WebUI
|
|
2139
|
+
* often reach OmniRoute from a container while the user's browser needs a
|
|
2140
|
+
* LAN, tunnel, or reverse-proxy URL.
|
|
2141
|
+
*/
|
|
2142
|
+
function derivePublicBaseUrl(
|
|
2143
|
+
clientHeaders?: Record<string, string> | null,
|
|
2144
|
+
log?: { debug?: (tag: string, msg: string) => void }
|
|
2145
|
+
): string {
|
|
2146
|
+
const explicitPublicBase = normalizePublicBaseUrl(process.env.OMNIROUTE_PUBLIC_BASE_URL);
|
|
2147
|
+
if (explicitPublicBase) {
|
|
2148
|
+
log?.debug?.("CGPT-WEB", `derivePublicBaseUrl: using OMNIROUTE_PUBLIC_BASE_URL`);
|
|
2149
|
+
return explicitPublicBase;
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
const headerBase = deriveHeaderBaseUrl(clientHeaders);
|
|
2153
|
+
const configuredBase =
|
|
2154
|
+
normalizePublicBaseUrl(process.env.OMNIROUTE_BASE_URL) ||
|
|
2155
|
+
normalizePublicBaseUrl(process.env.NEXT_PUBLIC_BASE_URL);
|
|
2156
|
+
|
|
2157
|
+
log?.debug?.(
|
|
2158
|
+
"CGPT-WEB",
|
|
2159
|
+
`derivePublicBaseUrl: configured=${configuredBase ?? "-"} header=${headerBase ?? "-"}`
|
|
2160
|
+
);
|
|
2161
|
+
|
|
2162
|
+
if (configuredBase && (!headerBase || !isLocalBaseUrl(configuredBase))) return configuredBase;
|
|
2163
|
+
if (headerBase) return headerBase;
|
|
2164
|
+
if (configuredBase) return configuredBase;
|
|
2165
|
+
|
|
2166
|
+
return `http://localhost:${process.env.PORT || 20128}`;
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
// ─── Image asset resolution ────────────────────────────────────────────────
|
|
2170
|
+
// ChatGPT's image_gen tool emits `image_asset_pointer` parts whose
|
|
2171
|
+
// `asset_pointer` is one of:
|
|
2172
|
+
//
|
|
2173
|
+
// file-service://file-XXXX → resolved via /backend-api/files/{id}/download
|
|
2174
|
+
// sediment://file-XXXX → resolved via /backend-api/conversation/{conv_id}/attachment/{id}/download
|
|
2175
|
+
//
|
|
2176
|
+
// Both endpoints return JSON `{ download_url: "<azure-blob-sas-url>", ... }`.
|
|
2177
|
+
// The signed URL has a limited lifetime (typically a few hours), but that's
|
|
2178
|
+
// usually sufficient for the user to view the image in their UI right after
|
|
2179
|
+
// generation. Persistent storage can be layered on later if needed.
|
|
2180
|
+
|
|
2181
|
+
const FILE_SERVICE_PREFIX = "file-service://";
|
|
2182
|
+
const SEDIMENT_PREFIX = "sediment://";
|
|
2183
|
+
|
|
2184
|
+
interface ResolverContext {
|
|
2185
|
+
accessToken: string;
|
|
2186
|
+
accountId: string | null;
|
|
2187
|
+
sessionId: string;
|
|
2188
|
+
deviceId: string;
|
|
2189
|
+
cookie: string;
|
|
2190
|
+
signal?: AbortSignal | null;
|
|
2191
|
+
log?: { debug?: (tag: string, msg: string) => void; warn?: (tag: string, msg: string) => void };
|
|
2192
|
+
/**
|
|
2193
|
+
* Absolute base URL that downstream clients should use to fetch cached
|
|
2194
|
+
* images served by /v1/chatgpt-web/image/<id>. Derived from the inbound
|
|
2195
|
+
* request host so the URL is reachable from whatever network the client
|
|
2196
|
+
* came in on (localhost, Tailscale, cloudflared tunnel, etc.).
|
|
2197
|
+
*/
|
|
2198
|
+
publicBaseUrl: string;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
async function fetchDownloadUrl(endpoint: string, ctx: ResolverContext): Promise<string | null> {
|
|
2202
|
+
const headers: Record<string, string> = {
|
|
2203
|
+
...browserHeaders(),
|
|
2204
|
+
...oaiHeaders(ctx.sessionId, ctx.deviceId),
|
|
2205
|
+
Accept: "application/json",
|
|
2206
|
+
Authorization: `Bearer ${ctx.accessToken}`,
|
|
2207
|
+
Cookie: buildSessionCookieHeader(ctx.cookie),
|
|
2208
|
+
};
|
|
2209
|
+
if (ctx.accountId) headers["chatgpt-account-id"] = ctx.accountId;
|
|
2210
|
+
|
|
2211
|
+
const response = await tlsFetchChatGpt(endpoint, {
|
|
2212
|
+
method: "GET",
|
|
2213
|
+
headers,
|
|
2214
|
+
timeoutMs: 30_000,
|
|
2215
|
+
signal: ctx.signal,
|
|
2216
|
+
});
|
|
2217
|
+
if (response.status !== 200) {
|
|
2218
|
+
ctx.log?.warn?.(
|
|
2219
|
+
"CGPT-WEB",
|
|
2220
|
+
`Image download URL fetch failed (${response.status}) for ${endpoint}`
|
|
2221
|
+
);
|
|
2222
|
+
return null;
|
|
2223
|
+
}
|
|
2224
|
+
let parsed: { download_url?: string } = {};
|
|
2225
|
+
try {
|
|
2226
|
+
parsed = JSON.parse(response.text || "{}");
|
|
2227
|
+
} catch {
|
|
2228
|
+
console.warn("[chatgpt-web] image download URL parse failed");
|
|
2229
|
+
return null;
|
|
2230
|
+
}
|
|
2231
|
+
return parsed.download_url ?? null;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
/**
|
|
2235
|
+
* Download a chatgpt.com signed image URL and re-serve it from OmniRoute's
|
|
2236
|
+
* short-lived image cache. The URLs returned by /files/<id>/download and
|
|
2237
|
+
* /conversation/<cid>/attachment/<fid>/download point at chatgpt.com's
|
|
2238
|
+
* estuary endpoint, which 403s for any request without the user's session
|
|
2239
|
+
* cookie. Downstream clients (Open WebUI, OpenAI-compatible apps) won't
|
|
2240
|
+
* have those cookies, so we download once via the authenticated TLS client
|
|
2241
|
+
* and return a browser-fetchable OmniRoute URL.
|
|
2242
|
+
*/
|
|
2243
|
+
const IMAGE_DOWNLOAD_MAX_BYTES = 8 * 1024 * 1024;
|
|
2244
|
+
|
|
2245
|
+
async function imageUrlToCachedImageUrl(
|
|
2246
|
+
signedUrl: string,
|
|
2247
|
+
ctx: ResolverContext,
|
|
2248
|
+
imageContext?: ChatGptImageConversationContext
|
|
2249
|
+
): Promise<string | null> {
|
|
2250
|
+
const headers: Record<string, string> = {
|
|
2251
|
+
...browserHeaders(),
|
|
2252
|
+
Accept: "image/*,*/*;q=0.8",
|
|
2253
|
+
Authorization: `Bearer ${ctx.accessToken}`,
|
|
2254
|
+
Cookie: buildSessionCookieHeader(ctx.cookie),
|
|
2255
|
+
};
|
|
2256
|
+
if (ctx.accountId) headers["chatgpt-account-id"] = ctx.accountId;
|
|
2257
|
+
|
|
2258
|
+
let response: TlsFetchResult;
|
|
2259
|
+
try {
|
|
2260
|
+
response = await tlsFetchChatGpt(signedUrl, {
|
|
2261
|
+
method: "GET",
|
|
2262
|
+
headers,
|
|
2263
|
+
timeoutMs: 60_000,
|
|
2264
|
+
signal: ctx.signal,
|
|
2265
|
+
// Required for binary payloads — the underlying tls-client returns
|
|
2266
|
+
// bytes as a `data:<mime>;base64,...` string when this is true.
|
|
2267
|
+
// Without it, raw image bytes get mangled by UTF-8 decoding.
|
|
2268
|
+
byteResponse: true,
|
|
2269
|
+
});
|
|
2270
|
+
} catch (err) {
|
|
2271
|
+
ctx.log?.warn?.(
|
|
2272
|
+
"CGPT-WEB",
|
|
2273
|
+
`Image fetch failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2274
|
+
);
|
|
2275
|
+
return null;
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
if (response.status !== 200) {
|
|
2279
|
+
ctx.log?.warn?.(
|
|
2280
|
+
"CGPT-WEB",
|
|
2281
|
+
`Image fetch returned HTTP ${response.status} (${(response.text || "").slice(0, 120)})`
|
|
2282
|
+
);
|
|
2283
|
+
return null;
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
if (response.text == null || response.text.length === 0) return null;
|
|
2287
|
+
|
|
2288
|
+
// tls-client-node already returns binary bodies as a "data:<mime>;base64,..."
|
|
2289
|
+
// string (see node_modules/tls-client-node/dist/response.js — its bytes()
|
|
2290
|
+
// method splits on the comma to extract base64). Decode back into bytes
|
|
2291
|
+
// so we can hand them to the cache.
|
|
2292
|
+
let bytes: Buffer;
|
|
2293
|
+
let mime: string;
|
|
2294
|
+
if (/^data:[^;]{1,256};base64,/.test(response.text)) {
|
|
2295
|
+
const commaIdx = response.text.indexOf(",");
|
|
2296
|
+
const header = response.text.slice(5, commaIdx); // strip "data:"
|
|
2297
|
+
mime = header.split(";")[0] || "image/png";
|
|
2298
|
+
bytes = Buffer.from(response.text.slice(commaIdx + 1), "base64");
|
|
2299
|
+
} else {
|
|
2300
|
+
// Plain-text body (shouldn't happen for binary downloads with
|
|
2301
|
+
// byteResponse:true, but handle defensively).
|
|
2302
|
+
bytes = Buffer.from(response.text, "binary");
|
|
2303
|
+
mime = response.headers.get("content-type")?.split(";")[0]?.trim() || "image/png";
|
|
2304
|
+
}
|
|
2305
|
+
if (bytes.length === 0 || bytes.length > IMAGE_DOWNLOAD_MAX_BYTES) {
|
|
2306
|
+
if (bytes.length > IMAGE_DOWNLOAD_MAX_BYTES) {
|
|
2307
|
+
ctx.log?.warn?.(
|
|
2308
|
+
"CGPT-WEB",
|
|
2309
|
+
`Image too large to cache (${bytes.length} bytes > ${IMAGE_DOWNLOAD_MAX_BYTES}); skipping`
|
|
2310
|
+
);
|
|
2311
|
+
}
|
|
2312
|
+
return null;
|
|
2313
|
+
}
|
|
2314
|
+
// Cache the image and return a stable HTTP URL pointing at our own
|
|
2315
|
+
// /v1/chatgpt-web/image/<id> route. Streaming the raw base64 back via
|
|
2316
|
+
// SSE deltas works but Open WebUI's progressive markdown renderer shows
|
|
2317
|
+
// each chunk as plain text mid-stream — the user sees megabytes of
|
|
2318
|
+
// base64 scroll past before the image renders. URL-based delivery
|
|
2319
|
+
// produces a small markdown delta and renders instantly when the
|
|
2320
|
+
// browser fetches the URL.
|
|
2321
|
+
const id = storeChatGptImage(bytes, mime, undefined, imageContext);
|
|
2322
|
+
return `${ctx.publicBaseUrl}/v1/chatgpt-web/image/${id}`;
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
/**
|
|
2326
|
+
* Resolve the async image_gen result by registering a WebSocket with
|
|
2327
|
+
* chatgpt.com and listening for the image_asset_pointer.
|
|
2328
|
+
*
|
|
2329
|
+
* Background: when chatgpt.com is busy ("Lots of people are creating images
|
|
2330
|
+
* right now") the image_gen tool defers — the initial SSE finishes with a
|
|
2331
|
+
* "Processing image..." placeholder and the real image arrives over a
|
|
2332
|
+
* WebSocket pubsub. (We checked: the conversation tree at
|
|
2333
|
+
* `/backend-api/conversation/{id}` is NOT updated when the image lands, so
|
|
2334
|
+
* polling that endpoint does nothing.)
|
|
2335
|
+
*
|
|
2336
|
+
* Flow:
|
|
2337
|
+
* 1. POST /backend-api/register-websocket → { wss_url, expires_at, ... }
|
|
2338
|
+
* 2. Open the wss_url with the standard WebSocket client.
|
|
2339
|
+
* Auth lives in the URL (signed access token), so we don't need the
|
|
2340
|
+
* TLS-impersonation transport here.
|
|
2341
|
+
* 3. Each WS message is JSON like { type: "wss-message", data: { ...
|
|
2342
|
+
* conversation event ... } }. The conversation event has the same
|
|
2343
|
+
* shape as the SSE events from /backend-api/f/conversation.
|
|
2344
|
+
* 4. Watch for assistant messages with multimodal_text + image_asset_pointer
|
|
2345
|
+
* OR a `message_stream_complete` for the conversation. Resolve when
|
|
2346
|
+
* either pointer arrives or the timeout fires.
|
|
2347
|
+
*/
|
|
2348
|
+
async function registerWebSocket(ctx: ResolverContext): Promise<string | null> {
|
|
2349
|
+
// chatgpt.com migrated from POST /backend-api/register-websocket to a
|
|
2350
|
+
// GET-only endpoint under /backend-api/celsius/ws/user. The response shape
|
|
2351
|
+
// also changed from `{ wss_url }` → `{ websocket_url }`. Newer codebases
|
|
2352
|
+
// (g4f, etc.) all hit the celsius path; the legacy path now 404s.
|
|
2353
|
+
// Keep the legacy path as a fallback for older deployments.
|
|
2354
|
+
const candidates = [
|
|
2355
|
+
{ url: `${CHATGPT_BASE}/backend-api/celsius/ws/user`, method: "GET" as const },
|
|
2356
|
+
{ url: `${CHATGPT_BASE}/backend-api/register-websocket`, method: "POST" as const },
|
|
2357
|
+
];
|
|
2358
|
+
const headers: Record<string, string> = {
|
|
2359
|
+
...browserHeaders(),
|
|
2360
|
+
...oaiHeaders(ctx.sessionId, ctx.deviceId),
|
|
2361
|
+
Accept: "application/json",
|
|
2362
|
+
Authorization: `Bearer ${ctx.accessToken}`,
|
|
2363
|
+
Cookie: buildSessionCookieHeader(ctx.cookie),
|
|
2364
|
+
};
|
|
2365
|
+
if (ctx.accountId) headers["chatgpt-account-id"] = ctx.accountId;
|
|
2366
|
+
|
|
2367
|
+
for (const { url, method } of candidates) {
|
|
2368
|
+
let r: TlsFetchResult;
|
|
2369
|
+
try {
|
|
2370
|
+
r = await tlsFetchChatGpt(url, {
|
|
2371
|
+
method,
|
|
2372
|
+
headers,
|
|
2373
|
+
body: method === "POST" ? "" : undefined,
|
|
2374
|
+
timeoutMs: 30_000,
|
|
2375
|
+
signal: ctx.signal,
|
|
2376
|
+
});
|
|
2377
|
+
} catch (err) {
|
|
2378
|
+
ctx.log?.warn?.(
|
|
2379
|
+
"CGPT-WEB",
|
|
2380
|
+
`register-websocket fetch failed for ${url}: ${err instanceof Error ? err.message : String(err)}`
|
|
2381
|
+
);
|
|
2382
|
+
continue;
|
|
2383
|
+
}
|
|
2384
|
+
if (r.status === 200) {
|
|
2385
|
+
try {
|
|
2386
|
+
const data = JSON.parse(r.text || "{}") as {
|
|
2387
|
+
websocket_url?: string;
|
|
2388
|
+
wss_url?: string;
|
|
2389
|
+
};
|
|
2390
|
+
const ws = data.websocket_url ?? data.wss_url;
|
|
2391
|
+
if (ws) {
|
|
2392
|
+
ctx.log?.debug?.("CGPT-WEB", `Got WebSocket URL via ${url}`);
|
|
2393
|
+
return ws;
|
|
2394
|
+
}
|
|
2395
|
+
} catch {
|
|
2396
|
+
console.warn("[chatgpt-web] WebSocket URL parse failed, falling through");
|
|
2397
|
+
/* fall through */
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
ctx.log?.warn?.(
|
|
2401
|
+
"CGPT-WEB",
|
|
2402
|
+
`register-websocket via ${url} → ${r.status}: ${(r.text || "").slice(0, 200)}`
|
|
2403
|
+
);
|
|
2404
|
+
}
|
|
2405
|
+
return null;
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
interface WsWaitOutcome {
|
|
2409
|
+
pointers: ImagePointerRef[];
|
|
2410
|
+
/** True if the connection emitted an error event. Used by the retry layer
|
|
2411
|
+
* to decide whether a transport blip is worth a second attempt. */
|
|
2412
|
+
errored: boolean;
|
|
2413
|
+
/** True if any frame (message or open) was actually received from the
|
|
2414
|
+
* server. A retry is most valuable when the connection died before
|
|
2415
|
+
* exchanging any data. */
|
|
2416
|
+
gotAnyMessage: boolean;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
async function waitForImageViaWebSocket(
|
|
2420
|
+
wssUrl: string,
|
|
2421
|
+
conversationId: string,
|
|
2422
|
+
timeoutMs: number,
|
|
2423
|
+
ctx: ResolverContext
|
|
2424
|
+
): Promise<WsWaitOutcome> {
|
|
2425
|
+
return new Promise((resolve) => {
|
|
2426
|
+
const found = new Map<string, ImagePointerRef>();
|
|
2427
|
+
let resolved = false;
|
|
2428
|
+
let errored = false;
|
|
2429
|
+
let gotAnyMessage = false;
|
|
2430
|
+
const finish = () => {
|
|
2431
|
+
if (resolved) return;
|
|
2432
|
+
resolved = true;
|
|
2433
|
+
try {
|
|
2434
|
+
ws.close();
|
|
2435
|
+
} catch {
|
|
2436
|
+
console.warn("[chatgpt-web] ws.close failed");
|
|
2437
|
+
/* ignore */
|
|
2438
|
+
}
|
|
2439
|
+
resolve({
|
|
2440
|
+
pointers: Array.from(found.values()),
|
|
2441
|
+
errored,
|
|
2442
|
+
gotAnyMessage,
|
|
2443
|
+
});
|
|
2444
|
+
};
|
|
2445
|
+
const ws = new WebSocket(wssUrl);
|
|
2446
|
+
const timer = setTimeout(() => {
|
|
2447
|
+
ctx.log?.warn?.("CGPT-WEB", `WebSocket image wait timed out after ${timeoutMs}ms`);
|
|
2448
|
+
finish();
|
|
2449
|
+
}, timeoutMs);
|
|
2450
|
+
const onAbort = () => {
|
|
2451
|
+
ctx.log?.debug?.("CGPT-WEB", "WebSocket aborted by client");
|
|
2452
|
+
finish();
|
|
2453
|
+
};
|
|
2454
|
+
ctx.signal?.addEventListener?.("abort", onAbort);
|
|
2455
|
+
ws.onopen = () => {
|
|
2456
|
+
gotAnyMessage = true;
|
|
2457
|
+
ctx.log?.debug?.("CGPT-WEB", "WebSocket open — waiting for image events");
|
|
2458
|
+
};
|
|
2459
|
+
ws.onerror = (e) => {
|
|
2460
|
+
errored = true;
|
|
2461
|
+
ctx.log?.warn?.("CGPT-WEB", `WebSocket error: ${(e as ErrorEvent).message ?? "unknown"}`);
|
|
2462
|
+
};
|
|
2463
|
+
ws.onclose = () => {
|
|
2464
|
+
clearTimeout(timer);
|
|
2465
|
+
ctx.signal?.removeEventListener?.("abort", onAbort);
|
|
2466
|
+
finish();
|
|
2467
|
+
};
|
|
2468
|
+
ws.onmessage = (event) => {
|
|
2469
|
+
gotAnyMessage = true;
|
|
2470
|
+
let payload: unknown;
|
|
2471
|
+
const raw = typeof event.data === "string" ? event.data : event.data.toString();
|
|
2472
|
+
try {
|
|
2473
|
+
payload = JSON.parse(raw);
|
|
2474
|
+
} catch {
|
|
2475
|
+
console.warn("[chatgpt-web] WebSocket event JSON parse failed");
|
|
2476
|
+
return;
|
|
2477
|
+
}
|
|
2478
|
+
// chatgpt.com's celsius WS frames look like:
|
|
2479
|
+
// { type: "conversation-update",
|
|
2480
|
+
// payload: { conversation_id: "...",
|
|
2481
|
+
// update_content: { message: { ... }, ... } } }
|
|
2482
|
+
// Older deployments wrapped the conversation event directly as { data }.
|
|
2483
|
+
const obj = payload as Record<string, unknown>;
|
|
2484
|
+
const candidates: ChatGptStreamEvent[] = [];
|
|
2485
|
+
const innerPayload = obj.payload as Record<string, unknown> | undefined;
|
|
2486
|
+
const updateContent = innerPayload?.update_content as Record<string, unknown> | undefined;
|
|
2487
|
+
if (updateContent?.message) {
|
|
2488
|
+
candidates.push({
|
|
2489
|
+
message: updateContent.message as ChatGptStreamEvent["message"],
|
|
2490
|
+
conversation_id: innerPayload?.conversation_id as string | undefined,
|
|
2491
|
+
});
|
|
2492
|
+
}
|
|
2493
|
+
if (innerPayload?.message) {
|
|
2494
|
+
candidates.push({
|
|
2495
|
+
message: innerPayload.message as ChatGptStreamEvent["message"],
|
|
2496
|
+
conversation_id: innerPayload.conversation_id as string | undefined,
|
|
2497
|
+
});
|
|
2498
|
+
}
|
|
2499
|
+
if ((obj.data as { message?: unknown } | undefined)?.message) {
|
|
2500
|
+
candidates.push(obj.data as ChatGptStreamEvent);
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
for (const data of candidates) {
|
|
2504
|
+
if (data?.conversation_id && data.conversation_id !== conversationId) continue;
|
|
2505
|
+
const m = data?.message;
|
|
2506
|
+
// The async image_gen result arrives as a TOOL-role message
|
|
2507
|
+
// ({"author":{"role":"tool","name":"t2uay3k.sj1i4kz"}}), so we
|
|
2508
|
+
// accept tool messages here too — extractImagePointers does the
|
|
2509
|
+
// actual content_type filtering.
|
|
2510
|
+
if (Array.isArray(m?.content?.parts)) {
|
|
2511
|
+
for (const ptr of extractImagePointers(m.content?.parts ?? [])) {
|
|
2512
|
+
const existing = found.get(ptr);
|
|
2513
|
+
found.set(
|
|
2514
|
+
ptr,
|
|
2515
|
+
existing?.messageId
|
|
2516
|
+
? existing
|
|
2517
|
+
: { pointer: ptr, ...(m?.id ? { messageId: m.id } : {}) }
|
|
2518
|
+
);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
if (m?.metadata && typeof m.metadata === "object") {
|
|
2522
|
+
const md = m.metadata as Record<string, unknown>;
|
|
2523
|
+
const ptr = (md.asset_pointer ?? md.image_asset_pointer) as string | undefined;
|
|
2524
|
+
if (typeof ptr === "string") {
|
|
2525
|
+
const existing = found.get(ptr);
|
|
2526
|
+
found.set(
|
|
2527
|
+
ptr,
|
|
2528
|
+
existing?.messageId
|
|
2529
|
+
? existing
|
|
2530
|
+
: { pointer: ptr, ...(m?.id ? { messageId: m.id } : {}) }
|
|
2531
|
+
);
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
if (found.size > 0) finish();
|
|
2536
|
+
};
|
|
2537
|
+
});
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
// Default 3-minute wait for the async image_gen tool to produce an image
|
|
2541
|
+
// pointer over the celsius WebSocket. Tunable so deployments can stretch
|
|
2542
|
+
// during chatgpt.com queue-deep windows ("Lots of people are creating
|
|
2543
|
+
// images right now") without code changes.
|
|
2544
|
+
const DEFAULT_ASYNC_IMAGE_TIMEOUT_MS = 180_000;
|
|
2545
|
+
|
|
2546
|
+
function configuredAsyncImageTimeoutMs(): number {
|
|
2547
|
+
const raw = Number(process.env.OMNIROUTE_CGPT_WEB_IMAGE_TIMEOUT_MS);
|
|
2548
|
+
if (!Number.isFinite(raw) || raw <= 0) return DEFAULT_ASYNC_IMAGE_TIMEOUT_MS;
|
|
2549
|
+
return Math.floor(raw);
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
async function pollForAsyncImage(
|
|
2553
|
+
conversationId: string,
|
|
2554
|
+
ctx: ResolverContext,
|
|
2555
|
+
opts: { timeoutMs?: number } = {}
|
|
2556
|
+
): Promise<ImagePointerRef[]> {
|
|
2557
|
+
const totalTimeoutMs = opts.timeoutMs ?? configuredAsyncImageTimeoutMs();
|
|
2558
|
+
const deadline = Date.now() + totalTimeoutMs;
|
|
2559
|
+
|
|
2560
|
+
// One reconnect attempt on transport error: the WS endpoint is signed and
|
|
2561
|
+
// short-lived, and a network blip during the long wait would otherwise
|
|
2562
|
+
// lose the image entirely. The deadline is shared across attempts so we
|
|
2563
|
+
// never exceed the caller's budget.
|
|
2564
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
2565
|
+
const remaining = deadline - Date.now();
|
|
2566
|
+
if (remaining <= 0) break;
|
|
2567
|
+
const wssUrl = await registerWebSocket(ctx);
|
|
2568
|
+
if (!wssUrl) {
|
|
2569
|
+
ctx.log?.warn?.(
|
|
2570
|
+
"CGPT-WEB",
|
|
2571
|
+
attempt === 0
|
|
2572
|
+
? "Could not register WebSocket — async image gen not retrievable"
|
|
2573
|
+
: `WebSocket re-registration failed on retry attempt ${attempt + 1}`
|
|
2574
|
+
);
|
|
2575
|
+
if (attempt === 0) continue; // try again — registration can be flaky
|
|
2576
|
+
return [];
|
|
2577
|
+
}
|
|
2578
|
+
ctx.log?.debug?.(
|
|
2579
|
+
"CGPT-WEB",
|
|
2580
|
+
`Registered WebSocket for async image (attempt ${attempt + 1}, ${remaining}ms remaining)`
|
|
2581
|
+
);
|
|
2582
|
+
const outcome = await waitForImageViaWebSocket(wssUrl, conversationId, remaining, ctx);
|
|
2583
|
+
if (outcome.pointers.length > 0) return outcome.pointers;
|
|
2584
|
+
if (ctx.signal?.aborted) return [];
|
|
2585
|
+
// Only retry when the connection died before producing anything useful.
|
|
2586
|
+
// A clean close with no pointers (e.g., upstream cancellation) shouldn't
|
|
2587
|
+
// burn a second attempt — the result would be the same.
|
|
2588
|
+
if (!outcome.errored || outcome.gotAnyMessage) return [];
|
|
2589
|
+
ctx.log?.warn?.(
|
|
2590
|
+
"CGPT-WEB",
|
|
2591
|
+
`WebSocket attempt ${attempt + 1} ended in transport error before any frame; retrying`
|
|
2592
|
+
);
|
|
2593
|
+
}
|
|
2594
|
+
return [];
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
function makeImageResolver(ctx: ResolverContext): ImageResolver {
|
|
2598
|
+
// Cache resolutions across the same request — the same pointer can show up
|
|
2599
|
+
// on multiple SSE events (in-progress + finished_successfully). One HTTP
|
|
2600
|
+
// round-trip per unique pointer is enough.
|
|
2601
|
+
const cache = new Map<string, string | null>();
|
|
2602
|
+
|
|
2603
|
+
return async (assetPointer, conversationId, parentMessageId) => {
|
|
2604
|
+
if (cache.has(assetPointer)) return cache.get(assetPointer) ?? null;
|
|
2605
|
+
|
|
2606
|
+
let fileId: string | null = null;
|
|
2607
|
+
if (assetPointer.startsWith(FILE_SERVICE_PREFIX)) {
|
|
2608
|
+
fileId = assetPointer.slice(FILE_SERVICE_PREFIX.length);
|
|
2609
|
+
} else if (assetPointer.startsWith(SEDIMENT_PREFIX)) {
|
|
2610
|
+
fileId = assetPointer.slice(SEDIMENT_PREFIX.length);
|
|
2611
|
+
} else {
|
|
2612
|
+
ctx.log?.warn?.("CGPT-WEB", `Unknown asset_pointer scheme: ${assetPointer}`);
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
let signedUrl: string | null = null;
|
|
2616
|
+
if (fileId) {
|
|
2617
|
+
// Both endpoints return a chatgpt.com estuary URL signed for the
|
|
2618
|
+
// user's current session — that URL 403s without the cookie, so
|
|
2619
|
+
// downstream clients can't fetch it directly. We download once via
|
|
2620
|
+
// the authenticated TLS client and expose the bytes through
|
|
2621
|
+
// OmniRoute's short-lived image cache.
|
|
2622
|
+
//
|
|
2623
|
+
// /files/{id}/download is the historical path. It works for
|
|
2624
|
+
// chat-uploaded files and the older image_gen output format
|
|
2625
|
+
// (`file-XXXX`). Newer image-edit results from continued
|
|
2626
|
+
// conversations land with a `file_00000000XXXX` shape that 422s on
|
|
2627
|
+
// /files/{id}/download — they're conversation-scoped attachments
|
|
2628
|
+
// and only resolve through /conversation/{cid}/attachment/{fid}/
|
|
2629
|
+
// download. We try /files first because it's cheaper and works for
|
|
2630
|
+
// the common case, then fall through.
|
|
2631
|
+
signedUrl = await fetchDownloadUrl(
|
|
2632
|
+
`${CHATGPT_BASE}/backend-api/files/${encodeURIComponent(fileId)}/download`,
|
|
2633
|
+
ctx
|
|
2634
|
+
);
|
|
2635
|
+
if (!signedUrl && conversationId) {
|
|
2636
|
+
signedUrl = await fetchDownloadUrl(
|
|
2637
|
+
`${CHATGPT_BASE}/backend-api/conversation/${encodeURIComponent(conversationId)}/attachment/${encodeURIComponent(fileId)}/download`,
|
|
2638
|
+
ctx
|
|
2639
|
+
);
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
let finalUrl: string | null = null;
|
|
2644
|
+
if (signedUrl) {
|
|
2645
|
+
// chatgpt.com signed URLs require the user's session cookie to fetch,
|
|
2646
|
+
// so we materialize the bytes into our own cache and emit an OmniRoute
|
|
2647
|
+
// URL. If that fails (oversize, network error, etc.) we return null —
|
|
2648
|
+
// never the signed URL — because handing it back would emit broken
|
|
2649
|
+
// markdown that 403s for the client. Better to drop the image silently
|
|
2650
|
+
// than render a broken link.
|
|
2651
|
+
finalUrl = await imageUrlToCachedImageUrl(
|
|
2652
|
+
signedUrl,
|
|
2653
|
+
ctx,
|
|
2654
|
+
conversationId && parentMessageId ? { conversationId, parentMessageId } : undefined
|
|
2655
|
+
);
|
|
2656
|
+
}
|
|
2657
|
+
cache.set(assetPointer, finalUrl);
|
|
2658
|
+
if (finalUrl) {
|
|
2659
|
+
const preview = finalUrl.startsWith("data:")
|
|
2660
|
+
? `data:... (${finalUrl.length} chars)`
|
|
2661
|
+
: finalUrl.slice(0, 80) + "...";
|
|
2662
|
+
ctx.log?.debug?.("CGPT-WEB", `Resolved ${assetPointer} → ${preview}`);
|
|
2663
|
+
}
|
|
2664
|
+
return finalUrl;
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
// ─── Executor ───────────────────────────────────────────────────────────────
|
|
2669
|
+
|
|
2670
|
+
export class ChatGptWebExecutor extends BaseExecutor {
|
|
2671
|
+
constructor() {
|
|
2672
|
+
super("chatgpt-web", { id: "chatgpt-web", baseUrl: CONV_URL });
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
async execute({
|
|
2676
|
+
model,
|
|
2677
|
+
body,
|
|
2678
|
+
stream,
|
|
2679
|
+
credentials,
|
|
2680
|
+
signal,
|
|
2681
|
+
log,
|
|
2682
|
+
onCredentialsRefreshed,
|
|
2683
|
+
clientHeaders,
|
|
2684
|
+
}: ExecuteInput) {
|
|
2685
|
+
const messages = (body as Record<string, unknown> | null)?.messages as
|
|
2686
|
+
Array<Record<string, unknown>> | undefined;
|
|
2687
|
+
if (!messages || !Array.isArray(messages) || messages.length === 0) {
|
|
2688
|
+
return {
|
|
2689
|
+
response: errorResponse(400, "Missing or empty messages array"),
|
|
2690
|
+
url: CONV_URL,
|
|
2691
|
+
headers: {},
|
|
2692
|
+
transformedBody: body,
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
// Tool-call emulation (#5240): inject a `<tool>` contract when `tools` are
|
|
2697
|
+
// present; parsed back on the response side. Mirrors qwen-web/perplexity-web.
|
|
2698
|
+
const { hasTools, requestedTools, effectiveMessages } = prepareToolMessages(
|
|
2699
|
+
(body || {}) as Record<string, unknown>,
|
|
2700
|
+
messages as Array<{ role: string; content: unknown }>
|
|
2701
|
+
);
|
|
2702
|
+
|
|
2703
|
+
if (!credentials.apiKey) {
|
|
2704
|
+
return {
|
|
2705
|
+
response: errorResponse(
|
|
2706
|
+
401,
|
|
2707
|
+
"ChatGPT auth failed — paste your __Secure-next-auth.session-token cookie value."
|
|
2708
|
+
),
|
|
2709
|
+
url: CONV_URL,
|
|
2710
|
+
headers: {},
|
|
2711
|
+
transformedBody: body,
|
|
2712
|
+
};
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
// Pass the user's pasted cookie blob through to exchangeSession; the helper
|
|
2716
|
+
// accepts bare values, unchunked cookies, chunked (.0/.1) cookies, and full
|
|
2717
|
+
// "Cookie: ..." DevTools lines.
|
|
2718
|
+
const cookie = credentials.apiKey;
|
|
2719
|
+
|
|
2720
|
+
// 1. Token exchange
|
|
2721
|
+
let tokenEntry: TokenEntry;
|
|
2722
|
+
try {
|
|
2723
|
+
tokenEntry = await exchangeSession(cookie, signal);
|
|
2724
|
+
} catch (err) {
|
|
2725
|
+
if (err instanceof SessionAuthError) {
|
|
2726
|
+
log?.warn?.("CGPT-WEB", err.message);
|
|
2727
|
+
return {
|
|
2728
|
+
response: errorResponse(
|
|
2729
|
+
401,
|
|
2730
|
+
"ChatGPT auth failed — re-paste your __Secure-next-auth.session-token cookie from chatgpt.com.",
|
|
2731
|
+
"HTTP_401"
|
|
2732
|
+
),
|
|
2733
|
+
url: SESSION_URL,
|
|
2734
|
+
headers: {},
|
|
2735
|
+
transformedBody: body,
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2738
|
+
log?.error?.(
|
|
2739
|
+
"CGPT-WEB",
|
|
2740
|
+
`Session exchange failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2741
|
+
);
|
|
2742
|
+
return {
|
|
2743
|
+
response: errorResponse(
|
|
2744
|
+
502,
|
|
2745
|
+
`ChatGPT session exchange failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2746
|
+
),
|
|
2747
|
+
url: SESSION_URL,
|
|
2748
|
+
headers: {},
|
|
2749
|
+
transformedBody: body,
|
|
2750
|
+
};
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
// Surface any rotated cookie back to the caller so the DB credential is refreshed.
|
|
2754
|
+
if (tokenEntry.refreshedCookie && tokenEntry.refreshedCookie !== cookie) {
|
|
2755
|
+
const updated: ProviderCredentials = { ...credentials, apiKey: tokenEntry.refreshedCookie };
|
|
2756
|
+
try {
|
|
2757
|
+
await onCredentialsRefreshed?.(updated);
|
|
2758
|
+
} catch (err) {
|
|
2759
|
+
log?.warn?.(
|
|
2760
|
+
"CGPT-WEB",
|
|
2761
|
+
`Failed to persist refreshed cookie: ${err instanceof Error ? err.message : String(err)}`
|
|
2762
|
+
);
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
// 2a. Warmup — GET / to scrape DPL + script src so the prekey looks legit.
|
|
2767
|
+
let dplInfo: { dpl: string; scriptSrc: string };
|
|
2768
|
+
try {
|
|
2769
|
+
dplInfo = await fetchDpl(cookie, signal);
|
|
2770
|
+
} catch (err) {
|
|
2771
|
+
log?.warn?.(
|
|
2772
|
+
"CGPT-WEB",
|
|
2773
|
+
`DPL warmup failed (continuing with fallback): ${err instanceof Error ? err.message : String(err)}`
|
|
2774
|
+
);
|
|
2775
|
+
dplInfo = {
|
|
2776
|
+
dpl: `dpl=${OAI_CLIENT_VERSION.replace(/^prod-/, "")}`,
|
|
2777
|
+
scriptSrc: `${CHATGPT_BASE}/_next/static/chunks/webpack-${randomHex(16)}.js`,
|
|
2778
|
+
};
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
// 2a'. Browser-like session warmup. Sentinel scores the session by whether
|
|
2782
|
+
// the client recently hit /me, /conversations, /models — same as a real
|
|
2783
|
+
// browser does on page load. Failures here are non-fatal; the worst case
|
|
2784
|
+
// is Sentinel still escalates to Turnstile.
|
|
2785
|
+
const sessionId = randomUUID();
|
|
2786
|
+
const deviceId = deviceIdFor(cookie);
|
|
2787
|
+
await runSessionWarmup(
|
|
2788
|
+
tokenEntry.accessToken,
|
|
2789
|
+
tokenEntry.accountId,
|
|
2790
|
+
sessionId,
|
|
2791
|
+
deviceId,
|
|
2792
|
+
cookie,
|
|
2793
|
+
signal,
|
|
2794
|
+
log
|
|
2795
|
+
);
|
|
2796
|
+
|
|
2797
|
+
// 2a''. Resolve model + effort and apply thinking-effort preference for
|
|
2798
|
+
// thinking-capable models. Dedicated thinking models mirror the browser's
|
|
2799
|
+
// user-config PATCH; GPT-5.5 Pro sends the effort with the conversation
|
|
2800
|
+
// body because the Pro standard/extended budget is part of that turn.
|
|
2801
|
+
const resolvedModel = resolveChatGptModel(model, body, credentials.providerSpecificData);
|
|
2802
|
+
const modelSlug = resolvedModel.slug;
|
|
2803
|
+
const requestedEffort = resolvedModel.effort;
|
|
2804
|
+
if (requestedEffort && isThinkingCapableModel(model, modelSlug)) {
|
|
2805
|
+
await setUserThinkingEffort(
|
|
2806
|
+
modelSlug,
|
|
2807
|
+
requestedEffort,
|
|
2808
|
+
tokenEntry.accessToken,
|
|
2809
|
+
tokenEntry.accountId,
|
|
2810
|
+
sessionId,
|
|
2811
|
+
deviceId,
|
|
2812
|
+
cookie,
|
|
2813
|
+
signal,
|
|
2814
|
+
log
|
|
2815
|
+
);
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
// 2b. Sentinel chat-requirements
|
|
2819
|
+
let reqs: ChatRequirements;
|
|
2820
|
+
try {
|
|
2821
|
+
reqs = await prepareChatRequirements(
|
|
2822
|
+
tokenEntry.accessToken,
|
|
2823
|
+
tokenEntry.accountId,
|
|
2824
|
+
sessionId,
|
|
2825
|
+
deviceId,
|
|
2826
|
+
cookie,
|
|
2827
|
+
dplInfo,
|
|
2828
|
+
signal,
|
|
2829
|
+
log
|
|
2830
|
+
);
|
|
2831
|
+
} catch (err) {
|
|
2832
|
+
if (err instanceof SentinelBlockedError) {
|
|
2833
|
+
log?.warn?.("CGPT-WEB", err.message);
|
|
2834
|
+
return {
|
|
2835
|
+
response: errorResponse(
|
|
2836
|
+
403,
|
|
2837
|
+
"ChatGPT blocked the request (Sentinel/Turnstile required). Try again later or open chatgpt.com in a browser to refresh state.",
|
|
2838
|
+
"SENTINEL_BLOCKED"
|
|
2839
|
+
),
|
|
2840
|
+
url: SENTINEL_PREPARE_URL,
|
|
2841
|
+
headers: {},
|
|
2842
|
+
transformedBody: body,
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
log?.error?.(
|
|
2846
|
+
"CGPT-WEB",
|
|
2847
|
+
`Sentinel failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2848
|
+
);
|
|
2849
|
+
return {
|
|
2850
|
+
response: errorResponse(
|
|
2851
|
+
502,
|
|
2852
|
+
`ChatGPT sentinel failed: ${err instanceof Error ? err.message : String(err)}`
|
|
2853
|
+
),
|
|
2854
|
+
url: SENTINEL_PREPARE_URL,
|
|
2855
|
+
headers: {},
|
|
2856
|
+
transformedBody: body,
|
|
2857
|
+
};
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
log?.debug?.(
|
|
2861
|
+
"CGPT-WEB",
|
|
2862
|
+
`sentinel: token=${reqs.token ? "y" : "n"} pow=${reqs.proofofwork?.required ? "y" : "n"} turnstile=${reqs.turnstile?.required ? "y" : "n"}`
|
|
2863
|
+
);
|
|
2864
|
+
|
|
2865
|
+
// Optional: if a turnstile token was supplied via providerSpecificData,
|
|
2866
|
+
// pass it through. Otherwise, send the request anyway — sometimes Sentinel
|
|
2867
|
+
// reports turnstile.required even when the conversation endpoint accepts
|
|
2868
|
+
// requests without it.
|
|
2869
|
+
const turnstileToken =
|
|
2870
|
+
typeof credentials.providerSpecificData?.turnstileToken === "string"
|
|
2871
|
+
? credentials.providerSpecificData.turnstileToken
|
|
2872
|
+
: null;
|
|
2873
|
+
|
|
2874
|
+
// 3. Solve PoW (if required) — reuses the same browser-fingerprint config
|
|
2875
|
+
// shape as the prekey, just with the server-provided seed + difficulty.
|
|
2876
|
+
let proofToken: string | null = null;
|
|
2877
|
+
if (reqs.proofofwork?.required && reqs.proofofwork.seed && reqs.proofofwork.difficulty) {
|
|
2878
|
+
const powConfig = buildPrekeyConfig(CHATGPT_USER_AGENT, dplInfo.dpl, dplInfo.scriptSrc);
|
|
2879
|
+
proofToken = await solveProofOfWork(
|
|
2880
|
+
reqs.proofofwork.seed,
|
|
2881
|
+
reqs.proofofwork.difficulty,
|
|
2882
|
+
powConfig,
|
|
2883
|
+
log
|
|
2884
|
+
);
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
// 4. Build conversation request
|
|
2888
|
+
const parsed = parseOpenAIMessages(effectiveMessages);
|
|
2889
|
+
if (!parsed.currentMsg.trim() && parsed.history.length === 0) {
|
|
2890
|
+
return {
|
|
2891
|
+
response: errorResponse(400, "Empty user message"),
|
|
2892
|
+
url: CONV_URL,
|
|
2893
|
+
headers: {},
|
|
2894
|
+
transformedBody: body,
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2898
|
+
// Toggle Temporary Chat off only when ChatGPT needs a durable image
|
|
2899
|
+
// conversation. Text requests, including GPT-5.5 Pro, stay temporary so
|
|
2900
|
+
// they do not show up in the user's chatgpt.com sidebar/history.
|
|
2901
|
+
const imageEdit = looksLikeImageEditRequest(parsed);
|
|
2902
|
+
const continuation = imageEdit ? parsed.latestImageContext : null;
|
|
2903
|
+
const forImageGen = looksLikeImageGenRequest(parsed) || imageEdit;
|
|
2904
|
+
const persistConversation = forImageGen || !!continuation;
|
|
2905
|
+
if (forImageGen) {
|
|
2906
|
+
log?.debug?.(
|
|
2907
|
+
"CGPT-WEB",
|
|
2908
|
+
continuation
|
|
2909
|
+
? "Image edit intent detected — continuing saved image conversation"
|
|
2910
|
+
: "Image-gen intent detected — disabling Temporary Chat for this turn"
|
|
2911
|
+
);
|
|
2912
|
+
} else if (resolvedModel.isPro) {
|
|
2913
|
+
log?.debug?.("CGPT-WEB", "GPT-5.5 Pro text request — keeping Temporary Chat enabled");
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
const parentMessageId = continuation?.parentMessageId ?? randomUUID();
|
|
2917
|
+
const cgptBody = buildConversationBody(parsed, modelSlug, parentMessageId, {
|
|
2918
|
+
persistConversation,
|
|
2919
|
+
thinkingEffort: requestedEffort,
|
|
2920
|
+
continuation,
|
|
2921
|
+
});
|
|
2922
|
+
|
|
2923
|
+
const headers: Record<string, string> = {
|
|
2924
|
+
...browserHeaders(),
|
|
2925
|
+
...oaiHeaders(sessionId, deviceId),
|
|
2926
|
+
"Content-Type": "application/json",
|
|
2927
|
+
Accept: "text/event-stream",
|
|
2928
|
+
Authorization: `Bearer ${tokenEntry.accessToken}`,
|
|
2929
|
+
Cookie: buildSessionCookieHeader(cookie),
|
|
2930
|
+
};
|
|
2931
|
+
if (tokenEntry.accountId) headers["chatgpt-account-id"] = tokenEntry.accountId;
|
|
2932
|
+
if (reqs.token) headers["openai-sentinel-chat-requirements-token"] = reqs.token;
|
|
2933
|
+
if (reqs.prepare_token)
|
|
2934
|
+
headers["openai-sentinel-chat-requirements-prepare-token"] = reqs.prepare_token;
|
|
2935
|
+
if (proofToken) headers["openai-sentinel-proof-token"] = proofToken;
|
|
2936
|
+
if (turnstileToken) headers["openai-sentinel-turnstile-token"] = turnstileToken;
|
|
2937
|
+
|
|
2938
|
+
log?.info?.("CGPT-WEB", `Conversation request → ${modelSlug} (pow=${!!proofToken})`);
|
|
2939
|
+
|
|
2940
|
+
let response: TlsFetchResult;
|
|
2941
|
+
try {
|
|
2942
|
+
response = await tlsFetchChatGpt(CONV_URL, {
|
|
2943
|
+
method: "POST",
|
|
2944
|
+
headers,
|
|
2945
|
+
body: JSON.stringify(cgptBody),
|
|
2946
|
+
timeoutMs: 120_000, // generations can take a while
|
|
2947
|
+
signal,
|
|
2948
|
+
// For real-time streaming, ask the TLS client to write the body to
|
|
2949
|
+
// a temp file and surface it as a ReadableStream as it arrives —
|
|
2950
|
+
// otherwise long generations buffer entirely before the client sees
|
|
2951
|
+
// anything (and the downstream HTTP request can time out).
|
|
2952
|
+
stream,
|
|
2953
|
+
});
|
|
2954
|
+
} catch (err) {
|
|
2955
|
+
log?.error?.("CGPT-WEB", `Fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2956
|
+
const code = err instanceof TlsClientUnavailableError ? "TLS_UNAVAILABLE" : undefined;
|
|
2957
|
+
return {
|
|
2958
|
+
response: errorResponse(
|
|
2959
|
+
502,
|
|
2960
|
+
`ChatGPT connection failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
2961
|
+
code
|
|
2962
|
+
),
|
|
2963
|
+
url: CONV_URL,
|
|
2964
|
+
headers,
|
|
2965
|
+
transformedBody: cgptBody,
|
|
2966
|
+
};
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
if (response.status >= 400) {
|
|
2970
|
+
const status = response.status;
|
|
2971
|
+
// Log the upstream body on 4xx/5xx — error responses are small and the
|
|
2972
|
+
// upstream message is much more useful than our wrapper. Goes through
|
|
2973
|
+
// the executor logger so it respects the application's log config.
|
|
2974
|
+
log?.warn?.("CGPT-WEB", `conv ${status}: ${(response.text || "").slice(0, 400)}`);
|
|
2975
|
+
const errMsg = describeChatGptWebHttpError(status);
|
|
2976
|
+
if (status === 401 || status === 403) {
|
|
2977
|
+
tokenCache.delete(cookieKey(cookie));
|
|
2978
|
+
}
|
|
2979
|
+
log?.warn?.("CGPT-WEB", errMsg);
|
|
2980
|
+
return {
|
|
2981
|
+
response: errorResponse(status, errMsg, `HTTP_${status}`),
|
|
2982
|
+
url: CONV_URL,
|
|
2983
|
+
headers,
|
|
2984
|
+
transformedBody: cgptBody,
|
|
2985
|
+
};
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
// For streaming requests the TLS client returns a ReadableStream that
|
|
2989
|
+
// tails the temp file as it's written. For non-streaming requests, it
|
|
2990
|
+
// returns the full body as text — wrap that in a one-shot stream so the
|
|
2991
|
+
// existing SSE parser can consume it uniformly.
|
|
2992
|
+
let bodyStream: ReadableStream<Uint8Array>;
|
|
2993
|
+
if (response.body) {
|
|
2994
|
+
bodyStream = response.body;
|
|
2995
|
+
} else if (response.text) {
|
|
2996
|
+
bodyStream = stringToStream(response.text);
|
|
2997
|
+
} else {
|
|
2998
|
+
return {
|
|
2999
|
+
response: errorResponse(502, "ChatGPT returned empty response body"),
|
|
3000
|
+
url: CONV_URL,
|
|
3001
|
+
headers,
|
|
3002
|
+
transformedBody: cgptBody,
|
|
3003
|
+
};
|
|
3004
|
+
}
|
|
3005
|
+
|
|
3006
|
+
const cid = `chatcmpl-cgpt-${crypto.randomUUID().slice(0, 12)}`;
|
|
3007
|
+
const created = Math.floor(Date.now() / 1000);
|
|
3008
|
+
|
|
3009
|
+
const resolverCtx: ResolverContext = {
|
|
3010
|
+
accessToken: tokenEntry.accessToken,
|
|
3011
|
+
accountId: tokenEntry.accountId,
|
|
3012
|
+
sessionId,
|
|
3013
|
+
deviceId,
|
|
3014
|
+
cookie,
|
|
3015
|
+
signal,
|
|
3016
|
+
log,
|
|
3017
|
+
publicBaseUrl: derivePublicBaseUrl(clientHeaders, log),
|
|
3018
|
+
};
|
|
3019
|
+
const imageResolver = makeImageResolver(resolverCtx);
|
|
3020
|
+
const pollAsyncImage = (conversationId: string) =>
|
|
3021
|
+
pollForAsyncImage(conversationId, resolverCtx);
|
|
3022
|
+
const pollFinalAnswer = resolvedModel.isPro
|
|
3023
|
+
? (conversationId: string) => pollForFinalAssistantAnswer(conversationId, resolverCtx)
|
|
3024
|
+
: null;
|
|
3025
|
+
|
|
3026
|
+
// Tool mode buffers (no live streaming) and is gated off the image-gen path.
|
|
3027
|
+
const toolMode = hasTools && !forImageGen;
|
|
3028
|
+
|
|
3029
|
+
let finalResponse: Response;
|
|
3030
|
+
if (stream && !toolMode) {
|
|
3031
|
+
const sseStream = buildStreamingResponse(
|
|
3032
|
+
bodyStream,
|
|
3033
|
+
model,
|
|
3034
|
+
cid,
|
|
3035
|
+
created,
|
|
3036
|
+
imageResolver,
|
|
3037
|
+
pollAsyncImage,
|
|
3038
|
+
pollFinalAnswer,
|
|
3039
|
+
log,
|
|
3040
|
+
signal
|
|
3041
|
+
);
|
|
3042
|
+
finalResponse = new Response(sseStream, {
|
|
3043
|
+
status: 200,
|
|
3044
|
+
headers: {
|
|
3045
|
+
"Content-Type": "text/event-stream",
|
|
3046
|
+
"Cache-Control": "no-cache",
|
|
3047
|
+
"X-Accel-Buffering": "no",
|
|
3048
|
+
},
|
|
3049
|
+
});
|
|
3050
|
+
} else {
|
|
3051
|
+
finalResponse = await buildNonStreamingResponse(
|
|
3052
|
+
bodyStream,
|
|
3053
|
+
model,
|
|
3054
|
+
cid,
|
|
3055
|
+
created,
|
|
3056
|
+
parsed.currentMsg,
|
|
3057
|
+
imageResolver,
|
|
3058
|
+
pollAsyncImage,
|
|
3059
|
+
pollFinalAnswer,
|
|
3060
|
+
log,
|
|
3061
|
+
signal
|
|
3062
|
+
);
|
|
3063
|
+
if (toolMode) {
|
|
3064
|
+
finalResponse = await buildToolModeResponse(finalResponse, requestedTools, stream, {
|
|
3065
|
+
cid,
|
|
3066
|
+
created,
|
|
3067
|
+
model,
|
|
3068
|
+
});
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
return { response: finalResponse, url: CONV_URL, headers, transformedBody: cgptBody };
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
// Strip ChatGPT's internal entity markup. The browser renders these as proper
|
|
3077
|
+
// inline citations / chips via JS; for a plain text completion we just want
|
|
3078
|
+
// the human-readable form.
|
|
3079
|
+
// entity["city","Paris","capital of France"] → Paris
|
|
3080
|
+
// entity["…","value", …] → value
|
|
3081
|
+
const ENTITY_RE = /entity\["[^"]*","([^"]*)"[^\]]*\]/g;
|
|
3082
|
+
|
|
3083
|
+
function cleanChatGptText(text: string): string {
|
|
3084
|
+
return text.replace(ENTITY_RE, "$1");
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
function stringToStream(text: string): ReadableStream<Uint8Array> {
|
|
3088
|
+
const encoder = new TextEncoder();
|
|
3089
|
+
return new ReadableStream<Uint8Array>({
|
|
3090
|
+
start(controller) {
|
|
3091
|
+
controller.enqueue(encoder.encode(text));
|
|
3092
|
+
controller.close();
|
|
3093
|
+
},
|
|
3094
|
+
});
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
// Test-only: clear caches between tests
|
|
3098
|
+
export function __resetChatGptWebCachesForTesting(): void {
|
|
3099
|
+
tokenCache.clear();
|
|
3100
|
+
warmupCache.clear();
|
|
3101
|
+
thinkingEffortCache.clear();
|
|
3102
|
+
deviceIdCache.clear();
|
|
3103
|
+
__resetChatGptImageCacheForTesting();
|
|
3104
|
+
dplCache = null;
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
export const __derivePublicBaseUrlForTesting = derivePublicBaseUrl;
|