diff --git a/BROWSER.md b/BROWSER.md index 4477f178f..63d278a9a 100644 --- a/BROWSER.md +++ b/BROWSER.md @@ -1606,7 +1606,7 @@ the global `~/.gstack/browser-skills/foo/` only inside project-a. | `BROWSE_TUNNEL` | 0 | Activate the dual-listener tunnel architecture (requires `NGROK_AUTHTOKEN`) | | `BROWSE_TUNNEL_LOCAL_ONLY` | 0 | Test-only — bind both listeners locally without ngrok | | `CHROMIUM_PROFILE` | unset | Explicit headed Chromium profile directory (used by gbrowser's gbd per-workspace); honored by headed launch and profile-lock cleanup, not used by headless sessions | -| `GSTACK_DISABLE_GPU` | unset | Set to `off` to skip the macOS headless GPU-taming flag set (applied by default on Darwin to stop runaway GPU-process spin). That flag set also disables WebGL: `getContext('webgl')`/`'webgl2'` return `null`, so WebGL pages (three.js, Mapbox GL, sigma.js) show their fallback. Set `off` to QA WebGL or canvas-heavy apps. | +| `GSTACK_DISABLE_GPU` | unset | On macOS, headless Chromium starts with a GPU-taming flag set (it stops runaway GPU-process spin) that also disables WebGL: `getContext('webgl')`/`'webgl2'` return `null`, so WebGL pages (three.js, Mapbox GL, sigma.js) show their fallback. Set `GSTACK_DISABLE_GPU=off` to enable WebGL (`off` is the only recognized value). | | `GSTACK_BROWSE_MAX_HTML_BYTES` | 52428800 (50MB) | `load-html` size cap | | `GSTACK_SECURITY_OFF` | unset | Emergency kill switch — disable ML classifier | | `GSTACK_STEALTH` | unset | Set to `extended` (also accepts `1`/`true`) to layer six aggressive patches (WebGL spoof, faked plugins, mediaDevices) on top of Layer C. Actively lies; can break sites. | diff --git a/browse/src/cli.ts b/browse/src/cli.ts index d75881b15..8535bce0d 100644 --- a/browse/src/cli.ts +++ b/browse/src/cli.ts @@ -860,6 +860,7 @@ async function ensureServer(flags?: GlobalFlags): Promise { } else { console.error(`[browse] a browse daemon for this project is running with --headed/--proxy (started by another session).`); console.error(`[browse] pass the same flags to use it, or run 'browse disconnect' to start a plain one.`); + console.error(`[browse] why: BROWSER.md, "Daemon discipline": https://github.com/garrytan/gstack/blob/main/BROWSER.md#headed-mode--proxy--browser-native-downloads-v12800`); } process.exit(1); } diff --git a/browse/test/cli-config-mismatch-message.test.ts b/browse/test/cli-config-mismatch-message.test.ts index 3e8dff90c..a31c2fa44 100644 --- a/browse/test/cli-config-mismatch-message.test.ts +++ b/browse/test/cli-config-mismatch-message.test.ts @@ -43,6 +43,7 @@ describe('#3030: config-mismatch refusal names who started the daemon', () => { expect(r.out).toContain('running with --headed/--proxy (started by another session)'); expect(r.out).toContain("pass the same flags to use it, or run 'browse disconnect'"); expect(r.out).not.toContain('to apply --proxy/--headed'); + expect(r.out).toContain('BROWSER.md, "Daemon discipline": https://github.com/garrytan/gstack/blob/main/BROWSER.md#headed-mode--proxy--browser-native-downloads-v12800'); }, 45_000); test('a call that passed a different flag keeps the apply-via-disconnect hint', async () => { @@ -51,3 +52,21 @@ describe('#3030: config-mismatch refusal names who started the daemon', () => { expect(r.out).toContain("run 'browse disconnect' first to apply --proxy/--headed"); }, 45_000); }); + +describe('#3030: BROWSER.md says how to enable WebGL', () => { + const browserMd = fs.readFileSync(path.resolve(import.meta.dir, '../../BROWSER.md'), 'utf8'); + + test('the GSTACK_DISABLE_GPU row states the effect and the only recognized value', () => { + const row = browserMd.split('\n').find(line => line.startsWith('| `GSTACK_DISABLE_GPU`')) ?? ''; + expect(row).toContain('also disables WebGL'); + expect(row).toContain('Set `GSTACK_DISABLE_GPU=off` to enable WebGL (`off` is the only recognized value).'); + }); + + test('the Daemon discipline paragraph sits under the heading the refusal links to', () => { + const heading = browserMd.indexOf('### Headed mode + proxy + browser-native downloads (v1.28.0.0)'); + const discipline = browserMd.indexOf('**Daemon discipline.**'); + expect(heading).toBeGreaterThan(-1); + expect(discipline).toBeGreaterThan(heading); + expect(browserMd.slice(heading + 3, discipline).replace(/```[\s\S]*?```/g, '')).not.toMatch(/\n#{1,3} /); + }); +});