@ai-support-agent/cli 0.0.22 → 0.0.23-beta.1
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/dist/agent-transport.d.ts.map +1 -1
- package/dist/agent-transport.js +1 -0
- package/dist/agent-transport.js.map +1 -1
- package/dist/api-client.d.ts +2 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +4 -0
- package/dist/api-client.js.map +1 -1
- package/dist/browser/browser-local-server.d.ts +54 -0
- package/dist/browser/browser-local-server.d.ts.map +1 -0
- package/dist/browser/browser-local-server.js +295 -0
- package/dist/browser/browser-local-server.js.map +1 -0
- package/dist/commands/chat-executor.d.ts +1 -0
- package/dist/commands/chat-executor.d.ts.map +1 -1
- package/dist/commands/chat-executor.js +5 -3
- package/dist/commands/chat-executor.js.map +1 -1
- package/dist/commands/claude-code-runner.d.ts +2 -0
- package/dist/commands/claude-code-runner.d.ts.map +1 -1
- package/dist/commands/claude-code-runner.js +4 -0
- package/dist/commands/claude-code-runner.js.map +1 -1
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +1 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/mcp/config-writer.d.ts +4 -0
- package/dist/mcp/config-writer.d.ts.map +1 -1
- package/dist/mcp/config-writer.js +5 -0
- package/dist/mcp/config-writer.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +3 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/browser/browser-action-log.d.ts +20 -0
- package/dist/mcp/tools/browser/browser-action-log.d.ts.map +1 -0
- package/dist/mcp/tools/browser/browser-action-log.js +46 -0
- package/dist/mcp/tools/browser/browser-action-log.js.map +1 -0
- package/dist/mcp/tools/browser/browser-proxy-session.d.ts +95 -0
- package/dist/mcp/tools/browser/browser-proxy-session.d.ts.map +1 -0
- package/dist/mcp/tools/browser/browser-proxy-session.js +210 -0
- package/dist/mcp/tools/browser/browser-proxy-session.js.map +1 -0
- package/dist/mcp/tools/browser/browser-security.d.ts +13 -0
- package/dist/mcp/tools/browser/browser-security.d.ts.map +1 -0
- package/dist/mcp/tools/browser/browser-security.js +27 -0
- package/dist/mcp/tools/browser/browser-security.js.map +1 -0
- package/dist/mcp/tools/browser/browser-session-manager.d.ts +50 -0
- package/dist/mcp/tools/browser/browser-session-manager.d.ts.map +1 -0
- package/dist/mcp/tools/browser/browser-session-manager.js +111 -0
- package/dist/mcp/tools/browser/browser-session-manager.js.map +1 -0
- package/dist/mcp/tools/browser/browser-session.d.ts +93 -0
- package/dist/mcp/tools/browser/browser-session.d.ts.map +1 -0
- package/dist/mcp/tools/browser/browser-session.js +254 -0
- package/dist/mcp/tools/browser/browser-session.js.map +1 -0
- package/dist/mcp/tools/browser/browser-types.d.ts +7 -0
- package/dist/mcp/tools/browser/browser-types.d.ts.map +1 -0
- package/dist/mcp/tools/browser/browser-types.js +9 -0
- package/dist/mcp/tools/browser/browser-types.js.map +1 -0
- package/dist/mcp/tools/browser/element-info.d.ts +38 -0
- package/dist/mcp/tools/browser/element-info.d.ts.map +1 -0
- package/dist/mcp/tools/browser/element-info.js +223 -0
- package/dist/mcp/tools/browser/element-info.js.map +1 -0
- package/dist/mcp/tools/browser/playwright-loader.d.ts +20 -0
- package/dist/mcp/tools/browser/playwright-loader.d.ts.map +1 -0
- package/dist/mcp/tools/browser/playwright-loader.js +50 -0
- package/dist/mcp/tools/browser/playwright-loader.js.map +1 -0
- package/dist/mcp/tools/browser.d.ts +15 -0
- package/dist/mcp/tools/browser.d.ts.map +1 -0
- package/dist/mcp/tools/browser.js +294 -0
- package/dist/mcp/tools/browser.js.map +1 -0
- package/dist/mcp/tools/mcp-response.d.ts +12 -1
- package/dist/mcp/tools/mcp-response.d.ts.map +1 -1
- package/dist/mcp/tools/mcp-response.js +9 -0
- package/dist/mcp/tools/mcp-response.js.map +1 -1
- package/dist/types/chat.d.ts +1 -0
- package/dist/types/chat.d.ts.map +1 -1
- package/dist/types/project.d.ts +20 -0
- package/dist/types/project.d.ts.map +1 -1
- package/dist/update-checker.d.ts.map +1 -1
- package/dist/update-checker.js +2 -1
- package/dist/update-checker.js.map +1 -1
- package/dist/vscode/vscode-tunnel-websocket.d.ts +54 -2
- package/dist/vscode/vscode-tunnel-websocket.d.ts.map +1 -1
- package/dist/vscode/vscode-tunnel-websocket.js +303 -0
- package/dist/vscode/vscode-tunnel-websocket.js.map +1 -1
- package/docker/Dockerfile +47 -18
- package/package.json +3 -2
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* BrowserSession — Playwright wrapper managing a singleton browser + page.
|
|
4
|
+
* Auto-closes after idle timeout (5 minutes).
|
|
5
|
+
* Supports live view streaming (JPEG frames) and direct interaction.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.BrowserSession = void 0;
|
|
9
|
+
const logger_1 = require("../../../logger");
|
|
10
|
+
const browser_action_log_1 = require("./browser-action-log");
|
|
11
|
+
const browser_types_1 = require("./browser-types");
|
|
12
|
+
const element_info_1 = require("./element-info");
|
|
13
|
+
const playwright_loader_1 = require("./playwright-loader");
|
|
14
|
+
class BrowserSession {
|
|
15
|
+
browser = null;
|
|
16
|
+
page = null;
|
|
17
|
+
idleTimer = null;
|
|
18
|
+
idleTimeoutMs;
|
|
19
|
+
liveViewInterval = null;
|
|
20
|
+
/** Session-scoped temporary variables */
|
|
21
|
+
variables = new Map();
|
|
22
|
+
/** Action log for recording browser operations */
|
|
23
|
+
actionLog = new browser_action_log_1.BrowserActionLog();
|
|
24
|
+
constructor(idleTimeoutMs = browser_types_1.BROWSER_IDLE_TIMEOUT_MS) {
|
|
25
|
+
this.idleTimeoutMs = idleTimeoutMs;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the current page, launching browser if needed.
|
|
29
|
+
*/
|
|
30
|
+
async getPage() {
|
|
31
|
+
this.resetIdleTimer();
|
|
32
|
+
if (this.page) {
|
|
33
|
+
return this.page;
|
|
34
|
+
}
|
|
35
|
+
const pw = (0, playwright_loader_1.loadPlaywright)();
|
|
36
|
+
logger_1.logger.debug('[browser] Launching Chromium (headless)');
|
|
37
|
+
this.browser = await pw.chromium.launch({
|
|
38
|
+
headless: true,
|
|
39
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
40
|
+
});
|
|
41
|
+
this.page = await this.browser.newPage();
|
|
42
|
+
return this.page;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if a browser session is currently active.
|
|
46
|
+
*/
|
|
47
|
+
isActive() {
|
|
48
|
+
return this.browser !== null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Close the browser and clean up resources.
|
|
52
|
+
*/
|
|
53
|
+
async close() {
|
|
54
|
+
this.stopLiveView();
|
|
55
|
+
this.clearIdleTimer();
|
|
56
|
+
if (this.browser) {
|
|
57
|
+
logger_1.logger.debug('[browser] Closing browser');
|
|
58
|
+
try {
|
|
59
|
+
await this.browser.close();
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
logger_1.logger.debug(`[browser] Error closing browser: ${String(error)}`);
|
|
63
|
+
}
|
|
64
|
+
this.browser = null;
|
|
65
|
+
this.page = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Set the viewport size for the current page.
|
|
70
|
+
*/
|
|
71
|
+
async setViewport(width, height) {
|
|
72
|
+
const page = await this.getPage();
|
|
73
|
+
await page.setViewportSize({ width, height });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Take a screenshot of the current page.
|
|
77
|
+
*/
|
|
78
|
+
async screenshot(fullPage = true) {
|
|
79
|
+
const page = await this.getPage();
|
|
80
|
+
return page.screenshot({ fullPage, type: 'png' });
|
|
81
|
+
}
|
|
82
|
+
// --- Live View ---
|
|
83
|
+
/**
|
|
84
|
+
* Start live view streaming. Takes JPEG screenshots at the specified interval
|
|
85
|
+
* and calls the onFrame callback with the base64-encoded data.
|
|
86
|
+
* While live view is active, the idle timeout is disabled.
|
|
87
|
+
*/
|
|
88
|
+
startLiveView(intervalMs, onFrame) {
|
|
89
|
+
this.stopLiveView();
|
|
90
|
+
this.clearIdleTimer(); // Disable idle timeout during live view
|
|
91
|
+
let capturing = false;
|
|
92
|
+
this.liveViewInterval = setInterval(() => {
|
|
93
|
+
if (capturing || !this.page)
|
|
94
|
+
return;
|
|
95
|
+
capturing = true;
|
|
96
|
+
void (async () => {
|
|
97
|
+
try {
|
|
98
|
+
const buffer = await this.page.screenshot({ fullPage: false, type: 'jpeg', quality: 50 });
|
|
99
|
+
onFrame(buffer.toString('base64'));
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
logger_1.logger.debug(`[browser] Live view screenshot error: ${String(error)}`);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
capturing = false;
|
|
106
|
+
}
|
|
107
|
+
})();
|
|
108
|
+
}, intervalMs);
|
|
109
|
+
logger_1.logger.debug(`[browser] Live view started (interval=${intervalMs}ms)`);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Stop live view streaming and re-enable idle timeout.
|
|
113
|
+
*/
|
|
114
|
+
stopLiveView() {
|
|
115
|
+
if (this.liveViewInterval) {
|
|
116
|
+
clearInterval(this.liveViewInterval);
|
|
117
|
+
this.liveViewInterval = null;
|
|
118
|
+
logger_1.logger.debug('[browser] Live view stopped');
|
|
119
|
+
}
|
|
120
|
+
// Re-enable idle timeout
|
|
121
|
+
if (this.isActive()) {
|
|
122
|
+
this.resetIdleTimer();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check if live view is currently active.
|
|
127
|
+
*/
|
|
128
|
+
isLiveViewActive() {
|
|
129
|
+
return this.liveViewInterval !== null;
|
|
130
|
+
}
|
|
131
|
+
// --- Direct Interaction ---
|
|
132
|
+
/**
|
|
133
|
+
* Click at the specified coordinates.
|
|
134
|
+
*/
|
|
135
|
+
async executeMouseClick(x, y, button, clickCount) {
|
|
136
|
+
if (!this.page)
|
|
137
|
+
throw new Error('No active browser page');
|
|
138
|
+
this.resetIdleTimer();
|
|
139
|
+
// Get element info BEFORE click (element might change after click)
|
|
140
|
+
const elementInfo = await (0, element_info_1.getElementAtPoint)(this.page, x, y);
|
|
141
|
+
await this.page.mouse.click(x, y, {
|
|
142
|
+
button: button || 'left',
|
|
143
|
+
clickCount: clickCount || 1,
|
|
144
|
+
});
|
|
145
|
+
const elementDesc = elementInfo ? (0, element_info_1.formatElementInfo)(elementInfo) : '(no element)';
|
|
146
|
+
this.actionLog.add('direct', 'click', `(${x}, ${y}) ${elementDesc}`);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Scroll the page.
|
|
150
|
+
*/
|
|
151
|
+
async executeMouseWheel(deltaX, deltaY) {
|
|
152
|
+
if (!this.page)
|
|
153
|
+
throw new Error('No active browser page');
|
|
154
|
+
this.resetIdleTimer();
|
|
155
|
+
await this.page.mouse.wheel(deltaX, deltaY);
|
|
156
|
+
this.actionLog.add('direct', 'scroll', `deltaX=${deltaX} deltaY=${deltaY}`);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Type text into the focused element.
|
|
160
|
+
*/
|
|
161
|
+
async executeKeyboardType(text) {
|
|
162
|
+
if (!this.page)
|
|
163
|
+
throw new Error('No active browser page');
|
|
164
|
+
this.resetIdleTimer();
|
|
165
|
+
// Get focused element info for Playwright context
|
|
166
|
+
const focusedInfo = await (0, element_info_1.getFocusedElementInfo)(this.page);
|
|
167
|
+
await this.page.keyboard.type(text);
|
|
168
|
+
const target = focusedInfo ? `target=${(0, element_info_1.formatElementInfo)(focusedInfo)}` : '(no focused element)';
|
|
169
|
+
this.actionLog.add('direct', 'type', `"${text}" ${target}`);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Press a key combination.
|
|
173
|
+
*/
|
|
174
|
+
async executeKeyboardPress(key, modifiers) {
|
|
175
|
+
if (!this.page)
|
|
176
|
+
throw new Error('No active browser page');
|
|
177
|
+
this.resetIdleTimer();
|
|
178
|
+
const focusedInfo = await (0, element_info_1.getFocusedElementInfo)(this.page);
|
|
179
|
+
if (modifiers && modifiers.length > 0) {
|
|
180
|
+
const combo = [...modifiers, key].join('+');
|
|
181
|
+
await this.page.keyboard.press(combo);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
await this.page.keyboard.press(key);
|
|
185
|
+
}
|
|
186
|
+
const keyStr = modifiers?.length ? `${modifiers.join('+')}+${key}` : key;
|
|
187
|
+
const target = focusedInfo ? `target=${(0, element_info_1.formatElementInfo)(focusedInfo)}` : '';
|
|
188
|
+
this.actionLog.add('direct', 'press', target ? `${keyStr} ${target}` : keyStr);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Navigate back.
|
|
192
|
+
*/
|
|
193
|
+
async goBack() {
|
|
194
|
+
if (!this.page)
|
|
195
|
+
throw new Error('No active browser page');
|
|
196
|
+
this.resetIdleTimer();
|
|
197
|
+
await this.page.goBack();
|
|
198
|
+
this.actionLog.add('direct', 'go_back', this.getCurrentUrl());
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Navigate forward.
|
|
202
|
+
*/
|
|
203
|
+
async goForward() {
|
|
204
|
+
if (!this.page)
|
|
205
|
+
throw new Error('No active browser page');
|
|
206
|
+
this.resetIdleTimer();
|
|
207
|
+
await this.page.goForward();
|
|
208
|
+
this.actionLog.add('direct', 'go_forward', this.getCurrentUrl());
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Reload the page.
|
|
212
|
+
*/
|
|
213
|
+
async reload() {
|
|
214
|
+
if (!this.page)
|
|
215
|
+
throw new Error('No active browser page');
|
|
216
|
+
this.resetIdleTimer();
|
|
217
|
+
await this.page.reload();
|
|
218
|
+
this.actionLog.add('direct', 'reload', this.getCurrentUrl());
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Get current page URL.
|
|
222
|
+
*/
|
|
223
|
+
getCurrentUrl() {
|
|
224
|
+
if (!this.page)
|
|
225
|
+
return '';
|
|
226
|
+
return this.page.url();
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get current page title.
|
|
230
|
+
*/
|
|
231
|
+
async getPageTitle() {
|
|
232
|
+
if (!this.page)
|
|
233
|
+
return '';
|
|
234
|
+
return this.page.title();
|
|
235
|
+
}
|
|
236
|
+
resetIdleTimer() {
|
|
237
|
+
this.clearIdleTimer();
|
|
238
|
+
// Don't set idle timer during live view
|
|
239
|
+
if (this.liveViewInterval)
|
|
240
|
+
return;
|
|
241
|
+
this.idleTimer = setTimeout(() => {
|
|
242
|
+
logger_1.logger.debug('[browser] Idle timeout reached, closing browser');
|
|
243
|
+
void this.close();
|
|
244
|
+
}, this.idleTimeoutMs);
|
|
245
|
+
}
|
|
246
|
+
clearIdleTimer() {
|
|
247
|
+
if (this.idleTimer) {
|
|
248
|
+
clearTimeout(this.idleTimer);
|
|
249
|
+
this.idleTimer = null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
exports.BrowserSession = BrowserSession;
|
|
254
|
+
//# sourceMappingURL=browser-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-session.js","sourceRoot":"","sources":["../../../../src/mcp/tools/browser/browser-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-types.d.ts","sourceRoot":"","sources":["../../../../src/mcp/tools/browser/browser-types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAExD,eAAO,MAAM,uBAAuB,QAAgB,CAAA;AAEpD,eAAO,MAAM,iBAAiB,UAAoC,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Browser tool types
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BLOCKED_PROTOCOLS = exports.BROWSER_IDLE_TIMEOUT_MS = void 0;
|
|
7
|
+
exports.BROWSER_IDLE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
8
|
+
exports.BLOCKED_PROTOCOLS = ['file:', 'javascript:', 'data:'];
|
|
9
|
+
//# sourceMappingURL=browser-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-types.js","sourceRoot":"","sources":["../../../../src/mcp/tools/browser/browser-types.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAIU,QAAA,uBAAuB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA,CAAC,YAAY;AAEpD,QAAA,iBAAiB,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,CAAC,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* element-info.ts — Extracts Playwright-friendly element information from the page.
|
|
3
|
+
*
|
|
4
|
+
* Used by BrowserSession to enrich action logs with CSS selectors, element types,
|
|
5
|
+
* and text content so that chat AI can reproduce user interactions via Playwright.
|
|
6
|
+
*/
|
|
7
|
+
/** Structured information about a DOM element */
|
|
8
|
+
export interface ElementInfo {
|
|
9
|
+
/** Best CSS selector for Playwright (e.g., '#submit-btn', 'button:has-text("Login")') */
|
|
10
|
+
selector: string;
|
|
11
|
+
/** Tag name (e.g., 'button', 'input', 'a') */
|
|
12
|
+
tagName: string;
|
|
13
|
+
/** Element type attribute for input/button (e.g., 'text', 'submit', 'checkbox') */
|
|
14
|
+
type?: string;
|
|
15
|
+
/** Visible text content (truncated) */
|
|
16
|
+
text?: string;
|
|
17
|
+
/** Role attribute or implicit ARIA role */
|
|
18
|
+
role?: string;
|
|
19
|
+
/** name, placeholder, aria-label, or title attribute */
|
|
20
|
+
label?: string;
|
|
21
|
+
/** href for links */
|
|
22
|
+
href?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get element info at the given coordinates via page.evaluate().
|
|
26
|
+
* Returns null if no element found or on error.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getElementAtPoint(page: any, x: number, y: number): Promise<ElementInfo | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Get info about the currently focused element.
|
|
31
|
+
* Returns null if no element is focused or on error.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getFocusedElementInfo(page: any): Promise<ElementInfo | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Format ElementInfo into a human-readable, Playwright-actionable string.
|
|
36
|
+
*/
|
|
37
|
+
export declare function formatElementInfo(info: ElementInfo): string;
|
|
38
|
+
//# sourceMappingURL=element-info.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-info.d.ts","sourceRoot":"","sources":["../../../../src/mcp/tools/browser/element-info.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,QAAQ,EAAE,MAAM,CAAA;IAChB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAA;IACf,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAuKD;;;GAGG;AAEH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAMpG;AAED;;;GAGG;AAEH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAMlF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAiB3D"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* element-info.ts — Extracts Playwright-friendly element information from the page.
|
|
4
|
+
*
|
|
5
|
+
* Used by BrowserSession to enrich action logs with CSS selectors, element types,
|
|
6
|
+
* and text content so that chat AI can reproduce user interactions via Playwright.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.getElementAtPoint = getElementAtPoint;
|
|
10
|
+
exports.getFocusedElementInfo = getFocusedElementInfo;
|
|
11
|
+
exports.formatElementInfo = formatElementInfo;
|
|
12
|
+
/**
|
|
13
|
+
* JavaScript to run in the browser via page.evaluate() to extract element info.
|
|
14
|
+
* Returns ElementInfo-compatible object or null.
|
|
15
|
+
*/
|
|
16
|
+
const ELEMENT_AT_POINT_SCRIPT = `(point) => {
|
|
17
|
+
const el = document.elementFromPoint(point.x, point.y);
|
|
18
|
+
if (!el) return null;
|
|
19
|
+
return extractInfo(el);
|
|
20
|
+
|
|
21
|
+
function extractInfo(el) {
|
|
22
|
+
const tag = el.tagName.toLowerCase();
|
|
23
|
+
const id = el.id;
|
|
24
|
+
const name = el.getAttribute('name');
|
|
25
|
+
const type = el.getAttribute('type');
|
|
26
|
+
const role = el.getAttribute('role') || implicitRole(tag, type);
|
|
27
|
+
const ariaLabel = el.getAttribute('aria-label');
|
|
28
|
+
const placeholder = el.getAttribute('placeholder');
|
|
29
|
+
const title = el.getAttribute('title');
|
|
30
|
+
const href = el.getAttribute('href');
|
|
31
|
+
const text = getVisibleText(el);
|
|
32
|
+
const testId = el.getAttribute('data-testid') || el.getAttribute('data-test-id');
|
|
33
|
+
|
|
34
|
+
const selector = buildSelector(el, tag, id, name, testId, text, role, ariaLabel, placeholder, type);
|
|
35
|
+
const label = ariaLabel || placeholder || name || title || undefined;
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
selector,
|
|
39
|
+
tagName: tag,
|
|
40
|
+
type: type || undefined,
|
|
41
|
+
text: text || undefined,
|
|
42
|
+
role: role || undefined,
|
|
43
|
+
label: label || undefined,
|
|
44
|
+
href: href || undefined,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function buildSelector(el, tag, id, name, testId, text, role, ariaLabel, placeholder, type) {
|
|
49
|
+
// Priority 1: data-testid (most stable)
|
|
50
|
+
if (testId) return '[data-testid="' + testId + '"]';
|
|
51
|
+
|
|
52
|
+
// Priority 2: id (unique)
|
|
53
|
+
if (id) return '#' + CSS.escape(id);
|
|
54
|
+
|
|
55
|
+
// Priority 3: role + name (accessible selectors)
|
|
56
|
+
if (role && ariaLabel) return tag + '[role="' + role + '"][aria-label="' + ariaLabel + '"]';
|
|
57
|
+
|
|
58
|
+
// Priority 4: name attribute (forms)
|
|
59
|
+
if (name && (tag === 'input' || tag === 'select' || tag === 'textarea')) {
|
|
60
|
+
return tag + '[name="' + name + '"]';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Priority 5: Playwright text selector for buttons/links
|
|
64
|
+
if (text && text.length <= 50 && (tag === 'button' || tag === 'a' || role === 'button' || role === 'link')) {
|
|
65
|
+
return tag + ':has-text("' + text.replace(/"/g, '\\\\"') + '")';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Priority 6: placeholder for inputs
|
|
69
|
+
if (placeholder && tag === 'input') {
|
|
70
|
+
return 'input[placeholder="' + placeholder + '"]';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Priority 7: type for inputs
|
|
74
|
+
if (type && tag === 'input') {
|
|
75
|
+
// Check if unique enough by adding parent context
|
|
76
|
+
const parent = el.parentElement;
|
|
77
|
+
if (parent && parent.id) {
|
|
78
|
+
return '#' + CSS.escape(parent.id) + ' > input[type="' + type + '"]';
|
|
79
|
+
}
|
|
80
|
+
return 'input[type="' + type + '"]';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Priority 8: nth-of-type with class
|
|
84
|
+
const classes = Array.from(el.classList).filter(c => !c.match(/^(js-|is-|has-)/)).slice(0, 2);
|
|
85
|
+
if (classes.length > 0) {
|
|
86
|
+
const classSelector = tag + '.' + classes.map(c => CSS.escape(c)).join('.');
|
|
87
|
+
const siblings = document.querySelectorAll(classSelector);
|
|
88
|
+
if (siblings.length === 1) return classSelector;
|
|
89
|
+
const index = Array.from(siblings).indexOf(el);
|
|
90
|
+
if (index >= 0) return classSelector + ':nth-of-type(' + (index + 1) + ')';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Fallback: tag with index
|
|
94
|
+
return tag;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getVisibleText(el) {
|
|
98
|
+
const text = (el.textContent || '').trim();
|
|
99
|
+
return text.length > 80 ? text.substring(0, 77) + '...' : text;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function implicitRole(tag, type) {
|
|
103
|
+
if (tag === 'button') return 'button';
|
|
104
|
+
if (tag === 'a') return 'link';
|
|
105
|
+
if (tag === 'input') {
|
|
106
|
+
if (type === 'checkbox') return 'checkbox';
|
|
107
|
+
if (type === 'radio') return 'radio';
|
|
108
|
+
if (type === 'submit') return 'button';
|
|
109
|
+
if (!type || type === 'text' || type === 'email' || type === 'password' || type === 'search' || type === 'tel' || type === 'url' || type === 'number') return 'textbox';
|
|
110
|
+
}
|
|
111
|
+
if (tag === 'textarea') return 'textbox';
|
|
112
|
+
if (tag === 'select') return 'combobox';
|
|
113
|
+
if (tag === 'img') return 'img';
|
|
114
|
+
return '';
|
|
115
|
+
}
|
|
116
|
+
}`;
|
|
117
|
+
/**
|
|
118
|
+
* JavaScript to extract info about the currently focused element.
|
|
119
|
+
*/
|
|
120
|
+
const FOCUSED_ELEMENT_SCRIPT = `() => {
|
|
121
|
+
const el = document.activeElement;
|
|
122
|
+
if (!el || el === document.body) return null;
|
|
123
|
+
|
|
124
|
+
const tag = el.tagName.toLowerCase();
|
|
125
|
+
const id = el.id;
|
|
126
|
+
const name = el.getAttribute('name');
|
|
127
|
+
const type = el.getAttribute('type');
|
|
128
|
+
const role = el.getAttribute('role') || implicitRole(tag, type);
|
|
129
|
+
const ariaLabel = el.getAttribute('aria-label');
|
|
130
|
+
const placeholder = el.getAttribute('placeholder');
|
|
131
|
+
const title = el.getAttribute('title');
|
|
132
|
+
const testId = el.getAttribute('data-testid') || el.getAttribute('data-test-id');
|
|
133
|
+
|
|
134
|
+
const selector = buildSelector(el, tag, id, name, testId, '', role, ariaLabel, placeholder, type);
|
|
135
|
+
const label = ariaLabel || placeholder || name || title || undefined;
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
selector,
|
|
139
|
+
tagName: tag,
|
|
140
|
+
type: type || undefined,
|
|
141
|
+
role: role || undefined,
|
|
142
|
+
label: label || undefined,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
function buildSelector(el, tag, id, name, testId, text, role, ariaLabel, placeholder, type) {
|
|
146
|
+
if (testId) return '[data-testid="' + testId + '"]';
|
|
147
|
+
if (id) return '#' + CSS.escape(id);
|
|
148
|
+
if (role && ariaLabel) return tag + '[role="' + role + '"][aria-label="' + ariaLabel + '"]';
|
|
149
|
+
if (name && (tag === 'input' || tag === 'select' || tag === 'textarea')) {
|
|
150
|
+
return tag + '[name="' + name + '"]';
|
|
151
|
+
}
|
|
152
|
+
if (placeholder && tag === 'input') {
|
|
153
|
+
return 'input[placeholder="' + placeholder + '"]';
|
|
154
|
+
}
|
|
155
|
+
if (type && tag === 'input') return 'input[type="' + type + '"]';
|
|
156
|
+
const classes = Array.from(el.classList).filter(c => !c.match(/^(js-|is-|has-)/)).slice(0, 2);
|
|
157
|
+
if (classes.length > 0) return tag + '.' + classes.map(c => CSS.escape(c)).join('.');
|
|
158
|
+
return tag;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function implicitRole(tag, type) {
|
|
162
|
+
if (tag === 'button') return 'button';
|
|
163
|
+
if (tag === 'a') return 'link';
|
|
164
|
+
if (tag === 'input') {
|
|
165
|
+
if (type === 'checkbox') return 'checkbox';
|
|
166
|
+
if (type === 'radio') return 'radio';
|
|
167
|
+
if (type === 'submit') return 'button';
|
|
168
|
+
if (!type || type === 'text' || type === 'email' || type === 'password' || type === 'search' || type === 'tel' || type === 'url' || type === 'number') return 'textbox';
|
|
169
|
+
}
|
|
170
|
+
if (tag === 'textarea') return 'textbox';
|
|
171
|
+
if (tag === 'select') return 'combobox';
|
|
172
|
+
return '';
|
|
173
|
+
}
|
|
174
|
+
}`;
|
|
175
|
+
/**
|
|
176
|
+
* Get element info at the given coordinates via page.evaluate().
|
|
177
|
+
* Returns null if no element found or on error.
|
|
178
|
+
*/
|
|
179
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
180
|
+
async function getElementAtPoint(page, x, y) {
|
|
181
|
+
try {
|
|
182
|
+
return await page.evaluate(ELEMENT_AT_POINT_SCRIPT, { x, y });
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get info about the currently focused element.
|
|
190
|
+
* Returns null if no element is focused or on error.
|
|
191
|
+
*/
|
|
192
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
193
|
+
async function getFocusedElementInfo(page) {
|
|
194
|
+
try {
|
|
195
|
+
return await page.evaluate(FOCUSED_ELEMENT_SCRIPT);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Format ElementInfo into a human-readable, Playwright-actionable string.
|
|
203
|
+
*/
|
|
204
|
+
function formatElementInfo(info) {
|
|
205
|
+
const parts = [];
|
|
206
|
+
// selector is always first — the most important part for Playwright
|
|
207
|
+
parts.push(`selector="${info.selector}"`);
|
|
208
|
+
// Element description
|
|
209
|
+
const desc = [info.tagName];
|
|
210
|
+
if (info.type)
|
|
211
|
+
desc.push(`type=${info.type}`);
|
|
212
|
+
if (info.role)
|
|
213
|
+
desc.push(`role=${info.role}`);
|
|
214
|
+
parts.push(`<${desc.join(' ')}>`);
|
|
215
|
+
if (info.label)
|
|
216
|
+
parts.push(`label="${info.label}"`);
|
|
217
|
+
if (info.text)
|
|
218
|
+
parts.push(`text="${info.text}"`);
|
|
219
|
+
if (info.href)
|
|
220
|
+
parts.push(`href="${info.href}"`);
|
|
221
|
+
return parts.join(' ');
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=element-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-info.js","sourceRoot":"","sources":["../../../../src/mcp/tools/browser/element-info.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AA8LH,8CAMC;AAOD,sDAMC;AAKD,8CAiBC;AAnND;;;GAGG;AACH,MAAM,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoG9B,CAAA;AAEF;;GAEG;AACH,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsD7B,CAAA;AAEF;;;GAGG;AACH,8DAA8D;AACvD,KAAK,UAAU,iBAAiB,CAAC,IAAS,EAAE,CAAS,EAAE,CAAS;IACrE,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,uBAAuB,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAuB,CAAA;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,8DAA8D;AACvD,KAAK,UAAU,qBAAqB,CAAC,IAAS;IACnD,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAuB,CAAA;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAAC,IAAiB;IACjD,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,oEAAoE;IACpE,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;IAEzC,sBAAsB;IACtB,MAAM,IAAI,GAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACrC,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7C,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7C,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAEjC,IAAI,IAAI,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;IACnD,IAAI,IAAI,CAAC,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;IAChD,IAAI,IAAI,CAAC,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;IAEhD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Playwright loader.
|
|
3
|
+
* Playwright is an optional dependency — this module handles its absence gracefully.
|
|
4
|
+
*/
|
|
5
|
+
type PlaywrightModule = any;
|
|
6
|
+
/**
|
|
7
|
+
* Check if Playwright is available (installed).
|
|
8
|
+
*/
|
|
9
|
+
export declare function isPlaywrightAvailable(): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Dynamically load the Playwright module.
|
|
12
|
+
* Throws a user-friendly error if not installed.
|
|
13
|
+
*/
|
|
14
|
+
export declare function loadPlaywright(): PlaywrightModule;
|
|
15
|
+
/**
|
|
16
|
+
* Reset internal cache (for testing).
|
|
17
|
+
*/
|
|
18
|
+
export declare function resetPlaywrightCache(): void;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=playwright-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-loader.d.ts","sourceRoot":"","sources":["../../../../src/mcp/tools/browser/playwright-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,KAAK,gBAAgB,GAAG,GAAG,CAAA;AAK3B;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAQ/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,IAAI,gBAAgB,CAYjD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Dynamic Playwright loader.
|
|
4
|
+
* Playwright is an optional dependency — this module handles its absence gracefully.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.isPlaywrightAvailable = isPlaywrightAvailable;
|
|
8
|
+
exports.loadPlaywright = loadPlaywright;
|
|
9
|
+
exports.resetPlaywrightCache = resetPlaywrightCache;
|
|
10
|
+
let cachedModule = null;
|
|
11
|
+
let loadAttempted = false;
|
|
12
|
+
/**
|
|
13
|
+
* Check if Playwright is available (installed).
|
|
14
|
+
*/
|
|
15
|
+
function isPlaywrightAvailable() {
|
|
16
|
+
if (loadAttempted)
|
|
17
|
+
return cachedModule !== null;
|
|
18
|
+
try {
|
|
19
|
+
require.resolve('playwright');
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Dynamically load the Playwright module.
|
|
28
|
+
* Throws a user-friendly error if not installed.
|
|
29
|
+
*/
|
|
30
|
+
function loadPlaywright() {
|
|
31
|
+
if (cachedModule)
|
|
32
|
+
return cachedModule;
|
|
33
|
+
loadAttempted = true;
|
|
34
|
+
try {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
36
|
+
cachedModule = require('playwright');
|
|
37
|
+
return cachedModule;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
throw new Error('Playwright is not installed. Install it with: npm install playwright && npx playwright install chromium');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Reset internal cache (for testing).
|
|
45
|
+
*/
|
|
46
|
+
function resetPlaywrightCache() {
|
|
47
|
+
cachedModule = null;
|
|
48
|
+
loadAttempted = false;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=playwright-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright-loader.js","sourceRoot":"","sources":["../../../../src/mcp/tools/browser/playwright-loader.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAWH,sDAQC;AAMD,wCAYC;AAKD,oDAGC;AAxCD,IAAI,YAAY,GAA4B,IAAI,CAAA;AAChD,IAAI,aAAa,GAAG,KAAK,CAAA;AAEzB;;GAEG;AACH,SAAgB,qBAAqB;IACnC,IAAI,aAAa;QAAE,OAAO,YAAY,KAAK,IAAI,CAAA;IAC/C,IAAI,CAAC;QACH,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;QAC7B,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,cAAc;IAC5B,IAAI,YAAY;QAAE,OAAO,YAAY,CAAA;IACrC,aAAa,GAAG,IAAI,CAAA;IACpB,IAAI,CAAC;QACH,iEAAiE;QACjE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;QACpC,OAAO,YAAY,CAAA;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,yGAAyG,CAC1G,CAAA;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB;IAClC,YAAY,GAAG,IAAI,CAAA;IACnB,aAAa,GAAG,KAAK,CAAA;AACvB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser MCP tools — headless browser navigation and interaction.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1: browser_navigate, browser_close
|
|
5
|
+
* Phase 2: browser_click, browser_fill, browser_get_text, browser_login
|
|
6
|
+
* Phase 3: browser_open_session, browser_close_session, browser_set_variable, browser_get_variable, browser_list_variables
|
|
7
|
+
*/
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { ApiClient } from '../../api-client';
|
|
10
|
+
import { BrowserSessionManager } from './browser/browser-session-manager';
|
|
11
|
+
/**
|
|
12
|
+
* Register all browser tools on the MCP server.
|
|
13
|
+
*/
|
|
14
|
+
export declare function registerBrowserTools(server: McpServer, apiClient: ApiClient, sessionManager?: BrowserSessionManager): void;
|
|
15
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/browser.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGnE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAK5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAA;AA2BzE;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAmB1H"}
|