@underpostnet/cyberia 3.2.90 → 3.3.73

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 (490) hide show
  1. package/.dockerignore +1 -0
  2. package/.env.example +16 -0
  3. package/.github/workflows/coverall.cyberia.ci.yml +107 -0
  4. package/.github/workflows/cyberia-client.cd.yml +6 -18
  5. package/.github/workflows/cyberia-server.cd.yml +6 -18
  6. package/.github/workflows/docker-image.cyberia-client.ci.yml +8 -5
  7. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +8 -3
  8. package/.github/workflows/docker-image.cyberia-server.ci.yml +8 -5
  9. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +8 -5
  10. package/.github/workflows/docker-image.engine-cyberia.ci.yml +15 -1
  11. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +15 -1
  12. package/.github/workflows/engine-cyberia.cd.yml +6 -78
  13. package/.github/workflows/engine-cyberia.ci.yml +4 -5
  14. package/.github/workflows/ghpkg.ci.yml +42 -21
  15. package/.github/workflows/hardhat.ci.yml +12 -11
  16. package/.github/workflows/npmpkg.ci.yml +17 -15
  17. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -16
  18. package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
  19. package/.github/workflows/release.cd.yml +2 -9
  20. package/.prettierignore +1 -0
  21. package/AGENTS.md +47 -0
  22. package/CHANGELOG.md +581 -2417
  23. package/CLI-HELP.md +416 -127
  24. package/Dockerfile +25 -9
  25. package/Dockerfile.dev +25 -9
  26. package/Dockerfile.test +4 -4
  27. package/README.md +21 -1
  28. package/bin/build.js +70 -43
  29. package/bin/build.template.js +8 -3
  30. package/bin/cyberia.js +372 -135
  31. package/bin/deploy.js +18 -104
  32. package/bin/index.js +372 -135
  33. package/compose.env +16 -0
  34. package/conf.js +93 -4
  35. package/deploy/cyberia-client/deploy.sh +47 -0
  36. package/deploy/cyberia-client/package.sh +18 -0
  37. package/deploy/cyberia-client/state.sh +14 -0
  38. package/deploy/cyberia-server/deploy.sh +47 -0
  39. package/deploy/cyberia-server/package.sh +18 -0
  40. package/deploy/cyberia-server/state.sh +14 -0
  41. package/deploy/dd-cyberia/deploy.sh +22 -0
  42. package/deploy/dd-cyberia/init.sh +55 -0
  43. package/deploy/dd-cyberia/package.sh +17 -0
  44. package/deploy/dd-cyberia/state.sh +14 -0
  45. package/deploy/dd-cyberia/sync-deploy.sh +116 -0
  46. package/deploy/lib/github-actions-logging.sh +594 -0
  47. package/deploy/lib/host.sh +185 -0
  48. package/deploy/lib/state.sh +62 -0
  49. package/deploy/pwa-microservices-template/deploy.sh +45 -0
  50. package/deploy/release/deploy.sh +32 -0
  51. package/deployment.yaml +7 -2
  52. package/docker-compose.yml +31 -37
  53. package/examples/static-page/README.md +19 -19
  54. package/examples/static-page/ssr-components/CustomPage.js +2 -2
  55. package/hardhat/hardhat.config.js +1 -6
  56. package/hardhat/package-lock.json +104 -43
  57. package/hardhat/package.json +3 -3
  58. package/manifests/alertmanager/deployment.yaml +81 -0
  59. package/manifests/blackbox-exporter/deployment.yaml +75 -0
  60. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +36 -7
  61. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +77 -0
  62. package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
  63. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  64. package/manifests/grafana/deployment.yaml +26 -7
  65. package/manifests/grafana/kustomization.yaml +1 -0
  66. package/manifests/grafana/service.yaml +6 -1
  67. package/manifests/mariadb/pv.yaml +1 -0
  68. package/manifests/mongodb/statefulset.yaml +9 -2
  69. package/manifests/mysql/pv-pvc.yaml +14 -1
  70. package/manifests/postgresql/pv.yaml +12 -1
  71. package/manifests/postgresql/pvc.yaml +1 -0
  72. package/manifests/prometheus/deployment.yaml +18 -22
  73. package/package.json +83 -10
  74. package/scripts/audit-selinux.sh +64 -0
  75. package/scripts/event-monitor.sh +56 -0
  76. package/scripts/gpu-diag.sh +0 -0
  77. package/scripts/ip-info.sh +0 -0
  78. package/scripts/k3s-node-setup.sh +30 -14
  79. package/scripts/kubeadm-node-setup.sh +40 -31
  80. package/scripts/lxd-vm-setup.sh +0 -0
  81. package/scripts/maas-nat-firewalld.sh +0 -0
  82. package/scripts/maas-setup.sh +8 -8
  83. package/scripts/nat-iptables.sh +2 -0
  84. package/scripts/rhel-grpc-setup.sh +0 -0
  85. package/scripts/rocky-kickstart.sh +25 -9
  86. package/scripts/selinux-normalize.sh +907 -0
  87. package/scripts/shutdown-machine.sh +843 -0
  88. package/scripts/test-monitor.sh +3 -3
  89. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +1 -1
  90. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +2 -2
  91. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +2 -2
  92. package/src/api/core/core.controller.js +1 -1
  93. package/src/api/core/core.router.js +2 -2
  94. package/src/api/core/core.service.js +2 -2
  95. package/src/api/crypto/crypto.controller.js +1 -1
  96. package/src/api/crypto/crypto.service.js +1 -1
  97. package/src/api/cyberia-action/cyberia-action.controller.js +1 -1
  98. package/src/api/cyberia-action/cyberia-action.router.js +1 -1
  99. package/src/api/cyberia-action/cyberia-action.service.js +2 -2
  100. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -1
  101. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +1 -1
  102. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +2 -2
  103. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
  104. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
  105. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
  106. package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
  107. package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
  108. package/src/api/cyberia-entity/cyberia-entity.service.js +2 -2
  109. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +1 -1
  110. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +1 -1
  111. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +2 -2
  112. package/src/api/cyberia-instance/cyberia-fallback-capture.js +466 -0
  113. package/src/api/cyberia-instance/cyberia-fallback-default-items.js +63 -0
  114. package/src/api/cyberia-instance/cyberia-fallback-world.js +16 -1
  115. package/src/api/cyberia-instance/cyberia-instance-map.service.js +1 -1
  116. package/src/api/cyberia-instance/cyberia-instance.controller.js +4 -1
  117. package/src/api/cyberia-instance/cyberia-instance.router.js +15 -2
  118. package/src/api/cyberia-instance/cyberia-instance.service.js +45 -2
  119. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
  120. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +1 -0
  121. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
  122. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +2 -2
  123. package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
  124. package/src/api/cyberia-map/cyberia-map.router.js +1 -1
  125. package/src/api/cyberia-map/cyberia-map.service.js +2 -2
  126. package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
  127. package/src/api/cyberia-quest/cyberia-quest.router.js +1 -1
  128. package/src/api/cyberia-quest/cyberia-quest.service.js +2 -2
  129. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
  130. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
  131. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
  132. package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
  133. package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
  134. package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
  135. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +16 -29
  136. package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
  137. package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
  138. package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
  139. package/src/api/default/default.controller.js +1 -1
  140. package/src/api/default/default.router.js +1 -1
  141. package/src/api/default/default.service.js +2 -2
  142. package/src/api/document/document.controller.js +1 -1
  143. package/src/api/document/document.router.js +1 -1
  144. package/src/api/document/document.service.js +2 -2
  145. package/src/api/file/file.controller.js +1 -1
  146. package/src/api/file/file.router.js +1 -1
  147. package/src/api/file/file.service.js +2 -2
  148. package/src/api/instance/instance.controller.js +1 -1
  149. package/src/api/instance/instance.router.js +1 -1
  150. package/src/api/instance/instance.service.js +2 -2
  151. package/src/api/ipfs/ipfs.controller.js +1 -1
  152. package/src/api/ipfs/ipfs.router.js +2 -2
  153. package/src/api/ipfs/ipfs.service.js +2 -2
  154. package/src/api/object-layer/object-layer.controller.js +1 -1
  155. package/src/api/object-layer/object-layer.model.js +204 -3
  156. package/src/api/object-layer/object-layer.router.js +2 -2
  157. package/src/api/object-layer/object-layer.service.js +4 -17
  158. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
  159. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
  160. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
  161. package/src/api/test/test.controller.js +1 -1
  162. package/src/api/test/test.service.js +1 -1
  163. package/src/api/user/guest.service.js +3 -3
  164. package/src/api/user/user.controller.js +1 -1
  165. package/src/api/user/user.router.js +2 -2
  166. package/src/api/user/user.service.js +4 -4
  167. package/src/api.js +5 -9
  168. package/src/cli/app.js +391 -0
  169. package/src/cli/baremetal.js +30 -36
  170. package/src/cli/client.js +306 -0
  171. package/src/cli/cloud-init.js +3 -3
  172. package/src/cli/cluster.js +242 -135
  173. package/src/cli/db.js +126 -67
  174. package/src/cli/deploy.js +158 -60
  175. package/src/cli/docker-compose.js +5 -5
  176. package/src/cli/domains.js +184 -0
  177. package/src/cli/dotenv-store.js +143 -0
  178. package/src/cli/event.js +2281 -0
  179. package/src/cli/fs.js +3 -3
  180. package/src/cli/host.js +672 -0
  181. package/src/cli/image.js +4 -7
  182. package/src/cli/index.js +426 -134
  183. package/src/cli/ipfs.js +34 -30
  184. package/src/cli/kickstart.js +1 -1
  185. package/src/cli/kubectl.js +218 -41
  186. package/src/cli/lxd.js +3 -3
  187. package/src/cli/monitor.js +1359 -26
  188. package/src/cli/package.js +90 -0
  189. package/src/cli/release.js +73 -34
  190. package/src/cli/repository.js +564 -367
  191. package/src/cli/run.js +362 -654
  192. package/src/cli/secrets.js +2158 -964
  193. package/src/cli/ssh.js +651 -331
  194. package/src/cli/state.js +394 -0
  195. package/src/cli/static.js +2 -2
  196. package/src/cli/system.js +26 -13
  197. package/src/cli/test.js +199 -125
  198. package/src/cli/vultr.js +623 -0
  199. package/src/cli/wireguard.js +2930 -0
  200. package/src/client/components/core/CalendarCore.js +53 -96
  201. package/src/client/components/core/Docs.js +258 -211
  202. package/src/client/components/core/FileExplorer.js +0 -21
  203. package/src/client/components/core/PanelForm.js +4 -4
  204. package/src/client/components/core/Repository.js +56 -0
  205. package/src/client/components/core/Translate.js +7 -0
  206. package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
  207. package/src/client/components/core/Wallet.js +0 -11
  208. package/src/client/components/core/Worker.js +7 -1
  209. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -2
  210. package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +368 -0
  211. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +35 -1
  212. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +4 -0
  213. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
  214. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
  215. package/src/client/components/default/AppShellDefault.js +1 -1
  216. package/src/client/components/default/SettingsDefault.js +2 -2
  217. package/src/client/components/itemledger/SettingsItemledger.js +2 -2
  218. package/src/client/components/underpost/AppShellUnderpost.js +3 -2
  219. package/src/client/components/underpost/SettingsUnderpost.js +2 -2
  220. package/src/client/public/cyberia-docs/ARCHITECTURE.md +17 -3
  221. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +32 -5
  222. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +1 -1
  223. package/src/client/public/cyberia-docs/CYBERIA.md +21 -1
  224. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
  225. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  226. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  227. package/src/client/public/default/android-chrome-144x144.png +0 -0
  228. package/src/client/public/default/android-chrome-192x192.png +0 -0
  229. package/src/client/public/default/android-chrome-256x256.png +0 -0
  230. package/src/client/public/default/android-chrome-36x36.png +0 -0
  231. package/src/client/public/default/android-chrome-384x384.png +0 -0
  232. package/src/client/public/default/android-chrome-48x48.png +0 -0
  233. package/src/client/public/default/android-chrome-512x512.png +0 -0
  234. package/src/client/public/default/android-chrome-72x72.png +0 -0
  235. package/src/client/public/default/android-chrome-96x96.png +0 -0
  236. package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
  237. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  238. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  239. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  240. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  241. package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
  242. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  243. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  244. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  245. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  246. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  247. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  248. package/src/client/public/default/apple-touch-icon.png +0 -0
  249. package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
  250. package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
  251. package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
  252. package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
  253. package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
  254. package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
  255. package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
  256. package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
  257. package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
  258. package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
  259. package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
  260. package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
  261. package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
  262. package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
  263. package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
  264. package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
  265. package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
  266. package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
  267. package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
  268. package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
  269. package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
  270. package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
  271. package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
  272. package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
  273. package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
  274. package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
  275. package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
  276. package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
  277. package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
  278. package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
  279. package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
  280. package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
  281. package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
  282. package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
  283. package/src/client/public/default/assets/background/white.jpg +0 -0
  284. package/src/client/public/default/browserconfig.xml +1 -1
  285. package/src/client/public/default/favicon-16x16.png +0 -0
  286. package/src/client/public/default/favicon-32x32.png +0 -0
  287. package/src/client/public/default/favicon-48x48.png +0 -0
  288. package/src/client/public/default/favicon.ico +0 -0
  289. package/src/client/public/default/manifest.webmanifest +5 -5
  290. package/src/client/public/default/mstile-144x144.png +0 -0
  291. package/src/client/public/default/mstile-150x150.png +0 -0
  292. package/src/client/public/default/mstile-310x150.png +0 -0
  293. package/src/client/public/default/mstile-310x310.png +0 -0
  294. package/src/client/public/default/mstile-70x70.png +0 -0
  295. package/src/client/public/default/yandex-browser-50x50.png +0 -0
  296. package/src/client/public/default/yandex-browser-manifest.json +1 -1
  297. package/src/client/services/cyberia-instance/cyberia-instance.service.js +40 -0
  298. package/src/client/services/object-layer/object-layer.management.js +4 -4
  299. package/src/client/ssr/RootDocument.js +1 -1
  300. package/src/client/ssr/body/404.js +1 -1
  301. package/src/client/ssr/body/500.js +1 -1
  302. package/src/client/ssr/body/CacheControl.js +1 -1
  303. package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
  304. package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
  305. package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
  306. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
  307. package/src/client/ssr/head/CryptokoynScripts.js +1 -1
  308. package/src/client/ssr/head/Css.js +1 -1
  309. package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
  310. package/src/client/ssr/head/DefaultScripts.js +1 -1
  311. package/src/client/ssr/head/ItemledgerScripts.js +1 -1
  312. package/src/client/ssr/head/Microdata.js +1 -1
  313. package/src/client/ssr/head/Production.js +1 -1
  314. package/src/client/ssr/head/Pwa.js +1 -1
  315. package/src/client/ssr/head/PwaItemledger.js +1 -1
  316. package/src/client/ssr/head/Seo.js +1 -1
  317. package/src/client/ssr/head/UnderpostScripts.js +1 -1
  318. package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
  319. package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
  320. package/src/client/ssr/views/Cyberia404.js +7 -5
  321. package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
  322. package/src/client/ssr/views/Maintenance.js +1 -1
  323. package/src/client/ssr/views/NoNetworkConnection.js +1 -1
  324. package/src/client/ssr/views/Test.js +1 -1
  325. package/src/client/sw/core.sw.js +20 -14
  326. package/src/client-builder/client-build-docs.js +61 -46
  327. package/src/client-builder/client-build-live.js +2 -2
  328. package/src/client-builder/client-build.js +58 -44
  329. package/src/client-builder/client-dev-server.js +3 -3
  330. package/src/client-builder/client-icons.js +2 -2
  331. package/src/client-builder/ssr.js +3 -3
  332. package/src/client.build.js +3 -6
  333. package/src/client.dev.js +3 -6
  334. package/src/db/DataBaseProvider.js +186 -3
  335. package/src/db/mariadb/MariaDB.js +3 -2
  336. package/src/db/mongo/MongoBootstrap.js +273 -101
  337. package/src/db/mongo/MongooseDB.js +11 -8
  338. package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
  339. package/src/grpc/cyberia/grpc-server.js +1 -1
  340. package/src/index.js +103 -23
  341. package/src/mailer/EmailRender.js +2 -2
  342. package/src/mailer/MailerInterceptor.js +87 -0
  343. package/src/mailer/MailerProvider.js +50 -6
  344. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +1 -1
  345. package/src/projects/cyberia/besu-genesis-generator.js +3 -3
  346. package/src/projects/cyberia/catalog-cyberia.js +54 -6
  347. package/src/projects/cyberia/gemini-client.js +1 -1
  348. package/src/projects/cyberia/generate-saga.js +15 -24
  349. package/src/projects/cyberia/hot-reload-trigger.js +1 -1
  350. package/src/projects/cyberia/instance-data.js +22 -3
  351. package/src/projects/cyberia/ipfs-client.js +4 -4
  352. package/src/projects/cyberia/map-preview-generator.js +1 -1
  353. package/src/projects/cyberia/object-layer.js +12 -22
  354. package/src/projects/cyberia/semantic-layer-generator.js +1 -1
  355. package/src/projects/underpost/catalog-underpost.js +13 -2
  356. package/src/proxy.js +4 -7
  357. package/src/runtime/cyberia-client/Dockerfile +9 -4
  358. package/src/runtime/cyberia-client/Dockerfile.dev +8 -2
  359. package/src/runtime/cyberia-server/Dockerfile +9 -4
  360. package/src/runtime/cyberia-server/Dockerfile.dev +8 -2
  361. package/src/runtime/engine-cyberia/Dockerfile +25 -9
  362. package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
  363. package/src/runtime/engine-cyberia/Dockerfile.test +4 -4
  364. package/src/runtime/engine-cyberia/compose.env +16 -0
  365. package/src/runtime/engine-cyberia/docker-compose.yml +31 -37
  366. package/src/runtime/express/Express.js +32 -27
  367. package/src/runtime/lampp/Lampp.js +22 -70
  368. package/src/runtime/nginx/Nginx.js +1 -1
  369. package/src/runtime/wp/Wp.js +16 -48
  370. package/src/server/{catalog.js → build/catalog.js} +15 -5
  371. package/src/server/build/coverage.js +117 -0
  372. package/src/server/build/execution.js +402 -0
  373. package/src/server/build/package.js +465 -0
  374. package/src/server/build/testing.js +528 -0
  375. package/src/server/{dns.js → network/dns.js} +241 -14
  376. package/src/server/network/forward-proxy.js +541 -0
  377. package/src/server/{middlewares.js → network/middlewares.js} +59 -4
  378. package/src/server/network/node-capability.js +98 -0
  379. package/src/server/{peer.js → network/peer.js} +3 -3
  380. package/src/server/{proxy.js → network/proxy.js} +7 -13
  381. package/src/server/network/router.js +356 -0
  382. package/src/server/{tls.js → network/tls.js} +2 -2
  383. package/src/server/network/underpost-compression.js +186 -0
  384. package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +26 -16
  385. package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +19 -3
  386. package/src/server/{cri.js → ops/cri.js} +2 -2
  387. package/src/server/ops/cron.js +949 -0
  388. package/src/server/ops/event-notification.js +284 -0
  389. package/src/server/{logger.js → ops/logger.js} +108 -23
  390. package/src/server/ops/monitoring.js +1724 -0
  391. package/src/server/ops/systemd.js +259 -0
  392. package/src/server/{conf.js → runtime/conf.js} +521 -520
  393. package/src/server/runtime/config-scope.js +215 -0
  394. package/src/server/runtime/environment.js +149 -0
  395. package/src/server/{process.js → runtime/process.js} +153 -13
  396. package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
  397. package/src/server/{runtime.js → runtime/runtime.js} +12 -8
  398. package/src/server/{start.js → runtime/start.js} +128 -39
  399. package/src/server/{auth.js → security/auth.js} +7 -8
  400. package/src/server/security/container-storage.js +382 -0
  401. package/src/server/{crypto.js → security/crypto.js} +1 -1
  402. package/src/server/security/selinux.js +185 -0
  403. package/src/server/{backup.js → storage/backup.js} +17 -8
  404. package/src/server/{data-query.js → storage/data-query.js} +1 -1
  405. package/src/server/{downloader.js → storage/downloader.js} +2 -2
  406. package/src/server/storage/repository.js +68 -0
  407. package/src/server.js +4 -7
  408. package/src/ws/IoInterface.js +1 -1
  409. package/src/ws/IoServer.js +1 -1
  410. package/src/ws/core/core.ws.connection.js +1 -1
  411. package/src/ws/core/core.ws.emit.js +1 -1
  412. package/src/ws/core/core.ws.server.js +1 -1
  413. package/src/ws/default/default.ws.connection.js +1 -1
  414. package/src/ws/default/default.ws.emit.js +1 -1
  415. package/src/ws/default/default.ws.server.js +1 -1
  416. package/test/e2e/event-e2e-public-ingress-down.js +45 -0
  417. package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
  418. package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
  419. package/test/{api.test.js → integration/app/api.test.js} +33 -14
  420. package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +30 -0
  421. package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +2 -1
  422. package/test/integration/app/cyberia/cyberia-load.test.js +467 -0
  423. package/test/integration/app/cyberia/fallback-world-capture.test.js +253 -0
  424. package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
  425. package/test/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
  426. package/test/integration/infra/1-security/config-scope.test.js +305 -0
  427. package/test/integration/infra/1-security/container-storage.test.js +356 -0
  428. package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
  429. package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
  430. package/test/integration/infra/1-security/selinux.test.js +69 -0
  431. package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
  432. package/test/integration/infra/1-security/systemd-service.test.js +70 -0
  433. package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
  434. package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
  435. package/test/integration/infra/2-network/node-capability.test.js +182 -0
  436. package/test/integration/infra/2-network/wireguard-cli.test.js +1163 -0
  437. package/test/integration/infra/2-network/wireguard-edge.test.js +1731 -0
  438. package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
  439. package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
  440. package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
  441. package/test/integration/infra/4-ingress/deploy-routes.test.js +379 -0
  442. package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
  443. package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
  444. package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +47 -6
  445. package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +56 -4
  446. package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
  447. package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
  448. package/test/integration/infra/5-observability/event-notification.test.js +197 -0
  449. package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
  450. package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
  451. package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
  452. package/test/support/shell-harness.js +73 -0
  453. package/test/unit/build-template.test.js +97 -0
  454. package/test/unit/build-workflow-offline.test.js +132 -0
  455. package/test/unit/catalog.test.js +71 -0
  456. package/test/unit/client-build-docs.test.js +334 -0
  457. package/test/unit/conf-loading.test.js +303 -0
  458. package/test/unit/conf-resolution.test.js +979 -0
  459. package/test/unit/coverage-artifact.test.js +130 -0
  460. package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
  461. package/test/unit/deploy-env-resolution.test.js +38 -0
  462. package/test/unit/deploy-log-table.test.js +447 -0
  463. package/test/unit/execution-profiles.test.js +219 -0
  464. package/test/unit/fleet-sync-source.test.js +149 -0
  465. package/test/unit/logger-redaction.test.js +112 -0
  466. package/test/unit/package.test.js +403 -0
  467. package/test/unit/prepare-host.test.js +299 -0
  468. package/test/unit/process-environment.test.js +18 -0
  469. package/test/unit/propagation-message.test.js +78 -0
  470. package/test/unit/redeploy-plan.test.js +24 -0
  471. package/test/unit/release-bump.test.js +474 -0
  472. package/test/unit/ssh-output-redaction.test.js +28 -0
  473. package/test/unit/start-options.test.js +46 -0
  474. package/test/unit/test-tiers.test.js +193 -0
  475. package/vitest.config.js +58 -0
  476. package/.nycrc +0 -9
  477. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
  478. package/manifests/mongodb/pv-pvc.yaml +0 -59
  479. package/scripts/link-local-underpost-cli.sh +0 -6
  480. package/src/api/user/user.build.js +0 -16
  481. package/src/cli/env.js +0 -177
  482. package/src/client/components/core/Blockchain.js +0 -41
  483. package/src/client/components/core/Blog.js +0 -9
  484. package/src/client/components/core/KeyboardAvoidance.js +0 -145
  485. package/src/client/public/default/assets/background/dark.jpg +0 -0
  486. package/src/client/public/default/assets/background/white0-min.jpg +0 -0
  487. package/src/client/public/default/assets/background/white0.jpg +0 -0
  488. package/src/client/public/doc/favicon.ico +0 -0
  489. package/src/client/public/doc/sitemap +0 -148
  490. package/src/server/cron.js +0 -476
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The coverage HTML report as a build artifact: where a run leaves it, where an assembled
3
+ * deploy artifact carries it, and what is published when nothing was carried.
4
+ *
5
+ * The report is produced by the test stage (`npm run test:coverage`) and bundled into the
6
+ * deploy artifact at {@link COVERAGE_BUNDLE_DIRECTORY}. A workload container only ever
7
+ * copies it: running a test runner inside a pod costs minutes of the build phase, needs the
8
+ * dev dependency tree, and — because a suite's expected non-zero exits latch the runtime
9
+ * status contract — publishes a false `error` to the deployment monitor.
10
+ *
11
+ * @module src/server/build/coverage.js
12
+ * @namespace UnderpostCoverage
13
+ */
14
+
15
+ import fs from 'fs-extra';
16
+ import { UNDERPOST_TESTING } from './testing.js';
17
+
18
+ /**
19
+ * @constant COVERAGE_BUNDLE_DIRECTORY
20
+ * @description Path, relative to a source tree root, where an assembled deploy artifact
21
+ * carries the report. Deliberately not under `coverage/`, which every engine `.gitignore`
22
+ * excludes, so the bundled artifact is publishable source rather than a local run's output.
23
+ * @memberof UnderpostCoverage
24
+ */
25
+ const COVERAGE_BUNDLE_DIRECTORY = 'docs/coverage';
26
+
27
+ const normalizeRoot = (root) => `${root ?? ''}`.replace(/\/+$/, '');
28
+
29
+ /**
30
+ * @method coverageReportCandidates
31
+ * @description Every place a coverage HTML report can live in a source tree, most local
32
+ * first: the reporters a run writes, then the bundled artifact a deploy artifact carries.
33
+ * @param {string} root - Source tree root.
34
+ * @returns {string[]} Candidate report directories.
35
+ * @memberof UnderpostCoverage
36
+ */
37
+ const coverageReportCandidates = (root) => {
38
+ const base = normalizeRoot(root);
39
+ if (!base) return [];
40
+ const coverage = `${base}/${UNDERPOST_TESTING.coverageDirectory}`;
41
+ return [`${coverage}/html`, `${coverage}/lcov-report`, coverage, `${base}/${COVERAGE_BUNDLE_DIRECTORY}`];
42
+ };
43
+
44
+ /**
45
+ * @method resolveCoverageReportPath
46
+ * @description First candidate that actually carries an HTML index. A directory holding
47
+ * only `lcov.info` is not a report and is not published.
48
+ * @param {string} root - Source tree root.
49
+ * @returns {string|undefined} Report directory, or undefined when none is available.
50
+ * @memberof UnderpostCoverage
51
+ */
52
+ const resolveCoverageReportPath = (root) =>
53
+ coverageReportCandidates(root).find((candidate) => fs.existsSync(`${candidate}/index.html`));
54
+
55
+ /**
56
+ * @method bundleCoverageReport
57
+ * @description Copies the report of `fromRoot` into `toRoot`'s bundle directory, so the
58
+ * assembled artifact carries it into every container started from that source.
59
+ * @param {string} fromRoot - Tree the report was generated in.
60
+ * @param {string} toRoot - Assembled artifact root.
61
+ * @returns {{bundled: boolean, from?: string, to: string}} What was bundled, and where.
62
+ * @memberof UnderpostCoverage
63
+ */
64
+ const bundleCoverageReport = (fromRoot, toRoot) => {
65
+ const to = `${normalizeRoot(toRoot)}/${COVERAGE_BUNDLE_DIRECTORY}`;
66
+ const from = resolveCoverageReportPath(fromRoot);
67
+ if (!from) return { bundled: false, to };
68
+ fs.emptyDirSync(to);
69
+ fs.copySync(from, to);
70
+ return { bundled: true, from, to };
71
+ };
72
+
73
+ /**
74
+ * @method coverageUnavailablePage
75
+ * @description The page served where a report was expected but none was bundled. A static
76
+ * document, never an invitation to generate one at runtime.
77
+ * @param {object} [options]
78
+ * @param {string} [options.command] - Command that produces the report.
79
+ * @returns {string} HTML document.
80
+ * @memberof UnderpostCoverage
81
+ */
82
+ const coverageUnavailablePage = ({ command = 'npm run test:coverage' } = {}) => `<!doctype html>
83
+ <html lang="en">
84
+ <head>
85
+ <meta charset="utf-8" />
86
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
87
+ <meta name="robots" content="noindex" />
88
+ <title>Coverage report unavailable</title>
89
+ <style>
90
+ :root { color-scheme: light dark; }
91
+ body { margin: 0; display: grid; place-items: center; min-height: 100vh;
92
+ font: 16px/1.6 system-ui, -apple-system, Segoe UI, Roboto, sans-serif; }
93
+ main { max-width: 34rem; padding: 2rem; }
94
+ h1 { font-size: 1.25rem; margin: 0 0 .75rem; }
95
+ p { margin: 0 0 .75rem; opacity: .8; }
96
+ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9em; }
97
+ </style>
98
+ </head>
99
+ <body>
100
+ <main>
101
+ <h1>Coverage report unavailable</h1>
102
+ <p>This build carried no coverage report. The report is generated by the test stage and
103
+ bundled into the deploy artifact under <code>${COVERAGE_BUNDLE_DIRECTORY}</code>.</p>
104
+ <p>Run <code>${command}</code> in the build environment, then assemble and publish the
105
+ artifact again.</p>
106
+ </main>
107
+ </body>
108
+ </html>
109
+ `;
110
+
111
+ export {
112
+ COVERAGE_BUNDLE_DIRECTORY,
113
+ bundleCoverageReport,
114
+ coverageReportCandidates,
115
+ coverageUnavailablePage,
116
+ resolveCoverageReportPath,
117
+ };
@@ -0,0 +1,402 @@
1
+ /**
2
+ * This module is pure with respect to the environment: it classifies, resolves and
3
+ * decides, but never executes. Enforcement belongs to `src/server/runtime/process.js`,
4
+ * which owns the subprocess boundary.
5
+ *
6
+ * @module src/server/build/execution.js
7
+ * @namespace UnderpostExecution
8
+ */
9
+
10
+ import { execFileSync } from 'node:child_process';
11
+ import nodePath from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+
14
+ const packageRoot = nodePath.resolve(nodePath.dirname(fileURLToPath(import.meta.url)), '../../..');
15
+
16
+ /**
17
+ * @constant EXECUTION_PROFILE_ENV_KEY
18
+ * @description Environment key the active profile rides on into child processes. Reading it
19
+ * back is what makes a nested `underpost` invocation stay inside its parent's profile.
20
+ * @memberof UnderpostExecution
21
+ */
22
+ const EXECUTION_PROFILE_ENV_KEY = 'UNDERPOST_EXECUTION_PROFILE';
23
+
24
+ /**
25
+ * @constant CAPABILITIES
26
+ * @description The side-effect classes a command can exercise, ordered least to most
27
+ * privileged. The order is load-bearing: a compound command takes the capability of its
28
+ * most privileged segment, so `kubectl create … | kubectl apply -f -` is a cluster write
29
+ * rather than a cluster read.
30
+ * @memberof UnderpostExecution
31
+ */
32
+ const CAPABILITIES = ['fs', 'git', 'net', 'cluster:read', 'host:write', 'cluster:write'];
33
+
34
+ const capabilityRank = (capability) => CAPABILITIES.indexOf(capability);
35
+
36
+ /**
37
+ * @constant EXECUTION_PROFILES
38
+ * @description The profile table. `permits` is the closed set of capabilities a run may
39
+ * exercise; everything else is elided.
40
+ *
41
+ * `reads: 'neutral'` is what lets a denied read stay invisible to its caller: a blocked
42
+ * `cluster:read` returns the empty result that 200+ `silentOnError` call sites already
43
+ * treat as "not present", so no caller needs to learn a new failure mode.
44
+ * @memberof UnderpostExecution
45
+ */
46
+ const EXECUTION_PROFILES = {
47
+ // Full access. The default, and the only profile that may mutate a cluster or a host.
48
+ LIVE_CLUSTER: {
49
+ name: 'LIVE_CLUSTER',
50
+ permits: [...CAPABILITIES],
51
+ reads: 'live',
52
+ description: 'Full access: cluster and host mutation permitted.',
53
+ },
54
+ // Manifest and artifact generation. Filesystem and git are build outputs, not
55
+ // environment mutation, so they stay permitted — a build that regenerates manifests
56
+ // and commits them is doing its job.
57
+ HERMETIC_BUILD: {
58
+ name: 'HERMETIC_BUILD',
59
+ permits: ['fs', 'git'],
60
+ reads: 'neutral',
61
+ description: 'Build outputs only: no cluster, host or network side effects.',
62
+ },
63
+ // Nothing executes. Every effect is reported as the intent it would have carried out.
64
+ OFFLINE_DRY_RUN: {
65
+ name: 'OFFLINE_DRY_RUN',
66
+ permits: [],
67
+ reads: 'neutral',
68
+ description: 'Nothing executes; every command is reported as intent.',
69
+ },
70
+ };
71
+
72
+ const DEFAULT_PROFILE = 'LIVE_CLUSTER';
73
+
74
+ /**
75
+ * @constant PROFILE_ALIAS_FLAGS
76
+ * @description Legacy bypass flags mapped onto the profile that subsumes them, so an
77
+ * operator who already knows the old flag lands in the right profile without learning a
78
+ * new one. The flags stay on the CLI surface — they are published entrypoints.
79
+ *
80
+ * Each alias is guarded by `when`, because a bypass flag is only equivalent to a profile
81
+ * in the mode it was written for. `--disable-update-underpost-config` means "skip one
82
+ * projection step" during a live deploy, but means "this run cannot reach a cluster at
83
+ * all" during a manifest build; aliasing it unconditionally would silently turn a real
84
+ * deploy into a no-op. Flags that are step toggles in every mode are deliberately absent:
85
+ * they keep their own guards, which the profile gate sits underneath rather than replaces.
86
+ * @memberof UnderpostExecution
87
+ */
88
+ const PROFILE_ALIAS_FLAGS = {
89
+ disableUpdateUnderpostConfig: {
90
+ profile: 'HERMETIC_BUILD',
91
+ when: (options) => options?.buildManifest === true,
92
+ },
93
+ };
94
+
95
+ // Verbs that only read cluster state. Everything else kubectl accepts is treated as a
96
+ // write: an unrecognised verb must not be assumed harmless.
97
+ const KUBECTL_READ_VERBS = new Set([
98
+ 'get',
99
+ 'describe',
100
+ 'logs',
101
+ 'top',
102
+ 'explain',
103
+ 'api-resources',
104
+ 'api-versions',
105
+ 'version',
106
+ 'cluster-info',
107
+ 'config',
108
+ 'auth',
109
+ 'diff',
110
+ ]);
111
+
112
+ // kubectl flags that consume the token after them, which would otherwise be mistaken
113
+ // for the verb (`kubectl -n default get pods`).
114
+ const KUBECTL_VALUE_FLAGS = new Set(['-n', '--namespace', '--context', '--kubeconfig', '--server', '--user']);
115
+
116
+ const HOST_WRITE_BINARIES = new Set([
117
+ 'systemctl',
118
+ 'journalctl',
119
+ 'dnf',
120
+ 'yum',
121
+ 'apt',
122
+ 'apt-get',
123
+ 'rpm',
124
+ 'firewall-cmd',
125
+ 'setenforce',
126
+ 'semanage',
127
+ 'restorecon',
128
+ 'chcon',
129
+ 'mount',
130
+ 'umount',
131
+ 'mkfs',
132
+ 'parted',
133
+ 'lvcreate',
134
+ 'modprobe',
135
+ 'sysctl',
136
+ 'useradd',
137
+ 'groupadd',
138
+ 'usermod',
139
+ 'lxc',
140
+ 'lxd',
141
+ 'virsh',
142
+ 'virt-install',
143
+ 'docker',
144
+ 'podman',
145
+ 'crictl',
146
+ 'kind',
147
+ 'kubeadm',
148
+ 'k3s',
149
+ 'wg',
150
+ 'wg-quick',
151
+ 'ip',
152
+ 'iptables',
153
+ 'nft',
154
+ 'haproxy',
155
+ ]);
156
+
157
+ // `gh` reaches a remote service and writes to it (`gh secret set`, `gh release create`), so it
158
+ // is a network effect rather than local filesystem work — an unclassified binary would default
159
+ // to `fs` and run under HERMETIC_BUILD.
160
+ const NET_BINARIES = new Set(['curl', 'wget', 'ssh', 'scp', 'sftp', 'rsync', 'nc', 'ping', 'dig', 'nslookup', 'gh']);
161
+
162
+ const CLUSTER_BINARIES = new Set(['kubectl', 'helm']);
163
+
164
+ // Binaries that re-enter the engine. They are classified as local process work because
165
+ // the child resolves its own profile from the environment — gating them here would stop
166
+ // a hermetic build from reaching the very stages it exists to run.
167
+ const NESTED_CLI_BINARIES = new Set(['node', 'npm', 'npx', 'underpost']);
168
+
169
+ const stripSudo = (tokens) => {
170
+ if (tokens[0] !== 'sudo') return { tokens, escalated: false };
171
+ let index = 1;
172
+ // `sudo -u dd -E cmd` — step over sudo's own flags and their values.
173
+ while (index < tokens.length && tokens[index].startsWith('-')) {
174
+ const consumesValue = tokens[index] === '-u' || tokens[index] === '--user' || tokens[index] === '-g';
175
+ index += consumesValue ? 2 : 1;
176
+ }
177
+ return { tokens: tokens.slice(index), escalated: true };
178
+ };
179
+
180
+ const kubectlVerb = (tokens) => {
181
+ for (let index = 1; index < tokens.length; index++) {
182
+ const token = tokens[index];
183
+ if (KUBECTL_VALUE_FLAGS.has(token)) {
184
+ index++;
185
+ continue;
186
+ }
187
+ if (token.startsWith('-')) continue;
188
+ return token;
189
+ }
190
+ return '';
191
+ };
192
+
193
+ const classifySegment = (segment) => {
194
+ const tokens = `${segment ?? ''}`.trim().split(/\s+/).filter(Boolean);
195
+ if (tokens.length === 0) return 'fs';
196
+
197
+ const { tokens: bare, escalated } = stripSudo(tokens);
198
+ if (bare.length === 0) return escalated ? 'host:write' : 'fs';
199
+
200
+ // Leading `VAR=value` assignments are not the command.
201
+ let start = 0;
202
+ while (start < bare.length && /^[A-Z_][A-Z0-9_]*=/i.test(bare[start])) start++;
203
+ const binary = nodePath.basename(bare[start] ?? '');
204
+ const rest = bare.slice(start);
205
+
206
+ if (CLUSTER_BINARIES.has(binary)) {
207
+ if (binary === 'helm') {
208
+ const verb = rest[1] ?? '';
209
+ return verb === 'list' || verb === 'get' || verb === 'status' ? 'cluster:read' : 'cluster:write';
210
+ }
211
+ return KUBECTL_READ_VERBS.has(kubectlVerb(rest)) ? 'cluster:read' : 'cluster:write';
212
+ }
213
+ if (NESTED_CLI_BINARIES.has(binary)) return 'fs';
214
+ if (binary === 'git') return 'git';
215
+ if (HOST_WRITE_BINARIES.has(binary)) return 'host:write';
216
+ if (NET_BINARIES.has(binary)) return 'net';
217
+
218
+ // An escalated command we do not otherwise recognise is a host mutation by definition.
219
+ return escalated ? 'host:write' : 'fs';
220
+ };
221
+
222
+ /**
223
+ * Classifies the side effect a command exercises.
224
+ *
225
+ * Compound commands resolve to their most privileged segment, so a pipeline cannot
226
+ * launder a write through a read.
227
+ * @param {string} cmd - The command string.
228
+ * @returns {string} One of {@link CAPABILITIES}.
229
+ * @memberof UnderpostExecution
230
+ */
231
+ const classifyCommand = (cmd = '') => {
232
+ const text = `${cmd ?? ''}`;
233
+ if (!text.trim()) return 'fs';
234
+ // Heredoc bodies are data, not commands: `kubectl apply -f - <<'EOF' … EOF` must be
235
+ // classified on its first line alone, or YAML content would be parsed as a pipeline.
236
+ const [head] = text.split(/<<-?\s*'?[A-Za-z_]+'?/);
237
+ const segments = `${head}`.split(/&&|\|\||[;|]/);
238
+ let capability = 'fs';
239
+ for (const segment of segments) {
240
+ const segmentCapability = classifySegment(segment);
241
+ if (capabilityRank(segmentCapability) > capabilityRank(capability)) capability = segmentCapability;
242
+ }
243
+ return capability;
244
+ };
245
+
246
+ /**
247
+ * Resolves a profile by name, falling back to the default rather than throwing on an
248
+ * unknown value — an unreadable environment variable must not brick every command.
249
+ * @param {string} [name] - Profile name.
250
+ * @returns {object} The profile record.
251
+ * @memberof UnderpostExecution
252
+ */
253
+ const executionProfileFactory = (name = '') => {
254
+ const key = `${name ?? ''}`.trim().toUpperCase().replace(/-/g, '_');
255
+ return EXECUTION_PROFILES[key] ?? EXECUTION_PROFILES[DEFAULT_PROFILE];
256
+ };
257
+
258
+ /**
259
+ * Maps a CLI options object onto the profile it selects.
260
+ *
261
+ * An explicit `--profile` wins. Otherwise a legacy bypass flag selects the profile that
262
+ * subsumes it, but only in the mode where the two actually mean the same thing.
263
+ * @param {object} [options] - Commander options.
264
+ * @returns {string} Profile name.
265
+ * @memberof UnderpostExecution
266
+ */
267
+ const profileFromOptionsFactory = (options = {}) => {
268
+ const explicit = `${options?.profile ?? ''}`.trim();
269
+ if (explicit) return executionProfileFactory(explicit).name;
270
+ for (const [flag, alias] of Object.entries(PROFILE_ALIAS_FLAGS))
271
+ if (options?.[flag] === true && alias.when(options)) return alias.profile;
272
+ return '';
273
+ };
274
+
275
+ /**
276
+ * The profile in effect for this process.
277
+ * @returns {object} The active profile record.
278
+ * @memberof UnderpostExecution
279
+ */
280
+ const activeExecutionProfile = () => executionProfileFactory(process.env[EXECUTION_PROFILE_ENV_KEY]);
281
+
282
+ /**
283
+ * Installs a profile as the process-wide default.
284
+ *
285
+ * Writing the environment variable is the propagation mechanism: children inherit the
286
+ * environment, so a nested `underpost` invocation resolves the same profile with no
287
+ * command-string threading.
288
+ * @param {string} name - Profile name.
289
+ * @returns {object} The profile now in effect.
290
+ * @memberof UnderpostExecution
291
+ */
292
+ const setExecutionProfile = (name) => {
293
+ const profile = executionProfileFactory(name);
294
+ process.env[EXECUTION_PROFILE_ENV_KEY] = profile.name;
295
+ return profile;
296
+ };
297
+
298
+ /**
299
+ * Runs `fn` under `name`, restoring the previous profile afterwards.
300
+ *
301
+ * Restores through a `finally` and awaits a thenable result, so a throwing or async
302
+ * stage cannot leak a hermetic profile into the rest of the process.
303
+ * @param {string} name - Profile to run under.
304
+ * @param {Function} fn - Work to run.
305
+ * @returns {*} Whatever `fn` returns.
306
+ * @memberof UnderpostExecution
307
+ */
308
+ const withExecutionProfile = (name, fn) => {
309
+ const previous = process.env[EXECUTION_PROFILE_ENV_KEY];
310
+ const restore = () => {
311
+ if (previous === undefined) delete process.env[EXECUTION_PROFILE_ENV_KEY];
312
+ else process.env[EXECUTION_PROFILE_ENV_KEY] = previous;
313
+ };
314
+ setExecutionProfile(name);
315
+ let result;
316
+ try {
317
+ result = fn();
318
+ } catch (error) {
319
+ restore();
320
+ throw error;
321
+ }
322
+ if (result && typeof result.then === 'function') return result.finally(restore);
323
+ restore();
324
+ return result;
325
+ };
326
+
327
+ /**
328
+ * Decides whether a command may run under the active profile.
329
+ *
330
+ * Returns a decision rather than acting on one; `shellExec` owns the consequence.
331
+ * @param {string} cmd - The command string.
332
+ * @param {object} [context] - Optional overrides.
333
+ * @param {string} [context.profile] - Profile name, defaulting to the active one.
334
+ * @returns {{permitted: boolean, capability: string, profile: string, neutral: boolean}} Decision.
335
+ * @memberof UnderpostExecution
336
+ */
337
+ const executionDecisionFactory = (cmd, { profile } = {}) => {
338
+ const record = profile ? executionProfileFactory(profile) : activeExecutionProfile();
339
+ const capability = classifyCommand(cmd);
340
+ return {
341
+ permitted: record.permits.includes(capability),
342
+ capability,
343
+ profile: record.name,
344
+ neutral: record.reads === 'neutral',
345
+ };
346
+ };
347
+
348
+ const localUnderpostCli = `node ${packageRoot}/bin/index.js`;
349
+ let underpostCliCache;
350
+
351
+ /**
352
+ * Resolves how to invoke a CLI from a subprocess.
353
+ *
354
+ * Two different questions hide behind "run underpost", and conflating them runs the
355
+ * wrong codebase:
356
+ *
357
+ * - `local: true` — re-enter the package this process is running from, resolved through
358
+ * `import.meta.url`. From a checkout that is the checkout; from a global install it is
359
+ * that same global install, so a deployed node is unaffected. Every multi-stage runner
360
+ * wants this: a stage that re-enters `underpost` must run the code its parent runs, or
361
+ * the two disagree about layout. A stale global install reading credential seeds from a
362
+ * directory this version moved is what that skew looks like in practice.
363
+ * - default — whichever CLI this machine is meant to use: the globally linked binary
364
+ * when one exists, otherwise this checkout by absolute path, so a build box without a
365
+ * global install still works and callers that `cd` elsewhere keep resolving the same
366
+ * CLI.
367
+ *
368
+ * This replaces the `options.dev ? 'node bin' : 'underpost'` branch copied across 14 call
369
+ * sites, none of which agreed on what happened when neither resolved.
370
+ * @param {string} [name] - CLI to resolve.
371
+ * @param {object} [options] - Resolution options.
372
+ * @param {boolean} [options.local] - Re-enter this checkout instead of a global install.
373
+ * @returns {string} Command prefix to invoke it with.
374
+ * @memberof UnderpostExecution
375
+ */
376
+ const cli = (name = 'underpost', { local = false } = {}) => {
377
+ if (name !== 'underpost') return name;
378
+ if (local) return localUnderpostCli;
379
+ if (underpostCliCache) return underpostCliCache;
380
+ try {
381
+ execFileSync('sh', ['-c', 'command -v underpost'], { stdio: 'ignore' });
382
+ underpostCliCache = 'underpost';
383
+ } catch {
384
+ underpostCliCache = localUnderpostCli;
385
+ }
386
+ return underpostCliCache;
387
+ };
388
+
389
+ export {
390
+ activeExecutionProfile,
391
+ CAPABILITIES,
392
+ classifyCommand,
393
+ cli,
394
+ EXECUTION_PROFILE_ENV_KEY,
395
+ EXECUTION_PROFILES,
396
+ executionDecisionFactory,
397
+ executionProfileFactory,
398
+ PROFILE_ALIAS_FLAGS,
399
+ profileFromOptionsFactory,
400
+ setExecutionProfile,
401
+ withExecutionProfile,
402
+ };