@omnitron-dev/omnitron 0.3.1 → 0.3.3
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.
- package/README.md +575 -0
- package/bin/pnpmfile-sdk.mjs +35 -0
- package/bin/sdk.d.mts +8 -0
- package/bin/sdk.mjs +576 -0
- package/data/operations/managed-kernel.mjs +302 -0
- package/data/operations/stage-kernel.mjs +125 -0
- package/dist/BUILD.json +4 -3
- package/dist/cli/exit-after-output.d.ts +3 -0
- package/dist/cli/exit-after-output.d.ts.map +1 -0
- package/dist/cli/exit-after-output.js +11 -0
- package/dist/cli/exit-after-output.js.map +1 -0
- package/dist/cli/installed.d.ts +3 -0
- package/dist/cli/installed.d.ts.map +1 -0
- package/dist/cli/installed.js +17 -0
- package/dist/cli/installed.js.map +1 -0
- package/dist/cli/omnitron.js +456 -98
- package/dist/cli/omnitron.js.map +1 -1
- package/dist/cli/output-command.d.ts +13 -0
- package/dist/cli/output-command.d.ts.map +1 -0
- package/dist/cli/output-command.js +48 -0
- package/dist/cli/output-command.js.map +1 -0
- package/dist/cluster/cluster.rpc-service.d.ts +9 -77
- package/dist/cluster/cluster.rpc-service.d.ts.map +1 -1
- package/dist/cluster/cluster.rpc-service.js +20 -107
- package/dist/cluster/cluster.rpc-service.js.map +1 -1
- package/dist/cluster/enrollment.d.ts +9 -0
- package/dist/cluster/enrollment.d.ts.map +1 -0
- package/dist/cluster/enrollment.js +49 -0
- package/dist/cluster/enrollment.js.map +1 -0
- package/dist/cluster/index.d.ts +1 -1
- package/dist/cluster/index.d.ts.map +1 -1
- package/dist/cluster/leader-election.d.ts +39 -211
- package/dist/cluster/leader-election.d.ts.map +1 -1
- package/dist/cluster/leader-election.js +235 -512
- package/dist/cluster/leader-election.js.map +1 -1
- package/dist/cluster/mesh-link.d.ts +18 -51
- package/dist/cluster/mesh-link.d.ts.map +1 -1
- package/dist/cluster/mesh-link.js +65 -77
- package/dist/cluster/mesh-link.js.map +1 -1
- package/dist/cluster/mesh.d.ts +3 -1
- package/dist/cluster/mesh.d.ts.map +1 -1
- package/dist/cluster/mesh.js +76 -60
- package/dist/cluster/mesh.js.map +1 -1
- package/dist/cluster/slave-connector.d.ts +40 -4
- package/dist/cluster/slave-connector.d.ts.map +1 -1
- package/dist/cluster/slave-connector.js +348 -195
- package/dist/cluster/slave-connector.js.map +1 -1
- package/dist/cluster/standalone-infrastructure.d.ts +3 -0
- package/dist/cluster/standalone-infrastructure.d.ts.map +1 -0
- package/dist/cluster/standalone-infrastructure.js +25 -0
- package/dist/cluster/standalone-infrastructure.js.map +1 -0
- package/dist/cluster/stop-authority.d.ts +45 -0
- package/dist/cluster/stop-authority.d.ts.map +1 -0
- package/dist/cluster/stop-authority.js +112 -0
- package/dist/cluster/stop-authority.js.map +1 -0
- package/dist/commands/access.d.ts +9 -0
- package/dist/commands/access.d.ts.map +1 -0
- package/dist/commands/access.js +45 -0
- package/dist/commands/access.js.map +1 -0
- package/dist/commands/audit.d.ts +2 -0
- package/dist/commands/audit.d.ts.map +1 -1
- package/dist/commands/audit.js +9 -4
- package/dist/commands/audit.js.map +1 -1
- package/dist/commands/backend.d.ts +17 -0
- package/dist/commands/backend.d.ts.map +1 -0
- package/dist/commands/backend.js +264 -0
- package/dist/commands/backend.js.map +1 -0
- package/dist/commands/backup.d.ts +5 -0
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +17 -2
- package/dist/commands/backup.js.map +1 -1
- package/dist/commands/cluster.d.ts.map +1 -1
- package/dist/commands/cluster.js +33 -42
- package/dist/commands/cluster.js.map +1 -1
- package/dist/commands/daemon-cmd.d.ts.map +1 -1
- package/dist/commands/daemon-cmd.js +14 -3
- package/dist/commands/daemon-cmd.js.map +1 -1
- package/dist/commands/doctor.d.ts +11 -29
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +47 -45
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/files.d.ts +19 -0
- package/dist/commands/files.d.ts.map +1 -0
- package/dist/commands/files.js +226 -0
- package/dist/commands/files.js.map +1 -0
- package/dist/commands/fleet-rollout.d.ts +25 -0
- package/dist/commands/fleet-rollout.d.ts.map +1 -0
- package/dist/commands/fleet-rollout.js +155 -0
- package/dist/commands/fleet-rollout.js.map +1 -0
- package/dist/commands/fleet.d.ts.map +1 -1
- package/dist/commands/fleet.js +1 -0
- package/dist/commands/fleet.js.map +1 -1
- package/dist/commands/health-check.d.ts.map +1 -1
- package/dist/commands/health-check.js +13 -6
- package/dist/commands/health-check.js.map +1 -1
- package/dist/commands/infra.d.ts +5 -2
- package/dist/commands/infra.d.ts.map +1 -1
- package/dist/commands/infra.js +145 -53
- package/dist/commands/infra.js.map +1 -1
- package/dist/commands/logs.d.ts +6 -23
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +114 -38
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/metrics.d.ts.map +1 -1
- package/dist/commands/metrics.js +7 -1
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/migrate.d.ts +8 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +64 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/node-reconcile.d.ts +8 -0
- package/dist/commands/node-reconcile.d.ts.map +1 -0
- package/dist/commands/node-reconcile.js +49 -0
- package/dist/commands/node-reconcile.js.map +1 -0
- package/dist/commands/node.d.ts +0 -8
- package/dist/commands/node.d.ts.map +1 -1
- package/dist/commands/node.js +32 -35
- package/dist/commands/node.js.map +1 -1
- package/dist/commands/output.d.ts +3 -9
- package/dist/commands/output.d.ts.map +1 -1
- package/dist/commands/output.js +34 -25
- package/dist/commands/output.js.map +1 -1
- package/dist/commands/pipeline.d.ts +11 -2
- package/dist/commands/pipeline.d.ts.map +1 -1
- package/dist/commands/pipeline.js +75 -15
- package/dist/commands/pipeline.js.map +1 -1
- package/dist/commands/project.d.ts +8 -0
- package/dist/commands/project.d.ts.map +1 -1
- package/dist/commands/project.js +67 -1
- package/dist/commands/project.js.map +1 -1
- package/dist/commands/release-store.js +1 -1
- package/dist/commands/release-store.js.map +1 -1
- package/dist/commands/release.d.ts +5 -12
- package/dist/commands/release.d.ts.map +1 -1
- package/dist/commands/release.js +141 -33
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/scale.d.ts +1 -4
- package/dist/commands/scale.d.ts.map +1 -1
- package/dist/commands/scale.js +39 -19
- package/dist/commands/scale.js.map +1 -1
- package/dist/commands/secret-input.d.ts +3 -0
- package/dist/commands/secret-input.d.ts.map +1 -0
- package/dist/commands/secret-input.js +36 -0
- package/dist/commands/secret-input.js.map +1 -0
- package/dist/commands/secret.d.ts +4 -2
- package/dist/commands/secret.d.ts.map +1 -1
- package/dist/commands/secret.js +12 -2
- package/dist/commands/secret.js.map +1 -1
- package/dist/commands/service-resident.d.ts +25 -0
- package/dist/commands/service-resident.d.ts.map +1 -0
- package/dist/commands/service-resident.js +236 -0
- package/dist/commands/service-resident.js.map +1 -0
- package/dist/commands/service-units.d.ts +13 -1
- package/dist/commands/service-units.d.ts.map +1 -1
- package/dist/commands/service-units.js +38 -16
- package/dist/commands/service-units.js.map +1 -1
- package/dist/commands/service.d.ts +2 -24
- package/dist/commands/service.d.ts.map +1 -1
- package/dist/commands/service.js +89 -7
- package/dist/commands/service.js.map +1 -1
- package/dist/commands/stack.d.ts +11 -6
- package/dist/commands/stack.d.ts.map +1 -1
- package/dist/commands/stack.js +212 -38
- package/dist/commands/stack.js.map +1 -1
- package/dist/commands/start.d.ts +0 -21
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +33 -43
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.d.ts +1 -5
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +5 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/stop.d.ts +0 -22
- package/dist/commands/stop.d.ts.map +1 -1
- package/dist/commands/stop.js +26 -86
- package/dist/commands/stop.js.map +1 -1
- package/dist/commands/system.d.ts +4 -0
- package/dist/commands/system.d.ts.map +1 -0
- package/dist/commands/system.js +41 -0
- package/dist/commands/system.js.map +1 -0
- package/dist/commands/terminal.d.ts +7 -0
- package/dist/commands/terminal.d.ts.map +1 -0
- package/dist/commands/terminal.js +184 -0
- package/dist/commands/terminal.js.map +1 -0
- package/dist/commands/topology.d.ts +3 -0
- package/dist/commands/topology.d.ts.map +1 -0
- package/dist/commands/topology.js +107 -0
- package/dist/commands/topology.js.map +1 -0
- package/dist/commands/up.d.ts +4 -54
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +41 -98
- package/dist/commands/up.js.map +1 -1
- package/dist/commands/webapp.d.ts +0 -9
- package/dist/commands/webapp.d.ts.map +1 -1
- package/dist/commands/webapp.js +116 -69
- package/dist/commands/webapp.js.map +1 -1
- package/dist/config/app-path.d.ts +4 -0
- package/dist/config/app-path.d.ts.map +1 -0
- package/dist/config/app-path.js +6 -0
- package/dist/config/app-path.js.map +1 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +9 -3
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/define-ecosystem.d.ts.map +1 -1
- package/dist/config/define-ecosystem.js +12 -0
- package/dist/config/define-ecosystem.js.map +1 -1
- package/dist/config/index.d.ts +3 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/loader-child.d.ts +2 -0
- package/dist/config/loader-child.d.ts.map +1 -0
- package/dist/config/loader-child.js +56 -0
- package/dist/config/loader-child.js.map +1 -0
- package/dist/config/loader.d.ts +7 -2
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +14 -27
- package/dist/config/loader.js.map +1 -1
- package/dist/config/project-stacks.d.ts +5 -0
- package/dist/config/project-stacks.d.ts.map +1 -0
- package/dist/config/project-stacks.js +24 -0
- package/dist/config/project-stacks.js.map +1 -0
- package/dist/config/saved.d.ts +67 -0
- package/dist/config/saved.d.ts.map +1 -0
- package/dist/config/saved.js +81 -0
- package/dist/config/saved.js.map +1 -0
- package/dist/config/types.d.ts +96 -52
- package/dist/config/types.d.ts.map +1 -1
- package/dist/daemon/backend-candidate.d.ts +32 -0
- package/dist/daemon/backend-candidate.d.ts.map +1 -0
- package/dist/daemon/backend-candidate.js +129 -0
- package/dist/daemon/backend-candidate.js.map +1 -0
- package/dist/daemon/backend-child.d.ts +5 -0
- package/dist/daemon/backend-child.d.ts.map +1 -0
- package/dist/daemon/backend-child.js +13 -0
- package/dist/daemon/backend-child.js.map +1 -0
- package/dist/daemon/backend-control.d.ts +43 -0
- package/dist/daemon/backend-control.d.ts.map +1 -0
- package/dist/daemon/backend-control.js +155 -0
- package/dist/daemon/backend-control.js.map +1 -0
- package/dist/daemon/backend-crash-recovery.d.ts +4 -0
- package/dist/daemon/backend-crash-recovery.d.ts.map +1 -0
- package/dist/daemon/backend-crash-recovery.js +136 -0
- package/dist/daemon/backend-crash-recovery.js.map +1 -0
- package/dist/daemon/backend-handoff-types.d.ts +27 -0
- package/dist/daemon/backend-handoff-types.d.ts.map +1 -0
- package/dist/daemon/backend-handoff-types.js +33 -0
- package/dist/daemon/backend-handoff-types.js.map +1 -0
- package/dist/daemon/backend-recovery-journal.d.ts +30 -0
- package/dist/daemon/backend-recovery-journal.d.ts.map +1 -0
- package/dist/daemon/backend-recovery-journal.js +67 -0
- package/dist/daemon/backend-recovery-journal.js.map +1 -0
- package/dist/daemon/backend-recovery-monitor.d.ts +35 -0
- package/dist/daemon/backend-recovery-monitor.d.ts.map +1 -0
- package/dist/daemon/backend-recovery-monitor.js +113 -0
- package/dist/daemon/backend-recovery-monitor.js.map +1 -0
- package/dist/daemon/backend-supervisor-entry.d.ts +2 -0
- package/dist/daemon/backend-supervisor-entry.d.ts.map +1 -0
- package/dist/daemon/backend-supervisor-entry.js +3 -0
- package/dist/daemon/backend-supervisor-entry.js.map +1 -0
- package/dist/daemon/backend-supervisor-main.d.ts +2 -0
- package/dist/daemon/backend-supervisor-main.d.ts.map +1 -0
- package/dist/daemon/backend-supervisor-main.js +45 -0
- package/dist/daemon/backend-supervisor-main.js.map +1 -0
- package/dist/daemon/backend-supervisor.d.ts +61 -0
- package/dist/daemon/backend-supervisor.d.ts.map +1 -0
- package/dist/daemon/backend-supervisor.js +339 -0
- package/dist/daemon/backend-supervisor.js.map +1 -0
- package/dist/daemon/backend-upgrade.d.ts +41 -0
- package/dist/daemon/backend-upgrade.d.ts.map +1 -0
- package/dist/daemon/backend-upgrade.js +444 -0
- package/dist/daemon/backend-upgrade.js.map +1 -0
- package/dist/daemon/backend.rpc-service.d.ts +11 -0
- package/dist/daemon/backend.rpc-service.d.ts.map +1 -0
- package/dist/daemon/backend.rpc-service.js +44 -0
- package/dist/daemon/backend.rpc-service.js.map +1 -0
- package/dist/daemon/daemon-client.d.ts +12 -79
- package/dist/daemon/daemon-client.d.ts.map +1 -1
- package/dist/daemon/daemon-client.js +28 -270
- package/dist/daemon/daemon-client.js.map +1 -1
- package/dist/daemon/daemon-entry.d.ts +0 -7
- package/dist/daemon/daemon-entry.d.ts.map +1 -1
- package/dist/daemon/daemon-entry.js +2 -115
- package/dist/daemon/daemon-entry.js.map +1 -1
- package/dist/daemon/daemon-main.d.ts +9 -0
- package/dist/daemon/daemon-main.d.ts.map +1 -0
- package/dist/daemon/daemon-main.js +105 -0
- package/dist/daemon/daemon-main.js.map +1 -0
- package/dist/daemon/daemon-scheduler.d.ts +8 -5
- package/dist/daemon/daemon-scheduler.d.ts.map +1 -1
- package/dist/daemon/daemon-scheduler.js +45 -73
- package/dist/daemon/daemon-scheduler.js.map +1 -1
- package/dist/daemon/daemon-state-store.service.d.ts +5 -39
- package/dist/daemon/daemon-state-store.service.d.ts.map +1 -1
- package/dist/daemon/daemon-state-store.service.js +30 -17
- package/dist/daemon/daemon-state-store.service.js.map +1 -1
- package/dist/daemon/daemon.d.ts +45 -60
- package/dist/daemon/daemon.d.ts.map +1 -1
- package/dist/daemon/daemon.js +720 -396
- package/dist/daemon/daemon.js.map +1 -1
- package/dist/daemon/daemon.module.d.ts +13 -11
- package/dist/daemon/daemon.module.d.ts.map +1 -1
- package/dist/daemon/daemon.module.js +136 -101
- package/dist/daemon/daemon.module.js.map +1 -1
- package/dist/daemon/daemon.rpc-service.d.ts +9 -17
- package/dist/daemon/daemon.rpc-service.d.ts.map +1 -1
- package/dist/daemon/daemon.rpc-service.js +83 -22
- package/dist/daemon/daemon.rpc-service.js.map +1 -1
- package/dist/daemon/darwin-owner-evidence.d.ts +57 -0
- package/dist/daemon/darwin-owner-evidence.d.ts.map +1 -0
- package/dist/daemon/darwin-owner-evidence.js +131 -0
- package/dist/daemon/darwin-owner-evidence.js.map +1 -0
- package/dist/daemon/database-startup.d.ts +7 -0
- package/dist/daemon/database-startup.d.ts.map +1 -0
- package/dist/daemon/database-startup.js +40 -0
- package/dist/daemon/database-startup.js.map +1 -0
- package/dist/daemon/entry-guard.d.ts +2 -0
- package/dist/daemon/entry-guard.d.ts.map +1 -0
- package/dist/daemon/entry-guard.js +34 -0
- package/dist/daemon/entry-guard.js.map +1 -0
- package/dist/daemon/execution-capabilities.d.ts +7 -0
- package/dist/daemon/execution-capabilities.d.ts.map +1 -0
- package/dist/daemon/execution-capabilities.js +23 -0
- package/dist/daemon/execution-capabilities.js.map +1 -0
- package/dist/daemon/execution-client.d.ts +88 -0
- package/dist/daemon/execution-client.d.ts.map +1 -0
- package/dist/daemon/execution-client.js +433 -0
- package/dist/daemon/execution-client.js.map +1 -0
- package/dist/daemon/execution-config.d.ts +7 -0
- package/dist/daemon/execution-config.d.ts.map +1 -0
- package/dist/daemon/execution-config.js +61 -0
- package/dist/daemon/execution-config.js.map +1 -0
- package/dist/daemon/execution-entry.d.ts +2 -0
- package/dist/daemon/execution-entry.d.ts.map +1 -0
- package/dist/daemon/execution-entry.js +3 -0
- package/dist/daemon/execution-entry.js.map +1 -0
- package/dist/daemon/execution-host.d.ts +133 -0
- package/dist/daemon/execution-host.d.ts.map +1 -0
- package/dist/daemon/execution-host.js +308 -0
- package/dist/daemon/execution-host.js.map +1 -0
- package/dist/daemon/execution-journal.d.ts +17 -0
- package/dist/daemon/execution-journal.d.ts.map +1 -0
- package/dist/daemon/execution-journal.js +75 -0
- package/dist/daemon/execution-journal.js.map +1 -0
- package/dist/daemon/execution-launcher.d.ts +12 -0
- package/dist/daemon/execution-launcher.d.ts.map +1 -0
- package/dist/daemon/execution-launcher.js +110 -0
- package/dist/daemon/execution-launcher.js.map +1 -0
- package/dist/daemon/execution-main.d.ts +2 -0
- package/dist/daemon/execution-main.d.ts.map +1 -0
- package/dist/daemon/execution-main.js +48 -0
- package/dist/daemon/execution-main.js.map +1 -0
- package/dist/daemon/execution-owner.d.ts +16 -0
- package/dist/daemon/execution-owner.d.ts.map +1 -0
- package/dist/daemon/execution-owner.js +97 -0
- package/dist/daemon/execution-owner.js.map +1 -0
- package/dist/daemon/execution-platform.d.ts +3 -0
- package/dist/daemon/execution-platform.d.ts.map +1 -0
- package/dist/daemon/execution-platform.js +23 -0
- package/dist/daemon/execution-platform.js.map +1 -0
- package/dist/daemon/execution-shutdown.d.ts +13 -0
- package/dist/daemon/execution-shutdown.d.ts.map +1 -0
- package/dist/daemon/execution-shutdown.js +30 -0
- package/dist/daemon/execution-shutdown.js.map +1 -0
- package/dist/daemon/managed-entry.d.ts +2 -0
- package/dist/daemon/managed-entry.d.ts.map +1 -0
- package/dist/daemon/managed-entry.js +3 -0
- package/dist/daemon/managed-entry.js.map +1 -0
- package/dist/daemon/managed-main.d.ts +2 -0
- package/dist/daemon/managed-main.d.ts.map +1 -0
- package/dist/daemon/managed-main.js +29 -0
- package/dist/daemon/managed-main.js.map +1 -0
- package/dist/daemon/managed-owner-build.d.ts +17 -0
- package/dist/daemon/managed-owner-build.d.ts.map +1 -0
- package/dist/daemon/managed-owner-build.js +108 -0
- package/dist/daemon/managed-owner-build.js.map +1 -0
- package/dist/daemon/managed-owner-entry.d.ts +2 -0
- package/dist/daemon/managed-owner-entry.d.ts.map +1 -0
- package/dist/daemon/managed-owner-entry.js +3 -0
- package/dist/daemon/managed-owner-entry.js.map +1 -0
- package/dist/daemon/managed-owner-main.d.ts +2 -0
- package/dist/daemon/managed-owner-main.d.ts.map +1 -0
- package/dist/daemon/managed-owner-main.js +53 -0
- package/dist/daemon/managed-owner-main.js.map +1 -0
- package/dist/daemon/managed-runtime.d.ts +39 -0
- package/dist/daemon/managed-runtime.d.ts.map +1 -0
- package/dist/daemon/managed-runtime.js +137 -0
- package/dist/daemon/managed-runtime.js.map +1 -0
- package/dist/daemon/network-publication.d.ts +5 -0
- package/dist/daemon/network-publication.d.ts.map +1 -0
- package/dist/daemon/network-publication.js +25 -0
- package/dist/daemon/network-publication.js.map +1 -0
- package/dist/daemon/owner-reconciliation.d.ts +58 -0
- package/dist/daemon/owner-reconciliation.d.ts.map +1 -0
- package/dist/daemon/owner-reconciliation.js +265 -0
- package/dist/daemon/owner-reconciliation.js.map +1 -0
- package/dist/daemon/pid-manager.d.ts +2 -0
- package/dist/daemon/pid-manager.d.ts.map +1 -1
- package/dist/daemon/pid-manager.js +5 -0
- package/dist/daemon/pid-manager.js.map +1 -1
- package/dist/daemon/private-control-client.d.ts +8 -0
- package/dist/daemon/private-control-client.d.ts.map +1 -0
- package/dist/daemon/private-control-client.js +41 -0
- package/dist/daemon/private-control-client.js.map +1 -0
- package/dist/daemon/recovery-state.d.ts +20 -0
- package/dist/daemon/recovery-state.d.ts.map +1 -0
- package/dist/daemon/recovery-state.js +53 -0
- package/dist/daemon/recovery-state.js.map +1 -0
- package/dist/daemon/rpc-connection.d.ts +29 -0
- package/dist/daemon/rpc-connection.d.ts.map +1 -0
- package/dist/daemon/rpc-connection.js +137 -0
- package/dist/daemon/rpc-connection.js.map +1 -0
- package/dist/daemon/runtime-installation.d.ts +58 -0
- package/dist/daemon/runtime-installation.d.ts.map +1 -0
- package/dist/daemon/runtime-installation.js +346 -0
- package/dist/daemon/runtime-installation.js.map +1 -0
- package/dist/daemon/runtime-launcher.d.ts +6 -0
- package/dist/daemon/runtime-launcher.d.ts.map +1 -0
- package/dist/daemon/runtime-launcher.js +45 -0
- package/dist/daemon/runtime-launcher.js.map +1 -0
- package/dist/daemon/schema-transition.d.ts +43 -0
- package/dist/daemon/schema-transition.d.ts.map +1 -0
- package/dist/daemon/schema-transition.js +239 -0
- package/dist/daemon/schema-transition.js.map +1 -0
- package/dist/daemon/spawn-daemon.d.ts +2 -0
- package/dist/daemon/spawn-daemon.d.ts.map +1 -1
- package/dist/daemon/spawn-daemon.js +4 -0
- package/dist/daemon/spawn-daemon.js.map +1 -1
- package/dist/daemon/startup-config.d.ts +77 -0
- package/dist/daemon/startup-config.d.ts.map +1 -0
- package/dist/daemon/startup-config.js +31 -0
- package/dist/daemon/startup-config.js.map +1 -0
- package/dist/daemon/startup-failure-journal.d.ts +3 -0
- package/dist/daemon/startup-failure-journal.d.ts.map +1 -0
- package/dist/daemon/startup-failure-journal.js +29 -0
- package/dist/daemon/startup-failure-journal.js.map +1 -0
- package/dist/daemon/state-store.d.ts +16 -24
- package/dist/daemon/state-store.d.ts.map +1 -1
- package/dist/daemon/state-store.js +51 -9
- package/dist/daemon/state-store.js.map +1 -1
- package/dist/daemon/supervisor-transition.d.ts +101 -0
- package/dist/daemon/supervisor-transition.d.ts.map +1 -0
- package/dist/daemon/supervisor-transition.js +503 -0
- package/dist/daemon/supervisor-transition.js.map +1 -0
- package/dist/daemon/system-guardian-cgroup.d.ts +16 -0
- package/dist/daemon/system-guardian-cgroup.d.ts.map +1 -0
- package/dist/daemon/system-guardian-cgroup.js +202 -0
- package/dist/daemon/system-guardian-cgroup.js.map +1 -0
- package/dist/daemon/system-guardian-unit.d.ts +9 -0
- package/dist/daemon/system-guardian-unit.d.ts.map +1 -0
- package/dist/daemon/system-guardian-unit.js +41 -0
- package/dist/daemon/system-guardian-unit.js.map +1 -0
- package/dist/database/connection.d.ts.map +1 -1
- package/dist/database/connection.js +2 -6
- package/dist/database/connection.js.map +1 -1
- package/dist/database/log-durability.d.ts +5 -0
- package/dist/database/log-durability.d.ts.map +1 -0
- package/dist/database/log-durability.js +28 -0
- package/dist/database/log-durability.js.map +1 -0
- package/dist/database/migrate.js +2 -15
- package/dist/database/migrate.js.map +1 -1
- package/dist/database/migration-registry-compatibility.d.ts +13 -0
- package/dist/database/migration-registry-compatibility.d.ts.map +1 -0
- package/dist/database/migration-registry-compatibility.js +108 -0
- package/dist/database/migration-registry-compatibility.js.map +1 -0
- package/dist/database/migration-runner.d.ts +8 -42
- package/dist/database/migration-runner.d.ts.map +1 -1
- package/dist/database/migration-runner.js +114 -123
- package/dist/database/migration-runner.js.map +1 -1
- package/dist/database/migrations/014_replication_receipts.d.ts +5 -0
- package/dist/database/migrations/014_replication_receipts.d.ts.map +1 -0
- package/dist/database/migrations/014_replication_receipts.js +6 -0
- package/dist/database/migrations/014_replication_receipts.js.map +1 -0
- package/dist/database/migrations/015_persistent_metrics.d.ts +5 -0
- package/dist/database/migrations/015_persistent_metrics.d.ts.map +1 -0
- package/dist/database/migrations/015_persistent_metrics.js +7 -0
- package/dist/database/migrations/015_persistent_metrics.js.map +1 -0
- package/dist/database/migrations/016_cluster_lease.d.ts +4 -0
- package/dist/database/migrations/016_cluster_lease.d.ts.map +1 -0
- package/dist/database/migrations/016_cluster_lease.js +6 -0
- package/dist/database/migrations/016_cluster_lease.js.map +1 -0
- package/dist/database/migrations/index.d.ts +1 -1
- package/dist/database/migrations/index.d.ts.map +1 -1
- package/dist/database/migrations/index.js +22 -13
- package/dist/database/migrations/index.js.map +1 -1
- package/dist/database/schema-compatibility.d.ts +34 -0
- package/dist/database/schema-compatibility.d.ts.map +1 -0
- package/dist/database/schema-compatibility.js +109 -0
- package/dist/database/schema-compatibility.js.map +1 -0
- package/dist/database/schema-expansion-policy.d.ts +10 -0
- package/dist/database/schema-expansion-policy.d.ts.map +1 -0
- package/dist/database/schema-expansion-policy.js +21 -0
- package/dist/database/schema-expansion-policy.js.map +1 -0
- package/dist/database/schema-transition-plan.d.ts +53 -0
- package/dist/database/schema-transition-plan.d.ts.map +1 -0
- package/dist/database/schema-transition-plan.js +102 -0
- package/dist/database/schema-transition-plan.js.map +1 -0
- package/dist/database/schema-worker.d.ts +2 -0
- package/dist/database/schema-worker.d.ts.map +1 -0
- package/dist/database/schema-worker.js +55 -0
- package/dist/database/schema-worker.js.map +1 -0
- package/dist/database/schema.d.ts +6 -1
- package/dist/database/schema.d.ts.map +1 -1
- package/dist/database/slave-migrations/001_replication_evidence.d.ts +4 -0
- package/dist/database/slave-migrations/001_replication_evidence.d.ts.map +1 -0
- package/dist/database/slave-migrations/001_replication_evidence.js +89 -0
- package/dist/database/slave-migrations/001_replication_evidence.js.map +1 -0
- package/dist/database/slave-migrations/index.d.ts +6 -0
- package/dist/database/slave-migrations/index.d.ts.map +1 -0
- package/dist/database/slave-migrations/index.js +10 -0
- package/dist/database/slave-migrations/index.js.map +1 -0
- package/dist/diagnostics/index.d.ts +16 -0
- package/dist/diagnostics/index.d.ts.map +1 -0
- package/dist/diagnostics/index.js +22 -0
- package/dist/diagnostics/index.js.map +1 -0
- package/dist/execution/command-failure.d.ts +7 -0
- package/dist/execution/command-failure.d.ts.map +1 -0
- package/dist/execution/command-failure.js +29 -0
- package/dist/execution/command-failure.js.map +1 -0
- package/dist/execution/execution.service.d.ts +6 -1
- package/dist/execution/execution.service.d.ts.map +1 -1
- package/dist/execution/execution.service.js +32 -30
- package/dist/execution/execution.service.js.map +1 -1
- package/dist/files/archive-codec.d.ts +57 -0
- package/dist/files/archive-codec.d.ts.map +1 -0
- package/dist/files/archive-codec.js +454 -0
- package/dist/files/archive-codec.js.map +1 -0
- package/dist/files/archive-node.d.ts +27 -0
- package/dist/files/archive-node.d.ts.map +1 -0
- package/dist/files/archive-node.js +379 -0
- package/dist/files/archive-node.js.map +1 -0
- package/dist/files/archive-publication.d.ts +26 -0
- package/dist/files/archive-publication.d.ts.map +1 -0
- package/dist/files/archive-publication.js +224 -0
- package/dist/files/archive-publication.js.map +1 -0
- package/dist/files/file-config.d.ts +7 -0
- package/dist/files/file-config.d.ts.map +1 -0
- package/dist/files/file-config.js +11 -0
- package/dist/files/file-config.js.map +1 -0
- package/dist/files/file-node.d.ts +65 -0
- package/dist/files/file-node.d.ts.map +1 -0
- package/dist/files/file-node.js +863 -0
- package/dist/files/file-node.js.map +1 -0
- package/dist/files/file-node.rpc-service.d.ts +12 -0
- package/dist/files/file-node.rpc-service.d.ts.map +1 -0
- package/dist/files/file-node.rpc-service.js +42 -0
- package/dist/files/file-node.rpc-service.js.map +1 -0
- package/dist/files/files.rpc-service.d.ts +107 -0
- package/dist/files/files.rpc-service.d.ts.map +1 -0
- package/dist/files/files.rpc-service.js +769 -0
- package/dist/files/files.rpc-service.js.map +1 -0
- package/dist/files/structural-policy.d.ts +3 -0
- package/dist/files/structural-policy.d.ts.map +1 -0
- package/dist/files/structural-policy.js +27 -0
- package/dist/files/structural-policy.js.map +1 -0
- package/dist/health/contracts.d.ts +30 -0
- package/dist/health/contracts.d.ts.map +1 -0
- package/dist/health/contracts.js +58 -0
- package/dist/health/contracts.js.map +1 -0
- package/dist/health/heartbeat.d.ts +11 -0
- package/dist/health/heartbeat.d.ts.map +1 -0
- package/dist/health/heartbeat.js +32 -0
- package/dist/health/heartbeat.js.map +1 -0
- package/dist/health/index.d.ts +4 -0
- package/dist/health/index.d.ts.map +1 -0
- package/dist/health/index.js +2 -0
- package/dist/health/index.js.map +1 -0
- package/dist/health/readiness.d.ts +15 -0
- package/dist/health/readiness.d.ts.map +1 -0
- package/dist/health/readiness.js +170 -0
- package/dist/health/readiness.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/infrastructure/apt-packages.d.ts +34 -0
- package/dist/infrastructure/apt-packages.d.ts.map +1 -0
- package/dist/infrastructure/apt-packages.js +232 -0
- package/dist/infrastructure/apt-packages.js.map +1 -0
- package/dist/infrastructure/bare-metal-plan.d.ts +10 -1
- package/dist/infrastructure/bare-metal-plan.d.ts.map +1 -1
- package/dist/infrastructure/bare-metal-plan.js +21 -0
- package/dist/infrastructure/bare-metal-plan.js.map +1 -1
- package/dist/infrastructure/bare-metal-runner.d.ts.map +1 -1
- package/dist/infrastructure/bare-metal-runner.js +13 -2
- package/dist/infrastructure/bare-metal-runner.js.map +1 -1
- package/dist/infrastructure/config-normalizer.d.ts +4 -3
- package/dist/infrastructure/config-normalizer.d.ts.map +1 -1
- package/dist/infrastructure/config-normalizer.js +11 -3
- package/dist/infrastructure/config-normalizer.js.map +1 -1
- package/dist/infrastructure/container-runtime.d.ts +37 -3
- package/dist/infrastructure/container-runtime.d.ts.map +1 -1
- package/dist/infrastructure/container-runtime.js +235 -64
- package/dist/infrastructure/container-runtime.js.map +1 -1
- package/dist/infrastructure/health-recovery.d.ts +19 -0
- package/dist/infrastructure/health-recovery.d.ts.map +1 -0
- package/dist/infrastructure/health-recovery.js +39 -0
- package/dist/infrastructure/health-recovery.js.map +1 -0
- package/dist/infrastructure/host-ingress-config.d.ts.map +1 -1
- package/dist/infrastructure/host-ingress-config.js +11 -8
- package/dist/infrastructure/host-ingress-config.js.map +1 -1
- package/dist/infrastructure/host-ingress-lock.d.ts +2 -3
- package/dist/infrastructure/host-ingress-lock.d.ts.map +1 -1
- package/dist/infrastructure/host-ingress-lock.js +1 -35
- package/dist/infrastructure/host-ingress-lock.js.map +1 -1
- package/dist/infrastructure/host-ingress-takeover.js +3 -3
- package/dist/infrastructure/host-ingress-takeover.js.map +1 -1
- package/dist/infrastructure/host-ingress-types.d.ts +4 -2
- package/dist/infrastructure/host-ingress-types.d.ts.map +1 -1
- package/dist/infrastructure/host-ingress.service.d.ts.map +1 -1
- package/dist/infrastructure/host-ingress.service.js +6 -6
- package/dist/infrastructure/host-ingress.service.js.map +1 -1
- package/dist/infrastructure/host-inspection.d.ts +1 -0
- package/dist/infrastructure/host-inspection.d.ts.map +1 -1
- package/dist/infrastructure/host-inspection.js +3 -2
- package/dist/infrastructure/host-inspection.js.map +1 -1
- package/dist/infrastructure/infrastructure.service.d.ts +80 -29
- package/dist/infrastructure/infrastructure.service.d.ts.map +1 -1
- package/dist/infrastructure/infrastructure.service.js +561 -216
- package/dist/infrastructure/infrastructure.service.js.map +1 -1
- package/dist/infrastructure/merge-config.d.ts +10 -0
- package/dist/infrastructure/merge-config.d.ts.map +1 -0
- package/dist/infrastructure/merge-config.js +27 -0
- package/dist/infrastructure/merge-config.js.map +1 -0
- package/dist/infrastructure/orphan-containers.d.ts +1 -0
- package/dist/infrastructure/orphan-containers.d.ts.map +1 -1
- package/dist/infrastructure/orphan-containers.js +1 -1
- package/dist/infrastructure/orphan-containers.js.map +1 -1
- package/dist/infrastructure/owned-container.d.ts +4 -0
- package/dist/infrastructure/owned-container.d.ts.map +1 -0
- package/dist/infrastructure/owned-container.js +13 -0
- package/dist/infrastructure/owned-container.js.map +1 -0
- package/dist/infrastructure/phantom-endpoint-janitor.d.ts +4 -1
- package/dist/infrastructure/phantom-endpoint-janitor.d.ts.map +1 -1
- package/dist/infrastructure/phantom-endpoint-janitor.js +11 -2
- package/dist/infrastructure/phantom-endpoint-janitor.js.map +1 -1
- package/dist/infrastructure/presets/minio.d.ts.map +1 -1
- package/dist/infrastructure/presets/minio.js +15 -25
- package/dist/infrastructure/presets/minio.js.map +1 -1
- package/dist/infrastructure/presets/postgres.d.ts.map +1 -1
- package/dist/infrastructure/presets/postgres.js +35 -31
- package/dist/infrastructure/presets/postgres.js.map +1 -1
- package/dist/infrastructure/presets/tor.d.ts +1 -2
- package/dist/infrastructure/presets/tor.d.ts.map +1 -1
- package/dist/infrastructure/presets/tor.js +7 -13
- package/dist/infrastructure/presets/tor.js.map +1 -1
- package/dist/infrastructure/provisioning-outcome.d.ts +7 -0
- package/dist/infrastructure/provisioning-outcome.d.ts.map +1 -1
- package/dist/infrastructure/provisioning-outcome.js +41 -8
- package/dist/infrastructure/provisioning-outcome.js.map +1 -1
- package/dist/infrastructure/service-resolver.d.ts +1 -1
- package/dist/infrastructure/service-resolver.d.ts.map +1 -1
- package/dist/infrastructure/service-resolver.js +22 -10
- package/dist/infrastructure/service-resolver.js.map +1 -1
- package/dist/infrastructure/stack-infra-manager.d.ts +25 -4
- package/dist/infrastructure/stack-infra-manager.d.ts.map +1 -1
- package/dist/infrastructure/stack-infra-manager.js +181 -21
- package/dist/infrastructure/stack-infra-manager.js.map +1 -1
- package/dist/infrastructure/types.d.ts +60 -3
- package/dist/infrastructure/types.d.ts.map +1 -1
- package/dist/mcp/mcp-bridge.js +1 -1
- package/dist/mcp/mcp-bridge.js.map +1 -1
- package/dist/mcp/monitoring-reading.d.ts +18 -0
- package/dist/mcp/monitoring-reading.d.ts.map +1 -0
- package/dist/mcp/monitoring-reading.js +84 -0
- package/dist/mcp/monitoring-reading.js.map +1 -0
- package/dist/mcp/tool-groups/apps.tools.d.ts.map +1 -1
- package/dist/mcp/tool-groups/apps.tools.js +6 -4
- package/dist/mcp/tool-groups/apps.tools.js.map +1 -1
- package/dist/mcp/tool-groups/management.tools.d.ts +2 -0
- package/dist/mcp/tool-groups/management.tools.d.ts.map +1 -1
- package/dist/mcp/tool-groups/management.tools.js +17 -38
- package/dist/mcp/tool-groups/management.tools.js.map +1 -1
- package/dist/mcp/tool-groups/monitoring.tools.d.ts.map +1 -1
- package/dist/mcp/tool-groups/monitoring.tools.js +45 -2
- package/dist/mcp/tool-groups/monitoring.tools.js.map +1 -1
- package/dist/mcp/unavailable-tools.js +2 -2
- package/dist/mcp/unavailable-tools.js.map +1 -1
- package/dist/migration/configuration.d.ts +8 -0
- package/dist/migration/configuration.d.ts.map +1 -0
- package/dist/migration/configuration.js +16 -0
- package/dist/migration/configuration.js.map +1 -0
- package/dist/migration/index.d.ts +25 -0
- package/dist/migration/index.d.ts.map +1 -0
- package/dist/migration/index.js +27 -0
- package/dist/migration/index.js.map +1 -0
- package/dist/migration/operation.d.ts +31 -0
- package/dist/migration/operation.d.ts.map +1 -0
- package/dist/migration/operation.js +67 -0
- package/dist/migration/operation.js.map +1 -0
- package/dist/migration/workspace.d.ts +28 -0
- package/dist/migration/workspace.d.ts.map +1 -0
- package/dist/migration/workspace.js +91 -0
- package/dist/migration/workspace.js.map +1 -0
- package/dist/monitoring/app-health.indicator.d.ts +1 -1
- package/dist/monitoring/app-health.indicator.d.ts.map +1 -1
- package/dist/monitoring/docker-health.indicator.d.ts +4 -2
- package/dist/monitoring/docker-health.indicator.d.ts.map +1 -1
- package/dist/monitoring/docker-health.indicator.js +66 -5
- package/dist/monitoring/docker-health.indicator.js.map +1 -1
- package/dist/monitoring/event-loop-watch.d.ts.map +1 -1
- package/dist/monitoring/event-loop-watch.js.map +1 -1
- package/dist/monitoring/infrastructure-health.d.ts +15 -0
- package/dist/monitoring/infrastructure-health.d.ts.map +1 -0
- package/dist/monitoring/infrastructure-health.js +2 -0
- package/dist/monitoring/infrastructure-health.js.map +1 -0
- package/dist/monitoring/log-entry.d.ts +7 -0
- package/dist/monitoring/log-entry.d.ts.map +1 -0
- package/dist/monitoring/log-entry.js +16 -0
- package/dist/monitoring/log-entry.js.map +1 -0
- package/dist/monitoring/log-journal.d.ts +40 -0
- package/dist/monitoring/log-journal.d.ts.map +1 -0
- package/dist/monitoring/log-journal.js +158 -0
- package/dist/monitoring/log-journal.js.map +1 -0
- package/dist/monitoring/log-line.d.ts +19 -14
- package/dist/monitoring/log-line.d.ts.map +1 -1
- package/dist/monitoring/log-line.js +70 -59
- package/dist/monitoring/log-line.js.map +1 -1
- package/dist/monitoring/log-manager.d.ts +34 -37
- package/dist/monitoring/log-manager.d.ts.map +1 -1
- package/dist/monitoring/log-manager.js +164 -63
- package/dist/monitoring/log-manager.js.map +1 -1
- package/dist/monitoring/log-sink-health.indicator.d.ts +10 -0
- package/dist/monitoring/log-sink-health.indicator.d.ts.map +1 -0
- package/dist/monitoring/log-sink-health.indicator.js +21 -0
- package/dist/monitoring/log-sink-health.indicator.js.map +1 -0
- package/dist/monitoring/metrics-storage.indicator.d.ts +9 -0
- package/dist/monitoring/metrics-storage.indicator.d.ts.map +1 -0
- package/dist/monitoring/metrics-storage.indicator.js +18 -0
- package/dist/monitoring/metrics-storage.indicator.js.map +1 -0
- package/dist/monitoring/process-lifetime.d.ts +11 -0
- package/dist/monitoring/process-lifetime.d.ts.map +1 -0
- package/dist/monitoring/process-lifetime.js +45 -0
- package/dist/monitoring/process-lifetime.js.map +1 -0
- package/dist/monitoring/runtime-diagnostics.d.ts +47 -0
- package/dist/monitoring/runtime-diagnostics.d.ts.map +1 -0
- package/dist/monitoring/runtime-diagnostics.js +88 -0
- package/dist/monitoring/runtime-diagnostics.js.map +1 -0
- package/dist/observability/metrics-bridge.d.ts +5 -1
- package/dist/observability/metrics-bridge.d.ts.map +1 -1
- package/dist/observability/metrics-bridge.js +11 -0
- package/dist/observability/metrics-bridge.js.map +1 -1
- package/dist/operations.d.ts +1 -0
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js.map +1 -1
- package/dist/orchestrator/app-handle.d.ts +24 -6
- package/dist/orchestrator/app-handle.d.ts.map +1 -1
- package/dist/orchestrator/app-handle.js +39 -8
- package/dist/orchestrator/app-handle.js.map +1 -1
- package/dist/orchestrator/application-runtime-child.d.ts +2 -0
- package/dist/orchestrator/application-runtime-child.d.ts.map +1 -0
- package/dist/orchestrator/application-runtime-child.js +15 -0
- package/dist/orchestrator/application-runtime-child.js.map +1 -0
- package/dist/orchestrator/application-runtime-resolution.d.ts +4 -0
- package/dist/orchestrator/application-runtime-resolution.d.ts.map +1 -0
- package/dist/orchestrator/application-runtime-resolution.js +62 -0
- package/dist/orchestrator/application-runtime-resolution.js.map +1 -0
- package/dist/orchestrator/application-runtime.d.ts +3 -0
- package/dist/orchestrator/application-runtime.d.ts.map +1 -0
- package/dist/orchestrator/application-runtime.js +20 -0
- package/dist/orchestrator/application-runtime.js.map +1 -0
- package/dist/orchestrator/application-topology.d.ts +20 -0
- package/dist/orchestrator/application-topology.d.ts.map +1 -0
- package/dist/orchestrator/application-topology.js +90 -0
- package/dist/orchestrator/application-topology.js.map +1 -0
- package/dist/orchestrator/bootstrap-loader-child.d.ts +1 -1
- package/dist/orchestrator/bootstrap-loader-child.js +7 -16
- package/dist/orchestrator/bootstrap-loader-child.js.map +1 -1
- package/dist/orchestrator/bootstrap-loader.d.ts +8 -18
- package/dist/orchestrator/bootstrap-loader.d.ts.map +1 -1
- package/dist/orchestrator/bootstrap-loader.js +21 -204
- package/dist/orchestrator/bootstrap-loader.js.map +1 -1
- package/dist/orchestrator/bootstrap-process.d.ts +5 -5
- package/dist/orchestrator/bootstrap-process.d.ts.map +1 -1
- package/dist/orchestrator/bootstrap-process.js +37 -38
- package/dist/orchestrator/bootstrap-process.js.map +1 -1
- package/dist/orchestrator/build-service.d.ts +3 -3
- package/dist/orchestrator/build-service.d.ts.map +1 -1
- package/dist/orchestrator/build-service.js +29 -80
- package/dist/orchestrator/build-service.js.map +1 -1
- package/dist/orchestrator/call-exposed-service.d.ts +8 -0
- package/dist/orchestrator/call-exposed-service.d.ts.map +1 -1
- package/dist/orchestrator/classic-launcher.d.ts +3 -2
- package/dist/orchestrator/classic-launcher.d.ts.map +1 -1
- package/dist/orchestrator/classic-launcher.js +26 -22
- package/dist/orchestrator/classic-launcher.js.map +1 -1
- package/dist/orchestrator/dependency-resolver.d.ts.map +1 -1
- package/dist/orchestrator/dependency-resolver.js +7 -38
- package/dist/orchestrator/dependency-resolver.js.map +1 -1
- package/dist/orchestrator/execution-contract.d.ts +67 -0
- package/dist/orchestrator/execution-contract.d.ts.map +1 -0
- package/dist/orchestrator/execution-contract.js +2 -0
- package/dist/orchestrator/execution-contract.js.map +1 -0
- package/dist/orchestrator/file-watcher.d.ts +1 -1
- package/dist/orchestrator/file-watcher.d.ts.map +1 -1
- package/dist/orchestrator/file-watcher.js +10 -3
- package/dist/orchestrator/file-watcher.js.map +1 -1
- package/dist/orchestrator/installed-execution.d.ts +7 -0
- package/dist/orchestrator/installed-execution.d.ts.map +1 -0
- package/dist/orchestrator/installed-execution.js +25 -0
- package/dist/orchestrator/installed-execution.js.map +1 -0
- package/dist/orchestrator/module-worker-process.d.ts.map +1 -1
- package/dist/orchestrator/module-worker-process.js +2 -1
- package/dist/orchestrator/module-worker-process.js.map +1 -1
- package/dist/orchestrator/orchestrator.service.d.ts +87 -76
- package/dist/orchestrator/orchestrator.service.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator.service.js +941 -469
- package/dist/orchestrator/orchestrator.service.js.map +1 -1
- package/dist/orchestrator/process-janitor.d.ts +3 -0
- package/dist/orchestrator/process-janitor.d.ts.map +1 -1
- package/dist/orchestrator/process-janitor.js +5 -1
- package/dist/orchestrator/process-janitor.js.map +1 -1
- package/dist/orchestrator/runtime-package-resolution.d.ts +8 -0
- package/dist/orchestrator/runtime-package-resolution.d.ts.map +1 -0
- package/dist/orchestrator/runtime-package-resolution.js +56 -0
- package/dist/orchestrator/runtime-package-resolution.js.map +1 -0
- package/dist/orchestrator/service-router.d.ts +4 -108
- package/dist/orchestrator/service-router.d.ts.map +1 -1
- package/dist/orchestrator/service-router.js +51 -237
- package/dist/orchestrator/service-router.js.map +1 -1
- package/dist/orchestrator/ts-compiler.d.ts.map +1 -1
- package/dist/orchestrator/ts-compiler.js +9 -17
- package/dist/orchestrator/ts-compiler.js.map +1 -1
- package/dist/project/artifact-builder.d.ts +17 -7
- package/dist/project/artifact-builder.d.ts.map +1 -1
- package/dist/project/artifact-builder.js +89 -56
- package/dist/project/artifact-builder.js.map +1 -1
- package/dist/project/bootstrap-metadata.d.ts +42 -0
- package/dist/project/bootstrap-metadata.d.ts.map +1 -0
- package/dist/project/bootstrap-metadata.js +76 -0
- package/dist/project/bootstrap-metadata.js.map +1 -0
- package/dist/project/config-resolver.js +3 -3
- package/dist/project/config-resolver.js.map +1 -1
- package/dist/project/configuration-catalog.d.ts +8 -0
- package/dist/project/configuration-catalog.d.ts.map +1 -0
- package/dist/project/configuration-catalog.js +29 -0
- package/dist/project/configuration-catalog.js.map +1 -0
- package/dist/project/declared-config.d.ts +2 -0
- package/dist/project/declared-config.d.ts.map +1 -1
- package/dist/project/declared-config.js +8 -5
- package/dist/project/declared-config.js.map +1 -1
- package/dist/project/dependency-topology.d.ts +10 -0
- package/dist/project/dependency-topology.d.ts.map +1 -0
- package/dist/project/dependency-topology.js +55 -0
- package/dist/project/dependency-topology.js.map +1 -0
- package/dist/project/infrastructure-ownership.d.ts +5 -0
- package/dist/project/infrastructure-ownership.d.ts.map +1 -0
- package/dist/project/infrastructure-ownership.js +26 -0
- package/dist/project/infrastructure-ownership.js.map +1 -0
- package/dist/project/installed-stack-resume.d.ts +62 -0
- package/dist/project/installed-stack-resume.d.ts.map +1 -0
- package/dist/project/installed-stack-resume.js +261 -0
- package/dist/project/installed-stack-resume.js.map +1 -0
- package/dist/project/launch-failure.d.ts +3 -0
- package/dist/project/launch-failure.d.ts.map +1 -0
- package/dist/project/launch-failure.js +32 -0
- package/dist/project/launch-failure.js.map +1 -0
- package/dist/project/local-release-selection.d.ts +18 -0
- package/dist/project/local-release-selection.d.ts.map +1 -0
- package/dist/project/local-release-selection.js +36 -0
- package/dist/project/local-release-selection.js.map +1 -0
- package/dist/project/node-app-config.d.ts +8 -1
- package/dist/project/node-app-config.d.ts.map +1 -1
- package/dist/project/node-app-config.js +20 -28
- package/dist/project/node-app-config.js.map +1 -1
- package/dist/project/node-stack-stop.d.ts +83 -0
- package/dist/project/node-stack-stop.d.ts.map +1 -0
- package/dist/project/node-stack-stop.js +219 -0
- package/dist/project/node-stack-stop.js.map +1 -0
- package/dist/project/public-stack.d.ts +4 -0
- package/dist/project/public-stack.d.ts.map +1 -0
- package/dist/project/public-stack.js +29 -0
- package/dist/project/public-stack.js.map +1 -0
- package/dist/project/registry.d.ts +18 -27
- package/dist/project/registry.d.ts.map +1 -1
- package/dist/project/registry.js +244 -55
- package/dist/project/registry.js.map +1 -1
- package/dist/project/requirements-scanner.d.ts +4 -8
- package/dist/project/requirements-scanner.d.ts.map +1 -1
- package/dist/project/requirements-scanner.js +39 -146
- package/dist/project/requirements-scanner.js.map +1 -1
- package/dist/project/stack-diagnosis.d.ts +18 -0
- package/dist/project/stack-diagnosis.d.ts.map +1 -0
- package/dist/project/stack-diagnosis.js +94 -0
- package/dist/project/stack-diagnosis.js.map +1 -0
- package/dist/project/stack-health.d.ts +12 -0
- package/dist/project/stack-health.d.ts.map +1 -0
- package/dist/project/stack-health.js +27 -0
- package/dist/project/stack-health.js.map +1 -0
- package/dist/project/stack-observation.d.ts +8 -0
- package/dist/project/stack-observation.d.ts.map +1 -0
- package/dist/project/stack-observation.js +126 -0
- package/dist/project/stack-observation.js.map +1 -0
- package/dist/project/stack-placement.d.ts +11 -0
- package/dist/project/stack-placement.d.ts.map +1 -0
- package/dist/project/stack-placement.js +57 -0
- package/dist/project/stack-placement.js.map +1 -0
- package/dist/project/stack-stop-review.d.ts +14 -0
- package/dist/project/stack-stop-review.d.ts.map +1 -0
- package/dist/project/stack-stop-review.js +22 -0
- package/dist/project/stack-stop-review.js.map +1 -0
- package/dist/project/stack-targets.d.ts +24 -0
- package/dist/project/stack-targets.d.ts.map +1 -0
- package/dist/project/stack-targets.js +67 -0
- package/dist/project/stack-targets.js.map +1 -0
- package/dist/project/topology-control.d.ts +41 -0
- package/dist/project/topology-control.d.ts.map +1 -0
- package/dist/project/topology-control.js +133 -0
- package/dist/project/topology-control.js.map +1 -0
- package/dist/project/topology-plan.d.ts +11 -0
- package/dist/project/topology-plan.d.ts.map +1 -0
- package/dist/project/topology-plan.js +154 -0
- package/dist/project/topology-plan.js.map +1 -0
- package/dist/project/topology-services.d.ts +7 -0
- package/dist/project/topology-services.d.ts.map +1 -0
- package/dist/project/topology-services.js +36 -0
- package/dist/project/topology-services.js.map +1 -0
- package/dist/project/topology-state.d.ts +27 -0
- package/dist/project/topology-state.d.ts.map +1 -0
- package/dist/project/topology-state.js +71 -0
- package/dist/project/topology-state.js.map +1 -0
- package/dist/project/workspace-kind.d.ts +3 -0
- package/dist/project/workspace-kind.d.ts.map +1 -0
- package/dist/project/workspace-kind.js +13 -0
- package/dist/project/workspace-kind.js.map +1 -0
- package/dist/readiness.d.ts +4 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +3 -0
- package/dist/readiness.js.map +1 -0
- package/dist/release/application-closure.d.ts +4 -0
- package/dist/release/application-closure.d.ts.map +1 -0
- package/dist/release/application-closure.js +61 -0
- package/dist/release/application-closure.js.map +1 -0
- package/dist/release/archive.d.ts +3 -0
- package/dist/release/archive.d.ts.map +1 -0
- package/dist/release/archive.js +3 -0
- package/dist/release/archive.js.map +1 -0
- package/dist/release/artifact-inspection.d.ts +50 -0
- package/dist/release/artifact-inspection.d.ts.map +1 -0
- package/dist/release/artifact-inspection.js +169 -0
- package/dist/release/artifact-inspection.js.map +1 -0
- package/dist/release/artifact-volumes.d.ts +10 -0
- package/dist/release/artifact-volumes.d.ts.map +1 -0
- package/dist/release/artifact-volumes.js +112 -0
- package/dist/release/artifact-volumes.js.map +1 -0
- package/dist/release/build-run.d.ts +12 -15
- package/dist/release/build-run.d.ts.map +1 -1
- package/dist/release/build-run.js +265 -81
- package/dist/release/build-run.js.map +1 -1
- package/dist/release/builder.d.ts +8 -2
- package/dist/release/builder.d.ts.map +1 -1
- package/dist/release/builder.js +18 -4
- package/dist/release/builder.js.map +1 -1
- package/dist/release/closure-library-path.d.ts +26 -0
- package/dist/release/closure-library-path.d.ts.map +1 -0
- package/dist/release/closure-library-path.js +160 -0
- package/dist/release/closure-library-path.js.map +1 -0
- package/dist/release/definition-inputs.d.ts +6 -3
- package/dist/release/definition-inputs.d.ts.map +1 -1
- package/dist/release/definition-inputs.js +145 -14
- package/dist/release/definition-inputs.js.map +1 -1
- package/dist/release/framework-source.d.ts +29 -0
- package/dist/release/framework-source.d.ts.map +1 -0
- package/dist/release/framework-source.js +63 -0
- package/dist/release/framework-source.js.map +1 -0
- package/dist/release/frozen-installation.d.ts +23 -0
- package/dist/release/frozen-installation.d.ts.map +1 -0
- package/dist/release/frozen-installation.js +159 -0
- package/dist/release/frozen-installation.js.map +1 -0
- package/dist/release/import-artifacts.d.ts +9 -0
- package/dist/release/import-artifacts.d.ts.map +1 -0
- package/dist/release/import-artifacts.js +60 -0
- package/dist/release/import-artifacts.js.map +1 -0
- package/dist/release/infrastructure.d.ts +6 -0
- package/dist/release/infrastructure.d.ts.map +1 -0
- package/dist/release/infrastructure.js +74 -0
- package/dist/release/infrastructure.js.map +1 -0
- package/dist/release/installed-artifacts.d.ts +12 -0
- package/dist/release/installed-artifacts.d.ts.map +1 -0
- package/dist/release/installed-artifacts.js +199 -0
- package/dist/release/installed-artifacts.js.map +1 -0
- package/dist/release/installed-graph.d.ts +28 -0
- package/dist/release/installed-graph.d.ts.map +1 -0
- package/dist/release/installed-graph.js +138 -0
- package/dist/release/installed-graph.js.map +1 -0
- package/dist/release/installed-origin.d.ts +42 -0
- package/dist/release/installed-origin.d.ts.map +1 -0
- package/dist/release/installed-origin.js +130 -0
- package/dist/release/installed-origin.js.map +1 -0
- package/dist/release/installed-release.d.ts +23 -0
- package/dist/release/installed-release.d.ts.map +1 -0
- package/dist/release/installed-release.js +141 -0
- package/dist/release/installed-release.js.map +1 -0
- package/dist/release/layout.d.ts +27 -32
- package/dist/release/layout.d.ts.map +1 -1
- package/dist/release/layout.js +162 -51
- package/dist/release/layout.js.map +1 -1
- package/dist/release/load.d.ts +3 -0
- package/dist/release/load.d.ts.map +1 -1
- package/dist/release/load.js +50 -8
- package/dist/release/load.js.map +1 -1
- package/dist/release/local-installation.d.ts +20 -0
- package/dist/release/local-installation.d.ts.map +1 -0
- package/dist/release/local-installation.js +298 -0
- package/dist/release/local-installation.js.map +1 -0
- package/dist/release/manifest.d.ts +87 -12
- package/dist/release/manifest.d.ts.map +1 -1
- package/dist/release/manifest.js +31 -7
- package/dist/release/manifest.js.map +1 -1
- package/dist/release/migration-programs.d.ts +12 -0
- package/dist/release/migration-programs.d.ts.map +1 -0
- package/dist/release/migration-programs.js +45 -0
- package/dist/release/migration-programs.js.map +1 -0
- package/dist/release/migrations.d.ts +12 -1
- package/dist/release/migrations.d.ts.map +1 -1
- package/dist/release/migrations.js +124 -9
- package/dist/release/migrations.js.map +1 -1
- package/dist/release/origin.d.ts +33 -0
- package/dist/release/origin.d.ts.map +1 -0
- package/dist/release/origin.js +253 -0
- package/dist/release/origin.js.map +1 -0
- package/dist/release/owner-admission.d.ts +15 -0
- package/dist/release/owner-admission.d.ts.map +1 -0
- package/dist/release/owner-admission.js +37 -0
- package/dist/release/owner-admission.js.map +1 -0
- package/dist/release/owner-executable.d.ts +3 -0
- package/dist/release/owner-executable.d.ts.map +1 -0
- package/dist/release/owner-executable.js +40 -0
- package/dist/release/owner-executable.js.map +1 -0
- package/dist/release/package-gates.d.ts +9 -0
- package/dist/release/package-gates.d.ts.map +1 -0
- package/dist/release/package-gates.js +42 -0
- package/dist/release/package-gates.js.map +1 -0
- package/dist/release/package-resolution.d.ts +4 -0
- package/dist/release/package-resolution.d.ts.map +1 -0
- package/dist/release/package-resolution.js +28 -0
- package/dist/release/package-resolution.js.map +1 -0
- package/dist/release/portable-archive.d.ts +12 -0
- package/dist/release/portable-archive.d.ts.map +1 -0
- package/dist/release/portable-archive.js +96 -0
- package/dist/release/portable-archive.js.map +1 -0
- package/dist/release/registry-download.d.ts +2 -0
- package/dist/release/registry-download.d.ts.map +1 -0
- package/dist/release/registry-download.js +157 -0
- package/dist/release/registry-download.js.map +1 -0
- package/dist/release/registry-framework.d.ts +39 -0
- package/dist/release/registry-framework.d.ts.map +1 -0
- package/dist/release/registry-framework.js +279 -0
- package/dist/release/registry-framework.js.map +1 -0
- package/dist/release/registry-transport.d.ts +27 -0
- package/dist/release/registry-transport.d.ts.map +1 -0
- package/dist/release/registry-transport.js +96 -0
- package/dist/release/registry-transport.js.map +1 -0
- package/dist/release/repository-identity.d.ts +12 -0
- package/dist/release/repository-identity.d.ts.map +1 -0
- package/dist/release/repository-identity.js +62 -0
- package/dist/release/repository-identity.js.map +1 -0
- package/dist/release/runtime-extractor.d.ts +4 -0
- package/dist/release/runtime-extractor.d.ts.map +1 -0
- package/dist/release/runtime-extractor.js +15 -0
- package/dist/release/runtime-extractor.js.map +1 -0
- package/dist/release/runtime-provenance.d.ts +40 -0
- package/dist/release/runtime-provenance.d.ts.map +1 -0
- package/dist/release/runtime-provenance.js +229 -0
- package/dist/release/runtime-provenance.js.map +1 -0
- package/dist/release/source.d.ts +12 -0
- package/dist/release/source.d.ts.map +1 -0
- package/dist/release/source.js +168 -0
- package/dist/release/source.js.map +1 -0
- package/dist/release/store.d.ts +13 -4
- package/dist/release/store.d.ts.map +1 -1
- package/dist/release/store.js +70 -14
- package/dist/release/store.js.map +1 -1
- package/dist/release/target-runtime.d.ts +19 -0
- package/dist/release/target-runtime.d.ts.map +1 -0
- package/dist/release/target-runtime.js +41 -0
- package/dist/release/target-runtime.js.map +1 -0
- package/dist/release/vendored-framework.d.ts +28 -0
- package/dist/release/vendored-framework.d.ts.map +1 -0
- package/dist/release/vendored-framework.js +98 -0
- package/dist/release/vendored-framework.js.map +1 -0
- package/dist/release/workspace-source.d.ts +3 -0
- package/dist/release/workspace-source.d.ts.map +1 -0
- package/dist/release/workspace-source.js +33 -0
- package/dist/release/workspace-source.js.map +1 -0
- package/dist/runtime/context.d.ts +29 -0
- package/dist/runtime/context.d.ts.map +1 -0
- package/dist/runtime/context.js +111 -0
- package/dist/runtime/context.js.map +1 -0
- package/dist/runtime/identity.d.ts +15 -0
- package/dist/runtime/identity.d.ts.map +1 -0
- package/dist/runtime/identity.js +33 -0
- package/dist/runtime/identity.js.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/services/access.rpc-service.d.ts +41 -0
- package/dist/services/access.rpc-service.d.ts.map +1 -0
- package/dist/services/access.rpc-service.js +248 -0
- package/dist/services/access.rpc-service.js.map +1 -0
- package/dist/services/alert.service.d.ts +1 -1
- package/dist/services/alert.service.d.ts.map +1 -1
- package/dist/services/alert.service.js +1 -1
- package/dist/services/anonymous-input.d.ts +0 -21
- package/dist/services/anonymous-input.d.ts.map +1 -1
- package/dist/services/anonymous-input.js +1 -27
- package/dist/services/anonymous-input.js.map +1 -1
- package/dist/services/audit.service.d.ts +10 -30
- package/dist/services/audit.service.d.ts.map +1 -1
- package/dist/services/audit.service.js +72 -18
- package/dist/services/audit.service.js.map +1 -1
- package/dist/services/auth-context.d.ts +2 -0
- package/dist/services/auth-context.d.ts.map +1 -1
- package/dist/services/auth-context.js +4 -1
- package/dist/services/auth-context.js.map +1 -1
- package/dist/services/auth.rpc-service.d.ts +3 -1
- package/dist/services/auth.rpc-service.d.ts.map +1 -1
- package/dist/services/auth.rpc-service.js +13 -3
- package/dist/services/auth.rpc-service.js.map +1 -1
- package/dist/services/auth.service.d.ts +11 -21
- package/dist/services/auth.service.d.ts.map +1 -1
- package/dist/services/auth.service.js +125 -139
- package/dist/services/auth.service.js.map +1 -1
- package/dist/services/backup-storage.d.ts +14 -0
- package/dist/services/backup-storage.d.ts.map +1 -0
- package/dist/services/backup-storage.js +195 -0
- package/dist/services/backup-storage.js.map +1 -0
- package/dist/services/backup.rpc-service.d.ts +2 -0
- package/dist/services/backup.rpc-service.d.ts.map +1 -1
- package/dist/services/backup.rpc-service.js +9 -0
- package/dist/services/backup.rpc-service.js.map +1 -1
- package/dist/services/backup.service.d.ts +33 -126
- package/dist/services/backup.service.d.ts.map +1 -1
- package/dist/services/backup.service.js +174 -153
- package/dist/services/backup.service.js.map +1 -1
- package/dist/services/bundle-builder.d.ts +12 -0
- package/dist/services/bundle-builder.d.ts.map +1 -1
- package/dist/services/bundle-builder.js +77 -62
- package/dist/services/bundle-builder.js.map +1 -1
- package/dist/services/bundle-overrides.d.ts +25 -0
- package/dist/services/bundle-overrides.d.ts.map +1 -0
- package/dist/services/bundle-overrides.js +96 -0
- package/dist/services/bundle-overrides.js.map +1 -0
- package/dist/services/daemon-authority.d.ts +16 -0
- package/dist/services/daemon-authority.d.ts.map +1 -0
- package/dist/services/daemon-authority.js +54 -0
- package/dist/services/daemon-authority.js.map +1 -0
- package/dist/services/daemon-principal.d.ts +31 -0
- package/dist/services/daemon-principal.d.ts.map +1 -0
- package/dist/services/daemon-principal.js +37 -0
- package/dist/services/daemon-principal.js.map +1 -0
- package/dist/services/deploy.service.d.ts +13 -57
- package/dist/services/deploy.service.d.ts.map +1 -1
- package/dist/services/deploy.service.js +15 -263
- package/dist/services/deploy.service.js.map +1 -1
- package/dist/services/event-broadcaster.rpc-service.d.ts +7 -1
- package/dist/services/event-broadcaster.rpc-service.d.ts.map +1 -1
- package/dist/services/event-broadcaster.rpc-service.js +89 -11
- package/dist/services/event-broadcaster.rpc-service.js.map +1 -1
- package/dist/services/fleet.rpc-service.d.ts +0 -6
- package/dist/services/fleet.rpc-service.d.ts.map +1 -1
- package/dist/services/fleet.rpc-service.js +0 -13
- package/dist/services/fleet.rpc-service.js.map +1 -1
- package/dist/services/health-check.service.d.ts +1 -1
- package/dist/services/health-check.service.d.ts.map +1 -1
- package/dist/services/health-check.service.js +30 -5
- package/dist/services/health-check.service.js.map +1 -1
- package/dist/services/host-upgrade.d.ts +55 -3
- package/dist/services/host-upgrade.d.ts.map +1 -1
- package/dist/services/host-upgrade.js +167 -16
- package/dist/services/host-upgrade.js.map +1 -1
- package/dist/services/infrastructure.rpc-service.d.ts +9 -2
- package/dist/services/infrastructure.rpc-service.d.ts.map +1 -1
- package/dist/services/infrastructure.rpc-service.js +117 -36
- package/dist/services/infrastructure.rpc-service.js.map +1 -1
- package/dist/services/kernel-catalog.d.ts +14 -0
- package/dist/services/kernel-catalog.d.ts.map +1 -0
- package/dist/services/kernel-catalog.js +172 -0
- package/dist/services/kernel-catalog.js.map +1 -0
- package/dist/services/kernel-distribution.d.ts +5 -0
- package/dist/services/kernel-distribution.d.ts.map +1 -0
- package/dist/services/kernel-distribution.js +49 -0
- package/dist/services/kernel-distribution.js.map +1 -0
- package/dist/services/kernel-rollout.service.d.ts +55 -0
- package/dist/services/kernel-rollout.service.d.ts.map +1 -0
- package/dist/services/kernel-rollout.service.js +394 -0
- package/dist/services/kernel-rollout.service.js.map +1 -0
- package/dist/services/local-bundle.d.ts.map +1 -1
- package/dist/services/local-bundle.js +3 -1
- package/dist/services/local-bundle.js.map +1 -1
- package/dist/services/log-collector.rpc-service.d.ts +5 -11
- package/dist/services/log-collector.rpc-service.d.ts.map +1 -1
- package/dist/services/log-collector.rpc-service.js +6 -6
- package/dist/services/log-collector.rpc-service.js.map +1 -1
- package/dist/services/log-collector.service.d.ts +39 -109
- package/dist/services/log-collector.service.d.ts.map +1 -1
- package/dist/services/log-collector.service.js +286 -261
- package/dist/services/log-collector.service.js.map +1 -1
- package/dist/services/log-query.d.ts +6 -0
- package/dist/services/log-query.d.ts.map +1 -0
- package/dist/services/log-query.js +46 -0
- package/dist/services/log-query.js.map +1 -0
- package/dist/services/managed-rollout.d.ts +54 -0
- package/dist/services/managed-rollout.d.ts.map +1 -0
- package/dist/services/managed-rollout.js +124 -0
- package/dist/services/managed-rollout.js.map +1 -0
- package/dist/services/metrics.rpc-service.d.ts +23 -0
- package/dist/services/metrics.rpc-service.d.ts.map +1 -0
- package/dist/services/metrics.rpc-service.js +73 -0
- package/dist/services/metrics.rpc-service.js.map +1 -0
- package/dist/services/migration-process.d.ts +32 -0
- package/dist/services/migration-process.d.ts.map +1 -0
- package/dist/services/migration-process.js +71 -0
- package/dist/services/migration-process.js.map +1 -0
- package/dist/services/migration-target.d.ts +57 -0
- package/dist/services/migration-target.d.ts.map +1 -0
- package/dist/services/migration-target.js +221 -0
- package/dist/services/migration-target.js.map +1 -0
- package/dist/services/node-health.repository.d.ts +2 -59
- package/dist/services/node-health.repository.d.ts.map +1 -1
- package/dist/services/node-health.repository.js.map +1 -1
- package/dist/services/node-host-key.d.ts +9 -0
- package/dist/services/node-host-key.d.ts.map +1 -0
- package/dist/services/node-host-key.js +17 -0
- package/dist/services/node-host-key.js.map +1 -0
- package/dist/services/node-manager.rpc-service.d.ts +44 -37
- package/dist/services/node-manager.rpc-service.d.ts.map +1 -1
- package/dist/services/node-manager.rpc-service.js +208 -37
- package/dist/services/node-manager.rpc-service.js.map +1 -1
- package/dist/services/node-manager.service.d.ts +3 -0
- package/dist/services/node-manager.service.d.ts.map +1 -1
- package/dist/services/node-manager.service.js +78 -27
- package/dist/services/node-manager.service.js.map +1 -1
- package/dist/services/node-registration.d.ts +17 -0
- package/dist/services/node-registration.d.ts.map +1 -0
- package/dist/services/node-registration.js +73 -0
- package/dist/services/node-registration.js.map +1 -0
- package/dist/services/node-upgrade.service.d.ts +2 -28
- package/dist/services/node-upgrade.service.d.ts.map +1 -1
- package/dist/services/node-upgrade.service.js +2 -3
- package/dist/services/node-upgrade.service.js.map +1 -1
- package/dist/services/pipeline-definition.d.ts +14 -0
- package/dist/services/pipeline-definition.d.ts.map +1 -0
- package/dist/services/pipeline-definition.js +77 -0
- package/dist/services/pipeline-definition.js.map +1 -0
- package/dist/services/pipeline.rpc-service.d.ts +10 -3
- package/dist/services/pipeline.rpc-service.d.ts.map +1 -1
- package/dist/services/pipeline.rpc-service.js +38 -14
- package/dist/services/pipeline.rpc-service.js.map +1 -1
- package/dist/services/pipeline.service.d.ts +30 -46
- package/dist/services/pipeline.service.d.ts.map +1 -1
- package/dist/services/pipeline.service.js +401 -344
- package/dist/services/pipeline.service.js.map +1 -1
- package/dist/services/project.rpc-service.d.ts +212 -14
- package/dist/services/project.rpc-service.d.ts.map +1 -1
- package/dist/services/project.rpc-service.js +303 -31
- package/dist/services/project.rpc-service.js.map +1 -1
- package/dist/services/project.service.d.ts +174 -56
- package/dist/services/project.service.d.ts.map +1 -1
- package/dist/services/project.service.js +1957 -661
- package/dist/services/project.service.js.map +1 -1
- package/dist/services/release.rpc-service.d.ts +5 -18
- package/dist/services/release.rpc-service.d.ts.map +1 -1
- package/dist/services/release.rpc-service.js +34 -11
- package/dist/services/release.rpc-service.js.map +1 -1
- package/dist/services/release.service.d.ts +2 -47
- package/dist/services/release.service.d.ts.map +1 -1
- package/dist/services/release.service.js +26 -2
- package/dist/services/release.service.js.map +1 -1
- package/dist/services/remote-deployer.service.d.ts +41 -113
- package/dist/services/remote-deployer.service.d.ts.map +1 -1
- package/dist/services/remote-deployer.service.js +415 -378
- package/dist/services/remote-deployer.service.js.map +1 -1
- package/dist/services/remote-provisioner.d.ts +1 -1
- package/dist/services/remote-provisioner.js +1 -1
- package/dist/services/replication-evidence.d.ts +24 -0
- package/dist/services/replication-evidence.d.ts.map +1 -0
- package/dist/services/replication-evidence.js +61 -0
- package/dist/services/replication-evidence.js.map +1 -0
- package/dist/services/replication-history.d.ts +80 -0
- package/dist/services/replication-history.d.ts.map +1 -0
- package/dist/services/replication-history.js +279 -0
- package/dist/services/replication-history.js.map +1 -0
- package/dist/services/replication-reconciliation.d.ts +74 -0
- package/dist/services/replication-reconciliation.d.ts.map +1 -0
- package/dist/services/replication-reconciliation.js +351 -0
- package/dist/services/replication-reconciliation.js.map +1 -0
- package/dist/services/slave-storage.service.d.ts +2 -19
- package/dist/services/slave-storage.service.d.ts.map +1 -1
- package/dist/services/slave-storage.service.js +19 -6
- package/dist/services/slave-storage.service.js.map +1 -1
- package/dist/services/static-carry-forward.d.ts +2 -2
- package/dist/services/static-carry-forward.d.ts.map +1 -1
- package/dist/services/static-carry-forward.js +10 -3
- package/dist/services/static-carry-forward.js.map +1 -1
- package/dist/services/sync-summary.d.ts.map +1 -1
- package/dist/services/sync-summary.js +1 -18
- package/dist/services/sync-summary.js.map +1 -1
- package/dist/services/sync.rpc-service.d.ts +17 -20
- package/dist/services/sync.rpc-service.d.ts.map +1 -1
- package/dist/services/sync.rpc-service.js +23 -37
- package/dist/services/sync.rpc-service.js.map +1 -1
- package/dist/services/sync.service.d.ts +53 -157
- package/dist/services/sync.service.d.ts.map +1 -1
- package/dist/services/sync.service.js +239 -337
- package/dist/services/sync.service.js.map +1 -1
- package/dist/services/system-info.rpc-service.d.ts +3 -1
- package/dist/services/system-info.rpc-service.d.ts.map +1 -1
- package/dist/services/system-info.rpc-service.js +5 -5
- package/dist/services/system-info.rpc-service.js.map +1 -1
- package/dist/services/system-info.service.d.ts +3 -1
- package/dist/services/system-info.service.d.ts.map +1 -1
- package/dist/services/system-info.service.js +17 -9
- package/dist/services/system-info.service.js.map +1 -1
- package/dist/services/telemetry.rpc-service.d.ts +4 -23
- package/dist/services/telemetry.rpc-service.d.ts.map +1 -1
- package/dist/services/telemetry.rpc-service.js +4 -32
- package/dist/services/telemetry.rpc-service.js.map +1 -1
- package/dist/services/telemetry.service.d.ts +2 -30
- package/dist/services/telemetry.service.d.ts.map +1 -1
- package/dist/services/telemetry.service.js +25 -131
- package/dist/services/telemetry.service.js.map +1 -1
- package/dist/services/terminal-ssh.d.ts +16 -0
- package/dist/services/terminal-ssh.d.ts.map +1 -0
- package/dist/services/terminal-ssh.js +101 -0
- package/dist/services/terminal-ssh.js.map +1 -0
- package/dist/services/terminal.rpc-service.d.ts +84 -0
- package/dist/services/terminal.rpc-service.d.ts.map +1 -0
- package/dist/services/terminal.rpc-service.js +508 -0
- package/dist/services/terminal.rpc-service.js.map +1 -0
- package/dist/services/trace-collector.rpc-service.js +3 -3
- package/dist/services/trace-collector.rpc-service.js.map +1 -1
- package/dist/services/wait-for-postgres.d.ts +1 -1
- package/dist/services/wait-for-postgres.d.ts.map +1 -1
- package/dist/services/wait-for-postgres.js +13 -28
- package/dist/services/wait-for-postgres.js.map +1 -1
- package/dist/shared/build-stamp.d.ts +4 -1
- package/dist/shared/build-stamp.d.ts.map +1 -1
- package/dist/shared/build-stamp.js +14 -6
- package/dist/shared/build-stamp.js.map +1 -1
- package/dist/shared/describe-error.d.ts +1 -21
- package/dist/shared/describe-error.d.ts.map +1 -1
- package/dist/shared/describe-error.js +1 -38
- package/dist/shared/describe-error.js.map +1 -1
- package/dist/shared/dto/access.d.ts +55 -0
- package/dist/shared/dto/access.d.ts.map +1 -0
- package/dist/shared/dto/access.js +2 -0
- package/dist/shared/dto/access.js.map +1 -0
- package/dist/shared/dto/audit.d.ts +32 -0
- package/dist/shared/dto/audit.d.ts.map +1 -0
- package/dist/shared/dto/audit.js +3 -0
- package/dist/shared/dto/audit.js.map +1 -0
- package/dist/shared/dto/auth.d.ts +2 -0
- package/dist/shared/dto/auth.d.ts.map +1 -1
- package/dist/shared/dto/auth.js +5 -1
- package/dist/shared/dto/auth.js.map +1 -1
- package/dist/shared/dto/backend.d.ts +50 -0
- package/dist/shared/dto/backend.d.ts.map +1 -0
- package/dist/shared/dto/backend.js +3 -0
- package/dist/shared/dto/backend.js.map +1 -0
- package/dist/shared/dto/backups.d.ts +99 -0
- package/dist/shared/dto/backups.d.ts.map +1 -1
- package/dist/shared/dto/cluster.d.ts +44 -0
- package/dist/shared/dto/cluster.d.ts.map +1 -0
- package/dist/shared/dto/cluster.js +3 -0
- package/dist/shared/dto/cluster.js.map +1 -0
- package/dist/shared/dto/files.d.ts +328 -0
- package/dist/shared/dto/files.d.ts.map +1 -0
- package/dist/shared/dto/files.js +2 -0
- package/dist/shared/dto/files.js.map +1 -0
- package/dist/shared/dto/health.d.ts +59 -0
- package/dist/shared/dto/health.d.ts.map +1 -1
- package/dist/shared/dto/installed-release.d.ts +40 -0
- package/dist/shared/dto/installed-release.d.ts.map +1 -0
- package/dist/shared/dto/installed-release.js +2 -0
- package/dist/shared/dto/installed-release.js.map +1 -0
- package/dist/shared/dto/kernel-catalog.d.ts +11 -0
- package/dist/shared/dto/kernel-catalog.d.ts.map +1 -0
- package/dist/shared/dto/kernel-catalog.js +2 -0
- package/dist/shared/dto/kernel-catalog.js.map +1 -0
- package/dist/shared/dto/logs.d.ts +21 -0
- package/dist/shared/dto/logs.d.ts.map +1 -1
- package/dist/shared/dto/metrics.d.ts +4 -64
- package/dist/shared/dto/metrics.d.ts.map +1 -1
- package/dist/shared/dto/metrics.js +0 -15
- package/dist/shared/dto/metrics.js.map +1 -1
- package/dist/shared/dto/nodes.d.ts +96 -22
- package/dist/shared/dto/nodes.d.ts.map +1 -1
- package/dist/shared/dto/pipelines.d.ts +22 -1
- package/dist/shared/dto/pipelines.d.ts.map +1 -1
- package/dist/shared/dto/project.d.ts +289 -30
- package/dist/shared/dto/project.d.ts.map +1 -1
- package/dist/shared/dto/releases.d.ts +59 -0
- package/dist/shared/dto/releases.d.ts.map +1 -0
- package/dist/shared/dto/releases.js +2 -0
- package/dist/shared/dto/releases.js.map +1 -0
- package/dist/shared/dto/services.d.ts +174 -37
- package/dist/shared/dto/services.d.ts.map +1 -1
- package/dist/shared/dto/services.js +15 -6
- package/dist/shared/dto/services.js.map +1 -1
- package/dist/shared/dto/stack-diagnosis.d.ts +20 -0
- package/dist/shared/dto/stack-diagnosis.d.ts.map +1 -0
- package/dist/shared/dto/stack-diagnosis.js +8 -0
- package/dist/shared/dto/stack-diagnosis.js.map +1 -0
- package/dist/shared/dto/stack-execution.d.ts +65 -0
- package/dist/shared/dto/stack-execution.d.ts.map +1 -0
- package/dist/shared/dto/stack-execution.js +33 -0
- package/dist/shared/dto/stack-execution.js.map +1 -0
- package/dist/shared/dto/system-info.d.ts +11 -0
- package/dist/shared/dto/system-info.d.ts.map +1 -1
- package/dist/shared/dto/system-info.js +7 -1
- package/dist/shared/dto/system-info.js.map +1 -1
- package/dist/shared/dto/terminal.d.ts +40 -0
- package/dist/shared/dto/terminal.d.ts.map +1 -0
- package/dist/shared/dto/terminal.js +2 -0
- package/dist/shared/dto/terminal.js.map +1 -0
- package/dist/shared/dto/topology.d.ts +96 -0
- package/dist/shared/dto/topology.d.ts.map +1 -0
- package/dist/shared/dto/topology.js +2 -0
- package/dist/shared/dto/topology.js.map +1 -0
- package/dist/shared/emergency-output.d.ts +3 -0
- package/dist/shared/emergency-output.d.ts.map +1 -0
- package/dist/shared/emergency-output.js +22 -0
- package/dist/shared/emergency-output.js.map +1 -0
- package/dist/shared/env-config.d.ts +2 -0
- package/dist/shared/env-config.d.ts.map +1 -1
- package/dist/shared/env-config.js +1 -0
- package/dist/shared/env-config.js.map +1 -1
- package/dist/shared/events.d.ts +2 -0
- package/dist/shared/events.d.ts.map +1 -1
- package/dist/shared/events.js.map +1 -1
- package/dist/shared/execution-identity.d.ts +39 -0
- package/dist/shared/execution-identity.d.ts.map +1 -0
- package/dist/shared/execution-identity.js +160 -0
- package/dist/shared/execution-identity.js.map +1 -0
- package/dist/shared/file-policy.d.ts +5 -0
- package/dist/shared/file-policy.d.ts.map +1 -0
- package/dist/shared/file-policy.js +18 -0
- package/dist/shared/file-policy.js.map +1 -0
- package/dist/shared/framework-source.d.ts +11 -0
- package/dist/shared/framework-source.d.ts.map +1 -0
- package/dist/shared/framework-source.js +31 -0
- package/dist/shared/framework-source.js.map +1 -0
- package/dist/shared/fresh-module-reader.d.ts +40 -0
- package/dist/shared/fresh-module-reader.d.ts.map +1 -0
- package/dist/shared/fresh-module-reader.js +257 -0
- package/dist/shared/fresh-module-reader.js.map +1 -0
- package/dist/shared/host-boot-identity.d.ts +12 -0
- package/dist/shared/host-boot-identity.d.ts.map +1 -0
- package/dist/shared/host-boot-identity.js +89 -0
- package/dist/shared/host-boot-identity.js.map +1 -0
- package/dist/shared/kernel-file-lock.d.ts +3 -0
- package/dist/shared/kernel-file-lock.d.ts.map +1 -0
- package/dist/shared/kernel-file-lock.js +53 -0
- package/dist/shared/kernel-file-lock.js.map +1 -0
- package/dist/shared/linux-file-lock.d.ts +4 -0
- package/dist/shared/linux-file-lock.d.ts.map +1 -0
- package/dist/shared/linux-file-lock.js +36 -0
- package/dist/shared/linux-file-lock.js.map +1 -0
- package/dist/shared/log-ingestion-reading.d.ts +9 -0
- package/dist/shared/log-ingestion-reading.d.ts.map +1 -0
- package/dist/shared/log-ingestion-reading.js +32 -0
- package/dist/shared/log-ingestion-reading.js.map +1 -0
- package/dist/shared/log-levels.d.ts +19 -0
- package/dist/shared/log-levels.d.ts.map +1 -0
- package/dist/shared/log-levels.js +23 -0
- package/dist/shared/log-levels.js.map +1 -0
- package/dist/shared/metrics-storage-reading.d.ts +8 -0
- package/dist/shared/metrics-storage-reading.d.ts.map +1 -0
- package/dist/shared/metrics-storage-reading.js +27 -0
- package/dist/shared/metrics-storage-reading.js.map +1 -0
- package/dist/shared/pnpm.d.ts.map +1 -1
- package/dist/shared/pnpm.js +7 -0
- package/dist/shared/pnpm.js.map +1 -1
- package/dist/shared/pool-scale.d.ts +10 -0
- package/dist/shared/pool-scale.d.ts.map +1 -0
- package/dist/shared/pool-scale.js +25 -0
- package/dist/shared/pool-scale.js.map +1 -0
- package/dist/shared/private-files.d.ts +10 -22
- package/dist/shared/private-files.d.ts.map +1 -1
- package/dist/shared/private-files.js +95 -82
- package/dist/shared/private-files.js.map +1 -1
- package/dist/shared/relay-reading.d.ts +19 -0
- package/dist/shared/relay-reading.d.ts.map +1 -0
- package/dist/shared/relay-reading.js +22 -0
- package/dist/shared/relay-reading.js.map +1 -0
- package/dist/shared/release-reading.d.ts +124 -32
- package/dist/shared/release-reading.d.ts.map +1 -1
- package/dist/shared/release-reading.js +208 -28
- package/dist/shared/release-reading.js.map +1 -1
- package/dist/shared/roles.d.ts +19 -18
- package/dist/shared/roles.d.ts.map +1 -1
- package/dist/shared/roles.js +21 -26
- package/dist/shared/roles.js.map +1 -1
- package/dist/shared/runtime-artifact.d.ts +20 -0
- package/dist/shared/runtime-artifact.d.ts.map +1 -0
- package/dist/shared/runtime-artifact.js +83 -0
- package/dist/shared/runtime-artifact.js.map +1 -0
- package/dist/shared/stack-reading.d.ts +18 -0
- package/dist/shared/stack-reading.d.ts.map +1 -0
- package/dist/shared/stack-reading.js +15 -0
- package/dist/shared/stack-reading.js.map +1 -0
- package/dist/shared/sync-reading.d.ts +30 -38
- package/dist/shared/sync-reading.d.ts.map +1 -1
- package/dist/shared/sync-reading.js +90 -29
- package/dist/shared/sync-reading.js.map +1 -1
- package/dist/shared/tokens.d.ts +3 -1
- package/dist/shared/tokens.d.ts.map +1 -1
- package/dist/shared/tokens.js +1 -0
- package/dist/shared/tokens.js.map +1 -1
- package/dist/supervisor/restart-policy.d.ts.map +1 -1
- package/dist/supervisor/restart-policy.js +7 -5
- package/dist/supervisor/restart-policy.js.map +1 -1
- package/dist/webapp/console-container.d.ts +7 -0
- package/dist/webapp/console-container.d.ts.map +1 -0
- package/dist/webapp/console-container.js +23 -0
- package/dist/webapp/console-container.js.map +1 -0
- package/dist/webapp/webapp.service.d.ts.map +1 -1
- package/dist/webapp/webapp.service.js +71 -24
- package/dist/webapp/webapp.service.js.map +1 -1
- package/dist/workers/health-monitor.service.d.ts.map +1 -1
- package/dist/workers/health-monitor.service.js +8 -1
- package/dist/workers/health-monitor.service.js.map +1 -1
- package/dist/workers/types.d.ts +2 -0
- package/dist/workers/types.d.ts.map +1 -1
- package/package.json +109 -44
- package/webapp/dist/BUILD.json +4 -3
- package/webapp/dist/assets/access-48hkC2-P.js +1 -0
- package/webapp/dist/assets/access-jWkHxGc7.js +1 -0
- package/webapp/dist/assets/admin-data-table-BKSDI8sU.js +1 -0
- package/webapp/dist/assets/alert-expression-CSblScoa.js +1 -0
- package/webapp/dist/assets/alert-notify-settings-C_CHZrFm.js +1 -0
- package/webapp/dist/assets/alert-uBG6r9NW.js +1 -0
- package/webapp/dist/assets/alerts-CuPQ9YQJ.js +1 -0
- package/webapp/dist/assets/app-address-BCXfN59F.js +1 -0
- package/webapp/dist/assets/apps-Drio028B.js +1 -0
- package/webapp/dist/assets/audit-npSFJECe.js +1 -0
- package/webapp/dist/assets/auth-CcgjP7hR.js +1 -0
- package/webapp/dist/assets/card-DkH32gMY.js +1 -0
- package/webapp/dist/assets/chart-BpGXlCJc.js +2 -0
- package/webapp/dist/assets/charts-BY7qI6s2.js +41 -0
- package/webapp/dist/assets/charts-CpBdHU_A.js +56 -0
- package/webapp/dist/assets/client-DFnuv2z3.js +3 -0
- package/webapp/dist/assets/code-editor-DK6Tmmxv.js +4 -0
- package/webapp/dist/assets/comparison-bars-BtC0dHjF.js +1 -0
- package/webapp/dist/assets/confirm-dialog-BbGsAz7_.js +1 -0
- package/webapp/dist/assets/console-keyboard-D3YCA0yU.js +1 -0
- package/webapp/dist/assets/constants-CyhngVXh.js +1 -0
- package/webapp/dist/assets/containers-Bl8gAyZX.js +3 -0
- package/webapp/dist/assets/context-FIuwpd9_.js +1 -0
- package/webapp/dist/assets/controls-C5W8gHU4.js +1 -0
- package/webapp/dist/assets/daemon-memory-BiEtr9jr.js +1 -0
- package/webapp/dist/assets/dashboard-UHlet3H9.js +1 -0
- package/webapp/dist/assets/dashboard-builder-67PZFqhE.js +1 -0
- package/webapp/dist/assets/dayjs.min-DHwAvH5f.js +1 -0
- package/webapp/dist/assets/deploy-release-dialog-BOXcD5Wo.js +1 -0
- package/webapp/dist/assets/deployments-qV20Ak0P.js +1 -0
- package/webapp/dist/assets/detail-B4TiBfxU.js +8 -0
- package/webapp/dist/assets/detail-B85-Wees.js +2 -0
- package/webapp/dist/assets/detail-CYS81K6Y.js +1 -0
- package/webapp/dist/assets/dist-BUDn8hLh.js +11 -0
- package/webapp/dist/assets/dist-Ci5pYRbx.js +1 -0
- package/webapp/dist/assets/dist-DQmjHv4-.js +1 -0
- package/webapp/dist/assets/dist-De9Fty7a.js +1 -0
- package/webapp/dist/assets/dist-DsGmKwmk.js +23 -0
- package/webapp/dist/assets/empty-content-7jOFhG4C.js +1 -0
- package/webapp/dist/assets/error-boundary-CLLI8PWw.js +3 -0
- package/webapp/dist/assets/event-fields-CiyqrRO8.js +1 -0
- package/webapp/dist/assets/files-DigwXndm.js +2 -0
- package/webapp/dist/assets/formatters-RnRy4bXm.js +1 -0
- package/webapp/dist/assets/icons-OboZgWSt.js +1 -0
- package/webapp/dist/assets/index-uq8Dkk4c.js +1 -0
- package/webapp/dist/assets/label-B1SF0RTL.js +1 -0
- package/webapp/dist/assets/log-level-filter-Bcn31D1F.js +1 -0
- package/webapp/dist/assets/log-location-DuBjh6HQ.js +1 -0
- package/webapp/dist/assets/logs-BqHOufk8.js +4 -0
- package/webapp/dist/assets/metric-chart-M-kI-XOt.js +1 -0
- package/webapp/dist/assets/metric-series-WxePniyw.js +1 -0
- package/webapp/dist/assets/metrics-QIQaF7iS.js +1 -0
- package/webapp/dist/assets/model-ZEXEHm0l.js +2 -0
- package/webapp/dist/assets/mui-6ssfcZcz.js +1 -0
- package/webapp/dist/assets/mui-AnEgAVjz.js +52 -0
- package/webapp/dist/assets/mui-B1jrje6Z.js +1 -0
- package/webapp/dist/assets/mui-BIz4LSVs.js +1 -0
- package/webapp/dist/assets/mui-BTN91yBK.js +28 -0
- package/webapp/dist/assets/mui-BXSXx9c9.js +124 -0
- package/webapp/dist/assets/mui-BwsIrivo.js +37 -0
- package/webapp/dist/assets/mui-C_hromOc.js +1 -0
- package/webapp/dist/assets/mui-Cd711nOk.js +4 -0
- package/webapp/dist/assets/mui-CdB_p2lt.js +10 -0
- package/webapp/dist/assets/mui-DyT2n1lx.js +1 -0
- package/webapp/dist/assets/mui-agG5dued.js +1 -0
- package/webapp/dist/assets/mui-y44PQOfG.js +1 -0
- package/webapp/dist/assets/nodes-BJPk02vv.js +7 -0
- package/webapp/dist/assets/omnitron-logo-koMCbsCv.js +1 -0
- package/webapp/dist/assets/operation-status-Bm1x1wTz.js +1 -0
- package/webapp/dist/assets/page-breadcrumbs-fR7Jmpp4.js +1 -0
- package/webapp/dist/assets/pipelines-T6Ch9LTw.js +3 -0
- package/webapp/dist/assets/popper-m_9b0JRf.js +1 -0
- package/webapp/dist/assets/preload-helper-DzCYNvzR.js +1 -0
- package/webapp/dist/assets/project.store--G5pIF-J.js +1 -0
- package/webapp/dist/assets/projects-YoBzcQ24.js +1 -0
- package/webapp/dist/assets/react-BN8qLIAi.js +9 -0
- package/webapp/dist/assets/react-CEuifcaQ.js +1 -0
- package/webapp/dist/assets/react-apexcharts.esm-DoOt-pu4.js +1 -0
- package/webapp/dist/assets/realtime.store-CBDO4Abj.js +1 -0
- package/webapp/dist/assets/release-bits-B2a_wbft.js +1 -0
- package/webapp/dist/assets/release-build-panel-BQmIdKW9.js +1 -0
- package/webapp/dist/assets/release-deployment-state-DRrEI2pP.js +1 -0
- package/webapp/dist/assets/release-wire-f2VuBoBF.js +1 -0
- package/webapp/dist/assets/releases-Bp3CDgmo.js +2 -0
- package/webapp/dist/assets/rolldown-runtime-CNOWq4Kh.js +1 -0
- package/webapp/dist/assets/search-input-C0MHI8vO.js +1 -0
- package/webapp/dist/assets/services-BddvcFi8.js +1 -0
- package/webapp/dist/assets/settings-B3egRvbI.js +1 -0
- package/webapp/dist/assets/settled-pair-B-wgSZl6.js +1 -0
- package/webapp/dist/assets/shared-styles-C0vN3I7_.js +1 -0
- package/webapp/dist/assets/shell-command-C3pXf1vi.js +1 -0
- package/webapp/dist/assets/sign-in-9q-Nfy83.js +1 -0
- package/webapp/dist/assets/skeleton-BmWDJWF9.js +1 -0
- package/webapp/dist/assets/snackbar-DSPmjMyi.js +1 -0
- package/webapp/dist/assets/stack-health-status-BIv2C10x.js +1 -0
- package/webapp/dist/assets/stack-start-notice-BVnMOwRQ.js +1 -0
- package/webapp/dist/assets/stacks-IZ1mfizi.js +1 -0
- package/webapp/dist/assets/storage-CKXnhqkt.js +1 -0
- package/webapp/dist/assets/store-bqUBn2A4.js +1 -0
- package/webapp/dist/assets/sync-reading-CN0jPKAg.js +1 -0
- package/webapp/dist/assets/system-info-BFKNM-hf.js +1 -0
- package/webapp/dist/assets/table-BOWT6Y9d.js +1 -0
- package/webapp/dist/assets/table-viewport-Bp4pq-c_.js +1 -0
- package/webapp/dist/assets/tabs-k5NPGLnJ.js +1 -0
- package/webapp/dist/assets/terminal-BrP-ENHg.css +1 -0
- package/webapp/dist/assets/terminal-QWBau-Us.js +62 -0
- package/webapp/dist/assets/terminal-input-b7f2VoE_.js +2 -0
- package/webapp/dist/assets/topology-CPey5dP6.js +8 -0
- package/webapp/dist/assets/topology-metadata-dialog-BivuU2y6.js +2 -0
- package/webapp/dist/assets/topology-routes.worker-BoYrIzAH.js +1 -0
- package/webapp/dist/assets/traces-By0rdfDC.js +1 -0
- package/webapp/dist/assets/use-copy-to-clipboard-BWYpToS3.js +1 -0
- package/webapp/dist/assets/use-polled-resource-B22hhr5L.js +1 -0
- package/webapp/dist/assets/use-stack-context-UkBsF-l7.js +1 -0
- package/webapp/dist/assets/webapp-BnxOVfrc.css +1 -0
- package/webapp/dist/assets/webapp-D-UhjFhS.js +35 -0
- package/webapp/dist/console-version.json +1 -0
- package/webapp/dist/index.html +54 -7
- package/dist/release/daemon-omni.d.ts +0 -41
- package/dist/release/daemon-omni.d.ts.map +0 -1
- package/dist/release/daemon-omni.js +0 -59
- package/dist/release/daemon-omni.js.map +0 -1
- package/dist/services/netron-telemetry-transport.d.ts +0 -42
- package/dist/services/netron-telemetry-transport.d.ts.map +0 -1
- package/dist/services/netron-telemetry-transport.js +0 -93
- package/dist/services/netron-telemetry-transport.js.map +0 -1
- package/webapp/dist/assets/FormControlLabel-BbSxpQwN.js +0 -1
- package/webapp/dist/assets/Grid-DizFZYue.js +0 -1
- package/webapp/dist/assets/Switch-Bfiqzx8l.js +0 -1
- package/webapp/dist/assets/alerts-DH_RVRS3.js +0 -1
- package/webapp/dist/assets/apps-B96355sz.js +0 -1
- package/webapp/dist/assets/audit-CYGtbi4k.js +0 -1
- package/webapp/dist/assets/client-BmqFExWT.js +0 -1
- package/webapp/dist/assets/constants-veKGFkC6.js +0 -1
- package/webapp/dist/assets/containers-B_TpThln.js +0 -2
- package/webapp/dist/assets/daemon-memory-CCPZ4cV_.js +0 -1
- package/webapp/dist/assets/dashboard-DHf2_65N.js +0 -1
- package/webapp/dist/assets/dashboard-builder-CWY2pjeG.js +0 -1
- package/webapp/dist/assets/deploy-release-dialog-Dp-zpSAd.js +0 -1
- package/webapp/dist/assets/deployments-tJI6O3fK.js +0 -1
- package/webapp/dist/assets/detail-B2zP1Hto.js +0 -8
- package/webapp/dist/assets/detail-CoIrwmsy.js +0 -1
- package/webapp/dist/assets/detail-DWaGaOtL.js +0 -2
- package/webapp/dist/assets/formatters-BqjL73N3.js +0 -1
- package/webapp/dist/assets/fs-Ccl3sAcp.js +0 -1
- package/webapp/dist/assets/index-Dvo0iprx.css +0 -1
- package/webapp/dist/assets/index-vMnY_4jR.js +0 -412
- package/webapp/dist/assets/logs-DTRjOfX8.js +0 -12
- package/webapp/dist/assets/maplibre-gl-DowHoZAx.js +0 -814
- package/webapp/dist/assets/metrics-UCajtdox.js +0 -2
- package/webapp/dist/assets/nodes-DuTVU0f6.js +0 -7
- package/webapp/dist/assets/pipelines-C8BeDcAl.js +0 -4
- package/webapp/dist/assets/projects-Dp_zFfLY.js +0 -1
- package/webapp/dist/assets/react-DlmbjZJV.js +0 -1
- package/webapp/dist/assets/react-apexcharts.esm-C8pn45ml.js +0 -97
- package/webapp/dist/assets/release-bits-BzTKz5fM.js +0 -1
- package/webapp/dist/assets/release-build-panel-DcW9YcXQ.js +0 -1
- package/webapp/dist/assets/releases-B4hcr1bE.js +0 -2
- package/webapp/dist/assets/rolldown-runtime-XKyaHtl9.js +0 -1
- package/webapp/dist/assets/settings-DIteZTbe.js +0 -1
- package/webapp/dist/assets/settled-pair-C7SjZuns.js +0 -1
- package/webapp/dist/assets/sign-in-D4kMBOHp.js +0 -1
- package/webapp/dist/assets/stacks-Brnf-EB7.js +0 -1
- package/webapp/dist/assets/sync-reading-C3PnJ7AX.js +0 -1
- package/webapp/dist/assets/system-info-CFDNVKkU.js +0 -1
- package/webapp/dist/assets/topology-7WeTUsDN.js +0 -12
- package/webapp/dist/assets/traces-9c3gwCN-.js +0 -1
- package/webapp/dist/assets/use-stack-context-CnA5FTHu.js +0 -1
|
@@ -1,3 +1,44 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { readProjectStacks, resolveProjectStacks } from '../config/project-stacks.js';
|
|
11
|
+
import { observeStackHealth } from '../project/stack-health.js';
|
|
12
|
+
import { LocalReleaseSelection } from '../project/local-release-selection.js';
|
|
13
|
+
import { resolveAppPath } from '../config/app-path.js';
|
|
14
|
+
import { infrastructureOwnership, ownsStackInfrastructure } from '../project/infrastructure-ownership.js';
|
|
15
|
+
import { InstalledStackResume, resumeDestinations } from '../project/installed-stack-resume.js';
|
|
16
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
17
|
+
import { diagnoseContainers } from '../project/stack-diagnosis.js';
|
|
18
|
+
import { diagnosisStatus } from '../shared/dto/stack-diagnosis.js';
|
|
19
|
+
import { buildInfraFromRequirements, requirementsFromDefinitions } from '../project/requirements-scanner.js';
|
|
20
|
+
import { randomUUID } from 'node:crypto';
|
|
21
|
+
import { NodeStackStop, stackDefinitionFingerprint } from '../project/node-stack-stop.js';
|
|
22
|
+
import { stackStopReview } from '../project/stack-stop-review.js';
|
|
23
|
+
import { inspectStackContainers, removeContainer } from '../infrastructure/container-runtime.js';
|
|
24
|
+
import { stackExecutionKey } from '../shared/dto/stack-execution.js';
|
|
25
|
+
import { publicStack } from '../project/public-stack.js';
|
|
26
|
+
import { TopologyStateStore } from '../project/topology-state.js';
|
|
27
|
+
import { TopologyControl } from '../project/topology-control.js';
|
|
28
|
+
import { artifactVolumeBindings, artifactVolumeEntries, bindArtifactVolumes } from '../release/artifact-volumes.js';
|
|
29
|
+
import { mergeInfrastructure, mergeInferredInfrastructure } from '../infrastructure/merge-config.js';
|
|
30
|
+
export { mergeInfrastructure } from '../infrastructure/merge-config.js';
|
|
31
|
+
import { currentActor, describeFailure } from './audit.service.js';
|
|
32
|
+
import { publicLaunchFailure } from '../project/launch-failure.js';
|
|
33
|
+
import { nodeApplicationNames, resolveNodePlacements, stackNodeKey } from '../project/stack-placement.js';
|
|
34
|
+
import { StackTargets } from '../project/stack-targets.js';
|
|
35
|
+
import { configurationInfo, publicStackName, validateConfigurationNames } from '../project/configuration-catalog.js';
|
|
36
|
+
import { nodePlacement, observeStack } from '../project/stack-observation.js';
|
|
37
|
+
import { getCachedBootstrapDefinition } from '../orchestrator/bootstrap-loader.js';
|
|
38
|
+
import { BootstrapMetadataStore, readBootstrapMetadata, metadataSourceChanged, TOPOLOGY_REFRESH_TIMEOUT_MS } from '../project/bootstrap-metadata.js';
|
|
39
|
+
import { appDependencies } from '../project/dependency-topology.js';
|
|
40
|
+
import { isControlPlaneWorkspace } from '../project/workspace-kind.js';
|
|
41
|
+
import { RecoveryMutation, RecoveryBusyError } from '../daemon/recovery-state.js';
|
|
1
42
|
/**
|
|
2
43
|
* ProjectService — Manages projects and their stacks
|
|
3
44
|
*
|
|
@@ -24,7 +65,7 @@ import { reportPhases } from '../project/deploy-phases.js';
|
|
|
24
65
|
import { effectiveAppName } from '../orchestrator/orchestrator.service.js';
|
|
25
66
|
import { summariseSync } from './sync-summary.js';
|
|
26
67
|
import { StackInfrastructureManager } from '../infrastructure/stack-infra-manager.js';
|
|
27
|
-
import { describeProvisioning, summariseProvisioning } from '../infrastructure/provisioning-outcome.js';
|
|
68
|
+
import { describeProvisioning, summariseProvisioning, stackServiceObservation } from '../infrastructure/provisioning-outcome.js';
|
|
28
69
|
import { waitForPostgres } from './wait-for-postgres.js';
|
|
29
70
|
import { resolveStack, resolvedConfigToEnv } from '../project/config-resolver.js';
|
|
30
71
|
import { resolveStartupOrder } from '../orchestrator/dependency-resolver.js';
|
|
@@ -34,8 +75,9 @@ import { NODE_STACK } from '../project/node-app-config.js';
|
|
|
34
75
|
import { overlayCredentials } from '../infrastructure/node-credentials.js';
|
|
35
76
|
import { readDeclaredConfig, withDeclaredConfig } from '../project/declared-config.js';
|
|
36
77
|
import { bindService } from '../infrastructure/service-binding.js';
|
|
37
|
-
import { RemoteDeployer, stackNodeToDeployTarget, withNodeCredentials, } from './remote-deployer.service.js';
|
|
78
|
+
import { RemoteDeployer, stackNodeToDeployTarget, withNodeCredentials, withDeploymentProgress, } from './remote-deployer.service.js';
|
|
38
79
|
import { withNodeLeases } from './node-deploy-lease.js';
|
|
80
|
+
import { readDeploymentProgress } from '../shared/release-reading.js';
|
|
39
81
|
import { DEFAULT_MAINTENANCE_TIMING, MaintenanceLock } from './maintenance-lock.js';
|
|
40
82
|
import { gatewayRedisDb } from '../infrastructure/gateway-redis-db.js';
|
|
41
83
|
/**
|
|
@@ -45,7 +87,21 @@ import { gatewayRedisDb } from '../infrastructure/gateway-redis-db.js';
|
|
|
45
87
|
*/
|
|
46
88
|
const MAINTENANCE_PROBE_PATHS = ['/', '/api/main/health'];
|
|
47
89
|
import { migrationOutput } from './migration-output.js';
|
|
90
|
+
import { redactDiagnosticText } from '@omnitron-dev/titan/diagnostics';
|
|
91
|
+
import { COMPILED_MIGRATION_ENTRY, localMigrationTargets, requestedMigrations, migrationDatabaseEnvironment, validateMigrationDestinations, validateMigrationOwners, orderMigrationPlacements } from './migration-target.js';
|
|
92
|
+
import { executeLocalMigration } from './migration-process.js';
|
|
93
|
+
import { expandServiceDeclarations, normalizeInfraConfig } from '../infrastructure/config-normalizer.js';
|
|
94
|
+
import { createDefaultRegistry } from '../infrastructure/presets/index.js';
|
|
95
|
+
import { readArchiveInfrastructure } from '../release/infrastructure.js';
|
|
96
|
+
import { releaseArtifactOrigins } from '../release/origin.js';
|
|
48
97
|
import { ExecutionService } from '../execution/execution.service.js';
|
|
98
|
+
class LocalReleaseStopRequired extends Error {
|
|
99
|
+
}
|
|
100
|
+
function sameLaunchCode(actual, expected) {
|
|
101
|
+
if (!expected.releaseRoot)
|
|
102
|
+
return !actual.releaseRoot;
|
|
103
|
+
return actual.bootstrap === expected.bootstrap && actual.script === expected.script && actual.releaseRoot === expected.releaseRoot;
|
|
104
|
+
}
|
|
49
105
|
/** Whether two paths name one directory — symlinks and `..` resolved. */
|
|
50
106
|
function samePath(a, b) {
|
|
51
107
|
const real = (p) => {
|
|
@@ -58,25 +114,6 @@ function samePath(a, b) {
|
|
|
58
114
|
};
|
|
59
115
|
return real(a) === real(b);
|
|
60
116
|
}
|
|
61
|
-
/**
|
|
62
|
-
* The infrastructure a stack actually gets: the ecosystem's, plus its own.
|
|
63
|
-
*
|
|
64
|
-
* Shallow for the top-level keys — a stack that declares `postgres` replaces
|
|
65
|
-
* the ecosystem's — and merged for `services`, because those are named
|
|
66
|
-
* things and a stack adding one must not remove the rest.
|
|
67
|
-
*/
|
|
68
|
-
export function mergeInfrastructure(ecosystemConfig, stackConfig) {
|
|
69
|
-
if (!stackConfig.infrastructure && !ecosystemConfig.infrastructure)
|
|
70
|
-
return undefined;
|
|
71
|
-
return {
|
|
72
|
-
...ecosystemConfig.infrastructure,
|
|
73
|
-
...stackConfig.infrastructure,
|
|
74
|
-
services: {
|
|
75
|
-
...ecosystemConfig.infrastructure?.services,
|
|
76
|
-
...stackConfig.infrastructure?.services,
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
117
|
// =============================================================================
|
|
81
118
|
// ProjectService
|
|
82
119
|
// =============================================================================
|
|
@@ -100,16 +137,93 @@ const CREDENTIALED_SERVICES = ['postgres', 'redis', 'minio'];
|
|
|
100
137
|
export class ProjectService extends EventEmitter {
|
|
101
138
|
logger;
|
|
102
139
|
orchestrator;
|
|
140
|
+
daemonStateStore;
|
|
141
|
+
recoveryRegistryDigest() { return this.registry.recoveryDigest(); }
|
|
142
|
+
/** Read-only bookkeeping; no timers are stopped and no Docker call runs. */
|
|
143
|
+
captureRecoveryState() {
|
|
144
|
+
if (this.rereading?.size || this.ownershipFrozen || this.resumingInstalled || this.startsInFlight.size || this.stopsInFlight.size || this.nodeStackStop.busy || this.reconcilingStacks ||
|
|
145
|
+
[...this.stackStates.values()].some(state => state.status === 'starting' || state.status === 'stopping')) {
|
|
146
|
+
throw new RecoveryBusyError('Project ownership is changing; a recovery snapshot is not ready');
|
|
147
|
+
}
|
|
148
|
+
return this.projectSnapshot(this.infraManager.captureRecoveryState(), this.resumeTimer !== null);
|
|
149
|
+
}
|
|
150
|
+
projectSnapshot(infrastructure, reconciling) {
|
|
151
|
+
return structuredClone({
|
|
152
|
+
projects: [...this.configRegistry], infrastructure,
|
|
153
|
+
bootstrapTopology: [...this.bootstrapTopology],
|
|
154
|
+
resumeBackoff: [...this.resumeBackoff], reconciling,
|
|
155
|
+
stacks: [...this.stackStates].map(([key, { infraService: _infra, ...state }]) => [key, state]),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
/** Capture controllers' bookkeeping, retaining native application ownership in the execution host. */
|
|
159
|
+
async prepareHandoff() {
|
|
160
|
+
if (this.rereading?.size || this.ownershipFrozen || this.resumingInstalled || this.startsInFlight.size || this.stopsInFlight.size || this.nodeStackStop.busy || this.reconcilingStacks || [...this.stackStates.values()].some(state => state.status === 'starting' || state.status === 'stopping')) {
|
|
161
|
+
throw new Error('A stack lifecycle operation is active; finish it before transferring the backend');
|
|
162
|
+
}
|
|
163
|
+
this.handoffReconciling = this.resumeTimer !== null;
|
|
164
|
+
this.ownershipFrozen = true;
|
|
165
|
+
if (this.resumeTimer)
|
|
166
|
+
clearInterval(this.resumeTimer);
|
|
167
|
+
this.resumeTimer = null;
|
|
168
|
+
try {
|
|
169
|
+
const infrastructure = await this.infraManager.prepareHandoff();
|
|
170
|
+
return this.projectSnapshot(infrastructure, this.handoffReconciling);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
this.resumeAfterHandoff();
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
resumeAfterHandoff() {
|
|
178
|
+
this.ownershipFrozen = false;
|
|
179
|
+
this.infraManager.resumeAfterHandoff();
|
|
180
|
+
if (this.handoffReconciling)
|
|
181
|
+
this.startEnabledStacksReconciler();
|
|
182
|
+
this.handoffReconciling = false;
|
|
183
|
+
}
|
|
184
|
+
async adoptHandoff(snapshot) {
|
|
185
|
+
if (this.stackStates.size)
|
|
186
|
+
throw new Error('Project runtime is not empty before adoption');
|
|
187
|
+
for (const [name, project] of snapshot.projects) {
|
|
188
|
+
const registered = this.registry.get(name);
|
|
189
|
+
if (!registered)
|
|
190
|
+
throw new Error(`Project disappeared during backend transfer: ${name}`);
|
|
191
|
+
this.configRegistry.set(name, project);
|
|
192
|
+
}
|
|
193
|
+
for (const [key, definition] of snapshot.bootstrapTopology ?? [])
|
|
194
|
+
this.bootstrapTopology.set(key, definition);
|
|
195
|
+
await this.infraManager.adoptHandoff(snapshot.infrastructure);
|
|
196
|
+
for (const [key, value] of snapshot.resumeBackoff)
|
|
197
|
+
this.resumeBackoff.set(key, value);
|
|
198
|
+
this.handoffReconciling = snapshot.reconciling;
|
|
199
|
+
this.ownershipFrozen = true;
|
|
200
|
+
for (const [key, state] of snapshot.stacks) {
|
|
201
|
+
this.stackStates.set(key, { ...state, infraService: this.infraManager.getInstance(state.project, state.stack) });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
103
204
|
registry;
|
|
205
|
+
topologyControl;
|
|
206
|
+
bootstrapTopology = new Map();
|
|
207
|
+
bootstrapMetadata;
|
|
104
208
|
configRegistry = new Map();
|
|
105
209
|
stackStates = new Map();
|
|
106
210
|
infraManager;
|
|
211
|
+
nodeStackStop;
|
|
212
|
+
clusterStop;
|
|
213
|
+
installedResume;
|
|
214
|
+
coordinatingResume = false;
|
|
215
|
+
get resumingInstalled() { return this.coordinatingResume || this.installedResume?.busy === true; }
|
|
216
|
+
hostedInfrastructure = () => undefined;
|
|
217
|
+
drainHostedProvisioning = async () => { };
|
|
218
|
+
stopsInFlight = new Map();
|
|
107
219
|
slaveConnector = null;
|
|
108
220
|
deployer;
|
|
109
221
|
// Enabled-stacks reconciler state (see startEnabledStacksReconciler)
|
|
110
222
|
resumeTimer = null;
|
|
111
223
|
resumeBackoff = new Map();
|
|
112
|
-
|
|
224
|
+
reconcilingStacks = false;
|
|
225
|
+
handoffReconciling = false;
|
|
226
|
+
ownershipFrozen = false;
|
|
113
227
|
syncService;
|
|
114
228
|
secrets;
|
|
115
229
|
audit;
|
|
@@ -127,6 +241,7 @@ export class ProjectService extends EventEmitter {
|
|
|
127
241
|
super();
|
|
128
242
|
this.logger = logger;
|
|
129
243
|
this.orchestrator = orchestrator;
|
|
244
|
+
this.daemonStateStore = daemonStateStore;
|
|
130
245
|
this.syncService = options.syncService;
|
|
131
246
|
this.secrets = options.secrets;
|
|
132
247
|
this.audit = options.audit;
|
|
@@ -135,11 +250,237 @@ export class ProjectService extends EventEmitter {
|
|
|
135
250
|
// pid-lock, etc.). The registry itself is unchanged in API;
|
|
136
251
|
// only its backing storage moved.
|
|
137
252
|
this.registry = new ProjectRegistry(daemonStateStore);
|
|
253
|
+
this.bootstrapMetadata = new BootstrapMetadataStore(daemonStateStore);
|
|
254
|
+
this.topologyControl = new TopologyControl(new TopologyStateStore(daemonStateStore), {
|
|
255
|
+
configuration: async (scope) => {
|
|
256
|
+
const config = await this.loadProjectConfig(scope.project);
|
|
257
|
+
const stack = this.resolveStacks(config, scope.project)[scope.stack];
|
|
258
|
+
if (!stack)
|
|
259
|
+
throw new Error(`Stack '${scope.stack}' not found`);
|
|
260
|
+
return { config, stack, apps: this.resolveStackApps(stack, config), userStacks: this.loadUserStacks(scope.project) };
|
|
261
|
+
},
|
|
262
|
+
observation: scope => this.withRemoteAppStatuses(scope.project, this.getStack(scope.project, scope.stack)),
|
|
263
|
+
dependencies: scope => this.getTopology(scope),
|
|
264
|
+
nodes: () => daemonStateStore.selectNodesSync().map(node => ({
|
|
265
|
+
id: node.id, name: node.name, host: node.host, port: node.port,
|
|
266
|
+
})),
|
|
267
|
+
});
|
|
138
268
|
this.infraManager = new StackInfrastructureManager(logger);
|
|
269
|
+
this.nodeStackStop = new NodeStackStop(daemonStateStore, {
|
|
270
|
+
execution: orchestrator,
|
|
271
|
+
definition: (scope, cached) => this.stopDefinition(scope, cached),
|
|
272
|
+
containers: scope => inspectStackContainers(scope),
|
|
273
|
+
disableResume: scope => this.registry.setStackEnabled(scope.project, scope.stack, false),
|
|
274
|
+
retireInfrastructure: async (scope) => {
|
|
275
|
+
await this.drainHostedProvisioning(scope);
|
|
276
|
+
await this.hostedInfrastructure()?.retireStack(scope);
|
|
277
|
+
await this.infraManager.getInstance(scope.project, scope.stack)?.retireStack(scope);
|
|
278
|
+
},
|
|
279
|
+
removeContainer: id => removeContainer(id, { graceful: true }),
|
|
280
|
+
});
|
|
139
281
|
// The deployer reaches nodes through the same SSH implementation as the
|
|
140
282
|
// health checks, which is the only one that can present a password or a
|
|
141
283
|
// key passphrase — see the note at the top of `remote-deployer.service.ts`.
|
|
142
284
|
this.deployer = new RemoteDeployer(logger, new ExecutionService(logger));
|
|
285
|
+
this.deployer.onProgress(event => {
|
|
286
|
+
if (event.project && event.stack)
|
|
287
|
+
this.emit('stack:deploy_progress', event.project, event.stack, event);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
setHostedInfrastructure(get, drain) {
|
|
291
|
+
this.hostedInfrastructure = get;
|
|
292
|
+
this.drainHostedProvisioning = drain;
|
|
293
|
+
}
|
|
294
|
+
/** Authority needed by an explicit start, including infrastructure provisioning. */
|
|
295
|
+
assertStartAuthority(scope, stack) {
|
|
296
|
+
if (this.clusterStop && (stack.type !== 'local' || stack.managedOrigin)) {
|
|
297
|
+
throw new Error('Deploying this allocation requires a recipient-fenced deployment adapter. To recover retained applications after a confirmed stop, use stack resume.');
|
|
298
|
+
}
|
|
299
|
+
this.assertStackMutable(stack.managedOrigin ?? scope);
|
|
300
|
+
}
|
|
301
|
+
assertStackMutable(scope) {
|
|
302
|
+
this.assertInfrastructureMutable(scope);
|
|
303
|
+
}
|
|
304
|
+
assertInfrastructureMutable(scope) {
|
|
305
|
+
if (this.clusterStop)
|
|
306
|
+
throw new Error('Direct cluster lifecycle changes require recipient-fenced authority. Use a reviewed stack stop or resume the installed applications.');
|
|
307
|
+
this.nodeStackStop.assertMutable(scope);
|
|
308
|
+
}
|
|
309
|
+
setClusterStop(recipient, election) {
|
|
310
|
+
this.nodeStackStop.setRecipient(recipient);
|
|
311
|
+
this.clusterStop = { recipient, election };
|
|
312
|
+
this.installedResume = new InstalledStackResume(recipient, this.nodeStackStop, this.daemonStateStore, this.orchestrator, async (scope) => {
|
|
313
|
+
const { runtime, fingerprint } = await this.stopDefinition({ ...scope, applications: true }, true);
|
|
314
|
+
if (!runtime)
|
|
315
|
+
throw new Error('No installed application allocation is bound to this stack');
|
|
316
|
+
const config = this.getLoadedConfig(runtime.project);
|
|
317
|
+
const stack = this.resolveStacks(config, runtime.project)[runtime.stack];
|
|
318
|
+
if (ownsStackInfrastructure(stack))
|
|
319
|
+
throw new Error('Installed recovery requires application-only infrastructure ownership');
|
|
320
|
+
const order = resolveStartupOrder(this.resolveStackApps(stack, config)).flat().map(app => app.name);
|
|
321
|
+
return { runtime, fingerprint, order };
|
|
322
|
+
}, (runtime, receipt) => {
|
|
323
|
+
const state = this.stackStates.get(stackExecutionKey(runtime));
|
|
324
|
+
if (state) {
|
|
325
|
+
state.status = receipt.outcome === 'running' ? 'running' : 'error';
|
|
326
|
+
state.startedAt = receipt.outcome === 'running' ? Date.parse(receipt.observedAt) : null;
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
resumeRecipient() {
|
|
331
|
+
if (!this.installedResume)
|
|
332
|
+
throw new Error('This node is not enrolled for signed application recovery');
|
|
333
|
+
if (this.ownershipFrozen || this.reconcilingStacks || this.startsInFlight.size || this.stopsInFlight.size || this.nodeStackStop.busy) {
|
|
334
|
+
throw new Error('A stack operation or backend transfer is in progress');
|
|
335
|
+
}
|
|
336
|
+
return this.installedResume;
|
|
337
|
+
}
|
|
338
|
+
prepareNodeInstalledResume(request) { return this.resumeRecipient().prepare(request); }
|
|
339
|
+
executeNodeInstalledResume(request) { return this.resumeRecipient().execute(request); }
|
|
340
|
+
getNodeInstalledResumeReceipt(request) {
|
|
341
|
+
if (!this.installedResume)
|
|
342
|
+
throw new Error('This node is not enrolled for signed application recovery');
|
|
343
|
+
return this.installedResume.receipt(request);
|
|
344
|
+
}
|
|
345
|
+
async resumeInstalledStack(project, stack) {
|
|
346
|
+
const scope = { project, stack }, key = stackExecutionKey(scope);
|
|
347
|
+
const cluster = this.clusterStop;
|
|
348
|
+
if (!cluster?.election?.isLeader)
|
|
349
|
+
throw new Error('Installed stack recovery requires the elected controller');
|
|
350
|
+
const election = cluster.election;
|
|
351
|
+
this.resumeRecipient();
|
|
352
|
+
if (this.resumingInstalled)
|
|
353
|
+
throw new Error('Another installed allocation is resuming');
|
|
354
|
+
this.coordinatingResume = true;
|
|
355
|
+
try {
|
|
356
|
+
const stop = this.daemonStateStore.kvGetSync(`stack:${key}:last-stop`, { strict: true });
|
|
357
|
+
if (stop?.phase !== 'stopped' || stop.supersededAt || !stop.intentCommitted || !stop.nodes.length) {
|
|
358
|
+
throw new Error('Review and complete a signed stop on every recipient before installed recovery');
|
|
359
|
+
}
|
|
360
|
+
const config = await this.loadProjectConfig(project);
|
|
361
|
+
const declaration = this.resolveStacks(config, project)[stack];
|
|
362
|
+
if (!declaration)
|
|
363
|
+
throw new Error('Registered stack configuration is unavailable');
|
|
364
|
+
const apps = this.resolveStackApps(declaration, config);
|
|
365
|
+
const source = declaration.managedOrigin ?? scope;
|
|
366
|
+
const operationId = randomUUID();
|
|
367
|
+
const placements = declaration.type === 'local'
|
|
368
|
+
? [{ node: undefined, apps }]
|
|
369
|
+
: resolveNodePlacements(declaration, apps);
|
|
370
|
+
if (declaration.type === 'local' && !declaration.managedOrigin)
|
|
371
|
+
throw new Error('Local recovery requires an explicitly bound allocation');
|
|
372
|
+
const targets = placements.map(placement => {
|
|
373
|
+
const { node } = placement;
|
|
374
|
+
const nodeId = node ? node.nodeId : cluster.recipient.audience.nodeId;
|
|
375
|
+
const label = node ? stackNodeKey(node.host, node.port) : 'local';
|
|
376
|
+
const stopped = stop.nodes.find(item => item.authority?.nodeId === nodeId &&
|
|
377
|
+
// Older stop journals used unbracketed IPv6 endpoint strings.
|
|
378
|
+
(item.node === label || (node && item.node === `${node.host}:${node.port ?? 9700}`)));
|
|
379
|
+
if (!nodeId || !placement.apps.length || !stopped?.observedAt || !stopped.operationId || stopped.outcome !== 'stopped' ||
|
|
380
|
+
stopped.authority?.clusterId !== cluster.recipient.audience.clusterId) {
|
|
381
|
+
throw new Error(`Target '${label}' lacks a matching signed stop for the application placement`);
|
|
382
|
+
}
|
|
383
|
+
const request = { ...source, audience: { clusterId: stopped.authority.clusterId, nodeId },
|
|
384
|
+
operationId, stopOperationId: stopped.operationId, stopObservedAt: stopped.observedAt, apps: placement.apps.map(app => app.name) };
|
|
385
|
+
if (!node)
|
|
386
|
+
return { node: label, request, prepare: () => this.prepareNodeInstalledResume(request),
|
|
387
|
+
execute: (plan, permit) => this.executeNodeInstalledResume({ ...request, plan, permit }),
|
|
388
|
+
receipt: async () => this.getNodeInstalledResumeReceipt(request) };
|
|
389
|
+
const connector = this.slaveConnector;
|
|
390
|
+
if (!connector)
|
|
391
|
+
throw new Error('Cluster node RPC is unavailable');
|
|
392
|
+
const rpc = (method, input, read = false) => connector.invokeOnSlave(node.host, node.port ?? 9700, 'OmnitronProject', method, [input], { retryOnDisconnect: read });
|
|
393
|
+
return { node: label, request,
|
|
394
|
+
prepare: () => rpc('prepareNodeInstalledResume', request, true),
|
|
395
|
+
execute: (plan, permit) => rpc('executeNodeInstalledResume', { ...request, plan, permit }),
|
|
396
|
+
receipt: () => rpc('getNodeInstalledResumeReceipt', request, true) };
|
|
397
|
+
});
|
|
398
|
+
if (targets.length !== stop.nodes.length || new Set(targets.map(t => t.request.audience.nodeId)).size !== targets.length) {
|
|
399
|
+
throw new Error('Placement differs from the last confirmed stop');
|
|
400
|
+
}
|
|
401
|
+
const report = { ...scope, operationId, stopOperationId: stop.id, outcome: 'unconfirmed', nodes: [] };
|
|
402
|
+
const journal = `stack:${key}:last-resume`;
|
|
403
|
+
const previous = this.daemonStateStore.kvGetSync(journal, { strict: true });
|
|
404
|
+
if (previous?.stopOperationId === stop.id)
|
|
405
|
+
throw new Error('Recovery was already attempted. Inspect its receipts and obtain a fresh signed stop before retrying.');
|
|
406
|
+
let targetsRemembered = false;
|
|
407
|
+
const result = await resumeDestinations(report, targets, plan => election.authorizeEffect(plan), progress => {
|
|
408
|
+
if (!targetsRemembered && declaration.type !== 'local') {
|
|
409
|
+
this.rememberStackTargets(project, stack, declaration, config);
|
|
410
|
+
targetsRemembered = true;
|
|
411
|
+
}
|
|
412
|
+
this.daemonStateStore.kvUpdateSync(journal, current => {
|
|
413
|
+
if (current?.stopOperationId === stop.id && current.operationId !== operationId)
|
|
414
|
+
throw new Error('A different controller already attempted this stop');
|
|
415
|
+
return progress;
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
this.stackStates.set(key, { project, stack, config: declaration, status: result.outcome === 'running' ? 'running' : 'error',
|
|
419
|
+
startedAt: result.outcome === 'running' ? Date.now() : null, infraService: null });
|
|
420
|
+
if (result.outcome === 'running')
|
|
421
|
+
this.markPriorStopSuperseded(project, stack, stop.id);
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
424
|
+
finally {
|
|
425
|
+
this.coordinatingResume = false;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
async stopDefinition(scope, cached = false) {
|
|
429
|
+
let allocation;
|
|
430
|
+
let unbound = false;
|
|
431
|
+
// Only registrations owned by this project can claim one of its allocations.
|
|
432
|
+
const registrations = this.registry.get(scope.project) ? this.registry.registrations(scope.project) : [];
|
|
433
|
+
for (const registration of registrations) {
|
|
434
|
+
const config = cached ? this.getLoadedConfig(registration.name) : await this.loadProjectConfig(registration.name);
|
|
435
|
+
if (!config)
|
|
436
|
+
throw new Error(`Configuration '${registration.name}' is unavailable for inspection; refresh its declarations`);
|
|
437
|
+
for (const [name, stack] of Object.entries(this.resolveStacks(config, registration.name))) {
|
|
438
|
+
if (stack.type !== 'local')
|
|
439
|
+
continue;
|
|
440
|
+
if (!stack.managedOrigin) {
|
|
441
|
+
unbound = true;
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
if (stack.managedOrigin.project !== scope.project || stack.managedOrigin.stack !== scope.stack)
|
|
445
|
+
continue;
|
|
446
|
+
if (allocation)
|
|
447
|
+
throw new Error('Ambiguous node allocation ownership');
|
|
448
|
+
if (!scope.applications)
|
|
449
|
+
throw new Error('Node owns an application allocation outside the requested placement; reconcile the topology');
|
|
450
|
+
const runtime = { project: registration.name, stack: name };
|
|
451
|
+
allocation = { runtime, fingerprint: stackDefinitionFingerprint([runtime.project, name, stack, config.apps]) };
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (!allocation && (scope.applications || unbound))
|
|
455
|
+
throw new Error(`Node allocation '${scope.project}/${scope.stack}' has no verified source-stack binding; bind its managedOrigin before remote stop`);
|
|
456
|
+
return allocation ?? { runtime: null, fingerprint: stackDefinitionFingerprint([scope.project, scope.stack, false]) };
|
|
457
|
+
}
|
|
458
|
+
inspectNodeStackStop(scope) {
|
|
459
|
+
return this.nodeStackStop.inspect(scope);
|
|
460
|
+
}
|
|
461
|
+
async prepareNodeStackStop(scope) {
|
|
462
|
+
if (this.resumingInstalled)
|
|
463
|
+
throw new Error('An installed allocation is resuming');
|
|
464
|
+
if (this.ownershipFrozen)
|
|
465
|
+
throw new Error('Project ownership is frozen for backend transfer');
|
|
466
|
+
if (this.startsInFlight.size || this.stopsInFlight.size)
|
|
467
|
+
throw new Error('A stack lifecycle operation is active; retry when it finishes');
|
|
468
|
+
return this.nodeStackStop.prepare(scope);
|
|
469
|
+
}
|
|
470
|
+
async executeNodeStackStop(scope) {
|
|
471
|
+
if (this.resumingInstalled)
|
|
472
|
+
throw new Error('An installed allocation is resuming');
|
|
473
|
+
if (this.ownershipFrozen)
|
|
474
|
+
throw new Error('Project ownership is frozen for backend transfer');
|
|
475
|
+
const receipt = await this.nodeStackStop.execute(scope);
|
|
476
|
+
if (receipt.applications) {
|
|
477
|
+
const state = this.stackStates.get(stackExecutionKey(receipt.applications));
|
|
478
|
+
if (state) {
|
|
479
|
+
state.status = 'stopped';
|
|
480
|
+
state.startedAt = null;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return receipt;
|
|
143
484
|
}
|
|
144
485
|
/**
|
|
145
486
|
* Adopt the daemon's mesh connector.
|
|
@@ -175,7 +516,7 @@ export class ProjectService extends EventEmitter {
|
|
|
175
516
|
const declared = stackNodeToDeployTarget(node);
|
|
176
517
|
if (!this.resolveNodeCredentials)
|
|
177
518
|
return declared;
|
|
178
|
-
const registered = await this.resolveNodeCredentials(node.host
|
|
519
|
+
const registered = await this.resolveNodeCredentials(node.host, node.port ?? 9700, node.nodeId);
|
|
179
520
|
return withNodeCredentials(declared, registered);
|
|
180
521
|
}
|
|
181
522
|
/**
|
|
@@ -213,17 +554,99 @@ export class ProjectService extends EventEmitter {
|
|
|
213
554
|
* `deploying` from the first second to the last, and every event that said
|
|
214
555
|
* WHERE it was reached a handler that re-emitted it to nobody.
|
|
215
556
|
*/
|
|
216
|
-
getDeployProgress() {
|
|
217
|
-
|
|
557
|
+
getDeployProgress(scope) {
|
|
558
|
+
const records = this.deployer.getProgress();
|
|
559
|
+
if (!scope)
|
|
560
|
+
return records;
|
|
561
|
+
const configurations = this.registry.registrations(scope.project).map(configurationInfo);
|
|
562
|
+
return readDeploymentProgress(records, { name: scope.project, configurations }, scope.stack).transfers;
|
|
218
563
|
}
|
|
219
564
|
listProjects() {
|
|
220
|
-
|
|
565
|
+
const revision = this.registry.catalogRevision();
|
|
566
|
+
return this.registry.list().filter(project => !isControlPlaneWorkspace(project.path)).map(project => {
|
|
567
|
+
const records = this.registry.registrations(project.name);
|
|
568
|
+
const infos = records.map(record => this.toProjectInfo(record));
|
|
569
|
+
return {
|
|
570
|
+
...this.toProjectInfo(project),
|
|
571
|
+
enabledStacks: records.flatMap(record => (record.enabledStacks ?? []).map(stack => publicStackName(record, stack))),
|
|
572
|
+
runningStacks: infos.reduce((count, info) => count + info.runningStacks, 0),
|
|
573
|
+
totalStacks: infos.reduce((count, info) => count + info.totalStacks, 0),
|
|
574
|
+
configurations: records.map(configurationInfo),
|
|
575
|
+
catalogRevision: revision,
|
|
576
|
+
};
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
/** Source inventory for execution, backup and recovery; not the public project catalogue. */
|
|
580
|
+
listProjectRegistrations() {
|
|
581
|
+
return this.registry.listRegistrations().filter(project => !isControlPlaneWorkspace(project.path)).map(project => this.toProjectInfo(project));
|
|
582
|
+
}
|
|
583
|
+
projectConfigurations(project) {
|
|
584
|
+
return this.registry.registrations(project);
|
|
585
|
+
}
|
|
586
|
+
async resolveProjectStack(scope, options = {}) {
|
|
587
|
+
const resolved = this.registry.resolveStack(scope);
|
|
588
|
+
if (resolved.project !== scope.project) {
|
|
589
|
+
const config = options.readOnly ? this.getLoadedConfig(scope.project) : await this.ensureConfig(scope.project);
|
|
590
|
+
if (!config)
|
|
591
|
+
throw new Error('Project configuration is not loaded; reload it before resolving imported stack diagnostics');
|
|
592
|
+
if (Object.hasOwn(this.resolveStacks(config, scope.project), scope.stack))
|
|
593
|
+
throw new Error(`Ambiguous stack '${scope.stack}': both default and imported configurations declare it`);
|
|
594
|
+
}
|
|
595
|
+
return { ...scope, ...resolved };
|
|
596
|
+
}
|
|
597
|
+
publicStackInfo(project, source, info) {
|
|
598
|
+
return {
|
|
599
|
+
...publicStack(info),
|
|
600
|
+
name: project === source.name ? info.name : publicStackName(source, info.name),
|
|
601
|
+
runtimeScope: { project: source.name, stack: info.name },
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
async importProjectConfiguration(input) {
|
|
605
|
+
validateConfigurationNames(input?.name, input?.stacks);
|
|
606
|
+
if (typeof input.project !== 'string' || typeof input.path !== 'string' || typeof input.expectedRevision !== 'string')
|
|
607
|
+
throw new Error('Project, configuration path and catalogue revision are required');
|
|
608
|
+
const candidate = this.registry.prepareConfiguration(input);
|
|
609
|
+
const { loadEcosystemConfig } = await import('../config/loader.js');
|
|
610
|
+
const source = await loadEcosystemConfig(candidate.path);
|
|
611
|
+
const sourceStacks = this.resolveStacks(source, candidate);
|
|
612
|
+
const declared = Object.keys(sourceStacks);
|
|
613
|
+
if (declared.length !== Object.keys(input.stacks).length || declared.some(name => !Object.hasOwn(input.stacks, name)))
|
|
614
|
+
throw new Error('Map every configured source stack explicitly; none may disappear during import');
|
|
615
|
+
const owner = await this.ensureConfig(input.project);
|
|
616
|
+
const occupied = new Set(Object.keys(this.resolveStacks(owner, input.project)));
|
|
617
|
+
for (const name of Object.values(input.stacks))
|
|
618
|
+
if (occupied.has(name))
|
|
619
|
+
throw new Error(`Stack '${name}' already exists in the project default configuration`);
|
|
620
|
+
const existing = this.registry.get(candidate.name);
|
|
621
|
+
if (existing) {
|
|
622
|
+
for (const name of existing.enabledStacks ?? [])
|
|
623
|
+
if (!input.stacks[name])
|
|
624
|
+
throw new Error(`Auto-start source stack '${name}' is missing from the mapping`);
|
|
625
|
+
for (const name of this.getRunningStacks(existing.name, true))
|
|
626
|
+
if (!input.stacks[name])
|
|
627
|
+
throw new Error(`Running source stack '${name}' is missing from the mapping`);
|
|
628
|
+
for (const name of Object.keys(this.loadUserStacks(existing.name)))
|
|
629
|
+
if (!input.stacks[name])
|
|
630
|
+
throw new Error(`User-defined source stack '${name}' is missing from the mapping`);
|
|
631
|
+
}
|
|
632
|
+
const record = this.registry.importConfiguration(input);
|
|
633
|
+
this.configRegistry.set(record.name, { config: source, loadedAt: Date.now() });
|
|
634
|
+
await this.audit?.record({ action: 'project.configuration.import', resourceType: 'project', resourceId: input.project,
|
|
635
|
+
details: { configuration: input.name, runtimeProject: record.name, stacks: input.stacks } });
|
|
636
|
+
this.emit('project:updated', input.project, {});
|
|
637
|
+
return this.getProject(input.project);
|
|
638
|
+
}
|
|
639
|
+
async detachProjectConfiguration(data) {
|
|
640
|
+
this.registry.detachConfiguration(data.project, data.name, data.expectedRevision);
|
|
641
|
+
await this.audit?.record({ action: 'project.configuration.detach', resourceType: 'project', resourceId: data.project,
|
|
642
|
+
details: { configuration: data.name } });
|
|
643
|
+
this.emit('project:updated', data.project, {});
|
|
221
644
|
}
|
|
222
645
|
getProject(name) {
|
|
223
646
|
const project = this.registry.get(name);
|
|
224
647
|
if (!project)
|
|
225
648
|
throw new Error(`Project '${name}' not found`);
|
|
226
|
-
return this.
|
|
649
|
+
return this.listProjects().find(record => record.name === (project.configuration?.project ?? name));
|
|
227
650
|
}
|
|
228
651
|
async addProject(name, projectPath) {
|
|
229
652
|
// Already registered AT THIS PATH: read it again rather than refuse.
|
|
@@ -257,19 +680,32 @@ export class ProjectService extends EventEmitter {
|
|
|
257
680
|
* running apps were redefined.
|
|
258
681
|
*/
|
|
259
682
|
async rereadProject(name) {
|
|
683
|
+
if (this.ownershipFrozen || this.rereading?.has(name) || [...this.startsInFlight.keys()].some(key => key.startsWith(name + '/')))
|
|
684
|
+
throw new Error('Project launch definitions are changing; retry after the current operation');
|
|
685
|
+
(this.rereading ??= new Set()).add(name);
|
|
686
|
+
try {
|
|
687
|
+
return await this.rereadProjectOnce(name);
|
|
688
|
+
}
|
|
689
|
+
finally {
|
|
690
|
+
this.rereading.delete(name);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
async rereadProjectOnce(name) {
|
|
260
694
|
this.configRegistry.delete(name);
|
|
261
695
|
const config = await this.loadProjectConfig(name);
|
|
262
696
|
const stacks = this.resolveStacks(config, name);
|
|
263
697
|
let redefined = 0;
|
|
264
698
|
for (const [key, state] of this.stackStates) {
|
|
265
|
-
if (!key.startsWith(`${name}/`) || state.status === 'stopped' || state.status === 'error')
|
|
699
|
+
if (!key.startsWith(`${name}/`) || state.status === 'stopped' || (state.status === 'error' && state.startedAt === null))
|
|
266
700
|
continue;
|
|
267
701
|
const stackConfig = stacks[state.stack];
|
|
268
702
|
// A remote stack's apps run on its nodes; they are redefined there.
|
|
269
703
|
if (!stackConfig || stackConfig.type !== 'local')
|
|
270
704
|
continue;
|
|
271
|
-
const
|
|
272
|
-
|
|
705
|
+
const entries = await this.launchEntries(name, state.stack, stackConfig, config);
|
|
706
|
+
if (entries.some(entry => entry.releaseRoot) && !stackConfig.managedOrigin)
|
|
707
|
+
await this.checkLocalOwner(name, state.stack, entries, false);
|
|
708
|
+
const definitions = await this.loadAppDefinitions(name, stackConfig, config, entries);
|
|
273
709
|
const stackEnv = await this.stackEnvFor(name, state.stack, stackConfig, entries.map((e) => e.name));
|
|
274
710
|
const build = this.stackEntryBuilder(name, state.stack, stackConfig, config, definitions, stackEnv);
|
|
275
711
|
for (const entry of entries) {
|
|
@@ -284,6 +720,17 @@ export class ProjectService extends EventEmitter {
|
|
|
284
720
|
}
|
|
285
721
|
/** Refresh only local container monitoring from the current validated project config. */
|
|
286
722
|
async refreshInfrastructureOwnership(projectName, stackName) {
|
|
723
|
+
if (this.ownershipFrozen || this.resumingInstalled || this.startsInFlight.size || this.stopsInFlight.size || this.reconcilingStacks)
|
|
724
|
+
throw new Error('Project lifecycle ownership is changing; retry the infrastructure refresh after it settles');
|
|
725
|
+
this.ownershipFrozen = true;
|
|
726
|
+
try {
|
|
727
|
+
return await this.refreshInfrastructureOwnershipOnce(projectName, stackName);
|
|
728
|
+
}
|
|
729
|
+
finally {
|
|
730
|
+
this.ownershipFrozen = false;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
async refreshInfrastructureOwnershipOnce(projectName, stackName) {
|
|
287
734
|
const project = this.registry.get(projectName);
|
|
288
735
|
if (!project)
|
|
289
736
|
throw new Error(`Project '${projectName}' not found`);
|
|
@@ -292,13 +739,21 @@ export class ProjectService extends EventEmitter {
|
|
|
292
739
|
const stack = this.resolveStacks(config, projectName)[stackName];
|
|
293
740
|
if (!stack || stack.type !== 'local')
|
|
294
741
|
throw new Error('Ownership refresh requires a declared local stack');
|
|
295
|
-
|
|
742
|
+
if (!ownsStackInfrastructure(stack)) {
|
|
743
|
+
const released = await this.infraManager.refreshOwnership(projectName, stackName, stack, {}, undefined, project.path);
|
|
744
|
+
const state = this.stackStates.get(`${projectName}/${stackName}`);
|
|
745
|
+
if (state) {
|
|
746
|
+
state.config = stack;
|
|
747
|
+
state.infraService = null;
|
|
748
|
+
}
|
|
749
|
+
return released;
|
|
750
|
+
}
|
|
751
|
+
const entries = await this.launchEntries(projectName, stackName, stack, config);
|
|
752
|
+
const definitions = await this.loadAppDefinitions(projectName, stack, config, entries);
|
|
296
753
|
// The same requirements merge used at stack start. A failed fresh scan must
|
|
297
754
|
// refuse a handoff rather than silently releasing auto-detected services.
|
|
298
|
-
const
|
|
299
|
-
const
|
|
300
|
-
const explicit = mergeInfrastructure(config, stack);
|
|
301
|
-
const effective = { ...auto, ...explicit, services: { ...explicit?.services } };
|
|
755
|
+
const auto = buildInfraFromRequirements(requirementsFromDefinitions(definitions));
|
|
756
|
+
const effective = mergeInferredInfrastructure(auto, mergeInfrastructure(config, stack));
|
|
302
757
|
return this.infraManager.refreshOwnership(projectName, stackName, stack, effective, definitions, project.path);
|
|
303
758
|
}
|
|
304
759
|
updateProject(name, data) {
|
|
@@ -308,7 +763,7 @@ export class ProjectService extends EventEmitter {
|
|
|
308
763
|
if (data.path) {
|
|
309
764
|
const nextPath = path.resolve(data.path);
|
|
310
765
|
if (nextPath !== path.resolve(project.path)) {
|
|
311
|
-
const running = this.getRunningStacks(name);
|
|
766
|
+
const running = this.getRunningStacks(name, true);
|
|
312
767
|
if (running.length > 0) {
|
|
313
768
|
throw new Error(`Project '${name}' has running stacks: ${running.join(', ')}. Stop them before changing its path.`);
|
|
314
769
|
}
|
|
@@ -324,7 +779,7 @@ export class ProjectService extends EventEmitter {
|
|
|
324
779
|
}
|
|
325
780
|
removeProject(name) {
|
|
326
781
|
// Stop all running stacks for this project first
|
|
327
|
-
const runningStacks = this.getRunningStacks(name);
|
|
782
|
+
const runningStacks = this.getRunningStacks(name, true);
|
|
328
783
|
if (runningStacks.length > 0) {
|
|
329
784
|
throw new Error(`Project '${name}' has running stacks: ${runningStacks.join(', ')}. Stop them first.`);
|
|
330
785
|
}
|
|
@@ -386,7 +841,7 @@ export class ProjectService extends EventEmitter {
|
|
|
386
841
|
// Check for duplicates in both config stacks and user stacks
|
|
387
842
|
const config = await this.loadProjectConfig(project);
|
|
388
843
|
const allStacks = this.resolveStacks(config, project);
|
|
389
|
-
if (allStacks[input.name]) {
|
|
844
|
+
if (allStacks[input.name] || this.registry.registrations(project).some(source => Object.values(source.configuration?.stacks ?? {}).includes(input.name))) {
|
|
390
845
|
throw new Error(`Stack '${input.name}' already exists`);
|
|
391
846
|
}
|
|
392
847
|
// Build stack config
|
|
@@ -394,7 +849,12 @@ export class ProjectService extends EventEmitter {
|
|
|
394
849
|
type: input.type,
|
|
395
850
|
apps: input.apps,
|
|
396
851
|
...(input.nodeIds && input.type !== 'local' && {
|
|
397
|
-
nodes: input.nodeIds.map((id) =>
|
|
852
|
+
nodes: input.nodeIds.map((id) => {
|
|
853
|
+
const node = this.daemonStateStore.selectNodesSync().find(candidate => candidate.id === id);
|
|
854
|
+
if (!node)
|
|
855
|
+
throw new Error(`Fleet node '${id}' not found; register it before creating a stack`);
|
|
856
|
+
return { nodeId: node.id, host: node.host, port: node.port, label: node.name, role: 'app' };
|
|
857
|
+
}),
|
|
398
858
|
}),
|
|
399
859
|
};
|
|
400
860
|
// For local stacks, auto-generate optimal infrastructure from app requirements
|
|
@@ -437,7 +897,6 @@ export class ProjectService extends EventEmitter {
|
|
|
437
897
|
// `pull access denied` for, while the preset named the image the
|
|
438
898
|
// resolver actually uses. A prefetch that pulls something else is
|
|
439
899
|
// worse than none — it reports success and warms nothing.
|
|
440
|
-
const { createDefaultRegistry } = await import('../infrastructure/presets/index.js');
|
|
441
900
|
const presets = createDefaultRegistry();
|
|
442
901
|
const images = new Set();
|
|
443
902
|
for (const name of ['postgres', 'redis', 'minio']) {
|
|
@@ -483,6 +942,11 @@ export class ProjectService extends EventEmitter {
|
|
|
483
942
|
if (state && state.status !== 'stopped') {
|
|
484
943
|
throw new Error(`Stack '${stackName}' is currently ${state.status}. Stop it first.`);
|
|
485
944
|
}
|
|
945
|
+
if (userStacks[stackName].type === 'local') {
|
|
946
|
+
const selection = this.localSelection(project, stackName).read();
|
|
947
|
+
if (selection.release)
|
|
948
|
+
throw new Error('Clear the selected release before deleting this stack');
|
|
949
|
+
}
|
|
486
950
|
this.deleteUserStack(project, stackName);
|
|
487
951
|
this.stackStates.delete(stateKey);
|
|
488
952
|
this.logger.info({ project, stack: stackName }, 'Stack deleted');
|
|
@@ -494,15 +958,8 @@ export class ProjectService extends EventEmitter {
|
|
|
494
958
|
throw new Error(`Project '${project}' not found`);
|
|
495
959
|
return path.join(proj.path, 'omnitron.stacks.json');
|
|
496
960
|
}
|
|
497
|
-
loadUserStacks(project) {
|
|
498
|
-
|
|
499
|
-
try {
|
|
500
|
-
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
501
|
-
return JSON.parse(raw);
|
|
502
|
-
}
|
|
503
|
-
catch {
|
|
504
|
-
return {};
|
|
505
|
-
}
|
|
961
|
+
loadUserStacks(project, filePath = this.getUserStacksPath(project)) {
|
|
962
|
+
return readProjectStacks(filePath, project);
|
|
506
963
|
}
|
|
507
964
|
saveUserStack(project, name, config) {
|
|
508
965
|
const filePath = this.getUserStacksPath(project);
|
|
@@ -519,6 +976,368 @@ export class ProjectService extends EventEmitter {
|
|
|
519
976
|
// ===========================================================================
|
|
520
977
|
// Stacks
|
|
521
978
|
// ===========================================================================
|
|
979
|
+
/** Remote deployments across projects; local process/container inventory has its own authoritative source. */
|
|
980
|
+
async getTopologyInventory() {
|
|
981
|
+
const result = {
|
|
982
|
+
generatedAt: new Date().toISOString(), deployments: [], errors: [],
|
|
983
|
+
};
|
|
984
|
+
for (const project of this.listProjectRegistrations()) {
|
|
985
|
+
const source = this.registry.get(project.name);
|
|
986
|
+
const owner = source.configuration?.project ?? project.name;
|
|
987
|
+
try {
|
|
988
|
+
await this.ensureConfig(project.name);
|
|
989
|
+
const stacks = this.listStacks(project.name).filter(stack => stack.type !== 'local');
|
|
990
|
+
for (let offset = 0; offset < stacks.length; offset += 4) {
|
|
991
|
+
const observations = await Promise.allSettled(stacks.slice(offset, offset + 4).map(stack => this.withRemoteAppStatuses(project.name, stack)));
|
|
992
|
+
observations.forEach((observation, index) => {
|
|
993
|
+
if (observation.status === 'fulfilled') {
|
|
994
|
+
result.deployments.push({ project: owner, stack: this.publicStackInfo(owner, source, observation.value) });
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
result.errors.push({
|
|
998
|
+
project: owner, stack: publicStackName(source, stacks[offset + index].name), reason: 'Node inventory could not be read',
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
catch {
|
|
1005
|
+
result.errors.push({ project: owner, reason: `Configuration '${source.configuration?.name ?? 'default'}' could not be read` });
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
return result;
|
|
1009
|
+
}
|
|
1010
|
+
async getTopologyWorkspace(scope) {
|
|
1011
|
+
return this.topologyControl.workspace(scope);
|
|
1012
|
+
}
|
|
1013
|
+
async saveTopologyDraft(data) {
|
|
1014
|
+
return this.topologyControl.saveDraft(data);
|
|
1015
|
+
}
|
|
1016
|
+
async planTopology(data) {
|
|
1017
|
+
return this.topologyControl.plan(data);
|
|
1018
|
+
}
|
|
1019
|
+
async applyTopologyPlan(data) {
|
|
1020
|
+
const result = await this.topologyControl.apply(data, currentActor());
|
|
1021
|
+
if (result.changed) {
|
|
1022
|
+
// The local FULL commit already contains intent and its receipt; this is a searchable audit copy.
|
|
1023
|
+
await this.audit?.record({
|
|
1024
|
+
action: 'topology.apply', resourceType: 'stack', resourceId: `${data.project}/${data.stack}`,
|
|
1025
|
+
details: {
|
|
1026
|
+
plan: result.plan.id, revision: result.plan.baseRevision,
|
|
1027
|
+
effect: result.plan.effect, changes: result.plan.changes,
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
this.emit('project:updated', data.project, {});
|
|
1031
|
+
}
|
|
1032
|
+
return result.plan;
|
|
1033
|
+
}
|
|
1034
|
+
/** Reads persisted launch evidence and static JSON only; never imports application code. */
|
|
1035
|
+
readAppDeclaration(project, stack, app, bootstrap) {
|
|
1036
|
+
const state = this.bootstrapMetadata?.readState(bootstrap);
|
|
1037
|
+
const saved = state?.metadata;
|
|
1038
|
+
const launched = this.orchestrator?.getHandle(ProjectService.handleKey(project, stack, app))?.bootstrapMetadata;
|
|
1039
|
+
const observed = launched?.bootstrap === bootstrap ? launched : null;
|
|
1040
|
+
const record = observed && (!saved || observed.capturedAt > saved.capturedAt) ? observed : saved;
|
|
1041
|
+
const metadata = record?.definition ?? getCachedBootstrapDefinition(bootstrap) ?? this.bootstrapTopology.get(bootstrap);
|
|
1042
|
+
const declared = readDeclaredConfig(bootstrap);
|
|
1043
|
+
return { state, record, metadata, declared, observed };
|
|
1044
|
+
}
|
|
1045
|
+
/** Inspect prerequisites without provisioning, migration, application start or bootstrap imports. */
|
|
1046
|
+
diagnosesInFlight = new Map();
|
|
1047
|
+
async diagnoseStack(projectName, stackName) {
|
|
1048
|
+
const key = JSON.stringify([projectName, stackName]);
|
|
1049
|
+
const pending = this.diagnosesInFlight.get(key);
|
|
1050
|
+
if (pending)
|
|
1051
|
+
return pending;
|
|
1052
|
+
const request = this.inspectStackReadiness(projectName, stackName);
|
|
1053
|
+
this.diagnosesInFlight.set(key, request);
|
|
1054
|
+
try {
|
|
1055
|
+
return await request;
|
|
1056
|
+
}
|
|
1057
|
+
finally {
|
|
1058
|
+
this.diagnosesInFlight.delete(key);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
async inspectStackReadiness(projectName, stackName) {
|
|
1062
|
+
const started = Date.now();
|
|
1063
|
+
const checks = [];
|
|
1064
|
+
const deferred = ['Application startup and application health', 'Database authentication, schema and migrations', 'Registry downloads and available storage capacity', 'Configuration changes made after this observation'];
|
|
1065
|
+
const done = () => ({ project: projectName, stack: stackName, checkedAt: new Date().toISOString(), durationMs: Date.now() - started, status: diagnosisStatus(checks), checks, deferred });
|
|
1066
|
+
const add = (code, resource, state, message, action) => checks.push({ code, resource, state, message, ...(action && { action }) });
|
|
1067
|
+
const project = this.registry.get(projectName);
|
|
1068
|
+
if (!project)
|
|
1069
|
+
throw new Error('Project is not registered');
|
|
1070
|
+
const loaded = this.configRegistry.get(projectName);
|
|
1071
|
+
if (!loaded) {
|
|
1072
|
+
add('configuration.unloaded', 'Project configuration', 'unknown', 'Project configuration has not been loaded.', 'Reload the trusted project configuration, then retry.');
|
|
1073
|
+
return done();
|
|
1074
|
+
}
|
|
1075
|
+
const config = loaded.config;
|
|
1076
|
+
const stack = this.resolveStacks(config, projectName)[stackName];
|
|
1077
|
+
if (!stack)
|
|
1078
|
+
throw new Error('Stack is not declared');
|
|
1079
|
+
try {
|
|
1080
|
+
this.assertStartAuthority({ project: projectName, stack: stackName }, stack);
|
|
1081
|
+
}
|
|
1082
|
+
catch (err) {
|
|
1083
|
+
add('start.authority', 'Start authority', 'blocked', err.message, 'Inspect the allocation owner and its stop operation before planning a start.');
|
|
1084
|
+
}
|
|
1085
|
+
add('configuration.loaded', 'Project configuration', 'pass', `Using daemon configuration loaded at ${new Date(loaded.loadedAt).toISOString()}.`, 'Reload project configuration after editing it.');
|
|
1086
|
+
// This is observed history, not a fresh execution of user code. Reuse the
|
|
1087
|
+
// status reader so a recovered app or an intentionally stopped stack does
|
|
1088
|
+
// not keep an obsolete launch failure in its readiness report.
|
|
1089
|
+
if (stack.type === 'local' && this.stackStates.get(`${projectName}/${stackName}`)?.appStartErrors) {
|
|
1090
|
+
const previous = this.toStackInfo(projectName, stackName, stack);
|
|
1091
|
+
for (const issue of previous.notUp ?? [])
|
|
1092
|
+
add('application.previous-start', 'Previous start', 'unknown', `${previous.startedAt ? previous.startedAt + ' · ' : ''}${issue}`, 'This records the previous start attempt; current startup remains unverified. Correct the cause and retry the stack start.');
|
|
1093
|
+
}
|
|
1094
|
+
let entries;
|
|
1095
|
+
let infrastructure = mergeInfrastructure(config, stack);
|
|
1096
|
+
try {
|
|
1097
|
+
const selected = stack.type === 'local' && !stack.managedOrigin ? this.localSelection(projectName, stackName).read().release : null;
|
|
1098
|
+
if (selected && artifactVolumeBindings(infrastructure).length) {
|
|
1099
|
+
const { loadRelease } = await import('../release/load.js');
|
|
1100
|
+
const release = await loadRelease(selected.id, await this.releaseStore());
|
|
1101
|
+
if (release.manifestSha256 !== selected.manifestSha256)
|
|
1102
|
+
throw Error('Selected release manifest changed on disk');
|
|
1103
|
+
const installed = await this.installedRelease(projectName, stackName, stack, config, release);
|
|
1104
|
+
if (!installed.infrastructure)
|
|
1105
|
+
throw Error('Selected artifact infrastructure is unavailable');
|
|
1106
|
+
entries = installed.entries;
|
|
1107
|
+
infrastructure = installed.infrastructure;
|
|
1108
|
+
}
|
|
1109
|
+
else
|
|
1110
|
+
entries = await this.launchEntries(projectName, stackName, stack, config);
|
|
1111
|
+
if (selected)
|
|
1112
|
+
add('release.selected', 'Release', 'pass', 'Next start uses verified release ' + selected.id);
|
|
1113
|
+
else if (stack.release?.mode === 'required')
|
|
1114
|
+
add('release.required', 'Release', 'unknown', 'This stack requires an immutable release; no release was selected for this check.', 'Use Deploy release and review its admission checks.');
|
|
1115
|
+
}
|
|
1116
|
+
catch (error) {
|
|
1117
|
+
add('release.unavailable', 'Selected release', 'blocked', describeFailure(error), 'Restore the pinned installation or select another verified release.');
|
|
1118
|
+
return done();
|
|
1119
|
+
}
|
|
1120
|
+
if (Array.isArray(stack.apps)) {
|
|
1121
|
+
const available = new Set(entries.map(entry => entry.name));
|
|
1122
|
+
for (const name of stack.apps)
|
|
1123
|
+
if (!available.has(name))
|
|
1124
|
+
add('application.undeclared', name, 'blocked', 'The selected application is absent or disabled in project configuration.');
|
|
1125
|
+
}
|
|
1126
|
+
if (!entries.length)
|
|
1127
|
+
add('applications.empty', 'Applications', 'blocked', 'No enabled applications are selected.');
|
|
1128
|
+
try {
|
|
1129
|
+
resolveStartupOrder(entries);
|
|
1130
|
+
add('dependencies.valid', 'Application dependencies', 'pass', 'Startup dependency order resolved.');
|
|
1131
|
+
}
|
|
1132
|
+
catch {
|
|
1133
|
+
add('dependencies.invalid', 'Application dependencies', 'blocked', 'Application dependencies cannot be ordered.', 'Check missing dependencies and dependency cycles in the project configuration.');
|
|
1134
|
+
}
|
|
1135
|
+
let migrationEnvironment = null;
|
|
1136
|
+
try {
|
|
1137
|
+
migrationEnvironment = await this.stackEnvFor(projectName, stackName, stack, entries.map(entry => entry.name));
|
|
1138
|
+
add('environment.valid', 'Environment', 'pass', 'Environment declarations and vault references resolved; values are withheld.');
|
|
1139
|
+
}
|
|
1140
|
+
catch {
|
|
1141
|
+
add('environment.invalid', 'Environment', 'blocked', 'Environment declarations or vault references could not be resolved.', 'Check stack environment variable names and required vault entries.');
|
|
1142
|
+
}
|
|
1143
|
+
const definitions = new Map();
|
|
1144
|
+
for (const entry of entries) {
|
|
1145
|
+
if (entry.releaseFrom) {
|
|
1146
|
+
add('release.import-unselected', entry.name, 'blocked', 'This application comes from a pinned producer release; no verified installation is selected.', 'Prepare and select the consumer release before checking its application prerequisites.');
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
if (!entry.bootstrap) {
|
|
1150
|
+
add('application.classic', entry.name, 'unknown', 'Executable prerequisites for this command-based application have not been probed.', 'Check the configured command and working directory before starting.');
|
|
1151
|
+
continue;
|
|
1152
|
+
}
|
|
1153
|
+
const bootstrap = resolveAppPath(project.path, entry, entry.bootstrap);
|
|
1154
|
+
if (!fs.existsSync(bootstrap))
|
|
1155
|
+
add('bootstrap.missing', entry.name, 'blocked', 'The configured bootstrap file is missing.', 'Restore the application sources or correct its bootstrap path.');
|
|
1156
|
+
const { metadata, declared, state, record } = this.readAppDeclaration(projectName, stackName, entry.name, bootstrap);
|
|
1157
|
+
if (declared.malformed)
|
|
1158
|
+
add('declaration.invalid', entry.name, 'blocked', 'Application configuration is invalid or unreadable.', 'Check config/default.json syntax and permissions.');
|
|
1159
|
+
if (!metadata || !record || metadataSourceChanged(record) || state?.failure)
|
|
1160
|
+
add('declaration.unknown', entry.name, 'unknown', state?.failure ? `Last declaration read at ${state.failure.at}: ${state.failure.reason}` : 'Current bootstrap declarations have not been verified.', 'Refresh declarations as an administrator, then run diagnosis again.');
|
|
1161
|
+
else
|
|
1162
|
+
add('declaration.current', entry.name, 'pass', `Bootstrap declaration captured at ${record.capturedAt}; source file is unchanged.`);
|
|
1163
|
+
if (metadata || declared.omnitron)
|
|
1164
|
+
definitions.set(entry.name, withDeclaredConfig({ name: entry.name, version: '', processes: [], ...metadata }, declared));
|
|
1165
|
+
}
|
|
1166
|
+
try {
|
|
1167
|
+
if (!ownsStackInfrastructure(stack)) {
|
|
1168
|
+
const mode = infrastructureOwnership(stack);
|
|
1169
|
+
const owner = mode === 'controller' ? `${stack.managedOrigin.project}/${stack.managedOrigin.stack}` : 'an external operator';
|
|
1170
|
+
add('infrastructure.delegated', 'Infrastructure ownership', 'pass', `Infrastructure provisioning and migrations belong to ${owner}; this stack starts applications only.`);
|
|
1171
|
+
add('infrastructure.external-readiness', 'Application connections', 'unknown', 'Explicit application connection settings are retained. Endpoint reachability, credentials and schema have not been verified.', 'Inspect the owning infrastructure and application health; this node will not create substitute services.');
|
|
1172
|
+
return done();
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
catch (error) {
|
|
1176
|
+
add('infrastructure.ownership-invalid', 'Infrastructure ownership', 'blocked', error.message, 'Correct the explicit ownership binding; diagnosis changes no infrastructure.');
|
|
1177
|
+
return done();
|
|
1178
|
+
}
|
|
1179
|
+
if (stack.type !== 'local') {
|
|
1180
|
+
try {
|
|
1181
|
+
resolveNodePlacements(stack, entries);
|
|
1182
|
+
add('placement.valid', 'Placement', 'pass', 'Configured node placement resolved.');
|
|
1183
|
+
}
|
|
1184
|
+
catch {
|
|
1185
|
+
add('placement.invalid', 'Placement', 'blocked', 'Node placement is incomplete or inconsistent.', 'Check selected applications, node identities and placement assignments.');
|
|
1186
|
+
}
|
|
1187
|
+
add('remote.prerequisites', 'Remote infrastructure', 'unknown', 'Host prerequisites must be inspected on each target node; local Docker is not evidence for a remote stack.', `Use omnitron infra inspect ${projectName}/${stackName} for host-service evidence and Nodes for RPC diagnostics.`);
|
|
1188
|
+
return done();
|
|
1189
|
+
}
|
|
1190
|
+
try {
|
|
1191
|
+
const effective = mergeInferredInfrastructure(buildInfraFromRequirements(requirementsFromDefinitions(definitions)), infrastructure);
|
|
1192
|
+
const migrations = requestedMigrations(expandServiceDeclarations(effective, createDefaultRegistry()));
|
|
1193
|
+
if (migrations.length) {
|
|
1194
|
+
try {
|
|
1195
|
+
const targets = await localMigrationTargets(migrations, entries, project.path, (names, files) => this.launchEntries(projectName, stackName, stack, config, names, files));
|
|
1196
|
+
if (migrationEnvironment) {
|
|
1197
|
+
const resolveMigration = this.stackEntryBuilder(projectName, stackName, stack, config, definitions, migrationEnvironment);
|
|
1198
|
+
validateMigrationDestinations(targets.map(target => {
|
|
1199
|
+
const entry = entries.find(app => app.name === target.app);
|
|
1200
|
+
return { app: target.app, database: migrationDatabaseEnvironment(target.environmentApp, resolveMigration(entry).env?.['DATABASE_URL'] ?? '', target.database, target.mode) };
|
|
1201
|
+
}));
|
|
1202
|
+
}
|
|
1203
|
+
add('migrations.entries', 'Database migration programs', 'pass', `Migration owners: ${targets.map(target => `${target.service}/${target.database} → ${target.app} (${target.mode === 'config' ? target.release : 'legacy script'})`).join('; ')}. Programs are available; database access, schema and execution remain unverified.`);
|
|
1204
|
+
}
|
|
1205
|
+
catch (error) {
|
|
1206
|
+
add('migrations.unavailable', 'Database migration programs', 'blocked', describeFailure(error), 'Restore the requested migration program or prepare a complete verified release. No database or infrastructure was changed.');
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
const declaredServices = { ...effective.services };
|
|
1210
|
+
for (const definition of definitions.values())
|
|
1211
|
+
Object.assign(declaredServices, definition.omnitronConfig?.infrastructure);
|
|
1212
|
+
for (const [service, requirement] of Object.entries(declaredServices)) {
|
|
1213
|
+
// Presets acquire their provisioning definition in the shared resolver below.
|
|
1214
|
+
if ('preset' in requirement)
|
|
1215
|
+
continue;
|
|
1216
|
+
const binding = bindService(requirement, stack.serviceOverrides?.[service]);
|
|
1217
|
+
if (binding.provisioning === 'none')
|
|
1218
|
+
add('service.unconfigured', service, 'blocked', 'This service has no provisioning definition or external endpoint.');
|
|
1219
|
+
if (binding.provisioning === 'bareMetal' || binding.provisioning === 'external')
|
|
1220
|
+
add('service.host', service, 'unknown', 'This service uses a host or external endpoint; Docker readiness does not verify it.', 'Check the service endpoint, authentication and host configuration separately.');
|
|
1221
|
+
}
|
|
1222
|
+
const mounts = artifactVolumeBindings(effective);
|
|
1223
|
+
if (mounts.length) {
|
|
1224
|
+
add('release.mounts-unselected', 'Artifact volumes', 'blocked', 'These mounts require an installed release: ' + mounts.map(([, mount]) => mount.app + ' → ' + mount.service + ':' + mount.target).join(', '), 'Prepare and select the consumer release, then run diagnosis again.');
|
|
1225
|
+
}
|
|
1226
|
+
else {
|
|
1227
|
+
const containers = await this.infraManager.planStack(projectName, stackName, stack, effective, definitions, project.path);
|
|
1228
|
+
checks.push(...await diagnoseContainers(containers));
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
catch {
|
|
1232
|
+
add('infrastructure.invalid', 'Infrastructure plan', 'blocked', 'The infrastructure declaration could not be resolved.', 'Review service presets, port mappings and service overrides. No infrastructure was changed.');
|
|
1233
|
+
}
|
|
1234
|
+
return done();
|
|
1235
|
+
}
|
|
1236
|
+
/** Configuration evidence from the launch builder; it never starts, probes or modifies applications. */
|
|
1237
|
+
async getTopology(scope) {
|
|
1238
|
+
const result = { version: 1, generatedAt: new Date().toISOString(), dependencies: [], warnings: [], coverage: [] };
|
|
1239
|
+
const projects = scope.project ? [scope.project] : this.listProjectRegistrations().map(project => project.name);
|
|
1240
|
+
for (const project of projects) {
|
|
1241
|
+
let config;
|
|
1242
|
+
try {
|
|
1243
|
+
config = await this.ensureConfig(project);
|
|
1244
|
+
}
|
|
1245
|
+
catch {
|
|
1246
|
+
result.warnings.push({ project, reason: 'Project configuration could not be read' });
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
for (const [stack, stackConfig] of Object.entries(this.resolveStacks(config, project))) {
|
|
1250
|
+
if (scope.stack && stack !== scope.stack)
|
|
1251
|
+
continue;
|
|
1252
|
+
try {
|
|
1253
|
+
const entries = this.resolveStackApps(stackConfig, config);
|
|
1254
|
+
const pinned = stackConfig.type === 'local' && !stackConfig.managedOrigin && this.localSelection(project, stack).read().release;
|
|
1255
|
+
const definitions = new Map();
|
|
1256
|
+
const directory = this.registry.get(project)?.path;
|
|
1257
|
+
for (const entry of entries) {
|
|
1258
|
+
const handle = pinned ? this.orchestrator.getHandle(ProjectService.handleKey(project, stack, entry.name)) : undefined;
|
|
1259
|
+
if (!entry.bootstrap && (!entry.releaseFrom || (handle?.entry?.script && !handle.entry.bootstrap))) {
|
|
1260
|
+
result.coverage.push({ project, stack, app: entry.name, source: 'entry' });
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
if (!directory)
|
|
1264
|
+
continue;
|
|
1265
|
+
const bootstrap = pinned ? handle?.bootstrapMetadata?.bootstrap
|
|
1266
|
+
: entry.bootstrap ? resolveAppPath(directory, entry, entry.bootstrap) : undefined;
|
|
1267
|
+
if (!bootstrap) {
|
|
1268
|
+
result.coverage.push({ project, stack, app: entry.name, source: 'missing' });
|
|
1269
|
+
result.warnings.push({ project, stack, app: entry.name, reason: pinned ? 'Owner has not reported a launch declaration for the selected release' : 'Imported application has no selected release; prepare and select it before reading topology declarations' });
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
const { state, record, metadata, declared, observed } = this.readAppDeclaration(project, stack, entry.name, bootstrap);
|
|
1273
|
+
result.coverage.push({ project, stack, app: entry.name,
|
|
1274
|
+
source: record ? record === observed ? 'launch' : 'snapshot' : metadata ? 'memory' : 'missing',
|
|
1275
|
+
...(record && { capturedAt: record.capturedAt }) });
|
|
1276
|
+
if (!metadata)
|
|
1277
|
+
result.warnings.push({ project, stack, app: entry.name, reason: 'Bootstrap metadata not loaded; refresh declarations before relying on complete dependency coverage' });
|
|
1278
|
+
if (state?.failure)
|
|
1279
|
+
result.warnings.push({ project, stack, app: entry.name, reason: state.failure.reason });
|
|
1280
|
+
if (record && metadataSourceChanged(record))
|
|
1281
|
+
result.warnings.push({ project, stack, app: entry.name, reason: 'Bootstrap file changed after declaration capture; refresh metadata to inspect the current configuration' });
|
|
1282
|
+
if (declared.malformed)
|
|
1283
|
+
result.warnings.push({ project, stack, app: entry.name, reason: 'Application configuration is malformed; file declarations are unavailable' });
|
|
1284
|
+
if (metadata || declared.omnitron)
|
|
1285
|
+
definitions.set(entry.name, withDeclaredConfig({ name: entry.name, version: '', processes: [], ...metadata }, declared));
|
|
1286
|
+
}
|
|
1287
|
+
const env = await this.stackEnvFor(project, stack, stackConfig, entries.map(entry => entry.name));
|
|
1288
|
+
const build = this.stackEntryBuilder(project, stack, stackConfig, config, definitions, env);
|
|
1289
|
+
for (const entry of entries) {
|
|
1290
|
+
if (entry.enabled === false)
|
|
1291
|
+
continue;
|
|
1292
|
+
const definition = definitions.get(entry.name);
|
|
1293
|
+
result.dependencies.push(...appDependencies(project, stack, build(entry), definition));
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
catch {
|
|
1297
|
+
result.warnings.push({ project, stack, reason: 'Launch configuration could not be resolved; dependencies are incomplete' });
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
return result;
|
|
1302
|
+
}
|
|
1303
|
+
/** Explicit administrative discovery. Imports trusted declarations in bounded child processes, never starts a stack. */
|
|
1304
|
+
async refreshTopologyMetadata(scope, deadline = Date.now() + TOPOLOGY_REFRESH_TIMEOUT_MS) {
|
|
1305
|
+
const config = await this.ensureConfig(scope.project);
|
|
1306
|
+
const directory = this.registry.get(scope.project)?.path;
|
|
1307
|
+
if (!directory)
|
|
1308
|
+
throw new Error('Project is not registered');
|
|
1309
|
+
const bootstraps = new Map();
|
|
1310
|
+
const stacks = this.resolveStacks(config, scope.project);
|
|
1311
|
+
if (scope.stack && !stacks[scope.stack])
|
|
1312
|
+
throw new Error('Stack is not configured');
|
|
1313
|
+
for (const [name, stack] of Object.entries(stacks)) {
|
|
1314
|
+
if (scope.stack && scope.stack !== name)
|
|
1315
|
+
continue;
|
|
1316
|
+
for (const app of await this.launchEntries(scope.project, name, stack, config))
|
|
1317
|
+
if (app.bootstrap)
|
|
1318
|
+
bootstraps.set(resolveAppPath(directory, app, app.bootstrap), app.name);
|
|
1319
|
+
}
|
|
1320
|
+
const result = { refreshed: 0, failures: [] };
|
|
1321
|
+
for (const [bootstrap, app] of bootstraps) {
|
|
1322
|
+
try {
|
|
1323
|
+
const remaining = Math.min(30_000, deadline - Date.now());
|
|
1324
|
+
if (remaining <= 0)
|
|
1325
|
+
throw new Error('Declaration refresh deadline exceeded; this application was not read');
|
|
1326
|
+
const { metadata: record } = await readBootstrapMetadata(bootstrap, false, remaining);
|
|
1327
|
+
this.bootstrapMetadata.save(record);
|
|
1328
|
+
this.bootstrapTopology.set(bootstrap, record.definition);
|
|
1329
|
+
result.refreshed += 1;
|
|
1330
|
+
}
|
|
1331
|
+
catch (error) {
|
|
1332
|
+
this.bootstrapMetadata.fail(bootstrap, error);
|
|
1333
|
+
this.logger.error({ project: scope.project, app, error }, 'Bootstrap metadata refresh failed; previous declaration retained');
|
|
1334
|
+
result.failures.push({ project: scope.project, app, reason: `Declaration read failed: ${publicLaunchFailure(error)}. Previous metadata was retained.` });
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
await this.audit?.record({ action: 'topology.metadata.refresh', resourceType: 'project', resourceId: scope.project,
|
|
1338
|
+
details: { stack: scope.stack, refreshed: result.refreshed, failures: result.failures.length } });
|
|
1339
|
+
return result;
|
|
1340
|
+
}
|
|
522
1341
|
listStacks(projectName) {
|
|
523
1342
|
const config = this.getLoadedConfig(projectName);
|
|
524
1343
|
if (!config)
|
|
@@ -562,18 +1381,32 @@ export class ProjectService extends EventEmitter {
|
|
|
562
1381
|
* asking for a stack that is already being started should end when it has
|
|
563
1382
|
* been started, which is what the caller meant.
|
|
564
1383
|
*/
|
|
1384
|
+
rereading;
|
|
565
1385
|
startsInFlight = new Map();
|
|
566
1386
|
async startStack(projectName, stackName, opts) {
|
|
1387
|
+
if (this.resumingInstalled)
|
|
1388
|
+
throw new Error('An installed allocation is resuming');
|
|
1389
|
+
if (this.ownershipFrozen)
|
|
1390
|
+
throw new Error('Project ownership is frozen for backend transfer');
|
|
567
1391
|
const inFlightKey = `${projectName}/${stackName}`;
|
|
1392
|
+
if (this.stopsInFlight.has(inFlightKey))
|
|
1393
|
+
throw new Error('This stack is stopping; wait for its acknowledgement');
|
|
1394
|
+
if (this.rereading?.has(projectName))
|
|
1395
|
+
throw new Error('Project definitions are being updated; retry the start');
|
|
1396
|
+
if (opts?.workingTree && opts.release)
|
|
1397
|
+
throw new Error('Select either a release or the working tree');
|
|
1398
|
+
const selection = JSON.stringify([opts?.release, opts?.workingTree === true, opts?.reinstall === true]);
|
|
568
1399
|
const running = this.startsInFlight.get(inFlightKey);
|
|
569
1400
|
if (running) {
|
|
570
1401
|
this.logger.info({ project: projectName, stack: stackName }, 'This stack is already being started — joining the run in progress');
|
|
571
|
-
|
|
1402
|
+
if (running.selection !== selection)
|
|
1403
|
+
throw new Error('Another stack start selected different launch code; retry after it completes');
|
|
1404
|
+
return running.promise;
|
|
572
1405
|
}
|
|
573
|
-
const started = this.startStackOnce(projectName, stackName, opts?.source ?? 'unknown', opts?.allowDirty === true, opts?.release, opts?.reinstall === true).finally(() => {
|
|
1406
|
+
const started = this.startStackOnce(projectName, stackName, opts?.source ?? 'unknown', opts?.allowDirty === true, opts?.release, opts?.reinstall === true, opts?.workingTree === true).finally(() => {
|
|
574
1407
|
this.startsInFlight.delete(inFlightKey);
|
|
575
1408
|
});
|
|
576
|
-
this.startsInFlight.set(inFlightKey, started);
|
|
1409
|
+
this.startsInFlight.set(inFlightKey, { selection, promise: started });
|
|
577
1410
|
return started;
|
|
578
1411
|
}
|
|
579
1412
|
/**
|
|
@@ -601,10 +1434,10 @@ export class ProjectService extends EventEmitter {
|
|
|
601
1434
|
* deploys nothing, and the reconciler re-asks a node that does not answer
|
|
602
1435
|
* every few minutes — rows about looking, not about changing anything.
|
|
603
1436
|
*/
|
|
604
|
-
async startStackOnce(projectName, stackName, source, allowDirty, releaseId, reinstall = false) {
|
|
1437
|
+
async startStackOnce(projectName, stackName, source, allowDirty, releaseId, reinstall = false, workingTree = false) {
|
|
605
1438
|
const known = { attach: false };
|
|
606
1439
|
try {
|
|
607
|
-
return await this.runStackStart(projectName, stackName, source, allowDirty, releaseId, known, reinstall);
|
|
1440
|
+
return await this.runStackStart(projectName, stackName, source, allowDirty, releaseId, known, reinstall, workingTree);
|
|
608
1441
|
}
|
|
609
1442
|
catch (err) {
|
|
610
1443
|
if (!known.attach) {
|
|
@@ -617,7 +1450,7 @@ export class ProjectService extends EventEmitter {
|
|
|
617
1450
|
details: {
|
|
618
1451
|
source,
|
|
619
1452
|
...(known.type ? { type: known.type } : {}),
|
|
620
|
-
...(releaseId ? { release: releaseId } : {}),
|
|
1453
|
+
...((releaseId ?? known.release) ? { release: releaseId ?? known.release } : {}),
|
|
621
1454
|
...(known.commit !== undefined ? { commit: known.commit } : {}),
|
|
622
1455
|
},
|
|
623
1456
|
});
|
|
@@ -625,23 +1458,34 @@ export class ProjectService extends EventEmitter {
|
|
|
625
1458
|
throw err;
|
|
626
1459
|
}
|
|
627
1460
|
}
|
|
628
|
-
async runStackStart(projectName, stackName, source, allowDirty, releaseId, known, reinstall = false) {
|
|
1461
|
+
async runStackStart(projectName, stackName, source, allowDirty, releaseId, known, reinstall = false, workingTree = false) {
|
|
629
1462
|
const config = await this.loadProjectConfig(projectName);
|
|
630
1463
|
const stacks = this.resolveStacks(config, projectName);
|
|
631
1464
|
const stackConfig = stacks[stackName];
|
|
632
1465
|
if (!stackConfig)
|
|
633
1466
|
throw new Error(`Stack '${stackName}' not found in project '${projectName}'`);
|
|
634
1467
|
known.type = stackConfig.type;
|
|
1468
|
+
ownsStackInfrastructure(stackConfig); // Reject ambiguous ownership before attachment, build, migration or provisioning.
|
|
635
1469
|
// A restart of this daemon re-attaches a remote stack; it never deploys
|
|
636
1470
|
// one. See `attachRemoteStack`.
|
|
637
1471
|
if (stackConfig.type !== 'local' && (source === 'boot' || source === 'auto-resume')) {
|
|
638
1472
|
known.attach = true;
|
|
639
1473
|
return this.attachRemoteStack(projectName, stackName, stackConfig, source);
|
|
640
1474
|
}
|
|
1475
|
+
this.assertStartAuthority({ project: projectName, stack: stackName }, stackConfig);
|
|
641
1476
|
// A release, when one is named — and a stack that takes nothing else.
|
|
1477
|
+
const local = stackConfig.type === 'local' && !stackConfig.managedOrigin;
|
|
1478
|
+
if ((workingTree || (releaseId && stackConfig.type === 'local')) && !local)
|
|
1479
|
+
throw new Error('Only an independently managed local stack can change its local release selection');
|
|
1480
|
+
const selector = local ? this.localSelection(projectName, stackName) : null;
|
|
1481
|
+
const prior = selector?.read();
|
|
1482
|
+
const requested = releaseId ?? (workingTree ? undefined : prior?.release?.id);
|
|
642
1483
|
let release = null;
|
|
643
|
-
if (
|
|
644
|
-
release =
|
|
1484
|
+
if (requested) {
|
|
1485
|
+
known.release = requested;
|
|
1486
|
+
release = await this.admitRelease(projectName, stackName, stackConfig, config, requested);
|
|
1487
|
+
if (prior?.release?.id === requested && prior.release.manifestSha256 !== release.manifestSha256)
|
|
1488
|
+
throw new Error('The selected release manifest changed on disk; restore its original bytes');
|
|
645
1489
|
}
|
|
646
1490
|
else if (stackConfig.release?.mode === 'required') {
|
|
647
1491
|
throw new Error(`${projectName}/${stackName} takes releases only: \`omnitron release build ${projectName} --for ${stackName}\`, ` +
|
|
@@ -696,7 +1540,7 @@ export class ProjectService extends EventEmitter {
|
|
|
696
1540
|
// With a release the disk has already been held to the release's commit
|
|
697
1541
|
// (`admitRelease`) — stricter than HEAD, and the artifacts are not built
|
|
698
1542
|
// from it at all.
|
|
699
|
-
if (stackConfig.type
|
|
1543
|
+
if (stackConfig.type !== 'local' && !release) {
|
|
700
1544
|
if (!allowDirty) {
|
|
701
1545
|
for (const { root, tree: t } of trees) {
|
|
702
1546
|
const refusal = refusalForDirtyTree(t, `${projectName}/${stackName}`);
|
|
@@ -714,10 +1558,32 @@ export class ProjectService extends EventEmitter {
|
|
|
714
1558
|
this.logger.warn({ project: projectName, stack: stackName, why: tree.why }, 'Could not tell whether the working tree matches its commit — shipping what is on disk');
|
|
715
1559
|
}
|
|
716
1560
|
}
|
|
1561
|
+
const selected = release ? { id: release.id, manifestSha256: release.manifestSha256 } : null;
|
|
1562
|
+
const changing = !!selector && !isDeepStrictEqual(prior?.release, selected);
|
|
1563
|
+
const mounted = artifactVolumeBindings(mergeInfrastructure(config, stackConfig));
|
|
1564
|
+
if (mounted.length && (!local || !release || !ownsStackInfrastructure(stackConfig)))
|
|
1565
|
+
throw Error('Artifact volumes require a selected release on an independently owned local stack');
|
|
1566
|
+
const installation = mounted.length && release
|
|
1567
|
+
? await this.installedRelease(projectName, stackName, stackConfig, config, release, true) : undefined;
|
|
1568
|
+
const entries = installation?.entries ?? (local && release ? await this.installedEntries(projectName, stackName, stackConfig, config, release, true) : undefined);
|
|
1569
|
+
let localMembershipChanged = false;
|
|
1570
|
+
if (local) {
|
|
1571
|
+
const target = entries ?? this.resolveStackApps(stackConfig, config);
|
|
1572
|
+
const plan = await this.checkLocalOwner(projectName, stackName, target, changing, source === 'boot' || source === 'auto-resume');
|
|
1573
|
+
// A changed immutable mount may not reach Docker before the ordinary owner stop fence.
|
|
1574
|
+
if (mounted.length && changing && plan.phase !== 'stopped')
|
|
1575
|
+
throw new LocalReleaseStopRequired('Stop this local stack before changing its immutable artifact volumes');
|
|
1576
|
+
const names = new Set(target.map(entry => ProjectService.handleKey(projectName, stackName, entry.name)));
|
|
1577
|
+
localMembershipChanged = plan.apps.some(app => !names.has(app.name));
|
|
1578
|
+
if ((source === 'boot' || source === 'auto-resume') && plan.phase !== 'running')
|
|
1579
|
+
throw new Error('The selected local stack is fenced; an explicit start must recover or roll back the selection');
|
|
1580
|
+
}
|
|
717
1581
|
const stateKey = `${projectName}/${stackName}`;
|
|
718
1582
|
const existing = this.stackStates.get(stateKey);
|
|
719
1583
|
// Not for a release: putting a release onto a running stack is the point.
|
|
720
|
-
|
|
1584
|
+
const completedLocalStart = stackConfig.type === 'local' && existing?.startedAt != null &&
|
|
1585
|
+
(existing.status === 'degraded' || existing.status === 'error');
|
|
1586
|
+
if ((existing?.status === 'running' || completedLocalStart) && !changing && !localMembershipChanged && (!release || local)) {
|
|
721
1587
|
// `running` is a claim about the past, and nothing revises it when the
|
|
722
1588
|
// apps underneath fall over — so this short-circuit used to hand the
|
|
723
1589
|
// operator a stack it had not touched. `toStackInfo` reads live
|
|
@@ -743,7 +1609,9 @@ export class ProjectService extends EventEmitter {
|
|
|
743
1609
|
// wrapper returns the info untouched.
|
|
744
1610
|
const current = await this.withRemoteAppStatuses(projectName, this.toStackInfo(projectName, stackName, stackConfig));
|
|
745
1611
|
const down = current.apps.filter((a) => a.status !== 'online');
|
|
746
|
-
if (down.length === 0) {
|
|
1612
|
+
if (down.length === 0 && !current.notUp?.length) {
|
|
1613
|
+
if (existing)
|
|
1614
|
+
existing.status = 'running';
|
|
747
1615
|
this.logger.warn({ project: projectName, stack: stackName }, 'Stack already running');
|
|
748
1616
|
return current;
|
|
749
1617
|
}
|
|
@@ -770,21 +1638,40 @@ export class ProjectService extends EventEmitter {
|
|
|
770
1638
|
this.emit('stack:starting', projectName, stackName, stackConfig.type);
|
|
771
1639
|
let reach = null;
|
|
772
1640
|
try {
|
|
1641
|
+
this.assertStackMutable(stackConfig.managedOrigin ?? { project: projectName, stack: stackName });
|
|
773
1642
|
if (stackConfig.type === 'local') {
|
|
774
|
-
|
|
1643
|
+
const scope = { project: projectName, stack: stackName };
|
|
1644
|
+
const enable = () => this.orchestrator.enableStackExecution(scope, { automatic: source === 'boot' || source === 'auto-resume' });
|
|
1645
|
+
if (selector && prior) {
|
|
1646
|
+
const target = entries ?? this.resolveStackApps(stackConfig, config);
|
|
1647
|
+
await this.startLocalStack(projectName, stackName, stackConfig, config, target, async (build) => {
|
|
1648
|
+
await this.prepareLocalOwner(projectName, stackName, target, changing, build, () => {
|
|
1649
|
+
if (changing)
|
|
1650
|
+
selector.select(prior.revision, selected);
|
|
1651
|
+
else if (selector.read().revision !== prior.revision)
|
|
1652
|
+
throw new Error('Local release selection changed during admission');
|
|
1653
|
+
});
|
|
1654
|
+
await enable();
|
|
1655
|
+
}, release ? (names, files) => this.installedEntries(projectName, stackName, stackConfig, config, release, false, names, files) : undefined, installation ? async () => {
|
|
1656
|
+
const plan = await this.checkLocalOwner(projectName, stackName, target, changing);
|
|
1657
|
+
if (changing && plan.phase !== 'stopped')
|
|
1658
|
+
throw new LocalReleaseStopRequired('Artifact volume selection requires the owner stop fence');
|
|
1659
|
+
return (await this.installedRelease(projectName, stackName, stackConfig, config, release, false)).infrastructure;
|
|
1660
|
+
} : undefined);
|
|
1661
|
+
}
|
|
1662
|
+
else {
|
|
1663
|
+
await this.startLocalStack(projectName, stackName, stackConfig, config, undefined, async () => { await enable(); });
|
|
1664
|
+
}
|
|
775
1665
|
}
|
|
776
|
-
else
|
|
1666
|
+
else {
|
|
1667
|
+
this.rememberStackTargets(projectName, stackName, stackConfig, config);
|
|
777
1668
|
reach = await this.startRemoteStack(projectName, stackName, stackConfig, config, release, reinstall);
|
|
778
1669
|
}
|
|
779
|
-
else if (stackConfig.type === 'cluster') {
|
|
780
|
-
await this.startClusterStack(projectName, stackName, stackConfig, config);
|
|
781
|
-
}
|
|
782
1670
|
state.status = 'running';
|
|
783
1671
|
state.startedAt = Date.now();
|
|
784
1672
|
// Update registry with enabled stack
|
|
785
1673
|
this.updateEnabledStacks(projectName, stackName, true);
|
|
786
|
-
this.
|
|
787
|
-
this.logger.info({ project: projectName, stack: stackName, source }, 'Stack started');
|
|
1674
|
+
this.markPriorStopSuperseded(projectName, stackName);
|
|
788
1675
|
const info = this.toStackInfo(projectName, stackName, stackConfig);
|
|
789
1676
|
// What did not come up, said in the row. A boot at 12:14 UTC on
|
|
790
1677
|
// 2026-09-23 started sample/dev with payments and messaging errored and
|
|
@@ -792,11 +1679,22 @@ export class ProjectService extends EventEmitter {
|
|
|
792
1679
|
// apps never did. For a local stack the apps are this daemon's; for a
|
|
793
1680
|
// remote one, what did not happen is a node that was skipped.
|
|
794
1681
|
const notUp = stackConfig.type === 'local'
|
|
795
|
-
? info.apps.filter((a) => a.status !== 'online').map((a) => `${a.name} (${a.status})`)
|
|
1682
|
+
? info.notUp ?? info.apps.filter((a) => a.status !== 'online').map((a) => `${a.name} (${a.status})`)
|
|
796
1683
|
: [
|
|
797
1684
|
...(reach?.skipped ?? []).map((n) => `node ${typeof n === 'string' ? n : JSON.stringify(n)}`),
|
|
798
1685
|
...(reach?.notReady ?? []).map((n) => `infrastructure on ${n}`),
|
|
799
1686
|
];
|
|
1687
|
+
if (stackConfig.type === 'local')
|
|
1688
|
+
state.status = info.status;
|
|
1689
|
+
const outcome = notUp.length === 0 ? 'ok' : state.status === 'error' ? 'failed' : 'partial';
|
|
1690
|
+
if (outcome === 'ok') {
|
|
1691
|
+
this.emit('stack:started', projectName, stackName, stackConfig.type, info.apps.length, info.nodes?.length ?? 0);
|
|
1692
|
+
this.logger.info({ project: projectName, stack: stackName, source }, 'Stack started');
|
|
1693
|
+
}
|
|
1694
|
+
else {
|
|
1695
|
+
this.emit('stack:error', projectName, stackName, notUp.join(', '));
|
|
1696
|
+
this.logger.warn({ project: projectName, stack: stackName, source, outcome, notUp }, 'Stack start incomplete');
|
|
1697
|
+
}
|
|
800
1698
|
// Recorded for every caller, and saying WHICH one. `source: 'unknown'`
|
|
801
1699
|
// is deliberate rather than a default of `'operator'`: a caller that
|
|
802
1700
|
// has not been taught to identify itself should be visible in the
|
|
@@ -810,7 +1708,7 @@ export class ProjectService extends EventEmitter {
|
|
|
810
1708
|
resourceType: 'stack',
|
|
811
1709
|
resourceId: `${projectName}/${stackName}`,
|
|
812
1710
|
// Said, now that the other ending is recorded too (`startStackOnce`).
|
|
813
|
-
outcome
|
|
1711
|
+
outcome,
|
|
814
1712
|
// The commit, so the trail answers what went out and not only when.
|
|
815
1713
|
// A node's artifacts carry no revision (every app is version `0.0.1`
|
|
816
1714
|
// forever, and `BUNDLE.json` is excluded from the checksum and read
|
|
@@ -841,6 +1739,7 @@ export class ProjectService extends EventEmitter {
|
|
|
841
1739
|
...(release
|
|
842
1740
|
? {
|
|
843
1741
|
release: release.id,
|
|
1742
|
+
releaseManifestSha256: release.manifestSha256,
|
|
844
1743
|
releaseProjectCommit: release.manifest.project.commit.slice(0, 8),
|
|
845
1744
|
releaseOmniCommit: release.manifest.omni.commit.slice(0, 8),
|
|
846
1745
|
}
|
|
@@ -861,52 +1760,293 @@ export class ProjectService extends EventEmitter {
|
|
|
861
1760
|
throw err;
|
|
862
1761
|
}
|
|
863
1762
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1763
|
+
declaredStopTargets(stack, config) {
|
|
1764
|
+
if (stack.type === 'local')
|
|
1765
|
+
return [];
|
|
1766
|
+
const apps = this.resolveStackApps(stack, config).map(app => app.name);
|
|
1767
|
+
return (stack.nodes ?? []).map(node => ({ host: node.host, port: node.port ?? 9700,
|
|
1768
|
+
applications: nodeApplicationNames(stack, node, apps).length > 0, ...(node.nodeId && { nodeId: node.nodeId }) }));
|
|
1769
|
+
}
|
|
1770
|
+
stopRequest(scope, node, keepInfrastructure = false) {
|
|
1771
|
+
if (this.clusterStop && !node.nodeId)
|
|
1772
|
+
throw new Error('A pinned recipient nodeId is required for clustered stack stop');
|
|
1773
|
+
return { ...scope, applications: node.applications, ...(keepInfrastructure && { keepInfrastructure: true }),
|
|
1774
|
+
...(this.clusterStop && { audience: { clusterId: this.clusterStop.recipient.audience.clusterId, nodeId: node.nodeId } }) };
|
|
1775
|
+
}
|
|
1776
|
+
validateStopInventory(request, result) {
|
|
1777
|
+
if (result?.protocol !== 1 || result.project !== request.project || result.stack !== request.stack ||
|
|
1778
|
+
result.applications !== request.applications || !result.definition || !Array.isArray(result.containers) ||
|
|
1779
|
+
(request.applications ? !result.execution || !Array.isArray(result.execution.apps) : result.execution !== null)) {
|
|
1780
|
+
throw new Error('Invalid node stop inventory; update the recipient before stopping');
|
|
1781
|
+
}
|
|
1782
|
+
if (result.audience?.clusterId !== request.audience?.clusterId || result.audience?.nodeId !== request.audience?.nodeId) {
|
|
1783
|
+
throw new Error('Recipient did not confirm the required cluster and node identity');
|
|
874
1784
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1785
|
+
if ((result.keepInfrastructure === true) !== (request.keepInfrastructure === true) ||
|
|
1786
|
+
(result.keepInfrastructure !== undefined && typeof result.keepInfrastructure !== 'boolean')) {
|
|
1787
|
+
throw new Error('Recipient did not confirm the requested infrastructure retention mode; update it before stopping');
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
async localStopInventory(scope, stack, execution, keepInfrastructure = false) {
|
|
1791
|
+
const containers = await this.infraManager.inspectStopContainers(scope.project, scope.stack);
|
|
1792
|
+
return { ...scope, protocol: 1, applications: true, execution, definition: stackDefinitionFingerprint(stack),
|
|
1793
|
+
containers, ...(keepInfrastructure && { keepInfrastructure: true }) };
|
|
1794
|
+
}
|
|
1795
|
+
async reviewStackStop(project, stack, keepInfrastructure = false) {
|
|
1796
|
+
if (typeof keepInfrastructure !== 'boolean')
|
|
1797
|
+
throw new Error('Invalid infrastructure retention mode');
|
|
1798
|
+
const scope = { project, stack };
|
|
1799
|
+
const key = stackExecutionKey(scope);
|
|
1800
|
+
const targets = [];
|
|
878
1801
|
try {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1802
|
+
if (this.ownershipFrozen || this.resumingInstalled || this.startsInFlight.has(key) || this.stopsInFlight.has(key))
|
|
1803
|
+
throw new Error('A lifecycle operation is active; wait and check again');
|
|
1804
|
+
if (this.clusterStop) {
|
|
1805
|
+
if (!this.clusterStop.election)
|
|
1806
|
+
throw new Error('Review this stop on the elected controller');
|
|
1807
|
+
await this.clusterStop.election.confirmStopAuthority();
|
|
1808
|
+
}
|
|
1809
|
+
const config = this.getLoadedConfig(project);
|
|
1810
|
+
if (!config)
|
|
1811
|
+
throw new Error('Project declarations are unavailable; refresh them before reviewing this stop');
|
|
1812
|
+
const declaration = this.stackStates.get(key)?.config ?? this.resolveStacks(config, project)[stack];
|
|
1813
|
+
if (!declaration)
|
|
1814
|
+
throw new Error(`Stack '${key}' not found`);
|
|
1815
|
+
const journal = new StackTargets(this.daemonStateStore);
|
|
1816
|
+
const { nodes } = journal.inspect(scope, this.declaredStopTargets(declaration, config));
|
|
1817
|
+
if (declaration.type === 'local') {
|
|
883
1818
|
try {
|
|
884
|
-
|
|
1819
|
+
if (this.clusterStop && !declaration.managedOrigin)
|
|
1820
|
+
throw new Error('Cluster stack stop requires an explicitly bound allocation');
|
|
1821
|
+
const inventory = declaration.managedOrigin
|
|
1822
|
+
? await this.nodeStackStop.inspect({ ...declaration.managedOrigin, applications: true,
|
|
1823
|
+
...(keepInfrastructure && { keepInfrastructure: true }),
|
|
1824
|
+
...(this.clusterStop && { audience: this.clusterStop.recipient.audience }) })
|
|
1825
|
+
: await this.localStopInventory(scope, declaration, await this.orchestrator.inspectStackExecution(scope), keepInfrastructure);
|
|
1826
|
+
targets.push({ node: 'local', inventory });
|
|
885
1827
|
}
|
|
886
|
-
catch (
|
|
887
|
-
|
|
1828
|
+
catch (error) {
|
|
1829
|
+
targets.push({ node: 'local', reason: describeFailure(error) });
|
|
888
1830
|
}
|
|
889
1831
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1832
|
+
else if (!nodes.length)
|
|
1833
|
+
throw new Error('No remote allocation inventory is available');
|
|
1834
|
+
for (const node of nodes) {
|
|
1835
|
+
const endpoint = stackNodeKey(node.host, node.port);
|
|
1836
|
+
try {
|
|
1837
|
+
if (!this.slaveConnector)
|
|
1838
|
+
throw new Error('Node RPC is unavailable');
|
|
1839
|
+
const request = this.stopRequest(scope, node, keepInfrastructure);
|
|
1840
|
+
const inventory = await this.slaveConnector.invokeOnSlave(node.host, node.port, 'OmnitronProject', 'inspectNodeStackStop', [request]);
|
|
1841
|
+
this.validateStopInventory(request, inventory);
|
|
1842
|
+
targets.push({ node: endpoint, inventory });
|
|
1843
|
+
}
|
|
1844
|
+
catch (error) {
|
|
1845
|
+
targets.push({ node: endpoint, reason: describeFailure(error) });
|
|
894
1846
|
}
|
|
895
1847
|
}
|
|
896
|
-
|
|
897
|
-
|
|
1848
|
+
}
|
|
1849
|
+
catch (error) {
|
|
1850
|
+
targets.push({ node: 'controller', reason: describeFailure(error) });
|
|
1851
|
+
}
|
|
1852
|
+
return stackStopReview(scope, targets, keepInfrastructure);
|
|
1853
|
+
}
|
|
1854
|
+
async stopStack(projectName, stackName, review, keepInfrastructure = false) {
|
|
1855
|
+
if (typeof keepInfrastructure !== 'boolean')
|
|
1856
|
+
throw new Error('Invalid infrastructure retention mode');
|
|
1857
|
+
const key = stackExecutionKey({ project: projectName, stack: stackName });
|
|
1858
|
+
if (this.resumingInstalled)
|
|
1859
|
+
throw new Error('An installed allocation is resuming');
|
|
1860
|
+
if (this.ownershipFrozen)
|
|
1861
|
+
throw new Error('Project ownership is frozen for backend transfer');
|
|
1862
|
+
if (this.startsInFlight.has(key))
|
|
1863
|
+
throw new Error('This stack is starting; finish or reconcile the start before stopping');
|
|
1864
|
+
const pending = this.stopsInFlight.get(key);
|
|
1865
|
+
if (pending && (review !== undefined || pending.keepInfrastructure !== keepInfrastructure))
|
|
1866
|
+
throw new Error('A stop is already running; inspect its progress');
|
|
1867
|
+
if (pending)
|
|
1868
|
+
return pending.promise;
|
|
1869
|
+
if (review !== undefined && (typeof review !== 'string' || !/^[a-f0-9]{64}$/.test(review)))
|
|
1870
|
+
throw new Error('Invalid stack stop review');
|
|
1871
|
+
const operation = this.stopStackOnce(projectName, stackName, review, keepInfrastructure).finally(() => this.stopsInFlight.delete(key));
|
|
1872
|
+
this.stopsInFlight.set(key, { keepInfrastructure, promise: operation });
|
|
1873
|
+
return operation;
|
|
1874
|
+
}
|
|
1875
|
+
async stopStackOnce(projectName, stackName, review, keepInfrastructure) {
|
|
1876
|
+
const key = `${projectName}/${stackName}`;
|
|
1877
|
+
const config = await this.loadProjectConfig(projectName);
|
|
1878
|
+
const stackConfig = this.stackStates.get(key)?.config ?? this.resolveStacks(config, projectName)[stackName];
|
|
1879
|
+
if (!stackConfig)
|
|
1880
|
+
throw new Error(`Stack '${stackName}' not found`);
|
|
1881
|
+
const state = this.stackStates.get(key) ?? {
|
|
1882
|
+
project: projectName, stack: stackName, status: 'stopped',
|
|
1883
|
+
config: stackConfig, startedAt: null, infraService: null,
|
|
1884
|
+
};
|
|
1885
|
+
this.stackStates.set(key, state);
|
|
1886
|
+
const progress = {
|
|
1887
|
+
id: randomUUID(), phase: 'preparing', startedAt: new Date().toISOString(), nodes: [],
|
|
1888
|
+
...(keepInfrastructure && { keepInfrastructure: true }),
|
|
1889
|
+
};
|
|
1890
|
+
const persist = () => this.daemonStateStore.kvUpdateSync(`stack:${key}:last-stop`, () => progress);
|
|
1891
|
+
persist();
|
|
1892
|
+
try {
|
|
1893
|
+
// Complete every read-only admission check before sending any stop.
|
|
1894
|
+
const prepared = [];
|
|
1895
|
+
const cluster = this.clusterStop;
|
|
1896
|
+
if (cluster && !cluster.election?.isLeader)
|
|
1897
|
+
throw new Error('Stack stop requires the elected controller of this cluster');
|
|
1898
|
+
let managedPlan = null;
|
|
1899
|
+
let managedPermit;
|
|
1900
|
+
let targetsRevision;
|
|
1901
|
+
let localPlan = null;
|
|
1902
|
+
if (stackConfig.type === 'local' && stackConfig.managedOrigin) {
|
|
1903
|
+
managedPlan = await this.nodeStackStop.prepare({ ...stackConfig.managedOrigin, applications: true,
|
|
1904
|
+
...(keepInfrastructure && { keepInfrastructure: true }),
|
|
1905
|
+
...(cluster && { audience: cluster.recipient.audience }) });
|
|
1906
|
+
managedPermit = cluster?.election?.authorizeStop(managedPlan);
|
|
1907
|
+
}
|
|
1908
|
+
else if (stackConfig.type === 'local') {
|
|
1909
|
+
if (cluster)
|
|
1910
|
+
throw new Error('Cluster stack stop requires an explicitly bound allocation');
|
|
1911
|
+
localPlan = await this.orchestrator.inspectStackExecution({ project: projectName, stack: stackName });
|
|
1912
|
+
}
|
|
1913
|
+
const scope = { project: projectName, stack: stackName };
|
|
1914
|
+
const localInventory = localPlan ? await this.localStopInventory(scope, stackConfig, localPlan, keepInfrastructure) : null;
|
|
1915
|
+
const declared = this.declaredStopTargets(stackConfig, config);
|
|
1916
|
+
const targetJournal = new StackTargets(this.daemonStateStore);
|
|
1917
|
+
if (declared.length)
|
|
1918
|
+
targetJournal.remember({ project: projectName, stack: stackName }, declared);
|
|
1919
|
+
const targets = targetJournal.inspect({ project: projectName, stack: stackName });
|
|
1920
|
+
if (stackConfig.type !== 'local' && targets.nodes.length === 0)
|
|
1921
|
+
throw new Error('No remote allocation inventory is available; stop cannot be confirmed');
|
|
1922
|
+
if (targets.nodes.length) {
|
|
1923
|
+
if (!this.slaveConnector)
|
|
1924
|
+
throw new Error('Node RPC is unavailable; no remote stop has been sent');
|
|
1925
|
+
targetsRevision = targets.revision;
|
|
1926
|
+
progress.nodes = targets.nodes.map(node => ({ node: `${node.host}:${node.port}`, outcome: 'checking' }));
|
|
1927
|
+
persist();
|
|
1928
|
+
for (const [index, node] of targets.nodes.entries()) {
|
|
1929
|
+
let plan;
|
|
1930
|
+
try {
|
|
1931
|
+
const request = this.stopRequest(scope, node, keepInfrastructure);
|
|
1932
|
+
plan = await this.slaveConnector.invokeOnSlave(node.host, node.port, 'OmnitronProject', 'prepareNodeStackStop', [request]);
|
|
1933
|
+
this.validateStopInventory(request, plan);
|
|
1934
|
+
if (!plan.operationId)
|
|
1935
|
+
throw new Error('Invalid node stop plan');
|
|
1936
|
+
const permit = cluster?.election?.authorizeStop(plan);
|
|
1937
|
+
prepared.push({ host: node.host, port: node.port, plan, permit });
|
|
1938
|
+
if (permit && request.audience)
|
|
1939
|
+
progress.nodes[index].authority = { ...request.audience, epoch: permit.epoch };
|
|
1940
|
+
}
|
|
1941
|
+
catch (error) {
|
|
1942
|
+
progress.nodes[index].outcome = 'unknown';
|
|
1943
|
+
progress.nodes[index].reason = describeFailure(error);
|
|
1944
|
+
throw new Error(`Cannot prepare ${node.host}:${node.port}: ${describeFailure(error)}`, { cause: error });
|
|
1945
|
+
}
|
|
1946
|
+
progress.nodes[index].outcome = 'prepared';
|
|
1947
|
+
persist();
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
if (review !== undefined) {
|
|
1951
|
+
const actual = [];
|
|
1952
|
+
if (managedPlan)
|
|
1953
|
+
actual.push({ node: 'local', inventory: managedPlan });
|
|
1954
|
+
if (localInventory)
|
|
1955
|
+
actual.push({ node: 'local', inventory: localInventory });
|
|
1956
|
+
for (const target of prepared)
|
|
1957
|
+
actual.push({ node: stackNodeKey(target.host, target.port), inventory: target.plan });
|
|
1958
|
+
if (stackStopReview(scope, actual, keepInfrastructure).digest !== review)
|
|
1959
|
+
throw new Error('Stack changed since review; inspect the current footprint before stopping');
|
|
1960
|
+
}
|
|
1961
|
+
// Losing this durable write refuses the operation before any effect.
|
|
1962
|
+
if (cluster)
|
|
1963
|
+
await cluster.election.confirmStopAuthority();
|
|
1964
|
+
this.updateEnabledStacks(projectName, stackName, false);
|
|
1965
|
+
progress.phase = 'stopping';
|
|
1966
|
+
progress.intentCommitted = true;
|
|
1967
|
+
persist();
|
|
1968
|
+
state.status = 'stopping';
|
|
1969
|
+
this.emit('stack:stopping', projectName, stackName);
|
|
1970
|
+
if (managedPlan) {
|
|
1971
|
+
const receipt = await this.nodeStackStop.execute({ ...stackConfig.managedOrigin, operationId: managedPlan.operationId,
|
|
1972
|
+
...(keepInfrastructure && { keepInfrastructure: true }), ...(managedPermit && { permit: managedPermit }) });
|
|
1973
|
+
if (receipt.authority) {
|
|
1974
|
+
progress.nodes.push({ node: 'local', outcome: 'stopped', operationId: receipt.operationId, observedAt: receipt.observedAt, authority: receipt.authority });
|
|
1975
|
+
persist();
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
else if (localPlan && localInventory) {
|
|
1979
|
+
await this.orchestrator.stopStackExecution({ operationId: progress.id, plan: localPlan });
|
|
1980
|
+
if (keepInfrastructure) {
|
|
1981
|
+
const retained = await this.infraManager.inspectStopContainers(projectName, stackName);
|
|
1982
|
+
if (!isDeepStrictEqual([...retained].sort((a, b) => a.id.localeCompare(b.id)), [...localInventory.containers].sort((a, b) => a.id.localeCompare(b.id)))) {
|
|
1983
|
+
throw new Error('Retained stack infrastructure changed; inspect before confirming this application stop');
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
else
|
|
1987
|
+
await this.infraManager.teardownStack(projectName, stackName, localInventory.containers);
|
|
1988
|
+
}
|
|
1989
|
+
if (prepared.length) {
|
|
1990
|
+
const failures = [];
|
|
1991
|
+
for (const [index, target] of prepared.entries()) {
|
|
1992
|
+
const item = progress.nodes[index];
|
|
1993
|
+
item.outcome = 'stopping';
|
|
1994
|
+
persist();
|
|
1995
|
+
try {
|
|
1996
|
+
const receipt = await this.slaveConnector.invokeOnSlave(target.host, target.port, 'OmnitronProject', 'executeNodeStackStop', [{ project: projectName, stack: stackName, operationId: target.plan.operationId,
|
|
1997
|
+
...(keepInfrastructure && { keepInfrastructure: true }),
|
|
1998
|
+
...(target.permit && { permit: target.permit }) }], { retryOnDisconnect: true });
|
|
1999
|
+
if (receipt?.protocol !== 1 || receipt.operationId !== target.plan.operationId || receipt.project !== projectName ||
|
|
2000
|
+
receipt.stack !== stackName || receipt.outcome !== 'stopped' ||
|
|
2001
|
+
(receipt.keepInfrastructure === true) !== keepInfrastructure ||
|
|
2002
|
+
(keepInfrastructure && (!Array.isArray(receipt.keptContainers) || receipt.removedContainers?.length !== 0 ||
|
|
2003
|
+
!isDeepStrictEqual([...receipt.keptContainers].sort(), target.plan.containers.map(c => c.id).sort()))) || (target.permit &&
|
|
2004
|
+
(receipt.authority?.epoch !== target.permit.epoch || receipt.authority.nodeId !== target.plan.audience.nodeId ||
|
|
2005
|
+
receipt.authority.clusterId !== target.plan.audience.clusterId)))
|
|
2006
|
+
throw new Error('Invalid node stop acknowledgement');
|
|
2007
|
+
item.outcome = 'stopped';
|
|
2008
|
+
item.operationId = receipt.operationId;
|
|
2009
|
+
item.observedAt = receipt.observedAt;
|
|
2010
|
+
}
|
|
2011
|
+
catch (error) {
|
|
2012
|
+
item.outcome = 'unknown';
|
|
2013
|
+
item.reason = describeFailure(error);
|
|
2014
|
+
failures.push(`${item.node}: ${item.reason}`);
|
|
2015
|
+
}
|
|
2016
|
+
persist();
|
|
2017
|
+
}
|
|
2018
|
+
if (failures.length)
|
|
2019
|
+
throw new Error(`Stack stop is unconfirmed on ${failures.join('; ')}`);
|
|
2020
|
+
}
|
|
2021
|
+
// Retained infrastructure is still owned here, including former placements.
|
|
2022
|
+
// A later full stop must retain the obligation to contact those nodes.
|
|
2023
|
+
if (!keepInfrastructure && targetsRevision !== undefined)
|
|
2024
|
+
new StackTargets(this.daemonStateStore)
|
|
2025
|
+
.confirmStopped({ project: projectName, stack: stackName }, targetsRevision);
|
|
2026
|
+
progress.phase = 'stopped';
|
|
2027
|
+
progress.completedAt = new Date().toISOString();
|
|
2028
|
+
persist();
|
|
898
2029
|
state.status = 'stopped';
|
|
899
2030
|
state.startedAt = null;
|
|
900
|
-
this.updateEnabledStacks(projectName, stackName, false);
|
|
901
2031
|
this.emit('stack:stopped', projectName, stackName, 'manual');
|
|
902
|
-
this.
|
|
903
|
-
return this.toStackInfo(projectName, stackName, state.config);
|
|
2032
|
+
return this.toStackInfo(projectName, stackName, stackConfig);
|
|
904
2033
|
}
|
|
905
|
-
catch (
|
|
906
|
-
|
|
907
|
-
|
|
2034
|
+
catch (error) {
|
|
2035
|
+
progress.phase = 'failed';
|
|
2036
|
+
progress.error = describeFailure(error);
|
|
2037
|
+
persist();
|
|
2038
|
+
if (progress.intentCommitted)
|
|
2039
|
+
state.status = 'error';
|
|
2040
|
+
this.emit('stack:error', projectName, stackName, String(error));
|
|
2041
|
+
throw error;
|
|
908
2042
|
}
|
|
909
2043
|
}
|
|
2044
|
+
rememberStackTargets(project, stack, definition, config) {
|
|
2045
|
+
const targets = resolveNodePlacements(definition, this.resolveStackApps(definition, config))
|
|
2046
|
+
.map(({ node, apps }) => ({ host: node.host, port: node.port ?? 9700, applications: apps.length > 0,
|
|
2047
|
+
...(node.nodeId && { nodeId: node.nodeId }) }));
|
|
2048
|
+
new StackTargets(this.daemonStateStore).remember({ project, stack }, targets);
|
|
2049
|
+
}
|
|
910
2050
|
/**
|
|
911
2051
|
* What is running in this stack, asked of whoever is running it.
|
|
912
2052
|
*
|
|
@@ -932,60 +2072,23 @@ export class ProjectService extends EventEmitter {
|
|
|
932
2072
|
const stackConfig = stacks[stackName] ?? state?.config;
|
|
933
2073
|
if (!stackConfig)
|
|
934
2074
|
throw new Error(`Stack '${stackName}' not found`);
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
const configForStatus = this.getLoadedConfig(projectName);
|
|
941
|
-
const configuredNames = configForStatus
|
|
942
|
-
? new Set(this.resolveStackApps(stackConfig, configForStatus).map((a) => a.name))
|
|
943
|
-
: new Set();
|
|
944
|
-
const apps = this.orchestrator.list().filter((a) => {
|
|
945
|
-
if (a.name.startsWith(prefix))
|
|
946
|
-
return true;
|
|
947
|
-
if (a.name.includes('/'))
|
|
948
|
-
return false;
|
|
949
|
-
return configuredNames.has(effectiveAppName(a.name));
|
|
950
|
-
});
|
|
951
|
-
const onlineApps = apps.filter((a) => a.status === 'online');
|
|
952
|
-
// Derive status from app health if running
|
|
953
|
-
let status = state?.status ?? 'stopped';
|
|
954
|
-
if (status === 'running' && apps.length > 0 && onlineApps.length < apps.length) {
|
|
955
|
-
status = 'degraded';
|
|
956
|
-
}
|
|
957
|
-
const base = {
|
|
2075
|
+
const observed = stackConfig.type === 'local'
|
|
2076
|
+
? { info: await this.withRemoteAppStatuses(projectName, this.toStackInfo(projectName, stackName, stackConfig)), answered: 1 }
|
|
2077
|
+
: await this.askNodes(projectName, this.getStack(projectName, stackName));
|
|
2078
|
+
const { info, answered } = observed;
|
|
2079
|
+
return {
|
|
958
2080
|
name: stackName,
|
|
959
2081
|
type: stackConfig.type,
|
|
960
|
-
status,
|
|
961
|
-
totalApps: apps.length,
|
|
962
|
-
onlineApps: onlineApps.length,
|
|
963
|
-
totalNodes: stackConfig.nodes?.length ?? 1,
|
|
964
|
-
connectedNodes: stackConfig.type === 'local' ? 1 : 0,
|
|
965
|
-
totalCpu: apps.reduce((sum, a) => sum + a.cpu, 0),
|
|
966
|
-
totalMemory: apps.reduce((sum, a) => sum + a.memory, 0),
|
|
967
|
-
syncSummary: (stackConfig.type === 'remote' || stackConfig.type === 'cluster')
|
|
968
|
-
? summariseSync(this.nodesWithSync(stackConfig))
|
|
969
|
-
: null,
|
|
970
|
-
};
|
|
971
|
-
if (stackConfig.type === 'local')
|
|
972
|
-
return base;
|
|
973
|
-
// The node is asked, and what it answers replaces every count that was
|
|
974
|
-
// taken from this machine. `connectedNodes` is the number of nodes that
|
|
975
|
-
// ANSWERED rather than the number of entries in the connector's
|
|
976
|
-
// registry: that registry reported 0 connections for a node that was
|
|
977
|
-
// answering RPCs in the same second.
|
|
978
|
-
const { info, answered } = await this.askNodes(projectName, this.getStack(projectName, stackName));
|
|
979
|
-
const remote = info.apps;
|
|
980
|
-
const up = remote.filter((a) => a.status === 'online');
|
|
981
|
-
return {
|
|
982
|
-
...base,
|
|
983
2082
|
status: info.status,
|
|
984
|
-
|
|
985
|
-
|
|
2083
|
+
...(info.health && { health: info.health }),
|
|
2084
|
+
...(info.notUp?.length && { notUp: info.notUp }),
|
|
2085
|
+
totalApps: info.apps.length,
|
|
2086
|
+
onlineApps: info.apps.filter(app => app.status === 'online').length,
|
|
2087
|
+
totalNodes: stackConfig.type === 'local' ? 1 : stackConfig.nodes?.length ?? 0,
|
|
986
2088
|
connectedNodes: answered,
|
|
987
|
-
totalCpu:
|
|
988
|
-
totalMemory:
|
|
2089
|
+
totalCpu: info.apps.reduce((sum, app) => sum + (app.cpu ?? 0), 0),
|
|
2090
|
+
totalMemory: info.apps.reduce((sum, app) => sum + (app.memory ?? 0), 0),
|
|
2091
|
+
syncSummary: stackConfig.type === 'local' ? null : summariseSync(this.nodesWithSync(stackConfig)),
|
|
989
2092
|
};
|
|
990
2093
|
}
|
|
991
2094
|
/**
|
|
@@ -1009,6 +2112,66 @@ export class ProjectService extends EventEmitter {
|
|
|
1009
2112
|
const { releasesRoot } = await import('../release/load.js');
|
|
1010
2113
|
return releasesRoot();
|
|
1011
2114
|
}
|
|
2115
|
+
/** Explicit disk inspection: never performed by polling and never alters launch evidence. */
|
|
2116
|
+
async inspectInstalledArtifacts(project, stack, catalogProject) {
|
|
2117
|
+
const { inspectInstalledArtifacts } = await import('../release/installed-artifacts.js');
|
|
2118
|
+
const { installedReleaseCatalog, installedReleaseReport, boundedInstalledNodeRead, installedNodeFailure, installedReadDeadline } = await import('../release/installed-release.js');
|
|
2119
|
+
await this.ensureConfig(project);
|
|
2120
|
+
const configured = this.getStack(project, stack);
|
|
2121
|
+
const catalog = await installedReleaseCatalog(await this.releaseStore(), catalogProject);
|
|
2122
|
+
let info = configured;
|
|
2123
|
+
if (configured.type !== 'local') {
|
|
2124
|
+
this.getSlaveConnector();
|
|
2125
|
+
try {
|
|
2126
|
+
info = await installedReadDeadline(() => this.withRemoteAppStatuses(project, configured), 10_000);
|
|
2127
|
+
}
|
|
2128
|
+
catch {
|
|
2129
|
+
return installedReleaseReport(catalogProject, stack, catalog, [{ node: stack, error: 'Stack node inventory is unavailable or timed out; installed files were not inspected' }]);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
const nodes = [];
|
|
2133
|
+
if (info.type === 'local') {
|
|
2134
|
+
const names = info.apps.map(app => app.handleKey);
|
|
2135
|
+
try {
|
|
2136
|
+
nodes.push({ node: 'local', observations: names.length ? await inspectInstalledArtifacts(this.orchestrator, names) : [] });
|
|
2137
|
+
}
|
|
2138
|
+
catch {
|
|
2139
|
+
nodes.push({ node: 'local', error: 'Installed-file inspection failed or the process owner is unavailable' });
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
else {
|
|
2143
|
+
const placements = info.placements ?? [];
|
|
2144
|
+
if (placements.length > 16)
|
|
2145
|
+
throw new Error('Installed-file inspection supports up to 16 nodes per stack');
|
|
2146
|
+
const deadline = Date.now() + 60_000;
|
|
2147
|
+
for (let offset = 0; offset < placements.length; offset += 4) {
|
|
2148
|
+
await Promise.all(placements.slice(offset, offset + 4).map(async (placement) => {
|
|
2149
|
+
const node = placement.label ?? placement.nodeKey;
|
|
2150
|
+
if (deadline - Date.now() < 55_000) {
|
|
2151
|
+
nodes.push({ node, error: 'Stack inspection time budget exhausted before this node; inspect again' });
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2154
|
+
const names = placement.apps.flatMap(app => app.process ? [app.process.name] : []);
|
|
2155
|
+
if (!placement.appsObserved) {
|
|
2156
|
+
nodes.push({ node, error: 'Node inventory is unavailable' });
|
|
2157
|
+
return;
|
|
2158
|
+
}
|
|
2159
|
+
if (!names.length) {
|
|
2160
|
+
nodes.push({ node, error: 'No installed application handles were reported by this node' });
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
try {
|
|
2164
|
+
const observations = await boundedInstalledNodeRead(() => this.getSlaveConnector().invokeOnSlave(placement.host, placement.port, 'OmnitronDaemon', 'inspectInstalledArtifacts', [{ names }]));
|
|
2165
|
+
nodes.push({ node, observations });
|
|
2166
|
+
}
|
|
2167
|
+
catch (error) {
|
|
2168
|
+
nodes.push({ node, error: installedNodeFailure(error) });
|
|
2169
|
+
}
|
|
2170
|
+
}));
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
return installedReleaseReport(catalogProject, stack, catalog, nodes);
|
|
2174
|
+
}
|
|
1012
2175
|
/**
|
|
1013
2176
|
* Take a release for a stack, or say why not.
|
|
1014
2177
|
*
|
|
@@ -1033,25 +2196,51 @@ export class ProjectService extends EventEmitter {
|
|
|
1033
2196
|
* thing worse than a refusal at deploy time is a console that promised the
|
|
1034
2197
|
* opposite.
|
|
1035
2198
|
*/
|
|
1036
|
-
async checkRelease(projectName, stackName, releaseId) {
|
|
2199
|
+
async checkRelease(projectName, stackName, releaseId, prepare = false) {
|
|
2200
|
+
let preparedArtifactVolumes;
|
|
1037
2201
|
try {
|
|
1038
2202
|
const config = await this.loadProjectConfig(projectName);
|
|
1039
2203
|
const stacks = this.resolveStacks(config, projectName);
|
|
1040
2204
|
const stackConfig = stacks[stackName];
|
|
1041
2205
|
if (!stackConfig)
|
|
1042
2206
|
throw new Error(`Stack '${stackName}' not found in project '${projectName}'`);
|
|
2207
|
+
this.assertStartAuthority({ project: projectName, stack: stackName }, stackConfig);
|
|
2208
|
+
if (prepare && stackConfig.type !== 'local')
|
|
2209
|
+
throw new Error('Preparing a local release requires a local stack');
|
|
1043
2210
|
const release = await this.admitRelease(projectName, stackName, stackConfig, config, releaseId, 'check');
|
|
2211
|
+
let retiring = [];
|
|
2212
|
+
if (stackConfig.type === 'local') {
|
|
2213
|
+
const inspected = artifactVolumeBindings(mergeInfrastructure(config, stackConfig)).length
|
|
2214
|
+
? await this.installedRelease(projectName, stackName, stackConfig, config, release, prepare) : undefined;
|
|
2215
|
+
const entries = inspected?.entries ?? await this.installedEntries(projectName, stackName, stackConfig, config, release, prepare);
|
|
2216
|
+
if (prepare && inspected)
|
|
2217
|
+
preparedArtifactVolumes = inspected.preparedArtifactVolumes;
|
|
2218
|
+
const prior = this.localSelection(projectName, stackName).read().release;
|
|
2219
|
+
if (prior?.id === release.id && prior.manifestSha256 !== release.manifestSha256)
|
|
2220
|
+
throw new Error('Selected manifest changed on disk');
|
|
2221
|
+
const plan = await this.checkLocalOwner(projectName, stackName, entries, prior?.id !== release.id);
|
|
2222
|
+
if (artifactVolumeBindings(mergeInfrastructure(config, stackConfig)).length && prior?.id !== release.id && plan.phase !== 'stopped')
|
|
2223
|
+
throw new LocalReleaseStopRequired('Stop this local stack before changing its immutable artifact volumes');
|
|
2224
|
+
const target = new Set(entries.map(entry => ProjectService.handleKey(projectName, stackName, entry.name)));
|
|
2225
|
+
retiring = plan.apps.filter(app => !target.has(app.name)).map(app => app.name);
|
|
2226
|
+
}
|
|
1044
2227
|
const gates = release.manifest.gates;
|
|
1045
2228
|
const passed = gates.filter((g) => g.status === 'passed').length;
|
|
1046
2229
|
return {
|
|
1047
2230
|
ok: true,
|
|
2231
|
+
...(preparedArtifactVolumes?.length && { preparedArtifactVolumes }),
|
|
1048
2232
|
because: `${projectName}/${stackName} would take ${release.id}: ${passed} of ${gates.length} gates passed, ` +
|
|
1049
2233
|
`${release.files.length} artifact(s) match the manifest` +
|
|
1050
|
-
`${release.staticsDir ? `, static bundle built for ${release.manifest.statics?.stack}` : ''}
|
|
2234
|
+
`${release.staticsDir ? `, static bundle built for ${release.manifest.statics?.stack}` : ''}.` +
|
|
2235
|
+
(retiring.length ? ` After a confirmed whole-stack stop, reconciliation will retire these application registrations: ${retiring.join(', ')}. Logs, artifacts and infrastructure are preserved.` : ''),
|
|
1051
2236
|
};
|
|
1052
2237
|
}
|
|
1053
2238
|
catch (err) {
|
|
1054
|
-
|
|
2239
|
+
const { LocalReleaseNotPreparedError } = await import('../release/local-installation.js');
|
|
2240
|
+
return { ok: false, because: err.message,
|
|
2241
|
+
...(err instanceof LocalReleaseNotPreparedError && { state: 'unprepared' }),
|
|
2242
|
+
...(err instanceof LocalReleaseStopRequired && { state: 'requires-stop',
|
|
2243
|
+
...(preparedArtifactVolumes?.length && { preparedArtifactVolumes }) }) };
|
|
1055
2244
|
}
|
|
1056
2245
|
}
|
|
1057
2246
|
/**
|
|
@@ -1493,20 +2682,25 @@ export class ProjectService extends EventEmitter {
|
|
|
1493
2682
|
* with nothing, so an uncommitted edit to the gateway's template reached the
|
|
1494
2683
|
* test node past every gate a release has.
|
|
1495
2684
|
*/
|
|
1496
|
-
async deploymentReads(projectName, stackConfig, config, root) {
|
|
2685
|
+
async deploymentReads(projectName, stackConfig, config, root, definitions) {
|
|
1497
2686
|
const { definitionInputs } = await import('../release/definition-inputs.js');
|
|
1498
2687
|
const { shippedDirsOf } = await import('../infrastructure/shipped-config.js');
|
|
1499
|
-
const shipped = shippedDirsOf(mergeInfrastructure(config, stackConfig), await this.collectDeclaredServices(projectName, stackConfig, config));
|
|
1500
|
-
return definitionInputs(root, this.resolveStackApps(stackConfig, config).flatMap((a) => (a.bootstrap ? [a.bootstrap] : [])), shipped.map(([, dir]) => dir));
|
|
2688
|
+
const shipped = shippedDirsOf(mergeInfrastructure(config, stackConfig), await this.collectDeclaredServices(projectName, stackConfig, config, { definitions }));
|
|
2689
|
+
return definitionInputs(root, this.resolveStackApps(stackConfig, config).flatMap((a) => (a.bootstrap ? [resolveAppPath(root, a, a.bootstrap)] : [])), shipped.map(([, dir]) => dir));
|
|
1501
2690
|
}
|
|
1502
2691
|
async admitRelease(projectName, stackName, stackConfig, config, releaseId, purpose = 'deploy') {
|
|
1503
|
-
if (stackConfig.type === 'local')
|
|
1504
|
-
throw new Error(
|
|
1505
|
-
}
|
|
2692
|
+
if (stackConfig.type === 'local' && stackConfig.managedOrigin)
|
|
2693
|
+
throw new Error('A node allocation takes its release from its owning controller');
|
|
1506
2694
|
const { loadRelease, treeEqualsCommit } = await import('../release/load.js');
|
|
1507
2695
|
const { decideStackRelease } = await import('../release/manifest.js');
|
|
1508
2696
|
const release = await loadRelease(releaseId, await this.releaseStore());
|
|
1509
|
-
const
|
|
2697
|
+
const configuredApps = this.resolveStackApps(stackConfig, config);
|
|
2698
|
+
if (stackConfig.type !== 'local' && configuredApps.some(entry => entry.releaseFrom || release.manifest.artifacts.some(artifact => artifact.app === entry.name && artifact.origin)))
|
|
2699
|
+
throw Error('Imported applications require verified local release installation. Remote admission needs producer-captured deployment declarations and a compatible execution owner.');
|
|
2700
|
+
const mounted = artifactVolumeEntries(mergeInfrastructure(config, stackConfig), config.apps);
|
|
2701
|
+
if (mounted.length && (stackConfig.type !== 'local' || stackConfig.managedOrigin || !ownsStackInfrastructure(stackConfig)))
|
|
2702
|
+
throw Error('Artifact volumes require an independently owned local stack');
|
|
2703
|
+
const apps = [...new Set([...configuredApps, ...mounted].map(a => a.name))];
|
|
1510
2704
|
// The fourth argument, which was `[]` for as long as nothing wrote one:
|
|
1511
2705
|
// a stack declaring `verifiedOn` refused every release forever, with a
|
|
1512
2706
|
// refusal that read «nothing has been verified on 'test'» while there
|
|
@@ -1526,13 +2720,23 @@ export class ProjectService extends EventEmitter {
|
|
|
1526
2720
|
this.logger.info({ project: projectName, stack: stackName }, 'No release is recorded as running on this stack — applied migrations are not compared');
|
|
1527
2721
|
}
|
|
1528
2722
|
else if (running !== release.id) {
|
|
1529
|
-
let
|
|
2723
|
+
let previous;
|
|
1530
2724
|
try {
|
|
1531
|
-
|
|
2725
|
+
previous = (await loadRelease(running, await this.releaseStore())).manifest;
|
|
1532
2726
|
}
|
|
1533
2727
|
catch (err) {
|
|
1534
2728
|
this.logger.warn({ project: projectName, stack: stackName, running, error: err.message }, 'The release running on this stack could not be read — its migrations are not compared');
|
|
1535
2729
|
}
|
|
2730
|
+
if (previous) {
|
|
2731
|
+
const { changedImportedMigrations } = await import('../release/migrations.js');
|
|
2732
|
+
const imported = changedImportedMigrations(previous, release.manifest, apps);
|
|
2733
|
+
if (imported.length)
|
|
2734
|
+
throw Error('Refusing release ' + release.id + ': imported migration history changed (' + imported.slice(0, 5).join(', ') + '). Preserve applied migrations in the producer package.');
|
|
2735
|
+
}
|
|
2736
|
+
else if (release.manifest.artifacts.some(artifact => apps.includes(artifact.app) && artifact.origin)) {
|
|
2737
|
+
throw Error('Cannot verify imported migration history because the previously running release is unavailable: ' + running);
|
|
2738
|
+
}
|
|
2739
|
+
const ran = previous?.migrations;
|
|
1536
2740
|
if (ran && release.manifest.migrations) {
|
|
1537
2741
|
const { changedReleasedMigrations } = await import('../release/migrations.js');
|
|
1538
2742
|
const changed = changedReleasedMigrations(ran, release.manifest.migrations);
|
|
@@ -1550,17 +2754,48 @@ export class ProjectService extends EventEmitter {
|
|
|
1550
2754
|
if (!project)
|
|
1551
2755
|
throw new Error(`Project '${projectName}' is not in the registry`);
|
|
1552
2756
|
const commit = release.manifest.project.commit;
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
2757
|
+
if (stackConfig.type === 'local') {
|
|
2758
|
+
if (!/^[a-f0-9]{40}$/.test(commit))
|
|
2759
|
+
throw new Error('A local release requires a full source commit');
|
|
2760
|
+
const { promisify } = await import('node:util');
|
|
2761
|
+
const { execFile } = await import('node:child_process');
|
|
2762
|
+
const { stdout } = await promisify(execFile)('git', ['rev-parse', '--show-prefix'], { cwd: project.path });
|
|
2763
|
+
const directory = stdout.trimEnd().replace(/\/$/, '');
|
|
2764
|
+
if (directory !== (release.manifest.project.directory ?? ''))
|
|
2765
|
+
throw new Error('This local release belongs to another project directory in the repository');
|
|
2766
|
+
}
|
|
2767
|
+
// Only what the definition reads has to be the release's. A failed input
|
|
2768
|
+
// inventory cannot be replaced by a narrower or unverified comparison.
|
|
1557
2769
|
const root = fs.realpathSync(project.path);
|
|
2770
|
+
// Local admission materializes the verified package itself. On remote deployments
|
|
2771
|
+
// the old single-project bootstrap still supplies its code declarations; its data
|
|
2772
|
+
// file must come from the admitted archive, including when that file is absent.
|
|
2773
|
+
let remoteDefinitions;
|
|
2774
|
+
if (stackConfig.type !== 'local') {
|
|
2775
|
+
const entries = this.resolveStackApps(stackConfig, config);
|
|
2776
|
+
const archived = new Map();
|
|
2777
|
+
for (const entry of entries) {
|
|
2778
|
+
if (entry.bootstrap)
|
|
2779
|
+
archived.set(entry.name, await readArchiveInfrastructure(release, entry.name));
|
|
2780
|
+
}
|
|
2781
|
+
remoteDefinitions = await this.loadAppDefinitions(projectName, stackConfig, config, entries, archived);
|
|
2782
|
+
}
|
|
1558
2783
|
let inputs;
|
|
1559
2784
|
try {
|
|
1560
|
-
|
|
2785
|
+
// Local launches read executable app declarations from the verified installation. Only
|
|
2786
|
+
// project-level configuration and explicitly mounted infrastructure still come from Git.
|
|
2787
|
+
if (stackConfig.type === 'local') {
|
|
2788
|
+
const { definitionInputs } = await import('../release/definition-inputs.js');
|
|
2789
|
+
const { shippedDirsOf } = await import('../infrastructure/shipped-config.js');
|
|
2790
|
+
inputs = await definitionInputs(root, [], shippedDirsOf(mergeInfrastructure(config, stackConfig), {}).map(([, dir]) => dir));
|
|
2791
|
+
if (!inputs.length)
|
|
2792
|
+
throw new Error('No registered configuration inputs are available');
|
|
2793
|
+
}
|
|
2794
|
+
else
|
|
2795
|
+
inputs = await this.deploymentReads(projectName, stackConfig, config, root, remoteDefinitions);
|
|
1561
2796
|
}
|
|
1562
2797
|
catch (err) {
|
|
1563
|
-
|
|
2798
|
+
throw new Error(`Cannot verify deployment inputs for ${projectName}/${stackName}: ${err.message}`, { cause: err });
|
|
1564
2799
|
}
|
|
1565
2800
|
const tree = await treeEqualsCommit(root, commit, inputs);
|
|
1566
2801
|
if (!tree.equal) {
|
|
@@ -1571,15 +2806,17 @@ export class ProjectService extends EventEmitter {
|
|
|
1571
2806
|
`The definition is read from ${project.path}, so those have to be the release's: commit or set them aside, ` +
|
|
1572
2807
|
`check out ${commit.slice(0, 8)}, or build a release from what is here.`);
|
|
1573
2808
|
}
|
|
1574
|
-
//
|
|
1575
|
-
//
|
|
1576
|
-
const {
|
|
1577
|
-
const
|
|
1578
|
-
if (
|
|
1579
|
-
throw new Error(`Refusing release ${release.id} for ${projectName}/${stackName}: ${
|
|
2809
|
+
// Admission and installation use the same native resident/runtime proof.
|
|
2810
|
+
// A frozen application supplies its own SDK; a controller build is not its runtime.
|
|
2811
|
+
const { refuseReleaseOwners } = await import('../release/owner-admission.js');
|
|
2812
|
+
const incompatible = stackConfig.type === 'local' ? null : refuseReleaseOwners(release.manifest, stackConfig, configuredApps, await this.askReleaseOwners(stackConfig, configuredApps));
|
|
2813
|
+
if (incompatible) {
|
|
2814
|
+
throw new Error(`Refusing release ${release.id} for ${projectName}/${stackName}: ${incompatible}.`);
|
|
1580
2815
|
}
|
|
1581
2816
|
const infra = mergeInfrastructure(config, stackConfig);
|
|
1582
2817
|
const servesStatics = Boolean(infra?.services?.['gateway']?.config?.staticDir ?? infra?.gateway?.staticDir);
|
|
2818
|
+
if (servesStatics && stackConfig.type === 'local')
|
|
2819
|
+
throw new Error('This local gateway serves a working-tree static directory; an immutable local gateway binding is required');
|
|
1583
2820
|
if (servesStatics && (!release.staticsDir || release.manifest.statics?.stack !== stackName)) {
|
|
1584
2821
|
throw new Error(`Refusing release ${release.id} for ${projectName}/${stackName}: the stack's gateway serves a static bundle, and the ` +
|
|
1585
2822
|
`release carries ${release.manifest.statics ? `one built for '${release.manifest.statics.stack}'` : 'none'} — ` +
|
|
@@ -1593,18 +2830,19 @@ export class ProjectService extends EventEmitter {
|
|
|
1593
2830
|
projectCommit: commit.slice(0, 8),
|
|
1594
2831
|
omniCommit: release.manifest.omni.commit.slice(0, 8),
|
|
1595
2832
|
}, purpose === 'check' ? 'Release would be admitted — asked, not deployed' : 'Release admitted');
|
|
1596
|
-
return release;
|
|
2833
|
+
return remoteDefinitions ? { ...release, remoteDefinitions } : release;
|
|
1597
2834
|
}
|
|
1598
|
-
/**
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
return Promise.all((stackConfig.
|
|
2835
|
+
/** Inspect exactly the nodes that will execute applications. Delivery repeats
|
|
2836
|
+
* this OS/private-Netron proof; version suffixes never substitute for it. */
|
|
2837
|
+
async askReleaseOwners(stackConfig, apps) {
|
|
2838
|
+
return Promise.all(resolveNodePlacements(stackConfig, apps).filter(placement => placement.apps.length).map(async ({ node }) => {
|
|
1602
2839
|
try {
|
|
1603
|
-
const
|
|
1604
|
-
return { host: node.host,
|
|
2840
|
+
const bundle = await this.deployer.inspectInstalledBundle(await this.targetForStackNode(node));
|
|
2841
|
+
return { host: node.host, port: node.port ?? 9700,
|
|
2842
|
+
...(bundle.applicationRuntime && { runtime: bundle.applicationRuntime }) };
|
|
1605
2843
|
}
|
|
1606
2844
|
catch (err) {
|
|
1607
|
-
return { host: node.host,
|
|
2845
|
+
return { host: node.host, port: node.port ?? 9700, error: describeFailure(err) };
|
|
1608
2846
|
}
|
|
1609
2847
|
}));
|
|
1610
2848
|
}
|
|
@@ -1665,6 +2903,38 @@ export class ProjectService extends EventEmitter {
|
|
|
1665
2903
|
getInfraManager() {
|
|
1666
2904
|
return this.infraManager;
|
|
1667
2905
|
}
|
|
2906
|
+
/** Cached declarations plus persisted intent; this query never starts infrastructure. */
|
|
2907
|
+
getInfrastructureHealthContext() {
|
|
2908
|
+
const context = { stacks: [], requiredContainers: [], unresolvedStacks: [],
|
|
2909
|
+
observedServices: this.infraManager.listInstances().flatMap(({ infra }) => Object.values(infra.getState().services)) };
|
|
2910
|
+
const apps = this.orchestrator.list();
|
|
2911
|
+
for (const project of this.registry.listRegistrations()) {
|
|
2912
|
+
const config = this.getLoadedConfig(project.name);
|
|
2913
|
+
if (!config)
|
|
2914
|
+
continue; // Unknown ownership must remain visible to the indicator.
|
|
2915
|
+
const running = this.getRunningStacks(project.name);
|
|
2916
|
+
for (const [stack, declaration] of Object.entries(this.resolveStacks(config, project.name))) {
|
|
2917
|
+
if (declaration.type !== 'local')
|
|
2918
|
+
continue;
|
|
2919
|
+
const prefix = `${project.name}/${stack}/`;
|
|
2920
|
+
const active = project.enabledStacks?.includes(stack) || running.includes(stack) ||
|
|
2921
|
+
apps.some(app => app.name.startsWith(prefix) && ['online', 'starting', 'restarting'].includes(app.status));
|
|
2922
|
+
const intent = active ? 'running' : project.enabledStacks ? 'inactive' : 'unknown';
|
|
2923
|
+
const scope = { project: project.name, stack, intent };
|
|
2924
|
+
context.stacks.push(scope);
|
|
2925
|
+
if (intent !== 'running')
|
|
2926
|
+
continue;
|
|
2927
|
+
const infra = this.infraManager.getInstance(project.name, stack);
|
|
2928
|
+
if (infra) {
|
|
2929
|
+
scope.containers = infra.getDesiredServices().map(service => service.name);
|
|
2930
|
+
context.requiredContainers.push(...scope.containers);
|
|
2931
|
+
}
|
|
2932
|
+
else if (mergeInfrastructure(config, declaration))
|
|
2933
|
+
context.unresolvedStacks.push(`${project.name}/${stack}`);
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
return context;
|
|
2937
|
+
}
|
|
1668
2938
|
// ===========================================================================
|
|
1669
2939
|
// Enabled-stacks reconciler — desired-state convergence
|
|
1670
2940
|
// ===========================================================================
|
|
@@ -1686,9 +2956,12 @@ export class ProjectService extends EventEmitter {
|
|
|
1686
2956
|
if (this.resumeTimer)
|
|
1687
2957
|
return;
|
|
1688
2958
|
this.resumeTimer = setInterval(() => {
|
|
2959
|
+
if (this.ownershipFrozen || this.resumingInstalled || this.reconcilingStacks)
|
|
2960
|
+
return;
|
|
2961
|
+
this.reconcilingStacks = true;
|
|
1689
2962
|
void this.reconcileEnabledStacks().catch((err) => {
|
|
1690
2963
|
this.logger.warn({ error: err.message }, 'Enabled-stacks reconciler tick failed');
|
|
1691
|
-
});
|
|
2964
|
+
}).finally(() => { this.reconcilingStacks = false; });
|
|
1692
2965
|
}, intervalMs);
|
|
1693
2966
|
this.resumeTimer.unref();
|
|
1694
2967
|
this.logger.info({ intervalMs }, 'Enabled-stacks reconciler started');
|
|
@@ -1696,10 +2969,13 @@ export class ProjectService extends EventEmitter {
|
|
|
1696
2969
|
async reconcileEnabledStacks() {
|
|
1697
2970
|
// Collect the divergence set first — cheap, no I/O.
|
|
1698
2971
|
const pending = [];
|
|
1699
|
-
for (const project of this.registry.
|
|
2972
|
+
for (const project of this.registry.listRegistrations()) {
|
|
1700
2973
|
for (const stackName of project.enabledStacks ?? []) {
|
|
1701
2974
|
const state = this.stackStates.get(`${project.name}/${stackName}`);
|
|
1702
|
-
|
|
2975
|
+
// Only retry a failed lifecycle attempt. App supervision owns later
|
|
2976
|
+
// changes to a running stack, including individual operator stops.
|
|
2977
|
+
const status = state?.status === 'error' && state.config.type === 'local' && state.startedAt != null
|
|
2978
|
+
? this.toStackInfo(project.name, stackName, state.config).status : state?.status;
|
|
1703
2979
|
// Converge only from fully-down states (absent/stopped/error).
|
|
1704
2980
|
// 'degraded' means mostly-up — restarting the whole stack over it
|
|
1705
2981
|
// would churn healthy apps; per-app recovery owns that case.
|
|
@@ -1711,17 +2987,9 @@ export class ProjectService extends EventEmitter {
|
|
|
1711
2987
|
}
|
|
1712
2988
|
if (pending.length === 0)
|
|
1713
2989
|
return;
|
|
1714
|
-
//
|
|
1715
|
-
//
|
|
1716
|
-
|
|
1717
|
-
if (!(await isDockerAvailable())) {
|
|
1718
|
-
if (this.dockerWasAvailable !== false) {
|
|
1719
|
-
this.logger.warn({ pending: pending.map((p) => `${p.project}/${p.stack}`) }, 'Reconciler: Docker unavailable — deferring enabled-stack resume');
|
|
1720
|
-
}
|
|
1721
|
-
this.dockerWasAvailable = false;
|
|
1722
|
-
return;
|
|
1723
|
-
}
|
|
1724
|
-
this.dockerWasAvailable = true;
|
|
2990
|
+
// Infrastructure admission belongs to startStack, including ownership and
|
|
2991
|
+
// inferred requirements. A global Docker probe would strand remote and
|
|
2992
|
+
// externally provisioned stacks behind an unrelated local dependency.
|
|
1725
2993
|
const now = Date.now();
|
|
1726
2994
|
for (const { project, stack } of pending) {
|
|
1727
2995
|
const key = `${project}/${stack}`;
|
|
@@ -1735,7 +3003,14 @@ export class ProjectService extends EventEmitter {
|
|
|
1735
3003
|
if (!this.getLoadedConfig(project)) {
|
|
1736
3004
|
await this.loadProjectConfig(project);
|
|
1737
3005
|
}
|
|
1738
|
-
await this.startStack(project, stack, { source: 'auto-resume' });
|
|
3006
|
+
const result = await this.startStack(project, stack, { source: 'auto-resume' });
|
|
3007
|
+
if (result.type === 'local' && result.status !== 'running') {
|
|
3008
|
+
if (result.status === 'degraded') {
|
|
3009
|
+
this.logger.warn({ project, stack, notUp: result.notUp }, 'Reconciler: partial start under application supervision');
|
|
3010
|
+
continue;
|
|
3011
|
+
}
|
|
3012
|
+
throw new Error(result.notUp?.join(', ') || `Stack state: ${result.status}`);
|
|
3013
|
+
}
|
|
1739
3014
|
this.resumeBackoff.delete(key);
|
|
1740
3015
|
this.logger.info({ project, stack }, 'Reconciler: stack resumed');
|
|
1741
3016
|
}
|
|
@@ -1803,7 +3078,7 @@ export class ProjectService extends EventEmitter {
|
|
|
1803
3078
|
this.emit('project:config_reloaded', projectName);
|
|
1804
3079
|
}
|
|
1805
3080
|
else {
|
|
1806
|
-
for (const project of this.registry.
|
|
3081
|
+
for (const project of this.registry.listRegistrations()) {
|
|
1807
3082
|
await this.loadProjectConfig(project.name);
|
|
1808
3083
|
this.emit('project:config_reloaded', project.name);
|
|
1809
3084
|
}
|
|
@@ -1812,12 +3087,19 @@ export class ProjectService extends EventEmitter {
|
|
|
1812
3087
|
// ===========================================================================
|
|
1813
3088
|
// Helpers
|
|
1814
3089
|
// ===========================================================================
|
|
1815
|
-
getRunningStacks(projectName) {
|
|
3090
|
+
getRunningStacks(projectName, includeIncomplete = false) {
|
|
1816
3091
|
const result = [];
|
|
1817
3092
|
for (const [, state] of this.stackStates) {
|
|
1818
|
-
if (state.project
|
|
3093
|
+
if (state.project !== projectName)
|
|
3094
|
+
continue;
|
|
3095
|
+
if (includeIncomplete && state.status !== 'stopped') {
|
|
1819
3096
|
result.push(state.stack);
|
|
3097
|
+
continue;
|
|
1820
3098
|
}
|
|
3099
|
+
const status = state.config.type === 'local' && state.startedAt != null
|
|
3100
|
+
? this.toStackInfo(projectName, state.stack, state.config).status : state.status;
|
|
3101
|
+
if (status === 'running' || status === 'starting')
|
|
3102
|
+
result.push(state.stack);
|
|
1821
3103
|
}
|
|
1822
3104
|
return result;
|
|
1823
3105
|
}
|
|
@@ -1877,7 +3159,7 @@ export class ProjectService extends EventEmitter {
|
|
|
1877
3159
|
if (config.database) {
|
|
1878
3160
|
const dbConfig = typeof config.database === 'object' ? config.database : {};
|
|
1879
3161
|
databases[appName] = {
|
|
1880
|
-
|
|
3162
|
+
// A discovered dependency is not authority to execute a migration.
|
|
1881
3163
|
...(dbConfig.extensions?.length ? { extensions: dbConfig.extensions } : {}),
|
|
1882
3164
|
};
|
|
1883
3165
|
}
|
|
@@ -1973,105 +3255,42 @@ export class ProjectService extends EventEmitter {
|
|
|
1973
3255
|
return readings;
|
|
1974
3256
|
}
|
|
1975
3257
|
async validateStackInfrastructure(projectName, stackName) {
|
|
1976
|
-
const
|
|
1977
|
-
const
|
|
1978
|
-
const stackConfig = stacks[stackName];
|
|
1979
|
-
if (!stackConfig) {
|
|
1980
|
-
return { valid: false, issues: [`Stack '${stackName}' not found`] };
|
|
1981
|
-
}
|
|
1982
|
-
const project = this.registry.get(projectName);
|
|
1983
|
-
if (!project) {
|
|
1984
|
-
return { valid: false, issues: [`Project '${projectName}' not found`] };
|
|
1985
|
-
}
|
|
1986
|
-
// Determine effective infrastructure: stack-level overrides ecosystem-level
|
|
1987
|
-
const effectiveInfra = stackConfig.infrastructure ?? config.infrastructure;
|
|
1988
|
-
const issues = [];
|
|
1989
|
-
// Scan app requirements
|
|
1990
|
-
const { scanRequirements } = await import('../project/requirements-scanner.js');
|
|
1991
|
-
const appEntries = this.resolveStackApps(stackConfig, config);
|
|
1992
|
-
const reqs = await scanRequirements(appEntries, project.path);
|
|
1993
|
-
// Check PostgreSQL
|
|
1994
|
-
if (reqs.databases.length > 0 && !effectiveInfra?.postgres) {
|
|
1995
|
-
issues.push(`PostgreSQL required by ${reqs.databases.map((d) => d.app).join(', ')} but not configured in infrastructure`);
|
|
1996
|
-
}
|
|
1997
|
-
else if (reqs.databases.length > 0 && effectiveInfra?.postgres) {
|
|
1998
|
-
// Check individual databases exist in config
|
|
1999
|
-
const configuredDbs = new Set(Object.keys(effectiveInfra.postgres.databases ?? {}));
|
|
2000
|
-
for (const db of reqs.databases) {
|
|
2001
|
-
if (!configuredDbs.has(db.database)) {
|
|
2002
|
-
issues.push(`Database '${db.database}' needed by '${db.app}' not found in postgres.databases`);
|
|
2003
|
-
}
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
|
-
// Check Redis
|
|
2007
|
-
if (reqs.redis.appCount > 0 && !effectiveInfra?.redis) {
|
|
2008
|
-
issues.push(`Redis required by ${reqs.redis.allocations.map((a) => a.app).join(', ')} but not configured in infrastructure`);
|
|
2009
|
-
}
|
|
2010
|
-
// Check S3/MinIO
|
|
2011
|
-
if (reqs.buckets.length > 0 && !effectiveInfra?.minio) {
|
|
2012
|
-
issues.push(`S3/MinIO required by ${reqs.buckets.map((b) => b.app).join(', ')} but not configured in infrastructure`);
|
|
2013
|
-
}
|
|
2014
|
-
else if (reqs.buckets.length > 0 && effectiveInfra?.minio) {
|
|
2015
|
-
const configuredBuckets = new Set(effectiveInfra.minio.buckets ?? []);
|
|
2016
|
-
for (const b of reqs.buckets) {
|
|
2017
|
-
if (configuredBuckets.size > 0 && !configuredBuckets.has(b.bucket)) {
|
|
2018
|
-
issues.push(`Bucket '${b.bucket}' needed by '${b.app}' not found in minio.buckets`);
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
3258
|
+
const report = await this.diagnoseStack(projectName, stackName);
|
|
3259
|
+
const issues = report.checks.filter(check => check.state === 'blocked' || check.state === 'unknown').map(check => `${check.resource}: ${check.message}`);
|
|
2022
3260
|
return { valid: issues.length === 0, issues };
|
|
2023
3261
|
}
|
|
2024
3262
|
// ===========================================================================
|
|
2025
3263
|
// Private — Stack Launchers
|
|
2026
3264
|
// ===========================================================================
|
|
2027
|
-
async startLocalStack(projectName, stackName, stackConfig, ecosystemConfig) {
|
|
3265
|
+
async startLocalStack(projectName, stackName, stackConfig, ecosystemConfig, selectedEntries, enable, verifyMigrations, verifyArtifactInfrastructure) {
|
|
3266
|
+
const ownsInfrastructure = ownsStackInfrastructure(stackConfig);
|
|
2028
3267
|
const stateKey = `${projectName}/${stackName}`;
|
|
2029
3268
|
const state = this.stackStates.get(stateKey);
|
|
2030
|
-
// 0. Validate infrastructure covers app requirements (warn only, don't block)
|
|
2031
|
-
try {
|
|
2032
|
-
const validation = await this.validateStackInfrastructure(projectName, stackName);
|
|
2033
|
-
if (!validation.valid) {
|
|
2034
|
-
for (const issue of validation.issues) {
|
|
2035
|
-
this.logger.warn({ project: projectName, stack: stackName, issue }, 'Infrastructure validation issue');
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
}
|
|
2039
|
-
catch {
|
|
2040
|
-
// Validation is best-effort — don't block stack start
|
|
2041
|
-
}
|
|
2042
3269
|
// 1. Load bootstrap definitions (needed for both infra provisioning and config resolution)
|
|
2043
|
-
const appEntries = this.resolveStackApps(stackConfig, ecosystemConfig);
|
|
3270
|
+
const appEntries = selectedEntries ?? this.resolveStackApps(stackConfig, ecosystemConfig);
|
|
2044
3271
|
// What the stack gives its apps, vault references resolved — now, before
|
|
2045
3272
|
// anything is provisioned: a key the vault does not hold refuses the
|
|
2046
3273
|
// start here (project/stack-env.ts).
|
|
2047
3274
|
const stackEnv = await this.stackEnvFor(projectName, stackName, stackConfig, appEntries.map((e) => e.name));
|
|
2048
3275
|
const project = this.registry.get(projectName);
|
|
2049
|
-
const appDefinitions = await this.loadAppDefinitions(projectName, stackConfig, ecosystemConfig);
|
|
3276
|
+
const appDefinitions = await this.loadAppDefinitions(projectName, stackConfig, ecosystemConfig, appEntries);
|
|
2050
3277
|
// 2. Provision per-stack infrastructure (built-in + app-declared services)
|
|
2051
3278
|
// Auto-detect core services (postgres, redis, minio) from app requirements,
|
|
2052
3279
|
// then merge with explicit infrastructure config (gateway, custom services).
|
|
2053
3280
|
// Merge stack-level + ecosystem-level infrastructure (stack overrides ecosystem, but both contribute)
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
services: { ...explicitServices },
|
|
2068
|
-
};
|
|
2069
|
-
this.logger.info({ project: projectName, stack: stackName, auto: Object.keys(autoInfra), explicit: Object.keys(effectiveInfra.services ?? {}) }, 'Merged auto-detected + explicit infrastructure');
|
|
2070
|
-
}
|
|
2071
|
-
}
|
|
2072
|
-
catch (err) {
|
|
2073
|
-
this.logger.warn({ error: err.message }, 'Failed to auto-detect infrastructure requirements');
|
|
2074
|
-
}
|
|
3281
|
+
for (const entry of appEntries) {
|
|
3282
|
+
if (entry.bootstrap && !appDefinitions.has(entry.name))
|
|
3283
|
+
throw new Error(`Bootstrap declarations unavailable for ${entry.name}`);
|
|
3284
|
+
}
|
|
3285
|
+
const effectiveInfra = ownsInfrastructure ? mergeInferredInfrastructure(buildInfraFromRequirements(requirementsFromDefinitions(appDefinitions)), verifyArtifactInfrastructure ? await verifyArtifactInfrastructure() : mergeInfrastructure(ecosystemConfig, stackConfig)) : undefined;
|
|
3286
|
+
// Refuse missing/tampered migrators before creating or changing infrastructure.
|
|
3287
|
+
if (effectiveInfra && project) {
|
|
3288
|
+
const migrations = await localMigrationTargets(requestedMigrations(normalizeInfraConfig(effectiveInfra, createDefaultRegistry())), appEntries, project.path, verifyMigrations);
|
|
3289
|
+
const resolveMigration = this.stackEntryBuilder(projectName, stackName, stackConfig, ecosystemConfig, appDefinitions, stackEnv);
|
|
3290
|
+
validateMigrationDestinations(migrations.map(target => {
|
|
3291
|
+
const entry = appEntries.find(app => app.name === target.app);
|
|
3292
|
+
return { app: target.app, database: migrationDatabaseEnvironment(target.environmentApp, resolveMigration(entry).env?.['DATABASE_URL'] ?? '', target.database, target.mode) };
|
|
3293
|
+
}));
|
|
2075
3294
|
}
|
|
2076
3295
|
if (effectiveInfra) {
|
|
2077
3296
|
try {
|
|
@@ -2092,10 +3311,10 @@ export class ProjectService extends EventEmitter {
|
|
|
2092
3311
|
// Bail out with a clear, structured error so the operator sees the
|
|
2093
3312
|
// real cause first.
|
|
2094
3313
|
//
|
|
2095
|
-
//
|
|
2096
|
-
//
|
|
2097
|
-
//
|
|
2098
|
-
// operation
|
|
3314
|
+
// Explicit controller/external ownership never enters this provisioning
|
|
3315
|
+
// branch. Declarations alone do not imply permission to manage an
|
|
3316
|
+
// application's existing services. For deliberate degraded-mode
|
|
3317
|
+
// operation of stack-owned infrastructure, settings can opt in via
|
|
2099
3318
|
// `settings.allowDegradedInfra: true` (the field is namespaced under
|
|
2100
3319
|
// settings to remain forward-compatible with stack schema).
|
|
2101
3320
|
const allowDegraded = stackConfig.settings?.['allowDegradedInfra'] === true;
|
|
@@ -2111,17 +3330,17 @@ export class ProjectService extends EventEmitter {
|
|
|
2111
3330
|
throwable.cause = err;
|
|
2112
3331
|
throw throwable;
|
|
2113
3332
|
}
|
|
3333
|
+
state.infrastructureStartFailed = true;
|
|
2114
3334
|
this.logger.warn({ project: projectName, stack: stackName }, 'allowDegradedInfra=true — proceeding with app start despite infra failure');
|
|
2115
3335
|
}
|
|
2116
3336
|
}
|
|
2117
|
-
//
|
|
3337
|
+
// Resolve once for both migrations and application startup. Inferred service
|
|
3338
|
+
// requirements are not permission to connect to a database or run migrations.
|
|
3339
|
+
const appConfigBuilder = this.stackEntryBuilder(projectName, stackName, stackConfig, ecosystemConfig, appDefinitions, stackEnv);
|
|
2118
3340
|
if (effectiveInfra && project) {
|
|
2119
|
-
await this.runStackMigrations(projectName, stackName,
|
|
3341
|
+
await this.runStackMigrations(projectName, stackName, this.infraManager.getNormalizedServices(projectName, stackName) ?? {}, appEntries, project.path, appConfigBuilder, verifyMigrations);
|
|
2120
3342
|
}
|
|
2121
|
-
|
|
2122
|
-
// configuration (`stackEntryBuilder` — the same builder a re-read of
|
|
2123
|
-
// the project uses to redefine running apps).
|
|
2124
|
-
const appConfigBuilder = this.stackEntryBuilder(projectName, stackName, stackConfig, ecosystemConfig, appDefinitions, stackEnv);
|
|
3343
|
+
await enable?.(appConfigBuilder);
|
|
2125
3344
|
// 4. Start apps in dependency-aware parallel batches.
|
|
2126
3345
|
//
|
|
2127
3346
|
// Pre-fix this was a serial `for ... await` loop in declaration
|
|
@@ -2139,13 +3358,12 @@ export class ProjectService extends EventEmitter {
|
|
|
2139
3358
|
// parallel, and a wave can't start until every member of the
|
|
2140
3359
|
// previous wave is online. Same skip-on-failed-dep semantics
|
|
2141
3360
|
// as startAll.
|
|
2142
|
-
const
|
|
2143
|
-
const blocked = new Set();
|
|
3361
|
+
const problems = new Map();
|
|
2144
3362
|
for (const wave of resolveStartupOrder(appEntries)) {
|
|
2145
3363
|
await Promise.all(wave.map(async (entry) => {
|
|
2146
|
-
const blockingDep = (entry.dependsOn ?? []).find((d) =>
|
|
3364
|
+
const blockingDep = (entry.dependsOn ?? []).find((d) => problems.has(d));
|
|
2147
3365
|
if (blockingDep) {
|
|
2148
|
-
|
|
3366
|
+
problems.set(entry.name, `dependency ${blockingDep} did not start`);
|
|
2149
3367
|
this.logger.warn({ app: entry.name, project: projectName, stack: stackName, blockedBy: blockingDep }, 'Skipping app — dependency failed or was blocked');
|
|
2150
3368
|
return;
|
|
2151
3369
|
}
|
|
@@ -2162,14 +3380,15 @@ export class ProjectService extends EventEmitter {
|
|
|
2162
3380
|
await this.orchestrator.startApp(namespacedEntry, ecosystemConfig);
|
|
2163
3381
|
}
|
|
2164
3382
|
catch (err) {
|
|
2165
|
-
|
|
3383
|
+
// The raw exception can contain credentials, app output or host paths.
|
|
3384
|
+
// Keep bounded process evidence in stack views available to readers.
|
|
3385
|
+
problems.set(entry.name, publicLaunchFailure(err));
|
|
2166
3386
|
this.logger.error({ app: entry.name, project: projectName, stack: stackName, error: err.message }, 'Failed to start app in stack');
|
|
2167
3387
|
}
|
|
2168
3388
|
}));
|
|
2169
3389
|
}
|
|
2170
|
-
if (
|
|
2171
|
-
|
|
2172
|
-
}
|
|
3390
|
+
if (problems.size)
|
|
3391
|
+
state.appStartErrors = Object.fromEntries(problems);
|
|
2173
3392
|
}
|
|
2174
3393
|
/**
|
|
2175
3394
|
* Deploy a stack to its nodes, and say what it is doing while it does it.
|
|
@@ -2183,7 +3402,7 @@ export class ProjectService extends EventEmitter {
|
|
|
2183
3402
|
async startRemoteStack(projectName, stackName, stackConfig, ecosystemConfig, release = null, reinstall = false) {
|
|
2184
3403
|
const phases = reportPhases(this.logger, { project: projectName, stack: stackName });
|
|
2185
3404
|
try {
|
|
2186
|
-
return await this.deployRemoteStack(projectName, stackName, stackConfig, ecosystemConfig, phases, release, reinstall);
|
|
3405
|
+
return await withDeploymentProgress({ project: projectName, stack: stackName, operationId: randomUUID(), ...(release && { releaseId: release.id }) }, () => this.deployRemoteStack(projectName, stackName, stackConfig, ecosystemConfig, phases, release, reinstall));
|
|
2187
3406
|
}
|
|
2188
3407
|
finally {
|
|
2189
3408
|
// Every exit, including the refusals: a reporter that outlives its
|
|
@@ -2220,9 +3439,15 @@ export class ProjectService extends EventEmitter {
|
|
|
2220
3439
|
// variant selection and override merging, on the side with less
|
|
2221
3440
|
// information.
|
|
2222
3441
|
phases.enter('reading what the applications declare');
|
|
2223
|
-
|
|
3442
|
+
if (release && !release.remoteDefinitions)
|
|
3443
|
+
throw Error('Checked remote release has no admitted infrastructure definitions');
|
|
3444
|
+
const declaredServices = await this.collectDeclaredServices(projectName, stackConfig, ecosystemConfig, { definitions: release?.remoteDefinitions });
|
|
2224
3445
|
// Build artifacts for apps in this stack
|
|
2225
3446
|
const appEntries = this.resolveStackApps(stackConfig, ecosystemConfig);
|
|
3447
|
+
const migrationRequests = requestedMigrations({ ...declaredServices,
|
|
3448
|
+
...normalizeInfraConfig(mergeInfrastructure(ecosystemConfig, stackConfig) ?? {}, createDefaultRegistry()) });
|
|
3449
|
+
validateMigrationOwners(migrationRequests, appEntries);
|
|
3450
|
+
const placements = orderMigrationPlacements(migrationRequests, resolveNodePlacements(stackConfig, appEntries));
|
|
2226
3451
|
// What the stack gives its apps, vault references resolved — before any
|
|
2227
3452
|
// node is touched: a key the vault does not hold refuses the deployment
|
|
2228
3453
|
// here, not after a node was provisioned (project/stack-env.ts). A remote
|
|
@@ -2240,6 +3465,7 @@ export class ProjectService extends EventEmitter {
|
|
|
2240
3465
|
// Built in clean clones, gated, and checked against the manifest by
|
|
2241
3466
|
// `loadRelease` — nothing is compiled here, from this machine's disk.
|
|
2242
3467
|
const files = new Map(release.files.map((f) => [f.app, f]));
|
|
3468
|
+
const sources = new Map(releaseArtifactOrigins(release.manifest, release.manifestSha256).map(source => [source.app, source]));
|
|
2243
3469
|
artifacts = appEntries.flatMap((entry) => {
|
|
2244
3470
|
const file = files.get(entry.name);
|
|
2245
3471
|
return file
|
|
@@ -2251,6 +3477,8 @@ export class ProjectService extends EventEmitter {
|
|
|
2251
3477
|
builtAt: release.manifest.builtAt,
|
|
2252
3478
|
checksum: file.inputs,
|
|
2253
3479
|
tarballSha256: file.sha256,
|
|
3480
|
+
release: sources.get(file.app).origin,
|
|
3481
|
+
...(sources.get(file.app).artifact.launch && { launch: sources.get(file.app).artifact.launch }),
|
|
2254
3482
|
}]
|
|
2255
3483
|
: [];
|
|
2256
3484
|
});
|
|
@@ -2353,7 +3581,9 @@ export class ProjectService extends EventEmitter {
|
|
|
2353
3581
|
const locks = new Map();
|
|
2354
3582
|
try {
|
|
2355
3583
|
// Deploy to each node: provision slave → deploy artifacts → connect
|
|
2356
|
-
for (const node of
|
|
3584
|
+
for (const { node, apps: nodeApps } of placements) {
|
|
3585
|
+
const selectedNames = new Set(nodeApps.map(app => app.name));
|
|
3586
|
+
const nodeArtifacts = artifacts.filter(artifact => selectedNames.has(artifact.app));
|
|
2357
3587
|
const nodeKey = `${node.host}:${node.port ?? 9700}`;
|
|
2358
3588
|
if (!leases.has(nodeKey)) {
|
|
2359
3589
|
skipped.push(nodeKey);
|
|
@@ -2361,9 +3591,6 @@ export class ProjectService extends EventEmitter {
|
|
|
2361
3591
|
continue;
|
|
2362
3592
|
}
|
|
2363
3593
|
// 1. Provision slave: install runtime + omnitron + generate slave config + start daemon
|
|
2364
|
-
const unsubProvision = this.deployer.onProgress((progress) => {
|
|
2365
|
-
this.emit('stack:deploy_progress', projectName, stackName, progress);
|
|
2366
|
-
});
|
|
2367
3594
|
const target = targets.get(nodeKey);
|
|
2368
3595
|
const master = await resolveMasterHost({ advertiseHost: _dc.advertiseHost, bindHost: _dc.host }, target);
|
|
2369
3596
|
if (master.host) {
|
|
@@ -2381,7 +3608,6 @@ export class ProjectService extends EventEmitter {
|
|
|
2381
3608
|
locks.set(nodeKey, lock);
|
|
2382
3609
|
phases.enter(`provisioning ${node.host}`);
|
|
2383
3610
|
const provisioned = await this.deployer.provisionSlaveNode(target, master.host, masterPort, projectName);
|
|
2384
|
-
unsubProvision();
|
|
2385
3611
|
if (!provisioned) {
|
|
2386
3612
|
skipped.push(nodeKey);
|
|
2387
3613
|
this.logger.error({ node: nodeKey }, 'Failed to provision slave — skipping');
|
|
@@ -2470,25 +3696,23 @@ export class ProjectService extends EventEmitter {
|
|
|
2470
3696
|
// and swept the master's `sample-test-*` as orphans. An address is not an
|
|
2471
3697
|
// instruction to build what it points at.
|
|
2472
3698
|
phases.enter(`resolving the environment for ${node.host}`);
|
|
2473
|
-
const appEnv = await this.resolveNodeAppEnv(ecosystemConfig, projectName, stackConfig,
|
|
3699
|
+
const appEnv = await this.resolveNodeAppEnv(ecosystemConfig, projectName, stackConfig, nodeApps, deployedInfra, release?.remoteDefinitions);
|
|
2474
3700
|
// The stack's own, over the computed — as on a local stack, where
|
|
2475
3701
|
// `settings.env` wins over both.
|
|
2476
|
-
for (const [app, env] of Object.entries(stackEnv))
|
|
3702
|
+
for (const [app, env] of Object.entries(stackEnv).filter(([name]) => selectedNames.has(name)))
|
|
2477
3703
|
appEnv[app] = { ...(appEnv[app] ?? {}), ...env };
|
|
2478
3704
|
// 3. Deploy app artifacts via SSH
|
|
2479
|
-
if (
|
|
2480
|
-
const unsubDeploy = this.deployer.onProgress((progress) => {
|
|
2481
|
-
this.emit('stack:deploy_progress', projectName, stackName, progress);
|
|
2482
|
-
});
|
|
3705
|
+
if (nodeArtifacts.length > 0) {
|
|
2483
3706
|
// The definitions travel with the artifacts. A node that receives
|
|
2484
3707
|
// one without the other has files it cannot run, and says so only if
|
|
2485
3708
|
// someone asks it directly.
|
|
2486
|
-
await leases.confirm(nodeKey, `delivering ${
|
|
2487
|
-
phases.enter(`delivering ${
|
|
3709
|
+
await leases.confirm(nodeKey, `delivering ${nodeArtifacts.length} artifact(s) to ${node.host}`);
|
|
3710
|
+
phases.enter(`delivering ${nodeArtifacts.length} artifact(s) to ${node.host}`);
|
|
2488
3711
|
const sink = ecosystemConfig.monitoring?.alertSink;
|
|
2489
|
-
const results = await this.deployer.deployToStack([target],
|
|
2490
|
-
apps:
|
|
3712
|
+
const results = await this.deployer.deployToStack([target], nodeArtifacts, projectName, {
|
|
3713
|
+
apps: nodeApps,
|
|
2491
3714
|
appEnv,
|
|
3715
|
+
migrations: migrationRequests.filter(request => selectedNames.has(request.app)),
|
|
2492
3716
|
stack: stackName,
|
|
2493
3717
|
// Past the node's record of what each app runs: ship and restart all.
|
|
2494
3718
|
...(reinstall ? { force: true } : {}),
|
|
@@ -2496,7 +3720,6 @@ export class ProjectService extends EventEmitter {
|
|
|
2496
3720
|
// sink on the stack it runs; named as the master names the stack.
|
|
2497
3721
|
...(sink && { alertSink: { ...sink, host: `${projectName}/${stackName}` } }),
|
|
2498
3722
|
});
|
|
2499
|
-
unsubDeploy();
|
|
2500
3723
|
const failed = results.filter((r) => r.status === 'failed');
|
|
2501
3724
|
if (failed.length > 0) {
|
|
2502
3725
|
// The other half of the same invariant. Building every artifact
|
|
@@ -2620,7 +3843,7 @@ export class ProjectService extends EventEmitter {
|
|
|
2620
3843
|
* node, beside containers whose ports are published on that node's
|
|
2621
3844
|
* loopback. Local is what they are from where they stand.
|
|
2622
3845
|
*/
|
|
2623
|
-
async resolveNodeAppEnv(ecosystemConfig, projectName, stackConfig, appEntries, infrastructure) {
|
|
3846
|
+
async resolveNodeAppEnv(ecosystemConfig, projectName, stackConfig, appEntries, infrastructure, admitted) {
|
|
2624
3847
|
if (!infrastructure)
|
|
2625
3848
|
return {};
|
|
2626
3849
|
try {
|
|
@@ -2633,7 +3856,7 @@ export class ProjectService extends EventEmitter {
|
|
|
2633
3856
|
// Measured: the node's config carried five environment variables per
|
|
2634
3857
|
// app and not the one the apps were failing for. An empty map is not a
|
|
2635
3858
|
// map of nothing declared; it is a map nobody filled.
|
|
2636
|
-
const definitions = await this.loadAppDefinitions(projectName, stackConfig, ecosystemConfig);
|
|
3859
|
+
const definitions = admitted ?? await this.loadAppDefinitions(projectName, stackConfig, ecosystemConfig);
|
|
2637
3860
|
// The stack's answers about services it does not provision.
|
|
2638
3861
|
//
|
|
2639
3862
|
// `serviceOverrides` is where a stack says that bitcoin and monero are
|
|
@@ -2668,8 +3891,8 @@ export class ProjectService extends EventEmitter {
|
|
|
2668
3891
|
// Not fatal, and loud: the apps would start with whatever their own
|
|
2669
3892
|
// definitions carry, which is how they came to be started with
|
|
2670
3893
|
// `postgres:postgres` in the first place.
|
|
2671
|
-
this.logger.error({ project: projectName, error: err.message }, 'Could not resolve
|
|
2672
|
-
|
|
3894
|
+
this.logger.error({ project: projectName, error: err.message }, 'Could not resolve application service bindings; deployment refused before application delivery');
|
|
3895
|
+
throw new Error('Could not resolve application service bindings', { cause: err });
|
|
2673
3896
|
}
|
|
2674
3897
|
}
|
|
2675
3898
|
/**
|
|
@@ -3007,206 +4230,59 @@ export class ProjectService extends EventEmitter {
|
|
|
3007
4230
|
return { ready: false, detail: `could not ask the node: ${err.message}` };
|
|
3008
4231
|
}
|
|
3009
4232
|
}
|
|
3010
|
-
async startClusterStack(projectName, stackName, stackConfig, ecosystemConfig) {
|
|
3011
|
-
const nodes = stackConfig.nodes ?? [];
|
|
3012
|
-
if (nodes.length === 0) {
|
|
3013
|
-
throw new Error(`Cluster stack '${stackName}' has no nodes configured`);
|
|
3014
|
-
}
|
|
3015
|
-
this.logger.info({ project: projectName, stack: stackName, nodes: nodes.length }, 'Starting cluster stack — coordinating slave daemons');
|
|
3016
|
-
const connector = this.getSlaveConnector();
|
|
3017
|
-
// Build artifacts
|
|
3018
|
-
const appEntries = this.resolveStackApps(stackConfig, ecosystemConfig);
|
|
3019
|
-
const project = this.registry.get(projectName);
|
|
3020
|
-
let artifacts = [];
|
|
3021
|
-
if (project) {
|
|
3022
|
-
try {
|
|
3023
|
-
const { ArtifactBuilder } = await import('../project/artifact-builder.js');
|
|
3024
|
-
const builder = new ArtifactBuilder(project.path, undefined, {
|
|
3025
|
-
info: (msg) => this.logger.info({ project: project.name }, msg),
|
|
3026
|
-
});
|
|
3027
|
-
const outcome = await builder.buildAll(appEntries);
|
|
3028
|
-
artifacts = outcome.built;
|
|
3029
|
-
for (const f of outcome.failed) {
|
|
3030
|
-
this.logger.error({ app: f.app, error: f.error }, 'Artifact build failed');
|
|
3031
|
-
}
|
|
3032
|
-
}
|
|
3033
|
-
catch (err) {
|
|
3034
|
-
this.logger.error({ error: err.message }, 'Artifact build failed');
|
|
3035
|
-
}
|
|
3036
|
-
}
|
|
3037
|
-
// Resolve master address (from the SLAVE's perspective — what it dials)
|
|
3038
|
-
const { DEFAULT_DAEMON_CONFIG: _dc } = await import('../config/defaults.js');
|
|
3039
|
-
const { resolveMasterHost } = await import('./master-address.js');
|
|
3040
|
-
const masterPort = _dc.port;
|
|
3041
|
-
// Provision all slave nodes in parallel (install runtime + omnitron + config)
|
|
3042
|
-
const appNodes = nodes.filter((n) => n.role !== 'database' && n.role !== 'cache');
|
|
3043
|
-
// One writer per machine, as for a remote stack — see `deployRemoteStack`
|
|
3044
|
-
// and `node-deploy-lease.ts`.
|
|
3045
|
-
const targets = new Map();
|
|
3046
|
-
for (const node of appNodes) {
|
|
3047
|
-
targets.set(`${node.host}:${node.port ?? 9700}`, await this.targetForStackNode(node));
|
|
3048
|
-
}
|
|
3049
|
-
await withNodeLeases([...targets].map(([node, target]) => ({
|
|
3050
|
-
node,
|
|
3051
|
-
machine: `${target.host}:${target.sshPort ?? 22}`,
|
|
3052
|
-
run: this.deployer.leaseRunner(target),
|
|
3053
|
-
})), `${projectName}/${stackName}`, this.logger, async (leases) => {
|
|
3054
|
-
const leased = appNodes.filter((n) => leases.has(`${n.host}:${n.port ?? 9700}`));
|
|
3055
|
-
for (const [node, because] of leases.unreachable) {
|
|
3056
|
-
this.logger.error({ node, because }, 'Could not reach this node to lease it — skipping');
|
|
3057
|
-
}
|
|
3058
|
-
for (const node of leased) {
|
|
3059
|
-
const nodeKey = `${node.host}:${node.port ?? 9700}`;
|
|
3060
|
-
const unsubProvision = this.deployer.onProgress((progress) => {
|
|
3061
|
-
this.emit('stack:deploy_progress', projectName, stackName, progress);
|
|
3062
|
-
});
|
|
3063
|
-
const target = targets.get(nodeKey);
|
|
3064
|
-
const master = await resolveMasterHost({ advertiseHost: _dc.advertiseHost, bindHost: _dc.host }, target);
|
|
3065
|
-
this.logger.info({ node: node.host, masterHost: master.host ?? '(none — this node is pulled from)', from: master.source }, 'Resolved the master address this slave will dial');
|
|
3066
|
-
await leases.confirm(nodeKey, `provisioning ${node.host}`);
|
|
3067
|
-
await this.deployer.provisionSlaveNode(target, master.host, masterPort, projectName);
|
|
3068
|
-
unsubProvision();
|
|
3069
|
-
}
|
|
3070
|
-
// Deploy app artifacts to all provisioned nodes
|
|
3071
|
-
if (artifacts.length > 0 && leased.length > 0) {
|
|
3072
|
-
for (const node of leased) {
|
|
3073
|
-
await leases.confirm(`${node.host}:${node.port ?? 9700}`, `delivering to ${node.host}`);
|
|
3074
|
-
}
|
|
3075
|
-
const unsubDeploy = this.deployer.onProgress((progress) => {
|
|
3076
|
-
this.emit('stack:deploy_progress', projectName, stackName, progress);
|
|
3077
|
-
});
|
|
3078
|
-
const results = await this.deployer.deployToStack(leased.map((n) => targets.get(`${n.host}:${n.port ?? 9700}`)), artifacts, projectName,
|
|
3079
|
-
// Same on the cluster path: artifacts without definitions are files a
|
|
3080
|
-
// node cannot run.
|
|
3081
|
-
{ concurrency: 5, apps: appEntries });
|
|
3082
|
-
unsubDeploy();
|
|
3083
|
-
const successful = results.filter((r) => r.status === 'success').length;
|
|
3084
|
-
const failed = results.filter((r) => r.status === 'failed').length;
|
|
3085
|
-
this.logger.info({ successful, failed, total: results.length }, 'Cluster deployment complete');
|
|
3086
|
-
}
|
|
3087
|
-
});
|
|
3088
|
-
// Connect to all slave daemons
|
|
3089
|
-
for (const node of nodes) {
|
|
3090
|
-
await connector.addSlave({
|
|
3091
|
-
host: node.host,
|
|
3092
|
-
port: node.port ?? 9700,
|
|
3093
|
-
label: node.label,
|
|
3094
|
-
stack: stackName,
|
|
3095
|
-
project: projectName,
|
|
3096
|
-
});
|
|
3097
|
-
}
|
|
3098
|
-
}
|
|
3099
4233
|
// ===========================================================================
|
|
3100
4234
|
// Private — Utilities
|
|
3101
4235
|
// ===========================================================================
|
|
3102
4236
|
/**
|
|
3103
|
-
*
|
|
3104
|
-
* stack infrastructure config. Migrations are executed after databases are
|
|
4237
|
+
* Execute the explicit normalized migration owners in their declared order. Migrations are executed after databases are
|
|
3105
4238
|
* created but before apps are started, so apps always see a current schema.
|
|
3106
4239
|
*
|
|
3107
|
-
*
|
|
3108
|
-
*
|
|
3109
|
-
*
|
|
4240
|
+
* Config bindings use declared cwd and verified release members. Legacy true
|
|
4241
|
+
* retains the conventional script. Both receive the owner's resolved environment.
|
|
4242
|
+
* Normalized presets are the authority for opt-in, including legacy sugar.
|
|
3110
4243
|
*/
|
|
3111
|
-
async runStackMigrations(projectName, stackName,
|
|
3112
|
-
const
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
const
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
//
|
|
3122
|
-
//
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
// Three minutes, not one. A minute fits a container that already has its
|
|
3126
|
-
// data directory; it does not fit a cold one, which has to initdb and
|
|
3127
|
-
// install extensions before it listens — and on a loaded machine that is
|
|
3128
|
-
// several minutes. When the wait gives up the whole `stack start` aborts
|
|
3129
|
-
// and no app is started at all, so a deadline set for the warm case turns
|
|
3130
|
-
// a slow first boot into a stack that will not come up.
|
|
3131
|
-
await waitForPostgres('localhost', port, user, password, 180_000);
|
|
3132
|
-
for (const [dbName, dbConfig] of Object.entries(pgConfig.databases)) {
|
|
3133
|
-
// Only run if migrate flag is set AND a matching app exists
|
|
3134
|
-
if (!dbConfig || typeof dbConfig !== 'object' || !dbConfig.migrate)
|
|
3135
|
-
continue;
|
|
3136
|
-
if (!appNames.has(dbName))
|
|
3137
|
-
continue;
|
|
3138
|
-
const migrateScript = path.join(projectPath, 'apps', dbName, 'src', 'database', 'migrate.ts');
|
|
3139
|
-
try {
|
|
3140
|
-
await fs.promises.access(migrateScript);
|
|
3141
|
-
}
|
|
3142
|
-
catch {
|
|
3143
|
-
this.logger.debug({ app: dbName, script: migrateScript }, 'Migration script not found — skipping');
|
|
3144
|
-
continue;
|
|
3145
|
-
}
|
|
3146
|
-
this.logger.info({ project: projectName, stack: stackName, database: dbName }, 'Running database migrations');
|
|
3147
|
-
// Retry policy: up to 5 attempts with exponential backoff (1s, 2s, 4s, 8s, 16s).
|
|
3148
|
-
// Migration tools are typically idempotent, so retrying after a transient
|
|
3149
|
-
// ECONNREFUSED / role-creation race is safe.
|
|
3150
|
-
const maxAttempts = 5;
|
|
3151
|
-
let attempt = 0;
|
|
3152
|
-
let lastErr = null;
|
|
3153
|
-
while (attempt < maxAttempts) {
|
|
3154
|
-
attempt++;
|
|
4244
|
+
async runStackMigrations(projectName, stackName, services, appEntries, projectPath, resolveEntry, verifyMigrations) {
|
|
4245
|
+
const names = requestedMigrations(services);
|
|
4246
|
+
const planned = await localMigrationTargets(names, appEntries, projectPath, verifyMigrations);
|
|
4247
|
+
// Validate every resolved destination before the first DB probe. No missing source fallback.
|
|
4248
|
+
const targets = planned.map(target => {
|
|
4249
|
+
const env = resolveEntry(appEntries.find(entry => entry.name === target.app)).env;
|
|
4250
|
+
const database = migrationDatabaseEnvironment(target.environmentApp, env?.['DATABASE_URL'] ?? '', target.database, target.mode);
|
|
4251
|
+
return { target, env, database };
|
|
4252
|
+
});
|
|
4253
|
+
validateMigrationDestinations(targets.map(({ target, database }) => ({ app: target.app, database })));
|
|
4254
|
+
// Import every config and resolve its definitions before the first connection.
|
|
4255
|
+
// Runtime connect()/SQL are intentionally not executed by this preflight.
|
|
4256
|
+
for (const { target, env, database } of targets)
|
|
4257
|
+
if (target.mode === 'config') {
|
|
3155
4258
|
try {
|
|
3156
|
-
|
|
3157
|
-
// holds the daemon's whole event loop for as long as the child runs
|
|
3158
|
-
// — no RPC answered, no heartbeat, no mesh — which the event-loop
|
|
3159
|
-
// watch caught at a master boot on 2026-09-23: six migrations back
|
|
3160
|
-
// to back, 1 539 / 871 / 727 / 941 / 740 / 782 ms, 5.6 s of a daemon
|
|
3161
|
-
// that answered nothing, on every restart. And with the retries
|
|
3162
|
-
// below, a migration that hangs held it up to 60 s per attempt.
|
|
3163
|
-
const { execFile } = await import('node:child_process');
|
|
3164
|
-
const { promisify } = await import('node:util');
|
|
3165
|
-
const running = promisify(execFile)(process.execPath, ['--import', 'tsx/esm', migrateScript], {
|
|
3166
|
-
cwd: path.join(projectPath, 'apps', dbName),
|
|
3167
|
-
env: {
|
|
3168
|
-
...process.env,
|
|
3169
|
-
DATABASE_URL: `postgresql://${user}:${password}@localhost:${port}/${dbName}`,
|
|
3170
|
-
// App-specific env vars (various naming conventions)
|
|
3171
|
-
[`${dbName.toUpperCase()}__DATABASE__HOST`]: 'localhost',
|
|
3172
|
-
[`${dbName.toUpperCase()}__DATABASE__PORT`]: String(port),
|
|
3173
|
-
[`${dbName.toUpperCase()}__DATABASE__DATABASE`]: dbName,
|
|
3174
|
-
[`${dbName.toUpperCase()}__DATABASE__USER`]: user,
|
|
3175
|
-
[`${dbName.toUpperCase()}__DATABASE__PASSWORD`]: password,
|
|
3176
|
-
},
|
|
3177
|
-
timeout: 60_000,
|
|
3178
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
3179
|
-
});
|
|
3180
|
-
// Nothing to read from us, as `stdio: 'ignore'` said before.
|
|
3181
|
-
running.child.stdin?.end();
|
|
3182
|
-
const { stdout } = await running;
|
|
3183
|
-
// What the migrations said, in order — see `migrationOutput`.
|
|
3184
|
-
for (const line of migrationOutput(String(stdout))) {
|
|
3185
|
-
this.logger.info({ database: dbName, line }, 'Migration said');
|
|
3186
|
-
}
|
|
3187
|
-
this.logger.info({ database: dbName, attempts: attempt }, 'Migrations applied');
|
|
3188
|
-
lastErr = null;
|
|
3189
|
-
break;
|
|
4259
|
+
await executeLocalMigration(target, { ...env, ...database.env }, { validate: true });
|
|
3190
4260
|
}
|
|
3191
|
-
catch (
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
// Only retry on transient connection errors. Schema / SQL errors
|
|
3195
|
-
// are deterministic and won't go away by trying again.
|
|
3196
|
-
const transient = /ECONNREFUSED|ECONNRESET|ENOTCONN|terminat(ing|ed)|server closing|kysely.*Connection/i.test(stderr) ||
|
|
3197
|
-
/ECONNREFUSED|ECONNRESET/i.test(err.message ?? '');
|
|
3198
|
-
if (!transient || attempt >= maxAttempts) {
|
|
3199
|
-
this.logger.error({ database: dbName, attempt, error: err.message, stderr }, 'Migration failed — app may fail to start');
|
|
3200
|
-
break;
|
|
3201
|
-
}
|
|
3202
|
-
const delayMs = Math.min(1000 * 2 ** (attempt - 1), 16_000);
|
|
3203
|
-
this.logger.warn({ database: dbName, attempt, nextRetryInMs: delayMs, error: err.message }, 'Migration transient failure — retrying');
|
|
3204
|
-
await new Promise((r) => setTimeout(r, delayMs));
|
|
4261
|
+
catch (error) {
|
|
4262
|
+
const failed = error;
|
|
4263
|
+
throw new Error(`Migration config refused for ${target.service}/${target.database} (${target.app}); no migrations executed: ${describeFailure(failed.stderr?.trim() || failed)}`, { cause: error });
|
|
3205
4264
|
}
|
|
3206
4265
|
}
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
4266
|
+
for (const { target, env, database } of targets) {
|
|
4267
|
+
const { host, port, user, password, connectionString } = database;
|
|
4268
|
+
this.logger.info({ project: projectName, stack: stackName, database: target.database, host, port }, 'Waiting for migration database');
|
|
4269
|
+
await waitForPostgres(host, port, user, password, 180_000, connectionString);
|
|
4270
|
+
// Database readiness may take minutes. Recheck the canonical payload and owner immediately before execution.
|
|
4271
|
+
const [current] = await localMigrationTargets([target], appEntries, projectPath, verifyMigrations);
|
|
4272
|
+
if (!current || !isDeepStrictEqual(current, target))
|
|
4273
|
+
throw Error('Migration target changed during database readiness');
|
|
4274
|
+
this.logger.info({ project: projectName, stack: stackName, database: target.database }, 'Running database migrations');
|
|
4275
|
+
try {
|
|
4276
|
+
const { stdout } = await executeLocalMigration(target, { ...env, ...database.env });
|
|
4277
|
+
for (const line of migrationOutput(stdout))
|
|
4278
|
+
this.logger.info({ database: target.database, line: redactDiagnosticText(line) }, 'Migration said');
|
|
4279
|
+
this.logger.info({ database: target.database, attempts: 1 }, 'Migrations applied');
|
|
4280
|
+
}
|
|
4281
|
+
catch (error) {
|
|
4282
|
+
const failed = error;
|
|
4283
|
+
this.logger.error({ database: target.database, error: redactDiagnosticText(failed.message), stderr: redactDiagnosticText(failed.stderr?.trim() ?? '') }, 'Migration failed — stack startup refused');
|
|
4284
|
+
const reason = describeFailure(failed.stderr?.trim() || failed);
|
|
4285
|
+
throw new Error(`Migration failed for ${projectName}/${stackName}/${target.service}/${target.database} (${target.app}); stack startup refused: ${reason}. Inspect the migration ledger before retrying.`, { cause: error });
|
|
3210
4286
|
}
|
|
3211
4287
|
}
|
|
3212
4288
|
}
|
|
@@ -3216,13 +4292,13 @@ export class ProjectService extends EventEmitter {
|
|
|
3216
4292
|
* Config stacks take precedence over user stacks with the same name.
|
|
3217
4293
|
* No implicit fallbacks — stacks must be created explicitly.
|
|
3218
4294
|
*/
|
|
3219
|
-
resolveStacks(config,
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
return
|
|
3224
|
-
|
|
3225
|
-
return
|
|
4295
|
+
resolveStacks(config, project) {
|
|
4296
|
+
if (!project)
|
|
4297
|
+
return config.stacks ?? {};
|
|
4298
|
+
if (typeof project !== 'string')
|
|
4299
|
+
return resolveProjectStacks(config, project.path, project.name);
|
|
4300
|
+
const directory = path.dirname(this.getUserStacksPath(project));
|
|
4301
|
+
return resolveProjectStacks(config, directory, project);
|
|
3226
4302
|
}
|
|
3227
4303
|
/**
|
|
3228
4304
|
* The environment a stack gives its apps, vault references resolved
|
|
@@ -3253,17 +4329,17 @@ export class ProjectService extends EventEmitter {
|
|
|
3253
4329
|
const project = this.registry.get(projectName);
|
|
3254
4330
|
const portAllocation = this.infraManager.getPortAllocation(projectName, stackName);
|
|
3255
4331
|
const normalizedSvcs = this.infraManager.getNormalizedServices(projectName, stackName);
|
|
3256
|
-
const resolvedStack = resolveStack(ecosystemConfig, projectName, stackName, stackConfig, appDefinitions, portAllocation ?? undefined, normalizedSvcs ?? undefined);
|
|
4332
|
+
const resolvedStack = ownsStackInfrastructure(stackConfig) ? resolveStack(ecosystemConfig, projectName, stackName, stackConfig, appDefinitions, portAllocation ?? undefined, normalizedSvcs ?? undefined) : undefined;
|
|
3257
4333
|
return (entry) => {
|
|
3258
|
-
const appConfig = resolvedStack
|
|
4334
|
+
const appConfig = resolvedStack?.appConfigs.get(entry.name);
|
|
3259
4335
|
const infraEnv = appConfig ? resolvedConfigToEnv(appConfig, entry.name, stackName) : {};
|
|
3260
4336
|
return {
|
|
3261
4337
|
...entry,
|
|
3262
4338
|
name: ProjectService.handleKey(projectName, stackName, entry.name),
|
|
3263
4339
|
...(entry.bootstrap && project
|
|
3264
|
-
? { bootstrap:
|
|
4340
|
+
? { bootstrap: resolveAppPath(project.path, entry, entry.bootstrap) }
|
|
3265
4341
|
: {}),
|
|
3266
|
-
...(project ? { cwd: project.path } : {}),
|
|
4342
|
+
...(project ? { cwd: resolveAppPath(project.path, entry) } : {}),
|
|
3267
4343
|
env: {
|
|
3268
4344
|
// Derived first, stated second. `infraEnv` is COMPUTED from the
|
|
3269
4345
|
// stack's configuration; `entry.env` is WRITTEN — by a developer
|
|
@@ -3304,39 +4380,35 @@ export class ProjectService extends EventEmitter {
|
|
|
3304
4380
|
* its chain daemon, its cache — was invisible on that path. The local
|
|
3305
4381
|
* path had it all along, twenty lines up from where it was needed.
|
|
3306
4382
|
*/
|
|
3307
|
-
async loadAppDefinitions(projectName, stackConfig, ecosystemConfig) {
|
|
4383
|
+
async loadAppDefinitions(projectName, stackConfig, ecosystemConfig, appEntries = this.resolveStackApps(stackConfig, ecosystemConfig), archived) {
|
|
3308
4384
|
const appDefinitions = new Map();
|
|
3309
|
-
const appEntries = this.resolveStackApps(stackConfig, ecosystemConfig);
|
|
3310
4385
|
const project = this.registry.get(projectName);
|
|
3311
4386
|
for (const entry of appEntries) {
|
|
4387
|
+
if (entry.releaseFrom)
|
|
4388
|
+
throw Error('Application ' + entry.name + ' requires a prepared and selected release before its prerequisites can be read');
|
|
3312
4389
|
if (entry.bootstrap && project) {
|
|
3313
4390
|
try {
|
|
3314
|
-
const bootstrapAbsPath =
|
|
3315
|
-
const {
|
|
3316
|
-
|
|
4391
|
+
const bootstrapAbsPath = resolveAppPath(project.path, entry, entry.bootstrap);
|
|
4392
|
+
const { definition: loaded, metadata } = await readBootstrapMetadata(bootstrapAbsPath);
|
|
4393
|
+
this.bootstrapMetadata.save(metadata);
|
|
4394
|
+
// Preserve only declarative data across backend handoffs, never executable hooks.
|
|
4395
|
+
this.bootstrapTopology.set(bootstrapAbsPath, structuredClone({ ...(loaded.requires ? { requires: loaded.requires } : {}), ...(loaded.omnitronConfig ? { omnitronConfig: loaded.omnitronConfig } : {}) }));
|
|
3317
4396
|
// The app's `config/default.json`, read now and put on a COPY of
|
|
3318
4397
|
// the definition: the loaded one is the loader's cache entry, and
|
|
3319
4398
|
// writing to it made the first read the only one until the daemon
|
|
3320
4399
|
// restarted (`project/declared-config.ts`).
|
|
3321
|
-
const declared = readDeclaredConfig(bootstrapAbsPath);
|
|
3322
|
-
if (declared
|
|
3323
|
-
|
|
3324
|
-
|
|
4400
|
+
const declared = archived ? archived.get(entry.name) : readDeclaredConfig(bootstrapAbsPath);
|
|
4401
|
+
if (!declared)
|
|
4402
|
+
throw Error('Missing admitted app declaration');
|
|
4403
|
+
if (declared.malformed)
|
|
4404
|
+
throw new Error(`Application configuration is invalid or unreadable: ${entry.name}`);
|
|
3325
4405
|
const definition = withDeclaredConfig(loaded, declared);
|
|
3326
4406
|
appDefinitions.set(entry.name, definition);
|
|
3327
4407
|
}
|
|
3328
4408
|
catch (err) {
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
// daemon — so a swallowed failure here does not degrade the
|
|
3333
|
-
// deployment, it silently removes a whole category from it.
|
|
3334
|
-
//
|
|
3335
|
-
// Measured: a remote stack reported `declared by apps: (none)` and
|
|
3336
|
-
// provisioned no infrastructure, because every bootstrap import
|
|
3337
|
-
// had failed in a process that could not load TypeScript. Nothing
|
|
3338
|
-
// said so at any level.
|
|
3339
|
-
this.logger.warn({ app: entry.name, bootstrap: entry.bootstrap, error: err.message }, 'Could not load this app\'s definition — anything it declares it needs will not be provisioned');
|
|
4409
|
+
this.bootstrapMetadata.fail(resolveAppPath(project.path, entry, entry.bootstrap), err);
|
|
4410
|
+
this.logger.error({ app: entry.name, bootstrap: entry.bootstrap }, 'Stack prerequisites unavailable; refusing incomplete provisioning');
|
|
4411
|
+
throw new Error(`Could not read declarations for ${entry.name}: ${publicLaunchFailure(err)}. Refresh declarations before starting.`, { cause: err });
|
|
3340
4412
|
}
|
|
3341
4413
|
}
|
|
3342
4414
|
}
|
|
@@ -3348,16 +4420,14 @@ export class ProjectService extends EventEmitter {
|
|
|
3348
4420
|
* One entry per service name: two applications that both declare
|
|
3349
4421
|
* `postgres` mean one Postgres, which is what `serviceOverrides` keys on
|
|
3350
4422
|
* and what the container resolver has always assumed. A later declaration
|
|
3351
|
-
* that disagrees with an earlier one
|
|
3352
|
-
*
|
|
3353
|
-
* different versions of one service is a fact about the stack, not a
|
|
3354
|
-
* detail to resolve by ordering.
|
|
4423
|
+
* that disagrees with an earlier one refuses the plan; application ordering
|
|
4424
|
+
* must not silently choose the configuration of a shared service.
|
|
3355
4425
|
*/
|
|
3356
4426
|
async collectDeclaredServices(projectName, stackConfig, ecosystemConfig,
|
|
3357
4427
|
/** Keep the ones reached elsewhere too — to ask whether they answer, not to provision them. */
|
|
3358
4428
|
options = {}) {
|
|
3359
4429
|
const merged = {};
|
|
3360
|
-
const definitions = await this.loadAppDefinitions(projectName, stackConfig, ecosystemConfig);
|
|
4430
|
+
const definitions = options.definitions ?? await this.loadAppDefinitions(projectName, stackConfig, ecosystemConfig);
|
|
3361
4431
|
for (const [appName, definition] of definitions) {
|
|
3362
4432
|
const declared = definition.omnitronConfig?.infrastructure;
|
|
3363
4433
|
if (!declared)
|
|
@@ -3371,7 +4441,8 @@ export class ProjectService extends EventEmitter {
|
|
|
3371
4441
|
if (provisioning === 'disabled' || (provisioning === 'external' && !options.external))
|
|
3372
4442
|
continue;
|
|
3373
4443
|
if (merged[name]) {
|
|
3374
|
-
|
|
4444
|
+
if (!isDeepStrictEqual(merged[name], requirement))
|
|
4445
|
+
throw new Error(`Conflicting infrastructure declarations for ${name}; use one shared specification or distinct service names`);
|
|
3375
4446
|
continue;
|
|
3376
4447
|
}
|
|
3377
4448
|
merged[name] = requirement;
|
|
@@ -3379,6 +4450,114 @@ export class ProjectService extends EventEmitter {
|
|
|
3379
4450
|
}
|
|
3380
4451
|
return merged;
|
|
3381
4452
|
}
|
|
4453
|
+
localSelection(project, stack) {
|
|
4454
|
+
const registration = this.registry.get(project);
|
|
4455
|
+
if (!registration)
|
|
4456
|
+
throw new Error('Project is not registered');
|
|
4457
|
+
return new LocalReleaseSelection(this.daemonStateStore, { project, stack }, registration.registeredAt);
|
|
4458
|
+
}
|
|
4459
|
+
async installedEntries(project, stack, definition, config, release, prepare = false, requiredMigrations, migrationFiles) {
|
|
4460
|
+
return (await this.installedRelease(project, stack, definition, config, release, prepare, requiredMigrations, migrationFiles)).entries;
|
|
4461
|
+
}
|
|
4462
|
+
async installedRelease(project, stack, definition, config, release, prepare = false, requiredMigrations, migrationFiles) {
|
|
4463
|
+
const root = this.registry.get(project)?.path;
|
|
4464
|
+
if (!root)
|
|
4465
|
+
throw new Error('Project is not registered');
|
|
4466
|
+
const { inspectLocalReleaseInstallation, inspectLocalReleasePayloads } = await import('../release/local-installation.js');
|
|
4467
|
+
const stackEntries = this.resolveStackApps(definition, config);
|
|
4468
|
+
// Validate the complete stack before narrowing executable verification to
|
|
4469
|
+
// migration owners. Other applications still own their declared environment.
|
|
4470
|
+
const env = await this.stackEnvFor(project, stack, definition, stackEntries.map(entry => entry.name));
|
|
4471
|
+
const configured = stackEntries.filter(entry => !requiredMigrations || requiredMigrations.includes(entry.name));
|
|
4472
|
+
const entries = configured.map(entry => ({
|
|
4473
|
+
...entry, env: { ...entry.env, ...env[entry.name] },
|
|
4474
|
+
...(!entry.releaseFrom || entry.cwd !== undefined ? { cwd: resolveAppPath(root, entry) } : {}),
|
|
4475
|
+
...(entry.bootstrap && { bootstrap: resolveAppPath(root, entry, entry.bootstrap) }),
|
|
4476
|
+
}));
|
|
4477
|
+
const ownerPid = this.orchestrator.getExecutionOwnerPid?.();
|
|
4478
|
+
if (!ownerPid)
|
|
4479
|
+
throw new Error('A verified local release requires a connected independent execution owner');
|
|
4480
|
+
const ownerPackage = await this.orchestrator.getExecutionOwnerPackage?.();
|
|
4481
|
+
if (!ownerPackage)
|
|
4482
|
+
throw new Error('Verified runtime origin requires an independently verified execution-owner package');
|
|
4483
|
+
const infrastructure = mergeInfrastructure(config, definition);
|
|
4484
|
+
const payloads = artifactVolumeEntries(infrastructure, config.apps);
|
|
4485
|
+
const roots = await inspectLocalReleasePayloads(release, payloads, prepare);
|
|
4486
|
+
const resolved = await bindArtifactVolumes(infrastructure, roots);
|
|
4487
|
+
const installed = await inspectLocalReleaseInstallation(release, stack, entries, { prepare, ownerPid, ownerPackage,
|
|
4488
|
+
...(requiredMigrations && { requiredFiles: migrationFiles ?? Object.fromEntries(requiredMigrations.map(app => [app, [COMPILED_MIGRATION_ENTRY]])) }),
|
|
4489
|
+
});
|
|
4490
|
+
const preparedArtifactVolumes = artifactVolumeBindings(infrastructure).map(([binding, value]) => {
|
|
4491
|
+
const service = resolved?.services?.[value.service];
|
|
4492
|
+
const mount = service && !('preset' in service) ? service.docker?.volumes?.[binding] : undefined;
|
|
4493
|
+
const artifact = release.manifest.artifacts.find(item => item.app === value.app);
|
|
4494
|
+
if (!mount || typeof mount === 'string' || mount.readonly !== true || !artifact)
|
|
4495
|
+
throw Error('Verified artifact volume result is unavailable');
|
|
4496
|
+
return { binding, ...value, source: mount.source, target: mount.target, readonly: true,
|
|
4497
|
+
releaseId: release.id, manifestSha256: release.manifestSha256, artifactSha256: artifact.sha256 };
|
|
4498
|
+
});
|
|
4499
|
+
return { entries: installed, infrastructure: resolved, preparedArtifactVolumes };
|
|
4500
|
+
}
|
|
4501
|
+
/** All consumers of executable declarations must see the same physical entry as the owner. */
|
|
4502
|
+
async launchEntries(project, stack, definition, config, requiredMigrations, migrationFiles) {
|
|
4503
|
+
const selected = definition.type === 'local' && !definition.managedOrigin ? this.localSelection(project, stack).read().release : null;
|
|
4504
|
+
if (!selected)
|
|
4505
|
+
return this.resolveStackApps(definition, config);
|
|
4506
|
+
const { loadRelease } = await import('../release/load.js');
|
|
4507
|
+
const release = await loadRelease(selected.id, await this.releaseStore());
|
|
4508
|
+
if (release.manifestSha256 !== selected.manifestSha256)
|
|
4509
|
+
throw new Error('Selected release manifest changed on disk');
|
|
4510
|
+
return this.installedEntries(project, stack, definition, config, release, false, requiredMigrations, migrationFiles);
|
|
4511
|
+
}
|
|
4512
|
+
async checkLocalOwner(project, stack, entries, changing, automatic = false) {
|
|
4513
|
+
const plan = await this.orchestrator.inspectStackExecution({ project, stack });
|
|
4514
|
+
await this.orchestrator.refreshObservation?.();
|
|
4515
|
+
if (plan.phase === 'stopping' || plan.phase === 'starting')
|
|
4516
|
+
throw new Error('Stack has an unfinished owner operation; inspect it before selecting a release');
|
|
4517
|
+
const expected = new Map(entries.map(entry => [ProjectService.handleKey(project, stack, entry.name), entry]));
|
|
4518
|
+
const retiring = plan.apps.filter(app => !expected.has(app.name));
|
|
4519
|
+
if (retiring.length) {
|
|
4520
|
+
if (this.orchestrator.getExecutionCapabilities?.()['stoppedStackReconciliation'] !== 1)
|
|
4521
|
+
throw new Error('Execution owner does not support stopped stack reconciliation; prepare an owner upgrade with an application restart plan before changing stack membership.');
|
|
4522
|
+
if (automatic)
|
|
4523
|
+
throw new Error('Automatic resume cannot remove retained applications; explicitly stop and start the selected stack to reconcile its membership');
|
|
4524
|
+
}
|
|
4525
|
+
for (const app of plan.apps) {
|
|
4526
|
+
const handle = this.orchestrator.getHandle(app.name);
|
|
4527
|
+
const entry = expected.get(app.name);
|
|
4528
|
+
if (!handle)
|
|
4529
|
+
throw new Error('Owner observation changed; inspect the local stack before selecting a release: ' + app.name);
|
|
4530
|
+
if (handle.status === 'stopped' && handle.pid === null)
|
|
4531
|
+
continue;
|
|
4532
|
+
if (retiring.length || changing || plan.phase === 'stopped' || !entry || !sameLaunchCode(handle.entry, entry))
|
|
4533
|
+
throw new LocalReleaseStopRequired('Stop the applications of this local stack before changing launch code or membership: ' + app.name);
|
|
4534
|
+
}
|
|
4535
|
+
return plan;
|
|
4536
|
+
}
|
|
4537
|
+
/** The owner's durable stop fence covers the gap between the SQLite commit and the complete graph reconciliation. */
|
|
4538
|
+
async prepareLocalOwner(project, stack, entries, changing, build, commit) {
|
|
4539
|
+
let plan = await this.checkLocalOwner(project, stack, entries, changing);
|
|
4540
|
+
const next = new Map(entries.map(entry => { const built = build(entry); return [built.name, built]; }));
|
|
4541
|
+
const retiring = plan.apps.some(app => !next.has(app.name));
|
|
4542
|
+
if ((changing || retiring) && plan.phase !== 'stopped') {
|
|
4543
|
+
await this.orchestrator.stopStackExecution({ operationId: randomUUID(), plan });
|
|
4544
|
+
plan = await this.orchestrator.inspectStackExecution({ project, stack });
|
|
4545
|
+
}
|
|
4546
|
+
commit();
|
|
4547
|
+
if (changing || plan.phase === 'stopped') {
|
|
4548
|
+
if (this.orchestrator.getExecutionCapabilities?.()['stoppedStackReconciliation'] === 1) {
|
|
4549
|
+
await this.orchestrator.reconcileStoppedStack({ operationId: randomUUID(), plan, entries: [...next.values()] });
|
|
4550
|
+
}
|
|
4551
|
+
else {
|
|
4552
|
+
// Existing owners can replace entries of an unchanged inventory only.
|
|
4553
|
+
// checkLocalOwner refuses membership changes before committing a selection.
|
|
4554
|
+
for (const { name } of plan.apps) {
|
|
4555
|
+
if (!await this.orchestrator.redefineApp(name, next.get(name)))
|
|
4556
|
+
throw new Error('Owner could not confirm the selected entry for ' + name + '; the stack remains fenced');
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
}
|
|
4560
|
+
}
|
|
3382
4561
|
resolveStackApps(stackConfig, ecosystemConfig) {
|
|
3383
4562
|
const allApps = ecosystemConfig.apps.filter((a) => a.enabled !== false);
|
|
3384
4563
|
if (!stackConfig.apps || stackConfig.apps === 'all') {
|
|
@@ -3386,25 +4565,15 @@ export class ProjectService extends EventEmitter {
|
|
|
3386
4565
|
}
|
|
3387
4566
|
return allApps.filter((a) => Array.isArray(stackConfig.apps) ? stackConfig.apps.includes(a.name) : true);
|
|
3388
4567
|
}
|
|
4568
|
+
markPriorStopSuperseded(project, stack, expectedStopId) {
|
|
4569
|
+
this.daemonStateStore.kvUpdateSync(`stack:${project}/${stack}:last-stop`, prior => {
|
|
4570
|
+
if (!prior || (expectedStopId && prior.id !== expectedStopId))
|
|
4571
|
+
return prior;
|
|
4572
|
+
return { ...prior, supersededAt: new Date().toISOString() };
|
|
4573
|
+
});
|
|
4574
|
+
}
|
|
3389
4575
|
updateEnabledStacks(projectName, stackName, enabled) {
|
|
3390
|
-
|
|
3391
|
-
if (!project)
|
|
3392
|
-
return;
|
|
3393
|
-
const enabledStacks = new Set(project.enabledStacks ?? []);
|
|
3394
|
-
if (enabled) {
|
|
3395
|
-
enabledStacks.add(stackName);
|
|
3396
|
-
}
|
|
3397
|
-
else {
|
|
3398
|
-
enabledStacks.delete(stackName);
|
|
3399
|
-
}
|
|
3400
|
-
project.enabledStacks = [...enabledStacks];
|
|
3401
|
-
// Persist to disk via registry
|
|
3402
|
-
try {
|
|
3403
|
-
this.registry.persist();
|
|
3404
|
-
}
|
|
3405
|
-
catch {
|
|
3406
|
-
// Non-critical — runtime state is authoritative
|
|
3407
|
-
}
|
|
4576
|
+
this.registry.setStackEnabled(projectName, stackName, enabled);
|
|
3408
4577
|
}
|
|
3409
4578
|
toProjectInfo(project) {
|
|
3410
4579
|
const config = this.configRegistry.get(project.name)?.config;
|
|
@@ -3438,12 +4607,25 @@ export class ProjectService extends EventEmitter {
|
|
|
3438
4607
|
* deployment fails a script; and the console's stack page shows the same
|
|
3439
4608
|
* six rows, stopped.
|
|
3440
4609
|
*
|
|
3441
|
-
*
|
|
3442
|
-
*
|
|
3443
|
-
* local rows are at least honest about being local.
|
|
4610
|
+
* Each node retains its own observation. An unavailable source is unknown;
|
|
4611
|
+
* a replica on another node cannot turn that missing observation into online.
|
|
3444
4612
|
*/
|
|
4613
|
+
localHealthRead = null;
|
|
3445
4614
|
async withRemoteAppStatuses(projectName, info) {
|
|
3446
|
-
|
|
4615
|
+
if (info.type !== 'local')
|
|
4616
|
+
return (await this.askNodes(projectName, info)).info;
|
|
4617
|
+
// Concurrent inventory consumers share one owner read; no stale health cache
|
|
4618
|
+
// survives an application restart or silently becomes a green default.
|
|
4619
|
+
const read = this.localHealthRead ??= Promise.resolve().then(() => this.orchestrator.getHealth())
|
|
4620
|
+
.catch(() => null).finally(() => { if (this.localHealthRead === read)
|
|
4621
|
+
this.localHealthRead = null; });
|
|
4622
|
+
const health = await read;
|
|
4623
|
+
// Re-read lifecycle after the asynchronous health observation.
|
|
4624
|
+
const current = this.getStack(projectName, info.name);
|
|
4625
|
+
const unchanged = new Set(info.apps.filter(a => current.apps.some(b => b.handleKey === a.handleKey &&
|
|
4626
|
+
b.pid === a.pid && b.status === a.status && b.restarts === a.restarts)).map(a => a.handleKey));
|
|
4627
|
+
const bound = health && Object.fromEntries(Object.entries(health).filter(([name]) => unchanged.has(name)));
|
|
4628
|
+
return observeStackHealth(current, new Map([['local', bound]]), new Date().toISOString());
|
|
3447
4629
|
}
|
|
3448
4630
|
/**
|
|
3449
4631
|
* The same question, with the count of nodes that answered it.
|
|
@@ -3457,85 +4639,46 @@ export class ProjectService extends EventEmitter {
|
|
|
3457
4639
|
async askNodes(projectName, info) {
|
|
3458
4640
|
if (info.type !== 'remote' && info.type !== 'cluster')
|
|
3459
4641
|
return { info, answered: 0 };
|
|
3460
|
-
const nodes = info.config.nodes ?? [];
|
|
3461
|
-
if
|
|
3462
|
-
|
|
3463
|
-
const
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
answered += 1;
|
|
3470
|
-
// The containers are on the node too — `infraManager.getInstance`
|
|
3471
|
-
// answers about THIS machine, where a remote stack has none, so the
|
|
3472
|
-
// stack read "Infrastructure: not provisioned" beside a node running
|
|
3473
|
-
// postgres, redis, minio, the gateway and tor.
|
|
3474
|
-
remoteInfra ??= await this.remoteInfraStatus(projectName, info.name, node);
|
|
3475
|
-
for (const app of status?.apps ?? []) {
|
|
3476
|
-
// The node names them `<project>/deployed/<app>`; the stack knows
|
|
3477
|
-
// them by the app's own name.
|
|
3478
|
-
const bare = app.name.includes('/') ? app.name.slice(app.name.lastIndexOf('/') + 1) : app.name;
|
|
3479
|
-
reported.set(bare, app);
|
|
3480
|
-
}
|
|
3481
|
-
}
|
|
3482
|
-
catch (err) {
|
|
3483
|
-
this.logger.warn({ project: projectName, stack: info.name, node: node.host, error: err.message }, 'Could not ask this node what it is running — its apps are reported as this master sees them');
|
|
3484
|
-
}
|
|
3485
|
-
}
|
|
3486
|
-
if (answered === 0)
|
|
3487
|
-
return { info, answered };
|
|
3488
|
-
const apps = info.apps.map((app) => {
|
|
3489
|
-
const running = reported.get(app.name);
|
|
3490
|
-
if (!running)
|
|
3491
|
-
return app;
|
|
4642
|
+
const nodes = [...(info.config.nodes ?? [])];
|
|
4643
|
+
// Keep the last actual runtime footprint visible if its source configuration changes.
|
|
4644
|
+
// Unadmitted target plans are never evidence that a workload ran on a node.
|
|
4645
|
+
for (const old of this.stackStates.get(`${projectName}/${info.name}`)?.config.nodes ?? [])
|
|
4646
|
+
if (!nodes.some(node => node.host === old.host && (node.port ?? 9700) === (old.port ?? 9700)))
|
|
4647
|
+
nodes.push({ ...old, apps: [] });
|
|
4648
|
+
if (nodes.length === 0)
|
|
4649
|
+
return { info: observeStackHealth(info, new Map(), new Date().toISOString()), answered: 0 };
|
|
4650
|
+
if (!this.slaveConnector)
|
|
3492
4651
|
return {
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
status: running.status,
|
|
3496
|
-
pid: running.pid,
|
|
3497
|
-
instances: running.instances,
|
|
3498
|
-
uptime: running.uptime,
|
|
3499
|
-
restarts: running.restarts,
|
|
3500
|
-
cpu: running.cpu,
|
|
3501
|
-
memory: running.memory,
|
|
3502
|
-
port: running.port ?? null,
|
|
4652
|
+
info: observeStackHealth(observeStack(info, nodes.map(node => nodePlacement(projectName, info, node, null, null, new Date().toISOString()))), new Map(), new Date().toISOString()),
|
|
4653
|
+
answered: 0,
|
|
3503
4654
|
};
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
//
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
status,
|
|
3524
|
-
infrastructure: remoteInfra ?? info.infrastructure,
|
|
3525
|
-
apps,
|
|
3526
|
-
startedAt: longest > 0 ? new Date(Date.now() - longest).toISOString() : null,
|
|
3527
|
-
uptime: longest,
|
|
3528
|
-
},
|
|
3529
|
-
answered,
|
|
3530
|
-
};
|
|
4655
|
+
const health = new Map();
|
|
4656
|
+
const placements = [];
|
|
4657
|
+
// Bounded fan-out: one failed peer must not hide the other peers' inventory.
|
|
4658
|
+
for (let offset = 0; offset < nodes.length; offset += 4) {
|
|
4659
|
+
placements.push(...await Promise.all(nodes.slice(offset, offset + 4).map(async (node) => {
|
|
4660
|
+
const [processAnswer, infrastructureAnswer, healthAnswer] = await Promise.allSettled([
|
|
4661
|
+
this.slaveConnector.invokeOnSlave(node.host, node.port ?? 9700, 'OmnitronDaemon', 'status', []),
|
|
4662
|
+
this.remoteInfraStatus(projectName, info.name, node),
|
|
4663
|
+
this.slaveConnector.invokeOnSlave(node.host, node.port ?? 9700, 'OmnitronDaemon', 'getHealth', [{}]),
|
|
4664
|
+
]);
|
|
4665
|
+
const status = processAnswer.status === 'fulfilled'
|
|
4666
|
+
? processAnswer.value : null;
|
|
4667
|
+
const processes = status && Array.isArray(status.apps) ? status.apps : null;
|
|
4668
|
+
const reading = healthAnswer.status === 'fulfilled' ? healthAnswer.value : null;
|
|
4669
|
+
health.set(stackNodeKey(node.host, node.port), reading?.apps ?? null);
|
|
4670
|
+
return nodePlacement(projectName, info, node, processes, infrastructureAnswer.status === 'fulfilled' ? infrastructureAnswer.value : null, new Date().toISOString());
|
|
4671
|
+
})));
|
|
4672
|
+
}
|
|
4673
|
+
return { info: observeStackHealth(observeStack(info, placements), health, new Date().toISOString()), answered: placements.filter(node => node.appsObserved).length };
|
|
3531
4674
|
}
|
|
3532
4675
|
/**
|
|
3533
4676
|
* One node's infrastructure, named the way the stack names it.
|
|
3534
4677
|
*
|
|
3535
4678
|
* The node's container names carry ITS prefix (`sample-test-postgres`), and
|
|
3536
4679
|
* a reader wants the service — `postgres` — so the same stripping the
|
|
3537
|
-
* local path does is done here. `null`
|
|
3538
|
-
*
|
|
4680
|
+
* local path does is done here. `null` means the node could not answer;
|
|
4681
|
+
* an empty inventory is an observed absence, never this master's inventory.
|
|
3539
4682
|
*/
|
|
3540
4683
|
async remoteInfraStatus(projectName, stackName, node) {
|
|
3541
4684
|
if (!this.slaveConnector)
|
|
@@ -3552,25 +4695,21 @@ export class ProjectService extends EventEmitter {
|
|
|
3552
4695
|
const entries = state?.services
|
|
3553
4696
|
? Object.entries(state.services)
|
|
3554
4697
|
: ((await this.slaveConnector.invokeOnSlave(node.host, node.port ?? 9700, 'OmnitronInfra', 'listContainers', [])) ?? [])
|
|
3555
|
-
.filter((c) => c.name.startsWith(prefix))
|
|
3556
4698
|
.map((c) => [c.name, c]);
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
4699
|
+
const owned = entries.filter(([, service]) => service.project || service.stack
|
|
4700
|
+
? service.project === projectName && service.stack === stackName
|
|
4701
|
+
: service.name.startsWith(prefix));
|
|
4702
|
+
for (const [containerName, svc] of owned) {
|
|
3560
4703
|
const name = containerName.startsWith(prefix) ? containerName.slice(prefix.length) : containerName;
|
|
3561
|
-
services[name] = {
|
|
3562
|
-
status: svc.status === 'running' ? 'running' : svc.error ? 'error' : 'stopped',
|
|
3563
|
-
containerName: svc.name,
|
|
3564
|
-
port: svc.ports ? Object.values(svc.ports)[0] ?? null : null,
|
|
3565
|
-
};
|
|
4704
|
+
services[name] = stackServiceObservation({ ...svc, service: svc.service ?? name });
|
|
3566
4705
|
}
|
|
3567
4706
|
// Without the node's own verdict, "ready" is what the containers say:
|
|
3568
4707
|
// every one of them running.
|
|
3569
|
-
const ready =
|
|
4708
|
+
const ready = Object.values(services).every(svc => svc.status === 'running' && svc.health !== 'unhealthy' && svc.health !== 'starting' && !svc.error && (!svc.provisioning || svc.provisioning === 'ready'));
|
|
3570
4709
|
return { ready, services };
|
|
3571
4710
|
}
|
|
3572
4711
|
catch (err) {
|
|
3573
|
-
this.logger.warn({ project: projectName, stack: stackName, node: node.host, error: err.message }, '
|
|
4712
|
+
this.logger.warn({ project: projectName, stack: stackName, node: node.host, error: err.message }, 'Node infrastructure inventory unavailable; placement state is unknown');
|
|
3574
4713
|
return null;
|
|
3575
4714
|
}
|
|
3576
4715
|
}
|
|
@@ -3611,6 +4750,8 @@ export class ProjectService extends EventEmitter {
|
|
|
3611
4750
|
cpu: running.cpu,
|
|
3612
4751
|
memory: running.memory,
|
|
3613
4752
|
port: running.port ?? null,
|
|
4753
|
+
...(running.generation !== undefined && { generation: running.generation }),
|
|
4754
|
+
...(running.release && { release: running.release }),
|
|
3614
4755
|
};
|
|
3615
4756
|
}
|
|
3616
4757
|
// App is configured but not running
|
|
@@ -3640,6 +4781,8 @@ export class ProjectService extends EventEmitter {
|
|
|
3640
4781
|
cpu: running.cpu,
|
|
3641
4782
|
memory: running.memory,
|
|
3642
4783
|
port: running.port ?? null,
|
|
4784
|
+
...(running.generation !== undefined && { generation: running.generation }),
|
|
4785
|
+
...(running.release && { release: running.release }),
|
|
3643
4786
|
});
|
|
3644
4787
|
}
|
|
3645
4788
|
// Populate real node connectivity from SlaveConnector
|
|
@@ -3684,11 +4827,7 @@ export class ProjectService extends EventEmitter {
|
|
|
3684
4827
|
const serviceName = containerName.startsWith(stackPrefix)
|
|
3685
4828
|
? containerName.slice(stackPrefix.length)
|
|
3686
4829
|
: containerName;
|
|
3687
|
-
infraServices[serviceName] =
|
|
3688
|
-
status: svcState.status === 'running' ? 'running' : svcState.error ? 'error' : 'stopped',
|
|
3689
|
-
containerName: svcState.name,
|
|
3690
|
-
port: svcState.ports ? Object.values(svcState.ports)[0] ?? null : null,
|
|
3691
|
-
};
|
|
4830
|
+
infraServices[serviceName] = stackServiceObservation(svcState);
|
|
3692
4831
|
}
|
|
3693
4832
|
}
|
|
3694
4833
|
const infrastructure = {
|
|
@@ -3704,10 +4843,35 @@ export class ProjectService extends EventEmitter {
|
|
|
3704
4843
|
// restart). It is reported as what it is.
|
|
3705
4844
|
const onNodes = config.type === 'remote' || config.type === 'cluster';
|
|
3706
4845
|
const since = state?.startedAt ?? null;
|
|
4846
|
+
let status = state?.status ?? 'stopped';
|
|
4847
|
+
const notUp = [];
|
|
4848
|
+
if (!onNodes && (status === 'running' || (since !== null && (status === 'degraded' || status === 'error')))) {
|
|
4849
|
+
const down = apps.filter(app => app.status !== 'online');
|
|
4850
|
+
for (const app of down) {
|
|
4851
|
+
const errors = state?.appStartErrors;
|
|
4852
|
+
const reason = errors && Object.hasOwn(errors, app.name) ? errors[app.name] : undefined;
|
|
4853
|
+
notUp.push(`${app.name} (${app.status})${reason ? ': ' + reason : ''}`);
|
|
4854
|
+
}
|
|
4855
|
+
if (!infrastructure.ready && (state?.infrastructureStartFailed || state?.infraService?.getDesiredServices().length)) {
|
|
4856
|
+
notUp.push('infrastructure: required services are not ready (see daemon log)');
|
|
4857
|
+
}
|
|
4858
|
+
if (notUp.length === 0)
|
|
4859
|
+
status = 'running';
|
|
4860
|
+
else if (down.length < apps.length || down.some(app => app.status === 'starting') ||
|
|
4861
|
+
(apps.length === 0 && Object.values(infrastructure.services).some(service => service.status === 'running')))
|
|
4862
|
+
status = 'degraded';
|
|
4863
|
+
else
|
|
4864
|
+
status = 'error';
|
|
4865
|
+
}
|
|
4866
|
+
const selectedRelease = config.type === 'local' && !config.managedOrigin ? this.localSelection(projectName, stackName).read().release : null;
|
|
3707
4867
|
return {
|
|
3708
4868
|
name: stackName,
|
|
3709
4869
|
type: config.type,
|
|
3710
|
-
|
|
4870
|
+
...(selectedRelease && { selectedRelease }),
|
|
4871
|
+
status,
|
|
4872
|
+
...(notUp.length > 0 && { notUp }),
|
|
4873
|
+
resumeOperation: this.daemonStateStore.kvGetSync(`stack:${stateKey}:last-resume`, { strict: true }) ?? undefined,
|
|
4874
|
+
stopOperation: this.daemonStateStore.kvGetSync(`stack:${projectName}/${stackName}:last-stop`, { strict: true }) ?? undefined,
|
|
3711
4875
|
config,
|
|
3712
4876
|
nodes,
|
|
3713
4877
|
apps,
|
|
@@ -3719,6 +4883,138 @@ export class ProjectService extends EventEmitter {
|
|
|
3719
4883
|
};
|
|
3720
4884
|
}
|
|
3721
4885
|
}
|
|
4886
|
+
__decorate([
|
|
4887
|
+
RecoveryMutation(),
|
|
4888
|
+
__metadata("design:type", Function),
|
|
4889
|
+
__metadata("design:paramtypes", [Object]),
|
|
4890
|
+
__metadata("design:returntype", void 0)
|
|
4891
|
+
], ProjectService.prototype, "executeNodeInstalledResume", null);
|
|
4892
|
+
__decorate([
|
|
4893
|
+
RecoveryMutation(),
|
|
4894
|
+
__metadata("design:type", Function),
|
|
4895
|
+
__metadata("design:paramtypes", [String, String]),
|
|
4896
|
+
__metadata("design:returntype", Promise)
|
|
4897
|
+
], ProjectService.prototype, "resumeInstalledStack", null);
|
|
4898
|
+
__decorate([
|
|
4899
|
+
RecoveryMutation(),
|
|
4900
|
+
__metadata("design:type", Function),
|
|
4901
|
+
__metadata("design:paramtypes", [Object]),
|
|
4902
|
+
__metadata("design:returntype", Promise)
|
|
4903
|
+
], ProjectService.prototype, "prepareNodeStackStop", null);
|
|
4904
|
+
__decorate([
|
|
4905
|
+
RecoveryMutation(),
|
|
4906
|
+
__metadata("design:type", Function),
|
|
4907
|
+
__metadata("design:paramtypes", [Object]),
|
|
4908
|
+
__metadata("design:returntype", Promise)
|
|
4909
|
+
], ProjectService.prototype, "executeNodeStackStop", null);
|
|
4910
|
+
__decorate([
|
|
4911
|
+
RecoveryMutation(),
|
|
4912
|
+
__metadata("design:type", Function),
|
|
4913
|
+
__metadata("design:paramtypes", [Object]),
|
|
4914
|
+
__metadata("design:returntype", Promise)
|
|
4915
|
+
], ProjectService.prototype, "importProjectConfiguration", null);
|
|
4916
|
+
__decorate([
|
|
4917
|
+
RecoveryMutation(),
|
|
4918
|
+
__metadata("design:type", Function),
|
|
4919
|
+
__metadata("design:paramtypes", [Object]),
|
|
4920
|
+
__metadata("design:returntype", Promise)
|
|
4921
|
+
], ProjectService.prototype, "detachProjectConfiguration", null);
|
|
4922
|
+
__decorate([
|
|
4923
|
+
RecoveryMutation(),
|
|
4924
|
+
__metadata("design:type", Function),
|
|
4925
|
+
__metadata("design:paramtypes", [String, String]),
|
|
4926
|
+
__metadata("design:returntype", Promise)
|
|
4927
|
+
], ProjectService.prototype, "addProject", null);
|
|
4928
|
+
__decorate([
|
|
4929
|
+
RecoveryMutation(),
|
|
4930
|
+
__metadata("design:type", Function),
|
|
4931
|
+
__metadata("design:paramtypes", [String]),
|
|
4932
|
+
__metadata("design:returntype", Promise)
|
|
4933
|
+
], ProjectService.prototype, "rereadProject", null);
|
|
4934
|
+
__decorate([
|
|
4935
|
+
RecoveryMutation(),
|
|
4936
|
+
__metadata("design:type", Function),
|
|
4937
|
+
__metadata("design:paramtypes", [String, String]),
|
|
4938
|
+
__metadata("design:returntype", Promise)
|
|
4939
|
+
], ProjectService.prototype, "refreshInfrastructureOwnership", null);
|
|
4940
|
+
__decorate([
|
|
4941
|
+
RecoveryMutation(),
|
|
4942
|
+
__metadata("design:type", Function),
|
|
4943
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
4944
|
+
__metadata("design:returntype", Object)
|
|
4945
|
+
], ProjectService.prototype, "updateProject", null);
|
|
4946
|
+
__decorate([
|
|
4947
|
+
RecoveryMutation(),
|
|
4948
|
+
__metadata("design:type", Function),
|
|
4949
|
+
__metadata("design:paramtypes", [String]),
|
|
4950
|
+
__metadata("design:returntype", void 0)
|
|
4951
|
+
], ProjectService.prototype, "removeProject", null);
|
|
4952
|
+
__decorate([
|
|
4953
|
+
RecoveryMutation(),
|
|
4954
|
+
__metadata("design:type", Function),
|
|
4955
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
4956
|
+
__metadata("design:returntype", Promise)
|
|
4957
|
+
], ProjectService.prototype, "createStack", null);
|
|
4958
|
+
__decorate([
|
|
4959
|
+
RecoveryMutation(),
|
|
4960
|
+
__metadata("design:type", Function),
|
|
4961
|
+
__metadata("design:paramtypes", [String, String]),
|
|
4962
|
+
__metadata("design:returntype", Promise)
|
|
4963
|
+
], ProjectService.prototype, "deleteStack", null);
|
|
4964
|
+
__decorate([
|
|
4965
|
+
RecoveryMutation(),
|
|
4966
|
+
__metadata("design:type", Function),
|
|
4967
|
+
__metadata("design:paramtypes", [Object]),
|
|
4968
|
+
__metadata("design:returntype", Promise)
|
|
4969
|
+
], ProjectService.prototype, "saveTopologyDraft", null);
|
|
4970
|
+
__decorate([
|
|
4971
|
+
RecoveryMutation(),
|
|
4972
|
+
__metadata("design:type", Function),
|
|
4973
|
+
__metadata("design:paramtypes", [Object]),
|
|
4974
|
+
__metadata("design:returntype", Promise)
|
|
4975
|
+
], ProjectService.prototype, "planTopology", null);
|
|
4976
|
+
__decorate([
|
|
4977
|
+
RecoveryMutation(),
|
|
4978
|
+
__metadata("design:type", Function),
|
|
4979
|
+
__metadata("design:paramtypes", [Object]),
|
|
4980
|
+
__metadata("design:returntype", Promise)
|
|
4981
|
+
], ProjectService.prototype, "applyTopologyPlan", null);
|
|
4982
|
+
__decorate([
|
|
4983
|
+
RecoveryMutation(),
|
|
4984
|
+
__metadata("design:type", Function),
|
|
4985
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
4986
|
+
__metadata("design:returntype", Promise)
|
|
4987
|
+
], ProjectService.prototype, "refreshTopologyMetadata", null);
|
|
4988
|
+
__decorate([
|
|
4989
|
+
RecoveryMutation(),
|
|
4990
|
+
__metadata("design:type", Function),
|
|
4991
|
+
__metadata("design:paramtypes", [String, String, Object]),
|
|
4992
|
+
__metadata("design:returntype", Promise)
|
|
4993
|
+
], ProjectService.prototype, "startStack", null);
|
|
4994
|
+
__decorate([
|
|
4995
|
+
RecoveryMutation(),
|
|
4996
|
+
__metadata("design:type", Function),
|
|
4997
|
+
__metadata("design:paramtypes", [String, String, String, Object]),
|
|
4998
|
+
__metadata("design:returntype", Promise)
|
|
4999
|
+
], ProjectService.prototype, "stopStack", null);
|
|
5000
|
+
__decorate([
|
|
5001
|
+
RecoveryMutation(),
|
|
5002
|
+
__metadata("design:type", Function),
|
|
5003
|
+
__metadata("design:paramtypes", []),
|
|
5004
|
+
__metadata("design:returntype", Promise)
|
|
5005
|
+
], ProjectService.prototype, "reconcileEnabledStacks", null);
|
|
5006
|
+
__decorate([
|
|
5007
|
+
RecoveryMutation(),
|
|
5008
|
+
__metadata("design:type", Function),
|
|
5009
|
+
__metadata("design:paramtypes", [String]),
|
|
5010
|
+
__metadata("design:returntype", Promise)
|
|
5011
|
+
], ProjectService.prototype, "loadProjectConfig", null);
|
|
5012
|
+
__decorate([
|
|
5013
|
+
RecoveryMutation(),
|
|
5014
|
+
__metadata("design:type", Function),
|
|
5015
|
+
__metadata("design:paramtypes", [String]),
|
|
5016
|
+
__metadata("design:returntype", Promise)
|
|
5017
|
+
], ProjectService.prototype, "reloadConfig", null);
|
|
3722
5018
|
/**
|
|
3723
5019
|
* How a master's daemon builds this service: the tokens it resolves, in
|
|
3724
5020
|
* order, and the factory that receives them.
|