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.
- package/README.md +275 -91
- package/dist-cli/bin.js +3 -3
- package/dist-cli/chunks/{agent-EV3LMTI2.js → agent-BKFAIL4L.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-GOUAYJWS.js → agent-wrapper-3JFHSCJV.js} +2 -2
- package/dist-cli/chunks/{app-fonts-7ZGCVBWL.js → app-fonts-TTXUEYKI.js} +2 -2
- package/dist-cli/chunks/{assert-CZPAVBXB.js → assert-C7VKSGRY.js} +2 -2
- package/dist-cli/chunks/auto-bootstrap-HJ6L7Y4E.js +2 -0
- package/dist-cli/chunks/beta-7T5LPWKT.js +2 -0
- package/dist-cli/chunks/{chunk-G7W4URXX.js → chunk-63HEFSKV.js} +2 -2
- package/dist-cli/chunks/{chunk-QDGNAEBR.js → chunk-75DY2QLM.js} +2 -2
- package/dist-cli/chunks/{chunk-2VUPH4SQ.js → chunk-7PCYCERP.js} +1 -1
- package/dist-cli/chunks/{chunk-UOBTAOWL.js → chunk-ANGFHMMH.js} +2 -2
- package/dist-cli/chunks/{chunk-EKZJFBGU.js → chunk-BEJTLGKN.js} +2 -2
- package/dist-cli/chunks/{chunk-ZFDBEITH.js → chunk-BKV7FUWW.js} +3 -3
- package/dist-cli/chunks/{chunk-QDRAULAC.js → chunk-CA6CJRKB.js} +2 -2
- package/dist-cli/chunks/{chunk-UNM45I7B.js → chunk-CAUZR5SF.js} +2 -2
- package/dist-cli/chunks/{chunk-YQM3RWNV.js → chunk-CCRTUMOG.js} +2 -2
- package/dist-cli/chunks/{chunk-PVWYEMAR.js → chunk-CO4QNRZH.js} +1 -1
- package/dist-cli/chunks/{chunk-56LSJ5RZ.js → chunk-CQKGKXOK.js} +2 -2
- package/dist-cli/chunks/{chunk-QLXTTGJD.js → chunk-E3LM63GU.js} +2 -2
- package/dist-cli/chunks/{chunk-5PB5X45Q.js → chunk-E7NOG434.js} +2 -2
- package/dist-cli/chunks/{chunk-7WF23EDW.js → chunk-I34PNOL4.js} +2 -2
- package/dist-cli/chunks/{chunk-ASXKBNRL.js → chunk-IBFKK5VG.js} +2 -2
- package/dist-cli/chunks/{chunk-XAVQLMY7.js → chunk-J7HWHGGQ.js} +2 -2
- package/dist-cli/chunks/{chunk-OC5UQFLJ.js → chunk-JZLPETG5.js} +1 -1
- package/dist-cli/chunks/{chunk-CLO4G4ZK.js → chunk-K6AINLM6.js} +1 -1
- package/dist-cli/chunks/{chunk-2RQ5M6MJ.js → chunk-LLIJHSV3.js} +2 -2
- package/dist-cli/chunks/{chunk-TXDUBB6W.js → chunk-LVEXGHOK.js} +2 -2
- package/dist-cli/chunks/chunk-MD6MPZOP.js +1 -0
- package/dist-cli/chunks/chunk-NJMSKW6O.js +1 -0
- package/dist-cli/chunks/{chunk-5JSVHUKF.js → chunk-OICALYNN.js} +3 -3
- package/dist-cli/chunks/{chunk-XMZJQZFB.js → chunk-PKR7EICH.js} +2 -2
- package/dist-cli/chunks/{chunk-7TWZYT4A.js → chunk-PRSSYJLK.js} +1 -1
- package/dist-cli/chunks/{chunk-4PJFJ77U.js → chunk-Q4G7RPYT.js} +1 -1
- package/dist-cli/chunks/chunk-QOFYAH6W.js +2 -0
- package/dist-cli/chunks/{chunk-MLNENEA4.js → chunk-QPVFBWCA.js} +1 -1
- package/dist-cli/chunks/{chunk-AQ6QT2DY.js → chunk-RHG2LVLG.js} +2 -2
- package/dist-cli/chunks/{chunk-MLVYS3AE.js → chunk-RKMZY5E3.js} +2 -2
- package/dist-cli/chunks/{chunk-DNF7AILW.js → chunk-RPEGHZHO.js} +2 -2
- package/dist-cli/chunks/{chunk-CYELNMX7.js → chunk-RRJJOP5X.js} +2 -2
- package/dist-cli/chunks/{chunk-IJU5JVF2.js → chunk-RSKKY2YX.js} +1 -1
- package/dist-cli/chunks/{chunk-U6TZCGUB.js → chunk-S743VRVC.js} +1 -1
- package/dist-cli/chunks/{chunk-GBBGAIOO.js → chunk-U3UNJQLS.js} +1 -1
- package/dist-cli/chunks/{chunk-Z5WZKI7J.js → chunk-UIIVIVVT.js} +2 -2
- package/dist-cli/chunks/{chunk-5HFMOWG3.js → chunk-VPS5IHVH.js} +1 -1
- package/dist-cli/chunks/{chunk-OODLGJLZ.js → chunk-VTN2ZZQK.js} +2 -2
- package/dist-cli/chunks/{chunk-45Z6VTEC.js → chunk-VTZMWNB5.js} +1 -1
- package/dist-cli/chunks/{chunk-UM7EEN26.js → chunk-WYDF3HWY.js} +1 -1
- package/dist-cli/chunks/{chunk-QMUNA6VQ.js → chunk-XYXDJB37.js} +3 -3
- package/dist-cli/chunks/{chunk-XOK53FNY.js → chunk-XZI43TP4.js} +1 -1
- package/dist-cli/chunks/chunk-ZBTQ5NUM.js +1 -0
- package/dist-cli/chunks/{chunk-PWRO2WPW.js → chunk-ZV2GNDOE.js} +1 -1
- package/dist-cli/chunks/{chunk-S5ZKCSJZ.js → chunk-ZWCYWRHB.js} +1 -1
- package/dist-cli/chunks/cli-version-YLSCTD4T.js +2 -0
- package/dist-cli/chunks/{compat-DZRWNMC2.js → compat-QI454ESE.js} +3 -3
- package/dist-cli/chunks/{config-JNO26BKX.js → config-3WQGW5NO.js} +2 -2
- package/dist-cli/chunks/{control-GWQ44GVR.js → control-V3LESW4B.js} +2 -2
- package/dist-cli/chunks/{cpu-profile-2VSK3E45.js → cpu-profile-7TARS6YI.js} +2 -2
- package/dist-cli/chunks/{daemon-QMFDDXFF.js → daemon-Y32JYZ4J.js} +2 -2
- package/dist-cli/chunks/{debug-Q3K4TXLC.js → debug-2IZSN6F3.js} +3 -3
- package/dist-cli/chunks/demo-app-registry-2IQUP2VH.js +2 -0
- package/dist-cli/chunks/{detox-TX4AUV2W.js → detox-5KNUYOUK.js} +2 -2
- package/dist-cli/chunks/{device-E5RRT67Y.js → device-232QOTCZ.js} +2 -2
- package/dist-cli/chunks/{diagnose-CGHRCXKF.js → diagnose-KQW7BDVO.js} +2 -2
- package/dist-cli/chunks/drivers-HJS44U4X.js +2 -0
- package/dist-cli/chunks/{electron-YSY7HZBI.js → electron-O6ELQRHJ.js} +3 -3
- package/dist-cli/chunks/flow-N75N77WK.js +2 -0
- package/dist-cli/chunks/help-JQEAOFW4.js +2 -0
- package/dist-cli/chunks/{hints-V7M7GKXL.js → hints-ZJSHULYA.js} +2 -2
- package/dist-cli/chunks/{home-paths-KYTBCLPQ.js → home-paths-O7V4JW2V.js} +2 -2
- package/dist-cli/chunks/{inspect-ZUYLUCRJ.js → inspect-T36ODJAN.js} +4 -4
- package/dist-cli/chunks/install-LSIVGKDQ.js +2 -0
- package/dist-cli/chunks/{install-desktop-HKLVDL7Q.js → install-desktop-O3QC6B7F.js} +3 -3
- package/dist-cli/chunks/{keys-QJDWORM5.js → keys-PRTEPMIU.js} +2 -2
- package/dist-cli/chunks/{launch-CBIYGOV7.js → launch-K4LIL5HE.js} +3 -3
- package/dist-cli/chunks/{login-II2EWFHY.js → login-VDH2M4OQ.js} +4 -4
- package/dist-cli/chunks/{logout-E52753SB.js → logout-H6NCIG4O.js} +2 -2
- package/dist-cli/chunks/{maestro-DUIODHYF.js → maestro-7EWZQE7C.js} +2 -2
- package/dist-cli/chunks/{preview-LSVZXF7S.js → preview-6ADUV4HS.js} +2 -2
- package/dist-cli/chunks/{profile-2LUO56EX.js → profile-D634RPOU.js} +2 -2
- package/dist-cli/chunks/{react-KPNHGOQ3.js → react-CI2XOOKG.js} +2 -2
- package/dist-cli/chunks/{record-LLNZNJSY.js → record-6PYC73J5.js} +2 -2
- package/dist-cli/chunks/runtime-P32F4A7T.js +2 -0
- package/dist-cli/chunks/{runtime-delivery-A2PZCEPU.js → runtime-delivery-O2LM35Z3.js} +2 -2
- package/dist-cli/chunks/{screenshot-OEIH2ETO.js → screenshot-DYLZI2UB.js} +2 -2
- package/dist-cli/chunks/{screenshot-mode-3TDLGZPT.js → screenshot-mode-WCJJZJ4I.js} +2 -2
- package/dist-cli/chunks/{screenshots-GCJW22B5.js → screenshots-UBYQLSGU.js} +2 -2
- package/dist-cli/chunks/{server-CBH2UYLT.js → server-INUPMADW.js} +3 -3
- package/dist-cli/chunks/setup-repo-4BMV6H4N.js +2 -0
- package/dist-cli/chunks/{skills-DVPCRUUO.js → skills-FLIZVEDL.js} +2 -2
- package/dist-cli/chunks/{start-2T5QRVL2.js → start-4I2VFTT7.js} +4 -4
- package/dist-cli/chunks/store-5RD45SIG.js +2 -0
- package/dist-cli/chunks/telemetry-T3OM5YCG.js +2 -0
- package/dist-cli/chunks/{test-ZGUHV3XE.js → test-NQ5O2ND4.js} +3 -3
- package/dist-cli/chunks/{three-mode-3BUTKBTR.js → three-mode-QKX5ZD3T.js} +2 -2
- package/dist-cli/chunks/{timeline-WS2FXPG2.js → timeline-HQGP3BWQ.js} +2 -2
- package/dist-cli/chunks/{upgrade-FFTCNLCI.js → upgrade-NJCLU3PX.js} +2 -2
- package/dist-cli/chunks/upload-WQNOKGN6.js +2 -0
- package/dist-cli/chunks/{version-OQOFXNMZ.js → version-R6C63OCK.js} +2 -2
- package/dist-cli/chunks/{web-Y2ZMUXP4.js → web-ISRXHOOS.js} +2 -2
- package/dist-cli/chunks/{what-happened-5A2RUC6V.js → what-happened-YGHZIXUQ.js} +2 -2
- package/dist-cli/chunks/{whoami-ZVM5A34Z.js → whoami-6WPS2PJB.js} +2 -2
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +1 -1
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +1 -1
- package/dist-lib/metro.cjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/demo-app-registry.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +1 -1
- package/dist-lib/vite.cjs +1 -1
- package/package.json +1 -1
- package/src/native-seam-manifest.ts +1 -1
- package/dist-cli/chunks/auto-bootstrap-PRFJ44RD.js +0 -2
- package/dist-cli/chunks/beta-HNHNY7GJ.js +0 -2
- package/dist-cli/chunks/chunk-GGLJLJ7P.js +0 -1
- package/dist-cli/chunks/chunk-TVCTN2EF.js +0 -2
- package/dist-cli/chunks/chunk-VH4VPAMR.js +0 -1
- package/dist-cli/chunks/chunk-ZANDDBFJ.js +0 -1
- package/dist-cli/chunks/cli-version-EOI7UVCS.js +0 -2
- package/dist-cli/chunks/demo-app-registry-LDTURANL.js +0 -2
- package/dist-cli/chunks/drivers-WF5OT6EA.js +0 -2
- package/dist-cli/chunks/flow-PV4WV3DZ.js +0 -2
- package/dist-cli/chunks/help-RZ2M7P7U.js +0 -2
- package/dist-cli/chunks/install-XLJ4CJUN.js +0 -2
- package/dist-cli/chunks/runtime-B3RQF5NL.js +0 -2
- package/dist-cli/chunks/setup-repo-U7XMVCQA.js +0 -2
- package/dist-cli/chunks/store-WIQ4ZE4Z.js +0 -2
- package/dist-cli/chunks/telemetry-2XPV6PVU.js +0 -2
- package/dist-cli/chunks/upload-JQMTDQMQ.js +0 -2
package/README.md
CHANGED
|
@@ -1,32 +1,192 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
CLI
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
270
|
+
See `../../docs/migrating-from-detox.md` for details.
|
|
90
271
|
|
|
91
|
-
##
|
|
272
|
+
## Coming from Maestro
|
|
92
273
|
|
|
93
|
-
SootSim's flow runner natively speaks
|
|
94
|
-
|
|
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
|
-
|
|
103
|
-
`scrollUntilVisible`, `launchApp`, `when:`, `repeat`, `runFlow`, …).
|
|
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
|
-
|
|
288
|
+
## Screenshots and recordings
|
|
107
289
|
|
|
108
|
-
|
|
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
|
|
123
|
-
|
|
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
|
-
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
|
360
|
+
## Skills registry
|
|
183
361
|
|
|
184
|
-
|
|
185
|
-
|
|
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
|
|
369
|
+
- `src/features/site/docs/sootsim/cli/*` is generated output, not hand-edited
|
|
189
370
|
|
|
190
|
-
|
|
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
|
-
##
|
|
377
|
+
## Runtime delivery (the sootsim home and CDN)
|
|
197
378
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
223
|
-
|
|
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.
|
|
3
|
-
import{a as u,b as d,c as g}from"./chunks/chunk-
|
|
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-
|
|
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.
|
|
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-
|
|
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.
|
|
2
|
-
import{b as M}from"./chunk-
|
|
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.
|
|
2
|
-
import{d as p}from"./chunk-
|
|
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};
|