btcp-browser-agent 0.1.12 → 0.1.16
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/LICENSE +21 -21
- package/README.md +338 -338
- package/package.json +69 -69
- package/packages/core/dist/actions.js +35 -35
- package/packages/extension/dist/background.d.ts +13 -11
- package/packages/extension/dist/background.js +30 -72
- package/packages/extension/dist/index.d.ts +37 -10
- package/packages/extension/dist/index.js +23 -58
- package/packages/extension/dist/remote.js +1 -62
- package/packages/extension/dist/session-manager.d.ts +30 -0
- package/packages/extension/dist/session-manager.js +192 -33
- package/packages/extension/dist/transport/base-transport.d.ts +65 -0
- package/packages/extension/dist/transport/base-transport.js +115 -0
- package/packages/extension/dist/transport/chrome-extension.d.ts +71 -0
- package/packages/extension/dist/transport/chrome-extension.js +131 -0
- package/packages/extension/dist/transport/direct.d.ts +69 -0
- package/packages/extension/dist/transport/direct.js +90 -0
- package/packages/extension/dist/transport/index.d.ts +10 -0
- package/packages/extension/dist/transport/index.js +12 -0
- package/packages/extension/dist/transport/types.d.ts +73 -0
- package/packages/extension/dist/transport/types.js +8 -0
package/package.json
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "btcp-browser-agent",
|
|
3
|
-
"version": "0.1.
|
|
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
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js",
|
|
12
|
-
"default": "./dist/index.js"
|
|
13
|
-
},
|
|
14
|
-
"./core": {
|
|
15
|
-
"types": "./packages/core/dist/index.d.ts",
|
|
16
|
-
"import": "./packages/core/dist/index.js",
|
|
17
|
-
"default": "./packages/core/dist/index.js"
|
|
18
|
-
},
|
|
19
|
-
"./extension": {
|
|
20
|
-
"types": "./packages/extension/dist/index.d.ts",
|
|
21
|
-
"import": "./packages/extension/dist/index.js",
|
|
22
|
-
"default": "./packages/extension/dist/index.js"
|
|
23
|
-
},
|
|
24
|
-
"./extension/content": {
|
|
25
|
-
"types": "./packages/extension/dist/content.d.ts",
|
|
26
|
-
"import": "./packages/extension/dist/content.js",
|
|
27
|
-
"default": "./packages/extension/dist/content.js"
|
|
28
|
-
},
|
|
29
|
-
"./extension/background": {
|
|
30
|
-
"types": "./packages/extension/dist/background.d.ts",
|
|
31
|
-
"import": "./packages/extension/dist/background.js",
|
|
32
|
-
"default": "./packages/extension/dist/background.js"
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
"scripts": {
|
|
36
|
-
"build": "npm run build:packages && tsc -p tsconfig.build.json",
|
|
37
|
-
"build:packages": "tsc -p packages/core/tsconfig.json && tsc -p packages/extension/tsconfig.json && tsc -p packages/cli/tsconfig.json",
|
|
38
|
-
"clean": "rm -rf dist packages/*/dist",
|
|
39
|
-
"prepare": "npm run build",
|
|
40
|
-
"test": "vitest run",
|
|
41
|
-
"test:watch": "vitest",
|
|
42
|
-
"typecheck": "tsc --noEmit"
|
|
43
|
-
},
|
|
44
|
-
"workspaces": [
|
|
45
|
-
"packages/core",
|
|
46
|
-
"packages/extension",
|
|
47
|
-
"packages/cli"
|
|
48
|
-
],
|
|
49
|
-
"files": [
|
|
50
|
-
"dist",
|
|
51
|
-
"packages/core/dist",
|
|
52
|
-
"packages/extension/dist",
|
|
53
|
-
"!**/__tests__",
|
|
54
|
-
"!**/*.map"
|
|
55
|
-
],
|
|
56
|
-
"license": "Apache-2.0",
|
|
57
|
-
"repository": {
|
|
58
|
-
"type": "git",
|
|
59
|
-
"url": "git+https://github.com/browser-tool-calling-protocol/btcp-browser-agent.git"
|
|
60
|
-
},
|
|
61
|
-
"dependencies": {},
|
|
62
|
-
"devDependencies": {
|
|
63
|
-
"@types/chrome": "^0.0.268",
|
|
64
|
-
"@types/node": "^20.10.0",
|
|
65
|
-
"jsdom": "^24.0.0",
|
|
66
|
-
"typescript": "^5.3.0",
|
|
67
|
-
"vitest": "^2.0.0"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "btcp-browser-agent",
|
|
3
|
+
"version": "0.1.16",
|
|
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
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./core": {
|
|
15
|
+
"types": "./packages/core/dist/index.d.ts",
|
|
16
|
+
"import": "./packages/core/dist/index.js",
|
|
17
|
+
"default": "./packages/core/dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./extension": {
|
|
20
|
+
"types": "./packages/extension/dist/index.d.ts",
|
|
21
|
+
"import": "./packages/extension/dist/index.js",
|
|
22
|
+
"default": "./packages/extension/dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./extension/content": {
|
|
25
|
+
"types": "./packages/extension/dist/content.d.ts",
|
|
26
|
+
"import": "./packages/extension/dist/content.js",
|
|
27
|
+
"default": "./packages/extension/dist/content.js"
|
|
28
|
+
},
|
|
29
|
+
"./extension/background": {
|
|
30
|
+
"types": "./packages/extension/dist/background.d.ts",
|
|
31
|
+
"import": "./packages/extension/dist/background.js",
|
|
32
|
+
"default": "./packages/extension/dist/background.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "npm run build:packages && tsc -p tsconfig.build.json",
|
|
37
|
+
"build:packages": "tsc -p packages/core/tsconfig.json && tsc -p packages/extension/tsconfig.json && tsc -p packages/cli/tsconfig.json",
|
|
38
|
+
"clean": "rm -rf dist packages/*/dist",
|
|
39
|
+
"prepare": "npm run build",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"typecheck": "tsc --noEmit"
|
|
43
|
+
},
|
|
44
|
+
"workspaces": [
|
|
45
|
+
"packages/core",
|
|
46
|
+
"packages/extension",
|
|
47
|
+
"packages/cli"
|
|
48
|
+
],
|
|
49
|
+
"files": [
|
|
50
|
+
"dist",
|
|
51
|
+
"packages/core/dist",
|
|
52
|
+
"packages/extension/dist",
|
|
53
|
+
"!**/__tests__",
|
|
54
|
+
"!**/*.map"
|
|
55
|
+
],
|
|
56
|
+
"license": "Apache-2.0",
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/browser-tool-calling-protocol/btcp-browser-agent.git"
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/chrome": "^0.0.268",
|
|
64
|
+
"@types/node": "^20.10.0",
|
|
65
|
+
"jsdom": "^24.0.0",
|
|
66
|
+
"typescript": "^5.3.0",
|
|
67
|
+
"vitest": "^2.0.0"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -864,15 +864,15 @@ export class DOMActions {
|
|
|
864
864
|
// Create overlay container with absolute positioning covering entire document
|
|
865
865
|
this.overlayContainer = this.document.createElement('div');
|
|
866
866
|
this.overlayContainer.id = 'btcp-highlight-overlay';
|
|
867
|
-
this.overlayContainer.style.cssText = `
|
|
868
|
-
position: absolute;
|
|
869
|
-
top: 0;
|
|
870
|
-
left: 0;
|
|
871
|
-
width: ${this.document.documentElement.scrollWidth}px;
|
|
872
|
-
height: ${this.document.documentElement.scrollHeight}px;
|
|
873
|
-
pointer-events: none;
|
|
874
|
-
z-index: 999999;
|
|
875
|
-
contain: layout style paint;
|
|
867
|
+
this.overlayContainer.style.cssText = `
|
|
868
|
+
position: absolute;
|
|
869
|
+
top: 0;
|
|
870
|
+
left: 0;
|
|
871
|
+
width: ${this.document.documentElement.scrollWidth}px;
|
|
872
|
+
height: ${this.document.documentElement.scrollHeight}px;
|
|
873
|
+
pointer-events: none;
|
|
874
|
+
z-index: 999999;
|
|
875
|
+
contain: layout style paint;
|
|
876
876
|
`;
|
|
877
877
|
let highlightedCount = 0;
|
|
878
878
|
// Create border overlays and labels for each ref
|
|
@@ -893,17 +893,17 @@ export class DOMActions {
|
|
|
893
893
|
const border = this.document.createElement('div');
|
|
894
894
|
border.className = 'btcp-ref-border';
|
|
895
895
|
border.dataset.ref = ref;
|
|
896
|
-
border.style.cssText = `
|
|
897
|
-
position: absolute;
|
|
898
|
-
width: ${bbox.width}px;
|
|
899
|
-
height: ${bbox.height}px;
|
|
900
|
-
transform: translate3d(${bbox.left + this.window.scrollX}px, ${bbox.top + this.window.scrollY}px, 0);
|
|
901
|
-
border: 2px solid rgba(59, 130, 246, 0.8);
|
|
902
|
-
border-radius: 2px;
|
|
903
|
-
box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.2);
|
|
904
|
-
pointer-events: none;
|
|
905
|
-
will-change: transform;
|
|
906
|
-
contain: layout style paint;
|
|
896
|
+
border.style.cssText = `
|
|
897
|
+
position: absolute;
|
|
898
|
+
width: ${bbox.width}px;
|
|
899
|
+
height: ${bbox.height}px;
|
|
900
|
+
transform: translate3d(${bbox.left + this.window.scrollX}px, ${bbox.top + this.window.scrollY}px, 0);
|
|
901
|
+
border: 2px solid rgba(59, 130, 246, 0.8);
|
|
902
|
+
border-radius: 2px;
|
|
903
|
+
box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.2);
|
|
904
|
+
pointer-events: none;
|
|
905
|
+
will-change: transform;
|
|
906
|
+
contain: layout style paint;
|
|
907
907
|
`;
|
|
908
908
|
// Create label
|
|
909
909
|
const label = this.document.createElement('div');
|
|
@@ -911,21 +911,21 @@ export class DOMActions {
|
|
|
911
911
|
label.dataset.ref = ref;
|
|
912
912
|
// Extract number from ref (e.g., "@ref:5" -> "5")
|
|
913
913
|
label.textContent = ref.replace('@ref:', '');
|
|
914
|
-
label.style.cssText = `
|
|
915
|
-
position: absolute;
|
|
916
|
-
transform: translate3d(${bbox.left + this.window.scrollX}px, ${bbox.top + this.window.scrollY}px, 0);
|
|
917
|
-
background: rgba(59, 130, 246, 0.9);
|
|
918
|
-
color: white;
|
|
919
|
-
padding: 2px 6px;
|
|
920
|
-
border-radius: 3px;
|
|
921
|
-
font-family: monospace;
|
|
922
|
-
font-size: 11px;
|
|
923
|
-
font-weight: bold;
|
|
924
|
-
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
|
925
|
-
pointer-events: none;
|
|
926
|
-
white-space: nowrap;
|
|
927
|
-
will-change: transform;
|
|
928
|
-
contain: layout style paint;
|
|
914
|
+
label.style.cssText = `
|
|
915
|
+
position: absolute;
|
|
916
|
+
transform: translate3d(${bbox.left + this.window.scrollX}px, ${bbox.top + this.window.scrollY}px, 0);
|
|
917
|
+
background: rgba(59, 130, 246, 0.9);
|
|
918
|
+
color: white;
|
|
919
|
+
padding: 2px 6px;
|
|
920
|
+
border-radius: 3px;
|
|
921
|
+
font-family: monospace;
|
|
922
|
+
font-size: 11px;
|
|
923
|
+
font-weight: bold;
|
|
924
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
|
925
|
+
pointer-events: none;
|
|
926
|
+
white-space: nowrap;
|
|
927
|
+
will-change: transform;
|
|
928
|
+
contain: layout style paint;
|
|
929
929
|
`;
|
|
930
930
|
this.overlayContainer.appendChild(border);
|
|
931
931
|
this.overlayContainer.appendChild(label);
|
|
@@ -91,22 +91,23 @@ export declare class BackgroundAgent {
|
|
|
91
91
|
*/
|
|
92
92
|
tab(tabId: number): TabHandle;
|
|
93
93
|
/**
|
|
94
|
-
* Get the currently active tab (
|
|
94
|
+
* Get the currently active tab (ensures session exists, creates if needed)
|
|
95
|
+
* This is the core "get or create" method that enables automatic session management.
|
|
95
96
|
*/
|
|
96
|
-
getActiveTab(): Promise<ChromeTab
|
|
97
|
+
getActiveTab(): Promise<ChromeTab>;
|
|
97
98
|
/**
|
|
98
|
-
* List all tabs
|
|
99
|
+
* List all tabs in session (ensures session exists, creates if needed)
|
|
99
100
|
*/
|
|
100
101
|
listTabs(): Promise<TabInfo[]>;
|
|
101
102
|
/**
|
|
102
|
-
* Create a new tab
|
|
103
|
+
* Create a new tab in session (ensures session exists, creates if needed)
|
|
103
104
|
*/
|
|
104
105
|
newTab(options?: {
|
|
105
106
|
url?: string;
|
|
106
107
|
active?: boolean;
|
|
107
108
|
}): Promise<TabInfo>;
|
|
108
109
|
/**
|
|
109
|
-
* Check if a tab is in the active session
|
|
110
|
+
* Check if a tab is in the active session (ensures session exists, creates if needed)
|
|
110
111
|
*/
|
|
111
112
|
private isTabInSession;
|
|
112
113
|
/**
|
|
@@ -118,7 +119,7 @@ export declare class BackgroundAgent {
|
|
|
118
119
|
*/
|
|
119
120
|
switchTab(tabId: number): Promise<void>;
|
|
120
121
|
/**
|
|
121
|
-
* Navigate to a URL (
|
|
122
|
+
* Navigate to a URL (session auto-created if needed)
|
|
122
123
|
* Always waits for page to be idle before returning.
|
|
123
124
|
* Verifies navigation completed to the expected origin.
|
|
124
125
|
*/
|
|
@@ -126,25 +127,25 @@ export declare class BackgroundAgent {
|
|
|
126
127
|
waitUntil?: 'load' | 'domcontentloaded';
|
|
127
128
|
}): Promise<void>;
|
|
128
129
|
/**
|
|
129
|
-
* Go back in history
|
|
130
|
+
* Go back in history (session auto-created if needed)
|
|
130
131
|
*/
|
|
131
132
|
back(): Promise<void>;
|
|
132
133
|
/**
|
|
133
|
-
* Go forward in history
|
|
134
|
+
* Go forward in history (session auto-created if needed)
|
|
134
135
|
*/
|
|
135
136
|
forward(): Promise<void>;
|
|
136
137
|
/**
|
|
137
|
-
* Reload the current page
|
|
138
|
+
* Reload the current page (session auto-created if needed)
|
|
138
139
|
*/
|
|
139
140
|
reload(options?: {
|
|
140
141
|
bypassCache?: boolean;
|
|
141
142
|
}): Promise<void>;
|
|
142
143
|
/**
|
|
143
|
-
* Get the current URL
|
|
144
|
+
* Get the current URL (session auto-created if needed)
|
|
144
145
|
*/
|
|
145
146
|
getUrl(): Promise<string>;
|
|
146
147
|
/**
|
|
147
|
-
* Get the page title
|
|
148
|
+
* Get the page title (session auto-created if needed)
|
|
148
149
|
*/
|
|
149
150
|
getTitle(): Promise<string>;
|
|
150
151
|
/**
|
|
@@ -180,6 +181,7 @@ export declare class BackgroundAgent {
|
|
|
180
181
|
}): Promise<Response>;
|
|
181
182
|
/**
|
|
182
183
|
* Send a command to the ContentAgent in a specific tab
|
|
184
|
+
* Session is automatically created if it doesn't exist.
|
|
183
185
|
*/
|
|
184
186
|
sendToContentAgent(command: Command, tabId?: number): Promise<Response>;
|
|
185
187
|
/**
|
|
@@ -151,38 +151,19 @@ export class BackgroundAgent {
|
|
|
151
151
|
// TAB MANAGEMENT
|
|
152
152
|
// ============================================================================
|
|
153
153
|
/**
|
|
154
|
-
* Get the currently active tab (
|
|
154
|
+
* Get the currently active tab (ensures session exists, creates if needed)
|
|
155
|
+
* This is the core "get or create" method that enables automatic session management.
|
|
155
156
|
*/
|
|
156
157
|
async getActiveTab() {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
if (sessionGroupId === null) {
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
// Get active tab and verify it's in the session
|
|
163
|
-
return new Promise((resolve) => {
|
|
164
|
-
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
|
165
|
-
const activeTab = tabs[0];
|
|
166
|
-
// Only return if it's in the session group
|
|
167
|
-
if (activeTab && activeTab.groupId === sessionGroupId) {
|
|
168
|
-
resolve(activeTab);
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
resolve(null);
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
});
|
|
158
|
+
const tabId = await this.sessionManager.getSessionTab();
|
|
159
|
+
return chrome.tabs.get(tabId);
|
|
175
160
|
}
|
|
176
161
|
/**
|
|
177
|
-
* List all tabs
|
|
162
|
+
* List all tabs in session (ensures session exists, creates if needed)
|
|
178
163
|
*/
|
|
179
164
|
async listTabs() {
|
|
180
|
-
//
|
|
181
|
-
const sessionGroupId = this.sessionManager.
|
|
182
|
-
// Session is required
|
|
183
|
-
if (sessionGroupId === null) {
|
|
184
|
-
throw new Error('No active session. Create a session first to manage tabs.');
|
|
185
|
-
}
|
|
165
|
+
// Ensure session exists (creates if needed)
|
|
166
|
+
const sessionGroupId = await this.sessionManager.ensureSession();
|
|
186
167
|
// Only return tabs in the session group
|
|
187
168
|
const tabs = await new Promise((resolve) => {
|
|
188
169
|
chrome.tabs.query({ groupId: sessionGroupId }, (t) => resolve(t));
|
|
@@ -196,14 +177,11 @@ export class BackgroundAgent {
|
|
|
196
177
|
}));
|
|
197
178
|
}
|
|
198
179
|
/**
|
|
199
|
-
* Create a new tab
|
|
180
|
+
* Create a new tab in session (ensures session exists, creates if needed)
|
|
200
181
|
*/
|
|
201
182
|
async newTab(options) {
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
if (sessionGroupId === null) {
|
|
205
|
-
throw new Error('No active session. Create a session first to manage tabs.');
|
|
206
|
-
}
|
|
183
|
+
// Ensure session exists (creates if needed)
|
|
184
|
+
await this.sessionManager.ensureSession();
|
|
207
185
|
const tab = await new Promise((resolve) => {
|
|
208
186
|
chrome.tabs.create({ url: options?.url, active: options?.active ?? true }, (t) => resolve(t));
|
|
209
187
|
});
|
|
@@ -231,14 +209,11 @@ export class BackgroundAgent {
|
|
|
231
209
|
};
|
|
232
210
|
}
|
|
233
211
|
/**
|
|
234
|
-
* Check if a tab is in the active session
|
|
212
|
+
* Check if a tab is in the active session (ensures session exists, creates if needed)
|
|
235
213
|
*/
|
|
236
214
|
async isTabInSession(tabId) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (sessionGroupId === null) {
|
|
240
|
-
throw new Error('No active session. Create a session first to manage tabs.');
|
|
241
|
-
}
|
|
215
|
+
// Ensure session exists (creates if needed)
|
|
216
|
+
const sessionGroupId = await this.sessionManager.ensureSession();
|
|
242
217
|
// Check if tab is in the session group
|
|
243
218
|
const tab = await chrome.tabs.get(tabId);
|
|
244
219
|
return tab.groupId === sessionGroupId;
|
|
@@ -282,19 +257,13 @@ export class BackgroundAgent {
|
|
|
282
257
|
// NAVIGATION
|
|
283
258
|
// ============================================================================
|
|
284
259
|
/**
|
|
285
|
-
* Navigate to a URL (
|
|
260
|
+
* Navigate to a URL (session auto-created if needed)
|
|
286
261
|
* Always waits for page to be idle before returning.
|
|
287
262
|
* Verifies navigation completed to the expected origin.
|
|
288
263
|
*/
|
|
289
264
|
async navigate(url, _options) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
throw new Error('No active tab');
|
|
293
|
-
// Validate tab is in session
|
|
294
|
-
const inSession = await this.isTabInSession(tabId);
|
|
295
|
-
if (!inSession) {
|
|
296
|
-
throw new Error('Cannot navigate: tab is not in the active session');
|
|
297
|
-
}
|
|
265
|
+
// getActiveTab() ensures session exists and always returns a valid tab
|
|
266
|
+
const tabId = this.activeTabId ?? (await this.getActiveTab()).id;
|
|
298
267
|
await new Promise((resolve) => {
|
|
299
268
|
chrome.tabs.update(tabId, { url }, () => resolve());
|
|
300
269
|
});
|
|
@@ -334,52 +303,46 @@ export class BackgroundAgent {
|
|
|
334
303
|
}
|
|
335
304
|
}
|
|
336
305
|
/**
|
|
337
|
-
* Go back in history
|
|
306
|
+
* Go back in history (session auto-created if needed)
|
|
338
307
|
*/
|
|
339
308
|
async back() {
|
|
340
|
-
const tabId = this.activeTabId ?? (await this.getActiveTab())
|
|
341
|
-
if (!tabId)
|
|
342
|
-
throw new Error('No active tab');
|
|
309
|
+
const tabId = this.activeTabId ?? (await this.getActiveTab()).id;
|
|
343
310
|
await new Promise((resolve) => {
|
|
344
311
|
chrome.tabs.goBack(tabId, () => resolve());
|
|
345
312
|
});
|
|
346
313
|
}
|
|
347
314
|
/**
|
|
348
|
-
* Go forward in history
|
|
315
|
+
* Go forward in history (session auto-created if needed)
|
|
349
316
|
*/
|
|
350
317
|
async forward() {
|
|
351
|
-
const tabId = this.activeTabId ?? (await this.getActiveTab())
|
|
352
|
-
if (!tabId)
|
|
353
|
-
throw new Error('No active tab');
|
|
318
|
+
const tabId = this.activeTabId ?? (await this.getActiveTab()).id;
|
|
354
319
|
await new Promise((resolve) => {
|
|
355
320
|
chrome.tabs.goForward(tabId, () => resolve());
|
|
356
321
|
});
|
|
357
322
|
}
|
|
358
323
|
/**
|
|
359
|
-
* Reload the current page
|
|
324
|
+
* Reload the current page (session auto-created if needed)
|
|
360
325
|
*/
|
|
361
326
|
async reload(options) {
|
|
362
|
-
const tabId = this.activeTabId ?? (await this.getActiveTab())
|
|
363
|
-
if (!tabId)
|
|
364
|
-
throw new Error('No active tab');
|
|
327
|
+
const tabId = this.activeTabId ?? (await this.getActiveTab()).id;
|
|
365
328
|
await new Promise((resolve) => {
|
|
366
329
|
chrome.tabs.reload(tabId, { bypassCache: options?.bypassCache }, () => resolve());
|
|
367
330
|
});
|
|
368
331
|
await this.waitForTabLoad(tabId);
|
|
369
332
|
}
|
|
370
333
|
/**
|
|
371
|
-
* Get the current URL
|
|
334
|
+
* Get the current URL (session auto-created if needed)
|
|
372
335
|
*/
|
|
373
336
|
async getUrl() {
|
|
374
337
|
const tab = await this.getActiveTab();
|
|
375
|
-
return tab
|
|
338
|
+
return tab.url || '';
|
|
376
339
|
}
|
|
377
340
|
/**
|
|
378
|
-
* Get the page title
|
|
341
|
+
* Get the page title (session auto-created if needed)
|
|
379
342
|
*/
|
|
380
343
|
async getTitle() {
|
|
381
344
|
const tab = await this.getActiveTab();
|
|
382
|
-
return tab
|
|
345
|
+
return tab.title || '';
|
|
383
346
|
}
|
|
384
347
|
// ============================================================================
|
|
385
348
|
// SCREENSHOTS
|
|
@@ -449,19 +412,14 @@ export class BackgroundAgent {
|
|
|
449
412
|
}
|
|
450
413
|
/**
|
|
451
414
|
* Send a command to the ContentAgent in a specific tab
|
|
415
|
+
* Session is automatically created if it doesn't exist.
|
|
452
416
|
*/
|
|
453
417
|
async sendToContentAgent(command, tabId) {
|
|
454
418
|
// Ensure command has an ID for internal use
|
|
455
419
|
const id = command.id || generateBgCommandId();
|
|
456
420
|
const internalCmd = { ...command, id };
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
return {
|
|
460
|
-
id,
|
|
461
|
-
success: false,
|
|
462
|
-
error: 'No active tab for DOM command',
|
|
463
|
-
};
|
|
464
|
-
}
|
|
421
|
+
// getActiveTab() ensures session exists and always returns a valid tab
|
|
422
|
+
const targetTabId = tabId ?? this.activeTabId ?? (await this.getActiveTab()).id;
|
|
465
423
|
// Try sending with automatic retry and recovery
|
|
466
424
|
return this.sendMessageWithRetry(targetTabId, internalCmd);
|
|
467
425
|
}
|
|
@@ -693,7 +651,7 @@ export class BackgroundAgent {
|
|
|
693
651
|
case 'popupInitialize': {
|
|
694
652
|
console.log('[BackgroundAgent] Popup initializing, checking for session reconnection...');
|
|
695
653
|
// Check if we have a stored session but no active connection
|
|
696
|
-
const sessionGroupId = this.sessionManager.
|
|
654
|
+
const sessionGroupId = await this.sessionManager.getActiveSessionGroupIdAsync();
|
|
697
655
|
if (sessionGroupId === null) {
|
|
698
656
|
// Try to reconnect from storage
|
|
699
657
|
const result = await chrome.storage.session.get('btcp_active_session');
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
42
|
import type { Command, Response } from './types.js';
|
|
43
|
+
import type { Transport } from './transport/types.js';
|
|
43
44
|
import { BackgroundAgent as _BackgroundAgent, getBackgroundAgent as _getBackgroundAgent, setupMessageListener as _setupMessageListener, BrowserAgent as _BrowserAgent, getBrowserAgent as _getBrowserAgent } from './background.js';
|
|
44
45
|
export * from './types.js';
|
|
45
46
|
export { createScriptMessenger, createMethodMessenger, type MessageDefinitions, type ScriptMessenger, type MethodMessenger, type ScriptMessengerOptions, type PayloadOf, type ResultOf, } from './script-messenger.js';
|
|
@@ -47,6 +48,7 @@ export { createRemoteAgent, getBrowserToolDefinitions, mapToolToCommand, formatR
|
|
|
47
48
|
export { _BackgroundAgent as BackgroundAgent, _getBackgroundAgent as getBackgroundAgent, _setupMessageListener as setupMessageListener, _BrowserAgent as BrowserAgent, _getBrowserAgent as getBrowserAgent, };
|
|
48
49
|
export { createContentAgent, type ContentAgent } from '../../core/dist/index.js';
|
|
49
50
|
export type { SnapshotData, BoundingBox, Modifier, } from '../../core/dist/index.js';
|
|
51
|
+
export * from './transport/index.js';
|
|
50
52
|
/**
|
|
51
53
|
* Client for sending commands to the extension background script
|
|
52
54
|
*/
|
|
@@ -147,6 +149,24 @@ export interface Client {
|
|
|
147
149
|
*/
|
|
148
150
|
evaluate(expression: string): Promise<unknown>;
|
|
149
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Options for creating a client
|
|
154
|
+
*/
|
|
155
|
+
export interface CreateClientOptions {
|
|
156
|
+
/**
|
|
157
|
+
* Transport to use for sending commands.
|
|
158
|
+
* Defaults to ChromeExtensionTransport.
|
|
159
|
+
*
|
|
160
|
+
* @example Using direct transport in background script:
|
|
161
|
+
* ```typescript
|
|
162
|
+
* import { createClient, createDirectTransport, getBackgroundAgent } from '@btcp/browser-agent/extension';
|
|
163
|
+
*
|
|
164
|
+
* const transport = createDirectTransport({ agent: getBackgroundAgent() });
|
|
165
|
+
* const client = createClient({ transport });
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
transport?: Transport;
|
|
169
|
+
}
|
|
150
170
|
/**
|
|
151
171
|
* Generate a unique command ID for BTCP commands
|
|
152
172
|
*/
|
|
@@ -154,24 +174,31 @@ export declare function generateCommandId(): string;
|
|
|
154
174
|
/**
|
|
155
175
|
* Create a client for communicating with the extension
|
|
156
176
|
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* - In background scripts: Uses BackgroundAgent directly for better performance
|
|
177
|
+
* By default uses ChromeExtensionTransport for popup/content script contexts.
|
|
178
|
+
* Pass a custom transport for different communication mechanisms.
|
|
160
179
|
*
|
|
161
|
-
* @example
|
|
180
|
+
* @example Default (Chrome Extension):
|
|
162
181
|
* ```typescript
|
|
163
182
|
* import { createClient } from '@btcp/browser-agent/extension';
|
|
164
183
|
* const client = createClient();
|
|
165
184
|
* await client.navigate('https://example.com');
|
|
166
185
|
* ```
|
|
167
186
|
*
|
|
168
|
-
* @example
|
|
187
|
+
* @example With explicit transport:
|
|
169
188
|
* ```typescript
|
|
170
|
-
* import { createClient } from '@btcp/browser-agent/extension';
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
189
|
+
* import { createClient, createChromeExtensionTransport } from '@btcp/browser-agent/extension';
|
|
190
|
+
*
|
|
191
|
+
* const transport = createChromeExtensionTransport({ debug: true });
|
|
192
|
+
* const client = createClient({ transport });
|
|
193
|
+
* ```
|
|
194
|
+
*
|
|
195
|
+
* @example Direct transport (background script):
|
|
196
|
+
* ```typescript
|
|
197
|
+
* import { createClient, createDirectTransport, getBackgroundAgent } from '@btcp/browser-agent/extension';
|
|
198
|
+
*
|
|
199
|
+
* const transport = createDirectTransport({ agent: getBackgroundAgent() });
|
|
200
|
+
* const client = createClient({ transport });
|
|
174
201
|
* ```
|
|
175
202
|
*/
|
|
176
|
-
export declare function createClient(): Client;
|
|
203
|
+
export declare function createClient(options?: CreateClientOptions): Client;
|
|
177
204
|
//# sourceMappingURL=index.d.ts.map
|