Release / release (push) Successful in 6s
Replicate the dsh-facet release stack: a Gitea Actions workflow (.gitea/workflows/release.yml) + .gitea/scripts/release.sh read the commit subjects since the last tag, bump package.json, prepend CHANGELOG.md, tag vX.Y.Z and publish the release via API. Agreed install ref is #semver:^0.1.0, and tags are what 'dsh plugin --profile web update' moves to, so the workflow is what makes updates work. Add the release.test.sh suite (43 checks), npm scripts (test:release / release:plan / release:notes), AGENTS.md with the commit conventions, CHANGELOG.md seed, and the README *Releases* rules.
166 lines
9.9 KiB
Markdown
166 lines
9.9 KiB
Markdown
# 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).
|
|
|
|
## Install (from git — recommended)
|
|
|
|
This repo lives on `git.notexpectedyet.com`, so the install uses the same `git+https://…#semver:` convention as the other plugins in the profile (`dsh-facet`, `dsh-recall`, `dsh-slopo`). On GitHub the equivalent shorthand would be `github:<you>/dsh-browser-install#semver:^0.1.0`:
|
|
|
|
```sh
|
|
dsh plugin --profile web add "git+https://git.notexpectedyet.com/Not-Expected-Yet/dsh-browser-install.git#semver:^0.1.0"
|
|
```
|
|
|
|
- The **`#semver:^0.1.0` ref is what makes updates work**: it resolves against the repo's version tags (each release is a `v<version>` tag), and `pnpm update` moves the dependency within the range. This mirrors exactly how `dsh-facet`/`dsh-recall`/`dsh-slopo` are kept current in this profile. To publish a new version: bump `version` in `package.json`, commit, tag it, push — then run
|
|
|
|
```sh
|
|
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 resolves git deps over HTTPS, so **the repo must be public** on the forge (or the DSH host needs git credentials). A private/new repo makes `git ls-remote` fail with "could not read Username ... terminal prompts disabled", which is what a bare `#master` install hit here.
|
|
|
|
## Alternative: install from a local checkout
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
dsh --profile web
|
|
```
|
|
|
|
```sh
|
|
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`):
|
|
|
|
```yaml
|
|
- 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. |
|
|
|
|
## Releases
|
|
|
|
`master` releases itself. Every push runs `.gitea/workflows/release.yml`
|
|
(Gitea Actions), which reads the commit subjects since the last tag, bumps
|
|
`package.json`, prepends `CHANGELOG.md`, tags `vX.Y.Z`, and publishes the Gitea
|
|
release with those subjects as the body. The tag is what `dsh plugin --profile
|
|
web update` moves to via the `#semver:^0.1.0` install ref — no tags, no update,
|
|
which is why this workflow exists.
|
|
|
|
### Commit subjects are release input
|
|
|
|
| Subject | Bump | Notes section |
|
|
|---|---|---|
|
|
| `feat: …` | minor | **Added** |
|
|
| `feat!: …`, `fix(x)!: …`, `BREAKING`, or `[major]` | major | **Breaking** |
|
|
| `fix: …`, `perf: …`, `revert: …` | patch | **Fixed** |
|
|
| `docs: …` | patch | **Documented** |
|
|
| plain sentence / anything else | patch | **Other** (or flat list) |
|
|
| `[skip release]` | — | nothing published |
|
|
|
|
`feat` counts only as its own word, so `Fix the feature flag leak` is a patch.
|
|
`[minor]` raises a patch subject to a minor without a `feat`. Only the subject
|
|
line is read.
|
|
|
|
### First release
|
|
|
|
With no tags yet, the released version is the one declared in `package.json`
|
|
(currently `v0.1.0`) — the first tag matches what the code says, and every later
|
|
release moves forward from it.
|
|
|
|
### Manual run
|
|
|
|
`workflow_dispatch` is enabled for the first release (no tags yet) and for a
|
|
release whose push failed after the tag was created; trigger it from the
|
|
Actions tab.
|
|
|
|
### Checking before pushing
|
|
|
|
```sh
|
|
npm run release:plan # would this HEAD release? (yes/no)
|
|
npm run release:notes # the exact notes it would publish
|
|
npm run test:release # the checks behind the bump and the notes
|
|
```
|
|
|
|
## 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
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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). |