@cc-monitoring/proxy-core 0.3.1 → 0.4.1

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 (409) hide show
  1. package/dist/cli/__tests__/commands.test.js +20 -1
  2. package/dist/cli/commands/exec.js +31 -42
  3. package/dist/cli/commands/repair.d.ts +1 -0
  4. package/dist/cli/commands/repair.js +15 -0
  5. package/dist/cli/index.js +30 -0
  6. package/dist/docs/404.html +2 -2
  7. package/dist/docs/assets/{app.B9A39uFv.js → app.g7N9jkzn.js} +1 -1
  8. package/dist/docs/assets/chunks/@localSearchIndexroot.IHxQqfJH.js +1 -0
  9. package/dist/docs/assets/chunks/{VPLocalSearchBox.DdhC696L.js → VPLocalSearchBox.BHwI475_.js} +1 -1
  10. package/dist/docs/assets/chunks/{theme.CfB2tOFi.js → theme.FhAJSNDh.js} +2 -2
  11. package/dist/docs/assets/{proxy-cli_data-hooks.md.w9Oe1OD3.js → proxy-cli_data-hooks.md.Bz8CVbxO.js} +1 -1
  12. package/dist/docs/assets/{proxy-core_ui-routes.md.Dyqkeoiv.js → proxy-core_ui-routes.md.B21t1p7R.js} +1 -1
  13. package/dist/docs/hashmap.json +1 -1
  14. package/dist/docs/index.html +3 -3
  15. package/dist/docs/overview.html +3 -3
  16. package/dist/docs/packages-utils.html +3 -3
  17. package/dist/docs/proxy-admin/env-and-build.html +3 -3
  18. package/dist/docs/proxy-admin/index.html +3 -3
  19. package/dist/docs/proxy-admin/layout-and-menu.html +3 -3
  20. package/dist/docs/proxy-admin/pages.html +3 -3
  21. package/dist/docs/proxy-admin/routing-and-auth.html +3 -3
  22. package/dist/docs/proxy-admin/services-and-mock.html +3 -3
  23. package/dist/docs/proxy-cli/api-layer.html +3 -3
  24. package/dist/docs/proxy-cli/app-shell.html +3 -3
  25. package/dist/docs/proxy-cli/components-common.html +3 -3
  26. package/dist/docs/proxy-cli/components-config.html +3 -3
  27. package/dist/docs/proxy-cli/components-requests.html +3 -3
  28. package/dist/docs/proxy-cli/components-stats.html +3 -3
  29. package/dist/docs/proxy-cli/components-status.html +3 -3
  30. package/dist/docs/proxy-cli/data-hooks.html +5 -5
  31. package/dist/docs/proxy-cli/index.html +3 -3
  32. package/dist/docs/proxy-cli/utils-and-i18n.html +3 -3
  33. package/dist/docs/proxy-core/chat-api.html +3 -3
  34. package/dist/docs/proxy-core/cli.html +3 -3
  35. package/dist/docs/proxy-core/config.html +3 -3
  36. package/dist/docs/proxy-core/data-layer.html +3 -3
  37. package/dist/docs/proxy-core/entry.html +3 -3
  38. package/dist/docs/proxy-core/index.html +3 -3
  39. package/dist/docs/proxy-core/pm2.html +3 -3
  40. package/dist/docs/proxy-core/providers.html +3 -3
  41. package/dist/docs/proxy-core/proxy.html +3 -3
  42. package/dist/docs/proxy-core/stats.html +3 -3
  43. package/dist/docs/proxy-core/tool.html +3 -3
  44. package/dist/docs/proxy-core/ui-routes.html +5 -5
  45. package/dist/docs/proxy-core/utilities.html +3 -3
  46. package/dist/docs/quickstart.html +3 -3
  47. package/dist/docs/request-flow.html +3 -3
  48. package/dist/logger/index.d.ts +0 -3
  49. package/dist/logger/index.js +0 -1
  50. package/dist/mcp-server/tools.js +8 -9
  51. package/dist/server/__tests__/data-push.test.js +44 -0
  52. package/dist/server/__tests__/providers-route.test.js +1 -0
  53. package/dist/server/__tests__/proxy.test.js +281 -2
  54. package/dist/server/__tests__/server.test.js +15 -10
  55. package/dist/server/__tests__/ui-route.test.js +43 -0
  56. package/dist/server/auth.d.ts +0 -1
  57. package/dist/server/auth.js +1 -1
  58. package/dist/server/chat-route.js +48 -162
  59. package/dist/server/data-push.d.ts +2 -1
  60. package/dist/server/data-push.js +22 -7
  61. package/dist/server/index.js +4 -3
  62. package/dist/server/proxy.js +193 -229
  63. package/dist/server/ui-route.js +84 -60
  64. package/dist/tools/index.d.ts +1 -1
  65. package/dist/tools/index.js +1 -1
  66. package/dist/tools/registry.d.ts +0 -4
  67. package/dist/tools/registry.js +0 -8
  68. package/dist/types/config.d.ts +0 -13
  69. package/dist/types/pm2.d.ts +1 -2
  70. package/dist/ui/assets/{1c-DAu68mP4.js → 1c-CaPSZBmB.js} +1 -1
  71. package/dist/ui/assets/{abnf-Cu8RHCHK.js → abnf-DBDOsmne.js} +1 -1
  72. package/dist/ui/assets/{abnfDiagram-VRR7QNED-BUBToPXF.js → abnfDiagram-VRR7QNED-D8zK__Mj.js} +1 -1
  73. package/dist/ui/assets/{accesslog-BG-34XOy.js → accesslog-BOLs2Kig.js} +1 -1
  74. package/dist/ui/assets/{actionscript-iw0HHMGM.js → actionscript-BZYKhS1B.js} +1 -1
  75. package/dist/ui/assets/{ada-qRqj6VGQ.js → ada-DyacnMpg.js} +1 -1
  76. package/dist/ui/assets/{angelscript-BeaXyx_P.js → angelscript-CAN6j0H3.js} +1 -1
  77. package/dist/ui/assets/{apache-CU8d-z9X.js → apache-BE4vouiQ.js} +1 -1
  78. package/dist/ui/assets/{applescript-d1Wxkqz1.js → applescript-CS88I1yz.js} +1 -1
  79. package/dist/ui/assets/{arc-BMsPIm3j.js → arc-CTMmdrSV.js} +1 -1
  80. package/dist/ui/assets/{arcade-CtbgrWTM.js → arcade-B0f0f5SD.js} +1 -1
  81. package/dist/ui/assets/{architectureDiagram-ZJ3FMSHR-CVjC5TrS.js → architectureDiagram-ZJ3FMSHR-Dtvpnopp.js} +1 -1
  82. package/dist/ui/assets/{arduino-Bc9L39Hr.js → arduino-Bhm-OSb2.js} +1 -1
  83. package/dist/ui/assets/{armasm-DuWmpjWR.js → armasm-CEy-mDHc.js} +1 -1
  84. package/dist/ui/assets/{asciidoc-CDIhFTnx.js → asciidoc-CrZqQBTj.js} +1 -1
  85. package/dist/ui/assets/{aspectj-SYj-57Es.js → aspectj-B4IlFyyJ.js} +1 -1
  86. package/dist/ui/assets/{autohotkey-hBi5dq37.js → autohotkey-Cihy-FjA.js} +1 -1
  87. package/dist/ui/assets/{autoit-BSawXzhg.js → autoit-Dis_JVJq.js} +1 -1
  88. package/dist/ui/assets/{avrasm-CwYom8Np.js → avrasm-CVhMycHj.js} +1 -1
  89. package/dist/ui/assets/{awk-BvBRfGo2.js → awk-BCViwLf5.js} +1 -1
  90. package/dist/ui/assets/{axapta-CqUavA8k.js → axapta-pWB4JVB_.js} +1 -1
  91. package/dist/ui/assets/{bash-BS_ZnmEu.js → bash-B6Giryub.js} +1 -1
  92. package/dist/ui/assets/{basic-xbP4IXrN.js → basic-DcrjcHB0.js} +1 -1
  93. package/dist/ui/assets/{blockDiagram-677ZJIJ3-CmpI3f5p.js → blockDiagram-677ZJIJ3-DfhtgI1r.js} +1 -1
  94. package/dist/ui/assets/{bnf-BECwu6uW.js → bnf-cpKU0967.js} +1 -1
  95. package/dist/ui/assets/{brainfuck-QQgu4j4N.js → brainfuck-CFFdk4cM.js} +1 -1
  96. package/dist/ui/assets/{coq-IDMIFfem.js → c-CW6KPMeI.js} +1 -1
  97. package/dist/ui/assets/{c-like-DYrFOtXy.js → c-like-CWn59vrg.js} +1 -1
  98. package/dist/ui/assets/{c4Diagram-LMCZKHZV-B6Hgngz9.js → c4Diagram-LMCZKHZV-D1Oh6k37.js} +1 -1
  99. package/dist/ui/assets/{cal-CNMrJ6v5.js → cal-8i26_L39.js} +1 -1
  100. package/dist/ui/assets/{capnproto-DYCKl6oY.js → capnproto-CviwnFBf.js} +1 -1
  101. package/dist/ui/assets/{ceylon-mb8tRPl0.js → ceylon-CQyc4rWs.js} +1 -1
  102. package/dist/ui/assets/channel-Da2MvpNG.js +1 -0
  103. package/dist/ui/assets/{chunk-2Q5K7J3B-LxvQ-G67.js → chunk-2Q5K7J3B-oswPBwIn.js} +1 -1
  104. package/dist/ui/assets/{chunk-32BRIVSS-DxzKK6Aw.js → chunk-32BRIVSS-B5pU_9b9.js} +1 -1
  105. package/dist/ui/assets/{chunk-5VM5RSS4-CZEoYJ2W.js → chunk-5VM5RSS4-gvtfMc4w.js} +1 -1
  106. package/dist/ui/assets/{chunk-EX3LRPZG-DS2PytrN.js → chunk-EX3LRPZG-BkBaDMy4.js} +1 -1
  107. package/dist/ui/assets/{chunk-JWPE2WC7-UX0Sr1Ie.js → chunk-JWPE2WC7-Ch-C75ar.js} +1 -1
  108. package/dist/ui/assets/{chunk-MOJQB5TN-YbJgT9t4.js → chunk-MOJQB5TN-BfYhj1jN.js} +1 -1
  109. package/dist/ui/assets/{chunk-RYQCIY6F-DS2VH_U9.js → chunk-RYQCIY6F-D8izfruP.js} +1 -1
  110. package/dist/ui/assets/{chunk-V7JOEXUC-CZr0pkI1.js → chunk-V7JOEXUC-CbzHnW90.js} +1 -1
  111. package/dist/ui/assets/{chunk-VR4S4FIN-50opPAD3.js → chunk-VR4S4FIN-Dr4M5qZA.js} +1 -1
  112. package/dist/ui/assets/{chunk-XXDRQBXY-DAAX9l7U.js → chunk-XXDRQBXY-QE-R8dRe.js} +1 -1
  113. package/dist/ui/assets/classDiagram-OUVF2IWQ-mHUiWjcC.js +1 -0
  114. package/dist/ui/assets/classDiagram-v2-EOCWNBFH-mHUiWjcC.js +1 -0
  115. package/dist/ui/assets/{clean-C4X7dSkh.js → clean-DZeAtQ7H.js} +1 -1
  116. package/dist/ui/assets/{clojure-DSg9gUa7.js → clojure-U4ebZ7NQ.js} +1 -1
  117. package/dist/ui/assets/{clojure-repl-CagRYx7h.js → clojure-repl-B4QSIzUI.js} +1 -1
  118. package/dist/ui/assets/{cmake-CFdBn53c.js → cmake-UHexZySb.js} +1 -1
  119. package/dist/ui/assets/{coffeescript-CJZtMOPx.js → coffeescript-Dg56H9Mr.js} +1 -1
  120. package/dist/ui/assets/{c-BRJEPYra.js → coq-C1g3mm0X.js} +1 -1
  121. package/dist/ui/assets/{cos-Bx2VJuzG.js → cos-BL4DdyGh.js} +1 -1
  122. package/dist/ui/assets/{cose-bilkent-JH36ORCC-zWRLsZpZ.js → cose-bilkent-JH36ORCC-BHQslHP3.js} +1 -1
  123. package/dist/ui/assets/{cpp-CsG6DlC4.js → cpp-keZI-XAd.js} +1 -1
  124. package/dist/ui/assets/{crmsh-DTy9MnmJ.js → crmsh-DdXYVeeA.js} +1 -1
  125. package/dist/ui/assets/{crystal-BwgrLrZ1.js → crystal-C4AN0m0I.js} +1 -1
  126. package/dist/ui/assets/{csharp-B2VeTPAF.js → csharp-COUyKsNR.js} +1 -1
  127. package/dist/ui/assets/{csp-CTpydNhH.js → csp-DBc-rgSJ.js} +1 -1
  128. package/dist/ui/assets/{css-B0mMUbpY.js → css-B8m6O_Pw.js} +1 -1
  129. package/dist/ui/assets/{cynefin-VYW2F7L2-BmjQEj6q.js → cynefin-VYW2F7L2-B3L-WcKn.js} +1 -1
  130. package/dist/ui/assets/{cynefinDiagram-TSTJHNR4-Dr0Zrke0.js → cynefinDiagram-TSTJHNR4-fzaM4qcZ.js} +1 -1
  131. package/dist/ui/assets/{d-DjuDZnbI.js → d-dgQN4Rko.js} +1 -1
  132. package/dist/ui/assets/{dagre-VKFMJZFB-xoTPT2Uu.js → dagre-VKFMJZFB-DJHj16mR.js} +1 -1
  133. package/dist/ui/assets/{dart-xR3GU9TJ.js → dart-D2Y7LOq-.js} +1 -1
  134. package/dist/ui/assets/{delphi-bJas5QnG.js → delphi-DaGcSk5A.js} +1 -1
  135. package/dist/ui/assets/{diagram-FQU43EPY-rL_IU7gU.js → diagram-FQU43EPY-iZEZ1f-R.js} +1 -1
  136. package/dist/ui/assets/{diagram-G47NLZAW-EcDiCVGj.js → diagram-G47NLZAW-rkQb3r-C.js} +1 -1
  137. package/dist/ui/assets/{diagram-NH7WQ7WH-CGGXOjev.js → diagram-NH7WQ7WH-B4Fy72tP.js} +1 -1
  138. package/dist/ui/assets/{diagram-OA4YK3LP-DnE7hDjD.js → diagram-OA4YK3LP-BndmuIBq.js} +1 -1
  139. package/dist/ui/assets/{diagram-WEI45ONY-CTJH5eD8.js → diagram-WEI45ONY-CwlAiqOe.js} +1 -1
  140. package/dist/ui/assets/{diff-doJFF3fv.js → diff-prcRV1Y3.js} +1 -1
  141. package/dist/ui/assets/{django-D3b-owa_.js → django-B1zIoGSx.js} +1 -1
  142. package/dist/ui/assets/{dns-C42KYV-Z.js → dns-ZxI_aTwc.js} +1 -1
  143. package/dist/ui/assets/{dockerfile-9KUINc2j.js → dockerfile-Dp_fT6E-.js} +1 -1
  144. package/dist/ui/assets/{dos-CxnlJ6qp.js → dos-Cuu5c8vv.js} +1 -1
  145. package/dist/ui/assets/{dsconfig-6CMMCwmY.js → dsconfig-DcGTOhbS.js} +1 -1
  146. package/dist/ui/assets/{dts-D78rdUIB.js → dts-DCF8VaRk.js} +1 -1
  147. package/dist/ui/assets/{dust-Dv1uTfJU.js → dust-DeCmZWMK.js} +1 -1
  148. package/dist/ui/assets/{ebnf-BGbG1cFK.js → ebnf-BHQQcuF_.js} +1 -1
  149. package/dist/ui/assets/{ebnfDiagram-CCIWWBDH-z2VgRSUV.js → ebnfDiagram-CCIWWBDH-DJ_RuhTm.js} +1 -1
  150. package/dist/ui/assets/{elixir-ZgAQVY1J.js → elixir-BHmweJ4d.js} +1 -1
  151. package/dist/ui/assets/{elm-CudiFuNu.js → elm-v76Hd2B3.js} +1 -1
  152. package/dist/ui/assets/{erDiagram-Q63AITRT-X1TODuRv.js → erDiagram-Q63AITRT-DVZkY5RZ.js} +1 -1
  153. package/dist/ui/assets/{erb-Bz_NZh6W.js → erb-xvWF5bfx.js} +1 -1
  154. package/dist/ui/assets/{erlang-DNuj1nb2.js → erlang-CKBMURym.js} +1 -1
  155. package/dist/ui/assets/{erlang-repl-SP1Gan9W.js → erlang-repl-BjcfXt3Z.js} +1 -1
  156. package/dist/ui/assets/{excel-0K77uECF.js → excel-Cdrtxhml.js} +1 -1
  157. package/dist/ui/assets/{fix-CalJq7JD.js → fix-CB-So5So.js} +1 -1
  158. package/dist/ui/assets/{flix-BlIs11_w.js → flix-VM9AOrq6.js} +1 -1
  159. package/dist/ui/assets/{flowDiagram-23GEKE2U-DA2dnmc0.js → flowDiagram-23GEKE2U-vyGW67Xm.js} +1 -1
  160. package/dist/ui/assets/{fortran-ZCobXY2R.js → fortran-CcwJVO8H.js} +1 -1
  161. package/dist/ui/assets/{fsharp-Be8rwUtc.js → fsharp-DiATpw1u.js} +1 -1
  162. package/dist/ui/assets/{gams-DUat8frQ.js → gams-Csg9YNPz.js} +1 -1
  163. package/dist/ui/assets/{ganttDiagram-NO4QXBWP-DdFwaIGZ.js → ganttDiagram-NO4QXBWP-C2TRbwLw.js} +1 -1
  164. package/dist/ui/assets/{gauss-BB4U88dy.js → gauss-Bc4TC8Wh.js} +1 -1
  165. package/dist/ui/assets/{gcode-DzeUZuZg.js → gcode-Rig_2OoA.js} +1 -1
  166. package/dist/ui/assets/{gherkin-B_8vW_BO.js → gherkin-ByBU06QB.js} +1 -1
  167. package/dist/ui/assets/{gitGraphDiagram-IHSO6WYX-B-Dn8x6I.js → gitGraphDiagram-IHSO6WYX-CEiqfFIF.js} +1 -1
  168. package/dist/ui/assets/{glsl-DGHGjPC1.js → glsl-C8D6y2Tn.js} +1 -1
  169. package/dist/ui/assets/{gml-BYEku8Yh.js → gml-PHpGPHbT.js} +1 -1
  170. package/dist/ui/assets/{go-D8aSgQMc.js → go-BL_o_3ez.js} +1 -1
  171. package/dist/ui/assets/{golo-CDjCU_jh.js → golo-CWQ6dTUz.js} +1 -1
  172. package/dist/ui/assets/{gradle-BeUw7tl_.js → gradle-Y_I2KN2z.js} +1 -1
  173. package/dist/ui/assets/{groovy-5iCQOtR3.js → groovy-JnIv8p2l.js} +1 -1
  174. package/dist/ui/assets/{haml-BEdL8FJi.js → haml-B8W_h6mZ.js} +1 -1
  175. package/dist/ui/assets/{handlebars-BuYQNzoK.js → handlebars-DVm8mJuD.js} +1 -1
  176. package/dist/ui/assets/{haskell-I4FCBcW0.js → haskell-DlDi2Ty7.js} +1 -1
  177. package/dist/ui/assets/{haxe-yigyKfFr.js → haxe-CftFNVE-.js} +1 -1
  178. package/dist/ui/assets/{hsp-ZUk4j7d1.js → hsp-C67xnq9C.js} +1 -1
  179. package/dist/ui/assets/{htmlbars-QYP3lT3u.js → htmlbars-CmJCm67U.js} +1 -1
  180. package/dist/ui/assets/{http-BJpD1O71.js → http-b-FPEcHP.js} +1 -1
  181. package/dist/ui/assets/{hy-CIJ1bP6S.js → hy-BWD3ncpN.js} +1 -1
  182. package/dist/ui/assets/{index-COAgrx1J.js → index-C7s1zgJb.js} +242 -242
  183. package/dist/ui/assets/{index-BDXWttnT.js → index-D2UBwfSG.js} +3 -3
  184. package/dist/ui/assets/index-DlsVXaHT.css +10 -0
  185. package/dist/ui/assets/{infoDiagram-FWYZ7A6U-fTg9eLNt.js → infoDiagram-FWYZ7A6U-C0Px17IN.js} +1 -1
  186. package/dist/ui/assets/{inform7-DAsu-lC2.js → inform7-C-OYaMO5.js} +1 -1
  187. package/dist/ui/assets/{ini-Bhz-zHCg.js → ini-CVyYmykw.js} +1 -1
  188. package/dist/ui/assets/{irpf90-BunhbwBl.js → irpf90-BcpjWXIz.js} +1 -1
  189. package/dist/ui/assets/{isbl-q9L6hOhP.js → isbl-ZraBBeEr.js} +1 -1
  190. package/dist/ui/assets/{ishikawaDiagram-FXEZZL3T-B8G0sdgd.js → ishikawaDiagram-FXEZZL3T-BjmTMgpO.js} +1 -1
  191. package/dist/ui/assets/{java-Iks5Pkau.js → java-D_mE2DT6.js} +1 -1
  192. package/dist/ui/assets/{javascript-C4vltXPw.js → javascript-akXESsox.js} +1 -1
  193. package/dist/ui/assets/{jboss-cli-BQBvd2X2.js → jboss-cli-CFe3gTDt.js} +1 -1
  194. package/dist/ui/assets/{journeyDiagram-5HDEW3XC-Crbzgrum.js → journeyDiagram-5HDEW3XC-BpgJUvs6.js} +1 -1
  195. package/dist/ui/assets/{json-DXK0_jUK.js → json-sjN7AzRC.js} +1 -1
  196. package/dist/ui/assets/{julia-CCYoBiYK.js → julia-BJJYbUMV.js} +1 -1
  197. package/dist/ui/assets/{julia-repl-y8v2Oorl.js → julia-repl-BwruD8Ix.js} +1 -1
  198. package/dist/ui/assets/{kanban-definition-HUTT4EX6-C8sCOsBX.js → kanban-definition-HUTT4EX6-DFlE9edA.js} +1 -1
  199. package/dist/ui/assets/{kotlin-Bnjk7_fw.js → kotlin-CAIQsLCF.js} +1 -1
  200. package/dist/ui/assets/{lasso-B-al6x63.js → lasso-sU67h-5u.js} +1 -1
  201. package/dist/ui/assets/{latex-BUmqlb9r.js → latex-CVX-jP88.js} +1 -1
  202. package/dist/ui/assets/{ldif-D42RWj6Q.js → ldif-DGM9xbFZ.js} +1 -1
  203. package/dist/ui/assets/{leaf-CrH4BZ0m.js → leaf-AlB_zmwz.js} +1 -1
  204. package/dist/ui/assets/{less-upmRlQQW.js → less-CKwfEdZQ.js} +1 -1
  205. package/dist/ui/assets/{lisp-DHYS22SA.js → lisp-CXYLaAPw.js} +1 -1
  206. package/dist/ui/assets/{livecodeserver-CeQNmFP3.js → livecodeserver-C02fGGTS.js} +1 -1
  207. package/dist/ui/assets/{livescript-PJWpY1JI.js → livescript-CBMa8QF_.js} +1 -1
  208. package/dist/ui/assets/{llvm-BKtKWwCt.js → llvm-WXTIohTC.js} +1 -1
  209. package/dist/ui/assets/{lsl-RWRLRjk7.js → lsl-4obd2U_I.js} +1 -1
  210. package/dist/ui/assets/{lua-BFUDiPGm.js → lua-DTSy-TjF.js} +1 -1
  211. package/dist/ui/assets/{makefile-CUDyOz5D.js → makefile-Bf2UEO0i.js} +1 -1
  212. package/dist/ui/assets/{markdown-zZCLypc6.js → markdown-BgoGmFfJ.js} +1 -1
  213. package/dist/ui/assets/{mathematica-BHbxKxnq.js → mathematica-CSiA8KQh.js} +1 -1
  214. package/dist/ui/assets/{matlab-DlGtg--p.js → matlab-DoMvLsGB.js} +1 -1
  215. package/dist/ui/assets/{maxima-BBBeS3Ra.js → maxima-BFO4dfZO.js} +1 -1
  216. package/dist/ui/assets/{mel-nh8j7W_6.js → mel-DyBpmDxt.js} +1 -1
  217. package/dist/ui/assets/{mercury-DyyD-pqr.js → mercury-OL5G5uU6.js} +1 -1
  218. package/dist/ui/assets/{mindmap-definition-LN4V7U3C-CJlhDHqM.js → mindmap-definition-LN4V7U3C-s-nUZuCs.js} +1 -1
  219. package/dist/ui/assets/{mipsasm-hiEhRRiq.js → mipsasm-KQwdm39j.js} +1 -1
  220. package/dist/ui/assets/{mizar-BZa3iAAL.js → mizar-BqndJ30I.js} +1 -1
  221. package/dist/ui/assets/{mojolicious-BCIm2V_y.js → mojolicious-Bvc1uJd-.js} +1 -1
  222. package/dist/ui/assets/{monkey-zmK-7N8b.js → monkey-Dl_gLm7-.js} +1 -1
  223. package/dist/ui/assets/{moonscript-BsS_Mn3D.js → moonscript-BL6R6eAz.js} +1 -1
  224. package/dist/ui/assets/{n1ql-D7xVgDP0.js → n1ql-C09iOJfQ.js} +1 -1
  225. package/dist/ui/assets/{nginx-ct2q4dtd.js → nginx-C7W01nBr.js} +1 -1
  226. package/dist/ui/assets/{nim-RO0UOmW3.js → nim-Dj78szvD.js} +1 -1
  227. package/dist/ui/assets/{nix-BcRJxPKH.js → nix-BOtGyPmd.js} +1 -1
  228. package/dist/ui/assets/{node-repl-DgIefwxW.js → node-repl-19nZvZ4A.js} +1 -1
  229. package/dist/ui/assets/{nsis-DqHs9QhB.js → nsis-ByCl1zIm.js} +1 -1
  230. package/dist/ui/assets/{objectivec-BBcWqc7Y.js → objectivec-BhdaT_Jr.js} +1 -1
  231. package/dist/ui/assets/{ocaml-BExfw7D6.js → ocaml-D_NXvSO7.js} +1 -1
  232. package/dist/ui/assets/{openscad-CHPRELQb.js → openscad-DOhagvnA.js} +1 -1
  233. package/dist/ui/assets/{oxygene-DpS9uMJW.js → oxygene-5BnkqMRH.js} +1 -1
  234. package/dist/ui/assets/{parser3-CjJ_QdBp.js → parser3-jSWlxi8m.js} +1 -1
  235. package/dist/ui/assets/{pegDiagram-2B236MQR-GM70woGy.js → pegDiagram-2B236MQR-CideH-XT.js} +1 -1
  236. package/dist/ui/assets/{perl-BSNM2Kt8.js → perl-CfsqjZ4b.js} +1 -1
  237. package/dist/ui/assets/{pf-Z-hKf_88.js → pf-kOewxO29.js} +1 -1
  238. package/dist/ui/assets/{pgsql-DDgznxBg.js → pgsql-CXm2Xd-Z.js} +1 -1
  239. package/dist/ui/assets/{php-BzAhxF-l.js → php-CRiA_dH8.js} +1 -1
  240. package/dist/ui/assets/{php-template-DkgcKHRc.js → php-template-ByMrxqSd.js} +1 -1
  241. package/dist/ui/assets/{pieDiagram-ENE6RG2P-Bst7mPyz.js → pieDiagram-ENE6RG2P-CJkCdB38.js} +1 -1
  242. package/dist/ui/assets/{plaintext-BYEppD0R.js → plaintext-BJx0R6A5.js} +1 -1
  243. package/dist/ui/assets/{pony-BT_kQd1K.js → pony-CozCHtz4.js} +1 -1
  244. package/dist/ui/assets/{powershell-BfRsuSsf.js → powershell-BT7gMMgn.js} +1 -1
  245. package/dist/ui/assets/{processing-DpzWe5Iu.js → processing-FOIxn_W_.js} +1 -1
  246. package/dist/ui/assets/{profile-BCOhS5cM.js → profile-BsoUuD9w.js} +1 -1
  247. package/dist/ui/assets/{prolog-OxaCCaW8.js → prolog-Bd3gvQ2N.js} +1 -1
  248. package/dist/ui/assets/{properties-DMFlA2Ca.js → properties-BWGFdny9.js} +1 -1
  249. package/dist/ui/assets/{protobuf-CRDFkXvZ.js → protobuf-pQSKoOOY.js} +1 -1
  250. package/dist/ui/assets/{puppet-DnrVyLzx.js → puppet-DnEN_mgS.js} +1 -1
  251. package/dist/ui/assets/{purebasic-Dht4Hniv.js → purebasic-BqoOGoRw.js} +1 -1
  252. package/dist/ui/assets/{python-B0aMirWu.js → python-TeVYomkE.js} +1 -1
  253. package/dist/ui/assets/{python-repl-BlYDBGa7.js → python-repl-zqu5igFS.js} +1 -1
  254. package/dist/ui/assets/{q-DzrtW9kT.js → q-CZC9Dltd.js} +1 -1
  255. package/dist/ui/assets/{qml-DnfQsR3i.js → qml-ZNInP45Q.js} +1 -1
  256. package/dist/ui/assets/{quadrantDiagram-ABIIQ3AL-DNRYurBT.js → quadrantDiagram-ABIIQ3AL-Dd7PwxsF.js} +1 -1
  257. package/dist/ui/assets/{r-DGI_ZuTD.js → r-B39DAmkC.js} +1 -1
  258. package/dist/ui/assets/{railroadDiagram-RFXS5EU6-CmbC3Vz4.js → railroadDiagram-RFXS5EU6-DtO5PVFZ.js} +1 -1
  259. package/dist/ui/assets/{reasonml-3SohMgh2.js → reasonml-B-ZWFJt_.js} +1 -1
  260. package/dist/ui/assets/{requirementDiagram-TGXJPOKE-8CxrXHco.js → requirementDiagram-TGXJPOKE-DihF4OTj.js} +1 -1
  261. package/dist/ui/assets/{rib-DfRSd4Qa.js → rib-CnGG0qMF.js} +1 -1
  262. package/dist/ui/assets/{roboconf-CQczGdGW.js → roboconf-Ewtfps4u.js} +1 -1
  263. package/dist/ui/assets/{routeros-DCka-T2i.js → routeros-CPcmP1c7.js} +1 -1
  264. package/dist/ui/assets/{rsl-Dr8MOwMo.js → rsl-NbjWFbPH.js} +1 -1
  265. package/dist/ui/assets/{ruby-C2zFSkh_.js → ruby-DWDdy5yE.js} +1 -1
  266. package/dist/ui/assets/{ruleslanguage-C97YsgQq.js → ruleslanguage-Cv_DZe2r.js} +1 -1
  267. package/dist/ui/assets/{rust-H3h8kd9A.js → rust-NZnsfER5.js} +1 -1
  268. package/dist/ui/assets/{sankeyDiagram-HTMAVEWB-DBXLWjEq.js → sankeyDiagram-HTMAVEWB-D6i-Mzte.js} +1 -1
  269. package/dist/ui/assets/{sas-04z0Xu8P.js → sas-C4wv0P6G.js} +1 -1
  270. package/dist/ui/assets/{scala-DyB9Aoiv.js → scala-DUdTOIgx.js} +1 -1
  271. package/dist/ui/assets/{scheme-DBj6nxUi.js → scheme-DEdFa14t.js} +1 -1
  272. package/dist/ui/assets/{scilab-Btj7AD9s.js → scilab-xY51J2bR.js} +1 -1
  273. package/dist/ui/assets/{scss-BXZJyXt7.js → scss-BEYp1sXc.js} +1 -1
  274. package/dist/ui/assets/{sequenceDiagram-DBY2YBRQ-DXIkBFKC.js → sequenceDiagram-DBY2YBRQ-njQlqy_J.js} +1 -1
  275. package/dist/ui/assets/{shell-BsreQR1Z.js → shell-fq6bkhOw.js} +1 -1
  276. package/dist/ui/assets/{sizeCapture-X5ZJPWSS-ocS9jxAQ.js → sizeCapture-X5ZJPWSS-tLn70-Fb.js} +1 -1
  277. package/dist/ui/assets/{smali-D9wQw9zm.js → smali-CsSAuSYg.js} +1 -1
  278. package/dist/ui/assets/{smalltalk-Dku6G_H3.js → smalltalk-DJ7zrd87.js} +1 -1
  279. package/dist/ui/assets/{sml-BknyU6ya.js → sml-CnDwl0nH.js} +1 -1
  280. package/dist/ui/assets/{sqf-DRYMaOg8.js → sqf-Cc2SX4Zy.js} +1 -1
  281. package/dist/ui/assets/{sql-Cw3Ax4KA.js → sql-DUQA_V_4.js} +1 -1
  282. package/dist/ui/assets/{sql_more-vWdslLE0.js → sql_more-BHoH-8oO.js} +1 -1
  283. package/dist/ui/assets/{stan-8XH5bDS6.js → stan-D-7F5jH6.js} +1 -1
  284. package/dist/ui/assets/{stata-4e77wgfe.js → stata-xKD_DnDE.js} +1 -1
  285. package/dist/ui/assets/{stateDiagram-2N3HPSRC-RMmL9jMG.js → stateDiagram-2N3HPSRC-B4O8weor.js} +1 -1
  286. package/dist/ui/assets/stateDiagram-v2-6OUMAXLB-CO881_ht.js +1 -0
  287. package/dist/ui/assets/{step21-ByIeDMY3.js → step21-obc_kzQY.js} +1 -1
  288. package/dist/ui/assets/{stylus-ZAhJyWsw.js → stylus-CkAUSRd-.js} +1 -1
  289. package/dist/ui/assets/{subunit-pPW2UGkj.js → subunit-BDv8PGo1.js} +1 -1
  290. package/dist/ui/assets/{swift-CX3BB5az.js → swift-CHsYyW-p.js} +1 -1
  291. package/dist/ui/assets/{swimlanes-5IMT3BWC-C2zAuJ56.js → swimlanes-5IMT3BWC-DSr9Ty4A.js} +2 -2
  292. package/dist/ui/assets/swimlanesDiagram-G3AALYLV-DFMCDs8b.js +8 -0
  293. package/dist/ui/assets/{taggerscript-BHcjbWIC.js → taggerscript-C_2JQgO9.js} +1 -1
  294. package/dist/ui/assets/{tap-BOl1lBlS.js → tap-Bi497Z2c.js} +1 -1
  295. package/dist/ui/assets/{tcl-DZjeOBDh.js → tcl-B1XMvLaD.js} +1 -1
  296. package/dist/ui/assets/{thrift-BxboWg4x.js → thrift-D9X0Hmmc.js} +1 -1
  297. package/dist/ui/assets/{timeline-definition-FHXFAJF6-BOxsxyq_.js → timeline-definition-FHXFAJF6-C4_gj50B.js} +1 -1
  298. package/dist/ui/assets/{tp-BNM6ql3J.js → tp-CJG4Odbx.js} +1 -1
  299. package/dist/ui/assets/{twig-Dj9ZiXD4.js → twig-flj72_wc.js} +1 -1
  300. package/dist/ui/assets/{typescript-7MEJlu_6.js → typescript-CWPwVoLd.js} +1 -1
  301. package/dist/ui/assets/{vala-DNqOAgHJ.js → vala-LuCNWNuU.js} +1 -1
  302. package/dist/ui/assets/{vbnet-T0mNKVc_.js → vbnet-CGUT6ss0.js} +1 -1
  303. package/dist/ui/assets/{vbscript-CeuwVMAI.js → vbscript-CNiUBJhn.js} +1 -1
  304. package/dist/ui/assets/{vbscript-html-BbJSt2w6.js → vbscript-html-CJGgjjbc.js} +1 -1
  305. package/dist/ui/assets/{vennDiagram-L72KCM5P-BX9mWeiT.js → vennDiagram-L72KCM5P-BxcAUnYo.js} +1 -1
  306. package/dist/ui/assets/{verilog-DYuhYfkX.js → verilog-C4f9ZBTK.js} +1 -1
  307. package/dist/ui/assets/{vhdl-Cf9aT-t7.js → vhdl-CyDQIZve.js} +1 -1
  308. package/dist/ui/assets/{vim-DciP44K1.js → vim-XAUBqa0M.js} +1 -1
  309. package/dist/ui/assets/{wardleyDiagram-EHGQE667-CDj2rOzu.js → wardleyDiagram-EHGQE667-DlwSH77q.js} +1 -1
  310. package/dist/ui/assets/{x86asm-E-AN_LXO.js → x86asm-Cp6vHuUx.js} +1 -1
  311. package/dist/ui/assets/{xl-CxYLJelN.js → xl-BHgDofIM.js} +1 -1
  312. package/dist/ui/assets/{xml-yz1SRLuU.js → xml-QloVn4s_.js} +1 -1
  313. package/dist/ui/assets/{xquery-BLzBF3tV.js → xquery-spJZLmyM.js} +1 -1
  314. package/dist/ui/assets/{xychartDiagram-FW5EYKEG-Czapk9-Z.js → xychartDiagram-FW5EYKEG-BxGVZcnb.js} +1 -1
  315. package/dist/ui/assets/{yaml-Cv4Y7iN_.js → yaml-CUju4iq0.js} +1 -1
  316. package/dist/ui/assets/{zephir-KRcdQfEX.js → zephir-ChVpmD0Y.js} +1 -1
  317. package/dist/ui/index.html +2 -2
  318. package/dist/utils/__tests__/abort-signals.test.js +67 -0
  319. package/dist/utils/__tests__/db-maintenance.test.js +55 -0
  320. package/dist/utils/__tests__/db-migration.test.js +79 -1
  321. package/dist/utils/__tests__/inject-loop.test.js +1 -53
  322. package/dist/utils/__tests__/learn-tasks-db.test.js +82 -0
  323. package/dist/utils/__tests__/openai-converter.test.js +25 -0
  324. package/dist/utils/__tests__/pi-crawl-tools.test.js +15 -4
  325. package/dist/utils/__tests__/pi-dedup-reconcile.test.js +11 -9
  326. package/dist/utils/__tests__/pi-events.test.d.ts +1 -0
  327. package/dist/utils/__tests__/pi-events.test.js +172 -0
  328. package/dist/utils/__tests__/pi-learn-orchestrator.test.js +216 -6
  329. package/dist/utils/__tests__/pi-runtime-json.test.js +38 -3
  330. package/dist/utils/__tests__/providers-db.test.js +40 -2
  331. package/dist/utils/__tests__/request-writer.test.js +532 -39
  332. package/dist/utils/__tests__/sse-protocol.test.d.ts +1 -0
  333. package/dist/utils/__tests__/sse-protocol.test.js +115 -0
  334. package/dist/utils/__tests__/stats-aggregator.test.d.ts +1 -0
  335. package/dist/utils/__tests__/stats-aggregator.test.js +114 -0
  336. package/dist/utils/__tests__/vault.test.d.ts +1 -0
  337. package/dist/utils/__tests__/{knowledge-store.test.js → vault.test.js} +16 -16
  338. package/dist/utils/abort-signals.d.ts +13 -0
  339. package/dist/utils/abort-signals.js +33 -0
  340. package/dist/utils/db-maintenance.d.ts +8 -0
  341. package/dist/utils/db-maintenance.js +18 -0
  342. package/dist/utils/db.d.ts +2 -0
  343. package/dist/utils/db.js +86 -0
  344. package/dist/utils/esm-dirname.d.ts +0 -15
  345. package/dist/utils/esm-dirname.js +0 -4
  346. package/dist/utils/inject-loop.d.ts +0 -27
  347. package/dist/utils/inject-loop.js +2 -56
  348. package/dist/utils/learn-tasks-db.d.ts +15 -0
  349. package/dist/utils/learn-tasks-db.js +35 -0
  350. package/dist/utils/openai-converter.js +88 -78
  351. package/dist/utils/pi-browser-tools.d.ts +0 -2
  352. package/dist/utils/pi-browser-tools.js +0 -174
  353. package/dist/utils/pi-crawl-tools.d.ts +2 -1
  354. package/dist/utils/pi-crawl-tools.js +16 -11
  355. package/dist/utils/pi-dedup.d.ts +8 -30
  356. package/dist/utils/pi-dedup.js +32 -67
  357. package/dist/utils/pi-events.d.ts +65 -0
  358. package/dist/utils/pi-events.js +98 -0
  359. package/dist/utils/pi-learn-orchestrator.d.ts +6 -5
  360. package/dist/utils/pi-learn-orchestrator.js +71 -106
  361. package/dist/utils/pi-runtime.d.ts +5 -6
  362. package/dist/utils/pi-runtime.js +38 -42
  363. package/dist/utils/pi-sandbox-ops.d.ts +0 -36
  364. package/dist/utils/pi-sandbox-ops.js +5 -190
  365. package/dist/utils/providers-db.d.ts +3 -0
  366. package/dist/utils/providers-db.js +21 -7
  367. package/dist/utils/request-writer.d.ts +77 -26
  368. package/dist/utils/request-writer.js +362 -56
  369. package/dist/utils/sse-protocol.d.ts +34 -0
  370. package/dist/utils/sse-protocol.js +121 -0
  371. package/dist/utils/sse-stream-processor.d.ts +4 -9
  372. package/dist/utils/sse-stream-processor.js +13 -104
  373. package/dist/utils/sse-writer.d.ts +1 -1
  374. package/dist/utils/sse-writer.js +3 -2
  375. package/dist/utils/stats-aggregator.d.ts +1 -1
  376. package/dist/utils/stats-aggregator.js +44 -24
  377. package/dist/utils/tool-injector.js +7 -0
  378. package/dist/utils/vault.d.ts +111 -0
  379. package/dist/utils/vault.js +434 -0
  380. package/ecosystem.config.cjs +2 -1
  381. package/package.json +1 -1
  382. package/dist/docs/assets/chunks/@localSearchIndexroot.B6Qfxe6W.js +0 -1
  383. package/dist/server/middleware/__tests__/request-id.test.js +0 -28
  384. package/dist/server/middleware/request-id.d.ts +0 -2
  385. package/dist/server/middleware/request-id.js +0 -12
  386. package/dist/types/api.d.ts +0 -20
  387. package/dist/types/index.d.ts +0 -4
  388. package/dist/types/index.js +0 -4
  389. package/dist/types/log.d.ts +0 -13
  390. package/dist/ui/assets/channel-Bc4Td8eZ.js +0 -1
  391. package/dist/ui/assets/classDiagram-OUVF2IWQ-BicxhZFq.js +0 -1
  392. package/dist/ui/assets/classDiagram-v2-EOCWNBFH-BicxhZFq.js +0 -1
  393. package/dist/ui/assets/index-C0fxlU9d.css +0 -10
  394. package/dist/ui/assets/stateDiagram-v2-6OUMAXLB-QvaEO5U6.js +0 -1
  395. package/dist/ui/assets/swimlanesDiagram-G3AALYLV-DPgCOY0D.js +0 -8
  396. package/dist/utils/__tests__/sse-accumulator.test.js +0 -83
  397. package/dist/utils/knowledge-store.d.ts +0 -51
  398. package/dist/utils/knowledge-store.js +0 -241
  399. package/dist/utils/migrate-files-to-db.d.ts +0 -1
  400. package/dist/utils/migrate-files-to-db.js +0 -138
  401. package/dist/utils/sse-accumulator.d.ts +0 -9
  402. package/dist/utils/sse-accumulator.js +0 -59
  403. /package/dist/docs/assets/{proxy-cli_data-hooks.md.w9Oe1OD3.lean.js → proxy-cli_data-hooks.md.Bz8CVbxO.lean.js} +0 -0
  404. /package/dist/docs/assets/{proxy-core_ui-routes.md.Dyqkeoiv.lean.js → proxy-core_ui-routes.md.B21t1p7R.lean.js} +0 -0
  405. /package/dist/server/{middleware/__tests__/request-id.test.d.ts → __tests__/data-push.test.d.ts} +0 -0
  406. /package/dist/{types/api.js → utils/__tests__/abort-signals.test.d.ts} +0 -0
  407. /package/dist/{types/log.js → utils/__tests__/db-maintenance.test.d.ts} +0 -0
  408. /package/dist/utils/__tests__/{knowledge-store.test.d.ts → learn-tasks-db.test.d.ts} +0 -0
  409. /package/dist/utils/__tests__/{sse-accumulator.test.d.ts → openai-converter.test.d.ts} +0 -0
@@ -1,16 +1,17 @@
1
1
  import { Readable, finished } from 'stream';
2
2
  import { convertClaudeToOpenAIRequest, convertOpenAIToClaudeResponse, createStreamTransformer, buildOpenAIHeaders } from '../utils/openai-converter.js';
3
3
  import { loadConfig } from '../config/index.js';
4
- import { saveRequestDB, extractTokenUsage, flushEventBatch, deleteResponseEvents, deleteRequestRow, updateRequestTokens, EVENT_BATCH_SIZE } from '../utils/request-writer.js';
5
- import { cleanOldRequestsDB } from '../utils/request-writer.js';
6
- import { pushNewRequest } from './data-push.js';
4
+ import { createRequestRecorder, isStreamCompleteEvents } from '../utils/request-writer.js';
5
+ import { cleanOldRequestsDB, interruptStaleRequestsOnStartup } from '../utils/request-writer.js';
6
+ import { pushNewRequest, broadcastToDataClients, clearStatsNotify, scheduleStatsNotify } from './data-push.js';
7
7
  import { initDatabase, closeDatabase, getDatabase } from '../utils/db.js';
8
+ import { recoverCrashedTasks } from '../utils/learn-tasks-db.js';
9
+ import { getActiveProvider } from '../utils/providers-db.js';
8
10
  import { aggregateStats } from '../utils/stats-aggregator.js';
9
11
  import { mcpManager } from '../utils/mcp-manager.js';
10
12
  import { getMachineIdSync, preloadMachineId } from '../utils/machine-id.js';
11
- // Cached prepared statement for active provider lookup (avoids per-request allocation)
12
- let activeProviderStmt = null;
13
- let cachedDbInstance = null;
13
+ import { createEventDecoder } from '../utils/sse-protocol.js';
14
+ import { createClientAbortSignal, createTimeoutSignal } from '../utils/abort-signals.js';
14
15
  /**
15
16
  * Resolves the proxy target per request:
16
17
  * - If a provider with is_default=1 exists in DB, use its base_url + api_key + tier models (partial replacement)
@@ -36,15 +37,7 @@ export function resolveProxyTarget(proxyConfig) {
36
37
  baseUrl = configUrl.origin;
37
38
  targetPath = configUrl.pathname.replace(/\/$/, '');
38
39
  const db = getDatabase();
39
- // Invalidate cached stmt if DB instance changed (happens in tests with fresh DB per test)
40
- if (db !== cachedDbInstance) {
41
- activeProviderStmt = null;
42
- cachedDbInstance = db;
43
- }
44
- if (!activeProviderStmt) {
45
- activeProviderStmt = db.prepare('SELECT base_url, api_key, model, haiku_model, sonnet_model, opus_model, is_openai_compatible, auth_style FROM providers WHERE is_default = 1 LIMIT 1');
46
- }
47
- const active = activeProviderStmt.get();
40
+ const active = getActiveProvider(db);
48
41
  if (active) {
49
42
  // Replace base_url only if provided
50
43
  if (active.base_url && active.base_url.trim() !== '') {
@@ -163,58 +156,6 @@ export function getErrorStatusCode(error) {
163
156
  }
164
157
  return 500;
165
158
  }
166
- /**
167
- * Push a parsed SSE event into batch/tail buffers.
168
- * Flushes batch to DB when it reaches EVENT_BATCH_SIZE (10).
169
- * Only memory held: current batch (≤10 events) + tail (≤5 events).
170
- */
171
- function pushEventToBuffers(event, sessionId, requestId, eventBatch, tailBuffer, eventIndexRef) {
172
- const entry = { index: eventIndexRef.value, type: event?.type, data: event };
173
- eventBatch.push(entry);
174
- // Maintain tail buffer (last 5 events for usage extraction)
175
- tailBuffer.push(event);
176
- if (tailBuffer.length > 5) {
177
- tailBuffer.shift();
178
- }
179
- // Flush batch to DB when threshold reached
180
- if (eventBatch.length >= EVENT_BATCH_SIZE) {
181
- flushEventBatch(sessionId, requestId, eventBatch, eventIndexRef.value - eventBatch.length + 1);
182
- eventBatch.length = 0; // Reset batch (memory freed)
183
- }
184
- eventIndexRef.value++;
185
- }
186
- /**
187
- * Flush remaining batch + update request completion in DB.
188
- * Extracts token usage from tail buffer (no need for full events array).
189
- */
190
- async function completeStreamSave(sessionId, requestId, requestData, eventBatch, tailBuffer, eventIndexRef, startTime, statusCode, resolved, forwardedModel, originalModel, machineId, request) {
191
- // Flush remaining events in batch
192
- if (eventBatch.length > 0) {
193
- flushEventBatch(sessionId, requestId, eventBatch, eventIndexRef.value - eventBatch.length);
194
- eventBatch.length = 0;
195
- }
196
- requestData.response.completedAt = new Date().toISOString();
197
- requestData.response.events = []; // Not needed for streaming, clear to avoid serialization
198
- await saveRequestDB(sessionId, requestId, requestData);
199
- const usage = extractTokenUsage(tailBuffer);
200
- updateRequestTokens(requestId, sessionId, usage);
201
- pushNewRequest({
202
- requestId,
203
- sessionId,
204
- compositeId: `${sessionId}:${requestId}`,
205
- method: request.method,
206
- path: request.url,
207
- timestamp: requestData.timestamp,
208
- statusCode,
209
- responseTime: Date.now() - startTime,
210
- model: forwardedModel || originalModel,
211
- machineId,
212
- userAgent: request.headers['user-agent'],
213
- baseUrl: resolved.baseUrl,
214
- usage,
215
- pendingId: requestId !== request.id ? `${sessionId}:${request.id}` : undefined
216
- }, 'completed');
217
- }
218
159
  export function getErrorMessage(error) {
219
160
  if (error.code === 'ECONNREFUSED') {
220
161
  return 'Target server unreachable';
@@ -227,12 +168,72 @@ export function getErrorMessage(error) {
227
168
  }
228
169
  return 'Proxy error';
229
170
  }
171
+ // SSE 流空闲看门狗:超过该时长无上游数据则销毁流,避免挂死产生僵尸行
172
+ const STREAM_IDLE_TIMEOUT_MS = 120000;
173
+ // 测试 seam:CCM_STREAM_IDLE_TIMEOUT_MS 覆盖超时(仅在 attach 时读一次)
174
+ function getIdleTimeoutMs() {
175
+ const envValue = Number(process.env.CCM_STREAM_IDLE_TIMEOUT_MS);
176
+ return Number.isFinite(envValue) && envValue > 0 ? envValue : STREAM_IDLE_TIMEOUT_MS;
177
+ }
178
+ function attachIdleWatchdog(nodeStream) {
179
+ let timer = null;
180
+ const clearTimer = () => {
181
+ if (timer) {
182
+ clearTimeout(timer);
183
+ timer = null;
184
+ }
185
+ };
186
+ const resetTimer = () => {
187
+ clearTimer();
188
+ timer = setTimeout(() => {
189
+ if (!nodeStream.destroyed) {
190
+ nodeStream.destroy(new Error('upstream idle timeout'));
191
+ }
192
+ }, getIdleTimeoutMs());
193
+ };
194
+ nodeStream.on('data', resetTimer);
195
+ nodeStream.on('end', clearTimer);
196
+ nodeStream.on('close', clearTimer);
197
+ nodeStream.on('error', clearTimer);
198
+ resetTimer();
199
+ return {
200
+ finish() {
201
+ clearTimer();
202
+ if (!nodeStream.destroyed) {
203
+ nodeStream.destroy();
204
+ }
205
+ }
206
+ };
207
+ }
230
208
  export async function setupProxy(fastify, proxyConfig) {
231
209
  // Load full config to get storage.maxDays
232
210
  const fullConfig = await loadConfig();
233
211
  const maxDays = fullConfig.storage?.maxDays ?? 7;
234
212
  // Initialize SQLite database
235
- initDatabase();
213
+ const db = initDatabase();
214
+ // 回收上次进程遗留的无主 running learn 任务(进程崩溃/被 kill)
215
+ const recoveredTasks = recoverCrashedTasks(db);
216
+ if (recoveredTasks > 0) {
217
+ console.log(`Recovered ${recoveredTasks} crashed learn task(s) from previous run`);
218
+ }
219
+ // 回收上次进程遗留的僵尸 SSE 请求行(流中断/挂死未回调):逐行分流
220
+ const { completed: completedAtStartup, interrupted: interruptedAtStartup } = interruptStaleRequestsOnStartup();
221
+ if (completedAtStartup > 0 || interruptedAtStartup > 0) {
222
+ console.log(`Recovered ${completedAtStartup} completed / ${interruptedAtStartup} interrupted stale request(s)`);
223
+ }
224
+ // 收割周期化:每 60s 扫 >2min 仍 pending 的行
225
+ const staleReaperTimer = setInterval(() => {
226
+ try {
227
+ const { completed, interrupted } = interruptStaleRequestsOnStartup(2 * 60000);
228
+ // 收割到终态行时通知前端重取 stats(trailing 合并)
229
+ if (completed + interrupted > 0) {
230
+ scheduleStatsNotify();
231
+ }
232
+ }
233
+ catch (err) {
234
+ console.error('Stale request reaping failed:', err);
235
+ }
236
+ }, 60000);
236
237
  // Initialize MCP connections (after DB is ready)
237
238
  try {
238
239
  await mcpManager.init();
@@ -245,7 +246,8 @@ export async function setupProxy(fastify, proxyConfig) {
245
246
  // Start stats aggregation timer (every 5 minutes)
246
247
  const statsTimer = setInterval(() => {
247
248
  try {
248
- aggregateStats();
249
+ aggregateStats(maxDays);
250
+ broadcastToDataClients({ type: 'stats', action: 'notify' });
249
251
  }
250
252
  catch (err) {
251
253
  console.error('Stats aggregation failed:', err);
@@ -262,7 +264,8 @@ export async function setupProxy(fastify, proxyConfig) {
262
264
  }, 1800000); // 30 minutes
263
265
  // Initial aggregation on startup
264
266
  try {
265
- aggregateStats();
267
+ aggregateStats(maxDays);
268
+ broadcastToDataClients({ type: 'stats', action: 'notify' });
266
269
  }
267
270
  catch (err) {
268
271
  console.error('Initial stats aggregation failed:', err);
@@ -278,6 +281,8 @@ export async function setupProxy(fastify, proxyConfig) {
278
281
  fastify.addHook('onClose', async () => {
279
282
  clearInterval(statsTimer);
280
283
  clearInterval(cleanupTimer);
284
+ clearInterval(staleReaperTimer);
285
+ clearStatsNotify();
281
286
  await mcpManager.close();
282
287
  closeDatabase();
283
288
  });
@@ -286,9 +291,12 @@ export async function setupProxy(fastify, proxyConfig) {
286
291
  fastify.all('/v1/*', async (request, reply) => {
287
292
  const startTime = Date.now();
288
293
  const resolved = resolveProxyTarget(proxyConfig);
294
+ // 下游客户端中止:整个请求作用域建一次,两处上游 fetch 复用
295
+ const clientSignal = createClientAbortSignal(request.raw);
289
296
  const sessionId = request.headers['x-claude-code-session-id'] || 'unknown-session';
290
297
  // Replace model based on tier matching (getProxyModel)
291
298
  const originalModel = request.body?.model;
299
+ let rec;
292
300
  try {
293
301
  const proxyModel = getProxyModel(originalModel, resolved);
294
302
  if (proxyModel && request.body && typeof request.body === 'object') {
@@ -312,9 +320,9 @@ export async function setupProxy(fastify, proxyConfig) {
312
320
  (forwardedAuthHeader?.startsWith('Bearer ') ? forwardedAuthHeader.slice(7) : undefined);
313
321
  // Log complete request data (Fastify already parsed body)
314
322
  const machineId = getMachineIdSync() ?? undefined;
315
- const requestData = {
316
- requestId: request.id, // Will be updated with cloud x-request-id later
323
+ const meta = {
317
324
  sessionId,
325
+ localRequestId: request.id, // Will be replaced with cloud x-request-id row on completion
318
326
  timestamp: new Date().toISOString(),
319
327
  apiKey,
320
328
  proxyApiKey,
@@ -326,34 +334,12 @@ export async function setupProxy(fastify, proxyConfig) {
326
334
  path: request.url,
327
335
  headers: forwardHeaders,
328
336
  body: request.body // Full payload: model, messages, tools, MCP params, etc.
329
- },
330
- response: {
331
- statusCode: 0, // Will be updated after fetch
332
- events: [] // Only used for JSON responses; streaming uses batch buffer
333
337
  }
334
338
  };
335
- // SSE event batch buffer: flush to DB every ~10 events to avoid in-memory accumulation
336
- // Tail buffer: keep last 5 events for token usage extraction at stream end
337
- const eventBatch = [];
338
- const tailBuffer = [];
339
- const eventIndexRef = { value: 0 };
339
+ rec = createRequestRecorder(meta);
340
340
  request.log.info(`request logged: ${request.id} ${request.method} ${request.url}`);
341
341
  // Save initial record + push pending status to frontend
342
- await saveRequestDB(sessionId, request.id, requestData);
343
- pushNewRequest({
344
- requestId: request.id,
345
- sessionId,
346
- compositeId: `${sessionId}:${request.id}`,
347
- method: request.method,
348
- path: request.url,
349
- timestamp: requestData.timestamp,
350
- statusCode: 0,
351
- responseTime: 0,
352
- model: forwardedModel,
353
- machineId,
354
- userAgent: request.headers['user-agent'],
355
- baseUrl: resolved.baseUrl
356
- }, 'pending');
342
+ await rec.pending();
357
343
  // --- OpenAI Compatible Mode ---
358
344
  if (resolved.is_openai_compatible) {
359
345
  const openaiRequest = convertClaudeToOpenAIRequest(request.body, forwardedModel);
@@ -366,14 +352,11 @@ export async function setupProxy(fastify, proxyConfig) {
366
352
  method: request.method,
367
353
  headers: openaiHeaders,
368
354
  body: JSON.stringify(openaiRequest),
369
- signal: AbortSignal.timeout(300000)
355
+ signal: createTimeoutSignal(300000, clientSignal)
370
356
  });
371
357
  request.log.info(`response status: ${response.status}`);
372
358
  // Extract cloud request ID
373
359
  const cloudRequestId = response.headers.get('x-request-id') || request.id;
374
- requestData.requestId = cloudRequestId;
375
- requestData.response.statusCode = response.status;
376
- requestData.response.headers = Object.fromEntries(response.headers);
377
360
  // Check response type
378
361
  const contentType = response.headers.get('content-type') || '';
379
362
  const isSSE = contentType.includes('text/event-stream');
@@ -386,46 +369,30 @@ export async function setupProxy(fastify, proxyConfig) {
386
369
  const openaiRes = JSON.parse(responseBody);
387
370
  const claudeRes = convertOpenAIToClaudeResponse(openaiRes, forwardedModel || originalModel);
388
371
  claudeResponseBody = JSON.stringify(claudeRes);
389
- requestData.response.events.push(claudeRes);
372
+ rec.appendEvent(claudeRes);
390
373
  }
391
374
  catch {
392
375
  claudeResponseBody = responseBody;
393
376
  try {
394
- requestData.response.events.push(JSON.parse(responseBody));
377
+ rec.appendEvent(JSON.parse(responseBody));
395
378
  }
396
379
  catch {
397
- requestData.response.events.push({ raw: responseBody });
380
+ rec.appendEvent({ raw: responseBody });
398
381
  }
399
382
  }
400
- requestData.response.completedAt = new Date().toISOString();
401
- if (cloudRequestId !== request.id) {
402
- deleteResponseEvents(request.id, sessionId);
403
- deleteRequestRow(request.id, sessionId);
404
- }
405
- await saveRequestDB(sessionId, cloudRequestId, requestData);
406
- const usage = extractTokenUsage(requestData.response.events);
407
- pushNewRequest({
408
- requestId: cloudRequestId,
409
- sessionId,
410
- compositeId: `${sessionId}:${cloudRequestId}`,
411
- method: request.method,
412
- path: request.url,
413
- timestamp: requestData.timestamp,
383
+ await rec.complete({
384
+ cloudRequestId: cloudRequestId !== request.id ? cloudRequestId : undefined,
414
385
  statusCode: response.status,
415
- responseTime: Date.now() - startTime,
416
- model: forwardedModel,
417
- machineId,
418
- userAgent: request.headers['user-agent'],
419
- baseUrl: resolved.baseUrl,
420
- usage,
421
- pendingId: cloudRequestId !== request.id ? `${sessionId}:${request.id}` : undefined
422
- }, 'completed');
386
+ headers: Object.fromEntries(response.headers)
387
+ });
423
388
  const elapsed = Date.now() - startTime;
424
389
  request.log.info(`json response (openai): ${cloudRequestId} ${response.status} (${elapsed}ms)`);
425
390
  reply.code(response.status).headers({ 'Content-Type': 'application/json' }).send(claudeResponseBody);
426
391
  return;
427
392
  }
428
393
  // Handle SSE streaming response
394
+ // 上游响应头已到达:回写真实 status_code(状态仍 pending,保留 spinner)
395
+ rec.headers({ statusCode: response.status, headers: Object.fromEntries(response.headers) });
429
396
  reply.hijack();
430
397
  const responseHeaders = Object.fromEntries(response.headers);
431
398
  // Replace content-type with Claude SSE format
@@ -433,55 +400,56 @@ export async function setupProxy(fastify, proxyConfig) {
433
400
  reply.raw.writeHead(response.status, responseHeaders);
434
401
  if (response.body) {
435
402
  const nodeStream = Readable.fromWeb(response.body);
403
+ // 客户端断开:销毁上游流,finished 回调走中断落库
404
+ clientSignal.addEventListener('abort', () => {
405
+ if (!nodeStream.destroyed)
406
+ nodeStream.destroy(new Error('client disconnected'));
407
+ });
408
+ const watchdog = attachIdleWatchdog(nodeStream);
436
409
  const transformStream = createStreamTransformer(forwardedModel || originalModel);
437
- // Record SSE events via batch buffer (memory: ≤10 batch + ≤5 tail)
438
- nodeStream.on('data', (chunk) => {
439
- const text = chunk.toString();
440
- const lines = text.split('\n');
441
- for (const line of lines) {
442
- if (line.startsWith('data:')) {
443
- const eventData = line.slice(5).trim();
444
- if (eventData && eventData !== '[DONE]') {
445
- try {
446
- const event = JSON.parse(eventData);
447
- pushEventToBuffers(event, sessionId, cloudRequestId, eventBatch, tailBuffer, eventIndexRef);
448
- }
449
- catch {
450
- // Not valid JSON, skip
451
- }
452
- }
453
- }
410
+ // 流是否已逻辑终止(finish_reason / [DONE]);err 分支据此改走 complete
411
+ let logicallyEnded = false;
412
+ // 旁路解码器:只做事件入库,不介入转发管道;[DONE] 走 onDone 触发逻辑终止
413
+ const tapDecoder = createEventDecoder((e) => {
414
+ rec.appendEvent(e);
415
+ if (!logicallyEnded && isStreamCompleteEvents([e])) {
416
+ logicallyEnded = true;
417
+ watchdog.finish();
454
418
  }
419
+ }, () => {
420
+ if (!logicallyEnded) {
421
+ logicallyEnded = true;
422
+ watchdog.finish();
423
+ }
424
+ });
425
+ nodeStream.on('data', (chunk) => {
426
+ tapDecoder.write(chunk);
455
427
  });
456
428
  // Use finished() for reliable stream completion detection
457
429
  finished(nodeStream, async (err) => {
458
- if (err) {
430
+ tapDecoder.end();
431
+ if (err && !logicallyEnded) {
459
432
  request.log.error(`openai stream error: ${err.message}`);
460
- }
461
- // Convert OpenAI stream usage to Claude format for extractTokenUsage compatibility
462
- const lastChunk = tailBuffer.find((e) => e?.usage);
463
- if (lastChunk?.usage) {
464
- const syntheticEvent = {
465
- type: 'message_delta',
466
- usage: {
467
- output_tokens: lastChunk.usage.completion_tokens || 0
468
- }
469
- };
470
- pushEventToBuffers(syntheticEvent, sessionId, cloudRequestId, eventBatch, tailBuffer, eventIndexRef);
471
- }
472
- if (cloudRequestId !== request.id) {
473
- deleteResponseEvents(request.id, sessionId);
474
- deleteRequestRow(request.id, sessionId);
433
+ rec.interrupt({ statusCode: response.status, error: `stream interrupted: ${err.message}` });
434
+ return;
475
435
  }
476
436
  // Flush remaining batch + complete request
477
437
  try {
478
- await completeStreamSave(sessionId, cloudRequestId, requestData, eventBatch, tailBuffer, eventIndexRef, startTime, response.status, resolved, forwardedModel, originalModel, machineId, request);
438
+ await rec.complete({
439
+ cloudRequestId: cloudRequestId !== request.id ? cloudRequestId : undefined,
440
+ statusCode: response.status,
441
+ headers: Object.fromEntries(response.headers)
442
+ });
479
443
  const elapsed = Date.now() - startTime;
480
- request.log.info(`stream completed (openai): ${cloudRequestId} ${response.status} (${elapsed}ms, ${eventIndexRef.value} events)`);
444
+ request.log.info(`stream completed (openai): ${cloudRequestId} ${response.status} (${elapsed}ms)`);
481
445
  }
482
446
  catch (dbErr) {
483
447
  request.log.error(`openai stream save failed: ${dbErr.message}`);
484
448
  }
449
+ // pipe 在无 'end' 的 destroy 后不会收尾 dest,手动结束客户端连接
450
+ if (reply.raw.writableEnded === false && !reply.raw.destroyed) {
451
+ reply.raw.end();
452
+ }
485
453
  });
486
454
  // Pipe: OpenAI SSE → Transform → Claude SSE → Client
487
455
  nodeStream.pipe(transformStream).pipe(reply.raw);
@@ -498,15 +466,11 @@ export async function setupProxy(fastify, proxyConfig) {
498
466
  method: request.method,
499
467
  headers: forwardHeaders,
500
468
  body: request.method !== 'GET' ? JSON.stringify(request.body) : undefined,
501
- signal: AbortSignal.timeout(300000)
469
+ signal: createTimeoutSignal(300000, clientSignal)
502
470
  });
503
471
  request.log.info(`response status: ${response.status}`);
504
472
  // Extract cloud request ID from response header
505
473
  const cloudRequestId = response.headers.get('x-request-id') || request.id;
506
- requestData.requestId = cloudRequestId;
507
- // Update response status in requestData
508
- requestData.response.statusCode = response.status;
509
- requestData.response.headers = Object.fromEntries(response.headers);
510
474
  // Check response type: SSE stream or JSON
511
475
  const contentType = response.headers.get('content-type') || '';
512
476
  const isSSE = contentType.includes('text/event-stream');
@@ -517,35 +481,16 @@ export async function setupProxy(fastify, proxyConfig) {
517
481
  // Record JSON body as a single event
518
482
  try {
519
483
  const jsonBody = JSON.parse(responseBody);
520
- requestData.response.events.push(jsonBody);
484
+ rec.appendEvent(jsonBody);
521
485
  }
522
486
  catch {
523
- requestData.response.events.push({ raw: responseBody });
524
- }
525
- requestData.response.completedAt = new Date().toISOString();
526
- // Remove pending row if cloudRequestId differs from request.id
527
- if (cloudRequestId !== request.id) {
528
- deleteResponseEvents(request.id, sessionId);
529
- deleteRequestRow(request.id, sessionId);
487
+ rec.appendEvent({ raw: responseBody });
530
488
  }
531
- await saveRequestDB(sessionId, cloudRequestId, requestData);
532
- const usage = extractTokenUsage(requestData.response.events);
533
- pushNewRequest({
534
- requestId: cloudRequestId,
535
- sessionId,
536
- compositeId: `${sessionId}:${cloudRequestId}`,
537
- method: request.method,
538
- path: request.url,
539
- timestamp: requestData.timestamp,
489
+ await rec.complete({
490
+ cloudRequestId: cloudRequestId !== request.id ? cloudRequestId : undefined,
540
491
  statusCode: response.status,
541
- responseTime: Date.now() - startTime,
542
- model: forwardedModel,
543
- machineId,
544
- userAgent: request.headers['user-agent'],
545
- baseUrl: resolved.baseUrl,
546
- usage,
547
- pendingId: cloudRequestId !== request.id ? `${sessionId}:${request.id}` : undefined
548
- }, 'completed');
492
+ headers: Object.fromEntries(response.headers)
493
+ });
549
494
  const elapsed = Date.now() - startTime;
550
495
  request.log.info(`json response: ${cloudRequestId} ${response.status} (${elapsed}ms)`);
551
496
  reply.code(response.status);
@@ -554,6 +499,8 @@ export async function setupProxy(fastify, proxyConfig) {
554
499
  return;
555
500
  }
556
501
  // Hijack reply to take full control of SSE stream
502
+ // 上游响应头已到达:回写真实 status_code(状态仍 pending,保留 spinner)
503
+ rec.headers({ statusCode: response.status, headers: Object.fromEntries(response.headers) });
557
504
  reply.hijack();
558
505
  // Write response headers (preserve original from Aliyun)
559
506
  const responseHeaders = Object.fromEntries(response.headers);
@@ -562,42 +509,54 @@ export async function setupProxy(fastify, proxyConfig) {
562
509
  if (response.body) {
563
510
  // Convert Web ReadableStream to Node Readable stream
564
511
  const nodeStream = Readable.fromWeb(response.body);
565
- // Record SSE events via batch buffer (memory: ≤10 batch + ≤5 tail)
566
- nodeStream.on('data', (chunk) => {
567
- const text = chunk.toString();
568
- const lines = text.split('\n');
569
- for (const line of lines) {
570
- if (line.startsWith('data:')) {
571
- const eventData = line.slice(5).trim();
572
- if (eventData && eventData !== '[DONE]') {
573
- try {
574
- const event = JSON.parse(eventData);
575
- pushEventToBuffers(event, sessionId, cloudRequestId, eventBatch, tailBuffer, eventIndexRef);
576
- }
577
- catch {
578
- // Not valid JSON, skip
579
- }
580
- }
581
- }
512
+ // 客户端断开:销毁上游流,finished 回调走中断落库
513
+ clientSignal.addEventListener('abort', () => {
514
+ if (!nodeStream.destroyed)
515
+ nodeStream.destroy(new Error('client disconnected'));
516
+ });
517
+ const watchdog = attachIdleWatchdog(nodeStream);
518
+ // 流是否已逻辑终止(message_stop);err 分支据此改走 complete
519
+ let logicallyEnded = false;
520
+ // 旁路解码器:只做事件入库,不介入转发管道
521
+ const tapDecoder = createEventDecoder((e) => {
522
+ rec.appendEvent(e);
523
+ if (!logicallyEnded && isStreamCompleteEvents([e])) {
524
+ logicallyEnded = true;
525
+ watchdog.finish();
526
+ }
527
+ }, () => {
528
+ if (!logicallyEnded) {
529
+ logicallyEnded = true;
530
+ watchdog.finish();
582
531
  }
583
532
  });
533
+ nodeStream.on('data', (chunk) => {
534
+ tapDecoder.write(chunk);
535
+ });
584
536
  // Save request data when stream completes
585
537
  finished(nodeStream, async (err) => {
586
- if (err) {
538
+ tapDecoder.end();
539
+ if (err && !logicallyEnded) {
587
540
  request.log.error(`claude stream error: ${err.message}`);
588
- }
589
- if (cloudRequestId !== request.id) {
590
- deleteResponseEvents(request.id, sessionId);
591
- deleteRequestRow(request.id, sessionId);
541
+ rec.interrupt({ statusCode: response.status, error: `stream interrupted: ${err.message}` });
542
+ return;
592
543
  }
593
544
  try {
594
- await completeStreamSave(sessionId, cloudRequestId, requestData, eventBatch, tailBuffer, eventIndexRef, startTime, response.status, resolved, forwardedModel, originalModel, machineId, request);
545
+ await rec.complete({
546
+ cloudRequestId: cloudRequestId !== request.id ? cloudRequestId : undefined,
547
+ statusCode: response.status,
548
+ headers: Object.fromEntries(response.headers)
549
+ });
595
550
  const elapsed = Date.now() - startTime;
596
- request.log.info(`stream completed: ${cloudRequestId} ${response.status} (${elapsed}ms, ${eventIndexRef.value} events)`);
551
+ request.log.info(`stream completed: ${cloudRequestId} ${response.status} (${elapsed}ms)`);
597
552
  }
598
553
  catch (dbErr) {
599
554
  request.log.error(`stream save failed: ${dbErr.message}`);
600
555
  }
556
+ // pipe 在无 'end' 的 destroy 后不会收尾 dest,手动结束客户端连接
557
+ if (reply.raw.writableEnded === false && !reply.raw.destroyed) {
558
+ reply.raw.end();
559
+ }
601
560
  });
602
561
  // Pipe: Aliyun -> Claude Code
603
562
  nodeStream.pipe(reply.raw);
@@ -610,22 +569,27 @@ export async function setupProxy(fastify, proxyConfig) {
610
569
  errorMessage: error.message,
611
570
  errorCode: error.code || 'unknown'
612
571
  }, 'proxy error');
613
- // Fetch failed — no cloudRequestId available, use Fastify request.id
614
- pushNewRequest({
615
- requestId: request.id,
616
- sessionId,
617
- compositeId: `${sessionId}:${request.id}`,
618
- method: request.method,
619
- path: request.url,
620
- timestamp: new Date().toISOString(),
621
- statusCode: getErrorStatusCode(error),
622
- responseTime: Date.now() - startTime,
623
- model: originalModel,
624
- machineId: getMachineIdSync() ?? undefined,
625
- userAgent: request.headers['user-agent'],
626
- baseUrl: resolved.baseUrl
627
- }, 'completed');
628
572
  const statusCode = getErrorStatusCode(error);
573
+ if (rec) {
574
+ rec.fail({ statusCode, error: getErrorMessage(error) });
575
+ }
576
+ else {
577
+ // pending() 之前就抛错(header/meta 准备阶段),无 DB 行可更新,仅推送
578
+ pushNewRequest({
579
+ requestId: request.id,
580
+ sessionId,
581
+ compositeId: `${sessionId}:${request.id}`,
582
+ method: request.method,
583
+ path: request.url,
584
+ timestamp: new Date().toISOString(),
585
+ statusCode,
586
+ responseTime: Date.now() - startTime,
587
+ model: originalModel,
588
+ machineId: getMachineIdSync() ?? undefined,
589
+ userAgent: request.headers['user-agent'],
590
+ baseUrl: resolved.baseUrl
591
+ }, 'completed');
592
+ }
629
593
  reply.code(statusCode).send({
630
594
  error: getErrorMessage(error),
631
595
  requestId: request.id