@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 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.2`** 拉起本包(见 [launcher README](../ada-mcp-launcher/README.md)):
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.2"]
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.6`**(由 launcher 默认拉取)。
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.6
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.2", "--install-deps=all"]
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.2`。
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 import_node_fs2 = __toESM(require("node:fs"));
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: ["chrome"] # chromium | chrome | msedge | firefox | webkit | all\r\n playwrightDownloadHost: "https://npmmirror.com/mirrors/playwright"\r\n npmRegistryCandidates:\r\n - "https://registry.npmmirror.com"\r\n - "https://mirrors.cloud.tencent.com/npm"\r\n - "https://repo.huaweicloud.com/repository/npm"\r\n - "https://registry.npmjs.org"\r\n playwrightHostCandidates:\r\n - "https://npmmirror.com/mirrors/playwright"\r\n - "https://playwright.azureedge.net"\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';
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: ["chrome"],
2789
- playwrightDownloadHost: "https://npmmirror.com/mirrors/playwright",
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://playwright.azureedge.net"
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 ?? "https://registry.npmmirror.com";
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
- const raw = process.env.ADA_INSTALL_STRATEGY_TIMEOUT_MS;
3558
- const parsed = raw ? Number(raw) : 2e4;
3559
- return Number.isFinite(parsed) && parsed > 0 ? parsed : 2e4;
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 configured = Array.isArray(config.dependencies.npmRegistryCandidates) ? config.dependencies.npmRegistryCandidates.map((x) => normalizeRegistryUrl(String(x).trim())).filter(Boolean) : [];
3683
- const fallback = "https://registry.npmjs.org";
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
- return Array.from(/* @__PURE__ */ new Set([primary, ...configured, ...extra, fallback]));
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
- if (latency < bestLatency) {
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 configuredCandidates = Array.isArray(config.dependencies.playwrightHostCandidates) ? config.dependencies.playwrightHostCandidates.map((x) => normalizeHostUrl(String(x).trim())).filter(Boolean) : [];
3750
- const fallback = "https://playwright.azureedge.net";
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
- return Array.from(/* @__PURE__ */ new Set([configured, ...configuredCandidates, ...extra, fallback]));
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
- async function probeHostLatency(host) {
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(host, {
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 probeHostLatency(candidate);
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 = normalizeHostUrl(playwrightDownloadHost(config));
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
- if (latency < bestLatency) {
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 targets = playwrightInstallTargets(config);
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 args = targets.length > 0 ? ["exec", "playwright", "install", ...targets] : ["exec", "playwright", "install"];
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] \u4E0B\u8F7D\u6D4F\u89C8\u5668\uFF08\u955C\u50CF ${selectedHost}\uFF09\uFF0C\u76EE\u6807: ${targets.length ? targets.join(",") : "all"}`);
3938
- await runCommand2("npm", args, {
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: installStrategyTimeoutMs(),
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 import_node_fs = __toESM(require("node:fs"), 1);
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 (!import_node_fs.default.existsSync(pluginDir)) {
4625
+ if (!import_node_fs2.default.existsSync(pluginDir)) {
4520
4626
  return [];
4521
4627
  }
4522
- const entries = import_node_fs.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));
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: ["chrome"] };
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 = import_node_fs2.default.readFileSync(file, "utf8").trim();
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 import_node_fs4 = require("node:fs");
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 import_node_fs3 = require("node:fs");
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, import_node_fs3.existsSync)(dir)) {
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, import_node_fs4.existsSync)(command);
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, import_node_fs4.existsSync)(p)) {
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, import_node_fs4.existsSync)(file)) {
6937
+ if (!(0, import_node_fs5.existsSync)(file)) {
6819
6938
  continue;
6820
6939
  }
6821
6940
  try {
6822
- const raw = (0, import_node_fs4.readFileSync)(file, "utf8");
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, import_node_fs4.existsSync)(sdkRoot)) {
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, import_node_fs4.existsSync)(platformTools)) {
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, import_node_fs4.existsSync)(file)) {
6989
+ if (!(0, import_node_fs5.existsSync)(file)) {
6871
6990
  return false;
6872
6991
  }
6873
6992
  try {
6874
- const text = (0, import_node_fs4.readFileSync)(file, "utf8");
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, import_node_fs4.existsSync)(getAppiumExtensionsFile(candidate))) {
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.2"]
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.7",
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": "^1.59.1",
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
- const pwCandidates = playwrightHostCandidateList();
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");