peachsim 0.1.659 → 0.1.661
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 +10 -15
- package/cli/cli-update.ts +53 -21
- package/cli/commands/compat.ts +6 -2
- package/cli/commands/control.ts +12 -3
- package/cli/commands/inspect.ts +16 -14
- package/cli/commands/upgrade.ts +4 -2
- package/cli/current-sim.ts +4 -0
- package/cli/drivers/linux-browser-libs.lock.json +1088 -0
- package/cli/drivers/linux-browser-libs.ts +182 -0
- package/cli/drivers/playwright-provisioning.ts +20 -98
- package/cli/drivers/playwright-sim-host.ts +9 -0
- package/cli/drivers/playwright.ts +2 -1
- 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 +1 -1
- package/dist-lib/backend-origin.cjs +18 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +45 -41
- 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/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +5 -1
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +8 -3
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +1 -1
- package/dist-lib/native-app-identity.cjs +1 -1
- package/dist-lib/native-app-identity.mjs +1 -1
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +1339 -180
- package/dist-lib/swift-compile.cjs +1 -1
- package/dist-lib/swift-compile.mjs +1 -1
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +1 -1
- package/dist-lib/vite.cjs +1 -1
- package/kotlin/AppSources/android/app/Activity.kt +48 -3
- package/kotlin/AppSources/android/app/Application.kt +4 -0
- package/kotlin/AppSources/android/content/res/Configuration.kt +4 -0
- package/kotlin/AppSources/android/content/res/Resources.kt +3 -1
- package/kotlin/AppSources/android/view/Window.kt +15 -9
- package/kotlin/AppSources/androidx/activity/ComponentActivity.kt +36 -3
- package/kotlin/AppSources/dev/peach/app/PeachApp.kt +233 -86
- package/kotlin/ComposeSources/PeachComposeHost.kt +15 -3
- package/kotlin/fork/README.md +1 -0
- package/kotlin/fork/compose-worker-globals.patch +26 -0
- package/kotlin/fork/fork.json +1 -1
- package/kotlin/toolchain.json +3 -3
- package/package.json +2 -1
- package/src/android-theme.ts +14 -0
- package/src/backend-origin.ts +25 -0
- package/src/host/bridge-host.ts +24 -37
- package/src/kotlin-app.ts +29 -4
- package/src/kotlin-host.ts +7 -2
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// the shared libraries the linux browser links. a host that lacks any of them
|
|
2
|
+
// gets peach's own bundle of ubuntu 20.04's builds (scripts/linux-browser-libs.ts
|
|
3
|
+
// pins, builds and publishes it), unpacked once into the peach home and put on
|
|
4
|
+
// the browser's library path, so no host needs a package manager.
|
|
5
|
+
|
|
6
|
+
import { spawnSync } from 'node:child_process'
|
|
7
|
+
import { existsSync } from 'node:fs'
|
|
8
|
+
import { join } from 'node:path'
|
|
9
|
+
import { installVerifiedTarball } from '@contrast/runtime-delivery'
|
|
10
|
+
import { rnxHomeDir } from '../../src/home-paths'
|
|
11
|
+
import { isRecord } from '../../src/is-record'
|
|
12
|
+
import { DEFAULT_CLI_RELEASE_ORIGIN } from '../cli-update'
|
|
13
|
+
import lockJson from './linux-browser-libs.lock.json' with { type: 'json' }
|
|
14
|
+
|
|
15
|
+
// the debian package providing each shared library chromium and its headless
|
|
16
|
+
// shell link (readelf NEEDED, linux x64 and arm64), apart from glibc's own.
|
|
17
|
+
export const LIBRARY_PACKAGES: Record<string, string> = {
|
|
18
|
+
'libasound.so.2': 'libasound2',
|
|
19
|
+
'libatk-1.0.so.0': 'libatk1.0-0',
|
|
20
|
+
'libatk-bridge-2.0.so.0': 'libatk-bridge2.0-0',
|
|
21
|
+
'libatspi.so.0': 'libatspi2.0-0',
|
|
22
|
+
'libcairo.so.2': 'libcairo2',
|
|
23
|
+
'libcups.so.2': 'libcups2',
|
|
24
|
+
'libdbus-1.so.3': 'libdbus-1-3',
|
|
25
|
+
'libexpat.so.1': 'libexpat1',
|
|
26
|
+
'libgbm.so.1': 'libgbm1',
|
|
27
|
+
'libgio-2.0.so.0': 'libglib2.0-0',
|
|
28
|
+
'libglib-2.0.so.0': 'libglib2.0-0',
|
|
29
|
+
'libgobject-2.0.so.0': 'libglib2.0-0',
|
|
30
|
+
'libnspr4.so': 'libnspr4',
|
|
31
|
+
'libnss3.so': 'libnss3',
|
|
32
|
+
'libnssutil3.so': 'libnss3',
|
|
33
|
+
'libpango-1.0.so.0': 'libpango-1.0-0',
|
|
34
|
+
'libsmime3.so': 'libnss3',
|
|
35
|
+
'libudev.so.1': 'libudev1',
|
|
36
|
+
'libX11.so.6': 'libx11-6',
|
|
37
|
+
'libxcb.so.1': 'libxcb1',
|
|
38
|
+
'libXcomposite.so.1': 'libxcomposite1',
|
|
39
|
+
'libXdamage.so.1': 'libxdamage1',
|
|
40
|
+
'libXext.so.6': 'libxext6',
|
|
41
|
+
'libXfixes.so.3': 'libxfixes3',
|
|
42
|
+
'libxkbcommon.so.0': 'libxkbcommon0',
|
|
43
|
+
'libXrandr.so.2': 'libxrandr2',
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const LINUX_BROWSER_LIBS_ARCHES = ['x64', 'arm64'] as const
|
|
47
|
+
export type LinuxBrowserLibsArch = (typeof LINUX_BROWSER_LIBS_ARCHES)[number]
|
|
48
|
+
export const LINUX_BROWSER_LIBS_MARKER = 'peach-linux-libs.json'
|
|
49
|
+
|
|
50
|
+
export type LinuxBrowserLibsPackage = {
|
|
51
|
+
name: string
|
|
52
|
+
version: string
|
|
53
|
+
url: string
|
|
54
|
+
sha256: string
|
|
55
|
+
size: number
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type LinuxBrowserLibsLock = {
|
|
59
|
+
id: string
|
|
60
|
+
suite: string
|
|
61
|
+
arches: Record<LinuxBrowserLibsArch, LinuxBrowserLibsPackage[]>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isLockPackage(value: unknown): value is LinuxBrowserLibsPackage {
|
|
65
|
+
return (
|
|
66
|
+
isRecord(value) &&
|
|
67
|
+
typeof value.name === 'string' &&
|
|
68
|
+
typeof value.version === 'string' &&
|
|
69
|
+
typeof value.url === 'string' &&
|
|
70
|
+
typeof value.sha256 === 'string' &&
|
|
71
|
+
/^[a-f0-9]{64}$/.test(value.sha256) &&
|
|
72
|
+
typeof value.size === 'number'
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function parseLinuxBrowserLibsLock(value: unknown): LinuxBrowserLibsLock {
|
|
77
|
+
if (
|
|
78
|
+
!isRecord(value) ||
|
|
79
|
+
typeof value.id !== 'string' ||
|
|
80
|
+
!/^[a-f0-9]{16}$/.test(value.id) ||
|
|
81
|
+
typeof value.suite !== 'string' ||
|
|
82
|
+
!isRecord(value.arches)
|
|
83
|
+
) {
|
|
84
|
+
throw new Error('invalid linux browser library lock')
|
|
85
|
+
}
|
|
86
|
+
const { x64, arm64 } = value.arches
|
|
87
|
+
if (
|
|
88
|
+
!Array.isArray(x64) ||
|
|
89
|
+
!x64.every(isLockPackage) ||
|
|
90
|
+
!Array.isArray(arm64) ||
|
|
91
|
+
!arm64.every(isLockPackage)
|
|
92
|
+
) {
|
|
93
|
+
throw new Error('invalid linux browser library lock packages')
|
|
94
|
+
}
|
|
95
|
+
return { id: value.id, suite: value.suite, arches: { x64, arm64 } }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** the bucket path under the cli release origin. */
|
|
99
|
+
export function linuxBrowserLibsKey(id: string, arch: LinuxBrowserLibsArch): string {
|
|
100
|
+
return `linux-libs/${id}/${arch}.tar.gz`
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function missingLibraries(executablePath: string, libraryPath: string | null): string[] {
|
|
104
|
+
const ldd = spawnSync('ldd', [executablePath], {
|
|
105
|
+
encoding: 'utf8',
|
|
106
|
+
timeout: 30_000,
|
|
107
|
+
env: libraryPath
|
|
108
|
+
? {
|
|
109
|
+
...process.env,
|
|
110
|
+
LD_LIBRARY_PATH: [libraryPath, process.env.LD_LIBRARY_PATH]
|
|
111
|
+
.filter(Boolean)
|
|
112
|
+
.join(':'),
|
|
113
|
+
}
|
|
114
|
+
: process.env,
|
|
115
|
+
})
|
|
116
|
+
return [...(ldd.stdout ?? '').matchAll(/^\s*(\S+) => not found$/gm)].map(
|
|
117
|
+
(match) => match[1]!,
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export type LinuxBrowserLibrariesResult =
|
|
122
|
+
| { ok: true; libraryPath: string | null }
|
|
123
|
+
| { ok: false; message: string }
|
|
124
|
+
|
|
125
|
+
/** a linux browser whose shared libraries are missing exits 127 before
|
|
126
|
+
* playwright can say why. when any is missing, install peach's bundle and
|
|
127
|
+
* return the library path the browser must launch with. */
|
|
128
|
+
export async function ensureLinuxBrowserLibraries(
|
|
129
|
+
executablePath: string,
|
|
130
|
+
browser: string,
|
|
131
|
+
): Promise<LinuxBrowserLibrariesResult> {
|
|
132
|
+
if (process.platform !== 'linux') return { ok: true, libraryPath: null }
|
|
133
|
+
const missing = missingLibraries(executablePath, null)
|
|
134
|
+
if (!missing.length) return { ok: true, libraryPath: null }
|
|
135
|
+
const arch = LINUX_BROWSER_LIBS_ARCHES.find((candidate) => candidate === process.arch)
|
|
136
|
+
if (!arch) {
|
|
137
|
+
return {
|
|
138
|
+
ok: false,
|
|
139
|
+
message: `${browser} at ${executablePath} cannot start: this system is missing shared libraries it links (${missing.join(', ')}), and peach has no library bundle for ${process.arch}.`,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const lock = parseLinuxBrowserLibsLock(lockJson)
|
|
144
|
+
const dir = join(rnxHomeDir(), 'linux-libs', `${lock.id}-${arch}`)
|
|
145
|
+
if (!existsSync(join(dir, LINUX_BROWSER_LIBS_MARKER))) {
|
|
146
|
+
const url = `${DEFAULT_CLI_RELEASE_ORIGIN}/${linuxBrowserLibsKey(lock.id, arch)}`
|
|
147
|
+
process.stderr.write(
|
|
148
|
+
`peach: this system lacks ${missing.length} shared libraries ${browser} links; downloading them…\n`,
|
|
149
|
+
)
|
|
150
|
+
try {
|
|
151
|
+
const sidecar = await fetch(`${url}.sha256`)
|
|
152
|
+
if (!sidecar.ok)
|
|
153
|
+
throw new Error(`${url}.sha256: ${sidecar.status} ${sidecar.statusText}`)
|
|
154
|
+
await installVerifiedTarball({
|
|
155
|
+
url,
|
|
156
|
+
digest: {
|
|
157
|
+
algorithm: 'sha256',
|
|
158
|
+
encoding: 'hex',
|
|
159
|
+
value: (await sidecar.text()).trim(),
|
|
160
|
+
},
|
|
161
|
+
destDir: dir,
|
|
162
|
+
installedMarker: LINUX_BROWSER_LIBS_MARKER,
|
|
163
|
+
label: `linux browser libraries ${lock.id} (${arch})`,
|
|
164
|
+
})
|
|
165
|
+
} catch (error) {
|
|
166
|
+
return {
|
|
167
|
+
ok: false,
|
|
168
|
+
message: `${browser} at ${executablePath} cannot start: this system is missing shared libraries it links (${missing.join(', ')}), and downloading them failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const libraryPath = [join(dir, 'lib'), join(dir, 'lib', 'nss')].join(':')
|
|
174
|
+
const stillMissing = missingLibraries(executablePath, libraryPath)
|
|
175
|
+
if (stillMissing.length) {
|
|
176
|
+
return {
|
|
177
|
+
ok: false,
|
|
178
|
+
message: `${browser} at ${executablePath} cannot start: neither this system nor peach's library bundle (${dir}) provides ${stillMissing.join(', ')}.`,
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return { ok: true, libraryPath }
|
|
182
|
+
}
|
|
@@ -7,6 +7,7 @@ import { playwrightCoreDir } from '../../src/home-paths'
|
|
|
7
7
|
import { isRecord } from '../../src/is-record'
|
|
8
8
|
import { RNX_INTERNAL_CHILDREN, RNX_INTERNAL_COMMAND } from '../internal-child'
|
|
9
9
|
import { rnxSelfInvocation } from '../self-invocation'
|
|
10
|
+
import { ensureLinuxBrowserLibraries } from './linux-browser-libs'
|
|
10
11
|
import { PINNED_PLAYWRIGHT_CORE } from './playwright-core-pin'
|
|
11
12
|
|
|
12
13
|
export interface ResolvedPlaywright {
|
|
@@ -25,6 +26,8 @@ export type PlaywrightBrowserProvisioningResult =
|
|
|
25
26
|
ok: true
|
|
26
27
|
executablePath: string
|
|
27
28
|
installed: boolean
|
|
29
|
+
// the shared library path the browser must launch with, when the system lacks some
|
|
30
|
+
libraryPath: string | null
|
|
28
31
|
}
|
|
29
32
|
| {
|
|
30
33
|
ok: false
|
|
@@ -150,20 +153,25 @@ function probeBrowser(
|
|
|
150
153
|
}
|
|
151
154
|
}
|
|
152
155
|
|
|
153
|
-
export function ensurePlaywrightBrowser(
|
|
156
|
+
export async function ensurePlaywrightBrowser(
|
|
154
157
|
resolved: ResolvedPlaywright,
|
|
155
158
|
headless: boolean,
|
|
156
159
|
env: NodeJS.ProcessEnv = process.env,
|
|
157
|
-
): PlaywrightBrowserProvisioningResult {
|
|
160
|
+
): Promise<PlaywrightBrowserProvisioningResult> {
|
|
158
161
|
const headlessShell = headless && process.platform !== 'darwin'
|
|
159
162
|
const probeEnv = { ...env, SOOTSIM_PW_HEADLESS_SHELL: headlessShell ? '1' : '0' }
|
|
160
163
|
const browser = headlessShell ? 'chromium-headless-shell' : 'chromium'
|
|
161
164
|
const before = probeBrowser(resolved, probeEnv)
|
|
162
165
|
if (!before.ok) return before
|
|
163
166
|
if (before.exists) {
|
|
164
|
-
const
|
|
165
|
-
if (
|
|
166
|
-
return {
|
|
167
|
+
const libraries = await ensureLinuxBrowserLibraries(before.executablePath, browser)
|
|
168
|
+
if (!libraries.ok) return libraries
|
|
169
|
+
return {
|
|
170
|
+
ok: true,
|
|
171
|
+
executablePath: before.executablePath,
|
|
172
|
+
installed: false,
|
|
173
|
+
libraryPath: libraries.libraryPath,
|
|
174
|
+
}
|
|
167
175
|
}
|
|
168
176
|
|
|
169
177
|
// install only when neither an explicit binary nor an installed cache
|
|
@@ -211,98 +219,12 @@ export function ensurePlaywrightBrowser(
|
|
|
211
219
|
`${browser} executable is still missing at ${after.executablePath}`,
|
|
212
220
|
}
|
|
213
221
|
}
|
|
214
|
-
const
|
|
215
|
-
if (
|
|
216
|
-
return {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const LIBRARY_PACKAGES: Record<string, string> = {
|
|
222
|
-
'libasound.so.2': 'libasound2',
|
|
223
|
-
'libatk-1.0.so.0': 'libatk1.0-0',
|
|
224
|
-
'libatk-bridge-2.0.so.0': 'libatk-bridge2.0-0',
|
|
225
|
-
'libatspi.so.0': 'libatspi2.0-0',
|
|
226
|
-
'libcairo.so.2': 'libcairo2',
|
|
227
|
-
'libcups.so.2': 'libcups2',
|
|
228
|
-
'libdbus-1.so.3': 'libdbus-1-3',
|
|
229
|
-
'libexpat.so.1': 'libexpat1',
|
|
230
|
-
'libgbm.so.1': 'libgbm1',
|
|
231
|
-
'libgio-2.0.so.0': 'libglib2.0-0',
|
|
232
|
-
'libglib-2.0.so.0': 'libglib2.0-0',
|
|
233
|
-
'libgobject-2.0.so.0': 'libglib2.0-0',
|
|
234
|
-
'libnspr4.so': 'libnspr4',
|
|
235
|
-
'libnss3.so': 'libnss3',
|
|
236
|
-
'libnssutil3.so': 'libnss3',
|
|
237
|
-
'libpango-1.0.so.0': 'libpango-1.0-0',
|
|
238
|
-
'libsmime3.so': 'libnss3',
|
|
239
|
-
'libudev.so.1': 'libudev1',
|
|
240
|
-
'libX11.so.6': 'libx11-6',
|
|
241
|
-
'libxcb.so.1': 'libxcb1',
|
|
242
|
-
'libXcomposite.so.1': 'libxcomposite1',
|
|
243
|
-
'libXdamage.so.1': 'libxdamage1',
|
|
244
|
-
'libXext.so.6': 'libxext6',
|
|
245
|
-
'libXfixes.so.3': 'libxfixes3',
|
|
246
|
-
'libxkbcommon.so.0': 'libxkbcommon0',
|
|
247
|
-
'libXrandr.so.2': 'libxrandr2',
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// packages renamed by the 64-bit time_t transition in ubuntu 24.04 and debian 13.
|
|
251
|
-
const TIME64_PACKAGES = new Set([
|
|
252
|
-
'libasound2',
|
|
253
|
-
'libatk1.0-0',
|
|
254
|
-
'libatk-bridge2.0-0',
|
|
255
|
-
'libatspi2.0-0',
|
|
256
|
-
'libcups2',
|
|
257
|
-
'libglib2.0-0',
|
|
258
|
-
])
|
|
259
|
-
|
|
260
|
-
function isTime64Distro(): boolean {
|
|
261
|
-
if (!existsSync('/etc/os-release')) return false
|
|
262
|
-
const fields = Object.fromEntries(
|
|
263
|
-
readFileSync('/etc/os-release', 'utf8')
|
|
264
|
-
.split('\n')
|
|
265
|
-
.map((line) => line.match(/^(\w+)="?([^"]*)"?$/))
|
|
266
|
-
.filter((match) => match !== null)
|
|
267
|
-
.map((match) => [match[1], match[2]]),
|
|
268
|
-
)
|
|
269
|
-
const version = Number.parseFloat(fields.VERSION_ID ?? '')
|
|
270
|
-
return (
|
|
271
|
-
(fields.ID === 'ubuntu' && version >= 24.04) ||
|
|
272
|
-
(fields.ID === 'debian' && version >= 13)
|
|
273
|
-
)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/** a linux browser whose shared libraries are missing exits 127 before
|
|
277
|
-
* playwright can say why, so name them and their packages up front, as
|
|
278
|
-
* playwright's own host validation does. */
|
|
279
|
-
function missingLibrariesMessage(executablePath: string, browser: string): string | null {
|
|
280
|
-
if (process.platform !== 'linux') return null
|
|
281
|
-
const ldd = spawnSync('ldd', [executablePath], { encoding: 'utf8', timeout: 30_000 })
|
|
282
|
-
const libraries = [...(ldd.stdout ?? '').matchAll(/^\s*(\S+) => not found$/gm)].map(
|
|
283
|
-
(match) => match[1]!,
|
|
284
|
-
)
|
|
285
|
-
if (!libraries.length) return null
|
|
286
|
-
const time64 = isTime64Distro()
|
|
287
|
-
const packages = new Set<string>()
|
|
288
|
-
const unknown: string[] = []
|
|
289
|
-
for (const library of libraries) {
|
|
290
|
-
const name = LIBRARY_PACKAGES[library]
|
|
291
|
-
if (!name) unknown.push(library)
|
|
292
|
-
else packages.add(time64 && TIME64_PACKAGES.has(name) ? `${name}t64` : name)
|
|
222
|
+
const libraries = await ensureLinuxBrowserLibraries(after.executablePath, browser)
|
|
223
|
+
if (!libraries.ok) return libraries
|
|
224
|
+
return {
|
|
225
|
+
ok: true,
|
|
226
|
+
executablePath: after.executablePath,
|
|
227
|
+
installed: true,
|
|
228
|
+
libraryPath: libraries.libraryPath,
|
|
293
229
|
}
|
|
294
|
-
const sudo = process.getuid?.() === 0 ? '' : 'sudo '
|
|
295
|
-
return [
|
|
296
|
-
`${browser} at ${executablePath} cannot start: this system is missing shared libraries it links (${libraries.join(', ')}).`,
|
|
297
|
-
...(packages.size
|
|
298
|
-
? [
|
|
299
|
-
'On Debian or Ubuntu, install them and run the command again:',
|
|
300
|
-
'',
|
|
301
|
-
` ${sudo}apt-get update && ${sudo}apt-get install -y --no-install-recommends ${[...packages].join(' ')}`,
|
|
302
|
-
]
|
|
303
|
-
: []),
|
|
304
|
-
...(unknown.length
|
|
305
|
-
? ['', `No known Debian package provides: ${unknown.join(', ')}`]
|
|
306
|
-
: []),
|
|
307
|
-
].join('\n')
|
|
308
230
|
}
|
|
@@ -27,6 +27,9 @@ import { DISPOSABLE_BROWSER_CACHE_DIR_NAMES } from '../../src/browser-cache'
|
|
|
27
27
|
export const PLAYWRIGHT_SIM_HOST = `
|
|
28
28
|
const modulePath = process.env.SOOTSIM_PW_MODULE;
|
|
29
29
|
const executablePath = process.env.SOOTSIM_PW_EXECUTABLE;
|
|
30
|
+
// the shared libraries this host lacks, from peach's bundle. only the browser
|
|
31
|
+
// gets them on its library path: the bundled builds are older than the system's.
|
|
32
|
+
const libraryPath = process.env.SOOTSIM_PW_LIBRARY_PATH;
|
|
30
33
|
if (!executablePath) throw new Error('SOOTSIM_PW_EXECUTABLE is not set');
|
|
31
34
|
const url = process.env.SOOTSIM_PW_URL;
|
|
32
35
|
// the WS bridge port the sim page must register with, injected below as
|
|
@@ -335,6 +338,12 @@ const cleanupProfile = () => {
|
|
|
335
338
|
? { headless, channel, viewport, deviceScaleFactor }
|
|
336
339
|
: { headless, viewport, deviceScaleFactor };
|
|
337
340
|
opts.executablePath = executablePath;
|
|
341
|
+
if (libraryPath) {
|
|
342
|
+
opts.env = {
|
|
343
|
+
...process.env,
|
|
344
|
+
LD_LIBRARY_PATH: [libraryPath, process.env.LD_LIBRARY_PATH].filter(Boolean).join(':'),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
338
347
|
// playwright injects --force-color-profile=srgb by default, which reads
|
|
339
348
|
// visibly washed-out next to a normal chrome window on a wide-gamut mac
|
|
340
349
|
// display. strip it so driver-launched sims color-match the user's own
|
|
@@ -103,7 +103,7 @@ async function launch(opts: DriverLaunchOptions): Promise<DriverLaunchResult> {
|
|
|
103
103
|
// keep browser discovery, installation, and the detached host on one exact
|
|
104
104
|
// cache contract even if another caller mutates process.env during launch.
|
|
105
105
|
const playwrightEnv = { ...process.env }
|
|
106
|
-
const provisioning = ensurePlaywrightBrowser(
|
|
106
|
+
const provisioning = await ensurePlaywrightBrowser(
|
|
107
107
|
resolved,
|
|
108
108
|
opts.headless ?? true,
|
|
109
109
|
playwrightEnv,
|
|
@@ -151,6 +151,7 @@ async function launch(opts: DriverLaunchOptions): Promise<DriverLaunchResult> {
|
|
|
151
151
|
SOOTSIM_PW_CONNECTED_ACK_FILE: connectAckFile,
|
|
152
152
|
SOOTSIM_PW_CONNECT_TIMEOUT_MS: String(opts.connectTimeoutMs ?? 120_000),
|
|
153
153
|
SOOTSIM_PW_OWNER_PID: String(ownerPid),
|
|
154
|
+
SOOTSIM_PW_LIBRARY_PATH: provisioning.libraryPath ?? '',
|
|
154
155
|
},
|
|
155
156
|
},
|
|
156
157
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
|
@@ -26,11 +26,13 @@ __export(backend_origin_exports, {
|
|
|
26
26
|
LOOPBACK_PROBE_HOSTNAMES: () => LOOPBACK_PROBE_HOSTNAMES,
|
|
27
27
|
buildBrowserAssetProxyUrl: () => buildBrowserAssetProxyUrl,
|
|
28
28
|
buildLoopbackProbeBaseUrl: () => buildLoopbackProbeBaseUrl,
|
|
29
|
+
isBrowserAssetProxyUrl: () => isBrowserAssetProxyUrl,
|
|
29
30
|
isContrastBackendHost: () => isContrastBackendHost,
|
|
30
31
|
isContrastBackendOrigin: () => isContrastBackendOrigin,
|
|
31
32
|
isLoopbackHost: () => isLoopbackHost,
|
|
32
33
|
loopbackOriginOf: () => loopbackOriginOf,
|
|
33
34
|
normalizeHostname: () => normalizeHostname,
|
|
35
|
+
resolveBrowserAssetProxyUrl: () => resolveBrowserAssetProxyUrl,
|
|
34
36
|
resolveContrastBackendOrigin: () => resolveContrastBackendOrigin
|
|
35
37
|
});
|
|
36
38
|
module.exports = __toCommonJS(backend_origin_exports);
|
|
@@ -60,6 +62,19 @@ function buildBrowserAssetProxyUrl(targetUrl) {
|
|
|
60
62
|
const proxyPath = isLoopbackHost(new URL(targetUrl).hostname) ? "/__bundle-proxy" : "/__fetch-proxy";
|
|
61
63
|
return `${proxyPath}?url=${encodeURIComponent(targetUrl)}`;
|
|
62
64
|
}
|
|
65
|
+
function resolveBrowserAssetProxyUrl(targetUrl, currentOrigin) {
|
|
66
|
+
const proxyUrl = isBrowserAssetProxyUrl(targetUrl) ? targetUrl : buildBrowserAssetProxyUrl(targetUrl);
|
|
67
|
+
if (proxyUrl.startsWith("/__bundle-proxy?")) return proxyUrl;
|
|
68
|
+
return new URL(proxyUrl, resolveContrastBackendOrigin(currentOrigin)).toString();
|
|
69
|
+
}
|
|
70
|
+
function isBrowserAssetProxyUrl(value) {
|
|
71
|
+
try {
|
|
72
|
+
const url = new URL(value, DEFAULT_CONTRAST_BACKEND_ORIGIN);
|
|
73
|
+
return isContrastBackendOrigin(url.origin) && url.pathname === "/__fetch-proxy" && url.searchParams.has("url");
|
|
74
|
+
} catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
63
78
|
function isContrastBackendHost(hostname) {
|
|
64
79
|
const normalized = normalizeHostname(hostname);
|
|
65
80
|
return isLoopbackHost(normalized) || normalized === "contrast.dev" || normalized.endsWith(".contrast.dev");
|
|
@@ -89,10 +104,12 @@ function resolveContrastBackendOrigin(currentOrigin) {
|
|
|
89
104
|
LOOPBACK_PROBE_HOSTNAMES,
|
|
90
105
|
buildBrowserAssetProxyUrl,
|
|
91
106
|
buildLoopbackProbeBaseUrl,
|
|
107
|
+
isBrowserAssetProxyUrl,
|
|
92
108
|
isContrastBackendHost,
|
|
93
109
|
isContrastBackendOrigin,
|
|
94
110
|
isLoopbackHost,
|
|
95
111
|
loopbackOriginOf,
|
|
96
112
|
normalizeHostname,
|
|
113
|
+
resolveBrowserAssetProxyUrl,
|
|
97
114
|
resolveContrastBackendOrigin
|
|
98
115
|
});
|
package/dist-lib/beta.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
package/dist-lib/beta.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
package/dist-lib/cloud.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
package/dist-lib/cloud.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
package/dist-lib/config.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
package/dist-lib/detox/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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 __defProp = Object.defineProperty;
|
package/dist-lib/home-paths.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.661 | (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;
|