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
|
@@ -359,6 +359,48 @@ export function stopWindowsChromeProfile(profileWindows, closeSeconds = 15) {
|
|
|
359
359
|
}
|
|
360
360
|
return parseStopOutput(result.stdout, profileWindows);
|
|
361
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* PowerShell that lists the Chrome processes running on the profile matched
|
|
364
|
+
* by `$Pattern` and prints their ids as a JSON array. It never closes them.
|
|
365
|
+
*/
|
|
366
|
+
export function runningScript() {
|
|
367
|
+
return [
|
|
368
|
+
"param([string]$Pattern)",
|
|
369
|
+
"$ErrorActionPreference = 'Stop'",
|
|
370
|
+
PROFILE_PROCESS_FUNCTION,
|
|
371
|
+
"$ids = @(Get-ProfileChrome | ForEach-Object { [int]$_.ProcessId })",
|
|
372
|
+
"[Console]::Out.Write((ConvertTo-Json -Compress -InputObject $ids))",
|
|
373
|
+
].join("\r\n");
|
|
374
|
+
}
|
|
375
|
+
/** Parse the running script's stdout into process ids. */
|
|
376
|
+
export function parseRunningOutput(stdout) {
|
|
377
|
+
const trimmed = stdout.trim();
|
|
378
|
+
if (!trimmed)
|
|
379
|
+
return [];
|
|
380
|
+
const parsed = JSON.parse(trimmed);
|
|
381
|
+
if (parsed === null)
|
|
382
|
+
return [];
|
|
383
|
+
return (Array.isArray(parsed) ? parsed : [parsed]).map(Number);
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Ids of the Chrome processes running on `profileWindows`, for callers that
|
|
387
|
+
* must leave an open profile alone. Throws when the check cannot run.
|
|
388
|
+
*/
|
|
389
|
+
export function windowsChromeProfileProcesses(profileWindows) {
|
|
390
|
+
const result = runPowerShellScript("profile-running", runningScript(), ["-Pattern", profileCommandLinePattern(profileWindows)], 30_000);
|
|
391
|
+
if (result.status !== 0) {
|
|
392
|
+
throw new Error(`Checking for Chrome on ${profileWindows} failed: ${(result.stderr || result.stdout || result.error?.message || "no output").trim().slice(0, 400)}`);
|
|
393
|
+
}
|
|
394
|
+
return parseRunningOutput(result.stdout);
|
|
395
|
+
}
|
|
396
|
+
/** Linux form of a drive-letter Windows path (`C:\x` becomes `/mnt/c/x`). */
|
|
397
|
+
export function windowsPathToWsl(windowsPath) {
|
|
398
|
+
const match = /^([A-Za-z]):\\(.*)$/.exec(windowsPath);
|
|
399
|
+
if (!match)
|
|
400
|
+
throw new Error(`Not a drive-letter path: ${windowsPath}`);
|
|
401
|
+
const rest = (match[2] ?? "").replace(/\\+/g, "/").replace(/\/+$/, "");
|
|
402
|
+
return `/mnt/${match[1].toLowerCase()}${rest ? `/${rest}` : ""}`;
|
|
403
|
+
}
|
|
362
404
|
/** Windows form of a `/mnt/<drive>/...` path. */
|
|
363
405
|
export function wslPathToWindows(linuxPath) {
|
|
364
406
|
const match = /^\/mnt\/([a-z])\/(.*)$/.exec(linuxPath);
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Cookie } from "../cookies/client.js";
|
|
2
|
+
import type { WindowsChromeHarvest } from "./windows-chrome.js";
|
|
3
|
+
/**
|
|
4
|
+
* Keep the shared cookie store current with the WSL sign-in profile.
|
|
5
|
+
*
|
|
6
|
+
* Under WSL, `browse --login` signs in through the Windows host's Chrome on a
|
|
7
|
+
* dedicated profile and copies that site's cookies into the cookie store when
|
|
8
|
+
* the window closes. People also keep that profile open as an ordinary
|
|
9
|
+
* browser and sign in to other sites there later. Nothing copies those
|
|
10
|
+
* sign-ins when the window is closed outside a waiting `--login` run, so a
|
|
11
|
+
* later headless run would show the site signed out.
|
|
12
|
+
*
|
|
13
|
+
* Before a headless run, this module compares the profile's cookie database
|
|
14
|
+
* modification time with the time recorded at the last copy for the run's
|
|
15
|
+
* domain. When the profile changed and no Chrome is running on it, the
|
|
16
|
+
* domain's cookies are read (the same headless read `--login` uses) and
|
|
17
|
+
* merged into the store. A profile that is open is never touched. A small
|
|
18
|
+
* owner-only state file next to the store records, per domain, the database
|
|
19
|
+
* time and a fingerprint of the cookies last read. When a read returns the
|
|
20
|
+
* same cookies as last time, nothing is merged, so cookies a headless run
|
|
21
|
+
* refreshed in the store are not replaced with the profile's older copies.
|
|
22
|
+
*/
|
|
23
|
+
export interface DomainSyncRecord {
|
|
24
|
+
/** When the profile's cookies for the domain were last read. */
|
|
25
|
+
syncedAt: string;
|
|
26
|
+
/** Cookie database modification time right after that read. */
|
|
27
|
+
cookieDbMtimeMs: number;
|
|
28
|
+
/** SHA-256 over the cookies read, so an unchanged read merges nothing. */
|
|
29
|
+
fingerprint: string;
|
|
30
|
+
/** Number of cookies read. */
|
|
31
|
+
cookies: number;
|
|
32
|
+
}
|
|
33
|
+
export interface LoginSyncState {
|
|
34
|
+
version: 1;
|
|
35
|
+
domains: Record<string, DomainSyncRecord>;
|
|
36
|
+
}
|
|
37
|
+
/** The sync-state file for a cookie store: `<store>.login-sync.json`, beside it. */
|
|
38
|
+
export declare function loginSyncStatePath(storePath: string): string;
|
|
39
|
+
export declare function readLoginSyncState(statePath: string): LoginSyncState;
|
|
40
|
+
/** Write the state file owner-only, replacing it atomically. */
|
|
41
|
+
export declare function writeLoginSyncState(statePath: string, state: LoginSyncState): void;
|
|
42
|
+
/** Order-independent fingerprint of a cookie set. Values are hashed, never stored. */
|
|
43
|
+
export declare function cookieFingerprint(cookies: Cookie[]): string;
|
|
44
|
+
export type LoginSyncDecision =
|
|
45
|
+
/** No cookie database: the profile has never been used. */
|
|
46
|
+
"no-profile"
|
|
47
|
+
/** The database has not changed since the last read for this domain. */
|
|
48
|
+
| "current"
|
|
49
|
+
/** The database is newer, but Chrome is open on the profile. */
|
|
50
|
+
| "open"
|
|
51
|
+
/** The database is newer (or the read is forced) and the profile is closed. */
|
|
52
|
+
| "sync";
|
|
53
|
+
/**
|
|
54
|
+
* Decide what to do before reading. `running` is consulted only when the
|
|
55
|
+
* database is newer, since checking for Chrome costs a PowerShell call.
|
|
56
|
+
*/
|
|
57
|
+
export declare function decideLoginSync(input: {
|
|
58
|
+
cookieDbMtimeMs: number | undefined;
|
|
59
|
+
record: DomainSyncRecord | undefined;
|
|
60
|
+
running: () => boolean;
|
|
61
|
+
force?: boolean;
|
|
62
|
+
}): LoginSyncDecision;
|
|
63
|
+
export interface LoginSyncDeps {
|
|
64
|
+
/** Newest modification time of the profile's cookie database, or `undefined`. */
|
|
65
|
+
cookieDbMtimeMs: () => number | undefined;
|
|
66
|
+
/** Ids of Chrome processes running on the profile. */
|
|
67
|
+
profileProcesses: () => number[];
|
|
68
|
+
/** Read the profile's cookies for `domain`; the profile must be closed. */
|
|
69
|
+
harvest: (domain: string) => WindowsChromeHarvest;
|
|
70
|
+
now?: () => Date;
|
|
71
|
+
}
|
|
72
|
+
export interface LoginSyncResult {
|
|
73
|
+
status: "no-profile" | "current" | "open" | "busy" | "synced" | "unchanged";
|
|
74
|
+
domain: string;
|
|
75
|
+
/** Cookies read from the profile (0 when nothing was read). */
|
|
76
|
+
cookies: number;
|
|
77
|
+
/** Cookies merged into the store (0 unless `synced`). */
|
|
78
|
+
merged: number;
|
|
79
|
+
sessionCookies: number;
|
|
80
|
+
/** Cookie names merged, sorted. Never values. */
|
|
81
|
+
names: string[];
|
|
82
|
+
/** Chrome process ids on the profile when `open`. */
|
|
83
|
+
processes: number[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Copy the profile's cookies for `domain` into the store when the profile
|
|
87
|
+
* changed since the last copy and is closed. `force` skips the time check
|
|
88
|
+
* and merges whatever is read. Throws only on read failures; the caller
|
|
89
|
+
* decides whether that is a warning.
|
|
90
|
+
*/
|
|
91
|
+
export declare function syncLoginCookies(input: {
|
|
92
|
+
domain: string;
|
|
93
|
+
storePath: string;
|
|
94
|
+
deps: LoginSyncDeps;
|
|
95
|
+
force?: boolean;
|
|
96
|
+
}): LoginSyncResult;
|
|
97
|
+
/**
|
|
98
|
+
* Record a read that happened elsewhere (the `--login` flow), so the next
|
|
99
|
+
* headless run does not read the same cookies again.
|
|
100
|
+
*/
|
|
101
|
+
export declare function recordLoginSync(input: {
|
|
102
|
+
domain: string;
|
|
103
|
+
storePath: string;
|
|
104
|
+
cookies: Cookie[];
|
|
105
|
+
cookieDbMtimeMs: number | undefined;
|
|
106
|
+
now?: Date;
|
|
107
|
+
}): void;
|
|
108
|
+
/** Newest mtime among a profile's cookie database locations, or `undefined`. */
|
|
109
|
+
export declare function profileCookieDbMtimeMs(profileLinux: string, stat?: (path: string) => number | undefined): number | undefined;
|
|
110
|
+
//# sourceMappingURL=windows-login-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windows-login-sync.d.ts","sourceRoot":"","sources":["../../../src/lib/browser/windows-login-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
|
+
import { CookieJar, mergeCookies } from "../cookies/index.js";
|
|
5
|
+
/** The sync-state file for a cookie store: `<store>.login-sync.json`, beside it. */
|
|
6
|
+
export function loginSyncStatePath(storePath) {
|
|
7
|
+
const name = basename(storePath).replace(/\.json$/i, "");
|
|
8
|
+
return join(dirname(storePath), `${name}.login-sync.json`);
|
|
9
|
+
}
|
|
10
|
+
export function readLoginSyncState(statePath) {
|
|
11
|
+
try {
|
|
12
|
+
const parsed = JSON.parse(readFileSync(statePath, "utf8"));
|
|
13
|
+
if (parsed && parsed.version === 1 && parsed.domains && typeof parsed.domains === "object") {
|
|
14
|
+
return { version: 1, domains: parsed.domains };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// Missing or unreadable state means nothing has been copied yet.
|
|
19
|
+
}
|
|
20
|
+
return { version: 1, domains: {} };
|
|
21
|
+
}
|
|
22
|
+
/** Write the state file owner-only, replacing it atomically. */
|
|
23
|
+
export function writeLoginSyncState(statePath, state) {
|
|
24
|
+
mkdirSync(dirname(statePath), { recursive: true, mode: 0o700 });
|
|
25
|
+
const tmp = `${statePath}.${process.pid}.tmp`;
|
|
26
|
+
writeFileSync(tmp, `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 });
|
|
27
|
+
renameSync(tmp, statePath);
|
|
28
|
+
}
|
|
29
|
+
/** Order-independent fingerprint of a cookie set. Values are hashed, never stored. */
|
|
30
|
+
export function cookieFingerprint(cookies) {
|
|
31
|
+
const rows = cookies
|
|
32
|
+
.map((c) => JSON.stringify([c.name, c.domain, c.path, c.value, c.expires]))
|
|
33
|
+
.sort();
|
|
34
|
+
return createHash("sha256").update(rows.join("\n")).digest("hex");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Decide what to do before reading. `running` is consulted only when the
|
|
38
|
+
* database is newer, since checking for Chrome costs a PowerShell call.
|
|
39
|
+
*/
|
|
40
|
+
export function decideLoginSync(input) {
|
|
41
|
+
if (input.cookieDbMtimeMs === undefined)
|
|
42
|
+
return "no-profile";
|
|
43
|
+
if (!input.force && input.record && input.cookieDbMtimeMs <= input.record.cookieDbMtimeMs)
|
|
44
|
+
return "current";
|
|
45
|
+
return input.running() ? "open" : "sync";
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Copy the profile's cookies for `domain` into the store when the profile
|
|
49
|
+
* changed since the last copy and is closed. `force` skips the time check
|
|
50
|
+
* and merges whatever is read. Throws only on read failures; the caller
|
|
51
|
+
* decides whether that is a warning.
|
|
52
|
+
*/
|
|
53
|
+
export function syncLoginCookies(input) {
|
|
54
|
+
const { domain, storePath, deps } = input;
|
|
55
|
+
const base = {
|
|
56
|
+
status: "current",
|
|
57
|
+
domain,
|
|
58
|
+
cookies: 0,
|
|
59
|
+
merged: 0,
|
|
60
|
+
sessionCookies: 0,
|
|
61
|
+
names: [],
|
|
62
|
+
processes: [],
|
|
63
|
+
};
|
|
64
|
+
const statePath = loginSyncStatePath(storePath);
|
|
65
|
+
const state = readLoginSyncState(statePath);
|
|
66
|
+
const record = state.domains[domain];
|
|
67
|
+
let processes = [];
|
|
68
|
+
const decision = decideLoginSync({
|
|
69
|
+
cookieDbMtimeMs: deps.cookieDbMtimeMs(),
|
|
70
|
+
record,
|
|
71
|
+
force: input.force,
|
|
72
|
+
running: () => {
|
|
73
|
+
processes = deps.profileProcesses();
|
|
74
|
+
return processes.length > 0;
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
if (decision === "no-profile")
|
|
78
|
+
return { ...base, status: "no-profile" };
|
|
79
|
+
if (decision === "current")
|
|
80
|
+
return base;
|
|
81
|
+
if (decision === "open")
|
|
82
|
+
return { ...base, status: "open", processes };
|
|
83
|
+
const release = acquireSyncLock(statePath);
|
|
84
|
+
if (!release)
|
|
85
|
+
return { ...base, status: "busy" };
|
|
86
|
+
try {
|
|
87
|
+
const { cookies } = deps.harvest(domain);
|
|
88
|
+
const fingerprint = cookieFingerprint(cookies);
|
|
89
|
+
const changed = input.force || !record || record.fingerprint !== fingerprint;
|
|
90
|
+
let merged = [];
|
|
91
|
+
if (changed && cookies.length > 0) {
|
|
92
|
+
const jar = new CookieJar({ path: storePath, source: "harn-browse" });
|
|
93
|
+
jar.save(mergeCookies(jar.load(), cookies));
|
|
94
|
+
merged = cookies;
|
|
95
|
+
}
|
|
96
|
+
const after = readLoginSyncState(statePath);
|
|
97
|
+
after.domains[domain] = {
|
|
98
|
+
syncedAt: (deps.now?.() ?? new Date()).toISOString(),
|
|
99
|
+
cookieDbMtimeMs: deps.cookieDbMtimeMs() ?? 0,
|
|
100
|
+
fingerprint,
|
|
101
|
+
cookies: cookies.length,
|
|
102
|
+
};
|
|
103
|
+
writeLoginSyncState(statePath, after);
|
|
104
|
+
return {
|
|
105
|
+
...base,
|
|
106
|
+
status: merged.length > 0 ? "synced" : "unchanged",
|
|
107
|
+
cookies: cookies.length,
|
|
108
|
+
merged: merged.length,
|
|
109
|
+
sessionCookies: merged.filter((c) => c.expires <= 0).length,
|
|
110
|
+
names: [...new Set(merged.map((c) => c.name))].sort(),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
release();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Record a read that happened elsewhere (the `--login` flow), so the next
|
|
119
|
+
* headless run does not read the same cookies again.
|
|
120
|
+
*/
|
|
121
|
+
export function recordLoginSync(input) {
|
|
122
|
+
const statePath = loginSyncStatePath(input.storePath);
|
|
123
|
+
const state = readLoginSyncState(statePath);
|
|
124
|
+
state.domains[input.domain] = {
|
|
125
|
+
syncedAt: (input.now ?? new Date()).toISOString(),
|
|
126
|
+
cookieDbMtimeMs: input.cookieDbMtimeMs ?? 0,
|
|
127
|
+
fingerprint: cookieFingerprint(input.cookies),
|
|
128
|
+
cookies: input.cookies.length,
|
|
129
|
+
};
|
|
130
|
+
writeLoginSyncState(statePath, state);
|
|
131
|
+
}
|
|
132
|
+
/** Newest mtime among a profile's cookie database locations, or `undefined`. */
|
|
133
|
+
export function profileCookieDbMtimeMs(profileLinux, stat = fileMtimeMs) {
|
|
134
|
+
const times = [
|
|
135
|
+
join(profileLinux, "Default", "Network", "Cookies"),
|
|
136
|
+
join(profileLinux, "Default", "Cookies"),
|
|
137
|
+
]
|
|
138
|
+
.map((path) => stat(path))
|
|
139
|
+
.filter((t) => t !== undefined);
|
|
140
|
+
return times.length > 0 ? Math.max(...times) : undefined;
|
|
141
|
+
}
|
|
142
|
+
function fileMtimeMs(path) {
|
|
143
|
+
try {
|
|
144
|
+
return statSync(path).mtimeMs;
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const LOCK_STALE_MS = 3 * 60_000;
|
|
151
|
+
/**
|
|
152
|
+
* One read at a time: a second headless Chrome on the profile would refuse
|
|
153
|
+
* to start. A lock older than three minutes is treated as abandoned.
|
|
154
|
+
*/
|
|
155
|
+
function acquireSyncLock(statePath) {
|
|
156
|
+
const lockPath = `${statePath}.lock`;
|
|
157
|
+
mkdirSync(dirname(lockPath), { recursive: true, mode: 0o700 });
|
|
158
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
159
|
+
try {
|
|
160
|
+
closeSync(openSync(lockPath, "wx", 0o600));
|
|
161
|
+
return () => rmSync(lockPath, { force: true });
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
const age = existsSync(lockPath) ? Date.now() - (fileMtimeMs(lockPath) ?? 0) : 0;
|
|
165
|
+
if (age < LOCK_STALE_MS)
|
|
166
|
+
return null;
|
|
167
|
+
rmSync(lockPath, { force: true });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
@@ -71,9 +71,9 @@ export declare class CookieJar {
|
|
|
71
71
|
private stamp;
|
|
72
72
|
private update;
|
|
73
73
|
/**
|
|
74
|
-
* Read + filter helper. Drops expired cookies.
|
|
75
|
-
* uses
|
|
76
|
-
*
|
|
74
|
+
* Read + filter helper. Drops expired cookies. The optional domain filter
|
|
75
|
+
* uses `domainMatches` (equal host or any subdomain); it is a query, not a
|
|
76
|
+
* delivery rule. `header(url)` uses the stricter `cookieSendsToHost`.
|
|
77
77
|
*/
|
|
78
78
|
list(filter?: {
|
|
79
79
|
domain?: string;
|
|
@@ -129,12 +129,37 @@ export declare class CookieJar {
|
|
|
129
129
|
* `(name, domain, path)` triple replace older ones. Pure; caller must save.
|
|
130
130
|
*/
|
|
131
131
|
export declare function mergeCookies(store: CookieStore, incoming: Cookie[]): CookieStore;
|
|
132
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Filter-style domain match used by `list({ domain })` and `clear({ domain })`:
|
|
134
|
+
* a cookie domain "matches" a hostname when they are equal or the hostname is
|
|
135
|
+
* a subdomain of it, whether or not the cookie is host-only. Never use this to
|
|
136
|
+
* decide whether a cookie may be SENT to a host; use `cookieSendsToHost`.
|
|
137
|
+
*/
|
|
133
138
|
export declare function domainMatches(cookieDomain: string, hostname: string): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* RFC 6265 delivery rule. A cookie domain with a leading dot is a domain
|
|
141
|
+
* cookie and goes to the domain and every subdomain. A cookie domain without
|
|
142
|
+
* one is host-only (how CDP, Playwright and `Set-Cookie` without `Domain`
|
|
143
|
+
* record it) and goes to that exact host and nowhere else.
|
|
144
|
+
*/
|
|
145
|
+
export declare function cookieSendsToHost(cookieDomain: string, hostname: string): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Cookie-prefix rules (RFC 6265bis). `__Secure-` needs Secure; `__Host-` also
|
|
148
|
+
* needs Path=/ and no Domain attribute, which a jar records as a host-only
|
|
149
|
+
* domain (no leading dot). Browsers refuse a violating cookie, so the jar
|
|
150
|
+
* never stores one and the browser injector never hands one to Chromium.
|
|
151
|
+
*/
|
|
152
|
+
export declare function violatesCookiePrefix(cookie: Pick<Cookie, "name" | "domain" | "path" | "secure">): boolean;
|
|
153
|
+
/** True for hosts a browser treats as a secure context without TLS. */
|
|
154
|
+
export declare function isLoopbackHost(hostname: string): boolean;
|
|
134
155
|
/** RFC 6265 path match. */
|
|
135
156
|
export declare function pathMatches(cookiePath: string, urlPath: string): boolean;
|
|
136
157
|
/** `expires <= 0` is a session cookie / no expiry; otherwise compare to now. */
|
|
137
158
|
export declare function isExpired(cookie: Cookie): boolean;
|
|
138
|
-
/**
|
|
159
|
+
/**
|
|
160
|
+
* Cookies that should be sent with a request to `url`: exact-host delivery for
|
|
161
|
+
* host-only cookies, domain delivery for dotted ones, then path and Secure.
|
|
162
|
+
* Secure cookies go over HTTPS and to loopback hosts, as in browsers.
|
|
163
|
+
*/
|
|
139
164
|
export declare function matchingCookies(store: CookieStore, url: string): Cookie[];
|
|
140
165
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/cookies/client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/cookies/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -189,9 +189,9 @@ export class CookieJar {
|
|
|
189
189
|
});
|
|
190
190
|
}
|
|
191
191
|
/**
|
|
192
|
-
* Read + filter helper. Drops expired cookies.
|
|
193
|
-
* uses
|
|
194
|
-
*
|
|
192
|
+
* Read + filter helper. Drops expired cookies. The optional domain filter
|
|
193
|
+
* uses `domainMatches` (equal host or any subdomain); it is a query, not a
|
|
194
|
+
* delivery rule. `header(url)` uses the stricter `cookieSendsToHost`.
|
|
195
195
|
*/
|
|
196
196
|
list(filter = {}) {
|
|
197
197
|
const store = this.load();
|
|
@@ -349,15 +349,56 @@ export function mergeCookies(store, incoming) {
|
|
|
349
349
|
map.set(key(c), c);
|
|
350
350
|
return { ...store, cookies: Array.from(map.values()) };
|
|
351
351
|
}
|
|
352
|
-
/**
|
|
352
|
+
/**
|
|
353
|
+
* Filter-style domain match used by `list({ domain })` and `clear({ domain })`:
|
|
354
|
+
* a cookie domain "matches" a hostname when they are equal or the hostname is
|
|
355
|
+
* a subdomain of it, whether or not the cookie is host-only. Never use this to
|
|
356
|
+
* decide whether a cookie may be SENT to a host; use `cookieSendsToHost`.
|
|
357
|
+
*/
|
|
353
358
|
export function domainMatches(cookieDomain, hostname) {
|
|
354
|
-
const cd = cookieDomain.startsWith(".") ? cookieDomain.slice(1) : cookieDomain;
|
|
355
|
-
|
|
359
|
+
const cd = (cookieDomain.startsWith(".") ? cookieDomain.slice(1) : cookieDomain).toLowerCase();
|
|
360
|
+
const host = hostname.toLowerCase();
|
|
361
|
+
if (host === cd)
|
|
356
362
|
return true;
|
|
357
|
-
if (
|
|
363
|
+
if (host.endsWith(`.${cd}`))
|
|
358
364
|
return true;
|
|
359
365
|
return false;
|
|
360
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* RFC 6265 delivery rule. A cookie domain with a leading dot is a domain
|
|
369
|
+
* cookie and goes to the domain and every subdomain. A cookie domain without
|
|
370
|
+
* one is host-only (how CDP, Playwright and `Set-Cookie` without `Domain`
|
|
371
|
+
* record it) and goes to that exact host and nowhere else.
|
|
372
|
+
*/
|
|
373
|
+
export function cookieSendsToHost(cookieDomain, hostname) {
|
|
374
|
+
const host = hostname.toLowerCase();
|
|
375
|
+
if (cookieDomain.startsWith("."))
|
|
376
|
+
return domainMatches(cookieDomain, host);
|
|
377
|
+
return cookieDomain.toLowerCase() === host;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Cookie-prefix rules (RFC 6265bis). `__Secure-` needs Secure; `__Host-` also
|
|
381
|
+
* needs Path=/ and no Domain attribute, which a jar records as a host-only
|
|
382
|
+
* domain (no leading dot). Browsers refuse a violating cookie, so the jar
|
|
383
|
+
* never stores one and the browser injector never hands one to Chromium.
|
|
384
|
+
*/
|
|
385
|
+
export function violatesCookiePrefix(cookie) {
|
|
386
|
+
if (cookie.name.startsWith("__Secure-") && !cookie.secure)
|
|
387
|
+
return true;
|
|
388
|
+
if (cookie.name.startsWith("__Host-") &&
|
|
389
|
+
(!cookie.secure || cookie.domain.startsWith(".") || cookie.path !== "/")) {
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
/** True for hosts a browser treats as a secure context without TLS. */
|
|
395
|
+
export function isLoopbackHost(hostname) {
|
|
396
|
+
const host = hostname.toLowerCase();
|
|
397
|
+
return (host === "localhost" ||
|
|
398
|
+
host.endsWith(".localhost") ||
|
|
399
|
+
host === "[::1]" ||
|
|
400
|
+
/^127(?:\.\d{1,3}){3}$/.test(host));
|
|
401
|
+
}
|
|
361
402
|
/** RFC 6265 path match. */
|
|
362
403
|
export function pathMatches(cookiePath, urlPath) {
|
|
363
404
|
if (urlPath === cookiePath)
|
|
@@ -374,14 +415,18 @@ export function isExpired(cookie) {
|
|
|
374
415
|
return false;
|
|
375
416
|
return cookie.expires < Date.now() / 1000;
|
|
376
417
|
}
|
|
377
|
-
/**
|
|
418
|
+
/**
|
|
419
|
+
* Cookies that should be sent with a request to `url`: exact-host delivery for
|
|
420
|
+
* host-only cookies, domain delivery for dotted ones, then path and Secure.
|
|
421
|
+
* Secure cookies go over HTTPS and to loopback hosts, as in browsers.
|
|
422
|
+
*/
|
|
378
423
|
export function matchingCookies(store, url) {
|
|
379
424
|
const parsed = new URL(url);
|
|
380
|
-
const isSecure = parsed.protocol === "https:";
|
|
425
|
+
const isSecure = parsed.protocol === "https:" || isLoopbackHost(parsed.hostname);
|
|
381
426
|
return store.cookies.filter((c) => {
|
|
382
427
|
if (isExpired(c))
|
|
383
428
|
return false;
|
|
384
|
-
if (!
|
|
429
|
+
if (!cookieSendsToHost(c.domain, parsed.hostname))
|
|
385
430
|
return false;
|
|
386
431
|
if (!pathMatches(c.path, parsed.pathname))
|
|
387
432
|
return false;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { type Cookie, CookieJar, type CookieJarOptions, type CookieStore, CookieStoreParseError, domainMatches, type InfoResult, isExpired, matchingCookies, mergeCookies, type OriginEntry, pathMatches, } from "./client.js";
|
|
1
|
+
export { type Cookie, CookieJar, type CookieJarOptions, type CookieStore, CookieStoreParseError, cookieSendsToHost, domainMatches, type InfoResult, isExpired, isLoopbackHost, matchingCookies, mergeCookies, type OriginEntry, pathMatches, violatesCookiePrefix, } from "./client.js";
|
|
2
2
|
export { applyExtraCookies, type ExtraCookies } from "./extra.js";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/cookies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,qBAAqB,EACrB,aAAa,EACb,KAAK,UAAU,EACf,SAAS,EACT,eAAe,EACf,YAAY,EACZ,KAAK,WAAW,EAChB,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/cookies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,KAAK,UAAU,EACf,SAAS,EACT,cAAc,EACd,eAAe,EACf,YAAY,EACZ,KAAK,WAAW,EAChB,WAAW,EACX,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { CookieJar, CookieStoreParseError, domainMatches, isExpired, matchingCookies, mergeCookies, pathMatches, } from "./client.js";
|
|
1
|
+
export { CookieJar, CookieStoreParseError, cookieSendsToHost, domainMatches, isExpired, isLoopbackHost, matchingCookies, mergeCookies, pathMatches, violatesCookiePrefix, } from "./client.js";
|
|
2
2
|
export { applyExtraCookies } from "./extra.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docs-metadata-sync.d.ts","sourceRoot":"","sources":["../../src/lib/docs-metadata-sync.ts"],"names":[],"mappings":"AAYA,wBAAgB,2BAA2B,CAAC,IAAI,EAAE;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B,GAAG,IAAI,CAGP;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IACtD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"docs-metadata-sync.d.ts","sourceRoot":"","sources":["../../src/lib/docs-metadata-sync.ts"],"names":[],"mappings":"AAYA,wBAAgB,2BAA2B,CAAC,IAAI,EAAE;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B,GAAG,IAAI,CAGP;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IACtD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgHD,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAiLhC"}
|
|
@@ -81,6 +81,30 @@ function addDays(value, days) {
|
|
|
81
81
|
date.setUTCDate(date.getUTCDate() + days);
|
|
82
82
|
return date.toISOString().replace(".000Z", "Z");
|
|
83
83
|
}
|
|
84
|
+
/** Allowance for clock skew between machines before a timestamp counts as future. */
|
|
85
|
+
const FUTURE_SKEW_MS = 10 * 60 * 1000;
|
|
86
|
+
/** Lifecycle timestamps record events that already happened, so none may be later than now. */
|
|
87
|
+
const PAST_TIMESTAMP_FIELDS = [
|
|
88
|
+
"created_at",
|
|
89
|
+
"updated_at",
|
|
90
|
+
"status_changed_at",
|
|
91
|
+
"resolved_at",
|
|
92
|
+
"reviewed_at",
|
|
93
|
+
];
|
|
94
|
+
function inFuture(value, now) {
|
|
95
|
+
if (typeof value !== "string")
|
|
96
|
+
return false;
|
|
97
|
+
const time = Date.parse(value);
|
|
98
|
+
return Number.isFinite(time) && time > Date.parse(now) + FUTURE_SKEW_MS;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* While a status is unchanged its status_changed_at is pinned to the committed
|
|
102
|
+
* value. A committed value later than now cannot be true, so a correction to a
|
|
103
|
+
* time that has already passed is accepted instead of being reverted.
|
|
104
|
+
*/
|
|
105
|
+
function correctsFutureStatusTime(committed, current, now) {
|
|
106
|
+
return inFuture(committed, now) && typeof current === "string" && !inFuture(current, now);
|
|
107
|
+
}
|
|
84
108
|
export async function runDocsMetadataSync(opts) {
|
|
85
109
|
if (opts.cached && !opts.check) {
|
|
86
110
|
throw new Error("--cached requires --check because Git index validation is read-only");
|
|
@@ -133,7 +157,8 @@ export async function runDocsMetadataSync(opts) {
|
|
|
133
157
|
}
|
|
134
158
|
if (!statusChanged &&
|
|
135
159
|
beforeV2 &&
|
|
136
|
-
current.data.status_changed_at !== beforeV2.status_changed_at
|
|
160
|
+
current.data.status_changed_at !== beforeV2.status_changed_at &&
|
|
161
|
+
!correctsFutureStatusTime(beforeV2.status_changed_at, current.data.status_changed_at, now)) {
|
|
137
162
|
expected.status_changed_at = beforeV2.status_changed_at;
|
|
138
163
|
fields.push("status_changed_at");
|
|
139
164
|
}
|
|
@@ -164,6 +189,19 @@ export async function runDocsMetadataSync(opts) {
|
|
|
164
189
|
continue;
|
|
165
190
|
}
|
|
166
191
|
if (opts.check) {
|
|
192
|
+
const future = PAST_TIMESTAMP_FIELDS.filter((field) => inFuture(current.data[field], now));
|
|
193
|
+
if (future.length > 0) {
|
|
194
|
+
rows.push({
|
|
195
|
+
repo: target.name,
|
|
196
|
+
path,
|
|
197
|
+
status: "invalid",
|
|
198
|
+
fields: [...future],
|
|
199
|
+
message: future
|
|
200
|
+
.map((field) => `${field}: cannot be later than the current time`)
|
|
201
|
+
.join("; "),
|
|
202
|
+
});
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
167
205
|
const validation = validateDocsMetadataV2(current.data);
|
|
168
206
|
if (!validation.valid) {
|
|
169
207
|
rows.push({
|
|
@@ -196,7 +234,8 @@ export async function runDocsMetadataSync(opts) {
|
|
|
196
234
|
}
|
|
197
235
|
if (beforeV2 &&
|
|
198
236
|
!statusChanged &&
|
|
199
|
-
current.data.status_changed_at !== beforeV2.status_changed_at
|
|
237
|
+
current.data.status_changed_at !== beforeV2.status_changed_at &&
|
|
238
|
+
!correctsFutureStatusTime(beforeV2.status_changed_at, current.data.status_changed_at, now)) {
|
|
200
239
|
drift.push("status_changed_at");
|
|
201
240
|
}
|
|
202
241
|
if (opts.reviewed &&
|
|
@@ -25,6 +25,14 @@ export interface DurableJobDocument {
|
|
|
25
25
|
/** Directory the wrapped command runs in. */
|
|
26
26
|
cwd: string;
|
|
27
27
|
created_at: string;
|
|
28
|
+
/**
|
|
29
|
+
* Wait while Linux memory pressure is above this before queueing, as the
|
|
30
|
+
* foreground run does. The client resolves it from the resource's project config at launch.
|
|
31
|
+
*/
|
|
32
|
+
defer_on_pressure?: {
|
|
33
|
+
psi_full_avg10: number;
|
|
34
|
+
max_wait_seconds: number;
|
|
35
|
+
};
|
|
28
36
|
}
|
|
29
37
|
export interface DurableJobStatus {
|
|
30
38
|
schema_version: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durable-job.d.ts","sourceRoot":"","sources":["../../src/lib/durable-job.ts"],"names":[],"mappings":"AA4BA,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C,sEAAsE;AACtE,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C;iDACiD;AACjD,eAAO,MAAM,8BAA8B,SAAU,CAAC;AAEtD,eAAO,MAAM,6BAA6B,aAAa,CAAC;AACxD,eAAO,MAAM,2BAA2B,gBAAgB,CAAC;AACzD,eAAO,MAAM,wBAAwB,YAAY,CAAC;AAIlD,wEAAwE;AACxE,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;AAE/E,qEAAqE;AACrE,MAAM,MAAM,uBAAuB,GAAG,eAAe,GAAG,MAAM,CAAC;AAI/D,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"durable-job.d.ts","sourceRoot":"","sources":["../../src/lib/durable-job.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/durable-job.js
CHANGED
|
@@ -101,8 +101,18 @@ export function readJobDocument(jobDir) {
|
|
|
101
101
|
: [],
|
|
102
102
|
cwd: typeof parsed.cwd === "string" ? parsed.cwd : "",
|
|
103
103
|
created_at: typeof parsed.created_at === "string" ? parsed.created_at : "",
|
|
104
|
+
...readDeferOnPressure(parsed.defer_on_pressure),
|
|
104
105
|
};
|
|
105
106
|
}
|
|
107
|
+
function readDeferOnPressure(value) {
|
|
108
|
+
if (!isRecord(value))
|
|
109
|
+
return {};
|
|
110
|
+
const threshold = value.psi_full_avg10;
|
|
111
|
+
const maxWait = value.max_wait_seconds;
|
|
112
|
+
return typeof threshold === "number" && typeof maxWait === "number" && maxWait > 0
|
|
113
|
+
? { defer_on_pressure: { psi_full_avg10: threshold, max_wait_seconds: maxWait } }
|
|
114
|
+
: {};
|
|
115
|
+
}
|
|
106
116
|
/** Read the status document, or null when it is missing or unusable. */
|
|
107
117
|
export function readJobStatus(jobDir) {
|
|
108
118
|
const path = join(resolve(jobDir), DURABLE_JOB_STATUS_FILENAME);
|