@revisium/orchestrator 0.1.0 → 0.2.0-alpha.1
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 +78 -0
- package/control-plane/bootstrap.config.json +43 -1
- package/control-plane/default-playbook/catalog/pipelines.json +1250 -7
- package/control-plane/default-playbook/catalog/roles.json +54 -0
- package/dist/api/graphql-api/graphql-ws/pubsub.module.js +0 -1
- package/dist/api/graphql-api/graphql-ws/pubsub.module.js.map +1 -1
- package/dist/api/graphql-api/graphql-ws/run-progress-subscription-poller.service.js +0 -1
- package/dist/api/graphql-api/graphql-ws/run-progress-subscription-poller.service.js.map +1 -1
- package/dist/api/graphql-api/graphql-ws/subscription-bridge.service.js +0 -1
- package/dist/api/graphql-api/graphql-ws/subscription-bridge.service.js.map +1 -1
- package/dist/api/graphql-api/inbox/inbox.resolver.js +12 -0
- package/dist/api/graphql-api/inbox/inbox.resolver.js.map +1 -1
- package/dist/api/graphql-api/inbox/inputs/resolve-gate.input.js +37 -0
- package/dist/api/graphql-api/inbox/inputs/resolve-gate.input.js.map +1 -0
- package/dist/api/graphql-api/pr/model/pr-readiness.model.js +0 -4
- package/dist/api/graphql-api/pr/model/pr-readiness.model.js.map +1 -1
- package/dist/cli/commands/doctor-report.js +0 -18
- package/dist/cli/commands/doctor-report.js.map +1 -1
- package/dist/cli/commands/lifecycle.js +3 -71
- package/dist/cli/commands/lifecycle.js.map +1 -1
- package/dist/cli/commands/mcp.js +0 -14
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/process-tree.js +4 -26
- package/dist/cli/commands/process-tree.js.map +1 -1
- package/dist/cli/commands/revisium-helpers.js +0 -6
- package/dist/cli/commands/revisium-helpers.js.map +1 -1
- package/dist/cli/commands/rogue-reaper.js +0 -34
- package/dist/cli/commands/rogue-reaper.js.map +1 -1
- package/dist/cli/index.js +0 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/program.js +0 -5
- package/dist/cli/program.js.map +1 -1
- package/dist/config.js +0 -25
- package/dist/config.js.map +1 -1
- package/dist/control-plane/bootstrap.js +0 -28
- package/dist/control-plane/bootstrap.js.map +1 -1
- package/dist/control-plane/change-notifications.js +0 -1
- package/dist/control-plane/change-notifications.js.map +1 -1
- package/dist/control-plane/client-transport.js +0 -11
- package/dist/control-plane/client-transport.js.map +1 -1
- package/dist/control-plane/default-playbook-policy.js +64 -4
- package/dist/control-plane/default-playbook-policy.js.map +1 -1
- package/dist/control-plane/definitions.js +8 -17
- package/dist/control-plane/definitions.js.map +1 -1
- package/dist/control-plane/inbox.js +2 -120
- package/dist/control-plane/inbox.js.map +1 -1
- package/dist/control-plane/resolve-cwd.js +0 -61
- package/dist/control-plane/resolve-cwd.js.map +1 -1
- package/dist/control-plane/schema-migration.js +0 -9
- package/dist/control-plane/schema-migration.js.map +1 -1
- package/dist/control-plane/seed-default-playbook.js +5 -70
- package/dist/control-plane/seed-default-playbook.js.map +1 -1
- package/dist/control-plane/steps.js +0 -15
- package/dist/control-plane/steps.js.map +1 -1
- package/dist/e2e/kit/assertions.js +1 -1
- package/dist/e2e/kit/assertions.js.map +1 -1
- package/dist/engine/dbos.service.js +9 -179
- package/dist/engine/dbos.service.js.map +1 -1
- package/dist/engine/ensure-postgres.js +0 -48
- package/dist/engine/ensure-postgres.js.map +1 -1
- package/dist/features/inbox/commands/handlers/inbox-command.handlers.js +16 -0
- package/dist/features/inbox/commands/handlers/inbox-command.handlers.js.map +1 -1
- package/dist/features/inbox/commands/impl/resolve-gate.command.js +7 -0
- package/dist/features/inbox/commands/impl/resolve-gate.command.js.map +1 -0
- package/dist/features/inbox/commands/index.js +2 -1
- package/dist/features/inbox/commands/index.js.map +1 -1
- package/dist/features/inbox/inbox-api.service.js +4 -0
- package/dist/features/inbox/inbox-api.service.js.map +1 -1
- package/dist/host/daemon.js +2 -21
- package/dist/host/daemon.js.map +1 -1
- package/dist/host/dbos-identity.js +0 -31
- package/dist/host/dbos-identity.js.map +1 -1
- package/dist/host/ensure-host.js +1 -37
- package/dist/host/ensure-host.js.map +1 -1
- package/dist/host/ensure-revisium.js +1 -135
- package/dist/host/ensure-revisium.js.map +1 -1
- package/dist/host/host-runtime.js +0 -28
- package/dist/host/host-runtime.js.map +1 -1
- package/dist/host/host.lifecycle.js +0 -27
- package/dist/host/host.lifecycle.js.map +1 -1
- package/dist/host/queue-ownership.js +0 -42
- package/dist/host/queue-ownership.js.map +1 -1
- package/dist/host/queue-poller-census.js +1 -28
- package/dist/host/queue-poller-census.js.map +1 -1
- package/dist/http/graphql-host.module.js +0 -3
- package/dist/http/graphql-host.module.js.map +1 -1
- package/dist/mcp/mcp-capabilities.js +20 -14
- package/dist/mcp/mcp-capabilities.js.map +1 -1
- package/dist/mcp/mcp-facade.service.js +44 -23
- package/dist/mcp/mcp-facade.service.js.map +1 -1
- package/dist/mcp/mcp-http.service.js +0 -18
- package/dist/mcp/mcp-http.service.js.map +1 -1
- package/dist/mcp/mcp-tools.js +28 -40
- package/dist/mcp/mcp-tools.js.map +1 -1
- package/dist/observability/activity-signal.js +0 -4
- package/dist/observability/activity-signal.js.map +1 -1
- package/dist/observability/agent-activity-reporter.js +0 -4
- package/dist/observability/agent-activity-reporter.js.map +1 -1
- package/dist/pipeline/await-human.js +13 -43
- package/dist/pipeline/await-human.js.map +1 -1
- package/dist/pipeline/data-driven-task.workflow.js +121 -238
- package/dist/pipeline/data-driven-task.workflow.js.map +1 -1
- package/dist/pipeline/data-driven-template.js +0 -25
- package/dist/pipeline/data-driven-template.js.map +1 -1
- package/dist/pipeline/pipeline.module.js +0 -12
- package/dist/pipeline/pipeline.module.js.map +1 -1
- package/dist/pipeline/pipeline.service.js +10 -99
- package/dist/pipeline/pipeline.service.js.map +1 -1
- package/dist/pipeline-core/index.js +1 -13
- package/dist/pipeline-core/index.js.map +1 -1
- package/dist/pipeline-core/interpret.js +21 -94
- package/dist/pipeline-core/interpret.js.map +1 -1
- package/dist/pipeline-core/kit/assertions.js +0 -19
- package/dist/pipeline-core/kit/assertions.js.map +1 -1
- package/dist/pipeline-core/kit/builders.js +2 -21
- package/dist/pipeline-core/kit/builders.js.map +1 -1
- package/dist/pipeline-core/kit/drive.js +1 -31
- package/dist/pipeline-core/kit/drive.js.map +1 -1
- package/dist/pipeline-core/kit/fixtures.js +26 -86
- package/dist/pipeline-core/kit/fixtures.js.map +1 -1
- package/dist/pipeline-core/kit/index.js +0 -8
- package/dist/pipeline-core/kit/index.js.map +1 -1
- package/dist/pipeline-core/types.js +13 -52
- package/dist/pipeline-core/types.js.map +1 -1
- package/dist/pipeline-core/validate-capability.js +0 -3
- package/dist/pipeline-core/validate-capability.js.map +1 -1
- package/dist/pipeline-core/validate-conflicts.js +0 -9
- package/dist/pipeline-core/validate-conflicts.js.map +1 -1
- package/dist/pipeline-core/validate-dataflow.js +2 -21
- package/dist/pipeline-core/validate-dataflow.js.map +1 -1
- package/dist/pipeline-core/validate-diff.js +3 -36
- package/dist/pipeline-core/validate-diff.js.map +1 -1
- package/dist/pipeline-core/validate-edges.js +0 -6
- package/dist/pipeline-core/validate-edges.js.map +1 -1
- package/dist/pipeline-core/validate-graph.js +0 -11
- package/dist/pipeline-core/validate-graph.js.map +1 -1
- package/dist/pipeline-core/validate-loops.js +37 -44
- package/dist/pipeline-core/validate-loops.js.map +1 -1
- package/dist/pipeline-core/validate-parallel.js +9 -15
- package/dist/pipeline-core/validate-parallel.js.map +1 -1
- package/dist/pipeline-core/validate-sink.js +0 -7
- package/dist/pipeline-core/validate-sink.js.map +1 -1
- package/dist/pipeline-core/validate-topology.js +1 -20
- package/dist/pipeline-core/validate-topology.js.map +1 -1
- package/dist/pipeline-core/validate-verdict.js +52 -7
- package/dist/pipeline-core/validate-verdict.js.map +1 -1
- package/dist/pipeline-core/validate.js +5 -29
- package/dist/pipeline-core/validate.js.map +1 -1
- package/dist/playbook/catalog-loader.js +8 -3
- package/dist/playbook/catalog-loader.js.map +1 -1
- package/dist/playbook/import-mapper.js +1 -9
- package/dist/playbook/import-mapper.js.map +1 -1
- package/dist/poller/pr-readiness-core.js +0 -74
- package/dist/poller/pr-readiness-core.js.map +1 -1
- package/dist/revisium/inbox.service.js +0 -24
- package/dist/revisium/inbox.service.js.map +1 -1
- package/dist/revisium/playbooks.service.js +2 -4
- package/dist/revisium/playbooks.service.js.map +1 -1
- package/dist/revisium/revisium.module.js +0 -12
- package/dist/revisium/revisium.module.js.map +1 -1
- package/dist/revisium/roles.service.js +0 -6
- package/dist/revisium/roles.service.js.map +1 -1
- package/dist/revisium/run.service.js +1 -75
- package/dist/revisium/run.service.js.map +1 -1
- package/dist/revisium/tokens.js +0 -4
- package/dist/revisium/tokens.js.map +1 -1
- package/dist/run/append-event.js +0 -34
- package/dist/run/append-event.js.map +1 -1
- package/dist/run/block-run.js.map +1 -1
- package/dist/run/cancel-run.js +0 -33
- package/dist/run/cancel-run.js.map +1 -1
- package/dist/run/complete-run.js.map +1 -1
- package/dist/run/create-run.js +0 -7
- package/dist/run/create-run.js.map +1 -1
- package/dist/run/fail-run.js +0 -3
- package/dist/run/fail-run.js.map +1 -1
- package/dist/run/inspect-run.js +0 -26
- package/dist/run/inspect-run.js.map +1 -1
- package/dist/run/run-outputs.js +0 -19
- package/dist/run/run-outputs.js.map +1 -1
- package/dist/run/terminal-run-status.js +0 -12
- package/dist/run/terminal-run-status.js.map +1 -1
- package/dist/runners/claude-code.service.js +0 -11
- package/dist/runners/claude-code.service.js.map +1 -1
- package/dist/runners/codex.service.js +0 -9
- package/dist/runners/codex.service.js.map +1 -1
- package/dist/runners/gh-identity.js +0 -52
- package/dist/runners/gh-identity.js.map +1 -1
- package/dist/runners/integrator-branch-naming.js +3 -11
- package/dist/runners/integrator-branch-naming.js.map +1 -1
- package/dist/runners/integrator-git.js +0 -25
- package/dist/runners/integrator-git.js.map +1 -1
- package/dist/runners/integrator-remote.js +0 -2
- package/dist/runners/integrator-remote.js.map +1 -1
- package/dist/runners/integrator-types.js +0 -7
- package/dist/runners/integrator-types.js.map +1 -1
- package/dist/runners/integrator.js +44 -176
- package/dist/runners/integrator.js.map +1 -1
- package/dist/runners/runner.module.js +0 -6
- package/dist/runners/runner.module.js.map +1 -1
- package/dist/runners/tokens.js +0 -4
- package/dist/runners/tokens.js.map +1 -1
- package/dist/runners/worktree.service.js +1 -17
- package/dist/runners/worktree.service.js.map +1 -1
- package/dist/task-control-plane/run-watch.service.js +50 -174
- package/dist/task-control-plane/run-watch.service.js.map +1 -1
- package/dist/task-control-plane/task-control-plane-api.service.js +134 -36
- package/dist/task-control-plane/task-control-plane-api.service.js.map +1 -1
- package/dist/worker/build-context.js +0 -16
- package/dist/worker/build-context.js.map +1 -1
- package/dist/worker/claude-code-runner.js +9 -42
- package/dist/worker/claude-code-runner.js.map +1 -1
- package/dist/worker/codex-runner.js +88 -22
- package/dist/worker/codex-runner.js.map +1 -1
- package/dist/worker/git-worktree-manager.js +20 -67
- package/dist/worker/git-worktree-manager.js.map +1 -1
- package/dist/worker/process-executor.js +0 -12
- package/dist/worker/process-executor.js.map +1 -1
- package/dist/worker/result-envelope.js +33 -49
- package/dist/worker/result-envelope.js.map +1 -1
- package/dist/worker/runner-dispatch.js +0 -3
- package/dist/worker/runner-dispatch.js.map +1 -1
- package/dist/worker/runner.js.map +1 -1
- package/dist/worker/stub-runner.js +0 -7
- package/dist/worker/stub-runner.js.map +1 -1
- package/package.json +2 -2
package/dist/host/daemon.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/host/daemon.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/host/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,38 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pinned DBOS identity for the host daemon (slice 140, Part A Layer 2).
|
|
3
|
-
*
|
|
4
|
-
* DBOS derives two identifiers from the environment AT IMPORT TIME (dbos-sdk `utils.js` `globalParams`
|
|
5
|
-
* is a module-level literal: `executorID = process.env.DBOS__VMID || 'local'`,
|
|
6
|
-
* `appVersion = process.env.DBOS__APPVERSION || ''`). Left unset, every daemon is `executorID='local'`
|
|
7
|
-
* with a source-MD5 appVersion — indistinguishable in `pg_stat_activity` and brittle across releases.
|
|
8
|
-
* We pin both, so:
|
|
9
|
-
* - executorID = `revo-<profile>` makes each daemon's DBOS connections self-identifying in
|
|
10
|
-
* `pg_stat_activity.application_name` (`dbos_transact_<executorID>_<appVersion>`), which is the
|
|
11
|
-
* ONLY way `revo doctor` can see a rogue/legacy poller that holds no inbound port.
|
|
12
|
-
* - appVersion = a value WE control, decoupled from the npm release version. DBOS recovery keys on
|
|
13
|
-
* a HARD `application_version` equality, so a per-release version would strand in-flight PENDING
|
|
14
|
-
* work on every alpha bump. Pinning a stable workflow version keeps recovery working across a
|
|
15
|
-
* routine upgrade; it is bumped ONLY when the durable workflow/step SOURCE actually changes.
|
|
16
|
-
*
|
|
17
|
-
* Because `globalParams` is read at import time, the pin must be present in the daemon process's
|
|
18
|
-
* environment from the start — it is applied to the detached child's env in `ensure-host.spawnDaemon`,
|
|
19
|
-
* never mutated after the dbos-sdk module has loaded.
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
|
-
* Pinned DBOS `appVersion`. NOT the npm package version — bump this ONLY when the durable DBOS
|
|
23
|
-
* workflow/step function SOURCE changes (which moves the recovery/dequeue version boundary). A routine
|
|
24
|
-
* release that does not touch workflow-step code keeps this value so in-flight work recovers cleanly.
|
|
25
|
-
*/
|
|
26
1
|
export const DBOS_WORKFLOW_VERSION = '1';
|
|
27
|
-
/** Stable per-profile DBOS executor id (`revo-<profile>`) — the owner identity the census matches on. */
|
|
28
2
|
export function dbosExecutorId(profile) {
|
|
29
3
|
return `revo-${profile}`;
|
|
30
4
|
}
|
|
31
|
-
/**
|
|
32
|
-
* The `DBOS__VMID` / `DBOS__APPVERSION` env pin for a profile's daemon. An explicitly-SET value in
|
|
33
|
-
* `env` wins (so a custom layout / test can override) — including an explicit empty string, hence `??`
|
|
34
|
-
* not `||`; only an absent (undefined) var falls back to the pinned defaults.
|
|
35
|
-
*/
|
|
36
5
|
export function dbosEnvPin(profile, env = process.env) {
|
|
37
6
|
return {
|
|
38
7
|
DBOS__VMID: env['DBOS__VMID'] ?? dbosExecutorId(profile),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dbos-identity.js","sourceRoot":"","sources":["../../src/host/dbos-identity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dbos-identity.js","sourceRoot":"","sources":["../../src/host/dbos-identity.ts"],"names":[],"mappings":"AAsBA,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAGzC,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,QAAQ,OAAO,EAAE,CAAC;AAC3B,CAAC;AAKD,MAAM,UAAU,UAAU,CACxB,OAAe,EACf,MAAyB,OAAO,CAAC,GAAG;IAEpC,OAAO;QACL,UAAU,EAAE,GAAG,CAAC,YAAY,CAAC,IAAI,cAAc,CAAC,OAAO,CAAC;QACxD,gBAAgB,EAAE,GAAG,CAAC,kBAAkB,CAAC,IAAI,qBAAqB;KACnE,CAAC;AACJ,CAAC"}
|
package/dist/host/ensure-host.js
CHANGED
|
@@ -1,30 +1,17 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ensureHost() — attach-or-spawn the long-lived Revo host daemon (ADR 0006), mirroring the
|
|
3
|
-
* three-state model of ensureRevisium:
|
|
4
|
-
* 1. HEALTHY → attach, return alreadyRunning=true.
|
|
5
|
-
* 2. ALIVE BUT UNHEALTHY → wait for it to finish booting; never spawn a duplicate (the GraphQL
|
|
6
|
-
* port would collide), then throw if it stays unhealthy.
|
|
7
|
-
* 3. NO LIVE DAEMON → spawn detached + unref, wait for GraphQL health.
|
|
8
|
-
*
|
|
9
|
-
* The daemon is the single DBOS owner; this helper is the ONLY way the stack acquires a host.
|
|
10
|
-
*/
|
|
11
1
|
import { spawn } from 'node:child_process';
|
|
12
2
|
import { closeSync, openSync } from 'node:fs';
|
|
13
3
|
import { getConfig, isAlive, resolveDefaultGraphqlPort } from '../config.js';
|
|
14
4
|
import { hostCodeVersion, isHostRunning, readHostRuntime, removeHostRuntime, } from './host-runtime.js';
|
|
15
5
|
import { dbosEnvPin } from './dbos-identity.js';
|
|
16
6
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
17
|
-
/** Budget to let an alive-but-not-yet-healthy daemon finish booting before giving up. */
|
|
18
7
|
const DEFAULT_RECHECK_MS = 20_000;
|
|
19
8
|
const POLL_INTERVAL_MS = 300;
|
|
20
|
-
/** The GraphQL port the daemon listens on — must match startGraphqlHost's resolution. */
|
|
21
9
|
export function expectedGraphqlPort() {
|
|
22
10
|
const env = process.env['REVO_GRAPHQL_PORT'];
|
|
23
11
|
if (env && /^\d+$/.test(env.trim()))
|
|
24
12
|
return Number(env.trim());
|
|
25
13
|
return resolveDefaultGraphqlPort();
|
|
26
14
|
}
|
|
27
|
-
/** GraphQL liveness probe: the daemon answers a trivial query on its port. */
|
|
28
15
|
export async function isGraphqlHealthy(port) {
|
|
29
16
|
try {
|
|
30
17
|
const res = await fetch(`http://127.0.0.1:${port}/graphql`, {
|
|
@@ -39,11 +26,6 @@ export async function isGraphqlHealthy(port) {
|
|
|
39
26
|
return false;
|
|
40
27
|
}
|
|
41
28
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Ready = `host.json` present with a LIVE pid AND GraphQL answering. The daemon writes host.json
|
|
44
|
-
* last (after the control-plane bootstrap), so readiness implies the whole stack is usable — not
|
|
45
|
-
* merely that the GraphQL socket is open.
|
|
46
|
-
*/
|
|
47
29
|
async function waitForReady(timeoutMs) {
|
|
48
30
|
const deadline = Date.now() + timeoutMs;
|
|
49
31
|
for (;;) {
|
|
@@ -55,11 +37,6 @@ async function waitForReady(timeoutMs) {
|
|
|
55
37
|
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
56
38
|
}
|
|
57
39
|
}
|
|
58
|
-
/**
|
|
59
|
-
* Build the argv to re-invoke THIS CLI as the detached daemon. Handles dev (tsx running the `.ts`
|
|
60
|
-
* source — node needs the tsx loader) vs prod (node running the compiled `.js` entry directly).
|
|
61
|
-
* `entry` defaults to this process's script; it is a parameter so the dev/prod split is unit-testable.
|
|
62
|
-
*/
|
|
63
40
|
export function daemonSpawnArgv(entry = process.argv[1]) {
|
|
64
41
|
const daemonArgs = ['__daemon'];
|
|
65
42
|
if (entry.endsWith('.ts')) {
|
|
@@ -70,44 +47,31 @@ export function daemonSpawnArgv(entry = process.argv[1]) {
|
|
|
70
47
|
function spawnDaemon() {
|
|
71
48
|
const out = openSync(getConfig().hostLogFile, 'a');
|
|
72
49
|
const [cmd, args] = daemonSpawnArgv();
|
|
73
|
-
// env inheritance carries the resolved profile (REVO_PROFILE / REVO_* knobs) to the daemon, PLUS the
|
|
74
|
-
// pinned DBOS identity (DBOS__VMID/DBOS__APPVERSION). dbos-sdk reads those at IMPORT time, so they
|
|
75
|
-
// must be in the child's env from the start — set here, never mutated after launch (slice 140).
|
|
76
50
|
const env = { ...process.env, ...dbosEnvPin(getConfig().profile, process.env) };
|
|
77
51
|
const child = spawn(cmd, args, { detached: true, stdio: ['ignore', out, out], env });
|
|
78
52
|
closeSync(out);
|
|
79
53
|
if (!child.pid)
|
|
80
54
|
throw new Error('Failed to spawn Revo host daemon: spawn returned no pid');
|
|
81
|
-
child.unref();
|
|
55
|
+
child.unref();
|
|
82
56
|
}
|
|
83
|
-
/** Attach to the running host daemon, or spawn one if absent — the only way the stack acquires a host. */
|
|
84
57
|
export async function ensureHost(options = {}) {
|
|
85
58
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
86
59
|
const recheckMs = options.recheckMs ?? DEFAULT_RECHECK_MS;
|
|
87
60
|
const existing = readHostRuntime();
|
|
88
61
|
if (existing && isHostRunning()) {
|
|
89
62
|
if (await isGraphqlHealthy(existing.graphqlPort)) {
|
|
90
|
-
// Version-check (slice 139): surface a daemon running a DIFFERENT build than this install so it
|
|
91
|
-
// can't silently serve stale behavior. We warn (not auto-kill) to avoid disrupting in-flight runs;
|
|
92
|
-
// `revo restart` (or `doctor`) replaces it. A 0.0.0 dev checkout doesn't change version → no warn.
|
|
93
63
|
if (existing.version !== undefined && existing.version !== hostCodeVersion()) {
|
|
94
64
|
console.warn(`[host] attached to a daemon running version ${existing.version}, but this build is ` +
|
|
95
65
|
`${hostCodeVersion()} — run \`revo restart\` to replace the stale daemon.`);
|
|
96
66
|
}
|
|
97
67
|
return { runtime: existing, alreadyRunning: true };
|
|
98
68
|
}
|
|
99
|
-
// Alive but not ready — likely mid-boot/bootstrap. Wait; never spawn a duplicate (port collision).
|
|
100
69
|
const ready = await waitForReady(recheckMs);
|
|
101
70
|
if (ready)
|
|
102
71
|
return { runtime: ready, alreadyRunning: true };
|
|
103
72
|
throw new Error(`Revo host daemon (pid ${existing.pid}) is alive but not healthy on GraphQL port ` +
|
|
104
73
|
`${existing.graphqlPort}. Stop it with \`revo stop\` and retry.`);
|
|
105
74
|
}
|
|
106
|
-
// No live daemon — clear a stale host.json (dead pid), then spawn a fresh detached one.
|
|
107
|
-
// The check-then-spawn is not atomic, but the daemon's advisory-lock singleton (slice 139,
|
|
108
|
-
// queue-ownership.ts) makes a concurrent cold-start safe: if two ensureHost() callers both spawn
|
|
109
|
-
// `__daemon`, only one wins the per-profile lock and lives; the loser exits before DBOS/queue, and
|
|
110
|
-
// every waiter observes the winner's host.json via waitForReady.
|
|
111
75
|
if (existing)
|
|
112
76
|
removeHostRuntime();
|
|
113
77
|
spawnDaemon();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ensure-host.js","sourceRoot":"","sources":["../../src/host/ensure-host.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ensure-host.js","sourceRoot":"","sources":["../../src/host/ensure-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,90 +1,24 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ensureRevisium() — shared auto-start helper (Round 3).
|
|
3
|
-
*
|
|
4
|
-
* Models three distinct states (F7):
|
|
5
|
-
* 1. HEALTHY → no-op, return proven runtime.
|
|
6
|
-
* 2. NO LIVE DAEMON (null runtime or dead pid) → fresh detached spawn + wait.
|
|
7
|
-
* 3. ALIVE BUT UNHEALTHY → bounded re-poll, then throw (never orphan a live process).
|
|
8
|
-
*
|
|
9
|
-
* Called by BOTH `revisium start` command and host bootstrap (DRY extraction, F9).
|
|
10
|
-
*
|
|
11
|
-
* TOCTOU / compare-and-delete (F16, F19, F21):
|
|
12
|
-
* In state 2 we re-classify IMMEDIATELY before any removal. decideRuntimeAction()
|
|
13
|
-
* returns a RuntimeDecision that encodes BOTH the action AND shouldRemove — so the
|
|
14
|
-
* identity rule (only remove when the recheck still matches the stale snapshot by pid
|
|
15
|
-
* AND startedAt) lives in one tested place and the caller cannot drift from it (F21).
|
|
16
|
-
*
|
|
17
|
-
* The same removeRuntimeIfMatches() helper is shared by the state-2 pre-spawn cleanup
|
|
18
|
-
* AND the spawn-timeout cleanup (F19), so neither path can accidentally delete a runtime
|
|
19
|
-
* that belongs to a concurrently-started daemon.
|
|
20
|
-
*
|
|
21
|
-
* Known limitation — concurrent cold-start (deferred per ТЗ §3.3.5, slice 0001):
|
|
22
|
-
* Two host processes starting with no live daemon in the same narrow window can still
|
|
23
|
-
* race to spawn — the compare-and-delete re-check narrows the orphan window but does
|
|
24
|
-
* NOT serialize spawns. Full cross-process file-locking is intentionally deferred to a
|
|
25
|
-
* future slice. The practical guard is the re-check plus the fact that the host path
|
|
26
|
-
* only runs for explicit dev:* invocations.
|
|
27
|
-
*/
|
|
28
1
|
import { spawn } from 'node:child_process';
|
|
29
2
|
import { closeSync, existsSync, openSync, readFileSync, writeFileSync } from 'node:fs';
|
|
30
3
|
import { createRequire } from 'node:module';
|
|
31
4
|
import { baseUrl, findFreePort, getConfig, healthUrl, isAlive, isHealthy, readRuntime, removeRuntime, } from '../cli/config.js';
|
|
32
5
|
import { killTree, parsePort, tailLines, waitForExit, waitHealthy, } from '../cli/commands/revisium-helpers.js';
|
|
33
6
|
const require = createRequire(import.meta.url);
|
|
34
|
-
/** Default cold-start timeout in ms (120 s — matches the existing waitHealthy default). */
|
|
35
7
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
36
|
-
/** Default re-poll budget for the alive-but-unhealthy case (consensus MINOR, round 4). */
|
|
37
8
|
const DEFAULT_RECHECK_MS = 20_000;
|
|
38
|
-
/**
|
|
39
|
-
* Remove runtime.json ONLY if the current file on disk still matches the given snapshot
|
|
40
|
-
* (same pid AND startedAt). Shared by the state-2 pre-spawn path and the spawn-timeout
|
|
41
|
-
* cleanup path (F19 / F21 DRY) so neither can accidentally delete a runtime that belongs
|
|
42
|
-
* to a concurrently-started daemon.
|
|
43
|
-
*
|
|
44
|
-
* If the file has been replaced (different identity) or already removed (null), this is a
|
|
45
|
-
* safe no-op — the file either belongs to another process or was already cleaned up.
|
|
46
|
-
*
|
|
47
|
-
* Exported (@internal) for unit testing; production callers should use only the internal
|
|
48
|
-
* call sites (state-2 pre-spawn cleanup and spawn-timeout cleanup).
|
|
49
|
-
*/
|
|
50
9
|
export function removeRuntimeIfMatches(snapshot) {
|
|
51
10
|
const current = readRuntime();
|
|
52
11
|
if (current?.pid === snapshot.pid && current?.startedAt === snapshot.startedAt) {
|
|
53
12
|
removeRuntime();
|
|
54
13
|
}
|
|
55
14
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Decide what action to take in state 2 ("no live daemon" from initial classification)
|
|
58
|
-
* given the stale snapshot and an immediate re-read immediately before any removal.
|
|
59
|
-
*
|
|
60
|
-
* Pure function (no side effects, no I/O) — unit-testable in isolation (F18 / F16 / F21).
|
|
61
|
-
*
|
|
62
|
-
* Compare-and-delete contract (F16, F21):
|
|
63
|
-
* - shouldRemove is true ONLY when the re-read runtime STILL matches the stale snapshot
|
|
64
|
-
* (same pid AND startedAt). This prevents deleting a live runtime written concurrently
|
|
65
|
-
* BETWEEN our initial dead-pid read and this re-check.
|
|
66
|
-
* - If the re-check shows a DIFFERENT (or new) live pid, the action routes into alive
|
|
67
|
-
* paths ('return-running' or 'repoll') and shouldRemove is always false.
|
|
68
|
-
*
|
|
69
|
-
* @param stalePidSnapshot - The RuntimeState we observed initially (dead pid), or null.
|
|
70
|
-
* @param recheck - The result of readRuntime() immediately before removal.
|
|
71
|
-
* @param recheckAlive - Whether recheck.pid is alive (ignored when recheck is null).
|
|
72
|
-
* @param recheckHealthy - Whether recheck daemon is healthy (ignored when recheck is null).
|
|
73
|
-
*/
|
|
74
15
|
export function decideRuntimeAction(stalePidSnapshot, recheck, recheckAlive, recheckHealthy) {
|
|
75
16
|
if (recheck && recheckAlive) {
|
|
76
|
-
// A concurrent process wrote a live runtime — do NOT delete or spawn over it.
|
|
77
17
|
if (recheckHealthy)
|
|
78
18
|
return { action: 'return-running', shouldRemove: false };
|
|
79
19
|
return { action: 'repoll', shouldRemove: false };
|
|
80
20
|
}
|
|
81
|
-
// No live runtime after re-read. Determine whether we may remove the stale file.
|
|
82
21
|
if (stalePidSnapshot !== null) {
|
|
83
|
-
// Compare-and-delete identity check (F21): only set shouldRemove=true when the
|
|
84
|
-
// recheck runtime STILL matches the stale snapshot (same pid AND startedAt).
|
|
85
|
-
// - recheck null → file already cleaned up by another process; skip remove but proceed.
|
|
86
|
-
// - recheck present but different identity → new stale entry from a different past run;
|
|
87
|
-
// our snapshot doesn't own it — leave it for its owner.
|
|
88
22
|
const matchesSnapshot = recheck !== null &&
|
|
89
23
|
recheck.pid === stalePidSnapshot.pid &&
|
|
90
24
|
recheck.startedAt === stalePidSnapshot.startedAt;
|
|
@@ -92,21 +26,6 @@ export function decideRuntimeAction(stalePidSnapshot, recheck, recheckAlive, rec
|
|
|
92
26
|
}
|
|
93
27
|
return { action: 'spawn', shouldRemove: false };
|
|
94
28
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Classify the daemon state given a snapshot of: the runtime file, whether the
|
|
97
|
-
* recorded pid is alive, and whether the daemon responds to health checks.
|
|
98
|
-
*
|
|
99
|
-
* Pure function (no side effects, no I/O) — unit-testable in isolation (F15).
|
|
100
|
-
*
|
|
101
|
-
* - 'healthy' → pid alive AND health check passed.
|
|
102
|
-
* - 'alive-unhealthy' → pid alive BUT health check failed (never spawn a second daemon).
|
|
103
|
-
* - 'no-live-daemon' → no runtime.json OR recorded pid is dead (safe to spawn fresh).
|
|
104
|
-
*
|
|
105
|
-
* Note: the alive-unhealthy case intentionally includes a concurrent-start scenario
|
|
106
|
-
* where the runtime was written but the daemon is mid-startup (F11): returning
|
|
107
|
-
* 'alive-unhealthy' rather than 'no-live-daemon' routes callers into the bounded
|
|
108
|
-
* re-poll path instead of unconditionally spawning a second daemon.
|
|
109
|
-
*/
|
|
110
29
|
export function classifyRuntimeState(rt, alive, healthy) {
|
|
111
30
|
if (!rt || !alive)
|
|
112
31
|
return 'no-live-daemon';
|
|
@@ -114,13 +33,6 @@ export function classifyRuntimeState(rt, alive, healthy) {
|
|
|
114
33
|
return 'healthy';
|
|
115
34
|
return 'alive-unhealthy';
|
|
116
35
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Handle state 3: alive-but-unhealthy daemon.
|
|
119
|
-
*
|
|
120
|
-
* Performs a bounded re-poll. Returns `{alreadyRunning:true}` if the daemon becomes
|
|
121
|
-
* healthy within `recheckMs`. Throws with an actionable message otherwise — never
|
|
122
|
-
* removes runtime.json, never spawns (would orphan a live process).
|
|
123
|
-
*/
|
|
124
36
|
async function handleAliveUnhealthy(rt, recheckMs) {
|
|
125
37
|
const becameHealthy = await waitHealthy(healthUrl(rt.httpPort), recheckMs);
|
|
126
38
|
if (becameHealthy) {
|
|
@@ -131,10 +43,6 @@ async function handleAliveUnhealthy(rt, recheckMs) {
|
|
|
131
43
|
}
|
|
132
44
|
throw new Error(`Revisium (pid ${rt.pid}) is running but unhealthy — run \`revo revisium stop\` and retry`);
|
|
133
45
|
}
|
|
134
|
-
/**
|
|
135
|
-
* Perform the "re-poll" branch of state 2: a concurrent start wrote a live-but-unhealthy
|
|
136
|
-
* runtime — poll for late-up rather than spawning a second daemon.
|
|
137
|
-
*/
|
|
138
46
|
async function recheckRepoll(rtRecheck, recheckMs) {
|
|
139
47
|
const becameHealthy = await waitHealthy(healthUrl(rtRecheck.httpPort), recheckMs);
|
|
140
48
|
if (becameHealthy) {
|
|
@@ -145,14 +53,6 @@ async function recheckRepoll(rtRecheck, recheckMs) {
|
|
|
145
53
|
}
|
|
146
54
|
throw new Error(`Revisium (pid ${rtRecheck.pid}) is running but unhealthy — run \`revo revisium stop\` and retry`);
|
|
147
55
|
}
|
|
148
|
-
/**
|
|
149
|
-
* Spawn the standalone daemon (detached + unref'd) and wait for health.
|
|
150
|
-
*
|
|
151
|
-
* Writes extended runtime.json (F8: includes dataDir). On health timeout, kills the
|
|
152
|
-
* spawned child, removes its runtime.json (compare-and-delete — F19), and throws.
|
|
153
|
-
*
|
|
154
|
-
* @returns EnsureResult from the freshly-written runtime.json (F3: pid-proven ports).
|
|
155
|
-
*/
|
|
156
56
|
async function startAndWaitForHealth(cfg, timeoutMs) {
|
|
157
57
|
const { httpPort, pgPort, dataDir, logFile, runtimeFile } = cfg;
|
|
158
58
|
const entry = require.resolve('@revisium/standalone/bin/revisium-standalone.js');
|
|
@@ -162,14 +62,12 @@ async function startAndWaitForHealth(cfg, timeoutMs) {
|
|
|
162
62
|
if (!child.pid) {
|
|
163
63
|
throw new Error('Failed to start standalone Revisium: spawn returned no pid');
|
|
164
64
|
}
|
|
165
|
-
child.unref();
|
|
166
|
-
// Capture the exact identity of the runtime WE just wrote (F19).
|
|
65
|
+
child.unref();
|
|
167
66
|
const spawnStartedAt = new Date().toISOString();
|
|
168
67
|
writeFileSync(runtimeFile, JSON.stringify({ httpPort, pgPort, pid: child.pid, startedAt: spawnStartedAt, dataDir }, null, 2));
|
|
169
68
|
const spawnSnapshot = { pid: child.pid, startedAt: spawnStartedAt };
|
|
170
69
|
const spawnHealthy = await waitHealthy(healthUrl(httpPort), timeoutMs);
|
|
171
70
|
if (!spawnHealthy) {
|
|
172
|
-
// F19: Only removeRuntime() when the file still identifies OUR child (compare-and-delete).
|
|
173
71
|
const logTail = tailLines(logFile, 20);
|
|
174
72
|
killTree(child.pid, 'SIGTERM');
|
|
175
73
|
await waitForExit(child.pid, 20_000);
|
|
@@ -179,24 +77,11 @@ async function startAndWaitForHealth(cfg, timeoutMs) {
|
|
|
179
77
|
throw new Error(`Revisium did not become healthy on ${baseUrl(httpPort)} within ${timeoutMs / 1000}s` +
|
|
180
78
|
` — see \`revo revisium logs\`\n${logTail}`);
|
|
181
79
|
}
|
|
182
|
-
// Re-read the freshly-written runtime.json (F3: pid-proven ports).
|
|
183
80
|
const written = readRuntime();
|
|
184
81
|
if (!written)
|
|
185
82
|
throw new Error('runtime.json disappeared after successful start');
|
|
186
83
|
return { runtime: written, alreadyRunning: false };
|
|
187
84
|
}
|
|
188
|
-
/**
|
|
189
|
-
* Ensure the Revisium standalone daemon is running and healthy.
|
|
190
|
-
*
|
|
191
|
-
* Three-state logic (F7):
|
|
192
|
-
* - State 1: recorded pid alive + healthy → return immediately (alreadyRunning = true).
|
|
193
|
-
* - State 2: no runtime.json OR recorded pid is dead → removeRuntime if stale, spawn fresh.
|
|
194
|
-
* - State 3: recorded pid alive but unhealthy → bounded re-poll (recheckMs), then THROW
|
|
195
|
-
* (never removeRuntime, never spawn — leaves the live process for `revo revisium stop`).
|
|
196
|
-
*
|
|
197
|
-
* @returns EnsureResult with the pid-proven runtime (fresh or existing).
|
|
198
|
-
* @throws if the daemon is alive-but-unhealthy after re-poll, or if spawn times out.
|
|
199
|
-
*/
|
|
200
85
|
export async function ensureRevisium(options = {}) {
|
|
201
86
|
const { timeoutMs = DEFAULT_TIMEOUT_MS, recheckMs = DEFAULT_RECHECK_MS } = options;
|
|
202
87
|
const config = getConfig();
|
|
@@ -204,19 +89,12 @@ export async function ensureRevisium(options = {}) {
|
|
|
204
89
|
const alive = rt ? isAlive(rt.pid) : false;
|
|
205
90
|
const healthy = rt && alive ? await isHealthy(rt.httpPort) : false;
|
|
206
91
|
const stateClass = classifyRuntimeState(rt, alive, healthy);
|
|
207
|
-
// ── State 1: healthy ──────────────────────────────────────────────────────
|
|
208
92
|
if (stateClass === 'healthy') {
|
|
209
93
|
return { runtime: rt, alreadyRunning: true };
|
|
210
94
|
}
|
|
211
|
-
// ── State 3: alive but unhealthy ─────────────────────────────────────────
|
|
212
95
|
if (stateClass === 'alive-unhealthy') {
|
|
213
96
|
return handleAliveUnhealthy(rt, recheckMs);
|
|
214
97
|
}
|
|
215
|
-
// ── State 2: no live daemon ───────────────────────────────────────────────
|
|
216
|
-
//
|
|
217
|
-
// F16 / F21 compare-and-delete: re-classify IMMEDIATELY before any removal.
|
|
218
|
-
// decideRuntimeAction() encodes both the action AND shouldRemove so the identity
|
|
219
|
-
// check lives in one tested pure function (F21).
|
|
220
98
|
const rtRecheck = readRuntime();
|
|
221
99
|
const recheckAlive = rtRecheck ? isAlive(rtRecheck.pid) : false;
|
|
222
100
|
const recheckHealthy = rtRecheck && recheckAlive ? await isHealthy(rtRecheck.httpPort) : false;
|
|
@@ -227,13 +105,9 @@ export async function ensureRevisium(options = {}) {
|
|
|
227
105
|
if (decision.action === 'repoll') {
|
|
228
106
|
return recheckRepoll(rtRecheck, recheckMs);
|
|
229
107
|
}
|
|
230
|
-
// action === 'remove-and-spawn' or 'spawn'
|
|
231
|
-
// F22: removeRuntimeIfMatches re-reads and checks identity AT DELETE TIME.
|
|
232
108
|
if (decision.shouldRemove)
|
|
233
109
|
removeRuntimeIfMatches(rtRecheck);
|
|
234
110
|
const httpPort = await findFreePort(parsePort(options.port, config.preferredPort));
|
|
235
|
-
// CR5: Two independent findFreePort calls can return the same port when their search
|
|
236
|
-
// bases overlap. If they collide, re-pick pgPort from httpPort+1 to guarantee distinct ports.
|
|
237
111
|
let pgPort = await findFreePort(parsePort(options.pgPort, config.preferredPgPort));
|
|
238
112
|
if (pgPort === httpPort) {
|
|
239
113
|
pgPort = await findFreePort(httpPort + 1);
|
|
@@ -241,21 +115,13 @@ export async function ensureRevisium(options = {}) {
|
|
|
241
115
|
const dataDir = options.data ?? config.dataDir;
|
|
242
116
|
return startAndWaitForHealth({ httpPort, pgPort, dataDir, logFile: config.logFile, runtimeFile: config.runtimeFile }, timeoutMs);
|
|
243
117
|
}
|
|
244
|
-
/** Maximum valid TCP port number (inclusive). */
|
|
245
118
|
const MAX_TCP_PORT = 65535;
|
|
246
|
-
/**
|
|
247
|
-
* Read the postmaster.pid file to cross-check the pg port from runtime.json (F3).
|
|
248
|
-
* Returns the pg port if found, or null if the file is absent (older standalone layout).
|
|
249
|
-
*
|
|
250
|
-
* CR6: Valid port range is 1–65535 (inclusive). Out-of-range values (e.g. 70000) return null.
|
|
251
|
-
*/
|
|
252
119
|
export function readPostmasterPgPort(dataDir) {
|
|
253
120
|
const postmasterPid = `${dataDir}/pgdata/postmaster.pid`;
|
|
254
121
|
if (!existsSync(postmasterPid))
|
|
255
122
|
return null;
|
|
256
123
|
try {
|
|
257
124
|
const lines = readFileSync(postmasterPid, 'utf8').split(/\r?\n/);
|
|
258
|
-
// Line 4 (index 3) is the pg port according to the PostgreSQL postmaster.pid format.
|
|
259
125
|
const portStr = lines[3]?.trim();
|
|
260
126
|
if (!portStr)
|
|
261
127
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ensure-revisium.js","sourceRoot":"","sources":["../../src/host/ensure-revisium.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ensure-revisium.js","sourceRoot":"","sources":["../../src/host/ensure-revisium.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,23 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Host daemon runtime file (`host.json`) — the tracked identity of the long-lived Revo/NestJS
|
|
3
|
-
* host daemon, the single DBOS owner (ADR 0006). It lives beside the standalone `runtime.json`
|
|
4
|
-
* in the profile data dir but is a SEPARATE file: the standalone daemon and the host daemon have
|
|
5
|
-
* independent lifecycles, so conflating them would let one's stale pid mask the other's state.
|
|
6
|
-
*
|
|
7
|
-
* Shape is intentionally minimal — only what `ensureHost`/`status`/clients need: the pid to prove
|
|
8
|
-
* liveness, the GraphQL port that is the client transport, and the profile/startedAt for display
|
|
9
|
-
* and compare-and-delete identity. Standalone HTTP/pg ports stay in `runtime.json` (readRuntime).
|
|
10
|
-
*/
|
|
11
1
|
import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
12
2
|
import { join } from 'node:path';
|
|
13
3
|
import { fileURLToPath } from 'node:url';
|
|
14
4
|
import { getConfig, isAlive, profileDataDir, PROFILES } from '../config.js';
|
|
15
5
|
let cachedCodeVersion;
|
|
16
|
-
/**
|
|
17
|
-
* This build's package version (e.g. `0.1.0-alpha.7`). Recorded in host.json so `ensureHost`/`doctor`
|
|
18
|
-
* can detect a stale daemon running a DIFFERENT build than the current install (slice 139). A `0.0.0`
|
|
19
|
-
* dev checkout does not change across rebuilds, so dev relies on `revo restart`; released builds differ.
|
|
20
|
-
*/
|
|
21
6
|
export function hostCodeVersion() {
|
|
22
7
|
if (cachedCodeVersion === undefined) {
|
|
23
8
|
try {
|
|
@@ -37,7 +22,6 @@ export function hostRuntimeFile() {
|
|
|
37
22
|
export function readHostRuntime() {
|
|
38
23
|
return readHostRuntimeAt(hostRuntimeFile());
|
|
39
24
|
}
|
|
40
|
-
/** Read+parse a host.json at an ARBITRARY path (a sibling profile's). Null when absent/corrupt. */
|
|
41
25
|
export function readHostRuntimeAt(file) {
|
|
42
26
|
if (!existsSync(file))
|
|
43
27
|
return null;
|
|
@@ -48,12 +32,6 @@ export function readHostRuntimeAt(file) {
|
|
|
48
32
|
return null;
|
|
49
33
|
}
|
|
50
34
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Live tracked daemon pids across ALL profiles (slice 140 Phase 2). The rogue reaper must protect
|
|
53
|
-
* EVERY profile's daemon tree, not just the active one — else `revo stop --all --profile default`
|
|
54
|
-
* would SIGKILL a live `dev` daemon (and its bridges), aborting that profile's in-flight DBOS work.
|
|
55
|
-
* Band-default dirs only (a custom REVO_DATA_DIR layout isn't enumerable; documented in profileDataDir).
|
|
56
|
-
*/
|
|
57
35
|
export function allTrackedHostPids() {
|
|
58
36
|
const pids = [];
|
|
59
37
|
for (const profile of Object.keys(PROFILES)) {
|
|
@@ -71,18 +49,12 @@ export function removeHostRuntime() {
|
|
|
71
49
|
if (existsSync(file))
|
|
72
50
|
rmSync(file);
|
|
73
51
|
}
|
|
74
|
-
/**
|
|
75
|
-
* Remove `host.json` ONLY if the file on disk still matches the snapshot (same pid AND startedAt).
|
|
76
|
-
* A replaced file (a concurrently-started daemon) or an already-removed file is a safe no-op — so
|
|
77
|
-
* cleanup can never delete a runtime that belongs to another daemon (compare-and-delete).
|
|
78
|
-
*/
|
|
79
52
|
export function removeHostRuntimeIfMatches(snapshot) {
|
|
80
53
|
const current = readHostRuntime();
|
|
81
54
|
if (current?.pid === snapshot.pid && current?.startedAt === snapshot.startedAt) {
|
|
82
55
|
removeHostRuntime();
|
|
83
56
|
}
|
|
84
57
|
}
|
|
85
|
-
/** True when `host.json` records a daemon whose pid is still alive (liveness, not health). */
|
|
86
58
|
export function isHostRunning() {
|
|
87
59
|
const runtime = readHostRuntime();
|
|
88
60
|
return runtime !== null && isAlive(runtime.pid);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-runtime.js","sourceRoot":"","sources":["../../src/host/host-runtime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"host-runtime.js","sourceRoot":"","sources":["../../src/host/host-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,22 +10,6 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
11
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
12
|
};
|
|
13
|
-
/**
|
|
14
|
-
* HostLifecycle — OnApplicationBootstrap / OnApplicationShutdown.
|
|
15
|
-
*
|
|
16
|
-
* Boot order (§2 of the ТЗ):
|
|
17
|
-
* 1. ensureRevisium() — auto-start if no live daemon; three-state (F7).
|
|
18
|
-
* 2. Pid-proven pg port from the returned runtime (never resolvePorts() — F3).
|
|
19
|
-
* 3. Optional postmaster.pid cross-check (F8).
|
|
20
|
-
* 4. ensurePostgres(provenPgPort) — CREATE DATABASE dbos if absent.
|
|
21
|
-
* 5. dbosService.setConfig(url) + dbosService.launch().
|
|
22
|
-
*
|
|
23
|
-
* Shutdown:
|
|
24
|
-
* - DBOS.shutdown() only.
|
|
25
|
-
* - INTENTIONALLY does NOT stop the Revisium daemon (Round 3, human decision):
|
|
26
|
-
* the daemon is detached+unref'd and is a shared, long-lived process; subsequent
|
|
27
|
-
* `revo dev:status <id>` invocations must find the same daemon + embedded Postgres.
|
|
28
|
-
*/
|
|
29
13
|
import { Inject, Injectable, Optional } from '@nestjs/common';
|
|
30
14
|
import { DbosService } from '../engine/dbos.service.js';
|
|
31
15
|
import { ensureRevisium, readPostmasterPgPort } from './ensure-revisium.js';
|
|
@@ -35,8 +19,6 @@ function isMcpStdioHost() {
|
|
|
35
19
|
return process.env.REVO_MCP_STDIO === '1';
|
|
36
20
|
}
|
|
37
21
|
function keepControlPlaneBootstrapDependency(_api) {
|
|
38
|
-
// The constructor dependency forces the control-plane provider graph to instantiate
|
|
39
|
-
// before DBOS.launch(); pipeline registration must precede launch.
|
|
40
22
|
}
|
|
41
23
|
let HostLifecycle = class HostLifecycle {
|
|
42
24
|
dbosService;
|
|
@@ -45,11 +27,8 @@ let HostLifecycle = class HostLifecycle {
|
|
|
45
27
|
keepControlPlaneBootstrapDependency(taskControlPlaneApi);
|
|
46
28
|
}
|
|
47
29
|
async onApplicationBootstrap() {
|
|
48
|
-
// Step 1: Ensure Revisium daemon is running (auto-start if absent).
|
|
49
30
|
const { runtime } = await ensureRevisium();
|
|
50
|
-
// Step 2: Pid-proven pg port (F3 — never resolvePorts()).
|
|
51
31
|
let provenPgPort = runtime.pgPort;
|
|
52
|
-
// Optional cross-check against postmaster.pid (F8).
|
|
53
32
|
if (runtime.dataDir) {
|
|
54
33
|
const pmPort = readPostmasterPgPort(runtime.dataDir);
|
|
55
34
|
if (pmPort !== null && pmPort !== runtime.pgPort) {
|
|
@@ -60,19 +39,13 @@ let HostLifecycle = class HostLifecycle {
|
|
|
60
39
|
provenPgPort = pmPort;
|
|
61
40
|
}
|
|
62
41
|
}
|
|
63
|
-
// If runtime.dataDir is absent (older standalone), skip cross-check and rely on rt.pgPort.
|
|
64
|
-
// Step 3: Ensure the `dbos` database exists.
|
|
65
42
|
await ensurePostgres(provenPgPort);
|
|
66
|
-
// Step 4+5: Configure + launch DBOS.
|
|
67
43
|
this.dbosService.setConfig(dbosSystemDatabaseUrl(provenPgPort), {
|
|
68
44
|
logLevel: isMcpStdioHost() ? 'warn' : undefined,
|
|
69
45
|
});
|
|
70
46
|
await this.dbosService.launch();
|
|
71
47
|
}
|
|
72
48
|
async onApplicationShutdown() {
|
|
73
|
-
// Intentional: host does not own the Revisium daemon lifecycle.
|
|
74
|
-
// Only DBOS.shutdown() runs on exit.
|
|
75
|
-
// The daemon was spawned detached+unref'd and must outlive the host process.
|
|
76
49
|
await this.dbosService.shutdown();
|
|
77
50
|
}
|
|
78
51
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.lifecycle.js","sourceRoot":"","sources":["../../src/host/host.lifecycle.ts"],"names":[],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"host.lifecycle.js","sourceRoot":"","sources":["../../src/host/host.lifecycle.ts"],"names":[],"mappings":";;;;;;;;;;;;AAcA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAiD,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC7G,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACrF,OAAO,EAAE,0BAA0B,EAAE,MAAM,yDAAyD,CAAC;AAErG,SAAS,cAAc;IACrB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,GAAG,CAAC;AAC5C,CAAC;AAED,SAAS,mCAAmC,CAAC,IAA4C;AACzF,CAAC;AAGM,IAAM,aAAa,GAAnB,MAAM,aAAa;IAEgB;IADxC,YACwC,WAAwB,EAG9D,mBAAgD;QAHV,gBAAW,GAAX,WAAW,CAAa;QAK9D,mCAAmC,CAAC,mBAAmB,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,sBAAsB;QAC1B,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,cAAc,EAAE,CAAC;QAE3C,IAAI,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;QAElC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,MAAM,GAAG,oBAAoB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACrD,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;gBACjD,MAAM,IAAI,KAAK,CACb,sCAAsC,OAAO,CAAC,MAAM,oCAAoC,MAAM,IAAI;oBAChG,6DAA6D,CAChE,CAAC;YACJ,CAAC;YACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,YAAY,GAAG,MAAM,CAAC;YACxB,CAAC;QACH,CAAC;QAED,MAAM,cAAc,CAAC,YAAY,CAAC,CAAC;QAEnC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,qBAAqB,CAAC,YAAY,CAAC,EAAE;YAC9D,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;SAChD,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,qBAAqB;QACzB,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;IACpC,CAAC;CACF,CAAA;AAvCY,aAAa;IADzB,UAAU,EAAE;IAGR,WAAA,MAAM,CAAC,WAAW,CAAC,CAAA;IACnB,WAAA,QAAQ,EAAE,CAAA;IACV,WAAA,MAAM,CAAC,0BAA0B,CAAC,CAAA;qCAFgB,WAAW;QAGxC,0BAA0B;GALvC,aAAa,CAuCzB"}
|