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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// generate + ad-hoc sign the
|
|
1
|
+
// generate + ad-hoc sign the Peach Daemon.app wrapper that the launchd
|
|
2
2
|
// agent points its ProgramArguments at.
|
|
3
3
|
//
|
|
4
4
|
// why this exists: macOS Background Task Management (the "X wants to run in
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
// pointed at bun directly the entry shows "software from Jarred Sumner"
|
|
8
8
|
// because that's bun's code-signing identity. wrapping the real invocation
|
|
9
9
|
// in an ad-hoc-signed `.app` lets BTM read CFBundleDisplayName from the
|
|
10
|
-
// bundle Info.plist instead, so the entry reads "
|
|
10
|
+
// bundle Info.plist instead, so the entry reads "Peach Daemon".
|
|
11
11
|
//
|
|
12
12
|
// the bundle is regenerated every time `daemonInstall` runs — it's tiny
|
|
13
13
|
// (a plist + a shell launcher) and the launcher embeds a snapshot of the
|
|
14
|
-
// invocation, so `
|
|
14
|
+
// invocation, so `peach upgrade` followed by `peach daemon install --force` is
|
|
15
15
|
// the documented upgrade path. there's no notarization
|
|
16
16
|
// or Developer ID signing here yet; ad-hoc + hardened runtime is enough to
|
|
17
17
|
// fix the attribution, and a real signed bundle (shipped via the runtime
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
import type { RnxSelfInvocation } from '../self-invocation'
|
|
29
29
|
|
|
30
30
|
const BUNDLE_IDENTIFIER = 'dev.sootsim.daemon'
|
|
31
|
-
const BUNDLE_DISPLAY_NAME = '
|
|
31
|
+
const BUNDLE_DISPLAY_NAME = 'Peach Daemon'
|
|
32
32
|
// kept in sync with package.json on release. exact value doesn't matter for
|
|
33
33
|
// BTM display — macOS only reads it for "About this app" surfaces — so we
|
|
34
34
|
// hardcode it rather than reach back into the package.json from a runtime
|
|
@@ -43,7 +43,7 @@ export interface DaemonAppPaths {
|
|
|
43
43
|
launcherPath: string
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
/** rebuild ~/.
|
|
46
|
+
/** rebuild ~/.peach/daemon-app/Peach Daemon.app/ for the given invocation
|
|
47
47
|
* and re-sign it ad-hoc. safe to call repeatedly; the directory is wiped
|
|
48
48
|
* before write so stale state from earlier installs can't leak through. */
|
|
49
49
|
export function ensureDaemonApp(
|
|
@@ -97,7 +97,7 @@ function renderInfoPlist(): string {
|
|
|
97
97
|
<dict>
|
|
98
98
|
<key>CFBundleDevelopmentRegion</key><string>en</string>
|
|
99
99
|
<key>CFBundleDisplayName</key><string>${BUNDLE_DISPLAY_NAME}</string>
|
|
100
|
-
<key>CFBundleExecutable</key><string>
|
|
100
|
+
<key>CFBundleExecutable</key><string>peach-daemon</string>
|
|
101
101
|
<key>CFBundleIdentifier</key><string>${BUNDLE_IDENTIFIER}</string>
|
|
102
102
|
<key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
|
|
103
103
|
<key>CFBundleName</key><string>${BUNDLE_DISPLAY_NAME}</string>
|
|
@@ -135,11 +135,11 @@ function renderLauncherScript(
|
|
|
135
135
|
const quoted = parts.map(shellSingleQuote).join(' ')
|
|
136
136
|
const stdout = shellSingleQuote(logDir.stdout)
|
|
137
137
|
const stderr = shellSingleQuote(logDir.stderr)
|
|
138
|
-
const
|
|
139
|
-
const environment =
|
|
138
|
+
const peachHome = process.env.PEACH_HOME
|
|
139
|
+
const environment = peachHome ? `export PEACH_HOME=${shellSingleQuote(peachHome)}\n` : ''
|
|
140
140
|
return `#!/bin/sh
|
|
141
|
-
#
|
|
142
|
-
# launchd spawns this; we exec the real
|
|
141
|
+
# peach daemon launcher generated by 'peach daemon install'.
|
|
142
|
+
# launchd spawns this; we exec the real peach invocation in place so
|
|
143
143
|
# launchd tracks the daemon process, not the shell wrapper.
|
|
144
144
|
#
|
|
145
145
|
# stdout: ${logDir.stdout}
|
package/cli/commands/daemon.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
//
|
|
1
|
+
// peach daemon — manage the persistent peach bridge daemon
|
|
2
2
|
//
|
|
3
|
-
// wraps `
|
|
3
|
+
// wraps `peach serve` so it starts automatically on login and restarts if
|
|
4
4
|
// it crashes. the agent points at the executable that ran `daemon install`,
|
|
5
|
-
// so `
|
|
6
|
-
// `
|
|
5
|
+
// so `peach upgrade` (which replaces that file in place) is picked up by
|
|
6
|
+
// `peach daemon restart` without touching the plist/unit file.
|
|
7
7
|
//
|
|
8
|
-
// `daemonInstall` is shared by `
|
|
9
|
-
// `
|
|
8
|
+
// `daemonInstall` is shared by `peach setup` and the explicit
|
|
9
|
+
// `peach daemon install` machine-bootstrap path.
|
|
10
10
|
//
|
|
11
11
|
// macOS: ~/Library/LaunchAgents/dev.sootsim.daemon.plist (launchd)
|
|
12
12
|
// linux: ~/.config/systemd/user/sootsim-daemon.service (systemd --user)
|
|
13
|
-
// win: not yet supported — use `
|
|
13
|
+
// win: not yet supported — use `peach serve` in a persistent shell.
|
|
14
14
|
//
|
|
15
15
|
// the macOS launchd Label is `dev.sootsim.daemon` (matching the .app
|
|
16
16
|
// CFBundleIdentifier).
|
|
@@ -148,10 +148,12 @@ ${programArgs}
|
|
|
148
148
|
function renderSystemdUnit(invocation: RnxSelfInvocation, port: number): string {
|
|
149
149
|
mkdirSync(LOG_DIR_LINUX, { recursive: true })
|
|
150
150
|
const exec = [invocation.executable, ...invocation.prefixArgs].map(shellQuote).join(' ')
|
|
151
|
-
const
|
|
152
|
-
const environment =
|
|
151
|
+
const peachHome = process.env.PEACH_HOME
|
|
152
|
+
const environment = peachHome
|
|
153
|
+
? `Environment=${shellQuote(`PEACH_HOME=${peachHome}`)}\n`
|
|
154
|
+
: ''
|
|
153
155
|
return `[Unit]
|
|
154
|
-
Description=
|
|
156
|
+
Description=peach bridge daemon
|
|
155
157
|
After=default.target
|
|
156
158
|
|
|
157
159
|
[Service]
|
|
@@ -230,7 +232,7 @@ export async function daemonInstall({ port, force }: { port: number; force: bool
|
|
|
230
232
|
const path = macPlistPath()
|
|
231
233
|
if (existsSync(path) && !force) {
|
|
232
234
|
console.log(` already installed at ${path}`)
|
|
233
|
-
console.log(` pass --force to overwrite, or use '
|
|
235
|
+
console.log(` pass --force to overwrite, or use 'peach daemon restart' to reload`)
|
|
234
236
|
return
|
|
235
237
|
}
|
|
236
238
|
mkdirSync(dirname(path), { recursive: true })
|
|
@@ -280,7 +282,7 @@ export async function daemonInstall({ port, force }: { port: number; force: bool
|
|
|
280
282
|
throw new Error(
|
|
281
283
|
`launchctl bootstrap failed: ${attempt.reason}` +
|
|
282
284
|
(first !== attempt.reason ? ` (initial: ${first})` : '') +
|
|
283
|
-
` — try \`
|
|
285
|
+
` — try \`peach daemon uninstall\` then retry, or reboot if launchd state is stuck.`,
|
|
284
286
|
)
|
|
285
287
|
}
|
|
286
288
|
}
|
|
@@ -321,7 +323,7 @@ export async function daemonInstall({ port, force }: { port: number; force: bool
|
|
|
321
323
|
const path = linuxUnitPath()
|
|
322
324
|
if (existsSync(path) && !force) {
|
|
323
325
|
console.log(` already installed at ${path}`)
|
|
324
|
-
console.log(` pass --force to overwrite, or use '
|
|
326
|
+
console.log(` pass --force to overwrite, or use 'peach daemon restart' to reload`)
|
|
325
327
|
return
|
|
326
328
|
}
|
|
327
329
|
mkdirSync(dirname(path), { recursive: true })
|
|
@@ -345,7 +347,7 @@ export async function daemonInstall({ port, force }: { port: number; force: bool
|
|
|
345
347
|
const msg = (enable.stderr || enable.stdout || '').trim()
|
|
346
348
|
throw new Error(
|
|
347
349
|
`systemctl enable --now failed${msg ? `: ${msg}` : ''}` +
|
|
348
|
-
` — try \`
|
|
350
|
+
` — try \`peach daemon uninstall\` then retry.`,
|
|
349
351
|
)
|
|
350
352
|
}
|
|
351
353
|
const isActive = spawnSync('systemctl', ['--user', 'is-active', SERVICE_NAME_LINUX], {
|
|
@@ -358,7 +360,7 @@ export async function daemonInstall({ port, force }: { port: number; force: bool
|
|
|
358
360
|
|
|
359
361
|
// user-scope systemd units are killed when the user's last session ends.
|
|
360
362
|
// on most distros the daemon then disappears at logout — the unit is
|
|
361
|
-
// "active" until you log off, then silently dead, and `
|
|
363
|
+
// "active" until you log off, then silently dead, and `peach` looks
|
|
362
364
|
// broken when you ssh back in. `loginctl enable-linger` keeps the user
|
|
363
365
|
// bus alive across sessions, which is what every other long-running
|
|
364
366
|
// user agent (gpg-agent, ssh-agent --systemd) also relies on.
|
|
@@ -393,7 +395,7 @@ export async function daemonInstall({ port, force }: { port: number; force: bool
|
|
|
393
395
|
}
|
|
394
396
|
|
|
395
397
|
console.error(` background daemon is not supported on ${process.platform}`)
|
|
396
|
-
console.error(` run '
|
|
398
|
+
console.error(` run 'peach serve' in a persistent shell instead`)
|
|
397
399
|
rnxExit(1)
|
|
398
400
|
}
|
|
399
401
|
|
|
@@ -436,7 +438,7 @@ export function teardownLegacyMacDaemonArtifacts(
|
|
|
436
438
|
|
|
437
439
|
/** stop + disable + remove the persistent launchd / systemd registration
|
|
438
440
|
* and its support files (mac .app bundle, log dir). does NOT touch user
|
|
439
|
-
* data under ~/.
|
|
441
|
+
* data under ~/.peach. idempotent: each entry is checked before remove. */
|
|
440
442
|
export function teardownDaemonService(): TeardownReport {
|
|
441
443
|
const removed: string[] = []
|
|
442
444
|
if (process.platform === 'darwin') {
|
|
@@ -495,11 +497,11 @@ export function teardownDaemonService(): TeardownReport {
|
|
|
495
497
|
return { removed, missing: true }
|
|
496
498
|
}
|
|
497
499
|
|
|
498
|
-
/** remove EVERYTHING under ~/.
|
|
500
|
+
/** remove EVERYTHING under ~/.peach: installed runtimes, electron support,
|
|
499
501
|
* device profiles and their app storage, live dev-bridge state, lockfile,
|
|
500
|
-
* and config. only `
|
|
502
|
+
* and config. only `peach daemon uninstall --purge` calls this, because a
|
|
501
503
|
* user who asked to remove a background service did not ask to lose the
|
|
502
|
-
* simulators they have been working in. idempotent. honors $
|
|
504
|
+
* simulators they have been working in. idempotent. honors $PEACH_HOME. */
|
|
503
505
|
export function teardownRnxHome(): TeardownReport {
|
|
504
506
|
const removed: string[] = []
|
|
505
507
|
const home = rnxHomeDir()
|
|
@@ -557,8 +559,8 @@ function printBridgeLiveness() {
|
|
|
557
559
|
}
|
|
558
560
|
console.log(
|
|
559
561
|
lock
|
|
560
|
-
? ' bridge: NOT responding — stale lockfile; check `
|
|
561
|
-
: ' bridge: NOT responding — no lockfile; check `
|
|
562
|
+
? ' bridge: NOT responding — stale lockfile; check `peach daemon logs`'
|
|
563
|
+
: ' bridge: NOT responding — no lockfile; check `peach daemon logs`',
|
|
562
564
|
)
|
|
563
565
|
}
|
|
564
566
|
|
|
@@ -609,7 +611,7 @@ async function daemonRestart() {
|
|
|
609
611
|
if (process.platform === 'darwin') {
|
|
610
612
|
const path = macPlistPath()
|
|
611
613
|
if (!existsSync(path)) {
|
|
612
|
-
console.error(' daemon not registered. run `
|
|
614
|
+
console.error(' daemon not registered. run `peach daemon install`')
|
|
613
615
|
rnxExit(1)
|
|
614
616
|
}
|
|
615
617
|
const res = spawnSync(
|
|
@@ -627,7 +629,7 @@ async function daemonRestart() {
|
|
|
627
629
|
if (process.platform === 'linux') {
|
|
628
630
|
const path = linuxUnitPath()
|
|
629
631
|
if (!existsSync(path)) {
|
|
630
|
-
console.error(' daemon not registered. run `
|
|
632
|
+
console.error(' daemon not registered. run `peach daemon install`')
|
|
631
633
|
rnxExit(1)
|
|
632
634
|
}
|
|
633
635
|
const res = spawnSync('systemctl', ['--user', 'restart', SERVICE_NAME_LINUX], {
|
|
@@ -649,7 +651,7 @@ async function daemonStart() {
|
|
|
649
651
|
if (process.platform === 'darwin') {
|
|
650
652
|
const path = macPlistPath()
|
|
651
653
|
if (!existsSync(path)) {
|
|
652
|
-
console.error(' daemon not registered. run `
|
|
654
|
+
console.error(' daemon not registered. run `peach daemon install`')
|
|
653
655
|
rnxExit(1)
|
|
654
656
|
}
|
|
655
657
|
const res = spawnSync(
|
|
@@ -667,7 +669,7 @@ async function daemonStart() {
|
|
|
667
669
|
if (process.platform === 'linux') {
|
|
668
670
|
const path = linuxUnitPath()
|
|
669
671
|
if (!existsSync(path)) {
|
|
670
|
-
console.error(' daemon not registered. run `
|
|
672
|
+
console.error(' daemon not registered. run `peach daemon install`')
|
|
671
673
|
rnxExit(1)
|
|
672
674
|
}
|
|
673
675
|
const res = spawnSync('systemctl', ['--user', 'start', SERVICE_NAME_LINUX], {
|
|
@@ -741,7 +743,7 @@ async function daemonLogs(rest: string[]) {
|
|
|
741
743
|
const err = resolve(LOG_DIR, 'bridge.err.log')
|
|
742
744
|
const present = [out, err].filter((p) => existsSync(p))
|
|
743
745
|
if (present.length === 0) {
|
|
744
|
-
console.error(` no daemon logs yet at ${LOG_DIR}. run \`
|
|
746
|
+
console.error(` no daemon logs yet at ${LOG_DIR}. run \`peach daemon install\``)
|
|
745
747
|
rnxExit(1)
|
|
746
748
|
}
|
|
747
749
|
const args = follow
|
package/cli/commands/debug.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
//
|
|
1
|
+
// peach debug — drive __sootsimDebug.* from the terminal
|
|
2
2
|
//
|
|
3
|
-
// connects to a running
|
|
3
|
+
// connects to a running peach instance via the WebSocket bridge and forwards
|
|
4
4
|
// debug channel management, tree snapshots, and on-demand inspectors without
|
|
5
5
|
// requiring a browser console.
|
|
6
6
|
//
|
|
7
7
|
// usage examples:
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
8
|
+
// peach debug enable sheets,portals,onlayout
|
|
9
|
+
// peach debug status
|
|
10
|
+
// peach debug snapshot before
|
|
11
11
|
// # ... tap something in the UI ...
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
12
|
+
// peach debug snapshot after
|
|
13
|
+
// peach debug diff before after
|
|
14
|
+
// peach debug find sheets
|
|
15
|
+
// peach debug find portals
|
|
16
|
+
// peach debug recent portals
|
|
17
|
+
// peach debug flags
|
|
18
18
|
//
|
|
19
19
|
// all commands print JSON by default so they pipe cleanly into jq.
|
|
20
20
|
|
|
@@ -59,10 +59,10 @@ const SHELL_TRACE_HOOK_GLOBAL = '__sootsimDebugAnimation'
|
|
|
59
59
|
|
|
60
60
|
function printHelp() {
|
|
61
61
|
console.log(`
|
|
62
|
-
|
|
62
|
+
peach debug — drive __sootsimDebug from the terminal
|
|
63
63
|
|
|
64
64
|
usage:
|
|
65
|
-
|
|
65
|
+
peach debug <subcommand> [args]
|
|
66
66
|
|
|
67
67
|
record, recent, and clear-events target the tenant; --host selects the page buffer.
|
|
68
68
|
enable/disable/toggle also update the tenant unless --host is passed.
|
|
@@ -83,7 +83,7 @@ subcommands:
|
|
|
83
83
|
scroll <id>, scroll-hit <x> <y>,
|
|
84
84
|
hit <x> <y>, gesture <x> <y>
|
|
85
85
|
|
|
86
|
-
js <code> execute javascript in the running app.
|
|
86
|
+
js <code> execute javascript in the running app. peach
|
|
87
87
|
is the canonical state object — reach into its
|
|
88
88
|
bridges / state / chrome / debug slots directly.
|
|
89
89
|
e.g. SootSim.bridges.test.findByText("Sign in")
|
|
@@ -128,16 +128,16 @@ options:
|
|
|
128
128
|
--json compact JSON output for pipelines
|
|
129
129
|
|
|
130
130
|
examples:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
131
|
+
peach debug enable sheets,portals
|
|
132
|
+
peach debug snapshot before
|
|
133
|
+
peach debug snapshot after
|
|
134
|
+
peach debug diff before after
|
|
135
|
+
peach debug image-audit per-image source pixels per device pixel
|
|
136
|
+
peach debug find sheets | jq '.[] | select(.translateY != null)'
|
|
137
|
+
peach debug trace shell on 240
|
|
138
|
+
peach debug trace shell 120
|
|
139
|
+
peach debug trace anim on 3 240
|
|
140
|
+
peach debug trace anim 3 60
|
|
141
141
|
`)
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -325,7 +325,7 @@ function readNumberFlag(args: string[], flag: string): number | null {
|
|
|
325
325
|
|
|
326
326
|
// enable the audit, force one real frame, then read and stop. the screenshot
|
|
327
327
|
// request is what forces the frame: it runs the same publish-and-paint pass
|
|
328
|
-
// `
|
|
328
|
+
// `peach screenshot` does, so the rows describe pixels that were actually
|
|
329
329
|
// produced rather than a tree walk's guess about them.
|
|
330
330
|
async function runImageAudit(bridge: WsBridge): Promise<ImageAuditReportView> {
|
|
331
331
|
await call(
|
|
@@ -442,7 +442,7 @@ export async function runDebug(args: string[], opts: DebugOptions) {
|
|
|
442
442
|
const channels = parseChannelList(rest[0])
|
|
443
443
|
if (channels.length === 0) {
|
|
444
444
|
console.error(
|
|
445
|
-
` usage:
|
|
445
|
+
` usage: peach debug enable <channel[,channel,...]>\n known: ${KNOWN_CHANNELS.join(', ')}`,
|
|
446
446
|
)
|
|
447
447
|
rnxExit(1)
|
|
448
448
|
}
|
|
@@ -471,7 +471,7 @@ export async function runDebug(args: string[], opts: DebugOptions) {
|
|
|
471
471
|
case 'toggle': {
|
|
472
472
|
const channel = rest[0]
|
|
473
473
|
if (!channel) {
|
|
474
|
-
console.error(' usage:
|
|
474
|
+
console.error(' usage: peach debug toggle <channel>')
|
|
475
475
|
rnxExit(1)
|
|
476
476
|
}
|
|
477
477
|
const result = await callDebugBridge(
|
|
@@ -538,7 +538,7 @@ export async function runDebug(args: string[], opts: DebugOptions) {
|
|
|
538
538
|
const a = rest[0]
|
|
539
539
|
const b = rest[1]
|
|
540
540
|
if (!a || !b) {
|
|
541
|
-
console.error(' usage:
|
|
541
|
+
console.error(' usage: peach debug diff <labelA> <labelB>')
|
|
542
542
|
rnxExit(1)
|
|
543
543
|
}
|
|
544
544
|
// Maps don't serialize — project added/removed/changed arrays only
|
|
@@ -600,7 +600,7 @@ export async function runDebug(args: string[], opts: DebugOptions) {
|
|
|
600
600
|
const result = await inspectDebugFind(bridge, target)
|
|
601
601
|
console.log(fmt(result, pretty))
|
|
602
602
|
} else {
|
|
603
|
-
console.error(' usage:
|
|
603
|
+
console.error(' usage: peach debug find <sheets|portals|boundaries>')
|
|
604
604
|
rnxExit(1)
|
|
605
605
|
}
|
|
606
606
|
break
|
|
@@ -617,7 +617,7 @@ export async function runDebug(args: string[], opts: DebugOptions) {
|
|
|
617
617
|
const sub = rest[1]
|
|
618
618
|
if (!sub || sub === '--help' || sub === '-h') {
|
|
619
619
|
console.error(
|
|
620
|
-
' usage:
|
|
620
|
+
' usage: peach debug trace anim <on|off|status|clear|<id>> [id|limit]',
|
|
621
621
|
)
|
|
622
622
|
rnxExit(1)
|
|
623
623
|
}
|
|
@@ -664,7 +664,7 @@ export async function runDebug(args: string[], opts: DebugOptions) {
|
|
|
664
664
|
break
|
|
665
665
|
}
|
|
666
666
|
if (traceTarget !== 'shell') {
|
|
667
|
-
console.error(' usage:
|
|
667
|
+
console.error(' usage: peach debug trace <shell|anim> [args]')
|
|
668
668
|
rnxExit(1)
|
|
669
669
|
}
|
|
670
670
|
if (!traceCommand || /^[0-9]+$/.test(traceCommand)) {
|
|
@@ -695,7 +695,7 @@ export async function runDebug(args: string[], opts: DebugOptions) {
|
|
|
695
695
|
break
|
|
696
696
|
}
|
|
697
697
|
console.error(
|
|
698
|
-
' usage:
|
|
698
|
+
' usage: peach debug trace shell [on [limit]|off|status|clear|recentLimit]',
|
|
699
699
|
)
|
|
700
700
|
rnxExit(1)
|
|
701
701
|
}
|
|
@@ -740,9 +740,9 @@ export async function runDebug(args: string[], opts: DebugOptions) {
|
|
|
740
740
|
rethrowIfExit(err)
|
|
741
741
|
console.error(` debug failed: ${err.message}`)
|
|
742
742
|
await printBridgeFailureDiagnostics(bridge, {
|
|
743
|
-
errorsCommand: `
|
|
744
|
-
warningsCommand: `
|
|
745
|
-
requestsCommand: `
|
|
743
|
+
errorsCommand: `peach get errors 5${simHint}`,
|
|
744
|
+
warningsCommand: `peach get warnings 5${simHint}`,
|
|
745
|
+
requestsCommand: `peach get requests 5${simHint}`,
|
|
746
746
|
})
|
|
747
747
|
rnxExit(1)
|
|
748
748
|
} finally {
|
package/cli/commands/desktop.ts
CHANGED
|
@@ -8,11 +8,11 @@ interface DesktopOptions {
|
|
|
8
8
|
|
|
9
9
|
function printHelp(): void {
|
|
10
10
|
console.log(`
|
|
11
|
-
|
|
11
|
+
peach desktop — launch or install the desktop companion
|
|
12
12
|
|
|
13
13
|
usage:
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
peach desktop [options]
|
|
15
|
+
peach desktop install [options]
|
|
16
16
|
|
|
17
17
|
options:
|
|
18
18
|
--port <number> connect to a running dev server on this port
|
package/cli/commands/detox.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
//
|
|
1
|
+
// peach detox — run existing detox suites against peach
|
|
2
2
|
//
|
|
3
|
-
// use the customer's jest config unchanged. the
|
|
3
|
+
// use the customer's jest config unchanged. the peach preset maps the detox
|
|
4
4
|
// driver and owns browser cleanup; jest owns setup, selection and deadlines.
|
|
5
5
|
|
|
6
6
|
import { spawn } from 'child_process'
|
|
@@ -30,18 +30,18 @@ interface RunDetoxOpts {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const HELP = `
|
|
33
|
-
|
|
33
|
+
peach detox — run detox-style tests against a peach shell
|
|
34
34
|
|
|
35
35
|
usage:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
peach detox [testFiles...] run all tests under e2e/, test/e2e/, or detox/
|
|
37
|
+
peach detox --config <path> use a specific jest config
|
|
38
|
+
peach detox init scaffold rnx-detox.config.cjs + sample test
|
|
39
|
+
peach detox --watch jest watch mode
|
|
40
|
+
peach detox --headed keep the peach shell window visible
|
|
41
|
+
peach detox -t <pattern> pass a jest --testNamePattern
|
|
42
|
+
peach detox --no-launch skip auto-launching a peach shell
|
|
43
|
+
|
|
44
|
+
peach detox auto-launches a peach shell if none is running on the
|
|
45
45
|
expected port. disable with --no-launch if you're managing the shell yourself.
|
|
46
46
|
`
|
|
47
47
|
|
|
@@ -71,11 +71,11 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
71
71
|
Number(getFlag('--port')) || opts.port || Number(process.env.SOOTSIM_PORT) || 5173
|
|
72
72
|
if (getFlag('--sim')) {
|
|
73
73
|
throw new Error(
|
|
74
|
-
'Detox owns its browser context; pin its shell with
|
|
74
|
+
'Detox owns its browser context; pin its shell with PEACH_URL and --device instead of --sim',
|
|
75
75
|
)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
// consume only
|
|
78
|
+
// consume only peach options. jest must receive its complete argv, including
|
|
79
79
|
// selection, timeouts, reporters, coverage, and options added by future jest versions.
|
|
80
80
|
const jestArgs: string[] = ['jest']
|
|
81
81
|
for (let index = 0; index < args.length; index++) {
|
|
@@ -111,11 +111,11 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
111
111
|
if (pkg.jest) localConfig = 'package.json'
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
// test dir discovery. external test suites set
|
|
114
|
+
// test dir discovery. external test suites set PEACH_TEST_DIR; otherwise
|
|
115
115
|
// we auto-detect the conventional locations.
|
|
116
116
|
const detoxDirs = ['e2e', 'test/e2e', 'detox']
|
|
117
|
-
let testDir: string | null = process.env.
|
|
118
|
-
? resolve(process.cwd(), process.env.
|
|
117
|
+
let testDir: string | null = process.env.PEACH_TEST_DIR
|
|
118
|
+
? resolve(process.cwd(), process.env.PEACH_TEST_DIR)
|
|
119
119
|
: null
|
|
120
120
|
if (!testDir && !localConfig && args[0] && !args[0].startsWith('-')) {
|
|
121
121
|
const target = resolve(args[0])
|
|
@@ -134,15 +134,15 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
134
134
|
if (!testDir && !localConfig) {
|
|
135
135
|
console.error(
|
|
136
136
|
` error: no detox tests found. expected one of: ${detoxDirs.join(', ')}\n` +
|
|
137
|
-
` run '
|
|
137
|
+
` run 'peach detox init' to scaffold a starter suite.`,
|
|
138
138
|
)
|
|
139
139
|
rnxExit(1)
|
|
140
140
|
}
|
|
141
141
|
testDir ??= process.cwd()
|
|
142
142
|
|
|
143
|
-
// auto-launch: make sure
|
|
143
|
+
// auto-launch: make sure a peach shell is reachable before running jest.
|
|
144
144
|
// Reuse the Maestro discovery path: scan local dev servers.
|
|
145
|
-
// and `
|
|
145
|
+
// and `peach open` the first match.
|
|
146
146
|
if (!noLaunch) {
|
|
147
147
|
await ensureShellRunning(port)
|
|
148
148
|
}
|
|
@@ -204,7 +204,7 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
204
204
|
const auth = resolveCliAuth()
|
|
205
205
|
const wantsRegistration =
|
|
206
206
|
!watch &&
|
|
207
|
-
(process.env.
|
|
207
|
+
(process.env.PEACH_REQUIRE_REGISTRATION === 'true' ||
|
|
208
208
|
shouldRegisterRun({ uploadedShare: false, auth }))
|
|
209
209
|
let resultsFile: string | null =
|
|
210
210
|
getFlag('--outputFile') || process.env.SOOTSIM_JEST_OUTPUT_FILE || null
|
|
@@ -217,12 +217,12 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
217
217
|
jestArgs.push('--outputFile', resultsFile)
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
console.log(`
|
|
220
|
+
console.log(` peach detox`)
|
|
221
221
|
console.log(` test dir: ${testDir}`)
|
|
222
222
|
console.log(` port: ${port}${headed ? ' (headed)' : ''}`)
|
|
223
223
|
if (localConfig) console.log(` config: ${localConfig}`)
|
|
224
224
|
|
|
225
|
-
const shellBaseUrl = process.env.
|
|
225
|
+
const shellBaseUrl = process.env.PEACH_URL || `http://localhost:${port}`
|
|
226
226
|
const app = getFlag('--app')
|
|
227
227
|
const shellUrl = new URL(app ? await buildShellUrl(app, shellBaseUrl) : shellBaseUrl)
|
|
228
228
|
const device = getFlag('--device')
|
|
@@ -253,16 +253,16 @@ export async function runDetox(args: string[], opts: RunDetoxOpts = {}): Promise
|
|
|
253
253
|
stdio: 'inherit',
|
|
254
254
|
env: {
|
|
255
255
|
...process.env,
|
|
256
|
-
|
|
256
|
+
PEACH_TEST_DIR: testDir,
|
|
257
257
|
SOOTSIM_PORT: String(port),
|
|
258
258
|
SOOTSIM_HEADED: headed ? '1' : '',
|
|
259
|
-
|
|
259
|
+
PEACH_URL: shellUrl.toString(),
|
|
260
260
|
},
|
|
261
261
|
})
|
|
262
262
|
|
|
263
263
|
// jest owns the browsers, and it only reaps them from its own signal and exit
|
|
264
264
|
// handlers. this process is the one a caller has a pid for, so a SIGTERM aimed
|
|
265
|
-
// at `
|
|
265
|
+
// at `peach detox` lands here and jest never hears about it: it is a
|
|
266
266
|
// grandchild (this -> npx -> jest), so it keeps running with no parent and
|
|
267
267
|
// every chrome it launched is orphaned for good. forward the signal instead of
|
|
268
268
|
// exiting out from under it, and let its exit code end this process.
|
|
@@ -414,7 +414,7 @@ async function ensureShellRunning(port: number): Promise<void> {
|
|
|
414
414
|
if (shellUrl) return
|
|
415
415
|
|
|
416
416
|
console.warn(
|
|
417
|
-
` warn: no
|
|
417
|
+
` warn: no peach shell reachable on :${port}. start your app, then run \`peach open <app-port>\`\n` +
|
|
418
418
|
` or pass --no-launch if you're managing the shell yourself.`,
|
|
419
419
|
)
|
|
420
420
|
}
|
|
@@ -427,9 +427,9 @@ function initDetoxProject(): void {
|
|
|
427
427
|
} else {
|
|
428
428
|
writeFileSync(
|
|
429
429
|
configPath,
|
|
430
|
-
`//
|
|
431
|
-
// extends the
|
|
432
|
-
// to the
|
|
430
|
+
`// peach detox jest config.
|
|
431
|
+
// extends the peach preset, which rewrites \`import ... from 'detox'\`
|
|
432
|
+
// to the peach driver and keeps your jest transforms intact.
|
|
433
433
|
|
|
434
434
|
/** @type {import('@jest/types').Config.InitialOptions} */
|
|
435
435
|
module.exports = {
|
|
@@ -450,7 +450,7 @@ module.exports = {
|
|
|
450
450
|
mkdirSync(e2eDir, { recursive: true })
|
|
451
451
|
writeFileSync(
|
|
452
452
|
samplePath,
|
|
453
|
-
`// sample
|
|
453
|
+
`// sample peach detox test. resolve \`detox\` via the jest preset in
|
|
454
454
|
// rnx-detox.config.cjs — no extra imports required.
|
|
455
455
|
|
|
456
456
|
import { by, device, element, expect, waitFor } from 'detox'
|
|
@@ -475,7 +475,7 @@ describe('example', () => {
|
|
|
475
475
|
}
|
|
476
476
|
|
|
477
477
|
console.log(`\n next:`)
|
|
478
|
-
console.log(`
|
|
479
|
-
console.log(`
|
|
478
|
+
console.log(` peach detox # run the sample`)
|
|
479
|
+
console.log(` peach detox --watch`)
|
|
480
480
|
rnxExit(0)
|
|
481
481
|
}
|
package/cli/commands/device.ts
CHANGED
|
@@ -26,18 +26,18 @@ type CurrentDeviceSnapshot = {
|
|
|
26
26
|
|
|
27
27
|
function usage() {
|
|
28
28
|
console.log(`
|
|
29
|
-
|
|
29
|
+
peach device — inspect or change the live device preset for a sim
|
|
30
30
|
|
|
31
31
|
usage:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
peach device
|
|
33
|
+
peach device get
|
|
34
|
+
peach device set <model> [--sim <id>]
|
|
35
|
+
peach device list
|
|
36
36
|
|
|
37
37
|
examples:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
peach device
|
|
39
|
+
peach device set iphone-16
|
|
40
|
+
peach device set iphone-16-pro-max --sim a7
|
|
41
41
|
`)
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -106,7 +106,7 @@ export async function runDeviceCommand(args: string[], opts: DeviceCommandOption
|
|
|
106
106
|
}
|
|
107
107
|
if (!(model in devices)) {
|
|
108
108
|
console.error(` error: unknown device "${model}"`)
|
|
109
|
-
console.error(' run `
|
|
109
|
+
console.error(' run `peach device list` to see valid models')
|
|
110
110
|
rnxExit(1)
|
|
111
111
|
}
|
|
112
112
|
const parsed = parseBridgeCliArgs(args, { port: opts.port })
|