@reticlehq/server 3.8.2 → 3.10.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/LICENSE +201 -104
- package/NOTICE +5 -0
- package/README.md +1 -1
- package/SKILL.md +13 -12
- package/bin/launch.js +13 -0
- package/bin/package.json +3 -0
- package/bin/reticle.js +66 -8
- package/dist/chromium-launch-options.d.ts +25 -0
- package/dist/chromium-launch-options.js +21 -1
- package/dist/command/cli/adhoc-suite.js +2 -0
- package/dist/command/cli/adhoc-verdict.d.ts +2 -43
- package/dist/command/cli/adhoc-verdict.js +50 -160
- package/dist/command/cli/answers/gate-exit.d.ts +6 -2
- package/dist/command/cli/answers/gate-exit.js +12 -2
- package/dist/command/cli/answers/mcp-connection-hint.d.ts +2 -0
- package/dist/command/cli/answers/mcp-connection-hint.js +6 -0
- package/dist/command/cli/ci-hint.d.ts +44 -0
- package/dist/command/cli/ci-hint.js +72 -0
- package/dist/command/cli/cli-doctor.d.ts +6 -1
- package/dist/command/cli/cli-doctor.js +46 -5
- package/dist/command/cli/cli-flow-commands.d.ts +7 -1
- package/dist/command/cli/cli-flow-commands.js +39 -24
- package/dist/command/cli/cli-kill.d.ts +21 -0
- package/dist/command/cli/cli-kill.js +47 -4
- package/dist/command/cli/cli-parse-compare.d.ts +22 -0
- package/dist/command/cli/cli-parse-compare.js +75 -0
- package/dist/command/cli/cli-parse-grade.d.ts +36 -0
- package/dist/command/cli/cli-parse-grade.js +75 -0
- package/dist/command/cli/cli-parse-grammar.d.ts +2 -0
- package/dist/command/cli/cli-parse-grammar.js +18 -20
- package/dist/command/cli/cli-parse-verify.d.ts +10 -0
- package/dist/command/cli/cli-parse-verify.js +63 -14
- package/dist/command/cli/cli-parse.d.ts +22 -17
- package/dist/command/cli/cli-parse.js +54 -32
- package/dist/command/cli/cli-update-commands.d.ts +1 -1
- package/dist/command/cli/cli-update-commands.js +29 -20
- package/dist/command/cli/cli-usage.js +61 -31
- package/dist/command/cli/cli-verify.d.ts +17 -3
- package/dist/command/cli/cli-verify.js +59 -21
- package/dist/command/cli/cloud-cli.js +2 -119
- package/dist/command/cli/daemon-link/daemon-client.d.ts +45 -0
- package/dist/command/cli/daemon-link/daemon-client.js +205 -0
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +6 -1
- package/dist/command/cli/doctor/browser/chromium-hint.js +11 -5
- package/dist/command/cli/doctor/doctor-daemon-line.js +6 -3
- package/dist/command/cli/doctor/doctor-fix.d.ts +44 -0
- package/dist/command/cli/doctor/doctor-fix.js +89 -0
- package/dist/command/cli/doctor/doctor-launcher-line.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-launcher-line.js +60 -0
- package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
- package/dist/command/cli/doctor/doctor-rows.js +4 -0
- package/dist/command/cli/doctor-repairs.d.ts +64 -0
- package/dist/command/cli/doctor-repairs.js +214 -0
- package/dist/command/cli/drive/drive-command.js +3 -7
- package/dist/command/cli/ensure-daemon.d.ts +26 -0
- package/dist/command/cli/ensure-daemon.js +57 -0
- package/dist/command/cli/finished-run/grade-command.d.ts +23 -0
- package/dist/command/cli/finished-run/grade-command.js +180 -0
- package/dist/command/cli/first-use-checks.d.ts +41 -0
- package/dist/command/cli/first-use-checks.js +127 -0
- package/dist/command/cli/first-verdict-node.d.ts +3 -0
- package/dist/command/cli/first-verdict-node.js +189 -0
- package/dist/command/cli/first-verdict.d.ts +126 -0
- package/dist/command/cli/first-verdict.js +267 -0
- package/dist/command/cli/launch/sdk-version-match.d.ts +3 -2
- package/dist/command/cli/launch/sdk-version-match.js +3 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +1 -1
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +7 -6
- package/dist/command/cli/mcp-command.js +13 -4
- package/dist/command/cli/ports/port-holder.d.ts +11 -0
- package/dist/command/cli/ports/port-holder.js +36 -4
- package/dist/command/cli/setup-mcp-cli.d.ts +27 -1
- package/dist/command/cli/setup-mcp-cli.js +98 -23
- package/dist/command/cli/status/status-lines.d.ts +2 -0
- package/dist/command/cli/status/status-lines.js +72 -17
- package/dist/command/cli/status-fields.d.ts +5 -1
- package/dist/command/cli/status-fields.js +24 -6
- package/dist/command/cli/try-command.d.ts +7 -0
- package/dist/command/cli/try-command.js +11 -3
- package/dist/command/cli/two-builds/compare-command.d.ts +48 -0
- package/dist/command/cli/two-builds/compare-command.js +314 -0
- package/dist/command/cli/verify-against.d.ts +61 -0
- package/dist/command/cli/verify-against.js +187 -0
- package/dist/command/cli/verify-no-session.d.ts +2 -0
- package/dist/command/cli/verify-no-session.js +13 -0
- package/dist/command/cli/verify-results.d.ts +35 -2
- package/dist/command/cli/verify-results.js +76 -10
- package/dist/command/cli.js +112 -70
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +3 -1
- package/dist/command/daemon/binding/daemon-status-probe.js +4 -2
- package/dist/command/daemon/daemon-argv.d.ts +9 -0
- package/dist/command/daemon/daemon-argv.js +11 -0
- package/dist/command/daemon/daemon-resolve.d.ts +8 -1
- package/dist/command/daemon/daemon-resolve.js +27 -1
- package/dist/command/daemon/daemon.d.ts +22 -4
- package/dist/command/daemon/daemon.js +52 -10
- package/dist/command/daemon/lifetime/daemon-exit.d.ts +24 -0
- package/dist/command/daemon/lifetime/daemon-exit.js +39 -0
- package/dist/command/daemon/lifetime/death-cause.d.ts +15 -0
- package/dist/command/daemon/lifetime/death-cause.js +149 -0
- package/dist/command/daemon/lifetime/heartbeat.d.ts +7 -15
- package/dist/command/daemon/lifetime/heartbeat.js +11 -15
- package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
- package/dist/command/daemon/lifetime/startup-failure.js +62 -0
- package/dist/command/daemon/port-state.d.ts +14 -0
- package/dist/command/daemon/port-state.js +31 -0
- package/dist/command/setup/agent-configs.d.ts +2 -0
- package/dist/command/setup/agent-configs.js +42 -15
- package/dist/command/setup/agent-next-steps.d.ts +77 -0
- package/dist/command/setup/agent-next-steps.js +284 -0
- package/dist/command/setup/agent-writer.js +11 -6
- package/dist/command/setup/bringup/dev-server-files.d.ts +9 -0
- package/dist/command/setup/bringup/dev-server-files.js +17 -0
- package/dist/command/setup/bringup/static-page-server.d.ts +14 -2
- package/dist/command/setup/bringup/static-page-server.js +43 -9
- package/dist/command/setup/init/init-host.js +2 -0
- package/dist/command/setup/init/init-runtime.d.ts +6 -1
- package/dist/command/setup/init/init-runtime.js +63 -21
- package/dist/command/setup/mcp-launcher.d.ts +3 -0
- package/dist/command/setup/mcp-launcher.js +11 -0
- package/dist/command/setup/node-effects.d.ts +3 -2
- package/dist/command/setup/node-effects.js +9 -3
- package/dist/command/setup/remaining-steps.d.ts +8 -0
- package/dist/command/setup/remaining-steps.js +10 -0
- package/dist/command/setup/run-setup.d.ts +2 -0
- package/dist/command/setup/run-setup.js +9 -1
- package/dist/command/setup/setup-command.js +36 -15
- package/dist/command/setup/setup-mcp.d.ts +8 -0
- package/dist/command/setup/setup-mcp.js +47 -20
- package/dist/command/setup/start-dev-server.d.ts +8 -0
- package/dist/command/setup/start-dev-server.js +43 -0
- package/dist/command/update/updater.js +6 -0
- package/dist/command/version/identity/server-version.d.ts +2 -0
- package/dist/command/version/identity/server-version.js +2 -0
- package/dist/command/version/version-skew.d.ts +7 -0
- package/dist/command/version/version-skew.js +14 -3
- package/dist/features/compare/action-plan.d.ts +260 -0
- package/dist/features/compare/action-plan.js +543 -0
- package/dist/features/compare/behaviour-compare.d.ts +24 -0
- package/dist/features/compare/behaviour-compare.js +85 -0
- package/dist/features/compare/behaviour-diff.d.ts +97 -0
- package/dist/features/compare/behaviour-diff.js +202 -0
- package/dist/features/compare/behaviour-report.d.ts +40 -0
- package/dist/features/compare/behaviour-report.js +128 -0
- package/dist/features/compare/build-readings.d.ts +106 -0
- package/dist/features/compare/build-readings.js +250 -0
- package/dist/features/compare/compare-diff.d.ts +62 -0
- package/dist/features/compare/compare-diff.js +177 -0
- package/dist/features/compare/compare-report-html.d.ts +6 -0
- package/dist/features/compare/compare-report-html.js +102 -0
- package/dist/features/compare/compare-report.d.ts +15 -0
- package/dist/features/compare/compare-report.js +73 -0
- package/dist/features/compare/compare-run.d.ts +60 -0
- package/dist/features/compare/compare-run.js +123 -0
- package/dist/features/compare/outside/effect-lines.d.ts +26 -0
- package/dist/features/compare/outside/effect-lines.js +103 -0
- package/dist/features/compare/outside/state-sources.d.ts +76 -0
- package/dist/features/compare/outside/state-sources.js +164 -0
- package/dist/features/compare/outside/value-dictionary.d.ts +51 -0
- package/dist/features/compare/outside/value-dictionary.js +227 -0
- package/dist/features/compare/visible-content.d.ts +20 -0
- package/dist/features/compare/visible-content.js +125 -0
- package/dist/features/crawl/crawl-empty.js +2 -1
- package/dist/features/crawl/crawl-exhaustive.d.ts +55 -0
- package/dist/features/crawl/crawl-exhaustive.js +138 -0
- package/dist/features/crawl/crawl-tools.d.ts +9 -2
- package/dist/features/crawl/crawl-tools.js +28 -12
- package/dist/features/crawl/crawl.d.ts +8 -9
- package/dist/features/crawl/crawl.js +61 -65
- package/dist/features/exhaust/app-coverage.d.ts +5 -1
- package/dist/features/exhaust/app-coverage.js +3 -1
- package/dist/features/exhaust/code-coverage.js +8 -4
- package/dist/features/exhaust/edge-cases.d.ts +133 -0
- package/dist/features/exhaust/edge-cases.js +310 -0
- package/dist/features/exhaust/edges/edge-drive.d.ts +42 -0
- package/dist/features/exhaust/edges/edge-drive.js +364 -0
- package/dist/features/exhaust/edges/edge-run.d.ts +59 -0
- package/dist/features/exhaust/edges/edge-run.js +169 -0
- package/dist/features/exhaust/explorer.d.ts +87 -14
- package/dist/features/exhaust/explorer.js +159 -25
- package/dist/features/exhaust/failure-branches.d.ts +5 -0
- package/dist/features/exhaust/failure-branches.js +29 -11
- package/dist/features/exhaust/invariants.d.ts +112 -0
- package/dist/features/exhaust/invariants.js +188 -0
- package/dist/features/exhaust/ledger.d.ts +68 -0
- package/dist/features/exhaust/ledger.js +121 -13
- package/dist/features/exhaust/session-fold.d.ts +10 -0
- package/dist/features/exhaust/session-fold.js +46 -3
- package/dist/features/exhaust/session-port.d.ts +3 -1
- package/dist/features/exhaust/session-port.js +97 -28
- package/dist/features/grade/grade-answer.d.ts +99 -0
- package/dist/features/grade/grade-answer.js +112 -0
- package/dist/features/grade/grade-noise.d.ts +27 -0
- package/dist/features/grade/grade-noise.js +55 -0
- package/dist/features/grade/grade-renumber.d.ts +12 -0
- package/dist/features/grade/grade-renumber.js +86 -0
- package/dist/features/grade/grade-state.d.ts +50 -0
- package/dist/features/grade/grade-state.js +93 -0
- package/dist/features/grade/grade-suggest.d.ts +33 -0
- package/dist/features/grade/grade-suggest.js +98 -0
- package/dist/features/license/license.d.ts +11 -71
- package/dist/features/license/license.js +43 -136
- package/dist/features/visual/desktop-capture.d.ts +33 -0
- package/dist/features/visual/desktop-capture.js +94 -0
- package/dist/features/visual/step-shots.js +139 -35
- package/dist/features/visual/visual-tools.d.ts +0 -18
- package/dist/features/visual/visual-tools.js +67 -99
- package/dist/import-graph.js +5 -6
- package/dist/index.d.ts +3 -1
- package/dist/index.js +19 -7
- package/dist/judgement/capsule/causal-summary.js +9 -2
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +13 -1
- package/dist/judgement/runs/artifact/replay-mapping.js +1 -1
- package/dist/judgement/runs/reticle-runner.js +16 -2
- package/dist/judgement/runs/run-ledger.d.ts +7 -5
- package/dist/judgement/runs/run-ledger.js +98 -69
- package/dist/judgement/runs/runner-port.d.ts +2 -0
- package/dist/judgement/runs/runner-port.js +23 -5
- package/dist/judgement/runs/verification-sync.d.ts +6 -3
- package/dist/judgement/runs/verification-sync.js +21 -10
- package/dist/language/flows/affected-tools.d.ts +1 -1
- package/dist/language/flows/affected-tools.js +49 -30
- package/dist/language/flows/change/git-changed.d.ts +2 -1
- package/dist/language/flows/change/git-changed.js +19 -2
- package/dist/language/flows/fields/flow-secret-field.d.ts +1 -13
- package/dist/language/flows/fields/flow-secret-field.js +77 -21
- package/dist/language/flows/flow-anchor.d.ts +60 -2
- package/dist/language/flows/flow-anchor.js +125 -20
- package/dist/language/flows/flow-classify.d.ts +20 -0
- package/dist/language/flows/flow-classify.js +90 -12
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +25 -1
- package/dist/language/flows/flow-journey.d.ts +38 -4
- package/dist/language/flows/flow-journey.js +138 -8
- package/dist/language/flows/flow-replay-run.d.ts +0 -16
- package/dist/language/flows/flow-replay-run.js +76 -14
- package/dist/language/flows/flow-replay-types.d.ts +4 -2
- package/dist/language/flows/flow-replay.d.ts +19 -1
- package/dist/language/flows/flow-replay.js +169 -52
- package/dist/language/flows/flow-replayable.js +7 -1
- package/dist/language/flows/flow-start-path.d.ts +34 -5
- package/dist/language/flows/flow-start-path.js +202 -15
- package/dist/language/flows/flow-step-runners.d.ts +14 -13
- package/dist/language/flows/flow-step-runners.js +184 -109
- package/dist/language/flows/flow-store-for-session.d.ts +26 -25
- package/dist/language/flows/flow-store-for-session.js +51 -5
- package/dist/language/flows/flow-tools.js +46 -33
- package/dist/language/flows/flows.js +176 -76
- package/dist/language/flows/recording/tape/recordings.d.ts +21 -16
- package/dist/language/flows/recording/tape/recordings.js +112 -49
- package/dist/language/flows/replay.d.ts +36 -12
- package/dist/language/flows/replay.js +193 -50
- package/dist/language/flows/steps/page-steps.d.ts +68 -0
- package/dist/language/flows/steps/page-steps.js +198 -0
- package/dist/language/flows/suite/sequential-run.d.ts +68 -0
- package/dist/language/flows/suite/sequential-run.js +96 -0
- package/dist/language/flows/verify-change-tools.js +26 -15
- package/dist/launch-chromium.d.ts +1 -0
- package/dist/launch-chromium.js +49 -12
- package/dist/memory/cloud/background-sync.d.ts +13 -0
- package/dist/memory/cloud/background-sync.js +24 -3
- package/dist/memory/cloud/sync-daemon.js +26 -27
- package/dist/memory/cloud/sync-screenshots.js +6 -1
- package/dist/memory/journal/attach-journal.js +2 -1
- package/dist/memory/journal/drive-flow.d.ts +7 -0
- package/dist/memory/journal/drive-flow.js +44 -13
- package/dist/memory/journal/journal-recorder.d.ts +4 -2
- package/dist/memory/journal/session-journal.d.ts +7 -0
- package/dist/memory/journal/session-journal.js +58 -10
- package/dist/memory/project/root-claims.d.ts +9 -0
- package/dist/memory/project/root-claims.js +9 -4
- package/dist/portal/bridge/bridge.d.ts +13 -4
- package/dist/portal/bridge/bridge.js +10 -9
- package/dist/portal/input/download-capture.d.ts +7 -0
- package/dist/portal/input/download-capture.js +92 -0
- package/dist/portal/input/network-detail.d.ts +6 -0
- package/dist/portal/input/network-detail.js +12 -1
- package/dist/portal/input/network-mock-tools.js +49 -13
- package/dist/portal/input/network-mock.d.ts +8 -0
- package/dist/portal/input/network-mock.js +30 -10
- package/dist/portal/input/page-capture.d.ts +24 -0
- package/dist/portal/input/page-capture.js +33 -0
- package/dist/portal/input/real-input.d.ts +35 -18
- package/dist/portal/input/real-input.js +99 -33
- package/dist/portal/input/viewport-tools.d.ts +8 -0
- package/dist/portal/input/viewport-tools.js +81 -21
- package/dist/portal/pool/browser-pool.d.ts +16 -30
- package/dist/portal/pool/browser-pool.js +64 -52
- package/dist/portal/pool/context-permissions.d.ts +34 -17
- package/dist/portal/pool/context-permissions.js +94 -1
- package/dist/portal/pool/csp-widen.d.ts +62 -0
- package/dist/portal/pool/csp-widen.js +132 -0
- package/dist/portal/pool/determinism.d.ts +52 -0
- package/dist/portal/pool/determinism.js +259 -0
- package/dist/portal/pool/lease-emulation.d.ts +34 -1
- package/dist/portal/pool/lease-emulation.js +18 -0
- package/dist/portal/pool/lease-marker-stamp.d.ts +6 -0
- package/dist/portal/pool/lease-marker-stamp.js +15 -1
- package/dist/portal/pool/lease-page-error.d.ts +56 -0
- package/dist/portal/pool/lease-page-error.js +160 -0
- package/dist/portal/pool/page-error-journal.d.ts +63 -0
- package/dist/portal/pool/page-error-journal.js +208 -0
- package/dist/portal/pool/playwright-launcher.js +71 -15
- package/dist/portal/pool/pool-contract.d.ts +49 -1
- package/dist/portal/pool/pool-lease.d.ts +70 -2
- package/dist/portal/pool/pool-lease.js +23 -0
- package/dist/portal/pool/zero-install.d.ts +8 -0
- package/dist/portal/pool/zero-install.js +91 -1
- package/dist/portal/session/dev-server/dev-command.d.ts +11 -2
- package/dist/portal/session/dev-server/dev-command.js +110 -16
- package/dist/portal/session/dev-server/dev-server-probe.d.ts +7 -0
- package/dist/portal/session/dev-server/dev-server-probe.js +9 -0
- package/dist/portal/session/dev-server/project-ports.d.ts +21 -0
- package/dist/portal/session/dev-server/project-ports.js +62 -0
- package/dist/portal/session/facts/departed-network.d.ts +66 -0
- package/dist/portal/session/facts/departed-network.js +142 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +9 -0
- package/dist/portal/session/no-session-diagnosis.js +76 -27
- package/dist/portal/session/no-session-next-action.d.ts +14 -0
- package/dist/portal/session/no-session-next-action.js +56 -7
- package/dist/portal/session/no-session-watch.d.ts +6 -0
- package/dist/portal/session/no-session-watch.js +41 -4
- package/dist/portal/session/page-commands/undeclared-command.d.ts +9 -3
- package/dist/portal/session/page-commands/undeclared-command.js +16 -7
- package/dist/portal/session/recorded-gaps.d.ts +1 -1
- package/dist/portal/session/recorded-gaps.js +5 -2
- package/dist/portal/session/serving-directory.d.ts +10 -0
- package/dist/portal/session/serving-directory.js +39 -8
- package/dist/portal/session/session-manager.d.ts +10 -0
- package/dist/portal/session/session-manager.js +211 -81
- package/dist/portal/session/session.js +4 -3
- package/dist/portal/session/zero-install-first-call.d.ts +20 -1
- package/dist/portal/session/zero-install-first-call.js +34 -2
- package/dist/start-wiring.d.ts +30 -2
- package/dist/start-wiring.js +74 -9
- package/dist/surface/mcp/mcp-proxy.js +10 -2
- package/dist/surface/mcp/mcp.js +8 -5
- package/dist/surface/mcp/peer/agents-connected.d.ts +6 -0
- package/dist/surface/mcp/peer/agents-connected.js +58 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +17 -5
- package/dist/surface/mcp/shared-params.d.ts +2 -0
- package/dist/surface/mcp/shared-params.js +31 -0
- package/dist/surface/tools/act/act-observation.d.ts +40 -2
- package/dist/surface/tools/act/act-observation.js +118 -12
- package/dist/surface/tools/act/act-preflight.js +38 -18
- package/dist/surface/tools/act/act-sequence-retry.d.ts +23 -2
- package/dist/surface/tools/act/act-sequence-retry.js +61 -5
- package/dist/surface/tools/act/act-target.js +3 -1
- package/dist/surface/tools/act/after-match.d.ts +10 -0
- package/dist/surface/tools/act/after-match.js +40 -0
- package/dist/surface/tools/act/already-true.js +1 -1
- package/dist/surface/tools/act/navigation/reload-result.d.ts +12 -1
- package/dist/surface/tools/act/navigation/reload-result.js +25 -2
- package/dist/surface/tools/act/resolve-target.d.ts +17 -1
- package/dist/surface/tools/act/resolve-target.js +59 -9
- package/dist/surface/tools/act/sequence-grade.d.ts +15 -1
- package/dist/surface/tools/act/sequence-grade.js +45 -27
- package/dist/surface/tools/act/settle-in-flight.js +27 -15
- package/dist/surface/tools/act-command.d.ts +23 -0
- package/dist/surface/tools/act-command.js +50 -0
- package/dist/surface/tools/act-sequence-tool.js +81 -50
- package/dist/surface/tools/act-tools.d.ts +1 -13
- package/dist/surface/tools/act-tools.js +83 -95
- package/dist/surface/tools/agent/build-staleness.d.ts +1 -1
- package/dist/surface/tools/agent/build-staleness.js +1 -1
- package/dist/surface/tools/assert/assert-verdict.js +18 -12
- package/dist/surface/tools/browser-tools.js +87 -13
- package/dist/surface/tools/compare-tools.d.ts +11 -0
- package/dist/surface/tools/compare-tools.js +94 -0
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.js +11 -3
- package/dist/surface/tools/empty-session-list.d.ts +2 -0
- package/dist/surface/tools/empty-session-list.js +4 -2
- package/dist/surface/tools/error-recovery.d.ts +12 -1
- package/dist/surface/tools/error-recovery.js +131 -40
- package/dist/surface/tools/explore-tools.js +1 -1
- package/dist/surface/tools/invoke-tool.js +29 -21
- package/dist/surface/tools/lease-coverage-tools.d.ts +49 -0
- package/dist/surface/tools/lease-coverage-tools.js +128 -0
- package/dist/surface/tools/lease-hint.d.ts +42 -1
- package/dist/surface/tools/lease-hint.js +52 -3
- package/dist/surface/tools/lease-readiness.d.ts +47 -0
- package/dist/surface/tools/lease-readiness.js +126 -0
- package/dist/surface/tools/lease-seed.d.ts +16 -1
- package/dist/surface/tools/lease-seed.js +37 -4
- package/dist/surface/tools/lease-tools.d.ts +3 -41
- package/dist/surface/tools/lease-tools.js +104 -124
- package/dist/surface/tools/lease-url-identity.d.ts +46 -0
- package/dist/surface/tools/lease-url-identity.js +97 -0
- package/dist/surface/tools/merge-tools.js +99 -2
- package/dist/surface/tools/merged-name-redirect.js +5 -1
- package/dist/surface/tools/observe-tools.js +15 -9
- package/dist/surface/tools/read/console-pre-attach.d.ts +19 -0
- package/dist/surface/tools/read/console-pre-attach.js +23 -0
- package/dist/surface/tools/read-tools.js +33 -30
- package/dist/surface/tools/real-input-attempt.d.ts +0 -8
- package/dist/surface/tools/real-input-attempt.js +143 -28
- package/dist/surface/tools/repeat-hint.d.ts +40 -0
- package/dist/surface/tools/repeat-hint.js +171 -0
- package/dist/surface/tools/run-report.d.ts +2 -2
- package/dist/surface/tools/run-report.js +27 -15
- package/dist/surface/tools/session-ports.d.ts +14 -0
- package/dist/surface/tools/session-ports.js +53 -0
- package/dist/surface/tools/tool-kit.d.ts +18 -0
- package/dist/surface/tools/tool-kit.js +9 -3
- package/dist/surface/tools/tool-surface.js +0 -1
- package/dist/surface/tools/tools.js +54 -18
- package/dist/surface/tools/type-spelling.d.ts +13 -0
- package/dist/surface/tools/type-spelling.js +36 -0
- package/dist/telemetry/agent-client.d.ts +6 -0
- package/dist/telemetry/agent-client.js +10 -0
- package/dist/telemetry/argument-shape.js +17 -7
- package/dist/telemetry/cli-telemetry.d.ts +10 -1
- package/dist/telemetry/cli-telemetry.js +15 -0
- package/dist/telemetry/init-telemetry.d.ts +2 -1
- package/dist/telemetry/init-telemetry.js +47 -2
- package/dist/telemetry/install-trace.js +23 -4
- package/dist/telemetry/onboarding-funnel.d.ts +19 -2
- package/dist/telemetry/onboarding-funnel.js +25 -3
- package/dist/telemetry/proxy-telemetry.d.ts +2 -1
- package/dist/telemetry/proxy-telemetry.js +2 -2
- package/dist/telemetry/session-metrics.d.ts +1 -1
- package/dist/telemetry/session-metrics.js +67 -27
- package/dist/telemetry/telemetry.d.ts +3 -1
- package/dist/telemetry/telemetry.js +60 -28
- package/dist/telemetry/tool-refused.d.ts +6 -3
- package/dist/telemetry/tool-refused.js +15 -3
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +1 -1
- package/docs/architecture.md +3 -6
- package/docs/capabilities.mdx +61 -1
- package/docs/cli/affected.mdx +9 -7
- package/docs/cli/cloud.mdx +3 -3
- package/docs/cli/compare.mdx +94 -0
- package/docs/cli/doctor.mdx +41 -2
- package/docs/cli/grade.mdx +97 -0
- package/docs/cli/help.mdx +4 -2
- package/docs/cli/license.mdx +13 -12
- package/docs/cli/restart.mdx +7 -5
- package/docs/cli/rollback.mdx +5 -5
- package/docs/cli/setup.mdx +13 -2
- package/docs/cli/status.mdx +4 -0
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/update.mdx +2 -2
- package/docs/cli/verify.mdx +23 -1
- package/docs/cli.mdx +34 -24
- package/docs/deploy-checks.md +25 -0
- package/docs/desktop.mdx +1 -1
- package/docs/enterprise.md +17 -22
- package/docs/environment.mdx +1 -0
- package/docs/first-drive.md +1 -1
- package/docs/flows.md +11 -1
- package/docs/frameworks.mdx +9 -9
- package/docs/getting-started.md +1 -1
- package/docs/install-manual.mdx +16 -1
- package/docs/license-activation.mdx +3 -3
- package/docs/packages/engine.mdx +1 -1
- package/docs/packages/server.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/test.mdx +1 -1
- package/docs/packages.mdx +1 -1
- package/docs/platform-integration.md +1 -1
- package/docs/predicates.mdx +1 -1
- package/docs/quickstart.mdx +68 -49
- package/docs/skill-setup.md +1 -1
- package/docs/skill.md +14 -13
- package/docs/telemetry.md +6 -4
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/verify.mdx +4 -1
- package/docs/troubleshooting.mdx +1 -1
- package/docs/usage.md +17 -14
- package/package.json +18 -14
- package/LICENSE-ENTERPRISE +0 -37
- package/dist/features/ee/audit-log.d.ts +0 -14
- package/dist/features/ee/audit-log.js +0 -11
- package/dist/features/exhaust/exhaust-run.d.ts +0 -33
- package/dist/features/exhaust/exhaust-run.js +0 -82
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `reticle_lease { action: "coverage" }` — which code of a leased page ran, as the JS engine counted it.
|
|
3
|
+
*
|
|
4
|
+
* A caller outside the app cannot see which branches a drive reached. The JS engine can: each script's block
|
|
5
|
+
* ranges carry a count. This reads them from the lease's own Playwright page, the way
|
|
6
|
+
* `reticle_network_mock` reaches it, so it needs no change to the app.
|
|
7
|
+
*
|
|
8
|
+
* A take returns what ran since the previous take, and coverage keeps running across a reload.
|
|
9
|
+
* Start it on acquire (`jsCoverage: true`) to see the code that runs while the page loads; a start
|
|
10
|
+
* after the load misses it.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import { ReticleTool, asNumber, asString } from '@reticlehq/core';
|
|
14
|
+
import { sessionIdShape } from './tool-kit.js';
|
|
15
|
+
export const LeaseCoverageMode = { START: 'start', TAKE: 'take' };
|
|
16
|
+
/** Ranges one take returns when the caller sets no cap. */
|
|
17
|
+
export const DEFAULT_MAX_COVERAGE_RANGES = 20_000;
|
|
18
|
+
const MAX_COVERAGE_RANGES = 200_000;
|
|
19
|
+
const NOT_A_LEASE = 'reticle_lease { action: "coverage" } reads a leased tab only. Acquire one with reticle_lease { action: "acquire", url, jsCoverage: true }, then pass its sessionId.';
|
|
20
|
+
const CANNOT_MEASURE = 'This leased page cannot measure JS coverage. Coverage needs Chromium, and a page that is still open.';
|
|
21
|
+
const WAS_NOT_RUNNING = 'Coverage was not running, so this take holds nothing. It runs now: do the work, then take again. To see the code that runs while the page loads, acquire with jsCoverage: true.';
|
|
22
|
+
/**
|
|
23
|
+
* Flatten each script's functions into one range list, keep the scripts `urlContains` names, and
|
|
24
|
+
* stop at `maxRanges`. What the cap drops is counted in `truncated`, beside the value.
|
|
25
|
+
*/
|
|
26
|
+
export function flattenCoverage(entries, maxRanges, urlContains) {
|
|
27
|
+
const kept = entries.filter((e) => urlContains === undefined || e.url.includes(urlContains));
|
|
28
|
+
const scripts = [];
|
|
29
|
+
let rangeCount = 0;
|
|
30
|
+
let droppedRanges = 0;
|
|
31
|
+
let droppedScripts = 0;
|
|
32
|
+
for (const entry of kept) {
|
|
33
|
+
const ranges = entry.functions.flatMap((f) => f.ranges);
|
|
34
|
+
const room = maxRanges - rangeCount;
|
|
35
|
+
if (room <= 0 && 0 < ranges.length) {
|
|
36
|
+
droppedScripts += 1;
|
|
37
|
+
droppedRanges += ranges.length;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const taken = ranges
|
|
41
|
+
.slice(0, Math.max(0, room))
|
|
42
|
+
.map(({ startOffset, endOffset, count }) => ({ startOffset, endOffset, count }));
|
|
43
|
+
droppedRanges += ranges.length - taken.length;
|
|
44
|
+
rangeCount += taken.length;
|
|
45
|
+
scripts.push({ url: entry.url, ranges: taken });
|
|
46
|
+
}
|
|
47
|
+
const filteredOut = entries.length - kept.length;
|
|
48
|
+
return {
|
|
49
|
+
scripts,
|
|
50
|
+
scriptCount: scripts.length,
|
|
51
|
+
rangeCount,
|
|
52
|
+
...(0 < filteredOut ? { filteredOut } : {}),
|
|
53
|
+
...(0 < droppedRanges
|
|
54
|
+
? { truncated: { scripts: droppedScripts, ranges: droppedRanges, maxRanges } }
|
|
55
|
+
: {}),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** The acquire argument that starts coverage before the page's first script. */
|
|
59
|
+
export const LEASE_JS_COVERAGE_ARG = z
|
|
60
|
+
.boolean()
|
|
61
|
+
.optional()
|
|
62
|
+
.describe('Start JS coverage before the page runs its first script, so reticle_lease { action: "coverage" } also counts the code that runs while the page loads.');
|
|
63
|
+
export const LEASE_COVERAGE_TOOL = {
|
|
64
|
+
name: ReticleTool.LEASE_COVERAGE,
|
|
65
|
+
description: 'JS coverage of a leased tab: which code ran. { mode: "start" } starts it; { mode: "take" } (the default) returns, for each script, its url and block ranges { startOffset, endOffset, count } since the previous take. A count of 0 is code that did not run. Acquire with jsCoverage: true to include the code that runs while the page loads. Use urlContains to keep only your own scripts; a take that hits maxRanges says what it dropped in `truncated`.',
|
|
66
|
+
example: { mode: 'take', urlContains: '/src/' },
|
|
67
|
+
inputSchema: {
|
|
68
|
+
mode: z
|
|
69
|
+
.enum([LeaseCoverageMode.START, LeaseCoverageMode.TAKE])
|
|
70
|
+
.optional()
|
|
71
|
+
.describe('"start" starts coverage; "take" (default) returns what ran since the previous take.'),
|
|
72
|
+
urlContains: z
|
|
73
|
+
.string()
|
|
74
|
+
.min(1)
|
|
75
|
+
.optional()
|
|
76
|
+
.describe('Keep only the scripts whose url contains this text, for example "/src/".'),
|
|
77
|
+
maxRanges: z
|
|
78
|
+
.number()
|
|
79
|
+
.int()
|
|
80
|
+
.min(1)
|
|
81
|
+
.max(MAX_COVERAGE_RANGES)
|
|
82
|
+
.optional()
|
|
83
|
+
.describe(`The most ranges one take returns. Default ${String(DEFAULT_MAX_COVERAGE_RANGES)}.`),
|
|
84
|
+
...sessionIdShape,
|
|
85
|
+
},
|
|
86
|
+
outputSchema: {
|
|
87
|
+
running: z.boolean().describe('Whether coverage runs on the page after this call.'),
|
|
88
|
+
scripts: z
|
|
89
|
+
.array(z.object({
|
|
90
|
+
url: z.string(),
|
|
91
|
+
ranges: z.array(z.object({ startOffset: z.number(), endOffset: z.number(), count: z.number() })),
|
|
92
|
+
}))
|
|
93
|
+
.optional(),
|
|
94
|
+
scriptCount: z.number().optional(),
|
|
95
|
+
rangeCount: z.number().optional(),
|
|
96
|
+
filteredOut: z
|
|
97
|
+
.number()
|
|
98
|
+
.optional()
|
|
99
|
+
.describe('Scripts left out because their url did not contain urlContains.'),
|
|
100
|
+
truncated: z
|
|
101
|
+
.object({ scripts: z.number(), ranges: z.number(), maxRanges: z.number() })
|
|
102
|
+
.optional()
|
|
103
|
+
.describe('Present when the take hit maxRanges: the scripts and ranges it dropped. Absent means nothing was dropped.'),
|
|
104
|
+
hint: z.string().optional(),
|
|
105
|
+
},
|
|
106
|
+
handler: async (deps, args) => {
|
|
107
|
+
const session = deps.sessions.resolve(asString(args['sessionId']));
|
|
108
|
+
const coverage = deps.pool?.coverageOf(session.id);
|
|
109
|
+
if (coverage === undefined) {
|
|
110
|
+
throw new Error(NOT_A_LEASE);
|
|
111
|
+
}
|
|
112
|
+
if (LeaseCoverageMode.START === args['mode']) {
|
|
113
|
+
if (!(await coverage.start())) {
|
|
114
|
+
throw new Error(CANNOT_MEASURE);
|
|
115
|
+
}
|
|
116
|
+
return { running: true };
|
|
117
|
+
}
|
|
118
|
+
const entries = await coverage.take();
|
|
119
|
+
if (entries === undefined) {
|
|
120
|
+
if (!(await coverage.start())) {
|
|
121
|
+
throw new Error(CANNOT_MEASURE);
|
|
122
|
+
}
|
|
123
|
+
return { running: true, scripts: [], scriptCount: 0, rangeCount: 0, hint: WAS_NOT_RUNNING };
|
|
124
|
+
}
|
|
125
|
+
const maxRanges = asNumber(args['maxRanges']) ?? DEFAULT_MAX_COVERAGE_RANGES;
|
|
126
|
+
return { running: true, ...flattenCoverage(entries, maxRanges, asString(args['urlContains'])) };
|
|
127
|
+
},
|
|
128
|
+
};
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* So: rank by what the daemon actually knows, and fall back to the differential only when it knows
|
|
18
18
|
* nothing. A cause we hold positive evidence against is not printed at all.
|
|
19
19
|
*/
|
|
20
|
-
import
|
|
20
|
+
import { LeaseNotReadyReason } from '@reticlehq/core';
|
|
21
|
+
import { HeldLeaseReload, type LeaseReaderFailure } from './lease-readiness.js';
|
|
21
22
|
/** What the daemon can say for certain at the moment a lease comes back unconnected. */
|
|
22
23
|
export interface LeaseEvidence {
|
|
23
24
|
/** A dial WAS made and turned away, with the reason. The only certain cause there is. */
|
|
@@ -56,6 +57,13 @@ export interface LeaseEvidence {
|
|
|
56
57
|
*/
|
|
57
58
|
dialledUrl?: string;
|
|
58
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* The last sentence of every not-connected hint. It is true for an MCP caller, which keeps the
|
|
62
|
+
* lease. The CLI releases the lease itself, and puts `LEASE_RELEASED_SENTENCE` in its place.
|
|
63
|
+
*/
|
|
64
|
+
export declare const LEASE_KEPT_SENTENCE = "The tab stays leased. Release it with reticle_lease{action:\"release\"}.";
|
|
65
|
+
/** The sentence for a lease that is already released. */
|
|
66
|
+
export declare const LEASE_RELEASED_SENTENCE = "The lease is released.";
|
|
59
67
|
export declare function leaseNotConnectedHint(url: string, port: number, evidence?: LeaseEvidence): string;
|
|
60
68
|
/**
|
|
61
69
|
* The leased page was still loading when the wait ran out, so nothing has failed yet: a slow first
|
|
@@ -65,3 +73,36 @@ export declare function leaseNotConnectedHint(url: string, port: number, evidenc
|
|
|
65
73
|
export declare function leaseStillLoadingHint(url: string, waitedMs: number): string;
|
|
66
74
|
/** The hint for a lease whose zero-install reader did not run (#1701). The lease is already released. */
|
|
67
75
|
export declare function leaseReaderFailedHint(url: string, failure: LeaseReaderFailure): string;
|
|
76
|
+
/** What `reticle_lease` answers for a held lease that it loaded again and that did not connect. */
|
|
77
|
+
export interface KeptLeaseAnswer {
|
|
78
|
+
sessionId: string;
|
|
79
|
+
url: string;
|
|
80
|
+
ready: false;
|
|
81
|
+
notReadyReason: LeaseNotReadyReason;
|
|
82
|
+
reused: true;
|
|
83
|
+
expiresInMs: number;
|
|
84
|
+
leased: number;
|
|
85
|
+
queued: number;
|
|
86
|
+
hint: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The answer for a held lease that is kept although its page did not connect (#1924). The tab is
|
|
90
|
+
* kept, because its context holds the cookies: a new context starts signed out. `reload` says
|
|
91
|
+
* whether its page loaded again; `cause` is why it did not connect, or why it did not load.
|
|
92
|
+
*/
|
|
93
|
+
export declare function keptLeaseAnswer(lease: {
|
|
94
|
+
sessionId: string;
|
|
95
|
+
origin: string;
|
|
96
|
+
url: string;
|
|
97
|
+
reload: HeldLeaseReload;
|
|
98
|
+
}, pool: {
|
|
99
|
+
leaseTtlMs(): number;
|
|
100
|
+
activeCount(): number;
|
|
101
|
+
queuedCount(): number;
|
|
102
|
+
}, cause: string): KeptLeaseAnswer;
|
|
103
|
+
/**
|
|
104
|
+
* Why a held lease's page did not load again, said from what the daemon can check: whether
|
|
105
|
+
* anything accepts a connection on the page's port. Only asked of a loopback URL, where the probe
|
|
106
|
+
* reaches the same server the browser does.
|
|
107
|
+
*/
|
|
108
|
+
export declare function notLoadedCause(url: string, portAccepts: ((port: number) => Promise<boolean>) | undefined): Promise<string>;
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* nothing. A cause we hold positive evidence against is not printed at all.
|
|
19
19
|
*/
|
|
20
20
|
import { isLoopbackHostname, LeaseNotReadyReason } from '@reticlehq/core';
|
|
21
|
+
import { HeldLeaseReload } from './lease-readiness.js';
|
|
21
22
|
import { INIT_COMMAND } from '../../portal/session/no-session-next-action.js';
|
|
22
23
|
const NUXT = 'nuxt';
|
|
23
24
|
const PORT_DIFFERENTIAL = "The usual cause is a PORT MISMATCH: the app's SDK connects to the port it was BUILT with, so a " +
|
|
@@ -37,7 +38,14 @@ const REAL_CAUSES = 'Causes that produce exactly this and are NOT a port problem
|
|
|
37
38
|
const NUXT_FIRST = 'This project is Nuxt: the most likely cause by a distance is a dev server that predates the ' +
|
|
38
39
|
'Reticle plugin, because Nuxt does NOT register a newly added plugin on HMR. Restart `nuxt dev` ' +
|
|
39
40
|
'and re-acquire before investigating anything else.';
|
|
40
|
-
|
|
41
|
+
/**
|
|
42
|
+
* The last sentence of every not-connected hint. It is true for an MCP caller, which keeps the
|
|
43
|
+
* lease. The CLI releases the lease itself, and puts `LEASE_RELEASED_SENTENCE` in its place.
|
|
44
|
+
*/
|
|
45
|
+
export const LEASE_KEPT_SENTENCE = 'The tab stays leased. Release it with reticle_lease{action:"release"}.';
|
|
46
|
+
const RELEASE = LEASE_KEPT_SENTENCE;
|
|
47
|
+
/** The sentence for a lease that is already released. */
|
|
48
|
+
export const LEASE_RELEASED_SENTENCE = 'The lease is released.';
|
|
41
49
|
const PRODUCTION_STUB = ' If this is a production build (`vite build`, `next build`, `next start`, or a deployed site), it strips ' +
|
|
42
50
|
'Reticle or replaces it with an inert stub by design; point the lease at the dev server instead.';
|
|
43
51
|
function isRemoteUrl(url) {
|
|
@@ -168,8 +176,49 @@ export function leaseStillLoadingHint(url, waitedMs) {
|
|
|
168
176
|
/** The hint for a lease whose zero-install reader did not run (#1701). The lease is already released. */
|
|
169
177
|
export function leaseReaderFailedHint(url, failure) {
|
|
170
178
|
return failure.reason === LeaseNotReadyReason.NOT_HTML_DOCUMENT
|
|
171
|
-
? `${url} is not an HTML page, so no SDK can connect to it.
|
|
179
|
+
? `${url} is not an HTML page, so no SDK can connect to it. ${LEASE_RELEASED_SENTENCE} ` +
|
|
172
180
|
'Acquire an HTML page of the app instead.'
|
|
173
|
-
: `The zero-install reader failed on ${url}: ${failure.cause}.
|
|
181
|
+
: `The zero-install reader failed on ${url}: ${failure.cause}. ${LEASE_RELEASED_SENTENCE} ` +
|
|
174
182
|
'If the tab closed during setup, acquire again.';
|
|
175
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* The answer for a held lease that is kept although its page did not connect (#1924). The tab is
|
|
186
|
+
* kept, because its context holds the cookies: a new context starts signed out. `reload` says
|
|
187
|
+
* whether its page loaded again; `cause` is why it did not connect, or why it did not load.
|
|
188
|
+
*/
|
|
189
|
+
export function keptLeaseAnswer(lease, pool, cause) {
|
|
190
|
+
const reloaded = lease.reload === HeldLeaseReload.RELOADED;
|
|
191
|
+
const what = reloaded
|
|
192
|
+
? 'Acquire loaded its page again, but the page did not connect.'
|
|
193
|
+
: `Acquire could not load ${lease.url} in it again: ${cause}`;
|
|
194
|
+
return {
|
|
195
|
+
sessionId: lease.sessionId,
|
|
196
|
+
url: lease.url,
|
|
197
|
+
ready: false,
|
|
198
|
+
notReadyReason: LeaseNotReadyReason.SDK_NEVER_DIALLED,
|
|
199
|
+
reused: true,
|
|
200
|
+
expiresInMs: pool.leaseTtlMs(),
|
|
201
|
+
leased: pool.activeCount(),
|
|
202
|
+
queued: pool.queuedCount(),
|
|
203
|
+
hint: `You already hold a lease on ${lease.origin}: ${lease.sessionId}. ${what} The tab and its ` +
|
|
204
|
+
'context are kept, so its cookies and sign-in are kept. A new context has no cookies: ' +
|
|
205
|
+
'release this lease first only if you want one, and pass seedStorage to start it signed in.' +
|
|
206
|
+
(reloaded ? ` ${cause}` : ' Acquire again when the app answers.'),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Why a held lease's page did not load again, said from what the daemon can check: whether
|
|
211
|
+
* anything accepts a connection on the page's port. Only asked of a loopback URL, where the probe
|
|
212
|
+
* reaches the same server the browser does.
|
|
213
|
+
*/
|
|
214
|
+
export async function notLoadedCause(url, portAccepts) {
|
|
215
|
+
const target = URL.canParse(url) ? new URL(url) : undefined;
|
|
216
|
+
if (portAccepts === undefined || target === undefined || !isLoopbackHostname(target.hostname)) {
|
|
217
|
+
return 'the page did not load. The app may be restarting.';
|
|
218
|
+
}
|
|
219
|
+
const defaultPort = 'https:' === target.protocol ? '443' : '80';
|
|
220
|
+
const port = '' === target.port ? defaultPort : target.port;
|
|
221
|
+
return (await portAccepts(Number(port)).catch(() => false))
|
|
222
|
+
? `the server on port ${port} accepted the connection, but the page did not finish loading. It may still be compiling.`
|
|
223
|
+
: `nothing accepts connections on port ${port}. The app may be restarting.`;
|
|
224
|
+
}
|
|
@@ -43,6 +43,27 @@ export declare function notReadyReason(answered: boolean, session: unknown, fail
|
|
|
43
43
|
* failure than the one being fixed.
|
|
44
44
|
*/
|
|
45
45
|
export declare function probeLeaseAlive(session: ProbeableSession | undefined, timeoutMs?: number): Promise<boolean>;
|
|
46
|
+
/**
|
|
47
|
+
* The longest `reticle_lease { action: "acquire" }` waits for its tab to be ready, when the caller
|
|
48
|
+
* gives no `timeout_ms`. It is the longest wait before this bound existed: the connect wait, the
|
|
49
|
+
* wait after the reader, and the loading grace rounds. So no acquire that was ready before is now
|
|
50
|
+
* refused.
|
|
51
|
+
*/
|
|
52
|
+
export declare const LEASE_READY_TIMEOUT_MS: number;
|
|
53
|
+
/**
|
|
54
|
+
* A connect wait for `connectOrInject` that ends at the deadline. Each wait is one connect wait at
|
|
55
|
+
* most, so the reader still goes in at the same time. A wait that starts after the deadline looks
|
|
56
|
+
* once and does not sleep.
|
|
57
|
+
*/
|
|
58
|
+
export declare function connectWaitUntil(deadline: number, now: () => number, sleeper?: (ms: number) => Promise<void>): (isConnected: () => boolean) => Promise<boolean>;
|
|
59
|
+
/**
|
|
60
|
+
* Wait until the leased tab is registered, answers a probe and is visible, or the deadline passes.
|
|
61
|
+
*
|
|
62
|
+
* A row in the sessions map is not proof that the next call works (#1650): the tab can be attached
|
|
63
|
+
* and answer nothing. So `ready` waits for a reply. Returns the reason at the deadline, or
|
|
64
|
+
* `undefined` when the tab is ready. It always looks once, also when the deadline has passed.
|
|
65
|
+
*/
|
|
66
|
+
export declare function awaitLeaseAnswering(registered: () => ProbeableSession | undefined, deadline: number, now: () => number, sleeper?: (ms: number) => Promise<void>): Promise<LeaseNotReadyReason | undefined>;
|
|
46
67
|
/**
|
|
47
68
|
* Poll until the leased tab's SDK has actually registered on the bridge, so the sessionId we return
|
|
48
69
|
* is immediately usable (the page navigates first, then connects a moment later — without this wait
|
|
@@ -90,4 +111,30 @@ export declare function moveReusedLease(tabUrl: () => string | undefined, reques
|
|
|
90
111
|
url: string;
|
|
91
112
|
arrived: boolean;
|
|
92
113
|
} | undefined>;
|
|
114
|
+
/** What loading a held lease's tab again found. */
|
|
115
|
+
export declare const HeldLeaseReload: {
|
|
116
|
+
/** Its tab had already registered, so nothing was loaded again. */
|
|
117
|
+
readonly REGISTERED: "registered";
|
|
118
|
+
/** The page loaded again. Its SDK may or may not have dialled in since. */
|
|
119
|
+
readonly RELOADED: "reloaded";
|
|
120
|
+
/** The page is still open, but it did not load: the server did not answer, or it timed out. */
|
|
121
|
+
readonly NOT_LOADED: "not-loaded";
|
|
122
|
+
/** The page is closed, or its context is gone. Nothing is left to keep. */
|
|
123
|
+
readonly GONE: "gone";
|
|
124
|
+
};
|
|
125
|
+
export type HeldLeaseReload = (typeof HeldLeaseReload)[keyof typeof HeldLeaseReload];
|
|
126
|
+
/**
|
|
127
|
+
* Load a held lease's tab again in its own context, and wait for its SDK to dial in (#1924).
|
|
128
|
+
*
|
|
129
|
+
* A held lease whose tab never registered used to be released, and a new context minted. That
|
|
130
|
+
* context has no cookies, so a tab the agent had signed in to was closed and replaced by a
|
|
131
|
+
* signed-out one, and the retry could not get the sign-in back. The context is the sign-in, so the
|
|
132
|
+
* retry keeps it and only loads the page again. The reader the first acquire injected is an init
|
|
133
|
+
* script of that page, so it runs again on the new document by itself.
|
|
134
|
+
*
|
|
135
|
+
* A navigation that fails is not a closed tab: a dev server that restarts refuses the connection
|
|
136
|
+
* for a moment, and the context with its cookies is still there. `isOpen` asks the page something,
|
|
137
|
+
* which tells the two apart. A tab that has registered already is left alone.
|
|
138
|
+
*/
|
|
139
|
+
export declare function reconnectHeldLease(goto: (timeoutMs: number) => Promise<boolean>, isOpen: () => Promise<boolean>, isLoading: () => Promise<boolean>, isRegistered: () => boolean, deadline: number, now: () => number): Promise<HeldLeaseReload>;
|
|
93
140
|
export {};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { LeaseNotReadyReason, ReticleCommand, RETICLE_URL_PARAM } from '@reticlehq/core';
|
|
8
8
|
import { NotHtmlDocumentError } from '../../portal/pool/zero-install.js';
|
|
9
|
+
import { waitForReconnect } from '../../portal/session/session-reconnect.js';
|
|
9
10
|
/**
|
|
10
11
|
* How long a liveness probe waits for the tab to say anything at all.
|
|
11
12
|
*
|
|
@@ -74,6 +75,52 @@ const LEASE_READY_POLL_MS = 100;
|
|
|
74
75
|
/** Extra readiness waits allowed while the leased page is still loading (each one is ~10 s). */
|
|
75
76
|
const LEASE_LOADING_GRACE_ROUNDS = 3;
|
|
76
77
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
78
|
+
/** One connect wait: how long a tab gets to dial in before the zero-install reader goes in. */
|
|
79
|
+
const LEASE_CONNECT_WAIT_MS = LEASE_READY_ATTEMPTS * LEASE_READY_POLL_MS;
|
|
80
|
+
/**
|
|
81
|
+
* The longest `reticle_lease { action: "acquire" }` waits for its tab to be ready, when the caller
|
|
82
|
+
* gives no `timeout_ms`. It is the longest wait before this bound existed: the connect wait, the
|
|
83
|
+
* wait after the reader, and the loading grace rounds. So no acquire that was ready before is now
|
|
84
|
+
* refused.
|
|
85
|
+
*/
|
|
86
|
+
export const LEASE_READY_TIMEOUT_MS = LEASE_CONNECT_WAIT_MS * (2 + LEASE_LOADING_GRACE_ROUNDS);
|
|
87
|
+
/** `waitForReconnect` waits for `current` to differ from `previous`: these two always differ. */
|
|
88
|
+
const NOT_REGISTERED = {};
|
|
89
|
+
const REGISTERED = {};
|
|
90
|
+
/**
|
|
91
|
+
* A connect wait for `connectOrInject` that ends at the deadline. Each wait is one connect wait at
|
|
92
|
+
* most, so the reader still goes in at the same time. A wait that starts after the deadline looks
|
|
93
|
+
* once and does not sleep.
|
|
94
|
+
*/
|
|
95
|
+
export function connectWaitUntil(deadline, now, sleeper = sleep) {
|
|
96
|
+
return (isConnected) => waitForReconnect({
|
|
97
|
+
current: () => (isConnected() ? REGISTERED : undefined),
|
|
98
|
+
previous: NOT_REGISTERED,
|
|
99
|
+
timeoutMs: Math.min(LEASE_CONNECT_WAIT_MS, Math.max(0, deadline - now())),
|
|
100
|
+
now,
|
|
101
|
+
sleep: sleeper,
|
|
102
|
+
pollMs: LEASE_READY_POLL_MS,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Wait until the leased tab is registered, answers a probe and is visible, or the deadline passes.
|
|
107
|
+
*
|
|
108
|
+
* A row in the sessions map is not proof that the next call works (#1650): the tab can be attached
|
|
109
|
+
* and answer nothing. So `ready` waits for a reply. Returns the reason at the deadline, or
|
|
110
|
+
* `undefined` when the tab is ready. It always looks once, also when the deadline has passed.
|
|
111
|
+
*/
|
|
112
|
+
export async function awaitLeaseAnswering(registered, deadline, now, sleeper = sleep) {
|
|
113
|
+
for (;;) {
|
|
114
|
+
const session = registered();
|
|
115
|
+
const reason = session === undefined
|
|
116
|
+
? LeaseNotReadyReason.SDK_STOPPED_ANSWERING
|
|
117
|
+
: notReadyReason(await probeLeaseAlive(session), session);
|
|
118
|
+
if (reason === undefined || now() >= deadline) {
|
|
119
|
+
return reason;
|
|
120
|
+
}
|
|
121
|
+
await sleeper(LEASE_READY_POLL_MS);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
77
124
|
/**
|
|
78
125
|
* Poll until the leased tab's SDK has actually registered on the bridge, so the sessionId we return
|
|
79
126
|
* is immediately usable (the page navigates first, then connects a moment later — without this wait
|
|
@@ -203,3 +250,82 @@ export async function moveReusedLease(tabUrl, requested, goto, now) {
|
|
|
203
250
|
}
|
|
204
251
|
return { navigated: true, url: requested, arrived: pageOf(tabUrl()) === target };
|
|
205
252
|
}
|
|
253
|
+
/** What loading a held lease's tab again found. */
|
|
254
|
+
export const HeldLeaseReload = {
|
|
255
|
+
/** Its tab had already registered, so nothing was loaded again. */
|
|
256
|
+
REGISTERED: 'registered',
|
|
257
|
+
/** The page loaded again. Its SDK may or may not have dialled in since. */
|
|
258
|
+
RELOADED: 'reloaded',
|
|
259
|
+
/** The page is still open, but it did not load: the server did not answer, or it timed out. */
|
|
260
|
+
NOT_LOADED: 'not-loaded',
|
|
261
|
+
/** The page is closed, or its context is gone. Nothing is left to keep. */
|
|
262
|
+
GONE: 'gone',
|
|
263
|
+
};
|
|
264
|
+
/**
|
|
265
|
+
* Load a held lease's tab again in its own context, and wait for its SDK to dial in (#1924).
|
|
266
|
+
*
|
|
267
|
+
* A held lease whose tab never registered used to be released, and a new context minted. That
|
|
268
|
+
* context has no cookies, so a tab the agent had signed in to was closed and replaced by a
|
|
269
|
+
* signed-out one, and the retry could not get the sign-in back. The context is the sign-in, so the
|
|
270
|
+
* retry keeps it and only loads the page again. The reader the first acquire injected is an init
|
|
271
|
+
* script of that page, so it runs again on the new document by itself.
|
|
272
|
+
*
|
|
273
|
+
* A navigation that fails is not a closed tab: a dev server that restarts refuses the connection
|
|
274
|
+
* for a moment, and the context with its cookies is still there. `isOpen` asks the page something,
|
|
275
|
+
* which tells the two apart. A tab that has registered already is left alone.
|
|
276
|
+
*/
|
|
277
|
+
export async function reconnectHeldLease(goto, isOpen, isLoading, isRegistered, deadline, now) {
|
|
278
|
+
if (isRegistered()) {
|
|
279
|
+
return HeldLeaseReload.REGISTERED;
|
|
280
|
+
}
|
|
281
|
+
// Bounded by the acquire's own deadline. Playwright reads a timeout of 0 as "none", so at least
|
|
282
|
+
// 1 ms; and raced too, because a frozen page can hold the navigation past its own timeout.
|
|
283
|
+
const budget = Math.max(1, deadline - now());
|
|
284
|
+
const went = await answerWithin(Promise.resolve(budget).then(goto), budget + HELD_PAGE_ANSWER_MS, false);
|
|
285
|
+
if (!went) {
|
|
286
|
+
return (await pageStillOpen(isOpen)) ? HeldLeaseReload.NOT_LOADED : HeldLeaseReload.GONE;
|
|
287
|
+
}
|
|
288
|
+
const loading = () => answerWithin(isLoading(), HELD_PAGE_ANSWER_MS, false);
|
|
289
|
+
await connectOrInject({ isLoading: loading }, isRegistered, connectWaitUntil(deadline, now));
|
|
290
|
+
return HeldLeaseReload.RELOADED;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* How long one question to a held page may take. A page that answers does so in milliseconds; a
|
|
294
|
+
* frozen one (a script in an endless loop) never does, and must not hold the acquire with it.
|
|
295
|
+
*/
|
|
296
|
+
const HELD_PAGE_ANSWER_MS = 250;
|
|
297
|
+
/** `work`'s answer, or `fallback` when it rejects or has not answered within `ms`. */
|
|
298
|
+
async function answerWithin(work, ms, fallback) {
|
|
299
|
+
let timer;
|
|
300
|
+
const late = new Promise((resolve) => {
|
|
301
|
+
timer = setTimeout(() => {
|
|
302
|
+
resolve(fallback);
|
|
303
|
+
}, ms);
|
|
304
|
+
});
|
|
305
|
+
try {
|
|
306
|
+
return await Promise.race([work.catch(() => fallback), late]);
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
clearTimeout(timer);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/** How many times a page whose navigation failed is asked whether it is still there. */
|
|
313
|
+
const OPEN_PROBE_ATTEMPTS = 3;
|
|
314
|
+
/**
|
|
315
|
+
* Whether a page still answers, after a navigation on it failed.
|
|
316
|
+
*
|
|
317
|
+
* Asked more than once, because the first question right after a failed navigation is often lost
|
|
318
|
+
* ("Execution context was destroyed") on a page that is still open, and the next one is answered.
|
|
319
|
+
* A closed page, or a page whose context is closed, refuses every time. A frozen page never answers,
|
|
320
|
+
* so each question is bounded, and it reads as gone.
|
|
321
|
+
*/
|
|
322
|
+
async function pageStillOpen(isOpen) {
|
|
323
|
+
for (let attempt = 0; attempt < OPEN_PROBE_ATTEMPTS; attempt++) {
|
|
324
|
+
const asked = Promise.resolve().then(isOpen);
|
|
325
|
+
if (await answerWithin(asked, HELD_PAGE_ANSWER_MS, false)) {
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
await sleep(LEASE_READY_POLL_MS);
|
|
329
|
+
}
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
@@ -24,6 +24,11 @@ export declare const NAV_CERT_REFUSED: string;
|
|
|
24
24
|
export declare const NAV_FAILED_QUESTION = "is the app running there?";
|
|
25
25
|
/** What a lease navigation that ran out of time tells the caller to do. */
|
|
26
26
|
export declare const NAV_TIMEOUT_ADVICE: string;
|
|
27
|
+
/**
|
|
28
|
+
* What a lease navigation that ran out of time tells the caller when the server accepted the
|
|
29
|
+
* connection: it is running and most likely compiling the route, so starting it again is wrong (#2005).
|
|
30
|
+
*/
|
|
31
|
+
export declare const NAV_COMPILING_ADVICE: string;
|
|
27
32
|
/**
|
|
28
33
|
* The sentence for a lease whose first navigation failed.
|
|
29
34
|
*
|
|
@@ -35,7 +40,17 @@ export declare const NAV_TIMEOUT_ADVICE: string;
|
|
|
35
40
|
* hunting for a dev server that is up instead of retrying, so the timeout sentence names both
|
|
36
41
|
* cases and leads with the retry.
|
|
37
42
|
*/
|
|
38
|
-
export declare function navFailureMessage(url: string, err: unknown, seed?: SeedStorage): string;
|
|
43
|
+
export declare function navFailureMessage(url: string, err: unknown, seed?: SeedStorage, serverAccepted?: boolean): string;
|
|
44
|
+
/**
|
|
45
|
+
* Whether the server behind a timed-out lease navigation accepted a connection.
|
|
46
|
+
*
|
|
47
|
+
* Only asked after a timeout and only of a loopback URL: the port probe reaches `localhost`, so a
|
|
48
|
+
* remote host would be answered by whatever listens locally on the same port. Never rejects; any
|
|
49
|
+
* doubt is `false`, which keeps the generic timeout advice.
|
|
50
|
+
*/
|
|
51
|
+
export declare function serverAcceptedAfterTimeout(url: string, err: unknown, seed: SeedStorage | undefined, portAccepts: ((port: number) => Promise<boolean>) | undefined): Promise<boolean>;
|
|
52
|
+
/** Whether a path is a sign-in page, by its name: `/login`, `/signin`, `/auth/login` and similar. */
|
|
53
|
+
export declare const isAuthPath: (pathname: string) => boolean;
|
|
39
54
|
/**
|
|
40
55
|
* Determine if a required seedStorage application precondition failed.
|
|
41
56
|
*
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Seeded storage for a lease, and the errors a lease's navigation throws: what a caller may see of
|
|
3
3
|
* them without seeing the secrets it seeded. Split from lease-tools.ts, which crossed the line cap.
|
|
4
4
|
*/
|
|
5
|
-
import { REDACTED_VALUE, scrubKnownSecrets } from '@reticlehq/core';
|
|
5
|
+
import { isLoopbackHostname, REDACTED_VALUE, scrubKnownSecrets, } from '@reticlehq/core';
|
|
6
6
|
function redactExactValue(text, value) {
|
|
7
7
|
if (0 === value.length)
|
|
8
8
|
return text;
|
|
@@ -95,6 +95,12 @@ export const NAV_FAILED_QUESTION = 'is the app running there?';
|
|
|
95
95
|
/** What a lease navigation that ran out of time tells the caller to do. */
|
|
96
96
|
export const NAV_TIMEOUT_ADVICE = 'If the app is running, a dev server compiling a route for the first time can take longer than ' +
|
|
97
97
|
'that: retry the acquire. If it is not running, start it first.';
|
|
98
|
+
/**
|
|
99
|
+
* What a lease navigation that ran out of time tells the caller when the server accepted the
|
|
100
|
+
* connection: it is running and most likely compiling the route, so starting it again is wrong (#2005).
|
|
101
|
+
*/
|
|
102
|
+
export const NAV_COMPILING_ADVICE = 'The server accepted the connection, so it is running and is probably still compiling this ' +
|
|
103
|
+
'route: wait a few seconds, then acquire the lease again. Do not start another dev server.';
|
|
98
104
|
/** Lead of the timeout sentence, before the budget ("within 30 s", or "in time" when unknown). */
|
|
99
105
|
const NAV_TIMEOUT_LEAD = 'the page did not finish loading';
|
|
100
106
|
const NAV_TIMEOUT_UNKNOWN_BUDGET = 'in time';
|
|
@@ -111,7 +117,7 @@ const PLAYWRIGHT_TIMEOUT = /Timeout (\d+)ms exceeded/;
|
|
|
111
117
|
* hunting for a dev server that is up instead of retrying, so the timeout sentence names both
|
|
112
118
|
* cases and leads with the retry.
|
|
113
119
|
*/
|
|
114
|
-
export function navFailureMessage(url, err, seed) {
|
|
120
|
+
export function navFailureMessage(url, err, seed, serverAccepted = false) {
|
|
115
121
|
const reason = cleanNavError(err, seed);
|
|
116
122
|
// A certificate the browser refused is not an app that is down: the server answered, with a cert
|
|
117
123
|
// no CA here vouches for (a self-signed or mkcert dev certificate). Asking "is the app running?"
|
|
@@ -125,8 +131,36 @@ export function navFailureMessage(url, err, seed) {
|
|
|
125
131
|
const within = budgetMs === undefined
|
|
126
132
|
? NAV_TIMEOUT_UNKNOWN_BUDGET
|
|
127
133
|
: `within ${String(Number(budgetMs) / 1000)} s`;
|
|
128
|
-
|
|
134
|
+
const advice = serverAccepted ? NAV_COMPILING_ADVICE : NAV_TIMEOUT_ADVICE;
|
|
135
|
+
return `could not open ${url} — ${NAV_TIMEOUT_LEAD} ${within} (${reason}). ${advice}`;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Whether the server behind a timed-out lease navigation accepted a connection.
|
|
139
|
+
*
|
|
140
|
+
* Only asked after a timeout and only of a loopback URL: the port probe reaches `localhost`, so a
|
|
141
|
+
* remote host would be answered by whatever listens locally on the same port. Never rejects; any
|
|
142
|
+
* doubt is `false`, which keeps the generic timeout advice.
|
|
143
|
+
*/
|
|
144
|
+
export async function serverAcceptedAfterTimeout(url, err, seed, portAccepts) {
|
|
145
|
+
if (portAccepts === undefined || NAV_TIMED_OUT !== cleanNavError(err, seed)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
let parsed;
|
|
149
|
+
try {
|
|
150
|
+
parsed = new URL(url);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
if (!isLoopbackHostname(parsed.hostname)) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
const defaultPort = 'https:' === parsed.protocol ? 443 : 80;
|
|
159
|
+
const port = '' === parsed.port ? defaultPort : Number(parsed.port);
|
|
160
|
+
return portAccepts(port).catch(() => false);
|
|
129
161
|
}
|
|
162
|
+
/** Whether a path is a sign-in page, by its name: `/login`, `/signin`, `/auth/login` and similar. */
|
|
163
|
+
export const isAuthPath = (pathname) => /(?:^|\/)(?:login|signin|sign-in|auth\/login|auth\/signin|session\/new)(?:$|\/)/i.test(pathname);
|
|
130
164
|
/**
|
|
131
165
|
* Determine if a required seedStorage application precondition failed.
|
|
132
166
|
*
|
|
@@ -159,7 +193,6 @@ export function evaluateSeedPrecondition(requestedUrl, actualUrl, seed, navStatu
|
|
|
159
193
|
return `seeded storage precondition not established: cross-origin redirect from ${reqParsed.origin} to ${actParsed.origin} skipped origin-scoped storage injection`;
|
|
160
194
|
}
|
|
161
195
|
// Clear redirect to an authentication/login endpoint when the requested URL was not a login page
|
|
162
|
-
const isAuthPath = (pathname) => /(?:^|\/)(?:login|signin|sign-in|auth\/login|auth\/signin|session\/new)(?:$|\/)/i.test(pathname);
|
|
163
196
|
if (!isAuthPath(reqParsed.pathname) && isAuthPath(actParsed.pathname)) {
|
|
164
197
|
return `seeded authentication precondition not established: redirected from ${reqParsed.pathname} to login page (${actParsed.pathname})`;
|
|
165
198
|
}
|
|
@@ -10,50 +10,11 @@
|
|
|
10
10
|
* Attach-only: the pool drives a browser against an already-running dev server — it never starts one.
|
|
11
11
|
*/
|
|
12
12
|
import { type SeedStorage } from '@reticlehq/core';
|
|
13
|
-
import { HudVisibility } from '@reticlehq/core';
|
|
14
13
|
import { type ToolDef, type ToolDeps } from './tool-kit.js';
|
|
15
14
|
import { type BrowserPool } from '../../portal/pool/browser-pool.js';
|
|
16
15
|
export { sessionMutationPort, sessionPerturbationPort, suiteFixturePort, suiteFixtureSeed, } from './session-ports.js';
|
|
17
|
-
|
|
18
|
-
* Append Reticle identity params (__reticle_session, optional __reticle_project) to a URL so the app's own SDK
|
|
19
|
-
* adopts them on connect. Pure; falls back to plain concatenation if the URL can't be parsed.
|
|
20
|
-
*/
|
|
21
|
-
export declare function appendReticleParams(url: string, session: string, projectId?: string, hud?: HudVisibility): string;
|
|
16
|
+
export { appendReticleParams, carryReticleIdentity, resolveLeasedSessionId, } from './lease-url-identity.js';
|
|
22
17
|
export { cleanNavError, evaluateSeedPrecondition, scrubSeedFromError } from './lease-seed.js';
|
|
23
|
-
/**
|
|
24
|
-
* The session a lease's tab actually registered as, or undefined while nothing has.
|
|
25
|
-
*
|
|
26
|
-
* Normally that is the lease id: `appendReticleParams` stamps `__reticle_session` on the URL and the
|
|
27
|
-
* SDK adopts it. An app that passes an explicit `session` to `connect()` keeps its own name instead,
|
|
28
|
-
* which is legitimate — a single-app fixture does it deliberately so a battery can address it by a
|
|
29
|
-
* known id. Matching on the lease id alone could never see those tabs, so the lease reported
|
|
30
|
-
* `ready: false` and a hint blaming a port mismatch while the session was connected and driveable.
|
|
31
|
-
*
|
|
32
|
-
* The URL marker is the evidence, and it is what makes this safe: two concurrent leases on one origin
|
|
33
|
-
* carry different markers, so neither can adopt the other's tab. Parsed rather than substring-matched,
|
|
34
|
-
* so `lease-abc` cannot claim `lease-abcdef`.
|
|
35
|
-
*/
|
|
36
|
-
export declare function resolveLeasedSessionId(sessions: {
|
|
37
|
-
get: (id: string) => unknown;
|
|
38
|
-
all: () => {
|
|
39
|
-
id: string;
|
|
40
|
-
url?: string;
|
|
41
|
-
}[];
|
|
42
|
-
}, leaseId: string): string | undefined;
|
|
43
|
-
/**
|
|
44
|
-
* Carry a claimed identity across a navigation.
|
|
45
|
-
*
|
|
46
|
-
* A LEASED tab is addressed by `__reticle_session` in its URL: that param is how the pool finds the
|
|
47
|
-
* context it opened, and — since succession was tightened — it is also the ONLY thing that lets the
|
|
48
|
-
* reconnecting document inherit the lease. `reticle_navigate` sent the caller's raw URL, so the very
|
|
49
|
-
* first navigation stripped the marker, the tab re-announced as an ordinary anonymous session, and
|
|
50
|
-
* the lease was orphaned: "no sessions are connected at all", with a browser window still open on
|
|
51
|
-
* screen. A leased browser that cannot survive being navigated is not a usable one.
|
|
52
|
-
*
|
|
53
|
-
* A tab that claims nothing is left exactly as it was — this must never bolt Reticle's params onto
|
|
54
|
-
* a human's own URL.
|
|
55
|
-
*/
|
|
56
|
-
export declare function carryReticleIdentity(fromUrl: string | undefined, toUrl: string): string;
|
|
57
18
|
/**
|
|
58
19
|
* Acquire a leased, SDK-connected context for a caller that is not the lease TOOL (the parallel suite).
|
|
59
20
|
* Returns the session id plus its release; the caller must release in a finally so a crashed flow never
|
|
@@ -63,6 +24,7 @@ export declare function acquireLeasedSession(pool: {
|
|
|
63
24
|
acquire: (url: string, opts: {
|
|
64
25
|
sessionId: string;
|
|
65
26
|
seedStorage?: SeedStorage;
|
|
27
|
+
ignoreHTTPSErrors?: boolean;
|
|
66
28
|
}) => Promise<{
|
|
67
29
|
sessionId: string;
|
|
68
30
|
release: () => Promise<void>;
|
|
@@ -83,7 +45,7 @@ export declare function acquireLeasedSession(pool: {
|
|
|
83
45
|
id: string;
|
|
84
46
|
url?: string;
|
|
85
47
|
}[];
|
|
86
|
-
}, url: string, projectId?: string, seedStorage?: SeedStorage): Promise<{
|
|
48
|
+
}, url: string, projectId?: string, seedStorage?: SeedStorage, ignoreHTTPSErrors?: boolean): Promise<{
|
|
87
49
|
sessionId: string;
|
|
88
50
|
release: () => Promise<void>;
|
|
89
51
|
}>;
|