@tequila99/githuman 0.11.2 → 0.13.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 (325) hide show
  1. package/README.md +74 -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 +10 -1
  5. package/dist/cli/index.js +19 -8
  6. package/dist/cli/server-runtime.js +5 -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 +95 -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/local-request.js +5 -0
  34. package/dist/server/hooks/access/require-access.js +13 -2
  35. package/dist/server/repositories/artifact-catalog.repo.js +283 -0
  36. package/dist/server/repositories/artifact-files.repo.js +181 -0
  37. package/dist/server/repositories/artifact-graph.repo.js +50 -0
  38. package/dist/server/repositories/artifact-search.repo.js +192 -0
  39. package/dist/server/repositories/artifact-transition.repo.js +85 -0
  40. package/dist/server/repositories/artifact-validation.repo.js +157 -0
  41. package/dist/server/repositories/notes-catalog.repo.js +2 -10
  42. package/dist/server/repositories/task.repo.js +114 -0
  43. package/dist/server/routes/agent.js +8 -1
  44. package/dist/server/routes/artifact-actions.js +129 -0
  45. package/dist/server/routes/artifact-agent.js +199 -0
  46. package/dist/server/routes/artifact-definitions.js +163 -0
  47. package/dist/server/routes/artifact-transitions.js +80 -0
  48. package/dist/server/routes/artifacts.js +111 -0
  49. package/dist/server/routes/events.js +5 -1
  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 +136 -10
  53. package/dist/server/services/agent/turn-task-context.js +1 -0
  54. package/dist/server/services/artifact-authorization.service.js +199 -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 +541 -0
  58. package/dist/server/services/artifact-graph.service.js +81 -0
  59. package/dist/server/services/artifact-lifecycle.service.js +431 -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 +608 -0
  64. package/dist/server/services/artifact-type.service.js +74 -0
  65. package/dist/server/services/artifact-validation-jobs.service.js +848 -0
  66. package/dist/server/services/device-connections.js +9 -1
  67. package/dist/server/services/review.service.js +6 -5
  68. package/dist/server/services/task.service.js +225 -0
  69. package/dist/server/utils/artifact-actor.js +13 -0
  70. package/dist/server/utils/artifact-page.js +42 -0
  71. package/dist/server/utils/take-within-bytes.js +16 -0
  72. package/dist/server/utils/text.js +7 -0
  73. package/dist/shared/agents/schemas.js +30 -0
  74. package/dist/shared/app/storage-constants.js +2 -0
  75. package/dist/shared/artifact-tools/descriptions.js +17 -0
  76. package/dist/shared/artifact-tools/instructions.js +14 -0
  77. package/dist/shared/artifact-tools/schemas.js +124 -0
  78. package/dist/shared/artifact-transitions/constants.js +21 -0
  79. package/dist/shared/artifact-transitions/schemas.js +111 -0
  80. package/dist/shared/artifact-transitions/transitions.js +61 -0
  81. package/dist/shared/artifact-transitions/types.js +1 -0
  82. package/dist/shared/artifacts/constants.js +34 -0
  83. package/dist/shared/artifacts/mcp-constants.js +24 -0
  84. package/dist/shared/artifacts/normalize.js +9 -0
  85. package/dist/shared/artifacts/schemas.js +874 -0
  86. package/dist/shared/artifacts/search.js +101 -0
  87. package/dist/shared/artifacts/types.js +1 -0
  88. package/dist/shared/events/schemas.js +1 -1
  89. package/dist/shared/events/types.js +1 -0
  90. package/dist/shared/tasks/schemas.js +261 -0
  91. package/dist/shared/tasks/types.js +1 -0
  92. package/dist/shared/utils/artifact-body.js +10 -0
  93. package/dist/shared/utils/canonical-json.js +22 -0
  94. package/dist/shared/utils/is-safe-prefixed-filename.js +10 -0
  95. package/dist/shared/utils/is-uuid.js +4 -2
  96. package/dist/shared/utils/is-valid-artifact-type-id.js +9 -0
  97. package/dist/shared/utils/is-valid-filename-prefix.js +13 -0
  98. package/dist/web/assets/AccessPage-D9xfFUBC.js +1 -0
  99. package/dist/web/assets/AddButton-DKNKmS1i.js +4 -0
  100. package/dist/web/assets/AddToAgentButton-wC1dzbDE.js +1 -0
  101. package/dist/web/assets/AppPage-Cozv2vqG.js +1 -0
  102. package/dist/web/assets/ArtifactsPage-IHUO7SbH.js +2 -0
  103. package/dist/web/assets/ArtifactsPage-nRD9XiRm.css +1 -0
  104. package/dist/web/assets/CancelButton-mkxRHwoZ.js +1 -0
  105. package/dist/web/assets/ClosePopup-Bd5a-Lu_.js +1 -0
  106. package/dist/web/assets/CreateButton-zCmb7snC.js +1 -0
  107. package/dist/web/assets/CreateReviewFabButton-D58IHr36.js +1 -0
  108. package/dist/web/assets/DialogHeader-BVrtb0Ri.js +1 -0
  109. package/dist/web/assets/DialogHeader-CPL9OtDV.css +1 -0
  110. package/dist/web/assets/{ErrorNotFound-DucDzOUJ.js → ErrorNotFound-fXS367lZ.js} +1 -1
  111. package/dist/web/assets/{IndexPage-D5PvyGrX.css → IndexPage-5V5GLtgi.css} +1 -1
  112. package/dist/web/assets/IndexPage-BJ1EQdlT.js +1 -0
  113. package/dist/web/assets/MainLayout-C3Ie5yOo.css +1 -0
  114. package/dist/web/assets/MainLayout-CeogGMHH.js +7 -0
  115. package/dist/web/assets/{NoteVisualEditor-CawpxEFB.css → NoteVisualEditor-DE4ACTRJ.css} +1 -1
  116. package/dist/web/assets/NoteVisualEditor-DqAZZig1.js +1054 -0
  117. package/dist/web/assets/NotesPage-C_eoCUl7.css +1 -0
  118. package/dist/web/assets/NotesPage-DXgI-kwN.js +4 -0
  119. package/dist/web/assets/{PdfPreview-rlkcqoMF.js → PdfPreview-ByeBjQMQ.js} +2 -2
  120. package/dist/web/assets/ProjectFilePage-DV1anYfN.js +1 -0
  121. package/dist/web/assets/QBtn-V3VXtJXi.js +1 -0
  122. package/dist/web/assets/QFab-DJhLqo9i.js +1 -0
  123. package/dist/web/assets/{QForm-Be8-ucuJ.js → QForm-ClpSneeD.js} +1 -1
  124. package/dist/web/assets/QInput-BnqLJGXl.js +1 -0
  125. package/dist/web/assets/QScrollArea-qf2G1s2r.js +1 -0
  126. package/dist/web/assets/QSeparator-B8uLaDiI.js +1 -0
  127. package/dist/web/assets/QSpace-CVp0CPR_.js +1 -0
  128. package/dist/web/assets/QSplitter-K2LjbupZ.js +1 -0
  129. package/dist/web/assets/QTabs-Bl_rIz4_.js +1 -0
  130. package/dist/web/assets/QVirtualScroll-BeEkuL6x.js +1 -0
  131. package/dist/web/assets/ReviewDetailPage-BgxcsTqY.css +1 -0
  132. package/dist/web/assets/ReviewDetailPage-CL4Dmz0L.js +1 -0
  133. package/dist/web/assets/ReviewsPage-BPJFTulG.js +1 -0
  134. package/dist/web/assets/SaveButton-D1q2suzR.js +3 -0
  135. package/dist/web/assets/SaveButton-Vtjvcbt0.css +1 -0
  136. package/dist/web/assets/SearchInput-DapVXrFj.js +1 -0
  137. package/dist/web/assets/StatusSwitcher-BtbSIr5q.js +1 -0
  138. package/dist/web/assets/{StatusSwitcher-D6i8_OfR.css → StatusSwitcher-DaE1ufAA.css} +1 -1
  139. package/dist/web/assets/TagFilterField-CPl-BxtL.js +1 -0
  140. package/dist/web/assets/TagFilterField-DjUH0m3_.css +1 -0
  141. package/dist/web/assets/{TerminalView-D4elkzN4.js → TerminalView-B56cEx8g.js} +12 -12
  142. package/dist/web/assets/{TerminalView-617DZuPt.css → TerminalView-BCeke4xd.css} +1 -1
  143. package/dist/web/assets/{abnfDiagram-O67JEVCF-Cugru96L.js → abnfDiagram-O67JEVCF-D_6l0gCA.js} +1 -1
  144. package/dist/web/assets/{access-store-Cdg8oRAM.js → access-store-CwkOl71l.js} +1 -1
  145. package/dist/web/assets/{agent-store-D_W7iD_B.js → agent-store-Cs-hu1v1.js} +5 -5
  146. package/dist/web/assets/architecture-7GRP2DOG-KT2-_RA5.js +1 -0
  147. package/dist/web/assets/{architectureDiagram-NJMV4G6O-7rMsE4Wt.js → architectureDiagram-NJMV4G6O-BFgjp9nL.js} +1 -1
  148. package/dist/web/assets/{blockDiagram-BEXU5L5S-BvSnwUrI.js → blockDiagram-BEXU5L5S-BWfbJk26.js} +1 -1
  149. package/dist/web/assets/{c4Diagram-YGBWAQC7-CgmQlCVx.js → c4Diagram-YGBWAQC7-oIqgOwQa.js} +1 -1
  150. package/dist/web/assets/channel-CAhty-fl.js +1 -0
  151. package/dist/web/assets/{chunk-3FUC2YCW-_i8udThz.js → chunk-3FUC2YCW-WI2pliXT.js} +1 -1
  152. package/dist/web/assets/{chunk-5DYCD2WN-Mhk2LlLk.js → chunk-5DYCD2WN-DYvaF8o2.js} +1 -1
  153. package/dist/web/assets/{chunk-742MDFTN-CDVcwgEp.js → chunk-742MDFTN-xd7B3x5h.js} +1 -1
  154. package/dist/web/assets/{chunk-7INBJB4K-C2c1fi9s.js → chunk-7INBJB4K-nsyx3RGB.js} +1 -1
  155. package/dist/web/assets/{chunk-7M6MHVWA-BeGkoIBL.js → chunk-7M6MHVWA-BxPQ9AEv.js} +1 -1
  156. package/dist/web/assets/{chunk-7PRAP22T-BVWTh9sT.js → chunk-7PRAP22T-CvHw2y-F.js} +1 -1
  157. package/dist/web/assets/{chunk-GTNCS2PH-gVhX7NZP.js → chunk-GTNCS2PH-DIfN1SOI.js} +1 -1
  158. package/dist/web/assets/{chunk-GWA4HPMP-BPdnIcQH.js → chunk-GWA4HPMP-2WZX1fG-.js} +1 -1
  159. package/dist/web/assets/{chunk-MBY4JIJT-B2VfO_06.js → chunk-MBY4JIJT-BL7akw7q.js} +1 -1
  160. package/dist/web/assets/{chunk-NETBCI7D-DZF_gITO.js → chunk-NETBCI7D-CQX1tY3W.js} +1 -1
  161. package/dist/web/assets/{chunk-O7XYJQB3-CDpBWiLq.js → chunk-O7XYJQB3-CtGXf4J6.js} +1 -1
  162. package/dist/web/assets/{chunk-UA2S7LBM-Iv7TSRE_.js → chunk-UA2S7LBM-C_3M_IIp.js} +1 -1
  163. package/dist/web/assets/{chunk-WEXAMYUT-Ce6goZ2Y.js → chunk-WEXAMYUT-6MO559WJ.js} +1 -1
  164. package/dist/web/assets/{chunk-Z7XXMR3K-CY6E6_Ux.js → chunk-Z7XXMR3K-CHJ55IlF.js} +1 -1
  165. package/dist/web/assets/{chunk-ZIGJFQKS-BLzsXZBc.js → chunk-ZIGJFQKS-533qcfPb.js} +1 -1
  166. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-Dz0kD9P6.js → classDiagram-v2-NBCMYWYE-CFySVswx.js} +1 -1
  167. package/dist/web/assets/copy-code-XoEu_Yh6.js +1 -0
  168. package/dist/web/assets/create-DedXY-KP.js +1 -0
  169. package/dist/web/assets/{cynefin-OW5HDTMX-DWIx3wRm.js → cynefin-OW5HDTMX-Dfg_2VTu.js} +1 -1
  170. package/dist/web/assets/{cynefinDiagram-VND7K2PF-2D80uocv.js → cynefinDiagram-VND7K2PF-DaSXiPph.js} +1 -1
  171. package/dist/web/assets/{dagre-6A5THRUB-BLBhukn-.js → dagre-6A5THRUB-D7d1gbRO.js} +1 -1
  172. package/dist/web/assets/date-snFAXn1W.js +1 -0
  173. package/dist/web/assets/{devices-store-BfZKPOhg.js → devices-store-DB_tWC1Q.js} +1 -1
  174. package/dist/web/assets/{diagram-22UHCM2B-DY8dMLZ3.js → diagram-22UHCM2B-CNuTss_x.js} +1 -1
  175. package/dist/web/assets/{diagram-3UASUU5V-Bf6Od1NH.js → diagram-3UASUU5V--Rt3-ARV.js} +1 -1
  176. package/dist/web/assets/{diagram-ATOU4E4O-CjLGcYD5.js → diagram-ATOU4E4O-BP79L-gP.js} +1 -1
  177. package/dist/web/assets/{diagram-CDSNMT55-gIvaVNvs.js → diagram-CDSNMT55-CRO5cocs.js} +1 -1
  178. package/dist/web/assets/{diagram-MLGK6HIB-VR5kiCi5.js → diagram-MLGK6HIB-Difu0Zmh.js} +1 -1
  179. package/dist/web/assets/{diagram-MPIPVDR6-CdAdj25I.js → diagram-MPIPVDR6-grQ3ie17.js} +1 -1
  180. package/dist/web/assets/diff-store-D4XiTkfi.js +93 -0
  181. package/dist/web/assets/{dist-CBGyhCYv.js → dist-75uxNoTf.js} +1 -1
  182. package/dist/web/assets/{dist-CIMEaJAX.js → dist-9rzCziV2.js} +1 -1
  183. package/dist/web/assets/{dist-CEl-Ko94.js → dist-B9i6cUYu.js} +1 -1
  184. package/dist/web/assets/{dist-yzAhvE-V.js → dist-BU-fZfEv.js} +1 -1
  185. package/dist/web/assets/{dist-CTd7Kuqx.js → dist-BeUB43Ur.js} +1 -1
  186. package/dist/web/assets/{dist-C85t5q3z.js → dist-BxIdk5Al.js} +1 -1
  187. package/dist/web/assets/{dist-joQDFmfy.js → dist-By534Syk.js} +1 -1
  188. package/dist/web/assets/{dist-C54FrSb8.js → dist-DFjLCq-0.js} +1 -1
  189. package/dist/web/assets/{dist-lgw4kcVA.js → dist-DNrP7O-J.js} +1 -1
  190. package/dist/web/assets/{dist-BU8erXQF.js → dist-Du5VPTAq.js} +1 -1
  191. package/dist/web/assets/{dist-DLuI1PxH.js → dist-DyBqP777.js} +1 -1
  192. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-irTAlMFO.js → ebnfDiagram-ZINNZB2B-DX2rcvMK.js} +1 -1
  193. package/dist/web/assets/{elk-276RUBZZ-Dj5ligvC.js → elk-276RUBZZ-DSHIl9hv.js} +1 -1
  194. package/dist/web/assets/{erDiagram-OPXOYQCR-Bvn6rbL1.js → erDiagram-OPXOYQCR-BIZVJO8N.js} +1 -1
  195. package/dist/web/assets/eventmodeling-NTZA5JFV-Bh_xDKx6.js +1 -0
  196. package/dist/web/assets/flowDiagram-KWPJA3E3-DXkIxaXL.js +1 -0
  197. package/dist/web/assets/{ganttDiagram-FUAMR5RP-BNGAUF5H.js → ganttDiagram-FUAMR5RP-D4kPfh4X.js} +1 -1
  198. package/dist/web/assets/{gitGraph-4MIJSDKK-DYSHOl72.js → gitGraph-4MIJSDKK-B41R-oAb.js} +1 -1
  199. package/dist/web/assets/{gitGraphDiagram-X574FWY7-BGwRHrPq.js → gitGraphDiagram-X574FWY7-xXrZiSJI.js} +1 -1
  200. package/dist/web/assets/i18n-Cfw_OfRR.js +1 -0
  201. package/dist/web/assets/icons-BIOTvmdT.js +1 -0
  202. package/dist/web/assets/index-B1Bugudn.js +2 -0
  203. package/dist/web/assets/index-BQU3KK1E.css +1 -0
  204. package/dist/web/assets/{info-A6RAGUB7-DNWR20pW.js → info-A6RAGUB7-CgSmK-La.js} +1 -1
  205. package/dist/web/assets/{infoDiagram-VRGFBTTK-BhNwJLY5.js → infoDiagram-VRGFBTTK-CDNEJ_R-.js} +1 -1
  206. package/dist/web/assets/{init-app-cg-B_TFN.js → init-app-C3tD3faT.js} +1 -1
  207. package/dist/web/assets/{is-D0waCELe.js → is-B1kBQ5Va.js} +1 -1
  208. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-CSkDpn64.js → ishikawaDiagram-OU5B5YK6-ZcU1IxFn.js} +1 -1
  209. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-D2CEecC3.js → journeyDiagram-ZHPQQLJL-g-8ZRhJj.js} +1 -1
  210. package/dist/web/assets/{kanban-definition-PNTS6WVX-CQHlo4Hh.js → kanban-definition-PNTS6WVX-nfsfxVOE.js} +1 -1
  211. package/dist/web/assets/{line-C-izH1ks.js → line-BpuX_ban.js} +1 -1
  212. package/dist/web/assets/markdown-ogvtoQzt.js +16 -0
  213. package/dist/web/assets/mdi-Du7sIqtH.js +1 -0
  214. package/dist/web/assets/{mermaid-parser.core-CAGwq6D-.js → mermaid-parser.core-BBj_ep7Z.js} +3 -3
  215. package/dist/web/assets/{mermaid.core--xlI_0tK.js → mermaid.core-DP4USVSM.js} +4 -4
  216. package/dist/web/assets/{mindmap-definition-NLK3R4M7-BuCxhlwy.js → mindmap-definition-NLK3R4M7-C5GhUvi3.js} +1 -1
  217. package/dist/web/assets/{packet-AYTQ26CC-Bi7x5lJK.js → packet-AYTQ26CC-BIdb-vfg.js} +1 -1
  218. package/dist/web/assets/{pegDiagram-GJSIUBJH-qWzBPkCQ.js → pegDiagram-GJSIUBJH-DH-SdKj6.js} +1 -1
  219. package/dist/web/assets/{pie-WAS4IAKB-Di0wC4XB.js → pie-WAS4IAKB-Du3-jRjK.js} +1 -1
  220. package/dist/web/assets/{pieDiagram-5QR66LMP-BCDVY9q2.js → pieDiagram-5QR66LMP-CJh2ngqY.js} +1 -1
  221. package/dist/web/assets/pinia-BfqEuvsi.js +1 -0
  222. package/dist/web/assets/pinia-mdReENsc.js +1 -0
  223. package/dist/web/assets/preview-store-DOnSmm_C.js +2 -0
  224. package/dist/web/assets/{project-files-store-byqyG2ts.js → project-files-store-CHP6RYFI.js} +1 -1
  225. package/dist/web/assets/{quadrantDiagram-O4NWA36T-CpFObUjT.js → quadrantDiagram-O4NWA36T-DYsSScxc.js} +1 -1
  226. package/dist/web/assets/{radar-RG4KPBEZ-Br8yH8rq.js → radar-RG4KPBEZ-BBjvaVVB.js} +1 -1
  227. package/dist/web/assets/{railroad-74A4TZTK-CACSqhA-.js → railroad-74A4TZTK-B-CRncBU.js} +1 -1
  228. package/dist/web/assets/railroad-abnf-HS5TGJTU-D37zpoYN.js +1 -0
  229. package/dist/web/assets/railroad-ebnf-LZEXJU2U-ZnMxH6Ym.js +1 -0
  230. package/dist/web/assets/railroad-peg-WCYAUIDC-Dg3g-VsM.js +1 -0
  231. package/dist/web/assets/{railroadDiagram-XR7U4H2S-sYXRKGK1.js → railroadDiagram-XR7U4H2S-hqp-2UHb.js} +1 -1
  232. package/dist/web/assets/{render-C7fu7uYY.js → render-Brao9Nj2.js} +1 -1
  233. package/dist/web/assets/repository-store-DZgQ5H7J.js +1 -0
  234. package/dist/web/assets/{requirementDiagram-PLB6GJNP-C9I1JbQc.js → requirementDiagram-PLB6GJNP-Dhuw5jcQ.js} +1 -1
  235. package/dist/web/assets/runtime-core.esm-bundler-DDnHUoIy.js +1 -0
  236. package/dist/web/assets/{safe-storage-C76bPypO.js → safe-storage-BchoYSbq.js} +1 -1
  237. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-CuvFQhPh.js → sankeyDiagram-IPEJSGJF-D5rCIxGR.js} +1 -1
  238. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-CaVb9Y6e.js → sequenceDiagram-PO4LG4MO-YEHR4Mp-.js} +1 -1
  239. package/dist/web/assets/single-flight-CyDZB3rb.js +1 -0
  240. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-Dp_APryr.js → stateDiagram-v2-GCMORJYK-9iWmz6Xf.js} +1 -1
  241. package/dist/web/assets/{swimlanes-2SLR337P-FclUI-k0.js → swimlanes-2SLR337P-Cjwvgt_n.js} +1 -1
  242. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-gJwxhWoO.js +8 -0
  243. package/dist/web/assets/{terminal-store-BnEkXuBj.js → terminal-store-i7HHlWzR.js} +1 -1
  244. package/dist/web/assets/theme-CQ9InaWa.js +1 -0
  245. package/dist/web/assets/{timeline-definition-EJHVYXUP-BAqwqj3c.js → timeline-definition-EJHVYXUP-Cd5n2-Py.js} +1 -1
  246. package/dist/web/assets/{treeView-Q6P3EWNA-D87jJbUQ.js → treeView-Q6P3EWNA-C_QH9vtR.js} +1 -1
  247. package/dist/web/assets/{treemap-WGGIJYW6-B6nhGhRc.js → treemap-WGGIJYW6-CVnU7TVz.js} +1 -1
  248. package/dist/web/assets/use-add-agent-context-D5VqMr0p.js +1 -0
  249. package/dist/web/assets/use-app-theme-DgC4ivPF.js +1 -0
  250. package/dist/web/assets/use-countdown-D6_qSOZJ.js +1 -0
  251. package/dist/web/assets/use-notify-error-Dx2D9IEP.js +1 -0
  252. package/dist/web/assets/use-router-link-D7geCGQI.js +1 -0
  253. package/dist/web/assets/use-row-height-BgHg4-J-.js +1 -0
  254. package/dist/web/assets/use-server-events-CvcbsbIf.js +1 -0
  255. package/dist/web/assets/use-virtual-scroll-DixIsJk4.js +1 -0
  256. package/dist/web/assets/{use-workspace-navigation-CnebLvBV.js → use-workspace-navigation-Dq-Aw8Xq.js} +1 -1
  257. package/dist/web/assets/{usecaseDiagram-POWQR4AR-7YcIKxAb.js → usecaseDiagram-POWQR4AR-Cbj2Pvab.js} +1 -1
  258. package/dist/web/assets/{vennDiagram-UO4OBE2U-BZq5cFC4.js → vennDiagram-UO4OBE2U-2j79YqCN.js} +1 -1
  259. package/dist/web/assets/viewport-store-NqQS7itT.js +1 -0
  260. package/dist/web/assets/{vue-i18n.runtime-CEtihHHL.js → vue-i18n.runtime-JOo8b7-t.js} +2 -2
  261. package/dist/web/assets/{wardley-WFR3VGLG-BT8YO9H4.js → wardley-WFR3VGLG-wsMK7zm3.js} +1 -1
  262. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-rDpXFf6D.js → wardleyDiagram-VNRHLVJA-D6PKTQIJ.js} +1 -1
  263. package/dist/web/assets/weighted-lru-BFtZKA_y.js +1 -0
  264. package/dist/web/assets/workspace-navigation-CoXKJJpP.js +1 -0
  265. package/dist/web/assets/{xychartDiagram-PMCCYNJV-ClniUuZc.js → xychartDiagram-PMCCYNJV-D1fQN7KG.js} +1 -1
  266. package/dist/web/index.html +10 -9
  267. package/package.json +5 -3
  268. package/dist/web/assets/AccessPage-CX3w30zw.js +0 -1
  269. package/dist/web/assets/AppPage-PFJG281y.js +0 -1
  270. package/dist/web/assets/CreateButton-Ch0b7juj.js +0 -1
  271. package/dist/web/assets/CreateReviewFabButton-DTuJzw3q.js +0 -1
  272. package/dist/web/assets/IndexPage-tgW07Qvs.js +0 -1
  273. package/dist/web/assets/MainLayout-C6yBhQnf.css +0 -1
  274. package/dist/web/assets/MainLayout-Cw4jpB1x.js +0 -8
  275. package/dist/web/assets/NoteVisualEditor-DiGhLhQg.js +0 -1054
  276. package/dist/web/assets/NotesPage-CVyVr9tl.js +0 -4
  277. package/dist/web/assets/NotesPage-CxGnZuVQ.css +0 -1
  278. package/dist/web/assets/PanelResize-B6FP9ZVB.js +0 -1
  279. package/dist/web/assets/ProjectFilePage-CkxfX9b1.js +0 -1
  280. package/dist/web/assets/QBtn-CyyV1Bx_.js +0 -1
  281. package/dist/web/assets/QExpansionItem-Dp9lbdVb.js +0 -4
  282. package/dist/web/assets/QFab-LOmZ9rev.js +0 -1
  283. package/dist/web/assets/QInput-Dq6131uz.js +0 -1
  284. package/dist/web/assets/QSelect-B34OqtIH.js +0 -1
  285. package/dist/web/assets/QSpace-BhOVGpQh.js +0 -1
  286. package/dist/web/assets/QVirtualScroll-BYgJ1Kh2.js +0 -1
  287. package/dist/web/assets/ReviewDetailPage-CkCilMNW.css +0 -1
  288. package/dist/web/assets/ReviewDetailPage-GNjBa4X0.js +0 -1
  289. package/dist/web/assets/ReviewsPage-An5fRS92.js +0 -1
  290. package/dist/web/assets/StatusSwitcher-DrFHHwGm.js +0 -1
  291. package/dist/web/assets/architecture-7GRP2DOG-BPCNj4lR.js +0 -1
  292. package/dist/web/assets/channel-CzRYxuLe.js +0 -1
  293. package/dist/web/assets/create-B9Tn5Xvi.js +0 -1
  294. package/dist/web/assets/diff-store-B4adGxC2.js +0 -93
  295. package/dist/web/assets/eventmodeling-NTZA5JFV-BJsTZ_nG.js +0 -1
  296. package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff +0 -0
  297. package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ-D-x-0Q06.woff2 +0 -0
  298. package/dist/web/assets/flowDiagram-KWPJA3E3-CvXh1Df7.js +0 -1
  299. package/dist/web/assets/i18n-DnlAcIbT.js +0 -1
  300. package/dist/web/assets/index-Cw7GJi3K.js +0 -2
  301. package/dist/web/assets/index-wsOPzloa.css +0 -1
  302. package/dist/web/assets/markdown-CvtLTiBZ.js +0 -16
  303. package/dist/web/assets/pinia-DO0AdKGg.js +0 -1
  304. package/dist/web/assets/pinia-uOZCmBi-.js +0 -1
  305. package/dist/web/assets/preview-store-DyO8G-ox.js +0 -1
  306. package/dist/web/assets/railroad-abnf-HS5TGJTU-DTBsRB1c.js +0 -1
  307. package/dist/web/assets/railroad-ebnf-LZEXJU2U-D3mrY4ar.js +0 -1
  308. package/dist/web/assets/railroad-peg-WCYAUIDC-33ZjWX0u.js +0 -1
  309. package/dist/web/assets/repository-store-DtRDth1L.js +0 -1
  310. package/dist/web/assets/runtime-core.esm-bundler-CAvPiMpd.js +0 -1
  311. package/dist/web/assets/single-flight-fPyIy7h5.js +0 -1
  312. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-CuUvsn0e.js +0 -8
  313. package/dist/web/assets/theme-CnbaLamF.js +0 -1
  314. package/dist/web/assets/use-add-agent-context-xHAjjOf1.js +0 -1
  315. package/dist/web/assets/use-app-theme-DEowpWRR.js +0 -1
  316. package/dist/web/assets/use-countdown-DlcwwJrs.js +0 -1
  317. package/dist/web/assets/use-panel-width-CjSWCFr4.js +0 -1
  318. package/dist/web/assets/use-router-link-CUoYaYUw.js +0 -1
  319. package/dist/web/assets/use-row-height-BsE9-rO2.js +0 -1
  320. package/dist/web/assets/use-server-events-C2X4Xz00.js +0 -1
  321. package/dist/web/assets/use-virtual-scroll-BAZGswHv.js +0 -1
  322. package/dist/web/assets/viewport-store-CCHiY2QG.js +0 -1
  323. package/dist/web/assets/weighted-lru-BGovtJ2a.js +0 -1
  324. package/dist/web/assets/workspace-navigation-DdW0rSGz.js +0 -1
  325. /package/dist/web/assets/{PanelResize-XjrbOj2S.css → AddToAgentButton-XjrbOj2S.css} +0 -0
@@ -1,3 +1,8 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { ARTIFACT_MCP_SERVER_NAME, ARTIFACT_AGENT_BEARER_PATTERN, ARTIFACT_AGENT_KEY_BYTES, ARTIFACT_AGENT_KEY_ENV, ARTIFACT_AGENT_URL_ENV } from "../../../shared/artifacts/mcp-constants.js";
3
+ import { fileURLToPath } from 'node:url';
4
+ import { sha256Hex } from "../../utils/hash.js";
5
+ import { ForbiddenError } from "../../errors/http.js";
1
6
  import { UnknownAgentPresetError, TooManyAgentSessionsError } from "../../errors/agents.js";
2
7
  import { MAX_AGENT_SESSIONS } from "../../../shared/agents/constants.js";
3
8
  import { uniqueChatName } from "../../../shared/agents/chat-names.js";
@@ -6,17 +11,78 @@ import { describePreset } from "../../config/agents.js";
6
11
  export function createSessionRegistry(options) {
7
12
  const sessions = new Map();
8
13
  const unsubscribeSession = new Map();
14
+ // Finished sessions kept open for inspection; a new session may replace them.
15
+ const retired = new Set();
9
16
  const listeners = new Set();
17
+ const keys = new Map();
18
+ const sessionKeys = new Map();
19
+ const deviceReleases = new Map();
20
+ const requests = new Map();
10
21
  let lastEventId = 0;
22
+ function revoke(id) {
23
+ const key = sessionKeys.get(id);
24
+ if (key)
25
+ keys.delete(key);
26
+ sessionKeys.delete(id);
27
+ deviceReleases.get(id)?.();
28
+ deviceReleases.delete(id);
29
+ for (const close of requests.get(id) ?? [])
30
+ close();
31
+ requests.delete(id);
32
+ }
33
+ function mcpServers(id) {
34
+ const url = options.artifactUrl?.();
35
+ if (!url)
36
+ return [];
37
+ if (sessions.get(id)?.status === 'closed') {
38
+ throw new ForbiddenError('Agent session is closed.');
39
+ }
40
+ // One live key per session: a second call replaces the first.
41
+ const previous = sessionKeys.get(id);
42
+ if (previous)
43
+ keys.delete(previous);
44
+ const key = randomBytes(ARTIFACT_AGENT_KEY_BYTES).toString('hex');
45
+ const hash = sha256Hex(key);
46
+ keys.set(hash, id);
47
+ sessionKeys.set(id, hash);
48
+ const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'js';
49
+ return [
50
+ {
51
+ name: ARTIFACT_MCP_SERVER_NAME,
52
+ command: process.execPath,
53
+ args: [
54
+ fileURLToPath(new URL(`../../../cli/index.${extension}`, import.meta.url)),
55
+ 'mcp',
56
+ '--stdio'
57
+ ],
58
+ env: [
59
+ { name: ARTIFACT_AGENT_URL_ENV, value: url },
60
+ { name: ARTIFACT_AGENT_KEY_ENV, value: key }
61
+ ]
62
+ }
63
+ ];
64
+ }
11
65
  function broadcast(message) {
12
66
  for (const listener of listeners) {
13
67
  listener(message);
14
68
  }
15
69
  }
16
70
  function drop(id) {
71
+ revoke(id);
17
72
  unsubscribeSession.get(id)?.();
18
73
  unsubscribeSession.delete(id);
19
74
  sessions.delete(id);
75
+ retired.delete(id);
76
+ }
77
+ function closeSession(id) {
78
+ const session = sessions.get(id);
79
+ if (!session) {
80
+ return false;
81
+ }
82
+ session.close();
83
+ drop(id);
84
+ broadcast({ type: 'sessions' });
85
+ return true;
20
86
  }
21
87
  function closeAll() {
22
88
  for (const session of sessions.values()) {
@@ -27,21 +93,67 @@ export function createSessionRegistry(options) {
27
93
  }
28
94
  }
29
95
  return {
96
+ authenticateArtifactKey(authorization) {
97
+ options.connections?.recheck();
98
+ if (!authorization ||
99
+ !ARTIFACT_AGENT_BEARER_PATTERN.test(authorization)) {
100
+ return undefined;
101
+ }
102
+ const id = keys.get(sha256Hex(authorization.slice(7)));
103
+ if (!id || sessions.get(id)?.status === 'closed')
104
+ return undefined;
105
+ return id;
106
+ },
107
+ trackArtifactRequest(id, close) {
108
+ let pending = requests.get(id);
109
+ if (!pending) {
110
+ pending = new Set();
111
+ requests.set(id, pending);
112
+ }
113
+ pending.add(close);
114
+ let released = false;
115
+ // A response fires both finish and close, so the second call must do nothing.
116
+ return () => {
117
+ if (released)
118
+ return;
119
+ released = true;
120
+ pending.delete(close);
121
+ if (pending.size === 0 && requests.get(id) === pending) {
122
+ requests.delete(id);
123
+ }
124
+ };
125
+ },
126
+ notifyTasksChanged: () => broadcast({ type: 'sessions' }),
127
+ notifyArtifactActionsChanged: () => broadcast({ type: 'artifact-actions' }),
30
128
  listPresets: () => options.presets.map(describePreset),
31
- create(presetId, reviewId, name) {
129
+ create(presetId, reviewId, name, deviceId, permissionPolicy, kind) {
32
130
  const preset = options.presets.find(p => p.id === presetId);
33
131
  if (!preset) {
34
132
  throw new UnknownAgentPresetError(`Unknown agent "${presetId}"`);
35
133
  }
134
+ if (sessions.size >= MAX_AGENT_SESSIONS) {
135
+ // A Set keeps insertion order, so the first retired session is the oldest.
136
+ const [oldest] = retired;
137
+ if (oldest !== undefined) {
138
+ closeSession(oldest);
139
+ }
140
+ }
36
141
  if (sessions.size >= MAX_AGENT_SESSIONS) {
37
142
  throw new TooManyAgentSessionsError(`At most ${MAX_AGENT_SESSIONS} chats can be open at once`);
38
143
  }
39
144
  const session = new AgentSession({
40
145
  preset,
146
+ permissionPolicy,
147
+ mcpServers,
41
148
  // Open chats are told apart by name: a taken one gets "(1)", "(2)", …
42
149
  name: uniqueChatName(name?.trim() || preset.title, [...sessions.values()].map(s => s.name)),
43
150
  cwd: options.repositoryPath,
44
151
  reviewId,
152
+ ...(kind === undefined ? {} : { kind }),
153
+ ...(options.taskIds ? { taskIds: options.taskIds } : {}),
154
+ ...(options.taskAssignment
155
+ ? { taskAssignment: options.taskAssignment }
156
+ : {}),
45
157
  nextEventId: () => ++lastEventId,
46
158
  ...(options.bufferLimit === undefined
47
159
  ? {}
@@ -49,26 +161,40 @@ export function createSessionRegistry(options) {
49
161
  });
50
162
  sessions.set(session.id, session);
51
163
  unsubscribeSession.set(session.id, session.onEvent(envelope => {
164
+ if (envelope.event.type === 'status' &&
165
+ envelope.event.status === 'closed') {
166
+ revoke(session.id);
167
+ }
52
168
  broadcast({
53
169
  type: 'event',
54
170
  envelope: { ...envelope, sessionId: session.id }
55
171
  });
56
172
  }));
173
+ if (deviceId !== undefined) {
174
+ const release = options.connections?.trackDevice(deviceId, () => session.close());
175
+ if (!release) {
176
+ session.close();
177
+ return session;
178
+ }
179
+ deviceReleases.set(session.id, release);
180
+ }
57
181
  broadcast({ type: 'sessions' });
58
182
  void session.start();
59
183
  return session;
60
184
  },
61
185
  get: id => sessions.get(id),
62
- list: () => [...sessions.values()].map(s => s.info()),
63
- close(id) {
64
- const session = sessions.get(id);
65
- if (!session) {
66
- return false;
186
+ list: () => [...sessions.values()].map(session => ({
187
+ ...session.info(),
188
+ ...(retired.has(session.id) ? { retired: true } : {})
189
+ })),
190
+ close: closeSession,
191
+ retire(id) {
192
+ if (sessions.has(id)) {
193
+ retired.add(id);
194
+ // A retired validator stays open for inspection, but its key must stop working.
195
+ revoke(id);
196
+ broadcast({ type: 'sessions' });
67
197
  }
68
- session.close();
69
- drop(id);
70
- broadcast({ type: 'sessions' });
71
- return true;
72
198
  },
73
199
  closeAll,
74
200
  subscribe(listener, afterId = 0) {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,199 @@
1
+ import { Value } from '@sinclair/typebox/value';
2
+ import { ArtifactAuthorizationApproveBody } from "../../shared/artifacts/schemas.js";
3
+ import { randomUUID } from 'node:crypto';
4
+ import { canonicalJson } from "../../shared/utils/canonical-json.js";
5
+ import { sha256Hex } from "../utils/hash.js";
6
+ import { artifactUserActor } from "../utils/artifact-actor.js";
7
+ import { ForbiddenError } from "../errors/http.js";
8
+ import { ArtifactConflictError, ArtifactNotFoundError } from "../errors/artifacts.js";
9
+ // Grants expire with the confirmed proposal and never survive this runtime.
10
+ const AUTHORIZATION_TTL_MS = 15 * 60_000;
11
+ export class ArtifactAuthorizationService {
12
+ pending = new Map();
13
+ grants = new Map();
14
+ repository;
15
+ now;
16
+ check;
17
+ live;
18
+ changed;
19
+ constructor(repository, now, check, live, changed) {
20
+ this.repository = repository;
21
+ this.now = now;
22
+ this.check = check;
23
+ this.live = live;
24
+ this.changed = changed;
25
+ }
26
+ request(action, sessionId) {
27
+ if (!this.live(sessionId)) {
28
+ throw new ForbiddenError('Action session is unavailable.');
29
+ }
30
+ for (const [id, value] of this.pending) {
31
+ if (value.expiresAt <= this.now() || !this.live(value.sessionId)) {
32
+ this.pending.delete(id);
33
+ }
34
+ }
35
+ for (const [id, value] of this.grants) {
36
+ if (value.expiresAt <= this.now() ||
37
+ !this.live(value.sessionId) ||
38
+ this.repository.consumed(id)) {
39
+ this.grants.delete(id);
40
+ }
41
+ }
42
+ if (this.pending.size >= 100 || this.grants.size >= 1000) {
43
+ throw new ArtifactConflictError('Too many pending actions or authorizations.');
44
+ }
45
+ const binding = this.check(action);
46
+ const value = {
47
+ id: randomUUID(),
48
+ action: structuredClone(action),
49
+ sessionId,
50
+ ...binding,
51
+ expiresAt: this.now() + AUTHORIZATION_TTL_MS
52
+ };
53
+ this.pending.set(value.id, value);
54
+ this.changed();
55
+ return structuredClone(value);
56
+ }
57
+ list() {
58
+ return [...this.pending.values()]
59
+ .filter(value => value.expiresAt > this.now() && this.live(value.sessionId))
60
+ .map(value => structuredClone(value));
61
+ }
62
+ approve(id, request) {
63
+ if (request.method !== 'POST' ||
64
+ request.routeOptions.url !== '/api/artifact-actions/:id/approve') {
65
+ throw new ForbiddenError('Structured user confirmation is required.');
66
+ }
67
+ const user = artifactUserActor(request);
68
+ if (!Value.Check(ArtifactAuthorizationApproveBody, request.body)) {
69
+ throw new ForbiddenError('Structured confirmation is invalid.');
70
+ }
71
+ const sourceUserRequestId = request.body.sourceUserRequestId;
72
+ const pending = this.pending.get(id);
73
+ if (!pending) {
74
+ const previous = [...this.grants.values()].find(value => value.pendingActionId === id &&
75
+ value.sourceUserRequestId === sourceUserRequestId &&
76
+ canonicalJson(value.user) === canonicalJson(user));
77
+ if (!previous)
78
+ throw new ArtifactNotFoundError();
79
+ this.assert(previous.id, previous.sessionId, previous.action);
80
+ return { authorizationId: previous.id, expiresAt: previous.expiresAt };
81
+ }
82
+ if (pending.expiresAt <= this.now() || !this.live(pending.sessionId)) {
83
+ throw new ForbiddenError('Pending action expired or was revoked.');
84
+ }
85
+ const current = this.check(pending.action);
86
+ if (canonicalJson(current) !==
87
+ canonicalJson({
88
+ actionDigest: pending.actionDigest,
89
+ manifestDigest: pending.manifestDigest
90
+ })) {
91
+ throw new ArtifactConflictError('Pending action is stale.');
92
+ }
93
+ const grant = {
94
+ ...pending,
95
+ pendingActionId: id,
96
+ id: randomUUID(),
97
+ user,
98
+ sourceUserRequestId
99
+ };
100
+ this.repository.appendAuthorization(grant.id, grant);
101
+ this.grants.set(grant.id, grant);
102
+ this.pending.delete(id);
103
+ this.changed();
104
+ return { authorizationId: grant.id, expiresAt: grant.expiresAt };
105
+ }
106
+ assert(id, sessionId, action) {
107
+ const grant = this.grants.get(id);
108
+ if (!grant ||
109
+ grant.sessionId !== sessionId ||
110
+ grant.expiresAt <= this.now() ||
111
+ !this.live(sessionId) ||
112
+ this.repository.consumed(id)) {
113
+ throw new ForbiddenError('Authorization is missing, expired, consumed or belongs to another session.');
114
+ }
115
+ const current = this.check(action);
116
+ if (canonicalJson(action) !== canonicalJson(grant.action) ||
117
+ current.actionDigest !== grant.actionDigest ||
118
+ current.manifestDigest !== grant.manifestDigest) {
119
+ throw new ArtifactConflictError('Authorization is stale or has the wrong scope.');
120
+ }
121
+ return grant;
122
+ }
123
+ describe(id) {
124
+ const pending = this.pending.get(id);
125
+ const grant = pending
126
+ ? undefined
127
+ : [...this.grants.values()].find(value => value.pendingActionId === id);
128
+ const value = pending ?? grant;
129
+ if (!value ||
130
+ value.expiresAt <= this.now() ||
131
+ !this.live(value.sessionId)) {
132
+ throw new ArtifactNotFoundError();
133
+ }
134
+ return {
135
+ pending: grant ? { ...grant, id: grant.pendingActionId } : value,
136
+ phase: grant
137
+ ? this.repository.consumed(grant.id)
138
+ ? 'consumed'
139
+ : 'authorized'
140
+ : 'pending',
141
+ authorizationId: grant?.id ?? null
142
+ };
143
+ }
144
+ status(id, sessionId) {
145
+ const known = this.pending.get(id) ??
146
+ [...this.grants.values()].find(value => value.pendingActionId === id);
147
+ // The requester learns how its request ended, not only that it is gone.
148
+ if (known?.sessionId === sessionId && known.expiresAt <= this.now()) {
149
+ return {
150
+ id,
151
+ state: 'expired',
152
+ authorizationId: null,
153
+ expiresAt: known.expiresAt
154
+ };
155
+ }
156
+ const revoked = known ? null : this.repository.revoked(id);
157
+ if (revoked?.sessionId === sessionId) {
158
+ return {
159
+ id,
160
+ state: 'revoked',
161
+ authorizationId: null,
162
+ expiresAt: revoked.expiresAt ?? this.now()
163
+ };
164
+ }
165
+ const value = this.describe(id);
166
+ if (value.pending.sessionId !== sessionId)
167
+ throw new ArtifactNotFoundError();
168
+ return {
169
+ id,
170
+ state: value.phase,
171
+ authorizationId: value.authorizationId,
172
+ expiresAt: value.pending.expiresAt
173
+ };
174
+ }
175
+ consume(id) {
176
+ this.repository.authorizationEvent(id, 'consumed', { at: this.now() });
177
+ }
178
+ revoke(id) {
179
+ const grant = [...this.grants.values()].find(value => value.pendingActionId === id);
180
+ if (grant) {
181
+ this.grants.delete(grant.id);
182
+ this.repository.authorizationEvent(grant.id, 'revoked', {
183
+ at: this.now()
184
+ });
185
+ }
186
+ const target = this.pending.get(id) ?? grant;
187
+ this.grants.delete(id);
188
+ this.pending.delete(id);
189
+ this.repository.authorizationEvent(id, 'revoked', {
190
+ at: this.now(),
191
+ sessionId: target?.sessionId,
192
+ expiresAt: target?.expiresAt
193
+ });
194
+ this.changed();
195
+ }
196
+ }
197
+ export function artifactActionDigest(action) {
198
+ return sha256Hex(canonicalJson(action));
199
+ }
@@ -0,0 +1,96 @@
1
+ import { Value } from '@sinclair/typebox/value';
2
+ import { ArtifactActorSchema } from "../../shared/artifacts/schemas.js";
3
+ import { canonicalJson } from "../../shared/utils/canonical-json.js";
4
+ import { ArtifactConflictError, ArtifactInputError, ArtifactNotFoundError } from "../errors/artifacts.js";
5
+ import { sha256Hex } from "../utils/hash.js";
6
+ import { artifactPage } from "../utils/artifact-page.js";
7
+ export function assertDefinitionInput(schema, value) {
8
+ if (!Value.Check(schema, value)) {
9
+ throw new ArtifactInputError('Invalid definition input.');
10
+ }
11
+ }
12
+ export class ArtifactDefinitionService {
13
+ options;
14
+ kind;
15
+ constructor(options, kind) {
16
+ this.options = options;
17
+ this.kind = kind;
18
+ }
19
+ get(id) {
20
+ const result = this.options.repository.get(this.kind, id);
21
+ if (!result)
22
+ throw new ArtifactNotFoundError();
23
+ return result;
24
+ }
25
+ list(limit = 50, cursor, search = '') {
26
+ const needle = search.trim().toLocaleLowerCase();
27
+ return this.options.catalog.readSnapshot(() => artifactPage(this.matching(needle), {
28
+ storeId: this.options.catalog.storeId,
29
+ epoch: this.options.catalog.epoch(),
30
+ // The cursor carries the search text, so a cursor cannot cross searches.
31
+ query: needle ? `${this.kind}:search:${needle}` : this.kind,
32
+ limit,
33
+ cursor
34
+ }));
35
+ }
36
+ matching(needle) {
37
+ const items = this.options.repository.all(this.kind);
38
+ if (!needle)
39
+ return items;
40
+ return items.filter(item => {
41
+ const name = 'title' in item ? item.title : item.label;
42
+ return `${name} ${item.id}`.toLocaleLowerCase().includes(needle);
43
+ });
44
+ }
45
+ history(id) {
46
+ this.get(id);
47
+ return this.options.repository.history(this.kind, id);
48
+ }
49
+ historyPage(id, limit = 50, cursor) {
50
+ return this.options.catalog.readSnapshot(() => artifactPage(this.history(id), {
51
+ storeId: this.options.catalog.storeId,
52
+ epoch: this.options.catalog.epoch(),
53
+ query: `${this.kind}:history:${id}`,
54
+ limit,
55
+ cursor
56
+ }));
57
+ }
58
+ mutate(requestId, payload, actor, run) {
59
+ return this.command(requestId, payload, actor, () => {
60
+ const result = run();
61
+ this.options.repository.append(this.kind, result, requestId, actor, new Date((this.options.now ?? Date.now)()).toISOString());
62
+ this.options.catalog.recordChange(this.kind, result.id, requestId);
63
+ return result;
64
+ });
65
+ }
66
+ command(requestId, payload, actor, run) {
67
+ const command = this.commandWithReplay(requestId, payload, actor, run);
68
+ this.options.onChange();
69
+ return command.result;
70
+ }
71
+ commandWithReplay(requestId, payload, actor, run) {
72
+ if (typeof requestId !== 'string' ||
73
+ !requestId.length ||
74
+ requestId.length > 128) {
75
+ throw new ArtifactInputError('Invalid request identifier.');
76
+ }
77
+ assertDefinitionInput(ArtifactActorSchema, actor);
78
+ const hash = sha256Hex(canonicalJson({ kind: this.kind, payload, actor }));
79
+ return this.options.catalog.transaction(() => {
80
+ const replay = this.options.repository.replay(requestId, hash);
81
+ if (replay)
82
+ return { result: replay.result, replayed: true };
83
+ const created = run();
84
+ this.options.repository.record(requestId, hash, created);
85
+ return { result: created, replayed: false };
86
+ });
87
+ }
88
+ assertVersion(actual, expected) {
89
+ if (!Number.isSafeInteger(expected) || expected < 1) {
90
+ throw new ArtifactInputError('Invalid expected version.');
91
+ }
92
+ if (actual !== expected) {
93
+ throw new ArtifactConflictError('Definition version changed.');
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,44 @@
1
+ import { Readable } from 'node:stream';
2
+ // Bound allocation independently of the total number of metadata versions.
3
+ const HISTORY_PAGE_SIZE = 50;
4
+ export class ArtifactExportService {
5
+ storage;
6
+ constructor(storage) {
7
+ this.storage = storage;
8
+ }
9
+ original(id) {
10
+ return this.storage.catalog.readSnapshot(() => this.storage.readVerifiedFile(id));
11
+ }
12
+ metadataHistory(id) {
13
+ const storage = this.storage;
14
+ const snapshot = storage.catalog.readSnapshot(() => {
15
+ const snapshotSeq = storage.catalog.eventSequence();
16
+ storage.catalog.assertExportHistory(id, snapshotSeq);
17
+ return {
18
+ formatVersion: 1,
19
+ storeId: storage.storeId,
20
+ artifactId: id,
21
+ snapshotSeq,
22
+ storeEpoch: storage.catalog.epoch(),
23
+ artifact: storage.get(id)
24
+ };
25
+ });
26
+ async function* chunks() {
27
+ yield JSON.stringify(snapshot).slice(0, -1) + ',"history":[';
28
+ let afterSeq = 0;
29
+ let first = true;
30
+ for (;;) {
31
+ const page = storage.catalog.exportHistory(id, afterSeq, snapshot.snapshotSeq, HISTORY_PAGE_SIZE);
32
+ if (!page.length)
33
+ break;
34
+ for (const event of page) {
35
+ yield (first ? '' : ',') + JSON.stringify(event);
36
+ first = false;
37
+ afterSeq = event.seq;
38
+ }
39
+ }
40
+ yield ']}';
41
+ }
42
+ return Readable.from(chunks(), { objectMode: false });
43
+ }
44
+ }