@reticlehq/server 3.4.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/SKILL.md +16 -8
- package/dist/chromium-launch-options.d.ts +3 -1
- package/dist/chromium-launch-options.js +2 -1
- package/dist/command/cli/adhoc-verdict.d.ts +1 -0
- package/dist/command/cli/adhoc-verdict.js +6 -3
- package/dist/command/cli/answers/open-note.d.ts +2 -0
- package/dist/command/cli/answers/open-note.js +22 -0
- package/dist/command/cli/auth/cloud-session.d.ts +4 -4
- package/dist/command/cli/auth/device-flow.d.ts +2 -2
- package/dist/command/cli/cli-flow-commands.js +4 -8
- package/dist/command/cli/cli-parse.d.ts +1 -1
- package/dist/command/cli/cli-parse.js +13 -11
- package/dist/command/cli/cli-verify.d.ts +1 -1
- package/dist/command/cli/cli-verify.js +35 -25
- package/dist/command/cli/cloud-cli.js +110 -12
- package/dist/command/cli/cloud-login.d.ts +9 -1
- package/dist/command/cli/cloud-login.js +43 -8
- package/dist/command/cli/demo/demo-page.js +11 -3
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +46 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +99 -1
- package/dist/command/cli/drive/drive-attach.d.ts +27 -2
- package/dist/command/cli/drive/drive-attach.js +71 -4
- package/dist/command/cli/drive/drive-command.d.ts +2 -1
- package/dist/command/cli/drive/drive-command.js +13 -7
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +4 -1
- package/dist/command/cli/ports/port-holder.d.ts +10 -0
- package/dist/command/cli/ports/port-holder.js +24 -0
- package/dist/command/cli/setup-mcp-cli.js +15 -5
- package/dist/command/cli/tutorial.d.ts +2 -2
- package/dist/command/cli/tutorial.js +6 -9
- package/dist/command/cli.d.ts +6 -0
- package/dist/command/cli.js +45 -23
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +2 -0
- package/dist/command/daemon/binding/daemon-status-probe.js +1 -0
- package/dist/command/daemon/daemon-resolve.d.ts +14 -0
- package/dist/command/daemon/daemon-resolve.js +33 -2
- package/dist/command/setup/agent-writer.d.ts +1 -1
- package/dist/command/setup/agent-writer.js +3 -2
- package/dist/command/setup/bringup/connect-lease.d.ts +1 -0
- package/dist/command/setup/bringup/connect-lease.js +1 -0
- package/dist/command/setup/bringup/dev-server-wait.d.ts +10 -0
- package/dist/command/setup/bringup/dev-server-wait.js +20 -1
- package/dist/command/setup/init/init-port.d.ts +4 -0
- package/dist/command/setup/init/init-port.js +10 -0
- package/dist/command/setup/init/init-runtime.d.ts +10 -0
- package/dist/command/setup/init/init-runtime.js +71 -9
- package/dist/command/setup/node-effects.js +8 -2
- package/dist/command/setup/remaining-steps.js +2 -1
- package/dist/command/setup/run-setup.d.ts +7 -0
- package/dist/command/setup/run-setup.js +38 -25
- package/dist/command/setup/setup-command.d.ts +2 -1
- package/dist/command/setup/setup-command.js +2 -0
- package/dist/command/setup/setup-mcp.d.ts +2 -0
- package/dist/command/setup/setup-mcp.js +14 -5
- package/dist/command/version/peer-announce.d.ts +6 -2
- package/dist/command/version/peer-announce.js +8 -6
- package/dist/command/version/version-nudge.d.ts +17 -3
- package/dist/command/version/version-nudge.js +25 -4
- package/dist/command/version/version-skew.js +11 -2
- package/dist/features/crawl/crawl-tools.js +4 -0
- package/dist/features/crawl/crawl.d.ts +6 -0
- package/dist/features/crawl/crawl.js +9 -1
- package/dist/features/exhaust/ledger.d.ts +7 -0
- package/dist/features/exhaust/ledger.js +35 -9
- package/dist/features/harness/app-findings.d.ts +2 -0
- package/dist/features/harness/app-findings.js +52 -0
- package/dist/features/harness/drive-report.d.ts +27 -1
- package/dist/features/harness/drive-report.js +162 -11
- package/dist/features/harness/drivers.d.ts +10 -14
- package/dist/features/harness/drivers.js +10 -14
- package/dist/features/harness/fill-values.d.ts +3 -69
- package/dist/features/harness/fill-values.js +3 -133
- package/dist/features/harness/goals.d.ts +21 -0
- package/dist/features/harness/goals.js +77 -0
- package/dist/features/harness/harness.d.ts +21 -0
- package/dist/features/harness/harness.js +69 -2
- package/dist/features/harness/platform/drive-target.d.ts +30 -0
- package/dist/features/harness/platform/drive-target.js +81 -0
- package/dist/features/harness/platform/local-apps.d.ts +94 -0
- package/dist/features/harness/platform/local-apps.js +204 -0
- package/dist/features/harness/platform/remote-drive.d.ts +180 -0
- package/dist/features/harness/platform/remote-drive.js +365 -0
- package/dist/features/harness/platform/script.d.ts +48 -0
- package/dist/features/harness/platform/script.js +128 -0
- package/dist/features/harness/platform/server-driver.d.ts +41 -0
- package/dist/features/harness/platform/server-driver.js +179 -0
- package/dist/features/harness/platform-config.d.ts +7 -0
- package/dist/features/harness/platform-config.js +14 -2
- package/dist/features/harness/script/app-controls.d.ts +5 -0
- package/dist/features/harness/script/app-controls.js +40 -0
- package/dist/features/harness/script/lane-ids.d.ts +14 -0
- package/dist/features/harness/script/lane-ids.js +16 -0
- package/dist/features/harness/script/script-run.d.ts +41 -0
- package/dist/features/harness/script/script-run.js +299 -0
- package/dist/features/license/license.d.ts +2 -2
- package/dist/features/visual/visual-tools.d.ts +10 -1
- package/dist/features/visual/visual-tools.js +40 -3
- package/dist/hooks/driven-by.d.ts +4 -0
- package/dist/hooks/driven-by.js +16 -0
- package/dist/hooks/hook-commands.js +15 -4
- package/dist/hooks/tool-log.d.ts +9 -0
- package/dist/hooks/tool-log.js +29 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +91 -89
- package/dist/judgement/capsule/capsule-store.d.ts +4 -4
- package/dist/judgement/runs/artifact/build-verification-run.js +6 -0
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +1 -1
- package/dist/judgement/runs/artifact/replay-mapping.js +16 -6
- package/dist/judgement/runs/artifact/run-store.d.ts +1 -1
- package/dist/judgement/runs/artifact/run-store.js +26 -1
- package/dist/judgement/runs/drive-run.d.ts +22 -0
- package/dist/judgement/runs/drive-run.js +99 -1
- package/dist/judgement/runs/repair-prompt.js +1 -1
- package/dist/judgement/runs/reticle-runner.js +8 -1
- package/dist/judgement/runs/verification-sync.d.ts +3 -2
- package/dist/judgement/runs/verification-sync.js +4 -3
- package/dist/language/flows/change/gate.d.ts +10 -0
- package/dist/language/flows/change/gate.js +22 -0
- package/dist/language/flows/decision.js +34 -0
- package/dist/language/flows/flow-anchor.d.ts +54 -8
- package/dist/language/flows/flow-anchor.js +88 -3
- package/dist/language/flows/flow-cross-step.d.ts +15 -0
- package/dist/language/flows/flow-cross-step.js +21 -0
- package/dist/language/flows/flow-learning.js +8 -11
- package/dist/language/flows/flow-mutate-tools.js +5 -3
- package/dist/language/flows/flow-replay-run.d.ts +23 -9
- package/dist/language/flows/flow-replay-run.js +101 -94
- package/dist/language/flows/flow-replay.d.ts +23 -1
- package/dist/language/flows/flow-replay.js +92 -19
- package/dist/language/flows/flow-scope.js +5 -1
- package/dist/language/flows/flow-step-runners.d.ts +20 -2
- package/dist/language/flows/flow-step-runners.js +236 -42
- package/dist/language/flows/flow-store-for-session.d.ts +2 -2
- package/dist/language/flows/flow-store-for-session.js +2 -2
- package/dist/language/flows/flow-tools.js +58 -34
- package/dist/language/flows/flows.js +5 -2
- package/dist/language/flows/heal-run.js +6 -4
- package/dist/language/flows/recording/flake.d.ts +2 -2
- package/dist/language/flows/recording/tape/recordings.d.ts +30 -4
- package/dist/language/flows/recording/tape/recordings.js +40 -8
- package/dist/language/flows/replay-from-hud.d.ts +20 -0
- package/dist/language/flows/replay-from-hud.js +51 -0
- package/dist/language/flows/replay.d.ts +9 -5
- package/dist/language/flows/replay.js +27 -9
- package/dist/language/flows/suite/suite-flakes.js +2 -0
- package/dist/language/flows/suite-selection.d.ts +2 -15
- package/dist/language/flows/suite-selection.js +4 -3
- package/dist/language/flows/verify-change-tools.js +8 -4
- package/dist/launch-chromium.d.ts +27 -5
- package/dist/launch-chromium.js +38 -8
- package/dist/memory/cloud/cloud-config.d.ts +12 -0
- package/dist/memory/cloud/cloud-config.js +15 -0
- package/dist/memory/cloud/cloud-sync.d.ts +17 -14
- package/dist/memory/cloud/cloud-sync.js +12 -1
- package/dist/memory/cloud/harness-config.d.ts +26 -1
- package/dist/memory/cloud/harness-config.js +99 -4
- package/dist/memory/cloud/hud-notices-source.d.ts +21 -0
- package/dist/memory/cloud/hud-notices-source.js +128 -0
- package/dist/memory/cloud/memory-scope.d.ts +144 -0
- package/dist/memory/cloud/memory-scope.js +205 -0
- package/dist/memory/cloud/sync-cycle.d.ts +21 -3
- package/dist/memory/cloud/sync-cycle.js +45 -5
- package/dist/memory/cloud/sync-daemon.js +7 -5
- package/dist/memory/cloud/sync-disk.d.ts +1 -1
- package/dist/memory/cloud/sync-disk.js +5 -8
- package/dist/memory/impact/account-watch.d.ts +19 -0
- package/dist/memory/impact/account-watch.js +34 -0
- package/dist/memory/impact/impact-recorder.d.ts +8 -0
- package/dist/memory/impact/impact-recorder.js +12 -2
- package/dist/memory/impact/impact-store.d.ts +16 -0
- package/dist/memory/impact/impact-store.js +142 -26
- package/dist/memory/intent/intent-shard.d.ts +2 -2
- package/dist/memory/intent/intent-tools.js +88 -5
- package/dist/memory/journal/envelope.d.ts +16 -16
- package/dist/memory/journal/journal-query.d.ts +14 -0
- package/dist/memory/journal/journal-query.js +15 -0
- package/dist/memory/journal/journal-recorder.js +7 -1
- package/dist/memory/journal/on-disk/retention.d.ts +3 -0
- package/dist/memory/journal/on-disk/retention.js +5 -0
- package/dist/memory/journal/on-disk/startup-maintenance.js +3 -1
- package/dist/memory/journal/on-disk/workspace-tiers.js +7 -4
- package/dist/memory/journal/session-end.js +6 -4
- package/dist/memory/project/artifact-root-resolver.d.ts +36 -1
- package/dist/memory/project/artifact-root-resolver.js +134 -26
- package/dist/memory/project/artifact-root.d.ts +42 -5
- package/dist/memory/project/artifact-root.js +87 -8
- package/dist/memory/project/dir/reticle-dir.d.ts +4 -2
- package/dist/memory/project/dir/reticle-dir.js +3 -1
- package/dist/memory/project/project-tools.js +22 -3
- package/dist/memory/project/session-root.d.ts +24 -1
- package/dist/memory/project/session-root.js +41 -5
- package/dist/memory/project/sync-status.d.ts +39 -0
- package/dist/memory/project/sync-status.js +90 -0
- package/dist/memory/recall/memory-tools.js +2 -0
- package/dist/memory/recall/project-memory.d.ts +19 -0
- package/dist/memory/recall/project-memory.js +45 -6
- package/dist/portal/bridge/bridge.d.ts +7 -4
- package/dist/portal/bridge/bridge.js +25 -3
- package/dist/portal/bridge/pairing-token.d.ts +9 -0
- package/dist/portal/bridge/pairing-token.js +17 -0
- package/dist/portal/input/real-input.d.ts +81 -10
- package/dist/portal/input/real-input.js +207 -22
- package/dist/portal/input/viewport-tools.js +19 -9
- package/dist/portal/pool/browser-pool.d.ts +22 -9
- package/dist/portal/pool/browser-pool.js +70 -35
- package/dist/portal/pool/playwright-launcher.d.ts +13 -0
- package/dist/portal/pool/playwright-launcher.js +17 -1
- package/dist/portal/pool/pool-contract.d.ts +7 -4
- package/dist/portal/session/command-payload.d.ts +8 -0
- package/dist/portal/session/command-payload.js +19 -0
- package/dist/portal/session/dev-server/dev-command.d.ts +12 -0
- package/dist/portal/session/dev-server/dev-command.js +61 -12
- package/dist/portal/session/facts/departure-note.d.ts +22 -0
- package/dist/portal/session/facts/departure-note.js +47 -0
- package/dist/portal/session/facts/session-replaced.d.ts +5 -0
- package/dist/portal/session/facts/session-replaced.js +5 -0
- package/dist/portal/session/first-run-wiring.d.ts +42 -0
- package/dist/portal/session/first-run-wiring.js +132 -0
- package/dist/portal/session/human/notes-ledger.d.ts +20 -0
- package/dist/portal/session/human/notes-ledger.js +55 -0
- package/dist/portal/session/human/review-store.d.ts +12 -0
- package/dist/portal/session/human/review-store.js +18 -10
- package/dist/portal/session/live-control-tools.d.ts +0 -13
- package/dist/portal/session/live-control-tools.js +28 -3
- package/dist/portal/session/no-session-diagnosis.d.ts +7 -0
- package/dist/portal/session/no-session-diagnosis.js +15 -0
- package/dist/portal/session/no-session-next-action.d.ts +11 -0
- package/dist/portal/session/no-session-next-action.js +31 -5
- package/dist/portal/session/no-session-watch.d.ts +14 -1
- package/dist/portal/session/no-session-watch.js +63 -10
- package/dist/portal/session/presence/session-mirror.js +3 -0
- package/dist/portal/session/serving-directory.d.ts +27 -0
- package/dist/portal/session/serving-directory.js +45 -0
- package/dist/portal/session/session-health.d.ts +7 -0
- package/dist/portal/session/session-health.js +6 -0
- package/dist/portal/session/session-info.d.ts +8 -1
- package/dist/portal/session/session-info.js +6 -0
- package/dist/portal/session/session-manager.d.ts +21 -0
- package/dist/portal/session/session-manager.js +44 -8
- package/dist/portal/session/session-reaper.js +4 -1
- package/dist/portal/session/session-successor.d.ts +5 -0
- package/dist/portal/session/session-tools.js +26 -3
- package/dist/portal/session/session.d.ts +12 -10
- package/dist/portal/session/session.js +30 -31
- package/dist/start-options.d.ts +6 -0
- package/dist/status-payload.d.ts +10 -0
- package/dist/status-payload.js +23 -0
- package/dist/surface/http-server.d.ts +1 -1
- package/dist/surface/http-server.js +15 -4
- package/dist/surface/mcp/mcp-proxy.d.ts +5 -0
- package/dist/surface/mcp/mcp-proxy.js +36 -5
- package/dist/surface/mcp/mcp.d.ts +11 -6
- package/dist/surface/mcp/mcp.js +28 -14
- package/dist/surface/mcp/proxy/proxy-log.js +3 -5
- package/dist/surface/mcp/server-instructions.d.ts +4 -2
- package/dist/surface/mcp/server-instructions.js +14 -4
- package/dist/surface/mcp/shared-params.js +2 -2
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +8 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.js +56 -2
- package/dist/surface/tools/act/settle-in-flight.js +25 -3
- package/dist/surface/tools/act-sequence-tool.js +2 -2
- package/dist/surface/tools/act-tools.js +25 -9
- package/dist/surface/tools/assert/assert-verdict.d.ts +1 -1
- package/dist/surface/tools/assert/assert-verdict.js +17 -2
- package/dist/surface/tools/browser-tools.js +8 -1
- package/dist/surface/tools/chat-drives.d.ts +26 -0
- package/dist/surface/tools/chat-drives.js +165 -0
- package/dist/surface/tools/dynamic-tools.js +29 -2
- package/dist/surface/tools/empty-session-list.d.ts +41 -0
- package/dist/surface/tools/empty-session-list.js +80 -0
- package/dist/surface/tools/error-recovery.d.ts +6 -0
- package/dist/surface/tools/error-recovery.js +38 -6
- package/dist/surface/tools/explore-tools.d.ts +10 -1
- package/dist/surface/tools/explore-tools.js +82 -13
- package/dist/surface/tools/feedback-tools.js +14 -0
- package/dist/surface/tools/harness-explore.d.ts +96 -14
- package/dist/surface/tools/harness-explore.js +227 -202
- package/dist/surface/tools/harness-plan.d.ts +12 -0
- package/dist/surface/tools/harness-plan.js +31 -0
- package/dist/surface/tools/harness-script.d.ts +8 -0
- package/dist/surface/tools/harness-script.js +342 -0
- package/dist/surface/tools/harness-toolset.d.ts +14 -1
- package/dist/surface/tools/harness-toolset.js +77 -6
- package/dist/surface/tools/invoke-tool.js +64 -9
- package/dist/surface/tools/lease-seed.d.ts +29 -0
- package/dist/surface/tools/lease-seed.js +123 -0
- package/dist/surface/tools/lease-tools.d.ts +3 -23
- package/dist/surface/tools/lease-tools.js +30 -131
- package/dist/surface/tools/live-call-text.js +2 -2
- package/dist/surface/tools/next-step.d.ts +19 -0
- package/dist/surface/tools/next-step.js +159 -0
- package/dist/surface/tools/observe-tools.js +2 -4
- package/dist/surface/tools/platform-moment.d.ts +12 -0
- package/dist/surface/tools/platform-moment.js +168 -0
- package/dist/surface/tools/query-hint-schema.d.ts +7 -0
- package/dist/surface/tools/query-hint-schema.js +10 -0
- package/dist/surface/tools/read-tools.js +8 -2
- package/dist/surface/tools/real-input-attempt.js +96 -13
- package/dist/surface/tools/tool-kit.d.ts +25 -1
- package/dist/surface/tools/tool-kit.js +4 -6
- package/dist/surface/tools/tools.js +20 -48
- package/dist/telemetry/argument-shape.js +1 -1
- package/dist/telemetry/hud-metrics.d.ts +2 -0
- package/dist/telemetry/hud-metrics.js +17 -0
- package/dist/telemetry/onboarding-firsts.d.ts +2 -2
- package/dist/telemetry/onboarding-firsts.js +2 -2
- package/dist/telemetry/telemetry.d.ts +12 -0
- package/dist/telemetry/telemetry.js +43 -14
- package/dist/telemetry/verification-of.js +1 -0
- package/dist/wire-journal.js +4 -1
- package/docs/README.md +1 -1
- package/docs/actions.mdx +1 -2
- package/docs/agent-cheatsheet.md +1 -1
- package/docs/autodrive.md +29 -25
- package/docs/cli/cloud.mdx +6 -4
- package/docs/cli/doctor.mdx +6 -0
- package/docs/cli/drive.mdx +8 -2
- package/docs/cli/init.mdx +11 -13
- package/docs/cli/status.mdx +5 -1
- package/docs/cli/version.mdx +3 -3
- package/docs/cli.mdx +14 -8
- package/docs/desktop.mdx +5 -1
- package/docs/environment.mdx +3 -7
- package/docs/getting-started.md +14 -2
- package/docs/human-control.md +1 -2
- package/docs/index.mdx +4 -3
- package/docs/install-agentic.mdx +1 -1
- package/docs/install-manual.mdx +2 -2
- package/docs/packages/next.mdx +3 -0
- package/docs/packages/tauri.mdx +2 -2
- package/docs/packages/vite-plugin.mdx +24 -1
- package/docs/packages.mdx +1 -1
- package/docs/quickstart.mdx +23 -11
- package/docs/telemetry.md +1 -1
- package/docs/tools/act.mdx +1 -1
- package/docs/tools/session-and-feedback.mdx +9 -9
- package/docs/usage.md +5 -3
- package/docs/what-is-recorded.md +13 -3
- package/docs/what-is-reticle.mdx +3 -1
- package/package.json +8 -12
- package/dist/features/harness/driver.d.ts +0 -50
- package/dist/features/harness/driver.js +0 -236
- package/dist/features/harness/jev-driver.d.ts +0 -188
- package/dist/features/harness/jev-driver.js +0 -678
- package/dist/features/harness/openai-driver.d.ts +0 -46
- package/dist/features/harness/openai-driver.js +0 -194
- package/dist/memory/project/dir/fill-value-store.d.ts +0 -32
- package/dist/memory/project/dir/fill-value-store.js +0 -88
- package/docs/local-registry.md +0 -143
|
@@ -3,6 +3,7 @@ import { FlowPredicateSchema, Verified } from '@reticlehq/core';
|
|
|
3
3
|
import { DANGEROUS_ACTION_CONFIRM_ARG, ReticleCommand, QueryBy, } from '@reticlehq/core';
|
|
4
4
|
import { ReticleTool } from '@reticlehq/core';
|
|
5
5
|
import { formatStepAddress } from 'open-verification';
|
|
6
|
+
import { refFor } from './flow-anchor.js';
|
|
6
7
|
import { asRecord, asString } from '@reticlehq/core';
|
|
7
8
|
/**
|
|
8
9
|
* The note attached when a testid resolves to multiple live elements (the first match is used).
|
|
@@ -12,6 +13,9 @@ import { asRecord, asString } from '@reticlehq/core';
|
|
|
12
13
|
export function ambiguousTestidNote(value) {
|
|
13
14
|
return `ambiguous testid '${value}', used first match`;
|
|
14
15
|
}
|
|
16
|
+
function ambiguousRoleNote(role, name) {
|
|
17
|
+
return `ambiguous ${role} named '${name}', used first match`;
|
|
18
|
+
}
|
|
15
19
|
/**
|
|
16
20
|
* The live element refs a QUERY resolved to (empty when it failed or matched nothing). Shared by BOTH
|
|
17
21
|
* replay engines (replayProgram + flow-replay's step runner) so element extraction — the core of anchor
|
|
@@ -87,6 +91,9 @@ export function pathOf(url) {
|
|
|
87
91
|
return undefined;
|
|
88
92
|
}
|
|
89
93
|
}
|
|
94
|
+
/** What a saved flow could not keep, and the fix. See RecordedStep.unkeptExpect. */
|
|
95
|
+
export const UNKEPT_EXPECT = 'its proved `until` names something a saved flow cannot replay (a session ref such as e12): ' +
|
|
96
|
+
'name the element by testid, role and name, or text, and the check is kept';
|
|
90
97
|
/**
|
|
91
98
|
* Capture an act into every in-flight recording, or do nothing when none is running.
|
|
92
99
|
*
|
|
@@ -96,7 +103,9 @@ export function pathOf(url) {
|
|
|
96
103
|
*/
|
|
97
104
|
export function captureAct(recordings, args, res,
|
|
98
105
|
/** Where this step ran, so the ambient tape can be cut into journeys. See RecordedStep.route. */
|
|
99
|
-
route
|
|
106
|
+
route,
|
|
107
|
+
/** The session that acted: only recordings it started take the step. */
|
|
108
|
+
session) {
|
|
100
109
|
// NO `active()` gate. `RecordingStore.capture` opens the AMBIENT tape on its first step, and that
|
|
101
110
|
// branch was unreachable from here: this returned early whenever nothing was open, and the only
|
|
102
111
|
// thing that opened anything was `reticle_record { action: "start" }`. So the comment on `capture`
|
|
@@ -111,15 +120,18 @@ route) {
|
|
|
111
120
|
// success means — the large majority of calls carry one — and dropping it produced a flow graded
|
|
112
121
|
// "assertion-free: it will pass even if the feature is broken", which is the regression-suite
|
|
113
122
|
// story failing at its last step. See enforceableExpect below.
|
|
114
|
-
const
|
|
123
|
+
const declared = args['until'] ?? args['predicate'];
|
|
124
|
+
const expect = enforceableExpect(declared);
|
|
115
125
|
if (expect !== undefined)
|
|
116
126
|
step.expect = expect;
|
|
127
|
+
else if (declared !== undefined)
|
|
128
|
+
step.unkeptExpect = UNKEPT_EXPECT;
|
|
117
129
|
if (route !== undefined)
|
|
118
130
|
step.page = route;
|
|
119
131
|
const intent = asString(args['intent'])?.trim();
|
|
120
132
|
if (intent !== undefined && intent.length > 0)
|
|
121
133
|
step.intent = intent;
|
|
122
|
-
recordings.capture(step, route);
|
|
134
|
+
recordings.capture(step, route, session);
|
|
123
135
|
}
|
|
124
136
|
/**
|
|
125
137
|
* Capture an act_and_wait once its verdict is known, keeping `until` as the step's expectation only
|
|
@@ -127,15 +139,15 @@ route) {
|
|
|
127
139
|
* a regression test asserting something never once observed to hold because of the action. The
|
|
128
140
|
* action itself is always recorded: it happened, and replay needs it to reach the next step.
|
|
129
141
|
*/
|
|
130
|
-
export function captureVerdictedAct(recordings, args, res, route, verified) {
|
|
142
|
+
export function captureVerdictedAct(recordings, args, res, route, verified, session) {
|
|
131
143
|
const { until: _until, predicate: _predicate, ...unproved } = args;
|
|
132
|
-
captureAct(recordings, Verified.YES === verified ? args : unproved, res, route);
|
|
144
|
+
captureAct(recordings, Verified.YES === verified ? args : unproved, res, route, session);
|
|
133
145
|
}
|
|
134
146
|
/** Fold a PASSING standalone assertion into the step it proved. See RecordingStore.attachExpect. */
|
|
135
|
-
export function captureAssertion(recordings, raw) {
|
|
147
|
+
export function captureAssertion(recordings, raw, session) {
|
|
136
148
|
const expect = enforceableExpect(raw);
|
|
137
149
|
if (expect !== undefined)
|
|
138
|
-
recordings.attachExpect(expect);
|
|
150
|
+
recordings.attachExpect(expect, session);
|
|
139
151
|
}
|
|
140
152
|
/**
|
|
141
153
|
* A declared predicate, as a recorded expectation — or nothing.
|
|
@@ -207,6 +219,10 @@ function compileAnchorArgs(r, ref, action, actArgs) {
|
|
|
207
219
|
};
|
|
208
220
|
if (source !== undefined)
|
|
209
221
|
roleArgs['source'] = source;
|
|
222
|
+
if ('number' === typeof r['nth'] && 'number' === typeof r['of']) {
|
|
223
|
+
roleArgs['nth'] = r['nth'];
|
|
224
|
+
roleArgs['of'] = r['of'];
|
|
225
|
+
}
|
|
210
226
|
return { args: roleArgs, stable: true };
|
|
211
227
|
}
|
|
212
228
|
const component = asString(r['component']);
|
|
@@ -275,11 +291,13 @@ async function resolveRef(session, step) {
|
|
|
275
291
|
const result = await session.command(ReticleCommand.QUERY, query);
|
|
276
292
|
if (!result.ok)
|
|
277
293
|
throw new Error(result.error ?? 'query failed');
|
|
278
|
-
const
|
|
294
|
+
const refs = queryRefs(result);
|
|
295
|
+
const meant = refFor(step, refs);
|
|
296
|
+
const ref = meant ?? refs[0];
|
|
279
297
|
if (ref === undefined) {
|
|
280
298
|
throw new Error(`${value} named '${String(name)}' did not resolve in current page`);
|
|
281
299
|
}
|
|
282
|
-
return { ref };
|
|
300
|
+
return meant === undefined ? { ref, note: ambiguousRoleNote(value, String(name)) } : { ref };
|
|
283
301
|
}
|
|
284
302
|
if (by === QueryBy.COMPONENT) {
|
|
285
303
|
const component = asString(step.component);
|
|
@@ -26,6 +26,8 @@ import { FlakeStore } from '../stores/flake-store.js';
|
|
|
26
26
|
export async function recordSuiteFlakes(fs, reticleRoot, runs) {
|
|
27
27
|
const flakes = new FlakeStore(fs, reticleRoot);
|
|
28
28
|
for (const { replay } of runs) {
|
|
29
|
+
if (replay.status === ReplayStatus.UNVERIFIABLE)
|
|
30
|
+
continue;
|
|
29
31
|
await flakes.record(replay.name, ReplayStatus.OK === replay.status).catch(() => undefined);
|
|
30
32
|
}
|
|
31
33
|
return flakes.flakyFlows().catch(() => []);
|
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Which flows a suite run replays, resolved against the store.
|
|
3
|
-
*
|
|
4
|
-
* Shared by the MCP tool and the `reticle verify` command so the two cannot disagree about what a
|
|
5
|
-
* selection means.
|
|
6
|
-
*
|
|
7
|
-
* Lives beside the flow store rather than under `suite/` on purpose: it needs `flowsForSession`, and
|
|
8
|
-
* from `suite/` that import made `suite` and `flows` need EACH OTHER -- a mutual pair, which the
|
|
9
|
-
* reach guard forbids growing because two directories that need each other cannot be read, moved or
|
|
10
|
-
* tested apart. Here it adds no edge at all: `flows` reaching itself is nothing, and `cli -> flows`
|
|
11
|
-
* already existed. The pure rule lives in core (`selectFlows`); this is the part that has to read
|
|
12
|
-
* the files, because a label lives in one and `list` returns only names.
|
|
13
|
-
*/
|
|
14
|
-
import { type ProjectId } from '@reticlehq/core';
|
|
1
|
+
import type { ProjectTarget } from '../../memory/project/session-root.js';
|
|
15
2
|
import type { ToolDeps } from '../../surface/tools/tool-kit.js';
|
|
16
3
|
/**
|
|
17
4
|
* Resolve what a suite run should replay, and what it is holding back.
|
|
@@ -23,7 +10,7 @@ import type { ToolDeps } from '../../surface/tools/tool-kit.js';
|
|
|
23
10
|
* would turn a corrupt file into missing coverage nobody is told about, and replay reports the parse
|
|
24
11
|
* failure far better than a silent omission does.
|
|
25
12
|
*/
|
|
26
|
-
export declare function resolveSuiteSelection(deps: ToolDeps,
|
|
13
|
+
export declare function resolveSuiteSelection(deps: ToolDeps, target: ProjectTarget, args: Record<string, unknown>): Promise<{
|
|
27
14
|
run: string[];
|
|
28
15
|
quarantined: string[];
|
|
29
16
|
unmatched: string[];
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* already existed. The pure rule lives in core (`selectFlows`); this is the part that has to read
|
|
12
12
|
* the files, because a label lives in one and `list` returns only names.
|
|
13
13
|
*/
|
|
14
|
-
import { FLOW_FILE_VERSION, orderFlows, selectFlows
|
|
14
|
+
import { FLOW_FILE_VERSION, orderFlows, selectFlows } from '@reticlehq/core';
|
|
15
15
|
import { flowsForSession } from './flow-store-for-session.js';
|
|
16
16
|
/**
|
|
17
17
|
* Resolve what a suite run should replay, and what it is holding back.
|
|
@@ -23,8 +23,9 @@ import { flowsForSession } from './flow-store-for-session.js';
|
|
|
23
23
|
* would turn a corrupt file into missing coverage nobody is told about, and replay reports the parse
|
|
24
24
|
* failure far better than a silent omission does.
|
|
25
25
|
*/
|
|
26
|
-
export async function resolveSuiteSelection(deps,
|
|
27
|
-
const
|
|
26
|
+
export async function resolveSuiteSelection(deps, target, args) {
|
|
27
|
+
const { projectId } = target;
|
|
28
|
+
const store = flowsForSession(deps, target).flows;
|
|
28
29
|
const names = await store.list(projectId);
|
|
29
30
|
const asStrings = (value) => Array.isArray(value) ? value.filter((v) => 'string' === typeof v) : [];
|
|
30
31
|
const wantedNames = asStrings(args['names']);
|
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { sessionRoot, sessionProjectId } from '../../memory/project/session-root.js';
|
|
3
3
|
import { verdictForSuite } from '../../judgement/outcome/verify-change-verdict.js';
|
|
4
4
|
import { attributedFailures } from '../../judgement/outcome/attributed-failure.js';
|
|
5
|
-
import { Verified } from '@reticlehq/core';
|
|
5
|
+
import { Verified, isAdvisory } from '@reticlehq/core';
|
|
6
6
|
import { ReticleTool } from '@reticlehq/core';
|
|
7
7
|
import { asNumber, asRecord, asString } from '@reticlehq/core';
|
|
8
8
|
import { workerCountSchema } from '../../surface/tools/args/numeric-bounds.js';
|
|
@@ -49,7 +49,7 @@ export const VERIFY_CHANGE_TOOLS = [
|
|
|
49
49
|
contradictions: z
|
|
50
50
|
.array(z.unknown())
|
|
51
51
|
.optional()
|
|
52
|
-
.describe('
|
|
52
|
+
.describe('Findings from channels inspected during replay. Advisory entries are reported without changing the verdict; verdict-deciding entries can turn a green suite into no or unknown. OMITTED when clean; see `measured` when it could not be looked for.'),
|
|
53
53
|
untouched: z
|
|
54
54
|
.array(z.unknown())
|
|
55
55
|
.optional()
|
|
@@ -170,8 +170,9 @@ export const VERIFY_CHANGE_TOOLS = [
|
|
|
170
170
|
// The suite went green. That is exactly when the remaining two channels are worth reading:
|
|
171
171
|
// a replay that passes while a write fails is the false green this whole product is about.
|
|
172
172
|
const extra = await inspectAfterReplay(deps, args, cursor);
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
const decidingContradictions = extra.contradictions.filter((c) => !isAdvisory(c.kind));
|
|
174
|
+
if (decidingContradictions.length > 0) {
|
|
175
|
+
const kinds = decidingContradictions.map((c) => c.kind).join(', ');
|
|
175
176
|
const earned = attributedFailures(affected, unknownProvenance).length > 0;
|
|
176
177
|
return {
|
|
177
178
|
verified: earned ? Verified.NO : Verified.UNKNOWN,
|
|
@@ -197,6 +198,7 @@ export const VERIFY_CHANGE_TOOLS = [
|
|
|
197
198
|
flowsRun: affected,
|
|
198
199
|
suite,
|
|
199
200
|
unknownProvenance,
|
|
201
|
+
...(0 === extra.contradictions.length ? {} : { contradictions: extra.contradictions }),
|
|
200
202
|
measured: extra.measured,
|
|
201
203
|
};
|
|
202
204
|
}
|
|
@@ -207,6 +209,7 @@ export const VERIFY_CHANGE_TOOLS = [
|
|
|
207
209
|
flowsRun: affected,
|
|
208
210
|
suite,
|
|
209
211
|
unknownProvenance,
|
|
212
|
+
...(0 === extra.contradictions.length ? {} : { contradictions: extra.contradictions }),
|
|
210
213
|
...(extra.untouched === undefined ? {} : { untouched: extra.untouched }),
|
|
211
214
|
measured: extra.measured,
|
|
212
215
|
};
|
|
@@ -243,6 +246,7 @@ async function inspectAfterReplay(deps, args, cursor) {
|
|
|
243
246
|
currentEditEpoch: session.currentEditEpoch,
|
|
244
247
|
appOrigin: session.url,
|
|
245
248
|
background: session.background,
|
|
249
|
+
namedNetUrls: [],
|
|
246
250
|
// The replay is the action, and the cursor is where it started.
|
|
247
251
|
actionSince: cursor,
|
|
248
252
|
});
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* line there corrupts the client's stream.
|
|
15
15
|
*/
|
|
16
16
|
import { type ChromiumLaunchOptions } from './chromium-launch-options.js';
|
|
17
|
-
import { type ChromiumProbe } from './command/cli/doctor/browser/chromium-hint.js';
|
|
17
|
+
import { type ChromiumPathProblem, type ChromiumProbe } from './command/cli/doctor/browser/chromium-hint.js';
|
|
18
18
|
/** The installed-browser channels Playwright can drive in place of its own build. */
|
|
19
19
|
export declare const ChromiumChannel: {
|
|
20
20
|
readonly CHROME: "chrome";
|
|
@@ -31,6 +31,8 @@ export interface ChromiumTargetDeps {
|
|
|
31
31
|
/** Where the bundled playwright expects its Chromium build. */
|
|
32
32
|
executablePath: () => string;
|
|
33
33
|
exists: (path: string) => boolean;
|
|
34
|
+
/** What stops the executable RETICLE_CHROMIUM_PATH names from launching, if anything. */
|
|
35
|
+
pathProblem: (path: string) => ChromiumPathProblem | undefined;
|
|
34
36
|
platform: NodeJS.Platform;
|
|
35
37
|
env: NodeJS.ProcessEnv;
|
|
36
38
|
/** Run the pinned Chromium install; resolves true when it succeeded. */
|
|
@@ -38,16 +40,29 @@ export interface ChromiumTargetDeps {
|
|
|
38
40
|
/** A progress line for a human — stderr in production. */
|
|
39
41
|
announce: (line: string) => void;
|
|
40
42
|
}
|
|
41
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Which browser to launch: the bundled build (no channel), an installed channel, the executable
|
|
45
|
+
* RETICLE_CHROMIUM_PATH names, or none at all. `unusableConfigured` is a named executable that
|
|
46
|
+
* cannot be launched, and why.
|
|
47
|
+
*/
|
|
42
48
|
export type ChromiumTarget = {
|
|
43
49
|
found: true;
|
|
44
50
|
channel?: ChromiumChannel;
|
|
51
|
+
executablePath?: string;
|
|
45
52
|
} | {
|
|
46
53
|
found: false;
|
|
54
|
+
unusableConfigured?: {
|
|
55
|
+
path: string;
|
|
56
|
+
problem: ChromiumPathProblem;
|
|
57
|
+
};
|
|
47
58
|
};
|
|
48
59
|
/**
|
|
49
60
|
* Decide which browser a launch will use, installing the pinned Chromium only as the last resort.
|
|
50
|
-
* Order: bundled build on disk → installed Chrome → installed Edge →
|
|
61
|
+
* Order: RETICLE_CHROMIUM_PATH → bundled build on disk → installed Chrome → installed Edge →
|
|
62
|
+
* install, then bundled.
|
|
63
|
+
*
|
|
64
|
+
* A named executable ends the search either way. It is set on a machine where the usual places are
|
|
65
|
+
* empty or unusable, so falling back from a typo to a download would only hide the typo.
|
|
51
66
|
*/
|
|
52
67
|
export declare function resolveChromiumTarget(deps: ChromiumTargetDeps): Promise<ChromiumTarget>;
|
|
53
68
|
/** The subset of Playwright's `BrowserType` this file needs, so tests can pass a fake. */
|
|
@@ -67,14 +82,21 @@ export declare function announceOnce(write: (line: string) => void): (line: stri
|
|
|
67
82
|
* turns into the pinned install command, so the user still gets the one line that fixes it.
|
|
68
83
|
*/
|
|
69
84
|
export declare function launchChromium<B>(chromium: LaunchableChromium<B>, headless: boolean, deps?: ChromiumTargetDeps): Promise<B>;
|
|
85
|
+
/** What the probes read off the machine, injected so a test can name a browser that is not there. */
|
|
86
|
+
interface ProbeMachine {
|
|
87
|
+
env: NodeJS.ProcessEnv;
|
|
88
|
+
exists: (path: string) => boolean;
|
|
89
|
+
pathProblem: (path: string) => ChromiumPathProblem | undefined;
|
|
90
|
+
}
|
|
70
91
|
/**
|
|
71
92
|
* The lease preflight's probe: "can a launch succeed?", which now includes the Chrome/Edge fallback
|
|
72
93
|
* and the automatic install. Without this the preflight would refuse a lease on a machine the
|
|
73
94
|
* launcher itself could serve. Falls back to the plain probe so a real refusal keeps its evidence.
|
|
74
95
|
*/
|
|
75
|
-
export declare function probeLaunchableChromium(): Promise<ChromiumProbe>;
|
|
96
|
+
export declare function probeLaunchableChromium(machine?: ProbeMachine): Promise<ChromiumProbe>;
|
|
76
97
|
/**
|
|
77
98
|
* `doctor`'s probe: the plain Chromium probe, plus the installed browser that stands in when the
|
|
78
99
|
* bundled build is missing. Never installs — a diagnostic that downloads 150 MiB is not one.
|
|
79
100
|
*/
|
|
80
|
-
export declare function probeChromiumWithFallback(): Promise<ChromiumProbe>;
|
|
101
|
+
export declare function probeChromiumWithFallback(machine?: ProbeMachine): Promise<ChromiumProbe>;
|
|
102
|
+
export {};
|
package/dist/launch-chromium.js
CHANGED
|
@@ -18,7 +18,7 @@ import { existsSync } from 'node:fs';
|
|
|
18
18
|
import { createRequire } from 'node:module';
|
|
19
19
|
import { dirname, join } from 'node:path';
|
|
20
20
|
import { chromiumLaunchOptions } from './chromium-launch-options.js';
|
|
21
|
-
import { bundledPlaywrightVersion, chromiumInstallCommand, probeChromium, } from './command/cli/doctor/browser/chromium-hint.js';
|
|
21
|
+
import { bundledPlaywrightVersion, chromiumInstallCommand, chromiumPathProblem, configuredChromiumPath, configuredChromiumProbe, configuredChromiumRefusal, probeChromium, } from './command/cli/doctor/browser/chromium-hint.js';
|
|
22
22
|
/** The installed-browser channels Playwright can drive in place of its own build. */
|
|
23
23
|
export const ChromiumChannel = {
|
|
24
24
|
CHROME: 'chrome',
|
|
@@ -72,9 +72,20 @@ const installingLine = (command) => `[reticle] No Chromium, Chrome or Edge found
|
|
|
72
72
|
const INSTALL_FAILED_LINE = '[reticle] The Chromium install did not complete.\n';
|
|
73
73
|
/**
|
|
74
74
|
* Decide which browser a launch will use, installing the pinned Chromium only as the last resort.
|
|
75
|
-
* Order: bundled build on disk → installed Chrome → installed Edge →
|
|
75
|
+
* Order: RETICLE_CHROMIUM_PATH → bundled build on disk → installed Chrome → installed Edge →
|
|
76
|
+
* install, then bundled.
|
|
77
|
+
*
|
|
78
|
+
* A named executable ends the search either way. It is set on a machine where the usual places are
|
|
79
|
+
* empty or unusable, so falling back from a typo to a download would only hide the typo.
|
|
76
80
|
*/
|
|
77
81
|
export async function resolveChromiumTarget(deps) {
|
|
82
|
+
const configured = configuredChromiumPath(deps.env);
|
|
83
|
+
if (configured !== undefined) {
|
|
84
|
+
const problem = deps.pathProblem(configured);
|
|
85
|
+
return problem === undefined
|
|
86
|
+
? { found: true, executablePath: configured }
|
|
87
|
+
: { found: false, unusableConfigured: { path: configured, problem } };
|
|
88
|
+
}
|
|
78
89
|
let bundled;
|
|
79
90
|
try {
|
|
80
91
|
bundled = deps.executablePath();
|
|
@@ -168,6 +179,7 @@ function defaultDeps(chromium) {
|
|
|
168
179
|
return {
|
|
169
180
|
executablePath: () => chromium.executablePath(),
|
|
170
181
|
exists: existsSync,
|
|
182
|
+
pathProblem: (path) => chromiumPathProblem(path, process.platform),
|
|
171
183
|
platform: process.platform,
|
|
172
184
|
env: process.env,
|
|
173
185
|
install: installPinnedChromium,
|
|
@@ -181,15 +193,30 @@ function defaultDeps(chromium) {
|
|
|
181
193
|
*/
|
|
182
194
|
export async function launchChromium(chromium, headless, deps = defaultDeps(chromium)) {
|
|
183
195
|
const target = await resolveChromiumTarget(deps);
|
|
184
|
-
|
|
185
|
-
|
|
196
|
+
// Refused here rather than launched: Playwright's error for a missing executable is the one
|
|
197
|
+
// `chromiumLaunchHint` answers with the install command, which cannot fix a wrong path.
|
|
198
|
+
if (!target.found && target.unusableConfigured !== undefined) {
|
|
199
|
+
const { path, problem } = target.unusableConfigured;
|
|
200
|
+
throw new Error(configuredChromiumRefusal(path, problem));
|
|
201
|
+
}
|
|
202
|
+
return target.found
|
|
203
|
+
? chromium.launch(chromiumLaunchOptions(headless, target.channel, target.executablePath))
|
|
204
|
+
: chromium.launch(chromiumLaunchOptions(headless));
|
|
186
205
|
}
|
|
206
|
+
const THIS_MACHINE = {
|
|
207
|
+
env: process.env,
|
|
208
|
+
exists: existsSync,
|
|
209
|
+
pathProblem: (path) => chromiumPathProblem(path, process.platform),
|
|
210
|
+
};
|
|
187
211
|
/**
|
|
188
212
|
* The lease preflight's probe: "can a launch succeed?", which now includes the Chrome/Edge fallback
|
|
189
213
|
* and the automatic install. Without this the preflight would refuse a lease on a machine the
|
|
190
214
|
* launcher itself could serve. Falls back to the plain probe so a real refusal keeps its evidence.
|
|
191
215
|
*/
|
|
192
|
-
export async function probeLaunchableChromium() {
|
|
216
|
+
export async function probeLaunchableChromium(machine = THIS_MACHINE) {
|
|
217
|
+
const configured = configuredChromiumProbe(machine.env, machine.pathProblem);
|
|
218
|
+
if (configured !== undefined)
|
|
219
|
+
return configured;
|
|
193
220
|
try {
|
|
194
221
|
const { chromium } = await import('playwright');
|
|
195
222
|
const target = await resolveChromiumTarget(defaultDeps(chromium));
|
|
@@ -205,14 +232,17 @@ export async function probeLaunchableChromium() {
|
|
|
205
232
|
* `doctor`'s probe: the plain Chromium probe, plus the installed browser that stands in when the
|
|
206
233
|
* bundled build is missing. Never installs — a diagnostic that downloads 150 MiB is not one.
|
|
207
234
|
*/
|
|
208
|
-
export async function probeChromiumWithFallback() {
|
|
235
|
+
export async function probeChromiumWithFallback(machine = THIS_MACHINE) {
|
|
236
|
+
const configured = configuredChromiumProbe(machine.env, machine.pathProblem);
|
|
237
|
+
if (configured !== undefined)
|
|
238
|
+
return configured;
|
|
209
239
|
const probe = await probeChromium();
|
|
210
240
|
if (probe.exists)
|
|
211
241
|
return probe;
|
|
212
242
|
const channel = findInstalledChannel({
|
|
213
|
-
exists:
|
|
243
|
+
exists: machine.exists,
|
|
214
244
|
platform: process.platform,
|
|
215
|
-
env:
|
|
245
|
+
env: machine.env,
|
|
216
246
|
});
|
|
217
247
|
return channel === undefined ? probe : { ...probe, fallback: CHANNEL_LABEL[channel] };
|
|
218
248
|
}
|
|
@@ -78,6 +78,18 @@ export declare function resolveProjectCloud(fs: FileSystemPort, reticleRoot: str
|
|
|
78
78
|
* needs no exported key — see `withLinkedCredential`.
|
|
79
79
|
*/
|
|
80
80
|
export declare const linkedCloudPort: (fs: FileSystemPort, reticleRoot: string, homeDir: string, env: NodeJS.ProcessEnv) => () => Promise<CloudConfig | null>;
|
|
81
|
+
/**
|
|
82
|
+
* The API key a tab's own project resolves to, so a credential is only ever used for its own tabs.
|
|
83
|
+
* A tab whose project cannot be resolved has no key.
|
|
84
|
+
*/
|
|
85
|
+
export declare const sessionApiKeyPort: (fs: FileSystemPort, homeDir: string, env: NodeJS.ProcessEnv, rootOf: (id: string) => string) => (sessionId: string) => Promise<string | undefined>;
|
|
86
|
+
/** The platform and key a tab's own project resolves to, or undefined when it is not linked. */
|
|
87
|
+
export declare const sessionCloudPort: (fs: FileSystemPort, homeDir: string, env: NodeJS.ProcessEnv, rootOf: (id: string) => string) => (sessionId: string) => Promise<CloudConfig | undefined>;
|
|
88
|
+
/**
|
|
89
|
+
* The credential for pushing RUNS, or null when this project opted out with `sync.runs: false`.
|
|
90
|
+
* `reticle verify` used the port above and pushed its run and progress regardless of the opt-out.
|
|
91
|
+
*/
|
|
92
|
+
export declare const linkedRunsCloudPort: (fs: FileSystemPort, reticleRoot: string, homeDir: string, env: NodeJS.ProcessEnv) => () => Promise<CloudConfig | null>;
|
|
81
93
|
/**
|
|
82
94
|
* The environment with this project's resolved credential written into it, for the readers that
|
|
83
95
|
* take an env record (the panel's model config and harness switch). Handed `process.env` alone,
|
|
@@ -182,6 +182,21 @@ export async function resolveProjectCloud(fs, reticleRoot, homeDir, env) {
|
|
|
182
182
|
* needs no exported key — see `withLinkedCredential`.
|
|
183
183
|
*/
|
|
184
184
|
export const linkedCloudPort = (fs, reticleRoot, homeDir, env) => async () => (await resolveProjectCloud(fs, reticleRoot, homeDir, env)).config;
|
|
185
|
+
/**
|
|
186
|
+
* The API key a tab's own project resolves to, so a credential is only ever used for its own tabs.
|
|
187
|
+
* A tab whose project cannot be resolved has no key.
|
|
188
|
+
*/
|
|
189
|
+
export const sessionApiKeyPort = (fs, homeDir, env, rootOf) => async (sessionId) => (await sessionCloudPort(fs, homeDir, env, rootOf)(sessionId))?.apiKey;
|
|
190
|
+
/** The platform and key a tab's own project resolves to, or undefined when it is not linked. */
|
|
191
|
+
export const sessionCloudPort = (fs, homeDir, env, rootOf) => async (sessionId) => (await resolveProjectCloud(fs, rootOf(sessionId), homeDir, env)).config ?? undefined;
|
|
192
|
+
/**
|
|
193
|
+
* The credential for pushing RUNS, or null when this project opted out with `sync.runs: false`.
|
|
194
|
+
* `reticle verify` used the port above and pushed its run and progress regardless of the opt-out.
|
|
195
|
+
*/
|
|
196
|
+
export const linkedRunsCloudPort = (fs, reticleRoot, homeDir, env) => async () => {
|
|
197
|
+
const cloud = await resolveProjectCloud(fs, reticleRoot, homeDir, env);
|
|
198
|
+
return cloud.policy.runs ? cloud.config : null;
|
|
199
|
+
};
|
|
185
200
|
/**
|
|
186
201
|
* The environment with this project's resolved credential written into it, for the readers that
|
|
187
202
|
* take an env record (the panel's model config and harness switch). Handed `process.env` alone,
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Optional cloud sync for saved flows. "Logged in" here means the two cloud env vars are set (written by
|
|
3
|
-
* `reticle login` later; settable by hand today): the hosted URL and an API key from the Reticle
|
|
4
|
-
* dashboard. When present, a freshly-saved flow is pushed to `POST /v1/flows` so the team's regression
|
|
5
|
-
* suite lives in the cloud — surviving refactors and runnable in CI. When absent, sync is a no-op and
|
|
6
|
-
* everything stays 100% local (the "no phone-home" default: nothing leaves the machine unless you opt in).
|
|
7
|
-
*
|
|
8
|
-
* Sync is best-effort: a network failure NEVER fails the local save. The flow is already on disk; the
|
|
9
|
-
* cloud copy is an enhancement, so a push error is logged and swallowed.
|
|
10
|
-
*/
|
|
11
1
|
import { z } from 'zod';
|
|
12
2
|
import { type FlowFile, type ReticleVerificationRun, type RunRecord, type VerifyProgressEvent } from '@reticlehq/core';
|
|
13
3
|
/**
|
|
@@ -40,6 +30,19 @@ export declare function cloudFetch(url: string, init: {
|
|
|
40
30
|
headers: Record<string, string>;
|
|
41
31
|
body?: string;
|
|
42
32
|
}, timeoutMs?: number): Promise<Response>;
|
|
33
|
+
/**
|
|
34
|
+
* One sync request, status and body, under the same timeout every cloud call has. The sync daemon
|
|
35
|
+
* and `reticle sync` each called a bare `fetch`: one accepted-but-silent connection left the daemon's
|
|
36
|
+
* cycle running forever, and the dashboard stopped updating with nothing logged.
|
|
37
|
+
*/
|
|
38
|
+
export declare function syncRequest(url: string, init: {
|
|
39
|
+
method: string;
|
|
40
|
+
headers: Record<string, string>;
|
|
41
|
+
body?: string;
|
|
42
|
+
}, timeoutMs?: number): Promise<{
|
|
43
|
+
status: number;
|
|
44
|
+
text: string;
|
|
45
|
+
}>;
|
|
43
46
|
export declare const SyncOutcome: {
|
|
44
47
|
readonly SYNCED: "synced";
|
|
45
48
|
readonly SKIPPED: "skipped";
|
|
@@ -123,20 +126,20 @@ declare const ServerVerificationSchema: z.ZodObject<{
|
|
|
123
126
|
}>, "many">;
|
|
124
127
|
summary: z.ZodString;
|
|
125
128
|
}, "strip", z.ZodTypeAny, {
|
|
126
|
-
verificationId: string;
|
|
127
|
-
verdict: string | null;
|
|
128
129
|
flows: {
|
|
129
130
|
status: string;
|
|
130
131
|
name: string;
|
|
131
132
|
}[];
|
|
132
|
-
summary: string;
|
|
133
|
-
}, {
|
|
134
133
|
verificationId: string;
|
|
135
134
|
verdict: string | null;
|
|
135
|
+
summary: string;
|
|
136
|
+
}, {
|
|
136
137
|
flows: {
|
|
137
138
|
status: string;
|
|
138
139
|
name: string;
|
|
139
140
|
}[];
|
|
141
|
+
verificationId: string;
|
|
142
|
+
verdict: string | null;
|
|
140
143
|
summary: string;
|
|
141
144
|
}>;
|
|
142
145
|
export type ServerVerification = z.infer<typeof ServerVerificationSchema>;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Sync is best-effort: a network failure NEVER fails the local save. The flow is already on disk; the
|
|
9
9
|
* cloud copy is an enhancement, so a push error is logged and swallowed.
|
|
10
10
|
*/
|
|
11
|
+
import { shareableRun } from '@reticlehq/core/artifacts';
|
|
11
12
|
import { z } from 'zod';
|
|
12
13
|
import { VERIFY_PROGRESS_MAX_EVENTS, platformCredentialFrom, } from '@reticlehq/core';
|
|
13
14
|
/**
|
|
@@ -97,6 +98,15 @@ export async function cloudFetch(url, init, timeoutMs = CLOUD_FETCH_TIMEOUT_MS)
|
|
|
97
98
|
throw err;
|
|
98
99
|
}
|
|
99
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* One sync request, status and body, under the same timeout every cloud call has. The sync daemon
|
|
103
|
+
* and `reticle sync` each called a bare `fetch`: one accepted-but-silent connection left the daemon's
|
|
104
|
+
* cycle running forever, and the dashboard stopped updating with nothing logged.
|
|
105
|
+
*/
|
|
106
|
+
export async function syncRequest(url, init, timeoutMs = CLOUD_FETCH_TIMEOUT_MS) {
|
|
107
|
+
const res = await cloudFetch(url, init, timeoutMs);
|
|
108
|
+
return { status: res.status, text: await res.text() };
|
|
109
|
+
}
|
|
100
110
|
export const SyncOutcome = {
|
|
101
111
|
SYNCED: 'synced',
|
|
102
112
|
SKIPPED: 'skipped',
|
|
@@ -143,7 +153,8 @@ export async function syncRunToCloud(run, config, fetchImpl) {
|
|
|
143
153
|
'content-type': 'application/json',
|
|
144
154
|
authorization: `Bearer ${config.apiKey}`,
|
|
145
155
|
},
|
|
146
|
-
|
|
156
|
+
// The prompt context leaves the machine only when the project opted in.
|
|
157
|
+
body: JSON.stringify(shareableRun(run)),
|
|
147
158
|
});
|
|
148
159
|
return res.ok
|
|
149
160
|
? { outcome: SyncOutcome.SYNCED, status: res.status }
|
|
@@ -5,8 +5,33 @@ export interface HarnessConfigView {
|
|
|
5
5
|
harnessEntitled: boolean;
|
|
6
6
|
/** Whether the platform holds a key for `provider`. Entitlement is not readiness — see the offer. */
|
|
7
7
|
providerReady: boolean;
|
|
8
|
+
credits?: {
|
|
9
|
+
used: number;
|
|
10
|
+
limit: number;
|
|
11
|
+
};
|
|
8
12
|
}
|
|
9
13
|
export interface ConfigSource {
|
|
10
14
|
read(): HarnessConfigView | undefined;
|
|
11
15
|
}
|
|
12
|
-
export
|
|
16
|
+
export interface MutableConfigSource extends ConfigSource {
|
|
17
|
+
applyWrite(enabled: boolean): void;
|
|
18
|
+
recheck(): void;
|
|
19
|
+
subscribe(listener: () => void): () => void;
|
|
20
|
+
/** Re-read in the background, telling listeners only when the answer changed. */
|
|
21
|
+
poll(): void;
|
|
22
|
+
}
|
|
23
|
+
export declare function harnessConfigSource(load: () => Promise<HarnessConfigView | undefined>, now?: () => number): MutableConfigSource;
|
|
24
|
+
export type ConfigsByRoot = (root: string) => MutableConfigSource;
|
|
25
|
+
/**
|
|
26
|
+
* One live config source per project root, created on first ask; `onChange` hears every update.
|
|
27
|
+
*
|
|
28
|
+
* The platform read arrives as `load`, so this cache never reaches for the harness feature that
|
|
29
|
+
* performs it: the daemon, which owns both, joins them. It sat inline in `start` and `startDaemon`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function harnessConfigsByRoot(load: (root: string) => Promise<HarnessConfigView | undefined>, onChange?: (root: string) => void): ConfigsByRoot;
|
|
32
|
+
/**
|
|
33
|
+
* The HUD's harness switch, written to the platform with this project's credential. Nothing is
|
|
34
|
+
* awaited: an accepted write updates the cache at once, and a refused or failed one re-reads the
|
|
35
|
+
* platform so the switch springs back to the truth rather than staying where the user left it.
|
|
36
|
+
*/
|
|
37
|
+
export declare function applyHarnessSwitch(configs: ConfigsByRoot, root: string, enabled: boolean, envFor: () => Promise<Record<string, string | undefined>>): void;
|