sootsim 0.1.135 → 0.1.137

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 (150) hide show
  1. package/README.md +275 -91
  2. package/dist-cli/bin.js +3 -3
  3. package/dist-cli/chunks/{agent-EV3LMTI2.js → agent-BKFAIL4L.js} +2 -2
  4. package/dist-cli/chunks/{agent-wrapper-GOUAYJWS.js → agent-wrapper-3JFHSCJV.js} +2 -2
  5. package/dist-cli/chunks/{app-fonts-7ZGCVBWL.js → app-fonts-TTXUEYKI.js} +2 -2
  6. package/dist-cli/chunks/{assert-CZPAVBXB.js → assert-C7VKSGRY.js} +2 -2
  7. package/dist-cli/chunks/auto-bootstrap-HJ6L7Y4E.js +2 -0
  8. package/dist-cli/chunks/beta-7T5LPWKT.js +2 -0
  9. package/dist-cli/chunks/{chunk-G7W4URXX.js → chunk-63HEFSKV.js} +2 -2
  10. package/dist-cli/chunks/{chunk-QDGNAEBR.js → chunk-75DY2QLM.js} +2 -2
  11. package/dist-cli/chunks/{chunk-2VUPH4SQ.js → chunk-7PCYCERP.js} +1 -1
  12. package/dist-cli/chunks/{chunk-UOBTAOWL.js → chunk-ANGFHMMH.js} +2 -2
  13. package/dist-cli/chunks/{chunk-EKZJFBGU.js → chunk-BEJTLGKN.js} +2 -2
  14. package/dist-cli/chunks/{chunk-ZFDBEITH.js → chunk-BKV7FUWW.js} +3 -3
  15. package/dist-cli/chunks/{chunk-QDRAULAC.js → chunk-CA6CJRKB.js} +2 -2
  16. package/dist-cli/chunks/{chunk-UNM45I7B.js → chunk-CAUZR5SF.js} +2 -2
  17. package/dist-cli/chunks/{chunk-YQM3RWNV.js → chunk-CCRTUMOG.js} +2 -2
  18. package/dist-cli/chunks/{chunk-PVWYEMAR.js → chunk-CO4QNRZH.js} +1 -1
  19. package/dist-cli/chunks/{chunk-56LSJ5RZ.js → chunk-CQKGKXOK.js} +2 -2
  20. package/dist-cli/chunks/{chunk-QLXTTGJD.js → chunk-E3LM63GU.js} +2 -2
  21. package/dist-cli/chunks/{chunk-5PB5X45Q.js → chunk-E7NOG434.js} +2 -2
  22. package/dist-cli/chunks/{chunk-7WF23EDW.js → chunk-I34PNOL4.js} +2 -2
  23. package/dist-cli/chunks/{chunk-ASXKBNRL.js → chunk-IBFKK5VG.js} +2 -2
  24. package/dist-cli/chunks/{chunk-XAVQLMY7.js → chunk-J7HWHGGQ.js} +2 -2
  25. package/dist-cli/chunks/{chunk-OC5UQFLJ.js → chunk-JZLPETG5.js} +1 -1
  26. package/dist-cli/chunks/{chunk-CLO4G4ZK.js → chunk-K6AINLM6.js} +1 -1
  27. package/dist-cli/chunks/{chunk-2RQ5M6MJ.js → chunk-LLIJHSV3.js} +2 -2
  28. package/dist-cli/chunks/{chunk-TXDUBB6W.js → chunk-LVEXGHOK.js} +2 -2
  29. package/dist-cli/chunks/chunk-MD6MPZOP.js +1 -0
  30. package/dist-cli/chunks/chunk-NJMSKW6O.js +1 -0
  31. package/dist-cli/chunks/{chunk-5JSVHUKF.js → chunk-OICALYNN.js} +3 -3
  32. package/dist-cli/chunks/{chunk-XMZJQZFB.js → chunk-PKR7EICH.js} +2 -2
  33. package/dist-cli/chunks/{chunk-7TWZYT4A.js → chunk-PRSSYJLK.js} +1 -1
  34. package/dist-cli/chunks/{chunk-4PJFJ77U.js → chunk-Q4G7RPYT.js} +1 -1
  35. package/dist-cli/chunks/chunk-QOFYAH6W.js +2 -0
  36. package/dist-cli/chunks/{chunk-MLNENEA4.js → chunk-QPVFBWCA.js} +1 -1
  37. package/dist-cli/chunks/{chunk-AQ6QT2DY.js → chunk-RHG2LVLG.js} +2 -2
  38. package/dist-cli/chunks/{chunk-MLVYS3AE.js → chunk-RKMZY5E3.js} +2 -2
  39. package/dist-cli/chunks/{chunk-DNF7AILW.js → chunk-RPEGHZHO.js} +2 -2
  40. package/dist-cli/chunks/{chunk-CYELNMX7.js → chunk-RRJJOP5X.js} +2 -2
  41. package/dist-cli/chunks/{chunk-IJU5JVF2.js → chunk-RSKKY2YX.js} +1 -1
  42. package/dist-cli/chunks/{chunk-U6TZCGUB.js → chunk-S743VRVC.js} +1 -1
  43. package/dist-cli/chunks/{chunk-GBBGAIOO.js → chunk-U3UNJQLS.js} +1 -1
  44. package/dist-cli/chunks/{chunk-Z5WZKI7J.js → chunk-UIIVIVVT.js} +2 -2
  45. package/dist-cli/chunks/{chunk-5HFMOWG3.js → chunk-VPS5IHVH.js} +1 -1
  46. package/dist-cli/chunks/{chunk-OODLGJLZ.js → chunk-VTN2ZZQK.js} +2 -2
  47. package/dist-cli/chunks/{chunk-45Z6VTEC.js → chunk-VTZMWNB5.js} +1 -1
  48. package/dist-cli/chunks/{chunk-UM7EEN26.js → chunk-WYDF3HWY.js} +1 -1
  49. package/dist-cli/chunks/{chunk-QMUNA6VQ.js → chunk-XYXDJB37.js} +3 -3
  50. package/dist-cli/chunks/{chunk-XOK53FNY.js → chunk-XZI43TP4.js} +1 -1
  51. package/dist-cli/chunks/chunk-ZBTQ5NUM.js +1 -0
  52. package/dist-cli/chunks/{chunk-PWRO2WPW.js → chunk-ZV2GNDOE.js} +1 -1
  53. package/dist-cli/chunks/{chunk-S5ZKCSJZ.js → chunk-ZWCYWRHB.js} +1 -1
  54. package/dist-cli/chunks/cli-version-YLSCTD4T.js +2 -0
  55. package/dist-cli/chunks/{compat-DZRWNMC2.js → compat-QI454ESE.js} +3 -3
  56. package/dist-cli/chunks/{config-JNO26BKX.js → config-3WQGW5NO.js} +2 -2
  57. package/dist-cli/chunks/{control-GWQ44GVR.js → control-V3LESW4B.js} +2 -2
  58. package/dist-cli/chunks/{cpu-profile-2VSK3E45.js → cpu-profile-7TARS6YI.js} +2 -2
  59. package/dist-cli/chunks/{daemon-QMFDDXFF.js → daemon-Y32JYZ4J.js} +2 -2
  60. package/dist-cli/chunks/{debug-Q3K4TXLC.js → debug-2IZSN6F3.js} +3 -3
  61. package/dist-cli/chunks/demo-app-registry-2IQUP2VH.js +2 -0
  62. package/dist-cli/chunks/{detox-TX4AUV2W.js → detox-5KNUYOUK.js} +2 -2
  63. package/dist-cli/chunks/{device-E5RRT67Y.js → device-232QOTCZ.js} +2 -2
  64. package/dist-cli/chunks/{diagnose-CGHRCXKF.js → diagnose-KQW7BDVO.js} +2 -2
  65. package/dist-cli/chunks/drivers-HJS44U4X.js +2 -0
  66. package/dist-cli/chunks/{electron-YSY7HZBI.js → electron-O6ELQRHJ.js} +3 -3
  67. package/dist-cli/chunks/flow-N75N77WK.js +2 -0
  68. package/dist-cli/chunks/help-JQEAOFW4.js +2 -0
  69. package/dist-cli/chunks/{hints-V7M7GKXL.js → hints-ZJSHULYA.js} +2 -2
  70. package/dist-cli/chunks/{home-paths-KYTBCLPQ.js → home-paths-O7V4JW2V.js} +2 -2
  71. package/dist-cli/chunks/{inspect-ZUYLUCRJ.js → inspect-T36ODJAN.js} +4 -4
  72. package/dist-cli/chunks/install-LSIVGKDQ.js +2 -0
  73. package/dist-cli/chunks/{install-desktop-HKLVDL7Q.js → install-desktop-O3QC6B7F.js} +3 -3
  74. package/dist-cli/chunks/{keys-QJDWORM5.js → keys-PRTEPMIU.js} +2 -2
  75. package/dist-cli/chunks/{launch-CBIYGOV7.js → launch-K4LIL5HE.js} +3 -3
  76. package/dist-cli/chunks/{login-II2EWFHY.js → login-VDH2M4OQ.js} +4 -4
  77. package/dist-cli/chunks/{logout-E52753SB.js → logout-H6NCIG4O.js} +2 -2
  78. package/dist-cli/chunks/{maestro-DUIODHYF.js → maestro-7EWZQE7C.js} +2 -2
  79. package/dist-cli/chunks/{preview-LSVZXF7S.js → preview-6ADUV4HS.js} +2 -2
  80. package/dist-cli/chunks/{profile-2LUO56EX.js → profile-D634RPOU.js} +2 -2
  81. package/dist-cli/chunks/{react-KPNHGOQ3.js → react-CI2XOOKG.js} +2 -2
  82. package/dist-cli/chunks/{record-LLNZNJSY.js → record-6PYC73J5.js} +2 -2
  83. package/dist-cli/chunks/runtime-P32F4A7T.js +2 -0
  84. package/dist-cli/chunks/{runtime-delivery-A2PZCEPU.js → runtime-delivery-O2LM35Z3.js} +2 -2
  85. package/dist-cli/chunks/{screenshot-OEIH2ETO.js → screenshot-DYLZI2UB.js} +2 -2
  86. package/dist-cli/chunks/{screenshot-mode-3TDLGZPT.js → screenshot-mode-WCJJZJ4I.js} +2 -2
  87. package/dist-cli/chunks/{screenshots-GCJW22B5.js → screenshots-UBYQLSGU.js} +2 -2
  88. package/dist-cli/chunks/{server-CBH2UYLT.js → server-INUPMADW.js} +3 -3
  89. package/dist-cli/chunks/setup-repo-4BMV6H4N.js +2 -0
  90. package/dist-cli/chunks/{skills-DVPCRUUO.js → skills-FLIZVEDL.js} +2 -2
  91. package/dist-cli/chunks/{start-2T5QRVL2.js → start-4I2VFTT7.js} +4 -4
  92. package/dist-cli/chunks/store-5RD45SIG.js +2 -0
  93. package/dist-cli/chunks/telemetry-T3OM5YCG.js +2 -0
  94. package/dist-cli/chunks/{test-ZGUHV3XE.js → test-NQ5O2ND4.js} +3 -3
  95. package/dist-cli/chunks/{three-mode-3BUTKBTR.js → three-mode-QKX5ZD3T.js} +2 -2
  96. package/dist-cli/chunks/{timeline-WS2FXPG2.js → timeline-HQGP3BWQ.js} +2 -2
  97. package/dist-cli/chunks/{upgrade-FFTCNLCI.js → upgrade-NJCLU3PX.js} +2 -2
  98. package/dist-cli/chunks/upload-WQNOKGN6.js +2 -0
  99. package/dist-cli/chunks/{version-OQOFXNMZ.js → version-R6C63OCK.js} +2 -2
  100. package/dist-cli/chunks/{web-Y2ZMUXP4.js → web-ISRXHOOS.js} +2 -2
  101. package/dist-cli/chunks/{what-happened-5A2RUC6V.js → what-happened-YGHZIXUQ.js} +2 -2
  102. package/dist-cli/chunks/{whoami-ZVM5A34Z.js → whoami-6WPS2PJB.js} +2 -2
  103. package/dist-lib/agent-daemon-client.cjs +1 -1
  104. package/dist-lib/agent-events.cjs +1 -1
  105. package/dist-lib/agent-sessions.cjs +1 -1
  106. package/dist-lib/attached-projects.cjs +1 -1
  107. package/dist-lib/auth/shared-session.cjs +1 -1
  108. package/dist-lib/backend-origin.cjs +1 -1
  109. package/dist-lib/beta.cjs +1 -1
  110. package/dist-lib/beta.mjs +1 -1
  111. package/dist-lib/bridge-constants.cjs +1 -1
  112. package/dist-lib/cli-constants.cjs +1 -1
  113. package/dist-lib/config.cjs +1 -1
  114. package/dist-lib/detox/index.cjs +1 -1
  115. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  116. package/dist-lib/home-paths.cjs +1 -1
  117. package/dist-lib/host/bridge-host.cjs +1 -1
  118. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  119. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  120. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  121. package/dist-lib/host/websocket-proxy.cjs +1 -1
  122. package/dist-lib/index.cjs +1 -1
  123. package/dist-lib/metro.cjs +1 -1
  124. package/dist-lib/profiles.cjs +1 -1
  125. package/dist-lib/render-mode.cjs +1 -1
  126. package/dist-lib/scripts/demo-app-registry.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 +1 -1
  131. package/dist-lib/vite.cjs +1 -1
  132. package/package.json +1 -1
  133. package/src/native-seam-manifest.ts +1 -1
  134. package/dist-cli/chunks/auto-bootstrap-PRFJ44RD.js +0 -2
  135. package/dist-cli/chunks/beta-HNHNY7GJ.js +0 -2
  136. package/dist-cli/chunks/chunk-GGLJLJ7P.js +0 -1
  137. package/dist-cli/chunks/chunk-TVCTN2EF.js +0 -2
  138. package/dist-cli/chunks/chunk-VH4VPAMR.js +0 -1
  139. package/dist-cli/chunks/chunk-ZANDDBFJ.js +0 -1
  140. package/dist-cli/chunks/cli-version-EOI7UVCS.js +0 -2
  141. package/dist-cli/chunks/demo-app-registry-LDTURANL.js +0 -2
  142. package/dist-cli/chunks/drivers-WF5OT6EA.js +0 -2
  143. package/dist-cli/chunks/flow-PV4WV3DZ.js +0 -2
  144. package/dist-cli/chunks/help-RZ2M7P7U.js +0 -2
  145. package/dist-cli/chunks/install-XLJ4CJUN.js +0 -2
  146. package/dist-cli/chunks/runtime-B3RQF5NL.js +0 -2
  147. package/dist-cli/chunks/setup-repo-U7XMVCQA.js +0 -2
  148. package/dist-cli/chunks/store-WIQ4ZE4Z.js +0 -2
  149. package/dist-cli/chunks/telemetry-2XPV6PVU.js +0 -2
  150. package/dist-cli/chunks/upload-JQMTDQMQ.js +0 -2
package/README.md CHANGED
@@ -1,32 +1,192 @@
1
- # SootSim
2
-
3
- CLI + framework plugins for SootBean's browser-native simulator.
4
-
5
- this package is the public, publishable face of SootSim. it contains:
6
-
7
- - `sootsim` CLI (bridge client — drives a running SootSim instance over
8
- WebSocket for describing UI, tapping elements, recording flows, etc.)
9
- - `sootsim/vite` vite plugin for wrapping an app bundler with SootSim
10
- - `sootsim/metro` metro plugin for wrapping a react native bundler
11
- - `sootsim/skills` registry for SootBean-flavored automation skills
12
-
13
- the rendering engine itself (canvas renderer, yoga layout, iOS/Android shell
14
- chrome, wasm, electron shell) lives in the private `sootsim-engine` workspace
15
- package and is not published.
16
-
17
- Platform status: iOS is the parity baseline. Android is bootstrapped and
18
- usable for focused conformance slices, with Android bundle/runtime identity,
19
- device profiles, initial shell chrome, gesture and three-button nav proof,
20
- Android system UI/window metrics with cutout-safe insets, and Android
21
- SootSim-only matrix runs.
22
- Android `react-native-screens`, `react-native-safe-area-context`,
23
- RefreshControl, keyboard/IME resize, EdgeEffect, scroll deceleration/fading
24
- edges/scrollbars, and basic system-bar API support are improving but still need
25
- the Android launch gates before this can be called full parity.
26
- It is not full Android parity until the Android launch gates in the SootBean repo
27
- plan pass.
1
+ # sootsim
2
+
3
+ The public, publishable face of SootSim: a CLI, two pairs of bundler plugins, a
4
+ Detox/Maestro test surface, and a skills registry for driving a browser-native
5
+ React Native simulator.
6
+
7
+ ## Overview
8
+
9
+ `sootsim` is everything you install to *talk to* a SootSim instance — it
10
+ contains **no rendering code**. The canvas engine (CanvasKit renderer, Yoga
11
+ layout, iOS/Android shell chrome, Electron shell) lives in the private
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`.
14
+
15
+ What this package gives you:
16
+
17
+ - the `sootsim` CLI the primary debugging and automation surface (inspect the
18
+ UI tree, tap elements, capture flows, screenshot, record, run agents)
19
+ - `sootsim/vite` and `sootsim/metro` bundler plugins that serve the installed
20
+ engine runtime from your own dev server
21
+ - `sootsim/detox` — a drop-in Detox driver + jest preset
22
+ - `sootsim/sdk` — the inspect/interact verbs as a programmatic API
23
+ - `sootsim/skills` a registry of SootBean-flavored automation skills
24
+
25
+ Platform status: iOS is the parity baseline. Android is bootstrapped and usable
26
+ for focused conformance slices (bundle/runtime identity, device profiles, shell
27
+ chrome, gesture + three-button nav, system UI/window metrics with cutout-safe
28
+ insets) but is not full parity until the Android launch gates in the SootBean
29
+ repo plan pass.
30
+
31
+ ## What's in this package (and what's not)
32
+
33
+ | in `sootsim` | in `sootsim-engine` (private, not published) |
34
+ | --- | --- |
35
+ | CLI, bridge client, drivers | CanvasKit renderer, Yoga layout |
36
+ | bridge daemon host (`SootSimBridgeHost`) | iOS/Android shell chrome, home grid, app switcher |
37
+ | vite/metro plugins, RN resolver, native stubs | Electron shell, wasm |
38
+ | Detox/Maestro runners, screenshots, skills | the actual pixels |
39
+
40
+ The engine is delivered at runtime, never bundled into this package. A
41
+ committed-but-unbuilt engine fix has no effect on a running sim until the
42
+ runtime tarball is rebuilt and re-fetched.
43
+
44
+ ## Architecture
45
+
46
+ The architecture is a **three-tier relay**: a short-lived CLI process, a single
47
+ persistent bridge daemon, and the sim itself — a browser/Electron *page* that
48
+ internally splits into a **shell worker** and a **tenant worker**.
49
+
50
+ ```mermaid
51
+ graph TD
52
+ subgraph cli["sootsim CLI process (short-lived, cli/)"]
53
+ BIN["bin.ts (dispatcher + telemetry)"]
54
+ BOOT["auto-bootstrap.ts (ensure runtime + daemon)"]
55
+ INSPECT["commands/inspect/* (describe/find/do/get/wait)"]
56
+ FLOW["bridge-flow-runner.ts (maestro + flow)"]
57
+ DETOXCMD["commands/detox.ts + detox/ driver"]
58
+ SHOTS["commands/screenshot + record"]
59
+ AGENTCMD["commands/agent.ts"]
60
+ WSB["ws-bridge.ts (WsBridge client)"]
61
+ DRIVERS["drivers/* (chromium/electron/playwright/system)"]
62
+ end
63
+ subgraph daemon["bridge daemon process (persistent :7668)"]
64
+ HOST["SootSimBridgeHost (host/bridge-host.ts, HTTP+WS)"]
65
+ AGENTHOST["AgentHost (host/agent-host.ts, FIFO fan-out)"]
66
+ AGENTSESS["agent-sessions.ts + attached-projects.ts"]
67
+ SCAN["/__server-scan (dev-server-scanner.ts)"]
68
+ PROXY["fetch-proxy-handler + websocket-proxy"]
69
+ RTHTTP["runtime HTTP + self-update (runtime-delivery.ts)"]
70
+ end
71
+ subgraph sim["sim page (browser / Electron, launched by a driver)"]
72
+ SHELLW["shell worker (iOS chrome, home grid, app switcher)"]
73
+ TENANTW["tenant worker (guest RN app tree)"]
74
+ end
75
+ subgraph build["build-time plugins (src/)"]
76
+ VITEONE["sootsim/vite = vite-plugin-one.ts (serve runtime at /__soot/)"]
77
+ VITERN["sootsim() = vite-plugin.ts (RN resolver + native stubs)"]
78
+ METRO["sootsim/metro = metro-plugin.ts"]
79
+ COMPAT["@soot/compat stub-manifest (native seams)"]
80
+ ENGINESHIM["sootsim-engine react-native shim"]
81
+ end
82
+ SKILLS["sootsim/skills registry.ts (builtin skills)"]
83
+ subgraph fs["~/.sootsim (home-paths.ts)"]
84
+ RTDIR["runtimes/<version>/ (engine assets)"]
85
+ LOCK["daemon.json lockfile"]
86
+ CFG["config.json"]
87
+ end
88
+ CDN["sootbean.com CDN (runtimes/manifest.json + tarballs)"]
89
+ BIN --> BOOT
90
+ BIN --> INSPECT
91
+ BIN --> FLOW
92
+ BIN --> DETOXCMD
93
+ BIN --> SHOTS
94
+ BIN --> AGENTCMD
95
+ BOOT --> RTDIR
96
+ BOOT --> LOCK
97
+ BOOT -->|"fetch runtime"| CDN
98
+ BOOT -->|"register launchd/systemd"| HOST
99
+ INSPECT --> WSB
100
+ FLOW --> WSB
101
+ SHOTS --> WSB
102
+ AGENTCMD --> WSB
103
+ DETOXCMD --> DRIVERS
104
+ DETOXCMD --> WSB
105
+ WSB -->|"JSON WS cmds"| HOST
106
+ HOST -->|"forward to registered sim"| SHELLW
107
+ SHELLW -->|"replies + pushes"| HOST
108
+ HOST -->|"relayed results"| WSB
109
+ HOST --> AGENTHOST
110
+ AGENTHOST --> AGENTSESS
111
+ AGENTCMD --> AGENTHOST
112
+ HOST --> SCAN
113
+ HOST --> PROXY
114
+ HOST --> RTHTTP
115
+ RTHTTP --> RTDIR
116
+ RTHTTP -->|"manifest + tarball"| CDN
117
+ DRIVERS -->|"open sim page URL"| SHELLW
118
+ SHELLW -->|"NativeUIRequest"| TENANTW
119
+ VITEONE -->|"serve /__soot/ from"| RTDIR
120
+ METRO -->|"serve /__soot/ from"| RTDIR
121
+ VITEONE -.->|"launch electron app"| SHELLW
122
+ VITERN -->|"alias react-native"| ENGINESHIM
123
+ VITERN -->|"alias native pkgs"| COMPAT
124
+ VITERN -->|"dev server hosts"| HOST
125
+ SKILLS --> INSPECT
126
+ ```
28
127
 
29
- ## getting started
128
+ **The CLI process** (`cli/bin.ts`) parses argv, emits invocation telemetry, and
129
+ lazy-imports exactly one command module per run. Runtime commands first call
130
+ `auto-bootstrap.ts`, which guarantees the engine runtime is unpacked under
131
+ `~/.sootsim/runtimes/<version>` (fetching from the sootbean.com CDN via
132
+ `runtime-delivery.ts` when missing) and that the bridge daemon is registered
133
+ with launchd/systemd and reachable. Interactive verbs then open a `WsBridge`
134
+ (`cli/ws-bridge.ts`) to the daemon on **port 7668** and send JSON commands.
135
+ Inspection/interaction verbs live in `cli/commands/inspect/*` and are
136
+ re-exported as the programmatic `sootsim/sdk`; the Maestro/flow runner is
137
+ `cli/bridge-flow-runner.ts`; the Detox compat driver is in `detox/`. Test and
138
+ screenshot paths additionally use `cli/drivers/*` (chromium, electron,
139
+ playwright, system) to actually launch or attach the sim page before driving it.
140
+
141
+ **The daemon is one process** — `SootSimBridgeHost` (`src/host/bridge-host.ts`).
142
+ `sootsim server` runs it in the foreground; `sootsim daemon` is the *same
143
+ process* wrapped with autostart and the `~/.sootsim/daemon.json` lockfile. It is
144
+ an HTTP+WS hub: sims register over WS, CLI clients connect to drive them, and
145
+ the host forwards each command to the targeted sim and relays the reply back. It
146
+ also serves the engine runtime over HTTP (with a self-update route), exposes
147
+ `/__server-scan` to discover local metro/expo/vxrn/one dev servers, and proxies
148
+ guest-app fetch/WebSocket traffic so the cross-origin tenant worker can reach
149
+ localhost. The `AgentHost` extension owns one FIFO reader per agent session and
150
+ fans agent events out to every subscriber.
151
+
152
+ **The sim** is a browser/Electron *page* launched by a CLI driver. Inside that
153
+ page run two workers: the **shell worker** renders iOS chrome (home grid, app
154
+ switcher) and the **tenant worker** runs the guest RN tree, sending native-UI
155
+ requests up to the shell over `NativeUIRequest`. The bridge always addresses the
156
+ **shell worker**, never an undifferentiated sim. (Never say "main" unqualified —
157
+ distinguish the host page thread, the shell worker, and the tenant worker.)
158
+
159
+ **Two distinct plugin families** round it out, and they must not be conflated:
160
+
161
+ - The published **`sootsim/vite`** (`src/vite-plugin-one.ts`, `sootsimPlugin`)
162
+ and **`sootsim/metro`** plugins do one thing: serve the installed runtime at
163
+ `/__soot/` so an app's own dev server can host the sim shell (the vite plugin
164
+ can also launch the Electron app).
165
+ - The internal **`sootsim()`** plugin (`src/vite-plugin.ts`) is a separate, much
166
+ larger RN-resolution / native-stub plugin: it aliases `react-native` to the
167
+ engine shim and native packages to `@soot/compat` stubs, applies worklets
168
+ transforms, and is the one that instantiates `SootSimBridgeHost` inside its
169
+ own dev server. It is used to build the engine/shell and to load external RN
170
+ apps — it is **not** the published `sootsim/vite` export.
171
+
172
+ ## Key components
173
+
174
+ | component | role | key files |
175
+ | --- | --- | --- |
176
+ | sootsim CLI (bin + dispatcher) | Short-lived terminal entry point. Parses argv, emits telemetry, lazy-imports one command per run, and on runtime commands first runs auto-bootstrap to ensure the runtime + daemon exist before connecting. | `cli/bin.ts`, `cli/parse-args.ts`, `cli/auto-bootstrap.ts`, `cli/help.ts` |
177
+ | WsBridge client | Client side of the CLI→daemon link. Resolves the daemon port from the lockfile, opens a WS to `:7668`, sends JSON commands and awaits `{id,result}`/`{id,error}`. Used by every interactive command. | `cli/ws-bridge.ts`, `src/bridge-constants.ts`, `cli/current-sim.ts` |
178
+ | inspect / SDK command surface | Runtime inspection + interaction verbs (describe, find, do tap, get errors, wait, layout, settle). Re-exported as `sootsim/sdk` so programmatic callers drive a sim the same way the CLI does. | `cli/commands/inspect/core.ts`, `cli/commands/inspect/actions.ts`, `cli/commands/inspect.ts`, `src/sdk.ts` |
179
+ | flow / maestro / detox runners | Drop-in test compatibility. `bridge-flow-runner.ts` runs maestro-format YAML and recorded flows over the bridge; `detox/` provides a `by`/`element`/`expect`/`device` driver + jest preset so existing Detox suites run unchanged. | `cli/bridge-flow-runner.ts`, `cli/commands/maestro.ts`, `cli/commands/detox.ts`, `detox/index.ts`, `detox/jest-preset.cjs` |
180
+ | sim drivers | Launch/attach the actual sim page. The registry resolves a driver (chromium, electron, playwright, system browser) which opens the shell URL; used by detox/preview/screenshot paths to bring a sim online for the bridge. | `cli/drivers/index.ts`, `cli/drivers/registry.ts`, `cli/drivers/chromium.ts`, `cli/drivers/electron.ts`, `cli/drivers/playwright.ts` |
181
+ | SootSimBridgeHost (server/daemon) | The single persistent process (`sootsim server`, 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/server.ts`, `cli/commands/daemon.ts`, `src/home-paths.ts` |
182
+ | 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` |
183
+ | 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` |
184
+ | 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` |
185
+ | `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` |
186
+ | `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` |
187
+ | skills registry | SootBean-flavored automation skills (a11y-review, compat-check, perf-profile, screenshot-all, test-flow, visual-diff) discoverable by trigger; published as `sootsim/skills` and loadable from a project manifest. | `src/skills/registry.ts`, `src/skills/types.ts`, `skills/soot.md` |
188
+
189
+ ## Getting started
30
190
 
31
191
  ```sh
32
192
  npm i -g sootsim
@@ -34,18 +194,17 @@ cd my-app && sootsim setup-repo
34
194
  sootsim open 8081 # load a running metro/expo dev server
35
195
  ```
36
196
 
37
- that's the canonical path — CLI first. the bridge daemon registers itself on
38
- first use; you don't need to run anything to install it. a desktop GUI
39
- (Electron) is optional on top of that, see `sootsim install-desktop`.
40
- In Electron, `File > New Window` duplicates the focused simulator, while
41
- `File > New Simulator >` opens a new simulator window for a selected device
42
- using the same device list as `Window >`.
197
+ CLI-first is the canonical path. The bridge daemon registers itself on first use
198
+ you don't need to start anything to install it. A desktop GUI (Electron) is
199
+ optional on top, via `sootsim install-desktop`. In Electron, `File > New Window`
200
+ duplicates the focused simulator, while `File > New Simulator >` opens a new
201
+ window for a selected device (same device list as the `Window >` menu).
43
202
 
44
203
  ## CLI
45
204
 
46
- the CLI is the primary debugging surface for SootSim. use it first for runtime
205
+ The CLI is the primary debugging surface for SootSim use it first for runtime
47
206
  inspection, interaction, animation debugging, shell tracing, screenshots, and
48
- flow capture instead of treating it as a test-only tool.
207
+ flow capture, not just as a test runner.
49
208
 
50
209
  ```sh
51
210
  bun sootsim list # connected tabs
@@ -65,33 +224,55 @@ bun sootsim flow end --output flows/login.yaml
65
224
  bun sootsim test --detox # shared Detox-style parity suite against sootsim
66
225
  ```
67
226
 
68
- see the full command reference via `bun sootsim --help` or at
227
+ See the full command reference via `bun sootsim --help` or at
69
228
  `src/features/site/docs/sootsim/cli/` in the SootBean repo.
70
229
 
71
- ## coming from detox
230
+ ## Bundler plugins (`sootsim/vite` and `sootsim/metro`)
231
+
232
+ These serve the installed engine runtime from your own dev server. They do
233
+ **not** resolve React Native or stub native modules — that is the internal
234
+ `sootsim()` plugin's job (see Architecture).
235
+
236
+ ```ts
237
+ // vite.config.ts
238
+ import { sootsimPlugin } from 'sootsim/vite'
239
+
240
+ export default {
241
+ plugins: [sootsimPlugin()],
242
+ }
243
+ ```
244
+
245
+ ```js
246
+ // metro.config.js
247
+ const { withSootsim } = require('sootsim/metro')
248
+
249
+ module.exports = withSootsim({ /* your metro config */ })
250
+ ```
72
251
 
73
- SootSim ships a drop-in detox driver. your existing detox test files
74
- (`import { by, element, expect, device } from 'detox'`) run against SootSim
75
- with no code changes just add one line to your jest config:
252
+ ## Coming from Detox
253
+
254
+ SootSim ships a drop-in Detox driver. Existing Detox test files
255
+ (`import { by, element, expect, device } from 'detox'`) run against SootSim with
256
+ no code changes — just add one line to your jest config:
76
257
 
77
258
  ```js
78
259
  // jest config
79
260
  { preset: 'sootsim/detox/jest-preset' }
80
261
  ```
81
262
 
82
- or use the CLI directly:
263
+ Or use the CLI directly:
83
264
 
84
265
  ```sh
85
266
  bun sootsim detox # auto-discovers e2e/ tests and launches a shell
86
267
  bun sootsim detox init # scaffold a config + sample test
87
268
  ```
88
269
 
89
- see `docs/migrating-from-detox.md` for details.
270
+ See `../../docs/migrating-from-detox.md` for details.
90
271
 
91
- ## coming from maestro
272
+ ## Coming from Maestro
92
273
 
93
- SootSim's flow runner natively speaks maestro's YAML format. point it at your
94
- existing `.maestro/` directory:
274
+ SootSim's flow runner natively speaks Maestro's YAML. Point it at your existing
275
+ `.maestro/` directory:
95
276
 
96
277
  ```sh
97
278
  bun sootsim maestro # discover .maestro/ and run all flows
@@ -99,13 +280,14 @@ bun sootsim maestro test .maestro/ # explicit
99
280
  bun sootsim maestro --list-compat # see the verb support matrix
100
281
  ```
101
282
 
102
- most maestro verbs work out of the box (`tapOn`, `assertVisible`, `inputText`,
103
- `scrollUntilVisible`, `launchApp`, `when:`, `repeat`, `runFlow`, …). verbs that
104
- need real device hardware (GPS, radio, photo library) throw a clear error.
283
+ Most Maestro verbs work out of the box (`tapOn`, `assertVisible`, `inputText`,
284
+ `scrollUntilVisible`, `launchApp`, `when:`, `repeat`, `runFlow`, …). Verbs that
285
+ need real device hardware (GPS, radio, photo library) throw a clear error. See
286
+ `../../docs/migrating-from-maestro.md` for the full compat matrix.
105
287
 
106
- see `docs/migrating-from-maestro.md` for the full compat matrix.
288
+ ## Screenshots and recordings
107
289
 
108
- for animation-heavy debugging, the useful path is usually:
290
+ For animation-heavy debugging, the useful path is usually:
109
291
 
110
292
  ```sh
111
293
  bun sootsim debug enable animated
@@ -119,11 +301,9 @@ bun sootsim record --duration 5 --output /tmp/sootsim-anim.mp4
119
301
  ```
120
302
 
121
303
  `sootsim screenshot --with-frame` composes the real SootSim shell device chrome
122
- around the raw screen bitmap. it reuses the shell bezel/button geometry and
123
- still excludes the electron top bar, rail gutter, and other window chrome.
124
-
125
- flows can use the same framed export path without changing normal maestro
126
- syntax:
304
+ around the raw screen bitmap (reusing the shell bezel/button geometry, excluding
305
+ the Electron top bar, rail gutter, and other window chrome). Flows can request
306
+ the framed export inline without changing Maestro syntax:
127
307
 
128
308
  ```yaml
129
309
  - takeScreenshot: hero
@@ -132,7 +312,7 @@ syntax:
132
312
  withFrame: true
133
313
  ```
134
314
 
135
- for plan-driven app-store exports, use `sootsim screenshots`:
315
+ For plan-driven app-store exports, use `sootsim screenshots`:
136
316
 
137
317
  ```yaml
138
318
  app: 8081
@@ -158,11 +338,12 @@ compose:
158
338
  bun sootsim screenshots --plan .sootsim/app-store.yaml
159
339
  ```
160
340
 
161
- the plan runner can reuse a visible sim for capture (`--sim a9`),
162
- stop after raw/framed intermediates (`--capture-only`), or rerender final
163
- marketing canvases from an existing raw directory (`--compose-only`).
164
-
165
- if your flow already writes screenshots to explicit project paths, set:
341
+ The plan runner can reuse a visible sim for capture (`--sim a9`), stop after
342
+ raw/framed intermediates (`--capture-only`), or rerender final marketing
343
+ canvases from an existing raw directory (`--compose-only`). If your flow already
344
+ writes screenshots to explicit project paths, set `from:` + `pathMode: flow` so
345
+ `sootsim screenshots` respects the flow's own `takeScreenshot` paths instead of
346
+ prepending `--screenshots <rawDir>`:
166
347
 
167
348
  ```yaml
168
349
  capture:
@@ -171,53 +352,56 @@ capture:
171
352
  pathMode: flow
172
353
  ```
173
354
 
174
- that tells `sootsim screenshots` to respect the flow's own `takeScreenshot`
175
- paths instead of prepending `--screenshots <rawDir>`.
176
-
177
- in the browser shell, `Screenshot Mode` now turns the live shell into a simple
178
- DOM composition surface: the rail + mac menu bar disappear, the device shifts
179
- down with a short transition, and editable title/subtitle fields appear above
180
- the frame for quick art-direction passes.
355
+ In the browser shell, `Screenshot Mode` turns the live shell into a simple DOM
356
+ composition surface: the rail + mac menu bar disappear, the device shifts down
357
+ with a short transition, and editable title/subtitle fields appear above the
358
+ frame for quick art-direction passes.
181
359
 
182
- ## registry and docs source of truth
360
+ ## Skills registry
183
361
 
184
- the published CLI registry and the generated website docs both come from
185
- `packages/sootsim-skills/`.
362
+ `sootsim/skills` is a registry of SootBean-flavored automation skills
363
+ (a11y-review, compat-check, perf-profile, screenshot-all, test-flow,
364
+ visual-diff) discoverable/matchable by trigger and loadable from a project
365
+ manifest. The published CLI registry and the generated website docs both come
366
+ from `packages/sootsim-skills/`:
186
367
 
187
368
  - `packages/sootsim/skills/soot.md` is generated from that registry
188
- - `src/features/site/docs/sootsim/cli/*` is generated output, not hand-edited source
369
+ - `src/features/site/docs/sootsim/cli/*` is generated output, not hand-edited
189
370
 
190
- when command docs or examples drift, update `packages/sootsim-skills/` and run:
371
+ When command docs or examples drift, update `packages/sootsim-skills/` and run:
191
372
 
192
373
  ```sh
193
374
  bun run generate:sootsim-docs
194
375
  ```
195
376
 
196
- ## usage in a framework
377
+ ## Runtime delivery (the sootsim home and CDN)
197
378
 
198
- ```ts
199
- // vite.config.ts
200
- import { sootsimPlugin } from 'sootsim/vite'
379
+ The engine never ships inside this package. `auto-bootstrap.ts` →
380
+ `runtime-delivery.ts` resolves the CDN origin (default `https://sootbean.com`,
381
+ overridable in `config.json`), fetches `runtimes/manifest.json` + the runtime
382
+ tarball, verifies its sha256, and unpacks it under `~/.sootsim`:
201
383
 
202
- export default {
203
- plugins: [sootsimPlugin()],
204
- }
205
384
  ```
206
-
207
- ```js
208
- // metro.config.js
209
- const { withSootsim } = require('sootsim/metro')
210
-
211
- module.exports = withSootsim({ /* your metro config */ })
385
+ ~/.sootsim/
386
+ ├── runtimes/
387
+ │ └── <version>/ unpacked engine assets (served at /__soot/)
388
+ ├── daemon.json lockfile: pid, ports, active runtime, heartbeat
389
+ └── config.json user prefs: update channel, cdn origin override
212
390
  ```
213
391
 
214
- ## development
392
+ The bridge daemon serves these assets over HTTP and exposes a self-update route;
393
+ `sootsim upgrade` / `sootsim runtime` drive explicit version changes from the
394
+ CLI side.
395
+
396
+ ## Development and building
215
397
 
216
- this package is part of the SootBean monorepo. to build the CLI bundle:
398
+ This package is part of the SootBean monorepo. To build the published CLI bundle:
217
399
 
218
400
  ```sh
219
401
  bun run build:cli
220
402
  ```
221
403
 
222
- the output is `dist-cli/bin.js`, a single esbuild output that gets published
223
- as the `sootsim` npm bin.
404
+ The output is `dist-cli/bin.js` a single esbuild output published as the
405
+ `sootsim` npm bin. Library exports build to `dist-lib/` (consumed via the
406
+ `exports` map: `.`, `./vite`, `./metro`, `./sdk`, `./skills`, `./detox`, and the
407
+ host/agent helpers). `bun run pack:smoke` validates the publishable tarball.
package/dist-cli/bin.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- /*! sootsim v0.1.135 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
3
- import{a as u,b as d,c as g}from"./chunks/chunk-5HFMOWG3.js";import"./chunks/chunk-45Z6VTEC.js";function f(a){let e=a instanceof Error?a.message:String(a);(/^command timed out after \d+s$/.test(e)||e.startsWith("sim disconnected:")||e.startsWith("bridge never reconnected")||e.startsWith("no sim connected with id ")||e.startsWith("could not connect to ws://")||e.startsWith("sootsim bridge daemon is not running")||e.startsWith("refusing to install the persistent sootsim daemon"))&&!process.env.SOOTSIM_VERBOSE&&(process.stderr.write(` ${e}
2
+ /*! sootsim v0.1.137 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
3
+ import{a as u,b as d,c as g}from"./chunks/chunk-VPS5IHVH.js";import"./chunks/chunk-VTZMWNB5.js";function f(a){let e=a instanceof Error?a.message:String(a);(/^command timed out after \d+s$/.test(e)||e.startsWith("sim disconnected:")||e.startsWith("bridge never reconnected")||e.startsWith("no sim connected with id ")||e.startsWith("could not connect to ws://")||e.startsWith("sootsim bridge daemon is not running")||e.startsWith("refusing to install the persistent sootsim daemon"))&&!process.env.SOOTSIM_VERBOSE&&(process.stderr.write(` ${e}
4
4
  `),process.exit(1)),a instanceof Error&&a.stack?process.stderr.write(`${a.stack}
5
5
  `):process.stderr.write(`${e}
6
6
  `),process.exit(1)}process.on("unhandledRejection",f);process.on("uncaughtException",f);var A=typeof __SOOTSIM_STANDALONE__<"u"&&__SOOTSIM_STANDALONE__,k=new Set(["preview","electron","install-desktop"]);function v(a){!A||!k.has(a)||(process.stderr.write(` sootsim ${a} isn't available in the standalone binary \u2014
7
7
  it needs vite / electron / playwright from a project's node_modules.
8
8
  install the npm package and run via bun or node instead:
9
9
  npm i -g sootsim && sootsim ${a}
10
- `),process.exit(1))}var t=g(process.argv);{let{trackCliEvent:a}=await import("./chunks/telemetry-2XPV6PVU.js");a({event:"cli_command_invoked",properties:{command:t.command||(t.version?"version":t.help?"help":"none"),arg_count:t.commandArgs.length,platform:process.platform,arch:process.arch,node_version:process.versions.node}})}async function c(a){let{flushCliTelemetry:e}=await import("./chunks/telemetry-2XPV6PVU.js");await e(),process.exit(a)}if(t.version){let{getCliVersion:a}=await import("./chunks/cli-version-EOI7UVCS.js"),{IS_BETA:e,BETA_LABEL:i}=await import("./chunks/beta-HNHNY7GJ.js"),m=e?` \xB7 ${i}`:"";console.log(`sootsim v${a()}${m}`);let{readActiveRuntime:h}=await import("./chunks/home-paths-KYTBCLPQ.js"),w=h();console.log(w?`runtime v${w}`:"runtime not installed"),await c(0)}if(t.help&&!t.command){let{printHelp:a}=await import("./chunks/help-RZ2M7P7U.js");a(),await c(0)}var o=t.globalFlags.port,n=t.verbose,p=t.globalFlags.device,l=t.globalFlags.theme,S=t.globalFlags.driver,E=t.globalFlags.headless===!0,b=t.globalFlags.sim??t.globalFlags.session??t.globalFlags.tab,s=b?["--sim",b,...t.commandArgs]:t.commandArgs;if(p||l){let{settingsStore:a}=await import("./chunks/store-WIQ4ZE4Z.js"),e={};p&&(e.deviceModel=p),l&&(e.colorScheme=l),a.apply(e)}if(!t.command&&t.commandArgs.length===0){let{runStart:a}=await import("./chunks/start-2T5QRVL2.js");await a([],{port:o}),await c(0)}var r=t.command??"";if(!r){if(t.commandArgs.length>0){let e=t.commandArgs.find(i=>!i.startsWith("-"))??t.commandArgs[0];console.error(` unknown command: ${e}`),console.error(" run `sootsim --help` to see the full surface."),await c(1)}let{printHelp:a}=await import("./chunks/help-RZ2M7P7U.js");a(),await c(0)}if(r in d){let a=d[r],e=t.commandArgs.length>0?` ${t.commandArgs.join(" ")}`:"";console.error(` \`sootsim ${r}\` was removed. use \`${a}\` instead.`),e&&console.error(` \u2192 ${a}${e}`),console.error(" run `sootsim --help` to see the new verb groups."),await c(1)}if(t.help||t.commandArgs.includes("--help")||t.commandArgs.includes("-h")){let a=r==="do"||r==="get"||r==="debug"||r==="shell"||r==="wait",e=t.commandArgs.find(i=>!i.startsWith("-"));if(r!=="shell")if(a&&!e){let{printGroupHelp:i}=await import("./chunks/help-RZ2M7P7U.js");i(r)&&await c(0)}else{let{printCommandHelp:i}=await import("./chunks/help-RZ2M7P7U.js");i(a&&e?e:r,{prefer:a&&e?"verb":"command",group:a&&e?r:void 0}),await c(0)}}v(r);if(u.has(r)){let{ensureSootsimReady:a,resolveBootstrapPort:e}=await import("./chunks/auto-bootstrap-PRFJ44RD.js"),i=e(s,o);await a(i);let{runInspect:m}=await import("./chunks/inspect-ZUYLUCRJ.js");await m([r,...s],{port:i,verbose:n})}else switch(r){case"assert":{let{runAssert:a}=await import("./chunks/assert-CZPAVBXB.js");await a(t.commandArgs);break}case"flow":{let{runFlow:a}=await import("./chunks/flow-PV4WV3DZ.js"),e=await a(s);process.exit(typeof e=="number"?e:0)}case"test":{let{runTest:a}=await import("./chunks/test-ZGUHV3XE.js");await a(t.commandArgs,{port:o,verbose:n});break}case"detox":{let{runDetox:a}=await import("./chunks/detox-TX4AUV2W.js");await a(t.commandArgs,{port:o,verbose:n});break}case"maestro":{let{runMaestro:a}=await import("./chunks/maestro-DUIODHYF.js"),e=await a(s,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"preview":{let{runPreview:a}=await import("./chunks/preview-LSVZXF7S.js");await a(t.commandArgs,{port:o,verbose:n});break}case"record":{let{runRecord:a}=await import("./chunks/record-LLNZNJSY.js");await a(s,{port:o,verbose:n});break}case"profile":{let{runProfile:a}=await import("./chunks/profile-2LUO56EX.js"),e=await a(t.commandArgs,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"cpu-profile":{let{runCpuProfile:a}=await import("./chunks/cpu-profile-2VSK3E45.js"),e=await a(s,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"react":{let{runReact:a}=await import("./chunks/react-KPNHGOQ3.js"),e=await a(s,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"screenshot":{let{runScreenshot:a}=await import("./chunks/screenshot-OEIH2ETO.js");await a(s,{port:o,verbose:n});break}case"screenshots":{let{runScreenshots:a}=await import("./chunks/screenshots-GCJW22B5.js"),e=await a(s,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"screenshot-mode":{let{runScreenshotMode:a}=await import("./chunks/screenshot-mode-3TDLGZPT.js");await a(t.commandArgs,{port:o,verbose:n});break}case"three-mode":case"3d-mode":{let{runThreeMode:a}=await import("./chunks/three-mode-3BUTKBTR.js");await a(t.commandArgs,{port:o,verbose:n});break}case"inspect":{let{runInspect:a}=await import("./chunks/inspect-ZUYLUCRJ.js");await a(s,{port:o,verbose:n});break}case"debug":{let{runDebug:a}=await import("./chunks/debug-Q3K4TXLC.js");await a(s,{port:o,verbose:n});break}case"timeline":{let{runTimeline:a}=await import("./chunks/timeline-WS2FXPG2.js");await a(s,{port:o,verbose:n});break}case"what-happened":{let{runWhatHappened:a}=await import("./chunks/what-happened-5A2RUC6V.js");await a(s,{port:o,verbose:n});break}case"diagnose":{let{runDiagnose:a}=await import("./chunks/diagnose-CGHRCXKF.js"),e=await a(s,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"open":{let{ensureSootsimReady:a,resolveBootstrapPort:e}=await import("./chunks/auto-bootstrap-PRFJ44RD.js"),i=e(s,o);await a(i);let{runOpenCommand:m}=await import("./chunks/control-GWQ44GVR.js");await m(s,{port:i});break}case"use":case"focus":{let{runUseCommand:a}=await import("./chunks/control-GWQ44GVR.js");await a(s,{port:o});break}case"claim":{let{runClaimCommand:a}=await import("./chunks/control-GWQ44GVR.js");await a(s,{port:o});break}case"close":{let{runCloseCommand:a}=await import("./chunks/control-GWQ44GVR.js");await a(s,{port:o});break}case"config":{let{runConfig:a}=await import("./chunks/config-JNO26BKX.js");await a(t.commandArgs);break}case"device":{let{runDeviceCommand:a}=await import("./chunks/device-E5RRT67Y.js");await a(s,{port:o});break}case"scan":case"compat":{let{runCompat:a}=await import("./chunks/compat-DZRWNMC2.js");await a(t.commandArgs);break}case"electron":{let{runElectron:a}=await import("./chunks/electron-YSY7HZBI.js");await a(t.commandArgs,{port:o,device:p,driver:S});break}case"login":{let{runLogin:a}=await import("./chunks/login-II2EWFHY.js");await a(t.commandArgs);break}case"logout":{let{runLogout:a}=await import("./chunks/logout-E52753SB.js");await a();break}case"whoami":{let{runWhoami:a}=await import("./chunks/whoami-ZVM5A34Z.js");await a();break}case"keys":{let{runKeys:a}=await import("./chunks/keys-QJDWORM5.js");await a(t.commandArgs);break}case"setup-repo":{let{runSetupRepo:a}=await import("./chunks/setup-repo-U7XMVCQA.js");await a(t.commandArgs);break}case"install":{let{runInstall:a}=await import("./chunks/install-XLJ4CJUN.js");await a(t.commandArgs);break}case"install-desktop":{let{runInstallDesktop:a}=await import("./chunks/install-desktop-HKLVDL7Q.js");await a(t.commandArgs);break}case"server":{let{runServer:a}=await import("./chunks/server-CBH2UYLT.js");await a(t.commandArgs,{port:o});break}case"daemon":{let{runDaemon:a}=await import("./chunks/daemon-QMFDDXFF.js");await a(t.commandArgs,{port:o});break}case"runtime":{let{runRuntime:a}=await import("./chunks/runtime-B3RQF5NL.js");await a(t.commandArgs);break}case"upgrade":{let{runUpgrade:a}=await import("./chunks/upgrade-FFTCNLCI.js");await a(t.commandArgs);break}case"version":{let{runVersion:a}=await import("./chunks/version-OQOFXNMZ.js");await a(t.commandArgs);break}case"launch":{let{runLaunch:a}=await import("./chunks/launch-CBIYGOV7.js");await a(t.commandArgs);break}case"start":{let{runStart:a}=await import("./chunks/start-2T5QRVL2.js");await a(t.commandArgs,{port:o});break}case"agent":{let{runAgentCommand:a}=await import("./chunks/agent-EV3LMTI2.js"),e=await a(t.commandArgs);process.exit(e)}case"agent-wrapper":{let{runAgentWrapper:a}=await import("./chunks/agent-wrapper-GOUAYJWS.js"),e=await a(t.commandArgs);process.exit(e)}case"skills":{let{runSkills:a}=await import("./chunks/skills-DVPCRUUO.js");await a(t.commandArgs);break}case"upload":{let{runUpload:a}=await import("./chunks/upload-JQMTDQMQ.js");await a(t.commandArgs,{port:o,verbose:n});break}case"app-fonts":{let{runAppFonts:a}=await import("./chunks/app-fonts-7ZGCVBWL.js");await a(t.commandArgs);break}case"hints":{let{runHints:a}=await import("./chunks/hints-V7M7GKXL.js");a(t.commandArgs);break}default:console.error(` unknown command: ${r}`),console.error(" run `sootsim --help` to see the full surface."),process.exit(1)}
10
+ `),process.exit(1))}var t=g(process.argv);{let{trackCliEvent:a}=await import("./chunks/telemetry-T3OM5YCG.js");a({event:"cli_command_invoked",properties:{command:t.command||(t.version?"version":t.help?"help":"none"),arg_count:t.commandArgs.length,platform:process.platform,arch:process.arch,node_version:process.versions.node}})}async function c(a){let{flushCliTelemetry:e}=await import("./chunks/telemetry-T3OM5YCG.js");await e(),process.exit(a)}if(t.version){let{getCliVersion:a}=await import("./chunks/cli-version-YLSCTD4T.js"),{IS_BETA:e,BETA_LABEL:i}=await import("./chunks/beta-7T5LPWKT.js"),m=e?` \xB7 ${i}`:"";console.log(`sootsim v${a()}${m}`);let{readActiveRuntime:h}=await import("./chunks/home-paths-O7V4JW2V.js"),w=h();console.log(w?`runtime v${w}`:"runtime not installed"),await c(0)}if(t.help&&!t.command){let{printHelp:a}=await import("./chunks/help-JQEAOFW4.js");a(),await c(0)}var o=t.globalFlags.port,n=t.verbose,p=t.globalFlags.device,l=t.globalFlags.theme,S=t.globalFlags.driver,E=t.globalFlags.headless===!0,b=t.globalFlags.sim??t.globalFlags.session??t.globalFlags.tab,s=b?["--sim",b,...t.commandArgs]:t.commandArgs;if(p||l){let{settingsStore:a}=await import("./chunks/store-5RD45SIG.js"),e={};p&&(e.deviceModel=p),l&&(e.colorScheme=l),a.apply(e)}if(!t.command&&t.commandArgs.length===0){let{runStart:a}=await import("./chunks/start-4I2VFTT7.js");await a([],{port:o}),await c(0)}var r=t.command??"";if(!r){if(t.commandArgs.length>0){let e=t.commandArgs.find(i=>!i.startsWith("-"))??t.commandArgs[0];console.error(` unknown command: ${e}`),console.error(" run `sootsim --help` to see the full surface."),await c(1)}let{printHelp:a}=await import("./chunks/help-JQEAOFW4.js");a(),await c(0)}if(r in d){let a=d[r],e=t.commandArgs.length>0?` ${t.commandArgs.join(" ")}`:"";console.error(` \`sootsim ${r}\` was removed. use \`${a}\` instead.`),e&&console.error(` \u2192 ${a}${e}`),console.error(" run `sootsim --help` to see the new verb groups."),await c(1)}if(t.help||t.commandArgs.includes("--help")||t.commandArgs.includes("-h")){let a=r==="do"||r==="get"||r==="debug"||r==="shell"||r==="wait",e=t.commandArgs.find(i=>!i.startsWith("-"));if(r!=="shell")if(a&&!e){let{printGroupHelp:i}=await import("./chunks/help-JQEAOFW4.js");i(r)&&await c(0)}else{let{printCommandHelp:i}=await import("./chunks/help-JQEAOFW4.js");i(a&&e?e:r,{prefer:a&&e?"verb":"command",group:a&&e?r:void 0}),await c(0)}}v(r);if(u.has(r)){let{ensureSootsimReady:a,resolveBootstrapPort:e}=await import("./chunks/auto-bootstrap-HJ6L7Y4E.js"),i=e(s,o);await a(i);let{runInspect:m}=await import("./chunks/inspect-T36ODJAN.js");await m([r,...s],{port:i,verbose:n})}else switch(r){case"assert":{let{runAssert:a}=await import("./chunks/assert-C7VKSGRY.js");await a(t.commandArgs);break}case"flow":{let{runFlow:a}=await import("./chunks/flow-N75N77WK.js"),e=await a(s);process.exit(typeof e=="number"?e:0)}case"test":{let{runTest:a}=await import("./chunks/test-NQ5O2ND4.js");await a(t.commandArgs,{port:o,verbose:n});break}case"detox":{let{runDetox:a}=await import("./chunks/detox-5KNUYOUK.js");await a(t.commandArgs,{port:o,verbose:n});break}case"maestro":{let{runMaestro:a}=await import("./chunks/maestro-7EWZQE7C.js"),e=await a(s,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"preview":{let{runPreview:a}=await import("./chunks/preview-6ADUV4HS.js");await a(t.commandArgs,{port:o,verbose:n});break}case"record":{let{runRecord:a}=await import("./chunks/record-6PYC73J5.js");await a(s,{port:o,verbose:n});break}case"profile":{let{runProfile:a}=await import("./chunks/profile-D634RPOU.js"),e=await a(t.commandArgs,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"cpu-profile":{let{runCpuProfile:a}=await import("./chunks/cpu-profile-7TARS6YI.js"),e=await a(s,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"react":{let{runReact:a}=await import("./chunks/react-CI2XOOKG.js"),e=await a(s,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"screenshot":{let{runScreenshot:a}=await import("./chunks/screenshot-DYLZI2UB.js");await a(s,{port:o,verbose:n});break}case"screenshots":{let{runScreenshots:a}=await import("./chunks/screenshots-UBYQLSGU.js"),e=await a(s,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"screenshot-mode":{let{runScreenshotMode:a}=await import("./chunks/screenshot-mode-WCJJZJ4I.js");await a(t.commandArgs,{port:o,verbose:n});break}case"three-mode":case"3d-mode":{let{runThreeMode:a}=await import("./chunks/three-mode-QKX5ZD3T.js");await a(t.commandArgs,{port:o,verbose:n});break}case"inspect":{let{runInspect:a}=await import("./chunks/inspect-T36ODJAN.js");await a(s,{port:o,verbose:n});break}case"debug":{let{runDebug:a}=await import("./chunks/debug-2IZSN6F3.js");await a(s,{port:o,verbose:n});break}case"timeline":{let{runTimeline:a}=await import("./chunks/timeline-HQGP3BWQ.js");await a(s,{port:o,verbose:n});break}case"what-happened":{let{runWhatHappened:a}=await import("./chunks/what-happened-YGHZIXUQ.js");await a(s,{port:o,verbose:n});break}case"diagnose":{let{runDiagnose:a}=await import("./chunks/diagnose-KQW7BDVO.js"),e=await a(s,{port:o,verbose:n});process.exit(typeof e=="number"?e:0)}case"open":{let{ensureSootsimReady:a,resolveBootstrapPort:e}=await import("./chunks/auto-bootstrap-HJ6L7Y4E.js"),i=e(s,o);await a(i);let{runOpenCommand:m}=await import("./chunks/control-V3LESW4B.js");await m(s,{port:i});break}case"use":case"focus":{let{runUseCommand:a}=await import("./chunks/control-V3LESW4B.js");await a(s,{port:o});break}case"claim":{let{runClaimCommand:a}=await import("./chunks/control-V3LESW4B.js");await a(s,{port:o});break}case"close":{let{runCloseCommand:a}=await import("./chunks/control-V3LESW4B.js");await a(s,{port:o});break}case"config":{let{runConfig:a}=await import("./chunks/config-3WQGW5NO.js");await a(t.commandArgs);break}case"device":{let{runDeviceCommand:a}=await import("./chunks/device-232QOTCZ.js");await a(s,{port:o});break}case"scan":case"compat":{let{runCompat:a}=await import("./chunks/compat-QI454ESE.js");await a(t.commandArgs);break}case"electron":{let{runElectron:a}=await import("./chunks/electron-O6ELQRHJ.js");await a(t.commandArgs,{port:o,device:p,driver:S});break}case"login":{let{runLogin:a}=await import("./chunks/login-VDH2M4OQ.js");await a(t.commandArgs);break}case"logout":{let{runLogout:a}=await import("./chunks/logout-H6NCIG4O.js");await a();break}case"whoami":{let{runWhoami:a}=await import("./chunks/whoami-6WPS2PJB.js");await a();break}case"keys":{let{runKeys:a}=await import("./chunks/keys-PRTEPMIU.js");await a(t.commandArgs);break}case"setup-repo":{let{runSetupRepo:a}=await import("./chunks/setup-repo-4BMV6H4N.js");await a(t.commandArgs);break}case"install":{let{runInstall:a}=await import("./chunks/install-LSIVGKDQ.js");await a(t.commandArgs);break}case"install-desktop":{let{runInstallDesktop:a}=await import("./chunks/install-desktop-O3QC6B7F.js");await a(t.commandArgs);break}case"server":{let{runServer:a}=await import("./chunks/server-INUPMADW.js");await a(t.commandArgs,{port:o});break}case"daemon":{let{runDaemon:a}=await import("./chunks/daemon-Y32JYZ4J.js");await a(t.commandArgs,{port:o});break}case"runtime":{let{runRuntime:a}=await import("./chunks/runtime-P32F4A7T.js");await a(t.commandArgs);break}case"upgrade":{let{runUpgrade:a}=await import("./chunks/upgrade-NJCLU3PX.js");await a(t.commandArgs);break}case"version":{let{runVersion:a}=await import("./chunks/version-R6C63OCK.js");await a(t.commandArgs);break}case"launch":{let{runLaunch:a}=await import("./chunks/launch-K4LIL5HE.js");await a(t.commandArgs);break}case"start":{let{runStart:a}=await import("./chunks/start-4I2VFTT7.js");await a(t.commandArgs,{port:o});break}case"agent":{let{runAgentCommand:a}=await import("./chunks/agent-BKFAIL4L.js"),e=await a(t.commandArgs);process.exit(e)}case"agent-wrapper":{let{runAgentWrapper:a}=await import("./chunks/agent-wrapper-3JFHSCJV.js"),e=await a(t.commandArgs);process.exit(e)}case"skills":{let{runSkills:a}=await import("./chunks/skills-FLIZVEDL.js");await a(t.commandArgs);break}case"upload":{let{runUpload:a}=await import("./chunks/upload-WQNOKGN6.js");await a(t.commandArgs,{port:o,verbose:n});break}case"app-fonts":{let{runAppFonts:a}=await import("./chunks/app-fonts-TTXUEYKI.js");await a(t.commandArgs);break}case"hints":{let{runHints:a}=await import("./chunks/hints-ZJSHULYA.js");a(t.commandArgs);break}default:console.error(` unknown command: ${r}`),console.error(" run `sootsim --help` to see the full surface."),process.exit(1)}
@@ -1,5 +1,5 @@
1
- /*! sootsim v0.1.135 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a as E,b as A,c as S,d as l,e as j,g as T,h as I,j as D,m as k,n as x,o as O,p as g,q as R,r as C}from"./chunk-UOBTAOWL.js";import"./chunk-U6TZCGUB.js";import{c as b}from"./chunk-ASXKBNRL.js";import"./chunk-CYELNMX7.js";import"./chunk-PWRO2WPW.js";import"./chunk-TVCTN2EF.js";import"./chunk-XOK53FNY.js";import"./chunk-GBBGAIOO.js";import"./chunk-IJU5JVF2.js";import"./chunk-4PJFJ77U.js";import{h as y,i as $}from"./chunk-UM7EEN26.js";import"./chunk-45Z6VTEC.js";import w from"node:fs";import d from"node:path";import{spawn as F}from"node:child_process";import B from"node:net";import{WebSocket as M}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 M(`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 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!==M.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 L(n=7668,e=400){return new Promise(t=>{let r=new B.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 W(n={}){let e=n.port??7668;if(await L(e))return{alreadyRunning:!0};if($()){let a=y()?"this is a dev workstation (IS_TAMAGUI_DEV=1)":"this is a dev checkout";throw new c(`no sootsim bridge on port ${e} and refusing to spawn a standalone daemon \u2014 ${a}. start the live \`bun dev\` stack so the bridge is up, or set SOOTSIM_FORCE_DAEMON_INSTALL=1 to spawn one here on purpose.`,"DEV_HOST_REFUSED")}let{cmd:t,prefixArgs:r}=C(),s=[...r,"server","--quiet"];e!==7668&&s.push("--port",String(e));let o=F(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 L(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 server\` manually to diagnose.`,"SPAWN_TIMEOUT")}async function h(n={}){await W({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 fe(n){let[e,...t]=n;try{switch(e){case void 0:case"--help":case"-h":case"help":return _(),0;case"attach":return await V(t);case"projects":return await J();case"project":return await G(t);case"sessions":return await H(t);case"start":return await q(t);case"prompt":return await K(t);case"watch":return await X(t);case"transcript":return await Q(t);case"end":return await Y(t);case"paths":return await Z();default:return process.stderr.write(`unknown agent subcommand: ${e}
1
+ /*! sootsim v0.1.137 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{a as E,b as A,c as S,d as l,e as j,g as T,h as I,j as D,m as k,n as x,o as O,p as g,q as R,r as C}from"./chunk-ANGFHMMH.js";import"./chunk-S743VRVC.js";import{c as b}from"./chunk-IBFKK5VG.js";import"./chunk-RRJJOP5X.js";import"./chunk-ZV2GNDOE.js";import"./chunk-QOFYAH6W.js";import"./chunk-XZI43TP4.js";import"./chunk-U3UNJQLS.js";import"./chunk-RSKKY2YX.js";import"./chunk-Q4G7RPYT.js";import{h as y,i as $}from"./chunk-WYDF3HWY.js";import"./chunk-VTZMWNB5.js";import w from"node:fs";import d from"node:path";import{spawn as F}from"node:child_process";import B from"node:net";import{WebSocket as M}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 M(`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 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!==M.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 L(n=7668,e=400){return new Promise(t=>{let r=new B.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 W(n={}){let e=n.port??7668;if(await L(e))return{alreadyRunning:!0};if($()){let a=y()?"this is a dev workstation (IS_TAMAGUI_DEV=1)":"this is a dev checkout";throw new c(`no sootsim bridge on port ${e} and refusing to spawn a standalone daemon \u2014 ${a}. start the live \`bun dev\` stack so the bridge is up, or set SOOTSIM_FORCE_DAEMON_INSTALL=1 to spawn one here on purpose.`,"DEV_HOST_REFUSED")}let{cmd:t,prefixArgs:r}=C(),s=[...r,"server","--quiet"];e!==7668&&s.push("--port",String(e));let o=F(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 L(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 server\` manually to diagnose.`,"SPAWN_TIMEOUT")}async function h(n={}){await W({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 fe(n){let[e,...t]=n;try{switch(e){case void 0:case"--help":case"-h":case"help":return _(),0;case"attach":return await V(t);case"projects":return await J();case"project":return await G(t);case"sessions":return await H(t);case"start":return await q(t);case"prompt":return await K(t);case"watch":return await X(t);case"transcript":return await Q(t);case"end":return await Y(t);case"paths":return await Z();default:return process.stderr.write(`unknown agent subcommand: ${e}
3
3
  `),_(),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 _(){b("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 V(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.135 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{b as M}from"./chunk-U6TZCGUB.js";import"./chunk-45Z6VTEC.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.137 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{b as M}from"./chunk-S743VRVC.js";import"./chunk-VTZMWNB5.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>
@@ -1,2 +1,2 @@
1
- /*! sootsim v0.1.135 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{d as p}from"./chunk-OC5UQFLJ.js";import"./chunk-45Z6VTEC.js";import{existsSync as h,mkdirSync as d,readFileSync as y,writeFileSync as g}from"node:fs";import{join as m}from"node:path";import{gzipSync as w}from"node:zlib";function f(t,e){let o=t.indexOf(`--${e}`);if(o!==-1&&o+1<t.length)return t[o+1];let i=t.find(r=>r.startsWith(`--${e}=`));return i?i.slice(e.length+3):void 0}async function S(t){let e=f(t,"wire")||"",o=f(t,"out-dir"),i=f(t,"manifest");if((!o||!i)&&(console.error(" usage: sootsim app-fonts stage --wire <wire> --out-dir <dir> --manifest <path>"),process.exit(1)),!e.trim()){console.log("no app fonts declared \u2014 nothing to stage");return}d(o,{recursive:!0});let r=await p(e,{onStaged:({url:n,byteLength:s})=>console.log(` staged app font: ${n} (${(s/1024).toFixed(1)} KiB)`),onError:(n,s)=>console.error(` warning: failed to fetch app font ${n}: ${s instanceof Error?s.message:s}`)}),a=[];for(let n of r){let s=w(n.bytes);g(m(o,n.urlhash),s),a.push({url:n.url,urlhash:n.urlhash,contentType:n.contentType,encoding:"gzip",sizeBytes:s.length,rawBytes:n.bytes.byteLength})}let c=[];if(h(i))try{let n=JSON.parse(y(i,"utf8"));Array.isArray(n)&&(c=n)}catch{}let u=new Set(c.map(n=>n.urlhash)),l=[...c,...a.filter(n=>!u.has(n.urlhash))];g(i,JSON.stringify(l)),console.log(`staged ${a.length} app font(s); manifest now has ${l.length} file(s)`)}async function E(t){let e=t[0];if(e==="stage"){await S(t.slice(1));return}console.error(` unknown app-fonts subcommand: ${e??"(none)"}`),console.error(" available: stage"),process.exit(1)}export{E as runAppFonts};
1
+ /*! sootsim v0.1.137 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ import{d as p}from"./chunk-JZLPETG5.js";import"./chunk-VTZMWNB5.js";import{existsSync as h,mkdirSync as d,readFileSync as y,writeFileSync as g}from"node:fs";import{join as m}from"node:path";import{gzipSync as w}from"node:zlib";function f(t,e){let o=t.indexOf(`--${e}`);if(o!==-1&&o+1<t.length)return t[o+1];let i=t.find(r=>r.startsWith(`--${e}=`));return i?i.slice(e.length+3):void 0}async function S(t){let e=f(t,"wire")||"",o=f(t,"out-dir"),i=f(t,"manifest");if((!o||!i)&&(console.error(" usage: sootsim app-fonts stage --wire <wire> --out-dir <dir> --manifest <path>"),process.exit(1)),!e.trim()){console.log("no app fonts declared \u2014 nothing to stage");return}d(o,{recursive:!0});let r=await p(e,{onStaged:({url:n,byteLength:s})=>console.log(` staged app font: ${n} (${(s/1024).toFixed(1)} KiB)`),onError:(n,s)=>console.error(` warning: failed to fetch app font ${n}: ${s instanceof Error?s.message:s}`)}),a=[];for(let n of r){let s=w(n.bytes);g(m(o,n.urlhash),s),a.push({url:n.url,urlhash:n.urlhash,contentType:n.contentType,encoding:"gzip",sizeBytes:s.length,rawBytes:n.bytes.byteLength})}let c=[];if(h(i))try{let n=JSON.parse(y(i,"utf8"));Array.isArray(n)&&(c=n)}catch{}let u=new Set(c.map(n=>n.urlhash)),l=[...c,...a.filter(n=>!u.has(n.urlhash))];g(i,JSON.stringify(l)),console.log(`staged ${a.length} app font(s); manifest now has ${l.length} file(s)`)}async function E(t){let e=t[0];if(e==="stage"){await S(t.slice(1));return}console.error(` unknown app-fonts subcommand: ${e??"(none)"}`),console.error(" available: stage"),process.exit(1)}export{E as runAppFonts};