agent-browser-loop 0.1.1 → 0.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.
@@ -16,15 +16,15 @@ agent-browser open <url> [options]
16
16
  | Flag | Description |
17
17
  |------|-------------|
18
18
  | `--headed` | Show browser window (default: headless) |
19
- | `--session <name>` | Named session for isolation |
20
- | `--viewport <WxH>` | Viewport size (default: 1280x720) |
19
+ | `--new, -n` | Create new session with auto-generated ID |
20
+ | `--session, -s <id>` | Target session (from `--new`) |
21
21
  | `--json` | Output as JSON |
22
22
 
23
23
  **Examples:**
24
24
  ```bash
25
25
  agent-browser open http://localhost:3000
26
26
  agent-browser open http://localhost:3000 --headed
27
- agent-browser open http://localhost:3000 --session test1 --viewport 1920x1080
27
+ agent-browser open --new http://localhost:3000 # Output: Session: swift-fox
28
28
  ```
29
29
 
30
30
  ---
@@ -40,7 +40,8 @@ agent-browser act <actions...> [options]
40
40
  **Options:**
41
41
  | Flag | Description |
42
42
  |------|-------------|
43
- | `--session <name>` | Target session |
43
+ | `--new, -n` | Create new session with auto-generated ID |
44
+ | `--session, -s <id>` | Target session (from `--new`) |
44
45
  | `--no-state` | Skip state in response |
45
46
  | `--json` | Output as JSON |
46
47
 
@@ -110,7 +111,7 @@ agent-browser wait [options]
110
111
  | `--not-text <string>` | Wait for text to disappear |
111
112
  | `--not-selector <css>` | Wait for element to disappear |
112
113
  | `--timeout <ms>` | Timeout in milliseconds (default: 30000) |
113
- | `--session <name>` | Target session |
114
+ | `--session, -s <id>` | Target session (from `--new`) |
114
115
  | `--json` | Output as JSON |
115
116
 
116
117
  **Examples:**
@@ -148,7 +149,7 @@ agent-browser state [options]
148
149
  **Options:**
149
150
  | Flag | Description |
150
151
  |------|-------------|
151
- | `--session <name>` | Target session |
152
+ | `--session, -s <id>` | Target session (from `--new`) |
152
153
  | `--json` | Output as JSON |
153
154
 
154
155
  **Output includes:**
@@ -196,7 +197,7 @@ agent-browser screenshot [options]
196
197
  |------|-------------|
197
198
  | `--output, -o <path>` | Save to file (PNG) instead of base64 output |
198
199
  | `--full-page` | Capture full scrollable page |
199
- | `--session <name>` | Target session |
200
+ | `--session, -s <id>` | Target session (from `--new`) |
200
201
 
201
202
  **Examples:**
202
203
  ```bash
@@ -214,7 +215,7 @@ agent-browser screenshot
214
215
 
215
216
  ### `close`
216
217
 
217
- Close browser and stop daemon.
218
+ Close browser session or stop daemon.
218
219
 
219
220
  ```bash
220
221
  agent-browser close [options]
@@ -223,13 +224,24 @@ agent-browser close [options]
223
224
  **Options:**
224
225
  | Flag | Description |
225
226
  |------|-------------|
226
- | `--session <name>` | Close specific session only |
227
+ | `--session, -s <id>` | Close specific session (daemon keeps running) |
228
+ | `--all` | Close all sessions and stop daemon |
229
+
230
+ ---
231
+
232
+ ### `sessions`
233
+
234
+ List all active browser sessions.
235
+
236
+ ```bash
237
+ agent-browser sessions [--json]
238
+ ```
227
239
 
228
240
  ---
229
241
 
230
242
  ### `status`
231
243
 
232
- Check if daemon is running.
244
+ Check if daemon is running and list active sessions.
233
245
 
234
246
  ```bash
235
247
  agent-browser status
@@ -304,7 +316,7 @@ These options work with most commands:
304
316
 
305
317
  | Flag | Description |
306
318
  |------|-------------|
307
- | `--session <name>` | Named session for isolation |
319
+ | `--session, -s <id>` | Target session ID (from `--new`) |
308
320
  | `--json` | JSON output format |
309
321
  | `--help` | Show help |
310
322
 
@@ -360,15 +372,20 @@ agent-browser close
360
372
 
361
373
  ### Multiple Sessions
362
374
  ```bash
363
- # Session A: Admin user
364
- agent-browser open http://localhost:3000/login --session admin
365
- agent-browser act type:input_0:admin@test.com --session admin
375
+ # Create sessions with auto-generated IDs
376
+ agent-browser open --new http://localhost:3000/login # Output: Session: swift-fox
377
+ agent-browser open --new http://localhost:3000/login # Output: Session: calm-river
378
+
379
+ # Interact with specific sessions
380
+ agent-browser act -s swift-fox type:input_0:admin@test.com
381
+ agent-browser act -s calm-river type:input_0:user@test.com
382
+
383
+ # List all sessions
384
+ agent-browser sessions
366
385
 
367
- # Session B: Regular user
368
- agent-browser open http://localhost:3000/login --session user
369
- agent-browser act type:input_0:user@test.com --session user
386
+ # Close specific session (daemon keeps running)
387
+ agent-browser close -s swift-fox
370
388
 
371
- # Close both
372
- agent-browser close --session admin
373
- agent-browser close --session user
389
+ # Close all sessions and stop daemon
390
+ agent-browser close --all
374
391
  ```
@@ -165,10 +165,6 @@ agent-browser close
165
165
  # Headed mode (visible browser)
166
166
  agent-browser open http://localhost:3000 --headed
167
167
 
168
- # Named session
169
- agent-browser open http://localhost:3000 --session my-test
170
- agent-browser act click:button_0 --session my-test
171
-
172
168
  # JSON output
173
169
  agent-browser state --json
174
170
 
@@ -176,6 +172,20 @@ agent-browser state --json
176
172
  agent-browser act click:button_0 --no-state
177
173
  ```
178
174
 
175
+ ## Multi-Session
176
+
177
+ Run multiple browsers in parallel with `--new`:
178
+
179
+ ```bash
180
+ agent-browser open --new http://localhost:3000 # Output: Session: swift-fox
181
+ agent-browser open --new http://localhost:3000 # Output: Session: calm-river
182
+
183
+ agent-browser act -s swift-fox click:button_0 # Target session
184
+ agent-browser sessions # List all
185
+ agent-browser close -s swift-fox # Close one session
186
+ agent-browser close --all # Close all, stop daemon
187
+ ```
188
+
179
189
  ## Screenshots
180
190
 
181
191
  ```bash
package/README.md CHANGED
@@ -66,7 +66,8 @@ Every command returns the current page state - interactive elements, form values
66
66
  | `wait` | Wait for condition |
67
67
  | `state` | Get current page state |
68
68
  | `screenshot` | Capture screenshot |
69
- | `close` | Close browser and daemon |
69
+ | `sessions` | List all active sessions |
70
+ | `close` | Close session or daemon |
70
71
  | `setup` | Install browser + skill files |
71
72
 
72
73
  ### Actions
@@ -95,11 +96,29 @@ agent-browser wait --timeout 60000 # Custom timeout
95
96
 
96
97
  ```bash
97
98
  --headed # Show browser window
98
- --session <name> # Named session
99
+ --new # Create new session with auto-generated ID
100
+ --session <id> # Target specific session (from --new)
99
101
  --json # JSON output
100
102
  --no-state # Skip state in response
101
103
  ```
102
104
 
105
+ ## Multi-Session
106
+
107
+ Run multiple browser sessions in parallel:
108
+
109
+ ```bash
110
+ # Create sessions with auto-generated IDs
111
+ agent-browser open --new http://localhost:3000 # Output: Session: swift-fox
112
+ agent-browser open --new http://localhost:3000 # Output: Session: calm-river
113
+
114
+ # Target specific sessions
115
+ agent-browser act -s swift-fox click:button_0
116
+ agent-browser state -s calm-river
117
+
118
+ # List all sessions
119
+ agent-browser sessions
120
+ ```
121
+
103
122
  ## State Output
104
123
 
105
124
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-browser-loop",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Let your AI coding agent drive a browser to verify its own work",
5
5
  "license": "MIT",
6
6
  "author": "Jason Silberman",
package/src/cli.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  cleanupDaemonFiles,
21
21
  DaemonClient,
22
22
  ensureDaemon,
23
+ ensureDaemonNewSession,
23
24
  isDaemonRunning,
24
25
  } from "./daemon";
25
26
  import { log, withLog } from "./log";
@@ -83,12 +84,17 @@ async function loadConfig(configPath: string): Promise<BrowserCliConfig> {
83
84
  // Shared CLI Options
84
85
  // ============================================================================
85
86
 
87
+ const newSessionFlag = flag({
88
+ long: "new",
89
+ short: "n",
90
+ description: "Create a new session with auto-generated ID",
91
+ });
92
+
86
93
  const sessionOption = option({
87
94
  long: "session",
88
95
  short: "s",
89
- type: string,
90
- defaultValue: () => "default",
91
- description: "Session name (default: default)",
96
+ type: optional(string),
97
+ description: "Session ID (from --new)",
92
98
  });
93
99
 
94
100
  const headlessFlag = flag({
@@ -308,6 +314,7 @@ const openCommand = command({
308
314
  args: {
309
315
  url: positional({ type: string, displayName: "url" }),
310
316
  session: sessionOption,
317
+ new: newSessionFlag,
311
318
  headless: headlessFlag,
312
319
  headed: headedFlag,
313
320
  config: configOption,
@@ -315,7 +322,17 @@ const openCommand = command({
315
322
  },
316
323
  handler: async (args) => {
317
324
  const browserOptions = await resolveBrowserOptions(args);
318
- const client = await ensureDaemon(args.session, browserOptions);
325
+
326
+ let client: DaemonClient;
327
+ if (args.new) {
328
+ client = await ensureDaemonNewSession(browserOptions);
329
+ } else if (args.session) {
330
+ client = await ensureDaemon(args.session, browserOptions, {
331
+ createIfMissing: false,
332
+ });
333
+ } else {
334
+ client = await ensureDaemon("default", browserOptions);
335
+ }
319
336
 
320
337
  const response = await client.act([{ type: "navigate", url: args.url }]);
321
338
 
@@ -325,9 +342,18 @@ const openCommand = command({
325
342
  }
326
343
 
327
344
  const data = response.data as { text?: string };
345
+ const sessionId = client.getSessionId();
346
+
328
347
  if (args.json) {
329
- console.log(JSON.stringify(response.data, null, 2));
348
+ const jsonData =
349
+ typeof response.data === "object" && response.data !== null
350
+ ? { ...(response.data as object), sessionId }
351
+ : { data: response.data, sessionId };
352
+ console.log(JSON.stringify(jsonData, null, 2));
330
353
  } else {
354
+ if (args.new && sessionId) {
355
+ console.log(`Session: ${sessionId}`);
356
+ }
331
357
  console.log(data.text ?? "Navigated successfully");
332
358
  }
333
359
  },
@@ -341,6 +367,7 @@ const actCommand = command({
341
367
  args: {
342
368
  actions: restPositionals({ type: string, displayName: "actions" }),
343
369
  session: sessionOption,
370
+ new: newSessionFlag,
344
371
  headless: headlessFlag,
345
372
  headed: headedFlag,
346
373
  config: configOption,
@@ -360,7 +387,17 @@ const actCommand = command({
360
387
  }
361
388
 
362
389
  const browserOptions = await resolveBrowserOptions(args);
363
- const client = await ensureDaemon(args.session, browserOptions);
390
+
391
+ let client: DaemonClient;
392
+ if (args.new) {
393
+ client = await ensureDaemonNewSession(browserOptions);
394
+ } else if (args.session) {
395
+ client = await ensureDaemon(args.session, browserOptions, {
396
+ createIfMissing: false,
397
+ });
398
+ } else {
399
+ client = await ensureDaemon("default", browserOptions);
400
+ }
364
401
 
365
402
  const actions = args.actions.map(parseAction);
366
403
  const response = await client.act(actions, {
@@ -376,6 +413,9 @@ const actCommand = command({
376
413
  if (args.json) {
377
414
  console.log(JSON.stringify(response.data, null, 2));
378
415
  } else {
416
+ if (args.new) {
417
+ console.log(`Session: ${client.getSessionId()}`);
418
+ }
379
419
  console.log(data.text ?? "Actions completed");
380
420
  }
381
421
 
@@ -558,26 +598,95 @@ const screenshotCommand = command({
558
598
  // --- close ---
559
599
  const closeCommand = command({
560
600
  name: "close",
561
- description: "Close the browser and stop the daemon",
601
+ description: "Close the browser session or shutdown daemon",
562
602
  args: {
563
603
  session: sessionOption,
604
+ all: flag({
605
+ long: "all",
606
+ description: "Close all sessions and shutdown daemon",
607
+ }),
564
608
  },
565
609
  handler: async (args) => {
566
610
  const client = new DaemonClient(args.session);
567
611
 
568
612
  if (!(await client.ping())) {
569
613
  console.log("Daemon not running.");
570
- cleanupDaemonFiles(args.session);
614
+ cleanupDaemonFiles();
571
615
  return;
572
616
  }
573
617
 
574
618
  try {
575
- await client.shutdown();
576
- console.log("Browser closed.");
619
+ if (args.all) {
620
+ await client.shutdown();
621
+ console.log("All sessions closed. Daemon stopped.");
622
+ } else {
623
+ const sessionId = args.session ?? "default";
624
+ const response = await client.closeSession(sessionId);
625
+ if (response.success) {
626
+ console.log(`Session "${sessionId}" closed.`);
627
+ } else {
628
+ console.error("Error:", response.error);
629
+ process.exit(1);
630
+ }
631
+ }
577
632
  } catch {
578
- // Daemon may have already exited
579
- cleanupDaemonFiles(args.session);
580
- console.log("Browser closed.");
633
+ cleanupDaemonFiles();
634
+ console.log("Session closed.");
635
+ }
636
+ },
637
+ });
638
+
639
+ // --- sessions ---
640
+ const sessionsCommand = command({
641
+ name: "sessions",
642
+ description: "List all active browser sessions",
643
+ args: {
644
+ json: jsonFlag,
645
+ },
646
+ handler: async (args) => {
647
+ const client = new DaemonClient();
648
+
649
+ if (!(await client.ping())) {
650
+ if (args.json) {
651
+ console.log(JSON.stringify({ sessions: [] }, null, 2));
652
+ } else {
653
+ console.log("Daemon not running. No active sessions.");
654
+ }
655
+ return;
656
+ }
657
+
658
+ const response = await client.list();
659
+
660
+ if (!response.success) {
661
+ console.error("Error:", response.error);
662
+ process.exit(1);
663
+ }
664
+
665
+ const data = response.data as {
666
+ sessions: Array<{
667
+ id: string;
668
+ url: string;
669
+ title: string;
670
+ busy: boolean;
671
+ lastUsed: number;
672
+ }>;
673
+ };
674
+
675
+ if (args.json) {
676
+ console.log(JSON.stringify(data, null, 2));
677
+ } else {
678
+ if (data.sessions.length === 0) {
679
+ console.log("No active sessions.");
680
+ } else {
681
+ console.log(`Sessions (${data.sessions.length}):\n`);
682
+ for (const s of data.sessions) {
683
+ const status = s.busy ? "[busy]" : "[idle]";
684
+ console.log(` ${s.id} ${status}`);
685
+ console.log(` ${s.title || "(no title)"}`);
686
+ console.log(` ${s.url}`);
687
+ console.log();
688
+ }
689
+ }
581
690
  }
582
691
  },
583
692
  });
@@ -586,14 +695,40 @@ const closeCommand = command({
586
695
  const statusCommand = command({
587
696
  name: "status",
588
697
  description: "Check if daemon is running",
589
- args: {
590
- session: sessionOption,
591
- },
592
- handler: async (args) => {
593
- const running = isDaemonRunning(args.session);
594
- console.log(
595
- `Session "${args.session}": ${running ? "running" : "not running"}`,
596
- );
698
+ args: {},
699
+ handler: async () => {
700
+ const running = isDaemonRunning();
701
+ if (running) {
702
+ console.log("Daemon is running.");
703
+ // Try to list sessions
704
+ const client = new DaemonClient();
705
+ if (await client.ping()) {
706
+ const response = await client.list();
707
+ if (response.success) {
708
+ const data = response.data as {
709
+ sessions: Array<{
710
+ id: string;
711
+ url: string;
712
+ title: string;
713
+ busy: boolean;
714
+ }>;
715
+ };
716
+ if (data.sessions.length === 0) {
717
+ console.log("No active sessions.");
718
+ } else {
719
+ console.log(`\nSessions (${data.sessions.length}):`);
720
+ for (const s of data.sessions) {
721
+ const status = s.busy ? "[busy]" : "[idle]";
722
+ console.log(` ${s.id} ${status}`);
723
+ console.log(` ${s.title || "(no title)"}`);
724
+ console.log(` ${s.url}`);
725
+ }
726
+ }
727
+ }
728
+ }
729
+ } else {
730
+ console.log("Daemon is not running.");
731
+ }
597
732
  },
598
733
  });
599
734
 
@@ -773,6 +908,7 @@ const cli = subcommands({
773
908
  state: stateCommand,
774
909
  screenshot: screenshotCommand,
775
910
  close: closeCommand,
911
+ sessions: sessionsCommand,
776
912
  status: statusCommand,
777
913
 
778
914
  // Setup & configuration