sootsim 0.1.227 → 0.1.229
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 +14 -14
- package/detox/element-types.ts +3 -3
- package/detox/expectations.ts +29 -24
- package/detox/gestures.ts +16 -16
- package/detox/index.ts +34 -32
- package/dist-cli/bin.js +4 -4
- package/dist-cli/chunks/{agent-HBVXCRJT.js → agent-O26YYN6Z.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-X376DPV2.js → agent-wrapper-GSMHXP6S.js} +2 -2
- package/dist-cli/chunks/{app-fonts-LIQTD5RP.js → app-fonts-TVTDVD7S.js} +2 -2
- package/dist-cli/chunks/{assert-X6PPLN72.js → assert-KEWRJSST.js} +2 -2
- package/dist-cli/chunks/{auth-INRWIM2C.js → auth-KQXLD6AC.js} +2 -2
- package/dist-cli/chunks/beta-BXBII74U.js +2 -0
- package/dist-cli/chunks/{camera-LGFHYCJ5.js → camera-RTNVNBMT.js} +2 -2
- package/dist-cli/chunks/{chunk-YB2LM6VB.js → chunk-32O3YKA2.js} +6 -6
- package/dist-cli/chunks/{chunk-SBIAPF6E.js → chunk-3FGXJMSF.js} +1 -1
- package/dist-cli/chunks/{chunk-QWRYD2QV.js → chunk-3FYUTNM4.js} +18 -18
- package/dist-cli/chunks/{chunk-A4BOYB55.js → chunk-3UE5OA64.js} +2 -2
- package/dist-cli/chunks/{chunk-LCJSTEIR.js → chunk-456MZ4LU.js} +2 -2
- package/dist-cli/chunks/{chunk-E75EBS77.js → chunk-4O73AHYA.js} +2 -2
- package/dist-cli/chunks/chunk-4WAZOAAS.js +2 -0
- package/dist-cli/chunks/{chunk-RNWJQ4UR.js → chunk-62GPXYH7.js} +1 -1
- package/dist-cli/chunks/chunk-6Q3M6FMW.js +24 -0
- package/dist-cli/chunks/chunk-6VQIZJLG.js +1 -0
- package/dist-cli/chunks/chunk-7FSM4I7A.js +1 -0
- package/dist-cli/chunks/{chunk-J2JAOIMN.js → chunk-7IBNJPSB.js} +2 -2
- package/dist-cli/chunks/{chunk-GUCFICA4.js → chunk-7LSM7YNF.js} +1 -1
- package/dist-cli/chunks/{chunk-J7IFVTWX.js → chunk-AB7XCG3A.js} +1 -1
- package/dist-cli/chunks/{chunk-MG6JE3AM.js → chunk-AZWH6YSL.js} +2 -2
- package/dist-cli/chunks/{chunk-ESK7B2BQ.js → chunk-BZOTL2IQ.js} +6 -6
- package/dist-cli/chunks/{chunk-2XXPF6LP.js → chunk-CE3XI55C.js} +2 -2
- package/dist-cli/chunks/{chunk-HRYKZ5YY.js → chunk-DXF6DHIR.js} +5 -5
- package/dist-cli/chunks/{chunk-DB3GMVOH.js → chunk-FF7GZMWQ.js} +2 -2
- package/dist-cli/chunks/chunk-GVOFX6LJ.js +118 -0
- package/dist-cli/chunks/{chunk-HXHB5A6R.js → chunk-HKBDDBKZ.js} +2 -2
- package/dist-cli/chunks/{chunk-UGWURWHN.js → chunk-HKZICBKI.js} +2 -2
- package/dist-cli/chunks/{chunk-E3DLUOFK.js → chunk-JK4AGL2U.js} +7 -7
- package/dist-cli/chunks/{chunk-NIN75M5H.js → chunk-OUJVQUX4.js} +2 -2
- package/dist-cli/chunks/chunk-OWSZCLYZ.js +2 -0
- package/dist-cli/chunks/{chunk-XESYTWFM.js → chunk-P3ORPGKZ.js} +29 -29
- package/dist-cli/chunks/{chunk-Q4FB222M.js → chunk-PNLTVAPK.js} +3 -3
- package/dist-cli/chunks/{chunk-4J7BIXEB.js → chunk-PY65YXHI.js} +2 -2
- package/dist-cli/chunks/{chunk-7LDHOQ7B.js → chunk-S4CJUMUZ.js} +34 -34
- package/dist-cli/chunks/{chunk-T6MEERRI.js → chunk-SCGYOJJP.js} +2 -2
- package/dist-cli/chunks/{chunk-HXSV263V.js → chunk-SHBXXKJW.js} +1 -1
- package/dist-cli/chunks/chunk-SPBHQVJ4.js +2 -0
- package/dist-cli/chunks/{chunk-F53C5HAB.js → chunk-SYZKYAPC.js} +1 -1
- package/dist-cli/chunks/{chunk-3L5EXWCP.js → chunk-THDVL3ST.js} +1 -1
- package/dist-cli/chunks/{chunk-HTVB3FW2.js → chunk-TJXRSDEU.js} +2 -2
- package/dist-cli/chunks/{chunk-NU755HCE.js → chunk-TWUPMUWQ.js} +1 -1
- package/dist-cli/chunks/{chunk-F5HOYUD2.js → chunk-U4OPCVAL.js} +1 -1
- package/dist-cli/chunks/chunk-UCVK6JDW.js +3 -0
- package/dist-cli/chunks/chunk-UGFZIHBA.js +2 -0
- package/dist-cli/chunks/{chunk-LM544J6V.js → chunk-V2TABN7W.js} +1 -1
- package/dist-cli/chunks/{chunk-D42IRY2Y.js → chunk-VKVRAWJR.js} +2 -2
- package/dist-cli/chunks/{chunk-7L5IKIHC.js → chunk-VUJ5FLL7.js} +1 -1
- package/dist-cli/chunks/{chunk-2BQOZATN.js → chunk-WRPO6J3Z.js} +3 -3
- package/dist-cli/chunks/chunk-XQHCHVHE.js +4 -0
- package/dist-cli/chunks/{chunk-NHE3YHDU.js → chunk-ZORXBO54.js} +2 -2
- package/dist-cli/chunks/cli-version-W42GGXV7.js +2 -0
- package/dist-cli/chunks/{compat-MH673TEJ.js → compat-HPS6RGWD.js} +3 -3
- package/dist-cli/chunks/{config-SSCDV5ZJ.js → config-EKIT7WKS.js} +2 -2
- package/dist-cli/chunks/{control-54R2XDLV.js → control-QFNWOCLW.js} +2 -2
- package/dist-cli/chunks/daemon-B6RUIEYN.js +2 -0
- package/dist-cli/chunks/{debug-PFP646II.js → debug-EU5JDWZR.js} +3 -3
- package/dist-cli/chunks/{desktop-BPYYJ3EM.js → desktop-OOCDJB3I.js} +3 -3
- package/dist-cli/chunks/{detox-BIUVHXYH.js → detox-BXXEPJ3F.js} +2 -2
- package/dist-cli/chunks/{device-BNSPNIP6.js → device-6B6NKGAW.js} +2 -2
- package/dist-cli/chunks/{diagnose-BPTDSGOB.js → diagnose-FOWBMKO6.js} +2 -2
- package/dist-cli/chunks/drivers-DC6SRA2P.js +2 -0
- package/dist-cli/chunks/{film-ILA6VSOG.js → film-MVJCHWBU.js} +3 -3
- package/dist-cli/chunks/flow-XO4D3LRA.js +2 -0
- package/dist-cli/chunks/help-WNPSM6ZU.js +2 -0
- package/dist-cli/chunks/{hidden-runtime-alias-TD7IVAPF.js → hidden-runtime-alias-OVGHDXOR.js} +2 -2
- package/dist-cli/chunks/{home-paths-H36JDTNO.js → home-paths-3W46OKY6.js} +2 -2
- package/dist-cli/chunks/inspect-4J5NJCXS.js +2 -0
- package/dist-cli/chunks/install-desktop-GAEFTQ2K.js +2 -0
- package/dist-cli/chunks/{login-5WGZX2ZD.js → login-CU72MJHE.js} +4 -4
- package/dist-cli/chunks/{logout-V53WTVIZ.js → logout-SF5EPW7M.js} +2 -2
- package/dist-cli/chunks/{maestro-HGKYRD54.js → maestro-NYFSOD2H.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-6DNZSKDP.js → maestro-generate-VDAXEVCE.js} +3 -3
- package/dist-cli/chunks/{mode-YFMKORE5.js → mode-VSABDCU5.js} +2 -2
- package/dist-cli/chunks/optional-demo-registry-YQZA2MRD.js +2 -0
- package/dist-cli/chunks/{perf-EOIGFDWM.js → perf-5XMCN7A2.js} +2 -2
- package/dist-cli/chunks/{record-2F54QS6Y.js → record-NAYAEQPD.js} +10 -10
- package/dist-cli/chunks/runtime-LR63M7XX.js +2 -0
- package/dist-cli/chunks/screenshot-command-HFNYL2CY.js +151 -0
- package/dist-cli/chunks/{screenshots-capture-DIICN24R.js → screenshots-capture-JCBFMJFS.js} +3 -3
- package/dist-cli/chunks/{serve-66NC6PPZ.js → serve-Z5HVGJ3C.js} +5 -5
- package/dist-cli/chunks/{setup-ZYN4BTBR.js → setup-YKDBK3RX.js} +3 -3
- package/dist-cli/chunks/{skills-MDBIER7U.js → skills-DQOWHZBC.js} +2 -2
- package/dist-cli/chunks/{storage-AQ3SEO4Y.js → storage-TKTKWTWE.js} +2 -2
- package/dist-cli/chunks/store-FPAX23SK.js +2 -0
- package/dist-cli/chunks/telemetry-CWRFOESH.js +2 -0
- package/dist-cli/chunks/{timeline-VEGHQZ36.js → timeline-QJPIAP7L.js} +2 -2
- package/dist-cli/chunks/{upgrade-RNDCRPLJ.js → upgrade-4ZEV4BY2.js} +3 -3
- package/dist-cli/chunks/upload-OF4CO6WM.js +2 -0
- package/dist-cli/chunks/{version-XUMPKQUM.js → version-OVU37MXP.js} +2 -2
- package/dist-cli/chunks/{web-ESQGHVPD.js → web-JUDK5W4T.js} +2 -2
- package/dist-cli/chunks/{what-happened-6NJENKUF.js → what-happened-TKCIGNLL.js} +2 -2
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.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 +2 -2
- package/dist-lib/backend-origin.cjs +18 -18
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +4 -4
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +321 -79
- package/dist-lib/dev-bundle-resolution.cjs +2 -2
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +18 -13
- 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/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +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/dev-server-scanner.cjs +7 -7
- package/dist-lib/sdk.cjs +12 -4
- package/dist-lib/sdk.mjs +12 -4
- package/dist-lib/skills.cjs +456 -170
- package/dist-lib/vite.cjs +1 -1
- package/package.json +4 -4
- package/scripts/dev-server-scanner.ts +5 -5
- package/scripts/postinstall.cjs +1 -1
- package/scripts/preuninstall.cjs +1 -1
- package/skills/{soot → contrast}/SKILL.md +4 -4
- package/skills/sootsim-perf/SKILL.md +3 -1
- package/src/auth/shared-session.ts +1 -1
- package/src/backend-origin.ts +11 -11
- package/src/bridge-constants.ts +3 -3
- package/src/home-paths.ts +1 -1
- package/src/host/agent-host.ts +2 -2
- package/src/host/bridge-host.ts +21 -21
- package/src/native-dev-bundle-url.ts +3 -3
- package/src/runtime-assets.ts +3 -3
- package/src/runtime-delivery.ts +10 -4
- package/src/screenshots/compose.ts +4 -1
- package/src/screenshots/frame-compose.ts +4 -1
- package/src/screenshots/orchestrate.ts +7 -2
- package/src/vite-plugin-one.ts +3 -3
- package/src/vite-plugin.ts +3 -3
- package/dist-cli/chunks/beta-WLLSIVLW.js +0 -2
- package/dist-cli/chunks/chunk-35EAEAN4.js +0 -24
- package/dist-cli/chunks/chunk-44Z2X5R4.js +0 -2
- package/dist-cli/chunks/chunk-4BLOZ3AF.js +0 -2
- package/dist-cli/chunks/chunk-7K5K5NKB.js +0 -2
- package/dist-cli/chunks/chunk-7RWDQCIT.js +0 -1
- package/dist-cli/chunks/chunk-BVD2JFOX.js +0 -1
- package/dist-cli/chunks/chunk-HBEEYWK5.js +0 -2
- package/dist-cli/chunks/chunk-JNUGPBWC.js +0 -118
- package/dist-cli/chunks/chunk-KBSJA7HK.js +0 -3
- package/dist-cli/chunks/chunk-Z2E2DDIQ.js +0 -4
- package/dist-cli/chunks/cli-version-NNBCIQKL.js +0 -2
- package/dist-cli/chunks/daemon-GL5GQTWB.js +0 -2
- package/dist-cli/chunks/drivers-KBKJVF2Y.js +0 -2
- package/dist-cli/chunks/flow-L247VVBM.js +0 -2
- package/dist-cli/chunks/help-QIZEANNK.js +0 -2
- package/dist-cli/chunks/inspect-6OAVS27G.js +0 -2
- package/dist-cli/chunks/install-desktop-LKYAUUQY.js +0 -2
- package/dist-cli/chunks/optional-demo-registry-YGNESGBH.js +0 -2
- package/dist-cli/chunks/runtime-FYVV4DCQ.js +0 -2
- package/dist-cli/chunks/screenshot-command-R535TYXY.js +0 -151
- package/dist-cli/chunks/store-6ZGLOTZI.js +0 -2
- package/dist-cli/chunks/telemetry-V76UPVDV.js +0 -2
- package/dist-cli/chunks/upload-KRBS3Y7Q.js +0 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ React Native simulator.
|
|
|
10
10
|
contains **no rendering code**. The canvas engine (CanvasKit renderer, Yoga
|
|
11
11
|
layout, iOS/Android shell chrome, Electron shell) lives in the private
|
|
12
12
|
`sootsim-engine` workspace package and is shipped at runtime as a versioned
|
|
13
|
-
tarball, fetched from the
|
|
13
|
+
tarball, fetched from the contrast.dev CDN and unpacked into `~/.sootsim`.
|
|
14
14
|
|
|
15
15
|
What this package gives you:
|
|
16
16
|
|
|
@@ -23,12 +23,12 @@ What this package gives you:
|
|
|
23
23
|
editor
|
|
24
24
|
- `sootsim/detox` — a drop-in Detox driver + jest preset
|
|
25
25
|
- `sootsim/sdk` — the inspect/interact verbs as a programmatic API
|
|
26
|
-
- `sootsim/skills` — a registry of
|
|
26
|
+
- `sootsim/skills` — a registry of Contrast-flavored automation skills
|
|
27
27
|
|
|
28
28
|
Platform status: iOS is the parity baseline. Android is bootstrapped and usable
|
|
29
29
|
for focused conformance slices (bundle/runtime identity, device profiles, shell
|
|
30
30
|
chrome, gesture + three-button nav, system UI/window metrics with cutout-safe
|
|
31
|
-
insets) but is not full parity until the Android launch gates in the
|
|
31
|
+
insets) but is not full parity until the Android launch gates in the Contrast
|
|
32
32
|
repo plan pass.
|
|
33
33
|
|
|
34
34
|
## What's in this package (and what's not)
|
|
@@ -81,7 +81,7 @@ graph TD
|
|
|
81
81
|
VITEONE["sootsim/vite = vite-plugin-one.ts (serve runtime at /__soot/)"]
|
|
82
82
|
VITERN["sootsim() = vite-plugin.ts (RN resolver + native stubs)"]
|
|
83
83
|
METRO["sootsim/metro = metro-plugin.ts"]
|
|
84
|
-
COMPAT["@
|
|
84
|
+
COMPAT["@sootsim/compat stub-manifest (native seams)"]
|
|
85
85
|
ENGINESHIM["sootsim-engine react-native shim"]
|
|
86
86
|
end
|
|
87
87
|
SKILLS["sootsim/skills registry.ts (builtin skills)"]
|
|
@@ -90,7 +90,7 @@ graph TD
|
|
|
90
90
|
LOCK["daemon.json lockfile"]
|
|
91
91
|
CFG["config.json"]
|
|
92
92
|
end
|
|
93
|
-
CDN["
|
|
93
|
+
CDN["contrast.dev CDN (runtimes/manifest.json + tarballs)"]
|
|
94
94
|
BIN --> SETUP
|
|
95
95
|
BIN --> RUNTIMECMD
|
|
96
96
|
BIN --> INSPECT
|
|
@@ -171,7 +171,7 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
|
|
|
171
171
|
can also launch the Electron app).
|
|
172
172
|
- The internal **`sootsim()`** plugin (`src/vite-plugin.ts`) is a separate, much
|
|
173
173
|
larger RN-resolution / native-stub plugin: it aliases `react-native` to the
|
|
174
|
-
engine shim and native packages to `@
|
|
174
|
+
engine shim and native packages to `@sootsim/compat` stubs, applies worklets
|
|
175
175
|
transforms, and is the one that instantiates `SootSimBridgeHost` inside its
|
|
176
176
|
own dev server. It is used to build the engine/shell and to load external RN
|
|
177
177
|
apps — it is **not** the published `sootsim/vite` export.
|
|
@@ -188,10 +188,10 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
|
|
|
188
188
|
| SootSimBridgeHost (serve/daemon) | The bridge process (`sootsim serve`, or autostarted as the daemon via launchd/systemd). HTTP+WS hub on `:7668`: relays commands between CLI clients and registered sims; also serves runtime HTTP, `/__server-scan`, and fetch/WS proxies. | `src/host/bridge-host.ts`, `cli/commands/serve.ts`, `cli/commands/daemon.ts`, `src/home-paths.ts` |
|
|
189
189
|
| AgentHost + sessions | Agent-routing extension of the host: owns the single FIFO reader per agent session and fans `agent:event`/`session-status` pushes to every WS subscriber. Backed by agent-sessions + attached-projects stores. | `src/host/agent-host.ts`, `src/agent-sessions.ts`, `src/attached-projects.ts`, `src/agent-daemon-client.ts` |
|
|
190
190
|
| host proxies + dev-server scan | Host-side helpers so a cross-origin tenant worker can reach local dev servers and same-origin APIs: `/__server-scan` discovers running metro/expo/vxrn/one servers; fetch + websocket proxies relay guest-app network through the daemon. | `src/host/fetch-proxy-handler.ts`, `src/host/websocket-proxy.ts`, `scripts/dev-server-scanner.ts`, `src/dev-bundle-resolution.ts` |
|
|
191
|
-
| runtime delivery + sootsim home | Versioned engine runtime management. Resolves the CDN origin (`https://
|
|
191
|
+
| runtime delivery + sootsim home | Versioned engine runtime management. Resolves the CDN origin (`https://contrast.dev`), fetches `runtimes/manifest.json` + the runtime tarball, verifies sha256, unpacks to `~/.sootsim/runtimes/<version>`, and tracks active version/config/daemon lockfile under the sootsim home. | `src/runtime-delivery.ts`, `src/home-paths.ts`, `src/runtime-assets.ts`, `scripts/postinstall.cjs` |
|
|
192
192
|
| `sootsim/vite` + `sootsim/metro` (runtime serving) | Published bundler plugins. Both serve the installed engine runtime at `/__soot/` from `~/.sootsim/runtimes/<version>` so an app's own dev server can host the sim shell; the vite plugin can also launch the Electron app. | `src/vite-plugin-one.ts`, `src/metro-plugin.ts`, `src/runtime-assets.ts` |
|
|
193
|
-
| `sootsim()` vite resolver plugin (internal) | The large RN-resolution / native-stub vite plugin: aliases `react-native` to the engine shim, native packages to `@
|
|
194
|
-
| skills registry | SootSim agent skills (`sootsim-setup`, `sootsim-debug`, `sootsim-test`, `sootsim-visual`, and the generated `
|
|
193
|
+
| `sootsim()` vite resolver plugin (internal) | The large RN-resolution / native-stub vite plugin: aliases `react-native` to the engine shim, native packages to `@sootsim/compat` stubs, applies worklets/babel transforms, and instantiates `SootSimBridgeHost` in its dev server. Distinct from the published `sootsim/vite` export. | `src/vite-plugin.ts`, `src/worklets-babel.ts`, `packages/compat/src/stub-manifest.ts` |
|
|
194
|
+
| skills registry | SootSim agent skills (`sootsim-setup`, `sootsim-debug`, `sootsim-test`, `sootsim-visual`, and the generated `contrast` index) installable with `sootsim skill`. | `skills/*.md`, `src/skills/registry.ts`, `src/skills/types.ts` |
|
|
195
195
|
|
|
196
196
|
## Getting started
|
|
197
197
|
|
|
@@ -267,7 +267,7 @@ sootsim detox # run Detox/Jest suites against sootsim
|
|
|
267
267
|
```
|
|
268
268
|
|
|
269
269
|
See the full command reference via `sootsim --help` or at
|
|
270
|
-
`src/features/site/docs/sootsim/cli/` in the
|
|
270
|
+
`src/features/site/docs/sootsim/cli/` in the Contrast repo.
|
|
271
271
|
|
|
272
272
|
## Bundler plugins (`sootsim/vite` and `sootsim/metro`)
|
|
273
273
|
|
|
@@ -405,10 +405,10 @@ frame for quick art-direction passes.
|
|
|
405
405
|
`sootsim skill install` installs SootSim agent skills in the standard
|
|
406
406
|
`<skill-name>/SKILL.md` layout used by Codex and Claude Code. The bundled
|
|
407
407
|
skills cover setup, debugging/perf/accessibility, testing, visual review, and
|
|
408
|
-
the generated `
|
|
408
|
+
the generated `contrast` command index. The published CLI registry and generated
|
|
409
409
|
website docs both come from `packages/sootsim-skills/`:
|
|
410
410
|
|
|
411
|
-
- `packages/sootsim/skills/
|
|
411
|
+
- `packages/sootsim/skills/contrast/SKILL.md` is generated from that registry
|
|
412
412
|
- `src/features/site/docs/sootsim/cli/*` is generated output, not hand-edited
|
|
413
413
|
|
|
414
414
|
When command docs or examples drift, update `packages/sootsim-skills/` and run:
|
|
@@ -420,7 +420,7 @@ bun run generate:sootsim-docs
|
|
|
420
420
|
## Runtime delivery (the sootsim home and CDN)
|
|
421
421
|
|
|
422
422
|
The engine never ships inside this package. `runtime-delivery.ts` resolves the
|
|
423
|
-
CDN origin (default `https://
|
|
423
|
+
CDN origin (default `https://contrast.dev`, overridable in `config.json`),
|
|
424
424
|
fetches `runtimes/manifest.json` + the runtime tarball, verifies its sha256,
|
|
425
425
|
and unpacks it under `~/.sootsim`:
|
|
426
426
|
|
|
@@ -438,7 +438,7 @@ CLI side.
|
|
|
438
438
|
|
|
439
439
|
## Development and building
|
|
440
440
|
|
|
441
|
-
This package is part of the
|
|
441
|
+
This package is part of the Contrast monorepo. To build the published CLI bundle:
|
|
442
442
|
|
|
443
443
|
```sh
|
|
444
444
|
bun run build:cli
|
package/detox/element-types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Matcher } from './matchers'
|
|
2
2
|
|
|
3
3
|
// element interaction object returned by element(matcher)
|
|
4
|
-
export interface
|
|
4
|
+
export interface SootSimElement {
|
|
5
5
|
// reference to the matcher for lazy evaluation
|
|
6
6
|
_matcher: Matcher
|
|
7
7
|
|
|
@@ -12,7 +12,7 @@ export interface SootElement {
|
|
|
12
12
|
duration: number,
|
|
13
13
|
normalizedStartX: number,
|
|
14
14
|
normalizedStartY: number,
|
|
15
|
-
targetElement:
|
|
15
|
+
targetElement: SootSimElement,
|
|
16
16
|
normalizedEndX: number,
|
|
17
17
|
normalizedEndY: number,
|
|
18
18
|
speed?: string,
|
|
@@ -32,5 +32,5 @@ export interface SootElement {
|
|
|
32
32
|
rotate(radians: number, speed?: 'fast' | 'slow'): Promise<void>
|
|
33
33
|
takeScreenshot(name: string): Promise<string>
|
|
34
34
|
getAttributes(): Promise<any>
|
|
35
|
-
atIndex(index: number):
|
|
35
|
+
atIndex(index: number): SootSimElement
|
|
36
36
|
}
|
package/detox/expectations.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// waitFor(element).toExist().withTimeout(ms)
|
|
4
4
|
|
|
5
5
|
import { dragScrollNode, readSootsimInteractiveViewport } from './gestures'
|
|
6
|
-
import type {
|
|
6
|
+
import type { SootSimElement } from './element-types'
|
|
7
7
|
import type { Matcher } from './matchers'
|
|
8
8
|
import type { Page } from 'playwright'
|
|
9
9
|
|
|
@@ -80,12 +80,12 @@ function describeNodeVisibility(node: any, viewport?: Viewport): string {
|
|
|
80
80
|
|
|
81
81
|
// expect(element) returns an object with assertion methods
|
|
82
82
|
export function createExpect(findNode: FindFn, getPage: GetPageFn) {
|
|
83
|
-
return function
|
|
84
|
-
return new
|
|
83
|
+
return function sootsimExpect(el: SootSimElement): SootSimExpectation {
|
|
84
|
+
return new SootSimExpectation(el._matcher, findNode, getPage, false)
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
class
|
|
88
|
+
class SootSimExpectation {
|
|
89
89
|
private matcher: Matcher
|
|
90
90
|
private findNode: FindFn
|
|
91
91
|
private getPage: GetPageFn
|
|
@@ -98,8 +98,13 @@ class SootExpectation {
|
|
|
98
98
|
this.negated = negated
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
get not():
|
|
102
|
-
return new
|
|
101
|
+
get not(): SootSimExpectation {
|
|
102
|
+
return new SootSimExpectation(
|
|
103
|
+
this.matcher,
|
|
104
|
+
this.findNode,
|
|
105
|
+
this.getPage,
|
|
106
|
+
!this.negated,
|
|
107
|
+
)
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
async toExist(): Promise<void> {
|
|
@@ -286,12 +291,12 @@ class SootExpectation {
|
|
|
286
291
|
|
|
287
292
|
// waitFor(element) returns a chainable object that polls until the assertion passes
|
|
288
293
|
export function createWaitFor(findNode: FindFn, getPage: GetPageFn) {
|
|
289
|
-
return function
|
|
290
|
-
return new
|
|
294
|
+
return function sootsimWaitFor(el: SootSimElement): SootSimWaitForChain {
|
|
295
|
+
return new SootSimWaitForChain(el._matcher, findNode, getPage)
|
|
291
296
|
}
|
|
292
297
|
}
|
|
293
298
|
|
|
294
|
-
class
|
|
299
|
+
class SootSimWaitForChain {
|
|
295
300
|
private matcher: Matcher
|
|
296
301
|
private findNode: FindFn
|
|
297
302
|
private getPage: GetPageFn
|
|
@@ -304,12 +309,12 @@ class SootWaitForChain {
|
|
|
304
309
|
this.getPage = getPage
|
|
305
310
|
}
|
|
306
311
|
|
|
307
|
-
get not():
|
|
312
|
+
get not(): SootSimWaitForChain {
|
|
308
313
|
this._negated = true
|
|
309
314
|
return this
|
|
310
315
|
}
|
|
311
316
|
|
|
312
|
-
toExist():
|
|
317
|
+
toExist(): SootSimWaitForAction {
|
|
313
318
|
this.assertionFn = async () => {
|
|
314
319
|
const node = await this.findNode(this.matcher)
|
|
315
320
|
const exists = node !== null && node !== undefined
|
|
@@ -317,10 +322,10 @@ class SootWaitForChain {
|
|
|
317
322
|
throw new Error(`waitFor toExist failed: ${JSON.stringify(this.matcher)}`)
|
|
318
323
|
}
|
|
319
324
|
}
|
|
320
|
-
return new
|
|
325
|
+
return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage)
|
|
321
326
|
}
|
|
322
327
|
|
|
323
|
-
toBeVisible():
|
|
328
|
+
toBeVisible(): SootSimWaitForAction {
|
|
324
329
|
this.assertionFn = async () => {
|
|
325
330
|
const node = await this.findNode(this.matcher)
|
|
326
331
|
const page = this.getPage()
|
|
@@ -332,10 +337,10 @@ class SootWaitForChain {
|
|
|
332
337
|
)
|
|
333
338
|
}
|
|
334
339
|
}
|
|
335
|
-
return new
|
|
340
|
+
return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage)
|
|
336
341
|
}
|
|
337
342
|
|
|
338
|
-
toBeNotVisible():
|
|
343
|
+
toBeNotVisible(): SootSimWaitForAction {
|
|
339
344
|
this.assertionFn = async () => {
|
|
340
345
|
const node = await this.findNode(this.matcher)
|
|
341
346
|
const visible = await isNodeVisible(this.getPage(), node)
|
|
@@ -343,10 +348,10 @@ class SootWaitForChain {
|
|
|
343
348
|
throw new Error(`waitFor toBeNotVisible failed: ${JSON.stringify(this.matcher)}`)
|
|
344
349
|
}
|
|
345
350
|
}
|
|
346
|
-
return new
|
|
351
|
+
return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage)
|
|
347
352
|
}
|
|
348
353
|
|
|
349
|
-
toHaveText(expectedText: string):
|
|
354
|
+
toHaveText(expectedText: string): SootSimWaitForAction {
|
|
350
355
|
this.assertionFn = async () => {
|
|
351
356
|
const node = await this.findNode(this.matcher)
|
|
352
357
|
if (!node)
|
|
@@ -361,10 +366,10 @@ class SootWaitForChain {
|
|
|
361
366
|
)
|
|
362
367
|
}
|
|
363
368
|
}
|
|
364
|
-
return new
|
|
369
|
+
return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage)
|
|
365
370
|
}
|
|
366
371
|
|
|
367
|
-
toHaveValue(value: string):
|
|
372
|
+
toHaveValue(value: string): SootSimWaitForAction {
|
|
368
373
|
this.assertionFn = async () => {
|
|
369
374
|
const node = await this.findNode(this.matcher)
|
|
370
375
|
if (!node) throw new Error(`waitFor toHaveValue: element not found`)
|
|
@@ -373,11 +378,11 @@ class SootWaitForChain {
|
|
|
373
378
|
throw new Error(`waitFor toHaveValue: expected "${value}" in "${actual}"`)
|
|
374
379
|
}
|
|
375
380
|
}
|
|
376
|
-
return new
|
|
381
|
+
return new SootSimWaitForAction(this.assertionFn, this.findNode, this.getPage)
|
|
377
382
|
}
|
|
378
383
|
}
|
|
379
384
|
|
|
380
|
-
class
|
|
385
|
+
class SootSimWaitForAction {
|
|
381
386
|
private assertionFn: () => Promise<void>
|
|
382
387
|
private findNode: FindFn
|
|
383
388
|
private getPage: GetPageFn
|
|
@@ -417,8 +422,8 @@ class SootWaitForAction {
|
|
|
417
422
|
}
|
|
418
423
|
}
|
|
419
424
|
|
|
420
|
-
whileElement(matcher: Matcher):
|
|
421
|
-
return new
|
|
425
|
+
whileElement(matcher: Matcher): SootSimWaitForScrollAction {
|
|
426
|
+
return new SootSimWaitForScrollAction(
|
|
422
427
|
this.assertionFn,
|
|
423
428
|
this.findNode,
|
|
424
429
|
this.getPage,
|
|
@@ -427,7 +432,7 @@ class SootWaitForAction {
|
|
|
427
432
|
}
|
|
428
433
|
}
|
|
429
434
|
|
|
430
|
-
class
|
|
435
|
+
class SootSimWaitForScrollAction {
|
|
431
436
|
private assertionFn: () => Promise<void>
|
|
432
437
|
private findNode: FindFn
|
|
433
438
|
private getPage: GetPageFn
|
package/detox/gestures.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// pull in @
|
|
1
|
+
// pull in @sootsim/globals so window.__sootsimTest and
|
|
2
2
|
// window.__sootsimFindInteractiveCanvas are typed inside page.evaluate
|
|
3
3
|
// bodies. all sootsim-namespaced globals are declared canonically there —
|
|
4
4
|
// add new entries to packages/sootsim-globals/src/index.ts, not here.
|
|
5
|
-
import type {} from '@
|
|
5
|
+
import type {} from '@sootsim/globals'
|
|
6
6
|
import type { Page } from 'playwright'
|
|
7
7
|
|
|
8
8
|
// shape of the surface-metrics bridge response we care about. the bridge
|
|
@@ -262,15 +262,15 @@ export async function readSootsimInteractiveViewport(
|
|
|
262
262
|
|
|
263
263
|
export async function sootsimToPage(
|
|
264
264
|
page: Page,
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
sootsimX: number,
|
|
266
|
+
sootsimY: number,
|
|
267
267
|
): Promise<{ x: number; y: number }> {
|
|
268
268
|
const metrics = await readSootsimPageMetrics(page)
|
|
269
269
|
const scaleX = metrics.canvas.width / metrics.window.width
|
|
270
270
|
const scaleY = metrics.canvas.height / metrics.window.height
|
|
271
271
|
return {
|
|
272
|
-
x: metrics.canvas.left +
|
|
273
|
-
y: metrics.canvas.top +
|
|
272
|
+
x: metrics.canvas.left + sootsimX * scaleX,
|
|
273
|
+
y: metrics.canvas.top + sootsimY * scaleY,
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
|
|
@@ -283,28 +283,28 @@ export async function dragScrollNode(
|
|
|
283
283
|
pixels: number,
|
|
284
284
|
direction: 'up' | 'down' | 'left' | 'right',
|
|
285
285
|
): Promise<void> {
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
let
|
|
289
|
-
let
|
|
286
|
+
const startSootSimX = node.absolutePosition.x + node.layout.width / 2
|
|
287
|
+
const startSootSimY = node.absolutePosition.y + node.layout.height / 2
|
|
288
|
+
let endSootSimX = startSootSimX
|
|
289
|
+
let endSootSimY = startSootSimY
|
|
290
290
|
|
|
291
291
|
switch (direction) {
|
|
292
292
|
case 'down':
|
|
293
|
-
|
|
293
|
+
endSootSimY -= pixels
|
|
294
294
|
break
|
|
295
295
|
case 'up':
|
|
296
|
-
|
|
296
|
+
endSootSimY += pixels
|
|
297
297
|
break
|
|
298
298
|
case 'left':
|
|
299
|
-
|
|
299
|
+
endSootSimX += pixels
|
|
300
300
|
break
|
|
301
301
|
case 'right':
|
|
302
|
-
|
|
302
|
+
endSootSimX -= pixels
|
|
303
303
|
break
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
const startPage = await sootsimToPage(page,
|
|
307
|
-
const endPage = await sootsimToPage(page,
|
|
306
|
+
const startPage = await sootsimToPage(page, startSootSimX, startSootSimY)
|
|
307
|
+
const endPage = await sootsimToPage(page, endSootSimX, endSootSimY)
|
|
308
308
|
const steps = Math.max(16, Math.ceil(pixels / 8))
|
|
309
309
|
|
|
310
310
|
await page.mouse.move(startPage.x, startPage.y)
|
package/detox/index.ts
CHANGED
|
@@ -15,12 +15,12 @@ import {
|
|
|
15
15
|
waitForSootsimGestureHandler,
|
|
16
16
|
} from './gestures'
|
|
17
17
|
import { by, type Matcher } from './matchers'
|
|
18
|
-
import type {
|
|
18
|
+
import type { SootSimElement } from './element-types'
|
|
19
19
|
import type { Browser, BrowserContext, Page } from 'playwright'
|
|
20
20
|
import type { SootsimEventMap, SootsimEventName } from 'sootsim-engine/sootsim-event'
|
|
21
21
|
|
|
22
22
|
export { by }
|
|
23
|
-
export type {
|
|
23
|
+
export type { SootSimElement } from './element-types'
|
|
24
24
|
|
|
25
25
|
const BASE_URL = process.env.SOOTSIM_URL || 'http://localhost:5173'
|
|
26
26
|
const SCREENSHOT_DIR =
|
|
@@ -517,7 +517,7 @@ async function isFocusedTextInputNode(page: Page, nodeInfo: any): Promise<boolea
|
|
|
517
517
|
return !!focused && typeof focused === 'object' && focused.nodeId === nodeInfo?.nodeId
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
-
async function focusElementForTextEntry(el:
|
|
520
|
+
async function focusElementForTextEntry(el: SootSimElement): Promise<void> {
|
|
521
521
|
const page = getPage()
|
|
522
522
|
const node = await findNodeByMatcher(el._matcher)
|
|
523
523
|
if (!node) {
|
|
@@ -669,8 +669,8 @@ async function dispatchSootsimEventInPage<K extends SootsimEventName>(
|
|
|
669
669
|
)
|
|
670
670
|
}
|
|
671
671
|
|
|
672
|
-
function
|
|
673
|
-
const el:
|
|
672
|
+
function createSootSimElement(matcher: Matcher): SootSimElement {
|
|
673
|
+
const el: SootSimElement = {
|
|
674
674
|
_matcher: matcher,
|
|
675
675
|
|
|
676
676
|
async tap(point) {
|
|
@@ -679,11 +679,11 @@ function createSootElement(matcher: Matcher): SootElement {
|
|
|
679
679
|
const node = await findNodeByMatcher(matcher)
|
|
680
680
|
if (!node) throw new Error(`element not found for tap: ${JSON.stringify(matcher)}`)
|
|
681
681
|
const defaultPoint = point ? null : await getDefaultTapPoint(page, node)
|
|
682
|
-
const
|
|
682
|
+
const targetSootSimX =
|
|
683
683
|
typeof point?.x === 'number'
|
|
684
684
|
? node.absolutePosition.x + point.x
|
|
685
685
|
: (defaultPoint?.x ?? node.absolutePosition.x + node.layout.width / 2)
|
|
686
|
-
const
|
|
686
|
+
const targetSootSimY =
|
|
687
687
|
typeof point?.y === 'number'
|
|
688
688
|
? node.absolutePosition.y + point.y
|
|
689
689
|
: (defaultPoint?.y ?? node.absolutePosition.y + node.layout.height / 2)
|
|
@@ -700,7 +700,7 @@ function createSootElement(matcher: Matcher): SootElement {
|
|
|
700
700
|
}
|
|
701
701
|
await tap(x, y)
|
|
702
702
|
},
|
|
703
|
-
{ x:
|
|
703
|
+
{ x: targetSootSimX, y: targetSootSimY },
|
|
704
704
|
)
|
|
705
705
|
// small settling time for react state updates
|
|
706
706
|
await page.waitForTimeout(50)
|
|
@@ -760,7 +760,7 @@ function createSootElement(matcher: Matcher): SootElement {
|
|
|
760
760
|
duration: number,
|
|
761
761
|
normalizedStartX: number,
|
|
762
762
|
normalizedStartY: number,
|
|
763
|
-
targetElement:
|
|
763
|
+
targetElement: SootSimElement,
|
|
764
764
|
normalizedEndX: number,
|
|
765
765
|
normalizedEndY: number,
|
|
766
766
|
speed = 'fast',
|
|
@@ -780,15 +780,17 @@ function createSootElement(matcher: Matcher): SootElement {
|
|
|
780
780
|
)
|
|
781
781
|
|
|
782
782
|
// compute start and end in sootsim coords
|
|
783
|
-
const
|
|
783
|
+
const startSootSimX =
|
|
784
784
|
srcNode.absolutePosition.x + srcNode.layout.width * normalizedStartX
|
|
785
|
-
const
|
|
785
|
+
const startSootSimY =
|
|
786
786
|
srcNode.absolutePosition.y + srcNode.layout.height * normalizedStartY
|
|
787
|
-
const
|
|
788
|
-
|
|
787
|
+
const endSootSimX =
|
|
788
|
+
tgtNode.absolutePosition.x + tgtNode.layout.width * normalizedEndX
|
|
789
|
+
const endSootSimY =
|
|
790
|
+
tgtNode.absolutePosition.y + tgtNode.layout.height * normalizedEndY
|
|
789
791
|
|
|
790
|
-
const startPage = await sootsimToPage(page,
|
|
791
|
-
const endPage = await sootsimToPage(page,
|
|
792
|
+
const startPage = await sootsimToPage(page, startSootSimX, startSootSimY)
|
|
793
|
+
const endPage = await sootsimToPage(page, endSootSimX, endSootSimY)
|
|
792
794
|
const steps = speed === 'slow' ? 20 : 10
|
|
793
795
|
|
|
794
796
|
// press at start position
|
|
@@ -927,12 +929,12 @@ function createSootElement(matcher: Matcher): SootElement {
|
|
|
927
929
|
const { absolutePosition, layout } = node
|
|
928
930
|
|
|
929
931
|
// start from center
|
|
930
|
-
const
|
|
931
|
-
const
|
|
932
|
+
const startSootSimX = absolutePosition.x + layout.width * 0.5
|
|
933
|
+
const startSootSimY = absolutePosition.y + layout.height * 0.5
|
|
932
934
|
|
|
933
935
|
// compute distance
|
|
934
|
-
let
|
|
935
|
-
let
|
|
936
|
+
let endSootSimX = startSootSimX
|
|
937
|
+
let endSootSimY = startSootSimY
|
|
936
938
|
const dist =
|
|
937
939
|
direction === 'up' || direction === 'down'
|
|
938
940
|
? layout.height * percentage
|
|
@@ -940,16 +942,16 @@ function createSootElement(matcher: Matcher): SootElement {
|
|
|
940
942
|
|
|
941
943
|
switch (direction) {
|
|
942
944
|
case 'up':
|
|
943
|
-
|
|
945
|
+
endSootSimY -= dist
|
|
944
946
|
break
|
|
945
947
|
case 'down':
|
|
946
|
-
|
|
948
|
+
endSootSimY += dist
|
|
947
949
|
break
|
|
948
950
|
case 'left':
|
|
949
|
-
|
|
951
|
+
endSootSimX -= dist
|
|
950
952
|
break
|
|
951
953
|
case 'right':
|
|
952
|
-
|
|
954
|
+
endSootSimX += dist
|
|
953
955
|
break
|
|
954
956
|
}
|
|
955
957
|
|
|
@@ -965,10 +967,10 @@ function createSootElement(matcher: Matcher): SootElement {
|
|
|
965
967
|
await drag(fromX, fromY, toX, toY, steps, stepMs)
|
|
966
968
|
},
|
|
967
969
|
{
|
|
968
|
-
fromX:
|
|
969
|
-
fromY:
|
|
970
|
-
toX:
|
|
971
|
-
toY:
|
|
970
|
+
fromX: startSootSimX,
|
|
971
|
+
fromY: startSootSimY,
|
|
972
|
+
toX: endSootSimX,
|
|
973
|
+
toY: endSootSimY,
|
|
972
974
|
steps,
|
|
973
975
|
stepMs: stepDelay,
|
|
974
976
|
},
|
|
@@ -1084,8 +1086,8 @@ function createSootElement(matcher: Matcher): SootElement {
|
|
|
1084
1086
|
}
|
|
1085
1087
|
|
|
1086
1088
|
// element() factory -- takes a matcher and returns an element interaction object
|
|
1087
|
-
export function element(matcher: Matcher):
|
|
1088
|
-
return
|
|
1089
|
+
export function element(matcher: Matcher): SootSimElement {
|
|
1090
|
+
return createSootSimElement(matcher)
|
|
1089
1091
|
}
|
|
1090
1092
|
|
|
1091
1093
|
// device object -- app lifecycle
|
|
@@ -1616,10 +1618,10 @@ export const device = {
|
|
|
1616
1618
|
}
|
|
1617
1619
|
|
|
1618
1620
|
// exports matching detox API
|
|
1619
|
-
const
|
|
1620
|
-
const
|
|
1621
|
+
const sootsimExpect = createExpect(findNodeByMatcher, getPage)
|
|
1622
|
+
const sootsimWaitFor = createWaitFor(findNodeByMatcher, getPage)
|
|
1621
1623
|
|
|
1622
|
-
export {
|
|
1624
|
+
export { sootsimExpect as expect, sootsimWaitFor as waitFor }
|
|
1623
1625
|
|
|
1624
1626
|
// cleanup -- call this in afterAll
|
|
1625
1627
|
export async function cleanup() {
|
package/dist-cli/bin.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/*! sootsim v0.1.
|
|
3
|
-
import{a as w,b as u}from"./chunks/chunk-
|
|
2
|
+
/*! sootsim v0.1.229 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
3
|
+
import{a as w,b as u}from"./chunks/chunk-SYZKYAPC.js";import"./chunks/chunk-U4OPCVAL.js";function f(t){let a=t instanceof Error?t.message:String(t);(/^command timed out after \d+s$/.test(a)||a.startsWith("sim disconnected:")||a.startsWith("bridge never reconnected")||a.startsWith("no sim connected with id ")||a.startsWith("could not connect to ws://")||a.startsWith("sootsim bridge daemon is not running")||a.startsWith("sootsim bridge lockfile is fresh")||a.startsWith("sootsim bridge exited before becoming ready")||a.startsWith("background daemon setup is unavailable")||a.startsWith("sootsim bridge did not start within"))&&!process.env.SOOTSIM_VERBOSE&&(process.stderr.write(` ${a}
|
|
4
4
|
`),process.exit(1)),t instanceof Error&&t.stack?process.stderr.write(`${t.stack}
|
|
5
5
|
`):process.stderr.write(`${a}
|
|
6
6
|
`),process.exit(1)}process.on("unhandledRejection",f);process.on("uncaughtException",f);var h=typeof __SOOTSIM_STANDALONE__<"u"&&__SOOTSIM_STANDALONE__,A=new Set(["desktop"]);function k(t){!h||!A.has(t)||(process.stderr.write(` sootsim ${t} isn't available in the standalone binary \u2014
|
|
7
7
|
it needs vite / electron / playwright from a project's node_modules.
|
|
8
8
|
run the npm package instead:
|
|
9
9
|
sootsim ${t}
|
|
10
|
-
`),process.exit(1))}var e=u(process.argv);if(!e.command&&e.commandArgs.length>0){let{resolveHiddenRuntimeAlias:t}=await import("./chunks/hidden-runtime-alias-
|
|
11
|
-
`)}async function c(t){let{flushCliTelemetry:a}=await import("./chunks/telemetry-
|
|
10
|
+
`),process.exit(1))}var e=u(process.argv);if(!e.command&&e.commandArgs.length>0){let{resolveHiddenRuntimeAlias:t}=await import("./chunks/hidden-runtime-alias-OVGHDXOR.js");e=t(e)}{let{trackCliEvent:t}=await import("./chunks/telemetry-CWRFOESH.js");t({event:"cli_command_invoked",properties:{command:e.command||(e.version?"version":e.help?"help":"none"),arg_count:e.commandArgs.length,platform:process.platform,arch:process.arch,node_version:process.versions.node}})}if(e.command!=="serve"&&e.command!=="daemon"){let{consumeRuntimeUpgradeNotice:t}=await import("./chunks/home-paths-3W46OKY6.js"),a=t();a&&process.stderr.write(` SootSim engine upgraded to v${a.to}`+(a.from?` (from v${a.from})`:"")+` \xB7 what's new: https://sootsim.com/changelog
|
|
11
|
+
`)}async function c(t){let{flushCliTelemetry:a}=await import("./chunks/telemetry-CWRFOESH.js");await a(),process.exit(t)}if(e.version){let{getCliVersion:t}=await import("./chunks/cli-version-W42GGXV7.js"),{IS_BETA:a,BETA_LABEL:i}=await import("./chunks/beta-BXBII74U.js"),p=a?` \xB7 ${i}`:"";console.log(`sootsim v${t()}${p}`);let{readActiveRuntime:b}=await import("./chunks/home-paths-3W46OKY6.js"),l=b();console.log(l?`runtime v${l}`:"runtime not installed"),await c(0)}if(e.help&&!e.command){let{printHelp:t}=await import("./chunks/help-WNPSM6ZU.js");t(),await c(0)}var o=e.globalFlags.port,r=e.verbose,m=e.globalFlags.device,d=e.globalFlags.theme,v=e.globalFlags.driver,_=e.globalFlags.headless===!0,g=e.globalFlags.sim??e.globalFlags.session??e.globalFlags.tab,s=g?["--sim",g,...e.commandArgs]:e.commandArgs;if(m||d){let{settingsStore:t}=await import("./chunks/store-FPAX23SK.js"),a={};m&&(a.deviceModel=m),d&&(a.colorScheme=d),t.apply(a)}if(!e.command&&e.commandArgs.length===0){let{runSetup:t,shouldRunSetupForBareCommand:a}=await import("./chunks/setup-YKDBK3RX.js");a()&&(await t(["--from-welcome"]),await c(0));let{printHelp:i}=await import("./chunks/help-WNPSM6ZU.js");i(),await c(0)}var n=e.command??"";if(!n){if(e.commandArgs.length>0){let a=e.commandArgs.find(i=>!i.startsWith("-"))??e.commandArgs[0];console.error(` unknown command: ${a}`),console.error(" run `sootsim --help` to see the full surface."),await c(1)}let{printHelp:t}=await import("./chunks/help-WNPSM6ZU.js");t(),await c(0)}if(e.help||e.commandArgs.includes("--help")||e.commandArgs.includes("-h")){if(n==="skill"){let{runSkill:i}=await import("./chunks/skills-DQOWHZBC.js");await i(e.commandArgs),await c(0)}let t=n==="do"||n==="get"||n==="debug"||n==="shell"||n==="wait",a=e.commandArgs.find(i=>!i.startsWith("-"));if(n!=="shell")if(t&&!a){let{printGroupHelp:i}=await import("./chunks/help-WNPSM6ZU.js");i(n)&&await c(0)}else{let{printCommandHelp:i}=await import("./chunks/help-WNPSM6ZU.js");i(t&&a?a:n,{prefer:t&&a?"verb":"command",group:t&&a?n:void 0}),await c(0)}}k(n);if(w.has(n)){let{runInspect:t}=await import("./chunks/inspect-4J5NJCXS.js");await t([n,...s],{port:o,verbose:r})}else switch(n){case"assert":{let{runAssert:t}=await import("./chunks/assert-KEWRJSST.js");await t(e.commandArgs);break}case"detox":{let{runDetox:t}=await import("./chunks/detox-BXXEPJ3F.js");await t(e.commandArgs,{port:o,verbose:r});break}case"maestro":{let{runMaestro:t}=await import("./chunks/maestro-NYFSOD2H.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"record":{let{runRecord:t}=await import("./chunks/record-NAYAEQPD.js");await t(s,{port:o,verbose:r});break}case"film":{let{runFilm:t}=await import("./chunks/film-MVJCHWBU.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"storage":{let{runStorage:t}=await import("./chunks/storage-TKTKWTWE.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"perf":{let{runPerf:t}=await import("./chunks/perf-5XMCN7A2.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"screenshot":{let{runScreenshotCommand:t}=await import("./chunks/screenshot-command-HFNYL2CY.js"),a=await t(s,{port:o,verbose:r});process.exit(typeof a=="number"?a:0)}case"mode":{let{runMode:t}=await import("./chunks/mode-VSABDCU5.js");await t(s,{port:o,verbose:r});break}case"inspect":{let{runInspect:t}=await import("./chunks/inspect-4J5NJCXS.js");await t(s,{port:o,verbose:r});break}case"debug":{let{runDebug:t}=await import("./chunks/debug-EU5JDWZR.js");await t(s,{port:o,verbose:r});break}case"timeline":{let{runTimeline:t}=await import("./chunks/timeline-QJPIAP7L.js");await t(s,{port:o,verbose:r});break}case"what-happened":{let{runWhatHappened:t}=await import("./chunks/what-happened-TKCIGNLL.js");await t(s,{port:o,verbose:r});break}case"open":{let{runOpenCommand:t}=await import("./chunks/control-QFNWOCLW.js");await t(s,{port:o});break}case"use":{let{runUseCommand:t}=await import("./chunks/control-QFNWOCLW.js");await t(s,{port:o});break}case"claim":{let{runClaimCommand:t}=await import("./chunks/control-QFNWOCLW.js");await t(s,{port:o});break}case"close":{let{runCloseCommand:t}=await import("./chunks/control-QFNWOCLW.js");await t(s,{port:o});break}case"device":{let{runDeviceCommand:t}=await import("./chunks/device-6B6NKGAW.js");await t(s,{port:o});break}case"compat":{let{runCompat:t}=await import("./chunks/compat-HPS6RGWD.js");await t(e.commandArgs);break}case"desktop":{let{runDesktop:t}=await import("./chunks/desktop-OOCDJB3I.js");await t(e.commandArgs,{port:o,device:m,driver:v});break}case"login":{let{runLogin:t}=await import("./chunks/login-CU72MJHE.js");await t(e.commandArgs);break}case"logout":{let{runLogout:t}=await import("./chunks/logout-SF5EPW7M.js");await t();break}case"auth":{let{runAuth:t}=await import("./chunks/auth-KQXLD6AC.js");await t(e.commandArgs);break}case"setup":{let{runSetup:t}=await import("./chunks/setup-YKDBK3RX.js");await t(e.commandArgs);break}case"serve":{let{runServe:t}=await import("./chunks/serve-Z5HVGJ3C.js");await t(e.commandArgs,{port:o});break}case"daemon":{let{runDaemon:t}=await import("./chunks/daemon-B6RUIEYN.js");await t(e.commandArgs,{port:o});break}case"runtime":{let{runRuntime:t}=await import("./chunks/runtime-LR63M7XX.js");await t(e.commandArgs);break}case"upgrade":{let{runUpgrade:t}=await import("./chunks/upgrade-4ZEV4BY2.js");await t(e.commandArgs);break}case"version":{let{runVersion:t}=await import("./chunks/version-OVU37MXP.js");await t(e.commandArgs);break}case"agent":{let{runAgentCommand:t}=await import("./chunks/agent-O26YYN6Z.js"),a=await t(e.commandArgs);process.exit(a)}case"agent-wrapper":{let{runAgentWrapper:t}=await import("./chunks/agent-wrapper-GSMHXP6S.js"),a=await t(e.commandArgs);process.exit(a)}case"skill":{let{runSkill:t}=await import("./chunks/skills-DQOWHZBC.js");await t(e.commandArgs);break}case"app-fonts":{let{runAppFonts:t}=await import("./chunks/app-fonts-TVTDVD7S.js");await t(e.commandArgs);break}case"config":{let{runConfig:t}=await import("./chunks/config-EKIT7WKS.js");await t(e.commandArgs);break}default:console.error(` unknown command: ${n}`),console.error(" run `sootsim --help` to see the full surface."),process.exit(1)}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
2
|
-
import{a as b,b as E,c as A,d as l,e as S,g as j,h as T,j as I,m as k,n as D,o as x,p as g,q as O,r as C}from"./chunk-
|
|
1
|
+
/*! sootsim v0.1.229 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
import{a as b,b as E,c as A,d as l,e as S,g as j,h as T,j as I,m as k,n as D,o as x,p as g,q as O,r as C}from"./chunk-3UE5OA64.js";import"./chunk-62GPXYH7.js";import{c as $}from"./chunk-HKBDDBKZ.js";import"./chunk-456MZ4LU.js";import"./chunk-CE3XI55C.js";import"./chunk-AZWH6YSL.js";import"./chunk-AB7XCG3A.js";import"./chunk-4O73AHYA.js";import"./chunk-GVOFX6LJ.js";import{j as y}from"./chunk-TWUPMUWQ.js";import"./chunk-VUJ5FLL7.js";import"./chunk-3FGXJMSF.js";import"./chunk-OUJVQUX4.js";import"./chunk-U4OPCVAL.js";import w from"node:fs";import d from"node:path";import{spawn as _}from"node:child_process";import F from"node:net";import{WebSocket as R}from"ws";var c=class extends Error{code;constructor(e,t){super(e),this.name="AgentDaemonError",this.code=t}},f=class{ws;port;commandTimeoutMs;ready;closed=!1;nextId=1;pending=new Map;eventListeners=new Set;statusListeners=new Set;disconnectListeners=new Set;constructor(e={}){this.port=e.port??7668,this.commandTimeoutMs=e.commandTimeoutMs??15e3,this.ws=new R(`ws://localhost:${this.port}`),this.ready=new Promise((t,r)=>{let s=()=>{this.ws.off("error",o),t()},o=i=>{this.ws.off("open",s),r(new c(`could not connect to sootsim daemon on port ${this.port}: ${i.message}`,"NO_DAEMON"))};this.ws.once("open",s),this.ws.once("error",o)}),this.ws.on("message",t=>this.handleMessage(t)),this.ws.on("close",()=>this.handleClose()),this.ws.on("error",()=>{})}async waitReady(){return this.ready}async listProjects(){return this.send("agent:list-projects")}async upsertProject(e){return this.send("agent:upsert-project",{input:e})}async deleteProject(e){return this.send("agent:delete-project",{projectId:e})}async autoAttachForUrl(e){return this.send("agent:auto-attach-for-url",{input:e})}async listSessions(e){return this.send("agent:list-sessions",{projectId:e})}async startSession(e){return this.send("agent:start-session",{input:e})}async sendPrompt(e,t){return this.send("agent:send-prompt",{sessionId:e,prompt:t})}async sendClaimedPrompt(e,t){return this.send("agent:send-claimed-prompt",{simId:e,prompt:t})}async endSession(e){return this.send("agent:end-session",{sessionId:e})}async getTranscript(e){return this.send("agent:get-transcript",{sessionId:e})}async getPaths(){return this.send("agent:get-paths")}async subscribeEvents(e){return this.send("agent:subscribe-events",{sessionId:e})}async unsubscribeEvents(e){return this.send("agent:unsubscribe-events",{sessionId:e})}onAgentEvent(e){return this.eventListeners.add(e),()=>this.eventListeners.delete(e)}onSessionStatusChange(e){return this.statusListeners.add(e),()=>this.statusListeners.delete(e)}onDisconnect(e){return this.disconnectListeners.add(e),()=>this.disconnectListeners.delete(e)}close(){if(!this.closed){this.closed=!0;try{this.ws.close()}catch{}}}async send(e,t={}){if(await this.ready,this.closed||this.ws.readyState!==R.OPEN)throw new c("daemon connection is closed","NO_DAEMON");let r=this.nextId++;return new Promise((s,o)=>{let i=setTimeout(()=>{this.pending.delete(r),o(new c(`${e} timed out after ${Math.round(this.commandTimeoutMs/1e3)}s`,"TIMEOUT"))},this.commandTimeoutMs);this.pending.set(r,{resolve:s,reject:o,timer:i});try{this.ws.send(JSON.stringify({id:r,type:e,...t}))}catch(a){clearTimeout(i),this.pending.delete(r),o(a instanceof Error?a:new Error(String(a)))}})}handleMessage(e){let t;try{t=JSON.parse(String(e))}catch{return}if(!t||typeof t!="object")return;if(t.type==="agent:event"){for(let s of this.eventListeners)try{s({sessionId:t.sessionId,event:t.event})}catch{}return}if(t.type==="agent:session-status"){for(let s of this.statusListeners)try{s(t.session)}catch{}return}if(typeof t.id!="number")return;let r=this.pending.get(t.id);r&&(this.pending.delete(t.id),clearTimeout(r.timer),t.error?r.reject(new c(t.error,t.code)):r.resolve(t.result))}handleClose(){if(!this.closed){this.closed=!0;for(let[,e]of this.pending)clearTimeout(e.timer),e.reject(new c("daemon disconnected","DISCONNECT"));this.pending.clear();for(let e of this.disconnectListeners)try{e()}catch{}}}};function M(n=7668,e=400){return new Promise(t=>{let r=new F.Socket,s=!1,o=i=>{s||(s=!0,r.destroy(),t(i))};r.setTimeout(e),r.once("connect",()=>o(!0)),r.once("timeout",()=>o(!1)),r.once("error",()=>o(!1)),r.connect(n,"127.0.0.1")})}async function B(n={}){let e=n.port??7668;if(await M(e))return{alreadyRunning:!0};if(y())throw new c(`no sootsim bridge on port ${e}. run \`sootsim setup\` to install the background daemon, or \`sootsim serve\` in another shell.`,"DEV_HOST_REFUSED");let{cmd:t,prefixArgs:r}=C(),s=[...r,"serve","--quiet"];e!==7668&&s.push("--port",String(e));let o=_(t,s,{detached:!0,stdio:"ignore",env:process.env,cwd:process.cwd()});o.unref();let i=Date.now()+(n.startupTimeoutMs??5e3);for(;Date.now()<i;){if(await M(e))return{alreadyRunning:!1,pid:o.pid};await new Promise(a=>setTimeout(a,100))}throw new c(`spawned sootsim daemon on port ${e} but it did not come up in time. run \`sootsim serve\` manually to diagnose.`,"SPAWN_TIMEOUT")}async function h(n={}){await B({port:n.port,startupTimeoutMs:n.startupTimeoutMs});let e=new f(n);try{await e.waitReady()}catch(t){throw e.close(),t}return e}async function ge(n){let[e,...t]=n;try{switch(e){case void 0:case"--help":case"-h":case"help":return L(),0;case"attach":return await W(t);case"projects":return await J();case"project":return await V(t);case"sessions":return await H(t);case"start":return await G(t);case"prompt":return await q(t);case"watch":return await K(t);case"transcript":return await z(t);case"end":return await Q(t);case"paths":return await Y();default:return process.stderr.write(`unknown agent subcommand: ${e}
|
|
3
3
|
`),L(),2}}catch(r){if(r instanceof c)return process.stderr.write(`${r.message}
|
|
4
4
|
`),1;throw r}}async function v(n){let e=await h({clientLabel:"sootsim-agent-cli"});try{return await n(e)}finally{e.close()}}function L(){$("agent")}function P(n,e){let t={},r=[];for(let s=0;s<n.length;s++){let o=n[s],i=e[o];i==="value"?(t[o]=n[s+1]??"",s++):i==="bool"?t[o]=!0:r.push(o)}return{flags:t,positional:r}}function U(n,e="codex"){return n==="codex"||n==="claude"?n:e}async function W(n){let{flags:e,positional:t}=P(n,{"--name":"value","--provider":"value"}),r=t[0];if(!r)return process.stderr.write(`usage: sootsim agent attach <dir> [--name X] [--provider codex|claude]
|
|
5
5
|
`),2;let s=d.resolve(r);if(!w.existsSync(s))return process.stderr.write(`directory does not exist: ${s}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
2
|
-
import{b as M}from"./chunk-
|
|
1
|
+
/*! sootsim v0.1.229 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
+
import{b as M}from"./chunk-62GPXYH7.js";import"./chunk-U4OPCVAL.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>
|