@reticlehq/server 3.9.0 → 3.10.1
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/SKILL.md +13 -12
- 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 -187
- 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/cli-doctor.js +1 -1
- package/dist/command/cli/cli-flow-commands.js +2 -9
- package/dist/command/cli/cli-kill.js +1 -1
- 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 +17 -15
- package/dist/command/cli/cli-parse.js +44 -27
- 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 +58 -29
- package/dist/command/cli/cli-verify.d.ts +11 -2
- package/dist/command/cli/cli-verify.js +27 -8
- 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/doctor-daemon-line.js +6 -3
- package/dist/command/cli/doctor-repairs.js +1 -1
- 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/lifecycle/daemon-lifecycle.d.ts +1 -1
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +1 -1
- package/dist/command/cli/ports/port-holder.d.ts +9 -0
- package/dist/command/cli/ports/port-holder.js +27 -3
- package/dist/command/cli/status/status-lines.d.ts +1 -0
- package/dist/command/cli/status/status-lines.js +22 -0
- package/dist/command/cli/status-fields.d.ts +5 -1
- package/dist/command/cli/status-fields.js +7 -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 +46 -41
- 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 +12 -1
- package/dist/command/daemon/daemon.js +35 -4
- package/dist/command/daemon/lifetime/death-cause.js +21 -2
- package/dist/command/daemon/lifetime/heartbeat.js +7 -2
- package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
- package/dist/command/daemon/lifetime/startup-failure.js +62 -0
- 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-runtime.js +43 -2
- 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 +6 -3
- package/dist/command/update/updater.js +6 -0
- package/dist/command/version/version-skew.js +1 -1
- 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/visual/visual-tools.js +66 -19
- package/dist/index.js +7 -4
- 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/runner-port.d.ts +2 -0
- package/dist/judgement/runs/runner-port.js +23 -5
- 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 +44 -0
- 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 +19 -3
- package/dist/language/flows/recording/tape/recordings.js +71 -5
- package/dist/language/flows/replay.d.ts +19 -5
- package/dist/language/flows/replay.js +99 -7
- 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/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/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/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 +28 -9
- 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 +22 -20
- package/dist/portal/input/real-input.js +41 -31
- package/dist/portal/input/viewport-tools.d.ts +8 -0
- package/dist/portal/input/viewport-tools.js +75 -18
- package/dist/portal/pool/browser-pool.d.ts +11 -29
- package/dist/portal/pool/browser-pool.js +35 -17
- package/dist/portal/pool/context-permissions.d.ts +17 -3
- package/dist/portal/pool/context-permissions.js +36 -5
- 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 +44 -17
- package/dist/portal/pool/lease-page-error.js +147 -5
- package/dist/portal/pool/page-error-journal.d.ts +7 -1
- package/dist/portal/pool/page-error-journal.js +27 -0
- package/dist/portal/pool/playwright-launcher.js +51 -8
- package/dist/portal/pool/pool-contract.d.ts +27 -1
- package/dist/portal/pool/pool-lease.d.ts +36 -1
- package/dist/portal/pool/zero-install.d.ts +8 -0
- package/dist/portal/pool/zero-install.js +23 -1
- package/dist/portal/session/dev-server/dev-command.d.ts +7 -6
- package/dist/portal/session/dev-server/dev-command.js +61 -15
- 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 +67 -25
- package/dist/portal/session/no-session-next-action.d.ts +2 -0
- package/dist/portal/session/no-session-next-action.js +12 -3
- package/dist/portal/session/no-session-watch.d.ts +6 -0
- package/dist/portal/session/no-session-watch.js +38 -3
- 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 +174 -63
- package/dist/start-wiring.d.ts +2 -1
- package/dist/start-wiring.js +2 -1
- package/dist/surface/mcp/mcp.js +3 -2
- 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 +1 -1
- 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/after-match.d.ts +4 -1
- package/dist/surface/tools/act/after-match.js +8 -1
- 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 +15 -1
- package/dist/surface/tools/act/resolve-target.js +24 -1
- package/dist/surface/tools/act/sequence-grade.d.ts +12 -0
- package/dist/surface/tools/act/sequence-grade.js +37 -26
- 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 +78 -50
- package/dist/surface/tools/act-tools.d.ts +1 -13
- package/dist/surface/tools/act-tools.js +74 -86
- 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 +16 -37
- package/dist/surface/tools/browser-tools.js +12 -6
- 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 +9 -2
- package/dist/surface/tools/error-recovery.d.ts +12 -1
- package/dist/surface/tools/error-recovery.js +91 -21
- package/dist/surface/tools/explore-tools.js +1 -1
- package/dist/surface/tools/invoke-tool.js +3 -2
- 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 +35 -1
- package/dist/surface/tools/lease-hint.js +42 -0
- package/dist/surface/tools/lease-readiness.d.ts +26 -0
- package/dist/surface/tools/lease-readiness.js +79 -0
- package/dist/surface/tools/lease-seed.d.ts +14 -1
- package/dist/surface/tools/lease-seed.js +35 -3
- package/dist/surface/tools/lease-tools.d.ts +3 -41
- package/dist/surface/tools/lease-tools.js +62 -110
- 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/session-ports.d.ts +14 -0
- package/dist/surface/tools/session-ports.js +53 -0
- package/dist/surface/tools/tool-kit.d.ts +6 -0
- package/dist/surface/tools/tool-kit.js +2 -3
- package/dist/surface/tools/tool-surface.js +0 -1
- package/dist/surface/tools/tools.js +28 -5
- 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 +1 -0
- package/dist/telemetry/tool-refused.d.ts +6 -3
- package/dist/telemetry/tool-refused.js +8 -3
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +1 -1
- 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 +1 -1
- package/docs/cli/grade.mdx +97 -0
- package/docs/cli/init.mdx +2 -2
- package/docs/cli/license.mdx +9 -7
- package/docs/cli/restart.mdx +7 -5
- package/docs/cli/rollback.mdx +5 -5
- 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 +33 -23
- package/docs/deploy-checks.md +1 -1
- package/docs/desktop.mdx +1 -1
- package/docs/enterprise.md +2 -2
- package/docs/environment.mdx +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 +1 -1
- package/docs/license-activation.mdx +3 -3
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages.mdx +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 +2 -2
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/verify.mdx +4 -1
- package/docs/troubleshooting.mdx +30 -1
- package/docs/usage.md +17 -14
- package/package.json +11 -14
- package/dist/features/exhaust/exhaust-run.d.ts +0 -33
- package/dist/features/exhaust/exhaust-run.js +0 -82
|
@@ -43,17 +43,53 @@ the first flow. It never drives: the Harness is opt-in and runs at any instrumen
|
|
|
43
43
|
const VERIFY_DETAIL = `Drives the URL and verifies the saved flows. Exit 0 means every one passed.
|
|
44
44
|
|
|
45
45
|
--explore [--persona <who>] no saved flows? let Reticle drive the app and record them
|
|
46
|
+
--against <reference-url> compare the URL with a reference build: drive both with the same
|
|
47
|
+
actions, and report each difference in what users see (exit 1)
|
|
46
48
|
--select <label> repeatable: verify only flows carrying these labels
|
|
47
49
|
--results-json <file> also write one verdict per journey as JSON, for CI
|
|
48
50
|
--confirm-dangerous this run may press what the replay guard holds as destructive
|
|
51
|
+
--require-start-control fail a flow whose first control is not on its freshly loaded start page
|
|
52
|
+
--sweep judge every step, also after one fails (the results score counts them all)
|
|
49
53
|
--storage-state <file> start signed in
|
|
50
|
-
--session-id <id>, --timeout
|
|
54
|
+
--session-id <id>, --timeout <ms>, --headed, --port N
|
|
51
55
|
--expect '<json predicate>' | --expect-file <path>
|
|
52
56
|
one verdict, no saved flows needed. Asks a running daemon,
|
|
53
57
|
never starts one, and waits up to --timeout ms (default 4000)
|
|
54
58
|
for it to come true. Exit 0 only on verified:"yes". For an
|
|
55
59
|
action --expect cannot do, use the HTTP MCP transport:
|
|
56
60
|
https://docs.reticle.sh/http-transport.md`;
|
|
61
|
+
const GRADE_DETAIL = `Grades a finished run of an app from snapshots of its stored data. It needs no
|
|
62
|
+
daemon and no browser.
|
|
63
|
+
|
|
64
|
+
grade snapshot --state <file> --out <file> [--url <url>]
|
|
65
|
+
read the declared state sources (and the page the run ended on) into a snapshot
|
|
66
|
+
grade judge --reference <file> --again <file> --candidate <file>
|
|
67
|
+
[--expect <file>] [--answer <text>] [--json]
|
|
68
|
+
compare the candidate snapshot with two snapshots of the correct run. A line that
|
|
69
|
+
differs between the two correct runs is ignored. --expect declares a source whose
|
|
70
|
+
rows are the correct answer, read now. Exit 0 = pass, 1 = fail, 2 = unverified
|
|
71
|
+
grade tables --list <command> --rows <template> [--container <name>] [--skip <prefix,...>]
|
|
72
|
+
--out <file>
|
|
73
|
+
write a declaration with one source for each table; {table} in the template is
|
|
74
|
+
the table name; --skip leaves out the tables whose name starts with a prefix
|
|
75
|
+
grade suggest --state <file> --reference <file> --again <file> --out <file>
|
|
76
|
+
keep only the sources that read the same on two correct runs`;
|
|
77
|
+
const COMPARE_DETAIL = `Drives two running builds of one app the same way, for example main and a pull request
|
|
78
|
+
preview, and reports each difference in what a person sees and in the requests and console
|
|
79
|
+
errors the app made. Install nothing in the app.
|
|
80
|
+
|
|
81
|
+
It plans every action on every control of the reference, drives the plan on the reference two
|
|
82
|
+
times and on the candidate one time, and ignores what changes between the two reference runs.
|
|
83
|
+
Point it at builds whose data you can change: it presses every button it finds.
|
|
84
|
+
|
|
85
|
+
--base <url> the build before the change: tag each difference as a regression, a missed
|
|
86
|
+
target or a change, and print a score
|
|
87
|
+
--state <file> a JSON file { "sources": [...] } that declares how to read the app's stored
|
|
88
|
+
data (kind command, http or file). Compare reads it after each sequence on
|
|
89
|
+
every build, so a change to a record that no page shows is a difference.
|
|
90
|
+
Start each row with the record id, so a changed row names its record
|
|
91
|
+
--json print one JSON object
|
|
92
|
+
Exit 0 = same, 1 = different, 2 = the run proves nothing (a lost browser session, or an error).`;
|
|
57
93
|
const BROWSER_NOTE = `The browser Reticle opens for your agent is shown so you can watch the run. --headless hides
|
|
58
94
|
it; CI, RETICLE_HEADLESS=1, or a Linux machine with no display hides it too.`;
|
|
59
95
|
/** What `--http` starts. MCP is already on the bridge port; this flag is the verify endpoint. */
|
|
@@ -96,16 +132,32 @@ const COMMANDS = [
|
|
|
96
132
|
group: HelpGroup.CHECK,
|
|
97
133
|
detail: VERIFY_DETAIL,
|
|
98
134
|
},
|
|
135
|
+
{
|
|
136
|
+
name: 'compare',
|
|
137
|
+
synopsis: 'compare <reference-url> <candidate-url> [--base <url>] [--state <file>] [--json] [--report <file>]',
|
|
138
|
+
summary: 'Drive two builds the same way and list what users would see change',
|
|
139
|
+
group: HelpGroup.CHECK,
|
|
140
|
+
detail: COMPARE_DETAIL,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'grade',
|
|
144
|
+
synopsis: 'grade <snapshot|judge|tables|suggest> [flags]',
|
|
145
|
+
summary: "Grade a finished run from snapshots of the app's stored data",
|
|
146
|
+
group: HelpGroup.MORE,
|
|
147
|
+
detail: GRADE_DETAIL,
|
|
148
|
+
},
|
|
99
149
|
{
|
|
100
150
|
name: 'gate',
|
|
101
|
-
synopsis: 'gate [--since <ref>] [--accept-coverage] [--hook] [file...]',
|
|
151
|
+
synopsis: 'gate [--list] [--since <ref>] [--accept-coverage] [--hook] [file...]',
|
|
102
152
|
summary: 'Fail unless passing checks cover the flows your changes touch',
|
|
103
153
|
group: HelpGroup.CHECK,
|
|
154
|
+
detail: '--list names the saved flows your changes touch, and checks nothing.',
|
|
104
155
|
},
|
|
105
156
|
{
|
|
106
157
|
name: 'status',
|
|
107
158
|
synopsis: 'status [--json] [--port N]',
|
|
108
159
|
summary: 'Is Reticle running, is your app connected, are you signed in',
|
|
160
|
+
detail: 'Shows the enterprise license when a key is set. --json always carries it.',
|
|
109
161
|
group: HelpGroup.MACHINE,
|
|
110
162
|
},
|
|
111
163
|
{
|
|
@@ -132,17 +184,12 @@ const COMMANDS = [
|
|
|
132
184
|
"With --force, frees the port by the Reticle daemon LISTENING on it, never the agent's MCP\n" +
|
|
133
185
|
'proxy, and refuses a listener that is not Reticle.',
|
|
134
186
|
},
|
|
135
|
-
{
|
|
136
|
-
name: 'restart',
|
|
137
|
-
synopsis: 'restart [--force] [--port N]',
|
|
138
|
-
summary: 'Stop Reticle, then start it and wait until it is listening',
|
|
139
|
-
group: HelpGroup.MACHINE,
|
|
140
|
-
},
|
|
141
187
|
{
|
|
142
188
|
name: 'update',
|
|
143
|
-
synopsis: 'update',
|
|
189
|
+
synopsis: 'update [--rollback]',
|
|
144
190
|
summary: 'Install the latest version and restart',
|
|
145
191
|
group: HelpGroup.MACHINE,
|
|
192
|
+
detail: '--rollback restores the previous version and restarts.',
|
|
146
193
|
},
|
|
147
194
|
{
|
|
148
195
|
name: 'logout',
|
|
@@ -170,7 +217,7 @@ const COMMANDS = [
|
|
|
170
217
|
synopsis: 'sync [--watch] [--move-history]',
|
|
171
218
|
summary: 'Send local runs to the dashboard and collect decisions',
|
|
172
219
|
group: HelpGroup.MORE,
|
|
173
|
-
detail: '
|
|
220
|
+
detail: ' --watch keep sending as runs are written\n' +
|
|
174
221
|
' --move-history also send runs, flows and records made under the previous link',
|
|
175
222
|
},
|
|
176
223
|
{
|
|
@@ -191,12 +238,6 @@ const COMMANDS = [
|
|
|
191
238
|
summary: 'What this project sends to the dashboard',
|
|
192
239
|
group: HelpGroup.MORE,
|
|
193
240
|
},
|
|
194
|
-
{
|
|
195
|
-
name: 'affected',
|
|
196
|
-
synopsis: 'affected [--since <ref>] [file...]',
|
|
197
|
-
summary: 'Which saved flows must re-verify for the changed files',
|
|
198
|
-
group: HelpGroup.MORE,
|
|
199
|
-
},
|
|
200
241
|
{
|
|
201
242
|
name: 'report',
|
|
202
243
|
synopsis: 'report [--session <id>] [--hook]',
|
|
@@ -236,7 +277,7 @@ const COMMANDS = [
|
|
|
236
277
|
synopsis: 'serve [--port N] [--drive <url>] [--headless] [--headed] [--http] [--http-port N] [--http-token T]',
|
|
237
278
|
summary: 'Start the daemon in the background; returns once it answers',
|
|
238
279
|
group: HelpGroup.MORE,
|
|
239
|
-
detail: DAEMON_DETAIL
|
|
280
|
+
detail: `--restart [--force] stops Reticle, then starts it and waits until it listens.\n\n${DAEMON_DETAIL}`,
|
|
240
281
|
},
|
|
241
282
|
{
|
|
242
283
|
name: 'drive',
|
|
@@ -250,18 +291,6 @@ const COMMANDS = [
|
|
|
250
291
|
summary: 'Register the MCP server with the coding agents on this machine',
|
|
251
292
|
group: HelpGroup.MORE,
|
|
252
293
|
},
|
|
253
|
-
{
|
|
254
|
-
name: 'license',
|
|
255
|
-
synopsis: 'license',
|
|
256
|
-
summary: 'Show the enterprise license status',
|
|
257
|
-
group: HelpGroup.MORE,
|
|
258
|
-
},
|
|
259
|
-
{
|
|
260
|
-
name: 'rollback',
|
|
261
|
-
synopsis: 'rollback',
|
|
262
|
-
summary: 'Restore the previous version and restart',
|
|
263
|
-
group: HelpGroup.MORE,
|
|
264
|
-
},
|
|
265
294
|
{
|
|
266
295
|
name: 'version',
|
|
267
296
|
synopsis: 'version',
|
|
@@ -39,6 +39,11 @@ export interface VerifyConnection {
|
|
|
39
39
|
* only the connection can read one.
|
|
40
40
|
*/
|
|
41
41
|
listFlows(select?: readonly string[]): Promise<string[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Each named flow's saved step count, for the results score: without it a replay that stopped at
|
|
44
|
+
* step 2 of 10 reads as a 2-step journey. Optional; a name it cannot read is left out.
|
|
45
|
+
*/
|
|
46
|
+
plannedSteps?(names: readonly string[]): Promise<ReadonlyMap<string, number>>;
|
|
42
47
|
/**
|
|
43
48
|
* Drive the app with a model and save what it drove. Present only when a model is configured.
|
|
44
49
|
*
|
|
@@ -190,8 +195,8 @@ export declare function routeVerify(args: {
|
|
|
190
195
|
hasPredicate: boolean;
|
|
191
196
|
/**
|
|
192
197
|
* True when the caller asked for something only a browser of OUR OWN can do: `--headed` or
|
|
193
|
-
* `--storage-state
|
|
194
|
-
* silently ignore the request.
|
|
198
|
+
* `--storage-state`, or a rule its replay tool does not take (`--require-start-control`). The
|
|
199
|
+
* running daemon's tab can honour none of them, so replaying there would silently ignore the request.
|
|
195
200
|
*/
|
|
196
201
|
wantsOwnBrowser?: boolean;
|
|
197
202
|
/**
|
|
@@ -230,6 +235,10 @@ export declare function handleVerify(parsed: {
|
|
|
230
235
|
resultsJson?: string;
|
|
231
236
|
/** Acknowledge the replay guard's destructive steps ("Place order") for this run only. */
|
|
232
237
|
confirmDangerous?: boolean;
|
|
238
|
+
/** Fail a flow whose step 1 control is missing on its freshly loaded start page. */
|
|
239
|
+
requireStartControl?: boolean;
|
|
240
|
+
/** Judge every step, also after one fails. */
|
|
241
|
+
sweep?: boolean;
|
|
233
242
|
},
|
|
234
243
|
/**
|
|
235
244
|
* Called once, after the last verdict line and before the exit. `cli.ts` passes the CI line:
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { cliRunAuthor } from '../../judgement/runs/verification-sync.js';
|
|
13
13
|
import { join, basename } from 'node:path';
|
|
14
14
|
import { readFileSync } from 'node:fs';
|
|
15
|
+
import { noSessionMessage } from './verify-no-session.js';
|
|
15
16
|
import { runAdhocVerdict } from './adhoc-verdict.js';
|
|
16
17
|
import { callerArtifactRoot } from '../../memory/project/link-directory.js';
|
|
17
18
|
import { verifyResults } from './verify-results.js';
|
|
@@ -59,9 +60,6 @@ const DEFAULT_PROJECT_NAME = 'app';
|
|
|
59
60
|
export function verifyProjectName(linked, configured, folder) {
|
|
60
61
|
return linked ?? configured ?? (folder || DEFAULT_PROJECT_NAME);
|
|
61
62
|
}
|
|
62
|
-
const MSG_NO_SESSION = 'No app connected: Reticle drove the URL but no @reticlehq/browser session dialed back.\n' +
|
|
63
|
-
' Make sure the SDK is in the build and reticle.connect() runs on the preview page' +
|
|
64
|
-
' (for a non-localhost preview: allowNonLocalhost + a pairing token).';
|
|
65
63
|
const MSG_NO_FLOWS = 'No saved flows to verify (.reticle/flows is empty), so refusing to report a pass for verifying nothing.\n' +
|
|
66
64
|
' Flows are recorded interactively by an agent (reticle_record{action:"start"} → act → reticle_flow_save via the\n' +
|
|
67
65
|
' MCP tools), then committed to .reticle/flows/. In CI, check those files in and re-run `reticle verify`.\n' +
|
|
@@ -104,7 +102,7 @@ export async function runVerify(args, outer) {
|
|
|
104
102
|
try {
|
|
105
103
|
const ready = await conn.sessionReady(args.timeoutMs);
|
|
106
104
|
if (!ready) {
|
|
107
|
-
ports.fail(
|
|
105
|
+
ports.fail(noSessionMessage(args.timeoutMs));
|
|
108
106
|
ports.exit(EXIT_FAIL);
|
|
109
107
|
return;
|
|
110
108
|
}
|
|
@@ -182,7 +180,7 @@ export async function runVerify(args, outer) {
|
|
|
182
180
|
// The run's OWN injected timestamp — no wall clock is read in this path (rule 7).
|
|
183
181
|
[{ phase: VerifyPhase.PUSHED, at: run.createdAt }], cloud, cloudFetch).catch(() => undefined);
|
|
184
182
|
if (args.resultsJson !== undefined && ports.writeResults !== undefined) {
|
|
185
|
-
await ports.writeResults(args.resultsJson, `${JSON.stringify(verifyResults(run, pushed), null, 2)}\n`);
|
|
183
|
+
await ports.writeResults(args.resultsJson, `${JSON.stringify(verifyResults(run, pushed, (await conn.plannedSteps?.(run.flows.map((f) => f.name))) ?? new Map()), null, 2)}\n`);
|
|
186
184
|
}
|
|
187
185
|
ports.out(renderRunReport(run));
|
|
188
186
|
ports.exit(run.verdict.status === VerdictStatus.PASS ? EXIT_PASS : EXIT_FAIL);
|
|
@@ -352,9 +350,24 @@ async function openLiveConnection(opts) {
|
|
|
352
350
|
});
|
|
353
351
|
const deps = buildVerifyDeps(running, opts.reticleRoot, opts.now);
|
|
354
352
|
const harnessEnv = await withLinkedCredential(deps, process.env);
|
|
355
|
-
const runner = new ReticleRunner(createRunnerPort(deps, opts.sessionId, {
|
|
353
|
+
const runner = new ReticleRunner(createRunnerPort(deps, opts.sessionId, {
|
|
354
|
+
confirmDangerous: true === opts.confirmDangerous,
|
|
355
|
+
requireStartControl: true === opts.requireStartControl,
|
|
356
|
+
sweep: true === opts.sweep,
|
|
357
|
+
}));
|
|
356
358
|
return {
|
|
357
359
|
sessionReady: (timeoutMs) => waitForSession(deps.sessions, timeoutMs, opts.now),
|
|
360
|
+
plannedSteps: async (names) => {
|
|
361
|
+
const planned = new Map();
|
|
362
|
+
for (const name of names) {
|
|
363
|
+
const loaded = await deps.flows.load(name).catch(() => undefined);
|
|
364
|
+
if (true === loaded?.ok) {
|
|
365
|
+
// A sequence counts its sub-steps, as the score does.
|
|
366
|
+
planned.set(name, loaded.value.steps.reduce((n, step) => n + (step.steps?.length ?? 1), 0));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return planned;
|
|
370
|
+
},
|
|
358
371
|
listFlows: async (select) => select === undefined || 0 === select.length
|
|
359
372
|
? deps.flows.list()
|
|
360
373
|
: (await resolveSuiteSelection(deps, projectOnly(undefined), { labels: [...select] })).run,
|
|
@@ -476,7 +489,7 @@ export function expectNeedsDaemonMessage(port, presence) {
|
|
|
476
489
|
export function daemonLacksKeyMessage(port) {
|
|
477
490
|
return (`${ReticleEnv.API_KEY} is set in this shell, but the daemon on port ${String(port)} was ` +
|
|
478
491
|
'started without it, and the daemon is what asks the platform to drive. Restart it from this ' +
|
|
479
|
-
`shell so it inherits the key: npx @reticlehq/server restart --port ${String(port)}`);
|
|
492
|
+
`shell so it inherits the key: npx @reticlehq/server serve --restart --port ${String(port)}`);
|
|
480
493
|
}
|
|
481
494
|
/** What `verify` does next, once the options and the state of the port are both known. */
|
|
482
495
|
export const VerifyRoute = {
|
|
@@ -567,6 +580,8 @@ beforeExit = () => undefined) {
|
|
|
567
580
|
...(parsed.storageState !== undefined ? { storageState: parsed.storageState } : {}),
|
|
568
581
|
...(parsed.sessionId !== undefined ? { sessionId: parsed.sessionId } : {}),
|
|
569
582
|
...(true === parsed.confirmDangerous ? { confirmDangerous: true } : {}),
|
|
583
|
+
...(true === parsed.requireStartControl ? { requireStartControl: true } : {}),
|
|
584
|
+
...(true === parsed.sweep ? { sweep: true } : {}),
|
|
570
585
|
}),
|
|
571
586
|
out: (line) => process.stdout.write(`${line}\n`),
|
|
572
587
|
fail: (line) => process.stderr.write(`${line}\n`),
|
|
@@ -593,7 +608,11 @@ beforeExit = () => undefined) {
|
|
|
593
608
|
const presence = await probePresence(port, { tcpOpen: probeDaemon, status: fetchStatus });
|
|
594
609
|
const plan = routeVerify({
|
|
595
610
|
hasPredicate: expectation !== undefined,
|
|
596
|
-
|
|
611
|
+
// `--require-start-control` too: the daemon's replay tool does not take that rule.
|
|
612
|
+
wantsOwnBrowser: !parsed.headless ||
|
|
613
|
+
parsed.storageState !== undefined ||
|
|
614
|
+
true === parsed.requireStartControl ||
|
|
615
|
+
true === parsed.sweep,
|
|
597
616
|
explore: true === parsed.explore || parsed.persona !== undefined,
|
|
598
617
|
hasSavedFlows: 0 <
|
|
599
618
|
(await new FlowStore(createNodeFileSystem(), reticleRoot, { now: Date.now }).list()).length,
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* `credentials.json` (per-project api keys from `reticle link`). The non-secret repo binding + sync policy
|
|
6
6
|
* is `<repo>/.reticle/cloud.json`. Auth for a command = `RETICLE_API_KEY` env (agent) OR the login token.
|
|
7
7
|
*/
|
|
8
|
-
import { CreatedProjectSchema } from './project-name.js';
|
|
9
8
|
import { keyRefusalAtLastSync } from '../../memory/project/sync-status.js';
|
|
10
9
|
import { ReticleEnv, apiKeyFrom } from '@reticlehq/core';
|
|
11
10
|
import { writeHarnessSwitch } from '../../memory/cloud/harness-switch.js';
|
|
@@ -24,11 +23,10 @@ import { RETICLE_CONFIG_BASENAME } from './ports/resolve/cli-port.js';
|
|
|
24
23
|
import { cmdLink, validateKey } from './cloud-link.js';
|
|
25
24
|
// `link` moved to its own module; the CLI entry still imports it from here.
|
|
26
25
|
export { cmdLink } from './cloud-link.js';
|
|
27
|
-
import { normalizeUrl } from './auth/cloud-session.js';
|
|
28
26
|
import { chooseWorkspace, cmdLogin, cmdLogout } from './cloud-login.js';
|
|
29
27
|
import { cmdTry } from './try-command.js';
|
|
30
28
|
import { CLOUD_COMMANDS } from './cli-parse-grammar.js';
|
|
31
|
-
import { api, baseUrl,
|
|
29
|
+
import { api, baseUrl, emit, err, flags, hint, readJson, readSession, readSessionFor, RETICLE_DIR, } from './cloud-kit.js';
|
|
32
30
|
import { syncRequest } from '../../memory/cloud/cloud-sync.js';
|
|
33
31
|
import { GateExit, gateExitWith } from './answers/gate-exit.js';
|
|
34
32
|
import { brokenOf } from './known-regression.js';
|
|
@@ -36,7 +34,7 @@ import { describeUnsyncedBriefly } from '../../memory/cloud/unsynced-roots.js';
|
|
|
36
34
|
import { machineUnsyncedRoots } from '../../memory/project/sync-status.js';
|
|
37
35
|
import { runSyncCycle } from '../../memory/cloud/sync-cycle.js';
|
|
38
36
|
import { describeSync } from '../../memory/cloud/sync-describe.js';
|
|
39
|
-
import { diskSink, diskSource, policyScreenshots,
|
|
37
|
+
import { diskSink, diskSource, policyScreenshots, readCloudState, } from '../../memory/cloud/sync-disk.js';
|
|
40
38
|
import { fetchPlatformRun } from '../../features/harness/platform/platform-drives.js';
|
|
41
39
|
import { SERVER_VERSION } from '../version/identity/server-version.js';
|
|
42
40
|
/**
|
|
@@ -82,48 +80,6 @@ const linkedRoot = () => {
|
|
|
82
80
|
const onlyHere = !existsSync(join(project, CLOUD_LINK_FILE)) && existsSync(join(here, CLOUD_LINK_FILE));
|
|
83
81
|
return onlyHere ? here : project;
|
|
84
82
|
};
|
|
85
|
-
/**
|
|
86
|
-
* `reticle whoami` — the one call an agent (or a confused human) makes to know its state: who am I logged
|
|
87
|
-
* in as, and is THIS repo attached to a cloud project (and with what sync policy / verify mode)?
|
|
88
|
-
*/
|
|
89
|
-
const cmdWhoami = async () => {
|
|
90
|
-
const session = await readSession();
|
|
91
|
-
const fs = createNodeFileSystem();
|
|
92
|
-
const cloud = await resolveProjectCloud(fs, linkedRoot(), homedir(), process.env);
|
|
93
|
-
/*
|
|
94
|
-
* The sync half of "what is my state". Without it the honest answer to "why does the dashboard
|
|
95
|
-
* look old?" was to go and read a JSON file — and the two failure modes a person actually hits
|
|
96
|
-
* (nothing has synced yet, and the last attempt errored) looked identical from out here.
|
|
97
|
-
*/
|
|
98
|
-
const reticleRoot = linkedRoot();
|
|
99
|
-
const state = readCloudState(reticleRoot);
|
|
100
|
-
const decisions = Object.keys(readCloudIssues(reticleRoot).triage).length;
|
|
101
|
-
emit({
|
|
102
|
-
loggedInAs: session?.orgName ?? null,
|
|
103
|
-
sync: {
|
|
104
|
-
lastPushAt: state.lastPushAt ?? null,
|
|
105
|
-
lastPullAt: state.lastPullAt ?? null,
|
|
106
|
-
/** Present only when the machine is behind BECAUSE something failed, which is the useful case. */
|
|
107
|
-
...(state.lastError === undefined ? {} : { lastError: state.lastError }),
|
|
108
|
-
/** Decisions collected from the dashboard and readable locally. */
|
|
109
|
-
decisionsHeld: decisions,
|
|
110
|
-
neverSynced: state.lastPullAt === undefined,
|
|
111
|
-
},
|
|
112
|
-
repo: {
|
|
113
|
-
attached: cloud.config !== null,
|
|
114
|
-
projectId: cloud.projectId,
|
|
115
|
-
url: cloud.config?.url ?? null,
|
|
116
|
-
sync: cloud.policy,
|
|
117
|
-
verify: cloud.verify,
|
|
118
|
-
},
|
|
119
|
-
});
|
|
120
|
-
// Signed out, `link` refuses with "run reticle login first"; `connect` does both in one go.
|
|
121
|
-
if (null === cloud.config)
|
|
122
|
-
hint(null === session
|
|
123
|
-
? 'this repo is not attached — run `reticle connect` (signs in and links in one step)'
|
|
124
|
-
: 'this repo is not attached — run `reticle connect`');
|
|
125
|
-
return 0;
|
|
126
|
-
};
|
|
127
83
|
/**
|
|
128
84
|
* Who is signed in and which project this repo is linked to, for `reticle status`. Local files
|
|
129
85
|
* only: status must answer with no network.
|
|
@@ -146,58 +102,6 @@ export const accountStatusFields = async () => {
|
|
|
146
102
|
...(refused === undefined ? {} : { keyRefused: refused }),
|
|
147
103
|
};
|
|
148
104
|
};
|
|
149
|
-
/** `reticle project ls` / `reticle project create <name>` — key- or session-authed. */
|
|
150
|
-
const cmdProject = async (argv) => {
|
|
151
|
-
const active = await readSession();
|
|
152
|
-
const url = baseUrl(active);
|
|
153
|
-
const session = await readSessionFor(url);
|
|
154
|
-
const token = bearer(session);
|
|
155
|
-
if (null === token) {
|
|
156
|
-
// Name BOTH hosts when there is a session for a different one. "Run reticle login" on its own is
|
|
157
|
-
// baffling to somebody who just did — the useful fact is that they logged in somewhere else.
|
|
158
|
-
err(null !== active && normalizeUrl(active.url) !== url
|
|
159
|
-
? `signed in to ${normalizeUrl(active.url)}, but this command targets ${url} — run \`reticle connect --url ${url}\`, or set RETICLE_API_KEY`
|
|
160
|
-
: `not signed in to ${url} — run \`reticle connect --url ${url}\`, or set RETICLE_API_KEY`);
|
|
161
|
-
return 2;
|
|
162
|
-
}
|
|
163
|
-
const sub = argv[0];
|
|
164
|
-
if ('ls' === sub) {
|
|
165
|
-
emit(await api('GET', `${url}/v1/projects`, token));
|
|
166
|
-
return 0;
|
|
167
|
-
}
|
|
168
|
-
if ('create' === sub) {
|
|
169
|
-
const name = argv.slice(1).join(' ').trim();
|
|
170
|
-
if (0 === name.length) {
|
|
171
|
-
err('usage: reticle project create <name>');
|
|
172
|
-
return 2;
|
|
173
|
-
}
|
|
174
|
-
const created = CreatedProjectSchema.parse(await api('POST', `${url}/v1/projects`, token, { name }));
|
|
175
|
-
emit(created);
|
|
176
|
-
hint(`next: \`reticle connect --project ${created.projectId}\` to bind this repo`);
|
|
177
|
-
return 0;
|
|
178
|
-
}
|
|
179
|
-
if ('rename' === sub) {
|
|
180
|
-
const id = argv[1];
|
|
181
|
-
const name = argv.slice(2).join(' ').trim();
|
|
182
|
-
if (id === undefined || 0 === name.length) {
|
|
183
|
-
err('usage: reticle project rename <projectId> <new name>');
|
|
184
|
-
return 2;
|
|
185
|
-
}
|
|
186
|
-
emit(await api('PATCH', `${url}/v1/projects/${encodeURIComponent(id)}`, token, { name }));
|
|
187
|
-
return 0;
|
|
188
|
-
}
|
|
189
|
-
if ('rm' === sub || 'delete' === sub) {
|
|
190
|
-
const id = argv[1];
|
|
191
|
-
if (id === undefined) {
|
|
192
|
-
err('usage: reticle project rm <projectId>');
|
|
193
|
-
return 2;
|
|
194
|
-
}
|
|
195
|
-
emit(await api('DELETE', `${url}/v1/projects/${encodeURIComponent(id)}`, token));
|
|
196
|
-
return 0;
|
|
197
|
-
}
|
|
198
|
-
err('usage: reticle project <ls|create <name>|rename <id> <name>|rm <id>>');
|
|
199
|
-
return 2;
|
|
200
|
-
};
|
|
201
105
|
const NO_INSTALL = '--no-install';
|
|
202
106
|
/** Whether `reticle connect`'s init half installs the packages: not under `--no-install`. */
|
|
203
107
|
export const connectInstalls = (argv) => !argv.includes(NO_INSTALL);
|
|
@@ -577,10 +481,6 @@ export const runCloudCommand = async (argv) => {
|
|
|
577
481
|
const [cmd, ...rest] = argv;
|
|
578
482
|
try {
|
|
579
483
|
switch (cmd) {
|
|
580
|
-
case 'login':
|
|
581
|
-
// The linker is passed in rather than imported by cloud-login: login ends by linking and
|
|
582
|
-
// link needs a session, so the cycle is broken at this one call site.
|
|
583
|
-
return await cmdLogin(rest, cmdLink);
|
|
584
484
|
case 'connect':
|
|
585
485
|
return await cmdConnect(rest);
|
|
586
486
|
case 'try':
|
|
@@ -590,29 +490,12 @@ export const runCloudCommand = async (argv) => {
|
|
|
590
490
|
});
|
|
591
491
|
case 'logout':
|
|
592
492
|
return await cmdLogout(rest);
|
|
593
|
-
case 'whoami':
|
|
594
|
-
return await cmdWhoami();
|
|
595
|
-
case 'project':
|
|
596
|
-
return await cmdProject(rest);
|
|
597
|
-
case 'link':
|
|
598
|
-
return await cmdLink(rest);
|
|
599
493
|
case 'config':
|
|
600
494
|
return await cmdConfig(rest);
|
|
601
|
-
// `push` is the name people type, so it takes `--watch` too: the help and the README say so.
|
|
602
|
-
case 'push':
|
|
603
|
-
return await cmdSync(rest);
|
|
604
495
|
case 'sync':
|
|
605
496
|
return await cmdSync(rest);
|
|
606
497
|
case 'runs':
|
|
607
498
|
return await cmdRunsFamily(rest);
|
|
608
|
-
case 'issues':
|
|
609
|
-
return await cmdIssues(rest);
|
|
610
|
-
case 'memory':
|
|
611
|
-
return await cmdMemory(rest);
|
|
612
|
-
case 'regression':
|
|
613
|
-
return await cmdRegression();
|
|
614
|
-
case 'share':
|
|
615
|
-
return await cmdShare(rest);
|
|
616
499
|
default:
|
|
617
500
|
err(`unknown cloud command '${cmd ?? ''}'`);
|
|
618
501
|
return 2;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export declare const leaseFailedLine: (url: string) => string;
|
|
2
|
+
/**
|
|
3
|
+
* Open `url` in a Reticle-leased browser unconditionally: `{ leased }` names the lease, which the
|
|
4
|
+
* caller hands to `releaseLease`; `{ failed }` is the lease's own refusal, as lines to print.
|
|
5
|
+
* `init`'s connect proof on a machine with no system browser takes this path directly.
|
|
6
|
+
*/
|
|
7
|
+
export declare function acquireLease(caller: ToolCaller, url: string,
|
|
8
|
+
/** Open it in a window somebody can watch, whatever the daemon was started as. */
|
|
9
|
+
headed?: boolean,
|
|
10
|
+
/** The `.reticle` the lease's runs belong in: the caller's project, not wherever the page resolves. */
|
|
11
|
+
root?: string): Promise<{
|
|
12
|
+
leased: string;
|
|
13
|
+
zeroInstall: boolean;
|
|
14
|
+
} | {
|
|
15
|
+
failed: string[];
|
|
16
|
+
}>;
|
|
17
|
+
/** Hand back a lease `leaseIfNoTab` opened. Best-effort: an expired lease is already gone. */
|
|
18
|
+
export declare function releaseLease(caller: ToolCaller, sessionId: string): Promise<void>;
|
|
19
|
+
/** The narrow slice of an MCP client this needs — one call, then close. */
|
|
20
|
+
export interface ToolCaller {
|
|
21
|
+
call(name: string, args: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export declare function endpointFor(port: number, token: string | undefined): URL;
|
|
25
|
+
export declare function connectOverSse(endpoint: URL): Promise<ToolCaller>;
|
|
26
|
+
/**
|
|
27
|
+
* The report object an MCP tool result carries, or undefined when the shape is not one.
|
|
28
|
+
*
|
|
29
|
+
* `field` is the key that makes a payload the report this caller wants: `verified` for a one-shot
|
|
30
|
+
* verdict, `status` for a suite. It is a parameter rather than a fixed name because the suite path
|
|
31
|
+
* reproduced this function's own incident the moment it reused it — the headline printed
|
|
32
|
+
* `unverifiable` while the JSON under it said `"status":"fail"` with 74 failing flows, because a
|
|
33
|
+
* suite report has no `verified` key and the text reader would only accept one that did.
|
|
34
|
+
*/
|
|
35
|
+
export declare function verdictOf(result: unknown, field?: string): Record<string, unknown> | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* The tool's own words when it refused, rather than the envelope carrying them.
|
|
38
|
+
*
|
|
39
|
+
* A refusal from these tools is the most useful text they produce — it names the cause and the next
|
|
40
|
+
* action — and printing the MCP result object around it buries that in JSON escaping. The reader is
|
|
41
|
+
* at a terminal; give them the sentence.
|
|
42
|
+
*/
|
|
43
|
+
export declare function refusalText(result: unknown): string | undefined;
|
|
44
|
+
/** The payload of a tool result: its structured content, or the JSON object in its text. */
|
|
45
|
+
export declare function payloadOf(result: unknown): Record<string, unknown>;
|