@reticlehq/server 3.9.0 → 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/SKILL.md +13 -12
- 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 -187
- 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/cli-doctor.js +1 -1
- package/dist/command/cli/cli-flow-commands.js +2 -9
- package/dist/command/cli/cli-kill.js +1 -1
- 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 +17 -15
- package/dist/command/cli/cli-parse.js +44 -27
- 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 +58 -29
- package/dist/command/cli/cli-verify.d.ts +11 -2
- package/dist/command/cli/cli-verify.js +27 -8
- 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/doctor-daemon-line.js +6 -3
- package/dist/command/cli/doctor-repairs.js +1 -1
- 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/lifecycle/daemon-lifecycle.d.ts +1 -1
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +1 -1
- package/dist/command/cli/ports/port-holder.d.ts +9 -0
- package/dist/command/cli/ports/port-holder.js +27 -3
- package/dist/command/cli/status/status-lines.d.ts +1 -0
- package/dist/command/cli/status/status-lines.js +22 -0
- package/dist/command/cli/status-fields.d.ts +5 -1
- package/dist/command/cli/status-fields.js +7 -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 +46 -41
- 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 +12 -1
- package/dist/command/daemon/daemon.js +35 -4
- package/dist/command/daemon/lifetime/death-cause.js +21 -2
- package/dist/command/daemon/lifetime/heartbeat.js +7 -2
- package/dist/command/daemon/lifetime/startup-failure.d.ts +29 -0
- package/dist/command/daemon/lifetime/startup-failure.js +62 -0
- 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-runtime.js +6 -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 +6 -3
- package/dist/command/update/updater.js +6 -0
- package/dist/command/version/version-skew.js +1 -1
- 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/visual/visual-tools.js +66 -19
- package/dist/index.js +7 -4
- 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/runner-port.d.ts +2 -0
- package/dist/judgement/runs/runner-port.js +23 -5
- 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 +44 -0
- 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 +19 -3
- package/dist/language/flows/recording/tape/recordings.js +71 -5
- package/dist/language/flows/replay.d.ts +19 -5
- package/dist/language/flows/replay.js +99 -7
- 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/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/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/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 +28 -9
- 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 +22 -20
- package/dist/portal/input/real-input.js +41 -31
- package/dist/portal/input/viewport-tools.d.ts +8 -0
- package/dist/portal/input/viewport-tools.js +75 -18
- package/dist/portal/pool/browser-pool.d.ts +11 -29
- package/dist/portal/pool/browser-pool.js +35 -17
- package/dist/portal/pool/context-permissions.d.ts +17 -3
- package/dist/portal/pool/context-permissions.js +36 -5
- 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 +44 -17
- package/dist/portal/pool/lease-page-error.js +147 -5
- package/dist/portal/pool/page-error-journal.d.ts +7 -1
- package/dist/portal/pool/page-error-journal.js +27 -0
- package/dist/portal/pool/playwright-launcher.js +51 -8
- package/dist/portal/pool/pool-contract.d.ts +27 -1
- package/dist/portal/pool/pool-lease.d.ts +36 -1
- package/dist/portal/pool/zero-install.d.ts +8 -0
- package/dist/portal/pool/zero-install.js +23 -1
- package/dist/portal/session/dev-server/dev-command.d.ts +7 -6
- package/dist/portal/session/dev-server/dev-command.js +61 -15
- 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 +67 -25
- package/dist/portal/session/no-session-next-action.d.ts +2 -0
- package/dist/portal/session/no-session-next-action.js +12 -3
- package/dist/portal/session/no-session-watch.d.ts +6 -0
- package/dist/portal/session/no-session-watch.js +38 -3
- 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 +174 -63
- package/dist/start-wiring.d.ts +2 -1
- package/dist/start-wiring.js +2 -1
- package/dist/surface/mcp/mcp.js +3 -2
- 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 +1 -1
- 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/after-match.d.ts +4 -1
- package/dist/surface/tools/act/after-match.js +8 -1
- 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 +15 -1
- package/dist/surface/tools/act/resolve-target.js +24 -1
- package/dist/surface/tools/act/sequence-grade.d.ts +12 -0
- package/dist/surface/tools/act/sequence-grade.js +37 -26
- 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 +78 -50
- package/dist/surface/tools/act-tools.d.ts +1 -13
- package/dist/surface/tools/act-tools.js +74 -86
- 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 +16 -37
- package/dist/surface/tools/browser-tools.js +12 -6
- 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 +9 -2
- package/dist/surface/tools/error-recovery.d.ts +12 -1
- package/dist/surface/tools/error-recovery.js +91 -21
- package/dist/surface/tools/explore-tools.js +1 -1
- package/dist/surface/tools/invoke-tool.js +3 -2
- 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 +35 -1
- package/dist/surface/tools/lease-hint.js +42 -0
- package/dist/surface/tools/lease-readiness.d.ts +26 -0
- package/dist/surface/tools/lease-readiness.js +79 -0
- package/dist/surface/tools/lease-seed.d.ts +14 -1
- package/dist/surface/tools/lease-seed.js +35 -3
- package/dist/surface/tools/lease-tools.d.ts +3 -41
- package/dist/surface/tools/lease-tools.js +62 -110
- 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/session-ports.d.ts +14 -0
- package/dist/surface/tools/session-ports.js +53 -0
- package/dist/surface/tools/tool-kit.d.ts +6 -0
- package/dist/surface/tools/tool-kit.js +2 -3
- package/dist/surface/tools/tool-surface.js +0 -1
- package/dist/surface/tools/tools.js +28 -5
- 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 +1 -0
- package/dist/telemetry/tool-refused.d.ts +6 -3
- package/dist/telemetry/tool-refused.js +8 -3
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +1 -1
- 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 +1 -1
- package/docs/cli/grade.mdx +97 -0
- package/docs/cli/license.mdx +9 -7
- package/docs/cli/restart.mdx +7 -5
- package/docs/cli/rollback.mdx +5 -5
- 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 +33 -23
- package/docs/deploy-checks.md +1 -1
- package/docs/desktop.mdx +1 -1
- package/docs/enterprise.md +2 -2
- package/docs/environment.mdx +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 +1 -1
- package/docs/license-activation.mdx +3 -3
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages.mdx +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 +2 -2
- 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 +13 -13
- package/dist/features/exhaust/exhaust-run.d.ts +0 -33
- package/dist/features/exhaust/exhaust-run.js +0 -82
|
@@ -5,7 +5,7 @@ import { safeProjectId } from './flow-result.js';
|
|
|
5
5
|
import { changeInPlace } from './narrow-write.js';
|
|
6
6
|
import { dirname } from 'node:path';
|
|
7
7
|
import { gitTracks } from '../../machine/git-tracks.js';
|
|
8
|
-
import { AnchorKind, DEGRADED_ANCHOR_ROLE, DOCUMENT_ANCHOR_ROLE, FLOW_FILE_VERSION, flowFileVersionFor, FlowErrorCode, FlowFileSchema, FlowStepTool, QueryBy,
|
|
8
|
+
import { AnchorKind, DEGRADED_ANCHOR_ROLE, DOCUMENT_ANCHOR_ROLE, FLOW_FILE_VERSION, flowFileVersionFor, FlowErrorCode, FlowFileSchema, FlowStepTool, QueryBy, REDACTED_VALUE, isCredentialFieldName, isGlobalPressCall, } from '@reticlehq/core';
|
|
9
9
|
import { ReticleTool, flowJsonReplacer, mainCheckoutTwin } from '@reticlehq/core';
|
|
10
10
|
import { asRecord, asString } from '@reticlehq/core';
|
|
11
11
|
import { applyHealChanges } from './heal.js';
|
|
@@ -35,25 +35,44 @@ function sourceArg(src) {
|
|
|
35
35
|
const source = asRecord(src['source']);
|
|
36
36
|
const file = source['file'];
|
|
37
37
|
const line = source['line'];
|
|
38
|
-
if (typeof file !== 'string' || 0 === file.length || typeof line !== 'number')
|
|
38
|
+
if (typeof file !== 'string' || 0 === file.length || typeof line !== 'number') {
|
|
39
39
|
return undefined;
|
|
40
|
+
}
|
|
40
41
|
const out = { file, line };
|
|
41
|
-
if ('number' === typeof source['column'])
|
|
42
|
+
if ('number' === typeof source['column']) {
|
|
42
43
|
out.column = source['column'];
|
|
44
|
+
}
|
|
43
45
|
return out;
|
|
44
46
|
}
|
|
45
47
|
function componentAnchor(src) {
|
|
46
48
|
const component = asString(src['component']);
|
|
47
49
|
const source = sourceArg(src);
|
|
48
|
-
if (component === undefined && source === undefined)
|
|
50
|
+
if (component === undefined && source === undefined) {
|
|
49
51
|
return null;
|
|
52
|
+
}
|
|
50
53
|
const anchor = {
|
|
51
54
|
kind: AnchorKind.COMPONENT,
|
|
52
55
|
};
|
|
53
|
-
if (component !== undefined)
|
|
56
|
+
if (component !== undefined) {
|
|
54
57
|
anchor.component = component;
|
|
55
|
-
|
|
58
|
+
}
|
|
59
|
+
if (source !== undefined) {
|
|
56
60
|
anchor.source = source;
|
|
61
|
+
}
|
|
62
|
+
const text = asString(src['text']);
|
|
63
|
+
if (text !== undefined && text.length > 0) {
|
|
64
|
+
anchor.text = text;
|
|
65
|
+
}
|
|
66
|
+
const role = asString(src['role']);
|
|
67
|
+
if (role !== undefined && role.length > 0) {
|
|
68
|
+
anchor.role = role;
|
|
69
|
+
}
|
|
70
|
+
const nth = src['nth'];
|
|
71
|
+
const of = src['of'];
|
|
72
|
+
if ('number' === typeof nth && 'number' === typeof of && 1 < of) {
|
|
73
|
+
anchor.nth = nth;
|
|
74
|
+
anchor.of = of;
|
|
75
|
+
}
|
|
57
76
|
return anchor;
|
|
58
77
|
}
|
|
59
78
|
/** Pick the on-disk anchor for a normalized step: testid > component(auto) > degraded role. */
|
|
@@ -66,20 +85,19 @@ export function anchorForStep(args) {
|
|
|
66
85
|
value,
|
|
67
86
|
};
|
|
68
87
|
const source = sourceArg(args);
|
|
69
|
-
if (source !== undefined)
|
|
88
|
+
if (source !== undefined) {
|
|
70
89
|
anchor.source = source;
|
|
90
|
+
}
|
|
71
91
|
const of = args['of'];
|
|
72
|
-
if ('number' === typeof of && 1 < of)
|
|
92
|
+
if ('number' === typeof of && 1 < of) {
|
|
73
93
|
anchor.of = of;
|
|
94
|
+
// Its place among them, as a role anchor keeps one (#2135).
|
|
95
|
+
Object.assign(anchor, 'number' === typeof args['nth'] ? { nth: args['nth'] } : {});
|
|
96
|
+
}
|
|
74
97
|
return { anchor, degraded: false };
|
|
75
98
|
}
|
|
76
|
-
// Role + NAME is a real anchor, not a degraded placeholder
|
|
77
|
-
//
|
|
78
|
-
// The vocabulary has always described `{ kind:'role', role, name }` as an addressable anchor, and
|
|
79
|
-
// nothing ever produced one — the only ROLE anchor written to disk was the degraded placeholder
|
|
80
|
-
// that means "add a data-testid". So a step the recorder anchored by accessible name arrived here
|
|
81
|
-
// and had that name thrown away, which is precisely the anchor that distinguishes one row's
|
|
82
|
-
// control from another's when a shared JSX source location cannot.
|
|
99
|
+
// Role + NAME is a real anchor, not a degraded placeholder: the name separates one row's control
|
|
100
|
+
// from another's when a shared JSX source location cannot.
|
|
83
101
|
// A document-key press compiled with no element (#1559): a real anchor, not a degraded one. It is
|
|
84
102
|
// nameless on purpose, because it names no element; the press is what replay dispatches.
|
|
85
103
|
if (by === QueryBy.ROLE &&
|
|
@@ -88,20 +106,30 @@ export function anchorForStep(args) {
|
|
|
88
106
|
isGlobalPressCall({ action: args['action'], args: args['args'] })) {
|
|
89
107
|
return { anchor: { kind: AnchorKind.ROLE, role: DOCUMENT_ANCHOR_ROLE }, degraded: false };
|
|
90
108
|
}
|
|
91
|
-
if (by === QueryBy.ROLE && value !== undefined) {
|
|
109
|
+
if (by === QueryBy.ROLE && value !== undefined && value !== DEGRADED_ANCHOR_ROLE) {
|
|
92
110
|
const name = asString(args['name']);
|
|
111
|
+
// Provenance lives on the step, not the anchor; the position among namesakes is identity.
|
|
112
|
+
const nth = args['nth'];
|
|
113
|
+
const of = args['of'];
|
|
114
|
+
const among = 'number' === typeof nth && 'number' === typeof of ? { nth, of } : {};
|
|
93
115
|
if (name !== undefined && name.length > 0) {
|
|
94
|
-
//
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
116
|
+
// The nearest containers' roles: where a replay looks when a correct build renamed it.
|
|
117
|
+
const within = Array.isArray(args['within']) ? { within: args['within'].map(String) } : {};
|
|
118
|
+
return {
|
|
119
|
+
anchor: { kind: AnchorKind.ROLE, role: value, name, ...among, ...within },
|
|
120
|
+
degraded: false,
|
|
121
|
+
};
|
|
99
122
|
}
|
|
123
|
+
// A control with a role and no accessible name (an icon-only button) is addressed by its
|
|
124
|
+
// position among the nameless controls of its role. Saving it as the degraded placeholder made
|
|
125
|
+
// every step on an icon-only button unreplayable.
|
|
126
|
+
return { anchor: { kind: AnchorKind.ROLE, role: value, ...among }, degraded: false };
|
|
100
127
|
}
|
|
101
128
|
if (by === QueryBy.COMPONENT) {
|
|
102
129
|
const anchor = componentAnchor(args);
|
|
103
|
-
if (anchor !== null)
|
|
130
|
+
if (anchor !== null) {
|
|
104
131
|
return { anchor, degraded: false };
|
|
132
|
+
}
|
|
105
133
|
}
|
|
106
134
|
return { anchor: degradedAnchor(), degraded: true };
|
|
107
135
|
}
|
|
@@ -122,8 +150,9 @@ function subStepToFlowStep(raw) {
|
|
|
122
150
|
* exactly this, so dropping it here made the grader count assertions that were never saved.
|
|
123
151
|
*/
|
|
124
152
|
const expect = sub['expect'];
|
|
125
|
-
if (expect !== undefined)
|
|
153
|
+
if (expect !== undefined) {
|
|
126
154
|
step.expect = expect;
|
|
155
|
+
}
|
|
127
156
|
return step;
|
|
128
157
|
}
|
|
129
158
|
/**
|
|
@@ -137,24 +166,55 @@ function subStepToFlowStep(raw) {
|
|
|
137
166
|
* WHERE it was typed rather than by guessing whether the characters look secret — a guess that both
|
|
138
167
|
* over-redacts a search box and misses a password that happens to be a dictionary word.
|
|
139
168
|
*
|
|
140
|
-
* `
|
|
141
|
-
*
|
|
169
|
+
* `isCredentialFieldName` is the network key rule plus the word for a password in other
|
|
170
|
+
* languages. An SDK before 3.8.0 sends no secret mark, and then the name is the only signal.
|
|
142
171
|
*/
|
|
143
172
|
function redactSecretFill(anchor, args,
|
|
144
173
|
/** The SDK said the target holds a secret: redact whatever the field is called. */
|
|
145
174
|
secret = false) {
|
|
146
|
-
if (typeof args['value'] !== 'string')
|
|
175
|
+
if (typeof args['value'] !== 'string') {
|
|
147
176
|
return args;
|
|
177
|
+
}
|
|
148
178
|
const field = anchorFieldName(anchor);
|
|
149
|
-
const sensitive = secret || (field !== undefined &&
|
|
150
|
-
if (!sensitive)
|
|
179
|
+
const sensitive = secret || (field !== undefined && isCredentialFieldName(field));
|
|
180
|
+
if (!sensitive) {
|
|
151
181
|
return args;
|
|
182
|
+
}
|
|
152
183
|
// ponytail: a secret field with no name (a component anchor) gets no variable; replay then refuses it.
|
|
153
|
-
if (field === undefined)
|
|
184
|
+
if (field === undefined) {
|
|
154
185
|
return { ...args, value: REDACTED_FILL };
|
|
186
|
+
}
|
|
155
187
|
// The variable that supplies it, beside the placeholder: the file alone says what to set.
|
|
156
188
|
return { ...args, value: REDACTED_FILL, [SECRET_NAME_ARG]: secretEnvKey(field) };
|
|
157
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* An anchored flow with each credential fill redacted by its field name.
|
|
192
|
+
*
|
|
193
|
+
* An in-page recorder of an older SDK redacts by an English rule only, and its flow arrives here
|
|
194
|
+
* already anchored. A value that is already a placeholder is left as it is, with its variable name.
|
|
195
|
+
*/
|
|
196
|
+
function redactSavedFills(flow) {
|
|
197
|
+
let redacted = false;
|
|
198
|
+
const redact = (step) => {
|
|
199
|
+
const value = step.args?.['value'];
|
|
200
|
+
let out = step;
|
|
201
|
+
if (step.args !== undefined &&
|
|
202
|
+
'string' === typeof value &&
|
|
203
|
+
REDACTED_FILL !== value &&
|
|
204
|
+
REDACTED_VALUE !== value) {
|
|
205
|
+
const args = redactSecretFill(step.anchor, step.args);
|
|
206
|
+
if (args !== step.args) {
|
|
207
|
+
redacted = true;
|
|
208
|
+
out = { ...step, args };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return step.steps === undefined ? out : { ...out, steps: step.steps.map(redact) };
|
|
212
|
+
};
|
|
213
|
+
const steps = flow.steps.map(redact);
|
|
214
|
+
// Only a fill redacted here needs a variable name. A flow that already names its variables keeps
|
|
215
|
+
// them: two fills that share one older name must not be renamed.
|
|
216
|
+
return redacted ? withDistinctSecretKeys({ ...flow, steps }) : flow;
|
|
217
|
+
}
|
|
158
218
|
/** Whether a recorded step's args carry the SDK's secret mark. */
|
|
159
219
|
const markedSecret = (args) => true === args[SECRET_FIELD_ARG];
|
|
160
220
|
/**
|
|
@@ -176,8 +236,9 @@ export function redactRecordedSteps(steps) {
|
|
|
176
236
|
/** One normalized act's args with its inner fill `args` redacted by the anchor it would save as. */
|
|
177
237
|
function redactNormalizedArgs(normalized) {
|
|
178
238
|
const inner = normalized['args'];
|
|
179
|
-
if ('object' !== typeof inner || null === inner)
|
|
239
|
+
if ('object' !== typeof inner || null === inner) {
|
|
180
240
|
return normalized;
|
|
241
|
+
}
|
|
181
242
|
const redacted = redactSecretFill(anchorForStep(normalized).anchor, asRecord(inner), markedSecret(normalized));
|
|
182
243
|
return redacted === inner ? normalized : { ...normalized, args: redacted };
|
|
183
244
|
}
|
|
@@ -195,12 +256,15 @@ function redactNormalizedArgs(normalized) {
|
|
|
195
256
|
* the placeholder into the form. One function decides it for both sides, or the two drift again.
|
|
196
257
|
*/
|
|
197
258
|
export function anchorFieldName(anchor) {
|
|
198
|
-
if (AnchorKind.TESTID === anchor.kind)
|
|
259
|
+
if (AnchorKind.TESTID === anchor.kind) {
|
|
199
260
|
return anchor.value;
|
|
200
|
-
|
|
261
|
+
}
|
|
262
|
+
if (AnchorKind.ROLE === anchor.kind) {
|
|
201
263
|
return anchor.name;
|
|
202
|
-
|
|
264
|
+
}
|
|
265
|
+
if (AnchorKind.SIGNAL === anchor.kind) {
|
|
203
266
|
return anchor.name;
|
|
267
|
+
}
|
|
204
268
|
return undefined;
|
|
205
269
|
}
|
|
206
270
|
/**
|
|
@@ -216,10 +280,12 @@ function keepRoleSource(step, src) {
|
|
|
216
280
|
}
|
|
217
281
|
function buildStep(tool, anchor, action, args, degraded, secret = false) {
|
|
218
282
|
const step = { tool, anchor, args: redactSecretFill(anchor, args, secret) };
|
|
219
|
-
if (action !== undefined)
|
|
283
|
+
if (action !== undefined) {
|
|
220
284
|
step.action = action;
|
|
221
|
-
|
|
285
|
+
}
|
|
286
|
+
if (degraded) {
|
|
222
287
|
step.degraded = true;
|
|
288
|
+
}
|
|
223
289
|
return step;
|
|
224
290
|
}
|
|
225
291
|
/**
|
|
@@ -229,44 +295,51 @@ function buildStep(tool, anchor, action, args, degraded, secret = false) {
|
|
|
229
295
|
*/
|
|
230
296
|
export function recordedStepToFlowStep(step) {
|
|
231
297
|
const out = anchoredStep(step);
|
|
232
|
-
if (step.timeoutMs !== undefined)
|
|
298
|
+
if (step.timeoutMs !== undefined) {
|
|
233
299
|
out.timeoutMs = step.timeoutMs;
|
|
234
|
-
|
|
300
|
+
}
|
|
301
|
+
if (step.burstMs !== undefined) {
|
|
302
|
+
out.burstMs = step.burstMs;
|
|
303
|
+
}
|
|
304
|
+
if (step.page !== undefined) {
|
|
235
305
|
out.page = step.page;
|
|
236
|
-
|
|
306
|
+
}
|
|
307
|
+
if (step.endPage !== undefined && step.endPage !== step.page) {
|
|
237
308
|
out.endPage = step.endPage;
|
|
238
|
-
|
|
309
|
+
}
|
|
310
|
+
if (true === step.writes) {
|
|
239
311
|
out.writes = true;
|
|
312
|
+
}
|
|
240
313
|
return out;
|
|
241
314
|
}
|
|
242
315
|
function anchoredStep(step) {
|
|
243
316
|
if (step.invoke !== undefined) {
|
|
244
317
|
// An invocation drives nothing: no action, no args, and an anchor only because every step
|
|
245
|
-
// carries one.
|
|
246
|
-
// replayer would try to dispatch a step whose whole meaning is "go and run that document".
|
|
318
|
+
// carries one. Below, it would get an `action` that a replayer would try to dispatch.
|
|
247
319
|
const out = {
|
|
248
320
|
tool: FlowStepTool.INVOKE,
|
|
249
321
|
anchor: { kind: AnchorKind.TESTID, value: step.invoke },
|
|
250
322
|
invoke: step.invoke,
|
|
251
323
|
};
|
|
252
|
-
if (step.expect !== undefined)
|
|
324
|
+
if (step.expect !== undefined) {
|
|
253
325
|
out.expect = step.expect;
|
|
326
|
+
}
|
|
254
327
|
return out;
|
|
255
328
|
}
|
|
256
329
|
if (step.tool === ReticleTool.ACT_SEQUENCE) {
|
|
257
330
|
const rawSubs = Array.isArray(step.args['steps']) ? step.args['steps'] : [];
|
|
258
331
|
const subs = rawSubs.map(subStepToFlowStep);
|
|
259
332
|
const degraded = subs.some((s) => true === s.degraded);
|
|
260
|
-
// The first sub-step that HAS an anchor
|
|
261
|
-
//
|
|
262
|
-
// sub-step perfectly anchored — and replay then queried the DOM for a testid literally named
|
|
263
|
-
// "unresolved", so the step drifted on all 5 apps, every replay.
|
|
333
|
+
// The first sub-step that HAS an anchor: subs[0] gave a sequence the degraded sentinel whenever
|
|
334
|
+
// sub-step 0 lacked a testid, and replay looked for a testid named "unresolved" on every app.
|
|
264
335
|
const anchor = subs.find((s) => s.degraded !== true)?.anchor ?? subs[0]?.anchor ?? degradedAnchor();
|
|
265
336
|
const out = { tool: ReticleTool.ACT_SEQUENCE, anchor, steps: subs };
|
|
266
|
-
if (degraded)
|
|
337
|
+
if (degraded) {
|
|
267
338
|
out.degraded = true;
|
|
268
|
-
|
|
339
|
+
}
|
|
340
|
+
if (step.expect !== undefined) {
|
|
269
341
|
out.expect = step.expect;
|
|
342
|
+
}
|
|
270
343
|
return out;
|
|
271
344
|
}
|
|
272
345
|
const action = asString(step.args['action']);
|
|
@@ -274,14 +347,16 @@ function anchoredStep(step) {
|
|
|
274
347
|
const { anchor, degraded } = anchorForStep(step.args);
|
|
275
348
|
const out = buildStep(step.tool, anchor, action, args, degraded, markedSecret(step.args));
|
|
276
349
|
keepRoleSource(out, step.args);
|
|
277
|
-
if (step.expect !== undefined)
|
|
350
|
+
if (step.expect !== undefined) {
|
|
278
351
|
out.expect = step.expect;
|
|
352
|
+
}
|
|
279
353
|
return out;
|
|
280
354
|
}
|
|
281
355
|
/** Apply folded annotations onto an anchored flow (pure): per-step expect, dynamic[], success, intent. */
|
|
282
356
|
function withAnnotations(flow, ann) {
|
|
283
|
-
if (ann === undefined)
|
|
357
|
+
if (ann === undefined) {
|
|
284
358
|
return flow;
|
|
359
|
+
}
|
|
285
360
|
const steps = flow.steps.map((step, i) => {
|
|
286
361
|
const expect = ann.stepExpect.get(i);
|
|
287
362
|
return expect === undefined ? step : { ...step, expect };
|
|
@@ -290,10 +365,12 @@ function withAnnotations(flow, ann) {
|
|
|
290
365
|
if (ann.dynamic.length > 0) {
|
|
291
366
|
out.dynamic = ann.dynamic.map((value) => ({ kind: AnchorKind.TESTID, value }));
|
|
292
367
|
}
|
|
293
|
-
if (ann.success !== undefined)
|
|
368
|
+
if (ann.success !== undefined) {
|
|
294
369
|
out.success = ann.success;
|
|
295
|
-
|
|
370
|
+
}
|
|
371
|
+
if (ann.intent !== undefined) {
|
|
296
372
|
out.intent = ann.intent;
|
|
373
|
+
}
|
|
297
374
|
return out;
|
|
298
375
|
}
|
|
299
376
|
const JSON_INDENT = 2;
|
|
@@ -429,18 +506,21 @@ export class FlowStore {
|
|
|
429
506
|
* fill its gaps and are removed. The same scan collapses copies saved before this existed.
|
|
430
507
|
*/
|
|
431
508
|
async saveJourney(program, projectId, prefix) {
|
|
432
|
-
if (!isValidFlowName(program.name))
|
|
509
|
+
if (!isValidFlowName(program.name)) {
|
|
433
510
|
return { ok: false, code: FlowErrorCode.INVALID_NAME };
|
|
511
|
+
}
|
|
434
512
|
const pid = safeProjectId(projectId);
|
|
435
513
|
const fresh = this.#build(program, undefined, pid);
|
|
436
514
|
const saved = [];
|
|
437
515
|
const freshPrint = journeyFingerprint(fresh);
|
|
438
516
|
for (const name of await this.list(pid)) {
|
|
439
517
|
const loaded = await this.load(name, pid);
|
|
440
|
-
if (!loaded.ok)
|
|
518
|
+
if (!loaded.ok) {
|
|
441
519
|
continue;
|
|
442
|
-
|
|
520
|
+
}
|
|
521
|
+
if (name.startsWith(prefix)) {
|
|
443
522
|
saved.push(loaded.value);
|
|
523
|
+
}
|
|
444
524
|
// The agent already saved this journey under its own name: one journey, one flow. A web drive
|
|
445
525
|
// saved its named flow and an automatic `drive-saved-<email>` beside it, every time.
|
|
446
526
|
else if (journeyFingerprint(loaded.value) === freshPrint) {
|
|
@@ -456,11 +536,13 @@ export class FlowStore {
|
|
|
456
536
|
const taken = new Map(saved.map((flow) => [flow.name, journeyFingerprint(flow)]));
|
|
457
537
|
// The same name holding this journey driven with a step more or less: one file, the fuller drive.
|
|
458
538
|
const same = saved.find((flow) => flow.name === fresh.name && taken.get(flow.name) !== print);
|
|
459
|
-
if (same !== undefined && withinJourney(same, fresh))
|
|
539
|
+
if (same !== undefined && withinJourney(same, fresh)) {
|
|
460
540
|
return { ok: true, value: this.#summary(same) };
|
|
541
|
+
}
|
|
461
542
|
const extended = same !== undefined && withinJourney(fresh, same);
|
|
462
|
-
if (extended)
|
|
543
|
+
if (extended) {
|
|
463
544
|
taken.delete(fresh.name);
|
|
545
|
+
}
|
|
464
546
|
const named = {
|
|
465
547
|
...fresh,
|
|
466
548
|
...(extended && fresh.intent === undefined && same.intent !== undefined
|
|
@@ -471,19 +553,23 @@ export class FlowStore {
|
|
|
471
553
|
byPrint.set(print, [...(byPrint.get(print) ?? []), named]);
|
|
472
554
|
let result = named;
|
|
473
555
|
for (const [groupPrint, group] of byPrint) {
|
|
474
|
-
if (group.length < 2 && groupPrint !== print)
|
|
556
|
+
if (group.length < 2 && groupPrint !== print) {
|
|
475
557
|
continue;
|
|
558
|
+
}
|
|
476
559
|
const [kept, ...rest] = [...group].sort((a, b) => a.createdAt - b.createdAt);
|
|
477
|
-
if (kept === undefined)
|
|
560
|
+
if (kept === undefined) {
|
|
478
561
|
continue;
|
|
562
|
+
}
|
|
479
563
|
const merged = await this.#linkIntent(rest.reduce(mergeJourney, kept));
|
|
480
564
|
await this.#writeInPlace(merged, pid);
|
|
481
565
|
for (const dropped of rest) {
|
|
482
|
-
if (dropped.name !== merged.name && dropped !== named)
|
|
566
|
+
if (dropped.name !== merged.name && dropped !== named) {
|
|
483
567
|
await this.remove(dropped.name, pid);
|
|
568
|
+
}
|
|
484
569
|
}
|
|
485
|
-
if (groupPrint === print)
|
|
570
|
+
if (groupPrint === print) {
|
|
486
571
|
result = merged;
|
|
572
|
+
}
|
|
487
573
|
}
|
|
488
574
|
this.#onWrote?.();
|
|
489
575
|
return { ok: true, value: this.#summary(result) };
|
|
@@ -501,8 +587,9 @@ export class FlowStore {
|
|
|
501
587
|
* re-run FlowFileSchema before writing. save is left untouched.
|
|
502
588
|
*/
|
|
503
589
|
async saveFlow(flow, projectId) {
|
|
504
|
-
if (!isValidFlowName(flow.name))
|
|
590
|
+
if (!isValidFlowName(flow.name)) {
|
|
505
591
|
return { ok: false, code: FlowErrorCode.INVALID_NAME };
|
|
592
|
+
}
|
|
506
593
|
const pid = safeProjectId(projectId);
|
|
507
594
|
// Stamp the project INTO the file (so a flow carries its own scope) and route it to the matching
|
|
508
595
|
// per-project subdir. Both come from the same `pid`, so on-disk location and content always agree.
|
|
@@ -517,7 +604,7 @@ export class FlowStore {
|
|
|
517
604
|
detail: describeFlowZodFailure(parsed.error),
|
|
518
605
|
};
|
|
519
606
|
}
|
|
520
|
-
const valid = await this.#linkIntent(readyForReplay(parsed.data));
|
|
607
|
+
const valid = await this.#linkIntent(readyForReplay(redactSavedFills(parsed.data)));
|
|
521
608
|
await this.#fs.mkdir(flowParentDir(this.#root, asFlowName(valid.name), pid));
|
|
522
609
|
await this.#fs.writeFile(flowPath(this.#root, asFlowName(valid.name), pid), this.#serialize(valid));
|
|
523
610
|
this.#onWrote?.();
|
|
@@ -578,8 +665,9 @@ export class FlowStore {
|
|
|
578
665
|
}
|
|
579
666
|
/** The `.json` basenames (no extension) directly inside `dir`. [] if the dir is absent/unreadable. */
|
|
580
667
|
async #namesIn(dir) {
|
|
581
|
-
if (!(await this.#fs.exists(dir)))
|
|
668
|
+
if (!(await this.#fs.exists(dir))) {
|
|
582
669
|
return [];
|
|
670
|
+
}
|
|
583
671
|
let entries;
|
|
584
672
|
try {
|
|
585
673
|
entries = await this.#fs.readdir(dir);
|
|
@@ -603,8 +691,9 @@ export class FlowStore {
|
|
|
603
691
|
*/
|
|
604
692
|
async list(projectId) {
|
|
605
693
|
const own = await this.#listOwn(projectId);
|
|
606
|
-
if (this.#main === undefined)
|
|
694
|
+
if (this.#main === undefined) {
|
|
607
695
|
return own;
|
|
696
|
+
}
|
|
608
697
|
return [...new Set([...own, ...(await this.#main.list(projectId))])].sort();
|
|
609
698
|
}
|
|
610
699
|
async #listOwn(projectId) {
|
|
@@ -615,8 +704,9 @@ export class FlowStore {
|
|
|
615
704
|
const own = await this.#namesIn(flowDir(this.#root, pid));
|
|
616
705
|
return [...new Set([...own, ...legacy])].sort();
|
|
617
706
|
}
|
|
618
|
-
if (!(await this.#fs.exists(flowsDir)))
|
|
707
|
+
if (!(await this.#fs.exists(flowsDir))) {
|
|
619
708
|
return [];
|
|
709
|
+
}
|
|
620
710
|
let entries;
|
|
621
711
|
try {
|
|
622
712
|
entries = await this.#fs.readdir(flowsDir);
|
|
@@ -635,24 +725,28 @@ export class FlowStore {
|
|
|
635
725
|
async #resolveReadPath(name, pid) {
|
|
636
726
|
if (pid !== undefined) {
|
|
637
727
|
const nested = flowPath(this.#root, name, pid);
|
|
638
|
-
if (await this.#fs.exists(nested))
|
|
728
|
+
if (await this.#fs.exists(nested)) {
|
|
639
729
|
return nested;
|
|
730
|
+
}
|
|
640
731
|
}
|
|
641
732
|
const flat = flowPath(this.#root, name);
|
|
642
|
-
if (await this.#fs.exists(flat))
|
|
733
|
+
if (await this.#fs.exists(flat)) {
|
|
643
734
|
return flat;
|
|
735
|
+
}
|
|
644
736
|
// No projectId (CLI/CI/contract callers, e.g. reticle_domain): mirror list's subdir union on
|
|
645
737
|
// the read side too — a flow saved under .reticle/flows/<projectId>/ must still load, else it is
|
|
646
738
|
// listed and then silently dropped by `if (loaded.ok)` (reticle_domain reports flowCount:0).
|
|
647
|
-
if (pid === undefined)
|
|
739
|
+
if (pid === undefined) {
|
|
648
740
|
return this.#resolveNestedPath(name);
|
|
741
|
+
}
|
|
649
742
|
return null;
|
|
650
743
|
}
|
|
651
744
|
/** Scan the per-project subdirs for a flow by name — the read-side of list's no-pid union. */
|
|
652
745
|
async #resolveNestedPath(name) {
|
|
653
746
|
const flowsDir = reticleDirPaths(this.#root).flows;
|
|
654
|
-
if (!(await this.#fs.exists(flowsDir)))
|
|
747
|
+
if (!(await this.#fs.exists(flowsDir))) {
|
|
655
748
|
return null;
|
|
749
|
+
}
|
|
656
750
|
let entries;
|
|
657
751
|
try {
|
|
658
752
|
entries = await this.#fs.readdir(flowsDir);
|
|
@@ -663,8 +757,9 @@ export class FlowStore {
|
|
|
663
757
|
// Same provenance as `#namesIn`'s scan above: a subdir of `.reticle/flows/` is a projectId.
|
|
664
758
|
for (const dir of entries.filter((e) => !e.endsWith(FLOW_SUFFIX))) {
|
|
665
759
|
const nested = flowPath(this.#root, name, asProjectId(dir));
|
|
666
|
-
if (await this.#fs.exists(nested))
|
|
760
|
+
if (await this.#fs.exists(nested)) {
|
|
667
761
|
return nested;
|
|
762
|
+
}
|
|
668
763
|
}
|
|
669
764
|
return null;
|
|
670
765
|
}
|
|
@@ -674,16 +769,19 @@ export class FlowStore {
|
|
|
674
769
|
*/
|
|
675
770
|
async load(name, projectId) {
|
|
676
771
|
const own = await this.#loadOwn(name, projectId);
|
|
677
|
-
if (own.ok || own.code !== FlowErrorCode.NOT_FOUND || this.#main === undefined)
|
|
772
|
+
if (own.ok || own.code !== FlowErrorCode.NOT_FOUND || this.#main === undefined) {
|
|
678
773
|
return own;
|
|
774
|
+
}
|
|
679
775
|
return this.#main.load(name, projectId);
|
|
680
776
|
}
|
|
681
777
|
async #loadOwn(name, projectId) {
|
|
682
|
-
if (!isValidFlowName(name))
|
|
778
|
+
if (!isValidFlowName(name)) {
|
|
683
779
|
return { ok: false, code: FlowErrorCode.INVALID_NAME };
|
|
780
|
+
}
|
|
684
781
|
const path = await this.#resolveReadPath(name, safeProjectId(projectId));
|
|
685
|
-
if (null === path)
|
|
782
|
+
if (null === path) {
|
|
686
783
|
return { ok: false, code: FlowErrorCode.NOT_FOUND };
|
|
784
|
+
}
|
|
687
785
|
let text;
|
|
688
786
|
try {
|
|
689
787
|
text = await this.#fs.readFile(path);
|
|
@@ -703,11 +801,13 @@ export class FlowStore {
|
|
|
703
801
|
* not a silent no-op, so a typo doesn't read as success.
|
|
704
802
|
*/
|
|
705
803
|
async remove(name, projectId) {
|
|
706
|
-
if (!isValidFlowName(name))
|
|
804
|
+
if (!isValidFlowName(name)) {
|
|
707
805
|
return { ok: false, code: FlowErrorCode.INVALID_NAME };
|
|
806
|
+
}
|
|
708
807
|
const path = await this.#resolveReadPath(name, safeProjectId(projectId));
|
|
709
|
-
if (null === path)
|
|
808
|
+
if (null === path) {
|
|
710
809
|
return { ok: false, code: FlowErrorCode.NOT_FOUND };
|
|
810
|
+
}
|
|
711
811
|
await this.#fs.rm(path);
|
|
712
812
|
return { ok: true, value: undefined };
|
|
713
813
|
}
|
|
@@ -9,6 +9,8 @@ export interface RecordedStep {
|
|
|
9
9
|
stable: boolean;
|
|
10
10
|
/** The caller's per-step wait budget, when one was explicitly provided. */
|
|
11
11
|
timeoutMs?: number;
|
|
12
|
+
/** The act_sequence `burst` gap, when the sequence was driven with one. See FlowStep.burstMs. */
|
|
13
|
+
burstMs?: number;
|
|
12
14
|
/** Optional post-condition annotation carried into the on-disk flow's expect. */
|
|
13
15
|
expect?: Predicate;
|
|
14
16
|
/**
|
|
@@ -130,8 +132,12 @@ export declare const isAmbient: (name: string) => boolean;
|
|
|
130
132
|
export declare class RecordingStore {
|
|
131
133
|
#private;
|
|
132
134
|
private readonly byHarness;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
+
private readonly successorOf;
|
|
136
|
+
/**
|
|
137
|
+
* `byHarness`: whether the step being captured was driven by the Harness, which tapes its own.
|
|
138
|
+
* `successorOf`: the session that the same tab connected as after a page load, if any.
|
|
139
|
+
*/
|
|
140
|
+
constructor(byHarness?: () => boolean, successorOf?: (session: string) => string | undefined);
|
|
135
141
|
start(name: string, cursor: number, startPath?: string, session?: string, document?: string): void;
|
|
136
142
|
isRecording(name: string): boolean;
|
|
137
143
|
/**
|
|
@@ -201,7 +207,17 @@ export declare class RecordingStore {
|
|
|
201
207
|
saveCompiled(program: CompiledProgram): void;
|
|
202
208
|
getCompiled(name: string): CompiledProgram | undefined;
|
|
203
209
|
active(): string[];
|
|
204
|
-
/**
|
|
210
|
+
/**
|
|
211
|
+
* The ambient tape of one tab (or of steps no tab named), taken: what its session end saves.
|
|
212
|
+
*
|
|
213
|
+
* A session also ends when the app itself loads a page (a form post on a server-rendered app), and
|
|
214
|
+
* the journey goes on in the page that loaded. A tape with no check is not saved, so the next
|
|
215
|
+
* page's tape starts with a copy of it: a fill and the submit that proves it are then one flow, not
|
|
216
|
+
* a fill that is dropped and a submit on an empty form. When Reticle navigated away, the agent
|
|
217
|
+
* started a new journey, and nothing is carried.
|
|
218
|
+
* ponytail: only a tape with NO check is carried; unchecked steps after a check stay behind.
|
|
219
|
+
* ponytail: kept for the last CARRY_CAP ended pages; a tab that never loads a successor drops them.
|
|
220
|
+
*/
|
|
205
221
|
takeAmbient(session: string | undefined): ActiveRecording | undefined;
|
|
206
222
|
/**
|
|
207
223
|
* Names of recordings that have been STOPPED and compiled.
|