@jsonstudio/rcc 0.89.1960 → 0.89.2195

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 (559) hide show
  1. package/README.md +66 -6
  2. package/config/file-line-limit-policy.json +21 -0
  3. package/configsamples/config.json +1 -1
  4. package/configsamples/config.v1.quickstart.sanitized.json +6 -6
  5. package/configsamples/provider/iflow/config.v1.json +1 -1
  6. package/dist/app/config-readers.d.ts +18 -0
  7. package/dist/app/config-readers.js +95 -0
  8. package/dist/app/config-readers.js.map +1 -0
  9. package/dist/app/index.d.ts +9 -0
  10. package/dist/app/index.js +8 -0
  11. package/dist/app/index.js.map +1 -0
  12. package/dist/app/shutdown.d.ts +32 -0
  13. package/dist/app/shutdown.js +41 -0
  14. package/dist/app/shutdown.js.map +1 -0
  15. package/dist/bootstrap/index.d.ts +7 -0
  16. package/dist/bootstrap/index.js +7 -0
  17. package/dist/bootstrap/index.js.map +1 -0
  18. package/dist/bootstrap/log-filter.d.ts +15 -0
  19. package/dist/bootstrap/log-filter.js +81 -0
  20. package/dist/bootstrap/log-filter.js.map +1 -0
  21. package/dist/build-info.js +2 -2
  22. package/dist/cli/commands/claude.js +6 -5
  23. package/dist/cli/commands/claude.js.map +1 -1
  24. package/dist/cli/commands/init/basic.d.ts +1 -7
  25. package/dist/cli/commands/init/basic.js +0 -79
  26. package/dist/cli/commands/init/basic.js.map +1 -1
  27. package/dist/cli/commands/init/prompt-utils.d.ts +7 -0
  28. package/dist/cli/commands/init/prompt-utils.js +80 -0
  29. package/dist/cli/commands/init/prompt-utils.js.map +1 -0
  30. package/dist/cli/commands/init/workflows.js +2 -1
  31. package/dist/cli/commands/init/workflows.js.map +1 -1
  32. package/dist/cli/commands/init.js +103 -2
  33. package/dist/cli/commands/init.js.map +1 -1
  34. package/dist/cli/commands/launcher/index.d.ts +7 -0
  35. package/dist/cli/commands/launcher/index.js +7 -0
  36. package/dist/cli/commands/launcher/index.js.map +1 -0
  37. package/dist/cli/commands/launcher/types.d.ts +112 -0
  38. package/dist/cli/commands/launcher/types.js +7 -0
  39. package/dist/cli/commands/launcher/types.js.map +1 -0
  40. package/dist/cli/commands/launcher/utils.d.ts +114 -0
  41. package/dist/cli/commands/launcher/utils.js +378 -0
  42. package/dist/cli/commands/launcher/utils.js.map +1 -0
  43. package/dist/cli/commands/launcher-kernel.d.ts +2 -74
  44. package/dist/cli/commands/launcher-kernel.js +141 -143
  45. package/dist/cli/commands/launcher-kernel.js.map +1 -1
  46. package/dist/cli/commands/start-types.d.ts +67 -0
  47. package/dist/cli/commands/start-types.js +3 -0
  48. package/dist/cli/commands/start-types.js.map +1 -0
  49. package/dist/cli/commands/start-utils.d.ts +19 -0
  50. package/dist/cli/commands/start-utils.js +78 -0
  51. package/dist/cli/commands/start-utils.js.map +1 -0
  52. package/dist/cli/commands/start.d.ts +2 -67
  53. package/dist/cli/commands/start.js +131 -47
  54. package/dist/cli/commands/start.js.map +1 -1
  55. package/dist/cli/commands/stop.js +13 -0
  56. package/dist/cli/commands/stop.js.map +1 -1
  57. package/dist/cli/commands/tmux-inject.js +1 -1
  58. package/dist/cli/commands/tmux-inject.js.map +1 -1
  59. package/dist/cli/config/bundled-default-config.d.ts +18 -0
  60. package/dist/cli/config/bundled-default-config.js +62 -0
  61. package/dist/cli/config/bundled-default-config.js.map +1 -0
  62. package/dist/cli/config/init-config.js +60 -2
  63. package/dist/cli/config/init-config.js.map +1 -1
  64. package/dist/cli/config/init-provider-catalog.js +3 -1
  65. package/dist/cli/config/init-provider-catalog.js.map +1 -1
  66. package/dist/cli/config/precommand-default-script.d.ts +17 -0
  67. package/dist/cli/config/precommand-default-script.js +47 -0
  68. package/dist/cli/config/precommand-default-script.js.map +1 -0
  69. package/dist/cli.js +2 -0
  70. package/dist/cli.js.map +1 -1
  71. package/dist/client/gemini-cli/gemini-cli-protocol-client.js +55 -5
  72. package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
  73. package/dist/commands/oauth.js +28 -4
  74. package/dist/commands/oauth.js.map +1 -1
  75. package/dist/commands/quota-daemon.js +12 -0
  76. package/dist/commands/quota-daemon.js.map +1 -1
  77. package/dist/configsamples/config.v1.quickstart.sanitized.json +1147 -0
  78. package/dist/constants/index.d.ts +34 -0
  79. package/dist/constants/index.js +57 -0
  80. package/dist/constants/index.js.map +1 -1
  81. package/dist/docs/daemon-admin-ui.html +268 -11
  82. package/dist/error-handling/quiet-error-handling-center.js +19 -1
  83. package/dist/error-handling/quiet-error-handling-center.js.map +1 -1
  84. package/dist/index.js +233 -30
  85. package/dist/index.js.map +1 -1
  86. package/dist/manager/index.js +4 -4
  87. package/dist/manager/index.js.map +1 -1
  88. package/dist/manager/modules/quota/antigravity-quota-core.d.ts +26 -0
  89. package/dist/manager/modules/quota/antigravity-quota-core.js +23 -0
  90. package/dist/manager/modules/quota/antigravity-quota-core.js.map +1 -0
  91. package/dist/manager/modules/quota/antigravity-quota-helpers.d.ts +16 -0
  92. package/dist/manager/modules/quota/antigravity-quota-helpers.js +167 -0
  93. package/dist/manager/modules/quota/antigravity-quota-helpers.js.map +1 -0
  94. package/dist/manager/modules/quota/antigravity-quota-manager.d.ts +10 -39
  95. package/dist/manager/modules/quota/antigravity-quota-manager.js +167 -464
  96. package/dist/manager/modules/quota/antigravity-quota-manager.js.map +1 -1
  97. package/dist/manager/modules/quota/antigravity-quota-persistence.d.ts +20 -0
  98. package/dist/manager/modules/quota/antigravity-quota-persistence.js +139 -0
  99. package/dist/manager/modules/quota/antigravity-quota-persistence.js.map +1 -0
  100. package/dist/manager/modules/quota/antigravity-quota-runtime.d.ts +55 -0
  101. package/dist/manager/modules/quota/antigravity-quota-runtime.js +174 -0
  102. package/dist/manager/modules/quota/antigravity-quota-runtime.js.map +1 -0
  103. package/dist/manager/modules/quota/antigravity-quota-sync.d.ts +46 -0
  104. package/dist/manager/modules/quota/antigravity-quota-sync.js +162 -0
  105. package/dist/manager/modules/quota/antigravity-quota-sync.js.map +1 -0
  106. package/dist/manager/modules/quota/index.d.ts +1 -0
  107. package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.d.ts +13 -0
  108. package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.js +149 -0
  109. package/dist/manager/modules/quota/provider-quota-daemon.error-helpers.js.map +1 -0
  110. package/dist/manager/modules/quota/provider-quota-daemon.events.js +1 -148
  111. package/dist/manager/modules/quota/provider-quota-daemon.events.js.map +1 -1
  112. package/dist/manager/modules/quota/provider-quota-daemon.js.map +1 -1
  113. package/dist/manager/modules/quota/quota-adapter.d.ts +111 -0
  114. package/dist/manager/modules/quota/quota-adapter.js +325 -0
  115. package/dist/manager/modules/quota/quota-adapter.js.map +1 -0
  116. package/dist/manager/quota/provider-quota-center.d.ts +10 -0
  117. package/dist/manager/quota/provider-quota-center.js +15 -2
  118. package/dist/manager/quota/provider-quota-center.js.map +1 -1
  119. package/dist/modules/llmswitch/bridge/antigravity-signature.d.ts +28 -0
  120. package/dist/modules/llmswitch/bridge/antigravity-signature.js +180 -0
  121. package/dist/modules/llmswitch/bridge/antigravity-signature.js.map +1 -0
  122. package/dist/modules/llmswitch/bridge/index.d.ts +13 -0
  123. package/dist/modules/llmswitch/bridge/index.js +14 -0
  124. package/dist/modules/llmswitch/bridge/index.js.map +1 -0
  125. package/dist/modules/llmswitch/bridge/module-loader.d.ts +16 -0
  126. package/dist/modules/llmswitch/bridge/module-loader.js +59 -0
  127. package/dist/modules/llmswitch/bridge/module-loader.js.map +1 -0
  128. package/dist/modules/llmswitch/bridge/quota-manager.d.ts +8 -0
  129. package/dist/modules/llmswitch/bridge/quota-manager.js +37 -0
  130. package/dist/modules/llmswitch/bridge/quota-manager.js.map +1 -0
  131. package/dist/modules/llmswitch/bridge/response-converter.d.ts +11 -0
  132. package/dist/modules/llmswitch/bridge/response-converter.js +68 -0
  133. package/dist/modules/llmswitch/bridge/response-converter.js.map +1 -0
  134. package/dist/modules/llmswitch/bridge/routing-integrations.d.ts +12 -0
  135. package/dist/modules/llmswitch/bridge/routing-integrations.js +56 -0
  136. package/dist/modules/llmswitch/bridge/routing-integrations.js.map +1 -0
  137. package/dist/modules/llmswitch/bridge/runtime-integrations.d.ts +34 -0
  138. package/dist/modules/llmswitch/bridge/runtime-integrations.js +87 -0
  139. package/dist/modules/llmswitch/bridge/runtime-integrations.js.map +1 -0
  140. package/dist/modules/llmswitch/bridge/snapshot-recorder.d.ts +13 -0
  141. package/dist/modules/llmswitch/bridge/snapshot-recorder.js +484 -0
  142. package/dist/modules/llmswitch/bridge/snapshot-recorder.js.map +1 -0
  143. package/dist/modules/llmswitch/bridge/state-integrations.d.ts +59 -0
  144. package/dist/modules/llmswitch/bridge/state-integrations.js +264 -0
  145. package/dist/modules/llmswitch/bridge/state-integrations.js.map +1 -0
  146. package/dist/modules/llmswitch/bridge.d.ts +14 -131
  147. package/dist/modules/llmswitch/bridge.js +14 -834
  148. package/dist/modules/llmswitch/bridge.js.map +1 -1
  149. package/dist/modules/pipeline/types/provider-config-types.d.ts +240 -0
  150. package/dist/modules/pipeline/types/provider-config-types.js +2 -0
  151. package/dist/modules/pipeline/types/provider-config-types.js.map +1 -0
  152. package/dist/modules/pipeline/types/provider-types.d.ts +2 -239
  153. package/dist/modules/pipeline/types/provider-types.js +1 -1
  154. package/dist/modules/pipeline/types/provider-types.js.map +1 -1
  155. package/dist/modules/pipeline/utils/debug-logger.js +3 -5
  156. package/dist/modules/pipeline/utils/debug-logger.js.map +1 -1
  157. package/dist/providers/auth/apikey-auth.d.ts +57 -1
  158. package/dist/providers/auth/apikey-auth.js +131 -1
  159. package/dist/providers/auth/apikey-auth.js.map +1 -1
  160. package/dist/providers/auth/oauth-lifecycle/error-detection.d.ts +8 -0
  161. package/dist/providers/auth/oauth-lifecycle/error-detection.js +71 -0
  162. package/dist/providers/auth/oauth-lifecycle/error-detection.js.map +1 -0
  163. package/dist/providers/auth/oauth-lifecycle/index.d.ts +10 -0
  164. package/dist/providers/auth/oauth-lifecycle/index.js +11 -0
  165. package/dist/providers/auth/oauth-lifecycle/index.js.map +1 -0
  166. package/dist/providers/auth/oauth-lifecycle/path-resolver.d.ts +18 -0
  167. package/dist/providers/auth/oauth-lifecycle/path-resolver.js +121 -0
  168. package/dist/providers/auth/oauth-lifecycle/path-resolver.js.map +1 -0
  169. package/dist/providers/auth/oauth-lifecycle/throttle.d.ts +22 -0
  170. package/dist/providers/auth/oauth-lifecycle/throttle.js +37 -0
  171. package/dist/providers/auth/oauth-lifecycle/throttle.js.map +1 -0
  172. package/dist/providers/auth/oauth-lifecycle/token-helpers.d.ts +36 -0
  173. package/dist/providers/auth/oauth-lifecycle/token-helpers.js +127 -0
  174. package/dist/providers/auth/oauth-lifecycle/token-helpers.js.map +1 -0
  175. package/dist/providers/auth/oauth-lifecycle/token-io.d.ts +14 -0
  176. package/dist/providers/auth/oauth-lifecycle/token-io.js +151 -0
  177. package/dist/providers/auth/oauth-lifecycle/token-io.js.map +1 -0
  178. package/dist/providers/auth/oauth-lifecycle.js +70 -446
  179. package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
  180. package/dist/providers/auth/oauth-repair-cooldown.js +8 -3
  181. package/dist/providers/auth/oauth-repair-cooldown.js.map +1 -1
  182. package/dist/providers/auth/oauth-repair-env.js +5 -3
  183. package/dist/providers/auth/oauth-repair-env.js.map +1 -1
  184. package/dist/providers/auth/oauth-token-utils.d.ts +61 -0
  185. package/dist/providers/auth/oauth-token-utils.js +187 -0
  186. package/dist/providers/auth/oauth-token-utils.js.map +1 -0
  187. package/dist/providers/auth/oauth-utils/camoufox-helper.d.ts +21 -0
  188. package/dist/providers/auth/oauth-utils/camoufox-helper.js +82 -0
  189. package/dist/providers/auth/oauth-utils/camoufox-helper.js.map +1 -0
  190. package/dist/providers/auth/oauth-utils/error-extraction.d.ts +17 -0
  191. package/dist/providers/auth/oauth-utils/error-extraction.js +80 -0
  192. package/dist/providers/auth/oauth-utils/error-extraction.js.map +1 -0
  193. package/dist/providers/auth/oauth-utils/index.d.ts +7 -0
  194. package/dist/providers/auth/oauth-utils/index.js +8 -0
  195. package/dist/providers/auth/oauth-utils/index.js.map +1 -0
  196. package/dist/providers/auth/token-refresh/index.d.ts +6 -0
  197. package/dist/providers/auth/token-refresh/index.js +7 -0
  198. package/dist/providers/auth/token-refresh/index.js.map +1 -0
  199. package/dist/providers/auth/token-refresh/token-state.d.ts +17 -0
  200. package/dist/providers/auth/token-refresh/token-state.js +30 -0
  201. package/dist/providers/auth/token-refresh/token-state.js.map +1 -0
  202. package/dist/providers/auth/token-storage/index.d.ts +7 -0
  203. package/dist/providers/auth/token-storage/index.js +8 -0
  204. package/dist/providers/auth/token-storage/index.js.map +1 -0
  205. package/dist/providers/auth/token-storage/token-file-resolver.d.ts +12 -0
  206. package/dist/providers/auth/token-storage/token-file-resolver.js +117 -0
  207. package/dist/providers/auth/token-storage/token-file-resolver.js.map +1 -0
  208. package/dist/providers/auth/token-storage/token-persistence.d.ts +22 -0
  209. package/dist/providers/auth/token-storage/token-persistence.js +86 -0
  210. package/dist/providers/auth/token-storage/token-persistence.js.map +1 -0
  211. package/dist/providers/auth/tokenfile-auth.js +12 -9
  212. package/dist/providers/auth/tokenfile-auth.js.map +1 -1
  213. package/dist/providers/core/api/provider-config.d.ts +8 -0
  214. package/dist/providers/core/config/camoufox-launcher.d.ts +1 -0
  215. package/dist/providers/core/config/camoufox-launcher.js +40 -9
  216. package/dist/providers/core/config/camoufox-launcher.js.map +1 -1
  217. package/dist/providers/core/config/oauth-flows.js +7 -2
  218. package/dist/providers/core/config/oauth-flows.js.map +1 -1
  219. package/dist/providers/core/config/provider-debug-hooks.d.ts +0 -12
  220. package/dist/providers/core/config/provider-debug-hooks.js +16 -56
  221. package/dist/providers/core/config/provider-debug-hooks.js.map +1 -1
  222. package/dist/providers/core/config/provider-debug-output-utils.d.ts +38 -0
  223. package/dist/providers/core/config/provider-debug-output-utils.js +46 -0
  224. package/dist/providers/core/config/provider-debug-output-utils.js.map +1 -0
  225. package/dist/providers/core/config/provider-oauth-configs.js +13 -9
  226. package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
  227. package/dist/providers/core/runtime/antigravity-request-type.d.ts +2 -0
  228. package/dist/providers/core/runtime/antigravity-request-type.js +126 -0
  229. package/dist/providers/core/runtime/antigravity-request-type.js.map +1 -0
  230. package/dist/providers/core/runtime/base-provider-runtime-helpers.d.ts +27 -0
  231. package/dist/providers/core/runtime/base-provider-runtime-helpers.js +105 -0
  232. package/dist/providers/core/runtime/base-provider-runtime-helpers.js.map +1 -0
  233. package/dist/providers/core/runtime/base-provider-series-cooldown.d.ts +19 -0
  234. package/dist/providers/core/runtime/base-provider-series-cooldown.js +363 -0
  235. package/dist/providers/core/runtime/base-provider-series-cooldown.js.map +1 -0
  236. package/dist/providers/core/runtime/base-provider.d.ts +4 -35
  237. package/dist/providers/core/runtime/base-provider.js +20 -501
  238. package/dist/providers/core/runtime/base-provider.js.map +1 -1
  239. package/dist/providers/core/runtime/deepseek-http-provider-helpers.d.ts +32 -0
  240. package/dist/providers/core/runtime/deepseek-http-provider-helpers.js +301 -0
  241. package/dist/providers/core/runtime/deepseek-http-provider-helpers.js.map +1 -0
  242. package/dist/providers/core/runtime/deepseek-http-provider.d.ts +0 -3
  243. package/dist/providers/core/runtime/deepseek-http-provider.js +5 -127
  244. package/dist/providers/core/runtime/deepseek-http-provider.js.map +1 -1
  245. package/dist/providers/core/runtime/deepseek-session-pow-helpers.d.ts +21 -0
  246. package/dist/providers/core/runtime/deepseek-session-pow-helpers.js +99 -0
  247. package/dist/providers/core/runtime/deepseek-session-pow-helpers.js.map +1 -0
  248. package/dist/providers/core/runtime/deepseek-session-pow.js +17 -100
  249. package/dist/providers/core/runtime/deepseek-session-pow.js.map +1 -1
  250. package/dist/providers/core/runtime/gemini-cli-http-provider.d.ts +0 -15
  251. package/dist/providers/core/runtime/gemini-cli-http-provider.js +13 -303
  252. package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
  253. package/dist/providers/core/runtime/gemini-cli-response-postprocessor.d.ts +9 -0
  254. package/dist/providers/core/runtime/gemini-cli-response-postprocessor.js +85 -0
  255. package/dist/providers/core/runtime/gemini-cli-response-postprocessor.js.map +1 -0
  256. package/dist/providers/core/runtime/gemini-http-provider.js +2 -2
  257. package/dist/providers/core/runtime/gemini-http-provider.js.map +1 -1
  258. package/dist/providers/core/runtime/gemini-sse-normalizer.d.ts +25 -0
  259. package/dist/providers/core/runtime/gemini-sse-normalizer.js +159 -0
  260. package/dist/providers/core/runtime/gemini-sse-normalizer.js.map +1 -0
  261. package/dist/providers/core/runtime/http-request-executor.js +1 -48
  262. package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
  263. package/dist/providers/core/runtime/http-transport-provider.d.ts +2 -48
  264. package/dist/providers/core/runtime/http-transport-provider.js +158 -1273
  265. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  266. package/dist/providers/core/runtime/provider-bootstrap-utils.d.ts +20 -0
  267. package/dist/providers/core/runtime/provider-bootstrap-utils.js +78 -0
  268. package/dist/providers/core/runtime/provider-bootstrap-utils.js.map +1 -0
  269. package/dist/providers/core/runtime/provider-factory-helpers.d.ts +19 -0
  270. package/dist/providers/core/runtime/provider-factory-helpers.js +203 -0
  271. package/dist/providers/core/runtime/provider-factory-helpers.js.map +1 -0
  272. package/dist/providers/core/runtime/provider-factory.d.ts +9 -7
  273. package/dist/providers/core/runtime/provider-factory.js +57 -214
  274. package/dist/providers/core/runtime/provider-factory.js.map +1 -1
  275. package/dist/providers/core/runtime/provider-family-profile-utils.d.ts +23 -0
  276. package/dist/providers/core/runtime/provider-family-profile-utils.js +57 -0
  277. package/dist/providers/core/runtime/provider-family-profile-utils.js.map +1 -0
  278. package/dist/providers/core/runtime/provider-http-executor-utils.d.ts +32 -0
  279. package/dist/providers/core/runtime/provider-http-executor-utils.js +92 -0
  280. package/dist/providers/core/runtime/provider-http-executor-utils.js.map +1 -0
  281. package/dist/providers/core/runtime/provider-iflow-business-error-utils.d.ts +15 -0
  282. package/dist/providers/core/runtime/provider-iflow-business-error-utils.js +49 -0
  283. package/dist/providers/core/runtime/provider-iflow-business-error-utils.js.map +1 -0
  284. package/dist/providers/core/runtime/provider-request-executor-deps-factory.d.ts +29 -0
  285. package/dist/providers/core/runtime/provider-request-executor-deps-factory.js +41 -0
  286. package/dist/providers/core/runtime/provider-request-executor-deps-factory.js.map +1 -0
  287. package/dist/providers/core/runtime/provider-request-header-orchestrator.d.ts +30 -0
  288. package/dist/providers/core/runtime/provider-request-header-orchestrator.js +91 -0
  289. package/dist/providers/core/runtime/provider-request-header-orchestrator.js.map +1 -0
  290. package/dist/providers/core/runtime/provider-request-preprocessor.d.ts +5 -0
  291. package/dist/providers/core/runtime/provider-request-preprocessor.js +39 -0
  292. package/dist/providers/core/runtime/provider-request-preprocessor.js.map +1 -0
  293. package/dist/providers/core/runtime/provider-request-shaping-utils.d.ts +37 -0
  294. package/dist/providers/core/runtime/provider-request-shaping-utils.js +65 -0
  295. package/dist/providers/core/runtime/provider-request-shaping-utils.js.map +1 -0
  296. package/dist/providers/core/runtime/provider-response-postprocessor.d.ts +7 -0
  297. package/dist/providers/core/runtime/provider-response-postprocessor.js +28 -0
  298. package/dist/providers/core/runtime/provider-response-postprocessor.js.map +1 -0
  299. package/dist/providers/core/runtime/provider-runtime-utils.d.ts +16 -0
  300. package/dist/providers/core/runtime/provider-runtime-utils.js +51 -0
  301. package/dist/providers/core/runtime/provider-runtime-utils.js.map +1 -0
  302. package/dist/providers/core/runtime/responses-provider-helpers.d.ts +37 -0
  303. package/dist/providers/core/runtime/responses-provider-helpers.js +212 -0
  304. package/dist/providers/core/runtime/responses-provider-helpers.js.map +1 -0
  305. package/dist/providers/core/runtime/responses-provider.d.ts +0 -10
  306. package/dist/providers/core/runtime/responses-provider.js +14 -224
  307. package/dist/providers/core/runtime/responses-provider.js.map +1 -1
  308. package/dist/providers/core/runtime/runtime-endpoint-resolver.d.ts +21 -0
  309. package/dist/providers/core/runtime/runtime-endpoint-resolver.js +64 -0
  310. package/dist/providers/core/runtime/runtime-endpoint-resolver.js.map +1 -0
  311. package/dist/providers/core/runtime/service-profile-resolver.d.ts +29 -0
  312. package/dist/providers/core/runtime/service-profile-resolver.js +88 -0
  313. package/dist/providers/core/runtime/service-profile-resolver.js.map +1 -0
  314. package/dist/providers/core/runtime/transport/auth-mode-utils.d.ts +13 -0
  315. package/dist/providers/core/runtime/transport/auth-mode-utils.js +43 -0
  316. package/dist/providers/core/runtime/transport/auth-mode-utils.js.map +1 -0
  317. package/dist/providers/core/runtime/transport/auth-provider-factory.d.ts +37 -0
  318. package/dist/providers/core/runtime/transport/auth-provider-factory.js +131 -0
  319. package/dist/providers/core/runtime/transport/auth-provider-factory.js.map +1 -0
  320. package/dist/providers/core/runtime/transport/header-utils.d.ts +15 -0
  321. package/dist/providers/core/runtime/transport/header-utils.js +85 -0
  322. package/dist/providers/core/runtime/transport/header-utils.js.map +1 -0
  323. package/dist/providers/core/runtime/transport/iflow-signer.d.ts +12 -0
  324. package/dist/providers/core/runtime/transport/iflow-signer.js +63 -0
  325. package/dist/providers/core/runtime/transport/iflow-signer.js.map +1 -0
  326. package/dist/providers/core/runtime/transport/index.d.ts +15 -0
  327. package/dist/providers/core/runtime/transport/index.js +16 -0
  328. package/dist/providers/core/runtime/transport/index.js.map +1 -0
  329. package/dist/providers/core/runtime/transport/oauth-header-preflight.d.ts +12 -0
  330. package/dist/providers/core/runtime/transport/oauth-header-preflight.js +56 -0
  331. package/dist/providers/core/runtime/transport/oauth-header-preflight.js.map +1 -0
  332. package/dist/providers/core/runtime/transport/oauth-recovery-handler.d.ts +34 -0
  333. package/dist/providers/core/runtime/transport/oauth-recovery-handler.js +126 -0
  334. package/dist/providers/core/runtime/transport/oauth-recovery-handler.js.map +1 -0
  335. package/dist/providers/core/runtime/transport/provider-payload-utils.d.ts +21 -0
  336. package/dist/providers/core/runtime/transport/provider-payload-utils.js +88 -0
  337. package/dist/providers/core/runtime/transport/provider-payload-utils.js.map +1 -0
  338. package/dist/providers/core/runtime/transport/request-header-builder.d.ts +24 -0
  339. package/dist/providers/core/runtime/transport/request-header-builder.js +90 -0
  340. package/dist/providers/core/runtime/transport/request-header-builder.js.map +1 -0
  341. package/dist/providers/core/runtime/transport/runtime-detector.d.ts +22 -0
  342. package/dist/providers/core/runtime/transport/runtime-detector.js +63 -0
  343. package/dist/providers/core/runtime/transport/runtime-detector.js.map +1 -0
  344. package/dist/providers/core/runtime/transport/session-header-utils.d.ts +8 -0
  345. package/dist/providers/core/runtime/transport/session-header-utils.js +72 -0
  346. package/dist/providers/core/runtime/transport/session-header-utils.js.map +1 -0
  347. package/dist/providers/core/runtime/vision-debug-utils.d.ts +2 -0
  348. package/dist/providers/core/runtime/vision-debug-utils.js +31 -0
  349. package/dist/providers/core/runtime/vision-debug-utils.js.map +1 -1
  350. package/dist/providers/core/strategies/oauth-auth-code-flow.d.ts +13 -0
  351. package/dist/providers/core/strategies/oauth-auth-code-flow.js +272 -55
  352. package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
  353. package/dist/providers/core/utils/snapshot-writer-buffer.d.ts +13 -0
  354. package/dist/providers/core/utils/snapshot-writer-buffer.js +39 -0
  355. package/dist/providers/core/utils/snapshot-writer-buffer.js.map +1 -0
  356. package/dist/providers/core/utils/snapshot-writer.js +75 -54
  357. package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
  358. package/dist/providers/profile/families/antigravity-profile.js +2 -10
  359. package/dist/providers/profile/families/antigravity-profile.js.map +1 -1
  360. package/dist/providers/profile/families/deepseek-profile.d.ts +2 -0
  361. package/dist/providers/profile/families/deepseek-profile.js +110 -0
  362. package/dist/providers/profile/families/deepseek-profile.js.map +1 -0
  363. package/dist/providers/profile/families/iflow-profile.js +89 -10
  364. package/dist/providers/profile/families/iflow-profile.js.map +1 -1
  365. package/dist/providers/profile/provider-profile-loader.d.ts +5 -0
  366. package/dist/providers/profile/provider-profile-loader.js +35 -0
  367. package/dist/providers/profile/provider-profile-loader.js.map +1 -1
  368. package/dist/providers/profile/provider-profile.d.ts +16 -1
  369. package/dist/runtime/runtime-flags.js +1 -1
  370. package/dist/runtime/runtime-flags.js.map +1 -1
  371. package/dist/runtime/wasm-runtime/index.d.ts +56 -0
  372. package/dist/runtime/wasm-runtime/index.js +69 -0
  373. package/dist/runtime/wasm-runtime/index.js.map +1 -0
  374. package/dist/scripts/camoufox/launch-auth.mjs +158 -10
  375. package/dist/server/handlers/handler-response-utils.d.ts +13 -0
  376. package/dist/server/handlers/handler-response-utils.js +427 -0
  377. package/dist/server/handlers/handler-response-utils.js.map +1 -0
  378. package/dist/server/handlers/handler-utils.d.ts +2 -11
  379. package/dist/server/handlers/handler-utils.js +21 -421
  380. package/dist/server/handlers/handler-utils.js.map +1 -1
  381. package/dist/server/runtime/http-server/clock-client-reaper.d.ts +23 -0
  382. package/dist/server/runtime/http-server/clock-client-reaper.js +159 -0
  383. package/dist/server/runtime/http-server/clock-client-reaper.js.map +1 -0
  384. package/dist/server/runtime/http-server/clock-client-registry-utils.d.ts +87 -0
  385. package/dist/server/runtime/http-server/clock-client-registry-utils.js +276 -0
  386. package/dist/server/runtime/http-server/clock-client-registry-utils.js.map +1 -0
  387. package/dist/server/runtime/http-server/clock-client-registry.d.ts +5 -50
  388. package/dist/server/runtime/http-server/clock-client-registry.js +74 -320
  389. package/dist/server/runtime/http-server/clock-client-registry.js.map +1 -1
  390. package/dist/server/runtime/http-server/clock-client-route-utils.d.ts +12 -0
  391. package/dist/server/runtime/http-server/clock-client-route-utils.js +210 -0
  392. package/dist/server/runtime/http-server/clock-client-route-utils.js.map +1 -0
  393. package/dist/server/runtime/http-server/clock-client-routes.js +27 -201
  394. package/dist/server/runtime/http-server/clock-client-routes.js.map +1 -1
  395. package/dist/server/runtime/http-server/clock-daemon-log-throttle.d.ts +12 -0
  396. package/dist/server/runtime/http-server/clock-daemon-log-throttle.js +56 -0
  397. package/dist/server/runtime/http-server/clock-daemon-log-throttle.js.map +1 -0
  398. package/dist/server/runtime/http-server/daemon-admin/control-handler.js +143 -14
  399. package/dist/server/runtime/http-server/daemon-admin/control-handler.js.map +1 -1
  400. package/dist/server/runtime/http-server/daemon-admin/credentials-handler-utils.d.ts +19 -0
  401. package/dist/server/runtime/http-server/daemon-admin/credentials-handler-utils.js +107 -0
  402. package/dist/server/runtime/http-server/daemon-admin/credentials-handler-utils.js.map +1 -0
  403. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +2 -104
  404. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
  405. package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.d.ts +28 -0
  406. package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.js +298 -0
  407. package/dist/server/runtime/http-server/daemon-admin/providers-handler-routing-utils.js.map +1 -0
  408. package/dist/server/runtime/http-server/daemon-admin/providers-handler-utils.d.ts +22 -0
  409. package/dist/server/runtime/http-server/daemon-admin/providers-handler-utils.js +211 -0
  410. package/dist/server/runtime/http-server/daemon-admin/providers-handler-utils.js.map +1 -0
  411. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +25 -454
  412. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
  413. package/dist/server/runtime/http-server/daemon-admin/quota-handler.js +81 -32
  414. package/dist/server/runtime/http-server/daemon-admin/quota-handler.js.map +1 -1
  415. package/dist/server/runtime/http-server/daemon-admin/routecodex-x7e-gate.d.ts +22 -0
  416. package/dist/server/runtime/http-server/daemon-admin/routecodex-x7e-gate.js +70 -0
  417. package/dist/server/runtime/http-server/daemon-admin/routecodex-x7e-gate.js.map +1 -0
  418. package/dist/server/runtime/http-server/executor/antigravity-detector.d.ts +34 -0
  419. package/dist/server/runtime/http-server/executor/antigravity-detector.js +118 -0
  420. package/dist/server/runtime/http-server/executor/antigravity-detector.js.map +1 -0
  421. package/dist/server/runtime/http-server/executor/env-config.d.ts +13 -0
  422. package/dist/server/runtime/http-server/executor/env-config.js +20 -0
  423. package/dist/server/runtime/http-server/executor/env-config.js.map +1 -0
  424. package/dist/server/runtime/http-server/executor/index.d.ts +11 -0
  425. package/dist/server/runtime/http-server/executor/index.js +18 -0
  426. package/dist/server/runtime/http-server/executor/index.js.map +1 -0
  427. package/dist/server/runtime/http-server/executor/provider-request-context.d.ts +20 -0
  428. package/dist/server/runtime/http-server/executor/provider-request-context.js +38 -0
  429. package/dist/server/runtime/http-server/executor/provider-request-context.js.map +1 -0
  430. package/dist/server/runtime/http-server/executor/provider-response-converter.d.ts +23 -0
  431. package/dist/server/runtime/http-server/executor/provider-response-converter.js +337 -0
  432. package/dist/server/runtime/http-server/executor/provider-response-converter.js.map +1 -0
  433. package/dist/server/runtime/http-server/executor/provider-response-utils.d.ts +8 -0
  434. package/dist/server/runtime/http-server/executor/provider-response-utils.js +93 -0
  435. package/dist/server/runtime/http-server/executor/provider-response-utils.js.map +1 -0
  436. package/dist/server/runtime/http-server/executor/provider-runtime-resolver.d.ts +23 -0
  437. package/dist/server/runtime/http-server/executor/provider-runtime-resolver.js +85 -0
  438. package/dist/server/runtime/http-server/executor/provider-runtime-resolver.js.map +1 -0
  439. package/dist/server/runtime/http-server/executor/request-executor-core-utils.d.ts +7 -0
  440. package/dist/server/runtime/http-server/executor/request-executor-core-utils.js +39 -0
  441. package/dist/server/runtime/http-server/executor/request-executor-core-utils.js.map +1 -0
  442. package/dist/server/runtime/http-server/executor/request-retry-helpers.d.ts +16 -0
  443. package/dist/server/runtime/http-server/executor/request-retry-helpers.js +218 -0
  444. package/dist/server/runtime/http-server/executor/request-retry-helpers.js.map +1 -0
  445. package/dist/server/runtime/http-server/executor/retry-engine.d.ts +21 -0
  446. package/dist/server/runtime/http-server/executor/retry-engine.js +73 -0
  447. package/dist/server/runtime/http-server/executor/retry-engine.js.map +1 -0
  448. package/dist/server/runtime/http-server/executor/sse-error-handler.d.ts +14 -0
  449. package/dist/server/runtime/http-server/executor/sse-error-handler.js +127 -0
  450. package/dist/server/runtime/http-server/executor/sse-error-handler.js.map +1 -0
  451. package/dist/server/runtime/http-server/executor/usage-aggregator.d.ts +39 -0
  452. package/dist/server/runtime/http-server/executor/usage-aggregator.js +177 -0
  453. package/dist/server/runtime/http-server/executor/usage-aggregator.js.map +1 -0
  454. package/dist/server/runtime/http-server/executor/usage-logger.d.ts +7 -0
  455. package/dist/server/runtime/http-server/executor/usage-logger.js +13 -0
  456. package/dist/server/runtime/http-server/executor/usage-logger.js.map +1 -0
  457. package/dist/server/runtime/http-server/executor/utils.d.ts +21 -0
  458. package/dist/server/runtime/http-server/executor/utils.js +62 -0
  459. package/dist/server/runtime/http-server/executor/utils.js.map +1 -0
  460. package/dist/server/runtime/http-server/executor-metadata.js +83 -2
  461. package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
  462. package/dist/server/runtime/http-server/executor-response.js +17 -9
  463. package/dist/server/runtime/http-server/executor-response.js.map +1 -1
  464. package/dist/server/runtime/http-server/http-server-bootstrap.d.ts +31 -0
  465. package/dist/server/runtime/http-server/http-server-bootstrap.js +367 -0
  466. package/dist/server/runtime/http-server/http-server-bootstrap.js.map +1 -0
  467. package/dist/server/runtime/http-server/http-server-clock-daemon.d.ts +5 -0
  468. package/dist/server/runtime/http-server/http-server-clock-daemon.js +242 -0
  469. package/dist/server/runtime/http-server/http-server-clock-daemon.js.map +1 -0
  470. package/dist/server/runtime/http-server/http-server-legacy-pipeline.d.ts +2 -0
  471. package/dist/server/runtime/http-server/http-server-legacy-pipeline.js +65 -0
  472. package/dist/server/runtime/http-server/http-server-legacy-pipeline.js.map +1 -0
  473. package/dist/server/runtime/http-server/http-server-lifecycle.d.ts +27 -0
  474. package/dist/server/runtime/http-server/http-server-lifecycle.js +285 -0
  475. package/dist/server/runtime/http-server/http-server-lifecycle.js.map +1 -0
  476. package/dist/server/runtime/http-server/http-server-runtime-providers.d.ts +10 -0
  477. package/dist/server/runtime/http-server/http-server-runtime-providers.js +415 -0
  478. package/dist/server/runtime/http-server/http-server-runtime-providers.js.map +1 -0
  479. package/dist/server/runtime/http-server/http-server-runtime-setup.d.ts +2 -0
  480. package/dist/server/runtime/http-server/http-server-runtime-setup.js +93 -0
  481. package/dist/server/runtime/http-server/http-server-runtime-setup.js.map +1 -0
  482. package/dist/server/runtime/http-server/index.d.ts +4 -46
  483. package/dist/server/runtime/http-server/index.js +98 -2577
  484. package/dist/server/runtime/http-server/index.js.map +1 -1
  485. package/dist/server/runtime/http-server/request-executor.d.ts +8 -21
  486. package/dist/server/runtime/http-server/request-executor.js +94 -956
  487. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  488. package/dist/server/runtime/http-server/routes.js +2 -2
  489. package/dist/server/runtime/http-server/routes.js.map +1 -1
  490. package/dist/server/runtime/http-server/servertool-admin-state.d.ts +42 -0
  491. package/dist/server/runtime/http-server/servertool-admin-state.js +210 -0
  492. package/dist/server/runtime/http-server/servertool-admin-state.js.map +1 -0
  493. package/dist/server/runtime/http-server/stats-manager-internals.d.ts +96 -0
  494. package/dist/server/runtime/http-server/stats-manager-internals.js +311 -0
  495. package/dist/server/runtime/http-server/stats-manager-internals.js.map +1 -0
  496. package/dist/server/runtime/http-server/stats-manager-table.d.ts +6 -0
  497. package/dist/server/runtime/http-server/stats-manager-table.js +135 -0
  498. package/dist/server/runtime/http-server/stats-manager-table.js.map +1 -0
  499. package/dist/server/runtime/http-server/stats-manager.d.ts +0 -23
  500. package/dist/server/runtime/http-server/stats-manager.js +95 -483
  501. package/dist/server/runtime/http-server/stats-manager.js.map +1 -1
  502. package/dist/server/utils/client-connection-state.js +61 -0
  503. package/dist/server/utils/client-connection-state.js.map +1 -1
  504. package/dist/server/utils/request-id-manager.d.ts +6 -0
  505. package/dist/server/utils/request-id-manager.js +105 -15
  506. package/dist/server/utils/request-id-manager.js.map +1 -1
  507. package/dist/server/utils/stage-logger.js +14 -4
  508. package/dist/server/utils/stage-logger.js.map +1 -1
  509. package/dist/server-lifecycle/index.d.ts +6 -0
  510. package/dist/server-lifecycle/index.js +7 -0
  511. package/dist/server-lifecycle/index.js.map +1 -0
  512. package/dist/server-lifecycle/port-utils.d.ts +18 -0
  513. package/dist/server-lifecycle/port-utils.js +204 -0
  514. package/dist/server-lifecycle/port-utils.js.map +1 -0
  515. package/dist/sharedmodule/process-snapshot.d.ts +26 -0
  516. package/dist/sharedmodule/process-snapshot.js +141 -0
  517. package/dist/sharedmodule/process-snapshot.js.map +1 -0
  518. package/dist/token-daemon/index.js +10 -3
  519. package/dist/token-daemon/index.js.map +1 -1
  520. package/dist/token-daemon/token-daemon.js +9 -11
  521. package/dist/token-daemon/token-daemon.js.map +1 -1
  522. package/dist/token-daemon/token-utils.d.ts +1 -1
  523. package/dist/token-daemon/token-utils.js +19 -3
  524. package/dist/token-daemon/token-utils.js.map +1 -1
  525. package/dist/tools/semantic-replay-snapshot-loader.d.ts +4 -0
  526. package/dist/tools/semantic-replay-snapshot-loader.js +396 -0
  527. package/dist/tools/semantic-replay-snapshot-loader.js.map +1 -0
  528. package/dist/tools/semantic-replay.js +2 -393
  529. package/dist/tools/semantic-replay.js.map +1 -1
  530. package/dist/utils/daemon-stop-intent.d.ts +17 -0
  531. package/dist/utils/daemon-stop-intent.js +104 -0
  532. package/dist/utils/daemon-stop-intent.js.map +1 -0
  533. package/dist/utils/key-429-tracker.js +6 -5
  534. package/dist/utils/key-429-tracker.js.map +1 -1
  535. package/dist/utils/pipeline-health-manager.js +7 -6
  536. package/dist/utils/pipeline-health-manager.js.map +1 -1
  537. package/dist/utils/process-lifecycle-logger.js +45 -1
  538. package/dist/utils/process-lifecycle-logger.js.map +1 -1
  539. package/dist/utils/runtime-exit-forensics.d.ts +2 -2
  540. package/dist/utils/runtime-exit-forensics.js +10 -5
  541. package/dist/utils/runtime-exit-forensics.js.map +1 -1
  542. package/dist/utils/snapshot-writer.js +3 -3
  543. package/dist/utils/snapshot-writer.js.map +1 -1
  544. package/docs/QUOTA_MANAGER_V3.md +3 -0
  545. package/docs/VIRTUAL_ROUTER_PRIORITY_AND_HEALTH.md +114 -0
  546. package/docs/daemon-admin-ui.html +268 -11
  547. package/docs/file-line-limit-gate.md +30 -0
  548. package/docs/refactoring/host-164.3-responsibility-migration.md +62 -0
  549. package/docs/release-iflow-400-gate.md +58 -0
  550. package/docs/replay-evidence-iflow-400.txt +33 -0
  551. package/docs/stop-message-auto.md +4 -3
  552. package/package.json +4 -3
  553. package/scripts/auth-iflow-manual.mjs +13 -23
  554. package/scripts/camoufox/launch-auth.mjs +158 -10
  555. package/scripts/ci/check-file-line-limit.mjs +149 -0
  556. package/scripts/copy-compat-assets.mjs +26 -0
  557. package/scripts/copy-modules-config.mjs +18 -0
  558. package/scripts/tests/ci-jest.mjs +4 -0
  559. package/scripts/verify-codex-error-samples.mjs +27 -6
@@ -8,139 +8,29 @@
8
8
  * - 保持API兼容性
9
9
  */
10
10
  import express, {} from 'express';
11
- import * as fs from 'node:fs/promises';
12
- import path from 'node:path';
13
11
  import { ErrorHandlingCenter } from 'rcc-errorhandling';
14
- import { ProviderFactory } from '../../../providers/core/runtime/provider-factory.js';
15
12
  import { PipelineDebugLogger as PipelineDebugLoggerImpl } from '../../../modules/pipeline/utils/debug-logger.js';
16
- import { attachProviderRuntimeMetadata } from '../../../providers/core/runtime/provider-runtime-metadata.js';
17
- import { preloadAntigravityAliasUserAgents, primeAntigravityUserAgentVersion } from '../../../providers/auth/antigravity-user-agent.js';
18
- import { getAntigravityWarmupBlacklistDurationMs, isAntigravityWarmupEnabled, warmupCheckAntigravityAlias } from '../../../providers/auth/antigravity-warmup.js';
19
- import { shutdownCamoufoxLaunchers } from '../../../providers/core/config/camoufox-launcher.js';
20
13
  import { AuthFileResolver } from '../../../config/auth-file-resolver.js';
21
- import { buildProviderProfiles } from '../../../providers/profile/provider-profile-loader.js';
22
- import { isStageLoggingEnabled, logPipelineStage } from '../../utils/stage-logger.js';
14
+ import { isStageLoggingEnabled } from '../../utils/stage-logger.js';
23
15
  import { registerApiKeyAuthMiddleware, registerDefaultMiddleware } from './middleware.js';
24
- import { registerHttpRoutes, registerOAuthPortalRoute } from './routes.js';
25
- import { mapProviderProtocol, normalizeProviderType, resolveProviderIdentity, asRecord } from './provider-utils.js';
16
+ import { registerOAuthPortalRoute } from './routes.js';
26
17
  import { resolveRepoRoot } from './llmswitch-loader.js';
27
- import { enhanceProviderRequestId } from '../../utils/request-id-manager.js';
28
- import { convertProviderResponse as bridgeConvertProviderResponse, createSnapshotRecorder as bridgeCreateSnapshotRecorder, rebindResponsesConversationRequestId, extractSessionIdentifiersFromMetadata, bootstrapVirtualRouterConfig, getProviderSuccessCenter, getHubPipelineCtor, resolveClockConfigSnapshot, reserveClockDueTasks, commitClockDueReservation, clearClockTasksSnapshot } from '../../../modules/llmswitch/bridge.js';
29
- import { initializeRouteErrorHub, reportRouteError } from '../../../error-handling/route-error-hub.js';
30
- import { writeClientSnapshot } from '../../../providers/core/utils/snapshot-writer.js';
31
18
  import { createServerColoredLogger } from './colored-logger.js';
32
- import { formatValueForConsole } from '../../../utils/logger.js';
33
19
  import { QuietErrorHandlingCenter } from '../../../error-handling/quiet-error-handling-center.js';
34
- import { describeRetryReason, isNetworkTransportError, shouldRetryProviderError, waitBeforeRetry } from './executor-provider.js';
35
20
  import { ManagerDaemon } from '../../../manager/index.js';
36
- import { HealthManagerModule } from '../../../manager/modules/health/index.js';
37
- import { RoutingStateManagerModule } from '../../../manager/modules/routing/index.js';
38
- import { TokenManagerModule } from '../../../manager/modules/token/index.js';
39
21
  import { ensureServerScopedSessionDir } from './session-dir.js';
40
22
  import { canonicalizeServerId } from './server-id.js';
41
23
  import { StatsManager } from './stats-manager.js';
42
- import { loadRouteCodexConfig } from '../../../config/routecodex-config-loader.js';
43
- import { buildInfo } from '../../../build-info.js';
44
- import { getClockClientRegistry } from './clock-client-registry.js';
45
- import { toExactMatchClockConfig } from './clock-daemon-inject-config.js';
46
- import { isTmuxSessionAlive, killManagedTmuxSession } from './tmux-session-probe.js';
47
- import { terminateManagedClientProcess } from './managed-process-probe.js';
48
- import { recordHubShadowCompareDiff, resolveHubShadowCompareConfig, shouldRunHubShadowCompare } from './hub-shadow-compare.js';
49
- import { recordLlmsEngineShadowDiff, isLlmsEngineShadowEnabledForSubpath, resolveLlmsEngineShadowConfig, shouldRunLlmsEngineShadowForSubpath } from '../../../utils/llms-engine-shadow.js';
50
- import { resolveLlmswitchCoreVersion } from '../../../utils/runtime-versions.js';
51
- const DEFAULT_MAX_PROVIDER_ATTEMPTS = 6;
52
- const DEFAULT_ANTIGRAVITY_MAX_PROVIDER_ATTEMPTS = 20;
53
- const RETRYABLE_SSE_ERROR_CODE_HINTS = [
54
- 'internal_network_failure',
55
- 'network_error',
56
- 'api_connection_error',
57
- 'service_unavailable',
58
- 'internal_server_error',
59
- 'overloaded_error',
60
- 'rate_limit_error',
61
- 'request_timeout',
62
- 'timeout'
63
- ];
64
- const RETRYABLE_SSE_MESSAGE_HINTS = [
65
- 'internal network failure',
66
- 'network failure',
67
- 'network error',
68
- 'temporarily unavailable',
69
- 'temporarily unreachable',
70
- 'upstream disconnected',
71
- 'connection reset',
72
- 'connection closed',
73
- 'timed out',
74
- 'timeout'
75
- ];
76
- function firstNonEmptyString(candidates) {
77
- for (const candidate of candidates) {
78
- if (typeof candidate !== 'string') {
79
- continue;
80
- }
81
- const trimmed = candidate.trim();
82
- if (trimmed) {
83
- return trimmed;
84
- }
85
- }
86
- return undefined;
87
- }
88
- function firstFiniteNumber(candidates) {
89
- for (const candidate of candidates) {
90
- if (typeof candidate === 'number' && Number.isFinite(candidate)) {
91
- return candidate;
92
- }
93
- }
94
- return undefined;
95
- }
96
- function isRetryableSseWrapperError(message, errorCode, status) {
97
- if (typeof status === 'number' && Number.isFinite(status)) {
98
- if (status === 408 || status === 425 || status === 429 || status >= 500) {
99
- return true;
100
- }
101
- }
102
- const normalizedCode = typeof errorCode === 'string' ? errorCode.trim().toLowerCase() : '';
103
- if (normalizedCode && RETRYABLE_SSE_ERROR_CODE_HINTS.some((hint) => normalizedCode.includes(hint))) {
104
- return true;
105
- }
106
- const loweredMessage = message.toLowerCase();
107
- return RETRYABLE_SSE_MESSAGE_HINTS.some((hint) => loweredMessage.includes(hint));
108
- }
109
- function resolveMaxProviderAttempts() {
110
- const raw = String(process.env.ROUTECODEX_MAX_PROVIDER_ATTEMPTS || process.env.RCC_MAX_PROVIDER_ATTEMPTS || '')
111
- .trim()
112
- .toLowerCase();
113
- const parsed = raw ? Number.parseInt(raw, 10) : NaN;
114
- const candidate = Number.isFinite(parsed) ? parsed : DEFAULT_MAX_PROVIDER_ATTEMPTS;
115
- return Math.max(1, Math.min(20, candidate));
116
- }
117
- function resolveAntigravityMaxProviderAttempts() {
118
- const raw = String(process.env.ROUTECODEX_ANTIGRAVITY_MAX_PROVIDER_ATTEMPTS || process.env.RCC_ANTIGRAVITY_MAX_PROVIDER_ATTEMPTS || '')
119
- .trim()
120
- .toLowerCase();
121
- const parsed = raw ? Number.parseInt(raw, 10) : NaN;
122
- const candidate = Number.isFinite(parsed) ? parsed : DEFAULT_ANTIGRAVITY_MAX_PROVIDER_ATTEMPTS;
123
- return Math.max(1, Math.min(60, candidate));
124
- }
125
- function isAntigravityProviderKey(providerKey) {
126
- return typeof providerKey === 'string' && providerKey.startsWith('antigravity.');
127
- }
128
- function extractStatusCodeFromError(err) {
129
- if (!err || typeof err !== 'object')
130
- return undefined;
131
- const direct = err.statusCode;
132
- if (typeof direct === 'number')
133
- return direct;
134
- const nested = err.status;
135
- if (typeof nested === 'number')
136
- return nested;
137
- return undefined;
138
- }
139
- /**
140
- * RouteCodex Server V2
141
- *
142
- * 与V1完全并行实现,集成系统hooks
143
- */
24
+ import { resolveHubShadowCompareConfig } from './hub-shadow-compare.js';
25
+ import { resolveLlmsEngineShadowConfig } from '../../../utils/llms-engine-shadow.js';
26
+ import { createRequestExecutor } from './request-executor.js';
27
+ import { startClockReaper, stopClockReaper } from './clock-client-reaper.js';
28
+ import { resolveVirtualRouterInput, getModuleDependencies, registerDaemonAdminUiRoute, getErrorHandlingShim, createDebugCenterShim, updateProviderProfiles, ensureProviderProfilesFromUserConfig, tryBuildProfiles, findProviderProfile, applyProviderProfileOverrides, canonicalizeRuntimeProvider, logStage, extractProviderModel, buildProviderLabel, normalizeAuthType, resolveSecretValue, isSafeSecretReference, bootstrapVirtualRouter, ensureHubPipelineCtor, ensureHubPipelineEngineShadow, isPipelineReady, waitForRuntimeReady, isQuotaRoutingEnabled, shouldStartManagerDaemon, initializeRouteErrorHub } from './http-server-bootstrap.js';
29
+ import { shouldEnableClockDaemonInjectLoop, resolveRawClockConfig, stopClockDaemonInjectLoop, startClockDaemonInjectLoop, tickClockDaemonInjectLoop } from './http-server-clock-daemon.js';
30
+ import { setupRuntime } from './http-server-runtime-setup.js';
31
+ import { initializeProviderRuntimes, createProviderHandle, materializeRuntimeProfile, normalizeRuntimeBaseUrl, resolveRuntimeAuth, resolveApiKeyValue, isLocalBaseUrl, disposeProviders } from './http-server-runtime-providers.js';
32
+ import { initializeHttpServer, restartRuntimeFromDisk, startHttpServer, stopHttpServer, getHttpServerStatus, getHttpServerConfig, isHttpServerInitialized, isHttpServerRunning, handleHttpServerError, initializeWithUserConfig, reloadHttpServerRuntime, buildHttpHandlerContext } from './http-server-lifecycle.js';
33
+ import { executePipelineViaLegacyOverride } from './http-server-legacy-pipeline.js';
144
34
  export class RouteCodexHttpServer {
145
35
  app;
146
36
  server;
@@ -149,11 +39,12 @@ export class RouteCodexHttpServer {
149
39
  errorHandling;
150
40
  _isInitialized = false;
151
41
  _isRunning = false;
152
- // Runtime state
153
42
  hubPipeline = null;
154
43
  providerHandles = new Map();
155
44
  providerKeyToRuntimeKey = new Map();
156
45
  providerRuntimeInitErrors = new Map();
46
+ runtimeKeyCredentialSkipped = new Set();
47
+ startupExcludedProviderKeys = new Set();
157
48
  pipelineLogger = createNoopPipelineLogger();
158
49
  authResolver = new AuthFileResolver();
159
50
  userConfig = {};
@@ -182,14 +73,15 @@ export class RouteCodexHttpServer {
182
73
  clockDaemonInjectTimer = null;
183
74
  clockDaemonInjectTickInFlight = false;
184
75
  lastClockDaemonInjectErrorAtMs = 0;
76
+ clockDaemonInjectSkipLogByKey = new Map();
185
77
  lastClockDaemonCleanupAtMs = 0;
78
+ requestExecutor;
186
79
  constructor(config) {
187
80
  this.config = config;
188
81
  this.app = express();
189
82
  this.errorHandling = new QuietErrorHandlingCenter();
190
83
  this.stageLoggingEnabled = isStageLoggingEnabled();
191
84
  this.repoRoot = resolveRepoRoot(import.meta.url);
192
- // Ensure session-scoped routing state does not leak across server instances.
193
85
  ensureServerScopedSessionDir(canonicalizeServerId(this.config.server.host, this.config.server.port));
194
86
  try {
195
87
  this.pipelineLogger = new PipelineDebugLoggerImpl({ colored: this.coloredLogger }, { enableConsoleLogging: true });
@@ -202,8 +94,28 @@ export class RouteCodexHttpServer {
202
94
  this.runtimeReadyResolve = resolve;
203
95
  this.runtimeReadyReject = reject;
204
96
  });
205
- // Register critical routes early (before provider initialization)
206
- // This ensures OAuth Portal is available when providers check token validity
97
+ this.requestExecutor = createRequestExecutor({
98
+ runtimeManager: {
99
+ resolveRuntimeKey: (providerKey, fallback) => {
100
+ if (providerKey && this.providerKeyToRuntimeKey.has(providerKey)) {
101
+ return this.providerKeyToRuntimeKey.get(providerKey);
102
+ }
103
+ return fallback;
104
+ },
105
+ getHandleByRuntimeKey: (runtimeKey) => {
106
+ if (!runtimeKey) {
107
+ return undefined;
108
+ }
109
+ return this.providerHandles.get(runtimeKey);
110
+ }
111
+ },
112
+ getHubPipeline: () => this.hubPipeline,
113
+ getModuleDependencies: () => this.getModuleDependencies(),
114
+ logStage: (stage, requestId, details) => {
115
+ this.logStage(stage, requestId, details);
116
+ },
117
+ stats: this.stats
118
+ });
207
119
  registerApiKeyAuthMiddleware(this.app, this.config);
208
120
  registerDefaultMiddleware(this.app);
209
121
  registerOAuthPortalRoute(this.app);
@@ -212,2558 +124,168 @@ export class RouteCodexHttpServer {
212
124
  console.log('[RouteCodexHttpServer] Initialized (pipeline=hub)');
213
125
  }
214
126
  resolveVirtualRouterInput(userConfig) {
215
- if (userConfig?.virtualrouter && typeof userConfig.virtualrouter === 'object') {
216
- return userConfig.virtualrouter;
217
- }
218
- return userConfig;
127
+ return resolveVirtualRouterInput(this, userConfig);
219
128
  }
220
129
  getModuleDependencies() {
221
- if (!this.moduleDependencies) {
222
- this.moduleDependencies = {
223
- errorHandlingCenter: this.getErrorHandlingShim(),
224
- debugCenter: this.createDebugCenterShim(),
225
- logger: this.pipelineLogger
226
- };
227
- }
228
- return this.moduleDependencies;
130
+ return getModuleDependencies(this);
229
131
  }
230
- /**
231
- * Register Daemon Admin UI route.
232
- * Serves docs/daemon-admin-ui.html as a static page.
233
- * Note: daemon-admin UI/API now uses password login (stored at ~/.routecodex/login) instead of httpserver.apikey.
234
- */
235
132
  registerDaemonAdminUiRoute() {
236
- this.app.get('/daemon/admin', async (req, res) => {
237
- try {
238
- const fs = await import('node:fs/promises');
239
- let html = '';
240
- try {
241
- const filePath = new URL('../../../../docs/daemon-admin-ui.html', import.meta.url);
242
- html = await fs.readFile(filePath, 'utf8');
243
- }
244
- catch {
245
- // build output reads from dist/docs; fallback to cwd/docs for dev runners
246
- const path = await import('node:path');
247
- const fallback = path.join(process.cwd(), 'docs', 'daemon-admin-ui.html');
248
- html = await fs.readFile(fallback, 'utf8');
249
- }
250
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
251
- // Avoid stale admin UI in browsers / proxies after upgrades.
252
- res.setHeader('Cache-Control', 'no-store, max-age=0');
253
- res.setHeader('Pragma', 'no-cache');
254
- res.setHeader('X-RouteCodex-Version', buildInfo?.version ? String(buildInfo.version) : String(process.env.ROUTECODEX_VERSION || 'dev'));
255
- res.send(html);
256
- }
257
- catch (error) {
258
- const message = error instanceof Error ? error.message : String(error);
259
- res.status(500).json({
260
- error: {
261
- message: `Daemon admin UI not available: ${message}`
262
- }
263
- });
264
- }
265
- });
133
+ registerDaemonAdminUiRoute(this);
266
134
  }
267
135
  getErrorHandlingShim() {
268
- if (!this.errorHandlingShim) {
269
- this.errorHandlingShim = {
270
- handleError: async (errorPayload, contextPayload) => {
271
- const sanitizedError = formatErrorForErrorCenter(errorPayload);
272
- const sanitizedContext = formatErrorForErrorCenter(contextPayload);
273
- await this.errorHandling.handleError({
274
- error: sanitizedError,
275
- source: 'pipeline',
276
- severity: 'medium',
277
- timestamp: Date.now(),
278
- context: sanitizedContext
279
- });
280
- },
281
- createContext: () => ({}),
282
- getStatistics: () => ({})
283
- };
284
- }
285
- return this.errorHandlingShim;
136
+ return getErrorHandlingShim(this);
286
137
  }
287
138
  createDebugCenterShim() {
288
- return {
289
- logDebug: () => { },
290
- logError: () => { },
291
- logModule: () => { },
292
- processDebugEvent: () => { },
293
- getLogs: () => []
294
- };
139
+ return createDebugCenterShim();
295
140
  }
296
141
  updateProviderProfiles(collection, rawConfig) {
297
- this.providerProfileIndex.clear();
298
- const source = collection ?? this.tryBuildProfiles(rawConfig);
299
- if (!source) {
300
- return;
301
- }
302
- for (const profile of source.profiles) {
303
- if (profile && typeof profile.id === 'string' && profile.id.trim()) {
304
- this.providerProfileIndex.set(profile.id.trim(), profile);
305
- }
306
- }
142
+ updateProviderProfiles(this, collection, rawConfig);
307
143
  }
308
144
  ensureProviderProfilesFromUserConfig() {
309
- if (this.providerProfileIndex.size > 0) {
310
- return;
311
- }
312
- const fallback = this.tryBuildProfiles(this.userConfig);
313
- if (!fallback) {
314
- return;
315
- }
316
- for (const profile of fallback.profiles) {
317
- if (profile && typeof profile.id === 'string' && profile.id.trim()) {
318
- this.providerProfileIndex.set(profile.id.trim(), profile);
319
- }
320
- }
145
+ ensureProviderProfilesFromUserConfig(this);
321
146
  }
322
147
  tryBuildProfiles(config) {
323
- if (!config) {
324
- return null;
325
- }
326
- try {
327
- return buildProviderProfiles(config);
328
- }
329
- catch {
330
- return null;
331
- }
148
+ return tryBuildProfiles(config);
332
149
  }
333
150
  findProviderProfile(runtime) {
334
- const candidates = new Set();
335
- const pushCandidate = (value) => {
336
- if (typeof value === 'string' && value.trim()) {
337
- candidates.add(value.trim());
338
- }
339
- };
340
- pushCandidate(runtime.providerId);
341
- if (runtime.providerKey && runtime.providerKey.includes('.')) {
342
- pushCandidate(runtime.providerKey.split('.')[0]);
343
- }
344
- if (runtime.runtimeKey && runtime.runtimeKey.includes('.')) {
345
- pushCandidate(runtime.runtimeKey.split('.')[0]);
346
- }
347
- for (const candidate of candidates) {
348
- const profile = this.providerProfileIndex.get(candidate);
349
- if (profile) {
350
- return profile;
351
- }
352
- }
353
- return undefined;
151
+ return findProviderProfile(this, runtime);
354
152
  }
355
153
  applyProviderProfileOverrides(runtime) {
356
- const profile = this.findProviderProfile(runtime);
357
- if (!profile) {
358
- return this.canonicalizeRuntimeProvider(runtime);
359
- }
360
- const patched = { ...runtime };
361
- const originalFamily = patched.providerFamily || patched.providerType;
362
- patched.providerFamily = originalFamily;
363
- patched.providerType = profile.protocol;
364
- if (profile.moduleType && profile.moduleType.trim()) {
365
- patched.providerModule = profile.moduleType.trim();
366
- }
367
- if (!patched.baseUrl && profile.transport.baseUrl) {
368
- patched.baseUrl = profile.transport.baseUrl;
369
- }
370
- if (!patched.endpoint && profile.transport.endpoint) {
371
- patched.endpoint = profile.transport.endpoint;
372
- }
373
- if (!patched.headers && profile.transport.headers) {
374
- patched.headers = profile.transport.headers;
375
- }
376
- if (patched.timeoutMs === undefined && typeof profile.transport.timeoutMs === 'number') {
377
- patched.timeoutMs = profile.transport.timeoutMs;
378
- }
379
- if (patched.maxRetries === undefined && typeof profile.transport.maxRetries === 'number') {
380
- patched.maxRetries = profile.transport.maxRetries;
381
- }
382
- if (!patched.compatibilityProfile && profile.compatibilityProfile) {
383
- patched.compatibilityProfile = profile.compatibilityProfile;
384
- }
385
- if (!patched.defaultModel && profile.metadata?.defaultModel) {
386
- patched.defaultModel = profile.metadata.defaultModel;
387
- }
388
- if (!patched.deepseek && profile.metadata?.deepseek) {
389
- patched.deepseek = profile.metadata.deepseek;
390
- }
391
- return this.canonicalizeRuntimeProvider(patched);
154
+ return applyProviderProfileOverrides(this, runtime);
392
155
  }
393
156
  canonicalizeRuntimeProvider(runtime) {
394
- const { providerType, providerFamily } = resolveProviderIdentity(runtime.providerType, runtime.providerFamily);
395
- return {
396
- ...runtime,
397
- providerType: providerType,
398
- providerFamily
399
- };
157
+ return canonicalizeRuntimeProvider(runtime);
400
158
  }
401
159
  logStage(stage, requestId, details) {
402
- if (!this.stageLoggingEnabled) {
403
- return;
404
- }
405
- logPipelineStage(stage, requestId, details);
160
+ logStage(this, stage, requestId, details);
406
161
  }
407
162
  extractProviderModel(payload) {
408
- if (!payload) {
409
- return undefined;
410
- }
411
- const source = payload.data && typeof payload.data === 'object'
412
- ? payload.data
413
- : payload;
414
- const raw = source.model;
415
- if (typeof raw === 'string' && raw.trim()) {
416
- return raw.trim();
417
- }
418
- return undefined;
163
+ return extractProviderModel(this, payload);
419
164
  }
420
165
  buildProviderLabel(providerKey, model) {
421
- const key = typeof providerKey === 'string' && providerKey.trim() ? providerKey.trim() : undefined;
422
- const modelId = typeof model === 'string' && model.trim() ? model.trim() : undefined;
423
- if (!key && !modelId) {
424
- return undefined;
425
- }
426
- if (key && modelId) {
427
- return `${key}.${modelId}`;
428
- }
429
- return key || modelId;
166
+ return buildProviderLabel(this, providerKey, model);
430
167
  }
431
168
  normalizeAuthType(input) {
432
- const value = typeof input === 'string' ? input.toLowerCase() : '';
433
- if (value.includes('oauth')) {
434
- return 'oauth';
435
- }
436
- return 'apikey';
169
+ return normalizeAuthType(this, input);
437
170
  }
438
171
  async resolveSecretValue(raw) {
439
- if (!raw) {
440
- throw new Error('Secret reference is required but missing');
441
- }
442
- const trimmed = raw.trim();
443
- const envMatch = trimmed.match(/^\$\{([A-Z0-9_]+)\}$/i);
444
- if (envMatch) {
445
- const envValue = process.env[envMatch[1]];
446
- if (!envValue) {
447
- throw new Error(`Environment variable ${envMatch[1]} is not defined`);
448
- }
449
- return envValue;
450
- }
451
- if (/^[A-Z][A-Z0-9_]+$/.test(trimmed)) {
452
- const envValue = process.env[trimmed];
453
- if (!envValue) {
454
- throw new Error(`Environment variable ${trimmed} is not defined`);
455
- }
456
- return envValue;
457
- }
458
- if (trimmed.startsWith('authfile-')) {
459
- return await this.authResolver.resolveKey(trimmed);
460
- }
461
- return trimmed;
172
+ return await resolveSecretValue(this, raw);
462
173
  }
463
174
  isSafeSecretReference(value) {
464
- const trimmed = value.trim();
465
- if (!trimmed) {
466
- return false;
467
- }
468
- if (trimmed.startsWith('authfile-')) {
469
- return true;
470
- }
471
- if (/^\$\{[A-Z0-9_]+\}$/i.test(trimmed)) {
472
- return true;
473
- }
474
- if (/^[A-Z][A-Z0-9_]+$/.test(trimmed)) {
475
- return true;
476
- }
477
- return false;
175
+ return isSafeSecretReference(this, value);
478
176
  }
479
177
  async bootstrapVirtualRouter(input) {
480
- const artifacts = (await bootstrapVirtualRouterConfig(input));
481
- return artifacts;
178
+ return await bootstrapVirtualRouter(this, input);
482
179
  }
483
180
  async ensureHubPipelineCtor() {
484
- if (this.hubPipelineCtor) {
485
- return this.hubPipelineCtor;
486
- }
487
- const ctorFactory = await getHubPipelineCtor();
488
- this.hubPipelineCtor = ctorFactory;
489
- return this.hubPipelineCtor;
181
+ return await ensureHubPipelineCtor(this);
490
182
  }
491
183
  async ensureHubPipelineEngineShadow() {
492
- if (this.hubPipelineEngineShadow) {
493
- return this.hubPipelineEngineShadow;
494
- }
495
- if (!this.hubPipelineConfigForShadow) {
496
- throw new Error('Hub pipeline shadow config is not initialized');
497
- }
498
- const baseConfig = this.hubPipelineConfigForShadow;
499
- const shadowConfig = { ...baseConfig };
500
- // Avoid double side effects when shadow-running the pipeline: keep reads, drop writes.
501
- const routingStateStore = baseConfig.routingStateStore;
502
- if (routingStateStore && typeof routingStateStore.loadSync === 'function') {
503
- shadowConfig.routingStateStore = {
504
- loadSync: routingStateStore.loadSync.bind(routingStateStore),
505
- saveAsync: () => { }
506
- };
507
- }
508
- const healthStore = baseConfig.healthStore;
509
- if (healthStore && typeof healthStore.loadInitialSnapshot === 'function') {
510
- shadowConfig.healthStore = {
511
- loadInitialSnapshot: healthStore.loadInitialSnapshot.bind(healthStore)
512
- };
513
- }
514
- const quotaViewReadOnly = baseConfig.quotaViewReadOnly;
515
- if (typeof quotaViewReadOnly === 'function') {
516
- shadowConfig.quotaView = quotaViewReadOnly;
517
- }
518
- const bridge = (await import('../../../modules/llmswitch/bridge.js'));
519
- const getCtor = bridge.getHubPipelineCtorForImpl;
520
- if (typeof getCtor !== 'function') {
521
- throw new Error('llmswitch bridge does not expose getHubPipelineCtorForImpl');
522
- }
523
- const ctorFactory = await getCtor('engine');
524
- const hubCtor = ctorFactory;
525
- if (!('virtualRouter' in shadowConfig)) {
526
- throw new Error('HubPipeline shadow config missing virtualRouter');
527
- }
528
- this.hubPipelineEngineShadow = new hubCtor(shadowConfig);
529
- return this.hubPipelineEngineShadow;
184
+ return await ensureHubPipelineEngineShadow(this);
530
185
  }
531
186
  isPipelineReady() {
532
- return Boolean(this.hubPipeline);
187
+ return isPipelineReady(this);
533
188
  }
534
189
  async waitForRuntimeReady() {
535
- if (this.runtimeReadyResolved) {
536
- return;
537
- }
538
- if (this.runtimeReadyError) {
539
- throw this.runtimeReadyError;
540
- }
541
- const raw = String(process.env.ROUTECODEX_STARTUP_HOLD_MS || process.env.RCC_STARTUP_HOLD_MS || '').trim();
542
- const parsed = raw ? Number.parseInt(raw, 10) : NaN;
543
- const timeoutMs = Number.isFinite(parsed) && parsed > 0 ? parsed : 120_000;
544
- const timeoutPromise = new Promise((_resolve, reject) => {
545
- const timer = setTimeout(() => reject(new Error(`startup timeout after ${timeoutMs}ms`)), timeoutMs);
546
- try {
547
- timer.unref?.();
548
- }
549
- catch {
550
- // ignore
551
- }
552
- });
553
- await Promise.race([this.runtimeReadyPromise, timeoutPromise]);
190
+ await waitForRuntimeReady(this);
554
191
  }
555
192
  isQuotaRoutingEnabled() {
556
- const flag = this.config.server.quotaRoutingEnabled;
557
- if (typeof flag === 'boolean') {
558
- return flag;
559
- }
560
- return true;
193
+ return isQuotaRoutingEnabled(this);
561
194
  }
562
195
  shouldStartManagerDaemon() {
563
- const mockFlag = String(process.env.ROUTECODEX_USE_MOCK || '').trim();
564
- if (mockFlag === '1' || mockFlag.toLowerCase() === 'true') {
565
- return false;
566
- }
567
- if (process.env.ROUTECODEX_MOCK_CONFIG_PATH || process.env.ROUTECODEX_MOCK_SAMPLES_DIR) {
568
- return false;
569
- }
570
- return true;
196
+ return shouldStartManagerDaemon(this);
571
197
  }
572
198
  shouldEnableClockDaemonInjectLoop() {
573
- const raw = String(process.env.ROUTECODEX_CLOCK_DAEMON_INJECT_ENABLE || process.env.RCC_CLOCK_DAEMON_INJECT_ENABLE || '').trim().toLowerCase();
574
- if (raw === '0' || raw === 'false' || raw === 'off' || raw === 'no') {
575
- return false;
576
- }
577
- if (raw === '1' || raw === 'true' || raw === 'on' || raw === 'yes') {
578
- return true;
579
- }
580
- if (process.env.JEST_WORKER_ID || process.env.NODE_ENV === 'test') {
581
- return false;
582
- }
583
- return true;
199
+ return shouldEnableClockDaemonInjectLoop();
584
200
  }
585
201
  resolveRawClockConfig() {
586
- const user = this.userConfig && typeof this.userConfig === 'object' ? this.userConfig : {};
587
- const vr = user.virtualrouter && typeof user.virtualrouter === 'object' ? user.virtualrouter : null;
588
- if (vr && Object.prototype.hasOwnProperty.call(vr, 'clock')) {
589
- return vr.clock;
590
- }
591
- if (Object.prototype.hasOwnProperty.call(user, 'clock')) {
592
- return user.clock;
593
- }
594
- const artCfg = this.currentRouterArtifacts &&
595
- this.currentRouterArtifacts.config &&
596
- typeof this.currentRouterArtifacts.config === 'object'
597
- ? this.currentRouterArtifacts.config
598
- : null;
599
- if (artCfg && Object.prototype.hasOwnProperty.call(artCfg, 'clock')) {
600
- return artCfg.clock;
601
- }
602
- return undefined;
202
+ return resolveRawClockConfig(this);
603
203
  }
604
204
  stopClockDaemonInjectLoop() {
605
- if (this.clockDaemonInjectTimer) {
606
- clearInterval(this.clockDaemonInjectTimer);
607
- this.clockDaemonInjectTimer = null;
608
- }
205
+ stopClockDaemonInjectLoop(this);
609
206
  }
610
207
  startClockDaemonInjectLoop() {
611
- this.stopClockDaemonInjectLoop();
612
- if (!this.shouldEnableClockDaemonInjectLoop()) {
613
- return;
614
- }
615
- const rawTick = String(process.env.ROUTECODEX_CLOCK_DAEMON_INJECT_TICK_MS || process.env.RCC_CLOCK_DAEMON_INJECT_TICK_MS || '').trim();
616
- const parsedTick = rawTick ? Number.parseInt(rawTick, 10) : NaN;
617
- const tickMs = Number.isFinite(parsedTick) && parsedTick >= 200 ? Math.floor(parsedTick) : 1500;
618
- this.clockDaemonInjectTimer = setInterval(() => {
619
- void this.tickClockDaemonInjectLoop();
620
- }, tickMs);
621
- this.clockDaemonInjectTimer.unref?.();
622
- void this.tickClockDaemonInjectLoop();
208
+ startClockDaemonInjectLoop(this);
623
209
  }
624
210
  async tickClockDaemonInjectLoop() {
625
- if (this.clockDaemonInjectTickInFlight) {
626
- return;
627
- }
628
- this.clockDaemonInjectTickInFlight = true;
629
- try {
630
- const rawClockConfig = this.resolveRawClockConfig();
631
- const resolvedClockConfig = await resolveClockConfigSnapshot(rawClockConfig);
632
- if (!resolvedClockConfig) {
633
- return;
634
- }
635
- const clockConfig = toExactMatchClockConfig(resolvedClockConfig);
636
- const sessionDir = String(process.env.ROUTECODEX_SESSION_DIR || '').trim();
637
- const clockDir = sessionDir ? path.join(sessionDir, 'clock') : '';
638
- const entries = clockDir
639
- ? await fs.readdir(clockDir, { withFileTypes: true }).catch(() => [])
640
- : [];
641
- const registry = getClockClientRegistry();
642
- const now = Date.now();
643
- const cleanupRequestId = `clock_cleanup_${now}_${Math.random().toString(16).slice(2, 8)}`;
644
- const staleCleanup = registry.cleanupStaleHeartbeats({
645
- nowMs: now,
646
- terminateManagedTmuxSession: (tmuxSessionId) => killManagedTmuxSession(tmuxSessionId),
647
- terminateManagedClientProcess: (processInfo) => terminateManagedClientProcess(processInfo)
648
- });
649
- const deadTmuxCleanup = registry.cleanupDeadTmuxSessions({
650
- isTmuxSessionAlive,
651
- terminateManagedTmuxSession: (tmuxSessionId) => killManagedTmuxSession(tmuxSessionId),
652
- terminateManagedClientProcess: (processInfo) => terminateManagedClientProcess(processInfo)
653
- });
654
- const removedConversationSessionIds = Array.from(new Set([
655
- ...staleCleanup.removedConversationSessionIds,
656
- ...deadTmuxCleanup.removedConversationSessionIds
657
- ]));
658
- if (removedConversationSessionIds.length > 0) {
659
- for (const removedConversationSessionId of removedConversationSessionIds) {
660
- await clearClockTasksSnapshot({
661
- sessionId: removedConversationSessionId,
662
- config: clockConfig
663
- });
664
- }
665
- }
666
- const hasCleanupActions = staleCleanup.removedDaemonIds.length > 0
667
- || deadTmuxCleanup.removedDaemonIds.length > 0;
668
- if (hasCleanupActions && Date.now() - this.lastClockDaemonCleanupAtMs > 2000) {
669
- this.lastClockDaemonCleanupAtMs = Date.now();
670
- console.log('[RouteCodexHttpServer] clock daemon cleanup audit:', {
671
- requestId: cleanupRequestId,
672
- staleHeartbeat: {
673
- reason: 'heartbeat_timeout',
674
- staleAfterMs: staleCleanup.staleAfterMs,
675
- removedDaemonIds: staleCleanup.removedDaemonIds,
676
- removedTmuxSessionIds: staleCleanup.removedTmuxSessionIds,
677
- removedConversationSessionIds: staleCleanup.removedConversationSessionIds,
678
- killedTmuxSessionIds: staleCleanup.killedTmuxSessionIds,
679
- failedKillTmuxSessionIds: staleCleanup.failedKillTmuxSessionIds,
680
- skippedKillTmuxSessionIds: staleCleanup.skippedKillTmuxSessionIds,
681
- killedManagedClientPids: staleCleanup.killedManagedClientPids,
682
- failedKillManagedClientPids: staleCleanup.failedKillManagedClientPids,
683
- skippedKillManagedClientPids: staleCleanup.skippedKillManagedClientPids
684
- },
685
- deadTmux: {
686
- reason: 'tmux_not_alive',
687
- removedDaemonIds: deadTmuxCleanup.removedDaemonIds,
688
- removedTmuxSessionIds: deadTmuxCleanup.removedTmuxSessionIds,
689
- removedConversationSessionIds: deadTmuxCleanup.removedConversationSessionIds,
690
- killedTmuxSessionIds: deadTmuxCleanup.killedTmuxSessionIds,
691
- failedKillTmuxSessionIds: deadTmuxCleanup.failedKillTmuxSessionIds,
692
- skippedKillTmuxSessionIds: deadTmuxCleanup.skippedKillTmuxSessionIds,
693
- killedManagedClientPids: deadTmuxCleanup.killedManagedClientPids,
694
- failedKillManagedClientPids: deadTmuxCleanup.failedKillManagedClientPids,
695
- skippedKillManagedClientPids: deadTmuxCleanup.skippedKillManagedClientPids
696
- }
697
- });
698
- }
699
- for (const entry of entries) {
700
- if (!entry || typeof entry.name !== 'string') {
701
- continue;
702
- }
703
- if (!entry.name.endsWith('.json')) {
704
- continue;
705
- }
706
- if (typeof entry.isFile === 'function' && !entry.isFile()) {
707
- continue;
708
- }
709
- const sessionId = entry.name.slice(0, -'.json'.length).trim();
710
- if (!sessionId) {
711
- continue;
712
- }
713
- const reservationId = 'clockd_inject_' + now + '_' + Math.random().toString(16).slice(2, 8);
714
- const reserved = await reserveClockDueTasks({
715
- reservationId,
716
- sessionId,
717
- config: clockConfig,
718
- requestId: reservationId
719
- });
720
- if (!reserved || !reserved.reservation || typeof reserved.injectText !== 'string' || !reserved.injectText.trim()) {
721
- continue;
722
- }
723
- const bind = registry.bindConversationSession({ conversationSessionId: sessionId });
724
- const text = [
725
- '[Clock Reminder]: scheduled tasks are due.',
726
- reserved.injectText.trim(),
727
- 'You may call tools to complete these tasks.',
728
- 'MANDATORY: if waiting is needed, use the clock tool to schedule wake-up (clock.schedule) now; do not only promise to wait.'
729
- ].join('\n');
730
- const injected = await registry.inject({
731
- sessionId,
732
- text,
733
- requestId: reservationId,
734
- source: 'clock.daemon.inject'
735
- });
736
- if (!injected.ok) {
737
- const nowWarn = Date.now();
738
- if (nowWarn - this.lastClockDaemonInjectErrorAtMs > 5000) {
739
- this.lastClockDaemonInjectErrorAtMs = nowWarn;
740
- console.warn('[RouteCodexHttpServer] clock daemon inject skipped:', {
741
- sessionId,
742
- injectReason: injected.reason,
743
- bindOk: bind.ok,
744
- bindReason: bind.reason
745
- });
746
- }
747
- continue;
748
- }
749
- await commitClockDueReservation({
750
- reservation: reserved.reservation,
751
- config: clockConfig
752
- });
753
- }
754
- }
755
- catch (error) {
756
- const now = Date.now();
757
- if (now - this.lastClockDaemonInjectErrorAtMs > 5000) {
758
- this.lastClockDaemonInjectErrorAtMs = now;
759
- console.warn('[RouteCodexHttpServer] clock daemon inject loop tick failed:', error);
760
- }
761
- }
762
- finally {
763
- this.clockDaemonInjectTickInFlight = false;
764
- }
211
+ await tickClockDaemonInjectLoop(this);
765
212
  }
766
- /**
767
- * 初始化服务器
768
- */
769
213
  async initialize() {
770
- try {
771
- console.log('[RouteCodexHttpServer] Starting initialization...');
772
- // 初始化错误处理
773
- await this.errorHandling.initialize();
774
- await this.initializeRouteErrorHub();
775
- // 初始化 ManagerDaemon 骨架(当前模块为占位实现,不改变行为)
776
- // Mock regressions run many short-lived servers; skip daemon to avoid flakiness from lingering background tasks.
777
- if (this.shouldStartManagerDaemon() && !this.managerDaemon) {
778
- const serverId = canonicalizeServerId(this.config.server.host, this.config.server.port);
779
- const daemon = new ManagerDaemon({ serverId, quotaRoutingEnabled: this.isQuotaRoutingEnabled() });
780
- daemon.registerModule(new TokenManagerModule());
781
- daemon.registerModule(new RoutingStateManagerModule());
782
- daemon.registerModule(new HealthManagerModule());
783
- // Quota manager(当前仅用于 antigravity/gemini-cli 等需要配额信息的 Provider)
784
- try {
785
- const mod = (await import('../../../manager/modules/quota/index.js'));
786
- if (typeof mod.QuotaManagerModule === 'function') {
787
- daemon.registerModule(new mod.QuotaManagerModule());
788
- }
789
- }
790
- catch {
791
- // 可选模块,缺失时忽略
792
- }
793
- await daemon.start();
794
- this.managerDaemon = daemon;
795
- }
796
- // registerDefaultMiddleware and registerOAuthPortalRoute already called in constructor
797
- // Register remaining HTTP routes
798
- registerHttpRoutes({
799
- app: this.app,
800
- config: this.config,
801
- buildHandlerContext: () => this.buildHandlerContext(),
802
- getPipelineReady: () => this.isPipelineReady(),
803
- waitForPipelineReady: async () => await this.waitForRuntimeReady(),
804
- handleError: (error, context) => this.handleError(error, context),
805
- restartRuntimeFromDisk: async () => await this.restartRuntimeFromDisk(),
806
- getHealthSnapshot: () => {
807
- const healthModule = this.managerDaemon?.getModule('health');
808
- return healthModule?.getCurrentSnapshot() ?? null;
809
- },
810
- getRoutingState: (sessionId) => {
811
- const routingModule = this.managerDaemon?.getModule('routing');
812
- const store = routingModule?.getRoutingStateStore();
813
- if (!store) {
814
- return null;
815
- }
816
- const key = sessionId && sessionId.trim() ? `session:${sessionId.trim()}` : '';
817
- return key ? store.loadSync(key) : null;
818
- },
819
- getManagerDaemon: () => this.managerDaemon,
820
- getVirtualRouterArtifacts: () => this.currentRouterArtifacts,
821
- getStatsSnapshot: () => ({
822
- session: this.stats.snapshot(Math.round(process.uptime() * 1000)),
823
- historical: this.stats.snapshotHistorical()
824
- }),
825
- getServerId: () => canonicalizeServerId(this.config.server.host, this.config.server.port)
826
- });
827
- this._isInitialized = true;
828
- console.log('[RouteCodexHttpServer] Initialization completed successfully');
829
- }
830
- catch (error) {
831
- await this.handleError(error, 'initialization');
832
- throw error;
833
- }
214
+ await initializeHttpServer(this);
834
215
  }
835
216
  async restartRuntimeFromDisk() {
836
- // Serialize restarts to avoid racing provider disposals / hub updates.
837
- const run = async () => {
838
- const loaded = await loadRouteCodexConfig(this.config?.configPath);
839
- const userConfig = asRecord(loaded.userConfig) ?? {};
840
- const httpServerNode = asRecord(userConfig.httpserver) ??
841
- asRecord(asRecord(userConfig.modules)?.httpserver)?.config ??
842
- null;
843
- const nextApiKey = httpServerNode ? (typeof httpServerNode.apikey === 'string' ? String(httpServerNode.apikey).trim() : '') : '';
844
- const nextHost = httpServerNode ? (typeof httpServerNode.host === 'string' ? String(httpServerNode.host).trim() : '') : '';
845
- const nextPort = httpServerNode ? (typeof httpServerNode.port === 'number' ? Number(httpServerNode.port) : NaN) : NaN;
846
- const warnings = [];
847
- // Best-effort: allow rotating apikey at runtime by mutating config object.
848
- if (typeof nextApiKey === 'string' && nextApiKey !== String(this.config.server.apikey || '')) {
849
- this.config.server.apikey = nextApiKey || undefined;
850
- }
851
- // host/port changes require rebind; record warnings but do not attempt to re-listen.
852
- if (nextHost && nextHost !== this.config.server.host) {
853
- warnings.push(`httpserver.host changed to "${nextHost}" but live server keeps "${this.config.server.host}" until process restart`);
854
- }
855
- if (Number.isFinite(nextPort) && nextPort > 0 && nextPort !== this.config.server.port) {
856
- warnings.push(`httpserver.port changed to ${nextPort} but live server keeps ${this.config.server.port} until process restart`);
857
- }
858
- // Keep the server's configPath aligned with what was loaded.
859
- this.config.configPath = loaded.configPath;
860
- await this.reloadRuntime(loaded.userConfig, { providerProfiles: loaded.providerProfiles });
861
- return { reloadedAt: Date.now(), configPath: loaded.configPath, ...(warnings.length ? { warnings } : {}) };
862
- };
863
- const slot = this.restartChain.then(run);
864
- this.restartChain = slot.then(() => undefined, () => undefined);
865
- return await slot;
217
+ return await restartRuntimeFromDisk(this);
866
218
  }
867
- /**
868
- * 启动服务器
869
- */
870
219
  async start() {
871
- if (!this._isInitialized) {
872
- await this.initialize();
873
- }
874
- return new Promise((resolve, reject) => {
875
- this.server = this.app.listen(this.config.server.port, this.config.server.host, () => {
876
- this._isRunning = true;
877
- const boundAddress = this.server?.address();
878
- const resolvedPort = boundAddress && typeof boundAddress === 'object' && typeof boundAddress.port === 'number'
879
- ? boundAddress.port
880
- : this.config.server.port;
881
- process.env.ROUTECODEX_SERVER_PORT = String(resolvedPort);
882
- console.log(`[RouteCodexHttpServer] Server started on ${this.config.server.host}:${resolvedPort}`);
883
- resolve();
884
- });
885
- // In test runners (Jest), prevent the listen handle from keeping the process alive
886
- // in case some keep-alive sockets linger.
887
- if (process.env.JEST_WORKER_ID || process.env.NODE_ENV === 'test') {
888
- try {
889
- this.server.unref?.();
890
- }
891
- catch {
892
- // ignore
893
- }
894
- }
895
- this.server.on('connection', (socket) => {
896
- this.activeSockets.add(socket);
897
- socket.on('close', () => {
898
- this.activeSockets.delete(socket);
899
- });
900
- });
901
- this.server.on('error', async (error) => {
902
- await this.handleError(error, 'server_start');
903
- reject(error);
904
- });
905
- });
220
+ await startHttpServer(this);
221
+ // Start the clock reaper after server is running
222
+ startClockReaper();
906
223
  }
907
- /**
908
- * 停止服务器
909
- */
910
224
  async stop() {
911
- this.stopClockDaemonInjectLoop();
912
- try {
913
- await shutdownCamoufoxLaunchers();
914
- }
915
- catch {
916
- // ignore launcher cleanup errors
917
- }
918
- if (!this.server) {
919
- return;
920
- }
921
- if (this.server) {
922
- // Best-effort: close any open keep-alive sockets so server.close can finish.
923
- for (const socket of this.activeSockets) {
924
- try {
925
- socket.destroy();
926
- }
927
- catch {
928
- // ignore
929
- }
930
- }
931
- this.activeSockets.clear();
932
- try {
933
- const srv = this.server;
934
- srv.closeIdleConnections?.();
935
- srv.closeAllConnections?.();
936
- }
937
- catch {
938
- // ignore
939
- }
940
- return new Promise(resolve => {
941
- this.server?.close(async () => {
942
- this._isRunning = false;
943
- try {
944
- await this.disposeProviders();
945
- }
946
- catch { /* ignore */ }
947
- try {
948
- if (this.managerDaemon) {
949
- await this.managerDaemon.stop();
950
- this.managerDaemon = null;
951
- }
952
- }
953
- catch {
954
- // ignore manager shutdown failures
955
- }
956
- try {
957
- this.server?.removeAllListeners();
958
- }
959
- catch {
960
- // ignore
961
- }
962
- this.server = undefined;
963
- await this.errorHandling.destroy();
964
- try {
965
- const uptimeMs = Math.round(process.uptime() * 1000);
966
- const snapshot = this.stats.logSummary(uptimeMs);
967
- await this.stats.persistSnapshot(snapshot, { reason: 'server_shutdown' });
968
- await this.stats.logHistoricalSummary();
969
- }
970
- catch {
971
- // stats logging must never block shutdown
972
- }
973
- console.log('[RouteCodexHttpServer] Server stopped');
974
- resolve();
975
- });
976
- });
977
- }
225
+ stopClockReaper();
226
+ await stopHttpServer(this);
978
227
  }
979
- /**
980
- * 获取服务器状态
981
- */
982
228
  getStatus() {
983
- return {
984
- initialized: this._isInitialized,
985
- running: this._isRunning,
986
- port: this.config.server.port,
987
- host: this.config.server.host,
988
- uptime: process.uptime(),
989
- memory: process.memoryUsage(),
990
- version: 'v2'
991
- };
229
+ return getHttpServerStatus(this);
992
230
  }
993
- // Non-standard helper used by index.ts for logging URL
994
231
  getServerConfig() {
995
- return { host: this.config.server.host, port: this.config.server.port };
232
+ return getHttpServerConfig(this);
996
233
  }
997
- /**
998
- * V1兼容接口:检查是否已初始化
999
- */
1000
234
  isInitialized() {
1001
- return this._isInitialized;
235
+ return isHttpServerInitialized(this);
1002
236
  }
1003
- /**
1004
- * V1兼容接口:检查是否正在运行
1005
- */
1006
237
  isRunning() {
1007
- return this._isRunning;
238
+ return isHttpServerRunning(this);
1008
239
  }
1009
- /**
1010
- * 处理错误
1011
- */
1012
240
  async handleError(error, context) {
1013
- const payload = {
1014
- code: `SERVER_${context.toUpperCase()}`,
1015
- message: error.message || 'RouteCodex server error',
1016
- source: `routecodex-server-v2.${context}`,
1017
- scope: 'server',
1018
- severity: 'medium',
1019
- details: {
1020
- name: error.name,
1021
- stack: error.stack,
1022
- version: 'v2'
1023
- },
1024
- originalError: error
1025
- };
1026
- try {
1027
- await reportRouteError(payload);
1028
- }
1029
- catch (handlerError) {
1030
- console.error('[RouteCodexHttpServer] Failed to report error via RouteErrorHub:', formatValueForConsole(handlerError));
1031
- console.error('[RouteCodexHttpServer] Original error:', formatValueForConsole(error));
1032
- }
241
+ await handleHttpServerError(this, error, context);
1033
242
  }
1034
- // --- V1 parity helpers and attach methods ---
1035
243
  async initializeWithUserConfig(userConfig, context) {
1036
- try {
1037
- this.updateProviderProfiles(context?.providerProfiles, userConfig);
1038
- await this.setupRuntime(userConfig);
1039
- if (!this.runtimeReadyResolved) {
1040
- this.runtimeReadyResolved = true;
1041
- this.runtimeReadyResolve?.();
1042
- }
1043
- }
1044
- catch (error) {
1045
- const normalized = error instanceof Error ? error : new Error(String(error));
1046
- this.runtimeReadyError = normalized;
1047
- if (!this.runtimeReadyResolved) {
1048
- this.runtimeReadyReject?.(normalized);
1049
- }
1050
- throw error;
1051
- }
244
+ await initializeWithUserConfig(this, userConfig, context);
1052
245
  }
1053
246
  async reloadRuntime(userConfig, context) {
1054
- this.updateProviderProfiles(context?.providerProfiles, userConfig);
1055
- await this.setupRuntime(userConfig);
1056
- if (!this.runtimeReadyResolved) {
1057
- this.runtimeReadyResolved = true;
1058
- this.runtimeReadyResolve?.();
1059
- }
247
+ await reloadHttpServerRuntime(this, userConfig, context);
1060
248
  }
1061
249
  async setupRuntime(userConfig) {
1062
- this.userConfig = asRecord(userConfig);
1063
- this.ensureProviderProfilesFromUserConfig();
1064
- const routerInput = this.resolveVirtualRouterInput(this.userConfig);
1065
- const bootstrapArtifacts = await this.bootstrapVirtualRouter(routerInput);
1066
- this.currentRouterArtifacts = bootstrapArtifacts;
1067
- const hubCtor = await this.ensureHubPipelineCtor();
1068
- const hubConfig = {
1069
- virtualRouter: bootstrapArtifacts.config
1070
- };
1071
- // Unified Hub Framework V1: policy rollout toggle.
1072
- // Default: enforce (Phase 1: Responses-first outbound policy; currently only
1073
- // affects openai-responses provider outbound payload).
1074
- // - Disable via env: ROUTECODEX_HUB_POLICY_MODE=off
1075
- // - Enforce via env: ROUTECODEX_HUB_POLICY_MODE=enforce
1076
- const hubPolicyModeRaw = String(process.env.ROUTECODEX_HUB_POLICY_MODE || '').trim().toLowerCase();
1077
- const hubPolicyMode = hubPolicyModeRaw === 'off' || hubPolicyModeRaw === '0' || hubPolicyModeRaw === 'false'
1078
- ? null
1079
- : (hubPolicyModeRaw === 'observe' || hubPolicyModeRaw === 'enforce' ? hubPolicyModeRaw : 'enforce');
1080
- this.hubPolicyMode = hubPolicyMode ?? 'off';
1081
- if (hubPolicyMode) {
1082
- const sampleRateRaw = String(process.env.ROUTECODEX_HUB_POLICY_SAMPLE_RATE || '').trim();
1083
- const sampleRate = sampleRateRaw ? Number(sampleRateRaw) : undefined;
1084
- hubConfig.policy = {
1085
- mode: hubPolicyMode,
1086
- ...(Number.isFinite(sampleRate) ? { sampleRate } : {})
1087
- };
1088
- }
1089
- // Unified Hub Framework V1: tool surface rollout toggle (enforce by default in dev).
1090
- // - Disable via env: ROUTECODEX_HUB_TOOL_SURFACE_MODE=off
1091
- // - Observe via env: ROUTECODEX_HUB_TOOL_SURFACE_MODE=observe
1092
- // - Shadow (diff-only, no rewrites) via env: ROUTECODEX_HUB_TOOL_SURFACE_MODE=shadow
1093
- // - Enforce (rewrite outbound payload) via env: ROUTECODEX_HUB_TOOL_SURFACE_MODE=enforce
1094
- const toolSurfaceModeRaw = String(process.env.ROUTECODEX_HUB_TOOL_SURFACE_MODE || '').trim().toLowerCase();
1095
- const toolSurfaceMode = toolSurfaceModeRaw === 'off' || toolSurfaceModeRaw === '0' || toolSurfaceModeRaw === 'false'
1096
- ? null
1097
- : toolSurfaceModeRaw === 'observe' || toolSurfaceModeRaw === 'shadow' || toolSurfaceModeRaw === 'enforce'
1098
- ? toolSurfaceModeRaw
1099
- : buildInfo.mode === 'dev'
1100
- ? 'enforce'
1101
- : null;
1102
- if (toolSurfaceMode) {
1103
- const sampleRateRaw = String(process.env.ROUTECODEX_HUB_TOOL_SURFACE_SAMPLE_RATE || '').trim();
1104
- const sampleRate = sampleRateRaw ? Number(sampleRateRaw) : undefined;
1105
- hubConfig.toolSurface = {
1106
- mode: toolSurfaceMode,
1107
- ...(Number.isFinite(sampleRate) ? { sampleRate } : {})
1108
- };
1109
- // Also export the resolved mode to env so llmswitch-core response conversion
1110
- // (convertProviderResponse) can observe tool surface mismatches consistently.
1111
- if (!process.env.ROUTECODEX_HUB_TOOL_SURFACE_MODE) {
1112
- process.env.ROUTECODEX_HUB_TOOL_SURFACE_MODE = toolSurfaceMode;
1113
- }
1114
- }
1115
- // Unified Hub Framework V1: followup (servertool) shadow compare toggle.
1116
- // Implemented in llmswitch-core servertool engine; controlled by env for progressive rollout.
1117
- // Default: shadow in dev builds; off in release builds.
1118
- if (!process.env.ROUTECODEX_HUB_FOLLOWUP_MODE) {
1119
- if (buildInfo.mode === 'dev') {
1120
- process.env.ROUTECODEX_HUB_FOLLOWUP_MODE = 'shadow';
1121
- }
1122
- }
1123
- const healthModule = this.managerDaemon?.getModule('health');
1124
- const healthStore = healthModule?.getHealthStore();
1125
- if (healthStore) {
1126
- hubConfig.healthStore = healthStore;
1127
- }
1128
- const routingModule = this.managerDaemon?.getModule('routing');
1129
- const routingStateStore = routingModule?.getRoutingStateStore();
1130
- if (routingStateStore) {
1131
- hubConfig.routingStateStore = routingStateStore;
1132
- }
1133
- const quotaModule = this.managerDaemon?.getModule('quota');
1134
- if (this.isQuotaRoutingEnabled() && quotaModule && typeof quotaModule.getQuotaView === 'function') {
1135
- hubConfig.quotaView = quotaModule.getQuotaView();
1136
- if (typeof quotaModule.getQuotaViewReadOnly === 'function') {
1137
- hubConfig.quotaViewReadOnly = quotaModule.getQuotaViewReadOnly();
1138
- }
1139
- }
1140
- if (!this.hubPipeline) {
1141
- this.hubPipeline = new hubCtor(hubConfig);
1142
- }
1143
- else {
1144
- const existing = this.hubPipeline;
1145
- try {
1146
- existing.updateRuntimeDeps?.({
1147
- ...(healthStore ? { healthStore } : {}),
1148
- ...(routingStateStore ? { routingStateStore } : {}),
1149
- ...('quotaView' in hubConfig ? { quotaView: hubConfig.quotaView } : {})
1150
- });
1151
- }
1152
- catch {
1153
- // best-effort: runtime deps updates must never block reload
1154
- }
1155
- this.hubPipeline.updateVirtualRouterConfig(bootstrapArtifacts.config);
1156
- }
1157
- // llms-engine shadow: capture the latest hub config and reset shadow pipeline to avoid stale deps.
1158
- this.hubPipelineConfigForShadow = hubConfig;
1159
- this.hubPipelineEngineShadow = null;
1160
- await this.initializeProviderRuntimes(bootstrapArtifacts);
1161
- this.startClockDaemonInjectLoop();
250
+ await setupRuntime(this, userConfig);
1162
251
  }
1163
252
  buildHandlerContext() {
1164
- return {
1165
- executePipeline: this.executePipeline.bind(this),
1166
- errorHandling: this.errorHandling
1167
- };
253
+ return buildHttpHandlerContext(this);
1168
254
  }
1169
255
  async initializeProviderRuntimes(artifacts) {
1170
- const runtimeMap = artifacts?.targetRuntime ?? this.currentRouterArtifacts?.targetRuntime ?? undefined;
1171
- if (!runtimeMap) {
1172
- return;
1173
- }
1174
- await this.disposeProviders();
1175
- this.providerKeyToRuntimeKey.clear();
1176
- this.providerRuntimeInitErrors.clear();
1177
- // Antigravity UA preload (best-effort):
1178
- // - fetch latest UA version once (cached)
1179
- // - load per-alias camoufox fingerprints so UA suffix stays stable per OAuth session
1180
- try {
1181
- const aliases = [];
1182
- for (const [providerKey, runtime] of Object.entries(runtimeMap)) {
1183
- const key = typeof providerKey === 'string' ? providerKey.trim() : '';
1184
- const rk = runtime && typeof runtime.runtimeKey === 'string'
1185
- ? String(runtime.runtimeKey).trim()
1186
- : '';
1187
- for (const candidate of [rk, key]) {
1188
- if (!candidate.toLowerCase().startsWith('antigravity.')) {
1189
- continue;
1190
- }
1191
- const parts = candidate.split('.');
1192
- if (parts.length >= 2 && parts[1] && parts[1].trim()) {
1193
- aliases.push(parts[1].trim());
1194
- }
1195
- }
1196
- }
1197
- if (aliases.length) {
1198
- await Promise.allSettled([
1199
- primeAntigravityUserAgentVersion(),
1200
- preloadAntigravityAliasUserAgents(aliases)
1201
- ]);
1202
- }
1203
- }
1204
- catch {
1205
- // UA preload must never block runtime init/reload
1206
- }
1207
- const quotaModule = this.managerDaemon?.getModule('quota');
1208
- // Antigravity warmup (strict): if UA fingerprint suffix doesn't match local OAuth fingerprint, blacklist the alias.
1209
- if (isAntigravityWarmupEnabled()) {
1210
- try {
1211
- const providerKeysByAlias = new Map();
1212
- for (const providerKey of Object.keys(runtimeMap)) {
1213
- const key = typeof providerKey === 'string' ? providerKey.trim() : '';
1214
- if (!key.toLowerCase().startsWith('antigravity.')) {
1215
- continue;
1216
- }
1217
- const parts = key.split('.');
1218
- if (parts.length < 3) {
1219
- continue;
1220
- }
1221
- const alias = parts[1]?.trim();
1222
- if (!alias) {
1223
- continue;
1224
- }
1225
- const list = providerKeysByAlias.get(alias) || [];
1226
- list.push(key);
1227
- providerKeysByAlias.set(alias, list);
1228
- }
1229
- if (providerKeysByAlias.size > 0) {
1230
- const canBlacklist = Boolean(quotaModule && typeof quotaModule.disableProvider === 'function');
1231
- const durationMs = getAntigravityWarmupBlacklistDurationMs();
1232
- let okCount = 0;
1233
- let failCount = 0;
1234
- for (const [alias, providerKeys] of providerKeysByAlias.entries()) {
1235
- const result = await warmupCheckAntigravityAlias(alias);
1236
- if (result.ok) {
1237
- okCount += 1;
1238
- console.log(`[antigravity:warmup] ok alias=${alias} profile=${result.profileId} fp_os=${result.fingerprintOs} fp_arch=${result.fingerprintArch} ua_suffix=${result.actualSuffix} ua=${result.actualUserAgent}`);
1239
- continue;
1240
- }
1241
- failCount += 1;
1242
- const expected = result.expectedSuffix ? ` expected=${result.expectedSuffix}` : '';
1243
- const actual = result.actualSuffix ? ` actual=${result.actualSuffix}` : '';
1244
- const hint = result.reason === 'linux_not_allowed'
1245
- ? ` hint="run: routecodex camoufox-fp repair --provider antigravity --alias ${alias}"`
1246
- : result.reason === 'reauth_required'
1247
- ? ` hint="run: routecodex oauth antigravity-auto ${result.tokenFile || `antigravity-oauth-*-` + alias + `.json`}"` +
1248
- `${result.fromSuffix ? ` from=${result.fromSuffix}` : ''}${result.toSuffix ? ` to=${result.toSuffix}` : ''}`
1249
- : '';
1250
- console.error(`[antigravity:warmup] FAIL alias=${alias} profile=${result.profileId}${result.fingerprintOs ? ` fp_os=${result.fingerprintOs}` : ''}${result.fingerprintArch ? ` fp_arch=${result.fingerprintArch}` : ''} reason=${result.reason}${expected}${actual}${hint} providerKeys=${providerKeys.length}${canBlacklist ? '' : ' (quota module unavailable; cannot blacklist)'}`);
1251
- if (canBlacklist) {
1252
- await Promise.allSettled(providerKeys.map((providerKey) => quotaModule.disableProvider({ providerKey, mode: 'blacklist', durationMs })));
1253
- }
1254
- }
1255
- console.log(`[antigravity:warmup] summary ok=${okCount} fail=${failCount} total=${providerKeysByAlias.size}`);
1256
- }
1257
- }
1258
- catch {
1259
- // warmup is best-effort; never block server startup
1260
- }
1261
- }
1262
- // Multiple providerKeys may share the same runtimeKey (single provider handle with multiple models).
1263
- // Quota is tracked by providerKey, so we need a stable way to derive authType for every key,
1264
- // even when the handle has already been created for this runtimeKey.
1265
- const runtimeKeyAuthType = new Map();
1266
- const apikeyDailyResetTime = (() => {
1267
- const vr = this.userConfig && typeof this.userConfig === 'object' ? this.userConfig.virtualrouter : null;
1268
- const quota = vr && typeof vr === 'object' && !Array.isArray(vr) ? vr.quota : null;
1269
- if (!quota || typeof quota !== 'object' || Array.isArray(quota)) {
1270
- return null;
1271
- }
1272
- const raw = typeof quota.apikeyDailyResetTime === 'string'
1273
- ? quota.apikeyDailyResetTime
1274
- : typeof quota.apikeyResetTime === 'string'
1275
- ? quota.apikeyResetTime
1276
- : typeof quota.apikeyReset === 'string'
1277
- ? quota.apikeyReset
1278
- : null;
1279
- const trimmed = typeof raw === 'string' ? raw.trim() : '';
1280
- return trimmed ? trimmed : null;
1281
- })();
1282
- const failedRuntimeKeys = new Set();
1283
- for (const [providerKey, runtime] of Object.entries(runtimeMap)) {
1284
- if (!runtime) {
1285
- continue;
1286
- }
1287
- const runtimeKey = runtime.runtimeKey || providerKey;
1288
- const authTypeFromRuntime = runtime && runtime.auth && typeof runtime.auth.type === 'string'
1289
- ? String(runtime.auth.type).trim()
1290
- : null;
1291
- if (failedRuntimeKeys.has(runtimeKey)) {
1292
- continue;
1293
- }
1294
- if (!this.providerHandles.has(runtimeKey)) {
1295
- let patchedRuntime = null;
1296
- try {
1297
- const resolvedRuntime = await this.materializeRuntimeProfile(runtime);
1298
- patchedRuntime = this.applyProviderProfileOverrides(resolvedRuntime);
1299
- try {
1300
- const authTypeFromPatched = patchedRuntime && patchedRuntime.auth && typeof patchedRuntime.auth.type === 'string'
1301
- ? String(patchedRuntime.auth.type).trim()
1302
- : null;
1303
- if (authTypeFromPatched) {
1304
- runtimeKeyAuthType.set(runtimeKey, authTypeFromPatched);
1305
- }
1306
- else if (authTypeFromRuntime) {
1307
- runtimeKeyAuthType.set(runtimeKey, authTypeFromRuntime);
1308
- }
1309
- else if (!runtimeKeyAuthType.has(runtimeKey)) {
1310
- runtimeKeyAuthType.set(runtimeKey, null);
1311
- }
1312
- }
1313
- catch {
1314
- // ignore authType derivation failures
1315
- }
1316
- const handle = await this.createProviderHandle(runtimeKey, patchedRuntime);
1317
- this.providerHandles.set(runtimeKey, handle);
1318
- this.providerRuntimeInitErrors.delete(runtimeKey);
1319
- }
1320
- catch (error) {
1321
- // Non-blocking: do not crash server startup when a provider is misconfigured (e.g. missing env var).
1322
- // Emit a provider error so health/quota modules can mark it unhealthy and routing can fail over.
1323
- failedRuntimeKeys.add(runtimeKey);
1324
- if (error instanceof Error) {
1325
- this.providerRuntimeInitErrors.set(runtimeKey, error);
1326
- }
1327
- else {
1328
- this.providerRuntimeInitErrors.set(runtimeKey, new Error(String(error)));
1329
- }
1330
- try {
1331
- const { emitProviderError } = await import('../../../providers/core/utils/provider-error-reporter.js');
1332
- emitProviderError({
1333
- error,
1334
- stage: 'provider.runtime.init',
1335
- runtime: {
1336
- requestId: `startup_${Date.now()}`,
1337
- providerKey,
1338
- providerId: runtime.providerId || runtimeKey.split('.')[0],
1339
- providerType: String(runtime.providerType || runtime.providerType || 'unknown'),
1340
- providerProtocol: String(runtime.outboundProfile || ''),
1341
- routeName: 'startup',
1342
- pipelineId: 'startup',
1343
- runtimeKey
1344
- },
1345
- dependencies: this.getModuleDependencies(),
1346
- recoverable: false,
1347
- affectsHealth: true,
1348
- details: {
1349
- reason: 'provider_init_failed',
1350
- runtimeKey,
1351
- providerKey
1352
- }
1353
- });
1354
- }
1355
- catch {
1356
- // ignore emit failures
1357
- }
1358
- // Skip this providerKey mapping; requests will fail over if routing selects it.
1359
- continue;
1360
- }
1361
- }
1362
- // Register static quota metadata for every providerKey (not just per runtimeKey).
1363
- // Use the runtimeKey authType when available so shared runtimeKey models inherit correct policy.
1364
- try {
1365
- const authType = runtimeKeyAuthType.get(runtimeKey) ?? authTypeFromRuntime;
1366
- const apikeyResetForKey = authType === 'apikey' ? apikeyDailyResetTime : null;
1367
- quotaModule?.registerProviderStaticConfig?.(providerKey, {
1368
- authType: authType ?? null,
1369
- apikeyDailyResetTime: apikeyResetForKey
1370
- });
1371
- }
1372
- catch {
1373
- // best-effort: quota static config registration must never block runtime init
1374
- }
1375
- // Only map providerKey when runtimeKey has an initialized handle.
1376
- if (this.providerHandles.has(runtimeKey)) {
1377
- this.providerKeyToRuntimeKey.set(providerKey, runtimeKey);
1378
- }
1379
- }
256
+ await initializeProviderRuntimes(this, artifacts);
1380
257
  }
1381
258
  async createProviderHandle(runtimeKey, runtime) {
1382
- const protocolType = normalizeProviderType(runtime.providerType);
1383
- const providerFamily = runtime.providerFamily || protocolType;
1384
- const providerProtocol = (typeof runtime.outboundProfile === 'string' && runtime.outboundProfile.trim()
1385
- ? runtime.outboundProfile.trim()
1386
- : undefined) ?? mapProviderProtocol(protocolType);
1387
- const instance = ProviderFactory.createProviderFromRuntime(runtime, this.getModuleDependencies());
1388
- await instance.initialize();
1389
- const providerId = runtime.providerId || runtimeKey.split('.')[0];
1390
- return {
1391
- runtimeKey,
1392
- providerId,
1393
- providerType: protocolType,
1394
- providerFamily,
1395
- providerProtocol,
1396
- runtime,
1397
- instance
1398
- };
259
+ return await createProviderHandle(this, runtimeKey, runtime);
1399
260
  }
1400
261
  async materializeRuntimeProfile(runtime) {
1401
- const auth = await this.resolveRuntimeAuth(runtime);
1402
- const baseUrl = this.normalizeRuntimeBaseUrl(runtime);
1403
- const identity = resolveProviderIdentity(runtime.providerType, runtime.providerFamily);
1404
- return {
1405
- ...runtime,
1406
- ...(baseUrl ? { baseUrl } : {}),
1407
- providerType: identity.providerType,
1408
- providerFamily: identity.providerFamily,
1409
- auth
1410
- };
262
+ return await materializeRuntimeProfile(this, runtime);
1411
263
  }
1412
264
  normalizeRuntimeBaseUrl(runtime) {
1413
- const candidates = [runtime.baseUrl, runtime.endpoint];
1414
- for (const candidate of candidates) {
1415
- if (typeof candidate === 'string' && candidate.trim()) {
1416
- return candidate.trim();
1417
- }
1418
- }
1419
- return undefined;
265
+ return normalizeRuntimeBaseUrl(this, runtime);
1420
266
  }
1421
267
  async resolveRuntimeAuth(runtime) {
1422
- const auth = runtime.auth || { type: 'apikey' };
1423
- const authRecord = auth;
1424
- const authType = this.normalizeAuthType(auth.type);
1425
- const rawType = typeof auth.rawType === 'string' ? auth.rawType.trim().toLowerCase() : '';
1426
- const pickString = (...candidates) => {
1427
- for (const candidate of candidates) {
1428
- if (typeof candidate === 'string') {
1429
- const trimmed = candidate.trim();
1430
- if (trimmed) {
1431
- return trimmed;
1432
- }
1433
- }
1434
- }
1435
- return undefined;
1436
- };
1437
- const pickStringArray = (value) => {
1438
- if (!value) {
1439
- return undefined;
1440
- }
1441
- if (Array.isArray(value)) {
1442
- const normalized = value
1443
- .map((item) => pickString(item))
1444
- .filter((item) => typeof item === 'string');
1445
- return normalized.length ? normalized : undefined;
1446
- }
1447
- if (typeof value === 'string' && value.trim()) {
1448
- const normalized = value
1449
- .split(/[,\s]+/)
1450
- .map((item) => item.trim())
1451
- .filter(Boolean);
1452
- return normalized.length ? normalized : undefined;
1453
- }
1454
- return undefined;
1455
- };
1456
- if (authType === 'apikey') {
1457
- if (rawType === 'iflow-cookie') {
1458
- return { ...auth, type: 'apikey', rawType: auth.rawType ?? 'iflow-cookie' };
1459
- }
1460
- if (rawType === 'deepseek-account') {
1461
- // Compatibility note:
1462
- // legacy deepseek configs may still carry value/secretRef/mobile/password/clientId/clientSecret.
1463
- // Runtime auth for deepseek-account is tokenFile(+alias)-only; we strip legacy fields instead of
1464
- // failing init so existing deployments can migrate without downtime.
1465
- const tokenFile = pickString(authRecord.tokenFile, authRecord.token_file);
1466
- const accountAlias = pickString(authRecord.accountAlias, authRecord.account_alias, runtime.keyAlias);
1467
- return {
1468
- type: 'apikey',
1469
- rawType: auth.rawType ?? 'deepseek-account',
1470
- value: '',
1471
- ...(accountAlias ? { accountAlias } : {}),
1472
- ...(tokenFile ? { tokenFile } : {})
1473
- };
1474
- }
1475
- const value = await this.resolveApiKeyValue(runtime, auth);
1476
- return { ...auth, type: 'apikey', value };
1477
- }
1478
- const resolved = {
1479
- type: 'oauth',
1480
- secretRef: auth.secretRef,
1481
- value: auth.value,
1482
- oauthProviderId: auth.oauthProviderId,
1483
- rawType: auth.rawType,
1484
- tokenFile: pickString(authRecord.tokenFile, authRecord.token_file),
1485
- tokenUrl: pickString(authRecord.tokenUrl, authRecord.token_url),
1486
- deviceCodeUrl: pickString(authRecord.deviceCodeUrl, authRecord.device_code_url),
1487
- clientId: pickString(authRecord.clientId, authRecord.client_id),
1488
- clientSecret: pickString(authRecord.clientSecret, authRecord.client_secret),
1489
- authorizationUrl: pickString(authRecord.authorizationUrl, authRecord.authorization_url, authRecord.authUrl),
1490
- userInfoUrl: pickString(authRecord.userInfoUrl, authRecord.user_info_url),
1491
- refreshUrl: pickString(authRecord.refreshUrl, authRecord.refresh_url),
1492
- scopes: pickStringArray(authRecord.scopes ?? authRecord.scope)
1493
- };
1494
- let tokenFile = resolved.tokenFile;
1495
- if (!tokenFile && typeof auth.secretRef === 'string' && auth.secretRef.trim()) {
1496
- tokenFile = await this.resolveSecretValue(auth.secretRef.trim());
1497
- }
1498
- resolved.tokenFile = tokenFile;
1499
- return resolved;
268
+ return await resolveRuntimeAuth(this, runtime);
1500
269
  }
1501
270
  async resolveApiKeyValue(runtime, auth) {
1502
- const inline = typeof auth?.value === 'string' ? auth.value.trim() : '';
1503
- if (inline) {
1504
- if (this.isSafeSecretReference(inline)) {
1505
- return await this.resolveSecretValue(inline);
1506
- }
1507
- return inline;
1508
- }
1509
- const rawSecretRef = typeof auth?.secretRef === 'string' ? auth.secretRef.trim() : '';
1510
- // llmswitch-core may populate secretRef as a stable signature (e.g. "provider.alias").
1511
- // Only treat it as a resolvable secret reference when it matches our safe reference grammar.
1512
- if (rawSecretRef && this.isSafeSecretReference(rawSecretRef)) {
1513
- const resolved = await this.resolveSecretValue(rawSecretRef);
1514
- if (resolved) {
1515
- return resolved;
1516
- }
1517
- }
1518
- // Local OpenAI-compatible endpoints (e.g., LM Studio) may not require auth.
1519
- // Keep fail-fast for remote providers by only allowing empty apiKey when baseURL is local.
1520
- const baseURL = typeof runtime?.baseURL === 'string'
1521
- ? String(runtime.baseURL).trim()
1522
- : typeof runtime?.baseUrl === 'string'
1523
- ? String(runtime.baseUrl).trim()
1524
- : typeof runtime?.endpoint === 'string'
1525
- ? String(runtime.endpoint).trim()
1526
- : '';
1527
- if (this.isLocalBaseUrl(baseURL)) {
1528
- return '';
1529
- }
1530
- throw new Error(`Provider runtime "${runtime.runtimeKey || runtime.providerId}" missing API key`);
271
+ return await resolveApiKeyValue(this, runtime, auth);
1531
272
  }
1532
273
  isLocalBaseUrl(value) {
1533
- const raw = typeof value === 'string' ? value.trim() : '';
1534
- if (!raw) {
1535
- return false;
1536
- }
1537
- try {
1538
- const url = new URL(raw);
1539
- const host = String(url.hostname || '').trim().toLowerCase();
1540
- return (host === 'localhost' ||
1541
- host === '127.0.0.1' ||
1542
- host === '0.0.0.0' ||
1543
- host === '::1' ||
1544
- host === '::ffff:127.0.0.1');
1545
- }
1546
- catch {
1547
- const lower = raw.toLowerCase();
1548
- return (lower.includes('localhost') ||
1549
- lower.includes('127.0.0.1') ||
1550
- lower.includes('0.0.0.0') ||
1551
- lower.includes('[::1]'));
1552
- }
274
+ return isLocalBaseUrl(this, value);
1553
275
  }
1554
276
  async disposeProviders() {
1555
- const handles = Array.from(this.providerHandles.values());
1556
- await Promise.all(handles.map(async (handle) => {
1557
- try {
1558
- await handle.instance.cleanup();
1559
- }
1560
- catch {
1561
- // ignore cleanup errors
1562
- }
1563
- }));
1564
- this.providerHandles.clear();
1565
- ProviderFactory.clearInstanceCache?.();
277
+ await disposeProviders(this);
1566
278
  }
1567
279
  async executePipeline(input) {
1568
- if (!this.isPipelineReady()) {
1569
- throw new Error('Hub pipeline runtime is not initialized');
1570
- }
1571
- // Stats must remain stable across provider retries and requestId enhancements.
1572
- const statsRequestId = input.requestId;
1573
- this.stats.recordRequestStart(statsRequestId);
1574
- const initialMetadata = this.buildRequestMetadata(input);
1575
- const providerRequestId = input.requestId;
1576
- const clientRequestId = typeof initialMetadata.clientRequestId === 'string' && initialMetadata.clientRequestId.trim()
1577
- ? initialMetadata.clientRequestId.trim()
1578
- : providerRequestId;
1579
- this.logStage('request.received', providerRequestId, {
1580
- endpoint: input.entryEndpoint,
1581
- stream: initialMetadata.stream === true
1582
- });
1583
- try {
1584
- const headerUa = (typeof input.headers?.['user-agent'] === 'string' && input.headers['user-agent']) ||
1585
- (typeof input.headers?.['User-Agent'] === 'string' && input.headers['User-Agent']);
1586
- const headerOriginator = (typeof input.headers?.['originator'] === 'string' && input.headers['originator']) ||
1587
- (typeof input.headers?.['Originator'] === 'string' && input.headers['Originator']);
1588
- await writeClientSnapshot({
1589
- entryEndpoint: input.entryEndpoint,
1590
- requestId: input.requestId,
1591
- headers: asRecord(input.headers),
1592
- body: input.body,
1593
- metadata: {
1594
- ...initialMetadata,
1595
- userAgent: headerUa,
1596
- clientOriginator: headerOriginator
1597
- }
1598
- });
1599
- }
1600
- catch {
1601
- // snapshot failure should not block request path
1602
- }
1603
- const pipelineLabel = 'hub';
1604
- const iterationMetadata = initialMetadata;
1605
- // _followupTriggered = false;
1606
- // 单次 HTTP 请求内允许多次 failover(不在 Provider 层做重试):
1607
- // - 让 VirtualRouter 根据 excludedProviderKeys 跳过失败目标
1608
- // - 避免客户端“一次就断”导致对话破裂(尤其是 429 / prompt too long 等可恢复错误)
1609
- // - 通过 env 允许按部署/客户端调整:ROUTECODEX_MAX_PROVIDER_ATTEMPTS / RCC_MAX_PROVIDER_ATTEMPTS
1610
- let maxAttempts = resolveMaxProviderAttempts();
1611
- let attempt = 0;
1612
- let firstError = null;
1613
- const originalBodySnapshot = this.cloneRequestPayload(input.body);
1614
- const excludedProviderKeys = new Set();
1615
- let initialRoutePool = null;
1616
- while (attempt < maxAttempts) {
1617
- attempt += 1;
1618
- // 每次尝试前重置请求 body,避免上一轮 HubPipeline 的就地改写导致
1619
- // 第二轮出现 ChatEnvelopeValidationError(messages_missing) 之类的问题。
1620
- if (originalBodySnapshot && typeof originalBodySnapshot === 'object') {
1621
- const cloned = this.cloneRequestPayload(originalBodySnapshot) ??
1622
- { ...originalBodySnapshot };
1623
- input.body = cloned;
1624
- }
1625
- // 为本轮构建独立的 metadata 视图,并注入当前已排除的 providerKey 集合,
1626
- // 让 VirtualRouter 在同一 HTTP 请求内跳过已经 429 过的 key。
1627
- const metadataForIteration = {
1628
- ...iterationMetadata,
1629
- excludedProviderKeys: Array.from(excludedProviderKeys)
1630
- };
1631
- this.logStage(`${pipelineLabel}.start`, providerRequestId, {
1632
- endpoint: input.entryEndpoint,
1633
- stream: metadataForIteration.stream
1634
- });
1635
- const originalRequestSnapshot = this.cloneRequestPayload(input.body);
1636
- let pipelineResult;
1637
- try {
1638
- pipelineResult = await this.runHubPipeline(input, metadataForIteration);
1639
- }
1640
- catch (pipelineError) {
1641
- const pipelineErrorCode = typeof pipelineError.code === 'string'
1642
- ? String(pipelineError.code).trim()
1643
- : '';
1644
- const pipelineErrorMessage = pipelineError instanceof Error
1645
- ? pipelineError.message
1646
- : String(pipelineError ?? 'Unknown error');
1647
- const isPoolExhaustedError = pipelineErrorCode === 'PROVIDER_NOT_AVAILABLE' ||
1648
- pipelineErrorCode === 'ERR_NO_PROVIDER_TARGET' ||
1649
- /all providers unavailable/i.test(pipelineErrorMessage) ||
1650
- /virtual router did not produce a provider target/i.test(pipelineErrorMessage);
1651
- if (firstError && isPoolExhaustedError) {
1652
- throw firstError;
1653
- }
1654
- throw pipelineError;
1655
- }
1656
- const pipelineMetadata = pipelineResult.metadata ?? {};
1657
- const mergedMetadata = { ...metadataForIteration, ...pipelineMetadata };
1658
- this.logStage(`${pipelineLabel}.completed`, providerRequestId, {
1659
- route: pipelineResult.routingDecision?.routeName,
1660
- target: pipelineResult.target?.providerKey
1661
- });
1662
- if (!initialRoutePool && Array.isArray(pipelineResult.routingDecision?.pool)) {
1663
- initialRoutePool = [...pipelineResult.routingDecision.pool];
1664
- }
1665
- const providerPayload = pipelineResult.providerPayload;
1666
- const target = pipelineResult.target;
1667
- if (!providerPayload || !target?.providerKey) {
1668
- throw Object.assign(new Error('Virtual router did not produce a provider target'), {
1669
- code: 'ERR_NO_PROVIDER_TARGET',
1670
- requestId: input.requestId
1671
- });
1672
- }
1673
- // Ensure response-side conversion has access to route-selected target metadata (compat profiles, etc.).
1674
- // This is an execution metadata carrier only; tool/routing semantics still live in llmswitch-core.
1675
- if (!mergedMetadata.target) {
1676
- mergedMetadata.target = target;
1677
- }
1678
- if (!mergedMetadata.compatibilityProfile && typeof target.compatibilityProfile === 'string' && target.compatibilityProfile.trim()) {
1679
- mergedMetadata.compatibilityProfile = target.compatibilityProfile.trim();
1680
- }
1681
- const runtimeKey = target.runtimeKey || this.providerKeyToRuntimeKey.get(target.providerKey);
1682
- if (!runtimeKey) {
1683
- const error = Object.assign(new Error(`Runtime for provider ${target.providerKey} not initialized`), {
1684
- code: 'ERR_RUNTIME_NOT_FOUND',
1685
- requestId: input.requestId,
1686
- retryable: true
1687
- });
1688
- try {
1689
- const { emitProviderError } = await import('../../../providers/core/utils/provider-error-reporter.js');
1690
- emitProviderError({
1691
- error,
1692
- stage: 'provider.runtime.resolve',
1693
- runtime: {
1694
- requestId: input.requestId,
1695
- providerKey: target.providerKey,
1696
- providerId: target.providerKey.split('.')[0],
1697
- providerType: String(target.providerType || 'unknown'),
1698
- providerProtocol: String(target.outboundProfile || ''),
1699
- routeName: pipelineResult.routingDecision?.routeName,
1700
- pipelineId: target.providerKey,
1701
- target
1702
- },
1703
- dependencies: this.getModuleDependencies(),
1704
- recoverable: false,
1705
- affectsHealth: true,
1706
- details: {
1707
- reason: 'runtime_not_initialized',
1708
- providerKey: target.providerKey
1709
- }
1710
- });
1711
- }
1712
- catch {
1713
- // best-effort
1714
- }
1715
- if (!firstError) {
1716
- firstError = error;
1717
- }
1718
- excludedProviderKeys.add(target.providerKey);
1719
- this.logStage('provider.retry', input.requestId, {
1720
- providerKey: target.providerKey,
1721
- attempt,
1722
- nextAttempt: attempt + 1,
1723
- excluded: Array.from(excludedProviderKeys),
1724
- reason: 'runtime_not_initialized'
1725
- });
1726
- continue;
1727
- }
1728
- const handle = this.providerHandles.get(runtimeKey);
1729
- if (!handle) {
1730
- const initError = this.providerRuntimeInitErrors.get(runtimeKey);
1731
- const error = Object.assign(new Error(initError
1732
- ? `Provider runtime ${runtimeKey} failed to initialize: ${initError.message}`
1733
- : `Provider runtime ${runtimeKey} not found`), {
1734
- code: 'ERR_PROVIDER_NOT_FOUND',
1735
- requestId: input.requestId,
1736
- retryable: true
1737
- });
1738
- try {
1739
- const { emitProviderError } = await import('../../../providers/core/utils/provider-error-reporter.js');
1740
- emitProviderError({
1741
- error: initError ?? error,
1742
- stage: 'provider.runtime.resolve',
1743
- runtime: {
1744
- requestId: input.requestId,
1745
- providerKey: target.providerKey,
1746
- providerId: target.providerKey.split('.')[0],
1747
- providerType: String(target.providerType || 'unknown'),
1748
- providerProtocol: String(target.outboundProfile || ''),
1749
- routeName: pipelineResult.routingDecision?.routeName,
1750
- pipelineId: target.providerKey,
1751
- runtimeKey,
1752
- target
1753
- },
1754
- dependencies: this.getModuleDependencies(),
1755
- recoverable: false,
1756
- affectsHealth: true,
1757
- details: {
1758
- reason: 'runtime_handle_missing',
1759
- providerKey: target.providerKey,
1760
- runtimeKey
1761
- }
1762
- });
1763
- }
1764
- catch {
1765
- // best-effort
1766
- }
1767
- if (!firstError) {
1768
- firstError = error;
1769
- }
1770
- excludedProviderKeys.add(target.providerKey);
1771
- this.logStage('provider.retry', input.requestId, {
1772
- providerKey: target.providerKey,
1773
- attempt,
1774
- nextAttempt: attempt + 1,
1775
- excluded: Array.from(excludedProviderKeys),
1776
- reason: 'provider_runtime_missing'
1777
- });
1778
- continue;
1779
- }
1780
- const providerProtocol = target.outboundProfile ||
1781
- handle.providerProtocol;
1782
- const metadataModel = mergedMetadata?.target && typeof mergedMetadata.target === 'object'
1783
- ? mergedMetadata.target.clientModelId
1784
- : undefined;
1785
- const rawModel = this.extractProviderModel(providerPayload) ||
1786
- (typeof metadataModel === 'string' ? metadataModel : undefined);
1787
- const providerIdToken = target.providerKey || handle.providerId || runtimeKey;
1788
- if (!providerIdToken) {
1789
- throw Object.assign(new Error('Provider identifier missing for request'), {
1790
- code: 'ERR_PROVIDER_ID_MISSING',
1791
- requestId: providerRequestId
1792
- });
1793
- }
1794
- const enhancedRequestId = enhanceProviderRequestId(providerRequestId, {
1795
- entryEndpoint: input.entryEndpoint,
1796
- providerId: providerIdToken,
1797
- model: rawModel
1798
- });
1799
- // /v1/responses tool loop depends on a stable requestId mapping between:
1800
- // - inbound conversion capture (uses the initial requestId), and
1801
- // - outbound response record (may run after requestId enhancement).
1802
- // Enhancement happens for all providers, so rebind must be keyed off the client endpoint,
1803
- // not the providerProtocol (which can be gemini-chat/anthropic-messages/etc.).
1804
- if (String(input.entryEndpoint || '').startsWith('/v1/responses')) {
1805
- try {
1806
- await rebindResponsesConversationRequestId(providerRequestId, enhancedRequestId);
1807
- }
1808
- catch {
1809
- /* ignore rebind failures */
1810
- }
1811
- }
1812
- if (enhancedRequestId !== input.requestId) {
1813
- input.requestId = enhancedRequestId;
1814
- }
1815
- mergedMetadata.clientRequestId = clientRequestId;
1816
- const providerModel = rawModel;
1817
- const providerLabel = this.buildProviderLabel(target.providerKey, providerModel);
1818
- this.logStage('provider.prepare', input.requestId, {
1819
- providerKey: target.providerKey,
1820
- runtimeKey,
1821
- protocol: providerProtocol,
1822
- providerType: handle.providerType,
1823
- providerFamily: handle.providerFamily,
1824
- model: providerModel,
1825
- providerLabel
1826
- });
1827
- attachProviderRuntimeMetadata(providerPayload, {
1828
- requestId: input.requestId,
1829
- providerId: handle.providerId,
1830
- providerKey: target.providerKey,
1831
- providerType: handle.providerType,
1832
- providerFamily: handle.providerFamily,
1833
- providerProtocol,
1834
- pipelineId: target.providerKey,
1835
- routeName: pipelineResult.routingDecision?.routeName,
1836
- runtimeKey,
1837
- target,
1838
- metadata: mergedMetadata
1839
- });
1840
- this.stats.bindProvider(statsRequestId, {
1841
- providerKey: target.providerKey,
1842
- providerType: handle.providerType,
1843
- model: providerModel
1844
- });
1845
- this.logStage('provider.send.start', input.requestId, {
1846
- providerKey: target.providerKey,
1847
- runtimeKey,
1848
- protocol: providerProtocol,
1849
- providerType: handle.providerType,
1850
- providerFamily: handle.providerFamily,
1851
- model: providerModel,
1852
- providerLabel
1853
- });
1854
- try {
1855
- const providerResponse = await handle.instance.processIncoming(providerPayload);
1856
- const responseStatus = this.extractResponseStatus(providerResponse);
1857
- this.logStage('provider.send.completed', input.requestId, {
1858
- providerKey: target.providerKey,
1859
- status: responseStatus,
1860
- providerType: handle.providerType,
1861
- providerFamily: handle.providerFamily,
1862
- model: providerModel,
1863
- providerLabel
1864
- });
1865
- const wantsStreamBase = Boolean(input.metadata?.inboundStream ?? input.metadata?.stream);
1866
- const normalized = this.normalizeProviderResponse(providerResponse);
1867
- const pipelineProcessed = pipelineResult.processedRequest;
1868
- const pipelineStandardized = pipelineResult.standardizedRequest;
1869
- const requestSemantics = pipelineProcessed && typeof pipelineProcessed === 'object' && typeof pipelineProcessed.semantics === 'object'
1870
- ? pipelineProcessed.semantics
1871
- : pipelineStandardized && typeof pipelineStandardized === 'object' && typeof pipelineStandardized.semantics === 'object'
1872
- ? pipelineStandardized.semantics
1873
- : undefined;
1874
- const converted = await this.convertProviderResponseIfNeeded({
1875
- entryEndpoint: input.entryEndpoint,
1876
- providerType: handle.providerType,
1877
- requestId: input.requestId,
1878
- wantsStream: wantsStreamBase,
1879
- originalRequest: originalRequestSnapshot,
1880
- requestSemantics: requestSemantics,
1881
- processMode: pipelineResult.processMode,
1882
- response: normalized,
1883
- pipelineMetadata: mergedMetadata
1884
- });
1885
- // Treat upstream 429 as provider failure across protocols to avoid
1886
- // silently returning success and to let Virtual Router failover to other candidates.
1887
- // Keep existing Gemini compatibility behavior for 400/4xx thoughtSignature-like failures.
1888
- const convertedStatus = typeof converted.status === 'number' ? converted.status : undefined;
1889
- const isGlobalRetryable429 = convertedStatus === 429;
1890
- const isGeminiCompatFailure = typeof convertedStatus === 'number' &&
1891
- convertedStatus >= 400 &&
1892
- (isAntigravityProviderKey(target.providerKey) ||
1893
- (typeof target.providerKey === 'string' && target.providerKey.startsWith('gemini-cli.'))) &&
1894
- providerProtocol === 'gemini-chat';
1895
- if (isGlobalRetryable429 || isGeminiCompatFailure) {
1896
- const bodyForError = converted.body && typeof converted.body === 'object'
1897
- ? converted.body
1898
- : undefined;
1899
- const errMsg = bodyForError && bodyForError.error && typeof bodyForError.error === 'object'
1900
- ? String(bodyForError.error.message || bodyForError.error || '')
1901
- : '';
1902
- const statusCode = typeof convertedStatus === 'number' ? convertedStatus : 500;
1903
- const errorToThrow = new Error(errMsg && errMsg.trim().length ? errMsg : `HTTP ${statusCode}`);
1904
- errorToThrow.statusCode = statusCode;
1905
- errorToThrow.status = statusCode;
1906
- errorToThrow.response = { data: bodyForError };
1907
- try {
1908
- const { emitProviderError } = await import('../../../providers/core/utils/provider-error-reporter.js');
1909
- emitProviderError({
1910
- error: errorToThrow,
1911
- stage: 'provider.http',
1912
- runtime: {
1913
- requestId: input.requestId,
1914
- providerKey: target.providerKey,
1915
- providerId: handle.providerId,
1916
- providerType: handle.providerType,
1917
- providerFamily: handle.providerFamily,
1918
- providerProtocol,
1919
- routeName: pipelineResult.routingDecision?.routeName,
1920
- pipelineId: target.providerKey,
1921
- target,
1922
- runtimeKey
1923
- },
1924
- dependencies: this.getModuleDependencies(),
1925
- statusCode,
1926
- recoverable: statusCode === 429,
1927
- affectsHealth: true,
1928
- details: {
1929
- source: 'converted_response_status',
1930
- convertedStatus: statusCode,
1931
- wrappedErrorResponse: true
1932
- }
1933
- });
1934
- }
1935
- catch {
1936
- // best-effort; never block retry/failover path
1937
- }
1938
- throw errorToThrow;
1939
- }
1940
- const usage = this.extractUsageFromResult(converted, mergedMetadata);
1941
- // QuotaManager listens to provider error/success events; avoid duplicating accounting here.
1942
- this.stats.recordCompletion(statsRequestId, { usage, error: false });
1943
- // Notify llmswitch-core about successful completion so session-scoped routing state
1944
- // (e.g. Antigravity alias bindings) can be committed only after the first success.
1945
- try {
1946
- const center = await getProviderSuccessCenter().catch(() => null);
1947
- const sessionId = typeof mergedMetadata.sessionId === 'string' && mergedMetadata.sessionId.trim()
1948
- ? mergedMetadata.sessionId.trim()
1949
- : undefined;
1950
- const conversationId = typeof mergedMetadata.conversationId === 'string' && mergedMetadata.conversationId.trim()
1951
- ? mergedMetadata.conversationId.trim()
1952
- : undefined;
1953
- center?.emit({
1954
- runtime: {
1955
- requestId: input.requestId,
1956
- routeName: pipelineResult.routingDecision?.routeName,
1957
- providerKey: target.providerKey,
1958
- providerId: handle.providerId,
1959
- providerType: handle.providerType,
1960
- providerProtocol,
1961
- pipelineId: target.providerKey,
1962
- target
1963
- },
1964
- timestamp: Date.now(),
1965
- metadata: {
1966
- ...(sessionId ? { sessionId } : {}),
1967
- ...(conversationId ? { conversationId } : {})
1968
- }
1969
- });
1970
- }
1971
- catch {
1972
- // best-effort: must never affect request path
1973
- }
1974
- // 回传 session_id 和 conversation_id 到响应头(如果存在)
1975
- const sessionId = typeof mergedMetadata.sessionId === "string" && mergedMetadata.sessionId.trim()
1976
- ? mergedMetadata.sessionId.trim()
1977
- : undefined;
1978
- let conversationId = typeof mergedMetadata.conversationId === "string" && mergedMetadata.conversationId.trim()
1979
- ? mergedMetadata.conversationId.trim()
1980
- : undefined;
1981
- // 对称补齐:如果只有 session_id,则回传 conversation_id=session_id
1982
- if (!conversationId && sessionId) {
1983
- conversationId = sessionId;
1984
- }
1985
- if (sessionId || conversationId) {
1986
- if (!converted.headers) {
1987
- converted.headers = {};
1988
- }
1989
- if (sessionId && !converted.headers["session_id"]) {
1990
- converted.headers["session_id"] = sessionId;
1991
- }
1992
- if (conversationId && !converted.headers["conversation_id"]) {
1993
- converted.headers["conversation_id"] = conversationId;
1994
- }
1995
- }
1996
- return converted;
1997
- }
1998
- catch (error) {
1999
- this.logStage('provider.send.error', input.requestId, {
2000
- providerKey: target.providerKey,
2001
- message: error instanceof Error ? error.message : String(error ?? 'Unknown error'),
2002
- providerType: handle.providerType,
2003
- providerFamily: handle.providerFamily,
2004
- model: providerModel,
2005
- providerLabel
2006
- });
2007
- if (isAntigravityProviderKey(target.providerKey) && extractStatusCodeFromError(error) === 429) {
2008
- maxAttempts = Math.max(maxAttempts, resolveAntigravityMaxProviderAttempts());
2009
- }
2010
- // QuotaManager listens to provider error events emitted by providers.
2011
- if (!firstError) {
2012
- firstError = error;
2013
- }
2014
- const shouldRetry = attempt < maxAttempts && shouldRetryProviderError(error);
2015
- if (!shouldRetry) {
2016
- this.stats.recordCompletion(statsRequestId, { error: true });
2017
- throw error;
2018
- }
2019
- // Record this failed provider attempt even if the overall request succeeds later via failover.
2020
- this.stats.recordCompletion(statsRequestId, { error: true });
2021
- const singleProviderPool = Boolean(initialRoutePool && initialRoutePool.length === 1 && initialRoutePool[0] === target.providerKey);
2022
- if (singleProviderPool) {
2023
- if (isNetworkTransportError(error)) {
2024
- await waitBeforeRetry(error);
2025
- }
2026
- }
2027
- else if (target.providerKey) {
2028
- excludedProviderKeys.add(target.providerKey);
2029
- }
2030
- this.logStage('provider.retry', input.requestId, {
2031
- providerKey: target.providerKey,
2032
- attempt,
2033
- nextAttempt: attempt + 1,
2034
- excluded: Array.from(excludedProviderKeys),
2035
- reason: describeRetryReason(error)
2036
- });
2037
- continue;
2038
- }
2039
- }
2040
- // best-effort: if failover attempt could not produce a successful response,
2041
- // return the original error (avoid masking 429 with PROVIDER_NOT_AVAILABLE etc.).
2042
- this.stats.recordCompletion(statsRequestId, { error: true });
2043
- throw firstError ?? new Error('Provider execution failed without response');
2044
- }
2045
- async runHubPipeline(input, metadata) {
2046
- if (!this.hubPipeline) {
2047
- throw new Error('Hub pipeline runtime is not initialized');
2048
- }
2049
- const payload = asRecord(input.body);
2050
- const isInternalFollowup = asRecord(metadata.__rt)?.serverToolFollowup === true;
2051
- const wantsShadowCompare = !isInternalFollowup && shouldRunHubShadowCompare(this.hubShadowCompareConfig);
2052
- const pipelineInput = {
2053
- ...input,
2054
- id: input.requestId,
2055
- endpoint: input.entryEndpoint,
2056
- metadata: {
2057
- ...metadata,
2058
- logger: this.coloredLogger,
2059
- ...(wantsShadowCompare
2060
- ? { __hubShadowCompare: { baselineMode: this.hubShadowCompareConfig.baselineMode } }
2061
- : {})
2062
- },
2063
- payload
2064
- };
2065
- const result = await this.hubPipeline.execute(pipelineInput);
2066
- if (!result.providerPayload || !result.target?.providerKey) {
2067
- throw Object.assign(new Error('Virtual router did not produce a provider target'), {
2068
- code: 'ERR_NO_PROVIDER_TARGET',
2069
- requestId: input.requestId
2070
- });
2071
- }
2072
- const processMode = result.metadata?.processMode ?? 'chat';
2073
- const resultRecord = result;
2074
- const derivedRequestId = typeof resultRecord.requestId === 'string'
2075
- ? resultRecord.requestId
2076
- : input.requestId;
2077
- const llmsEngineShadowEnabled = !isInternalFollowup &&
2078
- isLlmsEngineShadowEnabledForSubpath(this.llmsEngineShadowConfig, 'conversion/hub/pipeline');
2079
- if (llmsEngineShadowEnabled) {
2080
- // Fail fast: if shadow is enabled for this module, engine core must be available.
2081
- await this.ensureHubPipelineEngineShadow();
2082
- }
2083
- const wantsLlmsEngineShadow = llmsEngineShadowEnabled &&
2084
- shouldRunLlmsEngineShadowForSubpath(this.llmsEngineShadowConfig, 'conversion/hub/pipeline');
2085
- // Unified Hub Framework V1: runtime black-box shadow compare (baseline policy vs current policy).
2086
- // - baseline payload is computed in the SAME hub pipeline execution (single-pass)
2087
- // - only writes errorsample when diff exists
2088
- try {
2089
- const shadow = result.metadata?.hubShadowCompare;
2090
- const baselineProviderPayload = shadow && typeof shadow === 'object' && !Array.isArray(shadow)
2091
- ? shadow.baselineProviderPayload
2092
- : undefined;
2093
- if (wantsShadowCompare && baselineProviderPayload && typeof baselineProviderPayload === 'object') {
2094
- const entryEndpoint = String(input.entryEndpoint || '/v1/chat/completions');
2095
- const routeHint = typeof metadata.routeHint === 'string'
2096
- ? String(metadata.routeHint)
2097
- : undefined;
2098
- const excludedProviderKeys = Array.isArray(metadata.excludedProviderKeys)
2099
- ? metadata.excludedProviderKeys
2100
- : [];
2101
- const cloneJsonSafe = (value) => {
2102
- try {
2103
- return JSON.parse(JSON.stringify(value));
2104
- }
2105
- catch {
2106
- return value;
2107
- }
2108
- };
2109
- const candidateOut = {
2110
- providerPayload: cloneJsonSafe(result.providerPayload),
2111
- target: cloneJsonSafe(result.target),
2112
- metadata: {
2113
- entryEndpoint: result.metadata?.entryEndpoint,
2114
- providerProtocol: result.metadata?.providerProtocol,
2115
- processMode: result.metadata?.processMode,
2116
- stream: result.metadata?.stream,
2117
- routeHint: result.metadata?.routeHint
2118
- }
2119
- };
2120
- void (async () => {
2121
- try {
2122
- const baselineOut = {
2123
- providerPayload: cloneJsonSafe(baselineProviderPayload),
2124
- target: shadow && typeof shadow === 'object' && !Array.isArray(shadow) && shadow.baselineTarget
2125
- ? cloneJsonSafe(shadow.baselineTarget)
2126
- : cloneJsonSafe(result.target),
2127
- metadata: {
2128
- entryEndpoint: result.metadata?.entryEndpoint,
2129
- providerProtocol: result.metadata?.providerProtocol,
2130
- processMode: result.metadata?.processMode,
2131
- stream: result.metadata?.stream,
2132
- routeHint: result.metadata?.routeHint
2133
- }
2134
- };
2135
- await recordHubShadowCompareDiff({
2136
- requestId: derivedRequestId,
2137
- entryEndpoint,
2138
- routeHint,
2139
- excludedProviderKeys,
2140
- baselineMode: this.hubShadowCompareConfig.baselineMode,
2141
- candidateMode: typeof shadow?.candidateMode === 'string' ? shadow.candidateMode : (this.hubPolicyMode ?? undefined),
2142
- baselineOut,
2143
- candidateOut
2144
- });
2145
- }
2146
- catch (error) {
2147
- // eslint-disable-next-line no-console
2148
- console.error('[unified-hub-shadow-runtime] baseline compare failed:', error);
2149
- }
2150
- })();
2151
- }
2152
- }
2153
- catch {
2154
- // best-effort only
2155
- }
2156
- // llms-engine: runtime black-box shadow compare (TS vs engine) for hub pipeline.
2157
- if (wantsLlmsEngineShadow) {
2158
- const cloneJsonSafe = (value) => {
2159
- try {
2160
- return JSON.parse(JSON.stringify(value));
2161
- }
2162
- catch {
2163
- return value;
2164
- }
2165
- };
2166
- const entryEndpoint = String(input.entryEndpoint || '/v1/chat/completions');
2167
- const routeHint = typeof metadata.routeHint === 'string'
2168
- ? String(metadata.routeHint)
2169
- : undefined;
2170
- const baselineOut = {
2171
- providerPayload: cloneJsonSafe(result.providerPayload),
2172
- target: cloneJsonSafe(result.target),
2173
- metadata: {
2174
- entryEndpoint: result.metadata?.entryEndpoint,
2175
- providerProtocol: result.metadata?.providerProtocol,
2176
- processMode: result.metadata?.processMode,
2177
- stream: result.metadata?.stream,
2178
- routeHint: result.metadata?.routeHint
2179
- }
2180
- };
2181
- void (async () => {
2182
- try {
2183
- const shadowPipeline = await this.ensureHubPipelineEngineShadow();
2184
- const shadowRequestId = `${input.requestId}__llms_engine_shadow`;
2185
- const baseMeta = pipelineInput.metadata;
2186
- const shadowInput = {
2187
- ...pipelineInput,
2188
- id: shadowRequestId,
2189
- endpoint: input.entryEndpoint,
2190
- metadata: {
2191
- ...(baseMeta && typeof baseMeta === 'object' ? baseMeta : {}),
2192
- __llmsEngineShadow: { baselineRequestId: derivedRequestId, entryEndpoint, routeHint }
2193
- },
2194
- payload: cloneJsonSafe(payload)
2195
- };
2196
- const shadowResult = await shadowPipeline.execute(shadowInput);
2197
- if (!shadowResult?.providerPayload || !shadowResult?.target) {
2198
- return;
2199
- }
2200
- const candidateOut = {
2201
- providerPayload: cloneJsonSafe(shadowResult.providerPayload),
2202
- target: cloneJsonSafe(shadowResult.target),
2203
- metadata: {
2204
- entryEndpoint: shadowResult.metadata?.entryEndpoint,
2205
- providerProtocol: shadowResult.metadata?.providerProtocol,
2206
- processMode: shadowResult.metadata?.processMode,
2207
- stream: shadowResult.metadata?.stream,
2208
- routeHint: shadowResult.metadata?.routeHint
2209
- }
2210
- };
2211
- await recordLlmsEngineShadowDiff({
2212
- group: 'hub-pipeline',
2213
- requestId: derivedRequestId,
2214
- subpath: 'conversion/hub/pipeline',
2215
- baselineImpl: 'ts',
2216
- candidateImpl: 'engine',
2217
- baselineOut,
2218
- candidateOut
2219
- });
2220
- }
2221
- catch (error) {
2222
- // eslint-disable-next-line no-console
2223
- console.error('[llms-engine-shadow] hub pipeline shadow failed:', error);
2224
- }
2225
- })();
2226
- }
2227
- return {
2228
- requestId: derivedRequestId,
2229
- providerPayload: result.providerPayload,
2230
- standardizedRequest: result.standardizedRequest && typeof result.standardizedRequest === 'object'
2231
- ? result.standardizedRequest
2232
- : undefined,
2233
- processedRequest: result.processedRequest && typeof result.processedRequest === 'object'
2234
- ? result.processedRequest
2235
- : undefined,
2236
- target: result.target,
2237
- routingDecision: result.routingDecision ?? undefined,
2238
- processMode,
2239
- metadata: result.metadata ?? {}
2240
- };
2241
- }
2242
- buildRequestMetadata(input) {
2243
- const userMeta = asRecord(input.metadata);
2244
- const headers = asRecord(input.headers);
2245
- const inboundUserAgent = this.extractHeaderValue(headers, 'user-agent');
2246
- const inboundOriginator = this.extractHeaderValue(headers, 'originator');
2247
- const resolvedUserAgent = typeof userMeta.userAgent === 'string' && userMeta.userAgent.trim()
2248
- ? userMeta.userAgent.trim()
2249
- : inboundUserAgent;
2250
- const resolvedOriginator = typeof userMeta.clientOriginator === 'string' && userMeta.clientOriginator.trim()
2251
- ? userMeta.clientOriginator.trim()
2252
- : inboundOriginator;
2253
- const routeHint = this.extractRouteHint(input) ?? userMeta.routeHint;
2254
- const processMode = userMeta.processMode || 'chat';
2255
- const runtimeFromUser = asRecord(userMeta.runtime);
2256
- const llmsVersion = resolveLlmswitchCoreVersion();
2257
- const metadata = {
2258
- ...userMeta,
2259
- entryEndpoint: input.entryEndpoint,
2260
- processMode,
2261
- direction: 'request',
2262
- stage: 'inbound',
2263
- routeHint,
2264
- stream: userMeta.stream === true,
2265
- runtime: {
2266
- ...(runtimeFromUser ?? {}),
2267
- routecodex: {
2268
- version: buildInfo.version,
2269
- mode: buildInfo.mode
2270
- },
2271
- llmswitchCore: llmsVersion ? { version: llmsVersion } : undefined,
2272
- node: { version: process.version }
2273
- },
2274
- ...(resolvedUserAgent ? { userAgent: resolvedUserAgent } : {}),
2275
- ...(resolvedOriginator ? { clientOriginator: resolvedOriginator } : {})
2276
- };
2277
- // 将原始客户端请求头快照到 metadata.clientHeaders,便于 llmswitch-core
2278
- // 的 extractSessionIdentifiersFromMetadata 从中解析 session_id / conversation_id。
2279
- if (!metadata.clientHeaders && headers && Object.keys(headers).length) {
2280
- const clientHeaders = {};
2281
- for (const [key, value] of Object.entries(headers)) {
2282
- if (typeof value === 'string') {
2283
- const trimmed = value.trim();
2284
- if (trimmed) {
2285
- clientHeaders[key] = trimmed;
2286
- }
2287
- }
2288
- else if (Array.isArray(value) && value.length) {
2289
- const first = String(value[0]).trim();
2290
- if (first) {
2291
- clientHeaders[key] = first;
2292
- }
2293
- }
2294
- }
2295
- if (Object.keys(clientHeaders).length) {
2296
- metadata.clientHeaders = clientHeaders;
2297
- }
2298
- }
2299
- // 在 Host 入口统一解析会话标识,后续 HubPipeline / servertool 等模块仅依赖
2300
- // sessionId / conversationId 字段,不再重复解析 clientHeaders。
2301
- const identifiers = extractSessionIdentifiersFromMetadata(metadata);
2302
- if (identifiers.sessionId) {
2303
- metadata.sessionId = identifiers.sessionId;
2304
- }
2305
- if (identifiers.conversationId) {
2306
- metadata.conversationId = identifiers.conversationId;
2307
- }
2308
- return metadata;
2309
- }
2310
- extractHeaderValue(headers, name) {
2311
- if (!headers) {
2312
- return undefined;
2313
- }
2314
- const target = name.toLowerCase();
2315
- for (const [key, value] of Object.entries(headers)) {
2316
- if (key.toLowerCase() !== target) {
2317
- continue;
2318
- }
2319
- if (typeof value === 'string') {
2320
- return value.trim() || undefined;
2321
- }
2322
- if (Array.isArray(value) && value.length) {
2323
- return String(value[0]).trim() || undefined;
2324
- }
2325
- return undefined;
2326
- }
2327
- return undefined;
2328
- }
2329
- extractRouteHint(input) {
2330
- const header = input.headers['x-route-hint'];
2331
- if (typeof header === 'string' && header.trim()) {
2332
- return header.trim();
2333
- }
2334
- if (Array.isArray(header) && header[0]) {
2335
- return String(header[0]);
2336
- }
2337
- return undefined;
2338
- }
2339
- extractResponseStatus(response) {
2340
- if (!response || typeof response !== 'object') {
2341
- return undefined;
2342
- }
2343
- const candidate = response.status;
2344
- return typeof candidate === 'number' ? candidate : undefined;
2345
- }
2346
- normalizeProviderResponse(response) {
2347
- const status = this.extractResponseStatus(response);
2348
- const headers = this.normalizeProviderResponseHeaders(response && typeof response === 'object' ? response.headers : undefined);
2349
- const body = response && typeof response === 'object' && 'data' in response
2350
- ? response.data
2351
- : response;
2352
- return { status, headers, body };
2353
- }
2354
- normalizeProviderResponseHeaders(headers) {
2355
- if (!headers || typeof headers !== 'object') {
2356
- return undefined;
2357
- }
2358
- const normalized = {};
2359
- for (const [key, value] of Object.entries(headers)) {
2360
- if (typeof value === 'string') {
2361
- normalized[key.toLowerCase()] = value;
2362
- }
2363
- }
2364
- return Object.keys(normalized).length ? normalized : undefined;
2365
- }
2366
- extractUsageFromResult(result, metadata) {
2367
- const candidates = [];
2368
- if (metadata && typeof metadata === 'object') {
2369
- const bag = metadata;
2370
- if (bag.usage) {
2371
- candidates.push(bag.usage);
2372
- }
2373
- }
2374
- if (result.body && typeof result.body === 'object') {
2375
- const body = result.body;
2376
- if (body.usage) {
2377
- candidates.push(body.usage);
2378
- }
2379
- if (body.response && typeof body.response === 'object') {
2380
- const responseNode = body.response;
2381
- if (responseNode.usage) {
2382
- candidates.push(responseNode.usage);
2383
- }
2384
- }
2385
- }
2386
- for (const candidate of candidates) {
2387
- const normalized = this.normalizeUsage(candidate);
2388
- if (normalized) {
2389
- return normalized;
2390
- }
2391
- }
2392
- return undefined;
2393
- }
2394
- normalizeUsage(value) {
2395
- if (!value || typeof value !== 'object') {
2396
- return undefined;
2397
- }
2398
- const record = value;
2399
- const prompt = typeof record.prompt_tokens === 'number'
2400
- ? record.prompt_tokens
2401
- : typeof record.input_tokens === 'number'
2402
- ? record.input_tokens
2403
- : undefined;
2404
- const completion = typeof record.completion_tokens === 'number'
2405
- ? record.completion_tokens
2406
- : typeof record.output_tokens === 'number'
2407
- ? record.output_tokens
2408
- : undefined;
2409
- let total = typeof record.total_tokens === 'number'
2410
- ? record.total_tokens
2411
- : undefined;
2412
- if (total === undefined && prompt !== undefined && completion !== undefined) {
2413
- total = prompt + completion;
2414
- }
2415
- if (prompt === undefined && completion === undefined && total === undefined) {
2416
- return undefined;
2417
- }
2418
- return {
2419
- prompt_tokens: prompt,
2420
- completion_tokens: completion,
2421
- total_tokens: total
2422
- };
2423
- }
2424
- async convertProviderResponseIfNeeded(options) {
2425
- const body = options.response.body;
2426
- if (body && typeof body === 'object') {
2427
- const wrapperError = this.extractSseWrapperError(body);
2428
- if (wrapperError) {
2429
- const codeSuffix = wrapperError.errorCode ? ` [${wrapperError.errorCode}]` : '';
2430
- const error = new Error(`Upstream SSE error event${codeSuffix}: ${wrapperError.message}`);
2431
- error.code = 'SSE_DECODE_ERROR';
2432
- if (wrapperError.errorCode) {
2433
- error.upstreamCode = wrapperError.errorCode;
2434
- }
2435
- error.retryable = wrapperError.retryable;
2436
- if (wrapperError.retryable) {
2437
- error.status = 503;
2438
- error.statusCode = 503;
2439
- }
2440
- throw error;
2441
- }
2442
- }
2443
- if (options.processMode === 'passthrough' && !options.wantsStream) {
2444
- return options.response;
2445
- }
2446
- const entry = (options.entryEndpoint || '').toLowerCase();
2447
- const needsAnthropicConversion = entry.includes('/v1/messages');
2448
- const needsResponsesConversion = entry.includes('/v1/responses');
2449
- const needsChatConversion = entry.includes('/v1/chat/completions');
2450
- if (!needsAnthropicConversion && !needsResponsesConversion && !needsChatConversion) {
2451
- return options.response;
2452
- }
2453
- if (!body || typeof body !== 'object') {
2454
- return options.response;
2455
- }
2456
- try {
2457
- const providerProtocol = mapProviderProtocol(options.providerType);
2458
- const metadataBag = asRecord(options.pipelineMetadata);
2459
- const originalModelId = this.extractClientModelId(metadataBag, options.originalRequest);
2460
- const assignedModelId = typeof metadataBag?.assignedModelId === 'string'
2461
- ? String(metadataBag.assignedModelId)
2462
- : metadataBag &&
2463
- typeof metadataBag === 'object' &&
2464
- metadataBag.target &&
2465
- typeof metadataBag.target === 'object' &&
2466
- typeof metadataBag.target.modelId === 'string'
2467
- ? metadataBag.target.modelId
2468
- : typeof metadataBag?.modelId === 'string'
2469
- ? String(metadataBag.modelId)
2470
- : undefined;
2471
- // 以 HubPipeline metadata 为基础构建 AdapterContext,确保诸如
2472
- // capturedChatRequest / webSearch / routeHint 等字段在响应侧可见,
2473
- // 便于 llmswitch-core 内部实现 servertool/web_search 的第三跳。
2474
- const baseContext = {
2475
- ...(metadataBag ?? {})
2476
- };
2477
- if (baseContext.capturedChatRequest === undefined &&
2478
- options.originalRequest &&
2479
- typeof options.originalRequest === 'object' &&
2480
- !Array.isArray(options.originalRequest)) {
2481
- baseContext.capturedChatRequest = options.originalRequest;
2482
- }
2483
- // 将 HubPipeline metadata.routeName 映射为 AdapterContext.routeId,
2484
- // 便于 llmswitch-core 在第三跳中使用 routeHint 复用首次路由决策。
2485
- if (typeof metadataBag?.routeName === 'string') {
2486
- baseContext.routeId = metadataBag.routeName;
2487
- }
2488
- baseContext.requestId = options.requestId;
2489
- baseContext.entryEndpoint = options.entryEndpoint || entry;
2490
- baseContext.providerProtocol = providerProtocol;
2491
- baseContext.originalModelId = originalModelId;
2492
- if (assignedModelId && assignedModelId.trim()) {
2493
- baseContext.modelId = assignedModelId.trim();
2494
- }
2495
- const adapterContext = baseContext;
2496
- const compatProfile = typeof metadataBag?.compatibilityProfile === 'string'
2497
- ? String(metadataBag.compatibilityProfile)
2498
- : metadataBag &&
2499
- typeof metadataBag === 'object' &&
2500
- metadataBag.target &&
2501
- typeof metadataBag.target === 'object' &&
2502
- typeof metadataBag.target.compatibilityProfile === 'string'
2503
- ? metadataBag.target.compatibilityProfile
2504
- : undefined;
2505
- if (compatProfile && compatProfile.trim()) {
2506
- adapterContext.compatibilityProfile = compatProfile.trim();
2507
- }
2508
- const stageRecorder = await bridgeCreateSnapshotRecorder(adapterContext, typeof adapterContext.entryEndpoint === 'string'
2509
- ? adapterContext.entryEndpoint
2510
- : options.entryEndpoint || entry);
2511
- const providerInvoker = async (invokeOptions) => {
2512
- const runtimeKey = this.providerKeyToRuntimeKey.get(invokeOptions.providerKey) || invokeOptions.providerKey;
2513
- const handle = this.providerHandles.get(runtimeKey);
2514
- if (!handle) {
2515
- throw new Error(`Provider runtime ${runtimeKey} not found`);
2516
- }
2517
- const providerResponse = await handle.instance.processIncoming(invokeOptions.payload);
2518
- const normalized = this.normalizeProviderResponse(providerResponse);
2519
- const bodyPayload = normalized.body && typeof normalized.body === 'object'
2520
- ? normalized.body
2521
- : normalized;
2522
- return { providerResponse: bodyPayload };
2523
- };
2524
- const reenterPipeline = async (reenterOpts) => {
2525
- const nestedEntry = reenterOpts.entryEndpoint || options.entryEndpoint || entry;
2526
- const nestedExtra = asRecord(reenterOpts.metadata) ?? {};
2527
- // 基于首次 HubPipeline metadata + 调用方注入的 metadata 构建新的请求 metadata。
2528
- // 不在 Host 层编码 servertool/web_search 等语义,由 llmswitch-core 负责。
2529
- const nestedMetadata = {
2530
- ...(metadataBag ?? {}),
2531
- ...nestedExtra,
2532
- entryEndpoint: nestedEntry,
2533
- direction: 'request',
2534
- stage: 'inbound'
2535
- };
2536
- // servertool followup 是内部二跳请求:不应继承客户端 headers 偏好(尤其是 Accept),
2537
- // 否则会导致上游返回非 SSE 响应而被当作 SSE 解析,出现“空回复”。
2538
- // E1: merge internal runtime metadata carrier (`__rt`) instead of clobbering it.
2539
- try {
2540
- const baseRt = asRecord(metadataBag?.__rt) ?? {};
2541
- const extraRt = asRecord(nestedExtra?.__rt) ?? {};
2542
- if (Object.keys(baseRt).length || Object.keys(extraRt).length) {
2543
- nestedMetadata.__rt = { ...baseRt, ...extraRt };
2544
- }
2545
- }
2546
- catch {
2547
- // best-effort
2548
- }
2549
- if (asRecord(nestedMetadata.__rt)?.serverToolFollowup === true) {
2550
- delete nestedMetadata.clientHeaders;
2551
- delete nestedMetadata.clientRequestId;
2552
- }
2553
- const nestedInput = {
2554
- entryEndpoint: nestedEntry,
2555
- method: 'POST',
2556
- requestId: reenterOpts.requestId,
2557
- headers: {},
2558
- query: {},
2559
- body: reenterOpts.body,
2560
- metadata: nestedMetadata
2561
- };
2562
- const nestedResult = await this.executePipeline(nestedInput);
2563
- const nestedBody = nestedResult.body && typeof nestedResult.body === 'object'
2564
- ? nestedResult.body
2565
- : undefined;
2566
- return { body: nestedBody };
2567
- };
2568
- const converted = await bridgeConvertProviderResponse({
2569
- providerProtocol,
2570
- providerResponse: body,
2571
- context: adapterContext,
2572
- entryEndpoint: options.entryEndpoint || entry,
2573
- wantsStream: options.wantsStream,
2574
- requestSemantics: options.requestSemantics,
2575
- providerInvoker,
2576
- stageRecorder,
2577
- reenterPipeline
2578
- });
2579
- if (converted.__sse_responses) {
2580
- return {
2581
- ...options.response,
2582
- body: { __sse_responses: converted.__sse_responses }
2583
- };
2584
- }
2585
- return {
2586
- ...options.response,
2587
- body: converted.body ?? body
2588
- };
2589
- }
2590
- catch (error) {
2591
- const err = error;
2592
- const message = err instanceof Error ? err.message : String(err ?? 'Unknown error');
2593
- const errRecord = err;
2594
- const errCode = typeof errRecord.code === 'string' ? errRecord.code : undefined;
2595
- const errName = typeof errRecord.name === 'string' ? errRecord.name : undefined;
2596
- const statusCandidate = typeof errRecord.status === 'number'
2597
- ? errRecord.status
2598
- : typeof errRecord.statusCode === 'number'
2599
- ? errRecord.statusCode
2600
- : undefined;
2601
- const isSseDecodeError = errCode === 'SSE_DECODE_ERROR' ||
2602
- (errName === 'ProviderProtocolError' && message.toLowerCase().includes('sse'));
2603
- const isServerToolFollowupError = errCode === 'SERVERTOOL_FOLLOWUP_FAILED' ||
2604
- errCode === 'SERVERTOOL_EMPTY_FOLLOWUP' ||
2605
- (typeof errCode === 'string' && errCode.startsWith('SERVERTOOL_'));
2606
- const isProviderProtocolError = errName === 'ProviderProtocolError';
2607
- // If we need to stream a client response, conversion failures are fatal: there is no safe fallback
2608
- // that preserves protocol correctness.
2609
- const isStreamingConversion = Boolean(options.wantsStream && (needsAnthropicConversion || needsResponsesConversion || needsChatConversion));
2610
- if (isSseDecodeError || isServerToolFollowupError || isStreamingConversion || (isProviderProtocolError && typeof statusCandidate === 'number')) {
2611
- console.error('[RouteCodexHttpServer] Fatal conversion error, bubbling as HTTP error', error);
2612
- throw error;
2613
- }
2614
- console.error('[RouteCodexHttpServer] Failed to convert provider response via llmswitch-core', error);
2615
- return options.response;
2616
- }
2617
- }
2618
- extractSseWrapperError(payload) {
2619
- return this.findSseWrapperError(payload, 2);
2620
- }
2621
- findSseWrapperError(record, depth) {
2622
- if (!record || typeof record !== 'object' || depth < 0) {
2623
- return undefined;
2624
- }
2625
- if (record.mode === 'sse') {
2626
- const normalized = this.normalizeSseWrapperErrorValue(record.error, depth);
2627
- if (normalized) {
2628
- return normalized;
2629
- }
2630
- }
2631
- const nestedKeys = ['body', 'data', 'payload', 'response'];
2632
- for (const key of nestedKeys) {
2633
- const nested = record[key];
2634
- if (!nested || typeof nested !== 'object' || Array.isArray(nested)) {
2635
- continue;
2636
- }
2637
- const found = this.findSseWrapperError(nested, depth - 1);
2638
- if (found) {
2639
- return found;
2640
- }
2641
- }
2642
- return undefined;
2643
- }
2644
- normalizeSseWrapperErrorValue(value, depth) {
2645
- if (value === undefined || value === null || depth < 0) {
2646
- return undefined;
2647
- }
2648
- if (typeof value === 'string') {
2649
- const trimmed = value.trim();
2650
- if (!trimmed) {
2651
- return undefined;
2652
- }
2653
- if (depth > 0 && (trimmed.startsWith('{') || trimmed.startsWith('['))) {
2654
- try {
2655
- const parsed = JSON.parse(trimmed);
2656
- const parsedInfo = this.normalizeSseWrapperErrorValue(parsed, depth - 1);
2657
- if (parsedInfo) {
2658
- return parsedInfo;
2659
- }
2660
- }
2661
- catch {
2662
- // fallback to raw string
2663
- }
2664
- }
2665
- return {
2666
- message: trimmed,
2667
- retryable: isRetryableSseWrapperError(trimmed)
2668
- };
2669
- }
2670
- if (typeof value !== 'object' || Array.isArray(value)) {
2671
- return undefined;
2672
- }
2673
- const record = value;
2674
- const directMessage = firstNonEmptyString([
2675
- record.message,
2676
- record.error_message,
2677
- record.errorMessage
2678
- ]);
2679
- const directCode = firstNonEmptyString([
2680
- record.code,
2681
- record.error_code,
2682
- record.errorCode,
2683
- record.type
2684
- ]);
2685
- const directStatus = firstFiniteNumber([
2686
- record.status,
2687
- record.statusCode,
2688
- record.status_code,
2689
- record.http_status
2690
- ]);
2691
- if (depth > 0) {
2692
- for (const key of ['error', 'data', 'payload', 'details', 'body', 'response']) {
2693
- const nestedInfo = this.normalizeSseWrapperErrorValue(record[key], depth - 1);
2694
- if (nestedInfo) {
2695
- const mergedCode = nestedInfo.errorCode ?? directCode;
2696
- const retryable = nestedInfo.retryable || isRetryableSseWrapperError(nestedInfo.message, mergedCode, directStatus);
2697
- return {
2698
- message: nestedInfo.message,
2699
- ...(mergedCode ? { errorCode: mergedCode } : {}),
2700
- retryable
2701
- };
2702
- }
2703
- }
2704
- }
2705
- if (directMessage) {
2706
- return {
2707
- message: directMessage,
2708
- ...(directCode ? { errorCode: directCode } : {}),
2709
- retryable: isRetryableSseWrapperError(directMessage, directCode, directStatus)
2710
- };
2711
- }
2712
- try {
2713
- const serialized = JSON.stringify(record);
2714
- if (serialized && serialized !== '{}') {
2715
- return {
2716
- message: serialized,
2717
- ...(directCode ? { errorCode: directCode } : {}),
2718
- retryable: isRetryableSseWrapperError(serialized, directCode, directStatus)
2719
- };
2720
- }
2721
- }
2722
- catch {
2723
- // ignore stringify failures
2724
- }
2725
- return undefined;
2726
- }
2727
- extractClientModelId(metadata, originalRequest) {
2728
- const candidates = [
2729
- metadata.clientModelId,
2730
- metadata.originalModelId,
2731
- (metadata.target && typeof metadata.target === 'object'
2732
- ? metadata.target.clientModelId
2733
- : undefined),
2734
- originalRequest && typeof originalRequest === 'object'
2735
- ? originalRequest.model
2736
- : undefined,
2737
- originalRequest && typeof originalRequest === 'object'
2738
- ? originalRequest.originalModelId
2739
- : undefined
2740
- ];
2741
- for (const candidate of candidates) {
2742
- if (typeof candidate === 'string' && candidate.trim()) {
2743
- return candidate.trim();
2744
- }
2745
- }
2746
- return undefined;
2747
- }
2748
- cloneRequestPayload(payload) {
2749
- if (!payload || typeof payload !== 'object') {
2750
- return undefined;
2751
- }
2752
- try {
2753
- return JSON.parse(JSON.stringify(payload));
2754
- }
2755
- catch {
2756
- return undefined;
280
+ const legacyRunHubPipeline = this.runHubPipeline;
281
+ if (Object.prototype.hasOwnProperty.call(this, 'runHubPipeline') &&
282
+ typeof legacyRunHubPipeline === 'function') {
283
+ return await executePipelineViaLegacyOverride(this, input, legacyRunHubPipeline);
2757
284
  }
285
+ return await this.requestExecutor.execute(input);
2758
286
  }
2759
287
  async initializeRouteErrorHub() {
2760
- try {
2761
- this.routeErrorHub = initializeRouteErrorHub({ errorHandlingCenter: this.errorHandling });
2762
- await this.routeErrorHub.initialize();
2763
- }
2764
- catch (error) {
2765
- console.error('[RouteCodexHttpServer] Failed to initialize RouteErrorHub', error);
2766
- }
288
+ await initializeRouteErrorHub(this);
2767
289
  }
2768
290
  }
2769
291
  function createNoopPipelineLogger() {
@@ -2798,9 +320,8 @@ function createNoopPipelineLogger() {
2798
320
  getProviderLogs: () => emptyList(),
2799
321
  getStatistics: emptyStats,
2800
322
  clearLogs: noop,
2801
- exportLogs: () => ([]),
323
+ exportLogs: () => [],
2802
324
  log: noop
2803
325
  };
2804
326
  }
2805
- import { formatErrorForErrorCenter } from '../../../utils/error-center-payload.js';
2806
327
  //# sourceMappingURL=index.js.map