sootsim 0.1.183 → 0.1.185

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 (156) hide show
  1. package/README.md +22 -19
  2. package/dist-cli/bin.js +6 -6
  3. package/dist-cli/chunks/{agent-AFAFZKMT.js → agent-VF756RZU.js} +2 -2
  4. package/dist-cli/chunks/{agent-wrapper-MV5WGYCS.js → agent-wrapper-IAFL7ROV.js} +2 -2
  5. package/dist-cli/chunks/{app-fonts-ERD5J644.js → app-fonts-VWLEC7ZA.js} +2 -2
  6. package/dist-cli/chunks/{assert-IBMYHSKD.js → assert-I55QVGG7.js} +2 -2
  7. package/dist-cli/chunks/beta-X6YPQVZH.js +2 -0
  8. package/dist-cli/chunks/{camera-Y6XUIEAP.js → camera-CYYBJH7W.js} +2 -2
  9. package/dist-cli/chunks/{chunk-XGVVYY6P.js → chunk-2T73IGB2.js} +2 -2
  10. package/dist-cli/chunks/{chunk-BTIARV2B.js → chunk-4NUQVW5P.js} +17 -4
  11. package/dist-cli/chunks/{chunk-FODJBAWY.js → chunk-4OUQAR34.js} +1 -1
  12. package/dist-cli/chunks/{chunk-RQDJPT6N.js → chunk-7OAUPGLN.js} +2 -2
  13. package/dist-cli/chunks/{chunk-BYKEWDZO.js → chunk-ARNLQPMQ.js} +4 -4
  14. package/dist-cli/chunks/{chunk-AP22KA3Q.js → chunk-BI56RR3G.js} +1 -1
  15. package/dist-cli/chunks/{chunk-WN6XO6JV.js → chunk-BMCBPMTC.js} +1 -1
  16. package/dist-cli/chunks/{chunk-64EKAMA3.js → chunk-C3UEINOZ.js} +2 -2
  17. package/dist-cli/chunks/{chunk-VSDVOOWG.js → chunk-C63EVPDN.js} +2 -2
  18. package/dist-cli/chunks/{chunk-CD3KUHS3.js → chunk-CYBRFHPU.js} +2 -2
  19. package/dist-cli/chunks/chunk-DZLBZGHN.js +62 -0
  20. package/dist-cli/chunks/{chunk-QP4W3SGO.js → chunk-E5DE33BU.js} +1 -1
  21. package/dist-cli/chunks/{chunk-WV7QBUFY.js → chunk-EXFB33AZ.js} +2 -2
  22. package/dist-cli/chunks/{chunk-QF6RJQ2U.js → chunk-FHJTTB3W.js} +2 -2
  23. package/dist-cli/chunks/{chunk-UXHEGFHQ.js → chunk-FUHXL6BW.js} +1 -1
  24. package/dist-cli/chunks/{chunk-WOOWTKM7.js → chunk-FY6DJGOS.js} +1 -1
  25. package/dist-cli/chunks/{chunk-YSTCZI6L.js → chunk-GX6MKIFC.js} +1 -1
  26. package/dist-cli/chunks/{chunk-JR7LKXPB.js → chunk-HQG4DRQ3.js} +1 -1
  27. package/dist-cli/chunks/chunk-IGJHME3T.js +1 -0
  28. package/dist-cli/chunks/{chunk-C7BIPAXT.js → chunk-IJP257WP.js} +1 -1
  29. package/dist-cli/chunks/chunk-JRL7PFNZ.js +463 -0
  30. package/dist-cli/chunks/{chunk-7SP7PFUR.js → chunk-K2THZIVC.js} +1 -1
  31. package/dist-cli/chunks/chunk-KOKIECMB.js +118 -0
  32. package/dist-cli/chunks/{chunk-IJMDXS7N.js → chunk-LI4RLX6B.js} +1 -1
  33. package/dist-cli/chunks/{chunk-3JW7SSYR.js → chunk-LJ3SOZBF.js} +2 -2
  34. package/dist-cli/chunks/{chunk-6TNJLDAQ.js → chunk-M3P67TBN.js} +1 -1
  35. package/dist-cli/chunks/{chunk-OIFGVAV2.js → chunk-MADEJDFD.js} +1 -1
  36. package/dist-cli/chunks/{chunk-RC3FKTYP.js → chunk-NL5IFN4T.js} +2 -2
  37. package/dist-cli/chunks/chunk-OAHGYDJR.js +13 -0
  38. package/dist-cli/chunks/{chunk-RQXZANGL.js → chunk-PDLQWZN4.js} +1 -1
  39. package/dist-cli/chunks/{chunk-KSV6H2IC.js → chunk-PIUKLO3W.js} +1 -1
  40. package/dist-cli/chunks/{chunk-FO3RPGBR.js → chunk-PKZNXDUJ.js} +2 -2
  41. package/dist-cli/chunks/{chunk-N2EXJ7HK.js → chunk-QQ4NSPAD.js} +2 -2
  42. package/dist-cli/chunks/{chunk-W3G5M4UB.js → chunk-QTFCJYBF.js} +3 -3
  43. package/dist-cli/chunks/{chunk-CPZNH3B3.js → chunk-R4KLD4P4.js} +3 -3
  44. package/dist-cli/chunks/chunk-SL6565MR.js +1 -0
  45. package/dist-cli/chunks/{chunk-QQDPEM7F.js → chunk-TFZO277G.js} +1 -1
  46. package/dist-cli/chunks/{chunk-4FVYQIRB.js → chunk-TNHF6BON.js} +1 -1
  47. package/dist-cli/chunks/{chunk-L3FPLPA4.js → chunk-TUF6EPCQ.js} +2 -2
  48. package/dist-cli/chunks/chunk-V4M2R6FZ.js +2 -0
  49. package/dist-cli/chunks/{chunk-6GCEDCO5.js → chunk-V5JEPE3P.js} +2 -2
  50. package/dist-cli/chunks/{chunk-PY4P54SD.js → chunk-WBY4JJ2K.js} +2 -2
  51. package/dist-cli/chunks/{chunk-RAEIXWER.js → chunk-WEJKLJ4B.js} +2 -2
  52. package/dist-cli/chunks/{chunk-SGWTX6CO.js → chunk-X4KS33ZC.js} +2 -2
  53. package/dist-cli/chunks/{chunk-HW65ZTXM.js → chunk-XU5EMZ4B.js} +2 -2
  54. package/dist-cli/chunks/cli-version-ICHQMND6.js +2 -0
  55. package/dist-cli/chunks/{compat-ZTFIQOVI.js → compat-ZJA3N4ZT.js} +3 -3
  56. package/dist-cli/chunks/{control-XWU7LZWC.js → control-CZFSYSYG.js} +2 -2
  57. package/dist-cli/chunks/{cpu-profile-3F2OQNCM.js → cpu-profile-ACSQMVXY.js} +2 -2
  58. package/dist-cli/chunks/daemon-337B4P3B.js +2 -0
  59. package/dist-cli/chunks/{debug-7MIHREPG.js → debug-W2ZOVTQO.js} +3 -3
  60. package/dist-cli/chunks/{detox-CDYD54PS.js → detox-ANJO6T27.js} +2 -2
  61. package/dist-cli/chunks/{device-H53IDQ67.js → device-KPPN4KYD.js} +2 -2
  62. package/dist-cli/chunks/{diagnose-YWSZCFPG.js → diagnose-5PEJYGES.js} +2 -2
  63. package/dist-cli/chunks/drivers-MSDIFHBD.js +2 -0
  64. package/dist-cli/chunks/electron-U456QLSQ.js +19 -0
  65. package/dist-cli/chunks/flow-JL3SC6HC.js +2 -0
  66. package/dist-cli/chunks/help-PA4RTR5X.js +2 -0
  67. package/dist-cli/chunks/{hints-BYH6BMHP.js → hints-MBBQLMQU.js} +2 -2
  68. package/dist-cli/chunks/{home-paths-AXKOJS62.js → home-paths-KQKYP2SE.js} +2 -2
  69. package/dist-cli/chunks/{inspect-TGQU6MZQ.js → inspect-ZP5SM4EW.js} +62 -62
  70. package/dist-cli/chunks/{install-desktop-AOJACCWA.js → install-desktop-OR6ZJ43X.js} +4 -4
  71. package/dist-cli/chunks/{keys-RC6SP7XY.js → keys-JSQMBUE7.js} +2 -2
  72. package/dist-cli/chunks/{login-EQDNEJFU.js → login-DVMWAI3G.js} +4 -4
  73. package/dist-cli/chunks/{logout-QEDUWSSY.js → logout-RIVPFE5N.js} +2 -2
  74. package/dist-cli/chunks/{maestro-6SVETVRL.js → maestro-TX6FPTVH.js} +8 -6
  75. package/dist-cli/chunks/optional-demo-registry-ONYG6DUI.js +2 -0
  76. package/dist-cli/chunks/{preview-UNXWRROE.js → preview-3AD7WNA6.js} +2 -2
  77. package/dist-cli/chunks/{profile-UBHQS25I.js → profile-HY55VFNR.js} +2 -2
  78. package/dist-cli/chunks/{react-THVP5NZX.js → react-HDRCKCKV.js} +2 -2
  79. package/dist-cli/chunks/{record-SOACBRM3.js → record-HW4C7QYN.js} +2 -2
  80. package/dist-cli/chunks/runtime-RHHPW5WO.js +2 -0
  81. package/dist-cli/chunks/{screenshot-MJKWIVV6.js → screenshot-XFR3QN5T.js} +2 -2
  82. package/dist-cli/chunks/{screenshot-mode-RP67FQSA.js → screenshot-mode-Y5CVO267.js} +2 -2
  83. package/dist-cli/chunks/{screenshots-LWDOE4RD.js → screenshots-RKUL2ZZT.js} +2 -2
  84. package/dist-cli/chunks/{screenshots-capture-RUXUL5GO.js → screenshots-capture-RVNF75ZD.js} +8 -8
  85. package/dist-cli/chunks/{serve-TMMMRVTI.js → serve-QMRYLWL5.js} +2 -2
  86. package/dist-cli/chunks/setup-ELNJS7AI.js +97 -0
  87. package/dist-cli/chunks/{skills-P3HEQGPB.js → skills-GPSJYS4W.js} +2 -2
  88. package/dist-cli/chunks/{slides-MICILFXO.js → slides-OCFNPIFG.js} +2 -2
  89. package/dist-cli/chunks/store-MBKDUQEQ.js +2 -0
  90. package/dist-cli/chunks/telemetry-46XXIUEE.js +2 -0
  91. package/dist-cli/chunks/{telemetry-7ZVCPD7R.js → telemetry-KFZQUFG5.js} +2 -2
  92. package/dist-cli/chunks/{test-NT7SGPG5.js → test-Z2HZXCZM.js} +3 -3
  93. package/dist-cli/chunks/{three-mode-6ADYURNM.js → three-mode-NKJFCDYS.js} +2 -2
  94. package/dist-cli/chunks/{timeline-JZNPRKEH.js → timeline-PRGQMQRR.js} +2 -2
  95. package/dist-cli/chunks/upgrade-3NW7JRRX.js +4 -0
  96. package/dist-cli/chunks/upload-QVDVJ6ZK.js +2 -0
  97. package/dist-cli/chunks/{version-GS2J6SFA.js → version-FDTZRIYT.js} +2 -2
  98. package/dist-cli/chunks/{web-J2XC4OK4.js → web-4N5QQEQJ.js} +2 -2
  99. package/dist-cli/chunks/{what-happened-O5MCMYJL.js → what-happened-IX2RRCOS.js} +2 -2
  100. package/dist-cli/chunks/{whoami-Z2Y2X2DN.js → whoami-QZ42FYYF.js} +2 -2
  101. package/dist-lib/agent-daemon-client.cjs +1 -1
  102. package/dist-lib/agent-events.cjs +1 -1
  103. package/dist-lib/agent-sessions.cjs +1 -1
  104. package/dist-lib/attached-projects.cjs +1 -1
  105. package/dist-lib/auth/shared-session.cjs +1 -1
  106. package/dist-lib/backend-origin.cjs +1 -1
  107. package/dist-lib/beta.cjs +1 -1
  108. package/dist-lib/beta.mjs +1 -1
  109. package/dist-lib/bridge-constants.cjs +1 -1
  110. package/dist-lib/cli-constants.cjs +1 -1
  111. package/dist-lib/config.cjs +1 -1
  112. package/dist-lib/detox/index.cjs +1 -1
  113. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  114. package/dist-lib/home-paths.cjs +1 -1
  115. package/dist-lib/host/bridge-host.cjs +1 -1
  116. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  117. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  118. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  119. package/dist-lib/host/websocket-proxy.cjs +1 -1
  120. package/dist-lib/index.cjs +1 -1
  121. package/dist-lib/jump-to-source-babel.cjs +1 -1
  122. package/dist-lib/menu.cjs +1 -1
  123. package/dist-lib/menu.mjs +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 +218 -47
  131. package/dist-lib/vite.cjs +1 -1
  132. package/package.json +1 -1
  133. package/scripts/postinstall.cjs +1 -1
  134. package/skills/soot.md +2 -2
  135. package/skills/sootsim-setup.md +7 -6
  136. package/dist-cli/chunks/beta-T7Z7WSTK.js +0 -2
  137. package/dist-cli/chunks/chunk-4HJKPRWO.js +0 -463
  138. package/dist-cli/chunks/chunk-77GTIGLV.js +0 -1
  139. package/dist-cli/chunks/chunk-B3W63SZA.js +0 -13
  140. package/dist-cli/chunks/chunk-D7ZB6A5T.js +0 -2
  141. package/dist-cli/chunks/chunk-F6DIP2WH.js +0 -62
  142. package/dist-cli/chunks/chunk-MBR6TG2P.js +0 -118
  143. package/dist-cli/chunks/chunk-NLI4YQOY.js +0 -1
  144. package/dist-cli/chunks/cli-version-P4SZNBPQ.js +0 -2
  145. package/dist-cli/chunks/daemon-CUQB4QAP.js +0 -2
  146. package/dist-cli/chunks/drivers-RRCIIDGS.js +0 -2
  147. package/dist-cli/chunks/electron-UPH35PTF.js +0 -18
  148. package/dist-cli/chunks/flow-ZDJI7ICC.js +0 -2
  149. package/dist-cli/chunks/help-U5QGSMDR.js +0 -2
  150. package/dist-cli/chunks/optional-demo-registry-KXYBVP4S.js +0 -2
  151. package/dist-cli/chunks/runtime-BAS2IZFB.js +0 -2
  152. package/dist-cli/chunks/setup-WYKWQPJW.js +0 -77
  153. package/dist-cli/chunks/store-7CIU7X6W.js +0 -2
  154. package/dist-cli/chunks/telemetry-LVBDBPPV.js +0 -2
  155. package/dist-cli/chunks/upgrade-K6V3HYCU.js +0 -4
  156. package/dist-cli/chunks/upload-Q2ZFKOSR.js +0 -2
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.185 | (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;
@@ -15834,6 +15834,25 @@ function readyProbeHasContent(probe) {
15834
15834
  function readyProbeHasTargetContent(probe) {
15835
15835
  return probe.targets > 0;
15836
15836
  }
15837
+ function isShellCommandUnavailable(error) {
15838
+ const message = error instanceof Error ? error.message : String(error);
15839
+ return message.includes("call target not found: SootSim.bridges.mainShell") || message.includes("test bridge unavailable before app-in-worker boot");
15840
+ }
15841
+ async function getShellState(bridge, readyTimeoutMs = 0) {
15842
+ const deadline = Date.now() + Math.max(0, readyTimeoutMs);
15843
+ while (true) {
15844
+ try {
15845
+ return await bridge.send({
15846
+ type: "call",
15847
+ path: "SootSim.bridges.mainShell.getState",
15848
+ args: []
15849
+ });
15850
+ } catch (error) {
15851
+ if (!isShellCommandUnavailable(error) || Date.now() >= deadline) throw error;
15852
+ await new Promise((r) => setTimeout(r, 50));
15853
+ }
15854
+ }
15855
+ }
15837
15856
  var LAYOUT_GET_NATIVE_EVAL, WAIT_READY_PROBE, READY_CONTENT_NODE_FLOOR, READY_NODE_STABLE_MS;
15838
15857
  var init_core = __esm({
15839
15858
  "cli/commands/inspect/core.ts"() {
@@ -16104,6 +16123,32 @@ var init_settling = __esm({
16104
16123
  }
16105
16124
  });
16106
16125
 
16126
+ // cli/commands/inspect/shared.ts
16127
+ function sleep(ms) {
16128
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
16129
+ }
16130
+ async function callShellCommand(bridge, method, ...args) {
16131
+ return callInBridgeWrite(bridge, `SootSim.bridges.mainShell.${method}`, ...args);
16132
+ }
16133
+ async function callShellCommandWhenReady(bridge, method, readyTimeoutMs, ...args) {
16134
+ const deadline = Date.now() + Math.max(0, readyTimeoutMs);
16135
+ while (true) {
16136
+ try {
16137
+ return await callShellCommand(bridge, method, ...args);
16138
+ } catch (error) {
16139
+ if (!isShellCommandUnavailable(error) || Date.now() >= deadline) throw error;
16140
+ await sleep(50);
16141
+ }
16142
+ }
16143
+ }
16144
+ var init_shared = __esm({
16145
+ "cli/commands/inspect/shared.ts"() {
16146
+ "use strict";
16147
+ init_ws_bridge();
16148
+ init_core();
16149
+ }
16150
+ });
16151
+
16107
16152
  // cli/maestro-js.ts
16108
16153
  function executeSyncHttp(url, method, params) {
16109
16154
  if (params?.multipartForm) {
@@ -16675,9 +16720,33 @@ __export(bridge_flow_runner_exports, {
16675
16720
  resolveFlowScreenshotPath: () => resolveFlowScreenshotPath,
16676
16721
  resolveFlowScreenshotPathWithMode: () => resolveFlowScreenshotPathWithMode
16677
16722
  });
16678
- function sleep(ms) {
16723
+ function sleep2(ms) {
16679
16724
  return new Promise((resolve6) => setTimeout(resolve6, ms));
16680
16725
  }
16726
+ function isObjectRecord(value) {
16727
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
16728
+ }
16729
+ function readStringKey(value, key) {
16730
+ if (!isObjectRecord(value)) return null;
16731
+ const raw = value[key];
16732
+ return typeof raw === "string" && raw.length > 0 ? raw : null;
16733
+ }
16734
+ function readBooleanKey(value, key) {
16735
+ if (!isObjectRecord(value)) return false;
16736
+ return value[key] === true;
16737
+ }
16738
+ function readRecentShellAppId(state) {
16739
+ if (!state) return null;
16740
+ const recentApps = state.recentApps;
16741
+ if (Array.isArray(recentApps)) {
16742
+ for (const app of recentApps) {
16743
+ const id = readStringKey(app, "id");
16744
+ if (id) return id;
16745
+ }
16746
+ }
16747
+ const bindings = state.surfaceBindings;
16748
+ return readStringKey(bindings, "app:one") || readStringKey(bindings, "app:two") || null;
16749
+ }
16681
16750
  function resolveFlowScreenshotPath(screenshotDir, name) {
16682
16751
  return resolveFlowScreenshotPathWithMode(screenshotDir, name, {
16683
16752
  mode: "dir"
@@ -16761,6 +16830,7 @@ var init_bridge_flow_runner = __esm({
16761
16830
  init_app_state_reset();
16762
16831
  init_core();
16763
16832
  init_settling();
16833
+ init_shared();
16764
16834
  init_maestro_js();
16765
16835
  init_recording_access();
16766
16836
  DEFAULT_TIMEOUT = 1e4;
@@ -16909,7 +16979,7 @@ var init_bridge_flow_runner = __esm({
16909
16979
  ` [flow] waiting for app to be ready\u2026 (${Math.round(elapsed / 1e3)}s)`
16910
16980
  );
16911
16981
  }
16912
- await sleep(500);
16982
+ await sleep2(500);
16913
16983
  }
16914
16984
  if (lastError?.message.startsWith("app failed to load:")) {
16915
16985
  throw lastError;
@@ -16992,7 +17062,7 @@ var init_bridge_flow_runner = __esm({
16992
17062
  const maxMs = Math.max(0, Math.round(opts.maxMs));
16993
17063
  if (maxMs <= 0) return;
16994
17064
  if (!opts.smart) {
16995
- await sleep(maxMs);
17065
+ await sleep2(maxMs);
16996
17066
  return;
16997
17067
  }
16998
17068
  const result = await waitForSootsimIdle({
@@ -17202,7 +17272,7 @@ var init_bridge_flow_runner = __esm({
17202
17272
  if (!direct2?.ok) {
17203
17273
  await this.tap(point.x, point.y);
17204
17274
  }
17205
- await sleep(300);
17275
+ await sleep2(300);
17206
17276
  return;
17207
17277
  }
17208
17278
  const TAP_WAIT_DEADLINE_MS = 5e3;
@@ -17210,7 +17280,7 @@ var init_bridge_flow_runner = __esm({
17210
17280
  const deadline = Date.now() + TAP_WAIT_DEADLINE_MS;
17211
17281
  let element = await this.findElement({ text: opts.text, id: opts.id });
17212
17282
  while (!element && Date.now() < deadline) {
17213
- await sleep(TAP_WAIT_POLL_MS);
17283
+ await sleep2(TAP_WAIT_POLL_MS);
17214
17284
  element = await this.findElement({ text: opts.text, id: opts.id });
17215
17285
  }
17216
17286
  if (element) {
@@ -17228,9 +17298,9 @@ var init_bridge_flow_runner = __esm({
17228
17298
  type: tapTarget?.target?.type ?? current.type ?? null
17229
17299
  });
17230
17300
  if (hit?.hit === true) {
17231
- await sleep(300);
17301
+ await sleep2(300);
17232
17302
  if (!current.isTextInput) return;
17233
- if (await this.waitForFocusedTextInput(600)) return;
17303
+ if (await this.waitForFocusedTextInput(600, current)) return;
17234
17304
  continue;
17235
17305
  }
17236
17306
  break;
@@ -17238,14 +17308,14 @@ var init_bridge_flow_runner = __esm({
17238
17308
  }
17239
17309
  const direct = await this.activatePressTarget(opts);
17240
17310
  if (direct?.ok) {
17241
- await sleep(300);
17311
+ await sleep2(300);
17242
17312
  return;
17243
17313
  }
17244
17314
  throw new Error(`tapOn: element not found: ${JSON.stringify(opts)}`);
17245
17315
  }
17246
17316
  async tapAtCoords(x, y) {
17247
17317
  await this.tap(x, y);
17248
- await sleep(300);
17318
+ await sleep2(300);
17249
17319
  }
17250
17320
  async doubleTapAtCoords(x, y, gapMs = 80) {
17251
17321
  const waitMs = Math.max(0, Math.round(gapMs));
@@ -17262,7 +17332,7 @@ var init_bridge_flow_runner = __esm({
17262
17332
  return !!second && second.hit !== false
17263
17333
  })()`);
17264
17334
  if (!ok) throw new Error(`doubleTapAtCoords failed at (${x}, ${y})`);
17265
- await sleep(300);
17335
+ await sleep2(300);
17266
17336
  }
17267
17337
  async assertVisible(target) {
17268
17338
  const opts = typeof target === "string" ? { text: target } : target;
@@ -17270,7 +17340,7 @@ var init_bridge_flow_runner = __esm({
17270
17340
  if (await this.isElementVisible(opts)) {
17271
17341
  return;
17272
17342
  }
17273
- await sleep(200);
17343
+ await sleep2(200);
17274
17344
  }
17275
17345
  const found = await this.findElement(opts);
17276
17346
  const detail = found ? ` (matched node at y=${Math.round(found.absolutePosition.y)} h=${Math.round(found.layout.height)} \u2014 off-screen)` : " (no matching node in tree)";
@@ -17289,29 +17359,48 @@ var init_bridge_flow_runner = __esm({
17289
17359
  return !!(element && element.absolutePosition.y + element.layout.height > 0 && element.absolutePosition.y < SCREEN_H);
17290
17360
  }
17291
17361
  async inputText(text) {
17292
- await this.waitForFocusedTextInput();
17362
+ if (!await this.waitForFocusedTextInput()) {
17363
+ throw new Error(
17364
+ "inputText: no focused TextInput; tap the input and wait for focus before typing"
17365
+ );
17366
+ }
17293
17367
  await this.bridge.send({
17294
17368
  type: "keyboard",
17295
17369
  simId: this.opts.simId,
17296
17370
  action: "type",
17297
17371
  text
17298
17372
  });
17299
- await sleep(200);
17373
+ await sleep2(200);
17300
17374
  }
17301
17375
  // poll for a focused text input. returns true once getFocusedNode reports a
17302
17376
  // node (focus landed), false if the budget elapses. callers use it both as a
17303
17377
  // tap-focus confirmation (tapOn retry) and a pre-type barrier (inputText).
17304
- async waitForFocusedTextInput(timeoutMs = 1500) {
17378
+ focusedTextInputMatches(focused, expected) {
17379
+ if (!expected) return true;
17380
+ if (typeof expected.nodeId === "number" && typeof focused.nodeId === "number" && expected.nodeId === focused.nodeId) {
17381
+ return true;
17382
+ }
17383
+ const expectedTestID = expected.testID ?? null;
17384
+ const focusedTestID = focused.testID ?? null;
17385
+ if (expectedTestID && focusedTestID && expectedTestID === focusedTestID) {
17386
+ return true;
17387
+ }
17388
+ const expectedId = expected.id ?? null;
17389
+ const focusedId = focused.id ?? null;
17390
+ if (expectedId && focusedId && expectedId === focusedId) {
17391
+ return true;
17392
+ }
17393
+ return false;
17394
+ }
17395
+ async waitForFocusedTextInput(timeoutMs = 1500, expected) {
17305
17396
  const deadline = Date.now() + timeoutMs;
17306
17397
  while (Date.now() < deadline) {
17307
17398
  try {
17308
- const focused = await this.callTest(
17309
- "getFocusedNode"
17310
- );
17311
- if (focused) return true;
17399
+ const focused = await this.callTest("getFocusedNode");
17400
+ if (focused && this.focusedTextInputMatches(focused, expected)) return true;
17312
17401
  } catch {
17313
17402
  }
17314
- await sleep(50);
17403
+ await sleep2(50);
17315
17404
  }
17316
17405
  return false;
17317
17406
  }
@@ -17322,7 +17411,7 @@ var init_bridge_flow_runner = __esm({
17322
17411
  action: "press",
17323
17412
  text: name
17324
17413
  });
17325
- await sleep(120);
17414
+ await sleep2(120);
17326
17415
  }
17327
17416
  async dispatchKey(ch) {
17328
17417
  await this.bridge.send({
@@ -17331,7 +17420,7 @@ var init_bridge_flow_runner = __esm({
17331
17420
  action: "dispatchKey",
17332
17421
  text: ch
17333
17422
  });
17334
- await sleep(120);
17423
+ await sleep2(120);
17335
17424
  }
17336
17425
  async hideKeyboard() {
17337
17426
  try {
@@ -17357,24 +17446,29 @@ var init_bridge_flow_runner = __esm({
17357
17446
  }
17358
17447
  })()`);
17359
17448
  if (!state?.visible && !state?.focused) {
17360
- await sleep(80);
17449
+ await sleep2(80);
17361
17450
  return;
17362
17451
  }
17363
- await sleep(80);
17452
+ await sleep2(80);
17364
17453
  }
17365
17454
  }
17366
17455
  async eraseText(count) {
17367
17456
  for (let i = 0; i < count; i++) {
17368
- await this.pressKey("Backspace");
17457
+ await this.bridge.send({
17458
+ type: "keyboard",
17459
+ simId: this.opts.simId,
17460
+ action: "dispatchKey",
17461
+ text: "Backspace"
17462
+ });
17369
17463
  }
17370
- await sleep(100);
17464
+ await sleep2(100);
17371
17465
  }
17372
17466
  async waitFor(opts) {
17373
17467
  const deadline = Date.now() + (opts.timeout || DEFAULT_TIMEOUT);
17374
17468
  while (Date.now() < deadline) {
17375
17469
  const element = await this.findElement(opts);
17376
17470
  if (element) return;
17377
- await sleep(200);
17471
+ await sleep2(200);
17378
17472
  }
17379
17473
  const final = await this.findElement(opts);
17380
17474
  const detail = final ? ` (matched node y=${Math.round(final.absolutePosition.y)} \u2014 still off-screen at deadline)` : "";
@@ -17386,7 +17480,7 @@ var init_bridge_flow_runner = __esm({
17386
17480
  if (!this.hasFreshVisualSettle()) {
17387
17481
  try {
17388
17482
  await this.waitForVisualSettle();
17389
- await sleep(250);
17483
+ await sleep2(250);
17390
17484
  } catch {
17391
17485
  }
17392
17486
  }
@@ -17586,7 +17680,7 @@ var init_bridge_flow_runner = __esm({
17586
17680
  if (!result?.ok) {
17587
17681
  throw new Error("drag failed");
17588
17682
  }
17589
- await sleep(300);
17683
+ await sleep2(300);
17590
17684
  }
17591
17685
  async swipeCoords(start, end, duration = 300) {
17592
17686
  const from = parsePoint(start);
@@ -17630,7 +17724,7 @@ var init_bridge_flow_runner = __esm({
17630
17724
  if (!result?.ok) {
17631
17725
  throw new Error(`scrollTo failed: ${result?.reason || "unknown error"}`);
17632
17726
  }
17633
- await sleep(250);
17727
+ await sleep2(250);
17634
17728
  }
17635
17729
  async pinch(opts) {
17636
17730
  const result = await this.evaluate(`(async () => {
@@ -17646,7 +17740,7 @@ var init_bridge_flow_runner = __esm({
17646
17740
  if (!result?.ok) {
17647
17741
  throw new Error(`pinch failed: ${result?.reason || "unknown error"}`);
17648
17742
  }
17649
- await sleep(250);
17743
+ await sleep2(250);
17650
17744
  }
17651
17745
  async dumpTree(depth = 6) {
17652
17746
  const tree = await this.bridge.send({
@@ -17703,7 +17797,7 @@ var init_bridge_flow_runner = __esm({
17703
17797
  return;
17704
17798
  }
17705
17799
  await this.swipe(swipeDirection, 250);
17706
- await sleep(500);
17800
+ await sleep2(500);
17707
17801
  }
17708
17802
  throw new Error(`scrollUntilVisible: "${opts.element}" not found`);
17709
17803
  }
@@ -17717,7 +17811,7 @@ var init_bridge_flow_runner = __esm({
17717
17811
  typeof opts.notVisible === "string" ? { text: opts.notVisible } : opts.notVisible
17718
17812
  ) : true;
17719
17813
  if (visibleOkay && notVisibleOkay) return;
17720
- await sleep(200);
17814
+ await sleep2(200);
17721
17815
  }
17722
17816
  throw new Error("extendedWaitUntil timed out");
17723
17817
  }
@@ -17744,7 +17838,7 @@ var init_bridge_flow_runner = __esm({
17744
17838
  PER_ATTEMPT_MS
17745
17839
  );
17746
17840
  } catch {
17747
- await sleep(1e3);
17841
+ await sleep2(1e3);
17748
17842
  }
17749
17843
  }
17750
17844
  if (!cleared) {
@@ -17755,17 +17849,54 @@ var init_bridge_flow_runner = __esm({
17755
17849
  }
17756
17850
  await this.evaluate("window.location.reload()").catch(() => {
17757
17851
  });
17758
- await sleep(500);
17852
+ await sleep2(500);
17759
17853
  await this.refreshSimId();
17760
17854
  }
17855
+ async softReloadGuestApp(resetStorage) {
17856
+ const reloaded = await this.evaluate(
17857
+ resetGuestAppStateEval(resetStorage),
17858
+ 12e4
17859
+ );
17860
+ if (!reloaded) {
17861
+ throw new Error("guest app soft reload bridge unavailable");
17862
+ }
17863
+ }
17864
+ async rearmCaptureAfterRuntimeReload() {
17865
+ if (!this.recordingEnabled && !this.profilingEnabled) return;
17866
+ const deadline = Date.now() + 1e4;
17867
+ let lastError = null;
17868
+ while (Date.now() < deadline) {
17869
+ await this.refreshSimId();
17870
+ try {
17871
+ if (this.recordingEnabled) await this.ensureRecordingStarted();
17872
+ if (this.profilingEnabled) await this.startProfile();
17873
+ return;
17874
+ } catch (err) {
17875
+ lastError = err;
17876
+ await sleep2(250);
17877
+ }
17878
+ }
17879
+ throw new Error(
17880
+ `capture re-arm failed after app reload: ${lastError instanceof Error ? lastError.message : String(lastError)}`
17881
+ );
17882
+ }
17761
17883
  async launchApp(opts) {
17762
17884
  await this.refreshSimId();
17763
17885
  if (opts && typeof opts === "object" && opts.arguments) {
17764
17886
  console.log("[flow] launchApp.arguments ignored (sootsim has no native process)");
17765
17887
  }
17766
17888
  const wantsClear = !!(opts && typeof opts === "object" && opts.clearState);
17889
+ const wantsResetRuntime = readBooleanKey(opts, "resetRuntime");
17767
17890
  if (!this.firstLaunchDone && !wantsClear) {
17768
17891
  this.firstLaunchDone = true;
17892
+ if (wantsResetRuntime) {
17893
+ if (this.recordingEnabled) await this.ensureRecordingStarted();
17894
+ await this.softReloadGuestApp(false);
17895
+ await this.launchShellAppFromHomeIfNeeded(opts);
17896
+ await this.waitForTree(12e4);
17897
+ } else {
17898
+ await this.launchShellAppFromHomeIfNeeded(opts);
17899
+ }
17769
17900
  await this.waitForTree(3e4);
17770
17901
  } else {
17771
17902
  this.firstLaunchDone = true;
@@ -17802,7 +17933,7 @@ var init_bridge_flow_runner = __esm({
17802
17933
  })`);
17803
17934
  this.markVisualSettled();
17804
17935
  } catch {
17805
- await sleep(180);
17936
+ await sleep2(180);
17806
17937
  this.markVisualSettled();
17807
17938
  }
17808
17939
  }
@@ -17812,6 +17943,45 @@ var init_bridge_flow_runner = __esm({
17812
17943
  hasFreshVisualSettle() {
17813
17944
  return Date.now() - this.lastVisualSettledAtMs < 750;
17814
17945
  }
17946
+ resolveShellLaunchAppId(opts, state) {
17947
+ if (typeof opts === "string" && opts.length > 0) return opts;
17948
+ if (isObjectRecord(opts)) {
17949
+ const explicit = readStringKey(opts, "appId") || readStringKey(opts, "id");
17950
+ if (explicit) return explicit;
17951
+ }
17952
+ return readRecentShellAppId(state) || "connect";
17953
+ }
17954
+ async waitForShellAppLaunched(appId, timeoutMs) {
17955
+ const deadline = Date.now() + timeoutMs;
17956
+ let lastState = null;
17957
+ while (Date.now() < deadline) {
17958
+ try {
17959
+ lastState = await getShellState(this.bridge);
17960
+ } catch {
17961
+ lastState = null;
17962
+ }
17963
+ if (lastState?.state === "app" && lastState.activeApp === appId && lastState.showSwitcher === false && typeof lastState.launchProgress === "number" && lastState.launchProgress >= 0.98) {
17964
+ return;
17965
+ }
17966
+ await sleep2(16);
17967
+ }
17968
+ }
17969
+ async launchShellAppFromHomeIfNeeded(opts) {
17970
+ let state = null;
17971
+ try {
17972
+ state = await getShellState(this.bridge);
17973
+ } catch {
17974
+ return false;
17975
+ }
17976
+ if (state?.state !== "home" || state.activeApp != null) return false;
17977
+ const appId = this.resolveShellLaunchAppId(opts, state);
17978
+ if (this.recordingEnabled) {
17979
+ await this.ensureRecordingStarted();
17980
+ }
17981
+ await callShellCommandWhenReady(this.bridge, "launchApp", 1e3, appId);
17982
+ await this.waitForShellAppLaunched(appId, 1e3);
17983
+ return true;
17984
+ }
17815
17985
  async startProfile() {
17816
17986
  await this.evaluate(`((sessionKey) => {
17817
17987
  const perf = window.__sootsimPerf
@@ -17914,18 +18084,19 @@ var init_bridge_flow_runner = __esm({
17914
18084
  }
17915
18085
  return result;
17916
18086
  }
17917
- // maestro stopApp: tear down current app runtime. mirrors the real cli,
17918
- // which kills the running process. sootsim has no separate process, so we
17919
- // soft-reload the guest app to a fresh cleared mount the next launchApp
17920
- // finds it ready. (does NOT unregister the service worker: it holds the
17921
- // engine's own bundle chunks, and wiping them 404s the next heavy reload.)
18087
+ // maestro stopApp: tear down the current app runtime without clearing app
18088
+ // data. sootsim has no separate native process, so a hard page reload is the
18089
+ // nearest equivalent to killing and later re-launching the tenant runtime.
18090
+ // State clearing belongs to clearState / launchApp({ clearState: true }).
17922
18091
  async stopApp() {
17923
- await this.reloadGuestApp(true);
18092
+ await this.reloadGuestApp(false);
18093
+ await this.rearmCaptureAfterRuntimeReload();
17924
18094
  }
17925
18095
  // maestro clearState: wipe app-owned storage and relaunch fresh. routes
17926
18096
  // through the same engine soft-reload launchApp uses under `clearState: true`.
17927
18097
  async clearState() {
17928
18098
  await this.reloadGuestApp(true);
18099
+ await this.rearmCaptureAfterRuntimeReload();
17929
18100
  }
17930
18101
  // maestro clearKeychain: there is no real keychain in sootsim's browser
17931
18102
  // sandbox. log loudly so the author notices, then continue — matching the
@@ -17990,7 +18161,7 @@ var init_bridge_flow_runner = __esm({
17990
18161
  if (!result?.ok) {
17991
18162
  throw new Error(`openLink failed: ${result?.error || "no test bridge"}`);
17992
18163
  }
17993
- await sleep(300);
18164
+ await sleep2(300);
17994
18165
  }
17995
18166
  // evaluate a `when:` predicate. returns true if the step should run.
17996
18167
  async evaluateWhen(when) {
@@ -18074,7 +18245,7 @@ var init_bridge_flow_runner = __esm({
18074
18245
  }
18075
18246
  const isAssertion = !!(step.assertVisible || step.assertNotVisible || step.extendedWaitUntil || step.waitFor);
18076
18247
  if (this.stepDelay > 0 && !isAssertion) {
18077
- await sleep(this.stepDelay);
18248
+ await sleep2(this.stepDelay);
18078
18249
  }
18079
18250
  } catch (error) {
18080
18251
  if (this.isOptional(step)) {
@@ -18131,7 +18302,7 @@ var init_bridge_flow_runner = __esm({
18131
18302
  } else if (step.launchApp) {
18132
18303
  await this.launchApp(step.launchApp);
18133
18304
  } else if (typeof step.wait === "number") {
18134
- await sleep(step.wait);
18305
+ await sleep2(step.wait);
18135
18306
  } else if (step.dumpTree) {
18136
18307
  await this.dumpTree(step.dumpTree);
18137
18308
  } else if (step.tapAtCoords) {
@@ -18585,7 +18756,7 @@ var skill4 = {
18585
18756
  };
18586
18757
 
18587
18758
  // src/skills/builtin/test-flow.ts
18588
- function sleep2(ms) {
18759
+ function sleep3(ms) {
18589
18760
  return new Promise((resolve6) => setTimeout(resolve6, ms));
18590
18761
  }
18591
18762
  var skill5 = {
@@ -18631,7 +18802,7 @@ var skill5 = {
18631
18802
  simId = opened.id;
18632
18803
  break;
18633
18804
  }
18634
- await sleep2(500);
18805
+ await sleep3(500);
18635
18806
  }
18636
18807
  if (!simId) {
18637
18808
  throw new Error("no sim connected to the bridge host");
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! sootsim v0.1.185 | (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.183",
3
+ "version": "0.1.185",
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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // postinstall — best-effort runtime install after `npm i -g sootsim`.
2
+ // postinstall — best-effort runtime install after npm/npx fetches sootsim.
3
3
  //
4
4
  // runs as a child of npm. failures here are non-fatal; setup/open/runtime
5
5
  // commands can install the runtime later.
package/skills/soot.md CHANGED
@@ -60,7 +60,7 @@ automation, inspecting the render tree, and managing sims.
60
60
  | `sootsim claim` | take an exclusive lease on a sim |
61
61
  | `sootsim agent` | attached projects + local agent sessions (codex/claude) |
62
62
  | `sootsim close` | close a connected sim |
63
- | `sootsim setup` | set up this machine and, optionally, this repo |
63
+ | `sootsim setup` | bootstrap SootSim, then install the global CLI |
64
64
  | `sootsim compat` | check package compatibility (alias: scan) |
65
65
  | `sootsim electron` | launch the desktop companion |
66
66
  | `sootsim daemon` | manage the sootsim bridge as a login agent |
@@ -104,7 +104,7 @@ skills for working with the SootSim native preview engine:
104
104
  | skill | description |
105
105
  | --- | --- |
106
106
  | `/sootsim-debug` | Debug rendering, animation, gesture, performance, and accessibility issues in a React Native app running under sootsim. Reach for this whenever an animation looks wrong (janky, stutters, snaps, only updates once, or doesn't move at all), a swipe/transition/gesture misbehaves or feels broken, scrolling drops frames or feels slow, the UI renders the wrong tree/props, or a screen reader can't reach something — and drive the sootsim CLI debug tooling first, not screenshots or ad-hoc logging. |
107
- | `/sootsim-setup` | Get sootsim connected to your React Native app — from `npm install sootsim` to a driveable sim |
107
+ | `/sootsim-setup` | Get sootsim connected to your React Native app — from `sootsim setup` to a driveable sim |
108
108
  | `/sootsim-test` | Run automated tests against your React Native app under sootsim — native YAML flows, Maestro drop-in, or Detox drop-in |
109
109
  | `/sootsim-visual` | Pixel-level rendering comparison between sootsim and native iOS — fonts, gradients, shadows, image fidelity |
110
110
 
@@ -1,13 +1,14 @@
1
1
  ---
2
2
  name: sootsim-setup
3
- description: Get sootsim connected to your React Native app — from `npm install sootsim` to a driveable sim
3
+ description: Get sootsim connected to your React Native app — from `sootsim setup` to a driveable sim
4
4
  ---
5
5
 
6
6
  # sootsim setup
7
7
 
8
- you've installed sootsim into a React Native project and want to drive it
9
- from the CLI. this skill takes you from zero to a connected, pinned sim
10
- that every other sootsim skill assumes exists. if `sootsim describe` already
8
+ you want to drive a React Native app with SootSim from the CLI. this skill
9
+ takes you from `sootsim setup` to a connected, pinned sim that every other
10
+ sootsim skill assumes exists. no per-app SootSim install is required. if
11
+ `sootsim describe` already
11
12
  returns a tree, you're past this skill — load `/sootsim-debug` for
12
13
  debugging or `/sootsim-test` for writing tests.
13
14
 
@@ -26,7 +27,7 @@ debugging or `/sootsim-test` for writing tests.
26
27
  ## first move
27
28
 
28
29
  ```sh
29
- sootsim setup # one-time installs daemon, optional repo setup
30
+ sootsim setup # one-time guided setup; installs daemon
30
31
  sootsim open 8081 # load a metro/expo dev server
31
32
  sootsim list # confirm exactly one sim is reachable
32
33
  sootsim use <id> # pin it for the rest of the workflow
@@ -38,7 +39,7 @@ the detected dev servers. for hosted or unusual targets, pass the URL
38
39
  explicitly:
39
40
 
40
41
  ```sh
41
- sootsim open 8081 # load a metro dev server
42
+ sootsim open 8081 # load a metro dev server
42
43
  sootsim open https://my-app.local # load a hosted bundle
43
44
  ```
44
45
 
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.1.183 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a,b,c,d}from"./chunk-6TNJLDAQ.js";import"./chunk-YSTCZI6L.js";export{d as BETA_ASK_HEADLINE,b as BETA_LABEL,c as BETA_TAGLINE,a as IS_BETA};