peachsim 0.1.571 → 0.1.573
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 +1 -1
- package/README.md +106 -106
- package/cli/app-config.ts +1 -1
- package/cli/app-fonts.ts +4 -4
- package/cli/auth.ts +11 -11
- package/cli/bin.ts +4 -4
- package/cli/bridge-flow-runner.ts +13 -13
- package/cli/browser-evals.ts +2 -2
- package/cli/cli-update.ts +6 -6
- package/cli/cloud-client.ts +16 -16
- package/cli/cloud-dispatch.ts +2 -2
- package/cli/cloud-session.ts +12 -12
- package/cli/cloud-test-tunnel.ts +4 -4
- package/cli/command-registry.ts +7 -7
- package/cli/commands/agent-wrapper.ts +7 -7
- package/cli/commands/agent.ts +16 -16
- package/cli/commands/app-fonts.ts +3 -3
- package/cli/commands/assert.ts +10 -10
- package/cli/commands/auth.ts +5 -5
- package/cli/commands/box/checkout-plane.ts +1 -1
- package/cli/commands/box/plane-share.ts +2 -2
- package/cli/commands/box/rnx-command.ts +5 -5
- package/cli/commands/box.ts +38 -38
- package/cli/commands/camera.ts +13 -13
- package/cli/commands/cleanup.ts +13 -13
- package/cli/commands/compat.ts +7 -7
- package/cli/commands/config.ts +5 -5
- package/cli/commands/control.ts +63 -63
- package/cli/commands/cpu-profile.ts +11 -11
- package/cli/commands/daemon-mac-app.ts +10 -10
- package/cli/commands/daemon.ts +29 -27
- package/cli/commands/debug.ts +35 -35
- package/cli/commands/desktop.ts +3 -3
- package/cli/commands/detox.ts +33 -33
- package/cli/commands/device.ts +9 -9
- package/cli/commands/diagnose.ts +17 -17
- package/cli/commands/do-chain.ts +4 -4
- package/cli/commands/electron.ts +9 -9
- package/cli/commands/film.ts +9 -9
- package/cli/commands/flow.ts +49 -49
- package/cli/commands/inspect/core.ts +12 -12
- package/cli/commands/inspect/count.ts +1 -1
- package/cli/commands/inspect/describe.ts +1 -1
- package/cli/commands/inspect/env.ts +2 -2
- package/cli/commands/inspect/find.ts +2 -2
- package/cli/commands/inspect/get-layout.ts +2 -2
- package/cli/commands/inspect/keyboard.ts +1 -1
- package/cli/commands/inspect/list.ts +4 -4
- package/cli/commands/inspect/memory.ts +1 -1
- package/cli/commands/inspect/resolve-target.ts +1 -1
- package/cli/commands/inspect/screens.ts +1 -1
- package/cli/commands/inspect/shared.ts +1 -1
- package/cli/commands/inspect/tree.ts +1 -1
- package/cli/commands/inspect/url.ts +1 -1
- package/cli/commands/inspect/wait-event.ts +5 -5
- package/cli/commands/inspect.ts +82 -82
- package/cli/commands/install-cli.ts +16 -16
- package/cli/commands/install-desktop.ts +26 -26
- package/cli/commands/login.ts +11 -11
- package/cli/commands/maestro-generate.ts +10 -10
- package/cli/commands/maestro.ts +77 -67
- package/cli/commands/mode.ts +4 -4
- package/cli/commands/no-bridge-hint.ts +12 -12
- package/cli/commands/perf.ts +11 -11
- package/cli/commands/permissions.ts +5 -5
- package/cli/commands/platform.ts +21 -21
- package/cli/commands/preview.ts +5 -5
- package/cli/commands/profile.ts +12 -12
- package/cli/commands/react.ts +15 -15
- package/cli/commands/record.ts +31 -31
- package/cli/commands/report-issue.ts +4 -4
- package/cli/commands/reset.ts +2 -2
- package/cli/commands/runtime.ts +23 -23
- package/cli/commands/screenshot-command.ts +2 -2
- package/cli/commands/screenshot-layers.ts +5 -5
- package/cli/commands/screenshot-mode.ts +7 -7
- package/cli/commands/screenshot.ts +24 -24
- package/cli/commands/screenshots-capture.ts +13 -13
- package/cli/commands/screenshots.ts +6 -6
- package/cli/commands/serve.ts +12 -12
- package/cli/commands/setup.ts +25 -25
- package/cli/commands/skills.ts +30 -23
- package/cli/commands/slides.ts +20 -20
- package/cli/commands/state.ts +2 -2
- package/cli/commands/storage.ts +3 -3
- package/cli/commands/telemetry.ts +2 -2
- package/cli/commands/test.ts +15 -15
- package/cli/commands/three-mode.ts +17 -17
- package/cli/commands/timeline.ts +12 -12
- package/cli/commands/upgrade.ts +11 -11
- package/cli/commands/upload.ts +26 -26
- package/cli/commands/version.ts +7 -7
- package/cli/commands/what-happened.ts +20 -20
- package/cli/current-sim.ts +3 -3
- package/cli/desktop-companion.ts +16 -16
- package/cli/drivers/electron.ts +4 -4
- package/cli/drivers/playwright-sim-host.ts +7 -7
- package/cli/drivers/playwright.ts +4 -4
- package/cli/drivers/registry.ts +1 -1
- package/cli/drivers/types.ts +5 -5
- package/cli/flow-export.ts +1 -1
- package/cli/flow-file.ts +5 -5
- package/cli/flow-session.ts +1 -1
- package/cli/help-core.ts +13 -13
- package/cli/hints.ts +14 -14
- package/cli/inspect-notice-state.ts +1 -1
- package/cli/internal-child.ts +2 -2
- package/cli/maestro-js.ts +4 -4
- package/cli/maestro-report.ts +1 -1
- package/cli/main.ts +14 -14
- package/cli/outbound-endpoints.ts +30 -30
- package/cli/parse-args.ts +7 -7
- package/cli/privacy.ts +1 -1
- package/cli/recording-access.ts +10 -10
- package/cli/resolve-assets.ts +2 -2
- package/cli/rnx-in-process.ts +5 -5
- package/cli/run-registry.ts +1 -1
- package/cli/run-rnx-core.ts +3 -3
- package/cli/run-rnx.ts +1 -1
- package/cli/runtime-notes.ts +1 -1
- package/cli/self-invocation.ts +1 -1
- package/cli/send-to-box.ts +1 -1
- package/cli/shell-init.ts +40 -40
- package/cli/ui.ts +1 -1
- package/cli/ws-bridge.ts +22 -22
- package/detox/config-loader.ts +1 -1
- package/detox/index.ts +28 -28
- package/detox/navigation.ts +2 -2
- package/dist-lib/agent-daemon-client.cjs +12 -12
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +2 -2
- package/dist-lib/agent-sessions.cjs +12 -12
- package/dist-lib/attached-projects.cjs +4 -4
- package/dist-lib/auth/shared-session.cjs +4 -4
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +4 -4
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +2 -2
- package/dist-lib/bridge-contract.mjs +2 -2
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +42 -42
- package/dist-lib/cloud.mjs +42 -42
- package/dist-lib/config.cjs +2 -2
- package/dist-lib/detox/index.cjs +29 -29
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +12 -12
- package/dist-lib/host/bridge-host.cjs +72 -71
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +6 -6
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +48 -48
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/menu.cjs +5 -5
- package/dist-lib/menu.mjs +5 -5
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +15 -15
- package/dist-lib/profiles.cjs +4 -4
- package/dist-lib/public-brand.cjs +3 -3
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +2 -1
- package/dist-lib/sdk.cjs +9 -9
- package/dist-lib/sdk.mjs +9 -9
- package/dist-lib/skills.cjs +416 -415
- package/dist-lib/swift-compile.cjs +1 -1
- package/dist-lib/swift-compile.mjs +1 -1
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite.cjs +24 -24
- package/package.json +1 -1
- package/scripts/dev-server-scanner.ts +4 -3
- package/skills/contrast/SKILL.md +75 -75
- package/skills/{rnx-debug → peach-debug}/SKILL.md +90 -90
- package/skills/{rnx-perf → peach-perf}/SKILL.md +13 -13
- package/skills/{rnx-setup → peach-setup}/SKILL.md +60 -60
- package/skills/{rnx-test → peach-test}/SKILL.md +44 -44
- package/skills/{rnx-visual → peach-visual}/SKILL.md +26 -26
- package/src/agent-daemon-client.ts +5 -5
- package/src/agent-events.ts +1 -1
- package/src/agent-identity.ts +1 -1
- package/src/agent-sessions.ts +13 -13
- package/src/attached-projects.ts +1 -1
- package/src/auth/shared-session.ts +1 -1
- package/src/bridge-constants.ts +3 -3
- package/src/bridge-contract-input.ts +1 -1
- package/src/bridge-contract.ts +3 -3
- package/src/cli-version.ts +6 -6
- package/src/cloud.ts +40 -40
- package/src/config.ts +1 -1
- package/src/connect.ts +6 -6
- package/src/dev-server-open.ts +8 -8
- package/src/disk-cleanup.ts +3 -3
- package/src/home-paths.ts +26 -26
- package/src/host/agent-host.ts +3 -3
- package/src/host/bridge-host.ts +33 -33
- package/src/host/fetch-proxy-handler.ts +1 -1
- package/src/host/plane-host.ts +2 -2
- package/src/host/replacement-module-handler.ts +2 -2
- package/src/menu.ts +2 -2
- package/src/metro-plugin.ts +7 -7
- package/src/metro-production-bundle.ts +2 -2
- package/src/native-dev-bundle-url.ts +2 -2
- package/src/playwright-host-log.ts +1 -1
- package/src/public-brand.ts +2 -2
- package/src/react-native-host-modules.ts +1 -1
- package/src/rolldown-plugin-swift.ts +3 -3
- package/src/runtime-assets.ts +11 -11
- package/src/runtime-delivery.ts +6 -6
- package/src/screenshots/orchestrate.ts +1 -1
- package/src/screenshots/org-deck.ts +1 -1
- package/src/screenshots/schema.ts +2 -2
- package/src/sim-client.ts +1 -1
- package/src/skills/builtin/compat-check.ts +2 -2
- package/src/vite-plugin-one.ts +8 -8
- package/src/vite-plugin-swift.ts +3 -3
- package/src/vite-plugin.ts +6 -19
package/cli/commands/cleanup.ts
CHANGED
|
@@ -35,13 +35,13 @@ function summarize(entries: SootsimCleanupEntry[]): string[] {
|
|
|
35
35
|
|
|
36
36
|
function printHelp(): void {
|
|
37
37
|
console.log(`
|
|
38
|
-
|
|
38
|
+
peach cleanup — inspect and reclaim local peach disk usage
|
|
39
39
|
|
|
40
40
|
usage:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
peach cleanup
|
|
42
|
+
peach cleanup --apply
|
|
43
|
+
peach cleanup --aggressive
|
|
44
|
+
peach cleanup --apply --aggressive
|
|
45
45
|
|
|
46
46
|
The default is a read-only preview. --apply removes downloaded runtime archives,
|
|
47
47
|
old runtimes outside a two-version rollback window, and disposable browser
|
|
@@ -61,14 +61,14 @@ export async function runCleanup(args: string[]): Promise<number> {
|
|
|
61
61
|
if (result.status === 'complete') {
|
|
62
62
|
process.stderr.write(
|
|
63
63
|
result.reclaimedBytes > 0
|
|
64
|
-
? `
|
|
64
|
+
? ` peach automatic cleanup reclaimed ${formatSootsimBytes(
|
|
65
65
|
result.reclaimedBytes,
|
|
66
66
|
)}; active runtimes and app data were kept.\n`
|
|
67
|
-
: '
|
|
67
|
+
: ' peach automatic cleanup found no obsolete local data.\n',
|
|
68
68
|
)
|
|
69
69
|
} else if (result.status === 'deferred') {
|
|
70
70
|
process.stderr.write(
|
|
71
|
-
`
|
|
71
|
+
` peach automatic cleanup reclaimed ${formatSootsimBytes(
|
|
72
72
|
result.reclaimedBytes,
|
|
73
73
|
)}; ${formatSootsimBytes(
|
|
74
74
|
result.deferredInUseBytes,
|
|
@@ -78,7 +78,7 @@ export async function runCleanup(args: string[]): Promise<number> {
|
|
|
78
78
|
return 0
|
|
79
79
|
} catch (error) {
|
|
80
80
|
process.stderr.write(
|
|
81
|
-
`
|
|
81
|
+
` peach automatic cleanup will retry on the next run: ${
|
|
82
82
|
error instanceof Error ? error.message : String(error)
|
|
83
83
|
}\n`,
|
|
84
84
|
)
|
|
@@ -112,7 +112,7 @@ export async function runCleanup(args: string[]): Promise<number> {
|
|
|
112
112
|
(entry) => entry.kind === 'runtime-version' && entry.state === 'aggressive',
|
|
113
113
|
)
|
|
114
114
|
|
|
115
|
-
console.log(`
|
|
115
|
+
console.log(` peach data home: ${plan.home}`)
|
|
116
116
|
console.log(` current size: ${formatSootsimBytes(plan.totalBytes)}`)
|
|
117
117
|
console.log(` active runtime: ${plan.activeRuntime ?? '(none installed)'}`)
|
|
118
118
|
console.log('')
|
|
@@ -143,7 +143,7 @@ export async function runCleanup(args: string[]): Promise<number> {
|
|
|
143
143
|
console.log(
|
|
144
144
|
` ${formatSootsimBytes(
|
|
145
145
|
inUseBrowserCaches.reduce((total, entry) => total + entry.bytes, 0),
|
|
146
|
-
)} ` + `of browser cache is open; close
|
|
146
|
+
)} ` + `of browser cache is open; close peach and rerun cleanup`,
|
|
147
147
|
)
|
|
148
148
|
}
|
|
149
149
|
|
|
@@ -151,7 +151,7 @@ export async function runCleanup(args: string[]): Promise<number> {
|
|
|
151
151
|
console.log(` reclaimable now: ${formatSootsimBytes(plan.reclaimableBytes)}`)
|
|
152
152
|
if (!apply) {
|
|
153
153
|
console.log(
|
|
154
|
-
` preview only — run \`
|
|
154
|
+
` preview only — run \`peach cleanup --apply${
|
|
155
155
|
aggressive ? ' --aggressive' : ''
|
|
156
156
|
}\` to remove these items`,
|
|
157
157
|
)
|
|
@@ -164,6 +164,6 @@ export async function runCleanup(args: string[]): Promise<number> {
|
|
|
164
164
|
result.removedPaths.length === 1 ? 'item' : 'items'
|
|
165
165
|
}; reclaimed ${formatSootsimBytes(result.reclaimedBytes)}`,
|
|
166
166
|
)
|
|
167
|
-
console.log(` remaining
|
|
167
|
+
console.log(` remaining peach data: ${formatSootsimBytes(result.remainingBytes)}`)
|
|
168
168
|
return 0
|
|
169
169
|
}
|
package/cli/commands/compat.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// peach compat — scan dependencies with the shared compatibility engine.
|
|
2
2
|
|
|
3
3
|
import { existsSync, readFileSync } from 'node:fs'
|
|
4
4
|
import { dirname, join, relative, resolve } from 'node:path'
|
|
@@ -20,12 +20,12 @@ interface PackageManifest {
|
|
|
20
20
|
export async function runCompat(args: string[]): Promise<void> {
|
|
21
21
|
if (args.includes('--help') || args.includes('-h')) {
|
|
22
22
|
console.log(`
|
|
23
|
-
|
|
23
|
+
peach compat — check package compatibility
|
|
24
24
|
|
|
25
25
|
usage:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
peach compat [package-name]
|
|
27
|
+
peach compat --json
|
|
28
|
+
peach compat --brief
|
|
29
29
|
|
|
30
30
|
flags:
|
|
31
31
|
--json machine-readable output
|
|
@@ -80,7 +80,7 @@ flags:
|
|
|
80
80
|
if (elsewhere.length > 1) {
|
|
81
81
|
console.log(`\n this workspace has more than one app. scan one with --app:`)
|
|
82
82
|
for (const entry of elsewhere) {
|
|
83
|
-
console.log(`
|
|
83
|
+
console.log(` peach compat --app ${relative(appDir, entry.dir)}`)
|
|
84
84
|
}
|
|
85
85
|
console.log()
|
|
86
86
|
return
|
|
@@ -99,7 +99,7 @@ function report(args: string[], result: ScanResult): void {
|
|
|
99
99
|
return
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
console.log(`\n
|
|
102
|
+
console.log(`\n peach compatibility report`)
|
|
103
103
|
console.log(` ${formatScanCounts(result)}\n`)
|
|
104
104
|
for (const line of formatDetailedScanLines(result)) console.log(` ${line}`)
|
|
105
105
|
const supported = result.packages.filter((item) => item.status === 'full').length
|
package/cli/commands/config.ts
CHANGED
|
@@ -3,15 +3,15 @@ import { runTelemetry } from './telemetry'
|
|
|
3
3
|
|
|
4
4
|
function printHelp(): void {
|
|
5
5
|
console.log(`
|
|
6
|
-
|
|
6
|
+
peach config — manage local CLI preferences
|
|
7
7
|
|
|
8
8
|
usage:
|
|
9
|
-
|
|
9
|
+
peach config telemetry [on|off|status]
|
|
10
10
|
|
|
11
11
|
environment:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
PEACH_HINTS=off suppress CLI hints
|
|
13
|
+
PEACH_HINTS=always show every CLI hint every time
|
|
14
|
+
PEACH_UPDATE_CHECK=off disable background CLI update checks and notices
|
|
15
15
|
`)
|
|
16
16
|
}
|
|
17
17
|
|
package/cli/commands/control.ts
CHANGED
|
@@ -183,7 +183,7 @@ export async function ensureBridgeForOpen(
|
|
|
183
183
|
return preferredPort
|
|
184
184
|
}
|
|
185
185
|
if (explicitPort && preferredWorld !== null) {
|
|
186
|
-
throw new Error(`
|
|
186
|
+
throw new Error(`peach open: requested bridge port ${preferredPort} is not reachable.`)
|
|
187
187
|
}
|
|
188
188
|
if (
|
|
189
189
|
!explicitPort &&
|
|
@@ -195,7 +195,7 @@ export async function ensureBridgeForOpen(
|
|
|
195
195
|
}
|
|
196
196
|
if (lock && isDaemonLockfileFresh(lock)) {
|
|
197
197
|
throw new Error(
|
|
198
|
-
`
|
|
198
|
+
`peach bridge lockfile is fresh (port ${lock.bridgePort}) but the bridge is not reachable. run \`peach daemon restart\` or \`peach daemon uninstall\`.`,
|
|
199
199
|
)
|
|
200
200
|
}
|
|
201
201
|
|
|
@@ -219,18 +219,18 @@ export async function ensureBridgeForOpen(
|
|
|
219
219
|
(await canConnectToBridge(selectedPort, 250))
|
|
220
220
|
) {
|
|
221
221
|
console.error(
|
|
222
|
-
` Started a local bridge on port ${selectedPort}. Run \`
|
|
222
|
+
` Started a local bridge on port ${selectedPort}. Run \`peach daemon install\` to keep it ready between commands.`,
|
|
223
223
|
)
|
|
224
224
|
return selectedPort
|
|
225
225
|
}
|
|
226
226
|
if (childExit) {
|
|
227
|
-
throw new Error(`
|
|
227
|
+
throw new Error(`peach bridge exited before becoming ready (exit ${childExit})`)
|
|
228
228
|
}
|
|
229
229
|
await sleep(150)
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
throw new Error(
|
|
233
|
-
`
|
|
233
|
+
`peach bridge did not start within ${Math.round(OPEN_BRIDGE_SPAWN_TIMEOUT_MS / 1000)}s`,
|
|
234
234
|
)
|
|
235
235
|
}
|
|
236
236
|
|
|
@@ -256,14 +256,14 @@ function assertExplicitBridgeLock(
|
|
|
256
256
|
return
|
|
257
257
|
}
|
|
258
258
|
throw new Error(
|
|
259
|
-
`${rnxPublicBrand.commandName} open: requested bridge port ${preferredPort}, but the active daemon owns port ${lock.bridgePort}. explicit --port never selects another bridge; stop that daemon or use an isolated
|
|
259
|
+
`${rnxPublicBrand.commandName} open: requested bridge port ${preferredPort}, but the active daemon owns port ${lock.bridgePort}. explicit --port never selects another bridge; stop that daemon or use an isolated PEACH_HOME.`,
|
|
260
260
|
)
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
async function ensureRuntimeForOpen() {
|
|
264
264
|
const active = readActiveRuntime()
|
|
265
265
|
if (active && fs.existsSync(runtimeDir(active))) return
|
|
266
|
-
console.error(' installing
|
|
266
|
+
console.error(' installing peach engine runtime...')
|
|
267
267
|
const { runRuntime } = await import('./runtime')
|
|
268
268
|
await runRuntime(['install'], {})
|
|
269
269
|
}
|
|
@@ -351,10 +351,10 @@ function buildShellPathOpenUrl(target: string, baseUrl: string): string {
|
|
|
351
351
|
return url.toString()
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
//
|
|
354
|
+
// peach share/preview/build links (e.g. https://rnxsim.com/preview/<id>,
|
|
355
355
|
// https://contrast.dev/build/<owner>/<repo>/<branch>) are full player pages
|
|
356
356
|
// served by the Contrast app — NOT Metro bundles and NOT driveable shell sims.
|
|
357
|
-
// passing one to `
|
|
357
|
+
// passing one to `peach open` used to fall through to the bundle resolver,
|
|
358
358
|
// which fetched the HTML page and choked with "Unexpected token '<'". two
|
|
359
359
|
// hard reasons they can't become a CLI-driveable sim:
|
|
360
360
|
// 1. the engine only attaches to the local CLI bridge from a localhost /
|
|
@@ -517,18 +517,18 @@ function parseOpenRuntimeConfig(args: string[]): RNXConfig | undefined {
|
|
|
517
517
|
if (args[i] === '--replace') {
|
|
518
518
|
const raw = args[i + 1]
|
|
519
519
|
if (!raw) {
|
|
520
|
-
console.error('
|
|
520
|
+
console.error(' peach open: --replace expects <module>=<file>')
|
|
521
521
|
rnxExit(1)
|
|
522
522
|
}
|
|
523
523
|
const eqIndex = raw.indexOf('=')
|
|
524
524
|
if (eqIndex <= 0 || eqIndex === raw.length - 1) {
|
|
525
|
-
console.error('
|
|
525
|
+
console.error(' peach open: --replace expects <module>=<file>')
|
|
526
526
|
rnxExit(1)
|
|
527
527
|
}
|
|
528
528
|
const moduleName = raw.slice(0, eqIndex).trim()
|
|
529
529
|
const filePath = normalizeReplacementPath(raw.slice(eqIndex + 1).trim())
|
|
530
530
|
if (!fs.existsSync(filePath)) {
|
|
531
|
-
console.error(`
|
|
531
|
+
console.error(` peach open: replacement file not found: ${filePath}`)
|
|
532
532
|
rnxExit(1)
|
|
533
533
|
}
|
|
534
534
|
modules[moduleName] = { file: filePath }
|
|
@@ -542,7 +542,7 @@ function parseOpenRuntimeConfig(args: string[]): RNXConfig | undefined {
|
|
|
542
542
|
const raw = args[i + 1]
|
|
543
543
|
const eqIndex = raw ? raw.indexOf('=') : -1
|
|
544
544
|
if (!raw || eqIndex <= 0 || eqIndex === raw.length - 1) {
|
|
545
|
-
console.error('
|
|
545
|
+
console.error(' peach open: --remap expects <host:port>=<host:port>')
|
|
546
546
|
rnxExit(1)
|
|
547
547
|
}
|
|
548
548
|
remap[raw.slice(0, eqIndex).trim()] = raw.slice(eqIndex + 1).trim()
|
|
@@ -585,7 +585,7 @@ export function applyRuntimeGenerationOrigin(baseUrl: string, version?: string):
|
|
|
585
585
|
if (!isLoopbackHost(url.hostname)) {
|
|
586
586
|
if (!version) return baseUrl
|
|
587
587
|
throw new Error(
|
|
588
|
-
`runtimeVersion ${selectedVersion} requires a local
|
|
588
|
+
`runtimeVersion ${selectedVersion} requires a local peach host; got ${url.origin}`,
|
|
589
589
|
)
|
|
590
590
|
}
|
|
591
591
|
url.hostname = runtimeVersionHostname(selectedVersion)
|
|
@@ -806,7 +806,7 @@ export async function buildShellUrl(
|
|
|
806
806
|
// callers that hand us a bare port or url (the flow/maestro runner, the
|
|
807
807
|
// electron companion) have no project to pass. derive it from the bundle
|
|
808
808
|
// metro advertises so those paths get the same UIAppFonts and splash
|
|
809
|
-
// stamping an `
|
|
809
|
+
// stamping an `peach open` gets. without it a flow renders the app's text in a
|
|
810
810
|
// fallback typeface, which then reads as a conformance diff.
|
|
811
811
|
const project =
|
|
812
812
|
opts.project ?? (await resolveAppProjectForBundleUrl(bundleUrlOf(resolved)))
|
|
@@ -1065,9 +1065,9 @@ async function waitForProcessExit(pid: number, timeoutMs = 2_500): Promise<boole
|
|
|
1065
1065
|
|
|
1066
1066
|
// a sim record's host pid can outlive its process; after pid reuse the number
|
|
1067
1067
|
// may point at an unrelated process (real incident 2026-07-14: a stale record
|
|
1068
|
-
// made `
|
|
1068
|
+
// made `peach close` SIGTERM the shared vite shell dev server, taking the
|
|
1069
1069
|
// whole dev stack down). only signal a pid whose argv proves it is still a
|
|
1070
|
-
//
|
|
1070
|
+
// peach playwright host launcher.
|
|
1071
1071
|
function isPlaywrightHostProcess(pid: number): boolean {
|
|
1072
1072
|
// `ps eww` prints argv then the process ENVIRONMENT for same-user
|
|
1073
1073
|
// processes. the marker must live in the env SUFFIX (everything past the
|
|
@@ -1101,7 +1101,7 @@ export async function terminatePlaywrightHostsForSims(
|
|
|
1101
1101
|
}
|
|
1102
1102
|
for (const pid of pids) {
|
|
1103
1103
|
if (!isPlaywrightHostProcess(pid)) {
|
|
1104
|
-
console.log(` skipped pid ${pid}: not a
|
|
1104
|
+
console.log(` skipped pid ${pid}: not a peach playwright host (stale sim record)`)
|
|
1105
1105
|
continue
|
|
1106
1106
|
}
|
|
1107
1107
|
try {
|
|
@@ -1187,7 +1187,7 @@ export function printOpenedSim(
|
|
|
1187
1187
|
// bounded — a cold guest bundle takes 5-15s to fully route, so we don't
|
|
1188
1188
|
// try to wait for that. instead: settle for a moment to let the first
|
|
1189
1189
|
// paint land, then describe whatever's on screen. for cold loads that's
|
|
1190
|
-
// a splash; the caller can `
|
|
1190
|
+
// a splash; the caller can `peach describe` again a second later. for
|
|
1191
1191
|
// warm loads it's the real screen. guarded by --no-describe.
|
|
1192
1192
|
async function maybeDescribeAfterOpen(wsPort: number, simId: string, args: string[]) {
|
|
1193
1193
|
if (args.includes('--no-describe')) return
|
|
@@ -1197,7 +1197,7 @@ async function maybeDescribeAfterOpen(wsPort: number, simId: string, args: strin
|
|
|
1197
1197
|
// brief settle, then dump the tree directly. don't go through
|
|
1198
1198
|
// runDescribeSubcommand — its screen-transition wait can block for many
|
|
1199
1199
|
// seconds on a still-routing guest app, which is exactly what we want
|
|
1200
|
-
// to skip for the open-side describe. caller can `
|
|
1200
|
+
// to skip for the open-side describe. caller can `peach describe`
|
|
1201
1201
|
// again later for post-routing state.
|
|
1202
1202
|
await waitForTreeStable(wsPort, simId, { stableMs: 150, maxMs: 400 })
|
|
1203
1203
|
const bridge = createBridge(wsPort, {
|
|
@@ -1223,7 +1223,7 @@ async function maybeDescribeAfterOpen(wsPort: number, simId: string, args: strin
|
|
|
1223
1223
|
console.log(
|
|
1224
1224
|
` app still loading: ${reason} (nodes: ${readyProbe.nodes}, targets: ${readyProbe.targets})`,
|
|
1225
1225
|
)
|
|
1226
|
-
console.log(' before interacting, run:
|
|
1226
|
+
console.log(' before interacting, run: peach wait ready --max-ms 120000')
|
|
1227
1227
|
}
|
|
1228
1228
|
}
|
|
1229
1229
|
const result = await inspectDescribe(bridge, {
|
|
@@ -1295,8 +1295,8 @@ export async function runLinkCommand(args: string[], opts: ControlCommandOptions
|
|
|
1295
1295
|
commandTimeoutMs: opts.timeoutMs,
|
|
1296
1296
|
})
|
|
1297
1297
|
if (parsed.positional.length !== 1) {
|
|
1298
|
-
console.error('
|
|
1299
|
-
console.error(' examples:
|
|
1298
|
+
console.error(' peach open: expected exactly one <url-or-route>')
|
|
1299
|
+
console.error(' examples: peach open /settings | peach open myapp://settings')
|
|
1300
1300
|
rnxExit(1)
|
|
1301
1301
|
}
|
|
1302
1302
|
|
|
@@ -1308,9 +1308,9 @@ export async function runLinkCommand(args: string[], opts: ControlCommandOptions
|
|
|
1308
1308
|
} catch (err) {
|
|
1309
1309
|
console.error(` link failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
1310
1310
|
await printBridgeFailureDiagnostics(bridge, {
|
|
1311
|
-
errorsCommand: `
|
|
1312
|
-
warningsCommand: `
|
|
1313
|
-
requestsCommand: `
|
|
1311
|
+
errorsCommand: `peach get errors 5${simHint}`,
|
|
1312
|
+
warningsCommand: `peach get warnings 5${simHint}`,
|
|
1313
|
+
requestsCommand: `peach get requests 5${simHint}`,
|
|
1314
1314
|
})
|
|
1315
1315
|
rnxExit(1)
|
|
1316
1316
|
} finally {
|
|
@@ -1355,12 +1355,12 @@ export async function runOpenCommand(
|
|
|
1355
1355
|
const cdpPort = cdpPortArg ? Number(cdpPortArg) : undefined
|
|
1356
1356
|
if (cdpPortArg && (!Number.isFinite(cdpPort) || cdpPort! <= 0)) {
|
|
1357
1357
|
console.error(
|
|
1358
|
-
`
|
|
1358
|
+
` peach open: --cdp-port must be a positive port number, got "${cdpPortArg}"`,
|
|
1359
1359
|
)
|
|
1360
1360
|
rnxExit(1)
|
|
1361
1361
|
}
|
|
1362
1362
|
if (requestedProfileId && ephemeralProfile) {
|
|
1363
|
-
console.error('
|
|
1363
|
+
console.error(' peach open: --profile cannot be combined with --ephemeral')
|
|
1364
1364
|
rnxExit(1)
|
|
1365
1365
|
}
|
|
1366
1366
|
const profileId = requestedProfileId ? ensureProfile(requestedProfileId).id : undefined
|
|
@@ -1368,21 +1368,21 @@ export async function runOpenCommand(
|
|
|
1368
1368
|
const commandConfig = parseOpenRuntimeConfig(args)
|
|
1369
1369
|
if (openInNewSim && parsed.simIdSource === 'flag') {
|
|
1370
1370
|
console.error(
|
|
1371
|
-
'
|
|
1371
|
+
' peach open: --new, --profile, and --ephemeral cannot be combined with --sim',
|
|
1372
1372
|
)
|
|
1373
1373
|
rnxExit(1)
|
|
1374
1374
|
}
|
|
1375
1375
|
const target = parsed.positional[0] || ''
|
|
1376
1376
|
|
|
1377
1377
|
// `--port` is the WS BRIDGE port; the app/dev-server port is positional. the
|
|
1378
|
-
// two read identically at a glance, so `
|
|
1378
|
+
// two read identically at a glance, so `peach open --new --port 4260` used to
|
|
1379
1379
|
// launch the Connect launcher against the default bridge and report success,
|
|
1380
1380
|
// and the probe that followed measured whatever sim happened to be selected.
|
|
1381
1381
|
// there is no meaningful `open --port` without a target, so refuse it.
|
|
1382
1382
|
if (!target && args.includes('--port')) {
|
|
1383
1383
|
const portValue = args[args.indexOf('--port') + 1] ?? '<number>'
|
|
1384
1384
|
console.error(
|
|
1385
|
-
`
|
|
1385
|
+
` peach open: --port is the WS bridge port, not the app port. to open the app on ${portValue}, pass it positionally: \`peach open ${portValue}\``,
|
|
1386
1386
|
)
|
|
1387
1387
|
rnxExit(1)
|
|
1388
1388
|
}
|
|
@@ -1390,7 +1390,7 @@ export async function runOpenCommand(
|
|
|
1390
1390
|
if (isAppDeepLinkTarget(target)) {
|
|
1391
1391
|
if (openInNewSim) {
|
|
1392
1392
|
console.error(
|
|
1393
|
-
'
|
|
1393
|
+
' peach open: deep links target the current app; remove --new, --profile, or --ephemeral',
|
|
1394
1394
|
)
|
|
1395
1395
|
rnxExit(1)
|
|
1396
1396
|
}
|
|
@@ -1410,7 +1410,7 @@ export async function runOpenCommand(
|
|
|
1410
1410
|
}
|
|
1411
1411
|
})()
|
|
1412
1412
|
console.log(
|
|
1413
|
-
' that’s
|
|
1413
|
+
' that’s a peach preview/build link — a full player page, not a driveable sim.',
|
|
1414
1414
|
)
|
|
1415
1415
|
console.log(' opening it in your browser for viewing…')
|
|
1416
1416
|
try {
|
|
@@ -1424,8 +1424,8 @@ export async function runOpenCommand(
|
|
|
1424
1424
|
}
|
|
1425
1425
|
console.log(
|
|
1426
1426
|
isLocal
|
|
1427
|
-
? ' to drive it under the CLI, point `
|
|
1428
|
-
: ' the CLI can’t drive a remote preview (the engine only attaches to the local bridge from localhost). to drive the app, run it locally and `
|
|
1427
|
+
? ' to drive it under the CLI, point `peach open` at the app’s dev port instead (e.g. `peach open 8081`).'
|
|
1428
|
+
: ' the CLI can’t drive a remote preview (the engine only attaches to the local bridge from localhost). to drive the app, run it locally and `peach open <port>`.',
|
|
1429
1429
|
)
|
|
1430
1430
|
return
|
|
1431
1431
|
}
|
|
@@ -1475,7 +1475,7 @@ export async function runOpenCommand(
|
|
|
1475
1475
|
// native desktop surface. neither path opens the user's browser profile.
|
|
1476
1476
|
const requestedDriverId = args.find((_, i) => args[i - 1] === '--driver') || ''
|
|
1477
1477
|
if (requestedDriverId && !isDriverId(requestedDriverId)) {
|
|
1478
|
-
console.error(` unknown driver "${requestedDriverId}" — run \`
|
|
1478
|
+
console.error(` unknown driver "${requestedDriverId}" — run \`peach list --drivers\``)
|
|
1479
1479
|
rnxExit(1)
|
|
1480
1480
|
}
|
|
1481
1481
|
const driverId: DriverId = isDriverId(requestedDriverId)
|
|
@@ -1489,21 +1489,21 @@ export async function runOpenCommand(
|
|
|
1489
1489
|
: readSimulatorDriverPreference() || 'playwright'
|
|
1490
1490
|
const driverHeadless = resolveDriverHeadless(args, driverId)
|
|
1491
1491
|
if (cdpPort && driverId !== 'playwright') {
|
|
1492
|
-
console.error('
|
|
1492
|
+
console.error(' peach open: --cdp-port is only honored by the playwright driver')
|
|
1493
1493
|
rnxExit(1)
|
|
1494
1494
|
}
|
|
1495
1495
|
|
|
1496
1496
|
// metro HMR mode. interactive opens default to hot=true so edits hot-apply
|
|
1497
|
-
// through
|
|
1497
|
+
// through peach's HmrClient — expo CLI's createBundleUrlSearchParams
|
|
1498
1498
|
// hardcodes hot=false on every launchAsset URL (a leftover toggle real iOS
|
|
1499
|
-
// sims tolerate but
|
|
1499
|
+
// sims tolerate but peach couldn't until now), so without this flip the
|
|
1500
1500
|
// bundle prelude doesn't include metro's HMR runtime + react-refresh and
|
|
1501
1501
|
// edits silently no-op. `--driver playwright` (agent / CI runs) defaults
|
|
1502
1502
|
// to hot=false for deterministic e2e. explicit `--hot` / `--no-hmr` win.
|
|
1503
1503
|
const hotFlag = args.includes('--hot')
|
|
1504
1504
|
const noHmrFlag = args.includes('--no-hmr')
|
|
1505
1505
|
if (hotFlag && noHmrFlag) {
|
|
1506
|
-
console.error('
|
|
1506
|
+
console.error(' peach open: --hot and --no-hmr cannot be combined')
|
|
1507
1507
|
rnxExit(1)
|
|
1508
1508
|
}
|
|
1509
1509
|
const hotMode = hotFlag ? true : noHmrFlag ? false : driverId !== 'playwright'
|
|
@@ -1514,7 +1514,7 @@ export async function runOpenCommand(
|
|
|
1514
1514
|
// shell last persisted, which silently skews browser comparisons.
|
|
1515
1515
|
const deviceArg = args.find((_, i) => args[i - 1] === '--device')
|
|
1516
1516
|
if (deviceArg && !(deviceArg in devices)) {
|
|
1517
|
-
console.error(`
|
|
1517
|
+
console.error(` peach open: unknown device "${deviceArg}"`)
|
|
1518
1518
|
console.error(` known devices: ${listSelectableDeviceModels().join(', ')}`)
|
|
1519
1519
|
rnxExit(1)
|
|
1520
1520
|
}
|
|
@@ -1527,7 +1527,7 @@ export async function runOpenCommand(
|
|
|
1527
1527
|
const viewportArg = args.find((_, i) => args[i - 1] === '--viewport')
|
|
1528
1528
|
const viewportMatch = viewportArg ? /^(\d+)x(\d+)$/.exec(viewportArg) : null
|
|
1529
1529
|
if (viewportArg && !viewportMatch) {
|
|
1530
|
-
console.error(`
|
|
1530
|
+
console.error(` peach open: --viewport must be <width>x<height>, got "${viewportArg}"`)
|
|
1531
1531
|
rnxExit(1)
|
|
1532
1532
|
}
|
|
1533
1533
|
const explicitViewport = viewportMatch
|
|
@@ -1652,9 +1652,9 @@ export async function runOpenCommand(
|
|
|
1652
1652
|
` open failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
1653
1653
|
)
|
|
1654
1654
|
await printBridgeFailureDiagnostics(bridge, {
|
|
1655
|
-
errorsCommand: `
|
|
1656
|
-
warningsCommand: `
|
|
1657
|
-
requestsCommand: `
|
|
1655
|
+
errorsCommand: `peach get errors 5${simHint}`,
|
|
1656
|
+
warningsCommand: `peach get warnings 5${simHint}`,
|
|
1657
|
+
requestsCommand: `peach get requests 5${simHint}`,
|
|
1658
1658
|
})
|
|
1659
1659
|
rnxExit(1)
|
|
1660
1660
|
}
|
|
@@ -1676,9 +1676,9 @@ export async function runOpenCommand(
|
|
|
1676
1676
|
` timed out waiting for sim ${targetSim.id} to load ${navigatedShellUrl}`,
|
|
1677
1677
|
)
|
|
1678
1678
|
await printBridgeFailureDiagnostics(bridge, {
|
|
1679
|
-
errorsCommand: `
|
|
1680
|
-
warningsCommand: `
|
|
1681
|
-
requestsCommand: `
|
|
1679
|
+
errorsCommand: `peach get errors 5${simHint}`,
|
|
1680
|
+
warningsCommand: `peach get warnings 5${simHint}`,
|
|
1681
|
+
requestsCommand: `peach get requests 5${simHint}`,
|
|
1682
1682
|
})
|
|
1683
1683
|
rnxExit(1)
|
|
1684
1684
|
}
|
|
@@ -1698,9 +1698,9 @@ export async function runOpenCommand(
|
|
|
1698
1698
|
` timed out waiting for current sim to load target after ${ready.attempts} polls over ${Math.round((ready.attempts * ready.intervalMs) / 1000)}s; ${observed}`,
|
|
1699
1699
|
)
|
|
1700
1700
|
await printBridgeFailureDiagnostics(bridge, {
|
|
1701
|
-
errorsCommand: `
|
|
1702
|
-
warningsCommand: `
|
|
1703
|
-
requestsCommand: `
|
|
1701
|
+
errorsCommand: `peach get errors 5${simHint}`,
|
|
1702
|
+
warningsCommand: `peach get warnings 5${simHint}`,
|
|
1703
|
+
requestsCommand: `peach get requests 5${simHint}`,
|
|
1704
1704
|
})
|
|
1705
1705
|
rnxExit(1)
|
|
1706
1706
|
}
|
|
@@ -1731,7 +1731,7 @@ export async function runOpenCommand(
|
|
|
1731
1731
|
}
|
|
1732
1732
|
const driver = getDriver(driverId)
|
|
1733
1733
|
if (!driver) {
|
|
1734
|
-
console.error(` unknown driver "${driverId}" — run \`
|
|
1734
|
+
console.error(` unknown driver "${driverId}" — run \`peach list --drivers\``)
|
|
1735
1735
|
rnxExit(1)
|
|
1736
1736
|
}
|
|
1737
1737
|
const connectWait = resolveDriverConnectWaitOptions()
|
|
@@ -1811,7 +1811,7 @@ async function runUseLikeCommand(args: string[], opts: ControlCommandOptions = {
|
|
|
1811
1811
|
// `use`/`focus` retarget the CLI at a sim — silently grabbing one a
|
|
1812
1812
|
// human is actively driving is the same footgun `claim` guards against,
|
|
1813
1813
|
// so honor the lock here too (this command historically did not, which
|
|
1814
|
-
// let `
|
|
1814
|
+
// let `peach use <id>` steal the user's live tab).
|
|
1815
1815
|
if (target.lockedBy && target.lockExpiresAt && target.lockExpiresAt > Date.now()) {
|
|
1816
1816
|
const secs = Math.max(0, Math.round((target.lockExpiresAt - Date.now()) / 1000))
|
|
1817
1817
|
if (target.lockedByKind === 'user-active') {
|
|
@@ -1821,7 +1821,7 @@ async function runUseLikeCommand(args: string[], opts: ControlCommandOptions = {
|
|
|
1821
1821
|
console.error(
|
|
1822
1822
|
` refused: ${target.id} is locked by the active user (${secs}s) — that's a live human tab`,
|
|
1823
1823
|
)
|
|
1824
|
-
console.error(` run \`
|
|
1824
|
+
console.error(` run \`peach open --new\` for a fresh investigation sim`)
|
|
1825
1825
|
rnxExit(1)
|
|
1826
1826
|
}
|
|
1827
1827
|
if (!force) {
|
|
@@ -1832,7 +1832,7 @@ async function runUseLikeCommand(args: string[], opts: ControlCommandOptions = {
|
|
|
1832
1832
|
` refused: ${target.id} is leased by ${target.lockedBy} (${secs}s)`,
|
|
1833
1833
|
)
|
|
1834
1834
|
console.error(
|
|
1835
|
-
` \`
|
|
1835
|
+
` \`peach use ${target.id} --force\` to take it, or \`peach open --new\` for a fresh sim`,
|
|
1836
1836
|
)
|
|
1837
1837
|
rnxExit(1)
|
|
1838
1838
|
}
|
|
@@ -1844,9 +1844,9 @@ async function runUseLikeCommand(args: string[], opts: ControlCommandOptions = {
|
|
|
1844
1844
|
rethrowIfExit(err)
|
|
1845
1845
|
console.error(` use failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
1846
1846
|
await printBridgeFailureDiagnostics(bridge, {
|
|
1847
|
-
errorsCommand: `
|
|
1848
|
-
warningsCommand: `
|
|
1849
|
-
requestsCommand: `
|
|
1847
|
+
errorsCommand: `peach get errors 5${simHint}`,
|
|
1848
|
+
warningsCommand: `peach get warnings 5${simHint}`,
|
|
1849
|
+
requestsCommand: `peach get requests 5${simHint}`,
|
|
1850
1850
|
})
|
|
1851
1851
|
rnxExit(1)
|
|
1852
1852
|
}
|
|
@@ -1892,7 +1892,7 @@ export async function runClaimCommand(args: string[], opts: ControlCommandOption
|
|
|
1892
1892
|
if (err instanceof BridgeSimLockedError) {
|
|
1893
1893
|
const secs = Math.max(0, Math.round(err.lock.expiresInMs / 1000))
|
|
1894
1894
|
console.error(` claim failed: locked by ${err.lock.by} for ${secs}s more`)
|
|
1895
|
-
console.error(` use --force to take it, or \`
|
|
1895
|
+
console.error(` use --force to take it, or \`peach open --new\` for a fresh sim`)
|
|
1896
1896
|
rnxExit(1)
|
|
1897
1897
|
}
|
|
1898
1898
|
console.error(` claim failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
@@ -2073,7 +2073,7 @@ export async function runCloseCommand(args: string[], opts: ControlCommandOption
|
|
|
2073
2073
|
)
|
|
2074
2074
|
}
|
|
2075
2075
|
if (plan.refused.some((entry) => entry.kind === 'cli') && !force) {
|
|
2076
|
-
console.error(` \`
|
|
2076
|
+
console.error(` \`peach close --all --force\` to close leased sims too`)
|
|
2077
2077
|
}
|
|
2078
2078
|
if (plan.staleSavedId) {
|
|
2079
2079
|
clearCurrentSimId()
|
|
@@ -2161,9 +2161,9 @@ export async function runCloseCommand(args: string[], opts: ControlCommandOption
|
|
|
2161
2161
|
rethrowIfExit(err)
|
|
2162
2162
|
console.error(` close failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2163
2163
|
await printBridgeFailureDiagnostics(bridge, {
|
|
2164
|
-
errorsCommand: `
|
|
2165
|
-
warningsCommand: `
|
|
2166
|
-
requestsCommand: `
|
|
2164
|
+
errorsCommand: `peach get errors 5${simHint}`,
|
|
2165
|
+
warningsCommand: `peach get warnings 5${simHint}`,
|
|
2166
|
+
requestsCommand: `peach get requests 5${simHint}`,
|
|
2167
2167
|
})
|
|
2168
2168
|
rnxExit(1)
|
|
2169
2169
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
//
|
|
1
|
+
// peach perf cpu — capture sampled CPU traces from the page and every worker.
|
|
2
2
|
//
|
|
3
3
|
// the JS Self-Profiler API (`new Profiler()`) cannot be constructed in a
|
|
4
|
-
// dedicated-worker scope, and
|
|
4
|
+
// dedicated-worker scope, and peach runs the guest app in the tenant worker,
|
|
5
5
|
// so the old in-worker approach was unsupported (F28). this implementation
|
|
6
6
|
// attaches Chromium's `Profiler` CDP domain to the page and worker targets —
|
|
7
7
|
// which returns the .cpuprofile shape directly for every execution context.
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
// target, `Target.setAutoAttach` to discover the worker child sessions, and
|
|
11
11
|
// route `Profiler.*` to each worker via its sessionId (flatten mode).
|
|
12
12
|
//
|
|
13
|
-
// the user's normal
|
|
13
|
+
// the user's normal peach tab runs in plain Chrome without a debug port.
|
|
14
14
|
// the easiest way to get a profilable sim is to open one with a CDP port —
|
|
15
15
|
// it stays driveable over the WS bridge, so you can scroll/navigate while
|
|
16
16
|
// profiling (the playwright driver passes --remote-debugging-port through):
|
|
17
17
|
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
18
|
+
// peach open <metro-port> --new --driver playwright --cdp-port 9222
|
|
19
|
+
// peach perf cpu --cdp-port 9222 --match <metro-port> --duration 5
|
|
20
20
|
//
|
|
21
21
|
// (a standalone CDP Chrome also works for profiling a static page, but it
|
|
22
22
|
// does NOT register on the bridge, so it can't be driven — see printNoCdp.)
|
|
@@ -430,13 +430,13 @@ export async function runCpuProfile(
|
|
|
430
430
|
Math.ceil((interactionBarrierTimeoutSeconds + duration + 5) * 1000),
|
|
431
431
|
)
|
|
432
432
|
const cdpPort = Number(
|
|
433
|
-
valueOf(args, '--cdp-port') ?? process.env.
|
|
433
|
+
valueOf(args, '--cdp-port') ?? process.env.PEACH_CDP_PORT ?? '9222',
|
|
434
434
|
)
|
|
435
435
|
// optional substring filter to pick the right page when several sims share
|
|
436
436
|
// the CDP browser (e.g. `--match 8089` or `--match /rn/8089`).
|
|
437
437
|
const match = valueOf(args, '--match') ?? '/rn/'
|
|
438
438
|
const outputArg = valueOf(args, '--output') ?? valueOf(args, '-o')
|
|
439
|
-
const outputPath = resolve(process.cwd(), outputArg ?? '/tmp/
|
|
439
|
+
const outputPath = resolve(process.cwd(), outputArg ?? '/tmp/peach.cpuprofile')
|
|
440
440
|
|
|
441
441
|
// locate the page target on the CDP endpoint. NOTE: Chrome's DevTools HTTP
|
|
442
442
|
// `/json` endpoints reject any Host header that isn't `localhost` (a
|
|
@@ -873,15 +873,15 @@ function printNoCdp(cdpPort: number) {
|
|
|
873
873
|
console.error(' so you can scroll/navigate while profiling):')
|
|
874
874
|
console.error('')
|
|
875
875
|
console.error(
|
|
876
|
-
`
|
|
876
|
+
` peach open <metro-port> --new --driver playwright --cdp-port ${cdpPort}`,
|
|
877
877
|
)
|
|
878
|
-
console.error(`
|
|
878
|
+
console.error(` peach perf cpu --cdp-port ${cdpPort} --match <metro-port>`)
|
|
879
879
|
console.error('')
|
|
880
880
|
console.error(' or launch a standalone CDP Chrome (not bridge-driveable):')
|
|
881
881
|
console.error('')
|
|
882
882
|
console.error(' "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \\')
|
|
883
883
|
console.error(
|
|
884
|
-
` --remote-debugging-port=${cdpPort} --user-data-dir=/tmp/
|
|
884
|
+
` --remote-debugging-port=${cdpPort} --user-data-dir=/tmp/peach-cdp \\`,
|
|
885
885
|
)
|
|
886
886
|
console.error(' --no-sandbox --disable-gpu-sandbox \\')
|
|
887
887
|
// never suggest swiftshader — software rendering pegs every core for the
|
|
@@ -890,7 +890,7 @@ function printNoCdp(cdpPort: number) {
|
|
|
890
890
|
console.error(' --use-gl=angle --use-angle=metal \\')
|
|
891
891
|
console.error(' "http://localhost:5173/rn/<metro-port>" &')
|
|
892
892
|
console.error('')
|
|
893
|
-
console.error(' override the port with --cdp-port or $
|
|
893
|
+
console.error(' override the port with --cdp-port or $PEACH_CDP_PORT.')
|
|
894
894
|
}
|
|
895
895
|
|
|
896
896
|
function shortUrl(url: string): string {
|