Merge remote-tracking branch 'origin/main' into garrytan/learn-from-analysis

This commit is contained in:
Garry Tan
2026-03-17 23:50:09 -07:00
4 changed files with 18 additions and 39 deletions
+10 -10
View File
@@ -15,7 +15,7 @@
* restores state. Falls back to clean slate on any failure.
*/
import { chromium, type Browser, type BrowserContext, type Page, type Locator } from 'playwright';
import { chromium, type Browser, type BrowserContext, type BrowserContextOptions, type Page, type Locator } from 'playwright';
import { addConsoleEntry, addNetworkEntry, addDialogEntry, networkBuffer, type DialogEntry } from './buffers';
export interface RefEntry {
@@ -57,7 +57,7 @@ export class BrowserManager {
process.exit(1);
});
const contextOptions: any = {
const contextOptions: BrowserContextOptions = {
viewport: { width: 1280, height: 720 },
};
if (this.customUserAgent) {
@@ -282,7 +282,7 @@ export class BrowserManager {
try {
// 1. Save state from current context
const savedCookies = await this.context.cookies();
const savedPages: Array<{ url: string; isActive: boolean; storage: any }> = [];
const savedPages: Array<{ url: string; isActive: boolean; storage: { localStorage: Record<string, string>; sessionStorage: Record<string, string> } | null }> = [];
for (const [id, page] of this.pages) {
const url = page.url();
@@ -308,7 +308,7 @@ export class BrowserManager {
await this.context.close().catch(() => {});
// 3. Create new context with updated settings
const contextOptions: any = {
const contextOptions: BrowserContextOptions = {
viewport: { width: 1280, height: 720 },
};
if (this.customUserAgent) {
@@ -340,15 +340,15 @@ export class BrowserManager {
// 6. Restore storage
if (saved.storage) {
try {
await page.evaluate((s: any) => {
await page.evaluate((s: { localStorage: Record<string, string>; sessionStorage: Record<string, string> }) => {
if (s.localStorage) {
for (const [k, v] of Object.entries(s.localStorage)) {
localStorage.setItem(k, v as string);
localStorage.setItem(k, v);
}
}
if (s.sessionStorage) {
for (const [k, v] of Object.entries(s.sessionStorage)) {
sessionStorage.setItem(k, v as string);
sessionStorage.setItem(k, v);
}
}
}, saved.storage);
@@ -369,13 +369,13 @@ export class BrowserManager {
this.clearRefs();
return null; // success
} catch (err: any) {
} catch (err: unknown) {
// Fallback: create a clean context + blank tab
try {
this.pages.clear();
if (this.context) await this.context.close().catch(() => {});
const contextOptions: any = {
const contextOptions: BrowserContextOptions = {
viewport: { width: 1280, height: 720 },
};
if (this.customUserAgent) {
@@ -387,7 +387,7 @@ export class BrowserManager {
} catch {
// If even the fallback fails, we're in trouble — but browser is still alive
}
return `Context recreation failed: ${err.message}. Browser reset to blank tab.`;
return `Context recreation failed: ${err instanceof Error ? err.message : String(err)}. Browser reset to blank tab.`;
}
}
+4 -25
View File
@@ -5,6 +5,7 @@
import type { BrowserManager } from './browser-manager';
import { handleSnapshot } from './snapshot';
import { getCleanText } from './read-commands';
import { READ_COMMANDS, WRITE_COMMANDS, META_COMMANDS } from './commands';
import * as Diff from 'diff';
import * as fs from 'fs';
import * as path from 'path';
@@ -20,28 +21,6 @@ function validateOutputPath(filePath: string): void {
}
}
// Command sets for chain routing (mirrors server.ts — kept local to avoid circular import)
const CHAIN_READ = new Set([
'text', 'html', 'links', 'forms', 'accessibility',
'js', 'eval', 'css', 'attrs',
'console', 'network', 'cookies', 'storage', 'perf',
'dialog', 'is',
]);
const CHAIN_WRITE = new Set([
'goto', 'back', 'forward', 'reload',
'click', 'fill', 'select', 'hover', 'type', 'press', 'scroll', 'wait',
'viewport', 'cookie', 'cookie-import', 'header', 'useragent',
'upload', 'dialog-accept', 'dialog-dismiss',
'cookie-import-browser',
]);
const CHAIN_META = new Set([
'tabs', 'tab', 'newtab', 'closetab',
'status', 'stop', 'restart',
'screenshot', 'pdf', 'responsive',
'chain', 'diff',
'url', 'snapshot',
]);
export async function handleMetaCommand(
command: string,
args: string[],
@@ -223,9 +202,9 @@ export async function handleMetaCommand(
const [name, ...cmdArgs] = cmd;
try {
let result: string;
if (CHAIN_WRITE.has(name)) result = await handleWriteCommand(name, cmdArgs, bm);
else if (CHAIN_READ.has(name)) result = await handleReadCommand(name, cmdArgs, bm);
else if (CHAIN_META.has(name)) result = await handleMetaCommand(name, cmdArgs, bm, shutdown);
if (WRITE_COMMANDS.has(name)) result = await handleWriteCommand(name, cmdArgs, bm);
else if (READ_COMMANDS.has(name)) result = await handleReadCommand(name, cmdArgs, bm);
else if (META_COMMANDS.has(name)) result = await handleMetaCommand(name, cmdArgs, bm, shutdown);
else throw new Error(`Unknown command: ${name}`);
results.push(`[${name}] ${result}`);
} catch (err: any) {
+3 -3
View File
@@ -104,7 +104,7 @@ async function flushBuffers() {
const lines = entries.map(e =>
`[${new Date(e.timestamp).toISOString()}] [${e.level}] ${e.text}`
).join('\n') + '\n';
await Bun.write(CONSOLE_LOG_PATH, (await Bun.file(CONSOLE_LOG_PATH).text().catch(() => '')) + lines);
fs.appendFileSync(CONSOLE_LOG_PATH, lines);
lastConsoleFlushed = consoleBuffer.totalAdded;
}
@@ -115,7 +115,7 @@ async function flushBuffers() {
const lines = entries.map(e =>
`[${new Date(e.timestamp).toISOString()}] ${e.method} ${e.url}${e.status || 'pending'} (${e.duration || '?'}ms, ${e.size || '?'}B)`
).join('\n') + '\n';
await Bun.write(NETWORK_LOG_PATH, (await Bun.file(NETWORK_LOG_PATH).text().catch(() => '')) + lines);
fs.appendFileSync(NETWORK_LOG_PATH, lines);
lastNetworkFlushed = networkBuffer.totalAdded;
}
@@ -126,7 +126,7 @@ async function flushBuffers() {
const lines = entries.map(e =>
`[${new Date(e.timestamp).toISOString()}] [${e.type}] "${e.message}" → ${e.action}${e.response ? ` "${e.response}"` : ''}`
).join('\n') + '\n';
await Bun.write(DIALOG_LOG_PATH, (await Bun.file(DIALOG_LOG_PATH).text().catch(() => '')) + lines);
fs.appendFileSync(DIALOG_LOG_PATH, lines);
lastDialogFlushed = dialogBuffer.totalAdded;
}
} catch {
+1 -1
View File
@@ -8,7 +8,7 @@
"browse": "./browse/dist/browse"
},
"scripts": {
"build": "bun run gen:skill-docs && bun build --compile browse/src/cli.ts --outfile browse/dist/browse && bun build --compile browse/src/find-browse.ts --outfile browse/dist/find-browse && git rev-parse HEAD > browse/dist/.version && rm -f .*.bun-build",
"build": "bun run gen:skill-docs && bun build --compile browse/src/cli.ts --outfile browse/dist/browse && bun build --compile browse/src/find-browse.ts --outfile browse/dist/find-browse && git rev-parse HEAD > browse/dist/.version && rm -f .*.bun-build || true",
"gen:skill-docs": "bun run scripts/gen-skill-docs.ts",
"dev": "bun run browse/src/cli.ts",
"server": "bun run browse/src/server.ts",