rnxsim 0.1.370 → 0.1.372
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/bin.ts +2 -2
- package/cli/commands/box/checkout-plane.ts +65 -14
- 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/control.ts +6 -1
- package/cli/outbound-endpoints.ts +37 -1
- package/dist-cli/bin.js +5 -5
- package/dist-cli/chunks/{agent-3R4424LW.js → agent-RTAKJ5UM.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-4PPWZWSY.js → agent-wrapper-JWRDI26D.js} +2 -2
- package/dist-cli/chunks/{app-fonts-KYXB6CXQ.js → app-fonts-RKO3N4PB.js} +2 -2
- package/dist-cli/chunks/{assert-7EZOWGYQ.js → assert-Y5LXPWC6.js} +2 -2
- package/dist-cli/chunks/{auth-L7M36H6C.js → auth-4SPTRWTM.js} +2 -2
- package/dist-cli/chunks/{beta-FKAF6YWJ.js → beta-KYS22QPY.js} +2 -2
- package/dist-cli/chunks/{box-5Y5VBMHV.js → box-7CK72ZPF.js} +617 -596
- package/dist-cli/chunks/{camera-XHF2RPFG.js → camera-RUL426MQ.js} +2 -2
- package/dist-cli/chunks/{chunk-EQ3OE2AV.js → chunk-2MA3Q44D.js} +1 -1
- package/dist-cli/chunks/{chunk-DHYTRCIP.js → chunk-2OA2WE7G.js} +2 -2
- package/dist-cli/chunks/{chunk-DMHSD6YJ.js → chunk-3C6LTIDV.js} +2 -2
- package/dist-cli/chunks/{chunk-NR2V2ONN.js → chunk-4UMODU6C.js} +2 -2
- package/dist-cli/chunks/{chunk-Z2VBSNFI.js → chunk-5Q6ZEZFF.js} +1 -1
- package/dist-cli/chunks/{chunk-LUOHRBJG.js → chunk-7FYOZQBM.js} +1 -1
- package/dist-cli/chunks/{chunk-Q4MRJ6PO.js → chunk-BRRWRBW7.js} +2 -2
- package/dist-cli/chunks/chunk-CEQXZ3GA.js +11 -0
- package/dist-cli/chunks/{chunk-DUXHYPBP.js → chunk-CZ2FOZVC.js} +2 -2
- package/dist-cli/chunks/{chunk-CMVRTZ5G.js → chunk-D5AT7UT5.js} +2 -2
- package/dist-cli/chunks/{chunk-D2GZASRV.js → chunk-DQOQQFMP.js} +1 -1
- package/dist-cli/chunks/{chunk-3UXXYJVD.js → chunk-EKSSAC4L.js} +1 -1
- package/dist-cli/chunks/{chunk-5BR33NAZ.js → chunk-EY5OHL2M.js} +1 -1
- package/dist-cli/chunks/{chunk-FK4JSTZ3.js → chunk-FOW2LPM7.js} +2 -2
- package/dist-cli/chunks/{chunk-WFQHT4QD.js → chunk-G5G7PQKO.js} +2 -2
- package/dist-cli/chunks/{chunk-WPV4H5S4.js → chunk-GPYG2L7R.js} +2 -2
- package/dist-cli/chunks/{chunk-3SOPYG7S.js → chunk-H2SKPUSI.js} +2 -2
- package/dist-cli/chunks/{chunk-6A6L3VJR.js → chunk-H567W7JT.js} +2 -2
- package/dist-cli/chunks/{chunk-PAUEZO6F.js → chunk-HDGZAVH3.js} +2 -2
- package/dist-cli/chunks/{chunk-AALKIAUZ.js → chunk-I37LRPZ3.js} +1 -1
- package/dist-cli/chunks/{chunk-JZLDHPB7.js → chunk-I7LRVN5M.js} +2 -2
- package/dist-cli/chunks/{chunk-3BG6UCQY.js → chunk-IX25FFF3.js} +11 -11
- package/dist-cli/chunks/{chunk-72S5FCBY.js → chunk-J2TJRPDB.js} +2 -2
- package/dist-cli/chunks/{chunk-RRX52O37.js → chunk-JPRHJMUJ.js} +1 -1
- package/dist-cli/chunks/{chunk-BMIJZNKD.js → chunk-K4TGUDY4.js} +2 -2
- package/dist-cli/chunks/{chunk-7C7TJYZ3.js → chunk-KSE5XGYU.js} +2 -2
- package/dist-cli/chunks/{chunk-OJZHECGH.js → chunk-KVVNCIJA.js} +1 -1
- package/dist-cli/chunks/{chunk-LXPHL6FO.js → chunk-KWLUOLYQ.js} +2 -2
- package/dist-cli/chunks/{chunk-C7BA5CCM.js → chunk-KXAE64HF.js} +2 -2
- package/dist-cli/chunks/{chunk-EWSWOWXK.js → chunk-L5BEWXQZ.js} +2 -2
- package/dist-cli/chunks/{chunk-D24EOJ4C.js → chunk-LAQKTHRM.js} +2 -2
- package/dist-cli/chunks/{chunk-IDLFVRPN.js → chunk-LWLHVF6Y.js} +1 -1
- package/dist-cli/chunks/{chunk-EOIBLKZO.js → chunk-MKHW3JNL.js} +2 -2
- package/dist-cli/chunks/{chunk-64JAEJ6N.js → chunk-MSM2E74F.js} +1 -1
- package/dist-cli/chunks/{chunk-DXV37CJN.js → chunk-NBEOA72C.js} +1 -1
- package/dist-cli/chunks/{chunk-H5X5FJ63.js → chunk-NLMDMNO6.js} +2 -2
- package/dist-cli/chunks/{chunk-6W4HIWC7.js → chunk-NT43X5LI.js} +2 -2
- package/dist-cli/chunks/{chunk-PNOENVMJ.js → chunk-NWPYM54M.js} +1 -1
- package/dist-cli/chunks/{chunk-O6QLKSAM.js → chunk-OLHHB6LD.js} +2 -2
- package/dist-cli/chunks/{chunk-MDCY532E.js → chunk-P6K6XFWE.js} +2 -2
- package/dist-cli/chunks/{chunk-LIMWG54N.js → chunk-PETP6LVI.js} +1 -1
- package/dist-cli/chunks/{chunk-YIZGNT7A.js → chunk-PKY76S57.js} +1 -1
- package/dist-cli/chunks/chunk-POR7R3TP.js +4 -0
- package/dist-cli/chunks/chunk-PUH6RQBJ.js +7 -0
- package/dist-cli/chunks/{chunk-NJMU5BN6.js → chunk-Q5WPPOFV.js} +1 -1
- package/dist-cli/chunks/{chunk-HQQTS2CB.js → chunk-Q7JSU5DI.js} +3 -3
- package/dist-cli/chunks/{chunk-U63T6LIQ.js → chunk-QA2PTSOC.js} +1 -1
- package/dist-cli/chunks/{chunk-2FOGXVOK.js → chunk-R7HMFLXI.js} +2 -2
- package/dist-cli/chunks/{chunk-FA75F34Z.js → chunk-RXKZJAGY.js} +1 -1
- package/dist-cli/chunks/{chunk-5RKRVS5A.js → chunk-SC7PGULZ.js} +2 -2
- package/dist-cli/chunks/{chunk-UFVAAWGZ.js → chunk-SLTEYNAE.js} +1 -1
- package/dist-cli/chunks/{chunk-6GPS7U4X.js → chunk-SSQCKJQZ.js} +2 -2
- package/dist-cli/chunks/{chunk-ARMXC3C3.js → chunk-SZX6ARJX.js} +3 -3
- package/dist-cli/chunks/{chunk-6S5LJV2O.js → chunk-U3XYGLXG.js} +2 -2
- package/dist-cli/chunks/chunk-UA5WQCBU.js +7 -0
- package/dist-cli/chunks/{chunk-VVQCZ42C.js → chunk-UVKX6FQ4.js} +1 -1
- package/dist-cli/chunks/{chunk-Z3DENGXW.js → chunk-VQQXUXXF.js} +2 -2
- package/dist-cli/chunks/{chunk-I52NT6VG.js → chunk-WR7LRCYW.js} +1 -1
- package/dist-cli/chunks/{chunk-HEKHLQ3B.js → chunk-XA4YU3A4.js} +3 -3
- package/dist-cli/chunks/{chunk-GRO74J2X.js → chunk-YEGWC2OY.js} +1 -1
- package/dist-cli/chunks/{chunk-AVJBT5LF.js → chunk-YWTBN7SD.js} +1 -1
- package/dist-cli/chunks/{chunk-PIZXOXBL.js → chunk-ZRQKEKHX.js} +2 -2
- package/dist-cli/chunks/{chunk-X6WQ4YWE.js → chunk-ZTZTRD5R.js} +1 -1
- package/dist-cli/chunks/{cleanup-MXMMJ7JW.js → cleanup-7ATBNZHC.js} +2 -2
- package/dist-cli/chunks/cli-version-D2IDQO7X.js +4 -0
- package/dist-cli/chunks/{compat-E6KE5KDI.js → compat-4ZC6ZMMA.js} +2 -2
- package/dist-cli/chunks/{config-QSLLVZDS.js → config-EOBODQ32.js} +2 -2
- package/dist-cli/chunks/{connect-QE4VU6GB.js → connect-ZQ72CZ7O.js} +2 -2
- package/dist-cli/chunks/control-FKPKLOE2.js +4 -0
- package/dist-cli/chunks/daemon-WFHWS3JU.js +4 -0
- package/dist-cli/chunks/{debug-5FG3MBJS.js → debug-7Z325GNU.js} +3 -3
- package/dist-cli/chunks/{desktop-5EZX7NX5.js → desktop-CR7ALX7H.js} +3 -3
- package/dist-cli/chunks/{detox-WJGQP5TM.js → detox-RZ37736M.js} +2 -2
- package/dist-cli/chunks/{device-D7LKCAMS.js → device-TA3SQP6K.js} +2 -2
- package/dist-cli/chunks/{diagnose-A3W6ZTGA.js → diagnose-JFKFKR3S.js} +2 -2
- package/dist-cli/chunks/disk-cleanup-HZM7VM4S.js +4 -0
- package/dist-cli/chunks/{do-chain-RUJNRPPF.js → do-chain-MTT65YNX.js} +2 -2
- package/dist-cli/chunks/drivers-BXEXWRVJ.js +4 -0
- package/dist-cli/chunks/{env-U67SQVTV.js → env-SFI24PLS.js} +2 -2
- package/dist-cli/chunks/{film-XC6UAQ3V.js → film-GPHSJLIH.js} +3 -3
- package/dist-cli/chunks/flow-FBCU3TZN.js +4 -0
- package/dist-cli/chunks/help-6MLROWYO.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-7S2TNZXG.js → hidden-runtime-alias-7Z6PYH53.js} +2 -2
- package/dist-cli/chunks/{home-paths-LMO5LB4F.js → home-paths-MEGVA6ET.js} +2 -2
- package/dist-cli/chunks/inspect-EWT5SXBD.js +4 -0
- package/dist-cli/chunks/install-desktop-IFBUH7MH.js +4 -0
- package/dist-cli/chunks/{login-JIU42S4C.js → login-EHUZYRAM.js} +2 -2
- package/dist-cli/chunks/{logout-JTAWQXZO.js → logout-WPGT62N6.js} +2 -2
- package/dist-cli/chunks/{maestro-TYBQYOGB.js → maestro-4HVDLCTI.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-YLAGWVKE.js → maestro-generate-VBS2EZMM.js} +3 -3
- package/dist-cli/chunks/{mode-DV24ZRZ6.js → mode-MGTS4QTF.js} +2 -2
- package/dist-cli/chunks/{optional-demo-registry-JZH66VIM.js → optional-demo-registry-F3FQT5WT.js} +2 -2
- package/dist-cli/chunks/{perf-LXXJ4EAX.js → perf-47R3OEI7.js} +2 -2
- package/dist-cli/chunks/{permissions-KAOVZUPW.js → permissions-ZOLFZWR2.js} +2 -2
- package/dist-cli/chunks/{platform-KW35446W.js → platform-C4E26ZBZ.js} +3 -3
- package/dist-cli/chunks/privacy-WXGGG4ZI.js +4 -0
- package/dist-cli/chunks/{record-EK3ETFHZ.js → record-OUXWCWUF.js} +3 -3
- package/dist-cli/chunks/{report-issue-NW5PCAZF.js → report-issue-LJJXONXQ.js} +2 -2
- package/dist-cli/chunks/reset-ZZYLPETS.js +4 -0
- package/dist-cli/chunks/resolve-target-MVZZR2JW.js +4 -0
- package/dist-cli/chunks/runtime-DEC5AF3M.js +4 -0
- package/dist-cli/chunks/{screenshot-command-TQSPTFT2.js → screenshot-command-CUHDOS5D.js} +3 -3
- package/dist-cli/chunks/{screenshot-layers-IZP3L3AQ.js → screenshot-layers-DVWQVKT6.js} +2 -2
- package/dist-cli/chunks/{screenshots-capture-K7LJOVAE.js → screenshots-capture-GWIUJ3A6.js} +2 -2
- package/dist-cli/chunks/{serve-MMX4WXXG.js → serve-TX5OKGJM.js} +19 -19
- package/dist-cli/chunks/setup-X7LLGW67.js +4 -0
- package/dist-cli/chunks/{sim-client-KLAYIFOC.js → sim-client-W4WZOZ3P.js} +2 -2
- package/dist-cli/chunks/{skills-N4D3POIS.js → skills-TDFQAZ6A.js} +2 -2
- package/dist-cli/chunks/{state-ES6TFQZF.js → state-P2Z25OLQ.js} +2 -2
- package/dist-cli/chunks/{storage-52DR5V2C.js → storage-DWEOFXEB.js} +2 -2
- package/dist-cli/chunks/store-TKBLMYCV.js +4 -0
- package/dist-cli/chunks/telemetry-E3CBKJMD.js +4 -0
- package/dist-cli/chunks/{timeline-NRVNB5JR.js → timeline-55XNA4GX.js} +2 -2
- package/dist-cli/chunks/{upgrade-LNED5YQ2.js → upgrade-3XRCVDVC.js} +2 -2
- package/dist-cli/chunks/upload-QB77GSMQ.js +4 -0
- package/dist-cli/chunks/{version-VEF2DYGG.js → version-WTYGLXBJ.js} +2 -2
- package/dist-cli/chunks/{web-CGDE6S73.js → web-ZWRY3VHM.js} +2 -2
- package/dist-cli/chunks/welcome-7ATM5EGX.js +5 -0
- package/dist-cli/chunks/{what-happened-FSIDWL32.js → what-happened-Q3IGIH4K.js} +2 -2
- package/dist-cli/chunks/{ws-bridge-QXFQLRE7.js → ws-bridge-KCPZSBBX.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 +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +149 -93
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +679 -307
- 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/disk-cleanup.ts +44 -0
- package/src/host/bridge-host.ts +11 -0
- package/src/host/plane-host.ts +203 -0
- package/dist-cli/chunks/chunk-GJRLPJQL.js +0 -6
- package/dist-cli/chunks/chunk-NTC46LSA.js +0 -4
- package/dist-cli/chunks/chunk-OTFTMGAL.js +0 -7
- package/dist-cli/chunks/cli-version-MLS55UTY.js +0 -4
- package/dist-cli/chunks/control-XD5F6K5T.js +0 -4
- package/dist-cli/chunks/daemon-UN5SE2JO.js +0 -4
- package/dist-cli/chunks/disk-cleanup-BDRHX5ZG.js +0 -4
- package/dist-cli/chunks/drivers-3NJ5RL6X.js +0 -4
- package/dist-cli/chunks/flow-FHZZD3P6.js +0 -4
- package/dist-cli/chunks/help-BYYDUSXF.js +0 -4
- package/dist-cli/chunks/inspect-RQCTWDCC.js +0 -4
- package/dist-cli/chunks/install-desktop-GOWXCQUE.js +0 -4
- package/dist-cli/chunks/privacy-MIFE72CG.js +0 -4
- package/dist-cli/chunks/reset-RZ4O5AFJ.js +0 -4
- package/dist-cli/chunks/resolve-target-7R5BNYMM.js +0 -4
- package/dist-cli/chunks/runtime-MZHLSRS6.js +0 -4
- package/dist-cli/chunks/setup-YFP3KEQZ.js +0 -4
- package/dist-cli/chunks/store-HOL45WCI.js +0 -4
- package/dist-cli/chunks/telemetry-BV6OTYPF.js +0 -4
- package/dist-cli/chunks/upload-RXKHFIBU.js +0 -4
- package/dist-cli/chunks/welcome-FT2AMUWW.js +0 -5
|
@@ -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/disk-cleanup.ts
CHANGED
|
@@ -8,6 +8,17 @@ const AUTOMATIC_CLEANUP_GENERATION = 1
|
|
|
8
8
|
const AUTOMATIC_CLEANUP_COMPLETE_FILE = `automatic-cleanup-v${AUTOMATIC_CLEANUP_GENERATION}.json`
|
|
9
9
|
const AUTOMATIC_CLEANUP_LOCK_FILE = `automatic-cleanup-v${AUTOMATIC_CLEANUP_GENERATION}.lock`
|
|
10
10
|
const AUTOMATIC_CLEANUP_LOCK_STALE_MS = 6 * 60 * 60 * 1000
|
|
11
|
+
// a deferral is recorded so the retry does not happen on literally every
|
|
12
|
+
// command. cleanup defers whenever anything is in use, and on a machine that
|
|
13
|
+
// always has a sim or daemon running that is the permanent state: the complete
|
|
14
|
+
// marker is never written, so every rnx invocation spawned a detached worker
|
|
15
|
+
// that rescanned the home directory, reclaimed nothing, and printed the same
|
|
16
|
+
// "N is in use and will be retried on a later run" line into the caller's
|
|
17
|
+
// stderr. measured on this machine: an identical 662.2 MiB across every
|
|
18
|
+
// invocation over several hours. the retry itself is right, its cadence was
|
|
19
|
+
// not, so it is throttled rather than removed.
|
|
20
|
+
const AUTOMATIC_CLEANUP_DEFERRED_FILE = `automatic-cleanup-v${AUTOMATIC_CLEANUP_GENERATION}.deferred.json`
|
|
21
|
+
const AUTOMATIC_CLEANUP_DEFER_RETRY_MS = 30 * 60 * 1000
|
|
11
22
|
|
|
12
23
|
export type SootsimCleanupKind =
|
|
13
24
|
| 'browser-cache'
|
|
@@ -65,6 +76,26 @@ export function isAutomaticSootsimCleanupComplete(): boolean {
|
|
|
65
76
|
}
|
|
66
77
|
}
|
|
67
78
|
|
|
79
|
+
// what the CLI asks before spawning a worker: is there anything for one to do
|
|
80
|
+
// right now. complete means never again; a recent deferral means not yet.
|
|
81
|
+
export function shouldSkipAutomaticSootsimCleanup(now = Date.now()): boolean {
|
|
82
|
+
if (isAutomaticSootsimCleanupComplete()) return true
|
|
83
|
+
const deferredPath = path.join(sootsimHomeDir(), AUTOMATIC_CLEANUP_DEFERRED_FILE)
|
|
84
|
+
try {
|
|
85
|
+
const parsed = JSON.parse(fs.readFileSync(deferredPath, 'utf8')) as {
|
|
86
|
+
generation?: unknown
|
|
87
|
+
attemptedAt?: unknown
|
|
88
|
+
}
|
|
89
|
+
if (parsed.generation !== AUTOMATIC_CLEANUP_GENERATION) return false
|
|
90
|
+
if (typeof parsed.attemptedAt !== 'number') return false
|
|
91
|
+
// a clock that moved backwards must not park cleanup forever
|
|
92
|
+
const age = now - parsed.attemptedAt
|
|
93
|
+
return age >= 0 && age < AUTOMATIC_CLEANUP_DEFER_RETRY_MS
|
|
94
|
+
} catch {
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
68
99
|
export function formatSootsimBytes(bytes: number): string {
|
|
69
100
|
if (bytes < 1024) return `${bytes} B`
|
|
70
101
|
const units = ['KiB', 'MiB', 'GiB', 'TiB']
|
|
@@ -358,6 +389,7 @@ export function applyAutomaticSootsimCleanup(): AutomaticSootsimCleanupResult {
|
|
|
358
389
|
ensureSootsimHome()
|
|
359
390
|
const home = sootsimHomeDir()
|
|
360
391
|
const completePath = path.join(home, AUTOMATIC_CLEANUP_COMPLETE_FILE)
|
|
392
|
+
const deferredPath = path.join(home, AUTOMATIC_CLEANUP_DEFERRED_FILE)
|
|
361
393
|
const lockPath = path.join(home, AUTOMATIC_CLEANUP_LOCK_FILE)
|
|
362
394
|
|
|
363
395
|
if (isAutomaticSootsimCleanupComplete()) {
|
|
@@ -462,6 +494,17 @@ export function applyAutomaticSootsimCleanup(): AutomaticSootsimCleanupResult {
|
|
|
462
494
|
0,
|
|
463
495
|
)
|
|
464
496
|
if (deferredInUseBytes > 0) {
|
|
497
|
+
const deferredTmp = `${deferredPath}.${process.pid}.tmp`
|
|
498
|
+
fs.writeFileSync(
|
|
499
|
+
deferredTmp,
|
|
500
|
+
`${JSON.stringify({
|
|
501
|
+
generation: AUTOMATIC_CLEANUP_GENERATION,
|
|
502
|
+
attemptedAt: Date.now(),
|
|
503
|
+
deferredInUseBytes,
|
|
504
|
+
})}\n`,
|
|
505
|
+
'utf8',
|
|
506
|
+
)
|
|
507
|
+
fs.renameSync(deferredTmp, deferredPath)
|
|
465
508
|
return {
|
|
466
509
|
status: 'deferred',
|
|
467
510
|
reclaimedBytes: result.reclaimedBytes,
|
|
@@ -481,6 +524,7 @@ export function applyAutomaticSootsimCleanup(): AutomaticSootsimCleanupResult {
|
|
|
481
524
|
'utf8',
|
|
482
525
|
)
|
|
483
526
|
fs.renameSync(tmp, completePath)
|
|
527
|
+
fs.rmSync(deferredPath, { force: true })
|
|
484
528
|
return {
|
|
485
529
|
status: 'complete',
|
|
486
530
|
reclaimedBytes: result.reclaimedBytes,
|
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
|
+
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/*! rnx v0.1.370 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{b as C}from"./chunk-FA75F34Z.js";import{f as h}from"./chunk-DHYTRCIP.js";import{L as B,T as b,g as S}from"./chunk-U63T6LIQ.js";import o from"node:fs";import c from"node:path";var g=1,k=`automatic-cleanup-v${g}.json`,x=`automatic-cleanup-v${g}.lock`,A=360*60*1e3;function w(){let i=c.join(S(),k);try{return JSON.parse(o.readFileSync(i,"utf8")).generation===g}catch{return!1}}function $(i){if(i<1024)return`${i} B`;let e=["KiB","MiB","GiB","TiB"],n=i/1024,r=e[0];for(let s=1;s<e.length&&n>=1024;s++)n/=1024,r=e[s];return`${n.toFixed(n>=10?1:2)} ${r}`}function p(i){let e;try{e=o.lstatSync(i)}catch{return 0}let n=e.blocks>0?e.blocks*512:e.size;if(!e.isDirectory()||e.isSymbolicLink())return n;let r=n;try{for(let s of o.readdirSync(i))r+=p(c.join(i,s))}catch{}return r}function E(i){for(let e of["SingletonLock","SingletonSocket","lockfile"])try{if(o.lstatSync(c.join(i,e)))return!0}catch{}return!1}function v(i,e){let n;try{n=o.readdirSync(i,{withFileTypes:!0})}catch{return[]}let r=E(e),s=[];for(let t of n){if(!t.isDirectory()||t.isSymbolicLink())continue;let a=c.join(i,t.name);if(C(t.name)){s.push({path:a,bytes:p(a),kind:"browser-cache",state:r?"in-use":"safe",selected:!r,detail:r?"browser profile is open; close it before cleanup":"disposable browser HTTP, bytecode, or GPU cache"});continue}s.push(...v(a,e))}return s}function P(i){let e=c.join(i,"profiles","playwright"),n;try{n=o.readdirSync(e,{withFileTypes:!0})}catch{return[]}return n.flatMap(r=>{if(!r.isDirectory()||r.isSymbolicLink())return[];let s=c.join(e,r.name);return v(s,s)})}function L(i){let e=c.join(i,"electron","userData");return v(e,e)}function R(i,e){let n=c.join(i,"recordings"),r;try{r=o.readdirSync(n,{withFileTypes:!0})}catch{return[]}return r.map(s=>{let t=c.join(n,s.name);return{path:t,bytes:p(t),kind:"recording",state:"aggressive",selected:e,detail:"user-created recording or captured-frame output"}})}function T(i,e){let n=c.join(i,"camera-fixtures"),r;try{r=o.readdirSync(n,{withFileTypes:!0})}catch{return[]}return r.map(s=>{let t=c.join(n,s.name);return{path:t,bytes:p(t),kind:"camera-fixture",state:"aggressive",selected:e,detail:"staged camera fixture video"}})}function U(i){let e=new Set(b()),n=[...e],r=h.planLocalRetention({retainVersions:2,protectVersions:n}),s=h.planLocalRetention({retainVersions:1,protectVersions:n}),t=new Set(r.removeVersions),a=new Set(s.removeVersions),l=new Set([...r.keepVersions,...r.removeVersions,...s.keepVersions,...s.removeVersions]),m=[];for(let u of[...l].sort()){let d=h.product.runtimeDir(u),y="retained",f="retained runtime";e.has(u)&&u!==r.activeVersion?(y="in-use",f="runtime is serving a connected simulator"):t.has(u)?(y="safe",f="inactive runtime outside the two-version rollback window"):a.has(u)?(y="aggressive",f="most recent rollback runtime"):u===r.activeVersion&&(f="active runtime; always protected"),m.push({path:d,bytes:p(d),kind:"runtime-version",state:y,selected:y==="safe"||y==="aggressive"&&i,detail:f})}for(let u of r.removeCacheFiles)m.push({path:u,bytes:p(u),kind:"runtime-archive",state:"safe",selected:!0,detail:"downloaded archive is unnecessary after successful extraction"});return{activeRuntime:r.activeVersion,entries:m}}function D(i={}){let e=i.aggressive===!0,n=S(),r=U(e),s=[...r.entries,...P(n),...L(n),...R(n,e),...T(n,e)].sort((t,a)=>a.bytes-t.bytes||t.path.localeCompare(a.path));return{home:n,activeRuntime:r.activeRuntime,entries:s,reclaimableBytes:s.reduce((t,a)=>t+(a.selected?a.bytes:0),0),protectedBytes:s.reduce((t,a)=>t+(a.selected?0:a.bytes),0),totalBytes:p(n)}}function I(i,e){let n=c.relative(i,e);if(!n||n.startsWith("..")||c.isAbsolute(n))throw new Error(`refusing cleanup target outside the rnx data home: ${e}`)}function V(i={}){let e=D(i),n=i.aggressive===!0?1:2;h.pruneLocalRetention({retainVersions:n,protectVersions:b()});for(let t of e.entries)if(t.selected&&!(t.kind==="runtime-archive"||t.kind==="runtime-version")){if(t.kind==="browser-cache"){let l=c.relative(e.home,t.path).split(c.sep),m=l[0]==="profiles"&&l[1]==="playwright"&&l[2]?c.join(e.home,"profiles","playwright",l[2]):l[0]==="electron"&&l[1]==="userData"?c.join(e.home,"electron","userData"):null;if(m&&E(m)){t.state="in-use",t.selected=!1,t.detail="browser profile opened while cleanup was starting";continue}}I(e.home,t.path),o.rmSync(t.path,{recursive:!0,force:!0})}let r=e.entries.filter(t=>t.selected&&!o.existsSync(t.path)).map(t=>t.path),s=p(e.home);return{...e,removedPaths:r,reclaimedBytes:Math.max(0,e.totalBytes-s),remainingBytes:s}}function M(){B();let i=S(),e=c.join(i,k),n=c.join(i,x);if(w())return{status:"already-complete",reclaimedBytes:0,remainingBytes:0,deferredInUseBytes:0};let r=null;for(let s=0;s<2&&r===null;s++)try{let t=o.openSync(n,"wx");try{o.writeFileSync(t,`${JSON.stringify({pid:process.pid,startedAt:Date.now()})}
|
|
5
|
-
`,"utf8"),r=t}catch(a){o.closeSync(t);try{o.unlinkSync(n)}catch{}throw a}}catch(t){if((t instanceof Error&&"code"in t?t.code:null)!=="EEXIST")throw t;let l=0,m=0,u=null;try{u=o.lstatSync(n);let d=JSON.parse(o.readFileSync(n,"utf8"));l=typeof d.pid=="number"?d.pid:0,m=typeof d.startedAt=="number"?d.startedAt:0}catch{}if(l>0&&m>0&&Date.now()-m<A)try{return process.kill(l,0),{status:"busy",reclaimedBytes:0,remainingBytes:0,deferredInUseBytes:0}}catch{}if(!u)continue;try{let d=o.lstatSync(n);if(d.dev!==u.dev||d.ino!==u.ino)return{status:"busy",reclaimedBytes:0,remainingBytes:0,deferredInUseBytes:0};o.unlinkSync(n)}catch{}}if(r===null)return{status:"busy",reclaimedBytes:0,remainingBytes:0,deferredInUseBytes:0};try{if(w())return{status:"already-complete",reclaimedBytes:0,remainingBytes:0,deferredInUseBytes:0};let s=V(),t=s.entries.reduce((l,m)=>l+(m.state==="in-use"?m.bytes:0),0);if(t>0)return{status:"deferred",reclaimedBytes:s.reclaimedBytes,remainingBytes:s.remainingBytes,deferredInUseBytes:t};let a=`${e}.${process.pid}.tmp`;return o.writeFileSync(a,`${JSON.stringify({generation:g,completedAt:Date.now(),reclaimedBytes:s.reclaimedBytes})}
|
|
6
|
-
`,"utf8"),o.renameSync(a,e),{status:"complete",reclaimedBytes:s.reclaimedBytes,remainingBytes:s.remainingBytes,deferredInUseBytes:0}}finally{o.closeSync(r);try{o.unlinkSync(n)}catch{}}}export{w as a,$ as b,D as c,V as d,M as e};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/*! rnx v0.1.370 | (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-6W4HIWC7.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 h(){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 T(n){return n.kind==="api-key"?`Bearer ${n.secret}`:`Bearer ${n.token}`}function k(n){return!n||n.kind!=="github"?null:{repoId:n.repoId,owner:n.owner??void 0,repo:n.repo??void 0,installationId:n.installationId}}export{h as a,T as b,k as c};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/*! rnx v0.1.370 | (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 fe,b as ze}from"./chunk-6S5LJV2O.js";import{e as rt}from"./chunk-DMHSD6YJ.js";import{c as Ve}from"./chunk-PAUEZO6F.js";import{a as Ze,b as et,d as ot}from"./chunk-6A6L3VJR.js";import{a as Ne,c as Fe,d as k,e as ue,f as L,g as me,h as je,i as pe,j as He,k as We,m as Je,n as Ye,o as Xe}from"./chunk-3BG6UCQY.js";import{m as A}from"./chunk-NR2V2ONN.js";import{a as Ke,b as qe}from"./chunk-D2GZASRV.js";import{h as tt,p as nt}from"./chunk-AALKIAUZ.js";import{a as Qe}from"./chunk-WFQHT4QD.js";import{a as Ge}from"./chunk-EQ3OE2AV.js";import{b as le,e as Oe}from"./chunk-CMVRTZ5G.js";import{b as Be,f as _e,g as _,h as P,i as D,n as De,o as ce,p as de,r as Le,s as Ae,t as E,u as I,v as T}from"./chunk-EOIBLKZO.js";import{b as z,c as u}from"./chunk-AVJBT5LF.js";import{f as Me}from"./chunk-DHYTRCIP.js";import{a as Te}from"./chunk-IDLFVRPN.js";import{D as Re,M as se,P as ae,R as B,T as Ee,l as ie}from"./chunk-U63T6LIQ.js";import{a as oe}from"./chunk-64JAEJ6N.js";import{spawn as dt,spawnSync as ut}from"node:child_process";import M from"node:fs";import Nt from"node:os";import U from"node:path";import{WebSocket as ge}from"ws";function it(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 Mt(e,n){return e?.includes("/one/metro-entry.bundle")?"one":typeof n=="string"&&n?"expo":"unknown"}function Ot(e,n,t){return`${e}//${n}:${t}`}function Bt(e){if(typeof window>"u")return!1;try{let n=new URL(e);return k(n.hostname)&&k(window.location.hostname)&&n.origin!==window.location.origin}catch{return!1}}async function st(e,n){let t={...n,cache:n?.cache??"no-store"};return Bt(e)?fetch(`/__fetch-proxy?url=${encodeURIComponent(e)}`,t):fetch(e,t)}function _t(e){return e==="https:"?443:80}function Dt(e){let n=e.pathname||"/";return(n==="/"||n==="")&&!e.search&&!e.hash}async function at(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:pe(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 lt(e,n){let t=e.replace(/\/+$/,"");try{let r=await st(`${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?je(a,t):void 0;if(s||i.name)return{bundleUrl:pe(s||`${t}${me}`),port:n,framework:Mt(s,i.sdkVersion),projectName:i.name}}}catch{}try{let r=await st(`${t}/status`);if(r.ok&&(await r.text()).includes("packager-status:running"))return{bundleUrl:`${t}${me}`,port:n,framework:"metro"}}catch{}return null}async function Lt(e,n){try{if(k(new URL(e).hostname)){let t=await at(n);if(t)return t}}catch{}return lt(e,n)}async function At(e){let n=await at(e);return n||((await Promise.all(Ne.map(r=>lt(Fe("http:",r,e),e)))).find(r=>r!==null)??null)}async function ct(e){let n=e.trim();if(/^\d+$/.test(n)){let s=parseInt(n,10),l=await At(s);if(l)return l;throw it(`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):_t(o),a=Ot(o,r.hostname,i);if(Dt(r)){let s=await Lt(a,i);if(s)return s;throw it(a)}return{bundleUrl:r.toString(),port:i,framework:"unknown"}}var Ft=12e4,mt=100,jt=5e3,pt=1e4,Ht={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 $(e){return new Promise(n=>setTimeout(n,e))}function ft(e){let n=process.env[e]?.trim();if(!n)return null;let t=Number(n);return Number.isInteger(t)&&t>0?t:null}function Wt(){let e=ft("SOOTSIM_DRIVER_CONNECT_TIMEOUT_MS")??ft("SOOTSIM_PW_CONNECT_TIMEOUT_MS")??Ft;return{timeoutMs:e,hostTimeoutMs:e+jt,intervalMs:mt,attempts:Math.max(1,Math.ceil(e/mt))}}function gt(e){return e==="electron"||e==="playwright"}function he(e,n){return n==="playwright"?e.meta?.sootsimHostDriver==="playwright":e.userAgent?.includes("Electron/")===!0}function Kt(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 Q(e){return e.trim()}async function qt(e,n=!1){let t=ae();ht(e,n,t);let r=ce(e);if((n||r!==null)&&await we(e,250))return e;if(t&&B(t)&&await we(t.bridgePort,500))return t.bridgePort;if(t&&B(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 Vt();let o=zt(["serve","--quiet","--port",String(e)]),i=null;o.once("exit",(s,l)=>{i=String(s??l??"unknown")}),o.unref();let a=Date.now()+pt;for(;Date.now()<a;){let s=ae();if(ht(e,n,s),s&&B(s)&&await we(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 $(150)}throw new Error(`rnx bridge did not start within ${Math.round(pt/1e3)}s`)}function ht(e,n,t){if(!(!n||!t||!B(t)||t.bridgePort===e))throw new Error(`${oe.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 Vt(){let e=se();if(e&&M.existsSync(ie(e)))return;console.error(" installing rnx engine runtime...");let{runRuntime:n}=await import("./runtime-MZHLSRS6.js");await n(["install"],{})}function zt(e){let n=process.argv[1];return n&&M.existsSync(n)?dt(process.execPath,[n,...e],{detached:!0,stdio:"ignore",env:process.env}):dt(oe.commandName,e,{detached:!0,stdio:"ignore",env:process.env})}function we(e,n){return new Promise(t=>{let r=new ge(`ws://127.0.0.1:${e}`,{handshakeTimeout:n}),o=!1,i=a=>{o||(o=!0,r.readyState===ge.OPEN?r.close():r.readyState===ge.CONNECTING&&r.terminate(),t(a))};r.once("open",()=>i(!0)),r.on("error",()=>i(!1)),setTimeout(()=>i(!1),n)})}function ve(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 Gt(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 Qt(e,n){let t=new URL(e,"http://sootsim.local"),r=new URL(n),o=J(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 Jt(e){try{let n=new URL(e).pathname.replace(/\/+$/,"")||"/";return/^\/preview\/[^/]+$/.test(n)||/^\/build\/.+/.test(n)}catch{return!1}}async function ar(e){let n=Q(e);return(await Se(n)).bundleUrl}function Yt(e){try{let n=new URL(e.startsWith("http")?e:`http://${e}`),t=n.pathname||"/";if(t!=="/"&&t!==""||n.search||n.hash||!k(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 Se(e){let n=Q(e),t=/^\d+$/.test(n)?Number(n):Yt(n);if(t&&t>0){let r=await We(t);if(r)return{bundleUrl:r.bundleUrl,port:r.port,framework:r.framework,projectName:r.projectName}}return ct(n)}function J(e){let n=e.replace(/\/+$/,"")||"/";return n==="/__soot"||n.startsWith("/__soot/")?"/__soot":""}function Xt(e,n){let t=Q(e),r=new URL(n),o=J(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 $t(e){let n=Q(e);return/^\d+$/.test(n)||/^https?:\/\//i.test(n)?!0:/^(localhost|127\.0\.0\.1|\[::1\]|[^/]+\.localhost):\d+(?:\/.*)?$/i.test(n)}async function Zt(e,n,t={}){let r=t.resolvedBundle??await Se(e),o=new URL(n),i=J(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:He(r.bundleUrl,t.hot);return o.searchParams.set("bundle",a),o.toString()}function en(e){return e.startsWith("~/")?U.join(Nt.homedir(),e.slice(2)):U.isAbsolute(e)?e:U.resolve(process.cwd(),e)}function tn(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>"),u(1));let a=i.indexOf("=");(a<=0||a===i.length-1)&&(console.error(" rnx open: --replace expects <module>=<file>"),u(1));let s=i.slice(0,a).trim(),l=en(i.slice(a+1).trim());M.existsSync(l)||(console.error(` rnx open: replacement file not found: ${l}`),u(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>"),u(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 nn(e){if(!e)return null;let n=await Ye(e.bundleUrl);return n?Ge(n):null}async function rn(e){if(M.existsSync(U.join(ie(e),"index.html")))return;console.error(` installing configured engine runtime ${e}...`);let n=await Me.install({version:e,setActive:!1,protectVersions:Ee()});console.error(` installed engine runtime ${n.version}`)}function on(e,n){let t=n??(fe()?void 0:se()??void 0);if(!t)return e;let r=new URL(e);if(!k(r.hostname)){if(!n)return e;throw new Error(`runtimeVersion ${t} requires a local rnx host; got ${r.origin}`)}return r.hostname=ze(t),r.toString()}function xt(){return De().shellBaseUrl}function sn(e){return ce(e)?.shellBaseUrl??Be(e)??xt()}async function an(e,n){try{let t=new URL(e),r=t.searchParams.get("bundle")||"";if(!r||!n)return e;let o=Je({bundleUrl:r,projectDir:n.appDir});if(o.length===0)return e;let i=await ln(o);return i.length===0?e:(t.searchParams.set("appFonts",Xe(i)),t.toString())}catch{return e}}async function ln(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 cn(e,n,t={}){if(!e)return new URL(n).toString();if(ve(e))return new URL(e).toString();if(Gt(e))return Qt(e,n);let r=Ht[e.toLowerCase()];if(r){let o=new URL(n),i=J(o.pathname);return o.pathname=`${i}${r}`,o.toString()}return $t(e)?Zt(e,n,t):Xt(e,n)}async function dn(e,n){try{let t=new URL(e),r=t.searchParams.get("bundle")||"";if(!r||!n)return e;let o=Ze({bundleUrl:r,projectDir:n.appDir});return!o||o.imageUrl&&!await un(o.imageUrl)?e:(t.searchParams.set("appSplash",et(o)),t.toString())}catch{return e}}async function un(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 G(e,n=xt(),t={}){let r=await cn(e,n,t),o=await dn(await an(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 wt(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 mn(e,n,t,r={}){let o=new URL(await G(e,n,r));return o.searchParams.set("inspectOpen",t),o.toString()}async function pn(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=I(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 $(i)}return null}function fn(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 gn(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 lr(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 ${gn(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=fn(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=hn(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: ${bt(Date.now()-t.connectedAt)}`),t.lastActiveAt&&t.lastActiveAt>0&&console.log(` last active: ${bt(Date.now()-t.lastActiveAt)}`)}}function hn(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)":fe()?`dev \u2014 local build (:${t||"?"})`:`runtime \u2014 published engine (:${t||"?"})`}catch{return null}}function bt(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 cr(e,n,t,r={}){let o=r.attempts??30,i=r.intervalMs??500;for(let a=0;a<o;a++){let s=I(e,{commandTimeoutMs:n});try{let d=(await s.listSims()).find(t);if(d)return d}catch{}finally{s.close()}await $(i)}return null}async function wn(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&&!ye(r);for(let d=0;d<i;d++){if(l())return{kind:"host-exit"};let m=I(e,{commandTimeoutMs:n});try{let h=(await m.listSims()).find(t);if(h)return l()?{kind:"host-exit"}:{kind:"match",sim:h}}catch{}finally{m.close()}if(l())return{kind:"host-exit"};await $(a)}return{kind:"timeout"}}async function bn(e,n,t,r={}){let o=r.attempts??20,i=r.intervalMs??250;for(let a=0;a<o;a++){let s=I(e,{commandTimeoutMs:n});try{let d=(await s.listSims()).find(m=>m.id===t);if(!d||d.readyState!=="open")return!0}catch{return!0}finally{s.close()}await $(i)}return!1}function vn(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 yn(e){if(!e.connectAckFile)return!1;try{return M.writeFileSync(e.connectAckFile,`${JSON.stringify({connectedAt:Date.now(),pid:e.pid??null})}
|
|
6
|
-
`,{flag:"w"}),!0}catch{return!1}}function vt(e){if(e.diagnosticLogPath)try{let n=M.readFileSync(e.diagnosticLogPath,"utf8").trim();if(!n){console.error(` ${U.basename(e.diagnosticLogPath)} was empty`);return}let t=n.length>4e3?`\u2026
|
|
7
|
-
${n.slice(-4e3)}`:n;console.error(` ${U.basename(e.diagnosticLogPath)}:`),console.error(t)}catch(n){let t=n instanceof Error?n.message:String(n);console.error(` failed to read ${U.basename(e.diagnosticLogPath)}: ${t}`)}}function ye(e){try{return process.kill(e,0),!0}catch(n){return n?.code==="EPERM"}}async function Sn(e,n=2500){let t=Date.now()+n;for(;Date.now()<t;){if(!ye(e))return!0;await $(100)}return!ye(e)}function $n(e){let n=ut("ps",["-o","command=","-p",String(e)],{encoding:"utf8",stdio:["ignore","pipe","pipe"]}),t=ut("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 be(e,n){let t=new Set(n),r=new Set;for(let o of e){if(!t.has(o.id))continue;let i=vn(o);i&&r.add(i)}for(let o of r){if(!$n(o)){console.log(` skipped pid ${o}: not a rnx playwright host (stale sim record)`);continue}try{process.kill(o,"SIGTERM")}catch{}if(await Sn(o)){console.log(` closed playwright host process ${o}`);continue}try{process.kill(o,"SIGKILL"),console.log(` force-closed playwright host process ${o}`)}catch{}}}function Y(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 yt(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 St(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 xn(e,n,{stableMs:150,maxMs:400});let o=I(e,{commandTimeoutMs:3e3,simId:n,cliLabel:"open --describe",simIdSource:"flag"});try{let i=null;try{i=await nt(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 tt(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 xn(e,n,t){let r=Date.now()+t.maxMs,o="(async () => (await window.__sootsimTest?.getNodeCount?.()) || 0)()",i=I(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 $(50)}}finally{i.close()}}async function Pn(e,n={}){let t=E(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"),u(1));let r=T(t),o=t.simId?` --sim ${t.simId}`:"";try{let i=await qe(r,t.positional[0]??"");console.log(` opened deep link: ${i}`)}catch(i){console.error(` link failed: ${i instanceof Error?i.message:String(i)}`),await A(r,{errorsCommand:`rnx get errors 5${o}`,warningsCommand:`rnx get warnings 5${o}`,requestsCommand:`rnx get requests 5${o}`}),u(1)}finally{r.close()}}async function dr(e,n={}){let t=E(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((f,p)=>e[p-1]==="--profile"),o=e.includes("--quiet"),i=e.includes("--ephemeral"),a=e.find((f,p)=>e[p-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}"`),u(1)),r&&i&&(console.error(" rnx open: --profile cannot be combined with --ephemeral"),u(1));let l=r?Ve(r).id:void 0,d=e.includes("--new")||!!l||i,m=tn(e);d&&t.simIdSource==="flag"&&(console.error(" rnx open: --new, --profile, and --ephemeral cannot be combined with --sim"),u(1));let c=t.positional[0]||"";if(Ke(c)){d&&(console.error(" rnx open: deep links target the current app; remove --new, --profile, or --ephemeral"),u(1)),await Pn(e,n);return}if(Jt(c)){let f=(()=>{try{return k(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 ot(c,{background:!1})}catch(p){console.error(` could not launch a browser: ${p instanceof Error?p.message:String(p)}`),console.error(` open it yourself: ${c}`),u(1)}console.log(f?" 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 h=$t(c)?await Se(c):void 0,N=await nn(h),X=await Qe(N),F=X?.runtimeVersion,j=ue(ue(X?{...X,runtimeVersion:void 0}:void 0,n.runtimeConfig),m);F&&await rn(F);let Z=e.find((f,p)=>e[p-1]==="--base-url");if(Z&&!t.explicitPort){let f=de(Z);f&&(t.wsPort=f)}t.wsPort=await qt(t.wsPort,t.explicitPort);let O=e.find((f,p)=>e[p-1]==="--driver")||"";O&&!gt(O)&&(console.error(` unknown driver "${O}" \u2014 run \`rnx list --drivers\``),u(1));let v=gt(O)?O:process.env.CI==="1"||process.env.CI==="true"||process.env.GITHUB_ACTIONS==="true"||Te()||!process.stdin.isTTY?"playwright":Re()||"playwright",It=Kt(e,v);s&&v!=="playwright"&&(console.error(" rnx open: --cdp-port is only honored by the playwright driver"),u(1));let $e=e.includes("--hot"),xe=e.includes("--no-hmr");$e&&xe&&(console.error(" rnx open: --hot and --no-hmr cannot be combined"),u(1));let kt=$e?!0:xe?!1:v!=="playwright",C=e.find((f,p)=>e[p-1]==="--device");C&&!(C in le)&&(console.error(` rnx open: unknown device "${C}"`),console.error(` known devices: ${Oe().join(", ")}`),u(1));let ee=C?le[C]:void 0,H=e.find((f,p)=>e[p-1]==="--viewport"),W=H?/^(\d+)x(\d+)$/.exec(H):null;H&&!W&&(console.error(` rnx open: --viewport must be <width>x<height>, got "${H}"`),u(1));let Ct=W?{width:Number(W[1]),height:Number(W[2])}:void 0,K={hot:kt,device:C,resolvedBundle:h,project:N},Ut=F?`http://localhost:${t.wsPort}/`:Z||sn(t.wsPort),x=on(Ut,F),Pe=e.includes("--base-url");Le(t.wsPort,x);let Rt=_();if(!d){let f=T(t),p=t.simId?` --sim ${t.simId}`:"",re=!1;try{let g=null,V=null;try{let w=await f.listSims();if(t.simIdSource==="saved")g=w.find(b=>b.id===Rt&&b.readyState==="open"&&he(b,v))??null,g||D();else if(t.simIdSource==="flag"&&(g=Y(w,t.simId),!he(g,v)))throw new Error(`sim ${g.id} is not managed by the ${v} driver`);if(!g&&t.simIdSource!=="flag"){V=await G(c,L(x,j),K);let b=new URL(V);b.searchParams.delete("inspectOpen"),g=w.find(S=>{if(S.readyState!=="open"||!S.url||!he(S,v))return!1;try{let Ue=new URL(S.url);return Ue.searchParams.delete("inspectOpen"),Ue.toString()===b.toString()}catch{return!1}})??null}if(!g)if(t.simIdSource!=="flag")re=!0;else throw new Error("no sim connected");if(!re&&g){let b=Pe||ve(c)?x:wt(g,x),S=V??await G(c,L(b,j),K);f.send({type:"evaluate",simId:g.id,code:`window.location.href = ${JSON.stringify(S)}`}).catch(()=>{})}}catch(w){console.error(` open failed: ${w instanceof Error?w.message:String(w)}`),await A(f,{errorsCommand:`rnx get errors 5${p}`,warningsCommand:`rnx get warnings 5${p}`,requestsCommand:`rnx get requests 5${p}`}),u(1)}if(!re&&g){await $(1500);let w=await pn(t.wsPort,t.commandTimeoutMs,g.id);w||(console.error(" timed out waiting for current sim to load target"),u(1)),w.bridge.close(),P(g.id);let b=Pe||ve(c)?x:wt(g,x),S=V??await G(c,L(b,j),K);return yt(S,{...g,url:S},"current sim",o),await St(t.wsPort,g.id,e),t.wsPort}}finally{f.close()}}let Et=L(x,j),Ie=`cli-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`,ke=await mn(c,Et,Ie,K),Ce=_e();Ce||(console.error(" could not resolve a stable owning process; refusing unsupervised sim host"),u(1));let Tt=f=>f.url?.includes(`inspectOpen=${Ie}`)??!1,R=rt(v);R||(console.error(` unknown driver "${v}" \u2014 run \`rnx list --drivers\``),u(1));let q=Wt(),y=await R.launch({url:ke,bridgePort:de(x)??t.wsPort,headless:It,profileId:l,ephemeralProfile:i,cdpPort:s,device:C,ownerPid:Ce,viewport:Ct??(ee?{width:ee.width+120,height:ee.height+60}:void 0),connectTimeoutMs:q.hostTimeoutMs});y.launched||(console.error(` ${R.name} driver: ${y.message}`),u(1));let te=await wn(t.wsPort,t.commandTimeoutMs,Tt,y.pid,{attempts:q.attempts,intervalMs:q.intervalMs});if(te.kind==="host-exit"&&(vt(y),console.error(` ${R.name} host crashed \u2014 sim is no longer attached`),u(1)),te.kind==="timeout"){if(y.pid)try{process.kill(y.pid,"SIGTERM"),console.error(` closed ${R.name} host process ${y.pid}`)}catch{}vt(y),console.error(` timed out after ${q.timeoutMs}ms waiting for opened sim to connect`),u(1)}let ne=te.sim;return P(ne.id),yn(y),yt(ke,ne,`${R.name} driver`,o),await St(t.wsPort,ne.id,e),t.wsPort}async function Pt(e,n={}){let t=E(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=Y(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"),u(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`),u(1))}await o.focusSim(s.id),P(s.id),console.log(` using: ${s.id}`)}catch(a){z(a),console.error(` use failed: ${a instanceof Error?a.message:String(a)}`),await A(o,{errorsCommand:`rnx get errors 5${i}`,warningsCommand:`rnx get warnings 5${i}`,requestsCommand:`rnx get requests 5${i}`}),u(1)}}finally{o.close()}}async function ur(e,n={}){await Pt(e,n)}async function mr(e,n={}){await Pt(e,n)}async function pr(e,n={}){let t=E(e,{port:n.port,commandTimeoutMs:n.timeoutMs,stripBooleanFlags:["--force"]}),r=e.includes("--force"),o=T(t);try{try{let i=await o.listSims(),a=Y(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});P(a.id);let d=Math.max(0,Math.round((l.lockExpiresAt-Date.now())/1e3)),m=l.bootedCount>0?` (booted ${l.bootedCount})`:"";console.log(` claimed: ${l.simId} [${d}s]${m}`),s&&console.log(` took over from: ${s}`)}catch(i){if(i instanceof Ae){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"),u(1)}console.error(` claim failed: ${i instanceof Error?i.message:String(i)}`),u(1)}}finally{o.close()}}async function In(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=I(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 $(250)}return{closed:[...o],remaining:[]}}function kn(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,m=null;if(n.closeOthers)if(o){let c=t.find(h=>h.id===o);c?l=c.id:d=o}else if(i&&a)l=i;else{let c=t.find(h=>h.isPrimary)??t[0]??null;c&&(l=c.id,i&&!a&&(m=c.id))}return{openIds:r,keepId:l,targets:d?[]:r.filter(c=>c!==l),staleSavedId:s,missingExplicitKeepId:d,fallbackKeepId:m}}async function fr(e,n={}){let t=E(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=_(),d=t.positional[0]||(t.simIdSource==="flag"?t.simId:void 0),m=kn(s,{closeOthers:a,explicitKeepId:d,savedKeepId:l});if(m.staleSavedId&&D(),m.fallbackKeepId&&(P(m.fallbackKeepId),console.log(` saved sim ${m.staleSavedId} is gone \u2014 keeping primary ${m.fallbackKeepId}`)),m.missingExplicitKeepId&&(console.error(` close failed: keep sim ${m.missingExplicitKeepId} is not connected; not closing other sims`),u(1)),m.targets.length===0){console.log(m.keepId?` nothing to close \u2014 only the kept sim ${m.keepId} is connected`:m.staleSavedId?` nothing to close \u2014 no sims connected (cleared stale current sim ${m.staleSavedId})`:" nothing to close \u2014 no sims connected");return}let c=await In(r,t.wsPort,t.commandTimeoutMs,m.targets);await be(s,m.targets);let h=_();h&&c.closed.includes(h)&&(m.keepId?P(m.keepId):D());let N=` closed ${c.closed.length} sim(s)${m.keepId?` (kept ${m.keepId})`:""}`;console.log(N),c.remaining.length>0&&(console.error(` close failed: still connected: ${c.remaining.join(", ")}`),u(1))}catch(s){z(s),console.error(` close failed: ${s instanceof Error?s.message:String(s)}`),u(1)}finally{r.close()}return}try{try{let s=await r.listSims(),l=Y(s,t.positional[0]||t.simId),d=s.find(c=>c.id!==l.id&&c.readyState==="open");await r.closeSim(l.id),await bn(t.wsPort,t.commandTimeoutMs,l.id)||(await be(s,[l.id]),console.error(` close failed: ${l.id} is still connected`),u(1)),await be(s,[l.id]),_()===l.id&&(d?P(d.id):D()),console.log(` closed: ${l.id}`)}catch(s){z(s),console.error(` close failed: ${s instanceof Error?s.message:String(s)}`),await A(r,{errorsCommand:`rnx get errors 5${o}`,warningsCommand:`rnx get warnings 5${o}`,requestsCommand:`rnx get requests 5${o}`}),u(1)}}finally{r.close()}}export{Wt as a,qt as b,ar as c,on as d,xt as e,sn as f,G as g,mn as h,fn as i,lr as j,cr as k,wn as l,vn as m,yn as n,be as o,Pn as p,dr as q,ur as r,mr as s,pr as t,In as u,kn as v,fr as w};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/*! rnx v0.1.370 | (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}from"./chunk-MDCY532E.js";import"./chunk-64JAEJ6N.js";import"./chunk-I52NT6VG.js";export{a as getCliVersion};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/*! rnx v0.1.370 | (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,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w}from"./chunk-OTFTMGAL.js";import"./chunk-6S5LJV2O.js";import"./chunk-3UXXYJVD.js";import"./chunk-DMHSD6YJ.js";import"./chunk-PAUEZO6F.js";import"./chunk-6A6L3VJR.js";import"./chunk-3BG6UCQY.js";import"./chunk-5BR33NAZ.js";import"./chunk-NR2V2ONN.js";import"./chunk-D2GZASRV.js";import"./chunk-AALKIAUZ.js";import"./chunk-EWSWOWXK.js";import"./chunk-PNOENVMJ.js";import"./chunk-WFQHT4QD.js";import"./chunk-EQ3OE2AV.js";import"./chunk-JZLDHPB7.js";import"./chunk-CMVRTZ5G.js";import"./chunk-EOIBLKZO.js";import"./chunk-OJZHECGH.js";import"./chunk-AVJBT5LF.js";import"./chunk-FA75F34Z.js";import"./chunk-DHYTRCIP.js";import"./chunk-IDLFVRPN.js";import"./chunk-U63T6LIQ.js";import"./chunk-64JAEJ6N.js";import"./chunk-I52NT6VG.js";export{d as applyRuntimeGenerationOrigin,h as buildOpenUrl,g as buildShellUrl,u as closeSimsBulk,b as ensureBridgeForOpen,v as planBulkCloseTargets,m as playwrightHostPidForSim,j as printConnectedSims,c as resolveBundleTarget,e as resolveDefaultShellBaseUrl,a as resolveDriverConnectWaitOptions,f as resolveShellBaseUrlForBridgePort,t as runClaimCommand,w as runCloseCommand,s as runFocusCommand,p as runLinkCommand,q as runOpenCommand,r as runUseCommand,n as signalDriverLaunchConnected,i as summarizeSimUrl,o as terminatePlaywrightHostsForSims,k as waitForSimMatch,l as waitForSimMatchOrHostExit};
|