@ada-mcp/mcp-server 0.1.5 → 0.1.6

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
@@ -14,10 +14,10 @@ ADA MCP server package that supports:
14
14
  pnpm dlx @ada-mcp/launcher
15
15
  ```
16
16
 
17
- 或直接(`0.1.5+` 在同次 dlx 安装依赖时 `preinstall` 测速):
17
+ 或直接(`0.1.6+`:`preinstall` 测速 registry + Playwright CDN,写入 `.npmrc` / `.ada-mcp-playwright-host`):
18
18
 
19
19
  ```bash
20
- pnpm dlx @ada-mcp/mcp-server
20
+ pnpm dlx @ada-mcp/mcp-server@0.1.6
21
21
  ```
22
22
 
23
23
  也可用 npm:
@@ -114,14 +114,13 @@ npx -y @ada-mcp/mcp-server
114
114
  "ada-mcp": {
115
115
  "command": "pnpm",
116
116
  "args": ["dlx", "@ada-mcp/launcher"]
117
- ```
118
-
119
- 或 `["dlx", "@ada-mcp/mcp-server@0.1.5"]`(依赖安装走 preinstall 探测,无需 `npm_config_registry`)
120
117
  }
121
118
  }
122
119
  }
123
120
  ```
124
121
 
122
+ 或 `"args": ["dlx", "@ada-mcp/mcp-server@0.1.5"]`(同次 dlx 安装依赖时由 preinstall 测速,无需 `npm_config_registry`)。
123
+
125
124
  Windows 若找不到 `pnpm`,可写完整路径,例如 `C:\\Users\\<你>\\AppData\\Roaming\\npm\\pnpm.cmd`,或改用 npx:
126
125
 
127
126
  ```json
package/dist/cli.cjs CHANGED
@@ -23,6 +23,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  mod
24
24
  ));
25
25
 
26
+ // src/bootstrap-deps.ts
27
+ var import_node_fs2 = __toESM(require("node:fs"));
28
+ var import_node_path10 = __toESM(require("node:path"));
29
+
26
30
  // ../ada-agent/dist/config.js
27
31
  var import_promises2 = __toESM(require("node:fs/promises"), 1);
28
32
  var import_node_path2 = __toESM(require("node:path"), 1);
@@ -6514,6 +6518,26 @@ function resolveBootstrapInstallDeps(argv2) {
6514
6518
  if (nativeDir) extras.nativeDriversDir = nativeDir;
6515
6519
  return { skip: false, scopes, force, extras };
6516
6520
  }
6521
+ function applyPreinstallPlaywrightHostFile() {
6522
+ if (process.env.PLAYWRIGHT_DOWNLOAD_HOST?.trim()) {
6523
+ return;
6524
+ }
6525
+ const roots = [process.env.INIT_CWD, process.cwd()].filter(
6526
+ (x) => typeof x === "string" && x.trim().length > 0
6527
+ );
6528
+ for (const root of roots) {
6529
+ const file = import_node_path10.default.join(root, ".ada-mcp-playwright-host");
6530
+ try {
6531
+ const host = import_node_fs2.default.readFileSync(file, "utf8").trim();
6532
+ if (host.length > 0) {
6533
+ process.env.PLAYWRIGHT_DOWNLOAD_HOST = host;
6534
+ console.error(`[ADA-MCP] using playwright CDN from ${file}: ${host}`);
6535
+ return;
6536
+ }
6537
+ } catch {
6538
+ }
6539
+ }
6540
+ }
6517
6541
  function ensureDefaultInstallTimeouts() {
6518
6542
  if (!process.env.ADA_INSTALL_STRATEGY_TIMEOUT_MS?.trim()) {
6519
6543
  process.env.ADA_INSTALL_STRATEGY_TIMEOUT_MS = "120000";
@@ -6523,6 +6547,7 @@ function ensureDefaultInstallTimeouts() {
6523
6547
  }
6524
6548
  }
6525
6549
  async function runBootstrapInstallDeps(argv2) {
6550
+ applyPreinstallPlaywrightHostFile();
6526
6551
  ensureDefaultInstallTimeouts();
6527
6552
  const plan = resolveBootstrapInstallDeps(argv2);
6528
6553
  if (plan.skip) {
@@ -6542,9 +6567,9 @@ async function runBootstrapInstallDeps(argv2) {
6542
6567
 
6543
6568
  // src/main.ts
6544
6569
  var import_promises13 = __toESM(require("node:fs/promises"));
6545
- var import_node_fs3 = require("node:fs");
6570
+ var import_node_fs4 = require("node:fs");
6546
6571
  var import_node_net2 = __toESM(require("node:net"));
6547
- var import_node_path13 = __toESM(require("node:path"));
6572
+ var import_node_path14 = __toESM(require("node:path"));
6548
6573
  var import_node_child_process5 = require("node:child_process");
6549
6574
  var import_node_url4 = require("node:url");
6550
6575
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
@@ -6552,8 +6577,8 @@ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
6552
6577
  var import_types = require("@modelcontextprotocol/sdk/types.js");
6553
6578
 
6554
6579
  // src/executor.ts
6555
- var import_node_fs2 = require("node:fs");
6556
- var import_node_path10 = __toESM(require("node:path"));
6580
+ var import_node_fs3 = require("node:fs");
6581
+ var import_node_path11 = __toESM(require("node:path"));
6557
6582
  var import_node_url3 = require("node:url");
6558
6583
  var import_meta = {};
6559
6584
  function ensureBundledPluginDir() {
@@ -6562,16 +6587,16 @@ function ensureBundledPluginDir() {
6562
6587
  }
6563
6588
  const candidates = [];
6564
6589
  try {
6565
- const here = import_node_path10.default.dirname((0, import_node_url3.fileURLToPath)(import_meta.url));
6566
- candidates.push(import_node_path10.default.join(here, "..", "plugins"));
6590
+ const here = import_node_path11.default.dirname((0, import_node_url3.fileURLToPath)(import_meta.url));
6591
+ candidates.push(import_node_path11.default.join(here, "..", "plugins"));
6567
6592
  } catch {
6568
6593
  }
6569
6594
  const dirname = globalThis.__dirname;
6570
6595
  if (typeof dirname === "string") {
6571
- candidates.push(import_node_path10.default.join(dirname, "..", "plugins"));
6596
+ candidates.push(import_node_path11.default.join(dirname, "..", "plugins"));
6572
6597
  }
6573
6598
  for (const dir of candidates) {
6574
- if ((0, import_node_fs2.existsSync)(dir)) {
6599
+ if ((0, import_node_fs3.existsSync)(dir)) {
6575
6600
  process.env.ADA_PLUGIN_DIR = dir;
6576
6601
  return;
6577
6602
  }
@@ -6607,30 +6632,30 @@ async function closeAllSessions() {
6607
6632
 
6608
6633
  // src/config.ts
6609
6634
  var import_promises11 = __toESM(require("node:fs/promises"));
6610
- var import_node_path11 = __toESM(require("node:path"));
6635
+ var import_node_path12 = __toESM(require("node:path"));
6611
6636
 
6612
6637
  // src/bundled-config.generated.ts
6613
- var bundledDefaultConfigYaml2 = '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 # \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://mirrors.tuna.tsinghua.edu.cn/npm" # \u6E05\u534E\u5927\u5B66\r\n - "https://repo.huaweicloud.com/repository/npm" # \u534E\u4E3A\u4E91\r\n - "https://mirrors.163.com/npm" # \u7F51\u6613\r\n - "https://registry.npmjs.org" # \u5B98\u65B9\u515C\u5E95\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';
6638
+ var bundledDefaultConfigYaml2 = '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://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://mirrors.tuna.tsinghua.edu.cn/npm" # \u6E05\u534E\u5927\u5B66\r\n - "https://repo.huaweicloud.com/repository/npm" # \u534E\u4E3A\u4E91\r\n - "https://mirrors.163.com/npm" # \u7F51\u6613\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';
6614
6639
 
6615
6640
  // src/config.ts
6616
- var DEFAULT_CONFIG_RELATIVE2 = import_node_path11.default.join("config", "default.yaml");
6617
- var LOCAL_DATA_DIR2 = import_node_path11.default.join(".ada-agent");
6618
- var EFFECTIVE_CONFIG_FILE2 = import_node_path11.default.join(LOCAL_DATA_DIR2, "agent.config.yaml");
6641
+ var DEFAULT_CONFIG_RELATIVE2 = import_node_path12.default.join("config", "default.yaml");
6642
+ var LOCAL_DATA_DIR2 = import_node_path12.default.join(".ada-agent");
6643
+ var EFFECTIVE_CONFIG_FILE2 = import_node_path12.default.join(LOCAL_DATA_DIR2, "agent.config.yaml");
6619
6644
  async function resolveWorkspaceRoot4(startDir = process.cwd()) {
6620
6645
  return resolveWorkspaceRoot(DEFAULT_CONFIG_RELATIVE2, startDir);
6621
6646
  }
6622
6647
  async function loadAgentConfig(cwd = process.cwd()) {
6623
6648
  let root = await resolveWorkspaceRoot4(cwd);
6624
- const defaultPath = import_node_path11.default.join(root, DEFAULT_CONFIG_RELATIVE2);
6649
+ const defaultPath = import_node_path12.default.join(root, DEFAULT_CONFIG_RELATIVE2);
6625
6650
  let defaultRaw;
6626
6651
  try {
6627
6652
  defaultRaw = await import_promises11.default.readFile(defaultPath, "utf8");
6628
6653
  } catch {
6629
6654
  defaultRaw = bundledDefaultConfigYaml2;
6630
- root = import_node_path11.default.dirname(process.execPath);
6655
+ root = import_node_path12.default.dirname(process.execPath);
6631
6656
  }
6632
6657
  const defaultConfig2 = jsYaml.load(defaultRaw) ?? {};
6633
- const effectivePath = import_node_path11.default.join(root, EFFECTIVE_CONFIG_FILE2);
6658
+ const effectivePath = import_node_path12.default.join(root, EFFECTIVE_CONFIG_FILE2);
6634
6659
  try {
6635
6660
  const effectiveFile = await import_promises11.default.readFile(effectivePath, "utf8");
6636
6661
  const effectiveConfig = jsYaml.load(effectiveFile) ?? {};
@@ -6642,7 +6667,7 @@ async function loadAgentConfig(cwd = process.cwd()) {
6642
6667
 
6643
6668
  // src/monitoring.ts
6644
6669
  var import_promises12 = __toESM(require("node:fs/promises"));
6645
- var import_node_path12 = __toESM(require("node:path"));
6670
+ var import_node_path13 = __toESM(require("node:path"));
6646
6671
  var import_jimp2 = require("jimp");
6647
6672
  function getScreenshotPath2(result) {
6648
6673
  const value = result.data?.screenshot;
@@ -6650,9 +6675,9 @@ function getScreenshotPath2(result) {
6650
6675
  }
6651
6676
  function buildOutputPath(options, command) {
6652
6677
  if (options.groupBySession) {
6653
- return import_node_path12.default.join(options.outputDir, command.sessionId, `${command.requestId}.png`);
6678
+ return import_node_path13.default.join(options.outputDir, command.sessionId, `${command.requestId}.png`);
6654
6679
  }
6655
- return import_node_path12.default.join(options.outputDir, `${command.requestId}.png`);
6680
+ return import_node_path13.default.join(options.outputDir, `${command.requestId}.png`);
6656
6681
  }
6657
6682
  async function captureMcpMonitor(command, result, options, runCommand4) {
6658
6683
  if (!options.enabled) {
@@ -6682,7 +6707,7 @@ async function captureMcpMonitor(command, result, options, runCommand4) {
6682
6707
  return null;
6683
6708
  }
6684
6709
  const targetPath = buildOutputPath(options, command);
6685
- await import_promises12.default.mkdir(import_node_path12.default.dirname(targetPath), { recursive: true });
6710
+ await import_promises12.default.mkdir(import_node_path13.default.dirname(targetPath), { recursive: true });
6686
6711
  const image = await import_jimp2.Jimp.read(sourcePath);
6687
6712
  if (options.keepAspectRatio) {
6688
6713
  image.scaleToFit({ w: options.maxWidth, h: options.maxHeight });
@@ -6726,8 +6751,8 @@ function parseServerEndpoint(serverUrl) {
6726
6751
  return { host, port };
6727
6752
  }
6728
6753
  function commandAvailable(command) {
6729
- if (import_node_path13.default.isAbsolute(command)) {
6730
- return (0, import_node_fs3.existsSync)(command);
6754
+ if (import_node_path14.default.isAbsolute(command)) {
6755
+ return (0, import_node_fs4.existsSync)(command);
6731
6756
  }
6732
6757
  const checker = process.platform === "win32" ? "where.exe" : "which";
6733
6758
  const checked = (0, import_node_child_process5.spawnSync)(checker, [command], {
@@ -6766,12 +6791,12 @@ function spawnDetachedChecked(cmd, args, env) {
6766
6791
  }
6767
6792
  function resolveAppiumNodeEntrypoint() {
6768
6793
  const candidates = [
6769
- import_node_path13.default.join(process.cwd(), "node_modules", "appium", "build", "lib", "main.js"),
6770
- import_node_path13.default.join(process.cwd(), "..", "node_modules", "appium", "build", "lib", "main.js"),
6771
- import_node_path13.default.join(process.cwd(), "..", "..", "node_modules", "appium", "build", "lib", "main.js")
6794
+ import_node_path14.default.join(process.cwd(), "node_modules", "appium", "build", "lib", "main.js"),
6795
+ import_node_path14.default.join(process.cwd(), "..", "node_modules", "appium", "build", "lib", "main.js"),
6796
+ import_node_path14.default.join(process.cwd(), "..", "..", "node_modules", "appium", "build", "lib", "main.js")
6772
6797
  ];
6773
6798
  for (const p of candidates) {
6774
- if ((0, import_node_fs3.existsSync)(p)) {
6799
+ if ((0, import_node_fs4.existsSync)(p)) {
6775
6800
  return p;
6776
6801
  }
6777
6802
  }
@@ -6786,15 +6811,15 @@ function loadPersistedHomes() {
6786
6811
  return persistedHomesCache;
6787
6812
  }
6788
6813
  const candidates = [
6789
- import_node_path13.default.join(process.cwd(), ".ada-agent", "deps-install-state.json"),
6790
- import_node_path13.default.join(process.cwd(), "..", ".ada-agent", "deps-install-state.json")
6814
+ import_node_path14.default.join(process.cwd(), ".ada-agent", "deps-install-state.json"),
6815
+ import_node_path14.default.join(process.cwd(), "..", ".ada-agent", "deps-install-state.json")
6791
6816
  ];
6792
6817
  for (const file of candidates) {
6793
- if (!(0, import_node_fs3.existsSync)(file)) {
6818
+ if (!(0, import_node_fs4.existsSync)(file)) {
6794
6819
  continue;
6795
6820
  }
6796
6821
  try {
6797
- const raw = (0, import_node_fs3.readFileSync)(file, "utf8");
6822
+ const raw = (0, import_node_fs4.readFileSync)(file, "utf8");
6798
6823
  const parsed = JSON.parse(raw);
6799
6824
  const androidHome = typeof parsed.androidHome === "string" ? parsed.androidHome.trim() : "";
6800
6825
  const appiumHome = typeof parsed.appiumHome === "string" ? parsed.appiumHome.trim() : "";
@@ -6817,36 +6842,36 @@ function resolveAndroidSdkRoot() {
6817
6842
  shell: false
6818
6843
  });
6819
6844
  const adbPath = (adbLookup.stdout ?? "").split(/\r?\n/).map((line) => line.trim()).find(Boolean);
6820
- const adbSdkRoot = adbPath ? import_node_path13.default.dirname(import_node_path13.default.dirname(adbPath)) : null;
6845
+ const adbSdkRoot = adbPath ? import_node_path14.default.dirname(import_node_path14.default.dirname(adbPath)) : null;
6821
6846
  const candidates = [
6822
6847
  process.env.ANDROID_SDK_ROOT,
6823
6848
  process.env.ANDROID_HOME,
6824
6849
  persisted.androidHome,
6825
6850
  adbSdkRoot,
6826
- process.env.LOCALAPPDATA ? import_node_path13.default.join(process.env.LOCALAPPDATA, "Android", "Sdk") : null,
6827
- process.env.USERPROFILE ? import_node_path13.default.join(process.env.USERPROFILE, "AppData", "Local", "Android", "Sdk") : null
6851
+ process.env.LOCALAPPDATA ? import_node_path14.default.join(process.env.LOCALAPPDATA, "Android", "Sdk") : null,
6852
+ process.env.USERPROFILE ? import_node_path14.default.join(process.env.USERPROFILE, "AppData", "Local", "Android", "Sdk") : null
6828
6853
  ].map((v) => typeof v === "string" ? v.trim() : "").filter(Boolean);
6829
6854
  for (const sdkRoot of candidates) {
6830
- if (persisted.androidHome && sdkRoot === persisted.androidHome && (0, import_node_fs3.existsSync)(sdkRoot)) {
6855
+ if (persisted.androidHome && sdkRoot === persisted.androidHome && (0, import_node_fs4.existsSync)(sdkRoot)) {
6831
6856
  return sdkRoot;
6832
6857
  }
6833
- const platformTools = import_node_path13.default.join(sdkRoot, "platform-tools");
6834
- if ((0, import_node_fs3.existsSync)(platformTools)) {
6858
+ const platformTools = import_node_path14.default.join(sdkRoot, "platform-tools");
6859
+ if ((0, import_node_fs4.existsSync)(platformTools)) {
6835
6860
  return sdkRoot;
6836
6861
  }
6837
6862
  }
6838
6863
  return null;
6839
6864
  }
6840
6865
  function getAppiumExtensionsFile(homeDir) {
6841
- return import_node_path13.default.join(homeDir, "node_modules", ".cache", "appium", "extensions.yaml");
6866
+ return import_node_path14.default.join(homeDir, "node_modules", ".cache", "appium", "extensions.yaml");
6842
6867
  }
6843
6868
  function hasAppiumDriver(homeDir, driverName) {
6844
6869
  const file = getAppiumExtensionsFile(homeDir);
6845
- if (!(0, import_node_fs3.existsSync)(file)) {
6870
+ if (!(0, import_node_fs4.existsSync)(file)) {
6846
6871
  return false;
6847
6872
  }
6848
6873
  try {
6849
- const text = (0, import_node_fs3.readFileSync)(file, "utf8");
6874
+ const text = (0, import_node_fs4.readFileSync)(file, "utf8");
6850
6875
  return text.toLowerCase().includes(driverName.toLowerCase());
6851
6876
  } catch {
6852
6877
  return false;
@@ -6859,7 +6884,7 @@ function resolveAppiumHome(platform) {
6859
6884
  persisted.appiumHome,
6860
6885
  process.cwd(),
6861
6886
  process.env.USERPROFILE,
6862
- process.env.USERPROFILE ? import_node_path13.default.join(process.env.USERPROFILE, ".appium") : null
6887
+ process.env.USERPROFILE ? import_node_path14.default.join(process.env.USERPROFILE, ".appium") : null
6863
6888
  ].map((v) => typeof v === "string" ? v.trim() : "").filter(Boolean);
6864
6889
  const uniq = Array.from(new Set(candidates));
6865
6890
  const targetDriver = platform === "android" ? "uiautomator2" : platform === "ios" ? "xcuitest" : "harmonyos";
@@ -6869,7 +6894,7 @@ function resolveAppiumHome(platform) {
6869
6894
  }
6870
6895
  }
6871
6896
  for (const candidate of uniq) {
6872
- if ((0, import_node_fs3.existsSync)(getAppiumExtensionsFile(candidate))) {
6897
+ if ((0, import_node_fs4.existsSync)(getAppiumExtensionsFile(candidate))) {
6873
6898
  return candidate;
6874
6899
  }
6875
6900
  }
@@ -8206,7 +8231,7 @@ function wireAdaMcpProtocolServer(mcp) {
8206
8231
  if (!file) {
8207
8232
  throw new Error("file is required");
8208
8233
  }
8209
- const taskPath = import_node_path13.default.isAbsolute(file) ? file : import_node_path13.default.resolve(process.cwd(), file);
8234
+ const taskPath = import_node_path14.default.isAbsolute(file) ? file : import_node_path14.default.resolve(process.cwd(), file);
8210
8235
  const tasks = await loadTaskFile2(taskPath);
8211
8236
  const results = await runTaskset2(tasks);
8212
8237
  const monitor = parseMonitorOptions(args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ada-mcp/mcp-server",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "ADA MCP server for web/mobile automation (stdio + remote HTTP)",
5
5
  "private": false,
6
6
  "type": "commonjs",
@@ -12,12 +12,13 @@
12
12
  "files": [
13
13
  "dist/cli.cjs",
14
14
  "plugins",
15
- "scripts/preinstall-registry.mjs",
15
+ "scripts/preinstall-probes.mjs",
16
16
  "scripts/registry-probe.mjs",
17
+ "scripts/playwright-probe.mjs",
17
18
  "README.md"
18
19
  ],
19
20
  "scripts": {
20
- "preinstall": "node scripts/preinstall-registry.mjs",
21
+ "preinstall": "node scripts/preinstall-probes.mjs",
21
22
  "dev": "tsx src/cli.ts",
22
23
  "prebuild": "node ../../scripts/generate-bundled-config.mjs",
23
24
  "build": "tsc -p tsconfig.json",
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Playwright 浏览器 CDN 测速(零依赖,与 dependency-installer 逻辑对齐)
3
+ */
4
+ export const DEFAULT_PLAYWRIGHT_HOST_CANDIDATES = [
5
+ "https://cdn.playwright.dev",
6
+ "https://playwright.azureedge.net",
7
+ "https://npmmirror.com/mirrors/playwright",
8
+ "https://cdn.npmmirror.com/binaries/playwright"
9
+ ];
10
+
11
+ function normalizeHostUrl(url) {
12
+ return String(url).replace(/\/$/, "");
13
+ }
14
+
15
+ /** 每个候选可尝试多个探测 URL(npmmirror 根路径常探测失败) */
16
+ export function playwrightProbeUrls(host) {
17
+ const h = normalizeHostUrl(host);
18
+ if (h.includes("npmmirror.com/mirrors/playwright")) {
19
+ return [h, "https://cdn.npmmirror.com/binaries/playwright"];
20
+ }
21
+ return [h];
22
+ }
23
+
24
+ export function playwrightHostCandidateList() {
25
+ const configured = normalizeHostUrl(
26
+ process.env.PLAYWRIGHT_DOWNLOAD_HOST?.trim() || DEFAULT_PLAYWRIGHT_HOST_CANDIDATES[0]
27
+ );
28
+ const extra = process.env.ADA_PLAYWRIGHT_HOST_CANDIDATES?.trim()
29
+ ? process.env.ADA_PLAYWRIGHT_HOST_CANDIDATES.split(",").map((x) => normalizeHostUrl(x.trim())).filter(Boolean)
30
+ : [];
31
+ const ordered = [configured, ...DEFAULT_PLAYWRIGHT_HOST_CANDIDATES, ...extra];
32
+ const seen = new Set();
33
+ const out = [];
34
+ for (const url of ordered) {
35
+ const n = normalizeHostUrl(url);
36
+ if (!seen.has(n)) {
37
+ seen.add(n);
38
+ out.push(n);
39
+ }
40
+ }
41
+ return out;
42
+ }
43
+
44
+ async function probeUrlLatency(url) {
45
+ const started = Date.now();
46
+ const controller = new AbortController();
47
+ const timer = setTimeout(() => controller.abort(), 5000);
48
+ try {
49
+ const response = await fetch(url, {
50
+ method: "GET",
51
+ redirect: "manual",
52
+ signal: controller.signal
53
+ });
54
+ if (response.status >= 200 && response.status < 500) {
55
+ return Date.now() - started;
56
+ }
57
+ return null;
58
+ } catch {
59
+ return null;
60
+ } finally {
61
+ clearTimeout(timer);
62
+ }
63
+ }
64
+
65
+ async function probePlaywrightHostLatency(host) {
66
+ const urls = playwrightProbeUrls(host);
67
+ let best = null;
68
+ for (const url of urls) {
69
+ const latency = await probeUrlLatency(url);
70
+ if (latency !== null && (best === null || latency < best)) {
71
+ best = latency;
72
+ }
73
+ }
74
+ return best;
75
+ }
76
+
77
+ export async function detectBestPlaywrightHost(candidates = playwrightHostCandidateList()) {
78
+ const probeResults = await Promise.all(
79
+ candidates.map(async (candidate) => ({
80
+ candidate,
81
+ latency: await probePlaywrightHostLatency(candidate)
82
+ }))
83
+ );
84
+ let best = candidates[0] ?? DEFAULT_PLAYWRIGHT_HOST_CANDIDATES[0];
85
+ let bestLatency = Number.POSITIVE_INFINITY;
86
+ let bestPriority = Number.POSITIVE_INFINITY;
87
+ for (const { candidate, latency } of probeResults) {
88
+ if (latency === null) continue;
89
+ const priority = candidates.indexOf(candidate);
90
+ const prio = priority >= 0 ? priority : Number.POSITIVE_INFINITY;
91
+ if (latency < bestLatency || (latency === bestLatency && prio < bestPriority)) {
92
+ best = candidate;
93
+ bestLatency = latency;
94
+ bestPriority = prio;
95
+ }
96
+ }
97
+ return { best: normalizeHostUrl(best), candidates, probeResults };
98
+ }
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * pnpm dlx 安装本包时:preinstall 测速 registry + Playwright CDN,写入 .npmrc 与 .ada-mcp-playwright-host
4
+ */
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+ import { detectBestRegistry, registryCandidateList } from "./registry-probe.mjs";
8
+ import { detectBestPlaywrightHost, playwrightHostCandidateList } from "./playwright-probe.mjs";
9
+
10
+ function installRoot() {
11
+ const init = process.env.INIT_CWD?.trim();
12
+ if (init && fs.existsSync(init)) {
13
+ return init;
14
+ }
15
+ return process.cwd();
16
+ }
17
+
18
+ function shouldRunProbe() {
19
+ if (process.env.ADA_MCP_SKIP_REGISTRY_PROBE === "1") {
20
+ return false;
21
+ }
22
+ if (process.env.ADA_MCP_FORCE_PREINSTALL_PROBE === "1") {
23
+ return true;
24
+ }
25
+ const init = (process.env.INIT_CWD || "").replace(/\\/g, "/");
26
+ if (/[\\/]dlx[\\/]/.test(init) || init.includes("__npx")) {
27
+ return true;
28
+ }
29
+ if (init) {
30
+ try {
31
+ if (fs.existsSync(path.join(init, "pnpm-workspace.yaml"))) {
32
+ return false;
33
+ }
34
+ } catch {
35
+ // ignore
36
+ }
37
+ }
38
+ return init.length > 0;
39
+ }
40
+
41
+ async function main() {
42
+ if (!shouldRunProbe()) {
43
+ return;
44
+ }
45
+ const root = installRoot();
46
+
47
+ const regCandidates = registryCandidateList();
48
+ const reg = await detectBestRegistry(regCandidates);
49
+ const npmrcPath = path.join(root, ".npmrc");
50
+ const regLine = `registry=${reg.best}\n`;
51
+ let npmrc = "";
52
+ try {
53
+ npmrc = fs.readFileSync(npmrcPath, "utf8");
54
+ } catch {
55
+ // new
56
+ }
57
+ if (!npmrc.includes(`registry=${reg.best}`)) {
58
+ fs.writeFileSync(npmrcPath, `${npmrc}${regLine}`, "utf8");
59
+ }
60
+ console.error(`[ada-mcp preinstall] registry: ${reg.best}`);
61
+ for (const { candidate, latency } of reg.probeResults) {
62
+ console.error(`[ada-mcp preinstall] registry ${candidate} -> ${latency === null ? "fail" : `${latency}ms`}`);
63
+ }
64
+
65
+ const pwCandidates = playwrightHostCandidateList();
66
+ const pw = await detectBestPlaywrightHost(pwCandidates);
67
+ const hostFile = path.join(root, ".ada-mcp-playwright-host");
68
+ fs.writeFileSync(hostFile, `${pw.best}\n`, "utf8");
69
+ process.env.PLAYWRIGHT_DOWNLOAD_HOST = pw.best;
70
+ console.error(`[ada-mcp preinstall] playwright CDN: ${pw.best} (wrote ${hostFile})`);
71
+ for (const { candidate, latency } of pw.probeResults) {
72
+ console.error(`[ada-mcp preinstall] playwright ${candidate} -> ${latency === null ? "fail" : `${latency}ms`}`);
73
+ }
74
+ }
75
+
76
+ main().catch((error) => {
77
+ console.error("[ada-mcp preinstall] probe failed:", error);
78
+ process.exit(0);
79
+ });
@@ -1,70 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * pnpm dlx / npm install 本包时:在安装 dependencies 之前测速并写入 .npmrc,
4
- * 使同一次安装中的 playwright 等依赖走最快 registry。
5
- */
6
- import fs from "node:fs";
7
- import path from "node:path";
8
- import { detectBestRegistry, registryCandidateList } from "./registry-probe.mjs";
9
-
10
- function installRoot() {
11
- const init = process.env.INIT_CWD?.trim();
12
- if (init && fs.existsSync(init)) {
13
- return init;
14
- }
15
- return process.cwd();
16
- }
17
-
18
- function shouldRunProbe() {
19
- if (process.env.ADA_MCP_SKIP_REGISTRY_PROBE === "1") {
20
- return false;
21
- }
22
- if (process.env.ADA_MCP_FORCE_PREINSTALL_PROBE === "1") {
23
- return true;
24
- }
25
- const init = (process.env.INIT_CWD || "").replace(/\\/g, "/");
26
- if (/[\\/]dlx[\\/]/.test(init) || init.includes("__npx")) {
27
- return true;
28
- }
29
- if (init) {
30
- try {
31
- if (fs.existsSync(path.join(init, "pnpm-workspace.yaml"))) {
32
- return false;
33
- }
34
- } catch {
35
- // ignore
36
- }
37
- }
38
- return init.length > 0;
39
- }
40
-
41
- async function main() {
42
- if (!shouldRunProbe()) {
43
- return;
44
- }
45
- const root = installRoot();
46
- const candidates = registryCandidateList();
47
- const { best, probeResults } = await detectBestRegistry(candidates);
48
- const npmrcPath = path.join(root, ".npmrc");
49
- const line = `registry=${best}\n`;
50
- let existing = "";
51
- try {
52
- existing = fs.readFileSync(npmrcPath, "utf8");
53
- } catch {
54
- // new file
55
- }
56
- if (!existing.includes(`registry=${best}`)) {
57
- fs.writeFileSync(npmrcPath, `${existing}${line}`, "utf8");
58
- }
59
- console.error(
60
- `[ada-mcp preinstall] registry probe: selected ${best} (candidates: ${candidates.join(", ")})`
61
- );
62
- for (const { candidate, latency } of probeResults) {
63
- console.error(`[ada-mcp preinstall] ${candidate} -> ${latency === null ? "fail" : `${latency}ms`}`);
64
- }
65
- }
66
-
67
- main().catch((error) => {
68
- console.error("[ada-mcp preinstall] registry probe failed:", error);
69
- process.exit(0);
70
- });