mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-03 23:37:53 +01:00
v1.94.31.0 fix(browse): Playwright 1.64.1 (Chromium 156) for the macOS sandbox-escape CVE; handoff honors GSTACK_CHROMIUM_PATH (#3947)
* test(browse): failing tests for #3907 (handoff ignores GSTACK_CHROMIUM_PATH; bundled Chromium predates CVE-2026-106378 fix) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(browse): handoff honors GSTACK_CHROMIUM_PATH (#3907) handoff() always launched Playwright's bundled Chromium, so a GSTACK_CHROMIUM_PATH browser dropped back to the bundle at handoff. It now passes executablePath like launch() and launchHeaded(), skips the poisoned-bundle probe and XProtect heal for a custom path, and never loads the extension into a build that bakes it in. Co-authored-by: merit-blake <265362871+merit-blake@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(deps): Playwright 1.64.1 (Chromium 156) for CVE-2026-106378 (#3907) Playwright 1.62.1 bundled Chromium 151.0.7922.34, which lacks the macOS GPU-process TCC fix (Chromium 0c2f3e54b265, crbug.com/507596239). 1.64.1 bundles 156.0.8078.4, which has it. The playwright-core patch is regenerated from our 1.62.1 patch: the launch windowsHide and TcpPortRandomizationWin hunks carry over unchanged; the taskkill hunk is dropped because 1.64 passes windowsHide there upstream. Co-authored-by: merit-blake <265362871+merit-blake@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore(pregate): map patches/** to the playwright-core patch test Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(browse): name the 1.64.1 patch file in the TcpPortRandomizationWin check Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): fragment for Playwright 1.64.1 and handoff GSTACK_CHROMIUM_PATH (#3907) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(troubleshooting): GSTACK_CHROMIUM_PATH covers headed browse and handoff Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(browse): the retired 1.62.1 patch stays deleted Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> Co-authored-by: merit-blake <265362871+merit-blake@users.noreply.github.com>
This commit is contained in:
17 files changed
+158
-58
No files matched your search
@@ -4,6 +4,7 @@
|
||||
"remote_gate": "bash scripts/ubicloud/pregate-remote.sh",
|
||||
"dependencies": {
|
||||
".github/workflows/**": ["test/pregate-selection.test.ts", "test/free-tests-workflow-wiring.test.ts"],
|
||||
"patches/**": ["browse/test/playwright-core-patch.test.ts"],
|
||||
"scripts/gen-ship-goldens.ts": ["test/host-config.test.ts"],
|
||||
"bun.lock": [],
|
||||
".gitignore": [],
|
||||
|
||||
+1
-1
@@ -1380,7 +1380,7 @@ browser's trust store and updates.
|
||||
eight eMudhra intermediate CAs by CRLSet on 2026-10-17, the day their key
|
||||
compromise was reported, and scheduled the removal of their root, emSign ECC
|
||||
Root CA - G3, from the Chrome Root Store for 2026-10-27. The root store built
|
||||
into gstack's pinned Playwright 1.62.1 (Chromium 151) still trusts that root, so
|
||||
into gstack's pinned Playwright 1.64.1 (Chromium 156) still trusts that root, so
|
||||
the headless browser accepts certificates those intermediates issued. That lasts
|
||||
until gstack moves to the first Playwright release whose Chromium is
|
||||
158.0.8115.0 or later, the first Chrome for Testing build measured to refuse
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# Changelog
|
||||
|
||||
## [1.94.31.0] - 2026-11-03
|
||||
|
||||
**gstack's browser now runs Chromium 156, which fixes CVE-2026-106378, a macOS sandbox escape ([#3907](https://github.com/garrytan/gstack/issues/3907)).** Playwright 1.62.1 bundled Chromium 151, which predates the fix. Your next `./setup` or `/gstack-upgrade` downloads the new browser.
|
||||
|
||||
### Fixed
|
||||
- **Playwright 1.64.1** (Chromium 156.0.8078.4, revision 1248). Chromium's fix is commit `0c2f3e54b265` ("Disclaim TCC responsibility for GPU process", crbug.com/507596239). Its code is in the tagged source for 156.0.8078.4 and missing from 151.0.7922.34. We checked the source; nobody ran the exploit. A new test fails if the bundled Chromium ever drops below 155.0.8059.12, the first tag with the fix.
|
||||
- **`browse handoff` opens the browser `GSTACK_CHROMIUM_PATH` names**, as the headless and headed launches already did. Before, handoff always fell back to Playwright's bundled Chromium. It never probes or self-heals a custom browser, and it doesn't load the sidebar extension into a build that already bakes it in.
|
||||
|
||||
### For contributors
|
||||
- `patches/playwright-core@1.64.1.patch` keeps the launch `windowsHide` hunk and the `TcpPortRandomizationWin` backport. Playwright 1.64 passes `windowsHide` on its `taskkill` call itself, so that hunk is gone; the installed-tree test still checks it.
|
||||
- `.gstack/pregate.json` maps `patches/**` to `browse/test/playwright-core-patch.test.ts`, so the next Playwright bump gets a test lane without renaming anything.
|
||||
- Linux arm64 now downloads the Chrome for Testing build (`chrome-linux-arm64.zip`).
|
||||
|
||||
## [1.94.30.0] - 2026-11-03
|
||||
|
||||
**/ship sends more diffs to full review: spend-binding code, eval scorers, a repo's own risk paths and docs that print secrets now count as risk, and a lane can declare a class the matchers miss (GSTA-258 build C).** Today's reviewer panel stays the default. The one-reader shape for non-risk code ships as an opt-in (`review.panel risk`) because its no-loss replay didn't pass: with only the diff and no repository, the reader found 2 of 68 non-risk blockers, so the replay is being redone with readers working in a checkout. A repo with platform review on can skip /ship's readers on plain docs.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# gstack digest v1.94.30.0 — regenerate/re-copy after upgrading gstack
|
||||
# gstack digest v1.94.31.0 — regenerate/re-copy after upgrading gstack
|
||||
|
||||
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
||||
for agent hosts without a full skill install. The full skills add workflows,
|
||||
|
||||
@@ -2215,7 +2215,11 @@ export class BrowserManager {
|
||||
// STEALTH_LAUNCH_ARGS the handed-off browser kept the AutomationControlled
|
||||
// tell that the other two paths strip.
|
||||
const launchArgs: string[] = ['--hide-crash-restore-bubble', ...STEALTH_LAUNCH_ARGS, ...buildGStackLaunchArgs(), ...this.proxyLaunchArgs()];
|
||||
if (extensionPath) {
|
||||
if (extensionPath && isCustomChromium()) {
|
||||
// Same rule as launchHeaded(): a build that bakes the extension in
|
||||
// crashes when it is loaded twice.
|
||||
console.log('[browse] Handoff: custom Chromium bakes in the extension — skipping --load-extension');
|
||||
} else if (extensionPath) {
|
||||
launchArgs.push(`--disable-extensions-except=${extensionPath}`);
|
||||
launchArgs.push(`--load-extension=${extensionPath}`);
|
||||
// Auth token is served via POST /extension-token (pinned-origin
|
||||
@@ -2234,27 +2238,31 @@ export class BrowserManager {
|
||||
fs.mkdirSync(userDataDir, { recursive: true });
|
||||
cleanSingletonLocks(userDataDir);
|
||||
|
||||
// Self-heal probe (#2242): handoff always launches the Playwright-cache
|
||||
// bundle (this launchPersistentContext call passes no executablePath),
|
||||
// so a bundle poisoned by the old in-place rebrand would GPU-crash here
|
||||
// exactly like launchHeaded(). Same probe, same contract: a
|
||||
// GSTACK_CHROMIUM_PATH bundle is never passed in. The rethrown typed
|
||||
// error surfaces through the outer catch as the actionable
|
||||
// "Cannot open headed browser" message, headless browser untouched.
|
||||
try {
|
||||
probePoisonedChromiumBundle(chromium.executablePath());
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof PoisonedBundleError) throw err;
|
||||
// Probe failures (no bundle yet, EACCES) fall through to launch.
|
||||
// #3907: same custom-binary support as launch()/launchHeaded().
|
||||
const executablePath = process.env.GSTACK_CHROMIUM_PATH || undefined;
|
||||
|
||||
// Self-heal probe (#2242): without a custom path, handoff launches the
|
||||
// Playwright-cache bundle, so a bundle poisoned by the old in-place
|
||||
// rebrand would GPU-crash here exactly like launchHeaded(). Same probe,
|
||||
// same contract: a GSTACK_CHROMIUM_PATH bundle is never probed. The
|
||||
// rethrown typed error surfaces through the outer catch as the
|
||||
// actionable "Cannot open headed browser" message, headless browser
|
||||
// untouched.
|
||||
if (!executablePath) {
|
||||
try {
|
||||
probePoisonedChromiumBundle(chromium.executablePath());
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof PoisonedBundleError) throw err;
|
||||
// Probe failures (no bundle yet, EACCES) fall through to launch.
|
||||
}
|
||||
}
|
||||
|
||||
// T1: same automation-tell-stripping defaults as launchHeaded().
|
||||
// The handoff path (headless → headed re-launch) takes the same
|
||||
// anti-detection posture.
|
||||
const { STEALTH_IGNORE_DEFAULT_ARGS } = await import('./stealth');
|
||||
// XProtect self-heal wrapper (P0 #2554): handoff always launches the
|
||||
// Playwright-cache bundle (no executablePath), so the heal applies
|
||||
// exactly as in launch()/launchHeaded().
|
||||
// XProtect self-heal wrapper (P0 #2554), scoped exactly as in
|
||||
// launch()/launchHeaded(): never heals a GSTACK_CHROMIUM_PATH bundle.
|
||||
newContext = await launchWithXProtectHeal(() => chromium.launchPersistentContext(userDataDir, {
|
||||
headless: false,
|
||||
...(this.xvfb ? { env: { ...process.env, DISPLAY: this.xvfb.display } } : {}),
|
||||
@@ -2268,10 +2276,11 @@ export class BrowserManager {
|
||||
chromiumSandbox: shouldEnableChromiumSandbox(),
|
||||
args: launchArgs,
|
||||
viewport: null,
|
||||
...(executablePath ? { executablePath } : {}),
|
||||
...(this.proxyConfig ? { proxy: this.proxyConfig } : {}),
|
||||
ignoreDefaultArgs: STEALTH_IGNORE_DEFAULT_ARGS,
|
||||
timeout: 15000,
|
||||
}));
|
||||
}), { usesCustomExecutable: Boolean(executablePath) });
|
||||
if (this.closing) {
|
||||
await newContext.close().catch(() => {});
|
||||
throw new Error('Browser is shutting down');
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
|
||||
import { isCustomChromium } from '../src/browser-manager';
|
||||
import { describe, test, expect, beforeEach, afterEach, spyOn } from 'bun:test';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { chromium } from 'playwright';
|
||||
import { BrowserManager, isCustomChromium } from '../src/browser-manager';
|
||||
|
||||
describe('browser-manager: isCustomChromium', () => {
|
||||
let origPath: string | undefined;
|
||||
@@ -65,3 +69,72 @@ describe('browser-manager: isCustomChromium', () => {
|
||||
expect(isCustomChromium()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// #3907: handoff() must launch the browser GSTACK_CHROMIUM_PATH names, like
|
||||
// launch() and launchHeaded(). The Playwright launch is a spy that rejects, so
|
||||
// no window opens and handoff() returns its "Cannot open headed browser" error.
|
||||
describe('handoff: GSTACK_CHROMIUM_PATH', () => {
|
||||
const EXTENSION = '/opt/gstack/extension';
|
||||
const ENV_KEYS = ['GSTACK_CHROMIUM_PATH', 'GSTACK_CHROMIUM_KIND', 'CHROMIUM_PROFILE'] as const;
|
||||
const saved: Record<string, string | undefined> = {};
|
||||
let tmpDir: string;
|
||||
let launch: ReturnType<typeof spyOn>;
|
||||
let bundledPath: ReturnType<typeof spyOn>;
|
||||
|
||||
function promotable(): BrowserManager {
|
||||
const manager = new BrowserManager();
|
||||
Object.assign(manager as unknown as Record<string, unknown>, {
|
||||
browser: {},
|
||||
context: {},
|
||||
saveState: async () => ({ cookies: [], pages: [] }),
|
||||
ensureHeadedDisplay: async () => {},
|
||||
findExtensionPath: () => EXTENSION,
|
||||
});
|
||||
return manager;
|
||||
}
|
||||
|
||||
const launchOptions = () => (launch.mock.calls[0] as [string, { executablePath?: string; args: string[] }])[1];
|
||||
|
||||
beforeEach(() => {
|
||||
for (const key of ENV_KEYS) saved[key] = process.env[key];
|
||||
delete process.env.GSTACK_CHROMIUM_PATH;
|
||||
delete process.env.GSTACK_CHROMIUM_KIND;
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'handoff-custom-chromium-'));
|
||||
process.env.CHROMIUM_PROFILE = path.join(tmpDir, 'profile');
|
||||
launch = spyOn(chromium, 'launchPersistentContext').mockRejectedValue(new Error('launch spy'));
|
||||
bundledPath = spyOn(chromium, 'executablePath').mockReturnValue(path.join(tmpDir, 'no-bundle', 'chrome'));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
launch.mockRestore();
|
||||
bundledPath.mockRestore();
|
||||
for (const key of ENV_KEYS) {
|
||||
if (saved[key] === undefined) delete process.env[key];
|
||||
else process.env[key] = saved[key];
|
||||
}
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
test('launches the GSTACK_CHROMIUM_PATH binary and never probes the Playwright bundle', async () => {
|
||||
process.env.GSTACK_CHROMIUM_PATH = '/opt/chrome/chrome';
|
||||
expect(await promotable().handoff('custom')).toContain('Cannot open headed browser');
|
||||
expect(launch).toHaveBeenCalledTimes(1);
|
||||
expect(launchOptions().executablePath).toBe('/opt/chrome/chrome');
|
||||
expect(bundledPath).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('without GSTACK_CHROMIUM_PATH it launches the probed Playwright bundle', async () => {
|
||||
expect(await promotable().handoff('bundled')).toContain('Cannot open headed browser');
|
||||
expect('executablePath' in launchOptions()).toBe(false);
|
||||
expect(bundledPath).toHaveBeenCalled();
|
||||
expect(launchOptions().args).toContain(`--load-extension=${EXTENSION}`);
|
||||
});
|
||||
|
||||
test('a build that bakes the extension in never gets it loaded twice', async () => {
|
||||
process.env.GSTACK_CHROMIUM_PATH = '/Applications/GBrowser.app/Contents/MacOS/GBrowser';
|
||||
await promotable().handoff('baked');
|
||||
expect(launchOptions().executablePath).toBe('/Applications/GBrowser.app/Contents/MacOS/GBrowser');
|
||||
expect(launchOptions().args.filter((arg) => arg.includes('extension'))).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -66,7 +66,7 @@ const nodeInfo = JSON.parse(nodeProbe.stdout);
|
||||
if (nodeInfo.architecture !== process.arch || !['x64', 'arm64'].includes(process.arch)) incomplete('runtime_architecture_mismatch');
|
||||
const require = createRequire(import.meta.url);
|
||||
const playwrightVersion = require('playwright/package.json').version;
|
||||
if (playwrightVersion !== '1.62.1') incomplete('playwright_1_62_1_required');
|
||||
if (playwrightVersion !== '1.64.1') incomplete('playwright_1_64_1_required');
|
||||
const versionProbe = spawnSync(path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe'), [
|
||||
'-NoProfile', '-NonInteractive', '-Command', NATIVE_BROWSER_VERSION_COMMAND,
|
||||
], { env: { ...environment, GSTACK_QUALIFY_BROWSER_EXE: executable }, encoding: 'utf8', timeout: 10_000, windowsHide: true });
|
||||
|
||||
@@ -213,7 +213,7 @@ async function qualifiedAdapterFixture() {
|
||||
const receipt = {
|
||||
browserName: 'Edge', architecture: runtime.architecture, windowsRelease: release(),
|
||||
executableSha256: '0'.repeat(64), nodeVersion: runtime.version, bunVersion: Bun.version,
|
||||
playwrightVersion: '1.62.1', sourceHashes,
|
||||
playwrightVersion: '1.64.1', sourceHashes,
|
||||
};
|
||||
const virtualRoot = `C:\\gstack-${path.basename(fixture)}`;
|
||||
const windowsEnv = { LOCALAPPDATA: `${virtualRoot}\\Local`, PROGRAMFILES: `${virtualRoot}\\Apps`, 'PROGRAMFILES(X86)': `${virtualRoot}\\Apps32` };
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/**
|
||||
* #2160/#1989: playwright-core is bun-patched to pass windowsHide at its two
|
||||
* #2160/#1989: playwright-core must pass windowsHide at its two
|
||||
* Windows-visible child_process sites — the browser launch spawn (Node
|
||||
* defaults windowsHide to FALSE for spawn, so browser children could flash a
|
||||
* console window) and the force-kill taskkill spawnSync. This is the repo's
|
||||
* console window) and the force-kill taskkill spawnSync. The launch site is
|
||||
* bun-patched; the taskkill site is upstream since Playwright 1.64 (#3907),
|
||||
* so only the installed-tree check below covers it. This is the repo's
|
||||
* first patchedDependencies entry; these static checks pin the three-legged
|
||||
* coherence (patch file ↔ package.json ↔ installed tree) so a playwright
|
||||
* bump that forgets to re-target the patch fails CI instead of silently
|
||||
@@ -35,15 +37,14 @@ describe('playwright-core windowsHide patch (#2160, #1989)', () => {
|
||||
expect(pkg.patchedDependencies[key]).toBe(`patches/playwright-core@${version}.patch`);
|
||||
});
|
||||
|
||||
test('the patch file exists and carries both windowsHide sites', () => {
|
||||
test('the patch file exists and carries the launch windowsHide site', () => {
|
||||
const version = installedPlaywrightCoreVersion();
|
||||
const patchPath = path.join(ROOT, 'patches', `playwright-core@${version}.patch`);
|
||||
expect(fs.existsSync(patchPath)).toBe(true);
|
||||
expect(fs.existsSync(path.join(ROOT, 'patches', 'playwright-core@1.62.1.patch'))).toBe(false);
|
||||
const patch = fs.readFileSync(patchPath, 'utf-8');
|
||||
// Launch spawnOptions site.
|
||||
expect(patch).toContain('+ windowsHide: true,');
|
||||
// taskkill force-kill site.
|
||||
expect(patch).toContain('shell: true, windowsHide: true');
|
||||
});
|
||||
|
||||
test('bun.lock is coherent: the lockfile records the patched dependency', () => {
|
||||
@@ -63,13 +64,28 @@ describe('playwright-core windowsHide patch (#2160, #1989)', () => {
|
||||
|
||||
// Windows Free Tests 40656299153 and 41056150287 (cookie-picker-ui, serial retry passed): goto failed with
|
||||
// net::ERR_NO_BUFFER_SPACE on 127.0.0.1. Chromium's TcpPortRandomizationWin raises WSAENOBUFS with ports free;
|
||||
// microsoft/playwright#43605 disables it in 1.65, and the 1.62.1 patch backports it; drop this block at 1.65.
|
||||
// microsoft/playwright#43605 disables it in 1.65, and the 1.64.1 patch backports it; drop this block at 1.65.
|
||||
describe('playwright-core TcpPortRandomizationWin backport (microsoft/playwright#43605)', () => {
|
||||
test('the patch and the installed tree disable the feature in the --disable-features list', () => {
|
||||
expect(fs.readFileSync(path.join(ROOT, 'patches', 'playwright-core@1.62.1.patch'), 'utf-8')).toContain('+ "TcpPortRandomizationWin"');
|
||||
expect(fs.readFileSync(path.join(ROOT, 'patches', 'playwright-core@1.64.1.patch'), 'utf-8')).toContain('+ "TcpPortRandomizationWin"');
|
||||
const bundle = fs.readFileSync(path.join(ROOT, 'node_modules', 'playwright-core', 'lib', 'coreBundle.js'), 'utf-8');
|
||||
const list = bundle.slice(bundle.indexOf('disabledFeatures = ['), bundle.indexOf('].filter(Boolean);', bundle.indexOf('disabledFeatures = [')));
|
||||
expect(list).toContain('"TcpPortRandomizationWin"');
|
||||
expect(bundle).toContain('"--disable-features=" + disabledFeatures.join(",")');
|
||||
});
|
||||
});
|
||||
|
||||
// #3907: CVE-2026-106378 (macOS GPU-process sandbox escape, crbug.com/507596239) is fixed by Chromium
|
||||
// commit 0c2f3e54b265 (main@{#1695786}), first tagged in 155.0.8059.12. Playwright 1.62.1 shipped
|
||||
// 151.0.7922.34, which lacks it. A Playwright pin whose bundled Chromium is older reopens the escape.
|
||||
describe('bundled Chromium carries the CVE-2026-106378 fix (#3907)', () => {
|
||||
test('playwright-core pins Chromium 155.0.8059.12 or later', () => {
|
||||
const browsers = JSON.parse(fs.readFileSync(
|
||||
path.join(ROOT, 'node_modules', 'playwright-core', 'browsers.json'), 'utf-8',
|
||||
)) as { browsers: Array<{ name: string; browserVersion: string }> };
|
||||
const version = browsers.browsers.find((b) => b.name === 'chromium')!.browserVersion.split('.').map(Number);
|
||||
const floor = [155, 0, 8059, 12];
|
||||
const cmp = version.map((part, i) => part - floor[i]).find((d) => d !== 0) ?? 0;
|
||||
expect(cmp).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
@@ -251,7 +251,7 @@ describe('cookie-import domain validation', () => {
|
||||
reset: 'not_requested', verification: { verified: false, reason: 'not_requested' }, message: 'Cookie copy complete.',
|
||||
});
|
||||
let currentUrl = 'https://example.test';
|
||||
const page = { url: () => currentUrl, isClosed: () => false };
|
||||
const page = { url: () => currentUrl, isClosed: () => false } as any;
|
||||
const session = { getPage: () => page, getActiveFrameOrPage: () => page, getFrame: () => null } as any;
|
||||
const manager = { trackCookieImportDomains() {} } as any;
|
||||
try {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"html-to-docx": "1.8.0",
|
||||
"html-to-vdom": "0.7.0",
|
||||
"marked": "^18.1.1",
|
||||
"playwright": "^1.62.1",
|
||||
"playwright": "^1.64.1",
|
||||
"sharp": "^0.35.4",
|
||||
"socks": "^2.8.10",
|
||||
"xterm": "^5.3.0",
|
||||
@@ -28,7 +28,7 @@
|
||||
},
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"playwright-core@1.62.1": "patches/playwright-core@1.62.1.patch",
|
||||
"playwright-core@1.64.1": "patches/playwright-core@1.64.1.patch",
|
||||
},
|
||||
"overrides": {
|
||||
"@hono/node-server": "2.1.6",
|
||||
@@ -335,8 +335,6 @@
|
||||
|
||||
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
@@ -469,9 +467,9 @@
|
||||
|
||||
"platform": ["platform@1.3.6", "", {}, "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="],
|
||||
|
||||
"playwright": ["playwright@1.62.1", "", { "dependencies": { "playwright-core": "1.62.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg=="],
|
||||
"playwright": ["playwright@1.64.1", "", { "dependencies": { "playwright-core": "1.64.1" }, "bin": { "playwright": "cli.js" } }, "sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA=="],
|
||||
|
||||
"playwright-core": ["playwright-core@1.62.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw=="],
|
||||
"playwright-core": ["playwright-core@1.64.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ=="],
|
||||
|
||||
"prettier": ["prettier@3.9.10", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA=="],
|
||||
|
||||
|
||||
@@ -2091,8 +2091,8 @@ browse chain 'goto https://example.com | text'
|
||||
<a id="browse-chromium-path-failed"></a>
|
||||
### `Chromium at GSTACK_CHROMIUM_PATH=<path> failed to launch: ...`
|
||||
|
||||
**Meaning.** Headless browse and make-pdf now use `GSTACK_CHROMIUM_PATH`
|
||||
(NixOS, macOS 13). That binary could not start.
|
||||
**Meaning.** Headless browse, headed browse, `browse handoff` and make-pdf use
|
||||
`GSTACK_CHROMIUM_PATH` (NixOS, macOS 13). That binary could not start.
|
||||
|
||||
**Fix.** Point `GSTACK_CHROMIUM_PATH` at a working Chromium, or
|
||||
`unset GSTACK_CHROMIUM_PATH` to use gstack's bundled one.
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gstack",
|
||||
"version": "1.94.30",
|
||||
"version": "1.94.31",
|
||||
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -77,7 +77,7 @@
|
||||
"html-to-docx": "1.8.0",
|
||||
"html-to-vdom": "0.7.0",
|
||||
"marked": "^18.1.1",
|
||||
"playwright": "^1.62.1",
|
||||
"playwright": "^1.64.1",
|
||||
"sharp": "^0.35.4",
|
||||
"socks": "^2.8.10",
|
||||
"xterm": "^5.3.0",
|
||||
@@ -115,6 +115,6 @@
|
||||
"sharp": "0.35.5"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"playwright-core@1.62.1": "patches/playwright-core@1.62.1.patch"
|
||||
"playwright-core@1.64.1": "patches/playwright-core@1.64.1.patch"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/lib/coreBundle.js b/lib/coreBundle.js
|
||||
index d9041dd0f009072511a7d808dec83121ff29e127..542acd31dd7e8ae084de952dc42ec63d0caba849 100644
|
||||
index 6893f1cf133efec2c5dccc35c77cc7a53870ccbc..7990e202c5b574ca5ea4c3e73c15c2da43a5b980 100644
|
||||
--- a/lib/coreBundle.js
|
||||
+++ b/lib/coreBundle.js
|
||||
@@ -8906,6 +8906,10 @@ async function launchProcess(options) {
|
||||
@@ -10031,6 +10031,10 @@ async function launchProcess(options) {
|
||||
env: options.env,
|
||||
cwd: options.cwd,
|
||||
shell: options.shell,
|
||||
@@ -13,16 +13,7 @@ index d9041dd0f009072511a7d808dec83121ff29e127..542acd31dd7e8ae084de952dc42ec63d
|
||||
stdio
|
||||
};
|
||||
const spawnedProcess = childProcess.spawn(options.command, options.args || [], spawnOptions);
|
||||
@@ -8985,7 +8989,7 @@ async function launchProcess(options) {
|
||||
options.log(`[pid=${spawnedProcess.pid}] <will force kill>`);
|
||||
try {
|
||||
if (process.platform === "win32") {
|
||||
- const taskkillProcess = childProcess.spawnSync(`taskkill /pid ${spawnedProcess.pid} /T /F`, { shell: true });
|
||||
+ const taskkillProcess = childProcess.spawnSync(`taskkill /pid ${spawnedProcess.pid} /T /F`, { shell: true, windowsHide: true });
|
||||
const [stdout2, stderr2] = [taskkillProcess.stdout.toString(), taskkillProcess.stderr.toString()];
|
||||
if (stdout2)
|
||||
options.log(`[pid=${spawnedProcess.pid}] taskkill stdout: ${stdout2}`);
|
||||
@@ -34634,7 +34638,10 @@ var init_chromiumSwitches = __esm({
|
||||
@@ -36204,7 +36208,10 @@ var init_chromiumSwitches = __esm({
|
||||
// Disables forced sign-in in Edge.
|
||||
"msForceBrowserSignIn",
|
||||
// Disables updating the preferred version in LaunchServices preferences on mac.
|
||||
@@ -41,7 +41,6 @@
|
||||
"browse/test/handoff.test.ts\tTS2554\tExpected 4 arguments, but got 3.": 2,
|
||||
"browse/test/pair-agent-e2e.test.ts\tTS2532\tObject is possibly 'undefined'.": 1,
|
||||
"browse/test/pty-inject-scan.test.ts\tTS2345\tArgument of type '{ Authorization?: undefined; } | { Authorization: string; }' is not assignable to parameter of type 'Record<string, string>'. Type '{ Authorization?: undefined; }' is not assignable to type 'Record<string, string>'. Property 'Authorization' is incompatible with index signature. Type 'undefined' is not assignable to type 'string'.": 1,
|
||||
"browse/test/security-audit-r2.test.ts\tTS2769\tNo overload matches this call. The last overload gave the following error. Type '{ isClosed: () => boolean; url: () => string; }' is missing the following properties from type 'Page': evaluate, evaluateHandle, addInitScript, $, and 110 more.": 1,
|
||||
"browse/test/server-auth.test.ts\tTS2345\tArgument of type '{ Host: string; Origin: string; } | { Host?: undefined; Origin: string; } | { Authorization: string; Host: string; Origin?: undefined; }' is not assignable to parameter of type 'Record<string, string>'. Type '{ Host?: undefined; Origin: string; }' is not assignable to type 'Record<string, string>'. Property 'Host' is incompatible with index signature. Type 'undefined' is not assignable to type 'string'.": 1,
|
||||
"browse/test/server-factory.test.ts\tTS2769\tNo overload matches this call. The last overload gave the following error. Argument of type '\"local\"' is not assignable to parameter of type 'undefined'.": 1,
|
||||
"browse/test/server-proxy-fail-fast.test.ts\tTS2339\tProperty 'subarray' does not exist on type 'string | NonSharedBuffer'. Property 'subarray' does not exist on type 'string'.": 1,
|
||||
|
||||
@@ -48,8 +48,8 @@ describe('Ubuntu fallback retains CPU architecture', () => {
|
||||
cwd: ROOT, env: { PATH: process.env.PATH, HOME: temp(), PLAYWRIGHT_HOST_PLATFORM_OVERRIDE: override }, encoding: 'utf8', timeout: 20_000,
|
||||
});
|
||||
expect(dryRun.status, dryRun.stderr).toBe(0);
|
||||
expect(dryRun.stdout).toContain(expected === 'arm64' ? 'chromium-linux-arm64.zip' : 'chrome-linux64.zip');
|
||||
expect(dryRun.stdout).not.toContain(expected === 'arm64' ? 'chrome-linux64.zip' : 'chromium-linux-arm64.zip');
|
||||
expect(dryRun.stdout).toContain(expected === 'arm64' ? 'chrome-linux-arm64.zip' : 'chrome-linux64.zip');
|
||||
expect(dryRun.stdout).not.toContain(expected === 'arm64' ? 'chrome-linux64.zip' : 'chrome-linux-arm64.zip');
|
||||
}, 30_000);
|
||||
}
|
||||
for (const cpu of ['riscv64', 'i686', 'unknown']) test(`${cpu} reports unsupported instead of installing x64`, () => {
|
||||
|
||||
Reference in new issue
Block a user