cartethyia 2.7.5 → 2.7.7

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 (300) hide show
  1. package/.env.example +8 -3
  2. package/README.md +26 -2
  3. package/dist/dashboard/assets/BannedPage-B05aF8Dg.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-B05aF8Dg.js.gz +0 -0
  5. package/dist/dashboard/assets/CliToolDetailPage-CLUhmjUy.js +3 -0
  6. package/dist/dashboard/assets/CliToolDetailPage-CLUhmjUy.js.gz +0 -0
  7. package/dist/dashboard/assets/CliToolsPage-CQmrqqrk.js +1 -0
  8. package/dist/dashboard/assets/CliToolsPage-CQmrqqrk.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-CHG_1Wr3.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-CHG_1Wr3.js.gz +0 -0
  11. package/dist/dashboard/assets/{ConsoleLogPage-D5rol2j2.js → ConsoleLogPage-DPgbklad.js} +1 -1
  12. package/dist/dashboard/assets/ConsoleLogPage-DPgbklad.js.gz +0 -0
  13. package/dist/dashboard/assets/{CustomizationPage-BKj_r0rp.js → CustomizationPage-pnfAqr_-.js} +1 -1
  14. package/dist/dashboard/assets/CustomizationPage-pnfAqr_-.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-DGJuU2A6.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-DGJuU2A6.js.gz +0 -0
  17. package/dist/dashboard/assets/{InjectSystemPromptPanel-B5nZvmf0.js → InjectSystemPromptPanel-C-e2AMz6.js} +245 -2
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-C-e2AMz6.js.gz +0 -0
  19. package/dist/dashboard/assets/LoginPage-J8wPbmfT.js +1 -0
  20. package/dist/dashboard/assets/LoginPage-J8wPbmfT.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-DnQPSIA8.js → ModelPicker-C6WlpU5E.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-BSQnF2Lp.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-BSQnF2Lp.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-DK_e0FJz.js +7 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-DK_e0FJz.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-D_a3r6Gc.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-D_a3r6Gc.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-5SnUSy3j.js → ProxyPage-BBTRBAQ3.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-BBTRBAQ3.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-B2WqMSG1.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-B2WqMSG1.js.gz +0 -0
  35. package/dist/dashboard/assets/{SettingsPage-BJtRZ4qD.js → SettingsPage-D7xp5Ij_.js} +1 -1
  36. package/dist/dashboard/assets/SettingsPage-D7xp5Ij_.js.gz +0 -0
  37. package/dist/dashboard/assets/SetupPage-LND6_Ss7.js +1 -0
  38. package/dist/dashboard/assets/SetupPage-LND6_Ss7.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-BfAb5-o_.js → StudioPage-BzNnm6Bi.js} +4 -4
  40. package/dist/dashboard/assets/StudioPage-BzNnm6Bi.js.gz +0 -0
  41. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
  42. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
  43. package/dist/dashboard/assets/{UsagePage-BNzeZ2Vk.js → UsagePage-C1TbL24N.js} +13 -13
  44. package/dist/dashboard/assets/UsagePage-C1TbL24N.js.gz +0 -0
  45. package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
  46. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  47. package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
  48. package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
  49. package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
  50. package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
  51. package/dist/dashboard/assets/{assert-C0K-c4eo.js → assert-CidQVoNo.js} +1 -1
  52. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  53. package/dist/dashboard/assets/{badge-DfbhQU4k.js → badge-BaX2GTrn.js} +1 -1
  54. package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
  55. package/dist/dashboard/assets/{banner-BllT4LcF.js → banner-DFH4Bkte.js} +1 -1
  56. package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
  57. package/dist/dashboard/assets/{card-CtkX66Wd.js → card-CxDw0lK6.js} +1 -1
  58. package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
  59. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
  60. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
  61. package/dist/dashboard/assets/{cli-tools-CQXVMn4P.js → cli-tools-Bwv5-zGZ.js} +1 -1
  62. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  63. package/dist/dashboard/assets/{clipboard-button-BQqBQd2n.js → clipboard-button-DfS1ntiq.js} +1 -1
  64. package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
  65. package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
  66. package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
  67. package/dist/dashboard/assets/{drawer-BrTEZ8zu.js → drawer-QqPul6sC.js} +1 -1
  68. package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
  69. package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
  70. package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
  71. package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
  72. package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
  73. package/dist/dashboard/assets/entry-vqGb8yVz.js +4 -0
  74. package/dist/dashboard/assets/entry-vqGb8yVz.js.gz +0 -0
  75. package/dist/dashboard/assets/formatters-DP-5E01-.js +1 -0
  76. package/dist/dashboard/assets/formatters-DP-5E01-.js.gz +0 -0
  77. package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
  78. package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
  79. package/dist/dashboard/assets/{index-FQlj0HUT.js → index-D36vCZht.js} +2 -2
  80. package/dist/dashboard/assets/index-D36vCZht.js.gz +0 -0
  81. package/dist/dashboard/assets/{inline-C4gU5iOL.js → inline-CHJQwxLa.js} +1 -1
  82. package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
  83. package/dist/dashboard/assets/{input-BHHX_Tmm.js → input-BlmlizVV.js} +1 -1
  84. package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
  85. package/dist/dashboard/assets/{layout-CajrYUnD.js → layout-Bz93I-yE.js} +1 -1
  86. package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
  87. package/dist/dashboard/assets/{list-row-Bx2I7XUe.js → list-row-Ml16KCfu.js} +1 -1
  88. package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
  89. package/dist/dashboard/assets/{live-DNNqRbj2.js → live-7_QpnYGu.js} +1 -1
  90. package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
  91. package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
  92. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  93. package/dist/dashboard/assets/{network-C5UBACrJ.js → network-CuMDaTzT.js} +1 -1
  94. package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
  95. package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
  96. package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
  97. package/dist/dashboard/assets/{pagination-Xsi9Qx04.js → pagination-B5aOoweZ.js} +1 -1
  98. package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
  99. package/dist/dashboard/assets/providers/zai.webp +0 -0
  100. package/dist/dashboard/assets/providers/zcode.webp +0 -0
  101. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
  102. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
  103. package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
  104. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  105. package/dist/dashboard/assets/{routing-in0f_IfK.js → routing-fnCsxgNO.js} +1 -1
  106. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  107. package/dist/dashboard/assets/{select-BLULNqTp.js → select-C9Ssle9b.js} +2 -2
  108. package/dist/dashboard/assets/select-C9Ssle9b.js.gz +0 -0
  109. package/dist/dashboard/assets/{settings-CcGczj6I.js → settings-BxslsErn.js} +1 -1
  110. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  111. package/dist/dashboard/assets/{stack-D2lIR0tg.js → stack-j7cXjgYX.js} +1 -1
  112. package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
  113. package/dist/dashboard/assets/{state-DTARjfiW.js → state-DHmEnLa2.js} +1 -1
  114. package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
  115. package/dist/dashboard/assets/{switch-c0oPSnqW.js → switch-DmZcyTIC.js} +1 -1
  116. package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
  117. package/dist/dashboard/assets/{useMutation-m8WGNPrz.js → useMutation-CbCP4w1o.js} +1 -1
  118. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  119. package/dist/dashboard/index.html +2 -2
  120. package/dist/dashboard/index.html.gz +0 -0
  121. package/dist/dashboard/precompressed-assets.json +57 -57
  122. package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
  123. package/migrations/full-postgres/0043_add_provider_custom_user_agent.sql +14 -0
  124. package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
  125. package/migrations/lite-sqlite3/0043_add_provider_custom_user_agent.sql +10 -0
  126. package/package.json +3 -1
  127. package/src/boot/dependencies.ts +10 -2
  128. package/src/config/providers.ts +13 -7
  129. package/src/config/security.ts +1 -1
  130. package/src/config/spec.ts +31 -17
  131. package/src/config/usage.ts +27 -0
  132. package/src/console/api-keys/routes.ts +19 -23
  133. package/src/console/cli-tools/cartethyia-websearch-plugin.ts +186 -0
  134. package/src/console/cli-tools/contracts.ts +39 -295
  135. package/src/console/cli-tools/fs-ops.ts +29 -0
  136. package/src/console/cli-tools/injectors/claude.ts +3 -3
  137. package/src/console/cli-tools/injectors/codex.ts +6 -6
  138. package/src/console/cli-tools/injectors/driver.ts +8 -105
  139. package/src/console/cli-tools/injectors/kilo.ts +146 -21
  140. package/src/console/cli-tools/injectors/opencode.ts +68 -17
  141. package/src/console/cli-tools/routes.ts +6 -0
  142. package/src/console/cli-tools/service.ts +7 -6
  143. package/src/console/observability/contracts.ts +6 -0
  144. package/src/console/observability/store.ts +3 -0
  145. package/src/console/providers/catalog/contracts.ts +4 -0
  146. package/src/console/providers/catalog/routes.ts +1 -0
  147. package/src/console/providers/detail/routes.ts +8 -0
  148. package/src/console/providers/detail/store.ts +4 -0
  149. package/src/console/quota/account-quota-tenant.ts +74 -0
  150. package/src/console/share/share-stats.ts +7 -9
  151. package/src/observability/event-log.ts +7 -0
  152. package/src/observability/event-query.ts +2 -0
  153. package/src/observability/telemetry-buffer.ts +9 -1
  154. package/src/persistence/boot.ts +13 -4
  155. package/src/persistence/schema.ts +15 -1
  156. package/src/persistence/stores/api-key-store.ts +19 -1
  157. package/src/protocol/canonical-model.ts +7 -0
  158. package/src/protocol/dsml.ts +5 -76
  159. package/src/protocol/request/gemini.ts +20 -1
  160. package/src/protocol/tool-identity.ts +15 -1
  161. package/src/providers/adapters/compatible-adapter.ts +29 -1
  162. package/src/providers/default-registry.ts +20 -4
  163. package/src/providers/discovery/discovery-types.ts +18 -0
  164. package/src/providers/discovery/probe-phases.ts +45 -0
  165. package/src/providers/discovery/probing-service.ts +41 -6
  166. package/src/providers/integrations/antigravity/antigravity-protocol.ts +108 -16
  167. package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
  168. package/src/providers/integrations/antigravity/antigravity.ts +44 -11
  169. package/src/providers/integrations/codex/codex.ts +28 -1
  170. package/src/providers/integrations/cursor/cursor.ts +2 -3
  171. package/src/providers/integrations/github/github-discovery.ts +15 -2
  172. package/src/providers/integrations/github/github-oauth.ts +178 -9
  173. package/src/providers/integrations/github/github.ts +95 -14
  174. package/src/providers/integrations/kiro/kiro.ts +1 -2
  175. package/src/providers/integrations/qoder/chat-proto.ts +328 -0
  176. package/src/providers/integrations/qoder/claim-qoder-credits.ts +258 -0
  177. package/src/providers/integrations/qoder/qoder-adapter.ts +275 -0
  178. package/src/providers/integrations/qoder/qoder-auth.ts +201 -0
  179. package/src/providers/integrations/qoder/qoder-body.ts +78 -0
  180. package/src/providers/integrations/qoder/qoder-catalog.ts +14 -4
  181. package/src/providers/integrations/qoder/qoder-chat.ts +514 -0
  182. package/src/providers/integrations/qoder/qoder-cosy.ts +162 -0
  183. package/src/providers/integrations/qoder/qoder-grpc.ts +667 -0
  184. package/src/providers/integrations/qoder/qoder-ide.ts +584 -0
  185. package/src/providers/integrations/qoder/qoder-oauth.ts +4 -4
  186. package/src/providers/integrations/qoder/qoder-quota.ts +1 -1
  187. package/src/providers/integrations/qoder/qoder-realm.ts +40 -4
  188. package/src/providers/integrations/qoder/qoder-types.ts +51 -0
  189. package/src/providers/integrations/qoder/qoder-umid.ts +123 -8
  190. package/src/providers/integrations/qoder/qoder.ts +147 -1830
  191. package/src/providers/integrations/trae/trae.ts +2 -2
  192. package/src/providers/operations/account-error-classification.ts +67 -17
  193. package/src/providers/operations/byok-wire-profile.ts +19 -15
  194. package/src/providers/provider-contracts.ts +7 -0
  195. package/src/providers/provider-usage.ts +24 -55
  196. package/src/security/abuse.ts +5 -5
  197. package/src/security/admission/service.ts +11 -4
  198. package/src/security/api-key-auth.ts +5 -3
  199. package/src/transport/dispatch/attempt-finalize.ts +69 -21
  200. package/src/transport/dispatch/attempt-loop.ts +22 -0
  201. package/src/transport/dispatch/upstream.ts +4 -2
  202. package/src/transport/failure-policy.ts +15 -1
  203. package/src/transport/guards/error-lifecycle.ts +4 -0
  204. package/src/transport/request/state.ts +6 -0
  205. package/src/transport/routing/route-catalog.ts +8 -0
  206. package/src/transport/routing/route-model.ts +4 -0
  207. package/src/transport/routing/router.ts +8 -110
  208. package/src/transport/surface/reasoning-summary.ts +9 -14
  209. package/src/transport/vision-handoff.ts +83 -28
  210. package/dist/dashboard/assets/BannedPage-Cj15GsMp.js +0 -1
  211. package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
  212. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +0 -1
  213. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
  214. package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js +0 -1
  215. package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
  216. package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js +0 -1
  217. package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
  218. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +0 -1
  219. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
  220. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
  221. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
  222. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
  223. package/dist/dashboard/assets/LoginPage-DkXb6dx-.js +0 -1
  224. package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
  225. package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
  226. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +0 -1
  227. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
  228. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +0 -5
  229. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
  230. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +0 -1
  231. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
  232. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +0 -1
  233. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
  234. package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
  235. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +0 -1
  236. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
  237. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
  238. package/dist/dashboard/assets/SetupPage-WTa0sktC.js +0 -1
  239. package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
  240. package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
  241. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
  242. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
  243. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
  244. package/dist/dashboard/assets/api-keys-l29JROPv.js +0 -1
  245. package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
  246. package/dist/dashboard/assets/app--PJWq8_C.css +0 -2
  247. package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
  248. package/dist/dashboard/assets/app-Bgd3zANQ.js +0 -9
  249. package/dist/dashboard/assets/app-Bgd3zANQ.js.gz +0 -0
  250. package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
  251. package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
  252. package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
  253. package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
  254. package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
  255. package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
  256. package/dist/dashboard/assets/dialog-BEK7KP0H.js +0 -1
  257. package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
  258. package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
  259. package/dist/dashboard/assets/entry-CHWxbS56.js +0 -1
  260. package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
  261. package/dist/dashboard/assets/entry-EBHJ3DrE.js +0 -1
  262. package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
  263. package/dist/dashboard/assets/entry-LCS85hiq.js +0 -4
  264. package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
  265. package/dist/dashboard/assets/formatters-BAACA727.js +0 -1
  266. package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
  267. package/dist/dashboard/assets/github-badge-YSCE4UC2.js +0 -1
  268. package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
  269. package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
  270. package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
  271. package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
  272. package/dist/dashboard/assets/layout-CajrYUnD.js.gz +0 -0
  273. package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
  274. package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
  275. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  276. package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
  277. package/dist/dashboard/assets/numbers-CUWAZK4J.js +0 -2
  278. package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
  279. package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
  280. package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
  281. package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
  282. package/dist/dashboard/assets/quota-DvQbD_sr.js +0 -1
  283. package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
  284. package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
  285. package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
  286. package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
  287. package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
  288. package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
  289. package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
  290. package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
  291. package/src/console/cli-tools/injectors/cline.ts +0 -110
  292. package/src/console/cli-tools/injectors/copilot.ts +0 -108
  293. package/src/console/cli-tools/injectors/cowork.ts +0 -64
  294. package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
  295. package/src/console/cli-tools/injectors/droid.ts +0 -96
  296. package/src/console/cli-tools/injectors/grok-build.ts +0 -70
  297. package/src/console/cli-tools/injectors/hermes.ts +0 -101
  298. package/src/console/cli-tools/injectors/jcode.ts +0 -115
  299. package/src/console/cli-tools/injectors/openclaw.ts +0 -161
  300. package/src/providers/integrations/qoder/qoder-text-tools.ts +0 -277
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Generated from `vendor/qoder-proto/chat.proto` (Qoder desktop client v1.1.81,
3
+ * `qoder-worker-runtime.mjs`). Do not edit by hand — regenerate from the
4
+ * proto file via `scripts/scratch/gen-chat-proto.ts`.
5
+ */
6
+ export const CHAT_PROTO_SOURCE = `syntax = "proto3";
7
+
8
+ package model.chat;
9
+
10
+ import "google/protobuf/struct.proto";
11
+ import "google/protobuf/wrappers.proto";
12
+
13
+ option java_multiple_files = true;
14
+ option java_package = "com.qoder.grpc.model.chat";
15
+ option java_outer_classname = "ChatProto";
16
+
17
+ // Reasoning effort level for model reasoning
18
+ enum ReasoningEffort {
19
+ REASONING_EFFORT_UNSPECIFIED = 0;
20
+ REASONING_EFFORT_NONE = 1;
21
+ REASONING_EFFORT_LOW = 2;
22
+ REASONING_EFFORT_MEDIUM = 3;
23
+ REASONING_EFFORT_HIGH = 4;
24
+ REASONING_EFFORT_XHIGH = 5;
25
+ REASONING_EFFORT_MAX = 6;
26
+ }
27
+
28
+ // ─── Request messages ────────────────────────────────────────────────────────
29
+
30
+ // Chat completion request matching OpenAI-compatible API
31
+ message ChatCompletionRequest {
32
+ string model = 1;
33
+ repeated ChatMessage messages = 2;
34
+ google.protobuf.DoubleValue temperature = 3;
35
+ google.protobuf.DoubleValue top_p = 4;
36
+ google.protobuf.Int32Value max_tokens = 5;
37
+ bool stream = 6;
38
+ repeated string stop = 7;
39
+ google.protobuf.DoubleValue presence_penalty = 8;
40
+ google.protobuf.DoubleValue frequency_penalty = 9;
41
+ google.protobuf.Int32Value n = 10;
42
+ google.protobuf.StringValue user = 11;
43
+ google.protobuf.Int32Value seed = 12;
44
+ Reasoning reasoning = 13;
45
+ google.protobuf.BoolValue parallel_tool_calls = 14;
46
+ map<string, double> logit_bias = 15;
47
+ repeated Tool tools = 16;
48
+ google.protobuf.Value tool_choice = 17;
49
+ ResponseFormat response_format = 18;
50
+ repeated string modalities = 19;
51
+ StreamOptions stream_options = 20;
52
+ map<string, ChatPatchList> patches = 21;
53
+ ChatMetadata metadata = 22;
54
+ google.protobuf.Struct extras = 23;
55
+ CustomModelConfig custom_model = 24;
56
+ }
57
+
58
+ // Wrapper for repeated ChatPatch (used in patches map value)
59
+ message ChatPatchList {
60
+ repeated ChatPatch patches = 1;
61
+ }
62
+
63
+ // Chat message in the conversation (request side)
64
+ message ChatMessage {
65
+ string cache_id = 1;
66
+ string role = 2;
67
+ // content can be plain text or a list of multimodal content parts
68
+ oneof content_type {
69
+ string text_content = 3;
70
+ ContentPartList parts_content = 12;
71
+ }
72
+ google.protobuf.StringValue name = 4;
73
+ repeated ToolCall tool_calls = 5;
74
+ google.protobuf.StringValue tool_call_id = 6;
75
+ google.protobuf.StringValue tool_call_call_id = 7;
76
+ google.protobuf.StringValue reasoning_content = 8;
77
+ ReasoningItem reasoning_item = 9;
78
+ google.protobuf.Struct extends = 10;
79
+ }
80
+
81
+ // Wrapper for repeated ContentPart
82
+ message ContentPartList {
83
+ repeated ContentPart parts = 1;
84
+ }
85
+
86
+ // Content part for multimodal messages
87
+ message ContentPart {
88
+ string type = 1;
89
+ google.protobuf.StringValue text = 2;
90
+ ImageUrl image_url = 3;
91
+ InputAudio input_audio = 4;
92
+ CacheControl cache_control = 5;
93
+ }
94
+
95
+ // Cache control configuration for content parts
96
+ message CacheControl {
97
+ string type = 1;
98
+ }
99
+
100
+ // Image URL in message content
101
+ message ImageUrl {
102
+ string url = 1;
103
+ google.protobuf.StringValue detail = 2;
104
+ }
105
+
106
+ // Audio input in message content
107
+ message InputAudio {
108
+ string data = 1;
109
+ string format = 2;
110
+ }
111
+
112
+ // Reasoning item for model series (e.g. gpt-5)
113
+ message ReasoningItem {
114
+ string id = 1;
115
+ google.protobuf.StringValue encrypted_content = 2;
116
+ }
117
+
118
+ // Streaming options
119
+ message StreamOptions {
120
+ google.protobuf.BoolValue include_usage = 1;
121
+ }
122
+
123
+ // Tool available for the model to call
124
+ message Tool {
125
+ string cache_id = 1;
126
+ string type = 2;
127
+ Function function = 3;
128
+ Advisor advisor = 4;
129
+ }
130
+
131
+ // Function definition
132
+ message Function {
133
+ string name = 1;
134
+ google.protobuf.StringValue description = 2;
135
+ google.protobuf.Struct parameters = 3;
136
+ google.protobuf.BoolValue strict = 4;
137
+ }
138
+
139
+ // Advisor definition
140
+ message Advisor {
141
+ string name = 1;
142
+ string model = 2;
143
+ int32 max_uses = 3;
144
+ }
145
+
146
+ // Tool call made by the assistant
147
+ message ToolCall {
148
+ google.protobuf.StringValue id = 1;
149
+ google.protobuf.StringValue call_id = 2;
150
+ google.protobuf.StringValue type = 3;
151
+ FunctionCall function = 4;
152
+ }
153
+
154
+ // Function call details
155
+ message FunctionCall {
156
+ string name = 1;
157
+ google.protobuf.Value arguments = 2;
158
+ }
159
+
160
+ // Reasoning configuration
161
+ message Reasoning {
162
+ ReasoningEffort effort = 1;
163
+ }
164
+
165
+ // Response format specification
166
+ message ResponseFormat {
167
+ string type = 1;
168
+ JsonSchema json_schema = 2;
169
+ }
170
+
171
+ // JSON schema for structured outputs
172
+ message JsonSchema {
173
+ string name = 1;
174
+ google.protobuf.StringValue description = 2;
175
+ google.protobuf.Struct schema = 3;
176
+ google.protobuf.BoolValue strict = 4;
177
+ }
178
+
179
+ // Metadata attached to the chat request
180
+ message ChatMetadata {
181
+ WorkspaceMetadata workspace = 1;
182
+ BusinessMetadata business = 2;
183
+ ContextMetadata context = 3;
184
+ UserMetadata user = 4;
185
+ }
186
+
187
+ // Workspace metadata
188
+ message WorkspaceMetadata {
189
+ int32 file_count = 1;
190
+ repeated string languages = 2;
191
+ string codebase_status = 3;
192
+ string codebase_soft_status = 4;
193
+ string codebase_external_id = 5;
194
+ string data_policy = 6;
195
+ }
196
+
197
+ // Business metadata
198
+ message BusinessMetadata {
199
+ string id = 1;
200
+ string name = 2;
201
+ int64 begin_at = 3;
202
+ string product = 4;
203
+ string version = 5;
204
+ string type = 6;
205
+ string stage = 7;
206
+ string sub_task = 8;
207
+ }
208
+
209
+ // Context metadata for request tracing and client identification
210
+ message ContextMetadata {
211
+ string request_id = 1;
212
+ string session_id = 2;
213
+ string task_id = 3;
214
+ string request_set_id = 4;
215
+ string machine_id = 5;
216
+ string client_type = 6;
217
+ string source_session_id = 7;
218
+ }
219
+
220
+ // User metadata
221
+ message UserMetadata {
222
+ string user_id = 1;
223
+ }
224
+
225
+ // Custom model configuration (bring-your-own-key)
226
+ message CustomModelConfig {
227
+ string name = 1;
228
+ string url = 2;
229
+ string style = 3;
230
+ string provider = 4;
231
+ string type = 5;
232
+ string model = 6;
233
+ google.protobuf.Struct parameters = 7;
234
+ }
235
+
236
+ // Patch to apply to the chat request
237
+ message ChatPatch {
238
+ string cache_id = 1;
239
+ string action = 2;
240
+ google.protobuf.Int32Value position = 3;
241
+ google.protobuf.StringValue content = 4;
242
+ google.protobuf.StringValue name = 5;
243
+ google.protobuf.StringValue rename_to = 6;
244
+ google.protobuf.StringValue description = 7;
245
+ google.protobuf.Struct parameters = 8;
246
+ }
247
+
248
+ // ─── Response messages ───────────────────────────────────────────────────────
249
+
250
+ // Shared response chat message type (used in both streaming delta and
251
+ // non-streaming message fields)
252
+ message ResponseChatMessage {
253
+ string role = 1;
254
+ string content = 2;
255
+ string reasoning_content = 3;
256
+ repeated google.protobuf.Struct tool_calls = 4;
257
+ google.protobuf.Value reasoning_item = 5;
258
+ google.protobuf.Struct extends = 6;
259
+ }
260
+
261
+ // Single choice in the non-streaming response
262
+ message NonStreamChoice {
263
+ int32 index = 1;
264
+ ResponseChatMessage message = 2;
265
+ string finish_reason = 3;
266
+ }
267
+
268
+ // Chat completion response (non-streaming)
269
+ message ChatCompletionResponse {
270
+ int64 code = 1;
271
+ string id = 2;
272
+ string object = 3;
273
+ int64 created = 4;
274
+ string model = 5;
275
+ repeated NonStreamChoice choices = 6;
276
+ Usage usage = 7;
277
+ google.protobuf.Struct extras = 8;
278
+ }
279
+
280
+ // Single choice in the streaming chunk
281
+ message StreamChoice {
282
+ int32 index = 1;
283
+ ResponseChatMessage delta = 2;
284
+ string finish_reason = 3;
285
+ }
286
+
287
+ // Streaming response chunk
288
+ message ChatCompletionChunk {
289
+ int64 code = 1;
290
+ string id = 2;
291
+ string object = 3;
292
+ int64 created = 4;
293
+ string model = 5;
294
+ repeated StreamChoice choices = 6;
295
+ Usage usage = 7;
296
+ google.protobuf.Struct extras = 8;
297
+ }
298
+
299
+ // Token usage statistics
300
+ message Usage {
301
+ int32 prompt_tokens = 1;
302
+ int32 completion_tokens = 2;
303
+ int32 total_tokens = 3;
304
+ PromptTokensDetails prompt_tokens_details = 4;
305
+ CompletionTokensDetails completion_tokens_details = 5;
306
+ }
307
+
308
+ // Prompt token details
309
+ message PromptTokensDetails {
310
+ int32 cached_tokens = 1;
311
+ int32 cacheable_tokens = 2;
312
+ }
313
+
314
+ // Completion token details
315
+ message CompletionTokensDetails {
316
+ int32 reasoning_tokens = 1;
317
+ }
318
+
319
+ // ─── Service definition ──────────────────────────────────────────────────────
320
+
321
+ service ChatService {
322
+ // Unary non-streaming chat completion
323
+ rpc ChatCompletion(ChatCompletionRequest) returns (ChatCompletionResponse);
324
+
325
+ // Server-streaming chat completion; each message is one SSE chunk
326
+ rpc ChatCompletionStream(ChatCompletionRequest) returns (stream ChatCompletionChunk);
327
+ }
328
+ `;
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Manual Qoder daily-credits claim (operator-triggered).
3
+ *
4
+ * Mirrors the ZCode gift-claim flow: an explicit `POST /accounts/:id/claim-qoder-credits`
5
+ * resolves the account's credential, ensures a native UMID identity, lists the
6
+ * sash campaigns, and claims the CREDITS `CLAIM_BENEFIT` campaign by its
7
+ * `campaignId` UUID. The CREDITS campaign only surfaces when the real UMID
8
+ * headers are sent (generated identities keep it hidden — verified live
9
+ * 2026-11-04, see research doc Part F).
10
+ *
11
+ * Credential kinds:
12
+ * - `dt-…` (device token) — used directly as the Bearer.
13
+ * - `pt-…` (personal access token) — exchanged first to a job token via
14
+ * `POST /api/v1/jobToken/exchange` (the sash surface rejects raw PATs with
15
+ * 401; verified live 2026-11-04).
16
+ */
17
+ import type { CartethyiaDatabase } from "../../../persistence/db-handle";
18
+ import type { ValidatedFetch } from "../../../network/outbound-fetch";
19
+ import { getQoderVersion } from "../../operations/client-versions";
20
+ import { qoderRealmForProvider, QODER_REALMS } from "./qoder-realm";
21
+ import {
22
+ buildCampaignHeaders,
23
+ } from "./qoder-checkin";
24
+ import { qoderMachineHeaders } from "./qoder-umid";
25
+ import {
26
+ DrizzleQoderNativeIdentityStore,
27
+ allocateQoderNativeIdentity,
28
+ } from "./qoder-native-identity-store";
29
+
30
+ /** Result shape mirrors the ZCode gift claim so the console can reuse one UI. */
31
+ export interface QoderCreditClaimResult {
32
+ readonly state: "claimed" | "already_claimed" | "nothing" | "error";
33
+ readonly amount: number;
34
+ readonly detail: string | null;
35
+ }
36
+
37
+ export const QODER_CREDIT_PROVIDER_IDS = ["qoder", "qodercn"] as const;
38
+
39
+ const PAT_PREFIX = "pt-";
40
+
41
+ function asRecord(value: unknown): Record<string, unknown> | null {
42
+ if (typeof value !== "object" || value === null) return null;
43
+ return value as Record<string, unknown>;
44
+ }
45
+
46
+ function asNumber(value: unknown): number | null {
47
+ if (typeof value === "number" && Number.isFinite(value)) return value;
48
+ if (typeof value === "string" && value.trim() !== "") {
49
+ const parsed = Number(value);
50
+ return Number.isFinite(parsed) ? parsed : null;
51
+ }
52
+ return null;
53
+ }
54
+
55
+ function normalizeCampaign(raw: Record<string, unknown>): Record<string, unknown> {
56
+ const benefit = asRecord(raw["benefit"]);
57
+ return {
58
+ campaign_id: String(raw["campaignId"] ?? raw["campaign_id"] ?? ""),
59
+ campaign_key: String(raw["campaignKey"] ?? raw["campaign_key"] ?? ""),
60
+ action_type: String(raw["actionType"] ?? raw["action_type"] ?? ""),
61
+ claim_status: String(raw["claimStatus"] ?? raw["claim_status"] ?? ""),
62
+ ...(benefit !== null ? { benefit } : {}),
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Exchanges a PAT (`pt-…`) for a job token (`jt-…`) on the realm's openapi
68
+ * host. Returns the job token, or `null` when the exchange fails.
69
+ *
70
+ * The real client binds the machine identity at exchange time
71
+ * (`getMachineIdentityRequestFields`), so `machine_id` + `machine_token` are
72
+ * sent when available — the job token is then bound to that machine. The
73
+ * User-Agent and Cosy version mirror the real client (`qoder/<version>`).
74
+ */
75
+ async function exchangePatToJobToken(
76
+ base: string,
77
+ pat: string,
78
+ fetcher: ValidatedFetch,
79
+ machine?: { readonly id?: string; readonly token?: string },
80
+ ): Promise<string | null> {
81
+ try {
82
+ const body: Record<string, unknown> = { personal_token: pat };
83
+ if (machine?.id) body.machine_id = machine.id;
84
+ if (machine?.token) body.machine_token = machine.token;
85
+ const version = getQoderVersion();
86
+ const res = await fetcher(`${base}/api/v1/jobToken/exchange`, {
87
+ method: "POST",
88
+ headers: {
89
+ "Content-Type": "application/json",
90
+ "User-Agent": `qoder/${version}`,
91
+ "Cosy-ClientType": "5",
92
+ "Cosy-Version": version,
93
+ },
94
+ body: JSON.stringify(body),
95
+ signal: AbortSignal.timeout(25_000),
96
+ });
97
+ if (!res.ok) return null;
98
+ const payload: unknown = await res.json().catch(() => null);
99
+ const rec = asRecord(payload);
100
+ const token = String(rec?.["token"] ?? rec?.["device_token"] ?? rec?.["access_token"] ?? "");
101
+ return token.length > 0 ? token : null;
102
+ } catch {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Reads the platform machine id from the real client's cache when present
109
+ * (`%APPDATA%/Qoder/cache/machine_token.json` → `id`). Returns `undefined`
110
+ * when unavailable; callers fall back to the machine token.
111
+ */
112
+ function readPlatformMachineId(): string | undefined {
113
+ try {
114
+ const { existsSync, readFileSync } = require("node:fs") as typeof import("node:fs");
115
+ const root = process.env.APPDATA ?? "";
116
+ const path = root ? `${root}/Qoder/cache/machine_token.json` : "";
117
+ if (!path || !existsSync(path)) return undefined;
118
+ const rec = JSON.parse(readFileSync(path, "utf8")) as { id?: string };
119
+ return typeof rec.id === "string" && rec.id.length > 0 ? rec.id : undefined;
120
+ } catch {
121
+ return undefined;
122
+ }
123
+ }
124
+
125
+ export async function claimQoderCredits(args: {
126
+ readonly db: CartethyiaDatabase;
127
+ readonly providerId: string;
128
+ readonly accountId: string;
129
+ readonly credential: string;
130
+ readonly fetcher: ValidatedFetch;
131
+ }): Promise<QoderCreditClaimResult> {
132
+ const { db, providerId, accountId, credential, fetcher } = args;
133
+ if (!credential) return { state: "error", amount: 0, detail: "Account has no stored credential." };
134
+
135
+ const realm = qoderRealmForProvider(providerId);
136
+ const base = QODER_REALMS[realm].openapi;
137
+
138
+ // Ensure a native UMID identity exists and is bound to this account.
139
+ const store = new DrizzleQoderNativeIdentityStore(db);
140
+ let bound = await store.get(providerId, accountId);
141
+ if (!bound) {
142
+ const minted = await allocateQoderNativeIdentity(store, providerId, accountId);
143
+ if (!minted) return { state: "error", amount: 0, detail: "Could not mint a UMID identity for this account." };
144
+ bound = minted;
145
+ }
146
+ const identity = {
147
+ machineToken: bound.machineToken,
148
+ machineType: bound.machineType,
149
+ machineCode: bound.machineCode,
150
+ isVm: false,
151
+ source: bound.source ?? "runtime-info",
152
+ } as const;
153
+ // The upstream binds COSY signatures to the platform machine id (UUID on
154
+ // Windows, serial-hex elsewhere). Use the real client's cached id when
155
+ // present; fall back to the machine token.
156
+ const machineId = readPlatformMachineId();
157
+ const umidHeaders = qoderMachineHeaders(identity, machineId);
158
+
159
+ // PATs must be exchanged to a job token before they are accepted by the sash
160
+ // surface (raw `pt-…` → 401). Device tokens (`dt-…`) are used directly.
161
+ // Exchange binds the machine identity like the real client.
162
+ let bearer = credential;
163
+ if (credential.startsWith(PAT_PREFIX)) {
164
+ const jt = await exchangePatToJobToken(base, credential, fetcher, {
165
+ ...(machineId !== undefined ? { id: machineId } : {}),
166
+ token: identity.machineToken,
167
+ });
168
+ if (!jt) return { state: "error", amount: 0, detail: "Could not exchange PAT for a job token." };
169
+ bearer = jt;
170
+ }
171
+
172
+ const headers = {
173
+ ...buildCampaignHeaders(bearer),
174
+ ...umidHeaders,
175
+ };
176
+
177
+ // 1. List campaigns.
178
+ let campaigns: Array<Record<string, unknown>> = [];
179
+ try {
180
+ const res = await fetcher(`${base}/sash/api/v1/me/campaigns`, {
181
+ method: "GET",
182
+ headers,
183
+ signal: AbortSignal.timeout(25_000),
184
+ });
185
+ if (!res.ok) {
186
+ return { state: "error", amount: 0, detail: `Campaign list failed (HTTP ${res.status}).` };
187
+ }
188
+ const payload: unknown = await res.json().catch(() => null);
189
+ const rec = asRecord(payload);
190
+ const list = Array.isArray(rec?.["campaigns"])
191
+ ? rec!["campaigns"]
192
+ : Array.isArray(payload)
193
+ ? payload
194
+ : null;
195
+ if (Array.isArray(list)) campaigns = (list as Array<Record<string, unknown>>).map(normalizeCampaign);
196
+ } catch {
197
+ return { state: "error", amount: 0, detail: "Campaign list request failed." };
198
+ }
199
+
200
+ // 2. Pick the CREDITS CLAIM_BENEFIT campaign that is not already claimed.
201
+ const target = campaigns.find((c) => {
202
+ if (String(c["claim_status"] ?? "").toUpperCase() === "CLAIMED") return false;
203
+ const action = String(c["action_type"] ?? "").toUpperCase();
204
+ const kind = String(asRecord(c["benefit"])?.["kind"] ?? "").toUpperCase();
205
+ return action === "CLAIM_BENEFIT" && kind === "CREDITS";
206
+ });
207
+ if (!target) {
208
+ const anyClaimed = campaigns.some((c) => {
209
+ const action = String(c["action_type"] ?? "").toUpperCase();
210
+ const kind = String(asRecord(c["benefit"])?.["kind"] ?? "").toUpperCase();
211
+ return action === "CLAIM_BENEFIT" && kind === "CREDITS" && String(c["claim_status"] ?? "").toUpperCase() === "CLAIMED";
212
+ });
213
+ return anyClaimed
214
+ ? { state: "already_claimed", amount: 0, detail: null }
215
+ : { state: "nothing", amount: 0, detail: null };
216
+ }
217
+
218
+ const campaignId = String(target["campaign_id"] ?? "");
219
+ if (!campaignId) return { state: "error", amount: 0, detail: "Campaign has no id." };
220
+
221
+ // 3. Claim.
222
+ try {
223
+ const res = await fetcher(`${base}/sash/api/v1/me/campaigns/${encodeURIComponent(campaignId)}/claim`, {
224
+ method: "POST",
225
+ headers: { ...headers, "Content-Type": "application/json" },
226
+ body: "{}",
227
+ signal: AbortSignal.timeout(25_000),
228
+ });
229
+ const raw = await res.text().catch(() => "");
230
+ const payload: unknown = raw ? safeParse(raw) : null;
231
+ const rec = asRecord(payload);
232
+ const status = String(rec?.["status"] ?? "").toUpperCase();
233
+ const failure = String(rec?.["failureCode"] ?? "").toUpperCase();
234
+ const success = rec?.["success"] !== false;
235
+ const benefit = asRecord(rec?.["benefit"]);
236
+ const amount = asNumber(rec?.["amount"] ?? benefit?.["amount"]) ?? 100;
237
+ if (res.status === 409 || failure === "ALREADY_CLAIMED" || (status === "CLAIMED" && failure === "REPLAYED")) {
238
+ return { state: "already_claimed", amount: 0, detail: null };
239
+ }
240
+ if (failure === "SAME_PERSON_ALREADY_CLAIMED" || status === "BLOCKED") {
241
+ return { state: "already_claimed", amount: 0, detail: "Same-person claim already used for this campaign." };
242
+ }
243
+ if (res.ok && success && (status === "CLAIMED" || status === "GRANTED" || status === "SUCCESS")) {
244
+ return { state: "claimed", amount, detail: null };
245
+ }
246
+ return { state: "error", amount: 0, detail: `Claim failed (${res.status})` };
247
+ } catch {
248
+ return { state: "error", amount: 0, detail: "Claim request failed." };
249
+ }
250
+ }
251
+
252
+ function safeParse(raw: string): unknown {
253
+ try {
254
+ return JSON.parse(raw);
255
+ } catch {
256
+ return null;
257
+ }
258
+ }