@fudrouter/fsrouter 0.6.59 → 0.6.60

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.
Files changed (556) hide show
  1. package/package.json +1 -1
  2. package/open-sse/AGENTS.md +0 -39
  3. package/open-sse/config/anthropicHeaders.js +0 -87
  4. package/open-sse/config/appConstants.js +0 -238
  5. package/open-sse/config/audioRegistry.js +0 -506
  6. package/open-sse/config/cliFingerprints.js +0 -304
  7. package/open-sse/config/codexInstructions.js +0 -119
  8. package/open-sse/config/codexQuotaScopes.js +0 -42
  9. package/open-sse/config/constants.js +0 -4
  10. package/open-sse/config/contextEditing.js +0 -68
  11. package/open-sse/config/credentialLoader.js +0 -83
  12. package/open-sse/config/defaultThinkingSignature.js +0 -12
  13. package/open-sse/config/embeddingRegistry.js +0 -264
  14. package/open-sse/config/errorConfig.js +0 -96
  15. package/open-sse/config/freeModelCatalog.data.js +0 -121
  16. package/open-sse/config/freeModelCatalog.js +0 -90
  17. package/open-sse/config/freeTierCatalog.js +0 -81
  18. package/open-sse/config/googleTtsLanguages.js +0 -62
  19. package/open-sse/config/imageParamDefs.js +0 -360
  20. package/open-sse/config/imageRegistry.js +0 -493
  21. package/open-sse/config/kiroConstants.js +0 -268
  22. package/open-sse/config/mediaConfig.js +0 -27
  23. package/open-sse/config/mediaServiceKinds.js +0 -71
  24. package/open-sse/config/models.js +0 -13
  25. package/open-sse/config/moderationRegistry.js +0 -71
  26. package/open-sse/config/musicRegistry.js +0 -99
  27. package/open-sse/config/ocrRegistry.js +0 -61
  28. package/open-sse/config/ollamaModels.js +0 -19
  29. package/open-sse/config/providerErrorRules.js +0 -114
  30. package/open-sse/config/providerFieldStrips.js +0 -35
  31. package/open-sse/config/providerHeaderProfiles.js +0 -191
  32. package/open-sse/config/providerModels.js +0 -106
  33. package/open-sse/config/providerRegistry.js +0 -16
  34. package/open-sse/config/providers.js +0 -19
  35. package/open-sse/config/registryUtils.js +0 -106
  36. package/open-sse/config/rerankRegistry.js +0 -177
  37. package/open-sse/config/runtimeConfig.js +0 -96
  38. package/open-sse/config/runway.js +0 -39
  39. package/open-sse/config/searchRegistry.js +0 -153
  40. package/open-sse/config/toolCloaking.js +0 -199
  41. package/open-sse/config/ttsModels.js +0 -129
  42. package/open-sse/config/videoRegistry.js +0 -207
  43. package/open-sse/executors/adapta-web.js +0 -435
  44. package/open-sse/executors/antigravity.js +0 -534
  45. package/open-sse/executors/antigravityUpstreamError.js +0 -13
  46. package/open-sse/executors/auggie.js +0 -437
  47. package/open-sse/executors/azure-openai.js +0 -32
  48. package/open-sse/executors/azure.js +0 -57
  49. package/open-sse/executors/base.js +0 -181
  50. package/open-sse/executors/bedrock.js +0 -633
  51. package/open-sse/executors/blackbox-web.js +0 -578
  52. package/open-sse/executors/chatgpt-web.js +0 -2042
  53. package/open-sse/executors/chipotle.js +0 -357
  54. package/open-sse/executors/claude-web-with-auto-refresh.js +0 -73
  55. package/open-sse/executors/claude-web.js +0 -586
  56. package/open-sse/executors/cliproxyapi.js +0 -296
  57. package/open-sse/executors/cloudflare-ai.js +0 -46
  58. package/open-sse/executors/codebuddy-cn.js +0 -40
  59. package/open-sse/executors/codex.js +0 -1324
  60. package/open-sse/executors/commandCode.js +0 -558
  61. package/open-sse/executors/commandcode.js +0 -88
  62. package/open-sse/executors/copilot-m365-connection.js +0 -176
  63. package/open-sse/executors/copilot-m365-frames.js +0 -192
  64. package/open-sse/executors/copilot-m365-web.js +0 -299
  65. package/open-sse/executors/copilot-web.js +0 -580
  66. package/open-sse/executors/cursor.js +0 -795
  67. package/open-sse/executors/deepseek-web-with-auto-refresh.js +0 -150
  68. package/open-sse/executors/deepseek-web.js +0 -875
  69. package/open-sse/executors/default.js +0 -430
  70. package/open-sse/executors/devin-cli.js +0 -351
  71. package/open-sse/executors/doubao-web.js +0 -550
  72. package/open-sse/executors/duckduckgo-web.js +0 -629
  73. package/open-sse/executors/forceResponsesUpstream.js +0 -33
  74. package/open-sse/executors/gemini-business.js +0 -320
  75. package/open-sse/executors/gemini-cli.js +0 -89
  76. package/open-sse/executors/gemini-web.js +0 -297
  77. package/open-sse/executors/github.js +0 -379
  78. package/open-sse/executors/gitlab.js +0 -569
  79. package/open-sse/executors/glm.js +0 -361
  80. package/open-sse/executors/grok-cli.js +0 -119
  81. package/open-sse/executors/grok-web.js +0 -345
  82. package/open-sse/executors/huggingchat.js +0 -467
  83. package/open-sse/executors/iflow.js +0 -108
  84. package/open-sse/executors/index.js +0 -78
  85. package/open-sse/executors/inner-ai.js +0 -527
  86. package/open-sse/executors/kimchi.js +0 -123
  87. package/open-sse/executors/kimi-web.js +0 -325
  88. package/open-sse/executors/kimi.js +0 -97
  89. package/open-sse/executors/kiro.js +0 -503
  90. package/open-sse/executors/kiroThinking.js +0 -44
  91. package/open-sse/executors/lmarena.js +0 -395
  92. package/open-sse/executors/mimo-free.js +0 -167
  93. package/open-sse/executors/mimocode.js +0 -392
  94. package/open-sse/executors/ninerouter.js +0 -155
  95. package/open-sse/executors/nlpcloud.js +0 -438
  96. package/open-sse/executors/ollama-local.js +0 -14
  97. package/open-sse/executors/opencode-go.js +0 -41
  98. package/open-sse/executors/opencode.js +0 -32
  99. package/open-sse/executors/perplexity-web.js +0 -581
  100. package/open-sse/executors/poe-web.js +0 -133
  101. package/open-sse/executors/pollinations.js +0 -95
  102. package/open-sse/executors/puter.js +0 -31
  103. package/open-sse/executors/qoder.js +0 -448
  104. package/open-sse/executors/qwen-web.js +0 -372
  105. package/open-sse/executors/qwen.js +0 -129
  106. package/open-sse/executors/t3-chat-web.js +0 -417
  107. package/open-sse/executors/theoldllm.js +0 -290
  108. package/open-sse/executors/trae.js +0 -418
  109. package/open-sse/executors/v0-vercel-web.js +0 -141
  110. package/open-sse/executors/venice-web.js +0 -142
  111. package/open-sse/executors/vertex.js +0 -131
  112. package/open-sse/executors/vertexMedia.js +0 -236
  113. package/open-sse/executors/windsurf.js +0 -560
  114. package/open-sse/executors/xai.js +0 -46
  115. package/open-sse/executors/xiaomi-tokenplan.js +0 -19
  116. package/open-sse/executors/yuanbao-web.js +0 -385
  117. package/open-sse/executors/zed-hosted.js +0 -281
  118. package/open-sse/executors/zenmux-free.js +0 -247
  119. package/open-sse/handlers/audioSpeech.js +0 -759
  120. package/open-sse/handlers/audioTranscription.js +0 -395
  121. package/open-sse/handlers/audioTranslation.js +0 -181
  122. package/open-sse/handlers/chatCore/nonStreamingHandler.js +0 -300
  123. package/open-sse/handlers/chatCore/requestDetail.js +0 -102
  124. package/open-sse/handlers/chatCore/sseToJsonHandler.js +0 -231
  125. package/open-sse/handlers/chatCore/streamingHandler.js +0 -95
  126. package/open-sse/handlers/chatCore.js +0 -404
  127. package/open-sse/handlers/embeddingProviders/_base.js +0 -4
  128. package/open-sse/handlers/embeddingProviders/gemini.js +0 -54
  129. package/open-sse/handlers/embeddingProviders/index.js +0 -23
  130. package/open-sse/handlers/embeddingProviders/openai.js +0 -39
  131. package/open-sse/handlers/embeddingProviders/openaiCompatNode.js +0 -13
  132. package/open-sse/handlers/embeddingsCore.js +0 -126
  133. package/open-sse/handlers/fetch/index.js +0 -237
  134. package/open-sse/handlers/imageGenerationCore.js +0 -230
  135. package/open-sse/handlers/imageProviders/_base.js +0 -31
  136. package/open-sse/handlers/imageProviders/antigravity.js +0 -73
  137. package/open-sse/handlers/imageProviders/blackForestLabs.js +0 -63
  138. package/open-sse/handlers/imageProviders/cloudflareAi.js +0 -207
  139. package/open-sse/handlers/imageProviders/codex.js +0 -217
  140. package/open-sse/handlers/imageProviders/comfyui.js +0 -25
  141. package/open-sse/handlers/imageProviders/falAi.js +0 -62
  142. package/open-sse/handlers/imageProviders/gemini.js +0 -40
  143. package/open-sse/handlers/imageProviders/huggingface.js +0 -38
  144. package/open-sse/handlers/imageProviders/index.js +0 -44
  145. package/open-sse/handlers/imageProviders/leonardo.js +0 -435
  146. package/open-sse/handlers/imageProviders/nanobanana.js +0 -79
  147. package/open-sse/handlers/imageProviders/openai.js +0 -61
  148. package/open-sse/handlers/imageProviders/runwayml.js +0 -72
  149. package/open-sse/handlers/imageProviders/sdwebui.js +0 -36
  150. package/open-sse/handlers/imageProviders/stabilityAi.js +0 -59
  151. package/open-sse/handlers/imageProviders/weavy.js +0 -222
  152. package/open-sse/handlers/moderations.js +0 -92
  153. package/open-sse/handlers/ocr.js +0 -89
  154. package/open-sse/handlers/rerank.js +0 -223
  155. package/open-sse/handlers/responseSanitizer/reasoning.js +0 -144
  156. package/open-sse/handlers/responsesHandler.js +0 -99
  157. package/open-sse/handlers/search/callers.js +0 -371
  158. package/open-sse/handlers/search/chatSearch.js +0 -409
  159. package/open-sse/handlers/search/index.js +0 -201
  160. package/open-sse/handlers/search/normalizers.js +0 -223
  161. package/open-sse/handlers/search.js +0 -1056
  162. package/open-sse/handlers/sseParser.js +0 -469
  163. package/open-sse/handlers/sttCore.js +0 -193
  164. package/open-sse/handlers/ttsCore.js +0 -74
  165. package/open-sse/handlers/ttsProviders/_base.js +0 -39
  166. package/open-sse/handlers/ttsProviders/edgeTts.js +0 -89
  167. package/open-sse/handlers/ttsProviders/elevenlabs.js +0 -48
  168. package/open-sse/handlers/ttsProviders/gemini.js +0 -117
  169. package/open-sse/handlers/ttsProviders/genericFormats.js +0 -169
  170. package/open-sse/handlers/ttsProviders/googleTts.js +0 -54
  171. package/open-sse/handlers/ttsProviders/index.js +0 -50
  172. package/open-sse/handlers/ttsProviders/localDevice.js +0 -87
  173. package/open-sse/handlers/ttsProviders/minimax.js +0 -59
  174. package/open-sse/handlers/ttsProviders/openai.js +0 -30
  175. package/open-sse/handlers/ttsProviders/openrouter.js +0 -70
  176. package/open-sse/handlers/usageExtractor.js +0 -93
  177. package/open-sse/handlers/videoGenerationCore.js +0 -119
  178. package/open-sse/handlers/videoProviders/index.js +0 -22
  179. package/open-sse/handlers/videoProviders/leonardo.js +0 -511
  180. package/open-sse/handlers/videoProviders/weavy.js +0 -198
  181. package/open-sse/handlers/webFetch.js +0 -200
  182. package/open-sse/index.js +0 -79
  183. package/open-sse/mcp-server/audit.js +0 -283
  184. package/open-sse/mcp-server/catalog.js +0 -184
  185. package/open-sse/mcp-server/descriptionCompressor.js +0 -175
  186. package/open-sse/mcp-server/httpAuthContext.js +0 -32
  187. package/open-sse/mcp-server/httpTransport.js +0 -229
  188. package/open-sse/mcp-server/index.js +0 -33
  189. package/open-sse/mcp-server/mcpCallerIdentity.js +0 -21
  190. package/open-sse/mcp-server/runtimeHeartbeat.js +0 -110
  191. package/open-sse/mcp-server/schemas/a2a.js +0 -153
  192. package/open-sse/mcp-server/schemas/audit.js +0 -17
  193. package/open-sse/mcp-server/schemas/index.js +0 -166
  194. package/open-sse/mcp-server/schemas/pickFastestModel.js +0 -90
  195. package/open-sse/mcp-server/schemas/toolDefinition.js +0 -0
  196. package/open-sse/mcp-server/schemas/toolSearch.js +0 -32
  197. package/open-sse/mcp-server/schemas/tools.js +0 -1219
  198. package/open-sse/mcp-server/scopeEnforcement.js +0 -80
  199. package/open-sse/mcp-server/server.js +0 -1168
  200. package/open-sse/mcp-server/toolCardinality.js +0 -78
  201. package/open-sse/mcp-server/toolSearch/catalog.js +0 -66
  202. package/open-sse/mcp-server/toolSearch/handler.js +0 -20
  203. package/open-sse/mcp-server/toolSearch/index.js +0 -10
  204. package/open-sse/mcp-server/toolSearch/register.js +0 -21
  205. package/open-sse/mcp-server/toolSearch/search.js +0 -55
  206. package/open-sse/mcp-server/toolSearch/signature.js +0 -67
  207. package/open-sse/mcp-server/tools/advancedTools.js +0 -893
  208. package/open-sse/mcp-server/tools/agentSkillTools.js +0 -69
  209. package/open-sse/mcp-server/tools/compressionTools.js +0 -309
  210. package/open-sse/mcp-server/tools/gamificationTools.js +0 -134
  211. package/open-sse/mcp-server/tools/githubSkillTools.js +0 -102
  212. package/open-sse/mcp-server/tools/memoryTools.js +0 -118
  213. package/open-sse/mcp-server/tools/notionTools.js +0 -101
  214. package/open-sse/mcp-server/tools/obsidianTools.js +0 -307
  215. package/open-sse/mcp-server/tools/pickFastestModel.js +0 -223
  216. package/open-sse/mcp-server/tools/pluginTools.js +0 -181
  217. package/open-sse/mcp-server/tools/poolTools.js +0 -139
  218. package/open-sse/mcp-server/tools/skillTools.js +0 -113
  219. package/open-sse/package.json +0 -10
  220. package/open-sse/providers/REGISTRY_TEMPLATE.js +0 -98
  221. package/open-sse/providers/capabilities.js +0 -293
  222. package/open-sse/providers/index.js +0 -51
  223. package/open-sse/providers/models/helpers.js +0 -20
  224. package/open-sse/providers/models/namePatterns.js +0 -33
  225. package/open-sse/providers/models/schema.js +0 -40
  226. package/open-sse/providers/pricing.js +0 -308
  227. package/open-sse/providers/registry/alicode-intl.js +0 -30
  228. package/open-sse/providers/registry/alicode.js +0 -31
  229. package/open-sse/providers/registry/anthropic.js +0 -37
  230. package/open-sse/providers/registry/antigravity.js +0 -81
  231. package/open-sse/providers/registry/assemblyai.js +0 -38
  232. package/open-sse/providers/registry/aws-polly.js +0 -45
  233. package/open-sse/providers/registry/azure.js +0 -21
  234. package/open-sse/providers/registry/black-forest-labs.js +0 -32
  235. package/open-sse/providers/registry/blackbox.js +0 -41
  236. package/open-sse/providers/registry/brave-search.js +0 -35
  237. package/open-sse/providers/registry/byteplus.js +0 -35
  238. package/open-sse/providers/registry/cartesia.js +0 -36
  239. package/open-sse/providers/registry/cerebras.js +0 -31
  240. package/open-sse/providers/registry/chutes.js +0 -24
  241. package/open-sse/providers/registry/claude.js +0 -87
  242. package/open-sse/providers/registry/cline.js +0 -51
  243. package/open-sse/providers/registry/clinepass.js +0 -57
  244. package/open-sse/providers/registry/cloudflare-ai.js +0 -55
  245. package/open-sse/providers/registry/codebuddy-cn.js +0 -77
  246. package/open-sse/providers/registry/codex.js +0 -100
  247. package/open-sse/providers/registry/cohere.js +0 -27
  248. package/open-sse/providers/registry/comfyui.js +0 -20
  249. package/open-sse/providers/registry/commandcode.js +0 -43
  250. package/open-sse/providers/registry/coqui.js +0 -30
  251. package/open-sse/providers/registry/cursor.js +0 -58
  252. package/open-sse/providers/registry/deepgram.js +0 -33
  253. package/open-sse/providers/registry/deepseek.js +0 -51
  254. package/open-sse/providers/registry/digitalocean.js +0 -92
  255. package/open-sse/providers/registry/edge-tts.js +0 -24
  256. package/open-sse/providers/registry/elevenlabs.js +0 -35
  257. package/open-sse/providers/registry/exa.js +0 -50
  258. package/open-sse/providers/registry/fal-ai.js +0 -34
  259. package/open-sse/providers/registry/featherless.js +0 -34
  260. package/open-sse/providers/registry/firecrawl.js +0 -34
  261. package/open-sse/providers/registry/fireworks.js +0 -29
  262. package/open-sse/providers/registry/gemini-cli.js +0 -58
  263. package/open-sse/providers/registry/gemini.js +0 -83
  264. package/open-sse/providers/registry/github.js +0 -88
  265. package/open-sse/providers/registry/gitlab.js +0 -32
  266. package/open-sse/providers/registry/glm-cn.js +0 -35
  267. package/open-sse/providers/registry/glm.js +0 -58
  268. package/open-sse/providers/registry/google-pse.js +0 -35
  269. package/open-sse/providers/registry/google-tts.js +0 -24
  270. package/open-sse/providers/registry/grok-cli.js +0 -86
  271. package/open-sse/providers/registry/grok-web.js +0 -39
  272. package/open-sse/providers/registry/groq.js +0 -37
  273. package/open-sse/providers/registry/huggingface.js +0 -34
  274. package/open-sse/providers/registry/hyperbolic.js +0 -35
  275. package/open-sse/providers/registry/ibm-bob.js +0 -27
  276. package/open-sse/providers/registry/iflow.js +0 -52
  277. package/open-sse/providers/registry/index.js +0 -210
  278. package/open-sse/providers/registry/inworld.js +0 -36
  279. package/open-sse/providers/registry/jina-ai.js +0 -42
  280. package/open-sse/providers/registry/jina-reader.js +0 -34
  281. package/open-sse/providers/registry/kilocode.js +0 -51
  282. package/open-sse/providers/registry/kimchi.js +0 -49
  283. package/open-sse/providers/registry/kimi-coding.js +0 -65
  284. package/open-sse/providers/registry/kimi.js +0 -56
  285. package/open-sse/providers/registry/kiro.js +0 -115
  286. package/open-sse/providers/registry/linkup.js +0 -34
  287. package/open-sse/providers/registry/local-device.js +0 -24
  288. package/open-sse/providers/registry/mimo-free.js +0 -24
  289. package/open-sse/providers/registry/minimax-cn.js +0 -76
  290. package/open-sse/providers/registry/minimax.js +0 -83
  291. package/open-sse/providers/registry/mistral.js +0 -34
  292. package/open-sse/providers/registry/mmf.js +0 -19
  293. package/open-sse/providers/registry/nanobanana.js +0 -35
  294. package/open-sse/providers/registry/nebius.js +0 -27
  295. package/open-sse/providers/registry/nvidia.js +0 -43
  296. package/open-sse/providers/registry/ollama-local.js +0 -19
  297. package/open-sse/providers/registry/ollama.js +0 -36
  298. package/open-sse/providers/registry/openai.js +0 -84
  299. package/open-sse/providers/registry/opencode-go.js +0 -41
  300. package/open-sse/providers/registry/opencode.js +0 -25
  301. package/open-sse/providers/registry/openrouter.js +0 -60
  302. package/open-sse/providers/registry/perplexity-agent.js +0 -49
  303. package/open-sse/providers/registry/perplexity-web.js +0 -33
  304. package/open-sse/providers/registry/perplexity.js +0 -37
  305. package/open-sse/providers/registry/playht.js +0 -36
  306. package/open-sse/providers/registry/qoder.js +0 -54
  307. package/open-sse/providers/registry/qwen.js +0 -33
  308. package/open-sse/providers/registry/recraft.js +0 -24
  309. package/open-sse/providers/registry/runwayml.js +0 -30
  310. package/open-sse/providers/registry/sdwebui.js +0 -20
  311. package/open-sse/providers/registry/searchapi.js +0 -35
  312. package/open-sse/providers/registry/searxng.js +0 -35
  313. package/open-sse/providers/registry/serper.js +0 -35
  314. package/open-sse/providers/registry/siliconflow.js +0 -39
  315. package/open-sse/providers/registry/stability-ai.js +0 -31
  316. package/open-sse/providers/registry/tavily.js +0 -50
  317. package/open-sse/providers/registry/together.js +0 -31
  318. package/open-sse/providers/registry/topaz.js +0 -19
  319. package/open-sse/providers/registry/tortoise.js +0 -30
  320. package/open-sse/providers/registry/venice.js +0 -56
  321. package/open-sse/providers/registry/vercel-ai-gateway.js +0 -41
  322. package/open-sse/providers/registry/vertex-partner.js +0 -29
  323. package/open-sse/providers/registry/vertex.js +0 -32
  324. package/open-sse/providers/registry/volcengine-ark.js +0 -35
  325. package/open-sse/providers/registry/voyage-ai.js +0 -30
  326. package/open-sse/providers/registry/xai.js +0 -45
  327. package/open-sse/providers/registry/xiaomi-mimo.js +0 -46
  328. package/open-sse/providers/registry/xiaomi-tokenplan.js +0 -63
  329. package/open-sse/providers/registry/youcom.js +0 -35
  330. package/open-sse/providers/schema.js +0 -76
  331. package/open-sse/providers/shared.js +0 -74
  332. package/open-sse/providers/thinkingLevels.js +0 -48
  333. package/open-sse/rtk/applyFilter.js +0 -15
  334. package/open-sse/rtk/autodetect.js +0 -111
  335. package/open-sse/rtk/caveman.js +0 -100
  336. package/open-sse/rtk/cavemanPrompts.js +0 -78
  337. package/open-sse/rtk/constants.js +0 -55
  338. package/open-sse/rtk/filters/buildOutput.js +0 -127
  339. package/open-sse/rtk/filters/dedupLog.js +0 -44
  340. package/open-sse/rtk/filters/find.js +0 -50
  341. package/open-sse/rtk/filters/gitDiff.js +0 -92
  342. package/open-sse/rtk/filters/gitLog.js +0 -99
  343. package/open-sse/rtk/filters/gitStatus.js +0 -117
  344. package/open-sse/rtk/filters/grep.js +0 -48
  345. package/open-sse/rtk/filters/ls.js +0 -79
  346. package/open-sse/rtk/filters/readNumbered.js +0 -27
  347. package/open-sse/rtk/filters/searchList.js +0 -52
  348. package/open-sse/rtk/filters/smartTruncate.js +0 -15
  349. package/open-sse/rtk/filters/tree.js +0 -32
  350. package/open-sse/rtk/headroom.js +0 -215
  351. package/open-sse/rtk/index.js +0 -155
  352. package/open-sse/rtk/paleo.js +0 -47
  353. package/open-sse/rtk/ponytail.js +0 -9
  354. package/open-sse/rtk/ponytailPrompt.js +0 -52
  355. package/open-sse/rtk/ponytailPrompts.js +0 -76
  356. package/open-sse/rtk/pxpipe.js +0 -104
  357. package/open-sse/rtk/registry.js +0 -38
  358. package/open-sse/rtk/systemInject.js +0 -98
  359. package/open-sse/services/accountFallback.js +0 -323
  360. package/open-sse/services/antigravityHeaders.js +0 -91
  361. package/open-sse/services/antigravityVersion.js +0 -154
  362. package/open-sse/services/clinepassModels.js +0 -63
  363. package/open-sse/services/codexQuotaFetcher.js +0 -312
  364. package/open-sse/services/codexVerbosity.js +0 -30
  365. package/open-sse/services/combo/context.js +0 -12
  366. package/open-sse/services/combo/fingerprintExpansion.js +0 -51
  367. package/open-sse/services/combo/headroomRanking.js +0 -28
  368. package/open-sse/services/combo/quotaExhaustionCutoff.js +0 -87
  369. package/open-sse/services/combo/quotaScoring.js +0 -226
  370. package/open-sse/services/combo/quotaShareConcurrency.js +0 -25
  371. package/open-sse/services/combo/quotaShareInflight.js +0 -50
  372. package/open-sse/services/combo/quotaShareStrategy.js +0 -130
  373. package/open-sse/services/combo/resolveAutoStrategy.js +0 -231
  374. package/open-sse/services/combo/responseValidation.js +0 -140
  375. package/open-sse/services/combo/runtimeUnits.js +0 -195
  376. package/open-sse/services/combo/sessionStickiness.js +0 -128
  377. package/open-sse/services/combo/shadowRouting.js +0 -131
  378. package/open-sse/services/combo/targetExhaustion.js +0 -75
  379. package/open-sse/services/combo/targetSorters.js +0 -126
  380. package/open-sse/services/combo/targetTimeoutRunner.js +0 -65
  381. package/open-sse/services/combo/validateQuality.js +0 -271
  382. package/open-sse/services/combo.js +0 -571
  383. package/open-sse/services/comboAgentMiddleware.js +0 -91
  384. package/open-sse/services/comboConfig.js +0 -153
  385. package/open-sse/services/comboManifestMetrics.js +0 -10
  386. package/open-sse/services/comboMetrics.js +0 -278
  387. package/open-sse/services/compact.js +0 -71
  388. package/open-sse/services/copilotModels.js +0 -155
  389. package/open-sse/services/emergencyFallback.js +0 -109
  390. package/open-sse/services/errorClassifier.js +0 -190
  391. package/open-sse/services/evalRouting.js +0 -201
  392. package/open-sse/services/freeWebSearch.js +0 -4
  393. package/open-sse/services/fusion.js +0 -223
  394. package/open-sse/services/intentClassifier.js +0 -631
  395. package/open-sse/services/kimchiModels.js +0 -176
  396. package/open-sse/services/kiroModels.js +0 -332
  397. package/open-sse/services/model.js +0 -136
  398. package/open-sse/services/modelCapabilities.js +0 -5
  399. package/open-sse/services/modelDeprecation.js +0 -101
  400. package/open-sse/services/modelFamilyFallback.js +0 -166
  401. package/open-sse/services/oauthCredentialManager.js +0 -156
  402. package/open-sse/services/pipeline.js +0 -107
  403. package/open-sse/services/projectId.js +0 -306
  404. package/open-sse/services/provider.js +0 -169
  405. package/open-sse/services/providerCooldownTracker.js +0 -94
  406. package/open-sse/services/providerDefaultRateLimit.js +0 -11
  407. package/open-sse/services/proxyAutoSelector.js +0 -24
  408. package/open-sse/services/qoderModels.js +0 -214
  409. package/open-sse/services/quotaFetchThrottle.js +0 -103
  410. package/open-sse/services/quotaMonitor.js +0 -310
  411. package/open-sse/services/quotaPreflight.js +0 -266
  412. package/open-sse/services/rateLimitManager/headers.js +0 -56
  413. package/open-sse/services/rateLimitManager.js +0 -616
  414. package/open-sse/services/rateLimitSemaphore.js +0 -112
  415. package/open-sse/services/responsesInputSanitizer.js +0 -99
  416. package/open-sse/services/slidingWindowLimiter.js +0 -44
  417. package/open-sse/services/streamRecovery.js +0 -355
  418. package/open-sse/services/taskAwareRouter.js +0 -227
  419. package/open-sse/services/taskAwareRouting.js +0 -332
  420. package/open-sse/services/thinkingBudget.js +0 -247
  421. package/open-sse/services/tokenRefresh/dedup.js +0 -31
  422. package/open-sse/services/tokenRefresh/providers.js +0 -624
  423. package/open-sse/services/tokenRefresh.js +0 -254
  424. package/open-sse/services/usage/claude.js +0 -147
  425. package/open-sse/services/usage/codebuddy-cn.js +0 -138
  426. package/open-sse/services/usage/codex.js +0 -267
  427. package/open-sse/services/usage/github.js +0 -100
  428. package/open-sse/services/usage/google.js +0 -241
  429. package/open-sse/services/usage/grok-cli.js +0 -274
  430. package/open-sse/services/usage/kiro.js +0 -188
  431. package/open-sse/services/usage/minimax.js +0 -234
  432. package/open-sse/services/usage/misc.js +0 -288
  433. package/open-sse/services/usage/shared.js +0 -70
  434. package/open-sse/services/usage.js +0 -62
  435. package/open-sse/services/usageDb.js +0 -2
  436. package/open-sse/services/webSearchRouting.js +0 -25
  437. package/open-sse/services/wildcardRouter.js +0 -59
  438. package/open-sse/shared/clineAuth.js +0 -37
  439. package/open-sse/shared/machineId.js +0 -19
  440. package/open-sse/shared/qoder/constants.js +0 -64
  441. package/open-sse/shared/qoder/cosy.js +0 -175
  442. package/open-sse/shared/qoder/encoding.js +0 -55
  443. package/open-sse/transformer/responsesTransformer.js +0 -439
  444. package/open-sse/transformer/streamToJsonConverter.js +0 -103
  445. package/open-sse/translator/concerns/chunk.js +0 -11
  446. package/open-sse/translator/concerns/finishReason.js +0 -63
  447. package/open-sse/translator/concerns/image.js +0 -124
  448. package/open-sse/translator/concerns/json.js +0 -5
  449. package/open-sse/translator/concerns/message.js +0 -7
  450. package/open-sse/translator/concerns/modality.js +0 -155
  451. package/open-sse/translator/concerns/paramSupport.js +0 -75
  452. package/open-sse/translator/concerns/prefetch.js +0 -96
  453. package/open-sse/translator/concerns/reasoning.js +0 -24
  454. package/open-sse/translator/concerns/thinking.js +0 -53
  455. package/open-sse/translator/concerns/thinkingUnified.js +0 -329
  456. package/open-sse/translator/concerns/toolCall.js +0 -153
  457. package/open-sse/translator/concerns/usage.js +0 -69
  458. package/open-sse/translator/formats/claude.js +0 -368
  459. package/open-sse/translator/formats/gemini.js +0 -380
  460. package/open-sse/translator/formats/maxTokens.js +0 -34
  461. package/open-sse/translator/formats/openai.js +0 -134
  462. package/open-sse/translator/formats/responsesApi.js +0 -141
  463. package/open-sse/translator/formats.js +0 -36
  464. package/open-sse/translator/helpers/claudeHelper.js +0 -216
  465. package/open-sse/translator/helpers/geminiHelper.js +0 -372
  466. package/open-sse/translator/helpers/imageHelper.js +0 -34
  467. package/open-sse/translator/helpers/maxTokensHelper.js +0 -27
  468. package/open-sse/translator/helpers/openaiHelper.js +0 -130
  469. package/open-sse/translator/helpers/responsesApiHelper.js +0 -139
  470. package/open-sse/translator/helpers/toolCallHelper.js +0 -148
  471. package/open-sse/translator/index.js +0 -252
  472. package/open-sse/translator/request/antigravity-to-openai.js +0 -242
  473. package/open-sse/translator/request/claude-to-kiro.js +0 -478
  474. package/open-sse/translator/request/claude-to-openai.js +0 -263
  475. package/open-sse/translator/request/gemini-to-openai.js +0 -152
  476. package/open-sse/translator/request/openai-responses.js +0 -385
  477. package/open-sse/translator/request/openai-to-claude.js +0 -383
  478. package/open-sse/translator/request/openai-to-commandcode.js +0 -176
  479. package/open-sse/translator/request/openai-to-cursor.js +0 -185
  480. package/open-sse/translator/request/openai-to-gemini.js +0 -442
  481. package/open-sse/translator/request/openai-to-kiro.js +0 -607
  482. package/open-sse/translator/request/openai-to-kiro.old.js +0 -278
  483. package/open-sse/translator/request/openai-to-ollama.js +0 -195
  484. package/open-sse/translator/request/openai-to-vertex.js +0 -42
  485. package/open-sse/translator/response/claude-to-openai.js +0 -195
  486. package/open-sse/translator/response/commandcode-to-openai.js +0 -188
  487. package/open-sse/translator/response/cursor-to-openai.js +0 -30
  488. package/open-sse/translator/response/gemini-to-openai.js +0 -147
  489. package/open-sse/translator/response/kiro-to-claude.js +0 -261
  490. package/open-sse/translator/response/kiro-to-openai.js +0 -160
  491. package/open-sse/translator/response/ollama-to-openai.js +0 -134
  492. package/open-sse/translator/response/openai-responses.js +0 -535
  493. package/open-sse/translator/response/openai-to-antigravity.js +0 -123
  494. package/open-sse/translator/response/openai-to-claude.js +0 -265
  495. package/open-sse/translator/schema/blocks.js +0 -43
  496. package/open-sse/translator/schema/defaults.js +0 -7
  497. package/open-sse/translator/schema/finishReasons.js +0 -27
  498. package/open-sse/translator/schema/index.js +0 -8
  499. package/open-sse/translator/schema/roles.js +0 -16
  500. package/open-sse/utils/agentGoalPolicy.js +0 -85
  501. package/open-sse/utils/aiSdkCompat.js +0 -75
  502. package/open-sse/utils/bypassHandler.js +0 -298
  503. package/open-sse/utils/cacheControlPolicy.js +0 -189
  504. package/open-sse/utils/claudeCloaking.js +0 -155
  505. package/open-sse/utils/claudeHeaderCache.js +0 -70
  506. package/open-sse/utils/claudeSignature.js +0 -41
  507. package/open-sse/utils/clientDetector.js +0 -63
  508. package/open-sse/utils/codebuddySanitizer.js +0 -96
  509. package/open-sse/utils/composerToolCalls.js +0 -158
  510. package/open-sse/utils/cors.js +0 -13
  511. package/open-sse/utils/cursorChecksum.js +0 -149
  512. package/open-sse/utils/cursorImages.js +0 -224
  513. package/open-sse/utils/cursorProtobuf.js +0 -904
  514. package/open-sse/utils/cursorVersionDetector.js +0 -55
  515. package/open-sse/utils/debugLog.js +0 -14
  516. package/open-sse/utils/diagnostics.js +0 -143
  517. package/open-sse/utils/error.js +0 -164
  518. package/open-sse/utils/estimateSize.js +0 -33
  519. package/open-sse/utils/finishReason.js +0 -32
  520. package/open-sse/utils/flattenToolHistory.js +0 -68
  521. package/open-sse/utils/headers.js +0 -36
  522. package/open-sse/utils/heapPressure.js +0 -35
  523. package/open-sse/utils/jsonToSse.js +0 -125
  524. package/open-sse/utils/keepaliveThreshold.js +0 -40
  525. package/open-sse/utils/logger.js +0 -104
  526. package/open-sse/utils/noThinkingAlias.js +0 -74
  527. package/open-sse/utils/number.js +0 -7
  528. package/open-sse/utils/ollamaTransform.js +0 -85
  529. package/open-sse/utils/opencodeHeaders.js +0 -47
  530. package/open-sse/utils/progressTracker.js +0 -81
  531. package/open-sse/utils/providerRequestLogging.js +0 -219
  532. package/open-sse/utils/proxyFetch.js +0 -369
  533. package/open-sse/utils/reasoningContentInjector.js +0 -79
  534. package/open-sse/utils/reasoningFields.js +0 -70
  535. package/open-sse/utils/requestLogger.js +0 -260
  536. package/open-sse/utils/responsesInputNormalization.js +0 -92
  537. package/open-sse/utils/responsesStatePolicy.js +0 -52
  538. package/open-sse/utils/responsesStreamHelpers.js +0 -49
  539. package/open-sse/utils/sessionManager.js +0 -240
  540. package/open-sse/utils/sleep.js +0 -6
  541. package/open-sse/utils/sse.js +0 -14
  542. package/open-sse/utils/sseConstants.js +0 -23
  543. package/open-sse/utils/sseHeartbeat.js +0 -104
  544. package/open-sse/utils/stream.js +0 -517
  545. package/open-sse/utils/streamFailureFinalization.js +0 -134
  546. package/open-sse/utils/streamHandler.js +0 -250
  547. package/open-sse/utils/streamHelpers.js +0 -137
  548. package/open-sse/utils/textualToolCall.js +0 -104
  549. package/open-sse/utils/thinkCloseMarker.js +0 -72
  550. package/open-sse/utils/thinkTagParser.js +0 -146
  551. package/open-sse/utils/toolCallArguments.js +0 -12
  552. package/open-sse/utils/toolDeduper.js +0 -49
  553. package/open-sse/utils/toolSources.js +0 -31
  554. package/open-sse/utils/upstreamResponseHeaders.js +0 -25
  555. package/open-sse/utils/urlSanitize.js +0 -15
  556. package/open-sse/utils/usageTracking.js +0 -347
@@ -1,2042 +0,0 @@
1
- import { BaseExecutor } from "./base.ts";
2
- import { describeChatGptWebHttpError } from "./chatgptWebErrors.ts";
3
- import { prepareToolMessages } from "../translator/webTools.ts";
4
- import { buildToolModeResponse } from "./chatgptWebTools.ts";
5
- import { createHash, randomUUID, randomBytes } from "node:crypto";
6
- import { sha3_512Hex } from "../utils/sha3-512.ts";
7
- import {
8
- tlsFetchChatGpt,
9
- TlsClientUnavailableError
10
- } from "../services/chatgptTlsClient.ts";
11
- import {
12
- storeChatGptImage,
13
- getChatGptImageConversationContext,
14
- __resetChatGptImageCacheForTesting
15
- } from "../services/chatgptImageCache.ts";
16
- import { isThinkingCapableModel, resolveChatGptModel } from "./chatgpt-web/models.ts";
17
- const CHATGPT_BASE = "https://chatgpt.com";
18
- const SESSION_URL = `${CHATGPT_BASE}/api/auth/session`;
19
- const SENTINEL_PREPARE_URL = `${CHATGPT_BASE}/backend-api/sentinel/chat-requirements/prepare`;
20
- const SENTINEL_CR_URL = `${CHATGPT_BASE}/backend-api/sentinel/chat-requirements`;
21
- const CONV_URL = `${CHATGPT_BASE}/backend-api/f/conversation`;
22
- const USER_LAST_USED_MODEL_CONFIG_URL = `${CHATGPT_BASE}/backend-api/settings/user_last_used_model_config`;
23
- const DEFAULT_PRO_POLL_TIMEOUT_MS = 20 * 6e4;
24
- const DEFAULT_PRO_POLL_INTERVAL_MS = 4e3;
25
- const CHATGPT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:152.0) Gecko/20100101 Firefox/152.0";
26
- const OAI_CLIENT_VERSION = "prod-81e0c5cdf6140e8c5db714d613337f4aeab94029";
27
- const OAI_CLIENT_BUILD_NUMBER = "6128297";
28
- const deviceIdCache = /* @__PURE__ */ new Map();
29
- function deviceIdFor(cookie) {
30
- const key = cookieKey(cookie);
31
- let id = deviceIdCache.get(key);
32
- if (!id) {
33
- const h = createHash("sha256").update(cookie).digest("hex");
34
- id = `${h.slice(0, 8)}-${h.slice(8, 12)}-4${h.slice(13, 16)}-${(parseInt(h.slice(16, 17), 16) & 3 | 8).toString(16)}${h.slice(17, 20)}-` + h.slice(20, 32);
35
- if (deviceIdCache.size >= 200) {
36
- const first = deviceIdCache.keys().next().value;
37
- if (first) deviceIdCache.delete(first);
38
- }
39
- deviceIdCache.set(key, id);
40
- }
41
- return id;
42
- }
43
- function browserHeaders() {
44
- return {
45
- Accept: "*/*",
46
- "Accept-Language": "en-US,en;q=0.9",
47
- "Cache-Control": "no-cache",
48
- Origin: CHATGPT_BASE,
49
- Pragma: "no-cache",
50
- Referer: `${CHATGPT_BASE}/`,
51
- "Sec-Fetch-Dest": "empty",
52
- "Sec-Fetch-Mode": "cors",
53
- "Sec-Fetch-Site": "same-origin",
54
- "User-Agent": CHATGPT_USER_AGENT
55
- };
56
- }
57
- function oaiHeaders(sessionId, deviceId) {
58
- return {
59
- "OAI-Language": "en-US",
60
- "OAI-Device-Id": deviceId,
61
- "OAI-Client-Version": OAI_CLIENT_VERSION,
62
- "OAI-Client-Build-Number": OAI_CLIENT_BUILD_NUMBER,
63
- "OAI-Session-Id": sessionId
64
- };
65
- }
66
- const TOKEN_TTL_MS = 5 * 60 * 1e3;
67
- const tokenCache = /* @__PURE__ */ new Map();
68
- function cookieKey(cookie) {
69
- return createHash("sha256").update(cookie).digest("hex").slice(0, 16);
70
- }
71
- function tokenLookup(cookie) {
72
- const entry = tokenCache.get(cookieKey(cookie));
73
- if (!entry) return null;
74
- if (Date.now() >= entry.expiresAt) {
75
- tokenCache.delete(cookieKey(cookie));
76
- return null;
77
- }
78
- return entry;
79
- }
80
- const TOKEN_CACHE_MAX = 200;
81
- function tokenStore(cookie, entry) {
82
- if (tokenCache.size >= TOKEN_CACHE_MAX && !tokenCache.has(cookieKey(cookie))) {
83
- const firstKey = tokenCache.keys().next().value;
84
- if (firstKey) tokenCache.delete(firstKey);
85
- }
86
- tokenCache.set(cookieKey(cookie), entry);
87
- }
88
- const SESSION_TOKEN_FAMILY_RE = /^__Secure-next-auth\.session-token(?:\.\d+)?$/;
89
- function mergeRefreshedCookie(originalCookie, setCookieHeader) {
90
- if (!setCookieHeader) return null;
91
- const matches = Array.from(
92
- setCookieHeader.matchAll(/(__Secure-next-auth\.session-token(?:\.\d+)?)=([^;,\s]+)/g)
93
- );
94
- if (matches.length === 0) return null;
95
- const refreshed = /* @__PURE__ */ new Map();
96
- for (const m of matches) refreshed.set(m[1], m[2]);
97
- let blob = originalCookie.trim();
98
- if (/^cookie\s*:\s*/i.test(blob)) blob = blob.replace(/^cookie\s*:\s*/i, "");
99
- if (!/=/.test(blob)) {
100
- return Array.from(refreshed, ([k, v]) => `${k}=${v}`).join("; ");
101
- }
102
- const pairs = blob.split(/;\s*/).filter(Boolean);
103
- const result = [];
104
- let mutated = false;
105
- let droppedStale = false;
106
- for (const pair of pairs) {
107
- const eqIdx = pair.indexOf("=");
108
- if (eqIdx < 0) {
109
- result.push(pair);
110
- continue;
111
- }
112
- const name = pair.slice(0, eqIdx).trim();
113
- const value = pair.slice(eqIdx + 1);
114
- if (SESSION_TOKEN_FAMILY_RE.test(name)) {
115
- if (!refreshed.has(name) || refreshed.get(name) !== value) mutated = true;
116
- droppedStale = true;
117
- continue;
118
- }
119
- result.push(`${name}=${value}`);
120
- }
121
- for (const [name, value] of refreshed) {
122
- result.push(`${name}=${value}`);
123
- }
124
- if (!droppedStale) mutated = true;
125
- return mutated ? result.join("; ") : null;
126
- }
127
- function buildSessionCookieHeader(rawInput) {
128
- let s = rawInput.trim();
129
- if (/^cookie\s*:\s*/i.test(s)) s = s.replace(/^cookie\s*:\s*/i, "");
130
- if (/__Secure-next-auth\.session-token(?:\.\d+)?\s*=/.test(s)) {
131
- return s;
132
- }
133
- return `__Secure-next-auth.session-token=${s}`;
134
- }
135
- async function exchangeSession(cookie, signal) {
136
- const cached = tokenLookup(cookie);
137
- if (cached) return cached;
138
- const headers = {
139
- ...browserHeaders(),
140
- Accept: "application/json",
141
- Cookie: buildSessionCookieHeader(cookie)
142
- };
143
- const response = await tlsFetchChatGpt(SESSION_URL, {
144
- method: "GET",
145
- headers,
146
- timeoutMs: 3e4,
147
- signal
148
- });
149
- if (response.status === 401 || response.status === 403) {
150
- throw new SessionAuthError("Invalid session cookie");
151
- }
152
- if (response.status >= 400) {
153
- throw new Error(`Session exchange failed (HTTP ${response.status})`);
154
- }
155
- const refreshed = mergeRefreshedCookie(cookie, response.headers.get("set-cookie"));
156
- let data = {};
157
- try {
158
- data = JSON.parse(response.text || "{}");
159
- } catch {
160
- console.warn("[chatgpt-web] session response JSON parse failed");
161
- }
162
- if (!data.accessToken) {
163
- throw new SessionAuthError("Session response missing accessToken \u2014 cookie likely expired");
164
- }
165
- const expiresAt = data.expires ? new Date(data.expires).getTime() : Date.now() + TOKEN_TTL_MS;
166
- const entry = {
167
- accessToken: data.accessToken,
168
- accountId: data.user?.id ?? null,
169
- expiresAt: Math.min(expiresAt, Date.now() + TOKEN_TTL_MS),
170
- refreshedCookie: refreshed ?? void 0
171
- };
172
- tokenStore(cookie, entry);
173
- return entry;
174
- }
175
- class SessionAuthError extends Error {
176
- constructor(message) {
177
- super(message);
178
- this.name = "SessionAuthError";
179
- }
180
- }
181
- const warmupCache = /* @__PURE__ */ new Map();
182
- const WARMUP_TTL_MS = 6e4;
183
- const WARMUP_CACHE_MAX = 200;
184
- async function runSessionWarmup(accessToken, accountId, sessionId, deviceId, cookie, signal, log) {
185
- const key = cookieKey(cookie) + ":" + accessToken.slice(-8);
186
- const now = Date.now();
187
- const last = warmupCache.get(key);
188
- if (last && now - last < WARMUP_TTL_MS) return;
189
- if (warmupCache.size >= WARMUP_CACHE_MAX && !warmupCache.has(key)) {
190
- const first = warmupCache.keys().next().value;
191
- if (first) warmupCache.delete(first);
192
- }
193
- warmupCache.set(key, now);
194
- const headers = {
195
- ...browserHeaders(),
196
- ...oaiHeaders(sessionId, deviceId),
197
- Accept: "*/*",
198
- Authorization: `Bearer ${accessToken}`,
199
- Cookie: buildSessionCookieHeader(cookie),
200
- Priority: "u=1, i"
201
- };
202
- if (accountId) headers["chatgpt-account-id"] = accountId;
203
- const urls = [
204
- `${CHATGPT_BASE}/backend-api/me`,
205
- `${CHATGPT_BASE}/backend-api/conversations?offset=0&limit=28&order=updated`,
206
- `${CHATGPT_BASE}/backend-api/models?history_and_training_disabled=false`
207
- ];
208
- for (const url of urls) {
209
- try {
210
- const r = await tlsFetchChatGpt(url, {
211
- method: "GET",
212
- headers,
213
- timeoutMs: 15e3,
214
- signal
215
- });
216
- log?.debug?.("CGPT-WEB", `warmup ${url.split("/backend-api/")[1]} \u2192 ${r.status}`);
217
- } catch (err) {
218
- log?.debug?.(
219
- "CGPT-WEB",
220
- `warmup ${url} failed: ${err instanceof Error ? err.message : String(err)}`
221
- );
222
- }
223
- }
224
- }
225
- const thinkingEffortCache = /* @__PURE__ */ new Map();
226
- const THINKING_EFFORT_TTL_MS = 5 * 60 * 1e3;
227
- const THINKING_EFFORT_CACHE_MAX = 400;
228
- function configuredProPollTimeoutMs() {
229
- const raw = Number(process.env.OMNIROUTE_CGPT_WEB_PRO_TIMEOUT_MS);
230
- if (!Number.isFinite(raw) || raw <= 0) return DEFAULT_PRO_POLL_TIMEOUT_MS;
231
- return Math.floor(raw);
232
- }
233
- function configuredProPollIntervalMs() {
234
- const raw = Number(process.env.OMNIROUTE_CGPT_WEB_PRO_POLL_INTERVAL_MS);
235
- if (!Number.isFinite(raw) || raw <= 0) return DEFAULT_PRO_POLL_INTERVAL_MS;
236
- return Math.floor(raw);
237
- }
238
- async function setUserThinkingEffort(modelSlug, effort, accessToken, accountId, sessionId, deviceId, cookie, signal, log) {
239
- const cacheKey = `${cookieKey(cookie)}:${modelSlug}:${effort}`;
240
- const now = Date.now();
241
- const last = thinkingEffortCache.get(cacheKey);
242
- if (last && now - last < THINKING_EFFORT_TTL_MS) {
243
- log?.debug?.("CGPT-WEB", `thinking_effort cached (${modelSlug}=${effort}) \u2014 skip PATCH`);
244
- return;
245
- }
246
- if (thinkingEffortCache.size >= THINKING_EFFORT_CACHE_MAX && !thinkingEffortCache.has(cacheKey)) {
247
- const first = thinkingEffortCache.keys().next().value;
248
- if (first) thinkingEffortCache.delete(first);
249
- }
250
- const url = `${USER_LAST_USED_MODEL_CONFIG_URL}?model_slug=${encodeURIComponent(modelSlug)}&thinking_effort=${encodeURIComponent(effort)}`;
251
- const headers = {
252
- ...browserHeaders(),
253
- ...oaiHeaders(sessionId, deviceId),
254
- Accept: "application/json",
255
- Authorization: `Bearer ${accessToken}`,
256
- Cookie: buildSessionCookieHeader(cookie),
257
- Priority: "u=4"
258
- };
259
- if (accountId) headers["chatgpt-account-id"] = accountId;
260
- try {
261
- const r = await tlsFetchChatGpt(url, {
262
- method: "PATCH",
263
- headers,
264
- timeoutMs: 15e3,
265
- signal
266
- });
267
- if (r.status >= 400) {
268
- log?.warn?.(
269
- "CGPT-WEB",
270
- `thinking_effort PATCH ${r.status} for ${modelSlug}=${effort} (continuing)`
271
- );
272
- return;
273
- }
274
- thinkingEffortCache.set(cacheKey, now);
275
- log?.debug?.("CGPT-WEB", `thinking_effort PATCH OK (${modelSlug}=${effort})`);
276
- } catch (err) {
277
- log?.warn?.(
278
- "CGPT-WEB",
279
- `thinking_effort PATCH failed: ${err instanceof Error ? err.message : String(err)}`
280
- );
281
- }
282
- }
283
- async function prepareChatRequirements(accessToken, accountId, sessionId, deviceId, cookie, dplInfo, signal, log) {
284
- const config = buildPrekeyConfig(CHATGPT_USER_AGENT, dplInfo.dpl, dplInfo.scriptSrc);
285
- const prekey = await buildPrepareToken(config, log);
286
- const headers = {
287
- ...browserHeaders(),
288
- ...oaiHeaders(sessionId, deviceId),
289
- "Content-Type": "application/json",
290
- Authorization: `Bearer ${accessToken}`,
291
- Cookie: buildSessionCookieHeader(cookie),
292
- Priority: "u=1, i"
293
- };
294
- if (accountId) headers["chatgpt-account-id"] = accountId;
295
- const prepResp = await tlsFetchChatGpt(SENTINEL_PREPARE_URL, {
296
- method: "POST",
297
- headers,
298
- body: JSON.stringify({ p: prekey }),
299
- timeoutMs: 3e4,
300
- signal
301
- });
302
- if (prepResp.status === 401 || prepResp.status === 403) {
303
- throw new SentinelBlockedError(`Sentinel /prepare blocked (HTTP ${prepResp.status})`);
304
- }
305
- if (prepResp.status >= 400) {
306
- throw new Error(`Sentinel /prepare failed (HTTP ${prepResp.status})`);
307
- }
308
- let prepData = {};
309
- try {
310
- prepData = JSON.parse(prepResp.text || "{}");
311
- } catch {
312
- console.warn("[chatgpt-web] chat requirements prep JSON parse failed");
313
- }
314
- if (!prepData.prepare_token) {
315
- return prepData;
316
- }
317
- const crBody = { p: prekey, prepare_token: prepData.prepare_token };
318
- const crResp = await tlsFetchChatGpt(SENTINEL_CR_URL, {
319
- method: "POST",
320
- headers,
321
- body: JSON.stringify(crBody),
322
- timeoutMs: 3e4,
323
- signal
324
- });
325
- if (crResp.status === 401 || crResp.status === 403) {
326
- throw new SentinelBlockedError(`Sentinel /chat-requirements blocked (HTTP ${crResp.status})`);
327
- }
328
- if (crResp.status >= 400) {
329
- return prepData;
330
- }
331
- try {
332
- const crData = JSON.parse(crResp.text || "{}");
333
- return { ...crData, prepare_token: prepData.prepare_token };
334
- } catch {
335
- console.warn("[chatgpt-web] chat requirements response JSON parse failed");
336
- return prepData;
337
- }
338
- }
339
- class SentinelBlockedError extends Error {
340
- constructor(message) {
341
- super(message);
342
- this.name = "SentinelBlockedError";
343
- }
344
- }
345
- let dplCache = null;
346
- const DPL_TTL_MS = 60 * 60 * 1e3;
347
- async function fetchDpl(cookie, signal) {
348
- if (dplCache && Date.now() < dplCache.expiresAt) {
349
- return { dpl: dplCache.dpl, scriptSrc: dplCache.scriptSrc };
350
- }
351
- const headers = {
352
- ...browserHeaders(),
353
- Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
354
- Cookie: buildSessionCookieHeader(cookie)
355
- };
356
- const response = await tlsFetchChatGpt(`${CHATGPT_BASE}/`, {
357
- method: "GET",
358
- headers,
359
- timeoutMs: 2e4,
360
- signal
361
- });
362
- const html = response.text || "";
363
- const dplMatch = html.match(/data-build="([^"]+)"/);
364
- const dpl = dplMatch ? `dpl=${dplMatch[1]}` : `dpl=${OAI_CLIENT_VERSION.replace(/^prod-/, "")}`;
365
- const scriptMatch = html.match(/<script[^>]+src="(https?:\/\/[^"]*\.js[^"]*)"/);
366
- const scriptSrc = scriptMatch?.[1] ?? `${CHATGPT_BASE}/_next/static/chunks/webpack-${randomHex(16)}.js`;
367
- dplCache = { dpl, scriptSrc, expiresAt: Date.now() + DPL_TTL_MS };
368
- return { dpl, scriptSrc };
369
- }
370
- function randomHex(n) {
371
- return randomBytes(Math.ceil(n / 2)).toString("hex").slice(0, n);
372
- }
373
- const NAVIGATOR_KEYS = [
374
- "webdriver\u2212false",
375
- "geolocation",
376
- "languages",
377
- "language",
378
- "platform",
379
- "userAgent",
380
- "vendor",
381
- "hardwareConcurrency",
382
- "deviceMemory",
383
- "permissions",
384
- "plugins",
385
- "mediaDevices"
386
- ];
387
- const DOCUMENT_KEYS = [
388
- "_reactListeningkfj3eavmks",
389
- "_reactListeningo743lnnpvdg",
390
- "location",
391
- "scrollingElement",
392
- "documentElement"
393
- ];
394
- const WINDOW_KEYS = [
395
- "webpackChunk_N_E",
396
- "__NEXT_DATA__",
397
- "chrome",
398
- "history",
399
- "screen",
400
- "navigation",
401
- "scrollX",
402
- "scrollY"
403
- ];
404
- function pick(arr) {
405
- return arr[Math.floor(Math.random() * arr.length)];
406
- }
407
- function buildPrekeyConfig(userAgent, dpl, scriptSrc) {
408
- const screenSizes = [3e3, 4e3, 3120, 4160];
409
- const cores = [8, 16, 24, 32];
410
- const dateStr = (/* @__PURE__ */ new Date()).toString();
411
- const perfNow = performance.now();
412
- const epochOffset = Date.now() - perfNow;
413
- return [
414
- pick(screenSizes),
415
- dateStr,
416
- 4294705152,
417
- 0,
418
- // mutated by solver
419
- userAgent,
420
- scriptSrc,
421
- dpl,
422
- "en-US",
423
- "en-US,en",
424
- 0,
425
- // mutated by solver
426
- pick(NAVIGATOR_KEYS),
427
- pick(DOCUMENT_KEYS),
428
- pick(WINDOW_KEYS),
429
- perfNow,
430
- randomUUID(),
431
- "",
432
- pick(cores),
433
- epochOffset
434
- ];
435
- }
436
- const POW_YIELD_EVERY = 1e3;
437
- function yieldToEventLoop() {
438
- return new Promise((resolve) => setImmediate(resolve));
439
- }
440
- async function solvePow(opts) {
441
- const cfg = [...opts.config];
442
- for (let i = 0; i < opts.maxIter; i++) {
443
- if (i > 0 && i % POW_YIELD_EVERY === 0) await yieldToEventLoop();
444
- cfg[3] = i;
445
- const json = JSON.stringify(cfg);
446
- const b642 = Buffer.from(json).toString("base64");
447
- const hash = sha3_512Hex(opts.seed + b642);
448
- if (opts.target && hash.slice(0, opts.target.length) <= opts.target) {
449
- return `${opts.prefix}${b642}`;
450
- }
451
- }
452
- opts.log?.warn?.(
453
- "CGPT-WEB",
454
- `PoW (${opts.label}) exhausted ${opts.maxIter} iterations against target=${opts.target || "<empty>"}; submitting unsolved token (Sentinel may reject)`
455
- );
456
- const b64 = Buffer.from(JSON.stringify(cfg)).toString("base64");
457
- return `${opts.prefix}${b64}`;
458
- }
459
- async function buildPrepareToken(config, log) {
460
- return solvePow({
461
- config,
462
- seed: "",
463
- target: "0fffff",
464
- prefix: "gAAAAAC",
465
- maxIter: 1e5,
466
- label: "prepare",
467
- log
468
- });
469
- }
470
- async function solveProofOfWork(seed, difficulty, config, log) {
471
- return solvePow({
472
- config,
473
- seed,
474
- target: (difficulty || "").toLowerCase(),
475
- prefix: "gAAAAAB",
476
- maxIter: 5e5,
477
- label: "conversation",
478
- log
479
- });
480
- }
481
- const DATA_URI_IMAGE_RE = /!\[([^\]]*)\]\(data:image\/[^)]+\)/g;
482
- const CACHED_IMAGE_URL_RE = /\/v1\/chatgpt-web\/image\/([a-f0-9]{16,64})(?=[)\s"'<>]|$)/gi;
483
- function stripInlinedImages(content) {
484
- return content.replace(
485
- DATA_URI_IMAGE_RE,
486
- (_, alt) => alt ? `[${alt}: generated image]` : "[generated image]"
487
- );
488
- }
489
- function findCachedImageContext(content) {
490
- let latest = null;
491
- for (const match of content.matchAll(CACHED_IMAGE_URL_RE)) {
492
- const id = match[1];
493
- const context = getChatGptImageConversationContext(id);
494
- if (context) latest = context;
495
- }
496
- return latest;
497
- }
498
- function parseOpenAIMessages(messages) {
499
- let systemMsg = "";
500
- const history = [];
501
- let latestImageContext = null;
502
- for (const msg of messages) {
503
- let role = String(msg.role || "user");
504
- if (role === "developer") role = "system";
505
- let content = "";
506
- if (typeof msg.content === "string") {
507
- content = msg.content;
508
- } else if (Array.isArray(msg.content)) {
509
- content = msg.content.filter((c) => c.type === "text").map((c) => String(c.text || "")).join(" ");
510
- }
511
- content = stripInlinedImages(content);
512
- const imageContext = findCachedImageContext(content);
513
- if (imageContext) latestImageContext = imageContext;
514
- if (!content.trim()) continue;
515
- if (role === "system") {
516
- systemMsg += (systemMsg ? "\n" : "") + content;
517
- } else if (role === "user" || role === "assistant") {
518
- history.push({ role, content });
519
- }
520
- }
521
- let currentMsg = "";
522
- if (history.length > 0 && history[history.length - 1].role === "user") {
523
- currentMsg = history.pop().content;
524
- }
525
- return { systemMsg, history, currentMsg, latestImageContext };
526
- }
527
- const IMAGE_GEN_REGEXES = [
528
- // verb + (anything within 40 chars) + image-noun
529
- /\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,
530
- // image-noun + "of" — "image of a kitten", "picture of mountains"
531
- /\b(?:image|picture|photo|photograph|illustration|drawing|painting|render)\s+of\b/i,
532
- // direct verb + a/an article — "draw a kitten", "paint an apple"
533
- /\b(?:draw|paint|sketch|render|illustrate)\s+(?:me\s+)?(?:a|an|some|the)\s+\w+/i,
534
- // explicit slash command users sometimes type — "/imagine ..."
535
- /^\s*\/(?:image|imagine|img|draw|paint)\b/im
536
- ];
537
- const OPENWEBUI_TOOL_PROMPT_MARKERS = [
538
- /<chat_history>/i,
539
- /^### Task:/im,
540
- /\bJSON format:\s*\{/i,
541
- /\bfollow_?ups\b.*\barray of strings\b/i
542
- ];
543
- const OPENWEBUI_IMAGE_CONTEXT_MARKERS = [
544
- /<context>\s*The requested image has been (?:created|edited and created) by the system successfully/i,
545
- /<context>\s*The requested image has been edited and created and is now being shown to the user/i,
546
- /<context>\s*Image generation was attempted but failed/i
547
- ];
548
- function hasOpenWebUIImageContext(parsed) {
549
- return OPENWEBUI_IMAGE_CONTEXT_MARKERS.some((re) => re.test(parsed.systemMsg));
550
- }
551
- function looksLikeImageGenRequest(parsed) {
552
- const text = parsed.currentMsg.trim();
553
- if (!text) return false;
554
- if (OPENWEBUI_TOOL_PROMPT_MARKERS.some((re) => re.test(text))) return false;
555
- if (hasOpenWebUIImageContext(parsed)) return false;
556
- return IMAGE_GEN_REGEXES.some((re) => re.test(text));
557
- }
558
- const IMAGE_EDIT_REGEXES = [
559
- /\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,
560
- /\b(?:make|turn|set|switch)\s+(?:it|the\s+(?:image|picture|photo|scene))\b[\s\S]{0,120}\b/i,
561
- /\b(?:add|remove|replace)\b[\s\S]{0,120}\b(?:it|image|picture|photo|background|sky|person|object|text|logo)\b/i,
562
- /\b(?:brighter|darker|night|daytime|time of day|sunset|sunrise|morning|evening|lighting|relight|background|style)\b/i,
563
- /^\s*(?:now|then|also)\b[\s\S]{0,120}\b(?:make|turn|change|adjust|add|remove|replace|edit)\b/i
564
- ];
565
- function looksLikeImageEditRequest(parsed) {
566
- if (!parsed.latestImageContext) return false;
567
- const text = parsed.currentMsg.trim();
568
- if (!text) return false;
569
- if (OPENWEBUI_TOOL_PROMPT_MARKERS.some((re) => re.test(text))) return false;
570
- if (hasOpenWebUIImageContext(parsed)) return false;
571
- return IMAGE_EDIT_REGEXES.some((re) => re.test(text));
572
- }
573
- function buildConversationBody(parsed, modelSlug, parentMessageId, options) {
574
- const systemParts = [];
575
- if (parsed.systemMsg.trim()) {
576
- systemParts.push(parsed.systemMsg.trim());
577
- }
578
- const continuation = options.continuation ?? null;
579
- if (!continuation && parsed.history.length > 0) {
580
- const formatted = parsed.history.map((h) => `${h.role === "assistant" ? "Assistant" : "User"}: ${h.content}`).join("\n\n");
581
- systemParts.push(
582
- `Prior conversation (for context \u2014 answer only the new user message below):
583
-
584
- ${formatted}`
585
- );
586
- }
587
- const messages = [];
588
- if (systemParts.length > 0) {
589
- messages.push({
590
- id: randomUUID(),
591
- author: { role: "system" },
592
- content: { content_type: "text", parts: [systemParts.join("\n\n")] }
593
- });
594
- }
595
- const currentUserContent = hasOpenWebUIImageContext(parsed) ? "Briefly acknowledge the image result described in the system context. Do not generate, edit, or request another image." : parsed.currentMsg || "";
596
- messages.push({
597
- id: randomUUID(),
598
- author: { role: "user" },
599
- content: { content_type: "text", parts: [currentUserContent] }
600
- });
601
- return {
602
- action: "next",
603
- messages,
604
- model: modelSlug,
605
- // Text-only API-style requests start fresh because clients replay full
606
- // history. Generated-image edits are the exception: ChatGPT needs the
607
- // original conversation node to adjust the actual image, not just a
608
- // markdown URL echoed back in a synthetic history block.
609
- conversation_id: continuation?.conversationId ?? null,
610
- parent_message_id: continuation?.parentMessageId ?? parentMessageId,
611
- timezone_offset_min: -(/* @__PURE__ */ new Date()).getTimezoneOffset(),
612
- // Temporary Chat is the default. Disable it only for image generation /
613
- // image edits, where ChatGPT needs durable conversation state for tools.
614
- history_and_training_disabled: !options.persistConversation,
615
- suggestions: [],
616
- websocket_request_id: randomUUID(),
617
- conversation_mode: { kind: "primary_assistant" },
618
- supports_buffering: true,
619
- force_parallel_switch: "auto",
620
- paragen_cot_summary_display_override: "allow",
621
- ...options.thinkingEffort ? { thinking_effort: options.thinkingEffort } : {}
622
- };
623
- }
624
- async function* readChatGptSseEvents(body, signal) {
625
- const reader = body.getReader();
626
- const decoder = new TextDecoder();
627
- let buffer = "";
628
- let dataLines = [];
629
- let eventName = null;
630
- function flush() {
631
- if (dataLines.length === 0) {
632
- eventName = null;
633
- return null;
634
- }
635
- const payload = dataLines.join("\n");
636
- dataLines = [];
637
- const sseEventName = eventName;
638
- eventName = null;
639
- const trimmed = payload.trim();
640
- if (!trimmed || trimmed === "[DONE]") return "done";
641
- try {
642
- const parsed = JSON.parse(trimmed);
643
- if (sseEventName && !parsed.type) parsed.type = sseEventName;
644
- return parsed;
645
- } catch {
646
- console.warn("[chatgpt-web] stream event JSON parse failed");
647
- return null;
648
- }
649
- }
650
- try {
651
- while (true) {
652
- if (signal?.aborted) return;
653
- const { value, done } = await reader.read();
654
- if (done) break;
655
- buffer += decoder.decode(value, { stream: true });
656
- while (true) {
657
- const idx = buffer.indexOf("\n");
658
- if (idx < 0) break;
659
- const rawLine = buffer.slice(0, idx);
660
- buffer = buffer.slice(idx + 1);
661
- const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
662
- if (line === "") {
663
- const parsed = flush();
664
- if (parsed === "done") return;
665
- if (parsed) yield parsed;
666
- continue;
667
- }
668
- if (line.startsWith("event:")) {
669
- eventName = line.slice(6).trim();
670
- } else if (line.startsWith("data:")) {
671
- dataLines.push(line.slice(5).trimStart());
672
- }
673
- }
674
- }
675
- buffer += decoder.decode();
676
- if (buffer.trim().startsWith("data:")) {
677
- dataLines.push(buffer.trim().slice(5).trimStart());
678
- }
679
- const tail = flush();
680
- if (tail && tail !== "done") yield tail;
681
- } finally {
682
- reader.releaseLock();
683
- }
684
- }
685
- function extractImagePointers(parts) {
686
- const out = [];
687
- for (const p of parts) {
688
- if (!p || typeof p !== "object") continue;
689
- const obj = p;
690
- if (obj.content_type === "image_asset_pointer" && typeof obj.asset_pointer === "string") {
691
- out.push(obj.asset_pointer);
692
- }
693
- }
694
- return out;
695
- }
696
- async function* extractContent(eventStream, signal) {
697
- let conversationId = null;
698
- let currentId = null;
699
- let currentParts = "";
700
- let emittedLen = 0;
701
- let isLive = false;
702
- const imagePointers = /* @__PURE__ */ new Map();
703
- let imageGenAsync = false;
704
- let handoff = false;
705
- for await (const event of readChatGptSseEvents(eventStream, signal)) {
706
- if (event.error) {
707
- const msg = typeof event.error === "string" ? event.error : event.error.message || "ChatGPT stream error";
708
- yield { error: msg, done: true };
709
- return;
710
- }
711
- if (event.conversation_id) conversationId = event.conversation_id;
712
- if (event.type === "stream_handoff") {
713
- handoff = true;
714
- yield {
715
- conversationId: conversationId ?? void 0,
716
- handoff: true
717
- };
718
- continue;
719
- }
720
- if (event.type === "server_ste_metadata") {
721
- const meta = event.metadata;
722
- if (meta && meta.turn_use_case === "image gen") {
723
- imageGenAsync = true;
724
- }
725
- }
726
- const m = event.message;
727
- if (!m) continue;
728
- if (m.metadata && typeof m.metadata.image_gen_task_id === "string") {
729
- imageGenAsync = true;
730
- }
731
- if (m.author?.role !== "assistant") continue;
732
- const id = m.id ?? null;
733
- const status = m.status ?? "";
734
- if (id && id !== currentId) {
735
- currentId = id;
736
- currentParts = "";
737
- emittedLen = 0;
738
- isLive = false;
739
- }
740
- if (status === "in_progress") {
741
- isLive = true;
742
- }
743
- const parts = m.content?.parts ?? [];
744
- if (parts.length === 0) continue;
745
- if (status === "finished_successfully" || status === "" || isLive) {
746
- for (const ptr of extractImagePointers(parts)) {
747
- const existing = imagePointers.get(ptr);
748
- imagePointers.set(
749
- ptr,
750
- existing?.messageId ? existing : { pointer: ptr, ...id ? { messageId: id } : {} }
751
- );
752
- }
753
- }
754
- const cumulative = parts.map((p) => typeof p === "string" ? p : "").join("");
755
- if (cumulative.length > currentParts.length) {
756
- currentParts = cumulative;
757
- }
758
- if (isLive && currentParts.length > emittedLen) {
759
- const delta = currentParts.slice(emittedLen);
760
- emittedLen = currentParts.length;
761
- yield {
762
- delta,
763
- answer: currentParts,
764
- conversationId: conversationId ?? void 0,
765
- messageId: currentId ?? void 0
766
- };
767
- }
768
- }
769
- if (!isLive && currentParts.length > emittedLen) {
770
- yield {
771
- delta: currentParts.slice(emittedLen),
772
- answer: currentParts,
773
- conversationId: conversationId ?? void 0,
774
- messageId: currentId ?? void 0
775
- };
776
- }
777
- yield {
778
- delta: "",
779
- answer: currentParts,
780
- conversationId: conversationId ?? void 0,
781
- messageId: currentId ?? void 0,
782
- imagePointers: imagePointers.size > 0 ? Array.from(imagePointers.values()) : void 0,
783
- imageGenAsync,
784
- handoff,
785
- done: true
786
- };
787
- }
788
- function textFromContentPart(part) {
789
- if (typeof part === "string") return part;
790
- if (!part || typeof part !== "object") return "";
791
- const obj = part;
792
- for (const key of ["text", "content", "summary"]) {
793
- const value = obj[key];
794
- if (typeof value === "string") return value;
795
- }
796
- return "";
797
- }
798
- function detailMessageText(message) {
799
- const content = message.content;
800
- if (!content) return "";
801
- if (typeof content.text === "string") return content.text;
802
- const parts = content.parts ?? [];
803
- return parts.map(textFromContentPart).join("");
804
- }
805
- function extractFinalAssistantAnswer(detail) {
806
- const nodes = Object.values(detail.mapping ?? {});
807
- let best = null;
808
- for (const node of nodes) {
809
- const message = node.message;
810
- if (!message || message.author?.role !== "assistant") continue;
811
- if (message.metadata?.is_visually_hidden === true) continue;
812
- const contentType = message.content?.content_type ?? "";
813
- if (contentType.includes("thought") || contentType.includes("reasoning")) continue;
814
- const text = detailMessageText(message).trim();
815
- if (!text) continue;
816
- const finished = message.status === "finished_successfully" && message.end_turn !== false;
817
- const sort = message.update_time ?? message.create_time ?? 0;
818
- if (!best || finished && (!best.finished || sort >= best.sort) || !finished && !best.finished && sort >= best.sort) {
819
- best = { text, messageId: message.id, finished, sort };
820
- }
821
- }
822
- if (!best) return null;
823
- return { text: best.text, messageId: best.messageId, finished: best.finished };
824
- }
825
- function delayWithAbort(ms, signal) {
826
- if (ms <= 0) return Promise.resolve();
827
- if (signal?.aborted) return Promise.resolve();
828
- return new Promise((resolve) => {
829
- const timer = setTimeout(() => {
830
- signal?.removeEventListener("abort", onAbort);
831
- resolve();
832
- }, ms);
833
- const onAbort = () => {
834
- clearTimeout(timer);
835
- signal?.removeEventListener("abort", onAbort);
836
- resolve();
837
- };
838
- signal?.addEventListener("abort", onAbort, { once: true });
839
- });
840
- }
841
- function decodeUtf8DataUrl(text) {
842
- const marker = ";base64,";
843
- if (!text.startsWith("data:") || !text.includes(marker)) return text;
844
- const base64 = text.slice(text.indexOf(marker) + marker.length);
845
- return new TextDecoder().decode(Buffer.from(base64, "base64"));
846
- }
847
- async function fetchConversationDetail(conversationId, ctx) {
848
- const url = `${CHATGPT_BASE}/backend-api/conversation/${encodeURIComponent(conversationId)}`;
849
- const headers = {
850
- ...browserHeaders(),
851
- ...oaiHeaders(ctx.sessionId, ctx.deviceId),
852
- Accept: "application/json",
853
- Authorization: `Bearer ${ctx.accessToken}`,
854
- Cookie: buildSessionCookieHeader(ctx.cookie)
855
- };
856
- if (ctx.accountId) headers["chatgpt-account-id"] = ctx.accountId;
857
- try {
858
- const response = await tlsFetchChatGpt(url, {
859
- method: "GET",
860
- headers,
861
- timeoutMs: 3e4,
862
- signal: ctx.signal,
863
- // The native tls-client text path can surface UTF-8 JSON as mojibake
864
- // (e.g. 👉 becomes 👉). Ask for raw bytes and decode as UTF-8 here so
865
- // the final answer appended after Pro stream_handoff preserves Unicode.
866
- byteResponse: true
867
- });
868
- if (response.status >= 400) {
869
- ctx.log?.warn?.(
870
- "CGPT-WEB",
871
- `conversation poll ${response.status}: ${(response.text || "").slice(0, 300)}`
872
- );
873
- return { detail: null, terminal: [401, 403, 404].includes(response.status) };
874
- }
875
- if (!response.text) return { detail: null, terminal: false };
876
- return {
877
- detail: JSON.parse(decodeUtf8DataUrl(response.text)),
878
- terminal: false
879
- };
880
- } catch (err) {
881
- ctx.log?.warn?.(
882
- "CGPT-WEB",
883
- `conversation poll failed: ${err instanceof Error ? err.message : String(err)}`
884
- );
885
- return { detail: null, terminal: false };
886
- }
887
- }
888
- async function pollForFinalAssistantAnswer(conversationId, ctx) {
889
- const started = Date.now();
890
- const timeoutMs = configuredProPollTimeoutMs();
891
- const intervalMs = configuredProPollIntervalMs();
892
- let last = null;
893
- let terminalPollFailure = false;
894
- while (!ctx.signal?.aborted && Date.now() - started < timeoutMs) {
895
- const { detail, terminal } = await fetchConversationDetail(conversationId, ctx);
896
- if (detail) {
897
- const answer = extractFinalAssistantAnswer(detail);
898
- if (answer) {
899
- last = answer;
900
- if (answer.finished) return answer;
901
- }
902
- }
903
- if (terminal) {
904
- terminalPollFailure = true;
905
- break;
906
- }
907
- const remaining = timeoutMs - (Date.now() - started);
908
- if (remaining <= 0) break;
909
- await delayWithAbort(Math.min(intervalMs, remaining), ctx.signal);
910
- }
911
- if (last) {
912
- ctx.log?.warn?.(
913
- "CGPT-WEB",
914
- terminalPollFailure ? `conversation poll stopped before finished_successfully; returning latest assistant text for ${conversationId}` : `conversation poll timed out before finished_successfully; returning latest assistant text for ${conversationId}`
915
- );
916
- } else {
917
- ctx.log?.warn?.(
918
- "CGPT-WEB",
919
- terminalPollFailure ? `conversation poll stopped without assistant text for ${conversationId}` : `conversation poll timed out without assistant text for ${conversationId}`
920
- );
921
- }
922
- return last;
923
- }
924
- function sseChunk(data) {
925
- return `data: ${JSON.stringify(data)}
926
-
927
- `;
928
- }
929
- function detectImageResolutionFailure(pointerCount, resolvedCount) {
930
- return pointerCount > 0 && resolvedCount === 0;
931
- }
932
- function imageMarkdown(urls) {
933
- if (urls.length === 0) return "";
934
- return "\n\n" + urls.map((u) => `![image](${u})`).join("\n\n");
935
- }
936
- async function resolveImagePointers(pointers, conversationId, resolver, log, fallbackParentMessageId) {
937
- if (!pointers || pointers.length === 0 || !resolver) return [];
938
- const urls = [];
939
- for (const ref of pointers) {
940
- try {
941
- const url = await resolver(
942
- ref.pointer,
943
- conversationId,
944
- ref.messageId ?? fallbackParentMessageId
945
- );
946
- if (url) urls.push(url);
947
- } catch (err) {
948
- log?.warn?.(
949
- "CGPT-WEB",
950
- `Image resolve failed (${ref.pointer}): ${err instanceof Error ? err.message : String(err)}`
951
- );
952
- }
953
- }
954
- return urls;
955
- }
956
- function buildStreamingResponse(eventStream, model, cid, created, resolver, pollAsyncImage, pollFinalAnswer, log, signal) {
957
- const encoder = new TextEncoder();
958
- return new ReadableStream(
959
- {
960
- async start(controller) {
961
- try {
962
- controller.enqueue(
963
- encoder.encode(
964
- sseChunk({
965
- id: cid,
966
- object: "chat.completion.chunk",
967
- created,
968
- model,
969
- system_fingerprint: null,
970
- choices: [
971
- { index: 0, delta: { role: "assistant" }, finish_reason: null, logprobs: null }
972
- ]
973
- })
974
- )
975
- );
976
- let conversationId = null;
977
- let imagePointers;
978
- let imageGenAsync = false;
979
- let handoff = false;
980
- let emittedText = "";
981
- let polledFinalAnswer = "";
982
- let parentCandidateMessageId = null;
983
- const emitTextDelta = (content) => {
984
- const cleaned = cleanChatGptText(content);
985
- if (!cleaned) return;
986
- emittedText += cleaned;
987
- controller.enqueue(
988
- encoder.encode(
989
- sseChunk({
990
- id: cid,
991
- object: "chat.completion.chunk",
992
- created,
993
- model,
994
- system_fingerprint: null,
995
- choices: [
996
- {
997
- index: 0,
998
- delta: { content: cleaned },
999
- finish_reason: null,
1000
- logprobs: null
1001
- }
1002
- ]
1003
- })
1004
- )
1005
- );
1006
- };
1007
- const appendFinalAnswer = (text) => {
1008
- const cleaned = cleanChatGptText(text);
1009
- const finalTrimmed = cleaned.trim();
1010
- if (!finalTrimmed) return;
1011
- const emittedTrimmed = emittedText.trim();
1012
- if (emittedTrimmed === finalTrimmed || emittedTrimmed.endsWith(finalTrimmed)) return;
1013
- const prefix = emittedTrimmed && !emittedText.endsWith("\n") ? "\n\n" : "";
1014
- emitTextDelta(`${prefix}${cleaned}`);
1015
- };
1016
- const startHeartbeat = (intervalMs = 5e3) => {
1017
- const heartbeatChunk = sseChunk({
1018
- id: cid,
1019
- object: "chat.completion.chunk",
1020
- created,
1021
- model,
1022
- system_fingerprint: null,
1023
- choices: [{ index: 0, delta: { content: "\u200B" }, finish_reason: null, logprobs: null }]
1024
- });
1025
- const timer = setInterval(() => {
1026
- try {
1027
- controller.enqueue(encoder.encode(heartbeatChunk));
1028
- } catch {
1029
- console.warn("[chatgpt-web] heartbeat enqueue failed - controller closed");
1030
- clearInterval(timer);
1031
- }
1032
- }, intervalMs);
1033
- return () => clearInterval(timer);
1034
- };
1035
- for await (const chunk of extractContent(eventStream, signal)) {
1036
- if (chunk.conversationId) conversationId = chunk.conversationId;
1037
- if (chunk.messageId) parentCandidateMessageId = chunk.messageId;
1038
- if (chunk.handoff) handoff = true;
1039
- if (chunk.error) {
1040
- controller.enqueue(
1041
- encoder.encode(
1042
- sseChunk({
1043
- id: cid,
1044
- object: "chat.completion.chunk",
1045
- created,
1046
- model,
1047
- system_fingerprint: null,
1048
- choices: [
1049
- {
1050
- index: 0,
1051
- delta: { content: `[Error: ${chunk.error}]` },
1052
- finish_reason: null,
1053
- logprobs: null
1054
- }
1055
- ]
1056
- })
1057
- )
1058
- );
1059
- break;
1060
- }
1061
- if (chunk.done) {
1062
- imagePointers = chunk.imagePointers;
1063
- imageGenAsync = chunk.imageGenAsync ?? false;
1064
- handoff = handoff || (chunk.handoff ?? false);
1065
- if (chunk.messageId) parentCandidateMessageId = chunk.messageId;
1066
- break;
1067
- }
1068
- if (chunk.delta) {
1069
- emitTextDelta(chunk.delta);
1070
- }
1071
- }
1072
- if (pollFinalAnswer && conversationId && handoff) {
1073
- const stopHb = startHeartbeat();
1074
- try {
1075
- const polled = await pollFinalAnswer(conversationId);
1076
- if (polled?.text) {
1077
- polledFinalAnswer = polled.text;
1078
- if (polled.messageId) parentCandidateMessageId = polled.messageId;
1079
- }
1080
- } finally {
1081
- stopHb();
1082
- }
1083
- }
1084
- if (polledFinalAnswer) {
1085
- appendFinalAnswer(polledFinalAnswer);
1086
- }
1087
- if (imageGenAsync && conversationId && (!imagePointers || imagePointers.length === 0) && pollAsyncImage) {
1088
- controller.enqueue(
1089
- encoder.encode(
1090
- sseChunk({
1091
- id: cid,
1092
- object: "chat.completion.chunk",
1093
- created,
1094
- model,
1095
- system_fingerprint: null,
1096
- choices: [
1097
- {
1098
- index: 0,
1099
- delta: { content: "_Generating image\u2026_\n\n" },
1100
- finish_reason: null,
1101
- logprobs: null
1102
- }
1103
- ]
1104
- })
1105
- )
1106
- );
1107
- const stopHb = startHeartbeat();
1108
- try {
1109
- const polled = await pollAsyncImage(conversationId);
1110
- if (polled.length > 0) imagePointers = polled;
1111
- } catch (err) {
1112
- log?.warn?.(
1113
- "CGPT-WEB",
1114
- `Async image poll failed: ${err instanceof Error ? err.message : String(err)}`
1115
- );
1116
- } finally {
1117
- stopHb();
1118
- }
1119
- }
1120
- const stopHb2 = startHeartbeat();
1121
- let urls = [];
1122
- try {
1123
- urls = await resolveImagePointers(
1124
- imagePointers,
1125
- conversationId,
1126
- resolver,
1127
- log,
1128
- parentCandidateMessageId
1129
- );
1130
- } finally {
1131
- stopHb2();
1132
- }
1133
- if (signal?.aborted) return;
1134
- const mdBlock = imageMarkdown(urls);
1135
- const safeEnqueue = (bytes) => {
1136
- try {
1137
- controller.enqueue(bytes);
1138
- return true;
1139
- } catch {
1140
- console.warn("[chatgpt-web] controller enqueue failed");
1141
- return false;
1142
- }
1143
- };
1144
- if (mdBlock) {
1145
- if (!safeEnqueue(
1146
- encoder.encode(
1147
- sseChunk({
1148
- id: cid,
1149
- object: "chat.completion.chunk",
1150
- created,
1151
- model,
1152
- system_fingerprint: null,
1153
- choices: [
1154
- {
1155
- index: 0,
1156
- delta: { content: mdBlock },
1157
- finish_reason: null,
1158
- logprobs: null
1159
- }
1160
- ]
1161
- })
1162
- )
1163
- ))
1164
- return;
1165
- }
1166
- if (!safeEnqueue(
1167
- encoder.encode(
1168
- sseChunk({
1169
- id: cid,
1170
- object: "chat.completion.chunk",
1171
- created,
1172
- model,
1173
- system_fingerprint: null,
1174
- choices: [{ index: 0, delta: {}, finish_reason: "stop", logprobs: null }]
1175
- })
1176
- )
1177
- ))
1178
- return;
1179
- safeEnqueue(encoder.encode("data: [DONE]\n\n"));
1180
- } catch (err) {
1181
- controller.enqueue(
1182
- encoder.encode(
1183
- sseChunk({
1184
- id: cid,
1185
- object: "chat.completion.chunk",
1186
- created,
1187
- model,
1188
- system_fingerprint: null,
1189
- choices: [
1190
- {
1191
- index: 0,
1192
- delta: {
1193
- content: `[Stream error: ${err instanceof Error ? err.message : String(err)}]`
1194
- },
1195
- finish_reason: "stop",
1196
- logprobs: null
1197
- }
1198
- ]
1199
- })
1200
- )
1201
- );
1202
- controller.enqueue(encoder.encode("data: [DONE]\n\n"));
1203
- } finally {
1204
- try {
1205
- controller.close();
1206
- } catch {
1207
- }
1208
- }
1209
- }
1210
- },
1211
- { highWaterMark: 16384 }
1212
- );
1213
- }
1214
- async function buildNonStreamingResponse(eventStream, model, cid, created, currentMsg, resolver, pollAsyncImage, pollFinalAnswer, log, signal) {
1215
- let fullAnswer = "";
1216
- let conversationId = null;
1217
- let imagePointers;
1218
- let imageGenAsync = false;
1219
- let handoff = false;
1220
- let parentCandidateMessageId = null;
1221
- for await (const chunk of extractContent(eventStream, signal)) {
1222
- if (chunk.conversationId) conversationId = chunk.conversationId;
1223
- if (chunk.messageId) parentCandidateMessageId = chunk.messageId;
1224
- if (chunk.handoff) handoff = true;
1225
- if (chunk.error) {
1226
- return new Response(
1227
- JSON.stringify({
1228
- error: { message: chunk.error, type: "upstream_error", code: "CHATGPT_ERROR" }
1229
- }),
1230
- { status: 502, headers: { "Content-Type": "application/json" } }
1231
- );
1232
- }
1233
- if (chunk.done) {
1234
- fullAnswer = chunk.answer || fullAnswer;
1235
- imagePointers = chunk.imagePointers;
1236
- imageGenAsync = chunk.imageGenAsync ?? false;
1237
- handoff = handoff || (chunk.handoff ?? false);
1238
- if (chunk.messageId) parentCandidateMessageId = chunk.messageId;
1239
- break;
1240
- }
1241
- if (chunk.answer) fullAnswer = chunk.answer;
1242
- }
1243
- if (pollFinalAnswer && conversationId && (handoff || !fullAnswer.trim())) {
1244
- const polled = await pollFinalAnswer(conversationId);
1245
- if (polled?.text) {
1246
- fullAnswer = polled.text;
1247
- if (polled.messageId) parentCandidateMessageId = polled.messageId;
1248
- }
1249
- }
1250
- fullAnswer = cleanChatGptText(fullAnswer);
1251
- if (imageGenAsync && conversationId && (!imagePointers || imagePointers.length === 0) && pollAsyncImage) {
1252
- try {
1253
- const polled = await pollAsyncImage(conversationId);
1254
- if (polled.length > 0) imagePointers = polled;
1255
- } catch (err) {
1256
- log?.warn?.(
1257
- "CGPT-WEB",
1258
- `Async image poll failed: ${err instanceof Error ? err.message : String(err)}`
1259
- );
1260
- }
1261
- }
1262
- const urls = await resolveImagePointers(
1263
- imagePointers,
1264
- conversationId,
1265
- resolver,
1266
- log,
1267
- parentCandidateMessageId
1268
- );
1269
- const imageResolutionFailed = detectImageResolutionFailure(
1270
- imagePointers?.length ?? 0,
1271
- urls.length
1272
- );
1273
- if (imageResolutionFailed && log?.warn) {
1274
- const schemes = (imagePointers ?? []).map((p) => p.pointer.split("://")[0] || p.pointer.slice(0, 24)).join(", ");
1275
- log.warn(
1276
- "CGPT-WEB",
1277
- `Image generated upstream but no asset pointer resolved (schemes: ${schemes}) \u2014 surfacing as unretrievable`
1278
- );
1279
- }
1280
- fullAnswer += imageMarkdown(urls);
1281
- const promptTokens = Math.ceil(currentMsg.length / 4);
1282
- const completionTokens = Math.ceil(fullAnswer.length / 4);
1283
- return new Response(
1284
- JSON.stringify({
1285
- id: cid,
1286
- object: "chat.completion",
1287
- created,
1288
- model,
1289
- system_fingerprint: null,
1290
- ...imageResolutionFailed ? { x_image_resolution_failed: true } : {},
1291
- choices: [
1292
- {
1293
- index: 0,
1294
- message: { role: "assistant", content: fullAnswer },
1295
- finish_reason: "stop",
1296
- logprobs: null
1297
- }
1298
- ],
1299
- usage: {
1300
- prompt_tokens: promptTokens,
1301
- completion_tokens: completionTokens,
1302
- total_tokens: promptTokens + completionTokens
1303
- }
1304
- }),
1305
- { status: 200, headers: { "Content-Type": "application/json" } }
1306
- );
1307
- }
1308
- function errorResponse(status, message, code) {
1309
- return new Response(
1310
- JSON.stringify({ error: { message, type: "upstream_error", ...code ? { code } : {} } }),
1311
- { status, headers: { "Content-Type": "application/json" } }
1312
- );
1313
- }
1314
- function normalizePublicBaseUrl(value) {
1315
- const trimmed = value?.trim();
1316
- if (!trimmed) return null;
1317
- return trimmed.replace(/\/+$/, "").replace(/\/v1$/i, "");
1318
- }
1319
- function firstForwardedValue(value) {
1320
- const first = value?.split(",")[0]?.trim();
1321
- return first || null;
1322
- }
1323
- function isLocalBaseUrl(baseUrl) {
1324
- try {
1325
- const host = new URL(baseUrl).hostname.toLowerCase();
1326
- return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "0.0.0.0";
1327
- } catch {
1328
- console.warn("[chatgpt-web] URL parse failed, falling back to regex");
1329
- return /\b(?:localhost|127\.0\.0\.1|0\.0\.0\.0)\b/i.test(baseUrl);
1330
- }
1331
- }
1332
- function deriveHeaderBaseUrl(clientHeaders) {
1333
- const headers = clientHeaders ?? {};
1334
- const lower = {};
1335
- for (const [k, v] of Object.entries(headers)) lower[k.toLowerCase()] = v;
1336
- const forwardedHost = firstForwardedValue(lower["x-forwarded-host"]);
1337
- const forwardedProto = firstForwardedValue(lower["x-forwarded-proto"]);
1338
- const host = forwardedHost || firstForwardedValue(lower["host"]);
1339
- if (!host) return null;
1340
- const isPlain = host.includes("localhost") || /^\d+\.\d+\.\d+\.\d+(:\d+)?$/.test(host) || host.endsWith(".local") || host.includes(":");
1341
- const proto = forwardedProto || (isPlain ? "http" : "https");
1342
- return `${proto}://${host}`;
1343
- }
1344
- function derivePublicBaseUrl(clientHeaders, log) {
1345
- const explicitPublicBase = normalizePublicBaseUrl(process.env.OMNIROUTE_PUBLIC_BASE_URL);
1346
- if (explicitPublicBase) {
1347
- log?.debug?.("CGPT-WEB", `derivePublicBaseUrl: using OMNIROUTE_PUBLIC_BASE_URL`);
1348
- return explicitPublicBase;
1349
- }
1350
- const headerBase = deriveHeaderBaseUrl(clientHeaders);
1351
- const configuredBase = normalizePublicBaseUrl(process.env.OMNIROUTE_BASE_URL) || normalizePublicBaseUrl(process.env.NEXT_PUBLIC_BASE_URL);
1352
- log?.debug?.(
1353
- "CGPT-WEB",
1354
- `derivePublicBaseUrl: configured=${configuredBase ?? "-"} header=${headerBase ?? "-"}`
1355
- );
1356
- if (configuredBase && (!headerBase || !isLocalBaseUrl(configuredBase))) return configuredBase;
1357
- if (headerBase) return headerBase;
1358
- if (configuredBase) return configuredBase;
1359
- return `http://localhost:${process.env.PORT || 20128}`;
1360
- }
1361
- const FILE_SERVICE_PREFIX = "file-service://";
1362
- const SEDIMENT_PREFIX = "sediment://";
1363
- async function fetchDownloadUrl(endpoint, ctx) {
1364
- const headers = {
1365
- ...browserHeaders(),
1366
- ...oaiHeaders(ctx.sessionId, ctx.deviceId),
1367
- Accept: "application/json",
1368
- Authorization: `Bearer ${ctx.accessToken}`,
1369
- Cookie: buildSessionCookieHeader(ctx.cookie)
1370
- };
1371
- if (ctx.accountId) headers["chatgpt-account-id"] = ctx.accountId;
1372
- const response = await tlsFetchChatGpt(endpoint, {
1373
- method: "GET",
1374
- headers,
1375
- timeoutMs: 3e4,
1376
- signal: ctx.signal
1377
- });
1378
- if (response.status !== 200) {
1379
- ctx.log?.warn?.(
1380
- "CGPT-WEB",
1381
- `Image download URL fetch failed (${response.status}) for ${endpoint}`
1382
- );
1383
- return null;
1384
- }
1385
- let parsed = {};
1386
- try {
1387
- parsed = JSON.parse(response.text || "{}");
1388
- } catch {
1389
- console.warn("[chatgpt-web] image download URL parse failed");
1390
- return null;
1391
- }
1392
- return parsed.download_url ?? null;
1393
- }
1394
- const IMAGE_DOWNLOAD_MAX_BYTES = 8 * 1024 * 1024;
1395
- async function imageUrlToCachedImageUrl(signedUrl, ctx, imageContext) {
1396
- const headers = {
1397
- ...browserHeaders(),
1398
- Accept: "image/*,*/*;q=0.8",
1399
- Authorization: `Bearer ${ctx.accessToken}`,
1400
- Cookie: buildSessionCookieHeader(ctx.cookie)
1401
- };
1402
- if (ctx.accountId) headers["chatgpt-account-id"] = ctx.accountId;
1403
- let response;
1404
- try {
1405
- response = await tlsFetchChatGpt(signedUrl, {
1406
- method: "GET",
1407
- headers,
1408
- timeoutMs: 6e4,
1409
- signal: ctx.signal,
1410
- // Required for binary payloads — the underlying tls-client returns
1411
- // bytes as a `data:<mime>;base64,...` string when this is true.
1412
- // Without it, raw image bytes get mangled by UTF-8 decoding.
1413
- byteResponse: true
1414
- });
1415
- } catch (err) {
1416
- ctx.log?.warn?.(
1417
- "CGPT-WEB",
1418
- `Image fetch failed: ${err instanceof Error ? err.message : String(err)}`
1419
- );
1420
- return null;
1421
- }
1422
- if (response.status !== 200) {
1423
- ctx.log?.warn?.(
1424
- "CGPT-WEB",
1425
- `Image fetch returned HTTP ${response.status} (${(response.text || "").slice(0, 120)})`
1426
- );
1427
- return null;
1428
- }
1429
- if (response.text == null || response.text.length === 0) return null;
1430
- let bytes;
1431
- let mime;
1432
- if (/^data:[^;]{1,256};base64,/.test(response.text)) {
1433
- const commaIdx = response.text.indexOf(",");
1434
- const header = response.text.slice(5, commaIdx);
1435
- mime = header.split(";")[0] || "image/png";
1436
- bytes = Buffer.from(response.text.slice(commaIdx + 1), "base64");
1437
- } else {
1438
- bytes = Buffer.from(response.text, "binary");
1439
- mime = response.headers.get("content-type")?.split(";")[0]?.trim() || "image/png";
1440
- }
1441
- if (bytes.length === 0 || bytes.length > IMAGE_DOWNLOAD_MAX_BYTES) {
1442
- if (bytes.length > IMAGE_DOWNLOAD_MAX_BYTES) {
1443
- ctx.log?.warn?.(
1444
- "CGPT-WEB",
1445
- `Image too large to cache (${bytes.length} bytes > ${IMAGE_DOWNLOAD_MAX_BYTES}); skipping`
1446
- );
1447
- }
1448
- return null;
1449
- }
1450
- const id = storeChatGptImage(bytes, mime, void 0, imageContext);
1451
- return `${ctx.publicBaseUrl}/v1/chatgpt-web/image/${id}`;
1452
- }
1453
- async function registerWebSocket(ctx) {
1454
- const candidates = [
1455
- { url: `${CHATGPT_BASE}/backend-api/celsius/ws/user`, method: "GET" },
1456
- { url: `${CHATGPT_BASE}/backend-api/register-websocket`, method: "POST" }
1457
- ];
1458
- const headers = {
1459
- ...browserHeaders(),
1460
- ...oaiHeaders(ctx.sessionId, ctx.deviceId),
1461
- Accept: "application/json",
1462
- Authorization: `Bearer ${ctx.accessToken}`,
1463
- Cookie: buildSessionCookieHeader(ctx.cookie)
1464
- };
1465
- if (ctx.accountId) headers["chatgpt-account-id"] = ctx.accountId;
1466
- for (const { url, method } of candidates) {
1467
- let r;
1468
- try {
1469
- r = await tlsFetchChatGpt(url, {
1470
- method,
1471
- headers,
1472
- body: method === "POST" ? "" : void 0,
1473
- timeoutMs: 3e4,
1474
- signal: ctx.signal
1475
- });
1476
- } catch (err) {
1477
- ctx.log?.warn?.(
1478
- "CGPT-WEB",
1479
- `register-websocket fetch failed for ${url}: ${err instanceof Error ? err.message : String(err)}`
1480
- );
1481
- continue;
1482
- }
1483
- if (r.status === 200) {
1484
- try {
1485
- const data = JSON.parse(r.text || "{}");
1486
- const ws = data.websocket_url ?? data.wss_url;
1487
- if (ws) {
1488
- ctx.log?.debug?.("CGPT-WEB", `Got WebSocket URL via ${url}`);
1489
- return ws;
1490
- }
1491
- } catch {
1492
- console.warn("[chatgpt-web] WebSocket URL parse failed, falling through");
1493
- }
1494
- }
1495
- ctx.log?.warn?.(
1496
- "CGPT-WEB",
1497
- `register-websocket via ${url} \u2192 ${r.status}: ${(r.text || "").slice(0, 200)}`
1498
- );
1499
- }
1500
- return null;
1501
- }
1502
- async function waitForImageViaWebSocket(wssUrl, conversationId, timeoutMs, ctx) {
1503
- return new Promise((resolve) => {
1504
- const found = /* @__PURE__ */ new Map();
1505
- let resolved = false;
1506
- let errored = false;
1507
- let gotAnyMessage = false;
1508
- const finish = () => {
1509
- if (resolved) return;
1510
- resolved = true;
1511
- try {
1512
- ws.close();
1513
- } catch {
1514
- console.warn("[chatgpt-web] ws.close failed");
1515
- }
1516
- resolve({
1517
- pointers: Array.from(found.values()),
1518
- errored,
1519
- gotAnyMessage
1520
- });
1521
- };
1522
- const ws = new WebSocket(wssUrl);
1523
- const timer = setTimeout(() => {
1524
- ctx.log?.warn?.("CGPT-WEB", `WebSocket image wait timed out after ${timeoutMs}ms`);
1525
- finish();
1526
- }, timeoutMs);
1527
- const onAbort = () => {
1528
- ctx.log?.debug?.("CGPT-WEB", "WebSocket aborted by client");
1529
- finish();
1530
- };
1531
- ctx.signal?.addEventListener?.("abort", onAbort);
1532
- ws.onopen = () => {
1533
- gotAnyMessage = true;
1534
- ctx.log?.debug?.("CGPT-WEB", "WebSocket open \u2014 waiting for image events");
1535
- };
1536
- ws.onerror = (e) => {
1537
- errored = true;
1538
- ctx.log?.warn?.("CGPT-WEB", `WebSocket error: ${e.message ?? "unknown"}`);
1539
- };
1540
- ws.onclose = () => {
1541
- clearTimeout(timer);
1542
- ctx.signal?.removeEventListener?.("abort", onAbort);
1543
- finish();
1544
- };
1545
- ws.onmessage = (event) => {
1546
- gotAnyMessage = true;
1547
- let payload;
1548
- const raw = typeof event.data === "string" ? event.data : event.data.toString();
1549
- try {
1550
- payload = JSON.parse(raw);
1551
- } catch {
1552
- console.warn("[chatgpt-web] WebSocket event JSON parse failed");
1553
- return;
1554
- }
1555
- const obj = payload;
1556
- const candidates = [];
1557
- const innerPayload = obj.payload;
1558
- const updateContent = innerPayload?.update_content;
1559
- if (updateContent?.message) {
1560
- candidates.push({
1561
- message: updateContent.message,
1562
- conversation_id: innerPayload?.conversation_id
1563
- });
1564
- }
1565
- if (innerPayload?.message) {
1566
- candidates.push({
1567
- message: innerPayload.message,
1568
- conversation_id: innerPayload.conversation_id
1569
- });
1570
- }
1571
- if (obj.data?.message) {
1572
- candidates.push(obj.data);
1573
- }
1574
- for (const data of candidates) {
1575
- if (data?.conversation_id && data.conversation_id !== conversationId) continue;
1576
- const m = data?.message;
1577
- if (Array.isArray(m?.content?.parts)) {
1578
- for (const ptr of extractImagePointers(m.content?.parts ?? [])) {
1579
- const existing = found.get(ptr);
1580
- found.set(
1581
- ptr,
1582
- existing?.messageId ? existing : { pointer: ptr, ...m?.id ? { messageId: m.id } : {} }
1583
- );
1584
- }
1585
- }
1586
- if (m?.metadata && typeof m.metadata === "object") {
1587
- const md = m.metadata;
1588
- const ptr = md.asset_pointer ?? md.image_asset_pointer;
1589
- if (typeof ptr === "string") {
1590
- const existing = found.get(ptr);
1591
- found.set(
1592
- ptr,
1593
- existing?.messageId ? existing : { pointer: ptr, ...m?.id ? { messageId: m.id } : {} }
1594
- );
1595
- }
1596
- }
1597
- }
1598
- if (found.size > 0) finish();
1599
- };
1600
- });
1601
- }
1602
- const DEFAULT_ASYNC_IMAGE_TIMEOUT_MS = 18e4;
1603
- function configuredAsyncImageTimeoutMs() {
1604
- const raw = Number(process.env.OMNIROUTE_CGPT_WEB_IMAGE_TIMEOUT_MS);
1605
- if (!Number.isFinite(raw) || raw <= 0) return DEFAULT_ASYNC_IMAGE_TIMEOUT_MS;
1606
- return Math.floor(raw);
1607
- }
1608
- async function pollForAsyncImage(conversationId, ctx, opts = {}) {
1609
- const totalTimeoutMs = opts.timeoutMs ?? configuredAsyncImageTimeoutMs();
1610
- const deadline = Date.now() + totalTimeoutMs;
1611
- for (let attempt = 0; attempt < 2; attempt++) {
1612
- const remaining = deadline - Date.now();
1613
- if (remaining <= 0) break;
1614
- const wssUrl = await registerWebSocket(ctx);
1615
- if (!wssUrl) {
1616
- ctx.log?.warn?.(
1617
- "CGPT-WEB",
1618
- attempt === 0 ? "Could not register WebSocket \u2014 async image gen not retrievable" : `WebSocket re-registration failed on retry attempt ${attempt + 1}`
1619
- );
1620
- if (attempt === 0) continue;
1621
- return [];
1622
- }
1623
- ctx.log?.debug?.(
1624
- "CGPT-WEB",
1625
- `Registered WebSocket for async image (attempt ${attempt + 1}, ${remaining}ms remaining)`
1626
- );
1627
- const outcome = await waitForImageViaWebSocket(wssUrl, conversationId, remaining, ctx);
1628
- if (outcome.pointers.length > 0) return outcome.pointers;
1629
- if (ctx.signal?.aborted) return [];
1630
- if (!outcome.errored || outcome.gotAnyMessage) return [];
1631
- ctx.log?.warn?.(
1632
- "CGPT-WEB",
1633
- `WebSocket attempt ${attempt + 1} ended in transport error before any frame; retrying`
1634
- );
1635
- }
1636
- return [];
1637
- }
1638
- function makeImageResolver(ctx) {
1639
- const cache = /* @__PURE__ */ new Map();
1640
- return async (assetPointer, conversationId, parentMessageId) => {
1641
- if (cache.has(assetPointer)) return cache.get(assetPointer) ?? null;
1642
- let fileId = null;
1643
- if (assetPointer.startsWith(FILE_SERVICE_PREFIX)) {
1644
- fileId = assetPointer.slice(FILE_SERVICE_PREFIX.length);
1645
- } else if (assetPointer.startsWith(SEDIMENT_PREFIX)) {
1646
- fileId = assetPointer.slice(SEDIMENT_PREFIX.length);
1647
- } else {
1648
- ctx.log?.warn?.("CGPT-WEB", `Unknown asset_pointer scheme: ${assetPointer}`);
1649
- }
1650
- let signedUrl = null;
1651
- if (fileId) {
1652
- signedUrl = await fetchDownloadUrl(
1653
- `${CHATGPT_BASE}/backend-api/files/${encodeURIComponent(fileId)}/download`,
1654
- ctx
1655
- );
1656
- if (!signedUrl && conversationId) {
1657
- signedUrl = await fetchDownloadUrl(
1658
- `${CHATGPT_BASE}/backend-api/conversation/${encodeURIComponent(conversationId)}/attachment/${encodeURIComponent(fileId)}/download`,
1659
- ctx
1660
- );
1661
- }
1662
- }
1663
- let finalUrl = null;
1664
- if (signedUrl) {
1665
- finalUrl = await imageUrlToCachedImageUrl(
1666
- signedUrl,
1667
- ctx,
1668
- conversationId && parentMessageId ? { conversationId, parentMessageId } : void 0
1669
- );
1670
- }
1671
- cache.set(assetPointer, finalUrl);
1672
- if (finalUrl) {
1673
- const preview = finalUrl.startsWith("data:") ? `data:... (${finalUrl.length} chars)` : finalUrl.slice(0, 80) + "...";
1674
- ctx.log?.debug?.("CGPT-WEB", `Resolved ${assetPointer} \u2192 ${preview}`);
1675
- }
1676
- return finalUrl;
1677
- };
1678
- }
1679
- class ChatGptWebExecutor extends BaseExecutor {
1680
- constructor() {
1681
- super("chatgpt-web", { id: "chatgpt-web", baseUrl: CONV_URL });
1682
- }
1683
- async execute({
1684
- model,
1685
- body,
1686
- stream,
1687
- credentials,
1688
- signal,
1689
- log,
1690
- onCredentialsRefreshed,
1691
- clientHeaders
1692
- }) {
1693
- const messages = body?.messages;
1694
- if (!messages || !Array.isArray(messages) || messages.length === 0) {
1695
- return {
1696
- response: errorResponse(400, "Missing or empty messages array"),
1697
- url: CONV_URL,
1698
- headers: {},
1699
- transformedBody: body
1700
- };
1701
- }
1702
- const { hasTools, requestedTools, effectiveMessages } = prepareToolMessages(
1703
- body || {},
1704
- messages
1705
- );
1706
- if (!credentials.apiKey) {
1707
- return {
1708
- response: errorResponse(
1709
- 401,
1710
- "ChatGPT auth failed \u2014 paste your __Secure-next-auth.session-token cookie value."
1711
- ),
1712
- url: CONV_URL,
1713
- headers: {},
1714
- transformedBody: body
1715
- };
1716
- }
1717
- const cookie = credentials.apiKey;
1718
- let tokenEntry;
1719
- try {
1720
- tokenEntry = await exchangeSession(cookie, signal);
1721
- } catch (err) {
1722
- if (err instanceof SessionAuthError) {
1723
- log?.warn?.("CGPT-WEB", err.message);
1724
- return {
1725
- response: errorResponse(
1726
- 401,
1727
- "ChatGPT auth failed \u2014 re-paste your __Secure-next-auth.session-token cookie from chatgpt.com.",
1728
- "HTTP_401"
1729
- ),
1730
- url: SESSION_URL,
1731
- headers: {},
1732
- transformedBody: body
1733
- };
1734
- }
1735
- log?.error?.(
1736
- "CGPT-WEB",
1737
- `Session exchange failed: ${err instanceof Error ? err.message : String(err)}`
1738
- );
1739
- return {
1740
- response: errorResponse(
1741
- 502,
1742
- `ChatGPT session exchange failed: ${err instanceof Error ? err.message : String(err)}`
1743
- ),
1744
- url: SESSION_URL,
1745
- headers: {},
1746
- transformedBody: body
1747
- };
1748
- }
1749
- if (tokenEntry.refreshedCookie && tokenEntry.refreshedCookie !== cookie) {
1750
- const updated = { ...credentials, apiKey: tokenEntry.refreshedCookie };
1751
- try {
1752
- await onCredentialsRefreshed?.(updated);
1753
- } catch (err) {
1754
- log?.warn?.(
1755
- "CGPT-WEB",
1756
- `Failed to persist refreshed cookie: ${err instanceof Error ? err.message : String(err)}`
1757
- );
1758
- }
1759
- }
1760
- let dplInfo;
1761
- try {
1762
- dplInfo = await fetchDpl(cookie, signal);
1763
- } catch (err) {
1764
- log?.warn?.(
1765
- "CGPT-WEB",
1766
- `DPL warmup failed (continuing with fallback): ${err instanceof Error ? err.message : String(err)}`
1767
- );
1768
- dplInfo = {
1769
- dpl: `dpl=${OAI_CLIENT_VERSION.replace(/^prod-/, "")}`,
1770
- scriptSrc: `${CHATGPT_BASE}/_next/static/chunks/webpack-${randomHex(16)}.js`
1771
- };
1772
- }
1773
- const sessionId = randomUUID();
1774
- const deviceId = deviceIdFor(cookie);
1775
- await runSessionWarmup(
1776
- tokenEntry.accessToken,
1777
- tokenEntry.accountId,
1778
- sessionId,
1779
- deviceId,
1780
- cookie,
1781
- signal,
1782
- log
1783
- );
1784
- const resolvedModel = resolveChatGptModel(model, body, credentials.providerSpecificData);
1785
- const modelSlug = resolvedModel.slug;
1786
- const requestedEffort = resolvedModel.effort;
1787
- if (requestedEffort && isThinkingCapableModel(model, modelSlug)) {
1788
- await setUserThinkingEffort(
1789
- modelSlug,
1790
- requestedEffort,
1791
- tokenEntry.accessToken,
1792
- tokenEntry.accountId,
1793
- sessionId,
1794
- deviceId,
1795
- cookie,
1796
- signal,
1797
- log
1798
- );
1799
- }
1800
- let reqs;
1801
- try {
1802
- reqs = await prepareChatRequirements(
1803
- tokenEntry.accessToken,
1804
- tokenEntry.accountId,
1805
- sessionId,
1806
- deviceId,
1807
- cookie,
1808
- dplInfo,
1809
- signal,
1810
- log
1811
- );
1812
- } catch (err) {
1813
- if (err instanceof SentinelBlockedError) {
1814
- log?.warn?.("CGPT-WEB", err.message);
1815
- return {
1816
- response: errorResponse(
1817
- 403,
1818
- "ChatGPT blocked the request (Sentinel/Turnstile required). Try again later or open chatgpt.com in a browser to refresh state.",
1819
- "SENTINEL_BLOCKED"
1820
- ),
1821
- url: SENTINEL_PREPARE_URL,
1822
- headers: {},
1823
- transformedBody: body
1824
- };
1825
- }
1826
- log?.error?.(
1827
- "CGPT-WEB",
1828
- `Sentinel failed: ${err instanceof Error ? err.message : String(err)}`
1829
- );
1830
- return {
1831
- response: errorResponse(
1832
- 502,
1833
- `ChatGPT sentinel failed: ${err instanceof Error ? err.message : String(err)}`
1834
- ),
1835
- url: SENTINEL_PREPARE_URL,
1836
- headers: {},
1837
- transformedBody: body
1838
- };
1839
- }
1840
- log?.debug?.(
1841
- "CGPT-WEB",
1842
- `sentinel: token=${reqs.token ? "y" : "n"} pow=${reqs.proofofwork?.required ? "y" : "n"} turnstile=${reqs.turnstile?.required ? "y" : "n"}`
1843
- );
1844
- const turnstileToken = typeof credentials.providerSpecificData?.turnstileToken === "string" ? credentials.providerSpecificData.turnstileToken : null;
1845
- let proofToken = null;
1846
- if (reqs.proofofwork?.required && reqs.proofofwork.seed && reqs.proofofwork.difficulty) {
1847
- const powConfig = buildPrekeyConfig(CHATGPT_USER_AGENT, dplInfo.dpl, dplInfo.scriptSrc);
1848
- proofToken = await solveProofOfWork(
1849
- reqs.proofofwork.seed,
1850
- reqs.proofofwork.difficulty,
1851
- powConfig,
1852
- log
1853
- );
1854
- }
1855
- const parsed = parseOpenAIMessages(effectiveMessages);
1856
- if (!parsed.currentMsg.trim() && parsed.history.length === 0) {
1857
- return {
1858
- response: errorResponse(400, "Empty user message"),
1859
- url: CONV_URL,
1860
- headers: {},
1861
- transformedBody: body
1862
- };
1863
- }
1864
- const imageEdit = looksLikeImageEditRequest(parsed);
1865
- const continuation = imageEdit ? parsed.latestImageContext : null;
1866
- const forImageGen = looksLikeImageGenRequest(parsed) || imageEdit;
1867
- const persistConversation = forImageGen || !!continuation;
1868
- if (forImageGen) {
1869
- log?.debug?.(
1870
- "CGPT-WEB",
1871
- continuation ? "Image edit intent detected \u2014 continuing saved image conversation" : "Image-gen intent detected \u2014 disabling Temporary Chat for this turn"
1872
- );
1873
- } else if (resolvedModel.isPro) {
1874
- log?.debug?.("CGPT-WEB", "GPT-5.5 Pro text request \u2014 keeping Temporary Chat enabled");
1875
- }
1876
- const parentMessageId = continuation?.parentMessageId ?? randomUUID();
1877
- const cgptBody = buildConversationBody(parsed, modelSlug, parentMessageId, {
1878
- persistConversation,
1879
- thinkingEffort: requestedEffort,
1880
- continuation
1881
- });
1882
- const headers = {
1883
- ...browserHeaders(),
1884
- ...oaiHeaders(sessionId, deviceId),
1885
- "Content-Type": "application/json",
1886
- Accept: "text/event-stream",
1887
- Authorization: `Bearer ${tokenEntry.accessToken}`,
1888
- Cookie: buildSessionCookieHeader(cookie)
1889
- };
1890
- if (tokenEntry.accountId) headers["chatgpt-account-id"] = tokenEntry.accountId;
1891
- if (reqs.token) headers["openai-sentinel-chat-requirements-token"] = reqs.token;
1892
- if (reqs.prepare_token)
1893
- headers["openai-sentinel-chat-requirements-prepare-token"] = reqs.prepare_token;
1894
- if (proofToken) headers["openai-sentinel-proof-token"] = proofToken;
1895
- if (turnstileToken) headers["openai-sentinel-turnstile-token"] = turnstileToken;
1896
- log?.info?.("CGPT-WEB", `Conversation request \u2192 ${modelSlug} (pow=${!!proofToken})`);
1897
- let response;
1898
- try {
1899
- response = await tlsFetchChatGpt(CONV_URL, {
1900
- method: "POST",
1901
- headers,
1902
- body: JSON.stringify(cgptBody),
1903
- timeoutMs: 12e4,
1904
- // generations can take a while
1905
- signal,
1906
- // For real-time streaming, ask the TLS client to write the body to
1907
- // a temp file and surface it as a ReadableStream as it arrives —
1908
- // otherwise long generations buffer entirely before the client sees
1909
- // anything (and the downstream HTTP request can time out).
1910
- stream
1911
- });
1912
- } catch (err) {
1913
- log?.error?.("CGPT-WEB", `Fetch failed: ${err instanceof Error ? err.message : String(err)}`);
1914
- const code = err instanceof TlsClientUnavailableError ? "TLS_UNAVAILABLE" : void 0;
1915
- return {
1916
- response: errorResponse(
1917
- 502,
1918
- `ChatGPT connection failed: ${err instanceof Error ? err.message : String(err)}`,
1919
- code
1920
- ),
1921
- url: CONV_URL,
1922
- headers,
1923
- transformedBody: cgptBody
1924
- };
1925
- }
1926
- if (response.status >= 400) {
1927
- const status = response.status;
1928
- log?.warn?.("CGPT-WEB", `conv ${status}: ${(response.text || "").slice(0, 400)}`);
1929
- const errMsg = describeChatGptWebHttpError(status);
1930
- if (status === 401 || status === 403) {
1931
- tokenCache.delete(cookieKey(cookie));
1932
- }
1933
- log?.warn?.("CGPT-WEB", errMsg);
1934
- return {
1935
- response: errorResponse(status, errMsg, `HTTP_${status}`),
1936
- url: CONV_URL,
1937
- headers,
1938
- transformedBody: cgptBody
1939
- };
1940
- }
1941
- let bodyStream;
1942
- if (response.body) {
1943
- bodyStream = response.body;
1944
- } else if (response.text) {
1945
- bodyStream = stringToStream(response.text);
1946
- } else {
1947
- return {
1948
- response: errorResponse(502, "ChatGPT returned empty response body"),
1949
- url: CONV_URL,
1950
- headers,
1951
- transformedBody: cgptBody
1952
- };
1953
- }
1954
- const cid = `chatcmpl-cgpt-${crypto.randomUUID().slice(0, 12)}`;
1955
- const created = Math.floor(Date.now() / 1e3);
1956
- const resolverCtx = {
1957
- accessToken: tokenEntry.accessToken,
1958
- accountId: tokenEntry.accountId,
1959
- sessionId,
1960
- deviceId,
1961
- cookie,
1962
- signal,
1963
- log,
1964
- publicBaseUrl: derivePublicBaseUrl(clientHeaders, log)
1965
- };
1966
- const imageResolver = makeImageResolver(resolverCtx);
1967
- const pollAsyncImage = (conversationId) => pollForAsyncImage(conversationId, resolverCtx);
1968
- const pollFinalAnswer = resolvedModel.isPro ? (conversationId) => pollForFinalAssistantAnswer(conversationId, resolverCtx) : null;
1969
- const toolMode = hasTools && !forImageGen;
1970
- let finalResponse;
1971
- if (stream && !toolMode) {
1972
- const sseStream = buildStreamingResponse(
1973
- bodyStream,
1974
- model,
1975
- cid,
1976
- created,
1977
- imageResolver,
1978
- pollAsyncImage,
1979
- pollFinalAnswer,
1980
- log,
1981
- signal
1982
- );
1983
- finalResponse = new Response(sseStream, {
1984
- status: 200,
1985
- headers: {
1986
- "Content-Type": "text/event-stream",
1987
- "Cache-Control": "no-cache",
1988
- "X-Accel-Buffering": "no"
1989
- }
1990
- });
1991
- } else {
1992
- finalResponse = await buildNonStreamingResponse(
1993
- bodyStream,
1994
- model,
1995
- cid,
1996
- created,
1997
- parsed.currentMsg,
1998
- imageResolver,
1999
- pollAsyncImage,
2000
- pollFinalAnswer,
2001
- log,
2002
- signal
2003
- );
2004
- if (toolMode) {
2005
- finalResponse = await buildToolModeResponse(finalResponse, requestedTools, stream, {
2006
- cid,
2007
- created,
2008
- model
2009
- });
2010
- }
2011
- }
2012
- return { response: finalResponse, url: CONV_URL, headers, transformedBody: cgptBody };
2013
- }
2014
- }
2015
- const ENTITY_RE = /entity\["[^"]*","([^"]*)"[^\]]*\]/g;
2016
- function cleanChatGptText(text) {
2017
- return text.replace(ENTITY_RE, "$1");
2018
- }
2019
- function stringToStream(text) {
2020
- const encoder = new TextEncoder();
2021
- return new ReadableStream({
2022
- start(controller) {
2023
- controller.enqueue(encoder.encode(text));
2024
- controller.close();
2025
- }
2026
- });
2027
- }
2028
- function __resetChatGptWebCachesForTesting() {
2029
- tokenCache.clear();
2030
- warmupCache.clear();
2031
- thinkingEffortCache.clear();
2032
- deviceIdCache.clear();
2033
- __resetChatGptImageCacheForTesting();
2034
- dplCache = null;
2035
- }
2036
- const __derivePublicBaseUrlForTesting = derivePublicBaseUrl;
2037
- export {
2038
- ChatGptWebExecutor,
2039
- __derivePublicBaseUrlForTesting,
2040
- __resetChatGptWebCachesForTesting,
2041
- detectImageResolutionFailure
2042
- };