@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
@@ -42,15 +42,18 @@ export class TerminalRegistry {
42
42
  }
43
43
  get(id) {
44
44
  const entry = this.entries.get(id);
45
- if (!entry)
45
+ if (!entry) {
46
46
  throw new TerminalNotFoundError('Terminal session no longer exists.');
47
+ }
47
48
  return entry.session;
48
49
  }
49
50
  async create(cols, rows) {
50
- if (this.stopped)
51
+ if (this.stopped) {
51
52
  throw new TerminalNotFoundError('Terminal server is stopping.');
52
- if (this.entries.size + this.creating >= MAX_TERMINAL_SESSIONS)
53
+ }
54
+ if (this.entries.size + this.creating >= MAX_TERMINAL_SESSIONS) {
53
55
  throw new TerminalLimitError('The terminal limit has been reached.');
56
+ }
54
57
  this.creating++;
55
58
  try {
56
59
  const backend = await (this.options.backend ?? createBackend)(this.options.repositoryPath, cols, rows);
@@ -78,16 +81,18 @@ export class TerminalRegistry {
78
81
  }
79
82
  attach(id, subscriber) {
80
83
  const entry = this.entries.get(id);
81
- if (!entry)
84
+ if (!entry) {
82
85
  throw new TerminalNotFoundError('Terminal session no longer exists.');
86
+ }
83
87
  entry.subscribers.add(subscriber);
84
88
  entry.orphanedSince = null;
85
89
  entry.idleSince = null;
86
90
  }
87
91
  detach(subscriber) {
88
92
  for (const entry of this.entries.values()) {
89
- if (!entry.subscribers.delete(subscriber) || entry.subscribers.size)
93
+ if (!entry.subscribers.delete(subscriber) || entry.subscribers.size) {
90
94
  continue;
95
+ }
91
96
  entry.orphanedSince = this.now();
92
97
  entry.idleSince = entry.session.running === 'idle' ? this.now() : null;
93
98
  }
@@ -99,8 +99,9 @@ export class TerminalSession {
99
99
  let end = Math.min(data.length, offset + FRAME_PART_UNITS);
100
100
  if (end < data.length &&
101
101
  data.charCodeAt(end - 1) >= HIGH_SURROGATE_FIRST &&
102
- data.charCodeAt(end - 1) <= HIGH_SURROGATE_LAST)
102
+ data.charCodeAt(end - 1) <= HIGH_SURROGATE_LAST) {
103
103
  end--;
104
+ }
104
105
  const part = data.slice(offset, end);
105
106
  offset = end;
106
107
  const bytes = Buffer.byteLength(part);
@@ -131,8 +132,9 @@ export class TerminalSession {
131
132
  .catch(() => this.onExit())
132
133
  .finally(() => {
133
134
  this.pendingBytes -= bytes;
134
- if (!this.stopped && this.pendingBytes < QUEUE_RESUME_BYTES)
135
+ if (!this.stopped && this.pendingBytes < QUEUE_RESUME_BYTES) {
135
136
  this.updateFlow();
137
+ }
136
138
  });
137
139
  }
138
140
  }
@@ -167,8 +169,9 @@ export class TerminalSession {
167
169
  scrollback = Math.floor(scrollback / 2);
168
170
  data = this.serializer.serialize({ scrollback }) + this.tail.tail;
169
171
  }
170
- if (Buffer.byteLength(data) > TERMINAL_SNAPSHOT_BYTES)
172
+ if (Buffer.byteLength(data) > TERMINAL_SNAPSHOT_BYTES) {
171
173
  throw new TerminalError('Terminal snapshot exceeds its limit.');
174
+ }
172
175
  send({
173
176
  type: 'snapshot',
174
177
  terminalId: this.id,
@@ -186,8 +189,9 @@ export class TerminalSession {
186
189
  this.backend.write(data);
187
190
  }
188
191
  line(data) {
189
- if (this.backend.mode !== 'pipe')
192
+ if (this.backend.mode !== 'pipe') {
190
193
  throw new TerminalError('Line input requires limited mode.');
194
+ }
191
195
  this.output(data.replace(/\n/g, '\r\n') + '\r\n');
192
196
  this.backend.write(data + '\n');
193
197
  }
@@ -203,8 +207,9 @@ export class TerminalSession {
203
207
  this.flush();
204
208
  this.queue = this.queue
205
209
  .then(() => {
206
- if (this.stopped || (cols === this.cols && rows === this.rows))
210
+ if (this.stopped || (cols === this.cols && rows === this.rows)) {
207
211
  return undefined;
212
+ }
208
213
  this.backend.resize(cols, rows);
209
214
  this.terminal.resize(cols, rows);
210
215
  this.cols = cols;
@@ -10,8 +10,9 @@ export class TerminalTokens {
10
10
  }
11
11
  issue(origin) {
12
12
  this.prune();
13
- if (this.tokens.size >= MAX_TOKENS)
13
+ if (this.tokens.size >= MAX_TOKENS) {
14
14
  this.tokens.delete(this.tokens.keys().next().value ?? '');
15
+ }
15
16
  const token = randomBytes(32).toString('base64url');
16
17
  this.tokens.set(token, { origin, expires: this.now() + TERMINAL_TOKEN_MS });
17
18
  return token;
@@ -22,9 +23,10 @@ export class TerminalTokens {
22
23
  return !!info && info.expires > this.now() && info.origin === origin;
23
24
  }
24
25
  prune() {
25
- for (const [token, info] of this.tokens)
26
+ for (const [token, info] of this.tokens) {
26
27
  if (info.expires <= this.now())
27
28
  this.tokens.delete(token);
29
+ }
28
30
  }
29
31
  clear() {
30
32
  this.tokens.clear();
@@ -0,0 +1,4 @@
1
+ import { createHash } from 'node:crypto';
2
+ export function sha256Hex(data) {
3
+ return createHash('sha256').update(data).digest('hex');
4
+ }
@@ -0,0 +1,19 @@
1
+ import { fileTypeFromBuffer } from 'file-type';
2
+ import { NOTE_ASSET_FORMATS, SUPPORTED_NOTE_ASSETS } from "../../shared/notes/asset-formats.js";
3
+ import { BadRequestError } from "../errors/http.js";
4
+ export async function detectNoteAssetFormat(bytes) {
5
+ let detected;
6
+ try {
7
+ detected = await fileTypeFromBuffer(bytes);
8
+ }
9
+ catch (cause) {
10
+ throw new BadRequestError('Could not identify attachment format', { cause });
11
+ }
12
+ const format = detected &&
13
+ NOTE_ASSET_FORMATS.find(candidate => candidate.extension === detected.ext ||
14
+ candidate.aliases.some(alias => alias === detected.ext));
15
+ if (!format) {
16
+ throw new BadRequestError('Supported attachments: ' + SUPPORTED_NOTE_ASSETS);
17
+ }
18
+ return format;
19
+ }
@@ -0,0 +1,193 @@
1
+ import { lookup } from 'node:dns/promises';
2
+ import { BlockList, isIP } from 'node:net';
3
+ import { Agent, request } from 'undici';
4
+ import { BadRequestError } from "../errors/http.js";
5
+ import { isCredentialFreeWebUrl } from "../../shared/utils/web-url.js";
6
+ // Stop connection attempts that cannot establish a socket promptly.
7
+ const CONNECTION_TIMEOUT_MS = 10000;
8
+ // Stop responses that send no headers or no body data.
9
+ const RESPONSE_IDLE_TIMEOUT_MS = 15000;
10
+ // Bound the size of response headers.
11
+ const MAX_HEADER_BYTES = 16384;
12
+ // Stop redirect chains and loops.
13
+ const MAX_REDIRECTS = 5;
14
+ // Only these statuses ask the client to follow a Location header.
15
+ const REDIRECT_STATUSES = [301, 302, 303, 307, 308];
16
+ // Network failures that mean the remote server cannot serve the attachment.
17
+ const UNREACHABLE_CODES = new Set([
18
+ 'ENOTFOUND',
19
+ 'ECONNREFUSED',
20
+ 'ECONNRESET',
21
+ 'EHOSTUNREACH',
22
+ 'ENETUNREACH',
23
+ 'ETIMEDOUT',
24
+ 'UND_ERR_SOCKET'
25
+ ]);
26
+ // Ask servers for the exact bytes: compressed bodies are not supported.
27
+ const REQUEST_HEADERS = {
28
+ 'accept-encoding': 'identity',
29
+ 'user-agent': 'githuman-note-assets'
30
+ };
31
+ // Exclude non-public IPv4 ranges before opening any socket.
32
+ const BLOCKED_IPV4 = new BlockList();
33
+ // IPv6 routing must be public and must exclude special-purpose ranges.
34
+ const PUBLIC_IPV6 = new BlockList();
35
+ const BLOCKED_IPV6 = new BlockList();
36
+ for (const [address, prefix] of [
37
+ ['0.0.0.0', 8],
38
+ ['10.0.0.0', 8],
39
+ ['100.64.0.0', 10],
40
+ ['127.0.0.0', 8],
41
+ ['169.254.0.0', 16],
42
+ ['172.16.0.0', 12],
43
+ ['192.0.0.0', 24],
44
+ ['192.0.2.0', 24],
45
+ ['192.88.99.0', 24],
46
+ ['192.168.0.0', 16],
47
+ ['198.18.0.0', 15],
48
+ ['198.51.100.0', 24],
49
+ ['203.0.113.0', 24],
50
+ ['224.0.0.0', 3]
51
+ ]) {
52
+ BLOCKED_IPV4.addSubnet(address, prefix, 'ipv4');
53
+ }
54
+ // Only globally routable IPv6 is eligible, with special-purpose ranges excluded.
55
+ PUBLIC_IPV6.addSubnet('2000::', 3, 'ipv6');
56
+ BLOCKED_IPV6.addSubnet('2001::', 23, 'ipv6');
57
+ BLOCKED_IPV6.addSubnet('2001:db8::', 32, 'ipv6');
58
+ BLOCKED_IPV6.addSubnet('2002::', 16, 'ipv6');
59
+ BLOCKED_IPV6.addSubnet('3fff::', 20, 'ipv6');
60
+ export function isPublicAssetAddress(address) {
61
+ const family = isIP(address);
62
+ if (family === 4)
63
+ return !BLOCKED_IPV4.check(address, 'ipv4');
64
+ return (family === 6 &&
65
+ PUBLIC_IPV6.check(address, 'ipv6') &&
66
+ !BLOCKED_IPV6.check(address, 'ipv6'));
67
+ }
68
+ /**
69
+ * Resolves a host name and refuses non-public results. The socket connects to
70
+ * the checked address, so no second lookup can change the target.
71
+ */
72
+ export function createPublicLookup(resolveHost = lookup) {
73
+ return (hostname, options, callback) => {
74
+ void (async () => {
75
+ let addresses;
76
+ try {
77
+ addresses = await resolveHost(hostname, { all: true, verbatim: true });
78
+ }
79
+ catch (error) {
80
+ callback(error instanceof Error ? error : new Error(String(error)), '');
81
+ return;
82
+ }
83
+ const first = addresses[0];
84
+ if (!first ||
85
+ addresses.some(item => !isPublicAssetAddress(item.address))) {
86
+ callback(new BadRequestError('Attachment address is not public'), '');
87
+ }
88
+ else if (options.all) {
89
+ callback(null, addresses);
90
+ }
91
+ else {
92
+ callback(null, first.address, first.family);
93
+ }
94
+ })();
95
+ };
96
+ }
97
+ function createAgent(resolveHost) {
98
+ return new Agent({
99
+ connect: {
100
+ lookup: createPublicLookup(resolveHost),
101
+ timeout: CONNECTION_TIMEOUT_MS
102
+ },
103
+ connectTimeout: CONNECTION_TIMEOUT_MS,
104
+ headersTimeout: RESPONSE_IDLE_TIMEOUT_MS,
105
+ bodyTimeout: RESPONSE_IDLE_TIMEOUT_MS,
106
+ maxHeaderSize: MAX_HEADER_BYTES
107
+ });
108
+ }
109
+ // Undici ignores an abort while it waits for a socket, so the wait ends here.
110
+ async function untilAborted(work, signal, discard) {
111
+ let abort;
112
+ const aborted = new Promise((_resolve, reject) => {
113
+ abort = () => reject(signal.reason);
114
+ signal.addEventListener('abort', abort, { once: true });
115
+ });
116
+ try {
117
+ return await Promise.race([work, aborted]);
118
+ }
119
+ catch (error) {
120
+ // A request that finishes after the abort still owns a socket.
121
+ work.then(discard, () => undefined);
122
+ throw error;
123
+ }
124
+ finally {
125
+ if (abort)
126
+ signal.removeEventListener('abort', abort);
127
+ }
128
+ }
129
+ let sharedAgent;
130
+ function parseAssetUrl(value, message, base) {
131
+ let url;
132
+ try {
133
+ url = new URL(value, base);
134
+ }
135
+ catch {
136
+ throw new BadRequestError(message);
137
+ }
138
+ if (!isCredentialFreeWebUrl(url)) {
139
+ throw new BadRequestError('Only public HTTP and HTTPS attachments are supported');
140
+ }
141
+ return url;
142
+ }
143
+ export async function remoteAssetRequest(source, method, signal, options = {}) {
144
+ const dispatcher = options.dispatcher ??
145
+ (options.resolveHost
146
+ ? createAgent(options.resolveHost)
147
+ : (sharedAgent ??= createAgent()));
148
+ let url = parseAssetUrl(source, 'Invalid attachment URL');
149
+ for (let redirects = 0;; redirects++) {
150
+ signal.throwIfAborted();
151
+ // The socket layer skips the lookup for IP literals, so check them here.
152
+ const host = url.hostname.replace(/^\[|\]$/g, '');
153
+ if (isIP(host) && !isPublicAssetAddress(host)) {
154
+ throw new BadRequestError('Attachment address is not public');
155
+ }
156
+ const { statusCode, headers, body } = await untilAborted(request(url, { method, signal, dispatcher, headers: REQUEST_HEADERS }), signal, late => late.body.destroy());
157
+ if (!REDIRECT_STATUSES.includes(statusCode)) {
158
+ return Object.assign(body, { statusCode, headers });
159
+ }
160
+ body.destroy();
161
+ if (redirects >= MAX_REDIRECTS) {
162
+ throw new BadRequestError('Too many attachment redirects');
163
+ }
164
+ const location = headers.location;
165
+ if (!location || Array.isArray(location)) {
166
+ throw new BadRequestError('Attachment redirect has no valid location');
167
+ }
168
+ const next = parseAssetUrl(location, 'Invalid attachment redirect', url);
169
+ if (url.protocol === 'https:' && next.protocol !== 'https:') {
170
+ throw new BadRequestError('Insecure attachment redirect');
171
+ }
172
+ url = next;
173
+ }
174
+ }
175
+ /** Maps transport failures to client errors; other errors pass through. */
176
+ export function translateRemoteAssetError(error) {
177
+ const code = typeof error === 'object' && error !== null && 'code' in error
178
+ ? error.code
179
+ : undefined;
180
+ if (error instanceof DOMException && error.name === 'TimeoutError') {
181
+ return new BadRequestError('Attachment download took too long');
182
+ }
183
+ if (code === 'UND_ERR_CONNECT_TIMEOUT') {
184
+ return new BadRequestError('Attachment connection timed out');
185
+ }
186
+ if (code === 'UND_ERR_HEADERS_TIMEOUT' || code === 'UND_ERR_BODY_TIMEOUT') {
187
+ return new BadRequestError('Attachment download stalled');
188
+ }
189
+ if (typeof code === 'string' && UNREACHABLE_CODES.has(code)) {
190
+ return new BadRequestError('Attachment server could not be reached');
191
+ }
192
+ return error;
193
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Runs `work` with a signal that aborts when the client closes the connection
3
+ * before the response ends. A normal finish does not abort the signal.
4
+ */
5
+ export async function withRequestSignal(reply, work) {
6
+ const controller = new AbortController();
7
+ const abort = () => {
8
+ if (!reply.raw.writableFinished)
9
+ controller.abort();
10
+ };
11
+ if (reply.raw.destroyed)
12
+ abort();
13
+ reply.raw.once('close', abort);
14
+ try {
15
+ return await work(controller.signal);
16
+ }
17
+ finally {
18
+ reply.raw.removeListener('close', abort);
19
+ }
20
+ }
@@ -0,0 +1,14 @@
1
+ export function hasErrorCode(error, ...codes) {
2
+ return (error !== null &&
3
+ typeof error === 'object' &&
4
+ 'code' in error &&
5
+ typeof error.code === 'string' &&
6
+ codes.includes(error.code));
7
+ }
8
+ export function isSystemError(error, code) {
9
+ return error instanceof Error && hasErrorCode(error, code);
10
+ }
11
+ // Git file reads treat a non-directory path component as a missing path.
12
+ export function isMissingPathError(error) {
13
+ return hasErrorCode(error, 'ENOENT', 'ENOTDIR');
14
+ }
@@ -37,6 +37,20 @@ export const FileSearchQuery = Type.Object({
37
37
  }))
38
38
  });
39
39
  export const ContextItem = Type.Union([
40
+ Type.Object({
41
+ kind: Type.Literal('note', { description: 'A project note.' }),
42
+ noteId: Type.String({ minLength: 1, description: 'Stable note UUID.' }),
43
+ storageId: Type.String({
44
+ minLength: 1,
45
+ description: 'Notes storage identity.'
46
+ }),
47
+ name: Type.String({
48
+ minLength: 1,
49
+ description: 'Display name when attached.'
50
+ })
51
+ }, {
52
+ description: 'The current Markdown of a note, including YAML properties.'
53
+ }),
40
54
  Type.Object({
41
55
  kind: Type.Literal('diff', { description: 'Live working-tree diff.' }),
42
56
  source: DiffSourceNameSchema,
@@ -10,5 +10,6 @@ export const SERVER_EVENT_TYPES = [
10
10
  'comment:created',
11
11
  'comment:updated',
12
12
  'comment:deleted',
13
+ 'notes:changed',
13
14
  'files:changed'
14
15
  ];
@@ -0,0 +1,57 @@
1
+ import { MAX_NOTE_ASSET_BYTES, MAX_NOTE_PDF_BYTES } from "./constants.js";
2
+ // Keep upload policy, response MIME types and asset identifiers in agreement.
3
+ export const NOTE_ASSET_FORMATS = [
4
+ // Animated PNG retains the same storage extension as existing PNG attachments.
5
+ {
6
+ extension: 'png',
7
+ mime: 'image/png',
8
+ label: 'PNG',
9
+ display: 'image',
10
+ maxBytes: MAX_NOTE_ASSET_BYTES,
11
+ aliases: ['apng']
12
+ },
13
+ {
14
+ extension: 'jpg',
15
+ mime: 'image/jpeg',
16
+ label: 'JPEG',
17
+ display: 'image',
18
+ maxBytes: MAX_NOTE_ASSET_BYTES,
19
+ aliases: ['jpeg']
20
+ },
21
+ {
22
+ extension: 'gif',
23
+ mime: 'image/gif',
24
+ label: 'GIF',
25
+ display: 'image',
26
+ maxBytes: MAX_NOTE_ASSET_BYTES,
27
+ aliases: []
28
+ },
29
+ {
30
+ extension: 'webp',
31
+ mime: 'image/webp',
32
+ label: 'WebP',
33
+ display: 'image',
34
+ maxBytes: MAX_NOTE_ASSET_BYTES,
35
+ aliases: []
36
+ },
37
+ {
38
+ extension: 'pdf',
39
+ mime: 'application/pdf',
40
+ label: 'PDF',
41
+ display: 'link',
42
+ maxBytes: MAX_NOTE_PDF_BYTES,
43
+ aliases: []
44
+ }
45
+ ];
46
+ // Existing content hashes and extensions remain valid across server upgrades.
47
+ export const NOTE_ASSET_ID_PATTERN = `^[a-f0-9]{64}\\.(${NOTE_ASSET_FORMATS.map(format => format.extension).join('|')})$`;
48
+ // Error messages follow the same allowlist as upload validation.
49
+ export const SUPPORTED_NOTE_ASSETS = NOTE_ASSET_FORMATS.map(format => format.label).join(', ');
50
+ // Extensions that identify an image attachment, including accepted aliases.
51
+ export const NOTE_IMAGE_EXTENSIONS = NOTE_ASSET_FORMATS.filter(format => format.display === 'image').flatMap(format => [format.extension, ...format.aliases]);
52
+ export function noteAssetFormat(extension) {
53
+ return NOTE_ASSET_FORMATS.find(format => format.extension === extension);
54
+ }
55
+ export function maxNoteAssetBytes(kind) {
56
+ return kind === 'pdf' ? MAX_NOTE_PDF_BYTES : MAX_NOTE_ASSET_BYTES;
57
+ }
@@ -0,0 +1,7 @@
1
+ // Bound image uploads in both the browser and the multipart parser.
2
+ export const MAX_NOTE_ASSET_BYTES = 10_000_000;
3
+ // PDF imports require confirmation above the smaller threshold.
4
+ export const NOTE_PDF_WARNING_BYTES = 50_000_000;
5
+ export const MAX_NOTE_PDF_BYTES = 100_000_000;
6
+ // This bounds useful downloads in one client operation.
7
+ export const MAX_NOTE_IMPORT_BYTES = 200_000_000;
@@ -0,0 +1,118 @@
1
+ import { isRecord } from "../utils/guards.js";
2
+ import { errorMessage } from "../utils/error-message.js";
3
+ import { isMap, isSeq, isScalar, parseDocument } from 'yaml';
4
+ export function splitNote(text) {
5
+ const bom = text.startsWith('\uFEFF') ? '\uFEFF' : '';
6
+ const source = text.slice(bom.length);
7
+ if (!/^---\r?\n/.test(source)) {
8
+ return {
9
+ head: bom,
10
+ body: source,
11
+ yaml: '',
12
+ eol: text.includes('\r\n') ? '\r\n' : '\n'
13
+ };
14
+ }
15
+ const match = source.match(/^---\r?\n([\s\S]*?)(?:^---[ \t]*(?:\r?\n|$))/m);
16
+ if (!match)
17
+ throw new Error('Unclosed YAML frontmatter');
18
+ return {
19
+ head: bom + match[0],
20
+ body: source.slice(match[0].length),
21
+ yaml: match[1] ?? '',
22
+ eol: match[0].includes('\r\n') ? '\r\n' : '\n'
23
+ };
24
+ }
25
+ export function noteMetadata(text) {
26
+ try {
27
+ const { doc } = parseNoteHeader(text);
28
+ const tags = doc.get('tags');
29
+ if (tags === undefined)
30
+ return { tags: [], error: null };
31
+ if (!isSeq(tags) ||
32
+ tags.items.some(item => !isScalar(item) || typeof item.value !== 'string')) {
33
+ throw new Error('tags must be a list of strings');
34
+ }
35
+ return {
36
+ tags: normalizeTags(tags.items.map(item => (isScalar(item) ? String(item.value) : ''))),
37
+ error: null
38
+ };
39
+ }
40
+ catch (error) {
41
+ return {
42
+ tags: [],
43
+ error: errorMessage(error)
44
+ };
45
+ }
46
+ }
47
+ export function normalizeTags(tags) {
48
+ return [...new Set(tags.map(tag => tag.trim()).filter(Boolean))];
49
+ }
50
+ export function setNoteTags(text, tags) {
51
+ const metadata = noteMetadata(text);
52
+ if (metadata.error)
53
+ throw new Error(metadata.error);
54
+ const next = normalizeTags(tags);
55
+ if (JSON.stringify(next) === JSON.stringify(metadata.tags))
56
+ return text;
57
+ const { parts, doc } = parseNoteHeader(text);
58
+ doc.set('tags', next);
59
+ return writeNoteHeader(text, parts, doc.toString());
60
+ }
61
+ function parseNoteHeader(text) {
62
+ const parts = splitNote(text);
63
+ const doc = parseDocument(parts.yaml);
64
+ if (doc.errors.length)
65
+ throw new Error(doc.errors[0]?.message);
66
+ if (doc.contents && !isMap(doc.contents)) {
67
+ throw new Error('Frontmatter must be a YAML mapping');
68
+ }
69
+ return { parts, doc };
70
+ }
71
+ export function noteProperties(text) {
72
+ const { doc } = parseNoteHeader(text);
73
+ if (!isMap(doc.contents))
74
+ return [];
75
+ const values = doc.toJS({ maxAliasCount: 100 });
76
+ if (!isRecord(values))
77
+ return [];
78
+ return doc.contents.items.map(item => {
79
+ if (!isScalar(item.key) || typeof item.key.value !== 'string') {
80
+ throw new Error('Property names must be strings');
81
+ }
82
+ const key = item.key.value;
83
+ return { key, value: values[key] };
84
+ });
85
+ }
86
+ function writeNoteHeader(text, parts, yaml) {
87
+ const bom = text.startsWith('\uFEFF') ? '\uFEFF' : '';
88
+ return (bom +
89
+ '---' +
90
+ parts.eol +
91
+ yaml.replace(/\n/g, parts.eol) +
92
+ '---' +
93
+ parts.eol +
94
+ parts.body);
95
+ }
96
+ export function setNoteProperty(text, key, value) {
97
+ if (!key.trim())
98
+ throw new Error('Property name must not be empty');
99
+ if (key === 'tags') {
100
+ if (!Array.isArray(value))
101
+ throw new Error('tags must be a list of strings');
102
+ return setNoteTags(text, value);
103
+ }
104
+ const { parts, doc } = parseNoteHeader(text);
105
+ const current = noteProperties(text).find(property => property.key === key);
106
+ if (current && JSON.stringify(current.value) === JSON.stringify(value)) {
107
+ return text;
108
+ }
109
+ doc.set(key, value);
110
+ return writeNoteHeader(text, parts, doc.toString());
111
+ }
112
+ export function removeNoteProperty(text, key) {
113
+ const { parts, doc } = parseNoteHeader(text);
114
+ if (!doc.has(key))
115
+ return text;
116
+ doc.delete(key);
117
+ return writeNoteHeader(text, parts, doc.toString());
118
+ }