firefox-devtools-mcp 0.3.0 → 0.5.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/README.md +4 -0
- package/dist/index.js +380 -348
- package/dist/snapshot.injected.global.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12192,6 +12192,9 @@ var init_core = __esm({
|
|
|
12192
12192
|
if (this.options.profilePath) {
|
|
12193
12193
|
firefoxOptions.setProfile(this.options.profilePath);
|
|
12194
12194
|
}
|
|
12195
|
+
if (this.options.acceptInsecureCerts) {
|
|
12196
|
+
firefoxOptions.setAcceptInsecureCerts(true);
|
|
12197
|
+
}
|
|
12195
12198
|
this.driver = await new Builder().forBrowser(Browser.FIREFOX).setFirefoxOptions(firefoxOptions).build();
|
|
12196
12199
|
log("\u2705 Firefox launched with BiDi");
|
|
12197
12200
|
this.currentContextId = await this.driver.getWindowHandle();
|
|
@@ -12211,6 +12214,30 @@ var init_core = __esm({
|
|
|
12211
12214
|
}
|
|
12212
12215
|
return this.driver;
|
|
12213
12216
|
}
|
|
12217
|
+
/**
|
|
12218
|
+
* Check if Firefox is still connected and responsive
|
|
12219
|
+
* Returns false if Firefox was closed or connection is broken
|
|
12220
|
+
*/
|
|
12221
|
+
async isConnected() {
|
|
12222
|
+
if (!this.driver) {
|
|
12223
|
+
return false;
|
|
12224
|
+
}
|
|
12225
|
+
try {
|
|
12226
|
+
await this.driver.getWindowHandle();
|
|
12227
|
+
return true;
|
|
12228
|
+
} catch (error) {
|
|
12229
|
+
logDebug("Connection check failed: Firefox is not responsive");
|
|
12230
|
+
return false;
|
|
12231
|
+
}
|
|
12232
|
+
}
|
|
12233
|
+
/**
|
|
12234
|
+
* Reset driver state (used when Firefox is detected as closed)
|
|
12235
|
+
*/
|
|
12236
|
+
reset() {
|
|
12237
|
+
this.driver = null;
|
|
12238
|
+
this.currentContextId = null;
|
|
12239
|
+
logDebug("Driver state reset");
|
|
12240
|
+
}
|
|
12214
12241
|
/**
|
|
12215
12242
|
* Get current browsing context ID
|
|
12216
12243
|
*/
|
|
@@ -12555,10 +12582,42 @@ var init_network = __esm({
|
|
|
12555
12582
|
*/
|
|
12556
12583
|
parseHeaders(headers) {
|
|
12557
12584
|
const result = {};
|
|
12585
|
+
const normalizeValue = (value) => {
|
|
12586
|
+
if (value === null || value === void 0) {
|
|
12587
|
+
return null;
|
|
12588
|
+
}
|
|
12589
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
12590
|
+
return String(value);
|
|
12591
|
+
}
|
|
12592
|
+
if (Array.isArray(value)) {
|
|
12593
|
+
const parts = value.map((item) => normalizeValue(item)).filter((item) => !!item);
|
|
12594
|
+
return parts.length > 0 ? parts.join(", ") : null;
|
|
12595
|
+
}
|
|
12596
|
+
if (typeof value === "object") {
|
|
12597
|
+
const obj = value;
|
|
12598
|
+
if ("value" in obj) {
|
|
12599
|
+
return normalizeValue(obj.value);
|
|
12600
|
+
}
|
|
12601
|
+
if ("bytes" in obj) {
|
|
12602
|
+
return normalizeValue(obj.bytes);
|
|
12603
|
+
}
|
|
12604
|
+
try {
|
|
12605
|
+
return JSON.stringify(obj);
|
|
12606
|
+
} catch {
|
|
12607
|
+
return null;
|
|
12608
|
+
}
|
|
12609
|
+
}
|
|
12610
|
+
return String(value);
|
|
12611
|
+
};
|
|
12558
12612
|
if (Array.isArray(headers)) {
|
|
12559
12613
|
for (const h of headers) {
|
|
12560
|
-
|
|
12561
|
-
|
|
12614
|
+
const name = h?.name ? String(h.name).toLowerCase() : "";
|
|
12615
|
+
if (!name) {
|
|
12616
|
+
continue;
|
|
12617
|
+
}
|
|
12618
|
+
const normalizedValue = normalizeValue(h?.value);
|
|
12619
|
+
if (normalizedValue !== null) {
|
|
12620
|
+
result[name] = normalizedValue;
|
|
12562
12621
|
}
|
|
12563
12622
|
}
|
|
12564
12623
|
}
|
|
@@ -12986,6 +13045,7 @@ var init_pages = __esm({
|
|
|
12986
13045
|
if (index >= 0 && index < handles.length) {
|
|
12987
13046
|
await this.driver.switchTo().window(handles[index]);
|
|
12988
13047
|
this.setCurrentContextId(handles[index]);
|
|
13048
|
+
this.cachedSelectedIdx = index;
|
|
12989
13049
|
}
|
|
12990
13050
|
}
|
|
12991
13051
|
/**
|
|
@@ -12996,6 +13056,7 @@ var init_pages = __esm({
|
|
|
12996
13056
|
const handles = await this.driver.getAllWindowHandles();
|
|
12997
13057
|
const newIdx = handles.length - 1;
|
|
12998
13058
|
this.setCurrentContextId(handles[newIdx]);
|
|
13059
|
+
this.cachedSelectedIdx = newIdx;
|
|
12999
13060
|
await this.driver.get(url);
|
|
13000
13061
|
return newIdx;
|
|
13001
13062
|
}
|
|
@@ -13131,7 +13192,7 @@ var MAX_ATTR_LENGTH;
|
|
|
13131
13192
|
var init_formatter = __esm({
|
|
13132
13193
|
"src/firefox/snapshot/formatter.ts"() {
|
|
13133
13194
|
"use strict";
|
|
13134
|
-
MAX_ATTR_LENGTH =
|
|
13195
|
+
MAX_ATTR_LENGTH = 30;
|
|
13135
13196
|
}
|
|
13136
13197
|
});
|
|
13137
13198
|
|
|
@@ -13300,10 +13361,12 @@ var init_manager = __esm({
|
|
|
13300
13361
|
const currentFilePath = fileURLToPath(currentFileUrl);
|
|
13301
13362
|
const currentDir = dirname(currentFilePath);
|
|
13302
13363
|
const possiblePaths = [
|
|
13303
|
-
// Production: relative to
|
|
13304
|
-
resolve(currentDir, "
|
|
13305
|
-
//
|
|
13306
|
-
resolve(process.cwd(), "dist/snapshot.injected.global.js")
|
|
13364
|
+
// Production: relative to bundled dist/index.js (same directory)
|
|
13365
|
+
resolve(currentDir, "snapshot.injected.global.js"),
|
|
13366
|
+
// Development: relative to current working directory
|
|
13367
|
+
resolve(process.cwd(), "dist/snapshot.injected.global.js"),
|
|
13368
|
+
// npx: package is in node_modules, try to find it relative to the binary
|
|
13369
|
+
resolve(currentDir, "../snapshot.injected.global.js")
|
|
13307
13370
|
];
|
|
13308
13371
|
const attemptedPaths = [];
|
|
13309
13372
|
for (const path of possiblePaths) {
|
|
@@ -13358,7 +13421,8 @@ ${attemptedPaths.map((p) => ` - ${p}`).join("\n")}`
|
|
|
13358
13421
|
root: result.tree,
|
|
13359
13422
|
snapshotId,
|
|
13360
13423
|
timestamp: Date.now(),
|
|
13361
|
-
truncated: result.truncated || false
|
|
13424
|
+
truncated: result.truncated || false,
|
|
13425
|
+
uidMap: result.uidMap
|
|
13362
13426
|
};
|
|
13363
13427
|
const snapshot = {
|
|
13364
13428
|
text: formatSnapshotTree(result.tree),
|
|
@@ -13726,6 +13790,24 @@ var init_firefox = __esm({
|
|
|
13726
13790
|
getDriver() {
|
|
13727
13791
|
return this.core.getDriver();
|
|
13728
13792
|
}
|
|
13793
|
+
/**
|
|
13794
|
+
* Check if Firefox is still connected and responsive
|
|
13795
|
+
* Returns false if Firefox was closed or connection is broken
|
|
13796
|
+
*/
|
|
13797
|
+
async isConnected() {
|
|
13798
|
+
return await this.core.isConnected();
|
|
13799
|
+
}
|
|
13800
|
+
/**
|
|
13801
|
+
* Reset all internal state (used when Firefox is detected as closed)
|
|
13802
|
+
*/
|
|
13803
|
+
reset() {
|
|
13804
|
+
this.core.reset();
|
|
13805
|
+
this.consoleEvents = null;
|
|
13806
|
+
this.networkEvents = null;
|
|
13807
|
+
this.dom = null;
|
|
13808
|
+
this.pages = null;
|
|
13809
|
+
this.snapshot = null;
|
|
13810
|
+
}
|
|
13729
13811
|
// ============================================================================
|
|
13730
13812
|
// Cleanup
|
|
13731
13813
|
// ============================================================================
|
|
@@ -13737,6 +13819,30 @@ var init_firefox = __esm({
|
|
|
13737
13819
|
});
|
|
13738
13820
|
|
|
13739
13821
|
// src/utils/response-helpers.ts
|
|
13822
|
+
function truncateText(text, maxChars, suffix = "\n\n[... truncated - exceeded size limit]") {
|
|
13823
|
+
if (text.length <= maxChars) {
|
|
13824
|
+
return text;
|
|
13825
|
+
}
|
|
13826
|
+
return text.slice(0, maxChars - suffix.length) + suffix;
|
|
13827
|
+
}
|
|
13828
|
+
function truncateHeaders(headers) {
|
|
13829
|
+
if (!headers) {
|
|
13830
|
+
return null;
|
|
13831
|
+
}
|
|
13832
|
+
const result = {};
|
|
13833
|
+
let totalChars = 0;
|
|
13834
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
13835
|
+
const truncatedValue = value.length > TOKEN_LIMITS.MAX_HEADER_VALUE_CHARS ? value.slice(0, TOKEN_LIMITS.MAX_HEADER_VALUE_CHARS) + "...[truncated]" : value;
|
|
13836
|
+
const entrySize = key.length + truncatedValue.length;
|
|
13837
|
+
if (totalChars + entrySize > TOKEN_LIMITS.MAX_HEADERS_TOTAL_CHARS) {
|
|
13838
|
+
result["__truncated__"] = "Headers truncated due to size limit";
|
|
13839
|
+
break;
|
|
13840
|
+
}
|
|
13841
|
+
result[key] = truncatedValue;
|
|
13842
|
+
totalChars += entrySize;
|
|
13843
|
+
}
|
|
13844
|
+
return result;
|
|
13845
|
+
}
|
|
13740
13846
|
function successResponse(message) {
|
|
13741
13847
|
return {
|
|
13742
13848
|
content: [
|
|
@@ -13769,26 +13875,40 @@ function jsonResponse(data) {
|
|
|
13769
13875
|
]
|
|
13770
13876
|
};
|
|
13771
13877
|
}
|
|
13878
|
+
var TOKEN_LIMITS;
|
|
13772
13879
|
var init_response_helpers = __esm({
|
|
13773
13880
|
"src/utils/response-helpers.ts"() {
|
|
13774
13881
|
"use strict";
|
|
13882
|
+
TOKEN_LIMITS = {
|
|
13883
|
+
/** Maximum characters for a single response (~12.5k tokens at ~4 chars/token) */
|
|
13884
|
+
MAX_RESPONSE_CHARS: 5e4,
|
|
13885
|
+
/** Maximum characters for screenshot base64 data (~10k tokens) */
|
|
13886
|
+
MAX_SCREENSHOT_CHARS: 4e4,
|
|
13887
|
+
/** Maximum characters per console message text */
|
|
13888
|
+
MAX_CONSOLE_MESSAGE_CHARS: 2e3,
|
|
13889
|
+
/** Maximum characters for network header values (per header) */
|
|
13890
|
+
MAX_HEADER_VALUE_CHARS: 500,
|
|
13891
|
+
/** Maximum total characters for all headers combined */
|
|
13892
|
+
MAX_HEADERS_TOTAL_CHARS: 5e3,
|
|
13893
|
+
/** Hard cap on snapshot lines (even if user requests more) */
|
|
13894
|
+
MAX_SNAPSHOT_LINES_CAP: 500,
|
|
13895
|
+
/** Warning threshold - show warning when response exceeds this */
|
|
13896
|
+
WARNING_THRESHOLD_CHARS: 3e4
|
|
13897
|
+
};
|
|
13775
13898
|
}
|
|
13776
13899
|
});
|
|
13777
13900
|
|
|
13778
13901
|
// src/tools/pages.ts
|
|
13779
13902
|
function formatPageList(tabs, selectedIdx) {
|
|
13780
|
-
const lines = [`\u{1F4C4} Open pages (${tabs.length} total, selected: [${selectedIdx}]):`];
|
|
13781
13903
|
if (tabs.length === 0) {
|
|
13782
|
-
|
|
13783
|
-
}
|
|
13784
|
-
|
|
13785
|
-
|
|
13786
|
-
|
|
13787
|
-
|
|
13788
|
-
|
|
13789
|
-
|
|
13790
|
-
lines.push(` ${url}`);
|
|
13791
|
-
}
|
|
13904
|
+
return "\u{1F4C4} No pages";
|
|
13905
|
+
}
|
|
13906
|
+
const lines = [`\u{1F4C4} ${tabs.length} pages (selected: ${selectedIdx})`];
|
|
13907
|
+
for (const tab of tabs) {
|
|
13908
|
+
const idx = tabs.indexOf(tab);
|
|
13909
|
+
const marker = idx === selectedIdx ? ">" : " ";
|
|
13910
|
+
const title = (tab.title || "Untitled").substring(0, 40);
|
|
13911
|
+
lines.push(`${marker}[${idx}] ${title}`);
|
|
13792
13912
|
}
|
|
13793
13913
|
return lines.join("\n");
|
|
13794
13914
|
}
|
|
@@ -13813,14 +13933,7 @@ async function handleNewPage(args2) {
|
|
|
13813
13933
|
const { getFirefox: getFirefox2 } = await init_index().then(() => index_exports);
|
|
13814
13934
|
const firefox3 = await getFirefox2();
|
|
13815
13935
|
const newIdx = await firefox3.createNewPage(url);
|
|
13816
|
-
|
|
13817
|
-
const tabs = firefox3.getTabs();
|
|
13818
|
-
const newTab = tabs[newIdx];
|
|
13819
|
-
return successResponse(
|
|
13820
|
-
`\u2705 Created new page [${newIdx}] and navigated to: ${url}
|
|
13821
|
-
Title: ${newTab?.title || "Loading..."}
|
|
13822
|
-
Total pages: ${tabs.length}`
|
|
13823
|
-
);
|
|
13936
|
+
return successResponse(`\u2705 new page [${newIdx}] \u2192 ${url}`);
|
|
13824
13937
|
} catch (error) {
|
|
13825
13938
|
return errorResponse(error);
|
|
13826
13939
|
}
|
|
@@ -13841,10 +13954,7 @@ async function handleNavigatePage(args2) {
|
|
|
13841
13954
|
throw new Error("No page selected");
|
|
13842
13955
|
}
|
|
13843
13956
|
await firefox3.navigate(url);
|
|
13844
|
-
return successResponse(
|
|
13845
|
-
`\u2705 Navigated page [${selectedIdx}] to: ${url}
|
|
13846
|
-
Previous URL: ${page.url}`
|
|
13847
|
-
);
|
|
13957
|
+
return successResponse(`\u2705 [${selectedIdx}] \u2192 ${url}`);
|
|
13848
13958
|
} catch (error) {
|
|
13849
13959
|
return errorResponse(error);
|
|
13850
13960
|
}
|
|
@@ -13857,47 +13967,30 @@ async function handleSelectPage(args2) {
|
|
|
13857
13967
|
await firefox3.refreshTabs();
|
|
13858
13968
|
const tabs = firefox3.getTabs();
|
|
13859
13969
|
let selectedIdx;
|
|
13860
|
-
let selectionMethod;
|
|
13861
13970
|
if (typeof pageIdx === "number") {
|
|
13862
13971
|
selectedIdx = pageIdx;
|
|
13863
|
-
selectionMethod = "by index";
|
|
13864
13972
|
} else if (url && typeof url === "string") {
|
|
13865
13973
|
const urlLower = url.toLowerCase();
|
|
13866
13974
|
const foundIdx = tabs.findIndex((tab) => tab.url?.toLowerCase().includes(urlLower));
|
|
13867
13975
|
if (foundIdx === -1) {
|
|
13868
|
-
throw new Error(
|
|
13869
|
-
`No page found with URL matching "${url}". Use list_pages to see all available pages.`
|
|
13870
|
-
);
|
|
13976
|
+
throw new Error(`No page matching URL "${url}"`);
|
|
13871
13977
|
}
|
|
13872
13978
|
selectedIdx = foundIdx;
|
|
13873
|
-
selectionMethod = `by URL pattern "${url}"`;
|
|
13874
13979
|
} else if (title && typeof title === "string") {
|
|
13875
13980
|
const titleLower = title.toLowerCase();
|
|
13876
13981
|
const foundIdx = tabs.findIndex((tab) => tab.title?.toLowerCase().includes(titleLower));
|
|
13877
13982
|
if (foundIdx === -1) {
|
|
13878
|
-
throw new Error(
|
|
13879
|
-
`No page found with title matching "${title}". Use list_pages to see all available pages.`
|
|
13880
|
-
);
|
|
13983
|
+
throw new Error(`No page matching title "${title}"`);
|
|
13881
13984
|
}
|
|
13882
13985
|
selectedIdx = foundIdx;
|
|
13883
|
-
selectionMethod = `by title pattern "${title}"`;
|
|
13884
13986
|
} else {
|
|
13885
|
-
throw new Error(
|
|
13886
|
-
"At least one of pageIdx, url, or title must be provided. Use list_pages to see available pages."
|
|
13887
|
-
);
|
|
13987
|
+
throw new Error("Provide pageIdx, url, or title");
|
|
13888
13988
|
}
|
|
13889
|
-
|
|
13890
|
-
|
|
13891
|
-
throw new Error(
|
|
13892
|
-
`Page at index ${selectedIdx} not found. Use list_pages to see valid indices.`
|
|
13893
|
-
);
|
|
13989
|
+
if (!tabs[selectedIdx]) {
|
|
13990
|
+
throw new Error(`Page [${selectedIdx}] not found`);
|
|
13894
13991
|
}
|
|
13895
13992
|
await firefox3.selectTab(selectedIdx);
|
|
13896
|
-
return successResponse(
|
|
13897
|
-
`\u2705 Selected page [${selectedIdx}] ${selectionMethod}
|
|
13898
|
-
Title: ${page.title || "Untitled"}
|
|
13899
|
-
URL: ${page.url || "about:blank"}`
|
|
13900
|
-
);
|
|
13993
|
+
return successResponse(`\u2705 selected [${selectedIdx}]`);
|
|
13901
13994
|
} catch (error) {
|
|
13902
13995
|
return errorResponse(error);
|
|
13903
13996
|
}
|
|
@@ -13917,10 +14010,7 @@ async function handleClosePage(args2) {
|
|
|
13917
14010
|
throw new Error(`Page with index ${pageIdx} not found`);
|
|
13918
14011
|
}
|
|
13919
14012
|
await firefox3.closeTab(pageIdx);
|
|
13920
|
-
return successResponse(
|
|
13921
|
-
`\u2705 Closed page [${pageIdx}]: ${pageToClose.title}
|
|
13922
|
-
${pageToClose.url}`
|
|
13923
|
-
);
|
|
14013
|
+
return successResponse(`\u2705 closed [${pageIdx}]`);
|
|
13924
14014
|
} catch (error) {
|
|
13925
14015
|
return errorResponse(error);
|
|
13926
14016
|
}
|
|
@@ -13932,7 +14022,7 @@ var init_pages2 = __esm({
|
|
|
13932
14022
|
init_response_helpers();
|
|
13933
14023
|
listPagesTool = {
|
|
13934
14024
|
name: "list_pages",
|
|
13935
|
-
description: "List
|
|
14025
|
+
description: "List open tabs (index, title, URL). Selected tab is marked.",
|
|
13936
14026
|
inputSchema: {
|
|
13937
14027
|
type: "object",
|
|
13938
14028
|
properties: {}
|
|
@@ -13940,13 +14030,13 @@ var init_pages2 = __esm({
|
|
|
13940
14030
|
};
|
|
13941
14031
|
newPageTool = {
|
|
13942
14032
|
name: "new_page",
|
|
13943
|
-
description: "Open
|
|
14033
|
+
description: "Open new tab at URL. Returns tab index.",
|
|
13944
14034
|
inputSchema: {
|
|
13945
14035
|
type: "object",
|
|
13946
14036
|
properties: {
|
|
13947
14037
|
url: {
|
|
13948
14038
|
type: "string",
|
|
13949
|
-
description: "URL
|
|
14039
|
+
description: "Target URL"
|
|
13950
14040
|
}
|
|
13951
14041
|
},
|
|
13952
14042
|
required: ["url"]
|
|
@@ -13954,13 +14044,13 @@ var init_pages2 = __esm({
|
|
|
13954
14044
|
};
|
|
13955
14045
|
navigatePageTool = {
|
|
13956
14046
|
name: "navigate_page",
|
|
13957
|
-
description: "Navigate
|
|
14047
|
+
description: "Navigate selected tab to URL.",
|
|
13958
14048
|
inputSchema: {
|
|
13959
14049
|
type: "object",
|
|
13960
14050
|
properties: {
|
|
13961
14051
|
url: {
|
|
13962
14052
|
type: "string",
|
|
13963
|
-
description: "URL
|
|
14053
|
+
description: "Target URL"
|
|
13964
14054
|
}
|
|
13965
14055
|
},
|
|
13966
14056
|
required: ["url"]
|
|
@@ -13968,21 +14058,21 @@ var init_pages2 = __esm({
|
|
|
13968
14058
|
};
|
|
13969
14059
|
selectPageTool = {
|
|
13970
14060
|
name: "select_page",
|
|
13971
|
-
description: "Select
|
|
14061
|
+
description: "Select active tab by index, URL, or title. Index takes precedence.",
|
|
13972
14062
|
inputSchema: {
|
|
13973
14063
|
type: "object",
|
|
13974
14064
|
properties: {
|
|
13975
14065
|
pageIdx: {
|
|
13976
14066
|
type: "number",
|
|
13977
|
-
description: "
|
|
14067
|
+
description: "Tab index (0-based, most reliable)"
|
|
13978
14068
|
},
|
|
13979
14069
|
url: {
|
|
13980
14070
|
type: "string",
|
|
13981
|
-
description:
|
|
14071
|
+
description: "URL substring (case-insensitive)"
|
|
13982
14072
|
},
|
|
13983
14073
|
title: {
|
|
13984
14074
|
type: "string",
|
|
13985
|
-
description:
|
|
14075
|
+
description: "Title substring (case-insensitive)"
|
|
13986
14076
|
}
|
|
13987
14077
|
},
|
|
13988
14078
|
required: []
|
|
@@ -13990,13 +14080,13 @@ var init_pages2 = __esm({
|
|
|
13990
14080
|
};
|
|
13991
14081
|
closePageTool = {
|
|
13992
14082
|
name: "close_page",
|
|
13993
|
-
description: "Close
|
|
14083
|
+
description: "Close tab by index.",
|
|
13994
14084
|
inputSchema: {
|
|
13995
14085
|
type: "object",
|
|
13996
14086
|
properties: {
|
|
13997
14087
|
pageIdx: {
|
|
13998
14088
|
type: "number",
|
|
13999
|
-
description: "
|
|
14089
|
+
description: "Tab index to close"
|
|
14000
14090
|
}
|
|
14001
14091
|
},
|
|
14002
14092
|
required: ["pageIdx"]
|
|
@@ -14034,6 +14124,10 @@ async function handleListConsoleMessages(args2) {
|
|
|
14034
14124
|
if (source) {
|
|
14035
14125
|
messages = messages.filter((msg) => msg.source?.toLowerCase() === source.toLowerCase());
|
|
14036
14126
|
}
|
|
14127
|
+
messages = messages.map((msg) => ({
|
|
14128
|
+
...msg,
|
|
14129
|
+
text: truncateText(msg.text, TOKEN_LIMITS.MAX_CONSOLE_MESSAGE_CHARS, "...[truncated]")
|
|
14130
|
+
}));
|
|
14037
14131
|
const maxLimit = limit ?? DEFAULT_LIMIT;
|
|
14038
14132
|
const filteredCount = messages.length;
|
|
14039
14133
|
const truncated = messages.length > maxLimit;
|
|
@@ -14127,7 +14221,7 @@ Total messages: ${totalCount}${filterInfo.length > 0 ? `, Filters: ${filterInfo.
|
|
|
14127
14221
|
}
|
|
14128
14222
|
if (truncated) {
|
|
14129
14223
|
output += `
|
|
14130
|
-
|
|
14224
|
+
[+${filteredCount - messages.length} more]`;
|
|
14131
14225
|
}
|
|
14132
14226
|
return successResponse(output);
|
|
14133
14227
|
} catch (error) {
|
|
@@ -14140,11 +14234,7 @@ async function handleClearConsoleMessages(_args) {
|
|
|
14140
14234
|
const firefox3 = await getFirefox2();
|
|
14141
14235
|
const count = (await firefox3.getConsoleMessages()).length;
|
|
14142
14236
|
firefox3.clearConsoleMessages();
|
|
14143
|
-
return successResponse(
|
|
14144
|
-
`Cleared ${count} console message(s) from buffer.
|
|
14145
|
-
|
|
14146
|
-
You can now capture fresh console output from new page activity.`
|
|
14147
|
-
);
|
|
14237
|
+
return successResponse(`\u2705 cleared ${count} messages`);
|
|
14148
14238
|
} catch (error) {
|
|
14149
14239
|
return errorResponse(error);
|
|
14150
14240
|
}
|
|
@@ -14156,42 +14246,42 @@ var init_console2 = __esm({
|
|
|
14156
14246
|
init_response_helpers();
|
|
14157
14247
|
listConsoleMessagesTool = {
|
|
14158
14248
|
name: "list_console_messages",
|
|
14159
|
-
description: "List console messages
|
|
14249
|
+
description: "List console messages. Supports filtering by level, time, text, source.",
|
|
14160
14250
|
inputSchema: {
|
|
14161
14251
|
type: "object",
|
|
14162
14252
|
properties: {
|
|
14163
14253
|
level: {
|
|
14164
14254
|
type: "string",
|
|
14165
14255
|
enum: ["debug", "info", "warn", "error"],
|
|
14166
|
-
description: "Filter by
|
|
14256
|
+
description: "Filter by level"
|
|
14167
14257
|
},
|
|
14168
14258
|
limit: {
|
|
14169
14259
|
type: "number",
|
|
14170
|
-
description: "
|
|
14260
|
+
description: "Max messages (default: 50)"
|
|
14171
14261
|
},
|
|
14172
14262
|
sinceMs: {
|
|
14173
14263
|
type: "number",
|
|
14174
|
-
description: "Only
|
|
14264
|
+
description: "Only last N ms"
|
|
14175
14265
|
},
|
|
14176
14266
|
textContains: {
|
|
14177
14267
|
type: "string",
|
|
14178
|
-
description: "
|
|
14268
|
+
description: "Text filter (case-insensitive)"
|
|
14179
14269
|
},
|
|
14180
14270
|
source: {
|
|
14181
14271
|
type: "string",
|
|
14182
|
-
description:
|
|
14272
|
+
description: "Filter by source"
|
|
14183
14273
|
},
|
|
14184
14274
|
format: {
|
|
14185
14275
|
type: "string",
|
|
14186
14276
|
enum: ["text", "json"],
|
|
14187
|
-
description: "Output format
|
|
14277
|
+
description: "Output format (default: text)"
|
|
14188
14278
|
}
|
|
14189
14279
|
}
|
|
14190
14280
|
}
|
|
14191
14281
|
};
|
|
14192
14282
|
clearConsoleMessagesTool = {
|
|
14193
14283
|
name: "clear_console_messages",
|
|
14194
|
-
description: "Clear
|
|
14284
|
+
description: "Clear collected console messages.",
|
|
14195
14285
|
inputSchema: {
|
|
14196
14286
|
type: "object",
|
|
14197
14287
|
properties: {}
|
|
@@ -14292,8 +14382,8 @@ async function handleListNetworkRequests(args2) {
|
|
|
14292
14382
|
resourceType: req.resourceType,
|
|
14293
14383
|
isXHR: req.isXHR,
|
|
14294
14384
|
timings: req.timings || null,
|
|
14295
|
-
requestHeaders: req.requestHeaders
|
|
14296
|
-
responseHeaders: req.responseHeaders
|
|
14385
|
+
requestHeaders: truncateHeaders(req.requestHeaders),
|
|
14386
|
+
responseHeaders: truncateHeaders(req.responseHeaders)
|
|
14297
14387
|
}));
|
|
14298
14388
|
}
|
|
14299
14389
|
return jsonResponse(responseData);
|
|
@@ -14303,15 +14393,9 @@ async function handleListNetworkRequests(args2) {
|
|
|
14303
14393
|
const statusInfo = req.status ? `[${req.status}${req.statusText ? " " + req.statusText : ""}]` : "[pending]";
|
|
14304
14394
|
return `${req.id} | ${req.method} ${req.url} ${statusInfo}${req.isXHR ? " (XHR)" : ""}`;
|
|
14305
14395
|
});
|
|
14306
|
-
const
|
|
14307
|
-
|
|
14308
|
-
|
|
14309
|
-
"Network Requests:",
|
|
14310
|
-
...formattedRequests,
|
|
14311
|
-
"",
|
|
14312
|
-
"TIP: Use the request ID (first column) with get_network_request for full details."
|
|
14313
|
-
].join("\n");
|
|
14314
|
-
return successResponse(summary);
|
|
14396
|
+
const header = `\u{1F4E1} ${requests.length} requests${hasMore ? ` (limit ${limit})` : ""}
|
|
14397
|
+
`;
|
|
14398
|
+
return successResponse(header + formattedRequests.join("\n"));
|
|
14315
14399
|
} else if (detail === "min") {
|
|
14316
14400
|
const minData = limitedRequests.map((req) => ({
|
|
14317
14401
|
id: req.id,
|
|
@@ -14324,8 +14408,7 @@ async function handleListNetworkRequests(args2) {
|
|
|
14324
14408
|
duration: req.timings?.duration
|
|
14325
14409
|
}));
|
|
14326
14410
|
return successResponse(
|
|
14327
|
-
|
|
14328
|
-
|
|
14411
|
+
`\u{1F4E1} ${requests.length} requests${hasMore ? ` (limit ${limit})` : ""}
|
|
14329
14412
|
` + JSON.stringify(minData, null, 2)
|
|
14330
14413
|
);
|
|
14331
14414
|
} else {
|
|
@@ -14338,19 +14421,16 @@ async function handleListNetworkRequests(args2) {
|
|
|
14338
14421
|
resourceType: req.resourceType,
|
|
14339
14422
|
isXHR: req.isXHR,
|
|
14340
14423
|
timings: req.timings || null,
|
|
14341
|
-
requestHeaders: req.requestHeaders
|
|
14342
|
-
responseHeaders: req.responseHeaders
|
|
14424
|
+
requestHeaders: truncateHeaders(req.requestHeaders),
|
|
14425
|
+
responseHeaders: truncateHeaders(req.responseHeaders)
|
|
14343
14426
|
}));
|
|
14344
14427
|
return successResponse(
|
|
14345
|
-
|
|
14346
|
-
|
|
14428
|
+
`\u{1F4E1} ${requests.length} requests${hasMore ? ` (limit ${limit})` : ""}
|
|
14347
14429
|
` + JSON.stringify(fullData, null, 2)
|
|
14348
14430
|
);
|
|
14349
14431
|
}
|
|
14350
14432
|
} catch (error) {
|
|
14351
|
-
return errorResponse(
|
|
14352
|
-
`Failed to list network requests: ${error instanceof Error ? error.message : String(error)}`
|
|
14353
|
-
);
|
|
14433
|
+
return errorResponse(error instanceof Error ? error : new Error(String(error)));
|
|
14354
14434
|
}
|
|
14355
14435
|
}
|
|
14356
14436
|
async function handleGetNetworkRequest(args2) {
|
|
@@ -14361,9 +14441,7 @@ async function handleGetNetworkRequest(args2) {
|
|
|
14361
14441
|
format = "text"
|
|
14362
14442
|
} = args2;
|
|
14363
14443
|
if (!id && !url) {
|
|
14364
|
-
return errorResponse(
|
|
14365
|
-
'Either "id" or "url" parameter is required.\n\nTIP: Call list_network_requests first and use the returned ID for reliable lookup.'
|
|
14366
|
-
);
|
|
14444
|
+
return errorResponse("id or url required");
|
|
14367
14445
|
}
|
|
14368
14446
|
const { getFirefox: getFirefox2 } = await init_index().then(() => index_exports);
|
|
14369
14447
|
const firefox3 = await getFirefox2();
|
|
@@ -14372,29 +14450,16 @@ async function handleGetNetworkRequest(args2) {
|
|
|
14372
14450
|
if (id) {
|
|
14373
14451
|
request = requests.find((req) => req.id === id);
|
|
14374
14452
|
if (!request) {
|
|
14375
|
-
return errorResponse(
|
|
14376
|
-
`No network request found with ID: ${id}
|
|
14377
|
-
|
|
14378
|
-
TIP: The request may have been cleared. Call list_network_requests to see available requests.`
|
|
14379
|
-
);
|
|
14453
|
+
return errorResponse(`ID ${id} not found`);
|
|
14380
14454
|
}
|
|
14381
14455
|
} else if (url) {
|
|
14382
14456
|
const matches = requests.filter((req) => req.url === url);
|
|
14383
14457
|
if (matches.length === 0) {
|
|
14384
|
-
return errorResponse(
|
|
14385
|
-
`No network request found with URL: ${url}
|
|
14386
|
-
|
|
14387
|
-
TIP: Use list_network_requests to see available requests.`
|
|
14388
|
-
);
|
|
14458
|
+
return errorResponse(`URL not found: ${url}`);
|
|
14389
14459
|
}
|
|
14390
14460
|
if (matches.length > 1) {
|
|
14391
|
-
const
|
|
14392
|
-
return errorResponse(
|
|
14393
|
-
`Multiple requests (${matches.length}) found with URL: ${url}
|
|
14394
|
-
|
|
14395
|
-
Please use one of these IDs with the "id" parameter:
|
|
14396
|
-
` + matchInfo
|
|
14397
|
-
);
|
|
14461
|
+
const ids = matches.map((req) => req.id).join(", ");
|
|
14462
|
+
return errorResponse(`Multiple matches, use id: ${ids}`);
|
|
14398
14463
|
}
|
|
14399
14464
|
request = matches[0];
|
|
14400
14465
|
}
|
|
@@ -14411,17 +14476,15 @@ Please use one of these IDs with the "id" parameter:
|
|
|
14411
14476
|
isXHR: request.isXHR ?? false,
|
|
14412
14477
|
timestamp: request.timestamp ?? null,
|
|
14413
14478
|
timings: request.timings ?? null,
|
|
14414
|
-
requestHeaders: request.requestHeaders
|
|
14415
|
-
responseHeaders: request.responseHeaders
|
|
14479
|
+
requestHeaders: truncateHeaders(request.requestHeaders),
|
|
14480
|
+
responseHeaders: truncateHeaders(request.responseHeaders)
|
|
14416
14481
|
};
|
|
14417
14482
|
if (format === "json") {
|
|
14418
14483
|
return jsonResponse(details);
|
|
14419
14484
|
}
|
|
14420
|
-
return successResponse(
|
|
14485
|
+
return successResponse(JSON.stringify(details, null, 2));
|
|
14421
14486
|
} catch (error) {
|
|
14422
|
-
return errorResponse(
|
|
14423
|
-
`Failed to get network request: ${error instanceof Error ? error.message : String(error)}`
|
|
14424
|
-
);
|
|
14487
|
+
return errorResponse(error instanceof Error ? error : new Error(String(error)));
|
|
14425
14488
|
}
|
|
14426
14489
|
}
|
|
14427
14490
|
var listNetworkRequestsTool, getNetworkRequestTool;
|
|
@@ -14431,82 +14494,82 @@ var init_network2 = __esm({
|
|
|
14431
14494
|
init_response_helpers();
|
|
14432
14495
|
listNetworkRequestsTool = {
|
|
14433
14496
|
name: "list_network_requests",
|
|
14434
|
-
description: "List
|
|
14497
|
+
description: "List network requests. Returns IDs for get_network_request.",
|
|
14435
14498
|
inputSchema: {
|
|
14436
14499
|
type: "object",
|
|
14437
14500
|
properties: {
|
|
14438
14501
|
limit: {
|
|
14439
14502
|
type: "number",
|
|
14440
|
-
description: "
|
|
14503
|
+
description: "Max requests (default: 50)"
|
|
14441
14504
|
},
|
|
14442
14505
|
sinceMs: {
|
|
14443
14506
|
type: "number",
|
|
14444
|
-
description: "
|
|
14507
|
+
description: "Only last N ms"
|
|
14445
14508
|
},
|
|
14446
14509
|
urlContains: {
|
|
14447
14510
|
type: "string",
|
|
14448
|
-
description: "
|
|
14511
|
+
description: "URL filter (case-insensitive)"
|
|
14449
14512
|
},
|
|
14450
14513
|
method: {
|
|
14451
14514
|
type: "string",
|
|
14452
|
-
description: "
|
|
14515
|
+
description: "HTTP method filter"
|
|
14453
14516
|
},
|
|
14454
14517
|
status: {
|
|
14455
14518
|
type: "number",
|
|
14456
|
-
description: "
|
|
14519
|
+
description: "Exact status code"
|
|
14457
14520
|
},
|
|
14458
14521
|
statusMin: {
|
|
14459
14522
|
type: "number",
|
|
14460
|
-
description: "
|
|
14523
|
+
description: "Min status code"
|
|
14461
14524
|
},
|
|
14462
14525
|
statusMax: {
|
|
14463
14526
|
type: "number",
|
|
14464
|
-
description: "
|
|
14527
|
+
description: "Max status code"
|
|
14465
14528
|
},
|
|
14466
14529
|
isXHR: {
|
|
14467
14530
|
type: "boolean",
|
|
14468
|
-
description: "
|
|
14531
|
+
description: "XHR/fetch only"
|
|
14469
14532
|
},
|
|
14470
14533
|
resourceType: {
|
|
14471
14534
|
type: "string",
|
|
14472
|
-
description: "
|
|
14535
|
+
description: "Resource type filter"
|
|
14473
14536
|
},
|
|
14474
14537
|
sortBy: {
|
|
14475
14538
|
type: "string",
|
|
14476
14539
|
enum: ["timestamp", "duration", "status"],
|
|
14477
|
-
description: "Sort
|
|
14540
|
+
description: "Sort field (default: timestamp)"
|
|
14478
14541
|
},
|
|
14479
14542
|
detail: {
|
|
14480
14543
|
type: "string",
|
|
14481
14544
|
enum: ["summary", "min", "full"],
|
|
14482
|
-
description: "
|
|
14545
|
+
description: "Detail level (default: summary)"
|
|
14483
14546
|
},
|
|
14484
14547
|
format: {
|
|
14485
14548
|
type: "string",
|
|
14486
14549
|
enum: ["text", "json"],
|
|
14487
|
-
description: "Output format
|
|
14550
|
+
description: "Output format (default: text)"
|
|
14488
14551
|
}
|
|
14489
14552
|
}
|
|
14490
14553
|
}
|
|
14491
14554
|
};
|
|
14492
14555
|
getNetworkRequestTool = {
|
|
14493
14556
|
name: "get_network_request",
|
|
14494
|
-
description: "Get
|
|
14557
|
+
description: "Get request details by ID. URL lookup as fallback.",
|
|
14495
14558
|
inputSchema: {
|
|
14496
14559
|
type: "object",
|
|
14497
14560
|
properties: {
|
|
14498
14561
|
id: {
|
|
14499
14562
|
type: "string",
|
|
14500
|
-
description: "
|
|
14563
|
+
description: "Request ID from list_network_requests"
|
|
14501
14564
|
},
|
|
14502
14565
|
url: {
|
|
14503
14566
|
type: "string",
|
|
14504
|
-
description: "
|
|
14567
|
+
description: "URL fallback (may match multiple)"
|
|
14505
14568
|
},
|
|
14506
14569
|
format: {
|
|
14507
14570
|
type: "string",
|
|
14508
14571
|
enum: ["text", "json"],
|
|
14509
|
-
description: "Output format
|
|
14572
|
+
description: "Output format (default: text)"
|
|
14510
14573
|
}
|
|
14511
14574
|
}
|
|
14512
14575
|
}
|
|
@@ -14514,15 +14577,31 @@ var init_network2 = __esm({
|
|
|
14514
14577
|
}
|
|
14515
14578
|
});
|
|
14516
14579
|
|
|
14580
|
+
// src/utils/uid-helpers.ts
|
|
14581
|
+
function handleUidError(error, uid) {
|
|
14582
|
+
const errorMsg = error.message;
|
|
14583
|
+
if (errorMsg.includes("stale") || errorMsg.includes("Snapshot") || errorMsg.includes("UID") || errorMsg.includes("not found")) {
|
|
14584
|
+
return new Error(`${uid} stale/invalid. Call take_snapshot first.`);
|
|
14585
|
+
}
|
|
14586
|
+
return error;
|
|
14587
|
+
}
|
|
14588
|
+
var init_uid_helpers = __esm({
|
|
14589
|
+
"src/utils/uid-helpers.ts"() {
|
|
14590
|
+
"use strict";
|
|
14591
|
+
}
|
|
14592
|
+
});
|
|
14593
|
+
|
|
14517
14594
|
// src/tools/snapshot.ts
|
|
14518
14595
|
async function handleTakeSnapshot(args2) {
|
|
14519
14596
|
try {
|
|
14520
14597
|
const {
|
|
14521
|
-
maxLines =
|
|
14598
|
+
maxLines: requestedMaxLines = DEFAULT_SNAPSHOT_LINES,
|
|
14522
14599
|
includeAttributes = false,
|
|
14523
14600
|
includeText = true,
|
|
14524
14601
|
maxDepth
|
|
14525
14602
|
} = args2 || {};
|
|
14603
|
+
const maxLines = Math.min(Math.max(1, requestedMaxLines), TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP);
|
|
14604
|
+
const wasCapped = requestedMaxLines > TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP;
|
|
14526
14605
|
const { getFirefox: getFirefox2 } = await init_index().then(() => index_exports);
|
|
14527
14606
|
const firefox3 = await getFirefox2();
|
|
14528
14607
|
const snapshot = await firefox3.takeSnapshot();
|
|
@@ -14538,23 +14617,19 @@ async function handleTakeSnapshot(args2) {
|
|
|
14538
14617
|
const lines = formattedText.split("\n");
|
|
14539
14618
|
const truncated = lines.length > maxLines;
|
|
14540
14619
|
const displayLines = truncated ? lines.slice(0, maxLines) : lines;
|
|
14541
|
-
let output =
|
|
14542
|
-
|
|
14543
|
-
|
|
14544
|
-
|
|
14545
|
-
output += "\u2022 On stale UID errors: call take_snapshot \u2192 retry your action\n";
|
|
14546
|
-
output += "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n";
|
|
14547
|
-
output += `Snapshot ID: ${snapshot.json.snapshotId}
|
|
14548
|
-
`;
|
|
14620
|
+
let output = `\u{1F4F8} Snapshot (id=${snapshot.json.snapshotId})`;
|
|
14621
|
+
if (wasCapped) {
|
|
14622
|
+
output += ` [maxLines capped: ${TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP}]`;
|
|
14623
|
+
}
|
|
14549
14624
|
if (snapshot.json.truncated) {
|
|
14550
|
-
output += "
|
|
14625
|
+
output += " [DOM truncated]";
|
|
14551
14626
|
}
|
|
14552
|
-
output += "\n";
|
|
14627
|
+
output += "\n\n";
|
|
14553
14628
|
output += displayLines.join("\n");
|
|
14554
14629
|
if (truncated) {
|
|
14555
14630
|
output += `
|
|
14556
14631
|
|
|
14557
|
-
|
|
14632
|
+
[+${lines.length - maxLines} lines, use maxLines to see more]`;
|
|
14558
14633
|
}
|
|
14559
14634
|
return successResponse(output);
|
|
14560
14635
|
} catch (error) {
|
|
@@ -14577,20 +14652,9 @@ async function handleResolveUidToSelector(args2) {
|
|
|
14577
14652
|
const firefox3 = await getFirefox2();
|
|
14578
14653
|
try {
|
|
14579
14654
|
const selector = firefox3.resolveUidToSelector(uid);
|
|
14580
|
-
return successResponse(
|
|
14581
|
-
|
|
14582
|
-
${selector}`);
|
|
14655
|
+
return successResponse(`${uid} \u2192 ${selector}`);
|
|
14583
14656
|
} catch (error) {
|
|
14584
|
-
|
|
14585
|
-
if (errorMsg.includes("stale") || errorMsg.includes("Snapshot") || errorMsg.includes("UID") || errorMsg.includes("not found")) {
|
|
14586
|
-
throw new Error(
|
|
14587
|
-
`UID "${uid}" is from an old snapshot or not found.
|
|
14588
|
-
|
|
14589
|
-
The page structure may have changed since the snapshot was taken.
|
|
14590
|
-
Please call take_snapshot to get fresh UIDs and try again.`
|
|
14591
|
-
);
|
|
14592
|
-
}
|
|
14593
|
-
throw error;
|
|
14657
|
+
throw handleUidError(error, uid);
|
|
14594
14658
|
}
|
|
14595
14659
|
} catch (error) {
|
|
14596
14660
|
return errorResponse(error);
|
|
@@ -14601,53 +14665,52 @@ async function handleClearSnapshot(_args) {
|
|
|
14601
14665
|
const { getFirefox: getFirefox2 } = await init_index().then(() => index_exports);
|
|
14602
14666
|
const firefox3 = await getFirefox2();
|
|
14603
14667
|
firefox3.clearSnapshot();
|
|
14604
|
-
return successResponse(
|
|
14605
|
-
"\u{1F9F9} Snapshot cache cleared.\n\nFor the next UID-dependent action, take a fresh snapshot first."
|
|
14606
|
-
);
|
|
14668
|
+
return successResponse("\u{1F9F9} Snapshot cleared");
|
|
14607
14669
|
} catch (error) {
|
|
14608
14670
|
return errorResponse(error);
|
|
14609
14671
|
}
|
|
14610
14672
|
}
|
|
14611
|
-
var
|
|
14673
|
+
var DEFAULT_SNAPSHOT_LINES, takeSnapshotTool, resolveUidToSelectorTool, clearSnapshotTool;
|
|
14612
14674
|
var init_snapshot2 = __esm({
|
|
14613
14675
|
"src/tools/snapshot.ts"() {
|
|
14614
14676
|
"use strict";
|
|
14615
14677
|
init_response_helpers();
|
|
14616
|
-
|
|
14678
|
+
init_uid_helpers();
|
|
14679
|
+
DEFAULT_SNAPSHOT_LINES = 100;
|
|
14617
14680
|
takeSnapshotTool = {
|
|
14618
14681
|
name: "take_snapshot",
|
|
14619
|
-
description: "Capture
|
|
14682
|
+
description: "Capture DOM snapshot with stable UIDs. Retake after navigation.",
|
|
14620
14683
|
inputSchema: {
|
|
14621
14684
|
type: "object",
|
|
14622
14685
|
properties: {
|
|
14623
14686
|
maxLines: {
|
|
14624
14687
|
type: "number",
|
|
14625
|
-
description: "
|
|
14688
|
+
description: "Max lines (default: 100)"
|
|
14626
14689
|
},
|
|
14627
14690
|
includeAttributes: {
|
|
14628
14691
|
type: "boolean",
|
|
14629
|
-
description: "Include
|
|
14692
|
+
description: "Include ARIA attributes (default: false)"
|
|
14630
14693
|
},
|
|
14631
14694
|
includeText: {
|
|
14632
14695
|
type: "boolean",
|
|
14633
|
-
description: "Include text
|
|
14696
|
+
description: "Include text (default: true)"
|
|
14634
14697
|
},
|
|
14635
14698
|
maxDepth: {
|
|
14636
14699
|
type: "number",
|
|
14637
|
-
description: "
|
|
14700
|
+
description: "Max tree depth"
|
|
14638
14701
|
}
|
|
14639
14702
|
}
|
|
14640
14703
|
}
|
|
14641
14704
|
};
|
|
14642
14705
|
resolveUidToSelectorTool = {
|
|
14643
14706
|
name: "resolve_uid_to_selector",
|
|
14644
|
-
description: "Resolve
|
|
14707
|
+
description: "Resolve UID to CSS selector. Fails if stale.",
|
|
14645
14708
|
inputSchema: {
|
|
14646
14709
|
type: "object",
|
|
14647
14710
|
properties: {
|
|
14648
14711
|
uid: {
|
|
14649
14712
|
type: "string",
|
|
14650
|
-
description: "
|
|
14713
|
+
description: "UID from snapshot"
|
|
14651
14714
|
}
|
|
14652
14715
|
},
|
|
14653
14716
|
required: ["uid"]
|
|
@@ -14655,7 +14718,7 @@ var init_snapshot2 = __esm({
|
|
|
14655
14718
|
};
|
|
14656
14719
|
clearSnapshotTool = {
|
|
14657
14720
|
name: "clear_snapshot",
|
|
14658
|
-
description: "Clear
|
|
14721
|
+
description: "Clear snapshot cache. Usually not needed.",
|
|
14659
14722
|
inputSchema: {
|
|
14660
14723
|
type: "object",
|
|
14661
14724
|
properties: {}
|
|
@@ -14665,18 +14728,6 @@ var init_snapshot2 = __esm({
|
|
|
14665
14728
|
});
|
|
14666
14729
|
|
|
14667
14730
|
// src/tools/input.ts
|
|
14668
|
-
function handleUidError(error, uid) {
|
|
14669
|
-
const errorMsg = error.message;
|
|
14670
|
-
if (errorMsg.includes("stale") || errorMsg.includes("Snapshot") || errorMsg.includes("UID") || errorMsg.includes("not found")) {
|
|
14671
|
-
return new Error(
|
|
14672
|
-
`UID "${uid}" is stale or invalid.
|
|
14673
|
-
|
|
14674
|
-
The page may have changed since the snapshot was taken.
|
|
14675
|
-
Please call take_snapshot to get fresh UIDs and try again.`
|
|
14676
|
-
);
|
|
14677
|
-
}
|
|
14678
|
-
return error;
|
|
14679
|
-
}
|
|
14680
14731
|
async function handleClickByUid(args2) {
|
|
14681
14732
|
try {
|
|
14682
14733
|
const { uid, dblClick } = args2;
|
|
@@ -14687,9 +14738,7 @@ async function handleClickByUid(args2) {
|
|
|
14687
14738
|
const firefox3 = await getFirefox2();
|
|
14688
14739
|
try {
|
|
14689
14740
|
await firefox3.clickByUid(uid, dblClick);
|
|
14690
|
-
return successResponse(
|
|
14691
|
-
`\u2705 ${dblClick ? "Double-clicked" : "Clicked"} element with UID "${uid}"`
|
|
14692
|
-
);
|
|
14741
|
+
return successResponse(`\u2705 ${dblClick ? "dblclick" : "click"} ${uid}`);
|
|
14693
14742
|
} catch (error) {
|
|
14694
14743
|
throw handleUidError(error, uid);
|
|
14695
14744
|
}
|
|
@@ -14707,7 +14756,7 @@ async function handleHoverByUid(args2) {
|
|
|
14707
14756
|
const firefox3 = await getFirefox2();
|
|
14708
14757
|
try {
|
|
14709
14758
|
await firefox3.hoverByUid(uid);
|
|
14710
|
-
return successResponse(`\u2705
|
|
14759
|
+
return successResponse(`\u2705 hover ${uid}`);
|
|
14711
14760
|
} catch (error) {
|
|
14712
14761
|
throw handleUidError(error, uid);
|
|
14713
14762
|
}
|
|
@@ -14728,10 +14777,7 @@ async function handleFillByUid(args2) {
|
|
|
14728
14777
|
const firefox3 = await getFirefox2();
|
|
14729
14778
|
try {
|
|
14730
14779
|
await firefox3.fillByUid(uid, value);
|
|
14731
|
-
return successResponse(
|
|
14732
|
-
`\u2705 Filled element with UID "${uid}"
|
|
14733
|
-
Value: ${value.substring(0, 50)}${value.length > 50 ? "..." : ""}`
|
|
14734
|
-
);
|
|
14780
|
+
return successResponse(`\u2705 fill ${uid}`);
|
|
14735
14781
|
} catch (error) {
|
|
14736
14782
|
throw handleUidError(error, uid);
|
|
14737
14783
|
}
|
|
@@ -14752,16 +14798,11 @@ async function handleDragByUidToUid(args2) {
|
|
|
14752
14798
|
const firefox3 = await getFirefox2();
|
|
14753
14799
|
try {
|
|
14754
14800
|
await firefox3.dragByUidToUid(fromUid, toUid);
|
|
14755
|
-
return successResponse(`\u2705
|
|
14801
|
+
return successResponse(`\u2705 drag ${fromUid}\u2192${toUid}`);
|
|
14756
14802
|
} catch (error) {
|
|
14757
14803
|
const errorMsg = error.message;
|
|
14758
14804
|
if (errorMsg.includes("stale") || errorMsg.includes("Snapshot") || errorMsg.includes("UID")) {
|
|
14759
|
-
throw new Error(
|
|
14760
|
-
`One or both UIDs (from: "${fromUid}", to: "${toUid}") are stale or invalid.
|
|
14761
|
-
|
|
14762
|
-
The page may have changed since the snapshot was taken.
|
|
14763
|
-
Please call take_snapshot to get fresh UIDs and try again.`
|
|
14764
|
-
);
|
|
14805
|
+
throw new Error(`UIDs stale/invalid. Call take_snapshot first.`);
|
|
14765
14806
|
}
|
|
14766
14807
|
throw error;
|
|
14767
14808
|
}
|
|
@@ -14787,21 +14828,11 @@ async function handleFillFormByUid(args2) {
|
|
|
14787
14828
|
const firefox3 = await getFirefox2();
|
|
14788
14829
|
try {
|
|
14789
14830
|
await firefox3.fillFormByUid(elements);
|
|
14790
|
-
return successResponse(
|
|
14791
|
-
`\u2705 Filled ${elements.length} form field(s):
|
|
14792
|
-
` + elements.map(
|
|
14793
|
-
(el) => ` - ${el.uid}: ${el.value.substring(0, 30)}${el.value.length > 30 ? "..." : ""}`
|
|
14794
|
-
).join("\n")
|
|
14795
|
-
);
|
|
14831
|
+
return successResponse(`\u2705 filled ${elements.length} fields`);
|
|
14796
14832
|
} catch (error) {
|
|
14797
14833
|
const errorMsg = error.message;
|
|
14798
14834
|
if (errorMsg.includes("stale") || errorMsg.includes("Snapshot") || errorMsg.includes("UID")) {
|
|
14799
|
-
throw new Error(
|
|
14800
|
-
`One or more UIDs are stale or invalid.
|
|
14801
|
-
|
|
14802
|
-
The page may have changed since the snapshot was taken.
|
|
14803
|
-
Please call take_snapshot to get fresh UIDs and try again.`
|
|
14804
|
-
);
|
|
14835
|
+
throw new Error(`UIDs stale/invalid. Call take_snapshot first.`);
|
|
14805
14836
|
}
|
|
14806
14837
|
throw error;
|
|
14807
14838
|
}
|
|
@@ -14822,26 +14853,17 @@ async function handleUploadFileByUid(args2) {
|
|
|
14822
14853
|
const firefox3 = await getFirefox2();
|
|
14823
14854
|
try {
|
|
14824
14855
|
await firefox3.uploadFileByUid(uid, filePath);
|
|
14825
|
-
return successResponse(`\u2705
|
|
14826
|
-
File: ${filePath}`);
|
|
14856
|
+
return successResponse(`\u2705 upload ${uid}`);
|
|
14827
14857
|
} catch (error) {
|
|
14828
14858
|
const errorMsg = error.message;
|
|
14829
14859
|
if (errorMsg.includes("stale") || errorMsg.includes("Snapshot") || errorMsg.includes("UID")) {
|
|
14830
14860
|
throw handleUidError(error, uid);
|
|
14831
14861
|
}
|
|
14832
14862
|
if (errorMsg.includes("not a file input") || errorMsg.includes('type="file"')) {
|
|
14833
|
-
throw new Error(
|
|
14834
|
-
`Element with UID "${uid}" is not an <input type="file"> element.
|
|
14835
|
-
|
|
14836
|
-
Please ensure the UID points to a file input element.`
|
|
14837
|
-
);
|
|
14863
|
+
throw new Error(`${uid} is not a file input`);
|
|
14838
14864
|
}
|
|
14839
14865
|
if (errorMsg.includes("hidden") || errorMsg.includes("not visible")) {
|
|
14840
|
-
throw new Error(
|
|
14841
|
-
`File input element with UID "${uid}" is hidden or not interactable.
|
|
14842
|
-
|
|
14843
|
-
Some file inputs are hidden and cannot be directly interacted with.`
|
|
14844
|
-
);
|
|
14866
|
+
throw new Error(`${uid} is hidden/not interactable`);
|
|
14845
14867
|
}
|
|
14846
14868
|
throw error;
|
|
14847
14869
|
}
|
|
@@ -14854,19 +14876,20 @@ var init_input = __esm({
|
|
|
14854
14876
|
"src/tools/input.ts"() {
|
|
14855
14877
|
"use strict";
|
|
14856
14878
|
init_response_helpers();
|
|
14879
|
+
init_uid_helpers();
|
|
14857
14880
|
clickByUidTool = {
|
|
14858
14881
|
name: "click_by_uid",
|
|
14859
|
-
description: "Click
|
|
14882
|
+
description: "Click element by UID. Set dblClick for double-click.",
|
|
14860
14883
|
inputSchema: {
|
|
14861
14884
|
type: "object",
|
|
14862
14885
|
properties: {
|
|
14863
14886
|
uid: {
|
|
14864
14887
|
type: "string",
|
|
14865
|
-
description: "
|
|
14888
|
+
description: "Element UID from snapshot"
|
|
14866
14889
|
},
|
|
14867
14890
|
dblClick: {
|
|
14868
14891
|
type: "boolean",
|
|
14869
|
-
description: "
|
|
14892
|
+
description: "Double-click (default: false)"
|
|
14870
14893
|
}
|
|
14871
14894
|
},
|
|
14872
14895
|
required: ["uid"]
|
|
@@ -14874,13 +14897,13 @@ var init_input = __esm({
|
|
|
14874
14897
|
};
|
|
14875
14898
|
hoverByUidTool = {
|
|
14876
14899
|
name: "hover_by_uid",
|
|
14877
|
-
description: "Hover over
|
|
14900
|
+
description: "Hover over element by UID.",
|
|
14878
14901
|
inputSchema: {
|
|
14879
14902
|
type: "object",
|
|
14880
14903
|
properties: {
|
|
14881
14904
|
uid: {
|
|
14882
14905
|
type: "string",
|
|
14883
|
-
description: "
|
|
14906
|
+
description: "Element UID from snapshot"
|
|
14884
14907
|
}
|
|
14885
14908
|
},
|
|
14886
14909
|
required: ["uid"]
|
|
@@ -14888,17 +14911,17 @@ var init_input = __esm({
|
|
|
14888
14911
|
};
|
|
14889
14912
|
fillByUidTool = {
|
|
14890
14913
|
name: "fill_by_uid",
|
|
14891
|
-
description: "Fill
|
|
14914
|
+
description: "Fill text input/textarea by UID.",
|
|
14892
14915
|
inputSchema: {
|
|
14893
14916
|
type: "object",
|
|
14894
14917
|
properties: {
|
|
14895
14918
|
uid: {
|
|
14896
14919
|
type: "string",
|
|
14897
|
-
description: "
|
|
14920
|
+
description: "Input element UID from snapshot"
|
|
14898
14921
|
},
|
|
14899
14922
|
value: {
|
|
14900
14923
|
type: "string",
|
|
14901
|
-
description: "
|
|
14924
|
+
description: "Text to fill"
|
|
14902
14925
|
}
|
|
14903
14926
|
},
|
|
14904
14927
|
required: ["uid", "value"]
|
|
@@ -14906,17 +14929,17 @@ var init_input = __esm({
|
|
|
14906
14929
|
};
|
|
14907
14930
|
dragByUidToUidTool = {
|
|
14908
14931
|
name: "drag_by_uid_to_uid",
|
|
14909
|
-
description: "
|
|
14932
|
+
description: "Drag element to another (HTML5 drag events).",
|
|
14910
14933
|
inputSchema: {
|
|
14911
14934
|
type: "object",
|
|
14912
14935
|
properties: {
|
|
14913
14936
|
fromUid: {
|
|
14914
14937
|
type: "string",
|
|
14915
|
-
description: "
|
|
14938
|
+
description: "Source element UID"
|
|
14916
14939
|
},
|
|
14917
14940
|
toUid: {
|
|
14918
14941
|
type: "string",
|
|
14919
|
-
description: "
|
|
14942
|
+
description: "Target element UID"
|
|
14920
14943
|
}
|
|
14921
14944
|
},
|
|
14922
14945
|
required: ["fromUid", "toUid"]
|
|
@@ -14924,23 +14947,23 @@ var init_input = __esm({
|
|
|
14924
14947
|
};
|
|
14925
14948
|
fillFormByUidTool = {
|
|
14926
14949
|
name: "fill_form_by_uid",
|
|
14927
|
-
description: "Fill multiple form fields
|
|
14950
|
+
description: "Fill multiple form fields at once.",
|
|
14928
14951
|
inputSchema: {
|
|
14929
14952
|
type: "object",
|
|
14930
14953
|
properties: {
|
|
14931
14954
|
elements: {
|
|
14932
14955
|
type: "array",
|
|
14933
|
-
description: "Array of
|
|
14956
|
+
description: "Array of {uid, value} pairs",
|
|
14934
14957
|
items: {
|
|
14935
14958
|
type: "object",
|
|
14936
14959
|
properties: {
|
|
14937
14960
|
uid: {
|
|
14938
14961
|
type: "string",
|
|
14939
|
-
description: "
|
|
14962
|
+
description: "Field UID"
|
|
14940
14963
|
},
|
|
14941
14964
|
value: {
|
|
14942
14965
|
type: "string",
|
|
14943
|
-
description: "
|
|
14966
|
+
description: "Field value"
|
|
14944
14967
|
}
|
|
14945
14968
|
},
|
|
14946
14969
|
required: ["uid", "value"]
|
|
@@ -14952,17 +14975,17 @@ var init_input = __esm({
|
|
|
14952
14975
|
};
|
|
14953
14976
|
uploadFileByUidTool = {
|
|
14954
14977
|
name: "upload_file_by_uid",
|
|
14955
|
-
description:
|
|
14978
|
+
description: "Upload file to file input by UID.",
|
|
14956
14979
|
inputSchema: {
|
|
14957
14980
|
type: "object",
|
|
14958
14981
|
properties: {
|
|
14959
14982
|
uid: {
|
|
14960
14983
|
type: "string",
|
|
14961
|
-
description: "
|
|
14984
|
+
description: "File input UID from snapshot"
|
|
14962
14985
|
},
|
|
14963
14986
|
filePath: {
|
|
14964
14987
|
type: "string",
|
|
14965
|
-
description: "Local
|
|
14988
|
+
description: "Local file path"
|
|
14966
14989
|
}
|
|
14967
14990
|
},
|
|
14968
14991
|
required: ["uid", "filePath"]
|
|
@@ -14972,60 +14995,46 @@ var init_input = __esm({
|
|
|
14972
14995
|
});
|
|
14973
14996
|
|
|
14974
14997
|
// src/tools/screenshot.ts
|
|
14998
|
+
function buildScreenshotResponse(base64Png, label) {
|
|
14999
|
+
const sizeKB = Math.round(base64Png.length / 1024);
|
|
15000
|
+
if (base64Png.length > TOKEN_LIMITS.MAX_SCREENSHOT_CHARS) {
|
|
15001
|
+
const truncatedData = base64Png.slice(0, TOKEN_LIMITS.MAX_SCREENSHOT_CHARS);
|
|
15002
|
+
return successResponse(`\u{1F4F8} ${label} (${sizeKB}KB) [truncated]
|
|
15003
|
+
${truncatedData}`);
|
|
15004
|
+
}
|
|
15005
|
+
const warn = base64Png.length > TOKEN_LIMITS.WARNING_THRESHOLD_CHARS ? " [large]" : "";
|
|
15006
|
+
return successResponse(`\u{1F4F8} ${label} (${sizeKB}KB)${warn}
|
|
15007
|
+
${base64Png}`);
|
|
15008
|
+
}
|
|
14975
15009
|
async function handleScreenshotPage(_args) {
|
|
14976
15010
|
try {
|
|
14977
15011
|
const { getFirefox: getFirefox2 } = await init_index().then(() => index_exports);
|
|
14978
15012
|
const firefox3 = await getFirefox2();
|
|
14979
15013
|
const base64Png = await firefox3.takeScreenshotPage();
|
|
14980
15014
|
if (!base64Png || typeof base64Png !== "string") {
|
|
14981
|
-
throw new Error("
|
|
15015
|
+
throw new Error("Invalid screenshot data");
|
|
14982
15016
|
}
|
|
14983
|
-
return
|
|
14984
|
-
`\u{1F4F8} Page screenshot captured (${Math.round(base64Png.length / 1024)}KB)
|
|
14985
|
-
|
|
14986
|
-
Base64 PNG data:
|
|
14987
|
-
${base64Png}`
|
|
14988
|
-
);
|
|
15017
|
+
return buildScreenshotResponse(base64Png, "page");
|
|
14989
15018
|
} catch (error) {
|
|
14990
|
-
return errorResponse(
|
|
14991
|
-
new Error(
|
|
14992
|
-
`Failed to capture page screenshot: ${error.message}
|
|
14993
|
-
|
|
14994
|
-
The page may not be fully loaded or accessible.`
|
|
14995
|
-
)
|
|
14996
|
-
);
|
|
15019
|
+
return errorResponse(error);
|
|
14997
15020
|
}
|
|
14998
15021
|
}
|
|
14999
15022
|
async function handleScreenshotByUid(args2) {
|
|
15000
15023
|
try {
|
|
15001
15024
|
const { uid } = args2;
|
|
15002
15025
|
if (!uid || typeof uid !== "string") {
|
|
15003
|
-
throw new Error("uid
|
|
15026
|
+
throw new Error("uid required");
|
|
15004
15027
|
}
|
|
15005
15028
|
const { getFirefox: getFirefox2 } = await init_index().then(() => index_exports);
|
|
15006
15029
|
const firefox3 = await getFirefox2();
|
|
15007
15030
|
try {
|
|
15008
15031
|
const base64Png = await firefox3.takeScreenshotByUid(uid);
|
|
15009
15032
|
if (!base64Png || typeof base64Png !== "string") {
|
|
15010
|
-
throw new Error("
|
|
15033
|
+
throw new Error("Invalid screenshot data");
|
|
15011
15034
|
}
|
|
15012
|
-
return
|
|
15013
|
-
`\u{1F4F8} Element screenshot captured for UID "${uid}" (${Math.round(base64Png.length / 1024)}KB)
|
|
15014
|
-
|
|
15015
|
-
Base64 PNG data:
|
|
15016
|
-
${base64Png}`
|
|
15017
|
-
);
|
|
15035
|
+
return buildScreenshotResponse(base64Png, uid);
|
|
15018
15036
|
} catch (error) {
|
|
15019
|
-
|
|
15020
|
-
if (errorMsg.includes("stale") || errorMsg.includes("Snapshot") || errorMsg.includes("UID") || errorMsg.includes("not found")) {
|
|
15021
|
-
throw new Error(
|
|
15022
|
-
`UID "${uid}" is stale or invalid.
|
|
15023
|
-
|
|
15024
|
-
The page may have changed since the snapshot was taken.
|
|
15025
|
-
Please call take_snapshot to get fresh UIDs and try again.`
|
|
15026
|
-
);
|
|
15027
|
-
}
|
|
15028
|
-
throw error;
|
|
15037
|
+
throw handleUidError(error, uid);
|
|
15029
15038
|
}
|
|
15030
15039
|
} catch (error) {
|
|
15031
15040
|
return errorResponse(error);
|
|
@@ -15036,9 +15045,10 @@ var init_screenshot = __esm({
|
|
|
15036
15045
|
"src/tools/screenshot.ts"() {
|
|
15037
15046
|
"use strict";
|
|
15038
15047
|
init_response_helpers();
|
|
15048
|
+
init_uid_helpers();
|
|
15039
15049
|
screenshotPageTool = {
|
|
15040
15050
|
name: "screenshot_page",
|
|
15041
|
-
description: "Capture
|
|
15051
|
+
description: "Capture page screenshot as base64 PNG.",
|
|
15042
15052
|
inputSchema: {
|
|
15043
15053
|
type: "object",
|
|
15044
15054
|
properties: {}
|
|
@@ -15046,13 +15056,13 @@ var init_screenshot = __esm({
|
|
|
15046
15056
|
};
|
|
15047
15057
|
screenshotByUidTool = {
|
|
15048
15058
|
name: "screenshot_by_uid",
|
|
15049
|
-
description: "Capture
|
|
15059
|
+
description: "Capture element screenshot by UID as base64 PNG.",
|
|
15050
15060
|
inputSchema: {
|
|
15051
15061
|
type: "object",
|
|
15052
15062
|
properties: {
|
|
15053
15063
|
uid: {
|
|
15054
15064
|
type: "string",
|
|
15055
|
-
description: "
|
|
15065
|
+
description: "Element UID from snapshot"
|
|
15056
15066
|
}
|
|
15057
15067
|
},
|
|
15058
15068
|
required: ["uid"]
|
|
@@ -15069,17 +15079,11 @@ async function handleAcceptDialog(args2) {
|
|
|
15069
15079
|
const firefox3 = await getFirefox2();
|
|
15070
15080
|
try {
|
|
15071
15081
|
await firefox3.acceptDialog(promptText);
|
|
15072
|
-
|
|
15073
|
-
if (promptText) {
|
|
15074
|
-
message += ` with text: "${promptText}"`;
|
|
15075
|
-
}
|
|
15076
|
-
return successResponse(message);
|
|
15082
|
+
return successResponse(promptText ? `\u2705 Accepted: "${promptText}"` : "\u2705 Accepted");
|
|
15077
15083
|
} catch (error) {
|
|
15078
15084
|
const errorMsg = error.message;
|
|
15079
15085
|
if (errorMsg.includes("no such alert") || errorMsg.includes("No dialog")) {
|
|
15080
|
-
throw new Error(
|
|
15081
|
-
"No active dialog found.\n\nDialogs must be accepted shortly after they appear. Make sure a dialog is currently visible on the page."
|
|
15082
|
-
);
|
|
15086
|
+
throw new Error("No active dialog");
|
|
15083
15087
|
}
|
|
15084
15088
|
throw error;
|
|
15085
15089
|
}
|
|
@@ -15093,13 +15097,11 @@ async function handleDismissDialog(_args) {
|
|
|
15093
15097
|
const firefox3 = await getFirefox2();
|
|
15094
15098
|
try {
|
|
15095
15099
|
await firefox3.dismissDialog();
|
|
15096
|
-
return successResponse("\u2705
|
|
15100
|
+
return successResponse("\u2705 Dismissed");
|
|
15097
15101
|
} catch (error) {
|
|
15098
15102
|
const errorMsg = error.message;
|
|
15099
15103
|
if (errorMsg.includes("no such alert") || errorMsg.includes("No dialog")) {
|
|
15100
|
-
throw new Error(
|
|
15101
|
-
"No active dialog found.\n\nDialogs must be dismissed shortly after they appear. Make sure a dialog is currently visible on the page."
|
|
15102
|
-
);
|
|
15104
|
+
throw new Error("No active dialog");
|
|
15103
15105
|
}
|
|
15104
15106
|
throw error;
|
|
15105
15107
|
}
|
|
@@ -15120,19 +15122,9 @@ async function handleNavigateHistory(args2) {
|
|
|
15120
15122
|
} else {
|
|
15121
15123
|
await firefox3.navigateForward();
|
|
15122
15124
|
}
|
|
15123
|
-
return successResponse(
|
|
15124
|
-
`\u2705 Navigated ${direction} in history
|
|
15125
|
-
|
|
15126
|
-
\u26A0\uFE0F UIDs from previous snapshots are now stale. Call take_snapshot before using UID-based actions.`
|
|
15127
|
-
);
|
|
15125
|
+
return successResponse(`\u2705 ${direction}`);
|
|
15128
15126
|
} catch (error) {
|
|
15129
|
-
return errorResponse(
|
|
15130
|
-
new Error(
|
|
15131
|
-
`Failed to navigate ${args2.direction || "in history"}: ${error.message}
|
|
15132
|
-
|
|
15133
|
-
The page may not have history in this direction available.`
|
|
15134
|
-
)
|
|
15135
|
-
);
|
|
15127
|
+
return errorResponse(error);
|
|
15136
15128
|
}
|
|
15137
15129
|
}
|
|
15138
15130
|
async function handleSetViewportSize(args2) {
|
|
@@ -15147,19 +15139,9 @@ async function handleSetViewportSize(args2) {
|
|
|
15147
15139
|
const { getFirefox: getFirefox2 } = await init_index().then(() => index_exports);
|
|
15148
15140
|
const firefox3 = await getFirefox2();
|
|
15149
15141
|
await firefox3.setViewportSize(width, height);
|
|
15150
|
-
return successResponse(
|
|
15151
|
-
`\u2705 Viewport size set to ${width}x${height} pixels
|
|
15152
|
-
|
|
15153
|
-
NOTE: Actual viewport may differ slightly in some browser modes (e.g., headless).`
|
|
15154
|
-
);
|
|
15142
|
+
return successResponse(`\u2705 ${width}x${height}`);
|
|
15155
15143
|
} catch (error) {
|
|
15156
|
-
return errorResponse(
|
|
15157
|
-
new Error(
|
|
15158
|
-
`Failed to set viewport size: ${error.message}
|
|
15159
|
-
|
|
15160
|
-
Some browser configurations may not support precise viewport sizing.`
|
|
15161
|
-
)
|
|
15162
|
-
);
|
|
15144
|
+
return errorResponse(error);
|
|
15163
15145
|
}
|
|
15164
15146
|
}
|
|
15165
15147
|
var acceptDialogTool, dismissDialogTool, navigateHistoryTool, setViewportSizeTool;
|
|
@@ -15169,20 +15151,20 @@ var init_utilities = __esm({
|
|
|
15169
15151
|
init_response_helpers();
|
|
15170
15152
|
acceptDialogTool = {
|
|
15171
15153
|
name: "accept_dialog",
|
|
15172
|
-
description: "Accept
|
|
15154
|
+
description: "Accept browser dialog. Provide promptText for prompts.",
|
|
15173
15155
|
inputSchema: {
|
|
15174
15156
|
type: "object",
|
|
15175
15157
|
properties: {
|
|
15176
15158
|
promptText: {
|
|
15177
15159
|
type: "string",
|
|
15178
|
-
description: "Text
|
|
15160
|
+
description: "Text for prompt dialogs"
|
|
15179
15161
|
}
|
|
15180
15162
|
}
|
|
15181
15163
|
}
|
|
15182
15164
|
};
|
|
15183
15165
|
dismissDialogTool = {
|
|
15184
15166
|
name: "dismiss_dialog",
|
|
15185
|
-
description: "Dismiss
|
|
15167
|
+
description: "Dismiss browser dialog.",
|
|
15186
15168
|
inputSchema: {
|
|
15187
15169
|
type: "object",
|
|
15188
15170
|
properties: {}
|
|
@@ -15190,14 +15172,14 @@ var init_utilities = __esm({
|
|
|
15190
15172
|
};
|
|
15191
15173
|
navigateHistoryTool = {
|
|
15192
15174
|
name: "navigate_history",
|
|
15193
|
-
description: "Navigate
|
|
15175
|
+
description: "Navigate history back/forward. UIDs become stale.",
|
|
15194
15176
|
inputSchema: {
|
|
15195
15177
|
type: "object",
|
|
15196
15178
|
properties: {
|
|
15197
15179
|
direction: {
|
|
15198
15180
|
type: "string",
|
|
15199
15181
|
enum: ["back", "forward"],
|
|
15200
|
-
description: "
|
|
15182
|
+
description: "back or forward"
|
|
15201
15183
|
}
|
|
15202
15184
|
},
|
|
15203
15185
|
required: ["direction"]
|
|
@@ -15205,17 +15187,17 @@ var init_utilities = __esm({
|
|
|
15205
15187
|
};
|
|
15206
15188
|
setViewportSizeTool = {
|
|
15207
15189
|
name: "set_viewport_size",
|
|
15208
|
-
description: "Set
|
|
15190
|
+
description: "Set viewport dimensions in pixels.",
|
|
15209
15191
|
inputSchema: {
|
|
15210
15192
|
type: "object",
|
|
15211
15193
|
properties: {
|
|
15212
15194
|
width: {
|
|
15213
15195
|
type: "number",
|
|
15214
|
-
description: "
|
|
15196
|
+
description: "Width in pixels"
|
|
15215
15197
|
},
|
|
15216
15198
|
height: {
|
|
15217
15199
|
type: "number",
|
|
15218
|
-
description: "
|
|
15200
|
+
description: "Height in pixels"
|
|
15219
15201
|
}
|
|
15220
15202
|
},
|
|
15221
15203
|
required: ["width", "height"]
|
|
@@ -15238,6 +15220,33 @@ var init_tools = __esm({
|
|
|
15238
15220
|
}
|
|
15239
15221
|
});
|
|
15240
15222
|
|
|
15223
|
+
// src/utils/errors.ts
|
|
15224
|
+
function isDisconnectionError(error) {
|
|
15225
|
+
if (error instanceof FirefoxDisconnectedError) {
|
|
15226
|
+
return true;
|
|
15227
|
+
}
|
|
15228
|
+
if (error instanceof Error) {
|
|
15229
|
+
const message = error.message.toLowerCase();
|
|
15230
|
+
return message.includes("session deleted") || message.includes("session not created") || message.includes("no such window") || message.includes("no such session") || message.includes("target window already closed") || message.includes("unable to connect") || message.includes("connection refused") || message.includes("not connected") || message.includes("driver not connected") || message.includes("invalid session id") || message.includes("browsing context has been discarded");
|
|
15231
|
+
}
|
|
15232
|
+
return false;
|
|
15233
|
+
}
|
|
15234
|
+
var FirefoxDisconnectedError;
|
|
15235
|
+
var init_errors2 = __esm({
|
|
15236
|
+
"src/utils/errors.ts"() {
|
|
15237
|
+
"use strict";
|
|
15238
|
+
FirefoxDisconnectedError = class extends Error {
|
|
15239
|
+
constructor(reason) {
|
|
15240
|
+
const baseMessage = "Firefox browser is not connected";
|
|
15241
|
+
const instruction = "The Firefox browser window was closed by the user. To continue browser automation, ask the user to restart the firefox-devtools-mcp server (they need to restart Claude Code or the MCP connection). This will launch a new Firefox instance.";
|
|
15242
|
+
const fullMessage = reason ? `${baseMessage}: ${reason}. ${instruction}` : `${baseMessage}. ${instruction}`;
|
|
15243
|
+
super(fullMessage);
|
|
15244
|
+
this.name = "FirefoxDisconnectedError";
|
|
15245
|
+
}
|
|
15246
|
+
};
|
|
15247
|
+
}
|
|
15248
|
+
});
|
|
15249
|
+
|
|
15241
15250
|
// node_modules/dotenv/package.json
|
|
15242
15251
|
var require_package = __commonJS({
|
|
15243
15252
|
"node_modules/dotenv/package.json"(exports, module) {
|
|
@@ -15640,28 +15649,46 @@ var require_main = __commonJS({
|
|
|
15640
15649
|
var index_exports = {};
|
|
15641
15650
|
__export(index_exports, {
|
|
15642
15651
|
FirefoxDevTools: () => FirefoxClient,
|
|
15652
|
+
FirefoxDisconnectedError: () => FirefoxDisconnectedError,
|
|
15643
15653
|
args: () => args,
|
|
15644
|
-
getFirefox: () => getFirefox
|
|
15654
|
+
getFirefox: () => getFirefox,
|
|
15655
|
+
isDisconnectionError: () => isDisconnectionError,
|
|
15656
|
+
resetFirefox: () => resetFirefox
|
|
15645
15657
|
});
|
|
15646
15658
|
import { version } from "process";
|
|
15647
15659
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
15648
15660
|
import { resolve as resolve2 } from "path";
|
|
15649
15661
|
import { realpathSync } from "fs";
|
|
15650
|
-
|
|
15651
|
-
if (
|
|
15652
|
-
|
|
15653
|
-
|
|
15654
|
-
firefoxPath: args.firefoxPath ?? void 0,
|
|
15655
|
-
headless: args.headless,
|
|
15656
|
-
profilePath: args.profilePath ?? void 0,
|
|
15657
|
-
viewport: args.viewport ?? void 0,
|
|
15658
|
-
args: args.firefoxArg ?? void 0,
|
|
15659
|
-
startUrl: args.startUrl ?? void 0
|
|
15660
|
-
};
|
|
15661
|
-
firefox2 = new FirefoxClient(options);
|
|
15662
|
-
await firefox2.connect();
|
|
15663
|
-
log("Firefox DevTools connection established");
|
|
15662
|
+
function resetFirefox() {
|
|
15663
|
+
if (firefox2) {
|
|
15664
|
+
firefox2.reset();
|
|
15665
|
+
firefox2 = null;
|
|
15664
15666
|
}
|
|
15667
|
+
log("Firefox instance reset - will reconnect on next tool call");
|
|
15668
|
+
}
|
|
15669
|
+
async function getFirefox() {
|
|
15670
|
+
if (firefox2) {
|
|
15671
|
+
const isConnected = await firefox2.isConnected();
|
|
15672
|
+
if (!isConnected) {
|
|
15673
|
+
log("Firefox connection lost - browser was closed or disconnected");
|
|
15674
|
+
resetFirefox();
|
|
15675
|
+
throw new FirefoxDisconnectedError("Browser was closed");
|
|
15676
|
+
}
|
|
15677
|
+
return firefox2;
|
|
15678
|
+
}
|
|
15679
|
+
log("Initializing Firefox DevTools connection...");
|
|
15680
|
+
const options = {
|
|
15681
|
+
firefoxPath: args.firefoxPath ?? void 0,
|
|
15682
|
+
headless: args.headless,
|
|
15683
|
+
profilePath: args.profilePath ?? void 0,
|
|
15684
|
+
viewport: args.viewport ?? void 0,
|
|
15685
|
+
args: args.firefoxArg ?? void 0,
|
|
15686
|
+
startUrl: args.startUrl ?? void 0,
|
|
15687
|
+
acceptInsecureCerts: args.acceptInsecureCerts
|
|
15688
|
+
};
|
|
15689
|
+
firefox2 = new FirefoxClient(options);
|
|
15690
|
+
await firefox2.connect();
|
|
15691
|
+
log("Firefox DevTools connection established");
|
|
15665
15692
|
return firefox2;
|
|
15666
15693
|
}
|
|
15667
15694
|
async function main() {
|
|
@@ -15729,7 +15756,9 @@ var init_index = __esm({
|
|
|
15729
15756
|
init_cli();
|
|
15730
15757
|
init_firefox();
|
|
15731
15758
|
init_tools();
|
|
15759
|
+
init_errors2();
|
|
15732
15760
|
init_firefox();
|
|
15761
|
+
init_errors2();
|
|
15733
15762
|
if (process.env.NODE_ENV !== "production") {
|
|
15734
15763
|
try {
|
|
15735
15764
|
const { config } = await Promise.resolve().then(() => __toESM(require_main(), 1));
|
|
@@ -15829,8 +15858,11 @@ var init_index = __esm({
|
|
|
15829
15858
|
await init_index();
|
|
15830
15859
|
export {
|
|
15831
15860
|
FirefoxClient as FirefoxDevTools,
|
|
15861
|
+
FirefoxDisconnectedError,
|
|
15832
15862
|
args,
|
|
15833
|
-
getFirefox
|
|
15863
|
+
getFirefox,
|
|
15864
|
+
isDisconnectionError,
|
|
15865
|
+
resetFirefox
|
|
15834
15866
|
};
|
|
15835
15867
|
/*! Bundled license information:
|
|
15836
15868
|
|