MCPcopy Create free account
hub / github.com/garrytan/gstack / BrowserManager

Class BrowserManager

browse/src/browser-manager.ts:149–1804  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

147}
148
149export class BrowserManager {
150 private browser: Browser | null = null;
151 private context: BrowserContext | null = null;
152 // Proxy config applied to chromium.launch() when set (D8). Set by server.ts
153 // at startup based on BROWSE_PROXY_URL. For SOCKS5 with auth, server.ts
154 // points this at the local bridge (socks5://127.0.0.1:<bridgePort>); for
155 // HTTP/HTTPS or unauth SOCKS5, it's the upstream URL directly.
156 private proxyConfig: { server: string; username?: string; password?: string } | null = null;
157 private pages: Map<number, Page> = new Map();
158 private tabSessions: Map<number, TabSession> = new Map();
159 private activeTabId: number = 0;
160 private nextTabId: number = 1;
161 private extraHeaders: Record<string, string> = {};
162 private customUserAgent: string | null = null;
163
164 // ─── Viewport + deviceScaleFactor (context options) ──────────
165 // Tracked at the manager level so recreateContext() preserves them.
166 // deviceScaleFactor is a *context* option, not a page-level setter — changes
167 // require recreateContext(). Viewport width/height can change on-page, but we
168 // track the latest so context recreation restores it instead of hardcoding 1280x720.
169 private deviceScaleFactor: number = 1;
170 private currentViewport: { width: number; height: number } = { width: 1280, height: 720 };
171
172 /** Server port — set after server starts, used by cookie-import-browser command */
173 public serverPort: number = 0;
174
175 // ─── Tab Ownership (multi-agent isolation) ──────────────
176 // Maps tabId → clientId. Unowned tabs (not in this map) are root-only for writes.
177 private tabOwnership: Map<number, string> = new Map();
178
179 // ─── Dialog Handling (global, not per-tab) ──────────────────
180 private dialogAutoAccept: boolean = true;
181 private dialogPromptText: string | null = null;
182
183 // ─── Cookie Origin Tracking ────────────────────────────────
184 private cookieImportedDomains: Set<string> = new Set();
185
186 // ─── Handoff State ─────────────────────────────────────────
187 private isHeaded: boolean = false;
188 private consecutiveFailures: number = 0;
189
190 // ─── Watch Mode ─────────────────────────────────────────
191 private watching = false;
192 public watchInterval: ReturnType<typeof setInterval> | null = null;
193 private watchSnapshots: string[] = [];
194 private watchStartTime: number = 0;
195
196 // ─── Headed State ────────────────────────────────────────
197 private connectionMode: 'launched' | 'headed' = 'launched';
198 private intentionalDisconnect = false;
199
200 // ─── Tab Count Guardrail (D5 + Codex single-tab flag) ───────
201 // Idempotent threshold trackers: each guardrail fires exactly once per
202 // upward crossing of its threshold and re-arms when the tab count drops
203 // back below. Pre-guardrail, nothing tracked tab count growth and a
204 // user could accumulate hundreds of tabs (each holding 50–300 MB of
205 // Chromium-side RSS) without warning until the OS OOM-killer fired.
206 // The toast UX lives in the sidebar (extension/sidepanel.js); the

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected