@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,240 @@
1
+ import { createLogger } from "../shared/logging/logger";
2
+ import type {
3
+ InsecureLevel,
4
+ IsolationProbeResult,
5
+ Tier0IsolationProbe,
6
+ Tier0RunnerFactory,
7
+ Tier1AvailabilityProbe,
8
+ Tier1Runner,
9
+ Tier1RunnerFactory,
10
+ } from "../shared/contracts/validation";
11
+ import { FacetError } from "../shared/errors/facet-error";
12
+
13
+ import { startFacetService, type RunningService, type StartServiceOptions } from "./server";
14
+ import { defaultInsecureReason } from "./verdict-enrichment";
15
+
16
+ interface MutableServiceArgs {
17
+ dbPath?: string;
18
+ installTokenPath?: string;
19
+ promoteTokenPath?: string;
20
+ lockPath?: string;
21
+ evidencePath?: string;
22
+ idleTimeoutMs?: number;
23
+ tier0RunnerPath?: string;
24
+ tier1RunnerPath?: string;
25
+ }
26
+
27
+ export interface ParsedServiceArgs {
28
+ readonly dbPath?: string;
29
+ readonly installTokenPath?: string;
30
+ readonly promoteTokenPath?: string;
31
+ readonly lockPath?: string;
32
+ readonly evidencePath?: string;
33
+ readonly idleTimeoutMs?: number;
34
+ readonly tier0RunnerPath?: string;
35
+ readonly tier1RunnerPath?: string;
36
+ }
37
+
38
+ export interface LoadedTier0Runner {
39
+ readonly factory: Tier0RunnerFactory;
40
+ readonly probe?: Tier0IsolationProbe;
41
+ }
42
+
43
+ export interface LoadedTier1Runner {
44
+ readonly factory: Tier1RunnerFactory;
45
+ readonly probe?: Tier1AvailabilityProbe;
46
+ }
47
+
48
+ export interface ServiceRunnerModules {
49
+ readonly tier0: LoadedTier0Runner;
50
+ readonly loadTier1: () => Promise<LoadedTier1Runner>;
51
+ }
52
+
53
+ export interface ServiceProcessHooks {
54
+ readonly onSignal?: (signal: "SIGTERM" | "SIGINT", handler: () => void) => void;
55
+ readonly startService?: (options: StartServiceOptions) => Promise<RunningService>;
56
+ readonly writeStderr?: (line: string) => void;
57
+ }
58
+
59
+ export function parseServiceArgs(argv: readonly string[]): ParsedServiceArgs {
60
+ const out: MutableServiceArgs = {};
61
+ for (let i = 0; i < argv.length; i += 1) {
62
+ const arg = argv[i];
63
+ if (arg === "--db-path") {
64
+ const value = argv[i + 1];
65
+ if (value !== undefined) {
66
+ out.dbPath = value;
67
+ i += 1;
68
+ }
69
+ } else if (arg === "--install-token-path") {
70
+ const value = argv[i + 1];
71
+ if (value !== undefined) {
72
+ out.installTokenPath = value;
73
+ i += 1;
74
+ }
75
+ } else if (arg === "--promote-token-path") {
76
+ const value = argv[i + 1];
77
+ if (value !== undefined) {
78
+ out.promoteTokenPath = value;
79
+ i += 1;
80
+ }
81
+ } else if (arg === "--lock-path") {
82
+ const value = argv[i + 1];
83
+ if (value !== undefined) {
84
+ out.lockPath = value;
85
+ i += 1;
86
+ }
87
+ } else if (arg === "--evidence-path") {
88
+ const value = argv[i + 1];
89
+ if (value !== undefined) {
90
+ out.evidencePath = value;
91
+ i += 1;
92
+ }
93
+ } else if (arg === "--idle-timeout-ms") {
94
+ const value = Number(argv[i + 1]);
95
+ if (Number.isFinite(value) && value > 0) {
96
+ out.idleTimeoutMs = value;
97
+ i += 1;
98
+ }
99
+ } else if (arg === "--tier0-runner-path") {
100
+ const value = argv[i + 1];
101
+ if (value !== undefined) {
102
+ out.tier0RunnerPath = value;
103
+ i += 1;
104
+ }
105
+ } else if (arg === "--tier1-runner-path") {
106
+ const value = argv[i + 1];
107
+ if (value !== undefined) {
108
+ out.tier1RunnerPath = value;
109
+ i += 1;
110
+ }
111
+ }
112
+ }
113
+ return out;
114
+ }
115
+
116
+ function parseInsecureLevel(raw: string | undefined): InsecureLevel {
117
+ if (raw === undefined || raw === "0") return 0;
118
+ if (raw === "1" || raw === "2" || raw === "3") return Number(raw) as InsecureLevel;
119
+ throw new FacetError(
120
+ "invalid_request",
121
+ `FACET_INSECURE must be one of 0, 1, 2, or 3; got '${raw}'`,
122
+ { retryable: false },
123
+ );
124
+ }
125
+
126
+ function isAutoFallbackEnabled(raw: string | undefined): boolean {
127
+ return raw === "1";
128
+ }
129
+
130
+ function boundedProbeReason(result: IsolationProbeResult): string {
131
+ const reason = result.reason?.trim() || "unavailable";
132
+ return reason.slice(0, 240);
133
+ }
134
+
135
+ function requireProbe<T>(probe: T | undefined, label: string): T {
136
+ if (probe !== undefined) return probe;
137
+ throw new FacetError("internal", `${label} probe is required for insecure auto fallback`, {
138
+ retryable: false,
139
+ });
140
+ }
141
+
142
+ function createLazyTier1Runner(
143
+ loadTier1: () => Promise<LoadedTier1Runner>,
144
+ level: InsecureLevel,
145
+ ): Tier1Runner {
146
+ let runner: Promise<Tier1Runner> | undefined;
147
+ return async (input) => {
148
+ runner ??= loadTier1().then((tier1) => tier1.factory(level));
149
+ return runner.then((loaded) => loaded(input));
150
+ };
151
+ }
152
+
153
+ export async function runServiceProcess(
154
+ argv: readonly string[],
155
+ env: NodeJS.ProcessEnv,
156
+ loadModules: (args: ParsedServiceArgs, env: NodeJS.ProcessEnv) => Promise<ServiceRunnerModules>,
157
+ hooks: ServiceProcessHooks = {},
158
+ ): Promise<number> {
159
+ const logger = createLogger({ component: "main" });
160
+ const args = parseServiceArgs(argv);
161
+ const onSignal = hooks.onSignal ?? ((signal, handler) => process.on(signal, handler));
162
+ const startService = hooks.startService ?? startFacetService;
163
+ const writeStderr = hooks.writeStderr ?? ((line) => process.stderr.write(line));
164
+ let running: RunningService | null = null;
165
+ const shutdown = async (signal: string): Promise<void> => {
166
+ logger.info("service.signal", { signal });
167
+ if (running) await running.stop();
168
+ };
169
+
170
+ onSignal("SIGTERM", () => void shutdown("SIGTERM"));
171
+ onSignal("SIGINT", () => void shutdown("SIGINT"));
172
+
173
+ try {
174
+ const forcedLevel = parseInsecureLevel(env.FACET_INSECURE);
175
+ const autoFallback = isAutoFallbackEnabled(env.FACET_INSECURE_AUTO);
176
+ const modules = await loadModules(args, env);
177
+ let insecureLevel = forcedLevel;
178
+ let insecureReason: string | null = null;
179
+ let tier1Module: LoadedTier1Runner | undefined;
180
+ if (autoFallback && forcedLevel < 2) {
181
+ tier1Module = await modules.loadTier1();
182
+ const tier0Probe = requireProbe(modules.tier0.probe, "Tier 0 isolation");
183
+ const tier0 = await tier0Probe();
184
+ if (!tier0.available) {
185
+ insecureLevel = 2;
186
+ insecureReason = `auto:${boundedProbeReason(tier0)}`;
187
+ } else if (forcedLevel === 0) {
188
+ const tier1Probe = requireProbe(tier1Module.probe, "Tier 1 availability");
189
+ const tier1 = await tier1Probe();
190
+ if (!tier1.available) {
191
+ insecureLevel = 1;
192
+ insecureReason = `auto:${boundedProbeReason(tier1)}`;
193
+ }
194
+ }
195
+ }
196
+ if (insecureLevel > 0) {
197
+ const reason = insecureReason ?? defaultInsecureReason(insecureLevel);
198
+ writeStderr(`WARN: FACET_INSECURE=${insecureLevel} — ${reason}\n`);
199
+ }
200
+ running = await startService({
201
+ ...(args.dbPath !== undefined ? { dbPath: args.dbPath } : {}),
202
+ ...(args.installTokenPath !== undefined ? { installTokenPath: args.installTokenPath } : {}),
203
+ ...(args.promoteTokenPath !== undefined ? { promoteTokenPath: args.promoteTokenPath } : {}),
204
+ ...(args.lockPath !== undefined ? { lockPath: args.lockPath } : {}),
205
+ ...(args.evidencePath !== undefined ? { evidencePath: args.evidencePath } : {}),
206
+ ...(args.idleTimeoutMs !== undefined ? { idleTimeoutMs: args.idleTimeoutMs } : {}),
207
+ tier0Runner: modules.tier0.factory(insecureLevel),
208
+ tier1Runner: createLazyTier1Runner(
209
+ () => (tier1Module === undefined ? modules.loadTier1() : Promise.resolve(tier1Module)),
210
+ insecureLevel,
211
+ ),
212
+ insecureLevel,
213
+ insecureReason,
214
+ logger,
215
+ });
216
+ writeStderr(
217
+ `${JSON.stringify({
218
+ event: "service.ready",
219
+ component: "main",
220
+ timestamp: new Date().toISOString(),
221
+ pid: process.pid,
222
+ port: running.port,
223
+ url: running.url,
224
+ ...(insecureLevel > 0
225
+ ? {
226
+ insecureLevel,
227
+ insecureReason: insecureReason ?? defaultInsecureReason(insecureLevel),
228
+ }
229
+ : {}),
230
+ })}\n`,
231
+ );
232
+ await running.waitUntilIdle();
233
+ await running.stop();
234
+ return 0;
235
+ } catch (error) {
236
+ const message = error instanceof Error ? error.message : String(error);
237
+ logger.error("service.failed", { errorMessage: message });
238
+ return 1;
239
+ }
240
+ }
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Command-route guard helpers.
3
+ *
4
+ * These support functions implement the per-request checks that sit
5
+ * between `handleCommand`'s header read and its dispatch call:
6
+ * request-metadata extraction, body-cap enforcement, JSON parsing,
7
+ * and the `FacetError.code → HTTP status` mapping used by the
8
+ * catch-all error path. Gallery routes use `resolveGalleryRequest` for the
9
+ * shared host → auth → lease → revision → artifact sequence, while command
10
+ * routes retain their longer orchestration in `router.ts`.
11
+ */
12
+
13
+ import { FacetError } from "../shared/errors/facet-error";
14
+ import { requireAnyBearer } from "./security/auth";
15
+ import {
16
+ checkHostOrigin,
17
+ isCrossSiteRejection,
18
+ isMissingHostRejection,
19
+ resolveHost,
20
+ } from "./security/host-origin";
21
+ import type { GalleryLeaseManager, GalleryLease } from "./security/leases";
22
+ import type { Artifact, Revision } from "../shared/contracts/artifact";
23
+ import type { ArtifactRepository } from "./store/repository";
24
+
25
+ /**
26
+ * Hard ceiling on the raw HTTP body. Distinct from `SOURCE_CAP_BYTES`
27
+ * (which governs artifact payload bytes after decode). 16 MiB is well
28
+ * above 5 MiB source × 4/3 base64 envelope slack, so a legitimate
29
+ * client never hits it.
30
+ */
31
+ export const RAW_BODY_CAP_BYTES = 16 * 1024 * 1024;
32
+
33
+ /**
34
+ * Request metadata extracted from headers — read BEFORE the body.
35
+ * Auth, mutation headers, and the raw size cap all branch on this
36
+ * object; the body itself is read only after those checks pass. This
37
+ * split keeps unauthenticated requests from touching the body at all.
38
+ */
39
+ export interface RequestMeta {
40
+ readonly url: string;
41
+ readonly method: string;
42
+ readonly host: string | null;
43
+ readonly origin: string | null;
44
+ readonly secFetchSite: string | null;
45
+ readonly contentType: string | null;
46
+ readonly authorization: string | null;
47
+ readonly contentLength: number | null;
48
+ readonly headers: { get(name: string): string | null };
49
+ }
50
+
51
+ export function readRequestMeta(req: Request): RequestMeta {
52
+ const url = new URL(req.url);
53
+ const cl = req.headers.get("content-length");
54
+ return {
55
+ url: url.pathname + url.search,
56
+ method: req.method,
57
+ host: req.headers.get("host"),
58
+ origin: req.headers.get("origin"),
59
+ secFetchSite: req.headers.get("sec-fetch-site"),
60
+ contentType: req.headers.get("content-type"),
61
+ authorization: req.headers.get("authorization"),
62
+ contentLength: cl !== null ? Number(cl) : null,
63
+ headers: req.headers,
64
+ };
65
+ }
66
+
67
+ export interface GalleryRequestDeps {
68
+ readonly expectedHost: string | (() => string);
69
+ readonly ownOrigin: string | (() => string);
70
+ readonly installToken: string;
71
+ readonly leases: GalleryLeaseManager;
72
+ readonly repository: ArtifactRepository;
73
+ }
74
+
75
+ export type GalleryRequestResolution =
76
+ | {
77
+ readonly ok: true;
78
+ readonly meta: RequestMeta;
79
+ readonly lease: GalleryLease;
80
+ readonly artifactId: string;
81
+ readonly revision: Revision;
82
+ readonly artifact: Artifact;
83
+ }
84
+ | { readonly ok: false; readonly response: Response };
85
+
86
+ export function statusForHostCheck(error: FacetError | undefined): number {
87
+ if (isCrossSiteRejection(error)) return 403;
88
+ if (isMissingHostRejection(error)) return 421;
89
+ return 400;
90
+ }
91
+
92
+ /** Resolve the shared host, capability, and bound-revision gallery guards. */
93
+ export function resolveGalleryRequest(
94
+ req: Request,
95
+ deps: GalleryRequestDeps,
96
+ ): GalleryRequestResolution {
97
+ const meta = readRequestMeta(req);
98
+ const hostCheck = checkHostOrigin({
99
+ method: meta.method,
100
+ host: meta.host,
101
+ origin: meta.origin,
102
+ secFetchSite: meta.secFetchSite,
103
+ expectedHost: resolveHost(deps.expectedHost),
104
+ ownOrigin: resolveHost(deps.ownOrigin),
105
+ });
106
+ if (!hostCheck.ok)
107
+ return {
108
+ ok: false,
109
+ response: new Response(null, { status: statusForHostCheck(hostCheck.error) }),
110
+ };
111
+ const auth = requireAnyBearer(meta.authorization, [deps.installToken]);
112
+ const leaseId = meta.headers.get("x-gallery-lease");
113
+ const artifactId = meta.headers.get("x-gallery-artifact");
114
+ if (!auth.ok || leaseId === null || artifactId === null) {
115
+ return {
116
+ ok: false,
117
+ response: new Response(null, { status: 401, headers: { "cache-control": "no-store" } }),
118
+ };
119
+ }
120
+ const lease = deps.leases
121
+ .list()
122
+ .find((entry) => entry.leaseId === leaseId && entry.artifactId === artifactId);
123
+ if (lease === undefined) {
124
+ return {
125
+ ok: false,
126
+ response: new Response(null, { status: 401, headers: { "cache-control": "no-store" } }),
127
+ };
128
+ }
129
+ const revisionSha = new URL(req.url).searchParams.get("revisionSha");
130
+ if (revisionSha === null || revisionSha.length === 0) {
131
+ return {
132
+ ok: false,
133
+ response: new Response(null, { status: 400, headers: { "cache-control": "no-store" } }),
134
+ };
135
+ }
136
+ const revision = deps.repository.getRevisionBySha(artifactId, revisionSha);
137
+ if (revision === null) {
138
+ return {
139
+ ok: false,
140
+ response: new Response(null, { status: 404, headers: { "cache-control": "no-store" } }),
141
+ };
142
+ }
143
+ const artifact = deps.repository.getArtifactById(artifactId);
144
+ if (artifact === null) {
145
+ return {
146
+ ok: false,
147
+ response: new Response(null, { status: 404, headers: { "cache-control": "no-store" } }),
148
+ };
149
+ }
150
+ return { ok: true, meta, lease, artifactId, revision, artifact };
151
+ }
152
+
153
+ /**
154
+ * Read the request body up to `RAW_BODY_CAP_BYTES`. The Content-Length
155
+ * header is the cheap first check (no body read needed when it already
156
+ * exceeds the cap); a second check after `req.text()` defends against a
157
+ * missing or lying Content-Length. Both violations throw the same typed
158
+ * `payload_too_large` error so the catch path returns a uniform 413.
159
+ */
160
+ export async function readCappedBody(req: Request, contentLength: number | null): Promise<string> {
161
+ if (contentLength !== null) {
162
+ if (!Number.isFinite(contentLength) || contentLength < 0) {
163
+ throw new FacetError("invalid_request", "Invalid Content-Length", { retryable: false });
164
+ }
165
+ if (contentLength > RAW_BODY_CAP_BYTES) {
166
+ throw new FacetError("payload_too_large", "Request body exceeds raw cap", {
167
+ retryable: false,
168
+ details: { capBytes: RAW_BODY_CAP_BYTES, receivedBytes: contentLength },
169
+ });
170
+ }
171
+ }
172
+ const bytes = new Uint8Array(await req.arrayBuffer());
173
+ const receivedBytes = bytes.byteLength;
174
+ if (receivedBytes > RAW_BODY_CAP_BYTES) {
175
+ throw new FacetError("payload_too_large", "Request body exceeds raw cap", {
176
+ retryable: false,
177
+ details: { capBytes: RAW_BODY_CAP_BYTES, receivedBytes },
178
+ });
179
+ }
180
+ return new TextDecoder().decode(bytes);
181
+ }
182
+
183
+ /**
184
+ * Parse the body text as JSON. On failure, return a sentinel symbol
185
+ * (`Symbol.for("invalid_json")`) rather than throwing — the caller
186
+ * decides how to surface it as an envelope error, keeping this helper
187
+ * free of envelope concerns.
188
+ */
189
+ export function parseBody(bodyText: string): unknown {
190
+ try {
191
+ return JSON.parse(bodyText);
192
+ } catch {
193
+ return Symbol.for("invalid_json");
194
+ }
195
+ }
196
+
197
+ export const INVALID_JSON = Symbol.for("invalid_json");
198
+
199
+ /**
200
+ * Map a `FacetError.code` to its HTTP status. Store codes that share
201
+ * a status with non-store codes (e.g. `constraint` and `duplicate_revision`
202
+ * both 409) are grouped so the switch arms stay flat. The default 500
203
+ * covers unrecognized codes; tier1_* failures are handled in runTier1Safe
204
+ * before reaching this function, while other codes without an explicit arm
205
+ * fall through to 500.
206
+ */
207
+ export function statusFor(error: FacetError): number {
208
+ switch (error.code) {
209
+ case "artifact_not_found":
210
+ case "revision_not_found":
211
+ case "template_not_found":
212
+ case "evidence_unavailable":
213
+ return 404;
214
+ case "payload_too_large":
215
+ return 413;
216
+ case "reserved_not_implemented":
217
+ case "unsupported_reserved_type":
218
+ case "invalid_envelope":
219
+ case "invalid_request":
220
+ case "unknown_schema_version":
221
+ return 400;
222
+ case "tier0_unavailable":
223
+ return 503;
224
+ case "duplicate_revision":
225
+ case "constraint":
226
+ case "foreign_key":
227
+ case "immutable_revision":
228
+ case "invalid_artifact_type":
229
+ case "revision_capacity_pinned":
230
+ return 409;
231
+ case "tier0_timeout":
232
+ case "tier0_protocol_error":
233
+ case "tier0_worker_died":
234
+ case "tier0_output_cap":
235
+ return 422;
236
+ default:
237
+ return 500;
238
+ }
239
+ }