harnery 0.45.0 → 0.45.2
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/dist/commander.js +0 -1
- package/dist/commands/files.d.ts +2 -2
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +4 -7
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +17 -11
- package/dist/commands/tunnel.d.ts +0 -15
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +153 -453
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +6 -45
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +0 -18
- package/dist/core/agents/live-authority-v3.d.ts +0 -1
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +0 -11
- package/dist/core/agents/live-lifecycle-v3.d.ts +2 -4
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/live-lifecycle-v3.js +7 -28
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +14 -25
- package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-writer.js +0 -5
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +2 -7
- package/dist/core/agents/state/proc-start.d.ts +20 -31
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +37 -70
- package/dist/core/agents/state/service-process.d.ts +61 -0
- package/dist/core/agents/state/service-process.d.ts.map +1 -0
- package/dist/core/agents/state/service-process.js +143 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +70 -180
- package/dist/core/artifacts/activity.d.ts +4 -1
- package/dist/core/artifacts/activity.d.ts.map +1 -1
- package/dist/core/artifacts/activity.js +8 -2
- package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
- package/dist/core/artifacts/delivery-card.js +1 -2
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +38 -12
- package/dist/core/context/index.d.ts +6 -4
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +194 -256
- package/dist/core/context/observation.d.ts +9 -9
- package/dist/core/context/observation.d.ts.map +1 -1
- package/dist/core/context/observation.js +37 -6
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +0 -29
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +0 -1
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +0 -7
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +64 -77
- package/dist/core/events/v3/runtime-build.d.ts +14 -0
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
- package/dist/core/events/v3/runtime-build.js +44 -0
- package/dist/core/governor/index.d.ts +2 -1
- package/dist/core/governor/index.d.ts.map +1 -1
- package/dist/core/governor/index.js +2 -1
- package/dist/core/governor/service-read.d.ts +3 -10
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +10 -94
- package/dist/core/governor/service.d.ts +3 -7
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +17 -182
- package/dist/core/hooks/cli.js +78 -48
- package/dist/core/hooks/resolve/owner.js +2 -2
- package/dist/core/resources/sampler.js +2 -2
- package/dist/core/resources/service-status.d.ts +2 -14
- package/dist/core/resources/service-status.d.ts.map +1 -1
- package/dist/core/resources/service-status.js +3 -23
- package/dist/core/resources/service.d.ts +3 -7
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +12 -101
- package/dist/core/resources/status.d.ts.map +1 -1
- package/dist/core/resources/status.js +3 -2
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +9 -2
- package/dist/core/servers/index.d.ts +11 -10
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +82 -192
- package/dist/core/servers/tunnels.d.ts +3 -5
- package/dist/core/servers/tunnels.d.ts.map +1 -1
- package/dist/core/servers/tunnels.js +2 -45
- package/dist/core/storage/durable-history.js +56 -22
- package/dist/core/storage/segments.d.ts.map +1 -1
- package/dist/core/storage/segments.js +73 -17
- package/dist/core/supervisor/service.d.ts +4 -7
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +23 -122
- package/dist/core/supervisor/services.js +2 -2
- package/dist/core/supervisor/status.d.ts +3 -17
- package/dist/core/supervisor/status.d.ts.map +1 -1
- package/dist/core/supervisor/status.js +7 -70
- package/dist/core/work/state.js +7 -7
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +23 -66
- package/dist/core/workflow/index.d.ts +3 -4
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -2
- package/dist/core/workflow/live-session-v3.d.ts +0 -6
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +5 -126
- package/dist/core/workflow/proof.d.ts +4 -4
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +34 -86
- package/dist/core/workflow/resume-lock.d.ts +17 -0
- package/dist/core/workflow/resume-lock.d.ts.map +1 -0
- package/dist/core/workflow/resume-lock.js +158 -0
- package/dist/core/workflow/run-state.d.ts +0 -21
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +3 -174
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +0 -19
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +0 -13
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +0 -19
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +0 -14
- package/dist/core/workflow/types.d.ts +11 -54
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.d.ts +4 -0
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +1 -1
- package/dist/core/workflow/workspaces/local-git.js +91 -210
- package/dist/core/workflow/workspaces/ownership.d.ts +0 -7
- package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/ownership.js +3 -130
- package/dist/core/workflow/workspaces/reclaim.d.ts +4 -7
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/reclaim.js +0 -4
- package/dist/core/workflow/workspaces/recovery.d.ts +1 -1
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +1 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +1 -4
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +0 -4
- package/dist/lib/tunnel/state.d.ts +6 -13
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +12 -58
- package/package.json +1 -6
- package/src/commander.ts +0 -10
- package/src/commands/files.ts +5 -13
- package/src/commands/servers.ts +23 -12
- package/src/commands/tunnel.ts +166 -533
- package/src/commands/workflow.ts +8 -58
- package/src/core/agents/coord-client.ts +0 -20
- package/src/core/agents/live-authority-v3.ts +0 -17
- package/src/core/agents/live-lifecycle-v3.ts +14 -52
- package/src/core/agents/state/heartbeat-writer.ts +12 -29
- package/src/core/agents/state/live-coordination-writer.ts +0 -29
- package/src/core/agents/state/pidmap.ts +2 -15
- package/src/core/agents/state/proc-start.ts +41 -69
- package/src/core/agents/state/service-process.ts +217 -0
- package/src/core/agents/state/stale-sweep.ts +82 -247
- package/src/core/artifacts/activity.ts +9 -2
- package/src/core/artifacts/delivery-card.ts +1 -7
- package/src/core/artifacts/index.ts +45 -12
- package/src/core/context/index.ts +207 -272
- package/src/core/context/observation.ts +40 -9
- package/src/core/events/v3/authority-outbox.ts +0 -56
- package/src/core/events/v3/producers/coordination-recorder.ts +0 -15
- package/src/core/events/v3/producers/recorder.ts +68 -94
- package/src/core/events/v3/runtime-build.ts +62 -0
- package/src/core/governor/index.ts +1 -2
- package/src/core/governor/service-read.ts +15 -114
- package/src/core/governor/service.ts +36 -220
- package/src/core/hooks/cli.ts +85 -47
- package/src/core/hooks/resolve/owner.ts +2 -2
- package/src/core/resources/sampler.ts +3 -2
- package/src/core/resources/service-status.ts +6 -35
- package/src/core/resources/service.ts +23 -133
- package/src/core/resources/status.ts +3 -2
- package/src/core/semantic/service.ts +8 -2
- package/src/core/servers/index.ts +86 -228
- package/src/core/servers/tunnels.ts +3 -55
- package/src/core/storage/durable-history.ts +52 -20
- package/src/core/storage/segments.ts +79 -17
- package/src/core/supervisor/service.ts +33 -155
- package/src/core/supervisor/services.ts +2 -2
- package/src/core/supervisor/status.ts +10 -85
- package/src/core/work/state.ts +7 -7
- package/src/core/workflow/engine.ts +22 -74
- package/src/core/workflow/index.ts +2 -13
- package/src/core/workflow/live-session-v3.ts +5 -146
- package/src/core/workflow/proof.ts +40 -104
- package/src/core/workflow/resume-lock.ts +179 -0
- package/src/core/workflow/run-state.ts +3 -207
- package/src/core/workflow/spawn-claude.ts +0 -18
- package/src/core/workflow/spawn-codex.ts +0 -13
- package/src/core/workflow/spawn-cursor.ts +0 -18
- package/src/core/workflow/spawn-opencode.ts +0 -14
- package/src/core/workflow/types.ts +11 -41
- package/src/core/workflow/workspaces/leases.ts +1 -1
- package/src/core/workflow/workspaces/local-git.ts +120 -239
- package/src/core/workflow/workspaces/ownership.ts +3 -158
- package/src/core/workflow/workspaces/reclaim.ts +3 -9
- package/src/core/workflow/workspaces/recovery.ts +1 -1
- package/src/core/workflow/workspaces/state.ts +1 -5
- package/src/lib/identities/assume.ts +0 -4
- package/src/lib/tunnel/state.ts +15 -70
- package/dist/commands/turn-rollback.d.ts +0 -5
- package/dist/commands/turn-rollback.d.ts.map +0 -1
- package/dist/commands/turn-rollback.js +0 -76
- package/dist/core/agents/state/process-handle.d.ts +0 -26
- package/dist/core/agents/state/process-handle.d.ts.map +0 -1
- package/dist/core/agents/state/process-handle.js +0 -121
- package/dist/core/agents/state/unchanged-file.d.ts +0 -44
- package/dist/core/agents/state/unchanged-file.d.ts.map +0 -1
- package/dist/core/agents/state/unchanged-file.js +0 -390
- package/dist/core/events/v3/generation-custody.d.ts +0 -5
- package/dist/core/events/v3/generation-custody.d.ts.map +0 -1
- package/dist/core/events/v3/generation-custody.js +0 -50
- package/dist/core/turn-rollback/content.d.ts +0 -53
- package/dist/core/turn-rollback/content.d.ts.map +0 -1
- package/dist/core/turn-rollback/content.js +0 -396
- package/dist/core/turn-rollback/index.d.ts +0 -72
- package/dist/core/turn-rollback/index.d.ts.map +0 -1
- package/dist/core/turn-rollback/index.js +0 -443
- package/dist/core/workflow/failure-evidence.d.ts +0 -16
- package/dist/core/workflow/failure-evidence.d.ts.map +0 -1
- package/dist/core/workflow/failure-evidence.js +0 -114
- package/dist/core/workflow/recovery-audit.d.ts +0 -30
- package/dist/core/workflow/recovery-audit.d.ts.map +0 -1
- package/dist/core/workflow/recovery-audit.js +0 -116
- package/src/commands/turn-rollback.ts +0 -111
- package/src/core/agents/state/process-handle.ts +0 -165
- package/src/core/agents/state/unchanged-file.ts +0 -456
- package/src/core/events/v3/generation-custody.ts +0 -59
- package/src/core/turn-rollback/content.ts +0 -499
- package/src/core/turn-rollback/index.ts +0 -580
- package/src/core/workflow/failure-evidence.ts +0 -141
- package/src/core/workflow/recovery-audit.ts +0 -181
|
@@ -10,30 +10,28 @@ import {
|
|
|
10
10
|
renameSync,
|
|
11
11
|
rmSync,
|
|
12
12
|
statSync,
|
|
13
|
-
unlinkSync,
|
|
14
13
|
writeFileSync,
|
|
15
14
|
} from "node:fs";
|
|
16
15
|
import { hostname } from "node:os";
|
|
17
16
|
import { dirname, join, resolve } from "node:path";
|
|
18
17
|
import { coordEnv } from "../../lib/env.ts";
|
|
18
|
+
import { type ProcessGenerationVerdict, processStartToken } from "../agents/state/proc-start.ts";
|
|
19
19
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
acquireServiceLease,
|
|
21
|
+
requestServiceStop,
|
|
22
|
+
type ServiceLiveness,
|
|
23
|
+
type ServiceProcessDependencies,
|
|
24
|
+
serviceStartRefusal,
|
|
25
|
+
} from "../agents/state/service-process.ts";
|
|
25
26
|
import { resolveBinName } from "../config.ts";
|
|
27
|
+
import { installedHarneryBuild } from "../events/v3/runtime-build.ts";
|
|
26
28
|
import { type NormalizedPolicy, normalizePolicy, type PolicyIsolation } from "../policy/index.ts";
|
|
27
29
|
import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/logger.ts";
|
|
28
30
|
import { stateDirMode, stateFileMode } from "../storage/modes.ts";
|
|
29
31
|
import { RotatingTextSink } from "../storage/process-log.ts";
|
|
30
32
|
import type { RunWorkItemInput } from "../work/index.ts";
|
|
31
33
|
import { type GovernorRunReport, type GovernorStopReason, runGovernor } from "./runner.ts";
|
|
32
|
-
import {
|
|
33
|
-
currentGovernorServiceBuild,
|
|
34
|
-
governorServiceBuildStatus,
|
|
35
|
-
observeGovernorServiceProcess,
|
|
36
|
-
} from "./service-read.ts";
|
|
34
|
+
import { readGovernorServiceRuntime, readGovernorServiceStatus } from "./service-read.ts";
|
|
37
35
|
import { type GovernorRecord, readGovernor } from "./state.ts";
|
|
38
36
|
|
|
39
37
|
export const GOVERNOR_SERVICE_CONFIG_SCHEMA_VERSION = 1 as const;
|
|
@@ -101,10 +99,6 @@ export interface GovernorServiceBuild {
|
|
|
101
99
|
revision: string;
|
|
102
100
|
}
|
|
103
101
|
|
|
104
|
-
export interface GovernorServiceProcessDependencies extends ProcessGenerationSignalDependencies {
|
|
105
|
-
probePid?: (pid: number) => boolean;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
102
|
export interface GovernorServiceStatusRecord {
|
|
109
103
|
schema_version: typeof GOVERNOR_SERVICE_STATUS_SCHEMA_VERSION;
|
|
110
104
|
pid: number;
|
|
@@ -173,27 +167,13 @@ export interface RunGovernorServiceDaemonInput {
|
|
|
173
167
|
onLog?: (line: string) => void;
|
|
174
168
|
}
|
|
175
169
|
|
|
176
|
-
interface ServiceLease {
|
|
177
|
-
pid: number;
|
|
178
|
-
start_token?: string;
|
|
179
|
-
running_build?: GovernorServiceBuild;
|
|
180
|
-
host: string;
|
|
181
|
-
nonce: string;
|
|
182
|
-
created_at: string;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
170
|
export function configureGovernorService(
|
|
186
171
|
input: ConfigureGovernorServiceInput,
|
|
187
172
|
): GovernorServiceConfig {
|
|
188
173
|
const coordRoot = resolve(input.coordRoot);
|
|
189
|
-
const
|
|
190
|
-
if (
|
|
191
|
-
throw
|
|
192
|
-
}
|
|
193
|
-
if (active) {
|
|
194
|
-
const liveness = statusOwnerIsLive(active);
|
|
195
|
-
if (liveness !== "stopped") throw serviceStartRefusal(coordRoot, active.pid, liveness);
|
|
196
|
-
}
|
|
174
|
+
const current = readGovernorServiceStatus(coordRoot);
|
|
175
|
+
if (current.liveness !== "stopped")
|
|
176
|
+
throw startRefusal(coordRoot, current.record?.pid, current.liveness);
|
|
197
177
|
const goalIds = unique(input.goalIds.map((value) => value.trim()).filter(Boolean));
|
|
198
178
|
if (goalIds.length < 1 || goalIds.length > MAX_GOALS) {
|
|
199
179
|
throw new Error(`governor service requires from 1 to ${MAX_GOALS} unique goal ids`);
|
|
@@ -249,65 +229,12 @@ export function readGovernorServiceConfig(coordRootRaw: string): GovernorService
|
|
|
249
229
|
return config;
|
|
250
230
|
}
|
|
251
231
|
|
|
252
|
-
export function readGovernorServiceRuntime(
|
|
253
|
-
coordRootRaw: string,
|
|
254
|
-
): GovernorServiceRuntime | undefined {
|
|
255
|
-
const coordRoot = resolve(coordRootRaw);
|
|
256
|
-
if (!existsSync(serviceRuntimePath(coordRoot))) return undefined;
|
|
257
|
-
const runtime = readBoundedJson<GovernorServiceRuntime>(
|
|
258
|
-
serviceRuntimePath(coordRoot),
|
|
259
|
-
"governor service runtime",
|
|
260
|
-
);
|
|
261
|
-
if (
|
|
262
|
-
runtime.schema_version !== GOVERNOR_SERVICE_RUNTIME_SCHEMA_VERSION ||
|
|
263
|
-
!validTimestamp(runtime.config_created_at) ||
|
|
264
|
-
!validTimestamp(runtime.updated_at) ||
|
|
265
|
-
!runtime.goals ||
|
|
266
|
-
typeof runtime.goals !== "object" ||
|
|
267
|
-
Array.isArray(runtime.goals)
|
|
268
|
-
) {
|
|
269
|
-
throw new Error("governor service runtime has an unsupported schema");
|
|
270
|
-
}
|
|
271
|
-
return runtime;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export function readGovernorServiceStatus(
|
|
275
|
-
coordRootRaw: string,
|
|
276
|
-
dependencies: GovernorServiceProcessDependencies = {},
|
|
277
|
-
): GovernorServiceStatus {
|
|
278
|
-
const coordRoot = resolve(coordRootRaw);
|
|
279
|
-
const record = readStatusRecord(coordRoot);
|
|
280
|
-
let config: GovernorServiceConfig | undefined;
|
|
281
|
-
let runtime: GovernorServiceRuntime | undefined;
|
|
282
|
-
try {
|
|
283
|
-
config = readGovernorServiceConfig(coordRoot);
|
|
284
|
-
} catch {
|
|
285
|
-
// A service has no usable configuration until explicitly configured.
|
|
286
|
-
}
|
|
287
|
-
try {
|
|
288
|
-
runtime = readGovernorServiceRuntime(coordRoot);
|
|
289
|
-
} catch {
|
|
290
|
-
// Status remains readable when recoverable runtime state is corrupt.
|
|
291
|
-
}
|
|
292
|
-
const observation = observeGovernorServiceProcess(record, dependencies);
|
|
293
|
-
if (!record && existsSync(serviceStatusPath(coordRoot))) observation.liveness = "unknown";
|
|
294
|
-
return {
|
|
295
|
-
running: observation.liveness === "running" && record?.state !== "stopped",
|
|
296
|
-
stale: observation.liveness === "stopped" && record !== undefined && record.state !== "stopped",
|
|
297
|
-
...observation,
|
|
298
|
-
...governorServiceBuildStatus(record),
|
|
299
|
-
record,
|
|
300
|
-
config,
|
|
301
|
-
runtime,
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
|
|
305
232
|
export async function spawnGovernorService(coordRootRaw: string): Promise<GovernorServiceStatus> {
|
|
306
233
|
const coordRoot = resolve(coordRootRaw);
|
|
307
234
|
readGovernorServiceConfig(coordRoot);
|
|
308
235
|
const current = readGovernorServiceStatus(coordRoot);
|
|
309
236
|
if (current.liveness !== "stopped") {
|
|
310
|
-
throw
|
|
237
|
+
throw startRefusal(coordRoot, current.record?.pid, current.liveness);
|
|
311
238
|
}
|
|
312
239
|
mkdirSync(serviceDir(coordRoot), { recursive: true, mode: stateDirMode() });
|
|
313
240
|
const sharedLogs = coordEnv("SHARED_LOGS") !== "0";
|
|
@@ -347,31 +274,19 @@ export async function spawnGovernorService(coordRootRaw: string): Promise<Govern
|
|
|
347
274
|
|
|
348
275
|
export function requestGovernorServiceStop(
|
|
349
276
|
coordRootRaw: string,
|
|
350
|
-
dependencies:
|
|
277
|
+
dependencies: ServiceProcessDependencies = {},
|
|
351
278
|
): GovernorServiceStatus {
|
|
352
279
|
const coordRoot = resolve(coordRootRaw);
|
|
353
280
|
const status = readGovernorServiceStatus(coordRoot, dependencies);
|
|
354
281
|
if ((status.record?.state === "stopped" || !status.record) && status.liveness === "stopped") {
|
|
355
282
|
return { ...status, signal_sent: false, signal_verdict: status.generation };
|
|
356
283
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
? signalIfSameGeneration(
|
|
364
|
-
status.record.pid,
|
|
365
|
-
status.record.start_token,
|
|
366
|
-
"SIGTERM",
|
|
367
|
-
dependencies,
|
|
368
|
-
)
|
|
369
|
-
: { sent: false, verdict: status.generation };
|
|
370
|
-
return {
|
|
371
|
-
...readGovernorServiceStatus(coordRoot, dependencies),
|
|
372
|
-
signal_sent: signal.sent,
|
|
373
|
-
signal_verdict: signal.verdict,
|
|
374
|
-
};
|
|
284
|
+
return requestServiceStop(
|
|
285
|
+
status,
|
|
286
|
+
() => readGovernorServiceStatus(coordRoot, dependencies),
|
|
287
|
+
serviceStopPath(coordRoot),
|
|
288
|
+
dependencies,
|
|
289
|
+
);
|
|
375
290
|
}
|
|
376
291
|
|
|
377
292
|
export async function runGovernorServiceSweep(
|
|
@@ -519,22 +434,21 @@ export async function runGovernorServiceDaemon(
|
|
|
519
434
|
): Promise<GovernorServiceStatusRecord> {
|
|
520
435
|
const coordRoot = resolve(input.coordRoot);
|
|
521
436
|
const config = readGovernorServiceConfig(coordRoot);
|
|
522
|
-
const
|
|
523
|
-
if (
|
|
524
|
-
throw
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const release = acquireServiceLease(coordRoot);
|
|
437
|
+
const current = readGovernorServiceStatus(coordRoot);
|
|
438
|
+
if (current.liveness !== "stopped")
|
|
439
|
+
throw startRefusal(coordRoot, current.record?.pid, current.liveness);
|
|
440
|
+
const { release } = acquireServiceLease(
|
|
441
|
+
serviceLeasePath(coordRoot),
|
|
442
|
+
(pid, liveness) => startRefusal(coordRoot, pid, liveness),
|
|
443
|
+
{ running_build: installedHarneryBuild() },
|
|
444
|
+
);
|
|
531
445
|
rmSync(serviceStopPath(coordRoot), { force: true });
|
|
532
446
|
const now = new Date().toISOString();
|
|
533
447
|
const status: GovernorServiceStatusRecord = {
|
|
534
448
|
schema_version: GOVERNOR_SERVICE_STATUS_SCHEMA_VERSION,
|
|
535
449
|
pid: process.pid,
|
|
536
450
|
start_token: processStartToken(process.pid) ?? undefined,
|
|
537
|
-
running_build:
|
|
451
|
+
running_build: installedHarneryBuild(),
|
|
538
452
|
host: hostname(),
|
|
539
453
|
nonce: randomUUID(),
|
|
540
454
|
state: "starting",
|
|
@@ -781,114 +695,16 @@ function normalizeIsolation(value: unknown): PolicyIsolation | undefined {
|
|
|
781
695
|
throw new Error("service isolation must be shared, worktree, sandbox, or remote");
|
|
782
696
|
}
|
|
783
697
|
|
|
784
|
-
function
|
|
785
|
-
const path = serviceLeasePath(coordRoot);
|
|
786
|
-
mkdirSync(dirname(path), { recursive: true, mode: stateDirMode() });
|
|
787
|
-
const owner: ServiceLease = {
|
|
788
|
-
pid: process.pid,
|
|
789
|
-
start_token: processStartToken(process.pid) ?? undefined,
|
|
790
|
-
running_build: currentGovernorServiceBuild(),
|
|
791
|
-
host: hostname(),
|
|
792
|
-
nonce: randomUUID(),
|
|
793
|
-
created_at: new Date().toISOString(),
|
|
794
|
-
};
|
|
795
|
-
const acquire = (): boolean => {
|
|
796
|
-
try {
|
|
797
|
-
const fd = openSync(path, "wx", stateFileMode());
|
|
798
|
-
try {
|
|
799
|
-
writeFileSync(fd, `${JSON.stringify(owner)}\n`, "utf8");
|
|
800
|
-
} finally {
|
|
801
|
-
closeSync(fd);
|
|
802
|
-
}
|
|
803
|
-
return true;
|
|
804
|
-
} catch (error) {
|
|
805
|
-
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
806
|
-
return false;
|
|
807
|
-
}
|
|
808
|
-
};
|
|
809
|
-
if (!acquire()) {
|
|
810
|
-
const existing = readLease(path);
|
|
811
|
-
const liveness = existing ? leaseOwnerIsLive(existing) : "unknown";
|
|
812
|
-
if (liveness !== "stopped") {
|
|
813
|
-
throw serviceStartRefusal(coordRoot, existing?.pid, liveness);
|
|
814
|
-
}
|
|
815
|
-
unlinkSync(path);
|
|
816
|
-
if (!acquire()) throw new Error("governor service lease raced with another process");
|
|
817
|
-
}
|
|
818
|
-
return () => {
|
|
819
|
-
try {
|
|
820
|
-
const existing = readLease(path);
|
|
821
|
-
if (existing?.nonce === owner.nonce) unlinkSync(path);
|
|
822
|
-
} catch {
|
|
823
|
-
// A stale lease is recoverable by the next explicit service start.
|
|
824
|
-
}
|
|
825
|
-
};
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
function readLease(path: string): ServiceLease | null {
|
|
829
|
-
try {
|
|
830
|
-
const value = JSON.parse(readFileSync(path, "utf8")) as Partial<ServiceLease>;
|
|
831
|
-
return Number.isSafeInteger(value.pid) &&
|
|
832
|
-
(value.pid ?? 0) > 0 &&
|
|
833
|
-
typeof value.host === "string" &&
|
|
834
|
-
typeof value.nonce === "string" &&
|
|
835
|
-
typeof value.created_at === "string"
|
|
836
|
-
? ({
|
|
837
|
-
...value,
|
|
838
|
-
start_token: typeof value.start_token === "string" ? value.start_token : undefined,
|
|
839
|
-
} as ServiceLease)
|
|
840
|
-
: null;
|
|
841
|
-
} catch {
|
|
842
|
-
return null;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
function leaseOwnerIsLive(lease: ServiceLease): GovernorServiceStatus["liveness"] {
|
|
847
|
-
return observeGovernorServiceProcess({ ...lease, state: "running" }).liveness;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
function readStatusRecord(coordRoot: string): GovernorServiceStatusRecord | undefined {
|
|
851
|
-
if (!existsSync(serviceStatusPath(coordRoot))) return undefined;
|
|
852
|
-
try {
|
|
853
|
-
const value = readBoundedJson<GovernorServiceStatusRecord>(
|
|
854
|
-
serviceStatusPath(coordRoot),
|
|
855
|
-
"governor service status",
|
|
856
|
-
);
|
|
857
|
-
if (
|
|
858
|
-
value.schema_version !== GOVERNOR_SERVICE_STATUS_SCHEMA_VERSION ||
|
|
859
|
-
!Number.isSafeInteger(value.pid) ||
|
|
860
|
-
value.pid < 1 ||
|
|
861
|
-
typeof value.host !== "string" ||
|
|
862
|
-
typeof value.nonce !== "string" ||
|
|
863
|
-
!validTimestamp(value.started_at) ||
|
|
864
|
-
!validTimestamp(value.heartbeat_at)
|
|
865
|
-
) {
|
|
866
|
-
return undefined;
|
|
867
|
-
}
|
|
868
|
-
return {
|
|
869
|
-
...value,
|
|
870
|
-
start_token: typeof value.start_token === "string" ? value.start_token : undefined,
|
|
871
|
-
};
|
|
872
|
-
} catch {
|
|
873
|
-
return undefined;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
function statusOwnerIsLive(record: GovernorServiceStatusRecord): GovernorServiceStatus["liveness"] {
|
|
878
|
-
return observeGovernorServiceProcess(record).liveness;
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
function serviceStartRefusal(
|
|
698
|
+
function startRefusal(
|
|
882
699
|
coordRoot: string,
|
|
883
700
|
pid: number | undefined,
|
|
884
|
-
liveness:
|
|
701
|
+
liveness: ServiceLiveness,
|
|
885
702
|
): Error {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
`${reason}; run \`${resolveBinName(coordRoot)} governor service stop\` to write a durable stop request before starting or reconfiguring it`,
|
|
703
|
+
return serviceStartRefusal(
|
|
704
|
+
"governor service",
|
|
705
|
+
`${resolveBinName(coordRoot)} governor service stop`,
|
|
706
|
+
pid,
|
|
707
|
+
liveness,
|
|
892
708
|
);
|
|
893
709
|
}
|
|
894
710
|
|
package/src/core/hooks/cli.ts
CHANGED
|
@@ -844,6 +844,9 @@ async function main(): Promise<number> {
|
|
|
844
844
|
if (eventName === "runtime-context-retry") {
|
|
845
845
|
return runRuntimeContextRetryWorker(coordRoot, adapter, extra);
|
|
846
846
|
}
|
|
847
|
+
if (eventName === SESSION_MAINTENANCE_EVENT) {
|
|
848
|
+
return runSessionMaintenanceWorker(coordRoot, adapter);
|
|
849
|
+
}
|
|
847
850
|
const ledgerRoute = resolveLiveEventLedgerRouteV3(coordRoot);
|
|
848
851
|
|
|
849
852
|
// Always log a breadcrumb, useful when an event_type maps to null or owner
|
|
@@ -1274,7 +1277,7 @@ function recordContextTelemetry(run: HookRun): void {
|
|
|
1274
1277
|
}
|
|
1275
1278
|
|
|
1276
1279
|
async function handleSessionStarted(run: HookRun): Promise<void> {
|
|
1277
|
-
const { coordRoot, adapter,
|
|
1280
|
+
const { coordRoot, adapter, payload, owner, sessionId, data } = run;
|
|
1278
1281
|
// A configured backup runs out of process and is freshness-gated per host.
|
|
1279
1282
|
// Startup never waits for restic or a remote provider; the child owns no
|
|
1280
1283
|
// hook stdio, so the harness is not held open until it finishes. A failed
|
|
@@ -1285,52 +1288,10 @@ async function handleSessionStarted(run: HookRun): Promise<void> {
|
|
|
1285
1288
|
} catch (err) {
|
|
1286
1289
|
logError(coordRoot, err, { phase: "backup-schedule" });
|
|
1287
1290
|
}
|
|
1288
|
-
//
|
|
1289
|
-
//
|
|
1290
|
-
//
|
|
1291
|
-
|
|
1292
|
-
imageJanitor(coordRoot);
|
|
1293
|
-
// Session start is the only caller of the artifact janitor and the storage
|
|
1294
|
-
// maintenance pass, so both load here instead of on every tool call.
|
|
1295
|
-
const { autoCleanArtifacts } = await import("../artifacts/index.ts");
|
|
1296
|
-
const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.ts");
|
|
1297
|
-
const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import(
|
|
1298
|
-
"../storage/maintenance-providers.ts"
|
|
1299
|
-
);
|
|
1300
|
-
// Effect: throttled hourly artifact sweep (see autoCleanArtifacts). It
|
|
1301
|
-
// deletes expired workspaces, plus unheld workspaces over the per-bundle
|
|
1302
|
-
// ceiling or repository budget once they have been idle past
|
|
1303
|
-
// artifacts.idle_grace_hours. Each deletion is rechecked first and recorded
|
|
1304
|
-
// in .harnery/artifact-deletions.jsonl. Best-effort like every janitor
|
|
1305
|
-
// here: a failure logs and never blocks session start.
|
|
1306
|
-
try {
|
|
1307
|
-
autoCleanArtifacts(coordRoot);
|
|
1308
|
-
} catch (err) {
|
|
1309
|
-
logError(coordRoot, err, { phase: "artifact-auto-clean" });
|
|
1310
|
-
}
|
|
1311
|
-
// Effect: apply the same daily throttle to closed V3 epochs. The newest
|
|
1312
|
-
// recovery epochs remain protected by the archive retention policy.
|
|
1313
|
-
try {
|
|
1314
|
-
autoCleanEventV3Archives(coordRoot);
|
|
1315
|
-
} catch (err) {
|
|
1316
|
-
logError(coordRoot, err, { phase: "event-v3-archive-auto-clean" });
|
|
1317
|
-
}
|
|
1318
|
-
// Plan one bounded, claim-first global slice from cached pressure only.
|
|
1319
|
-
// Existing owner janitors above remain the only active executors until
|
|
1320
|
-
// each provider receives a separate production activation.
|
|
1321
|
-
try {
|
|
1322
|
-
await runAutomaticMaintenancePass(
|
|
1323
|
-
createAutomaticMaintenanceComposition(coordRoot, {
|
|
1324
|
-
journal: () => {
|
|
1325
|
-
if (behavior.journalMaintenance) journalJanitor(coordRoot);
|
|
1326
|
-
},
|
|
1327
|
-
images: () => imageJanitor(coordRoot),
|
|
1328
|
-
artifacts: () => autoCleanArtifacts(coordRoot),
|
|
1329
|
-
}),
|
|
1330
|
-
);
|
|
1331
|
-
} catch (err) {
|
|
1332
|
-
logError(coordRoot, err, { phase: "storage-maintenance-auto" });
|
|
1333
|
-
}
|
|
1291
|
+
// Storage maintenance can walk large stores. It runs in a detached worker so
|
|
1292
|
+
// session start emits its context without waiting for it; an adapter that
|
|
1293
|
+
// stops a slow hook would otherwise drop that context entirely.
|
|
1294
|
+
scheduleSessionMaintenance(coordRoot, adapter);
|
|
1334
1295
|
let recovery: PreparedContextRecovery | null = null;
|
|
1335
1296
|
if (payload?.source === "compact") {
|
|
1336
1297
|
try {
|
|
@@ -1814,6 +1775,83 @@ async function handleCompletedResponse(run: CompletedResponseRun): Promise<numbe
|
|
|
1814
1775
|
return 0;
|
|
1815
1776
|
}
|
|
1816
1777
|
|
|
1778
|
+
const SESSION_MAINTENANCE_EVENT = "session-maintenance";
|
|
1779
|
+
|
|
1780
|
+
function scheduleSessionMaintenance(coordRoot: string, adapter: Adapter): void {
|
|
1781
|
+
const modulePath = process.argv[1];
|
|
1782
|
+
if (!modulePath) return;
|
|
1783
|
+
try {
|
|
1784
|
+
const child = spawn(
|
|
1785
|
+
process.execPath,
|
|
1786
|
+
[modulePath, SESSION_MAINTENANCE_EVENT, "--adapter", adapter],
|
|
1787
|
+
{
|
|
1788
|
+
cwd: coordRoot,
|
|
1789
|
+
detached: true,
|
|
1790
|
+
stdio: "ignore",
|
|
1791
|
+
env: childEnv(coordRoot),
|
|
1792
|
+
},
|
|
1793
|
+
);
|
|
1794
|
+
child.unref();
|
|
1795
|
+
} catch (err) {
|
|
1796
|
+
logError(coordRoot, err, { phase: "session-maintenance-spawn" });
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
/**
|
|
1801
|
+
* Session-start storage maintenance, run out of the hook's process. Every step
|
|
1802
|
+
* is best-effort and throttled by its own stamp: a failure logs and never
|
|
1803
|
+
* reaches the session.
|
|
1804
|
+
*/
|
|
1805
|
+
async function runSessionMaintenanceWorker(
|
|
1806
|
+
coordRoot: string,
|
|
1807
|
+
adapter: Adapter | null,
|
|
1808
|
+
): Promise<number> {
|
|
1809
|
+
const journalMaintenance = adapter ? adapterBehavior(adapter).journalMaintenance : false;
|
|
1810
|
+
// Effect (journal-maintaining adapters): prune stale journal archives +
|
|
1811
|
+
// sweep orphans. The recovery cue is read separately at session start.
|
|
1812
|
+
if (journalMaintenance) journalJanitor(coordRoot);
|
|
1813
|
+
imageJanitor(coordRoot);
|
|
1814
|
+
const { autoCleanArtifacts } = await import("../artifacts/index.ts");
|
|
1815
|
+
const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.ts");
|
|
1816
|
+
const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import(
|
|
1817
|
+
"../storage/maintenance-providers.ts"
|
|
1818
|
+
);
|
|
1819
|
+
// Effect: throttled hourly artifact sweep (see autoCleanArtifacts). It
|
|
1820
|
+
// deletes expired workspaces, plus unheld workspaces over the per-bundle
|
|
1821
|
+
// ceiling or repository budget once they have been idle past
|
|
1822
|
+
// artifacts.idle_grace_hours. Each deletion is rechecked first and recorded
|
|
1823
|
+
// in .harnery/artifact-deletions.jsonl.
|
|
1824
|
+
try {
|
|
1825
|
+
autoCleanArtifacts(coordRoot);
|
|
1826
|
+
} catch (err) {
|
|
1827
|
+
logError(coordRoot, err, { phase: "artifact-auto-clean" });
|
|
1828
|
+
}
|
|
1829
|
+
// Effect: apply the same daily throttle to closed V3 epochs. The newest
|
|
1830
|
+
// recovery epochs remain protected by the archive retention policy.
|
|
1831
|
+
try {
|
|
1832
|
+
autoCleanEventV3Archives(coordRoot);
|
|
1833
|
+
} catch (err) {
|
|
1834
|
+
logError(coordRoot, err, { phase: "event-v3-archive-auto-clean" });
|
|
1835
|
+
}
|
|
1836
|
+
// Plan one bounded, claim-first global slice from cached pressure only.
|
|
1837
|
+
// Existing owner janitors above remain the only active executors until
|
|
1838
|
+
// each provider receives a separate production activation.
|
|
1839
|
+
try {
|
|
1840
|
+
await runAutomaticMaintenancePass(
|
|
1841
|
+
createAutomaticMaintenanceComposition(coordRoot, {
|
|
1842
|
+
journal: () => {
|
|
1843
|
+
if (journalMaintenance) journalJanitor(coordRoot);
|
|
1844
|
+
},
|
|
1845
|
+
images: () => imageJanitor(coordRoot),
|
|
1846
|
+
artifacts: () => autoCleanArtifacts(coordRoot),
|
|
1847
|
+
}),
|
|
1848
|
+
);
|
|
1849
|
+
} catch (err) {
|
|
1850
|
+
logError(coordRoot, err, { phase: "storage-maintenance-auto" });
|
|
1851
|
+
}
|
|
1852
|
+
return 0;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1817
1855
|
function scheduleRuntimeContextRetry(
|
|
1818
1856
|
coordRoot: string,
|
|
1819
1857
|
adapter: Adapter,
|
|
@@ -4,7 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { coordEnv } from "../../../lib/env.ts";
|
|
5
5
|
import { resolveOwnerBySessionEnv } from "../../agents/coord-client.ts";
|
|
6
6
|
import { readLiveCoordinationRow } from "../../agents/state/live-coordination-view.ts";
|
|
7
|
-
import {
|
|
7
|
+
import { verifyProcessGeneration } from "../../agents/state/proc-start.ts";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Resolve the canonical `instance_id` for the current hook invocation.
|
|
@@ -98,7 +98,7 @@ export function resolveOwner(opts: {
|
|
|
98
98
|
// A row whose start token disagrees with the process now holding this
|
|
99
99
|
// pid is about a different, earlier process. Believing it hands this
|
|
100
100
|
// session another agent's identity, so walk past it.
|
|
101
|
-
const recycled =
|
|
101
|
+
const recycled = verifyProcessGeneration(pid, startToken) === "different";
|
|
102
102
|
if (owner && owner.length > 0 && !recycled) {
|
|
103
103
|
return {
|
|
104
104
|
instance_id: owner,
|
|
@@ -5,7 +5,7 @@ import { basename, join } from "node:path";
|
|
|
5
5
|
import { performance } from "node:perf_hooks";
|
|
6
6
|
import { readLiveCoordinationRows } from "../agents/state/live-coordination-view.ts";
|
|
7
7
|
import { parsePidmapRow } from "../agents/state/pidmap.ts";
|
|
8
|
-
import {
|
|
8
|
+
import { verifyProcessGeneration } from "../agents/state/proc-start.ts";
|
|
9
9
|
import {
|
|
10
10
|
RESOURCE_SNAPSHOT_SCHEMA_VERSION,
|
|
11
11
|
type ResourceProcessGroup,
|
|
@@ -386,7 +386,8 @@ function readOwnershipAnchors(
|
|
|
386
386
|
if (!byPid.has(pid)) continue;
|
|
387
387
|
try {
|
|
388
388
|
const row = parsePidmapRow(readFileSync(join(directory, entry), "utf8"));
|
|
389
|
-
if (!row.instanceId ||
|
|
389
|
+
if (!row.instanceId || verifyProcessGeneration(pid, row.startToken) === "different")
|
|
390
|
+
continue;
|
|
390
391
|
anchors.set(pid, { kind: "agent", id: row.instanceId, source: "pid-map" });
|
|
391
392
|
} catch {
|
|
392
393
|
// An unreadable or racing row cannot prove ownership.
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { hostname } from "node:os";
|
|
3
2
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "../agents/state/
|
|
3
|
+
observeServiceProcess,
|
|
4
|
+
type ServiceProcessDependencies,
|
|
5
|
+
} from "../agents/state/service-process.ts";
|
|
7
6
|
import {
|
|
8
7
|
RESOURCE_SERVICE_STATUS_SCHEMA_VERSION,
|
|
9
8
|
type ResourceServiceStatus,
|
|
@@ -13,15 +12,10 @@ import { readResourceServiceRecord, resourcePaths } from "./storage.ts";
|
|
|
13
12
|
|
|
14
13
|
const STALE_HEARTBEAT_MS = 15_000;
|
|
15
14
|
|
|
16
|
-
export interface ResourceProcessDependencies {
|
|
17
|
-
readToken?: (pid: number) => string | null;
|
|
18
|
-
probePid?: (pid: number) => unknown;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
15
|
export function readResourceServiceStatus(
|
|
22
16
|
coordRoot: string,
|
|
23
17
|
nowMs = Date.now(),
|
|
24
|
-
dependencies:
|
|
18
|
+
dependencies: ServiceProcessDependencies = {},
|
|
25
19
|
): ResourceServiceStatus {
|
|
26
20
|
const paths = resourcePaths(coordRoot);
|
|
27
21
|
const record = readResourceServiceRecord(coordRoot);
|
|
@@ -37,13 +31,11 @@ export function readResourceServiceStatus(
|
|
|
37
31
|
}
|
|
38
32
|
const heartbeatAt = Date.parse(record.heartbeat_at);
|
|
39
33
|
const stale = !Number.isFinite(heartbeatAt) || nowMs - heartbeatAt > STALE_HEARTBEAT_MS;
|
|
40
|
-
const
|
|
41
|
-
const liveness =
|
|
42
|
-
owner.generation === "same" && record.state === "stopped" ? "stopped" : owner.liveness;
|
|
34
|
+
const { generation, liveness } = observeServiceProcess(record, dependencies);
|
|
43
35
|
return {
|
|
44
36
|
running: liveness === "running" && !stale,
|
|
45
37
|
stale,
|
|
46
|
-
generation
|
|
38
|
+
generation,
|
|
47
39
|
liveness,
|
|
48
40
|
record,
|
|
49
41
|
status_path: paths.service,
|
|
@@ -62,24 +54,3 @@ function validRecord(
|
|
|
62
54
|
typeof value.heartbeat_at === "string"
|
|
63
55
|
);
|
|
64
56
|
}
|
|
65
|
-
|
|
66
|
-
export function resourceProcessLiveness(
|
|
67
|
-
owner: { pid: number; host: string; start_token?: string },
|
|
68
|
-
dependencies: ResourceProcessDependencies = {},
|
|
69
|
-
): { generation: ProcessGenerationVerdict; liveness: ResourceServiceStatus["liveness"] } {
|
|
70
|
-
if (owner.host !== hostname()) return { generation: "unknown", liveness: "unknown" };
|
|
71
|
-
try {
|
|
72
|
-
(dependencies.probePid ?? ((pid) => process.kill(pid, 0)))(owner.pid);
|
|
73
|
-
} catch (error) {
|
|
74
|
-
return {
|
|
75
|
-
generation: "unknown",
|
|
76
|
-
liveness: (error as NodeJS.ErrnoException).code === "ESRCH" ? "stopped" : "unknown",
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
const generation = verifyProcessGeneration(owner.pid, owner.start_token, dependencies.readToken);
|
|
80
|
-
return {
|
|
81
|
-
generation,
|
|
82
|
-
liveness:
|
|
83
|
-
generation === "same" ? "running" : generation === "different" ? "stopped" : "unknown",
|
|
84
|
-
};
|
|
85
|
-
}
|