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
|
@@ -108,6 +108,7 @@ async function resolvePortViaServerScan(port: number): Promise<ResolvedDevBundle
|
|
|
108
108
|
bundleUrl: string
|
|
109
109
|
framework?: string
|
|
110
110
|
projectName?: string
|
|
111
|
+
bundleUrlProvisional?: boolean
|
|
111
112
|
} =>
|
|
112
113
|
!!s &&
|
|
113
114
|
typeof s === 'object' &&
|
|
@@ -115,6 +116,13 @@ async function resolvePortViaServerScan(port: number): Promise<ResolvedDevBundle
|
|
|
115
116
|
typeof (s as { bundleUrl?: unknown }).bundleUrl === 'string',
|
|
116
117
|
)
|
|
117
118
|
if (!match) return null
|
|
119
|
+
// the scanner marks a bundle URL provisional when its own fast manifest
|
|
120
|
+
// probe got no answer and it fell back to the generic `/index.bundle`. that
|
|
121
|
+
// is enough to LIST the server and not enough to open it, so fall through
|
|
122
|
+
// to the direct probe below, which asks the manifest again with a budget
|
|
123
|
+
// that fits a real dev server. same rule the CLI applies in
|
|
124
|
+
// `resolveConnectionInputForCli`; a provisional URL must never reach a sim.
|
|
125
|
+
if (match.bundleUrlProvisional) return null
|
|
118
126
|
return {
|
|
119
127
|
// normalize exactly like the manifest-probe branch below — the scan
|
|
120
128
|
// returns metro's manifest launchAsset url (lazy=true for code-split
|
|
@@ -131,38 +139,122 @@ async function resolvePortViaServerScan(port: number): Promise<ResolvedDevBundle
|
|
|
131
139
|
}
|
|
132
140
|
}
|
|
133
141
|
|
|
142
|
+
// how a dev server answered `GET /`, which is the one request that tells the
|
|
143
|
+
// two kinds of dev server apart. an Expo or One server answers with its
|
|
144
|
+
// manifest. a vanilla Metro server answers with Metro's own HTML landing page
|
|
145
|
+
// and has no manifest to serve. NO answer is neither: a One server that is
|
|
146
|
+
// still starting leaves `/` hanging until its manifest middleware is up, while
|
|
147
|
+
// `/status` already reports packager-status:running. reading that silence as
|
|
148
|
+
// "this server has no manifest" opens the app on `<base>/index.bundle` — a
|
|
149
|
+
// second entry point, with its own module graph, that the app's manifest never
|
|
150
|
+
// named — so Metro builds the whole app twice and the connection is labelled
|
|
151
|
+
// `metro` instead of its real framework.
|
|
152
|
+
type ManifestProbe =
|
|
153
|
+
| { state: 'resolved'; bundle: ResolvedDevBundle }
|
|
154
|
+
| { state: 'no-manifest' }
|
|
155
|
+
| { state: 'no-answer' }
|
|
156
|
+
|
|
157
|
+
// a `/` that has not answered in this long is a server still coming up, not a
|
|
158
|
+
// server telling us what it is. without it an unresponsive dev server hangs
|
|
159
|
+
// the caller for as long as it feels like.
|
|
160
|
+
const MANIFEST_PROBE_TIMEOUT_MS = 5_000
|
|
161
|
+
// how long to keep asking a confirmed-running packager for its manifest.
|
|
162
|
+
const MANIFEST_BOOT_BUDGET_MS = 30_000
|
|
163
|
+
const MANIFEST_BOOT_RETRY_MS = 1_000
|
|
164
|
+
|
|
165
|
+
async function probeExpoManifest(
|
|
166
|
+
normalizedBaseUrl: string,
|
|
167
|
+
port: number,
|
|
168
|
+
): Promise<ManifestProbe> {
|
|
169
|
+
let body: string
|
|
170
|
+
// the deadline means STOP WAITING, not KILL THE REQUEST. `AbortSignal.timeout`
|
|
171
|
+
// does the second: it tears down a dev server that is still assembling its
|
|
172
|
+
// manifest, which fires "Cannot pipe to a closed or destroyed stream" into
|
|
173
|
+
// One's manifest middleware and can exit the user's dev server. so nothing
|
|
174
|
+
// here aborts. we race the probe against the budget, answer `no-answer` on
|
|
175
|
+
// time, and let the request run itself out in the background, discarded.
|
|
176
|
+
const pending = fetchDevProbe(`${normalizedBaseUrl}/`, {
|
|
177
|
+
headers: { 'expo-platform': 'ios' },
|
|
178
|
+
}).then(
|
|
179
|
+
async (res) => (res.ok ? { ok: true as const, body: await res.text() } : null),
|
|
180
|
+
() => undefined,
|
|
181
|
+
)
|
|
182
|
+
const answered = await Promise.race([
|
|
183
|
+
pending,
|
|
184
|
+
new Promise<'timeout'>((resolve) =>
|
|
185
|
+
setTimeout(() => resolve('timeout'), MANIFEST_PROBE_TIMEOUT_MS).unref?.(),
|
|
186
|
+
),
|
|
187
|
+
])
|
|
188
|
+
if (answered === 'timeout' || answered === undefined) return { state: 'no-answer' }
|
|
189
|
+
if (answered === null) return { state: 'no-manifest' }
|
|
190
|
+
body = answered.body
|
|
191
|
+
if (!body.trim()) return { state: 'no-answer' }
|
|
192
|
+
|
|
193
|
+
let manifest: any
|
|
194
|
+
try {
|
|
195
|
+
manifest = JSON.parse(body)
|
|
196
|
+
} catch {
|
|
197
|
+
// a page rather than a manifest — Metro's landing page, or an app's own
|
|
198
|
+
// SSR html. that is a real answer: this server serves no manifest.
|
|
199
|
+
return { state: 'no-manifest' }
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const client = manifest?.extra?.expoClient || manifest?.extra || {}
|
|
203
|
+
const rawLaunchUrl =
|
|
204
|
+
typeof manifest?.launchAsset?.url === 'string' ? manifest.launchAsset.url : undefined
|
|
205
|
+
const launchUrl = rawLaunchUrl
|
|
206
|
+
? alignLoopbackBundleUrlWithProbe(rawLaunchUrl, normalizedBaseUrl)
|
|
207
|
+
: undefined
|
|
208
|
+
if (!launchUrl && !client.name) return { state: 'no-manifest' }
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
state: 'resolved',
|
|
212
|
+
bundle: {
|
|
213
|
+
bundleUrl: normalizeNativeDevBundleUrl(
|
|
214
|
+
launchUrl || `${normalizedBaseUrl}${METRO_BUNDLE_PATH}`,
|
|
215
|
+
),
|
|
216
|
+
port,
|
|
217
|
+
framework: inferManifestFramework(launchUrl, client.sdkVersion),
|
|
218
|
+
projectName: client.name,
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function isPackagerRunning(normalizedBaseUrl: string): Promise<boolean> {
|
|
224
|
+
try {
|
|
225
|
+
const res = await fetchDevProbe(`${normalizedBaseUrl}/status`)
|
|
226
|
+
if (!res.ok) return false
|
|
227
|
+
return (await res.text()).includes('packager-status:running')
|
|
228
|
+
} catch {
|
|
229
|
+
return false
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
134
233
|
async function probeBaseUrlBundleDirect(
|
|
135
234
|
baseUrl: string,
|
|
136
235
|
port: number,
|
|
137
236
|
): Promise<ResolvedDevBundle | null> {
|
|
138
237
|
const normalizedBaseUrl = baseUrl.replace(/\/+$/, '')
|
|
139
238
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
: undefined
|
|
154
|
-
if (launchUrl || client.name) {
|
|
155
|
-
return {
|
|
156
|
-
bundleUrl: normalizeNativeDevBundleUrl(
|
|
157
|
-
launchUrl || `${normalizedBaseUrl}${METRO_BUNDLE_PATH}`,
|
|
158
|
-
),
|
|
159
|
-
port,
|
|
160
|
-
framework: inferManifestFramework(launchUrl, client.sdkVersion),
|
|
161
|
-
projectName: client.name,
|
|
162
|
-
}
|
|
163
|
-
}
|
|
239
|
+
let manifest = await probeExpoManifest(normalizedBaseUrl, port)
|
|
240
|
+
let packagerConfirmed = false
|
|
241
|
+
|
|
242
|
+
// nothing answered `/`. that is either a closed address — the other loopback
|
|
243
|
+
// family, a dead port — or a packager whose manifest middleware has not come
|
|
244
|
+
// up yet. `/status` separates them, and only the second is worth waiting on.
|
|
245
|
+
if (manifest.state === 'no-answer') {
|
|
246
|
+
packagerConfirmed = await isPackagerRunning(normalizedBaseUrl)
|
|
247
|
+
if (!packagerConfirmed) return null
|
|
248
|
+
const deadline = Date.now() + MANIFEST_BOOT_BUDGET_MS
|
|
249
|
+
while (manifest.state === 'no-answer' && Date.now() < deadline) {
|
|
250
|
+
await new Promise((resolve) => setTimeout(resolve, MANIFEST_BOOT_RETRY_MS))
|
|
251
|
+
manifest = await probeExpoManifest(normalizedBaseUrl, port)
|
|
164
252
|
}
|
|
165
|
-
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (manifest.state === 'resolved') return manifest.bundle
|
|
256
|
+
// a server that never says what it is gets no guessed entry point.
|
|
257
|
+
if (manifest.state === 'no-answer') return null
|
|
166
258
|
|
|
167
259
|
// vanilla Metro (or any non-Expo dev server) — packager-status:running on
|
|
168
260
|
// /status is the canonical signature. we previously also did `HEAD
|
|
@@ -176,21 +268,12 @@ async function probeBaseUrlBundleDirect(
|
|
|
176
268
|
// the Expo manifest probe above already returns the correct launchAsset.url
|
|
177
269
|
// for One apps (which serve the Expo manifest format via vxrn), so we
|
|
178
270
|
// drop the bespoke One probe entirely.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
bundleUrl: `${normalizedBaseUrl}${METRO_BUNDLE_PATH}`,
|
|
186
|
-
port,
|
|
187
|
-
framework: 'metro',
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
} catch {}
|
|
192
|
-
|
|
193
|
-
return null
|
|
271
|
+
if (!packagerConfirmed && !(await isPackagerRunning(normalizedBaseUrl))) return null
|
|
272
|
+
return {
|
|
273
|
+
bundleUrl: `${normalizedBaseUrl}${METRO_BUNDLE_PATH}`,
|
|
274
|
+
port,
|
|
275
|
+
framework: 'metro',
|
|
276
|
+
}
|
|
194
277
|
}
|
|
195
278
|
|
|
196
279
|
async function probeBaseUrlBundle(
|
package/src/host/bridge-host.ts
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
isFetchProxyRequestUrl,
|
|
50
50
|
} from './fetch-proxy-handler.ts'
|
|
51
51
|
import { openUrl as openUrlInBrowser, type OpenUrlOptions } from './open-url.ts'
|
|
52
|
+
import { PlaneHost } from './plane-host'
|
|
52
53
|
import {
|
|
53
54
|
handleReplacementModuleRequest,
|
|
54
55
|
isReplacementModuleRequestUrl,
|
|
@@ -389,6 +390,7 @@ export class SootSimBridgeHost {
|
|
|
389
390
|
private nextCliFallbackId = 1
|
|
390
391
|
private cliIdleTimer: NodeJS.Timeout | null = null
|
|
391
392
|
private agentHost: AgentHost
|
|
393
|
+
private planeHost = new PlaneHost()
|
|
392
394
|
private static CLI_IDLE_TIMEOUT_MS = 60_000
|
|
393
395
|
private static CLI_LEASE_TTL_MS = 600_000
|
|
394
396
|
private static USER_ACTIVE_LEASE_TTL_MS = 8_000
|
|
@@ -670,6 +672,14 @@ export class SootSimBridgeHost {
|
|
|
670
672
|
return
|
|
671
673
|
}
|
|
672
674
|
|
|
675
|
+
// plane:* is the project file plane, routed the same way and for the
|
|
676
|
+
// same reason: the CLI, a box, and a browser tab all speak one
|
|
677
|
+
// envelope. see plans/rnx-cloud/transport-decision.md.
|
|
678
|
+
if (typeof msg.type === 'string' && msg.type.startsWith('plane:')) {
|
|
679
|
+
void this.planeHost.handleMessage(ws, msg)
|
|
680
|
+
return
|
|
681
|
+
}
|
|
682
|
+
|
|
673
683
|
// runtime:* messages manage installed engine runtimes. list / use
|
|
674
684
|
// are handled in-daemon; install runs entirely inside the CLI so
|
|
675
685
|
// we don't need a daemon-side handler for it.
|
|
@@ -1068,6 +1078,7 @@ export class SootSimBridgeHost {
|
|
|
1068
1078
|
// always drop agent subscriptions first — the FIFO refcount needs
|
|
1069
1079
|
// to settle before any later broadcast fan-out fires.
|
|
1070
1080
|
this.agentHost.unregisterSocket(ws)
|
|
1081
|
+
this.planeHost.unregisterSocket(ws)
|
|
1071
1082
|
if (role === 'sim' && sim) {
|
|
1072
1083
|
this.rememberDisconnectedSim(sim)
|
|
1073
1084
|
if (this.primarySimId === sim.id) {
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// `plane:*` — the project file plane over the loopback bridge, so a browser
|
|
2
|
+
// tab can reach a real local box. the envelope and its constraints are decided
|
|
3
|
+
// in plans/rnx-cloud/transport-decision.md; this is the local half.
|
|
4
|
+
//
|
|
5
|
+
// the vocabulary is exactly ProjectFilePlane's members, because the point is
|
|
6
|
+
// that a remote plane is a third provider behind that seam rather than a
|
|
7
|
+
// second file API. it is remotable without changing the interface because
|
|
8
|
+
// `index()` is synchronous but `refresh()` is the async step the contract
|
|
9
|
+
// already requires before an index read: a remote plane refreshes into a local
|
|
10
|
+
// cache and reads that.
|
|
11
|
+
//
|
|
12
|
+
// WHY A ROOT MUST BE REGISTERED FIRST. this listener is loopback-only, but a
|
|
13
|
+
// WebSocket to 127.0.0.1 is not subject to CORS, so any page the user visits
|
|
14
|
+
// can open one. if `plane:open` took an arbitrary path, that page could read
|
|
15
|
+
// any file on the machine through this daemon. a root is reachable only while
|
|
16
|
+
// a box has registered it, and the registration dies with its socket.
|
|
17
|
+
|
|
18
|
+
import fs from 'node:fs'
|
|
19
|
+
import path from 'node:path'
|
|
20
|
+
import { dispatchPlaneCommand, indexVersion } from '@contrast/harness/plane'
|
|
21
|
+
import { CheckoutFilePlane } from '../../cli/commands/box/checkout-plane'
|
|
22
|
+
import type { ProjectFilePlane } from '@contrast/harness/plane'
|
|
23
|
+
import type { WebSocket } from 'ws'
|
|
24
|
+
|
|
25
|
+
const WS_OPEN = 1
|
|
26
|
+
|
|
27
|
+
interface OpenPlane {
|
|
28
|
+
root: string
|
|
29
|
+
plane: ProjectFilePlane
|
|
30
|
+
version: string
|
|
31
|
+
sockets: Set<WebSocket>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class PlaneNotRegisteredError extends Error {}
|
|
35
|
+
|
|
36
|
+
export class PlaneHost {
|
|
37
|
+
// roots a box authorized, each tied to the socket that authorized it
|
|
38
|
+
private registered = new Map<string, Set<WebSocket>>()
|
|
39
|
+
private open = new Map<string, OpenPlane>()
|
|
40
|
+
private byRoot = new Map<string, string>()
|
|
41
|
+
private nextPlaneId = 1
|
|
42
|
+
|
|
43
|
+
/** handle a plane:* message. returns true iff it was one. */
|
|
44
|
+
async handleMessage(ws: WebSocket, msg: unknown): Promise<boolean> {
|
|
45
|
+
if (typeof msg !== 'object' || msg === null) return false
|
|
46
|
+
const record: Record<string, unknown> = { ...msg }
|
|
47
|
+
const type = record.type
|
|
48
|
+
if (typeof type !== 'string' || !type.startsWith('plane:')) return false
|
|
49
|
+
const id = record.id
|
|
50
|
+
try {
|
|
51
|
+
this.respond(ws, id, await this.dispatch(ws, type, record))
|
|
52
|
+
} catch (err) {
|
|
53
|
+
const code =
|
|
54
|
+
err instanceof PlaneNotRegisteredError ? 'plane-not-registered' : undefined
|
|
55
|
+
this.respondError(ws, id, err instanceof Error ? err.message : String(err), code)
|
|
56
|
+
}
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
unregisterSocket(ws: WebSocket): void {
|
|
61
|
+
for (const [root, sockets] of [...this.registered]) {
|
|
62
|
+
if (!sockets.delete(ws)) continue
|
|
63
|
+
if (sockets.size > 0) continue
|
|
64
|
+
// the box that authorized this root is gone, so drop the plane with it
|
|
65
|
+
// rather than leaving a reachable handle behind.
|
|
66
|
+
this.registered.delete(root)
|
|
67
|
+
const planeId = this.byRoot.get(root)
|
|
68
|
+
if (planeId) {
|
|
69
|
+
this.open.delete(planeId)
|
|
70
|
+
this.byRoot.delete(root)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const [planeId, entry] of [...this.open]) {
|
|
74
|
+
entry.sockets.delete(ws)
|
|
75
|
+
if (entry.sockets.size === 0 && !this.registered.has(entry.root)) {
|
|
76
|
+
this.open.delete(planeId)
|
|
77
|
+
this.byRoot.delete(entry.root)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** roots currently reachable, for the daemon's status output. */
|
|
83
|
+
registeredRoots(): string[] {
|
|
84
|
+
return [...this.registered.keys()]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private async dispatch(
|
|
88
|
+
ws: WebSocket,
|
|
89
|
+
type: string,
|
|
90
|
+
msg: Record<string, unknown>,
|
|
91
|
+
): Promise<unknown> {
|
|
92
|
+
switch (type) {
|
|
93
|
+
case 'plane:register': {
|
|
94
|
+
const root = this.realRoot(String(msg.root ?? ''))
|
|
95
|
+
const sockets = this.registered.get(root) ?? new Set<WebSocket>()
|
|
96
|
+
sockets.add(ws)
|
|
97
|
+
this.registered.set(root, sockets)
|
|
98
|
+
// `holders` INCLUDING this one, because two boxes over one checkout is
|
|
99
|
+
// a mistake only the daemon can see: CheckoutFilePlane re-runs
|
|
100
|
+
// `git ls-files` before every command, so each box changes the file
|
|
101
|
+
// set the other is working from mid-run. the daemon reports it rather
|
|
102
|
+
// than refusing, because a box whose socket dropped and reconnected
|
|
103
|
+
// can briefly race its own stale entry, and a refusal there would
|
|
104
|
+
// brick a legitimate restart. the caller decides; see box.ts, which
|
|
105
|
+
// already refuses a box inside a box for the same reason.
|
|
106
|
+
return { root, holders: sockets.size }
|
|
107
|
+
}
|
|
108
|
+
// READ ONLY, and that is the whole point of it existing beside the
|
|
109
|
+
// `holders` field on plane:register.
|
|
110
|
+
//
|
|
111
|
+
// registration is the AUTHORIZATION step: it is what permits any page on
|
|
112
|
+
// this machine to open that root over a loopback socket that CORS does
|
|
113
|
+
// not protect. so a caller asking "is anyone else in this checkout"
|
|
114
|
+
// cannot be made to authorize the root in order to find out, even
|
|
115
|
+
// briefly. it also means the count is the honest one: no box reads 0 and
|
|
116
|
+
// a single box reads 1, where asking through register would have made
|
|
117
|
+
// every answer one higher and forced a threshold that counts the asker.
|
|
118
|
+
case 'plane:status': {
|
|
119
|
+
const root = this.realRoot(String(msg.root ?? ''))
|
|
120
|
+
const sockets = this.registered.get(root)
|
|
121
|
+
return { root, holders: sockets?.size ?? 0, registered: Boolean(sockets) }
|
|
122
|
+
}
|
|
123
|
+
case 'plane:open': {
|
|
124
|
+
const root = this.realRoot(String(msg.root ?? ''))
|
|
125
|
+
if (!this.registered.has(root)) {
|
|
126
|
+
throw new PlaneNotRegisteredError(
|
|
127
|
+
`no box has registered ${root}; start one with \`rnx box\` there`,
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
const existingId = this.byRoot.get(root)
|
|
131
|
+
const existing = existingId ? this.open.get(existingId) : undefined
|
|
132
|
+
if (existingId && existing) {
|
|
133
|
+
existing.sockets.add(ws)
|
|
134
|
+
return { planeId: existingId, version: existing.version }
|
|
135
|
+
}
|
|
136
|
+
const plane = new CheckoutFilePlane(root)
|
|
137
|
+
await plane.refresh()
|
|
138
|
+
const planeId = `plane-${this.nextPlaneId++}`
|
|
139
|
+
const entry: OpenPlane = {
|
|
140
|
+
root,
|
|
141
|
+
plane,
|
|
142
|
+
version: indexVersion(plane.index()),
|
|
143
|
+
sockets: new Set([ws]),
|
|
144
|
+
}
|
|
145
|
+
this.open.set(planeId, entry)
|
|
146
|
+
this.byRoot.set(root, planeId)
|
|
147
|
+
return { planeId, version: entry.version }
|
|
148
|
+
}
|
|
149
|
+
case 'plane:close': {
|
|
150
|
+
const entry = this.open.get(String(msg.planeId ?? ''))
|
|
151
|
+
if (entry) entry.sockets.delete(ws)
|
|
152
|
+
return { ok: true }
|
|
153
|
+
}
|
|
154
|
+
default: {
|
|
155
|
+
const entry = this.entry(msg)
|
|
156
|
+
const handled = await dispatchPlaneCommand(entry.plane, type, msg)
|
|
157
|
+
if (!handled) throw new Error(`unknown plane command: ${type}`)
|
|
158
|
+
// no companion change event on a write. the write lands on the real
|
|
159
|
+
// checkout and the dev server's own watcher sees it, so a notification
|
|
160
|
+
// here would be a second channel for something the filesystem already
|
|
161
|
+
// reports. the cloud placement, whose store nothing can watch, is where
|
|
162
|
+
// a write has to fan one out.
|
|
163
|
+
if (handled.version) entry.version = handled.version
|
|
164
|
+
return handled.result
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private entry(msg: Record<string, unknown>): OpenPlane {
|
|
170
|
+
const entry = this.open.get(String(msg.planeId ?? ''))
|
|
171
|
+
if (!entry) throw new Error(`unknown planeId: ${String(msg.planeId)}`)
|
|
172
|
+
return entry
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// resolve symlinks and relative segments before comparing, so a registered
|
|
176
|
+
// root and an opened one cannot disagree about the same directory.
|
|
177
|
+
private realRoot(root: string): string {
|
|
178
|
+
if (!root) throw new Error('root is required')
|
|
179
|
+
const resolved = path.resolve(root)
|
|
180
|
+
let real: string
|
|
181
|
+
try {
|
|
182
|
+
real = fs.realpathSync(resolved)
|
|
183
|
+
} catch {
|
|
184
|
+
throw new Error(`no such directory: ${resolved}`)
|
|
185
|
+
}
|
|
186
|
+
if (!fs.statSync(real).isDirectory()) throw new Error(`not a directory: ${real}`)
|
|
187
|
+
return real
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private respond(ws: WebSocket, id: unknown, result: unknown): void {
|
|
191
|
+
if (ws.readyState !== WS_OPEN) return
|
|
192
|
+
try {
|
|
193
|
+
ws.send(JSON.stringify({ id, result }))
|
|
194
|
+
} catch {}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private respondError(ws: WebSocket, id: unknown, error: string, code?: string): void {
|
|
198
|
+
if (ws.readyState !== WS_OPEN) return
|
|
199
|
+
try {
|
|
200
|
+
ws.send(JSON.stringify({ id, error, ...(code ? { code } : {}) }))
|
|
201
|
+
} catch {}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -11,6 +11,7 @@ export const REACT_NATIVE_PASSTHROUGH_SPECIFIERS: readonly string[] = [
|
|
|
11
11
|
'react-native/Libraries/StyleSheet',
|
|
12
12
|
'react-native/Libraries/Utilities/differ',
|
|
13
13
|
'react-native/Libraries/Utilities/stringifySafe',
|
|
14
|
+
'react-native/src/private/assets/AssetRegistry',
|
|
14
15
|
'react-native/src/private/featureflags',
|
|
15
16
|
'react-native/Libraries/TurboModule/TurboModuleRegistry',
|
|
16
17
|
'react-native/Libraries/Utilities/Platform',
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/*! rnx v0.1.369 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{p}from"./chunk-UDXQCEJU.js";import"./chunk-JLOOLA47.js";import"./chunk-UQJY6O72.js";import"./chunk-DKSZZABN.js";import{existsSync as h,mkdirSync as d,readFileSync as y,writeFileSync as g}from"node:fs";import{join as m}from"node:path";import{gzipSync as w}from"node:zlib";function f(t,e){let o=t.indexOf(`--${e}`);if(o!==-1&&o+1<t.length)return t[o+1];let r=t.find(i=>i.startsWith(`--${e}=`));return r?r.slice(e.length+3):void 0}async function x(t){let e=f(t,"wire")||"",o=f(t,"out-dir"),r=f(t,"manifest");if((!o||!r)&&(console.error(" usage: rnx app-fonts stage --wire <wire> --out-dir <dir> --manifest <path>"),process.exit(1)),!e.trim()){console.log("no app fonts declared \u2014 nothing to stage");return}d(o,{recursive:!0});let i=await p(e,{onStaged:({url:n,byteLength:s})=>console.log(` staged app font: ${n} (${(s/1024).toFixed(1)} KiB)`),onError:(n,s)=>console.error(` warning: failed to fetch app font ${n}: ${s instanceof Error?s.message:s}`)}),a=[];for(let n of i){let s=w(n.bytes);g(m(o,n.urlhash),s),a.push({url:n.url,urlhash:n.urlhash,contentType:n.contentType,encoding:"gzip",sizeBytes:s.length,rawBytes:n.bytes.byteLength})}let c=[];if(h(r))try{let n=JSON.parse(y(r,"utf8"));Array.isArray(n)&&(c=n)}catch{}let u=new Set(c.map(n=>n.urlhash)),l=[...c,...a.filter(n=>!u.has(n.urlhash))];g(r,JSON.stringify(l)),console.log(`staged ${a.length} app font(s); manifest now has ${l.length} file(s)`)}async function E(t){let e=t[0];if(e==="stage"){await x(t.slice(1));return}console.error(` unknown app-fonts subcommand: ${e??"(none)"}`),console.error(" available: stage"),process.exit(1)}export{E as runAppFonts};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/*! rnx v0.1.369 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{a as r}from"./chunk-JH5KE2OI.js";import{d as t}from"./chunk-ZW6JXEVX.js";import"./chunk-FBMNYSSW.js";import"./chunk-DKSZZABN.js";function i(){console.log(`
|
|
5
|
-
rnx auth \u2014 inspect CLI auth
|
|
6
|
-
|
|
7
|
-
usage:
|
|
8
|
-
rnx auth
|
|
9
|
-
|
|
10
|
-
API keys are read from RNX_API_KEY, or SOOTSIM_API_KEY if that is unset.
|
|
11
|
-
Use \`rnx login\` to sign in and \`rnx logout\` to clear the desktop session.
|
|
12
|
-
`)}async function s(){let o=r();if(!o)return console.log(" not signed in"),console.log(" set RNX_API_KEY=sk_rnx_... or run `rnx login`"),1;if(o.kind==="api-key")return console.log(` api key ${o.secret.slice(0,14)}...`),console.log(` source: ${o.envName} env var`),0;if(o.kind==="github")return console.log(` github token (${o.source})`),console.log(` repo: ${o.repoId}`),0;let e=await t(o.origin),n=e?.user;return console.log(` ${n?.email||n?.name||n?.id||"signed in"}`),console.log(` origin: ${e?.origin??o.origin}`),e?.updatedAt&&console.log(` updated: ${e.updatedAt}`),0}async function c(o){if(o.includes("--help")||o.includes("-h")){i();return}let e=o.find(n=>!n.startsWith("-"));if(!e||e==="status"){process.exitCode=await s();return}console.error(` unknown auth command: ${e}`),i(),process.exit(1)}export{c as runAuth};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/*! rnx v0.1.369 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{e as p}from"./chunk-ROISJKH3.js";import{a as c,b as l}from"./chunk-JH5KE2OI.js";import{a}from"./chunk-ZW6JXEVX.js";async function g(e,i){if(i)return i.replace(/\/$/,"");if(e.kind==="session"&&e.origin)return e.origin.replace(/\/$/,"");let r=a();return r?.origin?r.origin.replace(/\/$/,""):(await p()).replace(/\/$/,"")}async function x(e,i={}){let r=c();if(r||(process.stderr.write(`
|
|
5
|
-
rnx ${e} needs auth before it can check recording access.
|
|
6
|
-
|
|
7
|
-
pick one:
|
|
8
|
-
\u2022 run \`rnx login\`
|
|
9
|
-
\u2022 set RNX_API_KEY=sk_rnx_... (recommended for CI)
|
|
10
|
-
|
|
11
|
-
`),process.exit(1)),r.kind==="github"&&i.allowGitHubAuth)return;let s=await g(r,i.originOverride),n;try{n=await fetch(`${s}/api/sootsim/billing/subscription`,{headers:{authorization:l(r)}})}catch(t){process.stderr.write(`
|
|
12
|
-
rnx ${e} couldn't verify recording access.
|
|
13
|
-
${t instanceof Error?t.message:String(t)}
|
|
14
|
-
|
|
15
|
-
`),process.exit(1)}if(n.status===401&&(process.stderr.write(`
|
|
16
|
-
rnx ${e} couldn't verify recording access with the current auth against ${s}.
|
|
17
|
-
sign in again for that origin, or provide a valid RNX_API_KEY.
|
|
18
|
-
|
|
19
|
-
`),process.exit(1)),!n.ok){let t=await n.text().catch(()=>"");process.stderr.write(`
|
|
20
|
-
rnx ${e} couldn't verify recording access (${n.status}).
|
|
21
|
-
${t||"unexpected billing response"}
|
|
22
|
-
|
|
23
|
-
`),process.exit(1)}let o=await n.json();if(o.entitlements?.desktopRecording===!0)return;let u=o.plan??"free";process.stderr.write(`
|
|
24
|
-
rnx ${e} recording requires Personal, Team, or an active trial.
|
|
25
|
-
current plan: ${u}.
|
|
26
|
-
free keeps basic 3d mode; the trial unlocks the full Personal surface.
|
|
27
|
-
|
|
28
|
-
`),process.exit(1)}export{x as a};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/*! rnx v0.1.369 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{a as v}from"./chunk-EKAEEWC2.js";import{a as P,b as y,c as $,d as k,e as x,h as E,i as O}from"./chunk-WGE2P25F.js";import{spawn as M,spawnSync as p}from"child_process";import{chmodSync as I,copyFileSync as L,createWriteStream as C,existsSync as g,mkdirSync as D,rmSync as u,statSync as R,unlinkSync as B}from"fs";import{tmpdir as F}from"os";import{dirname as N,join as f,resolve as A}from"path";var b="https://contrast.dev/api/electron-release";function W(){let n=process.arch;if(process.platform==="darwin"){let t=n==="arm64"?"mac-arm64":"mac-x64",r=`sootsim-latest-${t}.dmg`;return{platform:t,filename:r,url:`${b}/${r}`,install:q}}if(process.platform==="linux"){let t=n==="arm64"?"linux-arm64":"linux-x64",r=`sootsim-latest-${t}.AppImage`;return{platform:t,filename:r,url:`${b}/${r}`,install:z}}if(process.platform==="win32"){let t="sootsim-latest-win-x64.exe";return{platform:"win-x64",filename:t,url:`${b}/${t}`,install:H}}return null}async function Y({url:n,dest:t,onProgress:r}){let s=await fetch(n,{redirect:"follow"});if(!s.ok)throw new Error(`${s.status} ${s.statusText} (${n})`);let o=s.headers.get("content-length"),a=o?Number(o):null;if(!s.body)throw new Error(`empty response body (${n})`);D(N(t),{recursive:!0});let l=C(t),d=s.body.getReader(),c=0;try{for(;;){let{done:e,value:i}=await d.read();if(e)break;l.write(Buffer.from(i)),c+=i.byteLength,r?.(c,a)}}finally{await new Promise((e,i)=>{l.end(m=>m?i(m):e())})}}function _(n,t){let r=(n/1048576).toFixed(1);if(!t)return` ${r} MB`;let s=Math.min(100,Math.round(n/t*100)),o=(t/(1024*1024)).toFixed(1),a=24,l=Math.round(s/100*a);return` ${P("\u2588".repeat(l))+$("\u2591".repeat(a-l))} ${s}% ${r} / ${o} MB`}async function q(n){let t=p("hdiutil",["attach","-nobrowse","-readonly",n],{encoding:"utf8"});if(t.status!==0)throw new Error(`hdiutil attach failed: ${t.stderr||t.stdout}`.trim());let r=t.stdout.trim().split(`
|
|
5
|
-
`),o=r[r.length-1].split(" ").pop()?.trim();if(!o||!g(o))throw new Error(`could not determine dmg mount point (output: ${t.stdout})`);let a=f(o,"rnx.app");if(!g(a))throw p("hdiutil",["detach","-force",o]),new Error(`rnx.app not found inside ${o}`);let l="/Applications",d=A(process.env.HOME||"","Applications"),c=l;try{let w=f(l,`.sootsim-write-probe-${process.pid}`);p("touch",[w]),g(w)?u(w,{force:!0}):c=d}catch{c=d}c===d&&D(c,{recursive:!0});let e=f(c,"rnx.app"),i=f(c,`.rnx-incoming-${process.pid}.app`);u(i,{recursive:!0,force:!0});let m=p("cp",["-R",a,i]);if(m.status!==0)throw p("hdiutil",["detach","-force",o]),u(i,{recursive:!0,force:!0}),new Error(`copy to ${c} failed: ${m.stderr?.toString()||""}`.trim());let S=p("hdiutil",["detach","-force",o]),h=f(c,`.rnx-previous-${process.pid}.app`);if(g(e)){let w=p("mv",[e,h]);if(w.status!==0)throw u(i,{recursive:!0,force:!0}),new Error(`could not replace ${e}: ${w.stderr?.toString()||""}`.trim())}let T=p("mv",[i,e]);if(T.status!==0)throw g(h)&&p("mv",[h,e]),u(i,{recursive:!0,force:!0}),new Error(`could not move into ${e}: ${T.stderr?.toString()||""}`.trim());return u(h,{recursive:!0,force:!0}),S.status!==0&&console.warn(` warning: failed to unmount ${o} (${S.stderr?.toString().trim()||"unknown error"})`),p("xattr",["-dr","com.apple.quarantine",e]),e}async function z(n){let t=A(process.env.HOME||"","Applications");D(t,{recursive:!0});let r=f(t,"rnx.AppImage");return g(r)&&u(r,{force:!0}),L(n,r),B(n),I(r,493),r}async function H(n){await new Promise((r,s)=>{let o=M("cmd",["/c","start",'""',"/wait",n],{stdio:"inherit"});o.once("error",s),o.once("exit",a=>{a===0?r():s(new Error(`installer exited with code ${a}`))})});let t=v();if(!t)throw new Error("the installer finished but the rnx application was not registered");return t.path}async function X(n){(n.includes("--help")||n.includes("-h"))&&(console.log(`
|
|
6
|
-
rnx desktop install
|
|
7
|
-
|
|
8
|
-
Download and install the optional rnx Desktop app.
|
|
9
|
-
The CLI works without it. Choose Desktop when you want a dedicated native window.
|
|
10
|
-
|
|
11
|
-
usage:
|
|
12
|
-
rnx desktop install [options]
|
|
13
|
-
|
|
14
|
-
options:
|
|
15
|
-
-y, --yes skip confirmation and install immediately
|
|
16
|
-
--force reinstall even if the companion is already present
|
|
17
|
-
|
|
18
|
-
examples:
|
|
19
|
-
rnx desktop install
|
|
20
|
-
rnx desktop install --yes
|
|
21
|
-
`),process.exit(0));let t=n.includes("--yes")||n.includes("-y")||process.env.SOOTSIM_NO_PROMPT==="1"||process.env.CI==="1"||!process.stdin.isTTY,r=n.includes("--force"),s=v();if(s&&!r){console.log(` ${k("\u2713")} Desktop app is already installed at ${s.path}`),console.log(` ${$("Pass --force to reinstall.")}`);return}let o=W();if(o||(console.error(` ${x("\xD7")} No desktop build is available for ${process.platform}/${process.arch}.`),console.error(" Supported: darwin-arm64, darwin-x64, linux-x64, linux-arm64, win32-x64"),process.exit(1)),console.log(` ${y("Desktop app")}`),console.log(` Platform ${o.platform}`),console.log(` Download ${o.url}`),console.log(),!t){if(!await O("Download and install now?",!0)){console.log(` ${$("Cancelled.")}`);return}console.log()}let a=f(F(),`sootsim-install-${Date.now()}`),l=f(a,o.filename),d=0;process.stdout.write(` Downloading ${o.filename}...
|
|
22
|
-
`);try{await Y({url:o.url,dest:l,onProgress:(e,i)=>{let m=Date.now();m-d<100&&e<(i??1/0)||(d=m,process.stdout.isTTY&&process.stdout.write(`\r\x1B[2K${_(e,i)}`))}})}catch(e){u(a,{recursive:!0,force:!0});let i=e instanceof Error?e.message:String(e);console.error(`
|
|
23
|
-
${x("\xD7")} Download failed: ${i}`),console.error(` The ${o.platform} desktop build may not be published yet, or your`),console.error(" network blocked the request. Check your connection and try again later."),console.error(" The CLI and daemon work without the desktop app. Run `rnx --help`."),process.exit(1)}process.stdout.isTTY&&process.stdout.write(`
|
|
24
|
-
`);let c=R(l).size;console.log(` ${k("\u2713")} Downloaded ${(c/(1024*1024)).toFixed(1)} MB`),console.log();try{let e=await E("Installing the desktop app",()=>o.install(l));console.log(` Installed at ${e}`),u(a,{recursive:!0,force:!0})}catch(e){console.error(` ${x("\xD7")} Install failed: ${e instanceof Error?e.message:String(e)}`),process.platform==="darwin"?(p("open",[l]),console.error(` Opened ${o.filename} \u2014 drag rnx to Applications to finish.`)):console.error(` Keeping the download at ${l} for manual installation.`),process.exit(1)}console.log(),console.log(` ${y("Ready")}`),console.log(" rnx desktop Launch the desktop app"),console.log(" rnx open <target> Open a demo or bundle in it")}export{X as a};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/*! rnx v0.1.369 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{a as me,b as qe}from"./chunk-OROUEG22.js";import{e as tt}from"./chunk-X3PEDNLV.js";import{c as Ke}from"./chunk-RYGQ6LZN.js";import{a as Ye,b as Xe,d as nt}from"./chunk-AKJYEX54.js";import{a as Le,c as Ae,d as I,e as ce,f as D,g as de,h as Ne,i as ue,j as Fe,k as je,m as Ge,n as Qe,o as Je}from"./chunk-UDXQCEJU.js";import{m as L}from"./chunk-2APXX4RN.js";import{a as He,b as We}from"./chunk-A56GQVNF.js";import{h as Ze,p as et}from"./chunk-D6MGELKZ.js";import{a as ze}from"./chunk-USHV3QFZ.js";import{a as Ve}from"./chunk-UQJY6O72.js";import{b as se,e as Ee}from"./chunk-F4OTMARL.js";import{b as Me,f as Oe,g as B,h as $,i as _,n as Be,o as ae,p as le,r as _e,s as De,t as R,u as P,v as T}from"./chunk-H2GB2PLY.js";import{f as Te}from"./chunk-IYPMULI7.js";import{a as Re}from"./chunk-A72MIIJI.js";import{D as Ce,M as oe,P as ie,R as O,T as Ue,l as re}from"./chunk-FBMNYSSW.js";import{a as ne}from"./chunk-MXWME7ET.js";import{spawn as lt,spawnSync as ct}from"node:child_process";import E from"node:fs";import Lt from"node:os";import C from"node:path";import{WebSocket as pe}from"ws";function rt(e){return new Error(`could not resolve a native bundle for ${e}. pass an explicit bundle URL or open Connect and choose the app there.`)}function Tt(e,n){return e?.includes("/one/metro-entry.bundle")?"one":typeof n=="string"&&n?"expo":"unknown"}function Et(e,n,t){return`${e}//${n}:${t}`}function Mt(e){if(typeof window>"u")return!1;try{let n=new URL(e);return I(n.hostname)&&I(window.location.hostname)&&n.origin!==window.location.origin}catch{return!1}}async function ot(e,n){let t={...n,cache:n?.cache??"no-store"};return Mt(e)?fetch(`/__fetch-proxy?url=${encodeURIComponent(e)}`,t):fetch(e,t)}function Ot(e){return e==="https:"?443:80}function Bt(e){let n=e.pathname||"/";return(n==="/"||n==="")&&!e.search&&!e.hash}async function it(e){try{let n=await fetch("/__server-scan",{cache:"no-store"});if(!n.ok||!(n.headers.get("content-type")||"").toLowerCase().includes("json"))return null;let t=await n.json();if(!Array.isArray(t))return null;let r=t.find(o=>!!o&&typeof o=="object"&&o.port===e&&typeof o.bundleUrl=="string");return r?{bundleUrl:ue(r.bundleUrl),port:e,framework:typeof r.framework=="string"?r.framework:"unknown",projectName:typeof r.projectName=="string"?r.projectName:void 0}:null}catch{return null}}async function st(e,n){let t=e.replace(/\/+$/,"");try{let r=await ot(`${t}/`,{headers:{"expo-platform":"ios"}});if(r.ok){let o=await r.json(),i=o?.extra?.expoClient||o?.extra||{},a=typeof o?.launchAsset?.url=="string"?o.launchAsset.url:void 0,s=a?Ne(a,t):void 0;if(s||i.name)return{bundleUrl:ue(s||`${t}${de}`),port:n,framework:Tt(s,i.sdkVersion),projectName:i.name}}}catch{}try{let r=await ot(`${t}/status`);if(r.ok&&(await r.text()).includes("packager-status:running"))return{bundleUrl:`${t}${de}`,port:n,framework:"metro"}}catch{}return null}async function _t(e,n){try{if(I(new URL(e).hostname)){let t=await it(n);if(t)return t}}catch{}return st(e,n)}async function Dt(e){let n=await it(e);return n||((await Promise.all(Le.map(r=>st(Ae("http:",r,e),e)))).find(r=>r!==null)??null)}async function at(e){let n=e.trim();if(/^\d+$/.test(n)){let s=parseInt(n,10),l=await Dt(s);if(l)return l;throw rt(`localhost:${s}`)}let t=n.startsWith("http")?n:`http://${n}`,r;try{r=new URL(t)}catch{throw new Error(`could not parse "${e}". pass a dev-server port, a dev-server base URL, or a full bundle URL.`)}let o=r.protocol||"http:",i=r.port?parseInt(r.port,10):Ot(o),a=Et(o,r.hostname,i);if(Bt(r)){let s=await _t(a,i);if(s)return s;throw rt(a)}return{bundleUrl:r.toString(),port:i,framework:"unknown"}}var At=12e4,dt=100,Nt=5e3,ut=1e4,Ft={rn:"/rn",connectrn:"/rn","connect-rn":"/rn",clock:"/app/clock","native-ui":"/app/native-ui",tamagui:"/app/tamagui",settings:"/app/settings",photos:"/app/photos",camera:"/app/camera"};function S(e){return new Promise(n=>setTimeout(n,e))}function mt(e){let n=process.env[e]?.trim();if(!n)return null;let t=Number(n);return Number.isInteger(t)&&t>0?t:null}function jt(){let e=mt("SOOTSIM_DRIVER_CONNECT_TIMEOUT_MS")??mt("SOOTSIM_PW_CONNECT_TIMEOUT_MS")??At;return{timeoutMs:e,hostTimeoutMs:e+Nt,intervalMs:dt,attempts:Math.max(1,Math.ceil(e/dt))}}function pt(e){return e==="electron"||e==="playwright"}function fe(e,n){return n==="playwright"?e.meta?.sootsimHostDriver==="playwright":e.userAgent?.includes("Electron/")===!0}function Ht(e,n){return e.includes("--headless")?!0:e.includes("--headed")?!1:n==="playwright"&&process.platform==="linux"&&!process.env.DISPLAY&&!process.env.WAYLAND_DISPLAY?(console.error(" no display detected \u2192 running playwright headless (override with --headed)"),!0):!1}function z(e){return e.trim()}async function Wt(e,n=!1){let t=ie();ft(e,n,t);let r=ae(e);if((n||r!==null)&&await ge(e,250))return e;if(t&&O(t)&&await ge(t.bridgePort,500))return t.bridgePort;if(t&&O(t))throw new Error(`rnx bridge lockfile is fresh (port ${t.bridgePort}) but the bridge is not reachable. run \`rnx daemon restart\` or \`rnx daemon uninstall\`.`);await Kt();let o=qt(["serve","--quiet","--port",String(e)]),i=null;o.once("exit",(s,l)=>{i=String(s??l??"unknown")}),o.unref();let a=Date.now()+ut;for(;Date.now()<a;){let s=ie();if(ft(e,n,s),s&&O(s)&&await ge(s.bridgePort,250))return console.error(` Started a local bridge on port ${s.bridgePort}. Run \`rnx daemon install\` to keep it ready between commands.`),s.bridgePort;if(i)throw new Error(`rnx bridge exited before becoming ready (exit ${i})`);await S(150)}throw new Error(`rnx bridge did not start within ${Math.round(ut/1e3)}s`)}function ft(e,n,t){if(!(!n||!t||!O(t)||t.bridgePort===e))throw new Error(`${ne.commandName} open: requested bridge port ${e}, but the active daemon owns port ${t.bridgePort}. explicit --port never selects another bridge; stop that daemon or use an isolated SOOTSIM_HOME.`)}async function Kt(){let e=oe();if(e&&E.existsSync(re(e)))return;console.error(" installing rnx engine runtime...");let{runRuntime:n}=await import("./runtime-EKJV2ZVI.js");await n(["install"],{})}function qt(e){let n=process.argv[1];return n&&E.existsSync(n)?lt(process.execPath,[n,...e],{detached:!0,stdio:"ignore",env:process.env}):lt(ne.commandName,e,{detached:!0,stdio:"ignore",env:process.env})}function ge(e,n){return new Promise(t=>{let r=new pe(`ws://127.0.0.1:${e}`,{handshakeTimeout:n}),o=!1,i=a=>{o||(o=!0,r.readyState===pe.OPEN?r.close():r.readyState===pe.CONNECTING&&r.terminate(),t(a))};r.once("open",()=>i(!0)),r.on("error",()=>i(!1)),setTimeout(()=>i(!1),n)})}function we(e){try{let n=new URL(e),t=n.pathname.replace(/\/+$/,"")||"/";return n.searchParams.has("open")||n.searchParams.has("port")||n.searchParams.has("bundle")||n.searchParams.has("demo")||n.pathname.includes("/sootsim/index.html")||t==="/sootsim"||n.pathname==="/__soot"||n.pathname==="/__soot/"||t==="/rn"||/^\/rn\/[^/]+$/i.test(t)||/^\/app\/[^/]+$/i.test(t)||t==="/__soot/rn"||/^\/__soot\/rn\/[^/]+$/i.test(t)||/^\/__soot\/app\/[^/]+$/i.test(t)}catch{return!1}}function Vt(e){try{let n=new URL(e,"http://sootsim.local");if(n.origin!=="http://sootsim.local")return!1;let t=n.pathname.replace(/\/+$/,"")||"/";return t==="/rn"||/^\/rn\/[^/]+$/i.test(t)||/^\/app\/[^/]+$/i.test(t)||t==="/__soot/rn"||/^\/__soot\/rn\/[^/]+$/i.test(t)||/^\/__soot\/app\/[^/]+$/i.test(t)}catch{return!1}}function zt(e,n){let t=new URL(e,"http://sootsim.local"),r=new URL(n),o=G(r.pathname),i=t.pathname.startsWith("/__soot/")?t.pathname.slice(7):t.pathname;return r.pathname=`${o}${i}`,r.search=t.search,r.hash=t.hash,r.toString()}function Gt(e){try{let n=new URL(e).pathname.replace(/\/+$/,"")||"/";return/^\/preview\/[^/]+$/.test(n)||/^\/build\/.+/.test(n)}catch{return!1}}async function or(e){let n=z(e);return(await ve(n)).bundleUrl}function Qt(e){try{let n=new URL(e.startsWith("http")?e:`http://${e}`),t=n.pathname||"/";if(t!=="/"&&t!==""||n.search||n.hash||!I(n.hostname))return null;let r=n.port?Number(n.port):n.protocol==="https:"?443:80;return Number.isFinite(r)&&r>0?r:null}catch{return null}}async function ve(e){let n=z(e),t=/^\d+$/.test(n)?Number(n):Qt(n);if(t&&t>0){let r=await je(t);if(r)return{bundleUrl:r.bundleUrl,port:r.port,framework:r.framework,projectName:r.projectName}}return at(n)}function G(e){let n=e.replace(/\/+$/,"")||"/";return n==="/__soot"||n.startsWith("/__soot/")?"/__soot":""}function Jt(e,n){let t=z(e),r=new URL(n),o=G(r.pathname);return r.pathname=`${o}/rn`,r.searchParams.delete("bundle"),r.searchParams.delete("demo"),r.searchParams.delete("app"),r.searchParams.delete("open"),r.searchParams.delete("port"),/^\d+$/.test(t)?r.pathname=`${o}/rn/${t}`:(r.pathname=`${o}/rn`,r.searchParams.set("open",t)),r.toString()}function yt(e){let n=z(e);return/^\d+$/.test(n)||/^https?:\/\//i.test(n)?!0:/^(localhost|127\.0\.0\.1|\[::1\]|[^/]+\.localhost):\d+(?:\/.*)?$/i.test(n)}async function Yt(e,n,t={}){let r=t.resolvedBundle??await ve(e),o=new URL(n),i=G(o.pathname);o.pathname=`${i}/rn`,o.searchParams.delete("open"),o.searchParams.delete("port"),o.searchParams.delete("demo"),o.searchParams.delete("app");let a=t.hot===void 0?r.bundleUrl:Fe(r.bundleUrl,t.hot);return o.searchParams.set("bundle",a),o.toString()}function Xt(e){return e.startsWith("~/")?C.join(Lt.homedir(),e.slice(2)):C.isAbsolute(e)?e:C.resolve(process.cwd(),e)}function Zt(e){let n={},t={};for(let o=0;o<e.length;o++){if(e[o]==="--replace"){let i=e[o+1];i||(console.error(" rnx open: --replace expects <module>=<file>"),process.exit(1));let a=i.indexOf("=");(a<=0||a===i.length-1)&&(console.error(" rnx open: --replace expects <module>=<file>"),process.exit(1));let s=i.slice(0,a).trim(),l=Xt(i.slice(a+1).trim());E.existsSync(l)||(console.error(` rnx open: replacement file not found: ${l}`),process.exit(1)),n[s]={file:l},o++;continue}if(e[o]==="--remap"){let i=e[o+1],a=i?i.indexOf("="):-1;(!i||a<=0||a===i.length-1)&&(console.error(" rnx open: --remap expects <host:port>=<host:port>"),process.exit(1)),t[i.slice(0,a).trim()]=i.slice(a+1).trim(),o++;continue}}let r={};return Object.keys(n).length>0&&(r.modules=n),Object.keys(t).length>0&&(r.network={remap:t}),r.modules||r.network?r:void 0}async function en(e){if(!e)return null;let n=await Qe(e.bundleUrl);return n?Ve(n):null}async function tn(e){if(E.existsSync(C.join(re(e),"index.html")))return;console.error(` installing configured engine runtime ${e}...`);let n=await Te.install({version:e,setActive:!1,protectVersions:Ue()});console.error(` installed engine runtime ${n.version}`)}function nn(e,n){let t=n??(me()?void 0:oe()??void 0);if(!t)return e;let r=new URL(e);if(!I(r.hostname)){if(!n)return e;throw new Error(`runtimeVersion ${t} requires a local rnx host; got ${r.origin}`)}return r.hostname=qe(t),r.toString()}function St(){return Be().shellBaseUrl}function rn(e){return ae(e)?.shellBaseUrl??Me(e)??St()}async function on(e,n){try{let t=new URL(e),r=t.searchParams.get("bundle")||"";if(!r||!n)return e;let o=Ge({bundleUrl:r,projectDir:n.appDir});if(o.length===0)return e;let i=await sn(o);return i.length===0?e:(t.searchParams.set("appFonts",Je(i)),t.toString())}catch{return e}}async function sn(e){let n=await Promise.all(e.map(async r=>{let o=new AbortController,i=setTimeout(()=>o.abort(),1500);try{let a=await fetch(r.url,{method:"HEAD",signal:o.signal});return a.status>=200&&a.status<400?r:null}catch{return null}finally{clearTimeout(i)}})),t=[];for(let r of n)r!==null&&t.push(r);return t}async function an(e,n,t={}){if(!e)return new URL(n).toString();if(we(e))return new URL(e).toString();if(Vt(e))return zt(e,n);let r=Ft[e.toLowerCase()];if(r){let o=new URL(n),i=G(o.pathname);return o.pathname=`${i}${r}`,o.toString()}return yt(e)?Yt(e,n,t):Jt(e,n)}async function ln(e,n){try{let t=new URL(e),r=t.searchParams.get("bundle")||"";if(!r||!n)return e;let o=Ye({bundleUrl:r,projectDir:n.appDir});return!o||o.imageUrl&&!await cn(o.imageUrl)?e:(t.searchParams.set("appSplash",Xe(o)),t.toString())}catch{return e}}async function cn(e){let n=new AbortController,t=setTimeout(()=>n.abort(),1500);try{let r=await fetch(e,{method:"HEAD",signal:n.signal});return r.status>=200&&r.status<400}catch{return!1}finally{clearTimeout(t)}}async function V(e,n=St(),t={}){let r=await an(e,n,t),o=await ln(await on(r,t.project),t.project);if(!t.device)return o;let i=new URL(o);return i.searchParams.set("device",t.device),i.toString()}function gt(e,n){let t=e?.url||e?.origin||n;try{let r=new URL(t);return r.searchParams.delete("bundle"),r.searchParams.delete("demo"),r.searchParams.delete("app"),r.searchParams.delete("open"),r.searchParams.delete("port"),r.searchParams.delete("inspectOpen"),r.searchParams.delete("appFonts"),r.searchParams.delete("appSplash"),r.toString()}catch{return n}}async function dn(e,n,t,r={}){let o=new URL(await V(e,n,r));return o.searchParams.set("inspectOpen",t),o.toString()}async function un(e,n,t,r={}){let o=r.attempts??30,i=r.intervalMs??500,a=r.minNodeCount??10;for(let s=0;s<o;s++){let l=P(e,{commandTimeoutMs:n,simId:t,simIdSource:"flag"});try{let d=await l.send({type:"evaluate",code:"(async () => (await window.__sootsimTest?.getNodeCount()) || 0)()"});if(typeof d=="number"&&d>a)return{bridge:l,count:d}}catch{}l.close(),await S(i)}return null}function mn(e){if(!e)return null;try{let n=new URL(e);if(n.searchParams.has("bundle")){let t=n.searchParams.get("bundle")||"";try{let r=new URL(t),o=r.pathname.length>36?`...${r.pathname.slice(-36)}`:r.pathname;return`bundle ${r.host}${o}`}catch{return"bundle"}}return n.searchParams.has("port")?`connect :${n.searchParams.get("port")||""}`:n.searchParams.has("open")?`connect ${n.searchParams.get("open")||""}`:n.searchParams.has("demo")?`demo ${n.searchParams.get("demo")||"default"}`:n.pathname.includes("/sootsim/index.html")||n.pathname==="/sootsim/"||n.pathname==="/sootsim"?"embedded rnx":null}catch{return null}}function pn(e){let n=e.indexOf(":");if(n<=0)return e.length>24?`${e.slice(0,21)}\u2026`:e;let t=e.slice(0,n),r=e.slice(n+1),o=r.length>12?`${r.slice(0,8)}\u2026`:r;return t==="TM_SESSION"||t==="CLAUDE_CODE_SESSION_ID"||t==="CODEX_THREAD_ID"?`agent ${o}`:`${t} ${o}`}function ir(e,n){if(e.length===0){console.log(" no sims connected");return}console.log(` connected sims (${e.length}):
|
|
5
|
-
`);for(let t of e){let r=t.lockedBy&&t.lockExpiresAt?`locked by ${pn(t.lockedBy)} (${Math.max(0,Math.round((t.lockExpiresAt-Date.now())/1e3))}s)`:"",o=[t.isPrimary?"primary":"",t.id===n?"selected":"",t.readyState,t.userVisible===!1?"hidden":t.userVisible===!0?"visible":"",t.attachedCliCount&&t.attachedCliCount>0?"in use":"",t.userFocused?"focused":"",r].filter(Boolean);console.log(` ${t.id}${o.length?` [${o.join(", ")}]`:""}`);let i=mn(t.url);if(i)console.log(` loaded: ${i}`);else if(t.url){let s=t.url.length>96?`${t.url.slice(0,93)}\u2026`:t.url;console.log(` url: ${s}`)}else t.origin&&console.log(` origin: ${t.origin}`);let a=fn(t);a&&console.log(` engine: ${a}`),t.title&&console.log(` title: ${t.title}`),t.visibilityState&&t.visibilityState!=="visible"&&console.log(` visibility: ${t.visibilityState}`),console.log(` connected: ${ht(Date.now()-t.connectedAt)}`),t.lastActiveAt&&t.lastActiveAt>0&&console.log(` last active: ${ht(Date.now()-t.lastActiveAt)}`)}}function fn(e){let n=e.url||e.origin||"";if(!n)return null;try{let t=new URL(n).port;return t==="5173"?"dev \u2014 local source (:5173)":t==="3000"?"Contrast app embedded (:3000)":me()?`dev \u2014 local build (:${t||"?"})`:`runtime \u2014 published engine (:${t||"?"})`}catch{return null}}function ht(e){let n=Math.max(0,Math.round(e/1e3));return n<60?`${n}s ago`:n<3600?`${Math.round(n/60)}m ago`:n<86400?`${Math.round(n/3600)}h ago`:`${Math.round(n/86400)}d ago`}async function sr(e,n,t,r={}){let o=r.attempts??30,i=r.intervalMs??500;for(let a=0;a<o;a++){let s=P(e,{commandTimeoutMs:n});try{let d=(await s.listSims()).find(t);if(d)return d}catch{}finally{s.close()}await S(i)}return null}async function gn(e,n,t,r,o={}){let i=o.attempts??30,a=o.intervalMs??500,s=!!r&&Number.isInteger(r)&&r>1,l=()=>s&&r!==void 0&&!be(r);for(let d=0;d<i;d++){if(l())return{kind:"host-exit"};let u=P(e,{commandTimeoutMs:n});try{let g=(await u.listSims()).find(t);if(g)return l()?{kind:"host-exit"}:{kind:"match",sim:g}}catch{}finally{u.close()}if(l())return{kind:"host-exit"};await S(a)}return{kind:"timeout"}}async function hn(e,n,t,r={}){let o=r.attempts??20,i=r.intervalMs??250;for(let a=0;a<o;a++){let s=P(e,{commandTimeoutMs:n});try{let d=(await s.listSims()).find(u=>u.id===t);if(!d||d.readyState!=="open")return!0}catch{return!0}finally{s.close()}await S(i)}return!1}function wn(e){let n=e.meta;if(!n||n.sootsimHostDriver!=="playwright")return null;let t=Number(n.sootsimHostPid);return!Number.isInteger(t)||t<=1||t===process.pid?null:t}function bn(e){if(!e.connectAckFile)return!1;try{return E.writeFileSync(e.connectAckFile,`${JSON.stringify({connectedAt:Date.now(),pid:e.pid??null})}
|
|
6
|
-
`,{flag:"w"}),!0}catch{return!1}}function wt(e){if(e.diagnosticLogPath)try{let n=E.readFileSync(e.diagnosticLogPath,"utf8").trim();if(!n){console.error(` ${C.basename(e.diagnosticLogPath)} was empty`);return}let t=n.length>4e3?`\u2026
|
|
7
|
-
${n.slice(-4e3)}`:n;console.error(` ${C.basename(e.diagnosticLogPath)}:`),console.error(t)}catch(n){let t=n instanceof Error?n.message:String(n);console.error(` failed to read ${C.basename(e.diagnosticLogPath)}: ${t}`)}}function be(e){try{return process.kill(e,0),!0}catch(n){return n?.code==="EPERM"}}async function vn(e,n=2500){let t=Date.now()+n;for(;Date.now()<t;){if(!be(e))return!0;await S(100)}return!be(e)}function yn(e){let n=ct("ps",["-o","command=","-p",String(e)],{encoding:"utf8",stdio:["ignore","pipe","pipe"]}),t=ct("ps",["eww","-o","command=","-p",String(e)],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});if(n.status!==0||t.status!==0)return!1;let r=(n.stdout||"").trimEnd(),o=(t.stdout||"").trimEnd();return(o.startsWith(r)?o.slice(r.length):o).includes("SOOTSIM_PW_URL=")}async function he(e,n){let t=new Set(n),r=new Set;for(let o of e){if(!t.has(o.id))continue;let i=wn(o);i&&r.add(i)}for(let o of r){if(!yn(o)){console.log(` skipped pid ${o}: not a rnx playwright host (stale sim record)`);continue}try{process.kill(o,"SIGTERM")}catch{}if(await vn(o)){console.log(` closed playwright host process ${o}`);continue}try{process.kill(o,"SIGKILL"),console.log(` force-closed playwright host process ${o}`)}catch{}}}function Q(e,n){if(n){let o=n.trim(),i=e.find(a=>a.id===o);if(!i)throw new Error(`no sim connected with id ${o}`);return i}let t=e.find(o=>o.isPrimary&&o.readyState==="open");if(t)return t;let r=e.find(o=>o.readyState==="open");if(r)return r;throw new Error("no sim connected")}function bt(e,n,t,r=!1){let[o,i]=[e,n.url].map(a=>{if(!a)return a;try{let s=new URL(a);if(s.searchParams.has("sootsimConfig"))return s.searchParams.set("sootsimConfig","[redacted]"),s.toString()}catch{}return a});if(r){console.log(` Opened ConnectRN [${t}]`);return}console.log(` ${t==="current sim"?"loaded":"opened"}: ${o} [${t}]`),console.log(` current sim: ${n.id}`),console.log(JSON.stringify({simId:n.id,url:i},null,2))}async function vt(e,n,t){if(t.includes("--no-describe"))return;let r=process.env.SOOTSIM_QUIET_TARGET_NOTICE;process.env.SOOTSIM_QUIET_TARGET_NOTICE="1";try{await Sn(e,n,{stableMs:150,maxMs:400});let o=P(e,{commandTimeoutMs:3e3,simId:n,cliLabel:"open --describe",simIdSource:"flag"});try{let i=null;try{i=await et(o,1)}catch{}if(i&&!i.ready){let s=i.loadingText?`still showing "${i.loadingText}"`:i.flag!==!0?"guest ready event has not fired":i.targets<=0?"no visible app content is inspectable yet":"app tree is still stabilizing";console.log(` app still loading: ${s} (nodes: ${i.nodes}, targets: ${i.targets})`),console.log(" before interacting, run: rnx wait ready --max-ms 120000")}let a=await Ze(o,{describe:!0,verbose:!1,filter:"",compact:!0,hideXy:!1,maxDepth:12});if(!a?.tree)return;console.log(""),console.log(a.tree)}finally{o.close()}}catch{}finally{r===void 0?delete process.env.SOOTSIM_QUIET_TARGET_NOTICE:process.env.SOOTSIM_QUIET_TARGET_NOTICE=r}}async function Sn(e,n,t){let r=Date.now()+t.maxMs,o="(async () => (await window.__sootsimTest?.getNodeCount?.()) || 0)()",i=P(e,{commandTimeoutMs:2e3,simId:n,simIdSource:"flag"});try{let a=-1,s=0;for(;Date.now()<r;){let l=-1;try{let d=await i.send({type:"evaluate",code:o});typeof d=="number"&&(l=d)}catch{}if(l>=0&&l===a){if(Date.now()-s>=t.stableMs)return}else a=l,s=Date.now();await S(50)}}finally{i.close()}}async function xn(e,n={}){let t=R(e,{port:n.port,commandTimeoutMs:n.timeoutMs});t.positional.length!==1&&(console.error(" rnx open: expected exactly one <url-or-route>"),console.error(" examples: rnx open /settings | rnx open myapp://settings"),process.exit(1));let r=T(t),o=t.simId?` --sim ${t.simId}`:"";try{let i=await We(r,t.positional[0]??"");console.log(` opened deep link: ${i}`)}catch(i){console.error(` link failed: ${i instanceof Error?i.message:String(i)}`),await L(r,{errorsCommand:`rnx get errors 5${o}`,warningsCommand:`rnx get warnings 5${o}`,requestsCommand:`rnx get requests 5${o}`}),process.exit(1)}finally{r.close()}}async function ar(e,n={}){let t=R(e,{port:n.port,commandTimeoutMs:n.timeoutMs,stripBooleanFlags:["--new","--headless","--headed","--ephemeral","--hot","--no-hmr","--no-describe","--quiet"],stripValueFlags:["--base-url","--replace","--driver","--profile","--cdp-port","--device","--viewport"]}),r=e.find((p,m)=>e[m-1]==="--profile"),o=e.includes("--quiet"),i=e.includes("--ephemeral"),a=e.find((p,m)=>e[m-1]==="--cdp-port"),s=a?Number(a):void 0;a&&(!Number.isFinite(s)||s<=0)&&(console.error(` rnx open: --cdp-port must be a positive port number, got "${a}"`),process.exit(1)),r&&i&&(console.error(" rnx open: --profile cannot be combined with --ephemeral"),process.exit(1));let l=r?Ke(r).id:void 0,d=e.includes("--new")||!!l||i,u=Zt(e);d&&t.simIdSource==="flag"&&(console.error(" rnx open: --new, --profile, and --ephemeral cannot be combined with --sim"),process.exit(1));let c=t.positional[0]||"";if(He(c)){d&&(console.error(" rnx open: deep links target the current app; remove --new, --profile, or --ephemeral"),process.exit(1)),await xn(e,n);return}if(Gt(c)){let p=(()=>{try{return I(new URL(c).hostname)}catch{return!1}})();console.log(" that\u2019s an rnx preview/build link \u2014 a full player page, not a driveable sim."),console.log(" opening it in your browser for viewing\u2026");try{await nt(c,{background:!1})}catch(m){console.error(` could not launch a browser: ${m instanceof Error?m.message:String(m)}`),console.error(` open it yourself: ${c}`),process.exit(1)}console.log(p?" to drive it under the CLI, point `rnx open` at the app\u2019s dev port instead (e.g. `rnx open 8081`).":" the CLI can\u2019t drive a remote preview (the engine only attaches to the local bridge from localhost). to drive the app, run it locally and `rnx open <port>`.");return}let g=yt(c)?await ve(c):void 0,A=await en(g),J=await ze(A),N=J?.runtimeVersion,F=ce(ce(J?{...J,runtimeVersion:void 0}:void 0,n.runtimeConfig),u);N&&await tn(N);let Y=e.find((p,m)=>e[m-1]==="--base-url");if(Y&&!t.explicitPort){let p=le(Y);p&&(t.wsPort=p)}t.wsPort=await Wt(t.wsPort,t.explicitPort);let M=e.find((p,m)=>e[m-1]==="--driver")||"";M&&!pt(M)&&(console.error(` unknown driver "${M}" \u2014 run \`rnx list --drivers\``),process.exit(1));let b=pt(M)?M:process.env.CI==="1"||process.env.CI==="true"||process.env.GITHUB_ACTIONS==="true"||Re()||!process.stdin.isTTY?"playwright":Ce()||"playwright",$t=Ht(e,b);s&&b!=="playwright"&&(console.error(" rnx open: --cdp-port is only honored by the playwright driver"),process.exit(1));let ye=e.includes("--hot"),Se=e.includes("--no-hmr");ye&&Se&&(console.error(" rnx open: --hot and --no-hmr cannot be combined"),process.exit(1));let Pt=ye?!0:Se?!1:b!=="playwright",k=e.find((p,m)=>e[m-1]==="--device");k&&!(k in se)&&(console.error(` rnx open: unknown device "${k}"`),console.error(` known devices: ${Ee().join(", ")}`),process.exit(1));let X=k?se[k]:void 0,j=e.find((p,m)=>e[m-1]==="--viewport"),H=j?/^(\d+)x(\d+)$/.exec(j):null;j&&!H&&(console.error(` rnx open: --viewport must be <width>x<height>, got "${j}"`),process.exit(1));let It=H?{width:Number(H[1]),height:Number(H[2])}:void 0,W={hot:Pt,device:k,resolvedBundle:g,project:A},kt=N?`http://localhost:${t.wsPort}/`:Y||rn(t.wsPort),x=nn(kt,N),xe=e.includes("--base-url");_e(t.wsPort,x);let Ct=B();if(!d){let p=T(t),m=t.simId?` --sim ${t.simId}`:"",te=!1;try{let f=null,q=null;try{let h=await p.listSims();if(t.simIdSource==="saved")f=h.find(w=>w.id===Ct&&w.readyState==="open"&&fe(w,b))??null,f||_();else if(t.simIdSource==="flag"&&(f=Q(h,t.simId),!fe(f,b)))throw new Error(`sim ${f.id} is not managed by the ${b} driver`);if(!f&&t.simIdSource!=="flag"){q=await V(c,D(x,F),W);let w=new URL(q);w.searchParams.delete("inspectOpen"),f=h.find(y=>{if(y.readyState!=="open"||!y.url||!fe(y,b))return!1;try{let ke=new URL(y.url);return ke.searchParams.delete("inspectOpen"),ke.toString()===w.toString()}catch{return!1}})??null}if(!f)if(t.simIdSource!=="flag")te=!0;else throw new Error("no sim connected");if(!te&&f){let w=xe||we(c)?x:gt(f,x),y=q??await V(c,D(w,F),W);p.send({type:"evaluate",simId:f.id,code:`window.location.href = ${JSON.stringify(y)}`}).catch(()=>{})}}catch(h){console.error(` open failed: ${h instanceof Error?h.message:String(h)}`),await L(p,{errorsCommand:`rnx get errors 5${m}`,warningsCommand:`rnx get warnings 5${m}`,requestsCommand:`rnx get requests 5${m}`}),process.exit(1)}if(!te&&f){await S(1500);let h=await un(t.wsPort,t.commandTimeoutMs,f.id);h||(console.error(" timed out waiting for current sim to load target"),process.exit(1)),h.bridge.close(),$(f.id);let w=xe||we(c)?x:gt(f,x),y=q??await V(c,D(w,F),W);return bt(y,{...f,url:y},"current sim",o),await vt(t.wsPort,f.id,e),t.wsPort}}finally{p.close()}}let Ut=D(x,F),$e=`cli-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`,Pe=await dn(c,Ut,$e,W),Ie=Oe();Ie||(console.error(" could not resolve a stable owning process; refusing unsupervised sim host"),process.exit(1));let Rt=p=>p.url?.includes(`inspectOpen=${$e}`)??!1,U=tt(b);U||(console.error(` unknown driver "${b}" \u2014 run \`rnx list --drivers\``),process.exit(1));let K=jt(),v=await U.launch({url:Pe,bridgePort:le(x)??t.wsPort,headless:$t,profileId:l,ephemeralProfile:i,cdpPort:s,device:k,ownerPid:Ie,viewport:It??(X?{width:X.width+120,height:X.height+60}:void 0),connectTimeoutMs:K.hostTimeoutMs});v.launched||(console.error(` ${U.name} driver: ${v.message}`),process.exit(1));let Z=await gn(t.wsPort,t.commandTimeoutMs,Rt,v.pid,{attempts:K.attempts,intervalMs:K.intervalMs});if(Z.kind==="host-exit"&&(wt(v),console.error(` ${U.name} host crashed \u2014 sim is no longer attached`),process.exit(1)),Z.kind==="timeout"){if(v.pid)try{process.kill(v.pid,"SIGTERM"),console.error(` closed ${U.name} host process ${v.pid}`)}catch{}wt(v),console.error(` timed out after ${K.timeoutMs}ms waiting for opened sim to connect`),process.exit(1)}let ee=Z.sim;return $(ee.id),bn(v),bt(Pe,ee,`${U.name} driver`,o),await vt(t.wsPort,ee.id,e),t.wsPort}async function xt(e,n={}){let t=R(e,{port:n.port,commandTimeoutMs:n.timeoutMs,stripBooleanFlags:["--force"]}),r=e.includes("--force"),o=T(t),i=t.simId?` --sim ${t.simId}`:"";try{try{let a=await o.listSims(),s=Q(a,t.positional[0]||t.simId);if(s.lockedBy&&s.lockExpiresAt&&s.lockExpiresAt>Date.now()){let l=Math.max(0,Math.round((s.lockExpiresAt-Date.now())/1e3));s.lockedByKind==="user-active"&&(console.error(` refused: ${s.id} is locked by the active user (${l}s) \u2014 that's a live human tab`),console.error(" run `rnx open --new` for a fresh investigation sim"),process.exit(1)),r||(console.error(` refused: ${s.id} is leased by ${s.lockedBy} (${l}s)`),console.error(` \`sootsim use ${s.id} --force\` to take it, or \`rnx open --new\` for a fresh sim`),process.exit(1))}await o.focusSim(s.id),$(s.id),console.log(` using: ${s.id}`)}catch(a){console.error(` use failed: ${a instanceof Error?a.message:String(a)}`),await L(o,{errorsCommand:`rnx get errors 5${i}`,warningsCommand:`rnx get warnings 5${i}`,requestsCommand:`rnx get requests 5${i}`}),process.exit(1)}}finally{o.close()}}async function lr(e,n={}){await xt(e,n)}async function cr(e,n={}){await xt(e,n)}async function dr(e,n={}){let t=R(e,{port:n.port,commandTimeoutMs:n.timeoutMs,stripBooleanFlags:["--force"]}),r=e.includes("--force"),o=T(t);try{try{let i=await o.listSims(),a=Q(i,t.positional[0]||t.simId),s=r&&a.lockedBy&&a.lockedByKind!=="user-active"?a.lockedBy:null,l=await o.claim(a.id,{force:r});$(a.id);let d=Math.max(0,Math.round((l.lockExpiresAt-Date.now())/1e3)),u=l.bootedCount>0?` (booted ${l.bootedCount})`:"";console.log(` claimed: ${l.simId} [${d}s]${u}`),s&&console.log(` took over from: ${s}`)}catch(i){if(i instanceof De){let a=Math.max(0,Math.round(i.lock.expiresInMs/1e3));console.error(` claim failed: locked by ${i.lock.by} for ${a}s more`),console.error(" use --force to take it, or `rnx open --new` for a fresh sim"),process.exit(1)}console.error(` claim failed: ${i instanceof Error?i.message:String(i)}`),process.exit(1)}}finally{o.close()}}async function $n(e,n,t,r){if(r.length===0)return{closed:[],remaining:[]};await Promise.all(r.map(i=>e.closeSim(i).catch(()=>{})));let o=new Set(r);for(let i=0;i<40;i++){let a=[];try{let s=P(n,{commandTimeoutMs:t});try{a=(await s.listSims()).filter(d=>o.has(d.id)&&d.readyState==="open").map(d=>d.id)}finally{s.close()}}catch{a=[]}if(a.length===0)return{closed:[...o],remaining:[]};if(i===39)return{closed:[...o].filter(s=>!a.includes(s)),remaining:a};await S(250)}return{closed:[...o],remaining:[]}}function Pn(e,n){let t=e.filter(c=>c.readyState==="open"),r=t.map(c=>c.id),o=n.explicitKeepId?.trim()||"",i=n.savedKeepId?.trim()||"",a=i?t.some(c=>c.id===i):!1,s=i&&!a?i:null,l=null,d=null,u=null;if(n.closeOthers)if(o){let c=t.find(g=>g.id===o);c?l=c.id:d=o}else if(i&&a)l=i;else{let c=t.find(g=>g.isPrimary)??t[0]??null;c&&(l=c.id,i&&!a&&(u=c.id))}return{openIds:r,keepId:l,targets:d?[]:r.filter(c=>c!==l),staleSavedId:s,missingExplicitKeepId:d,fallbackKeepId:u}}async function ur(e,n={}){let t=R(e,{port:n.port,commandTimeoutMs:n.timeoutMs,stripBooleanFlags:["--all","--others"]}),r=T(t),o=t.simId?` --sim ${t.simId}`:"",i=e.includes("--all"),a=e.includes("--others");if(i||a){try{let s=await r.listSims(),l=B(),d=t.positional[0]||(t.simIdSource==="flag"?t.simId:void 0),u=Pn(s,{closeOthers:a,explicitKeepId:d,savedKeepId:l});if(u.staleSavedId&&_(),u.fallbackKeepId&&($(u.fallbackKeepId),console.log(` saved sim ${u.staleSavedId} is gone \u2014 keeping primary ${u.fallbackKeepId}`)),u.missingExplicitKeepId&&(console.error(` close failed: keep sim ${u.missingExplicitKeepId} is not connected; not closing other sims`),process.exit(1)),u.targets.length===0){console.log(u.keepId?` nothing to close \u2014 only the kept sim ${u.keepId} is connected`:u.staleSavedId?` nothing to close \u2014 no sims connected (cleared stale current sim ${u.staleSavedId})`:" nothing to close \u2014 no sims connected");return}let c=await $n(r,t.wsPort,t.commandTimeoutMs,u.targets);await he(s,u.targets);let g=B();g&&c.closed.includes(g)&&(u.keepId?$(u.keepId):_());let A=` closed ${c.closed.length} sim(s)${u.keepId?` (kept ${u.keepId})`:""}`;console.log(A),c.remaining.length>0&&(console.error(` close failed: still connected: ${c.remaining.join(", ")}`),process.exit(1))}catch(s){console.error(` close failed: ${s instanceof Error?s.message:String(s)}`),process.exit(1)}finally{r.close()}return}try{try{let s=await r.listSims(),l=Q(s,t.positional[0]||t.simId),d=s.find(c=>c.id!==l.id&&c.readyState==="open");await r.closeSim(l.id),await hn(t.wsPort,t.commandTimeoutMs,l.id)||(await he(s,[l.id]),console.error(` close failed: ${l.id} is still connected`),process.exit(1)),await he(s,[l.id]),B()===l.id&&(d?$(d.id):_()),console.log(` closed: ${l.id}`)}catch(s){console.error(` close failed: ${s instanceof Error?s.message:String(s)}`),await L(r,{errorsCommand:`rnx get errors 5${o}`,warningsCommand:`rnx get warnings 5${o}`,requestsCommand:`rnx get requests 5${o}`}),process.exit(1)}}finally{r.close()}}export{jt as a,Wt as b,or as c,nn as d,St as e,rn as f,V as g,dn as h,mn as i,ir as j,sr as k,gn as l,wn as m,bn as n,he as o,xn as p,ar as q,lr as r,cr as s,dr as t,$n as u,Pn as v,ur as w};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/*! rnx v0.1.369 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{a as i}from"./chunk-ZW6JXEVX.js";var c=["ghs_","ghp_","gho_","ghu_","github_pat_"];function p(n){return!!(n&&n.length>=20&&c.some(e=>n.startsWith(e)))}function a(){let n=process.env.CONTRAST_INSTALLATION_TOKEN?.trim(),e=process.env.GITHUB_TOKEN?.trim(),t=n||e;if(!p(t))return null;let r=(process.env.CONTRAST_REPO||process.env.GITHUB_REPOSITORY||"").trim();if(!r)return null;let[o,s]=r.includes("/")?r.split("/",2):[null,null],u=(process.env.CONTRAST_REPO_ID||process.env.GITHUB_REPOSITORY_ID||"").trim(),l=(process.env.CONTRAST_INSTALLATION_ID||process.env.GITHUB_APP_INSTALLATION_ID||"").trim();return{kind:"github",token:t,repoId:r,repositoryId:u||null,owner:o||null,repo:s||null,installationId:l||null,source:n?"contrast-runner":"github-actions"}}function d(n){return n.startsWith("sk_rnx_")||n.startsWith("sk_sootsim_")}function I(){for(let n of["RNX_API_KEY","SOOTSIM_API_KEY"]){let e=process.env[n]?.trim();if(e&&d(e))return{secret:e,envName:n}}return null}function _(){let n=I();if(n)return{kind:"api-key",secret:n.secret,source:"env",envName:n.envName};let e=a();if(e)return e;let t=i();return t?.token?{kind:"session",token:t.token,origin:t.origin}:null}function A(n){return n.kind==="api-key"?`Bearer ${n.secret}`:`Bearer ${n.token}`}function h(n){return!n||n.kind!=="github"?null:{repoId:n.repoId,owner:n.owner??void 0,repo:n.repo??void 0,installationId:n.installationId}}export{_ as a,A as b,h as c};
|