sootsim 0.1.227 → 0.1.228
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-6P5T2QHC.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-X376DPV2.js → agent-wrapper-SMEMJZXI.js} +2 -2
- package/dist-cli/chunks/{app-fonts-LIQTD5RP.js → app-fonts-6OQTLX3U.js} +2 -2
- package/dist-cli/chunks/{assert-X6PPLN72.js → assert-ZAYFOW6R.js} +2 -2
- package/dist-cli/chunks/{auth-INRWIM2C.js → auth-Y53UANSH.js} +2 -2
- package/dist-cli/chunks/beta-ZQCASJ7C.js +2 -0
- package/dist-cli/chunks/{camera-LGFHYCJ5.js → camera-AJQWSOO4.js} +2 -2
- package/dist-cli/chunks/{chunk-2BQOZATN.js → chunk-2I62CFYI.js} +3 -3
- package/dist-cli/chunks/{chunk-E3DLUOFK.js → chunk-34MFG4IM.js} +7 -7
- package/dist-cli/chunks/{chunk-J7IFVTWX.js → chunk-4A6HM2K3.js} +1 -1
- package/dist-cli/chunks/chunk-4QOY4T27.js +3 -0
- package/dist-cli/chunks/{chunk-HRYKZ5YY.js → chunk-6Q2B7QHC.js} +5 -5
- package/dist-cli/chunks/{chunk-F5HOYUD2.js → chunk-6U3GMNTP.js} +1 -1
- package/dist-cli/chunks/{chunk-RNWJQ4UR.js → chunk-72AQH53U.js} +1 -1
- package/dist-cli/chunks/{chunk-NIN75M5H.js → chunk-7HMRWJHH.js} +1 -1
- package/dist-cli/chunks/{chunk-YB2LM6VB.js → chunk-7TJVPCQA.js} +6 -6
- package/dist-cli/chunks/{chunk-LM544J6V.js → chunk-A5SO4OGE.js} +1 -1
- package/dist-cli/chunks/{chunk-D42IRY2Y.js → chunk-AJ3CDBW6.js} +2 -2
- package/dist-cli/chunks/chunk-ALE5MU5Z.js +2 -0
- package/dist-cli/chunks/{chunk-NHE3YHDU.js → chunk-AVS7RL73.js} +2 -2
- package/dist-cli/chunks/chunk-AWF575Y5.js +1 -0
- package/dist-cli/chunks/chunk-BHNOPI7I.js +2 -0
- package/dist-cli/chunks/{chunk-LCJSTEIR.js → chunk-D2QUTBZP.js} +2 -2
- package/dist-cli/chunks/{chunk-ESK7B2BQ.js → chunk-DEVNCWS4.js} +6 -6
- package/dist-cli/chunks/{chunk-GUCFICA4.js → chunk-E5FOMYR6.js} +1 -1
- package/dist-cli/chunks/{chunk-2XXPF6LP.js → chunk-EFMPSG7T.js} +2 -2
- package/dist-cli/chunks/{chunk-4J7BIXEB.js → chunk-EKWOBYJO.js} +2 -2
- package/dist-cli/chunks/{chunk-E75EBS77.js → chunk-EPQ5EZBJ.js} +2 -2
- package/dist-cli/chunks/{chunk-Q4FB222M.js → chunk-FDSFA7WL.js} +3 -3
- package/dist-cli/chunks/{chunk-UGWURWHN.js → chunk-FSKQZHM2.js} +2 -2
- package/dist-cli/chunks/{chunk-QWRYD2QV.js → chunk-G42TLJVQ.js} +18 -18
- package/dist-cli/chunks/{chunk-HXHB5A6R.js → chunk-JA52F67H.js} +2 -2
- package/dist-cli/chunks/chunk-JKZY5CPQ.js +2 -0
- package/dist-cli/chunks/{chunk-MG6JE3AM.js → chunk-JV43GSZB.js} +2 -2
- package/dist-cli/chunks/{chunk-7L5IKIHC.js → chunk-JWZMAJC7.js} +1 -1
- package/dist-cli/chunks/{chunk-F53C5HAB.js → chunk-KIL5F7KD.js} +1 -1
- package/dist-cli/chunks/chunk-KY2KLPYL.js +1 -0
- package/dist-cli/chunks/{chunk-DB3GMVOH.js → chunk-LBXHG4AU.js} +2 -2
- package/dist-cli/chunks/{chunk-SBIAPF6E.js → chunk-MCMMRIJV.js} +1 -1
- package/dist-cli/chunks/{chunk-A4BOYB55.js → chunk-MWZOCZ7J.js} +2 -2
- package/dist-cli/chunks/{chunk-HXSV263V.js → chunk-NCXHUU7N.js} +1 -1
- package/dist-cli/chunks/{chunk-HTVB3FW2.js → chunk-NME4QWJ3.js} +2 -2
- package/dist-cli/chunks/{chunk-J2JAOIMN.js → chunk-OETQYAI2.js} +2 -2
- package/dist-cli/chunks/chunk-PJBYZZWR.js +2 -0
- package/dist-cli/chunks/{chunk-NU755HCE.js → chunk-PNPH4JLS.js} +1 -1
- package/dist-cli/chunks/chunk-RT3OS624.js +118 -0
- package/dist-cli/chunks/{chunk-7LDHOQ7B.js → chunk-SACWV3LZ.js} +20 -20
- package/dist-cli/chunks/chunk-SPJSTRXX.js +24 -0
- package/dist-cli/chunks/{chunk-3L5EXWCP.js → chunk-UYLESZU3.js} +1 -1
- package/dist-cli/chunks/{chunk-XESYTWFM.js → chunk-WWZL7HNB.js} +29 -29
- package/dist-cli/chunks/{chunk-T6MEERRI.js → chunk-ZBJ5JMXM.js} +2 -2
- package/dist-cli/chunks/chunk-ZWJEJR73.js +4 -0
- package/dist-cli/chunks/cli-version-V4GJYM74.js +2 -0
- package/dist-cli/chunks/{compat-MH673TEJ.js → compat-W7E4744D.js} +3 -3
- package/dist-cli/chunks/{config-SSCDV5ZJ.js → config-N7G65EY2.js} +2 -2
- package/dist-cli/chunks/{control-54R2XDLV.js → control-7VDFEVWU.js} +2 -2
- package/dist-cli/chunks/daemon-UI3PQ47F.js +2 -0
- package/dist-cli/chunks/{debug-PFP646II.js → debug-ZKUCSCOU.js} +3 -3
- package/dist-cli/chunks/{desktop-BPYYJ3EM.js → desktop-L3OXTT45.js} +3 -3
- package/dist-cli/chunks/{detox-BIUVHXYH.js → detox-QG4IBZAG.js} +2 -2
- package/dist-cli/chunks/{device-BNSPNIP6.js → device-QC6MDI7W.js} +2 -2
- package/dist-cli/chunks/{diagnose-BPTDSGOB.js → diagnose-HTUDENJK.js} +2 -2
- package/dist-cli/chunks/drivers-L2BXXDI7.js +2 -0
- package/dist-cli/chunks/{film-ILA6VSOG.js → film-CT7FHNLE.js} +3 -3
- package/dist-cli/chunks/flow-XUBXWJHL.js +2 -0
- package/dist-cli/chunks/help-7CIGANJN.js +2 -0
- package/dist-cli/chunks/{hidden-runtime-alias-TD7IVAPF.js → hidden-runtime-alias-YTUEPZ3U.js} +2 -2
- package/dist-cli/chunks/{home-paths-H36JDTNO.js → home-paths-7K6HDXOP.js} +2 -2
- package/dist-cli/chunks/inspect-ZY3QI7VJ.js +2 -0
- package/dist-cli/chunks/install-desktop-CVPRYFFV.js +2 -0
- package/dist-cli/chunks/{login-5WGZX2ZD.js → login-XHOOXAOW.js} +4 -4
- package/dist-cli/chunks/{logout-V53WTVIZ.js → logout-7BISNC5F.js} +2 -2
- package/dist-cli/chunks/{maestro-HGKYRD54.js → maestro-YD3G7LSG.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-6DNZSKDP.js → maestro-generate-WNOWZC5K.js} +3 -3
- package/dist-cli/chunks/{mode-YFMKORE5.js → mode-Y6JJMXU5.js} +2 -2
- package/dist-cli/chunks/optional-demo-registry-YUXURWKX.js +2 -0
- package/dist-cli/chunks/{perf-EOIGFDWM.js → perf-AY3ZKTDS.js} +2 -2
- package/dist-cli/chunks/{record-2F54QS6Y.js → record-ZC4W6QN6.js} +10 -10
- package/dist-cli/chunks/runtime-MVYAP3UX.js +2 -0
- package/dist-cli/chunks/screenshot-command-NRKTGTSJ.js +151 -0
- package/dist-cli/chunks/{screenshots-capture-DIICN24R.js → screenshots-capture-LZR3AHWN.js} +3 -3
- package/dist-cli/chunks/{serve-66NC6PPZ.js → serve-4X2WI5XF.js} +5 -5
- package/dist-cli/chunks/{setup-ZYN4BTBR.js → setup-KODPGJBJ.js} +3 -3
- package/dist-cli/chunks/{skills-MDBIER7U.js → skills-DEAUDOAN.js} +2 -2
- package/dist-cli/chunks/{storage-AQ3SEO4Y.js → storage-P7FQRH5T.js} +2 -2
- package/dist-cli/chunks/store-PTRXOHHV.js +2 -0
- package/dist-cli/chunks/telemetry-JHKOHSI2.js +2 -0
- package/dist-cli/chunks/{timeline-VEGHQZ36.js → timeline-7ISYXLR3.js} +2 -2
- package/dist-cli/chunks/{upgrade-RNDCRPLJ.js → upgrade-NZBJS5L2.js} +3 -3
- package/dist-cli/chunks/upload-J34H5SXV.js +2 -0
- package/dist-cli/chunks/{version-XUMPKQUM.js → version-J6BLGLTO.js} +2 -2
- package/dist-cli/chunks/{web-ESQGHVPD.js → web-Y6UVYM3Z.js} +2 -2
- package/dist-cli/chunks/{what-happened-6NJENKUF.js → what-happened-YKXXY6W2.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 +425 -169
- 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/dist-lib/vite.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! sootsim v0.1.
|
|
1
|
+
/*! sootsim v0.1.228 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sootsim",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.228",
|
|
4
4
|
"description": "sootsim CLI + vite/metro plugins + skills registry. bridge client for driving the proprietary sootsim-engine over WebSocket.",
|
|
5
5
|
"author": "Tamagui LLC",
|
|
6
6
|
"license": "MIT",
|
|
@@ -171,9 +171,9 @@
|
|
|
171
171
|
}
|
|
172
172
|
},
|
|
173
173
|
"devDependencies": {
|
|
174
|
-
"@
|
|
175
|
-
"@
|
|
176
|
-
"@
|
|
174
|
+
"@sootsim/compat": "workspace:*",
|
|
175
|
+
"@sootsim/globals": "workspace:*",
|
|
176
|
+
"@sootsim/skills": "workspace:*",
|
|
177
177
|
"@types/ws": "^8.5.13",
|
|
178
178
|
"esbuild": "^0.25.0",
|
|
179
179
|
"sootsim-engine": "workspace:*",
|
|
@@ -644,14 +644,14 @@ export interface ScanOptions {
|
|
|
644
644
|
buildIconProxyUrl?: (externalUrl: string) => string
|
|
645
645
|
}
|
|
646
646
|
|
|
647
|
-
function
|
|
647
|
+
function isSootSimSelfServer(server: DiscoveredServer): boolean {
|
|
648
648
|
const bundleId = server.bundleId?.trim().toLowerCase()
|
|
649
649
|
if (bundleId?.startsWith('dev.sootsim.')) return true
|
|
650
650
|
|
|
651
651
|
return false
|
|
652
652
|
}
|
|
653
653
|
|
|
654
|
-
export const
|
|
654
|
+
export const __isSootSimSelfServerForTests = isSootSimSelfServer
|
|
655
655
|
|
|
656
656
|
// per-(port, pid) probe cache. as long as the same process still owns the
|
|
657
657
|
// port, its signature endpoints can't have changed, so we return the cached
|
|
@@ -666,10 +666,10 @@ interface PortCacheEntry {
|
|
|
666
666
|
}
|
|
667
667
|
const portCache = new Map<number, PortCacheEntry>()
|
|
668
668
|
|
|
669
|
-
// negative-cache holds a node/bun port that isn't a bundler (e.g.
|
|
669
|
+
// negative-cache holds a node/bun port that isn't a bundler (e.g. Contrast's own
|
|
670
670
|
// api server, zero, postgres, a dev tool). at scan interval 3s and a 1.5s ttl
|
|
671
671
|
// we used to re-probe every scan, which hits `/` on the port every 3s — that
|
|
672
|
-
// waking
|
|
672
|
+
// waking Contrast's SSR router ~20x/min was a real memory-leak driver. 30s gives
|
|
673
673
|
// the scanner a much lighter touch on unrelated node processes while still
|
|
674
674
|
// catching pid-change invalidation immediately when a process restarts.
|
|
675
675
|
const NEGATIVE_CACHE_TTL_MS = 30_000
|
|
@@ -796,5 +796,5 @@ export async function scanDevServers(
|
|
|
796
796
|
if (!livePids.has(pid)) cwdByPid.delete(pid)
|
|
797
797
|
}
|
|
798
798
|
|
|
799
|
-
return results.filter((r) => !
|
|
799
|
+
return results.filter((r) => !isSootSimSelfServer(r))
|
|
800
800
|
}
|
package/scripts/postinstall.cjs
CHANGED
|
@@ -17,7 +17,7 @@ if (process.env.SOOTSIM_SKIP_POSTINSTALL === '1') {
|
|
|
17
17
|
process.exit(0)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
// in dev / workspace installs (i.e. running inside the
|
|
20
|
+
// in dev / workspace installs (i.e. running inside the Contrast monorepo, not
|
|
21
21
|
// installed as a dep), the package dir is `<repo>/packages/sootsim/` rather
|
|
22
22
|
// than `.../node_modules/sootsim/`. skip the runtime fetch in that case —
|
|
23
23
|
// the workspace already has the engine source.
|
package/scripts/preuninstall.cjs
CHANGED
|
@@ -29,7 +29,7 @@ if (process.env.SOOTSIM_SKIP_PREUNINSTALL === '1') {
|
|
|
29
29
|
process.exit(0)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
// dev / workspace installs (running inside the
|
|
32
|
+
// dev / workspace installs (running inside the Contrast monorepo, not in a
|
|
33
33
|
// node_modules tree) — don't tear anything down. the developer is rebuilding
|
|
34
34
|
// the workspace, not actually uninstalling.
|
|
35
35
|
const pkgDir = path.resolve(__dirname, '..')
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: contrast
|
|
3
3
|
description: >-
|
|
4
|
-
index of all
|
|
4
|
+
index of all Contrast and SootSim agent skills and CLI commands.
|
|
5
5
|
auto-generated from the CLI info registry — do not edit by hand.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
#
|
|
8
|
+
# contrast
|
|
9
9
|
|
|
10
10
|
overview of all available skills and CLI commands in this repo.
|
|
11
11
|
|
|
12
|
-
> this file is auto-generated by `bun scripts/generate-
|
|
12
|
+
> this file is auto-generated by `bun scripts/generate-contrast-skill.ts`.
|
|
13
13
|
> to update, edit the source registries and re-run the generator.
|
|
14
14
|
|
|
15
15
|
## sootsim CLI commands
|
|
@@ -23,7 +23,9 @@ indict, remeasure — and every wasted week came from skipping a step.
|
|
|
23
23
|
browser matters just as much: playwright's bundled headless chromium has
|
|
24
24
|
NO GPU — CanvasKit falls back to SwiftShader and every number lies high.
|
|
25
25
|
use `--browser webkit` (real Apple GPU even headless, the iOS proxy) or
|
|
26
|
-
GPU-backed Chrome
|
|
26
|
+
GPU-backed Chrome via `launchReapedChrome` from `scripts/lib/reap-browser`,
|
|
27
|
+
never a bare `chromium.launch()`. never `channel: 'chrome'` — that is the
|
|
28
|
+
user's own browser, and automation launching it hijacks their Dock events.
|
|
27
29
|
2. **machinery silently not engaging is the default failure mode.** the
|
|
28
30
|
raster tier compiled, gated, and did *nothing* for months — zero
|
|
29
31
|
promotions on its textbook case — until per-gate rejection counters
|
|
@@ -37,7 +37,7 @@ export type SharedDesktopAuthSession = {
|
|
|
37
37
|
|
|
38
38
|
const SESSION_VERSION = 1 as const
|
|
39
39
|
const SESSION_FILE_ENV = 'SOOTSIM_SHARED_AUTH_FILE'
|
|
40
|
-
const DEFAULT_ORIGIN = 'https://
|
|
40
|
+
const DEFAULT_ORIGIN = 'https://contrast.dev'
|
|
41
41
|
|
|
42
42
|
function getBaseDir() {
|
|
43
43
|
const explicit = process.env[SESSION_FILE_ENV]
|
package/src/backend-origin.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const DEFAULT_CONTRAST_BACKEND_ORIGIN = 'https://contrast.dev'
|
|
2
2
|
|
|
3
3
|
function normalizeHostname(hostname: string): string {
|
|
4
4
|
return hostname.replace(/^\[|\]$/g, '').toLowerCase()
|
|
@@ -15,35 +15,35 @@ export function isLoopbackHost(hostname: string): boolean {
|
|
|
15
15
|
)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export function
|
|
18
|
+
export function isContrastBackendHost(hostname: string): boolean {
|
|
19
19
|
const normalized = normalizeHostname(hostname)
|
|
20
20
|
return (
|
|
21
21
|
isLoopbackHost(normalized) ||
|
|
22
|
-
normalized === '
|
|
23
|
-
normalized.endsWith('.
|
|
22
|
+
normalized === 'contrast.dev' ||
|
|
23
|
+
normalized.endsWith('.contrast.dev')
|
|
24
24
|
)
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function
|
|
27
|
+
export function isContrastBackendOrigin(origin: string): boolean {
|
|
28
28
|
try {
|
|
29
|
-
return
|
|
29
|
+
return isContrastBackendHost(new URL(origin).hostname)
|
|
30
30
|
} catch {
|
|
31
31
|
return false
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export function
|
|
35
|
+
export function resolveContrastBackendOrigin(currentOrigin?: string | null): string {
|
|
36
36
|
const origin = currentOrigin?.trim()
|
|
37
|
-
if (!origin) return
|
|
37
|
+
if (!origin) return DEFAULT_CONTRAST_BACKEND_ORIGIN
|
|
38
38
|
|
|
39
39
|
try {
|
|
40
40
|
const parsed = new URL(origin)
|
|
41
|
-
if (isLoopbackHost(parsed.hostname) ||
|
|
41
|
+
if (isLoopbackHost(parsed.hostname) || isContrastBackendHost(parsed.hostname)) {
|
|
42
42
|
return parsed.origin
|
|
43
43
|
}
|
|
44
44
|
} catch {}
|
|
45
45
|
|
|
46
|
-
return
|
|
46
|
+
return DEFAULT_CONTRAST_BACKEND_ORIGIN
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
export {
|
|
49
|
+
export { DEFAULT_CONTRAST_BACKEND_ORIGIN }
|
package/src/bridge-constants.ts
CHANGED
|
@@ -42,11 +42,11 @@ export function resolveSootsimBridgePortForHttpPort(input: {
|
|
|
42
42
|
return resolveSootsimBridgePort({ portOffset })
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
const
|
|
45
|
+
const CONTRAST_DEV_HTTP_BASE_PORT = 3000
|
|
46
46
|
const SOOTSIM_SITE_DEV_HTTP_BASE_PORT = 3001
|
|
47
47
|
const SOOTSIM_SHELL_DEV_HTTP_BASE_PORT = 5173
|
|
48
48
|
const LOCAL_DEV_HTTP_BASE_PORTS = [
|
|
49
|
-
|
|
49
|
+
CONTRAST_DEV_HTTP_BASE_PORT,
|
|
50
50
|
SOOTSIM_SHELL_DEV_HTTP_BASE_PORT,
|
|
51
51
|
]
|
|
52
52
|
const MAX_LOCAL_DEV_PORT_OFFSET = 2000
|
|
@@ -88,7 +88,7 @@ export function resolveSootsimBridgePortForRuntimeLocation(
|
|
|
88
88
|
) {
|
|
89
89
|
return resolveSootsimBridgePortForHttpPort({
|
|
90
90
|
httpPort,
|
|
91
|
-
baseHttpPort:
|
|
91
|
+
baseHttpPort: CONTRAST_DEV_HTTP_BASE_PORT,
|
|
92
92
|
})
|
|
93
93
|
}
|
|
94
94
|
if (
|
package/src/home-paths.ts
CHANGED
|
@@ -39,7 +39,7 @@ export function sootsimHomeDir(): string {
|
|
|
39
39
|
return path.join(homedir(), '.sootsim')
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
// detect when sootsim is running from a source checkout (the
|
|
42
|
+
// detect when sootsim is running from a source checkout (the Contrast monorepo)
|
|
43
43
|
// rather than a published npm install. used to skip auto-install of the
|
|
44
44
|
// persistent launchd / systemd agent: dev shells shouldn't register an agent
|
|
45
45
|
// whose Program path points at workspace artifacts that change between
|
package/src/host/agent-host.ts
CHANGED
|
@@ -63,7 +63,7 @@ interface PendingPromptEcho {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
export interface AgentHostOptions {
|
|
66
|
-
/** callback so hosts with a
|
|
66
|
+
/** callback so hosts with a Contrast-env context (electron, dev middleware)
|
|
67
67
|
* can inject their own exclude list. defaults to the env-derived list
|
|
68
68
|
* the rest of the repo uses, so `sootsim serve` standalone does the
|
|
69
69
|
* right thing. */
|
|
@@ -79,7 +79,7 @@ export interface AgentCliLeaseSnapshot {
|
|
|
79
79
|
expiresAt: number
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/** default excludes keep the dev-server scanner from hitting
|
|
82
|
+
/** default excludes keep the dev-server scanner from hitting Contrast's own
|
|
83
83
|
* web / zero / postgres / r2 ports. mirrored from
|
|
84
84
|
* sootsim-engine/src/dev-scan-excludes.ts — duplicated rather than
|
|
85
85
|
* imported because sootsim is a dependency of sootsim-engine, not the
|
package/src/host/bridge-host.ts
CHANGED
|
@@ -65,12 +65,12 @@ export interface BridgeSimInfo {
|
|
|
65
65
|
visibilityState?: string
|
|
66
66
|
documentFocused?: boolean
|
|
67
67
|
/** registration "kind" — lets a single daemon host multiple surface
|
|
68
|
-
* types. omitted / unknown defaults to 'sootsim'. the
|
|
69
|
-
* IDE registers with kind='
|
|
68
|
+
* types. omitted / unknown defaults to 'sootsim'. the contrast web
|
|
69
|
+
* IDE registers with kind='contrast'; CLI consumers filter by this
|
|
70
70
|
* rather than running a parallel bridge. */
|
|
71
71
|
kind?: string
|
|
72
72
|
/** opaque metadata supplied at register time (e.g. projectId, route,
|
|
73
|
-
* attached iOS sim id for a
|
|
73
|
+
* attached iOS sim id for a contrast tab). free-form by design — the
|
|
74
74
|
* daemon does not interpret it, only stores + reports it back. */
|
|
75
75
|
meta?: Record<string, unknown>
|
|
76
76
|
}
|
|
@@ -208,7 +208,7 @@ interface BridgeForwardedCommand {
|
|
|
208
208
|
export interface BridgeHostOptions {
|
|
209
209
|
port?: number
|
|
210
210
|
openUrl?: (url: string, options?: OpenUrlOptions) => Promise<void> | void
|
|
211
|
-
/**
|
|
211
|
+
/** Contrast-specific excludes for the agent host's dev-server scan. passed
|
|
212
212
|
* through to AgentHost; see packages/sootsim-engine/src/dev-scan-excludes.ts
|
|
213
213
|
* for the canonical list. when omitted, AgentHost reads env vars directly. */
|
|
214
214
|
agentScanExcludes?: AgentHostOptions['getExcludePorts']
|
|
@@ -231,15 +231,15 @@ export interface BridgeHostOptions {
|
|
|
231
231
|
* fills in once the vite server is actually listening. only meaningful
|
|
232
232
|
* with writeDevLockfile. */
|
|
233
233
|
getShellPort?: () => number | null
|
|
234
|
-
/** the
|
|
234
|
+
/** the contrast origin this daemon's runtimes should talk to for auth,
|
|
235
235
|
* billing, and preview uploads — inlined into served runtime html as
|
|
236
|
-
* `window.
|
|
236
|
+
* `window.__sootsimContrastOrigin`. without it the engine's origin.ts
|
|
237
237
|
* resolves the daemon's own loopback host (localhost:<runtimePort>) to
|
|
238
238
|
* the dev `localhost:3000` stack, so a *prod* CLI user's preview
|
|
239
239
|
* recording would upload to a localhost stack that doesn't exist. the
|
|
240
240
|
* `sootsim serve` command resolves this with the same probe the
|
|
241
241
|
* upload itself uses (`resolveDefaultUploadOrigin`). */
|
|
242
|
-
|
|
242
|
+
contrastOrigin?: string
|
|
243
243
|
/** override the abandoned-sim GC TTL (ms). defaults to
|
|
244
244
|
* SIM_IDLE_REAP_TTL_MS (30 min). exists so the reaper integration test
|
|
245
245
|
* can drive the real timer path deterministically instead of mocking
|
|
@@ -302,7 +302,7 @@ const HTTP_MIME_TYPES: Record<string, string> = {
|
|
|
302
302
|
* hardcode the 7668 default in `resolveBridgePort` and register on a
|
|
303
303
|
* *different* daemon (or none), so `sootsim open` times out waiting for
|
|
304
304
|
* a sim that connected to the wrong bridge.
|
|
305
|
-
* - `window.
|
|
305
|
+
* - `window.__sootsimContrastOrigin` — the contrast origin for auth /
|
|
306
306
|
* billing / preview uploads. the daemon serves runtimes from its own
|
|
307
307
|
* loopback host, and engine `origin.ts` otherwise resolves any
|
|
308
308
|
* loopback host to the dev `localhost:3000` stack — so without this a
|
|
@@ -311,7 +311,7 @@ const HTTP_MIME_TYPES: Record<string, string> = {
|
|
|
311
311
|
export function injectSharedConfigIntoHtml(
|
|
312
312
|
data: Buffer,
|
|
313
313
|
bridgePort: number,
|
|
314
|
-
|
|
314
|
+
contrastOrigin: string | null,
|
|
315
315
|
): string {
|
|
316
316
|
let payload: string
|
|
317
317
|
try {
|
|
@@ -321,13 +321,13 @@ export function injectSharedConfigIntoHtml(
|
|
|
321
321
|
payload = '{}'
|
|
322
322
|
}
|
|
323
323
|
const bridgePortTag = bridgePort > 0 ? `window.__sootsimBridgePort=${bridgePort};` : ''
|
|
324
|
-
const
|
|
325
|
-
? `window.
|
|
324
|
+
const contrastOriginTag = contrastOrigin
|
|
325
|
+
? `window.__sootsimContrastOrigin=${JSON.stringify(contrastOrigin)};`
|
|
326
326
|
: ''
|
|
327
327
|
const tag =
|
|
328
328
|
`<script>window.__sootsimSharedConfig=${payload};` +
|
|
329
329
|
bridgePortTag +
|
|
330
|
-
|
|
330
|
+
contrastOriginTag +
|
|
331
331
|
`window.__sootsimCliVersion=${JSON.stringify(getCliVersion())};</script>`
|
|
332
332
|
const html = data.toString('utf8')
|
|
333
333
|
if (html.includes('<head>')) return html.replace('<head>', `<head>${tag}`)
|
|
@@ -405,7 +405,7 @@ export class SootSimBridgeHost {
|
|
|
405
405
|
private shouldWriteLockfile: boolean
|
|
406
406
|
private shouldWriteDevLockfile: boolean
|
|
407
407
|
private getShellPort: (() => number | null) | null
|
|
408
|
-
private
|
|
408
|
+
private contrastOrigin: string | null = null
|
|
409
409
|
private effectivePort = 0
|
|
410
410
|
private startedAt = 0
|
|
411
411
|
private heartbeatTimer: NodeJS.Timeout | null = null
|
|
@@ -462,7 +462,7 @@ export class SootSimBridgeHost {
|
|
|
462
462
|
: null
|
|
463
463
|
},
|
|
464
464
|
})
|
|
465
|
-
this.
|
|
465
|
+
this.contrastOrigin = opts.contrastOrigin?.replace(/\/$/, '') || null
|
|
466
466
|
this.simIdleReapTtlMs =
|
|
467
467
|
opts.simIdleReapTtlMs ?? SootSimBridgeHost.SIM_IDLE_REAP_TTL_MS
|
|
468
468
|
this.automationSimIdleReapTtlMs =
|
|
@@ -708,9 +708,9 @@ export class SootSimBridgeHost {
|
|
|
708
708
|
|
|
709
709
|
// Browsers always send an Origin header, but the IDE needs a second,
|
|
710
710
|
// short-lived CLI-role socket to route typed app_* calls to an
|
|
711
|
-
//
|
|
712
|
-
// CLI handshake, so let it correct the provisional
|
|
713
|
-
// before this socket has registered a page. This reuses the existing
|
|
711
|
+
// Contrast-dev-owned external runtime driver. `bridge:hello` is
|
|
712
|
+
// already the CLI handshake, so let it correct the provisional
|
|
713
|
+
// Origin-based role before this socket has registered a page. This reuses the existing
|
|
714
714
|
// daemon transport and call envelope; it does not add a second routing
|
|
715
715
|
// surface or let a registered sim switch roles mid-connection.
|
|
716
716
|
if (role === 'sim' && msg.type === 'bridge:hello' && sim && !sim.url) {
|
|
@@ -1219,7 +1219,7 @@ export class SootSimBridgeHost {
|
|
|
1219
1219
|
private refreshActiveRuntime() {
|
|
1220
1220
|
this.activeRuntimeVersion = readActiveRuntime()
|
|
1221
1221
|
// mode boundary (see runtime-assets resolveDevCheckoutRuntimeRoot): inside
|
|
1222
|
-
// the
|
|
1222
|
+
// the Contrast monorepo dev checkout serve the fresh dev-stack build so local
|
|
1223
1223
|
// engine edits reach previews without a publish; otherwise the installed
|
|
1224
1224
|
// runtime. the version above stays the installed version for display.
|
|
1225
1225
|
this.activeRuntimeDirPath = resolveDevCheckoutRuntimeRoot() ?? getActiveRuntimeDir()
|
|
@@ -1399,7 +1399,7 @@ export class SootSimBridgeHost {
|
|
|
1399
1399
|
// ("render worker crashed during boot" / "app:one surface registration
|
|
1400
1400
|
// failed"). SAB powers the shell-scene fast channel and the worklet
|
|
1401
1401
|
// runtime's SharedValues. mirrors sootsim-shell/vite.config.ts (dev) and
|
|
1402
|
-
//
|
|
1402
|
+
// contrast.dev app/_middleware.ts (prod) so the cli daemon serves the
|
|
1403
1403
|
// same surface those two already enforce. set via setHeader so subsequent
|
|
1404
1404
|
// writeHead() calls preserve them (writeHead only overrides keys it
|
|
1405
1405
|
// explicitly passes). Safari/iOS WebKit does not isolate documents for
|
|
@@ -1683,7 +1683,7 @@ export class SootSimBridgeHost {
|
|
|
1683
1683
|
return
|
|
1684
1684
|
}
|
|
1685
1685
|
res.end(
|
|
1686
|
-
injectSharedConfigIntoHtml(data, this.effectivePort, this.
|
|
1686
|
+
injectSharedConfigIntoHtml(data, this.effectivePort, this.contrastOrigin),
|
|
1687
1687
|
)
|
|
1688
1688
|
})
|
|
1689
1689
|
return
|
|
@@ -1714,7 +1714,7 @@ export class SootSimBridgeHost {
|
|
|
1714
1714
|
return
|
|
1715
1715
|
}
|
|
1716
1716
|
res.end(
|
|
1717
|
-
injectSharedConfigIntoHtml(data, this.effectivePort, this.
|
|
1717
|
+
injectSharedConfigIntoHtml(data, this.effectivePort, this.contrastOrigin),
|
|
1718
1718
|
)
|
|
1719
1719
|
})
|
|
1720
1720
|
return
|
|
@@ -13,7 +13,7 @@ function isNativeDevBundlePath(pathname: string): boolean {
|
|
|
13
13
|
return pathname.endsWith('.bundle')
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
// opt-in (
|
|
16
|
+
// opt-in (contrastbot preview-recording context only): serve a production
|
|
17
17
|
// minified bundle instead of the default dev=true unminified one. a
|
|
18
18
|
// preview run just records the app — it never needs fast-refresh or dev
|
|
19
19
|
// warnings — and the ~28MB unminified dev bundle cold-(re)boots
|
|
@@ -33,7 +33,7 @@ function applyPreviewProdBundle(parsed: URL): void {
|
|
|
33
33
|
export function normalizeNativeDevBundleUrl(bundleUrl: string): string {
|
|
34
34
|
try {
|
|
35
35
|
const isAbsolute = isAbsoluteHttpUrl(bundleUrl)
|
|
36
|
-
const parsed = new URL(bundleUrl, 'http://
|
|
36
|
+
const parsed = new URL(bundleUrl, 'http://sootsim.local')
|
|
37
37
|
parsed.pathname = parsed.pathname.replace(/\.\.bundle$/, '.bundle')
|
|
38
38
|
if (!isNativeDevBundlePath(parsed.pathname)) return bundleUrl
|
|
39
39
|
// do NOT force dev=true or minify=false — metro's dev server already
|
|
@@ -85,7 +85,7 @@ export function normalizeNativeDevBundleUrl(bundleUrl: string): string {
|
|
|
85
85
|
export function applyHotMode(bundleUrl: string, hot: boolean): string {
|
|
86
86
|
try {
|
|
87
87
|
const isAbsolute = isAbsoluteHttpUrl(bundleUrl)
|
|
88
|
-
const parsed = new URL(bundleUrl, 'http://
|
|
88
|
+
const parsed = new URL(bundleUrl, 'http://sootsim.local')
|
|
89
89
|
if (!isNativeDevBundlePath(parsed.pathname)) return bundleUrl
|
|
90
90
|
parsed.searchParams.set('hot', hot ? 'true' : 'false')
|
|
91
91
|
if (isAbsolute) return parsed.toString()
|
package/src/runtime-assets.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const SOOTSIM_RUNTIME_MISSING_MESSAGE =
|
|
|
12
12
|
|
|
13
13
|
// --- dev-checkout mode boundary --------------------------------------------
|
|
14
14
|
//
|
|
15
|
-
// there is exactly one rule for which engine gets served: INSIDE the
|
|
15
|
+
// there is exactly one rule for which engine gets served: INSIDE the Contrast
|
|
16
16
|
// monorepo dev checkout the fresh dev-stack build (public/sootsim, kept current
|
|
17
17
|
// by `bun dev`'s watch:sootsim) IS the engine; OUTSIDE it (a published CLI in a
|
|
18
18
|
// user's project, or an isolated home) the installed runtime IS. this is a mode
|
|
@@ -33,7 +33,7 @@ let _monorepoRootCache: string | null | undefined
|
|
|
33
33
|
function findMonorepoRoot(): string | null {
|
|
34
34
|
if (_monorepoRootCache !== undefined) return _monorepoRootCache
|
|
35
35
|
let dir = path.dirname(fileURLToPath(import.meta.url))
|
|
36
|
-
// walk up until we find the
|
|
36
|
+
// walk up until we find the Contrast monorepo: it has packages/sootsim-engine and
|
|
37
37
|
// a public/sootsim dev-build output slot. bounded by the filesystem root.
|
|
38
38
|
for (let i = 0; i < 12 && dir !== path.dirname(dir); i++) {
|
|
39
39
|
if (
|
|
@@ -49,7 +49,7 @@ function findMonorepoRoot(): string | null {
|
|
|
49
49
|
return null
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
/** the fresh dev-stack engine build to serve when running inside the
|
|
52
|
+
/** the fresh dev-stack engine build to serve when running inside the Contrast
|
|
53
53
|
* monorepo dev checkout, or null when the installed runtime should serve
|
|
54
54
|
* (published CLI, packaged electron, or an isolated SOOTSIM_HOME). */
|
|
55
55
|
export function resolveDevCheckoutRuntimeRoot(): string | null {
|
package/src/runtime-delivery.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
writeActiveRuntime,
|
|
15
15
|
} from './home-paths'
|
|
16
16
|
|
|
17
|
-
export const DEFAULT_RUNTIME_CDN_ORIGIN = 'https://
|
|
17
|
+
export const DEFAULT_RUNTIME_CDN_ORIGIN = 'https://contrast.dev'
|
|
18
18
|
export const RUNTIME_CDN_ORIGIN_ENV = 'SOOTSIM_CDN_ORIGIN'
|
|
19
19
|
export const RUNTIME_CHANNEL_ENV = 'SOOTSIM_RUNTIME_CHANNEL'
|
|
20
20
|
export const RUNTIME_PIN_ENV = 'SOOTSIM_RUNTIME_VERSION'
|
|
@@ -37,7 +37,7 @@ export interface RuntimeManifest {
|
|
|
37
37
|
|
|
38
38
|
/** website URL where the sootsim changelog is rendered. the CLI used to drain
|
|
39
39
|
* a paginated /api/changelog/sootsim endpoint here; the changelog is now a
|
|
40
|
-
* static page checked into the
|
|
40
|
+
* static page checked into the Contrast repo (one MDX file per runtime release
|
|
41
41
|
* under src/features/site/changelog/sootsim-entries/), so `sootsim runtime
|
|
42
42
|
* notes` points users at the page. */
|
|
43
43
|
export const SOOTSIM_CHANGELOG_URL = 'https://sootsim.com/changelog'
|
|
@@ -137,7 +137,8 @@ export function resolveRuntimeVersion(
|
|
|
137
137
|
opts: Pick<RuntimeInstallOptions, 'version' | 'channel'> = {},
|
|
138
138
|
): { version: string; channel: string; entry: RuntimeManifestVersion } {
|
|
139
139
|
const channel = resolveRuntimeChannel(opts.channel)
|
|
140
|
-
const version =
|
|
140
|
+
const version =
|
|
141
|
+
opts.version || resolveRuntimePin() || manifest.channels[channel]?.latest
|
|
141
142
|
if (!version) {
|
|
142
143
|
throw new Error(
|
|
143
144
|
`no version specified and channel '${channel}' has no latest entry in the manifest`,
|
|
@@ -242,7 +243,12 @@ export async function updateRuntimeToLatest(
|
|
|
242
243
|
latestVersion: pin,
|
|
243
244
|
}
|
|
244
245
|
}
|
|
245
|
-
const install = await installRuntime({
|
|
246
|
+
const install = await installRuntime({
|
|
247
|
+
version: pin,
|
|
248
|
+
channel,
|
|
249
|
+
cdnOrigin,
|
|
250
|
+
setActive: false,
|
|
251
|
+
})
|
|
246
252
|
return {
|
|
247
253
|
checked: true,
|
|
248
254
|
updated: true,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, writeFileSync } from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
|
+
import { launchReapedChrome } from '../../../../scripts/lib/reap-browser'
|
|
3
4
|
import {
|
|
4
5
|
DEFAULT_FRAME_SHADOW,
|
|
5
6
|
toAssetKey,
|
|
@@ -346,7 +347,9 @@ export async function composeMarketingScreenshots(
|
|
|
346
347
|
browserOverride ??
|
|
347
348
|
(await (async () => {
|
|
348
349
|
const { chromium } = await import('playwright')
|
|
349
|
-
return chromium.launch({
|
|
350
|
+
return launchReapedChrome((options) => chromium.launch(options), {
|
|
351
|
+
headless: true,
|
|
352
|
+
})
|
|
350
353
|
})())
|
|
351
354
|
try {
|
|
352
355
|
const context = await browser.newContext({ viewport: { width: 1280, height: 720 } })
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
SIDE_BUTTON_RESERVE,
|
|
9
9
|
} from 'sootsim-engine/ios/chrome-metrics'
|
|
10
10
|
import { getDevice, type DeviceModel } from 'sootsim-engine/settings'
|
|
11
|
+
import { launchReapedChrome } from '../../../../scripts/lib/reap-browser'
|
|
11
12
|
|
|
12
13
|
const METALLIC_RING_SHADOW =
|
|
13
14
|
'inset 0 0 0 0.5px #000, inset 0 0 0 2px #757575, inset 0 0 0 5px #212121'
|
|
@@ -363,7 +364,9 @@ export async function composeFramedScreenshot(
|
|
|
363
364
|
model: DeviceModel,
|
|
364
365
|
): Promise<Buffer> {
|
|
365
366
|
const { chromium } = await import('playwright')
|
|
366
|
-
const browser = await chromium.launch({
|
|
367
|
+
const browser = await launchReapedChrome((options) => chromium.launch(options), {
|
|
368
|
+
headless: true,
|
|
369
|
+
})
|
|
367
370
|
try {
|
|
368
371
|
const composer = await createFramedScreenshotComposer(browser)
|
|
369
372
|
try {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, writeFileSync } from 'fs'
|
|
2
2
|
import { tmpdir } from 'os'
|
|
3
3
|
import path from 'path'
|
|
4
|
+
import { launchReapedChrome } from '../../../../scripts/lib/reap-browser'
|
|
4
5
|
import { runFlowPlayback } from '../../cli/commands/flow'
|
|
5
6
|
import {
|
|
6
7
|
composeMarketingScreenshots,
|
|
@@ -103,7 +104,9 @@ async function buildFramedSources(
|
|
|
103
104
|
browserOverride ??
|
|
104
105
|
(await (async () => {
|
|
105
106
|
const { chromium } = await import('playwright')
|
|
106
|
-
return chromium.launch({
|
|
107
|
+
return launchReapedChrome((options) => chromium.launch(options), {
|
|
108
|
+
headless: true,
|
|
109
|
+
})
|
|
107
110
|
})())
|
|
108
111
|
try {
|
|
109
112
|
const composer = await createFramedScreenshotComposer(browser)
|
|
@@ -204,7 +207,9 @@ export async function runScreenshotsPlan(
|
|
|
204
207
|
const sharedBrowser = needsSharedBrowser
|
|
205
208
|
? await (async () => {
|
|
206
209
|
const { chromium } = await import('playwright')
|
|
207
|
-
return chromium.launch({
|
|
210
|
+
return launchReapedChrome((options) => chromium.launch(options), {
|
|
211
|
+
headless: true,
|
|
212
|
+
})
|
|
208
213
|
})()
|
|
209
214
|
: null
|
|
210
215
|
try {
|
package/src/vite-plugin-one.ts
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from './runtime-assets'
|
|
19
19
|
import type { Plugin } from 'vite'
|
|
20
20
|
|
|
21
|
-
export interface
|
|
21
|
+
export interface SootSimPluginOptions {
|
|
22
22
|
// custom bundle URL (default: auto-detect from One's metro)
|
|
23
23
|
bundleUrl?: string
|
|
24
24
|
// path prefix (default: '/__soot')
|
|
@@ -27,7 +27,7 @@ export interface SootPluginOptions {
|
|
|
27
27
|
enabled?: boolean
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
export function sootsimPlugin(options:
|
|
30
|
+
export function sootsimPlugin(options: SootSimPluginOptions = {}): Plugin[] {
|
|
31
31
|
if (options.enabled === false) return []
|
|
32
32
|
|
|
33
33
|
const prefix = options.prefix || '/__soot'
|
|
@@ -45,7 +45,7 @@ export function sootsimPlugin(options: SootPluginOptions = {}): Plugin[] {
|
|
|
45
45
|
// app's bundle) AND window.__sootsimBridgePort. the bridge-port injection
|
|
46
46
|
// mirrors the dev-stack bridge-host: without it the engine's
|
|
47
47
|
// resolveBridgePort() falls back to a location-derived guess that mis-maps
|
|
48
|
-
// a non-
|
|
48
|
+
// a non-Contrast app port (the mobile template's :4400 -> 9068) and the sim
|
|
49
49
|
// can't reach the bridge. resolved per-request from the daemon lockfile
|
|
50
50
|
// (the CLI's `open` writes/uses it before the shell loads) so the engine
|
|
51
51
|
// targets the SAME bridge the CLI does; default port otherwise.
|
package/src/vite-plugin.ts
CHANGED
|
@@ -25,7 +25,7 @@ import { shouldApplyWorkletsPlugin, transformWorkletsCode } from './worklets-bab
|
|
|
25
25
|
|
|
26
26
|
const sootsimPluginRequire = createRequire(import.meta.url)
|
|
27
27
|
|
|
28
|
-
export interface
|
|
28
|
+
export interface SootSimOptions {
|
|
29
29
|
// directory of an external RN app to load
|
|
30
30
|
app?: string
|
|
31
31
|
// additional source directories to treat as app sources (metro transforms apply)
|
|
@@ -39,7 +39,7 @@ const sootsimRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '
|
|
|
39
39
|
const workspaceRoot = path.resolve(sootsimRoot, '..', '..')
|
|
40
40
|
const workspaceNodeModules = path.resolve(workspaceRoot, 'node_modules')
|
|
41
41
|
const workspaceTamaguiDir = path.resolve(workspaceNodeModules, '@tamagui')
|
|
42
|
-
// stubs and the mutable React bridge live in @
|
|
42
|
+
// stubs and the mutable React bridge live in @sootsim/compat.
|
|
43
43
|
const compatRoot = path.resolve(sootsimRoot, '..', 'compat')
|
|
44
44
|
|
|
45
45
|
// the rendering engine and its shims live in the private sootsim-engine
|
|
@@ -235,7 +235,7 @@ function moduleExistsIn(pkgName: string, dir: string): boolean {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
export function sootsim(options:
|
|
238
|
+
export function sootsim(options: SootSimOptions = {}): Plugin[] {
|
|
239
239
|
const appDir = options.app ? path.resolve(options.app) : ''
|
|
240
240
|
const extraSources = (options.sources || []).map((s) => path.resolve(s))
|
|
241
241
|
const ownedPackages = new Set([...coreOwnedPackages, ...(options.ownedPackages || [])])
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.227 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import{c as f}from"./chunk-J7IFVTWX.js";import{a as C,d as x,f as F}from"./chunk-E75EBS77.js";import{M as y,N as w,O as I,P as B}from"./chunk-NU755HCE.js";import{WebSocket as k}from"ws";var S=null;function E(){return S||(S=O(),S)}function O(){let i=w();if(i&&B(i))return{source:"dev-bridge",bridgePort:i.bridgePort,shellBaseUrl:i.shellPort?`http://localhost:${i.shellPort}/`:f};let r=y();return r&&I(r)?{source:"daemon",bridgePort:r.bridgePort,shellBaseUrl:r.runtimePort>0?`http://localhost:${r.runtimePort}/`:f}:{source:"default",bridgePort:7668,shellBaseUrl:f}}function M(i){let r=w();if(r&&B(r)&&r.bridgePort===i)return{source:"dev-bridge",shellBaseUrl:r.shellPort?`http://localhost:${r.shellPort}/`:f};let e=y();return e&&I(e)&&e.bridgePort===i?{source:"daemon",shellBaseUrl:e.runtimePort>0?`http://localhost:${e.runtimePort}/`:f}:null}function J(i){let r;try{let l=new URL(i);r=l.port?Number(l.port):l.protocol==="https:"?443:80}catch{return null}if(!Number.isFinite(r)||r<=0)return null;let e=w();if(e&&B(e)&&(e.shellPort===r||e.runtimePort===r||e.bridgePort===r))return e.bridgePort;let d=y();return d&&I(d)&&(d.runtimePort===r||d.bridgePort===r)?d.bridgePort:null}function U(){return E().bridgePort}function G(i,r){let e=M(i),d=e?.source==="dev-bridge"?"dev bridge":e?.source==="daemon"?"daemon bridge":"bridge",l=e?`${e.source}.json`:"no lockfile",t=r??e?.shellBaseUrl;process.stderr.write(` \u2192 ${d} :${i} (${t?`shell ${t}, `:""}${l})
|
|
3
|
-
`)}var T=class extends Error{lock;constructor(r,e){super(r),this.name="BridgeSimLockedError",this.lock=e}};function _(i){return i==="flag"?"via --sim":i==="saved"?"saved via `sootsim use`":"primary fallback \u2014 no sim pinned"}function K(i,r={}){let e=new Set,d=r.port??U(),l=r.commandTimeoutMs??15e3,t,m="none",P=new Set(r.stripBooleanFlags??[]),h=new Set(r.stripValueFlags??[]);for(let n=0;n<i.length;n++){let a=i[n];if(a==="--port"||a==="-p"){e.add(n),n+1<i.length&&(e.add(n+1),d=Number(i[n+1])),n++;continue}if(a.startsWith("--port=")){e.add(n),d=Number(a.slice(7));continue}if(a==="--timeout"){e.add(n),n+1<i.length&&(e.add(n+1),l=Number(i[n+1])),n++;continue}if(a==="--sim"||a==="--session"||a==="--tab"){e.add(n),n+1<i.length&&(e.add(n+1),t=i[n+1]?.trim()||void 0,m="flag"),n++;continue}let s=a.startsWith("--sim=")?a.slice(6):a.startsWith("--session=")?a.slice(10):a.startsWith("--tab=")?a.slice(6):void 0;if(s!==void 0){e.add(n),t=s.trim()||void 0,m="flag";continue}if(P.has(a)){e.add(n);continue}h.has(a)&&(e.add(n),n+1<i.length&&e.add(n+1),n++)}if(!t){let n=x();n&&(t=n.trim()||void 0,m="saved")}return{positional:i.filter((n,a)=>!e.has(a)),wsPort:d,simId:t,simIdSource:m,commandTimeoutMs:l}}function N(i,r={}){let e=1,d=r.commandTimeoutMs??15e3,l=new Map,t=new k(`ws://localhost:${i}`),m=C(),P=new Promise((s,o)=>{t.on("open",()=>{try{t.send(JSON.stringify({type:"bridge:hello",id:0,cliIdentityKey:m.key,cliIdentitySource:m.source,cliLabel:r.cliLabel}))}catch{}s()}),t.on("error",c=>{let g=c.message?`: ${c.message}`:"";o(new Error(`could not connect to ws://localhost:${i}${g}`))})}),h=!1,n=!1;function a(s,o){if(n||process.env.SOOTSIM_QUIET_TARGET_NOTICE==="1"||s.startsWith("bridge:")||s==="focus"||s==="close")return;n=!0;let c=o??"primary",g=_(o?r.simIdSource:"none");process.stderr.write(` \u2192 ${c} (${g})
|
|
4
|
-
`)}return t.on("message",s=>{let o;try{o=JSON.parse(s.toString())}catch{return}if(o.id===0)return;let c=l.get(o.id);c&&(l.delete(o.id),o._otherCliCount>0&&!h&&(h=!0,process.stderr.write(`
|
|
5
|
-
\u26A0 ${o._otherCliCount} other CLI identity/identities are driving this sim
|
|
6
|
-
taps, scrolls, and keyboard input from multiple agents will collide on
|
|
7
|
-
the same screen state (this is not a bridge throughput limit).
|
|
8
|
-
use \`sootsim open --new\` for an isolated sim per agent.
|
|
9
|
-
|
|
10
|
-
`)),o.error?o._locked?c.reject(new T(o.error,o._locked)):c.reject(new Error(o.error)):c.resolve(o.result))}),t.on("close",(s,o)=>{let c=o?.toString()||"connection closed";for(let[g,u]of l)l.delete(g),u.reject(new Error(`sim disconnected: ${c} (code ${s})`))}),{async send(s,o){let c=o?.timeoutMs??d,g=async u=>{await P;let b=e++;return new Promise((A,W)=>{let $=setTimeout(()=>{l.delete(b),W(new Error(`command timed out after ${Math.round(c/1e3)}s`))},c);l.set(b,{resolve:v=>{clearTimeout($),A(v)},reject:v=>{clearTimeout($),W(v)}});let p={...s,id:b};p.simId===void 0&&u&&(p.simId=u),a(s.type??"",p.simId),t.send(JSON.stringify(p))})};try{return await g(r.simId)}catch(u){let b=u instanceof Error?u.message:String(u);throw r.simIdSource==="saved"&&r.simId&&s.simId===void 0&&b===`no sim connected with id ${r.simId}`?(F(),new Error(`saved sim ${r.simId} is gone; run \`sootsim list\` and \`sootsim use <sim>\`, or open a fresh sim`)):u}},async listSims(){let s=await this.send({type:"bridge:list-sims"});return Array.isArray(s)?s:[]},async openUrl(s,o={}){return this.send({type:"bridge:open",url:s,newWindow:o.newWindow===!0})},async focusSim(s){return this.send({type:"focus",simId:s})},async closeSim(s){return this.send({type:"close",simId:s})},async claim(s,o={}){return await this.send({type:"bridge:claim",simId:s,force:o.force===!0})},close(){try{t.readyState===k.OPEN&&t.send(JSON.stringify({type:"bridge:bye",id:0}))}catch{}t.close();let s=setTimeout(()=>{t.readyState!==k.CLOSED&&t.terminate()},250)}}}function Q(i){return N(i.wsPort,{commandTimeoutMs:i.commandTimeoutMs,simId:i.simId,simIdSource:i.simIdSource})}function R(i,r){let e=i.filter(t=>t.readyState==="open"&&t.id!==r),d=e.find(t=>t.userFocused)??e.find(t=>t.userVisible===!0&&t.isPrimary)??e.find(t=>t.userVisible===!0)??e.find(t=>t.isPrimary);if(!d)return null;let l=[d.isPrimary?"primary":null,d.userVisible===!1?"hidden":d.userVisible===!0?"visible":null,d.userFocused?"focused":null].filter(Boolean);return`${d.id}${l.length?` [${l.join(", ")}]`:""}`}async function z(i){try{let r=await i.send({type:"evaluate",code:`(() => ({
|
|
11
|
-
hidden: document.hidden,
|
|
12
|
-
visibilityState: document.visibilityState,
|
|
13
|
-
hasFocus: typeof document.hasFocus === 'function' ? document.hasFocus() : null,
|
|
14
|
-
simId: window.__sootsimBridge?.id ?? window.SootSim?.state?.simId ?? null,
|
|
15
|
-
url: location.href
|
|
16
|
-
}))()`}),e=typeof r=="object"&&r!==null?r:{hidden:r===!0};if(e.hidden===!0===!0){let l=null;try{l=R(await i.listSims(),e.simId)}catch{}return process.stderr.write(`
|
|
17
|
-
\u26A0 target sim${e.simId?` ${e.simId}`:""} is hidden (document.hidden = true)
|
|
18
|
-
`+(e.visibilityState?` visibility: ${e.visibilityState}
|
|
19
|
-
`:"")+` animations and rAF callbacks are throttled \u2014 coordinates may be wrong
|
|
20
|
-
and launch/transition animations will not complete.
|
|
21
|
-
`+(l?` another open sim looks usable: ${l}; target it with --sim.
|
|
22
|
-
`:" run `sootsim list` to find the visible sim and target it with --sim.\n")+` page-level window.focus() cannot unhide a browser tab.
|
|
23
|
-
|
|
24
|
-
`),{hidden:!0,warned:!0,simId:e.simId}}return{hidden:!1,warned:!1,simId:e.simId}}catch{return{hidden:!1,warned:!1}}}async function X(i,r,e={}){let d={type:"evaluate",code:r};return e.acquireLock&&(d.acquireLock=!0),i.send(d)}async function Y(i,r,...e){return i.send({type:"call",path:r,args:e})}async function Z(i,r,...e){return i.send({type:"call",path:r,args:e,acquireLock:!0})}export{E as a,M as b,J as c,U as d,G as e,T as f,K as g,N as h,Q as i,z as j,X as k,Y as l,Z as m};
|