@reticlehq/server 3.7.0 → 3.8.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 +59 -68
- package/bin/reticle.js +9 -2
- package/dist/command/cli/adhoc-suite.js +1 -1
- package/dist/command/cli/adhoc-verdict.d.ts +12 -0
- package/dist/command/cli/adhoc-verdict.js +62 -4
- package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
- package/dist/command/cli/answers/gate-hook-message.js +5 -1
- package/dist/command/cli/auth/cloud-session.d.ts +11 -19
- package/dist/command/cli/auth/cloud-session.js +23 -0
- package/dist/command/cli/auth/device-flow.d.ts +3 -37
- package/dist/command/cli/cli-doctor.js +66 -21
- package/dist/command/cli/cli-flow-commands.js +53 -11
- package/dist/command/cli/cli-kill.d.ts +8 -0
- package/dist/command/cli/cli-kill.js +43 -3
- package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
- package/dist/command/cli/cli-parse-feedback.js +13 -1
- package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
- package/dist/command/cli/cli-parse-grammar.js +1 -6
- package/dist/command/cli/cli-parse-serve.d.ts +24 -0
- package/dist/command/cli/cli-parse-serve.js +124 -0
- package/dist/command/cli/cli-parse.d.ts +12 -11
- package/dist/command/cli/cli-parse.js +100 -123
- package/dist/command/cli/cli-usage.d.ts +0 -11
- package/dist/command/cli/cli-usage.js +43 -16
- package/dist/command/cli/cli-verify.d.ts +1 -1
- package/dist/command/cli/cli-verify.js +29 -14
- package/dist/command/cli/cloud-cli.js +27 -9
- package/dist/command/cli/cloud-kit.js +3 -3
- package/dist/command/cli/cloud-link.d.ts +1 -20
- package/dist/command/cli/cloud-link.js +2 -0
- package/dist/command/cli/cloud-login.d.ts +6 -2
- package/dist/command/cli/cloud-login.js +13 -41
- package/dist/command/cli/config/checkout.d.ts +10 -0
- package/dist/command/cli/config/checkout.js +78 -0
- package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
- package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
- 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/drive/drive-attach.d.ts +5 -0
- package/dist/command/cli/drive/drive-attach.js +21 -1
- package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
- package/dist/command/cli/hook/codex-transcript.js +224 -0
- package/dist/command/cli/hook/episode-structure.d.ts +24 -0
- package/dist/command/cli/hook/episode-structure.js +138 -0
- package/dist/command/cli/hook/hook-command.d.ts +9 -0
- package/dist/command/cli/hook/hook-command.js +211 -0
- package/dist/command/cli/hook/hook-fast.d.ts +31 -0
- package/dist/command/cli/hook/hook-fast.js +136 -0
- package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
- package/dist/command/cli/hook/prompt-hook.js +199 -0
- package/dist/command/cli/hook/tool-hook.d.ts +21 -0
- package/dist/command/cli/hook/tool-hook.js +259 -0
- package/dist/command/cli/launch/cli-launch.d.ts +2 -0
- package/dist/command/cli/launch/cli-launch.js +22 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
- package/dist/command/cli/mcp-command.d.ts +9 -0
- package/dist/command/cli/mcp-command.js +127 -41
- package/dist/command/cli/open-navigate.d.ts +43 -0
- package/dist/command/cli/open-navigate.js +85 -0
- package/dist/command/cli/ports/port-holder.d.ts +16 -4
- package/dist/command/cli/ports/port-holder.js +58 -12
- package/dist/command/cli/ports/resolve/cli-port.js +18 -2
- package/dist/command/cli/project-name.d.ts +1 -7
- package/dist/command/cli/project-name.js +1 -0
- package/dist/command/cli/report-command.d.ts +8 -2
- package/dist/command/cli/report-command.js +95 -18
- package/dist/command/cli/status-fields.d.ts +9 -2
- package/dist/command/cli/status-fields.js +148 -4
- package/dist/command/cli/try-command.d.ts +3 -4
- package/dist/command/cli/try-command.js +6 -11
- package/dist/command/cli/verify-results.d.ts +4 -1
- package/dist/command/cli/verify-results.js +3 -1
- package/dist/command/cli.d.ts +1 -7
- package/dist/command/cli.js +79 -160
- package/dist/command/daemon/daemon-resilience.d.ts +6 -1
- package/dist/command/daemon/daemon-resilience.js +31 -18
- package/dist/command/daemon/daemon.d.ts +11 -0
- package/dist/command/daemon/daemon.js +70 -2
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
- package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
- package/dist/command/daemon/wake-decision.js +4 -2
- package/dist/command/daemon/win32-escape.d.ts +32 -0
- package/dist/command/daemon/win32-escape.js +119 -0
- package/dist/command/setup/agent-configs.d.ts +9 -0
- package/dist/command/setup/agent-configs.js +9 -0
- package/dist/command/setup/auto-approve.d.ts +10 -3
- package/dist/command/setup/auto-approve.js +51 -3
- package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
- package/dist/command/setup/bringup/dev-server-wait.js +5 -1
- package/dist/command/setup/bringup/ensure-daemon.js +4 -7
- package/dist/command/setup/init/init-host.js +6 -0
- package/dist/command/setup/init/init-port.js +5 -4
- package/dist/command/setup/init/init-runtime.js +7 -1
- package/dist/command/setup/node-effects.d.ts +8 -0
- package/dist/command/setup/node-effects.js +36 -4
- package/dist/command/setup/probe/dev-port.d.ts +26 -0
- package/dist/command/setup/probe/dev-port.js +117 -0
- package/dist/command/setup/probe/page-probe.d.ts +1 -1
- package/dist/command/setup/probe/page-probe.js +11 -3
- package/dist/command/setup/remaining-steps.d.ts +0 -7
- package/dist/command/setup/remaining-steps.js +3 -2
- package/dist/command/setup/run-setup.d.ts +11 -0
- package/dist/command/setup/run-setup.js +33 -5
- package/dist/command/setup/setup-command.d.ts +2 -0
- package/dist/command/setup/setup-command.js +22 -4
- package/dist/command/update/updater.d.ts +1 -6
- package/dist/command/update/updater.js +4 -5
- package/dist/command/update/version-seen.d.ts +24 -0
- package/dist/command/update/version-seen.js +65 -0
- package/dist/command/version/peer-announce.d.ts +9 -1
- package/dist/command/version/peer-announce.js +25 -2
- package/dist/command/version/sdk-fix.d.ts +5 -6
- package/dist/command/version/sdk-fix.js +17 -32
- package/dist/command/version/version-skew.d.ts +16 -1
- package/dist/command/version/version-skew.js +84 -10
- package/dist/features/crawl/crawl-tools.js +2 -2
- package/dist/features/crawl/crawl.js +2 -0
- package/dist/features/exhaust/ledger.d.ts +15 -96
- package/dist/features/exhaust/ledger.js +3 -3
- package/dist/features/harness/drive-runs.d.ts +28 -4
- package/dist/features/harness/drive-runs.js +99 -7
- package/dist/features/harness/drivers.d.ts +1 -1
- package/dist/features/harness/drivers.js +1 -1
- package/dist/features/harness/harness.d.ts +5 -0
- package/dist/features/harness/harness.js +24 -12
- package/dist/features/harness/platform/platform-drives.d.ts +7 -1
- package/dist/features/harness/platform/platform-drives.js +12 -7
- package/dist/features/harness/platform/refusal.d.ts +1 -1
- package/dist/features/harness/platform/refusal.js +4 -4
- package/dist/features/harness/platform/remote-drive.js +70 -28
- package/dist/features/harness/platform/server-driver.js +22 -1
- package/dist/features/harness/platform-config.d.ts +1 -10
- package/dist/features/harness/platform-config.js +0 -20
- package/dist/features/license/license.d.ts +2 -14
- package/dist/features/visual/step-shots.d.ts +33 -1
- package/dist/features/visual/step-shots.js +155 -1
- package/dist/features/visual/visual-tools.js +12 -4
- package/dist/hooks/coding-agents.d.ts +6 -0
- package/dist/hooks/coding-agents.js +24 -3
- package/dist/hooks/episode-stamp.d.ts +32 -0
- package/dist/hooks/episode-stamp.js +61 -0
- package/dist/hooks/hook-commands.js +19 -9
- package/dist/index.d.ts +5 -1
- package/dist/index.js +66 -24
- package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
- package/dist/judgement/app-graph/journal-steps.js +92 -0
- package/dist/judgement/capsule/capsule-store.d.ts +2 -39
- package/dist/judgement/domain/domain-model.js +1 -10
- package/dist/judgement/domain/domain-tools.js +4 -2
- package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
- package/dist/judgement/runs/artifact/run-store.js +153 -28
- package/dist/judgement/runs/default-run-id.d.ts +3 -1
- package/dist/judgement/runs/default-run-id.js +15 -3
- package/dist/judgement/runs/drive-run.d.ts +3 -3
- package/dist/judgement/runs/drive-run.js +11 -7
- package/dist/judgement/runs/run-ledger.d.ts +21 -0
- package/dist/judgement/runs/run-ledger.js +143 -0
- package/dist/judgement/runs/runner-port.d.ts +1 -1
- package/dist/judgement/runs/runner-port.js +8 -2
- package/dist/judgement/runs/verification-sync.d.ts +4 -3
- package/dist/judgement/runs/verification-sync.js +27 -5
- package/dist/language/flows/affected-tools.js +2 -1
- package/dist/language/flows/annotate-notes/annotate.js +8 -3
- package/dist/language/flows/annotate-tools.js +1 -1
- package/dist/language/flows/change/affected.d.ts +8 -0
- package/dist/language/flows/change/affected.js +20 -7
- package/dist/language/flows/decision.js +19 -4
- package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
- package/dist/language/flows/fields/flow-secret-field.js +67 -5
- package/dist/language/flows/flow-anchor.d.ts +5 -0
- package/dist/language/flows/flow-anchor.js +4 -0
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +9 -0
- package/dist/language/flows/flow-expect-grammar.js +15 -10
- package/dist/language/flows/flow-intent.js +6 -1
- package/dist/language/flows/flow-journey.d.ts +8 -0
- package/dist/language/flows/flow-journey.js +13 -0
- package/dist/language/flows/flow-lease.d.ts +27 -0
- package/dist/language/flows/flow-lease.js +59 -0
- package/dist/language/flows/flow-replay-run.d.ts +1 -87
- package/dist/language/flows/flow-replay-run.js +111 -310
- package/dist/language/flows/flow-replay.js +21 -19
- package/dist/language/flows/flow-replayable.d.ts +38 -0
- package/dist/language/flows/flow-replayable.js +179 -0
- package/dist/language/flows/flow-start-path.d.ts +108 -0
- package/dist/language/flows/flow-start-path.js +313 -0
- package/dist/language/flows/flow-step-runners.d.ts +23 -2
- package/dist/language/flows/flow-step-runners.js +191 -32
- package/dist/language/flows/flow-store-for-session.d.ts +8 -1
- package/dist/language/flows/flow-store-for-session.js +6 -0
- package/dist/language/flows/flow-tools.d.ts +2 -23
- package/dist/language/flows/flow-tools.js +63 -113
- package/dist/language/flows/flows.d.ts +4 -6
- package/dist/language/flows/flows.js +90 -15
- package/dist/language/flows/heal-run.d.ts +10 -4
- package/dist/language/flows/heal-run.js +26 -9
- package/dist/language/flows/recording/flake.d.ts +3 -27
- package/dist/language/flows/recording/flake.js +1 -1
- package/dist/language/flows/recording/recording-backtrack.js +1 -4
- package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
- package/dist/language/flows/recording/tape/recordings.js +9 -3
- package/dist/language/flows/replay.d.ts +25 -5
- package/dist/language/flows/replay.js +83 -16
- package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
- package/dist/language/flows/suite/coverage.d.ts +9 -0
- package/dist/language/flows/suite/coverage.js +12 -1
- package/dist/language/flows/suite-selection.d.ts +25 -0
- package/dist/language/flows/suite-selection.js +41 -9
- package/dist/machine/git-tracks.d.ts +6 -0
- package/dist/machine/git-tracks.js +36 -0
- package/dist/memory/cloud/background-sync.d.ts +25 -0
- package/dist/memory/cloud/background-sync.js +79 -0
- package/dist/memory/cloud/cloud-config.d.ts +12 -1
- package/dist/memory/cloud/cloud-config.js +39 -13
- package/dist/memory/cloud/cloud-sync.d.ts +3 -24
- package/dist/memory/cloud/cloud-sync.js +5 -2
- package/dist/memory/cloud/harness-config.d.ts +0 -7
- package/dist/memory/cloud/memory-scope.d.ts +19 -0
- package/dist/memory/cloud/memory-scope.js +48 -0
- package/dist/memory/cloud/sync-answer.d.ts +43 -0
- package/dist/memory/cloud/sync-answer.js +42 -0
- package/dist/memory/cloud/sync-cycle.d.ts +40 -10
- package/dist/memory/cloud/sync-cycle.js +97 -144
- package/dist/memory/cloud/sync-daemon.d.ts +50 -3
- package/dist/memory/cloud/sync-daemon.js +473 -83
- package/dist/memory/cloud/sync-describe.d.ts +28 -0
- package/dist/memory/cloud/sync-describe.js +92 -0
- package/dist/memory/cloud/sync-disk.d.ts +9 -1
- package/dist/memory/cloud/sync-disk.js +137 -33
- package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
- package/dist/memory/cloud/sync-flow-versions.js +42 -0
- package/dist/memory/impact/impact-store.js +32 -15
- package/dist/memory/intent/intent-shard.d.ts +41 -266
- package/dist/memory/intent/intent-tools.js +20 -61
- package/dist/memory/intent/record-request.d.ts +27 -0
- package/dist/memory/intent/record-request.js +88 -0
- package/dist/memory/intent/settled-words.js +9 -1
- package/dist/memory/journal/drive-flow.d.ts +3 -0
- package/dist/memory/journal/drive-flow.js +11 -41
- package/dist/memory/journal/envelope-store.js +1 -1
- package/dist/memory/journal/envelope.d.ts +6 -154
- package/dist/memory/journal/episode-log.d.ts +107 -0
- package/dist/memory/journal/episode-log.js +302 -0
- package/dist/memory/journal/journal-recorder.d.ts +7 -0
- package/dist/memory/journal/journal-recorder.js +22 -9
- package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
- package/dist/memory/journal/on-disk/ambient-file.js +1 -1
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
- package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
- package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
- package/dist/memory/journal/self-observation.js +3 -1
- package/dist/memory/journal/session-end.js +23 -9
- package/dist/memory/journal/session-journal.js +39 -20
- package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
- package/dist/memory/project/artifact-root-resolver.js +68 -23
- package/dist/memory/project/artifact-root.d.ts +17 -3
- package/dist/memory/project/artifact-root.js +47 -9
- package/dist/memory/project/fs/fs-port.d.ts +5 -0
- package/dist/memory/project/fs/fs-port.js +13 -0
- package/dist/memory/project/learned-routes.d.ts +3 -4
- package/dist/memory/project/learned-routes.js +3 -11
- package/dist/memory/project/link-directory.js +5 -2
- package/dist/memory/project/root-claims.d.ts +3 -1
- package/dist/memory/project/root-claims.js +41 -7
- package/dist/memory/project/session-root.d.ts +1 -1
- package/dist/memory/project/session-root.js +1 -15
- package/dist/memory/project/sync-status.js +5 -6
- package/dist/memory/recall/memory-tools.js +2 -2
- package/dist/memory/recall/platform-memory.d.ts +3 -1
- package/dist/memory/recall/platform-memory.js +9 -1
- package/dist/memory/recall/project-memory.js +15 -19
- package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
- package/dist/portal/bridge/auth-failure-reason.js +19 -0
- package/dist/portal/bridge/bridge.d.ts +19 -4
- package/dist/portal/bridge/bridge.js +150 -177
- package/dist/portal/bridge/harness-request.d.ts +15 -0
- package/dist/portal/bridge/harness-request.js +19 -0
- package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
- package/dist/portal/bridge/origin/origin-follow.js +110 -0
- package/dist/portal/bridge/pairing-token.d.ts +6 -0
- package/dist/portal/bridge/pairing-token.js +47 -16
- package/dist/portal/bridge/protocol-skew.d.ts +27 -2
- package/dist/portal/bridge/protocol-skew.js +66 -2
- package/dist/portal/bridge/session-nonces.d.ts +46 -0
- package/dist/portal/bridge/session-nonces.js +72 -0
- package/dist/portal/input/viewport-tools.d.ts +31 -0
- package/dist/portal/input/viewport-tools.js +106 -15
- package/dist/portal/pool/bounded-close.d.ts +23 -0
- package/dist/portal/pool/bounded-close.js +38 -0
- package/dist/portal/pool/browser-pool.d.ts +44 -39
- package/dist/portal/pool/browser-pool.js +247 -95
- package/dist/portal/pool/context-permissions.d.ts +9 -1
- package/dist/portal/pool/context-permissions.js +26 -0
- package/dist/portal/pool/lease-emulation.d.ts +37 -0
- package/dist/portal/pool/lease-emulation.js +29 -0
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +74 -7
- package/dist/portal/pool/pool-contract.d.ts +23 -1
- package/dist/portal/pool/pool-lease.d.ts +128 -0
- package/dist/portal/pool/pool-lease.js +49 -0
- package/dist/portal/pool/zero-install.d.ts +8 -1
- package/dist/portal/pool/zero-install.js +24 -2
- package/dist/portal/production/production-activation.d.ts +136 -0
- package/dist/portal/production/production-activation.js +173 -0
- package/dist/portal/production/production-drive.d.ts +52 -0
- package/dist/portal/production/production-drive.js +301 -0
- package/dist/portal/production/production-hello.d.ts +23 -0
- package/dist/portal/production/production-hello.js +23 -0
- package/dist/portal/realm/web-realm.js +42 -15
- package/dist/portal/session/control-envelope.d.ts +1 -1
- package/dist/portal/session/dev-server/served-document.d.ts +0 -5
- package/dist/portal/session/dev-server/served-document.js +6 -8
- package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
- package/dist/portal/session/first-run-wiring.d.ts +3 -36
- package/dist/portal/session/first-run-wiring.js +10 -103
- package/dist/portal/session/live-control-tools.js +61 -19
- package/dist/portal/session/no-session-next-action.d.ts +11 -0
- package/dist/portal/session/no-session-next-action.js +40 -15
- package/dist/portal/session/no-session-watch.d.ts +7 -0
- package/dist/portal/session/no-session-watch.js +67 -23
- package/dist/portal/session/output-budget.d.ts +10 -0
- package/dist/portal/session/output-budget.js +16 -0
- package/dist/portal/session/presence/session-lease.d.ts +2 -0
- package/dist/portal/session/presence/session-lease.js +8 -0
- package/dist/portal/session/recorded-gaps.d.ts +15 -11
- package/dist/portal/session/recorded-gaps.js +39 -18
- package/dist/portal/session/serving-directory.d.ts +24 -2
- package/dist/portal/session/serving-directory.js +93 -5
- package/dist/portal/session/session-info.d.ts +26 -0
- package/dist/portal/session/session-info.js +20 -0
- package/dist/portal/session/session-manager.d.ts +35 -3
- package/dist/portal/session/session-manager.js +69 -14
- package/dist/portal/session/session-successor.js +1 -9
- package/dist/portal/session/session-tools.js +22 -4
- package/dist/portal/session/session.d.ts +10 -10
- package/dist/portal/session/session.js +37 -29
- package/dist/portal/session/zero-install-first-call.d.ts +7 -2
- package/dist/portal/session/zero-install-first-call.js +43 -20
- package/dist/start-wiring.d.ts +2 -2
- package/dist/start-wiring.js +17 -3
- package/dist/status-payload.d.ts +11 -1
- package/dist/status-payload.js +7 -1
- package/dist/surface/agent-presence-grace.d.ts +11 -0
- package/dist/surface/agent-presence-grace.js +34 -0
- package/dist/surface/http-server.d.ts +12 -1
- package/dist/surface/http-server.js +64 -15
- package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
- package/dist/surface/mcp/faults/mcp-outage.js +19 -1
- package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
- package/dist/surface/mcp/mcp-proxy.js +194 -267
- package/dist/surface/mcp/mcp.d.ts +20 -3
- package/dist/surface/mcp/mcp.js +88 -25
- package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
- package/dist/surface/mcp/proxy/drive-calls.js +114 -5
- package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
- package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
- package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
- package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
- package/dist/surface/mcp/server-instructions.js +35 -16
- package/dist/surface/mcp/shared-params.d.ts +8 -0
- package/dist/surface/mcp/shared-params.js +15 -1
- package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
- package/dist/surface/mcp/sse-frame-parser.js +18 -0
- package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
- package/dist/surface/mcp/surface-vocabulary.js +13 -0
- package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
- package/dist/surface/mcp/tool-catalog-cache.js +14 -4
- package/dist/surface/tools/account-tool.d.ts +4 -3
- package/dist/surface/tools/account-tool.js +20 -31
- package/dist/surface/tools/act/act-danger.d.ts +2 -0
- package/dist/surface/tools/act/act-danger.js +41 -14
- package/dist/surface/tools/act/act-observation.d.ts +11 -0
- package/dist/surface/tools/act/act-observation.js +33 -0
- package/dist/surface/tools/act/act-preflight.js +10 -3
- package/dist/surface/tools/act/act-sequence-retry.js +2 -1
- package/dist/surface/tools/act/act-target.js +19 -5
- package/dist/surface/tools/act/durable.js +1 -1
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
- package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
- package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
- package/dist/surface/tools/act/unsettled-find.js +71 -0
- package/dist/surface/tools/act-sequence-tool.js +53 -24
- package/dist/surface/tools/act-tools.d.ts +2 -3
- package/dist/surface/tools/act-tools.js +49 -47
- package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
- package/dist/surface/tools/agent/for-the-agent.js +41 -3
- package/dist/surface/tools/assert/assert-grade.js +8 -3
- package/dist/surface/tools/assert/assert-verdict.js +4 -2
- package/dist/surface/tools/browser-tools.js +7 -4
- package/dist/surface/tools/chat-drives.js +50 -25
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.d.ts +8 -0
- package/dist/surface/tools/dynamic-tools.js +89 -34
- package/dist/surface/tools/empty-session-list.d.ts +18 -25
- package/dist/surface/tools/empty-session-list.js +67 -38
- package/dist/surface/tools/error-recovery.d.ts +17 -7
- package/dist/surface/tools/error-recovery.js +53 -20
- package/dist/surface/tools/explore-tools.d.ts +10 -0
- package/dist/surface/tools/explore-tools.js +72 -19
- package/dist/surface/tools/feedback-tools.js +23 -2
- package/dist/surface/tools/gap-novelty.d.ts +6 -0
- package/dist/surface/tools/gap-novelty.js +9 -0
- package/dist/surface/tools/gaps/capability-absences.js +4 -3
- package/dist/surface/tools/harness-explore.d.ts +1 -1
- package/dist/surface/tools/harness-explore.js +6 -4
- package/dist/surface/tools/harness-gate.d.ts +43 -21
- package/dist/surface/tools/harness-gate.js +55 -81
- package/dist/surface/tools/harness-script.d.ts +2 -0
- package/dist/surface/tools/harness-script.js +11 -6
- package/dist/surface/tools/harness-toolset.js +3 -4
- package/dist/surface/tools/invoke-tool.d.ts +0 -8
- package/dist/surface/tools/invoke-tool.js +164 -178
- package/dist/surface/tools/lease-hint.d.ts +9 -0
- package/dist/surface/tools/lease-hint.js +22 -3
- package/dist/surface/tools/lease-permissions.d.ts +1 -1
- package/dist/surface/tools/lease-readiness.d.ts +55 -0
- package/dist/surface/tools/lease-readiness.js +150 -2
- package/dist/surface/tools/lease-tools.d.ts +3 -75
- package/dist/surface/tools/lease-tools.js +124 -188
- package/dist/surface/tools/map-tools.d.ts +20 -0
- package/dist/surface/tools/map-tools.js +145 -0
- package/dist/surface/tools/merge-tools.js +11 -7
- package/dist/surface/tools/next-step.js +6 -3
- package/dist/surface/tools/observe-tools.js +46 -35
- package/dist/surface/tools/platform-moment.d.ts +3 -12
- package/dist/surface/tools/platform-moment.js +12 -24
- package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
- package/dist/surface/tools/read/complete-snapshot.js +13 -2
- package/dist/surface/tools/read/query-paginate.js +6 -0
- package/dist/surface/tools/read/query-shape.d.ts +9 -0
- package/dist/surface/tools/read/query-shape.js +15 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
- package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
- package/dist/surface/tools/read-tools.js +5 -5
- package/dist/surface/tools/real-input-attempt.d.ts +11 -0
- package/dist/surface/tools/real-input-attempt.js +206 -38
- package/dist/surface/tools/reconcile-tools.js +21 -8
- package/dist/surface/tools/reticle-result.d.ts +3 -0
- package/dist/surface/tools/reticle-result.js +8 -1
- package/dist/surface/tools/run-report.d.ts +22 -0
- package/dist/surface/tools/run-report.js +141 -0
- package/dist/surface/tools/session-ports.d.ts +57 -0
- package/dist/surface/tools/session-ports.js +102 -0
- package/dist/surface/tools/tool-kit.d.ts +58 -4
- package/dist/surface/tools/tool-kit.js +140 -6
- package/dist/surface/tools/tool-surface.d.ts +2 -0
- package/dist/surface/tools/tool-surface.js +45 -0
- package/dist/surface/tools/tools.js +49 -87
- package/dist/telemetry/automation-hint.d.ts +5 -0
- package/dist/telemetry/automation-hint.js +7 -0
- package/dist/telemetry/cli-telemetry.d.ts +22 -0
- package/dist/telemetry/cli-telemetry.js +80 -11
- package/dist/telemetry/daemon-telemetry.d.ts +3 -1
- package/dist/telemetry/daemon-telemetry.js +9 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +8 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/feedback.js +51 -4
- package/dist/telemetry/harness-run.d.ts +3 -3
- package/dist/telemetry/harness-run.js +18 -7
- package/dist/telemetry/hud-events.d.ts +1 -1
- package/dist/telemetry/hud-metrics.d.ts +6 -3
- package/dist/telemetry/hud-metrics.js +20 -8
- package/dist/telemetry/install-source.js +1 -1
- package/dist/telemetry/mcp-connection.d.ts +1 -1
- package/dist/telemetry/mcp-connection.js +21 -2
- package/dist/telemetry/onboarding-firsts.d.ts +6 -1
- package/dist/telemetry/onboarding-firsts.js +41 -1
- package/dist/telemetry/proxy-telemetry.d.ts +42 -0
- package/dist/telemetry/proxy-telemetry.js +70 -1
- package/dist/telemetry/session-metrics.d.ts +1 -3
- package/dist/telemetry/session-metrics.js +16 -12
- package/dist/telemetry/telemetry.d.ts +9 -1
- package/dist/telemetry/telemetry.js +16 -1
- package/dist/telemetry/verification-of.d.ts +3 -1
- package/dist/telemetry/verification-of.js +7 -1
- package/dist/wire-journal.js +12 -2
- package/docs/README.md +4 -2
- package/docs/actions.mdx +7 -6
- package/docs/adapters.md +2 -1
- package/docs/agent-cheatsheet.md +19 -5
- package/docs/architecture.md +13 -10
- package/docs/autodrive.md +6 -5
- package/docs/benchmarks.md +2 -2
- package/docs/capabilities.mdx +1 -1
- package/docs/cli/affected.mdx +1 -1
- package/docs/cli/cloud.mdx +6 -6
- package/docs/cli/doctor.mdx +3 -1
- package/docs/cli/feedback.mdx +11 -7
- package/docs/cli/gate.mdx +1 -1
- package/docs/cli/help.mdx +1 -1
- package/docs/cli/init.mdx +20 -16
- package/docs/cli/mcp.mdx +5 -2
- package/docs/cli/open.mdx +5 -2
- package/docs/cli/report.mdx +1 -1
- package/docs/cli/restart.mdx +1 -1
- package/docs/cli/rollback.mdx +4 -4
- package/docs/cli/serve.mdx +7 -5
- package/docs/cli/setup.mdx +1 -1
- package/docs/cli/status.mdx +6 -2
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/telemetry.mdx +5 -1
- package/docs/cli/try.mdx +8 -8
- package/docs/cli/update.mdx +8 -5
- package/docs/cli/verify.mdx +12 -5
- package/docs/cli/version.mdx +1 -1
- package/docs/cli.mdx +10 -6
- package/docs/debugging.md +1 -1
- package/docs/deploy-checks.md +8 -6
- package/docs/desktop.mdx +14 -18
- package/docs/enterprise.md +6 -6
- package/docs/environment.mdx +5 -3
- package/docs/faq.mdx +9 -6
- package/docs/first-drive.md +19 -9
- package/docs/flows.md +3 -4
- package/docs/for-agents.md +1 -1
- package/docs/frameworks.mdx +34 -11
- package/docs/getting-started.md +46 -29
- package/docs/http-transport.md +2 -2
- package/docs/human-control.md +8 -8
- package/docs/index.mdx +2 -2
- package/docs/install-agentic.mdx +18 -19
- package/docs/install-manual.mdx +8 -4
- package/docs/integration-patterns.md +5 -5
- package/docs/multi-agent-testing.md +7 -5
- package/docs/packages/browser.mdx +34 -2
- package/docs/packages/core.mdx +2 -1
- package/docs/packages/electron.mdx +15 -3
- package/docs/packages/eslint-plugin.mdx +1 -1
- package/docs/packages/next.mdx +17 -5
- package/docs/packages/react.mdx +8 -1
- package/docs/packages/server.mdx +3 -3
- package/docs/packages/tauri.mdx +3 -3
- package/docs/packages/vite-plugin.mdx +1 -1
- package/docs/packages.mdx +19 -6
- package/docs/platform-integration.md +12 -4
- package/docs/predicates.mdx +18 -7
- package/docs/production-mode.md +108 -0
- package/docs/prompt-capture.md +99 -0
- package/docs/quickstart.mdx +14 -12
- package/docs/recipes.mdx +9 -8
- package/docs/required-checks.md +3 -2
- package/docs/skill-file.mdx +4 -3
- package/docs/skill-setup.md +2 -2
- package/docs/skill.md +62 -70
- package/docs/state-files.md +1 -0
- package/docs/state-management.mdx +11 -9
- package/docs/system-map.md +14 -13
- package/docs/telemetry.md +32 -24
- package/docs/testing.md +9 -5
- package/docs/tools/act-and-wait.mdx +17 -13
- package/docs/tools/act-sequence.mdx +40 -66
- package/docs/tools/act.mdx +7 -5
- package/docs/tools/assert.mdx +1 -1
- package/docs/tools/look.mdx +1 -1
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/network.mdx +13 -11
- package/docs/tools/overview.mdx +3 -3
- package/docs/tools/query.mdx +3 -3
- package/docs/tools/session-and-feedback.mdx +9 -6
- package/docs/tools/sessions.mdx +7 -0
- package/docs/tools/snapshot.mdx +9 -7
- package/docs/tools/tools-and-run.mdx +11 -10
- package/docs/tools/verify.mdx +1 -1
- package/docs/troubleshooting.mdx +21 -9
- package/docs/usage.md +58 -43
- package/docs/use-cases.mdx +2 -2
- package/docs/vs-chrome-devtools-mcp.mdx +5 -5
- package/docs/vs-playwright-mcp.mdx +5 -4
- package/docs/what-is-recorded.md +12 -7
- package/docs/what-is-reticle.mdx +2 -2
- package/docs/why-reticle.mdx +1 -1
- package/package.json +9 -12
- package/dist/command/dev/stale-issue-guard.d.ts +0 -34
- package/dist/command/dev/stale-issue-guard.js +0 -69
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* not: `npx reticle` resolves the PACKAGE named `reticle`, which belongs to somebody else, and npx
|
|
10
10
|
* will happily fetch and run it. So the npx spelling leads every page.
|
|
11
11
|
*/
|
|
12
|
+
import { FEEDBACK_KIND_CHOICES } from '@reticlehq/core';
|
|
12
13
|
export const HelpGroup = {
|
|
13
14
|
START: 'Get started',
|
|
14
15
|
CHECK: 'Check your app',
|
|
@@ -21,14 +22,16 @@ export const HelpGroup = {
|
|
|
21
22
|
export const HELP_ALL_TOPIC = 'all';
|
|
22
23
|
const INIT_DETAIL = `init wires the project, boots the app and proves a page connected. It ends with the
|
|
23
24
|
instrumentation score, what is missing, and a prompt for your coding agent, which proves
|
|
24
|
-
the first flow. It never drives: the Harness is opt-in and
|
|
25
|
+
the first flow. It never drives: the Harness is opt-in and runs at any instrumentation score.
|
|
25
26
|
|
|
26
27
|
--app <dir> which app in a monorepo: writes its files only (add --start to boot it)
|
|
27
28
|
--env KEY=VALUE what the app needs to reach a usable state (repeatable)
|
|
28
29
|
--files-only write the files and stop; do not boot the app
|
|
29
30
|
--no-install do not install packages
|
|
31
|
+
--new-id give this checkout its own project id (a copied repo keeps the original's)
|
|
30
32
|
--no-mcp skip the MCP registration, the agent rule files and the /reticle command
|
|
31
33
|
--hooks also install the agent hooks
|
|
34
|
+
--no-prompt-capture install no hook that saves each prompt to .reticle/
|
|
32
35
|
--relaunch print the command that restarts this agent conversation with the tools loaded
|
|
33
36
|
--license <key> write an enterprise key to .env and keep .env out of git
|
|
34
37
|
--json print the result as one JSON object on stdout
|
|
@@ -47,11 +50,15 @@ const VERIFY_DETAIL = `Drives the URL and verifies the saved flows. Exit 0 means
|
|
|
47
50
|
--session-id <id>, --timeout N, --headed, --port N
|
|
48
51
|
--expect '<json predicate>' | --expect-file <path>
|
|
49
52
|
one verdict, no saved flows needed. Asks a running daemon,
|
|
50
|
-
never starts one
|
|
53
|
+
never starts one, and waits up to --timeout ms (default 4000)
|
|
54
|
+
for it to come true. Exit 0 only on verified:"yes". For an
|
|
51
55
|
action --expect cannot do, use the HTTP MCP transport:
|
|
52
56
|
https://docs.reticle.sh/http-transport.md`;
|
|
53
57
|
const BROWSER_NOTE = `The browser Reticle opens for your agent is shown so you can watch the run. --headless hides
|
|
54
58
|
it; CI, RETICLE_HEADLESS=1, or a Linux machine with no display hides it too.`;
|
|
59
|
+
/** What `--http` starts. MCP is already on the bridge port; this flag is the verify endpoint. */
|
|
60
|
+
const HTTP_HELP = '--http starts the verify endpoint. MCP is already served on the bridge port.';
|
|
61
|
+
const DAEMON_DETAIL = `${BROWSER_NOTE}\n\n${HTTP_HELP}`;
|
|
55
62
|
const COMMANDS = [
|
|
56
63
|
{
|
|
57
64
|
name: 'init',
|
|
@@ -72,9 +79,8 @@ const COMMANDS = [
|
|
|
72
79
|
' --project <name|id> the dashboard project to link\n' +
|
|
73
80
|
' --workspace <name|id> the workspace to link in, when you belong to more than one\n' +
|
|
74
81
|
' --url <origin> the Reticle platform, when it is not the default\n\n' +
|
|
75
|
-
'Without a browser (CI, an agent),
|
|
76
|
-
'
|
|
77
|
-
' reticle login --email <e> --code <123456> signs in, then links this repo',
|
|
82
|
+
'Sign-in is with Google or GitHub, in the browser. Without a browser (CI, an agent), set\n' +
|
|
83
|
+
'RETICLE_API_KEY to a key from Settings > API keys. Email sign-in is gone.',
|
|
78
84
|
},
|
|
79
85
|
{
|
|
80
86
|
name: 'try',
|
|
@@ -110,16 +116,18 @@ const COMMANDS = [
|
|
|
110
116
|
},
|
|
111
117
|
{
|
|
112
118
|
name: 'open',
|
|
113
|
-
synopsis: 'open [url] [--port N]',
|
|
119
|
+
synopsis: 'open [url] [--port N] [--navigate]',
|
|
114
120
|
summary: 'Open the app in your browser, or reuse a tab that is already connected',
|
|
115
121
|
group: HelpGroup.MACHINE,
|
|
122
|
+
detail: '--navigate moves a tab already on that origin to the url instead of leaving it.',
|
|
116
123
|
},
|
|
117
124
|
{
|
|
118
125
|
name: 'stop',
|
|
119
126
|
synopsis: 'stop [--force] [--quiet] [--port N]',
|
|
120
127
|
summary: 'Stop Reticle; --force frees its port even without a recorded pid',
|
|
121
128
|
group: HelpGroup.MACHINE,
|
|
122
|
-
detail: 'Without --force,
|
|
129
|
+
detail: 'Without --force, asks the daemon to stop (an authenticated request on loopback), and sends\n' +
|
|
130
|
+
'a signal to its recorded pid only if it does not accept.\n' +
|
|
123
131
|
"With --force, frees the port by the Reticle daemon LISTENING on it, never the agent's MCP\n" +
|
|
124
132
|
'proxy, and refuses a listener that is not Reticle.',
|
|
125
133
|
},
|
|
@@ -143,11 +151,12 @@ const COMMANDS = [
|
|
|
143
151
|
},
|
|
144
152
|
{
|
|
145
153
|
name: 'feedback',
|
|
146
|
-
synopsis: 'feedback [--rating 1-5] [--bug] "message"',
|
|
154
|
+
synopsis: 'feedback [--rating 1-5] [--bug] [--contact <email>] "message"',
|
|
147
155
|
summary: "Tell us what worked and what didn't; prints exactly what it sends",
|
|
148
156
|
group: HelpGroup.ACCOUNT,
|
|
149
|
-
detail:
|
|
150
|
-
'works from anywhere, including a setup that never finished
|
|
157
|
+
detail: `Agents: reticle feedback --agent --kind ${FEEDBACK_KIND_CHOICES} "message"\n` +
|
|
158
|
+
'works from anywhere, including a setup that never finished.\n' +
|
|
159
|
+
'--contact <email> adds an address for a reply. It is sent only when you give it.',
|
|
151
160
|
},
|
|
152
161
|
{
|
|
153
162
|
name: 'telemetry',
|
|
@@ -177,7 +186,7 @@ const COMMANDS = [
|
|
|
177
186
|
},
|
|
178
187
|
{
|
|
179
188
|
name: 'config',
|
|
180
|
-
synopsis: 'config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server]',
|
|
189
|
+
synopsis: 'config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server] [--harness on|off]',
|
|
181
190
|
summary: 'What this project sends to the dashboard',
|
|
182
191
|
group: HelpGroup.MORE,
|
|
183
192
|
},
|
|
@@ -199,19 +208,34 @@ const COMMANDS = [
|
|
|
199
208
|
summary: 'Resume after a compaction: what was proved, failed and is still owed',
|
|
200
209
|
group: HelpGroup.MORE,
|
|
201
210
|
},
|
|
211
|
+
{
|
|
212
|
+
name: 'hook',
|
|
213
|
+
synopsis: 'hook [install|uninstall|status|prompt --agent <id>|tool --agent <id>|export --structure]',
|
|
214
|
+
summary: 'Save prompts, tool calls, the model and closing messages to .reticle/, through your agent hooks',
|
|
215
|
+
group: HelpGroup.MORE,
|
|
216
|
+
detail: ' hook install add the prompt-capture hook for the agents used here\n' +
|
|
217
|
+
' hook uninstall remove exactly the entries hook install added\n' +
|
|
218
|
+
' hook status which agents capture prompts, and which rely on the MCP instructions\n' +
|
|
219
|
+
' hook prompt --agent <id> what the agent hook runs; reads the prompt on stdin\n' +
|
|
220
|
+
' hook tool --agent <id> what the post-tool hook runs; keeps the call in .reticle/episodes/\n' +
|
|
221
|
+
' hook export --structure each snapshot step in .reticle/episodes/ as text-free page structure (JSONL)\n\n' +
|
|
222
|
+
'One switch stops prompts, tool calls, the model and the closing message. Off for one project:\n' +
|
|
223
|
+
'"promptCapture": false in .reticle.json. Off on this machine: RETICLE_PROMPT_CAPTURE=0.\n' +
|
|
224
|
+
'Tool calls also stop with the journal: "journal": false or RETICLE_JOURNAL=0.',
|
|
225
|
+
},
|
|
202
226
|
{
|
|
203
227
|
name: 'mcp',
|
|
204
228
|
synopsis: 'mcp [--port N] [--drive <url>] [--headless] [--headed] [--http] [--http-port N] [--http-token T]',
|
|
205
229
|
summary: 'The MCP stdio server your agent runs; starts the daemon if needed',
|
|
206
230
|
group: HelpGroup.MORE,
|
|
207
|
-
detail:
|
|
231
|
+
detail: DAEMON_DETAIL,
|
|
208
232
|
},
|
|
209
233
|
{
|
|
210
234
|
name: 'serve',
|
|
211
235
|
synopsis: 'serve [--port N] [--drive <url>] [--headless] [--headed] [--http] [--http-port N] [--http-token T]',
|
|
212
236
|
summary: 'Start the daemon in the background; returns once it answers',
|
|
213
237
|
group: HelpGroup.MORE,
|
|
214
|
-
detail:
|
|
238
|
+
detail: DAEMON_DETAIL,
|
|
215
239
|
},
|
|
216
240
|
{
|
|
217
241
|
name: 'drive',
|
|
@@ -268,15 +292,18 @@ export const DOCUMENTED_COMMANDS = COMMANDS.map((c) => c.name);
|
|
|
268
292
|
* answers with the page of the command that replaced it, and says so.
|
|
269
293
|
*/
|
|
270
294
|
export function renderHelp(topic, renamed = new Map()) {
|
|
271
|
-
if (topic === undefined)
|
|
295
|
+
if (topic === undefined) {
|
|
272
296
|
return CLI_USAGE;
|
|
273
|
-
|
|
297
|
+
}
|
|
298
|
+
if (HELP_ALL_TOPIC === topic) {
|
|
274
299
|
return CLI_USAGE_ALL;
|
|
300
|
+
}
|
|
275
301
|
const now = renamed.get(topic);
|
|
276
302
|
const target = now?.split(' ')[0] ?? topic;
|
|
277
303
|
const doc = COMMANDS.find((c) => c.name === target);
|
|
278
|
-
if (doc === undefined)
|
|
304
|
+
if (doc === undefined) {
|
|
279
305
|
return CLI_USAGE;
|
|
306
|
+
}
|
|
280
307
|
return [
|
|
281
308
|
`usage: reticle ${doc.synopsis}`,
|
|
282
309
|
...(now === undefined ? [] : [` (\`reticle ${topic}\` is now \`reticle ${now}\`)`]),
|
|
@@ -81,7 +81,7 @@ interface VerifyArgs {
|
|
|
81
81
|
resultsJson?: string;
|
|
82
82
|
}
|
|
83
83
|
/** Orchestration: boot → wait for a session → replay flows → verdict → exit code. Browser-free. */
|
|
84
|
-
export declare function runVerify(args: VerifyArgs,
|
|
84
|
+
export declare function runVerify(args: VerifyArgs, outer: VerifyPorts): Promise<void>;
|
|
85
85
|
/**
|
|
86
86
|
* Why the run did not reach the dashboard. A revoked key left CI green with "cloud run sync failed
|
|
87
87
|
* (401)" in the log as the only trace; a refused key is said as one, loudly.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* so the decision logic — including the two honesty guards (no session, no flows ⇒ never a green pass)
|
|
10
10
|
* — is unit-tested without launching a real browser.
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
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
15
|
import { runAdhocVerdict } from './adhoc-verdict.js';
|
|
@@ -24,7 +24,7 @@ import { resolveSuiteSelection } from '../../language/flows/suite-selection.js';
|
|
|
24
24
|
import { projectOnly } from '../../memory/project/session-root.js';
|
|
25
25
|
import { configuredPairingToken, nodePairingTokenDeps, readOrCreatePairingTokenSync, defaultPairingTokenDir, } from '../../portal/bridge/pairing-token.js';
|
|
26
26
|
import { randomUUID } from 'node:crypto';
|
|
27
|
-
import { MCP_SSE_PATH, MCP_MESSAGE_PATH, RETICLE_DEFAULT_PORT, ReticleEnv, bridgeWsUrl, isLoopbackHostname, ReticleDir,
|
|
27
|
+
import { MCP_SSE_PATH, MCP_MESSAGE_PATH, RETICLE_DEFAULT_PORT, ReticleEnv, bridgeWsUrl, isLoopbackHostname, ReticleDir, RunFramework, RunProfile, VerdictStatus, VerifyPhase, } from '@reticlehq/core';
|
|
28
28
|
import { start } from '../../index.js';
|
|
29
29
|
import { probePresence, describePresence, PortPresence, } from '../daemon/binding/port-presence.js';
|
|
30
30
|
import { EXPECT_FLAG } from './cli-parse-grammar.js';
|
|
@@ -59,7 +59,6 @@ const DEFAULT_PROJECT_NAME = 'app';
|
|
|
59
59
|
export function verifyProjectName(linked, configured, folder) {
|
|
60
60
|
return linked ?? configured ?? (folder || DEFAULT_PROJECT_NAME);
|
|
61
61
|
}
|
|
62
|
-
const VERIFY_AGENT_ID = CLI_CLIENT_NAME;
|
|
63
62
|
const MSG_NO_SESSION = 'No app connected: Reticle drove the URL but no @reticlehq/browser session dialed back.\n' +
|
|
64
63
|
' Make sure the SDK is in the build and reticle.connect() runs on the preview page' +
|
|
65
64
|
' (for a non-localhost preview: allowNonLocalhost + a pairing token).';
|
|
@@ -85,10 +84,22 @@ async function openConnection(ports) {
|
|
|
85
84
|
}
|
|
86
85
|
}
|
|
87
86
|
/** Orchestration: boot → wait for a session → replay flows → verdict → exit code. Browser-free. */
|
|
88
|
-
export async function runVerify(args,
|
|
89
|
-
const conn = await openConnection(
|
|
87
|
+
export async function runVerify(args, outer) {
|
|
88
|
+
const conn = await openConnection(outer);
|
|
90
89
|
if (conn === undefined)
|
|
91
90
|
return;
|
|
91
|
+
/*
|
|
92
|
+
* The exit is held until the connection has closed. The live exit is process.exit, and close()
|
|
93
|
+
* runs the last cloud sync cycle that sends this run's flows and screenshots: exiting first skipped
|
|
94
|
+
* the `finally` below, so with no daemon running the run reached the dashboard without them.
|
|
95
|
+
*/
|
|
96
|
+
let exitCode;
|
|
97
|
+
const ports = {
|
|
98
|
+
...outer,
|
|
99
|
+
exit: (code) => {
|
|
100
|
+
exitCode = code;
|
|
101
|
+
},
|
|
102
|
+
};
|
|
92
103
|
try {
|
|
93
104
|
const ready = await conn.sessionReady(args.timeoutMs);
|
|
94
105
|
if (!ready) {
|
|
@@ -160,7 +171,7 @@ export async function runVerify(args, ports) {
|
|
|
160
171
|
progress.stop();
|
|
161
172
|
await progress.flush().catch(() => undefined);
|
|
162
173
|
}
|
|
163
|
-
await pushRunToCloud(run, cloud, ports); // best-effort; opt-in; never changes the verdict or exit code
|
|
174
|
+
const pushed = await pushRunToCloud(run, cloud, ports); // best-effort; opt-in; never changes the verdict or exit code
|
|
164
175
|
/*
|
|
165
176
|
* The last event anybody watching is waiting for, sent after the artifact has actually landed —
|
|
166
177
|
* so "pushed" on a dashboard means the run is really there, not that we were about to try.
|
|
@@ -169,7 +180,7 @@ export async function runVerify(args, ports) {
|
|
|
169
180
|
// The run's OWN injected timestamp — no wall clock is read in this path (rule 7).
|
|
170
181
|
[{ phase: VerifyPhase.PUSHED, at: run.createdAt }], cloud, cloudFetch).catch(() => undefined);
|
|
171
182
|
if (args.resultsJson !== undefined && ports.writeResults !== undefined) {
|
|
172
|
-
await ports.writeResults(args.resultsJson, `${JSON.stringify(verifyResults(run), null, 2)}\n`);
|
|
183
|
+
await ports.writeResults(args.resultsJson, `${JSON.stringify(verifyResults(run, pushed), null, 2)}\n`);
|
|
173
184
|
}
|
|
174
185
|
ports.out(renderRunReport(run));
|
|
175
186
|
ports.exit(run.verdict.status === VerdictStatus.PASS ? EXIT_PASS : EXIT_FAIL);
|
|
@@ -180,6 +191,9 @@ export async function runVerify(args, ports) {
|
|
|
180
191
|
}
|
|
181
192
|
finally {
|
|
182
193
|
await conn.close().catch(() => undefined);
|
|
194
|
+
// In the finally, so the early refusals that `return` from the try exit too.
|
|
195
|
+
if (exitCode !== undefined)
|
|
196
|
+
outer.exit(exitCode);
|
|
183
197
|
}
|
|
184
198
|
}
|
|
185
199
|
/**
|
|
@@ -222,7 +236,7 @@ async function pushRunToCloud(run, config, ports) {
|
|
|
222
236
|
if (null === config) {
|
|
223
237
|
ports.out(`run ${run.runId} was kept on this computer: this repo is not linked, so nothing was sent ` +
|
|
224
238
|
`to the dashboard. To save runs for your team: ${CONNECT}`);
|
|
225
|
-
return;
|
|
239
|
+
return false;
|
|
226
240
|
}
|
|
227
241
|
const result = await syncRunToCloud(run, config, cloudFetch);
|
|
228
242
|
if (result.outcome === SyncOutcome.SYNCED) {
|
|
@@ -230,10 +244,10 @@ async function pushRunToCloud(run, config, ports) {
|
|
|
230
244
|
ports.out(dashboard === undefined
|
|
231
245
|
? `↑ run ${run.runId} recorded on the Reticle dashboard`
|
|
232
246
|
: seeRunLine(dashboard, run.runId));
|
|
247
|
+
return true;
|
|
233
248
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
249
|
+
ports.fail(runSyncFailure(result.status, result.error));
|
|
250
|
+
return false;
|
|
237
251
|
}
|
|
238
252
|
/** The platform answered: the credential was not accepted. */
|
|
239
253
|
const REFUSED_STATUSES = new Set([401, 403]);
|
|
@@ -360,12 +374,11 @@ async function openLiveConnection(opts) {
|
|
|
360
374
|
verify: async (onProgress) => {
|
|
361
375
|
const run = await runner.verify({
|
|
362
376
|
project: { name: opts.projectName, framework: RunFramework.OTHER, previewUrl: opts.url },
|
|
363
|
-
|
|
364
|
-
trigger: { kind: RunTrigger.OEM },
|
|
377
|
+
...cliRunAuthor(process.env),
|
|
365
378
|
profile: RunProfile.PROD_PREVIEW,
|
|
366
379
|
...(onProgress === undefined ? {} : { onProgress }),
|
|
367
380
|
});
|
|
368
|
-
// Persist the artifact. `reticle gate` decides from
|
|
381
|
+
// Persist the artifact. `reticle gate` decides from the runs in the store, so without this the
|
|
369
382
|
// documented CI loop is broken end to end: `reticle verify` could pass and the gate would still
|
|
370
383
|
// block, because it never sees a passing run. (Only the optional `serve --http` endpoint used to
|
|
371
384
|
// persist.) Best-effort — a disk failure must not turn a passing verification into a failure.
|
|
@@ -591,6 +604,8 @@ export function handleVerify(parsed) {
|
|
|
591
604
|
port,
|
|
592
605
|
...(parsed.url !== undefined && '' !== parsed.url ? { url: parsed.url } : {}),
|
|
593
606
|
predicate: expectation,
|
|
607
|
+
// How long the expectation may take to come true (#1533).
|
|
608
|
+
...(parsed.timeoutMs === undefined ? {} : { timeoutMs: parsed.timeoutMs }),
|
|
594
609
|
// Read fresh, not from `presence`: that probe asks whether a daemon is on the port, and
|
|
595
610
|
// this asks whether a TAB is — the difference between asserting and opening the url first.
|
|
596
611
|
sessions: async () => summarizeStatus(await fetchStatus(port)).sessions,
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { CreatedProjectSchema } from './project-name.js';
|
|
9
9
|
import { keyRefusalAtLastSync } from '../../memory/project/sync-status.js';
|
|
10
|
-
import { apiKeyFrom } from '@reticlehq/core';
|
|
10
|
+
import { ReticleEnv, apiKeyFrom } from '@reticlehq/core';
|
|
11
|
+
import { writeHarnessSwitch } from '../../memory/cloud/harness-switch.js';
|
|
11
12
|
import { PlatformLinkVia } from '@reticlehq/core/telemetry';
|
|
12
13
|
import { existsSync } from 'node:fs';
|
|
13
14
|
import { writeFile } from 'node:fs/promises';
|
|
@@ -17,7 +18,7 @@ import { join } from 'node:path';
|
|
|
17
18
|
import { z } from 'zod';
|
|
18
19
|
import { createNodeFileSystem } from '../../memory/project/fs/fs-port.js';
|
|
19
20
|
import { CLOUD_LINK_FILE, resolveProjectCloud } from '../../memory/cloud/cloud-config.js';
|
|
20
|
-
import {
|
|
21
|
+
import { readMemoryPages, MemoryResponseScope, scopeMemoryResponse, scopeOfMemoryResponse, } from '../../memory/cloud/memory-scope.js';
|
|
21
22
|
import { linkDirectoryFrom } from '../../memory/project/link-directory.js';
|
|
22
23
|
import { RETICLE_CONFIG_BASENAME } from './ports/resolve/cli-port.js';
|
|
23
24
|
import { cmdLink, validateKey } from './cloud-link.js';
|
|
@@ -33,9 +34,11 @@ import { GateExit, gateExitWith } from './answers/gate-exit.js';
|
|
|
33
34
|
import { brokenOf } from './known-regression.js';
|
|
34
35
|
import { describeUnsyncedBriefly } from '../../memory/cloud/unsynced-roots.js';
|
|
35
36
|
import { machineUnsyncedRoots } from '../../memory/project/sync-status.js';
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
37
|
+
import { runSyncCycle } from '../../memory/cloud/sync-cycle.js';
|
|
38
|
+
import { describeSync } from '../../memory/cloud/sync-describe.js';
|
|
39
|
+
import { diskSink, diskSource, policyScreenshots, readCloudIssues, readCloudState, } from '../../memory/cloud/sync-disk.js';
|
|
38
40
|
import { fetchPlatformRun } from '../../features/harness/platform/platform-drives.js';
|
|
41
|
+
import { SERVER_VERSION } from '../version/identity/server-version.js';
|
|
39
42
|
/**
|
|
40
43
|
* Where `reticle login` dials when nothing says otherwise: the hosted service.
|
|
41
44
|
*
|
|
@@ -260,6 +263,23 @@ const cmdConnect = async (given) => {
|
|
|
260
263
|
/** `reticle config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server]`. */
|
|
261
264
|
const cmdConfig = async (argv) => {
|
|
262
265
|
const f = flags(argv);
|
|
266
|
+
// The Harness switch lives on the platform, not in cloud.json: every drive spends the workspace's
|
|
267
|
+
// credits, so it is the user's to turn on, with the project's own key.
|
|
268
|
+
if (f['harness'] !== undefined) {
|
|
269
|
+
const enabled = 'on' === f['harness'] ? true : 'off' === f['harness'] ? false : undefined;
|
|
270
|
+
if (enabled === undefined) {
|
|
271
|
+
err('--harness must be on|off');
|
|
272
|
+
return 2;
|
|
273
|
+
}
|
|
274
|
+
const { url, apiKey } = await repoCloud();
|
|
275
|
+
const env = { [ReticleEnv.API_KEY]: apiKey, [ReticleEnv.CLOUD_URL]: url };
|
|
276
|
+
if (!(await writeHarnessSwitch(env, enabled))) {
|
|
277
|
+
err(`the platform did not switch the Harness ${f['harness']} (locked, no access, or offline)`);
|
|
278
|
+
return 1;
|
|
279
|
+
}
|
|
280
|
+
emit({ harness: f['harness'] });
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
263
283
|
const linkPath = join(linkedRoot(), CLOUD_LINK_FILE);
|
|
264
284
|
const raw = await readJson(linkPath);
|
|
265
285
|
if (null === raw || typeof raw !== 'object') {
|
|
@@ -322,9 +342,7 @@ const cmdSync = async (argv) => {
|
|
|
322
342
|
capsules: () => (cloud.policy.capsules ? full.capsules() : []),
|
|
323
343
|
// `memory` is the project's cross-run history and the derived records that summarise it.
|
|
324
344
|
derived: (kind) => cloud.policy.memory ? full.derived(kind) : undefined,
|
|
325
|
-
...(
|
|
326
|
-
? { screenshot: full.screenshot }
|
|
327
|
-
: {}),
|
|
345
|
+
...policyScreenshots(full.screenshot, cloud.policy),
|
|
328
346
|
};
|
|
329
347
|
const once = async () => {
|
|
330
348
|
const report = await runSyncCycle({
|
|
@@ -336,6 +354,7 @@ const cmdSync = async (argv) => {
|
|
|
336
354
|
sink: diskSink(reticleRoot),
|
|
337
355
|
state: readCloudState(reticleRoot),
|
|
338
356
|
now: () => Date.now(),
|
|
357
|
+
clientVersion: SERVER_VERSION,
|
|
339
358
|
request: (url, init) => syncRequest(url, init),
|
|
340
359
|
});
|
|
341
360
|
emit({
|
|
@@ -508,8 +527,7 @@ const cmdMemory = async (argv) => {
|
|
|
508
527
|
// `undefined` for anything it cannot show to be this project's, so there is no ordering a future
|
|
509
528
|
// edit can get wrong — the body it must not print is one it was never handed. The verdict is read
|
|
510
529
|
// only to say WHICH refusal this is.
|
|
511
|
-
const
|
|
512
|
-
const body = await api('GET', memoryReadUrl(url, scope), apiKey);
|
|
530
|
+
const body = await readMemoryPages(url, { projectId, subject }, (page) => api('GET', page, apiKey));
|
|
513
531
|
const printable = scopeMemoryResponse(body, projectId);
|
|
514
532
|
if (printable === undefined) {
|
|
515
533
|
err(MemoryResponseScope.OTHER === scopeOfMemoryResponse(body, projectId)
|
|
@@ -15,9 +15,9 @@ import { homedir } from 'node:os';
|
|
|
15
15
|
import { join } from 'node:path';
|
|
16
16
|
import { z } from 'zod';
|
|
17
17
|
import { cloudFetch } from '../../memory/cloud/cloud-sync.js';
|
|
18
|
-
import { normalizeUrl, readSessionFrom, resolveSessionFor, sessionPath, } from './auth/cloud-session.js';
|
|
18
|
+
import { ACTIVE_SESSION_FILE, normalizeUrl, readSessionFrom, resolveSessionFor, sessionPath, } from './auth/cloud-session.js';
|
|
19
19
|
export const RETICLE_DIR = '.reticle';
|
|
20
|
-
export const SESSION_FILE =
|
|
20
|
+
export const SESSION_FILE = ACTIVE_SESSION_FILE;
|
|
21
21
|
export const CREDENTIALS_FILE = 'credentials.json';
|
|
22
22
|
/** The user-level reticle directory, `~/.reticle`. Sessions and credentials live here, not in a repo. */
|
|
23
23
|
export const home = () => join(homedir(), RETICLE_DIR);
|
|
@@ -105,7 +105,7 @@ export class CredentialRefusedError extends Error {
|
|
|
105
105
|
: reason.replace(/\.$/, '')}. Mint a new key in Settings → API keys and set RETICLE_API_KEY to it.`
|
|
106
106
|
: `the platform refused your sign-in (${String(status)}): ${reason === undefined || KEY_REFUSED === reason
|
|
107
107
|
? 'it expired, was revoked, or you no longer have access to the workspace'
|
|
108
|
-
: reason.replace(/\.$/, '')}. Run \`
|
|
108
|
+
: reason.replace(/\.$/, '')}. Run \`npx @reticlehq/server connect\`.`);
|
|
109
109
|
this.status = status;
|
|
110
110
|
this.apiKey = apiKey;
|
|
111
111
|
}
|
|
@@ -10,28 +10,9 @@ export declare const validateKey: (url: string, key: string) => Promise<z.infer<
|
|
|
10
10
|
declare const WhoamiSchema: z.ZodObject<{
|
|
11
11
|
projectId: z.ZodString;
|
|
12
12
|
projectName: z.ZodString;
|
|
13
|
-
/**
|
|
14
|
-
* Which tenant the key belongs to. Optional for the same reason `dashboardUrl` is — an older
|
|
15
|
-
* cloud does not send it — and when it is missing a stored key cannot be proved to be ours, which
|
|
16
|
-
* is why the reuse path below mints instead of guessing.
|
|
17
|
-
*/
|
|
18
13
|
orgId: z.ZodOptional<z.ZodString>;
|
|
19
|
-
/**
|
|
20
|
-
* Where this project's dashboard lives. Optional because an older cloud does not send it, and a
|
|
21
|
-
* CLI that refused to link against one would break the thing it is supposed to connect.
|
|
22
|
-
*/
|
|
23
14
|
dashboardUrl: z.ZodOptional<z.ZodString>;
|
|
24
|
-
},
|
|
25
|
-
projectId: string;
|
|
26
|
-
projectName: string;
|
|
27
|
-
orgId?: string | undefined;
|
|
28
|
-
dashboardUrl?: string | undefined;
|
|
29
|
-
}, {
|
|
30
|
-
projectId: string;
|
|
31
|
-
projectName: string;
|
|
32
|
-
orgId?: string | undefined;
|
|
33
|
-
dashboardUrl?: string | undefined;
|
|
34
|
-
}>;
|
|
15
|
+
}, z.core.$strip>;
|
|
35
16
|
export interface PlatformLinked {
|
|
36
17
|
via: PlatformLinkVia;
|
|
37
18
|
/** The project this folder was bound to before, if any: a re-run against it is not a new link. */
|
|
@@ -280,6 +280,8 @@ via = PlatformLinkVia.LINK) => {
|
|
|
280
280
|
* this can be read aloud, pasted into an issue, or left in a terminal without leaking anything.
|
|
281
281
|
*/
|
|
282
282
|
hint(`bound this repo to project "${projectName}"${inWorkspace(workspace)}`);
|
|
283
|
+
if (dashboardUrl !== undefined)
|
|
284
|
+
hint(`dashboard: ${dashboardUrl}`);
|
|
283
285
|
if (projectDir !== resolve(cwd))
|
|
284
286
|
hint(`linked ${relative(cwd, projectDir)}: that is where this app's runs are written`);
|
|
285
287
|
hint(reusedKey
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/** Run `reticle link` with these arguments. Injected; see the note at the top of this file. */
|
|
2
2
|
type Linker = (argv: readonly string[]) => Promise<number>;
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Email sign-in is gone: the platform answers its routes with 410 `email_sign_in_removed`. The sentence
|
|
5
|
+
* starts with the platform's own, so the terminal and the sign-in page agree. `--email`, `--code`
|
|
6
|
+
* and a positional address now stop with it, because the person who typed them wants no browser, and
|
|
7
|
+
* a device flow that waits for an approval nobody can give is worse than a clear refusal.
|
|
6
8
|
*/
|
|
9
|
+
export declare const EMAIL_SIGN_IN_REMOVED: string;
|
|
10
|
+
/** `reticle login` — the browser device flow. Email sign-in is gone (see EMAIL_SIGN_IN_REMOVED). */
|
|
7
11
|
export declare const cmdLogin: (argv: readonly string[], link: Linker, autoLink?: boolean) => Promise<number>;
|
|
8
12
|
/** `reticle logout` — forget the cached session token (per-project keys under credentials.json stay). */
|
|
9
13
|
/**
|
|
@@ -19,24 +19,16 @@ import { RETICLE_CONFIG_BASENAME } from './ports/resolve/cli-port.js';
|
|
|
19
19
|
import { DevicePollSchema, DeviceStartSchema, LoginOrigin, openBrowser, withLoginOrigin, } from './auth/device-flow.js';
|
|
20
20
|
import { normalizeUrl, sessionPath, SESSIONS_DIR } from './auth/cloud-session.js';
|
|
21
21
|
import { api, baseUrl, emit, err, flags, hint, home, readSession, RETICLE_DIR, SESSION_FILE, } from './cloud-kit.js';
|
|
22
|
-
const LoginSchema = z.object({
|
|
23
|
-
token: z.string(),
|
|
24
|
-
org: z.object({ id: z.string().optional(), name: z.string() }),
|
|
25
|
-
});
|
|
26
22
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* then exchange it. (It used to take an email alone — which meant anyone who knew your address owned your
|
|
32
|
-
* org.) `--org` is only consulted when the account is brand new; a returning user never needs it.
|
|
33
|
-
*
|
|
34
|
-
* Without `--code` we request one and stop, telling the user to re-run with it. The one exception is a
|
|
35
|
-
* LOCAL cloud, whose dev mailer cannot actually deliver mail and so echoes the code back in its response
|
|
36
|
-
* (`devCode`) — there we complete the login in a single command rather than asking a developer to read a
|
|
37
|
-
* code out of a server log they may not even be tailing.
|
|
23
|
+
* Email sign-in is gone: the platform answers its routes with 410 `email_sign_in_removed`. The sentence
|
|
24
|
+
* starts with the platform's own, so the terminal and the sign-in page agree. `--email`, `--code`
|
|
25
|
+
* and a positional address now stop with it, because the person who typed them wants no browser, and
|
|
26
|
+
* a device flow that waits for an approval nobody can give is worse than a clear refusal.
|
|
38
27
|
*/
|
|
39
|
-
const
|
|
28
|
+
export const EMAIL_SIGN_IN_REMOVED = 'Email sign-in is gone. Sign in with Google or GitHub: run `reticle connect`, which opens the browser. ' +
|
|
29
|
+
'Without a browser (CI, an agent), set RETICLE_API_KEY to a key from Settings > API keys.';
|
|
30
|
+
/** The flags the removed email sign-in took. Any one of them gets the sentence above. */
|
|
31
|
+
const EMAIL_FLAGS = ['email', 'code', 'org'];
|
|
40
32
|
const MeSchema = z.object({ email: z.string().min(1).nullable().optional() });
|
|
41
33
|
/** The signed-in address, asked of the cloud. Best-effort: a login never fails for want of it. */
|
|
42
34
|
const signedInEmail = async (url, token) => {
|
|
@@ -151,35 +143,15 @@ const cmdLoginDevice = async (explicitUrl, project, link, autoLink, open = openB
|
|
|
151
143
|
return 1;
|
|
152
144
|
}
|
|
153
145
|
};
|
|
154
|
-
/**
|
|
155
|
-
* `reticle login` — browser device flow by default; `--email <e>` (or a positional email) keeps the
|
|
156
|
-
* headless two-step code path for CI/servers where opening a browser makes no sense.
|
|
157
|
-
*/
|
|
146
|
+
/** `reticle login` — the browser device flow. Email sign-in is gone (see EMAIL_SIGN_IN_REMOVED). */
|
|
158
147
|
export const cmdLogin = async (argv, link, autoLink = true) => {
|
|
159
148
|
const f = flags(argv);
|
|
160
149
|
const positional = argv[0] !== undefined && !argv[0].startsWith('--') ? argv[0] : undefined;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return
|
|
164
|
-
const org = f['org'];
|
|
165
|
-
const url = baseUrl(null, f['url']);
|
|
166
|
-
let code = f['code'];
|
|
167
|
-
if (code === undefined) {
|
|
168
|
-
const requested = RequestCodeSchema.parse(await api('POST', `${url}/v1/auth/request-code`, null, {
|
|
169
|
-
email,
|
|
170
|
-
...(org !== undefined ? { orgName: org } : {}),
|
|
171
|
-
}));
|
|
172
|
-
// A real cloud mails the code and never echoes it; a local one cannot mail, so it hands it back.
|
|
173
|
-
if (requested.devCode === undefined) {
|
|
174
|
-
emit({ codeSent: true, to: email });
|
|
175
|
-
hint(`check your inbox, then: \`reticle login --email ${email} --code <the 6-digit code>\``);
|
|
176
|
-
return 0;
|
|
177
|
-
}
|
|
178
|
-
code = requested.devCode;
|
|
150
|
+
if (positional !== undefined || EMAIL_FLAGS.some((name) => f[name] !== undefined)) {
|
|
151
|
+
err(EMAIL_SIGN_IN_REMOVED);
|
|
152
|
+
return 2;
|
|
179
153
|
}
|
|
180
|
-
|
|
181
|
-
await writeSession(url, parsed.token, parsed.org.name, parsed.org.id, f['project'], link, email, autoLink);
|
|
182
|
-
return 0;
|
|
154
|
+
return cmdLoginDevice(f['url'], f['project'], link, autoLink);
|
|
183
155
|
};
|
|
184
156
|
/** `reticle logout` — forget the cached session token (per-project keys under credentials.json stay). */
|
|
185
157
|
/**
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Checkout } from '@reticlehq/core';
|
|
2
|
+
/** The checkout `dir` is in, or undefined outside git. */
|
|
3
|
+
export declare function checkoutAt(dir: string): Checkout | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* The same folder in every other checkout of the repository that holds `directory`: the main
|
|
6
|
+
* checkout and each linked worktree. Empty outside git and without worktrees.
|
|
7
|
+
*/
|
|
8
|
+
export declare function worktreeTwins(directory: string): string[];
|
|
9
|
+
/** True when the repository whose git directory is `commonDir` has a linked worktree. One read. */
|
|
10
|
+
export declare function hasLinkedWorktrees(commonDir: string): boolean;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The git checkout a directory is in, read from disk. The rule is core's `checkoutOf`; this file
|
|
3
|
+
* only supplies the reads, so every server module asks the same question the same way.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, readdirSync, realpathSync } from 'node:fs';
|
|
6
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
7
|
+
import { checkoutOf, samePath } from '@reticlehq/core';
|
|
8
|
+
/** Where git keeps one folder for each linked worktree, under the repository's git directory. */
|
|
9
|
+
const WORKTREES_DIR = 'worktrees';
|
|
10
|
+
/** The file in that folder that names the worktree's `.git`. */
|
|
11
|
+
const GITDIR_FILE = 'gitdir';
|
|
12
|
+
/**
|
|
13
|
+
* The long form of a path. Windows can spell one folder two ways (`RUNNER~1` and `runneradmin`), and
|
|
14
|
+
* git writes the long one, so a short-form caller was named as its own twin.
|
|
15
|
+
*/
|
|
16
|
+
const longForm = (path) => {
|
|
17
|
+
try {
|
|
18
|
+
return realpathSync.native(path);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return path;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const readOrUndefined = (path) => {
|
|
25
|
+
try {
|
|
26
|
+
return readFileSync(path, 'utf8');
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Missing, a directory, or unreadable: not the file we look for.
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
/** The checkout `dir` is in, or undefined outside git. */
|
|
34
|
+
export function checkoutAt(dir) {
|
|
35
|
+
return checkoutOf(dir, readOrUndefined);
|
|
36
|
+
}
|
|
37
|
+
/** The top directory of each linked worktree of the repository whose git directory is `commonDir`. */
|
|
38
|
+
function linkedWorktreeRoots(commonDir) {
|
|
39
|
+
const admin = join(commonDir, WORKTREES_DIR);
|
|
40
|
+
let names = [];
|
|
41
|
+
try {
|
|
42
|
+
names = readdirSync(admin);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
return names.flatMap((name) => {
|
|
48
|
+
// `<worktree>/.git`, absolute, or relative to this file when git writes relative paths.
|
|
49
|
+
const pointer = readOrUndefined(join(admin, name, GITDIR_FILE))?.trim();
|
|
50
|
+
return pointer === undefined || 0 === pointer.length
|
|
51
|
+
? []
|
|
52
|
+
: [dirname(resolve(admin, name, pointer))];
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The same folder in every other checkout of the repository that holds `directory`: the main
|
|
57
|
+
* checkout and each linked worktree. Empty outside git and without worktrees.
|
|
58
|
+
*/
|
|
59
|
+
export function worktreeTwins(directory) {
|
|
60
|
+
const from = checkoutAt(directory);
|
|
61
|
+
if (from === undefined)
|
|
62
|
+
return [];
|
|
63
|
+
const rel = relative(from.root, directory);
|
|
64
|
+
const roots = new Set([from.mainRoot, ...linkedWorktreeRoots(from.commonDir)]);
|
|
65
|
+
const self = longForm(from.root);
|
|
66
|
+
return [...roots]
|
|
67
|
+
.filter((root) => !samePath(longForm(root), self))
|
|
68
|
+
.map((root) => join(root, rel));
|
|
69
|
+
}
|
|
70
|
+
/** True when the repository whose git directory is `commonDir` has a linked worktree. One read. */
|
|
71
|
+
export function hasLinkedWorktrees(commonDir) {
|
|
72
|
+
try {
|
|
73
|
+
return readdirSync(join(commonDir, WORKTREES_DIR)).length > 0;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|