cartethyia 2.7.8 → 2.7.9

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 (371) hide show
  1. package/.env.example +0 -1
  2. package/README.md +29 -59
  3. package/dist/dashboard/assets/BannedPage-BxoG1MrL.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-BxoG1MrL.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolDetailPage-BRWKPQPf.js → CliToolDetailPage-ixwgKk53.js} +1 -1
  6. package/dist/dashboard/assets/CliToolDetailPage-ixwgKk53.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.js → CliToolsPage-BmttNV3U.js} +1 -1
  8. package/dist/dashboard/assets/CliToolsPage-BmttNV3U.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-BE3qwwfC.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-BE3qwwfC.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-CRZdmMIX.js.gz +0 -0
  13. package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js +1 -0
  14. package/dist/dashboard/assets/CustomizationPage-Dv_FJ6Er.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-CoVrw2sM.js.gz +0 -0
  17. package/dist/dashboard/assets/{InjectSystemPromptPanel-Cv-2Ol_K.js → InjectSystemPromptPanel-BOoO8rpz.js} +2 -2
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-BOoO8rpz.js.gz +0 -0
  19. package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-ChJuNNVN.js} +1 -1
  20. package/dist/dashboard/assets/LoginPage-ChJuNNVN.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.js → ModelPicker-BZAJtPg1.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-BZAJtPg1.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-BE40s3u5.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-BE40s3u5.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js +8 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-sfLoQtPV.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-CQ6bPUf_.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-DcepmPWo.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-emlTuxz6.js → ProxyPage-BJ87Pp0U.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-BJ87Pp0U.js.gz +0 -0
  33. package/dist/dashboard/assets/{QuotaPage-CMJKNYWb.js → QuotaPage-gkSyRAKa.js} +1 -1
  34. package/dist/dashboard/assets/QuotaPage-gkSyRAKa.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-C5HPWqEv.js.gz +0 -0
  37. package/dist/dashboard/assets/{SetupPage-CUZrKs9r.js → SetupPage-CFNcATJJ.js} +1 -1
  38. package/dist/dashboard/assets/SetupPage-CFNcATJJ.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-jWmUdDpy.js → StudioPage-YoD4tY5l.js} +1 -1
  40. package/dist/dashboard/assets/StudioPage-YoD4tY5l.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-DxjaAhb_.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-DxjaAhb_.js.gz +0 -0
  43. package/dist/dashboard/assets/api-keys-BTlkq_Uk.js +1 -0
  44. package/dist/dashboard/assets/api-keys-BTlkq_Uk.js.gz +0 -0
  45. package/dist/dashboard/assets/app-BX2nnKqs.css +2 -0
  46. package/dist/dashboard/assets/app-BX2nnKqs.css.gz +0 -0
  47. package/dist/dashboard/assets/assert-CndthXFQ.js +1 -0
  48. package/dist/dashboard/assets/assert-CndthXFQ.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-DyI_mJfi.js → badge-DdbT_7Am.js} +1 -1
  50. package/dist/dashboard/assets/badge-DdbT_7Am.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-CsGN-nbm.js} +1 -1
  54. package/dist/dashboard/assets/banner-CsGN-nbm.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-mgZ_TLLZ.js} +1 -1
  64. package/dist/dashboard/assets/card-mgZ_TLLZ.js.gz +0 -0
  65. package/dist/dashboard/assets/cli-tools-A1-v8-03.js +1 -0
  66. package/dist/dashboard/assets/cli-tools-A1-v8-03.js.gz +0 -0
  67. package/dist/dashboard/assets/{clipboard-button-ByjCk0o9.js → clipboard-button-CJ4Zty89.js} +1 -1
  68. package/dist/dashboard/assets/clipboard-button-CJ4Zty89.js.gz +0 -0
  69. package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-B5L2jPQ_.js} +1 -1
  70. package/dist/dashboard/assets/dialog-B5L2jPQ_.js.gz +0 -0
  71. package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-DkjUouAj.js} +1 -1
  72. package/dist/dashboard/assets/drawer-DkjUouAj.js.gz +0 -0
  73. package/dist/dashboard/assets/{entry-COaC-irN.js → entry-B-LVLV6o.js} +1 -1
  74. package/dist/dashboard/assets/entry-B-LVLV6o.js.gz +0 -0
  75. package/dist/dashboard/assets/entry-CAhdYl3B.js +1 -0
  76. package/dist/dashboard/assets/entry-CAhdYl3B.js.gz +0 -0
  77. package/dist/dashboard/assets/entry-D2xD0Tmd.js +4 -0
  78. package/dist/dashboard/assets/entry-D2xD0Tmd.js.gz +0 -0
  79. package/dist/dashboard/assets/{formatters-DcHJyRiu.js → formatters-CrsdLVsn.js} +1 -1
  80. package/dist/dashboard/assets/formatters-CrsdLVsn.js.gz +0 -0
  81. package/dist/dashboard/assets/{index-CgKmFngv.js → index-BU64Lqvp.js} +2 -2
  82. package/dist/dashboard/assets/index-BU64Lqvp.js.gz +0 -0
  83. package/dist/dashboard/assets/{inline-B2BirKry.js → inline-B2aDLyyg.js} +1 -1
  84. package/dist/dashboard/assets/inline-B2aDLyyg.js.gz +0 -0
  85. package/dist/dashboard/assets/{input-D-qV9dKY.js → input-BCVkdR9d.js} +1 -1
  86. package/dist/dashboard/assets/input-BCVkdR9d.js.gz +0 -0
  87. package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-DNcAdMK-.js} +1 -1
  88. package/dist/dashboard/assets/layout-DNcAdMK-.js.gz +0 -0
  89. package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-DgrIH0hw.js} +1 -1
  90. package/dist/dashboard/assets/list-row-DgrIH0hw.js.gz +0 -0
  91. package/dist/dashboard/assets/{live-DGizF06-.js → live-Cae01iu8.js} +1 -1
  92. package/dist/dashboard/assets/live-Cae01iu8.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-CRib7H_2.js} +1 -1
  96. package/dist/dashboard/assets/network-CRib7H_2.js.gz +0 -0
  97. package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-BKQE8Xpz.js} +1 -1
  98. package/dist/dashboard/assets/numbers-BKQE8Xpz.js.gz +0 -0
  99. package/dist/dashboard/assets/{pagination-BAywyMzS.js → pagination-ecPvG_8g.js} +1 -1
  100. package/dist/dashboard/assets/pagination-ecPvG_8g.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/{query-keys-YSoQOUh6.js → query-keys-IKLh0YFd.js} +1 -1
  106. package/dist/dashboard/assets/query-keys-IKLh0YFd.js.gz +0 -0
  107. package/dist/dashboard/assets/routing-Ckm1UXuC.js +1 -0
  108. package/dist/dashboard/assets/routing-Ckm1UXuC.js.gz +0 -0
  109. package/dist/dashboard/assets/{select-BCOcv8WK.js → select-ChoFWyKz.js} +1 -1
  110. package/dist/dashboard/assets/select-ChoFWyKz.js.gz +0 -0
  111. package/dist/dashboard/assets/settings-CKjFPlKW.js +1 -0
  112. package/dist/dashboard/assets/settings-CKjFPlKW.js.gz +0 -0
  113. package/dist/dashboard/assets/{stack-ByKt3Q1g.js → stack-DWCwU3fh.js} +1 -1
  114. package/dist/dashboard/assets/stack-DWCwU3fh.js.gz +0 -0
  115. package/dist/dashboard/assets/{state-DXbGTx7N.js → state-Cg3VL5J_.js} +1 -1
  116. package/dist/dashboard/assets/state-Cg3VL5J_.js.gz +0 -0
  117. package/dist/dashboard/assets/story/25817331.webp +0 -0
  118. package/dist/dashboard/assets/story/Shorekeeper.webp +0 -0
  119. package/dist/dashboard/assets/story/cartethyia-god.webp +0 -0
  120. package/dist/dashboard/assets/story/fleurdelys_plus.webp +0 -0
  121. package/dist/dashboard/assets/story/jinhsi-blossom.webp +0 -0
  122. package/dist/dashboard/assets/story/phrolova-a.webp +0 -0
  123. package/dist/dashboard/assets/story/requestdeniawokkjpg.webp +0 -0
  124. package/dist/dashboard/assets/story/wuthering-waves-hiyuki-aemeath-hiyuki-rover.webp +0 -0
  125. package/dist/dashboard/assets/{switch-ZeTsW623.js → switch-Db70BMix.js} +1 -1
  126. package/dist/dashboard/assets/switch-Db70BMix.js.gz +0 -0
  127. package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BqdE0LuM.js} +1 -1
  128. package/dist/dashboard/assets/useMutation-BqdE0LuM.js.gz +0 -0
  129. package/dist/dashboard/console-backgrounds.webp +0 -0
  130. package/dist/dashboard/console-backgrounds_initial.webp +0 -0
  131. package/dist/dashboard/donation/qris-risun-marke.webp +0 -0
  132. package/dist/dashboard/index.html +3 -3
  133. package/dist/dashboard/index.html.gz +0 -0
  134. package/dist/dashboard/precompressed-assets.json +57 -55
  135. package/migrations/full-postgres/0044_add_models_warning_metadata.sql +10 -0
  136. package/migrations/full-postgres/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  137. package/migrations/lite-sqlite3/0044_add_models_warning_metadata.sql +11 -0
  138. package/migrations/lite-sqlite3/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  139. package/package.json +1 -1
  140. package/src/boot/dependencies.ts +11 -3
  141. package/src/config/cooldowns.ts +1 -1
  142. package/src/config/spec.ts +0 -6
  143. package/src/console/api-keys/routes.ts +11 -4
  144. package/src/console/console-router.ts +7 -0
  145. package/src/console/dashboard-assets.ts +5 -5
  146. package/src/console/domain-registration.ts +27 -0
  147. package/src/console/ip-bans/routes.ts +99 -0
  148. package/src/console/providers/catalog/catalog-projections.ts +5 -2
  149. package/src/console/providers/catalog/contracts.ts +123 -0
  150. package/src/console/providers/catalog/model-operations.ts +122 -10
  151. package/src/console/providers/catalog/provider-operations.ts +116 -8
  152. package/src/console/providers/catalog/public-model-store.ts +0 -0
  153. package/src/console/providers/catalog/routes.ts +64 -4
  154. package/src/console/providers/catalog/store-helpers.ts +30 -1
  155. package/src/console/providers/catalog/store.ts +243 -3
  156. package/src/console/quota/account-quota-global.ts +89 -1
  157. package/src/console/quota/native-identity.ts +2 -0
  158. package/src/console/settings/contracts.ts +18 -0
  159. package/src/console/settings/store.ts +32 -0
  160. package/src/console/system-prompt/contracts.ts +0 -1
  161. package/src/main.ts +1 -0
  162. package/src/observability/event-query.ts +18 -4
  163. package/src/persistence/dialect.ts +12 -0
  164. package/src/persistence/schema.ts +20 -2
  165. package/src/protocol/dsml.ts +1 -1
  166. package/src/protocol/gateway-error.ts +1 -0
  167. package/src/protocol/reasoning.ts +1 -1
  168. package/src/protocol/request/chat.ts +4 -4
  169. package/src/protocol/request/messages.ts +1 -1
  170. package/src/protocol/response/gemini.ts +6 -1
  171. package/src/protocol/sse-decoder.ts +25 -8
  172. package/src/protocol/stream-error-frames.ts +13 -3
  173. package/src/protocol/translation/quirks.ts +1 -1
  174. package/src/protocol/translation/tool-repair.ts +2 -2
  175. package/src/protocol/upstream/send-claude-messages.ts +15 -4
  176. package/src/protocol/upstream-parsing.ts +1 -1
  177. package/src/providers/adapters/compatible-adapter.ts +7 -5
  178. package/src/providers/authentication/oauth-refresh-service.ts +149 -36
  179. package/src/providers/default-registry.ts +68 -3
  180. package/src/providers/discovery/discovery-types.ts +12 -0
  181. package/src/providers/discovery/probe-phases.ts +61 -30
  182. package/src/providers/discovery/probing-service.ts +279 -164
  183. package/src/providers/integrations/antigravity/antigravity-protocol.ts +117 -2
  184. package/src/providers/integrations/antigravity/antigravity.ts +13 -2
  185. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +72 -34
  186. package/src/providers/integrations/buddy/buddy-chat-shared.ts +18 -18
  187. package/src/providers/integrations/buddy/buddy-checkin.ts +17 -39
  188. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +2 -2
  189. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +5 -5
  190. package/src/providers/integrations/buddy/buddy-identity.ts +70 -0
  191. package/src/providers/integrations/buddy/buddy-media.ts +4 -5
  192. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +2 -2
  193. package/src/providers/integrations/buddy/buddy-quota-shared.ts +1 -1
  194. package/src/providers/integrations/buddy/buddy-usage-shared.ts +1 -3
  195. package/src/providers/integrations/buddy/capabilities.ts +4 -41
  196. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
  197. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +20 -1
  198. package/src/providers/integrations/buddy/codebuddy/provider.ts +2 -2
  199. package/src/providers/integrations/buddy/codebuddy/shared.ts +6 -10
  200. package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
  201. package/src/providers/integrations/cursor/cursor-exec.ts +738 -0
  202. package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
  203. package/src/providers/integrations/cursor/cursor-request.ts +9 -8
  204. package/src/providers/integrations/cursor/cursor-shared.ts +19 -1
  205. package/src/providers/integrations/cursor/cursor.ts +50 -30
  206. package/src/providers/integrations/cursor/protocol/tool-canonical.ts +26 -100
  207. package/src/providers/integrations/devin/devin-auth.ts +353 -0
  208. package/src/providers/integrations/devin/devin-catalog.ts +207 -0
  209. package/src/providers/integrations/devin/devin-http.ts +91 -0
  210. package/src/providers/integrations/devin/devin-quota.ts +214 -0
  211. package/src/providers/integrations/devin/devin-shared.ts +111 -0
  212. package/src/providers/integrations/devin/devin.ts +539 -0
  213. package/src/providers/integrations/devin/protocol/devin-proto.ts +584 -0
  214. package/src/providers/integrations/devin/protocol/framing.ts +124 -0
  215. package/src/providers/integrations/factory/factory.ts +2 -2
  216. package/src/providers/integrations/freepi/freepi-catalog.ts +60 -0
  217. package/src/providers/integrations/freepi/freepi-discovery.ts +82 -0
  218. package/src/providers/integrations/freepi/freepi-headers.ts +21 -0
  219. package/src/providers/integrations/freepi/freepi-oauth.ts +142 -0
  220. package/src/providers/integrations/freepi/freepi-protocol.ts +242 -0
  221. package/src/providers/integrations/freepi/freepi-quota.ts +126 -0
  222. package/src/providers/integrations/freepi/freepi-realm.ts +35 -0
  223. package/src/providers/integrations/freepi/freepi.ts +292 -0
  224. package/src/providers/integrations/gemini.ts +2 -2
  225. package/src/providers/integrations/hermes/hermes-oauth.ts +94 -0
  226. package/src/providers/integrations/hermes.ts +4 -12
  227. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  228. package/src/providers/integrations/perch/perch-catalog.ts +105 -0
  229. package/src/providers/integrations/perch/perch-oauth.ts +170 -0
  230. package/src/providers/integrations/perch/perch-protocol.ts +211 -0
  231. package/src/providers/integrations/perch/perch-quota.ts +137 -0
  232. package/src/providers/integrations/perch/perch-realm.ts +82 -0
  233. package/src/providers/integrations/perch/perch-system-prompt.ts +13 -0
  234. package/src/providers/integrations/perch/perch.ts +315 -0
  235. package/src/providers/integrations/perch/saffron-system-prompt.txt +8 -0
  236. package/src/providers/integrations/qoder/chat-proto.ts +328 -328
  237. package/src/providers/integrations/qoder/claim-qoder-credits.ts +1 -1
  238. package/src/providers/integrations/qoder/qoder-adapter.ts +89 -28
  239. package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
  240. package/src/providers/integrations/qoder/qoder-catalog.ts +6 -1
  241. package/src/providers/integrations/qoder/qoder-checkin.ts +2 -2
  242. package/src/providers/integrations/qoder/qoder-grpc.ts +210 -6
  243. package/src/providers/integrations/qoder/qoder-ide.ts +5 -1
  244. package/src/providers/integrations/qoder/qoder-identity-cache.ts +184 -0
  245. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +83 -18
  246. package/src/providers/integrations/qoder/qoder-realm.ts +9 -6
  247. package/src/providers/integrations/qoder/qoder-umid.ts +109 -342
  248. package/src/providers/integrations/qoder/qoder-virtual.ts +33 -1
  249. package/src/providers/integrations/qoder/qoder.ts +3 -6
  250. package/src/providers/integrations/trae/trae-catalog.ts +9 -14
  251. package/src/providers/integrations/trae/trae-headers.ts +36 -11
  252. package/src/providers/integrations/trae/trae-oauth.ts +14 -17
  253. package/src/providers/integrations/trae/trae-protocol.ts +3 -6
  254. package/src/providers/integrations/trae/trae-quota.ts +103 -21
  255. package/src/providers/integrations/trae/trae-realm.ts +36 -15
  256. package/src/providers/integrations/trae/trae.ts +97 -7
  257. package/src/providers/operations/account-error-classification.ts +66 -1
  258. package/src/providers/operations/account-health-service.ts +192 -26
  259. package/src/providers/operations/account-refresh-status.ts +284 -0
  260. package/src/providers/operations/account-tier.ts +67 -0
  261. package/src/providers/operations/client-versions.ts +37 -99
  262. package/src/providers/operations/cooldown-rules.ts +61 -0
  263. package/src/providers/operations/provider-catalog-seeder.ts +4 -0
  264. package/src/providers/provider-contracts.ts +29 -0
  265. package/src/providers/provider-metadata.ts +59 -4
  266. package/src/providers/provider-model-definition.ts +36 -0
  267. package/src/providers/quota/refresh.ts +21 -0
  268. package/src/security/abuse.ts +71 -0
  269. package/src/transport/dispatch/proxy-request.ts +1 -0
  270. package/src/transport/dispatch/streaming/stream.ts +1 -0
  271. package/src/transport/dispatch/upstream.ts +4 -1
  272. package/src/transport/failure-policy.ts +113 -7
  273. package/src/transport/routing/router.ts +25 -1
  274. package/src/transport/surface/messages/adapter.ts +2 -1
  275. package/src/transport/surface/responses/encode.ts +12 -1
  276. package/src/workers/daily-checkin.ts +1 -1
  277. package/src/workers/quota-refresh-sweep.ts +1 -1
  278. package/src/workers/scheduler.ts +22 -4
  279. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js +0 -1
  280. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js.gz +0 -0
  281. package/dist/dashboard/assets/CliToolDetailPage-BRWKPQPf.js.gz +0 -0
  282. package/dist/dashboard/assets/CliToolsPage-BP-Y5iHN.js.gz +0 -0
  283. package/dist/dashboard/assets/CombosPage-B7JHozVa.js +0 -1
  284. package/dist/dashboard/assets/CombosPage-B7JHozVa.js.gz +0 -0
  285. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js +0 -1
  286. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js.gz +0 -0
  287. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js +0 -1
  288. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js.gz +0 -0
  289. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js +0 -1
  290. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js.gz +0 -0
  291. package/dist/dashboard/assets/InjectSystemPromptPanel-Cv-2Ol_K.js.gz +0 -0
  292. package/dist/dashboard/assets/LoginPage-bKfz_b1K.js.gz +0 -0
  293. package/dist/dashboard/assets/ModelPicker-B-WQxx_H.js.gz +0 -0
  294. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js +0 -1
  295. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js.gz +0 -0
  296. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js +0 -7
  297. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js.gz +0 -0
  298. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +0 -1
  299. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  300. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js +0 -1
  301. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js.gz +0 -0
  302. package/dist/dashboard/assets/ProxyPage-emlTuxz6.js.gz +0 -0
  303. package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js.gz +0 -0
  304. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js +0 -1
  305. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js.gz +0 -0
  306. package/dist/dashboard/assets/SetupPage-CUZrKs9r.js.gz +0 -0
  307. package/dist/dashboard/assets/StudioPage-jWmUdDpy.js.gz +0 -0
  308. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js +0 -56
  309. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js.gz +0 -0
  310. package/dist/dashboard/assets/api-keys-KvX83mRe.js +0 -1
  311. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  312. package/dist/dashboard/assets/app-BEsU8hKs.css +0 -2
  313. package/dist/dashboard/assets/app-BEsU8hKs.css.gz +0 -0
  314. package/dist/dashboard/assets/assert-CidQVoNo.js +0 -1
  315. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  316. package/dist/dashboard/assets/badge-DyI_mJfi.js.gz +0 -0
  317. package/dist/dashboard/assets/banner-B-yXtCqb.js.gz +0 -0
  318. package/dist/dashboard/assets/brand/bansos_page.png +0 -0
  319. package/dist/dashboard/assets/brand/denia_bansos.jpg +0 -0
  320. package/dist/dashboard/assets/brand/favicon.jpg +0 -0
  321. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  322. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  323. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  324. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  325. package/dist/dashboard/assets/brand/sidebar_ico.jpg +0 -0
  326. package/dist/dashboard/assets/card-_HwAEqFF.js.gz +0 -0
  327. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +0 -1
  328. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  329. package/dist/dashboard/assets/clipboard-button-ByjCk0o9.js.gz +0 -0
  330. package/dist/dashboard/assets/dialog-BZhOlfz-.js.gz +0 -0
  331. package/dist/dashboard/assets/drawer-BADqIRYP.js.gz +0 -0
  332. package/dist/dashboard/assets/entry-COaC-irN.js.gz +0 -0
  333. package/dist/dashboard/assets/entry-Ca7LSCet.js +0 -4
  334. package/dist/dashboard/assets/entry-Ca7LSCet.js.gz +0 -0
  335. package/dist/dashboard/assets/entry-PDd27uCd.js +0 -1
  336. package/dist/dashboard/assets/entry-PDd27uCd.js.gz +0 -0
  337. package/dist/dashboard/assets/formatters-DcHJyRiu.js.gz +0 -0
  338. package/dist/dashboard/assets/index-CgKmFngv.js.gz +0 -0
  339. package/dist/dashboard/assets/inline-B2BirKry.js.gz +0 -0
  340. package/dist/dashboard/assets/input-D-qV9dKY.js.gz +0 -0
  341. package/dist/dashboard/assets/layout-B7tVWhtV.js.gz +0 -0
  342. package/dist/dashboard/assets/list-row-CtkGE3bF.js.gz +0 -0
  343. package/dist/dashboard/assets/live-DGizF06-.js.gz +0 -0
  344. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +0 -1
  345. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  346. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  347. package/dist/dashboard/assets/network-C9UE-bOr.js.gz +0 -0
  348. package/dist/dashboard/assets/numbers-BnAd22a_.js.gz +0 -0
  349. package/dist/dashboard/assets/pagination-BAywyMzS.js.gz +0 -0
  350. package/dist/dashboard/assets/query-keys-YSoQOUh6.js.gz +0 -0
  351. package/dist/dashboard/assets/routing-fnCsxgNO.js +0 -1
  352. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  353. package/dist/dashboard/assets/select-BCOcv8WK.js.gz +0 -0
  354. package/dist/dashboard/assets/settings-BxslsErn.js +0 -1
  355. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  356. package/dist/dashboard/assets/stack-ByKt3Q1g.js.gz +0 -0
  357. package/dist/dashboard/assets/state-DXbGTx7N.js.gz +0 -0
  358. package/dist/dashboard/assets/switch-ZeTsW623.js.gz +0 -0
  359. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  360. package/dist/dashboard/console-backgrounds.jpg +0 -0
  361. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  362. package/dist/dashboard/donation/qris-risun-marke.png +0 -0
  363. package/src/providers/integrations/buddy/turing-device-token.ts +0 -262
  364. package/src/providers/integrations/buddy/workbuddy/cn-oauth.ts +0 -30
  365. package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +0 -58
  366. package/src/providers/integrations/buddy/workbuddy/oauth.ts +0 -31
  367. package/src/providers/integrations/buddy/workbuddy/provider.ts +0 -209
  368. package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -161
  369. package/src/providers/integrations/buddy/workbuddy/shared.ts +0 -285
  370. /package/dist/dashboard/assets/{app-CTxoqyYB.js → app-B7kIWFm5.js} +0 -0
  371. /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-B7kIWFm5.js.gz} +0 -0
@@ -0,0 +1,738 @@
1
+ // Cursor-native tool executor — runs Cursor's host tools locally instead of
2
+ // relaying them to the client or refusing them.
3
+ //
4
+ // Cursor asks the local client to execute host tools through `ExecServerMessage`
5
+ // frames (read, shell, write, edit, grep, glob, ls, delete, and the modern
6
+ // `pi_*` family). Previously this gateway relayed those to the caller as tool
7
+ // calls or refused them; this module executes them in-process, inside one
8
+ // workspace root, and returns the protobuf result message the wire expects.
9
+ //
10
+ // Safety model:
11
+ // - Every filesystem operation resolves inside `root` (the gateway's
12
+ // workspace). A path that escapes is refused with the tool's `rejected`
13
+ // or `permission_denied` case — never executed.
14
+ // - Shell commands run through `spawn` with `shell: false` on POSIX and a
15
+ // parsed argv on Windows (no `cmd /c` string interpolation); the working
16
+ // directory is pinned to `root`.
17
+ // - `delete` refuses to delete the workspace root itself, and `edit`/`write`
18
+ // refuse to touch paths outside the root.
19
+ //
20
+ // The result shape is keyed by the origin exec frame (which variant asked),
21
+ // so a reply can only ever come back in the arm it arrived on.
22
+
23
+ import { spawn } from "node:child_process";
24
+ import {
25
+ readFileSync,
26
+ writeFileSync,
27
+ existsSync,
28
+ lstatSync,
29
+ readdirSync,
30
+ unlinkSync,
31
+ mkdirSync,
32
+ realpathSync,
33
+ } from "node:fs";
34
+ import { resolve, join, relative, dirname, isAbsolute } from "node:path";
35
+ import { createHash } from "node:crypto";
36
+
37
+ /** Max output bytes a tool result carries before truncation is reported. */
38
+ export const CURSOR_TOOL_MAX_OUTPUT_BYTES = 64 * 1024;
39
+ /** Max lines a read result reports before truncation. */
40
+ export const CURSOR_TOOL_MAX_LINES = 2000;
41
+
42
+ /**
43
+ * The decoded result of one executed tool, ready to be wrapped in the exec
44
+ * frame's result oneof.
45
+ */
46
+ export interface NativeToolResult {
47
+ /** The ExecClientMessage result field name (e.g. `read_result`, `ct_read_result`). */
48
+ readonly field: string;
49
+ /** The protobuf result message object, keyed by its oneof arm. */
50
+ readonly message: Record<string, unknown>;
51
+ }
52
+
53
+ /** One exec-frame → result-field mapping (wire arms, not tool names). */
54
+ const EXEC_RESULT_FIELD: Readonly<Record<string, string>> = {
55
+ shell_args: "shell_result",
56
+ write_args: "write_result",
57
+ delete_args: "delete_result",
58
+ grep_args: "grep_result",
59
+ read_args: "read_result",
60
+ ls_args: "ls_result",
61
+ // Modern exec arms are namespaced `ct_` (cartethyia) on the wire — the
62
+ // upstream calls them `pi_`, but the canonical tool surface is one name per
63
+ // tool, so the wire suffix here is ours alone.
64
+ ct_read_args: "pi_read_result",
65
+ ct_bash_args: "pi_bash_result",
66
+ ct_edit_args: "pi_edit_result",
67
+ ct_write_args: "pi_write_result",
68
+ ct_grep_args: "pi_grep_result",
69
+ ct_find_args: "pi_find_result",
70
+ ct_ls_args: "pi_ls_result",
71
+ };
72
+
73
+ /**
74
+ * Resolves an `ExecServerMessage` frame to `{ frameKey, args }` when the
75
+ * frame carries an executable tool payload. `frameKey` is the wire key
76
+ * (`read_args`, `pi_read_args`, …); `args` are canonicalised snake-case
77
+ * arguments. Returns `undefined` for non-tool frames (id, exec_id,
78
+ * request_context_args, …).
79
+ */
80
+ export function resolveExecFrame(
81
+ execServer: Record<string, unknown>,
82
+ ): { readonly frameKey: string; readonly args: Record<string, unknown> } | undefined {
83
+ for (const [key, value] of Object.entries(execServer)) {
84
+ if (value === null || value === undefined || !key.endsWith("_args")) continue;
85
+ if (!Object.hasOwn(EXEC_RESULT_FIELD, key)) continue;
86
+ return { frameKey: key, args: canonicalToolArgumentsFrom(value) };
87
+ }
88
+ return undefined;
89
+ }
90
+
91
+ function canonicalToolArgumentsFrom(value: unknown): Record<string, unknown> {
92
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
93
+ return Object.fromEntries(
94
+ Object.entries(value as Record<string, unknown>).map(([key, child]) => [
95
+ key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase(),
96
+ child,
97
+ ]),
98
+ );
99
+ }
100
+
101
+ /** Resolves a tool path against the workspace root; null when it escapes. */
102
+ function resolveInside(root: string, input: string | undefined): string | null {
103
+ if (input === undefined || input.trim().length === 0) return null;
104
+ const raw = input.trim();
105
+ const abs = isAbsolute(raw) ? raw : resolve(root, raw);
106
+ const rel = relative(root, abs);
107
+ if (rel.startsWith("..") || isAbsolute(rel)) return null;
108
+ return abs;
109
+ }
110
+
111
+ /** Best-effort realpath for exists checks; falls back to the resolved path. */
112
+ function safeRealpath(p: string): string {
113
+ try {
114
+ return realpathSync(p);
115
+ } catch {
116
+ return p;
117
+ }
118
+ }
119
+
120
+ function isFile(p: string): boolean {
121
+ try {
122
+ return lstatSync(p).isFile();
123
+ } catch {
124
+ return false;
125
+ }
126
+ }
127
+
128
+ function isDir(p: string): boolean {
129
+ try {
130
+ return lstatSync(p).isDirectory();
131
+ } catch {
132
+ return false;
133
+ }
134
+ }
135
+
136
+ function truncate(text: string, maxBytes: number): { text: string; truncated: boolean } {
137
+ const bytes = Buffer.byteLength(text, "utf8");
138
+ if (bytes <= maxBytes) return { text, truncated: false };
139
+ // Trim by characters so we never split a UTF-8 sequence.
140
+ let out = text;
141
+ while (Buffer.byteLength(out, "utf8") > maxBytes) {
142
+ out = out.slice(0, -1);
143
+ }
144
+ return { text: out, truncated: true };
145
+ }
146
+
147
+ function shellSuccess(args: Record<string, unknown>, cwd: string, stdout: string, stderr: string, exitCode: number, startedMs: number): Record<string, unknown> {
148
+ return {
149
+ success: {
150
+ command: String(args["command"] ?? ""),
151
+ working_directory: String(args["working_directory"] ?? cwd),
152
+ exit_code: exitCode,
153
+ stdout,
154
+ stderr,
155
+ execution_time: Date.now() - startedMs,
156
+ },
157
+ };
158
+ }
159
+
160
+ function shellFailure(args: Record<string, unknown>, cwd: string, stdout: string, stderr: string, exitCode: number, startedMs: number): Record<string, unknown> {
161
+ return {
162
+ failure: {
163
+ command: String(args["command"] ?? ""),
164
+ working_directory: String(args["working_directory"] ?? cwd),
165
+ exit_code: exitCode,
166
+ stdout,
167
+ stderr,
168
+ execution_time: Date.now() - startedMs,
169
+ },
170
+ };
171
+ }
172
+
173
+ /** Parses a command string into an argv array (Windows-aware). */
174
+ function parseCommand(command: string): string[] {
175
+ const argv: string[] = [];
176
+ let current = "";
177
+ let quote: string | null = null;
178
+ for (let i = 0; i < command.length; i++) {
179
+ const ch = command[i]!;
180
+ if (quote !== null) {
181
+ if (ch === quote) quote = null;
182
+ else current += ch;
183
+ continue;
184
+ }
185
+ if (ch === '"' || ch === "'") {
186
+ quote = ch;
187
+ continue;
188
+ }
189
+ if (ch === " " || ch === "\t") {
190
+ if (current.length > 0) {
191
+ argv.push(current);
192
+ current = "";
193
+ }
194
+ continue;
195
+ }
196
+ current += ch;
197
+ }
198
+ if (current.length > 0) argv.push(current);
199
+ return argv;
200
+ }
201
+
202
+ async function runShell(
203
+ args: Record<string, unknown>,
204
+ root: string,
205
+ timeoutMs: number,
206
+ ): Promise<Record<string, unknown>> {
207
+ const command = String(args["command"] ?? "").trim();
208
+ if (!command) return { rejected: { reason: "empty command" } };
209
+ const cwd = String(args["working_directory"] ?? root).trim() || root;
210
+ const cwdAbs = resolveInside(root, cwd);
211
+ if (cwdAbs === null) return { rejected: { command, working_directory: cwd, reason: "working directory escapes the workspace root" } };
212
+
213
+ const started = Date.now();
214
+ const shellTimeout = timeoutMs > 0 ? timeoutMs : 30_000;
215
+ const argv = parseCommand(command);
216
+ if (argv.length === 0) return { rejected: { command, working_directory: cwd, reason: "empty command" } };
217
+
218
+ return await new Promise<Record<string, unknown>>((resolvePromise) => {
219
+ const child = spawn(argv[0]!, argv.slice(1), {
220
+ cwd: cwdAbs,
221
+ shell: false,
222
+ windowsHide: true,
223
+ env: { ...process.env, CURSOR_WORKSPACE_ROOT: root },
224
+ });
225
+ let stdout = "";
226
+ let stderr = "";
227
+ let settled = false;
228
+ const timer = setTimeout(() => {
229
+ if (settled) return;
230
+ settled = true;
231
+ child.kill("SIGKILL");
232
+ resolvePromise({
233
+ timeout: {
234
+ command,
235
+ working_directory: cwd,
236
+ timeout_ms: shellTimeout,
237
+ },
238
+ });
239
+ }, shellTimeout);
240
+ child.stdout.on("data", (d: Buffer) => {
241
+ stdout += d.toString("utf8");
242
+ if (Buffer.byteLength(stdout) > CURSOR_TOOL_MAX_OUTPUT_BYTES * 2) child.stdout.pause();
243
+ });
244
+ child.stderr.on("data", (d: Buffer) => {
245
+ stderr += d.toString("utf8");
246
+ if (Buffer.byteLength(stderr) > CURSOR_TOOL_MAX_OUTPUT_BYTES * 2) child.stderr.pause();
247
+ });
248
+ child.on("error", (err) => {
249
+ if (settled) return;
250
+ settled = true;
251
+ clearTimeout(timer);
252
+ resolvePromise({
253
+ failure: {
254
+ command,
255
+ working_directory: cwd,
256
+ exit_code: -1,
257
+ stdout,
258
+ stderr: `${stderr}\n${err.message}`.trim(),
259
+ execution_time: Date.now() - started,
260
+ },
261
+ });
262
+ });
263
+ child.on("close", (code) => {
264
+ if (settled) return;
265
+ settled = true;
266
+ clearTimeout(timer);
267
+ const out = truncate(stdout, CURSOR_TOOL_MAX_OUTPUT_BYTES);
268
+ const err = truncate(stderr, CURSOR_TOOL_MAX_OUTPUT_BYTES);
269
+ if (code === 0) {
270
+ resolvePromise(shellSuccess(args, cwd, out.text, err.text, 0, started));
271
+ } else {
272
+ resolvePromise(shellFailure(args, cwd, out.text, err.text, code ?? -1, started));
273
+ }
274
+ });
275
+ });
276
+ }
277
+
278
+ async function readFile(
279
+ args: Record<string, unknown>,
280
+ root: string,
281
+ pi: boolean,
282
+ ): Promise<Record<string, unknown>> {
283
+ const path = resolveInside(root, String(args["path"] ?? ""));
284
+ if (path === null) {
285
+ return pi
286
+ ? { error: { error: "path escapes the workspace root" } }
287
+ : { rejected: { path: String(args["path"] ?? ""), reason: "path escapes the workspace root" } };
288
+ }
289
+ if (!existsSync(path)) {
290
+ return pi
291
+ ? { error: { error: `file not found: ${path}` } }
292
+ : { file_not_found: { path } };
293
+ }
294
+ if (!isFile(path)) {
295
+ return pi
296
+ ? { error: { error: `not a regular file: ${path}` } }
297
+ : { invalid_file: { path, reason: "not a regular file" } };
298
+ }
299
+ try {
300
+ const size = lstatSync(path).size;
301
+ const offset = typeof args["offset"] === "number" ? Math.max(0, args["offset"]) : 0;
302
+ const raw = readFileSync(path, "utf8");
303
+ const lines = raw.split("\n");
304
+ let content: string;
305
+ let truncation: Record<string, unknown> | undefined;
306
+ if (offset > 0 || lines.length > CURSOR_TOOL_MAX_LINES) {
307
+ const slice = lines.slice(offset, offset + CURSOR_TOOL_MAX_LINES).join("\n");
308
+ const out = truncate(slice, CURSOR_TOOL_MAX_OUTPUT_BYTES);
309
+ content = out.text;
310
+ if (out.truncated || lines.length > CURSOR_TOOL_MAX_LINES) {
311
+ truncation = {
312
+ truncated: true,
313
+ truncated_by: "line_limit",
314
+ total_lines: lines.length,
315
+ output_lines: Math.min(lines.length - offset, CURSOR_TOOL_MAX_LINES),
316
+ output_bytes: Buffer.byteLength(content),
317
+ max_lines: CURSOR_TOOL_MAX_LINES,
318
+ };
319
+ }
320
+ } else {
321
+ const out = truncate(raw, CURSOR_TOOL_MAX_OUTPUT_BYTES);
322
+ content = out.text;
323
+ if (out.truncated) {
324
+ truncation = {
325
+ truncated: true,
326
+ truncated_by: "byte_limit",
327
+ total_lines: lines.length,
328
+ output_lines: lines.length,
329
+ output_bytes: Buffer.byteLength(content),
330
+ max_bytes: CURSOR_TOOL_MAX_OUTPUT_BYTES,
331
+ };
332
+ }
333
+ }
334
+ if (pi) {
335
+ return truncation
336
+ ? { success: { output: content, truncation } }
337
+ : { success: { output: content } };
338
+ }
339
+ return {
340
+ success: {
341
+ path,
342
+ content,
343
+ total_lines: lines.length,
344
+ file_size: size,
345
+ truncated: truncation !== undefined,
346
+ },
347
+ };
348
+ } catch (err) {
349
+ const msg = err instanceof Error ? err.message : String(err);
350
+ return pi ? { error: { error: msg } } : { error: { path, error: msg } };
351
+ }
352
+ }
353
+
354
+ function globFiles(args: Record<string, unknown>, root: string): Record<string, unknown> {
355
+ const target = resolveInside(root, String(args["target_directory"] ?? "."));
356
+ if (target === null) return { error: "target directory escapes the workspace root" };
357
+ if (!isDir(target)) return { error: `not a directory: ${target}` };
358
+ const pattern = String(args["glob_pattern"] ?? "**/*");
359
+ const out: string[] = [];
360
+ const walk = (dir: string, depth: number) => {
361
+ if (depth > 12) return;
362
+ let entries: string[];
363
+ try {
364
+ entries = readdirSync(dir);
365
+ } catch {
366
+ return;
367
+ }
368
+ for (const entry of entries) {
369
+ const full = join(dir, entry);
370
+ const rel = relative(target, full);
371
+ let isDirEntry = false;
372
+ try {
373
+ isDirEntry = lstatSync(full).isDirectory();
374
+ } catch {
375
+ continue;
376
+ }
377
+ if (isDirEntry) {
378
+ const match = simpleGlobMatch(pattern, rel);
379
+ if (match) out.push(rel);
380
+ walk(full, depth + 1);
381
+ } else {
382
+ const match = simpleGlobMatch(pattern, rel);
383
+ if (match) out.push(rel);
384
+ }
385
+ }
386
+ };
387
+ walk(target, 0);
388
+ out.sort();
389
+ return { files: out };
390
+ }
391
+
392
+ /** Minimal glob matcher: `*` (within a segment), `**` (any depth), `?`. */
393
+ function simpleGlobMatch(pattern: string, value: string): boolean {
394
+ const re = pattern
395
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
396
+ .replace(/\*\*/g, "__GLOBSTAR__")
397
+ .replace(/\*/g, "[^/]*")
398
+ .replace(/__GLOBSTAR__/g, ".*")
399
+ .replace(/\?/g, ".");
400
+ return new RegExp(`^${re}$`).test(value);
401
+ }
402
+
403
+ function grepFiles(args: Record<string, unknown>, root: string, pi: boolean): Record<string, unknown> {
404
+ const pattern = String(args["pattern"] ?? "");
405
+ if (!pattern) return pi ? { error: { error: "empty pattern" } } : { error: { error: "empty pattern" } };
406
+ const path = String(args["path"] ?? "");
407
+ const searchRoot = path ? resolveInside(root, path) : root;
408
+ if (searchRoot === null) {
409
+ return pi ? { error: { error: "path escapes the workspace root" } } : { error: { error: "path escapes the workspace root" } };
410
+ }
411
+ const glob = String(args["glob"] ?? "");
412
+ const ignoreCase = args["ignore_case"] === true || args["case_insensitive"] === true;
413
+ const literal = args["literal"] === true;
414
+ const limit = typeof args["limit"] === "number" ? args["limit"] : 200;
415
+ const flags = ignoreCase ? "gi" : "g";
416
+ const re = literal ? new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), flags) : new RegExp(pattern, flags);
417
+ const matches: Array<{ file: string; matches: Array<{ lineNumber: number; content: string }> }> = [];
418
+ let totalMatched = 0;
419
+
420
+ const walk = (dir: string, depth: number) => {
421
+ if (depth > 12 || totalMatched >= limit) return;
422
+ let entries: string[];
423
+ try {
424
+ entries = readdirSync(dir);
425
+ } catch {
426
+ return;
427
+ }
428
+ for (const entry of entries) {
429
+ if (totalMatched >= limit) return;
430
+ if (entry.startsWith(".")) continue;
431
+ const full = join(dir, entry);
432
+ const rel = relative(searchRoot, full);
433
+ let isDirEntry = false;
434
+ try {
435
+ isDirEntry = lstatSync(full).isDirectory();
436
+ } catch {
437
+ continue;
438
+ }
439
+ if (isDirEntry) {
440
+ walk(full, depth + 1);
441
+ continue;
442
+ }
443
+ if (glob && !simpleGlobMatch(glob, rel)) continue;
444
+ try {
445
+ const content = readFileSync(full, "utf8");
446
+ const lines = content.split("\n");
447
+ const fileMatches: Array<{ lineNumber: number; content: string }> = [];
448
+ for (let i = 0; i < lines.length && fileMatches.length < 20; i++) {
449
+ re.lastIndex = 0;
450
+ if (re.test(lines[i]!)) {
451
+ fileMatches.push({ lineNumber: i + 1, content: lines[i]!.slice(0, 500) });
452
+ }
453
+ }
454
+ if (fileMatches.length > 0) {
455
+ matches.push({ file: rel, matches: fileMatches });
456
+ totalMatched += fileMatches.length;
457
+ }
458
+ } catch {
459
+ // unreadable file — skip
460
+ }
461
+ }
462
+ };
463
+ walk(searchRoot, 0);
464
+
465
+ if (pi) {
466
+ const lines = matches.flatMap((m) => m.matches.map((mm) => `${m.file}:${mm.lineNumber}:${mm.content}`));
467
+ const out = truncate(lines.join("\n"), CURSOR_TOOL_MAX_OUTPUT_BYTES);
468
+ return { success: { output: out.text, truncation: out.truncated ? { truncated: true, truncated_by: "byte_limit" } : undefined } };
469
+ }
470
+ return {
471
+ success: {
472
+ pattern,
473
+ path: path || root,
474
+ output_mode: "content",
475
+ workspace_results: {
476
+ "": {
477
+ content: {
478
+ matches,
479
+ total_lines: totalMatched,
480
+ total_matched_lines: totalMatched,
481
+ client_truncated: totalMatched >= limit,
482
+ ripgrep_truncated: false,
483
+ },
484
+ },
485
+ },
486
+ },
487
+ };
488
+ }
489
+
490
+ function lsTree(args: Record<string, unknown>, root: string, pi: boolean): Record<string, unknown> {
491
+ const path = resolveInside(root, String(args["path"] ?? "."));
492
+ if (path === null) {
493
+ return pi ? { error: { error: "path escapes the workspace root" } } : { rejected: { path: String(args["path"] ?? ""), reason: "path escapes the workspace root" } };
494
+ }
495
+ if (!isDir(path)) {
496
+ return pi ? { error: { error: `not a directory: ${path}` } } : { error: { path, error: "not a directory" } };
497
+ }
498
+ const buildNode = (dir: string): Record<string, unknown> => {
499
+ const childrenDirs: Record<string, unknown>[] = [];
500
+ const childrenFiles: Array<{ name: string }> = [];
501
+ let entries: string[];
502
+ try {
503
+ entries = readdirSync(dir).sort();
504
+ } catch {
505
+ entries = [];
506
+ }
507
+ for (const entry of entries) {
508
+ const full = join(dir, entry);
509
+ try {
510
+ if (lstatSync(full).isDirectory()) childrenDirs.push(buildNode(full));
511
+ else childrenFiles.push({ name: entry });
512
+ } catch {
513
+ // skip
514
+ }
515
+ }
516
+ return {
517
+ abs_path: dir,
518
+ children_dirs: childrenDirs,
519
+ children_files: childrenFiles,
520
+ num_files: childrenFiles.length,
521
+ };
522
+ };
523
+ const tree = buildNode(path);
524
+ if (pi) {
525
+ const lines: string[] = [];
526
+ const flatten = (node: Record<string, unknown>, prefix: string) => {
527
+ for (const d of (node["children_dirs"] ?? []) as Record<string, unknown>[]) {
528
+ lines.push(`${prefix}${d["abs_path"]}/`);
529
+ flatten(d, `${prefix} `);
530
+ }
531
+ for (const f of (node["children_files"] ?? []) as Array<{ name: string }>) {
532
+ lines.push(`${prefix}${f.name}`);
533
+ }
534
+ };
535
+ flatten(tree, "");
536
+ const out = truncate(lines.join("\n"), CURSOR_TOOL_MAX_OUTPUT_BYTES);
537
+ return { success: { output: out.text, truncation: out.truncated ? { truncated: true, truncated_by: "byte_limit" } : undefined } };
538
+ }
539
+ return { success: { directory_tree_root: tree } };
540
+ }
541
+
542
+ function editFile(args: Record<string, unknown>, root: string, pi: boolean): Record<string, unknown> {
543
+ const path = resolveInside(root, String(args["path"] ?? ""));
544
+ if (path === null) {
545
+ return pi ? { rejected: { reason: "path escapes the workspace root" } } : { rejected: { path: String(args["path"] ?? ""), reason: "path escapes the workspace root" } };
546
+ }
547
+ if (!existsSync(path)) {
548
+ return pi ? { error: { error: `file not found: ${path}` } } : { file_not_found: { path } };
549
+ }
550
+ if (!isFile(path)) {
551
+ return pi ? { error: { error: `not a regular file: ${path}` } } : { invalid_file: { path, reason: "not a regular file" } };
552
+ }
553
+ try {
554
+ const original = readFileSync(path, "utf8");
555
+ let content = original;
556
+ // Legacy edit: stream_content is the *new full content* (old EditToolArgs
557
+ // semantics — the IDE sends the entire replacement file).
558
+ if (typeof args["stream_content"] === "string" && !pi) {
559
+ content = args["stream_content"];
560
+ } else {
561
+ const edits = (args["edits"] ?? []) as Array<{ old_text?: string; new_text?: string }>;
562
+ for (const edit of edits) {
563
+ const oldText = edit.old_text ?? "";
564
+ const newText = edit.new_text ?? "";
565
+ if (!oldText) continue;
566
+ if (!content.includes(oldText)) {
567
+ return pi
568
+ ? { error: { error: `old_text not found in ${path}` } }
569
+ : { error: { path, error: `old_text not found in ${path}` } };
570
+ }
571
+ content = content.replace(oldText, newText);
572
+ }
573
+ }
574
+ writeFileSync(path, content, "utf8");
575
+ const lines = content.split("\n");
576
+ const out = truncate(content, CURSOR_TOOL_MAX_OUTPUT_BYTES);
577
+ if (pi) {
578
+ return {
579
+ success: {
580
+ output: out.text,
581
+ truncation: out.truncated ? { truncated: true, truncated_by: "byte_limit" } : undefined,
582
+ },
583
+ };
584
+ }
585
+ return {
586
+ success: {
587
+ path,
588
+ content: out.text,
589
+ total_lines: lines.length,
590
+ file_size: Buffer.byteLength(content),
591
+ truncated: out.truncated,
592
+ },
593
+ };
594
+ } catch (err) {
595
+ const msg = err instanceof Error ? err.message : String(err);
596
+ return pi ? { error: { error: msg } } : { error: { path, error: msg } };
597
+ }
598
+ }
599
+
600
+ function writeFileTool(args: Record<string, unknown>, root: string, pi: boolean): Record<string, unknown> {
601
+ const path = resolveInside(root, String(args["path"] ?? ""));
602
+ if (path === null) {
603
+ return pi ? { rejected: { reason: "path escapes the workspace root" } } : { rejected: { path: String(args["path"] ?? ""), reason: "path escapes the workspace root" } };
604
+ }
605
+ let content = "";
606
+ if (typeof args["content"] === "string") content = args["content"];
607
+ else if (typeof args["file_text"] === "string") content = args["file_text"];
608
+ else if (args["file_bytes"] instanceof Uint8Array) content = new TextDecoder().decode(args["file_bytes"] as Uint8Array);
609
+ try {
610
+ mkdirSync(dirname(path), { recursive: true });
611
+ writeFileSync(path, content, "utf8");
612
+ const lines = content.split("\n");
613
+ if (pi) {
614
+ const out = truncate(content, CURSOR_TOOL_MAX_OUTPUT_BYTES);
615
+ return {
616
+ success: {
617
+ output: out.text,
618
+ truncation: out.truncated ? { truncated: true, truncated_by: "byte_limit" } : undefined,
619
+ },
620
+ };
621
+ }
622
+ return {
623
+ success: {
624
+ path,
625
+ lines_created: lines.length,
626
+ file_size: Buffer.byteLength(content),
627
+ file_content_after_write: truncate(content, CURSOR_TOOL_MAX_OUTPUT_BYTES).text,
628
+ },
629
+ };
630
+ } catch (err) {
631
+ const msg = err instanceof Error ? err.message : String(err);
632
+ return pi ? { error: { error: msg } } : { error: { path, error: msg } };
633
+ }
634
+ }
635
+
636
+ function deleteFileTool(args: Record<string, unknown>, root: string): Record<string, unknown> {
637
+ const path = resolveInside(root, String(args["path"] ?? ""));
638
+ if (path === null) {
639
+ return { rejected: { path: String(args["path"] ?? ""), reason: "path escapes the workspace root" } };
640
+ }
641
+ if (path === safeRealpath(root)) {
642
+ return { permission_denied: { path, client_visible_error: "refusing to delete the workspace root", is_readonly: true } };
643
+ }
644
+ if (!existsSync(path)) {
645
+ return { error: { path, error: "file not found" } };
646
+ }
647
+ try {
648
+ unlinkSync(path);
649
+ return { success: { path, deleted_file: path } };
650
+ } catch (err) {
651
+ return { error: { path, error: err instanceof Error ? err.message : String(err) } };
652
+ }
653
+ }
654
+
655
+ /**
656
+ * Executes one Cursor-native exec frame. `frameKey` is the exec-frame key
657
+ * (`read_args`, `pi_read_args`, …); `args` are the decoded canonical (snake)
658
+ * arguments. Returns the result message for the frame's result oneof, or
659
+ * `undefined` when the frame is not executable (the caller refuses it).
660
+ */
661
+ export async function executeNativeTool(
662
+ frameKey: string,
663
+ args: Record<string, unknown>,
664
+ root: string,
665
+ timeoutMs = 30_000,
666
+ ): Promise<NativeToolResult | undefined> {
667
+ const field = EXEC_RESULT_FIELD[frameKey];
668
+ if (field === undefined) return undefined;
669
+
670
+ let message: Record<string, unknown>;
671
+ switch (frameKey) {
672
+ case "read_args":
673
+ message = await readFile(args, root, false);
674
+ break;
675
+ case "ct_read_args":
676
+ message = await readFile(args, root, true);
677
+ break;
678
+ case "shell_args":
679
+ message = await runShell(args, root, timeoutMs);
680
+ break;
681
+ case "ct_bash_args":
682
+ message = await runShell(args, root, timeoutMs);
683
+ break;
684
+ case "glob_args":
685
+ message = globFiles(args, root);
686
+ break;
687
+ case "grep_args":
688
+ message = grepFiles(args, root, false);
689
+ break;
690
+ case "ct_grep_args":
691
+ message = grepFiles(args, root, true);
692
+ break;
693
+ case "ls_args":
694
+ message = lsTree(args, root, false);
695
+ break;
696
+ case "ct_ls_args":
697
+ message = lsTree(args, root, true);
698
+ break;
699
+ case "edit_args":
700
+ message = editFile(args, root, false);
701
+ break;
702
+ case "ct_edit_args":
703
+ message = editFile(args, root, true);
704
+ break;
705
+ case "write_args":
706
+ message = writeFileTool(args, root, false);
707
+ break;
708
+ case "ct_write_args":
709
+ message = writeFileTool(args, root, true);
710
+ break;
711
+ case "delete_args":
712
+ message = deleteFileTool(args, root);
713
+ break;
714
+ case "ct_find_args": {
715
+ const path = resolveInside(root, String(args["path"] ?? "."));
716
+ if (path === null) message = { error: { error: "path escapes the workspace root" } };
717
+ else {
718
+ const pattern = String(args["pattern"] ?? "*");
719
+ const files = globFiles({ target_directory: path, glob_pattern: pattern }, root);
720
+ message = { success: { output: (files["files"] as string[] ?? []).join("\n") } };
721
+ }
722
+ break;
723
+ }
724
+ default:
725
+ return undefined;
726
+ }
727
+
728
+ return { field, message };
729
+ }
730
+
731
+ /** Blobs a file for output-blob results (unused here; kept for symmetry). */
732
+ export function fileDigest(path: string): string {
733
+ try {
734
+ return createHash("sha256").update(readFileSync(path)).digest("hex");
735
+ } catch {
736
+ return "";
737
+ }
738
+ }