@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,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classifying WHY a browser connection failed, into causes that each have a different fix.
|
|
3
|
+
*
|
|
4
|
+
* A raw error message cannot be sent — it carries CDP URLs, executable paths, and home directories —
|
|
5
|
+
* and would be useless if it could, because the same cause arrives worded five different ways from
|
|
6
|
+
* Playwright, Node and the OS. What we need is the small set of THINGS THAT ACTUALLY GO WRONG:
|
|
7
|
+
* a missing browser binary is a docs problem, a refused CDP endpoint is a configuration problem, a
|
|
8
|
+
* pool timeout is a capacity problem. Three very different responses from us.
|
|
9
|
+
*
|
|
10
|
+
* `OTHER` is the bucket to watch. A large `OTHER` count means the classifier has a blind spot and
|
|
11
|
+
* this list needs another entry — which is a signal in itself, and the reason it is never merged
|
|
12
|
+
* into one of the known causes.
|
|
13
|
+
*/
|
|
14
|
+
import { ConnectFailure } from '@reticlehq/core';
|
|
15
|
+
/** Ordered most-specific first; the first match wins. Patterns track messages we have actually seen. */
|
|
16
|
+
const RULES = [
|
|
17
|
+
// Playwright's own wording when the browser was never downloaded.
|
|
18
|
+
{
|
|
19
|
+
match: /executable doesn.?t exist|playwright install|browsertype\.launch/i,
|
|
20
|
+
cause: ConnectFailure.CHROMIUM_MISSING,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
match: /cannot find module ['"]?playwright|playwright is not installed/i,
|
|
24
|
+
cause: ConnectFailure.PLAYWRIGHT_MISSING,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
match: /econnrefused|connect over cdp|websocket error|failed to connect|enotfound|etimedout/i,
|
|
28
|
+
cause: ConnectFailure.CDP_UNREACHABLE,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
match: /target (?:page|closed)|browser has been closed|browser has disconnected|crashed/i,
|
|
32
|
+
cause: ConnectFailure.BROWSER_CRASHED,
|
|
33
|
+
},
|
|
34
|
+
{ match: /pool|lease.*timed out|timed out waiting/i, cause: ConnectFailure.POOL_TIMEOUT },
|
|
35
|
+
];
|
|
36
|
+
/** The cause behind a connection error, or `OTHER` when we do not recognize it. */
|
|
37
|
+
export function classifyConnectFailure(error) {
|
|
38
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
39
|
+
for (const rule of RULES) {
|
|
40
|
+
if (rule.match.test(message))
|
|
41
|
+
return rule.cause;
|
|
42
|
+
}
|
|
43
|
+
return ConnectFailure.OTHER;
|
|
44
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Stops the timers this installed. Called from the daemon's shutdown path. */
|
|
2
|
+
export interface DaemonTelemetry {
|
|
3
|
+
/**
|
|
4
|
+
* Emit the final, rich session summary and stop flushing. Safe to call more than once.
|
|
5
|
+
*
|
|
6
|
+
* AWAITABLE, and the daemon must await it. Every other send in the product is fire-and-forget
|
|
7
|
+
* because losing one counter never matters — but this one carries the whole session, and the
|
|
8
|
+
* process calls `process.exit(0)` immediately afterwards, which kills an in-flight POST. Verified
|
|
9
|
+
* against a local capture server: fire-and-forget lost the event every single time. A send that
|
|
10
|
+
* fails or hangs still cannot block the exit, because `emit` swallows its own errors and is bounded
|
|
11
|
+
* by its own request timeout.
|
|
12
|
+
*/
|
|
13
|
+
shutdown(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Report the daemon as started, profile the project once, and keep the session counters flushing.
|
|
17
|
+
* Every timer is `unref`'d so telemetry can never be the reason a daemon refuses to exit.
|
|
18
|
+
*/
|
|
19
|
+
export declare function installDaemonTelemetry(cwd: string, now?: () => number): DaemonTelemetry;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The daemon's telemetry lifecycle, in one place: start, the periodic flush that survives a kill, the
|
|
3
|
+
* one-shot project profile, and the rich summary on shutdown.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from `cli.ts` because it is a cohesive unit with its own timers and its own failure rule
|
|
6
|
+
* (nothing here may ever be able to fail a daemon start), and because `cli.ts` is a dispatcher — the
|
|
7
|
+
* more of this that lives there, the harder it is to see what a command actually does.
|
|
8
|
+
*/
|
|
9
|
+
import { TelemetryEventKind } from '@reticlehq/core';
|
|
10
|
+
import { getTelemetry } from './telemetry.js';
|
|
11
|
+
import { getSessionMetrics } from './session-metrics.js';
|
|
12
|
+
import { profileProject } from './project-profile.js';
|
|
13
|
+
import { startUpdateCheck } from '../update/update-nudge.js';
|
|
14
|
+
import { markDaemonStart } from './mcp-connection.js';
|
|
15
|
+
/** Let the daemon finish coming up before walking the source tree for a profile. */
|
|
16
|
+
const PROJECT_PROFILE_DELAY_MS = 5_000;
|
|
17
|
+
/**
|
|
18
|
+
* How often a long-lived daemon flushes its counters.
|
|
19
|
+
*
|
|
20
|
+
* ponytail: coarse on purpose. A daemon that is SIGKILLed — OOM, a closed laptop, `kill -9` — never
|
|
21
|
+
* reaches its shutdown handler, so without this the whole session's counters die with it. Thirty
|
|
22
|
+
* minutes bounds that loss to one window while keeping the event volume near zero for the common case
|
|
23
|
+
* (most daemons stop cleanly and send exactly one summary). If the lost windows ever show up as a
|
|
24
|
+
* real gap, the upgrade is a small on-disk journal replayed at the next start, not a shorter timer.
|
|
25
|
+
*/
|
|
26
|
+
const SESSION_FLUSH_MS = 30 * 60 * 1000;
|
|
27
|
+
/**
|
|
28
|
+
* Report the daemon as started, profile the project once, and keep the session counters flushing.
|
|
29
|
+
* Every timer is `unref`'d so telemetry can never be the reason a daemon refuses to exit.
|
|
30
|
+
*/
|
|
31
|
+
export function installDaemonTelemetry(cwd, now = () => Date.now()) {
|
|
32
|
+
const metrics = getSessionMetrics(now);
|
|
33
|
+
void getTelemetry().emit(TelemetryEventKind.DAEMON_STARTED);
|
|
34
|
+
// Ask npm whether a newer Reticle exists, so the agent can tell the human. Nothing checked before
|
|
35
|
+
// this, so a published fix was invisible until someone manually ran `reticle update`.
|
|
36
|
+
startUpdateCheck(now);
|
|
37
|
+
markDaemonStart(now());
|
|
38
|
+
// One profile per daemon start: what kind of project this is and how deeply Reticle is used in it.
|
|
39
|
+
// Deferred off the boot path — it walks the source tree, and nothing about a daemon coming up
|
|
40
|
+
// should wait on a filesystem scan.
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
try {
|
|
43
|
+
void getTelemetry().emit(TelemetryEventKind.PROJECT_PROFILED, {
|
|
44
|
+
project: profileProject(cwd, now()),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
/* a profile is a nice-to-have; it must never touch the daemon */
|
|
49
|
+
}
|
|
50
|
+
}, PROJECT_PROFILE_DELAY_MS).unref();
|
|
51
|
+
const flush = setInterval(() => {
|
|
52
|
+
if (metrics.empty)
|
|
53
|
+
return; // an idle window is not worth an event
|
|
54
|
+
void getTelemetry().emit(TelemetryEventKind.DAEMON_STOPPED, {
|
|
55
|
+
session: metrics.summarize(false),
|
|
56
|
+
});
|
|
57
|
+
metrics.reset();
|
|
58
|
+
}, SESSION_FLUSH_MS);
|
|
59
|
+
flush.unref();
|
|
60
|
+
let stopped;
|
|
61
|
+
return {
|
|
62
|
+
shutdown() {
|
|
63
|
+
// Both SIGTERM and the idle-shutdown path can call this; the second caller awaits the first
|
|
64
|
+
// send rather than emitting a duplicate summary.
|
|
65
|
+
stopped ??= (async () => {
|
|
66
|
+
clearInterval(flush);
|
|
67
|
+
// The rich one: the whole session in a single event — duration, the tool histogram, error
|
|
68
|
+
// shapes, verifications, browser launches. This is what replaced the per-tool-call event.
|
|
69
|
+
await getTelemetry().emit(TelemetryEventKind.DAEMON_STOPPED, {
|
|
70
|
+
session: metrics.summarize(true),
|
|
71
|
+
});
|
|
72
|
+
})();
|
|
73
|
+
return stopped;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Walk up from `cwd` to the filesystem root, looking for a package.json that names this monorepo. */
|
|
2
|
+
export declare function isReticleSourceCheckout(cwd: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Load `<dir>/.env` into `env`. Best-effort and deliberately minimal — no expansion, no multiline.
|
|
5
|
+
*
|
|
6
|
+
* A variable already present in the environment ALWAYS wins. A .env that overrode an explicit
|
|
7
|
+
* `RETICLE_PORT` from the caller's shell would bind the daemon somewhere the caller never asked for,
|
|
8
|
+
* which is a worse surprise than the convenience is worth.
|
|
9
|
+
*/
|
|
10
|
+
export declare function loadDotEnv(dir: string, env?: NodeJS.ProcessEnv): void;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, parse } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Two guards that keep Reticle's own development off its telemetry.
|
|
5
|
+
*
|
|
6
|
+
* `.env` is the convenient one and is useful to every user: put `RETICLE_TELEMETRY=0` in a project's
|
|
7
|
+
* `.env` and the daemon honours it. But `.env` is gitignored, so it exists only on the machine that
|
|
8
|
+
* created it — a fresh clone of this repo would phone home on the first `reticle serve` a
|
|
9
|
+
* contributor runs. `isReticleSourceCheckout` closes that: the marker is the committed root
|
|
10
|
+
* package.json, so the guarantee travels with the repository instead of with one developer's
|
|
11
|
+
* untracked file.
|
|
12
|
+
*/
|
|
13
|
+
/** The monorepo's root package name — the one committed marker that says "this IS Reticle". */
|
|
14
|
+
const MONOREPO_NAME = 'reticle-monorepo';
|
|
15
|
+
/** Walk up from `cwd` to the filesystem root, looking for a package.json that names this monorepo. */
|
|
16
|
+
export function isReticleSourceCheckout(cwd) {
|
|
17
|
+
let dir = cwd;
|
|
18
|
+
const { root } = parse(cwd);
|
|
19
|
+
for (;;) {
|
|
20
|
+
const pkg = join(dir, 'package.json');
|
|
21
|
+
if (existsSync(pkg)) {
|
|
22
|
+
try {
|
|
23
|
+
const parsed = JSON.parse(readFileSync(pkg, 'utf8'));
|
|
24
|
+
const name = typeof parsed === 'object' && parsed !== null
|
|
25
|
+
? parsed.name
|
|
26
|
+
: undefined;
|
|
27
|
+
if (name === MONOREPO_NAME)
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
/* an unreadable/!JSON package.json simply is not the marker */
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (dir === root)
|
|
35
|
+
return false;
|
|
36
|
+
const next = dirname(dir);
|
|
37
|
+
if (next === dir)
|
|
38
|
+
return false;
|
|
39
|
+
dir = next;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Strip one layer of matching quotes from a .env value. */
|
|
43
|
+
function unquote(value) {
|
|
44
|
+
const first = value[0];
|
|
45
|
+
if ((first === '"' || first === "'") && value.endsWith(first) && value.length >= 2) {
|
|
46
|
+
return value.slice(1, -1);
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Load `<dir>/.env` into `env`. Best-effort and deliberately minimal — no expansion, no multiline.
|
|
52
|
+
*
|
|
53
|
+
* A variable already present in the environment ALWAYS wins. A .env that overrode an explicit
|
|
54
|
+
* `RETICLE_PORT` from the caller's shell would bind the daemon somewhere the caller never asked for,
|
|
55
|
+
* which is a worse surprise than the convenience is worth.
|
|
56
|
+
*/
|
|
57
|
+
export function loadDotEnv(dir, env = process.env) {
|
|
58
|
+
const file = join(dir, '.env');
|
|
59
|
+
let text;
|
|
60
|
+
try {
|
|
61
|
+
if (!existsSync(file))
|
|
62
|
+
return;
|
|
63
|
+
text = readFileSync(file, 'utf8');
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
for (const raw of text.split('\n')) {
|
|
69
|
+
const line = raw.trim();
|
|
70
|
+
if (line.length === 0 || line.startsWith('#'))
|
|
71
|
+
continue;
|
|
72
|
+
const eq = line.indexOf('=');
|
|
73
|
+
if (eq <= 0)
|
|
74
|
+
continue; // not a KEY=value pair — skip rather than fail the whole file
|
|
75
|
+
const key = line.slice(0, eq).trim();
|
|
76
|
+
if (key.length === 0 || env[key] !== undefined)
|
|
77
|
+
continue;
|
|
78
|
+
env[key] = unquote(line.slice(eq + 1).trim());
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** The skeleton of a message: variable parts blanked, whitespace collapsed, length bounded. */
|
|
2
|
+
export declare function errorSkeleton(message: string): string;
|
|
3
|
+
/** A stable, opaque group key for an error message. Same defect ⇒ same key, on every machine. */
|
|
4
|
+
export declare function fingerprintError(message: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Frames from a stack trace that belong to RETICLE, reduced to `basename:line`.
|
|
7
|
+
*
|
|
8
|
+
* A crash stack is mostly the user's own application and node internals; neither is ours to collect,
|
|
9
|
+
* and the user's frames are the ones that would carry their directory layout and module names. Only
|
|
10
|
+
* frames inside a `@reticlehq/*` package or a `reticle` dist path survive, and even those keep just
|
|
11
|
+
* the file basename — enough to point a maintainer at the right file, never enough to describe
|
|
12
|
+
* anyone's machine.
|
|
13
|
+
*/
|
|
14
|
+
export declare function reticleFrames(stack: string): string[];
|
|
15
|
+
/** How many of our own frames to carry. The innermost few are where the defect is. */
|
|
16
|
+
export declare const MAX_REPORTED_FRAMES = 12;
|
|
17
|
+
/**
|
|
18
|
+
* A crash fingerprint from the error's type and its Reticle-owned frames. Falls back to the message
|
|
19
|
+
* skeleton when a stack has no Reticle frames at all (a crash from deep inside a dependency), so a
|
|
20
|
+
* crash is never dropped for lack of a clean attribution.
|
|
21
|
+
*/
|
|
22
|
+
export declare function fingerprintCrash(errorType: string, stack: string, message: string): string;
|
|
23
|
+
/** The error's constructor name (`TypeError`), or `Unknown` for a thrown non-Error. */
|
|
24
|
+
export declare function errorTypeOf(error: unknown): string;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning errors into something we can COUNT without carrying anything anyone wrote.
|
|
3
|
+
*
|
|
4
|
+
* An error message is the most tempting thing to send and the most dangerous: it is where a selector,
|
|
5
|
+
* a URL, a customer id, a file path, or a snippet of someone's app ends up. But "which error is
|
|
6
|
+
* hurting people most" is genuinely the highest-value product question we can ask, and it needs
|
|
7
|
+
* grouping — a thousand machines hitting the same defect have to collapse into one row before anyone
|
|
8
|
+
* can rank it.
|
|
9
|
+
*
|
|
10
|
+
* A fingerprint gives us the grouping without the content: strip every variable part, keep the
|
|
11
|
+
* skeleton, hash it. `no baseline named 'checkout-v3'` and `no baseline named 'login'` both become
|
|
12
|
+
* the same fingerprint, and neither flow name leaves the machine. The output is a short hex string,
|
|
13
|
+
* so even the skeleton is not readable on our side — it is an opaque group key, and its only power is
|
|
14
|
+
* that identical failures share it.
|
|
15
|
+
*/
|
|
16
|
+
import { createHash } from 'node:crypto';
|
|
17
|
+
/**
|
|
18
|
+
* The variable parts, replaced before hashing. Order matters: quoted strings go first, because a
|
|
19
|
+
* quoted value often contains the very numbers and paths the later rules would only partly catch.
|
|
20
|
+
*/
|
|
21
|
+
const VARIABLE_PARTS = [
|
|
22
|
+
/"[^"]*"|'[^']*'|`[^`]*`/g, // quoted values — names, selectors, snippets
|
|
23
|
+
/\b[a-z][a-z0-9+.-]*:\/\/\S+/gi, // URLs
|
|
24
|
+
/(?:\/[\w.-]+){2,}\/?/g, // POSIX-ish paths
|
|
25
|
+
/[A-Za-z]:\\[^\s]*/g, // Windows paths
|
|
26
|
+
/\b[0-9a-f]{8,}\b/gi, // hex ids, hashes, uuids-without-dashes
|
|
27
|
+
/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, // uuids
|
|
28
|
+
/\d+/g, // any remaining number — ports, counts, timeouts
|
|
29
|
+
];
|
|
30
|
+
/** The skeleton of a message: variable parts blanked, whitespace collapsed, length bounded. */
|
|
31
|
+
export function errorSkeleton(message) {
|
|
32
|
+
let skeleton = message;
|
|
33
|
+
for (const pattern of VARIABLE_PARTS)
|
|
34
|
+
skeleton = skeleton.replace(pattern, '*');
|
|
35
|
+
return skeleton.replace(/\s+/g, ' ').trim().slice(0, 200);
|
|
36
|
+
}
|
|
37
|
+
/** A stable, opaque group key for an error message. Same defect ⇒ same key, on every machine. */
|
|
38
|
+
export function fingerprintError(message) {
|
|
39
|
+
return createHash('sha256').update(errorSkeleton(message)).digest('hex').slice(0, 12);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Frames from a stack trace that belong to RETICLE, reduced to `basename:line`.
|
|
43
|
+
*
|
|
44
|
+
* A crash stack is mostly the user's own application and node internals; neither is ours to collect,
|
|
45
|
+
* and the user's frames are the ones that would carry their directory layout and module names. Only
|
|
46
|
+
* frames inside a `@reticlehq/*` package or a `reticle` dist path survive, and even those keep just
|
|
47
|
+
* the file basename — enough to point a maintainer at the right file, never enough to describe
|
|
48
|
+
* anyone's machine.
|
|
49
|
+
*/
|
|
50
|
+
export function reticleFrames(stack) {
|
|
51
|
+
const frames = [];
|
|
52
|
+
for (const line of stack.split('\n')) {
|
|
53
|
+
if (!/@reticlehq[/\\]|[/\\]reticle[/\\]dist[/\\]/.test(line))
|
|
54
|
+
continue;
|
|
55
|
+
const location = line.match(/([\w.-]+\.(?:js|mjs|cjs|ts)):(\d+)/);
|
|
56
|
+
if (location === null)
|
|
57
|
+
continue;
|
|
58
|
+
// `at Object.runTool (/path/to/invoke-tool.js:88:3)` → `runTool`. The function name is half the
|
|
59
|
+
// value of a frame in an RCA — "it died in act-tools.js" narrows to a file, "it died in
|
|
60
|
+
// resolveAnchor" names the thing that broke. Anonymous frames just report the location.
|
|
61
|
+
const fn = line.match(/at\s+(?:async\s+)?(?:new\s+)?([\w.$<>[\]]+)\s+\(/)?.[1];
|
|
62
|
+
const where = `${location[1] ?? ''}:${location[2] ?? ''}`;
|
|
63
|
+
// `Object.` / `Module.` prefixes are V8 noise that make the same function look like two.
|
|
64
|
+
const name = fn?.replace(/^(?:Object|Module|Function)\./, '');
|
|
65
|
+
frames.push(name === undefined || name === '' ? where : `${name}@${where}`);
|
|
66
|
+
}
|
|
67
|
+
return frames;
|
|
68
|
+
}
|
|
69
|
+
/** How many of our own frames to carry. The innermost few are where the defect is. */
|
|
70
|
+
export const MAX_REPORTED_FRAMES = 12;
|
|
71
|
+
/**
|
|
72
|
+
* A crash fingerprint from the error's type and its Reticle-owned frames. Falls back to the message
|
|
73
|
+
* skeleton when a stack has no Reticle frames at all (a crash from deep inside a dependency), so a
|
|
74
|
+
* crash is never dropped for lack of a clean attribution.
|
|
75
|
+
*/
|
|
76
|
+
export function fingerprintCrash(errorType, stack, message) {
|
|
77
|
+
const frames = reticleFrames(stack);
|
|
78
|
+
const basis = frames.length > 0
|
|
79
|
+
? `${errorType}|${frames.join('|')}`
|
|
80
|
+
: `${errorType}|${errorSkeleton(message)}`;
|
|
81
|
+
return createHash('sha256').update(basis).digest('hex').slice(0, 12);
|
|
82
|
+
}
|
|
83
|
+
/** The error's constructor name (`TypeError`), or `Unknown` for a thrown non-Error. */
|
|
84
|
+
export function errorTypeOf(error) {
|
|
85
|
+
if (error instanceof Error)
|
|
86
|
+
return error.constructor.name.slice(0, 64);
|
|
87
|
+
return 'Unknown';
|
|
88
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { TelemetryAction } from '../cli-parse.js';
|
|
2
|
+
/**
|
|
3
|
+
* `reticle feedback [--rating 1-5] [--bug] "what happened"` — the human half of the feedback channel.
|
|
4
|
+
*
|
|
5
|
+
* Kept to a single command with no prompts, no editor, and no account: the cost of telling us
|
|
6
|
+
* something has to be lower than the cost of shrugging and moving on, or we only ever hear from people
|
|
7
|
+
* angry enough to open an issue. A bare rating is a valid report; the words are optional garnish.
|
|
8
|
+
*
|
|
9
|
+
* What is sent is PRINTED before it goes, every time. A channel that carries free text and does not
|
|
10
|
+
* show you the payload is asking for trust it has not earned.
|
|
11
|
+
*/
|
|
12
|
+
export declare function handleFeedback(text: string, rating: number | undefined, bug: boolean): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* `reticle identify` — opt-in, and it shows the consent notice before it sends anything.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately NOT interactive: a prompt that appears in the middle of someone's work is pressure,
|
|
17
|
+
* and this is the one command where the user must be choosing freely. They type what they want to
|
|
18
|
+
* share, or they never run it at all.
|
|
19
|
+
*/
|
|
20
|
+
export declare function handleIdentify(parsed: {
|
|
21
|
+
context?: string;
|
|
22
|
+
company?: string;
|
|
23
|
+
email?: string;
|
|
24
|
+
forget: boolean;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* `reticle telemetry [status|enable|disable]` — the user-facing control for anonymous usage metrics.
|
|
28
|
+
* `disable` persists a machine-wide opt-out (survives shells, unlike RETICLE_TELEMETRY=0); `status`
|
|
29
|
+
* says what's on, why, and where the full policy lives. Human-readable to stdout, like `doctor`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function handleTelemetry(action: TelemetryAction): void;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { FeedbackKind, FeedbackSource } from '@reticlehq/core';
|
|
2
|
+
import { BUG_FLAG, RATING_FLAG } from '../cli-parse.js';
|
|
3
|
+
import { describeFeedbackPayload, submitFeedback } from './feedback.js';
|
|
4
|
+
import { describeTelemetry, setTelemetryEnabled } from './telemetry.js';
|
|
5
|
+
import { TelemetryAction } from '../cli-parse.js';
|
|
6
|
+
import { clearIdentity, IDENTIFY_NOTICE, readIdentity, saveIdentity, submitIdentity, UsageContextKind, } from './identify.js';
|
|
7
|
+
/**
|
|
8
|
+
* `reticle feedback [--rating 1-5] [--bug] "what happened"` — the human half of the feedback channel.
|
|
9
|
+
*
|
|
10
|
+
* Kept to a single command with no prompts, no editor, and no account: the cost of telling us
|
|
11
|
+
* something has to be lower than the cost of shrugging and moving on, or we only ever hear from people
|
|
12
|
+
* angry enough to open an issue. A bare rating is a valid report; the words are optional garnish.
|
|
13
|
+
*
|
|
14
|
+
* What is sent is PRINTED before it goes, every time. A channel that carries free text and does not
|
|
15
|
+
* show you the payload is asking for trust it has not earned.
|
|
16
|
+
*/
|
|
17
|
+
export async function handleFeedback(text, rating, bug) {
|
|
18
|
+
const line = (s) => {
|
|
19
|
+
process.stdout.write(`${s}\n`);
|
|
20
|
+
};
|
|
21
|
+
if (text === '' && rating === undefined) {
|
|
22
|
+
line(`usage: reticle feedback [${RATING_FLAG} 1-5] [${BUG_FLAG}] "what worked, what didn't"`);
|
|
23
|
+
line(' your words go to the maintainers; nothing from your app is ever included.');
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const receipt = await submitFeedback({
|
|
28
|
+
source: FeedbackSource.HUMAN,
|
|
29
|
+
kind: bug ? FeedbackKind.BUG : FeedbackKind.EXPERIENCE,
|
|
30
|
+
// A rating with no words is a real report, but the schema wants text — say what the rating means.
|
|
31
|
+
text: text === '' ? `rated ${String(rating)}/5 with no comment` : text,
|
|
32
|
+
...(rating !== undefined ? { rating } : {}),
|
|
33
|
+
});
|
|
34
|
+
line(`sending ${JSON.stringify(describeFeedbackPayload(receipt.context))}`);
|
|
35
|
+
if (receipt.redacted.length > 0) {
|
|
36
|
+
line(`redacted ${receipt.redacted.join(', ')} (removed before sending)`);
|
|
37
|
+
}
|
|
38
|
+
line(receipt.sent
|
|
39
|
+
? 'thanks your feedback is in. it genuinely changes what gets built next.'
|
|
40
|
+
: `not sent ${receipt.reason ?? 'unknown reason'}`);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* `reticle identify` — opt-in, and it shows the consent notice before it sends anything.
|
|
44
|
+
*
|
|
45
|
+
* Deliberately NOT interactive: a prompt that appears in the middle of someone's work is pressure,
|
|
46
|
+
* and this is the one command where the user must be choosing freely. They type what they want to
|
|
47
|
+
* share, or they never run it at all.
|
|
48
|
+
*/
|
|
49
|
+
export async function handleIdentify(parsed) {
|
|
50
|
+
const line = (s) => {
|
|
51
|
+
process.stdout.write(`${s}\n`);
|
|
52
|
+
};
|
|
53
|
+
if (parsed.forget) {
|
|
54
|
+
clearIdentity();
|
|
55
|
+
line('forgotten the local identity file is deleted; nothing further is sent.');
|
|
56
|
+
line(' to have what was already sent removed, email support@reticlehq.com');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const context = Object.values(UsageContextKind).find((k) => k === parsed.context);
|
|
60
|
+
if (context === undefined) {
|
|
61
|
+
const existing = readIdentity();
|
|
62
|
+
if (existing !== undefined)
|
|
63
|
+
line(`current ${JSON.stringify(existing)}`);
|
|
64
|
+
line(`usage: reticle identify --context <${Object.values(UsageContextKind).join('|')}>`);
|
|
65
|
+
line(' [--company "Acme"] [--email you@acme.com] [--forget]');
|
|
66
|
+
line('');
|
|
67
|
+
line(IDENTIFY_NOTICE);
|
|
68
|
+
process.exitCode = existing === undefined ? 1 : 0;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const identity = {
|
|
72
|
+
context,
|
|
73
|
+
...(parsed.company !== undefined ? { company: parsed.company } : {}),
|
|
74
|
+
...(parsed.email !== undefined ? { email: parsed.email } : {}),
|
|
75
|
+
};
|
|
76
|
+
line(IDENTIFY_NOTICE);
|
|
77
|
+
line('');
|
|
78
|
+
line(`sending ${JSON.stringify(identity)}`);
|
|
79
|
+
saveIdentity(identity);
|
|
80
|
+
const sent = await submitIdentity(identity);
|
|
81
|
+
line(sent
|
|
82
|
+
? 'thanks saved and sent. we may reach out; `reticle identify --forget` undoes this.'
|
|
83
|
+
: 'not sent telemetry is disabled on this machine, so it was saved locally only.');
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `reticle telemetry [status|enable|disable]` — the user-facing control for anonymous usage metrics.
|
|
87
|
+
* `disable` persists a machine-wide opt-out (survives shells, unlike RETICLE_TELEMETRY=0); `status`
|
|
88
|
+
* says what's on, why, and where the full policy lives. Human-readable to stdout, like `doctor`.
|
|
89
|
+
*/
|
|
90
|
+
export function handleTelemetry(action) {
|
|
91
|
+
const line = (s) => {
|
|
92
|
+
process.stdout.write(`${s}\n`);
|
|
93
|
+
};
|
|
94
|
+
if (action !== TelemetryAction.STATUS) {
|
|
95
|
+
setTelemetryEnabled(action === TelemetryAction.ENABLE);
|
|
96
|
+
}
|
|
97
|
+
const state = describeTelemetry();
|
|
98
|
+
line(`telemetry ${state.enabled ? 'enabled' : 'disabled'} (${state.reason})`);
|
|
99
|
+
line(`policy ${state.policyUrl}`);
|
|
100
|
+
if (state.enabled)
|
|
101
|
+
line('opt out reticle telemetry disable (or RETICLE_TELEMETRY=0 / DO_NOT_TRACK=1)');
|
|
102
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { McpScope, type Feedback } from '@reticlehq/core';
|
|
2
|
+
/** The context fields — the whole `Feedback` shape minus what the author supplies. */
|
|
3
|
+
export type FeedbackContext = Pick<Feedback, 'stack' | 'stackMajor' | 'runtime' | 'engine' | 'driver' | 'client' | 'clientVersion' | 'mcpScope'>;
|
|
4
|
+
/**
|
|
5
|
+
* The project's framework and its MAJOR version, or `{}` when package.json is absent, unreadable, or
|
|
6
|
+
* names nothing we recognize. The major comes from `parseMajor` — the same range parser `reticle init`
|
|
7
|
+
* uses to decide React 19 source mapping — so a range like `^15.0.0-canary.3` narrows the same way in
|
|
8
|
+
* both places instead of via a second, subtly different reader.
|
|
9
|
+
*/
|
|
10
|
+
export declare function detectStack(cwd: string, read?: (path: string) => string): {
|
|
11
|
+
stack?: string;
|
|
12
|
+
stackMajor?: number;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* User scope unless the project checks in its own MCP registration. `reticle init` deliberately
|
|
16
|
+
* registers ONCE at user scope, so `project` here means the team wired it up by hand — which is
|
|
17
|
+
* itself the interesting signal.
|
|
18
|
+
*/
|
|
19
|
+
export declare function detectMcpScope(cwd: string, exists?: (path: string) => boolean): McpScope;
|
|
20
|
+
export declare function setMcpClientNameHook(hook: () => {
|
|
21
|
+
name?: string;
|
|
22
|
+
version?: string;
|
|
23
|
+
} | undefined): void;
|
|
24
|
+
/** What the connected session knows about itself — the shell it runs in and its rendering engine. */
|
|
25
|
+
export interface SessionFacts {
|
|
26
|
+
runtime?: string;
|
|
27
|
+
engine?: string;
|
|
28
|
+
/** True when Reticle drives the page over CDP rather than observing the human's own browser. */
|
|
29
|
+
driven?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** Assemble the full context. Every field independently optional — a partial context still ships. */
|
|
32
|
+
export declare function feedbackContext(cwd: string, session?: SessionFacts): FeedbackContext;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The environment context stapled to every feedback report. Feedback without it is a complaint;
|
|
3
|
+
* feedback with it is a work item — "reticle_act misses the click" is unactionable, "reticle_act
|
|
4
|
+
* misses the click on Tauri + SvelteKit under a CDP driver" names the bug.
|
|
5
|
+
*
|
|
6
|
+
* Everything here is detected LOCALLY from files and env already on the machine, and every field is
|
|
7
|
+
* low-cardinality by construction (an enum, or a framework name off a fixed list). No paths, no repo
|
|
8
|
+
* names, no dependency inventory, no UA string — the same non-identifying bar the rest of telemetry
|
|
9
|
+
* holds to. Detection is entirely best-effort: an unreadable package.json or an SDK too old to report
|
|
10
|
+
* its runtime yields `undefined`, never a throw and never a guess.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { AppRuntime, McpScope, PageDriver } from '@reticlehq/core';
|
|
15
|
+
import { parseMajor } from '../init/detect.js';
|
|
16
|
+
/**
|
|
17
|
+
* Dependency → reported stack, in resolution order. Meta-frameworks come FIRST: a Next app also
|
|
18
|
+
* depends on react, and reporting `react` for it would collapse the single most important segment
|
|
19
|
+
* we have into the generic bucket.
|
|
20
|
+
*
|
|
21
|
+
* Deliberately NOT reusing `init/detect.ts`. That module answers a different question — "which build
|
|
22
|
+
* integration do I install?" — so its enum stops at next/vite/sveltekit/html. Widening it to carry
|
|
23
|
+
* vue/astro/solid would make `reticle init` branch on frameworks it has no install path for.
|
|
24
|
+
*/
|
|
25
|
+
const STACK_BY_DEP = [
|
|
26
|
+
['next', 'next'],
|
|
27
|
+
['@sveltejs/kit', 'sveltekit'],
|
|
28
|
+
['nuxt', 'nuxt'],
|
|
29
|
+
['astro', 'astro'],
|
|
30
|
+
['@remix-run/react', 'remix'],
|
|
31
|
+
['@angular/core', 'angular'],
|
|
32
|
+
['react', 'react'],
|
|
33
|
+
['vue', 'vue'],
|
|
34
|
+
['svelte', 'svelte'],
|
|
35
|
+
['solid-js', 'solid'],
|
|
36
|
+
['vite', 'vite'],
|
|
37
|
+
];
|
|
38
|
+
const PACKAGE_JSON = 'package.json';
|
|
39
|
+
/** A project-scoped MCP registration lives in one of these; `reticle init` writes user scope instead. */
|
|
40
|
+
const PROJECT_MCP_FILES = ['.mcp.json', join('.cursor', 'mcp.json'), join('.vscode', 'mcp.json')];
|
|
41
|
+
/**
|
|
42
|
+
* The project's framework and its MAJOR version, or `{}` when package.json is absent, unreadable, or
|
|
43
|
+
* names nothing we recognize. The major comes from `parseMajor` — the same range parser `reticle init`
|
|
44
|
+
* uses to decide React 19 source mapping — so a range like `^15.0.0-canary.3` narrows the same way in
|
|
45
|
+
* both places instead of via a second, subtly different reader.
|
|
46
|
+
*/
|
|
47
|
+
export function detectStack(cwd, read = readFile) {
|
|
48
|
+
let pkg;
|
|
49
|
+
try {
|
|
50
|
+
pkg = JSON.parse(read(join(cwd, PACKAGE_JSON)));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
const deps = { ...pkg.devDependencies, ...pkg.dependencies };
|
|
56
|
+
for (const [dep, stack] of STACK_BY_DEP) {
|
|
57
|
+
const range = deps[dep];
|
|
58
|
+
if (range === undefined)
|
|
59
|
+
continue;
|
|
60
|
+
const stackMajor = parseMajor(range);
|
|
61
|
+
return { stack, ...(stackMajor !== undefined ? { stackMajor } : {}) };
|
|
62
|
+
}
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* User scope unless the project checks in its own MCP registration. `reticle init` deliberately
|
|
67
|
+
* registers ONCE at user scope, so `project` here means the team wired it up by hand — which is
|
|
68
|
+
* itself the interesting signal.
|
|
69
|
+
*/
|
|
70
|
+
export function detectMcpScope(cwd, exists = fileExists) {
|
|
71
|
+
return PROJECT_MCP_FILES.some((file) => exists(join(cwd, file)))
|
|
72
|
+
? McpScope.PROJECT
|
|
73
|
+
: McpScope.USER;
|
|
74
|
+
}
|
|
75
|
+
function readFile(path) {
|
|
76
|
+
return readFileSync(path, 'utf8');
|
|
77
|
+
}
|
|
78
|
+
function fileExists(path) {
|
|
79
|
+
try {
|
|
80
|
+
readFileSync(path);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The MCP client on the other end, self-reported in its `initialize` handshake (`claude-code`,
|
|
89
|
+
* `cursor-vscode`, …). Read through a HOOK rather than sniffing env vars: the handshake is the
|
|
90
|
+
* client's own claim about itself, while an env-var table is a guess that silently rots every time a
|
|
91
|
+
* vendor renames a variable. Unset (a CLI run with no MCP peer) simply reports no client.
|
|
92
|
+
*/
|
|
93
|
+
let clientNameHook;
|
|
94
|
+
export function setMcpClientNameHook(hook) {
|
|
95
|
+
clientNameHook = hook;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The client's own name and version from its handshake.
|
|
99
|
+
*
|
|
100
|
+
* Note what is NOT here and cannot be: the MODEL. MCP's `clientInfo` carries a name, a title and a
|
|
101
|
+
* version and has no concept of a model, so the transport genuinely cannot tell us. The agent
|
|
102
|
+
* self-reports it on the feedback itself, which is the only mechanism available and a reliable one
|
|
103
|
+
* given the report is already something the agent authored.
|
|
104
|
+
*/
|
|
105
|
+
function detectClient() {
|
|
106
|
+
try {
|
|
107
|
+
const info = clientNameHook?.();
|
|
108
|
+
const name = info?.name;
|
|
109
|
+
const version = info?.version;
|
|
110
|
+
return {
|
|
111
|
+
...(name !== undefined && name !== '' ? { client: name.slice(0, 64) } : {}),
|
|
112
|
+
...(version !== undefined && version !== '' ? { clientVersion: version.slice(0, 32) } : {}),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return {};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function asRuntime(value) {
|
|
120
|
+
return Object.values(AppRuntime).find((known) => known === value);
|
|
121
|
+
}
|
|
122
|
+
function asEngine(value) {
|
|
123
|
+
// The SDK already narrows to blink/gecko/webkit before it reports, so anything else is a stale or
|
|
124
|
+
// hand-forged payload — drop it rather than widen the enum at the boundary.
|
|
125
|
+
const known = ['blink', 'gecko', 'webkit'];
|
|
126
|
+
return known.find((engine) => engine === value);
|
|
127
|
+
}
|
|
128
|
+
/** Assemble the full context. Every field independently optional — a partial context still ships. */
|
|
129
|
+
export function feedbackContext(cwd, session) {
|
|
130
|
+
const runtime = asRuntime(session?.runtime);
|
|
131
|
+
const engine = asEngine(session?.engine);
|
|
132
|
+
const client = detectClient();
|
|
133
|
+
return {
|
|
134
|
+
...detectStack(cwd),
|
|
135
|
+
...client,
|
|
136
|
+
...(runtime !== undefined ? { runtime } : {}),
|
|
137
|
+
...(engine !== undefined ? { engine } : {}),
|
|
138
|
+
...(session?.driven !== undefined
|
|
139
|
+
? { driver: session.driven ? PageDriver.CDP : PageDriver.SDK }
|
|
140
|
+
: {}),
|
|
141
|
+
mcpScope: detectMcpScope(cwd),
|
|
142
|
+
};
|
|
143
|
+
}
|