harnery 0.41.0 → 0.42.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/dist/commander.js +3 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +9 -1
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +113 -12
- package/dist/commands/browse.d.ts +164 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +263 -51
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +52 -0
- package/dist/commands/disk.d.ts +6 -0
- package/dist/commands/disk.d.ts.map +1 -0
- package/dist/commands/disk.js +80 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +48 -1
- package/dist/commands/files.d.ts +16 -0
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +54 -2
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +43 -9
- package/dist/commands/rm.d.ts +4 -0
- package/dist/commands/rm.d.ts.map +1 -0
- package/dist/commands/rm.js +87 -0
- package/dist/commands/servers.d.ts +7 -0
- package/dist/commands/servers.d.ts.map +1 -0
- package/dist/commands/servers.js +394 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +82 -2
- package/dist/core/adapters/profiles.d.ts +9 -0
- package/dist/core/adapters/profiles.d.ts.map +1 -1
- package/dist/core/adapters/profiles.js +22 -1
- package/dist/core/adapters/types.d.ts +9 -0
- package/dist/core/adapters/types.d.ts.map +1 -1
- package/dist/core/agents/cli.js +13 -6
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +2 -0
- package/dist/core/agents/state/live-coordination-view.d.ts +20 -0
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +60 -0
- package/dist/core/artifacts/delivery-card.js +9 -2
- package/dist/core/artifacts/index.d.ts +140 -4
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +522 -49
- package/dist/core/config.d.ts +68 -11
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +128 -3
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +2 -2
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +16 -0
- package/dist/core/events/v3/projection.js +9 -1
- package/dist/core/hooks/adapter/events.d.ts +17 -0
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +5 -0
- package/dist/core/hooks/adapter/runtime-telemetry.js +3 -1
- package/dist/core/hooks/adapter/wiring.d.ts +37 -7
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +62 -4
- package/dist/core/hooks/cli.js +45 -6
- package/dist/core/hooks/run-markers.d.ts +37 -0
- package/dist/core/hooks/run-markers.d.ts.map +1 -0
- package/dist/core/hooks/run-markers.js +109 -0
- package/dist/core/servers/index.d.ts +211 -0
- package/dist/core/servers/index.d.ts.map +1 -0
- package/dist/core/servers/index.js +560 -0
- package/dist/core/servers/net.d.ts +58 -0
- package/dist/core/servers/net.d.ts.map +1 -0
- package/dist/core/servers/net.js +270 -0
- package/dist/core/servers/tunnels.d.ts +14 -0
- package/dist/core/servers/tunnels.d.ts.map +1 -0
- package/dist/core/servers/tunnels.js +33 -0
- package/dist/core/storage/builtins.js +1 -0
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +30 -1
- package/dist/core/workflow/sandbox-projection.d.ts +13 -2
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
- package/dist/core/workflow/sandbox-projection.js +23 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +6 -1
- package/dist/core/workflow/spawn-codex.d.ts +2 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +20 -1
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +6 -1
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +4 -1
- package/dist/core/workflow/types.d.ts +47 -2
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/worker-access.d.ts +92 -0
- package/dist/core/workflow/worker-access.d.ts.map +1 -0
- package/dist/core/workflow/worker-access.js +450 -0
- package/dist/lib/browser/batch-steps.d.ts +18 -0
- package/dist/lib/browser/batch-steps.d.ts.map +1 -0
- package/dist/lib/browser/batch-steps.js +82 -0
- package/dist/lib/browser/collect/collector.d.ts +99 -0
- package/dist/lib/browser/collect/collector.d.ts.map +1 -0
- package/dist/lib/browser/collect/collector.js +202 -0
- package/dist/lib/browser/collect/fields.d.ts +31 -0
- package/dist/lib/browser/collect/fields.d.ts.map +1 -0
- package/dist/lib/browser/collect/fields.js +75 -0
- package/dist/lib/browser/collect/index.d.ts +6 -0
- package/dist/lib/browser/collect/index.d.ts.map +1 -0
- package/dist/lib/browser/collect/index.js +5 -0
- package/dist/lib/browser/collect/page-driver.d.ts +14 -0
- package/dist/lib/browser/collect/page-driver.d.ts.map +1 -0
- package/dist/lib/browser/collect/page-driver.js +306 -0
- package/dist/lib/browser/collect/presets.d.ts +52 -0
- package/dist/lib/browser/collect/presets.d.ts.map +1 -0
- package/dist/lib/browser/collect/presets.js +176 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts +85 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts.map +1 -0
- package/dist/lib/browser/collect/scroll-plan.js +118 -0
- package/dist/lib/browser/index.d.ts +1 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.d.ts +2 -0
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.js +13 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +10 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.js +1 -0
- package/dist/lib/browser/windows-chrome.d.ts +59 -0
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -0
- package/dist/lib/browser/windows-chrome.js +179 -0
- package/dist/lib/claude-desktop-share.d.ts +136 -0
- package/dist/lib/claude-desktop-share.d.ts.map +1 -0
- package/dist/lib/claude-desktop-share.js +394 -0
- package/dist/lib/disk-usage.d.ts +50 -0
- package/dist/lib/disk-usage.d.ts.map +1 -0
- package/dist/lib/disk-usage.js +303 -0
- package/dist/lib/docs-lint.d.ts +6 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +8 -3
- package/dist/lib/guarded-remove.d.ts +34 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -0
- package/dist/lib/guarded-remove.js +310 -0
- package/dist/lib/instructions/templates.js +8 -8
- package/dist/lib/tunnel/error-page.d.ts +1 -1
- package/dist/lib/tunnel/error-page.d.ts.map +1 -1
- package/dist/lib/tunnel/error-page.js +19 -8
- package/dist/lib/tunnel/gate.js +20 -1
- package/dist/lib/tunnel/path-scope.d.ts +22 -0
- package/dist/lib/tunnel/path-scope.d.ts.map +1 -0
- package/dist/lib/tunnel/path-scope.js +62 -0
- package/dist/lib/tunnel/state.d.ts +5 -0
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +17 -3
- package/package.json +6 -1
- package/schemas/config.schema.json +91 -0
- package/src/commander.ts +19 -0
- package/src/commands/agents.ts +14 -0
- package/src/commands/artifacts.ts +171 -19
- package/src/commands/browse.ts +400 -57
- package/src/commands/claude-desktop.ts +61 -0
- package/src/commands/disk.ts +114 -0
- package/src/commands/doctor.ts +52 -1
- package/src/commands/files.ts +93 -2
- package/src/commands/init.ts +55 -5
- package/src/commands/rm.ts +115 -0
- package/src/commands/servers.ts +506 -0
- package/src/commands/tunnel.ts +102 -2
- package/src/core/adapters/profiles.ts +22 -1
- package/src/core/adapters/types.ts +10 -0
- package/src/core/agents/cli.ts +14 -6
- package/src/core/agents/render/session-context.ts +1 -0
- package/src/core/agents/state/live-coordination-view.ts +73 -0
- package/src/core/artifacts/delivery-card.ts +10 -2
- package/src/core/artifacts/index.ts +697 -44
- package/src/core/config.ts +183 -4
- package/src/core/diagnostics/bundle.ts +2 -1
- package/src/core/events/v3/producers/recorder.ts +16 -0
- package/src/core/events/v3/projection.ts +11 -1
- package/src/core/hooks/adapter/events.ts +19 -0
- package/src/core/hooks/adapter/runtime-telemetry.ts +3 -1
- package/src/core/hooks/adapter/wiring.ts +116 -8
- package/src/core/hooks/cli.ts +47 -6
- package/src/core/hooks/run-markers.ts +132 -0
- package/src/core/servers/index.ts +712 -0
- package/src/core/servers/net.ts +283 -0
- package/src/core/servers/tunnels.ts +39 -0
- package/src/core/storage/builtins.ts +1 -0
- package/src/core/workflow/engine.ts +38 -1
- package/src/core/workflow/sandbox-projection.ts +38 -2
- package/src/core/workflow/spawn-claude.ts +5 -1
- package/src/core/workflow/spawn-codex.ts +18 -1
- package/src/core/workflow/spawn-cursor.ts +5 -1
- package/src/core/workflow/spawn-opencode.ts +4 -1
- package/src/core/workflow/types.ts +62 -2
- package/src/core/workflow/worker-access.ts +525 -0
- package/src/lib/browser/batch-steps.ts +79 -0
- package/src/lib/browser/collect/collector.ts +306 -0
- package/src/lib/browser/collect/fields.ts +87 -0
- package/src/lib/browser/collect/index.ts +47 -0
- package/src/lib/browser/collect/page-driver.ts +327 -0
- package/src/lib/browser/collect/presets.ts +237 -0
- package/src/lib/browser/collect/scroll-plan.ts +179 -0
- package/src/lib/browser/index.ts +1 -0
- package/src/lib/browser/page-review-judge.ts +16 -0
- package/src/lib/browser/qa-run-contracts.ts +11 -0
- package/src/lib/browser/qa-run.ts +1 -0
- package/src/lib/browser/windows-chrome.ts +227 -0
- package/src/lib/claude-desktop-share.ts +511 -0
- package/src/lib/disk-usage.ts +347 -0
- package/src/lib/docs-lint.ts +8 -3
- package/src/lib/guarded-remove.ts +376 -0
- package/src/lib/instructions/templates.ts +8 -8
- package/src/lib/tunnel/error-page.ts +25 -9
- package/src/lib/tunnel/gate.ts +22 -1
- package/src/lib/tunnel/path-scope.ts +73 -0
- package/src/lib/tunnel/state.ts +20 -3
|
@@ -13,19 +13,68 @@
|
|
|
13
13
|
import { existsSync, readFileSync } from "node:fs";
|
|
14
14
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
|
-
import { resolveBinName } from "../../config.js";
|
|
16
|
+
import { codexWindowsBridgeConfig, resolveBinName } from "../../config.js";
|
|
17
17
|
import { ADAPTER_SPECS, } from "./events.js";
|
|
18
18
|
import { checkOpenCodePlugin, isOwnedOpenCodePlugin } from "./opencode-plugin.js";
|
|
19
19
|
/** Bound Cursor Stop `followup_message` retries so a missed ritual cannot loop 5 times. */
|
|
20
20
|
export const CURSOR_STOP_FOLLOWUP_LOOP_LIMIT = 2;
|
|
21
21
|
/** Build a hook entry in the adapter's shape. */
|
|
22
22
|
export function makeEntry(shape, command, extras) {
|
|
23
|
-
if (shape !== "cursor")
|
|
24
|
-
return {
|
|
23
|
+
if (shape !== "cursor") {
|
|
24
|
+
return {
|
|
25
|
+
hooks: [
|
|
26
|
+
orderHandlerKeys({
|
|
27
|
+
type: "command",
|
|
28
|
+
command,
|
|
29
|
+
commandWindows: extras?.commandWindows,
|
|
30
|
+
timeout: extras?.timeout,
|
|
31
|
+
}),
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
25
35
|
return extras?.loop_limit !== undefined
|
|
26
36
|
? { command, loop_limit: extras.loop_limit }
|
|
27
37
|
: { command };
|
|
28
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Rebuild a handler with Harnery's fields first in a fixed order, dropping
|
|
41
|
+
* undefined ones and keeping any other fields after them.
|
|
42
|
+
*/
|
|
43
|
+
export function orderHandlerKeys(handler) {
|
|
44
|
+
const { type, command, commandWindows, timeout, ...rest } = handler;
|
|
45
|
+
return {
|
|
46
|
+
type,
|
|
47
|
+
command,
|
|
48
|
+
...(commandWindows !== undefined ? { commandWindows } : {}),
|
|
49
|
+
...(timeout !== undefined ? { timeout } : {}),
|
|
50
|
+
...rest,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The Windows command for an opted-in project (ADR 0197). It hands the hook to
|
|
55
|
+
* the bridge entry point when that is on PATH and the working directory is a
|
|
56
|
+
* WSL UNC path, and otherwise runs the usual command. `ProviderPath` drops the
|
|
57
|
+
* provider prefix that `Path` carries. The trailing `exit $LASTEXITCODE` is
|
|
58
|
+
* required: without it PowerShell reports any non-zero exit from inside the
|
|
59
|
+
* `if` block as 1, which would turn a hook's blocking exit 2 into a failure.
|
|
60
|
+
*/
|
|
61
|
+
export function hookCommandWindows(spec, agentHookPath, subcommand, adapter, entryPoint) {
|
|
62
|
+
const direct = hookCommand(spec, agentHookPath, subcommand, adapter);
|
|
63
|
+
return (`if ((Get-Command ${entryPoint} -ErrorAction Ignore) -and $PWD.ProviderPath -like '\\\\wsl*') ` +
|
|
64
|
+
`{ ${entryPoint} ${subcommand} --adapter ${adapter} } else { ${direct} }; exit $LASTEXITCODE`);
|
|
65
|
+
}
|
|
66
|
+
/** The `commandWindows` init writes for one event, or undefined for none. */
|
|
67
|
+
export function expectedCommandWindows(spec, agentHookPath, subcommand, adapter, options) {
|
|
68
|
+
if (!spec.windowsBridgeHooks || !options?.windowsBridgeEntryPoint)
|
|
69
|
+
return undefined;
|
|
70
|
+
return hookCommandWindows(spec, agentHookPath, subcommand, adapter, options.windowsBridgeEntryPoint);
|
|
71
|
+
}
|
|
72
|
+
/** The `timeout` init writes on one event's Harnery hook, or undefined for none. */
|
|
73
|
+
export function hookTimeoutFor(spec, subcommand) {
|
|
74
|
+
if (!spec.hookTimeouts)
|
|
75
|
+
return undefined;
|
|
76
|
+
return spec.hookTimeouts.bySubcommand?.[subcommand] ?? spec.hookTimeouts.default;
|
|
77
|
+
}
|
|
29
78
|
/** Pin Cursor Stop followups to Harnery's loop cap. Returns true when the entry changed. */
|
|
30
79
|
export function applyCursorStopLoopLimit(group, subcommand) {
|
|
31
80
|
if (subcommand !== "stop")
|
|
@@ -107,7 +156,15 @@ export function diffWiring(settings, spec, expected) {
|
|
|
107
156
|
duplicates.push(event);
|
|
108
157
|
if (expected && commands.length > 0) {
|
|
109
158
|
const canonical = hookCommand(spec, expected.agentHookPath, event.subcommand, expected.adapter);
|
|
110
|
-
|
|
159
|
+
const timeout = hookTimeoutFor(spec, event.subcommand);
|
|
160
|
+
const commandWindows = expectedCommandWindows(spec, expected.agentHookPath, event.subcommand, expected.adapter, expected);
|
|
161
|
+
const handlerDrift = groups.some((group) => "hooks" in group &&
|
|
162
|
+
Array.isArray(group.hooks) &&
|
|
163
|
+
group.hooks.some((hook) => typeof hook.command === "string" &&
|
|
164
|
+
commandWiresSubcommand(hook.command, event.subcommand) &&
|
|
165
|
+
((timeout !== undefined && hook.timeout !== timeout) ||
|
|
166
|
+
(spec.windowsBridgeHooks === true && hook.commandWindows !== commandWindows))));
|
|
167
|
+
if (handlerDrift || commands.some((command) => command !== canonical))
|
|
111
168
|
stale.push(event);
|
|
112
169
|
}
|
|
113
170
|
const wrongKey = Object.entries(hooks).some(([key, otherGroups]) => key !== event.settingsKey &&
|
|
@@ -222,6 +279,7 @@ export function loadAdapterWiring(projectRoot) {
|
|
|
222
279
|
? {
|
|
223
280
|
agentHookPath: agentHookPathForProject(projectRoot, packageRoot),
|
|
224
281
|
adapter: id,
|
|
282
|
+
windowsBridgeEntryPoint: codexWindowsBridgeConfig(projectRoot).entryPoint,
|
|
225
283
|
}
|
|
226
284
|
: undefined);
|
|
227
285
|
if (diff.present.length === 0)
|
package/dist/core/hooks/cli.js
CHANGED
|
@@ -42,7 +42,9 @@ import { closeProcessLoggers, legacyLogFields, processLogger } from "../storage/
|
|
|
42
42
|
import { stableScopeId } from "../workflow/scope-id.js";
|
|
43
43
|
import { adapterBehavior } from "./adapter/behaviors/index.js";
|
|
44
44
|
import { detectAdapter, shouldSkipHookAdapter } from "./adapter/detect.js";
|
|
45
|
+
import { ADAPTER_SPECS } from "./adapter/events.js";
|
|
45
46
|
import { extractBashCommand, extractToolDescription, normalizeEventName, parsePayload, } from "./adapter/parse.js";
|
|
47
|
+
import { hookTimeoutFor } from "./adapter/wiring.js";
|
|
46
48
|
import { scheduleBackupSnapshot } from "./backup-schedule.js";
|
|
47
49
|
import { captureImages, detectPresence, imageJanitor, journalArchive, journalJanitor, journalRecoveryCue, playSound, recordImageArtifactsV3, resetSoundCounters, runSessionSyncExtension, soundForEvent, } from "./effects/index.js";
|
|
48
50
|
import { toolInputHash, toolTargetHash } from "./events/input-hash.js";
|
|
@@ -52,9 +54,11 @@ import { adapterPidFromEnv, parsePsChainLine, selectAnchorPid } from "./resolve/
|
|
|
52
54
|
import { extractIntentComment, resolveIntent } from "./resolve/intent.js";
|
|
53
55
|
import { resolveOwner } from "./resolve/owner.js";
|
|
54
56
|
import { inspectSessionNameDisplayImmediately, scanAssistantStatusBoxPresent, scanStatusBoxPresent, scanTranscriptRuntime, } from "./resolve/transcript.js";
|
|
57
|
+
import { clearHookRunMarker, writeHookRunMarker } from "./run-markers.js";
|
|
55
58
|
import { sessionNamePresence } from "./session-name-presence.js";
|
|
56
59
|
import { shellWaiterReason } from "./shell-waiter.js";
|
|
57
60
|
let hookHealthState;
|
|
61
|
+
let hookRunMarkerPath = null;
|
|
58
62
|
function parseArgv(argv) {
|
|
59
63
|
const out = { eventName: null, extra: [] };
|
|
60
64
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -236,7 +240,11 @@ function buildEventData(eventType, ctx) {
|
|
|
236
240
|
const forkedFrom = undefined;
|
|
237
241
|
// Assign (or recover) name + kind in-process. Idempotent: resume
|
|
238
242
|
// returns the original name; new owner consumes a counter slot.
|
|
239
|
-
|
|
243
|
+
// Workflow children stay unnamed, as on the tool path: a pool name
|
|
244
|
+
// would make them the addressee of mail held for that name.
|
|
245
|
+
const assigned = coordEnv("WORKFLOW_CHILD") === "1"
|
|
246
|
+
? undefined
|
|
247
|
+
: assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session", forkedFrom);
|
|
240
248
|
// Write the adapter pid-map row so `harn agents whoami` ppid-walks find
|
|
241
249
|
// this owner. Prefer the payload pid (the actual claude binary), then the
|
|
242
250
|
// anchor walk (the `node` ancestor for Cursor, which has no payload pid),
|
|
@@ -299,7 +307,8 @@ function buildEventData(eventType, ctx) {
|
|
|
299
307
|
// SessionStart. Mint durable display identity here so a later
|
|
300
308
|
// SessionStart remains idempotent and Codec is not stuck on the
|
|
301
309
|
// 8-character native id.
|
|
302
|
-
if (
|
|
310
|
+
if (coordEnv("WORKFLOW_CHILD") !== "1" &&
|
|
311
|
+
!readLiveCoordinationRow(ctx.coordRoot, ctx.instanceId)?.name) {
|
|
303
312
|
assignNameInProcess(ctx.coordRoot, ctx.instanceId, "session");
|
|
304
313
|
}
|
|
305
314
|
const prompt = p?.prompt ?? "";
|
|
@@ -475,6 +484,23 @@ async function main() {
|
|
|
475
484
|
const { eventName, extra } = parseArgv(process.argv.slice(2));
|
|
476
485
|
const adapter = detectAdapter(process.argv.slice(2));
|
|
477
486
|
const behavior = adapter ? adapterBehavior(adapter) : null;
|
|
487
|
+
// Adapters that kill a hook at its timeout get a start marker, so a killed
|
|
488
|
+
// run leaves evidence for doctor (ADR 0197). Written before stdin is read.
|
|
489
|
+
const markerSpec = adapter ? ADAPTER_SPECS[adapter] : undefined;
|
|
490
|
+
const markerTimeout = markerSpec && eventName && markerSpec.events.some((e) => e.subcommand === eventName)
|
|
491
|
+
? hookTimeoutFor(markerSpec, eventName)
|
|
492
|
+
: undefined;
|
|
493
|
+
if (markerTimeout !== undefined && adapter && eventName && coordEnv("AGENT_COORD_OFF") !== "1") {
|
|
494
|
+
const markerRoot = resolveCoordRoot(process.cwd());
|
|
495
|
+
if (markerRoot) {
|
|
496
|
+
hookRunMarkerPath = writeHookRunMarker(markerRoot, {
|
|
497
|
+
event: eventName,
|
|
498
|
+
adapter,
|
|
499
|
+
pid: process.pid,
|
|
500
|
+
timeout_sec: markerTimeout,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
478
504
|
const raw = await readStdin();
|
|
479
505
|
hookHealthState = beginHookHealth({
|
|
480
506
|
started_at_ms: hookStartedAt,
|
|
@@ -941,9 +967,12 @@ async function handleSessionStarted(run) {
|
|
|
941
967
|
const { autoCleanArtifacts } = await import("../artifacts/index.js");
|
|
942
968
|
const { autoCleanEventV3Archives } = await import("../events/v3/archive-retention.js");
|
|
943
969
|
const { createAutomaticMaintenanceComposition, runAutomaticMaintenancePass } = await import("../storage/maintenance-providers.js");
|
|
944
|
-
// Effect: throttled
|
|
945
|
-
//
|
|
946
|
-
//
|
|
970
|
+
// Effect: throttled hourly artifact sweep (see autoCleanArtifacts). It
|
|
971
|
+
// deletes expired workspaces, plus unheld workspaces over the per-bundle
|
|
972
|
+
// ceiling or repository budget once they have been idle past
|
|
973
|
+
// artifacts.idle_grace_hours. Each deletion is rechecked first and recorded
|
|
974
|
+
// in .harnery/artifact-deletions.jsonl. Best-effort like every janitor
|
|
975
|
+
// here: a failure logs and never blocks session start.
|
|
947
976
|
try {
|
|
948
977
|
autoCleanArtifacts(coordRoot);
|
|
949
978
|
}
|
|
@@ -1291,7 +1320,8 @@ async function handlePostToolUse(run) {
|
|
|
1291
1320
|
// status is the wrong place to ask.
|
|
1292
1321
|
try {
|
|
1293
1322
|
const row = readLiveCoordinationRow(coordRoot, owner.instance_id);
|
|
1294
|
-
|
|
1323
|
+
// A workflow child has no user-visible title to set.
|
|
1324
|
+
const name = coordEnv("WORKFLOW_CHILD") === "1" ? undefined : sessionNameDisplayPending(row);
|
|
1295
1325
|
if (name && toolResponseMintedSessionName(payload?.tool_response, name)) {
|
|
1296
1326
|
// Record the title before asking for it. The suggestion can still
|
|
1297
1327
|
// change afterwards (an assigned-name rewrite, a lifecycle re-mint, a
|
|
@@ -1469,6 +1499,8 @@ function reportSessionNameObservationUnavailable(coordRoot, instanceId, adapter,
|
|
|
1469
1499
|
process.stderr.write(`Harnery: session-name observation unavailable (${reason}); allowing this ${eventName} without display enforcement. The name remains pending.\n`);
|
|
1470
1500
|
}
|
|
1471
1501
|
async function enforcePendingSessionNameDisplay(coordRoot, instanceId, adapter, payload, coordination) {
|
|
1502
|
+
if (coordEnv("WORKFLOW_CHILD") === "1")
|
|
1503
|
+
return true;
|
|
1472
1504
|
const name = sessionNameDisplayPending(coordination);
|
|
1473
1505
|
if (!name)
|
|
1474
1506
|
return true;
|
|
@@ -1830,6 +1862,11 @@ function cleanupSessionEnd(coordRoot, instanceId) {
|
|
|
1830
1862
|
}
|
|
1831
1863
|
}
|
|
1832
1864
|
async function emitUserPromptSubmitSystemMessage(coordRoot, instanceId, sessionId, adapter, workspaceCwd, recoveryBriefing = "", hostPromptContext = "") {
|
|
1865
|
+
// Workflow children get no prompt-time coordination context, matching
|
|
1866
|
+
// SessionStart. The turn ritual is not enforced for them, the naming nudge
|
|
1867
|
+
// would register them as named peers, and rendering drains peer mail.
|
|
1868
|
+
if (coordEnv("WORKFLOW_CHILD") === "1")
|
|
1869
|
+
return false;
|
|
1833
1870
|
const behavior = adapterBehavior(adapter);
|
|
1834
1871
|
const { renderPromptContext } = await import("../agents/render/prompt-context.js");
|
|
1835
1872
|
let additionalContext = renderPromptContext({
|
|
@@ -1943,6 +1980,7 @@ function completeRecoveryInjection(coordRoot, instanceId, sessionId, recovery) {
|
|
|
1943
1980
|
}
|
|
1944
1981
|
main()
|
|
1945
1982
|
.then(async (code) => {
|
|
1983
|
+
clearHookRunMarker(hookRunMarkerPath);
|
|
1946
1984
|
writeHookHealthCompletion(hookHealthState, {
|
|
1947
1985
|
finished_at_ms: performance.now(),
|
|
1948
1986
|
finished_rss_bytes: process.memoryUsage().rss,
|
|
@@ -1957,6 +1995,7 @@ main()
|
|
|
1957
1995
|
pid: process.pid,
|
|
1958
1996
|
phase: "top-level",
|
|
1959
1997
|
});
|
|
1998
|
+
clearHookRunMarker(hookRunMarkerPath);
|
|
1960
1999
|
writeHookHealthCompletion(hookHealthState, {
|
|
1961
2000
|
finished_at_ms: performance.now(),
|
|
1962
2001
|
finished_rss_bytes: process.memoryUsage().rss,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start markers for hook runs on adapters that kill a hook at its `timeout`
|
|
3
|
+
* (ADR 0197). A Codex hook that times out has its process tree killed and the
|
|
4
|
+
* tool proceeds without that hook's checks, and the killed process never
|
|
5
|
+
* reaches the completion receipt in `health.ts`. A small marker written at
|
|
6
|
+
* start and removed at completion leaves evidence of that case for doctor.
|
|
7
|
+
*
|
|
8
|
+
* Observer-only: every write, read, and removal swallows its own errors so a
|
|
9
|
+
* marker can never change hook behavior.
|
|
10
|
+
*/
|
|
11
|
+
export interface HookRunMarker {
|
|
12
|
+
event: string;
|
|
13
|
+
adapter: string;
|
|
14
|
+
pid: number;
|
|
15
|
+
started_at: string;
|
|
16
|
+
timeout_sec: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function hookRunMarkerDir(coordRoot: string): string;
|
|
19
|
+
/** Write this run's marker; returns its path, or null when it could not be written. */
|
|
20
|
+
export declare function writeHookRunMarker(coordRoot: string, marker: Omit<HookRunMarker, "started_at">, now?: Date): string | null;
|
|
21
|
+
export declare function clearHookRunMarker(path: string | null): void;
|
|
22
|
+
export interface UnfinishedHookRuns {
|
|
23
|
+
count: number;
|
|
24
|
+
/** Count per event name, most frequent first. */
|
|
25
|
+
events: Array<{
|
|
26
|
+
event: string;
|
|
27
|
+
count: number;
|
|
28
|
+
}>;
|
|
29
|
+
latest: string | null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Markers whose run outlived its timeout plus a grace period and whose
|
|
33
|
+
* process is gone, within the window. A live process past its timeout is
|
|
34
|
+
* still running and is not counted.
|
|
35
|
+
*/
|
|
36
|
+
export declare function findUnfinishedHookRuns(coordRoot: string, nowMs?: number, windowMs?: number, isAlive?: (pid: number) => boolean): UnfinishedHookRuns;
|
|
37
|
+
//# sourceMappingURL=run-markers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-markers.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/run-markers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,uFAAuF;AACvF,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,EACzC,GAAG,OAAa,GACf,MAAM,GAAG,IAAI,CAYf;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAO5D;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,KAAK,SAAa,EAClB,QAAQ,SAAsB,EAC9B,OAAO,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAsB,GAC/C,kBAAkB,CA+BpB"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start markers for hook runs on adapters that kill a hook at its `timeout`
|
|
3
|
+
* (ADR 0197). A Codex hook that times out has its process tree killed and the
|
|
4
|
+
* tool proceeds without that hook's checks, and the killed process never
|
|
5
|
+
* reaches the completion receipt in `health.ts`. A small marker written at
|
|
6
|
+
* start and removed at completion leaves evidence of that case for doctor.
|
|
7
|
+
*
|
|
8
|
+
* Observer-only: every write, read, and removal swallows its own errors so a
|
|
9
|
+
* marker can never change hook behavior.
|
|
10
|
+
*/
|
|
11
|
+
import { mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
/** Markers older than this are pruned when a new one is written. */
|
|
14
|
+
const MARKER_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
15
|
+
/** A run is unfinished once its marker outlives the timeout by this much. */
|
|
16
|
+
const UNFINISHED_GRACE_MS = 30_000;
|
|
17
|
+
export function hookRunMarkerDir(coordRoot) {
|
|
18
|
+
return join(coordRoot, ".harnery", "active", "hook-runs");
|
|
19
|
+
}
|
|
20
|
+
/** Write this run's marker; returns its path, or null when it could not be written. */
|
|
21
|
+
export function writeHookRunMarker(coordRoot, marker, now = new Date()) {
|
|
22
|
+
try {
|
|
23
|
+
const dir = hookRunMarkerDir(coordRoot);
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
pruneHookRunMarkers(dir, now.getTime());
|
|
26
|
+
const path = join(dir, `${marker.pid}-${safeName(marker.event)}.json`);
|
|
27
|
+
const body = { ...marker, started_at: now.toISOString() };
|
|
28
|
+
writeFileSync(path, `${JSON.stringify(body)}\n`);
|
|
29
|
+
return path;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function clearHookRunMarker(path) {
|
|
36
|
+
if (!path)
|
|
37
|
+
return;
|
|
38
|
+
try {
|
|
39
|
+
rmSync(path, { force: true });
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Observer-only.
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Markers whose run outlived its timeout plus a grace period and whose
|
|
47
|
+
* process is gone, within the window. A live process past its timeout is
|
|
48
|
+
* still running and is not counted.
|
|
49
|
+
*/
|
|
50
|
+
export function findUnfinishedHookRuns(coordRoot, nowMs = Date.now(), windowMs = 24 * 60 * 60 * 1000, isAlive = processAlive) {
|
|
51
|
+
const counts = new Map();
|
|
52
|
+
let latest = null;
|
|
53
|
+
let count = 0;
|
|
54
|
+
let names = [];
|
|
55
|
+
try {
|
|
56
|
+
names = readdirSync(hookRunMarkerDir(coordRoot)).filter((n) => n.endsWith(".json"));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return { count: 0, events: [], latest: null };
|
|
60
|
+
}
|
|
61
|
+
for (const name of names) {
|
|
62
|
+
let marker;
|
|
63
|
+
try {
|
|
64
|
+
marker = JSON.parse(readFileSync(join(hookRunMarkerDir(coordRoot), name), "utf8"));
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const started = Date.parse(marker.started_at);
|
|
70
|
+
if (!Number.isFinite(started) || nowMs - started > windowMs)
|
|
71
|
+
continue;
|
|
72
|
+
if (nowMs - started < marker.timeout_sec * 1000 + UNFINISHED_GRACE_MS)
|
|
73
|
+
continue;
|
|
74
|
+
if (isAlive(marker.pid))
|
|
75
|
+
continue;
|
|
76
|
+
count++;
|
|
77
|
+
counts.set(marker.event, (counts.get(marker.event) ?? 0) + 1);
|
|
78
|
+
if (!latest || marker.started_at > latest)
|
|
79
|
+
latest = marker.started_at;
|
|
80
|
+
}
|
|
81
|
+
const events = [...counts.entries()]
|
|
82
|
+
.map(([event, n]) => ({ event, count: n }))
|
|
83
|
+
.sort((a, b) => b.count - a.count || a.event.localeCompare(b.event));
|
|
84
|
+
return { count, events, latest };
|
|
85
|
+
}
|
|
86
|
+
function pruneHookRunMarkers(dir, nowMs) {
|
|
87
|
+
for (const name of readdirSync(dir)) {
|
|
88
|
+
const path = join(dir, name);
|
|
89
|
+
try {
|
|
90
|
+
if (nowMs - statSync(path).mtimeMs > MARKER_RETENTION_MS)
|
|
91
|
+
rmSync(path, { force: true });
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Observer-only.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function processAlive(pid) {
|
|
99
|
+
try {
|
|
100
|
+
process.kill(pid, 0);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
return err.code === "EPERM";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function safeName(value) {
|
|
108
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, "-").slice(0, 64) || "unknown";
|
|
109
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local server registry.
|
|
3
|
+
*
|
|
4
|
+
* Every command that starts a long-lived local server (a preview UI, a tuning
|
|
5
|
+
* panel, a tunnel gate, a dashboard) writes one record under
|
|
6
|
+
* `.harnery/servers/<id>.json` and removes it when the server stops. One
|
|
7
|
+
* reader can then list every running server, stop any of them, and clean up
|
|
8
|
+
* session servers whose starting agent is gone.
|
|
9
|
+
*
|
|
10
|
+
* Two kinds:
|
|
11
|
+
* - `service`: one long-running copy (a dashboard, a tunnel). Listed and
|
|
12
|
+
* stoppable by hand; never stopped automatically.
|
|
13
|
+
* - `session`: one copy per piece of work (a preview for one scene). Eligible
|
|
14
|
+
* for `gc` once its starting agent has ended or been unobserved for a long
|
|
15
|
+
* time AND nobody has used the server for the idle window.
|
|
16
|
+
*
|
|
17
|
+
* Fail-closed rules: a record whose pid was recycled is treated as dead, never
|
|
18
|
+
* signalled; an unreadable coordination view makes every owner `unknown`,
|
|
19
|
+
* which `gc` never stops; a record without an owner is never stopped
|
|
20
|
+
* automatically.
|
|
21
|
+
*/
|
|
22
|
+
import { type ScanSupport } from "./net.js";
|
|
23
|
+
export { ancestry, establishedConnectionCounts, listListeningPorts, listListeningSockets, lsofConnectionCounts, lsofListeningSockets, parseElapsed, parseLsofListen, parseProcNetTcp, processStartedAt, psProcessInfo, readProcessInfo, scanSupport, } from "./net.js";
|
|
24
|
+
export { tunnelServerId, tunnelServerInput } from "./tunnels.js";
|
|
25
|
+
export declare const SERVER_RECORD_SCHEMA_VERSION = 1;
|
|
26
|
+
export declare const SERVER_DEFAULT_IDLE_HOURS = 2;
|
|
27
|
+
export declare const SERVER_DEFAULT_OWNER_STALE_HOURS = 24;
|
|
28
|
+
export type ServerKind = "service" | "session";
|
|
29
|
+
export interface ServerRecord {
|
|
30
|
+
schema_version: typeof SERVER_RECORD_SCHEMA_VERSION;
|
|
31
|
+
id: string;
|
|
32
|
+
kind: ServerKind;
|
|
33
|
+
/** Free-form family name: `preview`, `tunnel`, `dashboard`, ... */
|
|
34
|
+
type: string;
|
|
35
|
+
label: string;
|
|
36
|
+
url?: string;
|
|
37
|
+
port?: number;
|
|
38
|
+
/** The process that owns the server's lifetime (signalled on stop). */
|
|
39
|
+
pid: number;
|
|
40
|
+
start_token?: string;
|
|
41
|
+
/** Further processes that belong to this server (helpers, children). */
|
|
42
|
+
pids?: number[];
|
|
43
|
+
host: string;
|
|
44
|
+
/** What the server serves, usually a directory. */
|
|
45
|
+
scope?: string;
|
|
46
|
+
cwd?: string;
|
|
47
|
+
log?: string;
|
|
48
|
+
/** Graceful stop command; signals are the fallback. */
|
|
49
|
+
stop_argv?: string[];
|
|
50
|
+
owner?: {
|
|
51
|
+
instance_id: string;
|
|
52
|
+
name?: string;
|
|
53
|
+
};
|
|
54
|
+
started_at: string;
|
|
55
|
+
registered_at: string;
|
|
56
|
+
last_active_at?: string;
|
|
57
|
+
}
|
|
58
|
+
export interface RegisterServerInput {
|
|
59
|
+
id?: string;
|
|
60
|
+
kind: ServerKind;
|
|
61
|
+
type: string;
|
|
62
|
+
label?: string;
|
|
63
|
+
url?: string;
|
|
64
|
+
port?: number;
|
|
65
|
+
pid?: number;
|
|
66
|
+
pids?: number[];
|
|
67
|
+
scope?: string;
|
|
68
|
+
cwd?: string;
|
|
69
|
+
log?: string;
|
|
70
|
+
stop_argv?: string[];
|
|
71
|
+
started_at?: string;
|
|
72
|
+
/** `null` records no owner; omitted resolves the calling agent session. */
|
|
73
|
+
owner?: {
|
|
74
|
+
instance_id: string;
|
|
75
|
+
name?: string;
|
|
76
|
+
} | null;
|
|
77
|
+
}
|
|
78
|
+
export interface ServerOptions {
|
|
79
|
+
coordRoot?: string | null;
|
|
80
|
+
}
|
|
81
|
+
export type ServerState = "running" | "dead" | "other-host";
|
|
82
|
+
export type OwnerState = "live" | "abandoned" | "ended" | "unknown" | "none";
|
|
83
|
+
export interface ServerView {
|
|
84
|
+
record: ServerRecord;
|
|
85
|
+
state: ServerState;
|
|
86
|
+
owner_state: OwnerState;
|
|
87
|
+
connections: number | null;
|
|
88
|
+
last_active_at: string;
|
|
89
|
+
idle_ms: number;
|
|
90
|
+
}
|
|
91
|
+
export interface UnregisteredListener {
|
|
92
|
+
pid: number;
|
|
93
|
+
port: number;
|
|
94
|
+
address: string;
|
|
95
|
+
command: string;
|
|
96
|
+
cwd: string | null;
|
|
97
|
+
}
|
|
98
|
+
export interface ServersReport {
|
|
99
|
+
schema_version: 1;
|
|
100
|
+
coord_root: string;
|
|
101
|
+
scan: ScanSupport;
|
|
102
|
+
servers: ServerView[];
|
|
103
|
+
unregistered: UnregisteredListener[];
|
|
104
|
+
pruned: string[];
|
|
105
|
+
}
|
|
106
|
+
export declare function serversDir(coordRoot: string): string;
|
|
107
|
+
/** A stable id for one server family serving one scope. */
|
|
108
|
+
export declare function serverId(type: string, scope?: string): string;
|
|
109
|
+
export declare function isValidServerId(id: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Record a running server. Returns null when no coordination root resolves
|
|
112
|
+
* (a tool running outside any Harnery project), so callers never fail
|
|
113
|
+
* because registration was impossible.
|
|
114
|
+
*/
|
|
115
|
+
export declare function registerServer(input: RegisterServerInput, options?: ServerOptions): ServerRecord | null;
|
|
116
|
+
/** Best-effort registration that never throws (for server start paths). */
|
|
117
|
+
export declare function tryRegisterServer(input: RegisterServerInput, options?: ServerOptions): ServerRecord | null;
|
|
118
|
+
/**
|
|
119
|
+
* Remove a server's record. With `pid`, only a record still naming that pid
|
|
120
|
+
* is removed, so a replacement that re-registered the same id survives.
|
|
121
|
+
*/
|
|
122
|
+
export declare function unregisterServer(id: string, options?: ServerOptions & {
|
|
123
|
+
pid?: number;
|
|
124
|
+
}): boolean;
|
|
125
|
+
export declare function readServer(id: string, options?: ServerOptions): ServerRecord | null;
|
|
126
|
+
/** Mark a server as used now. */
|
|
127
|
+
export declare function touchServer(id: string, options?: ServerOptions & {
|
|
128
|
+
now?: Date;
|
|
129
|
+
}): void;
|
|
130
|
+
/**
|
|
131
|
+
* A request hook for servers that want precise idle tracking: call the
|
|
132
|
+
* returned function on every request; it writes at most once a minute.
|
|
133
|
+
*/
|
|
134
|
+
export declare function createServerToucher(id: string, options?: ServerOptions): () => void;
|
|
135
|
+
export declare function readServerRecords(options?: ServerOptions): ServerRecord[];
|
|
136
|
+
export declare function pidAlive(pid: number): boolean;
|
|
137
|
+
export declare function serverState(record: ServerRecord): ServerState;
|
|
138
|
+
export interface ListServersOptions extends ServerOptions {
|
|
139
|
+
/** Look for listening processes inside the project that never registered. */
|
|
140
|
+
scan?: boolean;
|
|
141
|
+
/** Record observed connections as activity (default true). */
|
|
142
|
+
sample?: boolean;
|
|
143
|
+
/** Delete records of servers that are no longer running (default true). */
|
|
144
|
+
prune?: boolean;
|
|
145
|
+
ownerStaleHours?: number;
|
|
146
|
+
now?: Date;
|
|
147
|
+
}
|
|
148
|
+
/** Read every record, observe it, and optionally find unregistered listeners. */
|
|
149
|
+
export declare function listServers(options?: ListServersOptions): ServersReport;
|
|
150
|
+
/** Register running tunnels that have no current record. Returns adopted ids. */
|
|
151
|
+
export declare function adoptRunningTunnels(coordRoot: string): string[];
|
|
152
|
+
/**
|
|
153
|
+
* Listening processes that run inside the project (working directory or
|
|
154
|
+
* command line within the coordination root) but are not covered by any
|
|
155
|
+
* registered server: not a recorded pid, not a descendant of one, and not on
|
|
156
|
+
* a recorded port.
|
|
157
|
+
*/
|
|
158
|
+
export declare function findUnregisteredListeners(coordRoot: string, servers: Array<Pick<ServerView, "record" | "state">>): UnregisteredListener[];
|
|
159
|
+
export interface GcPolicy {
|
|
160
|
+
idleHours?: number;
|
|
161
|
+
ownerStaleHours?: number;
|
|
162
|
+
}
|
|
163
|
+
export interface GcDecision {
|
|
164
|
+
id: string;
|
|
165
|
+
action: "stop" | "keep";
|
|
166
|
+
reason: string;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Decide which session servers to stop. Pure: the caller supplies the views.
|
|
170
|
+
* A server is stopped only when all hold: kind `session`, running on this
|
|
171
|
+
* host, its owner ended or went unobserved past the stale window, and it has
|
|
172
|
+
* been idle for the idle window.
|
|
173
|
+
*/
|
|
174
|
+
export declare function planServerGc(views: ServerView[], policy?: GcPolicy): GcDecision[];
|
|
175
|
+
export interface StopResult {
|
|
176
|
+
id: string;
|
|
177
|
+
stopped: boolean;
|
|
178
|
+
method: "not-running" | "stop-command" | "signal" | "refused";
|
|
179
|
+
detail?: string;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Stop one server: its graceful stop command first when recorded, then
|
|
183
|
+
* SIGTERM to its process group, then SIGKILL after the grace period. A pid
|
|
184
|
+
* whose start token no longer matches is never signalled.
|
|
185
|
+
*/
|
|
186
|
+
export declare function stopServer(record: ServerRecord, options?: ServerOptions & {
|
|
187
|
+
graceMs?: number;
|
|
188
|
+
}): Promise<StopResult>;
|
|
189
|
+
/**
|
|
190
|
+
* The first free port in the configured range for `type`
|
|
191
|
+
* (`.harnery/config.jsonc` `servers.port_ranges`). Skips ports any process is
|
|
192
|
+
* listening on and ports a registered server claims. Returns null when the
|
|
193
|
+
* type has no configured range; throws when the range is exhausted.
|
|
194
|
+
*/
|
|
195
|
+
export declare function allocateServerPort(type: string, options?: ServerOptions): number | null;
|
|
196
|
+
export interface AdoptOptions extends ServerOptions {
|
|
197
|
+
type?: string;
|
|
198
|
+
kind?: ServerKind;
|
|
199
|
+
label?: string;
|
|
200
|
+
scope?: string;
|
|
201
|
+
/** `self` (default) records the adopting agent as owner; `none` records no owner. */
|
|
202
|
+
owner?: "self" | "none";
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Register a running server that never registered itself. Only a pid the scan
|
|
206
|
+
* reports as an unregistered listener inside the project can be adopted.
|
|
207
|
+
*/
|
|
208
|
+
export declare function adoptServer(pid: number, options?: AdoptOptions): ServerRecord;
|
|
209
|
+
/** Ensure the registry directory exists (for hosts that write records directly). */
|
|
210
|
+
export declare function ensureServersDir(coordRoot: string): string;
|
|
211
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/servers/index.ts"],"names":[],"mappings":"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"}
|