Files
dsh-browser-install/README.md
T
Plugin Dev d2e2eaed8d dsh-browser-install: auto-install Playwright Chromium and bridge it for dsh-ego-browser
On load, ensures the Playwright browser target (default: chromium) is installed
via `npx --yes playwright install chromium --with-deps` (background when missing,
so profile startup is not blocked), then symlinks the installed binary at
/usr/local/bin/google-chrome-stable (+ chromium). That exact name is what
dsh-ego-browser's PATH scan and its bundled --no-sandbox wrapper both look for,
so root/Docker containers get a working agent browser with zero manual PATH,
env-var or wrapper steps. Pre-existing user binaries/wrappers are never
clobbered. Ships as a dsh bundle (plain ESM, no build step); install via
`dsh plugin --profile web add github:<you>/dsh-browser-install#main` and update
with `dsh plugin --profile web update` (branch ref resolves the tip).
2026-10-01 08:36:01 +01:00

7.6 KiB

dsh-browser-install

Automatically installs Playwright Chromium (npx playwright install chromium --with-deps) into the Docker container running DeepSeek Harness, and bridges the installed binary onto PATH so the dsh-ego-browser side-bar plugin finds it — no manual "find the binary / symlink it / set EGO_LINUX_* env vars / wrap it with --no-sandbox" steps for agents ever again.

Why this plugin exists

dsh-ego-browser discovers its browser the following way (see lib/index.js → resolveEgoEnv and runtime/ego-linux/src/chrome.mjs):

  1. It scans PATH + fixed locations for google-chrome, google-chrome-stable, chromium, chromium-browser, … and
  2. on root hosts (docker default), it hands the found binary to a bundled --no-sandbox wrapper that only looks at fixed paths (/usr/bin/*, /opt/google/..., /usr/local/bin/google-chrome-stable, /snap/bin/chromium).

Playwright installs Chromium into ~/.cache/ms-playwright/chromium-<rev>/chrome-linux{,64}/chrome — a location invisible to both scans. That is why agents kept having to hunt the binary down, symlink/wrap it, and set EGO_LINUX_CHROME / EGO_LINUX_EXTRA_ARGS by hand.

This plugin does exactly that bridging once, automatically:

  • on load it checks the Playwright cache (idempotent, no-op when the browser is already there),
  • if the browser is missing it runs npx --yes playwright install chromium --with-deps in the background (profile startup is never blocked for minutes), and
  • it then symlinks the installed binary at /usr/local/bin/google-chrome-stable and /usr/local/bin/chromium — the exact names ego-browser's PATH scan and its bundled --no-sandbox wrapper look for. On this root container, the chain then resolves to: root detected → bundled --no-sandbox wrapper → --no-sandbox launch; no DISPLAY → automatic headless. No env vars, no manual wrapper, no agent steps.

Nothing is ever clobbered: a pre-existing system Chrome or a user wrapper keeps winning (the plugin only creates links where nothing of its own/an external binary exists).

Replace <you> with your GitHub account name:

dsh plugin --profile web add github:<you>/dsh-browser-install#main
  • The #main branch ref is what makes updates work: pnpm update re-resolves the branch tip (verified against pnpm 12.8.1), so after you push new commits simply run

    dsh plugin --profile web update                        # all deps
    dsh plugin --profile web update dsh-browser-install    # just this one
    
  • Bundle changes only become active after a profile restart: dsh --profile web.

  • No build step: the bundle ships plain ESM and declares no prepare script, so pnpm's default block on git-dependency build scripts never applies — nothing to allowBuilds in pnpm-workspace.yaml.

  • pnpm installs git deps over HTTPS, so keep the repo public (or set up credentials on the host).

Alternative: install from a local checkout

dsh plugin --profile web add /root/workspaces/Plugin\ Development/dsh-browser-install

A directory install is a symlinked file: dependency — handy while iterating. For persistent installs and updates, prefer the git flow above.

Restart and verify

Restart the profile, then check the plugin loaded and did its job:

dsh --profile web
dsh --profile web --dump-config | grep -A3 browser-install
ls -la /usr/local/bin/google-chrome-stable        # -> .../ms-playwright/chromium-*/chrome-linux64/chrome

Then from any agent session, ego_doctor should report a browser binary, and ego_space_open/ego_navigate work with no environment prep.

Non-root hosts: set symlinkDir to a directory on your PATH (e.g. ~/.local/bin). On root only /usr/local/bin is needed — it also satisfies the bundled --no-sandbox wrapper's absolute-path list.

Config

All fields are optional. Override them in your profile's cordis.patch.yml (or in the bundle row's config):

- id: browser-install
  name: "dsh-browser-install"
  config:
    withDeps: true          # run `--with-deps` (apt system deps); needs root/apt
    target: chromium        # chromium | chromium-headless-shell | chrome
    package: playwright     # npx package spec, e.g. "playwright@1.54.1"
    forceInstall: false     # always re-run the install on load
    installOnStartup: true  # install in background when the browser is missing
    cacheDir: ""            # empty = $PLAYWRIGHT_BROWSERS_PATH | ~/.cache/ms-playwright
    symlinkDir: /usr/local/bin
    symlinkNames: [google-chrome-stable, chromium]
    installTimeoutMs: 600000
Field Default Meaning
installOnStartup true Run the Playwright install when the plugin loads and no matching browser is found. The install runs in the background so profile startup is not blocked.
forceInstall false Always run the install on load, even when a browser is already present (e.g. after a Playwright upgrade).
package playwright Package spec passed to npx --yes <package> install .... Pin with e.g. playwright@1.54.1.
target chromium Which Playwright browser to ensure: chromium (full build), chromium-headless-shell, or chrome (Google Chrome). Default matches what ego-browser expects.
withDeps true Pass --with-deps so Playwright also installs the browser's system dependencies via apt (requires root; the DSH image runs as root).
cacheDir auto Playwright browsers cache root. Empty = $PLAYWRIGHT_BROWSERS_PATH, else ~/.cache/ms-playwright. When set here, the installer child also gets PLAYWRIGHT_BROWSERS_PATH.
symlinkDir /usr/local/bin Directory for the PATH links; must be on the DSH process PATH.
symlinkNames [google-chrome-stable, chromium] Link names to create. google-chrome-stable is the one ego-browser's PATH scan and its --no-sandbox wrapper both check.
installTimeoutMs 600000 Max time for the background install before it is killed.

Behaviour notes

  • No-clobber links: a link whose target lives inside the Playwright cache is treated as ours and refreshed to the newest installed revision; any other existing file/symlink is left untouched (and wins).
  • Lifecycle: the background installer child is killed if the plugin is unloaded mid-install (ctx.effect). The PATH links are intentionally not removed on unload — a running browser may be using them and they are harmless, idempotent shared state.
  • Error visibility: install failures, symlink errors, and a symlinkDir missing from PATH are all logged loudly under the dsh-browser-install logger scope, so an agent can read the reason instead of guessing.
  • ego-browser settings untouched: the plugin does not modify dsh-ego-browser's config or any EGO_LINUX_* env vars — it only fills the gap (install + visible symlink) that makes ego-browser's existing auto-adaptation work (root → --no-sandbox wrapper, no DISPLAY → headless).

Uninstall

dsh plugin --profile web remove dsh-browser-install

The symlinks are left in place (harmless; the browser keeps working for ego-browser). Remove them manually if you truly want them gone:

rm -f /usr/local/bin/google-chrome-stable /usr/local/bin/chromium

Development

Pure helpers (resolveCacheDir, resolveBrowserBinary, planSymlinks, applySymlinkPlan, buildInstallCmd) are exported for testing; the plugin itself needs no build step (plain ESM, no TypeScript compile).

  • Node ≥ 22 (the harness image ships v24).
  • Requires DSH ≥ 0.1.2-rc.1 (same baseline as dsh-ego-browser).
  • Runtime dependency: @deepseek-ai/schemastery (already present in the profile).