@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
|
@@ -13,12 +13,14 @@ import { basename, join } from 'node:path';
|
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
import { createNodeFileSystem } from '../../memory/project/fs/fs-port.js';
|
|
15
15
|
import { CLOUD_LINK_FILE, resolveProjectCloud } from '../../memory/cloud/cloud-config.js';
|
|
16
|
+
import { memoryReadUrl, MemoryResponseScope, scopeMemoryResponse, scopeOfMemoryResponse, } from '../../memory/cloud/memory-scope.js';
|
|
16
17
|
import { applyCredential, findCredential } from './auth/cloud-keystore.js';
|
|
17
18
|
import { defaultProjectFor } from './project-name.js';
|
|
18
19
|
import { RETICLE_CONFIG_BASENAME } from './ports/resolve/cli-port.js';
|
|
19
20
|
import { normalizeUrl } from './auth/cloud-session.js';
|
|
20
21
|
import { cmdLogin, cmdLogout } from './cloud-login.js';
|
|
21
22
|
import { api, baseUrl, bearer, CREDENTIALS_FILE, emit, err, flags, hint, home, readJson, readSession, readSessionFor, RETICLE_DIR, } from './cloud-kit.js';
|
|
23
|
+
import { syncRequest } from '../../memory/cloud/cloud-sync.js';
|
|
22
24
|
import { describeSync, runSyncCycle } from '../../memory/cloud/sync-cycle.js';
|
|
23
25
|
import { diskSink, diskSource, readCloudIssues, readCloudState } from '../../memory/cloud/sync-disk.js';
|
|
24
26
|
/**
|
|
@@ -42,6 +44,7 @@ import { diskSink, diskSource, readCloudIssues, readCloudState } from '../../mem
|
|
|
42
44
|
*/
|
|
43
45
|
const CLOUD_COMMANDS = new Set([
|
|
44
46
|
'login',
|
|
47
|
+
'connect',
|
|
45
48
|
'logout',
|
|
46
49
|
'whoami',
|
|
47
50
|
'link',
|
|
@@ -175,8 +178,11 @@ const cmdWhoami = async () => {
|
|
|
175
178
|
verify: cloud.verify,
|
|
176
179
|
},
|
|
177
180
|
});
|
|
181
|
+
// Signed out, `link` refuses with "run reticle login first"; `connect` does both in one go.
|
|
178
182
|
if (null === cloud.config)
|
|
179
|
-
hint(
|
|
183
|
+
hint(null === session
|
|
184
|
+
? 'this repo is not attached — run `reticle connect` (signs in and links in one step)'
|
|
185
|
+
: 'this repo is not attached — run `reticle link`');
|
|
180
186
|
return 0;
|
|
181
187
|
};
|
|
182
188
|
/** `reticle project ls` / `reticle project create <name>` — key- or session-authed. */
|
|
@@ -433,11 +439,68 @@ const cmdLink = async (argv) => {
|
|
|
433
439
|
* before they install, which is a legitimate order to work in.
|
|
434
440
|
*/
|
|
435
441
|
if (!(await createNodeFileSystem().exists(join(process.cwd(), RETICLE_CONFIG_BASENAME)))) {
|
|
436
|
-
hint(`no ${RETICLE_CONFIG_BASENAME} here
|
|
437
|
-
'
|
|
442
|
+
hint(`no ${RETICLE_CONFIG_BASENAME} here: this app's runs reach this binding only while its dev ` +
|
|
443
|
+
'server runs with the Reticle build plugin, which announces it. Run `reticle init` in the app ' +
|
|
444
|
+
'to make the binding hold without it.');
|
|
438
445
|
}
|
|
439
446
|
return 0;
|
|
440
447
|
};
|
|
448
|
+
/**
|
|
449
|
+
* `reticle connect` finishes the cloud half of first-run setup in one invocation. The CLI entry
|
|
450
|
+
* initializes an unwired app before calling this verb. A saved login is checked against this exact
|
|
451
|
+
* host before reuse; an expired one opens the normal browser approval flow. Login does not auto-link
|
|
452
|
+
* here because this command links once, to the project the caller named, then sends local history.
|
|
453
|
+
*/
|
|
454
|
+
const cmdConnect = async (argv) => {
|
|
455
|
+
for (let i = 0; i < argv.length; i += 2) {
|
|
456
|
+
const flag = argv[i];
|
|
457
|
+
const value = argv[i + 1];
|
|
458
|
+
if ((flag !== '--project' && flag !== '--url') ||
|
|
459
|
+
value === undefined ||
|
|
460
|
+
0 === value.length ||
|
|
461
|
+
value.startsWith('--')) {
|
|
462
|
+
err('usage: reticle connect [--project <name|id>] [--url <cloud origin>]');
|
|
463
|
+
return 2;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (!(await createNodeFileSystem().exists(join(process.cwd(), RETICLE_CONFIG_BASENAME)))) {
|
|
467
|
+
err('this app is not wired yet — run `reticle init` in its directory, then `reticle connect`');
|
|
468
|
+
return 2;
|
|
469
|
+
}
|
|
470
|
+
const f = flags(argv);
|
|
471
|
+
const url = baseUrl(null, f['url']);
|
|
472
|
+
if (undefined === apiKeyFrom(process.env)) {
|
|
473
|
+
const session = await readSessionFor(url);
|
|
474
|
+
let signedIn = false;
|
|
475
|
+
if (session !== null) {
|
|
476
|
+
try {
|
|
477
|
+
await api('GET', `${url}/v1/me`, session.token);
|
|
478
|
+
signedIn = true;
|
|
479
|
+
}
|
|
480
|
+
catch {
|
|
481
|
+
hint('saved sign-in needs renewal — opening browser approval');
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (!signedIn) {
|
|
485
|
+
const code = await cmdLogin(['--url', url], cmdLink, false);
|
|
486
|
+
if (code !== 0)
|
|
487
|
+
return code;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
const linkArgs = [
|
|
491
|
+
'--url',
|
|
492
|
+
url,
|
|
493
|
+
...(f['project'] === undefined ? [] : ['--project', f['project']]),
|
|
494
|
+
];
|
|
495
|
+
const linked = await cmdLink(linkArgs);
|
|
496
|
+
if (linked !== 0)
|
|
497
|
+
return linked;
|
|
498
|
+
const pushed = await cmdPush();
|
|
499
|
+
if (pushed !== 0)
|
|
500
|
+
return pushed;
|
|
501
|
+
hint('Connected. Open your coding agent and ask it to verify one flow in your app.');
|
|
502
|
+
return 0;
|
|
503
|
+
};
|
|
441
504
|
/** `reticle config [--runs on|off] [--memory on|off] [--flows on|off] [--verify local|server]`. */
|
|
442
505
|
const cmdConfig = async (argv) => {
|
|
443
506
|
const f = flags(argv);
|
|
@@ -508,10 +571,7 @@ const cmdSync = async (argv) => {
|
|
|
508
571
|
sink: diskSink(reticleRoot),
|
|
509
572
|
state: readCloudState(reticleRoot),
|
|
510
573
|
now: () => Date.now(),
|
|
511
|
-
request:
|
|
512
|
-
const res = await fetch(url, init);
|
|
513
|
-
return { status: res.status, text: await res.text() };
|
|
514
|
-
},
|
|
574
|
+
request: (url, init) => syncRequest(url, init),
|
|
515
575
|
});
|
|
516
576
|
emit({
|
|
517
577
|
ok: report.ok,
|
|
@@ -544,13 +604,25 @@ const cmdSync = async (argv) => {
|
|
|
544
604
|
};
|
|
545
605
|
/** `reticle push` — the name people already type. One cycle, same as `reticle sync`. */
|
|
546
606
|
const cmdPush = async () => cmdSync([]);
|
|
547
|
-
/**
|
|
607
|
+
/**
|
|
608
|
+
* The linked project: its cloud, and the ID the link declares. Throws a friendly error when not
|
|
609
|
+
* attached.
|
|
610
|
+
*
|
|
611
|
+
* The id rides along for `reticle memory`, whose entire job is "what does THIS project know" and
|
|
612
|
+
* which was asking with nothing but the API key. A key covers a workspace, not a repo, so on a
|
|
613
|
+
* workspace with two repos it could print a sibling's established knowledge under this project's
|
|
614
|
+
* heading with no way for the reader to tell. See `memory-scope.ts`.
|
|
615
|
+
*
|
|
616
|
+
* The other `repoCloud()` callers below still read with the key alone. That is not an oversight
|
|
617
|
+
* being papered over: this change is scoped to the memory read, and the rest each need their own
|
|
618
|
+
* look at what the platform filters on.
|
|
619
|
+
*/
|
|
548
620
|
const repoCloud = async () => {
|
|
549
621
|
const fs = createNodeFileSystem();
|
|
550
622
|
const cloud = await resolveProjectCloud(fs, join(process.cwd(), RETICLE_DIR), homedir(), process.env);
|
|
551
623
|
if (null === cloud.config)
|
|
552
624
|
throw new Error(cloud.reason ?? 'cloud not attached here: run `reticle link`, or set RETICLE_API_KEY');
|
|
553
|
-
return cloud.config;
|
|
625
|
+
return { ...cloud.config, projectId: cloud.projectId };
|
|
554
626
|
};
|
|
555
627
|
/** `reticle runs` — the linked project's recent run artifacts (the key scopes it server-side). */
|
|
556
628
|
const cmdRuns = async () => {
|
|
@@ -622,15 +694,39 @@ const cmdIssues = async (argv) => {
|
|
|
622
694
|
* whole corpus to answer one question is the cost the sharded store was built to avoid.
|
|
623
695
|
*/
|
|
624
696
|
const cmdMemory = async (argv) => {
|
|
625
|
-
const { url, apiKey } = await repoCloud();
|
|
697
|
+
const { url, apiKey, projectId } = await repoCloud();
|
|
626
698
|
const at = argv.indexOf('--subject');
|
|
627
699
|
const subject = -1 === at ? undefined : argv[at + 1];
|
|
628
700
|
if (-1 !== at && subject === undefined) {
|
|
629
701
|
err('usage: reticle memory [--subject <name>]');
|
|
630
702
|
return 2;
|
|
631
703
|
}
|
|
632
|
-
|
|
633
|
-
|
|
704
|
+
// Scoped to the LINKED project, on the way out AND on the way back. Without the parameter
|
|
705
|
+
// this printed whatever the key covered; without checking the response it still would, for as long
|
|
706
|
+
// as the platform ignores a parameter it does not know yet — which is exactly the window this ships
|
|
707
|
+
// in. This is the command whose whole job is "what does THIS project know", so a sibling's answer
|
|
708
|
+
// arriving under this heading is the one output nobody can tell from a correct one.
|
|
709
|
+
//
|
|
710
|
+
// The check is on the ENVELOPE and it REFUSES rather than printing. Printing an empty list would
|
|
711
|
+
// report "this project knows nothing", which is a different fact from "this response could not be
|
|
712
|
+
// shown to be about this project" and the one a person is likelier to act on. Nothing goes to
|
|
713
|
+
// stdout in that case: a caller piping this into an agent gets an error on stderr and a non-zero
|
|
714
|
+
// exit, never a plausible-looking empty answer.
|
|
715
|
+
//
|
|
716
|
+
// The refusal is the PRINTER's own, not a second check here: `scopeMemoryResponse` answers
|
|
717
|
+
// `undefined` for anything it cannot show to be this project's, so there is no ordering a future
|
|
718
|
+
// edit can get wrong — the body it must not print is one it was never handed. The verdict is read
|
|
719
|
+
// only to say WHICH refusal this is.
|
|
720
|
+
const scope = { projectId, subject };
|
|
721
|
+
const body = await api('GET', memoryReadUrl(url, scope), apiKey);
|
|
722
|
+
const printable = scopeMemoryResponse(body, projectId);
|
|
723
|
+
if (printable === undefined) {
|
|
724
|
+
err(MemoryResponseScope.OTHER === scopeOfMemoryResponse(body, projectId)
|
|
725
|
+
? `the server answered about a different project than this repo is linked to — refusing to print another project's knowledge as this one's. Check the link with \`reticle status\`.`
|
|
726
|
+
: `the server's response does not say which project it is about, so there is no way to tell whether this is this project's knowledge. Refusing to print it; check the link with \`reticle status\`.`);
|
|
727
|
+
return 1;
|
|
728
|
+
}
|
|
729
|
+
emit(printable);
|
|
634
730
|
return 0;
|
|
635
731
|
};
|
|
636
732
|
/** `reticle regression` — the CI gate: broken flows vs before. Exit 3 if any regressed (pipeline-friendly). */
|
|
@@ -661,6 +757,8 @@ export const runCloudCommand = async (argv) => {
|
|
|
661
757
|
// The linker is passed in rather than imported by cloud-login: login ends by linking and
|
|
662
758
|
// link needs a session, so the cycle is broken at this one call site.
|
|
663
759
|
return await cmdLogin(rest, cmdLink);
|
|
760
|
+
case 'connect':
|
|
761
|
+
return await cmdConnect(rest);
|
|
664
762
|
case 'logout':
|
|
665
763
|
return await cmdLogout(rest);
|
|
666
764
|
case 'whoami':
|
|
@@ -4,7 +4,7 @@ type Linker = (argv: readonly string[]) => Promise<number>;
|
|
|
4
4
|
* `reticle login` — browser device flow by default; `--email <e>` (or a positional email) keeps the
|
|
5
5
|
* headless two-step code path for CI/servers where opening a browser makes no sense.
|
|
6
6
|
*/
|
|
7
|
-
export declare const cmdLogin: (argv: readonly string[], link: Linker) => Promise<number>;
|
|
7
|
+
export declare const cmdLogin: (argv: readonly string[], link: Linker, autoLink?: boolean) => Promise<number>;
|
|
8
8
|
/** `reticle logout` — forget the cached session token (per-project keys under credentials.json stay). */
|
|
9
9
|
/**
|
|
10
10
|
* `reticle logout` — sign out of ONE host, not of everywhere.
|
|
@@ -15,4 +15,12 @@ export declare const cmdLogin: (argv: readonly string[], link: Linker) => Promis
|
|
|
15
15
|
* one you did not mean.
|
|
16
16
|
*/
|
|
17
17
|
export declare const cmdLogout: (argv?: readonly string[]) => Promise<number>;
|
|
18
|
+
/**
|
|
19
|
+
* The panel's Sign in: the same browser approval `reticle login` opens, started by the daemon, which
|
|
20
|
+
* owns `~/.reticle`. One flow at a time, and a second press while it is pending reopens its approval
|
|
21
|
+
* page rather than doing nothing: somebody who closed that tab must be able to get it back.
|
|
22
|
+
* `onSignedIn` runs when the approval lands so every open panel can repaint. A failure stays in the
|
|
23
|
+
* daemon log: the panel copied `reticle login` as it was clicked, which is the way through.
|
|
24
|
+
*/
|
|
25
|
+
export declare function hudSignIn(onSignedIn: () => void, start?: (open: (url: string) => void) => Promise<number>, opener?: (url: string) => void): () => void;
|
|
18
26
|
export {};
|
|
@@ -48,7 +48,7 @@ const signedInEmail = async (url, token) => {
|
|
|
48
48
|
}
|
|
49
49
|
};
|
|
50
50
|
/** Persist a session token under ~/.reticle and print the next step. Shared by both login paths. */
|
|
51
|
-
const writeSession = async (url, token, orgName, orgId, project, link, email) => {
|
|
51
|
+
const writeSession = async (url, token, orgName, orgId, project, link, email, autoLink = true) => {
|
|
52
52
|
await mkdir(join(home(), SESSIONS_DIR), { recursive: true });
|
|
53
53
|
// Who signed in, so a flow saved on this machine can say which person made it (FlowFile.author).
|
|
54
54
|
const who = email ?? (await signedInEmail(url, token));
|
|
@@ -65,7 +65,8 @@ const writeSession = async (url, token, orgName, orgId, project, link, email) =>
|
|
|
65
65
|
await writeFile(sessionPath(home(), url), body);
|
|
66
66
|
await writeFile(join(home(), SESSION_FILE), body);
|
|
67
67
|
emit({ loggedIn: orgName, session: join(home(), SESSION_FILE) });
|
|
68
|
-
|
|
68
|
+
if (autoLink)
|
|
69
|
+
await linkAfterLogin(url, project, link);
|
|
69
70
|
};
|
|
70
71
|
/**
|
|
71
72
|
* Finish the job when the shell is already standing in an unlinked Reticle project.
|
|
@@ -124,17 +125,17 @@ const linkAfterLogin = async (url, project, link) => {
|
|
|
124
125
|
* Browser device flow — the DEFAULT `reticle login` (like `gh auth login`): fetch a device + user code,
|
|
125
126
|
* open the browser to approve, then poll until the user confirms. No email to type, no code to copy back.
|
|
126
127
|
*/
|
|
127
|
-
const cmdLoginDevice = async (explicitUrl, project, link) => {
|
|
128
|
+
const cmdLoginDevice = async (explicitUrl, project, link, autoLink, open = openBrowser) => {
|
|
128
129
|
const url = baseUrl(null, explicitUrl);
|
|
129
130
|
const started = DeviceStartSchema.parse(await api('POST', `${url}/v1/auth/device/start`, null, {}));
|
|
130
131
|
hint(`Opening ${started.verificationUri} — confirm this code in the browser: ${started.userCode}`);
|
|
131
|
-
|
|
132
|
+
open(started.verificationUriComplete);
|
|
132
133
|
const intervalMs = Math.max(1, started.interval) * 1000;
|
|
133
134
|
for (;;) {
|
|
134
135
|
await sleep(intervalMs);
|
|
135
136
|
const poll = DevicePollSchema.parse(await api('POST', `${url}/v1/auth/device/token`, null, { deviceCode: started.deviceCode }));
|
|
136
137
|
if ('approved' === poll.status && poll.token !== undefined && poll.org !== undefined) {
|
|
137
|
-
await writeSession(url, poll.token, poll.org.name, poll.org.id, project, link);
|
|
138
|
+
await writeSession(url, poll.token, poll.org.name, poll.org.id, project, link, undefined, autoLink);
|
|
138
139
|
return 0;
|
|
139
140
|
}
|
|
140
141
|
if ('pending' === poll.status) {
|
|
@@ -154,12 +155,12 @@ const cmdLoginDevice = async (explicitUrl, project, link) => {
|
|
|
154
155
|
* `reticle login` — browser device flow by default; `--email <e>` (or a positional email) keeps the
|
|
155
156
|
* headless two-step code path for CI/servers where opening a browser makes no sense.
|
|
156
157
|
*/
|
|
157
|
-
export const cmdLogin = async (argv, link) => {
|
|
158
|
+
export const cmdLogin = async (argv, link, autoLink = true) => {
|
|
158
159
|
const f = flags(argv);
|
|
159
160
|
const positional = argv[0] !== undefined && !argv[0].startsWith('--') ? argv[0] : undefined;
|
|
160
161
|
const email = f['email'] ?? positional;
|
|
161
162
|
if (email === undefined)
|
|
162
|
-
return cmdLoginDevice(f['url'], f['project'], link);
|
|
163
|
+
return cmdLoginDevice(f['url'], f['project'], link, autoLink);
|
|
163
164
|
const org = f['org'];
|
|
164
165
|
const url = baseUrl(null, f['url']);
|
|
165
166
|
let code = f['code'];
|
|
@@ -177,7 +178,7 @@ export const cmdLogin = async (argv, link) => {
|
|
|
177
178
|
code = requested.devCode;
|
|
178
179
|
}
|
|
179
180
|
const parsed = LoginSchema.parse(await api('POST', `${url}/v1/auth/login`, null, { email, code }));
|
|
180
|
-
await writeSession(url, parsed.token, parsed.org.name, parsed.org.id, f['project'], link, email);
|
|
181
|
+
await writeSession(url, parsed.token, parsed.org.name, parsed.org.id, f['project'], link, email, autoLink);
|
|
181
182
|
return 0;
|
|
182
183
|
};
|
|
183
184
|
/** `reticle logout` — forget the cached session token (per-project keys under credentials.json stay). */
|
|
@@ -199,3 +200,37 @@ export const cmdLogout = async (argv = []) => {
|
|
|
199
200
|
emit({ loggedOut: true, url });
|
|
200
201
|
return 0;
|
|
201
202
|
};
|
|
203
|
+
/** The daemon signs in for the panel and links nothing: its working directory is not the user's app. */
|
|
204
|
+
const NO_LINK = () => Promise.resolve(0);
|
|
205
|
+
/**
|
|
206
|
+
* The panel's Sign in: the same browser approval `reticle login` opens, started by the daemon, which
|
|
207
|
+
* owns `~/.reticle`. One flow at a time, and a second press while it is pending reopens its approval
|
|
208
|
+
* page rather than doing nothing: somebody who closed that tab must be able to get it back.
|
|
209
|
+
* `onSignedIn` runs when the approval lands so every open panel can repaint. A failure stays in the
|
|
210
|
+
* daemon log: the panel copied `reticle login` as it was clicked, which is the way through.
|
|
211
|
+
*/
|
|
212
|
+
export function hudSignIn(onSignedIn, start = (open) => cmdLoginDevice(undefined, undefined, NO_LINK, false, open), opener = openBrowser) {
|
|
213
|
+
let running = false;
|
|
214
|
+
let pending;
|
|
215
|
+
return () => {
|
|
216
|
+
if (running) {
|
|
217
|
+
if (pending !== undefined)
|
|
218
|
+
opener(pending);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
running = true;
|
|
222
|
+
void start((url) => {
|
|
223
|
+
pending = url;
|
|
224
|
+
opener(url);
|
|
225
|
+
})
|
|
226
|
+
.then((code) => {
|
|
227
|
+
if (0 === code)
|
|
228
|
+
onSignedIn();
|
|
229
|
+
})
|
|
230
|
+
.catch(() => undefined)
|
|
231
|
+
.finally(() => {
|
|
232
|
+
running = false;
|
|
233
|
+
pending = undefined;
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
import { fileURLToPath } from 'node:url';
|
|
31
31
|
import { createServer } from 'node:http';
|
|
32
32
|
import { dirname, join, relative, extname, sep } from 'node:path';
|
|
33
|
-
import { existsSync, readFileSync, readdirSync
|
|
33
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
34
34
|
/** Where the tour starts. Everything else on the page is reached from here. */
|
|
35
35
|
const SDK_ENTRY = '@reticlehq/browser';
|
|
36
36
|
/** The signal the demo app fires when it saves — the consequence the tour declares in advance. */
|
|
@@ -208,13 +208,21 @@ export function serveDemoPage(html, graph) {
|
|
|
208
208
|
continue;
|
|
209
209
|
const file = join(mount.dir, path.slice(mount.prefix.length));
|
|
210
210
|
// A request that climbed out of the mount is not served, whatever it points at.
|
|
211
|
-
if (!file.startsWith(mount.dir + sep)
|
|
211
|
+
if (!file.startsWith(mount.dir + sep))
|
|
212
|
+
break;
|
|
213
|
+
// One read, no prior exists/stat check: a missing file or a directory throws here, and there
|
|
214
|
+
// is no window between a check and the read for the path to change under it.
|
|
215
|
+
let body;
|
|
216
|
+
try {
|
|
217
|
+
body = readFileSync(file);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
212
220
|
break;
|
|
213
221
|
}
|
|
214
222
|
res.writeHead(200, {
|
|
215
223
|
'content-type': '.js' === extname(file) ? JS_CONTENT_TYPE : 'application/octet-stream',
|
|
216
224
|
});
|
|
217
|
-
res.end(
|
|
225
|
+
res.end(body);
|
|
218
226
|
return;
|
|
219
227
|
}
|
|
220
228
|
res.writeHead(404);
|
|
@@ -48,7 +48,45 @@ export interface ChromiumProbe {
|
|
|
48
48
|
* `Microsoft Edge`) — see launch-chromium. Present only when `exists` is false and one was found.
|
|
49
49
|
*/
|
|
50
50
|
fallback?: string | undefined;
|
|
51
|
+
/** `executablePath` came from RETICLE_CHROMIUM_PATH, so the bundled build is not in play at all. */
|
|
52
|
+
configured?: boolean | undefined;
|
|
53
|
+
/** Why that configured executable cannot be launched. Present only when `exists` is false. */
|
|
54
|
+
problem?: ChromiumPathProblem | undefined;
|
|
51
55
|
}
|
|
56
|
+
/** What stops the executable RETICLE_CHROMIUM_PATH names from being launched. */
|
|
57
|
+
export declare const ChromiumPathProblem: {
|
|
58
|
+
readonly MISSING: "missing";
|
|
59
|
+
readonly NOT_A_FILE: "not-a-file";
|
|
60
|
+
readonly NOT_EXECUTABLE: "not-executable";
|
|
61
|
+
};
|
|
62
|
+
export type ChromiumPathProblem = (typeof ChromiumPathProblem)[keyof typeof ChromiumPathProblem];
|
|
63
|
+
/**
|
|
64
|
+
* What stops the file at `path` from being launched, or undefined when nothing does.
|
|
65
|
+
*
|
|
66
|
+
* Existing is not enough: Playwright cannot start a directory or a file without the executable bit,
|
|
67
|
+
* and its error for that reads like a missing install. Windows has no executable bit, so only the
|
|
68
|
+
* file check applies there.
|
|
69
|
+
*/
|
|
70
|
+
export declare function chromiumPathProblem(path: string, platform: NodeJS.Platform): ChromiumPathProblem | undefined;
|
|
71
|
+
/** The Chromium RETICLE_CHROMIUM_PATH names, or undefined when it is unset or blank. */
|
|
72
|
+
export declare function configuredChromiumPath(env: NodeJS.ProcessEnv): string | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* What the probes report when RETICLE_CHROMIUM_PATH is set, or undefined when it is not. The user
|
|
75
|
+
* named the browser, so the bundled revision, the installed channels and the browsers root say
|
|
76
|
+
* nothing about whether a launch will work. Only the named file does.
|
|
77
|
+
*/
|
|
78
|
+
export declare function configuredChromiumProbe(env: NodeJS.ProcessEnv, pathProblem: (path: string) => ChromiumPathProblem | undefined): ChromiumProbe | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* How every RETICLE_CHROMIUM_PATH refusal begins. Error recovery keys on it, so the variable's name
|
|
81
|
+
* is read as configuration rather than as a `reticle_*` tool the call got wrong.
|
|
82
|
+
*/
|
|
83
|
+
export declare const CHROMIUM_PATH_REFUSAL_PREFIX: string;
|
|
84
|
+
/**
|
|
85
|
+
* A RETICLE_CHROMIUM_PATH that cannot be launched. Worded apart from the missing-browser hint on
|
|
86
|
+
* purpose: that one sends the reader to install Playwright's build, which does nothing for a path
|
|
87
|
+
* typed wrong.
|
|
88
|
+
*/
|
|
89
|
+
export declare function configuredChromiumRefusal(path: string, problem: ChromiumPathProblem): string;
|
|
52
90
|
/** The install command, pinned when we know what to pin to. */
|
|
53
91
|
export declare function chromiumInstallCommand(playwrightVersion?: string): string;
|
|
54
92
|
/**
|
|
@@ -83,5 +121,13 @@ export declare function parseChromiumRevision(executablePath: string): {
|
|
|
83
121
|
export declare function probeChromium(): Promise<ChromiumProbe>;
|
|
84
122
|
/** The bundled playwright's version, or undefined if it cannot be read (the hint then goes unpinned). */
|
|
85
123
|
export declare function bundledPlaywrightVersion(): string | undefined;
|
|
124
|
+
/**
|
|
125
|
+
* Why the lease preflight refuses, or undefined when a launch can go ahead.
|
|
126
|
+
*
|
|
127
|
+
* "Chromium is not installed" is kept in the usual message because error-recovery routes it to the
|
|
128
|
+
* install fix. A RETICLE_CHROMIUM_PATH that cannot be launched is a wrong path, not a missing
|
|
129
|
+
* install, so it says only that and the install fix is never attached.
|
|
130
|
+
*/
|
|
131
|
+
export declare function chromiumPreflightRefusal(probe: ChromiumProbe): string | undefined;
|
|
86
132
|
/** The doctor line for the Chromium check — verdict, what was probed, and how to satisfy it. */
|
|
87
133
|
export declare function chromiumHint(probe: ChromiumProbe): string;
|
|
@@ -20,8 +20,86 @@
|
|
|
20
20
|
* resolves, so `PLAYWRIGHT_BROWSERS_PATH` and every platform default are honoured by construction
|
|
21
21
|
* rather than by a table of roots this file would have to keep in step with Playwright's.
|
|
22
22
|
*/
|
|
23
|
-
import { existsSync, readdirSync } from 'node:fs';
|
|
23
|
+
import { accessSync, constants as fsConstants, existsSync, readdirSync, statSync } from 'node:fs';
|
|
24
24
|
import { createRequire } from 'node:module';
|
|
25
|
+
import { ReticleEnv } from '@reticlehq/core';
|
|
26
|
+
/** What stops the executable RETICLE_CHROMIUM_PATH names from being launched. */
|
|
27
|
+
export const ChromiumPathProblem = {
|
|
28
|
+
MISSING: 'missing',
|
|
29
|
+
NOT_A_FILE: 'not-a-file',
|
|
30
|
+
NOT_EXECUTABLE: 'not-executable',
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* What stops the file at `path` from being launched, or undefined when nothing does.
|
|
34
|
+
*
|
|
35
|
+
* Existing is not enough: Playwright cannot start a directory or a file without the executable bit,
|
|
36
|
+
* and its error for that reads like a missing install. Windows has no executable bit, so only the
|
|
37
|
+
* file check applies there.
|
|
38
|
+
*/
|
|
39
|
+
export function chromiumPathProblem(path, platform) {
|
|
40
|
+
let isFile;
|
|
41
|
+
try {
|
|
42
|
+
isFile = statSync(path).isFile();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return ChromiumPathProblem.MISSING;
|
|
46
|
+
}
|
|
47
|
+
if (!isFile)
|
|
48
|
+
return ChromiumPathProblem.NOT_A_FILE;
|
|
49
|
+
if ('win32' === platform)
|
|
50
|
+
return undefined;
|
|
51
|
+
try {
|
|
52
|
+
accessSync(path, fsConstants.X_OK);
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return ChromiumPathProblem.NOT_EXECUTABLE;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** The Chromium RETICLE_CHROMIUM_PATH names, or undefined when it is unset or blank. */
|
|
60
|
+
export function configuredChromiumPath(env) {
|
|
61
|
+
const path = env[ReticleEnv.CHROMIUM_PATH]?.trim();
|
|
62
|
+
return path === undefined || 0 === path.length ? undefined : path;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* What the probes report when RETICLE_CHROMIUM_PATH is set, or undefined when it is not. The user
|
|
66
|
+
* named the browser, so the bundled revision, the installed channels and the browsers root say
|
|
67
|
+
* nothing about whether a launch will work. Only the named file does.
|
|
68
|
+
*/
|
|
69
|
+
export function configuredChromiumProbe(env, pathProblem) {
|
|
70
|
+
const path = configuredChromiumPath(env);
|
|
71
|
+
if (path === undefined)
|
|
72
|
+
return undefined;
|
|
73
|
+
const problem = pathProblem(path);
|
|
74
|
+
return {
|
|
75
|
+
executablePath: path,
|
|
76
|
+
exists: problem === undefined,
|
|
77
|
+
configured: true,
|
|
78
|
+
...(problem === undefined ? {} : { problem }),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* How every RETICLE_CHROMIUM_PATH refusal begins. Error recovery keys on it, so the variable's name
|
|
83
|
+
* is read as configuration rather than as a `reticle_*` tool the call got wrong.
|
|
84
|
+
*/
|
|
85
|
+
export const CHROMIUM_PATH_REFUSAL_PREFIX = `${ReticleEnv.CHROMIUM_PATH} points at `;
|
|
86
|
+
/** What each problem reads as, after the path. */
|
|
87
|
+
const CHROMIUM_PATH_PROBLEM_TEXT = {
|
|
88
|
+
[ChromiumPathProblem.MISSING]: 'which does not exist',
|
|
89
|
+
[ChromiumPathProblem.NOT_A_FILE]: 'which is not a file',
|
|
90
|
+
[ChromiumPathProblem.NOT_EXECUTABLE]: 'which is not executable',
|
|
91
|
+
};
|
|
92
|
+
const CHROMIUM_PATH_FIX = "fix the path, or unset it to use Playwright's own Chromium";
|
|
93
|
+
/** The doctor verdict for a RETICLE_CHROMIUM_PATH that can be launched. */
|
|
94
|
+
const CHROMIUM_PATH_IN_USE = (path) => `✓ using ${path} (${ReticleEnv.CHROMIUM_PATH})`;
|
|
95
|
+
/**
|
|
96
|
+
* A RETICLE_CHROMIUM_PATH that cannot be launched. Worded apart from the missing-browser hint on
|
|
97
|
+
* purpose: that one sends the reader to install Playwright's build, which does nothing for a path
|
|
98
|
+
* typed wrong.
|
|
99
|
+
*/
|
|
100
|
+
export function configuredChromiumRefusal(path, problem) {
|
|
101
|
+
return `${CHROMIUM_PATH_REFUSAL_PREFIX}${path}, ${CHROMIUM_PATH_PROBLEM_TEXT[problem]}; ${CHROMIUM_PATH_FIX}`;
|
|
102
|
+
}
|
|
25
103
|
/**
|
|
26
104
|
* A browsers-root entry that is a chromium build.
|
|
27
105
|
*
|
|
@@ -160,8 +238,28 @@ export function bundledPlaywrightVersion() {
|
|
|
160
238
|
}
|
|
161
239
|
return undefined;
|
|
162
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Why the lease preflight refuses, or undefined when a launch can go ahead.
|
|
243
|
+
*
|
|
244
|
+
* "Chromium is not installed" is kept in the usual message because error-recovery routes it to the
|
|
245
|
+
* install fix. A RETICLE_CHROMIUM_PATH that cannot be launched is a wrong path, not a missing
|
|
246
|
+
* install, so it says only that and the install fix is never attached.
|
|
247
|
+
*/
|
|
248
|
+
export function chromiumPreflightRefusal(probe) {
|
|
249
|
+
if (probe.exists)
|
|
250
|
+
return undefined;
|
|
251
|
+
if (true === probe.configured && probe.executablePath !== undefined) {
|
|
252
|
+
return configuredChromiumRefusal(probe.executablePath, probe.problem ?? ChromiumPathProblem.MISSING);
|
|
253
|
+
}
|
|
254
|
+
return `Chromium is not installed for Playwright — ${chromiumHint(probe)}`;
|
|
255
|
+
}
|
|
163
256
|
/** The doctor line for the Chromium check — verdict, what was probed, and how to satisfy it. */
|
|
164
257
|
export function chromiumHint(probe) {
|
|
258
|
+
if (true === probe.configured && probe.executablePath !== undefined) {
|
|
259
|
+
return probe.exists
|
|
260
|
+
? CHROMIUM_PATH_IN_USE(probe.executablePath)
|
|
261
|
+
: `✗ ${configuredChromiumRefusal(probe.executablePath, probe.problem ?? ChromiumPathProblem.MISSING)}`;
|
|
262
|
+
}
|
|
165
263
|
if (probe.exists) {
|
|
166
264
|
// Naming the revision on the happy line too: it is the number the mismatch line talks about, and
|
|
167
265
|
// a reader comparing two machines has nothing to compare without it.
|
|
@@ -44,7 +44,7 @@ export declare function decideDriveMode(presence: PortPresence): DriveMode;
|
|
|
44
44
|
/** A driveable session the daemon opened on our behalf. */
|
|
45
45
|
interface DriveSession {
|
|
46
46
|
sessionId: string;
|
|
47
|
-
/** Whether the tab's SDK actually registered — false ⇒ the app may not embed @reticlehq/
|
|
47
|
+
/** Whether the tab's SDK actually registered — false ⇒ the app may not embed @reticlehq/browser. */
|
|
48
48
|
ready: boolean;
|
|
49
49
|
/** How long the session survives untouched. Absent from a daemon that does not report it. */
|
|
50
50
|
expiresInMs?: number;
|
|
@@ -76,8 +76,33 @@ export declare function readAttachResponse(port: number, url: string, res: {
|
|
|
76
76
|
}): DriveAttachResult;
|
|
77
77
|
/** Ask the daemon on `port` to open a driveable browser at `url`. */
|
|
78
78
|
export declare function requestDriveSession(port: number, url: string, post?: DrivePost): Promise<DriveAttachResult>;
|
|
79
|
+
/** Which app the attached page says it is, beside the project this directory names. */
|
|
80
|
+
export interface AttachedApp {
|
|
81
|
+
/** What the JSON line carries. Every value is printable, see `printable`. */
|
|
82
|
+
fields: {
|
|
83
|
+
/** The project the page reported when it connected. */
|
|
84
|
+
projectId?: string;
|
|
85
|
+
title?: string;
|
|
86
|
+
/** The `projectId` in this directory's `.reticle.json`, when there is one. */
|
|
87
|
+
expectedProjectId?: string;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* The two project ids differ. Decided on the raw values, before either is made printable, so two
|
|
91
|
+
* ids that differ only in a control character still count as different.
|
|
92
|
+
*/
|
|
93
|
+
mismatch: boolean;
|
|
94
|
+
}
|
|
95
|
+
/** Nothing known about the app, as for a page that never connected. */
|
|
96
|
+
export declare const NO_ATTACHED_APP: AttachedApp;
|
|
97
|
+
/**
|
|
98
|
+
* Pick the attached session out of a `/status` payload. Pure.
|
|
99
|
+
*
|
|
100
|
+
* The drive answer is a lease result, which carries no project: the page reports that itself when
|
|
101
|
+
* it connects, and `/status` is where it is already listed.
|
|
102
|
+
*/
|
|
103
|
+
export declare function attachedApp(status: unknown, sessionId: string, expectedProjectId: string | undefined): AttachedApp;
|
|
79
104
|
/** The success line. Names the session, because that is the handle everything downstream needs. */
|
|
80
|
-
export declare function describeAttached(port: number, url: string, session: DriveSession): string;
|
|
105
|
+
export declare function describeAttached(port: number, url: string, session: DriveSession, app?: AttachedApp): string;
|
|
81
106
|
/**
|
|
82
107
|
* What an attach cannot honour: the window.
|
|
83
108
|
*
|