rnxsim 0.1.431 → 0.1.433

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 (78) hide show
  1. package/README.md +20 -50
  2. package/cli/auth.ts +33 -3
  3. package/cli/cloud-client.ts +524 -88
  4. package/cli/cloud-dispatch.ts +5 -10
  5. package/cli/cloud-session.ts +5 -5
  6. package/cli/commands/auth.ts +13 -2
  7. package/cli/commands/box.ts +189 -20
  8. package/cli/commands/control.ts +40 -13
  9. package/cli/commands/device.ts +1 -1
  10. package/cli/commands/flow.ts +189 -43
  11. package/cli/commands/inspect/get-layout.ts +1 -1
  12. package/cli/commands/inspect.ts +8 -1
  13. package/cli/commands/login.ts +79 -14
  14. package/cli/commands/maestro-generate.ts +70 -54
  15. package/cli/commands/maestro.ts +69 -20
  16. package/cli/commands/platform.ts +30 -32
  17. package/cli/commands/screenshot.ts +3 -2
  18. package/cli/commands/upload.ts +28 -10
  19. package/cli/help-core.ts +11 -1
  20. package/cli/outbound-endpoints.ts +27 -5
  21. package/cli/parse-args.ts +17 -0
  22. package/cli/shell-init.ts +4 -4
  23. package/cli/ws-bridge.ts +109 -62
  24. package/dist-lib/agent-daemon-client.cjs +1 -1
  25. package/dist-lib/agent-events.cjs +1 -1
  26. package/dist-lib/agent-identity.cjs +1 -1
  27. package/dist-lib/agent-sessions.cjs +1 -1
  28. package/dist-lib/attached-projects.cjs +1 -1
  29. package/dist-lib/auth/shared-session.cjs +17 -4
  30. package/dist-lib/backend-origin.cjs +1 -1
  31. package/dist-lib/beta.cjs +1 -1
  32. package/dist-lib/beta.mjs +1 -1
  33. package/dist-lib/bridge-constants.cjs +1 -1
  34. package/dist-lib/bridge-contract-input.cjs +1 -1
  35. package/dist-lib/bridge-contract-input.mjs +1 -1
  36. package/dist-lib/bridge-contract.cjs +1 -1
  37. package/dist-lib/bridge-contract.mjs +1 -1
  38. package/dist-lib/capture-contract.cjs +1 -1
  39. package/dist-lib/capture-contract.mjs +1 -1
  40. package/dist-lib/cli-constants.cjs +1 -1
  41. package/dist-lib/cloud-contract.cjs +1 -1
  42. package/dist-lib/cloud-contract.mjs +1 -1
  43. package/dist-lib/cloud.cjs +108 -93
  44. package/dist-lib/cloud.mjs +106 -91
  45. package/dist-lib/config.cjs +1 -1
  46. package/dist-lib/detox/index.cjs +1 -1
  47. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  48. package/dist-lib/home-paths.cjs +1 -1
  49. package/dist-lib/host/bridge-host.cjs +1 -1
  50. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  51. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  52. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  53. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  54. package/dist-lib/host/websocket-proxy.cjs +1 -1
  55. package/dist-lib/index.cjs +58 -139
  56. package/dist-lib/jump-to-source-babel.cjs +1 -1
  57. package/dist-lib/menu.cjs +1 -1
  58. package/dist-lib/menu.mjs +1 -1
  59. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  60. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  61. package/dist-lib/metro-production-bundle.cjs +1 -1
  62. package/dist-lib/metro-production-bundle.mjs +1 -1
  63. package/dist-lib/metro.cjs +33 -212
  64. package/dist-lib/profiles.cjs +1 -1
  65. package/dist-lib/public-brand.cjs +1 -1
  66. package/dist-lib/react-native-host-modules.cjs +1 -1
  67. package/dist-lib/react-native-host-modules.mjs +1 -1
  68. package/dist-lib/render-mode.cjs +1 -1
  69. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  70. package/dist-lib/sdk.cjs +1 -1
  71. package/dist-lib/sdk.mjs +1 -1
  72. package/dist-lib/skills.cjs +436 -351
  73. package/dist-lib/vite.cjs +1 -1
  74. package/package.json +2 -1
  75. package/src/auth/shared-session.ts +31 -0
  76. package/src/cloud.ts +83 -113
  77. package/src/metro-plugin.ts +30 -113
  78. package/src/vite-plugin.ts +11 -2
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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;
@@ -22,10 +22,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
22
22
  // src/cloud.ts
23
23
  var cloud_exports = {};
24
24
  __export(cloud_exports, {
25
- NanoBox: () => NanoBox,
26
25
  RNX_CLOUD_COMMAND_TYPES: () => RNX_CLOUD_COMMAND_TYPES,
27
26
  RNX_CLOUD_MAX_ARTIFACT_BYTES: () => RNX_CLOUD_MAX_ARTIFACT_BYTES,
28
- createNanoBox: () => createNanoBox,
27
+ RemoteSimulator: () => RemoteSimulator,
28
+ createRemoteSimulator: () => createRemoteSimulator,
29
29
  isRnxCloudCommandType: () => isRnxCloudCommandType
30
30
  });
31
31
  module.exports = __toCommonJS(cloud_exports);
@@ -39,6 +39,63 @@ function isLoopbackHost(hostname) {
39
39
  return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
40
40
  }
41
41
 
42
+ // src/capture-contract.ts
43
+ var RNX_SCREEN_CAPTURE_VERSION = 1;
44
+ function captureRecord(value) {
45
+ return value !== null && typeof value === "object" && !Array.isArray(value);
46
+ }
47
+ function captureInteger(value) {
48
+ return typeof value === "number" && Number.isSafeInteger(value);
49
+ }
50
+ function captureNumber(value) {
51
+ return typeof value === "number" && Number.isFinite(value);
52
+ }
53
+ function captureNullableString(value) {
54
+ return value === null || typeof value === "string";
55
+ }
56
+ function captureRect(value) {
57
+ return captureRecord(value) && captureNumber(value.x) && captureNumber(value.y) && captureNumber(value.width) && captureNumber(value.height);
58
+ }
59
+ function captureOptionalString(value) {
60
+ return value === void 0 || typeof value === "string";
61
+ }
62
+ function captureOptionalNullableString(value) {
63
+ return value === void 0 || captureNullableString(value);
64
+ }
65
+ function captureFontRegistration(value) {
66
+ 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);
67
+ }
68
+ function capturePoint(value) {
69
+ return captureRecord(value) && captureNumber(value.x) && captureNumber(value.y);
70
+ }
71
+ function captureNode(value) {
72
+ 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)) {
73
+ return false;
74
+ }
75
+ const accessibility = value.accessibility;
76
+ 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))) {
77
+ return false;
78
+ }
79
+ const interaction = value.interaction;
80
+ 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))) {
81
+ return false;
82
+ }
83
+ const layout = value.layout;
84
+ if (!captureRect(layout.local) || !(layout.frame === null || captureRect(layout.frame)) || !capturePoint(layout.absolutePosition) || !(layout.visibleFrame === null || captureRect(layout.visibleFrame))) {
85
+ return false;
86
+ }
87
+ const style = value.style;
88
+ return captureRecord(style.resolved) && captureRecord(style.authored) && captureRecord(style.computed) && captureRecord(style.tokenBindings) && (value.textLines === void 0 || Array.isArray(value.textLines));
89
+ }
90
+ function isRnxScreenCapture(value) {
91
+ 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))) {
92
+ return false;
93
+ }
94
+ const tree = value.tree;
95
+ const visual = value.visual;
96
+ return captureRecord(tree.viewport) && captureInteger(tree.viewport.width) && tree.viewport.width > 0 && captureInteger(tree.viewport.height) && tree.viewport.height > 0 && captureInteger(tree.rootNodeId) && (tree.commitRevision === void 0 || captureInteger(tree.commitRevision)) && 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);
97
+ }
98
+
42
99
  // src/cloud-contract.ts
43
100
  var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
44
101
  var RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024;
@@ -200,13 +257,48 @@ function cloudCommand(command) {
200
257
  }
201
258
  return { ...clean, type: command.type };
202
259
  }
203
- var NanoBox = class _NanoBox {
260
+ var RemoteSimulator = class {
204
261
  session;
262
+ // driving verbs live here so the client reads the way the CLI does. bound to
263
+ // this simulator at construction, and every verb reads the session live, so a
264
+ // confirmed claim reaches commands issued after it.
265
+ rnx;
205
266
  constructor(session) {
206
267
  this.session = session;
207
- }
208
- get boxId() {
209
- return this.session.boxId;
268
+ this.rnx = {
269
+ command: (command, options) => this.sendCommand(command, options),
270
+ describe: () => this.sendCommand({ type: "tree" }),
271
+ do: (command) => this.sendCommand(command),
272
+ wait: (condition, options) => {
273
+ const timeoutMs = options?.maxMs ?? DEFAULT_WAIT_READY_MS;
274
+ const waitCondition = condition === "ready" ? { type: "ready" } : condition;
275
+ return this.sendCommand(
276
+ {
277
+ type: "waitFor",
278
+ waitForOptions: { condition: waitCondition, timeoutMs }
279
+ },
280
+ { timeoutMs: timeoutMs + 1e3 }
281
+ );
282
+ },
283
+ screenshot: async (options) => {
284
+ const result = await this.sendCommand({
285
+ type: "screenshot",
286
+ layers: options?.layers ?? "tenant"
287
+ });
288
+ if (typeof result !== "string" || !result.startsWith("data:image/")) {
289
+ throw new Error("RNX Cloud screenshot did not return an image data url");
290
+ }
291
+ return result;
292
+ },
293
+ capture: async () => {
294
+ const result = await this.sendCommand({ type: "capture" });
295
+ if (!isRnxScreenCapture(result)) {
296
+ throw new Error("RNX Cloud capture returned an invalid screen capture");
297
+ }
298
+ return result;
299
+ },
300
+ reset: () => this.sendCommand({ type: "reset" })
301
+ };
210
302
  }
211
303
  get simId() {
212
304
  return this.session.simId;
@@ -216,17 +308,12 @@ var NanoBox = class _NanoBox {
216
308
  }
217
309
  get receipt() {
218
310
  return {
219
- boxId: this.session.boxId,
220
- size: "nano",
311
+ simId: this.session.simId,
221
312
  artifact: this.session.artifact,
222
- simulator: {
223
- simId: this.session.simId,
224
- artifact: this.session.artifact,
225
- claim: this.session.claim
226
- }
313
+ claim: this.session.claim
227
314
  };
228
315
  }
229
- async command(command, options) {
316
+ async sendCommand(command, options) {
230
317
  const body = {
231
318
  id: globalThis.crypto.randomUUID(),
232
319
  claimId: this.session.claim.id,
@@ -268,35 +355,6 @@ var NanoBox = class _NanoBox {
268
355
  }
269
356
  return parsed.result;
270
357
  }
271
- describe() {
272
- return this.command({ type: "tree" });
273
- }
274
- do(command) {
275
- return this.command(command);
276
- }
277
- wait(condition, options) {
278
- const timeoutMs = options?.maxMs ?? DEFAULT_WAIT_READY_MS;
279
- const waitCondition = condition === "ready" ? { type: "ready" } : condition;
280
- return this.command(
281
- {
282
- type: "waitFor",
283
- waitForOptions: { condition: waitCondition, timeoutMs }
284
- },
285
- { timeoutMs: timeoutMs + 1e3 }
286
- );
287
- }
288
- screenshot(options) {
289
- return this.command({
290
- type: "screenshot",
291
- layers: options?.layers ?? "tenant"
292
- });
293
- }
294
- capture() {
295
- return this.command({ type: "capture" });
296
- }
297
- reset() {
298
- return this.command({ type: "reset" });
299
- }
300
358
  async confirm() {
301
359
  const response = await fetch(
302
360
  `${this.session.origin}/v1/sims/${encodeURIComponent(this.session.simId)}/claim`,
@@ -324,48 +382,6 @@ var NanoBox = class _NanoBox {
324
382
  this.session = { ...this.session, claim };
325
383
  return claim;
326
384
  }
327
- async addSimulator(input = {}) {
328
- const device = input.device?.trim() || DEFAULT_DEVICE;
329
- const response = await fetch(
330
- `${this.session.origin}/v1/boxes/${encodeURIComponent(this.session.boxId)}/simulators`,
331
- {
332
- method: "POST",
333
- headers: {
334
- authorization: `Bearer ${this.session.boxToken}`,
335
- "x-rnx-account-authorization": this.session.authorization,
336
- "content-type": "application/json"
337
- },
338
- body: JSON.stringify({
339
- device,
340
- ...input.storageFrom === void 0 ? {} : { storageFrom: input.storageFrom }
341
- })
342
- }
343
- );
344
- const text = await response.text();
345
- if (!response.ok) throw responseError(response.status, text);
346
- let parsed;
347
- try {
348
- parsed = JSON.parse(text);
349
- } catch {
350
- throw new Error("RNX Cloud simulator create returned unreadable JSON");
351
- }
352
- if (!isRecord(parsed) || parsed.boxId !== this.session.boxId) {
353
- throw new Error("RNX Cloud simulator create returned an invalid box id");
354
- }
355
- const simulator = readSimulatorCreate(
356
- parsed.simulator,
357
- this.session.artifact.id,
358
- this.session.artifact.bytes
359
- );
360
- const pooled = new _NanoBox({
361
- ...this.session,
362
- simId: simulator.simId,
363
- simulatorToken: simulator.token,
364
- claim: simulator.claim
365
- });
366
- await pooled.confirm();
367
- return pooled;
368
- }
369
385
  async close() {
370
386
  const response = await fetch(
371
387
  `${this.session.origin}/v1/boxes/${encodeURIComponent(this.session.boxId)}`,
@@ -380,7 +396,7 @@ var NanoBox = class _NanoBox {
380
396
  }
381
397
  }
382
398
  };
383
- async function createNanoBox(input) {
399
+ async function createRemoteSimulator(input) {
384
400
  if (input.artifact.byteLength > RNX_CLOUD_MAX_ARTIFACT_BYTES) {
385
401
  throw new Error(
386
402
  `rnx Cloud artifact is ${input.artifact.byteLength} bytes and exceeds the ${RNX_CLOUD_MAX_ARTIFACT_BYTES}-byte limit`
@@ -424,9 +440,8 @@ async function createNanoBox(input) {
424
440
  throw new Error("RNX Cloud create returned unreadable JSON");
425
441
  }
426
442
  const created = readBoxCreate(parsed, input.artifact.byteLength, sha256);
427
- const box = new NanoBox({
443
+ const simulator = new RemoteSimulator({
428
444
  origin,
429
- authorization: input.authorization,
430
445
  boxId: created.receipt.boxId,
431
446
  boxToken: created.boxToken,
432
447
  simId: created.receipt.simulator.simId,
@@ -434,14 +449,14 @@ async function createNanoBox(input) {
434
449
  claim: created.receipt.simulator.claim,
435
450
  artifact: created.receipt.artifact
436
451
  });
437
- await box.confirm();
438
- return box;
452
+ await simulator.confirm();
453
+ return simulator;
439
454
  }
440
455
  // Annotate the CommonJS export names for ESM import in node:
441
456
  0 && (module.exports = {
442
- NanoBox,
443
457
  RNX_CLOUD_COMMAND_TYPES,
444
458
  RNX_CLOUD_MAX_ARTIFACT_BYTES,
445
- createNanoBox,
459
+ RemoteSimulator,
460
+ createRemoteSimulator,
446
461
  isRnxCloudCommandType
447
462
  });
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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);
@@ -12,6 +12,63 @@ function isLoopbackHost(hostname) {
12
12
  return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
13
13
  }
14
14
 
15
+ // src/capture-contract.ts
16
+ var RNX_SCREEN_CAPTURE_VERSION = 1;
17
+ function captureRecord(value) {
18
+ return value !== null && typeof value === "object" && !Array.isArray(value);
19
+ }
20
+ function captureInteger(value) {
21
+ return typeof value === "number" && Number.isSafeInteger(value);
22
+ }
23
+ function captureNumber(value) {
24
+ return typeof value === "number" && Number.isFinite(value);
25
+ }
26
+ function captureNullableString(value) {
27
+ return value === null || typeof value === "string";
28
+ }
29
+ function captureRect(value) {
30
+ return captureRecord(value) && captureNumber(value.x) && captureNumber(value.y) && captureNumber(value.width) && captureNumber(value.height);
31
+ }
32
+ function captureOptionalString(value) {
33
+ return value === void 0 || typeof value === "string";
34
+ }
35
+ function captureOptionalNullableString(value) {
36
+ return value === void 0 || captureNullableString(value);
37
+ }
38
+ function captureFontRegistration(value) {
39
+ 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);
40
+ }
41
+ function capturePoint(value) {
42
+ return captureRecord(value) && captureNumber(value.x) && captureNumber(value.y);
43
+ }
44
+ function captureNode(value) {
45
+ 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)) {
46
+ return false;
47
+ }
48
+ const accessibility = value.accessibility;
49
+ 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))) {
50
+ return false;
51
+ }
52
+ const interaction = value.interaction;
53
+ 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))) {
54
+ return false;
55
+ }
56
+ const layout = value.layout;
57
+ if (!captureRect(layout.local) || !(layout.frame === null || captureRect(layout.frame)) || !capturePoint(layout.absolutePosition) || !(layout.visibleFrame === null || captureRect(layout.visibleFrame))) {
58
+ return false;
59
+ }
60
+ const style = value.style;
61
+ return captureRecord(style.resolved) && captureRecord(style.authored) && captureRecord(style.computed) && captureRecord(style.tokenBindings) && (value.textLines === void 0 || Array.isArray(value.textLines));
62
+ }
63
+ function isRnxScreenCapture(value) {
64
+ 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))) {
65
+ return false;
66
+ }
67
+ const tree = value.tree;
68
+ const visual = value.visual;
69
+ return captureRecord(tree.viewport) && captureInteger(tree.viewport.width) && tree.viewport.width > 0 && captureInteger(tree.viewport.height) && tree.viewport.height > 0 && captureInteger(tree.rootNodeId) && (tree.commitRevision === void 0 || captureInteger(tree.commitRevision)) && 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);
70
+ }
71
+
15
72
  // src/cloud-contract.ts
16
73
  var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
17
74
  var RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024;
@@ -173,13 +230,48 @@ function cloudCommand(command) {
173
230
  }
174
231
  return { ...clean, type: command.type };
175
232
  }
176
- var NanoBox = class _NanoBox {
233
+ var RemoteSimulator = class {
177
234
  constructor(session) {
178
235
  __publicField(this, "session");
236
+ // driving verbs live here so the client reads the way the CLI does. bound to
237
+ // this simulator at construction, and every verb reads the session live, so a
238
+ // confirmed claim reaches commands issued after it.
239
+ __publicField(this, "rnx");
179
240
  this.session = session;
180
- }
181
- get boxId() {
182
- return this.session.boxId;
241
+ this.rnx = {
242
+ command: (command, options) => this.sendCommand(command, options),
243
+ describe: () => this.sendCommand({ type: "tree" }),
244
+ do: (command) => this.sendCommand(command),
245
+ wait: (condition, options) => {
246
+ const timeoutMs = options?.maxMs ?? DEFAULT_WAIT_READY_MS;
247
+ const waitCondition = condition === "ready" ? { type: "ready" } : condition;
248
+ return this.sendCommand(
249
+ {
250
+ type: "waitFor",
251
+ waitForOptions: { condition: waitCondition, timeoutMs }
252
+ },
253
+ { timeoutMs: timeoutMs + 1e3 }
254
+ );
255
+ },
256
+ screenshot: async (options) => {
257
+ const result = await this.sendCommand({
258
+ type: "screenshot",
259
+ layers: options?.layers ?? "tenant"
260
+ });
261
+ if (typeof result !== "string" || !result.startsWith("data:image/")) {
262
+ throw new Error("RNX Cloud screenshot did not return an image data url");
263
+ }
264
+ return result;
265
+ },
266
+ capture: async () => {
267
+ const result = await this.sendCommand({ type: "capture" });
268
+ if (!isRnxScreenCapture(result)) {
269
+ throw new Error("RNX Cloud capture returned an invalid screen capture");
270
+ }
271
+ return result;
272
+ },
273
+ reset: () => this.sendCommand({ type: "reset" })
274
+ };
183
275
  }
184
276
  get simId() {
185
277
  return this.session.simId;
@@ -189,17 +281,12 @@ var NanoBox = class _NanoBox {
189
281
  }
190
282
  get receipt() {
191
283
  return {
192
- boxId: this.session.boxId,
193
- size: "nano",
284
+ simId: this.session.simId,
194
285
  artifact: this.session.artifact,
195
- simulator: {
196
- simId: this.session.simId,
197
- artifact: this.session.artifact,
198
- claim: this.session.claim
199
- }
286
+ claim: this.session.claim
200
287
  };
201
288
  }
202
- async command(command, options) {
289
+ async sendCommand(command, options) {
203
290
  const body = {
204
291
  id: globalThis.crypto.randomUUID(),
205
292
  claimId: this.session.claim.id,
@@ -241,35 +328,6 @@ var NanoBox = class _NanoBox {
241
328
  }
242
329
  return parsed.result;
243
330
  }
244
- describe() {
245
- return this.command({ type: "tree" });
246
- }
247
- do(command) {
248
- return this.command(command);
249
- }
250
- wait(condition, options) {
251
- const timeoutMs = options?.maxMs ?? DEFAULT_WAIT_READY_MS;
252
- const waitCondition = condition === "ready" ? { type: "ready" } : condition;
253
- return this.command(
254
- {
255
- type: "waitFor",
256
- waitForOptions: { condition: waitCondition, timeoutMs }
257
- },
258
- { timeoutMs: timeoutMs + 1e3 }
259
- );
260
- }
261
- screenshot(options) {
262
- return this.command({
263
- type: "screenshot",
264
- layers: options?.layers ?? "tenant"
265
- });
266
- }
267
- capture() {
268
- return this.command({ type: "capture" });
269
- }
270
- reset() {
271
- return this.command({ type: "reset" });
272
- }
273
331
  async confirm() {
274
332
  const response = await fetch(
275
333
  `${this.session.origin}/v1/sims/${encodeURIComponent(this.session.simId)}/claim`,
@@ -297,48 +355,6 @@ var NanoBox = class _NanoBox {
297
355
  this.session = { ...this.session, claim };
298
356
  return claim;
299
357
  }
300
- async addSimulator(input = {}) {
301
- const device = input.device?.trim() || DEFAULT_DEVICE;
302
- const response = await fetch(
303
- `${this.session.origin}/v1/boxes/${encodeURIComponent(this.session.boxId)}/simulators`,
304
- {
305
- method: "POST",
306
- headers: {
307
- authorization: `Bearer ${this.session.boxToken}`,
308
- "x-rnx-account-authorization": this.session.authorization,
309
- "content-type": "application/json"
310
- },
311
- body: JSON.stringify({
312
- device,
313
- ...input.storageFrom === void 0 ? {} : { storageFrom: input.storageFrom }
314
- })
315
- }
316
- );
317
- const text = await response.text();
318
- if (!response.ok) throw responseError(response.status, text);
319
- let parsed;
320
- try {
321
- parsed = JSON.parse(text);
322
- } catch {
323
- throw new Error("RNX Cloud simulator create returned unreadable JSON");
324
- }
325
- if (!isRecord(parsed) || parsed.boxId !== this.session.boxId) {
326
- throw new Error("RNX Cloud simulator create returned an invalid box id");
327
- }
328
- const simulator = readSimulatorCreate(
329
- parsed.simulator,
330
- this.session.artifact.id,
331
- this.session.artifact.bytes
332
- );
333
- const pooled = new _NanoBox({
334
- ...this.session,
335
- simId: simulator.simId,
336
- simulatorToken: simulator.token,
337
- claim: simulator.claim
338
- });
339
- await pooled.confirm();
340
- return pooled;
341
- }
342
358
  async close() {
343
359
  const response = await fetch(
344
360
  `${this.session.origin}/v1/boxes/${encodeURIComponent(this.session.boxId)}`,
@@ -353,7 +369,7 @@ var NanoBox = class _NanoBox {
353
369
  }
354
370
  }
355
371
  };
356
- async function createNanoBox(input) {
372
+ async function createRemoteSimulator(input) {
357
373
  if (input.artifact.byteLength > RNX_CLOUD_MAX_ARTIFACT_BYTES) {
358
374
  throw new Error(
359
375
  `rnx Cloud artifact is ${input.artifact.byteLength} bytes and exceeds the ${RNX_CLOUD_MAX_ARTIFACT_BYTES}-byte limit`
@@ -397,9 +413,8 @@ async function createNanoBox(input) {
397
413
  throw new Error("RNX Cloud create returned unreadable JSON");
398
414
  }
399
415
  const created = readBoxCreate(parsed, input.artifact.byteLength, sha256);
400
- const box = new NanoBox({
416
+ const simulator = new RemoteSimulator({
401
417
  origin,
402
- authorization: input.authorization,
403
418
  boxId: created.receipt.boxId,
404
419
  boxToken: created.boxToken,
405
420
  simId: created.receipt.simulator.simId,
@@ -407,13 +422,13 @@ async function createNanoBox(input) {
407
422
  claim: created.receipt.simulator.claim,
408
423
  artifact: created.receipt.artifact
409
424
  });
410
- await box.confirm();
411
- return box;
425
+ await simulator.confirm();
426
+ return simulator;
412
427
  }
413
428
  export {
414
- NanoBox,
415
429
  RNX_CLOUD_COMMAND_TYPES,
416
430
  RNX_CLOUD_MAX_ARTIFACT_BYTES,
417
- createNanoBox,
431
+ RemoteSimulator,
432
+ createRemoteSimulator,
418
433
  isRnxCloudCommandType
419
434
  };
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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.431 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.433 | (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;