mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-26 10:13:27 +08:00
feat(browser): support lightweight Docker browser installs (#154608)
* refactor(browser): share engine adapters for Chromium and Lightpanda Refresh signed stack identities without changing any source tree. Previous signed head:bed85c558dPR06 CI tested stale synthetic merge22409995, whose main base80c3c742 predates Peter Steinberger's canonical static-validation fix82411a34. Fresh synchronization must check the repaired merge context; neither old failed CI nor old passing runtime proof is relabeled. Preserve every source/test/dependency blob and previously qualified registry/catalog fixture correction. * improve(browser): default managed Chromium to efficient headless launches Refresh signed stack identities without changing any source tree. Previous signed head:b6cda11dd8PR06 CI tested stale synthetic merge22409995, whose main base80c3c742 predates Peter Steinberger's canonical static-validation fix82411a34. Fresh synchronization must check the repaired merge context; neither old failed CI nor old passing runtime proof is relabeled. Preserve every source/test/dependency blob and previously qualified registry/catalog fixture correction. * improve(browser): trim Docker browser images and harden Lightpanda sidecar Refresh signed stack identities without changing any source tree. Previous signed head:e6d320e30aPR06 CI tested stale synthetic merge22409995, whose main base80c3c742 predates Peter Steinberger's canonical static-validation fix82411a34. Fresh synchronization must check the repaired merge context; neither old failed CI nor old passing runtime proof is relabeled. Preserve every source/test/dependency blob and previously qualified registry/catalog fixture correction. * test(agents): retain canonical restored-run settlement fixture * test(agents): preserve upstream requester-disposal completion ordering * fix(browser): preserve shared Chromium discovery and Lightpanda lifecycle * fix(browser): align lightweight image integration Preserve the current shared browser discovery helper and renderer fixtures after baseline integration. Leave Lightpanda resource ceilings with operators and keep selected-image proof wording version neutral.
This commit is contained in:
1 parent
1adbd71472
commit
d4eccd8345
11 files changed
+170
-29
No files matched your search
+16
-11
@@ -357,22 +357,27 @@ RUN --mount=type=cache,id=openclaw-trixie-apt-cache,target=/var/cache/apt,sharin
|
||||
python3 -m pip install --no-cache-dir --break-system-packages $OPENCLAW_IMAGE_PIP_PACKAGES; \
|
||||
fi
|
||||
|
||||
# Optionally install Chromium and Xvfb for browser automation.
|
||||
# Build with: docker build --build-arg OPENCLAW_INSTALL_BROWSER=1 ...
|
||||
# Adds ~300MB but eliminates the 60-90s Playwright install on every container start.
|
||||
# Optionally install Chromium distributions for browser automation.
|
||||
# Legacy 1 keeps full Chromium and headless shell; explicit chromium or
|
||||
# headless-shell installs only that distribution; managed shell use requires an
|
||||
# explicit executablePath on a separate headless browser profile.
|
||||
# Must run after node_modules COPY so playwright-core is available.
|
||||
ARG OPENCLAW_INSTALL_BROWSER=""
|
||||
ENV PLAYWRIGHT_BROWSERS_PATH=/home/node/.cache/ms-playwright
|
||||
RUN --mount=type=cache,id=openclaw-trixie-apt-cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,id=openclaw-trixie-apt-lists,target=/var/lib/apt,sharing=locked \
|
||||
if [ -n "$OPENCLAW_INSTALL_BROWSER" ]; then \
|
||||
apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends xvfb && \
|
||||
install -d -m 0755 -o node -g node "$(dirname "$PLAYWRIGHT_BROWSERS_PATH")" && \
|
||||
mkdir -p "$PLAYWRIGHT_BROWSERS_PATH" && \
|
||||
node /app/node_modules/playwright-core/cli.js install --with-deps chromium && \
|
||||
chown -R node:node "$PLAYWRIGHT_BROWSERS_PATH"; \
|
||||
fi
|
||||
set -eu; \
|
||||
case "$OPENCLAW_INSTALL_BROWSER" in \
|
||||
""|0) exit 0 ;; \
|
||||
1) set -- ;; \
|
||||
chromium) set -- --no-shell ;; \
|
||||
headless-shell) set -- --only-shell ;; \
|
||||
*) echo 'OPENCLAW_INSTALL_BROWSER must be empty, 0, 1, chromium, or headless-shell' >&2; exit 1 ;; \
|
||||
esac; \
|
||||
install -d -m 0755 -o node -g node "$(dirname "$PLAYWRIGHT_BROWSERS_PATH")" && \
|
||||
mkdir -p "$PLAYWRIGHT_BROWSERS_PATH" && \
|
||||
node /app/node_modules/playwright-core/cli.js install --with-deps "$@" chromium && \
|
||||
chown -R node:node "$PLAYWRIGHT_BROWSERS_PATH"
|
||||
|
||||
# Optionally install Docker CLI for sandbox container management.
|
||||
# Build with: docker build --build-arg OPENCLAW_INSTALL_DOCKER_CLI=1 ...
|
||||
|
||||
@@ -6,10 +6,15 @@ They do not add an engine binary, container layer, or npm dependency to OpenClaw
|
||||
|
||||
## License boundary
|
||||
|
||||
OpenClaw's adapter is MIT-licensed. Lightpanda 1.0.2 is
|
||||
**AGPL-3.0-or-later**, as stated in its
|
||||
OpenClaw's adapter is MIT-licensed. Lightpanda 1.0.2 is **AGPL**, with an
|
||||
upstream declaration inconsistency at its selected source commit: its
|
||||
[licensing document](https://github.com/lightpanda-io/browser/blob/c5f7bb29b127040c899d4e181b5709f76fc71a6f/LICENSING.md)
|
||||
says `AGPL-3.0-only`, while its
|
||||
[source header](https://github.com/lightpanda-io/browser/blob/c5f7bb29b127040c899d4e181b5709f76fc71a6f/src/main.zig#L1)
|
||||
and [license](https://github.com/lightpanda-io/browser/blob/c5f7bb29b127040c899d4e181b5709f76fc71a6f/LICENSE).
|
||||
allows version 3 or later. Preserve both declarations during redistribution
|
||||
review; neither makes the engine permissively licensed. The
|
||||
[license text](https://github.com/lightpanda-io/browser/blob/c5f7bb29b127040c899d4e181b5709f76fc71a6f/LICENSE)
|
||||
is AGPL version 3.
|
||||
The engine is independently installed and communicates with OpenClaw over CDP;
|
||||
OpenClaw does not link, vendor, or relicense its implementation. This is not an
|
||||
MIT-only stack or a certification that every deployment is legally compliant.
|
||||
@@ -78,6 +83,11 @@ Linux ARM64, and both macOS architectures remain unverified by this update.
|
||||
The other three native binary pins were checked against release API digests,
|
||||
without download or execution.
|
||||
|
||||
The retained adapter observations above do not qualify this version's Compose
|
||||
WebSocket-readiness probe, independent probe-session close, crash restart, or
|
||||
SIGTERM/stop-grace behavior. Those container-specific checks require execution
|
||||
against the exact pinned image; historical 0.4.1 results are not proof for the selected image.
|
||||
|
||||
## Existing JavaScript clients
|
||||
|
||||
The following client inspection records the **2026-09-21** audit. Current client
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
# the repository's docker-compose.yml for an unpublished container-only endpoint.
|
||||
services:
|
||||
lightpanda:
|
||||
# Optional external AGPL-3.0-or-later engine, not an MIT-only image.
|
||||
# Optional external AGPL engine, not an MIT-only image. Upstream declarations
|
||||
# disagree on -only vs -or-later; see README.md for the source evidence.
|
||||
# See README.md before mirroring, bundling, or modifying this distribution.
|
||||
# Official 1.0.2 multi-platform index: linux/amd64 and linux/arm64.
|
||||
image: docker.io/lightpanda/browser:1.0.2@sha256:5d1d724c476957d8c2f8393a51fd089477523b0e5aa9c783c48688785d934fb9
|
||||
@@ -14,5 +15,26 @@ services:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
# This probe requires Bash /dev/tcp in the pinned distribution. Test the CDP
|
||||
# WebSocket upgrade, not a separate `lightpanda version` process. This probe
|
||||
# opens/closes its own session and does not touch an agent's active session.
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- /bin/bash
|
||||
- -ec
|
||||
- >-
|
||||
exec 3<>/dev/tcp/127.0.0.1/9222;
|
||||
printf 'GET / HTTP/1.1\r\nHost: 127.0.0.1:9222\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n' >&3;
|
||||
IFS= read -r response <&3;
|
||||
[[ "$$response" == *" 101 "* ]]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
start_period: 10s
|
||||
retries: 3
|
||||
# Override the upstream image's SIGKILL so Tini can forward graceful stop.
|
||||
stop_signal: SIGTERM
|
||||
stop_grace_period: 10s
|
||||
# The official image already runs as its lightpanda user under tini.
|
||||
# Docker restarts exited processes, not a still-running unhealthy process.
|
||||
restart: unless-stopped
|
||||
+38
-3
@@ -147,8 +147,43 @@ For a new installation from a local source build, bake Chromium into the image:
|
||||
OPENCLAW_IMAGE=openclaw:local OPENCLAW_INSTALL_BROWSER=1 ./scripts/docker/setup.sh
|
||||
```
|
||||
|
||||
This build-time option installs Chromium and Xvfb; setting it on
|
||||
an already-built container does not install a browser.
|
||||
`OPENCLAW_INSTALL_BROWSER=1` installs full Chromium and headless shell, preserving
|
||||
existing Playwright scripts that use default headless launches. Choose `chromium`
|
||||
to install only full Chromium, or `headless-shell` for a smaller, headless-only image:
|
||||
|
||||
```bash
|
||||
OPENCLAW_IMAGE=openclaw:local OPENCLAW_INSTALL_BROWSER=headless-shell ./scripts/docker/setup.sh
|
||||
```
|
||||
|
||||
All modes use the repository-pinned Playwright installer, including its system
|
||||
libraries, fonts, and FFmpeg helper. Empty or `0` omits the browser. These are
|
||||
build-time choices; setting them on an already-built container does not install
|
||||
or replace a browser. Headless shell cannot provide a headed browser window or
|
||||
Chrome extensions. Keep full Chromium for those workflows and validate your sites
|
||||
before switching. Neither browser distribution is MIT-only; inventory the actual
|
||||
image and preserve all third-party license notices.
|
||||
|
||||
OpenClaw automatically discovers full Chromium only. A `headless-shell` image
|
||||
requires an explicit shell executable path on a separate managed profile. Find
|
||||
the installed path inside the container (the revision directory changes with
|
||||
Playwright updates):
|
||||
|
||||
```bash
|
||||
find /home/node/.cache/ms-playwright -type f -name chrome-headless-shell
|
||||
```
|
||||
|
||||
Set `browser.profiles.shell.executablePath` to that path, give the profile its own
|
||||
`cdpPort`, and keep it headless. Select that profile when starting the browser.
|
||||
Do not reuse an existing full Chromium profile: switching distributions can
|
||||
discard localStorage. See [Browser configuration](/tools/browser/configuration).
|
||||
|
||||
With the explicit `chromium` mode,
|
||||
your own Playwright scripts must use `channel: "chromium"` for headless launches;
|
||||
Playwright's default headless launch expects the separate shell binary. Keep `1`
|
||||
to retain both binaries without changing existing scripts.
|
||||
The bundled Diffs PNG/PDF renderer supports all image modes. After explicit and
|
||||
system-browser choices, it selects Playwright's full Chromium when installed and
|
||||
otherwise retains Playwright's default headless-shell selection.
|
||||
|
||||
The image supplies Chromium, not a replacement for your browser configuration:
|
||||
|
||||
@@ -158,7 +193,7 @@ The image supplies Chromium, not a replacement for your browser configuration:
|
||||
- OpenClaw auto-detects the image's Playwright-managed Chromium on Linux. An
|
||||
explicit `browser.executablePath` or profile executable path must point to a
|
||||
binary inside the container; a path from your laptop will not work there.
|
||||
- A headless container needs headless browser operation. Check explicit
|
||||
- Managed browsers run headless by default. Check explicit
|
||||
`browser.headless`, profile headless settings, and `OPENCLAW_BROWSER_HEADLESS`
|
||||
overrides if startup reports a missing display. See [Browser configuration](/tools/browser-control).
|
||||
- Connect with `operator.admin` access to a Gateway advertising `browser.request`.
|
||||
|
||||
@@ -23,7 +23,7 @@ Optional variables accepted by `scripts/docker/setup.sh` (and, for the gateway c
|
||||
| `OPENCLAW_DOCKER_BUILD_NODE_OPTIONS` | Override the local source-build Node options (default `--max-old-space-size=8192`) |
|
||||
| `OPENCLAW_DOCKER_BUILD_TSDOWN_MAX_OLD_SPACE_MB` | Override the local source-build tsdown heap in MB |
|
||||
| `OPENCLAW_DOCKER_BUILD_SKIP_DTS` | Skip declaration output during runtime-only local image builds (default `1`) |
|
||||
| `OPENCLAW_INSTALL_BROWSER` | Bake Chromium + Xvfb into the image at build time |
|
||||
| `OPENCLAW_INSTALL_BROWSER` | Bake both Chromium distributions (`1`), only `chromium`, or only `headless-shell` into the image; empty or `0` omits the browser |
|
||||
| `OPENCLAW_EXTRA_MOUNTS` | Extra host bind mounts (comma-separated `source:target[:opts]`) |
|
||||
| `OPENCLAW_HOME_VOLUME` | Persist `/home/node` in a named Docker volume |
|
||||
| `OPENCLAW_TZ` | Set the gateway and CLI container timezone to an IANA name (default `UTC`) |
|
||||
|
||||
@@ -38,9 +38,12 @@ the Lightpanda engine. Existing profiles and engine configuration are unchanged.
|
||||
## Licensing and distribution
|
||||
|
||||
OpenClaw's adapter remains MIT-licensed. The optional Lightpanda engine is
|
||||
**AGPL-3.0-or-later**, not MIT: see its
|
||||
**AGPL**, not MIT. Version 1.0.2 has inconsistent declarations: its
|
||||
[licensing document](https://github.com/lightpanda-io/browser/blob/c5f7bb29b127040c899d4e181b5709f76fc71a6f/LICENSING.md)
|
||||
says `AGPL-3.0-only`, while its
|
||||
[pinned source notice](https://github.com/lightpanda-io/browser/blob/c5f7bb29b127040c899d4e181b5709f76fc71a6f/src/main.zig#L1)
|
||||
and [license](https://github.com/lightpanda-io/browser/blob/c5f7bb29b127040c899d4e181b5709f76fc71a6f/LICENSE).
|
||||
allows later versions. Record that discrepancy during your SBOM review; neither
|
||||
interpretation makes the engine permissively licensed.
|
||||
The existing Playwright client is Apache-2.0; the existing `ws` client is MIT.
|
||||
Their licenses and third-party notices still apply.
|
||||
|
||||
@@ -121,7 +124,8 @@ the official container image has Linux amd64 and arm64 variants.
|
||||
From the repository root:
|
||||
|
||||
```sh
|
||||
docker compose -f deploy/lightpanda/compose.yaml -f deploy/lightpanda/compose.host.yaml up -d
|
||||
docker compose -f deploy/lightpanda/compose.yaml -f deploy/lightpanda/compose.host.yaml up -d --wait
|
||||
docker compose -f deploy/lightpanda/compose.yaml -f deploy/lightpanda/compose.host.yaml ps
|
||||
docker compose -f deploy/lightpanda/compose.yaml -f deploy/lightpanda/compose.host.yaml exec lightpanda /bin/lightpanda version
|
||||
```
|
||||
|
||||
@@ -130,6 +134,21 @@ another host port, and update the profile URL to match. The image is pinned by i
|
||||
multi-platform digest, so Docker selects the host architecture without pulling a
|
||||
moving `latest` or `nightly` version.
|
||||
|
||||
The healthcheck opens an independent local CDP WebSocket session and checks its
|
||||
handshake. This proves listener readiness, not successful browsing or OpenClaw
|
||||
integration. The sample leaves memory, CPU, and PID limits unset. Set limits for
|
||||
your measured workload in your deployment's Compose configuration, or use
|
||||
[`docker update`](https://docs.docker.com/reference/cli/docker/container/update/)
|
||||
with `--memory`, `--cpus`, and `--pids-limit` for an existing container.
|
||||
`restart: unless-stopped` restarts a crashed engine but does not restart a
|
||||
still-running unhealthy container. Monitor unhealthy status and replace failed
|
||||
workers through your deployment's supervisor. Restarting loses that worker's
|
||||
page/login state; do not automatically replay clicks or submissions.
|
||||
|
||||
The stop signal is `SIGTERM`, with a 10-second grace period, overriding the
|
||||
upstream image's immediate `SIGKILL`. This is a single-worker example, not a
|
||||
high-availability service or transparent failover guarantee.
|
||||
|
||||
CDP gives a client control over the browser; the sample does not add CDP
|
||||
authentication. Do not change the loopback binding to a public address. Use an
|
||||
authenticated tunnel for access from another host.
|
||||
@@ -145,7 +164,7 @@ docker compose -f deploy/lightpanda/compose.yaml -f deploy/lightpanda/compose.ho
|
||||
Merge the sidecar into the repository's existing Compose project:
|
||||
|
||||
```sh
|
||||
docker compose -f docker-compose.yml -f deploy/lightpanda/compose.yaml up -d lightpanda
|
||||
docker compose -f docker-compose.yml -f deploy/lightpanda/compose.yaml up -d --wait lightpanda
|
||||
```
|
||||
|
||||
Configure the OpenClaw Gateway with `cdpUrl: "ws://lightpanda:9222"` in the profile
|
||||
@@ -371,7 +390,7 @@ container/binary version separately with the report. A Windows Node controller
|
||||
can use the same external-engine interface, but Windows Docker runtime behavior
|
||||
has not been verified for this sample.
|
||||
|
||||
Compare the same fixed tasks with a pinned Chromium baseline. Record
|
||||
Compare the same deterministic tasks with a pinned Chromium baseline. Record
|
||||
task completion before reporting speed or memory improvements; unsupported or
|
||||
failed work must not be counted as a successful fast result. Report warm and
|
||||
cold runs separately, engine versions, host OS/architecture, client overhead,
|
||||
|
||||
@@ -450,6 +450,7 @@ async function main() {
|
||||
const snapshot = await request("GET", "/snapshot", undefined, {
|
||||
targetId,
|
||||
format: "ai",
|
||||
mode: "efficient",
|
||||
});
|
||||
const text = String(snapshot.snapshot);
|
||||
const nameRef = /textbox[^\n]*\[ref=((?:f\d+)?e\d+)\]/.exec(text)?.[1];
|
||||
|
||||
@@ -19,8 +19,9 @@ import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vites
|
||||
import { registerDiffsPlugin } from "./plugin.js";
|
||||
import { createTempDiffRoot } from "./test-helpers.js";
|
||||
|
||||
const { launchMock } = vi.hoisted(() => ({
|
||||
const { launchMock, playwrightExecutablePathMock } = vi.hoisted(() => ({
|
||||
launchMock: vi.fn(),
|
||||
playwrightExecutablePathMock: vi.fn(),
|
||||
}));
|
||||
const toolContext = {
|
||||
agentId: "main",
|
||||
@@ -38,6 +39,7 @@ type ScreenshotParams = Parameters<
|
||||
vi.mock("playwright-core", () => ({
|
||||
chromium: {
|
||||
launch: launchMock,
|
||||
executablePath: playwrightExecutablePathMock,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -63,6 +65,7 @@ describe("PlaywrightDiffScreenshotter", () => {
|
||||
({ rootDir, cleanup: cleanupRootDir } = await createTempDiffRoot("openclaw-diffs-browser-"));
|
||||
outputPath = path.join(rootDir, "preview.png");
|
||||
launchMock.mockReset();
|
||||
playwrightExecutablePathMock.mockReset().mockReturnValue(path.join(rootDir, "chromium"));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -125,10 +128,41 @@ describe("PlaywrightDiffScreenshotter", () => {
|
||||
"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
"C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe",
|
||||
"C:\\Program Files (x86)\\BraveSoftware\\Brave-Browser\\Application\\brave.exe",
|
||||
playwrightExecutablePathMock(),
|
||||
]);
|
||||
expect(candidates.every((candidate) => path.win32.isAbsolute(candidate))).toBe(true);
|
||||
});
|
||||
|
||||
it.each([true, false])(
|
||||
"renders with Playwright-only installation when full Chromium is present=%s",
|
||||
async (hasFullChromium) => {
|
||||
vi.stubEnv("PATH", "");
|
||||
vi.stubEnv("OPENCLAW_BROWSER_EXECUTABLE_PATH", "");
|
||||
vi.stubEnv("BROWSER_EXECUTABLE_PATH", "");
|
||||
vi.stubEnv("PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH", "");
|
||||
const managedPath = path.join(rootDir, "playwright", "chromium", "chrome");
|
||||
playwrightExecutablePathMock.mockReturnValue(managedPath);
|
||||
vi.spyOn(fs, "access").mockImplementation(async (candidate) => {
|
||||
if (!hasFullChromium || candidate !== managedPath) {
|
||||
throw new Error("ENOENT");
|
||||
}
|
||||
});
|
||||
|
||||
launchMock.mockResolvedValue(createMockBrowser([]));
|
||||
const screenshotter = new PlaywrightDiffScreenshotter({ config: {} });
|
||||
await screenshotter.screenshotHtml(screenshotParams({ image: { scale: 1 } }));
|
||||
const [launchOptions] = expectDefined(launchMock.mock.calls[0], "browser launch");
|
||||
if (hasFullChromium) {
|
||||
// Playwright's default headless launch requests the absent shell binary.
|
||||
expect(launchOptions.executablePath).toBe(managedPath);
|
||||
} else {
|
||||
// A shell-only installation must retain Playwright's default selection.
|
||||
expect(launchOptions).not.toHaveProperty("executablePath");
|
||||
}
|
||||
await expect(fs.readFile(outputPath, "utf8")).resolves.toBe("png");
|
||||
},
|
||||
);
|
||||
|
||||
it("shares a browser across parallel renders and closes it after the idle window", async () => {
|
||||
const { pages, browser, screenshotter } = await createScreenshotterHarness();
|
||||
|
||||
|
||||
@@ -321,6 +321,14 @@ async function resolveBrowserExecutablePath(config: OpenClawConfig): Promise<str
|
||||
}
|
||||
}
|
||||
|
||||
// Full-only images omit the shell selected by an unspecified headless launch.
|
||||
// Keep discovery owned by the shared browser promise, using Playwright's public
|
||||
// full-browser path; absence preserves its default shell-only selection.
|
||||
const playwrightPath = chromium.executablePath();
|
||||
if (await canAccess(playwrightPath, fsConstants.X_OK)) {
|
||||
return playwrightPath;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,9 +90,13 @@ trap 'cleanup 143' TERM
|
||||
|
||||
mkdir -p "${HOME}" "${HOME}/.chrome" "${XDG_CONFIG_HOME}" "${XDG_CACHE_HOME}"
|
||||
|
||||
Xvfb :1 -screen 0 1280x800x24 -ac -nolisten tcp &
|
||||
XVFB_PID=$!
|
||||
echo "[sandbox] Xvfb started (PID: ${XVFB_PID})"
|
||||
# Headless Chromium does not use a display server. Keep the headed/noVNC path
|
||||
# unchanged, including when noVNC itself is disabled but Chromium is headed.
|
||||
if [[ "${HEADLESS}" != "1" ]]; then
|
||||
Xvfb :1 -screen 0 1280x800x24 -ac -nolisten tcp &
|
||||
XVFB_PID=$!
|
||||
echo "[sandbox] Xvfb started (PID: ${XVFB_PID})"
|
||||
fi
|
||||
|
||||
if [[ "${CDP_PORT}" -ge 65535 ]]; then
|
||||
CHROME_CDP_PORT="$((CDP_PORT - 1))"
|
||||
|
||||
@@ -982,7 +982,10 @@ printf 'status=%s\\n' "$status"
|
||||
[
|
||||
"ENV PLAYWRIGHT_BROWSERS_PATH=/home/node/.cache/ms-playwright",
|
||||
'mkdir -p "$PLAYWRIGHT_BROWSERS_PATH"',
|
||||
"node /app/node_modules/playwright-core/cli.js install --with-deps chromium",
|
||||
'node /app/node_modules/playwright-core/cli.js install --with-deps "$@" chromium',
|
||||
"1) set -- ;;",
|
||||
"chromium) set -- --no-shell",
|
||||
"headless-shell) set -- --only-shell",
|
||||
],
|
||||
],
|
||||
] as const)("root Dockerfile preserves %s", (_contract, fragments) => {
|
||||
|
||||
Reference in new issue
Block a user