@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
import { AnchorKind } from '@reticlehq/core';
|
|
3
4
|
import { CALL_SECRETS_ARG, callSecrets } from '../../../surface/tools/tool-kit.js';
|
|
@@ -14,12 +15,15 @@ import { CALL_SECRETS_ARG, callSecrets } from '../../../surface/tools/tool-kit.j
|
|
|
14
15
|
* to find the env var because the runner passed a role or a label instead.
|
|
15
16
|
*/
|
|
16
17
|
export function anchorFieldName(anchor) {
|
|
17
|
-
if (AnchorKind.TESTID === anchor.kind)
|
|
18
|
+
if (AnchorKind.TESTID === anchor.kind) {
|
|
18
19
|
return anchor.value;
|
|
19
|
-
|
|
20
|
+
}
|
|
21
|
+
if (AnchorKind.ROLE === anchor.kind) {
|
|
20
22
|
return anchor.name;
|
|
21
|
-
|
|
23
|
+
}
|
|
24
|
+
if (AnchorKind.SIGNAL === anchor.kind) {
|
|
22
25
|
return anchor.name;
|
|
26
|
+
}
|
|
23
27
|
return undefined;
|
|
24
28
|
}
|
|
25
29
|
/**
|
|
@@ -39,6 +43,16 @@ export const SECRET_NAME_ARG = 'secret';
|
|
|
39
43
|
export const SECRET_FIELD_ARG = 'secretField';
|
|
40
44
|
/** The prefix every secret's variable carries. */
|
|
41
45
|
const SECRET_ENV_PREFIX = 'RETICLE_SECRET_';
|
|
46
|
+
/** Any character outside ASCII. */
|
|
47
|
+
const NON_ASCII = /[^\p{ASCII}]/u;
|
|
48
|
+
/** The word in the variable of a field whose name is not ASCII. */
|
|
49
|
+
const NON_ASCII_FIELD = 'FIELD';
|
|
50
|
+
const NON_ASCII_DIGEST_LENGTH = 8;
|
|
51
|
+
/**
|
|
52
|
+
* The variable name an ASCII field gets. Up to 3.9.0 a non-ASCII field got this name too, and a
|
|
53
|
+
* flow saved then still names it.
|
|
54
|
+
*/
|
|
55
|
+
const asciiSecretEnvKey = (field) => `${SECRET_ENV_PREFIX}${field.replace(/[^a-zA-Z0-9]+/g, '_').toUpperCase()}`;
|
|
42
56
|
/**
|
|
43
57
|
* The environment variable that supplies one redacted field.
|
|
44
58
|
*
|
|
@@ -51,7 +65,16 @@ const SECRET_ENV_PREFIX = 'RETICLE_SECRET_';
|
|
|
51
65
|
* rules must not be able to drift. It used to sit in `replay.ts`, one import away from the sentence
|
|
52
66
|
* saying it had to stay together.
|
|
53
67
|
*/
|
|
54
|
-
export
|
|
68
|
+
export function secretEnvKey(field) {
|
|
69
|
+
if (!NON_ASCII.test(field)) {
|
|
70
|
+
return asciiSecretEnvKey(field);
|
|
71
|
+
}
|
|
72
|
+
// A name in another script has no ASCII letters to keep: "Пароль" and "Гасло" both became
|
|
73
|
+
// `RETICLE_SECRET__`, so one field's value was typed into the other. A short hash of the name
|
|
74
|
+
// keeps each field's variable distinct and the same on every machine.
|
|
75
|
+
const digest = createHash('sha256').update(field.normalize('NFC').toLowerCase()).digest('hex');
|
|
76
|
+
return `${SECRET_ENV_PREFIX}${NON_ASCII_FIELD}_${digest.slice(0, NON_ASCII_DIGEST_LENGTH).toUpperCase()}`;
|
|
77
|
+
}
|
|
55
78
|
/** The schema of that argument, shared by every tool that replays a flow. */
|
|
56
79
|
export { CALL_SECRETS_ARG };
|
|
57
80
|
export const callSecretsArg = z
|
|
@@ -64,30 +87,35 @@ export const callSecretsArg = z
|
|
|
64
87
|
*/
|
|
65
88
|
export function secretValue(envKey, env) {
|
|
66
89
|
for (const [key, value] of Object.entries(callSecrets() ?? {})) {
|
|
67
|
-
if (envKey === (key.startsWith(SECRET_ENV_PREFIX) ? key : secretEnvKey(key)))
|
|
90
|
+
if (envKey === (key.startsWith(SECRET_ENV_PREFIX) ? key : secretEnvKey(key))) {
|
|
68
91
|
return value;
|
|
92
|
+
}
|
|
69
93
|
}
|
|
70
94
|
return env[envKey];
|
|
71
95
|
}
|
|
72
96
|
const SECRET_OCCURRENCE = /^(?:[2-9]|[1-9][0-9]+)$/;
|
|
73
97
|
/** The field's own saved variable name, or its original key for legacy and invalid metadata. */
|
|
74
98
|
export function secretKeyForFill(args, field) {
|
|
75
|
-
if (args?.['value'] !== REDACTED_FILL)
|
|
99
|
+
if (args?.['value'] !== REDACTED_FILL) {
|
|
76
100
|
return undefined;
|
|
101
|
+
}
|
|
77
102
|
const named = args[SECRET_NAME_ARG];
|
|
78
103
|
const base = secretEnvKey(field);
|
|
79
|
-
// A flow can supply this field's occurrence, never an unrelated environment variable.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
104
|
+
// A flow can supply this field's occurrence, never an unrelated environment variable. The name
|
|
105
|
+
// an earlier version gave a non-ASCII field is still this field's.
|
|
106
|
+
if ('string' !== typeof named) {
|
|
107
|
+
return base;
|
|
108
|
+
}
|
|
109
|
+
const own = (key) => named === key ||
|
|
110
|
+
(named.startsWith(`${key}_`) && SECRET_OCCURRENCE.test(named.slice(key.length + 1)));
|
|
111
|
+
return own(base) || own(asciiSecretEnvKey(field)) ? named : base;
|
|
85
112
|
}
|
|
86
113
|
function* secretSteps(steps) {
|
|
87
114
|
for (const step of steps) {
|
|
88
115
|
yield step;
|
|
89
|
-
if (step.steps !== undefined)
|
|
116
|
+
if (step.steps !== undefined) {
|
|
90
117
|
yield* secretSteps(step.steps);
|
|
118
|
+
}
|
|
91
119
|
}
|
|
92
120
|
}
|
|
93
121
|
/** Allocate recorded fills' names while preserving legacy placeholders and existing suffixes. */
|
|
@@ -96,14 +124,17 @@ export function withDistinctSecretKeys(flow) {
|
|
|
96
124
|
const used = new Set();
|
|
97
125
|
for (const step of secretSteps(flow.steps)) {
|
|
98
126
|
const field = anchorFieldName(step.anchor);
|
|
99
|
-
if (field === undefined)
|
|
127
|
+
if (field === undefined) {
|
|
100
128
|
continue;
|
|
129
|
+
}
|
|
101
130
|
const key = secretKeyForFill(step.args, field);
|
|
102
|
-
if (key === undefined)
|
|
131
|
+
if (key === undefined) {
|
|
103
132
|
continue;
|
|
133
|
+
}
|
|
104
134
|
reserved.add(key);
|
|
105
|
-
if (key !== secretEnvKey(field) || step.args?.[SECRET_NAME_ARG] === undefined)
|
|
135
|
+
if (key !== secretEnvKey(field) || step.args?.[SECRET_NAME_ARG] === undefined) {
|
|
106
136
|
used.add(key);
|
|
137
|
+
}
|
|
107
138
|
}
|
|
108
139
|
const assign = (step) => {
|
|
109
140
|
let out = step;
|
|
@@ -119,8 +150,9 @@ export function withDistinctSecretKeys(flow) {
|
|
|
119
150
|
occurrence += 1;
|
|
120
151
|
}
|
|
121
152
|
used.add(key);
|
|
122
|
-
if (key !== base)
|
|
153
|
+
if (key !== base) {
|
|
123
154
|
out = { ...step, args: { ...step.args, [SECRET_NAME_ARG]: key } };
|
|
155
|
+
}
|
|
124
156
|
}
|
|
125
157
|
return step.steps === undefined ? out : { ...out, steps: step.steps.map(assign) };
|
|
126
158
|
};
|
|
@@ -138,23 +170,47 @@ export function withDistinctSecretKeys(flow) {
|
|
|
138
170
|
* non-zero length, so an empty variable leaves the placeholder and must count as unsupplied here.
|
|
139
171
|
* Two different answers to "is this supplied" is how the gate would pass and the replay still fail.
|
|
140
172
|
*/
|
|
173
|
+
function* flattenAll(steps) {
|
|
174
|
+
for (const step of steps) {
|
|
175
|
+
yield step;
|
|
176
|
+
yield* flattenAll(step.steps ?? []);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
141
179
|
export function unsuppliedSecrets(flow, env) {
|
|
142
180
|
const out = [];
|
|
143
181
|
const seen = new Set();
|
|
144
182
|
for (const step of secretSteps(flow.steps ?? [])) {
|
|
145
183
|
const field = anchorFieldName(step.anchor);
|
|
146
|
-
if (field === undefined)
|
|
184
|
+
if (field === undefined) {
|
|
147
185
|
continue;
|
|
186
|
+
}
|
|
148
187
|
const envKey = secretKeyForFill(step.args, field);
|
|
149
|
-
if (envKey === undefined)
|
|
188
|
+
if (envKey === undefined) {
|
|
150
189
|
continue;
|
|
190
|
+
}
|
|
151
191
|
const supplied = secretValue(envKey, env);
|
|
152
|
-
if (supplied !== undefined && supplied.length > 0)
|
|
192
|
+
if (supplied !== undefined && supplied.length > 0) {
|
|
153
193
|
continue;
|
|
154
|
-
|
|
194
|
+
}
|
|
195
|
+
if (seen.has(envKey)) {
|
|
155
196
|
continue;
|
|
197
|
+
}
|
|
156
198
|
seen.add(envKey);
|
|
157
199
|
out.push({ field, envKey });
|
|
158
200
|
}
|
|
201
|
+
// A request step that acts as another account needs that account's session.
|
|
202
|
+
for (const step of flattenAll(flow.steps ?? [])) {
|
|
203
|
+
const account = step.request?.as;
|
|
204
|
+
if (account === undefined) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const envKey = secretEnvKey(account);
|
|
208
|
+
const supplied = secretValue(envKey, env);
|
|
209
|
+
if ((supplied !== undefined && supplied.length > 0) || seen.has(envKey)) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
seen.add(envKey);
|
|
213
|
+
out.push({ field: account, envKey });
|
|
214
|
+
}
|
|
159
215
|
return out;
|
|
160
216
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnchorKind, type Drift, type FlowAnchor, type QueryEmptyHint } from '@reticlehq/core';
|
|
1
|
+
import { AnchorKind, type CommandResult, type Drift, type FlowAnchor, type QueryEmptyHint } from '@reticlehq/core';
|
|
2
2
|
import type { FlowReplaySession, Sleep } from './flow-replay-types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Finding a step's anchor again, and saying legibly why it could not be found.
|
|
@@ -33,6 +33,29 @@ export declare function nearestTestid(missing: string, present: string[]): strin
|
|
|
33
33
|
export declare function nearestIsAmbiguous(missing: string, present: string[]): boolean;
|
|
34
34
|
/** Build the legible-drift record for a testid anchor that resolved to zero live elements. */
|
|
35
35
|
export declare function testidDrift(value: string, hint: QueryEmptyHint | undefined): Drift;
|
|
36
|
+
/**
|
|
37
|
+
* How an anchor is ADDRESSED — the sentence that names it, and the label a result row carries.
|
|
38
|
+
*
|
|
39
|
+
* Two different strings, and neither can be derived from the other: `value` is a raw testid for one
|
|
40
|
+
* anchor kind and a human-readable label for the others, so one sentence cannot name both. Calling a
|
|
41
|
+
* role anchor `testid "button "View result""` sends the reader to edit a locator that does not exist
|
|
42
|
+
* on that step, which is the same misdirection the drift reason exists to remove.
|
|
43
|
+
*
|
|
44
|
+
* Derived from the anchor rather than passed in by the caller. The first version of the role fix took
|
|
45
|
+
* a `described` argument and wrote `ambiguousAnchorDrift(label, n, \`role anchor ${label}\`)` — the
|
|
46
|
+
* same string twice, so the `anchor` field and the sentence could drift apart with nothing to catch
|
|
47
|
+
* it, and the next anchor kind would have had to remember the convention on its own.
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* How a role anchor reads in a message: `button "View result"`, or for a control with no accessible
|
|
51
|
+
* name, `button (no accessible name, #2 of 3)` — never `button "undefined"`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function roleAnchorLabel(anchor: {
|
|
54
|
+
role: string;
|
|
55
|
+
name?: string | undefined;
|
|
56
|
+
nth?: number | undefined;
|
|
57
|
+
of?: number | undefined;
|
|
58
|
+
}): string;
|
|
36
59
|
/**
|
|
37
60
|
* What the stale-ref retry found when it ran the locator again.
|
|
38
61
|
*
|
|
@@ -106,10 +129,28 @@ export declare function queryOnce(session: FlowReplaySession, queryArgs: Record<
|
|
|
106
129
|
refs: string[];
|
|
107
130
|
hint?: QueryEmptyHint;
|
|
108
131
|
}>;
|
|
109
|
-
export declare function resolveQuery(session: FlowReplaySession, queryArgs: Record<string, unknown>, sleep: Sleep, now?: () => number
|
|
132
|
+
export declare function resolveQuery(session: FlowReplaySession, queryArgs: Record<string, unknown>, sleep: Sleep, now?: () => number, read?: (result: CommandResult) => {
|
|
133
|
+
refs: string[];
|
|
134
|
+
hint?: QueryEmptyHint;
|
|
135
|
+
}): Promise<{
|
|
110
136
|
refs: string[];
|
|
111
137
|
hint?: QueryEmptyHint;
|
|
112
138
|
}>;
|
|
139
|
+
/**
|
|
140
|
+
* Several queries for one control, pooled: each attempt runs every query and joins the refs, so
|
|
141
|
+
* the settle budget is spent once, not once for each query. A role anchor with `alsoRoles` uses it.
|
|
142
|
+
*/
|
|
143
|
+
export declare function resolveAnyQuery(session: FlowReplaySession, queries: readonly Record<string, unknown>[], sleep: Sleep, now?: () => number, read?: (result: CommandResult) => {
|
|
144
|
+
refs: string[];
|
|
145
|
+
hint?: QueryEmptyHint;
|
|
146
|
+
}): Promise<{
|
|
147
|
+
refs: string[];
|
|
148
|
+
hint?: QueryEmptyHint;
|
|
149
|
+
}>;
|
|
150
|
+
/** `resolveQuery`'s reader for a nameless role anchor: only the matches with no accessible name. */
|
|
151
|
+
export declare function readNamelessQuery(result: CommandResult): {
|
|
152
|
+
refs: string[];
|
|
153
|
+
};
|
|
113
154
|
/** Re-resolve a testid anchor. */
|
|
114
155
|
export declare function resolveTestid(session: FlowReplaySession, value: string, sleep: Sleep): Promise<{
|
|
115
156
|
refs: string[];
|
|
@@ -125,3 +166,20 @@ export declare function anchorLabel(anchor: FlowAnchor): string;
|
|
|
125
166
|
export declare function componentQueryArgs(anchor: Extract<FlowAnchor, {
|
|
126
167
|
kind: typeof AnchorKind.COMPONENT;
|
|
127
168
|
}>): Record<string, unknown>;
|
|
169
|
+
/** Query args for a NAMED role anchor — the handle that identifies an instance, not a JSX site. */
|
|
170
|
+
export declare function roleQueryArgs(anchor: Extract<FlowAnchor, {
|
|
171
|
+
kind: typeof AnchorKind.ROLE;
|
|
172
|
+
}>): Record<string, unknown>;
|
|
173
|
+
/**
|
|
174
|
+
* Resolve a role anchor. ONE function for the first look and the re-resolve after a stale ref: the
|
|
175
|
+
* re-resolve used to count every control of the role, so a nameless anchor's position came back
|
|
176
|
+
* ambiguous on a working page.
|
|
177
|
+
*
|
|
178
|
+
* A nameless anchor's position counts only the nameless controls of its role (an icon-only edit
|
|
179
|
+
* button is the 2nd nameless button of its row, not the 5th button), so it reads only those.
|
|
180
|
+
*/
|
|
181
|
+
export declare function resolveRoleAnchor(session: FlowReplaySession, anchor: Extract<FlowAnchor, {
|
|
182
|
+
kind: typeof AnchorKind.ROLE;
|
|
183
|
+
}>, sleep: Sleep): Promise<{
|
|
184
|
+
refs: string[];
|
|
185
|
+
}>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnchorKind, DriftReason, ReticleCommand, QueryBy, asRecord, asString, } from '@reticlehq/core';
|
|
2
|
-
import { queryRefs } from './replay.js';
|
|
2
|
+
import { namelessRefs, queryRefs } from './replay.js';
|
|
3
3
|
/**
|
|
4
4
|
* Finding a step's anchor again, and saying legibly why it could not be found.
|
|
5
5
|
*
|
|
@@ -20,16 +20,18 @@ export function editDistance(a, b) {
|
|
|
20
20
|
const cols = t.length + 1;
|
|
21
21
|
const prev = new Array(cols);
|
|
22
22
|
const curr = new Array(cols);
|
|
23
|
-
for (let j = 0; j < cols; j++)
|
|
23
|
+
for (let j = 0; j < cols; j++) {
|
|
24
24
|
prev[j] = j;
|
|
25
|
+
}
|
|
25
26
|
for (let i = 1; i < rows; i++) {
|
|
26
27
|
curr[0] = i;
|
|
27
28
|
for (let j = 1; j < cols; j++) {
|
|
28
29
|
const cost = s[i - 1] === t[j - 1] ? 0 : 1;
|
|
29
30
|
curr[j] = Math.min((prev[j] ?? 0) + 1, (curr[j - 1] ?? 0) + 1, (prev[j - 1] ?? 0) + cost);
|
|
30
31
|
}
|
|
31
|
-
for (let j = 0; j < cols; j++)
|
|
32
|
+
for (let j = 0; j < cols; j++) {
|
|
32
33
|
prev[j] = curr[j] ?? 0;
|
|
34
|
+
}
|
|
33
35
|
}
|
|
34
36
|
return prev[cols - 1] ?? 0;
|
|
35
37
|
}
|
|
@@ -58,8 +60,9 @@ export function nearestTestid(missing, present) {
|
|
|
58
60
|
bestDistance = distance;
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
|
-
if (null === best)
|
|
63
|
+
if (null === best) {
|
|
62
64
|
return null;
|
|
65
|
+
}
|
|
63
66
|
return bestDistance <= Math.max(missing.length, best.length) / 2 ? best : null;
|
|
64
67
|
}
|
|
65
68
|
/**
|
|
@@ -90,8 +93,9 @@ const ANCHOR_SETTLE_MAX_ATTEMPTS = 40;
|
|
|
90
93
|
/** Extract the live element refs + the zero-match near-miss hint from a QUERY command result. */
|
|
91
94
|
function readQuery(result) {
|
|
92
95
|
const refs = queryRefs(result);
|
|
93
|
-
if (!result.ok)
|
|
96
|
+
if (!result.ok) {
|
|
94
97
|
return { refs };
|
|
98
|
+
}
|
|
95
99
|
const payload = asRecord(result.result);
|
|
96
100
|
const rawHint = payload['hint'];
|
|
97
101
|
if ('object' === typeof rawHint && rawHint !== null) {
|
|
@@ -117,8 +121,9 @@ function readQuery(result) {
|
|
|
117
121
|
* Such a drift is surfaced (with a nearest) but never auto-healed.
|
|
118
122
|
*/
|
|
119
123
|
export function nearestIsAmbiguous(missing, present) {
|
|
120
|
-
if (present.length < 2)
|
|
124
|
+
if (present.length < 2) {
|
|
121
125
|
return false;
|
|
126
|
+
}
|
|
122
127
|
let min = Number.POSITIVE_INFINITY;
|
|
123
128
|
let count = 0;
|
|
124
129
|
for (const candidate of present) {
|
|
@@ -142,8 +147,9 @@ export function testidDrift(value, hint) {
|
|
|
142
147
|
anchor: value,
|
|
143
148
|
nearest: nearestTestid(value, present),
|
|
144
149
|
};
|
|
145
|
-
if (nearestIsAmbiguous(value, present))
|
|
150
|
+
if (nearestIsAmbiguous(value, present)) {
|
|
146
151
|
drift.ambiguous = true;
|
|
152
|
+
}
|
|
147
153
|
return drift;
|
|
148
154
|
}
|
|
149
155
|
/**
|
|
@@ -159,6 +165,19 @@ export function testidDrift(value, hint) {
|
|
|
159
165
|
* same string twice, so the `anchor` field and the sentence could drift apart with nothing to catch
|
|
160
166
|
* it, and the next anchor kind would have had to remember the convention on its own.
|
|
161
167
|
*/
|
|
168
|
+
/**
|
|
169
|
+
* How a role anchor reads in a message: `button "View result"`, or for a control with no accessible
|
|
170
|
+
* name, `button (no accessible name, #2 of 3)` — never `button "undefined"`.
|
|
171
|
+
*/
|
|
172
|
+
export function roleAnchorLabel(anchor) {
|
|
173
|
+
if (anchor.name !== undefined) {
|
|
174
|
+
return `${anchor.role} "${anchor.name}"`;
|
|
175
|
+
}
|
|
176
|
+
const at = anchor.nth !== undefined && anchor.of !== undefined
|
|
177
|
+
? `, #${String(anchor.nth + 1)} of ${String(anchor.of)}`
|
|
178
|
+
: '';
|
|
179
|
+
return `${anchor.role} (no accessible name${at})`;
|
|
180
|
+
}
|
|
162
181
|
function anchorAddress(anchor) {
|
|
163
182
|
if (anchor.kind === AnchorKind.TESTID) {
|
|
164
183
|
return { described: `testid "${anchor.value}"`, label: anchor.value };
|
|
@@ -166,7 +185,7 @@ function anchorAddress(anchor) {
|
|
|
166
185
|
if (anchor.kind === AnchorKind.ROLE) {
|
|
167
186
|
// Kind named explicitly: `button "View result"` alone reads as a testid that happens to contain
|
|
168
187
|
// a space, and the reader would go looking for a locator nothing on the step declares.
|
|
169
|
-
const label =
|
|
188
|
+
const label = roleAnchorLabel(anchor);
|
|
170
189
|
return { described: `role anchor ${label}`, label };
|
|
171
190
|
}
|
|
172
191
|
// A component needs no such qualifier: `componentLabel` already answers
|
|
@@ -226,8 +245,9 @@ export function ambiguityDrift(anchor, refs) {
|
|
|
226
245
|
* while the page still has exactly as many of them as when it was recorded. Otherwise undefined.
|
|
227
246
|
*/
|
|
228
247
|
export function refFor(anchor, refs) {
|
|
229
|
-
if (1 === refs.length)
|
|
248
|
+
if (1 === refs.length) {
|
|
230
249
|
return refs[0];
|
|
250
|
+
}
|
|
231
251
|
const { nth, of } = { ...anchor };
|
|
232
252
|
return 'number' === typeof nth && of === refs.length ? refs[nth] : undefined;
|
|
233
253
|
}
|
|
@@ -246,8 +266,9 @@ export function refFor(anchor, refs) {
|
|
|
246
266
|
*/
|
|
247
267
|
export function reresolved(anchor, refs) {
|
|
248
268
|
const drift = ambiguityDrift(anchor, refs);
|
|
249
|
-
if (drift !== null)
|
|
269
|
+
if (drift !== null) {
|
|
250
270
|
return { drift };
|
|
271
|
+
}
|
|
251
272
|
const ref = refFor(anchor, refs);
|
|
252
273
|
return ref === undefined ? undefined : { ref };
|
|
253
274
|
}
|
|
@@ -268,8 +289,9 @@ export function expectElementDrift(value, hint) {
|
|
|
268
289
|
anchor: value,
|
|
269
290
|
nearest: nearestTestid(value, present),
|
|
270
291
|
};
|
|
271
|
-
if (nearestIsAmbiguous(value, present))
|
|
292
|
+
if (nearestIsAmbiguous(value, present)) {
|
|
272
293
|
drift.ambiguous = true;
|
|
294
|
+
}
|
|
273
295
|
return drift;
|
|
274
296
|
}
|
|
275
297
|
/**
|
|
@@ -296,8 +318,9 @@ async function settleTick(session, sleep) {
|
|
|
296
318
|
await new Promise((resolve) => {
|
|
297
319
|
let done = false;
|
|
298
320
|
const finish = () => {
|
|
299
|
-
if (done)
|
|
321
|
+
if (done) {
|
|
300
322
|
return;
|
|
323
|
+
}
|
|
301
324
|
done = true;
|
|
302
325
|
resolve();
|
|
303
326
|
};
|
|
@@ -313,15 +336,34 @@ async function settleTick(session, sleep) {
|
|
|
313
336
|
export async function queryOnce(session, queryArgs) {
|
|
314
337
|
return readQuery(await session.command(ReticleCommand.QUERY, queryArgs));
|
|
315
338
|
}
|
|
316
|
-
export async function resolveQuery(session, queryArgs, sleep, now = Date.now) {
|
|
317
|
-
|
|
339
|
+
export async function resolveQuery(session, queryArgs, sleep, now = Date.now, read = readQuery) {
|
|
340
|
+
return await resolveAnyQuery(session, [queryArgs], sleep, now, read);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Several queries for one control, pooled: each attempt runs every query and joins the refs, so
|
|
344
|
+
* the settle budget is spent once, not once for each query. A role anchor with `alsoRoles` uses it.
|
|
345
|
+
*/
|
|
346
|
+
export async function resolveAnyQuery(session, queries, sleep, now = Date.now, read = readQuery) {
|
|
347
|
+
const look = async () => {
|
|
348
|
+
const reads = [];
|
|
349
|
+
for (const queryArgs of queries) {
|
|
350
|
+
reads.push(read(await session.command(ReticleCommand.QUERY, queryArgs)));
|
|
351
|
+
}
|
|
352
|
+
const hint = reads.find((read) => read.hint !== undefined)?.hint;
|
|
353
|
+
return { refs: reads.flatMap((read) => read.refs), ...(hint === undefined ? {} : { hint }) };
|
|
354
|
+
};
|
|
355
|
+
let last = await look();
|
|
318
356
|
const deadline = now() + ANCHOR_SETTLE_BUDGET_MS;
|
|
319
357
|
for (let attempt = 1; 0 === last.refs.length && now() < deadline && attempt < ANCHOR_SETTLE_MAX_ATTEMPTS; attempt += 1) {
|
|
320
358
|
await settleTick(session, sleep);
|
|
321
|
-
last =
|
|
359
|
+
last = await look();
|
|
322
360
|
}
|
|
323
361
|
return last;
|
|
324
362
|
}
|
|
363
|
+
/** `resolveQuery`'s reader for a nameless role anchor: only the matches with no accessible name. */
|
|
364
|
+
export function readNamelessQuery(result) {
|
|
365
|
+
return { refs: namelessRefs(result) };
|
|
366
|
+
}
|
|
325
367
|
/** Re-resolve a testid anchor. */
|
|
326
368
|
export function resolveTestid(session, value, sleep) {
|
|
327
369
|
return resolveQuery(session, { by: QueryBy.TESTID, value }, sleep);
|
|
@@ -337,20 +379,83 @@ export function componentLabel(anchor) {
|
|
|
337
379
|
}
|
|
338
380
|
/** The value of a step's primary anchor, for labelling the result row. */
|
|
339
381
|
export function anchorLabel(anchor) {
|
|
340
|
-
if (anchor.kind === AnchorKind.TESTID)
|
|
382
|
+
if (anchor.kind === AnchorKind.TESTID) {
|
|
341
383
|
return anchor.value;
|
|
342
|
-
|
|
384
|
+
}
|
|
385
|
+
if (anchor.kind === AnchorKind.SIGNAL) {
|
|
343
386
|
return anchor.name;
|
|
344
|
-
|
|
387
|
+
}
|
|
388
|
+
if (anchor.kind === AnchorKind.COMPONENT) {
|
|
345
389
|
return componentLabel(anchor);
|
|
390
|
+
}
|
|
346
391
|
return anchor.name ?? anchor.role;
|
|
347
392
|
}
|
|
348
393
|
/** QUERY args for a component auto-anchor — source (precise) + component name (coarse) as given. */
|
|
349
394
|
export function componentQueryArgs(anchor) {
|
|
350
395
|
const args = { by: QueryBy.COMPONENT };
|
|
351
|
-
if (anchor.component !== undefined)
|
|
396
|
+
if (anchor.component !== undefined) {
|
|
352
397
|
args['component'] = anchor.component;
|
|
353
|
-
|
|
398
|
+
}
|
|
399
|
+
if (anchor.source !== undefined) {
|
|
354
400
|
args['source'] = anchor.source;
|
|
401
|
+
}
|
|
402
|
+
if (anchor.text !== undefined) {
|
|
403
|
+
args['text'] = anchor.text;
|
|
404
|
+
}
|
|
405
|
+
// An older SDK ignores a role on a component query and answers as before.
|
|
406
|
+
if (anchor.role !== undefined) {
|
|
407
|
+
args['role'] = anchor.role;
|
|
408
|
+
}
|
|
355
409
|
return args;
|
|
356
410
|
}
|
|
411
|
+
/** Query args for a NAMED role anchor — the handle that identifies an instance, not a JSX site. */
|
|
412
|
+
export function roleQueryArgs(anchor) {
|
|
413
|
+
return {
|
|
414
|
+
by: QueryBy.ROLE,
|
|
415
|
+
value: anchor.role,
|
|
416
|
+
...(anchor.name === undefined ? {} : { name: anchor.name }),
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
/** One query for each role the anchor accepts: its own role, then `alsoRoles`. */
|
|
420
|
+
function roleQueries(anchor) {
|
|
421
|
+
return [anchor.role, ...(anchor.alsoRoles ?? [])].map((role) => roleQueryArgs({ ...anchor, role }));
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Resolve a role anchor. ONE function for the first look and the re-resolve after a stale ref: the
|
|
425
|
+
* re-resolve used to count every control of the role, so a nameless anchor's position came back
|
|
426
|
+
* ambiguous on a working page.
|
|
427
|
+
*
|
|
428
|
+
* A nameless anchor's position counts only the nameless controls of its role (an icon-only edit
|
|
429
|
+
* button is the 2nd nameless button of its row, not the 5th button), so it reads only those.
|
|
430
|
+
*/
|
|
431
|
+
export async function resolveRoleAnchor(session, anchor, sleep) {
|
|
432
|
+
// Every role the anchor accepts (`alsoRoles`), pooled in one settle loop; a nameless anchor keeps
|
|
433
|
+
// only the matches with no accessible name.
|
|
434
|
+
const found = await resolveAnyQuery(session, roleQueries(anchor), sleep, Date.now, anchor.name === undefined ? readNamelessQuery : undefined);
|
|
435
|
+
if (found.refs.length > 0 || anchor.name === undefined || anchor.within === undefined) {
|
|
436
|
+
return found;
|
|
437
|
+
}
|
|
438
|
+
const refs = await refsWithin(session, anchor.role, anchor.within);
|
|
439
|
+
return refs.length > 0 ? { refs } : found;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* The controls of `role` inside the recorded container chain, for a named control whose name is
|
|
443
|
+
* gone (a correct build may word a button differently). The chain is walked outermost first, and
|
|
444
|
+
* each container must be the only one of its role in the one before. When a container is not
|
|
445
|
+
* unique, its matches come back, so the caller reports the step as ambiguous and does not act.
|
|
446
|
+
*/
|
|
447
|
+
async function refsWithin(session, role, within) {
|
|
448
|
+
let scope;
|
|
449
|
+
for (const container of [...within].reverse()) {
|
|
450
|
+
const { refs } = await queryOnce(session, {
|
|
451
|
+
by: QueryBy.ROLE,
|
|
452
|
+
value: container,
|
|
453
|
+
...(scope === undefined ? {} : { scope }),
|
|
454
|
+
});
|
|
455
|
+
if (1 !== refs.length) {
|
|
456
|
+
return refs;
|
|
457
|
+
}
|
|
458
|
+
scope = refs[0];
|
|
459
|
+
}
|
|
460
|
+
return (await queryOnce(session, { by: QueryBy.ROLE, value: role, scope })).refs;
|
|
461
|
+
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* Pure: no IO, no clock.
|
|
20
20
|
*/
|
|
21
21
|
import type { FlowFile, FlowStep } from '@reticlehq/core';
|
|
22
|
+
import { z } from 'zod';
|
|
22
23
|
export declare const FlowAssertionGrade: {
|
|
23
24
|
/** At least one step (or the success end-condition) asserts a signal/network consequence. */
|
|
24
25
|
readonly ASSERTED: "asserted";
|
|
@@ -45,9 +46,28 @@ export interface FlowAssertionClassification {
|
|
|
45
46
|
* intent but only presence-only checks is the dangerous case — it claims a goal it cannot verify.
|
|
46
47
|
*/
|
|
47
48
|
intentVerified: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* How many checks of each predicate kind the flow runs, over every step, sub-step and the success
|
|
51
|
+
* state, composites flattened. Absent when the flow checks nothing. A presence-only grade with
|
|
52
|
+
* this list says WHAT is checked, so the grade does not read as "checks nothing" (#1840).
|
|
53
|
+
*/
|
|
54
|
+
checks?: Record<string, number>;
|
|
48
55
|
/** Present for presence-only / assertion-free flows: how to make the flow a real test. */
|
|
49
56
|
warning?: string;
|
|
50
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* The `assertions` block of the `reticle_flow_save` result. Declared here, next to the type it
|
|
60
|
+
* describes, because a validating profile removes a field that the output schema does not declare.
|
|
61
|
+
*/
|
|
62
|
+
export declare const FLOW_ASSERTIONS_OUTPUT: z.ZodOptional<z.ZodObject<{
|
|
63
|
+
grade: z.ZodString;
|
|
64
|
+
hasConsequenceAssertion: z.ZodBoolean;
|
|
65
|
+
totalSteps: z.ZodNumber;
|
|
66
|
+
consequenceSteps: z.ZodNumber;
|
|
67
|
+
weakSteps: z.ZodNumber;
|
|
68
|
+
checks: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
69
|
+
warning: z.ZodOptional<z.ZodString>;
|
|
70
|
+
}, z.core.$strip>>;
|
|
51
71
|
/**
|
|
52
72
|
* A step expect counts the same as a flow-level one, because replay now evaluates the same set.
|
|
53
73
|
*
|