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
package/src/commands/browse.ts
CHANGED
|
@@ -134,6 +134,7 @@ import {
|
|
|
134
134
|
import {
|
|
135
135
|
harvestDomain,
|
|
136
136
|
harvestWindowsChromeCookies,
|
|
137
|
+
keepWindowsChromeSession,
|
|
137
138
|
stopWindowsChromeProfile,
|
|
138
139
|
windowsChromePath,
|
|
139
140
|
windowsLocalAppData,
|
|
@@ -3465,15 +3466,18 @@ function useWindowsPlainLogin(): boolean {
|
|
|
3465
3466
|
|
|
3466
3467
|
/**
|
|
3467
3468
|
* The WSL form of `--login`: the person signs in through the Windows
|
|
3468
|
-
* Chrome on a dedicated Windows profile with nothing attached.
|
|
3469
|
-
*
|
|
3470
|
-
*
|
|
3469
|
+
* Chrome on a dedicated Windows profile with nothing attached. Before the
|
|
3470
|
+
* window opens, the profile is set to continue where it left off, so Chrome
|
|
3471
|
+
* keeps browser-session cookies when the window closes. When the window
|
|
3472
|
+
* closes, the profile's cookies for the sign-in site are read and merged
|
|
3473
|
+
* into the shared cookie store, which later `browse` runs attach, and the
|
|
3474
|
+
* last page open on that site is reported so an agent can resume from it.
|
|
3471
3475
|
*/
|
|
3472
3476
|
async function runWindowsPlainLogin(
|
|
3473
3477
|
url: string,
|
|
3474
3478
|
storePath: string,
|
|
3475
3479
|
loginCloseFile: string | null,
|
|
3476
|
-
emit:
|
|
3480
|
+
emit: Pick<EmitContext, "log" | "data">,
|
|
3477
3481
|
): Promise<void> {
|
|
3478
3482
|
const chromeLinux = windowsChromePath();
|
|
3479
3483
|
const localAppData = windowsLocalAppData();
|
|
@@ -3484,6 +3488,13 @@ async function runWindowsPlainLogin(
|
|
|
3484
3488
|
}
|
|
3485
3489
|
const chromeWindows = wslPathToWindows(chromeLinux);
|
|
3486
3490
|
const profileWindows = windowsPlainProfile(localAppData);
|
|
3491
|
+
const keep = keepWindowsChromeSession({ chromeWindows, profileWindows });
|
|
3492
|
+
if (!keep.kept) {
|
|
3493
|
+
emit.log(
|
|
3494
|
+
"[--login] Could not set the sign-in profile to keep browser-session cookies. A sign-in that relies on them survives only if you end it from the terminal instead of closing the window.",
|
|
3495
|
+
"warn",
|
|
3496
|
+
);
|
|
3497
|
+
}
|
|
3487
3498
|
const child = spawn(
|
|
3488
3499
|
chromeLinux,
|
|
3489
3500
|
[`--user-data-dir=${profileWindows}`, "--no-first-run", "--no-default-browser-check", url],
|
|
@@ -3507,24 +3518,49 @@ async function runWindowsPlainLogin(
|
|
|
3507
3518
|
);
|
|
3508
3519
|
await Promise.race(waits);
|
|
3509
3520
|
lifecycleAbort.abort();
|
|
3510
|
-
stopWindowsChromeProfile(profileWindows);
|
|
3521
|
+
const stop = stopWindowsChromeProfile(profileWindows);
|
|
3522
|
+
if (stop.forced > 0) {
|
|
3523
|
+
emit.log(
|
|
3524
|
+
"[--login] Chrome did not close when asked and was stopped. Cookies set in its last 30 seconds may be missing; run the sign-in again if later pages are signed out.",
|
|
3525
|
+
"warn",
|
|
3526
|
+
);
|
|
3527
|
+
}
|
|
3511
3528
|
await Promise.race([exited, new Promise((r) => setTimeout(r, 5000))]);
|
|
3512
3529
|
const domain = harvestDomain(url);
|
|
3513
|
-
const cookies = harvestWindowsChromeCookies({ chromeWindows, profileWindows, domain });
|
|
3530
|
+
const { cookies, pages } = harvestWindowsChromeCookies({ chromeWindows, profileWindows, domain });
|
|
3531
|
+
const lastPage = pages[0] ?? null;
|
|
3532
|
+
const names = [...new Set(cookies.map((cookie) => cookie.name))].sort();
|
|
3533
|
+
const sessionCookies = cookies.filter((cookie) => cookie.expires <= 0).length;
|
|
3514
3534
|
if (cookies.length === 0) {
|
|
3515
3535
|
emit.log(
|
|
3516
3536
|
`[--login] No ${domain} cookies were found in the Windows profile; the sign-in may not have completed.`,
|
|
3517
3537
|
"warn",
|
|
3518
3538
|
);
|
|
3519
|
-
|
|
3539
|
+
} else {
|
|
3540
|
+
const jar = new CookieJar({ path: storePath, source: "harn-browse" });
|
|
3541
|
+
jar.save(mergeCookies(jar.load(), cookies));
|
|
3542
|
+
emit.log(
|
|
3543
|
+
`[--login] Copied ${cookies.length} ${domain} cookies (${sessionCookies} browser-session) into ${storePath}: ${names.join(", ")}.`,
|
|
3544
|
+
"info",
|
|
3545
|
+
);
|
|
3520
3546
|
}
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3547
|
+
if (lastPage) {
|
|
3548
|
+
emit.log(
|
|
3549
|
+
`[--login] Last page: ${lastPage} (the last page you had open on ${domain}; open it to continue where the sign-in left off, since its address may carry a sign-in token that cookies alone do not replace).`,
|
|
3550
|
+
"info",
|
|
3551
|
+
);
|
|
3552
|
+
}
|
|
3553
|
+
emit.data({
|
|
3554
|
+
login: {
|
|
3555
|
+
domain,
|
|
3556
|
+
store: storePath,
|
|
3557
|
+
cookies: cookies.length,
|
|
3558
|
+
sessionCookies,
|
|
3559
|
+
names,
|
|
3560
|
+
lastPage,
|
|
3561
|
+
pages,
|
|
3562
|
+
},
|
|
3563
|
+
});
|
|
3528
3564
|
}
|
|
3529
3565
|
|
|
3530
3566
|
async function waitForLoginCloseFile(path: string, signal?: AbortSignal): Promise<void> {
|
|
@@ -50,6 +50,7 @@ export function registerDiagnosticsCommand(
|
|
|
50
50
|
findings,
|
|
51
51
|
bundles,
|
|
52
52
|
capability,
|
|
53
|
+
supervisor: readSupervisorStatus(repoRoot),
|
|
53
54
|
total_findings: findings.length,
|
|
54
55
|
total_bundles: bundles.length,
|
|
55
56
|
});
|
|
@@ -68,6 +69,7 @@ export function registerDiagnosticsCommand(
|
|
|
68
69
|
schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
|
|
69
70
|
kind: "diagnostic_finding",
|
|
70
71
|
finding,
|
|
72
|
+
supervisor: readSupervisorStatus(repoRoot),
|
|
71
73
|
timeline:
|
|
72
74
|
readSupervisorTimeline(repoRoot, finding.id) ?? buildSupervisorTimeline(finding),
|
|
73
75
|
explanation:
|
|
@@ -118,6 +120,7 @@ export function registerDiagnosticsCommand(
|
|
|
118
120
|
schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
|
|
119
121
|
kind: "diagnostic_advice",
|
|
120
122
|
source: { mode: "live" },
|
|
123
|
+
supervisor: readSupervisorStatus(repoRoot),
|
|
121
124
|
advice: buildDiagnosticAdvice({
|
|
122
125
|
assessment: published.assessment,
|
|
123
126
|
priorHysteresis: published.prior_hysteresis,
|
|
@@ -204,6 +207,13 @@ function liveFindingsCapability(repoRoot: string, reportPresent: boolean): Super
|
|
|
204
207
|
}
|
|
205
208
|
const status = readSupervisorStatus(repoRoot);
|
|
206
209
|
if (status.running) return { source_kind: "supervisor.findings", state: "supported" };
|
|
210
|
+
if (status.liveness === "unknown")
|
|
211
|
+
return {
|
|
212
|
+
source_kind: "supervisor.findings",
|
|
213
|
+
state: "error",
|
|
214
|
+
reason_code: "supervisor_generation_unknown",
|
|
215
|
+
detail: status.build_status,
|
|
216
|
+
};
|
|
207
217
|
return {
|
|
208
218
|
source_kind: "supervisor.findings",
|
|
209
219
|
state: "expired",
|
package/src/commands/doctor.ts
CHANGED
|
@@ -421,15 +421,34 @@ function checkWorkflowAdapter(adapter: AdapterName): { check: Check; installed:
|
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
function checkNode(): Check {
|
|
424
|
-
|
|
424
|
+
return nodeCheck(whichVersion("node"), process.versions);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Report the `node` on PATH, which is what Node-based tools run. Under Bun,
|
|
429
|
+
* `process.versions.node` is the Node version Bun emulates, not an installed
|
|
430
|
+
* Node, so it is used only when doctor itself runs on Node and PATH has none.
|
|
431
|
+
*/
|
|
432
|
+
export function nodeCheck(
|
|
433
|
+
onPath: { ok: boolean; out: string },
|
|
434
|
+
runtime: { node: string; bun?: string },
|
|
435
|
+
): Check {
|
|
436
|
+
const hint = "https://nodejs.org/en/download";
|
|
437
|
+
let v = onPath.ok ? onPath.out.replace(/^v/, "") : "";
|
|
438
|
+
if (!v) {
|
|
439
|
+
if (runtime.bun) {
|
|
440
|
+
return {
|
|
441
|
+
name: "node",
|
|
442
|
+
severity: "warn",
|
|
443
|
+
detail: "not on PATH (Bun runs harnery; Node-based tools need it)",
|
|
444
|
+
hint,
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
v = runtime.node;
|
|
448
|
+
}
|
|
425
449
|
const major = Number.parseInt(v.split(".")[0], 10);
|
|
426
450
|
if (Number.isNaN(major) || major < 20) {
|
|
427
|
-
return {
|
|
428
|
-
name: "node",
|
|
429
|
-
severity: "fail",
|
|
430
|
-
detail: `${v} (need ≥ 20)`,
|
|
431
|
-
hint: "https://nodejs.org/en/download",
|
|
432
|
-
};
|
|
451
|
+
return { name: "node", severity: "fail", detail: `${v} (need ≥ 20)`, hint };
|
|
433
452
|
}
|
|
434
453
|
return { name: "node", severity: "ok", detail: v };
|
|
435
454
|
}
|
package/src/commands/files.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { Command } from "commander";
|
|
|
4
4
|
import type { EmitContext, HarneryProgramContext } from "../commander.ts";
|
|
5
5
|
import { resolveCoordRoot } from "../core/agents/coord-client.ts";
|
|
6
6
|
import { DEFAULT_WEB_PORT, resolveWebPort } from "../core/config.ts";
|
|
7
|
+
import { createTunnelReadinessVerifier } from "../core/servers/index.ts";
|
|
7
8
|
import { coordRootId, localCoordRootIdUrl } from "../lib/coord-root-id.ts";
|
|
8
9
|
import {
|
|
9
10
|
encodedRepoPath,
|
|
@@ -14,10 +15,10 @@ import {
|
|
|
14
15
|
} from "../lib/local-file-url.ts";
|
|
15
16
|
import { isPathAllowed } from "../lib/tunnel/path-scope.ts";
|
|
16
17
|
import {
|
|
17
|
-
isTunnelStateLive,
|
|
18
18
|
listStates,
|
|
19
|
-
type
|
|
19
|
+
type TunnelReadinessVerifier,
|
|
20
20
|
type TunnelState,
|
|
21
|
+
tunnelReadiness,
|
|
21
22
|
tunnelTargetPort,
|
|
22
23
|
} from "../lib/tunnel/state.ts";
|
|
23
24
|
|
|
@@ -208,10 +209,10 @@ export function tunnelFileUrl(
|
|
|
208
209
|
local: LocalFileUrl,
|
|
209
210
|
port: number,
|
|
210
211
|
states: TunnelState[],
|
|
211
|
-
|
|
212
|
+
verifier: TunnelReadinessVerifier,
|
|
212
213
|
): string {
|
|
213
214
|
const live = states.filter(
|
|
214
|
-
(state) => tunnelTargetPort(state) === port &&
|
|
215
|
+
(state) => tunnelTargetPort(state) === port && tunnelReadiness(state, verifier) === "ready",
|
|
215
216
|
);
|
|
216
217
|
if (local.mode === "raw") {
|
|
217
218
|
const path = `/${encodedRepoPath(local.relPath)}`;
|
|
@@ -309,7 +310,14 @@ export function registerFilesCommand(
|
|
|
309
310
|
mode: opts.raw ? "raw" : opts.viewer ? "viewer" : "auto",
|
|
310
311
|
});
|
|
311
312
|
if (opts.verify !== false) await verifyDashboardRoot(coordRoot, port);
|
|
312
|
-
const url = opts.tunnel
|
|
313
|
+
const url = opts.tunnel
|
|
314
|
+
? tunnelFileUrl(
|
|
315
|
+
result,
|
|
316
|
+
port,
|
|
317
|
+
listStates(coordRoot),
|
|
318
|
+
createTunnelReadinessVerifier({ coordRoot }),
|
|
319
|
+
)
|
|
320
|
+
: result.url;
|
|
313
321
|
emit.text(`${url}\n`);
|
|
314
322
|
} catch (error) {
|
|
315
323
|
if (error instanceof TunnelLinkError) {
|
package/src/commands/governor.ts
CHANGED
|
@@ -385,12 +385,13 @@ function registerServiceCommand(
|
|
|
385
385
|
.action(async (opts: { json?: boolean }) => {
|
|
386
386
|
await withGovernorRootAsync(emit, async (coordRoot) => {
|
|
387
387
|
let status = requestGovernorServiceStop(coordRoot);
|
|
388
|
+
const signal = { signal_sent: status.signal_sent, signal_verdict: status.signal_verdict };
|
|
388
389
|
const deadline = Date.now() + 5_000;
|
|
389
390
|
while (status.running && Date.now() < deadline) {
|
|
390
391
|
await delay(50);
|
|
391
392
|
status = readGovernorServiceStatus(coordRoot);
|
|
392
393
|
}
|
|
393
|
-
emitServiceStatus(status, opts.json, emit);
|
|
394
|
+
emitServiceStatus({ ...status, ...signal }, opts.json, emit);
|
|
394
395
|
});
|
|
395
396
|
});
|
|
396
397
|
|
|
@@ -534,19 +535,31 @@ function emitServiceStatus(
|
|
|
534
535
|
emit.data(status);
|
|
535
536
|
return;
|
|
536
537
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
538
|
+
const state =
|
|
539
|
+
status.liveness === "unknown"
|
|
540
|
+
? "unknown"
|
|
541
|
+
: !status.config && !status.record
|
|
542
|
+
? "unconfigured"
|
|
543
|
+
: status.running
|
|
544
|
+
? (status.record?.state ?? "running")
|
|
545
|
+
: status.stale
|
|
546
|
+
? "stale"
|
|
547
|
+
: "stopped";
|
|
546
548
|
const lines = [
|
|
547
549
|
`governor service: ${state}`,
|
|
550
|
+
`generation: ${status.generation}`,
|
|
551
|
+
`liveness: ${status.liveness}`,
|
|
552
|
+
`running build: ${status.running_build ? `${status.running_build.version} (${status.running_build.revision})` : "unknown"}`,
|
|
553
|
+
`installed build: ${status.installed_build.version} (${status.installed_build.revision})`,
|
|
554
|
+
`build: ${status.build_status}`,
|
|
548
555
|
`goals: ${status.config?.goal_ids.join(", ") || "none"}`,
|
|
549
556
|
];
|
|
557
|
+
if (status.signal_sent !== undefined) {
|
|
558
|
+
lines.push(`signal sent: ${status.signal_sent ? "yes" : "no"} (${status.signal_verdict})`);
|
|
559
|
+
if (!status.signal_sent && status.record?.state !== "stopped") {
|
|
560
|
+
lines.push("The durable stop request remains for the service to read.");
|
|
561
|
+
}
|
|
562
|
+
}
|
|
550
563
|
if (status.record) {
|
|
551
564
|
lines.push(`pid: ${status.record.pid} on ${status.record.host}`);
|
|
552
565
|
lines.push(`heartbeat: ${status.record.heartbeat_at}`);
|
package/src/commands/inbox.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
import type { EmitContext } from "../commander.ts";
|
|
3
3
|
import type { HarneryInboxService } from "../core/inbox/service.ts";
|
|
4
|
+
import { watchInbox } from "../core/inbox/watcher.ts";
|
|
4
5
|
|
|
5
6
|
export function registerInboxCommand(
|
|
6
7
|
program: Command,
|
|
7
8
|
emit: EmitContext,
|
|
8
9
|
service?: HarneryInboxService,
|
|
10
|
+
defaultRecipient?: () => string,
|
|
9
11
|
): void {
|
|
10
12
|
const inbox = program.command("inbox").description("Inspect private coordination inbox state");
|
|
11
13
|
inbox
|
|
12
|
-
.command("list
|
|
14
|
+
.command("list [recipient]")
|
|
13
15
|
.option("--json", "Emit stable JSON")
|
|
14
|
-
.action((recipient: string, options: { json?: boolean }) => {
|
|
16
|
+
.action((recipient: string | undefined, options: { json?: boolean }) => {
|
|
15
17
|
run(emit, () => {
|
|
18
|
+
recipient = requireRecipient(recipient, defaultRecipient);
|
|
16
19
|
const rows = requireInboxService(service)
|
|
17
20
|
.pending(recipient)
|
|
18
21
|
.map((record) => ({
|
|
@@ -22,6 +25,7 @@ export function registerInboxCommand(
|
|
|
22
25
|
created_at: record.created_at,
|
|
23
26
|
body_bytes: record.body_bytes,
|
|
24
27
|
body: record.body,
|
|
28
|
+
state: "pending",
|
|
25
29
|
}));
|
|
26
30
|
if (options.json) {
|
|
27
31
|
emit.config({ format: "json" });
|
|
@@ -41,6 +45,52 @@ export function registerInboxCommand(
|
|
|
41
45
|
} else emit.rows([status as unknown as Record<string, unknown>]);
|
|
42
46
|
});
|
|
43
47
|
});
|
|
48
|
+
inbox
|
|
49
|
+
.command("watch [recipient]")
|
|
50
|
+
.description("Surface pending messages for this instance; poll history until timeout")
|
|
51
|
+
.option("--timeout <seconds>", "Stop after this many seconds", "60")
|
|
52
|
+
.option("--json", "Emit one JSON record per surfaced message")
|
|
53
|
+
.action(async (recipient: string | undefined, options: { json?: boolean; timeout: string }) => {
|
|
54
|
+
let controller: AbortController | undefined;
|
|
55
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
56
|
+
try {
|
|
57
|
+
const instanceId = requireRecipient(recipient, defaultRecipient);
|
|
58
|
+
const inboxService = requireInboxService(service);
|
|
59
|
+
const seconds = Number(options.timeout);
|
|
60
|
+
if (!Number.isFinite(seconds) || seconds <= 0 || seconds > 3600)
|
|
61
|
+
throw new Error("--timeout must be greater than 0 and at most 3600 seconds");
|
|
62
|
+
controller = new AbortController();
|
|
63
|
+
timer = setTimeout(() => controller!.abort(), seconds * 1000);
|
|
64
|
+
if (options.json) emit.config({ format: "json" });
|
|
65
|
+
for await (const message of watchInbox(inboxService, instanceId, {
|
|
66
|
+
signal: controller.signal,
|
|
67
|
+
})) {
|
|
68
|
+
const row = { ...message, state: "surfaced" };
|
|
69
|
+
if (options.json) emit.data(row);
|
|
70
|
+
else emit.rows([row]);
|
|
71
|
+
// An output error leaves this ID pending for prompt recovery.
|
|
72
|
+
await new Promise<void>((resolve, reject) =>
|
|
73
|
+
process.stdout.write("", (error) => (error ? reject(error) : resolve())),
|
|
74
|
+
);
|
|
75
|
+
inboxService.acknowledgeSurface(instanceId, [message.message_id]);
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
emit.error({
|
|
79
|
+
code: "inbox_failed",
|
|
80
|
+
message: error instanceof Error ? error.message : String(error),
|
|
81
|
+
});
|
|
82
|
+
emit.setExitCode(1);
|
|
83
|
+
} finally {
|
|
84
|
+
if (timer) clearTimeout(timer);
|
|
85
|
+
controller?.abort();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function requireRecipient(recipient: string | undefined, fallback?: () => string): string {
|
|
91
|
+
const resolved = recipient ?? fallback?.();
|
|
92
|
+
if (!resolved) throw new Error("recipient instance is required outside an agent session");
|
|
93
|
+
return resolved;
|
|
44
94
|
}
|
|
45
95
|
|
|
46
96
|
function requireInboxService(service?: HarneryInboxService): HarneryInboxService {
|
package/src/commands/rm.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
2
|
+
import { existsSync, lstatSync, readFileSync, realpathSync } from "node:fs";
|
|
3
|
+
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
4
4
|
import type { Command } from "commander";
|
|
5
5
|
import type { EmitContext, HarneryProgramContext } from "../commander.ts";
|
|
6
6
|
import { resolveCoordRoot } from "../core/agents/coord-client.ts";
|
|
7
|
+
import { artifactsRoot, parseArtifactManifest } from "../core/artifacts/index.ts";
|
|
7
8
|
import {
|
|
8
9
|
readCoordinationViewV3,
|
|
9
10
|
requireAuthoritySafeCoordinationViewV3,
|
|
@@ -25,6 +26,15 @@ export function registerRmCommand(
|
|
|
25
26
|
"Allowed directory; each target must be strictly inside it",
|
|
26
27
|
)
|
|
27
28
|
.option("-r, --recursive", "Permit directory removal after inspecting its complete contents")
|
|
29
|
+
.option(
|
|
30
|
+
"--clean-clone",
|
|
31
|
+
"Remove only clean standalone clones inside one owned artifact; refuse local-only commits and worktrees",
|
|
32
|
+
)
|
|
33
|
+
.option(
|
|
34
|
+
"--entry-limit <n>",
|
|
35
|
+
"Maximum inspected entries, 1-100000 (default 10000); larger limits require one current caller-owned managed artifact as --root",
|
|
36
|
+
(value: string) => Number(value),
|
|
37
|
+
)
|
|
28
38
|
.option(
|
|
29
39
|
"--allow-protected <path>",
|
|
30
40
|
"Remove this one protected path (Git metadata, credentials, Harnery state, keys) inside a target; repeat per path",
|
|
@@ -65,7 +75,34 @@ export function registerRmCommand(
|
|
|
65
75
|
const liveRoots = [coordRoot, gitTopLevel(process.cwd())].filter((dir): dir is string =>
|
|
66
76
|
Boolean(dir),
|
|
67
77
|
);
|
|
68
|
-
const
|
|
78
|
+
const assertOwnedArtifact = (root: string): void => {
|
|
79
|
+
if (!coordRoot || dirname(root) !== realpathSync(artifactsRoot(coordRoot)))
|
|
80
|
+
throw new Error("Copied state requires --root to name one owned managed artifact");
|
|
81
|
+
const view = requireAuthoritySafeCoordinationViewV3(readCoordinationViewV3(coordRoot));
|
|
82
|
+
const owner = resolveOwner({ payload: null, coordRoot });
|
|
83
|
+
const ownId = owner ? liveInstanceIdV3(owner.instance_id) : undefined;
|
|
84
|
+
if (!ownId || !view.instances[ownId]?.authority_eligible)
|
|
85
|
+
throw new Error("Copied state requires an authenticated current artifact owner");
|
|
86
|
+
const record = resolve(root, ".harnery-artifact.json");
|
|
87
|
+
const stat = lstatSync(record);
|
|
88
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
89
|
+
throw new Error("An owned artifact requires a regular manifest file");
|
|
90
|
+
const parsed = parseArtifactManifest(JSON.parse(readFileSync(record, "utf8")));
|
|
91
|
+
if (
|
|
92
|
+
!parsed.ok ||
|
|
93
|
+
parsed.manifest.released_at ||
|
|
94
|
+
!parsed.manifest.created_by?.instance_id ||
|
|
95
|
+
liveInstanceIdV3(parsed.manifest.created_by.instance_id) !== ownId
|
|
96
|
+
)
|
|
97
|
+
throw new Error(
|
|
98
|
+
"Copied state requires a valid unreleased artifact owned by the caller",
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
const report = guardedRemove(
|
|
102
|
+
paths,
|
|
103
|
+
{ ...options, liveRoots, assertOwnedArtifact },
|
|
104
|
+
checkClaims,
|
|
105
|
+
);
|
|
69
106
|
if (options.json) {
|
|
70
107
|
emit.config({ format: "json" });
|
|
71
108
|
emit.data(report);
|
|
@@ -76,7 +113,9 @@ export function registerRmCommand(
|
|
|
76
113
|
];
|
|
77
114
|
if (report.protected.length)
|
|
78
115
|
lines.push(
|
|
79
|
-
|
|
116
|
+
options.cleanClone
|
|
117
|
+
? "Verified clone metadata:"
|
|
118
|
+
: "Protected paths named with --allow-protected:",
|
|
80
119
|
...report.protected.map((path) => ` ${path}`),
|
|
81
120
|
);
|
|
82
121
|
if (report.links.length)
|
package/src/commands/servers.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
-
import { hostname } from "node:os";
|
|
3
2
|
import type { Command } from "commander";
|
|
4
3
|
import type { EmitContext, HarneryProgramContext } from "../commander.ts";
|
|
5
4
|
import { resolveCoordRoot } from "../core/agents/coord-client.ts";
|
|
@@ -111,22 +110,13 @@ export function registerServersCommand(
|
|
|
111
110
|
const preview = Boolean(options.type) && !options.yes;
|
|
112
111
|
const results: Array<StopResult | { id: string; would_stop: true }> = [];
|
|
113
112
|
for (const stray of strays) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
label: stray.command,
|
|
122
|
-
pid: stray.pid,
|
|
123
|
-
host: hostname(),
|
|
124
|
-
started_at: new Date().toISOString(),
|
|
125
|
-
registered_at: new Date().toISOString(),
|
|
126
|
-
},
|
|
127
|
-
{ coordRoot },
|
|
128
|
-
),
|
|
129
|
-
);
|
|
113
|
+
const receipt = adoptServer(stray.pid, {
|
|
114
|
+
coordRoot,
|
|
115
|
+
type: "unregistered",
|
|
116
|
+
owner: "none",
|
|
117
|
+
dependencies: { listServers: () => report },
|
|
118
|
+
});
|
|
119
|
+
results.push(await stopServer(receipt, { coordRoot }));
|
|
130
120
|
}
|
|
131
121
|
for (const view of selected) {
|
|
132
122
|
if (preview && !ids.includes(view.record.id))
|
|
@@ -207,10 +197,20 @@ export function registerServersCommand(
|
|
|
207
197
|
emit.config({ format: "json" });
|
|
208
198
|
emit.data({ applied: Boolean(options.yes), pruned: report.pruned, decisions, results });
|
|
209
199
|
} else {
|
|
210
|
-
const lines = decisions.map(
|
|
211
|
-
(
|
|
212
|
-
|
|
213
|
-
|
|
200
|
+
const lines = decisions.map((decision) => {
|
|
201
|
+
const result = results.find((item) => item.id === decision.id);
|
|
202
|
+
const action =
|
|
203
|
+
decision.action === "keep"
|
|
204
|
+
? "keep"
|
|
205
|
+
: !options.yes
|
|
206
|
+
? "would stop"
|
|
207
|
+
: result?.stopped
|
|
208
|
+
? "stopped"
|
|
209
|
+
: result?.method === "not-running"
|
|
210
|
+
? "already stopped"
|
|
211
|
+
: "could not stop";
|
|
212
|
+
return `${action} ${decision.id} ${result?.detail ?? decision.reason}`;
|
|
213
|
+
});
|
|
214
214
|
if (report.pruned.length)
|
|
215
215
|
lines.push(`Removed records of exited servers: ${report.pruned.join(", ")}`);
|
|
216
216
|
if (!decisions.length) lines.push("No registered servers.");
|
|
@@ -218,6 +218,7 @@ export function registerServersCommand(
|
|
|
218
218
|
lines.push("Preview only. Re-run with --yes to stop.");
|
|
219
219
|
emit.text(lines.join("\n"));
|
|
220
220
|
}
|
|
221
|
+
if (results.some((result) => result.method === "refused")) emit.setExitCode(1);
|
|
221
222
|
} catch (error) {
|
|
222
223
|
fail(error);
|
|
223
224
|
}
|
|
@@ -396,14 +397,14 @@ export function registerServersCommand(
|
|
|
396
397
|
|
|
397
398
|
cmd
|
|
398
399
|
.command("unregister <id>")
|
|
399
|
-
.description("Remove
|
|
400
|
+
.description("Remove the exact observed server registration (optionally require --pid)")
|
|
400
401
|
.option("--pid <pid>", "Only remove when the record names this pid")
|
|
401
402
|
.action((id: string, options: { pid?: string }) => {
|
|
402
403
|
try {
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
});
|
|
404
|
+
const coordRoot = root();
|
|
405
|
+
const record = readServer(id, { coordRoot });
|
|
406
|
+
const matchesPid = !options.pid || record?.pid === integer(options.pid, "--pid");
|
|
407
|
+
const removed = !!record && matchesPid && unregisterServer(record, { coordRoot });
|
|
407
408
|
emit.config({ format: "json" });
|
|
408
409
|
emit.data({ id, removed });
|
|
409
410
|
} catch (error) {
|
|
@@ -83,11 +83,16 @@ export function registerSupervisorCommand(
|
|
|
83
83
|
.action(async (options: { root?: string }) => {
|
|
84
84
|
try {
|
|
85
85
|
const root = resolve(options.root ?? coordRoot(context));
|
|
86
|
-
|
|
86
|
+
const requested = requestSupervisorStop(root);
|
|
87
|
+
let status = requested;
|
|
87
88
|
const deadline = Date.now() + 5_000;
|
|
88
89
|
while (status.running && Date.now() < deadline) {
|
|
89
90
|
await new Promise((done) => setTimeout(done, 50));
|
|
90
|
-
status =
|
|
91
|
+
status = {
|
|
92
|
+
...readSupervisorStatus(root),
|
|
93
|
+
signal_sent: requested.signal_sent,
|
|
94
|
+
signal_verdict: requested.signal_verdict,
|
|
95
|
+
};
|
|
91
96
|
}
|
|
92
97
|
emit.data(status);
|
|
93
98
|
} catch (error) {
|
package/src/commands/toc.ts
CHANGED
|
@@ -172,17 +172,25 @@ function renderToc(r: TocResult): string {
|
|
|
172
172
|
return lines.join("\n");
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
async function runSection(
|
|
175
|
+
export async function runSection(
|
|
176
|
+
file: string,
|
|
177
|
+
query: string,
|
|
178
|
+
opts: SectionOpts,
|
|
179
|
+
): Promise<SectionResult> {
|
|
176
180
|
if (!query.trim()) throw new Error("header query required");
|
|
177
181
|
const tocResult = await runToc(file, {});
|
|
178
182
|
const absPath = resolve(file);
|
|
179
183
|
const lines = readFileSync(absPath, "utf8").split("\n");
|
|
180
184
|
|
|
181
|
-
const
|
|
182
|
-
opts.caseSensitive
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
185
|
+
const same = (a: string, b: string) =>
|
|
186
|
+
opts.caseSensitive ? a === b : a.toLowerCase() === b.toLowerCase();
|
|
187
|
+
const contains = (a: string, b: string) =>
|
|
188
|
+
opts.caseSensitive ? a.includes(b) : a.toLowerCase().includes(b.toLowerCase());
|
|
189
|
+
// A header that equals the query wins over headers that merely contain it,
|
|
190
|
+
// so "Media" can still select "## Media" beside "## Media recipes".
|
|
191
|
+
const exact = tocResult.headers.filter((h) => same(h.text, query.trim()));
|
|
192
|
+
const matches =
|
|
193
|
+
exact.length > 0 ? exact : tocResult.headers.filter((h) => contains(h.text, query));
|
|
186
194
|
if (matches.length === 0) {
|
|
187
195
|
throw new Error(
|
|
188
196
|
`no header matching "${query}". Run \`${resolveBinName()} toc ${file}\` to list available headers.`,
|