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,633 @@
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.TerminalManager = exports.TERMINAL_EARLY_EXIT_MS = exports.TerminalSpawnError = exports.TerminalNameInvalidError = exports.TerminalNotFoundError = exports.TerminalLimitExceededError = exports.RingBuffer = exports.TERMINAL_DEFAULT_ROWS = exports.TERMINAL_DEFAULT_COLS = exports.TERMINAL_NAME_MAX = exports.TERMINAL_MAX_PER_PROJECT = exports.TERMINAL_KILL_GRACE_MS = exports.TERMINAL_SCROLLBACK_BYTES = void 0;
7
+ exports.resolveShell = resolveShell;
8
+ exports.resolveShellFor = resolveShellFor;
9
+ exports.shellArgs = shellArgs;
10
+ exports.mapSpawnError = mapSpawnError;
11
+ exports.getTerminalManager = getTerminalManager;
12
+ exports._resetTerminalManagerForTest = _resetTerminalManagerForTest;
13
+ const fs_1 = __importDefault(require("fs"));
14
+ const path_1 = __importDefault(require("path"));
15
+ const node_pty_1 = require("node-pty");
16
+ const ids_1 = require("./ids");
17
+ const terminal_osc_parser_1 = require("./terminal-osc-parser");
18
+ const terminal_marks_store_1 = require("./terminal-marks-store");
19
+ const terminal_shell_integration_1 = require("./terminal-shell-integration");
20
+ exports.TERMINAL_SCROLLBACK_BYTES = 262_144;
21
+ exports.TERMINAL_KILL_GRACE_MS = 2_000;
22
+ exports.TERMINAL_MAX_PER_PROJECT = 10;
23
+ exports.TERMINAL_NAME_MAX = 64;
24
+ exports.TERMINAL_DEFAULT_COLS = 80;
25
+ exports.TERMINAL_DEFAULT_ROWS = 24;
26
+ const WS_OPEN = 1;
27
+ // ─── Spawn-helper permission fix ──────────────────────────────────────────────
28
+ // node-pty's prebuilds occasionally lose the executable bit during npm extraction
29
+ // on some systems. Best-effort chmod so the first Terminal spawn does not fail
30
+ // with `posix_spawnp failed`.
31
+ function ensureSpawnHelperExecutable() {
32
+ if (process.platform === 'win32')
33
+ return;
34
+ try {
35
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
36
+ const ptyPkgPath = require.resolve('node-pty/package.json');
37
+ const ptyPkgDir = path_1.default.dirname(ptyPkgPath);
38
+ const arch = process.arch === 'arm64' ? 'arm64' : process.arch === 'x64' ? 'x64' : process.arch;
39
+ const platform = process.platform;
40
+ const helperPath = path_1.default.join(ptyPkgDir, 'prebuilds', `${platform}-${arch}`, 'spawn-helper');
41
+ if (fs_1.default.existsSync(helperPath)) {
42
+ const stat = fs_1.default.statSync(helperPath);
43
+ if ((stat.mode & 0o111) === 0) {
44
+ fs_1.default.chmodSync(helperPath, 0o755);
45
+ }
46
+ }
47
+ }
48
+ catch {
49
+ // Best effort only — if the helper is missing or unreachable, node-pty will
50
+ // surface a clearer error on the first spawn call.
51
+ }
52
+ }
53
+ ensureSpawnHelperExecutable();
54
+ // ─── Shell resolution ─────────────────────────────────────────────────────────
55
+ function resolveShell() {
56
+ return resolveShellFor(process.platform, process.env, (p) => { try {
57
+ return fs_1.default.existsSync(p);
58
+ }
59
+ catch {
60
+ return false;
61
+ } });
62
+ }
63
+ /**
64
+ * Pure, injectable shell resolver (testable on any platform).
65
+ * Order: explicit `$SHELL` → platform default.
66
+ * On Windows the default prefers PowerShell over cmd.exe: PowerShell 7 (`pwsh.exe`
67
+ * on PATH) → Windows PowerShell (`powershell.exe`, always present on Win10/11 at a
68
+ * known absolute path) → `COMSPEC`/cmd.exe as a last resort. cmd.exe is a poor
69
+ * default and has no shell-integration shim.
70
+ */
71
+ function resolveShellFor(platform, env, exists) {
72
+ const envShell = env.SHELL;
73
+ if (envShell && envShell.trim().length > 0)
74
+ return envShell.trim();
75
+ if (platform === 'win32') {
76
+ for (const dir of (env.PATH ?? '').split(';')) {
77
+ if (!dir)
78
+ continue;
79
+ const pwsh = `${dir.replace(/[\\/]$/, '')}\\pwsh.exe`;
80
+ if (exists(pwsh))
81
+ return pwsh;
82
+ }
83
+ const sysRoot = (env.SystemRoot || env.windir || 'C:\\Windows').replace(/[\\/]$/, '');
84
+ const winPosh = `${sysRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
85
+ if (exists(winPosh))
86
+ return winPosh;
87
+ return env.COMSPEC || 'cmd.exe';
88
+ }
89
+ return '/bin/zsh';
90
+ }
91
+ function shellArgs(shell) {
92
+ // Normalize so Windows paths (C:\foo\bar.exe) work even when running cross-platform tests.
93
+ const normalized = shell.replace(/\\/g, '/');
94
+ const base = path_1.default.posix.basename(normalized).toLowerCase();
95
+ if (base === 'zsh' || base === 'bash')
96
+ return ['-l', '-i'];
97
+ if (base === 'fish')
98
+ return ['-i'];
99
+ if (base === 'powershell.exe' || base === 'pwsh' || base === 'pwsh.exe' || base === 'powershell')
100
+ return ['-NoLogo'];
101
+ if (base === 'cmd.exe' || base === 'cmd')
102
+ return [];
103
+ return ['-i'];
104
+ }
105
+ // ─── Ring buffer ──────────────────────────────────────────────────────────────
106
+ class RingBuffer {
107
+ capacity;
108
+ chunks = [];
109
+ total = 0;
110
+ constructor(capacity) {
111
+ this.capacity = capacity;
112
+ }
113
+ append(chunk) {
114
+ if (chunk.length === 0)
115
+ return;
116
+ this.chunks.push(chunk);
117
+ this.total += chunk.length;
118
+ // Drop whole chunks from the front until we fit (except the last, which we trim)
119
+ while (this.total > this.capacity && this.chunks.length > 1) {
120
+ const drop = this.chunks.shift();
121
+ this.total -= drop.length;
122
+ }
123
+ if (this.total > this.capacity && this.chunks.length === 1) {
124
+ const head = this.chunks[0];
125
+ const excess = this.total - this.capacity;
126
+ this.chunks[0] = head.subarray(excess);
127
+ this.total = this.chunks[0].length;
128
+ }
129
+ }
130
+ snapshot() {
131
+ if (this.total === 0)
132
+ return Buffer.alloc(0);
133
+ return Buffer.concat(this.chunks, this.total);
134
+ }
135
+ size() { return this.total; }
136
+ clear() { this.chunks = []; this.total = 0; }
137
+ }
138
+ exports.RingBuffer = RingBuffer;
139
+ class TerminalLimitExceededError extends Error {
140
+ limit = exports.TERMINAL_MAX_PER_PROJECT;
141
+ constructor() { super('terminal_limit_exceeded'); this.name = 'TerminalLimitExceededError'; }
142
+ }
143
+ exports.TerminalLimitExceededError = TerminalLimitExceededError;
144
+ class TerminalNotFoundError extends Error {
145
+ constructor() { super('terminal_not_found'); this.name = 'TerminalNotFoundError'; }
146
+ }
147
+ exports.TerminalNotFoundError = TerminalNotFoundError;
148
+ class TerminalNameInvalidError extends Error {
149
+ constructor() { super('terminal_name_invalid'); this.name = 'TerminalNameInvalidError'; }
150
+ }
151
+ exports.TerminalNameInvalidError = TerminalNameInvalidError;
152
+ /** Thrown when node-pty fails to spawn the shell (e.g. the host is out of file
153
+ * descriptors). Lets the REST layer return a concrete, actionable error instead
154
+ * of an opaque 500. */
155
+ class TerminalSpawnError extends Error {
156
+ reason;
157
+ constructor(reason, detail) {
158
+ super(`terminal_spawn_failed:${reason}${detail ? ` (${detail})` : ''}`);
159
+ this.reason = reason;
160
+ this.name = 'TerminalSpawnError';
161
+ }
162
+ }
163
+ exports.TerminalSpawnError = TerminalSpawnError;
164
+ /** Map a node-pty spawn exception to a typed TerminalSpawnError. EMFILE/ENFILE
165
+ * (the host is out of file descriptors — the failure mode that broke terminals
166
+ * under the chokidar fd leak) get a distinct reason the client can act on. */
167
+ function mapSpawnError(err) {
168
+ const e = err;
169
+ if (e?.code === 'EMFILE' || e?.code === 'ENFILE') {
170
+ return new TerminalSpawnError('out-of-file-descriptors', e.message);
171
+ }
172
+ return new TerminalSpawnError('spawn-failed', e?.message ?? String(err));
173
+ }
174
+ /** A session that exits within this window of being created is treated as an
175
+ * "early exit" (probable failed spawn — e.g. no controlling tty under fd
176
+ * pressure) so the client can surface a clear failure instead of a blank pane. */
177
+ exports.TERMINAL_EARLY_EXIT_MS = 1_500;
178
+ const TOMBSTONE_TTL_MS = 30_000;
179
+ // ─── Manager ──────────────────────────────────────────────────────────────────
180
+ class TerminalManager {
181
+ sessions = new Map();
182
+ byProject = new Map();
183
+ tombstones = new Map();
184
+ tombstoneTimers = new Map();
185
+ listForProject(projectId) {
186
+ const set = this.byProject.get(projectId);
187
+ if (!set)
188
+ return [];
189
+ const out = [];
190
+ for (const id of set) {
191
+ const s = this.sessions.get(id);
192
+ if (s)
193
+ out.push(this.toMeta(s));
194
+ }
195
+ return out.sort((a, b) => a.createdAt - b.createdAt);
196
+ }
197
+ /** Scoped lookup: returns session only if both id AND projectId match. */
198
+ get(projectId, sessionId) {
199
+ const s = this.sessions.get(sessionId);
200
+ if (!s)
201
+ return undefined;
202
+ if (s.projectId !== projectId)
203
+ return undefined;
204
+ return s;
205
+ }
206
+ /** Unscoped lookup — used by the WS upgrade handler to validate cross-project access. */
207
+ getUnsafe(sessionId) {
208
+ return this.sessions.get(sessionId);
209
+ }
210
+ create(projectId, opts) {
211
+ const currentCount = this.byProject.get(projectId)?.size ?? 0;
212
+ if (currentCount >= exports.TERMINAL_MAX_PER_PROJECT)
213
+ throw new TerminalLimitExceededError();
214
+ const shell = resolveShell();
215
+ const baseArgs = shellArgs(shell);
216
+ const cols = clampDim(opts.cols ?? exports.TERMINAL_DEFAULT_COLS, 2, 1000);
217
+ const rows = clampDim(opts.rows ?? exports.TERMINAL_DEFAULT_ROWS, 2, 1000);
218
+ const env = {};
219
+ for (const [k, v] of Object.entries(process.env)) {
220
+ if (typeof v === 'string')
221
+ env[k] = v;
222
+ }
223
+ env.TERM = 'xterm-256color';
224
+ env.COLORTERM = 'truecolor';
225
+ const id = (0, ids_1.newId)();
226
+ const projectSlug = opts.projectSlug ?? projectId;
227
+ // Shell-integration: compose extra args/env when the resolved settings allow it.
228
+ const shellIntegration = (0, terminal_shell_integration_1.composeShellIntegrationSpawn)(shell, id, projectSlug, opts.settings ?? { shellIntegrationEnabled: false });
229
+ for (const [k, v] of Object.entries(shellIntegration.env))
230
+ env[k] = v;
231
+ const args = shellIntegration.args.length > 0
232
+ ? [...shellIntegration.args, ...baseArgs]
233
+ : baseArgs;
234
+ let pty;
235
+ try {
236
+ pty = (0, node_pty_1.spawn)(shell, args, {
237
+ cwd: opts.cwd,
238
+ cols, rows,
239
+ env,
240
+ name: 'xterm-256color',
241
+ });
242
+ }
243
+ catch (err) {
244
+ throw mapSpawnError(err);
245
+ }
246
+ const name = validateName(opts.name) ?? this.autoName(projectId, shell);
247
+ const session = {
248
+ id, projectId, name, shell, cwd: opts.cwd, cols, rows,
249
+ createdAt: Date.now(),
250
+ pty, buffer: new RingBuffer(exports.TERMINAL_SCROLLBACK_BYTES),
251
+ clients: new Set(),
252
+ exited: false,
253
+ oscParser: shellIntegration.shimPath ? new terminal_osc_parser_1.OscParser() : null,
254
+ currentCwd: null,
255
+ projectSlug,
256
+ projectDb: opts.projectDb ?? null,
257
+ shellIntegration,
258
+ };
259
+ pty.onData((chunk) => {
260
+ const buf = Buffer.from(chunk, 'utf8');
261
+ session.buffer.append(buf);
262
+ // Forward bytes to attached clients (binary, untouched).
263
+ for (const ws of session.clients) {
264
+ if (ws.readyState === WS_OPEN) {
265
+ try {
266
+ ws.send(buf, { binary: true });
267
+ }
268
+ catch { /* ignore */ }
269
+ }
270
+ }
271
+ // Then optionally parse for OSC marks and broadcast structured frames.
272
+ if (session.oscParser) {
273
+ const events = session.oscParser.feed(buf);
274
+ if (events.length > 0)
275
+ this.handleMarkEvents(session, events);
276
+ }
277
+ });
278
+ pty.onExit((e) => {
279
+ session.exited = true;
280
+ const now = Date.now();
281
+ const early = now - session.createdAt < exports.TERMINAL_EARLY_EXIT_MS;
282
+ // Any open mark gets recorded as killed.
283
+ if (session.projectDb) {
284
+ try {
285
+ (0, terminal_marks_store_1.markOpenAsKilled)(session.projectDb, session.id, now);
286
+ }
287
+ catch { /* ignore */ }
288
+ }
289
+ // Record a tombstone unless this is a deliberate kill (killSession removes
290
+ // the session from the registry first, so `!this.sessions.has` distinguishes
291
+ // an unexpected pty death from an intentional kill).
292
+ if (this.sessions.has(session.id)) {
293
+ this.recordTombstone(session.id, session.projectId, e?.exitCode ?? null, e?.signal ?? null, early);
294
+ // Tell attached clients WHY the session ended before closing the socket,
295
+ // so the UI shows a reason instead of a silent black pane.
296
+ const frame = JSON.stringify({ type: 'exit', code: e?.exitCode ?? null, signal: e?.signal ?? null, early });
297
+ for (const ws of session.clients) {
298
+ if (ws.readyState === WS_OPEN) {
299
+ try {
300
+ ws.send(frame);
301
+ }
302
+ catch { /* ignore */ }
303
+ }
304
+ }
305
+ }
306
+ for (const ws of session.clients) {
307
+ try {
308
+ ws.close(1000, early ? 'pty_exit_early' : 'pty_exit');
309
+ }
310
+ catch { /* ignore */ }
311
+ }
312
+ (0, terminal_shell_integration_1.cleanupSessionShim)(session.projectSlug, session.id);
313
+ this.removeFromRegistry(session);
314
+ });
315
+ this.sessions.set(id, session);
316
+ let set = this.byProject.get(projectId);
317
+ if (!set) {
318
+ set = new Set();
319
+ this.byProject.set(projectId, set);
320
+ }
321
+ set.add(id);
322
+ return this.toMeta(session);
323
+ }
324
+ /**
325
+ * Attach a WebSocket to the session: sends snapshot + ready frame, then wires live output.
326
+ * Caller is responsible for having verified projectId scope.
327
+ */
328
+ attach(sessionId, ws) {
329
+ const s = this.sessions.get(sessionId);
330
+ if (!s || s.exited)
331
+ return null;
332
+ try {
333
+ const snapshot = s.buffer.snapshot();
334
+ if (snapshot.length > 0)
335
+ ws.send(snapshot, { binary: true });
336
+ ws.send(JSON.stringify({ type: 'ready', id: s.id, name: s.name, cols: s.cols, rows: s.rows }));
337
+ }
338
+ catch {
339
+ return null;
340
+ }
341
+ s.clients.add(ws);
342
+ return this.toMeta(s);
343
+ }
344
+ detach(sessionId, ws) {
345
+ const s = this.sessions.get(sessionId);
346
+ if (!s)
347
+ return;
348
+ s.clients.delete(ws);
349
+ }
350
+ /** Returns the tombstone for a recently-exited session, scoped to projectId,
351
+ * or undefined. Used by the WS upgrade/attach path to report why a session a
352
+ * client is trying to reach has already gone. */
353
+ getTombstone(sessionId, projectId) {
354
+ const t = this.tombstones.get(sessionId);
355
+ if (!t || t.projectId !== projectId)
356
+ return undefined;
357
+ return t;
358
+ }
359
+ recordTombstone(sessionId, projectId, code, signal, early) {
360
+ this.tombstones.set(sessionId, { projectId, exitedAt: Date.now(), code, signal, early });
361
+ const prev = this.tombstoneTimers.get(sessionId);
362
+ if (prev)
363
+ clearTimeout(prev);
364
+ const timer = setTimeout(() => {
365
+ this.tombstones.delete(sessionId);
366
+ this.tombstoneTimers.delete(sessionId);
367
+ }, TOMBSTONE_TTL_MS);
368
+ // Don't keep the event loop alive solely for tombstone eviction.
369
+ if (typeof timer.unref === 'function')
370
+ timer.unref();
371
+ this.tombstoneTimers.set(sessionId, timer);
372
+ }
373
+ write(sessionId, data) {
374
+ const s = this.sessions.get(sessionId);
375
+ if (!s || s.exited)
376
+ return;
377
+ const str = typeof data === 'string' ? data : data.toString('utf8');
378
+ try {
379
+ s.pty.write(str);
380
+ }
381
+ catch { /* pty may have died between checks */ }
382
+ }
383
+ resize(sessionId, cols, rows) {
384
+ const s = this.sessions.get(sessionId);
385
+ if (!s || s.exited)
386
+ return;
387
+ const c = clampDim(cols, 2, 1000);
388
+ const r = clampDim(rows, 2, 1000);
389
+ try {
390
+ s.pty.resize(c, r);
391
+ }
392
+ catch { /* ignore */ }
393
+ s.cols = c;
394
+ s.rows = r;
395
+ }
396
+ rename(projectId, sessionId, name) {
397
+ const s = this.get(projectId, sessionId);
398
+ if (!s)
399
+ throw new TerminalNotFoundError();
400
+ const validated = validateName(name);
401
+ if (!validated)
402
+ throw new TerminalNameInvalidError();
403
+ s.name = validated;
404
+ const msg = JSON.stringify({ type: 'renamed', id: s.id, name: s.name });
405
+ for (const ws of s.clients) {
406
+ if (ws.readyState === WS_OPEN) {
407
+ try {
408
+ ws.send(msg);
409
+ }
410
+ catch { /* ignore */ }
411
+ }
412
+ }
413
+ return this.toMeta(s);
414
+ }
415
+ kill(projectId, sessionId) {
416
+ const s = this.get(projectId, sessionId);
417
+ if (!s)
418
+ return false;
419
+ this.killSession(s);
420
+ return true;
421
+ }
422
+ killAllForProject(projectId) {
423
+ const set = this.byProject.get(projectId);
424
+ if (!set)
425
+ return 0;
426
+ const ids = Array.from(set);
427
+ let killed = 0;
428
+ for (const id of ids) {
429
+ const s = this.sessions.get(id);
430
+ if (s) {
431
+ this.killSession(s);
432
+ killed++;
433
+ }
434
+ }
435
+ return killed;
436
+ }
437
+ async shutdown() {
438
+ const all = Array.from(this.sessions.values());
439
+ for (const s of all) {
440
+ try {
441
+ s.pty.kill('SIGTERM');
442
+ }
443
+ catch { /* ignore */ }
444
+ }
445
+ await new Promise((r) => setTimeout(r, exports.TERMINAL_KILL_GRACE_MS));
446
+ for (const s of Array.from(this.sessions.values())) {
447
+ try {
448
+ s.pty.kill('SIGKILL');
449
+ }
450
+ catch { /* ignore */ }
451
+ this.removeFromRegistry(s);
452
+ }
453
+ for (const timer of this.tombstoneTimers.values())
454
+ clearTimeout(timer);
455
+ this.tombstoneTimers.clear();
456
+ this.tombstones.clear();
457
+ }
458
+ sessionCount() { return this.sessions.size; }
459
+ // ─── Private ────────────────────────────────────────────────────────────────
460
+ /**
461
+ * Process an OSC parser event: persist command marks where appropriate and
462
+ * broadcast a JSON control frame `{type:"mark",kind,...}` on every attached
463
+ * WebSocket. Bytes are forwarded to xterm separately.
464
+ */
465
+ handleMarkEvents(session, events) {
466
+ const ts = Date.now();
467
+ for (const ev of events) {
468
+ // Persist where applicable.
469
+ if (session.projectDb) {
470
+ try {
471
+ if (ev.kind === 'pre-exec') {
472
+ // Open a new mark; if a previous one is still open (no D before next C),
473
+ // close it as killed-by-prompt.
474
+ const dangling = (0, terminal_marks_store_1.getOpenMark)(session.projectDb, session.id);
475
+ if (dangling) {
476
+ (0, terminal_marks_store_1.closeOpenMark)(session.projectDb, session.id, ts, null, null, session.currentCwd ?? null);
477
+ }
478
+ (0, terminal_marks_store_1.appendMark)(session.projectDb, {
479
+ sessionId: session.id,
480
+ startedAt: ts,
481
+ cwd: session.currentCwd ?? null,
482
+ });
483
+ }
484
+ else if (ev.kind === 'post-exec') {
485
+ (0, terminal_marks_store_1.closeOpenMark)(session.projectDb, session.id, ts, ev.exitCode ?? null, null, session.currentCwd ?? null);
486
+ }
487
+ else if (ev.kind === 'cwd') {
488
+ session.currentCwd = ev.payload ?? null;
489
+ }
490
+ }
491
+ catch { /* persistence is best-effort */ }
492
+ }
493
+ else if (ev.kind === 'cwd') {
494
+ session.currentCwd = ev.payload ?? null;
495
+ }
496
+ // Broadcast control frame.
497
+ const payload = { type: 'mark', kind: ev.kind, ts };
498
+ if (ev.exitCode !== undefined)
499
+ payload.payload = { exitCode: ev.exitCode };
500
+ if (ev.kind === 'cwd' && ev.payload)
501
+ payload.payload = { path: ev.payload };
502
+ const json = JSON.stringify(payload);
503
+ for (const ws of session.clients) {
504
+ if (ws.readyState === WS_OPEN) {
505
+ try {
506
+ ws.send(json);
507
+ }
508
+ catch { /* ignore */ }
509
+ }
510
+ }
511
+ }
512
+ }
513
+ /**
514
+ * Kill semantics: remove the session from the public registry immediately so
515
+ * subsequent lookups (REST, WS attach) return 404, then SIGTERM in the
516
+ * background, then SIGKILL after a grace period if needed. The onExit handler
517
+ * installed at creation becomes a no-op for already-removed sessions.
518
+ */
519
+ killSession(s) {
520
+ // Idempotent: if already removed from registry, skip.
521
+ if (!this.sessions.has(s.id))
522
+ return;
523
+ this.detachFromRegistry(s);
524
+ if (!s.exited) {
525
+ try {
526
+ s.pty.kill('SIGTERM');
527
+ }
528
+ catch { /* ignore */ }
529
+ s.killTimer = setTimeout(() => {
530
+ if (!s.exited) {
531
+ try {
532
+ s.pty.kill('SIGKILL');
533
+ }
534
+ catch { /* ignore */ }
535
+ }
536
+ }, exports.TERMINAL_KILL_GRACE_MS);
537
+ }
538
+ // Close any open command mark with the session's death timestamp.
539
+ if (s.projectDb) {
540
+ try {
541
+ (0, terminal_marks_store_1.markOpenAsKilled)(s.projectDb, s.id, Date.now());
542
+ }
543
+ catch { /* ignore */ }
544
+ }
545
+ (0, terminal_shell_integration_1.cleanupSessionShim)(s.projectSlug, s.id);
546
+ // Close clients so the WS upgrade handlers detach cleanly
547
+ for (const ws of s.clients) {
548
+ try {
549
+ ws.close(1000, 'session_closed');
550
+ }
551
+ catch { /* ignore */ }
552
+ }
553
+ s.clients.clear();
554
+ s.buffer.clear();
555
+ }
556
+ detachFromRegistry(s) {
557
+ this.sessions.delete(s.id);
558
+ const set = this.byProject.get(s.projectId);
559
+ if (set) {
560
+ set.delete(s.id);
561
+ if (set.size === 0)
562
+ this.byProject.delete(s.projectId);
563
+ }
564
+ }
565
+ removeFromRegistry(s) {
566
+ if (s.killTimer) {
567
+ clearTimeout(s.killTimer);
568
+ s.killTimer = undefined;
569
+ }
570
+ this.detachFromRegistry(s);
571
+ for (const ws of s.clients) {
572
+ try {
573
+ ws.close(1000, 'session_closed');
574
+ }
575
+ catch { /* ignore */ }
576
+ }
577
+ s.clients.clear();
578
+ s.buffer.clear();
579
+ }
580
+ autoName(projectId, shell) {
581
+ const base = path_1.default.basename(shell);
582
+ const metas = this.listForProject(projectId);
583
+ const matching = metas.filter((m) => m.name === base || m.name.startsWith(base + ' ('));
584
+ if (matching.length === 0)
585
+ return base;
586
+ return `${base} (${matching.length + 1})`;
587
+ }
588
+ toMeta(s) {
589
+ return {
590
+ id: s.id, projectId: s.projectId, name: s.name, shell: s.shell,
591
+ cwd: s.cwd, cols: s.cols, rows: s.rows, createdAt: s.createdAt,
592
+ };
593
+ }
594
+ }
595
+ exports.TerminalManager = TerminalManager;
596
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
597
+ function clampDim(n, lo, hi) {
598
+ if (!Number.isFinite(n))
599
+ return lo;
600
+ const v = Math.trunc(n);
601
+ if (v < lo)
602
+ return lo;
603
+ if (v > hi)
604
+ return hi;
605
+ return v;
606
+ }
607
+ function validateName(name) {
608
+ if (typeof name !== 'string')
609
+ return null;
610
+ const trimmed = name.trim();
611
+ if (trimmed.length < 1 || trimmed.length > exports.TERMINAL_NAME_MAX)
612
+ return null;
613
+ return trimmed;
614
+ }
615
+ // ─── Singleton ────────────────────────────────────────────────────────────────
616
+ let _instance = null;
617
+ /**
618
+ * Returns the process-wide TerminalManager. Lazy-initialised so that test suites
619
+ * that don't touch terminals pay no cost.
620
+ */
621
+ function getTerminalManager() {
622
+ if (!_instance)
623
+ _instance = new TerminalManager();
624
+ return _instance;
625
+ }
626
+ /** Reset the singleton. Tests only. */
627
+ function _resetTerminalManagerForTest() {
628
+ if (_instance) {
629
+ // Best-effort sync cleanup — tests call this in beforeEach
630
+ void _instance.shutdown();
631
+ }
632
+ _instance = null;
633
+ }