@ada-mcp/mcp-server 0.1.7 → 0.1.9
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 +6 -6
- package/dist/cli.cjs +168 -49
- package/package.json +2 -2
- package/scripts/preinstall-probes.mjs +5 -1
package/README.md
CHANGED
|
@@ -8,25 +8,25 @@ ADA MCP server package that supports:
|
|
|
8
8
|
|
|
9
9
|
## 标准安装(Cursor / MCP)
|
|
10
10
|
|
|
11
|
-
请使用 **`@ada-mcp/launcher@0.1.
|
|
11
|
+
请使用 **`@ada-mcp/launcher@0.1.3`** 拉起本包(见 [launcher README](../ada-mcp-launcher/README.md)):
|
|
12
12
|
|
|
13
13
|
```json
|
|
14
14
|
{
|
|
15
15
|
"mcpServers": {
|
|
16
16
|
"ada-mcp": {
|
|
17
17
|
"command": "pnpm",
|
|
18
|
-
"args": ["dlx", "@ada-mcp/launcher@0.1.
|
|
18
|
+
"args": ["dlx", "@ada-mcp/launcher@0.1.3"]
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
本包版本:**`@ada-mcp/mcp-server@0.1.
|
|
24
|
+
本包版本:**`@ada-mcp/mcp-server@0.1.7`**(由 launcher 默认拉取)。
|
|
25
25
|
|
|
26
26
|
直接调试本包(无 launcher 拉包前测速):
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
pnpm dlx @ada-mcp/mcp-server@0.1.
|
|
29
|
+
pnpm dlx @ada-mcp/mcp-server@0.1.7
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
## 启动时自动安装依赖(默认仅 Playwright)
|
|
@@ -81,12 +81,12 @@ pnpm dlx @ada-mcp/mcp-server@0.1.6
|
|
|
81
81
|
在标准 `args` 后追加,例如安装全部依赖:
|
|
82
82
|
|
|
83
83
|
```json
|
|
84
|
-
"args": ["dlx", "@ada-mcp/launcher@0.1.
|
|
84
|
+
"args": ["dlx", "@ada-mcp/launcher@0.1.3", "--install-deps=all"]
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
## Cursor MCP 配置
|
|
88
88
|
|
|
89
|
-
与上文**标准配置**相同:`pnpm` + `dlx @ada-mcp/launcher@0.1.
|
|
89
|
+
与上文**标准配置**相同:`pnpm` + `dlx @ada-mcp/launcher@0.1.3`。
|
|
90
90
|
|
|
91
91
|
Windows 若找不到 `pnpm`,可写完整路径,例如 `C:\\Users\\<你>\\AppData\\Roaming\\npm\\pnpm.cmd`,或改用 npx:
|
|
92
92
|
|
package/dist/cli.cjs
CHANGED
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/bootstrap-deps.ts
|
|
27
|
-
var
|
|
27
|
+
var import_node_fs3 = __toESM(require("node:fs"));
|
|
28
28
|
var import_node_path10 = __toESM(require("node:path"));
|
|
29
29
|
|
|
30
30
|
// ../ada-agent/dist/config.js
|
|
@@ -2720,7 +2720,7 @@ function createJsonLogger(source) {
|
|
|
2720
2720
|
}
|
|
2721
2721
|
|
|
2722
2722
|
// ../ada-agent/dist/bundled-config.generated.js
|
|
2723
|
-
var bundledDefaultConfigYaml = 'agent:\r\n id: "ada-agent-local"\r\n mode: "foreground"\r\n setupOnFirstRun: true\r\n\r\nbootstrapUI:\r\n enabled: true\r\n mode: "auto" # auto | cli | gui\r\n host: "127.0.0.1"\r\n port: 17650\r\n autoOpenBrowser: true\r\n sessionTtlSec: 600\r\n secretsProvider: "auto" # auto | keychain | credman | file\r\n native:\r\n enabled: false\r\n command: "" # e.g. ./bootstrap-ui / .\\bootstrap-ui.exe\r\n args: []\r\n timeoutMs: 120000\r\n fallbackToWeb: true\r\n\r\ntransport:\r\n mode: "auto"\r\n streamProtocol: "websocket"\r\n requestPath: "/api/v1/execute"\r\n healthPath: "/health"\r\n streamPath: "/ws"\r\n requestTimeoutMs: 15000\r\n\r\ngraphics:\r\n enabled: false\r\n fallbackOnSemanticFailure: false\r\n minConfidence: 0.8\r\n\r\nmonitoring:\r\n enabled: false\r\n platforms: ["web", "android", "ios", "harmony"] # \u53EF\u9009\u5B50\u96C6\r\n sampleEvery: 1 # \u6BCF N \u6761\u64CD\u4F5C\u91C7\u6837\u4E00\u6B21\uFF0C1 \u8868\u793A\u5168\u91CF\r\n outputDir: "artifacts/monitoring"\r\n onFailureOnly: false # true \u65F6\u4EC5\u5931\u8D25\u64CD\u4F5C\u6293\u56FE\uFF0C\u6027\u80FD\u66F4\u4F18\r\n groupBySession: true # \u6309 sessionId/requestId \u5206\u5C42\u5F52\u6863\r\n nonBlocking: true # true \u65F6\u76D1\u63A7\u5F02\u6B65\u6267\u884C\uFF0C\u4E0D\u963B\u585E\u4E3B\u94FE\u8DEF\r\n resolution:\r\n maxWidth: 1280\r\n maxHeight: 720\r\n keepAspectRatio: true # \u4FDD\u6301\u6BD4\u4F8B\uFF0C\u907F\u514D\u76D1\u63A7\u56FE\u50CF\u53D8\u5F62\r\n\r\nqueue:\r\n inboxDir: "tasks/inbox"\r\n processedDir: "tasks/processed"\r\n failedDir: "tasks/failed"\r\n pollIntervalMs: 3000\r\n maxFileRetryAttempts: 2\r\n\r\ndependencies:\r\n autoInstallOnStart: true\r\n playwrightBrowser: "chromium" # chromium | firefox | webkit | all\r\n playwrightInstallTargets: ["
|
|
2723
|
+
var bundledDefaultConfigYaml = 'agent:\r\n id: "ada-agent-local"\r\n mode: "foreground"\r\n setupOnFirstRun: true\r\n\r\nbootstrapUI:\r\n enabled: true\r\n mode: "auto" # auto | cli | gui\r\n host: "127.0.0.1"\r\n port: 17650\r\n autoOpenBrowser: true\r\n sessionTtlSec: 600\r\n secretsProvider: "auto" # auto | keychain | credman | file\r\n native:\r\n enabled: false\r\n command: "" # e.g. ./bootstrap-ui / .\\bootstrap-ui.exe\r\n args: []\r\n timeoutMs: 120000\r\n fallbackToWeb: true\r\n\r\ntransport:\r\n mode: "auto"\r\n streamProtocol: "websocket"\r\n requestPath: "/api/v1/execute"\r\n healthPath: "/health"\r\n streamPath: "/ws"\r\n requestTimeoutMs: 15000\r\n\r\ngraphics:\r\n enabled: false\r\n fallbackOnSemanticFailure: false\r\n minConfidence: 0.8\r\n\r\nmonitoring:\r\n enabled: false\r\n platforms: ["web", "android", "ios", "harmony"] # \u53EF\u9009\u5B50\u96C6\r\n sampleEvery: 1 # \u6BCF N \u6761\u64CD\u4F5C\u91C7\u6837\u4E00\u6B21\uFF0C1 \u8868\u793A\u5168\u91CF\r\n outputDir: "artifacts/monitoring"\r\n onFailureOnly: false # true \u65F6\u4EC5\u5931\u8D25\u64CD\u4F5C\u6293\u56FE\uFF0C\u6027\u80FD\u66F4\u4F18\r\n groupBySession: true # \u6309 sessionId/requestId \u5206\u5C42\u5F52\u6863\r\n nonBlocking: true # true \u65F6\u76D1\u63A7\u5F02\u6B65\u6267\u884C\uFF0C\u4E0D\u963B\u585E\u4E3B\u94FE\u8DEF\r\n resolution:\r\n maxWidth: 1280\r\n maxHeight: 720\r\n keepAspectRatio: true # \u4FDD\u6301\u6BD4\u4F8B\uFF0C\u907F\u514D\u76D1\u63A7\u56FE\u50CF\u53D8\u5F62\r\n\r\nqueue:\r\n inboxDir: "tasks/inbox"\r\n processedDir: "tasks/processed"\r\n failedDir: "tasks/failed"\r\n pollIntervalMs: 3000\r\n maxFileRetryAttempts: 2\r\n\r\ndependencies:\r\n autoInstallOnStart: true\r\n playwrightBrowser: "chromium" # chromium | firefox | webkit | all\r\n playwrightInstallTargets: ["chromium"] # chromium | chrome | msedge | firefox | webkit | all\r\n playwrightDownloadHost: "https://cdn.playwright.dev"\r\n # \u56FD\u5185 npm \u955C\u50CF\uFF08\u6309\u4F18\u5148\u7EA7\uFF1Binstall-deps \u542F\u52A8\u65F6\u6D4B\u901F\u9009\u6700\u5FEB\uFF0C\u65E0\u9700\u7528\u6237\u914D\u7F6E\uFF09\r\n npmRegistryCandidates:\r\n - "https://registry.npmmirror.com" # \u963F\u91CC\u4E91 / npmmirror\uFF08\u63A8\u8350\uFF09\r\n - "https://mirrors.cloud.tencent.com/npm" # \u817E\u8BAF\u4E91\r\n - "https://repo.huaweicloud.com/repository/npm" # \u534E\u4E3A\u4E91\r\n - "https://registry.npmjs.org" # \u5B98\u65B9\u515C\u5E95\r\n playwrightHostCandidates:\r\n - "https://cdn.playwright.dev"\r\n - "https://playwright.azureedge.net"\r\n - "https://npmmirror.com/mirrors/playwright"\r\n - "https://cdn.npmmirror.com/binaries/playwright"\r\n # \u539F\u751F WebDriver\uFF08geckodriver / chromedriver\uFF09\u7EDF\u4E00\u653E\u5728\u9879\u76EE dirver \u76EE\u5F55\r\n nativeDriversDir: "dirver"\r\n geckodriverVersion: "latest" # \u5982 0.36.0\uFF1Binstall-deps --only=selenium \u65F6\u4E0B\u8F7D\u5230\u6B64\u76EE\u5F55\r\n chromedriverVersion: "latest" # \u5982 137\u3001135\u3001match-chrome\uFF08\u5339\u914D\u672C\u673A Chrome \u4E3B\u7248\u672C\uFF09\r\n\r\nappium:\r\n serverUrl: "http://127.0.0.1:4723"\r\n requiredDrivers: ["uiautomator2", "xcuitest", "harmonyos"]\r\n';
|
|
2724
2724
|
|
|
2725
2725
|
// ../ada-agent/dist/config.js
|
|
2726
2726
|
var DEFAULT_CONFIG_RELATIVE = import_node_path2.default.join("config", "default.yaml");
|
|
@@ -2785,8 +2785,8 @@ var defaultConfig = {
|
|
|
2785
2785
|
dependencies: {
|
|
2786
2786
|
autoInstallOnStart: true,
|
|
2787
2787
|
playwrightBrowser: "chromium",
|
|
2788
|
-
playwrightInstallTargets: ["
|
|
2789
|
-
playwrightDownloadHost: "https://
|
|
2788
|
+
playwrightInstallTargets: ["chromium"],
|
|
2789
|
+
playwrightDownloadHost: "https://cdn.playwright.dev",
|
|
2790
2790
|
npmRegistryCandidates: [
|
|
2791
2791
|
"https://registry.npmmirror.com",
|
|
2792
2792
|
"https://mirrors.cloud.tencent.com/npm",
|
|
@@ -2794,8 +2794,10 @@ var defaultConfig = {
|
|
|
2794
2794
|
"https://registry.npmjs.org"
|
|
2795
2795
|
],
|
|
2796
2796
|
playwrightHostCandidates: [
|
|
2797
|
+
"https://cdn.playwright.dev",
|
|
2798
|
+
"https://playwright.azureedge.net",
|
|
2797
2799
|
"https://npmmirror.com/mirrors/playwright",
|
|
2798
|
-
"https://
|
|
2800
|
+
"https://cdn.npmmirror.com/binaries/playwright"
|
|
2799
2801
|
],
|
|
2800
2802
|
nativeDriversDir: "dirver",
|
|
2801
2803
|
geckodriverVersion: "latest",
|
|
@@ -2906,6 +2908,7 @@ async function loadSecret(provider, cwd = process.cwd()) {
|
|
|
2906
2908
|
|
|
2907
2909
|
// ../ada-agent/dist/dependency-installer.js
|
|
2908
2910
|
var import_node_module = require("node:module");
|
|
2911
|
+
var import_node_fs = require("node:fs");
|
|
2909
2912
|
var import_node_child_process2 = require("node:child_process");
|
|
2910
2913
|
var import_promises5 = __toESM(require("node:fs/promises"), 1);
|
|
2911
2914
|
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
@@ -3544,19 +3547,51 @@ function playwrightInstallTargets(config) {
|
|
|
3544
3547
|
const legacy = browserArg(config);
|
|
3545
3548
|
return legacy ? [legacy] : [];
|
|
3546
3549
|
}
|
|
3550
|
+
function expandPlaywrightInstallTargets(targets) {
|
|
3551
|
+
if (targets.length === 0) {
|
|
3552
|
+
return targets;
|
|
3553
|
+
}
|
|
3554
|
+
const lower = targets.map((x) => x.toLowerCase());
|
|
3555
|
+
if (lower.includes("chromium") || lower.includes("firefox") || lower.includes("webkit")) {
|
|
3556
|
+
return lower;
|
|
3557
|
+
}
|
|
3558
|
+
const channelOnly = lower.every((x) => x === "chrome" || x === "msedge");
|
|
3559
|
+
if (channelOnly) {
|
|
3560
|
+
return ["chromium", ...lower];
|
|
3561
|
+
}
|
|
3562
|
+
return lower;
|
|
3563
|
+
}
|
|
3564
|
+
function resolveBundledPlaywrightCli() {
|
|
3565
|
+
const pkgPath = require2.resolve("playwright/package.json");
|
|
3566
|
+
const root = import_node_path5.default.dirname(pkgPath);
|
|
3567
|
+
const raw = (0, import_node_fs.readFileSync)(pkgPath, "utf8");
|
|
3568
|
+
const version = String(JSON.parse(raw).version ?? "");
|
|
3569
|
+
return { command: "node", cliArgs: [import_node_path5.default.join(root, "cli.js")], version };
|
|
3570
|
+
}
|
|
3547
3571
|
function requiredAppiumDrivers(config) {
|
|
3548
3572
|
return Array.from(new Set(config.appium.requiredDrivers ?? []));
|
|
3549
3573
|
}
|
|
3574
|
+
var DEFAULT_NPM_REGISTRY_CANDIDATES = [
|
|
3575
|
+
"https://registry.npmmirror.com",
|
|
3576
|
+
"https://mirrors.cloud.tencent.com/npm",
|
|
3577
|
+
"https://repo.huaweicloud.com/repository/npm",
|
|
3578
|
+
"https://registry.npmjs.org"
|
|
3579
|
+
];
|
|
3550
3580
|
function npmProxyRegistry() {
|
|
3551
|
-
return process.env.ADA_NPM_PROXY_REGISTRY ??
|
|
3581
|
+
return process.env.ADA_NPM_PROXY_REGISTRY ?? DEFAULT_NPM_REGISTRY_CANDIDATES[0];
|
|
3552
3582
|
}
|
|
3553
3583
|
function pnpmProxyRegistry() {
|
|
3554
3584
|
return process.env.ADA_PNPM_PROXY_REGISTRY ?? npmProxyRegistry();
|
|
3555
3585
|
}
|
|
3586
|
+
function parsePositiveMs(raw, fallback) {
|
|
3587
|
+
const parsed = raw ? Number(raw) : fallback;
|
|
3588
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
3589
|
+
}
|
|
3556
3590
|
function installStrategyTimeoutMs() {
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3591
|
+
return parsePositiveMs(process.env.ADA_INSTALL_STRATEGY_TIMEOUT_MS, 12e4);
|
|
3592
|
+
}
|
|
3593
|
+
function playwrightInstallTimeoutMs() {
|
|
3594
|
+
return parsePositiveMs(process.env.ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS, 9e5);
|
|
3560
3595
|
}
|
|
3561
3596
|
function majorOf(versionLike) {
|
|
3562
3597
|
const text = versionLike.trim().replace(/^v/i, "");
|
|
@@ -3679,10 +3714,23 @@ function normalizeRegistryUrl(url) {
|
|
|
3679
3714
|
}
|
|
3680
3715
|
function registryCandidates(config, baseProxy) {
|
|
3681
3716
|
const primary = normalizeRegistryUrl(baseProxy);
|
|
3682
|
-
const
|
|
3683
|
-
const
|
|
3717
|
+
const fromConfig = Array.isArray(config.dependencies.npmRegistryCandidates) ? config.dependencies.npmRegistryCandidates.map((x) => normalizeRegistryUrl(String(x).trim())).filter(Boolean) : [];
|
|
3718
|
+
const configured = fromConfig.length > 0 ? fromConfig : [...DEFAULT_NPM_REGISTRY_CANDIDATES];
|
|
3684
3719
|
const extra = process.env.ADA_REGISTRY_CANDIDATES ? process.env.ADA_REGISTRY_CANDIDATES.split(",").map((x) => normalizeRegistryUrl(x.trim())).filter(Boolean) : [];
|
|
3685
|
-
|
|
3720
|
+
const ordered = [primary, ...configured, ...extra];
|
|
3721
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3722
|
+
const out = [];
|
|
3723
|
+
for (const url of ordered) {
|
|
3724
|
+
if (!seen.has(url)) {
|
|
3725
|
+
seen.add(url);
|
|
3726
|
+
out.push(url);
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
return out;
|
|
3730
|
+
}
|
|
3731
|
+
function registryPriorityIndex(candidates, registry) {
|
|
3732
|
+
const idx = candidates.indexOf(normalizeRegistryUrl(registry));
|
|
3733
|
+
return idx >= 0 ? idx : Number.POSITIVE_INFINITY;
|
|
3686
3734
|
}
|
|
3687
3735
|
async function probeRegistryLatency(registry) {
|
|
3688
3736
|
const target = `${normalizeRegistryUrl(registry)}/appium`;
|
|
@@ -3718,14 +3766,17 @@ async function detectBestRegistry(config, baseProxy) {
|
|
|
3718
3766
|
progress("registry.probe.result", { candidate, latencyMs: latency });
|
|
3719
3767
|
return { candidate, latency };
|
|
3720
3768
|
}));
|
|
3721
|
-
let best = normalizeRegistryUrl(baseProxy);
|
|
3769
|
+
let best = candidates[0] ?? normalizeRegistryUrl(baseProxy);
|
|
3722
3770
|
let bestLatency = Number.POSITIVE_INFINITY;
|
|
3771
|
+
let bestPriority = Number.POSITIVE_INFINITY;
|
|
3723
3772
|
for (const { candidate, latency } of probeResults) {
|
|
3724
3773
|
if (latency === null)
|
|
3725
3774
|
continue;
|
|
3726
|
-
|
|
3775
|
+
const priority = registryPriorityIndex(candidates, candidate);
|
|
3776
|
+
if (latency < bestLatency || latency === bestLatency && priority < bestPriority) {
|
|
3727
3777
|
best = candidate;
|
|
3728
3778
|
bestLatency = latency;
|
|
3779
|
+
bestPriority = priority;
|
|
3729
3780
|
}
|
|
3730
3781
|
}
|
|
3731
3782
|
detectedBestRegistryByKey.set(cacheKey, best);
|
|
@@ -3744,19 +3795,46 @@ function playwrightDownloadHost(config) {
|
|
|
3744
3795
|
function normalizeHostUrl(url) {
|
|
3745
3796
|
return url.replace(/\/$/, "");
|
|
3746
3797
|
}
|
|
3798
|
+
var DEFAULT_PLAYWRIGHT_HOST_CANDIDATES = [
|
|
3799
|
+
"https://cdn.playwright.dev",
|
|
3800
|
+
"https://playwright.azureedge.net",
|
|
3801
|
+
"https://npmmirror.com/mirrors/playwright",
|
|
3802
|
+
"https://cdn.npmmirror.com/binaries/playwright"
|
|
3803
|
+
];
|
|
3804
|
+
var PLAYWRIGHT_HOST_FALLBACK = DEFAULT_PLAYWRIGHT_HOST_CANDIDATES[0];
|
|
3805
|
+
function playwrightProbeUrls(host) {
|
|
3806
|
+
const h = normalizeHostUrl(host);
|
|
3807
|
+
if (h.includes("npmmirror.com/mirrors/playwright")) {
|
|
3808
|
+
return [h, "https://cdn.npmmirror.com/binaries/playwright"];
|
|
3809
|
+
}
|
|
3810
|
+
return [h];
|
|
3811
|
+
}
|
|
3747
3812
|
function playwrightHostCandidates(config) {
|
|
3748
3813
|
const configured = normalizeHostUrl(playwrightDownloadHost(config));
|
|
3749
|
-
const
|
|
3750
|
-
const
|
|
3814
|
+
const fromConfig = Array.isArray(config.dependencies.playwrightHostCandidates) ? config.dependencies.playwrightHostCandidates.map((x) => normalizeHostUrl(String(x).trim())).filter(Boolean) : [];
|
|
3815
|
+
const configuredList = fromConfig.length > 0 ? fromConfig : [...DEFAULT_PLAYWRIGHT_HOST_CANDIDATES];
|
|
3751
3816
|
const extra = process.env.ADA_PLAYWRIGHT_HOST_CANDIDATES ? process.env.ADA_PLAYWRIGHT_HOST_CANDIDATES.split(",").map((x) => normalizeHostUrl(x.trim())).filter(Boolean) : [];
|
|
3752
|
-
|
|
3817
|
+
const ordered = [configured, ...configuredList, ...extra];
|
|
3818
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3819
|
+
const out = [];
|
|
3820
|
+
for (const url of ordered) {
|
|
3821
|
+
if (!seen.has(url)) {
|
|
3822
|
+
seen.add(url);
|
|
3823
|
+
out.push(url);
|
|
3824
|
+
}
|
|
3825
|
+
}
|
|
3826
|
+
return out;
|
|
3753
3827
|
}
|
|
3754
|
-
|
|
3828
|
+
function playwrightHostPriorityIndex(candidates, host) {
|
|
3829
|
+
const idx = candidates.indexOf(normalizeHostUrl(host));
|
|
3830
|
+
return idx >= 0 ? idx : Number.POSITIVE_INFINITY;
|
|
3831
|
+
}
|
|
3832
|
+
async function probeUrlLatency(url) {
|
|
3755
3833
|
const started = Date.now();
|
|
3756
3834
|
const controller = new AbortController();
|
|
3757
3835
|
const timer = setTimeout(() => controller.abort(), 5e3);
|
|
3758
3836
|
try {
|
|
3759
|
-
const response = await fetch(
|
|
3837
|
+
const response = await fetch(url, {
|
|
3760
3838
|
method: "GET",
|
|
3761
3839
|
redirect: "manual",
|
|
3762
3840
|
signal: controller.signal
|
|
@@ -3771,6 +3849,16 @@ async function probeHostLatency(host) {
|
|
|
3771
3849
|
clearTimeout(timer);
|
|
3772
3850
|
}
|
|
3773
3851
|
}
|
|
3852
|
+
async function probePlaywrightHostLatency(host) {
|
|
3853
|
+
let best = null;
|
|
3854
|
+
for (const url of playwrightProbeUrls(host)) {
|
|
3855
|
+
const latency = await probeUrlLatency(url);
|
|
3856
|
+
if (latency !== null && (best === null || latency < best)) {
|
|
3857
|
+
best = latency;
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
return best;
|
|
3861
|
+
}
|
|
3774
3862
|
async function detectBestPlaywrightHost(config) {
|
|
3775
3863
|
if (detectedBestPlaywrightHost) {
|
|
3776
3864
|
return detectedBestPlaywrightHost;
|
|
@@ -3779,18 +3867,21 @@ async function detectBestPlaywrightHost(config) {
|
|
|
3779
3867
|
progress("playwright.host.probe.start", { candidates });
|
|
3780
3868
|
const probeResults = await Promise.all(candidates.map(async (candidate) => {
|
|
3781
3869
|
progress("playwright.host.probe.try", { candidate });
|
|
3782
|
-
const latency = await
|
|
3870
|
+
const latency = await probePlaywrightHostLatency(candidate);
|
|
3783
3871
|
progress("playwright.host.probe.result", { candidate, latencyMs: latency });
|
|
3784
3872
|
return { candidate, latency };
|
|
3785
3873
|
}));
|
|
3786
|
-
let best =
|
|
3874
|
+
let best = candidates[0] ?? PLAYWRIGHT_HOST_FALLBACK;
|
|
3787
3875
|
let bestLatency = Number.POSITIVE_INFINITY;
|
|
3876
|
+
let bestPriority = Number.POSITIVE_INFINITY;
|
|
3788
3877
|
for (const { candidate, latency } of probeResults) {
|
|
3789
3878
|
if (latency === null)
|
|
3790
3879
|
continue;
|
|
3791
|
-
|
|
3880
|
+
const priority = playwrightHostPriorityIndex(candidates, candidate);
|
|
3881
|
+
if (latency < bestLatency || latency === bestLatency && priority < bestPriority) {
|
|
3792
3882
|
best = candidate;
|
|
3793
3883
|
bestLatency = latency;
|
|
3884
|
+
bestPriority = priority;
|
|
3794
3885
|
}
|
|
3795
3886
|
}
|
|
3796
3887
|
detectedBestPlaywrightHost = best;
|
|
@@ -3807,7 +3898,7 @@ async function runInstallWithPriority(config, packages, onLogLine) {
|
|
|
3807
3898
|
const npmProxy = await detectBestRegistry(config, npmProxyRegistry());
|
|
3808
3899
|
const pnpmProxy = await detectBestRegistry(config, pnpmProxyRegistry());
|
|
3809
3900
|
progress("packages.install.start", { packages, npmProxy, pnpmProxy });
|
|
3810
|
-
onLogLine?.(`[deps] \u5728\u7EBF\u5B89\u88C5\u5305: ${packages.join(" ")} (\u987A\u5E8F: pnpm -> pnpm-proxy -> npm -> npm-proxy)`);
|
|
3901
|
+
onLogLine?.(`[deps] \u5728\u7EBF\u5B89\u88C5\u5305: ${packages.join(" ")} (registry \u63A2\u6D4B: npm=${npmProxy}, pnpm=${pnpmProxy}\uFF1B\u987A\u5E8F: pnpm -> pnpm-proxy -> npm -> npm-proxy)`);
|
|
3811
3902
|
const strategies = [
|
|
3812
3903
|
{
|
|
3813
3904
|
name: "pnpm",
|
|
@@ -3928,16 +4019,29 @@ async function verifyPlaywrightSelfTest(onLogLine) {
|
|
|
3928
4019
|
await b.close();
|
|
3929
4020
|
}
|
|
3930
4021
|
}
|
|
3931
|
-
async function installPlaywrightBrowser(config, onLogLine) {
|
|
3932
|
-
const
|
|
4022
|
+
async function installPlaywrightBrowser(config, onLogLine, options) {
|
|
4023
|
+
const rawTargets = playwrightInstallTargets(config);
|
|
4024
|
+
const targets = expandPlaywrightInstallTargets(rawTargets);
|
|
4025
|
+
if (rawTargets.length > 0 && targets.length > rawTargets.length) {
|
|
4026
|
+
onLogLine?.("[playwright] \u914D\u7F6E\u4EC5\u542B chrome/msedge \u901A\u9053\uFF0C\u5DF2\u81EA\u52A8\u52A0\u5165 chromium\uFF08\u81EA\u68C0\u9700 Playwright \u5185\u7F6E\u6D4F\u89C8\u5668\uFF09");
|
|
4027
|
+
}
|
|
3933
4028
|
progress("playwright.browser.install.start", { targets: targets.length > 0 ? targets : ["all"] });
|
|
3934
4029
|
onLogLine?.("[playwright] \u5F00\u59CB\u5728\u7EBF\u5B89\u88C5\u6D4F\u89C8\u5668");
|
|
3935
|
-
const
|
|
4030
|
+
const { command, cliArgs, version } = resolveBundledPlaywrightCli();
|
|
4031
|
+
const installArgs = [...cliArgs, "install"];
|
|
4032
|
+
if (options?.force) {
|
|
4033
|
+
installArgs.push("--force");
|
|
4034
|
+
}
|
|
4035
|
+
if (targets.length > 0) {
|
|
4036
|
+
installArgs.push(...targets);
|
|
4037
|
+
}
|
|
3936
4038
|
const selectedHost = await detectBestPlaywrightHost(config);
|
|
3937
|
-
onLogLine?.(`[playwright] \
|
|
3938
|
-
|
|
4039
|
+
onLogLine?.(`[playwright] \u4F7F\u7528\u5185\u7F6E playwright@${version} CLI\uFF0C\u955C\u50CF ${selectedHost}\uFF0C\u76EE\u6807: ${targets.length ? targets.join(",") : "all"}${options?.force ? " (--force)" : ""}`);
|
|
4040
|
+
const timeoutMs = playwrightInstallTimeoutMs();
|
|
4041
|
+
onLogLine?.(`[playwright] \u5B89\u88C5\u8D85\u65F6\u4E0A\u9650 ${Math.round(timeoutMs / 1e3)}s\uFF08\u53EF\u7528 ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS \u8C03\u6574\uFF09`);
|
|
4042
|
+
await runCommand2(command, installArgs, {
|
|
3939
4043
|
env: { PLAYWRIGHT_DOWNLOAD_HOST: selectedHost },
|
|
3940
|
-
timeoutMs
|
|
4044
|
+
timeoutMs,
|
|
3941
4045
|
onLogLine
|
|
3942
4046
|
});
|
|
3943
4047
|
progress("playwright.browser.install.done", { selectedHost });
|
|
@@ -4191,7 +4295,9 @@ async function ensureDriverDependencies(config, options) {
|
|
|
4191
4295
|
if (force) {
|
|
4192
4296
|
onLogLine?.(userRequestedBrowserTargets ? "[playwright] --force\uFF1A\u91CD\u65B0\u5B89\u88C5\u5F53\u524D\u52FE\u9009\u7684\u6D4F\u89C8\u5668\u901A\u9053" : "[playwright] --force\uFF1A\u6309\u914D\u7F6E\u6587\u4EF6\u4E2D\u7684\u76EE\u6807\u91CD\u65B0\u5B89\u88C5\u6D4F\u89C8\u5668");
|
|
4193
4297
|
}
|
|
4194
|
-
await installPlaywrightBrowser(configForPlaywright, onLogLine
|
|
4298
|
+
await installPlaywrightBrowser(configForPlaywright, onLogLine, {
|
|
4299
|
+
force: force || reinstallForTargets || !launchOk
|
|
4300
|
+
});
|
|
4195
4301
|
progress("playwright.selfcheck.verify");
|
|
4196
4302
|
await verifyPlaywrightSelfTest(onLogLine);
|
|
4197
4303
|
state.playwrightReady = true;
|
|
@@ -4364,7 +4470,7 @@ function getString(value) {
|
|
|
4364
4470
|
}
|
|
4365
4471
|
|
|
4366
4472
|
// ../../packages/plugin-host/src/index.ts
|
|
4367
|
-
var
|
|
4473
|
+
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
4368
4474
|
var import_node_path6 = __toESM(require("node:path"), 1);
|
|
4369
4475
|
var import_node_module2 = require("node:module");
|
|
4370
4476
|
function assertManifest(plugin) {
|
|
@@ -4516,10 +4622,10 @@ function loadPluginFromModule(requireFn, moduleId) {
|
|
|
4516
4622
|
}
|
|
4517
4623
|
}
|
|
4518
4624
|
function registerPluginsFromDirectory(host, pluginDir) {
|
|
4519
|
-
if (!
|
|
4625
|
+
if (!import_node_fs2.default.existsSync(pluginDir)) {
|
|
4520
4626
|
return [];
|
|
4521
4627
|
}
|
|
4522
|
-
const entries =
|
|
4628
|
+
const entries = import_node_fs2.default.readdirSync(pluginDir, { withFileTypes: true }).filter((ent) => ent.isFile() && (ent.name.endsWith(".cjs") || ent.name.endsWith(".js"))).map((ent) => import_node_path6.default.join(pluginDir, ent.name)).sort((a, b) => a.localeCompare(b));
|
|
4523
4629
|
if (entries.length === 0) {
|
|
4524
4630
|
return [];
|
|
4525
4631
|
}
|
|
@@ -4874,7 +4980,7 @@ function normalizePlaywrightTargets(raw) {
|
|
|
4874
4980
|
return { playwrightBrowser: "all", playwrightInstallTargets: ["all"] };
|
|
4875
4981
|
}
|
|
4876
4982
|
if (list.length === 0) {
|
|
4877
|
-
return { playwrightBrowser: "chromium", playwrightInstallTargets: ["
|
|
4983
|
+
return { playwrightBrowser: "chromium", playwrightInstallTargets: ["chromium"] };
|
|
4878
4984
|
}
|
|
4879
4985
|
const ordered = ["chromium", "chrome", "firefox", "webkit", "msedge"].filter((x) => list.includes(x));
|
|
4880
4986
|
const primary = ordered.find((x) => x === "chromium" || x === "firefox" || x === "webkit") ?? "chromium";
|
|
@@ -6518,6 +6624,13 @@ function resolveBootstrapInstallDeps(argv2) {
|
|
|
6518
6624
|
if (nativeDir) extras.nativeDriversDir = nativeDir;
|
|
6519
6625
|
return { skip: false, scopes, force, extras };
|
|
6520
6626
|
}
|
|
6627
|
+
var PREINSTALL_PLAYWRIGHT_HOST_ALLOW = /* @__PURE__ */ new Set([
|
|
6628
|
+
"https://cdn.playwright.dev",
|
|
6629
|
+
"https://playwright.azureedge.net"
|
|
6630
|
+
]);
|
|
6631
|
+
function normalizePlaywrightHost(url) {
|
|
6632
|
+
return url.replace(/\/$/, "");
|
|
6633
|
+
}
|
|
6521
6634
|
function applyPreinstallPlaywrightHostFile() {
|
|
6522
6635
|
if (process.env.PLAYWRIGHT_DOWNLOAD_HOST?.trim()) {
|
|
6523
6636
|
return;
|
|
@@ -6528,12 +6641,18 @@ function applyPreinstallPlaywrightHostFile() {
|
|
|
6528
6641
|
for (const root of roots) {
|
|
6529
6642
|
const file = import_node_path10.default.join(root, ".ada-mcp-playwright-host");
|
|
6530
6643
|
try {
|
|
6531
|
-
const host =
|
|
6532
|
-
if (host.length > 0) {
|
|
6644
|
+
const host = normalizePlaywrightHost(import_node_fs3.default.readFileSync(file, "utf8").trim());
|
|
6645
|
+
if (host.length > 0 && PREINSTALL_PLAYWRIGHT_HOST_ALLOW.has(host)) {
|
|
6533
6646
|
process.env.PLAYWRIGHT_DOWNLOAD_HOST = host;
|
|
6534
6647
|
console.error(`[ADA-MCP] using playwright CDN from ${file}: ${host}`);
|
|
6535
6648
|
return;
|
|
6536
6649
|
}
|
|
6650
|
+
if (host.length > 0) {
|
|
6651
|
+
console.error(
|
|
6652
|
+
`[ADA-MCP] ignore preinstall playwright CDN (runtime will re-probe): ${host}`
|
|
6653
|
+
);
|
|
6654
|
+
return;
|
|
6655
|
+
}
|
|
6537
6656
|
} catch {
|
|
6538
6657
|
}
|
|
6539
6658
|
}
|
|
@@ -6567,7 +6686,7 @@ async function runBootstrapInstallDeps(argv2) {
|
|
|
6567
6686
|
|
|
6568
6687
|
// src/main.ts
|
|
6569
6688
|
var import_promises13 = __toESM(require("node:fs/promises"));
|
|
6570
|
-
var
|
|
6689
|
+
var import_node_fs5 = require("node:fs");
|
|
6571
6690
|
var import_node_net2 = __toESM(require("node:net"));
|
|
6572
6691
|
var import_node_path14 = __toESM(require("node:path"));
|
|
6573
6692
|
var import_node_child_process5 = require("node:child_process");
|
|
@@ -6577,7 +6696,7 @@ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
|
6577
6696
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
6578
6697
|
|
|
6579
6698
|
// src/executor.ts
|
|
6580
|
-
var
|
|
6699
|
+
var import_node_fs4 = require("node:fs");
|
|
6581
6700
|
var import_node_path11 = __toESM(require("node:path"));
|
|
6582
6701
|
var import_node_url3 = require("node:url");
|
|
6583
6702
|
var import_meta = {};
|
|
@@ -6596,7 +6715,7 @@ function ensureBundledPluginDir() {
|
|
|
6596
6715
|
candidates.push(import_node_path11.default.join(dirname, "..", "plugins"));
|
|
6597
6716
|
}
|
|
6598
6717
|
for (const dir of candidates) {
|
|
6599
|
-
if ((0,
|
|
6718
|
+
if ((0, import_node_fs4.existsSync)(dir)) {
|
|
6600
6719
|
process.env.ADA_PLUGIN_DIR = dir;
|
|
6601
6720
|
return;
|
|
6602
6721
|
}
|
|
@@ -6752,7 +6871,7 @@ function parseServerEndpoint(serverUrl) {
|
|
|
6752
6871
|
}
|
|
6753
6872
|
function commandAvailable(command) {
|
|
6754
6873
|
if (import_node_path14.default.isAbsolute(command)) {
|
|
6755
|
-
return (0,
|
|
6874
|
+
return (0, import_node_fs5.existsSync)(command);
|
|
6756
6875
|
}
|
|
6757
6876
|
const checker = process.platform === "win32" ? "where.exe" : "which";
|
|
6758
6877
|
const checked = (0, import_node_child_process5.spawnSync)(checker, [command], {
|
|
@@ -6796,7 +6915,7 @@ function resolveAppiumNodeEntrypoint() {
|
|
|
6796
6915
|
import_node_path14.default.join(process.cwd(), "..", "..", "node_modules", "appium", "build", "lib", "main.js")
|
|
6797
6916
|
];
|
|
6798
6917
|
for (const p of candidates) {
|
|
6799
|
-
if ((0,
|
|
6918
|
+
if ((0, import_node_fs5.existsSync)(p)) {
|
|
6800
6919
|
return p;
|
|
6801
6920
|
}
|
|
6802
6921
|
}
|
|
@@ -6815,11 +6934,11 @@ function loadPersistedHomes() {
|
|
|
6815
6934
|
import_node_path14.default.join(process.cwd(), "..", ".ada-agent", "deps-install-state.json")
|
|
6816
6935
|
];
|
|
6817
6936
|
for (const file of candidates) {
|
|
6818
|
-
if (!(0,
|
|
6937
|
+
if (!(0, import_node_fs5.existsSync)(file)) {
|
|
6819
6938
|
continue;
|
|
6820
6939
|
}
|
|
6821
6940
|
try {
|
|
6822
|
-
const raw = (0,
|
|
6941
|
+
const raw = (0, import_node_fs5.readFileSync)(file, "utf8");
|
|
6823
6942
|
const parsed = JSON.parse(raw);
|
|
6824
6943
|
const androidHome = typeof parsed.androidHome === "string" ? parsed.androidHome.trim() : "";
|
|
6825
6944
|
const appiumHome = typeof parsed.appiumHome === "string" ? parsed.appiumHome.trim() : "";
|
|
@@ -6852,11 +6971,11 @@ function resolveAndroidSdkRoot() {
|
|
|
6852
6971
|
process.env.USERPROFILE ? import_node_path14.default.join(process.env.USERPROFILE, "AppData", "Local", "Android", "Sdk") : null
|
|
6853
6972
|
].map((v) => typeof v === "string" ? v.trim() : "").filter(Boolean);
|
|
6854
6973
|
for (const sdkRoot of candidates) {
|
|
6855
|
-
if (persisted.androidHome && sdkRoot === persisted.androidHome && (0,
|
|
6974
|
+
if (persisted.androidHome && sdkRoot === persisted.androidHome && (0, import_node_fs5.existsSync)(sdkRoot)) {
|
|
6856
6975
|
return sdkRoot;
|
|
6857
6976
|
}
|
|
6858
6977
|
const platformTools = import_node_path14.default.join(sdkRoot, "platform-tools");
|
|
6859
|
-
if ((0,
|
|
6978
|
+
if ((0, import_node_fs5.existsSync)(platformTools)) {
|
|
6860
6979
|
return sdkRoot;
|
|
6861
6980
|
}
|
|
6862
6981
|
}
|
|
@@ -6867,11 +6986,11 @@ function getAppiumExtensionsFile(homeDir) {
|
|
|
6867
6986
|
}
|
|
6868
6987
|
function hasAppiumDriver(homeDir, driverName) {
|
|
6869
6988
|
const file = getAppiumExtensionsFile(homeDir);
|
|
6870
|
-
if (!(0,
|
|
6989
|
+
if (!(0, import_node_fs5.existsSync)(file)) {
|
|
6871
6990
|
return false;
|
|
6872
6991
|
}
|
|
6873
6992
|
try {
|
|
6874
|
-
const text = (0,
|
|
6993
|
+
const text = (0, import_node_fs5.readFileSync)(file, "utf8");
|
|
6875
6994
|
return text.toLowerCase().includes(driverName.toLowerCase());
|
|
6876
6995
|
} catch {
|
|
6877
6996
|
return false;
|
|
@@ -6894,7 +7013,7 @@ function resolveAppiumHome(platform) {
|
|
|
6894
7013
|
}
|
|
6895
7014
|
}
|
|
6896
7015
|
for (const candidate of uniq) {
|
|
6897
|
-
if ((0,
|
|
7016
|
+
if ((0, import_node_fs5.existsSync)(getAppiumExtensionsFile(candidate))) {
|
|
6898
7017
|
return candidate;
|
|
6899
7018
|
}
|
|
6900
7019
|
}
|
|
@@ -8358,7 +8477,7 @@ async function startMcpServer() {
|
|
|
8358
8477
|
mcpServers: {
|
|
8359
8478
|
"ada-mcp": {
|
|
8360
8479
|
command: "pnpm",
|
|
8361
|
-
args: ["dlx", "@ada-mcp/launcher@0.1.
|
|
8480
|
+
args: ["dlx", "@ada-mcp/launcher@0.1.3"]
|
|
8362
8481
|
}
|
|
8363
8482
|
}
|
|
8364
8483
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ada-mcp/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "ADA MCP server for web/mobile automation (stdio + remote HTTP)",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "commonjs",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"express": "^5.2.1",
|
|
36
36
|
"jimp": "^1.6.0",
|
|
37
37
|
"js-yaml": "^4.1.0",
|
|
38
|
-
"playwright": "
|
|
38
|
+
"playwright": "1.59.1",
|
|
39
39
|
"selenium-webdriver": "^4.34.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
@@ -62,7 +62,11 @@ async function main() {
|
|
|
62
62
|
console.error(`[ada-mcp preinstall] registry ${candidate} -> ${latency === null ? "fail" : `${latency}ms`}`);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
/** preinstall 阶段依赖尚未安装,无法校验浏览器包是否存在;仅测官方 CDN,避免写入国内镜像后 404 */
|
|
66
|
+
const pwCandidates = [
|
|
67
|
+
"https://cdn.playwright.dev",
|
|
68
|
+
"https://playwright.azureedge.net"
|
|
69
|
+
];
|
|
66
70
|
const pw = await detectBestPlaywrightHost(pwCandidates);
|
|
67
71
|
const hostFile = path.join(root, ".ada-mcp-playwright-host");
|
|
68
72
|
fs.writeFileSync(hostFile, `${pw.best}\n`, "utf8");
|