browse-agent-cli 0.0.1

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 ADDED
@@ -0,0 +1,125 @@
1
+ # browse-agent-cli
2
+
3
+ `browse-agent-cli` is an npm package that provides:
4
+
5
+ - A CLI command: `browse-agent`
6
+ - Script-style exports for Node.js: `browse-agent-cli/script`
7
+
8
+ It is designed for browser automation workflows powered by `browse-agent-sdk`.
9
+
10
+ ## Installation
11
+
12
+ ### Global install (recommended for CLI)
13
+
14
+ ```bash
15
+ npm install -g browse-agent-cli
16
+ ```
17
+
18
+ After installation:
19
+
20
+ ```bash
21
+ browse-agent --help
22
+ ```
23
+
24
+ ### Project install (for script imports)
25
+
26
+ ```bash
27
+ npm install browse-agent-cli
28
+ ```
29
+
30
+ ## Quick Start (CLI)
31
+
32
+ ```bash
33
+ # One-time setup
34
+ browse-agent setup
35
+
36
+ # Launch browser session
37
+ browse-agent launch
38
+
39
+ # Navigate and extract
40
+ browse-agent navigate "https://example.com"
41
+ browse-agent get-content --format text
42
+
43
+ # Close session
44
+ browse-agent close
45
+ ```
46
+
47
+ ## CLI Commands
48
+
49
+ ### Lifecycle
50
+
51
+ - `browse-agent setup`
52
+ - `browse-agent launch`
53
+ - `browse-agent connect`
54
+ - `browse-agent close`
55
+ - `browse-agent clear`
56
+
57
+ ### Feature commands
58
+
59
+ - `browse-agent navigate <url>`
60
+ - `browse-agent get-content [--format text|html] [--tabId <id>]`
61
+ - `browse-agent get-dom <selector> [--property outerHTML|innerHTML|innerText] [--all] [--tabId <id>]`
62
+ - `browse-agent evaluate <expression> [--tabId <id>]`
63
+ - `browse-agent inject-script <code> [--tabId <id>]`
64
+ - `browse-agent inject-css <code> [--tabId <id>]`
65
+ - `browse-agent screenshot [visible|fullPage|area] [--format png|jpeg] [--quality <1-100>] [--tabId <id>]`
66
+ - `browse-agent tabs [list|close|activate] [tabId]`
67
+
68
+ ### Skill command
69
+
70
+ - `browse-agent skill <directory>`
71
+
72
+ This command copies the package `skill` folder into:
73
+
74
+ - `<directory>/browse-agent`
75
+
76
+ Conflict behavior when target exists and is not empty:
77
+
78
+ - `overwrite`: remove and replace target folder
79
+ - `rename`: enter a new directory name, then retry
80
+ - `cancel`: abort operation
81
+
82
+ ## Script Import
83
+
84
+ Import helper functions from `browse-agent-cli/script`:
85
+
86
+ ```ts
87
+ import { browse } from 'browse-agent-cli/script';
88
+
89
+ await browse(async (agent) => {
90
+ await agent.navigate('https://example.com');
91
+ const page = await agent.getContent({ format: 'text' });
92
+ return { title: page.title, content: page.content };
93
+ });
94
+ ```
95
+
96
+ You can also import modular helpers:
97
+
98
+ ```ts
99
+ import {
100
+ launchBrowser,
101
+ navigate,
102
+ getContent,
103
+ screenshot,
104
+ closeBrowser,
105
+ } from 'browse-agent-cli/script';
106
+ ```
107
+
108
+ ## Common Options
109
+
110
+ - `--browser <name>`: `chrome | chromium | edge | brave`
111
+ - `--headless`
112
+ - `--port <number>`
113
+ - `--servicePort <number>`
114
+ - `--timeout <ms>`
115
+ - `--tabId <id>`
116
+
117
+ ## Notes
118
+
119
+ - Node.js 18+ is required.
120
+ - One of Chrome/Chromium/Edge/Brave is required.
121
+ - For logged-in pages, ensure your browser profile/session strategy matches your environment.
122
+
123
+ ## License
124
+
125
+ MIT
package/dist/clear.js ADDED
@@ -0,0 +1,24 @@
1
+ import { t as BASE_DIR, u as loadSession } from "./config.js";
2
+ import { existsSync, rmSync } from "node:fs";
3
+ //#region src/scripts/clear.ts
4
+ async function clear() {
5
+ const baseDir = BASE_DIR;
6
+ console.log("Clearing browse-agent...");
7
+ const session = loadSession();
8
+ if (session?.pid) try {
9
+ process.kill(session.pid);
10
+ console.log(` Killed browser (PID: ${session.pid})`);
11
+ } catch (err) {
12
+ const killErr = err;
13
+ if (killErr.code !== "ESRCH") console.error(` Failed to kill browser (PID: ${session.pid}):`, killErr.message);
14
+ }
15
+ if (existsSync(baseDir)) {
16
+ rmSync(baseDir, { recursive: true });
17
+ console.log(` Removed ${baseDir}`);
18
+ } else console.log(` ${baseDir} does not exist, skipping.`);
19
+ console.log("Done.\n");
20
+ }
21
+ //#endregion
22
+ export { clear as t };
23
+
24
+ //# sourceMappingURL=clear.js.map
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,501 @@
1
+ #!/usr/bin/env node
2
+ import { l as loadServiceSession, n as DEFAULT_SERVICE_PORT, t as BASE_DIR } from "./config.js";
3
+ import { t as clear } from "./clear.js";
4
+ import { execSync, spawn } from "node:child_process";
5
+ import { cpSync, existsSync, mkdirSync, readdirSync, rmSync, statSync, unlinkSync } from "node:fs";
6
+ import { dirname, join, resolve } from "node:path";
7
+ import { createInterface } from "node:readline/promises";
8
+ import { fileURLToPath } from "node:url";
9
+ //#region src/scripts/setup.ts
10
+ async function setup() {
11
+ const baseDir = BASE_DIR;
12
+ const extensionDir = join(baseDir, "extension");
13
+ const zipPath = join(baseDir, "extension.zip");
14
+ const extensionInstalled = existsSync(join(extensionDir, "manifest.json"));
15
+ if (extensionInstalled) {
16
+ console.log("browse-agent is already set up.");
17
+ console.log(` Extension path: ${extensionDir}`);
18
+ console.log(" Extension: installed\n");
19
+ return;
20
+ }
21
+ console.log("Setting up browse-agent...\n");
22
+ mkdirSync(baseDir, { recursive: true });
23
+ if (extensionInstalled) {
24
+ console.log("\n[1/2] Downloading Chrome extension from latest release...");
25
+ console.log(" Skipped: extension is already installed.");
26
+ console.log("\n[2/2] Extracting extension...");
27
+ console.log(" Skipped: extension is already installed.");
28
+ } else {
29
+ console.log("\n[1/2] Downloading Chrome extension from latest release...");
30
+ const releaseJson = execSync(`curl -s "https://api.github.com/repos/imlinhanchao/browse-agent/releases/latest"`).toString();
31
+ const asset = JSON.parse(releaseJson).assets?.find((item) => item.name.endsWith(".zip"));
32
+ if (!asset) {
33
+ console.error("Error: No extension zip found in latest release.");
34
+ console.error("Visit https://github.com/imlinhanchao/browse-agent/releases to check.");
35
+ process.exit(1);
36
+ }
37
+ console.log(` Downloading ${asset.name} (${(asset.size / 1024).toFixed(1)} KB)...`);
38
+ execSync(`curl -sL -o "${zipPath}" "${asset.browser_download_url}"`);
39
+ console.log("\n[2/2] Extracting extension...");
40
+ if (existsSync(extensionDir)) execSync(`rm -rf "${extensionDir}"`);
41
+ mkdirSync(extensionDir, { recursive: true });
42
+ execSync(`unzip -o "${zipPath}" -d "${extensionDir}"`, { stdio: "pipe" });
43
+ unlinkSync(zipPath);
44
+ const files = readdirSync(extensionDir);
45
+ if (!files.includes("manifest.json")) {
46
+ const subdirs = files.filter((file) => {
47
+ try {
48
+ return readdirSync(join(extensionDir, file)).includes("manifest.json");
49
+ } catch {
50
+ return false;
51
+ }
52
+ });
53
+ if (subdirs.length > 0) {
54
+ const subdir = join(extensionDir, subdirs[0]);
55
+ execSync(`mv "${subdir}"/* "${extensionDir}"/`);
56
+ execSync(`rmdir "${subdir}"`);
57
+ } else {
58
+ console.error("Error: Extension extraction failed - manifest.json not found.");
59
+ process.exit(1);
60
+ }
61
+ }
62
+ }
63
+ console.log("\nSetup complete!");
64
+ console.log(` Extension path: ${extensionDir}`);
65
+ console.log(" Extension: installed\n");
66
+ }
67
+ //#endregion
68
+ //#region src/cli.ts
69
+ function parseArgs(argv) {
70
+ const args = argv.slice(2);
71
+ const command = args.find((a) => !a.startsWith("-"));
72
+ const positional = args.filter((a) => !a.startsWith("-"));
73
+ const flags = {};
74
+ for (let i = 0; i < args.length; i++) {
75
+ const arg = args[i];
76
+ if (arg === "--headless") {
77
+ flags.headless = true;
78
+ continue;
79
+ }
80
+ if (arg === "--all") {
81
+ flags.all = true;
82
+ continue;
83
+ }
84
+ if (arg.startsWith("--") && i + 1 < args.length && !args[i + 1].startsWith("-")) {
85
+ flags[arg.slice(2)] = args[++i];
86
+ continue;
87
+ }
88
+ }
89
+ return {
90
+ command,
91
+ positional: positional.slice(1),
92
+ flags
93
+ };
94
+ }
95
+ function getStringFlag(flags, key) {
96
+ const value = flags[key];
97
+ return typeof value === "string" ? value : void 0;
98
+ }
99
+ function getNumberFlag(flags, key) {
100
+ const value = getStringFlag(flags, key);
101
+ if (!value) return void 0;
102
+ const parsed = Number(value);
103
+ return Number.isNaN(parsed) ? void 0 : parsed;
104
+ }
105
+ function showHelp() {
106
+ console.log(`
107
+ browse-agent - Browser automation skill CLI
108
+
109
+ Usage: browse-agent <command> [options]
110
+
111
+ Lifecycle commands:
112
+ setup Install Chrome extension
113
+ launch Start background service and launch browser
114
+ connect Verify connection to running service/browser
115
+ close Stop browser and background service
116
+ clear Remove local installation data
117
+ skill <dir> Copy skill bundle into <dir>/browse-agent
118
+
119
+ Feature commands (require launch first):
120
+ navigate <url> Navigate to URL
121
+ get-content Get page content (HTML or text)
122
+ get-dom <selector> Query DOM elements
123
+ evaluate <expr> Evaluate JavaScript expression
124
+ inject-script <code> Inject and execute JavaScript
125
+ inject-css <code> Inject CSS stylesheet
126
+ screenshot [mode] Capture screenshot (visible|fullPage|area)
127
+ tabs [action] [id] Manage tabs (list|close|activate)
128
+
129
+ Options:
130
+ --browser <name> Browser: chrome | chromium | edge | brave (default: chrome)
131
+ --headless Run in headless mode
132
+ --port <number> WebSocket port for browser-agent (default: 9315)
133
+ --servicePort <number> Local service port (default: 9316)
134
+ --tabId <id> Target tab ID (from navigate or tabs list output)
135
+ --format <type> Content/screenshot format (text|html / png|jpeg)
136
+ --property <prop> DOM property: outerHTML | innerHTML | innerText
137
+ --all Return all DOM matches instead of first only
138
+ --quality <num> Screenshot quality (1-100, jpeg only)
139
+ --timeout <ms> Connection timeout in ms
140
+ --help, -h Show this help
141
+
142
+ Examples:
143
+ browse-agent setup
144
+ browse-agent launch --browser edge --headless
145
+ browse-agent navigate https://example.com
146
+ browse-agent get-content --format text
147
+ browse-agent get-dom "h1" --property innerText
148
+ browse-agent evaluate "document.title"
149
+ browse-agent screenshot fullPage --format png
150
+ browse-agent tabs list
151
+ browse-agent close
152
+ browse-agent clear
153
+ browse-agent skill ~/my-skills
154
+ `.trim());
155
+ }
156
+ function resolveSkillSourceDir() {
157
+ return join(dirname(fileURLToPath(import.meta.url)), "..", "skill");
158
+ }
159
+ function isNonEmptyDirectory(path) {
160
+ if (!existsSync(path)) return false;
161
+ if (!statSync(path).isDirectory()) return true;
162
+ return readdirSync(path).length > 0;
163
+ }
164
+ async function chooseSkillDestination(baseDir) {
165
+ let targetName = "browse-agent";
166
+ while (true) {
167
+ const targetDir = join(baseDir, targetName);
168
+ if (!isNonEmptyDirectory(targetDir)) return targetDir;
169
+ const rl = createInterface({
170
+ input: process.stdin,
171
+ output: process.stdout
172
+ });
173
+ const action = (await rl.question(`Target directory "${targetDir}" already exists and is not empty. Choose [o]verwrite, [r]ename, or [c]ancel: `)).trim().toLowerCase();
174
+ if (action === "o" || action === "overwrite") {
175
+ rmSync(targetDir, {
176
+ recursive: true,
177
+ force: true
178
+ });
179
+ rl.close();
180
+ return targetDir;
181
+ }
182
+ if (action === "r" || action === "rename") {
183
+ const nextName = (await rl.question("Enter a new directory name: ")).trim();
184
+ rl.close();
185
+ if (!nextName) {
186
+ console.error("Directory name cannot be empty.");
187
+ continue;
188
+ }
189
+ targetName = nextName;
190
+ continue;
191
+ }
192
+ rl.close();
193
+ return null;
194
+ }
195
+ }
196
+ async function installSkill(targetBaseDir) {
197
+ const sourceDir = resolveSkillSourceDir();
198
+ if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) throw new Error(`Skill source directory not found: ${sourceDir}`);
199
+ const targetDir = await chooseSkillDestination(targetBaseDir);
200
+ if (!targetDir) {
201
+ console.log("Skill installation canceled.");
202
+ return;
203
+ }
204
+ mkdirSync(targetDir, { recursive: true });
205
+ cpSync(sourceDir, targetDir, { recursive: true });
206
+ console.log(JSON.stringify({
207
+ success: true,
208
+ source: sourceDir,
209
+ target: targetDir
210
+ }, null, 2));
211
+ }
212
+ function sleep(ms) {
213
+ return new Promise((resolve) => {
214
+ setTimeout(resolve, ms);
215
+ });
216
+ }
217
+ async function requestService(servicePort, path, method = "GET", body) {
218
+ const response = await fetch(`http://127.0.0.1:${servicePort}${path}`, {
219
+ method,
220
+ headers: { "Content-Type": "application/json" },
221
+ body: body === void 0 ? void 0 : JSON.stringify(body)
222
+ });
223
+ const payload = await response.json();
224
+ if (!response.ok) throw new Error(payload.error || `Service request failed: ${method} ${path}`);
225
+ return payload;
226
+ }
227
+ async function isServiceHealthy(servicePort) {
228
+ try {
229
+ await requestService(servicePort, "/health", "GET");
230
+ return true;
231
+ } catch {
232
+ return false;
233
+ }
234
+ }
235
+ function getRequestedServicePort(flags) {
236
+ return getNumberFlag(flags, "servicePort") ?? DEFAULT_SERVICE_PORT;
237
+ }
238
+ async function ensureServiceRunning(flags) {
239
+ const requestedPort = getRequestedServicePort(flags);
240
+ if (await isServiceHealthy(requestedPort)) return {
241
+ servicePort: requestedPort,
242
+ newlyStarted: false
243
+ };
244
+ const existing = loadServiceSession();
245
+ if (existing && await isServiceHealthy(existing.servicePort)) return {
246
+ servicePort: existing.servicePort,
247
+ newlyStarted: false
248
+ };
249
+ const serviceScript = join(dirname(fileURLToPath(import.meta.url)), "service.js");
250
+ spawn(process.execPath, [
251
+ serviceScript,
252
+ "--service-port",
253
+ String(requestedPort)
254
+ ], {
255
+ stdio: "ignore",
256
+ detached: true
257
+ }).unref();
258
+ const deadline = Date.now() + 8e3;
259
+ while (Date.now() < deadline) {
260
+ if (await isServiceHealthy(requestedPort)) return {
261
+ servicePort: requestedPort,
262
+ newlyStarted: true
263
+ };
264
+ await sleep(200);
265
+ }
266
+ throw new Error("Failed to start background service.");
267
+ }
268
+ async function getRunningServicePort(flags) {
269
+ const requestedPort = getRequestedServicePort(flags);
270
+ if (await isServiceHealthy(requestedPort)) return requestedPort;
271
+ const existing = loadServiceSession();
272
+ if (existing && await isServiceHealthy(existing.servicePort)) return existing.servicePort;
273
+ throw new Error("Service is not running. Run \"browse-agent launch\" first.");
274
+ }
275
+ async function runServiceCommand(flags, name, payload = {}) {
276
+ const result = await requestService(await getRunningServicePort(flags), "/command", "POST", {
277
+ name,
278
+ payload
279
+ });
280
+ if (result !== void 0) console.log(JSON.stringify(result, null, 2));
281
+ }
282
+ const { command, positional, flags } = parseArgs(process.argv);
283
+ if (!command || flags.help || flags.h || command === "help") {
284
+ showHelp();
285
+ process.exit(0);
286
+ }
287
+ const browser = getStringFlag(flags, "browser");
288
+ const port = getStringFlag(flags, "port");
289
+ const timeout = getStringFlag(flags, "timeout");
290
+ if (browser) process.env.BROWSER = browser;
291
+ if (flags.headless) process.env.HEADLESS = "true";
292
+ if (port) process.env.BROWSE_AGENT_PORT = port;
293
+ if (timeout) process.env.CONNECTION_TIMEOUT = timeout;
294
+ try {
295
+ switch (command) {
296
+ case "setup":
297
+ await setup();
298
+ break;
299
+ case "launch": {
300
+ const service = await ensureServiceRunning(flags);
301
+ const servicePort = service.servicePort;
302
+ if (!service.newlyStarted) {
303
+ const status = await requestService(servicePort, "/status", "GET");
304
+ if (status.running === true) {
305
+ console.log(JSON.stringify({
306
+ servicePort,
307
+ skipped: "service-and-browser-already-running",
308
+ ...status
309
+ }, null, 2));
310
+ break;
311
+ }
312
+ }
313
+ const opts = {};
314
+ const launchBrowserFlag = getStringFlag(flags, "browser") || "chrome";
315
+ const launchPort = getNumberFlag(flags, "port");
316
+ const launchTimeout = getNumberFlag(flags, "timeout");
317
+ if (launchBrowserFlag) opts.browser = launchBrowserFlag;
318
+ if (flags.headless) opts.headless = true;
319
+ if (launchPort !== void 0) opts.port = launchPort;
320
+ if (launchTimeout !== void 0) opts.timeout = launchTimeout;
321
+ const result = await requestService(servicePort, "/launch", "POST", opts);
322
+ console.log(JSON.stringify({
323
+ servicePort,
324
+ ...result
325
+ }, null, 2));
326
+ break;
327
+ }
328
+ case "connect": {
329
+ const servicePort = await getRunningServicePort(flags);
330
+ const status = await requestService(servicePort, "/status", "GET");
331
+ console.log(JSON.stringify({
332
+ servicePort,
333
+ ...status
334
+ }, null, 2));
335
+ break;
336
+ }
337
+ case "close": {
338
+ const servicePort = await getRunningServicePort(flags);
339
+ const closeResult = await requestService(servicePort, "/close", "POST");
340
+ const shutdownResult = await requestService(servicePort, "/shutdown", "POST");
341
+ console.log(JSON.stringify({
342
+ servicePort,
343
+ ...closeResult,
344
+ ...shutdownResult
345
+ }, null, 2));
346
+ break;
347
+ }
348
+ case "clear":
349
+ try {
350
+ await requestService(await getRunningServicePort(flags), "/shutdown", "POST");
351
+ } catch {}
352
+ await clear();
353
+ break;
354
+ case "skill": {
355
+ const directory = positional[0];
356
+ if (!directory) {
357
+ console.error("Usage: browse-agent skill <directory>");
358
+ process.exit(1);
359
+ }
360
+ const targetBaseDir = directory.startsWith("~") ? join(process.env.HOME || "", directory.replace(/^~[\\/]?/, "")) : resolve(directory);
361
+ mkdirSync(targetBaseDir, { recursive: true });
362
+ if (!statSync(targetBaseDir).isDirectory()) throw new Error(`Target path is not a directory: ${targetBaseDir}`);
363
+ await installSkill(targetBaseDir);
364
+ break;
365
+ }
366
+ case "navigate": {
367
+ const url = positional[0];
368
+ if (!url) {
369
+ console.error("Usage: browse-agent navigate <url>");
370
+ process.exit(1);
371
+ }
372
+ await runServiceCommand(flags, "navigate", { url });
373
+ break;
374
+ }
375
+ case "get-content": {
376
+ const opts = {};
377
+ const format = getStringFlag(flags, "format");
378
+ const tabId = getNumberFlag(flags, "tabId");
379
+ if (format) opts.format = format;
380
+ if (tabId !== void 0) opts.tabId = tabId;
381
+ await runServiceCommand(flags, "get-content", { options: opts });
382
+ break;
383
+ }
384
+ case "get-dom": {
385
+ const selector = positional[0];
386
+ if (!selector) {
387
+ console.error("Usage: browse-agent get-dom <selector>");
388
+ process.exit(1);
389
+ }
390
+ const opts = {};
391
+ const property = getStringFlag(flags, "property");
392
+ const tabId = getNumberFlag(flags, "tabId");
393
+ if (property) opts.property = property;
394
+ if (flags.all) opts.all = true;
395
+ if (tabId !== void 0) opts.tabId = tabId;
396
+ await runServiceCommand(flags, "get-dom", {
397
+ selector,
398
+ options: opts
399
+ });
400
+ break;
401
+ }
402
+ case "evaluate": {
403
+ const expression = positional[0];
404
+ if (!expression) {
405
+ console.error("Usage: browse-agent evaluate <expression>");
406
+ process.exit(1);
407
+ }
408
+ const opts = {};
409
+ const tabId = getNumberFlag(flags, "tabId");
410
+ if (tabId !== void 0) opts.tabId = tabId;
411
+ await runServiceCommand(flags, "evaluate", {
412
+ expression,
413
+ options: opts
414
+ });
415
+ break;
416
+ }
417
+ case "inject-script": {
418
+ const code = positional[0];
419
+ if (!code) {
420
+ console.error("Usage: browse-agent inject-script <code>");
421
+ process.exit(1);
422
+ }
423
+ const opts = {};
424
+ const tabId = getNumberFlag(flags, "tabId");
425
+ if (tabId !== void 0) opts.tabId = tabId;
426
+ await runServiceCommand(flags, "inject-script", {
427
+ code,
428
+ options: opts
429
+ });
430
+ break;
431
+ }
432
+ case "inject-css": {
433
+ const code = positional[0];
434
+ if (!code) {
435
+ console.error("Usage: browse-agent inject-css <code>");
436
+ process.exit(1);
437
+ }
438
+ const opts = {};
439
+ const tabId = getNumberFlag(flags, "tabId");
440
+ if (tabId !== void 0) opts.tabId = tabId;
441
+ await runServiceCommand(flags, "inject-css", {
442
+ code,
443
+ options: opts
444
+ });
445
+ break;
446
+ }
447
+ case "screenshot": {
448
+ const mode = positional[0] || "visible";
449
+ const opts = {};
450
+ const format = getStringFlag(flags, "format");
451
+ const quality = getNumberFlag(flags, "quality");
452
+ const tabId = getNumberFlag(flags, "tabId");
453
+ if (format) opts.format = format;
454
+ if (quality !== void 0) opts.quality = quality;
455
+ if (tabId !== void 0) opts.tabId = tabId;
456
+ await runServiceCommand(flags, "screenshot", {
457
+ mode,
458
+ options: opts
459
+ });
460
+ break;
461
+ }
462
+ case "tabs": {
463
+ const action = positional[0] || "list";
464
+ const tabId = positional[1] ? Number(positional[1]) : void 0;
465
+ switch (action) {
466
+ case "list":
467
+ await runServiceCommand(flags, "tabs-list");
468
+ break;
469
+ case "close":
470
+ if (tabId === void 0) {
471
+ console.error("Usage: browse-agent tabs close <tabId>");
472
+ process.exit(1);
473
+ }
474
+ await runServiceCommand(flags, "tabs-close", { tabId });
475
+ break;
476
+ case "activate":
477
+ if (tabId === void 0) {
478
+ console.error("Usage: browse-agent tabs activate <tabId>");
479
+ process.exit(1);
480
+ }
481
+ await runServiceCommand(flags, "tabs-activate", { tabId });
482
+ break;
483
+ default:
484
+ console.error(`Unknown tabs action: ${action}. Use: list, close, activate`);
485
+ process.exit(1);
486
+ }
487
+ break;
488
+ }
489
+ default:
490
+ console.error(`Unknown command: ${command}\\nRun "browse-agent --help" for usage.`);
491
+ process.exit(1);
492
+ }
493
+ } catch (err) {
494
+ const message = err instanceof Error ? err.message : String(err);
495
+ console.error(`[browse-agent] Error: ${message}`);
496
+ process.exit(1);
497
+ }
498
+ //#endregion
499
+ export {};
500
+
501
+ //# sourceMappingURL=cli.js.map