rnxsim 0.1.455 → 0.1.457

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.
Files changed (62) hide show
  1. package/cli/cloud-client.ts +3 -1
  2. package/cli/cloud-dispatch.ts +17 -0
  3. package/cli/commands/do-chain.ts +1 -4
  4. package/cli/commands/inspect/actions.ts +63 -14
  5. package/cli/commands/inspect.ts +66 -5
  6. package/cli/ws-bridge.ts +3 -0
  7. package/dist-lib/agent-daemon-client.cjs +1 -1
  8. package/dist-lib/agent-events.cjs +1 -1
  9. package/dist-lib/agent-identity.cjs +1 -1
  10. package/dist-lib/agent-sessions.cjs +1 -1
  11. package/dist-lib/attached-projects.cjs +1 -1
  12. package/dist-lib/auth/shared-session.cjs +1 -1
  13. package/dist-lib/backend-origin.cjs +1 -1
  14. package/dist-lib/beta.cjs +1 -1
  15. package/dist-lib/beta.mjs +1 -1
  16. package/dist-lib/bridge-constants.cjs +1 -1
  17. package/dist-lib/bridge-contract-input.cjs +1 -1
  18. package/dist-lib/bridge-contract-input.mjs +1 -1
  19. package/dist-lib/bridge-contract.cjs +1 -1
  20. package/dist-lib/bridge-contract.mjs +1 -1
  21. package/dist-lib/capture-contract.cjs +1 -1
  22. package/dist-lib/capture-contract.mjs +1 -1
  23. package/dist-lib/cli-constants.cjs +1 -1
  24. package/dist-lib/cloud-contract.cjs +1 -1
  25. package/dist-lib/cloud-contract.mjs +1 -1
  26. package/dist-lib/cloud.cjs +1 -1
  27. package/dist-lib/cloud.mjs +1 -1
  28. package/dist-lib/config.cjs +1 -1
  29. package/dist-lib/detox/index.cjs +1 -1
  30. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  31. package/dist-lib/home-paths.cjs +1 -1
  32. package/dist-lib/host/bridge-host.cjs +1 -1
  33. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  34. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  35. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  36. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  37. package/dist-lib/host/websocket-proxy.cjs +1 -1
  38. package/dist-lib/index.cjs +583 -241
  39. package/dist-lib/jump-to-source-babel.cjs +1 -1
  40. package/dist-lib/jump-to-source-native.cjs +1 -1
  41. package/dist-lib/menu.cjs +1 -1
  42. package/dist-lib/menu.mjs +1 -1
  43. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  44. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  45. package/dist-lib/metro-production-bundle.cjs +1 -1
  46. package/dist-lib/metro-production-bundle.mjs +1 -1
  47. package/dist-lib/metro.cjs +1 -1
  48. package/dist-lib/profiles.cjs +1 -1
  49. package/dist-lib/public-brand.cjs +1 -1
  50. package/dist-lib/react-native-host-modules.cjs +1 -1
  51. package/dist-lib/react-native-host-modules.mjs +1 -1
  52. package/dist-lib/render-mode.cjs +1 -1
  53. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  54. package/dist-lib/sdk.cjs +43 -15
  55. package/dist-lib/sdk.mjs +43 -15
  56. package/dist-lib/skills.cjs +1118 -85
  57. package/dist-lib/vite.cjs +1 -1
  58. package/package.json +1 -1
  59. package/skills/rnx-debug/SKILL.md +1 -1
  60. package/src/bridge-contract.ts +2 -1
  61. package/src/connect.ts +18 -0
  62. package/src/sim-client.ts +4 -0
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.455 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.457 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -56,6 +56,514 @@ __export(src_exports, {
56
56
  });
57
57
  module.exports = __toCommonJS(src_exports);
58
58
 
59
+ // cli/cloud-client.ts
60
+ var import_node_crypto = require("node:crypto");
61
+
62
+ // ../sootsim-engine/src/metro-fingerprint-registry-client.ts
63
+ var import_metro_fingerprint_registry = require("rnxsim/metro-fingerprint-registry");
64
+
65
+ // ../sootsim-engine/src/metro-fingerprint.ts
66
+ var import_metro_production_bundle = require("rnxsim/metro-production-bundle");
67
+ var import_react_native_host_modules = require("rnxsim/react-native-host-modules");
68
+
69
+ // src/backend-origin.ts
70
+ function normalizeHostname(hostname) {
71
+ return hostname.replace(/^\[|\]$/g, "").toLowerCase();
72
+ }
73
+ function isLoopbackHost(hostname) {
74
+ const normalized = normalizeHostname(hostname);
75
+ return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
76
+ }
77
+
78
+ // src/cloud-contract.ts
79
+ var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
80
+ var RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024;
81
+ var RNX_CLOUD_COMMAND_TYPES = Object.freeze([
82
+ "state",
83
+ "query",
84
+ "resolve",
85
+ "perform",
86
+ "waitFor",
87
+ "reset",
88
+ "screenshot",
89
+ "capture",
90
+ "captureRegions",
91
+ "settle",
92
+ "setAppearance",
93
+ "openUrl",
94
+ "tree",
95
+ "tap",
96
+ "longPress",
97
+ "memory",
98
+ "diagnostics"
99
+ ]);
100
+ var RNX_CLOUD_COMMAND_TYPE_SET = new Set(RNX_CLOUD_COMMAND_TYPES);
101
+ function isRnxCloudCommandType(value) {
102
+ return RNX_CLOUD_COMMAND_TYPE_SET.has(value);
103
+ }
104
+
105
+ // src/metro-production-bundle.ts
106
+ var RNXSIM_METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
107
+ var RNX_METRO_MODULE_IDENTITY_VERSION = 2;
108
+ var RNXSIM_METRO_SOURCE_MAP_COMMENT = "//# sourceMappingURL=";
109
+ function detachRNXMetroSourceMapUrl(bundleText) {
110
+ const trimmedEnd = bundleText.replace(/\s+$/, "");
111
+ const commentStart = trimmedEnd.lastIndexOf(`
112
+ ${RNXSIM_METRO_SOURCE_MAP_COMMENT}`);
113
+ if (commentStart < 0) return { source: bundleText, sourceMappingUrl: null };
114
+ const url = trimmedEnd.slice(commentStart + 1 + RNXSIM_METRO_SOURCE_MAP_COMMENT.length);
115
+ if (url.includes("\n")) return { source: bundleText, sourceMappingUrl: null };
116
+ return { source: trimmedEnd.slice(0, commentStart), sourceMappingUrl: url };
117
+ }
118
+ function createRNXMetroModuleIdentityFooter(identity) {
119
+ const payload = JSON.stringify(identity).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(/<\//g, "<\\/");
120
+ return `
121
+ ${RNXSIM_METRO_MODULE_PATHS_PREFIX}${payload};`;
122
+ }
123
+ function appendRNXMetroModuleIdentityFooter(bundleText, identity) {
124
+ const footer = createRNXMetroModuleIdentityFooter(identity);
125
+ const detached = detachRNXMetroSourceMapUrl(bundleText);
126
+ return detached.sourceMappingUrl === null ? bundleText + footer : `${detached.source}${footer}
127
+ ${RNXSIM_METRO_SOURCE_MAP_COMMENT}${detached.sourceMappingUrl}`;
128
+ }
129
+
130
+ // src/public-brand.ts
131
+ var name = "rnx";
132
+ var rnxPublicBrand = Object.freeze({
133
+ name,
134
+ packageName: "rnxsim",
135
+ commandName: "rnx",
136
+ domain: "rnxsim.com",
137
+ origin: "https://rnxsim.com"
138
+ });
139
+
140
+ // src/home-paths.ts
141
+ var import_node_fs = __toESM(require("node:fs"), 1);
142
+ var import_node_os = require("node:os");
143
+ var import_node_path = __toESM(require("node:path"), 1);
144
+ var RNX_HOME_ENV = "RNX_HOME";
145
+ var ACTIVE_RUNTIME_FILE = "active";
146
+ var DAEMON_LOCKFILE = "daemon.json";
147
+ var DEV_BRIDGES_DIR = "dev-bridges";
148
+ var DAEMON_HEARTBEAT_STALE_MS = 3e4;
149
+ function rnxHomeDir() {
150
+ const override = process.env[RNX_HOME_ENV];
151
+ if (override && override.length > 0) return import_node_path.default.resolve(override);
152
+ return import_node_path.default.join((0, import_node_os.homedir)(), ".rnx");
153
+ }
154
+ function runtimesDir() {
155
+ return import_node_path.default.join(rnxHomeDir(), "runtimes");
156
+ }
157
+ function runtimeDir(version) {
158
+ return import_node_path.default.join(runtimesDir(), version);
159
+ }
160
+ function activeRuntimeFile() {
161
+ return import_node_path.default.join(runtimesDir(), ACTIVE_RUNTIME_FILE);
162
+ }
163
+ function daemonLockfilePath() {
164
+ return import_node_path.default.join(rnxHomeDir(), DAEMON_LOCKFILE);
165
+ }
166
+ function devBridgesDir() {
167
+ return import_node_path.default.join(rnxHomeDir(), DEV_BRIDGES_DIR);
168
+ }
169
+ function readActiveRuntime() {
170
+ try {
171
+ const value = import_node_fs.default.readFileSync(activeRuntimeFile(), "utf8").trim();
172
+ return value.length > 0 ? value : null;
173
+ } catch {
174
+ return null;
175
+ }
176
+ }
177
+ var DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
178
+ function readDaemonLockfile() {
179
+ try {
180
+ const fd = import_node_fs.default.openSync(daemonLockfilePath(), "r");
181
+ try {
182
+ const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES);
183
+ const bytesRead = import_node_fs.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
184
+ const raw = buf.subarray(0, bytesRead).toString("utf8");
185
+ const parsed = JSON.parse(raw);
186
+ if (parsed && parsed.schema === 1 && typeof parsed.pid === "number" && typeof parsed.bridgePort === "number" && typeof parsed.runtimePort === "number" && typeof parsed.startedAt === "number" && typeof parsed.heartbeatAt === "number" && (parsed.servedRuntimes === void 0 || Array.isArray(parsed.servedRuntimes) && parsed.servedRuntimes.every((version) => typeof version === "string"))) {
187
+ return parsed;
188
+ }
189
+ return null;
190
+ } finally {
191
+ import_node_fs.default.closeSync(fd);
192
+ }
193
+ } catch {
194
+ return null;
195
+ }
196
+ }
197
+ function parseDevBridgeLockfile(raw) {
198
+ try {
199
+ const parsed = JSON.parse(raw);
200
+ if (parsed && parsed.schema === 1 && parsed.source === "vite-dev" && typeof parsed.pid === "number" && typeof parsed.bridgePort === "number" && typeof parsed.runtimePort === "number" && typeof parsed.cwd === "string" && typeof parsed.startedAt === "number" && typeof parsed.heartbeatAt === "number" && (parsed.shellPort === void 0 || typeof parsed.shellPort === "number") && (parsed.servedRuntimes === void 0 || Array.isArray(parsed.servedRuntimes) && parsed.servedRuntimes.every((version) => typeof version === "string"))) {
201
+ return parsed;
202
+ }
203
+ return null;
204
+ } catch {
205
+ return null;
206
+ }
207
+ }
208
+ function readDevBridgeLockfiles() {
209
+ let entries;
210
+ try {
211
+ entries = import_node_fs.default.readdirSync(devBridgesDir(), { withFileTypes: true });
212
+ } catch {
213
+ return [];
214
+ }
215
+ const lockfiles = [];
216
+ for (const entry of entries) {
217
+ if (!entry.isFile() || !/^\d+\.json$/.test(entry.name)) continue;
218
+ try {
219
+ const filePath = import_node_path.default.join(devBridgesDir(), entry.name);
220
+ const fd = import_node_fs.default.openSync(filePath, "r");
221
+ try {
222
+ const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES);
223
+ const bytesRead = import_node_fs.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
224
+ const lockfile = parseDevBridgeLockfile(
225
+ buf.subarray(0, bytesRead).toString("utf8")
226
+ );
227
+ if (lockfile && entry.name === `${lockfile.bridgePort}.json`) {
228
+ lockfiles.push(lockfile);
229
+ }
230
+ } finally {
231
+ import_node_fs.default.closeSync(fd);
232
+ }
233
+ } catch {
234
+ }
235
+ }
236
+ return lockfiles.sort((left, right) => left.bridgePort - right.bridgePort);
237
+ }
238
+ function isDaemonLockfileFresh(lock, now = Date.now()) {
239
+ if (!lock) return false;
240
+ if (now - lock.heartbeatAt > DAEMON_HEARTBEAT_STALE_MS) return false;
241
+ try {
242
+ process.kill(lock.pid, 0);
243
+ return true;
244
+ } catch {
245
+ return false;
246
+ }
247
+ }
248
+ function isDevBridgeLockfileFresh(lock, now = Date.now()) {
249
+ if (!lock) return false;
250
+ if (now - lock.heartbeatAt > DAEMON_HEARTBEAT_STALE_MS) return false;
251
+ try {
252
+ process.kill(lock.pid, 0);
253
+ return true;
254
+ } catch {
255
+ return false;
256
+ }
257
+ }
258
+
259
+ // cli/run-rnx.ts
260
+ var import_node_async_hooks = require("node:async_hooks");
261
+ var sinks = new import_node_async_hooks.AsyncLocalStorage();
262
+
263
+ // cli/auth.ts
264
+ var CHOOSE_ACCOUNT_HINT = "this sign-in belongs to several accounts; run `rnx login --account <name>` to choose the one to bill";
265
+
266
+ // cli/cloud-session.ts
267
+ var CLOUD_SESSION_VERSION = 3;
268
+ var MAX_ENCODED_SESSION_BYTES = 32 * 1024;
269
+ var RNX_CLOUD_SESSION_ENV = "RNX_CLOUD_SESSION";
270
+ var RNX_SHELL_UPDATE_PREFIX = "__RNX_SHELL_UPDATE_V1__";
271
+ var RNX_SHELL_UPDATE_SET_PREFIX = `${RNX_SHELL_UPDATE_PREFIX}set:`;
272
+ var RNX_SHELL_UPDATE_UNSET = `${RNX_SHELL_UPDATE_PREFIX}unset`;
273
+ function isRecord(value) {
274
+ return value !== null && typeof value === "object" && !Array.isArray(value);
275
+ }
276
+ function normalizeCloudOrigin(source) {
277
+ let parsed;
278
+ try {
279
+ parsed = new URL(source);
280
+ } catch {
281
+ throw new Error(`invalid remote simulator origin: ${source}`);
282
+ }
283
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
284
+ throw new Error(`remote simulator origin must be an HTTP(S) origin: ${source}`);
285
+ }
286
+ if (parsed.protocol === "http:" && !isLoopbackHost(parsed.hostname)) {
287
+ throw new Error(`remote simulator origin requires HTTPS outside loopback: ${source}`);
288
+ }
289
+ return parsed.origin;
290
+ }
291
+ function createCloudSession(input) {
292
+ const session = {
293
+ version: CLOUD_SESSION_VERSION,
294
+ service: "sim",
295
+ boxId: input.boxId.trim(),
296
+ boxToken: input.boxToken,
297
+ simId: input.simId.trim(),
298
+ apiOrigin: normalizeCloudOrigin(input.apiOrigin.trim()),
299
+ claimId: input.claimId.trim(),
300
+ token: input.token
301
+ };
302
+ if (!session.boxId || !session.boxToken || !session.simId || !session.claimId || !session.token) {
303
+ throw new Error("cloud session requires box, simulator, origin, claim, and tokens");
304
+ }
305
+ return session;
306
+ }
307
+ function createCloudBoxSession(input) {
308
+ const session = {
309
+ version: CLOUD_SESSION_VERSION,
310
+ service: "box",
311
+ boxId: input.boxId.trim(),
312
+ boxToken: input.boxToken,
313
+ apiOrigin: normalizeCloudOrigin(input.apiOrigin.trim())
314
+ };
315
+ if (!session.boxId || !session.boxToken) {
316
+ throw new Error("cloud box session requires a box, an origin, and a box token");
317
+ }
318
+ return session;
319
+ }
320
+ function parseCloudSession(encoded) {
321
+ const value = encoded.trim();
322
+ if (!value || value.length > MAX_ENCODED_SESSION_BYTES || !/^[A-Za-z0-9_-]+$/.test(value)) {
323
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
324
+ }
325
+ let parsed;
326
+ try {
327
+ const bytes = Buffer.from(value, "base64url");
328
+ if (bytes.toString("base64url") !== value) {
329
+ throw new Error("non-canonical cloud session");
330
+ }
331
+ parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
332
+ } catch {
333
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
334
+ }
335
+ if (!isRecord(parsed) || parsed.version !== CLOUD_SESSION_VERSION) {
336
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
337
+ }
338
+ const boxId = parsed.boxId;
339
+ const boxToken = parsed.boxToken;
340
+ const apiOrigin = parsed.apiOrigin;
341
+ if (typeof boxId !== "string" || typeof boxToken !== "string" || typeof apiOrigin !== "string") {
342
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
343
+ }
344
+ try {
345
+ if (parsed.service === "box") {
346
+ return createCloudBoxSession({ boxId, boxToken, apiOrigin });
347
+ }
348
+ const simId = parsed.simId;
349
+ const claimId = parsed.claimId;
350
+ const token = parsed.token;
351
+ if (parsed.service !== "sim" || typeof simId !== "string" || typeof claimId !== "string" || typeof token !== "string") {
352
+ throw new Error("unreadable cloud session");
353
+ }
354
+ return createCloudSession({ boxId, boxToken, simId, apiOrigin, claimId, token });
355
+ } catch {
356
+ throw new Error(`${RNX_CLOUD_SESSION_ENV} is invalid`);
357
+ }
358
+ }
359
+ function readCloudSession(environment = process.env) {
360
+ const encoded = environment[RNX_CLOUD_SESSION_ENV];
361
+ return encoded?.trim() ? parseCloudSession(encoded) : null;
362
+ }
363
+
364
+ // cli/cloud-client.ts
365
+ var DEFAULT_RNX_CLOUD_ORIGIN = rnxPublicBrand.origin;
366
+ function isRecord2(value) {
367
+ return value !== null && typeof value === "object" && !Array.isArray(value);
368
+ }
369
+ function failureSentence(text) {
370
+ let parsed;
371
+ try {
372
+ parsed = JSON.parse(text);
373
+ } catch {
374
+ return null;
375
+ }
376
+ if (!isRecord2(parsed)) return null;
377
+ if (isRecord2(parsed.error) && typeof parsed.error.message === "string") {
378
+ return parsed.error.message;
379
+ }
380
+ if (typeof parsed.message === "string") return parsed.message;
381
+ if (typeof parsed.error === "string") return parsed.error;
382
+ return null;
383
+ }
384
+ var ACCOUNT_CHOICE_REFUSALS = /* @__PURE__ */ new Set([
385
+ "accountId is required",
386
+ "you do not have access to this account"
387
+ ]);
388
+ function responseError(status, text) {
389
+ const parsed = text ? failureSentence(text) : null;
390
+ const sentence = parsed && ACCOUNT_CHOICE_REFUSALS.has(parsed) ? CHOOSE_ACCOUNT_HINT : parsed;
391
+ return new Error(
392
+ `remote simulator request failed (${status})${sentence ? `: ${sentence}` : text.trim() ? `: ${text.trim()}` : ""}`
393
+ );
394
+ }
395
+ function resolveCloudSessionForParsed(parsed) {
396
+ const session = readCloudSession();
397
+ if (!session) return null;
398
+ if (session.service === "box") {
399
+ if (parsed.simIdSource === "flag" && parsed.simId !== session.boxId) {
400
+ throw new Error(
401
+ `this shell is connected to box ${session.boxId}; ${parsed.simId ? `--sim ${parsed.simId}` : "the requested simulator"} is not available in this shell`
402
+ );
403
+ }
404
+ return session;
405
+ }
406
+ if (parsed.simIdSource === "flag" && parsed.simId !== session.simId) {
407
+ throw new Error(
408
+ `this shell is connected to remote simulator ${session.simId}; ${parsed.simId ? `--sim ${parsed.simId}` : "the requested simulator"} is not available in this shell`
409
+ );
410
+ }
411
+ return session;
412
+ }
413
+ function cloudCommand(command) {
414
+ const clean = {};
415
+ for (const [key, value] of Object.entries(command)) {
416
+ if (key !== "id" && key !== "simId") clean[key] = value;
417
+ }
418
+ if (isRnxCloudCommandType(command.type)) return clean;
419
+ throw new Error(
420
+ `a remote simulator does not support bridge command type ${command.type}`
421
+ );
422
+ }
423
+ var CloudBridge = class {
424
+ constructor(session) {
425
+ this.session = session;
426
+ }
427
+ plane = "cloud";
428
+ async send(command, options) {
429
+ const body = {
430
+ id: (0, import_node_crypto.randomUUID)(),
431
+ claimId: this.session.claimId,
432
+ command: cloudCommand(command)
433
+ };
434
+ let response;
435
+ try {
436
+ response = await fetch(
437
+ `${this.session.apiOrigin}/v1/sims/${encodeURIComponent(this.session.simId)}/commands`,
438
+ {
439
+ method: "POST",
440
+ headers: {
441
+ authorization: `Bearer ${this.session.token}`,
442
+ "content-type": "application/json"
443
+ },
444
+ body: JSON.stringify(body),
445
+ ...options?.timeoutMs ? { signal: AbortSignal.timeout(options.timeoutMs) } : {}
446
+ }
447
+ );
448
+ } catch (error) {
449
+ throw new Error(
450
+ `could not reach the remote simulator at ${this.session.apiOrigin}: ${error instanceof Error ? error.message : String(error)}`
451
+ );
452
+ }
453
+ const text = await response.text();
454
+ if (!response.ok) throw responseError(response.status, text);
455
+ let parsed;
456
+ try {
457
+ parsed = JSON.parse(text);
458
+ } catch {
459
+ throw new Error("remote simulator command returned unreadable JSON");
460
+ }
461
+ if (!isRecord2(parsed) || parsed.id !== body.id) {
462
+ throw new Error("remote simulator command returned an invalid command receipt");
463
+ }
464
+ if (typeof parsed.error === "string" && parsed.error) throw new Error(parsed.error);
465
+ if (!Object.hasOwn(parsed, "result")) {
466
+ throw new Error("remote simulator command receipt has no result");
467
+ }
468
+ return parsed.result;
469
+ }
470
+ listSims() {
471
+ return Promise.reject(
472
+ new Error("a remote simulator does not expose a local simulator list")
473
+ );
474
+ }
475
+ focusSim() {
476
+ return Promise.reject(new Error("remote simulators have no browser window to focus"));
477
+ }
478
+ resolveReloadedSim() {
479
+ return Promise.resolve(null);
480
+ }
481
+ closeSim() {
482
+ return Promise.reject(
483
+ new Error("remote simulator close uses the simulator DELETE route")
484
+ );
485
+ }
486
+ claim() {
487
+ return Promise.reject(
488
+ new Error("remote simulator claim uses the simulator claim route")
489
+ );
490
+ }
491
+ close() {
492
+ }
493
+ };
494
+ var CloudBoxBridge = class {
495
+ constructor(session) {
496
+ this.session = session;
497
+ }
498
+ plane = "cloud";
499
+ async send(command, options) {
500
+ const id = (0, import_node_crypto.randomUUID)();
501
+ const payload = {};
502
+ for (const [key, value] of Object.entries(command)) {
503
+ if (key !== "id" && key !== "simId") payload[key] = value;
504
+ }
505
+ let response;
506
+ try {
507
+ response = await fetch(
508
+ `${this.session.apiOrigin}/v1/boxes/${encodeURIComponent(this.session.boxId)}/commands`,
509
+ {
510
+ method: "POST",
511
+ headers: {
512
+ authorization: `Bearer ${this.session.boxToken}`,
513
+ "content-type": "application/json"
514
+ },
515
+ body: JSON.stringify({ id, command: payload }),
516
+ ...options?.timeoutMs ? { signal: AbortSignal.timeout(options.timeoutMs) } : {}
517
+ }
518
+ );
519
+ } catch (error) {
520
+ throw new Error(
521
+ `could not reach box ${this.session.boxId} at ${this.session.apiOrigin}: ${error instanceof Error ? error.message : String(error)}`
522
+ );
523
+ }
524
+ const text = await response.text();
525
+ if (!response.ok) throw responseError(response.status, text);
526
+ let parsed;
527
+ try {
528
+ parsed = JSON.parse(text);
529
+ } catch {
530
+ throw new Error("box command returned unreadable JSON");
531
+ }
532
+ if (!isRecord2(parsed) || parsed.id !== id) {
533
+ throw new Error("box command returned an invalid command receipt");
534
+ }
535
+ if (typeof parsed.error === "string" && parsed.error) throw new Error(parsed.error);
536
+ if (!Object.hasOwn(parsed, "result")) {
537
+ throw new Error("box command receipt has no result");
538
+ }
539
+ return parsed.result;
540
+ }
541
+ listSims() {
542
+ return Promise.reject(new Error("a box does not expose a local simulator list"));
543
+ }
544
+ focusSim() {
545
+ return Promise.reject(new Error("a box simulator has no browser window to focus"));
546
+ }
547
+ resolveReloadedSim() {
548
+ return Promise.resolve(null);
549
+ }
550
+ closeSim() {
551
+ return Promise.reject(new Error("close a box simulator with rnx box stop"));
552
+ }
553
+ claim() {
554
+ return Promise.reject(
555
+ new Error("a box simulator is held by its Box page and has nothing to claim")
556
+ );
557
+ }
558
+ close() {
559
+ }
560
+ };
561
+ function createCloudBridgeForParsed(parsed) {
562
+ const session = resolveCloudSessionForParsed(parsed);
563
+ if (!session) return null;
564
+ return session.service === "box" ? new CloudBoxBridge(session) : new CloudBridge(session);
565
+ }
566
+
59
567
  // cli/current-sim.ts
60
568
  var import_fs2 = require("fs");
61
569
  var import_os = require("os");
@@ -215,25 +723,25 @@ function tryBuildDarwinCommFfi() {
215
723
  }
216
724
 
217
725
  // cli/secure-file.ts
218
- var import_node_crypto = require("node:crypto");
219
- var import_node_fs = require("node:fs");
220
- var import_node_path = require("node:path");
726
+ var import_node_crypto2 = require("node:crypto");
727
+ var import_node_fs2 = require("node:fs");
728
+ var import_node_path2 = require("node:path");
221
729
  function writeSecureJsonAtomic(filepath, value) {
222
- (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(filepath), { recursive: true });
223
- const temporary = `${filepath}.tmp-${process.pid}-${(0, import_node_crypto.randomUUID)()}`;
730
+ (0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(filepath), { recursive: true });
731
+ const temporary = `${filepath}.tmp-${process.pid}-${(0, import_node_crypto2.randomUUID)()}`;
224
732
  let descriptor = null;
225
733
  try {
226
- descriptor = (0, import_node_fs.openSync)(temporary, "wx", 384);
227
- (0, import_node_fs.fchmodSync)(descriptor, 384);
228
- (0, import_node_fs.writeFileSync)(descriptor, `${JSON.stringify(value, null, 2)}
734
+ descriptor = (0, import_node_fs2.openSync)(temporary, "wx", 384);
735
+ (0, import_node_fs2.fchmodSync)(descriptor, 384);
736
+ (0, import_node_fs2.writeFileSync)(descriptor, `${JSON.stringify(value, null, 2)}
229
737
  `);
230
- (0, import_node_fs.fsyncSync)(descriptor);
231
- (0, import_node_fs.closeSync)(descriptor);
738
+ (0, import_node_fs2.fsyncSync)(descriptor);
739
+ (0, import_node_fs2.closeSync)(descriptor);
232
740
  descriptor = null;
233
- (0, import_node_fs.renameSync)(temporary, filepath);
741
+ (0, import_node_fs2.renameSync)(temporary, filepath);
234
742
  } catch (error) {
235
- if (descriptor !== null) (0, import_node_fs.closeSync)(descriptor);
236
- (0, import_node_fs.rmSync)(temporary, { force: true });
743
+ if (descriptor !== null) (0, import_node_fs2.closeSync)(descriptor);
744
+ (0, import_node_fs2.rmSync)(temporary, { force: true });
237
745
  throw error;
238
746
  }
239
747
  }
@@ -394,213 +902,6 @@ function resolveSootsimBridgePort(input = {}) {
394
902
  // src/cli-constants.ts
395
903
  var DEFAULT_SOOTSIM_SHELL_URL = "http://localhost:5173/";
396
904
 
397
- // src/home-paths.ts
398
- var import_node_fs2 = __toESM(require("node:fs"), 1);
399
- var import_node_os = require("node:os");
400
- var import_node_path2 = __toESM(require("node:path"), 1);
401
- var RNX_HOME_ENV = "RNX_HOME";
402
- var ACTIVE_RUNTIME_FILE = "active";
403
- var DAEMON_LOCKFILE = "daemon.json";
404
- var DEV_BRIDGES_DIR = "dev-bridges";
405
- var DAEMON_HEARTBEAT_STALE_MS = 3e4;
406
- function rnxHomeDir() {
407
- const override = process.env[RNX_HOME_ENV];
408
- if (override && override.length > 0) return import_node_path2.default.resolve(override);
409
- return import_node_path2.default.join((0, import_node_os.homedir)(), ".rnx");
410
- }
411
- function runtimesDir() {
412
- return import_node_path2.default.join(rnxHomeDir(), "runtimes");
413
- }
414
- function runtimeDir(version) {
415
- return import_node_path2.default.join(runtimesDir(), version);
416
- }
417
- function activeRuntimeFile() {
418
- return import_node_path2.default.join(runtimesDir(), ACTIVE_RUNTIME_FILE);
419
- }
420
- function daemonLockfilePath() {
421
- return import_node_path2.default.join(rnxHomeDir(), DAEMON_LOCKFILE);
422
- }
423
- function devBridgesDir() {
424
- return import_node_path2.default.join(rnxHomeDir(), DEV_BRIDGES_DIR);
425
- }
426
- function readActiveRuntime() {
427
- try {
428
- const value = import_node_fs2.default.readFileSync(activeRuntimeFile(), "utf8").trim();
429
- return value.length > 0 ? value : null;
430
- } catch {
431
- return null;
432
- }
433
- }
434
- var DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
435
- function readDaemonLockfile() {
436
- try {
437
- const fd = import_node_fs2.default.openSync(daemonLockfilePath(), "r");
438
- try {
439
- const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES);
440
- const bytesRead = import_node_fs2.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
441
- const raw = buf.subarray(0, bytesRead).toString("utf8");
442
- const parsed = JSON.parse(raw);
443
- if (parsed && parsed.schema === 1 && typeof parsed.pid === "number" && typeof parsed.bridgePort === "number" && typeof parsed.runtimePort === "number" && typeof parsed.startedAt === "number" && typeof parsed.heartbeatAt === "number" && (parsed.servedRuntimes === void 0 || Array.isArray(parsed.servedRuntimes) && parsed.servedRuntimes.every((version) => typeof version === "string"))) {
444
- return parsed;
445
- }
446
- return null;
447
- } finally {
448
- import_node_fs2.default.closeSync(fd);
449
- }
450
- } catch {
451
- return null;
452
- }
453
- }
454
- function parseDevBridgeLockfile(raw) {
455
- try {
456
- const parsed = JSON.parse(raw);
457
- if (parsed && parsed.schema === 1 && parsed.source === "vite-dev" && typeof parsed.pid === "number" && typeof parsed.bridgePort === "number" && typeof parsed.runtimePort === "number" && typeof parsed.cwd === "string" && typeof parsed.startedAt === "number" && typeof parsed.heartbeatAt === "number" && (parsed.shellPort === void 0 || typeof parsed.shellPort === "number") && (parsed.servedRuntimes === void 0 || Array.isArray(parsed.servedRuntimes) && parsed.servedRuntimes.every((version) => typeof version === "string"))) {
458
- return parsed;
459
- }
460
- return null;
461
- } catch {
462
- return null;
463
- }
464
- }
465
- function readDevBridgeLockfiles() {
466
- let entries;
467
- try {
468
- entries = import_node_fs2.default.readdirSync(devBridgesDir(), { withFileTypes: true });
469
- } catch {
470
- return [];
471
- }
472
- const lockfiles = [];
473
- for (const entry of entries) {
474
- if (!entry.isFile() || !/^\d+\.json$/.test(entry.name)) continue;
475
- try {
476
- const filePath = import_node_path2.default.join(devBridgesDir(), entry.name);
477
- const fd = import_node_fs2.default.openSync(filePath, "r");
478
- try {
479
- const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES);
480
- const bytesRead = import_node_fs2.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
481
- const lockfile = parseDevBridgeLockfile(
482
- buf.subarray(0, bytesRead).toString("utf8")
483
- );
484
- if (lockfile && entry.name === `${lockfile.bridgePort}.json`) {
485
- lockfiles.push(lockfile);
486
- }
487
- } finally {
488
- import_node_fs2.default.closeSync(fd);
489
- }
490
- } catch {
491
- }
492
- }
493
- return lockfiles.sort((left, right) => left.bridgePort - right.bridgePort);
494
- }
495
- function isDaemonLockfileFresh(lock, now = Date.now()) {
496
- if (!lock) return false;
497
- if (now - lock.heartbeatAt > DAEMON_HEARTBEAT_STALE_MS) return false;
498
- try {
499
- process.kill(lock.pid, 0);
500
- return true;
501
- } catch {
502
- return false;
503
- }
504
- }
505
- function isDevBridgeLockfileFresh(lock, now = Date.now()) {
506
- if (!lock) return false;
507
- if (now - lock.heartbeatAt > DAEMON_HEARTBEAT_STALE_MS) return false;
508
- try {
509
- process.kill(lock.pid, 0);
510
- return true;
511
- } catch {
512
- return false;
513
- }
514
- }
515
-
516
- // ../sootsim-engine/src/metro-fingerprint-registry-client.ts
517
- var import_metro_fingerprint_registry = require("rnxsim/metro-fingerprint-registry");
518
-
519
- // ../sootsim-engine/src/metro-fingerprint.ts
520
- var import_metro_production_bundle = require("rnxsim/metro-production-bundle");
521
- var import_react_native_host_modules = require("rnxsim/react-native-host-modules");
522
-
523
- // src/backend-origin.ts
524
- function normalizeHostname(hostname) {
525
- return hostname.replace(/^\[|\]$/g, "").toLowerCase();
526
- }
527
- function isLoopbackHost(hostname) {
528
- const normalized = normalizeHostname(hostname);
529
- return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
530
- }
531
-
532
- // src/cloud-contract.ts
533
- var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
534
- var RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024;
535
- var RNX_CLOUD_COMMAND_TYPES = Object.freeze([
536
- "state",
537
- "query",
538
- "resolve",
539
- "perform",
540
- "waitFor",
541
- "reset",
542
- "screenshot",
543
- "capture",
544
- "captureRegions",
545
- "settle",
546
- "setAppearance",
547
- "openUrl",
548
- "tree",
549
- "tap",
550
- "longPress",
551
- "memory",
552
- "diagnostics"
553
- ]);
554
- var RNX_CLOUD_COMMAND_TYPE_SET = new Set(RNX_CLOUD_COMMAND_TYPES);
555
-
556
- // src/metro-production-bundle.ts
557
- var RNXSIM_METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
558
- var RNX_METRO_MODULE_IDENTITY_VERSION = 2;
559
- var RNXSIM_METRO_SOURCE_MAP_COMMENT = "//# sourceMappingURL=";
560
- function detachRNXMetroSourceMapUrl(bundleText) {
561
- const trimmedEnd = bundleText.replace(/\s+$/, "");
562
- const commentStart = trimmedEnd.lastIndexOf(`
563
- ${RNXSIM_METRO_SOURCE_MAP_COMMENT}`);
564
- if (commentStart < 0) return { source: bundleText, sourceMappingUrl: null };
565
- const url = trimmedEnd.slice(commentStart + 1 + RNXSIM_METRO_SOURCE_MAP_COMMENT.length);
566
- if (url.includes("\n")) return { source: bundleText, sourceMappingUrl: null };
567
- return { source: trimmedEnd.slice(0, commentStart), sourceMappingUrl: url };
568
- }
569
- function createRNXMetroModuleIdentityFooter(identity) {
570
- const payload = JSON.stringify(identity).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029").replace(/<\//g, "<\\/");
571
- return `
572
- ${RNXSIM_METRO_MODULE_PATHS_PREFIX}${payload};`;
573
- }
574
- function appendRNXMetroModuleIdentityFooter(bundleText, identity) {
575
- const footer = createRNXMetroModuleIdentityFooter(identity);
576
- const detached = detachRNXMetroSourceMapUrl(bundleText);
577
- return detached.sourceMappingUrl === null ? bundleText + footer : `${detached.source}${footer}
578
- ${RNXSIM_METRO_SOURCE_MAP_COMMENT}${detached.sourceMappingUrl}`;
579
- }
580
-
581
- // src/public-brand.ts
582
- var name = "rnx";
583
- var rnxPublicBrand = Object.freeze({
584
- name,
585
- packageName: "rnxsim",
586
- commandName: "rnx",
587
- domain: "rnxsim.com",
588
- origin: "https://rnxsim.com"
589
- });
590
-
591
- // cli/run-rnx.ts
592
- var import_node_async_hooks = require("node:async_hooks");
593
- var sinks = new import_node_async_hooks.AsyncLocalStorage();
594
-
595
- // cli/cloud-session.ts
596
- var MAX_ENCODED_SESSION_BYTES = 32 * 1024;
597
- var RNX_SHELL_UPDATE_PREFIX = "__RNX_SHELL_UPDATE_V1__";
598
- var RNX_SHELL_UPDATE_SET_PREFIX = `${RNX_SHELL_UPDATE_PREFIX}set:`;
599
- var RNX_SHELL_UPDATE_UNSET = `${RNX_SHELL_UPDATE_PREFIX}unset`;
600
-
601
- // cli/cloud-client.ts
602
- var DEFAULT_RNX_CLOUD_ORIGIN = rnxPublicBrand.origin;
603
-
604
905
  // cli/ws-bridge.ts
605
906
  function bridgeWsUrl(wsPort) {
606
907
  return `ws://localhost:${wsPort}`;
@@ -1913,8 +2214,15 @@ async function tapResolvedTarget(bridge, args) {
1913
2214
  let attempts = 0;
1914
2215
  let lastPayload = null;
1915
2216
  let lastResult = null;
1916
- const canWaitForIdle = bridge.plane !== "cloud" && timing.initialWaitMs > 0;
1917
- if (canWaitForIdle) {
2217
+ const settleCloud = async () => {
2218
+ try {
2219
+ await bridge.send({ type: "settle" });
2220
+ } catch {
2221
+ }
2222
+ };
2223
+ if (bridge.plane === "cloud") {
2224
+ await settleCloud();
2225
+ } else if (timing.initialWaitMs > 0) {
1918
2226
  try {
1919
2227
  await waitForSootsimIdle({
1920
2228
  bridge,
@@ -1954,34 +2262,45 @@ async function tapResolvedTarget(bridge, args) {
1954
2262
  lastOffscreenAt = null;
1955
2263
  const requestedTarget = tapTargetFromPayload(payload, args.textFallback);
1956
2264
  const { tree } = await inspectTree(bridge, 50);
2265
+ let moved = false;
1957
2266
  if (Array.isArray(tree) && tree.length > 0) {
1958
2267
  const hit = hitTestSemanticTree(tree, payload.cx, payload.cy);
1959
2268
  const matched = hit !== null && (nodeMatchesRequested(hit.node, requestedTarget) || hit.ancestors.some(
1960
2269
  (ancestor) => nodeMatchesRequested(ancestor, requestedTarget)
1961
2270
  ));
1962
- if (hit && !matched) {
2271
+ const blocks = hit !== null && (hit.node.type === "text" || hit.node.pressable === true || hit.ancestors.some((ancestor) => ancestor.pressable === true));
2272
+ if (hit && !matched && blocks) {
2273
+ moved = subtreeMatchesRequested(hit.node, requestedTarget);
1963
2274
  lastResult = {
1964
2275
  hit: false,
1965
- reason: "target-covered",
2276
+ reason: moved ? "target-moved" : "target-covered",
1966
2277
  requestedTarget,
1967
2278
  coordinateTarget: coveringNodeSummary(hit.node)
1968
2279
  };
1969
- return { payload, result: lastResult, attempts, failure: "missed" };
2280
+ if (!moved)
2281
+ return { payload, result: lastResult, attempts, failure: "missed" };
1970
2282
  }
1971
2283
  }
1972
- const result = await tapCoordinates(
1973
- bridge,
1974
- payload.cx,
1975
- payload.cy,
1976
- requestedTarget
1977
- );
1978
- lastResult = result;
1979
- if (isTapSuccess(result)) return { payload, result, attempts };
2284
+ if (!moved) {
2285
+ const result = await tapCoordinates(
2286
+ bridge,
2287
+ payload.cx,
2288
+ payload.cy,
2289
+ requestedTarget
2290
+ );
2291
+ lastResult = result;
2292
+ if (isTapSuccess(result)) return { payload, result, attempts };
2293
+ }
1980
2294
  }
1981
2295
  }
1982
2296
  const remaining = deadline - Date.now();
1983
2297
  if (remaining <= 0) break;
1984
- if (bridge.plane === "cloud" || timing.retryWaitMs <= 0) break;
2298
+ if (bridge.plane === "cloud") {
2299
+ if (lastResult?.reason !== "target-moved" || attempts >= 3) break;
2300
+ await settleCloud();
2301
+ continue;
2302
+ }
2303
+ if (timing.retryWaitMs <= 0) break;
1985
2304
  try {
1986
2305
  await waitForSootsimIdle({
1987
2306
  bridge,
@@ -2000,6 +2319,13 @@ async function tapResolvedTarget(bridge, args) {
2000
2319
  failure: lastPayload && typeof lastPayload.cx === "number" ? "missed" : "not-found"
2001
2320
  };
2002
2321
  }
2322
+ function subtreeMatchesRequested(node, requested) {
2323
+ for (const child of node.children ?? []) {
2324
+ if (nodeMatchesRequested(child, requested) || subtreeMatchesRequested(child, requested))
2325
+ return true;
2326
+ }
2327
+ return false;
2328
+ }
2003
2329
  async function tapCoordinates(bridge, x, y, target) {
2004
2330
  return bridge.send({
2005
2331
  type: "tap",
@@ -2298,6 +2624,9 @@ var SimClient = class {
2298
2624
  this.bridge = bridge;
2299
2625
  this.simId = simId;
2300
2626
  }
2627
+ get plane() {
2628
+ return this.bridge.plane;
2629
+ }
2301
2630
  // --- transport (WsBridge/InspectBridge) ---
2302
2631
  send(cmd, opts) {
2303
2632
  if (this.closed) {
@@ -2675,6 +3004,19 @@ function pickSim(sims, wanted, port) {
2675
3004
  return primary;
2676
3005
  }
2677
3006
  async function connect(options = {}) {
3007
+ const target = {
3008
+ simId: options.sim,
3009
+ simIdSource: options.sim ? "flag" : "none"
3010
+ };
3011
+ const cloud = resolveCloudSessionForParsed(target);
3012
+ if (cloud) {
3013
+ const bridge2 = createCloudBridgeForParsed(target);
3014
+ if (!bridge2)
3015
+ throw new SimConnectError("remote simulator session disappeared while connecting");
3016
+ return new SimClient(bridge2, cloud.service === "box" ? cloud.boxId : cloud.simId, {
3017
+ commandTimeoutMs: options.timeoutMs ?? 15e3
3018
+ });
3019
+ }
2678
3020
  const port = options.port ?? resolveBridgeWorld().bridgePort;
2679
3021
  const commandTimeoutMs = options.timeoutMs ?? 15e3;
2680
3022
  const discovery = createBridge(port, { commandTimeoutMs, cliLabel: "sdk" });
@@ -2880,12 +3222,12 @@ function isRnxCaptureRegionAtlas(value) {
2880
3222
 
2881
3223
  // src/vite-plugin-one.ts
2882
3224
  var import_fs4 = __toESM(require("fs"), 1);
2883
- var import_node_crypto3 = require("node:crypto");
3225
+ var import_node_crypto4 = require("node:crypto");
2884
3226
  var import_path3 = __toESM(require("path"), 1);
2885
3227
 
2886
3228
  // src/dev-server-open.ts
2887
3229
  var import_node_child_process = require("node:child_process");
2888
- var import_node_crypto2 = require("node:crypto");
3230
+ var import_node_crypto3 = require("node:crypto");
2889
3231
  var import_node_fs4 = __toESM(require("node:fs"), 1);
2890
3232
  var import_node_path4 = __toESM(require("node:path"), 1);
2891
3233
  var import_node_url = require("node:url");
@@ -2911,7 +3253,7 @@ function tagRnxBundleForPlugin(bundleUrl, replacementOrigin, replacementToken, p
2911
3253
  url.searchParams.set("rnxAppName", appName);
2912
3254
  url.searchParams.set(
2913
3255
  "rnxProjectId",
2914
- (0, import_node_crypto2.createHash)("sha256").update(import_node_fs4.default.realpathSync(projectRoot)).digest("hex").slice(0, 32)
3256
+ (0, import_node_crypto3.createHash)("sha256").update(import_node_fs4.default.realpathSync(projectRoot)).digest("hex").slice(0, 32)
2915
3257
  );
2916
3258
  }
2917
3259
  url.searchParams.set("rnxReplacementOrigin", replacementOrigin);
@@ -3531,7 +3873,7 @@ function rnxPlugin(options = {}) {
3531
3873
  if (options.enabled === false) return;
3532
3874
  const bundleUrl = options.bundleUrl || "/node_modules/one/metro-entry.bundle?platform=ios&dev=true&minify=false";
3533
3875
  const appName = resolveRnxDesktopAppName(options.open, server.config.root);
3534
- const replacementToken = (0, import_node_crypto3.randomUUID)();
3876
+ const replacementToken = (0, import_node_crypto4.randomUUID)();
3535
3877
  const configuredPort = server.config.server.port || 8081;
3536
3878
  const listeningPort = () => {
3537
3879
  const address = server.httpServer?.address();
@@ -3656,7 +3998,7 @@ function rnxPlugin(options = {}) {
3656
3998
  // src/metro-plugin.ts
3657
3999
  var import_fs5 = __toESM(require("fs"), 1);
3658
4000
  var import_module2 = require("module");
3659
- var import_node_crypto4 = require("node:crypto");
4001
+ var import_node_crypto5 = require("node:crypto");
3660
4002
  var import_path4 = __toESM(require("path"), 1);
3661
4003
  var prefix = "/__soot";
3662
4004
  function readCallableMetroExport(moduleValue, label) {
@@ -3731,7 +4073,7 @@ function withRNX(config) {
3731
4073
  const configuredBundleUrl = "/index.bundle?platform=ios&dev=true&hot=true&minify=false";
3732
4074
  const cfgServer = config.server;
3733
4075
  const port = resolveMetroPort(cfgServer);
3734
- const metroServerId = (0, import_node_crypto4.randomUUID)();
4076
+ const metroServerId = (0, import_node_crypto5.randomUUID)();
3735
4077
  const bundleUrl = tagRnxBundleForPlugin(
3736
4078
  configuredBundleUrl,
3737
4079
  `http://localhost:${port}`,