rnxsim 0.1.527 → 0.1.529

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 (61) hide show
  1. package/cli/cloud-client.ts +270 -3
  2. package/cli/cloud-dispatch.ts +22 -2
  3. package/cli/cloud-session.ts +60 -4
  4. package/cli/commands/platform.ts +202 -6
  5. package/cli/outbound-endpoints.ts +37 -17
  6. package/dist-lib/agent-daemon-client.cjs +1 -1
  7. package/dist-lib/agent-events.cjs +1 -1
  8. package/dist-lib/agent-identity.cjs +1 -1
  9. package/dist-lib/agent-sessions.cjs +1 -1
  10. package/dist-lib/attached-projects.cjs +1 -1
  11. package/dist-lib/auth/shared-session.cjs +1 -1
  12. package/dist-lib/backend-origin.cjs +1 -1
  13. package/dist-lib/beta.cjs +1 -1
  14. package/dist-lib/beta.mjs +1 -1
  15. package/dist-lib/bridge-constants.cjs +1 -1
  16. package/dist-lib/bridge-contract-input.cjs +1 -1
  17. package/dist-lib/bridge-contract-input.mjs +1 -1
  18. package/dist-lib/bridge-contract.cjs +1 -1
  19. package/dist-lib/bridge-contract.mjs +1 -1
  20. package/dist-lib/capture-contract.cjs +1 -1
  21. package/dist-lib/capture-contract.mjs +1 -1
  22. package/dist-lib/cli-constants.cjs +1 -1
  23. package/dist-lib/cloud-contract.cjs +1 -1
  24. package/dist-lib/cloud-contract.mjs +1 -1
  25. package/dist-lib/cloud.cjs +1 -1
  26. package/dist-lib/cloud.mjs +1 -1
  27. package/dist-lib/config.cjs +1 -1
  28. package/dist-lib/detox/index.cjs +1 -1
  29. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  30. package/dist-lib/home-paths.cjs +1 -1
  31. package/dist-lib/host/bridge-host.cjs +1 -1
  32. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  33. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  34. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  35. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  36. package/dist-lib/host/websocket-proxy.cjs +1 -1
  37. package/dist-lib/index.cjs +105 -3
  38. package/dist-lib/jump-to-source-babel.cjs +1 -1
  39. package/dist-lib/jump-to-source-native.cjs +1 -1
  40. package/dist-lib/menu.cjs +1 -1
  41. package/dist-lib/menu.mjs +1 -1
  42. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  43. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  44. package/dist-lib/metro-production-bundle.cjs +1 -1
  45. package/dist-lib/metro-production-bundle.mjs +1 -1
  46. package/dist-lib/metro.cjs +1 -1
  47. package/dist-lib/profiles.cjs +1 -1
  48. package/dist-lib/public-brand.cjs +1 -1
  49. package/dist-lib/react-native-host-modules.cjs +1 -1
  50. package/dist-lib/react-native-host-modules.mjs +1 -1
  51. package/dist-lib/render-mode.cjs +1 -1
  52. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  53. package/dist-lib/sdk.cjs +1 -1
  54. package/dist-lib/sdk.mjs +1 -1
  55. package/dist-lib/skills.cjs +105 -3
  56. package/dist-lib/swift-submit.cjs +1 -1
  57. package/dist-lib/swift-submit.mjs +1 -1
  58. package/dist-lib/swift.cjs +1 -1
  59. package/dist-lib/vite.cjs +1 -1
  60. package/package.json +1 -1
  61. package/src/connect.ts +1 -1
@@ -14,6 +14,7 @@ import {
14
14
  } from 'sootsim-engine/settings'
15
15
  import { settingsStore } from 'sootsim-engine/settings/store'
16
16
  import { rnxPublicBrand } from '../../src/public-brand'
17
+ import { UnsupportedRemoteHostError } from '../cloud-client'
17
18
  import { rethrowIfExit } from '../run-rnx'
18
19
  import type { CloudSession } from '../cloud-session'
19
20
 
@@ -27,23 +28,28 @@ function isDeviceModel(value: string): value is DeviceModel {
27
28
  }
28
29
 
29
30
  // flags this command reads for itself rather than treating as the bundle.
30
- const REMOTE_VALUE_FLAGS = new Set(['--device'])
31
+ const REMOTE_VALUE_FLAGS = new Set(['--device', '--name', '--endpoint'])
31
32
 
32
33
  function remoteInputFromArgs(
33
34
  args: string[],
34
35
  platform: RuntimePlatform,
35
36
  ): {
36
37
  bundlePath: string
38
+ name: string | null
37
39
  reuse: boolean
38
40
  fresh: boolean
39
41
  } {
40
42
  const positional: string[] = []
43
+ let name: string | null = null
41
44
  for (let index = 0; index < args.length; index++) {
42
45
  const arg = args[index]
43
- if (arg === '--remote' || arg === '--reuse' || arg === '--new') continue
46
+ if (arg === '--remote' || arg === '--nano' || arg === '--reuse' || arg === '--new') {
47
+ continue
48
+ }
44
49
  if (REMOTE_VALUE_FLAGS.has(arg)) {
45
50
  const value = args[++index] ?? null
46
51
  if (!value) throw new Error(`rnx remote ${platform} ${arg} requires a value`)
52
+ if (arg === '--name') name = value
47
53
  continue
48
54
  }
49
55
  const assigned = arg.match(/^(--[a-z-]+)=(.*)$/)
@@ -51,6 +57,7 @@ function remoteInputFromArgs(
51
57
  const value = assigned[2] ?? ''
52
58
  if (!value)
53
59
  throw new Error(`rnx remote ${platform} ${assigned[1]} requires a value`)
60
+ if (assigned[1] === '--name') name = value
54
61
  continue
55
62
  }
56
63
  if (
@@ -80,7 +87,25 @@ function remoteInputFromArgs(
80
87
  if (reuse && fresh) {
81
88
  throw new Error(`rnx remote ${platform} cannot combine --reuse and --new`)
82
89
  }
83
- return { bundlePath: positional[0], reuse, fresh }
90
+ return { bundlePath: positional[0], name, reuse, fresh }
91
+ }
92
+
93
+ /**
94
+ * the box's name, taken from what was built.
95
+ *
96
+ * a bundle arrives as a file or a URL and neither carries a name, so the last
97
+ * path segment without its extension is the one thing both have. box names are
98
+ * lowercase words joined by dashes, so anything else in it becomes a dash.
99
+ */
100
+ function boxNameForBundle(bundlePath: string): string {
101
+ const segment = bundlePath.split(/[?#]/)[0]?.split('/').filter(Boolean).at(-1) ?? ''
102
+ const stem = segment.replace(/\.[^.]+$/, '')
103
+ const name = stem
104
+ .toLowerCase()
105
+ .replace(/[^a-z0-9]+/g, '-')
106
+ .replace(/^-+|-+$/g, '')
107
+ if (!name) throw new Error(`rnx remote could not name a box after ${bundlePath}`)
108
+ return name
84
109
  }
85
110
 
86
111
  export async function runPlatformCommand(
@@ -129,7 +154,15 @@ export async function runPlatformCommand(
129
154
  }
130
155
 
131
156
  if (args.includes('--remote')) {
157
+ if (platform === 'android' && args.includes('--nano')) {
158
+ const error = new UnsupportedRemoteHostError('android', 'nano')
159
+ console.error(` ${error.message}`)
160
+ return 1
161
+ }
132
162
  try {
163
+ if (!args.includes('--nano')) {
164
+ return await runRemoteBox(platform, args, selectedDevice)
165
+ }
133
166
  return await runRemoteCreate(platform, args, selectedDevice)
134
167
  } catch (error) {
135
168
  rethrowIfExit(error)
@@ -185,6 +218,11 @@ export function runRemotePlatformCommand(
185
218
  args: string[],
186
219
  opts: { port?: number } = {},
187
220
  ): Promise<number | undefined> {
221
+ if (platform === 'android' && args.includes('--nano')) {
222
+ const error = new UnsupportedRemoteHostError('android', 'nano')
223
+ console.error(` ${error.message}`)
224
+ return Promise.resolve(1)
225
+ }
188
226
  return runPlatformCommand(platform, ['--remote', ...args], opts)
189
227
  }
190
228
 
@@ -207,6 +245,7 @@ async function runRemoteCreate(
207
245
  { authHeaderOrExit, cloudAccountIdOrExit },
208
246
  {
209
247
  claimCloudSimulator,
248
+ closeCloudBox,
210
249
  closeCloudSession,
211
250
  createRemoteSim,
212
251
  getRemoteSim,
@@ -232,7 +271,7 @@ async function runRemoteCreate(
232
271
 
233
272
  const existingSession = readCloudSession()
234
273
  if (
235
- existingSession &&
274
+ existingSession?.service === 'sim' &&
236
275
  !input.fresh &&
237
276
  existingSession.platform === platform &&
238
277
  existingSession.artifact?.sha256 === artifact.sha256
@@ -265,7 +304,8 @@ async function runRemoteCreate(
265
304
 
266
305
  // the new session supersedes the old one, so the shell update channel
267
306
  // carries exactly one update: the shell accepts nothing else.
268
- if (existingSession) await closeCloudSession(existingSession)
307
+ if (existingSession?.service === 'box') await closeCloudBox(existingSession)
308
+ else if (existingSession) await closeCloudSession(existingSession)
269
309
  const created = await createRemoteSim({
270
310
  bundlePath: input.bundlePath,
271
311
  device: selectedDevice,
@@ -310,6 +350,159 @@ async function runRemoteCreate(
310
350
  return openInBrowser(created.session.streamUrl)
311
351
  }
312
352
 
353
+ /**
354
+ * `rnx ios --remote` and `rnx android --remote` — the app runs in a micro box.
355
+ *
356
+ * the artifact is produced here with no size cap: the box holds the bundle in
357
+ * parts, so the single-message ceiling a nano upload answers to does not apply.
358
+ * a box created from the already-built app boots it in its own page, and this
359
+ * opens the bare shell watch url minted from the box's display grant — the same
360
+ * /sootsim/#remoteEngine fragment the nano service mints, pointed at the box.
361
+ */
362
+ async function runRemoteBox(
363
+ platform: RuntimePlatform,
364
+ args: string[],
365
+ selectedDevice: string,
366
+ ): Promise<number> {
367
+ const command = `${platform} --remote`
368
+ const input = remoteInputFromArgs(args, platform)
369
+ const [
370
+ { closeCloudBox, closeCloudSession, produceCloudArtifact, requestBoxWatchUrl },
371
+ box,
372
+ { cloudBoxConnectUrl, ensureCloudBoxBrowser, readCloudBoxSession },
373
+ { authHeaderOrExit, cloudAccountIdOrExit },
374
+ {
375
+ createCloudBoxSession,
376
+ publishCloudSessionToShell,
377
+ readCloudSession,
378
+ serializeCloudSession,
379
+ RNX_CLOUD_SESSION_ENV,
380
+ },
381
+ ] = await Promise.all([
382
+ import('../cloud-client'),
383
+ import('./box'),
384
+ import('rnx-cloud-box/client'),
385
+ import('../auth'),
386
+ import('../cloud-session'),
387
+ ])
388
+ const { openInBrowser } = box
389
+ // the box holds the bundle in parts, so the size a single message can carry
390
+ // is not what limits this one and there is no cap to apply here.
391
+ const artifact = await produceCloudArtifact(input.bundlePath, Number.POSITIVE_INFINITY)
392
+ const endpoint = box.cloudBoxEndpoint(args, command)
393
+ if (!endpoint) return 1
394
+
395
+ const existingSession = readCloudSession()
396
+ if (
397
+ existingSession?.service === 'box' &&
398
+ !input.fresh &&
399
+ existingSession.apiOrigin === endpoint &&
400
+ existingSession.platform === platform &&
401
+ existingSession.device === selectedDevice &&
402
+ existingSession.artifact?.sha256 === artifact.sha256
403
+ ) {
404
+ const live = await readCloudBoxSession({
405
+ endpoint: existingSession.apiOrigin,
406
+ boxId: existingSession.boxId,
407
+ accessToken: existingSession.boxToken,
408
+ })
409
+ if (live.sessionId) {
410
+ // the simulator lives in the box's Browser Run tab, which lapses on its
411
+ // own, so a reconnect restores it before promising commands will answer.
412
+ await ensureCloudBoxBrowser({
413
+ connectUrl: cloudBoxConnectUrl(existingSession.apiOrigin, existingSession.boxId),
414
+ accessToken: existingSession.boxToken,
415
+ })
416
+ // grants are short-lived, so a reconnect mints a fresh one rather than
417
+ // reopening whatever url the create printed.
418
+ const watchUrl = await requestBoxWatchUrl({
419
+ apiOrigin: existingSession.apiOrigin,
420
+ boxId: existingSession.boxId,
421
+ boxToken: existingSession.boxToken,
422
+ })
423
+ console.log(
424
+ ` reconnected to remote simulator: ${existingSession.boxId} (${platform})`,
425
+ )
426
+ console.log(` open ${watchUrl}`)
427
+ console.log(REMOTE_COMMANDS_HELP)
428
+ return openInBrowser(watchUrl)
429
+ }
430
+ }
431
+
432
+ const { auth, header } = authHeaderOrExit(command)
433
+ const accountId = cloudAccountIdOrExit(auth)
434
+
435
+ if (existingSession?.service === 'box') {
436
+ await closeCloudBox(existingSession)
437
+ try {
438
+ publishCloudSessionToShell(null)
439
+ } catch {
440
+ // no shell update channel is required outside an interactive rnx shell
441
+ }
442
+ } else if (existingSession) {
443
+ await closeCloudSession(existingSession)
444
+ }
445
+ const created = await box.createPrebuiltBundleBox({
446
+ bundle: new Uint8Array(artifact.bytes),
447
+ name: input.name ?? boxNameForBundle(input.bundlePath),
448
+ endpoint,
449
+ apiOrigin: box.cloudBoxApiOrigin(args, auth),
450
+ apiKey: header.replace(/^Bearer /, ''),
451
+ accountId,
452
+ })
453
+ // creating a box starts no browser, and only its Browser Run tab hosts the
454
+ // simulator, so without this the viewer opens onto a box nobody is running.
455
+ await ensureCloudBoxBrowser({
456
+ connectUrl: cloudBoxConnectUrl(endpoint, created.id),
457
+ accessToken: created.accessToken ?? '',
458
+ })
459
+ const produced = artifact.receipt
460
+ const session = createCloudBoxSession({
461
+ boxId: created.id,
462
+ boxToken: created.accessToken ?? '',
463
+ apiOrigin: endpoint,
464
+ platform,
465
+ device: selectedDevice,
466
+ artifact: {
467
+ sha256: artifact.sha256,
468
+ bytes: artifact.bytes.length,
469
+ },
470
+ })
471
+ try {
472
+ publishCloudSessionToShell(session)
473
+ } catch {
474
+ // shell update fd not configured
475
+ }
476
+ process.env[RNX_CLOUD_SESSION_ENV] = serializeCloudSession(session)
477
+
478
+ console.log(` box: ${created.id} (${artifact.bytes.length} bytes)`)
479
+ console.log(` platform: ${platform}`)
480
+ if (selectedDevice) console.log(` device: ${selectedDevice}`)
481
+ console.log(
482
+ ` source bundle: sha256:${produced.source.sha256} (${produced.source.bytes} bytes)`,
483
+ )
484
+ console.log(
485
+ ` normalized modules: ${produced.modules}; javascript ${produced.javascript.sourceBytes} -> ${produced.javascript.minifiedBytes} bytes`,
486
+ )
487
+ console.log(
488
+ ` embedded assets: ${produced.assets.descriptors} descriptors, ${produced.assets.variants} scales, ${produced.assets.bytes} bytes (${produced.assets.encodedBytes} encoded)`,
489
+ )
490
+ // the dest directory was chosen by resolving this bundle's descriptors
491
+ // against it, so say which one won rather than leaving it invisible.
492
+ if (artifact.assetsDirectory) console.log(` asset dest: ${artifact.assetsDirectory}`)
493
+ for (const warning of artifact.warnings) console.warn(` warning: ${warning}`)
494
+ console.log(REMOTE_COMMANDS_HELP)
495
+ // the session is published before the grant is minted, so a grant that never
496
+ // arrives still leaves the box addressable: re-running this command mints it.
497
+ const watchUrl = await requestBoxWatchUrl({
498
+ apiOrigin: endpoint,
499
+ boxId: created.id,
500
+ boxToken: created.accessToken ?? '',
501
+ })
502
+ console.log(` open ${watchUrl}`)
503
+ return openInBrowser(watchUrl)
504
+ }
505
+
313
506
  /** `rnx remote list`: the simulators this account runs that `rnx remote` made. */
314
507
  export async function runRemoteList(args: string[]): Promise<number> {
315
508
  try {
@@ -381,7 +574,10 @@ export async function runRemoteStop(args: string[]): Promise<number> {
381
574
  }
382
575
  // stopping the shell's own simulator clears the shell with it, so the
383
576
  // update channel is required before anything is deleted.
384
- const descriptor = current?.simId === simId ? requireCloudSessionShellUpdate() : null
577
+ const descriptor =
578
+ current?.service === 'sim' && current.simId === simId
579
+ ? requireCloudSessionShellUpdate()
580
+ : null
385
581
  const { auth, header } = authHeaderOrExit('remote stop')
386
582
  const accountId = cloudAccountIdOrExit(auth)
387
583
  const deleted = await deleteRemoteSim({
@@ -148,14 +148,27 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
148
148
  category: 'rnx_cloud_sim',
149
149
  count: 1,
150
150
  },
151
- 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${session.apiOrigin}/v1/sims/${encodeURIComponent(session.simId)}/claim`':
152
- { category: 'rnx_cloud_sim', count: 1 },
153
- // closing a nano-box simulator deletes its box; the local names changed
154
- // with the close but the destination did not.
155
- 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${session.apiOrigin}/v1/boxes/${encodeURIComponent(boxId)}`':
151
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${session.apiOrigin}/v1/sims/${encodeURIComponent()}/claim`':
156
152
  { category: 'rnx_cloud_sim', count: 1 },
153
+ // two call sites delete a box by id: closing a nano-box simulator, and
154
+ // closing a box from a session that is not itself the box. one destination,
155
+ // so one declaration.
156
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${session.apiOrigin}/v1/boxes/${encodeURIComponent()}`':
157
+ { category: 'rnx_cloud_sim', count: 2 },
157
158
  // closing an instance simulator deletes it by its own id with its own token.
158
- 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${session.apiOrigin}/v1/sims/${encodeURIComponent(session.simId)}`':
159
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${session.apiOrigin}/v1/sims/${encodeURIComponent()}`':
160
+ { category: 'rnx_cloud_sim', count: 1 },
161
+ // the box api mirrors the sim api for a micro box: the same destination and
162
+ // the same box token, reached through the session rather than a loose id.
163
+ // driving a box sends each command to its own box.
164
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${this.session.apiOrigin}/v1/boxes/${encodeURIComponent()}/commands`':
165
+ { category: 'rnx_cloud_sim', count: 1 },
166
+ // stopping a box leaves it allocated, unlike the delete above.
167
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${session.apiOrigin}/v1/boxes/${encodeURIComponent()}/stop`':
168
+ { category: 'rnx_cloud_sim', count: 1 },
169
+ // the display grant is polled until the box hands one back; the body is
170
+ // empty, so the request carries no project or app data.
171
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${options.apiOrigin}/v1/boxes/${encodeURIComponent()}/display-grant`':
159
172
  { category: 'rnx_cloud_sim', count: 1 },
160
173
  // `rnx remote` creates through the instance api and lists what it made.
161
174
  // the create carries labels and wait=true in the query; the list filters
@@ -171,9 +184,9 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
171
184
  // rereading one simulator and deleting one by id share the route: the
172
185
  // reattach probe reads with either credential and `rnx remote stop`
173
186
  // deletes with the account credential.
174
- 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${apiOrigin}/v1/sims/${encodeURIComponent(options.simId)}`':
187
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${apiOrigin}/v1/sims/${encodeURIComponent()}`':
175
188
  { category: 'rnx_cloud_sim', count: 2 },
176
- 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${this.session.apiOrigin}/v1/sims/${encodeURIComponent(this.session.simId)}/commands`':
189
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${this.session.apiOrigin}/v1/sims/${encodeURIComponent()}/commands`':
177
190
  { category: 'rnx_cloud_sim', count: 1 },
178
191
  'packages/sootsim/cli/cloud-client.ts :: fetch :: bundleUrl': {
179
192
  category: 'guest_app_network',
@@ -229,7 +242,7 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
229
242
  { category: 'rnx_cloud_box', count: 2 },
230
243
  // the account's own session meter: starting, listing and heartbeating the
231
244
  // session a cloud box is billed against. no project content.
232
- "packages/rnx-cloud-box/src/client.ts :: fetch :: `${options.apiOrigin.replace(/\\/+$/, '')}/api/v1/rnx/sessions${ options.accountId ? `?accountId=${encodeURIComponent(options.accountId)}` : '' }`":
245
+ "packages/rnx-cloud-box/src/client.ts :: fetch :: `${options.apiOrigin.replace(/\\/+$/, '')}/api/v1/rnx/sessions${ options.accountId ? `?accountId=${encodeURIComponent()}` : '' }`":
233
246
  { category: 'contrast_user_action', count: 1 },
234
247
  'packages/rnx-cloud-box/src/client.ts :: fetch :: url': {
235
248
  category: 'contrast_user_action',
@@ -244,9 +257,9 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
244
257
  },
245
258
  "packages/rnx-cloud-box/src/box-do.ts :: fetch :: `${origin}/api/v1/rnx/sessions${serviceAuthorized ? '/internal' : ''}`":
246
259
  { category: 'contrast_user_action', count: 1 },
247
- "packages/rnx-cloud-box/src/box-do.ts :: fetch :: `${origin}/api/v1/rnx/sessions/${ serviceAuthority ? 'internal/' : '' }${encodeURIComponent(sessionId)}${ accountId ? `?accountId=${encodeURIComponent(accountId)}` : '' }`":
260
+ "packages/rnx-cloud-box/src/box-do.ts :: fetch :: `${origin}/api/v1/rnx/sessions/${ serviceAuthority ? 'internal/' : '' }${encodeURIComponent()}${ accountId ? `?accountId=${encodeURIComponent()}` : '' }`":
248
261
  { category: 'contrast_user_action', count: 1 },
249
- 'packages/rnx-cloud-box/src/box-do.ts :: fetch :: `${origin}/api/v1/rnx/sessions/internal/${encodeURIComponent(sessionId)}`':
262
+ 'packages/rnx-cloud-box/src/box-do.ts :: fetch :: `${origin}/api/v1/rnx/sessions/internal/${encodeURIComponent()}`':
250
263
  { category: 'contrast_user_action', count: 1 },
251
264
  // the Cloudflare Worker forwards the already-authenticated request to the
252
265
  // selected Durable Object in the same service.
@@ -496,9 +509,9 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
496
509
  category: 'local_development',
497
510
  count: 1,
498
511
  },
499
- 'packages/sootsim/cli/commands/screenshots-capture.ts :: fetch :: `${apiOrigin}/api/sootsim/screenshot-capture?ref=${encodeURIComponent(ref)}&org=${encodeURIComponent(org)}`':
512
+ 'packages/sootsim/cli/commands/screenshots-capture.ts :: fetch :: `${apiOrigin}/api/sootsim/screenshot-capture?ref=${encodeURIComponent()}&org=${encodeURIComponent()}`':
500
513
  { category: 'contrast_user_action', count: 1 },
501
- 'packages/sootsim/cli/commands/screenshots-capture.ts :: fetch :: `${apiOrigin}/api/sootsim/screenshot-decks?org=${encodeURIComponent(org)}&repo=${encodeURIComponent(repo)}`':
514
+ 'packages/sootsim/cli/commands/screenshots-capture.ts :: fetch :: `${apiOrigin}/api/sootsim/screenshot-decks?org=${encodeURIComponent()}&repo=${encodeURIComponent()}`':
502
515
  { category: 'contrast_user_action', count: 1 },
503
516
  'packages/sootsim/cli/cli-update.ts :: fetch :: url': {
504
517
  category: 'cli_delivery',
@@ -551,7 +564,7 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
551
564
  category: 'local_development',
552
565
  count: 1,
553
566
  },
554
- 'packages/sootsim/src/dev-bundle-resolution.ts :: fetch :: `/__fetch-proxy?url=${encodeURIComponent(targetUrl)}`':
567
+ 'packages/sootsim/src/dev-bundle-resolution.ts :: fetch :: `/__fetch-proxy?url=${encodeURIComponent()}`':
555
568
  { category: 'guest_app_network', count: 1 },
556
569
  'packages/sootsim/src/dev-bundle-resolution.ts :: fetch :: targetUrl': {
557
570
  category: 'guest_app_network',
@@ -765,9 +778,16 @@ function callKind(
765
778
 
766
779
  function firstArgument(node: CallExpression | NewExpression, source: string): string {
767
780
  const first = node.arguments[0]
768
- return first
769
- ? source.slice(first.start, first.end).replace(/\s+/g, ' ').trim()
770
- : '<none>'
781
+ if (!first) return '<none>'
782
+ const text = source.slice(first.start, first.end).replace(/\s+/g, ' ').trim()
783
+ // the key is source text, so renaming a local used as a path id used to change
784
+ // it and red main on a commit that moved no destination. what justifies erasing
785
+ // the argument to encodeURIComponent is that it is a local NAME rather than a
786
+ // destination, not that it is a path segment: at dev-bundle-resolution.ts and
787
+ // backend-origin.ts the argument is a whole target url behind a proxy path. a
788
+ // name carries no destination either way, so erasing it loses nothing, while
789
+ // the host expression and the path itself still have to be re-declared.
790
+ return text.replace(/encodeURIComponent\([^()]*\)/g, 'encodeURIComponent()')
771
791
  }
772
792
 
773
793
  export function detectOutboundCalls(repositoryRoot: string): DetectedOutboundCall[] {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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;
package/dist-lib/beta.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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;
package/dist-lib/beta.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/beta.ts
4
4
  var IS_BETA = true;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_MAX_MS = 5e3;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.529 | (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;