specrails-desktop 2.0.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 (455) hide show
  1. package/.claude/commands/specrails/batch-implement.md +287 -0
  2. package/.claude/commands/specrails/compat-check.md +271 -0
  3. package/.claude/commands/specrails/doctor.md +62 -0
  4. package/.claude/commands/specrails/enrich.md +1635 -0
  5. package/.claude/commands/specrails/explore-spec.md +173 -0
  6. package/.claude/commands/specrails/health-check.md +527 -0
  7. package/.claude/commands/specrails/implement.md +1457 -0
  8. package/.claude/commands/specrails/memory-inspect.md +259 -0
  9. package/.claude/commands/specrails/opsx-diff.md +419 -0
  10. package/.claude/commands/specrails/propose-spec.md +102 -0
  11. package/.claude/commands/specrails/reconfig.md +89 -0
  12. package/.claude/commands/specrails/refactor-recommender.md +212 -0
  13. package/.claude/commands/specrails/retry.md +363 -0
  14. package/.claude/commands/specrails/telemetry.md +552 -0
  15. package/.claude/commands/specrails/why.md +96 -0
  16. package/LICENSE +21 -0
  17. package/README.md +290 -0
  18. package/cli/dist/specrails-desktop.js +1098 -0
  19. package/client/dist/assets/ActivityFeedPage-Gy4x8dBt.js +1 -0
  20. package/client/dist/assets/AgentsPage-CPgu--Fb.js +86 -0
  21. package/client/dist/assets/AnalyticsPage-B5sJEee2.js +1 -0
  22. package/client/dist/assets/BarChart-7IMQ8HY1.js +33 -0
  23. package/client/dist/assets/CodePage-CBdFvbwe.js +2 -0
  24. package/client/dist/assets/DesktopAnalyticsPage-w0rdTq4w.js +1 -0
  25. package/client/dist/assets/DocsDialog-BZUYM7wm.js +11 -0
  26. package/client/dist/assets/DocsPage-9QglWl46.js +11 -0
  27. package/client/dist/assets/ExportDropdown-BLZFXtNi.js +1 -0
  28. package/client/dist/assets/IntegrationsPage-BxBE4y99.js +3 -0
  29. package/client/dist/assets/JobDetailPage-DydWx_5S.js +16 -0
  30. package/client/dist/assets/JobsPage-20ibw0IO.js +1 -0
  31. package/client/dist/assets/abap-Bw6f2wDG.js +1 -0
  32. package/client/dist/assets/activity-BEIp_Y1A.js +1 -0
  33. package/client/dist/assets/activity-BdrPln96.js +1 -0
  34. package/client/dist/assets/activity-CpkRS8Sx.js +1 -0
  35. package/client/dist/assets/activity-DKCpESPt.js +1 -0
  36. package/client/dist/assets/activity-DOUVEjJi.js +1 -0
  37. package/client/dist/assets/activity-DRwkql_y.js +1 -0
  38. package/client/dist/assets/activity-DcDQ7tjw.js +1 -0
  39. package/client/dist/assets/activity-Dv6H7wEr.js +1 -0
  40. package/client/dist/assets/addon-image-3WCl5Vhd.js +1 -0
  41. package/client/dist/assets/addon-ligatures-C5OdliKs.js +2 -0
  42. package/client/dist/assets/addon-webgl-BbX6pSjl.js +44 -0
  43. package/client/dist/assets/addspec-B5yl4Loj.js +1 -0
  44. package/client/dist/assets/addspec-BEeF5-zc.js +1 -0
  45. package/client/dist/assets/addspec-D33ocMxf.js +1 -0
  46. package/client/dist/assets/addspec-DFswZ0jK.js +1 -0
  47. package/client/dist/assets/addspec-DRE-jZv7.js +1 -0
  48. package/client/dist/assets/addspec-DVZ15Jp8.js +1 -0
  49. package/client/dist/assets/addspec-Fkv91Opc.js +1 -0
  50. package/client/dist/assets/addspec-GWm4ffKl.js +1 -0
  51. package/client/dist/assets/agents-1nCDWRmP.js +1 -0
  52. package/client/dist/assets/agents-Bm9rPqnt.js +1 -0
  53. package/client/dist/assets/agents-CMxtJMLD.js +1 -0
  54. package/client/dist/assets/agents-DK-Dlc0i.js +1 -0
  55. package/client/dist/assets/agents-Q6Ldfpxx.js +1 -0
  56. package/client/dist/assets/agents-TeOSy-ax.js +1 -0
  57. package/client/dist/assets/agents-iTqjRajS.js +1 -0
  58. package/client/dist/assets/agents-s87sMGzL.js +1 -0
  59. package/client/dist/assets/agentstudio-B6Wb59E7.js +1 -0
  60. package/client/dist/assets/agentstudio-BADhZ41e.js +1 -0
  61. package/client/dist/assets/agentstudio-BSnWLR63.js +1 -0
  62. package/client/dist/assets/agentstudio-BdidyBzZ.js +1 -0
  63. package/client/dist/assets/agentstudio-CxlUllqI.js +1 -0
  64. package/client/dist/assets/agentstudio-D3I62TLJ.js +1 -0
  65. package/client/dist/assets/agentstudio-DuH9TogZ.js +1 -0
  66. package/client/dist/assets/agentstudio-Kw88_dUF.js +1 -0
  67. package/client/dist/assets/aiedit-BWxHGsYA.js +1 -0
  68. package/client/dist/assets/aiedit-D2ji6Qy0.js +1 -0
  69. package/client/dist/assets/aiedit-DAhZTvtk.js +1 -0
  70. package/client/dist/assets/aiedit-DJMny-D5.js +1 -0
  71. package/client/dist/assets/aiedit-DOcxERkU.js +1 -0
  72. package/client/dist/assets/aiedit-DvrcbwGv.js +1 -0
  73. package/client/dist/assets/aiedit-TTwzL1TS.js +1 -0
  74. package/client/dist/assets/aiedit-WBSjT_C1.js +1 -0
  75. package/client/dist/assets/analytics-BIdr0YfL.js +1 -0
  76. package/client/dist/assets/analytics-C6EzgtdE.js +1 -0
  77. package/client/dist/assets/analytics-C9Zc-rkM.js +1 -0
  78. package/client/dist/assets/analytics-CVx3YOc0.js +1 -0
  79. package/client/dist/assets/analytics-CYj0tfj7.js +1 -0
  80. package/client/dist/assets/analytics-CnY4kNG3.js +1 -0
  81. package/client/dist/assets/analytics-CrPCZRJ-.js +1 -0
  82. package/client/dist/assets/analytics-DMCto-TF.js +1 -0
  83. package/client/dist/assets/apex-Cw8_REBo.js +1 -0
  84. package/client/dist/assets/atom-one-dark-B-oHczHB.css +1 -0
  85. package/client/dist/assets/attachments-BIsSSnHJ.js +1 -0
  86. package/client/dist/assets/attachments-BW4L3l2L.js +1 -0
  87. package/client/dist/assets/attachments-Bcf6BG6V.js +1 -0
  88. package/client/dist/assets/attachments-Bke8sCU4.js +1 -0
  89. package/client/dist/assets/attachments-COcrGRFz.js +1 -0
  90. package/client/dist/assets/attachments-DYHGA2Dj.js +1 -0
  91. package/client/dist/assets/attachments-Dd92KpUH.js +1 -0
  92. package/client/dist/assets/attachments-DzdU6DV6.js +1 -0
  93. package/client/dist/assets/azcli-Cz6HAoOw.js +1 -0
  94. package/client/dist/assets/bat-CcJ-xyqL.js +1 -0
  95. package/client/dist/assets/bicep-z1WDCKYz.js +2 -0
  96. package/client/dist/assets/browser-5ErDlJoR.js +1 -0
  97. package/client/dist/assets/browser-Bc-YdlVg.js +1 -0
  98. package/client/dist/assets/browser-BlYF4OOq.js +1 -0
  99. package/client/dist/assets/browser-CT-ReZGt.js +1 -0
  100. package/client/dist/assets/browser-DGITz3fC.js +1 -0
  101. package/client/dist/assets/browser-JsAIGCEW.js +1 -0
  102. package/client/dist/assets/browser-M5-rbPlw.js +1 -0
  103. package/client/dist/assets/browser-Qya9cARy.js +1 -0
  104. package/client/dist/assets/cameligo-BRewOpfa.js +1 -0
  105. package/client/dist/assets/chat-BEGuC03z.js +1 -0
  106. package/client/dist/assets/chat-BEW60P_u.js +1 -0
  107. package/client/dist/assets/chat-BQNMD0PL.js +1 -0
  108. package/client/dist/assets/chat-BsbNGPW9.js +1 -0
  109. package/client/dist/assets/chat-CboQguCi.js +1 -0
  110. package/client/dist/assets/chat-DRCa9pOt.js +1 -0
  111. package/client/dist/assets/chat-DwUm6W9z.js +1 -0
  112. package/client/dist/assets/chat-yoXwguQu.js +1 -0
  113. package/client/dist/assets/chunk-CilyBKbf.js +1 -0
  114. package/client/dist/assets/clojure-DBjRWN6g.js +1 -0
  115. package/client/dist/assets/clsx-DnqN-uhr.js +1 -0
  116. package/client/dist/assets/code-AL1rVIMb.js +1 -0
  117. package/client/dist/assets/code-C0BKpkht.js +1 -0
  118. package/client/dist/assets/code-C0FTS3ew.js +1 -0
  119. package/client/dist/assets/code-CPcHxzxw.js +1 -0
  120. package/client/dist/assets/code-D3ryDniw.js +1 -0
  121. package/client/dist/assets/code-D3zVVQTj.js +1 -0
  122. package/client/dist/assets/code-PCmfS3dn.js +1 -0
  123. package/client/dist/assets/code-exI0G5Wd.js +1 -0
  124. package/client/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
  125. package/client/dist/assets/coffee-Cfk_XHGR.js +1 -0
  126. package/client/dist/assets/commands-B772IyDa.js +1 -0
  127. package/client/dist/assets/commands-BDDp6xFG.js +1 -0
  128. package/client/dist/assets/commands-CJxCry-o.js +1 -0
  129. package/client/dist/assets/commands-CfgY-_of.js +1 -0
  130. package/client/dist/assets/commands-DLrvnPNg.js +1 -0
  131. package/client/dist/assets/commands-IXMOKBYt.js +1 -0
  132. package/client/dist/assets/commands-UD1NzmwX.js +1 -0
  133. package/client/dist/assets/commands-sqrqsxyE.js +1 -0
  134. package/client/dist/assets/common-DCr6VzJ7.js +1 -0
  135. package/client/dist/assets/common-Dard9UNH.js +1 -0
  136. package/client/dist/assets/common-DeDELLZJ.js +1 -0
  137. package/client/dist/assets/common-DltqHaAe.js +1 -0
  138. package/client/dist/assets/common-Dmm1GhdD.js +1 -0
  139. package/client/dist/assets/common-DnjcgkPH.js +1 -0
  140. package/client/dist/assets/common-GbpxfPG8.js +1 -0
  141. package/client/dist/assets/common-wA36jmj1.js +1 -0
  142. package/client/dist/assets/cpp-BVob6BaP.js +1 -0
  143. package/client/dist/assets/csharp-C4fbRuOu.js +1 -0
  144. package/client/dist/assets/csp-DthFP_vT.js +1 -0
  145. package/client/dist/assets/css-CGMH0hcW.js +3 -0
  146. package/client/dist/assets/css.worker-Wv5dxAWO.js +89 -0
  147. package/client/dist/assets/cssMode-Cc6ozl-J.js +1 -0
  148. package/client/dist/assets/cypher-Pnf68BRV.js +1 -0
  149. package/client/dist/assets/dart-PMMOtxZX.js +1 -0
  150. package/client/dist/assets/dashboard-B4ixDVk8.js +1 -0
  151. package/client/dist/assets/dashboard-BZBADHSj.js +1 -0
  152. package/client/dist/assets/dashboard-C1MfeUHs.js +1 -0
  153. package/client/dist/assets/dashboard-C7SK6xu5.js +1 -0
  154. package/client/dist/assets/dashboard-CB6Le1yN.js +1 -0
  155. package/client/dist/assets/dashboard-CoTpMOBM.js +1 -0
  156. package/client/dist/assets/dashboard-Duo4DDCW.js +1 -0
  157. package/client/dist/assets/dashboard-I19DXBxw.js +1 -0
  158. package/client/dist/assets/dist-js-BY-Fv_fg.js +1 -0
  159. package/client/dist/assets/dist-js-Bakc4uxT.js +1 -0
  160. package/client/dist/assets/dockerfile-di1nsJCc.js +1 -0
  161. package/client/dist/assets/ecl-D_WVcB5M.js +1 -0
  162. package/client/dist/assets/editor-Br_kD0ds.css +1 -0
  163. package/client/dist/assets/editor.api2-XLGzZfbc.js +872 -0
  164. package/client/dist/assets/editor.main-CfXxHimg.js +6 -0
  165. package/client/dist/assets/editor.worker-Bd9IXS8d.js +26 -0
  166. package/client/dist/assets/elixir-OAdJEMOn.js +1 -0
  167. package/client/dist/assets/explore-4mFpnrKU.js +1 -0
  168. package/client/dist/assets/explore-A8Ltoblq.js +1 -0
  169. package/client/dist/assets/explore-B9A3iN2W.js +1 -0
  170. package/client/dist/assets/explore-BV5Xxlsn.js +1 -0
  171. package/client/dist/assets/explore-BrBJvfjP.js +1 -0
  172. package/client/dist/assets/explore-C3FSE42C.js +1 -0
  173. package/client/dist/assets/explore-D2EFgt8J.js +1 -0
  174. package/client/dist/assets/explore-hFc3HFcp.js +1 -0
  175. package/client/dist/assets/flow9-D3QEZjgn.js +1 -0
  176. package/client/dist/assets/format-command-CwGuwzGA.js +1 -0
  177. package/client/dist/assets/freemarker2-DP7J1gG3.js +3 -0
  178. package/client/dist/assets/fsharp-BF0k_8N8.js +1 -0
  179. package/client/dist/assets/go-BAQO5Jsz.js +1 -0
  180. package/client/dist/assets/graphql-hdFVFkiV.js +1 -0
  181. package/client/dist/assets/handlebars-BjRlucw6.js +1 -0
  182. package/client/dist/assets/hcl-DWnl1o-X.js +1 -0
  183. package/client/dist/assets/html-OumBQJ-U.js +1 -0
  184. package/client/dist/assets/html.worker-CQP8QQsS.js +502 -0
  185. package/client/dist/assets/htmlMode-CStc3zXM.js +1 -0
  186. package/client/dist/assets/index-CimDRRi7.css +2 -0
  187. package/client/dist/assets/index-XGZaKl_u.js +142 -0
  188. package/client/dist/assets/ini-CB-6OVu3.js +1 -0
  189. package/client/dist/assets/integrations-C3p12Ms6.js +1 -0
  190. package/client/dist/assets/integrations-Cr6hH7XR.js +1 -0
  191. package/client/dist/assets/integrations-Cublz3m6.js +1 -0
  192. package/client/dist/assets/integrations-D28q1kF6.js +1 -0
  193. package/client/dist/assets/integrations-DRdbki5W.js +1 -0
  194. package/client/dist/assets/integrations-DaC4SzzL.js +1 -0
  195. package/client/dist/assets/integrations-DmQYCUvN.js +1 -0
  196. package/client/dist/assets/integrations-HIlUxXVs.js +1 -0
  197. package/client/dist/assets/java-d1CmfiHX.js +1 -0
  198. package/client/dist/assets/javascript-CMk--e7g.js +1 -0
  199. package/client/dist/assets/jobs-BE1siB0M.js +1 -0
  200. package/client/dist/assets/jobs-BHcQ_Faf.js +1 -0
  201. package/client/dist/assets/jobs-CFfc2dNX.js +1 -0
  202. package/client/dist/assets/jobs-CSi5n8X_.js +1 -0
  203. package/client/dist/assets/jobs-Dc3X86PY.js +1 -0
  204. package/client/dist/assets/jobs-De5tASex.js +1 -0
  205. package/client/dist/assets/jobs-DsoXEdo7.js +1 -0
  206. package/client/dist/assets/jobs-Wl-ApPMb.js +1 -0
  207. package/client/dist/assets/json.worker-DzV-CpCQ.js +58 -0
  208. package/client/dist/assets/jsonMode-C2h3ZcjZ.js +7 -0
  209. package/client/dist/assets/julia-Bgv08lKa.js +1 -0
  210. package/client/dist/assets/kotlin-u98kaVTf.js +1 -0
  211. package/client/dist/assets/less-CjYwpgg5.js +2 -0
  212. package/client/dist/assets/lexon-YTjaAFBB.js +1 -0
  213. package/client/dist/assets/lib-CPxTMOAq.js +7 -0
  214. package/client/dist/assets/liquid-mI3KJrBE.js +1 -0
  215. package/client/dist/assets/lspLanguageFeatures-DU09ggWi.js +4 -0
  216. package/client/dist/assets/lua-BzmkWv27.js +1 -0
  217. package/client/dist/assets/m3-CFwk9fw0.js +1 -0
  218. package/client/dist/assets/markdown-CR5iMpSZ.js +1 -0
  219. package/client/dist/assets/mdx-C41VDTR_.js +1 -0
  220. package/client/dist/assets/mips-CcEalc17.js +1 -0
  221. package/client/dist/assets/monaco.contribution-CPObAXMC.js +2 -0
  222. package/client/dist/assets/msdax-BQbkawnr.js +1 -0
  223. package/client/dist/assets/mysql-GTlaaW_P.js +1 -0
  224. package/client/dist/assets/nav-0fwkrgHt.js +1 -0
  225. package/client/dist/assets/nav-BEL3MTwK.js +1 -0
  226. package/client/dist/assets/nav-B_G-TJDW.js +1 -0
  227. package/client/dist/assets/nav-C2YXcbZS.js +1 -0
  228. package/client/dist/assets/nav-ClzOE4mA.js +1 -0
  229. package/client/dist/assets/nav-CtYwmMgu.js +1 -0
  230. package/client/dist/assets/nav-D2bOGSEg.js +1 -0
  231. package/client/dist/assets/nav-iH1V5j6o.js +1 -0
  232. package/client/dist/assets/objective-c-Byu1T5if.js +1 -0
  233. package/client/dist/assets/pascal-BrfzBfRm.js +1 -0
  234. package/client/dist/assets/pascaligo-BXXKFUeo.js +1 -0
  235. package/client/dist/assets/perl-B3OikKq-.js +1 -0
  236. package/client/dist/assets/pgsql-CTsa0Acc.js +1 -0
  237. package/client/dist/assets/php-DiQh3FUW.js +1 -0
  238. package/client/dist/assets/pla-92uH8Fzm.js +1 -0
  239. package/client/dist/assets/postiats-BbeWkKUr.js +1 -0
  240. package/client/dist/assets/powerquery-DgDMzpsm.js +1 -0
  241. package/client/dist/assets/powershell-BfdUUzaG.js +1 -0
  242. package/client/dist/assets/preload-helper-DSXbuxSR.js +1 -0
  243. package/client/dist/assets/protobuf-BojW2ftW.js +2 -0
  244. package/client/dist/assets/pug-BxqTg3IU.js +1 -0
  245. package/client/dist/assets/python-Y27rKQtk.js +1 -0
  246. package/client/dist/assets/qsharp-BX_A-MW9.js +1 -0
  247. package/client/dist/assets/r-D9BMnxvJ.js +1 -0
  248. package/client/dist/assets/razor-Cd5-q9Bp.js +1 -0
  249. package/client/dist/assets/redis-5cJqEQJJ.js +1 -0
  250. package/client/dist/assets/redshift-d8BBqiwb.js +1 -0
  251. package/client/dist/assets/restructuredtext-C8a6yIcZ.js +1 -0
  252. package/client/dist/assets/ruby-egeh-6KX.js +1 -0
  253. package/client/dist/assets/rust-a3r9IInB.js +1 -0
  254. package/client/dist/assets/sb-y8iRIDei.js +1 -0
  255. package/client/dist/assets/scala-BPDK2AmK.js +1 -0
  256. package/client/dist/assets/scheme-BIWUEoOs.js +1 -0
  257. package/client/dist/assets/scss-CA-PSzwg.js +3 -0
  258. package/client/dist/assets/settings-55oDcbSh.js +1 -0
  259. package/client/dist/assets/settings-Bd4Tq1RB.js +1 -0
  260. package/client/dist/assets/settings-CCSM-Fhn.js +1 -0
  261. package/client/dist/assets/settings-D3e_bDoW.js +1 -0
  262. package/client/dist/assets/settings-DKbTkbn7.js +1 -0
  263. package/client/dist/assets/settings-Dxpo6_w7.js +1 -0
  264. package/client/dist/assets/settings-bt84e3Aa.js +1 -0
  265. package/client/dist/assets/settings-nu68QukM.js +1 -0
  266. package/client/dist/assets/setup-BMqwfbW9.js +1 -0
  267. package/client/dist/assets/setup-Bb5LcG28.js +1 -0
  268. package/client/dist/assets/setup-BeEx2_da.js +1 -0
  269. package/client/dist/assets/setup-CCCrB53Q.js +1 -0
  270. package/client/dist/assets/setup-CJA0ATmd.js +1 -0
  271. package/client/dist/assets/setup-CeiDbZcb.js +1 -0
  272. package/client/dist/assets/setup-Cus7TApA.js +1 -0
  273. package/client/dist/assets/setup-D9qOs2Xo.js +1 -0
  274. package/client/dist/assets/shell--LiT1Bja.js +1 -0
  275. package/client/dist/assets/solidity-DdqZccZg.js +1 -0
  276. package/client/dist/assets/sophia-S6-YxNG_.js +1 -0
  277. package/client/dist/assets/sparql-BSf5kMp2.js +1 -0
  278. package/client/dist/assets/specs-BFfu3u-a.js +1 -0
  279. package/client/dist/assets/specs-B__C8-8a.js +1 -0
  280. package/client/dist/assets/specs-CZ1PsXsC.js +1 -0
  281. package/client/dist/assets/specs-D2FzlLn9.js +1 -0
  282. package/client/dist/assets/specs-DaUTrNF9.js +1 -0
  283. package/client/dist/assets/specs-Dyc5hYeE.js +1 -0
  284. package/client/dist/assets/specs-cKEh2LXt.js +1 -0
  285. package/client/dist/assets/specs-k0PyLDVt.js +1 -0
  286. package/client/dist/assets/sql-D7KgjR8G.js +1 -0
  287. package/client/dist/assets/st-BnoDa-Ml.js +1 -0
  288. package/client/dist/assets/swift-DEUHTkUX.js +1 -0
  289. package/client/dist/assets/systemverilog-Tqb_KPnW.js +1 -0
  290. package/client/dist/assets/tcl-BmBFS2qq.js +1 -0
  291. package/client/dist/assets/terminal-80yDMgMF.js +1 -0
  292. package/client/dist/assets/terminal-Bje4ziIa.js +1 -0
  293. package/client/dist/assets/terminal-C2WYcFHF.js +1 -0
  294. package/client/dist/assets/terminal-CSONJOex.js +1 -0
  295. package/client/dist/assets/terminal-DEqzGtcr.js +1 -0
  296. package/client/dist/assets/terminal-DeWzh6ys.js +1 -0
  297. package/client/dist/assets/terminal-YOlsJCQj.js +1 -0
  298. package/client/dist/assets/terminal-lkZYR4wJ.js +1 -0
  299. package/client/dist/assets/tickets-CB7N30gm.js +1 -0
  300. package/client/dist/assets/tickets-CF2PYelu.js +1 -0
  301. package/client/dist/assets/tickets-DNOANUXr.js +1 -0
  302. package/client/dist/assets/tickets-DU1aqsbr.js +1 -0
  303. package/client/dist/assets/tickets-DYvafSaY.js +1 -0
  304. package/client/dist/assets/tickets-DlpC_iTg.js +1 -0
  305. package/client/dist/assets/tickets-DucYgtdl.js +1 -0
  306. package/client/dist/assets/tickets-clefmXLv.js +1 -0
  307. package/client/dist/assets/ts.worker-METxwbDZ.js +67719 -0
  308. package/client/dist/assets/tsMode-B0y_xEci.js +11 -0
  309. package/client/dist/assets/twig-BQV8igWC.js +1 -0
  310. package/client/dist/assets/typescript-BzK0OgwW.js +1 -0
  311. package/client/dist/assets/typespec-DlFroUGY.js +1 -0
  312. package/client/dist/assets/useProjectCache-DSaiGFjV.js +1 -0
  313. package/client/dist/assets/vb-BlrJpIMX.js +1 -0
  314. package/client/dist/assets/wgsl-BWgIc6FZ.js +298 -0
  315. package/client/dist/assets/workers-rt--R2Qy.js +1 -0
  316. package/client/dist/assets/xml-eX9QXAmI.js +1 -0
  317. package/client/dist/assets/yaml-fcsNkpOt.js +1 -0
  318. package/client/dist/index.html +246 -0
  319. package/docs/README.md +54 -0
  320. package/docs/cli.md +198 -0
  321. package/docs/codex.md +210 -0
  322. package/docs/creating-specs.md +197 -0
  323. package/docs/customizing.md +197 -0
  324. package/docs/getting-started.md +140 -0
  325. package/docs/internals/README.md +25 -0
  326. package/docs/internals/adding-a-provider.md +238 -0
  327. package/docs/internals/api-reference.md +634 -0
  328. package/docs/internals/architecture.md +332 -0
  329. package/docs/internals/configuration.md +172 -0
  330. package/docs/internals/openspec-workflow.md +282 -0
  331. package/docs/internals/operations-runbook.md +198 -0
  332. package/docs/internals/profiles.md +152 -0
  333. package/docs/platforms/macos.md +130 -0
  334. package/docs/platforms/windows.md +81 -0
  335. package/docs/running-pipelines.md +240 -0
  336. package/docs/terminal.md +138 -0
  337. package/docs/tracking-cost.md +155 -0
  338. package/package.json +82 -0
  339. package/server/dist/agent-generator.js +232 -0
  340. package/server/dist/agent-refine-db.js +124 -0
  341. package/server/dist/agent-refine-manager.js +526 -0
  342. package/server/dist/ai-invocations.js +111 -0
  343. package/server/dist/attachment-manager.js +299 -0
  344. package/server/dist/auth.js +207 -0
  345. package/server/dist/binary-probe.js +35 -0
  346. package/server/dist/browser-capture-manager.js +576 -0
  347. package/server/dist/browser-capture-types.js +28 -0
  348. package/server/dist/browser-network.js +149 -0
  349. package/server/dist/browser-playwright.js +888 -0
  350. package/server/dist/build-dirs.js +44 -0
  351. package/server/dist/changes-reader.js +120 -0
  352. package/server/dist/chat-manager.js +1060 -0
  353. package/server/dist/chromium-resolver.js +311 -0
  354. package/server/dist/code-explorer-router.js +788 -0
  355. package/server/dist/codex-otel-bridge.js +235 -0
  356. package/server/dist/command-resolver.js +102 -0
  357. package/server/dist/config.js +306 -0
  358. package/server/dist/context-budget.js +113 -0
  359. package/server/dist/context-scope.js +279 -0
  360. package/server/dist/contract-refine-runner.js +521 -0
  361. package/server/dist/core-compat.js +207 -0
  362. package/server/dist/core-package.js +14 -0
  363. package/server/dist/db.js +1034 -0
  364. package/server/dist/desktop-analytics.js +156 -0
  365. package/server/dist/desktop-db.js +456 -0
  366. package/server/dist/desktop-router.js +735 -0
  367. package/server/dist/docs-router.js +207 -0
  368. package/server/dist/explore-contract-refine.js +421 -0
  369. package/server/dist/explore-cwd-manager.js +242 -0
  370. package/server/dist/explore-draft-title.js +47 -0
  371. package/server/dist/explore-smash.js +450 -0
  372. package/server/dist/feature-flags.js +17 -0
  373. package/server/dist/file-provenance.js +382 -0
  374. package/server/dist/file-summary-generator.js +221 -0
  375. package/server/dist/file-summary-manager.js +689 -0
  376. package/server/dist/hooks.js +102 -0
  377. package/server/dist/ids.js +7 -0
  378. package/server/dist/index.js +586 -0
  379. package/server/dist/metrics.js +136 -0
  380. package/server/dist/mobile/index.js +16 -0
  381. package/server/dist/mobile/mobile-admin-router.js +84 -0
  382. package/server/dist/mobile/mobile-auth.js +67 -0
  383. package/server/dist/mobile/mobile-devices.js +80 -0
  384. package/server/dist/mobile/mobile-event-bus.js +39 -0
  385. package/server/dist/mobile/mobile-gateway.js +285 -0
  386. package/server/dist/mobile/mobile-mdns.js +81 -0
  387. package/server/dist/mobile/mobile-pairing.js +179 -0
  388. package/server/dist/mobile/mobile-redact.js +53 -0
  389. package/server/dist/mobile/mobile-router.js +411 -0
  390. package/server/dist/mobile/mobile-tls.js +86 -0
  391. package/server/dist/mobile/mobile-types.js +9 -0
  392. package/server/dist/mobile/mobile-ws.js +275 -0
  393. package/server/dist/path-resolver.js +298 -0
  394. package/server/dist/plugin-manager.js +617 -0
  395. package/server/dist/plugins/claude-approval.js +179 -0
  396. package/server/dist/plugins/claude-md-mutation.js +146 -0
  397. package/server/dist/plugins/codex-mcp.js +108 -0
  398. package/server/dist/plugins/contributors.js +72 -0
  399. package/server/dist/plugins/drift.js +58 -0
  400. package/server/dist/plugins/index.js +14 -0
  401. package/server/dist/plugins/json-mutation.js +120 -0
  402. package/server/dist/plugins/manager.js +32 -0
  403. package/server/dist/plugins/ownership.js +86 -0
  404. package/server/dist/plugins/paths.js +37 -0
  405. package/server/dist/plugins/prereq-installer.js +104 -0
  406. package/server/dist/plugins/rail-integration.js +79 -0
  407. package/server/dist/plugins/serena/index.js +13 -0
  408. package/server/dist/plugins/serena/install.js +91 -0
  409. package/server/dist/plugins/serena/instructions-content.js +21 -0
  410. package/server/dist/plugins/serena/manifest.js +111 -0
  411. package/server/dist/plugins/serena/verify.js +78 -0
  412. package/server/dist/plugins-router.js +215 -0
  413. package/server/dist/pricing.js +89 -0
  414. package/server/dist/profile-manager.js +310 -0
  415. package/server/dist/profiles-router.js +759 -0
  416. package/server/dist/project-registry.js +443 -0
  417. package/server/dist/project-router.js +4016 -0
  418. package/server/dist/proposal-manager.js +291 -0
  419. package/server/dist/provider-selection.js +69 -0
  420. package/server/dist/providers/claude-adapter.js +281 -0
  421. package/server/dist/providers/codex-adapter.js +264 -0
  422. package/server/dist/providers/index.js +23 -0
  423. package/server/dist/providers/registry.js +37 -0
  424. package/server/dist/providers/types.js +22 -0
  425. package/server/dist/queue-manager.js +1511 -0
  426. package/server/dist/rails-router.js +362 -0
  427. package/server/dist/rails-store.js +116 -0
  428. package/server/dist/result-event.js +106 -0
  429. package/server/dist/schemas/profile.v1.json +151 -0
  430. package/server/dist/setup-manager.js +1165 -0
  431. package/server/dist/setup-prerequisites.js +372 -0
  432. package/server/dist/smash-runner.js +663 -0
  433. package/server/dist/spec-draft-parser.js +133 -0
  434. package/server/dist/spec-launcher-manager.js +174 -0
  435. package/server/dist/spec-models.js +32 -0
  436. package/server/dist/specrails-tech-client.js +82 -0
  437. package/server/dist/spending.js +448 -0
  438. package/server/dist/telemetry-compactor.js +180 -0
  439. package/server/dist/telemetry-export.js +317 -0
  440. package/server/dist/telemetry-receiver.js +224 -0
  441. package/server/dist/terminal-manager.js +633 -0
  442. package/server/dist/terminal-marks-store.js +117 -0
  443. package/server/dist/terminal-osc-parser.js +159 -0
  444. package/server/dist/terminal-settings.js +282 -0
  445. package/server/dist/terminal-shell-integration.js +196 -0
  446. package/server/dist/ticket-broadcast.js +47 -0
  447. package/server/dist/ticket-store.js +397 -0
  448. package/server/dist/ticket-watcher.js +117 -0
  449. package/server/dist/types.js +10 -0
  450. package/server/dist/user-mcp-config.js +117 -0
  451. package/server/dist/util/cli-prompt.js +181 -0
  452. package/server/dist/util/secure-fs.js +50 -0
  453. package/server/dist/util/win-spawn.js +43 -0
  454. package/server/dist/webhook-manager.js +89 -0
  455. package/server/dist/ws-routing.js +47 -0
@@ -0,0 +1,317 @@
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.redactSecrets = redactSecrets;
7
+ exports.createDiagnosticZip = createDiagnosticZip;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const zlib_1 = __importDefault(require("zlib"));
10
+ // ─── Minimal ZIP writer ───────────────────────────────────────────────────────
11
+ // ZIP format is simple: local file headers + data + central directory + EOCD.
12
+ // We write stored (no compression) entries for simplicity — the outer gzip on
13
+ // the blob is already compressed; NDJSON text compresses fine at transfer layer.
14
+ function crc32(buf) {
15
+ // Standard CRC-32 polynomial table
16
+ const table = crc32Table();
17
+ let crc = 0xffffffff;
18
+ for (let i = 0; i < buf.length; i++) {
19
+ crc = (crc >>> 8) ^ table[(crc ^ buf[i]) & 0xff];
20
+ }
21
+ return (crc ^ 0xffffffff) >>> 0;
22
+ }
23
+ let _crcTable = null;
24
+ function crc32Table() {
25
+ if (_crcTable)
26
+ return _crcTable;
27
+ _crcTable = new Uint32Array(256);
28
+ for (let i = 0; i < 256; i++) {
29
+ let c = i;
30
+ for (let k = 0; k < 8; k++) {
31
+ c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1);
32
+ }
33
+ _crcTable[i] = c;
34
+ }
35
+ return _crcTable;
36
+ }
37
+ function buildZip(entries) {
38
+ const localHeaders = [];
39
+ const centralDirs = [];
40
+ let offset = 0;
41
+ for (const entry of entries) {
42
+ const nameBytes = Buffer.from(entry.name, 'utf-8');
43
+ const data = entry.data;
44
+ const crc = crc32(data);
45
+ const size = data.length;
46
+ // Local file header (30 bytes + name)
47
+ const local = Buffer.alloc(30 + nameBytes.length);
48
+ local.writeUInt32LE(0x04034b50, 0); // signature
49
+ local.writeUInt16LE(20, 4); // version needed
50
+ local.writeUInt16LE(0, 6); // flags
51
+ local.writeUInt16LE(0, 8); // compression = STORED
52
+ local.writeUInt16LE(0, 10); // mod time
53
+ local.writeUInt16LE(0, 12); // mod date
54
+ local.writeUInt32LE(crc, 14); // crc-32
55
+ local.writeUInt32LE(size, 18); // compressed size
56
+ local.writeUInt32LE(size, 22); // uncompressed size
57
+ local.writeUInt16LE(nameBytes.length, 26); // file name length
58
+ local.writeUInt16LE(0, 28); // extra field length
59
+ nameBytes.copy(local, 30);
60
+ // Central directory header (46 bytes + name)
61
+ const central = Buffer.alloc(46 + nameBytes.length);
62
+ central.writeUInt32LE(0x02014b50, 0); // signature
63
+ central.writeUInt16LE(20, 4); // version made by
64
+ central.writeUInt16LE(20, 6); // version needed
65
+ central.writeUInt16LE(0, 8); // flags
66
+ central.writeUInt16LE(0, 10); // compression = STORED
67
+ central.writeUInt16LE(0, 12); // mod time
68
+ central.writeUInt16LE(0, 14); // mod date
69
+ central.writeUInt32LE(crc, 16); // crc-32
70
+ central.writeUInt32LE(size, 20); // compressed size
71
+ central.writeUInt32LE(size, 24); // uncompressed size
72
+ central.writeUInt16LE(nameBytes.length, 28); // file name length
73
+ central.writeUInt16LE(0, 30); // extra field length
74
+ central.writeUInt16LE(0, 32); // file comment length
75
+ central.writeUInt16LE(0, 34); // disk number start
76
+ central.writeUInt16LE(0, 36); // int file attributes
77
+ central.writeUInt32LE(0, 38); // ext file attributes
78
+ central.writeUInt32LE(offset, 42); // relative offset of local header
79
+ nameBytes.copy(central, 46);
80
+ localHeaders.push(local, data);
81
+ centralDirs.push(central);
82
+ offset += local.length + size;
83
+ }
84
+ const centralDirBuf = Buffer.concat(centralDirs);
85
+ const centralDirSize = centralDirBuf.length;
86
+ const centralDirOffset = offset;
87
+ // End of central directory record
88
+ const eocd = Buffer.alloc(22);
89
+ eocd.writeUInt32LE(0x06054b50, 0); // signature
90
+ eocd.writeUInt16LE(0, 4); // disk number
91
+ eocd.writeUInt16LE(0, 6); // disk with CD
92
+ eocd.writeUInt16LE(entries.length, 8); // entries on this disk
93
+ eocd.writeUInt16LE(entries.length, 10); // total entries
94
+ eocd.writeUInt32LE(centralDirSize, 12); // central dir size
95
+ eocd.writeUInt32LE(centralDirOffset, 16); // central dir offset
96
+ eocd.writeUInt16LE(0, 20); // comment length
97
+ return Buffer.concat([...localHeaders, centralDirBuf, eocd]);
98
+ }
99
+ // ─── NDJSON reader ────────────────────────────────────────────────────────────
100
+ function decompressNdjson(filePath) {
101
+ try {
102
+ const compressed = fs_1.default.readFileSync(filePath);
103
+ return zlib_1.default.gunzipSync(compressed).toString('utf-8');
104
+ }
105
+ catch {
106
+ return '';
107
+ }
108
+ }
109
+ function hasTruncationMarker(filePath) {
110
+ const content = decompressNdjson(filePath);
111
+ return content.includes('"logs_truncated"');
112
+ }
113
+ // ─── Summary markdown ─────────────────────────────────────────────────────────
114
+ function buildSummaryFromRaw(filePath, job, truncated) {
115
+ const lines = [];
116
+ if (truncated) {
117
+ lines.push('> **truncated: true** — The 10 MB raw telemetry cap was hit during this run.');
118
+ lines.push('> Log payloads were dropped after the cap. Traces and metrics are complete.');
119
+ lines.push('');
120
+ }
121
+ lines.push(`# Diagnostic Summary — Job ${job.id}`);
122
+ lines.push('');
123
+ lines.push(`- **Command**: \`${job.command}\``);
124
+ lines.push(`- **Status**: ${job.status}`);
125
+ lines.push(`- **Started**: ${job.started_at}`);
126
+ lines.push(`- **Finished**: ${job.finished_at ?? 'N/A'}`);
127
+ if (job.duration_ms != null)
128
+ lines.push(`- **Duration**: ${job.duration_ms}ms`);
129
+ if (job.total_cost_usd != null)
130
+ lines.push(`- **Cost**: $${job.total_cost_usd.toFixed(4)}`);
131
+ if (job.tokens_in != null)
132
+ lines.push(`- **Tokens in**: ${job.tokens_in}`);
133
+ if (job.tokens_out != null)
134
+ lines.push(`- **Tokens out**: ${job.tokens_out}`);
135
+ lines.push('');
136
+ lines.push('Raw telemetry is available in `telemetry.ndjson`.');
137
+ return lines.join('\n');
138
+ }
139
+ function buildSummaryFromRows(summaries, job) {
140
+ const lines = [];
141
+ lines.push(`# Diagnostic Summary — Job ${job.id}`);
142
+ lines.push('');
143
+ lines.push(`- **Command**: \`${job.command}\``);
144
+ lines.push(`- **Status**: ${job.status}`);
145
+ lines.push(`- **Started**: ${job.started_at}`);
146
+ lines.push(`- **Finished**: ${job.finished_at ?? 'N/A'}`);
147
+ lines.push('');
148
+ lines.push('> Raw telemetry has been compacted (older than 7 days). Per-phase summary below.');
149
+ lines.push('');
150
+ lines.push('## Phase Summaries');
151
+ lines.push('');
152
+ for (const s of summaries) {
153
+ lines.push(`### Phase: ${s.phase}`);
154
+ if (s.durationMs != null)
155
+ lines.push(`- Duration: ${s.durationMs}ms`);
156
+ if (s.tokensInput != null)
157
+ lines.push(`- Tokens in: ${s.tokensInput}`);
158
+ if (s.tokensOutput != null)
159
+ lines.push(`- Tokens out: ${s.tokensOutput}`);
160
+ if (s.tokensCache != null)
161
+ lines.push(`- Cache tokens: ${s.tokensCache}`);
162
+ if (s.costUsd != null)
163
+ lines.push(`- Cost: $${s.costUsd.toFixed(4)}`);
164
+ if (s.apiErrors)
165
+ lines.push(`- API errors: ${s.apiErrors}`);
166
+ if (s.toolCalls) {
167
+ try {
168
+ const tc = JSON.parse(s.toolCalls);
169
+ lines.push(`- Tool calls: ${Object.entries(tc).map(([k, v]) => `${k}×${v}`).join(', ')}`);
170
+ }
171
+ catch {
172
+ lines.push(`- Tool calls: ${s.toolCalls}`);
173
+ }
174
+ }
175
+ lines.push('');
176
+ }
177
+ return lines.join('\n');
178
+ }
179
+ // ─── Log extraction ────────────────────────────────────────────────────────────
180
+ // App log output is never written to a flat file — it is streamed over
181
+ // WebSocket and persisted per-event in the `events` table. Reconstruct a
182
+ // readable logs.txt by formatting every event row in seq order.
183
+ function buildLogsFromEvents(events) {
184
+ if (events.length === 0) {
185
+ return '(No events recorded for this job.)\n';
186
+ }
187
+ const lines = [];
188
+ for (const ev of events) {
189
+ const ts = ev.timestamp;
190
+ const src = ev.source ?? ev.event_type;
191
+ let text = ev.payload;
192
+ // Most log events carry { line: string }; other event types may carry
193
+ // structured JSON. Extract the line field when present, else stringify.
194
+ try {
195
+ const parsed = JSON.parse(ev.payload);
196
+ if (typeof parsed.line === 'string')
197
+ text = parsed.line;
198
+ else if (typeof parsed.message === 'string')
199
+ text = parsed.message;
200
+ else
201
+ text = JSON.stringify(parsed);
202
+ }
203
+ catch {
204
+ // Payload isn't JSON — use raw string as-is
205
+ }
206
+ lines.push(`[${ts}] [${src}] ${redactSecrets(text)}`);
207
+ }
208
+ return lines.join('\n') + '\n';
209
+ }
210
+ /**
211
+ * Best-effort redaction of obvious secrets from diagnostic log text (B52). The
212
+ * exported ZIP is meant to be shared (support, bug reports), but the CLI event
213
+ * log can echo `export ANTHROPIC_API_KEY=...`, Bearer tokens, or provider keys.
214
+ * This scrubs the common shapes without destroying the log's diagnostic value.
215
+ */
216
+ function redactSecrets(text) {
217
+ return text
218
+ // KEY=VALUE / TOKEN: VALUE for anything ending in API_KEY/TOKEN/SECRET/PASSWORD/ACCESS_KEY
219
+ .replace(/\b([A-Za-z0-9_]*(?:API_KEY|TOKEN|SECRET|PASSWORD|ACCESS_KEY))(\s*[=:]\s*)(["']?)[^\s"']+\3/gi, '$1$2[REDACTED]')
220
+ // Provider key prefixes (Anthropic/OpenAI sk-..., GitHub gh*_...)
221
+ .replace(/\bsk-[A-Za-z0-9_-]{16,}/g, 'sk-[REDACTED]')
222
+ .replace(/\bgh[pousr]_[A-Za-z0-9]{16,}/g, 'gh_[REDACTED]')
223
+ // Authorization: Bearer <token>
224
+ .replace(/\b(Bearer)\s+[A-Za-z0-9._-]{12,}/gi, '$1 [REDACTED]');
225
+ }
226
+ async function createDiagnosticZip(res, opts) {
227
+ const { job, blob, summaries, events, profile, pluginSnapshotPath } = opts;
228
+ const entries = [];
229
+ // plugins.json — per-job plugin snapshot, if present.
230
+ let pluginsSummary = null;
231
+ if (pluginSnapshotPath && fs_1.default.existsSync(pluginSnapshotPath)) {
232
+ try {
233
+ const raw = fs_1.default.readFileSync(pluginSnapshotPath, 'utf8');
234
+ pluginsSummary = JSON.parse(raw);
235
+ entries.push({ name: 'plugins.json', data: Buffer.from(raw, 'utf-8') });
236
+ }
237
+ catch {
238
+ // ignore malformed snapshot — diagnostic still useful without it
239
+ }
240
+ }
241
+ // job-metadata.json
242
+ const metadata = {
243
+ id: job.id,
244
+ command: job.command,
245
+ status: job.status,
246
+ started_at: job.started_at,
247
+ finished_at: job.finished_at,
248
+ duration_ms: job.duration_ms,
249
+ total_cost_usd: job.total_cost_usd,
250
+ tokens_in: job.tokens_in,
251
+ tokens_out: job.tokens_out,
252
+ tokens_cache_read: job.tokens_cache_read,
253
+ tokens_cache_create: job.tokens_cache_create,
254
+ num_turns: job.num_turns,
255
+ model: job.model,
256
+ session_id: job.session_id,
257
+ };
258
+ entries.push({
259
+ name: 'job-metadata.json',
260
+ data: Buffer.from(JSON.stringify({ ...metadata, profile_name: profile?.name ?? null }, null, 2), 'utf-8'),
261
+ });
262
+ // profile.json — the snapshot the job ran under (if any)
263
+ if (profile && profile.json) {
264
+ entries.push({
265
+ name: 'profile.json',
266
+ data: Buffer.from(profile.json, 'utf-8'),
267
+ });
268
+ }
269
+ // telemetry.ndjson
270
+ let truncated = false;
271
+ if (blob.state === 'active' && blob.path && fs_1.default.existsSync(blob.path)) {
272
+ truncated = hasTruncationMarker(blob.path);
273
+ const ndjsonContent = decompressNdjson(blob.path);
274
+ entries.push({
275
+ name: 'telemetry.ndjson',
276
+ data: Buffer.from(ndjsonContent, 'utf-8'),
277
+ });
278
+ }
279
+ else {
280
+ // Compacted: include a header note
281
+ const header = '# Telemetry data has been compacted (raw blob older than 7 days).\n# See summary.md for per-phase aggregates.\n';
282
+ entries.push({
283
+ name: 'telemetry.ndjson',
284
+ data: Buffer.from(header, 'utf-8'),
285
+ });
286
+ }
287
+ // logs.txt — reconstructed from persisted events
288
+ const logsContent = buildLogsFromEvents(events);
289
+ entries.push({
290
+ name: 'logs.txt',
291
+ data: Buffer.from(logsContent, 'utf-8'),
292
+ });
293
+ // summary.md
294
+ let summaryContent;
295
+ if (blob.state === 'active' && blob.path) {
296
+ summaryContent = buildSummaryFromRaw(blob.path, job, truncated);
297
+ }
298
+ else {
299
+ summaryContent = buildSummaryFromRows(summaries, job);
300
+ }
301
+ if (pluginsSummary) {
302
+ const lines = ['', '## Plugins'];
303
+ if (pluginsSummary.active.length > 0) {
304
+ lines.push('', '### Active', ...pluginsSummary.active.map((p) => `- ${p.name}@${p.version}`));
305
+ }
306
+ if (pluginsSummary.degraded.length > 0) {
307
+ lines.push('', '### Degraded', ...pluginsSummary.degraded.map((p) => `- ${p.name} (${p.reason})`));
308
+ }
309
+ summaryContent += '\n' + lines.join('\n') + '\n';
310
+ }
311
+ entries.push({
312
+ name: 'summary.md',
313
+ data: Buffer.from(summaryContent, 'utf-8'),
314
+ });
315
+ const zipBuf = buildZip(entries);
316
+ res.end(zipBuf);
317
+ }
@@ -0,0 +1,224 @@
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.createTelemetryRouter = createTelemetryRouter;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const zlib_1 = __importDefault(require("zlib"));
11
+ const express_1 = require("express");
12
+ const db_1 = require("./db");
13
+ const db_2 = require("./db");
14
+ // 10 MB uncompressed cap per job blob
15
+ const BLOB_SIZE_CAP = 10 * 1024 * 1024;
16
+ // B2: the soft cap above only dropped `logs`, so traces/metrics could grow a
17
+ // per-job blob without bound (disk-fill DoS). This hard cap bounds the TOTAL
18
+ // blob across ALL signals while still letting traces/metrics outlive logs.
19
+ const BLOB_HARD_CAP = 50 * 1024 * 1024;
20
+ // Bounded in-memory append queue per (projectId, jobId) key — protects the
21
+ // Express event loop from a burst of OTLP payloads all trying to gzip-append
22
+ // simultaneously. Drops events (with a warning) when the queue exceeds this.
23
+ const QUEUE_CAP = 10_000;
24
+ const _blobState = new Map();
25
+ function getBlobState(key) {
26
+ let s = _blobState.get(key);
27
+ if (!s) {
28
+ s = { uncompressedSize: 0, truncationMarkerWritten: false, queue: [], writing: false };
29
+ _blobState.set(key, s);
30
+ }
31
+ return s;
32
+ }
33
+ function blobKey(projectId, jobId) {
34
+ return `${projectId}:${jobId}`;
35
+ }
36
+ // ─── Blob path ────────────────────────────────────────────────────────────────
37
+ function telemetryDir(projectSlug) {
38
+ return path_1.default.join(os_1.default.homedir(), '.specrails', 'projects', projectSlug, 'telemetry');
39
+ }
40
+ function blobPath(projectSlug, jobId) {
41
+ return path_1.default.join(telemetryDir(projectSlug), `${jobId}.ndjson.gz`);
42
+ }
43
+ // ─── Extract resource attributes ─────────────────────────────────────────────
44
+ function extractAttr(attributes, key) {
45
+ if (!attributes)
46
+ return undefined;
47
+ const attr = attributes.find((a) => a.key === key);
48
+ if (!attr)
49
+ return undefined;
50
+ const v = attr.value;
51
+ if (typeof v.stringValue === 'string')
52
+ return v.stringValue;
53
+ if (v.intValue != null)
54
+ return String(v.intValue);
55
+ return undefined;
56
+ }
57
+ function extractJobAndProject(body) {
58
+ if (typeof body !== 'object' || body === null)
59
+ return null;
60
+ // OTLP/JSON: resourceSpans | resourceMetrics | resourceLogs
61
+ const b = body;
62
+ const resourceItems = [
63
+ ...(Array.isArray(b.resourceSpans) ? b.resourceSpans : []),
64
+ ...(Array.isArray(b.resourceMetrics) ? b.resourceMetrics : []),
65
+ ...(Array.isArray(b.resourceLogs) ? b.resourceLogs : []),
66
+ ];
67
+ for (const rs of resourceItems) {
68
+ const attrs = rs.resource?.attributes;
69
+ const jobId = extractAttr(attrs, 'specrails.job_id');
70
+ const projectId = extractAttr(attrs, 'specrails.project_id');
71
+ if (jobId && projectId)
72
+ return { jobId, projectId };
73
+ }
74
+ return null;
75
+ }
76
+ // ─── Gzip append ─────────────────────────────────────────────────────────────
77
+ function appendToGzip(filePath, line) {
78
+ return new Promise((resolve, reject) => {
79
+ const data = Buffer.from(line + '\n', 'utf-8');
80
+ // Append a gzip member to the file. gzip is a concatenated format:
81
+ // a reader decompresses member by member, giving correct NDJSON.
82
+ const gz = zlib_1.default.createGzip();
83
+ const chunks = [];
84
+ gz.on('data', (c) => chunks.push(c));
85
+ gz.on('end', () => {
86
+ const compressed = Buffer.concat(chunks);
87
+ fs_1.default.appendFile(filePath, compressed, (err) => {
88
+ if (err)
89
+ reject(err);
90
+ else
91
+ resolve();
92
+ });
93
+ });
94
+ gz.on('error', reject);
95
+ gz.end(data);
96
+ });
97
+ }
98
+ // ─── Enqueue write task ───────────────────────────────────────────────────────
99
+ function enqueueWrite(state, task) {
100
+ if (state.queue.length >= QUEUE_CAP) {
101
+ // Drop with a warning — we don't want to stall the event loop
102
+ console.warn('[telemetry-receiver] append queue full — dropping telemetry event');
103
+ return;
104
+ }
105
+ const wrappedTask = () => {
106
+ task().then(drain, drain);
107
+ };
108
+ function drain() {
109
+ state.writing = false;
110
+ const next = state.queue.shift();
111
+ if (next) {
112
+ state.writing = true;
113
+ next();
114
+ }
115
+ }
116
+ state.queue.push(wrappedTask);
117
+ if (!state.writing) {
118
+ state.writing = true;
119
+ const next = state.queue.shift();
120
+ next();
121
+ }
122
+ }
123
+ // ─── Ingest handler ───────────────────────────────────────────────────────────
124
+ async function handleIngest(signal, body, registry, res) {
125
+ const ids = extractJobAndProject(body);
126
+ if (!ids) {
127
+ res.status(400).json({ error: 'Missing specrails.job_id or specrails.project_id in resource.attributes' });
128
+ return;
129
+ }
130
+ const { jobId, projectId } = ids;
131
+ const projectCtx = registry.getContext(projectId);
132
+ if (!projectCtx) {
133
+ res.status(404).json({ error: 'Project not found' });
134
+ return;
135
+ }
136
+ const { db, project } = projectCtx;
137
+ const job = (0, db_1.getJob)(db, jobId);
138
+ if (!job) {
139
+ res.status(404).json({ error: 'Job not found' });
140
+ return;
141
+ }
142
+ const key = blobKey(projectId, jobId);
143
+ const state = getBlobState(key);
144
+ const filePath = blobPath(project.slug, jobId);
145
+ const now = Date.now();
146
+ // Cap enforcement: drop logs once the 10 MB soft cap is reached (B2: and drop
147
+ // ANY signal — traces/metrics included — once the 50 MB hard cap is reached, so
148
+ // a job streaming endless spans can't fill the disk).
149
+ if (state.uncompressedSize >= BLOB_HARD_CAP || (state.uncompressedSize >= BLOB_SIZE_CAP && signal === 'logs')) {
150
+ res.status(200).json({ ok: true, dropped: true });
151
+ return;
152
+ }
153
+ // Determine raw line size for cap tracking (uncompressed JSON length)
154
+ const payloadStr = JSON.stringify(body);
155
+ const lineObj = { signal, receivedAt: new Date().toISOString(), payload: body };
156
+ const lineStr = JSON.stringify(lineObj);
157
+ const lineSize = Buffer.byteLength(lineStr, 'utf-8');
158
+ // Check if this write will push us over the cap
159
+ const willExceedCap = state.uncompressedSize + lineSize > BLOB_SIZE_CAP;
160
+ const prevSize = state.uncompressedSize;
161
+ state.uncompressedSize += lineSize;
162
+ // Ensure directory and blob pointer row exist before we enqueue the write
163
+ const dir = telemetryDir(project.slug);
164
+ fs_1.default.mkdirSync(dir, { recursive: true });
165
+ const existingBlob = (0, db_2.getTelemetryBlob)(db, jobId);
166
+ if (!existingBlob) {
167
+ (0, db_2.upsertTelemetryBlob)(db, {
168
+ jobId,
169
+ path: filePath,
170
+ byteSize: 0,
171
+ startedAt: now,
172
+ endedAt: now,
173
+ state: 'active',
174
+ });
175
+ }
176
+ else {
177
+ (0, db_2.upsertTelemetryBlob)(db, {
178
+ ...existingBlob,
179
+ byteSize: state.uncompressedSize,
180
+ endedAt: now,
181
+ });
182
+ }
183
+ // Enqueue the actual file append
184
+ enqueueWrite(state, async () => {
185
+ if (willExceedCap && signal === 'logs' && !state.truncationMarkerWritten) {
186
+ // Write the truncation marker once, before dropping this log event
187
+ state.truncationMarkerWritten = true;
188
+ const marker = JSON.stringify({ signal: 'control', event: 'logs_truncated', at: new Date().toISOString() });
189
+ await appendToGzip(filePath, marker);
190
+ }
191
+ // Drop further logs after cap
192
+ if (prevSize >= BLOB_SIZE_CAP && signal === 'logs')
193
+ return;
194
+ await appendToGzip(filePath, lineStr);
195
+ // Update byteSize in DB after each write
196
+ (0, db_2.upsertTelemetryBlob)(db, {
197
+ jobId,
198
+ path: filePath,
199
+ byteSize: state.uncompressedSize,
200
+ startedAt: existingBlob?.startedAt ?? now,
201
+ endedAt: now,
202
+ state: 'active',
203
+ });
204
+ });
205
+ // Suppress unused-variable warning for payloadStr — it's used above for size calc
206
+ void payloadStr;
207
+ res.status(200).json({ ok: true });
208
+ }
209
+ // ─── Router factory ───────────────────────────────────────────────────────────
210
+ function createTelemetryRouter(registry) {
211
+ const router = (0, express_1.Router)();
212
+ const handle = (signal) => (req, res) => {
213
+ handleIngest(signal, req.body, registry, res).catch((err) => {
214
+ console.error(`[telemetry-receiver] error handling ${signal}:`, err);
215
+ if (!res.headersSent) {
216
+ res.status(500).json({ error: 'Internal server error' });
217
+ }
218
+ });
219
+ };
220
+ router.post('/v1/traces', handle('traces'));
221
+ router.post('/v1/metrics', handle('metrics'));
222
+ router.post('/v1/logs', handle('logs'));
223
+ return router;
224
+ }