fundus 0.0.2 → 0.0.4

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 (303) hide show
  1. package/README.md +73 -0
  2. package/build/client/_app/immutable/assets/0.DLhIsYA_.css.br +0 -0
  3. package/build/client/_app/immutable/assets/0.DLhIsYA_.css.gz +0 -0
  4. package/build/client/_app/immutable/assets/2.ohi-TClG.css +1 -0
  5. package/build/client/_app/immutable/assets/2.ohi-TClG.css.br +0 -0
  6. package/build/client/_app/immutable/assets/2.ohi-TClG.css.gz +0 -0
  7. package/build/client/_app/immutable/assets/3.JwgtcTEd.css +1 -0
  8. package/build/client/_app/immutable/assets/3.JwgtcTEd.css.br +0 -0
  9. package/build/client/_app/immutable/assets/3.JwgtcTEd.css.gz +0 -0
  10. package/build/client/_app/immutable/assets/4.p004hmEN.css +1 -0
  11. package/build/client/_app/immutable/assets/4.p004hmEN.css.br +0 -0
  12. package/build/client/_app/immutable/assets/4.p004hmEN.css.gz +0 -0
  13. package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css +1 -0
  14. package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css.br +0 -0
  15. package/build/client/_app/immutable/assets/ConfirmDialog.BdKlitwi.css.gz +0 -0
  16. package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css +1 -0
  17. package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css.br +0 -0
  18. package/build/client/_app/immutable/assets/LibraryContextMenu.CMefGNE2.css.gz +0 -0
  19. package/build/client/_app/immutable/chunks/Bjy-W4x2.js.br +0 -0
  20. package/build/client/_app/immutable/chunks/Bjy-W4x2.js.gz +0 -0
  21. package/build/client/_app/immutable/chunks/CNRz3sSw.js.br +0 -0
  22. package/build/client/_app/immutable/chunks/CNRz3sSw.js.gz +0 -0
  23. package/build/client/_app/immutable/chunks/CO4RBJfa.js.br +0 -0
  24. package/build/client/_app/immutable/chunks/CO4RBJfa.js.gz +0 -0
  25. package/build/client/_app/immutable/chunks/DOpBpjxS.js +108 -0
  26. package/build/client/_app/immutable/chunks/DOpBpjxS.js.br +0 -0
  27. package/build/client/_app/immutable/chunks/DOpBpjxS.js.gz +0 -0
  28. package/{dist/editor/_app/immutable/chunks/BfPE5wdt.js → build/client/_app/immutable/chunks/DVEQFclp.js} +1 -1
  29. package/build/client/_app/immutable/chunks/DVEQFclp.js.br +1 -0
  30. package/build/client/_app/immutable/chunks/DVEQFclp.js.gz +0 -0
  31. package/{dist/editor/_app/immutable/chunks/DjvOiDmq.js → build/client/_app/immutable/chunks/DiFoWL2c.js} +1 -1
  32. package/build/client/_app/immutable/chunks/DiFoWL2c.js.br +0 -0
  33. package/build/client/_app/immutable/chunks/DiFoWL2c.js.gz +0 -0
  34. package/build/client/_app/immutable/chunks/DkADHpZF.js +1 -0
  35. package/build/client/_app/immutable/chunks/DkADHpZF.js.br +0 -0
  36. package/build/client/_app/immutable/chunks/DkADHpZF.js.gz +0 -0
  37. package/build/client/_app/immutable/chunks/xihTtKlq.js.br +1 -0
  38. package/build/client/_app/immutable/chunks/xihTtKlq.js.gz +0 -0
  39. package/{dist/editor/_app/immutable/entry/app.BkqbfI8n.js → build/client/_app/immutable/entry/app.CKgtYXQb.js} +2 -2
  40. package/build/client/_app/immutable/entry/app.CKgtYXQb.js.br +0 -0
  41. package/build/client/_app/immutable/entry/app.CKgtYXQb.js.gz +0 -0
  42. package/build/client/_app/immutable/entry/start.CYZu5qE1.js +1 -0
  43. package/build/client/_app/immutable/entry/start.CYZu5qE1.js.br +0 -0
  44. package/build/client/_app/immutable/entry/start.CYZu5qE1.js.gz +0 -0
  45. package/{dist/editor/_app/immutable/nodes/0.xdMI5GEU.js → build/client/_app/immutable/nodes/0.I_1SApYQ.js} +1 -1
  46. package/build/client/_app/immutable/nodes/0.I_1SApYQ.js.br +0 -0
  47. package/build/client/_app/immutable/nodes/0.I_1SApYQ.js.gz +0 -0
  48. package/{dist/editor/_app/immutable/nodes/1.D9p2tfoJ.js → build/client/_app/immutable/nodes/1.v6eUTnWq.js} +1 -1
  49. package/build/client/_app/immutable/nodes/1.v6eUTnWq.js.br +2 -0
  50. package/build/client/_app/immutable/nodes/1.v6eUTnWq.js.gz +0 -0
  51. package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js +3 -0
  52. package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js.br +0 -0
  53. package/build/client/_app/immutable/nodes/2.DoA7Dfl-.js.gz +0 -0
  54. package/build/client/_app/immutable/nodes/3.C-DO70Bs.js +1 -0
  55. package/build/client/_app/immutable/nodes/3.C-DO70Bs.js.br +0 -0
  56. package/build/client/_app/immutable/nodes/3.C-DO70Bs.js.gz +0 -0
  57. package/build/client/_app/immutable/nodes/4.CALOuSyz.js +2 -0
  58. package/build/client/_app/immutable/nodes/4.CALOuSyz.js.br +0 -0
  59. package/build/client/_app/immutable/nodes/4.CALOuSyz.js.gz +0 -0
  60. package/build/client/_app/version.json +1 -0
  61. package/build/client/_app/version.json.br +0 -0
  62. package/build/client/_app/version.json.gz +0 -0
  63. package/build/env.js +100 -0
  64. package/build/env.js.map +1 -0
  65. package/build/handler.js +22 -0
  66. package/build/handler.js.map +1 -0
  67. package/build/index.js +145 -0
  68. package/build/index.js.map +1 -0
  69. package/build/server/chunks/chunks/context.js-BUZqFfWG.js +429 -0
  70. package/build/server/chunks/chunks/context.js-BUZqFfWG.js.map +1 -0
  71. package/build/server/chunks/chunks/core.js-xfrkaQpS.js +458 -0
  72. package/build/server/chunks/chunks/core.js-xfrkaQpS.js.map +1 -0
  73. package/build/server/chunks/chunks/env.js-l0sNRNKZ.js +2 -0
  74. package/build/server/chunks/chunks/env.js-l0sNRNKZ.js.map +1 -0
  75. package/build/server/chunks/chunks/exports.js-C0rwMMtk.js +152 -0
  76. package/build/server/chunks/chunks/exports.js-C0rwMMtk.js.map +1 -0
  77. package/build/server/chunks/chunks/folder-path.js-ytx9ucgg.js +64 -0
  78. package/build/server/chunks/chunks/folder-path.js-ytx9ucgg.js.map +1 -0
  79. package/build/server/chunks/chunks/http.js-DvopzwKh.js +100 -0
  80. package/build/server/chunks/chunks/http.js-DvopzwKh.js.map +1 -0
  81. package/build/server/chunks/chunks/internal.js-DGo2jPcg.js +5753 -0
  82. package/build/server/chunks/chunks/internal.js-DGo2jPcg.js.map +1 -0
  83. package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js +22 -0
  84. package/build/server/chunks/chunks/internal2.js-BlXt2rfE.js.map +1 -0
  85. package/{dist/operations-DYM-5KVl.js → build/server/chunks/chunks/library-store.js-ZLudCJlT.js} +463 -492
  86. package/build/server/chunks/chunks/library-store.js-ZLudCJlT.js.map +1 -0
  87. package/build/server/chunks/chunks/manifest-appearance.js-DTk355LQ.js +146 -0
  88. package/build/server/chunks/chunks/manifest-appearance.js-DTk355LQ.js.map +1 -0
  89. package/build/server/chunks/chunks/routing.js-y-tryuip.js +213 -0
  90. package/build/server/chunks/chunks/routing.js-y-tryuip.js.map +1 -0
  91. package/build/server/chunks/chunks/shared.js-B1gC9MiJ.js +255 -0
  92. package/build/server/chunks/chunks/shared.js-B1gC9MiJ.js.map +1 -0
  93. package/build/server/chunks/chunks/utils.js-DnEJ3UjO.js +1048 -0
  94. package/build/server/chunks/chunks/utils.js-DnEJ3UjO.js.map +1 -0
  95. package/build/server/chunks/entries/endpoints/api/_...path_/_server.ts.js-Cq5gXVQT.js +27 -0
  96. package/build/server/chunks/entries/endpoints/api/_...path_/_server.ts.js-Cq5gXVQT.js.map +1 -0
  97. package/build/server/chunks/entries/endpoints/api/assets/_id_/_server.ts.js-RXbedi8q.js +24 -0
  98. package/build/server/chunks/entries/endpoints/api/assets/_id_/_server.ts.js-RXbedi8q.js.map +1 -0
  99. package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-D7Qae9vU.js +45 -0
  100. package/build/server/chunks/entries/endpoints/api/assets/_server.ts.js-D7Qae9vU.js.map +1 -0
  101. package/build/server/chunks/entries/endpoints/api/assets/delete/_server.ts.js-DtO20kgB.js +31 -0
  102. package/build/server/chunks/entries/endpoints/api/assets/delete/_server.ts.js-DtO20kgB.js.map +1 -0
  103. package/build/server/chunks/entries/endpoints/api/assets/move/_server.ts.js-BUebPbd5.js +31 -0
  104. package/build/server/chunks/entries/endpoints/api/assets/move/_server.ts.js-BUebPbd5.js.map +1 -0
  105. package/build/server/chunks/entries/endpoints/api/folders/_path_/_server.ts.js-BjT2qPMT.js +38 -0
  106. package/build/server/chunks/entries/endpoints/api/folders/_path_/_server.ts.js-BjT2qPMT.js.map +1 -0
  107. package/build/server/chunks/entries/endpoints/api/folders/_server.ts.js-DYRLsoT7.js +31 -0
  108. package/build/server/chunks/entries/endpoints/api/folders/_server.ts.js-DYRLsoT7.js.map +1 -0
  109. package/build/server/chunks/entries/endpoints/api/manifests/_name_/_server.ts.js-BbN2fGNN.js +24 -0
  110. package/build/server/chunks/entries/endpoints/api/manifests/_name_/_server.ts.js-BbN2fGNN.js.map +1 -0
  111. package/build/server/chunks/entries/endpoints/api/manifests/_server.ts.js-b_MKZ41b.js +31 -0
  112. package/build/server/chunks/entries/endpoints/api/manifests/_server.ts.js-b_MKZ41b.js.map +1 -0
  113. package/build/server/chunks/entries/endpoints/api/state/_server.ts.js-evEfMpvn.js +24 -0
  114. package/build/server/chunks/entries/endpoints/api/state/_server.ts.js-evEfMpvn.js.map +1 -0
  115. package/build/server/chunks/entries/endpoints/files/_...path_/_server.ts.js-C04XE_1w.js +27 -0
  116. package/build/server/chunks/entries/endpoints/files/_...path_/_server.ts.js-C04XE_1w.js.map +1 -0
  117. package/build/server/chunks/entries/endpoints/files/proxy/_...path_/_server.ts.js-DB8TdoEv.js +24 -0
  118. package/build/server/chunks/entries/endpoints/files/proxy/_...path_/_server.ts.js-DB8TdoEv.js.map +1 -0
  119. package/build/server/chunks/entries/endpoints/files/raw/_...path_/_server.ts.js-BrpaijZO.js +24 -0
  120. package/build/server/chunks/entries/endpoints/files/raw/_...path_/_server.ts.js-BrpaijZO.js.map +1 -0
  121. package/build/server/chunks/entries/hooks.server.js-BJJwZW_I.js +23 -0
  122. package/build/server/chunks/entries/hooks.server.js-BJJwZW_I.js.map +1 -0
  123. package/build/server/chunks/handler-DnbS2t1B.js +1475 -0
  124. package/build/server/chunks/handler-DnbS2t1B.js.map +1 -0
  125. package/build/server/chunks/index.js-g77q3Boj.js +3789 -0
  126. package/build/server/chunks/index.js-g77q3Boj.js.map +1 -0
  127. package/build/server/chunks/manifest.js-qmEXbstS.js +149 -0
  128. package/build/server/chunks/manifest.js-qmEXbstS.js.map +1 -0
  129. package/build/server/chunks/nodes/0.js-Bk6IZ-vD.js +11 -0
  130. package/build/server/chunks/nodes/0.js-Bk6IZ-vD.js.map +1 -0
  131. package/build/server/chunks/nodes/1.js-H-qwo4GJ.js +7 -0
  132. package/build/server/chunks/nodes/1.js-H-qwo4GJ.js.map +1 -0
  133. package/build/server/chunks/nodes/2.js-DRpXVLwp.js +7 -0
  134. package/build/server/chunks/nodes/2.js-DRpXVLwp.js.map +1 -0
  135. package/build/server/chunks/nodes/3.js-CDEzNFeU.js +7 -0
  136. package/build/server/chunks/nodes/3.js-CDEzNFeU.js.map +1 -0
  137. package/build/server/chunks/nodes/4.js-BKL7LSSm.js +7 -0
  138. package/build/server/chunks/nodes/4.js-BKL7LSSm.js.map +1 -0
  139. package/build/shims.js +33 -0
  140. package/build/shims.js.map +1 -0
  141. package/dist/cli.d.ts +2 -1
  142. package/dist/cli.js +116 -1336
  143. package/dist/config.d.ts +3 -2
  144. package/dist/config.js +2 -2
  145. package/dist/core/api-types.d.ts +141 -0
  146. package/dist/core/api-types.js +1 -0
  147. package/dist/core/asset-id.d.ts +28 -0
  148. package/dist/core/asset-id.js +59 -0
  149. package/dist/core/asset-reference.d.ts +23 -0
  150. package/dist/core/asset-reference.js +15 -0
  151. package/dist/core/canonicalize.d.ts +9 -0
  152. package/dist/core/canonicalize.js +36 -0
  153. package/dist/core/chroma-key.d.ts +30 -0
  154. package/dist/core/chroma-key.js +55 -0
  155. package/dist/core/config.d.ts +35 -0
  156. package/dist/core/config.js +4 -0
  157. package/dist/core/folder-path.d.ts +19 -0
  158. package/dist/core/folder-path.js +86 -0
  159. package/dist/core/index.d.ts +33 -499
  160. package/dist/core/index.js +17 -962
  161. package/dist/core/ingest.d.ts +24 -0
  162. package/dist/core/ingest.js +42 -0
  163. package/dist/core/library.d.ts +54 -0
  164. package/dist/core/library.js +138 -0
  165. package/dist/core/manifest-appearance.d.ts +35 -0
  166. package/dist/core/manifest-appearance.js +123 -0
  167. package/dist/core/manifest-entry.d.ts +104 -0
  168. package/dist/core/manifest-entry.js +1 -0
  169. package/dist/core/manifest-membership.d.ts +56 -0
  170. package/dist/core/manifest-membership.js +54 -0
  171. package/dist/core/plugins/audio.d.ts +26 -0
  172. package/dist/core/plugins/audio.js +44 -0
  173. package/dist/core/plugins/chroma-key-video.d.ts +29 -0
  174. package/dist/core/plugins/chroma-key-video.js +69 -0
  175. package/dist/core/plugins/contract.d.ts +35 -0
  176. package/dist/core/plugins/contract.js +1 -0
  177. package/dist/core/plugins/image.d.ts +30 -0
  178. package/dist/core/plugins/image.js +45 -0
  179. package/dist/core/plugins/registry.d.ts +16 -0
  180. package/dist/core/plugins/registry.js +52 -0
  181. package/dist/core/plugins/slice.d.ts +22 -0
  182. package/dist/core/plugins/slice.js +60 -0
  183. package/dist/core/plugins/video.d.ts +45 -0
  184. package/dist/core/plugins/video.js +77 -0
  185. package/dist/core/reference-graph.d.ts +41 -0
  186. package/dist/core/reference-graph.js +138 -0
  187. package/dist/core/slice-data.d.ts +61 -0
  188. package/dist/core/slice-data.js +49 -0
  189. package/dist/runtime/chroma-key-gl.d.ts +40 -22
  190. package/dist/runtime/chroma-key-gl.js +189 -183
  191. package/dist/runtime/components/ChromaKeyVideo.svelte +3 -3
  192. package/dist/runtime/components/ChromaKeyVideo.svelte.d.ts +21 -0
  193. package/dist/runtime/components/Image.svelte +1 -1
  194. package/dist/runtime/components/Image.svelte.d.ts +9 -0
  195. package/dist/runtime/components/Slice.svelte +1 -1
  196. package/dist/runtime/components/Slice.svelte.d.ts +19 -0
  197. package/dist/runtime/components/SliceCanvas.svelte +3 -3
  198. package/dist/runtime/components/SliceCanvas.svelte.d.ts +10 -0
  199. package/dist/runtime/components/SliceDom.svelte +3 -3
  200. package/dist/runtime/components/SliceDom.svelte.d.ts +10 -0
  201. package/dist/runtime/components/Video.svelte +2 -2
  202. package/dist/runtime/components/Video.svelte.d.ts +18 -0
  203. package/dist/runtime/geometry.d.ts +55 -35
  204. package/dist/runtime/geometry.js +186 -191
  205. package/dist/runtime/index.d.ts +9 -9
  206. package/dist/runtime/index.js +9 -9
  207. package/dist/runtime/kind-handlers.d.ts +33 -30
  208. package/dist/runtime/kind-handlers.js +85 -67
  209. package/dist/runtime/manifest.d.ts +53 -47
  210. package/dist/runtime/manifest.js +133 -113
  211. package/dist/runtime/observe-border-box-size.d.ts +1 -4
  212. package/dist/runtime/observe-border-box-size.js +9 -10
  213. package/dist/runtime/preload-registry.d.ts +22 -8
  214. package/dist/runtime/preload-registry.js +65 -62
  215. package/dist/server/build.d.ts +8 -0
  216. package/dist/server/build.js +25 -0
  217. package/dist/server/check.d.ts +24 -0
  218. package/dist/server/check.js +199 -0
  219. package/dist/server/cli-commands.d.ts +25 -0
  220. package/dist/server/cli-commands.js +1178 -0
  221. package/dist/server/cli-init.d.ts +3 -0
  222. package/dist/server/cli-init.js +53 -0
  223. package/dist/server/cli-io.d.ts +46 -0
  224. package/dist/server/cli-io.js +105 -0
  225. package/dist/server/codegen.d.ts +58 -0
  226. package/dist/server/codegen.js +185 -0
  227. package/dist/server/config-loader.d.ts +35 -0
  228. package/dist/server/config-loader.js +79 -0
  229. package/dist/server/context.d.ts +8 -0
  230. package/dist/server/context.js +19 -0
  231. package/dist/server/folder-filesystem.d.ts +7 -0
  232. package/dist/server/folder-filesystem.js +39 -0
  233. package/dist/server/http.d.ts +12 -0
  234. package/dist/server/http.js +100 -0
  235. package/dist/server/index.d.ts +17 -0
  236. package/dist/server/index.js +9 -0
  237. package/dist/server/job-hash.d.ts +10 -0
  238. package/dist/server/job-hash.js +23 -0
  239. package/dist/server/library-store.d.ts +82 -0
  240. package/dist/server/library-store.js +763 -0
  241. package/dist/server/operations.d.ts +87 -0
  242. package/dist/server/operations.js +427 -0
  243. package/dist/server/plugins/audio.d.ts +8 -0
  244. package/dist/server/plugins/audio.js +41 -0
  245. package/dist/server/plugins/chroma-key-video.d.ts +9 -0
  246. package/dist/server/plugins/chroma-key-video.js +40 -0
  247. package/dist/server/plugins/config.d.ts +13 -0
  248. package/dist/server/plugins/config.js +32 -0
  249. package/dist/server/plugins/contract.d.ts +114 -0
  250. package/dist/server/plugins/contract.js +16 -0
  251. package/dist/server/plugins/image.d.ts +9 -0
  252. package/dist/server/plugins/image.js +43 -0
  253. package/dist/server/plugins/slice.d.ts +15 -0
  254. package/dist/server/plugins/slice.js +76 -0
  255. package/dist/server/plugins/video.d.ts +7 -0
  256. package/dist/server/plugins/video.js +54 -0
  257. package/dist/server/plugins.d.ts +23 -0
  258. package/dist/server/plugins.js +35 -0
  259. package/dist/server/processing/audio-processing.d.ts +17 -0
  260. package/dist/server/processing/audio-processing.js +46 -0
  261. package/dist/server/processing/ffmpeg.d.ts +9 -0
  262. package/dist/server/processing/ffmpeg.js +50 -0
  263. package/dist/server/processing/image-processing.d.ts +16 -0
  264. package/dist/server/processing/image-processing.js +26 -0
  265. package/dist/server/processing/media-probe.d.ts +15 -0
  266. package/dist/server/processing/media-probe.js +27 -0
  267. package/dist/server/processing/processor.d.ts +50 -0
  268. package/dist/server/processing/processor.js +158 -0
  269. package/dist/server/processing/video-processing.d.ts +35 -0
  270. package/dist/server/processing/video-processing.js +87 -0
  271. package/dist/server/server.d.ts +13 -0
  272. package/dist/server/server.js +57 -0
  273. package/dist/server/start.d.ts +13 -0
  274. package/dist/server/start.js +80 -0
  275. package/package.json +31 -16
  276. package/dist/build-CZYFLLvl.js +0 -191
  277. package/dist/core/image-Dqmz8FXS.js +0 -54
  278. package/dist/core/reference-graph-CjuHbLDw.d.ts +0 -280
  279. package/dist/core/testing.d.ts +0 -27
  280. package/dist/core/testing.js +0 -80
  281. package/dist/editor/200.html +0 -37
  282. package/dist/editor/_app/immutable/assets/2.DIbbW7U1.css +0 -1
  283. package/dist/editor/_app/immutable/assets/3.C-9vFQ9A.css +0 -1
  284. package/dist/editor/_app/immutable/assets/4.BS8ElpOV.css +0 -1
  285. package/dist/editor/_app/immutable/assets/ConfirmDialog.CHEISE-L.css +0 -1
  286. package/dist/editor/_app/immutable/assets/LibraryContextMenu.CDjCmdV-.css +0 -1
  287. package/dist/editor/_app/immutable/chunks/Cx6qtxJg.js +0 -1
  288. package/dist/editor/_app/immutable/chunks/DjdrdRzT.js +0 -1
  289. package/dist/editor/_app/immutable/chunks/n7mvYc42.js +0 -5
  290. package/dist/editor/_app/immutable/entry/start.C8HHOLly.js +0 -1
  291. package/dist/editor/_app/immutable/nodes/2.BrA0Hyzu.js +0 -3
  292. package/dist/editor/_app/immutable/nodes/3.WO7tZH_d.js +0 -104
  293. package/dist/editor/_app/immutable/nodes/4.DQtx17aW.js +0 -2
  294. package/dist/editor/_app/version.json +0 -1
  295. package/dist/index.d.ts +0 -370
  296. package/dist/index.js +0 -4
  297. package/dist/start-Bhnqt9Dc.js +0 -279
  298. package/dist/start-D2IXOrni.js +0 -2
  299. /package/{dist/editor → build/client}/_app/immutable/assets/0.DLhIsYA_.css +0 -0
  300. /package/{dist/editor → build/client}/_app/immutable/chunks/Bjy-W4x2.js +0 -0
  301. /package/{dist/editor → build/client}/_app/immutable/chunks/CNRz3sSw.js +0 -0
  302. /package/{dist/editor → build/client}/_app/immutable/chunks/CO4RBJfa.js +0 -0
  303. /package/{dist/editor → build/client}/_app/immutable/chunks/xihTtKlq.js +0 -0
@@ -0,0 +1,1178 @@
1
+ /**
2
+ * The state/asset/folder/manifest command groups of the fundus CLI (M7): thin
3
+ * terminal adapters over the one operations layer (operations.ts) — the same
4
+ * class the REST server exposes to the editor, so every mutation runs the
5
+ * editor's autosave chain (write library → process affected assets →
6
+ * regenerate manifest modules). Every command supports `--json` (a single
7
+ * JSON document on stdout, nothing else there) and `--help`; expected
8
+ * failures throw and cli.ts maps them to exit code 1.
9
+ */
10
+ import { readFile } from 'node:fs/promises';
11
+ import { basename, resolve } from 'node:path';
12
+ import { compareCodeUnits, folderNameOf, parentFolderPath, manifestEmojiError, MANIFEST_COLORS, MANIFEST_EMOJIS, proposeAssetId, resolveManifestColor, typesForFileName } from '../core/index.js';
13
+ import { runBuild } from './build.js';
14
+ import { formatCheckFinding, runCheck } from './check.js';
15
+ import { loadConfig } from './config-loader.js';
16
+ import { FundusOperations } from './operations.js';
17
+ import { pluginsByType, serverPluginForType } from './plugins.js';
18
+ import { CliUsageError, formatBytes, parseArguments, parseJsonFlag, parseManifestList, printJsonDocument, requirePositionals } from './cli-io.js';
19
+ const COMMON_FLAGS = {
20
+ json: { takesValue: false },
21
+ help: { takesValue: false }
22
+ };
23
+ function hasHelpFlag(argumentList) {
24
+ return argumentList.includes('--help');
25
+ }
26
+ async function createOperations() {
27
+ return new FundusOperations(await loadConfig(process.cwd()));
28
+ }
29
+ function manifestRows(state) {
30
+ return Object.keys(state.library.manifests)
31
+ .sort(compareCodeUnits)
32
+ .map((name) => ({
33
+ name,
34
+ color: state.library.manifests[name]?.color ?? null,
35
+ emoji: state.library.manifests[name]?.emoji ?? null,
36
+ explicitMemberIds: state.manifestMemberships[name]?.explicitMemberIds ?? [],
37
+ passiveMembers: state.manifestMemberships[name]?.passiveMembers ?? [],
38
+ deliveredBytes: state.manifestDeliveredBytes[name] ?? 0
39
+ }));
40
+ }
41
+ /** A manifest's `emoji name` prefix for human output (emoji only when set). */
42
+ function manifestLabel(row) {
43
+ return row.emoji ? `${row.emoji} ${row.name}` : row.name;
44
+ }
45
+ function assetById(state, id) {
46
+ const asset = state.assets.find((candidate) => candidate.id === id);
47
+ if (!asset) {
48
+ throw new Error(`Unknown asset "${id}".`);
49
+ }
50
+ return asset;
51
+ }
52
+ // ---------------------------------------------------------------------------
53
+ // Human output
54
+ // ---------------------------------------------------------------------------
55
+ function membershipSummary(asset) {
56
+ const parts = [];
57
+ if (asset.manifests.length > 0)
58
+ parts.push(asset.manifests.join(','));
59
+ if (asset.passiveManifests.length > 0) {
60
+ parts.push(`passive: ${asset.passiveManifests.map((membership) => membership.manifest).join(',')}`);
61
+ }
62
+ return parts.length > 0 ? parts.join(' + ') : '(no manifest)';
63
+ }
64
+ function printAssetLines(assets, indent = '') {
65
+ const idWidth = Math.max(0, ...assets.map((asset) => asset.id.length));
66
+ const typeWidth = Math.max(0, ...assets.map((asset) => asset.type.length));
67
+ for (const asset of assets) {
68
+ const warningSuffix = asset.warnings.length > 0 ? ` ⚠ ${asset.warnings.length} warning(s)` : '';
69
+ console.log(`${indent}${asset.id.padEnd(idWidth)} ${asset.type.padEnd(typeWidth)} ` +
70
+ `${membershipSummary(asset)}${warningSuffix}`);
71
+ }
72
+ }
73
+ function printIssues(issues) {
74
+ if (issues.length === 0)
75
+ return;
76
+ console.log('Issues:');
77
+ for (const issue of issues) {
78
+ console.log(` - ${issue.assetId ? `[${issue.assetId}] ` : ''}${issue.message}`);
79
+ }
80
+ }
81
+ function printHumanState(state) {
82
+ const rows = manifestRows(state);
83
+ console.log(`${state.assets.length} asset(s), ${state.library.folders.length} folder(s), ${rows.length} manifest(s)`);
84
+ if (rows.length > 0) {
85
+ console.log('');
86
+ console.log('Manifests:');
87
+ const labels = rows.map(manifestLabel);
88
+ const nameWidth = Math.max(0, ...labels.map((label) => label.length));
89
+ rows.forEach((row, index) => {
90
+ console.log(` ${labels[index].padEnd(nameWidth)} ${row.explicitMemberIds.length} explicit + ` +
91
+ `${row.passiveMembers.length} passive member(s), ${formatBytes(row.deliveredBytes)} delivered`);
92
+ });
93
+ }
94
+ if (state.assets.length > 0) {
95
+ console.log('');
96
+ console.log('Assets:');
97
+ printAssetLines(state.assets, ' ');
98
+ }
99
+ const warningTotal = state.assets.reduce((sum, asset) => sum + asset.warnings.length, 0);
100
+ if (warningTotal > 0) {
101
+ console.log('');
102
+ console.log(`⚠ ${warningTotal} warning(s) — see \`fundus asset list --warnings-only\`.`);
103
+ }
104
+ if (state.issues.length > 0) {
105
+ console.log('');
106
+ printIssues(state.issues);
107
+ }
108
+ }
109
+ function printHumanAssetDetail(asset) {
110
+ console.log(`${asset.id} (${asset.type})`);
111
+ console.log(` raw: ${asset.raw}`);
112
+ console.log(` folder: ${asset.folder || 'root'}`);
113
+ console.log(` rawHash: ${asset.rawHash}`);
114
+ console.log(` manifests: ${asset.manifests.length > 0 ? asset.manifests.join(', ') : '(none)'}`);
115
+ for (const membership of asset.passiveManifests) {
116
+ console.log(` passive: ${membership.manifest} (via ${membership.via.join(', ')})`);
117
+ }
118
+ if (asset.proxy) {
119
+ const dimensions = asset.proxy.width !== undefined && asset.proxy.height !== undefined
120
+ ? `, ${asset.proxy.width}×${asset.proxy.height}`
121
+ : '';
122
+ const duration = asset.proxy.durationMs !== undefined ? `, ${asset.proxy.durationMs} ms` : '';
123
+ console.log(` proxy: ${asset.proxy.relativePath} (${formatBytes(asset.proxy.fileSize)}${dimensions}${duration})`);
124
+ }
125
+ else {
126
+ console.log(' proxy: (none — not delivered or not processed yet)');
127
+ }
128
+ for (const reference of asset.references.outgoing) {
129
+ console.log(` reference: ${reference.field} → ${reference.assetId}`);
130
+ }
131
+ for (const reference of asset.references.incoming) {
132
+ console.log(` referenced: ← ${reference.assetId} (${reference.field})`);
133
+ }
134
+ console.log(' parameters:');
135
+ const parametersJson = JSON.stringify(asset.parameters, null, '\t');
136
+ for (const line of parametersJson.split('\n')) {
137
+ console.log(` ${line}`);
138
+ }
139
+ for (const warning of asset.warnings) {
140
+ console.log(` ⚠ ${warning}`);
141
+ }
142
+ }
143
+ /** Human output of a successful mutation: headline, asset warnings, issues. */
144
+ function printMutationResult(headline, asset, state) {
145
+ console.log(headline);
146
+ if (asset) {
147
+ for (const warning of asset.warnings) {
148
+ console.log(` ⚠ ${warning}`);
149
+ }
150
+ }
151
+ printIssues(state.issues);
152
+ }
153
+ // ---------------------------------------------------------------------------
154
+ // fundus state
155
+ // ---------------------------------------------------------------------------
156
+ export const STATE_HELP = `fundus state — the full project state in one document
157
+
158
+ Usage: fundus state [--json]
159
+
160
+ The agent's one-stop overview: every asset (type, raw file, parameters,
161
+ explicit + passive manifest memberships, proxy status, reference edges,
162
+ warnings), every manifest (members, delivered bytes), library-level
163
+ validation issues, and the available preset names per asset type.
164
+
165
+ Flags:
166
+ --json print {"ok": true, "state": <FundusStateDto>} on stdout
167
+ --help this help
168
+
169
+ Read-only: nothing is processed or written.
170
+
171
+ Example:
172
+ fundus state --json | jq '.state.assets[].id'
173
+ `;
174
+ export async function runStateCommand(argumentList) {
175
+ if (hasHelpFlag(argumentList)) {
176
+ console.log(STATE_HELP);
177
+ return;
178
+ }
179
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus state');
180
+ requirePositionals(parsed, [], 'fundus state');
181
+ const operations = await createOperations();
182
+ const state = await operations.state();
183
+ if (parsed.flags.json) {
184
+ printJsonDocument({ ok: true, state });
185
+ return;
186
+ }
187
+ printHumanState(state);
188
+ }
189
+ // ---------------------------------------------------------------------------
190
+ // fundus asset …
191
+ // ---------------------------------------------------------------------------
192
+ export const ASSET_HELP = `fundus asset — inspect and mutate assets
193
+
194
+ Usage: fundus asset <subcommand> …
195
+
196
+ Every mutation runs the full editor autosave chain: write library →
197
+ process affected assets → regenerate manifest modules. There is no
198
+ separate apply/sync step. All subcommands accept --json (a single JSON
199
+ document on stdout) and --help.
200
+
201
+ Subcommands:
202
+ list list assets (--manifest, --type, --warnings-only)
203
+ show one asset in detail
204
+ ingest copy a file into the raw dir as a typed asset
205
+ set merge parameters and/or replace manifest membership
206
+ rename rename an asset id
207
+ move move an asset to a folder or root
208
+ delete delete an asset (record, raw file, proxies)
209
+
210
+ Run \`fundus asset <subcommand> --help\` for the focused page (flags,
211
+ semantics, an example) — e.g. \`fundus asset set --help\` documents the
212
+ --parameters merge rules.
213
+ `;
214
+ export const ASSET_LIST_HELP = `fundus asset list — list assets, optionally filtered
215
+
216
+ Usage: fundus asset list [--manifest <name>] [--type <type>] [--warnings-only] [--json]
217
+
218
+ One line per asset: id, type, manifest memberships (explicit +
219
+ passive), warning count.
220
+
221
+ Flags:
222
+ --manifest <name> only members of this manifest (explicit or passive)
223
+ --type <type> only this asset type (${Object.keys(pluginsByType).sort().join(', ')})
224
+ --warnings-only only assets with at least one warning
225
+ --json print {"ok": true, "assets": [<AssetStateDto>, …]}
226
+ --help this help
227
+
228
+ Read-only: nothing is processed or written.
229
+
230
+ Example:
231
+ fundus asset list --manifest mainCatalog --json
232
+ `;
233
+ export const ASSET_SHOW_HELP = `fundus asset show — one asset in detail
234
+
235
+ Usage: fundus asset show <id> [--json]
236
+
237
+ Prints the asset's raw file, content hash, parameters, explicit +
238
+ passive manifest memberships, proxy status, reference edges (outgoing
239
+ and incoming), and warnings.
240
+
241
+ Flags:
242
+ --json print {"ok": true, "asset": <AssetStateDto>}
243
+ --help this help
244
+
245
+ Read-only: nothing is processed or written.
246
+
247
+ Example:
248
+ fundus asset show navigationPanel --json
249
+ `;
250
+ export const ASSET_INGEST_HELP = `fundus asset ingest — copy a file into the raw dir as a typed asset
251
+
252
+ Usage: fundus asset ingest <file> [--type <type>] [--id <id>] [--folder <path|root>]
253
+ [--manifests <a,b>] [--parameters <json>] [--json]
254
+
255
+ Behaves like an editor upload: the file is copied into the raw
256
+ directory (sanitized, non-colliding name) and content-hashed, and the
257
+ record starts with the owning plugin's default parameters — a slice
258
+ additionally seeds insets from the image dimensions and a pixel ratio
259
+ from a @2x-style file-name suffix. Runs the full autosave chain
260
+ (write library → process → regenerate manifest modules).
261
+
262
+ Flags:
263
+ --type <type> required when the file extension is ambiguous
264
+ (.png could be image or slice, .mp4 video or
265
+ chroma-key-video); optional when exactly one
266
+ type accepts it (.wav → audio); known types:
267
+ ${Object.keys(pluginsByType).sort().join(', ')}
268
+ --id <id> asset id (camelCase identifier); default:
269
+ proposed from the file name
270
+ (panel_button@2x.png → sidebarPanel)
271
+ --folder <path|root> destination folder; default: root
272
+ --manifests <a,b> explicit manifest membership, applied in the
273
+ same operation (comma-separated; every name
274
+ must exist)
275
+ --parameters <json> parameter patch merged onto the plugin defaults
276
+ — same shallow-merge semantics as \`asset set\`
277
+ (see \`fundus asset set --help\`)
278
+ --json print {"ok": true, "asset": <AssetStateDto>, "issues": […]}
279
+ --help this help
280
+
281
+ A failed ingest (invalid patch, unknown manifest, …) leaves nothing
282
+ behind: the raw file and record are rolled back.
283
+
284
+ Example:
285
+ fundus asset ingest ./exports/navigation_panel@2x.png --type slice \\
286
+ --manifests mainCatalog --json
287
+ `;
288
+ export const ASSET_SET_HELP = `fundus asset set — update an asset's parameters and/or memberships
289
+
290
+ Usage: fundus asset set <id> [--parameters <json>] [--manifests <a,b>] [--json]
291
+
292
+ At least one of --parameters/--manifests is required. Runs the full
293
+ autosave chain (write library → process → regenerate manifest modules).
294
+
295
+ Flags:
296
+ --parameters <json> SHALLOW MERGE at the top level onto the current
297
+ parameters, then plugin validation:
298
+ {"preset": "webp-hq"} sets one field,
299
+ keeps the others
300
+ {"chromaKey": {…}} replaces the whole
301
+ chromaKey object
302
+ {"mask": null} clears an optional
303
+ field
304
+ {"mask": {"$asset": "someImage"}}
305
+ sets an asset
306
+ reference
307
+ --manifests <a,b> REPLACES the explicit membership set (declarative,
308
+ idempotent); --manifests "" clears it
309
+ --json print {"ok": true, "asset": <AssetStateDto>, "issues": […]}
310
+ --help this help
311
+
312
+ The mutation is validated first and rejected (exit 1, nothing written)
313
+ if it would leave the library invalid — e.g. a reference to a missing
314
+ asset, a reference cycle, or an unknown manifest name. Ingest reference
315
+ targets before pointing at them.
316
+
317
+ Example:
318
+ fundus asset set characterIdle \\
319
+ --parameters '{"mask": {"$asset": "characterIdleMask"}}' --json
320
+ `;
321
+ export const ASSET_RENAME_HELP = `fundus asset rename — rename an asset id
322
+
323
+ Usage: fundus asset rename <id> <newId> [--json]
324
+
325
+ Renames the asset's id (the raw file keeps its name — ids are the
326
+ stable key) and re-hashes its proxy under the new id via the autosave
327
+ chain. Fails if another asset references <id> — update the referrers
328
+ first — or if <newId> is taken or invalid.
329
+
330
+ Flags:
331
+ --json print {"ok": true, "asset": <AssetStateDto>, "issues": […]}
332
+ --help this help
333
+
334
+ Example:
335
+ fundus asset rename sidebarPanel navigationPanel --json
336
+ `;
337
+ export const ASSET_DELETE_HELP = `fundus asset delete — delete an asset
338
+
339
+ Usage: fundus asset delete <id> [--json]
340
+
341
+ Deletes the library record, its raw file, and its proxies, then
342
+ regenerates the affected manifest modules. Fails if another asset
343
+ references it — remove the references first.
344
+
345
+ Flags:
346
+ --json print {"ok": true, "deletedAssetId": "<id>", "issues": […]}
347
+ --help this help
348
+
349
+ Example:
350
+ fundus asset delete obsoleteBanner --json
351
+ `;
352
+ export const ASSET_MOVE_HELP = `fundus asset move — move an asset between folders
353
+
354
+ Usage: fundus asset move <id> <folder|root> [--json]
355
+
356
+ Moves the raw file and its current proxy without changing the stable asset id,
357
+ then runs the autosave chain. The destination folder must already exist; use
358
+ the literal "root" for the implicit root.
359
+
360
+ Flags:
361
+ --json print {"ok": true, "asset": <AssetStateDto>, "issues": […]}
362
+ --help this help
363
+
364
+ Example:
365
+ fundus asset move navigationPanel "UI/Buttons" --json
366
+ `;
367
+ const ASSET_SUBCOMMAND_HELP = {
368
+ list: ASSET_LIST_HELP,
369
+ show: ASSET_SHOW_HELP,
370
+ ingest: ASSET_INGEST_HELP,
371
+ set: ASSET_SET_HELP,
372
+ rename: ASSET_RENAME_HELP,
373
+ move: ASSET_MOVE_HELP,
374
+ delete: ASSET_DELETE_HELP
375
+ };
376
+ async function runAssetList(argumentList) {
377
+ const parsed = parseArguments(argumentList, {
378
+ ...COMMON_FLAGS,
379
+ manifest: { takesValue: true },
380
+ type: { takesValue: true },
381
+ 'warnings-only': { takesValue: false }
382
+ }, 'fundus asset list');
383
+ requirePositionals(parsed, [], 'fundus asset list');
384
+ const typeFilter = parsed.flags.type;
385
+ if (typeof typeFilter === 'string')
386
+ serverPluginForType(typeFilter);
387
+ const operations = await createOperations();
388
+ const state = await operations.state();
389
+ const manifestFilter = parsed.flags.manifest;
390
+ if (typeof manifestFilter === 'string' &&
391
+ !Object.hasOwn(state.library.manifests, manifestFilter)) {
392
+ throw new Error(`Unknown manifest "${manifestFilter}".`);
393
+ }
394
+ const assets = state.assets.filter((asset) => {
395
+ if (typeof typeFilter === 'string' && asset.type !== typeFilter)
396
+ return false;
397
+ if (typeof manifestFilter === 'string' &&
398
+ !asset.manifests.includes(manifestFilter) &&
399
+ !asset.passiveManifests.some((membership) => membership.manifest === manifestFilter)) {
400
+ return false;
401
+ }
402
+ if (parsed.flags['warnings-only'] && asset.warnings.length === 0)
403
+ return false;
404
+ return true;
405
+ });
406
+ if (parsed.flags.json) {
407
+ printJsonDocument({ ok: true, assets });
408
+ return;
409
+ }
410
+ if (assets.length === 0) {
411
+ console.log('No matching assets.');
412
+ return;
413
+ }
414
+ printAssetLines(assets);
415
+ }
416
+ async function runAssetShow(argumentList) {
417
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus asset show');
418
+ const [id] = requirePositionals(parsed, ['<id>'], 'fundus asset show');
419
+ const operations = await createOperations();
420
+ const state = await operations.state();
421
+ const asset = assetById(state, id);
422
+ if (parsed.flags.json) {
423
+ printJsonDocument({ ok: true, asset });
424
+ return;
425
+ }
426
+ printHumanAssetDetail(asset);
427
+ }
428
+ /**
429
+ * Infer the asset type from the file extension: unambiguous → that type;
430
+ * ambiguous (png: image/slice, mp4: video/chroma-key-video) → `--type` is
431
+ * required; unknown → list what the plugins accept.
432
+ */
433
+ function inferTypeForFileName(fileName) {
434
+ const candidates = typesForFileName(fileName);
435
+ if (candidates.length === 1)
436
+ return candidates[0];
437
+ const extension = fileName.slice(fileName.lastIndexOf('.') + 1).toLowerCase();
438
+ if (candidates.length === 0) {
439
+ const knownExtensions = [
440
+ ...new Set(Object.values(pluginsByType).flatMap((plugin) => plugin.accepts))
441
+ ]
442
+ .sort()
443
+ .join(', ');
444
+ throw new Error(`No asset type accepts ".${extension}" files (accepted extensions: ${knownExtensions}).`);
445
+ }
446
+ throw new Error(`The ".${extension}" extension is ambiguous — pass --type <${candidates.join('|')}>.`);
447
+ }
448
+ async function runAssetIngest(argumentList) {
449
+ const parsed = parseArguments(argumentList, {
450
+ ...COMMON_FLAGS,
451
+ type: { takesValue: true },
452
+ id: { takesValue: true },
453
+ manifests: { takesValue: true },
454
+ parameters: { takesValue: true },
455
+ folder: { takesValue: true }
456
+ }, 'fundus asset ingest');
457
+ const [filePath] = requirePositionals(parsed, ['<file>'], 'fundus asset ingest');
458
+ const absolutePath = resolve(process.cwd(), filePath);
459
+ let bytes;
460
+ try {
461
+ bytes = new Uint8Array(await readFile(absolutePath));
462
+ }
463
+ catch {
464
+ throw new Error(`Cannot read file "${filePath}".`);
465
+ }
466
+ const fileName = basename(absolutePath);
467
+ const type = typeof parsed.flags.type === 'string' ? parsed.flags.type : inferTypeForFileName(fileName);
468
+ const id = typeof parsed.flags.id === 'string' ? parsed.flags.id : proposeAssetId(fileName);
469
+ const parametersPatch = typeof parsed.flags.parameters === 'string'
470
+ ? parseJsonFlag('parameters', parsed.flags.parameters)
471
+ : undefined;
472
+ const manifests = typeof parsed.flags.manifests === 'string'
473
+ ? parseManifestList(parsed.flags.manifests)
474
+ : undefined;
475
+ const folder = typeof parsed.flags.folder === 'string' ? parseFolderToken(parsed.flags.folder) : '';
476
+ const operations = await createOperations();
477
+ const state = await operations.ingest({
478
+ fileName,
479
+ bytes,
480
+ type,
481
+ id,
482
+ folder,
483
+ parametersPatch,
484
+ manifests
485
+ });
486
+ const asset = assetById(state, id);
487
+ if (parsed.flags.json) {
488
+ printJsonDocument({ ok: true, asset, issues: state.issues });
489
+ return;
490
+ }
491
+ printMutationResult(`Ingested "${fileName}" as ${type} asset "${id}".`, asset, state);
492
+ }
493
+ async function runAssetSet(argumentList) {
494
+ const parsed = parseArguments(argumentList, { ...COMMON_FLAGS, parameters: { takesValue: true }, manifests: { takesValue: true } }, 'fundus asset set');
495
+ const [id] = requirePositionals(parsed, ['<id>'], 'fundus asset set');
496
+ if (parsed.flags.parameters === undefined && parsed.flags.manifests === undefined) {
497
+ throw new CliUsageError('`fundus asset set` needs --parameters and/or --manifests — run `fundus asset set --help`.');
498
+ }
499
+ const parametersPatch = typeof parsed.flags.parameters === 'string'
500
+ ? parseJsonFlag('parameters', parsed.flags.parameters)
501
+ : undefined;
502
+ const manifests = typeof parsed.flags.manifests === 'string'
503
+ ? parseManifestList(parsed.flags.manifests)
504
+ : undefined;
505
+ const operations = await createOperations();
506
+ const state = await operations.setAsset(id, { parametersPatch, manifests });
507
+ const asset = assetById(state, id);
508
+ if (parsed.flags.json) {
509
+ printJsonDocument({ ok: true, asset, issues: state.issues });
510
+ return;
511
+ }
512
+ printMutationResult(`Updated asset "${id}".`, asset, state);
513
+ }
514
+ async function runAssetRename(argumentList) {
515
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus asset rename');
516
+ const [id, newId] = requirePositionals(parsed, ['<id>', '<newId>'], 'fundus asset rename');
517
+ const operations = await createOperations();
518
+ const state = await operations.renameAsset(id, newId);
519
+ const asset = assetById(state, newId);
520
+ if (parsed.flags.json) {
521
+ printJsonDocument({ ok: true, asset, issues: state.issues });
522
+ return;
523
+ }
524
+ printMutationResult(`Renamed asset "${id}" to "${newId}".`, asset, state);
525
+ }
526
+ function parseFolderToken(token) {
527
+ return token.toLocaleLowerCase('en-US') === 'root' ? '' : token;
528
+ }
529
+ async function runAssetMove(argumentList) {
530
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus asset move');
531
+ const [id, folderToken] = requirePositionals(parsed, ['<id>', '<folder|root>'], 'fundus asset move');
532
+ const operations = await createOperations();
533
+ const state = await operations.moveAsset(id, parseFolderToken(folderToken));
534
+ const asset = assetById(state, id);
535
+ if (parsed.flags.json) {
536
+ printJsonDocument({ ok: true, asset, issues: state.issues });
537
+ return;
538
+ }
539
+ printMutationResult(`Moved asset "${id}" to "${asset.folder || 'root'}".`, asset, state);
540
+ }
541
+ async function runAssetDelete(argumentList) {
542
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus asset delete');
543
+ const [id] = requirePositionals(parsed, ['<id>'], 'fundus asset delete');
544
+ const operations = await createOperations();
545
+ const state = await operations.deleteAsset(id, { failOnIntroducedIssues: true });
546
+ if (parsed.flags.json) {
547
+ printJsonDocument({ ok: true, deletedAssetId: id, issues: state.issues });
548
+ return;
549
+ }
550
+ printMutationResult(`Deleted asset "${id}" (record, raw file, proxies).`, null, state);
551
+ }
552
+ export async function runAssetCommand(argumentList) {
553
+ const [subcommand, ...rest] = argumentList;
554
+ if (subcommand === undefined || subcommand === '--help') {
555
+ console.log(ASSET_HELP);
556
+ if (subcommand === undefined) {
557
+ throw new CliUsageError('`fundus asset` needs a subcommand: list, show, ingest, set, rename, move, delete.');
558
+ }
559
+ return;
560
+ }
561
+ if (hasHelpFlag(rest)) {
562
+ // The focused per-subcommand page; the group overview for unknown ones.
563
+ console.log(ASSET_SUBCOMMAND_HELP[subcommand] ?? ASSET_HELP);
564
+ return;
565
+ }
566
+ switch (subcommand) {
567
+ case 'list':
568
+ return runAssetList(rest);
569
+ case 'show':
570
+ return runAssetShow(rest);
571
+ case 'ingest':
572
+ return runAssetIngest(rest);
573
+ case 'set':
574
+ return runAssetSet(rest);
575
+ case 'rename':
576
+ return runAssetRename(rest);
577
+ case 'move':
578
+ return runAssetMove(rest);
579
+ case 'delete':
580
+ return runAssetDelete(rest);
581
+ default:
582
+ throw new CliUsageError(`Unknown subcommand "asset ${subcommand}" — run \`fundus asset --help\`.`);
583
+ }
584
+ }
585
+ function folderRows(state) {
586
+ return [...state.library.folders].sort(compareCodeUnits).map((path) => ({
587
+ path,
588
+ name: folderNameOf(path),
589
+ parent: parentFolderPath(path),
590
+ directAssetIds: state.assets
591
+ .filter((asset) => asset.folder === path)
592
+ .map((asset) => asset.id)
593
+ .sort(compareCodeUnits)
594
+ }));
595
+ }
596
+ export const FOLDER_HELP = `fundus folder — manage the mirrored raw/proxy folder tree
597
+
598
+ Usage: fundus folder <subcommand> …
599
+
600
+ Subcommands:
601
+ list list registered folders and their direct assets
602
+ create create a persisted folder (fundus folder create <path>)
603
+ rename rename one folder segment and its subtree
604
+ move move a complete folder subtree below another folder
605
+ delete recursively delete a folder subtree and its assets
606
+
607
+ All mutations update rawDir and proxyDir together, maintain .gitkeep files,
608
+ and run the full autosave chain. All subcommands accept --json and --help.
609
+ `;
610
+ const FOLDER_LIST_HELP = `fundus folder list — list registered folders
611
+
612
+ Usage: fundus folder list [--json]
613
+
614
+ Prints each folder path, parent, and direct assets. Root is implicit and is not
615
+ included. Read-only: nothing is processed or written.
616
+
617
+ Flags:
618
+ --json print {"ok": true, "folders": […]}
619
+ --help this help
620
+
621
+ Example:
622
+ fundus folder list --json
623
+ `;
624
+ const FOLDER_CREATE_HELP = `fundus folder create — create a persisted folder
625
+
626
+ Usage: fundus folder create <path> [--json]
627
+
628
+ The parent must already exist (or be the implicit root). Creates matching raw
629
+ and proxy directories with managed .gitkeep files.
630
+
631
+ Flags:
632
+ --json print the created folder row and issues
633
+ --help this help
634
+
635
+ Example:
636
+ fundus folder create "UI/Buttons" --json
637
+ `;
638
+ const FOLDER_RENAME_HELP = `fundus folder rename — rename a folder
639
+
640
+ Usage: fundus folder rename <path> <newName> [--json]
641
+
642
+ Renames only the final path segment, preserving the parent. Descendant folders,
643
+ raw paths, proxy paths, and generated URLs move with it.
644
+
645
+ Flags:
646
+ --json print the renamed folder row and issues
647
+ --help this help
648
+
649
+ Example:
650
+ fundus folder rename "UI/Buttons" Controls --json
651
+ `;
652
+ const FOLDER_DELETE_HELP = `fundus folder delete — recursively delete a folder
653
+
654
+ Usage: fundus folder delete <path> --recursive [--json]
655
+
656
+ Deletes the complete subtree and all assets in it. --recursive is required to
657
+ make this destructive operation explicit. External references to deleted assets
658
+ are reported as validation issues after deletion.
659
+
660
+ Flags:
661
+ --recursive required destructive-operation acknowledgement
662
+ --json print the deleted path and resulting issues
663
+ --help this help
664
+
665
+ Example:
666
+ fundus folder delete "UI/Obsolete" --recursive --json
667
+ `;
668
+ const FOLDER_MOVE_HELP = `fundus folder move — move a folder subtree
669
+
670
+ Usage: fundus folder move <path> <parent|root> [--json]
671
+
672
+ Moves the folder, every descendant folder, and all contained raw/proxy files
673
+ below an existing parent. The folder keeps its name and cannot move into its
674
+ own subtree. Use the literal "root" for the implicit root.
675
+
676
+ Flags:
677
+ --json print the moved folder row and issues
678
+ --help this help
679
+
680
+ Example:
681
+ fundus folder move "UI/Buttons" Shared --json
682
+ `;
683
+ const FOLDER_SUBCOMMAND_HELP = {
684
+ list: FOLDER_LIST_HELP,
685
+ create: FOLDER_CREATE_HELP,
686
+ rename: FOLDER_RENAME_HELP,
687
+ move: FOLDER_MOVE_HELP,
688
+ delete: FOLDER_DELETE_HELP
689
+ };
690
+ async function runFolderList(argumentList) {
691
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus folder list');
692
+ requirePositionals(parsed, [], 'fundus folder list');
693
+ const folders = folderRows(await (await createOperations()).state());
694
+ if (parsed.flags.json) {
695
+ printJsonDocument({ ok: true, folders });
696
+ return;
697
+ }
698
+ if (folders.length === 0) {
699
+ console.log('No folders.');
700
+ return;
701
+ }
702
+ for (const folder of folders) {
703
+ console.log(`${folder.path} ${folder.directAssetIds.join(', ') || '(empty)'}`);
704
+ }
705
+ }
706
+ async function runFolderCreate(argumentList) {
707
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus folder create');
708
+ const [path] = requirePositionals(parsed, ['<path>'], 'fundus folder create');
709
+ const state = await (await createOperations()).createFolder(path);
710
+ const folder = folderRows(state).find((row) => row.path === path.normalize('NFC'));
711
+ if (parsed.flags.json) {
712
+ printJsonDocument({ ok: true, folder, issues: state.issues });
713
+ return;
714
+ }
715
+ printMutationResult(`Created folder "${folder?.path ?? path}".`, null, state);
716
+ }
717
+ async function runFolderRename(argumentList) {
718
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus folder rename');
719
+ const [path, newName] = requirePositionals(parsed, ['<path>', '<newName>'], 'fundus folder rename');
720
+ const state = await (await createOperations()).renameFolder(path, newName);
721
+ const nextPath = parentFolderPath(path) === '' ? newName : `${parentFolderPath(path)}/${newName}`;
722
+ const folder = folderRows(state).find((row) => row.path === nextPath.normalize('NFC'));
723
+ if (parsed.flags.json) {
724
+ printJsonDocument({ ok: true, folder, issues: state.issues });
725
+ return;
726
+ }
727
+ printMutationResult(`Renamed folder "${path}" to "${folder?.path ?? nextPath}".`, null, state);
728
+ }
729
+ async function runFolderMove(argumentList) {
730
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus folder move');
731
+ const [path, parentToken] = requirePositionals(parsed, ['<path>', '<parent|root>'], 'fundus folder move');
732
+ const parent = parseFolderToken(parentToken);
733
+ const state = await (await createOperations()).moveFolder(path, parent);
734
+ const nextPath = parent === '' ? folderNameOf(path) : `${parent}/${folderNameOf(path)}`;
735
+ const folder = folderRows(state).find((row) => row.path === nextPath.normalize('NFC'));
736
+ if (parsed.flags.json) {
737
+ printJsonDocument({ ok: true, folder, issues: state.issues });
738
+ return;
739
+ }
740
+ printMutationResult(`Moved folder "${path}" to "${folder?.path ?? nextPath}".`, null, state);
741
+ }
742
+ async function runFolderDelete(argumentList) {
743
+ const parsed = parseArguments(argumentList, { ...COMMON_FLAGS, recursive: { takesValue: false } }, 'fundus folder delete');
744
+ const [path] = requirePositionals(parsed, ['<path>'], 'fundus folder delete');
745
+ if (!parsed.flags.recursive) {
746
+ throw new CliUsageError('`fundus folder delete` requires --recursive because it deletes the subtree and its assets.');
747
+ }
748
+ const state = await (await createOperations()).deleteFolder(path);
749
+ if (parsed.flags.json) {
750
+ printJsonDocument({ ok: true, deletedFolderPath: path, issues: state.issues });
751
+ return;
752
+ }
753
+ printMutationResult(`Deleted folder "${path}" recursively.`, null, state);
754
+ }
755
+ export async function runFolderCommand(argumentList) {
756
+ const [subcommand, ...rest] = argumentList;
757
+ if (subcommand === undefined || subcommand === '--help') {
758
+ console.log(FOLDER_HELP);
759
+ if (subcommand === undefined) {
760
+ throw new CliUsageError('`fundus folder` needs a subcommand: list, create, rename, move, delete.');
761
+ }
762
+ return;
763
+ }
764
+ if (hasHelpFlag(rest)) {
765
+ console.log(FOLDER_SUBCOMMAND_HELP[subcommand] ?? FOLDER_HELP);
766
+ return;
767
+ }
768
+ switch (subcommand) {
769
+ case 'list':
770
+ return runFolderList(rest);
771
+ case 'create':
772
+ return runFolderCreate(rest);
773
+ case 'rename':
774
+ return runFolderRename(rest);
775
+ case 'move':
776
+ return runFolderMove(rest);
777
+ case 'delete':
778
+ return runFolderDelete(rest);
779
+ default:
780
+ throw new CliUsageError(`Unknown subcommand "folder ${subcommand}" — run \`fundus folder --help\`.`);
781
+ }
782
+ }
783
+ // ---------------------------------------------------------------------------
784
+ // fundus manifest …
785
+ // ---------------------------------------------------------------------------
786
+ export const MANIFEST_HELP = `fundus manifest — manage manifests (delivery units & preload groups)
787
+
788
+ Usage: fundus manifest <subcommand> …
789
+
790
+ A manifest is a delivery unit and a preload group: only assets in at
791
+ least one manifest (explicitly, or passively via the reference closure)
792
+ are processed into proxies and reachable from host code. Assets join and
793
+ leave manifests via \`fundus asset set <id> --manifests <a,b>\`. All
794
+ subcommands accept --json and --help.
795
+
796
+ Subcommands:
797
+ list list manifests with members and delivered bytes
798
+ create create an empty manifest (fundus manifest create <name>)
799
+ set set a manifest's editor color / emoji appearance
800
+ palette list the selectable appearance colors and emojis
801
+ rename rename a manifest (fundus manifest rename <name> <newName>)
802
+ delete delete a manifest and strip its memberships
803
+
804
+ Run \`fundus manifest <subcommand> --help\` for the focused page.
805
+ `;
806
+ export const MANIFEST_LIST_HELP = `fundus manifest list — list manifests
807
+
808
+ Usage: fundus manifest list [--json]
809
+
810
+ One line per manifest: name, delivered bytes (the preload-budget
811
+ number, over the full delivery set), explicit members, and passive
812
+ members (assets pulled in via the reference closure, with via-
813
+ attribution in the JSON output).
814
+
815
+ Flags:
816
+ --json print {"ok": true, "manifests": [{"name", "color", "emoji",
817
+ "explicitMemberIds", "passiveMembers", "deliveredBytes"}, …]}
818
+ --help this help
819
+
820
+ Read-only: nothing is processed or written.
821
+
822
+ Example:
823
+ fundus manifest list --json
824
+ `;
825
+ export const MANIFEST_CREATE_HELP = `fundus manifest create — create an empty manifest
826
+
827
+ Usage: fundus manifest create <name> [--json]
828
+
829
+ <name> must be a camelCase identifier; its kebab-case form names the
830
+ generated module (mainCatalog → main-catalog.generated.ts). Mutations run
831
+ the autosave chain, so the module is generated immediately.
832
+
833
+ Flags:
834
+ --json print {"ok": true, "manifest": {…}, "issues": […]}
835
+ --help this help
836
+
837
+ Example:
838
+ fundus manifest create mainCatalog --json
839
+ `;
840
+ export const MANIFEST_RENAME_HELP = `fundus manifest rename — rename a manifest
841
+
842
+ Usage: fundus manifest rename <name> <newName> [--json]
843
+
844
+ <newName> must be a camelCase identifier whose kebab-case form is
845
+ distinct from every other manifest's. The rename keeps the manifest's
846
+ appearance, rewrites every asset's membership, and replaces the
847
+ generated module (the old file is removed, the new one written). Host
848
+ code importing the module must be updated by hand — both the module
849
+ file name and the exported const change with the manifest name
850
+ (mainCatalog → main-catalog.generated.ts / \`mainCatalog\`).
851
+
852
+ Flags:
853
+ --json print {"ok": true, "manifest": {…}, "issues": […]}
854
+ --help this help
855
+
856
+ Example:
857
+ fundus manifest rename mainCatalog primaryCatalog --json
858
+ `;
859
+ export const MANIFEST_DELETE_HELP = `fundus manifest delete — delete a manifest
860
+
861
+ Usage: fundus manifest delete <name> [--json]
862
+
863
+ Deletes the manifest, strips it from every asset's membership, and
864
+ removes its generated module. Assets left in no manifest stop being
865
+ delivered (they warn as unreachable).
866
+
867
+ Flags:
868
+ --json print {"ok": true, "deletedManifestName": "<name>", "issues": […]}
869
+ --help this help
870
+
871
+ Example:
872
+ fundus manifest delete obsoleteShelf --json
873
+ `;
874
+ export const MANIFEST_SET_HELP = `fundus manifest set — set a manifest's editor appearance
875
+
876
+ Usage: fundus manifest set <name> [--color <name|hex|none>] [--emoji <emoji|none>] [--json]
877
+
878
+ Tags the manifest with a color and/or an emoji, shown on its label
879
+ throughout the editor UI. Both are chosen from the fixed sets below;
880
+ pass "none" (or an empty string) to clear either. At least one of
881
+ --color/--emoji is required. Runs the autosave chain.
882
+
883
+ Colors (name or #hex):
884
+ ${MANIFEST_COLORS.map((color) => `${color.name} (${color.value})`).join(', ')}
885
+
886
+ Emojis:
887
+ ${MANIFEST_EMOJIS.join(' ')}
888
+
889
+ Flags:
890
+ --color <name|hex|none> palette name ("blue"), hex value ("#3b82f6"),
891
+ or "none"/"" to clear
892
+ --emoji <emoji|none> one of the emojis above, or "none"/"" to clear
893
+ --json print the created/updated manifest row + issues
894
+ --help this help
895
+
896
+ Example:
897
+ fundus manifest set mainCatalog --color blue --emoji 🗂️ --json
898
+ `;
899
+ export const MANIFEST_PALETTE_HELP = `fundus manifest palette — list the selectable appearance colors and emojis
900
+
901
+ Usage: fundus manifest palette [--json]
902
+
903
+ The closed sets \`fundus manifest set\` validates against. Read this first
904
+ to discover valid --color / --emoji values programmatically instead of
905
+ guessing.
906
+
907
+ Flags:
908
+ --json print {"ok": true, "colors": [{"name", "value"}, …], "emojis": […]}
909
+ --help this help
910
+
911
+ Read-only: nothing is processed or written.
912
+
913
+ Example:
914
+ fundus manifest palette --json | jq '.emojis'
915
+ `;
916
+ const MANIFEST_SUBCOMMAND_HELP = {
917
+ list: MANIFEST_LIST_HELP,
918
+ create: MANIFEST_CREATE_HELP,
919
+ set: MANIFEST_SET_HELP,
920
+ palette: MANIFEST_PALETTE_HELP,
921
+ rename: MANIFEST_RENAME_HELP,
922
+ delete: MANIFEST_DELETE_HELP
923
+ };
924
+ async function runManifestList(argumentList) {
925
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus manifest list');
926
+ requirePositionals(parsed, [], 'fundus manifest list');
927
+ const operations = await createOperations();
928
+ const state = await operations.state();
929
+ const manifests = manifestRows(state);
930
+ if (parsed.flags.json) {
931
+ printJsonDocument({ ok: true, manifests });
932
+ return;
933
+ }
934
+ if (manifests.length === 0) {
935
+ console.log('No manifests.');
936
+ return;
937
+ }
938
+ const labels = manifests.map(manifestLabel);
939
+ const nameWidth = Math.max(0, ...labels.map((label) => label.length));
940
+ manifests.forEach((row, index) => {
941
+ const passiveSuffix = row.passiveMembers.length > 0
942
+ ? ` + passive: ${row.passiveMembers.map((member) => member.assetId).join(', ')}`
943
+ : '';
944
+ const colorSuffix = row.color ? ` ${row.color}` : '';
945
+ console.log(`${labels[index].padEnd(nameWidth)} ${formatBytes(row.deliveredBytes).padStart(9)} ` +
946
+ `${row.explicitMemberIds.join(', ') || '(empty)'}${passiveSuffix}${colorSuffix}`);
947
+ });
948
+ }
949
+ async function runManifestCreate(argumentList) {
950
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus manifest create');
951
+ const [name] = requirePositionals(parsed, ['<name>'], 'fundus manifest create');
952
+ const operations = await createOperations();
953
+ const state = await operations.createManifest(name);
954
+ const manifest = manifestRows(state).find((row) => row.name === name);
955
+ if (parsed.flags.json) {
956
+ printJsonDocument({ ok: true, manifest, issues: state.issues });
957
+ return;
958
+ }
959
+ printMutationResult(`Created manifest "${name}".`, null, state);
960
+ }
961
+ /** Resolve a `--color`/`--emoji` flag value to a set/clear/omit intent. */
962
+ function resolveAppearanceFlag(value, resolve, describe) {
963
+ if (typeof value !== 'string')
964
+ return undefined;
965
+ const token = value.trim();
966
+ if (token === '' || token.toLowerCase() === 'none')
967
+ return null;
968
+ const resolved = resolve(token);
969
+ if (resolved === null)
970
+ throw new Error(describe(token));
971
+ return resolved;
972
+ }
973
+ async function runManifestSet(argumentList) {
974
+ const parsed = parseArguments(argumentList, { ...COMMON_FLAGS, color: { takesValue: true }, emoji: { takesValue: true } }, 'fundus manifest set');
975
+ const [name] = requirePositionals(parsed, ['<name>'], 'fundus manifest set');
976
+ if (parsed.flags.color === undefined && parsed.flags.emoji === undefined) {
977
+ throw new CliUsageError('`fundus manifest set` needs --color and/or --emoji — run `fundus manifest set --help`.');
978
+ }
979
+ const color = resolveAppearanceFlag(parsed.flags.color, resolveManifestColor, (token) => `"${token}" is not a selectable manifest color — choose one of: ` +
980
+ `${MANIFEST_COLORS.map((entry) => `${entry.name} (${entry.value})`).join(', ')}, or "none".`);
981
+ const emoji = resolveAppearanceFlag(parsed.flags.emoji, (token) => (manifestEmojiError(token) === null ? token : null), (token) => manifestEmojiError(token) ?? `"${token}" is not a selectable manifest emoji.`);
982
+ const operations = await createOperations();
983
+ const state = await operations.setManifestAppearance(name, { color, emoji });
984
+ const manifest = manifestRows(state).find((row) => row.name === name);
985
+ if (parsed.flags.json) {
986
+ printJsonDocument({ ok: true, manifest, issues: state.issues });
987
+ return;
988
+ }
989
+ printMutationResult(`Updated manifest "${name}" appearance.`, null, state);
990
+ }
991
+ async function runManifestPalette(argumentList) {
992
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus manifest palette');
993
+ requirePositionals(parsed, [], 'fundus manifest palette');
994
+ if (parsed.flags.json) {
995
+ printJsonDocument({ ok: true, colors: MANIFEST_COLORS, emojis: MANIFEST_EMOJIS });
996
+ return;
997
+ }
998
+ console.log('Colors:');
999
+ const nameWidth = Math.max(0, ...MANIFEST_COLORS.map((color) => color.name.length));
1000
+ for (const color of MANIFEST_COLORS) {
1001
+ console.log(` ${color.name.padEnd(nameWidth)} ${color.value}`);
1002
+ }
1003
+ console.log('');
1004
+ console.log(`Emojis (${MANIFEST_EMOJIS.length}):`);
1005
+ console.log(` ${MANIFEST_EMOJIS.join(' ')}`);
1006
+ }
1007
+ async function runManifestRename(argumentList) {
1008
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus manifest rename');
1009
+ const [name, newName] = requirePositionals(parsed, ['<name>', '<newName>'], 'fundus manifest rename');
1010
+ const operations = await createOperations();
1011
+ const state = await operations.renameManifest(name, newName);
1012
+ const manifest = manifestRows(state).find((row) => row.name === newName);
1013
+ if (parsed.flags.json) {
1014
+ printJsonDocument({ ok: true, manifest, issues: state.issues });
1015
+ return;
1016
+ }
1017
+ printMutationResult(`Renamed manifest "${name}" to "${newName}" — update host imports of the generated module.`, null, state);
1018
+ }
1019
+ async function runManifestDelete(argumentList) {
1020
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus manifest delete');
1021
+ const [name] = requirePositionals(parsed, ['<name>'], 'fundus manifest delete');
1022
+ const operations = await createOperations();
1023
+ const state = await operations.deleteManifest(name);
1024
+ if (parsed.flags.json) {
1025
+ printJsonDocument({ ok: true, deletedManifestName: name, issues: state.issues });
1026
+ return;
1027
+ }
1028
+ printMutationResult(`Deleted manifest "${name}" and stripped it from every asset.`, null, state);
1029
+ }
1030
+ export async function runManifestCommand(argumentList) {
1031
+ const [subcommand, ...rest] = argumentList;
1032
+ if (subcommand === undefined || subcommand === '--help') {
1033
+ console.log(MANIFEST_HELP);
1034
+ if (subcommand === undefined) {
1035
+ throw new CliUsageError('`fundus manifest` needs a subcommand: list, create, set, palette, rename, delete.');
1036
+ }
1037
+ return;
1038
+ }
1039
+ if (hasHelpFlag(rest)) {
1040
+ // See `runAssetCommand` for the focused-help dispatch rule.
1041
+ console.log(MANIFEST_SUBCOMMAND_HELP[subcommand] ?? MANIFEST_HELP);
1042
+ return;
1043
+ }
1044
+ switch (subcommand) {
1045
+ case 'list':
1046
+ return runManifestList(rest);
1047
+ case 'create':
1048
+ return runManifestCreate(rest);
1049
+ case 'set':
1050
+ return runManifestSet(rest);
1051
+ case 'palette':
1052
+ return runManifestPalette(rest);
1053
+ case 'rename':
1054
+ return runManifestRename(rest);
1055
+ case 'delete':
1056
+ return runManifestDelete(rest);
1057
+ default:
1058
+ throw new CliUsageError(`Unknown subcommand "manifest ${subcommand}" — run \`fundus manifest --help\`.`);
1059
+ }
1060
+ }
1061
+ // ---------------------------------------------------------------------------
1062
+ // fundus build / fundus check (pipeline commands)
1063
+ // ---------------------------------------------------------------------------
1064
+ export const BUILD_HELP = `fundus build — run the headless pipeline
1065
+
1066
+ Usage: fundus build [--json]
1067
+
1068
+ Validates the library, processes every stale or missing proxy, prunes
1069
+ orphaned proxies, and regenerates all manifest modules. Deterministic
1070
+ and idempotent: a second run on an unchanged project processes nothing
1071
+ and rewrites byte-identical modules. Run it in CI, postinstall, or
1072
+ before the host build; commit the result.
1073
+
1074
+ Flags:
1075
+ --json print {"ok": true, "processed": […], "upToDate": […],
1076
+ "prunedProxies": […], "modulesWritten": […],
1077
+ "modulesRemoved": […]} — asset ids for processing,
1078
+ file names for pruning/codegen
1079
+ --help this help
1080
+
1081
+ A validation failure processes nothing and exits 1 (with --json:
1082
+ {"error": {"message": …}}).
1083
+
1084
+ Example:
1085
+ fundus build --json | jq '.processed'
1086
+ `;
1087
+ export async function runBuildCommand(argumentList) {
1088
+ if (hasHelpFlag(argumentList)) {
1089
+ console.log(BUILD_HELP);
1090
+ return;
1091
+ }
1092
+ const parsed = parseArguments(argumentList, COMMON_FLAGS, 'fundus build');
1093
+ requirePositionals(parsed, [], 'fundus build');
1094
+ const { processing, codegen } = await runBuild(process.cwd());
1095
+ if (parsed.flags.json) {
1096
+ printJsonDocument({
1097
+ ok: true,
1098
+ processed: processing.processed,
1099
+ upToDate: processing.reused,
1100
+ prunedProxies: processing.pruned,
1101
+ modulesWritten: codegen.written,
1102
+ modulesRemoved: codegen.removed
1103
+ });
1104
+ return;
1105
+ }
1106
+ console.log(`fundus build: ${processing.processed.length} processed, ${processing.reused.length} up to date, ` +
1107
+ `${processing.pruned.length} pruned; ${codegen.written.length} manifest module(s) written` +
1108
+ (codegen.removed.length > 0 ? `, ${codegen.removed.length} removed` : '') +
1109
+ '.');
1110
+ }
1111
+ export const CHECK_HELP = `fundus check — verify the committed state (the CI gate)
1112
+
1113
+ Usage: fundus check [--allow-warnings] [--json]
1114
+
1115
+ Verify-only, no writes: the library must validate, every delivered
1116
+ asset's committed proxy must exist under its expected name (staleness
1117
+ is a name check), the proxy directory must hold no orphans, and every
1118
+ generated manifest module must be byte-identical to an in-memory
1119
+ regeneration. Warnings (unreachable asset, raw-hash drift) fail the
1120
+ check too unless --allow-warnings downgrades them; errors never
1121
+ downgrade.
1122
+
1123
+ Flags:
1124
+ --allow-warnings exit 0 despite warnings (errors still fail)
1125
+ --json print one report document:
1126
+ {"ok": <pass/fail>, "assetCount": …, "manifestCount": …,
1127
+ "errors": [<finding>, …], "warnings": [<finding>, …]}
1128
+ where a finding is {"kind", "message"} plus
1129
+ "assetId" / "manifest" / "fileName" where applicable
1130
+ --help this help
1131
+
1132
+ Exit codes: 0 = clean (or warnings allowed), 1 = failed. A failure to
1133
+ even run the check (e.g. no fundus.config.ts) exits 1 with the usual
1134
+ {"error": {"message": …}} envelope under --json.
1135
+
1136
+ Example:
1137
+ fundus check --json | jq '.errors[].kind'
1138
+ `;
1139
+ export async function runCheckCommand(argumentList) {
1140
+ if (hasHelpFlag(argumentList)) {
1141
+ console.log(CHECK_HELP);
1142
+ return;
1143
+ }
1144
+ const parsed = parseArguments(argumentList, { ...COMMON_FLAGS, 'allow-warnings': { takesValue: false } }, 'fundus check');
1145
+ requirePositionals(parsed, [], 'fundus check');
1146
+ const allowWarnings = parsed.flags['allow-warnings'] === true;
1147
+ const report = await runCheck(process.cwd());
1148
+ const warningsFatal = report.warnings.length > 0 && !allowWarnings;
1149
+ const failed = report.errors.length > 0 || warningsFatal;
1150
+ if (parsed.flags.json) {
1151
+ printJsonDocument({
1152
+ ok: !failed,
1153
+ assetCount: report.assetCount,
1154
+ manifestCount: report.manifestCount,
1155
+ errors: report.errors,
1156
+ warnings: report.warnings
1157
+ });
1158
+ if (failed)
1159
+ process.exitCode = 1;
1160
+ return;
1161
+ }
1162
+ for (const error of report.errors) {
1163
+ console.error(`✗ ${formatCheckFinding(error)}`);
1164
+ }
1165
+ for (const warning of report.warnings) {
1166
+ console.error(`⚠ ${formatCheckFinding(warning)}`);
1167
+ }
1168
+ if (failed) {
1169
+ console.error(`fundus check failed: ${report.errors.length} error(s), ${report.warnings.length} warning(s)` +
1170
+ (warningsFatal ? ' (warnings are errors — pass --allow-warnings to downgrade them)' : '') +
1171
+ '.');
1172
+ process.exitCode = 1;
1173
+ return;
1174
+ }
1175
+ console.log(`fundus check: ${report.assetCount} asset(s), ${report.manifestCount} manifest(s) — clean` +
1176
+ (report.warnings.length > 0 ? ` (${report.warnings.length} allowed warning(s))` : '') +
1177
+ '.');
1178
+ }