Add Brave CDP automation, replace Oracle browser mode

Connects to user's running Brave via Chrome DevTools Protocol
to automate ChatGPT interaction. Uses puppeteer-core to open a
tab, send the prompt, wait for response, and extract the result.

No cookies, no separate profiles, no copy/paste. Just connects
to the browser where the user is already logged in.

One-time setup: relaunch Brave with --remote-debugging-port=9222

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Taylor Eernisse
2026-02-07 16:16:41 -05:00
parent d776a266a8
commit e7882b917b
4163 changed files with 782828 additions and 148 deletions

22
node_modules/pac-proxy-agent/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2014 Nathan Rajlich <nathan@tootallnate.net>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

27
node_modules/pac-proxy-agent/README.md generated vendored Normal file
View File

@@ -0,0 +1,27 @@
pac-proxy-agent
===============
### A [PAC file][pac-wikipedia] proxy `http.Agent` implementation for HTTP and HTTPS
This module provides an `http.Agent` implementation that retreives the specified
[PAC proxy file][pac-wikipedia] and uses it to resolve which HTTP, HTTPS, or
SOCKS proxy, or if a direct connection should be used to connect to the
HTTP endpoint.
It is designed to be be used with the built-in `http` and `https` modules.
Example
-------
```ts
import * as http from 'http';
import { PacProxyAgent } from 'pac-proxy-agent';
const agent = new PacProxyAgent('pac+https://cloudup.com/ceGH2yZ0Bjp+');
http.get('http://nodejs.org/api/', { agent }, (res) => {
console.log('"response" event!', res.headers);
res.pipe(process.stdout);
});
```
[pac-wikipedia]: http://wikipedia.org/wiki/Proxy_auto-config

60
node_modules/pac-proxy-agent/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,60 @@
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="node" />
import * as net from 'net';
import * as http from 'http';
import { Readable } from 'stream';
import { URL } from 'url';
import { Agent, AgentConnectOpts } from 'agent-base';
import type { HttpProxyAgentOptions } from 'http-proxy-agent';
import type { HttpsProxyAgentOptions } from 'https-proxy-agent';
import type { SocksProxyAgentOptions } from 'socks-proxy-agent';
import { protocols as gProtocols, ProtocolOpts as GetUriOptions } from 'get-uri';
import { FindProxyForURL, PacResolverOptions } from 'pac-resolver';
type Protocols = keyof typeof gProtocols;
type Protocol<T> = T extends `pac+${infer P}:${infer _}` ? P : T extends `${infer P}:${infer _}` ? P : never;
export type PacProxyAgentOptions<T> = http.AgentOptions & PacResolverOptions & GetUriOptions<`${Protocol<T>}:`> & HttpProxyAgentOptions<''> & HttpsProxyAgentOptions<''> & SocksProxyAgentOptions & {
fallbackToDirect?: boolean;
};
/**
* The `PacProxyAgent` class.
*
* A few different "protocol" modes are supported (supported protocols are
* backed by the `get-uri` module):
*
* - "pac+data", "data" - refers to an embedded "data:" URI
* - "pac+file", "file" - refers to a local file
* - "pac+ftp", "ftp" - refers to a file located on an FTP server
* - "pac+http", "http" - refers to an HTTP endpoint
* - "pac+https", "https" - refers to an HTTPS endpoint
*/
export declare class PacProxyAgent<Uri extends string> extends Agent {
static readonly protocols: `pac+${Protocols}`[];
uri: URL;
opts: PacProxyAgentOptions<Uri>;
cache?: Readable;
resolver?: FindProxyForURL;
resolverHash: string;
resolverPromise?: Promise<FindProxyForURL>;
constructor(uri: Uri | URL, opts?: PacProxyAgentOptions<Uri>);
private clearResolverPromise;
/**
* Loads the PAC proxy file from the source if necessary, and returns
* a generated `FindProxyForURL()` resolver function to use.
*/
getResolver(): Promise<FindProxyForURL>;
private loadResolver;
/**
* Loads the contents of the PAC proxy file.
*
* @api private
*/
private loadPacFile;
/**
* Called when the node-core HTTP client library is creating a new HTTP request.
*/
connect(req: http.ClientRequest, opts: AgentConnectOpts): Promise<http.Agent | net.Socket>;
}
export {};
//# sourceMappingURL=index.d.ts.map

1
node_modules/pac-proxy-agent/dist/index.d.ts.map generated vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;AAAA,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAE3B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAI7B,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAY,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAEN,SAAS,IAAI,UAAU,EACvB,YAAY,IAAI,aAAa,EAC7B,MAAM,SAAS,CAAC;AACjB,OAAO,EAEN,eAAe,EACf,kBAAkB,EAClB,MAAM,cAAc,CAAC;AAuBtB,KAAK,SAAS,GAAG,MAAM,OAAO,UAAU,CAAC;AAGzC,KAAK,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,OAAO,MAAM,CAAC,IAAI,MAAM,CAAC,EAAE,GACrD,CAAC,GAEH,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,MAAM,CAAC,EAAE,GAC/B,CAAC,GACD,KAAK,CAAC;AAET,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI,IAAI,CAAC,YAAY,GACtD,kBAAkB,GAClB,aAAa,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,GAChC,qBAAqB,CAAC,EAAE,CAAC,GACzB,sBAAsB,CAAC,EAAE,CAAC,GAC1B,sBAAsB,GAAG;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,qBAAa,aAAa,CAAC,GAAG,SAAS,MAAM,CAAE,SAAQ,KAAK;IAC3D,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,SAAS,EAAE,EAAE,CAM7C;IAEF,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;gBAE/B,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,oBAAoB,CAAC,GAAG,CAAC;IAsB5D,OAAO,CAAC,oBAAoB,CAE1B;IAEF;;;OAGG;IACH,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;YAWzB,YAAY;IAwC1B;;;;OAIG;YACW,WAAW;IAazB;;OAEG;IACG,OAAO,CACZ,GAAG,EAAE,IAAI,CAAC,aAAa,EACvB,IAAI,EAAE,gBAAgB,GACpB,OAAO,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC;CA2GnC"}

238
node_modules/pac-proxy-agent/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,238 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PacProxyAgent = void 0;
const net = __importStar(require("net"));
const tls = __importStar(require("tls"));
const crypto = __importStar(require("crypto"));
const events_1 = require("events");
const debug_1 = __importDefault(require("debug"));
const url_1 = require("url");
const agent_base_1 = require("agent-base");
const get_uri_1 = require("get-uri");
const pac_resolver_1 = require("pac-resolver");
const quickjs_emscripten_1 = require("@tootallnate/quickjs-emscripten");
const debug = (0, debug_1.default)('pac-proxy-agent');
const setServernameFromNonIpHost = (options) => {
if (options.servername === undefined &&
options.host &&
!net.isIP(options.host)) {
return {
...options,
servername: options.host,
};
}
return options;
};
/**
* The `PacProxyAgent` class.
*
* A few different "protocol" modes are supported (supported protocols are
* backed by the `get-uri` module):
*
* - "pac+data", "data" - refers to an embedded "data:" URI
* - "pac+file", "file" - refers to a local file
* - "pac+ftp", "ftp" - refers to a file located on an FTP server
* - "pac+http", "http" - refers to an HTTP endpoint
* - "pac+https", "https" - refers to an HTTPS endpoint
*/
class PacProxyAgent extends agent_base_1.Agent {
constructor(uri, opts) {
super(opts);
this.clearResolverPromise = () => {
this.resolverPromise = undefined;
};
// Strip the "pac+" prefix
const uriStr = typeof uri === 'string' ? uri : uri.href;
this.uri = new url_1.URL(uriStr.replace(/^pac\+/i, ''));
debug('Creating PacProxyAgent with URI %o', this.uri.href);
// @ts-expect-error Not sure why TS is complaining here…
this.opts = { ...opts };
this.cache = undefined;
this.resolver = undefined;
this.resolverHash = '';
this.resolverPromise = undefined;
// For `PacResolver`
if (!this.opts.filename) {
this.opts.filename = this.uri.href;
}
}
/**
* Loads the PAC proxy file from the source if necessary, and returns
* a generated `FindProxyForURL()` resolver function to use.
*/
getResolver() {
if (!this.resolverPromise) {
this.resolverPromise = this.loadResolver();
this.resolverPromise.then(this.clearResolverPromise, this.clearResolverPromise);
}
return this.resolverPromise;
}
async loadResolver() {
try {
// (Re)load the contents of the PAC file URI
const [qjs, code] = await Promise.all([
(0, quickjs_emscripten_1.getQuickJS)(),
this.loadPacFile(),
]);
// Create a sha1 hash of the JS code
const hash = crypto.createHash('sha1').update(code).digest('hex');
if (this.resolver && this.resolverHash === hash) {
debug('Same sha1 hash for code - contents have not changed, reusing previous proxy resolver');
return this.resolver;
}
// Cache the resolver
debug('Creating new proxy resolver instance');
this.resolver = (0, pac_resolver_1.createPacResolver)(qjs, code, this.opts);
// Store that sha1 hash for future comparison purposes
this.resolverHash = hash;
return this.resolver;
}
catch (err) {
if (this.resolver &&
err.code === 'ENOTMODIFIED') {
debug('Got ENOTMODIFIED response, reusing previous proxy resolver');
return this.resolver;
}
throw err;
}
}
/**
* Loads the contents of the PAC proxy file.
*
* @api private
*/
async loadPacFile() {
debug('Loading PAC file: %o', this.uri);
const rs = await (0, get_uri_1.getUri)(this.uri, { ...this.opts, cache: this.cache });
debug('Got `Readable` instance for URI');
this.cache = rs;
const buf = await (0, agent_base_1.toBuffer)(rs);
debug('Read %o byte PAC file from URI', buf.length);
return buf.toString('utf8');
}
/**
* Called when the node-core HTTP client library is creating a new HTTP request.
*/
async connect(req, opts) {
const { secureEndpoint } = opts;
const isWebSocket = req.getHeader('upgrade') === 'websocket';
// First, get a generated `FindProxyForURL()` function,
// either cached or retrieved from the source
const resolver = await this.getResolver();
// Calculate the `url` parameter
const protocol = secureEndpoint ? 'https:' : 'http:';
const host = opts.host && net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host;
const defaultPort = secureEndpoint ? 443 : 80;
const url = Object.assign(new url_1.URL(req.path, `${protocol}//${host}`), defaultPort ? undefined : { port: opts.port });
debug('url: %s', url);
let result = await resolver(url);
// Default to "DIRECT" if a falsey value was returned (or nothing)
if (!result) {
result = 'DIRECT';
}
const proxies = String(result)
.trim()
.split(/\s*;\s*/g)
.filter(Boolean);
if (this.opts.fallbackToDirect && !proxies.includes('DIRECT')) {
proxies.push('DIRECT');
}
for (const proxy of proxies) {
let agent = null;
let socket = null;
const [type, target] = proxy.split(/\s+/);
debug('Attempting to use proxy: %o', proxy);
if (type === 'DIRECT') {
// Direct connection to the destination endpoint
if (secureEndpoint) {
socket = tls.connect(setServernameFromNonIpHost(opts));
}
else {
socket = net.connect(opts);
}
}
else if (type === 'SOCKS' || type === 'SOCKS5') {
// Use a SOCKSv5h proxy
const { SocksProxyAgent } = await Promise.resolve().then(() => __importStar(require('socks-proxy-agent')));
agent = new SocksProxyAgent(`socks://${target}`, this.opts);
}
else if (type === 'SOCKS4') {
// Use a SOCKSv4a proxy
const { SocksProxyAgent } = await Promise.resolve().then(() => __importStar(require('socks-proxy-agent')));
agent = new SocksProxyAgent(`socks4a://${target}`, this.opts);
}
else if (type === 'PROXY' ||
type === 'HTTP' ||
type === 'HTTPS') {
// Use an HTTP or HTTPS proxy
// http://dev.chromium.org/developers/design-documents/secure-web-proxy
const proxyURL = `${type === 'HTTPS' ? 'https' : 'http'}://${target}`;
if (secureEndpoint || isWebSocket) {
const { HttpsProxyAgent } = await Promise.resolve().then(() => __importStar(require('https-proxy-agent')));
agent = new HttpsProxyAgent(proxyURL, this.opts);
}
else {
const { HttpProxyAgent } = await Promise.resolve().then(() => __importStar(require('http-proxy-agent')));
agent = new HttpProxyAgent(proxyURL, this.opts);
}
}
try {
if (socket) {
// "DIRECT" connection, wait for connection confirmation
await (0, events_1.once)(socket, 'connect');
req.emit('proxy', { proxy, socket });
return socket;
}
if (agent) {
const s = await agent.connect(req, opts);
if (!(s instanceof net.Socket)) {
throw new Error('Expected a `net.Socket` to be returned from agent');
}
req.emit('proxy', { proxy, socket: s });
return s;
}
throw new Error(`Could not determine proxy type for: ${proxy}`);
}
catch (err) {
debug('Got error for proxy %o: %o', proxy, err);
req.emit('proxy', { proxy, error: err });
}
}
throw new Error(`Failed to establish a socket connection to proxies: ${JSON.stringify(proxies)}`);
}
}
PacProxyAgent.protocols = [
'pac+data',
'pac+file',
'pac+ftp',
'pac+http',
'pac+https',
];
exports.PacProxyAgent = PacProxyAgent;
//# sourceMappingURL=index.js.map

1
node_modules/pac-proxy-agent/dist/index.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

58
node_modules/pac-proxy-agent/package.json generated vendored Normal file
View File

@@ -0,0 +1,58 @@
{
"name": "pac-proxy-agent",
"version": "7.2.0",
"description": "A PAC file proxy `http.Agent` implementation for HTTP",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/TooTallNate/proxy-agents.git",
"directory": "packages/pac-proxy-agent"
},
"keywords": [
"pac",
"proxy",
"agent",
"http",
"https",
"socks",
"request",
"access"
],
"author": "Nathan Rajlich <nathan@tootallnate.net> (http://n8.io/)",
"license": "MIT",
"dependencies": {
"@tootallnate/quickjs-emscripten": "^0.23.0",
"agent-base": "^7.1.2",
"debug": "^4.3.4",
"get-uri": "^6.0.1",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.6",
"pac-resolver": "^7.0.1",
"socks-proxy-agent": "^8.0.5"
},
"devDependencies": {
"@types/debug": "^4.1.7",
"@types/jest": "^29.5.1",
"@types/node": "^14.18.45",
"async-listen": "^3.0.0",
"jest": "^29.5.0",
"socksv5": "0.0.6",
"ts-jest": "^29.1.0",
"typescript": "^5.0.4",
"proxy": "2.2.0",
"tsconfig": "0.0.0"
},
"engines": {
"node": ">= 14"
},
"scripts": {
"build": "tsc",
"test": "jest --env node --verbose --bail",
"lint": "eslint --ext .ts",
"pack": "node ../../scripts/pack.mjs"
}
}