Skip to content

feat: XDG config discovery, extraArgs support, and WSL2/container fixes#45

Open
mjdaly wants to merge 10 commits intoSawyerHood:mainfrom
mjdaly:feat/xdg-config-discovery
Open

feat: XDG config discovery, extraArgs support, and WSL2/container fixes#45
mjdaly wants to merge 10 commits intoSawyerHood:mainfrom
mjdaly:feat/xdg-config-discovery

Conversation

@mjdaly
Copy link

@mjdaly mjdaly commented Jan 16, 2026

⚠️ Dependencies

This PR depends on the following unmerged PRs being merged first:

The files modified in this PR (config.ts, client-lite.test.ts) do not exist in upstream main yet.


Summary

This PR adds several configuration improvements for better cross-platform and container support:

1. XDG and Project-Level Config Discovery

  • XDG Base Directory Specification support for config and state files
  • Project-level config discovery (walks up directory tree looking for .dev-browser/config.json)
  • DEV_BROWSER_CONFIG environment variable for explicit config path override
  • Backward compatible with existing ~/.dev-browser/ setups

2. Browser Extra Arguments (browser.extraArgs)

  • Pass custom command-line flags to Chrome via config
  • Essential for WSL2/container environments where GPU causes crashes

3. Default User Data Directory for CDP

  • Chrome requires a non-default profile for CDP debugging
  • Now defaults to $XDG_STATE_HOME/dev-browser/chrome-profile
  • Configurable via browser.userDataDir for using existing profiles

4. WSL2/Container SIGILL Fix Documentation

  • Documents the GPU-related SIGILL crash in virtualized environments
  • Provides recommended flags: --disable-gpu, --disable-software-rasterizer, --no-sandbox

Config Discovery Priority

Config file:

  1. DEV_BROWSER_CONFIG environment variable
  2. .dev-browser/config.json in cwd or ancestor directories (project config)
  3. $XDG_CONFIG_HOME/dev-browser/config.json (XDG compliant)
  4. ~/.config/dev-browser/config.json (XDG default)
  5. ~/.dev-browser/config.json (legacy fallback)

State files (active-servers.json, chrome-profile):

  1. $XDG_STATE_HOME/dev-browser/
  2. ~/.local/state/dev-browser/
  3. ~/.dev-browser/ (legacy)

New Config Options

{
  "browser": {
    "path": "/opt/google/chrome/google-chrome",
    "userDataDir": "/path/to/profile",
    "extraArgs": ["--disable-gpu", "--no-sandbox"]
  }
}
Option Description
browser.userDataDir Browser profile directory (defaults to XDG state dir)
browser.extraArgs Extra Chrome flags (e.g., ["--disable-gpu"])

WSL2/Container Example

For environments with GPU virtualization issues:

{
  "browser": {
    "path": "/opt/google/chrome/google-chrome",
    "extraArgs": [
      "--disable-gpu",
      "--disable-software-rasterizer",
      "--disable-dev-shm-usage",
      "--no-sandbox"
    ]
  }
}

Test plan

  • All existing tests pass
  • TypeScript compiles without errors
  • 77 tests passing (23 new config discovery tests)
  • Tested extraArgs flow from config → shell → browser launch

Code changes

File Change
src/config.ts XDG paths, project config, extraArgs in interface
src/external-browser.ts Default userDataDir, pass extraArgs to Chrome
scripts/get-browser-config.ts Output BROWSER_EXTRA_ARGS
scripts/start-external-browser.ts Parse and pass extraArgs
server.sh Export EXTRA_ARGS env var
SKILL.md Full documentation of new features

🤖 Generated with Claude Code

mjdaly and others added 3 commits January 16, 2026 23:00
Adds support for Chrome for Testing via CDP and multi-agent concurrency.
- External browser mode keeps browser open after automation
- Auto-detects Chrome for Testing installation
- New config file at ~/.dev-browser/config.json
- Better support for multi-agent workflows

Source: SawyerHood#24
Co-authored-by: Matthew O'Riordan <matthew@ably.com>
- Switch to lightweight HTTP-only client (12MB → 30KB memory per agent)
- Extract shared routes to eliminate code duplication
- Add complete HTTP API for all page operations
- Map-based page registry for O(1) lookup (was O(n) CDP scan)
- Auto-shutdown after 30 minutes of inactivity
- Stale server cleanup on startup

Source: SawyerHood#25
Co-authored-by: Matthew O'Riordan <matthew@ably.com>
Config file discovery now follows this priority:
1. DEV_BROWSER_CONFIG environment variable (explicit override)
2. .dev-browser/config.json in cwd or ancestors (project config)
3. $XDG_CONFIG_HOME/dev-browser/config.json (XDG compliant)
4. ~/.config/dev-browser/config.json (XDG default)
5. ~/.dev-browser/config.json (legacy fallback)

State files (active-servers.json) use XDG_STATE_HOME when set,
falling back to ~/.local/state/dev-browser or ~/.dev-browser.

This enables:
- Per-project browser configurations
- Proper XDG compliance on Linux
- Container-friendly config via env vars
- Backward compatibility with existing ~/.dev-browser setups

Also fixes test that expected old default port (9222 -> 19222)
and adds proper test isolation via config module mocking.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2156e8b48

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +420 to +424
export async function findAvailablePort(config?: DevBrowserConfig): Promise<number> {
const { portRange } = config || loadConfig();
const { start, end, step } = portRange;

for (let port = start; port < end; port += step) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Try the configured end port in allocation loop

The portRange.end value is documented as the “last port to try,” but the loop uses port < end, so the end port is never checked. If the only free port is exactly end (or start === end), findAvailablePort will throw “No available ports” even though a valid port exists. Consider using <= end or clarifying that end is exclusive.

Useful? React with 👍 / 👎.

Comment on lines 189 to 191
if (!entry) {
// Create new page in the persistent context (with timeout to prevent hangs)
const page = await withTimeout(context.newPage(), 30000, "Page creation timed out after 30s");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve viewport on page creation

The server no longer applies GetPageRequest.viewport when creating a new page. Existing callers (e.g., client.page(name, { viewport })) still send a viewport and expect it to be applied, but this handler now ignores it, so viewport sizing silently regresses. This breaks the documented API behavior for viewport on creation unless clients are updated to call the new set-viewport endpoint.

Useful? React with 👍 / 👎.

Comment on lines +103 to +107
if (process.platform === "darwin" && browserPath.endsWith(".app")) {
console.log(`Launching macOS app: ${browserPath}`);
console.log(` (App handles CDP port and user data dir internally)`);

const child = spawn("open", ["-a", browserPath], {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass CDP args when launching .app on macOS

When browserPath ends with .app, launchBrowserDetached calls open -a without --args, so neither --remote-debugging-port nor --user-data-dir is applied. That means cdpPort/userDataDir are ignored and the auto-launched browser won’t open the configured CDP port, causing getCdpEndpoint to time out on macOS when users follow the documented .app path configuration.

Useful? React with 👍 / 👎.

mjdaly and others added 6 commits January 16, 2026 23:41
- Tests for getXdgConfigHome() and getXdgStateHome()
- Tests for findProjectConfig() directory walking
- Tests for getConfigFilePath() priority chain
- Tests for getStateDir() fallback logic
- Export config functions to enable testing

20 new tests, all passing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove confusing "/" fallback in findProjectConfig - the
  parent === dir check handles root detection on all platforms
- Add test for "both XDG and legacy exist" scenario
- Add integration tests for loadConfig() that verify:
  - Project config is loaded when present
  - Browser path validation triggers warning and fallback

The integration test caught that loadConfig() validates browser
paths exist before using them - important behavior to document.

77 tests passing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Document config file discovery priority order
- Add Project-Level Configuration section
- Add XDG Base Directory Support section with table
- Add Environment Variable Override section
- Document state file locations for XDG compliance

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Document config file discovery priority order
- Add example config with common settings
- Explain project-level and container/CI usage

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Chrome refuses to enable CDP debugging when using the default profile,
failing with: "DevTools remote debugging requires a non-default data
directory. Specify this using --user-data-dir."

Now dev-browser always sets --user-data-dir:
- Uses explicit config value if provided
- Otherwise defaults to $XDG_STATE_HOME/dev-browser/chrome-profile

This ensures CDP debugging works out of the box without requiring
manual configuration.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Document why dev-browser uses a dedicated profile by default
- Explain how to configure userDataDir to use existing logged-in sessions
- Add common profile locations for macOS, Linux, Windows
- Note potential conflicts when using default profile with Chrome running

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@mjdaly mjdaly force-pushed the feat/xdg-config-discovery branch from b2561d4 to 7a4e5b5 Compare January 17, 2026 20:43
Adds support for passing extra command-line arguments to Chrome via
the config file. Useful for environment-specific flags like --disable-gpu
in WSL2/container environments where GPU virtualization causes SIGILL
crashes on complex pages.

Example config:
```json
{
  "browser": {
    "extraArgs": ["--disable-gpu", "--no-sandbox"]
  }
}
```

Also documents the WSL2 SIGILL issue and recommended flags:
- --disable-gpu (disable hardware acceleration)
- --disable-software-rasterizer (disable software GPU fallback)
- --disable-dev-shm-usage (use /tmp for shared memory)
- --no-sandbox (required in some containers)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@mjdaly mjdaly changed the title feat: add XDG and project-level config discovery feat: XDG config discovery, extraArgs support, and WSL2/container fixes Jan 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant