sootsim 0.1.143 → 0.1.145

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 (151) hide show
  1. package/dist-cli/bin.js +3 -3
  2. package/dist-cli/chunks/{agent-25GIVRCM.js → agent-YX5V2KV7.js} +2 -2
  3. package/dist-cli/chunks/{agent-wrapper-SHRSOH5F.js → agent-wrapper-3X53DI4P.js} +2 -2
  4. package/dist-cli/chunks/{app-fonts-JUWOZS4H.js → app-fonts-5I6J7HMM.js} +2 -2
  5. package/dist-cli/chunks/{assert-IVGHPA5A.js → assert-BDSCYJ3N.js} +2 -2
  6. package/dist-cli/chunks/auto-bootstrap-JVI75WWN.js +2 -0
  7. package/dist-cli/chunks/beta-IXSKJTBA.js +2 -0
  8. package/dist-cli/chunks/{camera-W33RHKCW.js → camera-HXVXO2XW.js} +2 -2
  9. package/dist-cli/chunks/{chunk-DYCX25AE.js → chunk-26P6DJRO.js} +2 -2
  10. package/dist-cli/chunks/{chunk-YJDPJTJ5.js → chunk-2GAVMQGW.js} +2 -2
  11. package/dist-cli/chunks/{chunk-7SBHZLQW.js → chunk-2R6ALEI7.js} +32 -52
  12. package/dist-cli/chunks/{chunk-DFKP5F2D.js → chunk-36RF3WRB.js} +1 -1
  13. package/dist-cli/chunks/{chunk-PJNUAUBJ.js → chunk-3AALQI5D.js} +2 -2
  14. package/dist-cli/chunks/{chunk-NLCQST4G.js → chunk-3Q7M3ATN.js} +2 -2
  15. package/dist-cli/chunks/{chunk-XXSE44NG.js → chunk-4QJCDB6B.js} +2 -2
  16. package/dist-cli/chunks/{chunk-I3YEKOFK.js → chunk-5HCF2FQK.js} +2 -2
  17. package/dist-cli/chunks/{chunk-HX23DKRO.js → chunk-5MF7XOPR.js} +1 -1
  18. package/dist-cli/chunks/{chunk-AAYFZNPR.js → chunk-6MAW2X3J.js} +1 -1
  19. package/dist-cli/chunks/{chunk-YLR5ZRKX.js → chunk-7FL2TKGB.js} +2 -2
  20. package/dist-cli/chunks/{chunk-YSJYN72Y.js → chunk-7GBAY2BX.js} +2 -2
  21. package/dist-cli/chunks/{chunk-BAEWF5VG.js → chunk-7ODTKW6U.js} +2 -2
  22. package/dist-cli/chunks/{chunk-VNNV5YRW.js → chunk-A27TUQQZ.js} +1 -1
  23. package/dist-cli/chunks/{chunk-7TN63AI6.js → chunk-ANTCWWXN.js} +1 -1
  24. package/dist-cli/chunks/{chunk-HNNOZQPP.js → chunk-AUMBVWWD.js} +2 -2
  25. package/dist-cli/chunks/{chunk-UC432AHL.js → chunk-AXNDFONG.js} +2 -2
  26. package/dist-cli/chunks/{chunk-QU2QUSZJ.js → chunk-C6XQHJFR.js} +2 -2
  27. package/dist-cli/chunks/chunk-CUPKQONE.js +119 -0
  28. package/dist-cli/chunks/{chunk-2BFY6NP3.js → chunk-DNM2AOHD.js} +1 -1
  29. package/dist-cli/chunks/{chunk-XMMYQPSE.js → chunk-F6UIBH3K.js} +1 -1
  30. package/dist-cli/chunks/{chunk-BHEMY7ME.js → chunk-F6YOWJAA.js} +2 -2
  31. package/dist-cli/chunks/chunk-HONMR5LW.js +1 -0
  32. package/dist-cli/chunks/chunk-HVRSV4QO.js +2 -0
  33. package/dist-cli/chunks/{chunk-4A5DOFDF.js → chunk-I5FEWPWO.js} +1 -1
  34. package/dist-cli/chunks/{chunk-QJPM3RET.js → chunk-KDNOWAFU.js} +2 -2
  35. package/dist-cli/chunks/{chunk-VVN2YEL6.js → chunk-KSNQFEBZ.js} +2 -2
  36. package/dist-cli/chunks/{chunk-XZXF5AYC.js → chunk-MNMNX4O7.js} +1 -1
  37. package/dist-cli/chunks/{chunk-K635OII3.js → chunk-NRBPTQVR.js} +3 -3
  38. package/dist-cli/chunks/{chunk-TPWSHYVO.js → chunk-NX6V5VAV.js} +3 -3
  39. package/dist-cli/chunks/chunk-OW2ZBOB3.js +2 -0
  40. package/dist-cli/chunks/{chunk-34SQBAH2.js → chunk-Q3UIMXJY.js} +2 -2
  41. package/dist-cli/chunks/{chunk-SWKPSL5W.js → chunk-QNJYE5OY.js} +2 -2
  42. package/dist-cli/chunks/{chunk-UN3RQBVH.js → chunk-QS37KABB.js} +2 -2
  43. package/dist-cli/chunks/{chunk-QDGEMD2I.js → chunk-QZAJOB4N.js} +1 -1
  44. package/dist-cli/chunks/{chunk-7SXBQTHJ.js → chunk-R76YX6KD.js} +2 -2
  45. package/dist-cli/chunks/{chunk-HBFZA6VQ.js → chunk-RGJAUL7P.js} +2 -2
  46. package/dist-cli/chunks/{chunk-R6XZO5GZ.js → chunk-RHHCV6M4.js} +2 -2
  47. package/dist-cli/chunks/{chunk-2R4BMZ6Z.js → chunk-RV3WN4FQ.js} +2 -2
  48. package/dist-cli/chunks/chunk-SMMTE4QY.js +1 -0
  49. package/dist-cli/chunks/{chunk-MREBZSAV.js → chunk-T35Q6ZGG.js} +1 -1
  50. package/dist-cli/chunks/{chunk-YPMUEFLF.js → chunk-TRA4IJCY.js} +1 -1
  51. package/dist-cli/chunks/chunk-WJA4MM4L.js +1 -0
  52. package/dist-cli/chunks/{chunk-YTK7ZFAM.js → chunk-YENFSYAG.js} +1 -1
  53. package/dist-cli/chunks/{chunk-JXZNBPSD.js → chunk-YWUBSH2K.js} +1 -1
  54. package/dist-cli/chunks/cli-version-C56OR6JV.js +2 -0
  55. package/dist-cli/chunks/{compat-4RDLRPPU.js → compat-OH3WCY73.js} +3 -3
  56. package/dist-cli/chunks/{config-3MVPTQK6.js → config-75MZT5JS.js} +2 -2
  57. package/dist-cli/chunks/{control-FVYUXUVP.js → control-KSGYRJQK.js} +2 -2
  58. package/dist-cli/chunks/{cpu-profile-7ZH2O6BN.js → cpu-profile-2EHAVP5P.js} +2 -2
  59. package/dist-cli/chunks/{daemon-35SSSS4Z.js → daemon-KRESTNSO.js} +2 -2
  60. package/dist-cli/chunks/{debug-A6KPCY4Y.js → debug-XJ2XIBGI.js} +3 -3
  61. package/dist-cli/chunks/{detox-2UJ7GS2I.js → detox-D3P2BFHN.js} +2 -2
  62. package/dist-cli/chunks/{device-H7UO5Y3Q.js → device-TMFWZCML.js} +2 -2
  63. package/dist-cli/chunks/{diagnose-N2GVYOSJ.js → diagnose-67SOCM6I.js} +2 -2
  64. package/dist-cli/chunks/drivers-OYNUTXKG.js +2 -0
  65. package/dist-cli/chunks/{electron-CZLJU4MK.js → electron-FLPDCIDO.js} +3 -3
  66. package/dist-cli/chunks/flow-Q2Z4UAPG.js +2 -0
  67. package/dist-cli/chunks/help-EVEPPKOR.js +2 -0
  68. package/dist-cli/chunks/{hints-6EUSL5M3.js → hints-ODFXO5MG.js} +2 -2
  69. package/dist-cli/chunks/{home-paths-O66RZCQB.js → home-paths-NIEX6VIW.js} +2 -2
  70. package/dist-cli/chunks/{inspect-PI7TIHTS.js → inspect-C5R2IXQP.js} +3 -3
  71. package/dist-cli/chunks/install-H4QD5NHJ.js +2 -0
  72. package/dist-cli/chunks/{install-desktop-MTUTQFM2.js → install-desktop-TGI4DCY3.js} +3 -3
  73. package/dist-cli/chunks/{keys-FHLYPKGB.js → keys-2QJCGBZU.js} +2 -2
  74. package/dist-cli/chunks/{launch-4Y77LAZZ.js → launch-A2NXENLP.js} +3 -3
  75. package/dist-cli/chunks/{login-CM7CTLRY.js → login-TQLNNRFL.js} +4 -4
  76. package/dist-cli/chunks/{logout-U6ADFOLS.js → logout-N6R66AFU.js} +2 -2
  77. package/dist-cli/chunks/{maestro-CIEC2573.js → maestro-DMNUWMEX.js} +2 -2
  78. package/dist-cli/chunks/optional-demo-registry-Q2WTD5VQ.js +2 -0
  79. package/dist-cli/chunks/{preview-TBLTTHD5.js → preview-O45WIZT7.js} +2 -2
  80. package/dist-cli/chunks/{profile-KUBUPLTD.js → profile-HSL4JQ2B.js} +2 -2
  81. package/dist-cli/chunks/{react-DKVACIO3.js → react-STTXJK2P.js} +2 -2
  82. package/dist-cli/chunks/{record-VLV2V7SN.js → record-6X7TU345.js} +2 -2
  83. package/dist-cli/chunks/runtime-K3KCLSIW.js +2 -0
  84. package/dist-cli/chunks/{runtime-delivery-JXJLNWVF.js → runtime-delivery-D76JBTRS.js} +2 -2
  85. package/dist-cli/chunks/{screenshot-7QCTJTD4.js → screenshot-WLKERV6O.js} +2 -2
  86. package/dist-cli/chunks/{screenshot-mode-GJAESKSM.js → screenshot-mode-QGT6WGQ6.js} +2 -2
  87. package/dist-cli/chunks/{screenshots-43LUBWAM.js → screenshots-H4CT4M6U.js} +2 -2
  88. package/dist-cli/chunks/{server-7SWPNQR7.js → server-NNJNTLTV.js} +3 -3
  89. package/dist-cli/chunks/setup-repo-I4NU3XER.js +2 -0
  90. package/dist-cli/chunks/{skills-O2EXQOQN.js → skills-FTYSXEVA.js} +2 -2
  91. package/dist-cli/chunks/slides-FTSJM4AC.js +46 -0
  92. package/dist-cli/chunks/{start-W74HSVN6.js → start-Q37DNJPY.js} +4 -4
  93. package/dist-cli/chunks/store-YJZETFWA.js +2 -0
  94. package/dist-cli/chunks/telemetry-73CWUPJ6.js +2 -0
  95. package/dist-cli/chunks/{test-LTDFFPPX.js → test-7WGTTJFK.js} +3 -3
  96. package/dist-cli/chunks/{three-mode-RXT6PH5B.js → three-mode-T6V3MWE7.js} +2 -2
  97. package/dist-cli/chunks/{timeline-PETWH7FT.js → timeline-T36NNMIW.js} +2 -2
  98. package/dist-cli/chunks/{upgrade-OVR52N6Q.js → upgrade-D5LA6F7T.js} +2 -2
  99. package/dist-cli/chunks/upload-DR6AU55F.js +2 -0
  100. package/dist-cli/chunks/{version-JKXXLSJL.js → version-NEMFK44K.js} +2 -2
  101. package/dist-cli/chunks/{web-QZTGXJE5.js → web-EBBS27LE.js} +2 -2
  102. package/dist-cli/chunks/{what-happened-X2VG2PJO.js → what-happened-LWSM2L7E.js} +2 -2
  103. package/dist-cli/chunks/{whoami-YF6SXJQ5.js → whoami-AXLQJ7NW.js} +2 -2
  104. package/dist-lib/agent-daemon-client.cjs +1 -1
  105. package/dist-lib/agent-events.cjs +1 -1
  106. package/dist-lib/agent-sessions.cjs +1 -1
  107. package/dist-lib/attached-projects.cjs +1 -1
  108. package/dist-lib/auth/shared-session.cjs +1 -1
  109. package/dist-lib/backend-origin.cjs +1 -1
  110. package/dist-lib/beta.cjs +1 -1
  111. package/dist-lib/beta.mjs +1 -1
  112. package/dist-lib/bridge-constants.cjs +1 -1
  113. package/dist-lib/cli-constants.cjs +1 -1
  114. package/dist-lib/config.cjs +1 -1
  115. package/dist-lib/detox/index.cjs +1 -1
  116. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  117. package/dist-lib/home-paths.cjs +1 -1
  118. package/dist-lib/host/bridge-host.cjs +1 -1
  119. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  120. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  121. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  122. package/dist-lib/host/websocket-proxy.cjs +1 -1
  123. package/dist-lib/index.cjs +1 -1
  124. package/dist-lib/metro.cjs +1 -1
  125. package/dist-lib/profiles.cjs +1 -1
  126. package/dist-lib/render-mode.cjs +1 -1
  127. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  128. package/dist-lib/sdk.cjs +1 -1
  129. package/dist-lib/sdk.mjs +1 -1
  130. package/dist-lib/skills.cjs +324 -45
  131. package/dist-lib/vite.cjs +1 -1
  132. package/package.json +1 -1
  133. package/dist-cli/chunks/auto-bootstrap-GYRB6UPD.js +0 -2
  134. package/dist-cli/chunks/beta-5KJOURFA.js +0 -2
  135. package/dist-cli/chunks/chunk-A2ZAQHNC.js +0 -1
  136. package/dist-cli/chunks/chunk-BJNIH4HL.js +0 -119
  137. package/dist-cli/chunks/chunk-GRFBOOAG.js +0 -2
  138. package/dist-cli/chunks/chunk-GY6MAFMV.js +0 -2
  139. package/dist-cli/chunks/chunk-NGA4DA7H.js +0 -1
  140. package/dist-cli/chunks/chunk-X3H76FTW.js +0 -1
  141. package/dist-cli/chunks/cli-version-G27CVTU4.js +0 -2
  142. package/dist-cli/chunks/drivers-LZZBBF46.js +0 -2
  143. package/dist-cli/chunks/flow-D64O66ME.js +0 -2
  144. package/dist-cli/chunks/help-H6K3SYVG.js +0 -2
  145. package/dist-cli/chunks/install-4VWYAR7Y.js +0 -2
  146. package/dist-cli/chunks/optional-demo-registry-BONZAQPP.js +0 -2
  147. package/dist-cli/chunks/runtime-H6MVUEU5.js +0 -2
  148. package/dist-cli/chunks/setup-repo-7FNZWY7U.js +0 -2
  149. package/dist-cli/chunks/store-F4YJGQBN.js +0 -2
  150. package/dist-cli/chunks/telemetry-OUIYC7UJ.js +0 -2
  151. package/dist-cli/chunks/upload-CCWGR633.js +0 -2
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.143 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.145 | (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;
@@ -15342,6 +15342,231 @@ var init_app_state_reset = __esm({
15342
15342
  }
15343
15343
  });
15344
15344
 
15345
+ // cli/commands/inspect/core.ts
15346
+ function layoutGetNativeEval(opts = {}) {
15347
+ const includeStyle = opts.styling === true;
15348
+ return `(async () => {
15349
+ const t = window.__sootsimTest
15350
+ if (!t || typeof t.listInspectable !== 'function') return []
15351
+ const list = await t.listInspectable({})
15352
+ if (!Array.isArray(list)) return []
15353
+ const includeStyle = ${JSON.stringify(includeStyle)}
15354
+
15355
+ const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0)
15356
+ const round = (v) => Math.round(num(v))
15357
+
15358
+ // borderRadius can be authored as a string ("12px") or split per-corner;
15359
+ // prefer the uniform borderRadius, else the largest corner radius present.
15360
+ const radiusOf = (style) => {
15361
+ if (!style) return 0
15362
+ const keys = ['borderRadius','borderTopLeftRadius','borderTopRightRadius','borderBottomLeftRadius','borderBottomRightRadius']
15363
+ let max = 0
15364
+ for (const k of keys) {
15365
+ const raw = style[k]
15366
+ const n = typeof raw === 'number' ? raw : (typeof raw === 'string' ? parseFloat(raw) : NaN)
15367
+ if (Number.isFinite(n)) max = Math.max(max, n)
15368
+ }
15369
+ return Math.round(max)
15370
+ }
15371
+ const fontSizeOf = (style) => {
15372
+ if (!style) return 0
15373
+ const raw = style.fontSize
15374
+ const n = typeof raw === 'number' ? raw : (typeof raw === 'string' ? parseFloat(raw) : NaN)
15375
+ return Number.isFinite(n) ? Math.round(n) : 0
15376
+ }
15377
+ // styling extractors \u2014 omit boring defaults so the agent only sees notable
15378
+ // styling (low opacity, struck text, clipping, a real border/color).
15379
+ const colorStr = (v) => {
15380
+ if (v == null) return undefined
15381
+ const s = String(v).split(' ').join('')
15382
+ return s && s !== 'transparent' && s !== 'rgba(0,0,0,0)' ? s : undefined
15383
+ }
15384
+ const opacityOf = (style) => {
15385
+ const o = style && style.opacity
15386
+ const n = typeof o === 'number' ? o : (typeof o === 'string' ? parseFloat(o) : NaN)
15387
+ return Number.isFinite(n) && n < 1 ? Math.round(n * 100) / 100 : undefined
15388
+ }
15389
+ const weightOf = (style) => {
15390
+ const w = style && style.fontWeight
15391
+ const n = typeof w === 'number' ? w : (typeof w === 'string' ? parseInt(w, 10) : NaN)
15392
+ return Number.isFinite(n) && n >= 600 ? n : undefined
15393
+ }
15394
+ const decoOf = (style) => {
15395
+ const d = style && (style.textDecorationLine || style.textDecoration)
15396
+ return d && d !== 'none' && d !== 'normal' ? String(d).split(' ')[0] : undefined
15397
+ }
15398
+ const overflowOf = (style) => (style && style.overflow === 'hidden' ? 'hidden' : undefined)
15399
+ const borderWOf = (style) => {
15400
+ if (!style) return undefined
15401
+ const keys = ['borderWidth','borderTopWidth','borderBottomWidth','borderLeftWidth','borderRightWidth']
15402
+ let max = 0
15403
+ for (const k of keys) {
15404
+ const r = style[k]
15405
+ const n = typeof r === 'number' ? r : (typeof r === 'string' ? parseFloat(r) : NaN)
15406
+ if (Number.isFinite(n)) max = Math.max(max, n)
15407
+ }
15408
+ return max > 0 ? Math.round(max) : undefined
15409
+ }
15410
+
15411
+ const isVisible = (n) => {
15412
+ const layout = n && n.layout
15413
+ if (!layout || layout.width <= 0 || layout.height <= 0) return false
15414
+ const abs = (n && (n.absolute || n.absolutePosition)) || null
15415
+ if (!abs) return true
15416
+ const device = (n && n.device) || {}
15417
+ const screenW = Number(device.width) || window.innerWidth || 0
15418
+ const screenH = Number(device.height) || window.innerHeight || 0
15419
+ if (!screenW || !screenH) return true
15420
+ return abs.x + layout.width > 0 && abs.y + layout.height > 0 && abs.x < screenW && abs.y < screenH
15421
+ }
15422
+
15423
+ return list.filter(isVisible).map((n) => {
15424
+ const layout = n.layout || { x: 0, y: 0, width: 0, height: 0 }
15425
+ const abs = n.absolute || n.absolutePosition || { x: layout.x, y: layout.y }
15426
+ const text = typeof n.text === 'string' && n.text.trim() ? n.text.trim() : undefined
15427
+ const base = {
15428
+ testID: n.testID || n.testId || undefined,
15429
+ role: n.accessibilityRole || n.role || undefined,
15430
+ type: typeof n.type === 'string' ? n.type : 'node',
15431
+ text,
15432
+ box: { x: round(abs.x), y: round(abs.y), w: round(layout.width), h: round(layout.height) },
15433
+ borderRadius: 0,
15434
+ padding: { t: 0, r: 0, b: 0, l: 0 },
15435
+ fontSize: 0,
15436
+ }
15437
+ if (!includeStyle) return base
15438
+ // computedStyle has the resolved values; style is the curated subset \u2014
15439
+ // fall back across both so radius/fontSize survive either shape.
15440
+ const style = Object.assign({}, n.style || {}, n.computedStyle || {})
15441
+ const pad = (n.boxModel && n.boxModel.padding) || {}
15442
+ return {
15443
+ ...base,
15444
+ borderRadius: radiusOf(style),
15445
+ padding: { t: round(pad.top), r: round(pad.right), b: round(pad.bottom), l: round(pad.left) },
15446
+ fontSize: fontSizeOf(style),
15447
+ color: text ? colorStr(style.color) : undefined,
15448
+ bg: colorStr(style.backgroundColor),
15449
+ opacity: opacityOf(style),
15450
+ fontWeight: weightOf(style),
15451
+ textDecoration: decoOf(style),
15452
+ overflow: overflowOf(style),
15453
+ borderWidth: borderWOf(style),
15454
+ }
15455
+ })
15456
+ })()`;
15457
+ }
15458
+ function readyProbeHasContent(probe) {
15459
+ return probe.targets > 0 || probe.nodes >= READY_CONTENT_NODE_FLOOR;
15460
+ }
15461
+ function readyProbeHasTargetContent(probe) {
15462
+ return probe.targets > 0;
15463
+ }
15464
+ var LAYOUT_GET_NATIVE_EVAL, WAIT_READY_PROBE, READY_CONTENT_NODE_FLOOR, READY_NODE_STABLE_MS;
15465
+ var init_core = __esm({
15466
+ "cli/commands/inspect/core.ts"() {
15467
+ "use strict";
15468
+ LAYOUT_GET_NATIVE_EVAL = layoutGetNativeEval();
15469
+ WAIT_READY_PROBE = `(async () => {
15470
+ const t = window.__sootsimTest
15471
+ let nodes = 0
15472
+ try { nodes = (await t?.getNodeCount?.()) || 0 } catch {}
15473
+ let targets = 0
15474
+ let loadingText = ''
15475
+ try {
15476
+ if (typeof t?.listInspectable === 'function') {
15477
+ const list = await t.listInspectable({})
15478
+ if (Array.isArray(list)) {
15479
+ const screenW = window.innerWidth || 0
15480
+ const screenH = window.innerHeight || 0
15481
+ const isVisible = (n) => {
15482
+ const layout = n?.layout
15483
+ if (!layout || layout.width <= 0 || layout.height <= 0) return false
15484
+ const abs = n?.absolutePosition || n?.absolute
15485
+ if (!abs) return true
15486
+ const device = n?.device || {}
15487
+ const w = Number(device.width) || screenW
15488
+ const h = Number(device.height) || screenH
15489
+ return (
15490
+ abs.x + layout.width > 0 &&
15491
+ abs.y + layout.height > 0 &&
15492
+ abs.x < w &&
15493
+ abs.y < h
15494
+ )
15495
+ }
15496
+ const hasContentSignal = (n) => {
15497
+ const role = n?.accessibilityRole || n?.role
15498
+ const label = n?.accessibilityLabel || n?.label
15499
+ const handlers = Array.isArray(n?.handlers) ? n.handlers : []
15500
+ const text = typeof n?.text === 'string' ? n.text.trim() : ''
15501
+ const testID = typeof (n?.testID || n?.testId) === 'string' ? (n.testID || n.testId) : ''
15502
+ if (!loadingText && /^(opening app|loading|capturing|connecting)$/i.test(text)) {
15503
+ loadingText = text
15504
+ }
15505
+ if (handlers.length > 0) return true
15506
+ if (role) return true
15507
+ if (n?.isTextInput) return true
15508
+ if (text) return true
15509
+ if (label) return true
15510
+ if (testID && !/^splash|loading/i.test(testID)) return true
15511
+ return false
15512
+ }
15513
+ targets = list.filter(n => isVisible(n) && hasContentSignal(n)).length
15514
+ }
15515
+ }
15516
+ } catch {}
15517
+ let externalReady = null
15518
+ let externalStatus = ''
15519
+ let externalError = ''
15520
+ try {
15521
+ const getExternalAppState = t?.getExternalAppState
15522
+ const external = typeof getExternalAppState === 'function'
15523
+ ? await Promise.race([
15524
+ getExternalAppState(),
15525
+ new Promise((resolve) => setTimeout(
15526
+ () => resolve({
15527
+ state: {
15528
+ ready: false,
15529
+ loading: true,
15530
+ status: 'waiting for tenant worker...',
15531
+ },
15532
+ }),
15533
+ 1000,
15534
+ )),
15535
+ ])
15536
+ : null
15537
+ const state = external?.state
15538
+ if (state && typeof state === 'object') {
15539
+ externalReady = state.ready === true
15540
+ if (typeof state.status === 'string') externalStatus = state.status
15541
+ if (typeof state.error === 'string') externalError = state.error
15542
+ }
15543
+ if (!externalError && Array.isArray(external?.entryErrors)) {
15544
+ const entry = external.entryErrors.find((item) => {
15545
+ return item && typeof item.message === 'string' && item.message.trim()
15546
+ })
15547
+ if (entry) externalError = entry.message
15548
+ }
15549
+ if (externalReady === false && externalStatus) loadingText = externalStatus
15550
+ } catch {}
15551
+ let errors = 0
15552
+ try { errors = window.__sootsimConsole?.count?.()?.errors ?? 0 } catch {}
15553
+ return {
15554
+ flag: (window).__sootsimExternalAppReady,
15555
+ at: (window).__sootsimExternalAppReadyAt || 0,
15556
+ nodes,
15557
+ targets,
15558
+ errors,
15559
+ loadingText,
15560
+ externalReady,
15561
+ externalStatus,
15562
+ externalError,
15563
+ }
15564
+ })()`;
15565
+ READY_CONTENT_NODE_FLOOR = 100;
15566
+ READY_NODE_STABLE_MS = 750;
15567
+ }
15568
+ });
15569
+
15345
15570
  // cli/commands/inspect/settling.ts
15346
15571
  async function waitForSootsimIdle({
15347
15572
  bridge,
@@ -16067,6 +16292,31 @@ function parsePoint(value) {
16067
16292
  y: parseCoord(rawY, SCREEN_H)
16068
16293
  };
16069
16294
  }
16295
+ function normalizeBareStringStep(verb) {
16296
+ switch (verb) {
16297
+ case "back":
16298
+ return { back: true };
16299
+ case "hideKeyboard":
16300
+ return { hideKeyboard: true };
16301
+ case "waitForAnimationToEnd":
16302
+ return { waitForAnimationToEnd: true };
16303
+ case "stopApp":
16304
+ return { stopApp: true };
16305
+ case "clearState":
16306
+ return { clearState: true };
16307
+ case "clearKeychain":
16308
+ return { clearKeychain: true };
16309
+ case "eraseText":
16310
+ return { eraseText: 50 };
16311
+ case "scroll":
16312
+ case "scrollDown":
16313
+ return { scroll: { direction: "DOWN" } };
16314
+ case "scrollUp":
16315
+ return { scroll: { direction: "UP" } };
16316
+ default:
16317
+ return { [verb]: true };
16318
+ }
16319
+ }
16070
16320
  function parseFlowSteps(content) {
16071
16321
  const parts = content.split(/^---$/m);
16072
16322
  const candidate = parts.length > 1 ? parts[parts.length - 1] : content;
@@ -16083,6 +16333,7 @@ var init_bridge_flow_runner = __esm({
16083
16333
  import_yaml = __toESM(require("yaml"), 1);
16084
16334
  init_frame_compose();
16085
16335
  init_app_state_reset();
16336
+ init_core();
16086
16337
  init_settling();
16087
16338
  init_maestro_js();
16088
16339
  init_recording_access();
@@ -16185,40 +16436,32 @@ var init_bridge_flow_runner = __esm({
16185
16436
  let lastError = null;
16186
16437
  let consecutiveBridgeFailures = 0;
16187
16438
  let lastProgressAt = 0;
16439
+ let lastNodes = -1;
16440
+ let nodeStableSince = startedAt;
16188
16441
  while (Date.now() < deadline) {
16189
16442
  try {
16190
- const state = await this.evaluate(
16191
- `(async () => {
16192
- const test = window.__sootsimTest
16193
- if (!test) return { count: 0, loading: true }
16194
- const count = (await test.getNodeCount?.()) || 0
16195
- const external = globalThis.__sootsimExternalAppState
16196
- if (external?.error) {
16197
- return { count, loading: false, error: String(external.error) }
16198
- }
16199
- if (external) {
16200
- return {
16201
- count,
16202
- loading: !external.ready,
16203
- error: null,
16204
- }
16205
- }
16206
- const loadingNode = await test.findByText?.('loading')
16207
- const capturingNode = await test.findByText?.('capturing')
16208
- const openingAppNode = await test.findByText?.('opening app')
16209
- const isLoading = !!loadingNode || !!capturingNode || !!openingAppNode || count < 5
16210
- return { count, loading: isLoading, error: null }
16211
- })()`,
16443
+ const probe = await this.evaluate(
16444
+ WAIT_READY_PROBE,
16212
16445
  // the readiness probe is cheap — cap it well under the bridge-wide
16213
16446
  // command timeout so a non-responsive sim is detected in seconds,
16214
16447
  // not after a full 15s-per-poll stall.
16215
16448
  8e3
16216
16449
  );
16217
16450
  consecutiveBridgeFailures = 0;
16218
- if (state?.error) {
16219
- throw new Error(`app failed to load: ${state.error}`);
16451
+ if (probe?.externalError) {
16452
+ throw new Error(`app failed to load: ${probe.externalError}`);
16453
+ }
16454
+ if (probe) {
16455
+ const now = Date.now();
16456
+ if (probe.nodes !== lastNodes) {
16457
+ lastNodes = probe.nodes;
16458
+ nodeStableSince = now;
16459
+ }
16460
+ const hasReadySignal = probe.flag === true && readyProbeHasContent(probe);
16461
+ const hasRenderedContentWithoutReadySignal = probe.flag !== true && readyProbeHasTargetContent(probe);
16462
+ const ready = (hasReadySignal || hasRenderedContentWithoutReadySignal) && probe.externalReady !== false && !probe.externalError && !probe.loadingText && now - nodeStableSince >= READY_NODE_STABLE_MS;
16463
+ if (ready) return;
16220
16464
  }
16221
- if (state && state.count > 10 && !state.loading) return;
16222
16465
  } catch (err) {
16223
16466
  lastError = err instanceof Error ? err : new Error(String(err));
16224
16467
  if (lastError.message.startsWith("app failed to load:")) {
@@ -16448,18 +16691,26 @@ var init_bridge_flow_runner = __esm({
16448
16691
  element = await this.findElement({ text: opts.text, id: opts.id });
16449
16692
  }
16450
16693
  if (element) {
16451
- const tapTarget = typeof element.nodeId === "number" ? await this.resolveTapTarget(element.nodeId) : null;
16452
- const cx = tapTarget?.cx ?? element.absolutePosition.x + element.layout.width / 2;
16453
- const cy = tapTarget?.cy ?? element.absolutePosition.y + element.layout.height / 2;
16454
- const hit = await this.tap(cx, cy, {
16455
- id: opts.id ?? tapTarget?.target?.id ?? element.id ?? null,
16456
- testID: opts.id ?? tapTarget?.target?.testID ?? element.testID ?? null,
16457
- text: opts.text ?? tapTarget?.target?.text ?? tapTarget?.target?.accessibilityLabel ?? element.text ?? element.accessibilityLabel ?? null,
16458
- type: tapTarget?.target?.type ?? element.type ?? null
16459
- });
16460
- if (hit?.hit === true) {
16461
- await sleep(300);
16462
- return;
16694
+ const tapInputAttempts = element.isTextInput ? 4 : 1;
16695
+ for (let attempt = 0; attempt < tapInputAttempts; attempt++) {
16696
+ const current = attempt === 0 ? element : await this.findElement({ text: opts.text, id: opts.id });
16697
+ if (!current) break;
16698
+ const tapTarget = typeof current.nodeId === "number" ? await this.resolveTapTarget(current.nodeId) : null;
16699
+ const cx = tapTarget?.cx ?? current.absolutePosition.x + current.layout.width / 2;
16700
+ const cy = tapTarget?.cy ?? current.absolutePosition.y + current.layout.height / 2;
16701
+ const hit = await this.tap(cx, cy, {
16702
+ id: opts.id ?? tapTarget?.target?.id ?? current.id ?? null,
16703
+ testID: opts.id ?? tapTarget?.target?.testID ?? current.testID ?? null,
16704
+ text: opts.text ?? tapTarget?.target?.text ?? tapTarget?.target?.accessibilityLabel ?? current.text ?? current.accessibilityLabel ?? null,
16705
+ type: tapTarget?.target?.type ?? current.type ?? null
16706
+ });
16707
+ if (hit?.hit === true) {
16708
+ await sleep(300);
16709
+ if (!current.isTextInput) return;
16710
+ if (await this.waitForFocusedTextInput(600)) return;
16711
+ continue;
16712
+ }
16713
+ break;
16463
16714
  }
16464
16715
  }
16465
16716
  const direct = await this.activatePressTarget(opts);
@@ -16515,6 +16766,7 @@ var init_bridge_flow_runner = __esm({
16515
16766
  return !!(element && element.absolutePosition.y + element.layout.height > 0 && element.absolutePosition.y < SCREEN_H);
16516
16767
  }
16517
16768
  async inputText(text) {
16769
+ await this.waitForFocusedTextInput();
16518
16770
  await this.bridge.send({
16519
16771
  type: "keyboard",
16520
16772
  simId: this.opts.simId,
@@ -16523,6 +16775,23 @@ var init_bridge_flow_runner = __esm({
16523
16775
  });
16524
16776
  await sleep(200);
16525
16777
  }
16778
+ // poll for a focused text input. returns true once getFocusedNode reports a
16779
+ // node (focus landed), false if the budget elapses. callers use it both as a
16780
+ // tap-focus confirmation (tapOn retry) and a pre-type barrier (inputText).
16781
+ async waitForFocusedTextInput(timeoutMs = 1500) {
16782
+ const deadline = Date.now() + timeoutMs;
16783
+ while (Date.now() < deadline) {
16784
+ try {
16785
+ const focused = await this.callTest(
16786
+ "getFocusedNode"
16787
+ );
16788
+ if (focused) return true;
16789
+ } catch {
16790
+ }
16791
+ await sleep(50);
16792
+ }
16793
+ return false;
16794
+ }
16526
16795
  async pressKey(name) {
16527
16796
  await this.bridge.send({
16528
16797
  type: "keyboard",
@@ -17182,7 +17451,8 @@ var init_bridge_flow_runner = __esm({
17182
17451
  const value = entry ? entry[1] : void 0;
17183
17452
  return !!(value && typeof value === "object" && "optional" in value && value.optional);
17184
17453
  }
17185
- async runStep(rawStep, stepIndex) {
17454
+ async runStep(rawStepInput, stepIndex) {
17455
+ const rawStep = typeof rawStepInput === "string" ? normalizeBareStringStep(rawStepInput) : rawStepInput;
17186
17456
  const stepName = Object.keys(rawStep).find((k) => k !== "when") || Object.keys(rawStep)[0];
17187
17457
  console.log(`[flow] step ${stepIndex + 1}: ${stepName}`);
17188
17458
  let step;
@@ -17307,15 +17577,24 @@ var init_bridge_flow_runner = __esm({
17307
17577
  await this.assertTreeContains(step.assertTreeContains);
17308
17578
  } else if (typeof step.eraseText === "number") {
17309
17579
  await this.eraseText(step.eraseText);
17310
- } else if (step.repeat) {
17311
- const times = Number(step.repeat.times);
17312
- if (!Number.isFinite(times)) {
17313
- throw new Error(`repeat.times is not a number: ${step.repeat.times}`);
17314
- }
17315
- for (let i = 0; i < times; i++) {
17580
+ } else if (step.repeat && Array.isArray(step.repeat.commands)) {
17581
+ const rawTimes = step.repeat.times;
17582
+ const maxRuns = rawTimes === void 0 || rawTimes === null || rawTimes === "" ? Number.MAX_SAFE_INTEGER : Number(rawTimes);
17583
+ if (!Number.isFinite(maxRuns)) {
17584
+ throw new Error(`repeat.times is not a number: ${rawTimes}`);
17585
+ }
17586
+ const condition = step.repeat.when ?? step.repeat.condition;
17587
+ const checkCondition = async () => {
17588
+ if (!condition) return true;
17589
+ return this.evaluateWhen(condition);
17590
+ };
17591
+ const hardCap = condition ? maxRuns : Math.min(maxRuns, 1e3);
17592
+ let i = 0;
17593
+ while (i < hardCap && await checkCondition()) {
17316
17594
  for (let j = 0; j < step.repeat.commands.length; j++) {
17317
17595
  await this.runStep(step.repeat.commands[j], j);
17318
17596
  }
17597
+ i++;
17319
17598
  }
17320
17599
  } else if (step.stopApp !== void 0) {
17321
17600
  await this.stopApp();
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.143 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.145 | (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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sootsim",
3
- "version": "0.1.143",
3
+ "version": "0.1.145",
4
4
  "description": "sootsim CLI + vite/metro plugins + skills registry. bridge client for driving the proprietary sootsim-engine over WebSocket.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "MIT",
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.143 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a,b,c,d}from"./chunk-TPWSHYVO.js";import"./chunk-34SQBAH2.js";import"./chunk-MREBZSAV.js";import"./chunk-DFKP5F2D.js";import"./chunk-7TN63AI6.js";import"./chunk-VNNV5YRW.js";export{c as ensureDaemonRunning,a as ensureRuntimeInstalled,d as ensureSootsimReady,b as resolveBootstrapPort};
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.143 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a,b,c,d,e}from"./chunk-XMMYQPSE.js";import"./chunk-VNNV5YRW.js";export{e as BETA_ASK_HEADLINE,c as BETA_LABEL,d as BETA_TAGLINE,b as BETA_VERSION_TARGET,a as IS_BETA};
@@ -1 +0,0 @@
1
- /*! sootsim v0.1.143 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */