btcp-browser-agent 0.1.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.
Files changed (117) hide show
  1. package/CLAUDE.md +230 -0
  2. package/LICENSE +21 -0
  3. package/README.md +309 -0
  4. package/SKILL.md +143 -0
  5. package/SNAPSHOT_IMPROVEMENTS.md +302 -0
  6. package/USAGE.md +146 -0
  7. package/dist/index.d.ts +34 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +35 -0
  10. package/dist/index.js.map +1 -0
  11. package/docs/browser-cli-design.md +500 -0
  12. package/examples/chrome-extension/CHANGELOG.md +210 -0
  13. package/examples/chrome-extension/DEBUG.md +231 -0
  14. package/examples/chrome-extension/ERROR_FIXED.md +147 -0
  15. package/examples/chrome-extension/QUICK_TEST.md +189 -0
  16. package/examples/chrome-extension/README.md +149 -0
  17. package/examples/chrome-extension/SESSION_ONLY_MODE.md +305 -0
  18. package/examples/chrome-extension/TEST_WITH_YOUR_TABS.md +97 -0
  19. package/examples/chrome-extension/build.js +43 -0
  20. package/examples/chrome-extension/manifest.json +37 -0
  21. package/examples/chrome-extension/package-lock.json +1063 -0
  22. package/examples/chrome-extension/package.json +21 -0
  23. package/examples/chrome-extension/popup.html +195 -0
  24. package/examples/chrome-extension/src/background.ts +12 -0
  25. package/examples/chrome-extension/src/content.ts +7 -0
  26. package/examples/chrome-extension/src/popup.ts +303 -0
  27. package/examples/chrome-extension/src/scenario-google-github.ts +389 -0
  28. package/examples/chrome-extension/test-page.html +127 -0
  29. package/examples/chrome-extension/tests/README.md +206 -0
  30. package/examples/chrome-extension/tests/scenario-google-to-github-star.ts +380 -0
  31. package/examples/chrome-extension/tsconfig.json +14 -0
  32. package/examples/snapshots/README.md +207 -0
  33. package/examples/snapshots/amazon-com-detail.html +9528 -0
  34. package/examples/snapshots/amazon-com-detail.snapshot.txt +997 -0
  35. package/examples/snapshots/convert-snapshots.ts +97 -0
  36. package/examples/snapshots/edition-cnn-com.html +13292 -0
  37. package/examples/snapshots/edition-cnn-com.snapshot.txt +562 -0
  38. package/examples/snapshots/github-com-microsoft-vscode.html +2916 -0
  39. package/examples/snapshots/github-com-microsoft-vscode.snapshot.txt +455 -0
  40. package/examples/snapshots/google-search.html +20012 -0
  41. package/examples/snapshots/google-search.snapshot.txt +195 -0
  42. package/examples/snapshots/metadata.json +86 -0
  43. package/examples/snapshots/npr-org-templates.html +2031 -0
  44. package/examples/snapshots/npr-org-templates.snapshot.txt +224 -0
  45. package/examples/snapshots/stackoverflow-com.html +5216 -0
  46. package/examples/snapshots/stackoverflow-com.snapshot.txt +2404 -0
  47. package/examples/snapshots/test-all-mode.html +46 -0
  48. package/examples/snapshots/test-all-mode.snapshot.txt +5 -0
  49. package/examples/snapshots/validate.test.ts +296 -0
  50. package/package.json +65 -0
  51. package/packages/cli/package.json +42 -0
  52. package/packages/cli/src/__tests__/cli.test.ts +434 -0
  53. package/packages/cli/src/__tests__/errors.test.ts +226 -0
  54. package/packages/cli/src/__tests__/executor.test.ts +275 -0
  55. package/packages/cli/src/__tests__/formatter.test.ts +260 -0
  56. package/packages/cli/src/__tests__/parser.test.ts +288 -0
  57. package/packages/cli/src/__tests__/suggestions.test.ts +255 -0
  58. package/packages/cli/src/commands/back.ts +22 -0
  59. package/packages/cli/src/commands/check.ts +33 -0
  60. package/packages/cli/src/commands/clear.ts +33 -0
  61. package/packages/cli/src/commands/click.ts +32 -0
  62. package/packages/cli/src/commands/closetab.ts +31 -0
  63. package/packages/cli/src/commands/eval.ts +41 -0
  64. package/packages/cli/src/commands/fill.ts +30 -0
  65. package/packages/cli/src/commands/focus.ts +33 -0
  66. package/packages/cli/src/commands/forward.ts +22 -0
  67. package/packages/cli/src/commands/goto.ts +34 -0
  68. package/packages/cli/src/commands/help.ts +162 -0
  69. package/packages/cli/src/commands/hover.ts +34 -0
  70. package/packages/cli/src/commands/index.ts +129 -0
  71. package/packages/cli/src/commands/newtab.ts +35 -0
  72. package/packages/cli/src/commands/press.ts +40 -0
  73. package/packages/cli/src/commands/reload.ts +25 -0
  74. package/packages/cli/src/commands/screenshot.ts +27 -0
  75. package/packages/cli/src/commands/scroll.ts +64 -0
  76. package/packages/cli/src/commands/select.ts +35 -0
  77. package/packages/cli/src/commands/snapshot.ts +21 -0
  78. package/packages/cli/src/commands/tab.ts +32 -0
  79. package/packages/cli/src/commands/tabs.ts +26 -0
  80. package/packages/cli/src/commands/text.ts +27 -0
  81. package/packages/cli/src/commands/title.ts +17 -0
  82. package/packages/cli/src/commands/type.ts +38 -0
  83. package/packages/cli/src/commands/uncheck.ts +33 -0
  84. package/packages/cli/src/commands/url.ts +17 -0
  85. package/packages/cli/src/commands/wait.ts +54 -0
  86. package/packages/cli/src/errors.ts +164 -0
  87. package/packages/cli/src/executor.ts +68 -0
  88. package/packages/cli/src/formatter.ts +215 -0
  89. package/packages/cli/src/index.ts +257 -0
  90. package/packages/cli/src/parser.ts +195 -0
  91. package/packages/cli/src/suggestions.ts +207 -0
  92. package/packages/cli/src/terminal/Terminal.ts +365 -0
  93. package/packages/cli/src/terminal/index.ts +5 -0
  94. package/packages/cli/src/types.ts +155 -0
  95. package/packages/cli/tsconfig.json +20 -0
  96. package/packages/core/package.json +35 -0
  97. package/packages/core/src/actions.ts +1210 -0
  98. package/packages/core/src/errors.ts +296 -0
  99. package/packages/core/src/index.test.ts +638 -0
  100. package/packages/core/src/index.ts +220 -0
  101. package/packages/core/src/ref-map.ts +107 -0
  102. package/packages/core/src/snapshot.ts +873 -0
  103. package/packages/core/src/types.ts +536 -0
  104. package/packages/core/tsconfig.json +23 -0
  105. package/packages/extension/README.md +129 -0
  106. package/packages/extension/package.json +43 -0
  107. package/packages/extension/src/background.ts +888 -0
  108. package/packages/extension/src/content.ts +172 -0
  109. package/packages/extension/src/index.ts +579 -0
  110. package/packages/extension/src/session-manager.ts +385 -0
  111. package/packages/extension/src/session-types.ts +144 -0
  112. package/packages/extension/src/types.ts +162 -0
  113. package/packages/extension/tsconfig.json +28 -0
  114. package/src/index.ts +64 -0
  115. package/tsconfig.build.json +12 -0
  116. package/tsconfig.json +26 -0
  117. package/vitest.config.ts +13 -0
@@ -0,0 +1,305 @@
1
+ # Session-Only Mode
2
+
3
+ ## Overview
4
+
5
+ The BTCP Browser Agent **only manages tabs within its session/tab group**. No operations are allowed outside of a session.
6
+
7
+ ## Key Principle
8
+
9
+ **Session is Required** - All operations require an active session:
10
+
11
+ - ❌ Cannot list tabs without a session
12
+ - ❌ Cannot create tabs without a session
13
+ - ❌ Cannot navigate without a session
14
+ - ❌ Cannot perform DOM operations without a session
15
+ - ✅ Must create session first, then all operations work within that session
16
+
17
+ ## How It Works
18
+
19
+ ### 1. Session Must Be Created First
20
+
21
+ ```javascript
22
+ // Create a session to start working
23
+ await client.groupCreate({ title: "BTCP Session 1" });
24
+
25
+ // Now all operations work (scoped to session)
26
+ await client.tabNew({ url: "https://example.com" });
27
+ await client.listTabs(); // Only session tabs
28
+ ```
29
+
30
+ ### 2. Without Session = Error
31
+
32
+ ```javascript
33
+ // Try to list tabs without session
34
+ await client.listTabs();
35
+ // ❌ Error: "No active session. Create a session first to manage tabs."
36
+
37
+ // Try to create tab without session
38
+ await client.tabNew({ url: "https://example.com" });
39
+ // ❌ Error: "No active session. Create a session first to manage tabs."
40
+ ```
41
+
42
+ ### 3. Operations Are Session-Scoped
43
+
44
+ Once a session exists, all operations only affect tabs in that session:
45
+
46
+ ```javascript
47
+ // Create session with tab A
48
+ await client.groupCreate(); // Tab A joins session
49
+
50
+ // Create new tabs (auto-added to session)
51
+ await client.tabNew({ url: "https://example.com" }); // Tab B
52
+ await client.tabNew({ url: "https://github.com" }); // Tab C
53
+
54
+ // List tabs (only shows A, B, C)
55
+ const tabs = await client.listTabs(); // [A, B, C]
56
+
57
+ // User manually opens Tab D outside session
58
+
59
+ // Try to list tabs (still only shows session tabs)
60
+ const tabs2 = await client.listTabs(); // [A, B, C] - no Tab D
61
+
62
+ // Try to access Tab D
63
+ await client.switchTab(tabD.id);
64
+ // ❌ Error: "Cannot switch to tab: tab is not in the active session"
65
+ ```
66
+
67
+ ## Implementation Details
68
+
69
+ ### Session Requirement Check
70
+
71
+ Every operation checks for active session:
72
+
73
+ ```typescript
74
+ async listTabs(): Promise<TabInfo[]> {
75
+ const sessionGroupId = this.sessionManager.getActiveSessionGroupId();
76
+
77
+ // Session is required
78
+ if (sessionGroupId === null) {
79
+ throw new Error('No active session. Create a session first.');
80
+ }
81
+
82
+ // Only query tabs in the session group
83
+ const tabs = await chrome.tabs.query({ groupId: sessionGroupId });
84
+ return tabs.map(mapToTabInfo);
85
+ }
86
+ ```
87
+
88
+ ### Tab Validation
89
+
90
+ Operations validate tab membership:
91
+
92
+ ```typescript
93
+ private async isTabInSession(tabId: number): Promise<boolean> {
94
+ const sessionGroupId = this.sessionManager.getActiveSessionGroupId();
95
+
96
+ // Session required
97
+ if (sessionGroupId === null) {
98
+ throw new Error('No active session.');
99
+ }
100
+
101
+ // Check if tab is in session
102
+ const tab = await chrome.tabs.get(tabId);
103
+ return tab.groupId === sessionGroupId;
104
+ }
105
+ ```
106
+
107
+ ### New Tab Creation
108
+
109
+ New tabs are automatically added to session:
110
+
111
+ ```typescript
112
+ async newTab(options?: { url?: string }): Promise<TabInfo> {
113
+ // Check session exists
114
+ if (!this.sessionManager.getActiveSessionGroupId()) {
115
+ throw new Error('No active session.');
116
+ }
117
+
118
+ // Create tab
119
+ const tab = await chrome.tabs.create({ url: options?.url });
120
+
121
+ // Add to session
122
+ const added = await this.sessionManager.addTabToActiveSession(tab.id);
123
+ if (!added) {
124
+ // Cleanup if failed
125
+ await chrome.tabs.remove(tab.id);
126
+ throw new Error('Failed to add tab to session');
127
+ }
128
+
129
+ return mapToTabInfo(tab);
130
+ }
131
+ ```
132
+
133
+ ## Usage Example
134
+
135
+ ### Complete Workflow
136
+
137
+ ```javascript
138
+ // 1. Create session (required first step)
139
+ await client.groupCreate({ title: "Work Session" });
140
+ // Current tab joins blue group "Work Session"
141
+
142
+ // 2. Now can create tabs (auto-added to session)
143
+ await client.tabNew({ url: "https://example.com" });
144
+ await client.tabNew({ url: "https://github.com" });
145
+
146
+ // 3. List tabs (only shows session tabs)
147
+ const tabs = await client.listTabs();
148
+ console.log(tabs.length); // 3 tabs
149
+
150
+ // 4. Navigate, interact (only session tabs)
151
+ await client.navigate("https://newsite.com");
152
+ await client.click("#button");
153
+ await client.fill("#input", "value");
154
+
155
+ // 5. Close session (closes all tabs)
156
+ const session = await client.sessionGetCurrent();
157
+ await client.groupDelete(session.groupId);
158
+ ```
159
+
160
+ ### Error Handling
161
+
162
+ ```javascript
163
+ try {
164
+ // Try operation without session
165
+ await client.listTabs();
166
+ } catch (err) {
167
+ if (err.message.includes('No active session')) {
168
+ // Create session first
169
+ await client.groupCreate();
170
+
171
+ // Now retry
172
+ const tabs = await client.listTabs();
173
+ }
174
+ }
175
+ ```
176
+
177
+ ## Benefits
178
+
179
+ ### 1. Security & Isolation
180
+ - Extension cannot access tabs outside its scope
181
+ - User's personal tabs are protected
182
+ - Clear boundaries enforced by code
183
+
184
+ ### 2. Explicit Consent
185
+ - User must explicitly create session
186
+ - Visual indicator (tab group color/label)
187
+ - No accidental access to unrelated tabs
188
+
189
+ ### 3. Clean Separation
190
+ - Each session is independent
191
+ - Easy to manage multiple workflows
192
+ - Clear visual organization
193
+
194
+ ### 4. Predictable Behavior
195
+ - All operations scoped to one group
196
+ - No ambiguity about which tabs are managed
197
+ - Consistent error messages
198
+
199
+ ## Visual Indicators
200
+
201
+ ### Chrome Tab Groups
202
+ - Session tabs have **colored border** (e.g., blue)
203
+ - Group label shows session name (e.g., "BTCP Session 1")
204
+ - Can collapse/expand the group
205
+ - Easy to see which tabs are in scope
206
+
207
+ ### Extension Popup
208
+ - Shows session status (active/inactive)
209
+ - Displays session name and tab count
210
+ - Info: "Extension only manages tabs within the active session"
211
+ - Buttons disabled when no session
212
+
213
+ ## Comparison
214
+
215
+ ### Before (Universal Access)
216
+ ```javascript
217
+ // Could access any tab
218
+ const allTabs = await client.listTabs(); // All tabs in window
219
+
220
+ // Could navigate any tab
221
+ await client.switchTab(anyTabId); // Works
222
+
223
+ // Could close any tab
224
+ await client.tabClose(anyTabId); // Works
225
+ ```
226
+
227
+ ### After (Session-Only)
228
+ ```javascript
229
+ // Must create session first
230
+ await client.groupCreate();
231
+
232
+ // Only session tabs
233
+ const sessionTabs = await client.listTabs(); // Only grouped tabs
234
+
235
+ // Only session tabs accessible
236
+ await client.switchTab(sessionTabId); // ✅ Works
237
+ await client.switchTab(outsideTabId); // ❌ Error
238
+
239
+ // Only session tabs closable
240
+ await client.tabClose(sessionTabId); // ✅ Works
241
+ await client.tabClose(outsideTabId); // ❌ Error
242
+ ```
243
+
244
+ ## FAQ
245
+
246
+ **Q: Can I use the extension without creating a session?**
247
+ A: No. Session creation is required for all operations.
248
+
249
+ **Q: What if I manually move a tab out of the session group?**
250
+ A: The extension immediately loses access to that tab.
251
+
252
+ **Q: Can I manually add tabs to the session?**
253
+ A: Yes! Drag any tab into the session group, and it becomes accessible.
254
+
255
+ **Q: What happens if the session group is deleted?**
256
+ A: All operations will fail until a new session is created.
257
+
258
+ **Q: Can operations work across multiple sessions?**
259
+ A: No. Only one active session at a time, and operations are scoped to it.
260
+
261
+ **Q: Why is session required?**
262
+ A: Security, isolation, and explicit user consent. The extension should only manage what the user explicitly adds to the session.
263
+
264
+ ## Testing
265
+
266
+ ### Test Session Requirement
267
+
268
+ ```javascript
269
+ // 1. Try without session (should fail)
270
+ try {
271
+ await client.listTabs();
272
+ console.error('❌ Should have failed');
273
+ } catch (err) {
274
+ console.log('✅ Correctly requires session:', err.message);
275
+ }
276
+
277
+ // 2. Create session
278
+ await client.groupCreate({ title: "Test" });
279
+
280
+ // 3. Operations now work
281
+ const tabs = await client.listTabs();
282
+ console.log('✅ Session allows operations:', tabs.length);
283
+
284
+ // 4. Try accessing outside tab (should fail)
285
+ // (manually open tab outside session first)
286
+ try {
287
+ await client.switchTab(outsideTabId);
288
+ console.error('❌ Should have blocked outside tab');
289
+ } catch (err) {
290
+ console.log('✅ Correctly blocks outside tab:', err.message);
291
+ }
292
+ ```
293
+
294
+ ## Summary
295
+
296
+ **Core Principle**: Session-first, session-only
297
+
298
+ - ✅ Session required for all operations
299
+ - ✅ All operations scoped to session tabs only
300
+ - ✅ Clear errors when session missing
301
+ - ✅ Visual boundaries via tab groups
302
+ - ✅ Explicit user consent required
303
+ - ✅ Maximum security and isolation
304
+
305
+ The extension is now a **pure session manager** - it only works within the boundaries of its explicitly created session.
@@ -0,0 +1,97 @@
1
+ # Test Session with Your Current Tabs
2
+
3
+ ## ✅ Good News: Extension is Working!
4
+
5
+ Your tab list command worked perfectly. You have 5 tabs:
6
+ - GitHub BTCP repo
7
+ - Claude.ai session
8
+ - GitHub Pull Request
9
+ - **YouTube video (ACTIVE)** ← Perfect for testing!
10
+ - Chrome extensions page
11
+
12
+ ## Test the Session Now
13
+
14
+ Since the YouTube tab (ID: 1892096437) is active, it's perfect for testing:
15
+
16
+ ### Step 1: Create Session
17
+ 1. **Click the extension icon** (should still be open)
18
+ 2. **Click "Start New Session"** button
19
+ 3. **Watch the YouTube tab** - it should get a blue group border
20
+
21
+ ### Step 2: Verify Success
22
+ Check if you see:
23
+ - ✅ Blue/colored border around the YouTube tab
24
+ - ✅ Group label appears: "BTCP Session 1"
25
+ - ✅ Popup shows: "BTCP Session 1" with "1 tab"
26
+
27
+ ### Step 3: Test Auto-Grouping
28
+ 1. In the popup, click **"New Tab"** button
29
+ 2. A new tab should open at example.com
30
+ 3. **It should automatically join the "BTCP Session 1" group**
31
+ 4. Popup should update to show "2 tabs"
32
+
33
+ ### Step 4: Test Close Session
34
+ 1. Click **"Close Session"** button (red button)
35
+ 2. **Both tabs in the group should close** (YouTube + new tab)
36
+
37
+ ## What to Look For
38
+
39
+ ### Console Output (Background Service Worker)
40
+ When you click "Start New Session", you should see:
41
+
42
+ ```
43
+ [SessionManager] createGroup called with options: {}
44
+ [SessionManager] No tabIds provided, getting active tab...
45
+ [SessionManager] Active tab: {id: 1892096437, url: "https://www.youtube.com/...", ...}
46
+ [SessionManager] Creating group with tabs: [1892096437]
47
+ [SessionManager] Group created with ID: <some-number>
48
+ [SessionManager] Group updated with title: BTCP Session 1
49
+ ```
50
+
51
+ ### Visual Feedback
52
+ In your browser:
53
+ - YouTube tab gets colored border/background
54
+ - Tab shows group indicator
55
+ - Can collapse/expand the group by clicking the group label
56
+
57
+ ## If It Works
58
+
59
+ Congratulations! 🎉 Your session management is fully functional!
60
+
61
+ You can now:
62
+ - ✅ Start sessions to organize tabs
63
+ - ✅ Auto-group new tabs created by the extension
64
+ - ✅ Close entire sessions at once
65
+ - ✅ Track how many tabs are in each session
66
+
67
+ ## If It Doesn't Work
68
+
69
+ Check these:
70
+
71
+ 1. **Error in popup console?**
72
+ - Right-click popup → Inspect → Console tab
73
+ - Share the error message
74
+
75
+ 2. **Error in background console?**
76
+ - Go to chrome://extensions
77
+ - Click "service worker" under BTCP Browser Agent
78
+ - Check console for errors
79
+
80
+ 3. **Nothing happens?**
81
+ - Make sure YouTube tab is actually active (click on it first)
82
+ - Try refreshing the extension
83
+ - Check if "Start New Session" button is enabled
84
+
85
+ ## Next: Make Sessions Automatic
86
+
87
+ Based on your earlier comment about wanting `launch` to handle sessions, we can make this more automatic:
88
+
89
+ **Option 1:** First `tabNew` automatically creates a session
90
+ - User doesn't need to click "Start New Session"
91
+ - Just click "New Tab" and session auto-starts
92
+
93
+ **Option 2:** Session always exists
94
+ - Background script creates default session on startup
95
+ - All operations happen in that session
96
+
97
+ Would you like me to implement either of these?
@@ -0,0 +1,43 @@
1
+ import * as esbuild from 'esbuild';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+
5
+ const watch = process.argv.includes('--watch');
6
+
7
+ const config = {
8
+ entryPoints: [
9
+ 'src/background.ts',
10
+ 'src/content.ts',
11
+ 'src/popup.ts',
12
+ ],
13
+ bundle: true,
14
+ outdir: 'dist',
15
+ format: 'esm',
16
+ platform: 'browser',
17
+ target: 'chrome120',
18
+ sourcemap: true,
19
+ };
20
+
21
+ // Copy static files to dist
22
+ function copyStaticFiles() {
23
+ const filesToCopy = ['manifest.json', 'popup.html'];
24
+
25
+ if (!fs.existsSync('dist')) {
26
+ fs.mkdirSync('dist', { recursive: true });
27
+ }
28
+
29
+ for (const file of filesToCopy) {
30
+ fs.copyFileSync(file, path.join('dist', file));
31
+ }
32
+ }
33
+
34
+ if (watch) {
35
+ const ctx = await esbuild.context(config);
36
+ await ctx.watch();
37
+ copyStaticFiles();
38
+ console.log('Watching for changes...');
39
+ } else {
40
+ await esbuild.build(config);
41
+ copyStaticFiles();
42
+ console.log('Build complete');
43
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "manifest_version": 3,
3
+ "name": "BTCP Browser Agent",
4
+ "version": "1.0.0",
5
+ "description": "Browser automation with DOM actions and navigation",
6
+
7
+ "permissions": [
8
+ "activeTab",
9
+ "tabs",
10
+ "tabGroups",
11
+ "scripting",
12
+ "storage"
13
+ ],
14
+
15
+ "host_permissions": [
16
+ "<all_urls>"
17
+ ],
18
+
19
+ "background": {
20
+ "service_worker": "background.js",
21
+ "type": "module"
22
+ },
23
+
24
+ "content_scripts": [
25
+ {
26
+ "matches": ["<all_urls>"],
27
+ "js": ["content.js"],
28
+ "run_at": "document_idle",
29
+ "all_frames": true
30
+ }
31
+ ],
32
+
33
+ "action": {
34
+ "default_popup": "popup.html",
35
+ "default_title": "BTCP Agent"
36
+ }
37
+ }