@extension.dev/mcp 4.2.2 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +22 -0
- package/.claude-plugin/plugin.json +33 -0
- package/CHANGELOG.md +61 -0
- package/LICENSE +1 -1
- package/README.md +20 -7
- package/bin/extension-mcp.js +7 -1
- package/claude/CLAUDE.md +29 -39
- package/claude/commands/extension-debug.md +2 -3
- package/claude/rules/mcp-tools.md +68 -18
- package/dist/module.js +422 -41
- package/dist/src/__tests__/fixtures/ready-contract.d.ts +7 -0
- package/dist/src/index.d.ts +0 -7
- package/dist/src/lib/act.d.ts +0 -22
- package/dist/src/lib/browser-family.d.ts +5 -0
- package/dist/src/lib/exec.d.ts +0 -11
- package/dist/src/lib/launch-flags.d.ts +43 -0
- package/dist/src/lib/session-browser.d.ts +0 -1
- package/dist/src/lib/validate-input.d.ts +0 -5
- package/dist/src/tools/build.d.ts +24 -0
- package/dist/src/tools/dev.d.ts +45 -7
- package/dist/src/tools/doctor.d.ts +22 -0
- package/dist/src/tools/preview.d.ts +44 -1
- package/dist/src/tools/start.d.ts +44 -1
- package/dist/src/tools/uninstall-browser.d.ts +24 -0
- package/package.json +30 -16
- package/server.json +23 -0
package/dist/module.js
CHANGED
|
@@ -12,7 +12,7 @@ import { filterKeysForThisBrowser } from "browser-extension-manifest-fields";
|
|
|
12
12
|
import ws from "ws";
|
|
13
13
|
import node_http from "node:http";
|
|
14
14
|
import { clearCredentials, exchangeAndPersist, fetchLoginConfig, pollForToken, publish, readCredentials, resolveApiBase, resolveToken, safeApiBase, startDeviceCode } from "@extension.dev/core";
|
|
15
|
-
import { extensionInstall, getManagedBrowsersCacheRoot } from "extension-install";
|
|
15
|
+
import { extensionInstall, extensionUninstall, getManagedBrowsersCacheRoot } from "extension-install";
|
|
16
16
|
import { execFile } from "node:child_process";
|
|
17
17
|
import { promisify } from "node:util";
|
|
18
18
|
var add_feature_namespaceObject = {};
|
|
@@ -45,6 +45,12 @@ __webpack_require__.d(dev_namespaceObject, {
|
|
|
45
45
|
handler: ()=>dev_handler,
|
|
46
46
|
schema: ()=>dev_schema
|
|
47
47
|
});
|
|
48
|
+
var doctor_namespaceObject = {};
|
|
49
|
+
__webpack_require__.r(doctor_namespaceObject);
|
|
50
|
+
__webpack_require__.d(doctor_namespaceObject, {
|
|
51
|
+
handler: ()=>doctor_handler,
|
|
52
|
+
schema: ()=>doctor_schema
|
|
53
|
+
});
|
|
48
54
|
var dom_inspect_namespaceObject = {};
|
|
49
55
|
__webpack_require__.r(dom_inspect_namespaceObject);
|
|
50
56
|
__webpack_require__.d(dom_inspect_namespaceObject, {
|
|
@@ -171,6 +177,12 @@ __webpack_require__.d(storage_namespaceObject, {
|
|
|
171
177
|
handler: ()=>storage_handler,
|
|
172
178
|
schema: ()=>storage_schema
|
|
173
179
|
});
|
|
180
|
+
var uninstall_browser_namespaceObject = {};
|
|
181
|
+
__webpack_require__.r(uninstall_browser_namespaceObject);
|
|
182
|
+
__webpack_require__.d(uninstall_browser_namespaceObject, {
|
|
183
|
+
handler: ()=>uninstall_browser_handler,
|
|
184
|
+
schema: ()=>uninstall_browser_schema
|
|
185
|
+
});
|
|
174
186
|
var wait_namespaceObject = {};
|
|
175
187
|
__webpack_require__.r(wait_namespaceObject);
|
|
176
188
|
__webpack_require__.d(wait_namespaceObject, {
|
|
@@ -183,7 +195,7 @@ __webpack_require__.d(whoami_namespaceObject, {
|
|
|
183
195
|
handler: ()=>whoami_handler,
|
|
184
196
|
schema: ()=>whoami_schema
|
|
185
197
|
});
|
|
186
|
-
var package_namespaceObject = JSON.parse('{"rE":"4.
|
|
198
|
+
var package_namespaceObject = JSON.parse('{"rE":"4.4.0","El":{"OP":"^4.0.11"}}');
|
|
187
199
|
const create_schema = {
|
|
188
200
|
name: "extension_create",
|
|
189
201
|
description: "Create a new browser extension project from a template in the extension.dev template catalog. Use extension_list_templates to see available options.",
|
|
@@ -371,10 +383,20 @@ const build_schema = {
|
|
|
371
383
|
type: "string",
|
|
372
384
|
enum: [
|
|
373
385
|
"chrome",
|
|
386
|
+
"chromium",
|
|
374
387
|
"edge",
|
|
388
|
+
"brave",
|
|
389
|
+
"opera",
|
|
390
|
+
"vivaldi",
|
|
391
|
+
"yandex",
|
|
375
392
|
"firefox",
|
|
393
|
+
"waterfox",
|
|
394
|
+
"librewolf",
|
|
395
|
+
"safari",
|
|
376
396
|
"chromium-based",
|
|
377
|
-
"gecko-based"
|
|
397
|
+
"gecko-based",
|
|
398
|
+
"firefox-based",
|
|
399
|
+
"webkit-based"
|
|
378
400
|
],
|
|
379
401
|
default: "chrome",
|
|
380
402
|
description: "Target browser"
|
|
@@ -388,6 +410,30 @@ const build_schema = {
|
|
|
388
410
|
type: "boolean",
|
|
389
411
|
default: false,
|
|
390
412
|
description: "Include source code zip (required by some stores)"
|
|
413
|
+
},
|
|
414
|
+
zipFilename: {
|
|
415
|
+
type: "string",
|
|
416
|
+
description: "Custom .zip file name (defaults to name and version)"
|
|
417
|
+
},
|
|
418
|
+
polyfill: {
|
|
419
|
+
type: "boolean",
|
|
420
|
+
default: false,
|
|
421
|
+
description: "Apply cross-browser polyfill"
|
|
422
|
+
},
|
|
423
|
+
silent: {
|
|
424
|
+
type: "boolean",
|
|
425
|
+
default: false,
|
|
426
|
+
description: "Suppress build output"
|
|
427
|
+
},
|
|
428
|
+
mode: {
|
|
429
|
+
type: "string",
|
|
430
|
+
enum: [
|
|
431
|
+
"development",
|
|
432
|
+
"production",
|
|
433
|
+
"none"
|
|
434
|
+
],
|
|
435
|
+
default: "production",
|
|
436
|
+
description: "Bundler mode override (also sets NODE_ENV)"
|
|
391
437
|
}
|
|
392
438
|
},
|
|
393
439
|
required: [
|
|
@@ -402,6 +448,18 @@ async function build_handler(args) {
|
|
|
402
448
|
browser: args.browser ?? "chrome",
|
|
403
449
|
zip: args.zip ?? false,
|
|
404
450
|
zipSource: args.zipSource ?? false,
|
|
451
|
+
...args.zipFilename ? {
|
|
452
|
+
zipFilename: args.zipFilename
|
|
453
|
+
} : {},
|
|
454
|
+
...void 0 !== args.polyfill ? {
|
|
455
|
+
polyfill: args.polyfill
|
|
456
|
+
} : {},
|
|
457
|
+
...void 0 !== args.silent ? {
|
|
458
|
+
silent: args.silent
|
|
459
|
+
} : {},
|
|
460
|
+
...args.mode ? {
|
|
461
|
+
mode: args.mode
|
|
462
|
+
} : {},
|
|
405
463
|
exitOnError: false
|
|
406
464
|
});
|
|
407
465
|
return JSON.stringify({
|
|
@@ -514,6 +572,50 @@ function removeSession(projectPath, browser) {
|
|
|
514
572
|
function listSessions() {
|
|
515
573
|
return Array.from(process_manager_sessions.values());
|
|
516
574
|
}
|
|
575
|
+
const LAUNCH_FLAG_SCHEMA = {
|
|
576
|
+
profile: {
|
|
577
|
+
type: "string",
|
|
578
|
+
description: 'Browser profile path, or "false" to reuse the default user profile. Omit for a fresh throwaway profile.'
|
|
579
|
+
},
|
|
580
|
+
startingUrl: {
|
|
581
|
+
type: "string",
|
|
582
|
+
description: "URL the browser opens on launch"
|
|
583
|
+
},
|
|
584
|
+
chromiumBinary: {
|
|
585
|
+
type: "string",
|
|
586
|
+
description: "Path to a custom Chromium-based binary (overrides browser)"
|
|
587
|
+
},
|
|
588
|
+
geckoBinary: {
|
|
589
|
+
type: "string",
|
|
590
|
+
description: "Path to a custom Gecko/Firefox binary (overrides browser)"
|
|
591
|
+
},
|
|
592
|
+
host: {
|
|
593
|
+
type: "string",
|
|
594
|
+
description: "Dev server bind host. Use 0.0.0.0 for Docker or devcontainers. Defaults to 127.0.0.1"
|
|
595
|
+
},
|
|
596
|
+
publicHost: {
|
|
597
|
+
type: "string",
|
|
598
|
+
description: "Connectable host the browser dials for HMR and reload when it differs from the bind host"
|
|
599
|
+
},
|
|
600
|
+
extensions: {
|
|
601
|
+
type: "array",
|
|
602
|
+
items: {
|
|
603
|
+
type: "string"
|
|
604
|
+
},
|
|
605
|
+
description: "Companion extension paths or store URLs to load alongside the project"
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
function launchFlagArgs(args) {
|
|
609
|
+
const cli = [];
|
|
610
|
+
if (void 0 !== args.profile) cli.push("--profile", args.profile);
|
|
611
|
+
if (args.startingUrl) cli.push("--starting-url", args.startingUrl);
|
|
612
|
+
if (args.chromiumBinary) cli.push("--chromium-binary", args.chromiumBinary);
|
|
613
|
+
if (args.geckoBinary) cli.push("--gecko-binary", args.geckoBinary);
|
|
614
|
+
if (args.host) cli.push("--host", args.host);
|
|
615
|
+
if (args.publicHost) cli.push("--public-host", args.publicHost);
|
|
616
|
+
if (args.extensions?.length) cli.push("--extensions", args.extensions.join(","));
|
|
617
|
+
return cli;
|
|
618
|
+
}
|
|
517
619
|
const dev_schema = {
|
|
518
620
|
name: "extension_dev",
|
|
519
621
|
description: "Start the extension development server with hot module replacement. Launches a browser with the extension loaded. Returns process info for use with extension_wait and extension_source_inspect.",
|
|
@@ -528,10 +630,20 @@ const dev_schema = {
|
|
|
528
630
|
type: "string",
|
|
529
631
|
enum: [
|
|
530
632
|
"chrome",
|
|
633
|
+
"chromium",
|
|
531
634
|
"edge",
|
|
635
|
+
"brave",
|
|
636
|
+
"opera",
|
|
637
|
+
"vivaldi",
|
|
638
|
+
"yandex",
|
|
532
639
|
"firefox",
|
|
640
|
+
"waterfox",
|
|
641
|
+
"librewolf",
|
|
642
|
+
"safari",
|
|
533
643
|
"chromium-based",
|
|
534
|
-
"gecko-based"
|
|
644
|
+
"gecko-based",
|
|
645
|
+
"firefox-based",
|
|
646
|
+
"webkit-based"
|
|
535
647
|
],
|
|
536
648
|
default: "chrome"
|
|
537
649
|
},
|
|
@@ -544,6 +656,12 @@ const dev_schema = {
|
|
|
544
656
|
default: false,
|
|
545
657
|
description: "Start dev server without launching browser"
|
|
546
658
|
},
|
|
659
|
+
polyfill: {
|
|
660
|
+
type: "boolean",
|
|
661
|
+
default: true,
|
|
662
|
+
description: "Apply cross-browser polyfill"
|
|
663
|
+
},
|
|
664
|
+
...LAUNCH_FLAG_SCHEMA,
|
|
547
665
|
allowControl: {
|
|
548
666
|
type: "boolean",
|
|
549
667
|
default: false,
|
|
@@ -570,6 +688,8 @@ async function dev_handler(args) {
|
|
|
570
688
|
];
|
|
571
689
|
if (void 0 !== args.port) cliArgs.push("--port", String(args.port));
|
|
572
690
|
if (args.noBrowser) cliArgs.push("--no-browser");
|
|
691
|
+
if (false === args.polyfill) cliArgs.push("--polyfill", "false");
|
|
692
|
+
cliArgs.push(...launchFlagArgs(args));
|
|
573
693
|
if (args.allowControl) cliArgs.push("--allow-control");
|
|
574
694
|
if (args.allowEval) cliArgs.push("--allow-eval");
|
|
575
695
|
const child = spawnExtensionCli(cliArgs, {
|
|
@@ -617,10 +737,20 @@ const start_schema = {
|
|
|
617
737
|
type: "string",
|
|
618
738
|
enum: [
|
|
619
739
|
"chrome",
|
|
740
|
+
"chromium",
|
|
620
741
|
"edge",
|
|
742
|
+
"brave",
|
|
743
|
+
"opera",
|
|
744
|
+
"vivaldi",
|
|
745
|
+
"yandex",
|
|
621
746
|
"firefox",
|
|
747
|
+
"waterfox",
|
|
748
|
+
"librewolf",
|
|
749
|
+
"safari",
|
|
622
750
|
"chromium-based",
|
|
623
|
-
"gecko-based"
|
|
751
|
+
"gecko-based",
|
|
752
|
+
"firefox-based",
|
|
753
|
+
"webkit-based"
|
|
624
754
|
],
|
|
625
755
|
default: "chrome"
|
|
626
756
|
},
|
|
@@ -628,7 +758,17 @@ const start_schema = {
|
|
|
628
758
|
type: "boolean",
|
|
629
759
|
default: true,
|
|
630
760
|
description: "Apply cross-browser polyfill"
|
|
631
|
-
}
|
|
761
|
+
},
|
|
762
|
+
port: {
|
|
763
|
+
type: "number",
|
|
764
|
+
description: "Server port (0 for auto-assign)"
|
|
765
|
+
},
|
|
766
|
+
noBrowser: {
|
|
767
|
+
type: "boolean",
|
|
768
|
+
default: false,
|
|
769
|
+
description: "Build and serve without launching a browser"
|
|
770
|
+
},
|
|
771
|
+
...LAUNCH_FLAG_SCHEMA
|
|
632
772
|
},
|
|
633
773
|
required: [
|
|
634
774
|
"projectPath"
|
|
@@ -643,7 +783,10 @@ async function start_handler(args) {
|
|
|
643
783
|
"--browser",
|
|
644
784
|
browser
|
|
645
785
|
];
|
|
646
|
-
if (false === args.polyfill) cliArgs.push("--
|
|
786
|
+
if (false === args.polyfill) cliArgs.push("--polyfill", "false");
|
|
787
|
+
if (void 0 !== args.port) cliArgs.push("--port", String(args.port));
|
|
788
|
+
if (args.noBrowser) cliArgs.push("--no-browser");
|
|
789
|
+
cliArgs.push(...launchFlagArgs(args));
|
|
647
790
|
const child = spawnExtensionCli(cliArgs, {
|
|
648
791
|
projectDir: args.projectPath
|
|
649
792
|
});
|
|
@@ -687,13 +830,33 @@ const preview_schema = {
|
|
|
687
830
|
type: "string",
|
|
688
831
|
enum: [
|
|
689
832
|
"chrome",
|
|
833
|
+
"chromium",
|
|
690
834
|
"edge",
|
|
835
|
+
"brave",
|
|
836
|
+
"opera",
|
|
837
|
+
"vivaldi",
|
|
838
|
+
"yandex",
|
|
691
839
|
"firefox",
|
|
840
|
+
"waterfox",
|
|
841
|
+
"librewolf",
|
|
842
|
+
"safari",
|
|
692
843
|
"chromium-based",
|
|
693
|
-
"gecko-based"
|
|
844
|
+
"gecko-based",
|
|
845
|
+
"firefox-based",
|
|
846
|
+
"webkit-based"
|
|
694
847
|
],
|
|
695
848
|
default: "chrome"
|
|
696
|
-
}
|
|
849
|
+
},
|
|
850
|
+
port: {
|
|
851
|
+
type: "number",
|
|
852
|
+
description: "Server port (0 for auto-assign)"
|
|
853
|
+
},
|
|
854
|
+
noBrowser: {
|
|
855
|
+
type: "boolean",
|
|
856
|
+
default: false,
|
|
857
|
+
description: "Serve the preview without launching a browser"
|
|
858
|
+
},
|
|
859
|
+
...LAUNCH_FLAG_SCHEMA
|
|
697
860
|
},
|
|
698
861
|
required: [
|
|
699
862
|
"projectPath"
|
|
@@ -708,6 +871,9 @@ async function preview_handler(args) {
|
|
|
708
871
|
"--browser",
|
|
709
872
|
browser
|
|
710
873
|
];
|
|
874
|
+
if (void 0 !== args.port) cliArgs.push("--port", String(args.port));
|
|
875
|
+
if (args.noBrowser) cliArgs.push("--no-browser");
|
|
876
|
+
cliArgs.push(...launchFlagArgs(args));
|
|
711
877
|
const child = spawnExtensionCli(cliArgs, {
|
|
712
878
|
projectDir: args.projectPath
|
|
713
879
|
});
|
|
@@ -902,6 +1068,33 @@ async function get_template_source_handler(args) {
|
|
|
902
1068
|
} : {}
|
|
903
1069
|
});
|
|
904
1070
|
}
|
|
1071
|
+
const CHROMIUM_FAMILY = new Set([
|
|
1072
|
+
"chrome",
|
|
1073
|
+
"chromium",
|
|
1074
|
+
"edge",
|
|
1075
|
+
"brave",
|
|
1076
|
+
"opera",
|
|
1077
|
+
"vivaldi",
|
|
1078
|
+
"yandex",
|
|
1079
|
+
"chromium-based"
|
|
1080
|
+
]);
|
|
1081
|
+
const GECKO_FAMILY = new Set([
|
|
1082
|
+
"firefox",
|
|
1083
|
+
"waterfox",
|
|
1084
|
+
"librewolf",
|
|
1085
|
+
"gecko-based",
|
|
1086
|
+
"firefox-based"
|
|
1087
|
+
]);
|
|
1088
|
+
const WEBKIT_FAMILY = new Set([
|
|
1089
|
+
"safari",
|
|
1090
|
+
"webkit-based"
|
|
1091
|
+
]);
|
|
1092
|
+
function isChromiumFamily(browser) {
|
|
1093
|
+
return CHROMIUM_FAMILY.has(browser);
|
|
1094
|
+
}
|
|
1095
|
+
function isGeckoFamily(browser) {
|
|
1096
|
+
return GECKO_FAMILY.has(browser);
|
|
1097
|
+
}
|
|
905
1098
|
const manifest_validate_schema = {
|
|
906
1099
|
name: "extension_manifest_validate",
|
|
907
1100
|
description: "Validate a manifest.json file for correctness across browsers. Reports missing fields, invalid permissions, and cross-browser compatibility issues.",
|
|
@@ -961,15 +1154,8 @@ async function manifest_validate_handler(args) {
|
|
|
961
1154
|
const chromiumManifest = filterKeysForThisBrowser(manifest, "chrome");
|
|
962
1155
|
if (!chromiumManifest.manifest_version) result.errors.push('Missing manifest_version. Use "chromium:manifest_version": 3 and "firefox:manifest_version": 2 for cross-browser support.');
|
|
963
1156
|
for (const browser of browsers){
|
|
964
|
-
const isChromium =
|
|
965
|
-
|
|
966
|
-
"edge",
|
|
967
|
-
"chromium-based"
|
|
968
|
-
].includes(browser);
|
|
969
|
-
const isFirefox = [
|
|
970
|
-
"firefox",
|
|
971
|
-
"gecko-based"
|
|
972
|
-
].includes(browser);
|
|
1157
|
+
const isChromium = isChromiumFamily(browser);
|
|
1158
|
+
const isFirefox = isGeckoFamily(browser);
|
|
973
1159
|
const effective = filterKeysForThisBrowser(manifest, browser);
|
|
974
1160
|
const issues = [];
|
|
975
1161
|
if (isChromium) {
|
|
@@ -1749,13 +1935,7 @@ async function source_inspect_handler(args) {
|
|
|
1749
1935
|
"console"
|
|
1750
1936
|
]);
|
|
1751
1937
|
const maxBytes = args.maxBytes ?? 262144;
|
|
1752
|
-
|
|
1753
|
-
"chrome",
|
|
1754
|
-
"chromium",
|
|
1755
|
-
"edge",
|
|
1756
|
-
"chromium-based"
|
|
1757
|
-
].includes(browser);
|
|
1758
|
-
if (!isChromium) return JSON.stringify({
|
|
1938
|
+
if (!isChromiumFamily(browser)) return JSON.stringify({
|
|
1759
1939
|
error: `Source inspection for ${browser} uses RDP (Remote Debug Protocol). Currently only Chromium CDP is supported.`,
|
|
1760
1940
|
hint: 'Pass browser: "chrome" (against a Chromium-family dev session).'
|
|
1761
1941
|
});
|
|
@@ -1868,13 +2048,7 @@ const list_extensions_schema = {
|
|
|
1868
2048
|
};
|
|
1869
2049
|
async function list_extensions_handler(args) {
|
|
1870
2050
|
const { browser } = resolveSessionBrowser(args.projectPath, args.browser, "chrome");
|
|
1871
|
-
|
|
1872
|
-
"chrome",
|
|
1873
|
-
"chromium",
|
|
1874
|
-
"edge",
|
|
1875
|
-
"chromium-based"
|
|
1876
|
-
].includes(browser);
|
|
1877
|
-
if (!isChromium) return JSON.stringify({
|
|
2051
|
+
if (!isChromiumFamily(browser)) return JSON.stringify({
|
|
1878
2052
|
error: `Listing extensions for ${browser} uses RDP (Remote Debug Protocol). Currently only Chromium CDP is supported.`,
|
|
1879
2053
|
hint: 'Pass browser: "chrome" (against a Chromium-family dev session).'
|
|
1880
2054
|
});
|
|
@@ -2824,7 +2998,7 @@ async function wait_handler(args) {
|
|
|
2824
2998
|
}
|
|
2825
2999
|
const add_feature_schema = {
|
|
2826
3000
|
name: "extension_add_feature",
|
|
2827
|
-
description: "
|
|
3001
|
+
description: "Plan a new feature surface for an existing extension. Returns step-by-step instructions, the manifest additions to make, and reference templates from the extension.dev catalog. Does not modify files; apply the returned plan yourself.",
|
|
2828
3002
|
inputSchema: {
|
|
2829
3003
|
type: "object",
|
|
2830
3004
|
properties: {
|
|
@@ -3260,6 +3434,57 @@ async function install_browser_handler(args) {
|
|
|
3260
3434
|
});
|
|
3261
3435
|
}
|
|
3262
3436
|
}
|
|
3437
|
+
const uninstall_browser_schema = {
|
|
3438
|
+
name: "extension_uninstall_browser",
|
|
3439
|
+
description: "Remove a managed browser binary from the Extension.js cache. Only touches the managed cache, never system-installed browsers.",
|
|
3440
|
+
inputSchema: {
|
|
3441
|
+
type: "object",
|
|
3442
|
+
properties: {
|
|
3443
|
+
browser: {
|
|
3444
|
+
type: "string",
|
|
3445
|
+
enum: [
|
|
3446
|
+
"chrome",
|
|
3447
|
+
"chromium",
|
|
3448
|
+
"edge",
|
|
3449
|
+
"firefox"
|
|
3450
|
+
],
|
|
3451
|
+
description: "Managed browser to remove"
|
|
3452
|
+
},
|
|
3453
|
+
all: {
|
|
3454
|
+
type: "boolean",
|
|
3455
|
+
default: false,
|
|
3456
|
+
description: "Remove every managed browser binary"
|
|
3457
|
+
}
|
|
3458
|
+
},
|
|
3459
|
+
required: []
|
|
3460
|
+
}
|
|
3461
|
+
};
|
|
3462
|
+
async function uninstall_browser_handler(args) {
|
|
3463
|
+
const start = Date.now();
|
|
3464
|
+
if (!args.browser && !args.all) return JSON.stringify({
|
|
3465
|
+
status: "error",
|
|
3466
|
+
message: "Provide a browser to remove, or set all: true."
|
|
3467
|
+
});
|
|
3468
|
+
try {
|
|
3469
|
+
await extensionUninstall({
|
|
3470
|
+
browser: args.browser,
|
|
3471
|
+
all: args.all
|
|
3472
|
+
});
|
|
3473
|
+
return JSON.stringify({
|
|
3474
|
+
status: "uninstalled",
|
|
3475
|
+
target: args.all ? "all" : args.browser,
|
|
3476
|
+
duration: Date.now() - start,
|
|
3477
|
+
hint: "Use extension_list_browsers to confirm what remains in the managed cache."
|
|
3478
|
+
});
|
|
3479
|
+
} catch (err) {
|
|
3480
|
+
return JSON.stringify({
|
|
3481
|
+
status: "error",
|
|
3482
|
+
target: args.all ? "all" : args.browser,
|
|
3483
|
+
message: err instanceof Error ? err.message : String(err),
|
|
3484
|
+
duration: Date.now() - start
|
|
3485
|
+
});
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3263
3488
|
const list_browsers_schema = {
|
|
3264
3489
|
name: "extension_list_browsers",
|
|
3265
3490
|
description: "List managed browser binaries installed by the extension.dev platform. Shows what browsers are available in the managed cache without checking system-installed browsers.",
|
|
@@ -3333,7 +3558,14 @@ const detect_browsers_schema = {
|
|
|
3333
3558
|
"chrome",
|
|
3334
3559
|
"chromium",
|
|
3335
3560
|
"edge",
|
|
3336
|
-
"
|
|
3561
|
+
"brave",
|
|
3562
|
+
"opera",
|
|
3563
|
+
"vivaldi",
|
|
3564
|
+
"yandex",
|
|
3565
|
+
"firefox",
|
|
3566
|
+
"waterfox",
|
|
3567
|
+
"librewolf",
|
|
3568
|
+
"safari"
|
|
3337
3569
|
]
|
|
3338
3570
|
},
|
|
3339
3571
|
description: "Browsers to check. If omitted, checks all."
|
|
@@ -3345,8 +3577,21 @@ const ALL_BROWSERS = [
|
|
|
3345
3577
|
"chrome",
|
|
3346
3578
|
"chromium",
|
|
3347
3579
|
"edge",
|
|
3348
|
-
"
|
|
3580
|
+
"brave",
|
|
3581
|
+
"opera",
|
|
3582
|
+
"vivaldi",
|
|
3583
|
+
"yandex",
|
|
3584
|
+
"firefox",
|
|
3585
|
+
"waterfox",
|
|
3586
|
+
"librewolf",
|
|
3587
|
+
"safari"
|
|
3349
3588
|
];
|
|
3589
|
+
const MANAGED_INSTALLABLE = new Set([
|
|
3590
|
+
"chrome",
|
|
3591
|
+
"chromium",
|
|
3592
|
+
"edge",
|
|
3593
|
+
"firefox"
|
|
3594
|
+
]);
|
|
3350
3595
|
const SYSTEM_PATHS = {
|
|
3351
3596
|
darwin: {
|
|
3352
3597
|
chrome: [
|
|
@@ -3364,6 +3609,33 @@ const SYSTEM_PATHS = {
|
|
|
3364
3609
|
firefox: [
|
|
3365
3610
|
"/Applications/Firefox.app/Contents/MacOS/firefox",
|
|
3366
3611
|
`${process.env.HOME}/Applications/Firefox.app/Contents/MacOS/firefox`
|
|
3612
|
+
],
|
|
3613
|
+
brave: [
|
|
3614
|
+
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
|
|
3615
|
+
`${process.env.HOME}/Applications/Brave Browser.app/Contents/MacOS/Brave Browser`
|
|
3616
|
+
],
|
|
3617
|
+
opera: [
|
|
3618
|
+
"/Applications/Opera.app/Contents/MacOS/Opera",
|
|
3619
|
+
`${process.env.HOME}/Applications/Opera.app/Contents/MacOS/Opera`
|
|
3620
|
+
],
|
|
3621
|
+
vivaldi: [
|
|
3622
|
+
"/Applications/Vivaldi.app/Contents/MacOS/Vivaldi",
|
|
3623
|
+
`${process.env.HOME}/Applications/Vivaldi.app/Contents/MacOS/Vivaldi`
|
|
3624
|
+
],
|
|
3625
|
+
yandex: [
|
|
3626
|
+
"/Applications/Yandex.app/Contents/MacOS/Yandex",
|
|
3627
|
+
`${process.env.HOME}/Applications/Yandex.app/Contents/MacOS/Yandex`
|
|
3628
|
+
],
|
|
3629
|
+
waterfox: [
|
|
3630
|
+
"/Applications/Waterfox.app/Contents/MacOS/waterfox",
|
|
3631
|
+
`${process.env.HOME}/Applications/Waterfox.app/Contents/MacOS/waterfox`
|
|
3632
|
+
],
|
|
3633
|
+
librewolf: [
|
|
3634
|
+
"/Applications/LibreWolf.app/Contents/MacOS/librewolf",
|
|
3635
|
+
`${process.env.HOME}/Applications/LibreWolf.app/Contents/MacOS/librewolf`
|
|
3636
|
+
],
|
|
3637
|
+
safari: [
|
|
3638
|
+
"/Applications/Safari.app/Contents/MacOS/Safari"
|
|
3367
3639
|
]
|
|
3368
3640
|
},
|
|
3369
3641
|
linux: {
|
|
@@ -3386,6 +3658,31 @@ const SYSTEM_PATHS = {
|
|
|
3386
3658
|
"/usr/bin/firefox",
|
|
3387
3659
|
"/snap/bin/firefox",
|
|
3388
3660
|
"/usr/lib/firefox/firefox"
|
|
3661
|
+
],
|
|
3662
|
+
brave: [
|
|
3663
|
+
"/usr/bin/brave-browser",
|
|
3664
|
+
"/usr/bin/brave",
|
|
3665
|
+
"/snap/bin/brave"
|
|
3666
|
+
],
|
|
3667
|
+
opera: [
|
|
3668
|
+
"/usr/bin/opera",
|
|
3669
|
+
"/snap/bin/opera"
|
|
3670
|
+
],
|
|
3671
|
+
vivaldi: [
|
|
3672
|
+
"/usr/bin/vivaldi",
|
|
3673
|
+
"/usr/bin/vivaldi-stable"
|
|
3674
|
+
],
|
|
3675
|
+
yandex: [
|
|
3676
|
+
"/usr/bin/yandex-browser",
|
|
3677
|
+
"/usr/bin/yandex-browser-stable"
|
|
3678
|
+
],
|
|
3679
|
+
waterfox: [
|
|
3680
|
+
"/usr/bin/waterfox",
|
|
3681
|
+
"/opt/waterfox/waterfox"
|
|
3682
|
+
],
|
|
3683
|
+
librewolf: [
|
|
3684
|
+
"/usr/bin/librewolf",
|
|
3685
|
+
"/opt/librewolf/librewolf"
|
|
3389
3686
|
]
|
|
3390
3687
|
},
|
|
3391
3688
|
win32: {
|
|
@@ -3403,6 +3700,30 @@ const SYSTEM_PATHS = {
|
|
|
3403
3700
|
firefox: [
|
|
3404
3701
|
"C:\\Program Files\\Mozilla Firefox\\firefox.exe",
|
|
3405
3702
|
"C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"
|
|
3703
|
+
],
|
|
3704
|
+
brave: [
|
|
3705
|
+
"C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe",
|
|
3706
|
+
"C:\\Program Files (x86)\\BraveSoftware\\Brave-Browser\\Application\\brave.exe"
|
|
3707
|
+
],
|
|
3708
|
+
opera: [
|
|
3709
|
+
`${process.env.LOCALAPPDATA || ""}\\Programs\\Opera\\opera.exe`,
|
|
3710
|
+
"C:\\Program Files\\Opera\\opera.exe"
|
|
3711
|
+
],
|
|
3712
|
+
vivaldi: [
|
|
3713
|
+
`${process.env.LOCALAPPDATA || ""}\\Vivaldi\\Application\\vivaldi.exe`,
|
|
3714
|
+
"C:\\Program Files\\Vivaldi\\Application\\vivaldi.exe"
|
|
3715
|
+
],
|
|
3716
|
+
yandex: [
|
|
3717
|
+
`${process.env.LOCALAPPDATA || ""}\\Yandex\\YandexBrowser\\Application\\browser.exe`,
|
|
3718
|
+
"C:\\Program Files (x86)\\Yandex\\YandexBrowser\\Application\\browser.exe"
|
|
3719
|
+
],
|
|
3720
|
+
waterfox: [
|
|
3721
|
+
"C:\\Program Files\\Waterfox\\waterfox.exe",
|
|
3722
|
+
"C:\\Program Files (x86)\\Waterfox\\waterfox.exe"
|
|
3723
|
+
],
|
|
3724
|
+
librewolf: [
|
|
3725
|
+
"C:\\Program Files\\LibreWolf\\librewolf.exe",
|
|
3726
|
+
"C:\\Program Files (x86)\\LibreWolf\\librewolf.exe"
|
|
3406
3727
|
]
|
|
3407
3728
|
}
|
|
3408
3729
|
};
|
|
@@ -3513,7 +3834,8 @@ async function detect_browsers_handler(args) {
|
|
|
3513
3834
|
if (node_fs.existsSync(browserDir)) managed.installed.push(browser);
|
|
3514
3835
|
}
|
|
3515
3836
|
for (const browser of browsersToCheck){
|
|
3516
|
-
const isGecko =
|
|
3837
|
+
const isGecko = isGeckoFamily(browser);
|
|
3838
|
+
const isWebkit = WEBKIT_FAMILY.has(browser);
|
|
3517
3839
|
let binaryPath = findManagedBinary(browser);
|
|
3518
3840
|
let source = "managed";
|
|
3519
3841
|
if (!binaryPath) {
|
|
@@ -3521,14 +3843,14 @@ async function detect_browsers_handler(args) {
|
|
|
3521
3843
|
source = binaryPath ? "system" : "not_found";
|
|
3522
3844
|
}
|
|
3523
3845
|
let version = null;
|
|
3524
|
-
if (binaryPath) version = await getVersion(binaryPath, browser);
|
|
3846
|
+
if (binaryPath && !isWebkit) version = await getVersion(binaryPath, browser);
|
|
3525
3847
|
detected.push({
|
|
3526
3848
|
browser,
|
|
3527
3849
|
binaryPath,
|
|
3528
3850
|
source,
|
|
3529
|
-
engine: isGecko ? "gecko" : "chromium",
|
|
3851
|
+
engine: isWebkit ? "webkit" : isGecko ? "gecko" : "chromium",
|
|
3530
3852
|
version,
|
|
3531
|
-
cdpSupport: !isGecko,
|
|
3853
|
+
cdpSupport: !isGecko && !isWebkit,
|
|
3532
3854
|
rdpSupport: isGecko
|
|
3533
3855
|
});
|
|
3534
3856
|
}
|
|
@@ -3541,9 +3863,66 @@ async function detect_browsers_handler(args) {
|
|
|
3541
3863
|
available: available.map((d)=>d.browser),
|
|
3542
3864
|
missing: missing.map((d)=>d.browser)
|
|
3543
3865
|
},
|
|
3544
|
-
hint: missing.length ? `Missing browser(s): ${missing.map((d)=>d.browser).join(", ")}. Use extension_install_browser to install
|
|
3866
|
+
hint: missing.length ? `Missing browser(s): ${missing.map((d)=>d.browser).join(", ")}.${missing.some((d)=>MANAGED_INSTALLABLE.has(d.browser)) ? ` Use extension_install_browser to install ${missing.filter((d)=>MANAGED_INSTALLABLE.has(d.browser)).map((d)=>d.browser).join(", ")}.` : ""}` : "All requested browsers are available."
|
|
3545
3867
|
});
|
|
3546
3868
|
}
|
|
3869
|
+
const doctor_schema = {
|
|
3870
|
+
name: "extension_doctor",
|
|
3871
|
+
description: "Diagnose a dev session end-to-end: ready contract, dev-server process, control-port agreement, control channel, eval token, executor, and browser liveness. Returns one {check, status, detail, remediation?} entry per leg in dependency order — a 'skip' names the check that blocked it and is NOT a pass. Run this first when any act tool (storage/reload/eval/open) errors unexpectedly. Wraps `extension doctor`.",
|
|
3872
|
+
inputSchema: {
|
|
3873
|
+
type: "object",
|
|
3874
|
+
properties: {
|
|
3875
|
+
projectPath: {
|
|
3876
|
+
type: "string",
|
|
3877
|
+
description: "Path to the extension project root"
|
|
3878
|
+
},
|
|
3879
|
+
browser: {
|
|
3880
|
+
type: "string",
|
|
3881
|
+
description: "Browser session to diagnose. Defaults to the active dev session's browser for this project."
|
|
3882
|
+
}
|
|
3883
|
+
},
|
|
3884
|
+
required: [
|
|
3885
|
+
"projectPath"
|
|
3886
|
+
]
|
|
3887
|
+
}
|
|
3888
|
+
};
|
|
3889
|
+
async function doctor_handler(args) {
|
|
3890
|
+
const { browser } = resolveSessionBrowser(args.projectPath, args.browser);
|
|
3891
|
+
const { code, stdout, stderr } = await runExtensionCli([
|
|
3892
|
+
"doctor",
|
|
3893
|
+
args.projectPath,
|
|
3894
|
+
"--browser",
|
|
3895
|
+
browser,
|
|
3896
|
+
"--output",
|
|
3897
|
+
"json"
|
|
3898
|
+
], {
|
|
3899
|
+
cwd: args.projectPath
|
|
3900
|
+
});
|
|
3901
|
+
const out = stdout.trim();
|
|
3902
|
+
try {
|
|
3903
|
+
const checks = JSON.parse(out);
|
|
3904
|
+
if (!Array.isArray(checks)) throw new Error("not a check array");
|
|
3905
|
+
for (const check of checks){
|
|
3906
|
+
if ("string" == typeof check.detail) check.detail = toMcpSpeak(check.detail);
|
|
3907
|
+
if ("string" == typeof check.remediation) check.remediation = toMcpSpeak(check.remediation);
|
|
3908
|
+
}
|
|
3909
|
+
return JSON.stringify({
|
|
3910
|
+
browser,
|
|
3911
|
+
healthy: 0 === code,
|
|
3912
|
+
checks
|
|
3913
|
+
});
|
|
3914
|
+
} catch {
|
|
3915
|
+
const message = stderr.trim() || `extension exited with code ${code}`;
|
|
3916
|
+
return JSON.stringify({
|
|
3917
|
+
ok: false,
|
|
3918
|
+
error: {
|
|
3919
|
+
name: "CliError",
|
|
3920
|
+
message: toMcpSpeak(message),
|
|
3921
|
+
hint: "extension doctor requires a recent extension CLI — the project's local install may predate it."
|
|
3922
|
+
}
|
|
3923
|
+
});
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3547
3926
|
function typeOf(value) {
|
|
3548
3927
|
if (Array.isArray(value)) return "array";
|
|
3549
3928
|
if (null === value) return "null";
|
|
@@ -3635,8 +4014,10 @@ const tools = [
|
|
|
3635
4014
|
whoami_namespaceObject,
|
|
3636
4015
|
logout_namespaceObject,
|
|
3637
4016
|
install_browser_namespaceObject,
|
|
4017
|
+
uninstall_browser_namespaceObject,
|
|
3638
4018
|
list_browsers_namespaceObject,
|
|
3639
|
-
detect_browsers_namespaceObject
|
|
4019
|
+
detect_browsers_namespaceObject,
|
|
4020
|
+
doctor_namespaceObject
|
|
3640
4021
|
];
|
|
3641
4022
|
const toolMap = new Map();
|
|
3642
4023
|
for (const tool of tools)toolMap.set(tool.schema.name, tool);
|