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.
- package/CLAUDE.md +230 -0
- package/LICENSE +21 -0
- package/README.md +309 -0
- package/SKILL.md +143 -0
- package/SNAPSHOT_IMPROVEMENTS.md +302 -0
- package/USAGE.md +146 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/docs/browser-cli-design.md +500 -0
- package/examples/chrome-extension/CHANGELOG.md +210 -0
- package/examples/chrome-extension/DEBUG.md +231 -0
- package/examples/chrome-extension/ERROR_FIXED.md +147 -0
- package/examples/chrome-extension/QUICK_TEST.md +189 -0
- package/examples/chrome-extension/README.md +149 -0
- package/examples/chrome-extension/SESSION_ONLY_MODE.md +305 -0
- package/examples/chrome-extension/TEST_WITH_YOUR_TABS.md +97 -0
- package/examples/chrome-extension/build.js +43 -0
- package/examples/chrome-extension/manifest.json +37 -0
- package/examples/chrome-extension/package-lock.json +1063 -0
- package/examples/chrome-extension/package.json +21 -0
- package/examples/chrome-extension/popup.html +195 -0
- package/examples/chrome-extension/src/background.ts +12 -0
- package/examples/chrome-extension/src/content.ts +7 -0
- package/examples/chrome-extension/src/popup.ts +303 -0
- package/examples/chrome-extension/src/scenario-google-github.ts +389 -0
- package/examples/chrome-extension/test-page.html +127 -0
- package/examples/chrome-extension/tests/README.md +206 -0
- package/examples/chrome-extension/tests/scenario-google-to-github-star.ts +380 -0
- package/examples/chrome-extension/tsconfig.json +14 -0
- package/examples/snapshots/README.md +207 -0
- package/examples/snapshots/amazon-com-detail.html +9528 -0
- package/examples/snapshots/amazon-com-detail.snapshot.txt +997 -0
- package/examples/snapshots/convert-snapshots.ts +97 -0
- package/examples/snapshots/edition-cnn-com.html +13292 -0
- package/examples/snapshots/edition-cnn-com.snapshot.txt +562 -0
- package/examples/snapshots/github-com-microsoft-vscode.html +2916 -0
- package/examples/snapshots/github-com-microsoft-vscode.snapshot.txt +455 -0
- package/examples/snapshots/google-search.html +20012 -0
- package/examples/snapshots/google-search.snapshot.txt +195 -0
- package/examples/snapshots/metadata.json +86 -0
- package/examples/snapshots/npr-org-templates.html +2031 -0
- package/examples/snapshots/npr-org-templates.snapshot.txt +224 -0
- package/examples/snapshots/stackoverflow-com.html +5216 -0
- package/examples/snapshots/stackoverflow-com.snapshot.txt +2404 -0
- package/examples/snapshots/test-all-mode.html +46 -0
- package/examples/snapshots/test-all-mode.snapshot.txt +5 -0
- package/examples/snapshots/validate.test.ts +296 -0
- package/package.json +65 -0
- package/packages/cli/package.json +42 -0
- package/packages/cli/src/__tests__/cli.test.ts +434 -0
- package/packages/cli/src/__tests__/errors.test.ts +226 -0
- package/packages/cli/src/__tests__/executor.test.ts +275 -0
- package/packages/cli/src/__tests__/formatter.test.ts +260 -0
- package/packages/cli/src/__tests__/parser.test.ts +288 -0
- package/packages/cli/src/__tests__/suggestions.test.ts +255 -0
- package/packages/cli/src/commands/back.ts +22 -0
- package/packages/cli/src/commands/check.ts +33 -0
- package/packages/cli/src/commands/clear.ts +33 -0
- package/packages/cli/src/commands/click.ts +32 -0
- package/packages/cli/src/commands/closetab.ts +31 -0
- package/packages/cli/src/commands/eval.ts +41 -0
- package/packages/cli/src/commands/fill.ts +30 -0
- package/packages/cli/src/commands/focus.ts +33 -0
- package/packages/cli/src/commands/forward.ts +22 -0
- package/packages/cli/src/commands/goto.ts +34 -0
- package/packages/cli/src/commands/help.ts +162 -0
- package/packages/cli/src/commands/hover.ts +34 -0
- package/packages/cli/src/commands/index.ts +129 -0
- package/packages/cli/src/commands/newtab.ts +35 -0
- package/packages/cli/src/commands/press.ts +40 -0
- package/packages/cli/src/commands/reload.ts +25 -0
- package/packages/cli/src/commands/screenshot.ts +27 -0
- package/packages/cli/src/commands/scroll.ts +64 -0
- package/packages/cli/src/commands/select.ts +35 -0
- package/packages/cli/src/commands/snapshot.ts +21 -0
- package/packages/cli/src/commands/tab.ts +32 -0
- package/packages/cli/src/commands/tabs.ts +26 -0
- package/packages/cli/src/commands/text.ts +27 -0
- package/packages/cli/src/commands/title.ts +17 -0
- package/packages/cli/src/commands/type.ts +38 -0
- package/packages/cli/src/commands/uncheck.ts +33 -0
- package/packages/cli/src/commands/url.ts +17 -0
- package/packages/cli/src/commands/wait.ts +54 -0
- package/packages/cli/src/errors.ts +164 -0
- package/packages/cli/src/executor.ts +68 -0
- package/packages/cli/src/formatter.ts +215 -0
- package/packages/cli/src/index.ts +257 -0
- package/packages/cli/src/parser.ts +195 -0
- package/packages/cli/src/suggestions.ts +207 -0
- package/packages/cli/src/terminal/Terminal.ts +365 -0
- package/packages/cli/src/terminal/index.ts +5 -0
- package/packages/cli/src/types.ts +155 -0
- package/packages/cli/tsconfig.json +20 -0
- package/packages/core/package.json +35 -0
- package/packages/core/src/actions.ts +1210 -0
- package/packages/core/src/errors.ts +296 -0
- package/packages/core/src/index.test.ts +638 -0
- package/packages/core/src/index.ts +220 -0
- package/packages/core/src/ref-map.ts +107 -0
- package/packages/core/src/snapshot.ts +873 -0
- package/packages/core/src/types.ts +536 -0
- package/packages/core/tsconfig.json +23 -0
- package/packages/extension/README.md +129 -0
- package/packages/extension/package.json +43 -0
- package/packages/extension/src/background.ts +888 -0
- package/packages/extension/src/content.ts +172 -0
- package/packages/extension/src/index.ts +579 -0
- package/packages/extension/src/session-manager.ts +385 -0
- package/packages/extension/src/session-types.ts +144 -0
- package/packages/extension/src/types.ts +162 -0
- package/packages/extension/tsconfig.json +28 -0
- package/src/index.ts +64 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +26 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
# Browser-Based CLI Design Document
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document proposes an approach to implement a browser-based CLI for btcp-browser-agent, inspired by [vercel-labs/agent-browser](https://github.com/vercel-labs/agent-browser).
|
|
6
|
+
|
|
7
|
+
## Goals
|
|
8
|
+
|
|
9
|
+
1. Provide a command-line interface for browser automation within the browser context
|
|
10
|
+
2. Enable AI agents to control browsers using simple text commands
|
|
11
|
+
3. Maintain compatibility with existing @btcp/core and @btcp/extension packages
|
|
12
|
+
4. Support both interactive and programmatic usage
|
|
13
|
+
|
|
14
|
+
## Reference: agent-browser CLI
|
|
15
|
+
|
|
16
|
+
The agent-browser project provides these command patterns:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
agent-browser open example.com # Navigate to URL
|
|
20
|
+
agent-browser click @e2 # Click element by ref
|
|
21
|
+
agent-browser type @e3 "hello" # Type into element
|
|
22
|
+
agent-browser snapshot # Get accessibility tree
|
|
23
|
+
agent-browser screenshot page.png # Capture screenshot
|
|
24
|
+
agent-browser fill @e5 "value" # Fill form field
|
|
25
|
+
agent-browser scroll down 200 # Scroll page
|
|
26
|
+
agent-browser press Enter # Press key
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
### In-Browser CLI (Chrome Extension Only)
|
|
32
|
+
|
|
33
|
+
Commands are sent directly from the Chrome extension - no external processes or WebSocket bridges needed.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
37
|
+
│ Chrome Extension │
|
|
38
|
+
│ │
|
|
39
|
+
│ ┌─────────────────────────────────────────────────────────┐│
|
|
40
|
+
│ │ Terminal UI (Popup/Panel) ││
|
|
41
|
+
│ │ $ snapshot ││
|
|
42
|
+
│ │ - button 'Submit' [@ref:1] ││
|
|
43
|
+
│ │ - textbox 'Email' [@ref:2] ││
|
|
44
|
+
│ │ ││
|
|
45
|
+
│ │ $ click @ref:1 ││
|
|
46
|
+
│ │ ✓ Clicked element: button 'Submit' ││
|
|
47
|
+
│ └─────────────────────────────────────────────────────────┘│
|
|
48
|
+
│ │ │
|
|
49
|
+
│ ▼ │
|
|
50
|
+
│ ┌─────────────────────────────────────────────────────────┐│
|
|
51
|
+
│ │ CLI Parser & Executor (@btcp/cli) ││
|
|
52
|
+
│ │ parseCommand() → executeCommand() ││
|
|
53
|
+
│ └─────────────────────────────────────────────────────────┘│
|
|
54
|
+
│ │ │
|
|
55
|
+
│ ▼ │
|
|
56
|
+
│ ┌─────────────────────────────────────────────────────────┐│
|
|
57
|
+
│ │ BackgroundAgent (@btcp/extension) ││
|
|
58
|
+
│ │ Tab management, navigation, routing ││
|
|
59
|
+
│ └─────────────────────────────────────────────────────────┘│
|
|
60
|
+
│ │ │
|
|
61
|
+
│ chrome.tabs.sendMessage() │
|
|
62
|
+
│ ▼ │
|
|
63
|
+
│ ┌─────────────────────────────────────────────────────────┐│
|
|
64
|
+
│ │ ContentAgent (@btcp/core) ││
|
|
65
|
+
│ │ DOM operations, snapshots, interactions ││
|
|
66
|
+
│ └─────────────────────────────────────────────────────────┘│
|
|
67
|
+
└─────────────────────────────────────────────────────────────┘
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Benefits:**
|
|
71
|
+
- Pure browser-native approach
|
|
72
|
+
- No external dependencies
|
|
73
|
+
- Direct integration with existing @btcp packages
|
|
74
|
+
- Real-time command execution
|
|
75
|
+
- Works entirely within extension context
|
|
76
|
+
|
|
77
|
+
## Implementation Plan
|
|
78
|
+
|
|
79
|
+
### Phase 1: Command Parser & Executor
|
|
80
|
+
|
|
81
|
+
Create a new package `@btcp/cli` that provides command parsing and execution.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
packages/
|
|
85
|
+
cli/
|
|
86
|
+
src/
|
|
87
|
+
index.ts # Main exports
|
|
88
|
+
parser.ts # Command line parser
|
|
89
|
+
executor.ts # Command executor
|
|
90
|
+
commands/ # Individual command implementations
|
|
91
|
+
goto.ts
|
|
92
|
+
click.ts
|
|
93
|
+
type.ts
|
|
94
|
+
snapshot.ts
|
|
95
|
+
screenshot.ts
|
|
96
|
+
...
|
|
97
|
+
formatter.ts # Output formatting
|
|
98
|
+
types.ts # TypeScript types
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### Command Syntax
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// Command structure
|
|
105
|
+
interface CLICommand {
|
|
106
|
+
name: string;
|
|
107
|
+
args: string[];
|
|
108
|
+
flags: Record<string, string | boolean>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Examples:
|
|
112
|
+
// "goto https://example.com" → { name: 'goto', args: ['https://example.com'], flags: {} }
|
|
113
|
+
// "click @ref:5 --wait 1000" → { name: 'click', args: ['@ref:5'], flags: { wait: '1000' } }
|
|
114
|
+
// "screenshot --full" → { name: 'screenshot', args: [], flags: { full: true } }
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### Supported Commands
|
|
118
|
+
|
|
119
|
+
| Command | Syntax | Description |
|
|
120
|
+
|---------|--------|-------------|
|
|
121
|
+
| `goto` | `goto <url>` | Navigate to URL |
|
|
122
|
+
| `back` | `back` | Go back in history |
|
|
123
|
+
| `forward` | `forward` | Go forward in history |
|
|
124
|
+
| `reload` | `reload` | Reload current page |
|
|
125
|
+
| `snapshot` | `snapshot` | Get accessibility tree |
|
|
126
|
+
| `screenshot` | `screenshot [filename]` | Capture screenshot |
|
|
127
|
+
| `click` | `click <selector>` | Click element |
|
|
128
|
+
| `dblclick` | `dblclick <selector>` | Double-click element |
|
|
129
|
+
| `type` | `type <selector> <text>` | Type text into element |
|
|
130
|
+
| `fill` | `fill <selector> <value>` | Fill input field |
|
|
131
|
+
| `clear` | `clear <selector>` | Clear input field |
|
|
132
|
+
| `check` | `check <selector>` | Check checkbox |
|
|
133
|
+
| `uncheck` | `uncheck <selector>` | Uncheck checkbox |
|
|
134
|
+
| `select` | `select <selector> <value>` | Select dropdown option |
|
|
135
|
+
| `hover` | `hover <selector>` | Hover over element |
|
|
136
|
+
| `scroll` | `scroll <direction> [amount]` | Scroll page |
|
|
137
|
+
| `press` | `press <key>` | Press keyboard key |
|
|
138
|
+
| `wait` | `wait <ms>` | Wait for duration |
|
|
139
|
+
| `eval` | `eval <code>` | Execute JavaScript |
|
|
140
|
+
| `tabs` | `tabs` | List all tabs |
|
|
141
|
+
| `tab` | `tab <id>` | Switch to tab |
|
|
142
|
+
| `newtab` | `newtab [url]` | Open new tab |
|
|
143
|
+
| `closetab` | `closetab [id]` | Close tab |
|
|
144
|
+
| `help` | `help [command]` | Show help |
|
|
145
|
+
|
|
146
|
+
### Phase 2: In-Browser Terminal UI
|
|
147
|
+
|
|
148
|
+
Create a terminal component for the extension popup or a dedicated panel.
|
|
149
|
+
|
|
150
|
+
#### Terminal Component
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
// packages/cli/src/terminal/
|
|
154
|
+
interface TerminalConfig {
|
|
155
|
+
theme: 'dark' | 'light';
|
|
156
|
+
fontSize: number;
|
|
157
|
+
historySize: number;
|
|
158
|
+
prompt: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
interface TerminalState {
|
|
162
|
+
history: HistoryEntry[];
|
|
163
|
+
inputBuffer: string;
|
|
164
|
+
cursorPosition: number;
|
|
165
|
+
commandHistory: string[];
|
|
166
|
+
historyIndex: number;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
interface HistoryEntry {
|
|
170
|
+
type: 'input' | 'output' | 'error';
|
|
171
|
+
content: string;
|
|
172
|
+
timestamp: number;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### UI Features
|
|
177
|
+
|
|
178
|
+
1. **Command input** with cursor and editing
|
|
179
|
+
2. **Command history** (up/down arrows)
|
|
180
|
+
3. **Tab completion** for commands and refs
|
|
181
|
+
4. **Syntax highlighting** for commands
|
|
182
|
+
5. **Output formatting** (success/error/info)
|
|
183
|
+
6. **Scrollable history**
|
|
184
|
+
7. **Copy/paste support**
|
|
185
|
+
|
|
186
|
+
### Phase 3: Integration with BackgroundAgent
|
|
187
|
+
|
|
188
|
+
Connect the CLI to the existing BackgroundAgent for execution.
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Example integration
|
|
192
|
+
import { BackgroundAgent } from '@btcp/extension';
|
|
193
|
+
import { parseCommand, executeCommand } from '@btcp/cli';
|
|
194
|
+
|
|
195
|
+
const agent = new BackgroundAgent();
|
|
196
|
+
|
|
197
|
+
async function handleCommand(input: string): Promise<string> {
|
|
198
|
+
const command = parseCommand(input);
|
|
199
|
+
const result = await executeCommand(agent, command);
|
|
200
|
+
return formatResult(result);
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Detailed Component Design
|
|
205
|
+
|
|
206
|
+
### 1. Command Parser (`parser.ts`)
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
export function parseCommand(input: string): CLICommand {
|
|
210
|
+
const tokens = tokenize(input);
|
|
211
|
+
const name = tokens[0];
|
|
212
|
+
const { args, flags } = parseArgs(tokens.slice(1));
|
|
213
|
+
return { name, args, flags };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function tokenize(input: string): string[] {
|
|
217
|
+
// Handle quoted strings, escape characters
|
|
218
|
+
// "type @ref:1 \"hello world\"" → ['type', '@ref:1', 'hello world']
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function parseArgs(tokens: string[]): { args: string[], flags: Record<string, string | boolean> } {
|
|
222
|
+
// Parse --flag and --flag=value patterns
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 2. Command Executor (`executor.ts`)
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
export async function executeCommand(
|
|
230
|
+
agent: BackgroundAgent,
|
|
231
|
+
command: CLICommand
|
|
232
|
+
): Promise<CommandResult> {
|
|
233
|
+
const handler = commands[command.name];
|
|
234
|
+
if (!handler) {
|
|
235
|
+
throw new CLIError(`Unknown command: ${command.name}`);
|
|
236
|
+
}
|
|
237
|
+
return handler.execute(agent, command.args, command.flags);
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### 3. Individual Commands (`commands/*.ts`)
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// commands/goto.ts
|
|
245
|
+
export const gotoCommand: CommandHandler = {
|
|
246
|
+
name: 'goto',
|
|
247
|
+
description: 'Navigate to a URL',
|
|
248
|
+
usage: 'goto <url>',
|
|
249
|
+
examples: [
|
|
250
|
+
'goto https://example.com',
|
|
251
|
+
'goto github.com',
|
|
252
|
+
],
|
|
253
|
+
async execute(agent, args, flags) {
|
|
254
|
+
const url = args[0];
|
|
255
|
+
if (!url) {
|
|
256
|
+
throw new CLIError('URL required');
|
|
257
|
+
}
|
|
258
|
+
await agent.navigate(url);
|
|
259
|
+
return { success: true, message: `Navigated to ${url}` };
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// commands/snapshot.ts
|
|
264
|
+
export const snapshotCommand: CommandHandler = {
|
|
265
|
+
name: 'snapshot',
|
|
266
|
+
description: 'Get page accessibility tree',
|
|
267
|
+
usage: 'snapshot [--refs-only]',
|
|
268
|
+
async execute(agent, args, flags) {
|
|
269
|
+
const result = await agent.execute({
|
|
270
|
+
id: generateId(),
|
|
271
|
+
action: 'snapshot'
|
|
272
|
+
});
|
|
273
|
+
return {
|
|
274
|
+
success: true,
|
|
275
|
+
data: result.data.tree
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### 4. Output Formatter (`formatter.ts`)
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
export function formatResult(result: CommandResult): FormattedOutput {
|
|
285
|
+
if (result.success) {
|
|
286
|
+
return {
|
|
287
|
+
type: 'success',
|
|
288
|
+
content: result.message || formatData(result.data)
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
type: 'error',
|
|
293
|
+
content: `Error: ${result.error}`
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function formatData(data: unknown): string {
|
|
298
|
+
// Format snapshots, screenshots, etc.
|
|
299
|
+
if (typeof data === 'string') return data;
|
|
300
|
+
return JSON.stringify(data, null, 2);
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### 5. Terminal UI (`terminal/Terminal.ts`)
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
export class Terminal {
|
|
308
|
+
private state: TerminalState;
|
|
309
|
+
private config: TerminalConfig;
|
|
310
|
+
private onExecute: (command: string) => Promise<string>;
|
|
311
|
+
|
|
312
|
+
constructor(container: HTMLElement, config: Partial<TerminalConfig>) {
|
|
313
|
+
this.config = { ...defaultConfig, ...config };
|
|
314
|
+
this.state = initialState();
|
|
315
|
+
this.render(container);
|
|
316
|
+
this.bindEvents();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async handleInput(input: string): Promise<void> {
|
|
320
|
+
this.appendHistory({ type: 'input', content: `$ ${input}` });
|
|
321
|
+
try {
|
|
322
|
+
const output = await this.onExecute(input);
|
|
323
|
+
this.appendHistory({ type: 'output', content: output });
|
|
324
|
+
} catch (error) {
|
|
325
|
+
this.appendHistory({ type: 'error', content: error.message });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private bindEvents(): void {
|
|
330
|
+
// Keyboard handling: Enter, Up, Down, Tab, etc.
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private render(container: HTMLElement): void {
|
|
334
|
+
// Create terminal UI elements
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## File Structure
|
|
340
|
+
|
|
341
|
+
```
|
|
342
|
+
packages/
|
|
343
|
+
cli/
|
|
344
|
+
package.json
|
|
345
|
+
tsconfig.json
|
|
346
|
+
src/
|
|
347
|
+
index.ts # Main exports
|
|
348
|
+
types.ts # TypeScript types
|
|
349
|
+
parser.ts # Command parser
|
|
350
|
+
executor.ts # Command executor
|
|
351
|
+
formatter.ts # Output formatting
|
|
352
|
+
errors.ts # CLI errors
|
|
353
|
+
commands/
|
|
354
|
+
index.ts # Command registry
|
|
355
|
+
goto.ts
|
|
356
|
+
back.ts
|
|
357
|
+
forward.ts
|
|
358
|
+
reload.ts
|
|
359
|
+
snapshot.ts
|
|
360
|
+
screenshot.ts
|
|
361
|
+
click.ts
|
|
362
|
+
dblclick.ts
|
|
363
|
+
type.ts
|
|
364
|
+
fill.ts
|
|
365
|
+
clear.ts
|
|
366
|
+
check.ts
|
|
367
|
+
uncheck.ts
|
|
368
|
+
select.ts
|
|
369
|
+
hover.ts
|
|
370
|
+
scroll.ts
|
|
371
|
+
press.ts
|
|
372
|
+
wait.ts
|
|
373
|
+
eval.ts
|
|
374
|
+
tabs.ts
|
|
375
|
+
tab.ts
|
|
376
|
+
newtab.ts
|
|
377
|
+
closetab.ts
|
|
378
|
+
help.ts
|
|
379
|
+
terminal/
|
|
380
|
+
index.ts # Terminal exports
|
|
381
|
+
Terminal.ts # Terminal class
|
|
382
|
+
renderer.ts # DOM rendering
|
|
383
|
+
history.ts # Command history
|
|
384
|
+
completion.ts # Tab completion
|
|
385
|
+
styles.css # Terminal styles
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
## Example Usage
|
|
389
|
+
|
|
390
|
+
### Interactive Session
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
$ goto https://github.com
|
|
394
|
+
✓ Navigated to https://github.com
|
|
395
|
+
|
|
396
|
+
$ snapshot
|
|
397
|
+
- banner
|
|
398
|
+
- link 'Homepage' [@ref:1]
|
|
399
|
+
- navigation
|
|
400
|
+
- link 'Product' [@ref:2]
|
|
401
|
+
- link 'Solutions' [@ref:3]
|
|
402
|
+
- link 'Resources' [@ref:4]
|
|
403
|
+
- textbox 'Search or jump to...' [@ref:5]
|
|
404
|
+
- link 'Sign in' [@ref:6]
|
|
405
|
+
- link 'Sign up' [@ref:7]
|
|
406
|
+
- main
|
|
407
|
+
- heading 'Let's build from here' level=1
|
|
408
|
+
- textbox 'Search code...' [@ref:8]
|
|
409
|
+
|
|
410
|
+
$ click @ref:6
|
|
411
|
+
✓ Clicked: link 'Sign in'
|
|
412
|
+
|
|
413
|
+
$ snapshot
|
|
414
|
+
- main
|
|
415
|
+
- heading 'Sign in to GitHub' level=1
|
|
416
|
+
- textbox 'Username or email' [@ref:1]
|
|
417
|
+
- textbox 'Password' [@ref:2]
|
|
418
|
+
- button 'Sign in' [@ref:3]
|
|
419
|
+
- link 'Forgot password?' [@ref:4]
|
|
420
|
+
|
|
421
|
+
$ type @ref:1 myusername
|
|
422
|
+
✓ Typed "myusername" into: textbox 'Username or email'
|
|
423
|
+
|
|
424
|
+
$ fill @ref:2 mypassword
|
|
425
|
+
✓ Filled: textbox 'Password'
|
|
426
|
+
|
|
427
|
+
$ click @ref:3
|
|
428
|
+
✓ Clicked: button 'Sign in'
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Programmatic API (within extension)
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
// In popup.ts or background.ts
|
|
435
|
+
import { createCLI } from '@btcp/cli';
|
|
436
|
+
import { BackgroundAgent } from '@btcp/extension';
|
|
437
|
+
|
|
438
|
+
const agent = new BackgroundAgent();
|
|
439
|
+
const cli = createCLI(agent);
|
|
440
|
+
|
|
441
|
+
// Execute commands programmatically from extension code
|
|
442
|
+
await cli.execute('goto https://example.com');
|
|
443
|
+
const snapshot = await cli.execute('snapshot');
|
|
444
|
+
await cli.execute('click @ref:1');
|
|
445
|
+
|
|
446
|
+
// Or use the Client API for structured access
|
|
447
|
+
import { createClient } from '@btcp/extension';
|
|
448
|
+
const client = createClient();
|
|
449
|
+
await client.navigate('https://example.com');
|
|
450
|
+
const result = await client.snapshot();
|
|
451
|
+
await client.click('@ref:1');
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Implementation Timeline
|
|
455
|
+
|
|
456
|
+
### Milestone 1: Core CLI Package
|
|
457
|
+
- [ ] Set up @btcp/cli package structure
|
|
458
|
+
- [ ] Implement command parser
|
|
459
|
+
- [ ] Implement command executor
|
|
460
|
+
- [ ] Implement core navigation commands (goto, back, forward, reload)
|
|
461
|
+
- [ ] Implement snapshot command
|
|
462
|
+
- [ ] Add basic output formatting
|
|
463
|
+
|
|
464
|
+
### Milestone 2: DOM Commands
|
|
465
|
+
- [ ] Implement click, dblclick
|
|
466
|
+
- [ ] Implement type, fill, clear
|
|
467
|
+
- [ ] Implement check, uncheck, select
|
|
468
|
+
- [ ] Implement hover, scroll
|
|
469
|
+
- [ ] Implement press (keyboard)
|
|
470
|
+
- [ ] Implement wait, eval
|
|
471
|
+
|
|
472
|
+
### Milestone 3: Tab Management
|
|
473
|
+
- [ ] Implement tabs, tab, newtab, closetab
|
|
474
|
+
- [ ] Add multi-tab command support
|
|
475
|
+
|
|
476
|
+
### Milestone 4: Terminal UI
|
|
477
|
+
- [ ] Create terminal component
|
|
478
|
+
- [ ] Implement command input handling
|
|
479
|
+
- [ ] Add command history (up/down)
|
|
480
|
+
- [ ] Add tab completion
|
|
481
|
+
- [ ] Style terminal UI
|
|
482
|
+
- [ ] Integrate with extension popup
|
|
483
|
+
|
|
484
|
+
### Milestone 5: Polish & Documentation
|
|
485
|
+
- [ ] Add help command with usage info
|
|
486
|
+
- [ ] Add command validation and helpful errors
|
|
487
|
+
- [ ] Write documentation
|
|
488
|
+
- [ ] Add tests
|
|
489
|
+
|
|
490
|
+
## Conclusion
|
|
491
|
+
|
|
492
|
+
This design provides a pure in-browser CLI implementation that:
|
|
493
|
+
|
|
494
|
+
1. **Mimics agent-browser** with similar command syntax
|
|
495
|
+
2. **Runs entirely within Chrome extension** - no external processes
|
|
496
|
+
3. **Integrates seamlessly** with existing @btcp/core and @btcp/extension packages
|
|
497
|
+
4. **Provides terminal UI** in extension popup/panel for interactive use
|
|
498
|
+
5. **Supports programmatic API** for AI agents within the browser
|
|
499
|
+
|
|
500
|
+
The implementation starts with the core CLI package (parser, executor, commands), then adds the terminal UI component for the extension.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Latest Changes
|
|
4
|
+
|
|
5
|
+
### Session Management with Auto-Created Blank Tab
|
|
6
|
+
|
|
7
|
+
**What Changed:**
|
|
8
|
+
- When you click "Start New Session", it now **creates a new blank tab** and adds it to the session
|
|
9
|
+
- Previously used the current active tab, which could interrupt your workflow
|
|
10
|
+
- The new blank tab is ready for automation without affecting existing tabs
|
|
11
|
+
|
|
12
|
+
**Why:**
|
|
13
|
+
- Clean slate for each session
|
|
14
|
+
- Doesn't interfere with tabs you already have open
|
|
15
|
+
- Clear starting point for automation
|
|
16
|
+
|
|
17
|
+
**How It Works:**
|
|
18
|
+
```javascript
|
|
19
|
+
// User clicks "Start New Session"
|
|
20
|
+
await client.groupCreate({ title: "BTCP Session 1" });
|
|
21
|
+
|
|
22
|
+
// This now:
|
|
23
|
+
// 1. Creates a new blank tab (about:blank)
|
|
24
|
+
// 2. Adds it to a blue tab group labeled "BTCP Session 1"
|
|
25
|
+
// 3. Makes it the active tab
|
|
26
|
+
// 4. Ready for navigation/automation
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**User Experience:**
|
|
30
|
+
1. Click "Start New Session"
|
|
31
|
+
2. New blank tab opens in a blue group
|
|
32
|
+
3. All subsequent operations work in this session
|
|
33
|
+
4. Your existing tabs remain untouched
|
|
34
|
+
|
|
35
|
+
### Session-Only Mode
|
|
36
|
+
|
|
37
|
+
**What Changed:**
|
|
38
|
+
- Extension **only manages tabs within its session/tab group**
|
|
39
|
+
- All operations require an active session
|
|
40
|
+
- No operations allowed outside the session
|
|
41
|
+
|
|
42
|
+
**Operations Affected:**
|
|
43
|
+
- `listTabs()` - Only shows session tabs
|
|
44
|
+
- `tabNew()` - Only creates in session
|
|
45
|
+
- `closeTab()` - Only closes session tabs
|
|
46
|
+
- `switchTab()` - Only switches to session tabs
|
|
47
|
+
- `navigate()` - Only navigates session tabs
|
|
48
|
+
- All DOM operations - Only in session tabs
|
|
49
|
+
|
|
50
|
+
**Error Messages:**
|
|
51
|
+
Without a session:
|
|
52
|
+
```
|
|
53
|
+
Error: "No active session. Create a session first to manage tabs."
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Trying to access outside tabs:
|
|
57
|
+
```
|
|
58
|
+
Error: "Cannot switch to tab: tab is not in the active session"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Benefits:**
|
|
62
|
+
- ✅ Better security and isolation
|
|
63
|
+
- ✅ Clear visual boundaries (tab groups)
|
|
64
|
+
- ✅ Explicit user consent required
|
|
65
|
+
- ✅ Protects personal tabs from automation
|
|
66
|
+
- ✅ Clean separation of contexts
|
|
67
|
+
|
|
68
|
+
## Testing the Changes
|
|
69
|
+
|
|
70
|
+
### Test 1: New Blank Tab on Session Start
|
|
71
|
+
```
|
|
72
|
+
1. Open extension popup
|
|
73
|
+
2. Click "Start New Session"
|
|
74
|
+
3. Expected: New blank tab opens in blue group
|
|
75
|
+
4. Verify: Tab shows "about:blank" URL
|
|
76
|
+
5. Verify: Tab group labeled "BTCP Session 1"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Test 2: Session Isolation
|
|
80
|
+
```
|
|
81
|
+
1. Have 3 regular tabs open (A, B, C)
|
|
82
|
+
2. Start new session → new blank tab D in group
|
|
83
|
+
3. Click "List Tabs" → should only show tab D
|
|
84
|
+
4. Create new tab → should join session with D
|
|
85
|
+
5. Tabs A, B, C remain untouched
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Test 3: Operations Require Session
|
|
89
|
+
```
|
|
90
|
+
1. Open popup without starting session
|
|
91
|
+
2. Click "List Tabs" → Error shown
|
|
92
|
+
3. Click "New Tab" → Error shown
|
|
93
|
+
4. Start session → Operations now work
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Migration Guide
|
|
97
|
+
|
|
98
|
+
### Before
|
|
99
|
+
```javascript
|
|
100
|
+
// Extension used current active tab
|
|
101
|
+
await client.groupCreate();
|
|
102
|
+
// Your current tab got grouped
|
|
103
|
+
|
|
104
|
+
// Could access any tab
|
|
105
|
+
const tabs = await client.listTabs(); // All tabs
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### After
|
|
109
|
+
```javascript
|
|
110
|
+
// Extension creates new blank tab
|
|
111
|
+
await client.groupCreate();
|
|
112
|
+
// New blank tab created in group
|
|
113
|
+
// Your existing tabs untouched
|
|
114
|
+
|
|
115
|
+
// Only session tabs accessible
|
|
116
|
+
const tabs = await client.listTabs(); // Only session tabs
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### If You Want to Group Existing Tab
|
|
120
|
+
```javascript
|
|
121
|
+
// Get tab you want to add
|
|
122
|
+
const targetTab = await chrome.tabs.get(tabId);
|
|
123
|
+
|
|
124
|
+
// Create session with that tab
|
|
125
|
+
await client.groupCreate({ tabIds: [targetTab.id] });
|
|
126
|
+
|
|
127
|
+
// Or add to existing session
|
|
128
|
+
const session = await client.sessionGetCurrent();
|
|
129
|
+
await client.groupAddTabs(session.groupId, [targetTab.id]);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Visual Guide
|
|
133
|
+
|
|
134
|
+
### Session Start Flow
|
|
135
|
+
|
|
136
|
+
**Before:**
|
|
137
|
+
```
|
|
138
|
+
Browser tabs: [Gmail] [GitHub] [YouTube*]
|
|
139
|
+
↑ active
|
|
140
|
+
User clicks "Start New Session"
|
|
141
|
+
Result: [Gmail] [GitHub] [(YouTube* in blue group)]
|
|
142
|
+
↑ YouTube grouped, disrupts workflow
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**After:**
|
|
146
|
+
```
|
|
147
|
+
Browser tabs: [Gmail] [GitHub] [YouTube]
|
|
148
|
+
User clicks "Start New Session"
|
|
149
|
+
Result: [Gmail] [GitHub] [YouTube] [(blank* in blue group)]
|
|
150
|
+
↑ New tab, clean slate
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Session Isolation
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
Before session:
|
|
157
|
+
[Tab A] [Tab B] [Tab C] [Tab D] [Tab E]
|
|
158
|
+
↑ Extension could access all tabs
|
|
159
|
+
|
|
160
|
+
After session:
|
|
161
|
+
[Tab A] [Tab B] [Tab C] | [Session: Tab D] [Tab E*]
|
|
162
|
+
↑ Blue group boundary
|
|
163
|
+
Extension only accesses D and E
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Breaking Changes
|
|
167
|
+
|
|
168
|
+
### 1. Session Required
|
|
169
|
+
**Before:** Could use extension without session
|
|
170
|
+
**After:** Must create session first
|
|
171
|
+
|
|
172
|
+
**Fix:** Always call `groupCreate()` before other operations
|
|
173
|
+
|
|
174
|
+
### 2. No Universal Tab Access
|
|
175
|
+
**Before:** Could access any tab in window
|
|
176
|
+
**After:** Can only access tabs in session
|
|
177
|
+
|
|
178
|
+
**Fix:** Add tabs to session explicitly or create new tabs via extension
|
|
179
|
+
|
|
180
|
+
### 3. Active Tab Behavior
|
|
181
|
+
**Before:** Session used current active tab
|
|
182
|
+
**After:** Session creates new blank tab
|
|
183
|
+
|
|
184
|
+
**Fix:** If you need to add existing tab, use `groupCreate({ tabIds: [existingTabId] })`
|
|
185
|
+
|
|
186
|
+
## Rollback
|
|
187
|
+
|
|
188
|
+
If you need the old behavior, you can modify the source:
|
|
189
|
+
|
|
190
|
+
**Remove session requirement:**
|
|
191
|
+
In `background.ts`, change validation:
|
|
192
|
+
```typescript
|
|
193
|
+
if (sessionGroupId === null) {
|
|
194
|
+
return true; // Allow without session
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Use current tab instead of new:**
|
|
199
|
+
In `session-manager.ts`, revert to:
|
|
200
|
+
```typescript
|
|
201
|
+
const [activeTab] = await chrome.tabs.query({ active: true });
|
|
202
|
+
targetTabIds = [activeTab.id];
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Version Info
|
|
206
|
+
|
|
207
|
+
- **Build Date:** 2026-01-16
|
|
208
|
+
- **Session Manager:** Creates blank tab by default
|
|
209
|
+
- **Access Mode:** Session-only (no universal access)
|
|
210
|
+
- **Backward Compatible:** No (requires session for all operations)
|