harnery 0.45.1 → 0.46.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.d.ts +13 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commands/admission.d.ts.map +1 -1
- package/dist/commands/admission.js +214 -27
- package/dist/commands/agents.d.ts +4 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +102 -25
- package/dist/commands/browse.d.ts +3 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +172 -8
- package/dist/commands/fetch.js +1 -0
- package/dist/commands/ledger-v3.d.ts.map +1 -1
- package/dist/commands/ledger-v3.js +8 -2
- package/dist/commands/presence.d.ts.map +1 -1
- package/dist/commands/presence.js +40 -0
- package/dist/commands/qa-run.d.ts.map +1 -1
- package/dist/commands/qa-run.js +4 -3
- package/dist/commands/review-pack.d.ts.map +1 -1
- package/dist/commands/review-pack.js +33 -6
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +6 -45
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +0 -18
- package/dist/core/agents/index.d.ts +1 -0
- package/dist/core/agents/index.d.ts.map +1 -1
- package/dist/core/agents/index.js +1 -0
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +11 -4
- package/dist/core/agents/render/session-context.d.ts +0 -6
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +1 -6
- package/dist/core/agents/session-events.d.ts.map +1 -1
- package/dist/core/agents/session-events.js +12 -4
- package/dist/core/agents/state/heartbeat-reader.d.ts +3 -0
- package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.d.ts +3 -1
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +6 -2
- package/dist/core/agents/state/parent-reference-contract.d.ts +16 -0
- package/dist/core/agents/state/parent-reference-contract.d.ts.map +1 -0
- package/dist/core/agents/state/parent-reference-contract.js +1 -0
- package/dist/core/agents/state/parent-reference.d.ts +18 -0
- package/dist/core/agents/state/parent-reference.d.ts.map +1 -0
- package/dist/core/agents/state/parent-reference.js +119 -0
- package/dist/core/agents/state/pidmap.js +2 -2
- package/dist/core/agents/state/proc-start.d.ts +20 -31
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +37 -70
- package/dist/core/agents/state/service-process.d.ts +61 -0
- package/dist/core/agents/state/service-process.d.ts.map +1 -0
- package/dist/core/agents/state/service-process.js +143 -0
- package/dist/core/config.d.ts +39 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +63 -0
- package/dist/core/context/index.d.ts +6 -4
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +194 -256
- package/dist/core/context/observation.d.ts +9 -9
- package/dist/core/context/observation.d.ts.map +1 -1
- package/dist/core/context/observation.js +37 -6
- package/dist/core/diagnostics/path-identity.d.ts +1 -1
- package/dist/core/diagnostics/path-identity.d.ts.map +1 -1
- package/dist/core/diagnostics/path-identity.js +51 -6
- package/dist/core/events/v3/canonical.js +43 -14
- package/dist/core/events/v3/index.d.ts +1 -1
- package/dist/core/events/v3/index.d.ts.map +1 -1
- package/dist/core/events/v3/index.js +1 -1
- package/dist/core/events/v3/producers/recorder.d.ts +34 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +117 -13
- package/dist/core/events/v3/reader.d.ts.map +1 -1
- package/dist/core/events/v3/reader.js +53 -13
- package/dist/core/events/v3/runtime-build.d.ts +14 -0
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
- package/dist/core/events/v3/runtime-build.js +44 -0
- package/dist/core/events/v3/validate.d.ts.map +1 -1
- package/dist/core/events/v3/validate.js +36 -1
- package/dist/core/governor/index.d.ts +2 -1
- package/dist/core/governor/index.d.ts.map +1 -1
- package/dist/core/governor/index.js +2 -1
- package/dist/core/governor/service-read.d.ts +3 -10
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +10 -94
- package/dist/core/governor/service.d.ts +3 -7
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +17 -182
- package/dist/core/hooks/adapter/native-parent.d.ts +23 -0
- package/dist/core/hooks/adapter/native-parent.d.ts.map +1 -0
- package/dist/core/hooks/adapter/native-parent.js +91 -0
- package/dist/core/hooks/cli.js +79 -4
- package/dist/core/hooks/resolve/owner.d.ts +0 -11
- package/dist/core/hooks/resolve/owner.d.ts.map +1 -1
- package/dist/core/hooks/resolve/owner.js +2 -25
- package/dist/core/hooks/shell-admission.d.ts +84 -0
- package/dist/core/hooks/shell-admission.d.ts.map +1 -0
- package/dist/core/hooks/shell-admission.js +622 -0
- package/dist/core/hooks/shell-commit-tree.d.ts +2 -0
- package/dist/core/hooks/shell-commit-tree.d.ts.map +1 -0
- package/dist/core/hooks/shell-commit-tree.js +104 -0
- package/dist/core/inbox/contract.d.ts +12 -0
- package/dist/core/inbox/contract.d.ts.map +1 -1
- package/dist/core/inbox/contract.js +16 -0
- package/dist/core/inbox/peer-messages.d.ts +41 -16
- package/dist/core/inbox/peer-messages.d.ts.map +1 -1
- package/dist/core/inbox/peer-messages.js +372 -15
- 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 +83 -3
- package/dist/core/inbox/targets.d.ts +17 -3
- package/dist/core/inbox/targets.d.ts.map +1 -1
- package/dist/core/inbox/targets.js +77 -2
- package/dist/core/presence/git.d.ts +18 -12
- package/dist/core/presence/git.d.ts.map +1 -1
- package/dist/core/presence/git.js +32 -38
- package/dist/core/presence/index.d.ts +2 -2
- package/dist/core/presence/index.d.ts.map +1 -1
- package/dist/core/presence/index.js +8 -6
- package/dist/core/presence/mail.d.ts +133 -0
- package/dist/core/presence/mail.d.ts.map +1 -0
- package/dist/core/presence/mail.js +464 -0
- package/dist/core/resources/sampler.js +2 -2
- package/dist/core/resources/service-status.d.ts +2 -14
- package/dist/core/resources/service-status.d.ts.map +1 -1
- package/dist/core/resources/service-status.js +3 -23
- package/dist/core/resources/service.d.ts +3 -7
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +12 -101
- package/dist/core/resources/status.d.ts.map +1 -1
- package/dist/core/resources/status.js +3 -2
- package/dist/core/semantic/contract.d.ts +5 -5
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +9 -2
- package/dist/core/servers/index.d.ts +0 -2
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +3 -18
- package/dist/core/storage/modes.d.ts +4 -0
- package/dist/core/storage/modes.d.ts.map +1 -1
- package/dist/core/storage/modes.js +4 -0
- package/dist/core/storage/segments.d.ts.map +1 -1
- package/dist/core/storage/segments.js +5 -4
- package/dist/core/supervisor/service.d.ts +4 -7
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +23 -122
- package/dist/core/supervisor/services.js +2 -2
- package/dist/core/supervisor/status.d.ts +3 -17
- package/dist/core/supervisor/status.d.ts.map +1 -1
- package/dist/core/supervisor/status.js +7 -70
- package/dist/core/workflow/engine.js +2 -1
- package/dist/core/workflow/index.d.ts +3 -4
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -2
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +30 -74
- package/dist/core/workflow/resume-lock.d.ts +17 -0
- package/dist/core/workflow/resume-lock.d.ts.map +1 -0
- package/dist/core/workflow/resume-lock.js +158 -0
- package/dist/core/workflow/run-state.d.ts +0 -21
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +3 -174
- package/dist/core/workflow/workspaces/leases.d.ts +4 -0
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +1 -1
- package/dist/core/workflow/workspaces/local-git.js +91 -210
- package/dist/core/workflow/workspaces/ownership.d.ts +0 -7
- package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/ownership.js +3 -130
- package/dist/core/workflow/workspaces/reclaim.d.ts +4 -7
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/reclaim.js +0 -4
- package/dist/core/workflow/workspaces/recovery.d.ts +1 -1
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +1 -1
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +1 -4
- package/dist/lib/admission-history.d.ts +96 -0
- package/dist/lib/admission-history.d.ts.map +1 -0
- package/dist/lib/admission-history.js +196 -0
- package/dist/lib/admission.d.ts +56 -7
- package/dist/lib/admission.d.ts.map +1 -1
- package/dist/lib/admission.js +161 -19
- package/dist/lib/browser/client.d.ts +18 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +100 -12
- package/dist/lib/browser/content-checks.d.ts +3 -1
- package/dist/lib/browser/content-checks.d.ts.map +1 -1
- package/dist/lib/browser/content-checks.js +10 -2
- package/dist/lib/browser/critique.d.ts +32 -0
- package/dist/lib/browser/critique.d.ts.map +1 -1
- package/dist/lib/browser/critique.js +72 -3
- package/dist/lib/browser/geometry.d.ts +2 -0
- package/dist/lib/browser/geometry.d.ts.map +1 -1
- package/dist/lib/browser/geometry.js +61 -15
- package/dist/lib/browser/index.d.ts +1 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +1 -1
- package/dist/lib/browser/page-review-contracts.d.ts +6 -1
- package/dist/lib/browser/page-review-contracts.d.ts.map +1 -1
- package/dist/lib/browser/page-review-contracts.js +6 -1
- package/dist/lib/browser/page-review-judge.d.ts +3 -1
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.js +41 -11
- package/dist/lib/browser/page-review-pack.d.ts +3 -0
- package/dist/lib/browser/page-review-pack.d.ts.map +1 -1
- package/dist/lib/browser/page-review-pack.js +5 -2
- package/dist/lib/browser/qa-run-contracts.d.ts +11 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
- package/dist/lib/browser/qa-run-contracts.js +11 -4
- package/dist/lib/browser/qa-run.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.js +73 -16
- package/dist/lib/browser/runts.d.ts +4 -2
- package/dist/lib/browser/runts.d.ts.map +1 -1
- package/dist/lib/browser/runts.js +84 -33
- package/dist/lib/browser/windows-chrome.d.ts +14 -0
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
- package/dist/lib/browser/windows-chrome.js +42 -0
- package/dist/lib/browser/windows-login-sync.d.ts +110 -0
- package/dist/lib/browser/windows-login-sync.d.ts.map +1 -0
- package/dist/lib/browser/windows-login-sync.js +171 -0
- package/dist/lib/cookies/client.d.ts +30 -5
- package/dist/lib/cookies/client.d.ts.map +1 -1
- package/dist/lib/cookies/client.js +55 -10
- package/dist/lib/cookies/index.d.ts +1 -1
- package/dist/lib/cookies/index.d.ts.map +1 -1
- package/dist/lib/cookies/index.js +1 -1
- package/dist/lib/docs-metadata-sync.d.ts.map +1 -1
- package/dist/lib/docs-metadata-sync.js +41 -2
- package/dist/lib/durable-job.d.ts +8 -0
- package/dist/lib/durable-job.d.ts.map +1 -1
- package/dist/lib/durable-job.js +10 -0
- package/dist/lib/http/client.d.ts +9 -8
- package/dist/lib/http/client.d.ts.map +1 -1
- package/dist/lib/http/client.js +44 -2
- package/dist/lib/instructions/templates.js +4 -4
- package/package.json +2 -2
- package/src/commander.ts +13 -0
- package/src/commands/admission.ts +272 -29
- package/src/commands/agents.ts +132 -27
- package/src/commands/browse.ts +208 -6
- package/src/commands/fetch.ts +1 -0
- package/src/commands/ledger-v3.ts +8 -2
- package/src/commands/presence.ts +49 -0
- package/src/commands/qa-run.ts +4 -3
- package/src/commands/review-pack.ts +41 -5
- package/src/commands/workflow.ts +8 -58
- package/src/core/agents/coord-client.ts +0 -20
- package/src/core/agents/index.ts +6 -0
- package/src/core/agents/live-authority-v3.ts +11 -4
- package/src/core/agents/render/session-context.ts +1 -7
- package/src/core/agents/session-events.ts +18 -3
- package/src/core/agents/state/heartbeat-reader.ts +3 -0
- package/src/core/agents/state/live-coordination-view.ts +13 -6
- package/src/core/agents/state/parent-reference-contract.ts +14 -0
- package/src/core/agents/state/parent-reference.ts +179 -0
- package/src/core/agents/state/pidmap.ts +2 -2
- package/src/core/agents/state/proc-start.ts +41 -69
- package/src/core/agents/state/service-process.ts +217 -0
- package/src/core/config.ts +85 -0
- package/src/core/context/index.ts +207 -272
- package/src/core/context/observation.ts +40 -9
- package/src/core/diagnostics/path-identity.ts +52 -6
- package/src/core/events/v3/canonical.ts +46 -13
- package/src/core/events/v3/index.ts +3 -0
- package/src/core/events/v3/producers/recorder.ts +151 -13
- package/src/core/events/v3/reader.ts +55 -11
- package/src/core/events/v3/runtime-build.ts +62 -0
- package/src/core/events/v3/validate.ts +42 -1
- package/src/core/governor/index.ts +1 -2
- package/src/core/governor/service-read.ts +15 -114
- package/src/core/governor/service.ts +36 -220
- package/src/core/hooks/adapter/native-parent.ts +109 -0
- package/src/core/hooks/cli.ts +83 -4
- package/src/core/hooks/resolve/owner.ts +2 -25
- package/src/core/hooks/shell-admission.ts +634 -0
- package/src/core/hooks/shell-commit-tree.ts +102 -0
- package/src/core/inbox/contract.ts +18 -0
- package/src/core/inbox/peer-messages.ts +501 -15
- package/src/core/inbox/service.ts +108 -3
- package/src/core/inbox/targets.ts +94 -3
- package/src/core/presence/git.ts +45 -37
- package/src/core/presence/index.ts +16 -8
- package/src/core/presence/mail.ts +620 -0
- package/src/core/resources/sampler.ts +3 -2
- package/src/core/resources/service-status.ts +6 -35
- package/src/core/resources/service.ts +23 -133
- package/src/core/resources/status.ts +3 -2
- package/src/core/semantic/service.ts +8 -2
- package/src/core/servers/index.ts +3 -23
- package/src/core/storage/modes.ts +6 -0
- package/src/core/storage/segments.ts +5 -4
- package/src/core/supervisor/service.ts +33 -155
- package/src/core/supervisor/services.ts +2 -2
- package/src/core/supervisor/status.ts +10 -85
- package/src/core/workflow/engine.ts +1 -1
- package/src/core/workflow/index.ts +2 -13
- package/src/core/workflow/proof.ts +35 -83
- package/src/core/workflow/resume-lock.ts +179 -0
- package/src/core/workflow/run-state.ts +3 -207
- package/src/core/workflow/workspaces/leases.ts +1 -1
- package/src/core/workflow/workspaces/local-git.ts +120 -239
- package/src/core/workflow/workspaces/ownership.ts +3 -158
- package/src/core/workflow/workspaces/reclaim.ts +3 -9
- package/src/core/workflow/workspaces/recovery.ts +1 -1
- package/src/core/workflow/workspaces/state.ts +1 -5
- package/src/lib/admission-history.ts +267 -0
- package/src/lib/admission.ts +217 -19
- package/src/lib/browser/client.ts +147 -35
- package/src/lib/browser/content-checks.ts +14 -3
- package/src/lib/browser/critique.ts +113 -3
- package/src/lib/browser/geometry.ts +63 -14
- package/src/lib/browser/index.ts +5 -0
- package/src/lib/browser/page-review-contracts.ts +6 -1
- package/src/lib/browser/page-review-judge.ts +58 -13
- package/src/lib/browser/page-review-pack.ts +13 -2
- package/src/lib/browser/qa-run-contracts.ts +30 -4
- package/src/lib/browser/qa-run.ts +78 -17
- package/src/lib/browser/runts.ts +85 -37
- package/src/lib/browser/windows-chrome.ts +50 -0
- package/src/lib/browser/windows-login-sync.ts +275 -0
- package/src/lib/cookies/client.ts +62 -10
- package/src/lib/cookies/index.ts +3 -0
- package/src/lib/docs-metadata-sync.ts +44 -2
- package/src/lib/durable-job.ts +15 -0
- package/src/lib/http/client.ts +63 -1
- package/src/lib/instructions/templates.ts +4 -4
- package/dist/core/agents/state/process-handle.d.ts +0 -26
- package/dist/core/agents/state/process-handle.d.ts.map +0 -1
- package/dist/core/agents/state/process-handle.js +0 -121
- package/dist/core/workflow/recovery-audit.d.ts +0 -30
- package/dist/core/workflow/recovery-audit.d.ts.map +0 -1
- package/dist/core/workflow/recovery-audit.js +0 -116
- package/src/core/agents/state/process-handle.ts +0 -165
- package/src/core/workflow/recovery-audit.ts +0 -181
package/src/commands/agents.ts
CHANGED
|
@@ -78,6 +78,10 @@ import {
|
|
|
78
78
|
readLiveCoordinationRows,
|
|
79
79
|
} from "../core/agents/state/live-coordination-view.ts";
|
|
80
80
|
import { forkRelationLabels, resolveForkAncestry } from "../core/agents/state/names.ts";
|
|
81
|
+
import {
|
|
82
|
+
type AgentParentReference,
|
|
83
|
+
resolveAgentParentReference,
|
|
84
|
+
} from "../core/agents/state/parent-reference.ts";
|
|
81
85
|
import {
|
|
82
86
|
type AgentActivity,
|
|
83
87
|
foldSessionState,
|
|
@@ -114,6 +118,7 @@ import {
|
|
|
114
118
|
import {
|
|
115
119
|
listHookProducerStateRecordsV3,
|
|
116
120
|
readHookProducerStateV3,
|
|
121
|
+
scanHookProducerStateRecordsV3,
|
|
117
122
|
} from "../core/events/v3/producers/recorder.ts";
|
|
118
123
|
import { readLedgerV3 } from "../core/events/v3/reader.ts";
|
|
119
124
|
import { EVENT_V3_LEDGER_RELATIVE_ROOT } from "../core/events/v3/writer.ts";
|
|
@@ -124,7 +129,11 @@ import {
|
|
|
124
129
|
readRuntimeContextUsage,
|
|
125
130
|
} from "../core/hooks/adapter/runtime-telemetry.ts";
|
|
126
131
|
import { HarneryInboxCapacityError } from "../core/inbox/contract.ts";
|
|
127
|
-
import {
|
|
132
|
+
import {
|
|
133
|
+
type PeerRecipientResult,
|
|
134
|
+
peerInbox,
|
|
135
|
+
sendPeerMessage,
|
|
136
|
+
} from "../core/inbox/peer-messages.ts";
|
|
128
137
|
import { type RemoteMachine, readRemoteMachines } from "../core/presence/index.ts";
|
|
129
138
|
import { formatResourceSummary, readResourceStatus } from "../core/resources/status.ts";
|
|
130
139
|
import { registerContextCommand } from "./context.ts";
|
|
@@ -532,17 +541,31 @@ export function registerAgentsCommand(
|
|
|
532
541
|
.command("ping <name> <message...>")
|
|
533
542
|
.description(
|
|
534
543
|
"Queue a durable message for one known task instance by name. Stale or " +
|
|
535
|
-
"done tasks receive it when that same task resumes.
|
|
544
|
+
"done tasks receive it when that same task resumes. A recipient on " +
|
|
545
|
+
"another machine is reached through the shared origin. Ambiguous names " +
|
|
536
546
|
"require an exact instance; receipts do not confirm attention.",
|
|
537
547
|
)
|
|
538
548
|
.option("--instance-id <id>", "Select the exact known recipient instance")
|
|
549
|
+
.option("--generation-id <id>", "Require the exact recipient generation")
|
|
550
|
+
.option("--operation-id <id>", "Reuse a frozen delivery operation after a caller retry")
|
|
551
|
+
.option(
|
|
552
|
+
"--operation-replay-only",
|
|
553
|
+
"Require an existing plan after an ambiguous caller dispatch",
|
|
554
|
+
)
|
|
539
555
|
.option("--require-live", "Refuse a recipient outside live presence")
|
|
540
556
|
.option("--json", "JSON output")
|
|
541
557
|
.action(
|
|
542
558
|
(
|
|
543
559
|
name: string,
|
|
544
560
|
message: string[],
|
|
545
|
-
opts: {
|
|
561
|
+
opts: {
|
|
562
|
+
json?: boolean;
|
|
563
|
+
instanceId?: string;
|
|
564
|
+
generationId?: string;
|
|
565
|
+
operationId?: string;
|
|
566
|
+
operationReplayOnly?: boolean;
|
|
567
|
+
requireLive?: boolean;
|
|
568
|
+
},
|
|
546
569
|
) => {
|
|
547
570
|
runPing(name, message.join(" "), opts);
|
|
548
571
|
},
|
|
@@ -1754,6 +1777,7 @@ async function runShow(name: string, opts: { json?: boolean }): Promise<void> {
|
|
|
1754
1777
|
tool_counts: report?.tool_counts ?? [],
|
|
1755
1778
|
total_events: report?.total_events ?? 0,
|
|
1756
1779
|
bq_error: bqError,
|
|
1780
|
+
parent_reference: resolveAgentParentReference(root, hb),
|
|
1757
1781
|
};
|
|
1758
1782
|
|
|
1759
1783
|
if (opts.json) {
|
|
@@ -1774,6 +1798,7 @@ async function runShow(name: string, opts: { json?: boolean }): Promise<void> {
|
|
|
1774
1798
|
` session ${formatAge(ageSecs)} old · kind=${data.kind} · session_id=${data.session_id.slice(0, 8)}…`,
|
|
1775
1799
|
);
|
|
1776
1800
|
lines.push(` last heartbeat: ${formatAge(heartbeatAgeSecs)} ago`);
|
|
1801
|
+
lines.push(` parent: ${describeParentReference(data.parent_reference)}`);
|
|
1777
1802
|
lines.push(` activity: ${data.activity}`);
|
|
1778
1803
|
lines.push(
|
|
1779
1804
|
` lifecycle: ${data.task_state}${data.task_state === "blocked" && data.task_state_reason ? `: ${data.task_state_reason}` : ""}`,
|
|
@@ -2542,6 +2567,7 @@ function runStatus(opts: {
|
|
|
2542
2567
|
name: displayName,
|
|
2543
2568
|
instance_id: hb.instance_id,
|
|
2544
2569
|
kind: normalizeKind(hb.kind),
|
|
2570
|
+
parent_reference: resolveAgentParentReference(root, hb),
|
|
2545
2571
|
session_age_secs: ageSecs,
|
|
2546
2572
|
turn_elapsed_secs: turnElapsed ? turnElapsed.elapsed.secs : null,
|
|
2547
2573
|
turn_complete: turnElapsed ? turnElapsed.elapsed.complete : null,
|
|
@@ -2596,6 +2622,7 @@ function runStatus(opts: {
|
|
|
2596
2622
|
["changes", turnChanges.value],
|
|
2597
2623
|
["activity", activityOf(hb)],
|
|
2598
2624
|
["lifecycle", lifecycleLabel(hb)],
|
|
2625
|
+
["parent", describeParentReference(data.parent_reference)],
|
|
2599
2626
|
["context", ctxStr],
|
|
2600
2627
|
["resources", formatResourceSummary(resources)],
|
|
2601
2628
|
["files", filesStr],
|
|
@@ -3299,7 +3326,13 @@ export function collectEventLedgerHealthV3(root: string, nowMs = Date.now()): Ev
|
|
|
3299
3326
|
[];
|
|
3300
3327
|
const spanPressure: Extract<EventLedgerHealthV3, { state: "live" }>["span_pressure"] = [];
|
|
3301
3328
|
try {
|
|
3302
|
-
|
|
3329
|
+
const scan = scanHookProducerStateRecordsV3(root);
|
|
3330
|
+
for (const file of scan.invalid) {
|
|
3331
|
+
collectionErrors.push(
|
|
3332
|
+
`producer state skipped (${file.adapter}/${file.session_hash}.json): ${file.reason}`,
|
|
3333
|
+
);
|
|
3334
|
+
}
|
|
3335
|
+
for (const { state } of scan.records) {
|
|
3303
3336
|
if (state.spans.length >= SPAN_PRESSURE_SOFT_WATERMARK) {
|
|
3304
3337
|
spanPressure.push({
|
|
3305
3338
|
instance_id: state.instance_id,
|
|
@@ -4878,10 +4911,42 @@ function formatDuration(ms: number): string {
|
|
|
4878
4911
|
return h > 0 ? `${d}d${h}h` : `${d}d`;
|
|
4879
4912
|
}
|
|
4880
4913
|
|
|
4914
|
+
export function describeParentReference(reference: AgentParentReference): string {
|
|
4915
|
+
return reference.state === "verified"
|
|
4916
|
+
? `${reference.parent_name ?? "unnamed"} (${reference.parent_instance_id}; ${reference.source}; ${reference.addressable ? "addressable" : "not addressable"})`
|
|
4917
|
+
: `${reference.state}: ${reference.reason}`;
|
|
4918
|
+
}
|
|
4919
|
+
|
|
4920
|
+
export function formatPeerRecipientReceipt(recipient: PeerRecipientResult): string {
|
|
4921
|
+
if (!recipient.ok) return `${recipient.code}: ${recipient.message}`;
|
|
4922
|
+
const address = `agent-${recipient.target.name} (${recipient.target.instance_id})`;
|
|
4923
|
+
if (recipient.delivery_state === "delivered")
|
|
4924
|
+
return `Previously acknowledged by the recipient machine for ${address}; task attention is not confirmed.`;
|
|
4925
|
+
if (recipient.delivery_state === "expired")
|
|
4926
|
+
return `The prior remote message for ${address} expired; no new delivery is established.`;
|
|
4927
|
+
if (recipient.target.machine)
|
|
4928
|
+
return (
|
|
4929
|
+
`Stored for ${address} on ${recipient.target.machine} as ${recipient.message_id}. ` +
|
|
4930
|
+
(recipient.published
|
|
4931
|
+
? "Published to origin; recipient-machine import is not confirmed."
|
|
4932
|
+
: `Publication is not confirmed${recipient.publish_error ? ` (${recipient.publish_error})` : ""}.`)
|
|
4933
|
+
);
|
|
4934
|
+
return recipient.state === "duplicate"
|
|
4935
|
+
? `Previously stored for ${address} as ${recipient.message_id}; current surface state is not established by this receipt.`
|
|
4936
|
+
: `Stored for ${address} as ${recipient.message_id}; task attention is not confirmed.`;
|
|
4937
|
+
}
|
|
4938
|
+
|
|
4881
4939
|
function runPing(
|
|
4882
4940
|
name: string,
|
|
4883
4941
|
message: string,
|
|
4884
|
-
opts: {
|
|
4942
|
+
opts: {
|
|
4943
|
+
json?: boolean;
|
|
4944
|
+
instanceId?: string;
|
|
4945
|
+
generationId?: string;
|
|
4946
|
+
operationId?: string;
|
|
4947
|
+
operationReplayOnly?: boolean;
|
|
4948
|
+
requireLive?: boolean;
|
|
4949
|
+
},
|
|
4885
4950
|
): void {
|
|
4886
4951
|
if (!message || message.trim().length === 0) {
|
|
4887
4952
|
emit.error({ code: "empty_message", message: "message is required (and non-empty)" });
|
|
@@ -4910,10 +4975,17 @@ function runPing(
|
|
|
4910
4975
|
try {
|
|
4911
4976
|
sent = sendPeerMessage({
|
|
4912
4977
|
coordRoot,
|
|
4913
|
-
target: {
|
|
4978
|
+
target: {
|
|
4979
|
+
name,
|
|
4980
|
+
instanceId: opts.instanceId,
|
|
4981
|
+
generationId: opts.generationId,
|
|
4982
|
+
requireLive: opts.requireLive,
|
|
4983
|
+
},
|
|
4984
|
+
operationId: opts.operationId,
|
|
4985
|
+
operationReplayOnly: opts.operationReplayOnly,
|
|
4914
4986
|
senderName: fromName,
|
|
4915
4987
|
senderInstanceId: myOwner,
|
|
4916
|
-
body: message
|
|
4988
|
+
body: message,
|
|
4917
4989
|
});
|
|
4918
4990
|
} catch (error) {
|
|
4919
4991
|
if (error instanceof HarneryInboxCapacityError) {
|
|
@@ -4922,51 +4994,84 @@ function runPing(
|
|
|
4922
4994
|
}
|
|
4923
4995
|
throw error;
|
|
4924
4996
|
}
|
|
4997
|
+
if (opts.json) emit.config({ format: "json" });
|
|
4998
|
+
const routing = {
|
|
4999
|
+
operation_id: sent.operation_id,
|
|
5000
|
+
child: sent.child,
|
|
5001
|
+
parent_reference: sent.parent_reference,
|
|
5002
|
+
parent_copy: sent.parent_copy,
|
|
5003
|
+
complete: sent.complete,
|
|
5004
|
+
};
|
|
5005
|
+
// Keep both actual recipient writes visible, including parent-only success.
|
|
5006
|
+
for (const recipient of [sent.child, sent.parent_copy]) {
|
|
5007
|
+
if (!("ok" in recipient) || !recipient.ok) continue;
|
|
5008
|
+
emitEventV3({
|
|
5009
|
+
owner: myOwner,
|
|
5010
|
+
session: nativeSessionIdentity(myHb, myOwner),
|
|
5011
|
+
adapter: normalizeAdapter(myHb?.platform),
|
|
5012
|
+
observation: {
|
|
5013
|
+
event_type: "coord.message_observed",
|
|
5014
|
+
direction: "sent",
|
|
5015
|
+
subject: recipient.target.instance_id,
|
|
5016
|
+
message_id: recipient.message_id,
|
|
5017
|
+
body:
|
|
5018
|
+
recipient === sent.child
|
|
5019
|
+
? message
|
|
5020
|
+
: `[Parent copy for agent-${sent.child.target?.name} (${sent.child.target?.instance_id})]\n${message}`,
|
|
5021
|
+
},
|
|
5022
|
+
});
|
|
5023
|
+
}
|
|
4925
5024
|
if (!sent.ok) {
|
|
4926
|
-
emit.
|
|
5025
|
+
emit.data({
|
|
5026
|
+
...routing,
|
|
5027
|
+
ok: false,
|
|
4927
5028
|
code: sent.code,
|
|
4928
5029
|
message: sent.message,
|
|
5030
|
+
from: fromName,
|
|
4929
5031
|
...(sent.candidates ? { candidates: sent.candidates } : {}),
|
|
4930
5032
|
});
|
|
4931
|
-
process.
|
|
5033
|
+
process.exitCode = 1;
|
|
5034
|
+
return;
|
|
4932
5035
|
}
|
|
4933
5036
|
|
|
4934
|
-
|
|
4935
|
-
// in data, so read-only observers can render the communication without
|
|
4936
|
-
// joining journal files.
|
|
4937
|
-
emitEventV3({
|
|
4938
|
-
owner: myOwner,
|
|
4939
|
-
session: nativeSessionIdentity(myHb, myOwner),
|
|
4940
|
-
adapter: normalizeAdapter(myHb?.platform),
|
|
4941
|
-
observation: {
|
|
4942
|
-
event_type: "coord.message_observed",
|
|
4943
|
-
direction: "sent",
|
|
4944
|
-
subject: sent.target.instance_id,
|
|
4945
|
-
message_id: sent.message_id,
|
|
4946
|
-
body: message.trim(),
|
|
4947
|
-
},
|
|
4948
|
-
});
|
|
4949
|
-
|
|
5037
|
+
const remote = "published" in sent ? sent : null;
|
|
4950
5038
|
const data = {
|
|
5039
|
+
...routing,
|
|
4951
5040
|
peer: sent.target.name,
|
|
4952
5041
|
peer_instance_id: sent.target.instance_id,
|
|
5042
|
+
...(sent.target.machine ? { peer_machine: sent.target.machine } : {}),
|
|
4953
5043
|
state: sent.state,
|
|
4954
5044
|
message_id: sent.message_id,
|
|
4955
5045
|
synced: sent.synced,
|
|
4956
5046
|
body_bytes: sent.body_bytes,
|
|
4957
5047
|
from: fromName,
|
|
5048
|
+
...(remote
|
|
5049
|
+
? {
|
|
5050
|
+
published: remote.published,
|
|
5051
|
+
...(remote.delivery_state ? { delivery_state: remote.delivery_state } : {}),
|
|
5052
|
+
...(remote.publish_error ? { publish_error: remote.publish_error } : {}),
|
|
5053
|
+
}
|
|
5054
|
+
: {}),
|
|
4958
5055
|
};
|
|
4959
5056
|
|
|
4960
5057
|
if (opts.json) {
|
|
4961
5058
|
emit.config({ format: "json" });
|
|
4962
5059
|
emit.data(data);
|
|
5060
|
+
if (!sent.complete) process.exitCode = 1;
|
|
4963
5061
|
return;
|
|
4964
5062
|
}
|
|
4965
5063
|
emit.data(data);
|
|
5064
|
+
if (!sent.complete) process.exitCode = 1;
|
|
4966
5065
|
emit.text(
|
|
4967
|
-
|
|
4968
|
-
|
|
5066
|
+
`parent copy: ${
|
|
5067
|
+
"ok" in sent.parent_copy
|
|
5068
|
+
? sent.parent_copy.ok
|
|
5069
|
+
? formatPeerRecipientReceipt(sent.parent_copy)
|
|
5070
|
+
: `${sent.parent_copy.code}: ${sent.parent_copy.message}`
|
|
5071
|
+
: `skipped (${sent.parent_copy.reason})`
|
|
5072
|
+
}\n`,
|
|
4969
5073
|
);
|
|
5074
|
+
emit.text(`${formatPeerRecipientReceipt(sent.child)}\n`);
|
|
4970
5075
|
}
|
|
4971
5076
|
|
|
4972
5077
|
async function runWait(
|
package/src/commands/browse.ts
CHANGED
|
@@ -74,6 +74,7 @@ import {
|
|
|
74
74
|
} from "../lib/browser/page-review-budget.ts";
|
|
75
75
|
import {
|
|
76
76
|
PAGE_REVIEW_CAPTURE_PLAN_SCHEMA,
|
|
77
|
+
PAGE_REVIEW_MAX_TILE_BUDGET,
|
|
77
78
|
type PageReviewCapturePlan,
|
|
78
79
|
type PageReviewContextAllocation,
|
|
79
80
|
type PageReviewSourceEvidence,
|
|
@@ -137,10 +138,19 @@ import {
|
|
|
137
138
|
keepWindowsChromeSession,
|
|
138
139
|
stopWindowsChromeProfile,
|
|
139
140
|
windowsChromePath,
|
|
141
|
+
windowsChromeProfileProcesses,
|
|
140
142
|
windowsLocalAppData,
|
|
143
|
+
windowsPathToWsl,
|
|
141
144
|
windowsPlainProfile,
|
|
142
145
|
wslPathToWindows,
|
|
143
146
|
} from "../lib/browser/windows-chrome.ts";
|
|
147
|
+
import {
|
|
148
|
+
type LoginSyncDeps,
|
|
149
|
+
type LoginSyncResult,
|
|
150
|
+
profileCookieDbMtimeMs,
|
|
151
|
+
recordLoginSync,
|
|
152
|
+
syncLoginCookies,
|
|
153
|
+
} from "../lib/browser/windows-login-sync.ts";
|
|
144
154
|
import { applyExtraCookies, CookieJar, mergeCookies } from "../lib/cookies/index.ts";
|
|
145
155
|
import { commandPaceGate } from "../lib/pace/index.ts";
|
|
146
156
|
|
|
@@ -216,6 +226,7 @@ interface BrowseOpts {
|
|
|
216
226
|
collectOut?: string;
|
|
217
227
|
networkHar?: string;
|
|
218
228
|
login?: boolean;
|
|
229
|
+
syncLogin?: boolean;
|
|
219
230
|
attached?: boolean;
|
|
220
231
|
loginCloseFile?: string;
|
|
221
232
|
controlFile?: string;
|
|
@@ -249,7 +260,9 @@ interface BrowseOpts {
|
|
|
249
260
|
checkWidthAnnotate?: boolean;
|
|
250
261
|
// `--check-runts [selector]`: true = whole body, string = scope selector.
|
|
251
262
|
checkRunts?: boolean | string;
|
|
263
|
+
checkScanNodeBudget?: string;
|
|
252
264
|
checkRuntsMinChars?: string;
|
|
265
|
+
checkRuntsMaxFindings?: string;
|
|
253
266
|
checkRuntsFail?: boolean;
|
|
254
267
|
checkRuntsAnnotate?: boolean;
|
|
255
268
|
// Horizontal-overflow check
|
|
@@ -350,6 +363,7 @@ const VIEWPORT_PRESETS: Record<string, { width: number; height: number }> = {
|
|
|
350
363
|
// cookies/read: the many helper functions in this large file close over
|
|
351
364
|
// `emit` so action callbacks stay concise.
|
|
352
365
|
let emit: EmitContext;
|
|
366
|
+
let browseBinName = "harn";
|
|
353
367
|
|
|
354
368
|
export function registerBrowseCommand(
|
|
355
369
|
program: Command,
|
|
@@ -358,6 +372,7 @@ export function registerBrowseCommand(
|
|
|
358
372
|
binName = resolveBinName(),
|
|
359
373
|
): void {
|
|
360
374
|
emit = emitParam;
|
|
375
|
+
browseBinName = binName;
|
|
361
376
|
program
|
|
362
377
|
.command("browse <url>")
|
|
363
378
|
.description(
|
|
@@ -469,9 +484,17 @@ export function registerBrowseCommand(
|
|
|
469
484
|
.option(
|
|
470
485
|
"--login",
|
|
471
486
|
"Open the URL in the installed Google Chrome with nothing attached (no DevTools protocol, no " +
|
|
472
|
-
"Playwright) so a person can sign in; under WSL this is the Windows host's Chrome
|
|
473
|
-
"close the window or create the --login-close-file; later browse
|
|
474
|
-
"the cookies.
|
|
487
|
+
"Playwright) so a person can sign in; under WSL this is the Windows host's Chrome on a dedicated " +
|
|
488
|
+
"sign-in profile. Sign in, then close the window or create the --login-close-file; later browse " +
|
|
489
|
+
"and browse-session runs reuse the cookies. Under WSL the profile can also stay in use as an " +
|
|
490
|
+
"ordinary browser: each headless browse copies the target site's cookies from it when it changed " +
|
|
491
|
+
"and is closed, and an open profile is left alone (see --sync-login). Sites cannot tell this " +
|
|
492
|
+
"window from the person's own browser.",
|
|
493
|
+
)
|
|
494
|
+
.option(
|
|
495
|
+
"--sync-login",
|
|
496
|
+
"WSL only: copy the URL's site cookies from the Windows sign-in profile into the cookie store " +
|
|
497
|
+
"now, then exit without loading the page. Refuses while Chrome is open on that profile.",
|
|
475
498
|
)
|
|
476
499
|
.option(
|
|
477
500
|
"--attached",
|
|
@@ -621,6 +644,12 @@ export function registerBrowseCommand(
|
|
|
621
644
|
"Minimum block text length to scan (default 40; smaller labels can't meaningfully wrap).",
|
|
622
645
|
"40",
|
|
623
646
|
)
|
|
647
|
+
.option(
|
|
648
|
+
"--check-runts-max-findings <n>",
|
|
649
|
+
"Maximum runt findings to retain (integer 1..5000, default 50). Reaching the limit " +
|
|
650
|
+
"still marks the sweep incomplete; the separate 5000-block scan cap is unchanged.",
|
|
651
|
+
"50",
|
|
652
|
+
)
|
|
624
653
|
.option(
|
|
625
654
|
"--check-runts-fail",
|
|
626
655
|
"Run a document-wide runt check when needed; exit 2 on a runt, missing scope, or incomplete sweep.",
|
|
@@ -710,6 +739,10 @@ export function registerBrowseCommand(
|
|
|
710
739
|
)
|
|
711
740
|
.option("--check-crowd-fail", "Exit 2 when a crowd target fails, is unknown, or is missing.")
|
|
712
741
|
.option("--no-check-crowd-annotate", "Skip crowd screenshot annotations.")
|
|
742
|
+
.option(
|
|
743
|
+
"--check-scan-node-budget <n>",
|
|
744
|
+
"Explicit geometry/content traversal budget per scope (1–100000 nodes). Omit to keep each rule's existing limit; incomplete scans remain unknown.",
|
|
745
|
+
)
|
|
713
746
|
.option(
|
|
714
747
|
"--check-placeholder [selector]",
|
|
715
748
|
"Scan rendered text for unrendered template tokens and bound-value tells: " +
|
|
@@ -786,7 +819,7 @@ export function registerBrowseCommand(
|
|
|
786
819
|
)
|
|
787
820
|
.option(
|
|
788
821
|
"--check-critique-max-tiles <n>",
|
|
789
|
-
|
|
822
|
+
`Cap on tiles sent to the model, to bound cost (default 24, at most ${PAGE_REVIEW_MAX_TILE_BUDGET}).`,
|
|
790
823
|
"24",
|
|
791
824
|
)
|
|
792
825
|
.option("--check-critique-rubric <text>", "Override the default critique rubric.")
|
|
@@ -1000,6 +1033,23 @@ async function runBrowse(
|
|
|
1000
1033
|
"--check-overlap-threshold",
|
|
1001
1034
|
);
|
|
1002
1035
|
const crowdMin = parseNonNegativeNumber(opts.checkCrowdMin ?? "6", "--check-crowd-min");
|
|
1036
|
+
const scanNodeBudget =
|
|
1037
|
+
opts.checkScanNodeBudget === undefined
|
|
1038
|
+
? undefined
|
|
1039
|
+
: parsePositiveInt(opts.checkScanNodeBudget, "--check-scan-node-budget");
|
|
1040
|
+
if (
|
|
1041
|
+
scanNodeBudget !== undefined &&
|
|
1042
|
+
(!Number.isSafeInteger(scanNodeBudget) || scanNodeBudget > 100_000)
|
|
1043
|
+
) {
|
|
1044
|
+
throw new Error("--check-scan-node-budget must be an integer from 1 to 100000.");
|
|
1045
|
+
}
|
|
1046
|
+
const runtsMaxFindings = parsePositiveInt(
|
|
1047
|
+
opts.checkRuntsMaxFindings ?? "50",
|
|
1048
|
+
"--check-runts-max-findings",
|
|
1049
|
+
);
|
|
1050
|
+
if (runtsMaxFindings > 5000) {
|
|
1051
|
+
throw new Error("--check-runts-max-findings must be an integer from 1 to 5000.");
|
|
1052
|
+
}
|
|
1003
1053
|
const hitProfile = parseTargetSizeProfile(opts.checkHitProfile ?? "wcag-aa");
|
|
1004
1054
|
|
|
1005
1055
|
// Commander: `--no-cookies` turns `opts.cookies` into `false`. Default is `true`.
|
|
@@ -1048,6 +1098,13 @@ async function runBrowse(
|
|
|
1048
1098
|
);
|
|
1049
1099
|
}
|
|
1050
1100
|
|
|
1101
|
+
if (opts.syncLogin) {
|
|
1102
|
+
if (opts.login) throw new Error("--sync-login cannot run with --login.");
|
|
1103
|
+
if (!jar) throw new Error("--sync-login needs the cookie store; drop --no-cookies.");
|
|
1104
|
+
runSyncLogin(url, jar.path);
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1051
1108
|
const collectPlan = opts.collect ? buildCollectPlan(opts) : null;
|
|
1052
1109
|
|
|
1053
1110
|
const pace = commandPaceGate(opts.pace !== false, (message) => emit.log(message, "info"));
|
|
@@ -1067,12 +1124,15 @@ async function runBrowse(
|
|
|
1067
1124
|
return;
|
|
1068
1125
|
}
|
|
1069
1126
|
|
|
1127
|
+
if (jar && loginMode === "none") autoSyncLoginCookies(url, jar.path);
|
|
1128
|
+
|
|
1070
1129
|
const channel = resolveBrowserChannel(opts, Boolean(headed));
|
|
1071
1130
|
const userAgent = resolveUserAgent({
|
|
1072
1131
|
requested: opts.userAgent,
|
|
1073
1132
|
env: process.env.HARNERY_BROWSER_UA,
|
|
1074
1133
|
chromeMajor: launchedChromeMajor(channel),
|
|
1075
1134
|
});
|
|
1135
|
+
const cookieGuard = context?.cookieGuard?.();
|
|
1076
1136
|
const browser = new Browser({
|
|
1077
1137
|
profileDir: opts.profile ?? DEFAULT_PROFILE,
|
|
1078
1138
|
headed,
|
|
@@ -1084,6 +1144,7 @@ async function runBrowse(
|
|
|
1084
1144
|
waitUntil: opts.waitUntil as BrowseOpts["waitUntil"] as never,
|
|
1085
1145
|
recordHarPath: opts.networkHar ? resolve(opts.networkHar) : undefined,
|
|
1086
1146
|
extraHeaders: context?.extraHeaders,
|
|
1147
|
+
...(cookieGuard ? { cookieGuard } : {}),
|
|
1087
1148
|
pace,
|
|
1088
1149
|
launchArgs: resolveLaunchArgs(opts, Boolean(headed), Boolean(proxy)),
|
|
1089
1150
|
...(channel ? { channel } : {}),
|
|
@@ -1183,6 +1244,7 @@ async function runBrowse(
|
|
|
1183
1244
|
runts = await browser.checkRunts({
|
|
1184
1245
|
scope: typeof opts.checkRunts === "string" ? opts.checkRunts : null,
|
|
1185
1246
|
minChars: Number.parseInt(opts.checkRuntsMinChars ?? "40", 10),
|
|
1247
|
+
maxFindings: runtsMaxFindings,
|
|
1186
1248
|
});
|
|
1187
1249
|
}
|
|
1188
1250
|
const hasLayoutLint =
|
|
@@ -1194,6 +1256,7 @@ async function runBrowse(
|
|
|
1194
1256
|
let layoutLint: LayoutLintResult | undefined;
|
|
1195
1257
|
if (hasLayoutLint) {
|
|
1196
1258
|
layoutLint = await browser.checkLayoutLint({
|
|
1259
|
+
scanNodeBudget,
|
|
1197
1260
|
align: (opts.checkAlign ?? []).map((selector) => ({
|
|
1198
1261
|
selector,
|
|
1199
1262
|
axis: alignAxis,
|
|
@@ -1231,6 +1294,7 @@ async function runBrowse(
|
|
|
1231
1294
|
let content: ContentChecksResult | undefined;
|
|
1232
1295
|
if (hasContentChecks) {
|
|
1233
1296
|
content = await browser.checkContent({
|
|
1297
|
+
scanNodeBudget,
|
|
1234
1298
|
placeholder:
|
|
1235
1299
|
opts.checkPlaceholder !== undefined
|
|
1236
1300
|
? { scope: contentScope(opts.checkPlaceholder) }
|
|
@@ -1792,6 +1856,9 @@ async function runBrowse(
|
|
|
1792
1856
|
}
|
|
1793
1857
|
if (failed.length > 0) process.exitCode = 2;
|
|
1794
1858
|
}
|
|
1859
|
+
} catch (error) {
|
|
1860
|
+
// A cookie guard that stopped the session is the real failure, not the closed-page errors it caused.
|
|
1861
|
+
throw browser.cookieGuardFault ?? error;
|
|
1795
1862
|
} finally {
|
|
1796
1863
|
process.removeListener("SIGINT", handleSigint);
|
|
1797
1864
|
process.removeListener("SIGTERM", handleSigterm);
|
|
@@ -1799,6 +1866,7 @@ async function runBrowse(
|
|
|
1799
1866
|
await browser.close();
|
|
1800
1867
|
if (receivedSignal) process.exitCode = receivedSignal === "SIGINT" ? 130 : 143;
|
|
1801
1868
|
}
|
|
1869
|
+
if (browser.cookieGuardFault) throw browser.cookieGuardFault;
|
|
1802
1870
|
}
|
|
1803
1871
|
|
|
1804
1872
|
// ---------------------------------------------------------------------------
|
|
@@ -2800,7 +2868,7 @@ async function runQaPlanning(
|
|
|
2800
2868
|
);
|
|
2801
2869
|
const band = Number.parseInt(opts.checkCritiqueBand ?? "1400", 10);
|
|
2802
2870
|
const overlap = Number.parseInt(opts.checkCritiqueOverlap ?? "120", 10);
|
|
2803
|
-
const maxTiles =
|
|
2871
|
+
const maxTiles = critiqueTilingKnobs(opts).maxTiles;
|
|
2804
2872
|
const estimatedFullPageTiles = Math.min(
|
|
2805
2873
|
maxTiles,
|
|
2806
2874
|
Math.max(1, Math.ceil(pageHeight / Math.max(1, band - overlap))),
|
|
@@ -2833,7 +2901,12 @@ function critiqueTilingKnobs(
|
|
|
2833
2901
|
opts: BrowseOpts,
|
|
2834
2902
|
tileBudgetPx?: number,
|
|
2835
2903
|
): { maxTiles: number; band: number; overlap: number } {
|
|
2836
|
-
|
|
2904
|
+
// No flag value can exceed the hard per-invocation ceiling.
|
|
2905
|
+
const requested = Number.parseInt(opts.checkCritiqueMaxTiles ?? "24", 10);
|
|
2906
|
+
const maxTiles = Math.min(
|
|
2907
|
+
PAGE_REVIEW_MAX_TILE_BUDGET,
|
|
2908
|
+
Math.max(1, Number.isFinite(requested) ? requested : 24),
|
|
2909
|
+
);
|
|
2837
2910
|
const bandFlag = Math.max(200, Number.parseInt(opts.checkCritiqueBand ?? "1400", 10));
|
|
2838
2911
|
const band = tileBudgetPx ? Math.max(200, Math.min(bandFlag, tileBudgetPx)) : bandFlag;
|
|
2839
2912
|
const overlap = Math.max(0, Number.parseInt(opts.checkCritiqueOverlap ?? "120", 10));
|
|
@@ -3452,6 +3525,125 @@ async function runPlainLogin(
|
|
|
3452
3525
|
emit.log(`[--login] Chrome closed; cookies persisted in ${profileDir}.`, "info");
|
|
3453
3526
|
}
|
|
3454
3527
|
|
|
3528
|
+
/**
|
|
3529
|
+
* The WSL sign-in profile and the operations the cookie sync needs, or
|
|
3530
|
+
* `undefined` when this machine has none (not WSL, no Windows Chrome, or
|
|
3531
|
+
* `HARNERY_BROWSER_WSL_PLAIN=linux`).
|
|
3532
|
+
*/
|
|
3533
|
+
function windowsLoginProfile(): { profileWindows: string; deps: LoginSyncDeps } | undefined {
|
|
3534
|
+
if (!useWindowsPlainLogin()) return undefined;
|
|
3535
|
+
const chromeLinux = windowsChromePath();
|
|
3536
|
+
const localAppData = windowsLocalAppData();
|
|
3537
|
+
if (!chromeLinux || !localAppData) return undefined;
|
|
3538
|
+
const chromeWindows = wslPathToWindows(chromeLinux);
|
|
3539
|
+
const profileWindows = windowsPlainProfile(localAppData);
|
|
3540
|
+
const profileLinux = windowsPathToWsl(profileWindows);
|
|
3541
|
+
return {
|
|
3542
|
+
profileWindows,
|
|
3543
|
+
deps: {
|
|
3544
|
+
cookieDbMtimeMs: () => profileCookieDbMtimeMs(profileLinux),
|
|
3545
|
+
profileProcesses: () => windowsChromeProfileProcesses(profileWindows),
|
|
3546
|
+
harvest: (domain) =>
|
|
3547
|
+
harvestWindowsChromeCookies({
|
|
3548
|
+
chromeWindows,
|
|
3549
|
+
profileWindows,
|
|
3550
|
+
domain,
|
|
3551
|
+
pageTimeoutSeconds: 0,
|
|
3552
|
+
}),
|
|
3553
|
+
},
|
|
3554
|
+
};
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
/** One-line summary of a sync that read cookies. Names only, never values. */
|
|
3558
|
+
function loginSyncSummary(result: LoginSyncResult, storePath: string): string {
|
|
3559
|
+
if (result.status === "synced") {
|
|
3560
|
+
return `Copied ${result.merged} ${result.domain} cookies (${result.sessionCookies} browser-session) from the sign-in profile into ${storePath}: ${result.names.join(", ")}.`;
|
|
3561
|
+
}
|
|
3562
|
+
return `The sign-in profile's ${result.domain} cookies (${result.cookies}) match the last copy; the cookie store was left as it is.`;
|
|
3563
|
+
}
|
|
3564
|
+
|
|
3565
|
+
/**
|
|
3566
|
+
* Before a headless run under WSL, copy the target site's cookies from the
|
|
3567
|
+
* sign-in profile when the profile changed since the last copy and no
|
|
3568
|
+
* Chrome is open on it. Failures are warnings; the run continues.
|
|
3569
|
+
* `HARNERY_BROWSER_LOGIN_SYNC=off` turns it off.
|
|
3570
|
+
*/
|
|
3571
|
+
function autoSyncLoginCookies(url: string, storePath: string): void {
|
|
3572
|
+
if ((process.env.HARNERY_BROWSER_LOGIN_SYNC ?? "").toLowerCase() === "off") return;
|
|
3573
|
+
let profile: ReturnType<typeof windowsLoginProfile>;
|
|
3574
|
+
let domain: string;
|
|
3575
|
+
try {
|
|
3576
|
+
profile = windowsLoginProfile();
|
|
3577
|
+
domain = harvestDomain(url);
|
|
3578
|
+
} catch {
|
|
3579
|
+
return;
|
|
3580
|
+
}
|
|
3581
|
+
if (!profile) return;
|
|
3582
|
+
try {
|
|
3583
|
+
const result = syncLoginCookies({ domain, storePath, deps: profile.deps });
|
|
3584
|
+
if (result.status === "open") {
|
|
3585
|
+
emit.log(
|
|
3586
|
+
`[login-sync] The shared sign-in window (Windows Chrome on ${profile.profileWindows}) is open and changed since ${domain} cookies were last copied; this run uses the cookie store as it is. Its ${domain} session reaches headless runs after you close that window, or run \`${browseBinName} browse --sync-login ${url}\` once it is closed.`,
|
|
3587
|
+
"info",
|
|
3588
|
+
);
|
|
3589
|
+
} else if (result.status === "busy") {
|
|
3590
|
+
emit.log(
|
|
3591
|
+
"[login-sync] Another run is copying sign-in cookies; this run did not wait.",
|
|
3592
|
+
"debug",
|
|
3593
|
+
);
|
|
3594
|
+
} else if (result.status === "synced" || result.status === "unchanged") {
|
|
3595
|
+
emit.log(`[login-sync] ${loginSyncSummary(result, storePath)}`, "info");
|
|
3596
|
+
}
|
|
3597
|
+
} catch (err) {
|
|
3598
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3599
|
+
emit.log(
|
|
3600
|
+
`[login-sync] Could not copy ${domain} cookies from the sign-in profile; continuing with the cookie store as it is. ${message}`,
|
|
3601
|
+
"warn",
|
|
3602
|
+
);
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3606
|
+
/** `--sync-login`: copy the URL's site cookies from the sign-in profile now. */
|
|
3607
|
+
function runSyncLogin(url: string, storePath: string): void {
|
|
3608
|
+
const profile = windowsLoginProfile();
|
|
3609
|
+
if (!profile) {
|
|
3610
|
+
throw new Error(
|
|
3611
|
+
"--sync-login needs WSL with the Windows Google Chrome; this machine has no Windows sign-in profile.",
|
|
3612
|
+
);
|
|
3613
|
+
}
|
|
3614
|
+
const domain = harvestDomain(url);
|
|
3615
|
+
const result = syncLoginCookies({ domain, storePath, deps: profile.deps, force: true });
|
|
3616
|
+
if (result.status === "no-profile") {
|
|
3617
|
+
throw new Error(
|
|
3618
|
+
`The sign-in profile ${profile.profileWindows} has no cookies yet; sign in with \`${browseBinName} browse --login ${url}\` first.`,
|
|
3619
|
+
);
|
|
3620
|
+
}
|
|
3621
|
+
if (result.status === "open") {
|
|
3622
|
+
throw new Error(
|
|
3623
|
+
`Chrome is open on the sign-in profile ${profile.profileWindows} (process ${result.processes.join(", ")}). Close that window, then run --sync-login again.`,
|
|
3624
|
+
);
|
|
3625
|
+
}
|
|
3626
|
+
if (result.status === "busy") {
|
|
3627
|
+
throw new Error("Another run is copying sign-in cookies right now; try again in a moment.");
|
|
3628
|
+
}
|
|
3629
|
+
if (result.cookies === 0) {
|
|
3630
|
+
emit.log(`[--sync-login] No ${domain} cookies were found in the sign-in profile.`, "warn");
|
|
3631
|
+
} else {
|
|
3632
|
+
emit.log(`[--sync-login] ${loginSyncSummary(result, storePath)}`, "info");
|
|
3633
|
+
}
|
|
3634
|
+
emit.data({
|
|
3635
|
+
loginSync: {
|
|
3636
|
+
domain,
|
|
3637
|
+
store: storePath,
|
|
3638
|
+
status: result.status,
|
|
3639
|
+
cookies: result.cookies,
|
|
3640
|
+
merged: result.merged,
|
|
3641
|
+
sessionCookies: result.sessionCookies,
|
|
3642
|
+
names: result.names,
|
|
3643
|
+
},
|
|
3644
|
+
});
|
|
3645
|
+
}
|
|
3646
|
+
|
|
3455
3647
|
/**
|
|
3456
3648
|
* Under WSL, `--login` opens the Windows host's Google Chrome when one
|
|
3457
3649
|
* is installed. Some sign-in flows reject every browser running inside WSL,
|
|
@@ -3528,6 +3720,16 @@ async function runWindowsPlainLogin(
|
|
|
3528
3720
|
await Promise.race([exited, new Promise((r) => setTimeout(r, 5000))]);
|
|
3529
3721
|
const domain = harvestDomain(url);
|
|
3530
3722
|
const { cookies, pages } = harvestWindowsChromeCookies({ chromeWindows, profileWindows, domain });
|
|
3723
|
+
try {
|
|
3724
|
+
recordLoginSync({
|
|
3725
|
+
domain,
|
|
3726
|
+
storePath,
|
|
3727
|
+
cookies,
|
|
3728
|
+
cookieDbMtimeMs: profileCookieDbMtimeMs(windowsPathToWsl(profileWindows)),
|
|
3729
|
+
});
|
|
3730
|
+
} catch {
|
|
3731
|
+
// The record only saves the next headless run a repeat read.
|
|
3732
|
+
}
|
|
3531
3733
|
const lastPage = pages[0] ?? null;
|
|
3532
3734
|
const names = [...new Set(cookies.map((cookie) => cookie.name))].sort();
|
|
3533
3735
|
const sessionCookies = cookies.filter((cookie) => cookie.expires <= 0).length;
|
package/src/commands/fetch.ts
CHANGED