@tequila99/githuman 0.7.0 → 0.9.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 (392) hide show
  1. package/README.md +75 -0
  2. package/dist/cli/config.js +13 -1
  3. package/dist/cli/server-runtime.js +3 -0
  4. package/dist/server/app.js +37 -4
  5. package/dist/server/config/agents.js +2 -1
  6. package/dist/server/config/openapi.js +4 -0
  7. package/dist/server/db/migrations.js +61 -0
  8. package/dist/server/errors/note-assets.js +3 -0
  9. package/dist/server/errors/notes.js +3 -0
  10. package/dist/server/hooks/notes/same-origin.js +37 -0
  11. package/dist/server/hooks/terminal/local-origin.js +2 -1
  12. package/dist/server/repositories/notes-catalog.repo.js +129 -0
  13. package/dist/server/repositories/notes.repo.js +444 -0
  14. package/dist/server/routes/agent.js +1 -0
  15. package/dist/server/routes/notes.js +213 -0
  16. package/dist/server/routes/terminal.js +2 -1
  17. package/dist/server/services/agent/context-builder.js +13 -0
  18. package/dist/server/services/git.service.js +15 -11
  19. package/dist/server/services/notes-external-assets.service.js +177 -0
  20. package/dist/server/services/notes-identity.service.js +811 -0
  21. package/dist/server/services/notes-operations.service.js +296 -0
  22. package/dist/server/services/notes-watcher.service.js +44 -0
  23. package/dist/server/services/notes-writer.service.js +116 -0
  24. package/dist/server/services/notes.service.js +150 -0
  25. package/dist/server/services/terminal/backend.js +6 -3
  26. package/dist/server/services/terminal/channel.js +6 -3
  27. package/dist/server/services/terminal/registry.js +10 -5
  28. package/dist/server/services/terminal/session.js +10 -5
  29. package/dist/server/services/terminal/tokens.js +4 -2
  30. package/dist/server/utils/hash.js +4 -0
  31. package/dist/server/utils/note-asset-format.js +19 -0
  32. package/dist/server/utils/remote-asset-request.js +193 -0
  33. package/dist/server/utils/request-abort.js +20 -0
  34. package/dist/server/utils/system-error.js +14 -0
  35. package/dist/shared/agents/schemas.js +14 -0
  36. package/dist/shared/events/types.js +1 -0
  37. package/dist/shared/notes/asset-formats.js +57 -0
  38. package/dist/shared/notes/constants.js +7 -0
  39. package/dist/shared/notes/document.js +118 -0
  40. package/dist/shared/notes/external-assets.js +153 -0
  41. package/dist/shared/notes/identity.js +24 -0
  42. package/dist/shared/notes/links.js +44 -0
  43. package/dist/shared/notes/note-path.js +17 -0
  44. package/dist/shared/notes/reference-path.js +55 -0
  45. package/dist/shared/notes/references.js +190 -0
  46. package/dist/shared/notes/schemas.js +222 -0
  47. package/dist/shared/notes/types.js +1 -0
  48. package/dist/shared/utils/heading-slug.js +7 -0
  49. package/dist/shared/utils/is-uuid.js +5 -0
  50. package/dist/shared/utils/safe-decode.js +9 -0
  51. package/dist/shared/utils/web-url.js +15 -0
  52. package/dist/web/assets/AppPage-DdmqiV2R.js +1 -0
  53. package/dist/web/assets/ClosePopup-lpH_1Ju2.js +1 -0
  54. package/dist/web/assets/CreateButton-Dnpwobd9.js +93 -0
  55. package/dist/web/assets/CreateReviewFabButton-KvKPEJzI.js +1 -0
  56. package/dist/web/assets/{ErrorNotFound-CYOZg8yN.js → ErrorNotFound-Cq0wNQMu.js} +1 -1
  57. package/dist/web/assets/IndexPage-DE-IkAhK.js +1 -0
  58. package/dist/web/assets/IndexPage-F8MMAAkn.css +1 -0
  59. package/dist/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  60. package/dist/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  61. package/dist/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  62. package/dist/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  63. package/dist/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  64. package/dist/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  65. package/dist/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  66. package/dist/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  67. package/dist/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  68. package/dist/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  69. package/dist/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  70. package/dist/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  71. package/dist/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  72. package/dist/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  73. package/dist/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  74. package/dist/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  75. package/dist/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  76. package/dist/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  77. package/dist/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  78. package/dist/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  79. package/dist/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  80. package/dist/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  81. package/dist/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  82. package/dist/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  83. package/dist/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  84. package/dist/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  85. package/dist/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  86. package/dist/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  87. package/dist/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  88. package/dist/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  89. package/dist/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  90. package/dist/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  91. package/dist/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  92. package/dist/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  93. package/dist/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  94. package/dist/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  95. package/dist/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  96. package/dist/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  97. package/dist/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  98. package/dist/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  99. package/dist/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  100. package/dist/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  101. package/dist/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  102. package/dist/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  103. package/dist/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  104. package/dist/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  105. package/dist/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  106. package/dist/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  107. package/dist/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  108. package/dist/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  109. package/dist/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  110. package/dist/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  111. package/dist/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  112. package/dist/web/assets/KaTeX_Size3-Regular-gV2CO0n9.woff2 +0 -0
  113. package/dist/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  114. package/dist/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  115. package/dist/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  116. package/dist/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  117. package/dist/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  118. package/dist/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  119. package/dist/web/assets/MainLayout-DM30ynlR.css +1 -0
  120. package/dist/web/assets/MainLayout-wWA8ckBg.js +6 -0
  121. package/dist/web/assets/NoteVisualEditor-Bj13_oEl.js +1054 -0
  122. package/dist/web/assets/NoteVisualEditor-IH3s3d3W.css +1 -0
  123. package/dist/web/assets/NotesPage-FidBg83M.js +4 -0
  124. package/dist/web/assets/NotesPage-KaCEnfdz.css +1 -0
  125. package/dist/web/assets/PanelResize-C51WwwLD.css +1 -0
  126. package/dist/web/assets/PanelResize-DY0Nzmql.js +1 -0
  127. package/dist/web/assets/{PdfPreview-CIiKnzQN.js → PdfPreview-C7HomxEg.js} +1 -1
  128. package/dist/web/assets/QBtn-DoggJe1Q.js +1 -0
  129. package/dist/web/assets/QFab-C-YZWgmW.js +1 -0
  130. package/dist/web/assets/QForm-DpEhYVCa.js +4 -0
  131. package/dist/web/assets/QInput-DJgTh2Qy.js +1 -0
  132. package/dist/web/assets/QResizeObserver-DeRFR_WF.js +1 -0
  133. package/dist/web/assets/QScrollArea-BSSZoaV5.js +1 -0
  134. package/dist/web/assets/QSelect-2iOBkp1K.js +1 -0
  135. package/dist/web/assets/QSpace-B5NneHAd.js +1 -0
  136. package/dist/web/assets/QVirtualScroll-CZJMBpmR.js +1 -0
  137. package/dist/web/assets/ReviewDetailPage-BI1iAhSf.js +1 -0
  138. package/dist/web/assets/ReviewsPage-RyST0FlH.js +1 -0
  139. package/dist/web/assets/{StatusSwitcher-DTD5inU1.css → StatusSwitcher-BCv_9MtI.css} +1 -1
  140. package/dist/web/assets/StatusSwitcher-DAeNzNP7.js +1 -0
  141. package/dist/web/assets/{TerminalView-Ch-ndYr6.js → TerminalView-BAzJgUTq.js} +2 -2
  142. package/dist/web/assets/{TerminalView-BurzSD2L.css → TerminalView-C1jFlfLH.css} +1 -1
  143. package/dist/web/assets/_baseMerge-BzZFQ6mI.js +1 -0
  144. package/dist/web/assets/{abnfDiagram-O67JEVCF-Cab71FlB.js → abnfDiagram-O67JEVCF-BNZQ7duT.js} +1 -1
  145. package/dist/web/assets/agent-store-DP6Vrw6L.js +113 -0
  146. package/dist/web/assets/apl-gfMMiTNf.js +1 -0
  147. package/dist/web/assets/app-info-store-u6ZVFjv5.js +1 -0
  148. package/dist/web/assets/architecture-7GRP2DOG-BSdnjp9Z.js +1 -0
  149. package/dist/web/assets/{architectureDiagram-NJMV4G6O-Dl1WKOEi.js → architectureDiagram-NJMV4G6O-Bkj0kfFn.js} +1 -1
  150. package/dist/web/assets/asciiarmor-C_eisQCp.js +1 -0
  151. package/dist/web/assets/asn1-DCl_8MCW.js +1 -0
  152. package/dist/web/assets/asterisk-BEn0Cwpx.js +1 -0
  153. package/dist/web/assets/{blockDiagram-BEXU5L5S-CZ3Lxybj.js → blockDiagram-BEXU5L5S-CXO3zN2c.js} +1 -1
  154. package/dist/web/assets/brainfuck-D5EjA2JK.js +1 -0
  155. package/dist/web/assets/{c4Diagram-YGBWAQC7-ULi06PEd.js → c4Diagram-YGBWAQC7-BHfMeZNf.js} +1 -1
  156. package/dist/web/assets/ccount-CcG1m-Yh.js +1 -0
  157. package/dist/web/assets/channel-DmoOM6AB.js +1 -0
  158. package/dist/web/assets/{chunk-3FUC2YCW-B1f60wx3.js → chunk-3FUC2YCW-z1WbH7Gi.js} +1 -1
  159. package/dist/web/assets/{chunk-5DYCD2WN-f6Pcl3dF.js → chunk-5DYCD2WN-K9HPch97.js} +1 -1
  160. package/dist/web/assets/{chunk-742MDFTN-szX1QirW.js → chunk-742MDFTN-Bhjp9fC-.js} +1 -1
  161. package/dist/web/assets/{chunk-7INBJB4K-CP_ynmAQ.js → chunk-7INBJB4K-Do0EmU14.js} +1 -1
  162. package/dist/web/assets/{chunk-7M6MHVWA-DPL74uZE.js → chunk-7M6MHVWA-veeEjsOh.js} +5 -5
  163. package/dist/web/assets/{chunk-7PRAP22T-DH9TawZY.js → chunk-7PRAP22T-CPqB5tCK.js} +1 -1
  164. package/dist/web/assets/{chunk-GTNCS2PH-CKAuGvnf.js → chunk-GTNCS2PH-Civ1V52P.js} +1 -1
  165. package/dist/web/assets/{chunk-GWA4HPMP-DTbHCHSN.js → chunk-GWA4HPMP-C7LzAlyZ.js} +1 -1
  166. package/dist/web/assets/{chunk-MBY4JIJT-ClgvJMtC.js → chunk-MBY4JIJT-DfmJy0o_.js} +1 -1
  167. package/dist/web/assets/{chunk-NETBCI7D-Dq5Z7z8S.js → chunk-NETBCI7D-BZIKmMvL.js} +1 -1
  168. package/dist/web/assets/{chunk-O7XYJQB3-C0By5mcm.js → chunk-O7XYJQB3-DcHeu6b7.js} +1 -1
  169. package/dist/web/assets/{chunk-UA2S7LBM-BtquYpxe.js → chunk-UA2S7LBM-0SR2og6x.js} +1 -1
  170. package/dist/web/assets/{chunk-WEXAMYUT-BlZL42XJ.js → chunk-WEXAMYUT-CJMSoM-Z.js} +1 -1
  171. package/dist/web/assets/{chunk-Z7XXMR3K-BbtQbAvv.js → chunk-Z7XXMR3K-q6fce66x.js} +1 -1
  172. package/dist/web/assets/{chunk-ZIGJFQKS-tC_YdRrm.js → chunk-ZIGJFQKS-DsPzen4Y.js} +1 -1
  173. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-CvlA_A4N.js → classDiagram-v2-NBCMYWYE-Cpm_bvzb.js} +4 -4
  174. package/dist/web/assets/clike-D_6L3tnO.js +1 -0
  175. package/dist/web/assets/clojure-BflJGmDX.js +1 -0
  176. package/dist/web/assets/cmake-84nbDH0I.js +1 -0
  177. package/dist/web/assets/cobol-DHLQUbN7.js +1 -0
  178. package/dist/web/assets/coffeescript-De_zI4J0.js +1 -0
  179. package/dist/web/assets/commonlisp-DgAKBe0r.js +1 -0
  180. package/dist/web/assets/core-Bumj8M77.js +12 -0
  181. package/dist/web/assets/crystal-Bsdphhtm.js +1 -0
  182. package/dist/web/assets/css-CnaYoQib.js +1 -0
  183. package/dist/web/assets/{cynefin-OW5HDTMX-DFbhKkIi.js → cynefin-OW5HDTMX-ZTr5XZMW.js} +1 -1
  184. package/dist/web/assets/{cynefinDiagram-VND7K2PF-DHGaLNQN.js → cynefinDiagram-VND7K2PF-ByUmUx58.js} +1 -1
  185. package/dist/web/assets/cypher-p9eYesGn.js +1 -0
  186. package/dist/web/assets/d-DWkQyNnU.js +1 -0
  187. package/dist/web/assets/dagre-6A5THRUB-mQOy0E3r.js +4 -0
  188. package/dist/web/assets/{diagram-22UHCM2B-Djjo5ftL.js → diagram-22UHCM2B-BW0LPtyk.js} +8 -8
  189. package/dist/web/assets/{diagram-3UASUU5V-BcP7pJru.js → diagram-3UASUU5V-jo148ec5.js} +1 -1
  190. package/dist/web/assets/{diagram-ATOU4E4O-o_vsc8em.js → diagram-ATOU4E4O-CJt4F0i4.js} +1 -1
  191. package/dist/web/assets/{diagram-CDSNMT55-eyN9Aru8.js → diagram-CDSNMT55-DEShJv56.js} +1 -1
  192. package/dist/web/assets/{diagram-MLGK6HIB-Dfjipnvb.js → diagram-MLGK6HIB-DOFOADfs.js} +1 -1
  193. package/dist/web/assets/{diagram-MPIPVDR6-C2chocRe.js → diagram-MPIPVDR6-ux537BMl.js} +1 -1
  194. package/dist/web/assets/diff-ChtP43wD.js +1 -0
  195. package/dist/web/assets/dist-3B0OaUZ-.js +1 -0
  196. package/dist/web/assets/dist-7xWvOKEv.js +1 -0
  197. package/dist/web/assets/dist-BThMRW5U.js +23 -0
  198. package/dist/web/assets/dist-BWa3ecCV.js +2 -0
  199. package/dist/web/assets/dist-BY24_62d.js +1 -0
  200. package/dist/web/assets/dist-BbdrgL5V.js +11 -0
  201. package/dist/web/assets/dist-Bo7anpPj.js +9 -0
  202. package/dist/web/assets/dist-Bu9yRqTU.js +1 -0
  203. package/dist/web/assets/dist-Bvnostf5.js +1 -0
  204. package/dist/web/assets/dist-C3sBgvRy.js +1 -0
  205. package/dist/web/assets/dist-CK1va5C_.js +1 -0
  206. package/dist/web/assets/dist-CLv-grWO.js +1 -0
  207. package/dist/web/assets/dist-CqGqStPp.js +1 -0
  208. package/dist/web/assets/dist-D1EUrS6y.js +6 -0
  209. package/dist/web/assets/dist-D2Ce45aH.js +13 -0
  210. package/dist/web/assets/dist-D7Llw4iH.js +1 -0
  211. package/dist/web/assets/dist-DL0dBIkt.js +1 -0
  212. package/dist/web/assets/dist-DUt0feNB.js +1 -0
  213. package/dist/web/assets/dist-DZQ2IsS9.js +1 -0
  214. package/dist/web/assets/dist-DpKJcdvz.js +1 -0
  215. package/dist/web/assets/dist-DuZv0LJj.js +1 -0
  216. package/dist/web/assets/dist-LFPPEX39.js +1 -0
  217. package/dist/web/assets/dist-lC0EKTFH.js +1 -0
  218. package/dist/web/assets/dockerfile-DJkHQgkl.js +1 -0
  219. package/dist/web/assets/dtd-DTF72YNO.js +1 -0
  220. package/dist/web/assets/dylan-BCDoL_-p.js +1 -0
  221. package/dist/web/assets/ebnf-D4c0_ac3.js +1 -0
  222. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-CE58dwC-.js → ebnfDiagram-ZINNZB2B-DEybuZle.js} +1 -1
  223. package/dist/web/assets/ecl-BVIeWmwY.js +1 -0
  224. package/dist/web/assets/eiffel-BEjRio4Q.js +1 -0
  225. package/dist/web/assets/{elk-276RUBZZ-KyTn9aJi.js → elk-276RUBZZ-CZXZRpGb.js} +1 -1
  226. package/dist/web/assets/elm-Cshzl8qu.js +1 -0
  227. package/dist/web/assets/{erDiagram-OPXOYQCR-D9aOi7ZD.js → erDiagram-OPXOYQCR-BgRqGAt5.js} +1 -1
  228. package/dist/web/assets/erlang-BV0c4Hdn.js +1 -0
  229. package/dist/web/assets/eventmodeling-NTZA5JFV-ZuRq6Sae.js +1 -0
  230. package/dist/web/assets/factor-LKH5gN8L.js +1 -0
  231. package/dist/web/assets/fcl-ClOhbFR7.js +1 -0
  232. package/dist/web/assets/flowDiagram-KWPJA3E3-DRyHAdLb.js +1 -0
  233. package/dist/web/assets/forth-D6RzAl64.js +1 -0
  234. package/dist/web/assets/fortran-Bt6PBEDR.js +1 -0
  235. package/dist/web/assets/{ganttDiagram-FUAMR5RP-DRAI6xWM.js → ganttDiagram-FUAMR5RP-CBJwX1gk.js} +1 -1
  236. package/dist/web/assets/gas-BHEdbvp9.js +1 -0
  237. package/dist/web/assets/gherkin-oBAE_ms0.js +1 -0
  238. package/dist/web/assets/{gitGraph-4MIJSDKK-D1lAB6tr.js → gitGraph-4MIJSDKK-Dw-XA3G1.js} +1 -1
  239. package/dist/web/assets/{gitGraphDiagram-X574FWY7-77GVnwya.js → gitGraphDiagram-X574FWY7-BpGccoWp.js} +1 -1
  240. package/dist/web/assets/graphlib-C64HifUL.js +1 -0
  241. package/dist/web/assets/groovy-CUpZ7dEl.js +1 -0
  242. package/dist/web/assets/haskell-BRsoo5mP.js +1 -0
  243. package/dist/web/assets/haxe-DjVOVvNP.js +1 -0
  244. package/dist/web/assets/http-CLVgA2GD.js +1 -0
  245. package/dist/web/assets/i18n-sfJvmpVb.js +1 -0
  246. package/dist/web/assets/idl-BUZw3wgd.js +1 -0
  247. package/dist/web/assets/index-B2CzafCr.js +2 -0
  248. package/dist/web/assets/{info-A6RAGUB7-Di1qRpwO.js → info-A6RAGUB7-Dj5RfJsg.js} +1 -1
  249. package/dist/web/assets/{infoDiagram-VRGFBTTK-gceHneJ0.js → infoDiagram-VRGFBTTK-DyVSH991.js} +1 -1
  250. package/dist/web/assets/{init-app-IHgDyoRH.js → init-app-DSS44aRI.js} +1 -1
  251. package/dist/web/assets/{is-DssfAlMe.js → is-CE4jkrGQ.js} +1 -1
  252. package/dist/web/assets/isArrayLikeObject-BbaDyxId.js +1 -0
  253. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-B9QDQoSG.js → ishikawaDiagram-OU5B5YK6-Cu0n2oqT.js} +1 -1
  254. package/dist/web/assets/javascript-Bu8mWwtz.js +1 -0
  255. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-CVTMW0pg.js → journeyDiagram-ZHPQQLJL-Bz1Ot6Zg.js} +1 -1
  256. package/dist/web/assets/julia-CQfgDRfP.js +1 -0
  257. package/dist/web/assets/{kanban-definition-PNTS6WVX-TNoWYrUr.js → kanban-definition-PNTS6WVX-C42vuY7H.js} +1 -1
  258. package/dist/web/assets/{line-B_1CHIjZ.js → line-C5WKurbS.js} +1 -1
  259. package/dist/web/assets/livescript-BXxG0Yva.js +1 -0
  260. package/dist/web/assets/lua-DPCEkfD_.js +1 -0
  261. package/dist/web/assets/markdown-DBTEhbhQ.js +15 -0
  262. package/dist/web/assets/mathematica-u8YMmbU0.js +1 -0
  263. package/dist/web/assets/mbox-BEMVcqjs.js +1 -0
  264. package/dist/web/assets/{mermaid-parser.core-LAeoGxkL.js → mermaid-parser.core-B7l8qh8B.js} +3 -3
  265. package/dist/web/assets/{mermaid.core-DMppSzfM.js → mermaid.core-Drs2mjGg.js} +7 -7
  266. package/dist/web/assets/{mindmap-definition-NLK3R4M7-DJ-6nTWg.js → mindmap-definition-NLK3R4M7-BV1FNwCn.js} +1 -1
  267. package/dist/web/assets/mirc-C9zpzAZU.js +1 -0
  268. package/dist/web/assets/mllike-B45xgp2S.js +1 -0
  269. package/dist/web/assets/modelica-CqHI_q-D.js +1 -0
  270. package/dist/web/assets/mscgen-qgSLujhx.js +1 -0
  271. package/dist/web/assets/mumps-BSzj6Xyz.js +1 -0
  272. package/dist/web/assets/nginx-8H7HVH4w.js +1 -0
  273. package/dist/web/assets/nsis-viPvXFiq.js +1 -0
  274. package/dist/web/assets/ntriples-B9h1VB_g.js +1 -0
  275. package/dist/web/assets/octave-D2Q8cUp1.js +1 -0
  276. package/dist/web/assets/oz-BwTct_5T.js +1 -0
  277. package/dist/web/assets/{packet-AYTQ26CC-B4tvN4nf.js → packet-AYTQ26CC-BPmUY1Ig.js} +1 -1
  278. package/dist/web/assets/pascal-Dp_KdFgX.js +1 -0
  279. package/dist/web/assets/{pegDiagram-GJSIUBJH-BwQ6jaVw.js → pegDiagram-GJSIUBJH-D6NPxPdr.js} +1 -1
  280. package/dist/web/assets/perl-BkSkiI9m.js +1 -0
  281. package/dist/web/assets/{pie-WAS4IAKB-ZwlyCxls.js → pie-WAS4IAKB-CTp2jNAc.js} +1 -1
  282. package/dist/web/assets/{pieDiagram-5QR66LMP-BDYLBnB1.js → pieDiagram-5QR66LMP-BVkABrg7.js} +1 -1
  283. package/dist/web/assets/pig-AF4Hwhju.js +1 -0
  284. package/dist/web/assets/pinia-BVWaJ8NX.js +1 -0
  285. package/dist/web/assets/powershell-BcIto6Sf.js +1 -0
  286. package/dist/web/assets/properties-C357ku5U.js +1 -0
  287. package/dist/web/assets/protobuf-D1ij_kNL.js +1 -0
  288. package/dist/web/assets/pug-3K-JMW4g.js +1 -0
  289. package/dist/web/assets/puppet-BZyAS7I4.js +1 -0
  290. package/dist/web/assets/purify.es--06brOip.js +3 -0
  291. package/dist/web/assets/python-B_1Jd8HM.js +1 -0
  292. package/dist/web/assets/q-B9NhS7L_.js +1 -0
  293. package/dist/web/assets/{quadrantDiagram-O4NWA36T-CkmLCxOG.js → quadrantDiagram-O4NWA36T-LZCjVd08.js} +1 -1
  294. package/dist/web/assets/r-CxDmU1I4.js +1 -0
  295. package/dist/web/assets/{radar-RG4KPBEZ-Ca7H5QOY.js → radar-RG4KPBEZ-CCiUrmsg.js} +1 -1
  296. package/dist/web/assets/{railroad-74A4TZTK-CX3Z2QYJ.js → railroad-74A4TZTK-CZn8VJbK.js} +1 -1
  297. package/dist/web/assets/railroad-abnf-HS5TGJTU-B-9tG9pW.js +1 -0
  298. package/dist/web/assets/railroad-ebnf-LZEXJU2U-D7lUyGOq.js +1 -0
  299. package/dist/web/assets/railroad-peg-WCYAUIDC-ja94t32K.js +1 -0
  300. package/dist/web/assets/{railroadDiagram-XR7U4H2S-D8ii6sM4.js → railroadDiagram-XR7U4H2S-nopy-mdw.js} +1 -1
  301. package/dist/web/assets/{requirementDiagram-PLB6GJNP-iHY-tE7c.js → requirementDiagram-PLB6GJNP-BOeMzf-x.js} +1 -1
  302. package/dist/web/assets/rpm-gpDK5sbt.js +1 -0
  303. package/dist/web/assets/ruby-ChfNwlCY.js +1 -0
  304. package/dist/web/assets/{safe-storage-BCP757jk.js → safe-storage-aRoyeFxv.js} +1 -1
  305. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-CBprSUZk.js → sankeyDiagram-IPEJSGJF-Bbk0AQUe.js} +1 -1
  306. package/dist/web/assets/sas-C2OyD2Y6.js +1 -0
  307. package/dist/web/assets/scheme-D9qYN0V5.js +1 -0
  308. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-BkphpOQ_.js → sequenceDiagram-PO4LG4MO-ZpubjKFc.js} +1 -1
  309. package/dist/web/assets/shell-DwuoZtxw.js +1 -0
  310. package/dist/web/assets/sieve-tLWDfEkZ.js +1 -0
  311. package/dist/web/assets/simple-mode-DXiLM1-G.js +1 -0
  312. package/dist/web/assets/smalltalk-BLp5-jwC.js +1 -0
  313. package/dist/web/assets/solr-B78wSQJQ.js +1 -0
  314. package/dist/web/assets/sparql-Dz-mlCAC.js +1 -0
  315. package/dist/web/assets/spreadsheet-CTIncYxj.js +1 -0
  316. package/dist/web/assets/sql-DuQMFDvy.js +1 -0
  317. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-HN1RwAPZ.js → stateDiagram-v2-GCMORJYK-DNjDh9yZ.js} +2 -2
  318. package/dist/web/assets/stex-DNzd62Q_.js +1 -0
  319. package/dist/web/assets/stylus-BOVgKnng.js +1 -0
  320. package/dist/web/assets/swift-DKB6_1j6.js +1 -0
  321. package/dist/web/assets/{swimlanes-2SLR337P-Cc8jyETz.js → swimlanes-2SLR337P-C-_UzZF_.js} +1 -1
  322. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-CVSZNvS2.js +8 -0
  323. package/dist/web/assets/tcl-BGfruO1u.js +1 -0
  324. package/dist/web/assets/textile-C4jVoHG1.js +1 -0
  325. package/dist/web/assets/theme-CeQhGDjh.js +1 -0
  326. package/dist/web/assets/tiddlywiki-VlUiK86J.js +1 -0
  327. package/dist/web/assets/tiki-AK6FwT8q.js +1 -0
  328. package/dist/web/assets/{timeline-definition-EJHVYXUP-Bas0Ksw6.js → timeline-definition-EJHVYXUP-BiaJyHdJ.js} +1 -1
  329. package/dist/web/assets/toml-BPTmHmyx.js +1 -0
  330. package/dist/web/assets/{treeView-Q6P3EWNA-Ck-xt3KO.js → treeView-Q6P3EWNA-CENjlerX.js} +1 -1
  331. package/dist/web/assets/{treemap-WGGIJYW6-k9Ay9d-c.js → treemap-WGGIJYW6-CBpdf3L2.js} +1 -1
  332. package/dist/web/assets/troff-C2dAgh7P.js +1 -0
  333. package/dist/web/assets/ttcn-PPSTk1ow.js +1 -0
  334. package/dist/web/assets/ttcn-cfg-2b4Gb5sE.js +1 -0
  335. package/dist/web/assets/turtle-Cx3rYX2g.js +1 -0
  336. package/dist/web/assets/use-add-agent-context-SApxn9fW.js +1 -0
  337. package/dist/web/assets/{use-app-theme-BfRqhHi_.js → use-app-theme-DP8Ymwwb.js} +1 -1
  338. package/dist/web/assets/use-dark-BHObfh0O.js +1 -0
  339. package/dist/web/assets/use-panel-width-CTXD_csP.js +1 -0
  340. package/dist/web/assets/use-router-link-BGc0noIO.js +1 -0
  341. package/dist/web/assets/use-server-events-C13Sw-rU.js +1 -0
  342. package/dist/web/assets/use-virtual-scroll-BSXYZjZ5.js +1 -0
  343. package/dist/web/assets/{usecaseDiagram-POWQR4AR-BlzYgvXB.js → usecaseDiagram-POWQR4AR-CcJ83_Ve.js} +1 -1
  344. package/dist/web/assets/vb-BthdM_4N.js +1 -0
  345. package/dist/web/assets/vbscript-D9f6rSsU.js +1 -0
  346. package/dist/web/assets/velocity-CqftRsjg.js +1 -0
  347. package/dist/web/assets/{vennDiagram-UO4OBE2U-gjloIQdf.js → vennDiagram-UO4OBE2U-DBjzpbFr.js} +1 -1
  348. package/dist/web/assets/verilog-CjI9Y6RI.js +1 -0
  349. package/dist/web/assets/vhdl-kS471soi.js +1 -0
  350. package/dist/web/assets/{wardley-WFR3VGLG-BnsYd7B2.js → wardley-WFR3VGLG-DmlbUWUM.js} +1 -1
  351. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-ktflnFLZ.js → wardleyDiagram-VNRHLVJA-CWSZCqf4.js} +1 -1
  352. package/dist/web/assets/webidl-D5jiJvOU.js +1 -0
  353. package/dist/web/assets/window-store-DNnKUhxs.js +1 -0
  354. package/dist/web/assets/xquery-BKJx4mf9.js +1 -0
  355. package/dist/web/assets/{xychartDiagram-PMCCYNJV-Ch5MYgz0.js → xychartDiagram-PMCCYNJV-CMnNSaZv.js} +1 -1
  356. package/dist/web/assets/yacas-CXXgtw0p.js +1 -0
  357. package/dist/web/assets/z80-Co3PbHdE.js +1 -0
  358. package/dist/web/index.html +6 -3
  359. package/package.json +10 -2
  360. package/dist/web/assets/AppPage-BGcEFW_o.js +0 -93
  361. package/dist/web/assets/CreateReviewFabButton-CdUXVriU.js +0 -1
  362. package/dist/web/assets/IndexPage-CYFIGWy9.css +0 -1
  363. package/dist/web/assets/IndexPage-OqgW0yH8.js +0 -1
  364. package/dist/web/assets/MainLayout-Bi0JE3ER.js +0 -11
  365. package/dist/web/assets/MainLayout-DPkSlqMr.css +0 -1
  366. package/dist/web/assets/QBtn-CzrOO1zA.js +0 -1
  367. package/dist/web/assets/QScrollArea-C4BKutrt.js +0 -1
  368. package/dist/web/assets/QSelect-BXUaq4ky.js +0 -1
  369. package/dist/web/assets/QSpace-BMpRu3a-.js +0 -13
  370. package/dist/web/assets/ReviewDetailPage-DlR7Vxkw.js +0 -1
  371. package/dist/web/assets/ReviewsPage-DM8XtlUK.js +0 -1
  372. package/dist/web/assets/StatusSwitcher-Blg5JmAa.js +0 -1
  373. package/dist/web/assets/agent-store-CDuiBhST.js +0 -1
  374. package/dist/web/assets/app-info-store-BmD_P8q2.js +0 -1
  375. package/dist/web/assets/architecture-7GRP2DOG-DMgDJZyQ.js +0 -1
  376. package/dist/web/assets/channel-BnUyOMnk.js +0 -1
  377. package/dist/web/assets/core-C37KF-j0.js +0 -12
  378. package/dist/web/assets/dagre-6A5THRUB-Bk3Po5o5.js +0 -4
  379. package/dist/web/assets/eventmodeling-NTZA5JFV-7YdTAmVS.js +0 -1
  380. package/dist/web/assets/flowDiagram-KWPJA3E3-eOIiY2oF.js +0 -1
  381. package/dist/web/assets/graphlib-DS17s2tU.js +0 -1
  382. package/dist/web/assets/i18n-C5Xnmkwe.js +0 -1
  383. package/dist/web/assets/index-BqedItFI.js +0 -2
  384. package/dist/web/assets/pinia-BKoxNmoW.js +0 -1
  385. package/dist/web/assets/railroad-abnf-HS5TGJTU-DZSn8MK2.js +0 -1
  386. package/dist/web/assets/railroad-ebnf-LZEXJU2U-BVTJtDBq.js +0 -1
  387. package/dist/web/assets/railroad-peg-WCYAUIDC-DMzsNGzj.js +0 -1
  388. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-BxXXHQ70.js +0 -8
  389. package/dist/web/assets/theme-CPoPdFci.js +0 -1
  390. package/dist/web/assets/use-panel-width-C9dWftsJ.js +0 -1
  391. package/dist/web/assets/use-server-events-znzHKZZx.js +0 -1
  392. package/dist/web/assets/use-virtual-scroll-CIwaJCUj.js +0 -1
@@ -0,0 +1,444 @@
1
+ import { noteChildPath, noteParentPath } from "../../shared/notes/note-path.js";
2
+ import { constants } from 'node:fs';
3
+ import { createHash, randomUUID } from 'node:crypto';
4
+ import { lstat, link, mkdir, open, readdir, realpath, rename, unlink, writeFile } from 'node:fs/promises';
5
+ import { basename, dirname, join, resolve, sep } from 'node:path';
6
+ import { BadRequestError, NotFoundError } from "../errors/http.js";
7
+ import { isSystemError } from "../utils/system-error.js";
8
+ import { sha256Hex } from "../utils/hash.js";
9
+ import { NoteConflictError, NoteExistsError } from "../errors/notes.js";
10
+ import { noteAssetFormat } from "../../shared/notes/asset-formats.js";
11
+ // Bound file reads before allocating document buffers.
12
+ const MAX_BYTES = 1000000;
13
+ export class NotesRepository {
14
+ root;
15
+ writes = new Map();
16
+ constructor(root) {
17
+ this.root = resolve(root);
18
+ }
19
+ async path(path, markdown = true) {
20
+ if (!path ||
21
+ path.includes('\\') ||
22
+ path.includes('\0') ||
23
+ path.startsWith('/') ||
24
+ path
25
+ .split('/')
26
+ .some(part => !part || part === '.' || part === '..' || part.startsWith('.')) ||
27
+ (markdown && !path.toLowerCase().endsWith('.md'))) {
28
+ throw new BadRequestError('Invalid note path');
29
+ }
30
+ const target = resolve(this.root, path);
31
+ if (!target.startsWith(this.root + sep)) {
32
+ throw new BadRequestError('Path leaves the notes folder');
33
+ }
34
+ // Reject links inside the notes folder. The configured root and its parents may be links.
35
+ let current = this.root;
36
+ for (const part of target
37
+ .slice(this.root.length)
38
+ .split(sep)
39
+ .filter(Boolean)) {
40
+ current = join(current, part);
41
+ try {
42
+ if ((await lstat(current)).isSymbolicLink()) {
43
+ throw new BadRequestError('Symbolic links are not supported in the notes path');
44
+ }
45
+ }
46
+ catch (error) {
47
+ if (!isSystemError(error, 'ENOENT'))
48
+ throw error;
49
+ }
50
+ }
51
+ return target;
52
+ }
53
+ async read(path) {
54
+ const target = await this.path(path);
55
+ try {
56
+ const file = await open(target, constants.O_RDONLY | constants.O_NOFOLLOW);
57
+ try {
58
+ const stat = await file.stat();
59
+ if (!stat.isFile() || stat.size > MAX_BYTES) {
60
+ throw new BadRequestError('Note must be a Markdown file under 1 MB');
61
+ }
62
+ const bytes = await file.readFile();
63
+ if (bytes.byteLength > MAX_BYTES) {
64
+ throw new BadRequestError('Note exceeds 1 MB');
65
+ }
66
+ let text;
67
+ try {
68
+ text = new TextDecoder('utf-8', {
69
+ fatal: true,
70
+ ignoreBOM: true
71
+ }).decode(bytes);
72
+ }
73
+ catch {
74
+ throw new BadRequestError('Note is not valid UTF-8');
75
+ }
76
+ return { path, text, version: sha256Hex(text) };
77
+ }
78
+ finally {
79
+ await file.close();
80
+ }
81
+ }
82
+ catch (error) {
83
+ if (isSystemError(error, 'ENOENT')) {
84
+ throw new NotFoundError('Note not found');
85
+ }
86
+ throw error;
87
+ }
88
+ }
89
+ async list() {
90
+ const result = [];
91
+ await this.path('probe.md');
92
+ const walk = async (dir, prefix) => {
93
+ const entries = (await readdir(dir, { withFileTypes: true })).filter(entry => !entry.name.startsWith('.') &&
94
+ !entry.isSymbolicLink() &&
95
+ entry.name !== 'node_modules' &&
96
+ (prefix || entry.name !== 'assets'));
97
+ for (const entry of entries.filter(item => item.isDirectory())) {
98
+ const path = prefix + entry.name;
99
+ await this.path(path, false);
100
+ result.push({ path, folder: true, signature: 'folder' });
101
+ await walk(join(dir, entry.name), path + '/');
102
+ }
103
+ const files = entries.filter(entry => entry.isFile() && entry.name.toLowerCase().endsWith('.md'));
104
+ // Bound concurrent metadata reads while retaining path checks for each file.
105
+ for (let offset = 0; offset < files.length; offset += 16) {
106
+ const batch = await Promise.allSettled(files.slice(offset, offset + 16).map(async (entry) => {
107
+ const path = prefix + entry.name;
108
+ const target = await this.path(path);
109
+ const stat = await lstat(target, { bigint: true });
110
+ return {
111
+ path,
112
+ folder: false,
113
+ signature: `${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`
114
+ };
115
+ }));
116
+ for (const item of batch) {
117
+ if (item.status === 'rejected')
118
+ throw item.reason;
119
+ result.push(item.value);
120
+ }
121
+ }
122
+ };
123
+ await walk(this.root, '');
124
+ return result.sort((a, b) => a.path.localeCompare(b.path));
125
+ }
126
+ async serial(path, run) {
127
+ const prior = this.writes.get(path) ?? Promise.resolve();
128
+ const next = prior.catch(() => { }).then(run);
129
+ this.writes.set(path, next);
130
+ try {
131
+ return await next;
132
+ }
133
+ finally {
134
+ if (this.writes.get(path) === next)
135
+ this.writes.delete(path);
136
+ }
137
+ }
138
+ async save(path, text, expected) {
139
+ return this.serial(path, async () => {
140
+ if (Buffer.byteLength(text) > MAX_BYTES) {
141
+ throw new BadRequestError('Note exceeds 1 MB');
142
+ }
143
+ const target = await this.path(path);
144
+ await mkdir(dirname(target), { recursive: true });
145
+ await this.path(path);
146
+ let current;
147
+ if (expected !== undefined) {
148
+ try {
149
+ current = await this.read(path);
150
+ }
151
+ catch (error) {
152
+ if (error instanceof NotFoundError)
153
+ throw new NoteConflictError();
154
+ throw error;
155
+ }
156
+ if (current.version !== expected)
157
+ throw new NoteConflictError();
158
+ if (current.text === text)
159
+ return current;
160
+ // Retain one previous version per note, without accumulating autosave history.
161
+ const backups = join(this.root, '.history');
162
+ await mkdir(backups, { recursive: true });
163
+ if ((await lstat(backups)).isSymbolicLink() ||
164
+ !(await realpath(backups)).startsWith((await realpath(this.root)) + sep)) {
165
+ throw new BadRequestError('Invalid backup folder');
166
+ }
167
+ const backupTemp = join(backups, '.backup-' + randomUUID());
168
+ try {
169
+ await writeFile(backupTemp, current.text, { flag: 'wx', mode: 0o600 });
170
+ await rename(backupTemp, join(backups, sha256Hex(path) + '.md'));
171
+ }
172
+ finally {
173
+ await unlink(backupTemp).catch(error => {
174
+ if (!isSystemError(error, 'ENOENT'))
175
+ throw error;
176
+ });
177
+ }
178
+ }
179
+ const temporary = join(dirname(target), '.note-' + randomUUID());
180
+ try {
181
+ const file = await open(temporary, 'wx', 0o600);
182
+ try {
183
+ await file.writeFile(text);
184
+ await file.sync();
185
+ }
186
+ finally {
187
+ await file.close();
188
+ }
189
+ await this.path(path);
190
+ if (expected === undefined) {
191
+ try {
192
+ await link(temporary, target);
193
+ }
194
+ catch (error) {
195
+ if (isSystemError(error, 'EEXIST'))
196
+ throw new NoteExistsError();
197
+ throw error;
198
+ }
199
+ }
200
+ else {
201
+ if ((await this.read(path)).version !== expected) {
202
+ throw new NoteConflictError();
203
+ }
204
+ await rename(temporary, target);
205
+ }
206
+ }
207
+ finally {
208
+ await unlink(temporary).catch(error => {
209
+ if (!isSystemError(error, 'ENOENT'))
210
+ throw error;
211
+ });
212
+ }
213
+ return { path, text, version: sha256Hex(text) };
214
+ });
215
+ }
216
+ async saveAsset(bytes, extension) {
217
+ const staged = await this.stageAsset((async function* () {
218
+ yield bytes;
219
+ })(), extension, new AbortController().signal);
220
+ try {
221
+ return await staged.commit();
222
+ }
223
+ finally {
224
+ await staged.dispose();
225
+ }
226
+ }
227
+ async stageAsset(chunks, extension, signal) {
228
+ const format = noteAssetFormat(extension);
229
+ if (!format)
230
+ throw new BadRequestError('Unsupported attachment format');
231
+ const probe = await this.path('assets/probe.png', false);
232
+ await mkdir(dirname(probe), { recursive: true });
233
+ await this.path('assets/probe.png', false);
234
+ const temporary = join(dirname(probe), '.asset-' + randomUUID());
235
+ const dispose = async () => {
236
+ await unlink(temporary).catch(error => {
237
+ if (!isSystemError(error, 'ENOENT'))
238
+ throw error;
239
+ });
240
+ };
241
+ try {
242
+ const file = await open(temporary, 'wx', 0o600);
243
+ const hash = createHash('sha256');
244
+ let bytes = 0;
245
+ try {
246
+ for await (const chunk of chunks) {
247
+ signal.throwIfAborted();
248
+ bytes += chunk.length;
249
+ if (bytes > format.maxBytes) {
250
+ throw new BadRequestError('Attachment exceeds its size limit');
251
+ }
252
+ hash.update(chunk);
253
+ await file.writeFile(chunk);
254
+ }
255
+ if (!bytes)
256
+ throw new BadRequestError('Empty attachment');
257
+ await file.sync();
258
+ }
259
+ finally {
260
+ await file.close();
261
+ }
262
+ const id = hash.digest('hex') + '.' + extension;
263
+ return {
264
+ dispose,
265
+ commit: async () => {
266
+ signal.throwIfAborted();
267
+ const target = await this.path('assets/' + id, false);
268
+ try {
269
+ await link(temporary, target);
270
+ }
271
+ catch (error) {
272
+ if (!isSystemError(error, 'EEXIST'))
273
+ throw error;
274
+ }
275
+ return id;
276
+ }
277
+ };
278
+ }
279
+ catch (error) {
280
+ await dispose();
281
+ throw error;
282
+ }
283
+ }
284
+ async readAsset(id) {
285
+ const target = await this.path('assets/' + id, false);
286
+ try {
287
+ const file = await open(target, constants.O_RDONLY | constants.O_NOFOLLOW);
288
+ try {
289
+ const stat = await file.stat();
290
+ const format = noteAssetFormat(id.split('.').at(-1));
291
+ if (!format ||
292
+ !stat.isFile() ||
293
+ !stat.size ||
294
+ stat.size > format.maxBytes) {
295
+ throw new BadRequestError('Invalid attachment');
296
+ }
297
+ return file.createReadStream({ autoClose: true, end: stat.size - 1 });
298
+ }
299
+ catch (error) {
300
+ await file.close();
301
+ throw error;
302
+ }
303
+ }
304
+ catch (error) {
305
+ if (isSystemError(error, 'ENOENT')) {
306
+ throw new NotFoundError('Attachment not found');
307
+ }
308
+ throw error;
309
+ }
310
+ }
311
+ async sameEntry(path, other) {
312
+ const a = await lstat(await this.path(path, false));
313
+ const b = await lstat(await this.path(other, false));
314
+ return a.dev === b.dev && a.ino === b.ino;
315
+ }
316
+ async exactEntry(path) {
317
+ const target = await this.path(path, false);
318
+ try {
319
+ return (await readdir(dirname(target))).includes(basename(target));
320
+ }
321
+ catch (error) {
322
+ if (isSystemError(error, 'ENOENT'))
323
+ return false;
324
+ throw error;
325
+ }
326
+ }
327
+ async exists(path) {
328
+ const target = await this.path(path, false);
329
+ try {
330
+ await lstat(target);
331
+ return true;
332
+ }
333
+ catch (error) {
334
+ if (isSystemError(error, 'ENOENT'))
335
+ return false;
336
+ throw error;
337
+ }
338
+ }
339
+ async moveEntry(path, destination, folder) {
340
+ const source = await this.path(path, !folder);
341
+ const target = await this.path(destination, !folder);
342
+ if (destination.split('/')[0] === 'assets' ||
343
+ path.split('/')[0] === 'assets') {
344
+ throw new BadRequestError('Cannot move attachments');
345
+ }
346
+ if (folder && destination.startsWith(path + '/')) {
347
+ throw new BadRequestError('Cannot move a folder into itself');
348
+ }
349
+ if (path === destination)
350
+ return;
351
+ if (await this.exists(destination))
352
+ throw new NoteExistsError();
353
+ const stat = await lstat(source);
354
+ if (folder ? !stat.isDirectory() : !stat.isFile()) {
355
+ throw new BadRequestError('Entry type changed');
356
+ }
357
+ await mkdir(dirname(target), { recursive: true });
358
+ await this.path(destination, !folder);
359
+ if (folder) {
360
+ await rename(source, target);
361
+ }
362
+ else {
363
+ try {
364
+ await link(source, target);
365
+ }
366
+ catch (error) {
367
+ if (isSystemError(error, 'EEXIST'))
368
+ throw new NoteExistsError();
369
+ throw error;
370
+ }
371
+ await unlink(source);
372
+ }
373
+ }
374
+ async renameEntry(path, name, folder) {
375
+ return this.serial('rename', async () => {
376
+ if (!name.trim() ||
377
+ name !== name.trim() ||
378
+ /[/\\\0]/.test(name) ||
379
+ name.startsWith('.')) {
380
+ throw new BadRequestError('Invalid entry name');
381
+ }
382
+ if (path.split('/')[0] === 'assets') {
383
+ throw new BadRequestError('Cannot rename attachments');
384
+ }
385
+ const targetPath = noteChildPath(noteParentPath(path), name);
386
+ if (targetPath.split('/')[0] === 'assets') {
387
+ throw new BadRequestError('Reserved folder name');
388
+ }
389
+ const source = await this.path(path, !folder);
390
+ const target = await this.path(targetPath, !folder);
391
+ let stat;
392
+ try {
393
+ stat = await lstat(source);
394
+ }
395
+ catch (error) {
396
+ if (isSystemError(error, 'ENOENT')) {
397
+ throw new NotFoundError('Note entry not found');
398
+ }
399
+ throw error;
400
+ }
401
+ if (folder ? !stat.isDirectory() : !stat.isFile()) {
402
+ throw new BadRequestError('Entry type changed');
403
+ }
404
+ if (path === targetPath)
405
+ return targetPath;
406
+ try {
407
+ await lstat(target);
408
+ throw new NoteExistsError();
409
+ }
410
+ catch (error) {
411
+ if (!isSystemError(error, 'ENOENT'))
412
+ throw error;
413
+ }
414
+ if (folder) {
415
+ await rename(source, target);
416
+ }
417
+ else {
418
+ try {
419
+ await link(source, target);
420
+ }
421
+ catch (error) {
422
+ if (isSystemError(error, 'EEXIST'))
423
+ throw new NoteExistsError();
424
+ throw error;
425
+ }
426
+ await unlink(source);
427
+ }
428
+ return targetPath;
429
+ });
430
+ }
431
+ async folder(path) {
432
+ const target = await this.path(path, false);
433
+ await mkdir(dirname(target), { recursive: true });
434
+ await this.path(path, false);
435
+ try {
436
+ await mkdir(target);
437
+ }
438
+ catch (error) {
439
+ if (isSystemError(error, 'EEXIST'))
440
+ throw new NoteExistsError();
441
+ throw error;
442
+ }
443
+ }
444
+ }
@@ -124,6 +124,7 @@ export async function agentRoutes(app, opts) {
124
124
  }
125
125
  session.assertCanPrompt();
126
126
  const blocks = await buildPromptBlocks(request.body, {
127
+ notes: opts.notes,
127
128
  db,
128
129
  repositoryPath,
129
130
  embeddedContext: session.supportsEmbeddedContext,
@@ -0,0 +1,213 @@
1
+ import { NotesExternalAssetsService } from "../services/notes-external-assets.service.js";
2
+ import { InspectExternalAssetBody, PreviewExternalAssetBody, ImportExternalAssetBody, ExternalAssetInspectionSchema, ExternalAssetResultSchema } from "../../shared/notes/schemas.js";
3
+ import multipart from '@fastify/multipart';
4
+ import { MAX_NOTE_ASSET_BYTES } from "../../shared/notes/constants.js";
5
+ import { noteAssetFormat } from "../../shared/notes/asset-formats.js";
6
+ import { NoteRecordSchema, NoteIdQuery, NoteDescriptorSchema, NoteResolveBody, NoteResolutionSchema, NoteGraphSchema, ResolveIdentityBody, NoteMutationSchema, UploadNoteAssetBody, NoteAssetSchema, NoteAssetContentSchema, NoteAssetParams, NoteQuery, NoteSchema, NotesIndexSchema, CreateNoteBody, SaveNoteBody, RenameNoteBody, CreateFolderBody } from "../../shared/notes/schemas.js";
7
+ import { ERROR_RESPONSES, NoContentSchema } from "../../shared/http/schemas.js";
8
+ import { BadRequestError } from "../errors/http.js";
9
+ import { withRequestSignal } from "../utils/request-abort.js";
10
+ import { createNotesOriginHook } from "../hooks/notes/same-origin.js";
11
+ export async function notesRoutes(app, { service, loopbackBind = false, externalAssets = new NotesExternalAssetsService() }) {
12
+ await app.register(multipart, {
13
+ attachFieldsToBody: true,
14
+ limits: { fileSize: MAX_NOTE_ASSET_BYTES, files: 1, fields: 0, parts: 1 }
15
+ });
16
+ const typed = app.withTypeProvider();
17
+ app.addHook('onRequest', createNotesOriginHook({ loopbackBind }));
18
+ app.post('/api/notes/assets', {
19
+ preValidation: async (request) => {
20
+ if (!request.isMultipart()) {
21
+ throw new BadRequestError('Expected multipart/form-data');
22
+ }
23
+ },
24
+ schema: {
25
+ consumes: ['multipart/form-data'],
26
+ tags: ['notes'],
27
+ summary: 'Store a note image',
28
+ // The multipart validator rewrites file schemas during compilation.
29
+ body: structuredClone(UploadNoteAssetBody),
30
+ response: { 201: NoteAssetSchema, ...ERROR_RESPONSES }
31
+ }
32
+ }, async (request, reply) => {
33
+ const asset = await service.uploadAsset(await request.body.file.toBuffer());
34
+ reply.code(201);
35
+ return asset;
36
+ });
37
+ typed.post('/api/notes/assets/inspect', {
38
+ schema: {
39
+ tags: ['notes'],
40
+ summary: 'Inspect an external attachment',
41
+ body: InspectExternalAssetBody,
42
+ response: { 200: ExternalAssetInspectionSchema, ...ERROR_RESPONSES }
43
+ }
44
+ }, (request, reply) => withRequestSignal(reply, signal => externalAssets.inspect(request.body.url, signal)));
45
+ typed.post('/api/notes/assets/import', {
46
+ schema: {
47
+ tags: ['notes'],
48
+ summary: 'Import an external attachment',
49
+ body: ImportExternalAssetBody,
50
+ response: { 200: ExternalAssetResultSchema, ...ERROR_RESPONSES }
51
+ }
52
+ }, (request, reply) => withRequestSignal(reply, signal => externalAssets.importAsset(request.body, signal, (chunks, extension, saveSignal) => service.importAsset(chunks, extension, saveSignal))));
53
+ // The typed provider rejects a binary payload for the content schema.
54
+ app.post('/api/notes/assets/preview', {
55
+ schema: {
56
+ tags: ['notes'],
57
+ summary: 'Preview an external image',
58
+ body: PreviewExternalAssetBody,
59
+ response: { 200: NoteAssetContentSchema, ...ERROR_RESPONSES }
60
+ }
61
+ }, async (request, reply) => {
62
+ const preview = await withRequestSignal(reply, signal => externalAssets.preview(request.body.url, signal));
63
+ reply
64
+ .header('Cache-Control', 'no-store')
65
+ .header('X-Content-Type-Options', 'nosniff')
66
+ .type(preview.mime);
67
+ return reply.send(preview.body);
68
+ });
69
+ app.get('/api/notes/assets/:id', {
70
+ schema: {
71
+ tags: ['notes'],
72
+ summary: 'Read a stored note attachment',
73
+ params: NoteAssetParams,
74
+ response: { 200: NoteAssetContentSchema, ...ERROR_RESPONSES }
75
+ }
76
+ }, async (request, reply) => {
77
+ const image = await service.readAsset(request.params.id);
78
+ const extension = request.params.id.split('.').at(-1);
79
+ const format = noteAssetFormat(extension);
80
+ if (!format)
81
+ throw new BadRequestError('Unsupported attachment format');
82
+ reply.type(format.mime);
83
+ reply.header('X-Content-Type-Options', 'nosniff');
84
+ return reply.send(image);
85
+ });
86
+ typed.get('/api/notes', {
87
+ schema: {
88
+ tags: ['notes'],
89
+ summary: 'List project notes',
90
+ response: { 200: NotesIndexSchema, ...ERROR_RESPONSES }
91
+ }
92
+ }, () => service.index());
93
+ typed.get('/api/notes/path', {
94
+ schema: {
95
+ tags: ['notes'],
96
+ summary: 'Resolve a current or historical note path',
97
+ querystring: NoteQuery,
98
+ response: { 200: NoteRecordSchema, ...ERROR_RESPONSES }
99
+ }
100
+ }, request => service.identity.resolvePath(request.query.path));
101
+ typed.get('/api/notes/identity', {
102
+ schema: {
103
+ tags: ['notes'],
104
+ summary: 'Read a stable note identity and backlinks',
105
+ querystring: NoteIdQuery,
106
+ response: { 200: NoteDescriptorSchema, ...ERROR_RESPONSES }
107
+ }
108
+ }, request => service.identity.descriptor(request.query.id, request.query.storageId));
109
+ typed.get('/api/notes/by-id', {
110
+ schema: {
111
+ tags: ['notes'],
112
+ summary: 'Read a note by stable identity',
113
+ querystring: NoteIdQuery,
114
+ response: { 200: NoteSchema, ...ERROR_RESPONSES }
115
+ }
116
+ }, request => service.identity.readId(request.query.id, request.query.storageId));
117
+ typed.get('/api/notes/graph', {
118
+ schema: {
119
+ tags: ['notes'],
120
+ summary: 'Read the graph of note relationships',
121
+ response: { 200: NoteGraphSchema, ...ERROR_RESPONSES }
122
+ }
123
+ }, () => service.identity.graph());
124
+ typed.post('/api/notes/resolve', {
125
+ schema: {
126
+ tags: ['notes'],
127
+ summary: 'Resolve an occurrence in the current note source',
128
+ body: NoteResolveBody,
129
+ response: { 200: NoteResolutionSchema, ...ERROR_RESPONSES }
130
+ }
131
+ }, request => service.identity.resolve(request.body.sourceId, request.body.storageId, request.body.text, request.body.href, request.body.occurrence));
132
+ typed.post('/api/notes/identity/resolve', {
133
+ schema: {
134
+ tags: ['notes'],
135
+ summary: 'Resolve a restored or moved file identity',
136
+ body: ResolveIdentityBody,
137
+ response: { 204: NoContentSchema, ...ERROR_RESPONSES }
138
+ }
139
+ }, async (request, reply) => {
140
+ await service.identity.resolveIdentity(request.body.issueId, request.body.restore, request.body.candidateId);
141
+ reply.code(204).send(null);
142
+ });
143
+ typed.post('/api/notes/repair', {
144
+ schema: {
145
+ tags: ['notes'],
146
+ summary: 'Retry pending link repairs',
147
+ response: { 204: NoContentSchema, ...ERROR_RESPONSES }
148
+ }
149
+ }, async (_request, reply) => {
150
+ await service.identity.retry();
151
+ reply.code(204).send(null);
152
+ });
153
+ typed.get('/api/notes/document', {
154
+ schema: {
155
+ tags: ['notes'],
156
+ summary: 'Read a note',
157
+ querystring: NoteQuery,
158
+ response: { 200: NoteSchema, ...ERROR_RESPONSES }
159
+ }
160
+ }, request => service.read(request.query.path));
161
+ typed.post('/api/notes/document', {
162
+ schema: {
163
+ tags: ['notes'],
164
+ summary: 'Create a note',
165
+ body: CreateNoteBody,
166
+ response: { 201: NoteSchema, ...ERROR_RESPONSES }
167
+ }
168
+ }, async (request, reply) => {
169
+ const note = await service.save(request.body.path, request.body.text);
170
+ reply.code(201);
171
+ return note;
172
+ });
173
+ typed.patch('/api/notes/document', {
174
+ schema: {
175
+ tags: ['notes'],
176
+ summary: 'Save a note if its version matches',
177
+ body: SaveNoteBody,
178
+ response: { 200: NoteSchema, ...ERROR_RESPONSES }
179
+ }
180
+ }, request => service.save(request.body.path, request.body.text, request.body.version, request.body.id, request.body.storageId));
181
+ typed.post('/api/notes/delete', {
182
+ schema: {
183
+ tags: ['notes'],
184
+ summary: 'Hide a note without removing its file',
185
+ body: NoteIdQuery,
186
+ response: { 204: NoContentSchema, ...ERROR_RESPONSES }
187
+ }
188
+ }, async (request, reply) => {
189
+ await service.delete(request.body.id, request.body.storageId);
190
+ reply.code(204).send(null);
191
+ });
192
+ typed.post('/api/notes/rename', {
193
+ schema: {
194
+ tags: ['notes'],
195
+ summary: 'Rename a note or folder',
196
+ body: RenameNoteBody,
197
+ response: { 200: NoteMutationSchema, ...ERROR_RESPONSES }
198
+ }
199
+ }, async (request) => {
200
+ return service.rename(request.body.path, request.body.name, request.body.folder, request.body.target, request.body.id, request.body.revision);
201
+ });
202
+ typed.post('/api/notes/folder', {
203
+ schema: {
204
+ tags: ['notes'],
205
+ summary: 'Create a notes folder',
206
+ body: CreateFolderBody,
207
+ response: { 204: NoContentSchema, ...ERROR_RESPONSES }
208
+ }
209
+ }, async (request, reply) => {
210
+ await service.folder(request.body.path);
211
+ reply.code(204).send(null);
212
+ });
213
+ }
@@ -45,8 +45,9 @@ export async function terminalRoutes(app, options) {
45
45
  },
46
46
  onRequest: requireTerminalOrigin,
47
47
  preValidation: async (request) => {
48
- if (!tokens.consume(request.query.token, request.headers.origin ?? ''))
48
+ if (!tokens.consume(request.query.token, request.headers.origin ?? '')) {
49
49
  throw new ForbiddenError('Terminal connection token is invalid or expired.');
50
+ }
50
51
  }
51
52
  }, socket => {
52
53
  const connection = attachTerminalSocket(socket, options.registry, {