harnery 0.43.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/commander.js +1 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +41 -48
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +38 -11
- package/dist/commands/diagnostics.d.ts.map +1 -1
- package/dist/commands/diagnostics.js +10 -0
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +22 -7
- package/dist/commands/files.d.ts +2 -2
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +7 -4
- package/dist/commands/governor.js +22 -10
- package/dist/commands/inbox.d.ts +1 -1
- package/dist/commands/inbox.d.ts.map +1 -1
- package/dist/commands/inbox.js +55 -2
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +28 -4
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +26 -17
- package/dist/commands/supervisor.d.ts.map +1 -1
- package/dist/commands/supervisor.js +7 -2
- package/dist/commands/toc.d.ts +14 -0
- package/dist/commands/toc.d.ts.map +1 -1
- package/dist/commands/toc.js +7 -4
- package/dist/commands/tunnel.d.ts +15 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +453 -153
- package/dist/commands/turn-rollback.d.ts +5 -0
- package/dist/commands/turn-rollback.d.ts.map +1 -0
- package/dist/commands/turn-rollback.js +76 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +68 -30
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +18 -0
- package/dist/core/agents/live-authority-v3.d.ts +1 -0
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +11 -0
- package/dist/core/agents/live-lifecycle-v3.d.ts +4 -2
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/live-lifecycle-v3.js +28 -7
- package/dist/core/agents/mailbox.d.ts +9 -8
- package/dist/core/agents/mailbox.d.ts.map +1 -1
- package/dist/core/agents/mailbox.js +24 -17
- package/dist/core/agents/render/prompt-context.d.ts +2 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +8 -12
- package/dist/core/agents/render/session-context.d.ts +2 -8
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +7 -30
- package/dist/core/agents/rules/stop-hook.js +3 -3
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +25 -14
- package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-writer.js +5 -0
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +5 -0
- package/dist/core/agents/state/proc-start.d.ts +19 -1
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +46 -1
- package/dist/core/agents/state/process-handle.d.ts +26 -0
- package/dist/core/agents/state/process-handle.d.ts.map +1 -0
- package/dist/core/agents/state/process-handle.js +121 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +180 -70
- package/dist/core/agents/state/unchanged-file.d.ts +44 -0
- package/dist/core/agents/state/unchanged-file.d.ts.map +1 -0
- package/dist/core/agents/state/unchanged-file.js +390 -0
- package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
- package/dist/core/artifacts/delivery-card.js +2 -1
- package/dist/core/context/index.d.ts +22 -10
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +381 -119
- package/dist/core/context/observation.d.ts +26 -0
- package/dist/core/context/observation.d.ts.map +1 -0
- package/dist/core/context/observation.js +11 -0
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +25 -14
- package/dist/core/diagnostics/path-identity.d.ts +3 -0
- package/dist/core/diagnostics/path-identity.d.ts.map +1 -0
- package/dist/core/diagnostics/path-identity.js +12 -0
- package/dist/core/diagnostics/sanitize.d.ts.map +1 -1
- package/dist/core/diagnostics/sanitize.js +4 -3
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +29 -0
- package/dist/core/events/v3/generation-custody.d.ts +5 -0
- package/dist/core/events/v3/generation-custody.d.ts.map +1 -0
- package/dist/core/events/v3/generation-custody.js +50 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +1 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +7 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +77 -64
- package/dist/core/governor/planning.d.ts.map +1 -1
- package/dist/core/governor/planning.js +14 -1
- package/dist/core/governor/service-read.d.ts +10 -2
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +105 -22
- package/dist/core/governor/service.d.ts +20 -2
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +72 -47
- package/dist/core/hooks/cli.js +33 -5
- package/dist/core/inbox/contract.d.ts +2 -1
- package/dist/core/inbox/contract.d.ts.map +1 -1
- package/dist/core/inbox/index.d.ts +2 -0
- package/dist/core/inbox/index.d.ts.map +1 -1
- package/dist/core/inbox/index.js +2 -0
- package/dist/core/inbox/peer-messages.d.ts +37 -0
- package/dist/core/inbox/peer-messages.d.ts.map +1 -0
- package/dist/core/inbox/peer-messages.js +72 -0
- package/dist/core/inbox/service.d.ts +8 -1
- package/dist/core/inbox/service.d.ts.map +1 -1
- package/dist/core/inbox/service.js +67 -21
- package/dist/core/inbox/targets.d.ts +22 -0
- package/dist/core/inbox/targets.d.ts.map +1 -0
- package/dist/core/inbox/targets.js +108 -0
- package/dist/core/inbox/watcher.js +7 -3
- package/dist/core/journal/index.d.ts.map +1 -1
- package/dist/core/journal/index.js +17 -2
- package/dist/core/resources/contract.d.ts +3 -0
- package/dist/core/resources/contract.d.ts.map +1 -1
- package/dist/core/resources/service-status.d.ts +14 -1
- package/dist/core/resources/service-status.d.ts.map +1 -1
- package/dist/core/resources/service-status.js +23 -13
- package/dist/core/resources/service.d.ts +7 -13
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +34 -91
- package/dist/core/resources/status.d.ts +2 -5
- package/dist/core/resources/status.d.ts.map +1 -1
- package/dist/core/resources/status.js +36 -7
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +2 -9
- package/dist/core/servers/index.d.ts +34 -22
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +297 -144
- package/dist/core/servers/tunnels.d.ts +7 -7
- package/dist/core/servers/tunnels.d.ts.map +1 -1
- package/dist/core/servers/tunnels.js +47 -10
- package/dist/core/supervisor/contract.d.ts +12 -0
- package/dist/core/supervisor/contract.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.js +2 -1
- package/dist/core/supervisor/findings.d.ts.map +1 -1
- package/dist/core/supervisor/findings.js +54 -9
- package/dist/core/supervisor/service.d.ts +7 -1
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +59 -29
- package/dist/core/supervisor/services.d.ts.map +1 -1
- package/dist/core/supervisor/services.js +22 -1
- package/dist/core/supervisor/status.d.ts +17 -2
- package/dist/core/supervisor/status.d.ts.map +1 -1
- package/dist/core/supervisor/status.js +70 -12
- package/dist/core/supervisor/timeline.d.ts.map +1 -1
- package/dist/core/supervisor/timeline.js +2 -1
- package/dist/core/turn-rollback/content.d.ts +53 -0
- package/dist/core/turn-rollback/content.d.ts.map +1 -0
- package/dist/core/turn-rollback/content.js +396 -0
- package/dist/core/turn-rollback/index.d.ts +72 -0
- package/dist/core/turn-rollback/index.d.ts.map +1 -0
- package/dist/core/turn-rollback/index.js +443 -0
- package/dist/core/work/state.js +7 -7
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +105 -32
- package/dist/core/workflow/failure-evidence.d.ts +16 -0
- package/dist/core/workflow/failure-evidence.d.ts.map +1 -0
- package/dist/core/workflow/failure-evidence.js +114 -0
- package/dist/core/workflow/index.d.ts +5 -3
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +3 -2
- package/dist/core/workflow/live-session-v3.d.ts +6 -0
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +126 -5
- package/dist/core/workflow/proof.d.ts +5 -5
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +115 -20
- package/dist/core/workflow/recovery-audit.d.ts +30 -0
- package/dist/core/workflow/recovery-audit.d.ts.map +1 -0
- package/dist/core/workflow/recovery-audit.js +116 -0
- package/dist/core/workflow/run-state.d.ts +20 -1
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +141 -32
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +19 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +13 -0
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +19 -0
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +14 -0
- package/dist/core/workflow/types.d.ts +64 -17
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.d.ts +0 -1
- package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.js +0 -1
- package/dist/core/workflow/workspaces/leases.d.ts +3 -1
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +30 -16
- package/dist/core/workflow/workspaces/local-git.d.ts +6 -2
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +328 -8
- package/dist/core/workflow/workspaces/ownership.d.ts +11 -0
- package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -0
- package/dist/core/workflow/workspaces/ownership.js +194 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts +40 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/reclaim.js +14 -0
- package/dist/core/workflow/workspaces/recovery.d.ts +2 -0
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +42 -10
- package/dist/core/workflow/workspaces/state.d.ts +1 -0
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +21 -1
- package/dist/lib/browser/windows-chrome.d.ts +100 -12
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
- package/dist/lib/browser/windows-chrome.js +262 -73
- package/dist/lib/docs-links.d.ts.map +1 -1
- package/dist/lib/docs-links.js +45 -2
- package/dist/lib/guarded-remove.d.ts +7 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -1
- package/dist/lib/guarded-remove.js +106 -12
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +4 -0
- package/dist/lib/tunnel/state.d.ts +13 -6
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +58 -12
- package/dist/lib/zip/index.d.ts +41 -0
- package/dist/lib/zip/index.d.ts.map +1 -0
- package/dist/lib/zip/index.js +398 -0
- package/package.json +12 -1
- package/src/commander.ts +10 -0
- package/src/commands/agents.ts +54 -63
- package/src/commands/browse.ts +50 -14
- package/src/commands/diagnostics.ts +10 -0
- package/src/commands/doctor.ts +26 -7
- package/src/commands/files.ts +13 -5
- package/src/commands/governor.ts +23 -10
- package/src/commands/inbox.ts +52 -2
- package/src/commands/rm.ts +43 -4
- package/src/commands/servers.ts +27 -26
- package/src/commands/supervisor.ts +7 -2
- package/src/commands/toc.ts +14 -6
- package/src/commands/tunnel.ts +533 -166
- package/src/commands/turn-rollback.ts +111 -0
- package/src/commands/workflow.ts +89 -30
- package/src/core/agents/coord-client.ts +20 -0
- package/src/core/agents/live-authority-v3.ts +17 -0
- package/src/core/agents/live-lifecycle-v3.ts +52 -14
- package/src/core/agents/mailbox.ts +26 -17
- package/src/core/agents/render/prompt-context.ts +10 -12
- package/src/core/agents/render/session-context.ts +9 -32
- package/src/core/agents/rules/stop-hook.ts +3 -3
- package/src/core/agents/state/heartbeat-writer.ts +29 -12
- package/src/core/agents/state/live-coordination-writer.ts +29 -0
- package/src/core/agents/state/pidmap.ts +13 -0
- package/src/core/agents/state/proc-start.ts +62 -2
- package/src/core/agents/state/process-handle.ts +165 -0
- package/src/core/agents/state/stale-sweep.ts +247 -82
- package/src/core/agents/state/unchanged-file.ts +456 -0
- package/src/core/artifacts/delivery-card.ts +7 -1
- package/src/core/context/index.ts +446 -136
- package/src/core/context/observation.ts +29 -0
- package/src/core/diagnostics/bundle.ts +23 -18
- package/src/core/diagnostics/path-identity.ts +15 -0
- package/src/core/diagnostics/sanitize.ts +4 -3
- package/src/core/events/v3/authority-outbox.ts +56 -0
- package/src/core/events/v3/generation-custody.ts +59 -0
- package/src/core/events/v3/producers/coordination-recorder.ts +15 -0
- package/src/core/events/v3/producers/recorder.ts +94 -68
- package/src/core/governor/planning.ts +20 -8
- package/src/core/governor/service-read.ts +128 -20
- package/src/core/governor/service.ts +119 -42
- package/src/core/hooks/cli.ts +33 -5
- package/src/core/inbox/contract.ts +2 -1
- package/src/core/inbox/index.ts +2 -0
- package/src/core/inbox/peer-messages.ts +94 -0
- package/src/core/inbox/service.ts +88 -22
- package/src/core/inbox/targets.ts +148 -0
- package/src/core/inbox/watcher.ts +8 -9
- package/src/core/journal/index.ts +19 -2
- package/src/core/resources/contract.ts +4 -0
- package/src/core/resources/service-status.ts +35 -12
- package/src/core/resources/service.ts +57 -95
- package/src/core/resources/status.ts +57 -8
- package/src/core/semantic/service.ts +2 -8
- package/src/core/servers/index.ts +376 -141
- package/src/core/servers/tunnels.ts +57 -13
- package/src/core/supervisor/contract.ts +13 -0
- package/src/core/supervisor/explanations.ts +4 -1
- package/src/core/supervisor/findings.ts +68 -10
- package/src/core/supervisor/service.ts +87 -26
- package/src/core/supervisor/services.ts +26 -8
- package/src/core/supervisor/status.ts +89 -12
- package/src/core/supervisor/timeline.ts +2 -1
- package/src/core/turn-rollback/content.ts +499 -0
- package/src/core/turn-rollback/index.ts +580 -0
- package/src/core/work/state.ts +7 -7
- package/src/core/workflow/engine.ts +131 -49
- package/src/core/workflow/failure-evidence.ts +141 -0
- package/src/core/workflow/index.ts +12 -2
- package/src/core/workflow/live-session-v3.ts +146 -5
- package/src/core/workflow/proof.ts +134 -26
- package/src/core/workflow/recovery-audit.ts +181 -0
- package/src/core/workflow/run-state.ts +165 -34
- package/src/core/workflow/spawn-claude.ts +18 -0
- package/src/core/workflow/spawn-codex.ts +13 -0
- package/src/core/workflow/spawn-cursor.ts +18 -0
- package/src/core/workflow/spawn-opencode.ts +14 -0
- package/src/core/workflow/types.ts +51 -17
- package/src/core/workflow/workspaces/index.ts +0 -1
- package/src/core/workflow/workspaces/leases.ts +48 -17
- package/src/core/workflow/workspaces/local-git.ts +388 -7
- package/src/core/workflow/workspaces/ownership.ts +232 -0
- package/src/core/workflow/workspaces/reclaim.ts +62 -0
- package/src/core/workflow/workspaces/recovery.ts +45 -10
- package/src/core/workflow/workspaces/state.ts +20 -0
- package/src/lib/browser/windows-chrome.ts +346 -86
- package/src/lib/docs-links.ts +49 -2
- package/src/lib/guarded-remove.ts +132 -14
- package/src/lib/identities/assume.ts +4 -0
- package/src/lib/tunnel/state.ts +70 -15
- package/src/lib/zip/index.ts +483 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
1
|
import { randomUUID } from "node:crypto";
|
|
3
2
|
import {
|
|
4
3
|
closeSync,
|
|
@@ -10,9 +9,15 @@ import {
|
|
|
10
9
|
unlinkSync,
|
|
11
10
|
writeFileSync,
|
|
12
11
|
} from "node:fs";
|
|
13
|
-
import { hostname
|
|
12
|
+
import { hostname } from "node:os";
|
|
14
13
|
import { resolve } from "node:path";
|
|
15
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
type ProcessGenerationSignalDependencies,
|
|
16
|
+
type ProcessGenerationVerdict,
|
|
17
|
+
processStartToken,
|
|
18
|
+
signalIfSameGeneration,
|
|
19
|
+
} from "../agents/state/proc-start.ts";
|
|
20
|
+
import { resolveBinName } from "../config.ts";
|
|
16
21
|
import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/logger.ts";
|
|
17
22
|
import { stateDirMode, stateFileMode } from "../storage/modes.ts";
|
|
18
23
|
import {
|
|
@@ -22,7 +27,11 @@ import {
|
|
|
22
27
|
type ResourceServiceStatusRecord,
|
|
23
28
|
} from "./contract.ts";
|
|
24
29
|
import { sampleResources } from "./sampler.ts";
|
|
25
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
type ResourceProcessDependencies,
|
|
32
|
+
readResourceServiceStatus,
|
|
33
|
+
resourceProcessLiveness,
|
|
34
|
+
} from "./service-status.ts";
|
|
26
35
|
import { resourcePaths, writePrivateJsonAtomic } from "./storage.ts";
|
|
27
36
|
|
|
28
37
|
export const RESOURCE_SERVICE_DEFAULT_INTERVAL_MS = 2_000;
|
|
@@ -46,102 +55,47 @@ export interface RunResourceServiceInput {
|
|
|
46
55
|
wait?: (milliseconds: number) => Promise<void>;
|
|
47
56
|
}
|
|
48
57
|
|
|
49
|
-
export
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export async function spawnResourceService(
|
|
54
|
-
coordRootRaw: string,
|
|
55
|
-
options: { intervalMs?: number } = {},
|
|
56
|
-
): Promise<ResourceServiceStatus> {
|
|
57
|
-
const coordRoot = resolve(coordRootRaw);
|
|
58
|
-
const current = readResourceServiceStatus(coordRoot);
|
|
59
|
-
if (current.running) {
|
|
60
|
-
throw new Error(`resource observer is already running under pid ${current.record?.pid}`);
|
|
61
|
-
}
|
|
62
|
-
const intervalMs = normalizeInterval(options.intervalMs);
|
|
63
|
-
const paths = resourcePaths(coordRoot);
|
|
64
|
-
mkdirSync(paths.root, { recursive: true, mode: stateDirMode() });
|
|
65
|
-
rmSync(paths.stop, { force: true });
|
|
66
|
-
const harnBin = new URL("../../../bin/harn", import.meta.url).pathname;
|
|
67
|
-
if (!existsSync(harnBin)) throw new Error(`cannot find harn executable at ${harnBin}`);
|
|
68
|
-
let spawnError: Error | undefined;
|
|
69
|
-
const child = spawn(
|
|
70
|
-
harnBin,
|
|
71
|
-
["resources", "service", "daemon", "--root", coordRoot, "--interval-ms", String(intervalMs)],
|
|
72
|
-
{
|
|
73
|
-
cwd: coordRoot,
|
|
74
|
-
detached: true,
|
|
75
|
-
stdio: ["ignore", "ignore", "ignore"],
|
|
76
|
-
env: {
|
|
77
|
-
...process.env,
|
|
78
|
-
HARNERY_COORD_ROOT_OVERRIDE: coordRoot,
|
|
79
|
-
HARNERY_OUTPUT_SESSION_TEE: "0",
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
);
|
|
83
|
-
child.once("error", (error) => {
|
|
84
|
-
spawnError = error;
|
|
85
|
-
});
|
|
86
|
-
child.unref();
|
|
87
|
-
const deadline = Date.now() + 5_000;
|
|
88
|
-
while (Date.now() < deadline) {
|
|
89
|
-
await delay(50);
|
|
90
|
-
if (spawnError) throw spawnError;
|
|
91
|
-
const status = readResourceServiceStatus(coordRoot);
|
|
92
|
-
if (status.running && status.record?.pid === child.pid) return status;
|
|
93
|
-
if (child.exitCode !== null) break;
|
|
94
|
-
}
|
|
95
|
-
throw new Error(`resource observer failed to start; inspect ${paths.service}`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export async function ensureResourceServiceRunning(
|
|
99
|
-
coordRoot: string,
|
|
100
|
-
): Promise<EnsureResourceServiceResult> {
|
|
101
|
-
const status = readResourceServiceStatus(coordRoot);
|
|
102
|
-
if (status.running) return { state: "running", status };
|
|
103
|
-
if (platform() !== "linux") {
|
|
104
|
-
return {
|
|
105
|
-
state: "unsupported",
|
|
106
|
-
status,
|
|
107
|
-
error: `resource observer does not yet support ${platform()}`,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
try {
|
|
111
|
-
const started = await spawnResourceService(coordRoot);
|
|
112
|
-
return { state: "started", status: started };
|
|
113
|
-
} catch (error) {
|
|
114
|
-
return {
|
|
115
|
-
state: "unavailable",
|
|
116
|
-
status: readResourceServiceStatus(coordRoot),
|
|
117
|
-
error: error instanceof Error ? error.message : String(error),
|
|
118
|
-
};
|
|
119
|
-
}
|
|
58
|
+
export interface ResourceServiceStopResult extends ResourceServiceStatus {
|
|
59
|
+
signal_sent: boolean;
|
|
60
|
+
signal_verdict: ProcessGenerationVerdict;
|
|
120
61
|
}
|
|
121
62
|
|
|
122
|
-
export function requestResourceServiceStop(
|
|
63
|
+
export function requestResourceServiceStop(
|
|
64
|
+
coordRootRaw: string,
|
|
65
|
+
dependencies: ResourceProcessDependencies & ProcessGenerationSignalDependencies = {},
|
|
66
|
+
): ResourceServiceStopResult {
|
|
123
67
|
const coordRoot = resolve(coordRootRaw);
|
|
124
68
|
const paths = resourcePaths(coordRoot);
|
|
125
|
-
const status = readResourceServiceStatus(coordRoot);
|
|
69
|
+
const status = readResourceServiceStatus(coordRoot, Date.now(), dependencies);
|
|
126
70
|
mkdirSync(paths.root, { recursive: true, mode: stateDirMode() });
|
|
127
71
|
writePrivateJsonAtomic(paths.stop, {
|
|
128
72
|
requested_at: new Date().toISOString(),
|
|
129
73
|
requested_by_pid: process.pid,
|
|
130
74
|
});
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
75
|
+
const signal =
|
|
76
|
+
status.liveness === "running" && status.record?.host === hostname()
|
|
77
|
+
? signalIfSameGeneration(
|
|
78
|
+
status.record.pid,
|
|
79
|
+
status.record.start_token,
|
|
80
|
+
"SIGTERM",
|
|
81
|
+
dependencies,
|
|
82
|
+
)
|
|
83
|
+
: { sent: false, verdict: status.generation };
|
|
84
|
+
return {
|
|
85
|
+
...readResourceServiceStatus(coordRoot, Date.now(), dependencies),
|
|
86
|
+
signal_sent: signal.sent,
|
|
87
|
+
signal_verdict: signal.verdict,
|
|
88
|
+
};
|
|
139
89
|
}
|
|
140
90
|
|
|
141
91
|
export async function runResourceService(
|
|
142
92
|
input: RunResourceServiceInput,
|
|
143
93
|
): Promise<ResourceServiceStatusRecord> {
|
|
144
94
|
const coordRoot = resolve(input.coordRoot);
|
|
95
|
+
const current = readResourceServiceStatus(coordRoot);
|
|
96
|
+
if (current.liveness === "unknown") throw unknownResourceOwnerError();
|
|
97
|
+
if (current.liveness === "running")
|
|
98
|
+
throw new Error(`resource observer is already running under pid ${current.record?.pid}`);
|
|
145
99
|
const paths = resourcePaths(coordRoot);
|
|
146
100
|
const intervalMs = normalizeInterval(input.intervalMs);
|
|
147
101
|
const now = input.now ?? (() => new Date());
|
|
@@ -253,7 +207,9 @@ function acquireLease(coordRoot: string, now: Date): ResourceServiceLease {
|
|
|
253
207
|
} catch (error) {
|
|
254
208
|
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
255
209
|
const existing = readLease(paths.lease);
|
|
256
|
-
|
|
210
|
+
const liveness = existing ? leaseIsLive(existing) : "unknown";
|
|
211
|
+
if (liveness === "unknown") throw unknownResourceOwnerError();
|
|
212
|
+
if (existing && liveness === "running") {
|
|
257
213
|
throw new Error(`resource observer lease is held by pid ${existing.pid}`);
|
|
258
214
|
}
|
|
259
215
|
try {
|
|
@@ -279,7 +235,13 @@ function releaseLease(path: string, nonce: string): void {
|
|
|
279
235
|
function readLease(path: string): ResourceServiceLease | undefined {
|
|
280
236
|
try {
|
|
281
237
|
const value = JSON.parse(readFileSync(path, "utf8")) as ResourceServiceLease;
|
|
282
|
-
return value &&
|
|
238
|
+
return value &&
|
|
239
|
+
Number.isSafeInteger(value.pid) &&
|
|
240
|
+
value.pid > 0 &&
|
|
241
|
+
typeof value.host === "string" &&
|
|
242
|
+
value.host.length > 0 &&
|
|
243
|
+
typeof value.nonce === "string" &&
|
|
244
|
+
value.nonce.length > 0
|
|
283
245
|
? value
|
|
284
246
|
: undefined;
|
|
285
247
|
} catch {
|
|
@@ -287,14 +249,14 @@ function readLease(path: string): ResourceServiceLease | undefined {
|
|
|
287
249
|
}
|
|
288
250
|
}
|
|
289
251
|
|
|
290
|
-
function leaseIsLive(lease: ResourceServiceLease):
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
252
|
+
function leaseIsLive(lease: ResourceServiceLease): ResourceServiceStatus["liveness"] {
|
|
253
|
+
return resourceProcessLiveness(lease).liveness;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function unknownResourceOwnerError(): Error {
|
|
257
|
+
return new Error(
|
|
258
|
+
`resource observer process identity is unknown; run ${resolveBinName()} supervisor stop to write the durable stop request before starting again`,
|
|
259
|
+
);
|
|
298
260
|
}
|
|
299
261
|
|
|
300
262
|
function log(
|
|
@@ -10,8 +10,9 @@ import {
|
|
|
10
10
|
SUPERVISOR_PRESSURE_SCHEMA_VERSION,
|
|
11
11
|
type SupervisorCapability,
|
|
12
12
|
type SupervisorPressureRecord,
|
|
13
|
+
type SupervisorStatus,
|
|
13
14
|
} from "../supervisor/contract.ts";
|
|
14
|
-
import { readSupervisorStatus } from "../supervisor/status.ts";
|
|
15
|
+
import { installedSupervisorBuild, readSupervisorStatus } from "../supervisor/status.ts";
|
|
15
16
|
import { supervisorPaths } from "../supervisor/storage.ts";
|
|
16
17
|
import {
|
|
17
18
|
RESOURCE_SNAPSHOT_SCHEMA_VERSION,
|
|
@@ -43,7 +44,17 @@ export interface ResourceStatus {
|
|
|
43
44
|
platform: NodeJS.Platform | null;
|
|
44
45
|
namespace: ResourceSnapshot["namespace"];
|
|
45
46
|
support: ResourceSnapshot["support"] | null;
|
|
46
|
-
writer:
|
|
47
|
+
writer: Pick<
|
|
48
|
+
SupervisorStatus,
|
|
49
|
+
| "running"
|
|
50
|
+
| "stale"
|
|
51
|
+
| "generation"
|
|
52
|
+
| "liveness"
|
|
53
|
+
| "running_build"
|
|
54
|
+
| "installed_build"
|
|
55
|
+
| "restart_needed"
|
|
56
|
+
| "build_status"
|
|
57
|
+
>;
|
|
47
58
|
/**
|
|
48
59
|
* The one published pressure assessment, read from the observer's record so
|
|
49
60
|
* this surface, the prompt notice, the dashboard, and a diagnostic bundle all
|
|
@@ -222,12 +233,16 @@ export function formatResourceSummary(status: ResourceStatus): string {
|
|
|
222
233
|
? "unknown"
|
|
223
234
|
: `${formatBytes(m.memory_available_bytes)} available`;
|
|
224
235
|
const diskText = disk ? `${formatBytes(disk.available_bytes as number)} available` : "unknown";
|
|
225
|
-
return `${lead} ${status.namespace}/${status.platform}: CPU ${cpu}, RAM ${ram}, disk ${diskText}; ${Math.round((status.sample_age_ms ?? 0) / 1_000)}s old${status.support?.state === "partial" ? "; partial" : ""}${!status.writer.running ? "
|
|
236
|
+
return `${lead} ${status.namespace}/${status.platform}: CPU ${cpu}, RAM ${ram}, disk ${diskText}; ${Math.round((status.sample_age_ms ?? 0) / 1_000)}s old${status.support?.state === "partial" ? "; partial" : ""}${!status.writer.running ? `; writer ${status.writer.liveness === "unknown" ? "unknown" : status.writer.stale ? "stale" : "stopped"}` : ""}.`;
|
|
226
237
|
}
|
|
227
238
|
|
|
228
239
|
export function formatResourceStatus(status: ResourceStatus, bin: string): string {
|
|
229
240
|
const a = status.assessment;
|
|
230
241
|
const lines = [formatResourceSummary(status)];
|
|
242
|
+
const writer = status.writer;
|
|
243
|
+
lines.push(
|
|
244
|
+
`Writer: ${writer.liveness}; generation ${writer.generation}; running build ${writer.running_build ? `${writer.running_build.version} (${writer.running_build.revision})` : "unknown"}; installed build ${writer.installed_build.version} (${writer.installed_build.revision}); ${writer.build_status}.`,
|
|
245
|
+
);
|
|
231
246
|
lines.push(
|
|
232
247
|
`Assessment: ${a.state} at ${a.scope} scope; limiting resource ${a.limiting_resource}; trend ${a.trend}; evidence ${a.evidence_state}; recommended action ${a.recommended_action}.`,
|
|
233
248
|
);
|
|
@@ -247,7 +262,11 @@ export function formatResourceStatus(status: ResourceStatus, bin: string): strin
|
|
|
247
262
|
lines.push(
|
|
248
263
|
`Unattributed memory: ${Math.round(a.unattributed_memory_percent)}% of machine memory has no validated owner.`,
|
|
249
264
|
);
|
|
250
|
-
if (status.
|
|
265
|
+
if (status.writer.liveness === "unknown")
|
|
266
|
+
lines.push(
|
|
267
|
+
`Inspect the writer with ${bin} supervisor status. Run ${bin} supervisor stop to write its durable stop request before starting again.`,
|
|
268
|
+
);
|
|
269
|
+
else if (status.state !== "fresh" || !status.writer.running)
|
|
251
270
|
lines.push(
|
|
252
271
|
`Start or inspect the writer with ${bin} supervisor start --keep-alive and ${bin} supervisor status.`,
|
|
253
272
|
);
|
|
@@ -345,7 +364,15 @@ export function readPublishedPressure(
|
|
|
345
364
|
});
|
|
346
365
|
// A record can only be trusted while its writer is alive. Every surface makes
|
|
347
366
|
// the same check here so none of them reports a state the others do not.
|
|
348
|
-
|
|
367
|
+
const observer = writer ?? readWriterState(coordRoot, nowMs);
|
|
368
|
+
if (observer.liveness === "unknown") {
|
|
369
|
+
return gap(
|
|
370
|
+
"error",
|
|
371
|
+
"pressure_observer_unknown",
|
|
372
|
+
"Local resource pressure cannot be determined because the resource observer's process identity is unknown.",
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
if (!observer.running) {
|
|
349
376
|
return gap(
|
|
350
377
|
"expired",
|
|
351
378
|
"pressure_observer_not_running",
|
|
@@ -407,7 +434,7 @@ function readWriterState(coordRoot: string, nowMs: number): ResourceStatus["writ
|
|
|
407
434
|
// Keep malformed service metadata from making the read expensive. The shared
|
|
408
435
|
// status reader retains ownership of PID/start-token liveness checks.
|
|
409
436
|
if (statSync(supervisorPaths(coordRoot).service).size > 65_536) {
|
|
410
|
-
return
|
|
437
|
+
return unknownWriterState();
|
|
411
438
|
}
|
|
412
439
|
const writer = readSupervisorStatus(coordRoot, nowMs);
|
|
413
440
|
const futureHeartbeat =
|
|
@@ -415,13 +442,35 @@ function readWriterState(coordRoot: string, nowMs: number): ResourceStatus["writ
|
|
|
415
442
|
return {
|
|
416
443
|
running: writer.running && !futureHeartbeat,
|
|
417
444
|
stale: writer.stale || !!futureHeartbeat,
|
|
445
|
+
generation: writer.generation,
|
|
446
|
+
liveness: writer.liveness,
|
|
447
|
+
running_build: writer.running_build,
|
|
448
|
+
installed_build: writer.installed_build,
|
|
449
|
+
restart_needed: writer.restart_needed,
|
|
450
|
+
build_status: writer.build_status,
|
|
418
451
|
};
|
|
419
|
-
} catch {
|
|
452
|
+
} catch (error) {
|
|
420
453
|
// A missing writer must not break normal coordination.
|
|
421
|
-
return {
|
|
454
|
+
return {
|
|
455
|
+
...unknownWriterState(),
|
|
456
|
+
liveness: (error as NodeJS.ErrnoException).code === "ENOENT" ? "stopped" : "unknown",
|
|
457
|
+
};
|
|
422
458
|
}
|
|
423
459
|
}
|
|
424
460
|
|
|
461
|
+
function unknownWriterState(): ResourceStatus["writer"] {
|
|
462
|
+
return {
|
|
463
|
+
running: false,
|
|
464
|
+
stale: false,
|
|
465
|
+
generation: "unknown",
|
|
466
|
+
liveness: "unknown",
|
|
467
|
+
running_build: null,
|
|
468
|
+
installed_build: installedSupervisorBuild(),
|
|
469
|
+
restart_needed: true,
|
|
470
|
+
build_status: "running build unknown; restart needed",
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
425
474
|
function validPressureRecord(v: unknown): v is SupervisorPressureRecord {
|
|
426
475
|
if (
|
|
427
476
|
!object(v) ||
|
|
@@ -225,19 +225,13 @@ export async function ensureSemanticServiceRunning(
|
|
|
225
225
|
|
|
226
226
|
export function requestSemanticServiceStop(coordRootRaw: string): SemanticServiceStatus {
|
|
227
227
|
const coordRoot = resolve(coordRootRaw);
|
|
228
|
-
const status = readSemanticServiceStatus(coordRoot);
|
|
229
228
|
mkdirSync(semanticPaths(coordRoot).root, { recursive: true, mode: stateDirMode() });
|
|
230
229
|
writePrivateJsonAtomic(semanticPaths(coordRoot).stop, {
|
|
231
230
|
requested_at: new Date().toISOString(),
|
|
232
231
|
requested_by_pid: process.pid,
|
|
233
232
|
});
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
process.kill(status.record.pid, "SIGTERM");
|
|
237
|
-
} catch {
|
|
238
|
-
// The durable stop request remains for a racing or restarted daemon.
|
|
239
|
-
}
|
|
240
|
-
}
|
|
233
|
+
// This record has no generation witness. The durable request is the stop
|
|
234
|
+
// authority; a numeric PID cannot authorize an additional signal.
|
|
241
235
|
return readSemanticServiceStatus(coordRoot);
|
|
242
236
|
}
|
|
243
237
|
|