btcp-browser-agent 0.1.3 → 0.1.6
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 +10 -13
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -2
- package/package.json +1 -1
- package/packages/core/dist/actions.d.ts +6 -0
- package/packages/core/dist/actions.js +15 -3
- package/packages/core/dist/index.d.ts +5 -12
- package/packages/core/dist/index.js +5 -12
- package/packages/core/dist/types.d.ts +13 -10
- package/packages/extension/dist/background.d.ts +6 -4
- package/packages/extension/dist/background.js +27 -11
- package/packages/extension/dist/content.js +5 -4
- package/packages/extension/dist/index.d.ts +5 -1
- package/packages/extension/dist/index.js +9 -5
- package/packages/extension/dist/session-manager.js +2 -3
- package/packages/extension/dist/types.d.ts +2 -1
package/README.md
CHANGED
|
@@ -86,11 +86,11 @@ import { createContentAgent } from 'btcp-browser-agent';
|
|
|
86
86
|
const agent = createContentAgent();
|
|
87
87
|
|
|
88
88
|
// Take a snapshot
|
|
89
|
-
const { data } = await agent.execute({
|
|
89
|
+
const { data } = await agent.execute({ action: 'snapshot' });
|
|
90
90
|
console.log(data.tree); // Accessibility tree with refs
|
|
91
91
|
|
|
92
92
|
// Click an element using ref from snapshot
|
|
93
|
-
await agent.execute({
|
|
93
|
+
await agent.execute({ action: 'click', selector: '@ref:5' });
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
**Popup (sending commands via messaging):**
|
|
@@ -116,11 +116,11 @@ import { createContentAgent } from 'btcp-browser-agent';
|
|
|
116
116
|
const agent = createContentAgent();
|
|
117
117
|
|
|
118
118
|
// Take a snapshot
|
|
119
|
-
const { data } = await agent.execute({
|
|
119
|
+
const { data } = await agent.execute({ action: 'snapshot' });
|
|
120
120
|
|
|
121
121
|
// Interact with elements
|
|
122
|
-
await agent.execute({
|
|
123
|
-
await agent.execute({
|
|
122
|
+
await agent.execute({ action: 'click', selector: '@ref:5' });
|
|
123
|
+
await agent.execute({ action: 'fill', selector: '@ref:3', value: 'Hello' });
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
## API Reference
|
|
@@ -150,7 +150,7 @@ await agent.reload();
|
|
|
150
150
|
const screenshot = await agent.screenshot({ format: 'png' });
|
|
151
151
|
|
|
152
152
|
// Execute commands (routes to ContentAgent for DOM operations)
|
|
153
|
-
await agent.execute({
|
|
153
|
+
await agent.execute({ action: 'click', selector: '#submit' });
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
#### Multi-Tab Operations
|
|
@@ -167,7 +167,7 @@ await githubTab.click('@ref:5');
|
|
|
167
167
|
|
|
168
168
|
// Method 2: Specify tabId in execute
|
|
169
169
|
await agent.execute(
|
|
170
|
-
{
|
|
170
|
+
{ action: 'getText', selector: 'h1' },
|
|
171
171
|
{ tabId: tab2.id }
|
|
172
172
|
);
|
|
173
173
|
|
|
@@ -184,10 +184,7 @@ import { createContentAgent } from 'btcp-browser-agent';
|
|
|
184
184
|
const agent = createContentAgent();
|
|
185
185
|
|
|
186
186
|
// Execute commands
|
|
187
|
-
const response = await agent.execute({
|
|
188
|
-
id: 'cmd1',
|
|
189
|
-
action: 'snapshot'
|
|
190
|
-
});
|
|
187
|
+
const response = await agent.execute({ action: 'snapshot' });
|
|
191
188
|
```
|
|
192
189
|
|
|
193
190
|
#### Available Actions
|
|
@@ -238,11 +235,11 @@ const response = await agent.execute({
|
|
|
238
235
|
The `snapshot` action returns element references for stable selection:
|
|
239
236
|
|
|
240
237
|
```typescript
|
|
241
|
-
const { data } = await agent.execute({
|
|
238
|
+
const { data } = await agent.execute({ action: 'snapshot' });
|
|
242
239
|
// data.tree: "BUTTON 'Submit' [@ref:5]\nTEXTBOX 'Email' [@ref:3]"
|
|
243
240
|
|
|
244
241
|
// Use refs in subsequent commands
|
|
245
|
-
await agent.execute({
|
|
242
|
+
await agent.execute({ action: 'click', selector: '@ref:5' });
|
|
246
243
|
```
|
|
247
244
|
|
|
248
245
|
## Architecture
|
package/dist/index.d.ts
CHANGED
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
* ```typescript
|
|
26
26
|
* import { createContentAgent } from '@btcp/browser-agent';
|
|
27
27
|
* const agent = createContentAgent();
|
|
28
|
-
* await agent.execute({
|
|
28
|
+
* await agent.execute({ action: 'snapshot' });
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
export { createContentAgent, type ContentAgent, DOMActions, createSnapshot, createRefMap, createSimpleRefMap, type Command, type Response, type SnapshotData, type BoundingBox, type RefMap, type Modifier, } from '../packages/core/dist/index.js';
|
|
32
32
|
export type { ExtensionMessage, ExtensionResponse, TabInfo, ChromeTab, ExtensionCommand, } from '../packages/extension/dist/index.js';
|
|
33
|
-
export { BackgroundAgent, getBackgroundAgent, setupMessageListener, createClient, type Client, } from '../packages/extension/dist/index.js';
|
|
33
|
+
export { BackgroundAgent, getBackgroundAgent, setupMessageListener, createClient, generateCommandId, type Client, BrowserAgent, getBrowserAgent, } from '../packages/extension/dist/index.js';
|
|
34
34
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
* ```typescript
|
|
26
26
|
* import { createContentAgent } from '@btcp/browser-agent';
|
|
27
27
|
* const agent = createContentAgent();
|
|
28
|
-
* await agent.execute({
|
|
28
|
+
* await agent.execute({ action: 'snapshot' });
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
// Re-export everything from core (for standalone usage)
|
|
32
32
|
export { createContentAgent, DOMActions, createSnapshot, createRefMap, createSimpleRefMap, } from '../packages/core/dist/index.js';
|
|
33
33
|
// Re-export extension functions
|
|
34
|
-
export { BackgroundAgent, getBackgroundAgent, setupMessageListener, createClient,
|
|
34
|
+
export { BackgroundAgent, getBackgroundAgent, setupMessageListener, createClient, generateCommandId,
|
|
35
|
+
// Deprecated aliases for backwards compatibility
|
|
36
|
+
BrowserAgent, getBrowserAgent, } from '../packages/extension/dist/index.js';
|
|
35
37
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "btcp-browser-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Give AI agents the power to control browsers. A foundation for building agentic systems with smart DOM snapshots and stable element references.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* Element interaction handlers using native browser APIs.
|
|
5
5
|
*/
|
|
6
6
|
import type { Command, Response, RefMap } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Generate a unique command ID
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateCommandId(): string;
|
|
7
11
|
/**
|
|
8
12
|
* DOM Actions executor
|
|
9
13
|
*/
|
|
@@ -18,6 +22,8 @@ export declare class DOMActions {
|
|
|
18
22
|
constructor(doc: Document, win: Window, refMap: RefMap);
|
|
19
23
|
/**
|
|
20
24
|
* Execute a command and return a response
|
|
25
|
+
*
|
|
26
|
+
* The command ID is auto-generated internally - users don't need to provide it.
|
|
21
27
|
*/
|
|
22
28
|
execute(command: Command): Promise<Response>;
|
|
23
29
|
private dispatch;
|
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { createSnapshot } from './snapshot.js';
|
|
7
7
|
import { DetailedError, createElementNotFoundError, createElementNotCompatibleError, createTimeoutError, createInvalidParametersError, } from './errors.js';
|
|
8
|
+
// Command ID counter for auto-generated IDs
|
|
9
|
+
let commandIdCounter = 0;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a unique command ID
|
|
12
|
+
*/
|
|
13
|
+
export function generateCommandId() {
|
|
14
|
+
return `cmd_${Date.now()}_${commandIdCounter++}`;
|
|
15
|
+
}
|
|
8
16
|
/**
|
|
9
17
|
* DOM Actions executor
|
|
10
18
|
*/
|
|
@@ -23,18 +31,22 @@ export class DOMActions {
|
|
|
23
31
|
}
|
|
24
32
|
/**
|
|
25
33
|
* Execute a command and return a response
|
|
34
|
+
*
|
|
35
|
+
* The command ID is auto-generated internally - users don't need to provide it.
|
|
26
36
|
*/
|
|
27
37
|
async execute(command) {
|
|
38
|
+
// Auto-generate ID if not provided
|
|
39
|
+
const id = command.id || generateCommandId();
|
|
28
40
|
try {
|
|
29
41
|
const data = await this.dispatch(command);
|
|
30
|
-
return { id
|
|
42
|
+
return { id, success: true, data };
|
|
31
43
|
}
|
|
32
44
|
catch (error) {
|
|
33
45
|
const message = error instanceof Error ? error.message : String(error);
|
|
34
46
|
// Include structured error data if available
|
|
35
47
|
if (error instanceof DetailedError) {
|
|
36
48
|
return {
|
|
37
|
-
id
|
|
49
|
+
id,
|
|
38
50
|
success: false,
|
|
39
51
|
error: message,
|
|
40
52
|
errorCode: error.code,
|
|
@@ -42,7 +54,7 @@ export class DOMActions {
|
|
|
42
54
|
suggestions: error.suggestions,
|
|
43
55
|
};
|
|
44
56
|
}
|
|
45
|
-
return { id
|
|
57
|
+
return { id, success: false, error: message };
|
|
46
58
|
}
|
|
47
59
|
}
|
|
48
60
|
async dispatch(command) {
|
|
@@ -11,17 +11,10 @@
|
|
|
11
11
|
* const agent = createContentAgent(document, window);
|
|
12
12
|
*
|
|
13
13
|
* // Take a snapshot
|
|
14
|
-
* const snapshot = await agent.execute({
|
|
15
|
-
* id: '1',
|
|
16
|
-
* action: 'snapshot'
|
|
17
|
-
* });
|
|
14
|
+
* const snapshot = await agent.execute({ action: 'snapshot' });
|
|
18
15
|
*
|
|
19
16
|
* // Click an element
|
|
20
|
-
* await agent.execute({
|
|
21
|
-
* id: '2',
|
|
22
|
-
* action: 'click',
|
|
23
|
-
* selector: '@ref:5' // From snapshot
|
|
24
|
-
* });
|
|
17
|
+
* await agent.execute({ action: 'click', selector: '@ref:5' });
|
|
25
18
|
* ```
|
|
26
19
|
*/
|
|
27
20
|
import type { Command, Response, RefMap } from './types.js';
|
|
@@ -29,7 +22,7 @@ export * from './types.js';
|
|
|
29
22
|
export * from './errors.js';
|
|
30
23
|
export { createSnapshot } from './snapshot.js';
|
|
31
24
|
export { createRefMap, createSimpleRefMap } from './ref-map.js';
|
|
32
|
-
export { DOMActions } from './actions.js';
|
|
25
|
+
export { DOMActions, generateCommandId } from './actions.js';
|
|
33
26
|
/**
|
|
34
27
|
* ContentAgent - DOM automation agent that runs in content script context
|
|
35
28
|
*
|
|
@@ -83,10 +76,10 @@ export interface ContentAgent {
|
|
|
83
76
|
* const agent = createContentAgent();
|
|
84
77
|
*
|
|
85
78
|
* // Take a snapshot of the page
|
|
86
|
-
* const { data } = await agent.execute({
|
|
79
|
+
* const { data } = await agent.execute({ action: 'snapshot' });
|
|
87
80
|
*
|
|
88
81
|
* // Click an element using ref from snapshot
|
|
89
|
-
* await agent.execute({
|
|
82
|
+
* await agent.execute({ action: 'click', selector: '@ref:5' });
|
|
90
83
|
* ```
|
|
91
84
|
*/
|
|
92
85
|
export declare function createContentAgent(doc?: Document, win?: Window): ContentAgent;
|
|
@@ -11,17 +11,10 @@
|
|
|
11
11
|
* const agent = createContentAgent(document, window);
|
|
12
12
|
*
|
|
13
13
|
* // Take a snapshot
|
|
14
|
-
* const snapshot = await agent.execute({
|
|
15
|
-
* id: '1',
|
|
16
|
-
* action: 'snapshot'
|
|
17
|
-
* });
|
|
14
|
+
* const snapshot = await agent.execute({ action: 'snapshot' });
|
|
18
15
|
*
|
|
19
16
|
* // Click an element
|
|
20
|
-
* await agent.execute({
|
|
21
|
-
* id: '2',
|
|
22
|
-
* action: 'click',
|
|
23
|
-
* selector: '@ref:5' // From snapshot
|
|
24
|
-
* });
|
|
17
|
+
* await agent.execute({ action: 'click', selector: '@ref:5' });
|
|
25
18
|
* ```
|
|
26
19
|
*/
|
|
27
20
|
import { DOMActions } from './actions.js';
|
|
@@ -30,7 +23,7 @@ export * from './types.js';
|
|
|
30
23
|
export * from './errors.js';
|
|
31
24
|
export { createSnapshot } from './snapshot.js';
|
|
32
25
|
export { createRefMap, createSimpleRefMap } from './ref-map.js';
|
|
33
|
-
export { DOMActions } from './actions.js';
|
|
26
|
+
export { DOMActions, generateCommandId } from './actions.js';
|
|
34
27
|
/**
|
|
35
28
|
* Create a ContentAgent for DOM automation
|
|
36
29
|
*
|
|
@@ -44,10 +37,10 @@ export { DOMActions } from './actions.js';
|
|
|
44
37
|
* const agent = createContentAgent();
|
|
45
38
|
*
|
|
46
39
|
* // Take a snapshot of the page
|
|
47
|
-
* const { data } = await agent.execute({
|
|
40
|
+
* const { data } = await agent.execute({ action: 'snapshot' });
|
|
48
41
|
*
|
|
49
42
|
* // Click an element using ref from snapshot
|
|
50
|
-
* await agent.execute({
|
|
43
|
+
* await agent.execute({ action: 'click', selector: '@ref:5' });
|
|
51
44
|
* ```
|
|
52
45
|
*/
|
|
53
46
|
export function createContentAgent(doc = document, win = window) {
|
|
@@ -5,8 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export type CoreAction = 'click' | 'dblclick' | 'type' | 'fill' | 'clear' | 'check' | 'uncheck' | 'select' | 'focus' | 'blur' | 'hover' | 'scroll' | 'scrollIntoView' | 'snapshot' | 'querySelector' | 'querySelectorAll' | 'getText' | 'getAttribute' | 'getProperty' | 'getBoundingBox' | 'isVisible' | 'isEnabled' | 'isChecked' | 'press' | 'keyDown' | 'keyUp' | 'wait' | 'evaluate' | 'validateElement' | 'validateRefs' | 'highlight' | 'clearHighlight';
|
|
7
7
|
export interface BaseCommand {
|
|
8
|
+
/** Optional command ID. Auto-generated if not provided. */
|
|
9
|
+
id?: string;
|
|
10
|
+
action: CoreAction;
|
|
11
|
+
}
|
|
12
|
+
export interface InternalCommand {
|
|
8
13
|
id: string;
|
|
9
14
|
action: CoreAction;
|
|
15
|
+
[key: string]: unknown;
|
|
10
16
|
}
|
|
11
17
|
export type Selector = string;
|
|
12
18
|
export interface ClickCommand extends BaseCommand {
|
|
@@ -177,7 +183,6 @@ export interface EvaluateCommand extends BaseCommand {
|
|
|
177
183
|
* @example Pre-validate before typing
|
|
178
184
|
* ```typescript
|
|
179
185
|
* const validation = await agent.execute({
|
|
180
|
-
* id: 'v1',
|
|
181
186
|
* action: 'validateElement',
|
|
182
187
|
* selector: '#username',
|
|
183
188
|
* capabilities: ['editable']
|
|
@@ -185,7 +190,6 @@ export interface EvaluateCommand extends BaseCommand {
|
|
|
185
190
|
*
|
|
186
191
|
* if (validation.data.compatible) {
|
|
187
192
|
* await agent.execute({
|
|
188
|
-
* id: 'a1',
|
|
189
193
|
* action: 'type',
|
|
190
194
|
* selector: '#username',
|
|
191
195
|
* text: 'user@example.com'
|
|
@@ -213,20 +217,19 @@ export interface ValidateElementCommand extends BaseCommand {
|
|
|
213
217
|
* @example Check ref validity
|
|
214
218
|
* ```typescript
|
|
215
219
|
* const validation = await agent.execute({
|
|
216
|
-
* id: 'v1',
|
|
217
220
|
* action: 'validateRefs',
|
|
218
221
|
* refs: ['@ref:0', '@ref:1', '@ref:2']
|
|
219
222
|
* });
|
|
220
223
|
*
|
|
221
224
|
* // Use only valid refs
|
|
222
225
|
* for (const ref of validation.data.valid) {
|
|
223
|
-
* await agent.execute({
|
|
226
|
+
* await agent.execute({ action: 'click', selector: ref });
|
|
224
227
|
* }
|
|
225
228
|
*
|
|
226
229
|
* // Handle invalid refs
|
|
227
230
|
* if (validation.data.invalid.length > 0) {
|
|
228
231
|
* // Take new snapshot to get fresh refs
|
|
229
|
-
* await agent.execute({
|
|
232
|
+
* await agent.execute({ action: 'snapshot' });
|
|
230
233
|
* }
|
|
231
234
|
* ```
|
|
232
235
|
*/
|
|
@@ -245,17 +248,17 @@ export interface ValidateRefsCommand extends BaseCommand {
|
|
|
245
248
|
* @example Highlight elements after snapshot
|
|
246
249
|
* ```typescript
|
|
247
250
|
* // Take a snapshot first
|
|
248
|
-
* await agent.execute({
|
|
251
|
+
* await agent.execute({ action: 'snapshot' });
|
|
249
252
|
*
|
|
250
253
|
* // Show visual highlights
|
|
251
|
-
* await agent.execute({
|
|
254
|
+
* await agent.execute({ action: 'highlight' });
|
|
252
255
|
*
|
|
253
256
|
* // Labels now visible on page with @ref:0, @ref:1, etc.
|
|
254
257
|
* // Use the refs to interact with elements
|
|
255
|
-
* await agent.execute({
|
|
258
|
+
* await agent.execute({ action: 'click', selector: '@ref:5' });
|
|
256
259
|
*
|
|
257
260
|
* // Clear highlights when done
|
|
258
|
-
* await agent.execute({
|
|
261
|
+
* await agent.execute({ action: 'clearHighlight' });
|
|
259
262
|
* ```
|
|
260
263
|
*/
|
|
261
264
|
export interface HighlightCommand extends BaseCommand {
|
|
@@ -268,7 +271,7 @@ export interface HighlightCommand extends BaseCommand {
|
|
|
268
271
|
*
|
|
269
272
|
* @example Clear highlights
|
|
270
273
|
* ```typescript
|
|
271
|
-
* await agent.execute({
|
|
274
|
+
* await agent.execute({ action: 'clearHighlight' });
|
|
272
275
|
* ```
|
|
273
276
|
*/
|
|
274
277
|
export interface ClearHighlightCommand extends BaseCommand {
|
|
@@ -43,7 +43,7 @@ export interface TabHandle {
|
|
|
43
43
|
* ```typescript
|
|
44
44
|
* const agent = new BackgroundAgent();
|
|
45
45
|
* await agent.navigate('https://example.com');
|
|
46
|
-
* await agent.execute({
|
|
46
|
+
* await agent.execute({ action: 'click', selector: '#submit' });
|
|
47
47
|
* ```
|
|
48
48
|
*
|
|
49
49
|
* @example Multi-tab with explicit tabId
|
|
@@ -59,7 +59,7 @@ export interface TabHandle {
|
|
|
59
59
|
* await agent.tab(tab2.id).snapshot();
|
|
60
60
|
*
|
|
61
61
|
* // Or specify tabId in command
|
|
62
|
-
* await agent.execute({
|
|
62
|
+
* await agent.execute({ action: 'snapshot' }, { tabId: tab2.id });
|
|
63
63
|
* ```
|
|
64
64
|
*/
|
|
65
65
|
export declare class BackgroundAgent {
|
|
@@ -158,17 +158,19 @@ export declare class BackgroundAgent {
|
|
|
158
158
|
* Browser-level commands (navigate, screenshot, tabs) are handled here.
|
|
159
159
|
* DOM-level commands are forwarded to the ContentAgent in the target tab.
|
|
160
160
|
*
|
|
161
|
+
* Command IDs are auto-generated internally - users don't need to provide them.
|
|
162
|
+
*
|
|
161
163
|
* @param command - The command to execute
|
|
162
164
|
* @param options - Optional settings including target tabId
|
|
163
165
|
*
|
|
164
166
|
* @example Default (active tab)
|
|
165
167
|
* ```typescript
|
|
166
|
-
* await browser.execute({
|
|
168
|
+
* await browser.execute({ action: 'snapshot' });
|
|
167
169
|
* ```
|
|
168
170
|
*
|
|
169
171
|
* @example Specific tab
|
|
170
172
|
* ```typescript
|
|
171
|
-
* await browser.execute({
|
|
173
|
+
* await browser.execute({ action: 'snapshot' }, { tabId: 123 });
|
|
172
174
|
* ```
|
|
173
175
|
*/
|
|
174
176
|
execute(command: Command, options?: {
|
|
@@ -12,6 +12,14 @@
|
|
|
12
12
|
* - Routing DOM commands to ContentAgents in target tabs
|
|
13
13
|
*/
|
|
14
14
|
import { SessionManager } from './session-manager.js';
|
|
15
|
+
// Command ID counter for auto-generated IDs
|
|
16
|
+
let bgCommandIdCounter = 0;
|
|
17
|
+
/**
|
|
18
|
+
* Generate a unique command ID for background script
|
|
19
|
+
*/
|
|
20
|
+
function generateBgCommandId() {
|
|
21
|
+
return `bg_${Date.now()}_${bgCommandIdCounter++}`;
|
|
22
|
+
}
|
|
15
23
|
/**
|
|
16
24
|
* BackgroundAgent - High-level browser automation orchestrator
|
|
17
25
|
*
|
|
@@ -23,7 +31,7 @@ import { SessionManager } from './session-manager.js';
|
|
|
23
31
|
* ```typescript
|
|
24
32
|
* const agent = new BackgroundAgent();
|
|
25
33
|
* await agent.navigate('https://example.com');
|
|
26
|
-
* await agent.execute({
|
|
34
|
+
* await agent.execute({ action: 'click', selector: '#submit' });
|
|
27
35
|
* ```
|
|
28
36
|
*
|
|
29
37
|
* @example Multi-tab with explicit tabId
|
|
@@ -39,7 +47,7 @@ import { SessionManager } from './session-manager.js';
|
|
|
39
47
|
* await agent.tab(tab2.id).snapshot();
|
|
40
48
|
*
|
|
41
49
|
* // Or specify tabId in command
|
|
42
|
-
* await agent.execute({
|
|
50
|
+
* await agent.execute({ action: 'snapshot' }, { tabId: tab2.id });
|
|
43
51
|
* ```
|
|
44
52
|
*/
|
|
45
53
|
export class BackgroundAgent {
|
|
@@ -381,31 +389,36 @@ export class BackgroundAgent {
|
|
|
381
389
|
* Browser-level commands (navigate, screenshot, tabs) are handled here.
|
|
382
390
|
* DOM-level commands are forwarded to the ContentAgent in the target tab.
|
|
383
391
|
*
|
|
392
|
+
* Command IDs are auto-generated internally - users don't need to provide them.
|
|
393
|
+
*
|
|
384
394
|
* @param command - The command to execute
|
|
385
395
|
* @param options - Optional settings including target tabId
|
|
386
396
|
*
|
|
387
397
|
* @example Default (active tab)
|
|
388
398
|
* ```typescript
|
|
389
|
-
* await browser.execute({
|
|
399
|
+
* await browser.execute({ action: 'snapshot' });
|
|
390
400
|
* ```
|
|
391
401
|
*
|
|
392
402
|
* @example Specific tab
|
|
393
403
|
* ```typescript
|
|
394
|
-
* await browser.execute({
|
|
404
|
+
* await browser.execute({ action: 'snapshot' }, { tabId: 123 });
|
|
395
405
|
* ```
|
|
396
406
|
*/
|
|
397
407
|
async execute(command, options) {
|
|
408
|
+
// Auto-generate ID if not provided
|
|
409
|
+
const id = command.id || generateBgCommandId();
|
|
410
|
+
const internalCmd = { ...command, id };
|
|
398
411
|
try {
|
|
399
412
|
// Extension commands are handled directly by BrowserAgent
|
|
400
|
-
if (this.isExtensionCommand(
|
|
401
|
-
return this.executeExtensionCommand(
|
|
413
|
+
if (this.isExtensionCommand(internalCmd)) {
|
|
414
|
+
return this.executeExtensionCommand(internalCmd);
|
|
402
415
|
}
|
|
403
416
|
// DOM commands are forwarded to ContentAgent in the target tab
|
|
404
|
-
return this.sendToContentAgent(
|
|
417
|
+
return this.sendToContentAgent(internalCmd, options?.tabId);
|
|
405
418
|
}
|
|
406
419
|
catch (error) {
|
|
407
420
|
return {
|
|
408
|
-
id
|
|
421
|
+
id,
|
|
409
422
|
success: false,
|
|
410
423
|
error: error instanceof Error ? error.message : String(error),
|
|
411
424
|
};
|
|
@@ -415,16 +428,19 @@ export class BackgroundAgent {
|
|
|
415
428
|
* Send a command to the ContentAgent in a specific tab
|
|
416
429
|
*/
|
|
417
430
|
async sendToContentAgent(command, tabId) {
|
|
431
|
+
// Ensure command has an ID for internal use
|
|
432
|
+
const id = command.id || generateBgCommandId();
|
|
433
|
+
const internalCmd = { ...command, id };
|
|
418
434
|
const targetTabId = tabId ?? this.activeTabId ?? (await this.getActiveTab())?.id;
|
|
419
435
|
if (!targetTabId) {
|
|
420
436
|
return {
|
|
421
|
-
id
|
|
437
|
+
id,
|
|
422
438
|
success: false,
|
|
423
439
|
error: 'No active tab for DOM command',
|
|
424
440
|
};
|
|
425
441
|
}
|
|
426
442
|
// Try sending with automatic retry and recovery
|
|
427
|
-
return this.sendMessageWithRetry(targetTabId,
|
|
443
|
+
return this.sendMessageWithRetry(targetTabId, internalCmd);
|
|
428
444
|
}
|
|
429
445
|
/**
|
|
430
446
|
* Send message with automatic content script re-injection on failure
|
|
@@ -717,7 +733,7 @@ export function setupMessageListener() {
|
|
|
717
733
|
sendResponse({
|
|
718
734
|
type: 'btcp:response',
|
|
719
735
|
response: {
|
|
720
|
-
id: msg.command.id,
|
|
736
|
+
id: msg.command.id || 'unknown',
|
|
721
737
|
success: false,
|
|
722
738
|
error: error instanceof Error ? error.message : String(error),
|
|
723
739
|
},
|
|
@@ -40,23 +40,24 @@ async function handleCommand(command) {
|
|
|
40
40
|
return getContentAgent().execute(command);
|
|
41
41
|
}
|
|
42
42
|
// Extension commands that need content script execution
|
|
43
|
+
const id = command.id || 'unknown';
|
|
43
44
|
switch (command.action) {
|
|
44
45
|
case 'getUrl':
|
|
45
46
|
return {
|
|
46
|
-
id
|
|
47
|
+
id,
|
|
47
48
|
success: true,
|
|
48
49
|
data: { url: window.location.href },
|
|
49
50
|
};
|
|
50
51
|
case 'getTitle':
|
|
51
52
|
return {
|
|
52
|
-
id
|
|
53
|
+
id,
|
|
53
54
|
success: true,
|
|
54
55
|
data: { title: document.title },
|
|
55
56
|
};
|
|
56
57
|
default:
|
|
57
58
|
// Forward to background script
|
|
58
59
|
return {
|
|
59
|
-
id
|
|
60
|
+
id,
|
|
60
61
|
success: false,
|
|
61
62
|
error: `Command ${command.action} must be handled by background script`,
|
|
62
63
|
};
|
|
@@ -83,7 +84,7 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
|
83
84
|
sendResponse({
|
|
84
85
|
type: 'btcp:response',
|
|
85
86
|
response: {
|
|
86
|
-
id: msg.command.id,
|
|
87
|
+
id: msg.command.id || 'unknown',
|
|
87
88
|
success: false,
|
|
88
89
|
error: error instanceof Error ? error.message : String(error),
|
|
89
90
|
},
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* import { createContentAgent } from '@btcp/core';
|
|
27
27
|
*
|
|
28
28
|
* const agent = createContentAgent();
|
|
29
|
-
* await agent.execute({
|
|
29
|
+
* await agent.execute({ action: 'snapshot' });
|
|
30
30
|
* ```
|
|
31
31
|
*
|
|
32
32
|
* @example Popup/external usage:
|
|
@@ -202,6 +202,10 @@ export interface Client {
|
|
|
202
202
|
reconnected: boolean;
|
|
203
203
|
}>;
|
|
204
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Generate a unique command ID for BTCP commands
|
|
207
|
+
*/
|
|
208
|
+
export declare function generateCommandId(): string;
|
|
205
209
|
/**
|
|
206
210
|
* Create a client for communicating with the extension
|
|
207
211
|
*
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* import { createContentAgent } from '@btcp/core';
|
|
27
27
|
*
|
|
28
28
|
* const agent = createContentAgent();
|
|
29
|
-
* await agent.execute({
|
|
29
|
+
* await agent.execute({ action: 'snapshot' });
|
|
30
30
|
* ```
|
|
31
31
|
*
|
|
32
32
|
* @example Popup/external usage:
|
|
@@ -49,7 +49,10 @@ _BrowserAgent as BrowserAgent, _getBrowserAgent as getBrowserAgent, };
|
|
|
49
49
|
// Re-export ContentAgent for content script usage
|
|
50
50
|
export { createContentAgent } from '../../core/dist/index.js';
|
|
51
51
|
let commandIdCounter = 0;
|
|
52
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Generate a unique command ID for BTCP commands
|
|
54
|
+
*/
|
|
55
|
+
export function generateCommandId() {
|
|
53
56
|
return `cmd_${Date.now()}_${commandIdCounter++}`;
|
|
54
57
|
}
|
|
55
58
|
/**
|
|
@@ -99,11 +102,12 @@ export function createClient() {
|
|
|
99
102
|
return getAgent().execute(command);
|
|
100
103
|
}
|
|
101
104
|
// In popup/content context, use message passing
|
|
105
|
+
const id = command.id || generateCommandId();
|
|
102
106
|
return new Promise((resolve) => {
|
|
103
|
-
chrome.runtime.sendMessage({ type: 'btcp:command', command }, (response) => {
|
|
107
|
+
chrome.runtime.sendMessage({ type: 'btcp:command', command: { ...command, id } }, (response) => {
|
|
104
108
|
if (chrome.runtime.lastError) {
|
|
105
109
|
resolve({
|
|
106
|
-
id
|
|
110
|
+
id,
|
|
107
111
|
success: false,
|
|
108
112
|
error: chrome.runtime.lastError.message || 'Unknown error',
|
|
109
113
|
});
|
|
@@ -116,7 +120,7 @@ export function createClient() {
|
|
|
116
120
|
else {
|
|
117
121
|
// Unexpected pong response
|
|
118
122
|
resolve({
|
|
119
|
-
id
|
|
123
|
+
id,
|
|
120
124
|
success: false,
|
|
121
125
|
error: 'Unexpected response type',
|
|
122
126
|
});
|
|
@@ -136,9 +136,8 @@ export class SessionManager {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
console.log('[SessionManager] Creating group with tabs:', targetTabIds);
|
|
139
|
-
// Get the
|
|
140
|
-
const
|
|
141
|
-
const targetTab = tabs.find(t => targetTabIds.includes(t.id));
|
|
139
|
+
// Get the tab details to find its window
|
|
140
|
+
const targetTab = await chrome.tabs.get(targetTabIds[0]);
|
|
142
141
|
if (!targetTab || !targetTab.windowId) {
|
|
143
142
|
console.error('[SessionManager] Could not find valid window for tab');
|
|
144
143
|
throw new Error('Could not find a normal window for the tab');
|
|
@@ -7,7 +7,8 @@ import type { Command as CoreCommand, Response } from '../../core/dist/index.js'
|
|
|
7
7
|
import type { SessionCommand } from './session-types.js';
|
|
8
8
|
export type ExtensionAction = 'navigate' | 'back' | 'forward' | 'reload' | 'getUrl' | 'getTitle' | 'screenshot' | 'tabNew' | 'tabClose' | 'tabSwitch' | 'tabList' | 'groupCreate' | 'groupUpdate' | 'groupDelete' | 'groupList' | 'groupAddTabs' | 'groupRemoveTabs' | 'groupGet' | 'sessionGetCurrent' | 'popupInitialize';
|
|
9
9
|
export interface ExtensionBaseCommand {
|
|
10
|
-
|
|
10
|
+
/** Optional command ID. Auto-generated if not provided. */
|
|
11
|
+
id?: string;
|
|
11
12
|
action: ExtensionAction;
|
|
12
13
|
}
|
|
13
14
|
export interface NavigateCommand extends ExtensionBaseCommand {
|