@tequila99/githuman 0.11.0 → 0.12.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 (330) hide show
  1. package/README.md +62 -2
  2. package/dist/cli/commands/mcp.js +112 -0
  3. package/dist/cli/commands/pi-acp.js +21 -0
  4. package/dist/cli/config.js +7 -1
  5. package/dist/cli/index.js +19 -8
  6. package/dist/cli/server-runtime.js +1 -0
  7. package/dist/server/adapters/agents/artifact-permission.js +88 -0
  8. package/dist/server/adapters/pi-acp/agent.js +98 -0
  9. package/dist/server/adapters/pi-acp/compatibility.js +57 -0
  10. package/dist/server/adapters/pi-acp/control.js +76 -0
  11. package/dist/server/adapters/pi-acp/errors.js +17 -0
  12. package/dist/server/adapters/pi-acp/extension.js +255 -0
  13. package/dist/server/adapters/pi-acp/launch.js +16 -0
  14. package/dist/server/adapters/pi-acp/mcp.js +153 -0
  15. package/dist/server/adapters/pi-acp/process.js +199 -0
  16. package/dist/server/adapters/pi-acp/session.js +409 -0
  17. package/dist/server/adapters/pi-acp/vendor/LICENSE +21 -0
  18. package/dist/server/adapters/pi-acp/vendor/README.md +29 -0
  19. package/dist/server/adapters/pi-acp/vendor/prompt.js +56 -0
  20. package/dist/server/app.js +94 -5
  21. package/dist/server/config/agents.js +4 -6
  22. package/dist/server/db/artifact-graph-migrations.js +56 -0
  23. package/dist/server/db/artifact-migrations.js +70 -0
  24. package/dist/server/db/artifact-review-migrations.js +24 -0
  25. package/dist/server/db/artifact-search-migrations.js +31 -0
  26. package/dist/server/db/artifact-validation-migrations.js +70 -0
  27. package/dist/server/db/index.js +3 -0
  28. package/dist/server/db/migrations.js +10 -0
  29. package/dist/server/db/sqlite-error.js +15 -0
  30. package/dist/server/db/transaction.js +40 -0
  31. package/dist/server/errors/artifacts.js +28 -0
  32. package/dist/server/errors/database.js +2 -0
  33. package/dist/server/hooks/access/require-access.js +8 -0
  34. package/dist/server/repositories/artifact-catalog.repo.js +283 -0
  35. package/dist/server/repositories/artifact-files.repo.js +181 -0
  36. package/dist/server/repositories/artifact-graph.repo.js +50 -0
  37. package/dist/server/repositories/artifact-search.repo.js +192 -0
  38. package/dist/server/repositories/artifact-transition.repo.js +85 -0
  39. package/dist/server/repositories/artifact-validation.repo.js +146 -0
  40. package/dist/server/repositories/notes-catalog.repo.js +2 -10
  41. package/dist/server/repositories/task.repo.js +114 -0
  42. package/dist/server/routes/agent.js +8 -1
  43. package/dist/server/routes/artifact-actions.js +129 -0
  44. package/dist/server/routes/artifact-agent.js +183 -0
  45. package/dist/server/routes/artifact-definitions.js +163 -0
  46. package/dist/server/routes/artifact-transitions.js +80 -0
  47. package/dist/server/routes/artifacts.js +111 -0
  48. package/dist/server/routes/events.js +5 -1
  49. package/dist/server/routes/project-files.js +90 -0
  50. package/dist/server/services/agent/agent-session.js +180 -10
  51. package/dist/server/services/agent/context-builder.js +30 -2
  52. package/dist/server/services/agent/session-registry.js +106 -2
  53. package/dist/server/services/agent/turn-task-context.js +1 -0
  54. package/dist/server/services/artifact-authorization.service.js +174 -0
  55. package/dist/server/services/artifact-definition.service.js +96 -0
  56. package/dist/server/services/artifact-export.service.js +44 -0
  57. package/dist/server/services/artifact-graph-validation.js +497 -0
  58. package/dist/server/services/artifact-graph.service.js +81 -0
  59. package/dist/server/services/artifact-lifecycle.service.js +343 -0
  60. package/dist/server/services/artifact-maintenance.service.js +55 -0
  61. package/dist/server/services/artifact-search.service.js +331 -0
  62. package/dist/server/services/artifact-storage.service.js +646 -0
  63. package/dist/server/services/artifact-transition.service.js +607 -0
  64. package/dist/server/services/artifact-type.service.js +74 -0
  65. package/dist/server/services/artifact-validation-jobs.service.js +518 -0
  66. package/dist/server/services/device-connections.js +9 -1
  67. package/dist/server/services/project-files.service.js +144 -0
  68. package/dist/server/services/review.service.js +6 -5
  69. package/dist/server/services/task.service.js +225 -0
  70. package/dist/server/utils/artifact-actor.js +13 -0
  71. package/dist/server/utils/artifact-page.js +42 -0
  72. package/dist/server/utils/take-within-bytes.js +16 -0
  73. package/dist/server/utils/text.js +7 -0
  74. package/dist/shared/agents/schemas.js +27 -0
  75. package/dist/shared/app/storage-constants.js +2 -0
  76. package/dist/shared/artifact-tools/descriptions.js +11 -0
  77. package/dist/shared/artifact-tools/instructions.js +14 -0
  78. package/dist/shared/artifact-tools/schemas.js +119 -0
  79. package/dist/shared/artifact-transitions/constants.js +21 -0
  80. package/dist/shared/artifact-transitions/schemas.js +111 -0
  81. package/dist/shared/artifact-transitions/transitions.js +61 -0
  82. package/dist/shared/artifact-transitions/types.js +1 -0
  83. package/dist/shared/artifacts/constants.js +34 -0
  84. package/dist/shared/artifacts/mcp-constants.js +24 -0
  85. package/dist/shared/artifacts/normalize.js +9 -0
  86. package/dist/shared/artifacts/schemas.js +820 -0
  87. package/dist/shared/artifacts/search.js +101 -0
  88. package/dist/shared/artifacts/types.js +1 -0
  89. package/dist/shared/events/schemas.js +1 -1
  90. package/dist/shared/events/types.js +1 -0
  91. package/dist/shared/git/constants.js +2 -0
  92. package/dist/shared/project-files/constants.js +18 -0
  93. package/dist/shared/project-files/schemas.js +35 -0
  94. package/dist/shared/project-files/types.js +1 -0
  95. package/dist/shared/tasks/schemas.js +261 -0
  96. package/dist/shared/tasks/types.js +1 -0
  97. package/dist/shared/utils/artifact-body.js +10 -0
  98. package/dist/shared/utils/canonical-json.js +22 -0
  99. package/dist/shared/utils/is-safe-prefixed-filename.js +10 -0
  100. package/dist/shared/utils/is-uuid.js +4 -2
  101. package/dist/shared/utils/is-valid-artifact-type-id.js +9 -0
  102. package/dist/shared/utils/is-valid-filename-prefix.js +13 -0
  103. package/dist/shared/utils/markdown-path.js +4 -0
  104. package/dist/shared/utils/pdf-path.js +4 -0
  105. package/dist/web/assets/AccessPage-Dvj0aCkm.js +1 -0
  106. package/dist/web/assets/AddButton-CMC9qf51.js +4 -0
  107. package/dist/web/assets/AddToAgentButton-BitTdCEg.js +1 -0
  108. package/dist/web/assets/AppPage-Cozv2vqG.js +1 -0
  109. package/dist/web/assets/ArtifactDefinitionPicker-BQOoU60k.js +3 -0
  110. package/dist/web/assets/ArtifactDefinitionPicker-D3qdrxlC.css +1 -0
  111. package/dist/web/assets/ArtifactsPage-BJR-XYYs.js +2 -0
  112. package/dist/web/assets/ArtifactsPage-Cu1xdYNf.css +1 -0
  113. package/dist/web/assets/CancelButton-mkxRHwoZ.js +1 -0
  114. package/dist/web/assets/ClosePopup-BtDeDZsk.js +1 -0
  115. package/dist/web/assets/CreateButton-zCmb7snC.js +1 -0
  116. package/dist/web/assets/CreateReviewFabButton-BCg1TvFh.js +1 -0
  117. package/dist/web/assets/DialogHeader-CPL9OtDV.css +1 -0
  118. package/dist/web/assets/DialogHeader-CQvAZVx7.js +1 -0
  119. package/dist/web/assets/{ErrorNotFound-DtnO85PU.js → ErrorNotFound-fXS367lZ.js} +1 -1
  120. package/dist/web/assets/{IndexPage-D5PvyGrX.css → IndexPage-5V5GLtgi.css} +1 -1
  121. package/dist/web/assets/IndexPage-DMTZyWQA.js +1 -0
  122. package/dist/web/assets/MainLayout-DWJ_-EMV.js +7 -0
  123. package/dist/web/assets/MainLayout-Dy74m_q1.css +1 -0
  124. package/dist/web/assets/NoteVisualEditor-5rHBQ3j0.js +1054 -0
  125. package/dist/web/assets/{NoteVisualEditor-BYikrY7Q.css → NoteVisualEditor-DE4ACTRJ.css} +1 -1
  126. package/dist/web/assets/NotesPage-C_eoCUl7.css +1 -0
  127. package/dist/web/assets/NotesPage-CjaxjLBW.js +4 -0
  128. package/dist/web/assets/{PdfPreview-kL_nk9my.js → PdfPreview-2NQ5gkjh.js} +2 -2
  129. package/dist/web/assets/ProjectFilePage-B-WUUxnq.js +1 -0
  130. package/dist/web/assets/QBtn-V3VXtJXi.js +1 -0
  131. package/dist/web/assets/QFab-CxpedhPI.js +1 -0
  132. package/dist/web/assets/QForm-ClpSneeD.js +1 -0
  133. package/dist/web/assets/QInput-BnqLJGXl.js +1 -0
  134. package/dist/web/assets/QScrollArea-js5dkUz1.js +1 -0
  135. package/dist/web/assets/QSpace-C7iAnGlU.js +1 -0
  136. package/dist/web/assets/QTabs-DIy5gWMb.js +1 -0
  137. package/dist/web/assets/QVirtualScroll-DkD0TczV.js +1 -0
  138. package/dist/web/assets/ReviewDetailPage-BgxcsTqY.css +1 -0
  139. package/dist/web/assets/ReviewDetailPage-Bmc8DmSu.js +1 -0
  140. package/dist/web/assets/ReviewsPage-DR1IWCmu.js +1 -0
  141. package/dist/web/assets/SearchInput-BRyN93Z1.js +1 -0
  142. package/dist/web/assets/StatusSwitcher-CpkH8BuC.js +1 -0
  143. package/dist/web/assets/{StatusSwitcher-D6i8_OfR.css → StatusSwitcher-DaE1ufAA.css} +1 -1
  144. package/dist/web/assets/TagFilterField-Bl3FBkB5.js +1 -0
  145. package/dist/web/assets/TagFilterField-DjUH0m3_.css +1 -0
  146. package/dist/web/assets/{TerminalView-617DZuPt.css → TerminalView-BCeke4xd.css} +1 -1
  147. package/dist/web/assets/{TerminalView-BsO_SZut.js → TerminalView-tEyLM5qZ.js} +12 -12
  148. package/dist/web/assets/{abnfDiagram-O67JEVCF-qUSrlp_c.js → abnfDiagram-O67JEVCF-DN9xiV9p.js} +1 -1
  149. package/dist/web/assets/{access-store-Q8nkv4Nb.js → access-store-CwkOl71l.js} +1 -1
  150. package/dist/web/assets/{agent-store-D2jgeBgi.js → agent-store-Grbx4jkz.js} +5 -5
  151. package/dist/web/assets/architecture-7GRP2DOG-DmaYCP8T.js +1 -0
  152. package/dist/web/assets/{architectureDiagram-NJMV4G6O-D7RC6OS2.js → architectureDiagram-NJMV4G6O-BVAJOucG.js} +1 -1
  153. package/dist/web/assets/{blockDiagram-BEXU5L5S-CmNtTpfK.js → blockDiagram-BEXU5L5S-TJAfSX44.js} +1 -1
  154. package/dist/web/assets/{c4Diagram-YGBWAQC7-CKCcu1qx.js → c4Diagram-YGBWAQC7-5UHyT_tW.js} +1 -1
  155. package/dist/web/assets/channel-B4_5H-fb.js +1 -0
  156. package/dist/web/assets/{chunk-3FUC2YCW-Drz6oeKx.js → chunk-3FUC2YCW-CelgLyJi.js} +1 -1
  157. package/dist/web/assets/{chunk-5DYCD2WN-RItzEgV_.js → chunk-5DYCD2WN-bhEAVIbQ.js} +1 -1
  158. package/dist/web/assets/{chunk-742MDFTN-D6hKdOv7.js → chunk-742MDFTN-ByLz4EcD.js} +1 -1
  159. package/dist/web/assets/{chunk-7INBJB4K-Dk7-Eior.js → chunk-7INBJB4K-CK2i0csa.js} +1 -1
  160. package/dist/web/assets/{chunk-7M6MHVWA-Cw8E0Nkn.js → chunk-7M6MHVWA-CoCiujjC.js} +1 -1
  161. package/dist/web/assets/{chunk-7PRAP22T-t-mTeNXF.js → chunk-7PRAP22T-CY4NCrCY.js} +1 -1
  162. package/dist/web/assets/{chunk-GTNCS2PH-C7j2atV8.js → chunk-GTNCS2PH-DMB7bQGB.js} +1 -1
  163. package/dist/web/assets/{chunk-GWA4HPMP-CH9T9LZO.js → chunk-GWA4HPMP-F6qclPTR.js} +1 -1
  164. package/dist/web/assets/{chunk-MBY4JIJT-yXOO37xD.js → chunk-MBY4JIJT-CSiiBtlX.js} +1 -1
  165. package/dist/web/assets/{chunk-NETBCI7D-C-s2UtAR.js → chunk-NETBCI7D-CBh7GChL.js} +1 -1
  166. package/dist/web/assets/{chunk-O7XYJQB3-5Me_aV8o.js → chunk-O7XYJQB3-B2aqz0sN.js} +1 -1
  167. package/dist/web/assets/{chunk-UA2S7LBM-BQaibeff.js → chunk-UA2S7LBM-B_-P_eR0.js} +1 -1
  168. package/dist/web/assets/{chunk-WEXAMYUT-UBcOE4Lf.js → chunk-WEXAMYUT-19nMFxzK.js} +1 -1
  169. package/dist/web/assets/{chunk-Z7XXMR3K-BsA8o3Pt.js → chunk-Z7XXMR3K-Co7gzRY1.js} +1 -1
  170. package/dist/web/assets/{chunk-ZIGJFQKS--KwSI1U0.js → chunk-ZIGJFQKS-Eoi4K5bK.js} +1 -1
  171. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-pMF8oCmq.js → classDiagram-v2-NBCMYWYE-CfFGzL-A.js} +1 -1
  172. package/dist/web/assets/copy-code-DaqEfdXL.js +1 -0
  173. package/dist/web/assets/create-DedXY-KP.js +1 -0
  174. package/dist/web/assets/{cynefin-OW5HDTMX-DFgtXolF.js → cynefin-OW5HDTMX-DdZXl3KU.js} +1 -1
  175. package/dist/web/assets/{cynefinDiagram-VND7K2PF-CPY0JRyq.js → cynefinDiagram-VND7K2PF-WU21nYH-.js} +1 -1
  176. package/dist/web/assets/{dagre-6A5THRUB-KdjJKAB7.js → dagre-6A5THRUB-48z3F8Pl.js} +1 -1
  177. package/dist/web/assets/date-DZIuZp-w.js +1 -0
  178. package/dist/web/assets/devices-store-Bfk2wdrQ.js +1 -0
  179. package/dist/web/assets/{diagram-22UHCM2B-BDcOcvF_.js → diagram-22UHCM2B-B0AZgJW9.js} +1 -1
  180. package/dist/web/assets/{diagram-3UASUU5V-6j2nlZL3.js → diagram-3UASUU5V-CzEzrxuO.js} +1 -1
  181. package/dist/web/assets/{diagram-ATOU4E4O-BN8cdxED.js → diagram-ATOU4E4O-w6h1bD3w.js} +1 -1
  182. package/dist/web/assets/{diagram-CDSNMT55-cWPiXHkr.js → diagram-CDSNMT55-BdTHQd_o.js} +1 -1
  183. package/dist/web/assets/{diagram-MLGK6HIB-DcgkH_Vl.js → diagram-MLGK6HIB-pB4cj6c2.js} +1 -1
  184. package/dist/web/assets/{diagram-MPIPVDR6-D_e86L99.js → diagram-MPIPVDR6-C7C06qyV.js} +1 -1
  185. package/dist/web/assets/diff-store-DBDL5Hw5.js +93 -0
  186. package/dist/web/assets/{dist-Bdsq-Ze1.js → dist-B7Cu0FBW.js} +1 -1
  187. package/dist/web/assets/{dist-BVVhgjWT.js → dist-B8i8y3GY.js} +1 -1
  188. package/dist/web/assets/{dist-CjkUzU4s.js → dist-BNgPhnKo.js} +1 -1
  189. package/dist/web/assets/{dist-Bz40Nd9I.js → dist-BWoPV5P3.js} +1 -1
  190. package/dist/web/assets/{dist-BP40mvwJ.js → dist-CJ7isxli.js} +1 -1
  191. package/dist/web/assets/{dist-C9enVshI.js → dist-CTJJYvYG.js} +1 -1
  192. package/dist/web/assets/{dist-BwpyANeW.js → dist-CZz0KXAA.js} +1 -1
  193. package/dist/web/assets/{dist-CU43ytsh.js → dist-DNpwgDba.js} +1 -1
  194. package/dist/web/assets/{dist-BMFtkY2y.js → dist-DWCdhk-O.js} +1 -1
  195. package/dist/web/assets/{dist-MUQaJRtC.js → dist-DqSUdjG9.js} +1 -1
  196. package/dist/web/assets/{dist-CLCP7f3x.js → dist-Dy9K9LOR.js} +1 -1
  197. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-D8xHOFOi.js → ebnfDiagram-ZINNZB2B-75vt7539.js} +1 -1
  198. package/dist/web/assets/{elk-276RUBZZ-JiDwNyLy.js → elk-276RUBZZ-DXDjJVTy.js} +1 -1
  199. package/dist/web/assets/{erDiagram-OPXOYQCR-D3J6u81a.js → erDiagram-OPXOYQCR-tNNInTmk.js} +1 -1
  200. package/dist/web/assets/eventmodeling-NTZA5JFV-QMRehQtM.js +1 -0
  201. package/dist/web/assets/flowDiagram-KWPJA3E3-DJAgsbXC.js +1 -0
  202. package/dist/web/assets/{ganttDiagram-FUAMR5RP-nvAVhbgI.js → ganttDiagram-FUAMR5RP-h3E8lnCd.js} +1 -1
  203. package/dist/web/assets/{gitGraph-4MIJSDKK-Bv_wkB2T.js → gitGraph-4MIJSDKK-BUwY-iv2.js} +1 -1
  204. package/dist/web/assets/{gitGraphDiagram-X574FWY7-DxxwadhH.js → gitGraphDiagram-X574FWY7-C0AgQH5k.js} +1 -1
  205. package/dist/web/assets/i18n-CfTd46OX.js +1 -0
  206. package/dist/web/assets/icons-BoaZfGBj.js +1 -0
  207. package/dist/web/assets/index-BQU3KK1E.css +1 -0
  208. package/dist/web/assets/index-BTpokWxr.js +2 -0
  209. package/dist/web/assets/{info-A6RAGUB7-Dw18yeIE.js → info-A6RAGUB7-DVMZ4hdK.js} +1 -1
  210. package/dist/web/assets/{infoDiagram-VRGFBTTK-C3BrgjYg.js → infoDiagram-VRGFBTTK-kLaGXGQ2.js} +1 -1
  211. package/dist/web/assets/init-app-Bxc_l2yT.js +1 -0
  212. package/dist/web/assets/{is-D0waCELe.js → is-B1kBQ5Va.js} +1 -1
  213. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-DU2p9szX.js → ishikawaDiagram-OU5B5YK6-Oj7bZlyo.js} +1 -1
  214. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-Bs2zQnew.js → journeyDiagram-ZHPQQLJL-BG7V2Srs.js} +1 -1
  215. package/dist/web/assets/{kanban-definition-PNTS6WVX-pqXd5uNi.js → kanban-definition-PNTS6WVX-CIJiDE6d.js} +1 -1
  216. package/dist/web/assets/{line-CN4Dxpei.js → line-D2vEKYRZ.js} +1 -1
  217. package/dist/web/assets/markdown-Bm2wfvok.js +16 -0
  218. package/dist/web/assets/mdi-Byw1NZrZ.js +1 -0
  219. package/dist/web/assets/{mermaid-parser.core-DZqDOGRM.js → mermaid-parser.core-BcBEWjW6.js} +3 -3
  220. package/dist/web/assets/{mermaid.core-CUndCvkz.js → mermaid.core-DobO0deo.js} +4 -4
  221. package/dist/web/assets/{mindmap-definition-NLK3R4M7-DyK7YqB7.js → mindmap-definition-NLK3R4M7-y7Qz5gBF.js} +1 -1
  222. package/dist/web/assets/{packet-AYTQ26CC-DvlW_0BP.js → packet-AYTQ26CC-oRQgKr9d.js} +1 -1
  223. package/dist/web/assets/{pegDiagram-GJSIUBJH-k2e_-FWQ.js → pegDiagram-GJSIUBJH-RIU8XOHq.js} +1 -1
  224. package/dist/web/assets/{pie-WAS4IAKB-B99zaYjO.js → pie-WAS4IAKB-qmOlsLkx.js} +1 -1
  225. package/dist/web/assets/{pieDiagram-5QR66LMP-BPbS05lK.js → pieDiagram-5QR66LMP-BM932TDA.js} +1 -1
  226. package/dist/web/assets/pinia-BfqEuvsi.js +1 -0
  227. package/dist/web/assets/pinia-Cmp1GI8k.js +1 -0
  228. package/dist/web/assets/preview-store-D7qF6SgX.js +2 -0
  229. package/dist/web/assets/project-files-store-OhGs_H7U.js +1 -0
  230. package/dist/web/assets/{quadrantDiagram-O4NWA36T-DqNNdsd2.js → quadrantDiagram-O4NWA36T-qQZYwTy7.js} +1 -1
  231. package/dist/web/assets/{radar-RG4KPBEZ-BIAAr9LO.js → radar-RG4KPBEZ-Kdglk6eY.js} +1 -1
  232. package/dist/web/assets/{railroad-74A4TZTK-DYxr-1-f.js → railroad-74A4TZTK-kVhMYx4I.js} +1 -1
  233. package/dist/web/assets/railroad-abnf-HS5TGJTU-MiKgjOXr.js +1 -0
  234. package/dist/web/assets/railroad-ebnf-LZEXJU2U-BFL9h7Zx.js +1 -0
  235. package/dist/web/assets/railroad-peg-WCYAUIDC-ChGGfIn7.js +1 -0
  236. package/dist/web/assets/{railroadDiagram-XR7U4H2S-TNg-LqRR.js → railroadDiagram-XR7U4H2S-K9SrOGcT.js} +1 -1
  237. package/dist/web/assets/render-Brao9Nj2.js +1 -0
  238. package/dist/web/assets/repository-store-DZgQ5H7J.js +1 -0
  239. package/dist/web/assets/{requirementDiagram-PLB6GJNP-hKAyJR6W.js → requirementDiagram-PLB6GJNP-CtA9ZKEx.js} +1 -1
  240. package/dist/web/assets/runtime-core.esm-bundler-DDnHUoIy.js +1 -0
  241. package/dist/web/assets/{safe-storage-B1Aq5CGi.js → safe-storage-CvVvrm4v.js} +1 -1
  242. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-_MSL-tES.js → sankeyDiagram-IPEJSGJF-BH_t4exx.js} +1 -1
  243. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-Byq-joBG.js → sequenceDiagram-PO4LG4MO-hPNPqpDa.js} +1 -1
  244. package/dist/web/assets/single-flight-CyDZB3rb.js +1 -0
  245. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-BR2jAgaa.js → stateDiagram-v2-GCMORJYK-BVmjZDRD.js} +1 -1
  246. package/dist/web/assets/{swimlanes-2SLR337P-Nt8ojGKK.js → swimlanes-2SLR337P-Tgk8-6jj.js} +1 -1
  247. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-W1zDrK61.js +8 -0
  248. package/dist/web/assets/{terminal-store-D0abibMa.js → terminal-store-CKgJjtC6.js} +1 -1
  249. package/dist/web/assets/theme-slSxLNv6.js +1 -0
  250. package/dist/web/assets/{timeline-definition-EJHVYXUP-BtihLvxk.js → timeline-definition-EJHVYXUP-lAIHEGe8.js} +1 -1
  251. package/dist/web/assets/{treeView-Q6P3EWNA-D5HzXeKd.js → treeView-Q6P3EWNA-CdvzKtWf.js} +1 -1
  252. package/dist/web/assets/{treemap-WGGIJYW6-Dovjuutg.js → treemap-WGGIJYW6-BxDSJfrJ.js} +1 -1
  253. package/dist/web/assets/uid-B1JNhznI.js +1 -0
  254. package/dist/web/assets/use-add-agent-context-BSHGL5iV.js +1 -0
  255. package/dist/web/assets/use-app-theme-fmVV2QM8.js +1 -0
  256. package/dist/web/assets/use-countdown-D6_qSOZJ.js +1 -0
  257. package/dist/web/assets/use-notify-error-Dx2D9IEP.js +1 -0
  258. package/dist/web/assets/use-panel-width-BYOPuHat.js +1 -0
  259. package/dist/web/assets/use-router-link-D7geCGQI.js +1 -0
  260. package/dist/web/assets/use-row-height-BK8kNihW.js +1 -0
  261. package/dist/web/assets/use-server-events-CvcbsbIf.js +1 -0
  262. package/dist/web/assets/use-virtual-scroll-DixIsJk4.js +1 -0
  263. package/dist/web/assets/use-workspace-navigation-AaRMWmjQ.js +1 -0
  264. package/dist/web/assets/{usecaseDiagram-POWQR4AR-DSwRE8zj.js → usecaseDiagram-POWQR4AR-BH3KvMpz.js} +1 -1
  265. package/dist/web/assets/{vennDiagram-UO4OBE2U-C-B8yP1K.js → vennDiagram-UO4OBE2U-Dfi-raKp.js} +1 -1
  266. package/dist/web/assets/viewport-store-NqQS7itT.js +1 -0
  267. package/dist/web/assets/{vue-i18n.runtime-CEtihHHL.js → vue-i18n.runtime-JOo8b7-t.js} +2 -2
  268. package/dist/web/assets/{wardley-WFR3VGLG-wWgaZuh6.js → wardley-WFR3VGLG-DmbtSM24.js} +1 -1
  269. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-DbhenrHd.js → wardleyDiagram-VNRHLVJA-DIzhGvI7.js} +1 -1
  270. package/dist/web/assets/weighted-lru-BFtZKA_y.js +1 -0
  271. package/dist/web/assets/workspace-navigation-xfxULfM7.js +1 -0
  272. package/dist/web/assets/{xychartDiagram-PMCCYNJV-CxFDmAEt.js → xychartDiagram-PMCCYNJV-XgqSHH_N.js} +1 -1
  273. package/dist/web/index.html +10 -8
  274. package/package.json +5 -3
  275. package/dist/web/assets/AccessPage-_OKn9U0P.js +0 -1
  276. package/dist/web/assets/AppPage-oQ9Rz0be.js +0 -1
  277. package/dist/web/assets/CreateButton-D1EtoicG.js +0 -1
  278. package/dist/web/assets/CreateReviewFabButton-BxvT3i9e.js +0 -1
  279. package/dist/web/assets/IndexPage-gdD7BHxA.js +0 -1
  280. package/dist/web/assets/MainLayout-47PTLKZf.js +0 -6
  281. package/dist/web/assets/MainLayout-ikSrFqBh.css +0 -1
  282. package/dist/web/assets/NoteVisualEditor-BQ6mVfj0.js +0 -1054
  283. package/dist/web/assets/NotesPage-DNR9iW_G.js +0 -4
  284. package/dist/web/assets/NotesPage-_8naQciW.css +0 -1
  285. package/dist/web/assets/PanelResize-CAQFVc8b.js +0 -1
  286. package/dist/web/assets/QBtn-CB-yw_iP.js +0 -1
  287. package/dist/web/assets/QExpansionItem-Dr5oXO0m.js +0 -4
  288. package/dist/web/assets/QFab-DPz8RiP0.js +0 -1
  289. package/dist/web/assets/QForm-CASiKhGm.js +0 -1
  290. package/dist/web/assets/QInput-k34nCPWg.js +0 -1
  291. package/dist/web/assets/QSelect-CaKn9Tkg.js +0 -1
  292. package/dist/web/assets/QSpace-Di_bZQY6.js +0 -1
  293. package/dist/web/assets/QVirtualScroll-oGN_Tl5J.js +0 -1
  294. package/dist/web/assets/ReviewDetailPage-BKgIprY0.js +0 -1
  295. package/dist/web/assets/ReviewDetailPage-CkCilMNW.css +0 -1
  296. package/dist/web/assets/ReviewsPage-6PJ6wofN.js +0 -1
  297. package/dist/web/assets/StatusSwitcher-s9w6iKpR.js +0 -1
  298. package/dist/web/assets/architecture-7GRP2DOG-D6PShS0_.js +0 -1
  299. package/dist/web/assets/channel-D6lb5DbY.js +0 -1
  300. package/dist/web/assets/create-B9Tn5Xvi.js +0 -1
  301. package/dist/web/assets/devices-store-CrZ4MxyY.js +0 -1
  302. package/dist/web/assets/diff-store-ByxMCKoa.js +0 -93
  303. package/dist/web/assets/eventmodeling-NTZA5JFV-QCwZLvmm.js +0 -1
  304. package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff +0 -0
  305. package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ-D-x-0Q06.woff2 +0 -0
  306. package/dist/web/assets/flowDiagram-KWPJA3E3-CgF90hDi.js +0 -1
  307. package/dist/web/assets/i18n-DDCqtTTW.js +0 -1
  308. package/dist/web/assets/index-BNHt6zmP.js +0 -2
  309. package/dist/web/assets/index-BwbM_RKi.css +0 -1
  310. package/dist/web/assets/init-app-BouXTk8p.js +0 -1
  311. package/dist/web/assets/markdown-jR0fhe4d.js +0 -15
  312. package/dist/web/assets/pinia-BETEFLDG.js +0 -1
  313. package/dist/web/assets/pinia-DO0AdKGg.js +0 -1
  314. package/dist/web/assets/railroad-abnf-HS5TGJTU-GESjhc3t.js +0 -1
  315. package/dist/web/assets/railroad-ebnf-LZEXJU2U-Cybu0A7J.js +0 -1
  316. package/dist/web/assets/railroad-peg-WCYAUIDC-DCHxrtbI.js +0 -1
  317. package/dist/web/assets/runtime-core.esm-bundler-CAvPiMpd.js +0 -1
  318. package/dist/web/assets/single-flight-DLCbzLI3.js +0 -1
  319. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-Ds1hVYqh.js +0 -8
  320. package/dist/web/assets/theme-QEcOl0lf.js +0 -1
  321. package/dist/web/assets/use-add-agent-context-BaPUIO93.js +0 -1
  322. package/dist/web/assets/use-app-theme-B1cdbnHn.js +0 -1
  323. package/dist/web/assets/use-countdown-CTag4T13.js +0 -1
  324. package/dist/web/assets/use-panel-width-CB-0S3zX.js +0 -1
  325. package/dist/web/assets/use-router-link-Dr9wnnyZ.js +0 -1
  326. package/dist/web/assets/use-row-height-DQHVMtUe.js +0 -1
  327. package/dist/web/assets/use-server-events-BrBCbeHu.js +0 -1
  328. package/dist/web/assets/use-virtual-scroll-DG0UKjVk.js +0 -1
  329. package/dist/web/assets/weighted-lru-BSxRnMHR.js +0 -1
  330. /package/dist/web/assets/{PanelResize-XjrbOj2S.css → AddToAgentButton-XjrbOj2S.css} +0 -0
@@ -0,0 +1,283 @@
1
+ import { canonicalJson } from "../../shared/utils/canonical-json.js";
2
+ import { normalizeArtifact } from "../../shared/artifacts/normalize.js";
3
+ import { withTransaction } from "../db/transaction.js";
4
+ import { isSqliteBusyError } from "../db/sqlite-error.js";
5
+ import { randomUUID } from 'node:crypto';
6
+ import { join } from 'node:path';
7
+ import { artifactBusyError, ArtifactConflictError, ArtifactIntegrityError } from "../errors/artifacts.js";
8
+ export class ArtifactCatalogRepository {
9
+ onMetadata;
10
+ storeId;
11
+ db;
12
+ constructor(db, root, namespace) {
13
+ this.db = db;
14
+ this.storeId = this.transaction(() => {
15
+ const found = db
16
+ .prepare('SELECT id FROM artifact_stores WHERE canonical_root=? AND db_namespace=?')
17
+ .get(root, namespace);
18
+ if (found)
19
+ return found.id;
20
+ // A stopped backup keeps its identity when restored with its database.
21
+ const main = db.prepare('PRAGMA database_list').all().find(database => database.name === 'main');
22
+ if (main?.file === join(root, '.githuman', `${namespace}reviews.db`)) {
23
+ const stores = db
24
+ .prepare('SELECT id FROM artifact_stores WHERE db_namespace=?')
25
+ .all(namespace);
26
+ if (stores.length > 1) {
27
+ throw new ArtifactConflictError('Ambiguous restored artifact store.');
28
+ }
29
+ if (stores[0])
30
+ return stores[0].id;
31
+ }
32
+ const id = randomUUID();
33
+ db.prepare('INSERT INTO artifact_stores VALUES(?,?,?,1)').run(id, root, namespace);
34
+ db.prepare('INSERT INTO artifact_store_epochs VALUES(?,0)').run(id);
35
+ return id;
36
+ });
37
+ }
38
+ transaction(run) {
39
+ return this.runTransaction(run, 'immediate');
40
+ }
41
+ readSnapshot(run) {
42
+ return this.runTransaction(run, 'deferred');
43
+ }
44
+ runTransaction(run, mode) {
45
+ try {
46
+ return withTransaction(this.db, run, { mode });
47
+ }
48
+ catch (error) {
49
+ if (isSqliteBusyError(error)) {
50
+ throw artifactBusyError('Artifact database is busy. Retry the operation.');
51
+ }
52
+ throw error;
53
+ }
54
+ }
55
+ epoch() {
56
+ const row = this.db
57
+ .prepare('SELECT store_epoch FROM artifact_store_epochs WHERE store_id=?')
58
+ .get(this.storeId);
59
+ return row?.store_epoch ?? 0;
60
+ }
61
+ validationEpoch() {
62
+ return this.db
63
+ .prepare('SELECT COALESCE(MAX(seq),0) epoch FROM artifact_store_changes WHERE store_id=? AND validation_relevant=1')
64
+ .get(this.storeId).epoch;
65
+ }
66
+ get(id) {
67
+ const row = this.db
68
+ .prepare('SELECT snapshot_json FROM artifact_current WHERE store_id=? AND artifact_id=?')
69
+ .get(this.storeId, id);
70
+ return row
71
+ ? normalizeArtifact(JSON.parse(row.snapshot_json))
72
+ : undefined;
73
+ }
74
+ list(limit, after) {
75
+ const rows = this.db
76
+ .prepare(`SELECT c.snapshot_json FROM artifact_current c
77
+ JOIN artifacts a ON a.store_id=c.store_id AND a.id=c.artifact_id
78
+ WHERE c.store_id=? AND (? IS NULL OR (a.created_at,a.id)>(?,?))
79
+ ORDER BY a.created_at,a.id LIMIT ?`)
80
+ .all(this.storeId, after?.id ?? null, after?.createdAt ?? '', after?.id ?? '', limit);
81
+ return rows.map(row => normalizeArtifact(JSON.parse(row.snapshot_json)));
82
+ }
83
+ eventSequence() {
84
+ return this.db
85
+ .prepare('SELECT COALESCE(MAX(seq),0) seq FROM artifact_events WHERE store_id=?')
86
+ .get(this.storeId).seq;
87
+ }
88
+ assertExportHistory(id, snapshotSeq) {
89
+ const missing = this.db
90
+ .prepare(`SELECT e.seq FROM artifact_events e
91
+ LEFT JOIN artifact_metadata_versions v
92
+ ON v.store_id=e.store_id AND v.artifact_id=e.artifact_id AND v.version=json_extract(e.data_json,'$.afterVersion')
93
+ WHERE e.store_id=? AND e.artifact_id=? AND e.seq<=? AND v.version IS NULL LIMIT 1`)
94
+ .get(this.storeId, id, snapshotSeq);
95
+ if (missing) {
96
+ throw new ArtifactIntegrityError('Artifact history version is missing.');
97
+ }
98
+ }
99
+ exportHistory(id, afterSeq, snapshotSeq, limit) {
100
+ const rows = this.db
101
+ .prepare(`SELECT e.seq,e.data_json,v.snapshot_json
102
+ FROM artifact_events e LEFT JOIN artifact_metadata_versions v
103
+ ON v.store_id=e.store_id AND v.artifact_id=e.artifact_id AND v.version=json_extract(e.data_json,'$.afterVersion')
104
+ WHERE e.store_id=? AND e.artifact_id=? AND e.seq>? AND e.seq<=?
105
+ ORDER BY e.seq LIMIT ?`)
106
+ .all(this.storeId, id, afterSeq, snapshotSeq, limit);
107
+ return rows.map(row => {
108
+ if (row.snapshot_json === null) {
109
+ throw new ArtifactIntegrityError('Artifact history version is missing.');
110
+ }
111
+ return {
112
+ ...JSON.parse(row.data_json),
113
+ seq: row.seq,
114
+ snapshot: normalizeArtifact(JSON.parse(row.snapshot_json))
115
+ };
116
+ });
117
+ }
118
+ history(id, afterSeq, limit) {
119
+ const rows = this.db
120
+ .prepare('SELECT seq,data_json FROM artifact_events WHERE store_id=? AND artifact_id=? AND seq>? ORDER BY seq LIMIT ?')
121
+ .all(this.storeId, id, afterSeq, limit);
122
+ return rows.map(row => ({
123
+ ...JSON.parse(row.data_json),
124
+ seq: row.seq
125
+ }));
126
+ }
127
+ operation(requestId) {
128
+ const row = this.db
129
+ .prepare(`SELECT o.*, (SELECT state FROM artifact_operation_events e
130
+ WHERE e.store_id=o.store_id AND e.request_id=o.request_id ORDER BY e.seq DESC LIMIT 1) state
131
+ FROM artifact_operations o WHERE o.store_id=? AND o.request_id=?`)
132
+ .get(this.storeId, requestId);
133
+ return row
134
+ ? {
135
+ requestId: row.request_id,
136
+ kind: row.kind,
137
+ payloadHash: row.payload_hash,
138
+ artifactId: row.artifact_id,
139
+ data: JSON.parse(row.data_json),
140
+ state: row.state
141
+ }
142
+ : undefined;
143
+ }
144
+ operations(includeCommitted = true) {
145
+ const rows = this.db
146
+ .prepare(`SELECT request_id FROM artifact_operations o WHERE store_id=? AND kind=? AND (? OR (SELECT state FROM artifact_operation_events e WHERE e.store_id=o.store_id AND e.request_id=o.request_id ORDER BY e.seq DESC LIMIT 1) IN ('pending','awaiting_retry')) ORDER BY rowid`)
147
+ .all(this.storeId, 'create', Number(includeCommitted));
148
+ return rows.map(row => this.operation(row.request_id));
149
+ }
150
+ reserve(operation, now) {
151
+ this.db
152
+ .prepare('INSERT INTO artifact_operations VALUES(?,?,?,?,?,?)')
153
+ .run(this.storeId, operation.requestId, operation.kind, operation.payloadHash, operation.artifactId, JSON.stringify(operation.data));
154
+ this.transition(operation.requestId, 'pending', now);
155
+ }
156
+ transition(requestId, state, now) {
157
+ this.db
158
+ .prepare('INSERT INTO artifact_operation_events(store_id,request_id,state,created_at) VALUES(?,?,?,?)')
159
+ .run(this.storeId, requestId, state, now);
160
+ }
161
+ claim(requestId, owner, now, ttl) {
162
+ const lease = this.db
163
+ .prepare('SELECT owner,expires_at FROM artifact_operation_leases WHERE store_id=? AND request_id=?')
164
+ .get(this.storeId, requestId);
165
+ if (lease && lease.owner !== owner && lease.expires_at > now) {
166
+ throw new ArtifactConflictError('Artifact operation is in progress.');
167
+ }
168
+ this.db
169
+ .prepare(`INSERT INTO artifact_operation_leases VALUES(?,?,?,?)
170
+ ON CONFLICT(store_id,request_id) DO UPDATE SET owner=excluded.owner,expires_at=excluded.expires_at`)
171
+ .run(this.storeId, requestId, owner, now + ttl);
172
+ }
173
+ assertOwner(requestId, owner, now) {
174
+ const row = this.db
175
+ .prepare('SELECT owner,expires_at FROM artifact_operation_leases WHERE store_id=? AND request_id=?')
176
+ .get(this.storeId, requestId);
177
+ if (!row || row.owner !== owner || row.expires_at <= now) {
178
+ throw new ArtifactConflictError('Artifact operation lease expired.');
179
+ }
180
+ }
181
+ release(requestId, owner) {
182
+ this.db
183
+ .prepare('DELETE FROM artifact_operation_leases WHERE store_id=? AND request_id=? AND owner=?')
184
+ .run(this.storeId, requestId, owner);
185
+ }
186
+ insertArtifact(data) {
187
+ const a = data.artifact;
188
+ this.db
189
+ .prepare('INSERT INTO artifacts VALUES(?,?,?,?,?,?,?)')
190
+ .run(this.storeId, a.id, data.fileName, a.sha256, a.bytes, a.bodyStartLine, a.createdAt);
191
+ }
192
+ appendMetadata(artifact, requestId, actor, action) {
193
+ const previous = this.get(artifact.id);
194
+ const event = {
195
+ storeId: this.storeId,
196
+ artifactId: artifact.id,
197
+ requestId,
198
+ actor,
199
+ action,
200
+ beforeVersion: artifact.metadataVersion === 1 ? null : artifact.metadataVersion - 1,
201
+ afterVersion: artifact.metadataVersion,
202
+ createdAt: artifact.metadataUpdatedAt,
203
+ metadata: {
204
+ title: artifact.title,
205
+ ...(artifact.sourcePath === undefined
206
+ ? {}
207
+ : { sourcePath: artifact.sourcePath }),
208
+ tags: artifact.tags,
209
+ typeId: artifact.typeId,
210
+ taskIds: artifact.taskIds,
211
+ relations: artifact.relations
212
+ }
213
+ };
214
+ const snapshot = JSON.stringify(artifact);
215
+ this.db
216
+ .prepare('INSERT INTO artifact_metadata_versions VALUES(?,?,?,?,?)')
217
+ .run(this.storeId, artifact.id, artifact.metadataVersion, requestId, snapshot);
218
+ this.db
219
+ .prepare('INSERT INTO artifact_events(store_id,artifact_id,request_id,data_json) VALUES(?,?,?,?)')
220
+ .run(this.storeId, artifact.id, requestId, JSON.stringify(event));
221
+ this.db
222
+ .prepare(`INSERT INTO artifact_current VALUES(?,?,?,?) ON CONFLICT(store_id,artifact_id)
223
+ DO UPDATE SET version=excluded.version,snapshot_json=excluded.snapshot_json`)
224
+ .run(this.storeId, artifact.id, artifact.metadataVersion, snapshot);
225
+ const relevant = previous
226
+ ? canonicalJson([
227
+ previous.status,
228
+ previous.typeId,
229
+ previous.taskIds,
230
+ previous.relations,
231
+ previous.revisionOf
232
+ ]) !==
233
+ canonicalJson([
234
+ artifact.status,
235
+ artifact.typeId,
236
+ artifact.taskIds,
237
+ artifact.relations,
238
+ artifact.revisionOf
239
+ ])
240
+ : artifact.status !== 'draft' ||
241
+ artifact.taskIds.length > 0 ||
242
+ artifact.relations.length > 0 ||
243
+ artifact.revisionOf.length > 0;
244
+ this.recordChange('artifact', artifact.id, requestId, relevant);
245
+ this.onMetadata?.(artifact);
246
+ }
247
+ recordChange(kind, id, requestId, validationRelevant = true) {
248
+ const inserted = this.db
249
+ .prepare('INSERT INTO artifact_store_changes(store_id,entity_kind,entity_id,request_id,validation_relevant) VALUES(?,?,?,?,?)')
250
+ .run(this.storeId, kind, id, requestId, Number(validationRelevant));
251
+ this.db
252
+ .prepare('UPDATE artifact_store_epochs SET store_epoch=? WHERE store_id=?')
253
+ .run(Number(inserted.lastInsertRowid), this.storeId);
254
+ }
255
+ supersededArtifactIds() {
256
+ const rows = this.db
257
+ .prepare(`SELECT DISTINCT artifact_id FROM artifact_metadata_versions
258
+ WHERE store_id=? AND json_extract(snapshot_json, '$.status')='superseded'`)
259
+ .all(this.storeId);
260
+ return new Set(rows.map(row => row.artifact_id));
261
+ }
262
+ all() {
263
+ return this.db
264
+ .prepare('SELECT snapshot_json FROM artifact_current WHERE store_id=? ORDER BY artifact_id')
265
+ .all(this.storeId).map(row => normalizeArtifact(JSON.parse(row.snapshot_json)));
266
+ }
267
+ rebuild() {
268
+ this.transaction(() => {
269
+ this.db
270
+ .prepare('DELETE FROM artifact_current WHERE store_id=?')
271
+ .run(this.storeId);
272
+ this.db
273
+ .prepare(`INSERT INTO artifact_current SELECT v.store_id,v.artifact_id,v.version,v.snapshot_json
274
+ FROM artifact_metadata_versions v WHERE v.store_id=? AND v.version=(SELECT MAX(n.version)
275
+ FROM artifact_metadata_versions n WHERE n.store_id=v.store_id AND n.artifact_id=v.artifact_id)`)
276
+ .run(this.storeId);
277
+ this.db
278
+ .prepare(`INSERT INTO artifact_store_epochs VALUES(?,(SELECT COALESCE(MAX(seq),0) FROM artifact_store_changes WHERE store_id=?))
279
+ ON CONFLICT(store_id) DO UPDATE SET store_epoch=excluded.store_epoch`)
280
+ .run(this.storeId, this.storeId);
281
+ });
282
+ }
283
+ }
@@ -0,0 +1,181 @@
1
+ import { constants, closeSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readdirSync, readSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { isSafePrefixedFilename } from "../../shared/utils/is-safe-prefixed-filename.js";
4
+ import { isValidFilenamePrefix } from "../../shared/utils/is-valid-filename-prefix.js";
5
+ import { MAX_ARTIFACT_BYTES } from "../../shared/artifacts/constants.js";
6
+ import { ArtifactInputError, ArtifactIntegrityError } from "../errors/artifacts.js";
7
+ import { hasErrorCode } from "../utils/system-error.js";
8
+ import { sha256Hex } from "../utils/hash.js";
9
+ import { resolveWithinRepo } from "../services/git.service.js";
10
+ export class ArtifactFilesRepository {
11
+ repositoryPath;
12
+ root;
13
+ prefix;
14
+ constructor(repositoryPath, prefix) {
15
+ if (!isValidFilenamePrefix(prefix)) {
16
+ throw new ArtifactInputError('Invalid artifact filename prefix.');
17
+ }
18
+ this.repositoryPath = realpathSync(repositoryPath);
19
+ this.prefix = prefix;
20
+ this.root = resolveWithinRepo(this.repositoryPath, '.githuman/artifacts');
21
+ }
22
+ directory(create) {
23
+ for (const path of [join(this.repositoryPath, '.githuman'), this.root]) {
24
+ try {
25
+ if (create) {
26
+ try {
27
+ mkdirSync(path, { mode: 0o700 });
28
+ this.syncDirectory(join(path, '..'));
29
+ }
30
+ catch (error) {
31
+ if (!hasErrorCode(error, 'EEXIST'))
32
+ throw error;
33
+ }
34
+ }
35
+ const info = lstatSync(path);
36
+ if (info.isSymbolicLink() ||
37
+ !info.isDirectory() ||
38
+ realpathSync(path) !== path) {
39
+ throw new ArtifactIntegrityError('Artifact storage directory is not a regular directory.');
40
+ }
41
+ }
42
+ catch (error) {
43
+ if (hasErrorCode(error, 'ENOENT'))
44
+ return false;
45
+ throw error;
46
+ }
47
+ }
48
+ return true;
49
+ }
50
+ path(name) {
51
+ if (!isSafePrefixedFilename(name, this.prefix)) {
52
+ throw new ArtifactInputError('Invalid artifact file name.');
53
+ }
54
+ return resolveWithinRepo(this.repositoryPath, `.githuman/artifacts/${name}`);
55
+ }
56
+ read(name) {
57
+ if (!this.directory(false))
58
+ return undefined;
59
+ const path = this.path(name);
60
+ let fd;
61
+ try {
62
+ if (lstatSync(path).isSymbolicLink()) {
63
+ throw new ArtifactIntegrityError('Symbolic artifact files are not supported.');
64
+ }
65
+ fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
66
+ }
67
+ catch (error) {
68
+ if (hasErrorCode(error, 'ENOENT'))
69
+ return undefined;
70
+ if (hasErrorCode(error, 'ELOOP')) {
71
+ throw new ArtifactIntegrityError('Symbolic artifact files are not supported.');
72
+ }
73
+ throw error;
74
+ }
75
+ try {
76
+ const stat = fstatSync(fd);
77
+ if (!stat.isFile() || stat.size > MAX_ARTIFACT_BYTES) {
78
+ throw new ArtifactIntegrityError('Artifact file has an invalid size or type.');
79
+ }
80
+ const buffer = Buffer.alloc(stat.size + 1);
81
+ let count = 0;
82
+ while (count < buffer.length) {
83
+ const bytes = readSync(fd, buffer, count, buffer.length - count, count);
84
+ if (!bytes)
85
+ break;
86
+ count += bytes;
87
+ }
88
+ if (count !== stat.size) {
89
+ throw new ArtifactIntegrityError('Artifact file changed during reading.');
90
+ }
91
+ return buffer.subarray(0, count);
92
+ }
93
+ finally {
94
+ closeSync(fd);
95
+ }
96
+ }
97
+ verify(name, hash, bytes) {
98
+ const content = this.read(name);
99
+ if (!content || content.length !== bytes || sha256Hex(content) !== hash) {
100
+ throw new ArtifactIntegrityError('Artifact file is missing or its hash does not match.');
101
+ }
102
+ return content;
103
+ }
104
+ writeTemp(name, content) {
105
+ this.directory(true);
106
+ const fd = openSync(this.path(name), constants.O_WRONLY |
107
+ constants.O_CREAT |
108
+ constants.O_EXCL |
109
+ constants.O_NOFOLLOW, 0o600);
110
+ try {
111
+ writeFileSync(fd, content);
112
+ fsyncSync(fd);
113
+ }
114
+ finally {
115
+ closeSync(fd);
116
+ }
117
+ this.syncDirectory();
118
+ }
119
+ publish(temp, name, hash, bytes) {
120
+ this.verify(temp, hash, bytes);
121
+ this.syncFile(temp);
122
+ try {
123
+ linkSync(this.path(temp), this.path(name));
124
+ }
125
+ catch (error) {
126
+ if (!hasErrorCode(error, 'EEXIST'))
127
+ throw error;
128
+ this.verify(name, hash, bytes);
129
+ }
130
+ this.syncDirectory();
131
+ }
132
+ removeTemp(name) {
133
+ if (!name.endsWith('.tmp')) {
134
+ throw new ArtifactInputError('Only an operation temporary file may be removed.');
135
+ }
136
+ if (!this.directory(false))
137
+ return;
138
+ const path = this.path(name);
139
+ try {
140
+ if (lstatSync(path).isSymbolicLink()) {
141
+ throw new ArtifactIntegrityError('Symbolic artifact files are not supported.');
142
+ }
143
+ unlinkSync(path);
144
+ this.syncDirectory();
145
+ }
146
+ catch (error) {
147
+ if (!hasErrorCode(error, 'ENOENT'))
148
+ throw error;
149
+ }
150
+ }
151
+ names() {
152
+ return this.directory(false)
153
+ ? readdirSync(this.root).filter(name => name.startsWith(this.prefix))
154
+ : [];
155
+ }
156
+ syncFile(name) {
157
+ this.directory(false);
158
+ // Windows needs a writable handle to flush file contents.
159
+ const fd = openSync(this.path(name), constants.O_RDWR | constants.O_NOFOLLOW);
160
+ try {
161
+ fsyncSync(fd);
162
+ }
163
+ finally {
164
+ closeSync(fd);
165
+ }
166
+ // Recovery can find a link created before the previous directory flush.
167
+ this.syncDirectory();
168
+ }
169
+ syncDirectory(path = this.root) {
170
+ // Windows does not support opening directories for fsync through this API.
171
+ if (process.platform === 'win32')
172
+ return;
173
+ const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
174
+ try {
175
+ fsyncSync(fd);
176
+ }
177
+ finally {
178
+ closeSync(fd);
179
+ }
180
+ }
181
+ }
@@ -0,0 +1,50 @@
1
+ import { ArtifactIntegrityError } from "../errors/artifacts.js";
2
+ export class ArtifactGraphRepository {
3
+ db;
4
+ catalog;
5
+ constructor(db, catalog) {
6
+ this.db = db;
7
+ this.catalog = catalog;
8
+ }
9
+ appendLineage(artifact) {
10
+ const parent = artifact.revisionOf?.[0];
11
+ if (parent) {
12
+ this.db
13
+ .prepare('INSERT INTO artifact_revision_parents VALUES(?,?,?)')
14
+ .run(this.catalog.storeId, artifact.id, parent);
15
+ }
16
+ }
17
+ ancestors(id) {
18
+ const result = [];
19
+ const visited = new Set([id]);
20
+ let child = id;
21
+ for (;;) {
22
+ const row = this.db
23
+ .prepare('SELECT parent_id FROM artifact_revision_parents WHERE store_id=? AND child_id=?')
24
+ .get(this.catalog.storeId, child);
25
+ if (!row)
26
+ return result;
27
+ if (visited.has(row.parent_id)) {
28
+ throw new ArtifactIntegrityError('Artifact lineage contains a cycle.');
29
+ }
30
+ visited.add(row.parent_id);
31
+ result.push(row.parent_id);
32
+ child = row.parent_id;
33
+ }
34
+ }
35
+ edges(id, direction, kind) {
36
+ return this.catalog
37
+ .all()
38
+ .flatMap(artifact => artifact.relations
39
+ .filter(relation => (direction === 'incoming'
40
+ ? relation.targetId === id
41
+ : artifact.id === id) &&
42
+ (!kind || relation.kind === kind))
43
+ .map(relation => ({
44
+ sourceId: artifact.id,
45
+ targetId: relation.targetId,
46
+ kind: relation.kind
47
+ })))
48
+ .sort((a, b) => `${a.kind}:${a.sourceId}:${a.targetId}`.localeCompare(`${b.kind}:${b.sourceId}:${b.targetId}`));
49
+ }
50
+ }