@reticlehq/server 3.8.2 → 3.10.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/LICENSE +201 -104
- package/NOTICE +5 -0
- package/README.md +1 -1
- package/SKILL.md +13 -12
- package/bin/launch.js +13 -0
- package/bin/package.json +3 -0
- package/bin/reticle.js +66 -8
- package/dist/chromium-launch-options.d.ts +25 -0
- package/dist/chromium-launch-options.js +21 -1
- package/dist/command/cli/adhoc-suite.js +2 -0
- package/dist/command/cli/adhoc-verdict.d.ts +2 -43
- package/dist/command/cli/adhoc-verdict.js +50 -160
- package/dist/command/cli/answers/gate-exit.d.ts +6 -2
- package/dist/command/cli/answers/gate-exit.js +12 -2
- package/dist/command/cli/answers/mcp-connection-hint.d.ts +2 -0
- package/dist/command/cli/answers/mcp-connection-hint.js +6 -0
- package/dist/command/cli/ci-hint.d.ts +44 -0
- package/dist/command/cli/ci-hint.js +72 -0
- package/dist/command/cli/cli-doctor.d.ts +6 -1
- package/dist/command/cli/cli-doctor.js +46 -5
- package/dist/command/cli/cli-flow-commands.d.ts +7 -1
- package/dist/command/cli/cli-flow-commands.js +39 -24
- package/dist/command/cli/cli-kill.d.ts +21 -0
- package/dist/command/cli/cli-kill.js +47 -4
- package/dist/command/cli/cli-parse-compare.d.ts +22 -0
- package/dist/command/cli/cli-parse-compare.js +75 -0
- package/dist/command/cli/cli-parse-grade.d.ts +36 -0
- package/dist/command/cli/cli-parse-grade.js +75 -0
- package/dist/command/cli/cli-parse-grammar.d.ts +2 -0
- package/dist/command/cli/cli-parse-grammar.js +18 -20
- package/dist/command/cli/cli-parse-verify.d.ts +10 -0
- package/dist/command/cli/cli-parse-verify.js +63 -14
- package/dist/command/cli/cli-parse.d.ts +22 -17
- package/dist/command/cli/cli-parse.js +54 -32
- package/dist/command/cli/cli-update-commands.d.ts +1 -1
- package/dist/command/cli/cli-update-commands.js +29 -20
- package/dist/command/cli/cli-usage.js +61 -31
- package/dist/command/cli/cli-verify.d.ts +17 -3
- package/dist/command/cli/cli-verify.js +59 -21
- package/dist/command/cli/cloud-cli.js +2 -119
- package/dist/command/cli/daemon-link/daemon-client.d.ts +45 -0
- package/dist/command/cli/daemon-link/daemon-client.js +205 -0
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +6 -1
- package/dist/command/cli/doctor/browser/chromium-hint.js +11 -5
- package/dist/command/cli/doctor/doctor-daemon-line.js +6 -3
- package/dist/command/cli/doctor/doctor-fix.d.ts +44 -0
- package/dist/command/cli/doctor/doctor-fix.js +89 -0
- package/dist/command/cli/doctor/doctor-launcher-line.d.ts +23 -0
- package/dist/command/cli/doctor/doctor-launcher-line.js +60 -0
- package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
- package/dist/command/cli/doctor/doctor-rows.js +4 -0
- package/dist/command/cli/doctor-repairs.d.ts +64 -0
- package/dist/command/cli/doctor-repairs.js +214 -0
- package/dist/command/cli/drive/drive-command.js +3 -7
- package/dist/command/cli/ensure-daemon.d.ts +26 -0
- package/dist/command/cli/ensure-daemon.js +57 -0
- package/dist/command/cli/finished-run/grade-command.d.ts +23 -0
- package/dist/command/cli/finished-run/grade-command.js +180 -0
- package/dist/command/cli/first-use-checks.d.ts +41 -0
- package/dist/command/cli/first-use-checks.js +127 -0
- package/dist/command/cli/first-verdict-node.d.ts +3 -0
- package/dist/command/cli/first-verdict-node.js +189 -0
- package/dist/command/cli/first-verdict.d.ts +126 -0
- package/dist/command/cli/first-verdict.js +267 -0
- package/dist/command/cli/launch/sdk-version-match.d.ts +3 -2
- package/dist/command/cli/launch/sdk-version-match.js +3 -2
- package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +1 -1
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +7 -6
- package/dist/command/cli/mcp-command.js +13 -4
- package/dist/command/cli/ports/port-holder.d.ts +11 -0
- package/dist/command/cli/ports/port-holder.js +36 -4
- package/dist/command/cli/setup-mcp-cli.d.ts +27 -1
- package/dist/command/cli/setup-mcp-cli.js +98 -23
- package/dist/command/cli/status/status-lines.d.ts +2 -0
- package/dist/command/cli/status/status-lines.js +72 -17
- package/dist/command/cli/status-fields.d.ts +5 -1
- package/dist/command/cli/status-fields.js +24 -6
- package/dist/command/cli/try-command.d.ts +7 -0
- package/dist/command/cli/try-command.js +11 -3
- package/dist/command/cli/two-builds/compare-command.d.ts +48 -0
- package/dist/command/cli/two-builds/compare-command.js +314 -0
- package/dist/command/cli/verify-against.d.ts +61 -0
- package/dist/command/cli/verify-against.js +187 -0
- package/dist/command/cli/verify-no-session.d.ts +2 -0
- package/dist/command/cli/verify-no-session.js +13 -0
- package/dist/command/cli/verify-results.d.ts +35 -2
- package/dist/command/cli/verify-results.js +76 -10
- package/dist/command/cli.js +112 -70
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +3 -1
- package/dist/command/daemon/binding/daemon-status-probe.js +4 -2
- package/dist/command/daemon/daemon-argv.d.ts +9 -0
- package/dist/command/daemon/daemon-argv.js +11 -0
- package/dist/command/daemon/daemon-resolve.d.ts +8 -1
- package/dist/command/daemon/daemon-resolve.js +27 -1
- package/dist/command/daemon/daemon.d.ts +22 -4
- package/dist/command/daemon/daemon.js +52 -10
- package/dist/command/daemon/lifetime/daemon-exit.d.ts +24 -0
- package/dist/command/daemon/lifetime/daemon-exit.js +39 -0
- package/dist/command/daemon/lifetime/death-cause.d.ts +15 -0
- package/dist/command/daemon/lifetime/death-cause.js +149 -0
- package/dist/command/daemon/lifetime/heartbeat.d.ts +7 -15
- package/dist/command/daemon/lifetime/heartbeat.js +11 -15
- package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
- package/dist/command/daemon/lifetime/startup-failure.js +62 -0
- package/dist/command/daemon/port-state.d.ts +14 -0
- package/dist/command/daemon/port-state.js +31 -0
- package/dist/command/setup/agent-configs.d.ts +2 -0
- package/dist/command/setup/agent-configs.js +42 -15
- package/dist/command/setup/agent-next-steps.d.ts +77 -0
- package/dist/command/setup/agent-next-steps.js +284 -0
- package/dist/command/setup/agent-writer.js +11 -6
- package/dist/command/setup/bringup/dev-server-files.d.ts +9 -0
- package/dist/command/setup/bringup/dev-server-files.js +17 -0
- package/dist/command/setup/bringup/static-page-server.d.ts +14 -2
- package/dist/command/setup/bringup/static-page-server.js +43 -9
- package/dist/command/setup/init/init-host.js +2 -0
- package/dist/command/setup/init/init-runtime.d.ts +6 -1
- package/dist/command/setup/init/init-runtime.js +63 -21
- package/dist/command/setup/mcp-launcher.d.ts +3 -0
- package/dist/command/setup/mcp-launcher.js +11 -0
- package/dist/command/setup/node-effects.d.ts +3 -2
- package/dist/command/setup/node-effects.js +9 -3
- package/dist/command/setup/remaining-steps.d.ts +8 -0
- package/dist/command/setup/remaining-steps.js +10 -0
- package/dist/command/setup/run-setup.d.ts +2 -0
- package/dist/command/setup/run-setup.js +9 -1
- package/dist/command/setup/setup-command.js +36 -15
- package/dist/command/setup/setup-mcp.d.ts +8 -0
- package/dist/command/setup/setup-mcp.js +47 -20
- package/dist/command/setup/start-dev-server.d.ts +8 -0
- package/dist/command/setup/start-dev-server.js +43 -0
- package/dist/command/update/updater.js +6 -0
- package/dist/command/version/identity/server-version.d.ts +2 -0
- package/dist/command/version/identity/server-version.js +2 -0
- package/dist/command/version/version-skew.d.ts +7 -0
- package/dist/command/version/version-skew.js +14 -3
- package/dist/features/compare/action-plan.d.ts +260 -0
- package/dist/features/compare/action-plan.js +543 -0
- package/dist/features/compare/behaviour-compare.d.ts +24 -0
- package/dist/features/compare/behaviour-compare.js +85 -0
- package/dist/features/compare/behaviour-diff.d.ts +97 -0
- package/dist/features/compare/behaviour-diff.js +202 -0
- package/dist/features/compare/behaviour-report.d.ts +40 -0
- package/dist/features/compare/behaviour-report.js +128 -0
- package/dist/features/compare/build-readings.d.ts +106 -0
- package/dist/features/compare/build-readings.js +250 -0
- package/dist/features/compare/compare-diff.d.ts +62 -0
- package/dist/features/compare/compare-diff.js +177 -0
- package/dist/features/compare/compare-report-html.d.ts +6 -0
- package/dist/features/compare/compare-report-html.js +102 -0
- package/dist/features/compare/compare-report.d.ts +15 -0
- package/dist/features/compare/compare-report.js +73 -0
- package/dist/features/compare/compare-run.d.ts +60 -0
- package/dist/features/compare/compare-run.js +123 -0
- package/dist/features/compare/outside/effect-lines.d.ts +26 -0
- package/dist/features/compare/outside/effect-lines.js +103 -0
- package/dist/features/compare/outside/state-sources.d.ts +76 -0
- package/dist/features/compare/outside/state-sources.js +164 -0
- package/dist/features/compare/outside/value-dictionary.d.ts +51 -0
- package/dist/features/compare/outside/value-dictionary.js +227 -0
- package/dist/features/compare/visible-content.d.ts +20 -0
- package/dist/features/compare/visible-content.js +125 -0
- package/dist/features/crawl/crawl-empty.js +2 -1
- package/dist/features/crawl/crawl-exhaustive.d.ts +55 -0
- package/dist/features/crawl/crawl-exhaustive.js +138 -0
- package/dist/features/crawl/crawl-tools.d.ts +9 -2
- package/dist/features/crawl/crawl-tools.js +28 -12
- package/dist/features/crawl/crawl.d.ts +8 -9
- package/dist/features/crawl/crawl.js +61 -65
- package/dist/features/exhaust/app-coverage.d.ts +5 -1
- package/dist/features/exhaust/app-coverage.js +3 -1
- package/dist/features/exhaust/code-coverage.js +8 -4
- package/dist/features/exhaust/edge-cases.d.ts +133 -0
- package/dist/features/exhaust/edge-cases.js +310 -0
- package/dist/features/exhaust/edges/edge-drive.d.ts +42 -0
- package/dist/features/exhaust/edges/edge-drive.js +364 -0
- package/dist/features/exhaust/edges/edge-run.d.ts +59 -0
- package/dist/features/exhaust/edges/edge-run.js +169 -0
- package/dist/features/exhaust/explorer.d.ts +87 -14
- package/dist/features/exhaust/explorer.js +159 -25
- package/dist/features/exhaust/failure-branches.d.ts +5 -0
- package/dist/features/exhaust/failure-branches.js +29 -11
- package/dist/features/exhaust/invariants.d.ts +112 -0
- package/dist/features/exhaust/invariants.js +188 -0
- package/dist/features/exhaust/ledger.d.ts +68 -0
- package/dist/features/exhaust/ledger.js +121 -13
- package/dist/features/exhaust/session-fold.d.ts +10 -0
- package/dist/features/exhaust/session-fold.js +46 -3
- package/dist/features/exhaust/session-port.d.ts +3 -1
- package/dist/features/exhaust/session-port.js +97 -28
- package/dist/features/grade/grade-answer.d.ts +99 -0
- package/dist/features/grade/grade-answer.js +112 -0
- package/dist/features/grade/grade-noise.d.ts +27 -0
- package/dist/features/grade/grade-noise.js +55 -0
- package/dist/features/grade/grade-renumber.d.ts +12 -0
- package/dist/features/grade/grade-renumber.js +86 -0
- package/dist/features/grade/grade-state.d.ts +50 -0
- package/dist/features/grade/grade-state.js +93 -0
- package/dist/features/grade/grade-suggest.d.ts +33 -0
- package/dist/features/grade/grade-suggest.js +98 -0
- package/dist/features/license/license.d.ts +11 -71
- package/dist/features/license/license.js +43 -136
- package/dist/features/visual/desktop-capture.d.ts +33 -0
- package/dist/features/visual/desktop-capture.js +94 -0
- package/dist/features/visual/step-shots.js +139 -35
- package/dist/features/visual/visual-tools.d.ts +0 -18
- package/dist/features/visual/visual-tools.js +67 -99
- package/dist/import-graph.js +5 -6
- package/dist/index.d.ts +3 -1
- package/dist/index.js +19 -7
- package/dist/judgement/capsule/causal-summary.js +9 -2
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +13 -1
- package/dist/judgement/runs/artifact/replay-mapping.js +1 -1
- package/dist/judgement/runs/reticle-runner.js +16 -2
- package/dist/judgement/runs/run-ledger.d.ts +7 -5
- package/dist/judgement/runs/run-ledger.js +98 -69
- package/dist/judgement/runs/runner-port.d.ts +2 -0
- package/dist/judgement/runs/runner-port.js +23 -5
- package/dist/judgement/runs/verification-sync.d.ts +6 -3
- package/dist/judgement/runs/verification-sync.js +21 -10
- package/dist/language/flows/affected-tools.d.ts +1 -1
- package/dist/language/flows/affected-tools.js +49 -30
- package/dist/language/flows/change/git-changed.d.ts +2 -1
- package/dist/language/flows/change/git-changed.js +19 -2
- package/dist/language/flows/fields/flow-secret-field.d.ts +1 -13
- package/dist/language/flows/fields/flow-secret-field.js +77 -21
- package/dist/language/flows/flow-anchor.d.ts +60 -2
- package/dist/language/flows/flow-anchor.js +125 -20
- package/dist/language/flows/flow-classify.d.ts +20 -0
- package/dist/language/flows/flow-classify.js +90 -12
- package/dist/language/flows/flow-cross-step.d.ts +7 -1
- package/dist/language/flows/flow-cross-step.js +25 -1
- package/dist/language/flows/flow-journey.d.ts +38 -4
- package/dist/language/flows/flow-journey.js +138 -8
- package/dist/language/flows/flow-replay-run.d.ts +0 -16
- package/dist/language/flows/flow-replay-run.js +76 -14
- package/dist/language/flows/flow-replay-types.d.ts +4 -2
- package/dist/language/flows/flow-replay.d.ts +19 -1
- package/dist/language/flows/flow-replay.js +169 -52
- package/dist/language/flows/flow-replayable.js +7 -1
- package/dist/language/flows/flow-start-path.d.ts +34 -5
- package/dist/language/flows/flow-start-path.js +202 -15
- package/dist/language/flows/flow-step-runners.d.ts +14 -13
- package/dist/language/flows/flow-step-runners.js +184 -109
- package/dist/language/flows/flow-store-for-session.d.ts +26 -25
- package/dist/language/flows/flow-store-for-session.js +51 -5
- package/dist/language/flows/flow-tools.js +46 -33
- package/dist/language/flows/flows.js +176 -76
- package/dist/language/flows/recording/tape/recordings.d.ts +21 -16
- package/dist/language/flows/recording/tape/recordings.js +112 -49
- package/dist/language/flows/replay.d.ts +36 -12
- package/dist/language/flows/replay.js +193 -50
- package/dist/language/flows/steps/page-steps.d.ts +68 -0
- package/dist/language/flows/steps/page-steps.js +198 -0
- package/dist/language/flows/suite/sequential-run.d.ts +68 -0
- package/dist/language/flows/suite/sequential-run.js +96 -0
- package/dist/language/flows/verify-change-tools.js +26 -15
- package/dist/launch-chromium.d.ts +1 -0
- package/dist/launch-chromium.js +49 -12
- package/dist/memory/cloud/background-sync.d.ts +13 -0
- package/dist/memory/cloud/background-sync.js +24 -3
- package/dist/memory/cloud/sync-daemon.js +26 -27
- package/dist/memory/cloud/sync-screenshots.js +6 -1
- package/dist/memory/journal/attach-journal.js +2 -1
- package/dist/memory/journal/drive-flow.d.ts +7 -0
- package/dist/memory/journal/drive-flow.js +44 -13
- package/dist/memory/journal/journal-recorder.d.ts +4 -2
- package/dist/memory/journal/session-journal.d.ts +7 -0
- package/dist/memory/journal/session-journal.js +58 -10
- package/dist/memory/project/root-claims.d.ts +9 -0
- package/dist/memory/project/root-claims.js +9 -4
- package/dist/portal/bridge/bridge.d.ts +13 -4
- package/dist/portal/bridge/bridge.js +10 -9
- package/dist/portal/input/download-capture.d.ts +7 -0
- package/dist/portal/input/download-capture.js +92 -0
- package/dist/portal/input/network-detail.d.ts +6 -0
- package/dist/portal/input/network-detail.js +12 -1
- package/dist/portal/input/network-mock-tools.js +49 -13
- package/dist/portal/input/network-mock.d.ts +8 -0
- package/dist/portal/input/network-mock.js +30 -10
- package/dist/portal/input/page-capture.d.ts +24 -0
- package/dist/portal/input/page-capture.js +33 -0
- package/dist/portal/input/real-input.d.ts +35 -18
- package/dist/portal/input/real-input.js +99 -33
- package/dist/portal/input/viewport-tools.d.ts +8 -0
- package/dist/portal/input/viewport-tools.js +81 -21
- package/dist/portal/pool/browser-pool.d.ts +16 -30
- package/dist/portal/pool/browser-pool.js +64 -52
- package/dist/portal/pool/context-permissions.d.ts +34 -17
- package/dist/portal/pool/context-permissions.js +94 -1
- package/dist/portal/pool/csp-widen.d.ts +62 -0
- package/dist/portal/pool/csp-widen.js +132 -0
- package/dist/portal/pool/determinism.d.ts +52 -0
- package/dist/portal/pool/determinism.js +259 -0
- package/dist/portal/pool/lease-emulation.d.ts +34 -1
- package/dist/portal/pool/lease-emulation.js +18 -0
- package/dist/portal/pool/lease-marker-stamp.d.ts +6 -0
- package/dist/portal/pool/lease-marker-stamp.js +15 -1
- package/dist/portal/pool/lease-page-error.d.ts +56 -0
- package/dist/portal/pool/lease-page-error.js +160 -0
- package/dist/portal/pool/page-error-journal.d.ts +63 -0
- package/dist/portal/pool/page-error-journal.js +208 -0
- package/dist/portal/pool/playwright-launcher.js +71 -15
- package/dist/portal/pool/pool-contract.d.ts +49 -1
- package/dist/portal/pool/pool-lease.d.ts +70 -2
- package/dist/portal/pool/pool-lease.js +23 -0
- package/dist/portal/pool/zero-install.d.ts +8 -0
- package/dist/portal/pool/zero-install.js +91 -1
- package/dist/portal/session/dev-server/dev-command.d.ts +11 -2
- package/dist/portal/session/dev-server/dev-command.js +110 -16
- package/dist/portal/session/dev-server/dev-server-probe.d.ts +7 -0
- package/dist/portal/session/dev-server/dev-server-probe.js +9 -0
- package/dist/portal/session/dev-server/project-ports.d.ts +21 -0
- package/dist/portal/session/dev-server/project-ports.js +62 -0
- package/dist/portal/session/facts/departed-network.d.ts +66 -0
- package/dist/portal/session/facts/departed-network.js +142 -0
- package/dist/portal/session/no-session-diagnosis.d.ts +9 -0
- package/dist/portal/session/no-session-diagnosis.js +76 -27
- package/dist/portal/session/no-session-next-action.d.ts +14 -0
- package/dist/portal/session/no-session-next-action.js +56 -7
- package/dist/portal/session/no-session-watch.d.ts +6 -0
- package/dist/portal/session/no-session-watch.js +41 -4
- package/dist/portal/session/page-commands/undeclared-command.d.ts +9 -3
- package/dist/portal/session/page-commands/undeclared-command.js +16 -7
- package/dist/portal/session/recorded-gaps.d.ts +1 -1
- package/dist/portal/session/recorded-gaps.js +5 -2
- package/dist/portal/session/serving-directory.d.ts +10 -0
- package/dist/portal/session/serving-directory.js +39 -8
- package/dist/portal/session/session-manager.d.ts +10 -0
- package/dist/portal/session/session-manager.js +211 -81
- package/dist/portal/session/session.js +4 -3
- package/dist/portal/session/zero-install-first-call.d.ts +20 -1
- package/dist/portal/session/zero-install-first-call.js +34 -2
- package/dist/start-wiring.d.ts +30 -2
- package/dist/start-wiring.js +74 -9
- package/dist/surface/mcp/mcp-proxy.js +10 -2
- package/dist/surface/mcp/mcp.js +8 -5
- package/dist/surface/mcp/peer/agents-connected.d.ts +6 -0
- package/dist/surface/mcp/peer/agents-connected.js +58 -0
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +17 -5
- package/dist/surface/mcp/shared-params.d.ts +2 -0
- package/dist/surface/mcp/shared-params.js +31 -0
- package/dist/surface/tools/act/act-observation.d.ts +40 -2
- package/dist/surface/tools/act/act-observation.js +118 -12
- package/dist/surface/tools/act/act-preflight.js +38 -18
- package/dist/surface/tools/act/act-sequence-retry.d.ts +23 -2
- package/dist/surface/tools/act/act-sequence-retry.js +61 -5
- package/dist/surface/tools/act/act-target.js +3 -1
- package/dist/surface/tools/act/after-match.d.ts +10 -0
- package/dist/surface/tools/act/after-match.js +40 -0
- package/dist/surface/tools/act/already-true.js +1 -1
- package/dist/surface/tools/act/navigation/reload-result.d.ts +12 -1
- package/dist/surface/tools/act/navigation/reload-result.js +25 -2
- package/dist/surface/tools/act/resolve-target.d.ts +17 -1
- package/dist/surface/tools/act/resolve-target.js +59 -9
- package/dist/surface/tools/act/sequence-grade.d.ts +15 -1
- package/dist/surface/tools/act/sequence-grade.js +45 -27
- package/dist/surface/tools/act/settle-in-flight.js +27 -15
- package/dist/surface/tools/act-command.d.ts +23 -0
- package/dist/surface/tools/act-command.js +50 -0
- package/dist/surface/tools/act-sequence-tool.js +81 -50
- package/dist/surface/tools/act-tools.d.ts +1 -13
- package/dist/surface/tools/act-tools.js +83 -95
- package/dist/surface/tools/agent/build-staleness.d.ts +1 -1
- package/dist/surface/tools/agent/build-staleness.js +1 -1
- package/dist/surface/tools/assert/assert-verdict.js +18 -12
- package/dist/surface/tools/browser-tools.js +87 -13
- package/dist/surface/tools/compare-tools.d.ts +11 -0
- package/dist/surface/tools/compare-tools.js +94 -0
- package/dist/surface/tools/coverage-tools.js +1 -1
- package/dist/surface/tools/dynamic-tools.js +11 -3
- package/dist/surface/tools/empty-session-list.d.ts +2 -0
- package/dist/surface/tools/empty-session-list.js +4 -2
- package/dist/surface/tools/error-recovery.d.ts +12 -1
- package/dist/surface/tools/error-recovery.js +131 -40
- package/dist/surface/tools/explore-tools.js +1 -1
- package/dist/surface/tools/invoke-tool.js +29 -21
- package/dist/surface/tools/lease-coverage-tools.d.ts +49 -0
- package/dist/surface/tools/lease-coverage-tools.js +128 -0
- package/dist/surface/tools/lease-hint.d.ts +42 -1
- package/dist/surface/tools/lease-hint.js +52 -3
- package/dist/surface/tools/lease-readiness.d.ts +47 -0
- package/dist/surface/tools/lease-readiness.js +126 -0
- package/dist/surface/tools/lease-seed.d.ts +16 -1
- package/dist/surface/tools/lease-seed.js +37 -4
- package/dist/surface/tools/lease-tools.d.ts +3 -41
- package/dist/surface/tools/lease-tools.js +104 -124
- package/dist/surface/tools/lease-url-identity.d.ts +46 -0
- package/dist/surface/tools/lease-url-identity.js +97 -0
- package/dist/surface/tools/merge-tools.js +99 -2
- package/dist/surface/tools/merged-name-redirect.js +5 -1
- package/dist/surface/tools/observe-tools.js +15 -9
- package/dist/surface/tools/read/console-pre-attach.d.ts +19 -0
- package/dist/surface/tools/read/console-pre-attach.js +23 -0
- package/dist/surface/tools/read-tools.js +33 -30
- package/dist/surface/tools/real-input-attempt.d.ts +0 -8
- package/dist/surface/tools/real-input-attempt.js +143 -28
- package/dist/surface/tools/repeat-hint.d.ts +40 -0
- package/dist/surface/tools/repeat-hint.js +171 -0
- package/dist/surface/tools/run-report.d.ts +2 -2
- package/dist/surface/tools/run-report.js +27 -15
- package/dist/surface/tools/session-ports.d.ts +14 -0
- package/dist/surface/tools/session-ports.js +53 -0
- package/dist/surface/tools/tool-kit.d.ts +18 -0
- package/dist/surface/tools/tool-kit.js +9 -3
- package/dist/surface/tools/tool-surface.js +0 -1
- package/dist/surface/tools/tools.js +54 -18
- package/dist/surface/tools/type-spelling.d.ts +13 -0
- package/dist/surface/tools/type-spelling.js +36 -0
- package/dist/telemetry/agent-client.d.ts +6 -0
- package/dist/telemetry/agent-client.js +10 -0
- package/dist/telemetry/argument-shape.js +17 -7
- package/dist/telemetry/cli-telemetry.d.ts +10 -1
- package/dist/telemetry/cli-telemetry.js +15 -0
- package/dist/telemetry/init-telemetry.d.ts +2 -1
- package/dist/telemetry/init-telemetry.js +47 -2
- package/dist/telemetry/install-trace.js +23 -4
- package/dist/telemetry/onboarding-funnel.d.ts +19 -2
- package/dist/telemetry/onboarding-funnel.js +25 -3
- package/dist/telemetry/proxy-telemetry.d.ts +2 -1
- package/dist/telemetry/proxy-telemetry.js +2 -2
- package/dist/telemetry/session-metrics.d.ts +1 -1
- package/dist/telemetry/session-metrics.js +67 -27
- package/dist/telemetry/telemetry.d.ts +3 -1
- package/dist/telemetry/telemetry.js +60 -28
- package/dist/telemetry/tool-refused.d.ts +6 -3
- package/dist/telemetry/tool-refused.js +15 -3
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +1 -1
- package/docs/architecture.md +3 -6
- package/docs/capabilities.mdx +61 -1
- package/docs/cli/affected.mdx +9 -7
- package/docs/cli/cloud.mdx +3 -3
- package/docs/cli/compare.mdx +94 -0
- package/docs/cli/doctor.mdx +41 -2
- package/docs/cli/grade.mdx +97 -0
- package/docs/cli/help.mdx +4 -2
- package/docs/cli/license.mdx +13 -12
- package/docs/cli/restart.mdx +7 -5
- package/docs/cli/rollback.mdx +5 -5
- package/docs/cli/setup.mdx +13 -2
- package/docs/cli/status.mdx +4 -0
- package/docs/cli/stop.mdx +3 -3
- package/docs/cli/update.mdx +2 -2
- package/docs/cli/verify.mdx +23 -1
- package/docs/cli.mdx +34 -24
- package/docs/deploy-checks.md +25 -0
- package/docs/desktop.mdx +1 -1
- package/docs/enterprise.md +17 -22
- package/docs/environment.mdx +1 -0
- package/docs/first-drive.md +1 -1
- package/docs/flows.md +11 -1
- package/docs/frameworks.mdx +9 -9
- package/docs/getting-started.md +1 -1
- package/docs/install-manual.mdx +16 -1
- package/docs/license-activation.mdx +3 -3
- package/docs/packages/engine.mdx +1 -1
- package/docs/packages/server.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/test.mdx +1 -1
- package/docs/packages.mdx +1 -1
- package/docs/platform-integration.md +1 -1
- package/docs/predicates.mdx +1 -1
- package/docs/quickstart.mdx +68 -49
- package/docs/skill-setup.md +1 -1
- package/docs/skill.md +14 -13
- package/docs/telemetry.md +6 -4
- package/docs/tools/navigate.mdx +1 -1
- package/docs/tools/verify.mdx +4 -1
- package/docs/troubleshooting.mdx +1 -1
- package/docs/usage.md +17 -14
- package/package.json +18 -14
- package/LICENSE-ENTERPRISE +0 -37
- package/dist/features/ee/audit-log.d.ts +0 -14
- package/dist/features/ee/audit-log.js +0 -11
- package/dist/features/exhaust/exhaust-run.d.ts +0 -33
- package/dist/features/exhaust/exhaust-run.js +0 -82
|
@@ -6,26 +6,92 @@
|
|
|
6
6
|
* never rounded up to `yes`, and the run is `yes` only when every journey is.
|
|
7
7
|
*/
|
|
8
8
|
import { RunFlowStatus, VerdictStatus, Verified, } from '@reticlehq/core';
|
|
9
|
+
/**
|
|
10
|
+
* The shape of the `--results-json` file. 1 had only `verdict` and `results`; 2 adds `steps` per
|
|
11
|
+
* journey and the gated `score`. A consumer checks this before it reads a field that 1 did not have.
|
|
12
|
+
*/
|
|
13
|
+
export const RESULTS_SCHEMA_VERSION = 2;
|
|
14
|
+
/**
|
|
15
|
+
* One journey's steps. `planned` is the flow file's step count, when the caller could read it:
|
|
16
|
+
* without it, a replay that stopped at step 2 of 10 would look like a 2-step journey. Replay adds a
|
|
17
|
+
* `success` step for a flow with a success oracle, so the total is the larger of the two.
|
|
18
|
+
*/
|
|
19
|
+
function stepCounts(flow, planned) {
|
|
20
|
+
let passed = 0;
|
|
21
|
+
let failed = 0;
|
|
22
|
+
let judgedUnits = 0;
|
|
23
|
+
let swept;
|
|
24
|
+
for (const step of (flow.stepResults ?? [])) {
|
|
25
|
+
// A sequence counts its sub-steps; any other step counts once.
|
|
26
|
+
const units = step.subSteps?.total ?? 1;
|
|
27
|
+
judgedUnits += units;
|
|
28
|
+
const ok = step.ok && step.drift === undefined;
|
|
29
|
+
// Gated: once a step failed, later verdicts are reported apart and never paid.
|
|
30
|
+
if (swept !== undefined) {
|
|
31
|
+
if (ok) {
|
|
32
|
+
swept.passed += units;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
swept.passed += step.subSteps?.passed ?? 0;
|
|
36
|
+
swept.failed += 1;
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (ok) {
|
|
41
|
+
passed += units;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
passed += step.subSteps?.passed ?? 0;
|
|
45
|
+
failed += 1;
|
|
46
|
+
swept = { passed: 0, failed: 0 };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const total = Math.max(planned ?? flow.steps, judgedUnits);
|
|
50
|
+
const counts = { passed, failed, unknown: total - passed - failed };
|
|
51
|
+
if (swept !== undefined && swept.passed + swept.failed > 0) {
|
|
52
|
+
counts.swept = swept;
|
|
53
|
+
}
|
|
54
|
+
return counts;
|
|
55
|
+
}
|
|
9
56
|
function journeyVerdict(status) {
|
|
10
|
-
if (RunFlowStatus.FAIL === status)
|
|
57
|
+
if (RunFlowStatus.FAIL === status) {
|
|
11
58
|
return Verified.NO;
|
|
59
|
+
}
|
|
12
60
|
// A heal is consequence-verified before it is reported, so it proves as much as a pass.
|
|
13
|
-
if (RunFlowStatus.PASS === status || RunFlowStatus.HEALED === status)
|
|
61
|
+
if (RunFlowStatus.PASS === status || RunFlowStatus.HEALED === status) {
|
|
14
62
|
return Verified.YES;
|
|
63
|
+
}
|
|
15
64
|
return Verified.UNKNOWN;
|
|
16
65
|
}
|
|
17
|
-
/**
|
|
18
|
-
|
|
66
|
+
/**
|
|
67
|
+
* `pushed`: the run reached the platform, so its id resolves there and the results name it.
|
|
68
|
+
* `planned`: each flow file's step count, by journey name (see `stepCounts`).
|
|
69
|
+
*/
|
|
70
|
+
export function verifyResults(run, pushed = false, planned = new Map()) {
|
|
19
71
|
const results = run.flows.map((flow) => ({
|
|
20
72
|
journey: flow.name,
|
|
21
73
|
verdict: journeyVerdict(flow.status),
|
|
22
74
|
...(flow.failureReason === undefined ? {} : { reason: flow.failureReason }),
|
|
23
75
|
...(pushed ? { runId: run.runId } : {}),
|
|
76
|
+
steps: stepCounts(flow, planned.get(flow.name)),
|
|
24
77
|
}));
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
78
|
+
const sum = (key) => results.reduce((n, r) => n + r.steps[key], 0);
|
|
79
|
+
const passed = sum('passed');
|
|
80
|
+
const total = passed + sum('failed') + sum('unknown');
|
|
81
|
+
const score = {
|
|
82
|
+
passed,
|
|
83
|
+
failed: sum('failed'),
|
|
84
|
+
unknown: sum('unknown'),
|
|
85
|
+
total,
|
|
86
|
+
...(0 === total ? {} : { value: passed / total }),
|
|
87
|
+
};
|
|
88
|
+
let verdict = Verified.UNKNOWN;
|
|
89
|
+
if (results.some((r) => Verified.NO === r.verdict)) {
|
|
90
|
+
verdict = Verified.NO;
|
|
91
|
+
}
|
|
92
|
+
else if (VerdictStatus.PASS === run.verdict.status &&
|
|
93
|
+
results.every((r) => Verified.YES === r.verdict)) {
|
|
94
|
+
verdict = Verified.YES;
|
|
95
|
+
}
|
|
96
|
+
return { schemaVersion: RESULTS_SCHEMA_VERSION, verdict, results, score };
|
|
31
97
|
}
|
package/dist/command/cli.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { PlatformLinkVia } from '@reticlehq/core/telemetry';
|
|
2
|
+
import { OnboardingPhase, PlatformLinkVia } from '@reticlehq/core/telemetry';
|
|
3
3
|
import { hudSignIn } from './cli/cloud-login.js';
|
|
4
|
-
import { handleSetupMcp, handleSetupInstall } from './cli/setup-mcp-cli.js';
|
|
4
|
+
import { agentsBlock, handleSetupMcp, handleSetupInstall, handleSetupWaitAgents, readAgentRows, shouldWaitForAgents, } from './cli/setup-mcp-cli.js';
|
|
5
5
|
import { reportStepFromCli } from './cli-onboarding.js';
|
|
6
6
|
import { pathToFileURL } from 'node:url';
|
|
7
7
|
import { openFailureNote, openLaunchFailureNote } from './cli/answers/open-note.js';
|
|
8
|
+
import { writeCiHint } from './cli/ci-hint.js';
|
|
9
|
+
import { currentAutomationHint, isCiEnv } from '../telemetry/automation-hint.js';
|
|
8
10
|
import { realpathSync } from 'node:fs';
|
|
9
11
|
import { join } from 'node:path';
|
|
10
12
|
import { installCrashGuard } from './setup/setup-command.js';
|
|
11
13
|
import { handleStatus } from './cli/status-fields.js';
|
|
12
14
|
export { handleStatus, reportStatus } from './cli/status-fields.js';
|
|
13
15
|
import { reticleStateHome } from './daemon/daemon.js';
|
|
16
|
+
import { ensureDaemon } from './cli/ensure-daemon.js';
|
|
14
17
|
import { handleMcp } from './cli/mcp-command.js';
|
|
15
18
|
import { handleReport } from './cli/report-command.js';
|
|
16
19
|
import { handleContext } from './cli/context-command.js';
|
|
@@ -18,10 +21,11 @@ import { handleHook } from './cli/hook/hook-command.js';
|
|
|
18
21
|
import { HOOK_COMMAND } from './cli/cli-parse.js';
|
|
19
22
|
import { daemonProjectAt, readDaemonRegistry, resolveDaemonForProject, } from './daemon/daemon-resolve.js';
|
|
20
23
|
import { pickDaemonPortToBind } from './daemon/binding/free-port.js';
|
|
24
|
+
import { exitAfterClose } from './daemon/lifetime/daemon-exit.js';
|
|
21
25
|
import { isSameOrAbove, portForInit, portFromEnv } from './setup/init/init-port.js';
|
|
22
26
|
import { daemonStartOptions } from './cli/daemon-start-options.js';
|
|
23
27
|
import { handleGate, loadNamedFlows, resolveChangedFiles } from './cli/cli-flow-commands.js';
|
|
24
|
-
import { RETICLE_DEFAULT_PORT, ReticleDir, ReticleEnv } from '@reticlehq/core';
|
|
28
|
+
import { DaemonStartupError, RETICLE_DEFAULT_PORT, ReticleDir, ReticleEnv } from '@reticlehq/core';
|
|
25
29
|
import { loadDotEnv } from '../telemetry/dev-repo.js';
|
|
26
30
|
import { licenseKeyFromEnvFiles } from '../features/license/license-env.js';
|
|
27
31
|
import { LICENSE_KEY_ENV } from '../features/license/license.js';
|
|
@@ -33,34 +37,41 @@ import { startDaemon } from '../index.js';
|
|
|
33
37
|
import { cmdLink, connectInstalls, isCloudCommand, runCloudCommand } from './cli/cloud-cli.js';
|
|
34
38
|
import { SERVER_VERSION } from './version/identity/server-version.js';
|
|
35
39
|
import { log } from '../log.js';
|
|
36
|
-
import { isAlive, removePid,
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
40
|
+
import { isAlive, removePid, discoverDaemonPort, writeDaemonRegistry } from './daemon/daemon.js';
|
|
41
|
+
import { probePresence, presenceIsUsable } from './daemon/binding/port-presence.js';
|
|
42
|
+
import { probeDaemon } from '../surface/mcp/mcp-proxy.js';
|
|
39
43
|
import { installDaemonResilience, recordExitReason, DaemonExitReason, } from './daemon/daemon-resilience.js';
|
|
40
44
|
import { IdleShutdown, resolveIdleShutdownMs, resolveIdleCheckMs } from './daemon/idle-shutdown.js';
|
|
41
45
|
import { DaemonHeartbeat, resolveHeartbeatMs } from './daemon/lifetime/heartbeat.js';
|
|
42
46
|
import { everServedToolCall } from './daemon/lifetime/daemon-usefulness.js';
|
|
43
|
-
import {
|
|
44
|
-
import { summarizeStatus,
|
|
47
|
+
import { installStartupTrap, recordStartupFailure } from './daemon/lifetime/startup-failure.js';
|
|
48
|
+
import { summarizeStatus, decideOpen, openInBrowser, openCommand, } from './cli/launch/cli-launch.js';
|
|
45
49
|
import { NAVIGATE_NO_SESSION_ID, navigateLeftTab, openCandidates } from './cli/open-navigate.js';
|
|
46
50
|
import { fetchStatus } from './daemon/binding/daemon-status-probe.js';
|
|
47
51
|
import { handleDrive } from './cli/drive/drive-command.js';
|
|
48
52
|
import { handleVerify } from './cli/cli-verify.js';
|
|
53
|
+
import { handleVerifyAgainst } from './cli/verify-against.js';
|
|
54
|
+
import { handleCompare } from './cli/two-builds/compare-command.js';
|
|
55
|
+
import { handleGrade } from './cli/finished-run/grade-command.js';
|
|
56
|
+
import { FIRST_VERDICT_REASON, bareRunsFirstVerdict } from './cli/first-verdict.js';
|
|
57
|
+
import { cmdFirstVerdict } from './cli/first-verdict-node.js';
|
|
58
|
+
import { reportOnboardingStop } from '../telemetry/onboarding-funnel.js';
|
|
49
59
|
import { runKill } from './cli/cli-kill.js';
|
|
50
60
|
import { runInit, buildNodeIo, InitFailure } from '@reticlehq/init';
|
|
51
61
|
import { continueAfterInit } from './setup/init/init-runtime.js';
|
|
52
62
|
import { initFirstFlow } from './cli/try-command.js';
|
|
53
63
|
import { handleDoctor } from './cli/cli-doctor.js';
|
|
64
|
+
import { writeFirstUseWarnings } from './cli/first-use-checks.js';
|
|
54
65
|
import { serverInitHost } from './setup/init/init-host.js';
|
|
55
66
|
import { describeLicense } from '../features/license/license.js';
|
|
56
67
|
import { isLikelyDevServerPort, devServerPortWarning, readProjectPort, readProjectId, workspacePortConflict, projectDirOf, } from './cli/ports/resolve/cli-port.js';
|
|
57
68
|
import { helpRunsAt, helpVersionNote, nodeSpawner, readTextFile, reexecAtVersion, versionMatchNote, versionToMatch, } from './cli/launch/sdk-version-match.js';
|
|
58
|
-
import {
|
|
69
|
+
import { PORT_FLAG, parseCliArgs, CLI_USAGE, dialsTheDaemon } from './cli/cli-parse.js';
|
|
59
70
|
import { renderHelp } from './cli/cli-usage.js';
|
|
60
71
|
import { RENAMED_COMMANDS, renamedCommandFor, renamedNote } from './cli/cli-parse-grammar.js';
|
|
61
72
|
import { handleFeedback, handleTelemetry } from '../telemetry/feedback-cli.js';
|
|
62
73
|
import { installDaemonTelemetry } from '../telemetry/daemon-telemetry.js';
|
|
63
|
-
import { reportCliRun } from '../telemetry/cli-telemetry.js';
|
|
74
|
+
import { reportCliRun, reportGateCompleted } from '../telemetry/cli-telemetry.js';
|
|
64
75
|
// Re-exported so existing imports (and the CLI tests) keep resolving from '@/cli.js'.
|
|
65
76
|
export { parseCliArgs, CLI_USAGE };
|
|
66
77
|
/** Said when `connect` carries on past an init whose setup did not finish. */
|
|
@@ -94,6 +105,11 @@ async function handleInit(parsed) {
|
|
|
94
105
|
// RETICLE_PORT counts as explicit, as it does for every other command: resolved once here, so the
|
|
95
106
|
// port written into the project and the port the runtime phase binds are the same number.
|
|
96
107
|
const explicit = parsed.port ?? portFromEnv(process.env);
|
|
108
|
+
// The bridge port matters only when init goes on to start the app.
|
|
109
|
+
const bridgePort = true === parsed.filesOnly || parsed.dryRun
|
|
110
|
+
? undefined
|
|
111
|
+
: (explicit ?? readProjectPort(cwd) ?? RETICLE_DEFAULT_PORT);
|
|
112
|
+
await writeFirstUseWarnings(bridgePort);
|
|
97
113
|
const port = await portForInit(explicit, readProjectPort(cwd), readProjectId(cwd), {
|
|
98
114
|
// The registry names a daemon's owner; an older daemon that never registered is known by the
|
|
99
115
|
// projects its connected pages announced.
|
|
@@ -134,7 +150,7 @@ async function handleInit(parsed) {
|
|
|
134
150
|
deferOutcome: true,
|
|
135
151
|
continuesToRuntime: true !== parsed.filesOnly && true !== parsed.dryRun,
|
|
136
152
|
}, io);
|
|
137
|
-
await continueAfterInit({ ...parsed, port }, result, io, cwd, initFirstFlow);
|
|
153
|
+
await continueAfterInit({ ...parsed, port }, result, io, cwd, initFirstFlow, (print, mayWait) => agentsBlock(readAgentRows(cwd), print, mayWait && shouldWaitForAgents(process.stdout.isTTY, isCiEnv(process.env))));
|
|
138
154
|
return result;
|
|
139
155
|
}
|
|
140
156
|
// `serve`, `stop` and `restart` live in `cli/lifecycle/daemon-lifecycle.ts`: one idea, and the
|
|
@@ -149,12 +165,12 @@ import { handleServe, handleStop, handleRestart } from './cli/lifecycle/daemon-l
|
|
|
149
165
|
function handleVersion() {
|
|
150
166
|
process.stdout.write(`${SERVER_VERSION}\n`);
|
|
151
167
|
}
|
|
152
|
-
/** `reticle license` — show enterprise activation resolved from the environment (offline; nothing leaves). */
|
|
168
|
+
/** `reticle license` (now `status`) — show enterprise activation resolved from the environment (offline; nothing leaves). */
|
|
153
169
|
function handleLicense() {
|
|
154
170
|
log('reticle_license', { ...describeLicense(Date.now()) });
|
|
155
171
|
}
|
|
156
172
|
/**
|
|
157
|
-
* `reticle
|
|
173
|
+
* `reticle gate --list <file...>` (was `affected`) — print which saved flows must re-verify for the given changed files.
|
|
158
174
|
* Environment-side (costs the agent nothing per turn); the foundation of watch/gate. Never throws:
|
|
159
175
|
* a load error is logged, not fatal.
|
|
160
176
|
*/
|
|
@@ -178,32 +194,6 @@ async function handleAffected(files, since) {
|
|
|
178
194
|
});
|
|
179
195
|
}
|
|
180
196
|
}
|
|
181
|
-
/** Ensure a daemon is reachable on `port` (probe the real port; spawn + wait only if nothing's there). */
|
|
182
|
-
function ensureDaemon(port) {
|
|
183
|
-
return probePresence(port, { tcpOpen: probeDaemon, status: fetchStatus }).then(async (presence) => {
|
|
184
|
-
// Attaching to whatever already owns the port is the whole point of a daemon — but it means an
|
|
185
|
-
// upgrade does NOT take effect until that daemon dies, and nothing used to say so. Say it here,
|
|
186
|
-
// where both versions are in hand, and keep attaching: killing another agent's daemon on a
|
|
187
|
-
// version bump is worse than a loud warning.
|
|
188
|
-
if (presenceIsUsable(presence)) {
|
|
189
|
-
return warnOnDaemonSkew(port);
|
|
190
|
-
}
|
|
191
|
-
// Not free, not a daemon: a stranger holds the port. Spawning here is guaranteed to fail —
|
|
192
|
-
// the child cannot bind — and `waitForDaemon` would then report READY anyway, because its
|
|
193
|
-
// probe is the same bare TCP connect and the stranger accepts. That is the "serve reports
|
|
194
|
-
// success for a daemon that never bound" shape. Refuse instead, with the sentence `doctor`
|
|
195
|
-
// says.
|
|
196
|
-
if (PortPresence.FREE !== presence) {
|
|
197
|
-
throw new Error(describePresence(presence, port));
|
|
198
|
-
}
|
|
199
|
-
const scriptPath = process.argv[1];
|
|
200
|
-
if (scriptPath === undefined) {
|
|
201
|
-
throw new Error('cannot locate the reticle daemon script');
|
|
202
|
-
}
|
|
203
|
-
spawnDaemon(process.execPath, scriptPath, [DAEMON_INNER_COMMAND, PORT_FLAG, String(port)], port);
|
|
204
|
-
return waitForDaemon(port);
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
197
|
/**
|
|
208
198
|
* `reticle open [url]` — the one-command "show me the app". Resolves the port (the requested one if a
|
|
209
199
|
* daemon's there, else a running daemon it discovers — so the user never hunts for the port), ensures
|
|
@@ -212,6 +202,8 @@ function ensureDaemon(port) {
|
|
|
212
202
|
*/
|
|
213
203
|
/** How long `reticle open` waits for the launched page to register before reporting on it. */
|
|
214
204
|
const OPEN_SESSION_WAIT_MS = 8000;
|
|
205
|
+
/** For the heartbeat's resident memory, in MB. */
|
|
206
|
+
const BYTES_PER_MB = 1024 * 1024;
|
|
215
207
|
/**
|
|
216
208
|
* Poll until a NEW session shows up, or the wait runs out. Returns whether one did.
|
|
217
209
|
*
|
|
@@ -342,8 +334,12 @@ function handleDaemonInner(parsed) {
|
|
|
342
334
|
hudSignIn: (repaint, link) => hudSignIn(repaint, undefined, undefined, link),
|
|
343
335
|
hudLink: (root) => cmdLink([], root, PlatformLinkVia.HUD),
|
|
344
336
|
};
|
|
337
|
+
// Until the daemon is ready, a throw or a rejection is a failed start. Write it to the log with
|
|
338
|
+
// its code, so the next start can say why this one died (#2043).
|
|
339
|
+
const disposeStartupTrap = installStartupTrap(process, log);
|
|
345
340
|
startDaemon(options)
|
|
346
341
|
.then((server) => {
|
|
342
|
+
disposeStartupTrap();
|
|
347
343
|
log('reticle_daemon_ready', { port: parsed.port, pid: process.pid });
|
|
348
344
|
// Daemon lifecycle telemetry: started, a one-shot project profile, the periodic counter flush,
|
|
349
345
|
// and the rich summary on shutdown. All of it lives in one module — see daemon-telemetry.ts.
|
|
@@ -375,24 +371,15 @@ function handleDaemonInner(parsed) {
|
|
|
375
371
|
// to reach a socket that is still open.
|
|
376
372
|
server.announceShutdown?.();
|
|
377
373
|
// Awaited before the close/exit chain: `process.exit(0)` kills an in-flight POST, and this is
|
|
378
|
-
// the one event carrying the whole session.
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
.shutdown(reason)
|
|
386
|
-
.then(() => server.close())
|
|
387
|
-
.then(() => {
|
|
388
|
-
removePid(parsed.port);
|
|
389
|
-
process.exit(0);
|
|
390
|
-
})
|
|
391
|
-
.catch((err) => {
|
|
392
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
393
|
-
log('reticle_daemon_close_error', { error: message });
|
|
374
|
+
// the one event carrying the whole session. The reason rides out on the session summary;
|
|
375
|
+
// the proxy sees only a socket end. See SessionSummary.exit. The summary and the close share
|
|
376
|
+
// one bound, so a close that never settles cannot keep this process on its port (#1937).
|
|
377
|
+
void exitAfterClose({
|
|
378
|
+
summary: () => daemonTelemetry.shutdown(reason),
|
|
379
|
+
close: () => server.close(),
|
|
380
|
+
}).then((code) => {
|
|
394
381
|
removePid(parsed.port);
|
|
395
|
-
process.exit(
|
|
382
|
+
process.exit(code);
|
|
396
383
|
});
|
|
397
384
|
};
|
|
398
385
|
// Wrapped rather than passed directly: a signal handler receives the signal name as its first
|
|
@@ -432,12 +419,13 @@ function handleDaemonInner(parsed) {
|
|
|
432
419
|
facts: () => ({
|
|
433
420
|
sessions: server.bridge.sessions.count(),
|
|
434
421
|
served: everServedToolCall(),
|
|
422
|
+
rssMb: Math.round(process.memoryUsage().rss / BYTES_PER_MB),
|
|
435
423
|
}),
|
|
436
424
|
}).start();
|
|
437
425
|
})
|
|
438
426
|
.catch((error) => {
|
|
439
|
-
|
|
440
|
-
log
|
|
427
|
+
disposeStartupTrap();
|
|
428
|
+
recordStartupFailure(log, error, DaemonStartupError.START);
|
|
441
429
|
removePid(parsed.port);
|
|
442
430
|
process.exit(1);
|
|
443
431
|
});
|
|
@@ -515,7 +503,7 @@ export function main() {
|
|
|
515
503
|
const colour = true === process.stderr.isTTY && process.env['NO_COLOR'] === undefined;
|
|
516
504
|
process.stderr.write(`${renamedNote(argv[0] ?? '', renamedTo, colour)}\n`);
|
|
517
505
|
}
|
|
518
|
-
// Cloud subcommands (
|
|
506
|
+
// Cloud subcommands (connect/sync/runs/config) are a distinct family with their own async client;
|
|
519
507
|
// handle them before the local typed parser so `reticle login` etc. work as one tool.
|
|
520
508
|
if (isCloudCommand(argv[0])) {
|
|
521
509
|
// Asking a command what it does is never a request to run it. These dispatch before the typed
|
|
@@ -617,6 +605,17 @@ export function main() {
|
|
|
617
605
|
void handleInit(parsed);
|
|
618
606
|
break;
|
|
619
607
|
case 'serve':
|
|
608
|
+
// Bare `reticle` in a folder Reticle has not wired: the first verdict, with no agent and no
|
|
609
|
+
// `init`. A wired project, and `reticle serve` itself, keep serving.
|
|
610
|
+
if (bareRunsFirstVerdict(argv, myProjectId !== undefined || projectPort !== undefined || portConflict !== undefined)) {
|
|
611
|
+
void cmdFirstVerdict(parsed.port, !parsed.headless, (row) => reportOnboardingStop({ phase: OnboardingPhase.FIRST_RUN, reason: FIRST_VERDICT_REASON, ...row }, { detach: true }))
|
|
612
|
+
.then((code) => process.exit(code))
|
|
613
|
+
.catch((cause) => {
|
|
614
|
+
process.stderr.write(`reticle: ${cause instanceof Error ? cause.message : String(cause)}\n`);
|
|
615
|
+
process.exit(1);
|
|
616
|
+
});
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
620
619
|
handleServe(parsed);
|
|
621
620
|
break;
|
|
622
621
|
case 'stop':
|
|
@@ -633,7 +632,8 @@ export function main() {
|
|
|
633
632
|
void handleRestart(parsed.port, parsed.force);
|
|
634
633
|
break;
|
|
635
634
|
case 'status':
|
|
636
|
-
|
|
635
|
+
// Offline: the license key is read from the environment.
|
|
636
|
+
void handleStatus(parsed.port, parsed.json, { ...describeLicense(Date.now()) });
|
|
637
637
|
break;
|
|
638
638
|
case 'license':
|
|
639
639
|
handleLicense();
|
|
@@ -655,17 +655,24 @@ export function main() {
|
|
|
655
655
|
process.exit(1);
|
|
656
656
|
break;
|
|
657
657
|
case 'doctor':
|
|
658
|
-
void handleDoctor(parsed.port);
|
|
658
|
+
void handleDoctor(parsed.port, parsed.fix, reportStepFromCli);
|
|
659
659
|
break;
|
|
660
660
|
case 'setup-mcp':
|
|
661
|
-
|
|
661
|
+
void writeFirstUseWarnings().then(() => {
|
|
662
|
+
handleSetupMcp(reportStepFromCli, true, shouldWaitForAgents(process.stdout.isTTY, isCiEnv(process.env)));
|
|
663
|
+
});
|
|
664
|
+
break;
|
|
665
|
+
case 'setup-wait-agents':
|
|
666
|
+
void handleSetupWaitAgents(shouldWaitForAgents(process.stdout.isTTY, isCiEnv(process.env)));
|
|
662
667
|
break;
|
|
663
668
|
case 'setup-install':
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
+
void writeFirstUseWarnings().then(() => {
|
|
670
|
+
handleSetupInstall({
|
|
671
|
+
runtimeSecs: parsed.runtimeSecs,
|
|
672
|
+
installSecs: parsed.installSecs,
|
|
673
|
+
mcp: parsed.mcp,
|
|
674
|
+
}, reportStepFromCli);
|
|
675
|
+
});
|
|
669
676
|
break;
|
|
670
677
|
case 'open':
|
|
671
678
|
handleOpen(parsed.port, parsed.url, parsed.navigate);
|
|
@@ -674,13 +681,48 @@ export function main() {
|
|
|
674
681
|
handleDrive(parsed, readProjectId(process.cwd()));
|
|
675
682
|
break;
|
|
676
683
|
case 'verify':
|
|
677
|
-
|
|
684
|
+
void writeFirstUseWarnings().then(async () => {
|
|
685
|
+
// `--against` compares two builds through the daemon, and starts one when there is none.
|
|
686
|
+
if (parsed.against !== undefined) {
|
|
687
|
+
await handleVerifyAgainst({ ...parsed, against: parsed.against }, ensureDaemon, {
|
|
688
|
+
line: (text) => process.stdout.write(`${text}\n`),
|
|
689
|
+
fail: (text) => process.stderr.write(`${text}\n`),
|
|
690
|
+
exit: (code) => process.exit(code),
|
|
691
|
+
});
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
handleVerify(parsed, () => {
|
|
695
|
+
writeCiHint({ automation: currentAutomationHint(process.env), machineOutput: false });
|
|
696
|
+
});
|
|
697
|
+
});
|
|
698
|
+
break;
|
|
699
|
+
case 'compare':
|
|
700
|
+
// Two builds through the daemon, which it starts when there is none. Nothing to install.
|
|
701
|
+
void handleCompare(parsed, ensureDaemon, {
|
|
702
|
+
line: (text) => process.stdout.write(`${text}\n`),
|
|
703
|
+
exit: (code) => process.exit(code),
|
|
704
|
+
});
|
|
705
|
+
break;
|
|
706
|
+
case 'grade':
|
|
707
|
+
// Reads the declared state sources and snapshot files. No daemon and no browser.
|
|
708
|
+
void handleGrade(parsed.args, {
|
|
709
|
+
line: (text) => process.stdout.write(`${text}\n`),
|
|
710
|
+
exit: (code) => process.exit(code),
|
|
711
|
+
});
|
|
678
712
|
break;
|
|
679
713
|
case 'affected':
|
|
680
|
-
void handleAffected(parsed.files, parsed.since)
|
|
714
|
+
void handleAffected(parsed.files, parsed.since).then(() => {
|
|
715
|
+
writeCiHint({ automation: currentAutomationHint(process.env), machineOutput: false });
|
|
716
|
+
});
|
|
681
717
|
break;
|
|
682
718
|
case 'gate':
|
|
683
|
-
void handleGate(parsed.files, parsed.since, parsed.hook, parsed.acceptCoverage)
|
|
719
|
+
void handleGate(parsed.files, parsed.since, parsed.hook, parsed.acceptCoverage, reportGateCompleted).then(() => {
|
|
720
|
+
// A stop hook's output is read by the agent, so it is a machine output mode.
|
|
721
|
+
writeCiHint({
|
|
722
|
+
automation: currentAutomationHint(process.env),
|
|
723
|
+
machineOutput: true === parsed.hook,
|
|
724
|
+
});
|
|
725
|
+
});
|
|
684
726
|
break;
|
|
685
727
|
case 'report':
|
|
686
728
|
void handleReport(parsed.session, parsed.hook);
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function fetchStatus(port: number, host?: string,
|
|
11
11
|
/** Also tell the daemon a setup run is waiting on it, so it does not idle out mid-wait. */
|
|
12
|
-
hold?: boolean
|
|
12
|
+
hold?: boolean,
|
|
13
|
+
/** A longer limit, for a caller that must not mistake a busy daemon for a stuck one. */
|
|
14
|
+
timeoutMs?: number): Promise<unknown>;
|
|
13
15
|
/** One connected tab as `reticle status` reports it — the at-a-glance health line. */
|
|
14
16
|
interface StatusSession {
|
|
15
17
|
sessionId: string;
|
|
@@ -42,13 +42,15 @@ const ReticleStatusSchema = z
|
|
|
42
42
|
*/
|
|
43
43
|
export function fetchStatus(port, host = LOOPBACK_HOST,
|
|
44
44
|
/** Also tell the daemon a setup run is waiting on it, so it does not idle out mid-wait. */
|
|
45
|
-
hold = false
|
|
45
|
+
hold = false,
|
|
46
|
+
/** A longer limit, for a caller that must not mistake a busy daemon for a stuck one. */
|
|
47
|
+
timeoutMs = STATUS_PROBE_TIMEOUT_MS) {
|
|
46
48
|
return new Promise((resolve) => {
|
|
47
49
|
const req = http.get({
|
|
48
50
|
host,
|
|
49
51
|
port,
|
|
50
52
|
path: hold ? `${STATUS_PATH}?${STATUS_HOLD_QUERY}=1` : STATUS_PATH,
|
|
51
|
-
timeout:
|
|
53
|
+
timeout: timeoutMs,
|
|
52
54
|
// Shares the proxy's keep-alive agent: the proxy calls this on every reconnect, and a socket
|
|
53
55
|
// per probe is the same churn the POST leg was fixed for.
|
|
54
56
|
agent: loopbackAgent,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** The argv word that runs the daemon itself. `serve` and the MCP proxy spawn it; a person never types it. */
|
|
2
|
+
export declare const DAEMON_INNER_COMMAND = "_daemon";
|
|
3
|
+
/**
|
|
4
|
+
* True when `commandLine` runs a Reticle daemon (#1937).
|
|
5
|
+
*
|
|
6
|
+
* The MCP proxy (`reticle mcp`) and every other process fail this test, so nothing that relies on
|
|
7
|
+
* it can stop the agent's proxy or a stranger's process.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isReticleDaemonCommand(commandLine: string): boolean;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The argv word that runs the daemon itself. `serve` and the MCP proxy spawn it; a person never types it. */
|
|
2
|
+
export const DAEMON_INNER_COMMAND = '_daemon';
|
|
3
|
+
/**
|
|
4
|
+
* True when `commandLine` runs a Reticle daemon (#1937).
|
|
5
|
+
*
|
|
6
|
+
* The MCP proxy (`reticle mcp`) and every other process fail this test, so nothing that relies on
|
|
7
|
+
* it can stop the agent's proxy or a stranger's process.
|
|
8
|
+
*/
|
|
9
|
+
export function isReticleDaemonCommand(commandLine) {
|
|
10
|
+
return commandLine.trim().split(/\s+/).includes(DAEMON_INNER_COMMAND);
|
|
11
|
+
}
|
|
@@ -98,7 +98,14 @@ export declare function projectlessNote(myPort: number, others: readonly {
|
|
|
98
98
|
* record naming a daemon that is not the one being asked. The wording says which side is empty,
|
|
99
99
|
* since the failure is invisible precisely because both sides look healthy from where they stand.
|
|
100
100
|
*/
|
|
101
|
-
export declare function splitBrainNote(myPort: number, elsewhere: readonly number[]
|
|
101
|
+
export declare function splitBrainNote(myPort: number, elsewhere: readonly number[],
|
|
102
|
+
/** This project and the registry home, to tell whether the app's daemon belongs to another project. */
|
|
103
|
+
owner?: {
|
|
104
|
+
projectId: string | undefined;
|
|
105
|
+
home: string;
|
|
106
|
+
},
|
|
107
|
+
/** Where `RETICLE_PORT` is read from; injectable so the note is testable. */
|
|
108
|
+
env?: Readonly<Record<string, string | undefined>>): string | undefined;
|
|
102
109
|
/**
|
|
103
110
|
* The other stance on the same split, for a command standing on the port that HAS the app.
|
|
104
111
|
*
|
|
@@ -20,6 +20,7 @@ import { readdirSync, readFileSync } from 'node:fs';
|
|
|
20
20
|
import { join } from 'node:path';
|
|
21
21
|
import { DaemonRegistryEntrySchema, daemonRegistryPort, pickDaemonPort, } from '@reticlehq/core/artifacts';
|
|
22
22
|
import { ReticleEnv, RETICLE_DEFAULT_PORT } from '@reticlehq/core';
|
|
23
|
+
import { portFromEnv } from '../setup/init/init-port.js';
|
|
23
24
|
const RETICLE_PORT_ENV = ReticleEnv.PORT;
|
|
24
25
|
/**
|
|
25
26
|
* Every well-formed registry entry in `home`.
|
|
@@ -151,11 +152,36 @@ export function projectlessNote(myPort, others) {
|
|
|
151
152
|
* record naming a daemon that is not the one being asked. The wording says which side is empty,
|
|
152
153
|
* since the failure is invisible precisely because both sides look healthy from where they stand.
|
|
153
154
|
*/
|
|
154
|
-
export function splitBrainNote(myPort, elsewhere
|
|
155
|
+
export function splitBrainNote(myPort, elsewhere,
|
|
156
|
+
/** This project and the registry home, to tell whether the app's daemon belongs to another project. */
|
|
157
|
+
owner,
|
|
158
|
+
/** Where `RETICLE_PORT` is read from; injectable so the note is testable. */
|
|
159
|
+
env = process.env) {
|
|
155
160
|
if (0 === elsewhere.length)
|
|
156
161
|
return undefined;
|
|
157
162
|
const listed = elsewhere.map((port) => `:${String(port)}`).join(', ');
|
|
158
163
|
const first = elsewhere[0] ?? myPort;
|
|
164
|
+
// The app landed on ANOTHER project's daemon (#1652): two projects share a port. `resolveMcpPort`
|
|
165
|
+
// never adopts a daemon registered to a different project, so RETICLE_PORT would only relocate the
|
|
166
|
+
// agent again. The step that works is giving this project its own port, so its app comes here.
|
|
167
|
+
// Owner by port alone is safe: `elsewhere` is already liveness-filtered (`daemonsServingProjectElsewhere`).
|
|
168
|
+
const holder = owner === undefined ? undefined : daemonProjectAt(first, owner.home);
|
|
169
|
+
if (owner !== undefined && holder !== undefined && !adoptable(holder, owner.projectId)) {
|
|
170
|
+
// Port resolution is env-first (`--port`, `RETICLE_PORT`, `.reticle.json`, 4400), so a
|
|
171
|
+
// `RETICLE_PORT` left in the agent's MCP config from the old advice outranks the new `port`.
|
|
172
|
+
const envFirst = portFromEnv(env) === undefined
|
|
173
|
+
? 'Give this project its own port instead: set'
|
|
174
|
+
: `Give this project its own port instead: first remove ${RETICLE_PORT_ENV} from the agent's ` +
|
|
175
|
+
'MCP config, because it takes precedence over `.reticle.json`, then set';
|
|
176
|
+
return (`this project's app is connected to the Reticle daemon on :${String(first)}, which is ` +
|
|
177
|
+
`registered to another project, "${holder}", while this project's daemon is on ` +
|
|
178
|
+
`:${String(myPort)} — the two projects share a port, and the agent is attached to the empty ` +
|
|
179
|
+
`half. Pointing the agent at :${String(first)} will not help: an MCP server never attaches to ` +
|
|
180
|
+
`another project's daemon. ${envFirst} \`port\` in its ` +
|
|
181
|
+
`.reticle.json to one no other project uses (for example ${String(myPort)}), then restart the ` +
|
|
182
|
+
`dev server so the app connects to this project's daemon, and restart the agent session. ` +
|
|
183
|
+
`Leave :${String(first)} running; it serves "${holder}".`);
|
|
184
|
+
}
|
|
159
185
|
// The agent moves, not the app (#1546). The daemon the app is on is a working session this agent
|
|
160
186
|
// did not start, and it may be a person's, another agent's or another project's. Stopping it was
|
|
161
187
|
// the first thing this used to say. It comes last now, with what it costs.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type DeathCause } from './lifetime/death-cause.js';
|
|
1
2
|
import { type EscapeRequest, type EscapeResult } from './win32-escape.js';
|
|
2
3
|
export { MAX_DAEMON_LOG_BYTES, recoverOversizedLog } from './lifetime/log-cap.js';
|
|
3
4
|
/** Env override for the whole state directory — see ReticleEnv.STATE_DIR. */
|
|
@@ -22,6 +23,11 @@ export declare function rotateDaemonLog(path: string, deps: {
|
|
|
22
23
|
renameFile(from: string, to: string): void;
|
|
23
24
|
}, max?: number): boolean;
|
|
24
25
|
export declare function readPid(port: number, home?: string): number | null;
|
|
26
|
+
/**
|
|
27
|
+
* Whether a process with this pid exists. `EPERM` means it exists and this process may not signal
|
|
28
|
+
* it, which a daemon that WMI started can answer on Windows; only `ESRCH` means it is gone. Reading
|
|
29
|
+
* every throw as "dead" reclaimed a live daemon and started a second one on its port.
|
|
30
|
+
*/
|
|
25
31
|
export declare function isAlive(pid: number): boolean;
|
|
26
32
|
export declare function writePid(port: number): void;
|
|
27
33
|
/**
|
|
@@ -49,13 +55,22 @@ export declare function isRunning(port: number): boolean;
|
|
|
49
55
|
* (lowest port, deterministic), or null when none is running.
|
|
50
56
|
*/
|
|
51
57
|
export declare function discoverDaemonPort(): number | null;
|
|
58
|
+
/**
|
|
59
|
+
* Does this pidfile name a daemon that is still running?
|
|
60
|
+
*
|
|
61
|
+
* The pid must be alive AND the file must be newer than the last boot. A pid alone is not enough:
|
|
62
|
+
* the OS reuses pids, and after a reboot a pidfile named an unrelated Bluetooth service on Windows.
|
|
63
|
+
* The port was free, but serve skipped the start because that pid was alive, and the daemon never
|
|
64
|
+
* came up. A file whose age cannot be read is judged by its pid alone, as before.
|
|
65
|
+
*/
|
|
66
|
+
export declare function pidFileIsLive(path: string, pid: number, pidAlive: (pid: number) => boolean, bootMs?: () => number): boolean;
|
|
52
67
|
/**
|
|
53
68
|
* Sweep ~/.reticle for daemon-<port>.pid files whose process is no longer alive and delete them, so a
|
|
54
69
|
* crashed daemon never leaves a stale pidfile that confuses discovery or makes a port look "taken".
|
|
55
70
|
* Returns the ports reclaimed. `home` and `pidAlive` are injectable for testing (default to the real
|
|
56
71
|
* ~/.reticle and the process.kill(pid,0) liveness probe).
|
|
57
72
|
*/
|
|
58
|
-
export declare function reclaimStaleDaemons(home?: string, pidAlive?: (pid: number) => boolean): number[];
|
|
73
|
+
export declare function reclaimStaleDaemons(home?: string, pidAlive?: (pid: number) => boolean, bootMs?: () => number): number[];
|
|
59
74
|
/** The minimal shape of a spawned child that spawnDaemon uses. */
|
|
60
75
|
export interface SpawnedChild {
|
|
61
76
|
readonly pid?: number | undefined;
|
|
@@ -76,15 +91,18 @@ export interface SpawnDaemonDeps {
|
|
|
76
91
|
env?: Record<string, string | undefined>;
|
|
77
92
|
}): SpawnedChild;
|
|
78
93
|
pidAlive(pid: number): boolean;
|
|
94
|
+
/** When the machine last booted, epoch ms. Optional: the default reads `os.uptime()`. */
|
|
95
|
+
readonly bootTimeMs?: () => number;
|
|
79
96
|
/** `process.platform`. Optional so a test that does not care runs the detached start. */
|
|
80
97
|
readonly platform?: string;
|
|
81
98
|
/** Windows only: start the daemon outside the Job Object of this process. See win32-escape.ts. */
|
|
82
99
|
readonly escapeJob?: (request: EscapeRequest) => EscapeResult;
|
|
83
100
|
/**
|
|
84
|
-
* A predecessor died without a clean stop; `ageMs` is how long it had run
|
|
85
|
-
* does not care reports nothing. The
|
|
101
|
+
* A predecessor died without a clean stop; `ageMs` is how long it had run, and `cause` is what its
|
|
102
|
+
* own log says about how it ended. Optional so a test that does not care reports nothing. The
|
|
103
|
+
* default sends `daemon_died_unclean`.
|
|
86
104
|
*/
|
|
87
|
-
onUncleanDeath?(ageMs: number): void;
|
|
105
|
+
onUncleanDeath?(ageMs: number, cause?: DeathCause): void;
|
|
88
106
|
}
|
|
89
107
|
/**
|
|
90
108
|
* Spawn the reticle daemon as a detached background process, redirecting output to the log file.
|