specrails-desktop 2.16.0 → 2.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (424) hide show
  1. package/client/dist/assets/{ActivityFeedPage-C_clKk8d.js → ActivityFeedPage-mGnv5SOe.js} +1 -1
  2. package/client/dist/assets/AgentsPage-BH1isg5j.js +86 -0
  3. package/client/dist/assets/AnalyticsPage-ByyshYRI.js +1 -0
  4. package/client/dist/assets/{BarChart-Dq1h8eYR.js → BarChart-BqHUOnl9.js} +3 -3
  5. package/client/dist/assets/CodePage-CU7T_HYs.js +2 -0
  6. package/client/dist/assets/{DesktopAnalyticsPage-CYhXhJoQ.js → DesktopAnalyticsPage-CR-m97Ui.js} +1 -1
  7. package/client/dist/assets/DocsDialog-CEmZp_8V.js +11 -0
  8. package/client/dist/assets/DocsPage-DYbAQ1AT.js +11 -0
  9. package/client/dist/assets/{ExportDropdown-DdB2ncYh.js → ExportDropdown-C-o1Q4ef.js} +1 -1
  10. package/client/dist/assets/IntegrationsPage-C6fKdwVH.js +3 -0
  11. package/client/dist/assets/JobDetailPage-CKfIfBgE.js +16 -0
  12. package/client/dist/assets/JobsPage-nBng0riZ.js +1 -0
  13. package/client/dist/assets/LoopBuilderPage-Co_TYqYk.js +7 -0
  14. package/client/dist/assets/LoopsPage-a2hd37Ya.js +1 -0
  15. package/client/dist/assets/{activity-Ca2XoBi-.js → activity-D8vQJrpW.js} +1 -1
  16. package/client/dist/assets/{addon-image-biWX2UUm.js → addon-image-Ba-TY3wr.js} +1 -1
  17. package/client/dist/assets/{addon-ligatures-D4EiDb5r.js → addon-ligatures-T4ARLiuu.js} +1 -1
  18. package/client/dist/assets/{addon-webgl-CDCIZQ03.js → addon-webgl-Cd73n8eh.js} +1 -1
  19. package/client/dist/assets/{addspec-DE_VfIyP.js → addspec-BloGn_ey.js} +1 -1
  20. package/client/dist/assets/agent-9WdSxPU5.js +1 -0
  21. package/client/dist/assets/agent-ASIyVcg0.js +1 -0
  22. package/client/dist/assets/agent-Bm2tPrdr.js +1 -0
  23. package/client/dist/assets/agent-C4bO1l_v.js +1 -0
  24. package/client/dist/assets/agent-CgKsh88Z2.js +1 -0
  25. package/client/dist/assets/agent-DMUrz9ZS2.js +1 -0
  26. package/client/dist/assets/agent-Dwkz5mPS.js +1 -0
  27. package/client/dist/assets/agent-pO8x4jbR.js +1 -0
  28. package/client/dist/assets/{agents-DcoUUFZl.js → agents-52t8NVpS.js} +1 -1
  29. package/client/dist/assets/{agents-DCX9D3-Q.js → agents-BAxAwHy-.js} +1 -1
  30. package/client/dist/assets/{agents-Jc-CjBDB.js → agents-BUYBb825.js} +1 -1
  31. package/client/dist/assets/{agents-CIhRSOna.js → agents-BjT_xLjv.js} +1 -1
  32. package/client/dist/assets/{agents-BYjUOrLD.js → agents-C07al8rI.js} +1 -1
  33. package/client/dist/assets/{agents-1lhqEcXH.js → agents-DfGvYtpq2.js} +1 -1
  34. package/client/dist/assets/{agents-CQlu1nOm.js → agents-Dli56gr82.js} +1 -1
  35. package/client/dist/assets/{agents-DVYpVGur.js → agents-Eqw_AQ6H.js} +1 -1
  36. package/client/dist/assets/{agentstudio-DCTpIqOx.js → agentstudio-CZm2rOzt.js} +1 -1
  37. package/client/dist/assets/{aiedit-Dirqf3Bm.js → aiedit-DYtnZU9H.js} +1 -1
  38. package/client/dist/assets/{analytics-BE49LbUk.js → analytics-B4FJ_pry.js} +1 -1
  39. package/client/dist/assets/{attachments-C2JPppoP.js → attachments-Ci6_42Kh.js} +1 -1
  40. package/client/dist/assets/{browser-Dpo_ug4c.js → browser-ZB1afzjZ.js} +1 -1
  41. package/client/dist/assets/{chat-mq1x9cxZ.js → chat-VMflhzxn.js} +1 -1
  42. package/client/dist/assets/chunk-BNv3lrIs.js +1 -0
  43. package/client/dist/assets/{code-DtIatVES.js → code-wtcs0ivb.js} +1 -1
  44. package/client/dist/assets/commands-Bc8y9eNo2.js +1 -0
  45. package/client/dist/assets/commands-Bl3-KVfj.js +1 -0
  46. package/client/dist/assets/commands-C5WQ9blx2.js +1 -0
  47. package/client/dist/assets/commands-CM99Upsz2.js +1 -0
  48. package/client/dist/assets/commands-Cc2WpuXa.js +1 -0
  49. package/client/dist/assets/commands-CckaBWdV.js +1 -0
  50. package/client/dist/assets/commands-DASXJD-l.js +1 -0
  51. package/client/dist/assets/commands-k9wbNYoi.js +1 -0
  52. package/client/dist/assets/{common-kftoE0Q-.js → common-BPQQkgJZ.js} +1 -1
  53. package/client/dist/assets/{cssMode-DSHXamUH.js → cssMode-DsArsSCT.js} +1 -1
  54. package/client/dist/assets/{dashboard-BQ3Oa8CE.js → dashboard-Cyzp4ox9.js} +1 -1
  55. package/client/dist/assets/{dist-js-Ele_sHO7.js → dist-js-BHhtSerX.js} +1 -1
  56. package/client/dist/assets/{dist-js-D27I-bfi.js → dist-js-CUegfF6k.js} +1 -1
  57. package/client/dist/assets/{editor.api2-Cwla1nvV.js → editor.api2-T7u8kQWG.js} +1 -1
  58. package/client/dist/assets/{editor.main-Cd8qBRAT.js → editor.main-G3Sl9C67.js} +2 -2
  59. package/client/dist/assets/{explore-CBYU76JD.js → explore-BKVV5JSu.js} +1 -1
  60. package/client/dist/assets/{freemarker2-CkEKTBm2.js → freemarker2-pBzQNgFi.js} +1 -1
  61. package/client/dist/assets/{handlebars-BRGx0JpL.js → handlebars-CtBTCnBI.js} +1 -1
  62. package/client/dist/assets/{html-BfRIaGgN.js → html-Dx3DYBzY.js} +1 -1
  63. package/client/dist/assets/{htmlMode-BTpSnsDE.js → htmlMode-In7frFOo.js} +1 -1
  64. package/client/dist/assets/index-B0FxQALR.css +2 -0
  65. package/client/dist/assets/index-B7vB20Jw.js +152 -0
  66. package/client/dist/assets/{integrations-jJGaKwff.js → integrations-CuWnMR5L.js} +1 -1
  67. package/client/dist/assets/{javascript-DMmE2kKN.js → javascript-BKcn-YkN.js} +1 -1
  68. package/client/dist/assets/{jira-BwACz875.js → jira-DXMoAKWD.js} +1 -1
  69. package/client/dist/assets/{jobs-DRInW0AE.js → jobs---dwa4Nf.js} +1 -1
  70. package/client/dist/assets/{jsonMode-hYuaUleY.js → jsonMode-jNREoQ3C.js} +1 -1
  71. package/client/dist/assets/{lib-qdRfWwE1.js → lib-PQoHlR5u.js} +2 -2
  72. package/client/dist/assets/{liquid-C-VbuTr7.js → liquid-CtRkGqBu.js} +1 -1
  73. package/client/dist/assets/{loops-DfGadne0.js → loops-D8Iu6Jss.js} +1 -1
  74. package/client/dist/assets/{lspLanguageFeatures-BALx-vjn.js → lspLanguageFeatures-Cl3gvD_W.js} +1 -1
  75. package/client/dist/assets/mcp-B4irfLJ2.js +1 -0
  76. package/client/dist/assets/mcp-B6nwJJmR.js +1 -0
  77. package/client/dist/assets/mcp-BOoEGTSQ.js +1 -0
  78. package/client/dist/assets/mcp-BP7PpbDr2.js +1 -0
  79. package/client/dist/assets/mcp-CIH0pH1K.js +1 -0
  80. package/client/dist/assets/mcp-CNgFiTEh.js +1 -0
  81. package/client/dist/assets/mcp-D3E4_Mmk2.js +1 -0
  82. package/client/dist/assets/mcp-mupPuPDO2.js +1 -0
  83. package/client/dist/assets/{mdx-Cs8MNCzn.js → mdx-B_nJldHz.js} +1 -1
  84. package/client/dist/assets/{monaco.contribution-CIvCAJs4.js → monaco.contribution-PUeaZk8T.js} +2 -2
  85. package/client/dist/assets/{nav-CCz4krPP.js → nav-DE3yWQw6.js} +1 -1
  86. package/client/dist/assets/{python-CGShLxuI.js → python-DS1Ce-A6.js} +1 -1
  87. package/client/dist/assets/{razor-CvOBQGd5.js → razor-L41Qg9OK.js} +1 -1
  88. package/client/dist/assets/settings-BKmORxlB2.js +1 -0
  89. package/client/dist/assets/settings-BVv2JgQ8.js +1 -0
  90. package/client/dist/assets/settings-Bghm3pAV.js +1 -0
  91. package/client/dist/assets/settings-BuC_aplX.js +1 -0
  92. package/client/dist/assets/settings-Bw_Qk8E32.js +1 -0
  93. package/client/dist/assets/settings-CAKgJv7p.js +1 -0
  94. package/client/dist/assets/settings-CEUgk1Ck.js +1 -0
  95. package/client/dist/assets/settings-C_PDE20s2.js +1 -0
  96. package/client/dist/assets/setup-4us-Bz3H.js +1 -0
  97. package/client/dist/assets/setup-BYoDYoPY2.js +1 -0
  98. package/client/dist/assets/setup-Bf9eLKxU.js +1 -0
  99. package/client/dist/assets/setup-C5UbM0RP.js +1 -0
  100. package/client/dist/assets/setup-CqGrWb6G.js +1 -0
  101. package/client/dist/assets/setup-JFkWWZnR.js +1 -0
  102. package/client/dist/assets/setup-faMApo0U2.js +1 -0
  103. package/client/dist/assets/setup-tloaTobG2.js +1 -0
  104. package/client/dist/assets/{specs-C_8j4ELZ.js → specs-BYeAbIEy.js} +1 -1
  105. package/client/dist/assets/{terminal-Cj4HzP3p.js → terminal-DFPNRLKp.js} +1 -1
  106. package/client/dist/assets/{tickets-DBQXMYYA.js → tickets-CkfbISuK.js} +1 -1
  107. package/client/dist/assets/{tsMode-04uOpiiP.js → tsMode-BC1g186U.js} +1 -1
  108. package/client/dist/assets/{typescript-CX0CwyIZ.js → typescript-BGlRGvJW.js} +1 -1
  109. package/client/dist/assets/upload-PnJDPHZH.js +1 -0
  110. package/client/dist/assets/{value-BRVOAvhY.js → value-BTDefcOV.js} +1 -1
  111. package/client/dist/assets/{workers-yr2u4c3C.js → workers-CXcXtO3b.js} +1 -1
  112. package/client/dist/assets/{xml-C0OFlt5X.js → xml-JdZZaIhJ.js} +1 -1
  113. package/client/dist/assets/{yaml-DgabaPs5.js → yaml-Cm82BVBt.js} +1 -1
  114. package/client/dist/index.html +30 -28
  115. package/docs/README.md +2 -0
  116. package/docs/guide/de/integrations/5-mcp-server.md +74 -0
  117. package/docs/guide/de/integrations/6-agent-chat.md +64 -0
  118. package/docs/guide/en/integrations/5-mcp-server.md +74 -0
  119. package/docs/guide/en/integrations/6-agent-chat.md +64 -0
  120. package/docs/guide/es/integrations/5-mcp-server.md +74 -0
  121. package/docs/guide/es/integrations/6-agent-chat.md +64 -0
  122. package/docs/guide/fr/integrations/5-mcp-server.md +74 -0
  123. package/docs/guide/fr/integrations/6-agent-chat.md +64 -0
  124. package/docs/guide/it/integrations/5-mcp-server.md +74 -0
  125. package/docs/guide/it/integrations/6-agent-chat.md +64 -0
  126. package/docs/guide/ja/integrations/5-mcp-server.md +74 -0
  127. package/docs/guide/ja/integrations/6-agent-chat.md +64 -0
  128. package/docs/guide/pt/integrations/5-mcp-server.md +74 -0
  129. package/docs/guide/pt/integrations/6-agent-chat.md +64 -0
  130. package/docs/guide/zh/integrations/5-mcp-server.md +74 -0
  131. package/docs/guide/zh/integrations/6-agent-chat.md +64 -0
  132. package/docs/mcp.md +249 -0
  133. package/package.json +8 -5
  134. package/server/dist/agent-chat-manager.js +224 -0
  135. package/server/dist/agent-chat-router.js +148 -0
  136. package/server/dist/agent-cwd-manager.js +94 -0
  137. package/server/dist/agent-mcp-config.js +208 -0
  138. package/server/dist/agent-store.js +59 -0
  139. package/server/dist/agent-tier.js +86 -0
  140. package/server/dist/desktop-db.js +27 -0
  141. package/server/dist/index.js +38 -0
  142. package/server/dist/mcp/guide.js +82 -0
  143. package/server/dist/mcp/index.js +21 -0
  144. package/server/dist/mcp/mcp-admin-router.js +71 -0
  145. package/server/dist/mcp/mcp-server.js +143 -0
  146. package/server/dist/mcp/mcp-tiers.js +48 -0
  147. package/server/dist/mcp/mcp-token.js +119 -0
  148. package/server/dist/mcp/resources.js +24 -0
  149. package/server/dist/mcp/tools/agents.js +319 -0
  150. package/server/dist/mcp/tools/analytics.js +168 -0
  151. package/server/dist/mcp/tools/app-settings.js +81 -0
  152. package/server/dist/mcp/tools/catalog.js +45 -0
  153. package/server/dist/mcp/tools/chat.js +147 -0
  154. package/server/dist/mcp/tools/code.js +145 -0
  155. package/server/dist/mcp/tools/jira.js +222 -0
  156. package/server/dist/mcp/tools/jobs.js +258 -0
  157. package/server/dist/mcp/tools/loops.js +229 -0
  158. package/server/dist/mcp/tools/meta.js +96 -0
  159. package/server/dist/mcp/tools/plugins.js +83 -0
  160. package/server/dist/mcp/tools/projects.js +62 -0
  161. package/server/dist/mcp/tools/rails.js +181 -0
  162. package/server/dist/mcp/tools/setup.js +167 -0
  163. package/server/dist/mcp/tools/specs.js +327 -0
  164. package/server/dist/mcp/tools/types.js +156 -0
  165. package/server/dist/mcp/tools/watch.js +93 -0
  166. package/server/dist/mobile/mobile-event-bus.js +2 -0
  167. package/server/dist/project-router-setup.js +15 -1
  168. package/server/dist/project-router-tickets.js +2 -2
  169. package/server/dist/providers/codex-adapter.js +19 -3
  170. package/server/dist/rails-router.js +5 -0
  171. package/server/dist/setup-manager.js +28 -0
  172. package/server/dist/spawn-lifecycle.js +21 -13
  173. package/server/dist/workspace-manager.js +11 -0
  174. package/client/dist/assets/AgentsPage-jcPUtPhP.js +0 -86
  175. package/client/dist/assets/AnalyticsPage-DZkVU3yd.js +0 -1
  176. package/client/dist/assets/CodePage-BcbZT88K.js +0 -2
  177. package/client/dist/assets/DocsDialog-CS3Syf61.js +0 -11
  178. package/client/dist/assets/DocsPage-l3ZcTO2E.js +0 -11
  179. package/client/dist/assets/IntegrationsPage-CJ_Om1zu.js +0 -3
  180. package/client/dist/assets/JobDetailPage-D5Cxw0md.js +0 -16
  181. package/client/dist/assets/JobsPage-3v_iX83r.js +0 -1
  182. package/client/dist/assets/LoopBuilderPage-4s_adZy5.js +0 -7
  183. package/client/dist/assets/LoopsPage-DA94-o0j.js +0 -1
  184. package/client/dist/assets/chunk-CilyBKbf.js +0 -1
  185. package/client/dist/assets/commands-B9XEyMj-.js +0 -1
  186. package/client/dist/assets/commands-BI1Ta-E7.js +0 -1
  187. package/client/dist/assets/commands-BdI0CtOL.js +0 -1
  188. package/client/dist/assets/commands-C2ewlyXo.js +0 -1
  189. package/client/dist/assets/commands-CwypztRT.js +0 -1
  190. package/client/dist/assets/commands-DSX3yJyr.js +0 -1
  191. package/client/dist/assets/commands-Ic1DJDxm.js +0 -1
  192. package/client/dist/assets/commands-yNn4IA4C.js +0 -1
  193. package/client/dist/assets/index-BRMyOS1B.css +0 -2
  194. package/client/dist/assets/index-meTWPBzY.js +0 -144
  195. package/client/dist/assets/settings-0m2iuTzd.js +0 -1
  196. package/client/dist/assets/settings-BA1V6KdK.js +0 -1
  197. package/client/dist/assets/settings-BN7OusER.js +0 -1
  198. package/client/dist/assets/settings-Bi5FFkIw.js +0 -1
  199. package/client/dist/assets/settings-CRJxyTNN.js +0 -1
  200. package/client/dist/assets/settings-DyBxunCh.js +0 -1
  201. package/client/dist/assets/settings-fk-WlnUt.js +0 -1
  202. package/client/dist/assets/settings-iQe5Dim-.js +0 -1
  203. package/client/dist/assets/setup-BOmQ4vh2.js +0 -1
  204. package/client/dist/assets/setup-BwO2-kuK.js +0 -1
  205. package/client/dist/assets/setup-CUTNWz-0.js +0 -1
  206. package/client/dist/assets/setup-Cf3hjChU.js +0 -1
  207. package/client/dist/assets/setup-CsuXrbJ2.js +0 -1
  208. package/client/dist/assets/setup-Df4cJOqu.js +0 -1
  209. package/client/dist/assets/setup-Do0fWx6W.js +0 -1
  210. package/client/dist/assets/setup-GlkDhBlJ.js +0 -1
  211. package/client/dist/assets/upload-DPG87J4t.js +0 -1
  212. /package/client/dist/assets/{abap-CK68cr9-.js → abap-CpHUwCVK.js} +0 -0
  213. /package/client/dist/assets/{activity-Di2f50_Z.js → activity-B82z3Pna.js} +0 -0
  214. /package/client/dist/assets/{activity-B2MvEH54.js → activity-BId0pwuS2.js} +0 -0
  215. /package/client/dist/assets/{activity-D4dhNEwO.js → activity-BVpZrs0J.js} +0 -0
  216. /package/client/dist/assets/{activity-DHjfmAyT.js → activity-BySXFT2J2.js} +0 -0
  217. /package/client/dist/assets/{activity-usnNa7FX.js → activity-DIWnV0Pl.js} +0 -0
  218. /package/client/dist/assets/{activity-C7VdmeGb.js → activity-Uj576ckT.js} +0 -0
  219. /package/client/dist/assets/{activity-BbNkUmW4.js → activity-w1qMFwxh.js} +0 -0
  220. /package/client/dist/assets/{addspec-CO1xv6BL.js → addspec-B9dY_F5T.js} +0 -0
  221. /package/client/dist/assets/{addspec-CNyAUGz_.js → addspec-B9nhMikb.js} +0 -0
  222. /package/client/dist/assets/{addspec-IozoiiGs.js → addspec-BtNaJHu2.js} +0 -0
  223. /package/client/dist/assets/{addspec-BxMn39wn.js → addspec-D43TlMVp2.js} +0 -0
  224. /package/client/dist/assets/{addspec-1HEtmLv1.js → addspec-DfgIZ5lO.js} +0 -0
  225. /package/client/dist/assets/{addspec-DfuR6EAl.js → addspec-W0r16BOC2.js} +0 -0
  226. /package/client/dist/assets/{addspec-DAveBiCE.js → addspec-tbxr3sHa.js} +0 -0
  227. /package/client/dist/assets/{agentstudio-DCyDW8sy.js → agentstudio-0o3XKrcy.js} +0 -0
  228. /package/client/dist/assets/{agentstudio-9NbsoGXk.js → agentstudio-Bu8m5jDw.js} +0 -0
  229. /package/client/dist/assets/{agentstudio-B1y0JWY-.js → agentstudio-C7xxqVWn.js} +0 -0
  230. /package/client/dist/assets/{agentstudio-1E-bag77.js → agentstudio-D-UoNAIA.js} +0 -0
  231. /package/client/dist/assets/{agentstudio-B-jQgYV-.js → agentstudio-OJjjz6Uf2.js} +0 -0
  232. /package/client/dist/assets/{agentstudio-Q0Sq_jTg.js → agentstudio-ObTpVbBP.js} +0 -0
  233. /package/client/dist/assets/{agentstudio-Bwa72b1S.js → agentstudio-PzFB3vfh2.js} +0 -0
  234. /package/client/dist/assets/{aiedit-DUpe7NxI.js → aiedit-BIQqv2XM2.js} +0 -0
  235. /package/client/dist/assets/{aiedit-kAU7KADg.js → aiedit-BaLZalLa.js} +0 -0
  236. /package/client/dist/assets/{aiedit-CvSspQmE.js → aiedit-BjlUzasF.js} +0 -0
  237. /package/client/dist/assets/{aiedit-DSLtsYIu.js → aiedit-BmDufyGU.js} +0 -0
  238. /package/client/dist/assets/{aiedit-qTGI_PGs.js → aiedit-DAM_WRAl2.js} +0 -0
  239. /package/client/dist/assets/{aiedit-CfBNanHE.js → aiedit-DRrNiaB4.js} +0 -0
  240. /package/client/dist/assets/{aiedit-CGxeml8D.js → aiedit-sP_iao2j.js} +0 -0
  241. /package/client/dist/assets/{analytics-CgEkAUiP.js → analytics-BDqqCzN9.js} +0 -0
  242. /package/client/dist/assets/{analytics-DEsEAJZL.js → analytics-B_rxNC9X.js} +0 -0
  243. /package/client/dist/assets/{analytics-D1ElChhs.js → analytics-C4z_NgJZ.js} +0 -0
  244. /package/client/dist/assets/{analytics-DDqve9Ma.js → analytics-DL2vJCbq.js} +0 -0
  245. /package/client/dist/assets/{analytics-CQqu1yE5.js → analytics-Ds_PNxtO2.js} +0 -0
  246. /package/client/dist/assets/{analytics-BDlELxc7.js → analytics-MVJRjcY52.js} +0 -0
  247. /package/client/dist/assets/{analytics-DBGO8B6-.js → analytics-e1mkNTgy2.js} +0 -0
  248. /package/client/dist/assets/{apex-BHHyAVNb.js → apex-DlD7oJX6.js} +0 -0
  249. /package/client/dist/assets/{atom-one-dark-DrWoEEbZ.js → atom-one-dark-BrRN8wBy.js} +0 -0
  250. /package/client/dist/assets/{attachments-BNgH6Tcu.js → attachments-8QCKHCEF2.js} +0 -0
  251. /package/client/dist/assets/{attachments-BLqqPGyy.js → attachments-BP0Ac1yt.js} +0 -0
  252. /package/client/dist/assets/{attachments-UJtEq892.js → attachments-CJaO1vCH.js} +0 -0
  253. /package/client/dist/assets/{attachments-D5fVsMgb.js → attachments-CPBVeHX72.js} +0 -0
  254. /package/client/dist/assets/{attachments-3CuBBRFu.js → attachments-CV2uaTGE.js} +0 -0
  255. /package/client/dist/assets/{attachments-CppM7de8.js → attachments-CaOjdN332.js} +0 -0
  256. /package/client/dist/assets/{attachments-pW3zm9nu.js → attachments-Db7JfA8a.js} +0 -0
  257. /package/client/dist/assets/{azcli-C56lfIEP.js → azcli-CmXFAbc-.js} +0 -0
  258. /package/client/dist/assets/{bat-BLamSaf-.js → bat-DdNJq4hH.js} +0 -0
  259. /package/client/dist/assets/{bicep-lrOcEItS.js → bicep-Bppz7WOA.js} +0 -0
  260. /package/client/dist/assets/{browser-C__Vz7KR.js → browser-0xeW8fUa2.js} +0 -0
  261. /package/client/dist/assets/{browser-EFmPpkvO.js → browser-543jbxE4.js} +0 -0
  262. /package/client/dist/assets/{browser-DhwTc0ry.js → browser-C9rUUxo4.js} +0 -0
  263. /package/client/dist/assets/{browser-C91MQBxx.js → browser-CEqx11iy.js} +0 -0
  264. /package/client/dist/assets/{browser-B7i7Ysc8.js → browser-CLC0etB_2.js} +0 -0
  265. /package/client/dist/assets/{browser-DwE289IS.js → browser-DyG2l9Wa.js} +0 -0
  266. /package/client/dist/assets/{browser-BVQgd1_1.js → browser-ExmELH702.js} +0 -0
  267. /package/client/dist/assets/{cameligo-BK_p0bgU.js → cameligo-0MRMdSmg.js} +0 -0
  268. /package/client/dist/assets/{chat-C5nJF6L0.js → chat-CCytD3P8.js} +0 -0
  269. /package/client/dist/assets/{chat-UMl6H55K.js → chat-CN8BMAMc.js} +0 -0
  270. /package/client/dist/assets/{chat-BXVx6rb5.js → chat-Cu6F3d6g2.js} +0 -0
  271. /package/client/dist/assets/{chat-CE8bx6Rz.js → chat-DagUyoVA.js} +0 -0
  272. /package/client/dist/assets/{chat-PVakYp92.js → chat-IjutwlBg.js} +0 -0
  273. /package/client/dist/assets/{chat-9RcD5Lcn.js → chat-NQPCBQo72.js} +0 -0
  274. /package/client/dist/assets/{chat-BnJPyeUK.js → chat-vVpwkOp52.js} +0 -0
  275. /package/client/dist/assets/{clojure-BQ7JOykv.js → clojure-B5dKj863.js} +0 -0
  276. /package/client/dist/assets/{clsx-CjLeUJfG.js → clsx-1dnHRn8W.js} +0 -0
  277. /package/client/dist/assets/{code-OoLqhjoI.js → code-32l6Naxu2.js} +0 -0
  278. /package/client/dist/assets/{code-Lg4awa35.js → code-BaG05zK62.js} +0 -0
  279. /package/client/dist/assets/{code-BPMR8_Vp.js → code-C5cibF2F.js} +0 -0
  280. /package/client/dist/assets/{code-aCUqkVN_.js → code-Ce-nBIwj.js} +0 -0
  281. /package/client/dist/assets/{code-BsOBm1Fb.js → code-Dap54vOb.js} +0 -0
  282. /package/client/dist/assets/{code-u9Sg9ohM.js → code-Dhb6jbAU.js} +0 -0
  283. /package/client/dist/assets/{code-eh4vO2-_.js → code-_NOCo-Ol2.js} +0 -0
  284. /package/client/dist/assets/{coffee-BD3DiL5s.js → coffee-DSbWjzk7.js} +0 -0
  285. /package/client/dist/assets/{common-C9VyfY7g.js → common-0aY9HQBP.js} +0 -0
  286. /package/client/dist/assets/{common-LDwESeCX.js → common-5jScBJqt.js} +0 -0
  287. /package/client/dist/assets/{common-ULlk84G4.js → common-BdaRciTu.js} +0 -0
  288. /package/client/dist/assets/{common-CzCQ0rpW.js → common-CHARW6MZ2.js} +0 -0
  289. /package/client/dist/assets/{common-CNoRVpnA.js → common-CQCJzAQq2.js} +0 -0
  290. /package/client/dist/assets/{common-Dmpyh323.js → common-DNhhqjlJ.js} +0 -0
  291. /package/client/dist/assets/{common-CcHFVATT.js → common-YdyZtmaI2.js} +0 -0
  292. /package/client/dist/assets/{cpp-splnCwCh.js → cpp-SBF8SAh9.js} +0 -0
  293. /package/client/dist/assets/{csharp-BCpsl-o3.js → csharp-Lzuq_1t-.js} +0 -0
  294. /package/client/dist/assets/{csp-tkCWrZmb.js → csp-BEzxZqi1.js} +0 -0
  295. /package/client/dist/assets/{css-B_xdnReq.js → css-C3D3PKln.js} +0 -0
  296. /package/client/dist/assets/{cypher-OL1zq-y3.js → cypher-CQ3dEPmv.js} +0 -0
  297. /package/client/dist/assets/{dart-CtTlOkrm.js → dart-DCPmxolI.js} +0 -0
  298. /package/client/dist/assets/{dashboard-C52PElPV.js → dashboard-BTiH361F.js} +0 -0
  299. /package/client/dist/assets/{dashboard-C-dXxut-.js → dashboard-COSm_14E.js} +0 -0
  300. /package/client/dist/assets/{dashboard-CUjyhohl.js → dashboard-CfOs70FN2.js} +0 -0
  301. /package/client/dist/assets/{dashboard-zH9quXks.js → dashboard-CzHDp14_2.js} +0 -0
  302. /package/client/dist/assets/{dashboard-03EpOoMM.js → dashboard-D6Woy2B5.js} +0 -0
  303. /package/client/dist/assets/{dashboard-C2owt3eN.js → dashboard-DwbH4oVH.js} +0 -0
  304. /package/client/dist/assets/{dashboard-Ch6H-457.js → dashboard-cWhU3krA2.js} +0 -0
  305. /package/client/dist/assets/{dockerfile-DcQ5CNiW.js → dockerfile-B_2_aaTO.js} +0 -0
  306. /package/client/dist/assets/{ecl-DZpiDRt6.js → ecl-rADUeXsV.js} +0 -0
  307. /package/client/dist/assets/{elixir-Dzj2lqtK.js → elixir-DOd-TuJO.js} +0 -0
  308. /package/client/dist/assets/{explore-CCcPXrHg.js → explore-B82VMUJ_.js} +0 -0
  309. /package/client/dist/assets/{explore-DQ0XEjed.js → explore-BGz1rvQE.js} +0 -0
  310. /package/client/dist/assets/{explore-xQoaak6e.js → explore-BzdMEpuf.js} +0 -0
  311. /package/client/dist/assets/{explore-B2n8r9Ye.js → explore-CbW0381i2.js} +0 -0
  312. /package/client/dist/assets/{explore-4SD1DxR0.js → explore-ChtxIiSM.js} +0 -0
  313. /package/client/dist/assets/{explore-BxR9q8Hq.js → explore-DfcUfiPw2.js} +0 -0
  314. /package/client/dist/assets/{explore-CZt4Ncpa.js → explore-yRF7wFSp2.js} +0 -0
  315. /package/client/dist/assets/{flow9-CFENCHIZ.js → flow9-Bf7zg_j0.js} +0 -0
  316. /package/client/dist/assets/{format-command-CMsBTOPN.js → format-command-D96da4fZ.js} +0 -0
  317. /package/client/dist/assets/{fsharp-D-4bv7FB.js → fsharp-BqROZjbA.js} +0 -0
  318. /package/client/dist/assets/{go-djxbTJq7.js → go-oQTP5uLe.js} +0 -0
  319. /package/client/dist/assets/{graphql-DHIFrkZ6.js → graphql-CSHfTU3q.js} +0 -0
  320. /package/client/dist/assets/{hcl-CXVoowNb.js → hcl-CeG-RoFJ.js} +0 -0
  321. /package/client/dist/assets/{ini-Crzc5kly.js → ini-B2Dv4ohc.js} +0 -0
  322. /package/client/dist/assets/{integrations-C7u7SInG.js → integrations-B0OBPU9_.js} +0 -0
  323. /package/client/dist/assets/{integrations-B17KyA-i.js → integrations-BA5pM05t.js} +0 -0
  324. /package/client/dist/assets/{integrations-DkBi-Gsh.js → integrations-BJvrlcc7.js} +0 -0
  325. /package/client/dist/assets/{integrations-DwSVdCpf.js → integrations-CPKYkG36.js} +0 -0
  326. /package/client/dist/assets/{integrations-A9f08E0o.js → integrations-CnrX7vqw2.js} +0 -0
  327. /package/client/dist/assets/{integrations-9qxUNhPD.js → integrations-DBou159M2.js} +0 -0
  328. /package/client/dist/assets/{integrations-B46ac3sf.js → integrations-DeJFRyXz2.js} +0 -0
  329. /package/client/dist/assets/{java-CYLaJrdE.js → java-aUDDeufb.js} +0 -0
  330. /package/client/dist/assets/{jira-Dht8qNck.js → jira-BDgxAFTF2.js} +0 -0
  331. /package/client/dist/assets/{jira-4Fin3fu7.js → jira-BwLBgmfI2.js} +0 -0
  332. /package/client/dist/assets/{jira-0ldfMByG.js → jira-D3GGzXOI.js} +0 -0
  333. /package/client/dist/assets/{jira-BpnpUFlb.js → jira-DeotPx6P2.js} +0 -0
  334. /package/client/dist/assets/{jira-DzIVvNmy.js → jira-NwlUCcQD.js} +0 -0
  335. /package/client/dist/assets/{jira-CBU0200d.js → jira-Srx2P5zS.js} +0 -0
  336. /package/client/dist/assets/{jira-01H1RfQj.js → jira-mi37H4Kt.js} +0 -0
  337. /package/client/dist/assets/{jobs-jEQidSIb.js → jobs-B56vQEkh2.js} +0 -0
  338. /package/client/dist/assets/{jobs-DM9owF1j.js → jobs-BYIk6Tg_.js} +0 -0
  339. /package/client/dist/assets/{jobs-Bw4hqEro.js → jobs-C-lM-iax2.js} +0 -0
  340. /package/client/dist/assets/{jobs-BF9raUkL.js → jobs-CYdevGgT.js} +0 -0
  341. /package/client/dist/assets/{jobs-ym72SIR9.js → jobs-D8adm2BX.js} +0 -0
  342. /package/client/dist/assets/{jobs-DcCONARH.js → jobs-K_Y8KAZ2.js} +0 -0
  343. /package/client/dist/assets/{jobs-Oz3UtLQn.js → jobs-OwM0ZWhI2.js} +0 -0
  344. /package/client/dist/assets/{julia-BJ7tUU-T.js → julia-C5LbruO5.js} +0 -0
  345. /package/client/dist/assets/{kotlin-CeWcU3lC.js → kotlin-C6mOuSKk.js} +0 -0
  346. /package/client/dist/assets/{less-CviwWNG4.js → less-D5qC3caW.js} +0 -0
  347. /package/client/dist/assets/{lexon-Dt28jqDS.js → lexon-Dr2La-fj.js} +0 -0
  348. /package/client/dist/assets/{loops-DE-2cmLw.js → loops-BDhEfnx82.js} +0 -0
  349. /package/client/dist/assets/{loops-mcoeFMat.js → loops-BM5QggwH.js} +0 -0
  350. /package/client/dist/assets/{loops-D_ZxkD4l.js → loops-CPjLAAxj.js} +0 -0
  351. /package/client/dist/assets/{loops-BYNUJVyD.js → loops-DKnk79TB2.js} +0 -0
  352. /package/client/dist/assets/{loops-B_gS4Pqu.js → loops-DLW0aFMm.js} +0 -0
  353. /package/client/dist/assets/{loops-Fv4Bs-9A.js → loops-Dcv3dFy_2.js} +0 -0
  354. /package/client/dist/assets/{loops-B_w7STrw.js → loops-y7wFRx6M.js} +0 -0
  355. /package/client/dist/assets/{lua-B6AOaJSi.js → lua-Zy6u5z6Z.js} +0 -0
  356. /package/client/dist/assets/{m3-VnMkHJcy.js → m3-C7XHeDz_.js} +0 -0
  357. /package/client/dist/assets/{markdown-8vKNdvIM.js → markdown-PiF7r-2h.js} +0 -0
  358. /package/client/dist/assets/{mips-DoEcuzan.js → mips-nn07GiyE.js} +0 -0
  359. /package/client/dist/assets/{msdax-CNzvHyEp.js → msdax-DaKFVMST.js} +0 -0
  360. /package/client/dist/assets/{mysql-Buk3vUtL.js → mysql-C6d6xrbU.js} +0 -0
  361. /package/client/dist/assets/{nav-Cw3_40fA.js → nav-1hj2izqB2.js} +0 -0
  362. /package/client/dist/assets/{nav-DNDJ3H-_.js → nav-BBUKMAu-2.js} +0 -0
  363. /package/client/dist/assets/{nav-CrG_mYRr.js → nav-CLqkP-26.js} +0 -0
  364. /package/client/dist/assets/{nav-DVp1nEsi.js → nav-Cv46L837.js} +0 -0
  365. /package/client/dist/assets/{nav-DbAbG5_k.js → nav-DgA-rjLk2.js} +0 -0
  366. /package/client/dist/assets/{nav-B5W9IMnf.js → nav-EG2pr6vN.js} +0 -0
  367. /package/client/dist/assets/{nav-_0vAgZ_k.js → nav-r3mIJVfU.js} +0 -0
  368. /package/client/dist/assets/{objective-c-DfOmda-5.js → objective-c-B6KMtI-k.js} +0 -0
  369. /package/client/dist/assets/{pascal-DoAA2yjy.js → pascal-DYr8MPE7.js} +0 -0
  370. /package/client/dist/assets/{pascaligo-C65Vn1fu.js → pascaligo-B3pd-jJ_.js} +0 -0
  371. /package/client/dist/assets/{perl-INlfQ_ro.js → perl-z2xUzoaL.js} +0 -0
  372. /package/client/dist/assets/{pgsql-DtwRzglx.js → pgsql-DUvT-09-.js} +0 -0
  373. /package/client/dist/assets/{php-DfHqVNet.js → php-CGyOmJfy.js} +0 -0
  374. /package/client/dist/assets/{pla-D3dNVRYk.js → pla-CMqgvyD0.js} +0 -0
  375. /package/client/dist/assets/{postiats-UKXD1rM4.js → postiats-Dx5LUn4Z.js} +0 -0
  376. /package/client/dist/assets/{powerquery-zE3E3O7s.js → powerquery-DazYxvJ6.js} +0 -0
  377. /package/client/dist/assets/{powershell-Df9b3zQl.js → powershell-B3nwlDka.js} +0 -0
  378. /package/client/dist/assets/{protobuf-BF0nUVrW.js → protobuf-BGuu2u2s.js} +0 -0
  379. /package/client/dist/assets/{pug-Bj6Yikv_.js → pug-Dbte1MXA.js} +0 -0
  380. /package/client/dist/assets/{qsharp-BI4h-raZ.js → qsharp-DJLwBOvG.js} +0 -0
  381. /package/client/dist/assets/{r-DhpnmaXx.js → r-DmGBRSHj.js} +0 -0
  382. /package/client/dist/assets/{redis-DwYYPr4d.js → redis-VvUYn0a4.js} +0 -0
  383. /package/client/dist/assets/{redshift-D2f3Bc-4.js → redshift-6GdDrxg3.js} +0 -0
  384. /package/client/dist/assets/{restructuredtext-Dkiif6fe.js → restructuredtext-CfAB8aRU.js} +0 -0
  385. /package/client/dist/assets/{ruby-C6hLmHy0.js → ruby-CIYK6MXY.js} +0 -0
  386. /package/client/dist/assets/{rust-X8WAGtxB.js → rust-CTHZfjj9.js} +0 -0
  387. /package/client/dist/assets/{sb-4M2la0Kr.js → sb-CkKTdkz_.js} +0 -0
  388. /package/client/dist/assets/{scala-CaCXz58E.js → scala-Bx-Wr9TM.js} +0 -0
  389. /package/client/dist/assets/{scheme-CMdjpmP7.js → scheme-Dcfi6f83.js} +0 -0
  390. /package/client/dist/assets/{scss-QopmvPyp.js → scss-ByL-7PFQ.js} +0 -0
  391. /package/client/dist/assets/{shell-CDBchS00.js → shell-Csnb9Yqd.js} +0 -0
  392. /package/client/dist/assets/{solidity-DvBfi5mC.js → solidity-Dbw5-0QP.js} +0 -0
  393. /package/client/dist/assets/{sophia-HSnXR17u.js → sophia-ZQ3saZQx.js} +0 -0
  394. /package/client/dist/assets/{sparql-DhG_ppup.js → sparql-BEtOel8u.js} +0 -0
  395. /package/client/dist/assets/{specs-CUE3xSFM.js → specs-B5XOoH-02.js} +0 -0
  396. /package/client/dist/assets/{specs-C2wW5bq8.js → specs-B8mwScoM2.js} +0 -0
  397. /package/client/dist/assets/{specs-Bku8SypT.js → specs-Bj_3fDrQ.js} +0 -0
  398. /package/client/dist/assets/{specs-CnH7dOiF.js → specs-CWNKAScX2.js} +0 -0
  399. /package/client/dist/assets/{specs-BvsFNKTs.js → specs-D4z5d8uq.js} +0 -0
  400. /package/client/dist/assets/{specs-D4ORogcT.js → specs-DgvIRZtM.js} +0 -0
  401. /package/client/dist/assets/{specs-D6Y76Bgi.js → specs-DuRzE5wE.js} +0 -0
  402. /package/client/dist/assets/{sql-DHeIS-tf.js → sql-DGPDbzB7.js} +0 -0
  403. /package/client/dist/assets/{st-CMWchiRU.js → st-GeW2MOgh.js} +0 -0
  404. /package/client/dist/assets/{swift-CDXvBefj.js → swift-BVYTN8jq.js} +0 -0
  405. /package/client/dist/assets/{systemverilog-0RTukYUE.js → systemverilog-Deg2a8Lr.js} +0 -0
  406. /package/client/dist/assets/{tcl-CbBF_Ttl.js → tcl-6DnLmROF.js} +0 -0
  407. /package/client/dist/assets/{terminal-BLh00ouM.js → terminal-B2sExUis.js} +0 -0
  408. /package/client/dist/assets/{terminal-Cz_Puz0q.js → terminal-CH0fdHvP.js} +0 -0
  409. /package/client/dist/assets/{terminal-DFEH9XTQ.js → terminal-CPMigUX7.js} +0 -0
  410. /package/client/dist/assets/{terminal-CB33IsO1.js → terminal-DOwfkxqa2.js} +0 -0
  411. /package/client/dist/assets/{terminal-CR6t-Oey.js → terminal-DjSSa5qP.js} +0 -0
  412. /package/client/dist/assets/{terminal-CY_sPfdD.js → terminal-DoMTHedv2.js} +0 -0
  413. /package/client/dist/assets/{terminal-BTanJIt_.js → terminal-w0i-OCp32.js} +0 -0
  414. /package/client/dist/assets/{tickets-D6f3GSYn.js → tickets-CQTana1u.js} +0 -0
  415. /package/client/dist/assets/{tickets-blQnNA4N.js → tickets-D00mSt832.js} +0 -0
  416. /package/client/dist/assets/{tickets-68LH1xp6.js → tickets-D7CSuWjO.js} +0 -0
  417. /package/client/dist/assets/{tickets-OrJwUxJR.js → tickets-Dq7HDy8n.js} +0 -0
  418. /package/client/dist/assets/{tickets-PtHNxQo5.js → tickets-DvbFg_-c2.js} +0 -0
  419. /package/client/dist/assets/{tickets-REr-EQx9.js → tickets-heDAaj8C2.js} +0 -0
  420. /package/client/dist/assets/{tickets-jxsTrApR.js → tickets-onKZz6Dd.js} +0 -0
  421. /package/client/dist/assets/{twig-DCcGd8FE.js → twig-XbbXW9Yz.js} +0 -0
  422. /package/client/dist/assets/{typespec-HTphp-Uh.js → typespec-CRlV8GhI.js} +0 -0
  423. /package/client/dist/assets/{vb-Cb_yUcVd.js → vb-BHE2izPH.js} +0 -0
  424. /package/client/dist/assets/{wgsl-DrKU3YXj.js → wgsl-Bf5vkfmY.js} +0 -0
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadOrGenerateMcpToken = loadOrGenerateMcpToken;
7
+ exports.getMcpToken = getMcpToken;
8
+ exports.regenerateMcpToken = regenerateMcpToken;
9
+ exports.requireMcpAuth = requireMcpAuth;
10
+ exports._resetMcpTokenForTest = _resetMcpTokenForTest;
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const os_1 = __importDefault(require("os"));
14
+ const crypto_1 = require("crypto");
15
+ const auth_1 = require("../auth");
16
+ // ─── MCP-scoped token (design D3) ─────────────────────────────────────────────
17
+ //
18
+ // The MCP surface is authenticated by a token SEPARATE from the all-powerful
19
+ // master token (`~/.specrails/desktop.token`). The master token grants shell
20
+ // (terminals), arbitrary AI-CLI spawns and project admin; handing it to a
21
+ // third-party MCP client would be a security smell. Instead the MCP server
22
+ // mints its own scoped token at `~/.specrails/mcp.token` (0600). The stdio
23
+ // bridge reads it locally so it never appears in client config; the direct
24
+ // streamable-HTTP path requires it as `Authorization: Bearer <token>`.
25
+ //
26
+ // This module mirrors the shape of `server/auth.ts` (loadOrGenerateToken /
27
+ // safeEqual / requireAuth) for the scoped token. There is intentionally NO
28
+ // legacy-file migration — this is a greenfield credential.
29
+ // Honour the shared test-home override (SPECRAILS_REGISTRY_HOME) so tests never
30
+ // touch the real ~/.specrails/mcp.token; falls back to os.homedir() in prod.
31
+ function homeDir() {
32
+ return process.env.SPECRAILS_REGISTRY_HOME || os_1.default.homedir();
33
+ }
34
+ function tokenDir() {
35
+ return path_1.default.join(homeDir(), '.specrails');
36
+ }
37
+ function tokenPath() {
38
+ return path_1.default.join(tokenDir(), 'mcp.token');
39
+ }
40
+ let _mcpToken = null;
41
+ function generateToken() {
42
+ return (0, crypto_1.randomUUID)().replace(/-/g, '') + (0, crypto_1.randomUUID)().replace(/-/g, '');
43
+ }
44
+ /**
45
+ * Loads the persisted MCP token, or generates and persists a fresh one.
46
+ * Cached for the lifetime of the process.
47
+ */
48
+ function loadOrGenerateMcpToken() {
49
+ if (_mcpToken)
50
+ return _mcpToken;
51
+ try {
52
+ if (fs_1.default.existsSync(tokenPath())) {
53
+ const t = fs_1.default.readFileSync(tokenPath(), 'utf-8').trim();
54
+ if (t && t.length >= 32) {
55
+ _mcpToken = t;
56
+ return _mcpToken;
57
+ }
58
+ }
59
+ }
60
+ catch {
61
+ // Fall through to generate a new token.
62
+ }
63
+ _mcpToken = generateToken();
64
+ persist(_mcpToken);
65
+ return _mcpToken;
66
+ }
67
+ function persist(token) {
68
+ try {
69
+ fs_1.default.mkdirSync(tokenDir(), { recursive: true });
70
+ fs_1.default.writeFileSync(tokenPath(), token, { encoding: 'utf-8', mode: 0o600 });
71
+ }
72
+ catch (err) {
73
+ console.warn('[mcp] could not persist mcp token to disk:', err);
74
+ }
75
+ }
76
+ /** Returns the current MCP token (generating + persisting one if needed). */
77
+ function getMcpToken() {
78
+ return loadOrGenerateMcpToken();
79
+ }
80
+ /**
81
+ * Overwrites the MCP token with a fresh value (invalidating any previously
82
+ * issued token) and returns the new value. Used by the Settings "regenerate"
83
+ * action.
84
+ */
85
+ function regenerateMcpToken() {
86
+ const next = generateToken();
87
+ _mcpToken = next;
88
+ persist(next);
89
+ return next;
90
+ }
91
+ /** Extracts a bearer token from `Authorization: Bearer <t>` or `X-Desktop-Token`. */
92
+ function extractToken(req) {
93
+ const auth = req.headers['authorization'];
94
+ if (typeof auth === 'string' && auth.startsWith('Bearer ')) {
95
+ return auth.slice(7).trim();
96
+ }
97
+ const xToken = req.headers['x-desktop-token'];
98
+ if (typeof xToken === 'string' && xToken.trim())
99
+ return xToken.trim();
100
+ return null;
101
+ }
102
+ /**
103
+ * Express middleware gating the MCP transport: requires the MCP-scoped token
104
+ * (constant-time compared). Mirrors `requireAuth` from auth.ts but checks the
105
+ * MCP token, never the master token.
106
+ */
107
+ function requireMcpAuth(req, res, next) {
108
+ const provided = extractToken(req);
109
+ if (provided && (0, auth_1.safeEqual)(provided, getMcpToken())) {
110
+ next();
111
+ return;
112
+ }
113
+ // JSON-RPC-shaped error so MCP clients can parse it.
114
+ res.status(401).json({ jsonrpc: '2.0', error: { code: -32001, message: 'Unauthorized: invalid MCP token' }, id: null });
115
+ }
116
+ /** Test seam: clears the cached token so a test can point HOME elsewhere. */
117
+ function _resetMcpTokenForTest() {
118
+ _mcpToken = null;
119
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerResources = registerResources;
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const guide_1 = require("./guide");
6
+ const projects_1 = require("./tools/projects");
7
+ // Read-only state exposed as MCP resources. Read-tier only (resources are never
8
+ // mutating). Per-project resource templates (tickets, rails, analytics) are
9
+ // added in subsequent phases.
10
+ function registerResources(server, ctx) {
11
+ server.registerResource('guide', 'specrails://guide', { title: 'Specrails platform guide', description: 'Concepts, workflow and invariants for operating Specrails.', mimeType: 'text/markdown' }, async (uri) => ({
12
+ contents: [{ uri: uri.href, text: guide_1.SPECRAILS_GUIDE }],
13
+ }));
14
+ server.registerResource('projects', 'specrails://projects', { title: 'Projects', description: 'All registered Specrails projects.', mimeType: 'application/json' }, async (uri) => ({
15
+ contents: [{ uri: uri.href, text: JSON.stringify(ctx.registry.listContexts().map((c) => (0, projects_1.serializeProject)(c.project)), null, 2) }],
16
+ }));
17
+ server.registerResource('project', new mcp_js_1.ResourceTemplate('specrails://projects/{projectId}', { list: undefined }), { title: 'Project', description: 'A single project by id.', mimeType: 'application/json' }, async (uri, variables) => {
18
+ const projectId = String(variables.projectId);
19
+ const pc = ctx.registry.getContext(projectId);
20
+ if (!pc)
21
+ throw new Error(`Unknown projectId "${projectId}".`);
22
+ return { contents: [{ uri: uri.href, text: JSON.stringify((0, projects_1.serializeProject)(pc.project), null, 2) }] };
23
+ });
24
+ }
@@ -0,0 +1,319 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.agentsTools = agentsTools;
4
+ const zod_1 = require("zod");
5
+ const types_1 = require("./types");
6
+ /**
7
+ * Agents domain — agent PROFILES (declarative per-project routing config) and
8
+ * the agents CATALOG (the `.claude/agents/*.md` definitions: read-only `sr-*`
9
+ * upstream agents + user-authored `custom-*` agents, with AI generate/test/
10
+ * iterative-refine on the custom ones).
11
+ *
12
+ * CLAUDE-ONLY: agent profiles are a Claude concept. On a project whose primary
13
+ * provider (or any installed provider) is codex/gemini, rails force-null the
14
+ * profile and the whole section is hidden client-side — creating profiles for a
15
+ * non-Claude project is mostly inert. The server still validates against that
16
+ * provider's adapter catalog. Prefer using this tool on Claude projects.
17
+ *
18
+ * Every operation is per-project (mounted at /api/projects/:projectId/profiles)
19
+ * and is gated by SPECRAILS_AGENTS_SECTION — a disabled server 404s every route.
20
+ *
21
+ * The catalog `generate`, `test`, `refine_start`, and `refine_turn` actions
22
+ * SPAWN the claude CLI and incur real cost (refine records ai_invocations and
23
+ * streams agent_refine_* WS events). `refine_apply` / `update_custom` /
24
+ * `delete_custom` / `create_custom` mutate `custom-*.md` files on disk.
25
+ */
26
+ function agentsTools() {
27
+ return [
28
+ {
29
+ name: 'specrails_agents',
30
+ title: 'Agents (Profiles & Catalog)',
31
+ description: 'Manage agent PROFILES and the agents CATALOG for a project (Claude-only — profiles are inert on codex/gemini projects). ' +
32
+ 'PROFILE actions: list, get, resolve (preview which profile a rail would resolve to), create, update, delete (destructive — protected default/project-default cannot be deleted), ' +
33
+ 'duplicate, rename, migrate (seed a default profile from installed sr-*.md frontmatter), analytics (per-profile metrics), core_version (specrails-core version + 4.1.0 profile-aware check). ' +
34
+ 'CATALOG actions: catalog_list, catalog_get (full agent .md body), versions (custom agent edit history), create_custom (custom-*.md), update_custom, delete_custom (destructive), ' +
35
+ 'generate (AI-generate a draft custom agent — spawns claude, costs), test (AI smoke-test a draft body — spawns claude, costs), ' +
36
+ 'refine_start / refine_turn (AI iterative refine of a custom agent — spawn claude, cost, stream over WS), refine_list, refine_get, refine_patch (toggle autoTest), refine_cancel (kill the spawn), refine_apply (write the refined draft to disk).',
37
+ hintTier: 'read',
38
+ tier: (a) => {
39
+ const action = a.action;
40
+ if (['delete', 'delete_custom'].includes(action))
41
+ return 'destructive';
42
+ if (['generate', 'test', 'refine_start', 'refine_turn'].includes(action))
43
+ return 'ai-spawn';
44
+ if ([
45
+ 'create',
46
+ 'update',
47
+ 'duplicate',
48
+ 'rename',
49
+ 'migrate',
50
+ 'create_custom',
51
+ 'update_custom',
52
+ 'refine_patch',
53
+ 'refine_cancel',
54
+ 'refine_apply',
55
+ ].includes(action)) {
56
+ return 'write';
57
+ }
58
+ return 'read';
59
+ },
60
+ inputSchema: {
61
+ action: zod_1.z
62
+ .enum([
63
+ // PROFILES
64
+ 'list',
65
+ 'get',
66
+ 'resolve',
67
+ 'create',
68
+ 'update',
69
+ 'delete',
70
+ 'duplicate',
71
+ 'rename',
72
+ 'migrate',
73
+ 'analytics',
74
+ 'core_version',
75
+ // CATALOG (definitions)
76
+ 'catalog_list',
77
+ 'catalog_get',
78
+ 'versions',
79
+ 'create_custom',
80
+ 'update_custom',
81
+ 'delete_custom',
82
+ 'generate',
83
+ 'test',
84
+ // CATALOG (AI refine)
85
+ 'refine_start',
86
+ 'refine_turn',
87
+ 'refine_list',
88
+ 'refine_get',
89
+ 'refine_patch',
90
+ 'refine_cancel',
91
+ 'refine_apply',
92
+ ])
93
+ .describe('Operation to perform (profile or catalog action)'),
94
+ projectId: zod_1.z.string().optional().describe('Project id (defaults to the active project)'),
95
+ // Profile params
96
+ name: zod_1.z
97
+ .string()
98
+ .optional()
99
+ .describe('Profile name (get/update/delete/duplicate/rename source). For duplicate/rename, the NEW name goes in `newName`.'),
100
+ newName: zod_1.z.string().optional().describe('New profile name (for duplicate / rename)'),
101
+ profileName: zod_1.z
102
+ .string()
103
+ .optional()
104
+ .describe('Explicit profile name to preview in `resolve` (omit to resolve the project default)'),
105
+ profile: zod_1.z
106
+ .record(zod_1.z.unknown())
107
+ .optional()
108
+ .describe('Full Profile body for create/update: { schemaVersion:1, name, description?, provider?, orchestrator:{model}, agents:[], routing:[] }'),
109
+ windowDays: zod_1.z.number().int().optional().describe('analytics time window in days (1-365, default 30)'),
110
+ // Catalog params
111
+ agentId: zod_1.z
112
+ .string()
113
+ .optional()
114
+ .describe('Agent id for catalog_get/versions/update_custom/delete_custom and all refine actions (custom-* for mutations, sr-*|custom-* for reads)'),
115
+ body: zod_1.z.string().optional().describe('Agent .md body (for create_custom / update_custom)'),
116
+ agentName: zod_1.z
117
+ .string()
118
+ .optional()
119
+ .describe('New custom agent id for create_custom — must match ^custom-[a-z0-9][a-z0-9-]*$'),
120
+ description: zod_1.z.string().optional().describe('Plain-language description for `generate` (AI draft a custom agent)'),
121
+ draftBody: zod_1.z.string().optional().describe('Draft agent body to smoke-test in `test`'),
122
+ sampleTask: zod_1.z.string().optional().describe('Sample task the `test` action runs the draft against'),
123
+ instruction: zod_1.z.string().optional().describe('Refine instruction (for refine_start / refine_turn)'),
124
+ autoTest: zod_1.z.boolean().optional().describe('Auto-test the refined draft (refine_start default true; refine_patch toggles it)'),
125
+ refineId: zod_1.z.string().optional().describe('Refine session id (for refine_get/refine_turn/refine_patch/refine_cancel/refine_apply)'),
126
+ force: zod_1.z.boolean().optional().describe('Bypass the disk-hash guard when applying a refine draft (refine_apply)'),
127
+ },
128
+ async handler(ctx, args) {
129
+ const base = `${(0, types_1.projectPath)(ctx, args.projectId)}/profiles`;
130
+ const action = args.action;
131
+ const agentId = args.agentId;
132
+ const refineId = args.refineId;
133
+ const profileName = args.name;
134
+ switch (action) {
135
+ // ── PROFILES ────────────────────────────────────────────────────
136
+ case 'list':
137
+ return (0, types_1.apiCall)(ctx, 'GET', base);
138
+ case 'get': {
139
+ if (!profileName)
140
+ throw new Error('get requires a profile "name".');
141
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/${encodeURIComponent(profileName)}`);
142
+ }
143
+ case 'resolve': {
144
+ const p = args.profileName;
145
+ const qs = p ? `?profile=${encodeURIComponent(p)}` : '';
146
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/resolve${qs}`);
147
+ }
148
+ case 'create': {
149
+ if (!args.profile)
150
+ throw new Error('create requires a "profile" body.');
151
+ return (0, types_1.apiCall)(ctx, 'POST', base, args.profile);
152
+ }
153
+ case 'update': {
154
+ if (!profileName)
155
+ throw new Error('update requires a profile "name".');
156
+ if (!args.profile)
157
+ throw new Error('update requires a "profile" body (its name must equal "name").');
158
+ return (0, types_1.apiCall)(ctx, 'PATCH', `${base}/${encodeURIComponent(profileName)}`, args.profile);
159
+ }
160
+ case 'delete': {
161
+ if (!profileName)
162
+ throw new Error('delete requires a profile "name".');
163
+ return (0, types_1.apiCall)(ctx, 'DELETE', `${base}/${encodeURIComponent(profileName)}`);
164
+ }
165
+ case 'duplicate': {
166
+ if (!profileName)
167
+ throw new Error('duplicate requires a source profile "name".');
168
+ const newName = args.newName;
169
+ if (!newName)
170
+ throw new Error('duplicate requires a "newName".');
171
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/${encodeURIComponent(profileName)}/duplicate`, { name: newName });
172
+ }
173
+ case 'rename': {
174
+ if (!profileName)
175
+ throw new Error('rename requires a source profile "name".');
176
+ const newName = args.newName;
177
+ if (!newName)
178
+ throw new Error('rename requires a "newName".');
179
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/${encodeURIComponent(profileName)}/rename`, { name: newName });
180
+ }
181
+ case 'migrate':
182
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/migrate-from-settings`);
183
+ case 'analytics': {
184
+ const wd = args.windowDays;
185
+ const qs = typeof wd === 'number' ? `?windowDays=${wd}` : '';
186
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/analytics${qs}`);
187
+ }
188
+ case 'core_version':
189
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/core-version`);
190
+ // ── CATALOG: definitions ────────────────────────────────────────
191
+ case 'catalog_list':
192
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/catalog`);
193
+ case 'catalog_get': {
194
+ if (!agentId)
195
+ throw new Error('catalog_get requires an "agentId".');
196
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/catalog/${encodeURIComponent(agentId)}`);
197
+ }
198
+ case 'versions': {
199
+ if (!agentId)
200
+ throw new Error('versions requires an "agentId".');
201
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/catalog/${encodeURIComponent(agentId)}/versions`);
202
+ }
203
+ case 'create_custom': {
204
+ const id = args.agentName;
205
+ const body = args.body;
206
+ if (!id)
207
+ throw new Error('create_custom requires an "agentName" (custom-* id).');
208
+ if (!body)
209
+ throw new Error('create_custom requires a "body".');
210
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/catalog`, { id, body });
211
+ }
212
+ case 'update_custom': {
213
+ if (!agentId)
214
+ throw new Error('update_custom requires an "agentId" (custom-* only).');
215
+ const body = args.body;
216
+ if (!body)
217
+ throw new Error('update_custom requires a "body".');
218
+ return (0, types_1.apiCall)(ctx, 'PATCH', `${base}/catalog/${encodeURIComponent(agentId)}`, { body });
219
+ }
220
+ case 'delete_custom': {
221
+ if (!agentId)
222
+ throw new Error('delete_custom requires an "agentId" (custom-* only).');
223
+ return (0, types_1.apiCall)(ctx, 'DELETE', `${base}/catalog/${encodeURIComponent(agentId)}`);
224
+ }
225
+ // ── CATALOG: AI spawns (cost) ───────────────────────────────────
226
+ case 'generate': {
227
+ const name = args.agentName;
228
+ const description = args.description;
229
+ if (!name)
230
+ throw new Error('generate requires an "agentName" (custom-* id).');
231
+ if (!description)
232
+ throw new Error('generate requires a "description".');
233
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/catalog/generate`, { name, description });
234
+ }
235
+ case 'test': {
236
+ const draftBody = args.draftBody;
237
+ const sampleTask = args.sampleTask;
238
+ if (!draftBody)
239
+ throw new Error('test requires a "draftBody".');
240
+ if (!sampleTask)
241
+ throw new Error('test requires a "sampleTask".');
242
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/catalog/test`, {
243
+ ...(agentId ? { agentId } : {}),
244
+ draftBody,
245
+ sampleTask,
246
+ });
247
+ }
248
+ // ── CATALOG: AI refine sessions ─────────────────────────────────
249
+ case 'refine_start': {
250
+ if (!agentId)
251
+ throw new Error('refine_start requires an "agentId" (custom-* only).');
252
+ const instruction = args.instruction;
253
+ if (!instruction)
254
+ throw new Error('refine_start requires an "instruction".');
255
+ const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/catalog/${encodeURIComponent(agentId)}/refine`, {
256
+ instruction,
257
+ ...(typeof args.autoTest === 'boolean' ? { autoTest: args.autoTest } : {}),
258
+ });
259
+ return {
260
+ ...r,
261
+ hint: 'Refine streams over WS (agent_refine_* events). Use refine_get with the returned refineId to poll the session, then refine_apply to write the draft to disk.',
262
+ };
263
+ }
264
+ case 'refine_turn': {
265
+ if (!agentId)
266
+ throw new Error('refine_turn requires an "agentId".');
267
+ if (!refineId)
268
+ throw new Error('refine_turn requires a "refineId".');
269
+ const instruction = args.instruction;
270
+ if (!instruction)
271
+ throw new Error('refine_turn requires an "instruction".');
272
+ const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/catalog/${encodeURIComponent(agentId)}/refine/${encodeURIComponent(refineId)}/turn`, { instruction });
273
+ return {
274
+ ...r,
275
+ hint: 'The turn streams over WS (agent_refine_* events). Use refine_get with the refineId to read the updated draft.',
276
+ };
277
+ }
278
+ case 'refine_list': {
279
+ if (!agentId)
280
+ throw new Error('refine_list requires an "agentId".');
281
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/catalog/${encodeURIComponent(agentId)}/refine`);
282
+ }
283
+ case 'refine_get': {
284
+ if (!agentId)
285
+ throw new Error('refine_get requires an "agentId".');
286
+ if (!refineId)
287
+ throw new Error('refine_get requires a "refineId".');
288
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/catalog/${encodeURIComponent(agentId)}/refine/${encodeURIComponent(refineId)}`);
289
+ }
290
+ case 'refine_patch': {
291
+ if (!agentId)
292
+ throw new Error('refine_patch requires an "agentId".');
293
+ if (!refineId)
294
+ throw new Error('refine_patch requires a "refineId".');
295
+ if (typeof args.autoTest !== 'boolean')
296
+ throw new Error('refine_patch requires a boolean "autoTest".');
297
+ return (0, types_1.apiCall)(ctx, 'PATCH', `${base}/catalog/${encodeURIComponent(agentId)}/refine/${encodeURIComponent(refineId)}`, { autoTest: args.autoTest });
298
+ }
299
+ case 'refine_cancel': {
300
+ if (!agentId)
301
+ throw new Error('refine_cancel requires an "agentId".');
302
+ if (!refineId)
303
+ throw new Error('refine_cancel requires a "refineId".');
304
+ return (0, types_1.apiCall)(ctx, 'DELETE', `${base}/catalog/${encodeURIComponent(agentId)}/refine/${encodeURIComponent(refineId)}`);
305
+ }
306
+ case 'refine_apply': {
307
+ if (!agentId)
308
+ throw new Error('refine_apply requires an "agentId".');
309
+ if (!refineId)
310
+ throw new Error('refine_apply requires a "refineId".');
311
+ return (0, types_1.apiCall)(ctx, 'POST', `${base}/catalog/${encodeURIComponent(agentId)}/refine/${encodeURIComponent(refineId)}/apply`, { ...(typeof args.force === 'boolean' ? { force: args.force } : {}) });
312
+ }
313
+ default:
314
+ throw new Error(`Unknown action "${action}".`);
315
+ }
316
+ },
317
+ },
318
+ ];
319
+ }
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyticsTools = analyticsTools;
4
+ const zod_1 = require("zod");
5
+ const types_1 = require("./types");
6
+ /**
7
+ * Build a `?a=b&c=d` query string from a flat record, skipping undefined/null/''
8
+ * values. Arrays are joined with commas (the spending router parses CSV for
9
+ * surface/model/modelProvider/provider). Used by the read actions below.
10
+ */
11
+ function buildQuery(params) {
12
+ const parts = [];
13
+ for (const [k, v] of Object.entries(params)) {
14
+ if (v === undefined || v === null || v === '')
15
+ continue;
16
+ const value = Array.isArray(v) ? v.join(',') : String(v);
17
+ if (value === '')
18
+ continue;
19
+ parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(value)}`);
20
+ }
21
+ return parts.length ? `?${parts.join('&')}` : '';
22
+ }
23
+ function analyticsTools() {
24
+ return [
25
+ {
26
+ name: 'specrails_analytics',
27
+ title: 'Analytics & Spending',
28
+ description: 'Per-project cost analytics over billable AI-CLI invocations (jobs, quick-spec, explore-spec, ai-edit, smash, file-summary, loop). ' +
29
+ 'All read-only except set_budget. Actions: ' +
30
+ 'spending (full dashboard: cost summary with prev-period delta, by-surface/model/mode/provider breakdowns, daily timeline, cost-vs-turns scatter, top tickets), ' +
31
+ 'invocations (paginated raw invocation table), ' +
32
+ 'ticket_summary (per-ticket cost aggregate across all surfaces), ' +
33
+ 'export (download summary/raw analytics as CSV or JSON — file download, returns a note + headers), ' +
34
+ 'budget_get (daily budget, per-job threshold, cost-today, utilization %), ' +
35
+ 'budget_set (write — set/clear daily budget cap + per-job cost threshold; a too-low budget pauses the job queue). ' +
36
+ 'Prefer spending over invocations/export-raw for token economy. ' +
37
+ 'Filters: period(7d|30d|90d|all|custom), from/to (ISO, required for custom), surface[], model[], modelProvider[] (index-aligned to model), provider[], status(success|failed|aborted), minCostUsd, ticketId, tzOffsetMinutes(±840 — pass the user local offset so "today" lines up), sortBy.',
38
+ hintTier: 'read',
39
+ tier: (a) => (a.action === 'budget_set' ? 'write' : 'read'),
40
+ inputSchema: {
41
+ action: zod_1.z
42
+ .enum(['spending', 'invocations', 'ticket_summary', 'export', 'budget_get', 'budget_set'])
43
+ .describe('Operation to perform'),
44
+ projectId: zod_1.z.string().optional().describe('Project id (defaults to the active project)'),
45
+ // ─── Filters shared by spending / invocations / export ────────────────
46
+ period: zod_1.z
47
+ .enum(['7d', '30d', '90d', 'all', 'custom'])
48
+ .optional()
49
+ .describe('Time window (default 30d). "custom" requires from + to.'),
50
+ from: zod_1.z.string().optional().describe('ISO start date (required when period=custom)'),
51
+ to: zod_1.z.string().optional().describe('ISO end date (required when period=custom)'),
52
+ surface: zod_1.z
53
+ .array(zod_1.z.enum(['job', 'quick-spec', 'explore-spec', 'ai-edit', 'smash', 'file-summary', 'loop']))
54
+ .optional()
55
+ .describe('Filter to these surfaces (CSV)'),
56
+ model: zod_1.z.array(zod_1.z.string()).optional().describe('Filter to these model ids (CSV)'),
57
+ modelProvider: zod_1.z
58
+ .array(zod_1.z.string())
59
+ .optional()
60
+ .describe('Providers index-aligned to model[] (scopes each model id to its provider so codex/gemini estimated cost does not merge into a claude bar)'),
61
+ provider: zod_1.z
62
+ .array(zod_1.z.string())
63
+ .optional()
64
+ .describe('Filter to these providers (e.g. claude, codex, gemini) (CSV)'),
65
+ status: zod_1.z.enum(['success', 'failed', 'aborted']).optional().describe('Filter by invocation status'),
66
+ minCostUsd: zod_1.z.number().optional().describe('Only invocations costing at least this many USD'),
67
+ ticketId: zod_1.z.number().int().optional().describe('Scope to a single ticket id'),
68
+ tzOffsetMinutes: zod_1.z
69
+ .number()
70
+ .int()
71
+ .optional()
72
+ .describe('Local UTC offset in minutes (±840) for daily-timeline calendar-day bucketing (default 0 = UTC)'),
73
+ sortBy: zod_1.z.enum(['recency', 'cost']).optional().describe('Sort order'),
74
+ // ─── invocations pagination ───────────────────────────────────────────
75
+ limit: zod_1.z
76
+ .number()
77
+ .int()
78
+ .optional()
79
+ .describe('invocations: max rows (clamped server-side to [1, 10000])'),
80
+ offset: zod_1.z.number().int().optional().describe('invocations: row offset for pagination'),
81
+ // ─── ticket_summary ───────────────────────────────────────────────────
82
+ id: zod_1.z.number().int().optional().describe('ticket_summary: the ticket id to aggregate'),
83
+ // ─── export ───────────────────────────────────────────────────────────
84
+ format: zod_1.z.enum(['csv', 'json']).optional().describe('export: output format (default json)'),
85
+ mode: zod_1.z
86
+ .enum(['summary', 'raw'])
87
+ .optional()
88
+ .describe('export: summary (aggregated dashboard) or raw (up to 10000 invocation rows). Default summary.'),
89
+ // ─── budget_set ───────────────────────────────────────────────────────
90
+ dailyBudgetUsd: zod_1.z
91
+ .number()
92
+ .nullable()
93
+ .optional()
94
+ .describe('budget_set: daily budget cap USD (>0); null clears it'),
95
+ jobCostThresholdUsd: zod_1.z
96
+ .number()
97
+ .nullable()
98
+ .optional()
99
+ .describe('budget_set: per-job cost alert threshold USD (>0); null clears it'),
100
+ },
101
+ async handler(ctx, args) {
102
+ const base = (0, types_1.projectPath)(ctx, args.projectId);
103
+ const action = args.action;
104
+ const filters = {
105
+ period: args.period,
106
+ from: args.from,
107
+ to: args.to,
108
+ surface: args.surface,
109
+ model: args.model,
110
+ modelProvider: args.modelProvider,
111
+ provider: args.provider,
112
+ status: args.status,
113
+ minCostUsd: args.minCostUsd,
114
+ ticketId: args.ticketId,
115
+ tzOffsetMinutes: args.tzOffsetMinutes,
116
+ sortBy: args.sortBy,
117
+ };
118
+ switch (action) {
119
+ case 'spending':
120
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/spending${buildQuery(filters)}`);
121
+ case 'invocations':
122
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/invocations${buildQuery({
123
+ ...filters,
124
+ limit: args.limit,
125
+ offset: args.offset,
126
+ })}`);
127
+ case 'ticket_summary': {
128
+ const id = args.id;
129
+ if (id === undefined || id === null) {
130
+ throw new Error('ticket_summary requires an "id" (ticket id).');
131
+ }
132
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/tickets/${id}/spending-summary`);
133
+ }
134
+ case 'export': {
135
+ // GET /analytics/export streams a file download (Content-Disposition).
136
+ // apiCall returns the body text/JSON; for CSV it is the raw CSV string,
137
+ // for JSON the parsed dashboard/invocations object. We surface it
138
+ // inline with a note since there is no file sink over MCP.
139
+ const format = args.format ?? 'json';
140
+ const mode = args.mode ?? 'summary';
141
+ const data = await (0, types_1.apiCall)(ctx, 'GET', `${base}/analytics/export${buildQuery({ ...filters, format, mode })}`);
142
+ return {
143
+ note: `Analytics export (mode=${mode}, format=${format}). This endpoint is normally a file download; the content is returned inline below. Raw mode caps at 10000 rows. Prefer the "spending" action for already-aggregated, bounded data.`,
144
+ format,
145
+ mode,
146
+ data,
147
+ };
148
+ }
149
+ case 'budget_get':
150
+ return (0, types_1.apiCall)(ctx, 'GET', `${base}/budget`);
151
+ case 'budget_set': {
152
+ const body = {};
153
+ if ('dailyBudgetUsd' in args)
154
+ body.dailyBudgetUsd = args.dailyBudgetUsd;
155
+ if ('jobCostThresholdUsd' in args)
156
+ body.jobCostThresholdUsd = args.jobCostThresholdUsd;
157
+ if (Object.keys(body).length === 0) {
158
+ throw new Error('budget_set requires dailyBudgetUsd and/or jobCostThresholdUsd (number>0 to set, null to clear).');
159
+ }
160
+ return (0, types_1.apiCall)(ctx, 'PATCH', `${base}/budget`, body);
161
+ }
162
+ default:
163
+ throw new Error(`Unknown action "${action}".`);
164
+ }
165
+ },
166
+ },
167
+ ];
168
+ }