agentic-browser 1.0.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -59,8 +59,10 @@ AgenticBrowserCore → ControlApi → SessionManager → BrowserController (CDP)
59
59
  ```
60
60
 
61
61
  1. `createAgenticBrowserCore()` builds AppContext + ChromeCdpBrowserController
62
- 2. Commands execute via CDP `Runtime.evaluate` on the browser page
63
- 3. Results are recorded as evidence, indexed per-domain for memory search
62
+ 2. `createSession` either launches a new Chrome (`browser.launch()`) or connects to existing one (`browser.connect(cdpUrl)`) based on config
63
+ 3. Commands execute via CDP `Runtime.evaluate` on the browser page
64
+ 4. Results are recorded as evidence, indexed per-domain for memory search
65
+ 5. Connected sessions (pid=0) skip `process.kill` on terminate — the user's browser stays open
64
66
 
65
67
  ## Code Conventions
66
68
 
@@ -108,6 +110,8 @@ AgenticBrowserCore → ControlApi → SessionManager → BrowserController (CDP)
108
110
 
109
111
  - `AGENTIC_BROWSER_LOG_DIR` — base dir for sessions/memory/events (default: `.agentic-browser`)
110
112
  - `AGENTIC_BROWSER_CHROME_EXECUTABLE_PATH` — explicit Chrome path (auto-discovered if not set)
113
+ - `AGENTIC_BROWSER_CDP_URL` — connect to an already-running Chrome via CDP (e.g. `http://127.0.0.1:9222`)
114
+ - `AGENTIC_BROWSER_USER_PROFILE` — use real Chrome profile (`default`, `true`, or absolute path)
111
115
 
112
116
  ## MCP Server
113
117
 
package/README.md CHANGED
@@ -37,12 +37,74 @@ npm run lint
37
37
  npm test
38
38
  ```
39
39
 
40
+ ## Use Your Existing Chrome
41
+
42
+ By default, agentic-browser launches a fresh Chrome with a throwaway profile. If you want to use your logged-in sessions, cookies, or extensions, there are two ways:
43
+
44
+ ### Option 1: Control your running Chrome (recommended)
45
+
46
+ This lets agentic-browser take over your already-open Chrome — no need to quit it, no need to log in again.
47
+
48
+ **Step 1.** Quit Chrome, then relaunch it with remote debugging enabled:
49
+
50
+ ```bash
51
+ # macOS
52
+ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
53
+
54
+ # Linux
55
+ google-chrome --remote-debugging-port=9222
56
+
57
+ # Windows
58
+ chrome.exe --remote-debugging-port=9222
59
+ ```
60
+
61
+ Chrome opens normally with all your tabs, extensions, and sessions intact.
62
+
63
+ **Step 2.** Connect agentic-browser:
64
+
65
+ ```bash
66
+ agentic-browser agent start --cdp-url http://127.0.0.1:9222
67
+ ```
68
+
69
+ Stopping the session will **not** close your Chrome.
70
+
71
+ ### Option 2: Launch a new Chrome with your profile
72
+
73
+ **Important:** You must quit Chrome first. Chrome locks its profile directory — if Chrome is already running, this command will fail.
74
+
75
+ ```bash
76
+ # Quit Chrome, then:
77
+ agentic-browser agent start --user-profile default
78
+ ```
79
+
80
+ This launches a new Chrome window using your default profile. You can also pass a custom profile path:
81
+
82
+ ```bash
83
+ agentic-browser agent start --user-profile /path/to/chrome/profile
84
+ ```
85
+
86
+ Default profile locations per platform:
87
+ - **macOS:** `~/Library/Application Support/Google/Chrome`
88
+ - **Linux:** `~/.config/google-chrome`
89
+ - **Windows:** `%LOCALAPPDATA%\Google\Chrome\User Data`
90
+
91
+ ### Environment variables
92
+
93
+ These options can also be set via environment variables (CLI flags take precedence):
94
+
95
+ | Variable | Example | Description |
96
+ | ------------------------------ | ------------------------------ | ------------------------------- |
97
+ | `AGENTIC_BROWSER_CDP_URL` | `http://127.0.0.1:9222` | Connect to a running Chrome |
98
+ | `AGENTIC_BROWSER_USER_PROFILE` | `default` or an absolute path | Launch with a real profile |
99
+
40
100
  ## Agent Commands (Recommended for LLMs)
41
101
 
42
102
  The `agent` subcommand manages session state, auto-restarts on disconnect, generates command IDs, and retries failed commands automatically:
43
103
 
44
104
  ```bash
45
105
  agentic-browser agent start
106
+ agentic-browser agent start --cdp-url http://127.0.0.1:9222
107
+ agentic-browser agent start --user-profile default
46
108
  agentic-browser agent status
47
109
  agentic-browser agent run navigate '{"url":"https://example.com"}'
48
110
  agentic-browser agent run interact '{"action":"click","selector":"#login"}'
@@ -120,6 +182,8 @@ For direct control without session state management:
120
182
 
121
183
  ```bash
122
184
  agentic-browser session:start
185
+ agentic-browser session:start --cdp-url http://127.0.0.1:9222
186
+ agentic-browser session:start --user-profile default
123
187
  ```
124
188
 
125
189
  ### 2. Read Session Status
@@ -220,6 +284,27 @@ const memory = core.searchMemory({
220
284
  await core.stopSession(session.sessionId);
221
285
  ```
222
286
 
287
+ ### Connect to your running Chrome
288
+
289
+ ```ts
290
+ // Chrome must be running with --remote-debugging-port=9222
291
+ const core = createAgenticBrowserCore({
292
+ env: { ...process.env, AGENTIC_BROWSER_CDP_URL: "http://127.0.0.1:9222" },
293
+ });
294
+ const session = await core.startSession();
295
+ // Stopping the session will NOT close your Chrome
296
+ ```
297
+
298
+ ### Launch Chrome with your real profile
299
+
300
+ ```ts
301
+ // Chrome must be closed first
302
+ const core = createAgenticBrowserCore({
303
+ env: { ...process.env, AGENTIC_BROWSER_USER_PROFILE: "default" },
304
+ });
305
+ const session = await core.startSession();
306
+ ```
307
+
223
308
  ## Documentation
224
309
 
225
310
  ```bash
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { r as createCliRuntime } from "../runtime-4pX1c4Cs.mjs";
2
+ import { r as createCliRuntime } from "../runtime-D6awVhGy.mjs";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import crypto from "node:crypto";
@@ -243,7 +243,9 @@ async function main() {
243
243
  const runtime = createCliRuntime();
244
244
  const program = new Command();
245
245
  program.name("agentic-browser").description("Agentic browser CLI");
246
- program.command("session:start").action(async () => {
246
+ program.command("session:start").option("--cdp-url <url>", "connect to existing Chrome via CDP endpoint URL").option("--user-profile <path>", "use 'default' for system Chrome profile or an absolute path").action(async (options) => {
247
+ if (options.cdpUrl) runtime.context.config.cdpUrl = options.cdpUrl;
248
+ if (options.userProfile) runtime.context.config.userProfileDir = options.userProfile === "true" || options.userProfile === "default" ? "default" : options.userProfile;
247
249
  const result = await runSessionStart(runtime, { browser: "chrome" });
248
250
  console.log(JSON.stringify(result));
249
251
  });
@@ -308,7 +310,9 @@ async function main() {
308
310
  console.log(JSON.stringify(result));
309
311
  });
310
312
  const agent = program.command("agent").description("Stateful agent wrapper with session persistence and auto-retry");
311
- agent.command("start").action(async () => {
313
+ agent.command("start").option("--cdp-url <url>", "connect to existing Chrome via CDP endpoint URL").option("--user-profile <path>", "use 'default' for system Chrome profile or an absolute path").action(async (options) => {
314
+ if (options.cdpUrl) runtime.context.config.cdpUrl = options.cdpUrl;
315
+ if (options.userProfile) runtime.context.config.userProfileDir = options.userProfile === "true" || options.userProfile === "default" ? "default" : options.userProfile;
312
316
  const result = await agentStart(runtime);
313
317
  console.log(JSON.stringify(result));
314
318
  });
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-4pX1c4Cs.mjs";
2
+ import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-D6awVhGy.mjs";
3
3
 
4
4
  export { AgenticBrowserCore, createAgenticBrowserCore, createMockAgenticBrowserCore };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as createAgenticBrowserCore } from "../runtime-4pX1c4Cs.mjs";
2
+ import { n as createAgenticBrowserCore } from "../runtime-D6awVhGy.mjs";
3
3
  import { z } from "zod";
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -2,6 +2,7 @@
2
2
  import { spawn } from "node:child_process";
3
3
  import fs from "node:fs";
4
4
  import net from "node:net";
5
+ import os from "node:os";
5
6
  import path from "node:path";
6
7
  import WebSocket, { WebSocketServer } from "ws";
7
8
  import { URL as URL$1, fileURLToPath } from "node:url";
@@ -157,6 +158,13 @@ async function getFreePort() {
157
158
  });
158
159
  });
159
160
  }
161
+ function resolveDefaultProfileDir() {
162
+ const platform = os.platform();
163
+ const home = os.homedir();
164
+ if (platform === "darwin") return path.join(home, "Library", "Application Support", "Google", "Chrome");
165
+ if (platform === "win32") return path.join(process.env.LOCALAPPDATA ?? path.join(home, "AppData", "Local"), "Google", "Chrome", "User Data");
166
+ return path.join(home, ".config", "google-chrome");
167
+ }
160
168
  var ChromeCdpBrowserController = class {
161
169
  connections = /* @__PURE__ */ new Map();
162
170
  constructor(baseDir, connectionFactory = CdpConnection.connect) {
@@ -191,6 +199,19 @@ var ChromeCdpBrowserController = class {
191
199
  closeConnection(targetWsUrl) {
192
200
  this.dropConnection(targetWsUrl);
193
201
  }
202
+ async connect(cdpUrl) {
203
+ const parsed = new URL(cdpUrl);
204
+ const port = Number.parseInt(parsed.port, 10);
205
+ if (!port) throw new Error(`Invalid CDP URL: could not extract port from ${cdpUrl}`);
206
+ await waitForDebugger(port);
207
+ const targetWsUrl = await createTarget(cdpUrl);
208
+ await evaluateExpression(targetWsUrl, "window.location.href");
209
+ return {
210
+ pid: 0,
211
+ cdpUrl,
212
+ targetWsUrl
213
+ };
214
+ }
194
215
  async ensureEnabled(targetWsUrl) {
195
216
  const cached = this.connections.get(targetWsUrl);
196
217
  if (!cached) return;
@@ -203,11 +224,16 @@ var ChromeCdpBrowserController = class {
203
224
  cached.enabled.runtime = true;
204
225
  }
205
226
  }
206
- async launch(sessionId, explicitPath) {
227
+ async launch(sessionId, explicitPath, userProfileDir) {
207
228
  const executablePath = discoverChrome(explicitPath);
208
229
  const extension = loadControlExtension();
209
- const profileDir = path.join(this.baseDir, "profiles", sessionId);
230
+ let profileDir;
231
+ if (userProfileDir === "default") profileDir = resolveDefaultProfileDir();
232
+ else if (userProfileDir) profileDir = userProfileDir;
233
+ else profileDir = path.join(this.baseDir, "profiles", sessionId);
210
234
  fs.mkdirSync(profileDir, { recursive: true });
235
+ const lockFile = path.join(profileDir, "SingletonLock");
236
+ if (fs.existsSync(lockFile)) throw new Error(`Chrome profile is already in use (lock file exists: ${lockFile}). Quit the running Chrome instance first, or use --cdp-url to connect to it instead.`);
211
237
  const launchAttempts = [
212
238
  {
213
239
  withExtension: true,
@@ -640,6 +666,7 @@ var ChromeCdpBrowserController = class {
640
666
  }
641
667
  }
642
668
  terminate(pid) {
669
+ if (pid === 0) return;
643
670
  try {
644
671
  process.kill(pid, "SIGTERM");
645
672
  } catch {}
@@ -662,6 +689,19 @@ var MockBrowserController = class {
662
689
  targetWsUrl
663
690
  };
664
691
  }
692
+ async connect(cdpUrl) {
693
+ this.pages.set(cdpUrl, {
694
+ url: "about:blank",
695
+ title: "about:blank",
696
+ text: "",
697
+ html: "<html><body></body></html>"
698
+ });
699
+ return {
700
+ pid: 0,
701
+ cdpUrl,
702
+ targetWsUrl: cdpUrl
703
+ };
704
+ }
665
705
  async navigate(cdpUrl, url) {
666
706
  const page = this.pages.get(cdpUrl);
667
707
  if (!page) throw new Error("mock page missing");
@@ -785,7 +825,7 @@ var SessionManager = class {
785
825
  if (active && active.session.status !== "terminated") throw new Error("A managed session is already active");
786
826
  const sessionId = crypto.randomUUID();
787
827
  const token = this.ctx.tokenService.issue(sessionId);
788
- const launched = await this.browser.launch(sessionId, this.ctx.config.browserExecutablePath);
828
+ const launched = this.ctx.config.cdpUrl ? await this.browser.connect(this.ctx.config.cdpUrl) : await this.browser.launch(sessionId, this.ctx.config.browserExecutablePath, this.ctx.config.userProfileDir);
789
829
  const session = {
790
830
  sessionId,
791
831
  status: "ready",
@@ -1136,12 +1176,18 @@ function loadConfig(env = process.env) {
1136
1176
  const commandTimeoutMs = Number.parseInt(env.AGENTIC_BROWSER_COMMAND_TIMEOUT_MS ?? `${DEFAULT_TIMEOUT_MS}`, 10);
1137
1177
  if (Number.isNaN(wsPort) || wsPort <= 0) throw new Error("AGENTIC_BROWSER_WS_PORT must be a positive integer");
1138
1178
  if (Number.isNaN(commandTimeoutMs) || commandTimeoutMs <= 0) throw new Error("AGENTIC_BROWSER_COMMAND_TIMEOUT_MS must be a positive integer");
1179
+ const userProfile = env.AGENTIC_BROWSER_USER_PROFILE;
1180
+ let userProfileDir;
1181
+ if (userProfile === "true" || userProfile === "default") userProfileDir = "default";
1182
+ else if (userProfile && path.isAbsolute(userProfile)) userProfileDir = userProfile;
1139
1183
  return {
1140
1184
  host: env.AGENTIC_BROWSER_HOST ?? "127.0.0.1",
1141
1185
  wsPort,
1142
1186
  commandTimeoutMs,
1143
1187
  logDir: env.AGENTIC_BROWSER_LOG_DIR ?? path.resolve(process.cwd(), ".agentic-browser"),
1144
- browserExecutablePath: env.AGENTIC_BROWSER_CHROME_PATH
1188
+ browserExecutablePath: env.AGENTIC_BROWSER_CHROME_PATH,
1189
+ cdpUrl: env.AGENTIC_BROWSER_CDP_URL,
1190
+ userProfileDir
1145
1191
  };
1146
1192
  }
1147
1193
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-browser",
3
- "version": "1.0.3",
3
+ "version": "1.2.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",