cartethyia 2.7.8 → 2.8.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 (519) hide show
  1. package/.env.example +3 -3
  2. package/README.md +29 -59
  3. package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolDetailPage-BRWKPQPf.js → CliToolDetailPage-Bqj2aa1T.js} +1 -1
  6. package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.js → CliToolsPage-CXsaOAO2.js} +1 -1
  8. package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
  13. package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js +1 -0
  14. package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
  17. package/dist/dashboard/assets/{InjectSystemPromptPanel-Cv-2Ol_K.js → InjectSystemPromptPanel-BWFLECmz.js} +2 -2
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
  19. package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-CgoPc5B9.js} +1 -1
  20. package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.js → ModelPicker-D9bFQern.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +8 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-emlTuxz6.js → ProxyPage-BwNC_-Cx.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-CctTga2C.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
  37. package/dist/dashboard/assets/{SetupPage-CUZrKs9r.js → SetupPage-febLU4r1.js} +1 -1
  38. package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-jWmUdDpy.js → StudioPage-M6fvR5Wz.js} +1 -1
  40. package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-nyeuRqz9.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
  43. package/dist/dashboard/assets/api-keys-BFOlvrJC.js +1 -0
  44. package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
  45. package/dist/dashboard/assets/app-Dx2QgsNj.css +2 -0
  46. package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
  47. package/dist/dashboard/assets/{assert-CidQVoNo.js → assert-CqTLw5e7.js} +1 -1
  48. package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-DyI_mJfi.js → badge-C83EqLB0.js} +1 -1
  50. package/dist/dashboard/assets/badge-C83EqLB0.js.gz +0 -0
  51. package/dist/dashboard/assets/ban-durations-B58FyRxC.js +1 -0
  52. package/dist/dashboard/assets/ban-durations-B58FyRxC.js.gz +0 -0
  53. package/dist/dashboard/assets/{banner-B-yXtCqb.js → banner-D1xn7pIe.js} +1 -1
  54. package/dist/dashboard/assets/banner-D1xn7pIe.js.gz +0 -0
  55. package/dist/dashboard/assets/brand/bansos_page.webp +0 -0
  56. package/dist/dashboard/assets/brand/denia_bansos.webp +0 -0
  57. package/dist/dashboard/assets/brand/favicon.webp +0 -0
  58. package/dist/dashboard/assets/brand/login_register.webp +0 -0
  59. package/dist/dashboard/assets/brand/login_register_initial.webp +0 -0
  60. package/dist/dashboard/assets/brand/og_bansos.webp +0 -0
  61. package/dist/dashboard/assets/brand/og_image.webp +0 -0
  62. package/dist/dashboard/assets/brand/sidebar_ico.webp +0 -0
  63. package/dist/dashboard/assets/{card-_HwAEqFF.js → card-DU3Lq5Lm.js} +1 -1
  64. package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
  65. package/dist/dashboard/assets/cli-tools-Dw1U_drK.js +1 -0
  66. package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
  67. package/dist/dashboard/assets/{clipboard-button-ByjCk0o9.js → clipboard-button-CEL5Sg_J.js} +1 -1
  68. package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
  69. package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-BjFR5UHy.js} +1 -1
  70. package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
  71. package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-HvBW0AjI.js} +1 -1
  72. package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
  73. package/dist/dashboard/assets/entry-BSu3gFlt.js +4 -0
  74. package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
  75. package/dist/dashboard/assets/{entry-COaC-irN.js → entry-CECVELyy.js} +1 -1
  76. package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
  77. package/dist/dashboard/assets/entry-DYYofQ7W.js +1 -0
  78. package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
  79. package/dist/dashboard/assets/{formatters-DcHJyRiu.js → formatters-DLW4i8zK.js} +1 -1
  80. package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
  81. package/dist/dashboard/assets/{index-CgKmFngv.js → index-1-fCqe64.js} +2 -2
  82. package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
  83. package/dist/dashboard/assets/{inline-B2BirKry.js → inline-DGvU2oLK.js} +1 -1
  84. package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
  85. package/dist/dashboard/assets/{input-D-qV9dKY.js → input-iNnO1X2K.js} +1 -1
  86. package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
  87. package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-0q8UGypb.js} +1 -1
  88. package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
  89. package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-C0w2bQjJ.js} +1 -1
  90. package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
  91. package/dist/dashboard/assets/{live-DGizF06-.js → live-M5aJ_xCa.js} +1 -1
  92. package/dist/dashboard/assets/live-M5aJ_xCa.js.gz +0 -0
  93. package/dist/dashboard/assets/{mutation-GU7XdEjz.js → mutation-BQ0ccH_A.js} +1 -1
  94. package/dist/dashboard/assets/mutation-BQ0ccH_A.js.gz +0 -0
  95. package/dist/dashboard/assets/{network-C9UE-bOr.js → network-BKeQGT3K.js} +1 -1
  96. package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
  97. package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-CivAk3da.js} +1 -1
  98. package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
  99. package/dist/dashboard/assets/{pagination-BAywyMzS.js → pagination-CpzvI5ep.js} +1 -1
  100. package/dist/dashboard/assets/pagination-CpzvI5ep.js.gz +0 -0
  101. package/dist/dashboard/assets/providers/devin.svg +9 -0
  102. package/dist/dashboard/assets/providers/devin.svg.gz +0 -0
  103. package/dist/dashboard/assets/providers/windsurf.svg +10 -0
  104. package/dist/dashboard/assets/providers/windsurf.svg.gz +0 -0
  105. package/dist/dashboard/assets/providers/zro.svg +3 -0
  106. package/dist/dashboard/assets/providers/zro.svg.gz +0 -0
  107. package/dist/dashboard/assets/{query-keys-YSoQOUh6.js → query-keys-IKLh0YFd.js} +1 -1
  108. package/dist/dashboard/assets/query-keys-IKLh0YFd.js.gz +0 -0
  109. package/dist/dashboard/assets/quota-DiPD6UKH.js +1 -0
  110. package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
  111. package/dist/dashboard/assets/routing-BFQX2VBc.js +1 -0
  112. package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
  113. package/dist/dashboard/assets/{select-BCOcv8WK.js → select-CXbck0jG.js} +2 -2
  114. package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
  115. package/dist/dashboard/assets/settings-C_XvMHh9.js +1 -0
  116. package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
  117. package/dist/dashboard/assets/{stack-ByKt3Q1g.js → stack-DggRiCxE.js} +1 -1
  118. package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
  119. package/dist/dashboard/assets/{state-DXbGTx7N.js → state-ClFzfbYp.js} +1 -1
  120. package/dist/dashboard/assets/state-ClFzfbYp.js.gz +0 -0
  121. package/dist/dashboard/assets/story/25817331.webp +0 -0
  122. package/dist/dashboard/assets/story/Shorekeeper.webp +0 -0
  123. package/dist/dashboard/assets/story/cartethyia-god.webp +0 -0
  124. package/dist/dashboard/assets/story/fleurdelys_plus.webp +0 -0
  125. package/dist/dashboard/assets/story/jinhsi-blossom.webp +0 -0
  126. package/dist/dashboard/assets/story/phrolova-a.webp +0 -0
  127. package/dist/dashboard/assets/story/requestdeniawokkjpg.webp +0 -0
  128. package/dist/dashboard/assets/story/wuthering-waves-hiyuki-aemeath-hiyuki-rover.webp +0 -0
  129. package/dist/dashboard/assets/{switch-ZeTsW623.js → switch-CTIFxKqS.js} +1 -1
  130. package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
  131. package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BClG3yWj.js} +1 -1
  132. package/dist/dashboard/assets/useMutation-BClG3yWj.js.gz +0 -0
  133. package/dist/dashboard/console-backgrounds.webp +0 -0
  134. package/dist/dashboard/console-backgrounds_initial.webp +0 -0
  135. package/dist/dashboard/donation/qris-risun-marke.webp +0 -0
  136. package/dist/dashboard/index.html +3 -3
  137. package/dist/dashboard/index.html.gz +0 -0
  138. package/dist/dashboard/precompressed-assets.json +59 -56
  139. package/migrations/full-postgres/0044_add_models_warning_metadata.sql +10 -0
  140. package/migrations/full-postgres/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  141. package/migrations/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -0
  142. package/migrations/lite-sqlite3/0044_add_models_warning_metadata.sql +11 -0
  143. package/migrations/lite-sqlite3/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  144. package/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
  145. package/package.json +1 -1
  146. package/src/boot/app.ts +10 -13
  147. package/src/boot/dependencies.ts +12 -4
  148. package/src/boot/flush.ts +20 -0
  149. package/src/boot/lifecycle.ts +7 -5
  150. package/src/config/cooldowns.ts +1 -1
  151. package/src/config/routing.ts +26 -0
  152. package/src/config/spec-metadata.ts +72 -0
  153. package/src/config/spec.ts +20 -6
  154. package/src/console/api-keys/contracts.ts +12 -1
  155. package/src/console/api-keys/routes.ts +50 -31
  156. package/src/console/auth/account-reset.ts +7 -9
  157. package/src/console/backup/contracts.ts +4 -0
  158. package/src/console/backup/export-endpoint.ts +7 -5
  159. package/src/console/cli-tools/service.ts +6 -12
  160. package/src/console/console-router-guards.ts +23 -16
  161. package/src/console/console-router.ts +7 -0
  162. package/src/console/dashboard-assets.ts +5 -5
  163. package/src/console/domain-registration.ts +27 -2
  164. package/src/console/inferhub/normalize.ts +24 -28
  165. package/src/console/inferhub/types.ts +3 -0
  166. package/src/console/ip-bans/routes.ts +93 -0
  167. package/src/console/observability/store.ts +0 -53
  168. package/src/console/providers/catalog/catalog-projections.ts +5 -2
  169. package/src/console/providers/catalog/contracts.ts +123 -0
  170. package/src/console/providers/catalog/discover-constants.ts +6 -0
  171. package/src/console/providers/catalog/model-operations.ts +122 -10
  172. package/src/console/providers/catalog/provider-operations.ts +116 -8
  173. package/src/console/providers/catalog/public-model-store.ts +0 -0
  174. package/src/console/providers/catalog/routes.ts +65 -4
  175. package/src/console/providers/catalog/store-helpers.ts +30 -1
  176. package/src/console/providers/catalog/store.ts +249 -4
  177. package/src/console/quota/account-quota-global.ts +97 -19
  178. package/src/console/quota/account-quota-shared.ts +49 -0
  179. package/src/console/quota/account-quota-tenant.ts +6 -18
  180. package/src/console/quota/native-identity.ts +2 -0
  181. package/src/console/quota/quota-view.ts +19 -3
  182. package/src/console/routing/pools/operations.ts +31 -46
  183. package/src/console/settings/contracts.ts +18 -0
  184. package/src/console/settings/store.ts +32 -0
  185. package/src/console/shared/errors.ts +33 -6
  186. package/src/console/system-prompt/contracts.ts +0 -1
  187. package/src/main.ts +3 -3
  188. package/src/network/pool/byte-accounting.ts +3 -2
  189. package/src/network/pool/selector.ts +182 -39
  190. package/src/network/pool-cooldown-store.ts +89 -14
  191. package/src/network/pool-health.ts +49 -61
  192. package/src/network/rate-limit-verdict.ts +1 -1
  193. package/src/network/ssrf.ts +6 -6
  194. package/src/observability/event-log.ts +7 -361
  195. package/src/observability/event-query.ts +18 -4
  196. package/src/observability/performance-metrics.ts +9 -0
  197. package/src/observability/runtime-metrics.ts +14 -9
  198. package/src/observability/telemetry-buffer.ts +15 -25
  199. package/src/observability/telemetry-reconciliation.ts +9 -37
  200. package/src/observability/telemetry-row.ts +125 -0
  201. package/src/persistence/db-mode.ts +4 -1
  202. package/src/persistence/dialect.ts +30 -0
  203. package/src/persistence/migrate.ts +6 -3
  204. package/src/persistence/schema.ts +28 -2
  205. package/src/persistence/ttl-cache.ts +20 -2
  206. package/src/protocol/codecs.ts +1 -1
  207. package/src/protocol/dsml.ts +35 -87
  208. package/src/protocol/gateway-error.ts +1 -0
  209. package/src/protocol/markup-stream-parser.ts +195 -0
  210. package/src/protocol/reasoning-text.ts +19 -0
  211. package/src/protocol/reasoning.ts +1 -1
  212. package/src/protocol/request/chat.ts +14 -14
  213. package/src/protocol/request/gemini.ts +8 -2
  214. package/src/protocol/request/messages.ts +4 -5
  215. package/src/protocol/response/chat.ts +12 -20
  216. package/src/protocol/response/gemini.ts +6 -1
  217. package/src/protocol/response/omp-tool-call-markup.ts +43 -86
  218. package/src/protocol/response/responses.ts +18 -2
  219. package/src/protocol/sse-decoder.ts +25 -8
  220. package/src/protocol/stop-reason.ts +23 -1
  221. package/src/protocol/stream-error-frames.ts +13 -3
  222. package/src/protocol/think-split.ts +112 -0
  223. package/src/protocol/translation/dialect/qwen.ts +26 -58
  224. package/src/protocol/translation/quirks.ts +1 -1
  225. package/src/protocol/translation/tool-repair.ts +2 -2
  226. package/src/protocol/upstream/send-claude-messages.ts +15 -4
  227. package/src/protocol/upstream-parsing.ts +1 -1
  228. package/src/providers/adapters/compatible-adapter.ts +78 -17
  229. package/src/providers/authentication/oauth-client.ts +1 -1
  230. package/src/providers/authentication/oauth-device-flow.ts +98 -8
  231. package/src/providers/authentication/oauth-refresh-service.ts +153 -59
  232. package/src/providers/configured-provider.ts +148 -0
  233. package/src/providers/default-registry.ts +117 -27
  234. package/src/providers/discovery/discovery-types.ts +12 -0
  235. package/src/providers/discovery/probe-phases.ts +74 -33
  236. package/src/providers/discovery/probing-service.ts +293 -165
  237. package/src/providers/integrations/alibabacp/alibabacp-models.ts +1 -2
  238. package/src/providers/integrations/{dashscope-models.ts → alibabacp/dashscope-models.ts} +2 -2
  239. package/src/providers/integrations/antigravity/antigravity-protocol.ts +121 -2
  240. package/src/providers/integrations/antigravity/antigravity.ts +13 -2
  241. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +72 -34
  242. package/src/providers/integrations/buddy/buddy-chat-shared.ts +191 -35
  243. package/src/providers/integrations/buddy/buddy-checkin.ts +17 -39
  244. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +2 -2
  245. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +5 -5
  246. package/src/providers/integrations/buddy/buddy-identity.ts +89 -6
  247. package/src/providers/integrations/buddy/buddy-media.ts +4 -5
  248. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +2 -2
  249. package/src/providers/integrations/buddy/buddy-quota-shared.ts +1 -1
  250. package/src/providers/integrations/buddy/buddy-usage-shared.ts +1 -3
  251. package/src/providers/integrations/buddy/capabilities.ts +6 -43
  252. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
  253. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +23 -1
  254. package/src/providers/integrations/buddy/codebuddy/provider.ts +6 -2
  255. package/src/providers/integrations/buddy/codebuddy/shared.ts +38 -27
  256. package/src/providers/integrations/claude/claude-quota.ts +14 -17
  257. package/src/providers/integrations/cline/cline.ts +2 -2
  258. package/src/providers/integrations/commandcode/quota.ts +22 -28
  259. package/src/providers/integrations/cursor/cursor-auth.ts +0 -8
  260. package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
  261. package/src/providers/integrations/cursor/cursor-exec.ts +745 -0
  262. package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
  263. package/src/providers/integrations/cursor/cursor-request.ts +35 -20
  264. package/src/providers/integrations/cursor/cursor-shared.ts +26 -5
  265. package/src/providers/integrations/cursor/cursor.ts +139 -70
  266. package/src/providers/integrations/cursor/protocol/messages.ts +22 -29
  267. package/src/providers/integrations/cursor/protocol/protobuf-schema.ts +37 -0
  268. package/src/providers/integrations/cursor/protocol/tool-canonical.ts +26 -100
  269. package/src/providers/integrations/devin/devin-auth.ts +353 -0
  270. package/src/providers/integrations/devin/devin-catalog.ts +207 -0
  271. package/src/providers/integrations/devin/devin-http.ts +91 -0
  272. package/src/providers/integrations/devin/devin-quota.ts +214 -0
  273. package/src/providers/integrations/devin/devin-shared.ts +111 -0
  274. package/src/providers/integrations/devin/devin.ts +539 -0
  275. package/src/providers/integrations/devin/protocol/devin-proto.ts +558 -0
  276. package/src/providers/integrations/devin/protocol/framing.ts +124 -0
  277. package/src/providers/integrations/factory/factory-oauth.ts +17 -44
  278. package/src/providers/integrations/factory/factory.ts +2 -2
  279. package/src/providers/integrations/freebuff/assets/official-system-manager.txt +88 -0
  280. package/src/providers/integrations/freebuff/assets/official-system-worker.txt +57 -0
  281. package/src/providers/integrations/freebuff/assets/official-tools.json +1070 -0
  282. package/src/providers/integrations/freebuff/freebuff-catalog.ts +120 -0
  283. package/src/providers/integrations/freebuff/freebuff-constants.ts +1233 -0
  284. package/src/providers/integrations/freebuff/freebuff-discovery.ts +108 -0
  285. package/src/providers/integrations/freebuff/freebuff-oauth.ts +240 -0
  286. package/src/providers/integrations/freebuff/freebuff-quota.ts +260 -0
  287. package/src/providers/integrations/freebuff/freebuff-virtual.ts +89 -0
  288. package/src/providers/integrations/freebuff/freebuff.ts +682 -0
  289. package/src/providers/integrations/freepi/freepi-catalog.ts +60 -0
  290. package/src/providers/integrations/freepi/freepi-discovery.ts +82 -0
  291. package/src/providers/integrations/freepi/freepi-headers.ts +21 -0
  292. package/src/providers/integrations/freepi/freepi-oauth.ts +142 -0
  293. package/src/providers/integrations/freepi/freepi-protocol.ts +242 -0
  294. package/src/providers/integrations/freepi/freepi-quota.ts +126 -0
  295. package/src/providers/integrations/freepi/freepi-realm.ts +35 -0
  296. package/src/providers/integrations/freepi/freepi.ts +292 -0
  297. package/src/providers/integrations/gemini.ts +2 -2
  298. package/src/providers/integrations/grok/grok-oauth.ts +48 -45
  299. package/src/providers/integrations/grok/grok-ratelimit-store.ts +10 -17
  300. package/src/providers/integrations/grok/grok-turn-index.ts +11 -16
  301. package/src/providers/integrations/hermes/hermes-oauth.ts +94 -0
  302. package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +13 -21
  303. package/src/providers/integrations/hermes/index.ts +2 -0
  304. package/src/providers/integrations/kiro/kiro-catalog.ts +17 -1
  305. package/src/providers/integrations/kiro/kiro-discovery.ts +10 -12
  306. package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
  307. package/src/providers/integrations/kiro/kiro.ts +15 -19
  308. package/src/providers/integrations/ollamacloud/index.ts +2 -0
  309. package/src/providers/integrations/{ollamacloud-models.ts → ollamacloud/models.ts} +2 -2
  310. package/src/providers/integrations/{ollamacloud-quota.ts → ollamacloud/quota.ts} +3 -3
  311. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  312. package/src/providers/integrations/opencode/opencode-go-quota.ts +2 -8
  313. package/src/providers/integrations/perch/perch-catalog.ts +105 -0
  314. package/src/providers/integrations/perch/perch-credential.ts +25 -0
  315. package/src/providers/integrations/perch/perch-oauth.ts +170 -0
  316. package/src/providers/integrations/perch/perch-protocol.ts +208 -0
  317. package/src/providers/integrations/perch/perch-quota.ts +123 -0
  318. package/src/providers/integrations/perch/perch-realm.ts +82 -0
  319. package/src/providers/integrations/perch/perch-system-prompt.ts +13 -0
  320. package/src/providers/integrations/perch/perch.ts +305 -0
  321. package/src/providers/integrations/perch/saffron-system-prompt.txt +8 -0
  322. package/src/providers/integrations/qoder/chat-proto.ts +328 -328
  323. package/src/providers/integrations/qoder/claim-qoder-credits.ts +19 -60
  324. package/src/providers/integrations/qoder/qoder-adapter.ts +90 -40
  325. package/src/providers/integrations/qoder/qoder-auth.ts +101 -29
  326. package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
  327. package/src/providers/integrations/qoder/qoder-catalog.ts +26 -6
  328. package/src/providers/integrations/qoder/qoder-chat.ts +7 -21
  329. package/src/providers/integrations/qoder/qoder-checkin.ts +6 -6
  330. package/src/providers/integrations/qoder/qoder-grpc.ts +210 -7
  331. package/src/providers/integrations/qoder/qoder-ide.ts +5 -108
  332. package/src/providers/integrations/qoder/qoder-identity-cache.ts +184 -0
  333. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +83 -18
  334. package/src/providers/integrations/qoder/qoder-realm.ts +9 -6
  335. package/src/providers/integrations/qoder/qoder-umid.ts +109 -342
  336. package/src/providers/integrations/qoder/qoder-virtual.ts +33 -1
  337. package/src/providers/integrations/qoder/qoder.ts +3 -7
  338. package/src/providers/integrations/trae/trae-catalog.ts +9 -14
  339. package/src/providers/integrations/trae/trae-headers.ts +36 -11
  340. package/src/providers/integrations/trae/trae-oauth.ts +14 -17
  341. package/src/providers/integrations/trae/trae-protocol.ts +12 -11
  342. package/src/providers/integrations/trae/trae-quota.ts +96 -36
  343. package/src/providers/integrations/trae/trae-realm.ts +74 -15
  344. package/src/providers/integrations/trae/trae.ts +99 -26
  345. package/src/providers/integrations/zcode-start-plan/provider.ts +226 -23
  346. package/src/providers/integrations/zcode-start-plan/quota.ts +19 -2
  347. package/src/providers/integrations/zro/zro.ts +204 -0
  348. package/src/providers/operations/account-error-classification.ts +235 -278
  349. package/src/providers/operations/account-health-service.ts +273 -26
  350. package/src/providers/operations/account-refresh-status.ts +269 -0
  351. package/src/providers/operations/account-tier.ts +66 -0
  352. package/src/providers/operations/client-version-resolver.ts +1 -1
  353. package/src/providers/operations/client-versions.ts +42 -150
  354. package/src/providers/operations/cooldown-rules.ts +56 -0
  355. package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
  356. package/src/providers/operations/provider-catalog-seeder.ts +4 -0
  357. package/src/providers/operations/provider-catalog-service.ts +1 -1
  358. package/src/providers/operations/provider-credential-service.ts +102 -32
  359. package/src/providers/operations/provider-rules/github.ts +48 -0
  360. package/src/providers/operations/provider-rules/grok.ts +33 -0
  361. package/src/providers/operations/provider-rules/inception.ts +49 -0
  362. package/src/providers/operations/provider-rules/index.ts +32 -0
  363. package/src/providers/operations/provider-rules/perch.ts +37 -0
  364. package/src/providers/operations/provider-rules/qoder.ts +70 -0
  365. package/src/providers/operations/provider-rules/trae.ts +81 -0
  366. package/src/providers/provider-contracts.ts +33 -2
  367. package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
  368. package/src/providers/provider-metadata.ts +99 -14
  369. package/src/providers/provider-model-definition.ts +36 -0
  370. package/src/providers/provider-probe.ts +15 -1
  371. package/src/providers/provider-registry.ts +42 -33
  372. package/src/providers/quota/quota-limit-windows.ts +16 -5
  373. package/src/providers/quota/quota-support.ts +1 -1
  374. package/src/providers/quota/quota-window-parser.ts +90 -18
  375. package/src/providers/quota/refresh.ts +96 -18
  376. package/src/security/abuse.ts +71 -0
  377. package/src/security/api-key-auth-cache.ts +1 -1
  378. package/src/security/api-key-auth.ts +6 -2
  379. package/src/transport/dispatch/attempt-finalize.ts +14 -13
  380. package/src/transport/dispatch/attempt-loop.ts +71 -17
  381. package/src/transport/dispatch/generation.ts +2 -8
  382. package/src/transport/dispatch/native-shared.ts +25 -0
  383. package/src/transport/dispatch/proxy-request.ts +9 -46
  384. package/src/transport/dispatch/responses-compact.ts +2 -8
  385. package/src/transport/dispatch/streaming/finalize.ts +9 -6
  386. package/src/transport/dispatch/streaming/stream.ts +9 -36
  387. package/src/transport/dispatch/systemone.ts +2 -8
  388. package/src/transport/dispatch/token-count.ts +2 -6
  389. package/src/transport/dispatch/upstream.ts +67 -1
  390. package/src/transport/drain-endpoint.ts +31 -4
  391. package/src/transport/failure-policy.ts +129 -37
  392. package/src/transport/guards/body-policy.ts +7 -1
  393. package/src/transport/guards/error-lifecycle.ts +13 -1
  394. package/src/transport/guards/request-context.ts +22 -14
  395. package/src/transport/request/preparer.ts +81 -71
  396. package/src/transport/routing/bounded-lru.ts +14 -0
  397. package/src/transport/routing/drain-reserve.ts +129 -0
  398. package/src/transport/routing/plan-gate.ts +7 -1
  399. package/src/transport/routing/round-robin.ts +60 -0
  400. package/src/transport/routing/route-catalog.ts +123 -111
  401. package/src/transport/routing/route-model.ts +51 -9
  402. package/src/transport/routing/router.ts +418 -33
  403. package/src/transport/surface/adapters.ts +11 -2
  404. package/src/transport/surface/chat/adapter.ts +2 -2
  405. package/src/transport/surface/chat/encode.ts +47 -4
  406. package/src/transport/surface/chat/parse.ts +2 -12
  407. package/src/transport/surface/completion.ts +44 -33
  408. package/src/transport/surface/content-parts.ts +21 -0
  409. package/src/transport/surface/messages/adapter.ts +9 -2
  410. package/src/transport/surface/messages/encode.ts +11 -3
  411. package/src/transport/surface/messages/parse.ts +2 -1
  412. package/src/transport/surface/messages/stream.ts +33 -17
  413. package/src/transport/surface/responses/encode.ts +34 -6
  414. package/src/transport/surface/responses/parse.ts +3 -11
  415. package/src/workers/daily-checkin.ts +1 -1
  416. package/src/workers/quota-refresh-sweep.ts +1 -1
  417. package/src/workers/scheduler.ts +22 -4
  418. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js +0 -1
  419. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js.gz +0 -0
  420. package/dist/dashboard/assets/CliToolDetailPage-BRWKPQPf.js.gz +0 -0
  421. package/dist/dashboard/assets/CliToolsPage-BP-Y5iHN.js.gz +0 -0
  422. package/dist/dashboard/assets/CombosPage-B7JHozVa.js +0 -1
  423. package/dist/dashboard/assets/CombosPage-B7JHozVa.js.gz +0 -0
  424. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js +0 -1
  425. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js.gz +0 -0
  426. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js +0 -1
  427. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js.gz +0 -0
  428. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js +0 -1
  429. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js.gz +0 -0
  430. package/dist/dashboard/assets/InjectSystemPromptPanel-Cv-2Ol_K.js.gz +0 -0
  431. package/dist/dashboard/assets/LoginPage-bKfz_b1K.js.gz +0 -0
  432. package/dist/dashboard/assets/ModelPicker-B-WQxx_H.js.gz +0 -0
  433. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js +0 -1
  434. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js.gz +0 -0
  435. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js +0 -7
  436. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js.gz +0 -0
  437. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +0 -1
  438. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  439. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js +0 -1
  440. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js.gz +0 -0
  441. package/dist/dashboard/assets/ProxyPage-emlTuxz6.js.gz +0 -0
  442. package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js +0 -1
  443. package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js.gz +0 -0
  444. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js +0 -1
  445. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js.gz +0 -0
  446. package/dist/dashboard/assets/SetupPage-CUZrKs9r.js.gz +0 -0
  447. package/dist/dashboard/assets/StudioPage-jWmUdDpy.js.gz +0 -0
  448. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js +0 -56
  449. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js.gz +0 -0
  450. package/dist/dashboard/assets/api-keys-KvX83mRe.js +0 -1
  451. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  452. package/dist/dashboard/assets/app-BEsU8hKs.css +0 -2
  453. package/dist/dashboard/assets/app-BEsU8hKs.css.gz +0 -0
  454. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  455. package/dist/dashboard/assets/badge-DyI_mJfi.js.gz +0 -0
  456. package/dist/dashboard/assets/banner-B-yXtCqb.js.gz +0 -0
  457. package/dist/dashboard/assets/brand/bansos_page.png +0 -0
  458. package/dist/dashboard/assets/brand/denia_bansos.jpg +0 -0
  459. package/dist/dashboard/assets/brand/favicon.jpg +0 -0
  460. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  461. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  462. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  463. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  464. package/dist/dashboard/assets/brand/sidebar_ico.jpg +0 -0
  465. package/dist/dashboard/assets/card-_HwAEqFF.js.gz +0 -0
  466. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +0 -1
  467. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  468. package/dist/dashboard/assets/clipboard-button-ByjCk0o9.js.gz +0 -0
  469. package/dist/dashboard/assets/dialog-BZhOlfz-.js.gz +0 -0
  470. package/dist/dashboard/assets/drawer-BADqIRYP.js.gz +0 -0
  471. package/dist/dashboard/assets/entry-COaC-irN.js.gz +0 -0
  472. package/dist/dashboard/assets/entry-Ca7LSCet.js +0 -4
  473. package/dist/dashboard/assets/entry-Ca7LSCet.js.gz +0 -0
  474. package/dist/dashboard/assets/entry-PDd27uCd.js +0 -1
  475. package/dist/dashboard/assets/entry-PDd27uCd.js.gz +0 -0
  476. package/dist/dashboard/assets/formatters-DcHJyRiu.js.gz +0 -0
  477. package/dist/dashboard/assets/index-CgKmFngv.js.gz +0 -0
  478. package/dist/dashboard/assets/inline-B2BirKry.js.gz +0 -0
  479. package/dist/dashboard/assets/input-D-qV9dKY.js.gz +0 -0
  480. package/dist/dashboard/assets/layout-B7tVWhtV.js.gz +0 -0
  481. package/dist/dashboard/assets/list-row-CtkGE3bF.js.gz +0 -0
  482. package/dist/dashboard/assets/live-DGizF06-.js.gz +0 -0
  483. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +0 -1
  484. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  485. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  486. package/dist/dashboard/assets/network-C9UE-bOr.js.gz +0 -0
  487. package/dist/dashboard/assets/numbers-BnAd22a_.js.gz +0 -0
  488. package/dist/dashboard/assets/pagination-BAywyMzS.js.gz +0 -0
  489. package/dist/dashboard/assets/query-keys-YSoQOUh6.js.gz +0 -0
  490. package/dist/dashboard/assets/quota-DJ1QlAOq.js +0 -1
  491. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  492. package/dist/dashboard/assets/routing-fnCsxgNO.js +0 -1
  493. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  494. package/dist/dashboard/assets/select-BCOcv8WK.js.gz +0 -0
  495. package/dist/dashboard/assets/settings-BxslsErn.js +0 -1
  496. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  497. package/dist/dashboard/assets/stack-ByKt3Q1g.js.gz +0 -0
  498. package/dist/dashboard/assets/state-DXbGTx7N.js.gz +0 -0
  499. package/dist/dashboard/assets/switch-ZeTsW623.js.gz +0 -0
  500. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  501. package/dist/dashboard/console-backgrounds.jpg +0 -0
  502. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  503. package/dist/dashboard/donation/qris-risun-marke.png +0 -0
  504. package/src/providers/bundled-discovery-providers.ts +0 -98
  505. package/src/providers/configured-openai-providers.ts +0 -120
  506. package/src/providers/integrations/buddy/turing-device-token.ts +0 -262
  507. package/src/providers/integrations/buddy/workbuddy/cn-oauth.ts +0 -30
  508. package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +0 -58
  509. package/src/providers/integrations/buddy/workbuddy/oauth.ts +0 -31
  510. package/src/providers/integrations/buddy/workbuddy/provider.ts +0 -209
  511. package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -161
  512. package/src/providers/integrations/buddy/workbuddy/shared.ts +0 -285
  513. package/src/providers/operations/model-discovery-cache.ts +0 -34
  514. package/src/providers/operations/probe-fetch.ts +0 -15
  515. package/src/providers/operations/provider-version-cache.ts +0 -30
  516. package/src/providers/provider-capabilities.ts +0 -33
  517. package/src/providers/provider-credential-envelope.ts +0 -43
  518. /package/dist/dashboard/assets/{app-CTxoqyYB.js → app-DIXLjojg.js} +0 -0
  519. /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-DIXLjojg.js.gz} +0 -0
@@ -0,0 +1,170 @@
1
+ // Perch OAuth client — Supabase PKCE browser flow + refresh.
2
+ //
3
+ // Perch authentication is Supabase GoTrue (project
4
+ // zlfuvsfjtgsdtqcaykia.supabase.co) with Google/GitHub providers. The web
5
+ // client ships only the publishable key (`sb_publishable_...`), which is
6
+ // accepted as `apikey` for authorize/token. The authorization code exchange
7
+ // is GoTrue-specific: `grant_type=pkce` with `auth_code` as the field name
8
+ // (NOT `authorization_code`/`code` — both are rejected with
9
+ // `unsupported_grant_type` / `validation_failed`), plus `code_verifier`.
10
+ //
11
+ // The browser flow is a loopback redirect (`http://127.0.0.1:<port>/callback`)
12
+ // exactly like the desktop app's own sign-in controller.
13
+ //
14
+ // After exchange, the official CLI stamps `user_metadata.signup_source=cli`
15
+ // (used by the server for surface entitlement); we repeat that so the account
16
+ // is recognized as a CLI surface.
17
+
18
+ import { OAuthClient } from "../../authentication/oauth-client";
19
+ import type { OAuthAuthorizeRequest, OAuthExchangeResult } from "../../authentication/oauth-flow-store";
20
+ import { postJsonTokenRequest } from "../../authentication/oauth-flow-store";
21
+ import type { OAuthTokenRefreshResult } from "../../authentication/oauth-refresh-service";
22
+ import {
23
+ PERCH_APP_URL,
24
+ PERCH_SUPABASE_HOST,
25
+ PERCH_SUPABASE_PUBLISHABLE_KEY,
26
+ } from "./perch-realm";
27
+
28
+ const PERCH_SUPABASE_URL = `https://${PERCH_SUPABASE_HOST}`;
29
+ const PERCH_AUTHORIZE_PATH = "/auth/v1/authorize";
30
+ const PERCH_TOKEN_PATH = "/auth/v1/token";
31
+ const PERCH_GRANT_PKCE = "pkce";
32
+ const PERCH_GRANT_REFRESH = "refresh_token";
33
+ const PERCH_APISEY_COOKIE = "apikey";
34
+
35
+ export class PerchOAuthClient extends OAuthClient {
36
+ readonly supportsDeviceCode = false;
37
+ override readonly supportsBrowserCode = true;
38
+ protected readonly providerLabel = "Perch";
39
+ protected readonly clientId = PERCH_SUPABASE_PUBLISHABLE_KEY;
40
+ protected readonly tokenUrl = `${PERCH_SUPABASE_URL}${PERCH_TOKEN_PATH}`;
41
+ protected override readonly authorizeUrl = `${PERCH_SUPABASE_URL}${PERCH_AUTHORIZE_PATH}`;
42
+ // Google validates scopes for accounts that never approved this client; a
43
+ // missing scope answers `Error 400: invalid_request / Missing required
44
+ // parameter: scope` (flowName=GeneralOAuthFlow). These are the GoTrue
45
+ // default Google scopes, sent explicitly.
46
+ protected readonly scopes = "openid email profile";
47
+
48
+ /** Loopback callback path (the desktop app uses the same shape). */
49
+ override readonly browserRedirectUri = "http://127.0.0.1:1455/callback";
50
+
51
+ /**
52
+ * GoTrue token responses carry the signed-in user (`user.email`). Without
53
+ * this the exchange falls back to the provider id as the account label and
54
+ * every login of every Perch account collides on the same identity
55
+ * fingerprint — the second login replaces the first instead of adding a new
56
+ * row.
57
+ */
58
+ protected override extractAccountLabel(data: unknown): string | undefined {
59
+ const body = data as { user?: { email?: unknown } } | null | undefined;
60
+ const email = typeof body?.user?.email === "string" ? body.user.email.trim() : "";
61
+ return email.length > 0 ? email : undefined;
62
+ }
63
+
64
+ protected override extraAuthorizeParams(): Record<string, string> | undefined {
65
+ return {
66
+ provider: "google",
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Supabase's authorize endpoint speaks `redirect_to`, not the RFC-style
72
+ * `redirect_uri` the shared PKCE helper emits — a call redirected to a
73
+ * `redirect_uri` value lands on a Supabase error page.
74
+ *
75
+ * GoTrue does NOT echo the `state` parameter on the callback — the redirect
76
+ * back to `redirect_to` carries only `?code=...` (verified live). The
77
+ * console's callback listener correlates by `state` and fails closed when
78
+ * state is absent, so the state is embedded in `redirect_to` itself:
79
+ * `http://127.0.0.1:1455/callback?state=<state>`. Supabase preserves the
80
+ * query it was given and appends `code`, so the callback carries both.
81
+ */
82
+ override buildAuthorizeUrl(request: OAuthAuthorizeRequest): string {
83
+ const redirectTo = new URL(request.redirectUri);
84
+ redirectTo.searchParams.set("state", request.state);
85
+ const params = new URLSearchParams({
86
+ provider: "google",
87
+ client_id: this.clientId,
88
+ response_type: "code",
89
+ redirect_to: redirectTo.toString(),
90
+ scope: this.scopes,
91
+ state: request.state,
92
+ code_challenge: request.codeChallenge,
93
+ code_challenge_method: "S256",
94
+ });
95
+ return `${this.authorizeUrl}?${params.toString()}`;
96
+ }
97
+
98
+ /**
99
+ * GoTrue exchange: `grant_type=pkce` (query) with `auth_code` (body) —
100
+ * not the RFC-style `authorization_code`/`code` pair the generic base
101
+ * class posts. GoTrue v2.197.0 accepts **only JSON** bodies here (a form
102
+ * body answers `bad_json`), so this overrides the form-encoded generic
103
+ * helper. The apikey goes in the header (not as `client_id`).
104
+ *
105
+ * The console's `exchangeCode` passes the flow state; it must be replayed
106
+ * in `redirect_to` exactly as `buildAuthorizeUrl` embedded it, because
107
+ * GoTrue compares the exchange redirect against the authorize one.
108
+ */
109
+ override async exchangeCode(
110
+ code: string,
111
+ codeVerifier: string,
112
+ redirectUri: string,
113
+ state?: string,
114
+ ): Promise<OAuthExchangeResult> {
115
+ const redirectTo = new URL(redirectUri);
116
+ if (state) redirectTo.searchParams.set("state", state);
117
+ const response = await postJsonTokenRequest({
118
+ url: `${this.tokenUrl}?grant_type=${PERCH_GRANT_PKCE}`,
119
+ fetchFn: this.fetchFn,
120
+ body: {
121
+ auth_code: code,
122
+ redirect_to: redirectTo.toString(),
123
+ code_verifier: codeVerifier,
124
+ },
125
+ headers: {
126
+ accept: "application/json",
127
+ [PERCH_APISEY_COOKIE]: PERCH_SUPABASE_PUBLISHABLE_KEY,
128
+ },
129
+ label: "Perch token exchange",
130
+ });
131
+ const result = this.toExchangeResult(this.parseTokenResponse(response));
132
+ await this.#stampCliSignup(result.access).catch(() => undefined);
133
+ return result;
134
+ }
135
+
136
+ override async refresh(refreshToken: string, signal?: AbortSignal): Promise<OAuthTokenRefreshResult> {
137
+ const response = await postJsonTokenRequest({
138
+ url: `${this.tokenUrl}?grant_type=${PERCH_GRANT_REFRESH}`,
139
+ fetchFn: this.fetchFn,
140
+ body: {
141
+ refresh_token: refreshToken,
142
+ },
143
+ headers: {
144
+ accept: "application/json",
145
+ [PERCH_APISEY_COOKIE]: PERCH_SUPABASE_PUBLISHABLE_KEY,
146
+ },
147
+ ...(signal === undefined ? {} : { signal }),
148
+ label: "Perch token refresh",
149
+ });
150
+ return this.toRefreshResult(this.parseTokenResponse(response));
151
+ }
152
+
153
+ /** The CLI stamps `signup_source=cli` after login; mirror it. */
154
+ async #stampCliSignup(access: string): Promise<void> {
155
+ await this.fetchFn(`${PERCH_SUPABASE_URL}/auth/v1/user`, {
156
+ method: "PUT",
157
+ headers: {
158
+ apikey: PERCH_SUPABASE_PUBLISHABLE_KEY,
159
+ Authorization: `Bearer ${access}`,
160
+ "content-type": "application/json",
161
+ },
162
+ body: JSON.stringify({ data: { signup_source: "cli" } }),
163
+ });
164
+ }
165
+
166
+ /** The app origin the CLI writes into its session file. */
167
+ readonly appUrl = PERCH_APP_URL;
168
+ }
169
+
170
+ export const perchOAuthClient = new PerchOAuthClient();
@@ -0,0 +1,208 @@
1
+ // Perch wire protocol (pure HTTP, reverse-engineered from the official CLI).
2
+ //
3
+ // The hosted inference surface is `POST /api/perch-terminal/model-call`. The
4
+ // server surface-gates the call: it only accepts requests that carry the full
5
+ // CLI turn envelope — a fresh turn ticket minted with `{surface:"cli"}`,
6
+ // the Roost tier choice (`roostModelChoice`), the account attribution, and
7
+ // the Saffron system-prompt prefix (the server fingerprints it). Without that
8
+ // prompt the upstream answers `403 perch_surface_required` even with a valid
9
+ // token and ticket (verified live 2026-11-08).
10
+ //
11
+ // The stream is plain SSE with JSON frames:
12
+ // data: {"type":"keepalive"}
13
+ // data: {"type":"answer_delta","text":"..."}
14
+ // data: {"type":"done","ok":true,"text":"...","model":"...","usage":{...}}
15
+ // data: {"type":"model_call_failed",...}
16
+ // data: {"type":"error","message":"..."}
17
+
18
+ import type { CanonicalRequest } from "../../../protocol/canonical-model";
19
+ // The prompt lives in a plain TS module: Bun's standalone binary has no
20
+ // `saffron-system-prompt.txt` asset on disk, so a readFileSync()-backed
21
+ // constant silently became a two-line stub in the compiled binary and the
22
+ // upstream answered `403 perch_surface_required`.
23
+ export { PERCH_SAFFRON_SYSTEM_PROMPT } from "./perch-system-prompt";
24
+ import { PERCH_SAFFRON_SYSTEM_PROMPT } from "./perch-system-prompt";
25
+ import { PERCH_STARTER_PINS } from "./perch-realm";
26
+
27
+ /** Default hosted model id when the account endpoint is unreachable. */
28
+ export const PERCH_DEFAULT_CHAT_MODEL = "wandb-kimi-k2-6";
29
+
30
+ /** The CLI user-agent the upstream recognizes as a CLI surface. */
31
+ export const PERCH_CLI_USER_AGENT = "perchai-cli/unknown";
32
+
33
+ export interface PerchTurnContext {
34
+ readonly userId: string | null;
35
+ readonly workspaceId: string | null;
36
+ readonly orgId: string | null;
37
+ readonly planCode: string | null;
38
+ readonly runId: string;
39
+ /**
40
+ * Starter-pool pin (see `PERCH_STARTER_PINS`): when present the request
41
+ * carries the backing model id as `manualModelOptionId` +
42
+ * `preferredModelId` with `strictManual: true`, so the server actually
43
+ * serves the pinned model instead of the Roost default.
44
+ */
45
+ readonly pin?: string | undefined;
46
+ /** Roost tier for `roostModelChoice` (`standard` | `standard_max`). */
47
+ readonly roostTier?: string | undefined;
48
+ }
49
+
50
+ /**
51
+ * Builds the model-call body for one turn. The Saffron system-prompt prefix
52
+ * opens `request.messages` (required) and the caller's conversation follows.
53
+ * The tier is selected through `roostModelChoice` (`standard` | `standard 1m`).
54
+ */
55
+ export function buildPerchModelCallBody(
56
+ request: CanonicalRequest,
57
+ tier: string,
58
+ ctx: PerchTurnContext,
59
+ preferredModelId = PERCH_DEFAULT_CHAT_MODEL,
60
+ ): Record<string, unknown> {
61
+ const messages: Record<string, unknown>[] = [];
62
+ const systemParts = [
63
+ ...(request.system ?? []),
64
+ ...(request.instructions ?? []),
65
+ ];
66
+ const systemText = systemParts
67
+ .map((part) => (part.kind === "text" ? part.text : ""))
68
+ .filter((t) => t.trim().length > 0)
69
+ .join("\n\n");
70
+ // The upstream fingerprints the *prefix* of the Saffron prompt, not the
71
+ // whole string (both the 28k original and this 432-char prefix answer 200,
72
+ // while a plain assistant placeholder answers 403). Keep Saffron as the
73
+ // opener unconditionally: a caller-supplied system turn is appended, never
74
+ // substituted, so live traffic cannot strip the surface identity. The probe
75
+ // path injects exactly this prefix via `systemPrompt()`, hence `startsWith`
76
+ // is a no-op there and the wire body stays byte-identical to the verified one.
77
+ const systemContent = systemText.startsWith(PERCH_SAFFRON_SYSTEM_PROMPT)
78
+ ? systemText
79
+ : systemText
80
+ ? `${PERCH_SAFFRON_SYSTEM_PROMPT}\n\n${systemText}`
81
+ : PERCH_SAFFRON_SYSTEM_PROMPT;
82
+ messages.push({ role: "system", content: systemContent });
83
+ for (const message of request.messages) {
84
+ const text = message.content
85
+ .map((part) => (part.kind === "text" ? part.text : ""))
86
+ .filter((t) => t.trim().length > 0)
87
+ .join("\n");
88
+ if (!text) continue;
89
+ messages.push({ role: message.role, content: text });
90
+ }
91
+ const maxTokens =
92
+ request.generation_controls.max_tokens ??
93
+ request.generation_controls.max_output_tokens ??
94
+ request.generation_controls.max_completion_tokens ??
95
+ 32000;
96
+ const temperature = request.generation_controls.temperature ?? 0.6;
97
+ const runId = ctx.runId;
98
+ const pin = ctx.pin && ctx.pin !== "standard-max" ? ctx.pin : undefined;
99
+ const pinModel = pin ? PERCH_STARTER_PINS[pin]?.preferredModelId : undefined;
100
+ return {
101
+ request: {
102
+ messages,
103
+ temperature,
104
+ maxTokens: Number(maxTokens) || 32000,
105
+ },
106
+ runId,
107
+ lane: "chat",
108
+ strictManual: pinModel !== undefined,
109
+ preferredModelId: pinModel ?? preferredModelId,
110
+ avoidModelIds: [],
111
+ attribution: {
112
+ userId: ctx.userId,
113
+ workspaceId: ctx.workspaceId,
114
+ runId,
115
+ lane: "chat",
116
+ source: "cli",
117
+ billingMultiplier: null,
118
+ },
119
+ clientSurface: "cli",
120
+ resendAfterOutput: true,
121
+ roostModelChoice: ctx.roostTier ?? tier,
122
+ roostReasoning: true,
123
+ effort: { level: "high", orchestration: false },
124
+ ...(pinModel ? { manualModelOptionId: pinModel } : {}),
125
+ };
126
+ }
127
+
128
+ export interface PerchModelCallDone {
129
+ readonly ok: boolean;
130
+ readonly text: string;
131
+ readonly model: string;
132
+ readonly provider: string;
133
+ readonly usage?: Record<string, unknown>;
134
+ readonly error?: string;
135
+ readonly errorCode?: string;
136
+ }
137
+
138
+ export interface PerchSseEvent {
139
+ readonly type: string;
140
+ readonly data: Record<string, unknown>;
141
+ }
142
+
143
+ /** Parses an SSE body into JSON frames. */
144
+ export function parsePerchSse(text: string): PerchSseEvent[] {
145
+ const events: PerchSseEvent[] = [];
146
+ for (const rawLine of text.split("\n")) {
147
+ const line = rawLine.replace(/\r$/, "");
148
+ if (!line.startsWith("data:")) continue;
149
+ const payload = line.slice(5).trim();
150
+ if (!payload) continue;
151
+ try {
152
+ const data = JSON.parse(payload) as Record<string, unknown>;
153
+ if (typeof data["type"] === "string") {
154
+ events.push({ type: data["type"] as string, data });
155
+ }
156
+ } catch {
157
+ // Ignore non-JSON frames.
158
+ }
159
+ }
160
+ return events;
161
+ }
162
+
163
+ /** Extracts the final result from a completed stream. */
164
+ export function summarizePerchStream(events: readonly PerchSseEvent[]): PerchModelCallDone {
165
+ let text = "";
166
+ let ok = false;
167
+ let model = "";
168
+ let provider = "";
169
+ let usage: Record<string, unknown> | undefined;
170
+ let error: string | undefined;
171
+ let errorCode: string | undefined;
172
+
173
+ for (const event of events) {
174
+ const d = event.data;
175
+ if (event.type === "answer_delta" && typeof d["text"] === "string") {
176
+ text += d["text"];
177
+ } else if (event.type === "reasoning_delta" && typeof d["text"] === "string") {
178
+ // Reasoning text is not part of the visible answer.
179
+ void d;
180
+ } else if (event.type === "done") {
181
+ ok = d["ok"] === true;
182
+ if (ok) {
183
+ if (typeof d["text"] === "string") text = d["text"];
184
+ if (typeof d["model"] === "string") model = d["model"];
185
+ if (typeof d["provider"] === "string") provider = d["provider"];
186
+ const usageRaw = d["usage"];
187
+ if (usageRaw && typeof usageRaw === "object") {
188
+ usage = usageRaw as Record<string, unknown>;
189
+ }
190
+ } else {
191
+ if (typeof d["error"] === "string") error = d["error"];
192
+ if (typeof d["errorCode"] === "string") errorCode = d["errorCode"];
193
+ }
194
+ } else if (event.type === "model_call_failed" || event.type === "error") {
195
+ if (error === undefined && typeof d["error"] === "string") error = d["error"];
196
+ if (errorCode === undefined && typeof d["errorCode"] === "string") errorCode = d["errorCode"];
197
+ if (errorCode === undefined && typeof d["code"] === "string") errorCode = d["code"] as string;
198
+ }
199
+ }
200
+ return { ok: ok || text.length > 0, text, model, provider, ...(usage ? { usage } : {}), ...(error ? { error } : {}), ...(errorCode ? { errorCode } : {}) };
201
+ }
202
+
203
+ /** Renders a canonical message list into a debug label (for capture). */
204
+ export function summarizeMessages(request: CanonicalRequest): string {
205
+ return request.messages
206
+ .map((m) => `${m.role}:${m.content.map((p) => (p.kind === "text" ? p.text.length : 0)).reduce((a, b) => a + b, 0)}`)
207
+ .join("|");
208
+ }
@@ -0,0 +1,123 @@
1
+ // Perch quota collector.
2
+ //
3
+ // Reads the account's plan/allowance surface from the hosted API
4
+ // (`GET /api/perchai/account` with the stored access token). The account
5
+ // endpoint reports `planCode`, `planName`, `membershipRole`, usage meters
6
+ // (`usageMeter.monthlyUsd`, `roostRolling`), and `tierSelectionRequired`.
7
+ // Reads are Bearer-authenticated; never throws — a failed read returns a
8
+ // null result so the router treats it as unknown rather than dead.
9
+
10
+ import type { FetchLike, ProviderQuotaResult, ProviderQuotaWindow } from "../../quota/quota-contracts";
11
+ import { cleanError, percentWindow, text } from "../../quota/quota-contracts";
12
+ import type { QuotaCollectionContext } from "../../quota/quota-support";
13
+ import { PERCH_APP_URL } from "./perch-realm";
14
+ import { parsePerchCredentialToken } from "./perch-credential";
15
+
16
+ async function readAccount(
17
+ baseUrl: string,
18
+ token: string,
19
+ fetcher: FetchLike,
20
+ ): Promise<Record<string, unknown> | null> {
21
+ if (!token) return null;
22
+ return fetcher(`${baseUrl}/api/perchai/account`, {
23
+ method: "GET",
24
+ headers: {
25
+ accept: "application/json",
26
+ authorization: `Bearer ${token}`,
27
+ },
28
+ signal: AbortSignal.timeout(15_000),
29
+ })
30
+ .then(async (res) => {
31
+ if (!res.ok) return null;
32
+ try {
33
+ return (await res.json()) as Record<string, unknown>;
34
+ } catch {
35
+ return null;
36
+ }
37
+ })
38
+ .catch(() => null);
39
+ }
40
+
41
+ const USD_WINDOW_KIND = "usd_monthly";
42
+
43
+ /** Reads the account quota and returns a normalized result. */
44
+ export async function fetchPerchQuota(
45
+ credential: string,
46
+ fetcher: FetchLike,
47
+ _context?: QuotaCollectionContext,
48
+ ): Promise<ProviderQuotaResult> {
49
+ try {
50
+ const token = parsePerchCredentialToken(credential) ?? "";
51
+ const account = await readAccount(PERCH_APP_URL, token, fetcher);
52
+ if (!account) {
53
+ return { source: "perch", plan: null, windows: [], error: "Perch account endpoint unavailable" };
54
+ }
55
+ const session = account["session"] as Record<string, unknown> | undefined;
56
+ const plan = session && typeof session["planCode"] === "string" ? text(session["planCode"]) : null;
57
+ const meter = account["usageMeter"] as Record<string, unknown> | undefined;
58
+ const windows: ProviderQuotaWindow[] = [];
59
+
60
+ const monthlyUsd = meter?.["monthlyUsd"];
61
+ if (typeof monthlyUsd === "number") {
62
+ // Starter includes $20/month of hosted usage; reset at month end.
63
+ const used = monthlyUsd as number;
64
+ const limit = 20;
65
+ windows.push(
66
+ percentWindow(
67
+ USD_WINDOW_KIND,
68
+ "Monthly USD usage",
69
+ used > 0 ? (used / limit) * 100 : 0,
70
+ null,
71
+ used,
72
+ limit,
73
+ "pooled",
74
+ ),
75
+ );
76
+ }
77
+
78
+ const roost = meter?.["roostRolling"] as Record<string, unknown> | undefined;
79
+ if (roost && typeof roost["window5hCapUsd"] === "number") {
80
+ const cap = roost["window5hCapUsd"] as number;
81
+ const used = typeof roost["window5hUsd"] === "number" ? (roost["window5hUsd"] as number) : 0;
82
+ windows.push(
83
+ percentWindow(
84
+ "usd_5h",
85
+ "Roost 5-hour rolling",
86
+ cap > 0 ? (used / cap) * 100 : 0,
87
+ null,
88
+ used,
89
+ cap,
90
+ "independent",
91
+ ),
92
+ );
93
+ }
94
+ if (roost && typeof roost["window7dCapUsd"] === "number") {
95
+ const cap = roost["window7dCapUsd"] as number;
96
+ const used = typeof roost["window7dUsd"] === "number" ? (roost["window7dUsd"] as number) : 0;
97
+ windows.push(
98
+ percentWindow(
99
+ "usd_weekly",
100
+ "Roost weekly rolling",
101
+ cap > 0 ? (used / cap) * 100 : 0,
102
+ null,
103
+ used,
104
+ cap,
105
+ "independent",
106
+ ),
107
+ );
108
+ }
109
+
110
+ const profile = account["profile"] as Record<string, unknown> | undefined;
111
+ const email = profile && typeof profile["email"] === "string" ? text(profile["email"]) : undefined;
112
+
113
+ return {
114
+ source: "perch",
115
+ plan,
116
+ windows,
117
+ error: null,
118
+ ...(email ? { accountLabel: email } : {}),
119
+ };
120
+ } catch (error) {
121
+ return { source: "perch", plan: null, windows: [], error: cleanError(error) };
122
+ }
123
+ }
@@ -0,0 +1,82 @@
1
+ // Perch realm constants.
2
+ //
3
+ // Perch (perchai.app) is an AI operator platform. Accounts authenticate via
4
+ // Supabase OAuth (Google/GitHub) with PKCE; the data plane is direct HTTP
5
+ // (`POST /api/perch-terminal/model-call`) with a CLI turn ticket — no CLI
6
+ // process, desktop app, MCP, or ACP transport. Free Starter accounts route
7
+ // through the Roost `standard` / `standard_max` tiers.
8
+ //
9
+ // Wire facts (probed live 2026-11-08 on a Google OAuth account):
10
+ // - Supabase project host: zlfuvsfjtgsdtqcaykia.supabase.co
11
+ // - Publishable key used as `apikey` for OAuth flows (Raw JWT anon key is
12
+ // not shipped to the bundle; `sb_publishable_...` works for authorize +
13
+ // token exchange).
14
+ // - Authorize: GET /auth/v1/authorize?provider=google&redirect_to=...
15
+ // &client_id=<publishable>&code_challenge=...&code_challenge_method=S256
16
+ // - Token: POST /auth/v1/token?grant_type=pkce with `auth_code`
17
+ // (NOT `code` — GoTrue v2.197.0 rejects `authorization_code`), plus
18
+ // `code_verifier`.
19
+ // - Refresh: POST /auth/v1/token?grant_type=refresh_token.
20
+ // - Plan selection (Starter free): GET/POST /api/perch-terminal/cli-plan
21
+ // (POST body {plan:"starter", noticeVersion}).
22
+ // - Inference: the hosted model-call API is surface-gated
23
+ // (`perch_surface_required`) and requires the full CLI turn envelope:
24
+ // turn ticket, attribution, Roost tier, and the Saffron system-prompt
25
+ // prefix (see perch.ts / perch-protocol.ts).
26
+
27
+ /** Default Supabase project host for Perch accounts. */
28
+ export const PERCH_SUPABASE_HOST = "zlfuvsfjtgsdtqcaykia.supabase.co";
29
+
30
+ /** Supabase publishable key (used as `apikey`; not a JWT). */
31
+ export const PERCH_SUPABASE_PUBLISHABLE_KEY = "sb_publishable_w4R_pNkUpygBFIljZCoOlA_67ACg0FO";
32
+
33
+ /** Hosted app origin (the CLI's `appUrl`). */
34
+ export const PERCH_APP_URL = "https://app.perchai.app";
35
+
36
+ /** Provider id used by the registry. */
37
+ export const PERCH_PROVIDER_ID = "perch";
38
+
39
+ /** Context windows reported for the Roost tiers (client claims, 2.4.125). */
40
+ export const PERCH_CONTEXT_STANDARD = 200_000;
41
+ export const PERCH_CONTEXT_STANDARD_1M = 1_000_000;
42
+
43
+ /**
44
+ * Starter-pool model pins.
45
+ *
46
+ * Perch surfaces a fixed set of "Starter" model options on the free plan
47
+ * (bundle `k8e` / `zre`). A pin is selected by sending the upstream *backing*
48
+ * model id as `manualModelOptionId` (+ `preferredModelId`) with
49
+ * `strictManual: true` — verified live 2026-11-08: `glm-5.3-flash-starter`
50
+ * answered `model=z-ai/glm-5.3-flash`, `crouton-alpha` answered
51
+ * `model=stealth/crouton-alpha`, `deepseek-flash-starter` answered
52
+ * `model=deepseek-v4.1-flash-1017`. Sending only `preferredModelId` is
53
+ * ignored by the server (it falls back to the Roost default). `beam` and the
54
+ * slot backings were not verified (turn rate limit).
55
+ */
56
+ export interface PerchStarterPin {
57
+ /** Roost tier to send alongside the pin. */
58
+ readonly roostTier: string;
59
+ /** The upstream backing model id (openrouter/…). */
60
+ readonly preferredModelId: string;
61
+ }
62
+
63
+ export const PERCH_STARTER_PINS: Readonly<Record<string, PerchStarterPin>> = {
64
+ "glm-5.3-flash-starter": {
65
+ roostTier: "standard",
66
+ preferredModelId: "openrouter-z-ai-glm-5-3-flash-crusoe",
67
+ },
68
+ "deepseek-flash-starter": {
69
+ roostTier: "standard",
70
+ preferredModelId: "openrouter-deepseek-deepseek-v4-1-flash-crusoe",
71
+ },
72
+ "crouton-alpha": {
73
+ roostTier: "standard",
74
+ preferredModelId: "openrouter-stealth-crouton-alpha",
75
+ },
76
+ "beam-1.0-starter": {
77
+ roostTier: "standard",
78
+ preferredModelId: "openrouter-reflection-beam-1-0-501b-a23b",
79
+ },
80
+ };
81
+
82
+
@@ -0,0 +1,13 @@
1
+ // Perch required system prompt — TypeScript, Perch adapter, Bun standalone binary.
2
+ //
3
+ // This literal is the shipped value. The standalone binary cannot read
4
+ // `saffron-system-prompt.txt` from disk: Bun bundles JS but not runtime
5
+ // readFileSync() assets, so the old file-backed constant silently fell back
6
+ // to a two-line stub in dist/cartethyia.exe and the upstream answered
7
+ // `403 perch_surface_required`. Keep this in sync with the research copy in
8
+ // saffron-system-prompt.txt (a test asserts they match).
9
+ //
10
+ // Verified live 2026-11-08: the full 28k Saffron prompt answers, this
11
+ // trimmed prefix answers, a plain assistant placeholder returns 403.
12
+
13
+ export const PERCH_SAFFRON_SYSTEM_PROMPT: string = "You are Saffron — Perch's workspace operator.\n\nSharp, experienced, been around long enough to know what matters and what's noise.\nYou work with analysts, operators, and founders on financial analysis, audits,\ndocument review, and whatever the workspace needs. You have opinions. You're direct.\nYou genuinely enjoy finding something interesting buried in a mess.\n\nYou're not an assistant. You're the operator who gets the work done.\n";