@extension.dev/mcp 4.3.0 → 4.5.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 +1 -1
- package/.claude-plugin/plugin.json +5 -2
- package/CHANGELOG.md +39 -0
- package/LICENSE +1 -1
- package/README.md +8 -8
- package/bin/extension-mcp.js +7 -0
- 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 +626 -24
- package/dist/src/lib/browser-family.d.ts +1 -0
- package/dist/src/lib/credentials.d.ts +24 -0
- package/dist/src/lib/github-device.d.ts +40 -0
- package/dist/src/lib/launch-flags.d.ts +43 -0
- package/dist/src/lib/login-flow.d.ts +40 -0
- package/dist/src/lib/publish.d.ts +31 -0
- package/dist/src/tools/build.d.ts +24 -0
- package/dist/src/tools/dev.d.ts +45 -7
- 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 +20 -15
- package/server.json +3 -3
package/dist/module.js
CHANGED
|
@@ -11,8 +11,7 @@ import cross_spawn from "cross-spawn";
|
|
|
11
11
|
import { filterKeysForThisBrowser } from "browser-extension-manifest-fields";
|
|
12
12
|
import ws from "ws";
|
|
13
13
|
import node_http from "node:http";
|
|
14
|
-
import {
|
|
15
|
-
import { extensionInstall, getManagedBrowsersCacheRoot } from "extension-install";
|
|
14
|
+
import { extensionInstall, extensionUninstall, getManagedBrowsersCacheRoot } from "extension-install";
|
|
16
15
|
import { execFile } from "node:child_process";
|
|
17
16
|
import { promisify } from "node:util";
|
|
18
17
|
var add_feature_namespaceObject = {};
|
|
@@ -135,9 +134,9 @@ __webpack_require__.d(preview_namespaceObject, {
|
|
|
135
134
|
handler: ()=>preview_handler,
|
|
136
135
|
schema: ()=>preview_schema
|
|
137
136
|
});
|
|
138
|
-
var
|
|
139
|
-
__webpack_require__.r(
|
|
140
|
-
__webpack_require__.d(
|
|
137
|
+
var tools_publish_namespaceObject = {};
|
|
138
|
+
__webpack_require__.r(tools_publish_namespaceObject);
|
|
139
|
+
__webpack_require__.d(tools_publish_namespaceObject, {
|
|
141
140
|
handler: ()=>publish_handler,
|
|
142
141
|
schema: ()=>publish_schema
|
|
143
142
|
});
|
|
@@ -177,6 +176,12 @@ __webpack_require__.d(storage_namespaceObject, {
|
|
|
177
176
|
handler: ()=>storage_handler,
|
|
178
177
|
schema: ()=>storage_schema
|
|
179
178
|
});
|
|
179
|
+
var uninstall_browser_namespaceObject = {};
|
|
180
|
+
__webpack_require__.r(uninstall_browser_namespaceObject);
|
|
181
|
+
__webpack_require__.d(uninstall_browser_namespaceObject, {
|
|
182
|
+
handler: ()=>uninstall_browser_handler,
|
|
183
|
+
schema: ()=>uninstall_browser_schema
|
|
184
|
+
});
|
|
180
185
|
var wait_namespaceObject = {};
|
|
181
186
|
__webpack_require__.r(wait_namespaceObject);
|
|
182
187
|
__webpack_require__.d(wait_namespaceObject, {
|
|
@@ -189,7 +194,7 @@ __webpack_require__.d(whoami_namespaceObject, {
|
|
|
189
194
|
handler: ()=>whoami_handler,
|
|
190
195
|
schema: ()=>whoami_schema
|
|
191
196
|
});
|
|
192
|
-
var package_namespaceObject = JSON.parse('{"rE":"4.
|
|
197
|
+
var package_namespaceObject = JSON.parse('{"rE":"4.5.0","El":{"OP":"^4.0.11"}}');
|
|
193
198
|
const create_schema = {
|
|
194
199
|
name: "extension_create",
|
|
195
200
|
description: "Create a new browser extension project from a template in the extension.dev template catalog. Use extension_list_templates to see available options.",
|
|
@@ -379,9 +384,18 @@ const build_schema = {
|
|
|
379
384
|
"chrome",
|
|
380
385
|
"chromium",
|
|
381
386
|
"edge",
|
|
387
|
+
"brave",
|
|
388
|
+
"opera",
|
|
389
|
+
"vivaldi",
|
|
390
|
+
"yandex",
|
|
382
391
|
"firefox",
|
|
392
|
+
"waterfox",
|
|
393
|
+
"librewolf",
|
|
394
|
+
"safari",
|
|
383
395
|
"chromium-based",
|
|
384
|
-
"gecko-based"
|
|
396
|
+
"gecko-based",
|
|
397
|
+
"firefox-based",
|
|
398
|
+
"webkit-based"
|
|
385
399
|
],
|
|
386
400
|
default: "chrome",
|
|
387
401
|
description: "Target browser"
|
|
@@ -395,6 +409,30 @@ const build_schema = {
|
|
|
395
409
|
type: "boolean",
|
|
396
410
|
default: false,
|
|
397
411
|
description: "Include source code zip (required by some stores)"
|
|
412
|
+
},
|
|
413
|
+
zipFilename: {
|
|
414
|
+
type: "string",
|
|
415
|
+
description: "Custom .zip file name (defaults to name and version)"
|
|
416
|
+
},
|
|
417
|
+
polyfill: {
|
|
418
|
+
type: "boolean",
|
|
419
|
+
default: false,
|
|
420
|
+
description: "Apply cross-browser polyfill"
|
|
421
|
+
},
|
|
422
|
+
silent: {
|
|
423
|
+
type: "boolean",
|
|
424
|
+
default: false,
|
|
425
|
+
description: "Suppress build output"
|
|
426
|
+
},
|
|
427
|
+
mode: {
|
|
428
|
+
type: "string",
|
|
429
|
+
enum: [
|
|
430
|
+
"development",
|
|
431
|
+
"production",
|
|
432
|
+
"none"
|
|
433
|
+
],
|
|
434
|
+
default: "production",
|
|
435
|
+
description: "Bundler mode override (also sets NODE_ENV)"
|
|
398
436
|
}
|
|
399
437
|
},
|
|
400
438
|
required: [
|
|
@@ -409,6 +447,18 @@ async function build_handler(args) {
|
|
|
409
447
|
browser: args.browser ?? "chrome",
|
|
410
448
|
zip: args.zip ?? false,
|
|
411
449
|
zipSource: args.zipSource ?? false,
|
|
450
|
+
...args.zipFilename ? {
|
|
451
|
+
zipFilename: args.zipFilename
|
|
452
|
+
} : {},
|
|
453
|
+
...void 0 !== args.polyfill ? {
|
|
454
|
+
polyfill: args.polyfill
|
|
455
|
+
} : {},
|
|
456
|
+
...void 0 !== args.silent ? {
|
|
457
|
+
silent: args.silent
|
|
458
|
+
} : {},
|
|
459
|
+
...args.mode ? {
|
|
460
|
+
mode: args.mode
|
|
461
|
+
} : {},
|
|
412
462
|
exitOnError: false
|
|
413
463
|
});
|
|
414
464
|
return JSON.stringify({
|
|
@@ -521,6 +571,50 @@ function removeSession(projectPath, browser) {
|
|
|
521
571
|
function listSessions() {
|
|
522
572
|
return Array.from(process_manager_sessions.values());
|
|
523
573
|
}
|
|
574
|
+
const LAUNCH_FLAG_SCHEMA = {
|
|
575
|
+
profile: {
|
|
576
|
+
type: "string",
|
|
577
|
+
description: 'Browser profile path, or "false" to reuse the default user profile. Omit for a fresh throwaway profile.'
|
|
578
|
+
},
|
|
579
|
+
startingUrl: {
|
|
580
|
+
type: "string",
|
|
581
|
+
description: "URL the browser opens on launch"
|
|
582
|
+
},
|
|
583
|
+
chromiumBinary: {
|
|
584
|
+
type: "string",
|
|
585
|
+
description: "Path to a custom Chromium-based binary (overrides browser)"
|
|
586
|
+
},
|
|
587
|
+
geckoBinary: {
|
|
588
|
+
type: "string",
|
|
589
|
+
description: "Path to a custom Gecko/Firefox binary (overrides browser)"
|
|
590
|
+
},
|
|
591
|
+
host: {
|
|
592
|
+
type: "string",
|
|
593
|
+
description: "Dev server bind host. Use 0.0.0.0 for Docker or devcontainers. Defaults to 127.0.0.1"
|
|
594
|
+
},
|
|
595
|
+
publicHost: {
|
|
596
|
+
type: "string",
|
|
597
|
+
description: "Connectable host the browser dials for HMR and reload when it differs from the bind host"
|
|
598
|
+
},
|
|
599
|
+
extensions: {
|
|
600
|
+
type: "array",
|
|
601
|
+
items: {
|
|
602
|
+
type: "string"
|
|
603
|
+
},
|
|
604
|
+
description: "Companion extension paths or store URLs to load alongside the project"
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
function launchFlagArgs(args) {
|
|
608
|
+
const cli = [];
|
|
609
|
+
if (void 0 !== args.profile) cli.push("--profile", args.profile);
|
|
610
|
+
if (args.startingUrl) cli.push("--starting-url", args.startingUrl);
|
|
611
|
+
if (args.chromiumBinary) cli.push("--chromium-binary", args.chromiumBinary);
|
|
612
|
+
if (args.geckoBinary) cli.push("--gecko-binary", args.geckoBinary);
|
|
613
|
+
if (args.host) cli.push("--host", args.host);
|
|
614
|
+
if (args.publicHost) cli.push("--public-host", args.publicHost);
|
|
615
|
+
if (args.extensions?.length) cli.push("--extensions", args.extensions.join(","));
|
|
616
|
+
return cli;
|
|
617
|
+
}
|
|
524
618
|
const dev_schema = {
|
|
525
619
|
name: "extension_dev",
|
|
526
620
|
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.",
|
|
@@ -537,9 +631,18 @@ const dev_schema = {
|
|
|
537
631
|
"chrome",
|
|
538
632
|
"chromium",
|
|
539
633
|
"edge",
|
|
634
|
+
"brave",
|
|
635
|
+
"opera",
|
|
636
|
+
"vivaldi",
|
|
637
|
+
"yandex",
|
|
540
638
|
"firefox",
|
|
639
|
+
"waterfox",
|
|
640
|
+
"librewolf",
|
|
641
|
+
"safari",
|
|
541
642
|
"chromium-based",
|
|
542
|
-
"gecko-based"
|
|
643
|
+
"gecko-based",
|
|
644
|
+
"firefox-based",
|
|
645
|
+
"webkit-based"
|
|
543
646
|
],
|
|
544
647
|
default: "chrome"
|
|
545
648
|
},
|
|
@@ -552,6 +655,12 @@ const dev_schema = {
|
|
|
552
655
|
default: false,
|
|
553
656
|
description: "Start dev server without launching browser"
|
|
554
657
|
},
|
|
658
|
+
polyfill: {
|
|
659
|
+
type: "boolean",
|
|
660
|
+
default: true,
|
|
661
|
+
description: "Apply cross-browser polyfill"
|
|
662
|
+
},
|
|
663
|
+
...LAUNCH_FLAG_SCHEMA,
|
|
555
664
|
allowControl: {
|
|
556
665
|
type: "boolean",
|
|
557
666
|
default: false,
|
|
@@ -578,6 +687,8 @@ async function dev_handler(args) {
|
|
|
578
687
|
];
|
|
579
688
|
if (void 0 !== args.port) cliArgs.push("--port", String(args.port));
|
|
580
689
|
if (args.noBrowser) cliArgs.push("--no-browser");
|
|
690
|
+
if (false === args.polyfill) cliArgs.push("--polyfill", "false");
|
|
691
|
+
cliArgs.push(...launchFlagArgs(args));
|
|
581
692
|
if (args.allowControl) cliArgs.push("--allow-control");
|
|
582
693
|
if (args.allowEval) cliArgs.push("--allow-eval");
|
|
583
694
|
const child = spawnExtensionCli(cliArgs, {
|
|
@@ -627,9 +738,18 @@ const start_schema = {
|
|
|
627
738
|
"chrome",
|
|
628
739
|
"chromium",
|
|
629
740
|
"edge",
|
|
741
|
+
"brave",
|
|
742
|
+
"opera",
|
|
743
|
+
"vivaldi",
|
|
744
|
+
"yandex",
|
|
630
745
|
"firefox",
|
|
746
|
+
"waterfox",
|
|
747
|
+
"librewolf",
|
|
748
|
+
"safari",
|
|
631
749
|
"chromium-based",
|
|
632
|
-
"gecko-based"
|
|
750
|
+
"gecko-based",
|
|
751
|
+
"firefox-based",
|
|
752
|
+
"webkit-based"
|
|
633
753
|
],
|
|
634
754
|
default: "chrome"
|
|
635
755
|
},
|
|
@@ -637,7 +757,17 @@ const start_schema = {
|
|
|
637
757
|
type: "boolean",
|
|
638
758
|
default: true,
|
|
639
759
|
description: "Apply cross-browser polyfill"
|
|
640
|
-
}
|
|
760
|
+
},
|
|
761
|
+
port: {
|
|
762
|
+
type: "number",
|
|
763
|
+
description: "Server port (0 for auto-assign)"
|
|
764
|
+
},
|
|
765
|
+
noBrowser: {
|
|
766
|
+
type: "boolean",
|
|
767
|
+
default: false,
|
|
768
|
+
description: "Build and serve without launching a browser"
|
|
769
|
+
},
|
|
770
|
+
...LAUNCH_FLAG_SCHEMA
|
|
641
771
|
},
|
|
642
772
|
required: [
|
|
643
773
|
"projectPath"
|
|
@@ -652,7 +782,10 @@ async function start_handler(args) {
|
|
|
652
782
|
"--browser",
|
|
653
783
|
browser
|
|
654
784
|
];
|
|
655
|
-
if (false === args.polyfill) cliArgs.push("--
|
|
785
|
+
if (false === args.polyfill) cliArgs.push("--polyfill", "false");
|
|
786
|
+
if (void 0 !== args.port) cliArgs.push("--port", String(args.port));
|
|
787
|
+
if (args.noBrowser) cliArgs.push("--no-browser");
|
|
788
|
+
cliArgs.push(...launchFlagArgs(args));
|
|
656
789
|
const child = spawnExtensionCli(cliArgs, {
|
|
657
790
|
projectDir: args.projectPath
|
|
658
791
|
});
|
|
@@ -698,12 +831,31 @@ const preview_schema = {
|
|
|
698
831
|
"chrome",
|
|
699
832
|
"chromium",
|
|
700
833
|
"edge",
|
|
834
|
+
"brave",
|
|
835
|
+
"opera",
|
|
836
|
+
"vivaldi",
|
|
837
|
+
"yandex",
|
|
701
838
|
"firefox",
|
|
839
|
+
"waterfox",
|
|
840
|
+
"librewolf",
|
|
841
|
+
"safari",
|
|
702
842
|
"chromium-based",
|
|
703
|
-
"gecko-based"
|
|
843
|
+
"gecko-based",
|
|
844
|
+
"firefox-based",
|
|
845
|
+
"webkit-based"
|
|
704
846
|
],
|
|
705
847
|
default: "chrome"
|
|
706
|
-
}
|
|
848
|
+
},
|
|
849
|
+
port: {
|
|
850
|
+
type: "number",
|
|
851
|
+
description: "Server port (0 for auto-assign)"
|
|
852
|
+
},
|
|
853
|
+
noBrowser: {
|
|
854
|
+
type: "boolean",
|
|
855
|
+
default: false,
|
|
856
|
+
description: "Serve the preview without launching a browser"
|
|
857
|
+
},
|
|
858
|
+
...LAUNCH_FLAG_SCHEMA
|
|
707
859
|
},
|
|
708
860
|
required: [
|
|
709
861
|
"projectPath"
|
|
@@ -718,6 +870,9 @@ async function preview_handler(args) {
|
|
|
718
870
|
"--browser",
|
|
719
871
|
browser
|
|
720
872
|
];
|
|
873
|
+
if (void 0 !== args.port) cliArgs.push("--port", String(args.port));
|
|
874
|
+
if (args.noBrowser) cliArgs.push("--no-browser");
|
|
875
|
+
cliArgs.push(...launchFlagArgs(args));
|
|
721
876
|
const child = spawnExtensionCli(cliArgs, {
|
|
722
877
|
projectDir: args.projectPath
|
|
723
878
|
});
|
|
@@ -916,13 +1071,23 @@ const CHROMIUM_FAMILY = new Set([
|
|
|
916
1071
|
"chrome",
|
|
917
1072
|
"chromium",
|
|
918
1073
|
"edge",
|
|
1074
|
+
"brave",
|
|
1075
|
+
"opera",
|
|
1076
|
+
"vivaldi",
|
|
1077
|
+
"yandex",
|
|
919
1078
|
"chromium-based"
|
|
920
1079
|
]);
|
|
921
1080
|
const GECKO_FAMILY = new Set([
|
|
922
1081
|
"firefox",
|
|
1082
|
+
"waterfox",
|
|
1083
|
+
"librewolf",
|
|
923
1084
|
"gecko-based",
|
|
924
1085
|
"firefox-based"
|
|
925
1086
|
]);
|
|
1087
|
+
const WEBKIT_FAMILY = new Set([
|
|
1088
|
+
"safari",
|
|
1089
|
+
"webkit-based"
|
|
1090
|
+
]);
|
|
926
1091
|
function isChromiumFamily(browser) {
|
|
927
1092
|
return CHROMIUM_FAMILY.has(browser);
|
|
928
1093
|
}
|
|
@@ -2624,6 +2789,222 @@ async function dom_inspect_handler(args) {
|
|
|
2624
2789
|
if (null != args.timeout) cli.push("--timeout", String(args.timeout));
|
|
2625
2790
|
return runActVerb(cli, args.projectPath, args.timeout);
|
|
2626
2791
|
}
|
|
2792
|
+
function credentialsPath() {
|
|
2793
|
+
if ("win32" === process.platform) {
|
|
2794
|
+
const base = process.env.APPDATA || process.env.LOCALAPPDATA || node_path.join(node_os.homedir(), "AppData", "Roaming");
|
|
2795
|
+
return node_path.join(base, "extension-dev", "auth.json");
|
|
2796
|
+
}
|
|
2797
|
+
const xdg = String(process.env.XDG_CONFIG_HOME || "").trim();
|
|
2798
|
+
const base = xdg || node_path.join(node_os.homedir(), ".config");
|
|
2799
|
+
return node_path.join(base, "extension-dev", "auth.json");
|
|
2800
|
+
}
|
|
2801
|
+
function readCredentials() {
|
|
2802
|
+
try {
|
|
2803
|
+
const raw = node_fs.readFileSync(credentialsPath(), "utf8");
|
|
2804
|
+
const data = JSON.parse(raw);
|
|
2805
|
+
if (!data || "object" != typeof data) return null;
|
|
2806
|
+
if (1 !== data.version) return null;
|
|
2807
|
+
const token = String(data.token || "").trim();
|
|
2808
|
+
if (!token) return null;
|
|
2809
|
+
return {
|
|
2810
|
+
version: 1,
|
|
2811
|
+
token,
|
|
2812
|
+
workspaceSlug: String(data.workspaceSlug || ""),
|
|
2813
|
+
projectSlug: String(data.projectSlug || ""),
|
|
2814
|
+
expiresAt: Number(data.expiresAt || 0),
|
|
2815
|
+
api: String(data.api || "")
|
|
2816
|
+
};
|
|
2817
|
+
} catch {
|
|
2818
|
+
return null;
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
function writeCredentials(creds) {
|
|
2822
|
+
const file = credentialsPath();
|
|
2823
|
+
const dir = node_path.dirname(file);
|
|
2824
|
+
node_fs.mkdirSync(dir, {
|
|
2825
|
+
recursive: true,
|
|
2826
|
+
mode: 448
|
|
2827
|
+
});
|
|
2828
|
+
try {
|
|
2829
|
+
node_fs.chmodSync(dir, 448);
|
|
2830
|
+
} catch {}
|
|
2831
|
+
node_fs.writeFileSync(file, JSON.stringify(creds, null, 2) + "\n", {
|
|
2832
|
+
mode: 384
|
|
2833
|
+
});
|
|
2834
|
+
try {
|
|
2835
|
+
node_fs.chmodSync(file, 384);
|
|
2836
|
+
} catch {}
|
|
2837
|
+
return file;
|
|
2838
|
+
}
|
|
2839
|
+
function clearCredentials() {
|
|
2840
|
+
const file = credentialsPath();
|
|
2841
|
+
try {
|
|
2842
|
+
node_fs.unlinkSync(file);
|
|
2843
|
+
return {
|
|
2844
|
+
cleared: true,
|
|
2845
|
+
path: file
|
|
2846
|
+
};
|
|
2847
|
+
} catch {
|
|
2848
|
+
return {
|
|
2849
|
+
cleared: false,
|
|
2850
|
+
path: file
|
|
2851
|
+
};
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
function readValidCredentials(nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
2855
|
+
const creds = readCredentials();
|
|
2856
|
+
if (!creds) return null;
|
|
2857
|
+
if (creds.expiresAt && creds.expiresAt <= nowSeconds) return null;
|
|
2858
|
+
return creds;
|
|
2859
|
+
}
|
|
2860
|
+
const DEFAULT_API = "https://www.extension.dev";
|
|
2861
|
+
function resolveApiBase(api) {
|
|
2862
|
+
return String(api || process.env.EXTENSION_DEV_API_URL || DEFAULT_API).replace(/\/+$/, "");
|
|
2863
|
+
}
|
|
2864
|
+
function safeApiBase(raw) {
|
|
2865
|
+
let parsed;
|
|
2866
|
+
try {
|
|
2867
|
+
parsed = new URL(raw);
|
|
2868
|
+
} catch {
|
|
2869
|
+
return {
|
|
2870
|
+
ok: false,
|
|
2871
|
+
message: `Invalid platform URL: ${raw}`
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
const isLocalhost = "localhost" === parsed.hostname || "127.0.0.1" === parsed.hostname || "[::1]" === parsed.hostname || "::1" === parsed.hostname;
|
|
2875
|
+
if ("https:" !== parsed.protocol && !("http:" === parsed.protocol && isLocalhost)) return {
|
|
2876
|
+
ok: false,
|
|
2877
|
+
message: `Refusing to send the access token to ${raw}: use https (http is allowed only for localhost).`
|
|
2878
|
+
};
|
|
2879
|
+
return {
|
|
2880
|
+
ok: true,
|
|
2881
|
+
base: `${parsed.origin}${parsed.pathname}`.replace(/\/+$/, "")
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
async function fetchLoginConfig(apiBase, fetchImpl = fetch) {
|
|
2885
|
+
const override = String(process.env.EXTENSION_DEV_GITHUB_CLIENT_ID || "").trim();
|
|
2886
|
+
if (override) return {
|
|
2887
|
+
clientId: override,
|
|
2888
|
+
scope: "read:user"
|
|
2889
|
+
};
|
|
2890
|
+
const res = await fetchImpl(`${apiBase}/api/cli/login/config`, {
|
|
2891
|
+
headers: {
|
|
2892
|
+
accept: "application/json"
|
|
2893
|
+
}
|
|
2894
|
+
});
|
|
2895
|
+
if (!res.ok) throw new Error(`Could not fetch login config from ${apiBase} (${res.status}).`);
|
|
2896
|
+
const data = await res.json().catch(()=>({}));
|
|
2897
|
+
const clientId = String(data.githubClientId || "").trim();
|
|
2898
|
+
if (!clientId) throw new Error("Login is not configured on the server (no GitHub client id). Set EXTENSION_DEV_GITHUB_CLIENT_ID to override.");
|
|
2899
|
+
return {
|
|
2900
|
+
clientId,
|
|
2901
|
+
scope: String(data.scope || "read:user")
|
|
2902
|
+
};
|
|
2903
|
+
}
|
|
2904
|
+
async function exchangeAndPersist(args) {
|
|
2905
|
+
const doFetch = args.fetchImpl ?? fetch;
|
|
2906
|
+
const res = await doFetch(`${args.apiBase}/api/cli/login/exchange`, {
|
|
2907
|
+
method: "POST",
|
|
2908
|
+
headers: {
|
|
2909
|
+
"content-type": "application/json",
|
|
2910
|
+
accept: "application/json"
|
|
2911
|
+
},
|
|
2912
|
+
body: JSON.stringify({
|
|
2913
|
+
githubToken: args.githubToken,
|
|
2914
|
+
project: args.project
|
|
2915
|
+
})
|
|
2916
|
+
});
|
|
2917
|
+
const text = await res.text();
|
|
2918
|
+
let data;
|
|
2919
|
+
try {
|
|
2920
|
+
data = JSON.parse(text);
|
|
2921
|
+
} catch {
|
|
2922
|
+
data = {
|
|
2923
|
+
message: text
|
|
2924
|
+
};
|
|
2925
|
+
}
|
|
2926
|
+
if (!res.ok) throw new Error(`Login exchange failed (${res.status}): ${data.message || "unknown error"}`);
|
|
2927
|
+
const token = String(data.token || "").trim();
|
|
2928
|
+
if (!token) throw new Error("Login exchange returned no token.");
|
|
2929
|
+
const creds = {
|
|
2930
|
+
version: 1,
|
|
2931
|
+
token,
|
|
2932
|
+
workspaceSlug: String(data.workspaceSlug || ""),
|
|
2933
|
+
projectSlug: String(data.projectSlug || ""),
|
|
2934
|
+
expiresAt: Number(data.expiresAt || 0),
|
|
2935
|
+
api: args.apiBase
|
|
2936
|
+
};
|
|
2937
|
+
writeCredentials(creds);
|
|
2938
|
+
return creds;
|
|
2939
|
+
}
|
|
2940
|
+
function resolveToken() {
|
|
2941
|
+
const fromEnv = String(process.env.EXTENSION_DEV_TOKEN || "").trim();
|
|
2942
|
+
if (fromEnv) return fromEnv;
|
|
2943
|
+
const creds = readValidCredentials();
|
|
2944
|
+
return creds?.token ? String(creds.token).trim() : "";
|
|
2945
|
+
}
|
|
2946
|
+
async function publish(options = {}) {
|
|
2947
|
+
const token = options.token ?? resolveToken();
|
|
2948
|
+
if (!token) return {
|
|
2949
|
+
ok: false,
|
|
2950
|
+
error: {
|
|
2951
|
+
name: "PublishAuthError",
|
|
2952
|
+
message: "No token. Run login, or set EXTENSION_DEV_TOKEN (create one in the extension.dev dashboard)."
|
|
2953
|
+
}
|
|
2954
|
+
};
|
|
2955
|
+
const doFetch = options.fetchImpl ?? fetch;
|
|
2956
|
+
const apiCheck = safeApiBase(resolveApiBase(options.api));
|
|
2957
|
+
if (!apiCheck.ok) return {
|
|
2958
|
+
ok: false,
|
|
2959
|
+
error: {
|
|
2960
|
+
name: "PublishConfigError",
|
|
2961
|
+
message: apiCheck.message
|
|
2962
|
+
}
|
|
2963
|
+
};
|
|
2964
|
+
const url = `${apiCheck.base}/api/cli/publish`;
|
|
2965
|
+
const body = {};
|
|
2966
|
+
if (null != options.ttlHours) body.ttlHours = Number(options.ttlHours);
|
|
2967
|
+
if (options.buildSha) body.buildSha = options.buildSha;
|
|
2968
|
+
let res;
|
|
2969
|
+
try {
|
|
2970
|
+
res = await doFetch(url, {
|
|
2971
|
+
method: "POST",
|
|
2972
|
+
headers: {
|
|
2973
|
+
authorization: `Bearer ${token}`,
|
|
2974
|
+
"content-type": "application/json"
|
|
2975
|
+
},
|
|
2976
|
+
body: JSON.stringify(body)
|
|
2977
|
+
});
|
|
2978
|
+
} catch (err) {
|
|
2979
|
+
return {
|
|
2980
|
+
ok: false,
|
|
2981
|
+
error: {
|
|
2982
|
+
name: "PublishNetworkError",
|
|
2983
|
+
message: `Could not reach ${url}: ${err?.message || err}`
|
|
2984
|
+
}
|
|
2985
|
+
};
|
|
2986
|
+
}
|
|
2987
|
+
const text = await res.text();
|
|
2988
|
+
let data;
|
|
2989
|
+
try {
|
|
2990
|
+
data = JSON.parse(text);
|
|
2991
|
+
} catch {
|
|
2992
|
+
data = {
|
|
2993
|
+
message: text
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2996
|
+
if (!res.ok) return {
|
|
2997
|
+
ok: false,
|
|
2998
|
+
error: {
|
|
2999
|
+
name: "PublishError",
|
|
3000
|
+
message: `publish failed (${res.status}): ${data?.message || text || "unknown error"}`
|
|
3001
|
+
}
|
|
3002
|
+
};
|
|
3003
|
+
return {
|
|
3004
|
+
ok: true,
|
|
3005
|
+
data
|
|
3006
|
+
};
|
|
3007
|
+
}
|
|
2627
3008
|
const publish_schema = {
|
|
2628
3009
|
name: "extension_publish",
|
|
2629
3010
|
description: "Publish a project to extension.dev and return a shareable URL. Auth-gated: requires EXTENSION_DEV_TOKEN (a workspace/project access token) in the environment. Posts to the platform's CLI publish endpoint directly. This is the only tool that talks to the hosted platform rather than the local browser.",
|
|
@@ -2669,7 +3050,7 @@ async function publish_handler(args) {
|
|
|
2669
3050
|
});
|
|
2670
3051
|
return result.ok ? JSON.stringify(result.data) : JSON.stringify(result);
|
|
2671
3052
|
}
|
|
2672
|
-
const
|
|
3053
|
+
const release_promote_DEFAULT_API = "https://www.extension.dev";
|
|
2673
3054
|
const release_promote_schema = {
|
|
2674
3055
|
name: "extension_release_promote",
|
|
2675
3056
|
description: "Promote a built extension to a release channel (e.g. stable, preview, beta) on extension.dev, headless. Auth-gated: requires a release token in EXTENSION_DEV_TOKEN (mint and revoke it in the dashboard under project settings -> Access tokens). Posts to the platform's CLI release endpoint; the project is identified by the token. Cutting a version-bump PR is not available headlessly (it writes to your source repo and needs an interactive login).",
|
|
@@ -2729,7 +3110,7 @@ async function release_promote_handler(args) {
|
|
|
2729
3110
|
const buildId = String(args.buildId || "").trim();
|
|
2730
3111
|
const channel = String(args.channel || "").trim();
|
|
2731
3112
|
if (!buildId || !channel) return fail("ReleaseInputError", "buildId and channel are required.");
|
|
2732
|
-
const apiCheck = safeApiBase(String(args.api || process.env.EXTENSION_DEV_API_URL ||
|
|
3113
|
+
const apiCheck = safeApiBase(String(args.api || process.env.EXTENSION_DEV_API_URL || release_promote_DEFAULT_API));
|
|
2733
3114
|
if (!apiCheck.ok) return fail("ReleaseConfigError", apiCheck.message);
|
|
2734
3115
|
const url = `${apiCheck.base}/api/cli/release/promote`;
|
|
2735
3116
|
const body = {
|
|
@@ -2832,7 +3213,7 @@ async function wait_handler(args) {
|
|
|
2832
3213
|
}
|
|
2833
3214
|
const add_feature_schema = {
|
|
2834
3215
|
name: "extension_add_feature",
|
|
2835
|
-
description: "
|
|
3216
|
+
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.",
|
|
2836
3217
|
inputSchema: {
|
|
2837
3218
|
type: "object",
|
|
2838
3219
|
properties: {
|
|
@@ -3059,6 +3440,78 @@ async function add_feature_handler(args) {
|
|
|
3059
3440
|
hint: conflicts.length ? `Warning: ${conflicts.length} file(s) already exist and would be overwritten.` : "No conflicts detected. Safe to create all files."
|
|
3060
3441
|
});
|
|
3061
3442
|
}
|
|
3443
|
+
const GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
3444
|
+
const GITHUB_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
3445
|
+
const DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
3446
|
+
const defaultSleep = (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
|
|
3447
|
+
async function startDeviceCode(args) {
|
|
3448
|
+
const doFetch = args.fetchImpl ?? fetch;
|
|
3449
|
+
const res = await doFetch(GITHUB_DEVICE_CODE_URL, {
|
|
3450
|
+
method: "POST",
|
|
3451
|
+
headers: {
|
|
3452
|
+
accept: "application/json",
|
|
3453
|
+
"content-type": "application/json"
|
|
3454
|
+
},
|
|
3455
|
+
body: JSON.stringify({
|
|
3456
|
+
client_id: args.clientId,
|
|
3457
|
+
scope: args.scope || "read:user"
|
|
3458
|
+
})
|
|
3459
|
+
});
|
|
3460
|
+
const data = await res.json().catch(()=>({}));
|
|
3461
|
+
if (!res.ok || data.error) throw new Error(`GitHub device-code request failed: ${data.error_description || data.error || res.status}`);
|
|
3462
|
+
return {
|
|
3463
|
+
deviceCode: String(data.device_code || ""),
|
|
3464
|
+
userCode: String(data.user_code || ""),
|
|
3465
|
+
verificationUri: String(data.verification_uri || "https://github.com/login/device"),
|
|
3466
|
+
interval: Number(data.interval || 5),
|
|
3467
|
+
expiresIn: Number(data.expires_in || 900)
|
|
3468
|
+
};
|
|
3469
|
+
}
|
|
3470
|
+
async function pollForToken(args) {
|
|
3471
|
+
const doFetch = args.fetchImpl ?? fetch;
|
|
3472
|
+
const sleep = args.sleepImpl ?? defaultSleep;
|
|
3473
|
+
let intervalMs = 1000 * Math.max(1, args.interval);
|
|
3474
|
+
const deadline = Date.now() + Math.max(0, args.budgetMs);
|
|
3475
|
+
while(Date.now() < deadline){
|
|
3476
|
+
await sleep(intervalMs);
|
|
3477
|
+
const res = await doFetch(GITHUB_TOKEN_URL, {
|
|
3478
|
+
method: "POST",
|
|
3479
|
+
headers: {
|
|
3480
|
+
accept: "application/json",
|
|
3481
|
+
"content-type": "application/json"
|
|
3482
|
+
},
|
|
3483
|
+
body: JSON.stringify({
|
|
3484
|
+
client_id: args.clientId,
|
|
3485
|
+
device_code: args.deviceCode,
|
|
3486
|
+
grant_type: DEVICE_GRANT_TYPE
|
|
3487
|
+
})
|
|
3488
|
+
});
|
|
3489
|
+
const data = await res.json().catch(()=>({}));
|
|
3490
|
+
const token = String(data.access_token || "").trim();
|
|
3491
|
+
if (token) return {
|
|
3492
|
+
ok: true,
|
|
3493
|
+
githubToken: token
|
|
3494
|
+
};
|
|
3495
|
+
const error = String(data.error || "");
|
|
3496
|
+
if ("authorization_pending" === error) continue;
|
|
3497
|
+
if ("slow_down" === error) {
|
|
3498
|
+
intervalMs = Math.max(intervalMs + 5000, 1000 * Number(data.interval || 0));
|
|
3499
|
+
continue;
|
|
3500
|
+
}
|
|
3501
|
+
if ("expired_token" === error) return {
|
|
3502
|
+
ok: false,
|
|
3503
|
+
reason: "expired"
|
|
3504
|
+
};
|
|
3505
|
+
if ("access_denied" === error) return {
|
|
3506
|
+
ok: false,
|
|
3507
|
+
reason: "denied"
|
|
3508
|
+
};
|
|
3509
|
+
}
|
|
3510
|
+
return {
|
|
3511
|
+
ok: false,
|
|
3512
|
+
reason: "pending"
|
|
3513
|
+
};
|
|
3514
|
+
}
|
|
3062
3515
|
const login_schema = {
|
|
3063
3516
|
name: "extension_login",
|
|
3064
3517
|
description: "Authenticate to extension.dev via GitHub device-code and store a project-scoped access token locally so extension_publish can use it. Two-phase: call with `project` to get a code + URL for the user to authorize, then call again with the returned `deviceCode` to finish. Never returns the token. This is the only tool besides extension_publish that talks to the hosted platform.",
|
|
@@ -3268,6 +3721,57 @@ async function install_browser_handler(args) {
|
|
|
3268
3721
|
});
|
|
3269
3722
|
}
|
|
3270
3723
|
}
|
|
3724
|
+
const uninstall_browser_schema = {
|
|
3725
|
+
name: "extension_uninstall_browser",
|
|
3726
|
+
description: "Remove a managed browser binary from the Extension.js cache. Only touches the managed cache, never system-installed browsers.",
|
|
3727
|
+
inputSchema: {
|
|
3728
|
+
type: "object",
|
|
3729
|
+
properties: {
|
|
3730
|
+
browser: {
|
|
3731
|
+
type: "string",
|
|
3732
|
+
enum: [
|
|
3733
|
+
"chrome",
|
|
3734
|
+
"chromium",
|
|
3735
|
+
"edge",
|
|
3736
|
+
"firefox"
|
|
3737
|
+
],
|
|
3738
|
+
description: "Managed browser to remove"
|
|
3739
|
+
},
|
|
3740
|
+
all: {
|
|
3741
|
+
type: "boolean",
|
|
3742
|
+
default: false,
|
|
3743
|
+
description: "Remove every managed browser binary"
|
|
3744
|
+
}
|
|
3745
|
+
},
|
|
3746
|
+
required: []
|
|
3747
|
+
}
|
|
3748
|
+
};
|
|
3749
|
+
async function uninstall_browser_handler(args) {
|
|
3750
|
+
const start = Date.now();
|
|
3751
|
+
if (!args.browser && !args.all) return JSON.stringify({
|
|
3752
|
+
status: "error",
|
|
3753
|
+
message: "Provide a browser to remove, or set all: true."
|
|
3754
|
+
});
|
|
3755
|
+
try {
|
|
3756
|
+
await extensionUninstall({
|
|
3757
|
+
browser: args.browser,
|
|
3758
|
+
all: args.all
|
|
3759
|
+
});
|
|
3760
|
+
return JSON.stringify({
|
|
3761
|
+
status: "uninstalled",
|
|
3762
|
+
target: args.all ? "all" : args.browser,
|
|
3763
|
+
duration: Date.now() - start,
|
|
3764
|
+
hint: "Use extension_list_browsers to confirm what remains in the managed cache."
|
|
3765
|
+
});
|
|
3766
|
+
} catch (err) {
|
|
3767
|
+
return JSON.stringify({
|
|
3768
|
+
status: "error",
|
|
3769
|
+
target: args.all ? "all" : args.browser,
|
|
3770
|
+
message: err instanceof Error ? err.message : String(err),
|
|
3771
|
+
duration: Date.now() - start
|
|
3772
|
+
});
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3271
3775
|
const list_browsers_schema = {
|
|
3272
3776
|
name: "extension_list_browsers",
|
|
3273
3777
|
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.",
|
|
@@ -3341,7 +3845,14 @@ const detect_browsers_schema = {
|
|
|
3341
3845
|
"chrome",
|
|
3342
3846
|
"chromium",
|
|
3343
3847
|
"edge",
|
|
3344
|
-
"
|
|
3848
|
+
"brave",
|
|
3849
|
+
"opera",
|
|
3850
|
+
"vivaldi",
|
|
3851
|
+
"yandex",
|
|
3852
|
+
"firefox",
|
|
3853
|
+
"waterfox",
|
|
3854
|
+
"librewolf",
|
|
3855
|
+
"safari"
|
|
3345
3856
|
]
|
|
3346
3857
|
},
|
|
3347
3858
|
description: "Browsers to check. If omitted, checks all."
|
|
@@ -3353,8 +3864,21 @@ const ALL_BROWSERS = [
|
|
|
3353
3864
|
"chrome",
|
|
3354
3865
|
"chromium",
|
|
3355
3866
|
"edge",
|
|
3356
|
-
"
|
|
3867
|
+
"brave",
|
|
3868
|
+
"opera",
|
|
3869
|
+
"vivaldi",
|
|
3870
|
+
"yandex",
|
|
3871
|
+
"firefox",
|
|
3872
|
+
"waterfox",
|
|
3873
|
+
"librewolf",
|
|
3874
|
+
"safari"
|
|
3357
3875
|
];
|
|
3876
|
+
const MANAGED_INSTALLABLE = new Set([
|
|
3877
|
+
"chrome",
|
|
3878
|
+
"chromium",
|
|
3879
|
+
"edge",
|
|
3880
|
+
"firefox"
|
|
3881
|
+
]);
|
|
3358
3882
|
const SYSTEM_PATHS = {
|
|
3359
3883
|
darwin: {
|
|
3360
3884
|
chrome: [
|
|
@@ -3372,6 +3896,33 @@ const SYSTEM_PATHS = {
|
|
|
3372
3896
|
firefox: [
|
|
3373
3897
|
"/Applications/Firefox.app/Contents/MacOS/firefox",
|
|
3374
3898
|
`${process.env.HOME}/Applications/Firefox.app/Contents/MacOS/firefox`
|
|
3899
|
+
],
|
|
3900
|
+
brave: [
|
|
3901
|
+
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
|
|
3902
|
+
`${process.env.HOME}/Applications/Brave Browser.app/Contents/MacOS/Brave Browser`
|
|
3903
|
+
],
|
|
3904
|
+
opera: [
|
|
3905
|
+
"/Applications/Opera.app/Contents/MacOS/Opera",
|
|
3906
|
+
`${process.env.HOME}/Applications/Opera.app/Contents/MacOS/Opera`
|
|
3907
|
+
],
|
|
3908
|
+
vivaldi: [
|
|
3909
|
+
"/Applications/Vivaldi.app/Contents/MacOS/Vivaldi",
|
|
3910
|
+
`${process.env.HOME}/Applications/Vivaldi.app/Contents/MacOS/Vivaldi`
|
|
3911
|
+
],
|
|
3912
|
+
yandex: [
|
|
3913
|
+
"/Applications/Yandex.app/Contents/MacOS/Yandex",
|
|
3914
|
+
`${process.env.HOME}/Applications/Yandex.app/Contents/MacOS/Yandex`
|
|
3915
|
+
],
|
|
3916
|
+
waterfox: [
|
|
3917
|
+
"/Applications/Waterfox.app/Contents/MacOS/waterfox",
|
|
3918
|
+
`${process.env.HOME}/Applications/Waterfox.app/Contents/MacOS/waterfox`
|
|
3919
|
+
],
|
|
3920
|
+
librewolf: [
|
|
3921
|
+
"/Applications/LibreWolf.app/Contents/MacOS/librewolf",
|
|
3922
|
+
`${process.env.HOME}/Applications/LibreWolf.app/Contents/MacOS/librewolf`
|
|
3923
|
+
],
|
|
3924
|
+
safari: [
|
|
3925
|
+
"/Applications/Safari.app/Contents/MacOS/Safari"
|
|
3375
3926
|
]
|
|
3376
3927
|
},
|
|
3377
3928
|
linux: {
|
|
@@ -3394,6 +3945,31 @@ const SYSTEM_PATHS = {
|
|
|
3394
3945
|
"/usr/bin/firefox",
|
|
3395
3946
|
"/snap/bin/firefox",
|
|
3396
3947
|
"/usr/lib/firefox/firefox"
|
|
3948
|
+
],
|
|
3949
|
+
brave: [
|
|
3950
|
+
"/usr/bin/brave-browser",
|
|
3951
|
+
"/usr/bin/brave",
|
|
3952
|
+
"/snap/bin/brave"
|
|
3953
|
+
],
|
|
3954
|
+
opera: [
|
|
3955
|
+
"/usr/bin/opera",
|
|
3956
|
+
"/snap/bin/opera"
|
|
3957
|
+
],
|
|
3958
|
+
vivaldi: [
|
|
3959
|
+
"/usr/bin/vivaldi",
|
|
3960
|
+
"/usr/bin/vivaldi-stable"
|
|
3961
|
+
],
|
|
3962
|
+
yandex: [
|
|
3963
|
+
"/usr/bin/yandex-browser",
|
|
3964
|
+
"/usr/bin/yandex-browser-stable"
|
|
3965
|
+
],
|
|
3966
|
+
waterfox: [
|
|
3967
|
+
"/usr/bin/waterfox",
|
|
3968
|
+
"/opt/waterfox/waterfox"
|
|
3969
|
+
],
|
|
3970
|
+
librewolf: [
|
|
3971
|
+
"/usr/bin/librewolf",
|
|
3972
|
+
"/opt/librewolf/librewolf"
|
|
3397
3973
|
]
|
|
3398
3974
|
},
|
|
3399
3975
|
win32: {
|
|
@@ -3411,6 +3987,30 @@ const SYSTEM_PATHS = {
|
|
|
3411
3987
|
firefox: [
|
|
3412
3988
|
"C:\\Program Files\\Mozilla Firefox\\firefox.exe",
|
|
3413
3989
|
"C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"
|
|
3990
|
+
],
|
|
3991
|
+
brave: [
|
|
3992
|
+
"C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe",
|
|
3993
|
+
"C:\\Program Files (x86)\\BraveSoftware\\Brave-Browser\\Application\\brave.exe"
|
|
3994
|
+
],
|
|
3995
|
+
opera: [
|
|
3996
|
+
`${process.env.LOCALAPPDATA || ""}\\Programs\\Opera\\opera.exe`,
|
|
3997
|
+
"C:\\Program Files\\Opera\\opera.exe"
|
|
3998
|
+
],
|
|
3999
|
+
vivaldi: [
|
|
4000
|
+
`${process.env.LOCALAPPDATA || ""}\\Vivaldi\\Application\\vivaldi.exe`,
|
|
4001
|
+
"C:\\Program Files\\Vivaldi\\Application\\vivaldi.exe"
|
|
4002
|
+
],
|
|
4003
|
+
yandex: [
|
|
4004
|
+
`${process.env.LOCALAPPDATA || ""}\\Yandex\\YandexBrowser\\Application\\browser.exe`,
|
|
4005
|
+
"C:\\Program Files (x86)\\Yandex\\YandexBrowser\\Application\\browser.exe"
|
|
4006
|
+
],
|
|
4007
|
+
waterfox: [
|
|
4008
|
+
"C:\\Program Files\\Waterfox\\waterfox.exe",
|
|
4009
|
+
"C:\\Program Files (x86)\\Waterfox\\waterfox.exe"
|
|
4010
|
+
],
|
|
4011
|
+
librewolf: [
|
|
4012
|
+
"C:\\Program Files\\LibreWolf\\librewolf.exe",
|
|
4013
|
+
"C:\\Program Files (x86)\\LibreWolf\\librewolf.exe"
|
|
3414
4014
|
]
|
|
3415
4015
|
}
|
|
3416
4016
|
};
|
|
@@ -3521,7 +4121,8 @@ async function detect_browsers_handler(args) {
|
|
|
3521
4121
|
if (node_fs.existsSync(browserDir)) managed.installed.push(browser);
|
|
3522
4122
|
}
|
|
3523
4123
|
for (const browser of browsersToCheck){
|
|
3524
|
-
const isGecko =
|
|
4124
|
+
const isGecko = isGeckoFamily(browser);
|
|
4125
|
+
const isWebkit = WEBKIT_FAMILY.has(browser);
|
|
3525
4126
|
let binaryPath = findManagedBinary(browser);
|
|
3526
4127
|
let source = "managed";
|
|
3527
4128
|
if (!binaryPath) {
|
|
@@ -3529,14 +4130,14 @@ async function detect_browsers_handler(args) {
|
|
|
3529
4130
|
source = binaryPath ? "system" : "not_found";
|
|
3530
4131
|
}
|
|
3531
4132
|
let version = null;
|
|
3532
|
-
if (binaryPath) version = await getVersion(binaryPath, browser);
|
|
4133
|
+
if (binaryPath && !isWebkit) version = await getVersion(binaryPath, browser);
|
|
3533
4134
|
detected.push({
|
|
3534
4135
|
browser,
|
|
3535
4136
|
binaryPath,
|
|
3536
4137
|
source,
|
|
3537
|
-
engine: isGecko ? "gecko" : "chromium",
|
|
4138
|
+
engine: isWebkit ? "webkit" : isGecko ? "gecko" : "chromium",
|
|
3538
4139
|
version,
|
|
3539
|
-
cdpSupport: !isGecko,
|
|
4140
|
+
cdpSupport: !isGecko && !isWebkit,
|
|
3540
4141
|
rdpSupport: isGecko
|
|
3541
4142
|
});
|
|
3542
4143
|
}
|
|
@@ -3549,7 +4150,7 @@ async function detect_browsers_handler(args) {
|
|
|
3549
4150
|
available: available.map((d)=>d.browser),
|
|
3550
4151
|
missing: missing.map((d)=>d.browser)
|
|
3551
4152
|
},
|
|
3552
|
-
hint: missing.length ? `Missing browser(s): ${missing.map((d)=>d.browser).join(", ")}. Use extension_install_browser to install
|
|
4153
|
+
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."
|
|
3553
4154
|
});
|
|
3554
4155
|
}
|
|
3555
4156
|
const doctor_schema = {
|
|
@@ -3692,7 +4293,7 @@ const tools = [
|
|
|
3692
4293
|
reload_namespaceObject,
|
|
3693
4294
|
open_namespaceObject,
|
|
3694
4295
|
dom_inspect_namespaceObject,
|
|
3695
|
-
|
|
4296
|
+
tools_publish_namespaceObject,
|
|
3696
4297
|
release_promote_namespaceObject,
|
|
3697
4298
|
wait_namespaceObject,
|
|
3698
4299
|
add_feature_namespaceObject,
|
|
@@ -3700,6 +4301,7 @@ const tools = [
|
|
|
3700
4301
|
whoami_namespaceObject,
|
|
3701
4302
|
logout_namespaceObject,
|
|
3702
4303
|
install_browser_namespaceObject,
|
|
4304
|
+
uninstall_browser_namespaceObject,
|
|
3703
4305
|
list_browsers_namespaceObject,
|
|
3704
4306
|
detect_browsers_namespaceObject,
|
|
3705
4307
|
doctor_namespaceObject
|