rnxsim 0.1.519 → 0.1.521

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 (71) hide show
  1. package/cli/cloud-client.ts +419 -287
  2. package/cli/cloud-dispatch.ts +2 -21
  3. package/cli/cloud-session.ts +31 -85
  4. package/cli/commands/box.ts +15 -11
  5. package/cli/commands/inspect.ts +34 -1
  6. package/cli/commands/platform.ts +184 -232
  7. package/cli/main.ts +10 -2
  8. package/cli/outbound-endpoints.ts +21 -6
  9. package/dist-lib/agent-daemon-client.cjs +1 -1
  10. package/dist-lib/agent-events.cjs +1 -1
  11. package/dist-lib/agent-identity.cjs +1 -1
  12. package/dist-lib/agent-sessions.cjs +1 -1
  13. package/dist-lib/attached-projects.cjs +1 -1
  14. package/dist-lib/auth/shared-session.cjs +1 -1
  15. package/dist-lib/backend-origin.cjs +1 -1
  16. package/dist-lib/beta.cjs +1 -1
  17. package/dist-lib/beta.mjs +1 -1
  18. package/dist-lib/bridge-constants.cjs +1 -1
  19. package/dist-lib/bridge-contract-input.cjs +7 -2
  20. package/dist-lib/bridge-contract-input.mjs +7 -2
  21. package/dist-lib/bridge-contract.cjs +1 -1
  22. package/dist-lib/bridge-contract.mjs +1 -1
  23. package/dist-lib/capture-contract.cjs +1 -1
  24. package/dist-lib/capture-contract.mjs +1 -1
  25. package/dist-lib/cli-constants.cjs +1 -1
  26. package/dist-lib/cloud-contract.cjs +1 -1
  27. package/dist-lib/cloud-contract.mjs +1 -1
  28. package/dist-lib/cloud.cjs +18 -2
  29. package/dist-lib/cloud.mjs +18 -2
  30. package/dist-lib/config.cjs +1 -1
  31. package/dist-lib/detox/index.cjs +1 -1
  32. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  33. package/dist-lib/home-paths.cjs +1 -1
  34. package/dist-lib/host/bridge-host.cjs +112 -39
  35. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  36. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  37. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  38. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  39. package/dist-lib/host/websocket-proxy.cjs +1 -1
  40. package/dist-lib/index.cjs +44 -117
  41. package/dist-lib/jump-to-source-babel.cjs +1 -1
  42. package/dist-lib/jump-to-source-native.cjs +1 -1
  43. package/dist-lib/menu.cjs +30 -1
  44. package/dist-lib/menu.mjs +27 -1
  45. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  46. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  47. package/dist-lib/metro-production-bundle.cjs +1 -1
  48. package/dist-lib/metro-production-bundle.mjs +1 -1
  49. package/dist-lib/metro.cjs +1 -1
  50. package/dist-lib/profiles.cjs +1 -1
  51. package/dist-lib/public-brand.cjs +1 -1
  52. package/dist-lib/react-native-host-modules.cjs +1 -1
  53. package/dist-lib/react-native-host-modules.mjs +1 -1
  54. package/dist-lib/render-mode.cjs +1 -1
  55. package/dist-lib/scripts/dev-server-scanner.cjs +41 -4
  56. package/dist-lib/sdk.cjs +1 -1
  57. package/dist-lib/sdk.mjs +1 -1
  58. package/dist-lib/skills.cjs +138 -164
  59. package/dist-lib/swift.cjs +766 -24
  60. package/dist-lib/vite.cjs +103 -30
  61. package/package.json +1 -1
  62. package/scripts/dev-server-scanner.ts +72 -5
  63. package/src/bridge-contract-input.ts +10 -0
  64. package/src/bridge-contract.ts +5 -0
  65. package/src/cloud-contract.ts +7 -1
  66. package/src/cloud.ts +45 -6
  67. package/src/connect.ts +1 -1
  68. package/src/menu.ts +44 -0
  69. package/src/swift.ts +2 -0
  70. package/src/vite-plugin-one.ts +12 -0
  71. package/src/vite-plugin-swift.ts +141 -39
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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 __defProp = Object.defineProperty;
@@ -388,9 +388,14 @@ function parseResetOptions(value) {
388
388
  if (strategy !== "data" && strategy !== "full") {
389
389
  return validationFailure("reset.strategy", "must be data or full");
390
390
  }
391
+ const from = input.from;
392
+ if (from !== void 0 && (typeof from !== "string" || from.length === 0 || from.length > 64)) {
393
+ return validationFailure("reset.from", "must name a simulator");
394
+ }
391
395
  return {
392
396
  strategy,
393
- relaunch: optionalBoolean(input, "relaunch", "reset") ?? true
397
+ relaunch: optionalBoolean(input, "relaunch", "reset") ?? true,
398
+ ...from === void 0 ? {} : { from }
394
399
  };
395
400
  }
396
401
  function parseScreenshotOptions(value) {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_MAX_MS = 5e3;
@@ -352,9 +352,14 @@ function parseResetOptions(value) {
352
352
  if (strategy !== "data" && strategy !== "full") {
353
353
  return validationFailure("reset.strategy", "must be data or full");
354
354
  }
355
+ const from = input.from;
356
+ if (from !== void 0 && (typeof from !== "string" || from.length === 0 || from.length > 64)) {
357
+ return validationFailure("reset.from", "must name a simulator");
358
+ }
355
359
  return {
356
360
  strategy,
357
- relaunch: optionalBoolean(input, "relaunch", "reset") ?? true
361
+ relaunch: optionalBoolean(input, "relaunch", "reset") ?? true,
362
+ ...from === void 0 ? {} : { from }
358
363
  };
359
364
  }
360
365
  function parseScreenshotOptions(value) {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var REFUSED_PERFORM_STEP_TYPES = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/capture-contract.ts
4
4
  var RNX_SCREEN_CAPTURE_VERSION = 1;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/cloud-contract.ts
4
4
  var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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 __defProp = Object.defineProperty;
@@ -406,7 +406,23 @@ var RemoteSimulator = class {
406
406
  }
407
407
  return result;
408
408
  },
409
- reset: () => this.sendCommand({ type: "reset" }),
409
+ reset: (options) => {
410
+ if (options?.strategy !== void 0 && options.strategy !== "data" && options.strategy !== "full") {
411
+ throw new Error("RNX Cloud reset strategy must be data or full");
412
+ }
413
+ if (options?.from !== void 0 && (typeof options.from !== "string" || options.from.length === 0)) {
414
+ throw new Error("RNX Cloud reset from must name a simulator");
415
+ }
416
+ return this.sendCommand(
417
+ options === void 0 ? { type: "reset" } : {
418
+ type: "reset",
419
+ resetOptions: {
420
+ ...options.strategy === void 0 ? {} : { strategy: options.strategy },
421
+ ...options.from === void 0 ? {} : { from: options.from }
422
+ }
423
+ }
424
+ );
425
+ },
410
426
  state: (options) => this.sendCommand(
411
427
  options === void 0 ? { type: "state" } : { type: "state", stateOptions: options }
412
428
  )
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -376,7 +376,23 @@ var RemoteSimulator = class {
376
376
  }
377
377
  return result;
378
378
  },
379
- reset: () => this.sendCommand({ type: "reset" }),
379
+ reset: (options) => {
380
+ if (options?.strategy !== void 0 && options.strategy !== "data" && options.strategy !== "full") {
381
+ throw new Error("RNX Cloud reset strategy must be data or full");
382
+ }
383
+ if (options?.from !== void 0 && (typeof options.from !== "string" || options.from.length === 0)) {
384
+ throw new Error("RNX Cloud reset from must name a simulator");
385
+ }
386
+ return this.sendCommand(
387
+ options === void 0 ? { type: "reset" } : {
388
+ type: "reset",
389
+ resetOptions: {
390
+ ...options.strategy === void 0 ? {} : { strategy: options.strategy },
391
+ ...options.from === void 0 ? {} : { from: options.from }
392
+ }
393
+ }
394
+ );
395
+ },
380
396
  state: (options) => this.sendCommand(
381
397
  options === void 0 ? { type: "state" } : { type: "state", stateOptions: options }
382
398
  )
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -847,10 +847,11 @@ function listDeviceModels() {
847
847
  function getRuntimePlatform(model) {
848
848
  return getDevice(model).platform;
849
849
  }
850
- var LEGACY_HARDWARE_BUTTONS, PIXEL_HARDWARE_BUTTONS, GALAXY_HARDWARE_BUTTONS, ANDROID_GESTURE_NAV_BAR_HEIGHT, ANDROID_OS_VERSION, ANDROID_API_LEVEL, PIXEL_8_WIDTH, PIXEL_8_HEIGHT, PIXEL_8_STATUS_BAR_HEIGHT, devices, DEFAULT_DEVICE, DEFAULT_DEVICE_BY_PLATFORM;
850
+ var import_menu, LEGACY_HARDWARE_BUTTONS, PIXEL_HARDWARE_BUTTONS, GALAXY_HARDWARE_BUTTONS, ANDROID_GESTURE_NAV_BAR_HEIGHT, ANDROID_OS_VERSION, ANDROID_API_LEVEL, PIXEL_8_WIDTH, PIXEL_8_HEIGHT, PIXEL_8_STATUS_BAR_HEIGHT, devices, DEFAULT_DEVICE, DEFAULT_DEVICE_BY_PLATFORM;
851
851
  var init_devices = __esm({
852
852
  "../sootsim-engine/src/settings/devices.ts"() {
853
853
  "use strict";
854
+ import_menu = require("rnxsim/menu");
854
855
  LEGACY_HARDWARE_BUTTONS = {
855
856
  width: 2.5,
856
857
  lock: { side: "right", top: 188.7, height: 68.1 },
@@ -1247,6 +1248,9 @@ var init_devices = __esm({
1247
1248
  )
1248
1249
  }),
1249
1250
  "iphone-duo": {
1251
+ // offered on contributor surfaces only while the 27 home and the hinge
1252
+ // measurements are still settling (nate, 2026-09-15).
1253
+ developmentOnly: true,
1250
1254
  ...iosDevice({
1251
1255
  name: "iPhone Duo",
1252
1256
  width: 466,
@@ -1554,6 +1558,9 @@ var init_metro_production_bundle = __esm({
1554
1558
  });
1555
1559
 
1556
1560
  // ../contrast-bundler/src/metroAssetArtifact.ts
1561
+ function isEmbeddedAssetDescriptor(descriptor) {
1562
+ return descriptor.fileUris?.length === descriptor.scales.length && descriptor.fileHashes?.length === descriptor.scales.length;
1563
+ }
1557
1564
  function assetDescriptorKey(descriptor) {
1558
1565
  return `${descriptor.httpServerLocation}::${descriptor.name}::${descriptor.type}::${descriptor.hash}`;
1559
1566
  }
@@ -2145,9 +2152,27 @@ async function embedMetroAssetFiles(bundle, read) {
2145
2152
  const sizes = [];
2146
2153
  let variants = 0;
2147
2154
  let bytes = 0;
2155
+ const kept = /* @__PURE__ */ new Set();
2148
2156
  for (const site of sites) {
2149
2157
  const key = assetDescriptorKey(site.descriptor);
2150
- if (embedded.has(key)) continue;
2158
+ if (embedded.has(key) || kept.has(key)) continue;
2159
+ if (isEmbeddedAssetDescriptor(site.descriptor)) {
2160
+ kept.add(key);
2161
+ let descriptorBytes2 = 0;
2162
+ for (const uri of site.descriptor.fileUris ?? []) {
2163
+ const encoded = uri.slice(uri.indexOf(",") + 1);
2164
+ const padding = encoded.endsWith("==") ? 2 : encoded.endsWith("=") ? 1 : 0;
2165
+ const length = Math.floor(encoded.length * 3 / 4) - padding;
2166
+ variants++;
2167
+ bytes += length;
2168
+ descriptorBytes2 += length;
2169
+ }
2170
+ sizes.push({
2171
+ asset: `${site.descriptor.httpServerLocation}/${site.descriptor.name}.${site.descriptor.type}`,
2172
+ bytes: descriptorBytes2
2173
+ });
2174
+ continue;
2175
+ }
2151
2176
  const fileUris = [];
2152
2177
  const fileHashes = [];
2153
2178
  const loaded = /* @__PURE__ */ new Map();
@@ -2202,7 +2227,7 @@ async function embedMetroAssetFiles(bundle, read) {
2202
2227
  return {
2203
2228
  code,
2204
2229
  assets: {
2205
- descriptors: embedded.size,
2230
+ descriptors: embedded.size + kept.size,
2206
2231
  variants,
2207
2232
  bytes,
2208
2233
  sizes: sortedAssetSizes(sizes)
@@ -3559,21 +3584,26 @@ function normalizeCloudOrigin(source) {
3559
3584
  return parsed.origin;
3560
3585
  }
3561
3586
  function createCloudSession(input) {
3587
+ const boxId = input.boxId?.trim() ?? "";
3588
+ const boxToken = input.boxToken ?? "";
3589
+ if (boxId === "" !== (boxToken === "")) {
3590
+ throw new Error("cloud session names a box with both its id and its token");
3591
+ }
3562
3592
  const session = {
3563
3593
  version: CLOUD_SESSION_VERSION,
3564
3594
  service: "sim",
3565
- boxId: input.boxId.trim(),
3566
- boxToken: input.boxToken,
3567
3595
  simId: input.simId.trim(),
3568
3596
  apiOrigin: normalizeCloudOrigin(input.apiOrigin.trim()),
3569
3597
  claimId: input.claimId.trim(),
3570
3598
  token: input.token,
3599
+ ...input.streamUrl ? { streamUrl: input.streamUrl } : {},
3571
3600
  ...input.platform ? { platform: input.platform } : {},
3572
3601
  ...input.device?.trim() ? { device: input.device.trim() } : {},
3573
- ...input.artifact ? { artifact: input.artifact } : {}
3602
+ ...input.artifact ? { artifact: input.artifact } : {},
3603
+ ...boxId && boxToken ? { boxId, boxToken } : {}
3574
3604
  };
3575
- if (!session.boxId || !session.boxToken || !session.simId || !session.claimId || !session.token) {
3576
- throw new Error("cloud session requires box, simulator, origin, claim, and tokens");
3605
+ if (!session.simId || !session.claimId || !session.token) {
3606
+ throw new Error("cloud session requires a simulator, an origin, a claim, and a token");
3577
3607
  }
3578
3608
  return session;
3579
3609
  }
@@ -3648,7 +3678,8 @@ function failureSentence(text) {
3648
3678
  }
3649
3679
  if (!isRecord4(parsed)) return null;
3650
3680
  if (isRecord4(parsed.error) && typeof parsed.error.message === "string") {
3651
- return parsed.error.message;
3681
+ const code = typeof parsed.error.code === "string" && parsed.error.code ? ` [${parsed.error.code}]` : "";
3682
+ return `${parsed.error.message}${code}`;
3652
3683
  }
3653
3684
  if (typeof parsed.message === "string") return parsed.message;
3654
3685
  if (typeof parsed.error === "string") return parsed.error;
@@ -3970,10 +4001,21 @@ async function inferCloudModuleIdentity(source) {
3970
4001
  async function produceCloudArtifact(bundlePath, maxBytes = MAX_RNX_CLOUD_BUNDLE_BYTES) {
3971
4002
  return produceArtifact(bundlePath, maxBytes, REMOTE_COMMAND, "allow-placeholders");
3972
4003
  }
3973
- async function produceArtifact(bundlePath, maxBytes, command, assetPolicy) {
4004
+ async function embedBundleAssets(bundlePath, command, assetPolicy) {
3974
4005
  const source = await loadBundleSource(bundlePath);
3975
- const descriptors = extractAssetDescriptors(source);
3976
- const search = descriptors.length > 0 && !isRemoteBundleInput(bundlePath) ? searchMetroAssetDest(bundlePath, descriptors) : { dest: null, searched: [], truncated: false, unconfirmed: false };
4006
+ const descriptors = extractAssetDescriptors(source).filter(
4007
+ (descriptor) => !isEmbeddedAssetDescriptor(descriptor)
4008
+ );
4009
+ if (descriptors.length === 0) {
4010
+ return {
4011
+ source,
4012
+ code: source,
4013
+ search: { dest: null, searched: [], truncated: false, unconfirmed: false },
4014
+ dest: null,
4015
+ substituted: []
4016
+ };
4017
+ }
4018
+ const search = !isRemoteBundleInput(bundlePath) ? searchMetroAssetDest(bundlePath, descriptors) : { dest: null, searched: [], truncated: false, unconfirmed: false };
3977
4019
  const dest = search.dest;
3978
4020
  const placements = /* @__PURE__ */ new Map();
3979
4021
  descriptors.forEach((descriptor, index) => {
@@ -4022,7 +4064,17 @@ async function produceArtifact(bundlePath, maxBytes, command, assetPolicy) {
4022
4064
  `${command} could not read ${(0, import_node_path15.join)(dest?.directory ?? ".", classic)} for ${variant.descriptor.name}.${variant.descriptor.type} at scale ${variant.scale}, and has no stand-in for a .${variant.descriptor.type} asset. Rebuild with \`react-native bundle --assets-dest\`.`
4023
4065
  );
4024
4066
  });
4025
- const artifact = await buildRnxCloudArtifact(embedded.code, {
4067
+ return {
4068
+ source,
4069
+ code: embedded.code,
4070
+ search,
4071
+ dest,
4072
+ substituted
4073
+ };
4074
+ }
4075
+ async function produceArtifact(bundlePath, maxBytes, command, assetPolicy) {
4076
+ const prepared = await embedBundleAssets(bundlePath, command, assetPolicy);
4077
+ const artifact = await buildRnxCloudArtifact(prepared.code, {
4026
4078
  inferModuleIdentity: inferCloudModuleIdentity
4027
4079
  });
4028
4080
  const bytes = Buffer.from(artifact.code, "utf8");
@@ -4033,14 +4085,14 @@ async function produceArtifact(bundlePath, maxBytes, command, assetPolicy) {
4033
4085
  );
4034
4086
  }
4035
4087
  const warnings = [];
4036
- if (search.unconfirmed && dest) {
4088
+ if (prepared.search.unconfirmed && prepared.dest) {
4037
4089
  warnings.push(
4038
- `${command} read this bundle's assets from ${dest.directory} on layout alone: every asset declares a scale that directory does not hold, so not one of Metro's hashes could be reproduced. Confirm these are the images this bundle was built against.`
4090
+ `${command} read this bundle's assets from ${prepared.dest.directory} on layout alone: every asset declares a scale that directory does not hold, so not one of Metro's hashes could be reproduced. Confirm these are the images this bundle was built against.`
4039
4091
  );
4040
4092
  }
4041
- if (substituted.length > 0) {
4093
+ if (prepared.substituted.length > 0) {
4042
4094
  warnings.push(
4043
- `${command} embedded a placeholder image for ${substituted.length} asset(s) no scale of which is in ${search.searched.join(", ") || "any asset dest directory"}: ${substituted.join(", ")}. Rebuild with \`react-native bundle --assets-dest\` to ship the real images.`
4095
+ `${command} embedded a placeholder image for ${prepared.substituted.length} asset(s) no scale of which is in ${prepared.search.searched.join(", ") || "any asset dest directory"}: ${prepared.substituted.join(", ")}. Rebuild with \`react-native bundle --assets-dest\` to ship the real images.`
4044
4096
  );
4045
4097
  }
4046
4098
  if (bytes.length >= Math.floor(maxBytes * 0.8)) {
@@ -4052,7 +4104,7 @@ async function produceArtifact(bundlePath, maxBytes, command, assetPolicy) {
4052
4104
  bytes,
4053
4105
  sha256: (0, import_node_crypto5.createHash)("sha256").update(bytes).digest("hex"),
4054
4106
  receipt: artifact.receipt,
4055
- assetsDirectory: dest?.directory ?? null,
4107
+ assetsDirectory: prepared.dest?.directory ?? null,
4056
4108
  warnings
4057
4109
  };
4058
4110
  }
@@ -4086,9 +4138,6 @@ function readCreateResponse(value, expectedBytes, expectedSha256) {
4086
4138
  }
4087
4139
  async function createCloudBox(options) {
4088
4140
  const platform = options.platform ?? "ios";
4089
- if (platform === "android") {
4090
- throw new UnsupportedRemoteHostError("android", "nano");
4091
- }
4092
4141
  const apiOrigin = resolveCloudOrigin(options.apiOrigin);
4093
4142
  const artifact = await produceCloudArtifact(options.bundlePath);
4094
4143
  const boxName = options.boxName?.trim();
@@ -4158,7 +4207,7 @@ async function createCloudBox(options) {
4158
4207
  warnings: artifact.warnings
4159
4208
  };
4160
4209
  }
4161
- var import_node_crypto5, import_node_fs14, import_node_path15, UnsupportedRemoteHostError, DEFAULT_RNX_CLOUD_ORIGIN, MAX_RNX_CLOUD_BUNDLE_BYTES, REMOTE_COMMAND, MAX_ASSET_DEST_FILES, ACCOUNT_CHOICE_REFUSALS, PROJECT_ONLY_DIRECTORIES, MAX_PLACEMENT_COMBINATIONS;
4210
+ var import_node_crypto5, import_node_fs14, import_node_path15, DEFAULT_RNX_CLOUD_ORIGIN, MAX_RNX_CLOUD_BUNDLE_BYTES, REMOTE_COMMAND, MAX_ASSET_DEST_FILES, ACCOUNT_CHOICE_REFUSALS, PROJECT_ONLY_DIRECTORIES, MAX_PLACEMENT_COMBINATIONS;
4162
4211
  var init_cloud_client = __esm({
4163
4212
  "cli/cloud-client.ts"() {
4164
4213
  "use strict";
@@ -4176,19 +4225,6 @@ var init_cloud_client = __esm({
4176
4225
  init_auth();
4177
4226
  init_cloud_session();
4178
4227
  init_detect();
4179
- UnsupportedRemoteHostError = class extends Error {
4180
- code = "unsupported_host";
4181
- platform;
4182
- host;
4183
- constructor(platform, host, message) {
4184
- super(
4185
- message ?? `rnx remote ${platform} does not support --${host}: rnxsim/cloud only creates iOS simulators`
4186
- );
4187
- this.name = "UnsupportedRemoteHostError";
4188
- this.platform = platform;
4189
- this.host = host;
4190
- }
4191
- };
4192
4228
  DEFAULT_RNX_CLOUD_ORIGIN = rnxPublicBrand.origin;
4193
4229
  MAX_RNX_CLOUD_BUNDLE_BYTES = RNX_CLOUD_MAX_ARTIFACT_BYTES;
4194
4230
  REMOTE_COMMAND = "rnx remote";
@@ -5004,12 +5040,32 @@ function safeParseManifest(body) {
5004
5040
  return null;
5005
5041
  }
5006
5042
  }
5043
+ var localIconPathCache = /* @__PURE__ */ new Map();
5044
+ var localIconProbeInFlight = /* @__PURE__ */ new Set();
5045
+ function metroAssetPath(rawIcon) {
5046
+ return `/assets/${rawIcon.replace(/^\.?\//, "")}`;
5047
+ }
5048
+ function staticAssetPath(rawIcon) {
5049
+ return `/${rawIcon.replace(/^\.?\//, "")}`;
5050
+ }
5051
+ async function resolveLocalIconPath(hostname, port, rawIcon) {
5052
+ const candidates = [metroAssetPath(rawIcon), staticAssetPath(rawIcon)];
5053
+ for (const candidate of candidates) {
5054
+ const res = await httpGet(hostname, port, candidate, "HEAD", ENRICH_TIMEOUT_MS);
5055
+ if (res?.statusCode === 200) {
5056
+ localIconPathCache.set(port, candidate);
5057
+ portCache.delete(port);
5058
+ return;
5059
+ }
5060
+ }
5061
+ }
5007
5062
  function applyManifest(result, manifestRes, buildIconProxyUrl) {
5008
5063
  if (!manifestRes) return result;
5009
5064
  try {
5010
5065
  const manifest = JSON.parse(manifestRes.body);
5011
5066
  const client = manifest?.extra?.expoClient || manifest?.extra || {};
5012
5067
  if (client.name) result.projectName = client.name;
5068
+ if (typeof client.primaryColor === "string") result.primaryColor = client.primaryColor;
5013
5069
  if (client.ios?.bundleIdentifier) result.bundleId = client.ios.bundleIdentifier;
5014
5070
  if (result.framework === "metro" && client.sdkVersion) result.framework = "expo";
5015
5071
  const rawLaunchUrl = manifest?.launchAsset?.url;
@@ -5041,9 +5097,9 @@ function applyManifest(result, manifestRes, buildIconProxyUrl) {
5041
5097
  resolvedIconUrl = rawIcon;
5042
5098
  }
5043
5099
  } else {
5044
- const cleanPath = rawIcon.replace(/^\.?\//, "");
5045
- const assetPath = cleanPath.startsWith("assets/") ? `/${cleanPath}` : `/assets/${cleanPath}`;
5046
- const localUrl = `${buildLoopbackProbeBaseUrl("http:", manifestRes.hostname, result.port)}${assetPath}`;
5100
+ const base = buildLoopbackProbeBaseUrl("http:", manifestRes.hostname, result.port);
5101
+ const assetPath = localIconPathCache.get(result.port) ?? metroAssetPath(rawIcon);
5102
+ const localUrl = `${base}${assetPath}`;
5047
5103
  resolvedIconUrl = buildIconProxyUrl ? buildIconProxyUrl(localUrl) : localUrl;
5048
5104
  }
5049
5105
  if (resolvedIconUrl) {
@@ -5057,7 +5113,20 @@ function applyManifest(result, manifestRes, buildIconProxyUrl) {
5057
5113
  var ENRICH_TIMEOUT_MS = 2e4;
5058
5114
  var enrichmentCache = /* @__PURE__ */ new Map();
5059
5115
  var enrichmentInFlight = /* @__PURE__ */ new Set();
5116
+ function ensureLocalIconPathResolved(result) {
5117
+ const rawIcon = result.iconPath;
5118
+ if (!rawIcon) return;
5119
+ if (rawIcon.startsWith("http://") || rawIcon.startsWith("https://")) return;
5120
+ if (localIconPathCache.has(result.port)) return;
5121
+ if (localIconProbeInFlight.has(result.port)) return;
5122
+ localIconProbeInFlight.add(result.port);
5123
+ const hostname = normalizeHostname(new URL(result.bundleUrl).hostname);
5124
+ void resolveLocalIconPath(hostname, result.port, rawIcon).finally(
5125
+ () => localIconProbeInFlight.delete(result.port)
5126
+ );
5127
+ }
5060
5128
  function enrichWithManifest(result, buildIconProxyUrl) {
5129
+ ensureLocalIconPathResolved(result);
5061
5130
  if (result.projectName) return result;
5062
5131
  const cached3 = enrichmentCache.get(result.port);
5063
5132
  if (cached3) {
@@ -5285,6 +5354,9 @@ async function scanDevServers(opts = {}) {
5285
5354
  for (const p of [...enrichmentCache.keys()]) {
5286
5355
  if (!currentPorts.has(p)) enrichmentCache.delete(p);
5287
5356
  }
5357
+ for (const p of [...localIconPathCache.keys()]) {
5358
+ if (!currentPorts.has(p)) localIconPathCache.delete(p);
5359
+ }
5288
5360
  const results = [];
5289
5361
  const toProbe = [];
5290
5362
  for (const { port, pid } of processes) {
@@ -5298,6 +5370,7 @@ async function scanDevServers(opts = {}) {
5298
5370
  knownNonExpo.delete(port);
5299
5371
  knownOne.delete(port);
5300
5372
  enrichmentCache.delete(port);
5373
+ localIconPathCache.delete(port);
5301
5374
  }
5302
5375
  toProbe.push({ port, pid });
5303
5376
  }
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/host/fetch-proxy-overrides.ts
4
4
  var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.519 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.521 | (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 __defProp = Object.defineProperty;