kennel 0.2.3 → 0.2.6

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 (274) hide show
  1. package/README.md +23 -13
  2. package/dist/cli/dispatch.js +16 -3
  3. package/dist/cli/src/cli/dispatch.js +16 -3
  4. package/dist/cli/src/cli/usage.js +1 -1
  5. package/dist/cli/src/core/command.js +1 -0
  6. package/dist/cli/src/core/init.js +5 -5
  7. package/dist/cli/src/plugins/external.d.ts +28 -2
  8. package/dist/cli/src/plugins/external.js +77 -23
  9. package/dist/cli/src/site/server-plugins.d.ts +4 -9
  10. package/dist/cli/src/site/viewer.js +1 -0
  11. package/dist/cli/usage.js +1 -1
  12. package/dist/core/api-client.js +576 -10317
  13. package/dist/core/command.js +1 -0
  14. package/dist/core/core-folders.js +2313 -12088
  15. package/dist/core/init.js +5 -5
  16. package/dist/core/runtime.js +8237 -20713
  17. package/dist/package/preview.js +19617 -33352
  18. package/dist/plugins/external.d.ts +28 -2
  19. package/dist/plugins/external.js +77 -23
  20. package/dist/site/server-plugins.d.ts +4 -9
  21. package/dist/site/server-plugins.js +557 -10452
  22. package/dist/site/viewer.js +1 -0
  23. package/kernel/apps/web/package.json +2 -6
  24. package/kernel/apps/web/scripts/dev.mjs +53 -0
  25. package/kernel/apps/web/src/app.css +0 -3
  26. package/kernel/apps/web/src/external-plugins.ts +1 -1
  27. package/kernel/apps/web/src/folder-filter-icons.tsx +1 -1
  28. package/kernel/apps/web/src/folder-plugin-view.tsx +1 -1
  29. package/kernel/apps/web/src/main.tsx +0 -1
  30. package/kernel/apps/web/src/plugins.ts +1 -31
  31. package/kernel/apps/web/tsconfig.json +1 -2
  32. package/kernel/apps/web/vite.config.ts +32 -14
  33. package/kernel/packages/agents/package.json +1 -1
  34. package/kernel/packages/agents/src/agent-card.tsx +1 -1
  35. package/kernel/packages/agents/src/client.tsx +1 -1
  36. package/kernel/packages/agents/src/manifest.ts +1 -1
  37. package/kernel/packages/agents/src/server.ts +1 -1
  38. package/kernel/packages/agents/src/viewer.tsx +1 -1
  39. package/kernel/packages/api/package.json +1 -5
  40. package/kernel/packages/api/src/server/acp-live-plugin.ts +1 -1
  41. package/kernel/packages/api/src/server/api/create-agent-file.ts +2 -2
  42. package/kernel/packages/api/src/server/api/document-edit.ts +2 -2
  43. package/kernel/packages/api/src/server/api/open-path.ts +2 -2
  44. package/kernel/packages/api/src/server/folder-metadata-plugin.ts +17 -42
  45. package/kernel/packages/api/src/server/index.ts +0 -1
  46. package/kernel/packages/api/src/server/plugins.ts +52 -19
  47. package/kernel/packages/api/src/shared/viewer-route.ts +1 -1
  48. package/kernel/packages/api/test/core.test.ts +14 -26
  49. package/kernel/packages/markdown/README.md +1 -1
  50. package/kernel/packages/notes/package.json +1 -1
  51. package/kernel/packages/notes/src/client.tsx +1 -1
  52. package/kernel/packages/notes/src/manifest.ts +1 -1
  53. package/kernel/packages/notes/src/server.ts +1 -1
  54. package/kernel/packages/plugins/SKILL.md +39 -0
  55. package/kernel/{plugins/mail → packages/plugins}/package.json +53 -28
  56. package/kernel/packages/plugins/src/index.ts +52 -0
  57. package/kernel/packages/plugins/src/page.ts +17 -0
  58. package/kernel/{plugins/projects → packages/plugins}/tsconfig.json +5 -1
  59. package/kernel/packages/plugins/tsup.config.ts +25 -0
  60. package/kernel/packages/ui/src/components/content-issues.tsx +1 -1
  61. package/kernel/packages/ui/tsconfig.json +1 -1
  62. package/package.json +2 -3
  63. package/web/assets/{abnfDiagram-VRR7QNED-Bgfb1nEB.js → abnfDiagram-VRR7QNED-xd_g1uK3.js} +1 -1
  64. package/web/assets/app-CX20nNIa.js +2 -0
  65. package/web/assets/{app-options-D-RJiQ-X.js → app-options-CkPJE5ep.js} +1 -1
  66. package/web/assets/{app-view-header-BLvLq02T.js → app-view-header-CNk5KnA-.js} +1 -1
  67. package/web/assets/{arc-Dk_KI36B.js → arc-B0u5yBJA.js} +1 -1
  68. package/web/assets/{architectureDiagram-ZJ3FMSHR-NR_KhRCf.js → architectureDiagram-ZJ3FMSHR-DfxOAo8P.js} +1 -1
  69. package/web/assets/{ask-tray-TzeTcO6-.js → ask-tray-SscJecDY.js} +1 -1
  70. package/web/assets/{blockDiagram-677ZJIJ3-CkLkwx9C.js → blockDiagram-677ZJIJ3-Ci3Usxh6.js} +1 -1
  71. package/web/assets/{c4Diagram-LMCZKHZV-CFCUH8e_.js → c4Diagram-LMCZKHZV-BqgwIVgh.js} +1 -1
  72. package/web/assets/channel-X7WHyxxT.js +1 -0
  73. package/web/assets/{chunk-2Q5K7J3B-B_BPLWsW.js → chunk-2Q5K7J3B-CfX69l2U.js} +1 -1
  74. package/web/assets/{chunk-32BRIVSS-B1VB1hOy.js → chunk-32BRIVSS-CuFlzZkl.js} +1 -1
  75. package/web/assets/{chunk-5VM5RSS4-KFcYr8Co.js → chunk-5VM5RSS4-DidCMlJa.js} +1 -1
  76. package/web/assets/{chunk-EX3LRPZG-CpBX3kuK.js → chunk-EX3LRPZG-B5CDm1dS.js} +1 -1
  77. package/web/assets/{chunk-JWPE2WC7-BPSl40wG.js → chunk-JWPE2WC7-D5fGV_sH.js} +1 -1
  78. package/web/assets/{chunk-MOJQB5TN-D2RcYmoU.js → chunk-MOJQB5TN-ayhH5Q-H.js} +1 -1
  79. package/web/assets/{chunk-RYQCIY6F-m7Gyn-t3.js → chunk-RYQCIY6F-C4BfOOZn.js} +1 -1
  80. package/web/assets/{chunk-V7JOEXUC-TDvdopxS.js → chunk-V7JOEXUC-DpUEIM9V.js} +1 -1
  81. package/web/assets/{chunk-VR4S4FIN-DZTsG51a.js → chunk-VR4S4FIN-DoQuFFRh.js} +1 -1
  82. package/web/assets/{chunk-XXDRQBXY-Dp0--RE2.js → chunk-XXDRQBXY-CI9eiLwn.js} +1 -1
  83. package/web/assets/classDiagram-OUVF2IWQ-BjsLQ3dz.js +1 -0
  84. package/web/assets/classDiagram-v2-EOCWNBFH-BjsLQ3dz.js +1 -0
  85. package/web/assets/client-7OWw5kW2.js +6 -0
  86. package/web/assets/client-DYj_td4Q.js +17 -0
  87. package/web/assets/{client-j2kQmxsO.js → client-Dk-NBvJR.js} +4 -4
  88. package/web/assets/command-zkzH4hVz.js +38 -0
  89. package/web/assets/{copy-context-C0Uqsp-3.js → copy-context-D5oiWrYC.js} +1 -1
  90. package/web/assets/{cose-bilkent-JH36ORCC-CPu3PI8h.js → cose-bilkent-JH36ORCC-BHRk1mO3.js} +1 -1
  91. package/web/assets/{cynefin-VYW2F7L2-DWxftYAm.js → cynefin-VYW2F7L2-C1B09TRE.js} +1 -1
  92. package/web/assets/{cynefinDiagram-TSTJHNR4-DsmN2V-p.js → cynefinDiagram-TSTJHNR4-B27qXQbv.js} +1 -1
  93. package/web/assets/{dagre-VKFMJZFB-COEbApyY.js → dagre-VKFMJZFB-B1qEi32g.js} +1 -1
  94. package/web/assets/{diagram-FQU43EPY-0O9WGWKB.js → diagram-FQU43EPY-BMkkgADD.js} +1 -1
  95. package/web/assets/{diagram-G47NLZAW-CutEdMVh.js → diagram-G47NLZAW-Cb8_g1A5.js} +1 -1
  96. package/web/assets/{diagram-NH7WQ7WH-C9KZc3B2.js → diagram-NH7WQ7WH-B51wuc5m.js} +1 -1
  97. package/web/assets/{diagram-OA4YK3LP-D8kkPXbY.js → diagram-OA4YK3LP-xnsecbbI.js} +1 -1
  98. package/web/assets/{diagram-WEI45ONY-_YqiTaGP.js → diagram-WEI45ONY-DSZdyaAV.js} +1 -1
  99. package/web/assets/{document-Dgt5eZFj.js → document-Dc2dftTW.js} +3 -3
  100. package/web/assets/{ebnfDiagram-CCIWWBDH-JO-zQh6g.js → ebnfDiagram-CCIWWBDH-aTHmcUGy.js} +1 -1
  101. package/web/assets/{entities-C3wz7FCx.js → entities-CCUtpskY.js} +1 -1
  102. package/web/assets/{erDiagram-Q63AITRT-9UjQwJ4P.js → erDiagram-Q63AITRT-CUUPBcvo.js} +1 -1
  103. package/web/assets/{file-viewer-DZJ8nYnl.js → file-viewer-DJ3rsmef.js} +1 -1
  104. package/web/assets/{flowDiagram-23GEKE2U-Ca08tnYM.js → flowDiagram-23GEKE2U-aTQXx2Mh.js} +1 -1
  105. package/web/assets/{folder-image-BfGgxo7i.js → folder-image-Bnv43tzP.js} +1 -1
  106. package/web/assets/{folder-landing-qUKBCnG-.js → folder-landing-DCnzYXXY.js} +1 -1
  107. package/web/assets/{folder-list-BfFjv7rR.js → folder-list-DvVkVjiR.js} +1 -1
  108. package/web/assets/{folder-metadata-DCw8xzHk.js → folder-metadata-C9hMMzIH.js} +1 -1
  109. package/web/assets/{folder-mobile-bar-Ubz4babf.js → folder-mobile-bar-VoTL1GS0.js} +1 -1
  110. package/web/assets/folder-plugin-view-Cvd804ZU.js +6 -0
  111. package/web/assets/{ganttDiagram-NO4QXBWP-KnHzX5W3.js → ganttDiagram-NO4QXBWP-DiwdUp7u.js} +1 -1
  112. package/web/assets/{gitGraphDiagram-IHSO6WYX-Cm8woNtK.js → gitGraphDiagram-IHSO6WYX-BL2gG_IS.js} +1 -1
  113. package/web/assets/{index-CNyy98F8.js → index-CUTtMSyJ.js} +2 -2
  114. package/web/assets/index-CegvVQ1C.js +1 -0
  115. package/web/assets/index-Dwhf8F_U.css +1 -0
  116. package/web/assets/{infoDiagram-FWYZ7A6U-yyqmUtIb.js → infoDiagram-FWYZ7A6U-B3n0zZpt.js} +1 -1
  117. package/web/assets/{ishikawaDiagram-FXEZZL3T-BfvR_Zy4.js → ishikawaDiagram-FXEZZL3T-D7YytbEv.js} +1 -1
  118. package/web/assets/{journeyDiagram-5HDEW3XC-BKuw06HM.js → journeyDiagram-5HDEW3XC-BYwqEl3C.js} +1 -1
  119. package/web/assets/{kanban-definition-HUTT4EX6-n0010rmQ.js → kanban-definition-HUTT4EX6-BQHNJgXZ.js} +1 -1
  120. package/web/assets/{linear-C0d0lT8O.js → linear-BPiQCLNk.js} +1 -1
  121. package/web/assets/{mermaid.core-BDVbShyO.js → mermaid.core-CD-KvBjV.js} +4 -4
  122. package/web/assets/{metadata-KV0dpp4G.js → metadata-COJ7fZul.js} +1 -1
  123. package/web/assets/{mindmap-definition-LN4V7U3C--_0ks3GF.js → mindmap-definition-LN4V7U3C-C1L_gyoj.js} +1 -1
  124. package/web/assets/{pegDiagram-2B236MQR-CBoNIpKR.js → pegDiagram-2B236MQR-BDAfItqS.js} +1 -1
  125. package/web/assets/{pieDiagram-ENE6RG2P-MA5tkYh5.js → pieDiagram-ENE6RG2P-CkZYH3Vz.js} +1 -1
  126. package/web/assets/{popover-DBD7FF-W.js → popover-0oA2lLUV.js} +2 -2
  127. package/web/assets/{project-search-dialog-DM1IkSZu.js → project-search-dialog-B2oqMT6h.js} +1 -1
  128. package/web/assets/{quadrantDiagram-ABIIQ3AL-CWT1ryqF.js → quadrantDiagram-ABIIQ3AL-Dy8Al3YZ.js} +1 -1
  129. package/web/assets/{railroadDiagram-RFXS5EU6-ZiLB5FGi.js → railroadDiagram-RFXS5EU6-Dd9_yo7B.js} +1 -1
  130. package/web/assets/{requirementDiagram-TGXJPOKE-CQq9uneq.js → requirementDiagram-TGXJPOKE-DPhIUsbs.js} +1 -1
  131. package/web/assets/{sankeyDiagram-HTMAVEWB-qDG-0JqI.js → sankeyDiagram-HTMAVEWB-B2QkYtub.js} +1 -1
  132. package/web/assets/{search-DNrQA5fF.js → search-C1J0-xiB.js} +1 -1
  133. package/web/assets/select-CtXC_PLC.js +30 -0
  134. package/web/assets/{selection-popup-DmtTeRs2.js → selection-popup-qhlUFzGq.js} +1 -1
  135. package/web/assets/{sequenceDiagram-DBY2YBRQ-CiApurLO.js → sequenceDiagram-DBY2YBRQ-Bx7fcZDx.js} +1 -1
  136. package/web/assets/{sizeCapture-X5ZJPWSS-DZSR5QXm.js → sizeCapture-X5ZJPWSS-CIXIhl_A.js} +1 -1
  137. package/web/assets/{stateDiagram-2N3HPSRC-CjJJR9_z.js → stateDiagram-2N3HPSRC-DCrdAflB.js} +1 -1
  138. package/web/assets/stateDiagram-v2-6OUMAXLB-0KCFLYeQ.js +1 -0
  139. package/web/assets/{swimlanes-5IMT3BWC-CKmo1sg6.js → swimlanes-5IMT3BWC-DA2vc7iZ.js} +2 -2
  140. package/web/assets/swimlanesDiagram-G3AALYLV-CAw3AV84.js +8 -0
  141. package/web/assets/{timeline-definition-FHXFAJF6-D-NMwS3y.js → timeline-definition-FHXFAJF6-lKfJiV9V.js} +1 -1
  142. package/web/assets/{vennDiagram-L72KCM5P-CfL6LBWw.js → vennDiagram-L72KCM5P-DO1q2KzG.js} +1 -1
  143. package/web/assets/{view-context-gyDl_p2V.js → view-context-DXKM3-l1.js} +4 -4
  144. package/web/assets/{viewer-capabilities-Bpp8Uvcg.js → viewer-capabilities-Cig_xM9u.js} +1 -1
  145. package/web/assets/{wardleyDiagram-EHGQE667-DgldMxaj.js → wardleyDiagram-EHGQE667-Dal9LkuZ.js} +1 -1
  146. package/web/assets/{xychartDiagram-FW5EYKEG-CIoAIFX8.js → xychartDiagram-FW5EYKEG-B3dPLPII.js} +1 -1
  147. package/web/index.html +2 -2
  148. package/kernel/packages/api/test/partial-loading.test.ts +0 -43
  149. package/kernel/packages/folder-plugin/package.json +0 -50
  150. package/kernel/packages/folder-plugin/src/index.ts +0 -15
  151. package/kernel/packages/folder-plugin/tsconfig.json +0 -17
  152. package/kernel/plugins/calendar/SKILL.md +0 -173
  153. package/kernel/plugins/calendar/eslint.config.js +0 -10
  154. package/kernel/plugins/calendar/package.json +0 -73
  155. package/kernel/plugins/calendar/schema.json +0 -137
  156. package/kernel/plugins/calendar/src/calendar.tsx +0 -328
  157. package/kernel/plugins/calendar/src/client.tsx +0 -139
  158. package/kernel/plugins/calendar/src/context.ts +0 -48
  159. package/kernel/plugins/calendar/src/copy-button.tsx +0 -66
  160. package/kernel/plugins/calendar/src/event-dialog.tsx +0 -270
  161. package/kernel/plugins/calendar/src/index.ts +0 -4
  162. package/kernel/plugins/calendar/src/manifest.ts +0 -7
  163. package/kernel/plugins/calendar/src/parse.ts +0 -610
  164. package/kernel/plugins/calendar/src/server.ts +0 -160
  165. package/kernel/plugins/calendar/src/utils.ts +0 -80
  166. package/kernel/plugins/calendar/src/views.tsx +0 -418
  167. package/kernel/plugins/calendar/test/context.test.mjs +0 -60
  168. package/kernel/plugins/calendar/test/package.test.mjs +0 -43
  169. package/kernel/plugins/calendar/test/parse.test.mjs +0 -392
  170. package/kernel/plugins/calendar/test/server.test.mjs +0 -126
  171. package/kernel/plugins/calendar/tsconfig.json +0 -17
  172. package/kernel/plugins/mail/SKILL.md +0 -97
  173. package/kernel/plugins/mail/eslint.config.js +0 -10
  174. package/kernel/plugins/mail/src/api.ts +0 -359
  175. package/kernel/plugins/mail/src/bun-ffi.d.ts +0 -14
  176. package/kernel/plugins/mail/src/client.tsx +0 -14
  177. package/kernel/plugins/mail/src/flock.ts +0 -137
  178. package/kernel/plugins/mail/src/folders.ts +0 -37
  179. package/kernel/plugins/mail/src/history.ts +0 -485
  180. package/kernel/plugins/mail/src/index.ts +0 -3
  181. package/kernel/plugins/mail/src/mail-card.tsx +0 -123
  182. package/kernel/plugins/mail/src/mail-conversation.tsx +0 -153
  183. package/kernel/plugins/mail/src/mail-detail-page.tsx +0 -467
  184. package/kernel/plugins/mail/src/mail-hooks.ts +0 -252
  185. package/kernel/plugins/mail/src/mail-page.tsx +0 -109
  186. package/kernel/plugins/mail/src/mail-request.ts +0 -78
  187. package/kernel/plugins/mail/src/mail-send-box.tsx +0 -175
  188. package/kernel/plugins/mail/src/mailos.ts +0 -723
  189. package/kernel/plugins/mail/src/manifest.ts +0 -7
  190. package/kernel/plugins/mail/src/node.ts +0 -171
  191. package/kernel/plugins/mail/src/parse.ts +0 -139
  192. package/kernel/plugins/mail/src/server.ts +0 -125
  193. package/kernel/plugins/mail/src/types.ts +0 -10
  194. package/kernel/plugins/mail/src/viewer.tsx +0 -147
  195. package/kernel/plugins/mail/test/flock.test.mjs +0 -156
  196. package/kernel/plugins/mail/test/folders.test.mjs +0 -138
  197. package/kernel/plugins/mail/test/history.test.mjs +0 -460
  198. package/kernel/plugins/mail/test/mailos.test.mjs +0 -752
  199. package/kernel/plugins/mail/test/node.test.mjs +0 -207
  200. package/kernel/plugins/mail/test/package.test.mjs +0 -54
  201. package/kernel/plugins/mail/test/parse.test.mjs +0 -32
  202. package/kernel/plugins/mail/test/server.test.mjs +0 -116
  203. package/kernel/plugins/mail/test/viewer.test.mjs +0 -192
  204. package/kernel/plugins/mail/tsconfig.json +0 -17
  205. package/kernel/plugins/projects/SKILL.md +0 -73
  206. package/kernel/plugins/projects/eslint.config.js +0 -10
  207. package/kernel/plugins/projects/package.json +0 -70
  208. package/kernel/plugins/projects/src/client.tsx +0 -17
  209. package/kernel/plugins/projects/src/data/model.ts +0 -129
  210. package/kernel/plugins/projects/src/index.ts +0 -17
  211. package/kernel/plugins/projects/src/manifest.ts +0 -7
  212. package/kernel/plugins/projects/src/project-context.ts +0 -41
  213. package/kernel/plugins/projects/src/server.ts +0 -276
  214. package/kernel/plugins/projects/src/ui/project-card.tsx +0 -61
  215. package/kernel/plugins/projects/src/ui/projects-page.tsx +0 -102
  216. package/kernel/plugins/projects/test/package.test.mjs +0 -51
  217. package/kernel/plugins/tasks/SKILL.md +0 -341
  218. package/kernel/plugins/tasks/eslint.config.js +0 -10
  219. package/kernel/plugins/tasks/package.json +0 -74
  220. package/kernel/plugins/tasks/schema.json +0 -939
  221. package/kernel/plugins/tasks/src/api.ts +0 -212
  222. package/kernel/plugins/tasks/src/client.tsx +0 -23
  223. package/kernel/plugins/tasks/src/data/parse.ts +0 -544
  224. package/kernel/plugins/tasks/src/data/task-id.ts +0 -56
  225. package/kernel/plugins/tasks/src/data/todo-model.ts +0 -795
  226. package/kernel/plugins/tasks/src/index.ts +0 -34
  227. package/kernel/plugins/tasks/src/manifest.ts +0 -7
  228. package/kernel/plugins/tasks/src/pages/task-detail.tsx +0 -48
  229. package/kernel/plugins/tasks/src/pages/task-list.tsx +0 -88
  230. package/kernel/plugins/tasks/src/server.ts +0 -447
  231. package/kernel/plugins/tasks/src/styles.css +0 -9
  232. package/kernel/plugins/tasks/src/ui/compact-todo-list.tsx +0 -372
  233. package/kernel/plugins/tasks/src/ui/error-details.tsx +0 -78
  234. package/kernel/plugins/tasks/src/ui/task-page.tsx +0 -392
  235. package/kernel/plugins/tasks/src/ui/todo-agent-filter.tsx +0 -36
  236. package/kernel/plugins/tasks/src/ui/todo-filter-bar.tsx +0 -66
  237. package/kernel/plugins/tasks/src/ui/todo-item.tsx +0 -494
  238. package/kernel/plugins/tasks/src/ui/todo-status-filter.tsx +0 -60
  239. package/kernel/plugins/tasks/src/ui/todo-tag-filter.tsx +0 -48
  240. package/kernel/plugins/tasks/src/ui/todo-warnings.tsx +0 -106
  241. package/kernel/plugins/tasks/test/api.test.mjs +0 -99
  242. package/kernel/plugins/tasks/test/folder-sources.test.mjs +0 -46
  243. package/kernel/plugins/tasks/test/package.test.mjs +0 -54
  244. package/kernel/plugins/tasks/test/parse.test.mjs +0 -225
  245. package/kernel/plugins/tasks/test/server.test.mjs +0 -386
  246. package/kernel/plugins/tasks/tsconfig.json +0 -17
  247. package/web/assets/app-DoGr2Zi7.js +0 -2
  248. package/web/assets/channel-uMEjV8qc.js +0 -1
  249. package/web/assets/classDiagram-OUVF2IWQ-fbZocVor.js +0 -1
  250. package/web/assets/classDiagram-v2-EOCWNBFH-fbZocVor.js +0 -1
  251. package/web/assets/client-C6Nxdfif.js +0 -2
  252. package/web/assets/client-CXYfBko5.js +0 -17
  253. package/web/assets/client-CoKtOq-X.js +0 -7
  254. package/web/assets/client-DyRQz5RU.js +0 -53
  255. package/web/assets/client-OY6HUzuD.js +0 -98
  256. package/web/assets/client-YOYWZIFi.js +0 -34
  257. package/web/assets/command-BugZBGXE.js +0 -38
  258. package/web/assets/folder-plugin-view-DsWR1zmQ.js +0 -6
  259. package/web/assets/index-BaRnbYRw.js +0 -1
  260. package/web/assets/index-BexmLxIP.css +0 -1
  261. package/web/assets/index-DDirZMgx.js +0 -1
  262. package/web/assets/note-card-DBfX6gW1.js +0 -6
  263. package/web/assets/select-D8cqNgcp.js +0 -30
  264. package/web/assets/stateDiagram-v2-6OUMAXLB-BpeGSCu9.js +0 -1
  265. package/web/assets/swimlanesDiagram-G3AALYLV-D9RjLKb9.js +0 -8
  266. package/web/assets/use-folder-filter-LQyXns5Q.js +0 -1
  267. /package/kernel/packages/{folder-plugin → plugins}/eslint.config.js +0 -0
  268. /package/kernel/packages/{folder-plugin → plugins}/src/client.ts +0 -0
  269. /package/kernel/packages/{folder-plugin → plugins}/src/endpoint.ts +0 -0
  270. /package/kernel/packages/{folder-plugin → plugins}/src/server.ts +0 -0
  271. /package/kernel/packages/{folder-plugin → plugins}/src/subpath.ts +0 -0
  272. /package/kernel/packages/{folder-plugin → plugins}/src/types.ts +0 -0
  273. /package/kernel/packages/{folder-plugin → plugins}/src/validation.ts +0 -0
  274. /package/kernel/packages/{folder-plugin → plugins}/test/contract.test.mjs +0 -0
@@ -1,460 +0,0 @@
1
- import { createHash } from "node:crypto"
2
- import {
3
- mkdir,
4
- mkdtemp,
5
- readFile,
6
- rm,
7
- symlink,
8
- writeFile,
9
- } from "node:fs/promises"
10
- import { tmpdir } from "node:os"
11
- import { join } from "node:path"
12
- import { describe, expect, test } from "bun:test"
13
-
14
- import { deleteLocalDraftVerified, MailDeleteError } from "../src/history.ts"
15
-
16
- function sha256(content) {
17
- return createHash("sha256").update(content).digest("hex")
18
- }
19
-
20
- async function fixture() {
21
- const base = await mkdtemp(join(tmpdir(), "kennel-history-"))
22
- const mailDir = join(base, "drafts")
23
- await mkdir(mailDir, { recursive: true })
24
- return { base, mailDir }
25
- }
26
-
27
- async function writeDraft(mailDir, id, content) {
28
- const filePath = join(mailDir, `${id}.md`)
29
- await writeFile(filePath, content)
30
- return { filePath, revision: sha256(content), content }
31
- }
32
-
33
- async function cleanup(base) {
34
- await rm(base, { recursive: true, force: true })
35
- }
36
-
37
- describe("deleteLocalDraftVerified", () => {
38
- test("snapshots byte-exact history then deletes the draft", async () => {
39
- const { base, mailDir } = await fixture()
40
- try {
41
- const content = "---\nid: d_aaaaaa\nstatus: working\n---\n\nBody\n"
42
- const { filePath, revision } = await writeDraft(
43
- mailDir,
44
- "d_aaaaaa",
45
- content,
46
- )
47
-
48
- const outcome = await deleteLocalDraftVerified(
49
- { id: "d_aaaaaa", filePath, folder: mailDir, revision },
50
- mailDir,
51
- )
52
- expect(outcome).toBe("deleted")
53
-
54
- const snapshot = await readFile(
55
- join(mailDir, "history", "d_aaaaaa", `${revision}.md`),
56
- "utf8",
57
- )
58
- expect(snapshot).toBe(content)
59
- await expect(readFile(filePath, "utf8")).rejects.toThrow()
60
- } finally {
61
- await cleanup(base)
62
- }
63
- })
64
-
65
- test("uses MailOS's lock file layout .locks/<id>.md.lock", async () => {
66
- const { base, mailDir } = await fixture()
67
- try {
68
- const content = "---\nid: d_lockabc\nstatus: ready\n---\n\nBody\n"
69
- const { filePath, revision } = await writeDraft(
70
- mailDir,
71
- "d_lockabc",
72
- content,
73
- )
74
- await deleteLocalDraftVerified(
75
- { id: "d_lockabc", filePath, folder: mailDir, revision },
76
- mailDir,
77
- )
78
- // MailOS holds `.locks/<basename>.lock` with `<basename>` = "<id>.md".
79
- const lockPath = join(mailDir, ".locks", "d_lockabc.md.lock")
80
- await expect(readFile(lockPath, "utf8")).resolves.toBe("")
81
- } finally {
82
- await cleanup(base)
83
- }
84
- })
85
-
86
- test("refuses to delete when the file changed after the loaded revision", async () => {
87
- const { base, mailDir } = await fixture()
88
- try {
89
- const old = "---\nid: d_bbbbbb\nstatus: working\n---\n\nOld\n"
90
- const { filePath, revision } = await writeDraft(mailDir, "d_bbbbbb", old)
91
- await writeFile(
92
- filePath,
93
- "---\nid: d_bbbbbb\nstatus: working\n---\n\nNewer work\n",
94
- )
95
-
96
- await expect(
97
- deleteLocalDraftVerified(
98
- { id: "d_bbbbbb", filePath, folder: mailDir, revision },
99
- mailDir,
100
- ),
101
- ).rejects.toMatchObject({ name: "MailDeleteError", outcome: "changed" })
102
- // The newer work must be untouched.
103
- const current = await readFile(filePath, "utf8")
104
- expect(current).toContain("Newer work")
105
- } finally {
106
- await cleanup(base)
107
- }
108
- })
109
-
110
- test("does not unlink when the file is missing", async () => {
111
- const { base, mailDir } = await fixture()
112
- try {
113
- const { filePath } = await writeDraft(mailDir, "d_cccccc", "content")
114
- await rm(filePath)
115
- await expect(
116
- deleteLocalDraftVerified(
117
- {
118
- id: "d_cccccc",
119
- filePath,
120
- folder: mailDir,
121
- revision: sha256("content"),
122
- },
123
- mailDir,
124
- ),
125
- ).rejects.toMatchObject({ name: "MailDeleteError", outcome: "missing" })
126
- } finally {
127
- await cleanup(base)
128
- }
129
- })
130
-
131
- test("rejects traversal or invalid ids", async () => {
132
- const { base, mailDir } = await fixture()
133
- try {
134
- const { revision } = await writeDraft(mailDir, "d_dddddd", "x")
135
- for (const id of ["../d_dddddd", "a/b", "..", "."]) {
136
- await expect(
137
- deleteLocalDraftVerified(
138
- {
139
- id,
140
- filePath: join(mailDir, `${id}.md`),
141
- folder: mailDir,
142
- revision,
143
- },
144
- mailDir,
145
- ),
146
- ).rejects.toMatchObject({ name: "MailDeleteError", outcome: "invalid" })
147
- }
148
- } finally {
149
- await cleanup(base)
150
- }
151
- })
152
-
153
- test("rejects a draft path outside the mail folder", async () => {
154
- const { base, mailDir } = await fixture()
155
- try {
156
- const outside = join(base, "elsewhere.md")
157
- await writeFile(outside, "x")
158
- await expect(
159
- deleteLocalDraftVerified(
160
- {
161
- id: "d_eeeeee",
162
- filePath: outside,
163
- folder: mailDir,
164
- revision: sha256("x"),
165
- },
166
- mailDir,
167
- ),
168
- ).rejects.toMatchObject({ name: "MailDeleteError", outcome: "invalid" })
169
- } finally {
170
- await cleanup(base)
171
- }
172
- })
173
-
174
- test("rejects a symlinked draft file instead of following it", async () => {
175
- const { base, mailDir } = await fixture()
176
- try {
177
- const target = join(base, "secret.md")
178
- await writeFile(target, "outside content")
179
- const link = join(mailDir, "d_ffffff.md")
180
- await symlink(target, link)
181
-
182
- await expect(
183
- deleteLocalDraftVerified(
184
- {
185
- id: "d_ffffff",
186
- filePath: link,
187
- folder: mailDir,
188
- revision: sha256("outside content"),
189
- },
190
- mailDir,
191
- ),
192
- ).rejects.toMatchObject({ name: "MailDeleteError", outcome: "invalid" })
193
- // The symlink and its target both survive.
194
- await expect(readFile(link, "utf8")).resolves.toBe("outside content")
195
- await expect(readFile(target, "utf8")).resolves.toBe("outside content")
196
- } finally {
197
- await cleanup(base)
198
- }
199
- })
200
-
201
- test("rejects a symlinked history directory instead of following it", async () => {
202
- const { base, mailDir } = await fixture()
203
- try {
204
- const target = join(base, "elsewhere")
205
- await mkdir(target, { recursive: true })
206
- await symlink(target, join(mailDir, "history"))
207
-
208
- const content = "---\nid: d_histlink\nstatus: working\n---\n\nBody\n"
209
- const { filePath, revision } = await writeDraft(
210
- mailDir,
211
- "d_histlink",
212
- content,
213
- )
214
- await expect(
215
- deleteLocalDraftVerified(
216
- { id: "d_histlink", filePath, folder: mailDir, revision },
217
- mailDir,
218
- ),
219
- ).rejects.toMatchObject({ name: "MailDeleteError", outcome: "invalid" })
220
- // The draft survives and nothing was written through the symlink.
221
- await expect(readFile(filePath, "utf8")).resolves.toBe(content)
222
- await expect(
223
- readFile(join(target, `${revision}.md`), "utf8"),
224
- ).rejects.toThrow()
225
- } finally {
226
- await cleanup(base)
227
- }
228
- })
229
-
230
- test("refuses to overwrite corrupt existing history and keeps the draft", async () => {
231
- const { base, mailDir } = await fixture()
232
- try {
233
- const content = "---\nid: d_corrupt\nstatus: working\n---\n\nBody\n"
234
- const { filePath, revision } = await writeDraft(
235
- mailDir,
236
- "d_corrupt",
237
- content,
238
- )
239
- const historyDir = join(mailDir, "history", "d_corrupt")
240
- await mkdir(historyDir, { recursive: true })
241
- const historyPath = join(historyDir, `${revision}.md`)
242
- await writeFile(historyPath, "corrupted bytes")
243
-
244
- await expect(
245
- deleteLocalDraftVerified(
246
- { id: "d_corrupt", filePath, folder: mailDir, revision },
247
- mailDir,
248
- ),
249
- ).rejects.toMatchObject({ name: "MailDeleteError", outcome: "changed" })
250
- // Neither the draft nor the (corrupt) history is touched.
251
- await expect(readFile(filePath, "utf8")).resolves.toBe(content)
252
- await expect(readFile(historyPath, "utf8")).resolves.toBe(
253
- "corrupted bytes",
254
- )
255
- } finally {
256
- await cleanup(base)
257
- }
258
- })
259
-
260
- test("serializes concurrent deletes of the same draft through the real lock", async () => {
261
- const { base, mailDir } = await fixture()
262
- try {
263
- const content = "---\nid: d_111111\nstatus: working\n---\n\nBody\n"
264
- const { filePath, revision } = await writeDraft(
265
- mailDir,
266
- "d_111111",
267
- content,
268
- )
269
- const identity = { id: "d_111111", filePath, folder: mailDir, revision }
270
-
271
- const [first, second] = await Promise.allSettled([
272
- deleteLocalDraftVerified(identity, mailDir),
273
- deleteLocalDraftVerified(identity, mailDir),
274
- ])
275
- const outcomes = [first, second].map((result) =>
276
- result.status === "fulfilled" ? result.value : result.reason.outcome,
277
- )
278
- expect(outcomes.sort()).toEqual(["deleted", "missing"])
279
- // Exactly one byte-exact snapshot exists.
280
- const snapshot = await readFile(
281
- join(mailDir, "history", "d_111111", `${revision}.md`),
282
- "utf8",
283
- )
284
- expect(snapshot).toBe(content)
285
- } finally {
286
- await cleanup(base)
287
- }
288
- })
289
-
290
- test("waits for a lock held by another process, then deletes", async () => {
291
- const { base, mailDir } = await fixture()
292
- let child = null
293
- try {
294
- const content = "---\nid: d_proc\nstatus: ready\n---\n\nBody\n"
295
- const { filePath, revision } = await writeDraft(
296
- mailDir,
297
- "d_proc",
298
- content,
299
- )
300
- const lockPath = join(mailDir, ".locks", "d_proc.md.lock")
301
- // The child opens the lock file itself; openSync cannot create the
302
- // parent directory, so create the real `.locks` dir first (exactly as
303
- // deleteLocalDraftVerified's ensureRealDirChain would).
304
- await mkdir(join(mailDir, ".locks"), { recursive: true })
305
-
306
- // A separate OS process takes the MailOS lock for 800ms.
307
- const childScript = `
308
- import { openSync } from "node:fs"
309
- import { dlopen } from "bun:ffi"
310
- const lib = dlopen(process.platform === "darwin" ? "/usr/lib/libSystem.B.dylib" : "libc.so.6", { flock: { args: ["int", "int"], returns: "int" } })
311
- const flock = lib.symbols.flock
312
- const fd = openSync(${JSON.stringify(lockPath)}, "a+", 0o600)
313
- flock(fd, 2)
314
- console.log("CHILD_LOCKED")
315
- await Bun.sleep(800)
316
- flock(fd, 8)
317
- process.exit(0)
318
- `
319
- child = Bun.spawn({
320
- cmd: ["bun", "-e", childScript],
321
- cwd: import.meta.dirname,
322
- stdout: "pipe",
323
- stderr: "pipe",
324
- })
325
-
326
- // Wait until the foreign process actually holds the flock, otherwise
327
- // the delete could legitimately win the race. The decoded streamed
328
- // chunks are accumulated: byte-comparing the raw Uint8Array never
329
- // matches, and a marker split across chunks only matches once the text
330
- // accumulates. Bounded so a silent child cannot hang the suite.
331
- await waitForChildSignal(child, "CHILD_LOCKED")
332
-
333
- const startedAt = performance.now()
334
- const outcome = await deleteLocalDraftVerified(
335
- { id: "d_proc", filePath, folder: mailDir, revision },
336
- mailDir,
337
- )
338
- const elapsed = performance.now() - startedAt
339
-
340
- expect(outcome).toBe("deleted")
341
- // The delete had to wait for the foreign holder to release.
342
- expect(elapsed).toBeGreaterThanOrEqual(600)
343
- const snapshot = await readFile(
344
- join(mailDir, "history", "d_proc", `${revision}.md`),
345
- "utf8",
346
- )
347
- expect(snapshot).toBe(content)
348
- expect(await childExitedBounded(child)).toBe(0)
349
- } finally {
350
- if (child) killIfRunning(child)
351
- await cleanup(base)
352
- }
353
- })
354
-
355
- test("rejects a malformed revision", async () => {
356
- const { base, mailDir } = await fixture()
357
- try {
358
- const { filePath } = await writeDraft(mailDir, "d_222222", "x")
359
- await expect(
360
- deleteLocalDraftVerified(
361
- { id: "d_222222", filePath, folder: mailDir, revision: "not-a-hash" },
362
- mailDir,
363
- ),
364
- ).rejects.toMatchObject({ name: "MailDeleteError", outcome: "invalid" })
365
- } finally {
366
- await cleanup(base)
367
- }
368
- })
369
-
370
- test("delete is idempotent for an already-snapshotted revision", async () => {
371
- const { base, mailDir } = await fixture()
372
- try {
373
- const content = "---\nid: d_333333\nstatus: working\n---\n\nBody\n"
374
- const { filePath, revision } = await writeDraft(
375
- mailDir,
376
- "d_333333",
377
- content,
378
- )
379
- const identity = { id: "d_333333", filePath, folder: mailDir, revision }
380
- expect(await deleteLocalDraftVerified(identity, mailDir)).toBe("deleted")
381
- // Second attempt: no file, no duplicate snapshot overwrite concerns.
382
- await expect(
383
- deleteLocalDraftVerified(identity, mailDir),
384
- ).rejects.toMatchObject({
385
- outcome: "missing",
386
- })
387
- } finally {
388
- await cleanup(base)
389
- }
390
- })
391
- })
392
-
393
- test("MailDeleteError is an Error subclass", () => {
394
- const error = new MailDeleteError("changed", "nope")
395
- expect(error).toBeInstanceOf(Error)
396
- expect(error.outcome).toBe("changed")
397
- })
398
-
399
- /**
400
- * Read a spawned child's stdout stream and resolve once the decoded,
401
- * accumulated text contains `marker`. Chunks are decoded incrementally with a
402
- * streaming TextDecoder and concatenated; byte-comparing the Uint8Array or
403
- * skipping the accumulation step would never match across chunk boundaries.
404
- * Bounded so a silent child cannot hang the suite.
405
- */
406
- function waitForChildSignal(child, marker) {
407
- return new Promise((resolveSignal, rejectSignal) => {
408
- const reader = child.stdout.getReader()
409
- const decoder = new TextDecoder()
410
- let collected = ""
411
- const failTimer = setTimeout(
412
- () =>
413
- rejectSignal(new Error(`timeout waiting for child marker ${marker}`)),
414
- 10_000,
415
- )
416
- void (async () => {
417
- try {
418
- for (;;) {
419
- const { done, value } = await reader.read()
420
- if (done) break
421
- collected += decoder.decode(value, { stream: true })
422
- if (collected.includes(marker)) {
423
- clearTimeout(failTimer)
424
- resolveSignal()
425
- return
426
- }
427
- }
428
- clearTimeout(failTimer)
429
- rejectSignal(new Error(`child closed stdout without marker ${marker}`))
430
- } catch (error) {
431
- clearTimeout(failTimer)
432
- rejectSignal(error)
433
- }
434
- })()
435
- })
436
- }
437
-
438
- async function childExitedBounded(child, ms = 10_000) {
439
- return Promise.race([
440
- child.exited,
441
- new Promise((_, rejectExited) =>
442
- setTimeout(
443
- () => rejectExited(new Error("child did not exit in time")),
444
- ms,
445
- ),
446
- ),
447
- ]).catch(async (error) => {
448
- killIfRunning(child)
449
- await child.exited.catch(() => undefined)
450
- throw error
451
- })
452
- }
453
-
454
- function killIfRunning(child) {
455
- try {
456
- if (child.exitCode === null) child.kill()
457
- } catch {
458
- // Already gone.
459
- }
460
- }