@legionworks/facet 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/LICENSE-APACHE +202 -0
  2. package/LICENSE-MIT +23 -0
  3. package/README.md +158 -0
  4. package/dist/gallery/chunk-4g8rem85.css +656 -0
  5. package/dist/gallery/chunk-c6s9k9ty.js +5970 -0
  6. package/dist/gallery/frame/artifact.css +8424 -0
  7. package/dist/gallery/frame/chunks/abnfDiagram-N423BO3Z-jj74szbm.js +155 -0
  8. package/dist/gallery/frame/chunks/architecture-TIHT7OUA-cvdjn7dt.js +10 -0
  9. package/dist/gallery/frame/chunks/architectureDiagram-T3A2C74G-n4z77eg6.js +8675 -0
  10. package/dist/gallery/frame/chunks/blockDiagram-VBNYF7ZC-7xsqzrd1.js +3529 -0
  11. package/dist/gallery/frame/chunks/c4Diagram-5PPSVZJV-z2zjs7wx.js +2413 -0
  12. package/dist/gallery/frame/chunks/classDiagram-JCYQIIEL-ve8rngqp.js +47 -0
  13. package/dist/gallery/frame/chunks/classDiagram-v2-OCEON4UE-8cfaxb8z.js +47 -0
  14. package/dist/gallery/frame/chunks/cose-bilkent-JH36ORCC-sn92qexx.js +4822 -0
  15. package/dist/gallery/frame/chunks/cynefin-VYW2F7L2-tqjv913d.js +10 -0
  16. package/dist/gallery/frame/chunks/cynefinDiagram-MW4NZA55-c1c0dj12.js +500 -0
  17. package/dist/gallery/frame/chunks/dagre-VZM6K2ZE-7tyg4yt2.js +476 -0
  18. package/dist/gallery/frame/chunks/diagram-7IWD3JNH-y4eng9sc.js +526 -0
  19. package/dist/gallery/frame/chunks/diagram-B4RE2ZJO-rn3p7j5h.js +673 -0
  20. package/dist/gallery/frame/chunks/diagram-LBJQPF4R-w4a2cm9q.js +255 -0
  21. package/dist/gallery/frame/chunks/diagram-Q27KOJAE-aqc2v7wy.js +575 -0
  22. package/dist/gallery/frame/chunks/diagram-UB23O5K3-xncy8nyd.js +355 -0
  23. package/dist/gallery/frame/chunks/ebnfDiagram-BXEA7PRR-acwgxz16.js +175 -0
  24. package/dist/gallery/frame/chunks/erDiagram-JOGREHBK-mzp5nswt.js +1332 -0
  25. package/dist/gallery/frame/chunks/eventmodeling-45OFAUF4-sg07qr0c.js +10 -0
  26. package/dist/gallery/frame/chunks/flowDiagram-UKHOOZJN-2e5ygs44.js +29 -0
  27. package/dist/gallery/frame/chunks/ganttDiagram-PKOTCBZU-p2hhh7hr.js +2620 -0
  28. package/dist/gallery/frame/chunks/gitGraph-TEB2WS4Q-j6kw0naa.js +10 -0
  29. package/dist/gallery/frame/chunks/gitGraphDiagram-DS77QQ5N-4z9z8wjm.js +1347 -0
  30. package/dist/gallery/frame/chunks/info-DKCQHKI2-mpt80jd3.js +10 -0
  31. package/dist/gallery/frame/chunks/infoDiagram-6WML65LV-5rqm3mgg.js +65 -0
  32. package/dist/gallery/frame/chunks/ishikawaDiagram-WSZJBQD7-b51z96jy.js +975 -0
  33. package/dist/gallery/frame/chunks/journeyDiagram-NVQOT4AX-9jbfp0pk.js +1272 -0
  34. package/dist/gallery/frame/chunks/kanban-definition-27J2QSJJ-4x0vx4se.js +1116 -0
  35. package/dist/gallery/frame/chunks/katex-xfgk95tv.js +14034 -0
  36. package/dist/gallery/frame/chunks/markdown-1s5vwkf2.js +9 -0
  37. package/dist/gallery/frame/chunks/markdown-2h6bwsms.js +3239 -0
  38. package/dist/gallery/frame/chunks/markdown-2x113yh8.js +107 -0
  39. package/dist/gallery/frame/chunks/markdown-30b7rwmp.js +1943 -0
  40. package/dist/gallery/frame/chunks/markdown-356bfj4g.js +2031 -0
  41. package/dist/gallery/frame/chunks/markdown-44n9ea69.js +2406 -0
  42. package/dist/gallery/frame/chunks/markdown-56enrf2r.js +39 -0
  43. package/dist/gallery/frame/chunks/markdown-5avymm8t.js +725 -0
  44. package/dist/gallery/frame/chunks/markdown-6r4svars.js +33 -0
  45. package/dist/gallery/frame/chunks/markdown-6zz8efx2.js +24 -0
  46. package/dist/gallery/frame/chunks/markdown-7jp6prnb.js +19 -0
  47. package/dist/gallery/frame/chunks/markdown-809k7q41.js +55 -0
  48. package/dist/gallery/frame/chunks/markdown-82xmzhzd.js +1076 -0
  49. package/dist/gallery/frame/chunks/markdown-92n37gd4.js +7627 -0
  50. package/dist/gallery/frame/chunks/markdown-ag2sssf8.js +36 -0
  51. package/dist/gallery/frame/chunks/markdown-b4s7ntq2.js +19 -0
  52. package/dist/gallery/frame/chunks/markdown-c9k0fvp1.js +100 -0
  53. package/dist/gallery/frame/chunks/markdown-ceefymn8.js +125 -0
  54. package/dist/gallery/frame/chunks/markdown-d1wfbs70.js +243 -0
  55. package/dist/gallery/frame/chunks/markdown-dy5sgjhc.js +11022 -0
  56. package/dist/gallery/frame/chunks/markdown-dz3z5w30.js +30868 -0
  57. package/dist/gallery/frame/chunks/markdown-eg6ngz7y.js +30340 -0
  58. package/dist/gallery/frame/chunks/markdown-ff5wvb43.js +1149 -0
  59. package/dist/gallery/frame/chunks/markdown-ffg06nfz.js +86 -0
  60. package/dist/gallery/frame/chunks/markdown-fvfncadf.js +472 -0
  61. package/dist/gallery/frame/chunks/markdown-gvyszqpv.js +19 -0
  62. package/dist/gallery/frame/chunks/markdown-gx1p0ew0.js +88 -0
  63. package/dist/gallery/frame/chunks/markdown-gxye5frz.js +65 -0
  64. package/dist/gallery/frame/chunks/markdown-h7pzv34e.js +105 -0
  65. package/dist/gallery/frame/chunks/markdown-hab789t9.js +47 -0
  66. package/dist/gallery/frame/chunks/markdown-hjp6sj8t.js +125 -0
  67. package/dist/gallery/frame/chunks/markdown-jka64qgp.js +1993 -0
  68. package/dist/gallery/frame/chunks/markdown-k8xkn8mw.js +56 -0
  69. package/dist/gallery/frame/chunks/markdown-m7frq8x8.js +112 -0
  70. package/dist/gallery/frame/chunks/markdown-p740v4qm.js +36 -0
  71. package/dist/gallery/frame/chunks/markdown-pxfeh231.js +57 -0
  72. package/dist/gallery/frame/chunks/markdown-pzgd8z3s.js +87 -0
  73. package/dist/gallery/frame/chunks/markdown-qx3cngz2.js +1673 -0
  74. package/dist/gallery/frame/chunks/markdown-rawhfvvc.js +85 -0
  75. package/dist/gallery/frame/chunks/markdown-rd8w0ng9.js +49 -0
  76. package/dist/gallery/frame/chunks/markdown-rm2yxfjj.js +2773 -0
  77. package/dist/gallery/frame/chunks/markdown-s1jatrnk.js +993 -0
  78. package/dist/gallery/frame/chunks/markdown-sgfg5vvq.js +59 -0
  79. package/dist/gallery/frame/chunks/markdown-t4ethv9d.js +409 -0
  80. package/dist/gallery/frame/chunks/markdown-tnmabqvz.js +36 -0
  81. package/dist/gallery/frame/chunks/markdown-tvdpppw8.js +5292 -0
  82. package/dist/gallery/frame/chunks/markdown-v0x27twr.js +22 -0
  83. package/dist/gallery/frame/chunks/markdown-v14td68x.js +36 -0
  84. package/dist/gallery/frame/chunks/markdown-w6sgd54z.js +36 -0
  85. package/dist/gallery/frame/chunks/markdown-xw8dsk8b.js +2125 -0
  86. package/dist/gallery/frame/chunks/markdown-y18mve73.js +85 -0
  87. package/dist/gallery/frame/chunks/markdown-yengsms5.js +427 -0
  88. package/dist/gallery/frame/chunks/markdown-z1wc0v0d.js +1826 -0
  89. package/dist/gallery/frame/chunks/markdown-zazx9h4n.js +329 -0
  90. package/dist/gallery/frame/chunks/mermaid-de7p4v0x.js +30 -0
  91. package/dist/gallery/frame/chunks/mindmap-definition-FAOFIHXS-ra44cbem.js +1216 -0
  92. package/dist/gallery/frame/chunks/packet-7NZHBO7P-xppm5913.js +10 -0
  93. package/dist/gallery/frame/chunks/pegDiagram-VL7TDLO6-a2a4wg03.js +163 -0
  94. package/dist/gallery/frame/chunks/pie-RZYD4A2V-hnbxk152.js +10 -0
  95. package/dist/gallery/frame/chunks/pieDiagram-7S7Q4E2Y-f1vxjtmx.js +311 -0
  96. package/dist/gallery/frame/chunks/quadrantDiagram-CIZ2JOQS-ywafa6kh.js +1390 -0
  97. package/dist/gallery/frame/chunks/radar-I7S5WNFK-ewqd27se.js +10 -0
  98. package/dist/gallery/frame/chunks/railroad-3IZDKUUU-5hy4bdx3.js +10 -0
  99. package/dist/gallery/frame/chunks/railroad-abnf-AHOZXSZD-bh5sxz0d.js +10 -0
  100. package/dist/gallery/frame/chunks/railroad-ebnf-EBAXGLYW-6b9c3zeh.js +10 -0
  101. package/dist/gallery/frame/chunks/railroad-peg-LSFZ7HO6-avc8v39s.js +10 -0
  102. package/dist/gallery/frame/chunks/railroadDiagram-AXF67PYL-qc61wax4.js +131 -0
  103. package/dist/gallery/frame/chunks/requirementDiagram-LRYGKXZP-9bs4786c.js +1295 -0
  104. package/dist/gallery/frame/chunks/sankeyDiagram-W5VNT64P-awt1em8b.js +1339 -0
  105. package/dist/gallery/frame/chunks/sequenceDiagram-SI44F4Z6-qaf9mz3t.js +4324 -0
  106. package/dist/gallery/frame/chunks/sizeCapture-X5ZJPWSS-9fq685xd.js +64 -0
  107. package/dist/gallery/frame/chunks/stateDiagram-OKZ733FA-vx7a7hh0.js +456 -0
  108. package/dist/gallery/frame/chunks/stateDiagram-v2-UEYNNEHI-x4m6ws5t.js +46 -0
  109. package/dist/gallery/frame/chunks/swimlanes-SLNWSIFB-e4mxm1gh.js +8287 -0
  110. package/dist/gallery/frame/chunks/swimlanesDiagram-ULZ7WXOC-0h93e2qq.js +42 -0
  111. package/dist/gallery/frame/chunks/timeline-definition-Z64GVDOM-wp0bhbzg.js +1549 -0
  112. package/dist/gallery/frame/chunks/treeView-QDETBFTQ-hcx7n0vt.js +10 -0
  113. package/dist/gallery/frame/chunks/treemap-6X3UGDF4-av02skdw.js +10 -0
  114. package/dist/gallery/frame/chunks/vennDiagram-T6HMQDX7-pmvk1p7n.js +2566 -0
  115. package/dist/gallery/frame/chunks/wardley-OPB4EBWU-njsp6e40.js +10 -0
  116. package/dist/gallery/frame/chunks/wardleyDiagram-T6FBY63Y-eggmnv4z.js +979 -0
  117. package/dist/gallery/frame/chunks/xychartDiagram-ELKLHX3M-q40tnpe5.js +1954 -0
  118. package/dist/gallery/frame/frame.css +183 -0
  119. package/dist/gallery/frame/runtime/chart.js +42744 -0
  120. package/dist/gallery/frame/runtime/html.js +14 -0
  121. package/dist/gallery/frame/runtime/markdown.js +1403 -0
  122. package/dist/gallery/frame/runtime/mermaid.js +32 -0
  123. package/dist/gallery/frame/runtime/svg.js +14 -0
  124. package/dist/gallery/frame/runtime/tsx.js +52 -0
  125. package/dist/gallery/index.html +104 -0
  126. package/docs/reference/cli.md +163 -0
  127. package/docs/reference/export.md +106 -0
  128. package/docs/reference/html.md +215 -0
  129. package/docs/reference/http.md +85 -0
  130. package/docs/reference/mcp.md +78 -0
  131. package/docs/reference/security.md +119 -0
  132. package/docs/reference/storage.md +93 -0
  133. package/docs/reference/tsx.md +101 -0
  134. package/docs/reference/validation.md +212 -0
  135. package/package.json +89 -0
  136. package/scripts/build-gallery.ts +127 -0
  137. package/scripts/launch-netns.sh +67 -0
  138. package/skills/facet/SKILL.md +100 -0
  139. package/src/cli/client.ts +311 -0
  140. package/src/cli/commands/create.ts +49 -0
  141. package/src/cli/commands/doctor.ts +249 -0
  142. package/src/cli/commands/export.ts +179 -0
  143. package/src/cli/commands/instantiate.ts +35 -0
  144. package/src/cli/commands/list.ts +49 -0
  145. package/src/cli/commands/open.ts +75 -0
  146. package/src/cli/commands/pin.ts +39 -0
  147. package/src/cli/commands/promote.ts +49 -0
  148. package/src/cli/commands/publish.ts +122 -0
  149. package/src/cli/commands/read-back.ts +56 -0
  150. package/src/cli/commands/status.ts +114 -0
  151. package/src/cli/commands/watch.ts +146 -0
  152. package/src/cli/main.ts +522 -0
  153. package/src/cli/output.ts +106 -0
  154. package/src/cli/parser.ts +376 -0
  155. package/src/cli/presenter.ts +212 -0
  156. package/src/cli/service-metadata.ts +134 -0
  157. package/src/cli/spawn-service.ts +326 -0
  158. package/src/css.d.ts +4 -0
  159. package/src/gallery-web/app.ts +1581 -0
  160. package/src/gallery-web/export-menu.ts +121 -0
  161. package/src/gallery-web/export.ts +112 -0
  162. package/src/gallery-web/favicon.ts +54 -0
  163. package/src/gallery-web/frame/entries/chart.ts +6 -0
  164. package/src/gallery-web/frame/entries/html.ts +6 -0
  165. package/src/gallery-web/frame/entries/markdown.ts +6 -0
  166. package/src/gallery-web/frame/entries/mermaid.ts +6 -0
  167. package/src/gallery-web/frame/entries/svg.ts +6 -0
  168. package/src/gallery-web/frame/entries/tsx.ts +6 -0
  169. package/src/gallery-web/frame/frame-payload.ts +91 -0
  170. package/src/gallery-web/frame/renderer-validation.ts +13 -0
  171. package/src/gallery-web/frame/renderers/chart.ts +182 -0
  172. package/src/gallery-web/frame/renderers/dompurify-shim.ts +112 -0
  173. package/src/gallery-web/frame/renderers/html.ts +48 -0
  174. package/src/gallery-web/frame/renderers/markdown.ts +81 -0
  175. package/src/gallery-web/frame/renderers/mermaid.ts +120 -0
  176. package/src/gallery-web/frame/renderers/registry.ts +260 -0
  177. package/src/gallery-web/frame/renderers/svg.ts +393 -0
  178. package/src/gallery-web/frame/renderers/tsx.ts +61 -0
  179. package/src/gallery-web/frame/runtime.ts +501 -0
  180. package/src/gallery-web/frame/styles/artifact.css +8424 -0
  181. package/src/gallery-web/frame/styles/frame.css +183 -0
  182. package/src/gallery-web/frame/styles/html-source.css +23 -0
  183. package/src/gallery-web/frame/view-box.ts +33 -0
  184. package/src/gallery-web/frame-html.ts +72 -0
  185. package/src/gallery-web/index.html +104 -0
  186. package/src/gallery-web/session.ts +107 -0
  187. package/src/gallery-web/sse-client.ts +212 -0
  188. package/src/gallery-web/styles/app.css +420 -0
  189. package/src/gallery-web/styles/tokens.css +112 -0
  190. package/src/gallery-web/styles/verdict.css +186 -0
  191. package/src/gallery-web/swap.ts +64 -0
  192. package/src/gallery-web/theme.ts +40 -0
  193. package/src/gallery-web/view-state.ts +93 -0
  194. package/src/harness-adapters/claude-code/README.md +10 -0
  195. package/src/harness-adapters/claude-code/facet.sh +4 -0
  196. package/src/harness-adapters/codex/README.md +10 -0
  197. package/src/harness-adapters/codex/facet.sh +4 -0
  198. package/src/harness-adapters/mcp/README.md +13 -0
  199. package/src/harness-adapters/mcp/cli-bridge.ts +216 -0
  200. package/src/harness-adapters/mcp/main.ts +15 -0
  201. package/src/harness-adapters/mcp/server.ts +164 -0
  202. package/src/harness-adapters/mcp/tool-schemas.ts +53 -0
  203. package/src/harness-adapters/opencode/README.md +10 -0
  204. package/src/harness-adapters/opencode/facet.sh +4 -0
  205. package/src/runtime/compiled-entrypoints.ts +69 -0
  206. package/src/service/dispatcher.ts +717 -0
  207. package/src/service/export.ts +218 -0
  208. package/src/service/http-utils.ts +55 -0
  209. package/src/service/lexical/expectations.ts +210 -0
  210. package/src/service/lifecycle/idle-controller.ts +125 -0
  211. package/src/service/lifecycle/orphan-cleanup.ts +128 -0
  212. package/src/service/lifecycle/process-lock.ts +235 -0
  213. package/src/service/main.ts +136 -0
  214. package/src/service/process.ts +240 -0
  215. package/src/service/router-guards.ts +239 -0
  216. package/src/service/router.ts +627 -0
  217. package/src/service/security/auth.ts +168 -0
  218. package/src/service/security/host-origin.ts +138 -0
  219. package/src/service/security/http-guards.ts +13 -0
  220. package/src/service/security/leases.ts +182 -0
  221. package/src/service/security/token-store.ts +138 -0
  222. package/src/service/server.ts +356 -0
  223. package/src/service/store/database.ts +61 -0
  224. package/src/service/store/evidence-retention.ts +234 -0
  225. package/src/service/store/migrations.ts +167 -0
  226. package/src/service/store/repository-lifecycle.ts +121 -0
  227. package/src/service/store/repository.ts +732 -0
  228. package/src/service/store/schema.ts +218 -0
  229. package/src/service/stored-verdict.ts +97 -0
  230. package/src/service/stream.ts +301 -0
  231. package/src/service/verdict-enrichment.ts +45 -0
  232. package/src/shared/build/frame-bundle-plugins.ts +61 -0
  233. package/src/shared/build-mode.ts +28 -0
  234. package/src/shared/config/evidence-read.ts +98 -0
  235. package/src/shared/config/limits.ts +56 -0
  236. package/src/shared/config/paths.ts +96 -0
  237. package/src/shared/contracts/artifact-types.ts +2 -0
  238. package/src/shared/contracts/artifact.ts +98 -0
  239. package/src/shared/contracts/commands/_shared.ts +59 -0
  240. package/src/shared/contracts/commands/guards.ts +86 -0
  241. package/src/shared/contracts/commands/index.ts +204 -0
  242. package/src/shared/contracts/commands/names.ts +35 -0
  243. package/src/shared/contracts/commands/requests.ts +149 -0
  244. package/src/shared/contracts/commands/results.ts +200 -0
  245. package/src/shared/contracts/envelope.ts +140 -0
  246. package/src/shared/contracts/events.ts +41 -0
  247. package/src/shared/contracts/observed-counts.ts +19 -0
  248. package/src/shared/contracts/renderers.ts +6 -0
  249. package/src/shared/contracts/validation.ts +296 -0
  250. package/src/shared/errors/facet-error.ts +127 -0
  251. package/src/shared/errors/store-error.ts +68 -0
  252. package/src/shared/evidence-image.ts +37 -0
  253. package/src/shared/export.ts +31 -0
  254. package/src/shared/html/policy.ts +108 -0
  255. package/src/shared/html/style-vocabulary.ts +352 -0
  256. package/src/shared/logging/logger.ts +108 -0
  257. package/src/shared/logging/redact.ts +49 -0
  258. package/src/shared/security/frozen-csp.ts +13 -0
  259. package/src/shared/storage-version.ts +2 -0
  260. package/src/shared/tsx/execution.ts +29 -0
  261. package/src/shared/tsx/import-policy.ts +94 -0
  262. package/src/shared/util/dir-permissions.ts +94 -0
  263. package/src/shared/util/mermaid-nodes.ts +230 -0
  264. package/src/shared/util/process.ts +67 -0
  265. package/src/shared/util/time.ts +21 -0
  266. package/src/shared/version.ts +3 -0
  267. package/src/validation/sandbox/limits.ts +41 -0
  268. package/src/validation/sandbox/netns.ts +177 -0
  269. package/src/validation/tier0/chart.ts +177 -0
  270. package/src/validation/tier0/dom-shim.ts +54 -0
  271. package/src/validation/tier0/html.ts +408 -0
  272. package/src/validation/tier0/markdown.ts +285 -0
  273. package/src/validation/tier0/mermaid.ts +98 -0
  274. package/src/validation/tier0/runner.ts +504 -0
  275. package/src/validation/tier0/svg.ts +321 -0
  276. package/src/validation/tier0/tsx/allowlist-resolver.ts +77 -0
  277. package/src/validation/tier0/tsx/ast-policy-capabilities.ts +238 -0
  278. package/src/validation/tier0/tsx/ast-policy-shared.ts +71 -0
  279. package/src/validation/tier0/tsx/ast-policy.ts +181 -0
  280. package/src/validation/tier0/tsx/compiler.ts +156 -0
  281. package/src/validation/tier0/worker-dispatch.ts +211 -0
  282. package/src/validation/tier0/worker-entry.ts +96 -0
  283. package/src/validation/tier0/worker-input.ts +75 -0
  284. package/src/validation/tier1/browser-process.ts +147 -0
  285. package/src/validation/tier1/cdp-pipe.ts +281 -0
  286. package/src/validation/tier1/entries/chart.ts +5 -0
  287. package/src/validation/tier1/entries/html.ts +6 -0
  288. package/src/validation/tier1/entries/markdown.ts +5 -0
  289. package/src/validation/tier1/entries/mermaid.ts +5 -0
  290. package/src/validation/tier1/entries/svg.ts +5 -0
  291. package/src/validation/tier1/entries/tsx.ts +5 -0
  292. package/src/validation/tier1/frame-target.ts +162 -0
  293. package/src/validation/tier1/harness-entry.ts +261 -0
  294. package/src/validation/tier1/harness.ts +215 -0
  295. package/src/validation/tier1/isolated-probe.ts +94 -0
  296. package/src/validation/tier1/launcher.ts +177 -0
  297. package/src/validation/tier1/limits.ts +80 -0
  298. package/src/validation/tier1/nonce.ts +17 -0
  299. package/src/validation/tier1/protocol-probe.ts +519 -0
  300. package/src/validation/tier1/runner.ts +1122 -0
  301. package/src/validation/tier1/verdict.ts +246 -0
  302. package/src/validation/tier1/webp.ts +69 -0
  303. package/templates/README.md +88 -0
  304. package/templates/bar-compare.vl.json +63 -0
  305. package/templates/capacity-report.tsx +102 -0
  306. package/templates/decision-record.md +24 -0
  307. package/templates/deployment-state.mmd +40 -0
  308. package/templates/exemplar.md +91 -0
  309. package/templates/facet-story.tsx +799 -0
  310. package/templates/fleet-dashboard.html +141 -0
  311. package/templates/html-release-ledger.html +91 -0
  312. package/templates/html-status-report.html +114 -0
  313. package/templates/incident-console.tsx +147 -0
  314. package/templates/legion-boundaries.mmd +23 -0
  315. package/templates/legion-flow.mmd +15 -0
  316. package/templates/legion-sequence.mmd +14 -0
  317. package/templates/legion-state.mmd +7 -0
  318. package/templates/metric-card.svg +29 -0
  319. package/templates/observability-map.svg +101 -0
  320. package/templates/pipeline-audit.md +108 -0
  321. package/templates/release-metrics.vl.json +98 -0
  322. package/templates/service-topology.mmd +47 -0
  323. package/templates/status-report.md +32 -0
  324. package/templates/system-map.svg +53 -0
  325. package/templates/timeseries.vl.json +71 -0
  326. package/templates/tsx-interactive-counter.tsx +19 -0
  327. package/templates/tsx-status-report.tsx +31 -0
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: facet
3
+ description: Use when creating, publishing, validating, exporting, or inspecting Facet artifacts, diagrams, charts, SVG, Markdown, or gallery output; when choosing between Markdown, Mermaid, SVG, chart, HTML, and TSX; or when a Facet CLI envelope or typed error needs interpretation.
4
+ ---
5
+
6
+ # Facet
7
+
8
+ Install `@legionworks/facet` with `bun add -g` (recommended), `npm i -g`, or
9
+ `pnpm add -g`; use `bunx @legionworks/facet <verb>` without a global install.
10
+ Bun `1.4.0` or newer is the required runtime. npm and pnpm are distribution
11
+ channels only. The pinned browser downloads on the first visual read-back.
12
+
13
+ Facet stores and verifies artifact bytes through a versioned CLI envelope. Keep
14
+ artifact bytes separate from host capabilities.
15
+
16
+ ## Choose the artifact type
17
+
18
+ | Need | Type | Why |
19
+ | ------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------- |
20
+ | Prose or mixed documents | Markdown | Portable authored content. |
21
+ | One diagram | Mermaid | A single declarative diagram. |
22
+ | Authored vector output | SVG | Precise vector output under the SVG contract. |
23
+ | Data visualization | chart | Vega-Lite data visualization; `svg` is default, `canvas` only when chosen for the chart. |
24
+ | Static semantic report | HTML | Static semantic report with vendored styles. |
25
+ | Component composition | TSX: `static` \| `interactive` | Use `static` by default; use `interactive` only when runtime state or behavior is required. |
26
+
27
+ ## Publish and check
28
+
29
+ 1. Cold-start check: when environment, launch, or permission errors are present, run `facet doctor`; it is read-only and reports literal repair commands. Check status next and start only when needed: `facet status --start`.
30
+ 2. Create or reuse an artifact: `facet create --project-id <id> --slug <slug> --title <title>`.
31
+ 3. Publish source bytes with one of these forms:
32
+
33
+ ```sh
34
+ facet publish --artifact-id <id> --type markdown --file path/to/source.md
35
+ facet publish --artifact-id <id> --type markdown --file -
36
+ printf '%s' "$SOURCE" | facet publish --artifact-id <id> --type markdown
37
+ ```
38
+
39
+ Use `--watch --file <path>` for operator-led iterative authoring; it streams one publish envelope per changed attempt until Ctrl-C.
40
+
41
+ 4. For every publish response, branch on top-level `ok`. If it is true, separately inspect `data.verdict.status`, `tier`, `artifactId`, and `revisionSha`. `ok: true` only confirms command transport; `status: "error"` is a stored validation result, not a transport refusal.
42
+ 5. Read back Tier 0/latest by default: `facet read-back --artifact-id <id>`. Omit `--revision-sha` for the latest revision; pass its SHA only to pin reproducible read-back. Request `--tier 1` or `--tier visual` only when browser-backed evidence is needed.
43
+
44
+ ## Execution and evidence
45
+
46
+ `--execution static|interactive` applies to TSX only. `static` is the default;
47
+ `interactive` is for client runtime state or behavior. Verdicts carry execution
48
+ only for TSX. Interactive TSX is animation-eligible without probing; static
49
+ artifacts become animation-eligible only after live CSS or Web Animations are
50
+ detected.
51
+
52
+ Gallery display defaults to `system`; users may select `dark` or `light`, and
53
+ the tab persists that choice. Tier 1 stays dark for deterministic parity.
54
+
55
+ ## Export
56
+
57
+ Use `facet export <artifactId> --format source` for stored source, or
58
+ `--format render` for retained Tier 1 evidence. Exports write local artifact and
59
+ mandatory sidecar paths plus byte count by default. Use `--include-bytes` only
60
+ when an envelope consumer genuinely needs base64 bytes. Render export preserves
61
+ the detected evidence format:
62
+
63
+ - detected WebP for new captures;
64
+ - detected PNG for legacy captures.
65
+
66
+ ## Promotion
67
+
68
+ Promotion is operator-only. The CLI discovers the token from
69
+ `FACET_PROMOTE_TOKEN`, then `FACET_HOME/secrets/promote.token`; never put a
70
+ token on argv.
71
+
72
+ ## Error quick reference
73
+
74
+ | Error | Do next |
75
+ | ------------------------ | ---------------------------------------------------------------------------------------------------------------- |
76
+ | `invalid_request` | Correct the command flags, artifact type, or source bytes, then retry. |
77
+ | `artifact_not_found` | List or create the artifact, then use its returned ID. |
78
+ | `revision_not_found` | Omit the SHA for latest, or replace it with a returned revision SHA. |
79
+ | `duplicate_revision` | Use `error.details.revisionSha` as the existing revision SHA; do not republish identical bytes. |
80
+ | `evidence_unavailable` | Request Tier 1 evidence for that revision before a render export, or export source instead. |
81
+ | `output_unwritable` | Choose a writable `--out` path, resolve collisions, or use `--force` deliberately. |
82
+ | `tier0_*` | Repair the Tier 0 worker or its environment; a returned `status: "error"` is a verdict, not this envelope error. |
83
+ | `tier1_*` | Repair browser-backed validation availability, then retry the requested Tier 1 read-back. |
84
+ | `screenshot_unavailable` | Inspect `Verdict.screenshotError`; it is a degraded stored verdict field, not an envelope error. |
85
+
86
+ ## Boundaries
87
+
88
+ - Promotion is operator-only. Secrets never enter artifacts, notes, or argv.
89
+ - The service is byte-dumb: do not call loopback routes, import renderers, or
90
+ parse artifact bytes outside the documented CLI workflow.
91
+ - Do not run `facet open` as an agent: it launches local `xdg-open` for human
92
+ display on the operator's desktop. Ensure the service is active with
93
+ `facet status --start`, then use the documented [Steel/browser workflow](../../docs/guides/agents.md)
94
+ or ask a human to inspect.
95
+ - Preserve stdout exactly so callers can parse the versioned envelope.
96
+ - With shell access, the CLI is the integration — MCP is for structured-tool-only hosts, even when the host owns an MCP client. On that surface, `facet_open_url` is safe because it always uses `--no-launch`.
97
+
98
+ ## Deeper reference
99
+
100
+ → [Agent workflow](../../docs/guides/agents.md) · [CLI](../../docs/reference/cli.md) · [MCP](../../docs/reference/mcp.md) · [Export](../../docs/reference/export.md) · [Validation](../../docs/reference/validation.md) · [HTML](../../docs/reference/html.md) · [TSX](../../docs/reference/tsx.md) · [Security](../../docs/reference/security.md)
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Loopback HTTP client used by every CLI verb.
3
+ *
4
+ * The CLI talks to the service over HTTP only — never to the
5
+ * repository or any renderer module directly. `sendCommand` wraps
6
+ * the request in a strict `FacetEnvelope`, sends it with Bearer auth
7
+ * and the correct Host header, and parses the response through the
8
+ * shared `FacetEnvelopeSchema` so a forged or drift-ed response
9
+ * surfaces as a typed error envelope.
10
+ *
11
+ * Source bytes for `publish` are base64-encoded on the wire; the
12
+ * schema's `PublishBytesSchema` accepts only valid base64, so an
13
+ * invalid input is rejected at parse time (typed `invalid_request`).
14
+ */
15
+
16
+ import { errEnvelope, parseEnvelope, type FacetEnvelope } from "../shared/contracts/envelope";
17
+ import {
18
+ CommandRequestSchema,
19
+ CommandResultSchema,
20
+ type CommandRequest,
21
+ type CommandResult,
22
+ } from "../shared/contracts/commands";
23
+
24
+ const TIER1_TRACE = process.env.FACET_TIER1_TRACE === "1";
25
+ export const FACET_CLIENT_COMMAND_TIMEOUT_MS = 75_000;
26
+
27
+ function traceTier1Transport(stage: string): void {
28
+ if (!TIER1_TRACE) return;
29
+ process.stderr.write(`[tier1-transport] ${stage}\n`);
30
+ }
31
+
32
+ import { FacetError } from "../shared/errors/facet-error";
33
+ import { generateRequestId } from "../shared/util/time";
34
+ import { isMutationMethod } from "../service/security/http-guards";
35
+ import type { ArtifactType } from "../shared/contracts/artifact-types";
36
+ import type { Renderer } from "../shared/contracts/renderers";
37
+ import type { ScreenshotError, TsxExecutionMode } from "../shared/contracts/validation";
38
+
39
+ export interface FacetClientOptions {
40
+ readonly baseUrl: string;
41
+ readonly installToken: string;
42
+ readonly promoteToken?: string;
43
+ readonly commandTimeoutMs?: number;
44
+ /** `fetch` indirection so tests can stub network. */
45
+ readonly fetchImpl?: typeof fetch;
46
+ }
47
+
48
+ export interface SendCommandOptions {
49
+ /** Set true for any state-changing verb (default: derived from verb name). */
50
+ readonly isMutation?: boolean;
51
+ /** Extra headers to merge into the request (Content-Type is added automatically on mutations). */
52
+ readonly extraHeaders?: Readonly<Record<string, string>>;
53
+ }
54
+
55
+ export class FacetClient {
56
+ readonly #baseUrl: string;
57
+ readonly #installToken: string;
58
+ readonly #promoteToken: string | undefined;
59
+ readonly #commandTimeoutMs: number;
60
+ readonly #fetchImpl: typeof fetch;
61
+
62
+ constructor(options: FacetClientOptions) {
63
+ this.#baseUrl = options.baseUrl.replace(/\/$/, "");
64
+ this.#installToken = options.installToken;
65
+ this.#promoteToken = options.promoteToken;
66
+ this.#commandTimeoutMs = options.commandTimeoutMs ?? FACET_CLIENT_COMMAND_TIMEOUT_MS;
67
+ this.#fetchImpl = options.fetchImpl ?? fetch;
68
+ }
69
+
70
+ /**
71
+ * Build a typed `CommandRequest` for the given verb + args, parse
72
+ * it through the shared schema (so a malformed CLI surface fails
73
+ * here, not in the service), wrap it in an envelope, POST to the
74
+ * loopback service, and return the parsed response envelope.
75
+ *
76
+ * On a non-2xx HTTP response the service still returns a valid
77
+ * envelope (the router wraps every error in one), so the only
78
+ * `throw` path is a transport-level failure (connection refused,
79
+ * DNS error, etc.). Those surface as `FacetError("invalid_envelope",
80
+ * ...)` so the caller can wrap them in a typed envelope for the
81
+ * adapter.
82
+ */
83
+ async sendCommand(
84
+ command: CommandRequest,
85
+ options: SendCommandOptions = {},
86
+ ): Promise<FacetEnvelope<CommandResult>> {
87
+ // Re-parse the command via the strict schema — a forged or
88
+ // drift-ed CLI surface that produced a malformed request fails
89
+ // here with a typed issue list, never reaching the wire.
90
+ const parsed = CommandRequestSchema.parse(command);
91
+ const innerRequestId = parsed.requestId;
92
+ const isMutation = options.isMutation ?? isMutationMethodFor(parsed.command);
93
+ const envelope: FacetEnvelope<CommandRequest> = {
94
+ schemaVersion: "facet.v1",
95
+ requestId: generateRequestId(),
96
+ ok: true,
97
+ data: parsed,
98
+ };
99
+ const headers: Record<string, string> = {
100
+ authorization: `Bearer ${parsed.command === "promote" && this.#promoteToken !== undefined ? this.#promoteToken : this.#installToken}`,
101
+ host: new URL(this.#baseUrl).host,
102
+ };
103
+ if (isMutation) {
104
+ headers["content-type"] = "application/json";
105
+ }
106
+ if (options.extraHeaders) {
107
+ Object.assign(headers, options.extraHeaders);
108
+ }
109
+ let res: Response;
110
+ let text: string;
111
+ const signal = AbortSignal.timeout(this.#commandTimeoutMs);
112
+ try {
113
+ traceTier1Transport(`client:fetch:start command=${parsed.command}`);
114
+ res = await this.#fetchImpl(`${this.#baseUrl}/api/v1/commands`, {
115
+ method: "POST",
116
+ headers,
117
+ body: JSON.stringify(envelope),
118
+ signal,
119
+ });
120
+ traceTier1Transport(`client:fetch:complete command=${parsed.command} status=${res.status}`);
121
+ traceTier1Transport(`client:body:start command=${parsed.command}`);
122
+ text = await res.text();
123
+ traceTier1Transport(`client:body:complete command=${parsed.command} bytes=${text.length}`);
124
+ } catch (error) {
125
+ const timedOut = signal.aborted;
126
+ throw new FacetError(
127
+ "invalid_envelope",
128
+ timedOut
129
+ ? `Connection timed out after ${this.#commandTimeoutMs}ms`
130
+ : `Connection failed: ${(error as Error).message}`,
131
+ {
132
+ retryable: true,
133
+ cause: error,
134
+ details: timedOut
135
+ ? {
136
+ reason: "connection_timeout",
137
+ host: this.#baseUrl,
138
+ timeoutMs: this.#commandTimeoutMs,
139
+ }
140
+ : { reason: "connection_failed", host: this.#baseUrl },
141
+ },
142
+ );
143
+ }
144
+ let body: unknown;
145
+ try {
146
+ body = JSON.parse(text);
147
+ } catch {
148
+ throw new FacetError(
149
+ "invalid_envelope",
150
+ `Service response is not valid JSON (status ${res.status})`,
151
+ { retryable: false, details: { status: res.status } },
152
+ );
153
+ }
154
+ const parsed2 = parseEnvelope(body);
155
+ if (!parsed2.ok) {
156
+ // The service's response was a parse failure; surface a typed
157
+ // error envelope with the same shape as the wire would.
158
+ return errEnvelope(innerRequestId, parsed2.body) as FacetEnvelope<CommandResult>;
159
+ }
160
+ return parsed2.envelope as FacetEnvelope<CommandResult>;
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Map a wire `command` to whether it counts as a mutation. The CLI
166
+ * must set Content-Type for mutations; the service enforces the same
167
+ * check on the other side via the shared `isMutationMethod` helper.
168
+ */
169
+ function isMutationMethodFor(_command: CommandRequest["command"]): boolean {
170
+ // The CLI only ever sends POST, so every wire command is a
171
+ // mutation from the HTTP-method standpoint; the service's
172
+ // mutation guard is always satisfied. This helper exists so the
173
+ // CLI can express intent in one place and the security
174
+ // classification stays single-sourced.
175
+ void isMutationMethod;
176
+ return true;
177
+ }
178
+
179
+ /**
180
+ * Result of a successful publish. `artifactId` and `revisionSha` are
181
+ * the canonical IDs every downstream verb (read-back, status, open)
182
+ * carries. Publish is browser-free, so Tier 1 fields remain `null`;
183
+ * request a visual read-back for a Tier 1 verdict.
184
+ */
185
+ export interface PublishArtifactResult {
186
+ readonly artifactId: string;
187
+ readonly revisionSha: string;
188
+ readonly tier1Status: string | null;
189
+ readonly tier1ScreenshotPath: string | null;
190
+ readonly tier1ScreenshotError: ScreenshotError | null;
191
+ }
192
+
193
+ export interface PublishArtifactOptions {
194
+ readonly artifactType: ArtifactType;
195
+ readonly renderer?: Renderer;
196
+ readonly bytes: ArrayBuffer;
197
+ readonly slug?: string;
198
+ readonly note?: string;
199
+ readonly execution?: TsxExecutionMode;
200
+ }
201
+
202
+ /**
203
+ * Publish a new revision through an existing FacetClient. Resolves
204
+ * the artifact (create-then-publish) and returns the canonical IDs.
205
+ * Used by the acceptance test fixture helper, which needs to spawn
206
+ * a service via the existing CLI infrastructure.
207
+ */
208
+ export async function publishArtifact(
209
+ client: FacetClient,
210
+ options: PublishArtifactOptions,
211
+ ): Promise<PublishArtifactResult> {
212
+ const slug = options.slug ?? `acceptance-${crypto.randomUUID().slice(0, 8)}`;
213
+ const createRes = await client.sendCommand({
214
+ command: "create",
215
+ requestId: generateRequestId(),
216
+ projectId: "/facet",
217
+ slug,
218
+ title: slug,
219
+ });
220
+ if (!createRes.ok) {
221
+ throw FacetError.from(createRes.error);
222
+ }
223
+ const parsed = CommandResultSchema.parse(createRes.data);
224
+ if (parsed.command !== "create") {
225
+ throw new FacetError("invalid_envelope", `expected create result, got ${parsed.command}`);
226
+ }
227
+ const artifactId = parsed.artifact.id;
228
+ const base64 = btoa(String.fromCharCode(...new Uint8Array(options.bytes)));
229
+ const publishRes = await client.sendCommand({
230
+ command: "publish",
231
+ requestId: generateRequestId(),
232
+ artifactId,
233
+ artifactType: options.artifactType,
234
+ renderer: options.renderer ?? "svg",
235
+ bytes: base64,
236
+ ...(options.note !== undefined ? { note: options.note } : {}),
237
+ ...(options.execution === undefined ? {} : { execution: options.execution }),
238
+ });
239
+ if (!publishRes.ok) {
240
+ throw FacetError.from(publishRes.error);
241
+ }
242
+ const parsedPublish = CommandResultSchema.parse(publishRes.data);
243
+ if (parsedPublish.command !== "publish") {
244
+ throw new FacetError(
245
+ "invalid_envelope",
246
+ `expected publish result, got ${parsedPublish.command}`,
247
+ );
248
+ }
249
+ // Publish stays browser-free. Tier 1 is explicit through read-back.
250
+ const tier1Status = parsedPublish.tier1Verdict?.status ?? null;
251
+ return {
252
+ artifactId,
253
+ revisionSha: parsedPublish.revision.sha256,
254
+ tier1Status,
255
+ tier1ScreenshotPath: parsedPublish.tier1Verdict?.screenshotPath ?? null,
256
+ tier1ScreenshotError: parsedPublish.tier1Verdict?.screenshotError ?? null,
257
+ };
258
+ }
259
+
260
+ export interface ReadBackOptions {
261
+ readonly artifactId: string;
262
+ readonly revisionSha: string;
263
+ readonly tier: 0 | 1 | "visual";
264
+ }
265
+
266
+ /**
267
+ * Run `readBack` against an existing FacetClient and return the
268
+ * typed verdict. The service normalizes "visual" → 1 internally.
269
+ *
270
+ * The return shape is the canonical Verdict surface (plus the
271
+ * request-level `renderer`): the typed envelope is schema-validated
272
+ * by `CommandResultSchema.parse`, so the verdict that crosses the
273
+ * wire is already the canonical shape. Field-by-field copying here
274
+ * is the field-drop class (Must 2) — every field added since the
275
+ * helper was written was silently dropped. Pass the parsed verdict
276
+ * through so a new field on the schema is included by default.
277
+ */
278
+ export async function readBack(
279
+ client: FacetClient,
280
+ options: ReadBackOptions,
281
+ ): Promise<ReadBackResult> {
282
+ const res = await client.sendCommand({
283
+ command: "readBack",
284
+ requestId: generateRequestId(),
285
+ artifactId: options.artifactId,
286
+ revisionSha: options.revisionSha,
287
+ tier: options.tier,
288
+ });
289
+ if (!res.ok) {
290
+ throw FacetError.from(res.error);
291
+ }
292
+ const parsed = CommandResultSchema.parse(res.data);
293
+ if (parsed.command !== "readBack") {
294
+ throw new FacetError("invalid_envelope", `expected readBack result, got ${parsed.command}`);
295
+ }
296
+ return {
297
+ renderer: parsed.renderer,
298
+ verdict: parsed.verdict,
299
+ };
300
+ }
301
+
302
+ /**
303
+ * The canonical read-back result the client returns. The `verdict`
304
+ * is the canonical Verdict shape (already schema-validated by the
305
+ * envelope parse), and `renderer` is the request-level binding
306
+ * that accompanies it. Anything else lives on the verdict.
307
+ */
308
+ export type ReadBackResult = {
309
+ readonly renderer: Renderer;
310
+ readonly verdict: import("../shared/contracts/validation").Verdict;
311
+ };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * `facet create` — register a new artifact.
3
+ *
4
+ * The CLI maps the public verb (`create`) to the wire command
5
+ * (`create`) and builds a strict `CreateRequest` from the parsed
6
+ * argv. Required flags: `--project-id`, `--slug`, `--title`. The
7
+ * request id is minted here so the response envelope carries the
8
+ * CLI-side correlation id (the service echoes the request id back
9
+ * in its own envelope).
10
+ *
11
+ * Input-validation errors throw `FacetError("invalid_request", ...)`
12
+ * so the envelope preserves the typed `invalid_request` code (the
13
+ * house pattern for argv-shape failures) — the main catch recognises
14
+ * a pre-existing FacetError and passes `toBody()` through unchanged.
15
+ */
16
+
17
+ import { generateRequestId } from "../../shared/util/time";
18
+ import { FacetError } from "../../shared/errors/facet-error";
19
+ import type { CreateRequest } from "../../shared/contracts/commands/requests";
20
+
21
+ export function buildCreateRequest(
22
+ args: Readonly<Record<string, string | boolean>>,
23
+ ): CreateRequest {
24
+ const projectId = args["project-id"];
25
+ const slug = args["slug"];
26
+ const title = args["title"];
27
+ if (typeof projectId !== "string" || projectId.length === 0) {
28
+ throw new FacetError("invalid_request", "--project-id is required for create", {
29
+ retryable: false,
30
+ });
31
+ }
32
+ if (typeof slug !== "string" || slug.length === 0) {
33
+ throw new FacetError("invalid_request", "--slug is required for create", {
34
+ retryable: false,
35
+ });
36
+ }
37
+ if (typeof title !== "string" || title.length === 0) {
38
+ throw new FacetError("invalid_request", "--title is required for create", {
39
+ retryable: false,
40
+ });
41
+ }
42
+ return {
43
+ command: "create",
44
+ requestId: generateRequestId(),
45
+ projectId,
46
+ slug,
47
+ title,
48
+ };
49
+ }