peachsim 0.1.527
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +640 -0
- package/cli/app-config.ts +82 -0
- package/cli/app-fonts.ts +388 -0
- package/cli/app-project.ts +59 -0
- package/cli/app-splash.ts +171 -0
- package/cli/app-state-reset.ts +26 -0
- package/cli/auth.ts +184 -0
- package/cli/bin.ts +59 -0
- package/cli/bridge-diagnostics.ts +226 -0
- package/cli/bridge-flow-runner.ts +2806 -0
- package/cli/browser-backend.ts +44 -0
- package/cli/browser-evals.ts +96 -0
- package/cli/cli-update.ts +261 -0
- package/cli/cloud-client.ts +1530 -0
- package/cli/cloud-dispatch.ts +206 -0
- package/cli/cloud-session.ts +227 -0
- package/cli/command-contract.ts +221 -0
- package/cli/command-registry.ts +304 -0
- package/cli/commands/agent-wrapper.ts +1048 -0
- package/cli/commands/agent.ts +439 -0
- package/cli/commands/app-fonts.ts +99 -0
- package/cli/commands/assert.ts +542 -0
- package/cli/commands/auth.ts +71 -0
- package/cli/commands/box/bundle-plane.ts +71 -0
- package/cli/commands/box/checkout-plane.ts +256 -0
- package/cli/commands/box/plane-share.ts +208 -0
- package/cli/commands/box/rnx-command.ts +60 -0
- package/cli/commands/box/search-command.ts +157 -0
- package/cli/commands/box.ts +925 -0
- package/cli/commands/camera.ts +266 -0
- package/cli/commands/cleanup.ts +169 -0
- package/cli/commands/compat.ts +176 -0
- package/cli/commands/config.ts +34 -0
- package/cli/commands/control.ts +2154 -0
- package/cli/commands/cpu-profile.ts +941 -0
- package/cli/commands/daemon-mac-app.ts +165 -0
- package/cli/commands/daemon.ts +800 -0
- package/cli/commands/debug.ts +751 -0
- package/cli/commands/desktop.ts +39 -0
- package/cli/commands/detect.ts +201 -0
- package/cli/commands/detox.ts +481 -0
- package/cli/commands/device.ts +134 -0
- package/cli/commands/diagnose.ts +589 -0
- package/cli/commands/do-chain.ts +408 -0
- package/cli/commands/electron.ts +94 -0
- package/cli/commands/film.ts +379 -0
- package/cli/commands/flow.ts +1240 -0
- package/cli/commands/inspect/actions.ts +516 -0
- package/cli/commands/inspect/core.ts +2086 -0
- package/cli/commands/inspect/count.ts +17 -0
- package/cli/commands/inspect/describe.ts +119 -0
- package/cli/commands/inspect/env.ts +25 -0
- package/cli/commands/inspect/find.ts +169 -0
- package/cli/commands/inspect/get-layout.ts +53 -0
- package/cli/commands/inspect/keyboard.ts +54 -0
- package/cli/commands/inspect/list.ts +175 -0
- package/cli/commands/inspect/memory.ts +215 -0
- package/cli/commands/inspect/redaction.ts +39 -0
- package/cli/commands/inspect/resolve-target.ts +77 -0
- package/cli/commands/inspect/screens.ts +78 -0
- package/cli/commands/inspect/settle.ts +32 -0
- package/cli/commands/inspect/settling.ts +197 -0
- package/cli/commands/inspect/shared.ts +259 -0
- package/cli/commands/inspect/sleep.ts +15 -0
- package/cli/commands/inspect/tree.ts +23 -0
- package/cli/commands/inspect/url.ts +17 -0
- package/cli/commands/inspect/wait-event.ts +211 -0
- package/cli/commands/inspect/wait-idle.ts +25 -0
- package/cli/commands/inspect/wait-ready.ts +87 -0
- package/cli/commands/inspect/wait-selector.ts +59 -0
- package/cli/commands/inspect/wait.ts +32 -0
- package/cli/commands/inspect.ts +4738 -0
- package/cli/commands/install-cli.ts +275 -0
- package/cli/commands/install-desktop.ts +385 -0
- package/cli/commands/login.ts +357 -0
- package/cli/commands/logout.ts +24 -0
- package/cli/commands/maestro-generate.ts +375 -0
- package/cli/commands/maestro.ts +536 -0
- package/cli/commands/mode.ts +58 -0
- package/cli/commands/no-bridge-hint.ts +80 -0
- package/cli/commands/perf.ts +67 -0
- package/cli/commands/permissions.ts +203 -0
- package/cli/commands/platform.ts +410 -0
- package/cli/commands/preview.ts +103 -0
- package/cli/commands/profile.ts +106 -0
- package/cli/commands/react.ts +353 -0
- package/cli/commands/record.ts +1435 -0
- package/cli/commands/report-issue.ts +305 -0
- package/cli/commands/reset.ts +89 -0
- package/cli/commands/runtime.ts +348 -0
- package/cli/commands/screenshot-command.ts +106 -0
- package/cli/commands/screenshot-layers.ts +144 -0
- package/cli/commands/screenshot-mode.ts +38 -0
- package/cli/commands/screenshot.ts +492 -0
- package/cli/commands/screenshots-capture.ts +608 -0
- package/cli/commands/screenshots.ts +127 -0
- package/cli/commands/serve.ts +139 -0
- package/cli/commands/setup.ts +416 -0
- package/cli/commands/shell-boolean-mode.ts +82 -0
- package/cli/commands/skills.ts +468 -0
- package/cli/commands/slides.ts +357 -0
- package/cli/commands/state.ts +94 -0
- package/cli/commands/storage.ts +58 -0
- package/cli/commands/telemetry.ts +55 -0
- package/cli/commands/test.ts +535 -0
- package/cli/commands/three-mode.ts +771 -0
- package/cli/commands/timeline.ts +121 -0
- package/cli/commands/upgrade.ts +167 -0
- package/cli/commands/upload.ts +1203 -0
- package/cli/commands/version.ts +63 -0
- package/cli/commands/welcome.ts +17 -0
- package/cli/commands/what-happened.ts +328 -0
- package/cli/current-sim.ts +217 -0
- package/cli/desktop-companion.ts +315 -0
- package/cli/drivers/electron.ts +70 -0
- package/cli/drivers/index.ts +20 -0
- package/cli/drivers/playwright-provisioning.ts +184 -0
- package/cli/drivers/playwright-sim-host.ts +537 -0
- package/cli/drivers/playwright.ts +209 -0
- package/cli/drivers/registry.ts +65 -0
- package/cli/drivers/types.ts +102 -0
- package/cli/flow-export.ts +55 -0
- package/cli/flow-file.ts +142 -0
- package/cli/flow-live-status.ts +126 -0
- package/cli/flow-session.ts +140 -0
- package/cli/help-core.ts +143 -0
- package/cli/help.ts +44 -0
- package/cli/hidden-runtime-alias.ts +19 -0
- package/cli/hints.ts +214 -0
- package/cli/inspect-notice-state.ts +114 -0
- package/cli/internal-child.ts +176 -0
- package/cli/maestro-js.ts +323 -0
- package/cli/main.ts +704 -0
- package/cli/open-url.ts +8 -0
- package/cli/outbound-endpoints.ts +830 -0
- package/cli/parent-pid.ts +204 -0
- package/cli/parse-args.ts +189 -0
- package/cli/privacy.ts +53 -0
- package/cli/prompt.ts +56 -0
- package/cli/recording-access.ts +108 -0
- package/cli/registry.ts +2 -0
- package/cli/resolve-assets.ts +63 -0
- package/cli/rnx-in-process.ts +98 -0
- package/cli/run-registry.ts +226 -0
- package/cli/run-rnx-core.ts +148 -0
- package/cli/run-rnx.ts +137 -0
- package/cli/runtime-notes.ts +66 -0
- package/cli/runtime-summary.ts +25 -0
- package/cli/secure-file.ts +31 -0
- package/cli/self-invocation.ts +58 -0
- package/cli/send-to-box.ts +109 -0
- package/cli/setup-repository.ts +191 -0
- package/cli/shell-init.ts +98 -0
- package/cli/standalone.ts +4 -0
- package/cli/telemetry.ts +84 -0
- package/cli/ui.ts +68 -0
- package/cli/ws-bridge.ts +1020 -0
- package/detox/attempt-events.cjs +23 -0
- package/detox/colors.ts +54 -0
- package/detox/config-loader.ts +135 -0
- package/detox/element-types.ts +48 -0
- package/detox/expectations.ts +534 -0
- package/detox/gestures.ts +501 -0
- package/detox/index.ts +2694 -0
- package/detox/jest-preset.cjs +29 -0
- package/detox/jest-setup-after-env.cjs +22 -0
- package/detox/matchers.ts +30 -0
- package/detox/motion-change.cjs +109 -0
- package/detox/motion-change.d.cts +35 -0
- package/detox/navigation-lifecycle.ts +290 -0
- package/detox/navigation.ts +43 -0
- package/detox/proof-frame.cjs +114 -0
- package/detox/record-block-execution.cjs +78 -0
- package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
- package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
- package/detox/screenshots/color-test-rest-norngh.png +0 -0
- package/detox/screenshots/theme-blue-toggle.png +0 -0
- package/detox/screenshots/theme-blue.png +0 -0
- package/detox/screenshots/theme-red-toggle.png +0 -0
- package/detox/screenshots/theme-red.png +0 -0
- package/dist-lib/agent-daemon-client.cjs +502 -0
- package/dist-lib/agent-events.cjs +48 -0
- package/dist-lib/agent-identity.cjs +53 -0
- package/dist-lib/agent-sessions.cjs +759 -0
- package/dist-lib/attached-projects.cjs +526 -0
- package/dist-lib/auth/shared-session.cjs +208 -0
- package/dist-lib/backend-origin.cjs +88 -0
- package/dist-lib/beta.cjs +41 -0
- package/dist-lib/beta.mjs +13 -0
- package/dist-lib/bridge-constants.cjs +161 -0
- package/dist-lib/bridge-contract-input.cjs +451 -0
- package/dist-lib/bridge-contract-input.mjs +414 -0
- package/dist-lib/bridge-contract.cjs +130 -0
- package/dist-lib/bridge-contract.mjs +94 -0
- package/dist-lib/capture-contract.cjs +220 -0
- package/dist-lib/capture-contract.mjs +190 -0
- package/dist-lib/cli-constants.cjs +32 -0
- package/dist-lib/cloud-contract.cjs +194 -0
- package/dist-lib/cloud-contract.mjs +145 -0
- package/dist-lib/cloud.cjs +670 -0
- package/dist-lib/cloud.mjs +639 -0
- package/dist-lib/config.cjs +96 -0
- package/dist-lib/detox/index.cjs +3789 -0
- package/dist-lib/detox/jest-preset.cjs +29 -0
- package/dist-lib/dev-bundle-resolution.cjs +290 -0
- package/dist-lib/home-paths.cjs +638 -0
- package/dist-lib/host/bridge-host.cjs +10903 -0
- package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
- package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
- package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
- package/dist-lib/host/replacement-module-handler.cjs +469 -0
- package/dist-lib/host/websocket-proxy.cjs +298 -0
- package/dist-lib/index.cjs +4268 -0
- package/dist-lib/jump-to-source-babel.cjs +93 -0
- package/dist-lib/jump-to-source-native.cjs +112 -0
- package/dist-lib/menu.cjs +646 -0
- package/dist-lib/menu.mjs +604 -0
- package/dist-lib/metro-fingerprint-registry.cjs +186 -0
- package/dist-lib/metro-fingerprint-registry.mjs +156 -0
- package/dist-lib/metro-production-bundle.cjs +329 -0
- package/dist-lib/metro-production-bundle.mjs +295 -0
- package/dist-lib/metro.cjs +904 -0
- package/dist-lib/profiles.cjs +246 -0
- package/dist-lib/public-brand.cjs +39 -0
- package/dist-lib/react-native-host-modules.cjs +51 -0
- package/dist-lib/react-native-host-modules.mjs +25 -0
- package/dist-lib/render-mode.cjs +54 -0
- package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
- package/dist-lib/sdk.cjs +2433 -0
- package/dist-lib/sdk.mjs +2336 -0
- package/dist-lib/skills.cjs +39132 -0
- package/dist-lib/swift-submit.cjs +92 -0
- package/dist-lib/swift-submit.mjs +65 -0
- package/dist-lib/swift.cjs +1646 -0
- package/dist-lib/vite.cjs +1591 -0
- package/package.json +290 -0
- package/scripts/dev-server-scanner.ts +1291 -0
- package/scripts/optional-demo-registry.ts +113 -0
- package/skills/contrast/SKILL.md +165 -0
- package/skills/rnx-debug/SKILL.md +433 -0
- package/skills/rnx-perf/SKILL.md +399 -0
- package/skills/rnx-setup/SKILL.md +172 -0
- package/skills/rnx-test/SKILL.md +321 -0
- package/skills/rnx-visual/SKILL.md +126 -0
- package/src/agent-daemon-client.ts +410 -0
- package/src/agent-events.ts +71 -0
- package/src/agent-identity.ts +23 -0
- package/src/agent-prompt.ts +71 -0
- package/src/agent-sessions.ts +580 -0
- package/src/app-url.ts +39 -0
- package/src/attached-projects.ts +539 -0
- package/src/auth/shared-session.ts +235 -0
- package/src/backend-origin.ts +70 -0
- package/src/beta.ts +19 -0
- package/src/bridge-constants.ts +175 -0
- package/src/bridge-contract-input.ts +502 -0
- package/src/bridge-contract.ts +558 -0
- package/src/browser-cache.ts +16 -0
- package/src/capture-contract.ts +587 -0
- package/src/cli-constants.ts +1 -0
- package/src/cli-version.ts +40 -0
- package/src/cloud-contract.ts +274 -0
- package/src/cloud.ts +762 -0
- package/src/codex-client.ts +234 -0
- package/src/config.ts +132 -0
- package/src/connect.ts +101 -0
- package/src/dev-bundle-resolution.ts +346 -0
- package/src/dev-server-open.ts +198 -0
- package/src/disk-cleanup.ts +540 -0
- package/src/flow-timeout-scale.ts +15 -0
- package/src/home-paths.ts +806 -0
- package/src/host/agent-host.ts +666 -0
- package/src/host/bridge-host.ts +2788 -0
- package/src/host/fetch-proxy-handler.ts +449 -0
- package/src/host/fetch-proxy-overrides.ts +51 -0
- package/src/host/open-url.ts +89 -0
- package/src/host/plane-host.ts +203 -0
- package/src/host/replacement-module-handler.ts +158 -0
- package/src/host/websocket-proxy.ts +315 -0
- package/src/index.ts +71 -0
- package/src/jump-to-source-babel.ts +135 -0
- package/src/jump-to-source-native.ts +102 -0
- package/src/menu.ts +777 -0
- package/src/metro-fingerprint-registry.ts +302 -0
- package/src/metro-plugin.ts +341 -0
- package/src/metro-production-bundle.ts +471 -0
- package/src/native-dev-bundle-url.ts +118 -0
- package/src/native-seam-manifest.ts +341 -0
- package/src/playwright-host-log.ts +16 -0
- package/src/plist.ts +27 -0
- package/src/poll-until.ts +123 -0
- package/src/profiles.ts +179 -0
- package/src/public-brand.ts +9 -0
- package/src/react-native-host-modules.ts +26 -0
- package/src/render-mode.ts +27 -0
- package/src/rolldown-plugin-swift.ts +240 -0
- package/src/runtime-assets.ts +233 -0
- package/src/runtime-delivery.ts +54 -0
- package/src/screenshots/compose.ts +422 -0
- package/src/screenshots/frame-compose.ts +514 -0
- package/src/screenshots/orchestrate.ts +256 -0
- package/src/screenshots/org-deck.ts +91 -0
- package/src/screenshots/registry.ts +58 -0
- package/src/screenshots/schema.ts +364 -0
- package/src/sdk.ts +15 -0
- package/src/settings-bundle.ts +418 -0
- package/src/shell-bridge-identity.ts +15 -0
- package/src/sim-client.ts +697 -0
- package/src/skills/builtin/a11y-review.ts +132 -0
- package/src/skills/builtin/compat-check.ts +71 -0
- package/src/skills/builtin/maestro-test.ts +121 -0
- package/src/skills/builtin/perf-profile.ts +90 -0
- package/src/skills/builtin/screenshot-all.ts +52 -0
- package/src/skills/builtin/visual-diff.ts +100 -0
- package/src/skills/registry.ts +107 -0
- package/src/skills/types.ts +41 -0
- package/src/swift-submit.ts +185 -0
- package/src/swift.ts +7 -0
- package/src/team-machine-prompt.ts +73 -0
- package/src/vite-plugin-one.ts +264 -0
- package/src/vite-plugin-swift.ts +620 -0
- package/src/vite-plugin.ts +1946 -0
- package/src/worklets-babel.ts +250 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// shared helpers for inspect subcommands.
|
|
2
|
+
//
|
|
3
|
+
// pulled out of the monolithic inspect.ts so each subcommand can live in its
|
|
4
|
+
// own file without reaching back into a 4000-line switch. everything here is
|
|
5
|
+
// call-agnostic: just bridge-facing helpers that any subcommand can use.
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
callInBridge,
|
|
9
|
+
callInBridgeWrite,
|
|
10
|
+
createBridge,
|
|
11
|
+
type ParsedBridgeCliArgs,
|
|
12
|
+
type WsBridge,
|
|
13
|
+
} from '../../ws-bridge'
|
|
14
|
+
import { getShellState, inspectWaitReady, isShellCommandUnavailable } from './core'
|
|
15
|
+
import type { SootSimScreenTransitionWaitResult } from '@rnx/globals'
|
|
16
|
+
|
|
17
|
+
// shell-state helpers live in the transport-agnostic kernel module so the CLI
|
|
18
|
+
// and the in-browser agent share one implementation. re-exported here so
|
|
19
|
+
// existing subcommand imports (`./shared`) keep working.
|
|
20
|
+
export { getShellState, isShellCommandUnavailable }
|
|
21
|
+
|
|
22
|
+
export const SCREEN_TRANSITION_PREFLIGHT = {
|
|
23
|
+
timeoutMs: 1800,
|
|
24
|
+
settleMs: 48,
|
|
25
|
+
startWindowMs: 64,
|
|
26
|
+
} as const
|
|
27
|
+
|
|
28
|
+
export function sleep(ms: number) {
|
|
29
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// true when the caller passed --json. read verbs use this to route their
|
|
33
|
+
// payload to stdout-as-json and send everything else (headers, hints,
|
|
34
|
+
// warnings) to stderr so `jq` on stdout never sees framing noise.
|
|
35
|
+
export function wantsJson(args: string[]): boolean {
|
|
36
|
+
return args.includes('--json')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// emit a stable JSON document on stdout. no trailing prose, no ANSI,
|
|
40
|
+
// no colors. the 2-space indent matches the rest of the CLI and is
|
|
41
|
+
// cheap enough that pretty-printing by default is fine for bisect
|
|
42
|
+
// scripts — consumers who care pipe through `jq -c`.
|
|
43
|
+
export function printJson(payload: unknown): void {
|
|
44
|
+
process.stdout.write(`${JSON.stringify(payload ?? null, null, 2)}\n`)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// warnings, hints, "screen transition still active" messages — anything
|
|
48
|
+
// that isn't the payload. always goes to stderr so stdout stays parseable.
|
|
49
|
+
export function printWarn(msg: string): void {
|
|
50
|
+
process.stderr.write(`${msg}\n`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 24-hour wall-clock + relative offset for log/error/network timestamps.
|
|
54
|
+
// agents reading these care most about "is this new or stale" — append
|
|
55
|
+
// "+<n>s" for things younger than 60s. older entries keep the relative
|
|
56
|
+
// stamp in m/h/d so it's still scannable without diffing wall-clock times.
|
|
57
|
+
//
|
|
58
|
+
// returns "HH:MM:SS +Xs" where wall clock is locale-independent 24-hour.
|
|
59
|
+
// callers can wrap it in their own padding/dim ANSI.
|
|
60
|
+
export function formatLogTimestamp(ts: number, nowMs: number = Date.now()): string {
|
|
61
|
+
const d = new Date(ts)
|
|
62
|
+
const wall = `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`
|
|
63
|
+
const deltaMs = nowMs - ts
|
|
64
|
+
if (deltaMs < 0) return wall
|
|
65
|
+
const sec = Math.round(deltaMs / 1000)
|
|
66
|
+
if (sec < 60) return `${wall} +${sec}s`
|
|
67
|
+
if (sec < 3600) return `${wall} +${Math.round(sec / 60)}m`
|
|
68
|
+
if (sec < 86400) return `${wall} +${Math.round(sec / 3600)}h`
|
|
69
|
+
return `${wall} +${Math.round(sec / 86400)}d`
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function pad2(n: number): string {
|
|
73
|
+
return n < 10 ? `0${n}` : String(n)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function callTestBridge<T = unknown>(
|
|
77
|
+
bridge: WsBridge,
|
|
78
|
+
method: string,
|
|
79
|
+
...args: unknown[]
|
|
80
|
+
): Promise<T> {
|
|
81
|
+
return callInBridge<T>(bridge, `__sootsimTest.${method}`, ...args)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function callShellCommand<T = unknown>(
|
|
85
|
+
bridge: WsBridge,
|
|
86
|
+
method: 'launchApp' | 'goHome' | 'openSwitcher' | 'openSwitcherCard',
|
|
87
|
+
...args: unknown[]
|
|
88
|
+
): Promise<T> {
|
|
89
|
+
return callInBridgeWrite<T>(bridge, `SootSim.bridges.mainShell.${method}`, ...args)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function maybeWaitForStartedScreenTransitions(
|
|
93
|
+
bridge: WsBridge,
|
|
94
|
+
opts: { verbose?: boolean } = {},
|
|
95
|
+
) {
|
|
96
|
+
if (bridge.plane === 'cloud') return
|
|
97
|
+
try {
|
|
98
|
+
const result = await callTestBridge<SootSimScreenTransitionWaitResult>(
|
|
99
|
+
bridge,
|
|
100
|
+
'waitForScreenTransitions',
|
|
101
|
+
SCREEN_TRANSITION_PREFLIGHT,
|
|
102
|
+
)
|
|
103
|
+
if (!opts.verbose || !result?.started) return
|
|
104
|
+
if (result.timedOut) {
|
|
105
|
+
console.log(
|
|
106
|
+
` screen transition still active after ${result.waitedMs}ms; continuing`,
|
|
107
|
+
)
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
if (result.waitedMs > 0) {
|
|
111
|
+
console.log(` waited ${result.waitedMs}ms for screen transition settle`)
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
// best-effort preflight only — older runtimes or boot races should not
|
|
115
|
+
// block the actual command.
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function callShellCommandWhenReady<T = unknown>(
|
|
120
|
+
bridge: WsBridge,
|
|
121
|
+
method: 'launchApp' | 'goHome' | 'openSwitcher' | 'openSwitcherCard',
|
|
122
|
+
readyTimeoutMs: number,
|
|
123
|
+
...args: unknown[]
|
|
124
|
+
): Promise<T> {
|
|
125
|
+
const deadline = Date.now() + Math.max(0, readyTimeoutMs)
|
|
126
|
+
while (true) {
|
|
127
|
+
try {
|
|
128
|
+
return await callShellCommand<T>(bridge, method, ...args)
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (!isShellCommandUnavailable(error) || Date.now() >= deadline) throw error
|
|
131
|
+
await sleep(50)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function waitForSimReady(
|
|
137
|
+
wsPort: number,
|
|
138
|
+
commandTimeoutMs: number,
|
|
139
|
+
simId?: string,
|
|
140
|
+
opts: {
|
|
141
|
+
attempts?: number
|
|
142
|
+
intervalMs?: number
|
|
143
|
+
minNodeCount?: number
|
|
144
|
+
simIdSource?: ParsedBridgeCliArgs['simIdSource']
|
|
145
|
+
} = {},
|
|
146
|
+
) {
|
|
147
|
+
const attempts = opts.attempts ?? 30
|
|
148
|
+
const intervalMs = opts.intervalMs ?? 500
|
|
149
|
+
const minNodeCount = opts.minNodeCount ?? 10
|
|
150
|
+
|
|
151
|
+
for (let i = 0; i < attempts; i++) {
|
|
152
|
+
const bridge = createBridge(wsPort, {
|
|
153
|
+
commandTimeoutMs,
|
|
154
|
+
simId,
|
|
155
|
+
simIdSource: opts.simIdSource,
|
|
156
|
+
})
|
|
157
|
+
try {
|
|
158
|
+
const count = await bridge.send({
|
|
159
|
+
type: 'evaluate',
|
|
160
|
+
code: '(async () => (await window.__sootsimTest?.getNodeCount()) || 0)()',
|
|
161
|
+
})
|
|
162
|
+
if (typeof count === 'number' && count > minNodeCount) {
|
|
163
|
+
return { bridge, count }
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
// still loading or disconnected
|
|
167
|
+
}
|
|
168
|
+
bridge.close()
|
|
169
|
+
await sleep(intervalMs)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return null
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// waits until a bridge can evaluate a trivial expression. used by the reload
|
|
176
|
+
// full-page path after window.location.reload() to re-establish a bridge
|
|
177
|
+
// before handing off to pollForReloadReady. intentionally does not gate on
|
|
178
|
+
// node count — that check belongs in the subsequent ready poll, which uses
|
|
179
|
+
// the __sootsimExternalAppReady flag as its primary signal.
|
|
180
|
+
export async function waitForBridgeConnected(
|
|
181
|
+
wsPort: number,
|
|
182
|
+
commandTimeoutMs: number,
|
|
183
|
+
simId: string | undefined,
|
|
184
|
+
opts: {
|
|
185
|
+
timeoutMs?: number
|
|
186
|
+
intervalMs?: number
|
|
187
|
+
simIdSource?: ParsedBridgeCliArgs['simIdSource']
|
|
188
|
+
} = {},
|
|
189
|
+
): Promise<WsBridge | null> {
|
|
190
|
+
const timeoutMs = opts.timeoutMs ?? 8000
|
|
191
|
+
const intervalMs = opts.intervalMs ?? 250
|
|
192
|
+
const deadline = Date.now() + timeoutMs
|
|
193
|
+
while (Date.now() < deadline) {
|
|
194
|
+
const bridge = createBridge(wsPort, {
|
|
195
|
+
commandTimeoutMs,
|
|
196
|
+
simId,
|
|
197
|
+
simIdSource: opts.simIdSource,
|
|
198
|
+
})
|
|
199
|
+
try {
|
|
200
|
+
await bridge.send({ type: 'evaluate', code: '1' })
|
|
201
|
+
return bridge
|
|
202
|
+
} catch {
|
|
203
|
+
bridge.close()
|
|
204
|
+
}
|
|
205
|
+
await sleep(intervalMs)
|
|
206
|
+
}
|
|
207
|
+
return null
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface ReloadReadyResult {
|
|
211
|
+
ready: boolean
|
|
212
|
+
source: 'flag' | 'nodes-fallback' | 'error-bail' | 'timeout'
|
|
213
|
+
elapsedMs: number
|
|
214
|
+
nodes: number
|
|
215
|
+
targets: number
|
|
216
|
+
liveFrameActive: boolean
|
|
217
|
+
liveFrameChannels: number
|
|
218
|
+
liveFramePublishes: number
|
|
219
|
+
flag: unknown
|
|
220
|
+
loadingText: string
|
|
221
|
+
externalReady: boolean | null
|
|
222
|
+
externalStatus: string
|
|
223
|
+
externalError: string
|
|
224
|
+
errors: number
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// post-reload readiness is the same engine-owned condition as `rnx wait ready`.
|
|
228
|
+
// keeping reload on that primitive prevents another polling implementation
|
|
229
|
+
// from drifting on loading text, live frames, or tree stability.
|
|
230
|
+
export async function pollForReloadReady(
|
|
231
|
+
bridge: WsBridge,
|
|
232
|
+
opts: { timeoutMs?: number } = {},
|
|
233
|
+
): Promise<ReloadReadyResult> {
|
|
234
|
+
const timeoutMs = opts.timeoutMs ?? 10000
|
|
235
|
+
const status = await inspectWaitReady(bridge, timeoutMs)
|
|
236
|
+
const source = status.ready
|
|
237
|
+
? status.flag === true
|
|
238
|
+
? 'flag'
|
|
239
|
+
: 'nodes-fallback'
|
|
240
|
+
: status.errors > 0 && status.flag !== true
|
|
241
|
+
? 'error-bail'
|
|
242
|
+
: 'timeout'
|
|
243
|
+
return {
|
|
244
|
+
ready: status.ready,
|
|
245
|
+
source,
|
|
246
|
+
elapsedMs: status.elapsedMs,
|
|
247
|
+
nodes: status.nodes,
|
|
248
|
+
targets: status.targets,
|
|
249
|
+
liveFrameActive: status.liveFrameActive,
|
|
250
|
+
liveFrameChannels: status.liveFrameChannels,
|
|
251
|
+
liveFramePublishes: status.liveFramePublishes,
|
|
252
|
+
flag: status.flag,
|
|
253
|
+
loadingText: status.loadingText,
|
|
254
|
+
externalReady: status.externalReady,
|
|
255
|
+
externalStatus: status.externalStatus,
|
|
256
|
+
externalError: status.externalError,
|
|
257
|
+
errors: status.errors,
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { rnxExit } from '../../run-rnx'
|
|
2
|
+
import { sleep } from './shared'
|
|
3
|
+
|
|
4
|
+
export async function runSleepSubcommand(opts: {
|
|
5
|
+
positional: string[]
|
|
6
|
+
inspectUsage: (name: string, tail: string) => string
|
|
7
|
+
}): Promise<void> {
|
|
8
|
+
const seconds = opts.positional[1] ? Number(opts.positional[1]) : 0.5
|
|
9
|
+
if (!Number.isFinite(seconds) || seconds < 0) {
|
|
10
|
+
console.error(opts.inspectUsage('sleep', '[seconds]'))
|
|
11
|
+
rnxExit(1)
|
|
12
|
+
}
|
|
13
|
+
await sleep(seconds * 1000)
|
|
14
|
+
console.log(` slept ${seconds}s`)
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// `rnx get tree` — pruned structured semantic tree, default depth 5.
|
|
2
|
+
|
|
3
|
+
import { formatSemanticTree, inspectTree, isSimSemanticTree } from './core'
|
|
4
|
+
import { printJson, wantsJson } from './shared'
|
|
5
|
+
import type { WsBridge } from '../../ws-bridge'
|
|
6
|
+
|
|
7
|
+
export async function runTreeSubcommand(opts: {
|
|
8
|
+
bridge: WsBridge
|
|
9
|
+
args: string[]
|
|
10
|
+
positional: string[]
|
|
11
|
+
}): Promise<void> {
|
|
12
|
+
const { bridge, args, positional } = opts
|
|
13
|
+
// --boxes keeps the deeper default expected by geometry consumers. the
|
|
14
|
+
// semantic tree is structured in both modes.
|
|
15
|
+
const boxes = args.includes('--boxes')
|
|
16
|
+
const depth = positional[1] ? Number(positional[1]) : boxes ? 50 : 5
|
|
17
|
+
const { tree } = await inspectTree(bridge, depth)
|
|
18
|
+
if (wantsJson(args)) {
|
|
19
|
+
printJson({ depth, tree: tree ?? null })
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
console.log(isSimSemanticTree(tree) ? formatSemanticTree(tree) : '')
|
|
23
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// `rnx get url` — prints the current page URL the bridge is attached to.
|
|
2
|
+
|
|
3
|
+
import { inspectUrl } from './core'
|
|
4
|
+
import { printJson, wantsJson } from './shared'
|
|
5
|
+
import type { WsBridge } from '../../ws-bridge'
|
|
6
|
+
|
|
7
|
+
export async function runUrlSubcommand(
|
|
8
|
+
bridge: WsBridge,
|
|
9
|
+
opts: { args: string[] } = { args: [] },
|
|
10
|
+
): Promise<void> {
|
|
11
|
+
const result = await inspectUrl(bridge)
|
|
12
|
+
if (wantsJson(opts.args)) {
|
|
13
|
+
printJson(result)
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
console.log(result.url)
|
|
17
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// rnx wait event — block until a semantic timeline event of the
|
|
2
|
+
// given kind appears, or timeout.
|
|
3
|
+
//
|
|
4
|
+
// usage:
|
|
5
|
+
// rnx wait event <kind> [--max-ms 5000] [--since now|cursor]
|
|
6
|
+
// rnx wait event fetch [--filter <substring>]
|
|
7
|
+
// rnx wait event screen [--equals SignInPage]
|
|
8
|
+
//
|
|
9
|
+
// the existing wait family is layout-driven: `wait selector` polls
|
|
10
|
+
// for a testID, `wait idle` polls for rAF + animation settling,
|
|
11
|
+
// `wait ready` polls for bundle paint. `wait event` is the
|
|
12
|
+
// timeline-driven companion: agents that want to drive "tap → wait
|
|
13
|
+
// for the BeginSignIn fetch to land → tap next" no longer need to
|
|
14
|
+
// chain `do tap-text Continue` + `sleep 4` + `what-happened --kinds
|
|
15
|
+
// fetch --since 4s | grep BeginSignIn`. one read returns when the
|
|
16
|
+
// next event of the requested kind appears.
|
|
17
|
+
//
|
|
18
|
+
// the implementation polls timeline.recent() — server-side
|
|
19
|
+
// subscription would be cheaper but a 200ms poll is plenty for the
|
|
20
|
+
// human-paced UI events we care about and stays compatible with the
|
|
21
|
+
// existing query surface.
|
|
22
|
+
|
|
23
|
+
import { rnxExit } from '../../run-rnx'
|
|
24
|
+
import type { WsBridge } from '../../ws-bridge'
|
|
25
|
+
import type {
|
|
26
|
+
SootSimTimelineEvent,
|
|
27
|
+
SootSimTimelineQuery,
|
|
28
|
+
SootSimTimelineQueryResult,
|
|
29
|
+
} from '@rnx/globals'
|
|
30
|
+
|
|
31
|
+
interface WaitEventOptions {
|
|
32
|
+
bridge: WsBridge
|
|
33
|
+
args: string[]
|
|
34
|
+
positional: string[]
|
|
35
|
+
inspectUsage: (name: string, tail: string) => string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// every timeline event kind the cli currently knows about. matching
|
|
39
|
+
// the type on `SootSimTimelineEvent['kind']` would be more strict
|
|
40
|
+
// but the timeline can grow new kinds and we don't want this
|
|
41
|
+
// command to refuse an unknown one — the worst case is the wait
|
|
42
|
+
// just times out, which is exactly the right behaviour.
|
|
43
|
+
const KNOWN_KINDS = new Set([
|
|
44
|
+
'app-launch',
|
|
45
|
+
'toast',
|
|
46
|
+
'keyboard',
|
|
47
|
+
'screen',
|
|
48
|
+
'flow-step',
|
|
49
|
+
'route',
|
|
50
|
+
'alert',
|
|
51
|
+
'actionsheet',
|
|
52
|
+
'picker',
|
|
53
|
+
'notification',
|
|
54
|
+
'fetch',
|
|
55
|
+
'console',
|
|
56
|
+
'shell',
|
|
57
|
+
'scroll',
|
|
58
|
+
'gesture',
|
|
59
|
+
'text-input',
|
|
60
|
+
'react-commit',
|
|
61
|
+
'animation',
|
|
62
|
+
'reanimated',
|
|
63
|
+
])
|
|
64
|
+
|
|
65
|
+
function findFlagValue(args: string[], flag: string): string | undefined {
|
|
66
|
+
const idx = args.indexOf(flag)
|
|
67
|
+
if (idx >= 0 && idx + 1 < args.length) return args[idx + 1]
|
|
68
|
+
return undefined
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function eventMatches(
|
|
72
|
+
ev: SootSimTimelineEvent,
|
|
73
|
+
opts: { filter?: string; equals?: string },
|
|
74
|
+
): boolean {
|
|
75
|
+
if (!opts.filter && !opts.equals) return true
|
|
76
|
+
const data = ev.data as Record<string, unknown> | null
|
|
77
|
+
// build a one-string haystack from the most-likely-relevant fields
|
|
78
|
+
// per kind. agents asking for `wait event fetch --filter BeginSignIn`
|
|
79
|
+
// expect to match the URL; `wait event screen --equals Inbox`
|
|
80
|
+
// expects to match the screen name.
|
|
81
|
+
const haystack: string[] = []
|
|
82
|
+
if (data && typeof data === 'object') {
|
|
83
|
+
for (const key of [
|
|
84
|
+
'url',
|
|
85
|
+
'displayUrl',
|
|
86
|
+
'message',
|
|
87
|
+
'name',
|
|
88
|
+
'activeName',
|
|
89
|
+
'path',
|
|
90
|
+
'pathname',
|
|
91
|
+
'title',
|
|
92
|
+
'phase',
|
|
93
|
+
'event',
|
|
94
|
+
'type',
|
|
95
|
+
'kind',
|
|
96
|
+
]) {
|
|
97
|
+
const v = data[key]
|
|
98
|
+
if (typeof v === 'string' && v.length > 0) haystack.push(v)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const joined = haystack.join(' ')
|
|
102
|
+
if (opts.equals) {
|
|
103
|
+
return haystack.some((s) => s === opts.equals)
|
|
104
|
+
}
|
|
105
|
+
if (opts.filter) {
|
|
106
|
+
return joined.toLowerCase().includes(opts.filter.toLowerCase())
|
|
107
|
+
}
|
|
108
|
+
return true
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function runWaitEventSubcommand(opts: WaitEventOptions): Promise<void> {
|
|
112
|
+
const { bridge, args, positional, inspectUsage } = opts
|
|
113
|
+
// positional shape after verb-prefix strip and stripBooleanFlags:
|
|
114
|
+
// rnx wait event <kind> → ['event', '<kind>']
|
|
115
|
+
// so positional[1] is the kind.
|
|
116
|
+
const kind = positional[1]
|
|
117
|
+
if (!kind) {
|
|
118
|
+
console.error(
|
|
119
|
+
inspectUsage(
|
|
120
|
+
'event',
|
|
121
|
+
'<kind> [--max-ms 5000] [--filter <substring>] [--equals <exact>] [--since now|cursor]',
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
rnxExit(1)
|
|
125
|
+
}
|
|
126
|
+
if (!KNOWN_KINDS.has(kind)) {
|
|
127
|
+
console.error(
|
|
128
|
+
` warning: '${kind}' is not a known timeline kind — waiting anyway. known: ${[...KNOWN_KINDS].sort().join(', ')}`,
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const maxMsRaw = findFlagValue(args, '--max-ms')
|
|
133
|
+
const maxMs =
|
|
134
|
+
maxMsRaw && Number.isFinite(Number(maxMsRaw)) ? Math.max(100, Number(maxMsRaw)) : 5000
|
|
135
|
+
|
|
136
|
+
const filter = findFlagValue(args, '--filter')
|
|
137
|
+
const equals = findFlagValue(args, '--equals')
|
|
138
|
+
const sinceMode = findFlagValue(args, '--since') ?? 'now'
|
|
139
|
+
const jsonOut = args.includes('--json')
|
|
140
|
+
|
|
141
|
+
// capture the start watermark so we don't match an event that
|
|
142
|
+
// happened before this command. agents calling `wait event fetch`
|
|
143
|
+
// after a tap mean "the *next* fetch", not "any fetch since the
|
|
144
|
+
// session started". `--since cursor` falls back to the
|
|
145
|
+
// what-happened cursor semantics for callers that explicitly want
|
|
146
|
+
// that.
|
|
147
|
+
const startedAt = Date.now()
|
|
148
|
+
const deadline = startedAt + maxMs
|
|
149
|
+
const pollMs = 200
|
|
150
|
+
|
|
151
|
+
let lastWatermark = startedAt
|
|
152
|
+
|
|
153
|
+
while (Date.now() < deadline) {
|
|
154
|
+
const query: SootSimTimelineQuery = {
|
|
155
|
+
kinds: [kind] as SootSimTimelineQuery['kinds'],
|
|
156
|
+
since: sinceMode === 'cursor' ? undefined : lastWatermark,
|
|
157
|
+
limit: 50,
|
|
158
|
+
}
|
|
159
|
+
const result = (await bridge.send({
|
|
160
|
+
type: 'evaluate',
|
|
161
|
+
// hand-marshal the query through evaluate — the bridge already
|
|
162
|
+
// exposes `SootSim.bridges.timeline.recent` as a sync method,
|
|
163
|
+
// but evaluate is the universally available shape and the cost
|
|
164
|
+
// of a string-baked function is negligible at 5Hz.
|
|
165
|
+
code: `(async () => {
|
|
166
|
+
const t = window.SootSim?.bridges?.timeline
|
|
167
|
+
if (!t) return { ok: false, error: 'timeline bridge missing' }
|
|
168
|
+
return { ok: true, result: await t.recent(${JSON.stringify(query)}) }
|
|
169
|
+
})()`,
|
|
170
|
+
})) as { ok: true; result: SootSimTimelineQueryResult } | { ok: false; error: string }
|
|
171
|
+
if (!result || !result.ok) {
|
|
172
|
+
console.error(
|
|
173
|
+
` could not query timeline: ${result && 'error' in result ? result.error : 'unknown'}`,
|
|
174
|
+
)
|
|
175
|
+
rnxExit(1)
|
|
176
|
+
}
|
|
177
|
+
const evts = result.result.events ?? []
|
|
178
|
+
for (const ev of evts) {
|
|
179
|
+
if (eventMatches(ev, { filter, equals })) {
|
|
180
|
+
const elapsed = Date.now() - startedAt
|
|
181
|
+
if (jsonOut) {
|
|
182
|
+
console.log(JSON.stringify({ found: true, elapsedMs: elapsed, event: ev }))
|
|
183
|
+
} else {
|
|
184
|
+
console.log(
|
|
185
|
+
` ${kind} event after ${elapsed}ms${filter ? ` (filter: ${filter})` : ''}${equals ? ` (equals: ${equals})` : ''}`,
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// advance the local watermark to the high-water mark we just
|
|
192
|
+
// saw so the next poll doesn't redundantly re-scan the same
|
|
193
|
+
// events.
|
|
194
|
+
if (result.result.watermark && result.result.watermark > lastWatermark) {
|
|
195
|
+
lastWatermark = result.result.watermark
|
|
196
|
+
}
|
|
197
|
+
await new Promise((r) => setTimeout(r, pollMs))
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const elapsed = Date.now() - startedAt
|
|
201
|
+
if (jsonOut) {
|
|
202
|
+
console.log(
|
|
203
|
+
JSON.stringify({ found: false, elapsedMs: elapsed, kind, filter, equals }),
|
|
204
|
+
)
|
|
205
|
+
} else {
|
|
206
|
+
console.error(
|
|
207
|
+
` ⚠ wait event ${kind} timed out after ${elapsed}ms${filter ? ` (filter: ${filter})` : ''}${equals ? ` (equals: ${equals})` : ''}`,
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
rnxExit(1)
|
|
211
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { rnxExit } from '../../run-rnx'
|
|
2
|
+
import { resolveMaxMsFlag } from './core'
|
|
3
|
+
import { waitForSootsimIdle } from './settling'
|
|
4
|
+
import type { WsBridge } from '../../ws-bridge'
|
|
5
|
+
|
|
6
|
+
// alias for `do settle` under the discoverable `wait` verb grouping.
|
|
7
|
+
// layout hash stability across 3 polls; --strict also requires worker
|
|
8
|
+
// animation flags to be clear.
|
|
9
|
+
export async function runWaitIdleSubcommand(opts: {
|
|
10
|
+
bridge: WsBridge
|
|
11
|
+
args: string[]
|
|
12
|
+
positional: string[]
|
|
13
|
+
}): Promise<void> {
|
|
14
|
+
const { bridge, args } = opts
|
|
15
|
+
const strict = args.includes('--strict')
|
|
16
|
+
const maxMs = resolveMaxMsFlag(args, 3000)
|
|
17
|
+
const { elapsed, settled } = await waitForSootsimIdle({ bridge, maxMs, strict })
|
|
18
|
+
|
|
19
|
+
if (settled) {
|
|
20
|
+
console.log(` idle in ${elapsed}ms`)
|
|
21
|
+
} else {
|
|
22
|
+
console.error(` ⚠ wait idle timed out after ${elapsed}ms (may still be animating)`)
|
|
23
|
+
rnxExit(1)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { rnxExit } from '../../run-rnx'
|
|
2
|
+
import { inspectWaitReady, resolveMaxMsFlag, waitReadyReason } from './core'
|
|
3
|
+
import { waitForSootsimIdle } from './settling'
|
|
4
|
+
import type { WsBridge } from '../../ws-bridge'
|
|
5
|
+
|
|
6
|
+
// blocks until the guest app bundle has finished loading and painted real
|
|
7
|
+
// inspectable content. the persistent window flag
|
|
8
|
+
// `__sootsimExternalAppReady` is necessary but not sufficient: shell-owned
|
|
9
|
+
// canvas can expose a stable launch/loading tree before the app content lands.
|
|
10
|
+
// the engine owns the polling loop; this handler sends one request, then
|
|
11
|
+
// reports the typed result.
|
|
12
|
+
export async function runWaitReadySubcommand(opts: {
|
|
13
|
+
bridge: WsBridge
|
|
14
|
+
args: string[]
|
|
15
|
+
}): Promise<void> {
|
|
16
|
+
const { bridge, args } = opts
|
|
17
|
+
// default 20s because cold guest bundles (uniswap, expensify, bluesky)
|
|
18
|
+
// routinely take 10-15s to evaluate + mount.
|
|
19
|
+
const timeoutMs = resolveMaxMsFlag(args, 20000)
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
ready,
|
|
23
|
+
elapsedMs,
|
|
24
|
+
nodes,
|
|
25
|
+
targets,
|
|
26
|
+
liveFrameActive,
|
|
27
|
+
liveFrameChannels,
|
|
28
|
+
flag,
|
|
29
|
+
loadingText,
|
|
30
|
+
externalReady,
|
|
31
|
+
externalStatus,
|
|
32
|
+
externalError,
|
|
33
|
+
suppressedEntryError,
|
|
34
|
+
errors,
|
|
35
|
+
bridgeError,
|
|
36
|
+
} = await inspectWaitReady(bridge, timeoutMs, {
|
|
37
|
+
...(bridge.plane !== 'cloud'
|
|
38
|
+
? {
|
|
39
|
+
onProgress(status) {
|
|
40
|
+
console.error(
|
|
41
|
+
` still waiting after ${status.elapsedMs}ms — ${waitReadyReason(status)} (nodes: ${status.nodes}, targets: ${status.targets}, live surfaces: ${status.liveFrameActive ? status.liveFrameChannels : 0}, errors: ${status.errors})`,
|
|
42
|
+
)
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
: {}),
|
|
46
|
+
})
|
|
47
|
+
if (ready) {
|
|
48
|
+
if (bridge.plane === 'cloud') {
|
|
49
|
+
console.log(
|
|
50
|
+
` ready in ${elapsedMs}ms: ${nodes} nodes, ${targets} targets${liveFrameActive ? `, ${liveFrameChannels} live surfaces` : ''}`,
|
|
51
|
+
)
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
const remainingMs = timeoutMs - elapsedMs
|
|
55
|
+
const idleBudgetMs = Math.max(100, Math.min(10_000, remainingMs))
|
|
56
|
+
const idle = await waitForSootsimIdle({
|
|
57
|
+
bridge,
|
|
58
|
+
maxMs: idleBudgetMs,
|
|
59
|
+
pollMs: 32,
|
|
60
|
+
stablePolls: 2,
|
|
61
|
+
})
|
|
62
|
+
if (!idle.settled) {
|
|
63
|
+
console.error(
|
|
64
|
+
` ⚠ wait ready timed out after ${elapsedMs + idle.elapsed}ms — app mounted but did not settle (nodes: ${nodes}, targets: ${targets})`,
|
|
65
|
+
)
|
|
66
|
+
rnxExit(1)
|
|
67
|
+
}
|
|
68
|
+
console.log(
|
|
69
|
+
` ready in ${elapsedMs + idle.elapsed}ms: ${nodes} nodes, ${targets} targets${liveFrameActive ? `, ${liveFrameChannels} live surfaces` : ''}`,
|
|
70
|
+
)
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
const reason = waitReadyReason({
|
|
74
|
+
externalError,
|
|
75
|
+
loadingText,
|
|
76
|
+
externalReady,
|
|
77
|
+
externalStatus,
|
|
78
|
+
flag,
|
|
79
|
+
targets,
|
|
80
|
+
suppressedEntryError,
|
|
81
|
+
bridgeError,
|
|
82
|
+
})
|
|
83
|
+
console.error(
|
|
84
|
+
` ⚠ wait ready timed out after ${elapsedMs}ms — ${reason} (nodes: ${nodes}, targets: ${targets}, live surfaces: ${liveFrameActive ? liveFrameChannels : 0}, errors: ${errors})`,
|
|
85
|
+
)
|
|
86
|
+
rnxExit(1)
|
|
87
|
+
}
|