rnxsim 0.1.381 → 0.1.383

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 (138) hide show
  1. package/README.md +19 -6
  2. package/cli/app-fonts.ts +1 -1
  3. package/cli/bin.ts +118 -26
  4. package/cli/bridge-diagnostics.ts +1 -1
  5. package/cli/bridge-flow-runner.ts +9 -9
  6. package/cli/browser-evals.ts +3 -3
  7. package/cli/cli-update.ts +261 -0
  8. package/cli/cloud-client.ts +407 -0
  9. package/cli/cloud-dispatch.ts +165 -0
  10. package/cli/cloud-session.ts +156 -0
  11. package/cli/command-contract.ts +2 -2
  12. package/cli/command-registry.ts +9 -0
  13. package/cli/commands/agent-wrapper.ts +4 -4
  14. package/cli/commands/agent.ts +3 -3
  15. package/cli/commands/box/checkout-plane.ts +16 -8
  16. package/cli/commands/box/rnx-command.ts +2 -2
  17. package/cli/commands/box/search-command.ts +2 -2
  18. package/cli/commands/box.ts +123 -70
  19. package/cli/commands/config.ts +3 -2
  20. package/cli/commands/control.ts +6 -6
  21. package/cli/commands/cpu-profile.ts +2 -2
  22. package/cli/commands/daemon.ts +7 -7
  23. package/cli/commands/debug.ts +2 -2
  24. package/cli/commands/detox.ts +7 -7
  25. package/cli/commands/diagnose.ts +1 -1
  26. package/cli/commands/electron.ts +1 -1
  27. package/cli/commands/film.ts +1 -1
  28. package/cli/commands/flow.ts +4 -4
  29. package/cli/commands/inspect/core.ts +58 -7
  30. package/cli/commands/inspect/env.ts +5 -3
  31. package/cli/commands/inspect/shared.ts +2 -1
  32. package/cli/commands/inspect/wait-event.ts +1 -1
  33. package/cli/commands/inspect/wait-ready.ts +15 -5
  34. package/cli/commands/inspect.ts +31 -9
  35. package/cli/commands/install-desktop.ts +2 -2
  36. package/cli/commands/maestro.ts +3 -3
  37. package/cli/commands/permissions.ts +1 -1
  38. package/cli/commands/platform.ts +91 -0
  39. package/cli/commands/record.ts +3 -3
  40. package/cli/commands/report-issue.ts +1 -1
  41. package/cli/commands/runtime.ts +1 -1
  42. package/cli/commands/screenshot-mode.ts +1 -1
  43. package/cli/commands/screenshot.ts +25 -0
  44. package/cli/commands/serve.ts +1 -1
  45. package/cli/commands/skills.ts +1 -1
  46. package/cli/commands/timeline.ts +1 -1
  47. package/cli/commands/upgrade.ts +8 -34
  48. package/cli/commands/upload.ts +3 -3
  49. package/cli/commands/version.ts +14 -3
  50. package/cli/commands/what-happened.ts +2 -2
  51. package/cli/current-sim.ts +43 -30
  52. package/cli/desktop-companion.ts +1 -1
  53. package/cli/drivers/electron.ts +1 -1
  54. package/cli/drivers/playwright.ts +3 -3
  55. package/cli/drivers/types.ts +4 -4
  56. package/cli/flow-file.ts +3 -3
  57. package/cli/help-core.ts +18 -11
  58. package/cli/hidden-runtime-alias.ts +1 -1
  59. package/cli/hints.ts +2 -2
  60. package/cli/maestro-js.ts +2 -2
  61. package/cli/outbound-endpoints.ts +40 -6
  62. package/cli/parse-args.ts +1 -1
  63. package/cli/registry.ts +1 -1
  64. package/cli/resolve-assets.ts +2 -2
  65. package/cli/secure-file.ts +31 -0
  66. package/cli/shell-init.ts +98 -0
  67. package/cli/ws-bridge.ts +18 -8
  68. package/detox/expectations.ts +1 -1
  69. package/detox/gestures.ts +3 -3
  70. package/detox/index.ts +3 -3
  71. package/dist-lib/agent-daemon-client.cjs +1 -1
  72. package/dist-lib/agent-events.cjs +1 -1
  73. package/dist-lib/agent-identity.cjs +1 -1
  74. package/dist-lib/agent-sessions.cjs +1 -1
  75. package/dist-lib/attached-projects.cjs +1 -1
  76. package/dist-lib/auth/shared-session.cjs +1 -1
  77. package/dist-lib/backend-origin.cjs +1 -1
  78. package/dist-lib/beta.cjs +1 -1
  79. package/dist-lib/beta.mjs +1 -1
  80. package/dist-lib/bridge-constants.cjs +1 -1
  81. package/dist-lib/bridge-contract-input.cjs +20 -8
  82. package/dist-lib/bridge-contract-input.mjs +20 -8
  83. package/dist-lib/bridge-contract.cjs +1 -1
  84. package/dist-lib/bridge-contract.mjs +1 -1
  85. package/dist-lib/capture-contract.cjs +217 -0
  86. package/dist-lib/capture-contract.mjs +187 -0
  87. package/dist-lib/cli-constants.cjs +1 -1
  88. package/dist-lib/cloud-contract.cjs +41 -0
  89. package/dist-lib/cloud-contract.mjs +13 -0
  90. package/dist-lib/config.cjs +1 -1
  91. package/dist-lib/detox/index.cjs +4 -4
  92. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  93. package/dist-lib/home-paths.cjs +9 -1
  94. package/dist-lib/host/bridge-host.cjs +15 -10
  95. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  96. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  97. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  98. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  99. package/dist-lib/host/websocket-proxy.cjs +1 -1
  100. package/dist-lib/index.cjs +361 -75
  101. package/dist-lib/jump-to-source-babel.cjs +1 -1
  102. package/dist-lib/menu.cjs +1 -1
  103. package/dist-lib/menu.mjs +1 -1
  104. package/dist-lib/metro-production-bundle.cjs +1 -1
  105. package/dist-lib/metro-production-bundle.mjs +1 -1
  106. package/dist-lib/metro.cjs +1 -1
  107. package/dist-lib/profiles.cjs +1 -1
  108. package/dist-lib/public-brand.cjs +1 -1
  109. package/dist-lib/react-native-host-modules.cjs +1 -1
  110. package/dist-lib/react-native-host-modules.mjs +1 -1
  111. package/dist-lib/render-mode.cjs +1 -1
  112. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  113. package/dist-lib/sdk.cjs +44 -6
  114. package/dist-lib/sdk.mjs +44 -6
  115. package/dist-lib/skills.cjs +436 -155
  116. package/dist-lib/vite.cjs +1 -1
  117. package/package.json +20 -4
  118. package/scripts/dev-server-scanner.ts +3 -3
  119. package/skills/contrast/SKILL.md +1 -1
  120. package/skills/rnx-perf/SKILL.md +68 -52
  121. package/src/app-url.ts +2 -3
  122. package/src/bridge-contract-input.ts +25 -6
  123. package/src/bridge-contract.ts +35 -5
  124. package/src/capture-contract.ts +576 -0
  125. package/src/cloud-contract.ts +18 -0
  126. package/src/home-paths.ts +9 -3
  127. package/src/host/agent-host.ts +1 -1
  128. package/src/host/bridge-host.ts +13 -11
  129. package/src/host/plane-host.ts +2 -2
  130. package/src/index.ts +25 -0
  131. package/src/native-dev-bundle-url.ts +2 -2
  132. package/src/screenshots/orchestrate.ts +1 -1
  133. package/src/screenshots/org-deck.ts +1 -1
  134. package/src/settings-bundle.ts +1 -1
  135. package/src/sim-client.ts +4 -0
  136. package/src/vite-plugin-one.ts +2 -2
  137. package/src/vite-plugin.ts +1 -1
  138. package/cli/commands/box/cloud-box.ts +0 -294
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.381 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.383 | (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;
@@ -8,8 +8,8 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
8
8
  var __getProtoOf = Object.getPrototypeOf;
9
9
  var __hasOwnProp = Object.prototype.hasOwnProperty;
10
10
  var __export = (target, all) => {
11
- for (var name in all)
12
- __defProp(target, name, { get: all[name], enumerable: true });
11
+ for (var name2 in all)
12
+ __defProp(target, name2, { get: all[name2], enumerable: true });
13
13
  };
14
14
  var __copyProps = (to, from, except, desc) => {
15
15
  if (from && typeof from === "object" || typeof from === "function") {
@@ -35,6 +35,7 @@ __export(src_exports, {
35
35
  DEFAULT_SOOTSIM_BRIDGE_PORT: () => DEFAULT_SOOTSIM_BRIDGE_PORT,
36
36
  DEFAULT_SOOTSIM_BRIDGE_URL: () => DEFAULT_SOOTSIM_BRIDGE_URL,
37
37
  DEFAULT_SOOTSIM_SHELL_URL: () => DEFAULT_SOOTSIM_SHELL_URL,
38
+ RNX_SCREEN_CAPTURE_VERSION: () => RNX_SCREEN_CAPTURE_VERSION,
38
39
  SimClient: () => SimClient,
39
40
  SimClientError: () => SimClientError,
40
41
  SimConnectError: () => SimConnectError,
@@ -44,9 +45,14 @@ __export(src_exports, {
44
45
  SimResetError: () => SimResetError,
45
46
  SimTapError: () => SimTapError,
46
47
  connect: () => connect,
48
+ isRnxCaptureRegionAtlas: () => isRnxCaptureRegionAtlas,
49
+ isRnxScreenCapture: () => isRnxScreenCapture,
47
50
  jumpToSourceBabelPlugin: () => sootsimJumpToSourceBabelPlugin,
48
51
  metroPlugin: () => metro_plugin_default,
49
- rnxPlugin: () => rnxPlugin
52
+ rnxCaptureRasterRegions: () => rnxCaptureRasterRegions,
53
+ rnxPlugin: () => rnxPlugin,
54
+ rnxRasterFrames: () => rnxRasterFrames,
55
+ selectRnxRasterNodeIds: () => selectRnxRasterNodeIds
50
56
  });
51
57
  module.exports = __toCommonJS(src_exports);
52
58
 
@@ -208,8 +214,32 @@ function tryBuildDarwinCommFfi() {
208
214
  }
209
215
  }
210
216
 
217
+ // 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");
221
+ 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)()}`;
224
+ let descriptor = null;
225
+ 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)}
229
+ `);
230
+ (0, import_node_fs.fsyncSync)(descriptor);
231
+ (0, import_node_fs.closeSync)(descriptor);
232
+ descriptor = null;
233
+ (0, import_node_fs.renameSync)(temporary, filepath);
234
+ } catch (error) {
235
+ if (descriptor !== null) (0, import_node_fs.closeSync)(descriptor);
236
+ (0, import_node_fs.rmSync)(temporary, { force: true });
237
+ throw error;
238
+ }
239
+ }
240
+
211
241
  // cli/current-sim.ts
212
- var CURRENT_SIM_VERSION = 1;
242
+ var CURRENT_SIM_VERSION = 2;
213
243
  var CURRENT_SIM_PATH_ENV = "SOOTSIM_CLI_CURRENT_SIM_PATH";
214
244
  var IDENTITY_ENV_VARS = [
215
245
  "RNX_CLI_IDENTITY",
@@ -260,10 +290,10 @@ function findTopAncestor(start, maxDepth = 20) {
260
290
  return pid;
261
291
  }
262
292
  function getCliIdentity() {
263
- for (const name of IDENTITY_ENV_VARS) {
264
- const value = process.env[name];
293
+ for (const name2 of IDENTITY_ENV_VARS) {
294
+ const value = process.env[name2];
265
295
  if (value && value.trim()) {
266
- return { key: `${name}:${value.trim()}`, source: name, stable: true };
296
+ return { key: `${name2}:${value.trim()}`, source: name2, stable: true };
267
297
  }
268
298
  }
269
299
  const topPid = findTopAncestor(process.ppid);
@@ -282,35 +312,50 @@ function getCurrentSimPath() {
282
312
  const key = getCliIdentityKey();
283
313
  return (0, import_path.join)((0, import_os.tmpdir)(), `rnx-current-sim-${key}.json`);
284
314
  }
285
- function readCurrentSim() {
315
+ function legacyCloudCredentialDirectory(filepath) {
316
+ return (0, import_path.join)((0, import_path.dirname)(filepath), `${(0, import_path.basename)(filepath)}.credentials`);
317
+ }
318
+ function readCurrentTarget() {
286
319
  const filepath = getCurrentSimPath();
320
+ (0, import_fs2.rmSync)(legacyCloudCredentialDirectory(filepath), { recursive: true, force: true });
287
321
  if (!(0, import_fs2.existsSync)(filepath)) return null;
288
322
  try {
289
323
  const parsed = JSON.parse((0, import_fs2.readFileSync)(filepath, "utf8"));
290
- if (parsed.version !== CURRENT_SIM_VERSION || typeof parsed.simId !== "string" || !parsed.simId.trim() || typeof parsed.updatedAt !== "string") {
324
+ if (parsed === null || typeof parsed !== "object") {
291
325
  (0, import_fs2.rmSync)(filepath, { force: true });
292
326
  return null;
293
327
  }
294
- return {
328
+ const version = Reflect.get(parsed, "version");
329
+ const plane = Reflect.get(parsed, "plane");
330
+ const simId = Reflect.get(parsed, "simId");
331
+ const updatedAt = Reflect.get(parsed, "updatedAt");
332
+ if (version !== 1 && version !== CURRENT_SIM_VERSION || version === CURRENT_SIM_VERSION && plane !== "local" || typeof simId !== "string" || !simId.trim() || typeof updatedAt !== "string") {
333
+ (0, import_fs2.rmSync)(filepath, { force: true });
334
+ return null;
335
+ }
336
+ const target = {
295
337
  version: CURRENT_SIM_VERSION,
296
- simId: parsed.simId.trim(),
297
- updatedAt: parsed.updatedAt
338
+ plane: "local",
339
+ simId: simId.trim(),
340
+ updatedAt
298
341
  };
342
+ if (version === 1) writeSecureJsonAtomic(filepath, target);
343
+ return target;
299
344
  } catch {
300
345
  (0, import_fs2.rmSync)(filepath, { force: true });
301
346
  return null;
302
347
  }
303
348
  }
304
349
  function readCurrentSimId() {
305
- return readCurrentSim()?.simId || null;
350
+ return readCurrentTarget()?.simId ?? null;
306
351
  }
307
352
  function clearCurrentSimId() {
308
353
  (0, import_fs2.rmSync)(getCurrentSimPath(), { force: true });
309
354
  }
310
355
 
311
356
  // cli/ws-bridge.ts
312
- var import_node_fs2 = __toESM(require("node:fs"), 1);
313
- var import_node_path2 = __toESM(require("node:path"), 1);
357
+ var import_node_fs3 = __toESM(require("node:fs"), 1);
358
+ var import_node_path3 = __toESM(require("node:path"), 1);
314
359
  var import_ws = require("ws");
315
360
 
316
361
  // src/bridge-constants.ts
@@ -340,9 +385,9 @@ function resolveSootsimBridgePort(input = {}) {
340
385
  var DEFAULT_SOOTSIM_SHELL_URL = "http://localhost:5173/";
341
386
 
342
387
  // src/home-paths.ts
343
- var import_node_fs = __toESM(require("node:fs"), 1);
388
+ var import_node_fs2 = __toESM(require("node:fs"), 1);
344
389
  var import_node_os = require("node:os");
345
- var import_node_path = __toESM(require("node:path"), 1);
390
+ var import_node_path2 = __toESM(require("node:path"), 1);
346
391
  var RNX_HOME_ENV = "RNX_HOME";
347
392
  var ACTIVE_RUNTIME_FILE = "active";
348
393
  var DAEMON_LOCKFILE = "daemon.json";
@@ -350,27 +395,27 @@ var DEV_BRIDGES_DIR = "dev-bridges";
350
395
  var DAEMON_HEARTBEAT_STALE_MS = 3e4;
351
396
  function rnxHomeDir() {
352
397
  const override = process.env[RNX_HOME_ENV];
353
- if (override && override.length > 0) return import_node_path.default.resolve(override);
354
- return import_node_path.default.join((0, import_node_os.homedir)(), ".rnx");
398
+ if (override && override.length > 0) return import_node_path2.default.resolve(override);
399
+ return import_node_path2.default.join((0, import_node_os.homedir)(), ".rnx");
355
400
  }
356
401
  function runtimesDir() {
357
- return import_node_path.default.join(rnxHomeDir(), "runtimes");
402
+ return import_node_path2.default.join(rnxHomeDir(), "runtimes");
358
403
  }
359
404
  function runtimeDir(version) {
360
- return import_node_path.default.join(runtimesDir(), version);
405
+ return import_node_path2.default.join(runtimesDir(), version);
361
406
  }
362
407
  function activeRuntimeFile() {
363
- return import_node_path.default.join(runtimesDir(), ACTIVE_RUNTIME_FILE);
408
+ return import_node_path2.default.join(runtimesDir(), ACTIVE_RUNTIME_FILE);
364
409
  }
365
410
  function daemonLockfilePath() {
366
- return import_node_path.default.join(rnxHomeDir(), DAEMON_LOCKFILE);
411
+ return import_node_path2.default.join(rnxHomeDir(), DAEMON_LOCKFILE);
367
412
  }
368
413
  function devBridgesDir() {
369
- return import_node_path.default.join(rnxHomeDir(), DEV_BRIDGES_DIR);
414
+ return import_node_path2.default.join(rnxHomeDir(), DEV_BRIDGES_DIR);
370
415
  }
371
416
  function readActiveRuntime() {
372
417
  try {
373
- const value = import_node_fs.default.readFileSync(activeRuntimeFile(), "utf8").trim();
418
+ const value = import_node_fs2.default.readFileSync(activeRuntimeFile(), "utf8").trim();
374
419
  return value.length > 0 ? value : null;
375
420
  } catch {
376
421
  return null;
@@ -379,10 +424,10 @@ function readActiveRuntime() {
379
424
  var DAEMON_LOCKFILE_MAX_BYTES = 16 * 1024;
380
425
  function readDaemonLockfile() {
381
426
  try {
382
- const fd = import_node_fs.default.openSync(daemonLockfilePath(), "r");
427
+ const fd = import_node_fs2.default.openSync(daemonLockfilePath(), "r");
383
428
  try {
384
429
  const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES);
385
- const bytesRead = import_node_fs.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
430
+ const bytesRead = import_node_fs2.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
386
431
  const raw = buf.subarray(0, bytesRead).toString("utf8");
387
432
  const parsed = JSON.parse(raw);
388
433
  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"))) {
@@ -390,7 +435,7 @@ function readDaemonLockfile() {
390
435
  }
391
436
  return null;
392
437
  } finally {
393
- import_node_fs.default.closeSync(fd);
438
+ import_node_fs2.default.closeSync(fd);
394
439
  }
395
440
  } catch {
396
441
  return null;
@@ -410,7 +455,7 @@ function parseDevBridgeLockfile(raw) {
410
455
  function readDevBridgeLockfiles() {
411
456
  let entries;
412
457
  try {
413
- entries = import_node_fs.default.readdirSync(devBridgesDir(), { withFileTypes: true });
458
+ entries = import_node_fs2.default.readdirSync(devBridgesDir(), { withFileTypes: true });
414
459
  } catch {
415
460
  return [];
416
461
  }
@@ -418,11 +463,11 @@ function readDevBridgeLockfiles() {
418
463
  for (const entry of entries) {
419
464
  if (!entry.isFile() || !/^\d+\.json$/.test(entry.name)) continue;
420
465
  try {
421
- const filePath = import_node_path.default.join(devBridgesDir(), entry.name);
422
- const fd = import_node_fs.default.openSync(filePath, "r");
466
+ const filePath = import_node_path2.default.join(devBridgesDir(), entry.name);
467
+ const fd = import_node_fs2.default.openSync(filePath, "r");
423
468
  try {
424
469
  const buf = Buffer.alloc(DAEMON_LOCKFILE_MAX_BYTES);
425
- const bytesRead = import_node_fs.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
470
+ const bytesRead = import_node_fs2.default.readSync(fd, buf, 0, DAEMON_LOCKFILE_MAX_BYTES, 0);
426
471
  const lockfile = parseDevBridgeLockfile(
427
472
  buf.subarray(0, bytesRead).toString("utf8")
428
473
  );
@@ -430,7 +475,7 @@ function readDevBridgeLockfiles() {
430
475
  lockfiles.push(lockfile);
431
476
  }
432
477
  } finally {
433
- import_node_fs.default.closeSync(fd);
478
+ import_node_fs2.default.closeSync(fd);
434
479
  }
435
480
  } catch {
436
481
  }
@@ -458,6 +503,38 @@ function isDevBridgeLockfileFresh(lock, now = Date.now()) {
458
503
  }
459
504
  }
460
505
 
506
+ // src/cloud-contract.ts
507
+ var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
508
+ var RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024;
509
+
510
+ // src/public-brand.ts
511
+ var name = "rnx";
512
+ var rnxPublicBrand = Object.freeze({
513
+ name,
514
+ packageName: "rnxsim",
515
+ commandName: "rnx",
516
+ domain: "rnxsim.com",
517
+ origin: "https://rnxsim.com"
518
+ });
519
+
520
+ // src/backend-origin.ts
521
+ function normalizeHostname(hostname) {
522
+ return hostname.replace(/^\[|\]$/g, "").toLowerCase();
523
+ }
524
+ function isLoopbackHost(hostname) {
525
+ const normalized = normalizeHostname(hostname);
526
+ return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
527
+ }
528
+
529
+ // cli/cloud-session.ts
530
+ var MAX_ENCODED_SESSION_BYTES = 32 * 1024;
531
+ var RNX_SHELL_UPDATE_PREFIX = "__RNX_SHELL_UPDATE_V1__";
532
+ var RNX_SHELL_UPDATE_SET_PREFIX = `${RNX_SHELL_UPDATE_PREFIX}set:`;
533
+ var RNX_SHELL_UPDATE_UNSET = `${RNX_SHELL_UPDATE_PREFIX}unset`;
534
+
535
+ // cli/cloud-client.ts
536
+ var DEFAULT_RNX_CLOUD_ORIGIN = rnxPublicBrand.origin;
537
+
461
538
  // cli/ws-bridge.ts
462
539
  function bridgeWsUrl(wsPort) {
463
540
  return `ws://localhost:${wsPort}`;
@@ -467,17 +544,17 @@ function liveDevBridgeLockfiles() {
467
544
  return readDevBridgeLockfiles().filter((lockfile) => isDevBridgeLockfileFresh(lockfile));
468
545
  }
469
546
  function checkoutRoot(cwd) {
470
- let current = import_node_path2.default.resolve(cwd);
547
+ let current = import_node_path3.default.resolve(cwd);
471
548
  while (true) {
472
- if (import_node_fs2.default.existsSync(import_node_path2.default.join(current, ".git"))) return current;
473
- const parent = import_node_path2.default.dirname(current);
549
+ if (import_node_fs3.default.existsSync(import_node_path3.default.join(current, ".git"))) return current;
550
+ const parent = import_node_path3.default.dirname(current);
474
551
  if (parent === current) return null;
475
552
  current = parent;
476
553
  }
477
554
  }
478
555
  function pathContains(parent, child) {
479
- const relative = import_node_path2.default.relative(parent, child);
480
- return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path2.default.sep}`) && !import_node_path2.default.isAbsolute(relative);
556
+ const relative = import_node_path3.default.relative(parent, child);
557
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path3.default.sep}`) && !import_node_path3.default.isAbsolute(relative);
481
558
  }
482
559
  function resolveDefaultDevBridgeLockfile() {
483
560
  const lockfiles = liveDevBridgeLockfiles();
@@ -638,6 +715,7 @@ function createBridge(wsPort, opts = {}) {
638
715
  }
639
716
  });
640
717
  return {
718
+ plane: "local",
641
719
  async send(cmd, callOpts) {
642
720
  const effectiveTimeoutMs = callOpts?.timeoutMs ?? commandTimeoutMs;
643
721
  const sendOnce = async (defaultSimId) => {
@@ -1917,6 +1995,39 @@ async function inspectWarnings(bridge, limit = 20) {
1917
1995
  return Array.isArray(result) ? result : [];
1918
1996
  }
1919
1997
  async function inspectLogs(bridge) {
1998
+ if (bridge.plane === "cloud") {
1999
+ const state = await bridge.send({
2000
+ type: "state",
2001
+ stateOptions: {
2002
+ screenshot: false,
2003
+ tree: false,
2004
+ route: false,
2005
+ errors: false,
2006
+ logs: true,
2007
+ logLimit: 100
2008
+ }
2009
+ });
2010
+ const recentLogs = state && typeof state === "object" && Array.isArray(Reflect.get(state, "recentLogs")) ? Reflect.get(state, "recentLogs") : [];
2011
+ return recentLogs.flatMap((entry, index) => {
2012
+ if (!entry || typeof entry !== "object") return [];
2013
+ const text = Reflect.get(entry, "text");
2014
+ const rawLevel = Reflect.get(entry, "level");
2015
+ if (typeof text !== "string") return [];
2016
+ const level = rawLevel === "log" || rawLevel === "info" || rawLevel === "warn" || rawLevel === "error" || rawLevel === "debug" ? rawLevel : "error";
2017
+ const at = Reflect.get(entry, "at");
2018
+ const ts = typeof at === "number" && Number.isFinite(at) ? at : 0;
2019
+ return [
2020
+ {
2021
+ id: `cloud-${ts}-${index}`,
2022
+ source: "rnx-cloud",
2023
+ level,
2024
+ ts,
2025
+ args: [text],
2026
+ stack: null
2027
+ }
2028
+ ];
2029
+ });
2030
+ }
1920
2031
  const res = await bridge.send({
1921
2032
  type: "evaluate",
1922
2033
  code: `(() => {
@@ -1951,7 +2062,7 @@ function filterLogEntries(entries, opts = {}) {
1951
2062
  if (!opts.showInternal) {
1952
2063
  out = out.filter((e) => {
1953
2064
  const first = e.args[0];
1954
- return !(typeof first === "string" && first.startsWith("[rnx]"));
2065
+ return !(typeof first === "string" && /^\[(?:rnx|sootsim)(?:\s[^\]]*)?\]/.test(first));
1955
2066
  });
1956
2067
  }
1957
2068
  if (opts.level) out = out.filter((e) => opts.level.has(e.level));
@@ -2037,9 +2148,8 @@ function normalizeAppUrlTarget(target) {
2037
2148
  async function openAppUrl(bridge, target) {
2038
2149
  const normalized = normalizeAppUrlTarget(target);
2039
2150
  const result = await bridge.send({
2040
- type: "call",
2041
- path: "SootSim.bridges.test.openDeepLink",
2042
- args: [normalized]
2151
+ type: "openUrl",
2152
+ url: normalized
2043
2153
  });
2044
2154
  if (result !== null && typeof result === "object" && Reflect.get(result, "ok") === false) {
2045
2155
  const error = Reflect.get(result, "error");
@@ -2212,6 +2322,9 @@ var SimClient = class {
2212
2322
  openUrl(target) {
2213
2323
  return openAppUrl(this, target);
2214
2324
  }
2325
+ async setAppearance(appearance) {
2326
+ await this.send({ type: "setAppearance", appearance });
2327
+ }
2215
2328
  /**
2216
2329
  * block until the guest app has mounted and painted real content.
2217
2330
  * throws SimReadyTimeoutError (with the last probe status) on timeout.
@@ -2535,17 +2648,195 @@ async function connect(options = {}) {
2535
2648
  return new SimClient(bridge, picked.id, { commandTimeoutMs });
2536
2649
  }
2537
2650
 
2651
+ // src/capture-contract.ts
2652
+ var RNX_SCREEN_CAPTURE_VERSION = 1;
2653
+ function selectRnxRasterNodeIds(tree) {
2654
+ const nodesById = new Map(tree.nodes.map((node) => [node.nodeId, node]));
2655
+ const unsupported = /* @__PURE__ */ new Set();
2656
+ for (const node of tree.nodes) {
2657
+ const visibleFrame = node.visibleFrame ?? node.frame;
2658
+ if (!visibleFrame || visibleFrame.width <= 0 || visibleFrame.height <= 0) continue;
2659
+ if (node.type === "masked-view" || node.style._liquidGlass !== void 0 || node.style._liquidGlassEdge !== void 0) {
2660
+ unsupported.add(node.nodeId);
2661
+ }
2662
+ }
2663
+ for (const nodeId of [...unsupported]) {
2664
+ let parentNodeId = nodesById.get(nodeId)?.parentNodeId ?? null;
2665
+ while (parentNodeId !== null) {
2666
+ if (unsupported.has(parentNodeId)) {
2667
+ unsupported.delete(nodeId);
2668
+ break;
2669
+ }
2670
+ parentNodeId = nodesById.get(parentNodeId)?.parentNodeId ?? null;
2671
+ }
2672
+ }
2673
+ const screenArea = tree.width * tree.height;
2674
+ const maxLayerArea = screenArea * 0.25;
2675
+ const maxAtlasArea = screenArea * 0.5;
2676
+ const candidates = [...unsupported].map((nodeId) => {
2677
+ const node = nodesById.get(nodeId);
2678
+ const frame = node?.visibleFrame ?? node?.frame;
2679
+ return frame ? { nodeId, area: frame.width * frame.height } : null;
2680
+ }).filter(
2681
+ (candidate) => candidate !== null && candidate.area <= maxLayerArea
2682
+ ).sort((left, right) => left.area - right.area || left.nodeId - right.nodeId);
2683
+ const selected = /* @__PURE__ */ new Set();
2684
+ let atlasArea = 0;
2685
+ for (const candidate of candidates) {
2686
+ if (atlasArea + candidate.area > maxAtlasArea) continue;
2687
+ selected.add(candidate.nodeId);
2688
+ atlasArea += candidate.area;
2689
+ }
2690
+ return selected;
2691
+ }
2692
+ function rnxRasterFrames(tree, nodeIds) {
2693
+ const frames = /* @__PURE__ */ new Map();
2694
+ for (const node of tree.nodes) {
2695
+ if (!nodeIds.has(node.nodeId)) continue;
2696
+ const frame = node.frame ?? {
2697
+ x: node.absolutePosition.x,
2698
+ y: node.absolutePosition.y,
2699
+ width: node.layout.width,
2700
+ height: node.layout.height
2701
+ };
2702
+ const liquidGlass = node.style._liquidGlass;
2703
+ const capturePadding = liquidGlass && typeof liquidGlass === "object" && typeof Reflect.get(liquidGlass, "capturePadding") === "number" ? Reflect.get(liquidGlass, "capturePadding") : 0;
2704
+ const shadowRadius = typeof node.style.shadowRadius === "number" ? node.style.shadowRadius : 0;
2705
+ const shadowOffset = node.style.shadowOffset;
2706
+ const shadowOffsetX = shadowOffset && typeof shadowOffset === "object" && typeof Reflect.get(shadowOffset, "width") === "number" ? Reflect.get(shadowOffset, "width") : 0;
2707
+ const shadowOffsetY = shadowOffset && typeof shadowOffset === "object" && typeof Reflect.get(shadowOffset, "height") === "number" ? Reflect.get(shadowOffset, "height") : 0;
2708
+ const left = Math.max(
2709
+ 0,
2710
+ frame.x - Math.max(capturePadding, shadowRadius - shadowOffsetX)
2711
+ );
2712
+ const top = Math.max(
2713
+ 0,
2714
+ frame.y - Math.max(capturePadding, shadowRadius - shadowOffsetY)
2715
+ );
2716
+ const right = Math.min(
2717
+ tree.width,
2718
+ frame.x + frame.width + Math.max(capturePadding, shadowRadius + shadowOffsetX)
2719
+ );
2720
+ const bottom = Math.min(
2721
+ tree.height,
2722
+ frame.y + frame.height + Math.max(capturePadding, shadowRadius + shadowOffsetY)
2723
+ );
2724
+ if (right > left && bottom > top) {
2725
+ frames.set(node.nodeId, {
2726
+ x: left,
2727
+ y: top,
2728
+ width: right - left,
2729
+ height: bottom - top
2730
+ });
2731
+ }
2732
+ }
2733
+ return frames;
2734
+ }
2735
+ function rnxCaptureRasterRegions(capture) {
2736
+ const tree = {
2737
+ width: capture.tree.viewport.width,
2738
+ height: capture.tree.viewport.height,
2739
+ nodes: capture.tree.nodes.map((node) => ({
2740
+ nodeId: node.nodeId,
2741
+ parentNodeId: node.parentNodeId,
2742
+ type: node.type,
2743
+ style: node.style.resolved,
2744
+ layout: node.layout.local,
2745
+ frame: node.layout.frame,
2746
+ absolutePosition: node.layout.absolutePosition,
2747
+ visibleFrame: node.layout.visibleFrame
2748
+ }))
2749
+ };
2750
+ const nodeIds = selectRnxRasterNodeIds(tree);
2751
+ const frames = rnxRasterFrames(tree, nodeIds);
2752
+ return [...nodeIds].flatMap((nodeId) => {
2753
+ const frame = frames.get(nodeId);
2754
+ return frame ? [{ key: String(nodeId), nodeId, frame }] : [];
2755
+ });
2756
+ }
2757
+ function captureRecord(value) {
2758
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2759
+ }
2760
+ function captureInteger(value) {
2761
+ return typeof value === "number" && Number.isSafeInteger(value);
2762
+ }
2763
+ function captureNumber(value) {
2764
+ return typeof value === "number" && Number.isFinite(value);
2765
+ }
2766
+ function captureNullableString(value) {
2767
+ return value === null || typeof value === "string";
2768
+ }
2769
+ function captureRect(value) {
2770
+ return captureRecord(value) && captureNumber(value.x) && captureNumber(value.y) && captureNumber(value.width) && captureNumber(value.height);
2771
+ }
2772
+ function captureOptionalString(value) {
2773
+ return value === void 0 || typeof value === "string";
2774
+ }
2775
+ function captureOptionalNullableString(value) {
2776
+ return value === void 0 || captureNullableString(value);
2777
+ }
2778
+ function captureFontRegistration(value) {
2779
+ return captureRecord(value) && typeof value.family === "string" && value.family.length > 0 && captureOptionalString(value.url) && captureOptionalString(value.sourceLabel) && captureOptionalNullableString(value.parsedFamily) && captureOptionalNullableString(value.parsedPostScript) && captureOptionalNullableString(value.fullName);
2780
+ }
2781
+ function capturePoint(value) {
2782
+ return captureRecord(value) && captureNumber(value.x) && captureNumber(value.y);
2783
+ }
2784
+ function captureNode(value) {
2785
+ if (!captureRecord(value) || !captureInteger(value.nodeId) || !(value.parentNodeId === null || captureInteger(value.parentNodeId)) || !Array.isArray(value.childNodeIds) || !value.childNodeIds.every(captureInteger) || !captureInteger(value.paintIndex) || typeof value.type !== "string" || !captureNullableString(value.id) || !captureNullableString(value.testID) || !captureNullableString(value.text) || !captureInteger(value.childCount) || !captureRecord(value.accessibility) || !captureRecord(value.interaction) || !captureRecord(value.layout) || !captureRecord(value.style) || !captureRecord(value.source) || !captureRecord(value.props) || !captureRecord(value.inspection)) {
2786
+ return false;
2787
+ }
2788
+ const accessibility = value.accessibility;
2789
+ if (typeof accessibility.accessible !== "boolean" || !captureNullableString(accessibility.label) || !captureNullableString(accessibility.role) || !captureNullableString(accessibility.hint) || !(accessibility.liveRegion === null || accessibility.liveRegion === "none" || accessibility.liveRegion === "polite" || accessibility.liveRegion === "assertive") || !(accessibility.state === null || captureRecord(accessibility.state)) || !(accessibility.value === null || captureRecord(accessibility.value))) {
2790
+ return false;
2791
+ }
2792
+ const interaction = value.interaction;
2793
+ if (typeof interaction.pressed !== "boolean" || typeof interaction.pressable !== "boolean" || typeof interaction.textInput !== "boolean" || !(interaction.textSelection === null || captureRecord(interaction.textSelection)) || typeof interaction.secureTextEntry !== "boolean" || !captureNullableString(interaction.placeholder) || !capturePoint(interaction.scrollOffset) || !(interaction.zoomScale === null || captureNumber(interaction.zoomScale)) || typeof interaction.nativePinchEnabled !== "boolean" || !(interaction.contentOffsetProp === null || captureRecord(interaction.contentOffsetProp)) || !(interaction.strokeDashoffsetProp === null || captureNumber(interaction.strokeDashoffsetProp))) {
2794
+ return false;
2795
+ }
2796
+ const layout = value.layout;
2797
+ if (!captureRect(layout.local) || !(layout.frame === null || captureRect(layout.frame)) || !capturePoint(layout.absolutePosition) || !(layout.visibleFrame === null || captureRect(layout.visibleFrame))) {
2798
+ return false;
2799
+ }
2800
+ const style = value.style;
2801
+ return captureRecord(style.resolved) && captureRecord(style.authored) && captureRecord(style.computed) && captureRecord(style.tokenBindings) && (value.textLines === void 0 || Array.isArray(value.textLines));
2802
+ }
2803
+ function isRnxScreenCapture(value) {
2804
+ if (!captureRecord(value) || value.contractVersion !== RNX_SCREEN_CAPTURE_VERSION || !captureInteger(value.capturedAt) || !captureRecord(value.tree) || !(value.fonts === void 0 || Array.isArray(value.fonts) && value.fonts.length <= 200 && value.fonts.every(captureFontRegistration)) || !(value.visual === void 0 || captureRecord(value.visual))) {
2805
+ return false;
2806
+ }
2807
+ const tree = value.tree;
2808
+ const visual = value.visual;
2809
+ return captureRecord(tree.viewport) && captureInteger(tree.viewport.width) && tree.viewport.width > 0 && captureInteger(tree.viewport.height) && tree.viewport.height > 0 && captureInteger(tree.rootNodeId) && Array.isArray(tree.nodes) && tree.nodes.every(captureNode) && (visual === void 0 || visual.kind === "png" && (visual.producer === "headless-cpu-app-surface" || visual.producer === "browser-compositor-app-surface") && typeof visual.dataUri === "string" && visual.dataUri.startsWith("data:image/png;base64,") && captureInteger(visual.pixelWidth) && visual.pixelWidth > 0 && captureInteger(visual.pixelHeight) && visual.pixelHeight > 0);
2810
+ }
2811
+ function isRnxCaptureRegionAtlas(value) {
2812
+ if (!captureRecord(value) || value.kind !== "png-atlas" || value.producer !== "headless-cpu-app-surface" || typeof value.dataUri !== "string" || !value.dataUri.startsWith("data:image/png;base64,") || !captureNumber(value.width) || value.width <= 0 || !captureNumber(value.height) || value.height <= 0 || !captureInteger(value.pixelWidth) || value.pixelWidth <= 0 || !captureInteger(value.pixelHeight) || value.pixelHeight <= 0 || !Array.isArray(value.regions) || value.regions.length === 0 || value.regions.length > 256) {
2813
+ return false;
2814
+ }
2815
+ const scale = value.pixelWidth / value.width;
2816
+ if (!Number.isInteger(scale) || scale < 1 || scale > 4 || Math.abs(value.height * scale - value.pixelHeight) > 1e-6) {
2817
+ return false;
2818
+ }
2819
+ const keys = /* @__PURE__ */ new Set();
2820
+ for (const region of value.regions) {
2821
+ if (!captureRecord(region) || typeof region.key !== "string" || region.key.length === 0 || keys.has(region.key) || !captureInteger(region.nodeId) || region.nodeId <= 0 || !captureRect(region.frame) || region.frame.width <= 0 || region.frame.height <= 0 || !captureRect(region.source) || region.source.x < 0 || region.source.y < 0 || region.source.width !== region.frame.width || region.source.height !== region.frame.height || region.source.x + region.source.width > value.width || region.source.y + region.source.height > value.height) {
2822
+ return false;
2823
+ }
2824
+ keys.add(region.key);
2825
+ }
2826
+ return true;
2827
+ }
2828
+
2538
2829
  // src/vite-plugin-one.ts
2539
2830
  var import_fs4 = __toESM(require("fs"), 1);
2540
- var import_node_crypto2 = require("node:crypto");
2831
+ var import_node_crypto3 = require("node:crypto");
2541
2832
  var import_path3 = __toESM(require("path"), 1);
2542
2833
 
2543
2834
  // src/dev-server-open.ts
2544
2835
  var import_node_child_process = require("node:child_process");
2545
- var import_node_crypto = require("node:crypto");
2546
- var import_node_fs3 = __toESM(require("node:fs"), 1);
2836
+ var import_node_crypto2 = require("node:crypto");
2837
+ var import_node_fs4 = __toESM(require("node:fs"), 1);
2547
2838
  var import_node_http = __toESM(require("node:http"), 1);
2548
- var import_node_path3 = __toESM(require("node:path"), 1);
2839
+ var import_node_path4 = __toESM(require("node:path"), 1);
2549
2840
  var import_node_url = require("node:url");
2550
2841
  var scheduledMetroOpens = /* @__PURE__ */ new Set();
2551
2842
  function resolveRnxDesktopAppName(open, projectRoot) {
@@ -2555,8 +2846,8 @@ function resolveRnxDesktopAppName(open, projectRoot) {
2555
2846
  if (!appName) throw new Error("rnx: open.appName must not be empty");
2556
2847
  return appName;
2557
2848
  }
2558
- const packageJsonPath = import_node_path3.default.join(projectRoot, "package.json");
2559
- const packageJson = JSON.parse(import_node_fs3.default.readFileSync(packageJsonPath, "utf8"));
2849
+ const packageJsonPath = import_node_path4.default.join(projectRoot, "package.json");
2850
+ const packageJson = JSON.parse(import_node_fs4.default.readFileSync(packageJsonPath, "utf8"));
2560
2851
  const packageName = packageJson && typeof packageJson === "object" ? Reflect.get(packageJson, "name") : void 0;
2561
2852
  if (typeof packageName !== "string" || !packageName.trim()) {
2562
2853
  throw new Error(`rnx: ${packageJsonPath} must declare a package name`);
@@ -2570,7 +2861,7 @@ function tagRnxBundleForPlugin(bundleUrl, replacementOrigin, replacementToken, p
2570
2861
  url.searchParams.set("rnxAppName", appName);
2571
2862
  url.searchParams.set(
2572
2863
  "rnxProjectId",
2573
- (0, import_node_crypto.createHash)("sha256").update(import_node_fs3.default.realpathSync(projectRoot)).digest("hex").slice(0, 32)
2864
+ (0, import_node_crypto2.createHash)("sha256").update(import_node_fs4.default.realpathSync(projectRoot)).digest("hex").slice(0, 32)
2574
2865
  );
2575
2866
  }
2576
2867
  url.searchParams.set("rnxReplacementOrigin", replacementOrigin);
@@ -2605,8 +2896,8 @@ function observeRnxDesktopLaunch(child, appName) {
2605
2896
  });
2606
2897
  }
2607
2898
  function launchRnxDesktop(bundleUrl, appName) {
2608
- const packageRoot = import_node_path3.default.resolve(import_node_path3.default.dirname((0, import_node_url.fileURLToPath)(__sootsim_import_meta_url)), "..");
2609
- const binPath = import_node_path3.default.join(packageRoot, "dist-cli", "bin.js");
2899
+ const packageRoot = import_node_path4.default.resolve(import_node_path4.default.dirname((0, import_node_url.fileURLToPath)(__sootsim_import_meta_url)), "..");
2900
+ const binPath = import_node_path4.default.join(packageRoot, "dist-cli", "bin.js");
2610
2901
  console.log(`[rnx] opening ${appName} in the desktop simulator`);
2611
2902
  const child = (0, import_node_child_process.spawn)(
2612
2903
  process.execPath,
@@ -2696,22 +2987,11 @@ function openRnxDesktopWhenMetroIsReady({
2696
2987
 
2697
2988
  // src/host/replacement-module-handler.ts
2698
2989
  var import_promises = __toESM(require("node:fs/promises"), 1);
2699
- var import_node_path4 = __toESM(require("node:path"), 1);
2990
+ var import_node_path5 = __toESM(require("node:path"), 1);
2700
2991
  var import_esbuild = require("esbuild");
2701
-
2702
- // src/backend-origin.ts
2703
- function normalizeHostname(hostname) {
2704
- return hostname.replace(/^\[|\]$/g, "").toLowerCase();
2705
- }
2706
- function isLoopbackHost(hostname) {
2707
- const normalized = normalizeHostname(hostname);
2708
- return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
2709
- }
2710
-
2711
- // src/host/replacement-module-handler.ts
2712
2992
  var REPLACEMENT_MODULE_ROUTE = "/__sootsim-replacement-module";
2713
2993
  function getReplacementLoader(filePath) {
2714
- const ext = import_node_path4.default.extname(filePath).toLowerCase();
2994
+ const ext = import_node_path5.default.extname(filePath).toLowerCase();
2715
2995
  switch (ext) {
2716
2996
  case ".js":
2717
2997
  return "js";
@@ -2793,7 +3073,7 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
2793
3073
  res.end("replacement files must be .js, .jsx, .ts, or .tsx");
2794
3074
  return true;
2795
3075
  }
2796
- if (!import_node_path4.default.isAbsolute(targetPath)) {
3076
+ if (!import_node_path5.default.isAbsolute(targetPath)) {
2797
3077
  res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" });
2798
3078
  res.end("replacement file path must be absolute");
2799
3079
  return true;
@@ -2801,10 +3081,10 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
2801
3081
  const runtimePackages = new Set(url.searchParams.getAll("runtimePackage"));
2802
3082
  let resolvedProjectRoot;
2803
3083
  if (options.projectRoot) {
2804
- const lexicalProjectRoot = import_node_path4.default.resolve(options.projectRoot);
3084
+ const lexicalProjectRoot = import_node_path5.default.resolve(options.projectRoot);
2805
3085
  resolvedProjectRoot = await import_promises.default.realpath(options.projectRoot);
2806
- const lexicalTarget = import_node_path4.default.relative(lexicalProjectRoot, targetPath);
2807
- if (lexicalTarget.startsWith("..") || import_node_path4.default.isAbsolute(lexicalTarget)) {
3086
+ const lexicalTarget = import_node_path5.default.relative(lexicalProjectRoot, targetPath);
3087
+ if (lexicalTarget.startsWith("..") || import_node_path5.default.isAbsolute(lexicalTarget)) {
2808
3088
  res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
2809
3089
  res.end("replacement file must be inside the project root");
2810
3090
  return true;
@@ -2820,8 +3100,8 @@ async function handleReplacementModuleRequest(req, res, options = {}) {
2820
3100
  return true;
2821
3101
  }
2822
3102
  if (resolvedProjectRoot) {
2823
- const relativeTarget = import_node_path4.default.relative(resolvedProjectRoot, resolvedTargetPath);
2824
- if (relativeTarget.startsWith("..") || import_node_path4.default.isAbsolute(relativeTarget)) {
3103
+ const relativeTarget = import_node_path5.default.relative(resolvedProjectRoot, resolvedTargetPath);
3104
+ if (relativeTarget.startsWith("..") || import_node_path5.default.isAbsolute(relativeTarget)) {
2825
3105
  res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
2826
3106
  res.end("replacement file must be inside the project root");
2827
3107
  return true;
@@ -3062,7 +3342,7 @@ function rnxPlugin(options = {}) {
3062
3342
  if (options.enabled === false) return;
3063
3343
  const bundleUrl = options.bundleUrl || "/node_modules/one/metro-entry.bundle?platform=ios&dev=true&minify=false";
3064
3344
  const appName = resolveRnxDesktopAppName(options.open, server.config.root);
3065
- const replacementToken = (0, import_node_crypto2.randomUUID)();
3345
+ const replacementToken = (0, import_node_crypto3.randomUUID)();
3066
3346
  const configuredPort = server.config.server.port || 8081;
3067
3347
  const listeningPort = () => {
3068
3348
  const address = server.httpServer?.address();
@@ -3187,7 +3467,7 @@ function rnxPlugin(options = {}) {
3187
3467
  // src/metro-plugin.ts
3188
3468
  var import_fs5 = __toESM(require("fs"), 1);
3189
3469
  var import_module2 = require("module");
3190
- var import_node_crypto3 = require("node:crypto");
3470
+ var import_node_crypto4 = require("node:crypto");
3191
3471
  var import_path4 = __toESM(require("path"), 1);
3192
3472
 
3193
3473
  // src/metro-production-bundle.ts
@@ -3284,7 +3564,7 @@ function withRNX(config, options = {}) {
3284
3564
  const configuredBundleUrl = options.bundleUrl || "/index.bundle?platform=ios&dev=true&hot=true&minify=false";
3285
3565
  const cfgServer = config.server;
3286
3566
  const port = resolveMetroPort(cfgServer);
3287
- const metroServerId = (0, import_node_crypto3.randomUUID)();
3567
+ const metroServerId = (0, import_node_crypto4.randomUUID)();
3288
3568
  const bundleUrl = tagRnxBundleForPlugin(
3289
3569
  configuredBundleUrl,
3290
3570
  `http://localhost:${port}`,
@@ -3522,6 +3802,7 @@ function sootsimJumpToSourceBabelPlugin(api, options = {}) {
3522
3802
  DEFAULT_SOOTSIM_BRIDGE_PORT,
3523
3803
  DEFAULT_SOOTSIM_BRIDGE_URL,
3524
3804
  DEFAULT_SOOTSIM_SHELL_URL,
3805
+ RNX_SCREEN_CAPTURE_VERSION,
3525
3806
  SimClient,
3526
3807
  SimClientError,
3527
3808
  SimConnectError,
@@ -3531,7 +3812,12 @@ function sootsimJumpToSourceBabelPlugin(api, options = {}) {
3531
3812
  SimResetError,
3532
3813
  SimTapError,
3533
3814
  connect,
3815
+ isRnxCaptureRegionAtlas,
3816
+ isRnxScreenCapture,
3534
3817
  jumpToSourceBabelPlugin,
3535
3818
  metroPlugin,
3536
- rnxPlugin
3819
+ rnxCaptureRasterRegions,
3820
+ rnxPlugin,
3821
+ rnxRasterFrames,
3822
+ selectRnxRasterNodeIds
3537
3823
  });