rnxsim 0.1.369 → 0.1.371
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/cli/auth.ts +2 -1
- package/cli/bin.ts +283 -273
- package/cli/commands/app-fonts.ts +3 -2
- package/cli/commands/assert.ts +8 -7
- package/cli/commands/auth.ts +2 -1
- package/cli/commands/box/checkout-plane.ts +109 -29
- package/cli/commands/box/cloud-box.ts +294 -0
- package/cli/commands/box/plane-share.ts +208 -0
- package/cli/commands/box/rnx-command.ts +2 -2
- package/cli/commands/box/search-command.ts +157 -0
- package/cli/commands/box.ts +269 -13
- package/cli/commands/config.ts +2 -1
- package/cli/commands/control.ts +43 -34
- package/cli/commands/daemon.ts +22 -21
- package/cli/commands/debug.ts +17 -15
- package/cli/commands/detox.ts +5 -4
- package/cli/commands/device.ts +6 -5
- package/cli/commands/electron.ts +6 -5
- package/cli/commands/flow.ts +12 -11
- package/cli/commands/inspect/find.ts +2 -1
- package/cli/commands/inspect/keyboard.ts +2 -1
- package/cli/commands/inspect/sleep.ts +2 -1
- package/cli/commands/inspect/wait-event.ts +4 -3
- package/cli/commands/inspect/wait-idle.ts +2 -1
- package/cli/commands/inspect/wait-ready.ts +3 -2
- package/cli/commands/inspect/wait-selector.ts +4 -3
- package/cli/commands/inspect/wait.ts +2 -1
- package/cli/commands/inspect.ts +82 -80
- package/cli/commands/install-desktop.ts +5 -4
- package/cli/commands/login.ts +3 -2
- package/cli/commands/mode.ts +2 -1
- package/cli/commands/record.ts +38 -37
- package/cli/commands/runtime.ts +9 -8
- package/cli/commands/screenshot-layers.ts +4 -3
- package/cli/commands/screenshot-mode.ts +2 -1
- package/cli/commands/screenshot.ts +8 -7
- package/cli/commands/screenshots-capture.ts +2 -1
- package/cli/commands/serve.ts +5 -4
- package/cli/commands/shell-boolean-mode.ts +2 -1
- package/cli/commands/skills.ts +11 -10
- package/cli/commands/telemetry.ts +2 -1
- package/cli/commands/three-mode.ts +2 -1
- package/cli/commands/timeline.ts +4 -3
- package/cli/commands/upload.ts +14 -13
- package/cli/commands/what-happened.ts +2 -1
- package/cli/outbound-endpoints.ts +37 -1
- package/cli/recording-access.ts +6 -5
- package/cli/run-rnx.ts +140 -0
- package/dist-cli/bin.js +6 -6
- package/dist-cli/chunks/{agent-OUUFWDLF.js → agent-W2D2V7WF.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-7BE7DR4R.js → agent-wrapper-GVNM2Z4S.js} +2 -2
- package/dist-cli/chunks/app-fonts-J6R4TTXD.js +4 -0
- package/dist-cli/chunks/{assert-I3CMRNS7.js → assert-YN6U4ZTB.js} +11 -11
- package/dist-cli/chunks/auth-XGYZAK2P.js +12 -0
- package/dist-cli/chunks/{beta-BHSKY4K4.js → beta-WRDMHHWJ.js} +2 -2
- package/dist-cli/chunks/{box-3CVWXVTT.js → box-ML5TFUQW.js} +617 -596
- package/dist-cli/chunks/{camera-MNK73TYV.js → camera-OBEI4FZA.js} +2 -2
- package/dist-cli/chunks/{chunk-JXARZBWS.js → chunk-2FIT64GR.js} +1 -1
- package/dist-cli/chunks/{chunk-RYGQ6LZN.js → chunk-2LH4DYPM.js} +2 -2
- package/dist-cli/chunks/chunk-2QW3JH6T.js +99 -0
- package/dist-cli/chunks/{chunk-2APXX4RN.js → chunk-2W4YBCPX.js} +2 -2
- package/dist-cli/chunks/{chunk-X3PEDNLV.js → chunk-42YOIDLR.js} +2 -2
- package/dist-cli/chunks/{chunk-6BMM6OCW.js → chunk-47MSXLWK.js} +2 -2
- package/dist-cli/chunks/chunk-4OLUFHEZ.js +74 -0
- package/dist-cli/chunks/{chunk-WUR4FXWT.js → chunk-5PPVVGLH.js} +25 -25
- package/dist-cli/chunks/{chunk-AKJYEX54.js → chunk-6AZKZ5AJ.js} +2 -2
- package/dist-cli/chunks/{chunk-2PVVIPQS.js → chunk-6FPXLSBJ.js} +1 -1
- package/dist-cli/chunks/{chunk-CCI3DZKE.js → chunk-763GBI7G.js} +2 -2
- package/dist-cli/chunks/{chunk-CZ2HCNRK.js → chunk-7ASCDKA3.js} +1 -1
- package/dist-cli/chunks/{chunk-ZNNE5MAV.js → chunk-7Q4WHJDH.js} +1 -1
- package/dist-cli/chunks/{chunk-IYPMULI7.js → chunk-7TWD4P6J.js} +2 -2
- package/dist-cli/chunks/{chunk-45X3D6CI.js → chunk-7YB46LGI.js} +1 -1
- package/dist-cli/chunks/{chunk-3454VMHD.js → chunk-ADJSGO55.js} +2 -2
- package/dist-cli/chunks/{chunk-F4OTMARL.js → chunk-AGZZWLEL.js} +2 -2
- package/dist-cli/chunks/{chunk-UDXQCEJU.js → chunk-AVIH3TAZ.js} +11 -11
- package/dist-cli/chunks/{chunk-DCPDMF27.js → chunk-B26ZJUQA.js} +2 -2
- package/dist-cli/chunks/{chunk-D6MGELKZ.js → chunk-BNS5LNEU.js} +1 -1
- package/dist-cli/chunks/{chunk-FHRG772G.js → chunk-CJQXP4XM.js} +1 -1
- package/dist-cli/chunks/{chunk-OROUEG22.js → chunk-DP6RGTNN.js} +2 -2
- package/dist-cli/chunks/{chunk-V7I4CTAX.js → chunk-DYFOLOYL.js} +2 -2
- package/dist-cli/chunks/chunk-ERBGQEJ5.js +4 -0
- package/dist-cli/chunks/{chunk-H2GB2PLY.js → chunk-ESWSXF55.js} +2 -2
- package/dist-cli/chunks/{chunk-FGGESBWD.js → chunk-FLNGSIBL.js} +2 -2
- package/dist-cli/chunks/{chunk-4SCVGBOX.js → chunk-GQGS7TRK.js} +1 -1
- package/dist-cli/chunks/{chunk-WGE2P25F.js → chunk-H4DU457S.js} +1 -1
- package/dist-cli/chunks/{chunk-TBK5UXW2.js → chunk-HFY35VAG.js} +2 -2
- package/dist-cli/chunks/{chunk-JLOOLA47.js → chunk-HV3WOWQM.js} +1 -1
- package/dist-cli/chunks/{chunk-RV76U3EI.js → chunk-HZ365EMS.js} +10 -10
- package/dist-cli/chunks/chunk-IPNDXGVA.js +11 -0
- package/dist-cli/chunks/{chunk-TAC4CEIX.js → chunk-IX7Z6ZVJ.js} +2 -2
- package/dist-cli/chunks/{chunk-UQJY6O72.js → chunk-IYU2EOIU.js} +1 -1
- package/dist-cli/chunks/chunk-J6A5W2DI.js +30 -0
- package/dist-cli/chunks/chunk-JKRMBNPO.js +28 -0
- package/dist-cli/chunks/{chunk-VGJLCFSC.js → chunk-JUTLQ3GX.js} +1 -1
- package/dist-cli/chunks/{chunk-5QFJFASW.js → chunk-KHGXEU4L.js} +1 -1
- package/dist-cli/chunks/{chunk-A56GQVNF.js → chunk-KL34TRNW.js} +1 -1
- package/dist-cli/chunks/chunk-LFANRLWK.js +7 -0
- package/dist-cli/chunks/{chunk-NPVEBXU3.js → chunk-MOIQZVRU.js} +2 -2
- package/dist-cli/chunks/chunk-MW4E5EED.js +497 -0
- package/dist-cli/chunks/{chunk-AJJCYLBC.js → chunk-N3LAMTGS.js} +2 -2
- package/dist-cli/chunks/{chunk-QE5MC4V3.js → chunk-NQ4CI2VI.js} +1 -1
- package/dist-cli/chunks/{chunk-6F3WOWFC.js → chunk-OF3U4SZO.js} +2 -2
- package/dist-cli/chunks/{chunk-DKSZZABN.js → chunk-OF5OBB67.js} +1 -1
- package/dist-cli/chunks/{chunk-MXWME7ET.js → chunk-OQPZOEEP.js} +1 -1
- package/dist-cli/chunks/{chunk-SSBZA5VZ.js → chunk-OWDJQWJ7.js} +1 -1
- package/dist-cli/chunks/{chunk-YLNSLMQM.js → chunk-PTWMX4AA.js} +2 -2
- package/dist-cli/chunks/{chunk-TTUZ5MCK.js → chunk-RYJTPZCX.js} +1 -1
- package/dist-cli/chunks/{chunk-A72MIIJI.js → chunk-S3AH2O67.js} +1 -1
- package/dist-cli/chunks/{chunk-ZW6JXEVX.js → chunk-SH2HXHR2.js} +2 -2
- package/dist-cli/chunks/{chunk-EKAEEWC2.js → chunk-SHLJYXIN.js} +1 -1
- package/dist-cli/chunks/{chunk-4G4VCT2M.js → chunk-TL22EBF5.js} +2 -2
- package/dist-cli/chunks/{chunk-LU3AHE6T.js → chunk-TWNLRDIH.js} +2 -2
- package/dist-cli/chunks/{chunk-B5NW4TT3.js → chunk-UHYAY3RU.js} +2 -2
- package/dist-cli/chunks/{chunk-N5KDDGV6.js → chunk-UPMR6Y5P.js} +2 -2
- package/dist-cli/chunks/chunk-VEFSHPSU.js +24 -0
- package/dist-cli/chunks/{chunk-YPBDPVLR.js → chunk-WCB5XPCH.js} +2 -2
- package/dist-cli/chunks/{chunk-USHV3QFZ.js → chunk-WHZE63R2.js} +2 -2
- package/dist-cli/chunks/{chunk-ZEF57HQO.js → chunk-WLRIPAGJ.js} +1 -1
- package/dist-cli/chunks/{chunk-FBMNYSSW.js → chunk-Y7SKGRAV.js} +1 -1
- package/dist-cli/chunks/chunk-Z6CQDU74.js +4 -0
- package/dist-cli/chunks/{cleanup-H4XFRMZV.js → cleanup-PTJBMC34.js} +2 -2
- package/dist-cli/chunks/cli-version-6ZBD6GKP.js +4 -0
- package/dist-cli/chunks/{compat-NKGYERLN.js → compat-FJ2HWRAG.js} +2 -2
- package/dist-cli/chunks/config-PYGH7JPN.js +13 -0
- package/dist-cli/chunks/{connect-WWWAVVCF.js → connect-AJV4PZA4.js} +2 -2
- package/dist-cli/chunks/control-667PAYX4.js +4 -0
- package/dist-cli/chunks/daemon-EIBOHZYQ.js +4 -0
- package/dist-cli/chunks/{debug-RO6GLDIM.js → debug-XF2XUDHN.js} +20 -20
- package/dist-cli/chunks/desktop-HEZYNFYW.js +31 -0
- package/dist-cli/chunks/detox-JCFZJRNE.js +52 -0
- package/dist-cli/chunks/device-KGUVZHFW.js +18 -0
- package/dist-cli/chunks/{diagnose-HDRTUPDO.js → diagnose-KVJQ3TOI.js} +2 -2
- package/dist-cli/chunks/{disk-cleanup-QP2NYN43.js → disk-cleanup-7HQXJFUX.js} +2 -2
- package/dist-cli/chunks/{do-chain-6LP6SV5J.js → do-chain-3FYJNA2N.js} +2 -2
- package/dist-cli/chunks/drivers-5S6M5TJB.js +4 -0
- package/dist-cli/chunks/{env-4XD5D2G6.js → env-BX6S2SOM.js} +2 -2
- package/dist-cli/chunks/{film-I5SIXLS5.js → film-2WZUVYFA.js} +3 -3
- package/dist-cli/chunks/flow-6BZSWNHG.js +4 -0
- package/dist-cli/chunks/help-OGJBD4NP.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-LJV2S4M2.js → hidden-runtime-alias-PGF55OFH.js} +2 -2
- package/dist-cli/chunks/{home-paths-RFI2QRC3.js → home-paths-EGRMTSGE.js} +2 -2
- package/dist-cli/chunks/inspect-MZVDGAN4.js +4 -0
- package/dist-cli/chunks/install-desktop-QB76DNZF.js +4 -0
- package/dist-cli/chunks/login-HRFPYPZ3.js +26 -0
- package/dist-cli/chunks/{logout-DKXZZ6HU.js → logout-LRH5HZGP.js} +2 -2
- package/dist-cli/chunks/{maestro-WKGVVWRA.js → maestro-S53T4PPE.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-KCK6RK2C.js → maestro-generate-YKIVKOB3.js} +3 -3
- package/dist-cli/chunks/mode-47JKNUNS.js +26 -0
- package/dist-cli/chunks/{optional-demo-registry-EUSKBIPP.js → optional-demo-registry-ARZSPS5Y.js} +2 -2
- package/dist-cli/chunks/{perf-IMZMA5MT.js → perf-J73OKB4T.js} +2 -2
- package/dist-cli/chunks/{permissions-SLWXTLH6.js → permissions-L3FPI56H.js} +2 -2
- package/dist-cli/chunks/{platform-HQ735LAE.js → platform-YAROAHRM.js} +3 -3
- package/dist-cli/chunks/privacy-JZQCXHKN.js +4 -0
- package/dist-cli/chunks/record-QAQMGYOO.js +72 -0
- package/dist-cli/chunks/report-issue-4FVZCAFQ.js +29 -0
- package/dist-cli/chunks/reset-NYFUFZCG.js +4 -0
- package/dist-cli/chunks/resolve-target-SP22ZGFZ.js +4 -0
- package/dist-cli/chunks/runtime-WTS2QMXZ.js +4 -0
- package/dist-cli/chunks/{screenshot-command-HAJB5FHK.js → screenshot-command-Q5MGGEWY.js} +11 -11
- package/dist-cli/chunks/screenshot-layers-4JW7DRL7.js +20 -0
- package/dist-cli/chunks/screenshots-capture-X5PMO44E.js +42 -0
- package/dist-cli/chunks/{serve-TM46TKFW.js → serve-3JWJ45J3.js} +20 -20
- package/dist-cli/chunks/setup-USPDJESI.js +4 -0
- package/dist-cli/chunks/{sim-client-LQUZSO45.js → sim-client-RL6DGOJE.js} +2 -2
- package/dist-cli/chunks/skills-QNO6GDJB.js +50 -0
- package/dist-cli/chunks/{state-RUVM4Z4S.js → state-OGUCSUC3.js} +2 -2
- package/dist-cli/chunks/{storage-GQ33UTYN.js → storage-TG7KFQZ3.js} +2 -2
- package/dist-cli/chunks/store-ZYMQW2LN.js +4 -0
- package/dist-cli/chunks/telemetry-7CSLUUIW.js +4 -0
- package/dist-cli/chunks/timeline-R4G5P335.js +24 -0
- package/dist-cli/chunks/{upgrade-AZDZLUIT.js → upgrade-RDTXJ6O3.js} +2 -2
- package/dist-cli/chunks/upload-PLF2DPMS.js +4 -0
- package/dist-cli/chunks/{version-N3ALB75G.js → version-GU2NUZDS.js} +2 -2
- package/dist-cli/chunks/{web-XPHFZATA.js → web-7PUVG3GE.js} +2 -2
- package/dist-cli/chunks/welcome-4GGUV7FY.js +5 -0
- package/dist-cli/chunks/what-happened-OHAAX6F6.js +25 -0
- package/dist-cli/chunks/{ws-bridge-RUXOYOU6.js → ws-bridge-Z3O35MNF.js} +2 -2
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- 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 +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +69 -34
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +430 -106
- 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 +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +1 -1
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.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 +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +2 -1
- package/dist-lib/react-native-host-modules.mjs +2 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +149 -93
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +757 -353
- package/dist-lib/vite.cjs +1 -1
- package/package.json +1 -1
- package/scripts/dev-server-scanner.ts +275 -92
- package/src/dev-bundle-resolution.ts +123 -40
- package/src/host/bridge-host.ts +11 -0
- package/src/host/plane-host.ts +203 -0
- package/src/react-native-host-modules.ts +1 -0
- package/dist-cli/chunks/app-fonts-66V7TL26.js +0 -4
- package/dist-cli/chunks/auth-3FMWUVJH.js +0 -12
- package/dist-cli/chunks/chunk-3NLB5LCN.js +0 -28
- package/dist-cli/chunks/chunk-5RWUFIZW.js +0 -24
- package/dist-cli/chunks/chunk-COXCR6UU.js +0 -7
- package/dist-cli/chunks/chunk-JH5KE2OI.js +0 -4
- package/dist-cli/chunks/chunk-ROISJKH3.js +0 -99
- package/dist-cli/chunks/chunk-SUHAD6N2.js +0 -30
- package/dist-cli/chunks/chunk-TIB3CTRX.js +0 -497
- package/dist-cli/chunks/chunk-YFZLDFZK.js +0 -74
- package/dist-cli/chunks/cli-version-MJWLDXBW.js +0 -4
- package/dist-cli/chunks/config-WK7467KI.js +0 -13
- package/dist-cli/chunks/control-ESI7IZNN.js +0 -4
- package/dist-cli/chunks/daemon-UCLO27YP.js +0 -4
- package/dist-cli/chunks/desktop-FF34Z5X4.js +0 -31
- package/dist-cli/chunks/detox-Q3CWY5UX.js +0 -52
- package/dist-cli/chunks/device-WN4ZWLES.js +0 -18
- package/dist-cli/chunks/drivers-OR2JPAXD.js +0 -4
- package/dist-cli/chunks/flow-4GWW5DYT.js +0 -4
- package/dist-cli/chunks/help-MIIWOT3C.js +0 -4
- package/dist-cli/chunks/inspect-3ZGNJENJ.js +0 -4
- package/dist-cli/chunks/install-desktop-BYOJVTLS.js +0 -4
- package/dist-cli/chunks/login-GNIFSAFE.js +0 -26
- package/dist-cli/chunks/mode-YVMX7OJY.js +0 -26
- package/dist-cli/chunks/privacy-EDC7PF7G.js +0 -4
- package/dist-cli/chunks/record-N4KTTVDI.js +0 -72
- package/dist-cli/chunks/report-issue-PP752HAD.js +0 -29
- package/dist-cli/chunks/reset-DRMKGVP4.js +0 -4
- package/dist-cli/chunks/resolve-target-7WWI32R3.js +0 -4
- package/dist-cli/chunks/runtime-EKJV2ZVI.js +0 -4
- package/dist-cli/chunks/screenshot-layers-GOCJVM2G.js +0 -20
- package/dist-cli/chunks/screenshots-capture-YYJLYA3V.js +0 -42
- package/dist-cli/chunks/setup-FVPVIYE4.js +0 -4
- package/dist-cli/chunks/skills-V5TE57FN.js +0 -50
- package/dist-cli/chunks/store-IKNBDDKM.js +0 -4
- package/dist-cli/chunks/telemetry-2ZVU6WLK.js +0 -4
- package/dist-cli/chunks/timeline-LP7DMJYT.js +0 -24
- package/dist-cli/chunks/upload-DFQK2Q7X.js +0 -4
- package/dist-cli/chunks/welcome-2NFYLWLJ.js +0 -5
- package/dist-cli/chunks/what-happened-EJ5PHKT7.js +0 -25
package/cli/commands/box.ts
CHANGED
|
@@ -8,19 +8,41 @@
|
|
|
8
8
|
|
|
9
9
|
import path from 'node:path'
|
|
10
10
|
import { createInterface } from 'node:readline'
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
createHarnessShell,
|
|
13
|
+
HarnessProjectFs,
|
|
14
|
+
PROJECT_MOUNT,
|
|
15
|
+
} from '@contrast/harness/shell'
|
|
12
16
|
import { rnxPublicBrand } from '../../src/public-brand'
|
|
17
|
+
import { authHeaderOrExit } from '../auth'
|
|
13
18
|
import { findRepositoryPackageRoot } from '../setup-repository'
|
|
14
19
|
import { resolvePlacement } from '../ws-bridge'
|
|
15
20
|
import { CheckoutFilePlane, CheckoutRequiresGitError } from './box/checkout-plane'
|
|
21
|
+
import {
|
|
22
|
+
CloudBoxError,
|
|
23
|
+
connectCloudBox,
|
|
24
|
+
createCloudBox,
|
|
25
|
+
heartbeatSession,
|
|
26
|
+
listCloudBoxes,
|
|
27
|
+
stopCloudBox,
|
|
28
|
+
} from './box/cloud-box'
|
|
29
|
+
import { sharePlane } from './box/plane-share'
|
|
16
30
|
import { createRnxCommand } from './box/rnx-command'
|
|
31
|
+
import { createSearchCommand } from './box/search-command'
|
|
32
|
+
import type { HarnessShellResult } from '@contrast/harness/shell'
|
|
17
33
|
|
|
18
34
|
interface BoxCommandOptions {
|
|
19
35
|
port?: number
|
|
20
36
|
verbose?: boolean
|
|
21
37
|
}
|
|
22
38
|
|
|
23
|
-
const CLOUD_SUBCOMMANDS = new Set(['
|
|
39
|
+
const CLOUD_SUBCOMMANDS = new Set(['connect'])
|
|
40
|
+
|
|
41
|
+
// no default. a cloud box service has to be deployed before one can exist, and
|
|
42
|
+
// defaulting to an origin that answers nothing would advertise a capability
|
|
43
|
+
// that always fails.
|
|
44
|
+
const ENDPOINT_ENV = 'RNX_BOX_ENDPOINT'
|
|
45
|
+
const API_ORIGIN_ENV = 'RNX_API_ORIGIN'
|
|
24
46
|
|
|
25
47
|
function usage(): void {
|
|
26
48
|
const rnx = rnxPublicBrand.commandName
|
|
@@ -31,6 +53,12 @@ function usage(): void {
|
|
|
31
53
|
${rnx} box start a local box over the current checkout
|
|
32
54
|
${rnx} box create create a cloud box and enter its shell
|
|
33
55
|
${rnx} box create --name=<n> create a named cloud box
|
|
56
|
+
${rnx} box create --endpoint=<url>
|
|
57
|
+
the box service to create it in, or set
|
|
58
|
+
${ENDPOINT_ENV}
|
|
59
|
+
${rnx} box ls cloud boxes running on this account
|
|
60
|
+
${rnx} box stop --name=<n> stop a cloud box and its billing
|
|
61
|
+
${rnx} box delete --name=<n> stop it and wipe its files
|
|
34
62
|
|
|
35
63
|
a local box mounts this checkout at ${PROJECT_MOUNT}, gives you a shell with
|
|
36
64
|
ls, cat, find, rg, sed, pipes and redirects, and registers ${rnx} itself as a
|
|
@@ -60,6 +88,11 @@ export async function runBox(
|
|
|
60
88
|
}
|
|
61
89
|
|
|
62
90
|
const subcommand = args.find((arg) => !arg.startsWith('-'))
|
|
91
|
+
if (subcommand === 'create') return runCloudBoxCreate(args, opts)
|
|
92
|
+
if (subcommand === 'stop' || subcommand === 'delete') {
|
|
93
|
+
return runCloudBoxStop(args, subcommand === 'delete')
|
|
94
|
+
}
|
|
95
|
+
if (subcommand === 'ls') return runCloudBoxList(args)
|
|
63
96
|
if (subcommand !== undefined) {
|
|
64
97
|
if (CLOUD_SUBCOMMANDS.has(subcommand)) {
|
|
65
98
|
console.error(
|
|
@@ -78,6 +111,177 @@ export async function runBox(
|
|
|
78
111
|
return runLocalBox(opts)
|
|
79
112
|
}
|
|
80
113
|
|
|
114
|
+
function flag(args: string[], name: string): string | undefined {
|
|
115
|
+
const inline = args.find((arg) => arg.startsWith(`--${name}=`))
|
|
116
|
+
if (inline) return inline.slice(name.length + 3)
|
|
117
|
+
const index = args.indexOf(`--${name}`)
|
|
118
|
+
if (index >= 0 && args[index + 1] && !args[index + 1].startsWith('-')) {
|
|
119
|
+
return args[index + 1]
|
|
120
|
+
}
|
|
121
|
+
return undefined
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function apiOrigin(args: string[]): string {
|
|
125
|
+
return (
|
|
126
|
+
flag(args, 'api-origin') ??
|
|
127
|
+
process.env[API_ORIGIN_ENV]?.trim() ??
|
|
128
|
+
rnxPublicBrand.origin
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function requireEndpoint(args: string[], label: string): string | null {
|
|
133
|
+
const endpoint = flag(args, 'endpoint') ?? process.env[ENDPOINT_ENV]?.trim()
|
|
134
|
+
if (endpoint) return endpoint
|
|
135
|
+
const rnx = rnxPublicBrand.commandName
|
|
136
|
+
console.error(
|
|
137
|
+
` ${rnx} box ${label}: no cloud box service to talk to.\n` +
|
|
138
|
+
` set ${ENDPOINT_ENV} or pass --endpoint=<url>.`,
|
|
139
|
+
)
|
|
140
|
+
return null
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function runCloudBoxStop(args: string[], purge: boolean): Promise<number> {
|
|
144
|
+
const rnx = rnxPublicBrand.commandName
|
|
145
|
+
const label = purge ? 'delete' : 'stop'
|
|
146
|
+
const endpoint = requireEndpoint(args, label)
|
|
147
|
+
if (!endpoint) return 1
|
|
148
|
+
const name =
|
|
149
|
+
flag(args, 'name') ?? path.basename(findRepositoryPackageRoot(process.cwd()) ?? '')
|
|
150
|
+
if (!name) {
|
|
151
|
+
console.error(` ${rnx} box ${label}: which box? pass --name=<n>.`)
|
|
152
|
+
return 1
|
|
153
|
+
}
|
|
154
|
+
const { header } = authHeaderOrExit(`box ${label}`)
|
|
155
|
+
try {
|
|
156
|
+
const result = await stopCloudBox({
|
|
157
|
+
endpoint,
|
|
158
|
+
name,
|
|
159
|
+
apiKey: header.replace(/^Bearer /, ''),
|
|
160
|
+
purge,
|
|
161
|
+
})
|
|
162
|
+
console.log(
|
|
163
|
+
purge
|
|
164
|
+
? ` deleted ${name}, freeing ${Number(result.freedBytes ?? 0)} bytes`
|
|
165
|
+
: ` stopped ${name}`,
|
|
166
|
+
)
|
|
167
|
+
return 0
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (error instanceof CloudBoxError) {
|
|
170
|
+
console.error(` ${rnx} box ${label}: ${error.message}`)
|
|
171
|
+
return 1
|
|
172
|
+
}
|
|
173
|
+
throw error
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function runCloudBoxList(args: string[]): Promise<number> {
|
|
178
|
+
const rnx = rnxPublicBrand.commandName
|
|
179
|
+
const { header } = authHeaderOrExit('box ls')
|
|
180
|
+
try {
|
|
181
|
+
const boxes = await listCloudBoxes({
|
|
182
|
+
apiOrigin: apiOrigin(args),
|
|
183
|
+
apiKey: header.replace(/^Bearer /, ''),
|
|
184
|
+
})
|
|
185
|
+
if (boxes.length === 0) {
|
|
186
|
+
console.log(` no cloud boxes running on this account`)
|
|
187
|
+
return 0
|
|
188
|
+
}
|
|
189
|
+
for (const box of boxes) {
|
|
190
|
+
const minutes = Math.round(box.meteredMs / 60_000)
|
|
191
|
+
console.log(` ${box.name ?? box.id} running ${minutes}m since ${box.startedAt}`)
|
|
192
|
+
}
|
|
193
|
+
return 0
|
|
194
|
+
} catch (error) {
|
|
195
|
+
if (error instanceof CloudBoxError) {
|
|
196
|
+
console.error(` ${rnx} box ls: ${error.message}`)
|
|
197
|
+
return 1
|
|
198
|
+
}
|
|
199
|
+
throw error
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function runCloudBoxCreate(
|
|
204
|
+
args: string[],
|
|
205
|
+
opts: BoxCommandOptions,
|
|
206
|
+
): Promise<number> {
|
|
207
|
+
const rnx = rnxPublicBrand.commandName
|
|
208
|
+
const endpoint = flag(args, 'endpoint') ?? process.env[ENDPOINT_ENV]?.trim()
|
|
209
|
+
if (!endpoint) {
|
|
210
|
+
console.error(
|
|
211
|
+
` ${rnx} box create: no cloud box service to create a box in.\n` +
|
|
212
|
+
` set ${ENDPOINT_ENV} or pass --endpoint=<url>. \`${rnx} box\` runs the\n` +
|
|
213
|
+
` same shell locally over this checkout.`,
|
|
214
|
+
)
|
|
215
|
+
return 1
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const root = findRepositoryPackageRoot(process.cwd())
|
|
219
|
+
if (!root) {
|
|
220
|
+
console.error(
|
|
221
|
+
` ${rnx} box create: no project here. a box is created from a checkout,\n` +
|
|
222
|
+
` so run this inside a git repository that has a package.json.`,
|
|
223
|
+
)
|
|
224
|
+
return 1
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const plane = new CheckoutFilePlane(root)
|
|
228
|
+
try {
|
|
229
|
+
await plane.refresh()
|
|
230
|
+
} catch (error) {
|
|
231
|
+
if (error instanceof CheckoutRequiresGitError) {
|
|
232
|
+
console.error(` ${rnx} box create: ${error.message}`)
|
|
233
|
+
return 1
|
|
234
|
+
}
|
|
235
|
+
throw error
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const name = flag(args, 'name') ?? path.basename(root)
|
|
239
|
+
// the ACCOUNT's key. the box service forwards it to /v1/rnx/sessions, which
|
|
240
|
+
// is what authenticates this caller, names the account, and starts the
|
|
241
|
+
// session the box is billed against.
|
|
242
|
+
const { header } = authHeaderOrExit('box create')
|
|
243
|
+
const apiKey = header.replace(/^Bearer /, '')
|
|
244
|
+
|
|
245
|
+
console.log(` ...creating cloud box ${name}`)
|
|
246
|
+
let session: Awaited<ReturnType<typeof connectCloudBox>> | null = null
|
|
247
|
+
let heartbeat: { stop: () => void } | null = null
|
|
248
|
+
try {
|
|
249
|
+
const created = await createCloudBox({
|
|
250
|
+
endpoint,
|
|
251
|
+
name,
|
|
252
|
+
apiKey,
|
|
253
|
+
plane,
|
|
254
|
+
onProgress: opts.verbose
|
|
255
|
+
? (seeded, total) => console.log(` ...seeded ${seeded}/${total} files`)
|
|
256
|
+
: undefined,
|
|
257
|
+
})
|
|
258
|
+
console.log(` ...seeded ${created.files} project files`)
|
|
259
|
+
// shown once and never stored: this process does not own a credential
|
|
260
|
+
// store, and inventing one here would be a second place secrets live.
|
|
261
|
+
console.log(` ...box token (shown once): ${created.token}`)
|
|
262
|
+
console.log(` ...accessing shell\n`)
|
|
263
|
+
session = await connectCloudBox({ endpoint, name, token: created.token })
|
|
264
|
+
// bank the wall clock while the shell is open. the box holds no account
|
|
265
|
+
// credential, so the client that is using it is what keeps the session
|
|
266
|
+
// alive; when this process dies the sweep closes it at the last heartbeat.
|
|
267
|
+
heartbeat = heartbeatSession({
|
|
268
|
+
apiOrigin: apiOrigin(args),
|
|
269
|
+
apiKey,
|
|
270
|
+
sessionId: created.sessionId,
|
|
271
|
+
})
|
|
272
|
+
return await repl(session, '')
|
|
273
|
+
} catch (error) {
|
|
274
|
+
if (error instanceof CloudBoxError) {
|
|
275
|
+
console.error(` ${rnx} box create: ${error.message}`)
|
|
276
|
+
return 1
|
|
277
|
+
}
|
|
278
|
+
throw error
|
|
279
|
+
} finally {
|
|
280
|
+
heartbeat?.stop()
|
|
281
|
+
session?.close()
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
81
285
|
async function runLocalBox(opts: BoxCommandOptions): Promise<number> {
|
|
82
286
|
// findRepositoryPackageRoot answers both halves of what a box needs: the
|
|
83
287
|
// nearest package.json, and only when it sits inside a git repository. git
|
|
@@ -109,35 +313,90 @@ async function runLocalBox(opts: BoxCommandOptions): Promise<number> {
|
|
|
109
313
|
|
|
110
314
|
const shell = createHarnessShell({
|
|
111
315
|
projectFs: new HarnessProjectFs(plane),
|
|
112
|
-
commands: [
|
|
316
|
+
commands: [
|
|
317
|
+
createRnxCommand({ root, shellCwd: () => shell.cwd }),
|
|
318
|
+
// overrides just-bash's JavaScript rg, which reads every file body
|
|
319
|
+
// through the plane. see search-command.ts for the numbers.
|
|
320
|
+
createSearchCommand({ root }),
|
|
321
|
+
],
|
|
113
322
|
})
|
|
114
323
|
|
|
115
324
|
const relative = path.relative(root, process.cwd())
|
|
116
325
|
console.log(` ...starting local box`)
|
|
117
326
|
console.log(` ...mounting ${root} at ${PROJECT_MOUNT}`)
|
|
327
|
+
|
|
328
|
+
// authorize this checkout for the bridge daemon so a browser tab can reach
|
|
329
|
+
// this box's plane. deliberately NOT awaited: a box must not pay for a
|
|
330
|
+
// bridge that may not be there, and an older daemon never answers at all.
|
|
331
|
+
const shared = sharePlane(root, { port: opts.port })
|
|
332
|
+
// two boxes over one checkout is worth SAYING, not refusing. a person with
|
|
333
|
+
// two terminals in one project is doing something reasonable, and the plane
|
|
334
|
+
// re-reads the file list before every command, so neither box works from a
|
|
335
|
+
// stale one. what it does break is an agent mid-run, whose file set changes
|
|
336
|
+
// underneath it because something else is writing the tree. that is the wave
|
|
337
|
+
// case, and the wave's own isolation check is where a refusal belongs; this
|
|
338
|
+
// reports the fact so that check has something to refuse on.
|
|
339
|
+
void shared.settled.then((outcome) => {
|
|
340
|
+
if ('declined' in outcome) {
|
|
341
|
+
if (opts.verbose) console.log(` ...plane not shared: ${outcome.declined}`)
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
if (outcome.holders > 1) {
|
|
345
|
+
console.error(
|
|
346
|
+
`\n ${rnxPublicBrand.commandName} box: ${outcome.holders - 1} other box${
|
|
347
|
+
outcome.holders > 2 ? 'es are' : ' is'
|
|
348
|
+
} already open on this checkout.\n` +
|
|
349
|
+
` writes from either land on the same files. that is fine for a person\n` +
|
|
350
|
+
` with two terminals, and not fine for an agent, whose file set will\n` +
|
|
351
|
+
` change underneath it mid-run.\n`,
|
|
352
|
+
)
|
|
353
|
+
return
|
|
354
|
+
}
|
|
355
|
+
if (opts.verbose) {
|
|
356
|
+
console.log(` ...plane shared on the bridge (port ${outcome.port})`)
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
|
|
118
360
|
console.log(` ...accessing shell\n`)
|
|
119
361
|
if (opts.verbose) {
|
|
120
362
|
console.log(` ${plane.index().size} project files indexed\n`)
|
|
121
363
|
}
|
|
122
364
|
|
|
123
|
-
|
|
365
|
+
try {
|
|
366
|
+
// the checkout is shared with editors, agents, and the user's own terminal,
|
|
367
|
+
// so re-read the file set before every command rather than trusting a
|
|
368
|
+
// snapshot taken when the box started. a cloud box has no equivalent: its
|
|
369
|
+
// store is the box, and the box refreshes on its own side.
|
|
370
|
+
return await repl(shell, relative, () => plane.refresh())
|
|
371
|
+
} finally {
|
|
372
|
+
shared?.close()
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// the REPL drives whatever holds the shell. locally that is an in-process
|
|
377
|
+
// HarnessShell over this checkout; for a cloud box it is a socket to the box,
|
|
378
|
+
// which holds both the files and the shell. same loop, because the only
|
|
379
|
+
// difference is where exec runs.
|
|
380
|
+
interface BoxSession {
|
|
381
|
+
readonly cwd: string
|
|
382
|
+
exec(command: string): Promise<HarnessShellResult>
|
|
124
383
|
}
|
|
125
384
|
|
|
126
385
|
async function repl(
|
|
127
|
-
|
|
128
|
-
plane: CheckoutFilePlane,
|
|
386
|
+
session: BoxSession,
|
|
129
387
|
startRelative: string,
|
|
388
|
+
beforeCommand?: () => Promise<void>,
|
|
130
389
|
): Promise<number> {
|
|
131
390
|
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
132
391
|
|
|
133
392
|
// enter where the user was standing, so `rnx box` from a subdirectory does
|
|
134
393
|
// not silently teleport them to the project root.
|
|
135
394
|
if (startRelative && !startRelative.startsWith('..')) {
|
|
136
|
-
await
|
|
395
|
+
await session.exec(`cd ${JSON.stringify(startRelative)}`)
|
|
137
396
|
}
|
|
138
397
|
|
|
139
398
|
const prompt = () => {
|
|
140
|
-
rl.setPrompt(`${
|
|
399
|
+
rl.setPrompt(`${session.cwd} > `)
|
|
141
400
|
rl.prompt()
|
|
142
401
|
}
|
|
143
402
|
|
|
@@ -155,11 +414,8 @@ async function repl(
|
|
|
155
414
|
const run = async (command: string) => {
|
|
156
415
|
if (stopped) return
|
|
157
416
|
try {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// trusting a snapshot taken when the box started.
|
|
161
|
-
await plane.refresh()
|
|
162
|
-
const result = await shell.exec(command)
|
|
417
|
+
await beforeCommand?.()
|
|
418
|
+
const result = await session.exec(command)
|
|
163
419
|
if (result.text && result.text !== '(no output)') console.log(result.text)
|
|
164
420
|
} catch (error) {
|
|
165
421
|
console.error(error instanceof Error ? error.message : String(error))
|
package/cli/commands/config.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { rnxExit } from '../run-rnx'
|
|
1
2
|
import { runTelemetry } from './telemetry'
|
|
2
3
|
|
|
3
4
|
function printHelp(): void {
|
|
@@ -28,5 +29,5 @@ export async function runConfig(args: string[]): Promise<void> {
|
|
|
28
29
|
|
|
29
30
|
console.error(` unknown config command: ${sub ?? '(missing)'}`)
|
|
30
31
|
printHelp()
|
|
31
|
-
|
|
32
|
+
rnxExit(1)
|
|
32
33
|
}
|
package/cli/commands/control.ts
CHANGED
|
@@ -55,6 +55,7 @@ import {
|
|
|
55
55
|
} from '../current-sim'
|
|
56
56
|
import { getDriver } from '../drivers/registry'
|
|
57
57
|
import { openUrl as openUrlInBrowser } from '../open-url'
|
|
58
|
+
import { rethrowIfExit, rnxExit } from '../run-rnx'
|
|
58
59
|
import {
|
|
59
60
|
BridgeSimLockedError,
|
|
60
61
|
bridgePortForShellBaseUrl,
|
|
@@ -387,7 +388,12 @@ async function resolveConnectionInputForCli(target: string): Promise<ResolvedDev
|
|
|
387
388
|
|
|
388
389
|
if (registeredPort && registeredPort > 0) {
|
|
389
390
|
const discovered = await probePort(registeredPort)
|
|
390
|
-
|
|
391
|
+
// a provisional bundle URL means the scanner's fast manifest probe got no
|
|
392
|
+
// answer and fell back to the generic `/index.bundle`. that is enough to
|
|
393
|
+
// LIST the server and not enough to open it, so hand the question to
|
|
394
|
+
// `resolveConnectionInput`, which asks the manifest again with a budget
|
|
395
|
+
// that fits a real dev server and returns the entry point the app names.
|
|
396
|
+
if (discovered && !discovered.bundleUrlProvisional) {
|
|
391
397
|
return {
|
|
392
398
|
bundleUrl: discovered.bundleUrl,
|
|
393
399
|
port: discovered.port,
|
|
@@ -477,18 +483,18 @@ function parseOpenRuntimeConfig(args: string[]): SootSimConfig | undefined {
|
|
|
477
483
|
const raw = args[i + 1]
|
|
478
484
|
if (!raw) {
|
|
479
485
|
console.error(' rnx open: --replace expects <module>=<file>')
|
|
480
|
-
|
|
486
|
+
rnxExit(1)
|
|
481
487
|
}
|
|
482
488
|
const eqIndex = raw.indexOf('=')
|
|
483
489
|
if (eqIndex <= 0 || eqIndex === raw.length - 1) {
|
|
484
490
|
console.error(' rnx open: --replace expects <module>=<file>')
|
|
485
|
-
|
|
491
|
+
rnxExit(1)
|
|
486
492
|
}
|
|
487
493
|
const moduleName = raw.slice(0, eqIndex).trim()
|
|
488
494
|
const filePath = normalizeReplacementPath(raw.slice(eqIndex + 1).trim())
|
|
489
495
|
if (!fs.existsSync(filePath)) {
|
|
490
496
|
console.error(` rnx open: replacement file not found: ${filePath}`)
|
|
491
|
-
|
|
497
|
+
rnxExit(1)
|
|
492
498
|
}
|
|
493
499
|
modules[moduleName] = { file: filePath }
|
|
494
500
|
i++
|
|
@@ -502,7 +508,7 @@ function parseOpenRuntimeConfig(args: string[]): SootSimConfig | undefined {
|
|
|
502
508
|
const eqIndex = raw ? raw.indexOf('=') : -1
|
|
503
509
|
if (!raw || eqIndex <= 0 || eqIndex === raw.length - 1) {
|
|
504
510
|
console.error(' rnx open: --remap expects <host:port>=<host:port>')
|
|
505
|
-
|
|
511
|
+
rnxExit(1)
|
|
506
512
|
}
|
|
507
513
|
remap[raw.slice(0, eqIndex).trim()] = raw.slice(eqIndex + 1).trim()
|
|
508
514
|
i++
|
|
@@ -1338,7 +1344,7 @@ export async function runLinkCommand(args: string[], opts: ControlCommandOptions
|
|
|
1338
1344
|
if (parsed.positional.length !== 1) {
|
|
1339
1345
|
console.error(' rnx open: expected exactly one <url-or-route>')
|
|
1340
1346
|
console.error(' examples: rnx open /settings | rnx open myapp://settings')
|
|
1341
|
-
|
|
1347
|
+
rnxExit(1)
|
|
1342
1348
|
}
|
|
1343
1349
|
|
|
1344
1350
|
const bridge = createBridgeFromParsed(parsed)
|
|
@@ -1353,7 +1359,7 @@ export async function runLinkCommand(args: string[], opts: ControlCommandOptions
|
|
|
1353
1359
|
warningsCommand: `rnx get warnings 5${simHint}`,
|
|
1354
1360
|
requestsCommand: `rnx get requests 5${simHint}`,
|
|
1355
1361
|
})
|
|
1356
|
-
|
|
1362
|
+
rnxExit(1)
|
|
1357
1363
|
} finally {
|
|
1358
1364
|
bridge.close()
|
|
1359
1365
|
}
|
|
@@ -1397,11 +1403,11 @@ export async function runOpenCommand(
|
|
|
1397
1403
|
console.error(
|
|
1398
1404
|
` rnx open: --cdp-port must be a positive port number, got "${cdpPortArg}"`,
|
|
1399
1405
|
)
|
|
1400
|
-
|
|
1406
|
+
rnxExit(1)
|
|
1401
1407
|
}
|
|
1402
1408
|
if (requestedProfileId && ephemeralProfile) {
|
|
1403
1409
|
console.error(' rnx open: --profile cannot be combined with --ephemeral')
|
|
1404
|
-
|
|
1410
|
+
rnxExit(1)
|
|
1405
1411
|
}
|
|
1406
1412
|
const profileId = requestedProfileId ? ensureProfile(requestedProfileId).id : undefined
|
|
1407
1413
|
const openInNewSim = args.includes('--new') || !!profileId || ephemeralProfile
|
|
@@ -1410,7 +1416,7 @@ export async function runOpenCommand(
|
|
|
1410
1416
|
console.error(
|
|
1411
1417
|
' rnx open: --new, --profile, and --ephemeral cannot be combined with --sim',
|
|
1412
1418
|
)
|
|
1413
|
-
|
|
1419
|
+
rnxExit(1)
|
|
1414
1420
|
}
|
|
1415
1421
|
const target = parsed.positional[0] || ''
|
|
1416
1422
|
|
|
@@ -1419,7 +1425,7 @@ export async function runOpenCommand(
|
|
|
1419
1425
|
console.error(
|
|
1420
1426
|
' rnx open: deep links target the current app; remove --new, --profile, or --ephemeral',
|
|
1421
1427
|
)
|
|
1422
|
-
|
|
1428
|
+
rnxExit(1)
|
|
1423
1429
|
}
|
|
1424
1430
|
await runLinkCommand(args, opts)
|
|
1425
1431
|
return
|
|
@@ -1447,7 +1453,7 @@ export async function runOpenCommand(
|
|
|
1447
1453
|
` could not launch a browser: ${err instanceof Error ? err.message : String(err)}`,
|
|
1448
1454
|
)
|
|
1449
1455
|
console.error(` open it yourself: ${target}`)
|
|
1450
|
-
|
|
1456
|
+
rnxExit(1)
|
|
1451
1457
|
}
|
|
1452
1458
|
console.log(
|
|
1453
1459
|
isLocal
|
|
@@ -1492,7 +1498,7 @@ export async function runOpenCommand(
|
|
|
1492
1498
|
const requestedDriverId = args.find((_, i) => args[i - 1] === '--driver') || ''
|
|
1493
1499
|
if (requestedDriverId && !isDriverId(requestedDriverId)) {
|
|
1494
1500
|
console.error(` unknown driver "${requestedDriverId}" — run \`rnx list --drivers\``)
|
|
1495
|
-
|
|
1501
|
+
rnxExit(1)
|
|
1496
1502
|
}
|
|
1497
1503
|
const driverId: DriverId = isDriverId(requestedDriverId)
|
|
1498
1504
|
? requestedDriverId
|
|
@@ -1506,7 +1512,7 @@ export async function runOpenCommand(
|
|
|
1506
1512
|
const driverHeadless = resolveDriverHeadless(args, driverId)
|
|
1507
1513
|
if (cdpPort && driverId !== 'playwright') {
|
|
1508
1514
|
console.error(' rnx open: --cdp-port is only honored by the playwright driver')
|
|
1509
|
-
|
|
1515
|
+
rnxExit(1)
|
|
1510
1516
|
}
|
|
1511
1517
|
|
|
1512
1518
|
// metro HMR mode. interactive opens default to hot=true so edits hot-apply
|
|
@@ -1520,7 +1526,7 @@ export async function runOpenCommand(
|
|
|
1520
1526
|
const noHmrFlag = args.includes('--no-hmr')
|
|
1521
1527
|
if (hotFlag && noHmrFlag) {
|
|
1522
1528
|
console.error(' rnx open: --hot and --no-hmr cannot be combined')
|
|
1523
|
-
|
|
1529
|
+
rnxExit(1)
|
|
1524
1530
|
}
|
|
1525
1531
|
const hotMode = hotFlag ? true : noHmrFlag ? false : driverId !== 'playwright'
|
|
1526
1532
|
|
|
@@ -1532,7 +1538,7 @@ export async function runOpenCommand(
|
|
|
1532
1538
|
if (deviceArg && !(deviceArg in devices)) {
|
|
1533
1539
|
console.error(` rnx open: unknown device "${deviceArg}"`)
|
|
1534
1540
|
console.error(` known devices: ${listSelectableDeviceModels().join(', ')}`)
|
|
1535
|
-
|
|
1541
|
+
rnxExit(1)
|
|
1536
1542
|
}
|
|
1537
1543
|
const deviceSpec = deviceArg ? devices[deviceArg as DeviceModel] : undefined
|
|
1538
1544
|
|
|
@@ -1544,7 +1550,7 @@ export async function runOpenCommand(
|
|
|
1544
1550
|
const viewportMatch = viewportArg ? /^(\d+)x(\d+)$/.exec(viewportArg) : null
|
|
1545
1551
|
if (viewportArg && !viewportMatch) {
|
|
1546
1552
|
console.error(` rnx open: --viewport must be <width>x<height>, got "${viewportArg}"`)
|
|
1547
|
-
|
|
1553
|
+
rnxExit(1)
|
|
1548
1554
|
}
|
|
1549
1555
|
const explicitViewport = viewportMatch
|
|
1550
1556
|
? { width: Number(viewportMatch[1]), height: Number(viewportMatch[2]) }
|
|
@@ -1662,7 +1668,7 @@ export async function runOpenCommand(
|
|
|
1662
1668
|
warningsCommand: `rnx get warnings 5${simHint}`,
|
|
1663
1669
|
requestsCommand: `rnx get requests 5${simHint}`,
|
|
1664
1670
|
})
|
|
1665
|
-
|
|
1671
|
+
rnxExit(1)
|
|
1666
1672
|
}
|
|
1667
1673
|
|
|
1668
1674
|
if (!fallBackToNewSim && targetSim) {
|
|
@@ -1674,7 +1680,7 @@ export async function runOpenCommand(
|
|
|
1674
1680
|
)
|
|
1675
1681
|
if (!ready) {
|
|
1676
1682
|
console.error(' timed out waiting for current sim to load target')
|
|
1677
|
-
|
|
1683
|
+
rnxExit(1)
|
|
1678
1684
|
}
|
|
1679
1685
|
ready.bridge.close()
|
|
1680
1686
|
saveCurrentSimId(targetSim.id)
|
|
@@ -1706,7 +1712,7 @@ export async function runOpenCommand(
|
|
|
1706
1712
|
console.error(
|
|
1707
1713
|
' could not resolve a stable owning process; refusing unsupervised sim host',
|
|
1708
1714
|
)
|
|
1709
|
-
|
|
1715
|
+
rnxExit(1)
|
|
1710
1716
|
}
|
|
1711
1717
|
const simMatchesToken = (sim: BridgeSimInfo) =>
|
|
1712
1718
|
sim.url?.includes(`inspectOpen=${token}`) ?? false
|
|
@@ -1714,7 +1720,7 @@ export async function runOpenCommand(
|
|
|
1714
1720
|
const driver = getDriver(driverId)
|
|
1715
1721
|
if (!driver) {
|
|
1716
1722
|
console.error(` unknown driver "${driverId}" — run \`rnx list --drivers\``)
|
|
1717
|
-
|
|
1723
|
+
rnxExit(1)
|
|
1718
1724
|
}
|
|
1719
1725
|
const connectWait = resolveDriverConnectWaitOptions()
|
|
1720
1726
|
const result = await driver.launch({
|
|
@@ -1738,7 +1744,7 @@ export async function runOpenCommand(
|
|
|
1738
1744
|
})
|
|
1739
1745
|
if (!result.launched) {
|
|
1740
1746
|
console.error(` ${driver.name} driver: ${result.message}`)
|
|
1741
|
-
|
|
1747
|
+
rnxExit(1)
|
|
1742
1748
|
}
|
|
1743
1749
|
const outcome = await waitForSimMatchOrHostExit(
|
|
1744
1750
|
parsed.wsPort,
|
|
@@ -1750,7 +1756,7 @@ export async function runOpenCommand(
|
|
|
1750
1756
|
if (outcome.kind === 'host-exit') {
|
|
1751
1757
|
printDriverDiagnosticLogTail(result)
|
|
1752
1758
|
console.error(` ${driver.name} host crashed — sim is no longer attached`)
|
|
1753
|
-
|
|
1759
|
+
rnxExit(1)
|
|
1754
1760
|
}
|
|
1755
1761
|
if (outcome.kind === 'timeout') {
|
|
1756
1762
|
if (result.pid) {
|
|
@@ -1763,7 +1769,7 @@ export async function runOpenCommand(
|
|
|
1763
1769
|
console.error(
|
|
1764
1770
|
` timed out after ${connectWait.timeoutMs}ms waiting for opened sim to connect`,
|
|
1765
1771
|
)
|
|
1766
|
-
|
|
1772
|
+
rnxExit(1)
|
|
1767
1773
|
}
|
|
1768
1774
|
const match = outcome.sim
|
|
1769
1775
|
saveCurrentSimId(match.id)
|
|
@@ -1801,7 +1807,7 @@ async function runUseLikeCommand(args: string[], opts: ControlCommandOptions = {
|
|
|
1801
1807
|
` refused: ${target.id} is locked by the active user (${secs}s) — that's a live human tab`,
|
|
1802
1808
|
)
|
|
1803
1809
|
console.error(` run \`rnx open --new\` for a fresh investigation sim`)
|
|
1804
|
-
|
|
1810
|
+
rnxExit(1)
|
|
1805
1811
|
}
|
|
1806
1812
|
if (!force) {
|
|
1807
1813
|
// another CLI agent leases it. don't hijack its target silently;
|
|
@@ -1813,20 +1819,21 @@ async function runUseLikeCommand(args: string[], opts: ControlCommandOptions = {
|
|
|
1813
1819
|
console.error(
|
|
1814
1820
|
` \`sootsim use ${target.id} --force\` to take it, or \`rnx open --new\` for a fresh sim`,
|
|
1815
1821
|
)
|
|
1816
|
-
|
|
1822
|
+
rnxExit(1)
|
|
1817
1823
|
}
|
|
1818
1824
|
}
|
|
1819
1825
|
await bridge.focusSim(target.id)
|
|
1820
1826
|
saveCurrentSimId(target.id)
|
|
1821
1827
|
console.log(` using: ${target.id}`)
|
|
1822
1828
|
} catch (err) {
|
|
1829
|
+
rethrowIfExit(err)
|
|
1823
1830
|
console.error(` use failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
1824
1831
|
await printBridgeFailureDiagnostics(bridge, {
|
|
1825
1832
|
errorsCommand: `rnx get errors 5${simHint}`,
|
|
1826
1833
|
warningsCommand: `rnx get warnings 5${simHint}`,
|
|
1827
1834
|
requestsCommand: `rnx get requests 5${simHint}`,
|
|
1828
1835
|
})
|
|
1829
|
-
|
|
1836
|
+
rnxExit(1)
|
|
1830
1837
|
}
|
|
1831
1838
|
} finally {
|
|
1832
1839
|
bridge.close()
|
|
@@ -1871,10 +1878,10 @@ export async function runClaimCommand(args: string[], opts: ControlCommandOption
|
|
|
1871
1878
|
const secs = Math.max(0, Math.round(err.lock.expiresInMs / 1000))
|
|
1872
1879
|
console.error(` claim failed: locked by ${err.lock.by} for ${secs}s more`)
|
|
1873
1880
|
console.error(` use --force to take it, or \`rnx open --new\` for a fresh sim`)
|
|
1874
|
-
|
|
1881
|
+
rnxExit(1)
|
|
1875
1882
|
}
|
|
1876
1883
|
console.error(` claim failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
1877
|
-
|
|
1884
|
+
rnxExit(1)
|
|
1878
1885
|
}
|
|
1879
1886
|
} finally {
|
|
1880
1887
|
bridge.close()
|
|
@@ -2016,7 +2023,7 @@ export async function runCloseCommand(args: string[], opts: ControlCommandOption
|
|
|
2016
2023
|
console.error(
|
|
2017
2024
|
` close failed: keep sim ${plan.missingExplicitKeepId} is not connected; not closing other sims`,
|
|
2018
2025
|
)
|
|
2019
|
-
|
|
2026
|
+
rnxExit(1)
|
|
2020
2027
|
}
|
|
2021
2028
|
if (plan.targets.length === 0) {
|
|
2022
2029
|
console.log(
|
|
@@ -2047,11 +2054,12 @@ export async function runCloseCommand(args: string[], opts: ControlCommandOption
|
|
|
2047
2054
|
console.log(summary)
|
|
2048
2055
|
if (result.remaining.length > 0) {
|
|
2049
2056
|
console.error(` close failed: still connected: ${result.remaining.join(', ')}`)
|
|
2050
|
-
|
|
2057
|
+
rnxExit(1)
|
|
2051
2058
|
}
|
|
2052
2059
|
} catch (err) {
|
|
2060
|
+
rethrowIfExit(err)
|
|
2053
2061
|
console.error(` close failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2054
|
-
|
|
2062
|
+
rnxExit(1)
|
|
2055
2063
|
} finally {
|
|
2056
2064
|
bridge.close()
|
|
2057
2065
|
}
|
|
@@ -2074,7 +2082,7 @@ export async function runCloseCommand(args: string[], opts: ControlCommandOption
|
|
|
2074
2082
|
if (!closed) {
|
|
2075
2083
|
await terminatePlaywrightHostsForSims(sims, [target.id])
|
|
2076
2084
|
console.error(` close failed: ${target.id} is still connected`)
|
|
2077
|
-
|
|
2085
|
+
rnxExit(1)
|
|
2078
2086
|
}
|
|
2079
2087
|
await terminatePlaywrightHostsForSims(sims, [target.id])
|
|
2080
2088
|
if (readCurrentSimId() === target.id) {
|
|
@@ -2083,13 +2091,14 @@ export async function runCloseCommand(args: string[], opts: ControlCommandOption
|
|
|
2083
2091
|
}
|
|
2084
2092
|
console.log(` closed: ${target.id}`)
|
|
2085
2093
|
} catch (err) {
|
|
2094
|
+
rethrowIfExit(err)
|
|
2086
2095
|
console.error(` close failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2087
2096
|
await printBridgeFailureDiagnostics(bridge, {
|
|
2088
2097
|
errorsCommand: `rnx get errors 5${simHint}`,
|
|
2089
2098
|
warningsCommand: `rnx get warnings 5${simHint}`,
|
|
2090
2099
|
requestsCommand: `rnx get requests 5${simHint}`,
|
|
2091
2100
|
})
|
|
2092
|
-
|
|
2101
|
+
rnxExit(1)
|
|
2093
2102
|
}
|
|
2094
2103
|
} finally {
|
|
2095
2104
|
bridge.close()
|