@nuanu-ai/agentbrowse 0.2.22 → 0.2.23

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/README.md CHANGED
@@ -47,6 +47,16 @@ agentbrowse launch https://example.com
47
47
  agentbrowse launch https://example.com --headless
48
48
  ```
49
49
 
50
+ By default, `launch` runs in headful mode. Use `--headless` only when you
51
+ intentionally want a hidden browser:
52
+
53
+ ```bash
54
+ agentbrowse launch https://example.com --headless
55
+ ```
56
+
57
+ If you want to state the default mode explicitly in scripts, use
58
+ `--headful`.
59
+
50
60
  Navigate current session:
51
61
 
52
62
  ```bash
@@ -1,5 +1,16 @@
1
1
  /**
2
2
  * browse close — Close the browser and clean up session.
3
3
  */
4
- export declare function close(): Promise<void>;
4
+ export type CloseSuccessResult = {
5
+ success: true;
6
+ };
7
+ export type CloseFailureResult = {
8
+ success: false;
9
+ error: 'browser_close_failed';
10
+ outcomeType: 'blocked';
11
+ message: 'Browser close failed.';
12
+ reason: string;
13
+ };
14
+ export type CloseResult = CloseSuccessResult | CloseFailureResult;
15
+ export declare function close(): Promise<CloseResult>;
5
16
  //# sourceMappingURL=close.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"close.d.ts","sourceRoot":"","sources":["../../src/commands/close.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CA0B3C"}
1
+ {"version":3,"file":"close.d.ts","sourceRoot":"","sources":["../../src/commands/close.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,IAAI,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,sBAAsB,CAAC;IAC9B,WAAW,EAAE,SAAS,CAAC;IACvB,OAAO,EAAE,uBAAuB,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAMlE,wBAAsB,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC,CAwBlD"}
@@ -1,32 +1,30 @@
1
1
  /**
2
2
  * browse close — Close the browser and clean up session.
3
3
  */
4
- import { execSync } from 'node:child_process';
5
- import { loadSession, deleteSession, getSessionPort } from '../session.js';
6
- import { outputJSON, info } from '../output.js';
4
+ import { loadSession, deleteSession } from '../session.js';
5
+ import { info } from '../output.js';
6
+ import { terminateOwnedPid } from '../owned-process.js';
7
+ function isOwnedSessionRecord(session) {
8
+ return session?.identity?.ownership === 'agentbrowse';
9
+ }
7
10
  export async function close() {
8
11
  const session = loadSession();
9
- const port = getSessionPort(session);
10
- // Kill Chrome on the current session CDP port regardless of session state
11
- try {
12
- const out = execSync(`lsof -ti :${port}`, { encoding: 'utf-8' }).trim();
13
- if (out) {
14
- for (const pid of out.split('\n')) {
15
- try {
16
- process.kill(Number(pid), 'SIGTERM');
17
- }
18
- catch {
19
- /* already dead */
20
- }
21
- }
22
- info(`Killed Chrome on port ${port}`);
12
+ if (isOwnedSessionRecord(session)) {
13
+ const termination = await terminateOwnedPid(session.pid);
14
+ if (termination === 'still_alive') {
15
+ info(`Owned browser pid ${session.pid} did not exit; keeping session record`);
16
+ return {
17
+ success: false,
18
+ error: 'browser_close_failed',
19
+ outcomeType: 'blocked',
20
+ message: 'Browser close failed.',
21
+ reason: `Owned browser pid ${session.pid} did not exit after SIGTERM/SIGKILL.`,
22
+ };
23
23
  }
24
- }
25
- catch {
26
- /* no process on port */
24
+ info(`Killed owned browser pid ${session.pid}`);
27
25
  }
28
26
  if (session) {
29
27
  deleteSession();
30
28
  }
31
- outputJSON({ success: true });
29
+ return { success: true };
32
30
  }
@@ -1,10 +1,31 @@
1
1
  /**
2
2
  * browse launch [url] — Start browser session, optionally navigate.
3
3
  */
4
+ import type { SecretCatalogSummary } from '../secrets/catalog-sync.js';
4
5
  export type LaunchCliOptions = {
5
6
  compact?: boolean;
6
7
  profile?: string;
7
8
  headless?: boolean;
9
+ proxy?: string;
10
+ noProxy?: boolean;
8
11
  };
9
- export declare function launch(url?: string, opts?: LaunchCliOptions): Promise<void>;
12
+ export type LaunchSuccessResult = {
13
+ success: true;
14
+ runtime: 'managed';
15
+ captchaSolveCapable: true;
16
+ profile: string;
17
+ cdpUrl: string;
18
+ url: string;
19
+ title: string;
20
+ secretCatalog?: SecretCatalogSummary;
21
+ };
22
+ export type LaunchFailureResult = {
23
+ success: false;
24
+ error: 'browser_launch_failed';
25
+ outcomeType: 'blocked';
26
+ message: 'Browser launch failed.';
27
+ reason: string;
28
+ };
29
+ export type LaunchResult = LaunchSuccessResult | LaunchFailureResult;
30
+ export declare function launch(url?: string, opts?: LaunchCliOptions): Promise<LaunchResult>;
10
31
  //# sourceMappingURL=launch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/commands/launch.ts"],"names":[],"mappings":"AAAA;;GAEG;AAsBH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,wBAAsB,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQjF"}
1
+ {"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/commands/launch.ts"],"names":[],"mappings":"AAAA;;GAEG;AAWH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAWvE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,SAAS,CAAC;IACnB,mBAAmB,EAAE,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,oBAAoB,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,uBAAuB,CAAC;IAC/B,WAAW,EAAE,SAAS,CAAC;IACvB,OAAO,EAAE,wBAAwB,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AAErE,wBAAsB,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAazF"}
@@ -1,92 +1,112 @@
1
1
  /**
2
2
  * browse launch [url] — Start browser session, optionally navigate.
3
3
  */
4
- import { execSync } from 'node:child_process';
5
4
  import { readConfig } from '../solver/config.js';
6
5
  import { ensureProfile } from '../solver/profile-manager.js';
7
6
  import { launchSolver } from '../solver/browser-launcher.js';
8
- import { saveSession } from '../session.js';
7
+ import { buildOwnedSession, isOwnedSession, loadSession, saveSession } from '../session.js';
9
8
  import { findChromePid } from '../stagehand.js';
10
- import { outputContractFailure, outputJSON } from '../output.js';
11
9
  import { applyAgentpayGatewayEnv, tryResolveAgentpayGatewayConfig } from '../agentpay-gateway.js';
12
10
  import { connectPlaywright, disconnectPlaywright, syncLaunchPage } from '../playwright-runtime.js';
13
11
  import { summarizeSecretCatalog, syncSecretCatalogForUrl } from '../secrets/catalog-sync.js';
14
- const CDP_PORT = 9222;
12
+ import { terminateOwnedPid } from '../owned-process.js';
13
+ import { info } from '../output.js';
15
14
  const DEFAULT_PROFILE = 'default';
16
15
  const COMPACT_WINDOW = {
17
16
  width: 1280,
18
17
  height: 900,
19
18
  };
20
19
  export async function launch(url, opts) {
21
- await killChromeOnPort(CDP_PORT);
20
+ const cleanupFailure = await cleanupOwnedSession();
21
+ if (cleanupFailure) {
22
+ return cleanupFailure;
23
+ }
22
24
  const compact = opts?.compact ?? true;
23
25
  const profileName = opts?.profile ?? DEFAULT_PROFILE;
24
26
  const headless = opts?.headless ?? false;
25
- await launchManaged(url, profileName, headless, compact);
27
+ const proxyOverride = opts?.proxy;
28
+ const noProxy = opts?.noProxy ?? false;
29
+ return launchManaged(url, profileName, headless, compact, proxyOverride, noProxy);
26
30
  }
27
- async function killChromeOnPort(port) {
28
- const pids = getPidsOnPort(port);
29
- if (pids.length === 0)
30
- return;
31
- for (const pid of pids) {
32
- try {
33
- process.kill(pid, 'SIGTERM');
34
- }
35
- catch {
36
- // Skip stale PID.
37
- }
31
+ async function cleanupOwnedSession() {
32
+ const existingSession = loadSession();
33
+ if (!isOwnedSession(existingSession)) {
34
+ return null;
38
35
  }
39
- for (let i = 0; i < 20; i++) {
40
- if (getPidsOnPort(port).length === 0)
41
- return;
42
- await sleep(100);
36
+ const termination = await terminateOwnedPid(existingSession.pid);
37
+ if (termination === 'still_alive') {
38
+ return buildLaunchFailure(new Error(`Existing owned browser pid ${existingSession.pid} did not exit after SIGTERM/SIGKILL.`));
43
39
  }
44
- for (const pid of getPidsOnPort(port)) {
45
- try {
46
- process.kill(pid, 'SIGKILL');
47
- }
48
- catch {
49
- // Skip stale PID.
40
+ return null;
41
+ }
42
+ function buildLaunchFailure(err) {
43
+ return {
44
+ success: false,
45
+ error: 'browser_launch_failed',
46
+ outcomeType: 'blocked',
47
+ message: 'Browser launch failed.',
48
+ reason: formatUnknownError(err),
49
+ };
50
+ }
51
+ function getPortFromCdpUrl(cdpUrl) {
52
+ try {
53
+ const url = new URL(cdpUrl);
54
+ if (!url.port) {
55
+ return undefined;
50
56
  }
57
+ const port = Number(url.port);
58
+ return Number.isFinite(port) && port > 0 ? port : undefined;
59
+ }
60
+ catch {
61
+ return undefined;
51
62
  }
52
63
  }
53
- async function launchManaged(url, profileName, headless, compact) {
64
+ async function launchManaged(url, profileName, headless, compact, proxyOverride, noProxy = false) {
54
65
  let session;
55
66
  let browser = null;
56
67
  let secretCatalogSummary;
57
68
  try {
58
- const profile = ensureProfile(profileName);
69
+ const baseProfile = ensureProfile(profileName);
59
70
  const config = readConfig();
71
+ const runtimeProxy = resolveLaunchProxy({
72
+ profileProxy: baseProfile.fingerprint.proxy,
73
+ configProxy: config.defaults?.proxy,
74
+ cliProxy: proxyOverride,
75
+ noProxy,
76
+ });
77
+ const profile = withLaunchProxy(baseProfile, runtimeProxy);
60
78
  const gateway = tryResolveAgentpayGatewayConfig();
61
79
  if (gateway) {
62
80
  applyAgentpayGatewayEnv(gateway);
63
81
  }
82
+ if (runtimeProxy) {
83
+ info(`[launch] starting browser with proxy ${runtimeProxy.server}`);
84
+ }
64
85
  session = await launchSolver(profile, {
65
86
  headless: headless || config.defaults?.headless,
66
87
  url,
67
- cdpPort: CDP_PORT,
88
+ cdpPort: undefined,
68
89
  windowSize: compact ? COMPACT_WINDOW : undefined,
69
90
  });
70
91
  }
71
92
  catch (err) {
72
- outputContractFailure({
73
- error: 'browser_launch_failed',
74
- outcomeType: 'blocked',
75
- message: 'Browser launch failed.',
76
- reason: formatUnknownError(err),
77
- });
93
+ return buildLaunchFailure(err);
94
+ }
95
+ const cdpPort = getPortFromCdpUrl(session.cdpUrl);
96
+ const chromePid = findChromePid(cdpPort);
97
+ if (!chromePid) {
98
+ await session.close().catch(() => undefined);
99
+ return buildLaunchFailure(new Error('Launched browser PID could not be resolved.'));
78
100
  }
79
- const chromePid = findChromePid(CDP_PORT) ?? process.pid;
80
- const persistedSession = {
101
+ const persistedSession = buildOwnedSession({
81
102
  cdpUrl: session.cdpUrl,
82
103
  pid: chromePid,
83
- port: CDP_PORT,
84
104
  profile: profileName,
85
105
  launchedAt: new Date().toISOString(),
86
106
  capabilities: {
87
107
  captchaSolve: true,
88
108
  },
89
- };
109
+ });
90
110
  saveSession(persistedSession);
91
111
  let currentUrl = session.page.url();
92
112
  let title = await session.page.title().catch(() => '');
@@ -111,10 +131,12 @@ async function launchManaged(url, profileName, headless, compact) {
111
131
  }
112
132
  if (url) {
113
133
  const snapshot = await syncSecretCatalogForUrl(persistedSession, currentUrl || url);
114
- secretCatalogSummary = summarizeSecretCatalog(snapshot);
134
+ if (snapshot) {
135
+ secretCatalogSummary = summarizeSecretCatalog(snapshot);
136
+ }
115
137
  }
116
138
  saveSession(persistedSession);
117
- outputJSON({
139
+ return {
118
140
  success: true,
119
141
  runtime: 'managed',
120
142
  captchaSolveCapable: true,
@@ -123,21 +145,7 @@ async function launchManaged(url, profileName, headless, compact) {
123
145
  url: currentUrl,
124
146
  title,
125
147
  secretCatalog: secretCatalogSummary,
126
- });
127
- }
128
- function getPidsOnPort(port) {
129
- try {
130
- const out = execSync(`lsof -ti :${port}`, { encoding: 'utf-8' }).trim();
131
- if (!out)
132
- return [];
133
- return out
134
- .split('\n')
135
- .map((value) => Number(value))
136
- .filter((value) => Number.isFinite(value) && value > 0);
137
- }
138
- catch {
139
- return [];
140
- }
148
+ };
141
149
  }
142
150
  function formatUnknownError(err) {
143
151
  if (err instanceof Error)
@@ -154,6 +162,61 @@ function formatUnknownError(err) {
154
162
  }
155
163
  return String(err);
156
164
  }
157
- function sleep(ms) {
158
- return new Promise((resolve) => setTimeout(resolve, ms));
165
+ function resolveLaunchProxy(options) {
166
+ if (options.noProxy) {
167
+ return undefined;
168
+ }
169
+ if (options.cliProxy) {
170
+ return normalizeProxySetting(options.cliProxy);
171
+ }
172
+ if (options.configProxy) {
173
+ return normalizeProxySetting(options.configProxy);
174
+ }
175
+ if (options.profileProxy) {
176
+ return normalizeProxySetting(options.profileProxy);
177
+ }
178
+ return undefined;
179
+ }
180
+ function withLaunchProxy(profile, proxy) {
181
+ return {
182
+ ...profile,
183
+ fingerprint: {
184
+ ...profile.fingerprint,
185
+ proxy,
186
+ },
187
+ };
188
+ }
189
+ function normalizeProxySetting(value) {
190
+ if (typeof value === 'string') {
191
+ return parseProxyString(value);
192
+ }
193
+ const normalized = parseProxyString(value.server);
194
+ const username = value.username?.trim() || normalized.username;
195
+ const password = value.password ?? normalized.password;
196
+ return {
197
+ server: normalized.server,
198
+ ...(username ? { username } : {}),
199
+ ...(password ? { password } : {}),
200
+ };
201
+ }
202
+ function parseProxyString(value) {
203
+ const trimmed = value.trim();
204
+ if (!trimmed) {
205
+ throw new Error('Proxy value must not be empty.');
206
+ }
207
+ try {
208
+ const parsed = new URL(trimmed);
209
+ const server = `${parsed.protocol}//${parsed.host}`;
210
+ if (!parsed.hostname) {
211
+ throw new Error('missing hostname');
212
+ }
213
+ return {
214
+ server,
215
+ ...(parsed.username ? { username: decodeURIComponent(parsed.username) } : {}),
216
+ ...(parsed.password ? { password: decodeURIComponent(parsed.password) } : {}),
217
+ };
218
+ }
219
+ catch {
220
+ return { server: trimmed };
221
+ }
159
222
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAiPA,iBAAe,IAAI,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4JhE;AAED,OAAO,EAAE,IAAI,EAAE,CAAC;AAEhB,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAWzF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AA0QA,iBAAe,IAAI,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgKhE;AAED,OAAO,EAAE,IAAI,EAAE,CAAC;AAEhB,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAWzF"}
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ loadEnv();
6
6
  import { preflightAgentpayGateway } from './agentpay-gateway.js';
7
7
  import { browseCommand, browseCommandName } from './command-name.js';
8
8
  import { loadSession } from './session.js';
9
- import { outputError, fatal, info } from './output.js';
9
+ import { outputError, outputJSON, fatal, info } from './output.js';
10
10
  function usageText() {
11
11
  return `Usage: ${browseCommandName()} <command> [args] [options]
12
12
 
@@ -32,8 +32,10 @@ Options:
32
32
  --compact Launch browser in compact window size (1280x900, default)
33
33
  --full Launch browser in full-size window
34
34
  --profile <name> Solver profile name for launch (default: "default")
35
+ --proxy <url> Launch browser through proxy for this run only
36
+ --proxyless Ignore configured/profile proxy for this run only
37
+ --headful Explicit alias for headful browser mode (default)
35
38
  --headless Launch browser in headless mode
36
- --no-headless Explicit alias for visible browser mode
37
39
  --path <file> Output path for screenshot
38
40
  --timeout <seconds> Timeout for solve-captcha command (default: 90)
39
41
  --help Show this help message
@@ -113,13 +115,15 @@ function parseLaunchArgs(args) {
113
115
  let compact = true;
114
116
  let profile;
115
117
  let headless = false;
118
+ let proxy;
119
+ let noProxy = false;
116
120
  for (let i = 0; i < args.length; i++) {
117
121
  const arg = args[i];
118
122
  if (arg === '--headless') {
119
123
  headless = true;
120
124
  continue;
121
125
  }
122
- if (arg === '--no-headless') {
126
+ if (arg === '--headful') {
123
127
  headless = false;
124
128
  continue;
125
129
  }
@@ -134,21 +138,36 @@ function parseLaunchArgs(args) {
134
138
  if (arg === '--profile') {
135
139
  const value = args[i + 1];
136
140
  if (!value || value.startsWith('--')) {
137
- outputError(`Usage: ${browseCommand('launch', '[url]', '[--profile <name>]', '[--headless]')}`);
141
+ outputError(`Usage: ${browseCommand('launch', '[url]', '[--profile <name>]', '[--proxy <url>|--proxyless]', '[--headful|--headless]')}`);
138
142
  }
139
143
  profile = value;
140
144
  i += 1;
141
145
  continue;
142
146
  }
147
+ if (arg === '--proxy') {
148
+ const value = args[i + 1];
149
+ if (!value || value.startsWith('--')) {
150
+ outputError(`Usage: ${browseCommand('launch', '[url]', '[--profile <name>]', '[--proxy <url>|--proxyless]', '[--headful|--headless]')}`);
151
+ }
152
+ proxy = value;
153
+ noProxy = false;
154
+ i += 1;
155
+ continue;
156
+ }
157
+ if (arg === '--proxyless') {
158
+ proxy = undefined;
159
+ noProxy = true;
160
+ continue;
161
+ }
143
162
  if (arg.startsWith('--')) {
144
163
  outputError(`Unknown launch option: ${arg}`);
145
164
  }
146
165
  if (url) {
147
- outputError(`Usage: ${browseCommand('launch', '[url]', '[--profile <name>]', '[--headless]')}`);
166
+ outputError(`Usage: ${browseCommand('launch', '[url]', '[--profile <name>]', '[--proxy <url>|--proxyless]', '[--headful|--headless]')}`);
148
167
  }
149
168
  url = arg;
150
169
  }
151
- return { url, compact, profile, headless };
170
+ return { url, compact, profile, headless, proxy, noProxy };
152
171
  }
153
172
  function parseCaptchaTimeout(args) {
154
173
  const timeoutRaw = getFlag(args, '--timeout');
@@ -220,11 +239,13 @@ async function main(argv = process.argv) {
220
239
  case 'launch': {
221
240
  const { launch } = await import('./commands/launch.js');
222
241
  const launchArgs = parseLaunchArgs(args);
223
- await launch(launchArgs.url, {
242
+ outputJSON(await launch(launchArgs.url, {
224
243
  compact: launchArgs.compact,
225
244
  profile: launchArgs.profile,
226
245
  headless: launchArgs.headless,
227
- });
246
+ proxy: launchArgs.proxy,
247
+ noProxy: launchArgs.noProxy,
248
+ }));
228
249
  break;
229
250
  }
230
251
  case 'navigate': {
@@ -322,7 +343,7 @@ async function main(argv = process.argv) {
322
343
  }
323
344
  case 'close': {
324
345
  const { close } = await import('./commands/close.js');
325
- await close();
346
+ outputJSON(await close());
326
347
  break;
327
348
  }
328
349
  }
@@ -0,0 +1,4 @@
1
+ export type OwnedPidTerminationResult = 'not_found' | 'terminated' | 'sigkilled' | 'still_alive';
2
+ export declare function isPidAlive(pid: number): boolean;
3
+ export declare function terminateOwnedPid(pid: number): Promise<OwnedPidTerminationResult>;
4
+ //# sourceMappingURL=owned-process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owned-process.d.ts","sourceRoot":"","sources":["../src/owned-process.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,yBAAyB,GAAG,WAAW,GAAG,YAAY,GAAG,WAAW,GAAG,aAAa,CAAC;AAEjG,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAW/C;AAED,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAsBvF"}
@@ -0,0 +1,57 @@
1
+ const TERM_WAIT_ATTEMPTS = 10;
2
+ const TERM_WAIT_MS = 100;
3
+ const KILL_WAIT_ATTEMPTS = 5;
4
+ const KILL_WAIT_MS = 20;
5
+ export function isPidAlive(pid) {
6
+ try {
7
+ process.kill(pid, 0);
8
+ return true;
9
+ }
10
+ catch (error) {
11
+ const code = getErrorCode(error);
12
+ if (code === 'EPERM') {
13
+ return true;
14
+ }
15
+ return false;
16
+ }
17
+ }
18
+ export async function terminateOwnedPid(pid) {
19
+ try {
20
+ process.kill(pid, 'SIGTERM');
21
+ }
22
+ catch (error) {
23
+ return isPidAlive(pid) ? 'still_alive' : 'not_found';
24
+ }
25
+ if (await waitForPidExit(pid, TERM_WAIT_ATTEMPTS, TERM_WAIT_MS)) {
26
+ return 'terminated';
27
+ }
28
+ try {
29
+ process.kill(pid, 'SIGKILL');
30
+ }
31
+ catch {
32
+ return isPidAlive(pid) ? 'still_alive' : 'sigkilled';
33
+ }
34
+ if (await waitForPidExit(pid, KILL_WAIT_ATTEMPTS, KILL_WAIT_MS)) {
35
+ return 'sigkilled';
36
+ }
37
+ return 'still_alive';
38
+ }
39
+ async function waitForPidExit(pid, attempts, intervalMs) {
40
+ for (let attempt = 0; attempt < attempts; attempt++) {
41
+ if (!isPidAlive(pid)) {
42
+ return true;
43
+ }
44
+ await sleep(intervalMs);
45
+ }
46
+ return !isPidAlive(pid);
47
+ }
48
+ function getErrorCode(error) {
49
+ if (!error || typeof error !== 'object') {
50
+ return undefined;
51
+ }
52
+ const code = Reflect.get(error, 'code');
53
+ return typeof code === 'string' ? code : undefined;
54
+ }
55
+ function sleep(ms) {
56
+ return new Promise((resolve) => setTimeout(resolve, ms));
57
+ }
package/dist/session.d.ts CHANGED
@@ -19,12 +19,21 @@ export interface BrowseSession {
19
19
  launchedAt: string;
20
20
  port?: number;
21
21
  profile?: string;
22
+ identity?: BrowseSessionIdentity;
22
23
  capabilities?: {
23
24
  captchaSolve?: boolean;
24
25
  };
25
26
  runtime?: BrowseRuntimeState;
26
27
  transientSecretCache?: Record<string, CachedTransientSecretEntry>;
27
28
  }
29
+ export interface BrowseSessionIdentity {
30
+ browserInstanceRef: string;
31
+ endpoint: string;
32
+ pid: number;
33
+ profile?: string;
34
+ launchedAt: string;
35
+ ownership: 'agentbrowse';
36
+ }
28
37
  export declare function cleanupTransientSecretCache(session: BrowseSession, options?: {
29
38
  now?: string;
30
39
  }): boolean;
@@ -34,11 +43,15 @@ export declare function getCachedTransientSecret(session: BrowseSession, intentI
34
43
  }): CachedTransientSecretEntry | null;
35
44
  export declare function deleteCachedTransientSecret(session: BrowseSession, intentId: string): boolean;
36
45
  export declare function serializeSession(session: BrowseSession): string;
46
+ export declare function buildOwnedSession(session: Omit<BrowseSession, 'port' | 'identity'>): BrowseSession;
37
47
  export declare function saveSession(session: BrowseSession): void;
38
48
  export declare function loadSession(): BrowseSession | null;
39
49
  export declare function getSessionPort(session: BrowseSession | null): number;
40
50
  export declare function deleteSession(): void;
41
51
  export declare function supportsCaptchaSolve(session: BrowseSession | null): boolean;
52
+ export declare function isOwnedSession(session: BrowseSession | null | undefined): session is BrowseSession & {
53
+ identity: BrowseSessionIdentity;
54
+ };
42
55
  /** Check if the Chrome process from the session is still alive. */
43
56
  export declare function isSessionAlive(session: BrowseSession): boolean;
44
57
  //# sourceMappingURL=session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAK/D,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE;QACb,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC;IACF,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;CACnE;AAyBD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,OAAO,CAqBT;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,0BAA0B,GAChC,0BAA0B,CAQ5B;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,0BAA0B,GAAG,IAAI,CAMnC;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAW7F;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAW/D;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAGxD;AAED,wBAAgB,WAAW,IAAI,aAAa,GAAG,IAAI,CAclD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,CAapE;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,CAE3E;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAO9D"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAK/D,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,YAAY,CAAC,EAAE;QACb,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC;IACF,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;CACnE;AAED,MAAM,WAAW,qBAAqB;IACpC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,aAAa,CAAC;CAC1B;AAkDD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,OAAO,CAqBT;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,0BAA0B,GAChC,0BAA0B,CAQ5B;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,0BAA0B,GAAG,IAAI,CAMnC;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAW7F;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAW/D;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,UAAU,CAAC,GAChD,aAAa,CAaf;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAGxD;AAED,wBAAgB,WAAW,IAAI,aAAa,GAAG,IAAI,CAclD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,CAWpE;AAED,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,CAE3E;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GACxC,OAAO,IAAI,aAAa,GAAG;IAAE,QAAQ,EAAE,qBAAqB,CAAA;CAAE,CAahE;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAO9D"}
package/dist/session.js CHANGED
@@ -20,6 +20,30 @@ function ensureDir() {
20
20
  mkdirSync(SESSION_DIR, { recursive: true });
21
21
  }
22
22
  }
23
+ function parseSessionPort(cdpUrl) {
24
+ try {
25
+ const url = new URL(cdpUrl);
26
+ if (!url.port) {
27
+ return undefined;
28
+ }
29
+ const port = Number(url.port);
30
+ return Number.isFinite(port) && port > 0 ? port : undefined;
31
+ }
32
+ catch {
33
+ return undefined;
34
+ }
35
+ }
36
+ function parseBrowserInstanceRef(cdpUrl) {
37
+ try {
38
+ const url = new URL(cdpUrl);
39
+ const pathSegments = url.pathname.split('/').filter(Boolean);
40
+ return pathSegments.at(-1) ?? cdpUrl;
41
+ }
42
+ catch {
43
+ const pathSegments = cdpUrl.split('/').filter(Boolean);
44
+ return pathSegments.at(-1) ?? cdpUrl;
45
+ }
46
+ }
23
47
  function isExpired(expiresAt, now) {
24
48
  if (!expiresAt) {
25
49
  return false;
@@ -79,6 +103,20 @@ export function serializeSession(session) {
79
103
  return value;
80
104
  }, 2);
81
105
  }
106
+ export function buildOwnedSession(session) {
107
+ return {
108
+ ...session,
109
+ port: parseSessionPort(session.cdpUrl),
110
+ identity: {
111
+ browserInstanceRef: parseBrowserInstanceRef(session.cdpUrl),
112
+ endpoint: session.cdpUrl,
113
+ pid: session.pid,
114
+ profile: session.profile,
115
+ launchedAt: session.launchedAt,
116
+ ownership: 'agentbrowse',
117
+ },
118
+ };
119
+ }
82
120
  export function saveSession(session) {
83
121
  ensureDir();
84
122
  writeFileSync(SESSION_PATH, serializeSession(session));
@@ -105,13 +143,9 @@ export function getSessionPort(session) {
105
143
  if (session?.port)
106
144
  return session.port;
107
145
  if (session?.cdpUrl) {
108
- try {
109
- const url = new URL(session.cdpUrl);
110
- if (url.port)
111
- return Number(url.port);
112
- }
113
- catch {
114
- // Ignore malformed cdpUrl and use default fallback.
146
+ const parsedPort = parseSessionPort(session.cdpUrl);
147
+ if (parsedPort) {
148
+ return parsedPort;
115
149
  }
116
150
  }
117
151
  return 9222;
@@ -123,6 +157,17 @@ export function deleteSession() {
123
157
  export function supportsCaptchaSolve(session) {
124
158
  return session?.capabilities?.captchaSolve === true;
125
159
  }
160
+ export function isOwnedSession(session) {
161
+ if (!session?.identity) {
162
+ return false;
163
+ }
164
+ return (session.identity.ownership === 'agentbrowse' &&
165
+ session.identity.endpoint === session.cdpUrl &&
166
+ session.identity.pid === session.pid &&
167
+ session.identity.launchedAt === session.launchedAt &&
168
+ session.identity.browserInstanceRef.length > 0 &&
169
+ session.identity.profile === session.profile);
170
+ }
126
171
  /** Check if the Chrome process from the session is still alive. */
127
172
  export function isSessionAlive(session) {
128
173
  try {
@@ -24,6 +24,7 @@ export type ProxyConfig = {
24
24
  username?: string;
25
25
  password?: string;
26
26
  };
27
+ export type ProxySetting = string | ProxyConfig;
27
28
  export type ProfileInfo = {
28
29
  name: string;
29
30
  fingerprint: BrowserFingerprint;
@@ -37,6 +38,7 @@ export type SolverConfig = {
37
38
  };
38
39
  defaults?: {
39
40
  headless?: boolean;
41
+ proxy?: ProxySetting;
40
42
  };
41
43
  };
42
44
  export type CaptchaType = 'recaptcha-v2' | 'hcaptcha' | 'turnstile';
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/solver/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,kBAAkB,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,CAAC;AAEpE,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,sBAAsB,CAAC;AACrE,MAAM,MAAM,sBAAsB,GAAG,aAAa,GAAG,UAAU,GAAG,cAAc,CAAC;AAEjF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/solver/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,CAAC;AAEhD,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,kBAAkB,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,QAAQ,CAAC,EAAE;QACT,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,KAAK,CAAC,EAAE,YAAY,CAAC;KACtB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,CAAC;AAEpE,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,sBAAsB,CAAC;AACrE,MAAM,MAAM,sBAAsB,GAAG,aAAa,GAAG,UAAU,GAAG,cAAc,CAAC;AAEjF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuanu-ai/agentbrowse",
3
- "version": "0.2.22",
3
+ "version": "0.2.23",
4
4
  "type": "module",
5
5
  "description": "Browser automation CLI for AI agents: control a CDP browser, observe UI surfaces, act on refs, extract data, capture screenshots, complete protected fills, and solve captchas",
6
6
  "keywords": [