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.
- package/.claude/skills/agent-browser-loop/REFERENCE.md +37 -20
- package/.claude/skills/agent-browser-loop/SKILL.md +14 -4
- package/README.md +21 -2
- package/package.json +1 -1
- package/src/cli.ts +157 -21
- package/src/daemon.ts +482 -146
|
@@ -16,15 +16,15 @@ agent-browser open <url> [options]
|
|
|
16
16
|
| Flag | Description |
|
|
17
17
|
|------|-------------|
|
|
18
18
|
| `--headed` | Show browser window (default: headless) |
|
|
19
|
-
| `--
|
|
20
|
-
| `--
|
|
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
|
|
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
|
-
| `--
|
|
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 <
|
|
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 <
|
|
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 <
|
|
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
|
|
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 <
|
|
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 <
|
|
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
|
-
#
|
|
364
|
-
agent-browser open http://localhost:3000/login
|
|
365
|
-
agent-browser
|
|
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
|
-
#
|
|
368
|
-
agent-browser
|
|
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
|
|
372
|
-
agent-browser close --
|
|
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
|
-
| `
|
|
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
|
|
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
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
614
|
+
cleanupDaemonFiles();
|
|
571
615
|
return;
|
|
572
616
|
}
|
|
573
617
|
|
|
574
618
|
try {
|
|
575
|
-
|
|
576
|
-
|
|
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
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
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
|