@reticlehq/server 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bridge-security.d.ts +0 -1
- package/dist/bridge-security.js +0 -1
- package/dist/bridge.d.ts +0 -1
- package/dist/bridge.js +60 -10
- package/dist/bridge.test-harness.d.ts +0 -1
- package/dist/bridge.test-harness.js +0 -1
- package/dist/capsule/capsule-store.d.ts +2 -3
- package/dist/capsule/capsule-store.js +0 -1
- package/dist/capsule/capsule.d.ts +0 -1
- package/dist/capsule/capsule.js +0 -1
- package/dist/capsule/causal-summary.d.ts +21 -1
- package/dist/capsule/causal-summary.js +20 -2
- package/dist/capsule/divergence.d.ts +0 -1
- package/dist/capsule/divergence.js +0 -1
- package/dist/capsule/minimize.d.ts +0 -1
- package/dist/capsule/minimize.js +0 -1
- package/dist/capsule/predicate-to-links.d.ts +0 -1
- package/dist/capsule/predicate-to-links.js +0 -1
- package/dist/cli-doctor.d.ts +6 -0
- package/dist/cli-doctor.js +60 -0
- package/dist/cli-flow-commands.d.ts +0 -1
- package/dist/cli-flow-commands.js +0 -1
- package/dist/cli-launch.d.ts +0 -1
- package/dist/cli-launch.js +7 -3
- package/dist/cli-parse.d.ts +52 -2
- package/dist/cli-parse.js +147 -3
- package/dist/cli-port.d.ts +0 -1
- package/dist/cli-port.js +0 -1
- package/dist/cli-verify.d.ts +2 -1
- package/dist/cli-verify.js +24 -3
- package/dist/cli.d.ts +2 -4
- package/dist/cli.js +109 -10
- package/dist/cloud/cloud-config.d.ts +36 -0
- package/dist/cloud/cloud-config.js +86 -0
- package/dist/cloud/cloud-sync.d.ts +96 -4
- package/dist/cloud/cloud-sync.js +131 -3
- package/dist/cloud-cli.d.ts +3 -0
- package/dist/cloud-cli.js +514 -0
- package/dist/crawl/crawl-tools.d.ts +0 -1
- package/dist/crawl/crawl-tools.js +1 -2
- package/dist/crawl/crawl.d.ts +17 -3
- package/dist/crawl/crawl.js +43 -4
- package/dist/daemon-resilience.d.ts +6 -1
- package/dist/daemon-resilience.js +63 -1
- package/dist/daemon.d.ts +23 -3
- package/dist/daemon.js +78 -21
- package/dist/domain/domain-model.d.ts +0 -1
- package/dist/domain/domain-model.js +1 -2
- package/dist/domain/domain-tools.d.ts +0 -7
- package/dist/domain/domain-tools.js +21 -4
- package/dist/domain/flow-risk.d.ts +0 -1
- package/dist/domain/flow-risk.js +0 -1
- package/dist/ee/audit-log.d.ts +0 -1
- package/dist/ee/audit-log.js +0 -1
- package/dist/events/body-failures.d.ts +4 -0
- package/dist/events/body-failures.js +113 -0
- package/dist/events/contradictions.d.ts +53 -0
- package/dist/events/contradictions.js +282 -0
- package/dist/events/echo-mismatch.d.ts +4 -0
- package/dist/events/echo-mismatch.js +138 -0
- package/dist/events/event-filters.d.ts +17 -3
- package/dist/events/event-filters.js +13 -3
- package/dist/events/json-salvage.d.ts +32 -0
- package/dist/events/json-salvage.js +96 -0
- package/dist/events/predicate-eval.d.ts +2 -1
- package/dist/events/predicate-eval.js +56 -9
- package/dist/events/predicate.d.ts +0 -1
- package/dist/events/predicate.js +56 -2
- package/dist/events/reaction.d.ts +0 -1
- package/dist/events/reaction.js +0 -1
- package/dist/events/reconcile.d.ts +39 -0
- package/dist/events/reconcile.js +211 -0
- package/dist/events/ring-buffer.d.ts +0 -1
- package/dist/events/ring-buffer.js +0 -1
- package/dist/events/stale-response.d.ts +4 -0
- package/dist/events/stale-response.js +139 -0
- package/dist/events/unit-mismatch.d.ts +16 -0
- package/dist/events/unit-mismatch.js +167 -0
- package/dist/flows/affected-tools.d.ts +18 -0
- package/dist/flows/affected-tools.js +52 -0
- package/dist/flows/affected.d.ts +0 -1
- package/dist/flows/affected.js +0 -1
- package/dist/flows/annotate-tools.d.ts +0 -1
- package/dist/flows/annotate-tools.js +21 -3
- package/dist/flows/annotate.d.ts +0 -1
- package/dist/flows/annotate.js +0 -1
- package/dist/flows/annotation-store.d.ts +0 -1
- package/dist/flows/annotation-store.js +0 -1
- package/dist/flows/assertion-integrity.d.ts +0 -1
- package/dist/flows/assertion-integrity.js +0 -1
- package/dist/flows/assertion-tiers-store.d.ts +0 -1
- package/dist/flows/assertion-tiers-store.js +0 -1
- package/dist/flows/buddy-status.d.ts +0 -1
- package/dist/flows/buddy-status.js +0 -1
- package/dist/flows/coverage.d.ts +0 -1
- package/dist/flows/coverage.js +0 -1
- package/dist/flows/decision.d.ts +0 -1
- package/dist/flows/decision.js +0 -1
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +0 -1
- package/dist/flows/flake.d.ts +0 -1
- package/dist/flows/flake.js +0 -1
- package/dist/flows/flow-classify.d.ts +0 -1
- package/dist/flows/flow-classify.js +0 -1
- package/dist/flows/flow-replay-run.d.ts +0 -1
- package/dist/flows/flow-replay-run.js +24 -8
- package/dist/flows/flow-replay.d.ts +20 -4
- package/dist/flows/flow-replay.js +9 -43
- package/dist/flows/flow-report.d.ts +0 -1
- package/dist/flows/flow-report.js +0 -1
- package/dist/flows/flow-scope.d.ts +0 -1
- package/dist/flows/flow-scope.js +0 -1
- package/dist/flows/flow-sources.d.ts +0 -1
- package/dist/flows/flow-sources.js +0 -1
- package/dist/flows/flow-step-runners.d.ts +23 -0
- package/dist/flows/flow-step-runners.js +102 -0
- package/dist/flows/flow-success.d.ts +0 -1
- package/dist/flows/flow-success.js +0 -1
- package/dist/flows/flow-tools.d.ts +0 -1
- package/dist/flows/flow-tools.js +54 -16
- package/dist/flows/flows.d.ts +6 -2
- package/dist/flows/flows.js +15 -2
- package/dist/flows/gate.d.ts +0 -1
- package/dist/flows/gate.js +0 -1
- package/dist/flows/git-changed.d.ts +0 -1
- package/dist/flows/git-changed.js +0 -1
- package/dist/flows/heal-run.d.ts +0 -1
- package/dist/flows/heal-run.js +0 -1
- package/dist/flows/heal.d.ts +0 -1
- package/dist/flows/heal.js +0 -1
- package/dist/flows/parallel-suite.d.ts +0 -1
- package/dist/flows/parallel-suite.js +0 -1
- package/dist/flows/recordings.d.ts +0 -1
- package/dist/flows/recordings.js +0 -1
- package/dist/flows/replay.d.ts +0 -1
- package/dist/flows/replay.js +71 -4
- package/dist/flows/server-verify.d.ts +15 -0
- package/dist/flows/server-verify.js +42 -0
- package/dist/flows/verify-change-tools.d.ts +2 -0
- package/dist/flows/verify-change-tools.js +187 -0
- package/dist/flows/watch-batcher.d.ts +0 -1
- package/dist/flows/watch-batcher.js +0 -1
- package/dist/honesty/accepted-write.d.ts +14 -0
- package/dist/honesty/accepted-write.js +17 -0
- package/dist/honesty/blind-spots.d.ts +18 -1
- package/dist/honesty/blind-spots.js +47 -5
- package/dist/honesty/honesty.d.ts +0 -1
- package/dist/honesty/honesty.js +0 -1
- package/dist/honesty/uncaptured-bodies.d.ts +19 -0
- package/dist/honesty/uncaptured-bodies.js +30 -0
- package/dist/honesty/unread-outcome.d.ts +2 -0
- package/dist/honesty/unread-outcome.js +50 -0
- package/dist/honesty/verified.d.ts +40 -0
- package/dist/honesty/verified.js +86 -0
- package/dist/http-server.d.ts +0 -1
- package/dist/http-server.js +5 -1
- package/dist/hunt/hunt-report.d.ts +50 -0
- package/dist/hunt/hunt-report.js +84 -0
- package/dist/idle-shutdown.d.ts +0 -1
- package/dist/idle-shutdown.js +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/init/agent-rules.d.ts +0 -1
- package/dist/init/agent-rules.js +4 -2
- package/dist/init/cursor.d.ts +0 -1
- package/dist/init/cursor.js +0 -1
- package/dist/init/desktop-doctor.d.ts +40 -0
- package/dist/init/desktop-doctor.js +198 -0
- package/dist/init/detect.d.ts +0 -1
- package/dist/init/detect.js +0 -1
- package/dist/init/mcp.d.ts +0 -1
- package/dist/init/mcp.js +0 -1
- package/dist/init/node-io.d.ts +0 -1
- package/dist/init/node-io.js +0 -1
- package/dist/init/plan.d.ts +4 -1
- package/dist/init/plan.js +23 -5
- package/dist/init/project-id.d.ts +0 -1
- package/dist/init/project-id.js +0 -1
- package/dist/init/run.d.ts +0 -1
- package/dist/init/run.js +31 -3
- package/dist/init/snippets.d.ts +5 -1
- package/dist/init/snippets.js +5 -1
- package/dist/init/vite-config.d.ts +0 -1
- package/dist/init/vite-config.js +0 -1
- package/dist/input/network-detail.d.ts +0 -1
- package/dist/input/network-detail.js +0 -1
- package/dist/input/network-mock-tools.d.ts +0 -1
- package/dist/input/network-mock-tools.js +3 -4
- package/dist/input/network-mock.d.ts +0 -1
- package/dist/input/network-mock.js +0 -1
- package/dist/input/real-input.d.ts +14 -1
- package/dist/input/real-input.js +45 -3
- package/dist/input/scroll-find.d.ts +0 -1
- package/dist/input/scroll-find.js +0 -1
- package/dist/input/scroll-tools.d.ts +0 -1
- package/dist/input/scroll-tools.js +0 -1
- package/dist/input/viewport-tools.d.ts +0 -1
- package/dist/input/viewport-tools.js +3 -4
- package/dist/journal/ambient-store.d.ts +0 -1
- package/dist/journal/ambient-store.js +0 -1
- package/dist/journal/ambient.d.ts +0 -1
- package/dist/journal/ambient.js +0 -1
- package/dist/journal/attach-journal.d.ts +0 -1
- package/dist/journal/attach-journal.js +0 -1
- package/dist/journal/deviation-report.d.ts +0 -1
- package/dist/journal/deviation-report.js +0 -1
- package/dist/journal/deviation-service.d.ts +0 -1
- package/dist/journal/deviation-service.js +0 -1
- package/dist/journal/envelope-store.d.ts +0 -1
- package/dist/journal/envelope-store.js +0 -1
- package/dist/journal/envelope.d.ts +8 -9
- package/dist/journal/envelope.js +0 -1
- package/dist/journal/journal-query.d.ts +0 -1
- package/dist/journal/journal-query.js +0 -1
- package/dist/journal/journal-recorder.d.ts +0 -1
- package/dist/journal/journal-recorder.js +0 -1
- package/dist/journal/retention.d.ts +0 -1
- package/dist/journal/retention.js +0 -1
- package/dist/journal/rollups.d.ts +0 -1
- package/dist/journal/rollups.js +0 -1
- package/dist/journal/session-end.d.ts +0 -1
- package/dist/journal/session-end.js +0 -1
- package/dist/journal/session-journal.d.ts +0 -1
- package/dist/journal/session-journal.js +0 -1
- package/dist/license/license.d.ts +0 -1
- package/dist/license/license.js +0 -1
- package/dist/log.d.ts +0 -1
- package/dist/log.js +0 -1
- package/dist/mcp-proxy.d.ts +0 -1
- package/dist/mcp-proxy.js +0 -1
- package/dist/mcp.d.ts +15 -1
- package/dist/mcp.js +145 -17
- package/dist/oracles/flow-instrument-gaps.d.ts +0 -1
- package/dist/oracles/flow-instrument-gaps.js +0 -1
- package/dist/oracles/propose-consequences.d.ts +0 -1
- package/dist/oracles/propose-consequences.js +0 -1
- package/dist/oracles/self-instrument.d.ts +0 -1
- package/dist/oracles/self-instrument.js +0 -1
- package/dist/pairing-token.d.ts +0 -1
- package/dist/pairing-token.js +0 -1
- package/dist/phenomena/phenomena.d.ts +0 -1
- package/dist/phenomena/phenomena.js +0 -1
- package/dist/pool/browser-pool.d.ts +0 -1
- package/dist/pool/browser-pool.js +0 -1
- package/dist/pool/lease-reaper.d.ts +0 -1
- package/dist/pool/lease-reaper.js +0 -1
- package/dist/pool/playwright-launcher.d.ts +2 -1
- package/dist/pool/playwright-launcher.js +22 -2
- package/dist/project/baselines.d.ts +0 -1
- package/dist/project/baselines.js +5 -4
- package/dist/project/file-lock.d.ts +2 -1
- package/dist/project/file-lock.js +12 -2
- package/dist/project/fs-port.d.ts +0 -1
- package/dist/project/fs-port.js +0 -1
- package/dist/project/project-store.d.ts +0 -1
- package/dist/project/project-store.js +20 -9
- package/dist/project/project-tools.d.ts +0 -1
- package/dist/project/project-tools.js +57 -9
- package/dist/project/reticle-dir.d.ts +0 -1
- package/dist/project/reticle-dir.js +0 -1
- package/dist/runs/build-verification-run.d.ts +0 -1
- package/dist/runs/build-verification-run.js +0 -1
- package/dist/runs/profile-redact.d.ts +0 -1
- package/dist/runs/profile-redact.js +0 -1
- package/dist/runs/render-report.d.ts +0 -1
- package/dist/runs/render-report.js +0 -1
- package/dist/runs/repair-prompt.d.ts +0 -1
- package/dist/runs/repair-prompt.js +0 -1
- package/dist/runs/replay-mapping.d.ts +0 -1
- package/dist/runs/replay-mapping.js +0 -1
- package/dist/runs/reticle-runner.d.ts +0 -1
- package/dist/runs/reticle-runner.js +4 -2
- package/dist/runs/risk-classify.d.ts +0 -1
- package/dist/runs/risk-classify.js +0 -1
- package/dist/runs/run-diff.d.ts +0 -1
- package/dist/runs/run-diff.js +0 -1
- package/dist/runs/run-store.d.ts +0 -1
- package/dist/runs/run-store.js +0 -1
- package/dist/runs/run-tools.d.ts +0 -1
- package/dist/runs/run-tools.js +0 -1
- package/dist/runs/runner-port.d.ts +0 -1
- package/dist/runs/runner-port.js +0 -1
- package/dist/runs/verification-sync.d.ts +23 -0
- package/dist/runs/verification-sync.js +66 -0
- package/dist/runs/verify-http.d.ts +0 -1
- package/dist/runs/verify-http.js +0 -1
- package/dist/runs/verify-server.d.ts +0 -1
- package/dist/runs/verify-server.js +0 -1
- package/dist/server-version.d.ts +0 -1
- package/dist/server-version.js +0 -1
- package/dist/session/command-timeout.d.ts +35 -0
- package/dist/session/command-timeout.js +41 -0
- package/dist/session/control-envelope.d.ts +2 -3
- package/dist/session/control-envelope.js +2 -3
- package/dist/session/last-act.d.ts +31 -0
- package/dist/session/last-act.js +24 -0
- package/dist/session/live-control-tools.d.ts +0 -1
- package/dist/session/live-control-tools.js +3 -4
- package/dist/session/live-control.d.ts +0 -1
- package/dist/session/live-control.js +0 -1
- package/dist/session/observed-state.d.ts +12 -2
- package/dist/session/observed-state.js +29 -2
- package/dist/session/output-budget.d.ts +0 -1
- package/dist/session/output-budget.js +0 -1
- package/dist/session/pending-commands.d.ts +12 -0
- package/dist/session/pending-commands.js +36 -0
- package/dist/session/presenter-args.d.ts +0 -1
- package/dist/session/presenter-args.js +0 -1
- package/dist/session/review-store.d.ts +0 -1
- package/dist/session/review-store.js +0 -1
- package/dist/session/session-health.d.ts +26 -2
- package/dist/session/session-health.js +13 -1
- package/dist/session/session-info.d.ts +29 -0
- package/dist/session/session-info.js +1 -0
- package/dist/session/session-lease.d.ts +25 -0
- package/dist/session/session-lease.js +14 -0
- package/dist/session/session-manager.d.ts +7 -19
- package/dist/session/session-manager.js +29 -2
- package/dist/session/session-readiness.d.ts +0 -1
- package/dist/session/session-readiness.js +3 -4
- package/dist/session/session-reaper.d.ts +0 -1
- package/dist/session/session-reaper.js +0 -1
- package/dist/session/session-recommendation.d.ts +0 -1
- package/dist/session/session-recommendation.js +0 -1
- package/dist/session/session-tools.d.ts +0 -1
- package/dist/session/session-tools.js +2 -3
- package/dist/session/session.d.ts +27 -53
- package/dist/session/session.js +95 -66
- package/dist/session/state-select.d.ts +0 -1
- package/dist/session/state-select.js +0 -1
- package/dist/telemetry/argument-shape.d.ts +41 -0
- package/dist/telemetry/argument-shape.js +87 -0
- package/dist/telemetry/bug-found.d.ts +33 -0
- package/dist/telemetry/bug-found.js +95 -0
- package/dist/telemetry/cli-telemetry.d.ts +13 -0
- package/dist/telemetry/cli-telemetry.js +43 -0
- package/dist/telemetry/connect-failure.d.ts +16 -0
- package/dist/telemetry/connect-failure.js +44 -0
- package/dist/telemetry/daemon-telemetry.d.ts +19 -0
- package/dist/telemetry/daemon-telemetry.js +76 -0
- package/dist/telemetry/dev-repo.d.ts +10 -0
- package/dist/telemetry/dev-repo.js +80 -0
- package/dist/telemetry/error-fingerprint.d.ts +24 -0
- package/dist/telemetry/error-fingerprint.js +88 -0
- package/dist/telemetry/feedback-cli.d.ts +31 -0
- package/dist/telemetry/feedback-cli.js +102 -0
- package/dist/telemetry/feedback-context.d.ts +32 -0
- package/dist/telemetry/feedback-context.js +143 -0
- package/dist/telemetry/feedback.d.ts +66 -0
- package/dist/telemetry/feedback.js +175 -0
- package/dist/telemetry/git-facts.d.ts +21 -0
- package/dist/telemetry/git-facts.js +109 -0
- package/dist/telemetry/identify.d.ts +22 -0
- package/dist/telemetry/identify.js +73 -0
- package/dist/telemetry/init-telemetry.d.ts +26 -0
- package/dist/telemetry/init-telemetry.js +33 -0
- package/dist/telemetry/machine-snapshot.d.ts +6 -0
- package/dist/telemetry/machine-snapshot.js +39 -0
- package/dist/telemetry/mcp-connection.d.ts +5 -0
- package/dist/telemetry/mcp-connection.js +44 -0
- package/dist/telemetry/project-profile.d.ts +44 -0
- package/dist/telemetry/project-profile.js +205 -0
- package/dist/telemetry/run-telemetry.d.ts +21 -0
- package/dist/telemetry/run-telemetry.js +62 -0
- package/dist/telemetry/session-metrics.d.ts +103 -0
- package/dist/telemetry/session-metrics.js +329 -0
- package/dist/telemetry/telemetry.d.ts +68 -0
- package/dist/telemetry/telemetry.js +313 -0
- package/dist/token-auth.d.ts +0 -1
- package/dist/token-auth.js +0 -1
- package/dist/tools/act-capsule.d.ts +23 -0
- package/dist/tools/act-capsule.js +47 -0
- package/dist/tools/act-danger.d.ts +7 -0
- package/dist/tools/act-danger.js +44 -0
- package/dist/tools/act-helpers.d.ts +17 -0
- package/dist/tools/act-helpers.js +40 -0
- package/dist/tools/act-tools.d.ts +8 -1
- package/dist/tools/act-tools.js +96 -98
- package/dist/tools/act-view.d.ts +7 -1
- package/dist/tools/act-view.js +13 -1
- package/dist/tools/assert-grade.d.ts +22 -1
- package/dist/tools/assert-grade.js +46 -1
- package/dist/tools/assert-verdict.d.ts +21 -0
- package/dist/tools/assert-verdict.js +62 -0
- package/dist/tools/browser-tools.d.ts +0 -1
- package/dist/tools/browser-tools.js +2 -2
- package/dist/tools/contract-tools.d.ts +0 -1
- package/dist/tools/contract-tools.js +0 -1
- package/dist/tools/coverage-tools.d.ts +9 -0
- package/dist/tools/coverage-tools.js +92 -0
- package/dist/tools/dynamic-tools.d.ts +0 -1
- package/dist/tools/dynamic-tools.js +21 -2
- package/dist/tools/error-recovery.d.ts +18 -2
- package/dist/tools/error-recovery.js +42 -7
- package/dist/tools/feedback-tools.d.ts +24 -0
- package/dist/tools/feedback-tools.js +180 -0
- package/dist/tools/invoke-tool.d.ts +0 -1
- package/dist/tools/invoke-tool.js +116 -2
- package/dist/tools/lease-tools.d.ts +0 -1
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +0 -1
- package/dist/tools/merge-tools.js +0 -1
- package/dist/tools/observe-tools.d.ts +0 -1
- package/dist/tools/observe-tools.js +91 -39
- package/dist/tools/profiles.d.ts +1 -2
- package/dist/tools/profiles.js +43 -16
- package/dist/tools/query-paginate.d.ts +0 -1
- package/dist/tools/query-paginate.js +0 -1
- package/dist/tools/read-tools.d.ts +0 -1
- package/dist/tools/read-tools.js +10 -8
- package/dist/tools/reconcile-tools.d.ts +2 -0
- package/dist/tools/reconcile-tools.js +111 -0
- package/dist/tools/snapshot-delta.d.ts +0 -1
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-invoker.d.ts +0 -1
- package/dist/tools/tool-invoker.js +0 -1
- package/dist/tools/tool-kit.d.ts +16 -1
- package/dist/tools/tool-kit.js +8 -2
- package/dist/tools/tool-names.d.ts +6 -1
- package/dist/tools/tool-names.js +6 -1
- package/dist/tools/tools-helpers.d.ts +0 -1
- package/dist/tools/tools-helpers.js +0 -1
- package/dist/tools/tools.d.ts +0 -1
- package/dist/tools/tools.js +24 -4
- package/dist/update/update-checker.d.ts +0 -1
- package/dist/update/update-checker.js +26 -2
- package/dist/update/update-nudge.d.ts +20 -0
- package/dist/update/update-nudge.js +72 -0
- package/dist/update/updater.d.ts +14 -1
- package/dist/update/updater.js +33 -2
- package/dist/visual/visual-diff.d.ts +0 -1
- package/dist/visual/visual-diff.js +0 -1
- package/dist/visual/visual-store.d.ts +0 -1
- package/dist/visual/visual-store.js +0 -1
- package/dist/visual/visual-tools.d.ts +0 -1
- package/dist/visual/visual-tools.js +103 -16
- package/package.json +2 -2
- package/dist/bridge-security.d.ts.map +0 -1
- package/dist/bridge-security.js.map +0 -1
- package/dist/bridge.d.ts.map +0 -1
- package/dist/bridge.js.map +0 -1
- package/dist/bridge.test-harness.d.ts.map +0 -1
- package/dist/bridge.test-harness.js.map +0 -1
- package/dist/capsule/capsule-store.d.ts.map +0 -1
- package/dist/capsule/capsule-store.js.map +0 -1
- package/dist/capsule/capsule.d.ts.map +0 -1
- package/dist/capsule/capsule.js.map +0 -1
- package/dist/capsule/causal-summary.d.ts.map +0 -1
- package/dist/capsule/causal-summary.js.map +0 -1
- package/dist/capsule/divergence.d.ts.map +0 -1
- package/dist/capsule/divergence.js.map +0 -1
- package/dist/capsule/minimize.d.ts.map +0 -1
- package/dist/capsule/minimize.js.map +0 -1
- package/dist/capsule/predicate-to-links.d.ts.map +0 -1
- package/dist/capsule/predicate-to-links.js.map +0 -1
- package/dist/cli-flow-commands.d.ts.map +0 -1
- package/dist/cli-flow-commands.js.map +0 -1
- package/dist/cli-launch.d.ts.map +0 -1
- package/dist/cli-launch.js.map +0 -1
- package/dist/cli-parse.d.ts.map +0 -1
- package/dist/cli-parse.js.map +0 -1
- package/dist/cli-port.d.ts.map +0 -1
- package/dist/cli-port.js.map +0 -1
- package/dist/cli-verify.d.ts.map +0 -1
- package/dist/cli-verify.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cloud/cloud-sync.d.ts.map +0 -1
- package/dist/cloud/cloud-sync.js.map +0 -1
- package/dist/crawl/crawl-tools.d.ts.map +0 -1
- package/dist/crawl/crawl-tools.js.map +0 -1
- package/dist/crawl/crawl.d.ts.map +0 -1
- package/dist/crawl/crawl.js.map +0 -1
- package/dist/daemon-resilience.d.ts.map +0 -1
- package/dist/daemon-resilience.js.map +0 -1
- package/dist/daemon.d.ts.map +0 -1
- package/dist/daemon.js.map +0 -1
- package/dist/domain/domain-model.d.ts.map +0 -1
- package/dist/domain/domain-model.js.map +0 -1
- package/dist/domain/domain-tools.d.ts.map +0 -1
- package/dist/domain/domain-tools.js.map +0 -1
- package/dist/domain/flow-risk.d.ts.map +0 -1
- package/dist/domain/flow-risk.js.map +0 -1
- package/dist/ee/audit-log.d.ts.map +0 -1
- package/dist/ee/audit-log.js.map +0 -1
- package/dist/events/event-filters.d.ts.map +0 -1
- package/dist/events/event-filters.js.map +0 -1
- package/dist/events/predicate-eval.d.ts.map +0 -1
- package/dist/events/predicate-eval.js.map +0 -1
- package/dist/events/predicate.d.ts.map +0 -1
- package/dist/events/predicate.js.map +0 -1
- package/dist/events/reaction.d.ts.map +0 -1
- package/dist/events/reaction.js.map +0 -1
- package/dist/events/ring-buffer.d.ts.map +0 -1
- package/dist/events/ring-buffer.js.map +0 -1
- package/dist/flows/affected.d.ts.map +0 -1
- package/dist/flows/affected.js.map +0 -1
- package/dist/flows/annotate-tools.d.ts.map +0 -1
- package/dist/flows/annotate-tools.js.map +0 -1
- package/dist/flows/annotate.d.ts.map +0 -1
- package/dist/flows/annotate.js.map +0 -1
- package/dist/flows/annotation-store.d.ts.map +0 -1
- package/dist/flows/annotation-store.js.map +0 -1
- package/dist/flows/assertion-integrity.d.ts.map +0 -1
- package/dist/flows/assertion-integrity.js.map +0 -1
- package/dist/flows/assertion-tiers-store.d.ts.map +0 -1
- package/dist/flows/assertion-tiers-store.js.map +0 -1
- package/dist/flows/buddy-status.d.ts.map +0 -1
- package/dist/flows/buddy-status.js.map +0 -1
- package/dist/flows/coverage.d.ts.map +0 -1
- package/dist/flows/coverage.js.map +0 -1
- package/dist/flows/decision.d.ts.map +0 -1
- package/dist/flows/decision.js.map +0 -1
- package/dist/flows/flake-store.d.ts.map +0 -1
- package/dist/flows/flake-store.js.map +0 -1
- package/dist/flows/flake.d.ts.map +0 -1
- package/dist/flows/flake.js.map +0 -1
- package/dist/flows/flow-classify.d.ts.map +0 -1
- package/dist/flows/flow-classify.js.map +0 -1
- package/dist/flows/flow-replay-run.d.ts.map +0 -1
- package/dist/flows/flow-replay-run.js.map +0 -1
- package/dist/flows/flow-replay.d.ts.map +0 -1
- package/dist/flows/flow-replay.js.map +0 -1
- package/dist/flows/flow-report.d.ts.map +0 -1
- package/dist/flows/flow-report.js.map +0 -1
- package/dist/flows/flow-scope.d.ts.map +0 -1
- package/dist/flows/flow-scope.js.map +0 -1
- package/dist/flows/flow-sources.d.ts.map +0 -1
- package/dist/flows/flow-sources.js.map +0 -1
- package/dist/flows/flow-success.d.ts.map +0 -1
- package/dist/flows/flow-success.js.map +0 -1
- package/dist/flows/flow-tools.d.ts.map +0 -1
- package/dist/flows/flow-tools.js.map +0 -1
- package/dist/flows/flows.d.ts.map +0 -1
- package/dist/flows/flows.js.map +0 -1
- package/dist/flows/gate.d.ts.map +0 -1
- package/dist/flows/gate.js.map +0 -1
- package/dist/flows/git-changed.d.ts.map +0 -1
- package/dist/flows/git-changed.js.map +0 -1
- package/dist/flows/heal-run.d.ts.map +0 -1
- package/dist/flows/heal-run.js.map +0 -1
- package/dist/flows/heal.d.ts.map +0 -1
- package/dist/flows/heal.js.map +0 -1
- package/dist/flows/parallel-suite.d.ts.map +0 -1
- package/dist/flows/parallel-suite.js.map +0 -1
- package/dist/flows/recordings.d.ts.map +0 -1
- package/dist/flows/recordings.js.map +0 -1
- package/dist/flows/replay.d.ts.map +0 -1
- package/dist/flows/replay.js.map +0 -1
- package/dist/flows/watch-batcher.d.ts.map +0 -1
- package/dist/flows/watch-batcher.js.map +0 -1
- package/dist/honesty/blind-spots.d.ts.map +0 -1
- package/dist/honesty/blind-spots.js.map +0 -1
- package/dist/honesty/honesty.d.ts.map +0 -1
- package/dist/honesty/honesty.js.map +0 -1
- package/dist/http-server.d.ts.map +0 -1
- package/dist/http-server.js.map +0 -1
- package/dist/idle-shutdown.d.ts.map +0 -1
- package/dist/idle-shutdown.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/init/agent-rules.d.ts.map +0 -1
- package/dist/init/agent-rules.js.map +0 -1
- package/dist/init/cursor.d.ts.map +0 -1
- package/dist/init/cursor.js.map +0 -1
- package/dist/init/detect.d.ts.map +0 -1
- package/dist/init/detect.js.map +0 -1
- package/dist/init/mcp.d.ts.map +0 -1
- package/dist/init/mcp.js.map +0 -1
- package/dist/init/node-io.d.ts.map +0 -1
- package/dist/init/node-io.js.map +0 -1
- package/dist/init/plan.d.ts.map +0 -1
- package/dist/init/plan.js.map +0 -1
- package/dist/init/project-id.d.ts.map +0 -1
- package/dist/init/project-id.js.map +0 -1
- package/dist/init/run.d.ts.map +0 -1
- package/dist/init/run.js.map +0 -1
- package/dist/init/snippets.d.ts.map +0 -1
- package/dist/init/snippets.js.map +0 -1
- package/dist/init/vite-config.d.ts.map +0 -1
- package/dist/init/vite-config.js.map +0 -1
- package/dist/input/network-detail.d.ts.map +0 -1
- package/dist/input/network-detail.js.map +0 -1
- package/dist/input/network-mock-tools.d.ts.map +0 -1
- package/dist/input/network-mock-tools.js.map +0 -1
- package/dist/input/network-mock.d.ts.map +0 -1
- package/dist/input/network-mock.js.map +0 -1
- package/dist/input/real-input.d.ts.map +0 -1
- package/dist/input/real-input.js.map +0 -1
- package/dist/input/scroll-find.d.ts.map +0 -1
- package/dist/input/scroll-find.js.map +0 -1
- package/dist/input/scroll-tools.d.ts.map +0 -1
- package/dist/input/scroll-tools.js.map +0 -1
- package/dist/input/viewport-tools.d.ts.map +0 -1
- package/dist/input/viewport-tools.js.map +0 -1
- package/dist/journal/ambient-store.d.ts.map +0 -1
- package/dist/journal/ambient-store.js.map +0 -1
- package/dist/journal/ambient.d.ts.map +0 -1
- package/dist/journal/ambient.js.map +0 -1
- package/dist/journal/attach-journal.d.ts.map +0 -1
- package/dist/journal/attach-journal.js.map +0 -1
- package/dist/journal/deviation-report.d.ts.map +0 -1
- package/dist/journal/deviation-report.js.map +0 -1
- package/dist/journal/deviation-service.d.ts.map +0 -1
- package/dist/journal/deviation-service.js.map +0 -1
- package/dist/journal/envelope-store.d.ts.map +0 -1
- package/dist/journal/envelope-store.js.map +0 -1
- package/dist/journal/envelope.d.ts.map +0 -1
- package/dist/journal/envelope.js.map +0 -1
- package/dist/journal/journal-query.d.ts.map +0 -1
- package/dist/journal/journal-query.js.map +0 -1
- package/dist/journal/journal-recorder.d.ts.map +0 -1
- package/dist/journal/journal-recorder.js.map +0 -1
- package/dist/journal/retention.d.ts.map +0 -1
- package/dist/journal/retention.js.map +0 -1
- package/dist/journal/rollups.d.ts.map +0 -1
- package/dist/journal/rollups.js.map +0 -1
- package/dist/journal/session-end.d.ts.map +0 -1
- package/dist/journal/session-end.js.map +0 -1
- package/dist/journal/session-journal.d.ts.map +0 -1
- package/dist/journal/session-journal.js.map +0 -1
- package/dist/license/license.d.ts.map +0 -1
- package/dist/license/license.js.map +0 -1
- package/dist/log.d.ts.map +0 -1
- package/dist/log.js.map +0 -1
- package/dist/mcp-proxy.d.ts.map +0 -1
- package/dist/mcp-proxy.js.map +0 -1
- package/dist/mcp.d.ts.map +0 -1
- package/dist/mcp.js.map +0 -1
- package/dist/oracles/flow-instrument-gaps.d.ts.map +0 -1
- package/dist/oracles/flow-instrument-gaps.js.map +0 -1
- package/dist/oracles/propose-consequences.d.ts.map +0 -1
- package/dist/oracles/propose-consequences.js.map +0 -1
- package/dist/oracles/self-instrument.d.ts.map +0 -1
- package/dist/oracles/self-instrument.js.map +0 -1
- package/dist/pairing-token.d.ts.map +0 -1
- package/dist/pairing-token.js.map +0 -1
- package/dist/phenomena/phenomena.d.ts.map +0 -1
- package/dist/phenomena/phenomena.js.map +0 -1
- package/dist/pool/browser-pool.d.ts.map +0 -1
- package/dist/pool/browser-pool.js.map +0 -1
- package/dist/pool/lease-reaper.d.ts.map +0 -1
- package/dist/pool/lease-reaper.js.map +0 -1
- package/dist/pool/playwright-launcher.d.ts.map +0 -1
- package/dist/pool/playwright-launcher.js.map +0 -1
- package/dist/project/baselines.d.ts.map +0 -1
- package/dist/project/baselines.js.map +0 -1
- package/dist/project/file-lock.d.ts.map +0 -1
- package/dist/project/file-lock.js.map +0 -1
- package/dist/project/fs-port.d.ts.map +0 -1
- package/dist/project/fs-port.js.map +0 -1
- package/dist/project/project-store.d.ts.map +0 -1
- package/dist/project/project-store.js.map +0 -1
- package/dist/project/project-tools.d.ts.map +0 -1
- package/dist/project/project-tools.js.map +0 -1
- package/dist/project/reticle-dir.d.ts.map +0 -1
- package/dist/project/reticle-dir.js.map +0 -1
- package/dist/runs/build-verification-run.d.ts.map +0 -1
- package/dist/runs/build-verification-run.js.map +0 -1
- package/dist/runs/profile-redact.d.ts.map +0 -1
- package/dist/runs/profile-redact.js.map +0 -1
- package/dist/runs/render-report.d.ts.map +0 -1
- package/dist/runs/render-report.js.map +0 -1
- package/dist/runs/repair-prompt.d.ts.map +0 -1
- package/dist/runs/repair-prompt.js.map +0 -1
- package/dist/runs/replay-mapping.d.ts.map +0 -1
- package/dist/runs/replay-mapping.js.map +0 -1
- package/dist/runs/reticle-runner.d.ts.map +0 -1
- package/dist/runs/reticle-runner.js.map +0 -1
- package/dist/runs/risk-classify.d.ts.map +0 -1
- package/dist/runs/risk-classify.js.map +0 -1
- package/dist/runs/run-diff.d.ts.map +0 -1
- package/dist/runs/run-diff.js.map +0 -1
- package/dist/runs/run-store.d.ts.map +0 -1
- package/dist/runs/run-store.js.map +0 -1
- package/dist/runs/run-tools.d.ts.map +0 -1
- package/dist/runs/run-tools.js.map +0 -1
- package/dist/runs/runner-port.d.ts.map +0 -1
- package/dist/runs/runner-port.js.map +0 -1
- package/dist/runs/verify-http.d.ts.map +0 -1
- package/dist/runs/verify-http.js.map +0 -1
- package/dist/runs/verify-server.d.ts.map +0 -1
- package/dist/runs/verify-server.js.map +0 -1
- package/dist/server-version.d.ts.map +0 -1
- package/dist/server-version.js.map +0 -1
- package/dist/session/control-envelope.d.ts.map +0 -1
- package/dist/session/control-envelope.js.map +0 -1
- package/dist/session/live-control-tools.d.ts.map +0 -1
- package/dist/session/live-control-tools.js.map +0 -1
- package/dist/session/live-control.d.ts.map +0 -1
- package/dist/session/live-control.js.map +0 -1
- package/dist/session/observed-state.d.ts.map +0 -1
- package/dist/session/observed-state.js.map +0 -1
- package/dist/session/output-budget.d.ts.map +0 -1
- package/dist/session/output-budget.js.map +0 -1
- package/dist/session/presenter-args.d.ts.map +0 -1
- package/dist/session/presenter-args.js.map +0 -1
- package/dist/session/review-store.d.ts.map +0 -1
- package/dist/session/review-store.js.map +0 -1
- package/dist/session/session-health.d.ts.map +0 -1
- package/dist/session/session-health.js.map +0 -1
- package/dist/session/session-manager.d.ts.map +0 -1
- package/dist/session/session-manager.js.map +0 -1
- package/dist/session/session-readiness.d.ts.map +0 -1
- package/dist/session/session-readiness.js.map +0 -1
- package/dist/session/session-reaper.d.ts.map +0 -1
- package/dist/session/session-reaper.js.map +0 -1
- package/dist/session/session-recommendation.d.ts.map +0 -1
- package/dist/session/session-recommendation.js.map +0 -1
- package/dist/session/session-tools.d.ts.map +0 -1
- package/dist/session/session-tools.js.map +0 -1
- package/dist/session/session.d.ts.map +0 -1
- package/dist/session/session.js.map +0 -1
- package/dist/session/state-select.d.ts.map +0 -1
- package/dist/session/state-select.js.map +0 -1
- package/dist/token-auth.d.ts.map +0 -1
- package/dist/token-auth.js.map +0 -1
- package/dist/tools/act-tools.d.ts.map +0 -1
- package/dist/tools/act-tools.js.map +0 -1
- package/dist/tools/act-view.d.ts.map +0 -1
- package/dist/tools/act-view.js.map +0 -1
- package/dist/tools/assert-grade.d.ts.map +0 -1
- package/dist/tools/assert-grade.js.map +0 -1
- package/dist/tools/browser-tools.d.ts.map +0 -1
- package/dist/tools/browser-tools.js.map +0 -1
- package/dist/tools/contract-tools.d.ts.map +0 -1
- package/dist/tools/contract-tools.js.map +0 -1
- package/dist/tools/dynamic-tools.d.ts.map +0 -1
- package/dist/tools/dynamic-tools.js.map +0 -1
- package/dist/tools/error-recovery.d.ts.map +0 -1
- package/dist/tools/error-recovery.js.map +0 -1
- package/dist/tools/invoke-tool.d.ts.map +0 -1
- package/dist/tools/invoke-tool.js.map +0 -1
- package/dist/tools/lease-tools.d.ts.map +0 -1
- package/dist/tools/lease-tools.js.map +0 -1
- package/dist/tools/merge-tools.d.ts.map +0 -1
- package/dist/tools/merge-tools.js.map +0 -1
- package/dist/tools/observe-tools.d.ts.map +0 -1
- package/dist/tools/observe-tools.js.map +0 -1
- package/dist/tools/profiles.d.ts.map +0 -1
- package/dist/tools/profiles.js.map +0 -1
- package/dist/tools/query-paginate.d.ts.map +0 -1
- package/dist/tools/query-paginate.js.map +0 -1
- package/dist/tools/read-tools.d.ts.map +0 -1
- package/dist/tools/read-tools.js.map +0 -1
- package/dist/tools/snapshot-delta.d.ts.map +0 -1
- package/dist/tools/snapshot-delta.js.map +0 -1
- package/dist/tools/tool-invoker.d.ts.map +0 -1
- package/dist/tools/tool-invoker.js.map +0 -1
- package/dist/tools/tool-kit.d.ts.map +0 -1
- package/dist/tools/tool-kit.js.map +0 -1
- package/dist/tools/tool-names.d.ts.map +0 -1
- package/dist/tools/tool-names.js.map +0 -1
- package/dist/tools/tools-helpers.d.ts.map +0 -1
- package/dist/tools/tools-helpers.js.map +0 -1
- package/dist/tools/tools.d.ts.map +0 -1
- package/dist/tools/tools.js.map +0 -1
- package/dist/update/update-checker.d.ts.map +0 -1
- package/dist/update/update-checker.js.map +0 -1
- package/dist/update/updater.d.ts.map +0 -1
- package/dist/update/updater.js.map +0 -1
- package/dist/visual/visual-diff.d.ts.map +0 -1
- package/dist/visual/visual-diff.js.map +0 -1
- package/dist/visual/visual-store.d.ts.map +0 -1
- package/dist/visual/visual-store.js.map +0 -1
- package/dist/visual/visual-tools.d.ts.map +0 -1
- package/dist/visual/visual-tools.js.map +0 -1
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloud subcommands for the `reticle` CLI — the user/agent door to Reticle Cloud, folded into the ONE
|
|
3
|
+
* tool (was the standalone `reticle-cloud` bootstrap script). These are THIN clients over the `/v1` API:
|
|
4
|
+
* the moat is the server, not these verbs, and OSS reticle already ships the cloud-sync client — this just
|
|
5
|
+
* surfaces it. Creds live under `~/.reticle`: `session.json` (human token from `reticle login`) and
|
|
6
|
+
* `credentials.json` (per-project api keys from `reticle link`). The non-secret repo binding + sync policy
|
|
7
|
+
* is `<repo>/.reticle/cloud.json`. Auth for a command = `RETICLE_CLOUD_KEY` env (agent) OR the login token.
|
|
8
|
+
*/
|
|
9
|
+
import { mkdir, writeFile, readFile } from 'node:fs/promises';
|
|
10
|
+
import { spawn } from 'node:child_process';
|
|
11
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { createNodeFileSystem } from './project/fs-port.js';
|
|
16
|
+
import { RunStore } from './runs/run-store.js';
|
|
17
|
+
import { resolveProjectCloud } from './cloud/cloud-config.js';
|
|
18
|
+
import { syncRunToCloud, SyncOutcome } from './cloud/cloud-sync.js';
|
|
19
|
+
const DEFAULT_URL = 'http://localhost:8890';
|
|
20
|
+
const RETICLE_DIR = '.reticle';
|
|
21
|
+
const SESSION_FILE = 'session.json';
|
|
22
|
+
const CREDENTIALS_FILE = 'credentials.json';
|
|
23
|
+
const CLOUD_LINK_FILE = 'cloud.json';
|
|
24
|
+
const DEFAULT_PROJECT_ID = 'default';
|
|
25
|
+
const CLOUD_COMMANDS = new Set([
|
|
26
|
+
'login',
|
|
27
|
+
'logout',
|
|
28
|
+
'whoami',
|
|
29
|
+
'link',
|
|
30
|
+
'project',
|
|
31
|
+
'config',
|
|
32
|
+
'push',
|
|
33
|
+
'runs',
|
|
34
|
+
'regression',
|
|
35
|
+
'share',
|
|
36
|
+
]);
|
|
37
|
+
export const isCloudCommand = (cmd) => cmd !== undefined && CLOUD_COMMANDS.has(cmd);
|
|
38
|
+
const home = () => join(homedir(), RETICLE_DIR);
|
|
39
|
+
const err = (msg) => {
|
|
40
|
+
process.stderr.write(`reticle: ${msg}\n`);
|
|
41
|
+
};
|
|
42
|
+
/** A next-step nudge on stderr (humans read it; agents parse stdout JSON and ignore this). */
|
|
43
|
+
const hint = (msg) => {
|
|
44
|
+
process.stderr.write(`→ ${msg}\n`);
|
|
45
|
+
};
|
|
46
|
+
const emit = (obj) => {
|
|
47
|
+
process.stdout.write(`${JSON.stringify(obj, null, 2)}\n`);
|
|
48
|
+
};
|
|
49
|
+
/** Read + parse a JSON file, or null when missing/malformed (never throws). */
|
|
50
|
+
const readJson = async (path) => {
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(await readFile(path, 'utf8'));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
/** Parse `--flag value` pairs out of an argv tail. */
|
|
59
|
+
const flags = (argv) => {
|
|
60
|
+
const f = {};
|
|
61
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
62
|
+
const a = argv[i];
|
|
63
|
+
if (a !== undefined && a.startsWith('--') && i + 1 < argv.length) {
|
|
64
|
+
const v = argv[i + 1];
|
|
65
|
+
if (v !== undefined)
|
|
66
|
+
f[a.slice(2)] = v;
|
|
67
|
+
i += 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return f;
|
|
71
|
+
};
|
|
72
|
+
const SessionSchema = z.object({ url: z.string(), token: z.string(), orgName: z.string() });
|
|
73
|
+
const readSession = async () => {
|
|
74
|
+
const parsed = SessionSchema.safeParse(await readJson(join(home(), SESSION_FILE)));
|
|
75
|
+
return parsed.success ? parsed.data : null;
|
|
76
|
+
};
|
|
77
|
+
const baseUrl = (session) => {
|
|
78
|
+
const env = process.env['RETICLE_CLOUD_URL'];
|
|
79
|
+
return env !== undefined && env.length > 0
|
|
80
|
+
? env.replace(/\/+$/, '')
|
|
81
|
+
: (session?.url ?? DEFAULT_URL);
|
|
82
|
+
};
|
|
83
|
+
/** Bearer for a command: an explicit api key (agent) wins, else the human login token. */
|
|
84
|
+
const bearer = (session) => {
|
|
85
|
+
const key = process.env['RETICLE_CLOUD_KEY'];
|
|
86
|
+
if (key !== undefined && key.length > 0)
|
|
87
|
+
return key;
|
|
88
|
+
return session?.token ?? null;
|
|
89
|
+
};
|
|
90
|
+
/** One `/v1` call. Throws a friendly Error on a non-2xx so the command surfaces it and exits 1. */
|
|
91
|
+
const api = async (method, url, token, body) => {
|
|
92
|
+
const headers = {};
|
|
93
|
+
if (token !== null)
|
|
94
|
+
headers['authorization'] = `Bearer ${token}`;
|
|
95
|
+
if (body !== undefined)
|
|
96
|
+
headers['content-type'] = 'application/json';
|
|
97
|
+
const init = {
|
|
98
|
+
method,
|
|
99
|
+
headers,
|
|
100
|
+
};
|
|
101
|
+
if (body !== undefined)
|
|
102
|
+
init.body = JSON.stringify(body);
|
|
103
|
+
const res = await fetch(url, init);
|
|
104
|
+
const text = await res.text();
|
|
105
|
+
let json = null;
|
|
106
|
+
if (text.length > 0) {
|
|
107
|
+
try {
|
|
108
|
+
json = JSON.parse(text);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
throw new Error(`expected JSON from ${method} ${url} but got: ${text.slice(0, 120)}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
const parsed = z.object({ error: z.object({ message: z.string() }) }).safeParse(json);
|
|
116
|
+
throw new Error(parsed.success ? parsed.data.error.message : `${res.status} ${res.statusText}`);
|
|
117
|
+
}
|
|
118
|
+
return json;
|
|
119
|
+
};
|
|
120
|
+
const LoginSchema = z.object({ token: z.string(), org: z.object({ name: z.string() }) });
|
|
121
|
+
const KeySchema = z.object({ projectId: z.string(), projectName: z.string(), key: z.string() });
|
|
122
|
+
const WhoamiSchema = z.object({ projectId: z.string(), projectName: z.string() });
|
|
123
|
+
const CreatedProjectSchema = z.object({ projectId: z.string(), name: z.string() });
|
|
124
|
+
const ProjectsListSchema = z.object({
|
|
125
|
+
projects: z.array(z.object({ projectId: z.string(), name: z.string() })),
|
|
126
|
+
});
|
|
127
|
+
/** Resolve a --project value that may be a slug id OR a display name into the canonical projectId. */
|
|
128
|
+
const resolveProjectId = async (url, token, wanted) => {
|
|
129
|
+
const { projects } = ProjectsListSchema.parse(await api('GET', `${url}/v1/projects`, token));
|
|
130
|
+
const lc = wanted.toLowerCase();
|
|
131
|
+
const match = projects.find((p) => p.projectId === wanted || p.name.toLowerCase() === lc);
|
|
132
|
+
if (match === undefined)
|
|
133
|
+
throw new Error(`no project "${wanted}" — create it with \`reticle project create "${wanted}"\``);
|
|
134
|
+
return match.projectId;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* `reticle login --email <e> [--org <name>] [--code <123456>]` — sign in, cache the token under
|
|
138
|
+
* ~/.reticle.
|
|
139
|
+
*
|
|
140
|
+
* TWO STEPS, because the cloud proves you own the inbox before it hands out a session: ask for a code,
|
|
141
|
+
* then exchange it. (It used to take an email alone — which meant anyone who knew your address owned your
|
|
142
|
+
* org.) `--org` is only consulted when the account is brand new; a returning user never needs it.
|
|
143
|
+
*
|
|
144
|
+
* Without `--code` we request one and stop, telling the user to re-run with it. The one exception is a
|
|
145
|
+
* LOCAL cloud, whose dev mailer cannot actually deliver mail and so echoes the code back in its response
|
|
146
|
+
* (`devCode`) — there we complete the login in a single command rather than asking a developer to read a
|
|
147
|
+
* code out of a server log they may not even be tailing.
|
|
148
|
+
*/
|
|
149
|
+
const RequestCodeSchema = z.object({ devCode: z.string().optional() });
|
|
150
|
+
const DeviceStartSchema = z.object({
|
|
151
|
+
deviceCode: z.string(),
|
|
152
|
+
userCode: z.string(),
|
|
153
|
+
verificationUri: z.string(),
|
|
154
|
+
verificationUriComplete: z.string(),
|
|
155
|
+
interval: z.number(),
|
|
156
|
+
expiresAt: z.number(),
|
|
157
|
+
});
|
|
158
|
+
const DevicePollSchema = z.object({
|
|
159
|
+
status: z.string(),
|
|
160
|
+
token: z.string().optional(),
|
|
161
|
+
org: z.object({ name: z.string() }).optional(),
|
|
162
|
+
});
|
|
163
|
+
/** Best-effort open the approval page in the default browser; the printed URL is the headless fallback. */
|
|
164
|
+
const openBrowser = (target) => {
|
|
165
|
+
const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open';
|
|
166
|
+
const args = process.platform === 'win32' ? ['/c', 'start', '', target] : [target];
|
|
167
|
+
try {
|
|
168
|
+
const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
|
|
169
|
+
child.on('error', () => undefined);
|
|
170
|
+
child.unref();
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
/* no opener available — the user opens the printed URL manually */
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
/** Persist a session token under ~/.reticle and print the next step. Shared by both login paths. */
|
|
177
|
+
const writeSession = async (url, token, orgName) => {
|
|
178
|
+
await mkdir(home(), { recursive: true });
|
|
179
|
+
await writeFile(join(home(), SESSION_FILE), `${JSON.stringify({ url, token, orgName }, null, 2)}\n`);
|
|
180
|
+
emit({ loggedIn: orgName, session: join(home(), SESSION_FILE) });
|
|
181
|
+
hint('next: `reticle link` to bind this repo to your Default project (or `reticle project create <name>` first)');
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Browser device flow — the DEFAULT `reticle login` (like `gh auth login`): fetch a device + user code,
|
|
185
|
+
* open the browser to approve, then poll until the user confirms. No email to type, no code to copy back.
|
|
186
|
+
*/
|
|
187
|
+
const cmdLoginDevice = async () => {
|
|
188
|
+
const url = baseUrl(null);
|
|
189
|
+
const started = DeviceStartSchema.parse(await api('POST', `${url}/v1/auth/device/start`, null, {}));
|
|
190
|
+
hint(`Opening ${started.verificationUri} — confirm this code in the browser: ${started.userCode}`);
|
|
191
|
+
openBrowser(started.verificationUriComplete);
|
|
192
|
+
const intervalMs = Math.max(1, started.interval) * 1000;
|
|
193
|
+
for (;;) {
|
|
194
|
+
await sleep(intervalMs);
|
|
195
|
+
const poll = DevicePollSchema.parse(await api('POST', `${url}/v1/auth/device/token`, null, { deviceCode: started.deviceCode }));
|
|
196
|
+
if (poll.status === 'approved' && poll.token !== undefined && poll.org !== undefined) {
|
|
197
|
+
await writeSession(url, poll.token, poll.org.name);
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
200
|
+
if (poll.status === 'pending') {
|
|
201
|
+
if (Date.now() > started.expiresAt) {
|
|
202
|
+
err('device login expired — run `reticle login` again');
|
|
203
|
+
return 1;
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
err(poll.status === 'denied'
|
|
208
|
+
? 'device login was denied in the browser'
|
|
209
|
+
: 'device login expired — run `reticle login` again');
|
|
210
|
+
return 1;
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* `reticle login` — browser device flow by default; `--email <e>` (or a positional email) keeps the
|
|
215
|
+
* headless two-step code path for CI/servers where opening a browser makes no sense.
|
|
216
|
+
*/
|
|
217
|
+
const cmdLogin = async (argv) => {
|
|
218
|
+
const f = flags(argv);
|
|
219
|
+
const positional = argv[0] !== undefined && !argv[0].startsWith('--') ? argv[0] : undefined;
|
|
220
|
+
const email = f['email'] ?? positional;
|
|
221
|
+
if (email === undefined)
|
|
222
|
+
return cmdLoginDevice();
|
|
223
|
+
const org = f['org'];
|
|
224
|
+
const url = baseUrl(null);
|
|
225
|
+
let code = f['code'];
|
|
226
|
+
if (code === undefined) {
|
|
227
|
+
const requested = RequestCodeSchema.parse(await api('POST', `${url}/v1/auth/request-code`, null, {
|
|
228
|
+
email,
|
|
229
|
+
...(org !== undefined ? { orgName: org } : {}),
|
|
230
|
+
}));
|
|
231
|
+
// A real cloud mails the code and never echoes it; a local one cannot mail, so it hands it back.
|
|
232
|
+
if (requested.devCode === undefined) {
|
|
233
|
+
emit({ codeSent: true, to: email });
|
|
234
|
+
hint(`check your inbox, then: \`reticle login --email ${email} --code <the 6-digit code>\``);
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
237
|
+
code = requested.devCode;
|
|
238
|
+
}
|
|
239
|
+
const parsed = LoginSchema.parse(await api('POST', `${url}/v1/auth/login`, null, { email, code }));
|
|
240
|
+
await writeSession(url, parsed.token, parsed.org.name);
|
|
241
|
+
return 0;
|
|
242
|
+
};
|
|
243
|
+
/** `reticle logout` — forget the cached session token (per-project keys under credentials.json stay). */
|
|
244
|
+
const cmdLogout = async () => {
|
|
245
|
+
await writeFile(join(home(), SESSION_FILE), '').catch(() => undefined);
|
|
246
|
+
emit({ loggedOut: true });
|
|
247
|
+
return 0;
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* `reticle whoami` — the one call an agent (or a confused human) makes to know its state: who am I logged
|
|
251
|
+
* in as, and is THIS repo attached to a cloud project (and with what sync policy / verify mode)?
|
|
252
|
+
*/
|
|
253
|
+
const cmdWhoami = async () => {
|
|
254
|
+
const session = await readSession();
|
|
255
|
+
const fs = createNodeFileSystem();
|
|
256
|
+
const cloud = await resolveProjectCloud(fs, join(process.cwd(), RETICLE_DIR), homedir(), process.env);
|
|
257
|
+
emit({
|
|
258
|
+
loggedInAs: session?.orgName ?? null,
|
|
259
|
+
repo: {
|
|
260
|
+
attached: cloud.config !== null,
|
|
261
|
+
projectId: cloud.projectId,
|
|
262
|
+
url: cloud.config?.url ?? null,
|
|
263
|
+
sync: cloud.policy,
|
|
264
|
+
verify: cloud.verify,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
if (cloud.config === null)
|
|
268
|
+
hint('this repo is not attached — run `reticle link`');
|
|
269
|
+
return 0;
|
|
270
|
+
};
|
|
271
|
+
/** `reticle project ls` / `reticle project create <name>` — key- or session-authed. */
|
|
272
|
+
const cmdProject = async (argv) => {
|
|
273
|
+
const session = await readSession();
|
|
274
|
+
const token = bearer(session);
|
|
275
|
+
const url = baseUrl(session);
|
|
276
|
+
if (token === null) {
|
|
277
|
+
err('run `reticle login` first, or set RETICLE_CLOUD_KEY');
|
|
278
|
+
return 2;
|
|
279
|
+
}
|
|
280
|
+
const sub = argv[0];
|
|
281
|
+
if (sub === 'ls') {
|
|
282
|
+
emit(await api('GET', `${url}/v1/projects`, token));
|
|
283
|
+
return 0;
|
|
284
|
+
}
|
|
285
|
+
if (sub === 'create') {
|
|
286
|
+
const name = argv.slice(1).join(' ').trim();
|
|
287
|
+
if (name.length === 0) {
|
|
288
|
+
err('usage: reticle project create <name>');
|
|
289
|
+
return 2;
|
|
290
|
+
}
|
|
291
|
+
const created = CreatedProjectSchema.parse(await api('POST', `${url}/v1/projects`, token, { name }));
|
|
292
|
+
emit(created);
|
|
293
|
+
hint(`next: \`reticle link --project ${created.projectId}\` to bind this repo`);
|
|
294
|
+
return 0;
|
|
295
|
+
}
|
|
296
|
+
if (sub === 'rename') {
|
|
297
|
+
const id = argv[1];
|
|
298
|
+
const name = argv.slice(2).join(' ').trim();
|
|
299
|
+
if (id === undefined || name.length === 0) {
|
|
300
|
+
err('usage: reticle project rename <projectId> <new name>');
|
|
301
|
+
return 2;
|
|
302
|
+
}
|
|
303
|
+
emit(await api('PATCH', `${url}/v1/projects/${encodeURIComponent(id)}`, token, { name }));
|
|
304
|
+
return 0;
|
|
305
|
+
}
|
|
306
|
+
if (sub === 'rm' || sub === 'delete') {
|
|
307
|
+
const id = argv[1];
|
|
308
|
+
if (id === undefined) {
|
|
309
|
+
err('usage: reticle project rm <projectId>');
|
|
310
|
+
return 2;
|
|
311
|
+
}
|
|
312
|
+
emit(await api('DELETE', `${url}/v1/projects/${encodeURIComponent(id)}`, token));
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
err('usage: reticle project <ls|create <name>|rename <id> <name>|rm <id>>');
|
|
316
|
+
return 2;
|
|
317
|
+
};
|
|
318
|
+
/**
|
|
319
|
+
* `reticle link [--project <id>]` — bind THIS repo to a cloud project. With a login token it MINTS a
|
|
320
|
+
* project-scoped key (no pasting); with a pre-set RETICLE_CLOUD_KEY it resolves the key's project via
|
|
321
|
+
* whoami. Writes the non-secret binding to <repo>/.reticle/cloud.json and the secret key to
|
|
322
|
+
* ~/.reticle/credentials.json (keyed by projectId).
|
|
323
|
+
*/
|
|
324
|
+
const cmdLink = async (argv) => {
|
|
325
|
+
const f = flags(argv);
|
|
326
|
+
const session = await readSession();
|
|
327
|
+
const url = baseUrl(session);
|
|
328
|
+
const envKey = process.env['RETICLE_CLOUD_KEY'];
|
|
329
|
+
let projectId;
|
|
330
|
+
let projectName;
|
|
331
|
+
let key;
|
|
332
|
+
if (envKey !== undefined && envKey.length > 0) {
|
|
333
|
+
const who = WhoamiSchema.parse(await api('GET', `${url}/v1/cloud/whoami`, envKey));
|
|
334
|
+
projectId = who.projectId;
|
|
335
|
+
projectName = who.projectName;
|
|
336
|
+
key = envKey;
|
|
337
|
+
}
|
|
338
|
+
else if (session !== null) {
|
|
339
|
+
// --project accepts a slug id OR a display name; default when omitted. Resolve to the canonical id.
|
|
340
|
+
const wanted = f['project'];
|
|
341
|
+
const targetId = wanted === undefined
|
|
342
|
+
? DEFAULT_PROJECT_ID
|
|
343
|
+
: await resolveProjectId(url, session.token, wanted);
|
|
344
|
+
const minted = KeySchema.parse(await api('POST', `${url}/v1/keys`, session.token, {
|
|
345
|
+
name: 'reticle-cli',
|
|
346
|
+
projectId: targetId,
|
|
347
|
+
}));
|
|
348
|
+
projectId = minted.projectId;
|
|
349
|
+
projectName = minted.projectName;
|
|
350
|
+
key = minted.key;
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
err('run `reticle login` first, or set RETICLE_CLOUD_KEY to link with an existing key');
|
|
354
|
+
return 2;
|
|
355
|
+
}
|
|
356
|
+
const reticleDir = join(process.cwd(), RETICLE_DIR);
|
|
357
|
+
await mkdir(reticleDir, { recursive: true });
|
|
358
|
+
const linkPath = join(reticleDir, CLOUD_LINK_FILE);
|
|
359
|
+
const prev = await readJson(linkPath);
|
|
360
|
+
const prevObj = typeof prev === 'object' && prev !== null ? prev : {};
|
|
361
|
+
const cloudJson = {
|
|
362
|
+
projectId,
|
|
363
|
+
projectName,
|
|
364
|
+
url,
|
|
365
|
+
sync: prevObj['sync'] ?? { runs: true, memory: true, flows: true },
|
|
366
|
+
verify: prevObj['verify'] ?? 'local',
|
|
367
|
+
};
|
|
368
|
+
await writeFile(linkPath, `${JSON.stringify(cloudJson, null, 2)}\n`);
|
|
369
|
+
await mkdir(home(), { recursive: true });
|
|
370
|
+
const credPath = join(home(), CREDENTIALS_FILE);
|
|
371
|
+
const creds = (await readJson(credPath)) ?? {};
|
|
372
|
+
const credObj = typeof creds === 'object' && creds !== null ? creds : {};
|
|
373
|
+
credObj[projectId] = key;
|
|
374
|
+
await writeFile(credPath, `${JSON.stringify(credObj, null, 2)}\n`);
|
|
375
|
+
emit({ linked: projectName, projectId, cloudJson: linkPath, credentials: credPath });
|
|
376
|
+
hint('linked ✓ runs auto-push on `reticle verify`; `reticle push` sends existing local runs; `reticle whoami` shows state');
|
|
377
|
+
return 0;
|
|
378
|
+
};
|
|
379
|
+
/** `reticle config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server]`. */
|
|
380
|
+
const cmdConfig = async (argv) => {
|
|
381
|
+
const f = flags(argv);
|
|
382
|
+
const linkPath = join(process.cwd(), RETICLE_DIR, CLOUD_LINK_FILE);
|
|
383
|
+
const raw = await readJson(linkPath);
|
|
384
|
+
if (raw === null || typeof raw !== 'object') {
|
|
385
|
+
err('no .reticle/cloud.json here — run `reticle link` first');
|
|
386
|
+
return 2;
|
|
387
|
+
}
|
|
388
|
+
const cfg = raw;
|
|
389
|
+
const sync = typeof cfg['sync'] === 'object' && cfg['sync'] !== null
|
|
390
|
+
? cfg['sync']
|
|
391
|
+
: { runs: true, memory: true, flows: true };
|
|
392
|
+
const onoff = (v) => v === 'on' ? true : v === 'off' ? false : undefined;
|
|
393
|
+
for (const k of ['runs', 'memory', 'flows']) {
|
|
394
|
+
if (f[k] === undefined)
|
|
395
|
+
continue;
|
|
396
|
+
const b = onoff(f[k]);
|
|
397
|
+
if (b === undefined) {
|
|
398
|
+
err(`--${k} must be on|off`);
|
|
399
|
+
return 2;
|
|
400
|
+
}
|
|
401
|
+
sync[k] = b;
|
|
402
|
+
}
|
|
403
|
+
cfg['sync'] = sync;
|
|
404
|
+
if (f['verify'] !== undefined) {
|
|
405
|
+
if (f['verify'] !== 'local' && f['verify'] !== 'server') {
|
|
406
|
+
err('--verify must be local|server');
|
|
407
|
+
return 2;
|
|
408
|
+
}
|
|
409
|
+
cfg['verify'] = f['verify'];
|
|
410
|
+
}
|
|
411
|
+
await writeFile(linkPath, `${JSON.stringify(cfg, null, 2)}\n`);
|
|
412
|
+
emit({ updated: linkPath, sync: cfg['sync'], verify: cfg['verify'] });
|
|
413
|
+
return 0;
|
|
414
|
+
};
|
|
415
|
+
/** `reticle push` — best-effort push of local run artifacts to the linked project (honors sync policy). */
|
|
416
|
+
const cmdPush = async () => {
|
|
417
|
+
const fs = createNodeFileSystem();
|
|
418
|
+
const reticleRoot = join(process.cwd(), RETICLE_DIR);
|
|
419
|
+
const cloud = await resolveProjectCloud(fs, reticleRoot, homedir(), process.env);
|
|
420
|
+
if (cloud.config === null) {
|
|
421
|
+
err('cloud not attached here — run `reticle link` (or set RETICLE_CLOUD_URL/KEY)');
|
|
422
|
+
return 1;
|
|
423
|
+
}
|
|
424
|
+
if (!cloud.policy.runs) {
|
|
425
|
+
emit({ pushed: 0, skipped: 'sync.runs is off for this project (reticle config --runs on)' });
|
|
426
|
+
return 0;
|
|
427
|
+
}
|
|
428
|
+
const store = new RunStore(fs, reticleRoot);
|
|
429
|
+
const ids = await store.list();
|
|
430
|
+
let pushed = 0;
|
|
431
|
+
let failed = 0;
|
|
432
|
+
for (const id of ids) {
|
|
433
|
+
const read = await store.read(id);
|
|
434
|
+
if (!read.ok)
|
|
435
|
+
continue;
|
|
436
|
+
const res = await syncRunToCloud(read.run, cloud.config, (u, init) => fetch(u, init));
|
|
437
|
+
if (res.outcome === SyncOutcome.SYNCED)
|
|
438
|
+
pushed += 1;
|
|
439
|
+
else if (res.outcome === SyncOutcome.FAILED)
|
|
440
|
+
failed += 1;
|
|
441
|
+
}
|
|
442
|
+
emit({ pushed, failed, total: ids.length, project: cloud.projectId });
|
|
443
|
+
if (pushed > 0)
|
|
444
|
+
hint(`pushed ✓ see them in the dashboard Runs tab (${cloud.config.url})`);
|
|
445
|
+
return 0;
|
|
446
|
+
};
|
|
447
|
+
/** Resolve THIS repo's linked cloud (url + project-scoped key). Throws a friendly error if not attached. */
|
|
448
|
+
const repoCloud = async () => {
|
|
449
|
+
const fs = createNodeFileSystem();
|
|
450
|
+
const cloud = await resolveProjectCloud(fs, join(process.cwd(), RETICLE_DIR), homedir(), process.env);
|
|
451
|
+
if (cloud.config === null)
|
|
452
|
+
throw new Error('cloud not attached here — run `reticle link` (or set RETICLE_CLOUD_URL/KEY)');
|
|
453
|
+
return cloud.config;
|
|
454
|
+
};
|
|
455
|
+
/** `reticle runs` — the linked project's recent run artifacts (the key scopes it server-side). */
|
|
456
|
+
const cmdRuns = async () => {
|
|
457
|
+
const { url, apiKey } = await repoCloud();
|
|
458
|
+
emit(await api('GET', `${url}/v1/runs`, apiKey));
|
|
459
|
+
return 0;
|
|
460
|
+
};
|
|
461
|
+
/** `reticle regression` — the CI gate: broken flows vs before. Exit 3 if any regressed (pipeline-friendly). */
|
|
462
|
+
const cmdRegression = async () => {
|
|
463
|
+
const { url, apiKey } = await repoCloud();
|
|
464
|
+
const report = await api('GET', `${url}/v1/project/regression`, apiKey);
|
|
465
|
+
emit(report);
|
|
466
|
+
const parsed = z.object({ broken: z.array(z.unknown()) }).safeParse(report);
|
|
467
|
+
return parsed.success && parsed.data.broken.length > 0 ? 3 : 0;
|
|
468
|
+
};
|
|
469
|
+
/** `reticle share <runId>` — mint a public proof link for one run. */
|
|
470
|
+
const cmdShare = async (argv) => {
|
|
471
|
+
const runId = argv[0];
|
|
472
|
+
if (runId === undefined) {
|
|
473
|
+
err('usage: reticle share <runId>');
|
|
474
|
+
return 2;
|
|
475
|
+
}
|
|
476
|
+
const { url, apiKey } = await repoCloud();
|
|
477
|
+
emit(await api('POST', `${url}/v1/runs/${encodeURIComponent(runId)}/share`, apiKey));
|
|
478
|
+
return 0;
|
|
479
|
+
};
|
|
480
|
+
/** Dispatch a cloud subcommand. Returns the process exit code. */
|
|
481
|
+
export const runCloudCommand = async (argv) => {
|
|
482
|
+
const [cmd, ...rest] = argv;
|
|
483
|
+
try {
|
|
484
|
+
switch (cmd) {
|
|
485
|
+
case 'login':
|
|
486
|
+
return await cmdLogin(rest);
|
|
487
|
+
case 'logout':
|
|
488
|
+
return await cmdLogout();
|
|
489
|
+
case 'whoami':
|
|
490
|
+
return await cmdWhoami();
|
|
491
|
+
case 'project':
|
|
492
|
+
return await cmdProject(rest);
|
|
493
|
+
case 'link':
|
|
494
|
+
return await cmdLink(rest);
|
|
495
|
+
case 'config':
|
|
496
|
+
return await cmdConfig(rest);
|
|
497
|
+
case 'push':
|
|
498
|
+
return await cmdPush();
|
|
499
|
+
case 'runs':
|
|
500
|
+
return await cmdRuns();
|
|
501
|
+
case 'regression':
|
|
502
|
+
return await cmdRegression();
|
|
503
|
+
case 'share':
|
|
504
|
+
return await cmdShare(rest);
|
|
505
|
+
default:
|
|
506
|
+
err(`unknown cloud command '${cmd ?? ''}'`);
|
|
507
|
+
return 2;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
catch (e) {
|
|
511
|
+
err(e instanceof Error ? e.message : String(e));
|
|
512
|
+
return 1;
|
|
513
|
+
}
|
|
514
|
+
};
|
|
@@ -11,7 +11,7 @@ const nodeSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
11
11
|
export const CRAWL_TOOLS = [
|
|
12
12
|
{
|
|
13
13
|
name: ReticleTool.CRAWL,
|
|
14
|
-
description: 'Autonomously click every reachable interactive control (bounded by maxSteps, default 25) and report anomalies WITHOUT a script: console errors, failed requests
|
|
14
|
+
description: 'Autonomously click every reachable interactive control (bounded by maxSteps, default 25) and report anomalies WITHOUT a script. Two classes: single-channel faults (console errors, failed requests ≥400, DEAD controls that dispatched but did nothing) and CONTRADICTIONS — two channels disagreeing about the same click, e.g. the UI advanced while its write failed, a success signal fired over a failed request, a write succeeded and nothing changed, the same write fired twice, or the UI moved on over an in-flight request. Contradictions are the false greens a human cannot see, because a human watches one channel (the screen) and it looks correct. DESTRUCTIVE — it really clicks (may navigate/mutate state); use reticle_explore first for a non-destructive list. Returns { interactiveFound, stepsRun, anomalies[{kind,ref,desc,detail}], counts, visited, truncated }.',
|
|
15
15
|
inputSchema: {
|
|
16
16
|
maxSteps: z.number().optional().describe('Maximum number of controls to click. Default: 25.'),
|
|
17
17
|
settleMs: z
|
|
@@ -70,4 +70,3 @@ export const CRAWL_TOOLS = [
|
|
|
70
70
|
},
|
|
71
71
|
},
|
|
72
72
|
];
|
|
73
|
-
//# sourceMappingURL=crawl-tools.js.map
|
package/dist/crawl/crawl.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CrawlAnomalyKind, type CommandResult, type ReticleEvent } from '@reticlehq/core';
|
|
1
|
+
import { CrawlAnomalyKind, ContradictionKind, type CommandResult, type ReticleEvent } from '@reticlehq/core';
|
|
2
2
|
/** The slice of Session the crawler needs — so tests inject a fake without a live browser. */
|
|
3
3
|
export interface CrawlSession {
|
|
4
4
|
command(name: string, args?: Record<string, unknown>): Promise<CommandResult>;
|
|
@@ -16,7 +16,13 @@ export interface CrawlSession {
|
|
|
16
16
|
}
|
|
17
17
|
type CrawlSleep = (ms: number) => Promise<void>;
|
|
18
18
|
export interface CrawlAnomaly {
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* A single-channel fault (something bad in one stream) or a CONTRADICTION (two channels
|
|
21
|
+
* disagreeing about the same click). The second kind is why an autonomous crawl is worth more
|
|
22
|
+
* than a human clicking the same buttons: a person sees only the screen, so a UI that advanced
|
|
23
|
+
* while its write failed looks like success to them and always will.
|
|
24
|
+
*/
|
|
25
|
+
kind: CrawlAnomalyKind | ContradictionKind;
|
|
20
26
|
/** The control that triggered it. */
|
|
21
27
|
ref: string;
|
|
22
28
|
desc: string;
|
|
@@ -41,6 +47,7 @@ export interface CrawlReport {
|
|
|
41
47
|
consoleErrors: number;
|
|
42
48
|
failedRequests: number;
|
|
43
49
|
deadControls: number;
|
|
50
|
+
contradictions: number;
|
|
44
51
|
};
|
|
45
52
|
/** Descriptions of the controls actually clicked. */
|
|
46
53
|
visited: string[];
|
|
@@ -62,6 +69,13 @@ export interface CrawlOptions {
|
|
|
62
69
|
* orchestration over a CrawlSession: no browser/Node imports, fully unit-testable with a fake.
|
|
63
70
|
* Single-pass by design (no re-discovery) so it always terminates and never explodes on navigation.
|
|
64
71
|
*/
|
|
72
|
+
/**
|
|
73
|
+
* Controls whose correct behaviour IS to do nothing when clicked.
|
|
74
|
+
*
|
|
75
|
+
* Read off the same descriptor the crawler already has, so no extra round trip: `[disabled]` is inert
|
|
76
|
+
* by definition, an already `[checked]` radio/checkbox is idempotent, and clicking a text field
|
|
77
|
+
* focuses it rather than changing anything.
|
|
78
|
+
*/
|
|
79
|
+
export declare function legitimatelyInert(desc: string): boolean;
|
|
65
80
|
export declare function crawl(session: CrawlSession, opts: CrawlOptions, sleep: CrawlSleep): Promise<CrawlReport>;
|
|
66
81
|
export {};
|
|
67
|
-
//# sourceMappingURL=crawl.d.ts.map
|
package/dist/crawl/crawl.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { ActionType, CRAWL_DEFAULTS, CrawlAnomalyKind, DANGEROUS_ACTION_CONFIRM_ARG, EventType, ReticleCommand, } from '@reticlehq/core';
|
|
1
|
+
import { ActionType, CRAWL_DEFAULTS, CrawlAnomalyKind, ContradictionKind, DANGEROUS_ACTION_CONFIRM_ARG, EventType, ReticleCommand, } from '@reticlehq/core';
|
|
2
2
|
import { parseInteractive, asRecord, asNumber, asString, sourceOf, } from '../tools/tools-helpers.js';
|
|
3
3
|
import { ReticleTool } from '../tools/tool-names.js';
|
|
4
|
+
import { findContradictions } from '../events/contradictions.js';
|
|
4
5
|
/** Said out loud when the page was larger than one snapshot, so a clean sweep cannot mean "all clear". */
|
|
5
6
|
export const CAPPED_SNAPSHOT_NOTE = 'the page exceeded one snapshot, so controls past the cap were never seen — interactiveFound is a floor, not a total; re-run with a narrower `scope` to cover the rest';
|
|
6
7
|
/** Any buffer event that proves the app reacted to a click (vs a dead/no-op control). */
|
|
@@ -33,6 +34,20 @@ function failedRequests(events, floor) {
|
|
|
33
34
|
* orchestration over a CrawlSession: no browser/Node imports, fully unit-testable with a fake.
|
|
34
35
|
* Single-pass by design (no re-discovery) so it always terminates and never explodes on navigation.
|
|
35
36
|
*/
|
|
37
|
+
/**
|
|
38
|
+
* Controls whose correct behaviour IS to do nothing when clicked.
|
|
39
|
+
*
|
|
40
|
+
* Read off the same descriptor the crawler already has, so no extra round trip: `[disabled]` is inert
|
|
41
|
+
* by definition, an already `[checked]` radio/checkbox is idempotent, and clicking a text field
|
|
42
|
+
* focuses it rather than changing anything.
|
|
43
|
+
*/
|
|
44
|
+
export function legitimatelyInert(desc) {
|
|
45
|
+
if (desc.includes('[disabled]'))
|
|
46
|
+
return true;
|
|
47
|
+
if (desc.includes('[checked]'))
|
|
48
|
+
return true;
|
|
49
|
+
return /\b(textbox|searchbox|combobox|spinbutton)\b/.test(desc);
|
|
50
|
+
}
|
|
36
51
|
export async function crawl(session, opts, sleep) {
|
|
37
52
|
const maxSteps = opts.maxSteps ?? CRAWL_DEFAULTS.MAX_STEPS;
|
|
38
53
|
const settleMs = opts.settleMs ?? CRAWL_DEFAULTS.SETTLE_MS;
|
|
@@ -50,7 +65,7 @@ export async function crawl(session, opts, sleep) {
|
|
|
50
65
|
const coverageCapped = snapshot.truncated === true;
|
|
51
66
|
const anomalies = [];
|
|
52
67
|
const visited = [];
|
|
53
|
-
const counts = { consoleErrors: 0, failedRequests: 0, deadControls: 0 };
|
|
68
|
+
const counts = { consoleErrors: 0, failedRequests: 0, deadControls: 0, contradictions: 0 };
|
|
54
69
|
const seen = new Set();
|
|
55
70
|
let stepsRun = 0;
|
|
56
71
|
for (const item of items) {
|
|
@@ -107,9 +122,34 @@ export async function crawl(session, opts, sleep) {
|
|
|
107
122
|
...source,
|
|
108
123
|
});
|
|
109
124
|
}
|
|
125
|
+
// CONTRADICTIONS: the channels disagree about what this click did. Reported per control, so a
|
|
126
|
+
// crawl of an unknown app surfaces its false greens without anyone knowing where to look.
|
|
127
|
+
for (const c of findContradictions(events)) {
|
|
128
|
+
counts.contradictions += 1;
|
|
129
|
+
anomalies.push({
|
|
130
|
+
kind: c.kind,
|
|
131
|
+
ref: item.ref,
|
|
132
|
+
desc: item.desc,
|
|
133
|
+
detail: `${c.claim}, but ${c.counter} — ${c.detail}`,
|
|
134
|
+
...source,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
110
137
|
// DEAD: the click dispatched but the app produced no activity and no error to explain it.
|
|
138
|
+
//
|
|
139
|
+
// Unless the control is SUPPOSED to be inert. Measured on a shipments console: every one of the
|
|
140
|
+
// crawler's three anomalies was a control behaving correctly — a `[disabled]` button, an already
|
|
141
|
+
// `[checked]` radio, and a text input (a click focuses it; mutating would be the surprise). An
|
|
142
|
+
// anomaly list that is 100% false on a real app is worse than no anomaly list, because the agent
|
|
143
|
+
// spends its budget disproving it and learns to skip the field.
|
|
144
|
+
//
|
|
145
|
+
// Deliberately NOT fixed by counting focus as activity: focus moving is not the app reacting, and
|
|
146
|
+
// treating it as such would make a genuinely dead button that takes focus look alive — trading
|
|
147
|
+
// noise for the false negative this check exists to catch.
|
|
111
148
|
const dispatched = asRecord(act.result)['dispatched'] !== false && act.ok;
|
|
112
|
-
if (dispatched &&
|
|
149
|
+
if (dispatched &&
|
|
150
|
+
errs.length === 0 &&
|
|
151
|
+
!events.some(isActivity) &&
|
|
152
|
+
!legitimatelyInert(item.desc)) {
|
|
113
153
|
counts.deadControls += 1;
|
|
114
154
|
anomalies.push({
|
|
115
155
|
kind: CrawlAnomalyKind.DEAD_CONTROL,
|
|
@@ -133,4 +173,3 @@ export async function crawl(session, opts, sleep) {
|
|
|
133
173
|
...(coverageCapped ? { coverageNote: CAPPED_SNAPSHOT_NOTE } : {}),
|
|
134
174
|
};
|
|
135
175
|
}
|
|
136
|
-
//# sourceMappingURL=crawl.js.map
|
|
@@ -12,6 +12,11 @@ export interface ProcessLike {
|
|
|
12
12
|
on(event: string, listener: (arg: unknown) => void): unknown;
|
|
13
13
|
}
|
|
14
14
|
type LogFn = (event: string, data: Record<string, unknown>) => void;
|
|
15
|
+
/** The two ways a failure reaches the top of the process. Named so the analytics can tell them apart. */
|
|
16
|
+
export declare const CrashKind: {
|
|
17
|
+
readonly UNHANDLED_REJECTION: "unhandled_rejection";
|
|
18
|
+
readonly UNCAUGHT_EXCEPTION: "uncaught_exception";
|
|
19
|
+
};
|
|
20
|
+
export type CrashKind = (typeof CrashKind)[keyof typeof CrashKind];
|
|
15
21
|
export declare function installDaemonResilience(proc: ProcessLike, log: LogFn, onFatal: () => void): void;
|
|
16
22
|
export {};
|
|
17
|
-
//# sourceMappingURL=daemon-resilience.d.ts.map
|