@lbruton/spec-workflow-mcp 2.2.4

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 (407) hide show
  1. package/CHANGELOG.md +955 -0
  2. package/LICENSE +674 -0
  3. package/README.ar.md +314 -0
  4. package/README.de.md +314 -0
  5. package/README.es.md +314 -0
  6. package/README.fr.md +314 -0
  7. package/README.it.md +314 -0
  8. package/README.ja.md +316 -0
  9. package/README.ko.md +314 -0
  10. package/README.md +373 -0
  11. package/README.pt.md +314 -0
  12. package/README.ru.md +314 -0
  13. package/README.zh.md +314 -0
  14. package/dist/__tests__/config.test.d.ts +2 -0
  15. package/dist/__tests__/config.test.d.ts.map +1 -0
  16. package/dist/__tests__/config.test.js +264 -0
  17. package/dist/__tests__/config.test.js.map +1 -0
  18. package/dist/__tests__/index-args.test.d.ts +2 -0
  19. package/dist/__tests__/index-args.test.d.ts.map +1 -0
  20. package/dist/__tests__/index-args.test.js +43 -0
  21. package/dist/__tests__/index-args.test.js.map +1 -0
  22. package/dist/__tests__/index-entrypoint.test.d.ts +2 -0
  23. package/dist/__tests__/index-entrypoint.test.d.ts.map +1 -0
  24. package/dist/__tests__/index-entrypoint.test.js +23 -0
  25. package/dist/__tests__/index-entrypoint.test.js.map +1 -0
  26. package/dist/config.d.ts +26 -0
  27. package/dist/config.d.ts.map +1 -0
  28. package/dist/config.js +188 -0
  29. package/dist/config.js.map +1 -0
  30. package/dist/core/__tests__/git-utils.test.d.ts +2 -0
  31. package/dist/core/__tests__/git-utils.test.d.ts.map +1 -0
  32. package/dist/core/__tests__/git-utils.test.js +179 -0
  33. package/dist/core/__tests__/git-utils.test.js.map +1 -0
  34. package/dist/core/__tests__/mdx-validator.test.d.ts +2 -0
  35. package/dist/core/__tests__/mdx-validator.test.d.ts.map +1 -0
  36. package/dist/core/__tests__/mdx-validator.test.js +42 -0
  37. package/dist/core/__tests__/mdx-validator.test.js.map +1 -0
  38. package/dist/core/__tests__/path-utils.test.d.ts +2 -0
  39. package/dist/core/__tests__/path-utils.test.d.ts.map +1 -0
  40. package/dist/core/__tests__/path-utils.test.js +344 -0
  41. package/dist/core/__tests__/path-utils.test.js.map +1 -0
  42. package/dist/core/__tests__/project-registry.test.d.ts +2 -0
  43. package/dist/core/__tests__/project-registry.test.d.ts.map +1 -0
  44. package/dist/core/__tests__/project-registry.test.js +62 -0
  45. package/dist/core/__tests__/project-registry.test.js.map +1 -0
  46. package/dist/core/__tests__/security-utils.test.d.ts +2 -0
  47. package/dist/core/__tests__/security-utils.test.d.ts.map +1 -0
  48. package/dist/core/__tests__/security-utils.test.js +643 -0
  49. package/dist/core/__tests__/security-utils.test.js.map +1 -0
  50. package/dist/core/__tests__/task-validator.test.d.ts +2 -0
  51. package/dist/core/__tests__/task-validator.test.d.ts.map +1 -0
  52. package/dist/core/__tests__/task-validator.test.js +237 -0
  53. package/dist/core/__tests__/task-validator.test.js.map +1 -0
  54. package/dist/core/archive-service.d.ts +10 -0
  55. package/dist/core/archive-service.d.ts.map +1 -0
  56. package/dist/core/archive-service.js +99 -0
  57. package/dist/core/archive-service.js.map +1 -0
  58. package/dist/core/dashboard-session.d.ts +49 -0
  59. package/dist/core/dashboard-session.d.ts.map +1 -0
  60. package/dist/core/dashboard-session.js +132 -0
  61. package/dist/core/dashboard-session.js.map +1 -0
  62. package/dist/core/git-utils.d.ts +25 -0
  63. package/dist/core/git-utils.d.ts.map +1 -0
  64. package/dist/core/git-utils.js +87 -0
  65. package/dist/core/git-utils.js.map +1 -0
  66. package/dist/core/global-dir.d.ts +44 -0
  67. package/dist/core/global-dir.d.ts.map +1 -0
  68. package/dist/core/global-dir.js +74 -0
  69. package/dist/core/global-dir.js.map +1 -0
  70. package/dist/core/implementation-log-migrator.d.ts +41 -0
  71. package/dist/core/implementation-log-migrator.d.ts.map +1 -0
  72. package/dist/core/implementation-log-migrator.js +258 -0
  73. package/dist/core/implementation-log-migrator.js.map +1 -0
  74. package/dist/core/mdx-validator.d.ts +14 -0
  75. package/dist/core/mdx-validator.d.ts.map +1 -0
  76. package/dist/core/mdx-validator.js +34 -0
  77. package/dist/core/mdx-validator.js.map +1 -0
  78. package/dist/core/parser.d.ts +11 -0
  79. package/dist/core/parser.d.ts.map +1 -0
  80. package/dist/core/parser.js +126 -0
  81. package/dist/core/parser.js.map +1 -0
  82. package/dist/core/path-utils.d.ts +63 -0
  83. package/dist/core/path-utils.d.ts.map +1 -0
  84. package/dist/core/path-utils.js +288 -0
  85. package/dist/core/path-utils.js.map +1 -0
  86. package/dist/core/project-registry.d.ts +94 -0
  87. package/dist/core/project-registry.d.ts.map +1 -0
  88. package/dist/core/project-registry.js +297 -0
  89. package/dist/core/project-registry.js.map +1 -0
  90. package/dist/core/security-utils.d.ts +97 -0
  91. package/dist/core/security-utils.d.ts.map +1 -0
  92. package/dist/core/security-utils.js +264 -0
  93. package/dist/core/security-utils.js.map +1 -0
  94. package/dist/core/task-parser.d.ts +63 -0
  95. package/dist/core/task-parser.d.ts.map +1 -0
  96. package/dist/core/task-parser.js +332 -0
  97. package/dist/core/task-parser.js.map +1 -0
  98. package/dist/core/task-validator.d.ts +35 -0
  99. package/dist/core/task-validator.d.ts.map +1 -0
  100. package/dist/core/task-validator.js +236 -0
  101. package/dist/core/task-validator.js.map +1 -0
  102. package/dist/core/workspace-initializer.d.ts +16 -0
  103. package/dist/core/workspace-initializer.d.ts.map +1 -0
  104. package/dist/core/workspace-initializer.js +165 -0
  105. package/dist/core/workspace-initializer.js.map +1 -0
  106. package/dist/dashboard/__tests__/approval-storage-path-resolution.test.d.ts +2 -0
  107. package/dist/dashboard/__tests__/approval-storage-path-resolution.test.d.ts.map +1 -0
  108. package/dist/dashboard/__tests__/approval-storage-path-resolution.test.js +69 -0
  109. package/dist/dashboard/__tests__/approval-storage-path-resolution.test.js.map +1 -0
  110. package/dist/dashboard/__tests__/multi-server-approvals-content.test.d.ts +2 -0
  111. package/dist/dashboard/__tests__/multi-server-approvals-content.test.d.ts.map +1 -0
  112. package/dist/dashboard/__tests__/multi-server-approvals-content.test.js +116 -0
  113. package/dist/dashboard/__tests__/multi-server-approvals-content.test.js.map +1 -0
  114. package/dist/dashboard/__tests__/watcher-error-handling.test.d.ts +2 -0
  115. package/dist/dashboard/__tests__/watcher-error-handling.test.d.ts.map +1 -0
  116. package/dist/dashboard/__tests__/watcher-error-handling.test.js +118 -0
  117. package/dist/dashboard/__tests__/watcher-error-handling.test.js.map +1 -0
  118. package/dist/dashboard/approval-storage.d.ts +139 -0
  119. package/dist/dashboard/approval-storage.d.ts.map +1 -0
  120. package/dist/dashboard/approval-storage.js +586 -0
  121. package/dist/dashboard/approval-storage.js.map +1 -0
  122. package/dist/dashboard/execution-history-manager.d.ts +52 -0
  123. package/dist/dashboard/execution-history-manager.d.ts.map +1 -0
  124. package/dist/dashboard/execution-history-manager.js +161 -0
  125. package/dist/dashboard/execution-history-manager.js.map +1 -0
  126. package/dist/dashboard/implementation-log-manager.d.ts +97 -0
  127. package/dist/dashboard/implementation-log-manager.d.ts.map +1 -0
  128. package/dist/dashboard/implementation-log-manager.js +586 -0
  129. package/dist/dashboard/implementation-log-manager.js.map +1 -0
  130. package/dist/dashboard/job-scheduler.d.ts +91 -0
  131. package/dist/dashboard/job-scheduler.d.ts.map +1 -0
  132. package/dist/dashboard/job-scheduler.js +321 -0
  133. package/dist/dashboard/job-scheduler.js.map +1 -0
  134. package/dist/dashboard/multi-server.d.ts +42 -0
  135. package/dist/dashboard/multi-server.d.ts.map +1 -0
  136. package/dist/dashboard/multi-server.js +1313 -0
  137. package/dist/dashboard/multi-server.js.map +1 -0
  138. package/dist/dashboard/parser.d.ts +18 -0
  139. package/dist/dashboard/parser.d.ts.map +1 -0
  140. package/dist/dashboard/parser.js +243 -0
  141. package/dist/dashboard/parser.js.map +1 -0
  142. package/dist/dashboard/project-manager.d.ts +82 -0
  143. package/dist/dashboard/project-manager.d.ts.map +1 -0
  144. package/dist/dashboard/project-manager.js +257 -0
  145. package/dist/dashboard/project-manager.js.map +1 -0
  146. package/dist/dashboard/public/assets/Inter-Bold-CD3Pr7BX.woff2 +0 -0
  147. package/dist/dashboard/public/assets/Inter-Medium-B_8v_WHh.woff2 +0 -0
  148. package/dist/dashboard/public/assets/Inter-Regular-DRVdRqcI.woff2 +0 -0
  149. package/dist/dashboard/public/assets/Inter-SemiBold-CtskMddL.woff2 +0 -0
  150. package/dist/dashboard/public/assets/JetBrainsMono-Bold-D4WEaHbo.woff2 +0 -0
  151. package/dist/dashboard/public/assets/JetBrainsMono-Medium-3S3k2nMz.woff2 +0 -0
  152. package/dist/dashboard/public/assets/JetBrainsMono-Regular-BQaDgvhP.woff2 +0 -0
  153. package/dist/dashboard/public/assets/Tableau10-B-NsZVaP.js +1 -0
  154. package/dist/dashboard/public/assets/apl-B4CMkyY2.js +1 -0
  155. package/dist/dashboard/public/assets/arc-C8LPXB-J.js +1 -0
  156. package/dist/dashboard/public/assets/array-BKyUJesY.js +1 -0
  157. package/dist/dashboard/public/assets/asciiarmor-Df11BRmG.js +1 -0
  158. package/dist/dashboard/public/assets/asn1-EdZsLKOL.js +1 -0
  159. package/dist/dashboard/public/assets/asterisk-B-8jnY81.js +1 -0
  160. package/dist/dashboard/public/assets/blockDiagram-c4efeb88-RidjsOEy.js +118 -0
  161. package/dist/dashboard/public/assets/brainfuck-C4LP7Hcl.js +1 -0
  162. package/dist/dashboard/public/assets/c4Diagram-c83219d4-CAH3hSpm.js +10 -0
  163. package/dist/dashboard/public/assets/channel-CmDIZRCD.js +1 -0
  164. package/dist/dashboard/public/assets/classDiagram-beda092f-Bo46Efmw.js +2 -0
  165. package/dist/dashboard/public/assets/classDiagram-v2-2358418a-Be57sb3z.js +2 -0
  166. package/dist/dashboard/public/assets/clike-B9uivgTg.js +1 -0
  167. package/dist/dashboard/public/assets/clojure-BMjYHr_A.js +1 -0
  168. package/dist/dashboard/public/assets/clone-BiekPeZp.js +1 -0
  169. package/dist/dashboard/public/assets/cmake-BQqOBYOt.js +1 -0
  170. package/dist/dashboard/public/assets/cobol-CWcv1MsR.js +1 -0
  171. package/dist/dashboard/public/assets/coffeescript-S37ZYGWr.js +1 -0
  172. package/dist/dashboard/public/assets/commonlisp-DBKNyK5s.js +1 -0
  173. package/dist/dashboard/public/assets/createText-1719965b-YurEYFNx.js +7 -0
  174. package/dist/dashboard/public/assets/crystal-SjHAIU92.js +1 -0
  175. package/dist/dashboard/public/assets/css-BnMrqG3P.js +1 -0
  176. package/dist/dashboard/public/assets/cypher-C_CwsFkJ.js +1 -0
  177. package/dist/dashboard/public/assets/d-pRatUO7H.js +1 -0
  178. package/dist/dashboard/public/assets/diff-DbItnlRl.js +1 -0
  179. package/dist/dashboard/public/assets/dockerfile-BKs6k2Af.js +1 -0
  180. package/dist/dashboard/public/assets/dtd-DF_7sFjM.js +1 -0
  181. package/dist/dashboard/public/assets/dylan-DwRh75JA.js +1 -0
  182. package/dist/dashboard/public/assets/ebnf-CDyGwa7X.js +1 -0
  183. package/dist/dashboard/public/assets/ecl-Cabwm37j.js +1 -0
  184. package/dist/dashboard/public/assets/edges-96097737--BjsAXwD.js +4 -0
  185. package/dist/dashboard/public/assets/eiffel-CnydiIhH.js +1 -0
  186. package/dist/dashboard/public/assets/elm-vLlmbW-K.js +1 -0
  187. package/dist/dashboard/public/assets/erDiagram-0228fc6a-BLGuJz36.js +51 -0
  188. package/dist/dashboard/public/assets/erlang-BNw1qcRV.js +1 -0
  189. package/dist/dashboard/public/assets/factor-kuTfRLto.js +1 -0
  190. package/dist/dashboard/public/assets/fcl-Kvtd6kyn.js +1 -0
  191. package/dist/dashboard/public/assets/flowDb-c6c81e3f-C8vD2iEO.js +10 -0
  192. package/dist/dashboard/public/assets/flowDiagram-50d868cf-BhxgVmOU.js +4 -0
  193. package/dist/dashboard/public/assets/flowDiagram-v2-4f6560a1-DvKCh0ha.js +1 -0
  194. package/dist/dashboard/public/assets/flowchart-elk-definition-6af322e1-CxOZDcEC.js +139 -0
  195. package/dist/dashboard/public/assets/forth-Ffai-XNe.js +1 -0
  196. package/dist/dashboard/public/assets/fortran-DYz_wnZ1.js +1 -0
  197. package/dist/dashboard/public/assets/ganttDiagram-a2739b55-vP9JOLba.js +257 -0
  198. package/dist/dashboard/public/assets/gas-Bneqetm1.js +1 -0
  199. package/dist/dashboard/public/assets/gherkin-heZmZLOM.js +1 -0
  200. package/dist/dashboard/public/assets/gitGraphDiagram-82fe8481-Cw0sm0i1.js +70 -0
  201. package/dist/dashboard/public/assets/graph-DKTWMcEG.js +1 -0
  202. package/dist/dashboard/public/assets/groovy-D9Dt4D0W.js +1 -0
  203. package/dist/dashboard/public/assets/haskell-Cw1EW3IL.js +1 -0
  204. package/dist/dashboard/public/assets/haxe-H-WmDvRZ.js +1 -0
  205. package/dist/dashboard/public/assets/http-DBlCnlav.js +1 -0
  206. package/dist/dashboard/public/assets/idl-BEugSyMb.js +1 -0
  207. package/dist/dashboard/public/assets/index-1zJPiVa8.js +3 -0
  208. package/dist/dashboard/public/assets/index-5325376f-DWs4kCT4.js +1 -0
  209. package/dist/dashboard/public/assets/index-BITJ9OoM.js +1 -0
  210. package/dist/dashboard/public/assets/index-C38JlXWp.js +1 -0
  211. package/dist/dashboard/public/assets/index-CCjPelL2.js +2 -0
  212. package/dist/dashboard/public/assets/index-CD9WQNmE.js +1 -0
  213. package/dist/dashboard/public/assets/index-CU7K5Zcb.js +1 -0
  214. package/dist/dashboard/public/assets/index-CXQVOhJV.js +1 -0
  215. package/dist/dashboard/public/assets/index-CXcaRrZ2.js +1 -0
  216. package/dist/dashboard/public/assets/index-ChLAL6g5.css +1 -0
  217. package/dist/dashboard/public/assets/index-D0o1vVOe.js +7 -0
  218. package/dist/dashboard/public/assets/index-DCsxqRvu.js +1 -0
  219. package/dist/dashboard/public/assets/index-DL3iiiRz.js +1 -0
  220. package/dist/dashboard/public/assets/index-DMv2_K2V.js +1 -0
  221. package/dist/dashboard/public/assets/index-DX7EEJ21.js +1 -0
  222. package/dist/dashboard/public/assets/index-Dey_HIH7.js +1 -0
  223. package/dist/dashboard/public/assets/index-DzDTRLhf.js +1 -0
  224. package/dist/dashboard/public/assets/index-OePkEWBg.js +1 -0
  225. package/dist/dashboard/public/assets/index-_d82jdTP.js +1 -0
  226. package/dist/dashboard/public/assets/index-yCKz4OXA.js +319 -0
  227. package/dist/dashboard/public/assets/infoDiagram-8eee0895-BRq08fZf.js +7 -0
  228. package/dist/dashboard/public/assets/init-Gi6I4Gst.js +1 -0
  229. package/dist/dashboard/public/assets/javascript-iXu5QeM3.js +1 -0
  230. package/dist/dashboard/public/assets/journeyDiagram-c64418c1-Cf8D2OC8.js +139 -0
  231. package/dist/dashboard/public/assets/julia-DuME0IfC.js +1 -0
  232. package/dist/dashboard/public/assets/katex-XbL3y5x-.js +261 -0
  233. package/dist/dashboard/public/assets/layout-CVdidYA-.js +1 -0
  234. package/dist/dashboard/public/assets/line-BdckgA27.js +1 -0
  235. package/dist/dashboard/public/assets/linear-C9Nh3JLa.js +1 -0
  236. package/dist/dashboard/public/assets/livescript-BwQOo05w.js +1 -0
  237. package/dist/dashboard/public/assets/lua-BgMRiT3U.js +1 -0
  238. package/dist/dashboard/public/assets/mathematica-DTrFuWx2.js +1 -0
  239. package/dist/dashboard/public/assets/mbox-CNhZ1qSd.js +1 -0
  240. package/dist/dashboard/public/assets/mindmap-definition-8da855dc-CK-y1AmO.js +415 -0
  241. package/dist/dashboard/public/assets/mirc-CjQqDB4T.js +1 -0
  242. package/dist/dashboard/public/assets/mllike-CXdrOF99.js +1 -0
  243. package/dist/dashboard/public/assets/modelica-Dc1JOy9r.js +1 -0
  244. package/dist/dashboard/public/assets/mscgen-BA5vi2Kp.js +1 -0
  245. package/dist/dashboard/public/assets/mumps-BT43cFF4.js +1 -0
  246. package/dist/dashboard/public/assets/nginx-DdIZxoE0.js +1 -0
  247. package/dist/dashboard/public/assets/nsis-LdVXkNf5.js +1 -0
  248. package/dist/dashboard/public/assets/ntriples-BfvgReVJ.js +1 -0
  249. package/dist/dashboard/public/assets/octave-Ck1zUtKM.js +1 -0
  250. package/dist/dashboard/public/assets/ordinal-Cboi1Yqb.js +1 -0
  251. package/dist/dashboard/public/assets/oz-BzwKVEFT.js +1 -0
  252. package/dist/dashboard/public/assets/pascal--L3eBynH.js +1 -0
  253. package/dist/dashboard/public/assets/path-CbwjOpE9.js +1 -0
  254. package/dist/dashboard/public/assets/perl-CdXCOZ3F.js +1 -0
  255. package/dist/dashboard/public/assets/pieDiagram-a8764435-T8V0JN2R.js +35 -0
  256. package/dist/dashboard/public/assets/pig-CevX1Tat.js +1 -0
  257. package/dist/dashboard/public/assets/powershell-CFHJl5sT.js +1 -0
  258. package/dist/dashboard/public/assets/properties-C78fOPTZ.js +1 -0
  259. package/dist/dashboard/public/assets/protobuf-ChK-085T.js +1 -0
  260. package/dist/dashboard/public/assets/pug-DeIclll2.js +1 -0
  261. package/dist/dashboard/public/assets/puppet-DMA9R1ak.js +1 -0
  262. package/dist/dashboard/public/assets/python-BuPzkPfP.js +1 -0
  263. package/dist/dashboard/public/assets/q-pXgVlZs6.js +1 -0
  264. package/dist/dashboard/public/assets/quadrantDiagram-1e28029f-CmtVsb5L.js +7 -0
  265. package/dist/dashboard/public/assets/r-B6wPVr8A.js +1 -0
  266. package/dist/dashboard/public/assets/requirementDiagram-08caed73-BUcTnzDl.js +52 -0
  267. package/dist/dashboard/public/assets/rpm-CTu-6PCP.js +1 -0
  268. package/dist/dashboard/public/assets/ruby-B2Rjki9n.js +1 -0
  269. package/dist/dashboard/public/assets/sankeyDiagram-a04cb91d-FswuxQ9M.js +8 -0
  270. package/dist/dashboard/public/assets/sas-B4kiWyti.js +1 -0
  271. package/dist/dashboard/public/assets/scheme-C41bIUwD.js +1 -0
  272. package/dist/dashboard/public/assets/sequenceDiagram-c5b8d532-BJQ15rhX.js +122 -0
  273. package/dist/dashboard/public/assets/shell-CjFT_Tl9.js +1 -0
  274. package/dist/dashboard/public/assets/sieve-C3Gn_uJK.js +1 -0
  275. package/dist/dashboard/public/assets/simple-mode-GW_nhZxv.js +1 -0
  276. package/dist/dashboard/public/assets/smalltalk-CnHTOXQT.js +1 -0
  277. package/dist/dashboard/public/assets/solr-DehyRSwq.js +1 -0
  278. package/dist/dashboard/public/assets/sparql-DkYu6x3z.js +1 -0
  279. package/dist/dashboard/public/assets/spreadsheet-BCZA_wO0.js +1 -0
  280. package/dist/dashboard/public/assets/sql-D0XecflT.js +1 -0
  281. package/dist/dashboard/public/assets/stateDiagram-1ecb1508-BfyE0DYv.js +1 -0
  282. package/dist/dashboard/public/assets/stateDiagram-v2-c2b004d7-pcGOYyiW.js +1 -0
  283. package/dist/dashboard/public/assets/stex-C3f8Ysf7.js +1 -0
  284. package/dist/dashboard/public/assets/styles-b4e223ce--lUviH7V.js +160 -0
  285. package/dist/dashboard/public/assets/styles-ca3715f6-BXbrD1Av.js +207 -0
  286. package/dist/dashboard/public/assets/styles-d45a18b0-GyiMrLKu.js +116 -0
  287. package/dist/dashboard/public/assets/stylus-B533Al4x.js +1 -0
  288. package/dist/dashboard/public/assets/svgDrawCommon-b86b1483-DI4Z1GTS.js +1 -0
  289. package/dist/dashboard/public/assets/swift-BzpIVaGY.js +1 -0
  290. package/dist/dashboard/public/assets/tcl-DVfN8rqt.js +1 -0
  291. package/dist/dashboard/public/assets/textile-CnDTJFAw.js +1 -0
  292. package/dist/dashboard/public/assets/tiddlywiki-DO-Gjzrf.js +1 -0
  293. package/dist/dashboard/public/assets/tiki-DGYXhP31.js +1 -0
  294. package/dist/dashboard/public/assets/timeline-definition-faaaa080-B1IgohU4.js +61 -0
  295. package/dist/dashboard/public/assets/toml-Bm5Em-hy.js +1 -0
  296. package/dist/dashboard/public/assets/troff-wAsdV37c.js +1 -0
  297. package/dist/dashboard/public/assets/ttcn-CfJYG6tj.js +1 -0
  298. package/dist/dashboard/public/assets/ttcn-cfg-B9xdYoR4.js +1 -0
  299. package/dist/dashboard/public/assets/turtle-B1tBg_DP.js +1 -0
  300. package/dist/dashboard/public/assets/vb-CmGdzxic.js +1 -0
  301. package/dist/dashboard/public/assets/vbscript-BuJXcnF6.js +1 -0
  302. package/dist/dashboard/public/assets/velocity-D8B20fx6.js +1 -0
  303. package/dist/dashboard/public/assets/verilog-C6RDOZhf.js +1 -0
  304. package/dist/dashboard/public/assets/vhdl-lSbBsy5d.js +1 -0
  305. package/dist/dashboard/public/assets/webidl-ZXfAyPTL.js +1 -0
  306. package/dist/dashboard/public/assets/xquery-DzFWVndE.js +1 -0
  307. package/dist/dashboard/public/assets/xychartDiagram-f5964ef8-B5oRDe_I.js +7 -0
  308. package/dist/dashboard/public/assets/yacas-BJ4BC0dw.js +1 -0
  309. package/dist/dashboard/public/assets/z80-Hz9HOZM7.js +1 -0
  310. package/dist/dashboard/public/claude-icon-dark.svg +1 -0
  311. package/dist/dashboard/public/claude-icon.svg +1 -0
  312. package/dist/dashboard/public/index.html +16 -0
  313. package/dist/dashboard/settings-manager.d.ts +47 -0
  314. package/dist/dashboard/settings-manager.d.ts.map +1 -0
  315. package/dist/dashboard/settings-manager.js +180 -0
  316. package/dist/dashboard/settings-manager.js.map +1 -0
  317. package/dist/dashboard/utils.d.ts +31 -0
  318. package/dist/dashboard/utils.d.ts.map +1 -0
  319. package/dist/dashboard/utils.js +102 -0
  320. package/dist/dashboard/utils.js.map +1 -0
  321. package/dist/dashboard/watcher.d.ts +32 -0
  322. package/dist/dashboard/watcher.d.ts.map +1 -0
  323. package/dist/dashboard/watcher.js +173 -0
  324. package/dist/dashboard/watcher.js.map +1 -0
  325. package/dist/index.d.ts +13 -0
  326. package/dist/index.d.ts.map +1 -0
  327. package/dist/index.js +380 -0
  328. package/dist/index.js.map +1 -0
  329. package/dist/markdown/templates/design-template.md +96 -0
  330. package/dist/markdown/templates/product-template.md +51 -0
  331. package/dist/markdown/templates/requirements-template.md +50 -0
  332. package/dist/markdown/templates/structure-template.md +145 -0
  333. package/dist/markdown/templates/tasks-template.md +139 -0
  334. package/dist/markdown/templates/tech-template.md +99 -0
  335. package/dist/prompts/create-spec.d.ts +3 -0
  336. package/dist/prompts/create-spec.d.ts.map +1 -0
  337. package/dist/prompts/create-spec.js +93 -0
  338. package/dist/prompts/create-spec.js.map +1 -0
  339. package/dist/prompts/create-steering-doc.d.ts +3 -0
  340. package/dist/prompts/create-steering-doc.d.ts.map +1 -0
  341. package/dist/prompts/create-steering-doc.js +73 -0
  342. package/dist/prompts/create-steering-doc.js.map +1 -0
  343. package/dist/prompts/implement-task.d.ts +3 -0
  344. package/dist/prompts/implement-task.d.ts.map +1 -0
  345. package/dist/prompts/implement-task.js +173 -0
  346. package/dist/prompts/implement-task.js.map +1 -0
  347. package/dist/prompts/index.d.ts +15 -0
  348. package/dist/prompts/index.d.ts.map +1 -0
  349. package/dist/prompts/index.js +49 -0
  350. package/dist/prompts/index.js.map +1 -0
  351. package/dist/prompts/inject-spec-workflow-guide.d.ts +3 -0
  352. package/dist/prompts/inject-spec-workflow-guide.d.ts.map +1 -0
  353. package/dist/prompts/inject-spec-workflow-guide.js +47 -0
  354. package/dist/prompts/inject-spec-workflow-guide.js.map +1 -0
  355. package/dist/prompts/inject-steering-guide.d.ts +3 -0
  356. package/dist/prompts/inject-steering-guide.d.ts.map +1 -0
  357. package/dist/prompts/inject-steering-guide.js +51 -0
  358. package/dist/prompts/inject-steering-guide.js.map +1 -0
  359. package/dist/prompts/refresh-tasks.d.ts +3 -0
  360. package/dist/prompts/refresh-tasks.d.ts.map +1 -0
  361. package/dist/prompts/refresh-tasks.js +224 -0
  362. package/dist/prompts/refresh-tasks.js.map +1 -0
  363. package/dist/prompts/spec-status.d.ts +3 -0
  364. package/dist/prompts/spec-status.d.ts.map +1 -0
  365. package/dist/prompts/spec-status.js +75 -0
  366. package/dist/prompts/spec-status.js.map +1 -0
  367. package/dist/prompts/types.d.ts +13 -0
  368. package/dist/prompts/types.d.ts.map +1 -0
  369. package/dist/prompts/types.js +2 -0
  370. package/dist/prompts/types.js.map +1 -0
  371. package/dist/server.d.ts +17 -0
  372. package/dist/server.d.ts.map +1 -0
  373. package/dist/server.js +175 -0
  374. package/dist/server.js.map +1 -0
  375. package/dist/tools/__tests__/projectPath.test.d.ts +2 -0
  376. package/dist/tools/__tests__/projectPath.test.d.ts.map +1 -0
  377. package/dist/tools/__tests__/projectPath.test.js +187 -0
  378. package/dist/tools/__tests__/projectPath.test.js.map +1 -0
  379. package/dist/tools/approvals.d.ts +14 -0
  380. package/dist/tools/approvals.d.ts.map +1 -0
  381. package/dist/tools/approvals.js +490 -0
  382. package/dist/tools/approvals.js.map +1 -0
  383. package/dist/tools/index.d.ts +5 -0
  384. package/dist/tools/index.d.ts.map +1 -0
  385. package/dist/tools/index.js +52 -0
  386. package/dist/tools/index.js.map +1 -0
  387. package/dist/tools/log-implementation.d.ts +5 -0
  388. package/dist/tools/log-implementation.d.ts.map +1 -0
  389. package/dist/tools/log-implementation.js +397 -0
  390. package/dist/tools/log-implementation.js.map +1 -0
  391. package/dist/tools/spec-status.d.ts +5 -0
  392. package/dist/tools/spec-status.d.ts.map +1 -0
  393. package/dist/tools/spec-status.js +178 -0
  394. package/dist/tools/spec-status.js.map +1 -0
  395. package/dist/tools/spec-workflow-guide.d.ts +5 -0
  396. package/dist/tools/spec-workflow-guide.d.ts.map +1 -0
  397. package/dist/tools/spec-workflow-guide.js +291 -0
  398. package/dist/tools/spec-workflow-guide.js.map +1 -0
  399. package/dist/tools/steering-guide.d.ts +5 -0
  400. package/dist/tools/steering-guide.d.ts.map +1 -0
  401. package/dist/tools/steering-guide.js +192 -0
  402. package/dist/tools/steering-guide.js.map +1 -0
  403. package/dist/types.d.ts +172 -0
  404. package/dist/types.d.ts.map +1 -0
  405. package/dist/types.js +13 -0
  406. package/dist/types.js.map +1 -0
  407. package/package.json +105 -0
@@ -0,0 +1,297 @@
1
+ import { join } from 'path';
2
+ import { promises as fs } from 'fs';
3
+ import { basename, resolve } from 'path';
4
+ import { createHash } from 'crypto';
5
+ import { getGlobalDir, getPermissionErrorHelp } from './global-dir.js';
6
+ /**
7
+ * Generate a stable projectId from an absolute path
8
+ * Uses SHA-1 hash encoded as base64url
9
+ */
10
+ export function generateProjectId(absolutePath) {
11
+ const hash = createHash('sha1').update(absolutePath).digest('base64url');
12
+ // Take first 16 characters for readability
13
+ return hash.substring(0, 16);
14
+ }
15
+ /**
16
+ * Build display name for a workspace.
17
+ * - Main repo: "repo"
18
+ * - Worktree: "repo · worktree"
19
+ */
20
+ export function generateProjectDisplayName(workspacePath, workflowRootPath) {
21
+ const workspaceName = basename(workspacePath);
22
+ const repoName = basename(workflowRootPath);
23
+ if (workspacePath === workflowRootPath) {
24
+ return repoName;
25
+ }
26
+ return `${repoName} · ${workspaceName}`;
27
+ }
28
+ export class ProjectRegistry {
29
+ registryPath;
30
+ registryDir;
31
+ needsInitialization = false;
32
+ constructor() {
33
+ this.registryDir = getGlobalDir();
34
+ this.registryPath = join(this.registryDir, 'activeProjects.json');
35
+ }
36
+ /**
37
+ * Ensure the registry directory exists
38
+ */
39
+ async ensureRegistryDir() {
40
+ try {
41
+ await fs.mkdir(this.registryDir, { recursive: true });
42
+ }
43
+ catch (error) {
44
+ // Directory might already exist, ignore EEXIST errors
45
+ if (error.code === 'EEXIST') {
46
+ return;
47
+ }
48
+ // For permission errors, provide helpful guidance
49
+ if (error.code === 'EACCES' || error.code === 'EPERM') {
50
+ console.error(getPermissionErrorHelp('create directory', this.registryDir));
51
+ throw error;
52
+ }
53
+ // Re-throw other errors
54
+ throw error;
55
+ }
56
+ }
57
+ /**
58
+ * Read the registry file with atomic operations
59
+ * Returns a map keyed by projectId
60
+ */
61
+ async readRegistry() {
62
+ await this.ensureRegistryDir();
63
+ try {
64
+ const content = await fs.readFile(this.registryPath, 'utf-8');
65
+ // Handle empty or whitespace-only files
66
+ const trimmedContent = content.trim();
67
+ if (!trimmedContent) {
68
+ console.error(`[ProjectRegistry] Warning: ${this.registryPath} is empty, initializing with empty registry`);
69
+ // Mark that we need to write the file
70
+ this.needsInitialization = true;
71
+ return new Map();
72
+ }
73
+ const data = JSON.parse(trimmedContent);
74
+ const registry = new Map();
75
+ // Ensure backward compatibility with older formats:
76
+ // - instances may be missing
77
+ // - workflowRootPath may be missing
78
+ for (const [projectId, entry] of Object.entries(data)) {
79
+ const normalizedProjectPath = resolve(entry.projectPath);
80
+ const normalizedWorkflowRootPath = resolve(entry.workflowRootPath || entry.projectPath);
81
+ registry.set(projectId, {
82
+ ...entry,
83
+ projectPath: normalizedProjectPath,
84
+ workflowRootPath: normalizedWorkflowRootPath,
85
+ projectName: entry.projectName || generateProjectDisplayName(normalizedProjectPath, normalizedWorkflowRootPath),
86
+ instances: Array.isArray(entry.instances) ? entry.instances : []
87
+ });
88
+ }
89
+ return registry;
90
+ }
91
+ catch (error) {
92
+ if (error.code === 'ENOENT') {
93
+ // File doesn't exist yet, return empty map
94
+ this.needsInitialization = true;
95
+ return new Map();
96
+ }
97
+ if (error instanceof SyntaxError) {
98
+ // JSON parsing error - file is corrupted or invalid
99
+ console.error(`[ProjectRegistry] Error: Failed to parse ${this.registryPath}: ${error.message}`);
100
+ console.error(`[ProjectRegistry] The file may be corrupted. Initializing with empty registry.`);
101
+ // Back up the corrupted file
102
+ try {
103
+ const backupPath = `${this.registryPath}.corrupted.${Date.now()}`;
104
+ await fs.copyFile(this.registryPath, backupPath);
105
+ console.error(`[ProjectRegistry] Corrupted file backed up to: ${backupPath}`);
106
+ }
107
+ catch (backupError) {
108
+ // Ignore backup errors
109
+ }
110
+ this.needsInitialization = true;
111
+ return new Map();
112
+ }
113
+ throw error;
114
+ }
115
+ }
116
+ /**
117
+ * Write the registry file atomically
118
+ */
119
+ async writeRegistry(registry) {
120
+ await this.ensureRegistryDir();
121
+ const data = Object.fromEntries(registry);
122
+ const content = JSON.stringify(data, null, 2);
123
+ // Write to temporary file first, then rename for atomic operation
124
+ const tempPath = `${this.registryPath}.tmp`;
125
+ await fs.writeFile(tempPath, content, 'utf-8');
126
+ await fs.rename(tempPath, this.registryPath);
127
+ }
128
+ /**
129
+ * Check if a process is still running
130
+ * Note: When running in Docker with path translation, we can't check host PIDs,
131
+ * so we assume processes are alive if path translation is enabled.
132
+ */
133
+ isProcessAlive(pid) {
134
+ // If path translation is enabled, we're in Docker and can't check host PIDs
135
+ const hostPrefix = process.env.SPEC_WORKFLOW_HOST_PATH_PREFIX;
136
+ const containerPrefix = process.env.SPEC_WORKFLOW_CONTAINER_PATH_PREFIX;
137
+ if (hostPrefix && containerPrefix) {
138
+ // Can't verify host PIDs from inside Docker, assume alive
139
+ return true;
140
+ }
141
+ try {
142
+ // Sending signal 0 checks if process exists without actually sending a signal
143
+ process.kill(pid, 0);
144
+ return true;
145
+ }
146
+ catch (error) {
147
+ return false;
148
+ }
149
+ }
150
+ /**
151
+ * Register a project in the global registry
152
+ * Self-healing: If a project exists with dead PIDs, cleans them up and adds new PID
153
+ * Multi-instance: Allows unlimited MCP server instances per project
154
+ */
155
+ async registerProject(projectPath, pid, options = {}) {
156
+ const registry = await this.readRegistry();
157
+ const workspacePath = resolve(projectPath);
158
+ const workflowRootPath = resolve(options.workflowRootPath || projectPath);
159
+ const projectId = generateProjectId(workspacePath);
160
+ const projectName = options.projectName || generateProjectDisplayName(workspacePath, workflowRootPath);
161
+ const existing = registry.get(projectId);
162
+ if (existing) {
163
+ // Self-healing: Filter out dead PIDs
164
+ const liveInstances = existing.instances.filter(i => this.isProcessAlive(i.pid));
165
+ // Check if this PID is already registered (avoid duplicates)
166
+ if (!liveInstances.some(i => i.pid === pid)) {
167
+ liveInstances.push({ pid, registeredAt: new Date().toISOString() });
168
+ }
169
+ // Update with live instances (no limit on number of instances)
170
+ existing.projectPath = workspacePath;
171
+ existing.workflowRootPath = workflowRootPath;
172
+ existing.projectName = projectName;
173
+ existing.instances = liveInstances;
174
+ registry.set(projectId, existing);
175
+ }
176
+ else {
177
+ // New project
178
+ const entry = {
179
+ projectId,
180
+ projectPath: workspacePath,
181
+ workflowRootPath,
182
+ projectName,
183
+ instances: [{ pid, registeredAt: new Date().toISOString() }]
184
+ };
185
+ registry.set(projectId, entry);
186
+ }
187
+ await this.writeRegistry(registry);
188
+ return projectId;
189
+ }
190
+ /**
191
+ * Unregister a project from the global registry by path
192
+ * If pid is provided, only removes that specific instance
193
+ * If no pid provided, removes the entire project (backwards compat)
194
+ */
195
+ async unregisterProject(projectPath, pid) {
196
+ const registry = await this.readRegistry();
197
+ const absolutePath = resolve(projectPath);
198
+ const projectId = generateProjectId(absolutePath);
199
+ const entry = registry.get(projectId);
200
+ if (!entry)
201
+ return;
202
+ if (pid !== undefined) {
203
+ // Remove only this PID's instance
204
+ entry.instances = entry.instances.filter(i => i.pid !== pid);
205
+ if (entry.instances.length === 0) {
206
+ registry.delete(projectId);
207
+ }
208
+ else {
209
+ registry.set(projectId, entry);
210
+ }
211
+ }
212
+ else {
213
+ // Remove entire project (backwards compat)
214
+ registry.delete(projectId);
215
+ }
216
+ await this.writeRegistry(registry);
217
+ }
218
+ /**
219
+ * Unregister a project by projectId
220
+ */
221
+ async unregisterProjectById(projectId) {
222
+ const registry = await this.readRegistry();
223
+ registry.delete(projectId);
224
+ await this.writeRegistry(registry);
225
+ }
226
+ /**
227
+ * Get all active projects from the registry
228
+ */
229
+ async getAllProjects() {
230
+ const registry = await this.readRegistry();
231
+ return Array.from(registry.values());
232
+ }
233
+ /**
234
+ * Get a specific project by path
235
+ */
236
+ async getProject(projectPath) {
237
+ const registry = await this.readRegistry();
238
+ const absolutePath = resolve(projectPath);
239
+ const projectId = generateProjectId(absolutePath);
240
+ return registry.get(projectId) || null;
241
+ }
242
+ /**
243
+ * Get a specific project by projectId
244
+ */
245
+ async getProjectById(projectId) {
246
+ const registry = await this.readRegistry();
247
+ return registry.get(projectId) || null;
248
+ }
249
+ /**
250
+ * Clean up stale instances (where the process is no longer running)
251
+ * Projects with no live instances are removed entirely
252
+ * Returns the count of removed instances
253
+ */
254
+ async cleanupStaleProjects() {
255
+ const registry = await this.readRegistry();
256
+ let removedInstanceCount = 0;
257
+ let needsWrite = this.needsInitialization; // Write if file needs initialization
258
+ for (const [projectId, entry] of registry.entries()) {
259
+ const liveInstances = entry.instances.filter(i => this.isProcessAlive(i.pid));
260
+ const deadCount = entry.instances.length - liveInstances.length;
261
+ if (deadCount > 0) {
262
+ removedInstanceCount += deadCount;
263
+ needsWrite = true;
264
+ if (liveInstances.length === 0) {
265
+ // No live instances, remove entire project
266
+ registry.delete(projectId);
267
+ }
268
+ else {
269
+ // Keep project with only live instances
270
+ entry.instances = liveInstances;
271
+ registry.set(projectId, entry);
272
+ }
273
+ }
274
+ }
275
+ if (needsWrite) {
276
+ await this.writeRegistry(registry);
277
+ this.needsInitialization = false; // Reset flag after successful write
278
+ }
279
+ return removedInstanceCount;
280
+ }
281
+ /**
282
+ * Check if a project is registered by path
283
+ */
284
+ async isProjectRegistered(projectPath) {
285
+ const registry = await this.readRegistry();
286
+ const absolutePath = resolve(projectPath);
287
+ const projectId = generateProjectId(absolutePath);
288
+ return registry.has(projectId);
289
+ }
290
+ /**
291
+ * Get the registry file path for watching
292
+ */
293
+ getRegistryPath() {
294
+ return this.registryPath;
295
+ }
296
+ }
297
+ //# sourceMappingURL=project-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-registry.js","sourceRoot":"","sources":["../../src/core/project-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Security utilities for rate limiting and audit logging
3
+ * Implements security best practices for MCP servers
4
+ */
5
+ import { FastifyRequest, FastifyReply } from 'fastify';
6
+ import { SecurityConfig } from '../types.js';
7
+ export declare const DEFAULT_DASHBOARD_PORT = 5000;
8
+ export declare const DEFAULT_SECURITY_CONFIG: SecurityConfig;
9
+ export declare const VITE_DEV_PORT = 5173;
10
+ /**
11
+ * Generate allowed origins for CORS based on the actual port
12
+ * @param port - The port the dashboard is running on
13
+ * @returns Array of allowed origin URLs
14
+ */
15
+ export declare function generateAllowedOrigins(port: number): string[];
16
+ /**
17
+ * Check if an IP address is localhost
18
+ * @param address - IP address or hostname to check
19
+ * @returns true if the address is localhost (127.x.x.x, localhost, or ::1)
20
+ */
21
+ export declare function isLocalhostAddress(address: string): boolean;
22
+ /**
23
+ * Get security configuration with secure defaults
24
+ * Note: Network binding validation (bindAddress/allowExternalAccess) is handled separately at the config layer
25
+ * @param userConfig - Optional user-provided security configuration overrides
26
+ * @param port - The port the dashboard is running on (used to generate dynamic allowedOrigins)
27
+ */
28
+ export declare function getSecurityConfig(userConfig?: Partial<SecurityConfig>, port?: number): SecurityConfig;
29
+ /**
30
+ * Rate limiting implementation
31
+ */
32
+ export declare class RateLimiter {
33
+ private requests;
34
+ private config;
35
+ constructor(config: SecurityConfig);
36
+ /**
37
+ * Check if request should be rate limited
38
+ */
39
+ checkLimit(clientId: string): {
40
+ allowed: boolean;
41
+ retryAfter?: number;
42
+ };
43
+ /**
44
+ * Create rate limiting middleware
45
+ */
46
+ middleware(): (request: FastifyRequest, reply: FastifyReply) => Promise<undefined>;
47
+ /**
48
+ * Clean up old request records
49
+ */
50
+ private cleanup;
51
+ }
52
+ /**
53
+ * Audit log entry
54
+ */
55
+ export interface AuditLogEntry {
56
+ timestamp: string;
57
+ actor: string;
58
+ action: string;
59
+ resource: string;
60
+ result: 'success' | 'failure' | 'denied';
61
+ details?: Record<string, any>;
62
+ }
63
+ /**
64
+ * Audit logger for security events
65
+ */
66
+ export declare class AuditLogger {
67
+ private config;
68
+ private logPath;
69
+ constructor(config: SecurityConfig, workspaceRoot?: string);
70
+ /**
71
+ * Initialize audit log (create directory if needed)
72
+ */
73
+ initialize(): Promise<void>;
74
+ /**
75
+ * Log an audit event
76
+ */
77
+ log(entry: AuditLogEntry): Promise<void>;
78
+ /**
79
+ * Create audit logging middleware
80
+ */
81
+ middleware(): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
82
+ }
83
+ /**
84
+ * Security headers middleware
85
+ * @param port - The port the dashboard is running on (used for CSP connect-src for WebSocket)
86
+ */
87
+ export declare function createSecurityHeadersMiddleware(port?: number): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
88
+ /**
89
+ * CORS configuration
90
+ */
91
+ export declare function getCorsConfig(config: SecurityConfig): false | {
92
+ origin: (origin: string, callback: (error: Error | null, allow?: boolean) => void) => void;
93
+ credentials: boolean;
94
+ methods: string[];
95
+ allowedHeaders: string[];
96
+ };
97
+ //# sourceMappingURL=security-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-utils.d.ts","sourceRoot":"","sources":["../../src/core/security-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAI3C,eAAO,MAAM,uBAAuB,EAAE,cAOrC,CAAC;AAGF,eAAO,MAAM,aAAa,OAAO,CAAC;AAElC;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAW7D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI3D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,CAarG;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,EAAE,cAAc;IAOlC;;OAEG;IACI,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IA6B9E;;OAEG;IACI,UAAU,KACD,SAAS,cAAc,EAAE,OAAO,YAAY;IAmB5D;;OAEG;IACH,OAAO,CAAC,OAAO;CAahB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,cAAc,EAAE,aAAa,CAAC,EAAE,MAAM;IAc1D;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAajC;;OAEG;IACG,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAa9C;;OAEG;IACH,UAAU,KACM,SAAS,cAAc,EAAE,OAAO,YAAY;CA0B7D;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,CAAC,EAAE,MAAM,IAW7C,SAAS,cAAc,EAAE,OAAO,YAAY,mBAe3D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc;qBAM/B,MAAM,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI;;;;EAkBpF"}
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Security utilities for rate limiting and audit logging
3
+ * Implements security best practices for MCP servers
4
+ */
5
+ import { appendFile, mkdir } from 'fs/promises';
6
+ import { join } from 'path';
7
+ // Default port for the dashboard
8
+ export const DEFAULT_DASHBOARD_PORT = 5000;
9
+ // Default security configuration (secure by default)
10
+ // Note: allowedOrigins should be dynamically generated based on the actual port
11
+ export const DEFAULT_SECURITY_CONFIG = {
12
+ rateLimitEnabled: true,
13
+ rateLimitPerMinute: 120, // 120 requests per minute per client
14
+ auditLogEnabled: true,
15
+ auditLogRetentionDays: 30,
16
+ corsEnabled: true,
17
+ allowedOrigins: [`http://localhost:${DEFAULT_DASHBOARD_PORT}`, `http://127.0.0.1:${DEFAULT_DASHBOARD_PORT}`]
18
+ };
19
+ // Default Vite dev server port (used when running frontend in dev mode)
20
+ export const VITE_DEV_PORT = 5173;
21
+ /**
22
+ * Generate allowed origins for CORS based on the actual port
23
+ * @param port - The port the dashboard is running on
24
+ * @returns Array of allowed origin URLs
25
+ */
26
+ export function generateAllowedOrigins(port) {
27
+ const origins = [`http://localhost:${port}`, `http://127.0.0.1:${port}`];
28
+ // In non-production environments, also allow Vite dev server origin (port 5173)
29
+ // The Vite proxy forwards requests but preserves the Origin header
30
+ // Use !== 'production' to be permissive by default for local dev tools
31
+ if (process.env.NODE_ENV !== 'production') {
32
+ origins.push(`http://localhost:${VITE_DEV_PORT}`, `http://127.0.0.1:${VITE_DEV_PORT}`);
33
+ }
34
+ return origins;
35
+ }
36
+ /**
37
+ * Check if an IP address is localhost
38
+ * @param address - IP address or hostname to check
39
+ * @returns true if the address is localhost (127.x.x.x, localhost, or ::1)
40
+ */
41
+ export function isLocalhostAddress(address) {
42
+ return address === 'localhost' ||
43
+ address === '::1' || // IPv6 localhost
44
+ address.startsWith('127.'); // Any 127.x.x.x address (includes 127.0.0.1)
45
+ }
46
+ /**
47
+ * Get security configuration with secure defaults
48
+ * Note: Network binding validation (bindAddress/allowExternalAccess) is handled separately at the config layer
49
+ * @param userConfig - Optional user-provided security configuration overrides
50
+ * @param port - The port the dashboard is running on (used to generate dynamic allowedOrigins)
51
+ */
52
+ export function getSecurityConfig(userConfig, port) {
53
+ const actualPort = port || DEFAULT_DASHBOARD_PORT;
54
+ // Generate dynamic allowedOrigins based on the actual port if not explicitly provided
55
+ const dynamicAllowedOrigins = generateAllowedOrigins(actualPort);
56
+ const config = {
57
+ ...DEFAULT_SECURITY_CONFIG,
58
+ allowedOrigins: dynamicAllowedOrigins,
59
+ ...userConfig
60
+ };
61
+ return config;
62
+ }
63
+ /**
64
+ * Rate limiting implementation
65
+ */
66
+ export class RateLimiter {
67
+ requests = new Map();
68
+ config;
69
+ constructor(config) {
70
+ this.config = config;
71
+ // Clean up old entries every minute
72
+ setInterval(() => this.cleanup(), 60000);
73
+ }
74
+ /**
75
+ * Check if request should be rate limited
76
+ */
77
+ checkLimit(clientId) {
78
+ if (!this.config.rateLimitEnabled) {
79
+ return { allowed: true };
80
+ }
81
+ const now = Date.now();
82
+ const windowMs = 60000; // 1 minute window
83
+ const maxRequests = this.config.rateLimitPerMinute;
84
+ // Get client request history
85
+ const requests = this.requests.get(clientId) || [];
86
+ // Filter to requests within the current window
87
+ const recentRequests = requests.filter(timestamp => now - timestamp < windowMs);
88
+ // Check if limit exceeded
89
+ if (recentRequests.length >= maxRequests) {
90
+ const oldestRequest = recentRequests[0];
91
+ const retryAfter = Math.ceil((oldestRequest + windowMs - now) / 1000);
92
+ return { allowed: false, retryAfter };
93
+ }
94
+ // Add current request
95
+ recentRequests.push(now);
96
+ this.requests.set(clientId, recentRequests);
97
+ return { allowed: true };
98
+ }
99
+ /**
100
+ * Create rate limiting middleware
101
+ */
102
+ middleware() {
103
+ return async (request, reply) => {
104
+ // Use IP address as client identifier
105
+ const clientId = request.ip || 'unknown';
106
+ const result = this.checkLimit(clientId);
107
+ if (!result.allowed) {
108
+ return reply
109
+ .code(429)
110
+ .header('Retry-After', String(result.retryAfter || 60))
111
+ .send({
112
+ error: 'Too Many Requests',
113
+ message: `Rate limit exceeded. Maximum ${this.config.rateLimitPerMinute} requests per minute.`,
114
+ retryAfter: result.retryAfter
115
+ });
116
+ }
117
+ };
118
+ }
119
+ /**
120
+ * Clean up old request records
121
+ */
122
+ cleanup() {
123
+ const now = Date.now();
124
+ const windowMs = 60000;
125
+ for (const [clientId, requests] of this.requests.entries()) {
126
+ const recentRequests = requests.filter(timestamp => now - timestamp < windowMs);
127
+ if (recentRequests.length === 0) {
128
+ this.requests.delete(clientId);
129
+ }
130
+ else {
131
+ this.requests.set(clientId, recentRequests);
132
+ }
133
+ }
134
+ }
135
+ }
136
+ /**
137
+ * Audit logger for security events
138
+ */
139
+ export class AuditLogger {
140
+ config;
141
+ logPath;
142
+ constructor(config, workspaceRoot) {
143
+ this.config = config;
144
+ // Determine audit log path
145
+ if (config.auditLogPath) {
146
+ this.logPath = config.auditLogPath;
147
+ }
148
+ else if (workspaceRoot) {
149
+ this.logPath = join(workspaceRoot, '.spec-workflow', 'audit.log');
150
+ }
151
+ else {
152
+ // Fallback to temp directory
153
+ this.logPath = join(process.cwd(), 'audit.log');
154
+ }
155
+ }
156
+ /**
157
+ * Initialize audit log (create directory if needed)
158
+ */
159
+ async initialize() {
160
+ if (!this.config.auditLogEnabled) {
161
+ return;
162
+ }
163
+ try {
164
+ const logDir = join(this.logPath, '..');
165
+ await mkdir(logDir, { recursive: true });
166
+ }
167
+ catch (error) {
168
+ console.error('Failed to initialize audit log:', error);
169
+ }
170
+ }
171
+ /**
172
+ * Log an audit event
173
+ */
174
+ async log(entry) {
175
+ if (!this.config.auditLogEnabled) {
176
+ return;
177
+ }
178
+ try {
179
+ const logLine = JSON.stringify(entry) + '\n';
180
+ await appendFile(this.logPath, logLine, 'utf-8');
181
+ }
182
+ catch (error) {
183
+ console.error('Failed to write audit log:', error);
184
+ }
185
+ }
186
+ /**
187
+ * Create audit logging middleware
188
+ */
189
+ middleware() {
190
+ return async (request, reply) => {
191
+ const startTime = Date.now();
192
+ // Log after response is sent using reply.then() which fires after response completes
193
+ reply.then(() => {
194
+ const entry = {
195
+ timestamp: new Date().toISOString(),
196
+ actor: request.ip || 'unknown',
197
+ action: `${request.method} ${request.url}`,
198
+ resource: request.url,
199
+ result: reply.statusCode < 400 ? 'success' : reply.statusCode === 401 || reply.statusCode === 403 ? 'denied' : 'failure',
200
+ details: {
201
+ statusCode: reply.statusCode,
202
+ duration: Date.now() - startTime,
203
+ userAgent: request.headers['user-agent']
204
+ }
205
+ };
206
+ // Fire and forget - don't await to avoid blocking
207
+ this.log(entry).catch(() => { });
208
+ }, () => { } // Ignore errors from reply.then
209
+ );
210
+ };
211
+ }
212
+ }
213
+ /**
214
+ * Security headers middleware
215
+ * @param port - The port the dashboard is running on (used for CSP connect-src for WebSocket)
216
+ */
217
+ export function createSecurityHeadersMiddleware(port) {
218
+ const actualPort = port || DEFAULT_DASHBOARD_PORT;
219
+ // Build connect-src directive with WebSocket endpoints
220
+ let connectSrc = `'self' ws://localhost:${actualPort} ws://127.0.0.1:${actualPort}`;
221
+ // In non-production environments, also allow Vite dev server connections
222
+ if (process.env.NODE_ENV !== 'production') {
223
+ connectSrc += ` ws://localhost:${VITE_DEV_PORT} ws://127.0.0.1:${VITE_DEV_PORT}`;
224
+ }
225
+ return async (request, reply) => {
226
+ // Add security headers
227
+ reply.header('X-Content-Type-Options', 'nosniff'); // Prevent MIME type sniffing
228
+ reply.header('X-Frame-Options', 'DENY'); // Prevent clickjacking
229
+ reply.header('X-XSS-Protection', '1; mode=block'); // Enable XSS protection
230
+ reply.header('Referrer-Policy', 'strict-origin-when-cross-origin'); // Prevent referrer leakage
231
+ // CSP for dashboard
232
+ // Note: cdn.jsdelivr.net is required for highlight.js stylesheets used by the MDX editor
233
+ // connect-src allows WebSocket connections to the dashboard on the actual port
234
+ reply.header('Content-Security-Policy', `default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; img-src 'self' data:; connect-src ${connectSrc};`);
235
+ };
236
+ }
237
+ /**
238
+ * CORS configuration
239
+ */
240
+ export function getCorsConfig(config) {
241
+ if (!config.corsEnabled) {
242
+ return false; // Disable CORS
243
+ }
244
+ return {
245
+ origin: (origin, callback) => {
246
+ // Allow requests with no origin (e.g., curl, Postman)
247
+ if (!origin) {
248
+ callback(null, true);
249
+ return;
250
+ }
251
+ // Check if origin is in allowed list
252
+ if (config.allowedOrigins.includes(origin)) {
253
+ callback(null, true);
254
+ }
255
+ else {
256
+ callback(new Error('Not allowed by CORS'));
257
+ }
258
+ },
259
+ credentials: true,
260
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
261
+ allowedHeaders: ['Content-Type']
262
+ };
263
+ }
264
+ //# sourceMappingURL=security-utils.js.map