@reticlehq/server 2.3.0 → 2.5.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/SKILL.md +702 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +1 -1
- package/dist/{bridge.js → bridge/bridge.js} +105 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +6 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +21 -9
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/{cli-doctor.js → cli/cli-doctor.js} +9 -2
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/cli/cli-parse.d.ts +143 -0
- package/dist/{cli-parse.js → cli/cli-parse.js} +146 -65
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +15 -0
- package/dist/{cli-port.js → cli/cli-port.js} +29 -8
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +182 -93
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +49 -0
- package/dist/daemon/daemon-resilience.js +191 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +80 -48
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +13 -0
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +27 -4
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +43 -20
- package/dist/events/predicate-eval.js +159 -55
- package/dist/events/predicate.d.ts +2 -2
- package/dist/events/predicate.js +112 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +1 -1
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.d.ts +0 -6
- package/dist/flows/decision.js +64 -8
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +5 -7
- package/dist/flows/flow-replay.js +114 -47
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +157 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +67 -13
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +16 -3
- package/dist/honesty/blind-spots.js +24 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +22 -3
- package/dist/honesty/verified.js +48 -8
- package/dist/http-server.js +14 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +8 -6
- package/dist/index.js +32 -25
- package/dist/init/agent-rules.d.ts +2 -1
- package/dist/init/agent-rules.js +45 -7
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.d.ts +17 -0
- package/dist/init/capabilities.js +54 -0
- package/dist/init/cra.d.ts +37 -0
- package/dist/init/cra.js +90 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +26 -0
- package/dist/init/detect.js +78 -5
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +39 -0
- package/dist/init/next-patch.js +196 -0
- package/dist/init/node-io.js +48 -6
- package/dist/init/patch-kind.d.ts +20 -0
- package/dist/init/patch-kind.js +10 -0
- package/dist/init/plan-framework.d.ts +50 -0
- package/dist/init/plan-framework.js +341 -0
- package/dist/init/plan.d.ts +119 -6
- package/dist/init/plan.js +230 -131
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +22 -0
- package/dist/init/run.js +393 -28
- package/dist/init/slash-command.d.ts +27 -0
- package/dist/init/slash-command.js +61 -0
- package/dist/init/snippets.d.ts +41 -3
- package/dist/init/snippets.js +185 -10
- package/dist/init/vite-config.d.ts +4 -10
- package/dist/init/vite-config.js +15 -7
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.d.ts +15 -0
- package/dist/input/driven-redaction.js +76 -0
- package/dist/input/network-detail.d.ts +9 -3
- package/dist/input/network-detail.js +43 -23
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +31 -0
- package/dist/mcp/mcp-outage.js +48 -0
- package/dist/mcp/mcp-proxy.d.ts +124 -0
- package/dist/mcp/mcp-proxy.js +697 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +110 -35
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +38 -0
- package/dist/session/no-session-diagnosis.js +70 -0
- package/dist/session/no-session-watch.d.ts +19 -0
- package/dist/session/no-session-watch.js +61 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +26 -7
- package/dist/session/session-info.js +42 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +3 -0
- package/dist/session/session-manager.js +58 -9
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +20 -2
- package/dist/session/session.js +76 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/daemon-telemetry.d.ts +19 -0
- package/dist/telemetry/daemon-telemetry.js +12 -2
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +1 -1
- package/dist/telemetry/feedback-cli.js +4 -4
- package/dist/telemetry/feedback-context.d.ts +22 -0
- package/dist/telemetry/feedback-context.js +41 -1
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +2 -0
- package/dist/telemetry/session-metrics.js +68 -6
- package/dist/telemetry/telemetry.d.ts +27 -2
- package/dist/telemetry/telemetry.js +119 -29
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +84 -115
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +3 -1
- package/dist/tools/assert-verdict.js +8 -5
- package/dist/tools/browser-tools.js +30 -11
- package/dist/tools/contract-tools.js +1 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +90 -18
- package/dist/tools/error-recovery.d.ts +20 -2
- package/dist/tools/error-recovery.js +179 -4
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -6
- package/dist/tools/invoke-tool.js +57 -32
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-result.d.ts +17 -0
- package/dist/tools/navigate-result.js +31 -0
- package/dist/tools/observe-tools.js +54 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +85 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +88 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +8 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +204 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures.md +28 -0
- package/docs/flows.md +166 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +184 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-launch.js +0 -112
- package/dist/cli-parse.d.ts +0 -140
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -35
- package/dist/mcp-proxy.js +0 -190
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-doctor.d.ts → cli/cli-doctor.d.ts} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
package/dist/init/snippets.d.ts
CHANGED
|
@@ -6,8 +6,39 @@
|
|
|
6
6
|
export declare function viteManual(port: number | undefined): string;
|
|
7
7
|
/** Next.js config wrap — always printed (we never auto-rewrite next.config). */
|
|
8
8
|
export declare function nextConfigManual(configFile: string): string;
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The dev-only client component that connects Reticle after hydration.
|
|
11
|
+
*
|
|
12
|
+
* The token is not optional. The bridge requires a pairing token even on localhost, and unlike Vite
|
|
13
|
+
* (where the plugin injects it) a Next app has to carry it through `withReticle`, which publishes it
|
|
14
|
+
* as `NEXT_PUBLIC_RETICLE_TOKEN`. This file used to connect with only a projectId, so every Next
|
|
15
|
+
* setup ended at `bridge refused the connection: authentication failed` and no session ever appeared.
|
|
16
|
+
*/
|
|
17
|
+
export declare function nextReticleDevFile(port: number | undefined, projectId?: string, testids?: readonly string[], stores?: readonly string[]): string;
|
|
18
|
+
export declare function astroManual(port: number | undefined, projectId?: string,
|
|
19
|
+
/**
|
|
20
|
+
* A layout file that actually exists, when one does.
|
|
21
|
+
*
|
|
22
|
+
* Reported from the field: on `examples/framework-react` — which has no layout at all, only
|
|
23
|
+
* `src/pages/index.astro` — init printed thirty lines telling the user to paste into "your
|
|
24
|
+
* layout". Instructions that name a file the project does not have read as a mistake by the
|
|
25
|
+
* reader, and cost them the time it takes to go and confirm it is missing.
|
|
26
|
+
*/
|
|
27
|
+
layoutPath?: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* The app-side dev module the Vite plugin imports by convention.
|
|
30
|
+
*
|
|
31
|
+
* `registerCapabilities` tells the agent what it can drive without guessing; `registerStore` is the
|
|
32
|
+
* one that matters most and the one we cannot write for you — detecting that an app depends on
|
|
33
|
+
* zustand is easy, knowing which module exports the store instance is not, and a wrong import here
|
|
34
|
+
* breaks the module everything else hangs off. So the store lines are generated COMMENTED, naming
|
|
35
|
+
* the libraries actually found in package.json, with the exact call to uncomment.
|
|
36
|
+
*/
|
|
37
|
+
export declare function viteDevModuleFile(testids: readonly string[], stores: readonly string[]): string;
|
|
38
|
+
/** Where that module goes. Matches @reticlehq/vite-plugin's convention list. */
|
|
39
|
+
export declare const VITE_DEV_MODULE_PATH = "src/reticle-dev.ts";
|
|
40
|
+
/** Default root-layout path, used when no layout was found on disk (reporting only). */
|
|
41
|
+
export declare const NEXT_LAYOUT_PATH = "app/layout.tsx";
|
|
11
42
|
/** Mount instruction for the root layout. */
|
|
12
43
|
export declare const NEXT_LAYOUT_MANUAL = "Mount <ReticleDev /> in your root layout (app/layout.tsx), dev-only:\n\n import { ReticleDev } from './reticle-dev';\n // inside <body>:\n {process.env.NODE_ENV === 'development' ? <ReticleDev /> : null}";
|
|
13
44
|
/**
|
|
@@ -23,7 +54,14 @@ export declare const SVELTEKIT_HOOKS_PATH = "src/hooks.client.ts";
|
|
|
23
54
|
* Said to the user's face rather than discovered later. React, Next, Remix and Astro each have an
|
|
24
55
|
* app and a CI gate; SvelteKit has neither, so "it generated some wiring" is not evidence it works.
|
|
25
56
|
*/
|
|
26
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Said out loud when the app does not render through React. The SDK is framework-agnostic — DOM,
|
|
59
|
+
* network, console and routing all still work — but `@reticlehq/react` is a React adapter, so
|
|
60
|
+
* component names and source mapping do not, and no CI gate covers this stack. Reporting all-green
|
|
61
|
+
* here is the one thing this project exists not to do.
|
|
62
|
+
*/
|
|
63
|
+
export declare function unverifiedUiLibraryNote(library: string): string;
|
|
64
|
+
export declare const UNVERIFIED_FRAMEWORK_NOTE = "Reticle has no SvelteKit app and no CI gate for one, so this wiring is untested \u2014 it may work, but nothing proves it and nothing will tell us if it breaks. Supported and gated today: Vite + React, Next.js, Remix and Astro. If the hook does not register a session, please open an issue.";
|
|
27
65
|
/**
|
|
28
66
|
* Dev-only client hook that connects Reticle in a SvelteKit app. SvelteKit renders through app.html and
|
|
29
67
|
* never triggers Vite's index.html injection (verified), so the standard plugin can't auto-connect —
|
package/dist/init/snippets.js
CHANGED
|
@@ -37,8 +37,21 @@ export function nextConfigManual(configFile) {
|
|
|
37
37
|
|
|
38
38
|
export default withReticle(nextConfig);`;
|
|
39
39
|
}
|
|
40
|
-
/**
|
|
41
|
-
|
|
40
|
+
/**
|
|
41
|
+
* The dev-only client component that connects Reticle after hydration.
|
|
42
|
+
*
|
|
43
|
+
* The token is not optional. The bridge requires a pairing token even on localhost, and unlike Vite
|
|
44
|
+
* (where the plugin injects it) a Next app has to carry it through `withReticle`, which publishes it
|
|
45
|
+
* as `NEXT_PUBLIC_RETICLE_TOKEN`. This file used to connect with only a projectId, so every Next
|
|
46
|
+
* setup ended at `bridge refused the connection: authentication failed` and no session ever appeared.
|
|
47
|
+
*/
|
|
48
|
+
export function nextReticleDevFile(port, projectId, testids = [], stores = []) {
|
|
49
|
+
const base = connectArg(port, projectId);
|
|
50
|
+
const fields = '' === base ? '' : `${base.slice(1, -1).trim()}, `;
|
|
51
|
+
const ids = testids.map((t) => `'${t}'`).join(', ');
|
|
52
|
+
const storeBlock = 0 === stores.length
|
|
53
|
+
? ' // No state library detected. If you add one, register it here — see docs/usage.md.'
|
|
54
|
+
: stores.map((h) => ` // import your store, then: ${h}`).join('\n');
|
|
42
55
|
return `'use client';
|
|
43
56
|
import { useEffect } from 'react';
|
|
44
57
|
|
|
@@ -46,15 +59,151 @@ import { useEffect } from 'react';
|
|
|
46
59
|
export function ReticleDev() {
|
|
47
60
|
useEffect(() => {
|
|
48
61
|
if (process.env.NODE_ENV !== 'development') return;
|
|
49
|
-
void import('@reticlehq/react').then(({ reticle, install }) => {
|
|
62
|
+
void import('@reticlehq/react').then(({ reticle, install, registerCapabilities }) => {
|
|
50
63
|
install();
|
|
51
|
-
|
|
64
|
+
// Both provided by withReticle() in next.config. The bridge rejects a connect with no token;
|
|
65
|
+
// the root makes source paths repo-relative instead of absolute.
|
|
66
|
+
const token = process.env.NEXT_PUBLIC_RETICLE_TOKEN;
|
|
67
|
+
const root = process.env.NEXT_PUBLIC_RETICLE_ROOT;
|
|
68
|
+
reticle.connect({ ${fields}...(token ? { token } : {}), ...(root ? { root } : {}) });
|
|
69
|
+
|
|
70
|
+
// ── Start with ONE flow. ──────────────────────────────────────────────────────────────────
|
|
71
|
+
// Registering a store is the highest-value line here: it lets the agent check what the app
|
|
72
|
+
// BELIEVES, not just what it rendered. Pass the STORE, not \`() => store.getState()\` — the store
|
|
73
|
+
// form wires \`subscribe\` too, so every mutation emits a diff; the getter form is read-only.
|
|
74
|
+
${storeBlock}
|
|
75
|
+
registerCapabilities({
|
|
76
|
+
testids: [${ids}],${0 === testids.length ? ' // none found — add data-testid to your key elements' : ''}
|
|
77
|
+
signals: [], // names you pass to reticle.signal()
|
|
78
|
+
stores: [], // the keys you registered above
|
|
79
|
+
});
|
|
52
80
|
});
|
|
53
81
|
}, []);
|
|
54
82
|
return null;
|
|
55
83
|
}
|
|
56
84
|
`;
|
|
57
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Astro connect instructions.
|
|
88
|
+
*
|
|
89
|
+
* Astro is Vite-based but SSRs its own HTML, so the plugin's index.html injection never fires, and
|
|
90
|
+
* `vite` is not a direct dependency — so this used to fall through to the generic HTML advice, which
|
|
91
|
+
* tells you to add a connect to an "entry module" Astro does not have, or to bundle the SDK with
|
|
92
|
+
* esbuild. Both are wrong for Astro, and following either gets you nothing.
|
|
93
|
+
*
|
|
94
|
+
* Astro bundles a page `<script>`, so the bare import resolves there. The token has to be inlined by
|
|
95
|
+
* the config because there is no plugin in the page's path to inject it, and `build.target` has to be
|
|
96
|
+
* raised or Astro down-levels the modern SDK bundle and dies on a destructuring transform.
|
|
97
|
+
*/
|
|
98
|
+
/**
|
|
99
|
+
* Name the file the connect <script> should go in — the layout when one exists, otherwise the page,
|
|
100
|
+
* because a project with no layout has nowhere else to put it and should not be told otherwise.
|
|
101
|
+
*/
|
|
102
|
+
function layoutHost(layoutPath) {
|
|
103
|
+
return layoutPath === undefined
|
|
104
|
+
? '2. This project has no layout, so put it in the page you want instrumented (e.g.\n src/pages/index.astro) — every page you want a session from needs it — inside <body>:'
|
|
105
|
+
: `2. In ${layoutPath} (or any other page you want instrumented), inside <body>:`;
|
|
106
|
+
}
|
|
107
|
+
export function astroManual(port, projectId,
|
|
108
|
+
/**
|
|
109
|
+
* A layout file that actually exists, when one does.
|
|
110
|
+
*
|
|
111
|
+
* Reported from the field: on `examples/framework-react` — which has no layout at all, only
|
|
112
|
+
* `src/pages/index.astro` — init printed thirty lines telling the user to paste into "your
|
|
113
|
+
* layout". Instructions that name a file the project does not have read as a mistake by the
|
|
114
|
+
* reader, and cost them the time it takes to go and confirm it is missing.
|
|
115
|
+
*/
|
|
116
|
+
layoutPath) {
|
|
117
|
+
const extra = port !== undefined && port !== RETICLE_DEFAULT_PORT
|
|
118
|
+
? `\n url: '${bridgeWsUrl(port)}',`
|
|
119
|
+
: '';
|
|
120
|
+
const id = projectId !== undefined && projectId.length > 0 ? `\n projectId: '${projectId}',` : '';
|
|
121
|
+
return `Astro renders its own HTML, so the connect goes in a page <script> and the pairing token is inlined by the config.
|
|
122
|
+
|
|
123
|
+
1. In astro.config.mjs — inline the daemon's token and raise the build target:
|
|
124
|
+
|
|
125
|
+
import { readFileSync } from 'node:fs';
|
|
126
|
+
import { homedir } from 'node:os';
|
|
127
|
+
import { join } from 'node:path';
|
|
128
|
+
|
|
129
|
+
function reticleToken() {
|
|
130
|
+
const dir = process.env['RETICLE_PAIRING_TOKEN_DIR'] || join(homedir(), '.reticle');
|
|
131
|
+
try { return readFileSync(join(dir, 'pairing-token'), 'utf8').trim(); } catch { return ''; }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export default defineConfig({
|
|
135
|
+
vite: {
|
|
136
|
+
// Astro's default target down-levels the modern SDK bundle and fails on a destructuring transform.
|
|
137
|
+
build: { target: 'es2022' },
|
|
138
|
+
optimizeDeps: { esbuildOptions: { target: 'es2022' } },
|
|
139
|
+
define: {
|
|
140
|
+
__RETICLE_TOKEN__: JSON.stringify(reticleToken()),
|
|
141
|
+
// Without this, source pointers come back as absolute paths from YOUR machine — useless in a
|
|
142
|
+
// report. Every other framework gets it from its build plugin; Astro owns its Vite instance.
|
|
143
|
+
__RETICLE_ROOT__: JSON.stringify(process.cwd()),
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
${layoutHost(layoutPath)}
|
|
149
|
+
|
|
150
|
+
<script>
|
|
151
|
+
if (import.meta.env.DEV) {
|
|
152
|
+
const token = typeof __RETICLE_TOKEN__ !== 'undefined' ? __RETICLE_TOKEN__ : '';
|
|
153
|
+
const root = typeof __RETICLE_ROOT__ !== 'undefined' ? __RETICLE_ROOT__ : '';
|
|
154
|
+
const { reticle, install } = await import('@reticlehq/react');
|
|
155
|
+
install();
|
|
156
|
+
reticle.connect({${id}${extra}
|
|
157
|
+
...(token.length > 0 ? { token } : {}),
|
|
158
|
+
...(root.length > 0 ? { root } : {}),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
</script>
|
|
162
|
+
|
|
163
|
+
Start the daemon BEFORE \`astro dev\`, so the token file exists when the config is read. Until it does the token is empty and the page reloads once the daemon is up.`;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The app-side dev module the Vite plugin imports by convention.
|
|
167
|
+
*
|
|
168
|
+
* `registerCapabilities` tells the agent what it can drive without guessing; `registerStore` is the
|
|
169
|
+
* one that matters most and the one we cannot write for you — detecting that an app depends on
|
|
170
|
+
* zustand is easy, knowing which module exports the store instance is not, and a wrong import here
|
|
171
|
+
* breaks the module everything else hangs off. So the store lines are generated COMMENTED, naming
|
|
172
|
+
* the libraries actually found in package.json, with the exact call to uncomment.
|
|
173
|
+
*/
|
|
174
|
+
export function viteDevModuleFile(testids, stores) {
|
|
175
|
+
const ids = testids.map((t) => `'${t}'`).join(', ');
|
|
176
|
+
const storeBlock = 0 === stores.length
|
|
177
|
+
? ' // No state library detected. If you add one, register it here — see docs/usage.md.'
|
|
178
|
+
: stores.map((h) => ` // import your store, then: ${h}`).join('\n');
|
|
179
|
+
return `// Dev-only. Imported automatically by @reticlehq/vite-plugin — you do not need to import it.
|
|
180
|
+
// Self-guards on import.meta.env.DEV, so it is a no-op in a production build.
|
|
181
|
+
import { registerCapabilities } from '@reticlehq/react';
|
|
182
|
+
|
|
183
|
+
if (import.meta.env.DEV) {
|
|
184
|
+
// ── Start with ONE flow. ─────────────────────────────────────────────────────────────────────
|
|
185
|
+
// You do not need to describe the whole app to get value, and trying to is the slow path. Register
|
|
186
|
+
// the store your most important flow reads, and list the testids that flow touches. Add more later,
|
|
187
|
+
// when a flow you actually replay needs them.
|
|
188
|
+
//
|
|
189
|
+
// Registering a store is the highest-value line in this file: it lets the agent check what the app
|
|
190
|
+
// BELIEVES, not just what it rendered — the class of bug a screenshot cannot see. Pass the STORE,
|
|
191
|
+
// not \`() => store.getState()\`: the store form wires \`subscribe\` too, so every mutation emits a
|
|
192
|
+
// state diff; the getter form is read-only and silently produces empty diffs.
|
|
193
|
+
${storeBlock}
|
|
194
|
+
|
|
195
|
+
registerCapabilities({
|
|
196
|
+
testids: [${ids}],${0 === testids.length ? ' // none found — add data-testid to your key elements' : ''}
|
|
197
|
+
signals: [], // names you pass to reticle.signal()
|
|
198
|
+
stores: [], // the keys you registered above
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
`;
|
|
202
|
+
}
|
|
203
|
+
/** Where that module goes. Matches @reticlehq/vite-plugin's convention list. */
|
|
204
|
+
export const VITE_DEV_MODULE_PATH = 'src/reticle-dev.ts';
|
|
205
|
+
/** Default root-layout path, used when no layout was found on disk (reporting only). */
|
|
206
|
+
export const NEXT_LAYOUT_PATH = 'app/layout.tsx';
|
|
58
207
|
/** Mount instruction for the root layout. */
|
|
59
208
|
export const NEXT_LAYOUT_MANUAL = `Mount <ReticleDev /> in your root layout (app/layout.tsx), dev-only:
|
|
60
209
|
|
|
@@ -74,10 +223,11 @@ export function htmlManual(port, projectId) {
|
|
|
74
223
|
• Bundled app (Create React App, webpack, Parcel, Vue/Svelte CLI, etc.) — add to your ENTRY module
|
|
75
224
|
(e.g. src/index.js or src/main.js), where '@reticlehq/react' resolves through your bundler:
|
|
76
225
|
|
|
77
|
-
if (location.hostname === 'localhost') {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
226
|
+
if (window.location.hostname === 'localhost') {
|
|
227
|
+
void import('@reticlehq/react').then(({ reticle, install }) => {
|
|
228
|
+
install();
|
|
229
|
+
reticle.connect(${arg});
|
|
230
|
+
});
|
|
81
231
|
}
|
|
82
232
|
|
|
83
233
|
• Plain static HTML with no build step — the browser can't resolve the bare '@reticlehq/react' import, so
|
|
@@ -90,21 +240,46 @@ export const SVELTEKIT_HOOKS_PATH = 'src/hooks.client.ts';
|
|
|
90
240
|
* Said to the user's face rather than discovered later. React, Next, Remix and Astro each have an
|
|
91
241
|
* app and a CI gate; SvelteKit has neither, so "it generated some wiring" is not evidence it works.
|
|
92
242
|
*/
|
|
93
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Said out loud when the app does not render through React. The SDK is framework-agnostic — DOM,
|
|
245
|
+
* network, console and routing all still work — but `@reticlehq/react` is a React adapter, so
|
|
246
|
+
* component names and source mapping do not, and no CI gate covers this stack. Reporting all-green
|
|
247
|
+
* here is the one thing this project exists not to do.
|
|
248
|
+
*/
|
|
249
|
+
export function unverifiedUiLibraryNote(library) {
|
|
250
|
+
return `Detected a ${library} app. Reticle's DOM, network, console and state tools work here, and source file:line does too — the build plugin stamps data-reticle-source regardless of UI library (measured on preact and svelte). What @reticlehq/react adds and you will NOT get is React component identity: component names and component stacks. No CI gate covers ${library}. Gated today: Vite + React, Next.js, Remix, Astro. If something doesn't work, please open an issue.`;
|
|
251
|
+
}
|
|
252
|
+
export const UNVERIFIED_FRAMEWORK_NOTE = 'Reticle has no SvelteKit app and no CI gate for one, so this wiring is untested — it may work, but nothing proves it and nothing will tell us if it breaks. Supported and gated today: Vite + React, Next.js, Remix and Astro. If the hook does not register a session, please open an issue.';
|
|
94
253
|
/**
|
|
95
254
|
* Dev-only client hook that connects Reticle in a SvelteKit app. SvelteKit renders through app.html and
|
|
96
255
|
* never triggers Vite's index.html injection (verified), so the standard plugin can't auto-connect —
|
|
97
256
|
* a client hook is the reliable path. SvelteKit runs src/hooks.client.ts on the client at startup.
|
|
98
257
|
*/
|
|
99
258
|
export function svelteKitHooksFile(port, projectId) {
|
|
259
|
+
const base = connectArg(port, projectId);
|
|
260
|
+
const fields = '' === base ? '' : `${base.slice(1, -1).trim()}, `;
|
|
100
261
|
return `// Dev-only: connect Reticle on the client. SvelteKit renders via app.html, so the Vite-plugin
|
|
101
262
|
// index.html injection doesn't fire — connect from this client hook instead.
|
|
102
263
|
if (import.meta.env.DEV) {
|
|
103
264
|
void import('@reticlehq/react').then(({ reticle, install }) => {
|
|
104
265
|
install();
|
|
105
|
-
|
|
266
|
+
// The bridge requires the pairing token even on localhost. Nothing in a browser can read the
|
|
267
|
+
// file it lives in, so @reticlehq/vite-plugin inlines it here at build time. Without it the
|
|
268
|
+
// console reads "bridge refused the connection: authentication failed" and no session appears.
|
|
269
|
+
const token = typeof __RETICLE_TOKEN__ !== 'undefined' ? __RETICLE_TOKEN__ : '';
|
|
270
|
+
const root = typeof __RETICLE_ROOT__ !== 'undefined' ? __RETICLE_ROOT__ : '';
|
|
271
|
+
const sdkVersion = typeof __RETICLE_SDK_VERSION__ !== 'undefined' ? __RETICLE_SDK_VERSION__ : '';
|
|
272
|
+
reticle.connect({
|
|
273
|
+
${fields}...(token.length > 0 ? { token } : {}),
|
|
274
|
+
...(root.length > 0 ? { root } : {}),
|
|
275
|
+
...(sdkVersion.length > 0 ? { sdkVersion } : {}),
|
|
276
|
+
});
|
|
106
277
|
});
|
|
107
278
|
}
|
|
279
|
+
|
|
280
|
+
declare const __RETICLE_TOKEN__: string | undefined;
|
|
281
|
+
declare const __RETICLE_ROOT__: string | undefined;
|
|
282
|
+
declare const __RETICLE_SDK_VERSION__: string | undefined;
|
|
108
283
|
`;
|
|
109
284
|
}
|
|
110
285
|
/**
|
|
@@ -4,21 +4,15 @@
|
|
|
4
4
|
* bails to a `manual` result so we never half-edit a build config (a broken config is worse than a
|
|
5
5
|
* documented manual step).
|
|
6
6
|
*/
|
|
7
|
+
import { PatchKind, type SourcePatch } from './patch-kind.js';
|
|
7
8
|
export declare const VITE_IMPORT = "import { reticle } from '@reticlehq/vite-plugin';";
|
|
9
|
+
/** Alias kept so existing call sites read in Vite terms; the vocabulary is shared (see patch-kind). */
|
|
8
10
|
export declare const VitePatchKind: {
|
|
9
11
|
readonly APPLY: "apply";
|
|
10
12
|
readonly ALREADY: "already";
|
|
11
13
|
readonly MANUAL: "manual";
|
|
12
14
|
};
|
|
13
|
-
export type VitePatchKind =
|
|
14
|
-
type VitePatch =
|
|
15
|
-
kind: typeof VitePatchKind.APPLY;
|
|
16
|
-
code: string;
|
|
17
|
-
} | {
|
|
18
|
-
kind: typeof VitePatchKind.ALREADY;
|
|
19
|
-
} | {
|
|
20
|
-
kind: typeof VitePatchKind.MANUAL;
|
|
21
|
-
reason: string;
|
|
22
|
-
};
|
|
15
|
+
export type VitePatchKind = PatchKind;
|
|
16
|
+
type VitePatch = SourcePatch;
|
|
23
17
|
export declare function patchViteConfig(source: string, port?: number): VitePatch;
|
|
24
18
|
export {};
|
package/dist/init/vite-config.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* bails to a `manual` result so we never half-edit a build config (a broken config is worse than a
|
|
5
5
|
* documented manual step).
|
|
6
6
|
*/
|
|
7
|
+
import { PatchKind } from './patch-kind.js';
|
|
7
8
|
export const VITE_IMPORT = "import { reticle } from '@reticlehq/vite-plugin';";
|
|
8
9
|
const RETICLE_MARKER = '@reticlehq/vite-plugin';
|
|
9
10
|
/** The `reticle...)` call — carries the bridge port so the injected connect targets it. */
|
|
@@ -14,11 +15,8 @@ function reticlePluginCall(port) {
|
|
|
14
15
|
const PLUGINS_ARRAY = /plugins\s*:\s*\[/;
|
|
15
16
|
/** Matches an ES import statement (used to place our import after the last one). */
|
|
16
17
|
const IMPORT_LINE = /^import\s.+from\s+['"][^'"]+['"];?\s*$/gm;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
ALREADY: 'already',
|
|
20
|
-
MANUAL: 'manual',
|
|
21
|
-
};
|
|
18
|
+
/** Alias kept so existing call sites read in Vite terms; the vocabulary is shared (see patch-kind). */
|
|
19
|
+
export const VitePatchKind = PatchKind;
|
|
22
20
|
const NO_PLUGINS_REASON = "couldn't find a `plugins: [...]` array to extend";
|
|
23
21
|
function insertImport(source) {
|
|
24
22
|
const matches = [...source.matchAll(IMPORT_LINE)];
|
|
@@ -29,9 +27,19 @@ function insertImport(source) {
|
|
|
29
27
|
const end = last.index + last[0].length;
|
|
30
28
|
return `${source.slice(0, end)}\n${VITE_IMPORT}${source.slice(end)}`;
|
|
31
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Insert right after the opening `[` of the plugins array, spaced the way the surrounding line is.
|
|
32
|
+
*
|
|
33
|
+
* A multi-line array puts a newline next, and `[reticle(), \n` leaves trailing whitespace — exactly
|
|
34
|
+
* what a formatter rewrites, turning a one-line install into a diff against the user's own style. A
|
|
35
|
+
* single-line array needs the space, or the result reads `[reticle(),react()]`.
|
|
36
|
+
*/
|
|
32
37
|
function insertPlugin(source, port) {
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
return source.replace(PLUGINS_ARRAY, (match, _g, offset) => {
|
|
39
|
+
const next = source[offset + match.length] ?? '';
|
|
40
|
+
const separator = '' === next || /\s/.test(next) ? '' : ' ';
|
|
41
|
+
return `${match}${reticlePluginCall(port)},${separator}`;
|
|
42
|
+
});
|
|
35
43
|
}
|
|
36
44
|
export function patchViteConfig(source, port) {
|
|
37
45
|
if (source.includes(RETICLE_MARKER)) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which directories to look in for the app, when `reticle init` runs at a monorepo root.
|
|
3
|
+
*
|
|
4
|
+
* This used to be the literal list `['apps', 'packages']`. Measured on a real repo with three Next
|
|
5
|
+
* apps at `web/`, `admin/` and `space/`, it found none — so the redirect never fired, init ran
|
|
6
|
+
* against the root, and it reported ✓ for writing `app/reticle-dev.tsx` into a directory Next never
|
|
7
|
+
* compiles. A ⚠ tells a human to act; a ✓ tells them it is handled.
|
|
8
|
+
*
|
|
9
|
+
* A workspace DECLARES its packages, so that declaration is used first and directory names are not
|
|
10
|
+
* guessed at all. Where nothing is declared, every top-level directory is a better candidate than two
|
|
11
|
+
* hardcoded ones — filtered by the usual non-source suspects, and still subject to the `looksLikeApp`
|
|
12
|
+
* check that follows, so a wrong guess here costs a `package.json` read.
|
|
13
|
+
*/
|
|
14
|
+
interface WorkspaceSources {
|
|
15
|
+
/** Raw contents of pnpm-workspace.yaml, when present. */
|
|
16
|
+
pnpmWorkspace?: string;
|
|
17
|
+
/** The `workspaces` field of package.json — array form or the yarn/npm object form. */
|
|
18
|
+
pkgWorkspaces?: unknown;
|
|
19
|
+
/** Directory names at the repo root, used when nothing is declared. */
|
|
20
|
+
topLevelDirs?: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
export declare function workspaceParents(sources: WorkspaceSources): string[];
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which directories to look in for the app, when `reticle init` runs at a monorepo root.
|
|
3
|
+
*
|
|
4
|
+
* This used to be the literal list `['apps', 'packages']`. Measured on a real repo with three Next
|
|
5
|
+
* apps at `web/`, `admin/` and `space/`, it found none — so the redirect never fired, init ran
|
|
6
|
+
* against the root, and it reported ✓ for writing `app/reticle-dev.tsx` into a directory Next never
|
|
7
|
+
* compiles. A ⚠ tells a human to act; a ✓ tells them it is handled.
|
|
8
|
+
*
|
|
9
|
+
* A workspace DECLARES its packages, so that declaration is used first and directory names are not
|
|
10
|
+
* guessed at all. Where nothing is declared, every top-level directory is a better candidate than two
|
|
11
|
+
* hardcoded ones — filtered by the usual non-source suspects, and still subject to the `looksLikeApp`
|
|
12
|
+
* check that follows, so a wrong guess here costs a `package.json` read.
|
|
13
|
+
*/
|
|
14
|
+
/** Directories that are never a workspace package, whatever the layout. */
|
|
15
|
+
const NEVER_A_PACKAGE = new Set(['node_modules', 'dist', 'build', 'out', 'coverage', 'target']);
|
|
16
|
+
/** `packages/*` -> `packages`; `web` -> `web`. The base directory a glob searches. */
|
|
17
|
+
function globBase(pattern) {
|
|
18
|
+
const base = pattern
|
|
19
|
+
.split('/')[0]
|
|
20
|
+
?.trim()
|
|
21
|
+
.replace(/^['"]|['"]$/g, '');
|
|
22
|
+
return base === undefined || '' === base || '.' === base || base.includes('*') ? undefined : base;
|
|
23
|
+
}
|
|
24
|
+
function fromPnpm(yaml) {
|
|
25
|
+
// A deliberately small reader rather than a YAML dependency: this file is a list of globs, and the
|
|
26
|
+
// only shape that matters is `- 'pattern'` under `packages:`. A malformed file yields nothing,
|
|
27
|
+
// which falls through to the top-level scan rather than failing the install.
|
|
28
|
+
const out = [];
|
|
29
|
+
let inPackages = false;
|
|
30
|
+
for (const line of yaml.split('\n')) {
|
|
31
|
+
if (/^packages\s*:/.test(line)) {
|
|
32
|
+
inPackages = true;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (inPackages && /^\S/.test(line))
|
|
36
|
+
break;
|
|
37
|
+
const item = /^\s*-\s*(.+)$/.exec(line);
|
|
38
|
+
if (inPackages && item?.[1] !== undefined) {
|
|
39
|
+
const base = globBase(item[1]);
|
|
40
|
+
if (base !== undefined)
|
|
41
|
+
out.push(base);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
function fromPkg(workspaces) {
|
|
47
|
+
const list = Array.isArray(workspaces)
|
|
48
|
+
? workspaces
|
|
49
|
+
: 'object' === typeof workspaces && workspaces !== null
|
|
50
|
+
? workspaces.packages
|
|
51
|
+
: undefined;
|
|
52
|
+
if (!Array.isArray(list))
|
|
53
|
+
return [];
|
|
54
|
+
return list
|
|
55
|
+
.filter((p) => 'string' === typeof p)
|
|
56
|
+
.map(globBase)
|
|
57
|
+
.filter((p) => p !== undefined);
|
|
58
|
+
}
|
|
59
|
+
export function workspaceParents(sources) {
|
|
60
|
+
const declared = [
|
|
61
|
+
...(sources.pnpmWorkspace === undefined ? [] : fromPnpm(sources.pnpmWorkspace)),
|
|
62
|
+
...fromPkg(sources.pkgWorkspaces),
|
|
63
|
+
];
|
|
64
|
+
const candidates = declared.length > 0
|
|
65
|
+
? declared
|
|
66
|
+
: (sources.topLevelDirs ?? []).filter((d) => !d.startsWith('.') && !NEVER_A_PACKAGE.has(d));
|
|
67
|
+
return [...new Set(candidates)];
|
|
68
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type RedactionPolicy } from '@reticlehq/core';
|
|
2
|
+
/** Record what a session declared sensitive. Called when its hello lands. */
|
|
3
|
+
export declare function declareDrivenRedactionKeys(sessionId: string, keys: readonly string[]): void;
|
|
4
|
+
/** Drop a session's declarations when it disconnects, so a closed tab stops widening the union. */
|
|
5
|
+
export declare function forgetDrivenRedactionKeys(sessionId: string): void;
|
|
6
|
+
/** Every key declared by any connected session, sorted so the memo signature is stable. */
|
|
7
|
+
export declare function drivenRedactionKeys(): string[];
|
|
8
|
+
/**
|
|
9
|
+
* The rule in force for driven-path captures: the built-in rule plus every declared key. Identical
|
|
10
|
+
* to the built-in rule when no session declared anything, which is the overwhelmingly common case
|
|
11
|
+
* and the one that must stay byte-identical to before this feature existed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function drivenRedactionPolicy(): RedactionPolicy;
|
|
14
|
+
/** Test seam: drop every declaration. Not used in production — sessions unregister themselves. */
|
|
15
|
+
export declare function resetDrivenRedaction(): void;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { buildRedactionPolicy } from '@reticlehq/core';
|
|
2
|
+
/**
|
|
3
|
+
* The redaction rule the DRIVEN path uses — the one place in the daemon that redacts a payload the
|
|
4
|
+
* browser SDK never saw.
|
|
5
|
+
*
|
|
6
|
+
* ## Why this exists at all
|
|
7
|
+
*
|
|
8
|
+
* `connect({ redact: { keys } })` is configured in the page, and the SDK redacts everything it
|
|
9
|
+
* observes. But a driven session's request bodies and response headers are captured by the daemon
|
|
10
|
+
* straight from the network stack (`page.on('response')`), so they never pass through the SDK. An app
|
|
11
|
+
* that declared `licenceKey` sensitive would see it redacted everywhere the developer looked, and
|
|
12
|
+
* find it in cleartext in the on-disk journal — a leak on precisely the path they cannot inspect.
|
|
13
|
+
* So a session's declared keys are announced in its hello and land here.
|
|
14
|
+
*
|
|
15
|
+
* ## Why a daemon-wide UNION rather than a per-session rule
|
|
16
|
+
*
|
|
17
|
+
* A NET_DETAIL is redacted at capture time and only routed to a session afterwards, by `pageUrl`.
|
|
18
|
+
* At the moment of redaction there is no session in hand, and there is a real window in which there
|
|
19
|
+
* is no session at all yet. Resolving one first would mean either delaying redaction (holding a raw
|
|
20
|
+
* credential in memory until routing succeeds) or dropping the detail.
|
|
21
|
+
*
|
|
22
|
+
* The union sidesteps that, and is only safe because of an asymmetry that is enforced upstream:
|
|
23
|
+
* declared keys can ONLY add redaction. The worst case is that one local app's declared key
|
|
24
|
+
* over-redacts a same-named field in another local app on the same daemon — an inconvenience, in the
|
|
25
|
+
* safe direction, between two apps belonging to the same developer on the same machine. The
|
|
26
|
+
* dangerous direction is closed by construction: `allow` never crosses the bridge, so nothing a page
|
|
27
|
+
* sends can lower this floor below the built-in rule.
|
|
28
|
+
*/
|
|
29
|
+
/** Declared keys per session id, so a disconnect narrows the union back down. */
|
|
30
|
+
const declaredBySession = new Map();
|
|
31
|
+
/** Memoized policy + the key set it was built from, so the hot path does not rebuild per response. */
|
|
32
|
+
let cached;
|
|
33
|
+
function invalidate() {
|
|
34
|
+
cached = undefined;
|
|
35
|
+
}
|
|
36
|
+
/** Record what a session declared sensitive. Called when its hello lands. */
|
|
37
|
+
export function declareDrivenRedactionKeys(sessionId, keys) {
|
|
38
|
+
if (0 === keys.length) {
|
|
39
|
+
if (declaredBySession.delete(sessionId))
|
|
40
|
+
invalidate();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
declaredBySession.set(sessionId, [...keys]);
|
|
44
|
+
invalidate();
|
|
45
|
+
}
|
|
46
|
+
/** Drop a session's declarations when it disconnects, so a closed tab stops widening the union. */
|
|
47
|
+
export function forgetDrivenRedactionKeys(sessionId) {
|
|
48
|
+
if (declaredBySession.delete(sessionId))
|
|
49
|
+
invalidate();
|
|
50
|
+
}
|
|
51
|
+
/** Every key declared by any connected session, sorted so the memo signature is stable. */
|
|
52
|
+
export function drivenRedactionKeys() {
|
|
53
|
+
const all = new Set();
|
|
54
|
+
for (const keys of declaredBySession.values())
|
|
55
|
+
for (const key of keys)
|
|
56
|
+
all.add(key);
|
|
57
|
+
return [...all].sort();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The rule in force for driven-path captures: the built-in rule plus every declared key. Identical
|
|
61
|
+
* to the built-in rule when no session declared anything, which is the overwhelmingly common case
|
|
62
|
+
* and the one that must stay byte-identical to before this feature existed.
|
|
63
|
+
*/
|
|
64
|
+
export function drivenRedactionPolicy() {
|
|
65
|
+
const keys = drivenRedactionKeys();
|
|
66
|
+
const signature = keys.join('\u0000');
|
|
67
|
+
if (cached?.signature !== signature) {
|
|
68
|
+
cached = { signature, policy: buildRedactionPolicy({ keys }) };
|
|
69
|
+
}
|
|
70
|
+
return cached.policy;
|
|
71
|
+
}
|
|
72
|
+
/** Test seam: drop every declaration. Not used in production — sessions unregister themselves. */
|
|
73
|
+
export function resetDrivenRedaction() {
|
|
74
|
+
declaredBySession.clear();
|
|
75
|
+
invalidate();
|
|
76
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ReticleEvent } from '@reticlehq/core';
|
|
1
|
+
import { type RedactionPolicy, type ReticleEvent } from '@reticlehq/core';
|
|
2
2
|
/**
|
|
3
3
|
* CDP-authoritative network detail. Wherever the daemon has a CDP connection — whether it launched
|
|
4
4
|
* the browser or attached to one someone else started — `page.on('response')` sees the FULL response:
|
|
@@ -34,7 +34,13 @@ export interface NetworkDetail {
|
|
|
34
34
|
*/
|
|
35
35
|
pageUrl?: string;
|
|
36
36
|
}
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Shape a raw authoritative response into a NET_DETAIL payload (lower-cased headers).
|
|
39
|
+
*
|
|
40
|
+
* `policy` defaults to the built-in rule so this stays a PURE builder — the driven-path union is
|
|
41
|
+
* resolved by `attachNetworkDetail`, at the glue layer, where reading process state is not a lie
|
|
42
|
+
* about the function's inputs.
|
|
43
|
+
*/
|
|
38
44
|
export declare function buildNetworkDetail(raw: {
|
|
39
45
|
url: string;
|
|
40
46
|
method?: string;
|
|
@@ -43,7 +49,7 @@ export declare function buildNetworkDetail(raw: {
|
|
|
43
49
|
resourceType?: string;
|
|
44
50
|
requestBody?: string;
|
|
45
51
|
pageUrl?: string;
|
|
46
|
-
}): NetworkDetail;
|
|
52
|
+
}, policy?: RedactionPolicy): NetworkDetail;
|
|
47
53
|
/**
|
|
48
54
|
* Fold each NET_DETAIL onto the matching in-page NET_REQUEST (by method+url), enriching it with the
|
|
49
55
|
* authoritative headers/resourceType the page-side wrapper couldn't see — WITHOUT clobbering fields the
|