tab-agent 0.2.2 → 0.3.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/README.md CHANGED
@@ -51,7 +51,7 @@ npx tab-agent setup
51
51
  Because Tab Agent runs as a Chrome extension:
52
52
 
53
53
  - **Uses your existing cookies** — no re-authentication needed
54
- - **Access any site you're logged into** — GitHub, Twitter, Gmail, internal tools
54
+ - **Access any site you're logged into** — GitHub, X, Gmail, internal tools
55
55
  - **Works with SSO and 2FA** — enterprise apps, protected accounts
56
56
  - **No credential sharing** — your passwords stay in your browser
57
57
 
@@ -75,7 +75,7 @@ Because Tab Agent runs as a Chrome extension:
75
75
  > "Fill out this contact form with my details"
76
76
 
77
77
  **Data Extraction**
78
- > "Get the last 20 tweets from my timeline with author names"
78
+ > "Get the last 20 posts from my X timeline with author names"
79
79
 
80
80
  **Multi-step Workflows**
81
81
  > "Search Amazon for 'mechanical keyboard', filter by 4+ stars, and list the top 3"
@@ -145,6 +145,25 @@ This automatically:
145
145
  | `evaluate` | Run JavaScript in page context |
146
146
  | `dialog` | Handle alert/confirm/prompt |
147
147
 
148
+ ## CLI Usage
149
+
150
+ Run commands directly from terminal:
151
+
152
+ ```bash
153
+ npx tab-agent tabs # List active tabs
154
+ npx tab-agent snapshot # Get page content
155
+ npx tab-agent screenshot # Capture viewport
156
+ npx tab-agent screenshot --full # Capture full page
157
+ npx tab-agent click e5 # Click element
158
+ npx tab-agent type e3 "hello" # Type text
159
+ npx tab-agent fill e3 "value" # Fill field
160
+ npx tab-agent press Enter # Press key
161
+ npx tab-agent scroll down 500 # Scroll
162
+ npx tab-agent navigate "https://..." # Go to URL
163
+ npx tab-agent wait "Loading" # Wait for text
164
+ npx tab-agent evaluate "document.title" # Run JS
165
+ ```
166
+
148
167
  ---
149
168
 
150
169
  ## CLI Reference
package/bin/tab-agent.js CHANGED
@@ -1,40 +1,64 @@
1
1
  #!/usr/bin/env node
2
2
  const command = process.argv[2];
3
- const pkg = require('../package.json');
3
+
4
+ // Commands that go to the command module
5
+ const BROWSER_COMMANDS = ['tabs', 'snapshot', 'screenshot', 'click', 'type', 'fill', 'press', 'scroll', 'navigate', 'wait', 'evaluate'];
6
+
7
+ if (command === '-v' || command === '--version') {
8
+ console.log(require('../package.json').version);
9
+ process.exit(0);
10
+ }
11
+
12
+ if (BROWSER_COMMANDS.includes(command)) {
13
+ const { runCommand } = require('../cli/command.js');
14
+ runCommand(process.argv.slice(2));
15
+ } else {
16
+ switch (command) {
17
+ case 'setup':
18
+ require('../cli/setup.js');
19
+ break;
20
+ case 'start':
21
+ require('../cli/start.js');
22
+ break;
23
+ case 'status':
24
+ require('../cli/status.js');
25
+ break;
26
+ default:
27
+ showHelp();
28
+ }
29
+ }
4
30
 
5
31
  function showHelp() {
6
32
  console.log(`
7
33
  tab-agent - Browser control for Claude/Codex
8
34
 
9
- Commands:
10
- setup Auto-detect extension, register native host, install skills
11
- start Start the relay server
12
- status Check configuration status
35
+ Setup Commands:
36
+ setup Auto-detect extension, register native host, install skills
37
+ start Start the relay server
38
+ status Check configuration status
13
39
 
14
- Usage:
40
+ Browser Commands:
41
+ tabs List active tabs
42
+ snapshot Get AI-readable page content
43
+ screenshot [--full] Capture screenshot
44
+ click <ref> Click element (e.g., click e5)
45
+ type <ref> <text> Type text into element
46
+ fill <ref> <value> Fill form field
47
+ press <key> Press key (Enter, Escape, etc.)
48
+ scroll <dir> [amount] Scroll up/down
49
+ navigate <url> Go to URL
50
+ wait <text|selector> Wait for text or element
51
+ evaluate <script> Run JavaScript
52
+
53
+ Examples:
15
54
  npx tab-agent setup
16
- npx tab-agent start
17
- `);
18
- }
55
+ npx tab-agent snapshot
56
+ npx tab-agent click e5
57
+ npx tab-agent type e3 "hello world"
19
58
 
20
- switch (command) {
21
- case 'setup':
22
- require('../cli/setup.js');
23
- break;
24
- case 'start':
25
- require('../cli/start.js');
26
- break;
27
- case 'status':
28
- require('../cli/status.js');
29
- break;
30
- case '-v':
31
- case '--version':
32
- console.log(pkg.version);
33
- break;
34
- case undefined:
35
- showHelp();
36
- break;
37
- default:
38
- showHelp();
59
+ Version: ${require('../package.json').version}
60
+ `);
61
+ if (command && command !== 'help' && command !== '--help' && command !== '-h') {
39
62
  process.exit(1);
63
+ }
40
64
  }
package/cli/command.js ADDED
@@ -0,0 +1,208 @@
1
+ // cli/command.js
2
+ const WebSocket = require('ws');
3
+
4
+ const COMMANDS = ['tabs', 'snapshot', 'screenshot', 'click', 'type', 'fill', 'press', 'scroll', 'navigate', 'wait', 'evaluate'];
5
+
6
+ async function runCommand(args) {
7
+ const [command, ...params] = args;
8
+
9
+ if (!command || command === 'help') {
10
+ printHelp();
11
+ return;
12
+ }
13
+
14
+ if (!COMMANDS.includes(command)) {
15
+ console.error(`Unknown command: ${command}`);
16
+ console.error(`Available: ${COMMANDS.join(', ')}`);
17
+ process.exit(1);
18
+ }
19
+
20
+ const ws = new WebSocket('ws://localhost:9876');
21
+
22
+ const timeout = setTimeout(() => {
23
+ console.error('Connection timeout - is the relay running? Try: npx tab-agent start');
24
+ ws.close();
25
+ process.exit(1);
26
+ }, 5000);
27
+
28
+ ws.on('error', (err) => {
29
+ clearTimeout(timeout);
30
+ console.error('Connection failed:', err.message);
31
+ console.error('Make sure relay is running: npx tab-agent start');
32
+ process.exit(1);
33
+ });
34
+
35
+ ws.on('open', () => {
36
+ clearTimeout(timeout);
37
+
38
+ // First get tabs to find tabId
39
+ if (command === 'tabs') {
40
+ ws.send(JSON.stringify({ id: 1, action: 'tabs' }));
41
+ } else {
42
+ // Get active tab first, then run command
43
+ ws.send(JSON.stringify({ id: 0, action: 'tabs' }));
44
+ }
45
+ });
46
+
47
+ ws.on('message', (data) => {
48
+ const msg = JSON.parse(data);
49
+
50
+ // Handle tabs response
51
+ if (msg.id === 0) {
52
+ if (!msg.tabs || msg.tabs.length === 0) {
53
+ console.error('No active tabs. Click Tab Agent icon on a tab to activate it.');
54
+ ws.close();
55
+ process.exit(1);
56
+ }
57
+
58
+ const tabId = msg.tabs[0].tabId;
59
+ const payload = buildPayload(command, params, tabId);
60
+ ws.send(JSON.stringify({ id: 1, ...payload }));
61
+ return;
62
+ }
63
+
64
+ // Handle command response
65
+ if (msg.id === 1) {
66
+ if (command === 'tabs') {
67
+ printTabs(msg);
68
+ } else if (command === 'snapshot') {
69
+ printSnapshot(msg);
70
+ } else if (command === 'screenshot') {
71
+ printScreenshot(msg);
72
+ } else {
73
+ printResult(msg);
74
+ }
75
+ ws.close();
76
+ process.exit(msg.ok ? 0 : 1);
77
+ }
78
+ });
79
+ }
80
+
81
+ function buildPayload(command, params, tabId) {
82
+ const payload = { action: command, tabId };
83
+
84
+ switch (command) {
85
+ case 'click':
86
+ payload.ref = params[0];
87
+ break;
88
+ case 'type':
89
+ payload.ref = params[0];
90
+ payload.text = params.slice(1).join(' ');
91
+ if (params.includes('--submit')) {
92
+ payload.submit = true;
93
+ payload.text = payload.text.replace('--submit', '').trim();
94
+ }
95
+ break;
96
+ case 'fill':
97
+ payload.ref = params[0];
98
+ payload.value = params.slice(1).join(' ');
99
+ break;
100
+ case 'press':
101
+ payload.key = params[0];
102
+ break;
103
+ case 'scroll':
104
+ payload.direction = params[0] || 'down';
105
+ payload.amount = parseInt(params[1]) || 500;
106
+ break;
107
+ case 'navigate':
108
+ payload.url = params[0];
109
+ break;
110
+ case 'wait':
111
+ if (params[0]?.startsWith('.') || params[0]?.startsWith('#')) {
112
+ payload.selector = params[0];
113
+ } else {
114
+ payload.text = params.join(' ');
115
+ }
116
+ payload.timeout = parseInt(params.find(p => /^\d+$/.test(p))) || 5000;
117
+ break;
118
+ case 'evaluate':
119
+ payload.script = params.join(' ');
120
+ break;
121
+ case 'screenshot':
122
+ if (params.includes('--full') || params.includes('--fullPage')) {
123
+ payload.fullPage = true;
124
+ }
125
+ break;
126
+ }
127
+
128
+ return payload;
129
+ }
130
+
131
+ function printHelp() {
132
+ console.log(`
133
+ tab-agent - Browser control commands
134
+
135
+ Usage: npx tab-agent <command> [options]
136
+
137
+ Commands:
138
+ tabs List active tabs
139
+ snapshot Get AI-readable page content
140
+ screenshot [--full] Capture screenshot (--full for full page)
141
+ click <ref> Click element (e.g., click e5)
142
+ type <ref> <text> Type text into element
143
+ fill <ref> <value> Fill form field
144
+ press <key> Press key (Enter, Escape, Tab, etc.)
145
+ scroll <dir> [amount] Scroll up/down (default: 500px)
146
+ navigate <url> Go to URL
147
+ wait <text|selector> Wait for text or element
148
+ evaluate <script> Run JavaScript
149
+
150
+ Examples:
151
+ npx tab-agent tabs
152
+ npx tab-agent snapshot
153
+ npx tab-agent click e5
154
+ npx tab-agent type e3 hello world
155
+ npx tab-agent navigate https://google.com
156
+ npx tab-agent screenshot --full
157
+ `);
158
+ }
159
+
160
+ function printTabs(msg) {
161
+ if (!msg.ok) {
162
+ console.error('Error:', msg.error);
163
+ return;
164
+ }
165
+ console.log('Active tabs:\n');
166
+ msg.tabs.forEach((tab, i) => {
167
+ console.log(` ${i + 1}. [${tab.tabId}] ${tab.title}`);
168
+ console.log(` ${tab.url}\n`);
169
+ });
170
+ }
171
+
172
+ function printSnapshot(msg) {
173
+ if (!msg.ok) {
174
+ console.error('Error:', msg.error);
175
+ return;
176
+ }
177
+ console.log(msg.snapshot);
178
+ }
179
+
180
+ function printScreenshot(msg) {
181
+ if (!msg.ok) {
182
+ console.error('Error:', msg.error);
183
+ return;
184
+ }
185
+ // Save to file
186
+ const fs = require('fs');
187
+ const filename = `/tmp/tab-agent-screenshot-${Date.now()}.png`;
188
+ const base64Data = msg.screenshot.replace(/^data:image\/png;base64,/, '');
189
+ fs.writeFileSync(filename, base64Data, 'base64');
190
+ console.log(`Screenshot saved: ${filename}`);
191
+
192
+ // Try to open it
193
+ const { exec } = require('child_process');
194
+ exec(`open "${filename}"`, () => {});
195
+ }
196
+
197
+ function printResult(msg) {
198
+ if (!msg.ok) {
199
+ console.error('Error:', msg.error);
200
+ return;
201
+ }
202
+ console.log('OK');
203
+ if (msg.result !== undefined) {
204
+ console.log('Result:', msg.result);
205
+ }
206
+ }
207
+
208
+ module.exports = { runCommand };
@@ -191,32 +191,23 @@ async function routeCommand(tabId, command) {
191
191
  // Full page screenshot using chrome.debugger
192
192
  if (fullPage) {
193
193
  try {
194
- await chrome.debugger.attach({ tabId }, '1.3');
194
+ // Try to detach first in case previous attempt left debugger attached
195
+ try { await chrome.debugger.detach({ tabId }); } catch {}
195
196
 
196
- const { result: layout } = await chrome.debugger.sendCommand(
197
- { tabId },
198
- 'Page.getLayoutMetrics'
199
- );
197
+ await chrome.debugger.attach({ tabId }, '1.3');
200
198
 
201
- const { data } = await chrome.debugger.sendCommand(
199
+ const screenshot = await chrome.debugger.sendCommand(
202
200
  { tabId },
203
201
  'Page.captureScreenshot',
204
202
  {
205
203
  format: 'png',
206
- captureBeyondViewport: true,
207
- clip: {
208
- x: 0,
209
- y: 0,
210
- width: layout.contentSize.width,
211
- height: layout.contentSize.height,
212
- scale: 1
213
- }
204
+ captureBeyondViewport: true
214
205
  }
215
206
  );
216
207
 
217
208
  await chrome.debugger.detach({ tabId });
218
209
  audit('screenshot', { tabId, fullPage: true }, { ok: true });
219
- return { ok: true, screenshot: 'data:image/png;base64,' + data, format: 'png' };
210
+ return { ok: true, screenshot: 'data:image/png;base64,' + screenshot.data, format: 'png' };
220
211
  } catch (error) {
221
212
  try { await chrome.debugger.detach({ tabId }); } catch {}
222
213
  const result = { ok: false, error: error.message };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tab-agent",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Browser control for Claude Code and Codex via WebSocket",
5
5
  "bin": {
6
6
  "tab-agent": "./bin/tab-agent.js"
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: tab-agent
3
- description: Browser control via WebSocket - snapshot, click, type, fill, evaluate, screenshot
3
+ description: Browser control via CLI - snapshot, click, type, fill, screenshot
4
4
  ---
5
5
 
6
6
  # Tab Agent
7
7
 
8
- WebSocket `ws://localhost:9876`. User activates tabs via extension icon (green = active).
8
+ Control browser tabs via CLI. User activates tabs via extension icon (green = active).
9
9
 
10
10
  ## Before First Command
11
11
 
@@ -16,38 +16,44 @@ sleep 2
16
16
 
17
17
  ## Commands
18
18
 
19
- ```json
20
- {"id": 1, "action": "tabs"} // list active tabs
21
- {"id": 2, "action": "snapshot", "tabId": ID} // get page with refs [e1], [e2]...
22
- {"id": 3, "action": "screenshot", "tabId": ID} // viewport screenshot
23
- {"id": 4, "action": "screenshot", "tabId": ID, "fullPage": true} // full page screenshot
24
- {"id": 5, "action": "click", "tabId": ID, "ref": "e1"} // click element
25
- {"id": 6, "action": "fill", "tabId": ID, "ref": "e1", "value": "text"} // fill input
26
- {"id": 7, "action": "type", "tabId": ID, "ref": "e1", "text": "hello"} // type text
27
- {"id": 8, "action": "type", "tabId": ID, "ref": "e1", "text": "query", "submit": true} // type and Enter
28
- {"id": 9, "action": "press", "tabId": ID, "key": "Enter"} // press key
29
- {"id": 10, "action": "scroll", "tabId": ID, "direction": "down", "amount": 500}
30
- {"id": 11, "action": "scrollintoview", "tabId": ID, "ref": "e1"} // scroll element visible
31
- {"id": 12, "action": "navigate", "tabId": ID, "url": "https://..."}
32
- {"id": 13, "action": "wait", "tabId": ID, "text": "Loading complete"} // wait for text
33
- {"id": 14, "action": "wait", "tabId": ID, "selector": ".results", "timeout": 5000} // wait for element
34
- {"id": 15, "action": "evaluate", "tabId": ID, "script": "document.title"} // run JavaScript
35
- {"id": 16, "action": "batchfill", "tabId": ID, "fields": [{"ref": "e1", "value": "a"}, {"ref": "e2", "value": "b"}]}
36
- {"id": 17, "action": "dialog", "tabId": ID, "accept": true} // handle alert/confirm
19
+ ```bash
20
+ npx tab-agent tabs # List active tabs
21
+ npx tab-agent snapshot # Get page with refs [e1], [e2]...
22
+ npx tab-agent screenshot # Capture viewport
23
+ npx tab-agent screenshot --full # Capture full page
24
+ npx tab-agent click <ref> # Click element
25
+ npx tab-agent type <ref> <text> # Type text
26
+ npx tab-agent fill <ref> <value> # Fill form field
27
+ npx tab-agent press <key> # Press key (Enter, Escape, Tab)
28
+ npx tab-agent scroll <dir> [amount] # Scroll up/down
29
+ npx tab-agent navigate <url> # Go to URL
30
+ npx tab-agent wait <text|selector> # Wait for condition
31
+ npx tab-agent evaluate <script> # Run JavaScript
37
32
  ```
38
33
 
39
34
  ## Usage
40
35
 
41
- 1. `tabs` -> get tabId of active tab
36
+ 1. `tabs` -> find active tab
42
37
  2. `snapshot` -> read page, get element refs [e1], [e2]...
43
- 3. `click`/`fill`/`type` using refs
44
- 4. If snapshot incomplete (complex page) -> `screenshot` and analyze visually
38
+ 3. `click`/`type`/`fill` using refs
39
+ 4. If snapshot incomplete -> `screenshot` and analyze visually
40
+
41
+ ## Examples
42
+
43
+ ```bash
44
+ # Search Google
45
+ npx tab-agent navigate "https://google.com"
46
+ npx tab-agent snapshot
47
+ npx tab-agent type e1 "hello world"
48
+ npx tab-agent press Enter
49
+
50
+ # Read page content
51
+ npx tab-agent snapshot
52
+ npx tab-agent screenshot --full
53
+ ```
45
54
 
46
55
  ## Notes
47
56
 
48
- - Screenshot returns `{"screenshot": "data:image/png;base64,..."}` - analyze directly
49
- - Snapshot refs reset on each call - always snapshot before interacting
57
+ - Screenshot saves to /tmp/ and opens automatically
58
+ - Refs reset on each snapshot - always snapshot before interacting
50
59
  - Keys: Enter, Escape, Tab, Backspace, ArrowUp/Down/Left/Right
51
- - `type` with `submit: true` presses Enter after typing (for search boxes)
52
- - `evaluate` runs in page context - can access page variables/functions
53
- - `dialog` handles alert/confirm/prompt - debugger bar appears when attached
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: tab-agent
3
- description: Browser control via WebSocket
3
+ description: Browser control via CLI
4
4
  ---
5
5
 
6
6
  # Tab Agent
7
7
 
8
- `ws://localhost:9876` - User activates tabs via extension (green = active)
8
+ CLI browser control. User activates tabs via extension (green = active).
9
9
 
10
10
  ## Start Relay
11
11
 
@@ -15,26 +15,20 @@ curl -s http://localhost:9876/health || (npx tab-agent start &)
15
15
 
16
16
  ## Commands
17
17
 
18
- ```
19
- tabs -> list active tabs
20
- snapshot tabId -> page with refs [e1], [e2]...
21
- screenshot tabId -> viewport screenshot (base64 PNG)
22
- screenshot tabId fullPage=true -> full page screenshot
23
- click tabId ref -> click element
24
- fill tabId ref value -> fill input
25
- type tabId ref text -> type text
26
- type tabId ref text submit=true -> type and press Enter
27
- press tabId key -> Enter/Escape/Tab/Arrow*
28
- scroll tabId direction amount -> scroll page
29
- scrollintoview tabId ref -> scroll element visible
30
- navigate tabId url -> go to URL
31
- wait tabId text="..." -> wait for text
32
- wait tabId selector="..." timeout=ms -> wait for element
33
- evaluate tabId script="..." -> run JavaScript
34
- batchfill tabId fields=[...] -> fill multiple fields
35
- dialog tabId accept=true -> handle alert/confirm
18
+ ```bash
19
+ tabs # List active tabs
20
+ snapshot # Page with refs [e1], [e2]...
21
+ screenshot [--full] # Capture viewport/full page
22
+ click <ref> # Click element
23
+ type <ref> <text> # Type text
24
+ fill <ref> <value> # Fill form field
25
+ press <key> # Enter/Escape/Tab/Arrow*
26
+ scroll <dir> [amount] # Scroll up/down
27
+ navigate <url> # Go to URL
28
+ wait <text|selector> # Wait for condition
29
+ evaluate <script> # Run JavaScript
36
30
  ```
37
31
 
38
32
  ## Flow
39
33
 
40
- `tabs` -> `snapshot` -> `click`/`fill` -> repeat. Use `screenshot` if snapshot incomplete.
34
+ `snapshot` -> `click`/`type` -> repeat. Use `screenshot` if snapshot incomplete.