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.
Files changed (175) hide show
  1. package/README.md +14 -14
  2. package/detox/element-types.ts +3 -3
  3. package/detox/expectations.ts +29 -24
  4. package/detox/gestures.ts +16 -16
  5. package/detox/index.ts +34 -32
  6. package/dist-cli/bin.js +4 -4
  7. package/dist-cli/chunks/{agent-HBVXCRJT.js → agent-6P5T2QHC.js} +2 -2
  8. package/dist-cli/chunks/{agent-wrapper-X376DPV2.js → agent-wrapper-SMEMJZXI.js} +2 -2
  9. package/dist-cli/chunks/{app-fonts-LIQTD5RP.js → app-fonts-6OQTLX3U.js} +2 -2
  10. package/dist-cli/chunks/{assert-X6PPLN72.js → assert-ZAYFOW6R.js} +2 -2
  11. package/dist-cli/chunks/{auth-INRWIM2C.js → auth-Y53UANSH.js} +2 -2
  12. package/dist-cli/chunks/beta-ZQCASJ7C.js +2 -0
  13. package/dist-cli/chunks/{camera-LGFHYCJ5.js → camera-AJQWSOO4.js} +2 -2
  14. package/dist-cli/chunks/{chunk-2BQOZATN.js → chunk-2I62CFYI.js} +3 -3
  15. package/dist-cli/chunks/{chunk-E3DLUOFK.js → chunk-34MFG4IM.js} +7 -7
  16. package/dist-cli/chunks/{chunk-J7IFVTWX.js → chunk-4A6HM2K3.js} +1 -1
  17. package/dist-cli/chunks/chunk-4QOY4T27.js +3 -0
  18. package/dist-cli/chunks/{chunk-HRYKZ5YY.js → chunk-6Q2B7QHC.js} +5 -5
  19. package/dist-cli/chunks/{chunk-F5HOYUD2.js → chunk-6U3GMNTP.js} +1 -1
  20. package/dist-cli/chunks/{chunk-RNWJQ4UR.js → chunk-72AQH53U.js} +1 -1
  21. package/dist-cli/chunks/{chunk-NIN75M5H.js → chunk-7HMRWJHH.js} +1 -1
  22. package/dist-cli/chunks/{chunk-YB2LM6VB.js → chunk-7TJVPCQA.js} +6 -6
  23. package/dist-cli/chunks/{chunk-LM544J6V.js → chunk-A5SO4OGE.js} +1 -1
  24. package/dist-cli/chunks/{chunk-D42IRY2Y.js → chunk-AJ3CDBW6.js} +2 -2
  25. package/dist-cli/chunks/chunk-ALE5MU5Z.js +2 -0
  26. package/dist-cli/chunks/{chunk-NHE3YHDU.js → chunk-AVS7RL73.js} +2 -2
  27. package/dist-cli/chunks/chunk-AWF575Y5.js +1 -0
  28. package/dist-cli/chunks/chunk-BHNOPI7I.js +2 -0
  29. package/dist-cli/chunks/{chunk-LCJSTEIR.js → chunk-D2QUTBZP.js} +2 -2
  30. package/dist-cli/chunks/{chunk-ESK7B2BQ.js → chunk-DEVNCWS4.js} +6 -6
  31. package/dist-cli/chunks/{chunk-GUCFICA4.js → chunk-E5FOMYR6.js} +1 -1
  32. package/dist-cli/chunks/{chunk-2XXPF6LP.js → chunk-EFMPSG7T.js} +2 -2
  33. package/dist-cli/chunks/{chunk-4J7BIXEB.js → chunk-EKWOBYJO.js} +2 -2
  34. package/dist-cli/chunks/{chunk-E75EBS77.js → chunk-EPQ5EZBJ.js} +2 -2
  35. package/dist-cli/chunks/{chunk-Q4FB222M.js → chunk-FDSFA7WL.js} +3 -3
  36. package/dist-cli/chunks/{chunk-UGWURWHN.js → chunk-FSKQZHM2.js} +2 -2
  37. package/dist-cli/chunks/{chunk-QWRYD2QV.js → chunk-G42TLJVQ.js} +18 -18
  38. package/dist-cli/chunks/{chunk-HXHB5A6R.js → chunk-JA52F67H.js} +2 -2
  39. package/dist-cli/chunks/chunk-JKZY5CPQ.js +2 -0
  40. package/dist-cli/chunks/{chunk-MG6JE3AM.js → chunk-JV43GSZB.js} +2 -2
  41. package/dist-cli/chunks/{chunk-7L5IKIHC.js → chunk-JWZMAJC7.js} +1 -1
  42. package/dist-cli/chunks/{chunk-F53C5HAB.js → chunk-KIL5F7KD.js} +1 -1
  43. package/dist-cli/chunks/chunk-KY2KLPYL.js +1 -0
  44. package/dist-cli/chunks/{chunk-DB3GMVOH.js → chunk-LBXHG4AU.js} +2 -2
  45. package/dist-cli/chunks/{chunk-SBIAPF6E.js → chunk-MCMMRIJV.js} +1 -1
  46. package/dist-cli/chunks/{chunk-A4BOYB55.js → chunk-MWZOCZ7J.js} +2 -2
  47. package/dist-cli/chunks/{chunk-HXSV263V.js → chunk-NCXHUU7N.js} +1 -1
  48. package/dist-cli/chunks/{chunk-HTVB3FW2.js → chunk-NME4QWJ3.js} +2 -2
  49. package/dist-cli/chunks/{chunk-J2JAOIMN.js → chunk-OETQYAI2.js} +2 -2
  50. package/dist-cli/chunks/chunk-PJBYZZWR.js +2 -0
  51. package/dist-cli/chunks/{chunk-NU755HCE.js → chunk-PNPH4JLS.js} +1 -1
  52. package/dist-cli/chunks/chunk-RT3OS624.js +118 -0
  53. package/dist-cli/chunks/{chunk-7LDHOQ7B.js → chunk-SACWV3LZ.js} +20 -20
  54. package/dist-cli/chunks/chunk-SPJSTRXX.js +24 -0
  55. package/dist-cli/chunks/{chunk-3L5EXWCP.js → chunk-UYLESZU3.js} +1 -1
  56. package/dist-cli/chunks/{chunk-XESYTWFM.js → chunk-WWZL7HNB.js} +29 -29
  57. package/dist-cli/chunks/{chunk-T6MEERRI.js → chunk-ZBJ5JMXM.js} +2 -2
  58. package/dist-cli/chunks/chunk-ZWJEJR73.js +4 -0
  59. package/dist-cli/chunks/cli-version-V4GJYM74.js +2 -0
  60. package/dist-cli/chunks/{compat-MH673TEJ.js → compat-W7E4744D.js} +3 -3
  61. package/dist-cli/chunks/{config-SSCDV5ZJ.js → config-N7G65EY2.js} +2 -2
  62. package/dist-cli/chunks/{control-54R2XDLV.js → control-7VDFEVWU.js} +2 -2
  63. package/dist-cli/chunks/daemon-UI3PQ47F.js +2 -0
  64. package/dist-cli/chunks/{debug-PFP646II.js → debug-ZKUCSCOU.js} +3 -3
  65. package/dist-cli/chunks/{desktop-BPYYJ3EM.js → desktop-L3OXTT45.js} +3 -3
  66. package/dist-cli/chunks/{detox-BIUVHXYH.js → detox-QG4IBZAG.js} +2 -2
  67. package/dist-cli/chunks/{device-BNSPNIP6.js → device-QC6MDI7W.js} +2 -2
  68. package/dist-cli/chunks/{diagnose-BPTDSGOB.js → diagnose-HTUDENJK.js} +2 -2
  69. package/dist-cli/chunks/drivers-L2BXXDI7.js +2 -0
  70. package/dist-cli/chunks/{film-ILA6VSOG.js → film-CT7FHNLE.js} +3 -3
  71. package/dist-cli/chunks/flow-XUBXWJHL.js +2 -0
  72. package/dist-cli/chunks/help-7CIGANJN.js +2 -0
  73. package/dist-cli/chunks/{hidden-runtime-alias-TD7IVAPF.js → hidden-runtime-alias-YTUEPZ3U.js} +2 -2
  74. package/dist-cli/chunks/{home-paths-H36JDTNO.js → home-paths-7K6HDXOP.js} +2 -2
  75. package/dist-cli/chunks/inspect-ZY3QI7VJ.js +2 -0
  76. package/dist-cli/chunks/install-desktop-CVPRYFFV.js +2 -0
  77. package/dist-cli/chunks/{login-5WGZX2ZD.js → login-XHOOXAOW.js} +4 -4
  78. package/dist-cli/chunks/{logout-V53WTVIZ.js → logout-7BISNC5F.js} +2 -2
  79. package/dist-cli/chunks/{maestro-HGKYRD54.js → maestro-YD3G7LSG.js} +3 -3
  80. package/dist-cli/chunks/{maestro-generate-6DNZSKDP.js → maestro-generate-WNOWZC5K.js} +3 -3
  81. package/dist-cli/chunks/{mode-YFMKORE5.js → mode-Y6JJMXU5.js} +2 -2
  82. package/dist-cli/chunks/optional-demo-registry-YUXURWKX.js +2 -0
  83. package/dist-cli/chunks/{perf-EOIGFDWM.js → perf-AY3ZKTDS.js} +2 -2
  84. package/dist-cli/chunks/{record-2F54QS6Y.js → record-ZC4W6QN6.js} +10 -10
  85. package/dist-cli/chunks/runtime-MVYAP3UX.js +2 -0
  86. package/dist-cli/chunks/screenshot-command-NRKTGTSJ.js +151 -0
  87. package/dist-cli/chunks/{screenshots-capture-DIICN24R.js → screenshots-capture-LZR3AHWN.js} +3 -3
  88. package/dist-cli/chunks/{serve-66NC6PPZ.js → serve-4X2WI5XF.js} +5 -5
  89. package/dist-cli/chunks/{setup-ZYN4BTBR.js → setup-KODPGJBJ.js} +3 -3
  90. package/dist-cli/chunks/{skills-MDBIER7U.js → skills-DEAUDOAN.js} +2 -2
  91. package/dist-cli/chunks/{storage-AQ3SEO4Y.js → storage-P7FQRH5T.js} +2 -2
  92. package/dist-cli/chunks/store-PTRXOHHV.js +2 -0
  93. package/dist-cli/chunks/telemetry-JHKOHSI2.js +2 -0
  94. package/dist-cli/chunks/{timeline-VEGHQZ36.js → timeline-7ISYXLR3.js} +2 -2
  95. package/dist-cli/chunks/{upgrade-RNDCRPLJ.js → upgrade-NZBJS5L2.js} +3 -3
  96. package/dist-cli/chunks/upload-J34H5SXV.js +2 -0
  97. package/dist-cli/chunks/{version-XUMPKQUM.js → version-J6BLGLTO.js} +2 -2
  98. package/dist-cli/chunks/{web-ESQGHVPD.js → web-Y6UVYM3Z.js} +2 -2
  99. package/dist-cli/chunks/{what-happened-6NJENKUF.js → what-happened-YKXXY6W2.js} +2 -2
  100. package/dist-lib/agent-daemon-client.cjs +1 -1
  101. package/dist-lib/agent-events.cjs +1 -1
  102. package/dist-lib/agent-identity.cjs +1 -1
  103. package/dist-lib/agent-sessions.cjs +1 -1
  104. package/dist-lib/attached-projects.cjs +1 -1
  105. package/dist-lib/auth/shared-session.cjs +2 -2
  106. package/dist-lib/backend-origin.cjs +18 -18
  107. package/dist-lib/beta.cjs +1 -1
  108. package/dist-lib/beta.mjs +1 -1
  109. package/dist-lib/bridge-constants.cjs +4 -4
  110. package/dist-lib/cli-constants.cjs +1 -1
  111. package/dist-lib/config.cjs +1 -1
  112. package/dist-lib/detox/index.cjs +321 -79
  113. package/dist-lib/dev-bundle-resolution.cjs +2 -2
  114. package/dist-lib/home-paths.cjs +1 -1
  115. package/dist-lib/host/bridge-host.cjs +18 -13
  116. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  117. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  118. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  119. package/dist-lib/host/websocket-proxy.cjs +1 -1
  120. package/dist-lib/index.cjs +1 -1
  121. package/dist-lib/jump-to-source-babel.cjs +1 -1
  122. package/dist-lib/menu.cjs +1 -1
  123. package/dist-lib/menu.mjs +1 -1
  124. package/dist-lib/metro.cjs +1 -1
  125. package/dist-lib/profiles.cjs +1 -1
  126. package/dist-lib/render-mode.cjs +1 -1
  127. package/dist-lib/scripts/dev-server-scanner.cjs +7 -7
  128. package/dist-lib/sdk.cjs +12 -4
  129. package/dist-lib/sdk.mjs +12 -4
  130. package/dist-lib/skills.cjs +425 -169
  131. package/dist-lib/vite.cjs +1 -1
  132. package/package.json +4 -4
  133. package/scripts/dev-server-scanner.ts +5 -5
  134. package/scripts/postinstall.cjs +1 -1
  135. package/scripts/preuninstall.cjs +1 -1
  136. package/skills/{soot → contrast}/SKILL.md +4 -4
  137. package/skills/sootsim-perf/SKILL.md +3 -1
  138. package/src/auth/shared-session.ts +1 -1
  139. package/src/backend-origin.ts +11 -11
  140. package/src/bridge-constants.ts +3 -3
  141. package/src/home-paths.ts +1 -1
  142. package/src/host/agent-host.ts +2 -2
  143. package/src/host/bridge-host.ts +21 -21
  144. package/src/native-dev-bundle-url.ts +3 -3
  145. package/src/runtime-assets.ts +3 -3
  146. package/src/runtime-delivery.ts +10 -4
  147. package/src/screenshots/compose.ts +4 -1
  148. package/src/screenshots/frame-compose.ts +4 -1
  149. package/src/screenshots/orchestrate.ts +7 -2
  150. package/src/vite-plugin-one.ts +3 -3
  151. package/src/vite-plugin.ts +3 -3
  152. package/dist-cli/chunks/beta-WLLSIVLW.js +0 -2
  153. package/dist-cli/chunks/chunk-35EAEAN4.js +0 -24
  154. package/dist-cli/chunks/chunk-44Z2X5R4.js +0 -2
  155. package/dist-cli/chunks/chunk-4BLOZ3AF.js +0 -2
  156. package/dist-cli/chunks/chunk-7K5K5NKB.js +0 -2
  157. package/dist-cli/chunks/chunk-7RWDQCIT.js +0 -1
  158. package/dist-cli/chunks/chunk-BVD2JFOX.js +0 -1
  159. package/dist-cli/chunks/chunk-HBEEYWK5.js +0 -2
  160. package/dist-cli/chunks/chunk-JNUGPBWC.js +0 -118
  161. package/dist-cli/chunks/chunk-KBSJA7HK.js +0 -3
  162. package/dist-cli/chunks/chunk-Z2E2DDIQ.js +0 -4
  163. package/dist-cli/chunks/cli-version-NNBCIQKL.js +0 -2
  164. package/dist-cli/chunks/daemon-GL5GQTWB.js +0 -2
  165. package/dist-cli/chunks/drivers-KBKJVF2Y.js +0 -2
  166. package/dist-cli/chunks/flow-L247VVBM.js +0 -2
  167. package/dist-cli/chunks/help-QIZEANNK.js +0 -2
  168. package/dist-cli/chunks/inspect-6OAVS27G.js +0 -2
  169. package/dist-cli/chunks/install-desktop-LKYAUUQY.js +0 -2
  170. package/dist-cli/chunks/optional-demo-registry-YGNESGBH.js +0 -2
  171. package/dist-cli/chunks/runtime-FYVV4DCQ.js +0 -2
  172. package/dist-cli/chunks/screenshot-command-R535TYXY.js +0 -151
  173. package/dist-cli/chunks/store-6ZGLOTZI.js +0 -2
  174. package/dist-cli/chunks/telemetry-V76UPVDV.js +0 -2
  175. package/dist-cli/chunks/upload-KRBS3Y7Q.js +0 -2
@@ -1,4 +1,4 @@
1
- /*! sootsim v0.1.227 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
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;
@@ -32,6 +32,389 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
32
32
  ));
33
33
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
34
34
 
35
+ // ../../scripts/lib/reap-browser.ts
36
+ function currentRuntime() {
37
+ return { platform: process.platform, env: process.env };
38
+ }
39
+ function softwareChromeAllowed(runtime = currentRuntime()) {
40
+ return runtime.platform === "linux" && runtime.env.GITHUB_ACTIONS === "true" && runtime.env.RUNNER_OS === "Linux";
41
+ }
42
+ function isSoftwareRendererArgument(value) {
43
+ const arg = String(value);
44
+ return /^--disable-gpu(?:=.*)?$/i.test(arg) || /^--enable-unsafe-swiftshader(?:=.*)?$/i.test(arg) || /^--use-angle=(?:swiftshader|swiftshader-webgl)$/i.test(arg) || /^--use-gl=swiftshader$/i.test(arg);
45
+ }
46
+ function softwareRendererArgumentInText(command) {
47
+ const unsafe = command.match(
48
+ /(?:^|\s)(--disable-gpu(?:=[^\s]+)?|--enable-unsafe-swiftshader(?:=[^\s]+)?|--use-angle=(?:swiftshader|swiftshader-webgl)|--use-gl=swiftshader)(?=\s|$)/i
49
+ );
50
+ return unsafe?.[1] ?? null;
51
+ }
52
+ function expectedAngle(platform) {
53
+ if (platform === "darwin") return "metal";
54
+ if (platform === "linux") return "vulkan";
55
+ return "default";
56
+ }
57
+ function assertGpuBrowserCommand(command, runtime = currentRuntime()) {
58
+ if (softwareChromeAllowed(runtime)) return;
59
+ const unsafe = softwareRendererArgumentInText(command);
60
+ if (unsafe) {
61
+ throw new Error(
62
+ `[reap-browser] refused software-rendering Chrome process argument ${unsafe}`
63
+ );
64
+ }
65
+ const angle = expectedAngle(runtime.platform);
66
+ const args = command.split(/\s+/);
67
+ if (!args.includes(`--use-angle=${angle}`) || !args.includes("--enable-gpu")) {
68
+ throw new Error(
69
+ `[reap-browser] Chrome process did not retain the required ${angle} GPU arguments`
70
+ );
71
+ }
72
+ if (runtime.platform === "darwin" && command.includes("/Applications/Google Chrome.app/")) {
73
+ throw new Error(
74
+ "[reap-browser] refused the user's installed Google Chrome; use Playwright channel chromium"
75
+ );
76
+ }
77
+ }
78
+ function collectGpuRendererStrings(info) {
79
+ if (!info || typeof info !== "object") return [];
80
+ const record = info;
81
+ const gpu = record.gpu;
82
+ if (!gpu || typeof gpu !== "object") return [];
83
+ const gpuRecord = gpu;
84
+ const values = [];
85
+ const aux = gpuRecord.auxAttributes;
86
+ if (aux && typeof aux === "object") {
87
+ for (const value of Object.values(aux)) {
88
+ if (typeof value === "string") values.push(value);
89
+ }
90
+ }
91
+ const devices2 = gpuRecord.devices;
92
+ if (Array.isArray(devices2)) {
93
+ for (const device of devices2) {
94
+ if (!device || typeof device !== "object") continue;
95
+ for (const value of Object.values(device)) {
96
+ if (typeof value === "string") values.push(value);
97
+ }
98
+ }
99
+ }
100
+ return values;
101
+ }
102
+ function browserProcessPid(info) {
103
+ if (!info || typeof info !== "object") return null;
104
+ const record = info;
105
+ if (!Array.isArray(record.processInfo)) return null;
106
+ for (const entry of record.processInfo) {
107
+ if (!entry || typeof entry !== "object") continue;
108
+ const processInfo = entry;
109
+ if (processInfo.type === "browser" && typeof processInfo.id === "number" && Number.isInteger(processInfo.id) && processInfo.id > 0) {
110
+ return processInfo.id;
111
+ }
112
+ }
113
+ return null;
114
+ }
115
+ function assertGpuRendererInfo(info, runtime = currentRuntime()) {
116
+ if (softwareChromeAllowed(runtime)) return;
117
+ const renderers = collectGpuRendererStrings(info);
118
+ const summary = renderers.join(" | ");
119
+ if (!summary || /swiftshader|llvmpipe|software rasterizer/i.test(summary)) {
120
+ throw new Error(
121
+ `[reap-browser] Chrome reported a software renderer${summary ? `: ${summary}` : ""}`
122
+ );
123
+ }
124
+ if (runtime.platform === "darwin" && !/metal/i.test(summary)) {
125
+ throw new Error(
126
+ `[reap-browser] Chrome did not report an ANGLE Metal renderer: ${summary}`
127
+ );
128
+ }
129
+ }
130
+ function rootChromePid(b) {
131
+ if (b && typeof b === "object") {
132
+ const discovered = discoveredChromePids.get(b);
133
+ if (discovered !== void 0) return discovered;
134
+ }
135
+ const w = b;
136
+ if (w && typeof w.process === "function") {
137
+ try {
138
+ const p = w.process();
139
+ if (p && typeof p.pid === "number" && p.pid > 0) return p.pid;
140
+ } catch {
141
+ }
142
+ }
143
+ if (w && typeof w.browser === "function") {
144
+ try {
145
+ const child = w.browser();
146
+ if (child) return rootChromePid(child);
147
+ } catch {
148
+ }
149
+ }
150
+ return null;
151
+ }
152
+ function killTree(pid) {
153
+ let kids = [];
154
+ try {
155
+ kids = (0, import_node_child_process.execSync)(`pgrep -P ${pid} || true`).toString().split("\n").filter(Boolean);
156
+ } catch {
157
+ }
158
+ for (const k of kids) killTree(Number(k));
159
+ try {
160
+ process.kill(pid, "SIGKILL");
161
+ } catch {
162
+ }
163
+ }
164
+ function browserControl(b) {
165
+ const candidate = b;
166
+ if (!candidate || typeof candidate !== "object") return null;
167
+ if (typeof candidate.newBrowserCDPSession === "function" || typeof candidate.process === "function") {
168
+ return candidate;
169
+ }
170
+ if (typeof candidate.browser !== "function") return null;
171
+ try {
172
+ const browser = candidate.browser();
173
+ return browser ? browserControl(browser) : null;
174
+ } catch {
175
+ return null;
176
+ }
177
+ }
178
+ function registerChromePid(browser, control, pid) {
179
+ if (browser && typeof browser === "object") discoveredChromePids.set(browser, pid);
180
+ discoveredChromePids.set(control, pid);
181
+ reapPids.add(pid);
182
+ installReaper();
183
+ }
184
+ async function discoverChromePid(browser) {
185
+ const known = rootChromePid(browser);
186
+ if (known !== null) return known;
187
+ const control = browserControl(browser);
188
+ if (!control || typeof control.newBrowserCDPSession !== "function") return null;
189
+ const session = await control.newBrowserCDPSession();
190
+ try {
191
+ const pid = browserProcessPid(await session.send("SystemInfo.getProcessInfo"));
192
+ if (pid === null) return null;
193
+ registerChromePid(browser, control, pid);
194
+ return pid;
195
+ } finally {
196
+ await session.detach?.();
197
+ }
198
+ }
199
+ async function rejectLaunchedBrowser(browser) {
200
+ const root = rootChromePid(browser);
201
+ if (root !== null) {
202
+ reapPids.delete(root);
203
+ killTree(root);
204
+ return;
205
+ }
206
+ const candidate = browser;
207
+ if (candidate && typeof candidate.close === "function") {
208
+ try {
209
+ await candidate.close();
210
+ } catch {
211
+ }
212
+ }
213
+ }
214
+ async function validateLaunchedGpuBrowser(browser, runtime, rendererTarget) {
215
+ if (softwareChromeAllowed(runtime)) return;
216
+ const root = rootChromePid(browser);
217
+ const launchedControl = browserControl(browser);
218
+ if (root !== null && launchedControl) {
219
+ registerChromePid(browser, launchedControl, root);
220
+ }
221
+ if (root !== null && (runtime.platform === "darwin" || runtime.platform === "linux")) {
222
+ const command = (0, import_node_child_process.execFileSync)("ps", ["-p", String(root), "-o", "command="], {
223
+ encoding: "utf8"
224
+ }).trim();
225
+ assertGpuBrowserCommand(command, runtime);
226
+ }
227
+ const rendererBrowser = rendererTarget ? await rendererTarget(browser) : browser;
228
+ const control = browserControl(rendererBrowser);
229
+ if (control && typeof control.newBrowserCDPSession === "function") {
230
+ const session = await control.newBrowserCDPSession();
231
+ try {
232
+ const pid = browserProcessPid(await session.send("SystemInfo.getProcessInfo"));
233
+ if (pid === null) {
234
+ throw new Error("[reap-browser] Chrome did not report its browser process id");
235
+ }
236
+ registerChromePid(browser, control, pid);
237
+ if (rendererBrowser && typeof rendererBrowser === "object") {
238
+ discoveredChromePids.set(rendererBrowser, pid);
239
+ }
240
+ if (runtime.platform === "darwin" || runtime.platform === "linux") {
241
+ const command = (0, import_node_child_process.execFileSync)("ps", ["-p", String(pid), "-o", "command="], {
242
+ encoding: "utf8"
243
+ }).trim();
244
+ assertGpuBrowserCommand(command, runtime);
245
+ }
246
+ assertGpuRendererInfo(await session.send("SystemInfo.getInfo"), runtime);
247
+ return;
248
+ } finally {
249
+ await session.detach?.();
250
+ }
251
+ }
252
+ throw new Error("[reap-browser] could not validate the launched Chrome GPU renderer");
253
+ }
254
+ function installReaper() {
255
+ if (installed) return;
256
+ installed = true;
257
+ const reap = () => {
258
+ for (const pid of reapPids) killTree(pid);
259
+ };
260
+ process.on("SIGTERM", () => {
261
+ reap();
262
+ process.exit(0);
263
+ });
264
+ process.on("SIGINT", () => {
265
+ reap();
266
+ process.exit(0);
267
+ });
268
+ process.on("exit", reap);
269
+ }
270
+ async function launchReapedScoped(launch) {
271
+ const browser = await launch();
272
+ let root = rootChromePid(browser);
273
+ if (root === null) {
274
+ try {
275
+ root = await discoverChromePid(browser);
276
+ } catch {
277
+ root = null;
278
+ }
279
+ }
280
+ const mine = /* @__PURE__ */ new Set();
281
+ if (root != null) {
282
+ mine.add(root);
283
+ reapPids.add(root);
284
+ installReaper();
285
+ }
286
+ const evented = browserControl(browser) ?? browser;
287
+ if (typeof evented.on === "function") {
288
+ const disconnectEvent = typeof evented.newBrowserCDPSession === "function" ? "disconnected" : "close";
289
+ evented.on(disconnectEvent, () => {
290
+ for (const pid of mine) reapPids.delete(pid);
291
+ });
292
+ }
293
+ const reapNow = () => {
294
+ for (const pid of mine) {
295
+ killTree(pid);
296
+ reapPids.delete(pid);
297
+ }
298
+ mine.clear();
299
+ };
300
+ return { browser, reapNow };
301
+ }
302
+ async function launchReaped(launch) {
303
+ const { browser } = await launchReapedScoped(launch);
304
+ return browser;
305
+ }
306
+ function gpuChromeArgs(platform = process.platform) {
307
+ const angle = expectedAngle(platform);
308
+ return [
309
+ `--use-angle=${angle}`,
310
+ "--enable-gpu",
311
+ "--enable-gpu-rasterization",
312
+ "--enable-zero-copy",
313
+ "--ignore-gpu-blocklist"
314
+ ];
315
+ }
316
+ function gpuChromeLaunchOptions(opts = {}, runtime = currentRuntime()) {
317
+ const callerArgs = Array.isArray(opts.args) ? opts.args : [];
318
+ const unsafe = callerArgs.find(isSoftwareRendererArgument);
319
+ if (unsafe !== void 0) {
320
+ throw new Error(
321
+ `[reap-browser] refused software-rendering Chrome launch argument ${String(unsafe)}`
322
+ );
323
+ }
324
+ if (opts.executablePath !== void 0) {
325
+ throw new Error(
326
+ "[reap-browser] executablePath bypasses Playwright Chrome for Testing and is not allowed"
327
+ );
328
+ }
329
+ if (opts.ignoreDefaultArgs === true) {
330
+ throw new Error(
331
+ "[reap-browser] ignoreDefaultArgs=true bypasses required Chrome launch defaults"
332
+ );
333
+ }
334
+ const managedGpuArgs = /* @__PURE__ */ new Set([
335
+ "--enable-gpu",
336
+ "--enable-gpu-rasterization",
337
+ "--enable-zero-copy",
338
+ "--ignore-gpu-blocklist"
339
+ ]);
340
+ const unmanagedCallerArgs = callerArgs.filter((arg) => {
341
+ const value = String(arg);
342
+ return !managedGpuArgs.has(value) && !value.startsWith("--use-angle=");
343
+ });
344
+ const callerIgnored = Array.isArray(opts.ignoreDefaultArgs) ? opts.ignoreDefaultArgs : [];
345
+ const ignoredDefaults = [
346
+ .../* @__PURE__ */ new Set([...callerIgnored, "--disable-gpu", "--enable-unsafe-swiftshader"])
347
+ ];
348
+ return {
349
+ ...opts,
350
+ // playwright's `chromium` channel is the full Google Chrome for Testing
351
+ // app, not chromium_headless_shell. on macOS it has bundle id
352
+ // com.google.chrome.for.testing, so automation cannot become the target of
353
+ // Dock open/quit events intended for the user's com.google.Chrome browser.
354
+ channel: "chromium",
355
+ args: [...unmanagedCallerArgs, ...gpuChromeArgs(runtime.platform)],
356
+ ignoreDefaultArgs: ignoredDefaults
357
+ };
358
+ }
359
+ function preferGpuChrome(launch, opts, rendererTarget) {
360
+ return async () => {
361
+ const runtime = currentRuntime();
362
+ const allowSoftwareFallback = softwareChromeAllowed(runtime);
363
+ const gpuOptions = gpuChromeLaunchOptions(opts, runtime);
364
+ if (!gpuChromeMissing) {
365
+ let browser;
366
+ try {
367
+ browser = await launch(gpuOptions);
368
+ } catch (err) {
369
+ const msg = err instanceof Error ? err.message : String(err);
370
+ const missing = /not found at|not installed|Chromium distribution|Executable doesn't exist|No such file/i.test(
371
+ msg
372
+ );
373
+ if (!missing) throw err;
374
+ if (!allowSoftwareFallback) {
375
+ throw new Error(
376
+ "[reap-browser] Playwright Chrome for Testing is required for local GPU browser work; chromium_headless_shell fallback is disabled. Run `bunx playwright install chromium`.",
377
+ { cause: err }
378
+ );
379
+ }
380
+ gpuChromeMissing = true;
381
+ console.warn(
382
+ "[reap-browser] GitHub Actions Linux Chrome for Testing not found, falling back to chromium_headless_shell software rendering."
383
+ );
384
+ return launch(softwareFallbackChromeLaunchOptions(opts));
385
+ }
386
+ try {
387
+ await validateLaunchedGpuBrowser(browser, runtime, rendererTarget);
388
+ } catch (err) {
389
+ await rejectLaunchedBrowser(browser);
390
+ throw err;
391
+ }
392
+ return browser;
393
+ }
394
+ return launch(softwareFallbackChromeLaunchOptions(opts));
395
+ };
396
+ }
397
+ function softwareFallbackChromeLaunchOptions(opts) {
398
+ const fallback = { ...opts };
399
+ delete fallback.channel;
400
+ delete fallback.executablePath;
401
+ return fallback;
402
+ }
403
+ async function launchReapedChrome(launch, opts = {}, rendererTarget) {
404
+ return launchReaped(preferGpuChrome(launch, opts, rendererTarget));
405
+ }
406
+ var import_node_child_process, discoveredChromePids, reapPids, installed, gpuChromeMissing;
407
+ var init_reap_browser = __esm({
408
+ "../../scripts/lib/reap-browser.ts"() {
409
+ "use strict";
410
+ import_node_child_process = require("node:child_process");
411
+ discoveredChromePids = /* @__PURE__ */ new WeakMap();
412
+ reapPids = /* @__PURE__ */ new Set();
413
+ installed = false;
414
+ gpuChromeMissing = false;
415
+ }
416
+ });
417
+
35
418
  // ../compat/src/unsupported-packages.ts
36
419
  var UNSUPPORTABLE;
37
420
  var init_unsupported_packages = __esm({
@@ -9980,7 +10363,7 @@ function resolveSootsimBridgePortForRuntimeLocation(input = {}) {
9980
10363
  if (hrefUrl?.pathname.startsWith("/sootsim") && (hrefUrl.searchParams.has("embed") || hrefUrl.searchParams.has("nativePreview"))) {
9981
10364
  return resolveSootsimBridgePortForHttpPort({
9982
10365
  httpPort,
9983
- baseHttpPort: SOOTBEAN_DEV_HTTP_BASE_PORT
10366
+ baseHttpPort: CONTRAST_DEV_HTTP_BASE_PORT
9984
10367
  });
9985
10368
  }
9986
10369
  if (hrefUrl?.pathname.startsWith("/sootsim") && httpPort >= SOOTSIM_SITE_DEV_HTTP_BASE_PORT && httpPort <= SOOTSIM_SITE_DEV_HTTP_BASE_PORT + MAX_LOCAL_DEV_PORT_OFFSET) {
@@ -9993,16 +10376,16 @@ function resolveSootsimBridgePortForRuntimeLocation(input = {}) {
9993
10376
  if (baseHttpPort === null) return DEFAULT_SOOTSIM_BRIDGE_PORT;
9994
10377
  return resolveSootsimBridgePortForHttpPort({ httpPort, baseHttpPort });
9995
10378
  }
9996
- var DEFAULT_SOOTSIM_BRIDGE_PORT, SOOTBEAN_DEV_HTTP_BASE_PORT, SOOTSIM_SITE_DEV_HTTP_BASE_PORT, SOOTSIM_SHELL_DEV_HTTP_BASE_PORT, LOCAL_DEV_HTTP_BASE_PORTS, MAX_LOCAL_DEV_PORT_OFFSET, SOOTSIM_BRIDGE_SIM_CLOSE_CODE, SOOTSIM_BRIDGE_SIM_CLOSE_REASON;
10379
+ var DEFAULT_SOOTSIM_BRIDGE_PORT, CONTRAST_DEV_HTTP_BASE_PORT, SOOTSIM_SITE_DEV_HTTP_BASE_PORT, SOOTSIM_SHELL_DEV_HTTP_BASE_PORT, LOCAL_DEV_HTTP_BASE_PORTS, MAX_LOCAL_DEV_PORT_OFFSET, SOOTSIM_BRIDGE_SIM_CLOSE_CODE, SOOTSIM_BRIDGE_SIM_CLOSE_REASON;
9997
10380
  var init_bridge_constants = __esm({
9998
10381
  "src/bridge-constants.ts"() {
9999
10382
  "use strict";
10000
10383
  DEFAULT_SOOTSIM_BRIDGE_PORT = 7668;
10001
- SOOTBEAN_DEV_HTTP_BASE_PORT = 3e3;
10384
+ CONTRAST_DEV_HTTP_BASE_PORT = 3e3;
10002
10385
  SOOTSIM_SITE_DEV_HTTP_BASE_PORT = 3001;
10003
10386
  SOOTSIM_SHELL_DEV_HTTP_BASE_PORT = 5173;
10004
10387
  LOCAL_DEV_HTTP_BASE_PORTS = [
10005
- SOOTBEAN_DEV_HTTP_BASE_PORT,
10388
+ CONTRAST_DEV_HTTP_BASE_PORT,
10006
10389
  SOOTSIM_SHELL_DEV_HTTP_BASE_PORT
10007
10390
  ];
10008
10391
  MAX_LOCAL_DEV_PORT_OFFSET = 2e3;
@@ -10052,7 +10435,7 @@ function applyPreviewProdBundle(parsed) {
10052
10435
  function normalizeNativeDevBundleUrl(bundleUrl) {
10053
10436
  try {
10054
10437
  const isAbsolute2 = isAbsoluteHttpUrl(bundleUrl);
10055
- const parsed = new URL(bundleUrl, "http://soot.local");
10438
+ const parsed = new URL(bundleUrl, "http://sootsim.local");
10056
10439
  parsed.pathname = parsed.pathname.replace(/\.\.bundle$/, ".bundle");
10057
10440
  if (!isNativeDevBundlePath(parsed.pathname)) return bundleUrl;
10058
10441
  parsed.searchParams.delete("transform.bytecode");
@@ -10419,7 +10802,7 @@ async function probePort(port, buildIconProxyUrl) {
10419
10802
  knownNonPatched.add(port);
10420
10803
  return null;
10421
10804
  }
10422
- function isSootSelfServer(server) {
10805
+ function isSootSimSelfServer(server) {
10423
10806
  const bundleId = server.bundleId?.trim().toLowerCase();
10424
10807
  if (bundleId?.startsWith("dev.sootsim.")) return true;
10425
10808
  return false;
@@ -10503,7 +10886,7 @@ async function scanDevServers(opts = {}) {
10503
10886
  for (const pid of [...cwdByPid.keys()]) {
10504
10887
  if (!livePids.has(pid)) cwdByPid.delete(pid);
10505
10888
  }
10506
- return results.filter((r) => !isSootSelfServer(r));
10889
+ return results.filter((r) => !isSootSimSelfServer(r));
10507
10890
  }
10508
10891
  var import_child_process, import_http, import_net, import_util, execP, TIMEOUT_MS, MANIFEST_TIMEOUT_MS, TCP_GATE_MS, FALLBACK_PORTS, cwdByPid, ENRICH_TIMEOUT_MS, enrichmentCache, enrichmentInFlight, knownNonPatched, knownNonExpo, knownOne, portCache, NEGATIVE_CACHE_TTL_MS, WEAK_RESULT_CACHE_TTL_MS;
10509
10892
  var init_dev_server_scanner = __esm({
@@ -10981,7 +11364,12 @@ async function updateRuntimeToLatest(opts = {}) {
10981
11364
  latestVersion: pin
10982
11365
  };
10983
11366
  }
10984
- const install2 = await installRuntime({ version: pin, channel, cdnOrigin, setActive: false });
11367
+ const install2 = await installRuntime({
11368
+ version: pin,
11369
+ channel,
11370
+ cdnOrigin,
11371
+ setActive: false
11372
+ });
10985
11373
  return {
10986
11374
  checked: true,
10987
11375
  updated: true,
@@ -11091,7 +11479,7 @@ var init_runtime_delivery = __esm({
11091
11479
  import_stream = require("stream");
11092
11480
  import_promises = require("stream/promises");
11093
11481
  init_home_paths();
11094
- DEFAULT_RUNTIME_CDN_ORIGIN = "https://sootbean.com";
11482
+ DEFAULT_RUNTIME_CDN_ORIGIN = "https://contrast.dev";
11095
11483
  RUNTIME_CDN_ORIGIN_ENV = "SOOTSIM_CDN_ORIGIN";
11096
11484
  RUNTIME_CHANNEL_ENV = "SOOTSIM_RUNTIME_CHANNEL";
11097
11485
  RUNTIME_PIN_ENV = "SOOTSIM_RUNTIME_VERSION";
@@ -13046,7 +13434,7 @@ function shouldAcquireLease(msg) {
13046
13434
  if (msg.readOnly === true) return false;
13047
13435
  return WRITE_COMMAND_TYPES.has(msg.type);
13048
13436
  }
13049
- function injectSharedConfigIntoHtml(data, bridgePort, sootbeanOrigin) {
13437
+ function injectSharedConfigIntoHtml(data, bridgePort, contrastOrigin) {
13050
13438
  let payload;
13051
13439
  try {
13052
13440
  const cfg = readSharedConfig();
@@ -13055,8 +13443,8 @@ function injectSharedConfigIntoHtml(data, bridgePort, sootbeanOrigin) {
13055
13443
  payload = "{}";
13056
13444
  }
13057
13445
  const bridgePortTag = bridgePort > 0 ? `window.__sootsimBridgePort=${bridgePort};` : "";
13058
- const sootbeanOriginTag = sootbeanOrigin ? `window.__sootsimSootbeanOrigin=${JSON.stringify(sootbeanOrigin)};` : "";
13059
- const tag = `<script>window.__sootsimSharedConfig=${payload};` + bridgePortTag + sootbeanOriginTag + `window.__sootsimCliVersion=${JSON.stringify(getCliVersion())};</script>`;
13446
+ const contrastOriginTag = contrastOrigin ? `window.__sootsimContrastOrigin=${JSON.stringify(contrastOrigin)};` : "";
13447
+ const tag = `<script>window.__sootsimSharedConfig=${payload};` + bridgePortTag + contrastOriginTag + `window.__sootsimCliVersion=${JSON.stringify(getCliVersion())};</script>`;
13060
13448
  const html = data.toString("utf8");
13061
13449
  if (html.includes("<head>")) return html.replace("<head>", `<head>${tag}`);
13062
13450
  if (html.includes("</head>")) return html.replace("</head>", tag + "</head>");
@@ -13176,7 +13564,7 @@ var init_bridge_host = __esm({
13176
13564
  shouldWriteLockfile;
13177
13565
  shouldWriteDevLockfile;
13178
13566
  getShellPort;
13179
- sootbeanOrigin = null;
13567
+ contrastOrigin = null;
13180
13568
  effectivePort = 0;
13181
13569
  startedAt = 0;
13182
13570
  heartbeatTimer = null;
@@ -13223,7 +13611,7 @@ var init_bridge_host = __esm({
13223
13611
  } : null;
13224
13612
  }
13225
13613
  });
13226
- this.sootbeanOrigin = opts.sootbeanOrigin?.replace(/\/$/, "") || null;
13614
+ this.contrastOrigin = opts.contrastOrigin?.replace(/\/$/, "") || null;
13227
13615
  this.simIdleReapTtlMs = opts.simIdleReapTtlMs ?? _SootSimBridgeHost.SIM_IDLE_REAP_TTL_MS;
13228
13616
  this.automationSimIdleReapTtlMs = opts.automationSimIdleReapTtlMs ?? _SootSimBridgeHost.AUTOMATION_SIM_IDLE_REAP_TTL_MS;
13229
13617
  this.maxConcurrentAutomationSims = opts.maxConcurrentAutomationSims ?? _SootSimBridgeHost.MAX_CONCURRENT_AUTOMATION_SIMS;
@@ -14201,7 +14589,7 @@ var init_bridge_host = __esm({
14201
14589
  return;
14202
14590
  }
14203
14591
  res.end(
14204
- injectSharedConfigIntoHtml(data, this.effectivePort, this.sootbeanOrigin)
14592
+ injectSharedConfigIntoHtml(data, this.effectivePort, this.contrastOrigin)
14205
14593
  );
14206
14594
  });
14207
14595
  return;
@@ -14226,7 +14614,7 @@ var init_bridge_host = __esm({
14226
14614
  return;
14227
14615
  }
14228
14616
  res.end(
14229
- injectSharedConfigIntoHtml(data, this.effectivePort, this.sootbeanOrigin)
14617
+ injectSharedConfigIntoHtml(data, this.effectivePort, this.contrastOrigin)
14230
14618
  );
14231
14619
  });
14232
14620
  return;
@@ -15469,14 +15857,15 @@ function createBridge(wsPort, opts = {}) {
15469
15857
  ws.send(JSON.stringify(payload));
15470
15858
  });
15471
15859
  };
15860
+ const sentSimId = opts.simIdSource === "flag" ? opts.simId : readCurrentSimId() ?? opts.simId;
15472
15861
  try {
15473
- return await sendOnce(opts.simId);
15862
+ return await sendOnce(sentSimId);
15474
15863
  } catch (error) {
15475
15864
  const message = error instanceof Error ? error.message : String(error);
15476
- if (opts.simIdSource === "saved" && opts.simId && cmd.simId === void 0 && message === `no sim connected with id ${opts.simId}`) {
15865
+ if (opts.simIdSource !== "flag" && sentSimId && cmd.simId === void 0 && message === `no sim connected with id ${sentSimId}`) {
15477
15866
  clearCurrentSimId();
15478
15867
  throw new Error(
15479
- `saved sim ${opts.simId} is gone; run \`sootsim list\` and \`sootsim use <sim>\`, or open a fresh sim`
15868
+ `saved sim ${sentSimId} is gone; run \`sootsim list\` and \`sootsim use <sim>\`, or open a fresh sim`
15480
15869
  );
15481
15870
  }
15482
15871
  throw error;
@@ -15872,7 +16261,9 @@ function renderFrameHtml(layout, rawImageDataUrl) {
15872
16261
  }
15873
16262
  async function composeFramedScreenshot(rawPng, model) {
15874
16263
  const { chromium } = await import("playwright");
15875
- const browser = await chromium.launch({ headless: true });
16264
+ const browser = await launchReapedChrome((options) => chromium.launch(options), {
16265
+ headless: true
16266
+ });
15876
16267
  try {
15877
16268
  const composer = await createFramedScreenshotComposer(browser);
15878
16269
  try {
@@ -15932,6 +16323,7 @@ var init_frame_compose = __esm({
15932
16323
  "use strict";
15933
16324
  import_chrome_metrics = require("sootsim-engine/ios/chrome-metrics");
15934
16325
  import_settings = require("sootsim-engine/settings");
16326
+ init_reap_browser();
15935
16327
  METALLIC_RING_SHADOW = "inset 0 0 0 0.5px #000, inset 0 0 0 2px #757575, inset 0 0 0 5px #212121";
15936
16328
  FRAME_OUTLINE_BLEED = 1;
15937
16329
  }
@@ -16712,7 +17104,7 @@ var init_shared_session = __esm({
16712
17104
  init_home_paths();
16713
17105
  SESSION_VERSION = 1;
16714
17106
  SESSION_FILE_ENV = "SOOTSIM_SHARED_AUTH_FILE";
16715
- DEFAULT_ORIGIN = "https://sootbean.com";
17107
+ DEFAULT_ORIGIN = "https://contrast.dev";
16716
17108
  }
16717
17109
  });
16718
17110
 
@@ -16723,15 +17115,15 @@ function looksLikeGitHubToken(token) {
16723
17115
  );
16724
17116
  }
16725
17117
  function resolveGitHubActionsAuth() {
16726
- const sootRunnerToken = process.env.SOOT_INSTALLATION_TOKEN?.trim();
17118
+ const contrastRunnerToken = process.env.CONTRAST_INSTALLATION_TOKEN?.trim();
16727
17119
  const githubActionsToken = process.env.GITHUB_TOKEN?.trim();
16728
- const token = sootRunnerToken || githubActionsToken;
17120
+ const token = contrastRunnerToken || githubActionsToken;
16729
17121
  if (!looksLikeGitHubToken(token)) return null;
16730
- const repoSlug = (process.env.SOOT_REPO || process.env.GITHUB_REPOSITORY || "").trim();
17122
+ const repoSlug = (process.env.CONTRAST_REPO || process.env.GITHUB_REPOSITORY || "").trim();
16731
17123
  if (!repoSlug) return null;
16732
17124
  const [owner, repo] = repoSlug.includes("/") ? repoSlug.split("/", 2) : [null, null];
16733
- const repositoryId = (process.env.SOOT_REPO_ID || process.env.GITHUB_REPOSITORY_ID || "").trim();
16734
- const installationId = (process.env.SOOT_INSTALLATION_ID || process.env.GITHUB_APP_INSTALLATION_ID || "").trim();
17125
+ const repositoryId = (process.env.CONTRAST_REPO_ID || process.env.GITHUB_REPOSITORY_ID || "").trim();
17126
+ const installationId = (process.env.CONTRAST_INSTALLATION_ID || process.env.GITHUB_APP_INSTALLATION_ID || "").trim();
16735
17127
  return {
16736
17128
  kind: "github",
16737
17129
  token,
@@ -16740,7 +17132,7 @@ function resolveGitHubActionsAuth() {
16740
17132
  owner: owner || null,
16741
17133
  repo: repo || null,
16742
17134
  installationId: installationId || null,
16743
- source: sootRunnerToken ? "soot-runner" : "github-actions"
17135
+ source: contrastRunnerToken ? "contrast-runner" : "github-actions"
16744
17136
  };
16745
17137
  }
16746
17138
  function resolveCliAuth() {
@@ -16838,7 +17230,7 @@ var init_upload = __esm({
16838
17230
  init_open_url2();
16839
17231
  init_telemetry();
16840
17232
  init_ws_bridge();
16841
- REMOTE_DEFAULT_ORIGIN = process.env.SOOTSIM_UPLOAD_ORIGIN || "https://sootbean.com";
17233
+ REMOTE_DEFAULT_ORIGIN = process.env.SOOTSIM_UPLOAD_ORIGIN || "https://contrast.dev";
16842
17234
  REMOTE_DEFAULT_PUBLIC_PREVIEW_ORIGIN = process.env.SOOTSIM_PREVIEW_ORIGIN || "https://sootsim.com";
16843
17235
  LOCAL_DEFAULT_ORIGIN = "http://localhost:3000";
16844
17236
  LOCAL_UPLOAD_PROBE_TIMEOUT_MS = 2e3;
@@ -18869,147 +19261,8 @@ module.exports = __toCommonJS(registry_exports);
18869
19261
  var fs10 = __toESM(require("fs"), 1);
18870
19262
  var path10 = __toESM(require("path"), 1);
18871
19263
 
18872
- // ../../scripts/lib/reap-browser.ts
18873
- var import_node_child_process = require("node:child_process");
18874
- function rootChromePid(b) {
18875
- const w = b;
18876
- if (w && typeof w.process === "function") {
18877
- try {
18878
- const p = w.process();
18879
- if (p && typeof p.pid === "number" && p.pid > 0) return p.pid;
18880
- } catch {
18881
- }
18882
- }
18883
- if (w && typeof w.browser === "function") {
18884
- try {
18885
- const child = w.browser();
18886
- if (child) return rootChromePid(child);
18887
- } catch {
18888
- }
18889
- }
18890
- return null;
18891
- }
18892
- function killTree(pid) {
18893
- let kids = [];
18894
- try {
18895
- kids = (0, import_node_child_process.execSync)(`pgrep -P ${pid} || true`).toString().split("\n").filter(Boolean);
18896
- } catch {
18897
- }
18898
- for (const k of kids) killTree(Number(k));
18899
- try {
18900
- process.kill(pid, "SIGKILL");
18901
- } catch {
18902
- }
18903
- }
18904
- var reapPids = /* @__PURE__ */ new Set();
18905
- var installed = false;
18906
- function installReaper() {
18907
- if (installed) return;
18908
- installed = true;
18909
- const reap = () => {
18910
- for (const pid of reapPids) killTree(pid);
18911
- };
18912
- process.on("SIGTERM", () => {
18913
- reap();
18914
- process.exit(0);
18915
- });
18916
- process.on("SIGINT", () => {
18917
- reap();
18918
- process.exit(0);
18919
- });
18920
- process.on("exit", reap);
18921
- }
18922
- async function launchReapedScoped(launch) {
18923
- const browser = await launch();
18924
- const root = rootChromePid(browser);
18925
- const mine = /* @__PURE__ */ new Set();
18926
- if (root != null) {
18927
- mine.add(root);
18928
- reapPids.add(root);
18929
- installReaper();
18930
- }
18931
- const evented = browser;
18932
- if (typeof evented.on === "function") {
18933
- evented.on("disconnected", () => {
18934
- for (const pid of mine) reapPids.delete(pid);
18935
- });
18936
- }
18937
- const reapNow = () => {
18938
- for (const pid of mine) {
18939
- killTree(pid);
18940
- reapPids.delete(pid);
18941
- }
18942
- mine.clear();
18943
- };
18944
- return { browser, reapNow };
18945
- }
18946
- async function launchReaped(launch) {
18947
- const { browser } = await launchReapedScoped(launch);
18948
- return browser;
18949
- }
18950
- var gpuChromeMissing = false;
18951
- function gpuChromeArgs() {
18952
- const angle = process.platform === "darwin" ? "metal" : process.platform === "linux" ? "vulkan" : "default";
18953
- return [
18954
- `--use-angle=${angle}`,
18955
- "--enable-gpu",
18956
- "--enable-gpu-rasterization",
18957
- "--enable-zero-copy",
18958
- "--ignore-gpu-blocklist"
18959
- ];
18960
- }
18961
- function gpuChromeLaunchOptions(opts = {}) {
18962
- const callerArgs = Array.isArray(opts.args) ? opts.args : [];
18963
- const callerIgnored = Array.isArray(opts.ignoreDefaultArgs) ? opts.ignoreDefaultArgs : [];
18964
- return {
18965
- ...opts,
18966
- // playwright's `chromium` channel is the full Google Chrome for Testing
18967
- // app, not chromium_headless_shell. on macOS it has bundle id
18968
- // com.google.chrome.for.testing, so automation cannot become the target of
18969
- // Dock open/quit events intended for the user's com.google.Chrome browser.
18970
- channel: "chromium",
18971
- args: [...gpuChromeArgs(), ...callerArgs],
18972
- ignoreDefaultArgs: [...callerIgnored, "--enable-unsafe-swiftshader"]
18973
- };
18974
- }
18975
- function preferGpuChrome(launch, opts) {
18976
- return async () => {
18977
- const allowSoftwareFallback = process.env.CI === "true" || process.env.CI === "1" || process.env.GITHUB_ACTIONS === "true";
18978
- if (!gpuChromeMissing) {
18979
- try {
18980
- const callerArgs = Array.isArray(opts.args) ? opts.args : [];
18981
- if (!allowSoftwareFallback && callerArgs.some(
18982
- (arg) => arg === "--enable-unsafe-swiftshader" || /^--use-angle=(?:swiftshader|swiftshader-webgl)$/i.test(String(arg))
18983
- )) {
18984
- throw new Error("[reap-browser] refused local SwiftShader launch arguments");
18985
- }
18986
- return await launch(gpuChromeLaunchOptions(opts));
18987
- } catch (err) {
18988
- const msg = err instanceof Error ? err.message : String(err);
18989
- const missing = /not found at|not installed|Chromium distribution|Executable doesn't exist|No such file/i.test(
18990
- msg
18991
- );
18992
- if (!missing) throw err;
18993
- if (!allowSoftwareFallback) {
18994
- throw new Error(
18995
- "[reap-browser] Playwright Chrome for Testing is required for local GPU browser work; chromium_headless_shell fallback is disabled. Run `bunx playwright install chromium`.",
18996
- { cause: err }
18997
- );
18998
- }
18999
- gpuChromeMissing = true;
19000
- console.warn(
19001
- "[reap-browser] CI Chrome for Testing not found, falling back to chromium_headless_shell software rendering."
19002
- );
19003
- }
19004
- }
19005
- return launch(opts);
19006
- };
19007
- }
19008
- async function launchReapedChrome(launch, opts = {}) {
19009
- return launchReaped(preferGpuChrome(launch, opts));
19010
- }
19011
-
19012
19264
  // src/skills/builtin/a11y-review.ts
19265
+ init_reap_browser();
19013
19266
  var skill = {
19014
19267
  name: "a11y-review",
19015
19268
  description: "Check the app for accessibility issues",
@@ -19283,6 +19536,7 @@ var skill3 = {
19283
19536
  };
19284
19537
 
19285
19538
  // src/skills/builtin/perf-profile.ts
19539
+ init_reap_browser();
19286
19540
  var skill4 = {
19287
19541
  name: "perf-profile",
19288
19542
  description: "Profile render performance of the app",
@@ -19350,6 +19604,7 @@ var skill4 = {
19350
19604
  };
19351
19605
 
19352
19606
  // src/skills/builtin/screenshot-all.ts
19607
+ init_reap_browser();
19353
19608
  var skill5 = {
19354
19609
  name: "screenshot-all",
19355
19610
  description: "Capture screenshots of all screens in the app",
@@ -19392,6 +19647,7 @@ var skill5 = {
19392
19647
  };
19393
19648
 
19394
19649
  // src/skills/builtin/visual-diff.ts
19650
+ init_reap_browser();
19395
19651
  var skill6 = {
19396
19652
  name: "visual-diff",
19397
19653
  description: "Compare current rendering against baseline screenshots",