sootsim 0.1.226 → 0.1.228

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 (177) hide show
  1. package/README.md +14 -14
  2. package/detox/element-types.ts +3 -3
  3. package/detox/expectations.ts +29 -24
  4. package/detox/gestures.ts +16 -16
  5. package/detox/index.ts +34 -32
  6. package/dist-cli/bin.js +4 -4
  7. package/dist-cli/chunks/{agent-6RLGQQAV.js → agent-6P5T2QHC.js} +2 -2
  8. package/dist-cli/chunks/{agent-wrapper-FVSNJGBC.js → agent-wrapper-SMEMJZXI.js} +2 -2
  9. package/dist-cli/chunks/{app-fonts-CT4QE2DG.js → app-fonts-6OQTLX3U.js} +2 -2
  10. package/dist-cli/chunks/{assert-W3NC7YNE.js → assert-ZAYFOW6R.js} +2 -2
  11. package/dist-cli/chunks/{auth-R7Y7QI66.js → auth-Y53UANSH.js} +2 -2
  12. package/dist-cli/chunks/beta-ZQCASJ7C.js +2 -0
  13. package/dist-cli/chunks/{camera-Z7FSPTSI.js → camera-AJQWSOO4.js} +2 -2
  14. package/dist-cli/chunks/{chunk-G4EERBF2.js → chunk-2I62CFYI.js} +3 -3
  15. package/dist-cli/chunks/{chunk-7GUEXZKF.js → chunk-34MFG4IM.js} +7 -7
  16. package/dist-cli/chunks/{chunk-4FFUGYCE.js → chunk-4A6HM2K3.js} +1 -1
  17. package/dist-cli/chunks/chunk-4QOY4T27.js +3 -0
  18. package/dist-cli/chunks/{chunk-XWVYQTWQ.js → chunk-6Q2B7QHC.js} +5 -5
  19. package/dist-cli/chunks/{chunk-HJY4JYOF.js → chunk-6U3GMNTP.js} +1 -1
  20. package/dist-cli/chunks/{chunk-BANHHF53.js → chunk-72AQH53U.js} +1 -1
  21. package/dist-cli/chunks/chunk-7HMRWJHH.js +2 -0
  22. package/dist-cli/chunks/{chunk-NXRX5TMH.js → chunk-7TJVPCQA.js} +6 -6
  23. package/dist-cli/chunks/{chunk-FL2LU77A.js → chunk-A5SO4OGE.js} +1 -1
  24. package/dist-cli/chunks/{chunk-EV5654BH.js → chunk-AJ3CDBW6.js} +2 -2
  25. package/dist-cli/chunks/chunk-ALE5MU5Z.js +2 -0
  26. package/dist-cli/chunks/{chunk-TXWRQBSG.js → chunk-AVS7RL73.js} +2 -2
  27. package/dist-cli/chunks/chunk-AWF575Y5.js +1 -0
  28. package/dist-cli/chunks/chunk-BHNOPI7I.js +2 -0
  29. package/dist-cli/chunks/{chunk-3ECLF2KG.js → chunk-D2QUTBZP.js} +2 -2
  30. package/dist-cli/chunks/{chunk-33ICOBHB.js → chunk-DEVNCWS4.js} +6 -6
  31. package/dist-cli/chunks/{chunk-4M7IRWFT.js → chunk-E5FOMYR6.js} +1 -1
  32. package/dist-cli/chunks/chunk-EFMPSG7T.js +3 -0
  33. package/dist-cli/chunks/{chunk-FU2YJXGO.js → chunk-EKWOBYJO.js} +2 -2
  34. package/dist-cli/chunks/{chunk-BED4PLYP.js → chunk-EPQ5EZBJ.js} +2 -2
  35. package/dist-cli/chunks/{chunk-SWXABYVD.js → chunk-FDSFA7WL.js} +3 -3
  36. package/dist-cli/chunks/{chunk-EWKFEKZX.js → chunk-FSKQZHM2.js} +2 -2
  37. package/dist-cli/chunks/{chunk-LMFS4ITS.js → chunk-G42TLJVQ.js} +18 -18
  38. package/dist-cli/chunks/chunk-JA52F67H.js +8 -0
  39. package/dist-cli/chunks/chunk-JKZY5CPQ.js +2 -0
  40. package/dist-cli/chunks/{chunk-BTQGLGVN.js → chunk-JV43GSZB.js} +2 -2
  41. package/dist-cli/chunks/{chunk-RGI2QABU.js → chunk-JWZMAJC7.js} +1 -1
  42. package/dist-cli/chunks/{chunk-2KXBJELA.js → chunk-KIL5F7KD.js} +1 -1
  43. package/dist-cli/chunks/chunk-KY2KLPYL.js +1 -0
  44. package/dist-cli/chunks/{chunk-FZADDKVW.js → chunk-LBXHG4AU.js} +2 -2
  45. package/dist-cli/chunks/{chunk-AAXWQESK.js → chunk-MCMMRIJV.js} +1 -1
  46. package/dist-cli/chunks/{chunk-FEFQUXJP.js → chunk-MWZOCZ7J.js} +2 -2
  47. package/dist-cli/chunks/{chunk-XL43Q7OB.js → chunk-NCXHUU7N.js} +1 -1
  48. package/dist-cli/chunks/{chunk-IA5LIQRX.js → chunk-NME4QWJ3.js} +2 -2
  49. package/dist-cli/chunks/{chunk-GCHZKFHB.js → chunk-OETQYAI2.js} +2 -2
  50. package/dist-cli/chunks/chunk-PJBYZZWR.js +2 -0
  51. package/dist-cli/chunks/{chunk-3AYAZ6BT.js → chunk-PNPH4JLS.js} +1 -1
  52. package/dist-cli/chunks/chunk-RT3OS624.js +118 -0
  53. package/dist-cli/chunks/{chunk-GJWZ65QM.js → chunk-SACWV3LZ.js} +20 -20
  54. package/dist-cli/chunks/chunk-SPJSTRXX.js +24 -0
  55. package/dist-cli/chunks/{chunk-PTATA2F4.js → chunk-UYLESZU3.js} +1 -1
  56. package/dist-cli/chunks/{chunk-WF47H4KR.js → chunk-WWZL7HNB.js} +29 -29
  57. package/dist-cli/chunks/{chunk-2YIWRDQG.js → chunk-ZBJ5JMXM.js} +2 -2
  58. package/dist-cli/chunks/chunk-ZWJEJR73.js +4 -0
  59. package/dist-cli/chunks/cli-version-V4GJYM74.js +2 -0
  60. package/dist-cli/chunks/{compat-A4QGEWEF.js → compat-W7E4744D.js} +3 -3
  61. package/dist-cli/chunks/{config-YLHN3CWC.js → config-N7G65EY2.js} +2 -2
  62. package/dist-cli/chunks/{control-MTKT6267.js → control-7VDFEVWU.js} +2 -2
  63. package/dist-cli/chunks/daemon-UI3PQ47F.js +2 -0
  64. package/dist-cli/chunks/{debug-EE6BQVA4.js → debug-ZKUCSCOU.js} +3 -3
  65. package/dist-cli/chunks/{desktop-EU3ZYXAK.js → desktop-L3OXTT45.js} +3 -3
  66. package/dist-cli/chunks/{detox-K5CPLEZN.js → detox-QG4IBZAG.js} +2 -2
  67. package/dist-cli/chunks/{device-PRSG6GUM.js → device-QC6MDI7W.js} +2 -2
  68. package/dist-cli/chunks/{diagnose-GNP3HUOE.js → diagnose-HTUDENJK.js} +2 -2
  69. package/dist-cli/chunks/drivers-L2BXXDI7.js +2 -0
  70. package/dist-cli/chunks/{film-SHWP5F7U.js → film-CT7FHNLE.js} +3 -3
  71. package/dist-cli/chunks/flow-XUBXWJHL.js +2 -0
  72. package/dist-cli/chunks/help-7CIGANJN.js +2 -0
  73. package/dist-cli/chunks/hidden-runtime-alias-YTUEPZ3U.js +2 -0
  74. package/dist-cli/chunks/{home-paths-N56HBC4S.js → home-paths-7K6HDXOP.js} +2 -2
  75. package/dist-cli/chunks/inspect-ZY3QI7VJ.js +2 -0
  76. package/dist-cli/chunks/install-desktop-CVPRYFFV.js +2 -0
  77. package/dist-cli/chunks/{login-LK75Q5WK.js → login-XHOOXAOW.js} +4 -4
  78. package/dist-cli/chunks/{logout-DFOTMKE4.js → logout-7BISNC5F.js} +2 -2
  79. package/dist-cli/chunks/{maestro-6YM5KA2P.js → maestro-YD3G7LSG.js} +3 -3
  80. package/dist-cli/chunks/{maestro-generate-CHYG2L5C.js → maestro-generate-WNOWZC5K.js} +3 -3
  81. package/dist-cli/chunks/{mode-D5XU5PGG.js → mode-Y6JJMXU5.js} +2 -2
  82. package/dist-cli/chunks/optional-demo-registry-YUXURWKX.js +2 -0
  83. package/dist-cli/chunks/{perf-ACC64R7U.js → perf-AY3ZKTDS.js} +2 -2
  84. package/dist-cli/chunks/{record-BE7PQUPE.js → record-ZC4W6QN6.js} +10 -10
  85. package/dist-cli/chunks/runtime-MVYAP3UX.js +2 -0
  86. package/dist-cli/chunks/screenshot-command-NRKTGTSJ.js +151 -0
  87. package/dist-cli/chunks/{screenshots-capture-6W3KTBUD.js → screenshots-capture-LZR3AHWN.js} +3 -3
  88. package/dist-cli/chunks/{serve-XBIN4KC3.js → serve-4X2WI5XF.js} +5 -5
  89. package/dist-cli/chunks/{setup-IFZLKN5S.js → setup-KODPGJBJ.js} +3 -3
  90. package/dist-cli/chunks/{skills-MPRCDS35.js → skills-DEAUDOAN.js} +2 -2
  91. package/dist-cli/chunks/{storage-YZDVWKTL.js → storage-P7FQRH5T.js} +2 -2
  92. package/dist-cli/chunks/store-PTRXOHHV.js +2 -0
  93. package/dist-cli/chunks/telemetry-JHKOHSI2.js +2 -0
  94. package/dist-cli/chunks/{timeline-QA6L2PCD.js → timeline-7ISYXLR3.js} +2 -2
  95. package/dist-cli/chunks/{upgrade-JUAUWHUE.js → upgrade-NZBJS5L2.js} +3 -3
  96. package/dist-cli/chunks/upload-J34H5SXV.js +2 -0
  97. package/dist-cli/chunks/{version-D3P356LR.js → version-J6BLGLTO.js} +2 -2
  98. package/dist-cli/chunks/{web-4TXUAD54.js → web-Y6UVYM3Z.js} +2 -2
  99. package/dist-cli/chunks/{what-happened-CFULL2ZS.js → what-happened-YKXXY6W2.js} +2 -2
  100. package/dist-lib/agent-daemon-client.cjs +1 -1
  101. package/dist-lib/agent-events.cjs +1 -1
  102. package/dist-lib/agent-identity.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 +2 -2
  106. package/dist-lib/backend-origin.cjs +18 -18
  107. package/dist-lib/beta.cjs +1 -1
  108. package/dist-lib/beta.mjs +1 -1
  109. package/dist-lib/bridge-constants.cjs +4 -4
  110. package/dist-lib/cli-constants.cjs +1 -1
  111. package/dist-lib/config.cjs +1 -1
  112. package/dist-lib/detox/index.cjs +321 -79
  113. package/dist-lib/dev-bundle-resolution.cjs +2 -2
  114. package/dist-lib/home-paths.cjs +1 -1
  115. package/dist-lib/host/bridge-host.cjs +18 -13
  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 +7 -7
  128. package/dist-lib/sdk.cjs +12 -4
  129. package/dist-lib/sdk.mjs +12 -4
  130. package/dist-lib/skills.cjs +425 -169
  131. package/dist-lib/vite.cjs +1 -1
  132. package/package.json +4 -4
  133. package/scripts/dev-server-scanner.ts +5 -5
  134. package/scripts/postinstall.cjs +1 -1
  135. package/scripts/preuninstall.cjs +1 -1
  136. package/skills/{soot → contrast}/SKILL.md +4 -4
  137. package/skills/sootsim-perf/SKILL.md +3 -1
  138. package/src/auth/shared-session.ts +1 -1
  139. package/src/backend-origin.ts +11 -11
  140. package/src/bridge-constants.ts +3 -3
  141. package/src/home-paths.ts +1 -1
  142. package/src/host/agent-host.ts +2 -2
  143. package/src/host/bridge-host.ts +21 -21
  144. package/src/native-dev-bundle-url.ts +3 -3
  145. package/src/runtime-assets.ts +3 -3
  146. package/src/runtime-delivery.ts +10 -4
  147. package/src/screenshots/compose.ts +4 -1
  148. package/src/screenshots/frame-compose.ts +4 -1
  149. package/src/screenshots/orchestrate.ts +7 -2
  150. package/src/vite-plugin-one.ts +3 -3
  151. package/src/vite-plugin.ts +3 -3
  152. package/dist-cli/chunks/beta-DJSGXOPH.js +0 -2
  153. package/dist-cli/chunks/chunk-6H7YREYW.js +0 -24
  154. package/dist-cli/chunks/chunk-7GPF3DB3.js +0 -119
  155. package/dist-cli/chunks/chunk-ANOVVNGB.js +0 -2
  156. package/dist-cli/chunks/chunk-GEGC3K27.js +0 -3
  157. package/dist-cli/chunks/chunk-JPTMSWYU.js +0 -2
  158. package/dist-cli/chunks/chunk-LCFG2W3W.js +0 -2
  159. package/dist-cli/chunks/chunk-LM5LTPJA.js +0 -1
  160. package/dist-cli/chunks/chunk-LZTCDNMO.js +0 -4
  161. package/dist-cli/chunks/chunk-MUDGBEYR.js +0 -1
  162. package/dist-cli/chunks/chunk-NSPJ2YPI.js +0 -8
  163. package/dist-cli/chunks/chunk-UURHCZIJ.js +0 -2
  164. package/dist-cli/chunks/chunk-V4RE6EHK.js +0 -2
  165. package/dist-cli/chunks/cli-version-CG2D7KNU.js +0 -2
  166. package/dist-cli/chunks/daemon-TFLGTVRW.js +0 -2
  167. package/dist-cli/chunks/drivers-PHOFHA47.js +0 -2
  168. package/dist-cli/chunks/flow-5NOFCLFK.js +0 -2
  169. package/dist-cli/chunks/help-FQI3NVUB.js +0 -2
  170. package/dist-cli/chunks/inspect-DPTOAY56.js +0 -2
  171. package/dist-cli/chunks/install-desktop-QSQMEYRQ.js +0 -2
  172. package/dist-cli/chunks/optional-demo-registry-BX2PROVX.js +0 -2
  173. package/dist-cli/chunks/runtime-KG3OD3NE.js +0 -2
  174. package/dist-cli/chunks/screenshot-command-QMPXRAJU.js +0 -151
  175. package/dist-cli/chunks/store-I2ZLFU5Q.js +0 -2
  176. package/dist-cli/chunks/telemetry-4U5BKD6A.js +0 -2
  177. package/dist-cli/chunks/upload-5TWLNJAP.js +0 -2
package/README.md CHANGED
@@ -10,7 +10,7 @@ React Native simulator.
10
10
  contains **no rendering code**. The canvas engine (CanvasKit renderer, Yoga
11
11
  layout, iOS/Android shell chrome, Electron shell) lives in the private
12
12
  `sootsim-engine` workspace package and is shipped at runtime as a versioned
13
- tarball, fetched from the sootbean.com CDN and unpacked into `~/.sootsim`.
13
+ tarball, fetched from the contrast.dev CDN and unpacked into `~/.sootsim`.
14
14
 
15
15
  What this package gives you:
16
16
 
@@ -23,12 +23,12 @@ What this package gives you:
23
23
  editor
24
24
  - `sootsim/detox` — a drop-in Detox driver + jest preset
25
25
  - `sootsim/sdk` — the inspect/interact verbs as a programmatic API
26
- - `sootsim/skills` — a registry of SootBean-flavored automation skills
26
+ - `sootsim/skills` — a registry of Contrast-flavored automation skills
27
27
 
28
28
  Platform status: iOS is the parity baseline. Android is bootstrapped and usable
29
29
  for focused conformance slices (bundle/runtime identity, device profiles, shell
30
30
  chrome, gesture + three-button nav, system UI/window metrics with cutout-safe
31
- insets) but is not full parity until the Android launch gates in the SootBean
31
+ insets) but is not full parity until the Android launch gates in the Contrast
32
32
  repo plan pass.
33
33
 
34
34
  ## What's in this package (and what's not)
@@ -81,7 +81,7 @@ graph TD
81
81
  VITEONE["sootsim/vite = vite-plugin-one.ts (serve runtime at /__soot/)"]
82
82
  VITERN["sootsim() = vite-plugin.ts (RN resolver + native stubs)"]
83
83
  METRO["sootsim/metro = metro-plugin.ts"]
84
- COMPAT["@soot/compat stub-manifest (native seams)"]
84
+ COMPAT["@sootsim/compat stub-manifest (native seams)"]
85
85
  ENGINESHIM["sootsim-engine react-native shim"]
86
86
  end
87
87
  SKILLS["sootsim/skills registry.ts (builtin skills)"]
@@ -90,7 +90,7 @@ graph TD
90
90
  LOCK["daemon.json lockfile"]
91
91
  CFG["config.json"]
92
92
  end
93
- CDN["sootbean.com CDN (runtimes/manifest.json + tarballs)"]
93
+ CDN["contrast.dev CDN (runtimes/manifest.json + tarballs)"]
94
94
  BIN --> SETUP
95
95
  BIN --> RUNTIMECMD
96
96
  BIN --> INSPECT
@@ -171,7 +171,7 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
171
171
  can also launch the Electron app).
172
172
  - The internal **`sootsim()`** plugin (`src/vite-plugin.ts`) is a separate, much
173
173
  larger RN-resolution / native-stub plugin: it aliases `react-native` to the
174
- engine shim and native packages to `@soot/compat` stubs, applies worklets
174
+ engine shim and native packages to `@sootsim/compat` stubs, applies worklets
175
175
  transforms, and is the one that instantiates `SootSimBridgeHost` inside its
176
176
  own dev server. It is used to build the engine/shell and to load external RN
177
177
  apps — it is **not** the published `sootsim/vite` export.
@@ -188,10 +188,10 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
188
188
  | SootSimBridgeHost (serve/daemon) | The bridge process (`sootsim serve`, or autostarted as the daemon via launchd/systemd). HTTP+WS hub on `:7668`: relays commands between CLI clients and registered sims; also serves runtime HTTP, `/__server-scan`, and fetch/WS proxies. | `src/host/bridge-host.ts`, `cli/commands/serve.ts`, `cli/commands/daemon.ts`, `src/home-paths.ts` |
189
189
  | AgentHost + sessions | Agent-routing extension of the host: owns the single FIFO reader per agent session and fans `agent:event`/`session-status` pushes to every WS subscriber. Backed by agent-sessions + attached-projects stores. | `src/host/agent-host.ts`, `src/agent-sessions.ts`, `src/attached-projects.ts`, `src/agent-daemon-client.ts` |
190
190
  | host proxies + dev-server scan | Host-side helpers so a cross-origin tenant worker can reach local dev servers and same-origin APIs: `/__server-scan` discovers running metro/expo/vxrn/one servers; fetch + websocket proxies relay guest-app network through the daemon. | `src/host/fetch-proxy-handler.ts`, `src/host/websocket-proxy.ts`, `scripts/dev-server-scanner.ts`, `src/dev-bundle-resolution.ts` |
191
- | runtime delivery + sootsim home | Versioned engine runtime management. Resolves the CDN origin (`https://sootbean.com`), fetches `runtimes/manifest.json` + the runtime tarball, verifies sha256, unpacks to `~/.sootsim/runtimes/<version>`, and tracks active version/config/daemon lockfile under the sootsim home. | `src/runtime-delivery.ts`, `src/home-paths.ts`, `src/runtime-assets.ts`, `scripts/postinstall.cjs` |
191
+ | runtime delivery + sootsim home | Versioned engine runtime management. Resolves the CDN origin (`https://contrast.dev`), fetches `runtimes/manifest.json` + the runtime tarball, verifies sha256, unpacks to `~/.sootsim/runtimes/<version>`, and tracks active version/config/daemon lockfile under the sootsim home. | `src/runtime-delivery.ts`, `src/home-paths.ts`, `src/runtime-assets.ts`, `scripts/postinstall.cjs` |
192
192
  | `sootsim/vite` + `sootsim/metro` (runtime serving) | Published bundler plugins. Both serve the installed engine runtime at `/__soot/` from `~/.sootsim/runtimes/<version>` so an app's own dev server can host the sim shell; the vite plugin can also launch the Electron app. | `src/vite-plugin-one.ts`, `src/metro-plugin.ts`, `src/runtime-assets.ts` |
193
- | `sootsim()` vite resolver plugin (internal) | The large RN-resolution / native-stub vite plugin: aliases `react-native` to the engine shim, native packages to `@soot/compat` stubs, applies worklets/babel transforms, and instantiates `SootSimBridgeHost` in its dev server. Distinct from the published `sootsim/vite` export. | `src/vite-plugin.ts`, `src/worklets-babel.ts`, `packages/compat/src/stub-manifest.ts` |
194
- | skills registry | SootSim agent skills (`sootsim-setup`, `sootsim-debug`, `sootsim-test`, `sootsim-visual`, and the generated `soot` index) installable with `sootsim skill`. | `skills/*.md`, `src/skills/registry.ts`, `src/skills/types.ts` |
193
+ | `sootsim()` vite resolver plugin (internal) | The large RN-resolution / native-stub vite plugin: aliases `react-native` to the engine shim, native packages to `@sootsim/compat` stubs, applies worklets/babel transforms, and instantiates `SootSimBridgeHost` in its dev server. Distinct from the published `sootsim/vite` export. | `src/vite-plugin.ts`, `src/worklets-babel.ts`, `packages/compat/src/stub-manifest.ts` |
194
+ | skills registry | SootSim agent skills (`sootsim-setup`, `sootsim-debug`, `sootsim-test`, `sootsim-visual`, and the generated `contrast` index) installable with `sootsim skill`. | `skills/*.md`, `src/skills/registry.ts`, `src/skills/types.ts` |
195
195
 
196
196
  ## Getting started
197
197
 
@@ -267,7 +267,7 @@ sootsim detox # run Detox/Jest suites against sootsim
267
267
  ```
268
268
 
269
269
  See the full command reference via `sootsim --help` or at
270
- `src/features/site/docs/sootsim/cli/` in the SootBean repo.
270
+ `src/features/site/docs/sootsim/cli/` in the Contrast repo.
271
271
 
272
272
  ## Bundler plugins (`sootsim/vite` and `sootsim/metro`)
273
273
 
@@ -405,10 +405,10 @@ frame for quick art-direction passes.
405
405
  `sootsim skill install` installs SootSim agent skills in the standard
406
406
  `<skill-name>/SKILL.md` layout used by Codex and Claude Code. The bundled
407
407
  skills cover setup, debugging/perf/accessibility, testing, visual review, and
408
- the generated `soot` command index. The published CLI registry and generated
408
+ the generated `contrast` command index. The published CLI registry and generated
409
409
  website docs both come from `packages/sootsim-skills/`:
410
410
 
411
- - `packages/sootsim/skills/soot/SKILL.md` is generated from that registry
411
+ - `packages/sootsim/skills/contrast/SKILL.md` is generated from that registry
412
412
  - `src/features/site/docs/sootsim/cli/*` is generated output, not hand-edited
413
413
 
414
414
  When command docs or examples drift, update `packages/sootsim-skills/` and run:
@@ -420,7 +420,7 @@ bun run generate:sootsim-docs
420
420
  ## Runtime delivery (the sootsim home and CDN)
421
421
 
422
422
  The engine never ships inside this package. `runtime-delivery.ts` resolves the
423
- CDN origin (default `https://sootbean.com`, overridable in `config.json`),
423
+ CDN origin (default `https://contrast.dev`, overridable in `config.json`),
424
424
  fetches `runtimes/manifest.json` + the runtime tarball, verifies its sha256,
425
425
  and unpacks it under `~/.sootsim`:
426
426
 
@@ -438,7 +438,7 @@ CLI side.
438
438
 
439
439
  ## Development and building
440
440
 
441
- This package is part of the SootBean monorepo. To build the published CLI bundle:
441
+ This package is part of the Contrast monorepo. To build the published CLI bundle:
442
442
 
443
443
  ```sh
444
444
  bun run build:cli
@@ -1,7 +1,7 @@
1
1
  import type { Matcher } from './matchers'
2
2
 
3
3
  // element interaction object returned by element(matcher)
4
- export interface SootElement {
4
+ export interface SootSimElement {
5
5
  // reference to the matcher for lazy evaluation
6
6
  _matcher: Matcher
7
7
 
@@ -12,7 +12,7 @@ export interface SootElement {
12
12
  duration: number,
13
13
  normalizedStartX: number,
14
14
  normalizedStartY: number,
15
- targetElement: SootElement,
15
+ targetElement: SootSimElement,
16
16
  normalizedEndX: number,
17
17
  normalizedEndY: number,
18
18
  speed?: string,
@@ -32,5 +32,5 @@ export interface SootElement {
32
32
  rotate(radians: number, speed?: 'fast' | 'slow'): Promise<void>
33
33
  takeScreenshot(name: string): Promise<string>
34
34
  getAttributes(): Promise<any>
35
- atIndex(index: number): SootElement
35
+ atIndex(index: number): SootSimElement
36
36
  }
@@ -3,7 +3,7 @@
3
3
  // waitFor(element).toExist().withTimeout(ms)
4
4
 
5
5
  import { dragScrollNode, readSootsimInteractiveViewport } from './gestures'
6
- import type { SootElement } from './element-types'
6
+ import type { SootSimElement } from './element-types'
7
7
  import type { Matcher } from './matchers'
8
8
  import type { Page } from 'playwright'
9
9
 
@@ -80,12 +80,12 @@ function describeNodeVisibility(node: any, viewport?: Viewport): string {
80
80
 
81
81
  // expect(element) returns an object with assertion methods
82
82
  export function createExpect(findNode: FindFn, getPage: GetPageFn) {
83
- return function sootExpect(el: SootElement): SootExpectation {
84
- return new SootExpectation(el._matcher, findNode, getPage, false)
83
+ return function sootsimExpect(el: SootSimElement): SootSimExpectation {
84
+ return new SootSimExpectation(el._matcher, findNode, getPage, false)
85
85
  }
86
86
  }
87
87
 
88
- class SootExpectation {
88
+ class SootSimExpectation {
89
89
  private matcher: Matcher
90
90
  private findNode: FindFn
91
91
  private getPage: GetPageFn
@@ -98,8 +98,13 @@ class SootExpectation {
98
98
  this.negated = negated
99
99
  }
100
100
 
101
- get not(): SootExpectation {
102
- return new SootExpectation(this.matcher, this.findNode, this.getPage, !this.negated)
101
+ get not(): SootSimExpectation {
102
+ return new SootSimExpectation(
103
+ this.matcher,
104
+ this.findNode,
105
+ this.getPage,
106
+ !this.negated,
107
+ )
103
108
  }
104
109
 
105
110
  async toExist(): Promise<void> {
@@ -286,12 +291,12 @@ class SootExpectation {
286
291
 
287
292
  // waitFor(element) returns a chainable object that polls until the assertion passes
288
293
  export function createWaitFor(findNode: FindFn, getPage: GetPageFn) {
289
- return function sootWaitFor(el: SootElement): SootWaitForChain {
290
- return new SootWaitForChain(el._matcher, findNode, getPage)
294
+ return function sootsimWaitFor(el: SootSimElement): SootSimWaitForChain {
295
+ return new SootSimWaitForChain(el._matcher, findNode, getPage)
291
296
  }
292
297
  }
293
298
 
294
- class SootWaitForChain {
299
+ class SootSimWaitForChain {
295
300
  private matcher: Matcher
296
301
  private findNode: FindFn
297
302
  private getPage: GetPageFn
@@ -304,12 +309,12 @@ class SootWaitForChain {
304
309
  this.getPage = getPage
305
310
  }
306
311
 
307
- get not(): SootWaitForChain {
312
+ get not(): SootSimWaitForChain {
308
313
  this._negated = true
309
314
  return this
310
315
  }
311
316
 
312
- toExist(): SootWaitForAction {
317
+ toExist(): SootSimWaitForAction {
313
318
  this.assertionFn = async () => {
314
319
  const node = await this.findNode(this.matcher)
315
320
  const exists = node !== null && node !== undefined
@@ -317,10 +322,10 @@ class SootWaitForChain {
317
322
  throw new Error(`waitFor toExist failed: ${JSON.stringify(this.matcher)}`)
318
323
  }
319
324
  }
320
- return new SootWaitForAction(this.assertionFn, this.findNode, this.getPage)
325
+ return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage)
321
326
  }
322
327
 
323
- toBeVisible(): SootWaitForAction {
328
+ toBeVisible(): SootSimWaitForAction {
324
329
  this.assertionFn = async () => {
325
330
  const node = await this.findNode(this.matcher)
326
331
  const page = this.getPage()
@@ -332,10 +337,10 @@ class SootWaitForChain {
332
337
  )
333
338
  }
334
339
  }
335
- return new SootWaitForAction(this.assertionFn, this.findNode, this.getPage)
340
+ return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage)
336
341
  }
337
342
 
338
- toBeNotVisible(): SootWaitForAction {
343
+ toBeNotVisible(): SootSimWaitForAction {
339
344
  this.assertionFn = async () => {
340
345
  const node = await this.findNode(this.matcher)
341
346
  const visible = await isNodeVisible(this.getPage(), node)
@@ -343,10 +348,10 @@ class SootWaitForChain {
343
348
  throw new Error(`waitFor toBeNotVisible failed: ${JSON.stringify(this.matcher)}`)
344
349
  }
345
350
  }
346
- return new SootWaitForAction(this.assertionFn, this.findNode, this.getPage)
351
+ return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage)
347
352
  }
348
353
 
349
- toHaveText(expectedText: string): SootWaitForAction {
354
+ toHaveText(expectedText: string): SootSimWaitForAction {
350
355
  this.assertionFn = async () => {
351
356
  const node = await this.findNode(this.matcher)
352
357
  if (!node)
@@ -361,10 +366,10 @@ class SootWaitForChain {
361
366
  )
362
367
  }
363
368
  }
364
- return new SootWaitForAction(this.assertionFn, this.findNode, this.getPage)
369
+ return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage)
365
370
  }
366
371
 
367
- toHaveValue(value: string): SootWaitForAction {
372
+ toHaveValue(value: string): SootSimWaitForAction {
368
373
  this.assertionFn = async () => {
369
374
  const node = await this.findNode(this.matcher)
370
375
  if (!node) throw new Error(`waitFor toHaveValue: element not found`)
@@ -373,11 +378,11 @@ class SootWaitForChain {
373
378
  throw new Error(`waitFor toHaveValue: expected "${value}" in "${actual}"`)
374
379
  }
375
380
  }
376
- return new SootWaitForAction(this.assertionFn, this.findNode, this.getPage)
381
+ return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage)
377
382
  }
378
383
  }
379
384
 
380
- class SootWaitForAction {
385
+ class SootSimWaitForAction {
381
386
  private assertionFn: () => Promise<void>
382
387
  private findNode: FindFn
383
388
  private getPage: GetPageFn
@@ -417,8 +422,8 @@ class SootWaitForAction {
417
422
  }
418
423
  }
419
424
 
420
- whileElement(matcher: Matcher): SootWaitForScrollAction {
421
- return new SootWaitForScrollAction(
425
+ whileElement(matcher: Matcher): SootSimWaitForScrollAction {
426
+ return new SootSimWaitForScrollAction(
422
427
  this.assertionFn,
423
428
  this.findNode,
424
429
  this.getPage,
@@ -427,7 +432,7 @@ class SootWaitForAction {
427
432
  }
428
433
  }
429
434
 
430
- class SootWaitForScrollAction {
435
+ class SootSimWaitForScrollAction {
431
436
  private assertionFn: () => Promise<void>
432
437
  private findNode: FindFn
433
438
  private getPage: GetPageFn
package/detox/gestures.ts CHANGED
@@ -1,8 +1,8 @@
1
- // pull in @soot/sootsim-globals so window.__sootsimTest and
1
+ // pull in @sootsim/globals so window.__sootsimTest and
2
2
  // window.__sootsimFindInteractiveCanvas are typed inside page.evaluate
3
3
  // bodies. all sootsim-namespaced globals are declared canonically there —
4
4
  // add new entries to packages/sootsim-globals/src/index.ts, not here.
5
- import type {} from '@soot/sootsim-globals'
5
+ import type {} from '@sootsim/globals'
6
6
  import type { Page } from 'playwright'
7
7
 
8
8
  // shape of the surface-metrics bridge response we care about. the bridge
@@ -262,15 +262,15 @@ export async function readSootsimInteractiveViewport(
262
262
 
263
263
  export async function sootsimToPage(
264
264
  page: Page,
265
- sootX: number,
266
- sootY: number,
265
+ sootsimX: number,
266
+ sootsimY: number,
267
267
  ): Promise<{ x: number; y: number }> {
268
268
  const metrics = await readSootsimPageMetrics(page)
269
269
  const scaleX = metrics.canvas.width / metrics.window.width
270
270
  const scaleY = metrics.canvas.height / metrics.window.height
271
271
  return {
272
- x: metrics.canvas.left + sootX * scaleX,
273
- y: metrics.canvas.top + sootY * scaleY,
272
+ x: metrics.canvas.left + sootsimX * scaleX,
273
+ y: metrics.canvas.top + sootsimY * scaleY,
274
274
  }
275
275
  }
276
276
 
@@ -283,28 +283,28 @@ export async function dragScrollNode(
283
283
  pixels: number,
284
284
  direction: 'up' | 'down' | 'left' | 'right',
285
285
  ): Promise<void> {
286
- const startSootX = node.absolutePosition.x + node.layout.width / 2
287
- const startSootY = node.absolutePosition.y + node.layout.height / 2
288
- let endSootX = startSootX
289
- let endSootY = startSootY
286
+ const startSootSimX = node.absolutePosition.x + node.layout.width / 2
287
+ const startSootSimY = node.absolutePosition.y + node.layout.height / 2
288
+ let endSootSimX = startSootSimX
289
+ let endSootSimY = startSootSimY
290
290
 
291
291
  switch (direction) {
292
292
  case 'down':
293
- endSootY -= pixels
293
+ endSootSimY -= pixels
294
294
  break
295
295
  case 'up':
296
- endSootY += pixels
296
+ endSootSimY += pixels
297
297
  break
298
298
  case 'left':
299
- endSootX += pixels
299
+ endSootSimX += pixels
300
300
  break
301
301
  case 'right':
302
- endSootX -= pixels
302
+ endSootSimX -= pixels
303
303
  break
304
304
  }
305
305
 
306
- const startPage = await sootsimToPage(page, startSootX, startSootY)
307
- const endPage = await sootsimToPage(page, endSootX, endSootY)
306
+ const startPage = await sootsimToPage(page, startSootSimX, startSootSimY)
307
+ const endPage = await sootsimToPage(page, endSootSimX, endSootSimY)
308
308
  const steps = Math.max(16, Math.ceil(pixels / 8))
309
309
 
310
310
  await page.mouse.move(startPage.x, startPage.y)
package/detox/index.ts CHANGED
@@ -15,12 +15,12 @@ import {
15
15
  waitForSootsimGestureHandler,
16
16
  } from './gestures'
17
17
  import { by, type Matcher } from './matchers'
18
- import type { SootElement } from './element-types'
18
+ import type { SootSimElement } from './element-types'
19
19
  import type { Browser, BrowserContext, Page } from 'playwright'
20
20
  import type { SootsimEventMap, SootsimEventName } from 'sootsim-engine/sootsim-event'
21
21
 
22
22
  export { by }
23
- export type { SootElement } from './element-types'
23
+ export type { SootSimElement } from './element-types'
24
24
 
25
25
  const BASE_URL = process.env.SOOTSIM_URL || 'http://localhost:5173'
26
26
  const SCREENSHOT_DIR =
@@ -517,7 +517,7 @@ async function isFocusedTextInputNode(page: Page, nodeInfo: any): Promise<boolea
517
517
  return !!focused && typeof focused === 'object' && focused.nodeId === nodeInfo?.nodeId
518
518
  }
519
519
 
520
- async function focusElementForTextEntry(el: SootElement): Promise<void> {
520
+ async function focusElementForTextEntry(el: SootSimElement): Promise<void> {
521
521
  const page = getPage()
522
522
  const node = await findNodeByMatcher(el._matcher)
523
523
  if (!node) {
@@ -669,8 +669,8 @@ async function dispatchSootsimEventInPage<K extends SootsimEventName>(
669
669
  )
670
670
  }
671
671
 
672
- function createSootElement(matcher: Matcher): SootElement {
673
- const el: SootElement = {
672
+ function createSootSimElement(matcher: Matcher): SootSimElement {
673
+ const el: SootSimElement = {
674
674
  _matcher: matcher,
675
675
 
676
676
  async tap(point) {
@@ -679,11 +679,11 @@ function createSootElement(matcher: Matcher): SootElement {
679
679
  const node = await findNodeByMatcher(matcher)
680
680
  if (!node) throw new Error(`element not found for tap: ${JSON.stringify(matcher)}`)
681
681
  const defaultPoint = point ? null : await getDefaultTapPoint(page, node)
682
- const targetSootX =
682
+ const targetSootSimX =
683
683
  typeof point?.x === 'number'
684
684
  ? node.absolutePosition.x + point.x
685
685
  : (defaultPoint?.x ?? node.absolutePosition.x + node.layout.width / 2)
686
- const targetSootY =
686
+ const targetSootSimY =
687
687
  typeof point?.y === 'number'
688
688
  ? node.absolutePosition.y + point.y
689
689
  : (defaultPoint?.y ?? node.absolutePosition.y + node.layout.height / 2)
@@ -700,7 +700,7 @@ function createSootElement(matcher: Matcher): SootElement {
700
700
  }
701
701
  await tap(x, y)
702
702
  },
703
- { x: targetSootX, y: targetSootY },
703
+ { x: targetSootSimX, y: targetSootSimY },
704
704
  )
705
705
  // small settling time for react state updates
706
706
  await page.waitForTimeout(50)
@@ -760,7 +760,7 @@ function createSootElement(matcher: Matcher): SootElement {
760
760
  duration: number,
761
761
  normalizedStartX: number,
762
762
  normalizedStartY: number,
763
- targetElement: SootElement,
763
+ targetElement: SootSimElement,
764
764
  normalizedEndX: number,
765
765
  normalizedEndY: number,
766
766
  speed = 'fast',
@@ -780,15 +780,17 @@ function createSootElement(matcher: Matcher): SootElement {
780
780
  )
781
781
 
782
782
  // compute start and end in sootsim coords
783
- const startSootX =
783
+ const startSootSimX =
784
784
  srcNode.absolutePosition.x + srcNode.layout.width * normalizedStartX
785
- const startSootY =
785
+ const startSootSimY =
786
786
  srcNode.absolutePosition.y + srcNode.layout.height * normalizedStartY
787
- const endSootX = tgtNode.absolutePosition.x + tgtNode.layout.width * normalizedEndX
788
- const endSootY = tgtNode.absolutePosition.y + tgtNode.layout.height * normalizedEndY
787
+ const endSootSimX =
788
+ tgtNode.absolutePosition.x + tgtNode.layout.width * normalizedEndX
789
+ const endSootSimY =
790
+ tgtNode.absolutePosition.y + tgtNode.layout.height * normalizedEndY
789
791
 
790
- const startPage = await sootsimToPage(page, startSootX, startSootY)
791
- const endPage = await sootsimToPage(page, endSootX, endSootY)
792
+ const startPage = await sootsimToPage(page, startSootSimX, startSootSimY)
793
+ const endPage = await sootsimToPage(page, endSootSimX, endSootSimY)
792
794
  const steps = speed === 'slow' ? 20 : 10
793
795
 
794
796
  // press at start position
@@ -927,12 +929,12 @@ function createSootElement(matcher: Matcher): SootElement {
927
929
  const { absolutePosition, layout } = node
928
930
 
929
931
  // start from center
930
- const startSootX = absolutePosition.x + layout.width * 0.5
931
- const startSootY = absolutePosition.y + layout.height * 0.5
932
+ const startSootSimX = absolutePosition.x + layout.width * 0.5
933
+ const startSootSimY = absolutePosition.y + layout.height * 0.5
932
934
 
933
935
  // compute distance
934
- let endSootX = startSootX
935
- let endSootY = startSootY
936
+ let endSootSimX = startSootSimX
937
+ let endSootSimY = startSootSimY
936
938
  const dist =
937
939
  direction === 'up' || direction === 'down'
938
940
  ? layout.height * percentage
@@ -940,16 +942,16 @@ function createSootElement(matcher: Matcher): SootElement {
940
942
 
941
943
  switch (direction) {
942
944
  case 'up':
943
- endSootY -= dist
945
+ endSootSimY -= dist
944
946
  break
945
947
  case 'down':
946
- endSootY += dist
948
+ endSootSimY += dist
947
949
  break
948
950
  case 'left':
949
- endSootX -= dist
951
+ endSootSimX -= dist
950
952
  break
951
953
  case 'right':
952
- endSootX += dist
954
+ endSootSimX += dist
953
955
  break
954
956
  }
955
957
 
@@ -965,10 +967,10 @@ function createSootElement(matcher: Matcher): SootElement {
965
967
  await drag(fromX, fromY, toX, toY, steps, stepMs)
966
968
  },
967
969
  {
968
- fromX: startSootX,
969
- fromY: startSootY,
970
- toX: endSootX,
971
- toY: endSootY,
970
+ fromX: startSootSimX,
971
+ fromY: startSootSimY,
972
+ toX: endSootSimX,
973
+ toY: endSootSimY,
972
974
  steps,
973
975
  stepMs: stepDelay,
974
976
  },
@@ -1084,8 +1086,8 @@ function createSootElement(matcher: Matcher): SootElement {
1084
1086
  }
1085
1087
 
1086
1088
  // element() factory -- takes a matcher and returns an element interaction object
1087
- export function element(matcher: Matcher): SootElement {
1088
- return createSootElement(matcher)
1089
+ export function element(matcher: Matcher): SootSimElement {
1090
+ return createSootSimElement(matcher)
1089
1091
  }
1090
1092
 
1091
1093
  // device object -- app lifecycle
@@ -1616,10 +1618,10 @@ export const device = {
1616
1618
  }
1617
1619
 
1618
1620
  // exports matching detox API
1619
- const sootExpect = createExpect(findNodeByMatcher, getPage)
1620
- const sootWaitFor = createWaitFor(findNodeByMatcher, getPage)
1621
+ const sootsimExpect = createExpect(findNodeByMatcher, getPage)
1622
+ const sootsimWaitFor = createWaitFor(findNodeByMatcher, getPage)
1621
1623
 
1622
- export { sootExpect as expect, sootWaitFor as waitFor }
1624
+ export { sootsimExpect as expect, sootsimWaitFor as waitFor }
1623
1625
 
1624
1626
  // cleanup -- call this in afterAll
1625
1627
  export async function cleanup() {
package/dist-cli/bin.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- /*! sootsim v0.1.226 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
3
- import{a as w,b as u}from"./chunks/chunk-2KXBJELA.js";import"./chunks/chunk-HJY4JYOF.js";function f(t){let a=t instanceof Error?t.message:String(t);(/^command timed out after \d+s$/.test(a)||a.startsWith("sim disconnected:")||a.startsWith("bridge never reconnected")||a.startsWith("no sim connected with id ")||a.startsWith("could not connect to ws://")||a.startsWith("sootsim bridge daemon is not running")||a.startsWith("sootsim bridge lockfile is fresh")||a.startsWith("sootsim bridge exited before becoming ready")||a.startsWith("background daemon setup is unavailable")||a.startsWith("sootsim bridge did not start within"))&&!process.env.SOOTSIM_VERBOSE&&(process.stderr.write(` ${a}
2
+ /*! sootsim v0.1.228 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
3
+ import{a as w,b as u}from"./chunks/chunk-KIL5F7KD.js";import"./chunks/chunk-6U3GMNTP.js";function f(t){let a=t instanceof Error?t.message:String(t);(/^command timed out after \d+s$/.test(a)||a.startsWith("sim disconnected:")||a.startsWith("bridge never reconnected")||a.startsWith("no sim connected with id ")||a.startsWith("could not connect to ws://")||a.startsWith("sootsim bridge daemon is not running")||a.startsWith("sootsim bridge lockfile is fresh")||a.startsWith("sootsim bridge exited before becoming ready")||a.startsWith("background daemon setup is unavailable")||a.startsWith("sootsim bridge did not start within"))&&!process.env.SOOTSIM_VERBOSE&&(process.stderr.write(` ${a}
4
4
  `),process.exit(1)),t instanceof Error&&t.stack?process.stderr.write(`${t.stack}
5
5
  `):process.stderr.write(`${a}
6
6
  `),process.exit(1)}process.on("unhandledRejection",f);process.on("uncaughtException",f);var h=typeof __SOOTSIM_STANDALONE__<"u"&&__SOOTSIM_STANDALONE__,A=new Set(["desktop"]);function k(t){!h||!A.has(t)||(process.stderr.write(` sootsim ${t} isn't available in the standalone binary \u2014
7
7
  it needs vite / electron / playwright from a project's node_modules.
8
8
  run the npm package instead:
9
9
  sootsim ${t}
10
- `),process.exit(1))}var e=u(process.argv);{let{trackCliEvent:t}=await import("./chunks/telemetry-4U5BKD6A.js");t({event:"cli_command_invoked",properties:{command:e.command||(e.version?"version":e.help?"help":"none"),arg_count:e.commandArgs.length,platform:process.platform,arch:process.arch,node_version:process.versions.node}})}if(e.command!=="serve"&&e.command!=="daemon"){let{consumeRuntimeUpgradeNotice:t}=await import("./chunks/home-paths-N56HBC4S.js"),a=t();a&&process.stderr.write(` SootSim engine upgraded to v${a.to}`+(a.from?` (from v${a.from})`:"")+` \xB7 what's new: https://sootsim.com/changelog
11
- `)}async function c(t){let{flushCliTelemetry:a}=await import("./chunks/telemetry-4U5BKD6A.js");await a(),process.exit(t)}if(e.version){let{getCliVersion:t}=await import("./chunks/cli-version-CG2D7KNU.js"),{IS_BETA:a,BETA_LABEL:i}=await import("./chunks/beta-DJSGXOPH.js"),p=a?` \xB7 ${i}`:"";console.log(`sootsim v${t()}${p}`);let{readActiveRuntime:b}=await import("./chunks/home-paths-N56HBC4S.js"),l=b();console.log(l?`runtime v${l}`:"runtime not installed"),await c(0)}if(e.help&&!e.command){let{printHelp:t}=await import("./chunks/help-FQI3NVUB.js");t(),await c(0)}var o=e.globalFlags.port,r=e.verbose,m=e.globalFlags.device,d=e.globalFlags.theme,v=e.globalFlags.driver,_=e.globalFlags.headless===!0,g=e.globalFlags.sim??e.globalFlags.session??e.globalFlags.tab,s=g?["--sim",g,...e.commandArgs]:e.commandArgs;if(m||d){let{settingsStore:t}=await import("./chunks/store-I2ZLFU5Q.js"),a={};m&&(a.deviceModel=m),d&&(a.colorScheme=d),t.apply(a)}if(!e.command&&e.commandArgs.length===0){let{runSetup:t,shouldRunSetupForBareCommand:a}=await import("./chunks/setup-IFZLKN5S.js");a()&&(await t(["--from-welcome"]),await c(0));let{printHelp:i}=await import("./chunks/help-FQI3NVUB.js");i(),await c(0)}var n=e.command??"";if(!n){if(e.commandArgs.length>0){let a=e.commandArgs.find(i=>!i.startsWith("-"))??e.commandArgs[0];console.error(` unknown command: ${a}`),console.error(" run `sootsim --help` to see the full surface."),await c(1)}let{printHelp:t}=await import("./chunks/help-FQI3NVUB.js");t(),await c(0)}if(e.help||e.commandArgs.includes("--help")||e.commandArgs.includes("-h")){if(n==="skill"){let{runSkill:i}=await import("./chunks/skills-MPRCDS35.js");await i(e.commandArgs),await c(0)}let t=n==="do"||n==="get"||n==="debug"||n==="shell"||n==="wait",a=e.commandArgs.find(i=>!i.startsWith("-"));if(n!=="shell")if(t&&!a){let{printGroupHelp:i}=await import("./chunks/help-FQI3NVUB.js");i(n)&&await c(0)}else{let{printCommandHelp:i}=await import("./chunks/help-FQI3NVUB.js");i(t&&a?a:n,{prefer:t&&a?"verb":"command",group:t&&a?n:void 0}),await c(0)}}k(n);if(w.has(n)){let{runInspect:t}=await import("./chunks/inspect-DPTOAY56.js");await t([n,...s],{port:o,verbose:r})}else switch(n){case"assert":{let{runAssert:t}=await import("./chunks/assert-W3NC7YNE.js");await t(e.commandArgs);break}case"detox":{let{runDetox:t}=await import("./chunks/detox-K5CPLEZN.js");await t(e.commandArgs,{port:o,verbose:r});break}case"maestro":{let{runMaestro:t}=await import("./chunks/maestro-6YM5KA2P.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"record":{let{runRecord:t}=await import("./chunks/record-BE7PQUPE.js");await t(s,{port:o,verbose:r});break}case"film":{let{runFilm:t}=await import("./chunks/film-SHWP5F7U.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"storage":{let{runStorage:t}=await import("./chunks/storage-YZDVWKTL.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"perf":{let{runPerf:t}=await import("./chunks/perf-ACC64R7U.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"screenshot":{let{runScreenshotCommand:t}=await import("./chunks/screenshot-command-QMPXRAJU.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"mode":{let{runMode:t}=await import("./chunks/mode-D5XU5PGG.js");await t(s,{port:o,verbose:r});break}case"inspect":{let{runInspect:t}=await import("./chunks/inspect-DPTOAY56.js");await t(s,{port:o,verbose:r});break}case"debug":{let{runDebug:t}=await import("./chunks/debug-EE6BQVA4.js");await t(s,{port:o,verbose:r});break}case"timeline":{let{runTimeline:t}=await import("./chunks/timeline-QA6L2PCD.js");await t(s,{port:o,verbose:r});break}case"what-happened":{let{runWhatHappened:t}=await import("./chunks/what-happened-CFULL2ZS.js");await t(s,{port:o,verbose:r});break}case"open":{let{runOpenCommand:t}=await import("./chunks/control-MTKT6267.js");await t(s,{port:o});break}case"use":{let{runUseCommand:t}=await import("./chunks/control-MTKT6267.js");await t(s,{port:o});break}case"claim":{let{runClaimCommand:t}=await import("./chunks/control-MTKT6267.js");await t(s,{port:o});break}case"close":{let{runCloseCommand:t}=await import("./chunks/control-MTKT6267.js");await t(s,{port:o});break}case"device":{let{runDeviceCommand:t}=await import("./chunks/device-PRSG6GUM.js");await t(s,{port:o});break}case"compat":{let{runCompat:t}=await import("./chunks/compat-A4QGEWEF.js");await t(e.commandArgs);break}case"desktop":{let{runDesktop:t}=await import("./chunks/desktop-EU3ZYXAK.js");await t(e.commandArgs,{port:o,device:m,driver:v});break}case"login":{let{runLogin:t}=await import("./chunks/login-LK75Q5WK.js");await t(e.commandArgs);break}case"logout":{let{runLogout:t}=await import("./chunks/logout-DFOTMKE4.js");await t();break}case"auth":{let{runAuth:t}=await import("./chunks/auth-R7Y7QI66.js");await t(e.commandArgs);break}case"setup":{let{runSetup:t}=await import("./chunks/setup-IFZLKN5S.js");await t(e.commandArgs);break}case"serve":{let{runServe:t}=await import("./chunks/serve-XBIN4KC3.js");await t(e.commandArgs,{port:o});break}case"daemon":{let{runDaemon:t}=await import("./chunks/daemon-TFLGTVRW.js");await t(e.commandArgs,{port:o});break}case"runtime":{let{runRuntime:t}=await import("./chunks/runtime-KG3OD3NE.js");await t(e.commandArgs);break}case"upgrade":{let{runUpgrade:t}=await import("./chunks/upgrade-JUAUWHUE.js");await t(e.commandArgs);break}case"version":{let{runVersion:t}=await import("./chunks/version-D3P356LR.js");await t(e.commandArgs);break}case"agent":{let{runAgentCommand:t}=await import("./chunks/agent-6RLGQQAV.js"),a=await t(e.commandArgs);process.exit(a)}case"agent-wrapper":{let{runAgentWrapper:t}=await import("./chunks/agent-wrapper-FVSNJGBC.js"),a=await t(e.commandArgs);process.exit(a)}case"skill":{let{runSkill:t}=await import("./chunks/skills-MPRCDS35.js");await t(e.commandArgs);break}case"app-fonts":{let{runAppFonts:t}=await import("./chunks/app-fonts-CT4QE2DG.js");await t(e.commandArgs);break}case"config":{let{runConfig:t}=await import("./chunks/config-YLHN3CWC.js");await t(e.commandArgs);break}default:console.error(` unknown command: ${n}`),console.error(" run `sootsim --help` to see the full surface."),process.exit(1)}
10
+ `),process.exit(1))}var e=u(process.argv);if(!e.command&&e.commandArgs.length>0){let{resolveHiddenRuntimeAlias:t}=await import("./chunks/hidden-runtime-alias-YTUEPZ3U.js");e=t(e)}{let{trackCliEvent:t}=await import("./chunks/telemetry-JHKOHSI2.js");t({event:"cli_command_invoked",properties:{command:e.command||(e.version?"version":e.help?"help":"none"),arg_count:e.commandArgs.length,platform:process.platform,arch:process.arch,node_version:process.versions.node}})}if(e.command!=="serve"&&e.command!=="daemon"){let{consumeRuntimeUpgradeNotice:t}=await import("./chunks/home-paths-7K6HDXOP.js"),a=t();a&&process.stderr.write(` SootSim engine upgraded to v${a.to}`+(a.from?` (from v${a.from})`:"")+` \xB7 what's new: https://sootsim.com/changelog
11
+ `)}async function c(t){let{flushCliTelemetry:a}=await import("./chunks/telemetry-JHKOHSI2.js");await a(),process.exit(t)}if(e.version){let{getCliVersion:t}=await import("./chunks/cli-version-V4GJYM74.js"),{IS_BETA:a,BETA_LABEL:i}=await import("./chunks/beta-ZQCASJ7C.js"),p=a?` \xB7 ${i}`:"";console.log(`sootsim v${t()}${p}`);let{readActiveRuntime:b}=await import("./chunks/home-paths-7K6HDXOP.js"),l=b();console.log(l?`runtime v${l}`:"runtime not installed"),await c(0)}if(e.help&&!e.command){let{printHelp:t}=await import("./chunks/help-7CIGANJN.js");t(),await c(0)}var o=e.globalFlags.port,r=e.verbose,m=e.globalFlags.device,d=e.globalFlags.theme,v=e.globalFlags.driver,_=e.globalFlags.headless===!0,g=e.globalFlags.sim??e.globalFlags.session??e.globalFlags.tab,s=g?["--sim",g,...e.commandArgs]:e.commandArgs;if(m||d){let{settingsStore:t}=await import("./chunks/store-PTRXOHHV.js"),a={};m&&(a.deviceModel=m),d&&(a.colorScheme=d),t.apply(a)}if(!e.command&&e.commandArgs.length===0){let{runSetup:t,shouldRunSetupForBareCommand:a}=await import("./chunks/setup-KODPGJBJ.js");a()&&(await t(["--from-welcome"]),await c(0));let{printHelp:i}=await import("./chunks/help-7CIGANJN.js");i(),await c(0)}var n=e.command??"";if(!n){if(e.commandArgs.length>0){let a=e.commandArgs.find(i=>!i.startsWith("-"))??e.commandArgs[0];console.error(` unknown command: ${a}`),console.error(" run `sootsim --help` to see the full surface."),await c(1)}let{printHelp:t}=await import("./chunks/help-7CIGANJN.js");t(),await c(0)}if(e.help||e.commandArgs.includes("--help")||e.commandArgs.includes("-h")){if(n==="skill"){let{runSkill:i}=await import("./chunks/skills-DEAUDOAN.js");await i(e.commandArgs),await c(0)}let t=n==="do"||n==="get"||n==="debug"||n==="shell"||n==="wait",a=e.commandArgs.find(i=>!i.startsWith("-"));if(n!=="shell")if(t&&!a){let{printGroupHelp:i}=await import("./chunks/help-7CIGANJN.js");i(n)&&await c(0)}else{let{printCommandHelp:i}=await import("./chunks/help-7CIGANJN.js");i(t&&a?a:n,{prefer:t&&a?"verb":"command",group:t&&a?n:void 0}),await c(0)}}k(n);if(w.has(n)){let{runInspect:t}=await import("./chunks/inspect-ZY3QI7VJ.js");await t([n,...s],{port:o,verbose:r})}else switch(n){case"assert":{let{runAssert:t}=await import("./chunks/assert-ZAYFOW6R.js");await t(e.commandArgs);break}case"detox":{let{runDetox:t}=await import("./chunks/detox-QG4IBZAG.js");await t(e.commandArgs,{port:o,verbose:r});break}case"maestro":{let{runMaestro:t}=await import("./chunks/maestro-YD3G7LSG.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"record":{let{runRecord:t}=await import("./chunks/record-ZC4W6QN6.js");await t(s,{port:o,verbose:r});break}case"film":{let{runFilm:t}=await import("./chunks/film-CT7FHNLE.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"storage":{let{runStorage:t}=await import("./chunks/storage-P7FQRH5T.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"perf":{let{runPerf:t}=await import("./chunks/perf-AY3ZKTDS.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"screenshot":{let{runScreenshotCommand:t}=await import("./chunks/screenshot-command-NRKTGTSJ.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"mode":{let{runMode:t}=await import("./chunks/mode-Y6JJMXU5.js");await t(s,{port:o,verbose:r});break}case"inspect":{let{runInspect:t}=await import("./chunks/inspect-ZY3QI7VJ.js");await t(s,{port:o,verbose:r});break}case"debug":{let{runDebug:t}=await import("./chunks/debug-ZKUCSCOU.js");await t(s,{port:o,verbose:r});break}case"timeline":{let{runTimeline:t}=await import("./chunks/timeline-7ISYXLR3.js");await t(s,{port:o,verbose:r});break}case"what-happened":{let{runWhatHappened:t}=await import("./chunks/what-happened-YKXXY6W2.js");await t(s,{port:o,verbose:r});break}case"open":{let{runOpenCommand:t}=await import("./chunks/control-7VDFEVWU.js");await t(s,{port:o});break}case"use":{let{runUseCommand:t}=await import("./chunks/control-7VDFEVWU.js");await t(s,{port:o});break}case"claim":{let{runClaimCommand:t}=await import("./chunks/control-7VDFEVWU.js");await t(s,{port:o});break}case"close":{let{runCloseCommand:t}=await import("./chunks/control-7VDFEVWU.js");await t(s,{port:o});break}case"device":{let{runDeviceCommand:t}=await import("./chunks/device-QC6MDI7W.js");await t(s,{port:o});break}case"compat":{let{runCompat:t}=await import("./chunks/compat-W7E4744D.js");await t(e.commandArgs);break}case"desktop":{let{runDesktop:t}=await import("./chunks/desktop-L3OXTT45.js");await t(e.commandArgs,{port:o,device:m,driver:v});break}case"login":{let{runLogin:t}=await import("./chunks/login-XHOOXAOW.js");await t(e.commandArgs);break}case"logout":{let{runLogout:t}=await import("./chunks/logout-7BISNC5F.js");await t();break}case"auth":{let{runAuth:t}=await import("./chunks/auth-Y53UANSH.js");await t(e.commandArgs);break}case"setup":{let{runSetup:t}=await import("./chunks/setup-KODPGJBJ.js");await t(e.commandArgs);break}case"serve":{let{runServe:t}=await import("./chunks/serve-4X2WI5XF.js");await t(e.commandArgs,{port:o});break}case"daemon":{let{runDaemon:t}=await import("./chunks/daemon-UI3PQ47F.js");await t(e.commandArgs,{port:o});break}case"runtime":{let{runRuntime:t}=await import("./chunks/runtime-MVYAP3UX.js");await t(e.commandArgs);break}case"upgrade":{let{runUpgrade:t}=await import("./chunks/upgrade-NZBJS5L2.js");await t(e.commandArgs);break}case"version":{let{runVersion:t}=await import("./chunks/version-J6BLGLTO.js");await t(e.commandArgs);break}case"agent":{let{runAgentCommand:t}=await import("./chunks/agent-6P5T2QHC.js"),a=await t(e.commandArgs);process.exit(a)}case"agent-wrapper":{let{runAgentWrapper:t}=await import("./chunks/agent-wrapper-SMEMJZXI.js"),a=await t(e.commandArgs);process.exit(a)}case"skill":{let{runSkill:t}=await import("./chunks/skills-DEAUDOAN.js");await t(e.commandArgs);break}case"app-fonts":{let{runAppFonts:t}=await import("./chunks/app-fonts-6OQTLX3U.js");await t(e.commandArgs);break}case"config":{let{runConfig:t}=await import("./chunks/config-N7G65EY2.js");await t(e.commandArgs);break}default:console.error(` unknown command: ${n}`),console.error(" run `sootsim --help` to see the full surface."),process.exit(1)}
@@ -1,5 +1,5 @@
1
- /*! sootsim v0.1.226 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a as b,b as E,c as A,d as l,e as S,g as j,h as T,j as I,m as k,n as D,o as x,p as g,q as O,r as C}from"./chunk-FEFQUXJP.js";import"./chunk-BANHHF53.js";import{c as $}from"./chunk-NSPJ2YPI.js";import"./chunk-3ECLF2KG.js";import"./chunk-7GPF3DB3.js";import"./chunk-BTQGLGVN.js";import"./chunk-4FFUGYCE.js";import"./chunk-BED4PLYP.js";import{j as y}from"./chunk-3AYAZ6BT.js";import"./chunk-RGI2QABU.js";import"./chunk-AAXWQESK.js";import"./chunk-UURHCZIJ.js";import"./chunk-HJY4JYOF.js";import w from"node:fs";import d from"node:path";import{spawn as _}from"node:child_process";import F from"node:net";import{WebSocket as R}from"ws";var c=class extends Error{code;constructor(e,t){super(e),this.name="AgentDaemonError",this.code=t}},f=class{ws;port;commandTimeoutMs;ready;closed=!1;nextId=1;pending=new Map;eventListeners=new Set;statusListeners=new Set;disconnectListeners=new Set;constructor(e={}){this.port=e.port??7668,this.commandTimeoutMs=e.commandTimeoutMs??15e3,this.ws=new R(`ws://localhost:${this.port}`),this.ready=new Promise((t,r)=>{let s=()=>{this.ws.off("error",o),t()},o=i=>{this.ws.off("open",s),r(new c(`could not connect to sootsim daemon on port ${this.port}: ${i.message}`,"NO_DAEMON"))};this.ws.once("open",s),this.ws.once("error",o)}),this.ws.on("message",t=>this.handleMessage(t)),this.ws.on("close",()=>this.handleClose()),this.ws.on("error",()=>{})}async waitReady(){return this.ready}async listProjects(){return this.send("agent:list-projects")}async upsertProject(e){return this.send("agent:upsert-project",{input:e})}async deleteProject(e){return this.send("agent:delete-project",{projectId:e})}async autoAttachForUrl(e){return this.send("agent:auto-attach-for-url",{input:e})}async listSessions(e){return this.send("agent:list-sessions",{projectId:e})}async startSession(e){return this.send("agent:start-session",{input:e})}async sendPrompt(e,t){return this.send("agent:send-prompt",{sessionId:e,prompt:t})}async sendClaimedPrompt(e,t){return this.send("agent:send-claimed-prompt",{simId:e,prompt:t})}async endSession(e){return this.send("agent:end-session",{sessionId:e})}async getTranscript(e){return this.send("agent:get-transcript",{sessionId:e})}async getPaths(){return this.send("agent:get-paths")}async subscribeEvents(e){return this.send("agent:subscribe-events",{sessionId:e})}async unsubscribeEvents(e){return this.send("agent:unsubscribe-events",{sessionId:e})}onAgentEvent(e){return this.eventListeners.add(e),()=>this.eventListeners.delete(e)}onSessionStatusChange(e){return this.statusListeners.add(e),()=>this.statusListeners.delete(e)}onDisconnect(e){return this.disconnectListeners.add(e),()=>this.disconnectListeners.delete(e)}close(){if(!this.closed){this.closed=!0;try{this.ws.close()}catch{}}}async send(e,t={}){if(await this.ready,this.closed||this.ws.readyState!==R.OPEN)throw new c("daemon connection is closed","NO_DAEMON");let r=this.nextId++;return new Promise((s,o)=>{let i=setTimeout(()=>{this.pending.delete(r),o(new c(`${e} timed out after ${Math.round(this.commandTimeoutMs/1e3)}s`,"TIMEOUT"))},this.commandTimeoutMs);this.pending.set(r,{resolve:s,reject:o,timer:i});try{this.ws.send(JSON.stringify({id:r,type:e,...t}))}catch(a){clearTimeout(i),this.pending.delete(r),o(a instanceof Error?a:new Error(String(a)))}})}handleMessage(e){let t;try{t=JSON.parse(String(e))}catch{return}if(!t||typeof t!="object")return;if(t.type==="agent:event"){for(let s of this.eventListeners)try{s({sessionId:t.sessionId,event:t.event})}catch{}return}if(t.type==="agent:session-status"){for(let s of this.statusListeners)try{s(t.session)}catch{}return}if(typeof t.id!="number")return;let r=this.pending.get(t.id);r&&(this.pending.delete(t.id),clearTimeout(r.timer),t.error?r.reject(new c(t.error,t.code)):r.resolve(t.result))}handleClose(){if(!this.closed){this.closed=!0;for(let[,e]of this.pending)clearTimeout(e.timer),e.reject(new c("daemon disconnected","DISCONNECT"));this.pending.clear();for(let e of this.disconnectListeners)try{e()}catch{}}}};function M(n=7668,e=400){return new Promise(t=>{let r=new F.Socket,s=!1,o=i=>{s||(s=!0,r.destroy(),t(i))};r.setTimeout(e),r.once("connect",()=>o(!0)),r.once("timeout",()=>o(!1)),r.once("error",()=>o(!1)),r.connect(n,"127.0.0.1")})}async function B(n={}){let e=n.port??7668;if(await M(e))return{alreadyRunning:!0};if(y())throw new c(`no sootsim bridge on port ${e}. run \`sootsim setup\` to install the background daemon, or \`sootsim serve\` in another shell.`,"DEV_HOST_REFUSED");let{cmd:t,prefixArgs:r}=C(),s=[...r,"serve","--quiet"];e!==7668&&s.push("--port",String(e));let o=_(t,s,{detached:!0,stdio:"ignore",env:process.env,cwd:process.cwd()});o.unref();let i=Date.now()+(n.startupTimeoutMs??5e3);for(;Date.now()<i;){if(await M(e))return{alreadyRunning:!1,pid:o.pid};await new Promise(a=>setTimeout(a,100))}throw new c(`spawned sootsim daemon on port ${e} but it did not come up in time. run \`sootsim serve\` manually to diagnose.`,"SPAWN_TIMEOUT")}async function h(n={}){await B({port:n.port,startupTimeoutMs:n.startupTimeoutMs});let e=new f(n);try{await e.waitReady()}catch(t){throw e.close(),t}return e}async function ge(n){let[e,...t]=n;try{switch(e){case void 0:case"--help":case"-h":case"help":return L(),0;case"attach":return await W(t);case"projects":return await J();case"project":return await V(t);case"sessions":return await H(t);case"start":return await G(t);case"prompt":return await q(t);case"watch":return await K(t);case"transcript":return await z(t);case"end":return await Q(t);case"paths":return await Y();default:return process.stderr.write(`unknown agent subcommand: ${e}
1
+ /*! sootsim v0.1.228 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{a as b,b as E,c as A,d as l,e as S,g as j,h as T,j as I,m as k,n as D,o as x,p as g,q as O,r as C}from"./chunk-MWZOCZ7J.js";import"./chunk-72AQH53U.js";import{c as $}from"./chunk-JA52F67H.js";import"./chunk-D2QUTBZP.js";import"./chunk-EFMPSG7T.js";import"./chunk-JV43GSZB.js";import"./chunk-4A6HM2K3.js";import"./chunk-EPQ5EZBJ.js";import"./chunk-RT3OS624.js";import{j as y}from"./chunk-PNPH4JLS.js";import"./chunk-JWZMAJC7.js";import"./chunk-MCMMRIJV.js";import"./chunk-7HMRWJHH.js";import"./chunk-6U3GMNTP.js";import w from"node:fs";import d from"node:path";import{spawn as _}from"node:child_process";import F from"node:net";import{WebSocket as R}from"ws";var c=class extends Error{code;constructor(e,t){super(e),this.name="AgentDaemonError",this.code=t}},f=class{ws;port;commandTimeoutMs;ready;closed=!1;nextId=1;pending=new Map;eventListeners=new Set;statusListeners=new Set;disconnectListeners=new Set;constructor(e={}){this.port=e.port??7668,this.commandTimeoutMs=e.commandTimeoutMs??15e3,this.ws=new R(`ws://localhost:${this.port}`),this.ready=new Promise((t,r)=>{let s=()=>{this.ws.off("error",o),t()},o=i=>{this.ws.off("open",s),r(new c(`could not connect to sootsim daemon on port ${this.port}: ${i.message}`,"NO_DAEMON"))};this.ws.once("open",s),this.ws.once("error",o)}),this.ws.on("message",t=>this.handleMessage(t)),this.ws.on("close",()=>this.handleClose()),this.ws.on("error",()=>{})}async waitReady(){return this.ready}async listProjects(){return this.send("agent:list-projects")}async upsertProject(e){return this.send("agent:upsert-project",{input:e})}async deleteProject(e){return this.send("agent:delete-project",{projectId:e})}async autoAttachForUrl(e){return this.send("agent:auto-attach-for-url",{input:e})}async listSessions(e){return this.send("agent:list-sessions",{projectId:e})}async startSession(e){return this.send("agent:start-session",{input:e})}async sendPrompt(e,t){return this.send("agent:send-prompt",{sessionId:e,prompt:t})}async sendClaimedPrompt(e,t){return this.send("agent:send-claimed-prompt",{simId:e,prompt:t})}async endSession(e){return this.send("agent:end-session",{sessionId:e})}async getTranscript(e){return this.send("agent:get-transcript",{sessionId:e})}async getPaths(){return this.send("agent:get-paths")}async subscribeEvents(e){return this.send("agent:subscribe-events",{sessionId:e})}async unsubscribeEvents(e){return this.send("agent:unsubscribe-events",{sessionId:e})}onAgentEvent(e){return this.eventListeners.add(e),()=>this.eventListeners.delete(e)}onSessionStatusChange(e){return this.statusListeners.add(e),()=>this.statusListeners.delete(e)}onDisconnect(e){return this.disconnectListeners.add(e),()=>this.disconnectListeners.delete(e)}close(){if(!this.closed){this.closed=!0;try{this.ws.close()}catch{}}}async send(e,t={}){if(await this.ready,this.closed||this.ws.readyState!==R.OPEN)throw new c("daemon connection is closed","NO_DAEMON");let r=this.nextId++;return new Promise((s,o)=>{let i=setTimeout(()=>{this.pending.delete(r),o(new c(`${e} timed out after ${Math.round(this.commandTimeoutMs/1e3)}s`,"TIMEOUT"))},this.commandTimeoutMs);this.pending.set(r,{resolve:s,reject:o,timer:i});try{this.ws.send(JSON.stringify({id:r,type:e,...t}))}catch(a){clearTimeout(i),this.pending.delete(r),o(a instanceof Error?a:new Error(String(a)))}})}handleMessage(e){let t;try{t=JSON.parse(String(e))}catch{return}if(!t||typeof t!="object")return;if(t.type==="agent:event"){for(let s of this.eventListeners)try{s({sessionId:t.sessionId,event:t.event})}catch{}return}if(t.type==="agent:session-status"){for(let s of this.statusListeners)try{s(t.session)}catch{}return}if(typeof t.id!="number")return;let r=this.pending.get(t.id);r&&(this.pending.delete(t.id),clearTimeout(r.timer),t.error?r.reject(new c(t.error,t.code)):r.resolve(t.result))}handleClose(){if(!this.closed){this.closed=!0;for(let[,e]of this.pending)clearTimeout(e.timer),e.reject(new c("daemon disconnected","DISCONNECT"));this.pending.clear();for(let e of this.disconnectListeners)try{e()}catch{}}}};function M(n=7668,e=400){return new Promise(t=>{let r=new F.Socket,s=!1,o=i=>{s||(s=!0,r.destroy(),t(i))};r.setTimeout(e),r.once("connect",()=>o(!0)),r.once("timeout",()=>o(!1)),r.once("error",()=>o(!1)),r.connect(n,"127.0.0.1")})}async function B(n={}){let e=n.port??7668;if(await M(e))return{alreadyRunning:!0};if(y())throw new c(`no sootsim bridge on port ${e}. run \`sootsim setup\` to install the background daemon, or \`sootsim serve\` in another shell.`,"DEV_HOST_REFUSED");let{cmd:t,prefixArgs:r}=C(),s=[...r,"serve","--quiet"];e!==7668&&s.push("--port",String(e));let o=_(t,s,{detached:!0,stdio:"ignore",env:process.env,cwd:process.cwd()});o.unref();let i=Date.now()+(n.startupTimeoutMs??5e3);for(;Date.now()<i;){if(await M(e))return{alreadyRunning:!1,pid:o.pid};await new Promise(a=>setTimeout(a,100))}throw new c(`spawned sootsim daemon on port ${e} but it did not come up in time. run \`sootsim serve\` manually to diagnose.`,"SPAWN_TIMEOUT")}async function h(n={}){await B({port:n.port,startupTimeoutMs:n.startupTimeoutMs});let e=new f(n);try{await e.waitReady()}catch(t){throw e.close(),t}return e}async function ge(n){let[e,...t]=n;try{switch(e){case void 0:case"--help":case"-h":case"help":return L(),0;case"attach":return await W(t);case"projects":return await J();case"project":return await V(t);case"sessions":return await H(t);case"start":return await G(t);case"prompt":return await q(t);case"watch":return await K(t);case"transcript":return await z(t);case"end":return await Q(t);case"paths":return await Y();default:return process.stderr.write(`unknown agent subcommand: ${e}
3
3
  `),L(),2}}catch(r){if(r instanceof c)return process.stderr.write(`${r.message}
4
4
  `),1;throw r}}async function v(n){let e=await h({clientLabel:"sootsim-agent-cli"});try{return await n(e)}finally{e.close()}}function L(){$("agent")}function P(n,e){let t={},r=[];for(let s=0;s<n.length;s++){let o=n[s],i=e[o];i==="value"?(t[o]=n[s+1]??"",s++):i==="bool"?t[o]=!0:r.push(o)}return{flags:t,positional:r}}function U(n,e="codex"){return n==="codex"||n==="claude"?n:e}async function W(n){let{flags:e,positional:t}=P(n,{"--name":"value","--provider":"value"}),r=t[0];if(!r)return process.stderr.write(`usage: sootsim agent attach <dir> [--name X] [--provider codex|claude]
5
5
  `),2;let s=d.resolve(r);if(!w.existsSync(s))return process.stderr.write(`directory does not exist: ${s}
@@ -1,5 +1,5 @@
1
- /*! sootsim v0.1.226 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{b as M}from"./chunk-BANHHF53.js";import"./chunk-HJY4JYOF.js";import{execFile as G,spawn as q}from"node:child_process";import{randomUUID as X}from"node:crypto";import{constants as R,createReadStream as Y,createWriteStream as j,existsSync as I,openSync as J}from"node:fs";import V from"node:fs/promises";import z from"node:path";import K from"node:readline";import{promisify as Q}from"node:util";import{spawn as H}from"node:child_process";import L from"node:readline";var b=class extends Error{code;data;constructor(t,r,m){super(t),this.name="CodexRpcError",this.code=r,this.data=m}};function W(d){let t=H(d.bin,["app-server"],{cwd:d.cwd,env:{...process.env,...d.env},stdio:["pipe","pipe","pipe"]}),r=new Map,m=new Map,u=1,g=!1,a=new Promise(s=>{t.on("exit",(i,e)=>{g=!0;let p=new Error(`codex app-server exited (code=${i}, signal=${e??""})`);for(let{reject:f}of r.values())f(p);r.clear(),s({code:i,signal:e})})});L.createInterface({input:t.stdout,crlfDelay:1/0}).on("line",s=>{let i=s.trim();if(!i)return;let e;try{e=JSON.parse(i)}catch{return}if(e.id!=null&&(e.result!==void 0||e.error!==void 0)){let p=typeof e.id=="string"?Number(e.id):e.id,f=p!=null?r.get(p):void 0;if(!f)return;r.delete(p),e.error?f.reject(new b(e.error.message,e.error.code,e.error.data)):f.resolve(e.result);return}if(e.method){let p=m.get(e.method);if(!p)return;for(let f of p)try{f(e.params)}catch(l){console.error(`[codex-client] handler for "${e.method}" threw:`,l instanceof Error?l.stack??l.message:l)}}}),t.stderr.setEncoding("utf8"),t.stderr.on("data",s=>{let i=m.get("__stderr__");if(i)for(let e of i)try{e({text:s})}catch{}});function n(s){if(!g)try{t.stdin.write(JSON.stringify(s)+`
1
+ /*! sootsim v0.1.228 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{b as M}from"./chunk-72AQH53U.js";import"./chunk-6U3GMNTP.js";import{execFile as G,spawn as q}from"node:child_process";import{randomUUID as X}from"node:crypto";import{constants as R,createReadStream as Y,createWriteStream as j,existsSync as I,openSync as J}from"node:fs";import V from"node:fs/promises";import z from"node:path";import K from"node:readline";import{promisify as Q}from"node:util";import{spawn as H}from"node:child_process";import L from"node:readline";var b=class extends Error{code;data;constructor(t,r,m){super(t),this.name="CodexRpcError",this.code=r,this.data=m}};function W(d){let t=H(d.bin,["app-server"],{cwd:d.cwd,env:{...process.env,...d.env},stdio:["pipe","pipe","pipe"]}),r=new Map,m=new Map,u=1,g=!1,a=new Promise(s=>{t.on("exit",(i,e)=>{g=!0;let p=new Error(`codex app-server exited (code=${i}, signal=${e??""})`);for(let{reject:f}of r.values())f(p);r.clear(),s({code:i,signal:e})})});L.createInterface({input:t.stdout,crlfDelay:1/0}).on("line",s=>{let i=s.trim();if(!i)return;let e;try{e=JSON.parse(i)}catch{return}if(e.id!=null&&(e.result!==void 0||e.error!==void 0)){let p=typeof e.id=="string"?Number(e.id):e.id,f=p!=null?r.get(p):void 0;if(!f)return;r.delete(p),e.error?f.reject(new b(e.error.message,e.error.code,e.error.data)):f.resolve(e.result);return}if(e.method){let p=m.get(e.method);if(!p)return;for(let f of p)try{f(e.params)}catch(l){console.error(`[codex-client] handler for "${e.method}" threw:`,l instanceof Error?l.stack??l.message:l)}}}),t.stderr.setEncoding("utf8"),t.stderr.on("data",s=>{let i=m.get("__stderr__");if(i)for(let e of i)try{e({text:s})}catch{}});function n(s){if(!g)try{t.stdin.write(JSON.stringify(s)+`
3
3
  `)}catch{}}return{exited:a,on(s,i){let e=m.get(s);return e||(e=new Set,m.set(s,e)),e.add(i),()=>{e?.delete(i)}},request(s,i){if(g)return Promise.reject(new Error(`codex app-server closed; cannot call ${s}`));let e=u++;return new Promise((p,f)=>{r.set(e,{resolve:l=>p(l),reject:f,method:s}),n({jsonrpc:"2.0",id:e,method:s,params:i})})},notify(s,i){n({jsonrpc:"2.0",method:s,params:i})},async shutdown(s=1500){if(g)return;try{t.stdin.end()}catch{}let i=setTimeout(()=>{if(!g)try{t.kill("SIGTERM")}catch{}},s);try{await a}finally{clearTimeout(i)}},kill(s="SIGTERM"){if(!g)try{t.kill(s)}catch{}}}}var O="never",F="danger-full-access",Z={type:"dangerFullAccess"},D="fast",ee="medium",te=Q(G);async function ne(d,t){let r=t||d;if(r.includes("/")||r.includes("\\"))return I(r)?{ok:!0,path:r}:{ok:!1,message:`agent binary not found at path: ${r}`};try{let{stdout:m}=await te("which",[r],{timeout:1500}),u=m.trim();return u?{ok:!0,path:u}:{ok:!1,message:`${r} not found on PATH`}}catch{return{ok:!1,message:`${r} not found on PATH. install the ${d} CLI and retry, or pass --${d}-bin /path/to/${d} to agent start.`}}}function re(d){let t={"--session-id":"sessionId","--project-id":"projectId","--provider":"provider","--cwd":"cwd","--prompt-in":"promptIn","--events-out":"eventsOut","--transcript":"transcript","--codex-bin":"codexBin","--claude-bin":"claudeBin","--claude-session-uuid":"claudeSessionUuid"},r={};for(let m=0;m<d.length;m++){let u=d[m];if(u==="--fresh-thread"){r.freshThread=!0;continue}let g=t[u];g&&(r[g]=d[m+1],m++)}return r}async function Ie(d){let t=re(d);if(!t.sessionId||!t.projectId||!t.provider||!t.cwd||!t.promptIn||!t.eventsOut)return process.stderr.write(`usage: sootsim agent-wrapper --session-id <id> --project-id <id>
4
4
  --provider codex|claude --cwd <path>
5
5
  --prompt-in <fifo> --events-out <fifo>