cartethyia 2.5.9 → 2.6.0

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 (386) hide show
  1. package/.env.example +15 -42
  2. package/README.md +11 -10
  3. package/dist/dashboard/assets/{BannedPage-Brvuj_Eb.js → BannedPage-l6XMnfr9.js} +1 -1
  4. package/dist/dashboard/assets/BannedPage-l6XMnfr9.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolDetailPage-CqfXtI3y.js → CliToolDetailPage-DwOzAW45.js} +1 -1
  6. package/dist/dashboard/assets/CliToolDetailPage-DwOzAW45.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolsPage-GQeS91bg.js → CliToolsPage-BT3Up3QA.js} +1 -1
  8. package/dist/dashboard/assets/CliToolsPage-BT3Up3QA.js.gz +0 -0
  9. package/dist/dashboard/assets/{CombosPage-BDRsTXBB.js → CombosPage-6kYIUpzR.js} +1 -1
  10. package/dist/dashboard/assets/CombosPage-6kYIUpzR.js.gz +0 -0
  11. package/dist/dashboard/assets/{ConsoleLogPage-zP8AFHX0.js → ConsoleLogPage-zRnj1mlR.js} +1 -1
  12. package/dist/dashboard/assets/ConsoleLogPage-zRnj1mlR.js.gz +0 -0
  13. package/dist/dashboard/assets/{CustomizationPage-BmfrNZ14.js → CustomizationPage-C7m-H1Rz.js} +1 -1
  14. package/dist/dashboard/assets/CustomizationPage-C7m-H1Rz.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js.gz +0 -0
  17. package/dist/dashboard/assets/{LoginPage-DoreIgm8.js → LoginPage-CbphLvjg.js} +1 -1
  18. package/dist/dashboard/assets/LoginPage-CbphLvjg.js.gz +0 -0
  19. package/dist/dashboard/assets/{ModelPicker-C7pDb41R.js → ModelPicker-DVny4S5_.js} +1 -1
  20. package/dist/dashboard/assets/ModelPicker-DVny4S5_.js.gz +0 -0
  21. package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js +1 -0
  22. package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js.gz +0 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js +3 -0
  24. package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js.gz +0 -0
  25. package/dist/dashboard/assets/{ProvidersPage-BnoPgA7w.js → ProvidersPage-Dfb2896L.js} +1 -1
  26. package/dist/dashboard/assets/ProvidersPage-Dfb2896L.js.gz +0 -0
  27. package/dist/dashboard/assets/{ProxyPage-Bq_TGgdb.js → ProxyPage-BaCIdAUt.js} +1 -1
  28. package/dist/dashboard/assets/ProxyPage-BaCIdAUt.js.gz +0 -0
  29. package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js +1 -0
  30. package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js.gz +0 -0
  31. package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js +1 -0
  32. package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js.gz +0 -0
  33. package/dist/dashboard/assets/{SetupPage-Day3mCOn.js → SetupPage-D4g6vOGc.js} +1 -1
  34. package/dist/dashboard/assets/SetupPage-D4g6vOGc.js.gz +0 -0
  35. package/dist/dashboard/assets/{SortableList-DQxllnB5.js → SortableList-C93i14BD.js} +1 -1
  36. package/dist/dashboard/assets/SortableList-C93i14BD.js.gz +0 -0
  37. package/dist/dashboard/assets/{StudioPage-uMExoWu1.js → StudioPage-SgQpymjm.js} +1 -1
  38. package/dist/dashboard/assets/{StudioPage-uMExoWu1.js.gz → StudioPage-SgQpymjm.js.gz} +0 -0
  39. package/dist/dashboard/assets/{UsagePage-DXa1oR-4.js → UsagePage-BFME-eba.js} +2 -2
  40. package/dist/dashboard/assets/UsagePage-BFME-eba.js.gz +0 -0
  41. package/dist/dashboard/assets/{api-keys-DQYLQxpW.js → api-keys-C23S9sgp.js} +1 -1
  42. package/dist/dashboard/assets/api-keys-C23S9sgp.js.gz +0 -0
  43. package/dist/dashboard/assets/{app-BIt446yE.js → app-2KhtHCv9.js} +1 -1
  44. package/dist/dashboard/assets/{app-BIt446yE.js.gz → app-2KhtHCv9.js.gz} +0 -0
  45. package/dist/dashboard/assets/app-CEeaMHOy.css +2 -0
  46. package/dist/dashboard/assets/app-CEeaMHOy.css.gz +0 -0
  47. package/dist/dashboard/assets/{assert-C9FD_JwJ.js → assert-DU6qrMA_.js} +1 -1
  48. package/dist/dashboard/assets/assert-DU6qrMA_.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-CR72dasT.js → badge-CKsF123d.js} +1 -1
  50. package/dist/dashboard/assets/badge-CKsF123d.js.gz +0 -0
  51. package/dist/dashboard/assets/{banner-CI9_eSfO.js → banner-8j7uChA9.js} +1 -1
  52. package/dist/dashboard/assets/banner-8j7uChA9.js.gz +0 -0
  53. package/dist/dashboard/assets/{card-DS7CQEsO.js → card-CdsSlWA3.js} +1 -1
  54. package/dist/dashboard/assets/card-CdsSlWA3.js.gz +0 -0
  55. package/dist/dashboard/assets/{cli-tools-CNqzrfdb.js → cli-tools-BaFUuFFF.js} +1 -1
  56. package/dist/dashboard/assets/cli-tools-BaFUuFFF.js.gz +0 -0
  57. package/dist/dashboard/assets/{clipboard-button-611pSsaX.js → clipboard-button-uL5aYLfW.js} +1 -1
  58. package/dist/dashboard/assets/clipboard-button-uL5aYLfW.js.gz +0 -0
  59. package/dist/dashboard/assets/{dialog-k37exjNt.js → dialog-C07hr5_G.js} +1 -1
  60. package/dist/dashboard/assets/dialog-C07hr5_G.js.gz +0 -0
  61. package/dist/dashboard/assets/{drawer-CX7N8ucR.js → drawer-BPO1TcKH.js} +1 -1
  62. package/dist/dashboard/assets/drawer-BPO1TcKH.js.gz +0 -0
  63. package/dist/dashboard/assets/{entry-CTP8AyiS.js → entry-Bz_CtDfC.js} +1 -1
  64. package/dist/dashboard/assets/entry-Bz_CtDfC.js.gz +0 -0
  65. package/dist/dashboard/assets/entry-CwPRKk0d.js +4 -0
  66. package/dist/dashboard/assets/entry-CwPRKk0d.js.gz +0 -0
  67. package/dist/dashboard/assets/entry-GJRbmrpA.js +1 -0
  68. package/dist/dashboard/assets/entry-GJRbmrpA.js.gz +0 -0
  69. package/dist/dashboard/assets/{github-badge-sltk1qUi.js → github-badge-DmQreSDd.js} +1 -1
  70. package/dist/dashboard/assets/github-badge-DmQreSDd.js.gz +0 -0
  71. package/dist/dashboard/assets/{index-z3zDOSMM.js → index-Bblu6erR.js} +2 -2
  72. package/dist/dashboard/assets/index-Bblu6erR.js.gz +0 -0
  73. package/dist/dashboard/assets/{inline-BhY2sSnO.js → inline-C9dCYaBZ.js} +1 -1
  74. package/dist/dashboard/assets/inline-C9dCYaBZ.js.gz +0 -0
  75. package/dist/dashboard/assets/{input-V1umFiBk.js → input-CjCnXweJ.js} +1 -1
  76. package/dist/dashboard/assets/input-CjCnXweJ.js.gz +0 -0
  77. package/dist/dashboard/assets/{layout-BBOYycyS.js → layout-V6y8DAkl.js} +1 -1
  78. package/dist/dashboard/assets/layout-V6y8DAkl.js.gz +0 -0
  79. package/dist/dashboard/assets/{list-row-1zzZYM0B.js → list-row-CM1AaG_y.js} +1 -1
  80. package/dist/dashboard/assets/list-row-CM1AaG_y.js.gz +0 -0
  81. package/dist/dashboard/assets/{live-BivGfuUs.js → live-ta6GMb-8.js} +1 -1
  82. package/dist/dashboard/assets/live-ta6GMb-8.js.gz +0 -0
  83. package/dist/dashboard/assets/{model-bans-CmffjLuG.js → model-bans-BmYTmbAk.js} +1 -1
  84. package/dist/dashboard/assets/model-bans-BmYTmbAk.js.gz +0 -0
  85. package/dist/dashboard/assets/{mutation-46P0oWMe.js → mutation-g_fduiNw.js} +1 -1
  86. package/dist/dashboard/assets/mutation-g_fduiNw.js.gz +0 -0
  87. package/dist/dashboard/assets/{numbers-DkfMR5Qs.js → numbers-BOevJzdz.js} +1 -1
  88. package/dist/dashboard/assets/numbers-BOevJzdz.js.gz +0 -0
  89. package/dist/dashboard/assets/{query-keys-BFhGpwR1.js → query-keys-UETqfytp.js} +1 -1
  90. package/dist/dashboard/assets/query-keys-UETqfytp.js.gz +0 -0
  91. package/dist/dashboard/assets/{routing-CAb4F37R.js → routing-BPUo-W8d.js} +1 -1
  92. package/dist/dashboard/assets/routing-BPUo-W8d.js.gz +0 -0
  93. package/dist/dashboard/assets/{select-C6MMiaio.js → select-C980c4cz.js} +1 -1
  94. package/dist/dashboard/assets/select-C980c4cz.js.gz +0 -0
  95. package/dist/dashboard/assets/{settings-DNX9sydi.js → settings--3hxSyry.js} +1 -1
  96. package/dist/dashboard/assets/settings--3hxSyry.js.gz +0 -0
  97. package/dist/dashboard/assets/{stack-BHTfXdz_.js → stack-_gfTdLVB.js} +1 -1
  98. package/dist/dashboard/assets/stack-_gfTdLVB.js.gz +0 -0
  99. package/dist/dashboard/assets/{state-mPDRlQkl.js → state-2c5V4zo7.js} +1 -1
  100. package/dist/dashboard/assets/state-2c5V4zo7.js.gz +0 -0
  101. package/dist/dashboard/assets/{switch-DOmJxg7H.js → switch-Dkke7Naf.js} +1 -1
  102. package/dist/dashboard/assets/switch-Dkke7Naf.js.gz +0 -0
  103. package/dist/dashboard/assets/{useMutation-Dem0lXQj.js → useMutation-EDFRkn1i.js} +1 -1
  104. package/dist/dashboard/assets/useMutation-EDFRkn1i.js.gz +0 -0
  105. package/dist/dashboard/index.html +1 -1
  106. package/dist/dashboard/index.html.gz +0 -0
  107. package/dist/dashboard/precompressed-assets.json +51 -51
  108. package/migrations/README.md +52 -52
  109. package/migrations/full-postgres/0020_add_telemetry_event_analytics.sql +77 -0
  110. package/migrations/full-postgres/0021_add_api_key_concurrency_tolerance.sql +13 -0
  111. package/migrations/full-postgres/0022_add_inferhub_config_key.sql +17 -0
  112. package/migrations/full-postgres/0023_drop_api_key_concurrency_tolerance.sql +13 -0
  113. package/migrations/lite-sqlite3/0020_add_telemetry_event_analytics.sql +69 -0
  114. package/migrations/lite-sqlite3/0021_add_api_key_concurrency_tolerance.sql +13 -0
  115. package/migrations/lite-sqlite3/0022_add_inferhub_config_key.sql +17 -0
  116. package/migrations/lite-sqlite3/0023_drop_api_key_concurrency_tolerance.sql +13 -0
  117. package/package.json +96 -97
  118. package/scripts/cli/admin.ts +126 -125
  119. package/scripts/cli/autostart.ts +237 -237
  120. package/scripts/cli/background.ts +120 -110
  121. package/scripts/cli/bin/carte.cjs +8 -8
  122. package/scripts/cli/bin/cartethyia.cjs +167 -167
  123. package/scripts/cli/console-menu.ts +308 -275
  124. package/scripts/cli/controller.ts +145 -0
  125. package/scripts/cli/doctor.ts +1 -1
  126. package/scripts/cli/env.ts +99 -99
  127. package/scripts/cli/gateway.ts +264 -261
  128. package/scripts/{health.ts → cli/health.ts} +4 -4
  129. package/scripts/cli/instance.ts +234 -224
  130. package/scripts/cli/main.ts +188 -164
  131. package/scripts/cli/platform.ts +64 -64
  132. package/scripts/cli/prompt.ts +275 -107
  133. package/scripts/cli/services.ts +88 -88
  134. package/scripts/cli/setup.ts +445 -445
  135. package/scripts/cli/start.ts +85 -82
  136. package/scripts/cli/startup-signal.ts +23 -23
  137. package/scripts/cli/telemetry.ts +70 -0
  138. package/scripts/cli/tray/index.ts +263 -263
  139. package/scripts/cli/tray/windows.ps1 +107 -107
  140. package/scripts/cli/tray.ts +192 -208
  141. package/scripts/cli/update.ts +39 -39
  142. package/scripts/cli/version.ts +67 -67
  143. package/scripts/cli/wordmark.ts +58 -58
  144. package/src/boot/app.ts +16 -1
  145. package/src/boot/dependencies.ts +128 -25
  146. package/src/config/package-paths.ts +24 -0
  147. package/src/config/resources.ts +137 -0
  148. package/src/config/runtime.ts +133 -76
  149. package/src/config/spec.ts +30 -29
  150. package/src/config/telemetry.ts +0 -25
  151. package/src/console/api-keys/bucket-spend.ts +14 -17
  152. package/src/console/api-keys/contracts.ts +410 -410
  153. package/src/console/api-keys/routes.ts +12 -0
  154. package/src/console/audit/store.ts +2 -3
  155. package/src/console/auth/setup-service.ts +8 -1
  156. package/src/console/backup/routes.ts +7 -0
  157. package/src/console/backup/service.ts +463 -463
  158. package/src/console/backup/store.ts +16 -11
  159. package/src/console/backup/validate.ts +1 -4
  160. package/src/console/cli-tools/fs-ops.ts +18 -2
  161. package/src/console/data-management/contracts.ts +1 -8
  162. package/src/console/data-management/operations.ts +61 -48
  163. package/src/console/data-management/routes.ts +4 -2
  164. package/src/console/domain-registration.ts +25 -3
  165. package/src/console/inferhub/client.ts +139 -0
  166. package/src/console/inferhub/key-store.ts +129 -0
  167. package/src/console/inferhub/normalize.ts +347 -0
  168. package/src/console/inferhub/routes.ts +411 -0
  169. package/src/console/inferhub/store.ts +109 -0
  170. package/src/console/inferhub/types.ts +183 -0
  171. package/src/console/observability/contracts.ts +2 -41
  172. package/src/console/observability/store.ts +176 -371
  173. package/src/console/providers/catalog/public-model-store.ts +0 -0
  174. package/src/console/providers/catalog/store.ts +23 -36
  175. package/src/console/quota/account-quota-shared.ts +52 -52
  176. package/src/console/quota/account-quota-tenant.ts +16 -24
  177. package/src/console/quota/refresh.ts +441 -441
  178. package/src/console/remote-support/service.ts +270 -114
  179. package/src/console/routing/pools/import-parse.ts +206 -206
  180. package/src/console/routing/pools/operations.ts +543 -543
  181. package/src/console/routing/pools/probe-verdict.ts +132 -132
  182. package/src/console/routing/pools/routes.ts +181 -181
  183. package/src/console/routing/pools/store.ts +1 -1
  184. package/src/console/settings/contracts.ts +42 -40
  185. package/src/console/settings/store.ts +25 -25
  186. package/src/console/share/share-stats.ts +22 -4
  187. package/src/console/share/share-usage.ts +30 -4
  188. package/src/console/shared/cursor.ts +15 -0
  189. package/src/console/updates/repository-updates.ts +40 -39
  190. package/src/console/updates/routes.ts +12 -0
  191. package/src/main.ts +13 -5
  192. package/src/network/pool/resolver.ts +1104 -1104
  193. package/src/observability/client-name.ts +22 -0
  194. package/src/observability/event-log.ts +5 -29
  195. package/src/observability/event-query.ts +690 -0
  196. package/src/observability/log-ring.ts +26 -2
  197. package/src/observability/log-sanitize.ts +118 -0
  198. package/src/observability/logger.ts +17 -13
  199. package/src/observability/metrics.ts +35 -0
  200. package/src/observability/payload-capture.ts +114 -41
  201. package/src/observability/payload-store.ts +249 -24
  202. package/src/observability/raw-event-writer.ts +118 -0
  203. package/src/observability/runtime-metrics.ts +71 -1
  204. package/src/observability/telemetry-buffer.ts +100 -17
  205. package/src/observability/telemetry-reconciliation.ts +417 -0
  206. package/src/persistence/boot.ts +20 -9
  207. package/src/persistence/db-pg.ts +13 -0
  208. package/src/persistence/db-sqlite.ts +569 -497
  209. package/src/persistence/dialect.ts +3 -2
  210. package/src/persistence/migrate.ts +181 -181
  211. package/src/persistence/schema.ts +1496 -1406
  212. package/src/persistence/shared-cache.ts +5 -1
  213. package/src/persistence/stores/api-key-store.ts +581 -581
  214. package/src/persistence/stores/telemetry-store.ts +124 -63
  215. package/src/protocol/reasoning-text.ts +30 -0
  216. package/src/protocol/registry.ts +2 -0
  217. package/src/protocol/response/chat.ts +33 -7
  218. package/src/protocol/response/codex.ts +14 -2
  219. package/src/protocol/response/messages.ts +5 -2
  220. package/src/providers/authentication/oauth-flow-store.ts +3 -10
  221. package/src/providers/bundled-discovery-providers.ts +122 -122
  222. package/src/providers/default-registry.ts +14 -0
  223. package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
  224. package/src/providers/discovery/models-dev-catalog.ts +354 -354
  225. package/src/providers/discovery/probe-phases.ts +43 -7
  226. package/src/providers/discovery/probing-service.ts +70 -32
  227. package/src/providers/integrations/antigravity/antigravity.ts +9 -2
  228. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +85 -17
  229. package/src/providers/integrations/buddy/buddy-chat-shared.ts +934 -934
  230. package/src/providers/integrations/buddy/buddy-checkin.ts +5 -9
  231. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +3 -7
  232. package/src/providers/integrations/buddy/buddy-quota-shared.ts +3 -1
  233. package/src/providers/integrations/buddy/capabilities.ts +4 -0
  234. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +57 -49
  235. package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -180
  236. package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -206
  237. package/src/providers/integrations/buddy/workbuddy/quota.ts +161 -161
  238. package/src/providers/integrations/claude/claude-quota.ts +2 -2
  239. package/src/providers/integrations/claude/claude-user-profiles.ts +3 -2
  240. package/src/providers/integrations/claude/claude.ts +3 -4
  241. package/src/providers/integrations/cline/cline.ts +94 -8
  242. package/src/providers/integrations/codex/codex.ts +61 -37
  243. package/src/providers/integrations/cursor/cursor-catalog.ts +368 -368
  244. package/src/providers/integrations/cursor/cursor-http2.ts +300 -300
  245. package/src/providers/integrations/cursor/cursor-identity.ts +2 -4
  246. package/src/providers/integrations/cursor/cursor-request.ts +11 -0
  247. package/src/providers/integrations/cursor/cursor.ts +29 -17
  248. package/src/providers/integrations/cursor/protocol/framing.ts +129 -129
  249. package/src/providers/integrations/cursor/protocol/messages.ts +2380 -2380
  250. package/src/providers/integrations/cursor/protocol/struct.ts +305 -305
  251. package/src/providers/integrations/cursor/protocol/tool-canonical.ts +275 -0
  252. package/src/providers/integrations/github/github.ts +3 -1
  253. package/src/providers/integrations/grok/grok-quota.ts +279 -279
  254. package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -73
  255. package/src/providers/integrations/grok/grok-ratelimit.ts +218 -218
  256. package/src/providers/integrations/hermes.ts +7 -1
  257. package/src/providers/integrations/inferhub.ts +201 -73
  258. package/src/providers/integrations/kilo/kilo.ts +152 -152
  259. package/src/providers/integrations/kiro/kiro-machine-id.ts +2 -4
  260. package/src/providers/integrations/opencode/provider.ts +2 -0
  261. package/src/providers/integrations/openrouter/provider.ts +129 -129
  262. package/src/providers/integrations/qoder/qoder-quota.ts +193 -192
  263. package/src/providers/integrations/qoder/qoder-realm.ts +106 -106
  264. package/src/providers/integrations/trae/trae-headers.ts +17 -48
  265. package/src/providers/integrations/trae/trae-oauth.ts +4 -26
  266. package/src/providers/integrations/trae/trae-quota.ts +389 -384
  267. package/src/providers/integrations/trae/trae-realm.ts +15 -2
  268. package/src/providers/integrations/trae/trae.ts +22 -24
  269. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +100 -24
  270. package/src/providers/integrations/zcode/oauth.ts +1 -1
  271. package/src/providers/operations/account-health-service.ts +605 -279
  272. package/src/providers/provider-metadata.ts +12 -1
  273. package/src/providers/provider-model-definition.ts +373 -373
  274. package/src/providers/provider-probe.ts +3 -0
  275. package/src/providers/quota/quota-contracts.ts +3 -7
  276. package/src/providers/search/search-provider.ts +2 -6
  277. package/src/security/admission/contracts.ts +10 -0
  278. package/src/security/admission/in-memory-store.ts +89 -14
  279. package/src/security/admission/lua.ts +191 -182
  280. package/src/security/admission/redis-store.ts +30 -0
  281. package/src/security/admission/service.ts +18 -0
  282. package/src/security/api-key-auth-cache.ts +96 -0
  283. package/src/security/api-key-auth.ts +11 -100
  284. package/src/security/client-router-fingerprint.ts +3 -7
  285. package/src/security/ip-boundary.ts +7 -3
  286. package/src/shared/coerce.ts +67 -0
  287. package/src/shared/hash.ts +6 -0
  288. package/src/shared/time.ts +7 -0
  289. package/src/transport/dispatch/attempt-finalize.ts +26 -138
  290. package/src/transport/dispatch/attempt-loop.ts +103 -81
  291. package/src/transport/dispatch/capture-policy.ts +78 -0
  292. package/src/transport/dispatch/proxy-request.ts +4 -2
  293. package/src/transport/dispatch/streaming/encoder.ts +111 -5
  294. package/src/transport/dispatch/streaming/stream.ts +19 -2
  295. package/src/transport/dispatch/streaming/thinking-loop.ts +300 -0
  296. package/src/transport/failure-policy.ts +27 -0
  297. package/src/transport/guards/abuse-guard.ts +2 -2
  298. package/src/transport/guards/auth-guard.ts +17 -0
  299. package/src/transport/guards/body-policy.ts +3 -2
  300. package/src/transport/guards/concurrency-guard.ts +89 -13
  301. package/src/transport/guards/early-rejection-capture.ts +43 -3
  302. package/src/transport/guards/error-lifecycle.ts +10 -2
  303. package/src/transport/guards/guard-errors.ts +23 -0
  304. package/src/transport/guards/headroom-guard.ts +41 -7
  305. package/src/transport/guards/pipeline.ts +24 -0
  306. package/src/transport/guards/request-context.ts +2 -1
  307. package/src/transport/routing/admission.ts +51 -9
  308. package/src/transport/routing/bounded-lru.ts +49 -0
  309. package/src/transport/routing/candidate-index.ts +88 -0
  310. package/src/transport/routing/reservations.ts +121 -7
  311. package/src/transport/routing/round-robin.ts +10 -22
  312. package/src/transport/routing/route-catalog.ts +26 -13
  313. package/src/transport/routing/route-model.ts +22 -2
  314. package/src/transport/routing/router.ts +53 -31
  315. package/src/transport/surface/chat/encode.ts +2 -3
  316. package/src/transport/surface/chat/parse.ts +2 -1
  317. package/src/transport/surface/dialects.ts +11 -3
  318. package/src/transport/surface/messages/encode.ts +2 -1
  319. package/src/transport/surface/messages/parse.ts +3 -1
  320. package/src/transport/surface/messages/stream.ts +2 -6
  321. package/src/workers/maintenance-lease.ts +200 -0
  322. package/src/workers/scheduler.ts +101 -1
  323. package/dist/dashboard/assets/BannedPage-Brvuj_Eb.js.gz +0 -0
  324. package/dist/dashboard/assets/CliToolDetailPage-CqfXtI3y.js.gz +0 -0
  325. package/dist/dashboard/assets/CliToolsPage-GQeS91bg.js.gz +0 -0
  326. package/dist/dashboard/assets/CombosPage-BDRsTXBB.js.gz +0 -0
  327. package/dist/dashboard/assets/ConsoleLogPage-zP8AFHX0.js.gz +0 -0
  328. package/dist/dashboard/assets/CustomizationPage-BmfrNZ14.js.gz +0 -0
  329. package/dist/dashboard/assets/HealthEventsModal-Dn3M6WZc.js +0 -1
  330. package/dist/dashboard/assets/HealthEventsModal-Dn3M6WZc.js.gz +0 -0
  331. package/dist/dashboard/assets/LoginPage-DoreIgm8.js.gz +0 -0
  332. package/dist/dashboard/assets/ModelPicker-C7pDb41R.js.gz +0 -0
  333. package/dist/dashboard/assets/OverviewPage-CvvD1Ugk.js +0 -1
  334. package/dist/dashboard/assets/OverviewPage-CvvD1Ugk.js.gz +0 -0
  335. package/dist/dashboard/assets/ProviderDetailPage-Clbn1gsW.js +0 -3
  336. package/dist/dashboard/assets/ProviderDetailPage-Clbn1gsW.js.gz +0 -0
  337. package/dist/dashboard/assets/ProvidersPage-BnoPgA7w.js.gz +0 -0
  338. package/dist/dashboard/assets/ProxyPage-Bq_TGgdb.js.gz +0 -0
  339. package/dist/dashboard/assets/QuotaPage-D9DHXvom.js +0 -1
  340. package/dist/dashboard/assets/QuotaPage-D9DHXvom.js.gz +0 -0
  341. package/dist/dashboard/assets/SettingsPage-7xV8WkWL.js +0 -1
  342. package/dist/dashboard/assets/SettingsPage-7xV8WkWL.js.gz +0 -0
  343. package/dist/dashboard/assets/SetupPage-Day3mCOn.js.gz +0 -0
  344. package/dist/dashboard/assets/SortableList-DQxllnB5.js.gz +0 -0
  345. package/dist/dashboard/assets/UsagePage-DXa1oR-4.js.gz +0 -0
  346. package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
  347. package/dist/dashboard/assets/app-CX_Z8cMs.css +0 -2
  348. package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
  349. package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
  350. package/dist/dashboard/assets/badge-CR72dasT.js.gz +0 -0
  351. package/dist/dashboard/assets/banner-CI9_eSfO.js.gz +0 -0
  352. package/dist/dashboard/assets/card-DS7CQEsO.js.gz +0 -0
  353. package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
  354. package/dist/dashboard/assets/clipboard-button-611pSsaX.js.gz +0 -0
  355. package/dist/dashboard/assets/dialog-k37exjNt.js.gz +0 -0
  356. package/dist/dashboard/assets/drawer-CX7N8ucR.js.gz +0 -0
  357. package/dist/dashboard/assets/entry-B9Xvtnfj.js +0 -1
  358. package/dist/dashboard/assets/entry-B9Xvtnfj.js.gz +0 -0
  359. package/dist/dashboard/assets/entry-ByMGkR3C.js +0 -4
  360. package/dist/dashboard/assets/entry-ByMGkR3C.js.gz +0 -0
  361. package/dist/dashboard/assets/entry-CTP8AyiS.js.gz +0 -0
  362. package/dist/dashboard/assets/github-badge-sltk1qUi.js.gz +0 -0
  363. package/dist/dashboard/assets/index-z3zDOSMM.js.gz +0 -0
  364. package/dist/dashboard/assets/inline-BhY2sSnO.js.gz +0 -0
  365. package/dist/dashboard/assets/input-V1umFiBk.js.gz +0 -0
  366. package/dist/dashboard/assets/layout-BBOYycyS.js.gz +0 -0
  367. package/dist/dashboard/assets/list-row-1zzZYM0B.js.gz +0 -0
  368. package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
  369. package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
  370. package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
  371. package/dist/dashboard/assets/numbers-DkfMR5Qs.js.gz +0 -0
  372. package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
  373. package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
  374. package/dist/dashboard/assets/select-C6MMiaio.js.gz +0 -0
  375. package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
  376. package/dist/dashboard/assets/stack-BHTfXdz_.js.gz +0 -0
  377. package/dist/dashboard/assets/state-mPDRlQkl.js.gz +0 -0
  378. package/dist/dashboard/assets/switch-DOmJxg7H.js.gz +0 -0
  379. package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
  380. package/scripts/build/aot.ts +0 -143
  381. package/scripts/build/build.ts +0 -24
  382. package/scripts/build/compile.ts +0 -88
  383. package/scripts/build/restart.ts +0 -120
  384. package/scripts/build/start.ts +0 -25
  385. package/src/observability/event-compaction.ts +0 -317
  386. package/src/observability/event-day-index.ts +0 -440
@@ -1,206 +1,206 @@
1
- // Parsing a proxy list pasted from a file.
2
- //
3
- // Every provider hands out its proxy list in a different shape, and the shape
4
- // is not something an operator should have to convert by hand before this
5
- // gateway will accept it. Three forms cover what actually arrives:
6
- //
7
- // - a JSON array of pool objects (the shape `POST /network/pools` takes);
8
- // - one bare URI per line — `http://user:pass@1.2.3.4:8080`;
9
- // - one `host:port:user:password` row per line, which is what resellers send.
10
- //
11
- // A line that parses to nothing is reported rather than silently dropped: a
12
- // list of 200 where 40 were skipped looks like a successful import and leaves
13
- // the operator short 40 nodes with no reason.
14
- //
15
- // Secrets are handled as little as possible — they are parsed into a
16
- // credential and handed straight to the store, never logged.
17
-
18
- import { POOL_KINDS, POOL_STATUSES } from "./contracts";
19
-
20
- /** One row that parsed, ready to become a pool. */
21
- export interface ParsedProxyRow {
22
- readonly kind: (typeof POOL_KINDS)[number];
23
- readonly endpoint: string;
24
- readonly credential: string | null;
25
- readonly label: string | null;
26
- }
27
-
28
- /** One row that did not parse, with the reason. */
29
- export interface SkippedProxyRow {
30
- readonly line: number;
31
- readonly reason: string;
32
- }
33
-
34
- export interface ParsedProxyFile {
35
- readonly rows: readonly ParsedProxyRow[];
36
- readonly skipped: readonly SkippedProxyRow[];
37
- }
38
-
39
- /** Schemes this gateway can dispatch through, mapped to a pool kind. */
40
- const SCHEME_KINDS: Readonly<Record<string, (typeof POOL_KINDS)[number]>> = {
41
- http: "http",
42
- https: "http",
43
- socks5: "socks5",
44
- socks5h: "socks5",
45
- socks4: "socks5",
46
- };
47
-
48
- /** Kept as a name rather than a literal, because two callers compare against it. */
49
- const DEFAULT_KIND: (typeof POOL_KINDS)[number] = "http";
50
-
51
- function isPoolKind(value: unknown): value is (typeof POOL_KINDS)[number] {
52
- return typeof value === "string" && (POOL_KINDS as readonly string[]).includes(value);
53
- }
54
-
55
- function isPoolStatus(value: unknown): value is (typeof POOL_STATUSES)[number] {
56
- return typeof value === "string" && (POOL_STATUSES as readonly string[]).includes(value);
57
- }
58
-
59
- /**
60
- * The endpoint scheme, lowercased, or `null` when the line carries none.
61
- *
62
- * A line without a scheme is still valid — `host:port` is the most common
63
- * reseller form — so "no scheme" means "assume http", not "unparseable".
64
- */
65
- function schemeOf(text: string): string | null {
66
- const match = /^([a-z][a-z0-9+.-]*):\/\//i.exec(text.trim());
67
- const scheme = match?.[1];
68
- return scheme ? scheme.toLowerCase() : null;
69
- }
70
-
71
- /** Splits `host:port:user:password`, where the password may itself contain `:`. */
72
- function splitColonRow(text: string): {
73
- readonly host: string;
74
- readonly port: string;
75
- readonly user: string | null;
76
- readonly password: string | null;
77
- } | null {
78
- // The password is last and may contain colons, so it is everything after the
79
- // third separator rather than a fourth field.
80
- const parts: readonly string[] = text.trim().split(":");
81
- if (parts.length < 2) return null;
82
- const host = (parts[0] ?? "").trim();
83
- const port = (parts[1] ?? "").trim();
84
- if (!host || !port || !/^\d+$/.test(port)) return null;
85
- const user = parts.length > 2 ? (parts[2] ?? "").trim() : "";
86
- const password = parts.length > 3 ? parts.slice(3).join(":").trim() : "";
87
- return {
88
- host,
89
- port,
90
- user: user !== "" ? user : null,
91
- password: password !== "" ? password : null,
92
- };
93
- }
94
-
95
- /** One bare-URI line, e.g. `http://user:pass@1.2.3.4:8080`. */
96
- function parseUriLine(text: string): ParsedProxyRow | null {
97
- const scheme = schemeOf(text);
98
- if (scheme === null) return null;
99
- const kind = SCHEME_KINDS[scheme] ?? DEFAULT_KIND;
100
- const withoutScheme = text.trim().slice(scheme.length + 3);
101
- const at = withoutScheme.lastIndexOf("@");
102
- const authority = at >= 0 ? withoutScheme.slice(at + 1) : withoutScheme;
103
- const userinfo = at >= 0 ? withoutScheme.slice(0, at) : null;
104
- if (!authority) return null;
105
- const credential = userinfo
106
- ? decodeURIComponent(userinfo)
107
- : null;
108
- const hasPort = /:\d+$/.test(authority);
109
- const endpoint = hasPort ? authority : `${authority}`;
110
- if (!endpoint) return null;
111
- return { kind, endpoint: `${scheme}://${endpoint}`, credential, label: null };
112
- }
113
-
114
- /** One `host:port[:user[:password]]` line. */
115
- function parseColonLine(text: string): ParsedProxyRow | null {
116
- const row = splitColonRow(text);
117
- if (!row) return null;
118
- const credential =
119
- row.user !== null ? (row.password !== null ? `${row.user}:${row.password}` : row.user) : null;
120
- // The store requires a full URL, and a reseller row carries no scheme.
121
- // `http://` is the safe reading: it is the default for a bare row, and an
122
- // operator who means socks writes the scheme.
123
- return {
124
- kind: DEFAULT_KIND,
125
- endpoint: `http://${row.host}:${row.port}`,
126
- credential,
127
- label: null,
128
- };
129
- }
130
-
131
- /**
132
- * Parses one line.
133
- *
134
- * Blank lines and `#` comments are skipped without being reported: they are
135
- * structure, not failed rows, and listing them as skipped would bury the rows
136
- * that actually failed.
137
- */
138
- function parseLine(text: string): ParsedProxyRow | null {
139
- const trimmed = text.trim();
140
- if (trimmed === "" || trimmed.startsWith("#")) return null;
141
- return parseUriLine(trimmed) ?? parseColonLine(trimmed);
142
- }
143
-
144
- /** Parses a JSON array of pool objects. */
145
- function parseJsonArray(value: unknown): ParsedProxyFile | null {
146
- if (!Array.isArray(value)) return null;
147
- const rows: ParsedProxyRow[] = [];
148
- const skipped: SkippedProxyRow[] = [];
149
- value.forEach((entry, index) => {
150
- if (typeof entry !== "object" || entry === null) {
151
- skipped.push({ line: index + 1, reason: "not an object" });
152
- return;
153
- }
154
- const record = entry as Record<string, unknown>;
155
- const endpoint = typeof record["endpoint"] === "string" ? record["endpoint"].trim() : "";
156
- if (!endpoint) {
157
- skipped.push({ line: index + 1, reason: "no endpoint" });
158
- return;
159
- }
160
- rows.push({
161
- kind: isPoolKind(record["kind"]) ? record["kind"] : DEFAULT_KIND,
162
- endpoint,
163
- credential: typeof record["credential"] === "string" ? record["credential"] : null,
164
- label: typeof record["label"] === "string" ? record["label"] : null,
165
- });
166
- });
167
- return { rows, skipped };
168
- }
169
-
170
- /**
171
- * Parses a pasted proxy list.
172
- *
173
- * JSON is tried first, because a whole file that happens to be valid JSON is
174
- * unambiguous; anything else is treated as line-oriented text.
175
- */
176
- export function parseProxyFile(content: string): ParsedProxyFile {
177
- const trimmed = content.trim();
178
- if (trimmed === "") return { rows: [], skipped: [] };
179
- if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
180
- try {
181
- const parsed = JSON.parse(trimmed) as unknown;
182
- if (Array.isArray(parsed)) {
183
- const result = parseJsonArray(parsed);
184
- if (result) return result;
185
- }
186
- // A single object is one pool, which is a legitimate one-line file.
187
- const single = parseJsonArray([parsed]);
188
- if (single && single.rows.length > 0) return single;
189
- } catch {
190
- // Falls through to the line parser: a file that is *nearly* JSON is more
191
- // likely a text list than a corrupt one.
192
- }
193
- }
194
- const rows: ParsedProxyRow[] = [];
195
- const skipped: SkippedProxyRow[] = [];;
196
- trimmed.split(/\r?\n/).forEach((line, index) => {
197
- const parsed = parseLine(line);
198
- if (parsed) rows.push(parsed);
199
- else if (line.trim() !== "" && !line.trim().startsWith("#")) {
200
- skipped.push({ line: index + 1, reason: "not a proxy URI or host:port row" });
201
- }
202
- });
203
- return { rows, skipped };
204
- }
205
-
206
- export { isPoolKind, isPoolStatus };
1
+ // Parsing a proxy list pasted from a file.
2
+ //
3
+ // Every provider hands out its proxy list in a different shape, and the shape
4
+ // is not something an operator should have to convert by hand before this
5
+ // gateway will accept it. Three forms cover what actually arrives:
6
+ //
7
+ // - a JSON array of pool objects (the shape `POST /network/pools` takes);
8
+ // - one bare URI per line — `http://user:pass@1.2.3.4:8080`;
9
+ // - one `host:port:user:password` row per line, which is what resellers send.
10
+ //
11
+ // A line that parses to nothing is reported rather than silently dropped: a
12
+ // list of 200 where 40 were skipped looks like a successful import and leaves
13
+ // the operator short 40 nodes with no reason.
14
+ //
15
+ // Secrets are handled as little as possible — they are parsed into a
16
+ // credential and handed straight to the store, never logged.
17
+
18
+ import { POOL_KINDS, POOL_STATUSES } from "./contracts";
19
+
20
+ /** One row that parsed, ready to become a pool. */
21
+ export interface ParsedProxyRow {
22
+ readonly kind: (typeof POOL_KINDS)[number];
23
+ readonly endpoint: string;
24
+ readonly credential: string | null;
25
+ readonly label: string | null;
26
+ }
27
+
28
+ /** One row that did not parse, with the reason. */
29
+ export interface SkippedProxyRow {
30
+ readonly line: number;
31
+ readonly reason: string;
32
+ }
33
+
34
+ export interface ParsedProxyFile {
35
+ readonly rows: readonly ParsedProxyRow[];
36
+ readonly skipped: readonly SkippedProxyRow[];
37
+ }
38
+
39
+ /** Schemes this gateway can dispatch through, mapped to a pool kind. */
40
+ const SCHEME_KINDS: Readonly<Record<string, (typeof POOL_KINDS)[number]>> = {
41
+ http: "http",
42
+ https: "http",
43
+ socks5: "socks5",
44
+ socks5h: "socks5",
45
+ socks4: "socks5",
46
+ };
47
+
48
+ /** Kept as a name rather than a literal, because two callers compare against it. */
49
+ const DEFAULT_KIND: (typeof POOL_KINDS)[number] = "http";
50
+
51
+ function isPoolKind(value: unknown): value is (typeof POOL_KINDS)[number] {
52
+ return typeof value === "string" && (POOL_KINDS as readonly string[]).includes(value);
53
+ }
54
+
55
+ function isPoolStatus(value: unknown): value is (typeof POOL_STATUSES)[number] {
56
+ return typeof value === "string" && (POOL_STATUSES as readonly string[]).includes(value);
57
+ }
58
+
59
+ /**
60
+ * The endpoint scheme, lowercased, or `null` when the line carries none.
61
+ *
62
+ * A line without a scheme is still valid — `host:port` is the most common
63
+ * reseller form — so "no scheme" means "assume http", not "unparseable".
64
+ */
65
+ function schemeOf(text: string): string | null {
66
+ const match = /^([a-z][a-z0-9+.-]*):\/\//i.exec(text.trim());
67
+ const scheme = match?.[1];
68
+ return scheme ? scheme.toLowerCase() : null;
69
+ }
70
+
71
+ /** Splits `host:port:user:password`, where the password may itself contain `:`. */
72
+ function splitColonRow(text: string): {
73
+ readonly host: string;
74
+ readonly port: string;
75
+ readonly user: string | null;
76
+ readonly password: string | null;
77
+ } | null {
78
+ // The password is last and may contain colons, so it is everything after the
79
+ // third separator rather than a fourth field.
80
+ const parts: readonly string[] = text.trim().split(":");
81
+ if (parts.length < 2) return null;
82
+ const host = (parts[0] ?? "").trim();
83
+ const port = (parts[1] ?? "").trim();
84
+ if (!host || !port || !/^\d+$/.test(port)) return null;
85
+ const user = parts.length > 2 ? (parts[2] ?? "").trim() : "";
86
+ const password = parts.length > 3 ? parts.slice(3).join(":").trim() : "";
87
+ return {
88
+ host,
89
+ port,
90
+ user: user !== "" ? user : null,
91
+ password: password !== "" ? password : null,
92
+ };
93
+ }
94
+
95
+ /** One bare-URI line, e.g. `http://user:pass@1.2.3.4:8080`. */
96
+ function parseUriLine(text: string): ParsedProxyRow | null {
97
+ const scheme = schemeOf(text);
98
+ if (scheme === null) return null;
99
+ const kind = SCHEME_KINDS[scheme] ?? DEFAULT_KIND;
100
+ const withoutScheme = text.trim().slice(scheme.length + 3);
101
+ const at = withoutScheme.lastIndexOf("@");
102
+ const authority = at >= 0 ? withoutScheme.slice(at + 1) : withoutScheme;
103
+ const userinfo = at >= 0 ? withoutScheme.slice(0, at) : null;
104
+ if (!authority) return null;
105
+ const credential = userinfo
106
+ ? decodeURIComponent(userinfo)
107
+ : null;
108
+ const hasPort = /:\d+$/.test(authority);
109
+ const endpoint = hasPort ? authority : `${authority}`;
110
+ if (!endpoint) return null;
111
+ return { kind, endpoint: `${scheme}://${endpoint}`, credential, label: null };
112
+ }
113
+
114
+ /** One `host:port[:user[:password]]` line. */
115
+ function parseColonLine(text: string): ParsedProxyRow | null {
116
+ const row = splitColonRow(text);
117
+ if (!row) return null;
118
+ const credential =
119
+ row.user !== null ? (row.password !== null ? `${row.user}:${row.password}` : row.user) : null;
120
+ // The store requires a full URL, and a reseller row carries no scheme.
121
+ // `http://` is the safe reading: it is the default for a bare row, and an
122
+ // operator who means socks writes the scheme.
123
+ return {
124
+ kind: DEFAULT_KIND,
125
+ endpoint: `http://${row.host}:${row.port}`,
126
+ credential,
127
+ label: null,
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Parses one line.
133
+ *
134
+ * Blank lines and `#` comments are skipped without being reported: they are
135
+ * structure, not failed rows, and listing them as skipped would bury the rows
136
+ * that actually failed.
137
+ */
138
+ function parseLine(text: string): ParsedProxyRow | null {
139
+ const trimmed = text.trim();
140
+ if (trimmed === "" || trimmed.startsWith("#")) return null;
141
+ return parseUriLine(trimmed) ?? parseColonLine(trimmed);
142
+ }
143
+
144
+ /** Parses a JSON array of pool objects. */
145
+ function parseJsonArray(value: unknown): ParsedProxyFile | null {
146
+ if (!Array.isArray(value)) return null;
147
+ const rows: ParsedProxyRow[] = [];
148
+ const skipped: SkippedProxyRow[] = [];
149
+ value.forEach((entry, index) => {
150
+ if (typeof entry !== "object" || entry === null) {
151
+ skipped.push({ line: index + 1, reason: "not an object" });
152
+ return;
153
+ }
154
+ const record = entry as Record<string, unknown>;
155
+ const endpoint = typeof record["endpoint"] === "string" ? record["endpoint"].trim() : "";
156
+ if (!endpoint) {
157
+ skipped.push({ line: index + 1, reason: "no endpoint" });
158
+ return;
159
+ }
160
+ rows.push({
161
+ kind: isPoolKind(record["kind"]) ? record["kind"] : DEFAULT_KIND,
162
+ endpoint,
163
+ credential: typeof record["credential"] === "string" ? record["credential"] : null,
164
+ label: typeof record["label"] === "string" ? record["label"] : null,
165
+ });
166
+ });
167
+ return { rows, skipped };
168
+ }
169
+
170
+ /**
171
+ * Parses a pasted proxy list.
172
+ *
173
+ * JSON is tried first, because a whole file that happens to be valid JSON is
174
+ * unambiguous; anything else is treated as line-oriented text.
175
+ */
176
+ export function parseProxyFile(content: string): ParsedProxyFile {
177
+ const trimmed = content.trim();
178
+ if (trimmed === "") return { rows: [], skipped: [] };
179
+ if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
180
+ try {
181
+ const parsed = JSON.parse(trimmed) as unknown;
182
+ if (Array.isArray(parsed)) {
183
+ const result = parseJsonArray(parsed);
184
+ if (result) return result;
185
+ }
186
+ // A single object is one pool, which is a legitimate one-line file.
187
+ const single = parseJsonArray([parsed]);
188
+ if (single && single.rows.length > 0) return single;
189
+ } catch {
190
+ // Falls through to the line parser: a file that is *nearly* JSON is more
191
+ // likely a text list than a corrupt one.
192
+ }
193
+ }
194
+ const rows: ParsedProxyRow[] = [];
195
+ const skipped: SkippedProxyRow[] = [];;
196
+ trimmed.split(/\r?\n/).forEach((line, index) => {
197
+ const parsed = parseLine(line);
198
+ if (parsed) rows.push(parsed);
199
+ else if (line.trim() !== "" && !line.trim().startsWith("#")) {
200
+ skipped.push({ line: index + 1, reason: "not a proxy URI or host:port row" });
201
+ }
202
+ });
203
+ return { rows, skipped };
204
+ }
205
+
206
+ export { isPoolKind, isPoolStatus };