@ada-mcp/mcp-server 0.1.17 → 0.1.19

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,27 +8,27 @@ ADA MCP server package that supports:
8
8
 
9
9
  ## 标准安装(Cursor / MCP)
10
10
 
11
- 请使用 **`@ada-mcp/launcher@0.1.14`** 拉起本包(见 [launcher README](../ada-mcp-launcher/README.md)):
11
+ 请使用 **`@ada-mcp/launcher@0.1.19`** 拉起本包(见 [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.14"]
18
+ "args": ["dlx", "@ada-mcp/launcher@0.1.19"]
19
19
  }
20
20
  }
21
21
  }
22
22
  ```
23
23
 
24
- 本包版本:**`@ada-mcp/mcp-server@0.1.17`**(由 launcher 默认拉取;依赖锁定 `playwright@1.59.1`)。
24
+ 本包版本:**`@ada-mcp/mcp-server@0.1.19`**(由 launcher 默认拉取;依赖锁定 `playwright@1.59.1`)。
25
25
 
26
26
  直接调试本包(无 launcher 拉包前测速):
27
27
 
28
28
  ```bash
29
- pnpm dlx @ada-mcp/mcp-server@0.1.17
29
+ pnpm dlx @ada-mcp/mcp-server@0.1.19
30
30
  # npx 等价:
31
- npx -y @ada-mcp/mcp-server@0.1.17
31
+ npx -y @ada-mcp/mcp-server@0.1.19
32
32
  ```
33
33
 
34
34
  ## 启动时自动安装依赖(默认仅 Playwright)
@@ -83,27 +83,27 @@ npx -y @ada-mcp/mcp-server@0.1.17
83
83
  在标准 `args` 后追加,例如安装全部依赖:
84
84
 
85
85
  ```json
86
- "args": ["dlx", "@ada-mcp/launcher@0.1.14", "--install-deps=all"]
86
+ "args": ["dlx", "@ada-mcp/launcher@0.1.19", "--install-deps=all"]
87
87
  ```
88
88
 
89
89
  ## Cursor MCP 配置
90
90
 
91
- **pnpm(推荐)**:`pnpm` + `dlx @ada-mcp/launcher@0.1.14`
91
+ **pnpm(推荐)**:`pnpm` + `dlx @ada-mcp/launcher@0.1.19`
92
92
 
93
- **npx 等价**(`launcher@0.1.7+`):`npx` + `-y @ada-mcp/launcher@0.1.14`(内层同样 `npx -y` mcp-server,测速逻辑与 pnpm 一致)
93
+ **npx 等价**(`launcher@0.1.7+`):`npx` + `-y @ada-mcp/launcher@0.1.19`(内层同样 `npx -y` mcp-server,测速逻辑与 pnpm 一致)
94
94
 
95
95
  ```json
96
96
  {
97
97
  "mcpServers": {
98
98
  "ada-mcp": {
99
99
  "command": "npx",
100
- "args": ["-y", "@ada-mcp/launcher@0.1.14"]
100
+ "args": ["-y", "@ada-mcp/launcher@0.1.19"]
101
101
  }
102
102
  }
103
103
  }
104
104
  ```
105
105
 
106
- Windows 若找不到 `pnpm`,可将 `command` 改为 `pnpm.cmd` 绝对路径;无 pnpm 时只能直接 `npx -y @ada-mcp/mcp-server@0.1.17`(无 launcher 拉包测速)。
106
+ Windows 若找不到 `pnpm`,可将 `command` 改为 `pnpm.cmd` 绝对路径;无 pnpm 时只能直接 `npx -y @ada-mcp/mcp-server@0.1.19`(无 launcher 拉包测速)。
107
107
 
108
108
  ## Remote mode
109
109
 
package/dist/cli.cjs CHANGED
@@ -3671,7 +3671,132 @@ function playwrightInstallPackageSpec() {
3671
3671
  return `playwright@${fromEnv || PINNED_PLAYWRIGHT_VERSION}`;
3672
3672
  }
3673
3673
  function resolveInstallPackageSpecs(packages) {
3674
- return packages.map((pkg) => pkg === "playwright" ? playwrightInstallPackageSpec() : pkg);
3674
+ const out = [];
3675
+ for (const pkg of packages) {
3676
+ if (pkg === "playwright") {
3677
+ out.push(playwrightInstallPackageSpec());
3678
+ continue;
3679
+ }
3680
+ if (pkg === "zod") {
3681
+ const fromEnv = process.env.ADA_ZOD_VERSION?.trim();
3682
+ out.push(`zod@${fromEnv || PINNED_ZOD_VERSION}`);
3683
+ continue;
3684
+ }
3685
+ out.push(pkg);
3686
+ }
3687
+ return out;
3688
+ }
3689
+ function skipConflictRemoval() {
3690
+ const v = process.env.ADA_DEPS_SKIP_CONFLICT_REMOVAL?.trim().toLowerCase();
3691
+ return v === "1" || v === "true" || v === "yes";
3692
+ }
3693
+ function readInstalledPackageVersion(packageName) {
3694
+ try {
3695
+ const pkgPath = require2.resolve(`${packageName}/package.json`);
3696
+ const raw = (0, import_node_fs.readFileSync)(pkgPath, "utf8");
3697
+ return String(JSON.parse(raw).version ?? "").trim() || void 0;
3698
+ } catch {
3699
+ return void 0;
3700
+ }
3701
+ }
3702
+ function zodExportsV3Subpath(version2) {
3703
+ try {
3704
+ const pkgPath = require2.resolve("zod/package.json");
3705
+ const raw = (0, import_node_fs.readFileSync)(pkgPath, "utf8");
3706
+ const exportsField = JSON.parse(raw).exports;
3707
+ if (exportsField && "./v3" in exportsField) {
3708
+ return true;
3709
+ }
3710
+ } catch {
3711
+ }
3712
+ const major = Number(version2.split(".")[0]);
3713
+ const minor = Number(version2.split(".")[1]);
3714
+ if (major > 3) {
3715
+ return true;
3716
+ }
3717
+ return major === 3 && minor >= 25;
3718
+ }
3719
+ function detectWorkspacePackageConflicts(packages) {
3720
+ const conflicts = [];
3721
+ const expectedPlaywright = process.env.ADA_PLAYWRIGHT_VERSION?.trim() || PINNED_PLAYWRIGHT_VERSION;
3722
+ const expectedZod = process.env.ADA_ZOD_VERSION?.trim() || PINNED_ZOD_VERSION;
3723
+ if (packages.includes("playwright") && hasPackage("playwright")) {
3724
+ const installed = readInstalledPackageVersion("playwright");
3725
+ if (installed && installed !== expectedPlaywright) {
3726
+ conflicts.push({
3727
+ name: "playwright",
3728
+ installed,
3729
+ expected: expectedPlaywright,
3730
+ reason: "\u4E0E ADA \u9501\u5B9A\u7684 Playwright \u6D4F\u89C8\u5668\u5305\u7248\u672C\u4E0D\u4E00\u81F4"
3731
+ });
3732
+ }
3733
+ }
3734
+ if (hasPackage("@modelcontextprotocol/sdk") && hasPackage("zod")) {
3735
+ const zodVer = readInstalledPackageVersion("zod");
3736
+ if (zodVer && !zodExportsV3Subpath(zodVer)) {
3737
+ conflicts.push({
3738
+ name: "zod",
3739
+ installed: zodVer,
3740
+ expected: expectedZod,
3741
+ reason: "\u5F53\u524D zod \u4E0D\u652F\u6301 zod/v3\uFF0C\u4E0E @modelcontextprotocol/sdk \u51B2\u7A81"
3742
+ });
3743
+ }
3744
+ }
3745
+ return conflicts;
3746
+ }
3747
+ function packagesToRemoveForConflicts(conflicts) {
3748
+ const names = /* @__PURE__ */ new Set();
3749
+ for (const c of conflicts) {
3750
+ names.add(c.name);
3751
+ if (c.name === "playwright") {
3752
+ names.add("playwright-core");
3753
+ }
3754
+ }
3755
+ return Array.from(names);
3756
+ }
3757
+ async function removeWorkspacePackages(names, onLogLine) {
3758
+ const unique = Array.from(new Set(names)).filter(Boolean);
3759
+ if (unique.length === 0) {
3760
+ return true;
3761
+ }
3762
+ const timeoutMs = installStrategyTimeoutMs();
3763
+ const attempts = [
3764
+ { tool: "pnpm", args: ["remove", ...unique] },
3765
+ { tool: "npm", args: ["uninstall", ...unique] }
3766
+ ];
3767
+ for (const { tool, args } of attempts) {
3768
+ try {
3769
+ await runCommand2(tool, args, { timeoutMs, onLogLine });
3770
+ return true;
3771
+ } catch {
3772
+ }
3773
+ }
3774
+ return false;
3775
+ }
3776
+ async function reconcileWorkspacePackageConflicts(packages, onLogLine) {
3777
+ if (skipConflictRemoval()) {
3778
+ return [];
3779
+ }
3780
+ const conflicts = detectWorkspacePackageConflicts(packages);
3781
+ if (conflicts.length === 0) {
3782
+ return [];
3783
+ }
3784
+ for (const c of conflicts) {
3785
+ onLogLine?.(`[deps] \u68C0\u6D4B\u5230\u51B2\u7A81 ${c.name}@${c.installed}\uFF0C\u76EE\u6807 ${c.expected}\uFF08${c.reason}\uFF09`);
3786
+ }
3787
+ const toRemove = packagesToRemoveForConflicts(conflicts);
3788
+ onLogLine?.(`[deps] \u6B63\u5728\u5378\u8F7D: ${toRemove.join(", ")}`);
3789
+ const removed = await removeWorkspacePackages(toRemove, onLogLine);
3790
+ if (!removed) {
3791
+ onLogLine?.("[deps][warn] \u81EA\u52A8\u5378\u8F7D\u672A\u5B8C\u6210\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: pnpm remove " + toRemove.join(" "));
3792
+ } else {
3793
+ onLogLine?.("[deps] \u51B2\u7A81\u5305\u5DF2\u5378\u8F7D\uFF0C\u5C06\u5B89\u88C5\u9501\u5B9A\u7248\u672C");
3794
+ }
3795
+ const extraInstall = [];
3796
+ if (conflicts.some((c) => c.name === "zod") && hasPackage("@modelcontextprotocol/sdk")) {
3797
+ extraInstall.push("zod");
3798
+ }
3799
+ return extraInstall;
3675
3800
  }
3676
3801
  function shouldUseShell(command) {
3677
3802
  if (process.platform !== "win32") {
@@ -3693,7 +3818,14 @@ function briefErrorMessage(error2) {
3693
3818
  }
3694
3819
  return String(error2).split(/\r?\n/)[0]?.trim() || String(error2);
3695
3820
  }
3821
+ function depsVerboseEnabled() {
3822
+ return isTruthyEnv("ADA_DEPS_VERBOSE");
3823
+ }
3696
3824
  function shouldEmitPlaywrightCliLine(line) {
3825
+ if (depsVerboseEnabled()) {
3826
+ const t2 = line.trim();
3827
+ return t2.length > 0 && !/^\s*at\s/.test(t2) && !t2.includes("processTicksAndRejections");
3828
+ }
3697
3829
  const t = line.trim();
3698
3830
  if (!t) {
3699
3831
  return false;
@@ -3723,7 +3855,8 @@ function summarizePlaywrightCliLine(line) {
3723
3855
  return "[playwright][warn] \u5F53\u524D\u955C\u50CF\u672A\u5B89\u88C5\u6210\u529F";
3724
3856
  }
3725
3857
  if (/^Progress:/i.test(t)) {
3726
- return null;
3858
+ const pct = t.match(/(\d{1,3})%/)?.[1];
3859
+ return pct ? `[playwright] \u4E0B\u8F7D\u8FDB\u5EA6 ${pct}%` : "[playwright] \u4E0B\u8F7D\u4E2D\u2026";
3727
3860
  }
3728
3861
  if (t.length > 200) {
3729
3862
  return `[playwright] ${t.slice(0, 120)}\u2026`;
@@ -3735,15 +3868,27 @@ function createPlaywrightInstallLogSink(onLogLine) {
3735
3868
  return void 0;
3736
3869
  }
3737
3870
  const seen = /* @__PURE__ */ new Set();
3871
+ let lastProgressEmitAt = 0;
3738
3872
  return (line) => {
3739
3873
  if (!shouldEmitPlaywrightCliLine(line)) {
3740
3874
  return;
3741
3875
  }
3876
+ if (depsVerboseEnabled()) {
3877
+ onLogLine(line.trimEnd());
3878
+ return;
3879
+ }
3742
3880
  const summary = summarizePlaywrightCliLine(line);
3743
3881
  if (!summary) {
3744
3882
  return;
3745
3883
  }
3746
- if (seen.has(summary)) {
3884
+ const isProgress = /^Progress:/i.test(line.trim());
3885
+ if (isProgress) {
3886
+ const now = Date.now();
3887
+ if (now - lastProgressEmitAt < 12e3) {
3888
+ return;
3889
+ }
3890
+ lastProgressEmitAt = now;
3891
+ } else if (seen.has(summary)) {
3747
3892
  return;
3748
3893
  }
3749
3894
  seen.add(summary);
@@ -3920,7 +4065,13 @@ function installStrategyTimeoutMs() {
3920
4065
  return parsePositiveMs(process.env.ADA_INSTALL_STRATEGY_TIMEOUT_MS, 12e4);
3921
4066
  }
3922
4067
  function playwrightInstallTimeoutMs() {
3923
- return parsePositiveMs(process.env.ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS, 9e5);
4068
+ return parsePositiveMs(process.env.ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS, 18e5);
4069
+ }
4070
+ function playwrightInstallAttemptTimeoutMs(attemptIndex, baseTimeoutMs) {
4071
+ if (attemptIndex <= 0) {
4072
+ return baseTimeoutMs;
4073
+ }
4074
+ return Math.min(baseTimeoutMs, 6e5);
3924
4075
  }
3925
4076
  function majorOf(versionLike) {
3926
4077
  const text = versionLike.trim().replace(/^v/i, "");
@@ -4024,12 +4175,73 @@ function stepMeta(stage) {
4024
4175
  stepTotal: PROGRESS_STEPS.length
4025
4176
  };
4026
4177
  }
4178
+ function formatProgressDetail(stage, details) {
4179
+ if (!details) {
4180
+ return null;
4181
+ }
4182
+ if (stage === "registry.probe.result") {
4183
+ const candidate = String(details.candidate ?? "");
4184
+ const latency = details.latencyMs;
4185
+ return `[deps] npm \u955C\u50CF ${candidate} -> ${latency === null || latency === void 0 ? "fail" : `${latency}ms`}`;
4186
+ }
4187
+ if (stage === "playwright.host.probe.result") {
4188
+ const candidate = String(details.candidate ?? "");
4189
+ const latency = details.latencyMs;
4190
+ const artifactOk = details.artifactOk === true;
4191
+ const tail = artifactOk ? "\uFF0C\u6D4F\u89C8\u5668\u5305\u53EF\u4E0B\u8F7D" : latency === null || latency === void 0 ? "" : "\uFF0C\u4EC5\u8FDE\u901A";
4192
+ return `[playwright] CDN ${candidate} -> ${latency === null || latency === void 0 ? "fail" : `${latency}ms`}${tail}`;
4193
+ }
4194
+ if (stage === "packages.install.done") {
4195
+ const strategy = String(details.strategy ?? "");
4196
+ return strategy ? `[deps] \u5305\u5B89\u88C5\u5B8C\u6210\uFF08${strategy}\uFF09` : null;
4197
+ }
4198
+ if (stage === "playwright.browser.install.done") {
4199
+ const host = String(details.selectedHost ?? "");
4200
+ const attempt = details.attempt;
4201
+ return host ? `[playwright] \u6D4F\u89C8\u5668\u5B89\u88C5\u5B8C\u6210\uFF08${host}${attempt ? `\uFF0C\u7B2C ${attempt} \u4E2A\u955C\u50CF` : ""}\uFF09` : null;
4202
+ }
4203
+ return null;
4204
+ }
4205
+ function structuredLogToHuman(level, payload) {
4206
+ const { event, details } = payload;
4207
+ const d = details && typeof details === "object" ? details : {};
4208
+ if (event === "deps.playwright.browser.install.host.fail") {
4209
+ return `[playwright][warn] \u955C\u50CF ${d.host} \u5931\u8D25 (${d.attempt}): ${d.message}`;
4210
+ }
4211
+ if (event === "deps.install.strategy.try") {
4212
+ return `[deps] \u5C1D\u8BD5\u5B89\u88C5\u7B56\u7565: ${d.strategy}`;
4213
+ }
4214
+ if (event === "deps.install.strategy.ok") {
4215
+ return `[deps] \u5305\u5B89\u88C5\u6210\u529F: ${d.strategy}`;
4216
+ }
4217
+ if (event === "deps.install.strategy.fail") {
4218
+ return `[deps][warn] \u5B89\u88C5\u7B56\u7565\u5931\u8D25 (${d.strategy}): ${d.message}`;
4219
+ }
4220
+ if (event === "deps.registry.auto-selected") {
4221
+ return `[deps] \u9009\u7528 npm \u955C\u50CF: ${d.selected}`;
4222
+ }
4223
+ if (event === "deps.playwright.host.auto-selected") {
4224
+ return `[playwright] CDN \u6D4B\u901F\u6392\u5E8F: ${Array.isArray(d.ranked) ? d.ranked.join(" -> ") : d.selected}`;
4225
+ }
4226
+ if (level === "warn" || level === "error") {
4227
+ const msg = d.message ?? d.detail;
4228
+ return `[deps][${level}] ${event}${msg ? `: ${msg}` : ""}`;
4229
+ }
4230
+ if (depsVerboseEnabled()) {
4231
+ return `[deps] ${event}${Object.keys(d).length > 0 ? ` ${JSON.stringify(d)}` : ""}`;
4232
+ }
4233
+ return null;
4234
+ }
4027
4235
  function progress(stage, details) {
4028
4236
  if (depsHumanLog) {
4029
4237
  const label = PROGRESS_HUMAN_LABELS[stage];
4030
4238
  if (label) {
4031
4239
  depsHumanLog(label);
4032
4240
  }
4241
+ const detail = formatProgressDetail(stage, details);
4242
+ if (detail) {
4243
+ depsHumanLog(detail);
4244
+ }
4033
4245
  return;
4034
4246
  }
4035
4247
  const meta3 = stepMeta(stage);
@@ -4044,6 +4256,10 @@ function progress(stage, details) {
4044
4256
  }
4045
4257
  function depsStructuredLog(level, payload) {
4046
4258
  if (depsHumanLog) {
4259
+ const human = structuredLogToHuman(level, payload);
4260
+ if (human) {
4261
+ depsHumanLog(human);
4262
+ }
4047
4263
  return;
4048
4264
  }
4049
4265
  log(level, payload);
@@ -4142,12 +4358,42 @@ function playwrightProbeUrls(host) {
4142
4358
  }
4143
4359
  return [h];
4144
4360
  }
4361
+ function isChinaFriendlyNpmRegistry(registry2) {
4362
+ const r = registry2.toLowerCase();
4363
+ return CHINA_NPM_REGISTRY_HINTS.some((hint) => r.includes(hint));
4364
+ }
4365
+ function isTruthyEnv(name) {
4366
+ const s = String(process.env[name] ?? "").trim().toLowerCase();
4367
+ return s === "1" || s === "true" || s === "yes" || s === "on";
4368
+ }
4369
+ function playwrightHostConfiguredByUser(config2) {
4370
+ if (process.env.ADA_PLAYWRIGHT_HOST_FROM_PREINSTALL === "1") {
4371
+ return "";
4372
+ }
4373
+ const raw = process.env.PLAYWRIGHT_DOWNLOAD_HOST?.trim() || playwrightDownloadHost(config2).trim();
4374
+ return raw ? normalizeHostUrl(raw) : "";
4375
+ }
4376
+ function preferPlaywrightHostsForNpmRegistry(ranked, npmRegistry) {
4377
+ if (!isTruthyEnv("ADA_PLAYWRIGHT_PREFER_CN_MIRROR") && !isChinaFriendlyNpmRegistry(npmRegistry)) {
4378
+ return ranked;
4379
+ }
4380
+ const seen = /* @__PURE__ */ new Set();
4381
+ const out = [];
4382
+ for (const url2 of [...CHINA_PLAYWRIGHT_HOST_PRIORITY, ...ranked]) {
4383
+ const n = normalizeHostUrl(url2);
4384
+ if (!seen.has(n)) {
4385
+ seen.add(n);
4386
+ out.push(n);
4387
+ }
4388
+ }
4389
+ return out;
4390
+ }
4145
4391
  function playwrightHostCandidates(config2) {
4146
- const configured = normalizeHostUrl(playwrightDownloadHost(config2));
4392
+ const configured = playwrightHostConfiguredByUser(config2);
4147
4393
  const fromConfig = Array.isArray(config2.dependencies.playwrightHostCandidates) ? config2.dependencies.playwrightHostCandidates.map((x) => normalizeHostUrl(String(x).trim())).filter(Boolean) : [];
4148
4394
  const configuredList = fromConfig.length > 0 ? fromConfig : [...DEFAULT_PLAYWRIGHT_HOST_CANDIDATES];
4149
4395
  const extra = process.env.ADA_PLAYWRIGHT_HOST_CANDIDATES ? process.env.ADA_PLAYWRIGHT_HOST_CANDIDATES.split(",").map((x) => normalizeHostUrl(x.trim())).filter(Boolean) : [];
4150
- const ordered = [configured, ...configuredList, ...extra];
4396
+ const ordered = configured ? [configured, ...configuredList, ...extra] : [...configuredList, ...extra];
4151
4397
  const seen = /* @__PURE__ */ new Set();
4152
4398
  const out = [];
4153
4399
  for (const url2 of ordered) {
@@ -4236,7 +4482,9 @@ async function rankPlaywrightHosts(config2) {
4236
4482
  return ranked.length > 0 ? ranked : [...candidates];
4237
4483
  }
4238
4484
  async function runInstallWithPriority(config2, packages, onLogLine) {
4239
- const specs = resolveInstallPackageSpecs(packages);
4485
+ const extraFromReconcile = await reconcileWorkspacePackageConflicts(packages, onLogLine);
4486
+ const allPackages = Array.from(/* @__PURE__ */ new Set([...packages, ...extraFromReconcile]));
4487
+ const specs = resolveInstallPackageSpecs(allPackages);
4240
4488
  const npmProxy = await detectBestRegistry(config2, npmProxyRegistry());
4241
4489
  const pnpmProxy = await detectBestRegistry(config2, pnpmProxyRegistry());
4242
4490
  progress("packages.install.start", { packages: specs, npmProxy, pnpmProxy });
@@ -4281,6 +4529,7 @@ async function runInstallWithPriority(config2, packages, onLogLine) {
4281
4529
  let lastError = void 0;
4282
4530
  for (const strategy of strategies) {
4283
4531
  try {
4532
+ onLogLine?.(`[deps] \u5C1D\u8BD5 ${strategy.name} \u5B89\u88C5\u5305\u2026`);
4284
4533
  depsStructuredLog("info", { event: "deps.install.strategy.try", details: { strategy: strategy.name, packages } });
4285
4534
  await strategy.run();
4286
4535
  depsStructuredLog("info", { event: "deps.install.strategy.ok", details: { strategy: strategy.name } });
@@ -4397,24 +4646,32 @@ async function installPlaywrightBrowser(config2, onLogLine, options) {
4397
4646
  if (targets.length > 0) {
4398
4647
  installArgs.push(...targets);
4399
4648
  }
4400
- const rankedHosts = await rankPlaywrightHosts(config2);
4401
- const timeoutMs = playwrightInstallTimeoutMs();
4402
- onLogLine?.(`[playwright] \u5B89\u88C5\u8D85\u65F6\u4E0A\u9650 ${Math.round(timeoutMs / 1e3)}s\uFF08\u53EF\u7528 ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS \u8C03\u6574\uFF09`);
4649
+ const npmRegistry = await detectBestRegistry(config2, npmProxyRegistry());
4650
+ let rankedHosts = await rankPlaywrightHosts(config2);
4651
+ rankedHosts = preferPlaywrightHostsForNpmRegistry(rankedHosts, npmRegistry);
4652
+ const baseTimeoutMs = playwrightInstallTimeoutMs();
4653
+ onLogLine?.(
4654
+ `[playwright] \u5B89\u88C5\u8D85\u65F6\uFF1A\u9996\u4E2A\u955C\u50CF ${Math.round(baseTimeoutMs / 1e3)}s\uFF0C\u540E\u7EED\u955C\u50CF\u81F3\u591A 600s\uFF08ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS \u53EF\u8C03\uFF09`
4655
+ );
4656
+ if (isChinaFriendlyNpmRegistry(npmRegistry) || isTruthyEnv("ADA_PLAYWRIGHT_PREFER_CN_MIRROR")) {
4657
+ onLogLine?.(`[playwright] \u56FD\u5185 npm \u6E90 ${npmRegistry}\uFF0C\u4F18\u5148 npmmirror Playwright CDN`);
4658
+ }
4403
4659
  let lastHost = "";
4404
4660
  for (let i = 0; i < rankedHosts.length; i++) {
4405
4661
  const host = rankedHosts[i];
4662
+ const attemptTimeoutMs = playwrightInstallAttemptTimeoutMs(i, baseTimeoutMs);
4406
4663
  lastHost = host;
4407
4664
  if (i > 0) {
4408
- onLogLine?.(`[playwright] \u6362\u955C\u50CF: ${host}`);
4665
+ onLogLine?.(`[playwright] \u6362\u955C\u50CF (${i + 1}/${rankedHosts.length}): ${host}`);
4409
4666
  } else {
4410
4667
  onLogLine?.(
4411
- `[playwright] playwright@${version2}\uFF0CCDN ${host}\uFF0C\u76EE\u6807 ${targets.length ? targets.join(",") : "chromium"}`
4668
+ `[playwright] playwright@${version2}\uFF0CCDN ${host}\uFF08${i + 1}/${rankedHosts.length}\uFF09\uFF0C\u76EE\u6807 ${targets.length ? targets.join(",") : "chromium"}`
4412
4669
  );
4413
4670
  }
4414
4671
  try {
4415
4672
  await runCommand2(command, installArgs, {
4416
4673
  env: { PLAYWRIGHT_DOWNLOAD_HOST: host },
4417
- timeoutMs,
4674
+ timeoutMs: attemptTimeoutMs,
4418
4675
  onLogLine: createPlaywrightInstallLogSink(onLogLine)
4419
4676
  });
4420
4677
  progress("playwright.browser.install.done", { selectedHost: host, attempt: i + 1 });
@@ -4433,7 +4690,7 @@ async function installPlaywrightBrowser(config2, onLogLine, options) {
4433
4690
  }
4434
4691
  }
4435
4692
  onLogLine?.(
4436
- `[playwright][warn] \u6D4F\u89C8\u5668\u672A\u5B89\u88C5\u5B8C\u6210\uFF08\u5DF2\u5C1D\u8BD5 ${rankedHosts.length} \u4E2A CDN\uFF09\u3002\u53EF\u8BBE\u7F6E PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.playwright.dev \u540E\u91CD\u8BD5\uFF0C\u6216\u6267\u884C: npx playwright@${PINNED_PLAYWRIGHT_VERSION} install chromium`
4693
+ `[playwright][warn] \u6D4F\u89C8\u5668\u672A\u5B89\u88C5\u5B8C\u6210\uFF08\u5DF2\u5C1D\u8BD5 ${rankedHosts.length} \u4E2A CDN\uFF09\u3002\u56FD\u5185\u5EFA\u8BAE: set PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright \u5E76\u589E\u5927 ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS=1800000\uFF1B\u6216\u624B\u52A8: npx playwright@${PINNED_PLAYWRIGHT_VERSION} install chromium`
4437
4694
  );
4438
4695
  if (lastHost) {
4439
4696
  onLogLine?.(`[playwright][warn] \u6700\u540E\u5C1D\u8BD5: ${lastHost}`);
@@ -4925,7 +5182,7 @@ async function getDependencyHealth(config2) {
4925
5182
  missingAppiumDrivers
4926
5183
  };
4927
5184
  }
4928
- var import_node_module, import_node_fs, import_node_child_process2, import_promises5, import_node_path5, require2, PINNED_PLAYWRIGHT_VERSION, depsHumanLog, DEFAULT_NPM_REGISTRY_CANDIDATES, detectedBestRegistryByKey, PROGRESS_STEPS, PROGRESS_HUMAN_LABELS, DEFAULT_PLAYWRIGHT_HOST_CANDIDATES, PLAYWRIGHT_HOST_FALLBACK, PW_INSTALL_TARGETS;
5185
+ var import_node_module, import_node_fs, import_node_child_process2, import_promises5, import_node_path5, require2, PINNED_PLAYWRIGHT_VERSION, PINNED_ZOD_VERSION, depsHumanLog, DEFAULT_NPM_REGISTRY_CANDIDATES, detectedBestRegistryByKey, PROGRESS_STEPS, PROGRESS_HUMAN_LABELS, DEFAULT_PLAYWRIGHT_HOST_CANDIDATES, CHINA_NPM_REGISTRY_HINTS, CHINA_PLAYWRIGHT_HOST_PRIORITY, PLAYWRIGHT_HOST_FALLBACK, PW_INSTALL_TARGETS;
4929
5186
  var init_dependency_installer = __esm({
4930
5187
  "../ada-agent/src/dependency-installer.ts"() {
4931
5188
  import_node_module = require("node:module");
@@ -4938,6 +5195,7 @@ var init_dependency_installer = __esm({
4938
5195
  init_src2();
4939
5196
  require2 = (0, import_node_module.createRequire)(import_node_path5.default.join(process.cwd(), "package.json"));
4940
5197
  PINNED_PLAYWRIGHT_VERSION = "1.59.1";
5198
+ PINNED_ZOD_VERSION = "3.25.76";
4941
5199
  DEFAULT_NPM_REGISTRY_CANDIDATES = [
4942
5200
  "https://registry.npmmirror.com",
4943
5201
  "https://mirrors.cloud.tencent.com/npm",
@@ -4958,9 +5216,16 @@ var init_dependency_installer = __esm({
4958
5216
  "deps.ensure.start": "[deps] \u5F00\u59CB\u68C0\u6D4B\u4F9D\u8D56",
4959
5217
  "registry.probe.start": "[deps] \u63A2\u6D4B npm \u955C\u50CF\u2026",
4960
5218
  "packages.install.start": "[deps] \u5B89\u88C5 npm \u5305\u2026",
5219
+ "packages.install.done": "[deps] npm \u5305\u5B89\u88C5\u5B8C\u6210",
4961
5220
  "playwright.host.probe.start": "[playwright] \u63A2\u6D4B\u6D4F\u89C8\u5668 CDN\u2026",
4962
5221
  "playwright.browser.install.start": "[playwright] \u5B89\u88C5\u6D4F\u89C8\u5668\u2026",
5222
+ "playwright.browser.install.done": "[playwright] \u6D4F\u89C8\u5668\u5B89\u88C5\u5B8C\u6210",
5223
+ "playwright.selfcheck.start": "[playwright] \u81EA\u68C0\u2026",
5224
+ "playwright.selfcheck.done": "[playwright] \u81EA\u68C0\u5B8C\u6210",
4963
5225
  "appium.driver.ensure.start": "[appium] \u5B89\u88C5\u9A71\u52A8\u2026",
5226
+ "appium.driver.ensure.done": "[appium] \u9A71\u52A8\u5B89\u88C5\u5B8C\u6210",
5227
+ "selenium.check.start": "[selenium] \u68C0\u6D4B\u539F\u751F\u9A71\u52A8\u2026",
5228
+ "selenium.check.done": "[selenium] \u68C0\u6D4B\u5B8C\u6210",
4964
5229
  "deps.ensure.done": "[deps] \u4F9D\u8D56\u68C0\u6D4B\u5B8C\u6210"
4965
5230
  };
4966
5231
  DEFAULT_PLAYWRIGHT_HOST_CANDIDATES = [
@@ -4969,6 +5234,11 @@ var init_dependency_installer = __esm({
4969
5234
  "https://npmmirror.com/mirrors/playwright",
4970
5235
  "https://cdn.npmmirror.com/binaries/playwright"
4971
5236
  ];
5237
+ CHINA_NPM_REGISTRY_HINTS = ["npmmirror.com", "tencent", "huaweicloud", "huawei.com"];
5238
+ CHINA_PLAYWRIGHT_HOST_PRIORITY = [
5239
+ "https://cdn.npmmirror.com/binaries/playwright",
5240
+ "https://npmmirror.com/mirrors/playwright"
5241
+ ];
4972
5242
  PLAYWRIGHT_HOST_FALLBACK = DEFAULT_PLAYWRIGHT_HOST_CANDIDATES[0];
4973
5243
  PW_INSTALL_TARGETS = /* @__PURE__ */ new Set([
4974
5244
  "chromium",
@@ -32848,7 +33118,9 @@ function resolveBootstrapInstallDeps(argv2) {
32848
33118
  }
32849
33119
  var PREINSTALL_PLAYWRIGHT_HOST_ALLOW = /* @__PURE__ */ new Set([
32850
33120
  "https://cdn.playwright.dev",
32851
- "https://playwright.azureedge.net"
33121
+ "https://playwright.azureedge.net",
33122
+ "https://cdn.npmmirror.com/binaries/playwright",
33123
+ "https://npmmirror.com/mirrors/playwright"
32852
33124
  ]);
32853
33125
  function normalizePlaywrightHost(url2) {
32854
33126
  return url2.replace(/\/$/, "");
@@ -32866,7 +33138,8 @@ function applyPreinstallPlaywrightHostFile() {
32866
33138
  const host = normalizePlaywrightHost(import_node_fs3.default.readFileSync(file2, "utf8").trim());
32867
33139
  if (host.length > 0 && PREINSTALL_PLAYWRIGHT_HOST_ALLOW.has(host)) {
32868
33140
  process.env.PLAYWRIGHT_DOWNLOAD_HOST = host;
32869
- console.error(`[ADA-MCP] using playwright CDN from ${file2}: ${host}`);
33141
+ process.env.ADA_PLAYWRIGHT_HOST_FROM_PREINSTALL = "1";
33142
+ console.error(`[ADA-MCP] using playwright CDN from ${file2}: ${host} (install-deps \u5C06\u91CD\u65B0\u6D4B\u901F\u6392\u5E8F)`);
32870
33143
  return;
32871
33144
  }
32872
33145
  if (host.length > 0) {
@@ -32884,7 +33157,7 @@ function ensureDefaultInstallTimeouts() {
32884
33157
  process.env.ADA_INSTALL_STRATEGY_TIMEOUT_MS = "120000";
32885
33158
  }
32886
33159
  if (!process.env.ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS?.trim()) {
32887
- process.env.ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS = "900000";
33160
+ process.env.ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS = "1800000";
32888
33161
  }
32889
33162
  }
32890
33163
  async function runBootstrapInstallDeps(argv2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ada-mcp/mcp-server",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "ADA MCP server for web/mobile automation (stdio + remote HTTP)",
5
5
  "private": false,
6
6
  "type": "commonjs",
@@ -62,11 +62,16 @@ async function main() {
62
62
  console.error(`[ada-mcp preinstall] registry ${candidate} -> ${latency === null ? "fail" : `${latency}ms`}`);
63
63
  }
64
64
 
65
- /** preinstall 阶段依赖尚未安装,无法校验浏览器包是否存在;仅测官方 CDN,避免写入国内镜像后 404 */
66
- const pwCandidates = [
67
- "https://cdn.playwright.dev",
68
- "https://playwright.azureedge.net"
69
- ];
65
+ const chinaRegistry = /npmmirror|tencent|huaweicloud|huawei\.com/i.test(reg.best);
66
+ /** preinstall playwright 包时无法 HEAD 浏览器包;国内 registry 时优先写入 npmmirror CDN */
67
+ const pwCandidates = chinaRegistry
68
+ ? [
69
+ "https://cdn.npmmirror.com/binaries/playwright",
70
+ "https://npmmirror.com/mirrors/playwright",
71
+ "https://cdn.playwright.dev",
72
+ "https://playwright.azureedge.net"
73
+ ]
74
+ : ["https://cdn.playwright.dev", "https://playwright.azureedge.net"];
70
75
  const pw = await detectBestPlaywrightHost(pwCandidates);
71
76
  const hostFile = path.join(root, ".ada-mcp-playwright-host");
72
77
  fs.writeFileSync(hostFile, `${pw.best}\n`, "utf8");