peachsim 0.1.636 → 0.1.638

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 (79) hide show
  1. package/README.md +8 -5
  2. package/cli/bridge-flow-runner.ts +442 -14
  3. package/cli/commands/inspect/core.ts +93 -24
  4. package/cli/commands/maestro.ts +2 -2
  5. package/cli/commands/serve.ts +13 -1
  6. package/cli/drivers/playwright-provisioning.ts +8 -16
  7. package/cli/drivers/playwright-sim-host.ts +3 -0
  8. package/cli/drivers/playwright.ts +1 -0
  9. package/cli/internal-child.ts +51 -8
  10. package/dist-lib/agent-daemon-client.cjs +1 -1
  11. package/dist-lib/agent-events.cjs +1 -1
  12. package/dist-lib/agent-identity.cjs +1 -1
  13. package/dist-lib/agent-sessions.cjs +1 -1
  14. package/dist-lib/attached-projects.cjs +1 -1
  15. package/dist-lib/auth/shared-session.cjs +1 -1
  16. package/dist-lib/backend-origin.cjs +1 -1
  17. package/dist-lib/beta.cjs +1 -1
  18. package/dist-lib/beta.mjs +1 -1
  19. package/dist-lib/bridge-constants.cjs +1 -1
  20. package/dist-lib/bridge-contract-input.cjs +3 -2
  21. package/dist-lib/bridge-contract-input.mjs +3 -2
  22. package/dist-lib/bridge-contract.cjs +1 -1
  23. package/dist-lib/bridge-contract.mjs +1 -1
  24. package/dist-lib/capture-contract.cjs +1 -1
  25. package/dist-lib/capture-contract.mjs +1 -1
  26. package/dist-lib/cli-constants.cjs +1 -1
  27. package/dist-lib/cloud-contract.cjs +1 -1
  28. package/dist-lib/cloud-contract.mjs +1 -1
  29. package/dist-lib/cloud-test-args.cjs +1 -1
  30. package/dist-lib/cloud-test-args.mjs +1 -1
  31. package/dist-lib/cloud.cjs +1 -1
  32. package/dist-lib/cloud.mjs +1 -1
  33. package/dist-lib/config.cjs +1 -1
  34. package/dist-lib/detox/index.cjs +1 -1
  35. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  36. package/dist-lib/home-paths.cjs +1 -1
  37. package/dist-lib/host/bridge-host.cjs +4 -2
  38. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  39. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  40. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  41. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  42. package/dist-lib/host/websocket-proxy.cjs +1 -1
  43. package/dist-lib/index.cjs +76 -20
  44. package/dist-lib/jump-to-source-babel.cjs +1 -1
  45. package/dist-lib/jump-to-source-native.cjs +1 -1
  46. package/dist-lib/kotlin-host.cjs +1 -1
  47. package/dist-lib/menu.cjs +1 -1
  48. package/dist-lib/menu.mjs +1 -1
  49. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  50. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  51. package/dist-lib/metro-production-bundle.cjs +1 -1
  52. package/dist-lib/metro-production-bundle.mjs +1 -1
  53. package/dist-lib/metro.cjs +1 -1
  54. package/dist-lib/native-app-identity.cjs +1 -1
  55. package/dist-lib/native-app-identity.mjs +1 -1
  56. package/dist-lib/profiles.cjs +1 -1
  57. package/dist-lib/public-brand.cjs +1 -1
  58. package/dist-lib/react-native-host-modules.cjs +1 -1
  59. package/dist-lib/react-native-host-modules.mjs +1 -1
  60. package/dist-lib/render-mode.cjs +1 -1
  61. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  62. package/dist-lib/sdk.cjs +78 -20
  63. package/dist-lib/sdk.mjs +77 -20
  64. package/dist-lib/skills.cjs +394 -62
  65. package/dist-lib/swift-compile.cjs +89 -7
  66. package/dist-lib/swift-compile.mjs +89 -7
  67. package/dist-lib/swift-package.cjs +1 -1
  68. package/dist-lib/swift-package.mjs +1 -1
  69. package/dist-lib/swift-submit.cjs +1 -1
  70. package/dist-lib/swift-submit.mjs +1 -1
  71. package/dist-lib/swift.cjs +122 -34
  72. package/dist-lib/vite-swift.cjs +89 -7
  73. package/dist-lib/vite.cjs +1 -1
  74. package/package.json +1 -1
  75. package/src/bridge-contract-input.ts +3 -2
  76. package/src/bridge-contract.ts +8 -0
  77. package/src/host/bridge-host.ts +5 -5
  78. package/src/native-seam-manifest.ts +4 -6
  79. package/src/swift-compile.ts +105 -9
@@ -114,6 +114,7 @@ supported verbs:
114
114
  launchApp (including arguments), stopApp, clearState, clearKeychain (warn-only),
115
115
  selectors: text, id, index (negative from the end), childOf, below, above,
116
116
  leftOf, rightOf, width / height / tolerance, traits, enabled, selected, checked,
117
+ focused, containsChild, containsDescendants,
117
118
  tapOn (including repeat / delay), doubleTapOn, tapAtCoords, longPressOn,
118
119
  inputText, pressKey, dispatchKey, hideKeyboard, eraseText,
119
120
  setClipboard, pasteText,
@@ -166,8 +167,7 @@ not yet implemented (the step fails with "unsupported flow step"):
166
167
  travel, setLocation, setAirplaneMode, killApp, faker, inputRandom*,
167
168
  addMedia, startRecording, stopRecording (use --record flag),
168
169
  repeat.while (only repeat.times is supported).
169
- selector keys that fail the step: containsChild, containsDescendants,
170
- focused, css.
170
+ selector keys that fail the step: css.
171
171
  `)
172
172
  }
173
173
 
@@ -36,11 +36,13 @@ usage:
36
36
 
37
37
  options:
38
38
  --port <n> bridge port (defaults to ${DEFAULT_SOOTSIM_BRIDGE_PORT})
39
+ --host <address> listener address (defaults to 127.0.0.1)
39
40
  --quiet suppress per-connection logging
40
41
 
41
42
  examples:
42
43
  peach serve
43
44
  peach serve --port 7668 --quiet
45
+ peach serve --host 0.0.0.0
44
46
  `)
45
47
  rnxExit(0)
46
48
  }
@@ -54,6 +56,12 @@ examples:
54
56
  console.error(` invalid --port value: ${args[portArgIdx + 1]}`)
55
57
  rnxExit(1)
56
58
  }
59
+ const hostArgIdx = args.indexOf('--host')
60
+ const bindHost = hostArgIdx >= 0 ? args[hostArgIdx + 1] : '127.0.0.1'
61
+ if (!bindHost || bindHost.startsWith('-')) {
62
+ console.error(' --host requires a listener address')
63
+ rnxExit(1)
64
+ }
57
65
  const quiet = args.includes('--quiet') || args.includes('-q')
58
66
 
59
67
  // only one bridge per peach home. refuse to start a second one
@@ -74,6 +82,7 @@ examples:
74
82
  const contrastOrigin = await resolveDefaultUploadOrigin()
75
83
  const host = new SootSimBridgeHost({
76
84
  port,
85
+ host: bindHost,
77
86
  writeLockfile: true,
78
87
  contrastOrigin,
79
88
  })
@@ -102,7 +111,10 @@ examples:
102
111
  for (const id of currentIds) lastSimIds.add(id)
103
112
  }, 500)
104
113
 
105
- log(`peach bridge listening on ws://localhost:${boundPort} (runtime http on same port)`)
114
+ const displayHost = bindHost.includes(':') ? `[${bindHost}]` : bindHost
115
+ log(
116
+ `peach bridge listening on ws://${displayHost}:${boundPort} (runtime http on same port)`,
117
+ )
106
118
  if (boundPort !== port) {
107
119
  log(` (preferred port ${port} was taken — fell back to ${boundPort})`)
108
120
  }
@@ -48,18 +48,12 @@ export function resolvePlaywright(): ResolvedPlaywright | null {
48
48
  const packageJsonPath = requireFromRoot.resolve(`${spec}/package.json`)
49
49
  const parsed: unknown = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
50
50
  if (!isRecord(parsed) || typeof parsed.version !== 'string') continue
51
- const bin = parsed.bin
52
- let binPath: string | null = null
53
- if (typeof bin === 'string') {
54
- binPath = bin
55
- } else if (isRecord(bin)) {
56
- const named = bin.playwright ?? bin['playwright-core']
57
- const first = Object.values(bin).find((value) => typeof value === 'string')
58
- binPath =
59
- typeof named === 'string' ? named : typeof first === 'string' ? first : null
60
- }
61
- if (!binPath) continue
62
- const cliPath = join(dirname(packageJsonPath), binPath)
51
+ // resolve the core beside the API entry, never from the caller's
52
+ // project when running this package's provisioning subcommand.
53
+ const corePackagePath = createRequire(modulePath).resolve(
54
+ 'playwright-core/package.json',
55
+ )
56
+ const cliPath = join(dirname(corePackagePath), 'cli.js')
63
57
  if (!existsSync(cliPath)) continue
64
58
  return {
65
59
  spec,
@@ -166,10 +160,8 @@ export function ensurePlaywrightBrowser(
166
160
  return { ok: true, executablePath: before.executablePath, installed: false }
167
161
  }
168
162
 
169
- // the resolved package is the authority for both the required executable
170
- // path and the installer. never scan a shared cache or select a neighboring
171
- // revision: Playwright's own CLI installs the exact revision its API named.
172
- // that CLI is a javascript file, so this CLI runs it as itself.
163
+ // install only when neither an explicit binary nor an installed cache
164
+ // revision is available. the absolute core CLI owns its downloader scripts.
173
165
  const installer = rnxSelfInvocation()
174
166
  const install = spawnSync(
175
167
  installer.executable,
@@ -26,6 +26,8 @@ import { DISPOSABLE_BROWSER_CACHE_DIR_NAMES } from '../../src/browser-cache'
26
26
 
27
27
  export const PLAYWRIGHT_SIM_HOST = `
28
28
  const modulePath = process.env.SOOTSIM_PW_MODULE;
29
+ const executablePath = process.env.SOOTSIM_PW_EXECUTABLE;
30
+ if (!executablePath) throw new Error('SOOTSIM_PW_EXECUTABLE is not set');
29
31
  const url = process.env.SOOTSIM_PW_URL;
30
32
  // the WS bridge port the sim page must register with, injected below as
31
33
  // window.__sootsimBridgePort so the page skips its shell-port heuristic (which
@@ -332,6 +334,7 @@ const cleanupProfile = () => {
332
334
  const opts = channel
333
335
  ? { headless, channel, viewport, deviceScaleFactor }
334
336
  : { headless, viewport, deviceScaleFactor };
337
+ opts.executablePath = executablePath;
335
338
  // playwright injects --force-color-profile=srgb by default, which reads
336
339
  // visibly washed-out next to a normal chrome window on a wide-gamut mac
337
340
  // display. strip it so driver-launched sims color-match the user's own
@@ -132,6 +132,7 @@ async function launch(opts: DriverLaunchOptions): Promise<DriverLaunchResult> {
132
132
  env: {
133
133
  ...playwrightEnv,
134
134
  SOOTSIM_PW_MODULE: resolved.modulePath,
135
+ SOOTSIM_PW_EXECUTABLE: provisioning.executablePath,
135
136
  SOOTSIM_PW_URL: opts.url,
136
137
  SOOTSIM_PW_BRIDGE_PORT: opts.bridgePort ? String(opts.bridgePort) : '',
137
138
  SOOTSIM_PW_HEADLESS: (opts.headless ?? true) ? '1' : '0',
@@ -12,8 +12,9 @@
12
12
  // the same path. bin.ts dispatches here straight from argv, before any CLI
13
13
  // startup work, which keeps a per-request worker (sync-http) cheap.
14
14
 
15
- import { existsSync, writeFileSync } from 'node:fs'
15
+ import { accessSync, constants, existsSync, readdirSync, writeFileSync } from 'node:fs'
16
16
  import { createRequire } from 'node:module'
17
+ import { basename, dirname, join, relative, resolve } from 'node:path'
17
18
  import * as vm from 'node:vm'
18
19
 
19
20
  export const RNX_INTERNAL_COMMAND = '__internal'
@@ -88,8 +89,8 @@ export async function runInternalChild(args: string[]): Promise<void> {
88
89
  // own work does.
89
90
  }
90
91
 
91
- /** report where the resolved playwright package expects its chromium and
92
- * whether that file is there. a separate process so the caller's environment
92
+ /** select an explicit executable or the newest installed chromium in the
93
+ * resolved package's cache. a separate process so the caller's environment
93
94
  * snapshot — PLAYWRIGHT_BROWSERS_PATH above all — is the only browser cache
94
95
  * this ever sees. */
95
96
  function playwrightProbe(): ChildReply {
@@ -100,19 +101,61 @@ function playwrightProbe(): ChildReply {
100
101
  if (!isRecord(chromium) || typeof chromium.executablePath !== 'function') {
101
102
  throw new Error('resolved package does not expose browser "chromium"')
102
103
  }
103
- const executablePath: unknown = chromium.executablePath()
104
- if (typeof executablePath !== 'string') {
104
+ const expectedPath: unknown = chromium.executablePath()
105
+ if (typeof expectedPath !== 'string') {
105
106
  throw new Error('browser "chromium" reported no executable path')
106
107
  }
108
+ let executablePath = expectedPath
109
+ const override = process.env.PEACH_CHROMIUM_EXECUTABLE
110
+ if (override) {
111
+ executablePath = resolve(override)
112
+ accessSync(executablePath, constants.X_OK)
113
+ } else {
114
+ // use full chromium for both headed and headless launches. selecting it
115
+ // explicitly also avoids a second, revision-pinned headless-shell lookup.
116
+ let browserDir = dirname(expectedPath)
117
+ while (!/^chromium-\d+$/.test(basename(browserDir))) {
118
+ const parent = dirname(browserDir)
119
+ if (parent === browserDir) throw new Error('unexpected chromium cache layout')
120
+ browserDir = parent
121
+ }
122
+ const cacheDir = dirname(browserDir)
123
+ const binaryPath = relative(browserDir, expectedPath)
124
+ const revisions = existsSync(cacheDir)
125
+ ? readdirSync(cacheDir)
126
+ .filter((name) => /^chromium-\d+$/.test(name))
127
+ .sort((a, b) => Number(b.slice(9)) - Number(a.slice(9)))
128
+ : []
129
+ for (const revision of revisions) {
130
+ // chromium switched its linux archive directory to chrome-linux64.
131
+ const paths =
132
+ process.platform === 'linux'
133
+ ? ['chrome-linux64/chrome', 'chrome-linux/chrome']
134
+ : [binaryPath]
135
+ const installed = paths
136
+ .map((path) => join(cacheDir, revision, path))
137
+ .find((path) => {
138
+ try {
139
+ accessSync(path, constants.X_OK)
140
+ return true
141
+ } catch {
142
+ return false
143
+ }
144
+ })
145
+ if (installed) {
146
+ executablePath = installed
147
+ break
148
+ }
149
+ }
150
+ }
107
151
  return {
108
152
  stdout: JSON.stringify({ executablePath, exists: existsSync(executablePath) }),
109
153
  code: 0,
110
154
  }
111
155
  }
112
156
 
113
- /** run playwright's own CLI entry, the only thing allowed to install the
114
- * browser revision its API named. argv is shaped the way that CLI's argument
115
- * parser expects to receive it from node. it owns this process from here:
157
+ /** run the resolved playwright core's absolute CLI entry. argv is shaped the
158
+ * way its parser expects to receive it from node. it owns this process from here:
116
159
  * it sets the exit code and may exit outright. */
117
160
  function playwrightInstall(args: string[]): void {
118
161
  const [cliPath, ...cliArgs] = args
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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;
@@ -337,6 +337,7 @@ function parseSemanticQuery(value) {
337
337
  const query = recordValue(value, "query");
338
338
  const kind = stringValue(query.kind, "query.kind");
339
339
  if (kind === "count") return { kind };
340
+ if (kind === "accessibility") return { kind };
340
341
  if (kind === "tree") {
341
342
  const depth = optionalNumber(query, "depth", "query");
342
343
  return { kind, ...depth === void 0 ? {} : { depth } };
@@ -344,7 +345,7 @@ function parseSemanticQuery(value) {
344
345
  if (kind === "find") {
345
346
  return { kind, selector: parseSemanticSelector(query.selector, "query.selector") };
346
347
  }
347
- return validationFailure("query.kind", "must be tree, find, or count");
348
+ return validationFailure("query.kind", "must be tree, find, count, or accessibility");
348
349
  }
349
350
  function semanticResolveSelector(value, path) {
350
351
  const selector = recordValue(value, path);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_MAX_MS = 5e3;
@@ -301,6 +301,7 @@ function parseSemanticQuery(value) {
301
301
  const query = recordValue(value, "query");
302
302
  const kind = stringValue(query.kind, "query.kind");
303
303
  if (kind === "count") return { kind };
304
+ if (kind === "accessibility") return { kind };
304
305
  if (kind === "tree") {
305
306
  const depth = optionalNumber(query, "depth", "query");
306
307
  return { kind, ...depth === void 0 ? {} : { depth } };
@@ -308,7 +309,7 @@ function parseSemanticQuery(value) {
308
309
  if (kind === "find") {
309
310
  return { kind, selector: parseSemanticSelector(query.selector, "query.selector") };
310
311
  }
311
- return validationFailure("query.kind", "must be tree, find, or count");
312
+ return validationFailure("query.kind", "must be tree, find, count, or accessibility");
312
313
  }
313
314
  function semanticResolveSelector(value, path) {
314
315
  const selector = recordValue(value, path);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/cloud-test-args.ts
4
4
  var CLOUD_TEST_ARGS_MAX_COUNT = 32;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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;
@@ -9497,6 +9497,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
9497
9497
  // terminal 4001 code, so the client closes the abandoned window instead
9498
9498
  // of reconnecting the zombie straight back in.
9499
9499
  static SIM_IDLE_REAP_TTL_MS = 30 * 6e4;
9500
+ bindHost;
9500
9501
  preferredPort;
9501
9502
  portFallbackCount;
9502
9503
  shouldWriteLockfile;
@@ -9530,6 +9531,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
9530
9531
  inflightScan = null;
9531
9532
  static SCAN_FRESH_MS = 2e3;
9532
9533
  constructor(opts = {}) {
9534
+ this.bindHost = opts.host ?? "127.0.0.1";
9533
9535
  this.preferredPort = opts.port ?? DEFAULT_SOOTSIM_BRIDGE_PORT;
9534
9536
  this.port = this.preferredPort;
9535
9537
  this.shouldWriteLockfile = opts.writeLockfile === true;
@@ -9606,7 +9608,7 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
9606
9608
  reject(err);
9607
9609
  };
9608
9610
  server.once("error", onError);
9609
- server.listen(port, "127.0.0.1", () => {
9611
+ server.listen(port, this.bindHost, () => {
9610
9612
  if (settled) return;
9611
9613
  const address = server.address();
9612
9614
  if (!address || typeof address === "string") {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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.636 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.638 | (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;
@@ -1780,33 +1780,89 @@ function layoutGetNativeEval(opts = {}) {
1780
1780
  })()`;
1781
1781
  }
1782
1782
  var LAYOUT_GET_NATIVE_EVAL = layoutGetNativeEval();
1783
+ function isPeachAccessibilityTree(value) {
1784
+ if (!Array.isArray(value)) return false;
1785
+ return value.every((node) => {
1786
+ if (node === null || typeof node !== "object") return false;
1787
+ if (Reflect.get(node, "platform") !== "ios" && Reflect.get(node, "platform") !== "android")
1788
+ return false;
1789
+ if (typeof Reflect.get(node, "elementId") !== "string") return false;
1790
+ if (!Number.isInteger(Reflect.get(node, "nodeId"))) return false;
1791
+ for (const key of ["type", "role"]) {
1792
+ if (typeof Reflect.get(node, key) !== "string") return false;
1793
+ }
1794
+ for (const key of ["testID", "label", "hint", "value", "placeholder"]) {
1795
+ const field = Reflect.get(node, key);
1796
+ if (field !== null && typeof field !== "string") return false;
1797
+ }
1798
+ for (const key of [
1799
+ "isAccessibilityElement",
1800
+ "enabled",
1801
+ "selected",
1802
+ "checked",
1803
+ "clickable",
1804
+ "isTextInput"
1805
+ ]) {
1806
+ if (typeof Reflect.get(node, key) !== "boolean") return false;
1807
+ }
1808
+ const hasFocus = Reflect.get(node, "hasFocus");
1809
+ if (hasFocus !== void 0 && typeof hasFocus !== "boolean") return false;
1810
+ const state = Reflect.get(node, "state");
1811
+ if (state !== null) {
1812
+ if (typeof state !== "object") return false;
1813
+ const checked = Reflect.get(state, "checked");
1814
+ if (typeof checked !== "boolean" && checked !== "mixed") return false;
1815
+ for (const key of ["disabled", "selected", "busy", "expanded"]) {
1816
+ if (typeof Reflect.get(state, key) !== "boolean") return false;
1817
+ }
1818
+ }
1819
+ const value2 = Reflect.get(node, "accessibilityValue");
1820
+ if (value2 !== null) {
1821
+ if (typeof value2 !== "object") return false;
1822
+ for (const key of ["min", "max", "now"]) {
1823
+ const field = Reflect.get(value2, key);
1824
+ if (field !== void 0 && (typeof field !== "number" || !Number.isFinite(field)))
1825
+ return false;
1826
+ }
1827
+ const text = Reflect.get(value2, "text");
1828
+ if (text !== void 0 && typeof text !== "string") return false;
1829
+ }
1830
+ if (!isSemanticGeometry(Reflect.get(node, "frame"))) return false;
1831
+ return isPeachAccessibilityTree(Reflect.get(node, "children"));
1832
+ });
1833
+ }
1783
1834
  async function inspectAccessibilityTree(bridge) {
1784
1835
  const result = await bridge.send({
1785
1836
  type: "query",
1786
- query: { kind: "find", selector: { visible: true } }
1837
+ query: { kind: "accessibility" }
1787
1838
  });
1788
- if (!isSemanticQueryResult(result)) return [];
1789
- return result.nodes.flatMap((node) => {
1790
- if (node.accessibilityHidden) return [];
1791
- if (!node.role && !node.label && !node.text && !node.pressable) return [];
1792
- return [
1793
- {
1794
- role: node.role ?? (node.pressable ? "button" : node.type === "text" ? "statictext" : "none"),
1795
- label: node.label ?? node.text ?? null,
1796
- hint: null,
1797
- state: null,
1798
- testID: node.testID ?? null,
1839
+ const tree = result !== null && typeof result === "object" ? Reflect.get(result, "accessibility") : void 0;
1840
+ if (!isPeachAccessibilityTree(tree))
1841
+ throw new Error("engine returned an invalid accessibility snapshot");
1842
+ const nodes = [];
1843
+ const visit = (node) => {
1844
+ if (node.isAccessibilityElement) {
1845
+ nodes.push({
1846
+ role: node.role,
1847
+ label: node.label,
1848
+ hint: node.hint,
1849
+ state: node.state ? { ...node.state } : null,
1850
+ testID: node.testID,
1799
1851
  position: {
1800
- x: Math.round(node.geometry.x),
1801
- y: Math.round(node.geometry.y)
1852
+ x: Math.round(node.frame.x),
1853
+ y: Math.round(node.frame.y)
1802
1854
  },
1803
1855
  size: {
1804
- w: Math.round(node.geometry.width),
1805
- h: Math.round(node.geometry.height)
1856
+ w: Math.round(node.frame.width),
1857
+ h: Math.round(node.frame.height)
1806
1858
  }
1807
- }
1808
- ];
1809
- });
1859
+ });
1860
+ if (node.platform === "ios") return;
1861
+ }
1862
+ for (const child of node.children) visit(child);
1863
+ };
1864
+ for (const root of tree) visit(root);
1865
+ return nodes;
1810
1866
  }
1811
1867
  function resolveFindMode(q) {
1812
1868
  if (q.testId) {