@ada-mcp/mcp-server 0.1.1 → 0.1.3

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.
Files changed (3) hide show
  1. package/README.md +60 -0
  2. package/dist/cli.cjs +965 -868
  3. package/package.json +1 -4
package/dist/cli.cjs CHANGED
@@ -23,538 +23,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  mod
24
24
  ));
25
25
 
26
- // src/main.ts
27
- var import_promises13 = __toESM(require("node:fs/promises"));
28
- var import_node_fs3 = require("node:fs");
29
- var import_node_net2 = __toESM(require("node:net"));
30
- var import_node_path13 = __toESM(require("node:path"));
31
- var import_node_child_process5 = require("node:child_process");
32
- var import_node_url4 = require("node:url");
33
- var import_server = require("@modelcontextprotocol/sdk/server/index.js");
34
- var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
35
- var import_types = require("@modelcontextprotocol/sdk/types.js");
36
-
37
- // src/executor.ts
38
- var import_node_fs2 = require("node:fs");
39
- var import_node_path2 = __toESM(require("node:path"));
40
- var import_node_url = require("node:url");
41
-
42
- // ../../packages/driver-rpc/src/index.ts
43
- function parseWebEngineFromPayload(payload) {
44
- const p = asRecord(payload);
45
- const options = asRecord(p.options);
46
- const raw = (getString(p.engine) ?? getString(options.engine) ?? "playwright").toLowerCase();
47
- if (raw === "selenium") {
48
- return "selenium";
49
- }
50
- return "playwright";
51
- }
52
- function manifestWebEngine(manifest) {
53
- if (manifest.engine === "selenium") {
54
- return "selenium";
55
- }
56
- return "playwright";
57
- }
58
- function asRecord(value) {
59
- return typeof value === "object" && value !== null ? value : {};
60
- }
61
- function getString(value) {
62
- return typeof value === "string" && value.length > 0 ? value : void 0;
63
- }
64
-
65
- // ../../packages/core-kernel/src/index.ts
66
- var DriverSessionManager = class {
67
- sessions = /* @__PURE__ */ new Map();
68
- sessionKey(command) {
69
- if (command.platform === "web") {
70
- const engine = parseWebEngineFromPayload(command.payload);
71
- return `web:${engine}:${command.sessionId}`;
72
- }
73
- return `${command.platform}:${command.sessionId}`;
74
- }
75
- async getOrCreate(plugin, command) {
76
- const key = this.sessionKey(command);
77
- const existed = this.sessions.get(key);
78
- if (existed) {
79
- return existed;
80
- }
81
- const created = await plugin.createSession(command.platform);
82
- this.sessions.set(key, created);
83
- return created;
84
- }
85
- get(command) {
86
- const key = this.sessionKey(command);
87
- return this.sessions.get(key);
88
- }
89
- list() {
90
- const items = [];
91
- for (const [key, session] of this.sessions.entries()) {
92
- const parts = key.split(":");
93
- if (parts[0] === "web" && parts.length >= 3) {
94
- items.push({
95
- platform: "web",
96
- engine: parts[1],
97
- sessionId: parts.slice(2).join(":"),
98
- driverSessionId: session.id
99
- });
100
- continue;
101
- }
102
- const idx = key.indexOf(":");
103
- if (idx <= 0) {
104
- continue;
105
- }
106
- items.push({
107
- platform: key.slice(0, idx),
108
- sessionId: key.slice(idx + 1),
109
- driverSessionId: session.id
110
- });
111
- }
112
- return items;
113
- }
114
- clear(command) {
115
- const key = this.sessionKey(command);
116
- const existed = this.sessions.get(key);
117
- this.sessions.delete(key);
118
- return existed;
119
- }
120
- clearByPlatformSession(platform, sessionId, options) {
121
- if (platform === "web") {
122
- const engine = options?.engine ?? parseWebEngineFromPayload(options?.payload);
123
- const key2 = `web:${engine}:${sessionId}`;
124
- const existed2 = this.sessions.get(key2);
125
- this.sessions.delete(key2);
126
- return existed2;
127
- }
128
- const key = `${platform}:${sessionId}`;
129
- const existed = this.sessions.get(key);
130
- this.sessions.delete(key);
131
- return existed;
132
- }
133
- clearWebSession(sessionId, engine) {
134
- const key = `web:${engine}:${sessionId}`;
135
- const existed = this.sessions.get(key);
136
- this.sessions.delete(key);
137
- return existed;
138
- }
139
- clearAll() {
140
- const all = Array.from(this.sessions.values());
141
- this.sessions.clear();
142
- return all;
143
- }
144
- };
145
- var RetryPolicyEngine = class {
146
- constructor(maxAttempts, retryableErrorCodes) {
147
- this.maxAttempts = maxAttempts;
148
- this.retryableErrorCodes = retryableErrorCodes;
149
- }
150
- shouldRetry(result, attempt) {
151
- if (attempt >= this.maxAttempts) {
152
- return false;
153
- }
154
- if (result.success) {
155
- return false;
156
- }
157
- if (!result.errorCode) {
158
- return true;
159
- }
160
- return this.retryableErrorCodes.has(result.errorCode);
161
- }
162
- };
163
- var ResultAssembler = class {
164
- success(requestId, data) {
165
- return {
166
- requestId,
167
- success: true,
168
- data
169
- };
170
- }
171
- failure(requestId, errorCode, errorMessage) {
172
- return {
173
- requestId,
174
- success: false,
175
- errorCode,
176
- errorMessage
177
- };
178
- }
179
- normalize(requestId, result) {
180
- return {
181
- requestId,
182
- success: Boolean(result.success),
183
- data: result.data,
184
- errorCode: result.errorCode,
185
- errorMessage: result.errorMessage
186
- };
187
- }
188
- };
189
- var FeatureNegotiator = class {
190
- normalize(command) {
191
- if (command === "click") {
192
- return "tap";
193
- }
194
- return command;
195
- }
196
- check(plugin, command) {
197
- const expected = this.normalize(command.command);
198
- const declared = new Set(plugin.manifest.capabilities.map((item) => this.normalize(item)));
199
- if (declared.has(expected)) {
200
- return { ok: true };
201
- }
202
- return {
203
- ok: false,
204
- code: "DRIVER_CAPABILITY_UNSUPPORTED",
205
- message: `Plugin ${plugin.manifest.id} does not support command ${command.command} on platform ${command.platform}`
206
- };
207
- }
208
- };
209
- var TaskExecutor = class {
210
- constructor(pluginHost, options = {}) {
211
- this.pluginHost = pluginHost;
212
- this.maxAttempts = Math.max(1, options.maxAttempts ?? 2);
213
- this.retry = new RetryPolicyEngine(
214
- this.maxAttempts,
215
- new Set(options.retryableErrorCodes ?? ["TRANSIENT_DRIVER_ERROR", "NETWORK_TIMEOUT"])
216
- );
217
- }
218
- retry;
219
- maxAttempts;
220
- sessions = new DriverSessionManager();
221
- resultAssembler = new ResultAssembler();
222
- featureNegotiator = new FeatureNegotiator();
223
- async resolveContext(command) {
224
- const plugin = this.pluginHost.resolve(command);
225
- await this.pluginHost.ensureInitialized(plugin.manifest.id);
226
- const session = await this.sessions.getOrCreate(plugin, command);
227
- return { plugin, session };
228
- }
229
- async execute(command) {
230
- let attempt = 0;
231
- let lastFailure = this.resultAssembler.failure(command.requestId, "KERNEL_EXECUTION_FAILED", "unknown error");
232
- while (attempt < this.maxAttempts) {
233
- attempt += 1;
234
- try {
235
- const context = await this.resolveContext(command);
236
- const feature = this.featureNegotiator.check(context.plugin, command);
237
- if (!feature.ok) {
238
- return this.resultAssembler.failure(command.requestId, feature.code, feature.message);
239
- }
240
- const result = await context.plugin.execute(context.session, command);
241
- const normalized = this.resultAssembler.normalize(command.requestId, result);
242
- if (this.retry.shouldRetry(normalized, attempt)) {
243
- this.sessions.clear(command);
244
- lastFailure = normalized;
245
- continue;
246
- }
247
- return normalized;
248
- } catch (error) {
249
- const message = error instanceof Error ? error.message : String(error);
250
- const failed = this.resultAssembler.failure(command.requestId, "KERNEL_EXECUTION_FAILED", message);
251
- if (this.retry.shouldRetry(failed, attempt)) {
252
- this.sessions.clear(command);
253
- lastFailure = failed;
254
- continue;
255
- }
256
- return failed;
257
- }
258
- }
259
- return lastFailure;
260
- }
261
- listSessions() {
262
- return this.sessions.list();
263
- }
264
- async closeSession(platform, sessionId, options) {
265
- const plat = platform;
266
- const engine = plat === "web" ? options?.engine ?? parseWebEngineFromPayload(options?.payload) : void 0;
267
- const plugin = plat === "web" ? this.pluginHost.resolve({
268
- requestId: "",
269
- sessionId,
270
- platform: "web",
271
- command: "navigate",
272
- payload: { engine, ...options?.payload }
273
- }) : this.pluginHost.resolve({
274
- requestId: "",
275
- sessionId,
276
- platform: plat,
277
- command: "navigate"
278
- });
279
- const session = this.sessions.clearByPlatformSession(platform, sessionId, {
280
- engine,
281
- payload: options?.payload
282
- });
283
- if (!session) {
284
- return false;
285
- }
286
- if (plugin.destroySession) {
287
- await plugin.destroySession(session);
288
- }
289
- return true;
290
- }
291
- async closeAllSessions() {
292
- const all = this.sessions.list();
293
- let closed = 0;
294
- for (const item of all) {
295
- const ok = await this.closeSession(item.platform, item.sessionId, {
296
- engine: item.engine,
297
- payload: item.engine ? { engine: item.engine } : void 0
298
- });
299
- if (ok) {
300
- closed += 1;
301
- }
302
- }
303
- return closed;
304
- }
305
- };
306
-
307
- // ../../packages/plugin-host/src/index.ts
308
- var import_node_fs = __toESM(require("node:fs"), 1);
309
- var import_node_path = __toESM(require("node:path"), 1);
310
- var import_node_module = require("node:module");
311
- function assertManifest(plugin) {
312
- const m = plugin.manifest;
313
- if (!m.id || !m.version) {
314
- throw new Error("Invalid plugin manifest: missing id/version");
315
- }
316
- if (!Array.isArray(m.platforms) || m.platforms.length === 0) {
317
- throw new Error(`Invalid plugin manifest (${m.id}): platforms is empty`);
318
- }
319
- if (!Array.isArray(m.capabilities)) {
320
- throw new Error(`Invalid plugin manifest (${m.id}): capabilities must be array`);
321
- }
322
- if (!/^\d+\.\d+\.\d+/.test(m.version)) {
323
- throw new Error(`Invalid plugin manifest (${m.id}): version must look like semver`);
324
- }
325
- }
326
- var PluginHost = class {
327
- plugins = /* @__PURE__ */ new Map();
328
- webEngines = /* @__PURE__ */ new Map();
329
- manifests = /* @__PURE__ */ new Map();
330
- pluginById = /* @__PURE__ */ new Map();
331
- initializedPluginIds = /* @__PURE__ */ new Set();
332
- register(plugin) {
333
- assertManifest(plugin);
334
- if (this.manifests.has(plugin.manifest.id)) {
335
- throw new Error(`Plugin already registered: ${plugin.manifest.id}`);
336
- }
337
- const mobilePlatforms = plugin.manifest.platforms.filter((p) => p !== "web");
338
- if (plugin.manifest.platforms.includes("web")) {
339
- const engine = manifestWebEngine(plugin.manifest);
340
- if (this.webEngines.has(engine)) {
341
- throw new Error(`Web engine already registered: ${engine} (reject ${plugin.manifest.id})`);
342
- }
343
- this.webEngines.set(engine, plugin);
344
- }
345
- for (const platform of mobilePlatforms) {
346
- if (this.plugins.has(platform)) {
347
- throw new Error(`Platform already has plugin (${platform}), reject: ${plugin.manifest.id}`);
348
- }
349
- this.plugins.set(platform, plugin);
350
- }
351
- this.manifests.set(plugin.manifest.id, plugin.manifest);
352
- this.pluginById.set(plugin.manifest.id, plugin);
353
- }
354
- registerWebEngine(plugin) {
355
- this.register(plugin);
356
- }
357
- /** Resolve driver for a command (web routes by payload.engine). */
358
- resolve(command) {
359
- if (command.platform === "web") {
360
- const engine = parseWebEngineFromPayload(command.payload);
361
- const plugin2 = this.webEngines.get(engine);
362
- if (!plugin2) {
363
- if (engine === "selenium") {
364
- throw new Error(
365
- "WEB_ENGINE_SELENIUM_NOT_INSTALLED: register @ada/driver-selenium and ensure GeckoDriver/ChromeDriver is on PATH"
366
- );
367
- }
368
- throw new Error(`WEB_ENGINE_UNKNOWN: ${engine}`);
369
- }
370
- return plugin2;
371
- }
372
- const plugin = this.plugins.get(command.platform);
373
- if (!plugin) {
374
- throw new Error(`No plugin registered for platform: ${command.platform}`);
375
- }
376
- return plugin;
377
- }
378
- /** Legacy: mobile platforms only; web defaults to playwright. */
379
- resolvePlatform(platform) {
380
- if (platform === "web") {
381
- const plugin = this.webEngines.get("playwright");
382
- if (!plugin) {
383
- throw new Error("No web engine registered (playwright)");
384
- }
385
- return plugin;
386
- }
387
- return this.resolve({ requestId: "", sessionId: "", platform, command: "navigate" });
388
- }
389
- listWebEngines() {
390
- return Array.from(this.webEngines.keys());
391
- }
392
- listManifests() {
393
- return Array.from(this.manifests.values());
394
- }
395
- async ensureInitialized(pluginId, timeoutMs = 15e3) {
396
- if (this.initializedPluginIds.has(pluginId)) {
397
- return;
398
- }
399
- const plugin = this.pluginById.get(pluginId);
400
- if (!plugin) {
401
- throw new Error(`Plugin not registered: ${pluginId}`);
402
- }
403
- await Promise.race([
404
- plugin.init(),
405
- new Promise(
406
- (_, reject) => setTimeout(() => reject(new Error(`Plugin init timeout: ${pluginId}`)), timeoutMs)
407
- )
408
- ]);
409
- this.initializedPluginIds.add(pluginId);
410
- }
411
- async healthCheck(timeoutMs = 5e3) {
412
- const items = [];
413
- for (const [id] of this.manifests) {
414
- try {
415
- await this.ensureInitialized(id, timeoutMs);
416
- items.push({ id, ok: true, message: "healthy" });
417
- } catch (error) {
418
- items.push({
419
- id,
420
- ok: false,
421
- message: error instanceof Error ? error.message : String(error)
422
- });
423
- }
424
- }
425
- return items;
426
- }
427
- };
428
- var DEFAULT_PLUGIN_MODULE_IDS = [
429
- "@ada/driver-playwright",
430
- "@ada/driver-appium",
431
- "@ada/driver-selenium"
432
- ];
433
- function isPluginModule(mod) {
434
- if (!mod || typeof mod !== "object") {
435
- return false;
436
- }
437
- const m = mod.manifest;
438
- return !!m && typeof m.id === "string" && Array.isArray(m.platforms);
439
- }
440
- function resolvePluginDirs(explicitPluginDir) {
441
- const fromEnv = process.env.ADA_PLUGIN_DIR?.trim();
442
- const execDir = import_node_path.default.dirname(process.execPath);
443
- const cwd = process.cwd();
444
- return Array.from(
445
- new Set(
446
- [fromEnv, explicitPluginDir, import_node_path.default.join(execDir, "plugins"), import_node_path.default.join(cwd, "plugins"), import_node_path.default.join(cwd, "release", "plugins")].filter((x) => Boolean(x && x.trim())).map((x) => import_node_path.default.resolve(x))
447
- )
448
- );
449
- }
450
- function loadPluginFromModule(requireFn, moduleId) {
451
- try {
452
- const loaded = requireFn(moduleId);
453
- const plugin = loaded?.default ?? loaded;
454
- return isPluginModule(plugin) ? plugin : null;
455
- } catch {
456
- return null;
457
- }
458
- }
459
- function registerPluginsFromDirectory(host, pluginDir) {
460
- if (!import_node_fs.default.existsSync(pluginDir)) {
461
- return [];
462
- }
463
- 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_path.default.join(pluginDir, ent.name)).sort((a, b) => a.localeCompare(b));
464
- if (entries.length === 0) {
465
- return [];
466
- }
467
- const loaded = [];
468
- for (const file of entries) {
469
- const requireFn = (0, import_node_module.createRequire)(file);
470
- const plugin = loadPluginFromModule(requireFn, file);
471
- if (!plugin) {
472
- continue;
473
- }
474
- host.register(plugin);
475
- loaded.push(plugin.manifest);
476
- }
477
- return loaded;
478
- }
479
- function registerPluginsFromModuleIds(host, moduleIds) {
480
- const req = (0, import_node_module.createRequire)(typeof __filename === "string" ? __filename : process.cwd());
481
- const loaded = [];
482
- for (const moduleId of moduleIds) {
483
- const plugin = loadPluginFromModule(req, moduleId);
484
- if (!plugin) {
485
- continue;
486
- }
487
- host.register(plugin);
488
- loaded.push(plugin.manifest);
489
- }
490
- return loaded;
491
- }
492
- function registerRuntimePlugins(host, options) {
493
- const manifests2 = [];
494
- for (const pluginDir of resolvePluginDirs(options?.pluginDir)) {
495
- manifests2.push(...registerPluginsFromDirectory(host, pluginDir));
496
- }
497
- if (manifests2.length > 0) {
498
- return manifests2;
499
- }
500
- const fallbackModuleIds = options?.moduleIds?.length ? options.moduleIds : DEFAULT_PLUGIN_MODULE_IDS;
501
- return registerPluginsFromModuleIds(host, fallbackModuleIds);
502
- }
503
-
504
- // src/executor.ts
505
- var import_meta = {};
506
- function ensureBundledPluginDir() {
507
- if (process.env.ADA_PLUGIN_DIR?.trim()) {
508
- return;
509
- }
510
- const candidates = [];
511
- try {
512
- const here = import_node_path2.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
513
- candidates.push(import_node_path2.default.join(here, "..", "plugins"));
514
- } catch {
515
- }
516
- const dirname = globalThis.__dirname;
517
- if (typeof dirname === "string") {
518
- candidates.push(import_node_path2.default.join(dirname, "..", "plugins"));
519
- }
520
- for (const dir of candidates) {
521
- if ((0, import_node_fs2.existsSync)(dir)) {
522
- process.env.ADA_PLUGIN_DIR = dir;
523
- return;
524
- }
525
- }
526
- }
527
- function buildPluginHost() {
528
- ensureBundledPluginDir();
529
- const host = new PluginHost();
530
- registerRuntimePlugins(host);
531
- return host;
532
- }
533
- var manifests = registerRuntimePlugins(new PluginHost());
534
- var sharedExecutor = new TaskExecutor(buildPluginHost());
535
- async function runCommand(command) {
536
- return sharedExecutor.execute(command);
537
- }
538
- async function runTaskset(commands) {
539
- const results = [];
540
- for (const command of commands) {
541
- results.push(await sharedExecutor.execute(command));
542
- }
543
- return results;
544
- }
545
- function listActiveSessions() {
546
- return sharedExecutor.listSessions();
547
- }
548
- async function closeSession(platform, sessionId, options) {
549
- return sharedExecutor.closeSession(platform, sessionId, options);
550
- }
551
- async function closeAllSessions() {
552
- return sharedExecutor.closeAllSessions();
553
- }
554
-
555
- // src/config.ts
556
- var import_promises2 = __toESM(require("node:fs/promises"));
557
- var import_node_path4 = __toESM(require("node:path"));
26
+ // ../ada-agent/dist/config.js
27
+ var import_promises2 = __toESM(require("node:fs/promises"), 1);
28
+ var import_node_path2 = __toESM(require("node:path"), 1);
558
29
 
559
30
  // ../../node_modules/js-yaml/dist/js-yaml.mjs
560
31
  function isNothing(subject) {
@@ -3182,7 +2653,7 @@ var jsYaml = {
3182
2653
 
3183
2654
  // ../../packages/core-runtime/src/index.ts
3184
2655
  var import_promises = __toESM(require("node:fs/promises"), 1);
3185
- var import_node_path3 = __toESM(require("node:path"), 1);
2656
+ var import_node_path = __toESM(require("node:path"), 1);
3186
2657
  function isObject2(value) {
3187
2658
  return typeof value === "object" && value !== null && !Array.isArray(value);
3188
2659
  }
@@ -3199,9 +2670,9 @@ function deepMerge(left, right) {
3199
2670
  return output2;
3200
2671
  }
3201
2672
  async function resolveWorkspaceRoot(configRelativePath, startDir = process.cwd()) {
3202
- const exeDir = import_node_path3.default.dirname(process.execPath);
2673
+ const exeDir = import_node_path.default.dirname(process.execPath);
3203
2674
  if (exeDir && exeDir !== "." && exeDir.length > 1) {
3204
- const besideExe = import_node_path3.default.join(exeDir, configRelativePath);
2675
+ const besideExe = import_node_path.default.join(exeDir, configRelativePath);
3205
2676
  try {
3206
2677
  await import_promises.default.access(besideExe);
3207
2678
  return exeDir;
@@ -3210,12 +2681,12 @@ async function resolveWorkspaceRoot(configRelativePath, startDir = process.cwd()
3210
2681
  }
3211
2682
  let current = startDir;
3212
2683
  for (let i = 0; i < 10; i += 1) {
3213
- const candidate = import_node_path3.default.join(current, configRelativePath);
2684
+ const candidate = import_node_path.default.join(current, configRelativePath);
3214
2685
  try {
3215
2686
  await import_promises.default.access(candidate);
3216
2687
  return current;
3217
2688
  } catch {
3218
- const parent = import_node_path3.default.dirname(current);
2689
+ const parent = import_node_path.default.dirname(current);
3219
2690
  if (parent === current) {
3220
2691
  break;
3221
2692
  }
@@ -3234,111 +2705,23 @@ function createJsonLogger(source) {
3234
2705
  });
3235
2706
  if (level === "error") {
3236
2707
  console.error(line);
3237
- return;
3238
- }
3239
- if (level === "warn") {
3240
- console.warn(line);
3241
- return;
3242
- }
3243
- console.log(line);
3244
- };
3245
- }
3246
-
3247
- // src/bundled-config.generated.ts
3248
- 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';
3249
-
3250
- // src/config.ts
3251
- var DEFAULT_CONFIG_RELATIVE = import_node_path4.default.join("config", "default.yaml");
3252
- var LOCAL_DATA_DIR = import_node_path4.default.join(".ada-agent");
3253
- var EFFECTIVE_CONFIG_FILE = import_node_path4.default.join(LOCAL_DATA_DIR, "agent.config.yaml");
3254
- async function resolveWorkspaceRoot2(startDir = process.cwd()) {
3255
- return resolveWorkspaceRoot(DEFAULT_CONFIG_RELATIVE, startDir);
3256
- }
3257
- async function loadAgentConfig(cwd = process.cwd()) {
3258
- let root = await resolveWorkspaceRoot2(cwd);
3259
- const defaultPath = import_node_path4.default.join(root, DEFAULT_CONFIG_RELATIVE);
3260
- let defaultRaw;
3261
- try {
3262
- defaultRaw = await import_promises2.default.readFile(defaultPath, "utf8");
3263
- } catch {
3264
- defaultRaw = bundledDefaultConfigYaml;
3265
- root = import_node_path4.default.dirname(process.execPath);
3266
- }
3267
- const defaultConfig2 = jsYaml.load(defaultRaw) ?? {};
3268
- const effectivePath = import_node_path4.default.join(root, EFFECTIVE_CONFIG_FILE);
3269
- try {
3270
- const effectiveFile = await import_promises2.default.readFile(effectivePath, "utf8");
3271
- const effectiveConfig = jsYaml.load(effectiveFile) ?? {};
3272
- return deepMerge(defaultConfig2, effectiveConfig);
3273
- } catch {
3274
- return defaultConfig2;
3275
- }
3276
- }
3277
-
3278
- // src/monitoring.ts
3279
- var import_promises3 = __toESM(require("node:fs/promises"));
3280
- var import_node_path5 = __toESM(require("node:path"));
3281
- var import_jimp = require("jimp");
3282
- function getScreenshotPath(result) {
3283
- const value = result.data?.screenshot;
3284
- return typeof value === "string" ? value : null;
3285
- }
3286
- function buildOutputPath(options, command) {
3287
- if (options.groupBySession) {
3288
- return import_node_path5.default.join(options.outputDir, command.sessionId, `${command.requestId}.png`);
3289
- }
3290
- return import_node_path5.default.join(options.outputDir, `${command.requestId}.png`);
3291
- }
3292
- async function captureMcpMonitor(command, result, options, runCommand4) {
3293
- if (!options.enabled) {
3294
- return null;
3295
- }
3296
- if (options.onFailureOnly && result.success) {
3297
- return null;
3298
- }
3299
- if (command.command === "screenshot") {
3300
- return null;
3301
- }
3302
- const shotResult = await runCommand4({
3303
- requestId: `${command.requestId}-monitor`,
3304
- sessionId: command.sessionId,
3305
- platform: command.platform,
3306
- command: "screenshot",
3307
- payload: {
3308
- ...command.payload ?? {},
3309
- __monitorCapture: true
3310
- }
3311
- });
3312
- if (!shotResult.success) {
3313
- return null;
3314
- }
3315
- const sourcePath = getScreenshotPath(shotResult);
3316
- if (!sourcePath) {
3317
- return null;
3318
- }
3319
- const targetPath = buildOutputPath(options, command);
3320
- await import_promises3.default.mkdir(import_node_path5.default.dirname(targetPath), { recursive: true });
3321
- const image = await import_jimp.Jimp.read(sourcePath);
3322
- if (options.keepAspectRatio) {
3323
- image.scaleToFit({ w: options.maxWidth, h: options.maxHeight });
3324
- } else {
3325
- image.cover({ w: options.maxWidth, h: options.maxHeight });
3326
- }
3327
- await image.write(targetPath);
3328
- return targetPath;
2708
+ return;
2709
+ }
2710
+ if (level === "warn") {
2711
+ console.warn(line);
2712
+ return;
2713
+ }
2714
+ console.log(line);
2715
+ };
3329
2716
  }
3330
2717
 
3331
- // ../ada-agent/dist/config.js
3332
- var import_promises4 = __toESM(require("node:fs/promises"), 1);
3333
- var import_node_path6 = __toESM(require("node:path"), 1);
3334
-
3335
2718
  // ../ada-agent/dist/bundled-config.generated.js
3336
- 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 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';
2719
+ 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';
3337
2720
 
3338
2721
  // ../ada-agent/dist/config.js
3339
- var DEFAULT_CONFIG_RELATIVE2 = import_node_path6.default.join("config", "default.yaml");
3340
- var LOCAL_DATA_DIR2 = import_node_path6.default.join(".ada-agent");
3341
- var EFFECTIVE_CONFIG_FILE2 = import_node_path6.default.join(LOCAL_DATA_DIR2, "agent.config.yaml");
2722
+ var DEFAULT_CONFIG_RELATIVE = import_node_path2.default.join("config", "default.yaml");
2723
+ var LOCAL_DATA_DIR = import_node_path2.default.join(".ada-agent");
2724
+ var EFFECTIVE_CONFIG_FILE = import_node_path2.default.join(LOCAL_DATA_DIR, "agent.config.yaml");
3342
2725
  var defaultConfig = {
3343
2726
  agent: {
3344
2727
  id: "ada-agent-local",
@@ -3437,30 +2820,30 @@ function mergeConfig(base, overrides) {
3437
2820
  appium: { ...base.appium, ...overrides.appium }
3438
2821
  };
3439
2822
  }
3440
- async function resolveWorkspaceRoot3(startDir = process.cwd()) {
3441
- return resolveWorkspaceRoot(DEFAULT_CONFIG_RELATIVE2, startDir);
2823
+ async function resolveWorkspaceRoot2(startDir = process.cwd()) {
2824
+ return resolveWorkspaceRoot(DEFAULT_CONFIG_RELATIVE, startDir);
3442
2825
  }
3443
2826
  async function ensureLocalDataDir(cwd = process.cwd()) {
3444
- const root = await resolveWorkspaceRoot3(cwd);
3445
- const dir = import_node_path6.default.join(root, LOCAL_DATA_DIR2);
3446
- await import_promises4.default.mkdir(dir, { recursive: true });
2827
+ const root = await resolveWorkspaceRoot2(cwd);
2828
+ const dir = import_node_path2.default.join(root, LOCAL_DATA_DIR);
2829
+ await import_promises2.default.mkdir(dir, { recursive: true });
3447
2830
  return dir;
3448
2831
  }
3449
2832
  async function loadConfig(cwd = process.cwd()) {
3450
- let root = await resolveWorkspaceRoot3(cwd);
3451
- const defaultPath = import_node_path6.default.join(root, DEFAULT_CONFIG_RELATIVE2);
2833
+ let root = await resolveWorkspaceRoot2(cwd);
2834
+ const defaultPath = import_node_path2.default.join(root, DEFAULT_CONFIG_RELATIVE);
3452
2835
  let defaultRaw;
3453
2836
  try {
3454
- defaultRaw = await import_promises4.default.readFile(defaultPath, "utf8");
2837
+ defaultRaw = await import_promises2.default.readFile(defaultPath, "utf8");
3455
2838
  } catch {
3456
- defaultRaw = bundledDefaultConfigYaml2;
3457
- root = import_node_path6.default.dirname(process.execPath);
2839
+ defaultRaw = bundledDefaultConfigYaml;
2840
+ root = import_node_path2.default.dirname(process.execPath);
3458
2841
  }
3459
2842
  const defaultFromFile = jsYaml.load(defaultRaw);
3460
2843
  const mergedDefault = mergeConfig(defaultConfig, defaultFromFile);
3461
- const effectivePath = import_node_path6.default.join(root, EFFECTIVE_CONFIG_FILE2);
2844
+ const effectivePath = import_node_path2.default.join(root, EFFECTIVE_CONFIG_FILE);
3462
2845
  try {
3463
- const effectiveFile = await import_promises4.default.readFile(effectivePath, "utf8");
2846
+ const effectiveFile = await import_promises2.default.readFile(effectivePath, "utf8");
3464
2847
  const effective = jsYaml.load(effectiveFile);
3465
2848
  return mergeConfig(mergedDefault, effective);
3466
2849
  } catch {
@@ -3468,10 +2851,10 @@ async function loadConfig(cwd = process.cwd()) {
3468
2851
  }
3469
2852
  }
3470
2853
  async function saveEffectiveConfig(config, cwd = process.cwd()) {
3471
- const root = await resolveWorkspaceRoot3(cwd);
2854
+ const root = await resolveWorkspaceRoot2(cwd);
3472
2855
  await ensureLocalDataDir(root);
3473
- const effectivePath = import_node_path6.default.join(root, EFFECTIVE_CONFIG_FILE2);
3474
- await import_promises4.default.writeFile(effectivePath, jsYaml.dump(config), "utf8");
2856
+ const effectivePath = import_node_path2.default.join(root, EFFECTIVE_CONFIG_FILE);
2857
+ await import_promises2.default.writeFile(effectivePath, jsYaml.dump(config), "utf8");
3475
2858
  }
3476
2859
  function maskToken(token) {
3477
2860
  if (!token) {
@@ -3484,8 +2867,8 @@ function maskToken(token) {
3484
2867
  }
3485
2868
 
3486
2869
  // ../ada-agent/dist/secrets.js
3487
- var import_promises5 = __toESM(require("node:fs/promises"), 1);
3488
- var import_node_path7 = __toESM(require("node:path"), 1);
2870
+ var import_promises3 = __toESM(require("node:fs/promises"), 1);
2871
+ var import_node_path3 = __toESM(require("node:path"), 1);
3489
2872
  var SECRET_FILE = "secrets.json";
3490
2873
  function resolveProvider(provider) {
3491
2874
  if (provider === "keychain" || provider === "credman") {
@@ -3499,8 +2882,8 @@ async function saveSecret(record, provider, cwd = process.cwd()) {
3499
2882
  return;
3500
2883
  }
3501
2884
  const dir = await ensureLocalDataDir(cwd);
3502
- const file = import_node_path7.default.join(dir, SECRET_FILE);
3503
- await import_promises5.default.writeFile(file, JSON.stringify(record, null, 2), "utf8");
2885
+ const file = import_node_path3.default.join(dir, SECRET_FILE);
2886
+ await import_promises3.default.writeFile(file, JSON.stringify(record, null, 2), "utf8");
3504
2887
  }
3505
2888
  async function loadSecret(provider, cwd = process.cwd()) {
3506
2889
  const resolved = resolveProvider(provider);
@@ -3509,8 +2892,8 @@ async function loadSecret(provider, cwd = process.cwd()) {
3509
2892
  }
3510
2893
  try {
3511
2894
  const dir = await ensureLocalDataDir(cwd);
3512
- const file = import_node_path7.default.join(dir, SECRET_FILE);
3513
- const raw = await import_promises5.default.readFile(file, "utf8");
2895
+ const file = import_node_path3.default.join(dir, SECRET_FILE);
2896
+ const raw = await import_promises3.default.readFile(file, "utf8");
3514
2897
  return JSON.parse(raw);
3515
2898
  } catch {
3516
2899
  return null;
@@ -3518,10 +2901,10 @@ async function loadSecret(provider, cwd = process.cwd()) {
3518
2901
  }
3519
2902
 
3520
2903
  // ../ada-agent/dist/dependency-installer.js
3521
- var import_node_module2 = require("node:module");
2904
+ var import_node_module = require("node:module");
3522
2905
  var import_node_child_process2 = require("node:child_process");
3523
- var import_promises7 = __toESM(require("node:fs/promises"), 1);
3524
- var import_node_path9 = __toESM(require("node:path"), 1);
2906
+ var import_promises5 = __toESM(require("node:fs/promises"), 1);
2907
+ var import_node_path5 = __toESM(require("node:path"), 1);
3525
2908
 
3526
2909
  // ../ada-agent/dist/logger.js
3527
2910
  var baseLog = createJsonLogger("ada-agent");
@@ -3531,13 +2914,13 @@ function log(level, payload) {
3531
2914
 
3532
2915
  // ../../packages/native-drivers/src/index.ts
3533
2916
  var import_node_child_process = require("node:child_process");
3534
- var import_promises6 = __toESM(require("node:fs/promises"), 1);
3535
- var import_node_path8 = __toESM(require("node:path"), 1);
2917
+ var import_promises4 = __toESM(require("node:fs/promises"), 1);
2918
+ var import_node_path4 = __toESM(require("node:path"), 1);
3536
2919
  var DEFAULT_NATIVE_DRIVERS_DIR = "dirver";
3537
2920
  var CHROME_FOR_TESTING_JSON = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json";
3538
2921
  async function fileExists(filePath) {
3539
2922
  try {
3540
- await import_promises6.default.access(filePath);
2923
+ await import_promises4.default.access(filePath);
3541
2924
  return true;
3542
2925
  } catch {
3543
2926
  return false;
@@ -3545,19 +2928,19 @@ async function fileExists(filePath) {
3545
2928
  }
3546
2929
  async function dirExists(dirPath) {
3547
2930
  try {
3548
- const stat = await import_promises6.default.stat(dirPath);
2931
+ const stat = await import_promises4.default.stat(dirPath);
3549
2932
  return stat.isDirectory();
3550
2933
  } catch {
3551
2934
  return false;
3552
2935
  }
3553
2936
  }
3554
- async function resolveWorkspaceRoot4(cwd = process.cwd()) {
3555
- let current = import_node_path8.default.resolve(cwd);
2937
+ async function resolveWorkspaceRoot3(cwd = process.cwd()) {
2938
+ let current = import_node_path4.default.resolve(cwd);
3556
2939
  for (let i = 0; i < 8; i += 1) {
3557
- const pkg = import_node_path8.default.join(current, "package.json");
2940
+ const pkg = import_node_path4.default.join(current, "package.json");
3558
2941
  if (await fileExists(pkg)) {
3559
2942
  try {
3560
- const raw = await import_promises6.default.readFile(pkg, "utf8");
2943
+ const raw = await import_promises4.default.readFile(pkg, "utf8");
3561
2944
  const parsed = JSON.parse(raw);
3562
2945
  if (parsed.workspaces) {
3563
2946
  return current;
@@ -3565,27 +2948,27 @@ async function resolveWorkspaceRoot4(cwd = process.cwd()) {
3565
2948
  } catch {
3566
2949
  }
3567
2950
  }
3568
- const parent = import_node_path8.default.dirname(current);
2951
+ const parent = import_node_path4.default.dirname(current);
3569
2952
  if (parent === current) {
3570
2953
  break;
3571
2954
  }
3572
2955
  current = parent;
3573
2956
  }
3574
- return import_node_path8.default.resolve(cwd);
2957
+ return import_node_path4.default.resolve(cwd);
3575
2958
  }
3576
2959
  async function resolveNativeDriversDir(workspaceRoot) {
3577
- const root = workspaceRoot ? import_node_path8.default.resolve(workspaceRoot) : await resolveWorkspaceRoot4();
2960
+ const root = workspaceRoot ? import_node_path4.default.resolve(workspaceRoot) : await resolveWorkspaceRoot3();
3578
2961
  const fromEnv = process.env.ADA_DRIVERS_DIR?.trim();
3579
2962
  if (fromEnv) {
3580
- return import_node_path8.default.isAbsolute(fromEnv) ? fromEnv : import_node_path8.default.join(root, fromEnv);
2963
+ return import_node_path4.default.isAbsolute(fromEnv) ? fromEnv : import_node_path4.default.join(root, fromEnv);
3581
2964
  }
3582
2965
  for (const name of [DEFAULT_NATIVE_DRIVERS_DIR, "driver", "drivers"]) {
3583
- const candidate = import_node_path8.default.join(root, name);
2966
+ const candidate = import_node_path4.default.join(root, name);
3584
2967
  if (await dirExists(candidate)) {
3585
2968
  return candidate;
3586
2969
  }
3587
2970
  }
3588
- return import_node_path8.default.join(root, DEFAULT_NATIVE_DRIVERS_DIR);
2971
+ return import_node_path4.default.join(root, DEFAULT_NATIVE_DRIVERS_DIR);
3589
2972
  }
3590
2973
  function platformArchiveSuffix() {
3591
2974
  if (process.platform === "win32") {
@@ -3620,7 +3003,7 @@ async function listExecutablesInDir(driversDir) {
3620
3003
  if (!await dirExists(driversDir)) {
3621
3004
  return [];
3622
3005
  }
3623
- const entries = await import_promises6.default.readdir(driversDir, { withFileTypes: true });
3006
+ const entries = await import_promises4.default.readdir(driversDir, { withFileTypes: true });
3624
3007
  const files = [];
3625
3008
  for (const ent of entries) {
3626
3009
  if (ent.isFile()) {
@@ -3651,16 +3034,16 @@ async function findGeckodriverInDir(driversDir, version) {
3651
3034
  const names = await listLocalGeckodriverCandidates(driversDir);
3652
3035
  const prefer = geckodriverExeName();
3653
3036
  if (names.includes(prefer)) {
3654
- return import_node_path8.default.join(driversDir, prefer);
3037
+ return import_node_path4.default.join(driversDir, prefer);
3655
3038
  }
3656
3039
  if (version) {
3657
3040
  const tagged = names.find((n) => n.includes(version.replace(/^v/, "")));
3658
3041
  if (tagged) {
3659
- return import_node_path8.default.join(driversDir, tagged);
3042
+ return import_node_path4.default.join(driversDir, tagged);
3660
3043
  }
3661
3044
  }
3662
3045
  if (names.length > 0) {
3663
- return import_node_path8.default.join(driversDir, names[0]);
3046
+ return import_node_path4.default.join(driversDir, names[0]);
3664
3047
  }
3665
3048
  return void 0;
3666
3049
  }
@@ -3670,19 +3053,19 @@ async function findChromedriverInDir(driversDir, version) {
3670
3053
  }
3671
3054
  const wantMajor = version && version !== "latest" ? version.replace(/^v/i, "").split(".")[0] : void 0;
3672
3055
  if (wantMajor) {
3673
- const named = import_node_path8.default.join(driversDir, chromedriverExeName(wantMajor));
3056
+ const named = import_node_path4.default.join(driversDir, chromedriverExeName(wantMajor));
3674
3057
  if (await fileExists(named)) {
3675
3058
  return { path: named, version: wantMajor };
3676
3059
  }
3677
3060
  }
3678
- const generic = import_node_path8.default.join(driversDir, chromedriverExeName());
3061
+ const generic = import_node_path4.default.join(driversDir, chromedriverExeName());
3679
3062
  if (await fileExists(generic)) {
3680
3063
  return { path: generic, version: wantMajor ?? "generic" };
3681
3064
  }
3682
3065
  const locals = await listLocalChromedriverVersions(driversDir);
3683
3066
  if (locals.length > 0) {
3684
3067
  const pick = wantMajor && locals.includes(wantMajor) ? wantMajor : locals[0];
3685
- return { path: import_node_path8.default.join(driversDir, chromedriverExeName(pick)), version: pick };
3068
+ return { path: import_node_path4.default.join(driversDir, chromedriverExeName(pick)), version: pick };
3686
3069
  }
3687
3070
  return void 0;
3688
3071
  }
@@ -3749,7 +3132,7 @@ async function commandOnPath(command) {
3749
3132
  child.on("error", () => resolve(false));
3750
3133
  });
3751
3134
  }
3752
- async function runCommand2(command, args) {
3135
+ async function runCommand(command, args) {
3753
3136
  return new Promise((resolve, reject) => {
3754
3137
  const child = (0, import_node_child_process.spawn)(command, args, {
3755
3138
  stdio: "inherit",
@@ -3766,23 +3149,23 @@ async function runCommand2(command, args) {
3766
3149
  });
3767
3150
  }
3768
3151
  async function extractZip(zipPath, destDir) {
3769
- await import_promises6.default.mkdir(destDir, { recursive: true });
3152
+ await import_promises4.default.mkdir(destDir, { recursive: true });
3770
3153
  if (process.platform === "win32") {
3771
3154
  const escapedZip = zipPath.replace(/'/g, "''");
3772
3155
  const escapedDest = destDir.replace(/'/g, "''");
3773
- await runCommand2("powershell", [
3156
+ await runCommand("powershell", [
3774
3157
  "-NoProfile",
3775
3158
  "-Command",
3776
3159
  `Expand-Archive -LiteralPath '${escapedZip}' -DestinationPath '${escapedDest}' -Force`
3777
3160
  ]);
3778
3161
  return;
3779
3162
  }
3780
- await runCommand2("unzip", ["-o", zipPath, "-d", destDir]);
3163
+ await runCommand("unzip", ["-o", zipPath, "-d", destDir]);
3781
3164
  }
3782
3165
  async function findFileRecursive(dir, fileName) {
3783
- const entries = await import_promises6.default.readdir(dir, { withFileTypes: true });
3166
+ const entries = await import_promises4.default.readdir(dir, { withFileTypes: true });
3784
3167
  for (const ent of entries) {
3785
- const full = import_node_path8.default.join(dir, ent.name);
3168
+ const full = import_node_path4.default.join(dir, ent.name);
3786
3169
  if (ent.isFile() && ent.name.toLowerCase() === fileName.toLowerCase()) {
3787
3170
  return full;
3788
3171
  }
@@ -3796,10 +3179,10 @@ async function findFileRecursive(dir, fileName) {
3796
3179
  return void 0;
3797
3180
  }
3798
3181
  async function copyExecutable(src, dest) {
3799
- await import_promises6.default.mkdir(import_node_path8.default.dirname(dest), { recursive: true });
3800
- await import_promises6.default.copyFile(src, dest);
3182
+ await import_promises4.default.mkdir(import_node_path4.default.dirname(dest), { recursive: true });
3183
+ await import_promises4.default.copyFile(src, dest);
3801
3184
  if (process.platform !== "win32") {
3802
- await import_promises6.default.chmod(dest, 493);
3185
+ await import_promises4.default.chmod(dest, 493);
3803
3186
  }
3804
3187
  }
3805
3188
  async function fetchGeckodriverReleaseVersion(requested) {
@@ -3867,8 +3250,8 @@ async function detectInstalledChromeMajorVersion() {
3867
3250
  return void 0;
3868
3251
  }
3869
3252
  const candidates = [
3870
- import_node_path8.default.join(process.env["ProgramFiles"] ?? "C:\\Program Files", "Google", "Chrome", "Application", "chrome.exe"),
3871
- import_node_path8.default.join(
3253
+ import_node_path4.default.join(process.env["ProgramFiles"] ?? "C:\\Program Files", "Google", "Chrome", "Application", "chrome.exe"),
3254
+ import_node_path4.default.join(
3872
3255
  process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)",
3873
3256
  "Google",
3874
3257
  "Chrome",
@@ -3916,8 +3299,8 @@ async function downloadToFile(url, destPath) {
3916
3299
  throw new Error(`Download failed ${url}: HTTP ${res.status}`);
3917
3300
  }
3918
3301
  const buf = Buffer.from(await res.arrayBuffer());
3919
- await import_promises6.default.mkdir(import_node_path8.default.dirname(destPath), { recursive: true });
3920
- await import_promises6.default.writeFile(destPath, buf);
3302
+ await import_promises4.default.mkdir(import_node_path4.default.dirname(destPath), { recursive: true });
3303
+ await import_promises4.default.writeFile(destPath, buf);
3921
3304
  }
3922
3305
  async function downloadGeckodriver(driversDir, versionInput, onLogLine) {
3923
3306
  const tag = await fetchGeckodriverReleaseVersion(versionInput);
@@ -3925,22 +3308,22 @@ async function downloadGeckodriver(driversDir, versionInput, onLogLine) {
3925
3308
  const suffix = platformArchiveSuffix();
3926
3309
  const assetName = `geckodriver-${tag}-${suffix}`.replace("vv", "v");
3927
3310
  const url = `https://github.com/mozilla/geckodriver/releases/download/${tag}/geckodriver-${tag}-${suffix}`;
3928
- await import_promises6.default.mkdir(driversDir, { recursive: true });
3929
- const zipPath = import_node_path8.default.join(driversDir, `_download_geckodriver_${version}.zip`);
3930
- const extractDir = import_node_path8.default.join(driversDir, `_extract_geckodriver_${version}`);
3311
+ await import_promises4.default.mkdir(driversDir, { recursive: true });
3312
+ const zipPath = import_node_path4.default.join(driversDir, `_download_geckodriver_${version}.zip`);
3313
+ const extractDir = import_node_path4.default.join(driversDir, `_extract_geckodriver_${version}`);
3931
3314
  onLogLine?.(`[selenium] \u4E0B\u8F7D geckodriver ${tag} \uFFFD\uFFFD?${driversDir}`);
3932
3315
  onLogLine?.(`[selenium] URL: ${url}`);
3933
3316
  await downloadToFile(url, zipPath);
3934
- await import_promises6.default.rm(extractDir, { recursive: true, force: true });
3317
+ await import_promises4.default.rm(extractDir, { recursive: true, force: true });
3935
3318
  await extractZip(zipPath, extractDir);
3936
3319
  const found = await findFileRecursive(extractDir, geckodriverExeName());
3937
3320
  if (!found) {
3938
3321
  throw new Error(`geckodriver binary not found after extracting ${assetName}`);
3939
3322
  }
3940
- const dest = import_node_path8.default.join(driversDir, geckodriverExeName());
3323
+ const dest = import_node_path4.default.join(driversDir, geckodriverExeName());
3941
3324
  await copyExecutable(found, dest);
3942
- await import_promises6.default.rm(zipPath, { force: true });
3943
- await import_promises6.default.rm(extractDir, { recursive: true, force: true });
3325
+ await import_promises4.default.rm(zipPath, { force: true });
3326
+ await import_promises4.default.rm(extractDir, { recursive: true, force: true });
3944
3327
  onLogLine?.(`[selenium] geckodriver \u5DF2\u5199\uFFFD\uFFFD? ${dest}`);
3945
3328
  return { path: dest, version: tag };
3946
3329
  }
@@ -3958,40 +3341,40 @@ async function downloadChromedriver(driversDir, versionInput, onLogLine) {
3958
3341
  if (!url) {
3959
3342
  throw new Error(`No chromedriver download for version ${fullVersion} platform ${platform}`);
3960
3343
  }
3961
- await import_promises6.default.mkdir(driversDir, { recursive: true });
3962
- const zipPath = import_node_path8.default.join(driversDir, `_download_chromedriver_${major}.zip`);
3963
- const extractDir = import_node_path8.default.join(driversDir, `_extract_chromedriver_${major}`);
3344
+ await import_promises4.default.mkdir(driversDir, { recursive: true });
3345
+ const zipPath = import_node_path4.default.join(driversDir, `_download_chromedriver_${major}.zip`);
3346
+ const extractDir = import_node_path4.default.join(driversDir, `_extract_chromedriver_${major}`);
3964
3347
  onLogLine?.(`[selenium] \u4E0B\u8F7D chromedriver ${fullVersion} (\u4E3B\u7248\uFFFD\uFFFD?${major}) \uFFFD\uFFFD?${driversDir}`);
3965
3348
  onLogLine?.(`[selenium] URL: ${url}`);
3966
3349
  await downloadToFile(url, zipPath);
3967
- await import_promises6.default.rm(extractDir, { recursive: true, force: true });
3350
+ await import_promises4.default.rm(extractDir, { recursive: true, force: true });
3968
3351
  await extractZip(zipPath, extractDir);
3969
3352
  const found = await findFileRecursive(extractDir, chromedriverExeName());
3970
3353
  if (!found) {
3971
3354
  throw new Error(`chromedriver binary not found after extracting ${fullVersion}`);
3972
3355
  }
3973
- const destVersioned = import_node_path8.default.join(driversDir, chromedriverExeName(major));
3356
+ const destVersioned = import_node_path4.default.join(driversDir, chromedriverExeName(major));
3974
3357
  await copyExecutable(found, destVersioned);
3975
3358
  if (versionInput === "latest" || !versionInput) {
3976
- const destGeneric = import_node_path8.default.join(driversDir, chromedriverExeName());
3359
+ const destGeneric = import_node_path4.default.join(driversDir, chromedriverExeName());
3977
3360
  await copyExecutable(found, destGeneric);
3978
3361
  }
3979
- await import_promises6.default.rm(zipPath, { force: true });
3980
- await import_promises6.default.rm(extractDir, { recursive: true, force: true });
3362
+ await import_promises4.default.rm(zipPath, { force: true });
3363
+ await import_promises4.default.rm(extractDir, { recursive: true, force: true });
3981
3364
  onLogLine?.(`[selenium] chromedriver \u5DF2\u5199\uFFFD\uFFFD? ${destVersioned}`);
3982
3365
  return { path: destVersioned, version: fullVersion, major };
3983
3366
  }
3984
3367
  async function saveNativeDriverManifest(manifest, workspaceRoot) {
3985
- const root = workspaceRoot ?? await resolveWorkspaceRoot4();
3986
- const dir = import_node_path8.default.join(root, ".ada-agent");
3987
- await import_promises6.default.mkdir(dir, { recursive: true });
3988
- const file = import_node_path8.default.join(dir, "native-drivers.json");
3989
- await import_promises6.default.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
3368
+ const root = workspaceRoot ?? await resolveWorkspaceRoot3();
3369
+ const dir = import_node_path4.default.join(root, ".ada-agent");
3370
+ await import_promises4.default.mkdir(dir, { recursive: true });
3371
+ const file = import_node_path4.default.join(dir, "native-drivers.json");
3372
+ await import_promises4.default.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
3990
3373
  }
3991
3374
  async function ensureNativeWebDrivers(options = {}) {
3992
- const root = options.workspaceRoot ?? await resolveWorkspaceRoot4();
3375
+ const root = options.workspaceRoot ?? await resolveWorkspaceRoot3();
3993
3376
  const driversDir = options.driversDir ?? await resolveNativeDriversDir(root);
3994
- await import_promises6.default.mkdir(driversDir, { recursive: true });
3377
+ await import_promises4.default.mkdir(driversDir, { recursive: true });
3995
3378
  const log2 = options.onLogLine;
3996
3379
  log2?.(`[selenium] \u539F\u751F\u9A71\u52A8\u76EE\u5F55: ${driversDir}`);
3997
3380
  const localChromeVersions = await listLocalChromedriverVersions(driversDir);
@@ -4043,7 +3426,7 @@ async function ensureNativeWebDrivers(options = {}) {
4043
3426
  }
4044
3427
 
4045
3428
  // ../ada-agent/dist/dependency-installer.js
4046
- var require2 = (0, import_node_module2.createRequire)(import_node_path9.default.join(process.cwd(), "package.json"));
3429
+ var require2 = (0, import_node_module.createRequire)(import_node_path5.default.join(process.cwd(), "package.json"));
4047
3430
  function shouldUseShell(command) {
4048
3431
  if (process.platform !== "win32") {
4049
3432
  return false;
@@ -4058,7 +3441,7 @@ function hasPackage(packageName) {
4058
3441
  return false;
4059
3442
  }
4060
3443
  }
4061
- function runCommand3(command, args, options) {
3444
+ function runCommand2(command, args, options) {
4062
3445
  return new Promise((resolve, reject) => {
4063
3446
  const onLogLine = options?.onLogLine;
4064
3447
  const child = (0, import_node_child_process2.spawn)(command, args, {
@@ -4222,7 +3605,7 @@ async function getAppiumMajorVersion() {
4222
3605
  let version = "";
4223
3606
  try {
4224
3607
  const pkgPath = require2.resolve("appium/package.json");
4225
- const raw = await import_promises7.default.readFile(pkgPath, "utf8");
3608
+ const raw = await import_promises5.default.readFile(pkgPath, "utf8");
4226
3609
  version = String(JSON.parse(raw).version ?? "");
4227
3610
  } catch {
4228
3611
  version = "";
@@ -4424,28 +3807,28 @@ async function runInstallWithPriority(config, packages, onLogLine) {
4424
3807
  const strategies = [
4425
3808
  {
4426
3809
  name: "pnpm",
4427
- run: () => runCommand3("pnpm", ["add", ...packages], {
3810
+ run: () => runCommand2("pnpm", ["add", ...packages], {
4428
3811
  timeoutMs: installStrategyTimeoutMs(),
4429
3812
  onLogLine
4430
3813
  })
4431
3814
  },
4432
3815
  {
4433
3816
  name: "pnpm-proxy",
4434
- run: () => runCommand3("pnpm", ["add", ...packages, "--registry", pnpmProxy], {
3817
+ run: () => runCommand2("pnpm", ["add", ...packages, "--registry", pnpmProxy], {
4435
3818
  timeoutMs: installStrategyTimeoutMs(),
4436
3819
  onLogLine
4437
3820
  })
4438
3821
  },
4439
3822
  {
4440
3823
  name: "npm",
4441
- run: () => runCommand3("npm", ["install", ...packages], {
3824
+ run: () => runCommand2("npm", ["install", ...packages], {
4442
3825
  timeoutMs: installStrategyTimeoutMs(),
4443
3826
  onLogLine
4444
3827
  })
4445
3828
  },
4446
3829
  {
4447
3830
  name: "npm-proxy",
4448
- run: () => runCommand3("npm", ["install", ...packages, "--registry", npmProxy], {
3831
+ run: () => runCommand2("npm", ["install", ...packages, "--registry", npmProxy], {
4449
3832
  timeoutMs: installStrategyTimeoutMs(),
4450
3833
  onLogLine
4451
3834
  })
@@ -4485,14 +3868,14 @@ async function runAppiumDriverInstallWithPriority(config, driver, onLogLine) {
4485
3868
  const strategies = [
4486
3869
  {
4487
3870
  name: "npm",
4488
- run: () => runCommand3("npm", installArgs, {
3871
+ run: () => runCommand2("npm", installArgs, {
4489
3872
  timeoutMs: installStrategyTimeoutMs(),
4490
3873
  onLogLine
4491
3874
  })
4492
3875
  },
4493
3876
  {
4494
3877
  name: "npm-proxy",
4495
- run: () => runCommand3("npm", installArgs, {
3878
+ run: () => runCommand2("npm", installArgs, {
4496
3879
  env: { npm_config_registry: npmProxy },
4497
3880
  timeoutMs: installStrategyTimeoutMs(),
4498
3881
  onLogLine
@@ -4548,7 +3931,7 @@ async function installPlaywrightBrowser(config, onLogLine) {
4548
3931
  const args = targets.length > 0 ? ["exec", "playwright", "install", ...targets] : ["exec", "playwright", "install"];
4549
3932
  const selectedHost = await detectBestPlaywrightHost(config);
4550
3933
  onLogLine?.(`[playwright] \u4E0B\u8F7D\u6D4F\u89C8\u5668\uFF08\u955C\u50CF ${selectedHost}\uFF09\uFF0C\u76EE\u6807: ${targets.length ? targets.join(",") : "all"}`);
4551
- await runCommand3("npm", args, {
3934
+ await runCommand2("npm", args, {
4552
3935
  env: { PLAYWRIGHT_DOWNLOAD_HOST: selectedHost },
4553
3936
  timeoutMs: installStrategyTimeoutMs(),
4554
3937
  onLogLine
@@ -4569,7 +3952,7 @@ async function checkPlaywrightLaunchable() {
4569
3952
  async function verifyAppiumCommand() {
4570
3953
  try {
4571
3954
  const pkgPath = require2.resolve("appium/package.json");
4572
- const raw = await import_promises7.default.readFile(pkgPath, "utf8");
3955
+ const raw = await import_promises5.default.readFile(pkgPath, "utf8");
4573
3956
  const version = String(JSON.parse(raw).version ?? "");
4574
3957
  if (!version) {
4575
3958
  throw new Error("appium version check failed");
@@ -4653,8 +4036,8 @@ function configWithPlaywrightTargets(config, targets) {
4653
4036
  };
4654
4037
  }
4655
4038
  async function ensureSeleniumNativeDrivers(config, options, onLogLine) {
4656
- const root = await resolveWorkspaceRoot3(process.cwd());
4657
- const driversDir = import_node_path9.default.resolve(root, options?.nativeDriversDir ?? config.dependencies.nativeDriversDir ?? "dirver");
4039
+ const root = await resolveWorkspaceRoot2(process.cwd());
4040
+ const driversDir = import_node_path5.default.resolve(root, options?.nativeDriversDir ?? config.dependencies.nativeDriversDir ?? "dirver");
4658
4041
  const seleniumWebdriverInstalled = hasPackage("selenium-webdriver");
4659
4042
  if (seleniumWebdriverInstalled) {
4660
4043
  onLogLine?.("[selenium] npm \u5305 selenium-webdriver \u5DF2\u5B89\u88C5");
@@ -4687,17 +4070,17 @@ async function ensureSeleniumNativeDrivers(config, options, onLogLine) {
4687
4070
  };
4688
4071
  }
4689
4072
  async function prepareInstallHomes(onLogLine) {
4690
- const root = await resolveWorkspaceRoot3(process.cwd());
4691
- const projectAndroidHome = import_node_path9.default.join(root, "android-sdk");
4692
- const projectAppiumHome = import_node_path9.default.join(root, "appium");
4073
+ const root = await resolveWorkspaceRoot2(process.cwd());
4074
+ const projectAndroidHome = import_node_path5.default.join(root, "android-sdk");
4075
+ const projectAppiumHome = import_node_path5.default.join(root, "appium");
4693
4076
  const envAndroid = (process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? "").trim();
4694
4077
  const envAppium = (process.env.APPIUM_HOME ?? "").trim();
4695
4078
  const androidHome = envAndroid || projectAndroidHome;
4696
4079
  const appiumHome = envAppium || projectAppiumHome;
4697
4080
  const usedProjectFallbackAndroid = !envAndroid;
4698
4081
  const usedProjectFallbackAppium = !envAppium;
4699
- await import_promises7.default.mkdir(androidHome, { recursive: true });
4700
- await import_promises7.default.mkdir(appiumHome, { recursive: true });
4082
+ await import_promises5.default.mkdir(androidHome, { recursive: true });
4083
+ await import_promises5.default.mkdir(appiumHome, { recursive: true });
4701
4084
  process.env.ANDROID_HOME = androidHome;
4702
4085
  process.env.ANDROID_SDK_ROOT = androidHome;
4703
4086
  process.env.APPIUM_HOME = appiumHome;
@@ -4729,8 +4112,8 @@ function resolveRequestedDrivers(config, only) {
4729
4112
  async function loadInstallState() {
4730
4113
  try {
4731
4114
  const dir = await ensureLocalDataDir(process.cwd());
4732
- const file = import_node_path9.default.join(dir, "deps-install-state.json");
4733
- const raw = await import_promises7.default.readFile(file, "utf8");
4115
+ const file = import_node_path5.default.join(dir, "deps-install-state.json");
4116
+ const raw = await import_promises5.default.readFile(file, "utf8");
4734
4117
  return JSON.parse(raw);
4735
4118
  } catch {
4736
4119
  return {};
@@ -4738,8 +4121,8 @@ async function loadInstallState() {
4738
4121
  }
4739
4122
  async function saveInstallState(state) {
4740
4123
  const dir = await ensureLocalDataDir(process.cwd());
4741
- const file = import_node_path9.default.join(dir, "deps-install-state.json");
4742
- await import_promises7.default.writeFile(file, JSON.stringify(state, null, 2), "utf8");
4124
+ const file = import_node_path5.default.join(dir, "deps-install-state.json");
4125
+ await import_promises5.default.writeFile(file, JSON.stringify(state, null, 2), "utf8");
4743
4126
  }
4744
4127
  async function ensureDriverDependencies(config, options) {
4745
4128
  const startedAt = Date.now();
@@ -4871,8 +4254,8 @@ async function ensureDriverDependencies(config, options) {
4871
4254
  let chromedriverPath;
4872
4255
  let availableChromedriverMajors;
4873
4256
  if (needSelenium) {
4874
- const root = await resolveWorkspaceRoot3(process.cwd());
4875
- nativeDriversDir = import_node_path9.default.resolve(root, options?.nativeDriversDir ?? config.dependencies.nativeDriversDir ?? "dirver");
4257
+ const root = await resolveWorkspaceRoot2(process.cwd());
4258
+ nativeDriversDir = import_node_path5.default.resolve(root, options?.nativeDriversDir ?? config.dependencies.nativeDriversDir ?? "dirver");
4876
4259
  availableChromedriverMajors = await listLocalChromedriverVersions(nativeDriversDir);
4877
4260
  const resolved = await resolveNativeDrivers({
4878
4261
  workspaceRoot: root,
@@ -4911,50 +4294,270 @@ async function getDependencyHealth(config) {
4911
4294
  if (playwrightInstalled) {
4912
4295
  playwrightLaunchOk = await checkPlaywrightLaunchable();
4913
4296
  }
4914
- if (appiumInstalled) {
4915
- try {
4916
- const pkgPath = require2.resolve("appium/package.json");
4917
- const raw = await import_promises7.default.readFile(pkgPath, "utf8");
4918
- const version = String(JSON.parse(raw).version ?? "");
4919
- appiumCliOk = version.length > 0;
4920
- } catch {
4921
- appiumCliOk = false;
4297
+ if (appiumInstalled) {
4298
+ try {
4299
+ const pkgPath = require2.resolve("appium/package.json");
4300
+ const raw = await import_promises5.default.readFile(pkgPath, "utf8");
4301
+ const version = String(JSON.parse(raw).version ?? "");
4302
+ appiumCliOk = version.length > 0;
4303
+ } catch {
4304
+ appiumCliOk = false;
4305
+ }
4306
+ if (appiumCliOk) {
4307
+ const installed = (await getInstalledAppiumDrivers()).map((x) => x.toLowerCase());
4308
+ const required = config?.appium?.requiredDrivers && config.appium.requiredDrivers.length > 0 ? config.appium.requiredDrivers.map((x) => x.toLowerCase()) : ["uiautomator2", "xcuitest", "harmonyos"];
4309
+ missingAppiumDrivers = required.filter((x) => !installed.includes(x));
4310
+ appiumDriversOk = missingAppiumDrivers.length === 0;
4311
+ }
4312
+ }
4313
+ const root = await resolveWorkspaceRoot2(process.cwd());
4314
+ const driversDir = await resolveNativeDriversDir(root);
4315
+ const native = await resolveNativeDrivers({ workspaceRoot: root, driversDir });
4316
+ const seleniumWebdriverInstalled = hasPackage("selenium-webdriver");
4317
+ const geckodriverOk = native.geckodriverOk;
4318
+ const chromedriverOk = native.chromedriverOk;
4319
+ return {
4320
+ playwrightInstalled,
4321
+ playwrightLaunchOk,
4322
+ seleniumWebdriverInstalled,
4323
+ geckodriverOk,
4324
+ chromedriverOk,
4325
+ appiumInstalled,
4326
+ appiumCliOk,
4327
+ appiumDriversOk,
4328
+ missingAppiumDrivers
4329
+ };
4330
+ }
4331
+
4332
+ // ../ada-agent/dist/doctor.js
4333
+ var import_promises6 = __toESM(require("node:fs/promises"), 1);
4334
+ var import_node_net = __toESM(require("node:net"), 1);
4335
+ var import_node_path7 = __toESM(require("node:path"), 1);
4336
+ var import_node_url = require("node:url");
4337
+ var import_node_child_process3 = require("node:child_process");
4338
+
4339
+ // ../../packages/driver-rpc/src/index.ts
4340
+ function parseWebEngineFromPayload(payload) {
4341
+ const p = asRecord(payload);
4342
+ const options = asRecord(p.options);
4343
+ const raw = (getString(p.engine) ?? getString(options.engine) ?? "playwright").toLowerCase();
4344
+ if (raw === "selenium") {
4345
+ return "selenium";
4346
+ }
4347
+ return "playwright";
4348
+ }
4349
+ function manifestWebEngine(manifest) {
4350
+ if (manifest.engine === "selenium") {
4351
+ return "selenium";
4352
+ }
4353
+ return "playwright";
4354
+ }
4355
+ function asRecord(value) {
4356
+ return typeof value === "object" && value !== null ? value : {};
4357
+ }
4358
+ function getString(value) {
4359
+ return typeof value === "string" && value.length > 0 ? value : void 0;
4360
+ }
4361
+
4362
+ // ../../packages/plugin-host/src/index.ts
4363
+ var import_node_fs = __toESM(require("node:fs"), 1);
4364
+ var import_node_path6 = __toESM(require("node:path"), 1);
4365
+ var import_node_module2 = require("node:module");
4366
+ function assertManifest(plugin) {
4367
+ const m = plugin.manifest;
4368
+ if (!m.id || !m.version) {
4369
+ throw new Error("Invalid plugin manifest: missing id/version");
4370
+ }
4371
+ if (!Array.isArray(m.platforms) || m.platforms.length === 0) {
4372
+ throw new Error(`Invalid plugin manifest (${m.id}): platforms is empty`);
4373
+ }
4374
+ if (!Array.isArray(m.capabilities)) {
4375
+ throw new Error(`Invalid plugin manifest (${m.id}): capabilities must be array`);
4376
+ }
4377
+ if (!/^\d+\.\d+\.\d+/.test(m.version)) {
4378
+ throw new Error(`Invalid plugin manifest (${m.id}): version must look like semver`);
4379
+ }
4380
+ }
4381
+ var PluginHost = class {
4382
+ plugins = /* @__PURE__ */ new Map();
4383
+ webEngines = /* @__PURE__ */ new Map();
4384
+ manifests = /* @__PURE__ */ new Map();
4385
+ pluginById = /* @__PURE__ */ new Map();
4386
+ initializedPluginIds = /* @__PURE__ */ new Set();
4387
+ register(plugin) {
4388
+ assertManifest(plugin);
4389
+ if (this.manifests.has(plugin.manifest.id)) {
4390
+ throw new Error(`Plugin already registered: ${plugin.manifest.id}`);
4391
+ }
4392
+ const mobilePlatforms = plugin.manifest.platforms.filter((p) => p !== "web");
4393
+ if (plugin.manifest.platforms.includes("web")) {
4394
+ const engine = manifestWebEngine(plugin.manifest);
4395
+ if (this.webEngines.has(engine)) {
4396
+ throw new Error(`Web engine already registered: ${engine} (reject ${plugin.manifest.id})`);
4397
+ }
4398
+ this.webEngines.set(engine, plugin);
4399
+ }
4400
+ for (const platform of mobilePlatforms) {
4401
+ if (this.plugins.has(platform)) {
4402
+ throw new Error(`Platform already has plugin (${platform}), reject: ${plugin.manifest.id}`);
4403
+ }
4404
+ this.plugins.set(platform, plugin);
4405
+ }
4406
+ this.manifests.set(plugin.manifest.id, plugin.manifest);
4407
+ this.pluginById.set(plugin.manifest.id, plugin);
4408
+ }
4409
+ registerWebEngine(plugin) {
4410
+ this.register(plugin);
4411
+ }
4412
+ /** Resolve driver for a command (web routes by payload.engine). */
4413
+ resolve(command) {
4414
+ if (command.platform === "web") {
4415
+ const engine = parseWebEngineFromPayload(command.payload);
4416
+ const plugin2 = this.webEngines.get(engine);
4417
+ if (!plugin2) {
4418
+ if (engine === "selenium") {
4419
+ throw new Error(
4420
+ "WEB_ENGINE_SELENIUM_NOT_INSTALLED: register @ada/driver-selenium and ensure GeckoDriver/ChromeDriver is on PATH"
4421
+ );
4422
+ }
4423
+ throw new Error(`WEB_ENGINE_UNKNOWN: ${engine}`);
4424
+ }
4425
+ return plugin2;
4426
+ }
4427
+ const plugin = this.plugins.get(command.platform);
4428
+ if (!plugin) {
4429
+ throw new Error(`No plugin registered for platform: ${command.platform}`);
4430
+ }
4431
+ return plugin;
4432
+ }
4433
+ /** Legacy: mobile platforms only; web defaults to playwright. */
4434
+ resolvePlatform(platform) {
4435
+ if (platform === "web") {
4436
+ const plugin = this.webEngines.get("playwright");
4437
+ if (!plugin) {
4438
+ throw new Error("No web engine registered (playwright)");
4439
+ }
4440
+ return plugin;
4441
+ }
4442
+ return this.resolve({ requestId: "", sessionId: "", platform, command: "navigate" });
4443
+ }
4444
+ listWebEngines() {
4445
+ return Array.from(this.webEngines.keys());
4446
+ }
4447
+ listManifests() {
4448
+ return Array.from(this.manifests.values());
4449
+ }
4450
+ async ensureInitialized(pluginId, timeoutMs = 15e3) {
4451
+ if (this.initializedPluginIds.has(pluginId)) {
4452
+ return;
4453
+ }
4454
+ const plugin = this.pluginById.get(pluginId);
4455
+ if (!plugin) {
4456
+ throw new Error(`Plugin not registered: ${pluginId}`);
4457
+ }
4458
+ await Promise.race([
4459
+ plugin.init(),
4460
+ new Promise(
4461
+ (_, reject) => setTimeout(() => reject(new Error(`Plugin init timeout: ${pluginId}`)), timeoutMs)
4462
+ )
4463
+ ]);
4464
+ this.initializedPluginIds.add(pluginId);
4465
+ }
4466
+ async healthCheck(timeoutMs = 5e3) {
4467
+ const items = [];
4468
+ for (const [id] of this.manifests) {
4469
+ try {
4470
+ await this.ensureInitialized(id, timeoutMs);
4471
+ items.push({ id, ok: true, message: "healthy" });
4472
+ } catch (error) {
4473
+ items.push({
4474
+ id,
4475
+ ok: false,
4476
+ message: error instanceof Error ? error.message : String(error)
4477
+ });
4478
+ }
4479
+ }
4480
+ return items;
4481
+ }
4482
+ };
4483
+ var DEFAULT_PLUGIN_MODULE_IDS = [
4484
+ "@ada/driver-playwright",
4485
+ "@ada/driver-appium",
4486
+ "@ada/driver-selenium"
4487
+ ];
4488
+ function isPluginModule(mod) {
4489
+ if (!mod || typeof mod !== "object") {
4490
+ return false;
4491
+ }
4492
+ const m = mod.manifest;
4493
+ return !!m && typeof m.id === "string" && Array.isArray(m.platforms);
4494
+ }
4495
+ function resolvePluginDirs(explicitPluginDir) {
4496
+ const fromEnv = process.env.ADA_PLUGIN_DIR?.trim();
4497
+ const execDir = import_node_path6.default.dirname(process.execPath);
4498
+ const cwd = process.cwd();
4499
+ return Array.from(
4500
+ new Set(
4501
+ [fromEnv, explicitPluginDir, import_node_path6.default.join(execDir, "plugins"), import_node_path6.default.join(cwd, "plugins"), import_node_path6.default.join(cwd, "release", "plugins")].filter((x) => Boolean(x && x.trim())).map((x) => import_node_path6.default.resolve(x))
4502
+ )
4503
+ );
4504
+ }
4505
+ function loadPluginFromModule(requireFn, moduleId) {
4506
+ try {
4507
+ const loaded = requireFn(moduleId);
4508
+ const plugin = loaded?.default ?? loaded;
4509
+ return isPluginModule(plugin) ? plugin : null;
4510
+ } catch {
4511
+ return null;
4512
+ }
4513
+ }
4514
+ function registerPluginsFromDirectory(host, pluginDir) {
4515
+ if (!import_node_fs.default.existsSync(pluginDir)) {
4516
+ return [];
4517
+ }
4518
+ 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));
4519
+ if (entries.length === 0) {
4520
+ return [];
4521
+ }
4522
+ const loaded = [];
4523
+ for (const file of entries) {
4524
+ const requireFn = (0, import_node_module2.createRequire)(file);
4525
+ const plugin = loadPluginFromModule(requireFn, file);
4526
+ if (!plugin) {
4527
+ continue;
4922
4528
  }
4923
- if (appiumCliOk) {
4924
- const installed = (await getInstalledAppiumDrivers()).map((x) => x.toLowerCase());
4925
- const required = config?.appium?.requiredDrivers && config.appium.requiredDrivers.length > 0 ? config.appium.requiredDrivers.map((x) => x.toLowerCase()) : ["uiautomator2", "xcuitest", "harmonyos"];
4926
- missingAppiumDrivers = required.filter((x) => !installed.includes(x));
4927
- appiumDriversOk = missingAppiumDrivers.length === 0;
4529
+ host.register(plugin);
4530
+ loaded.push(plugin.manifest);
4531
+ }
4532
+ return loaded;
4533
+ }
4534
+ function registerPluginsFromModuleIds(host, moduleIds) {
4535
+ const req = (0, import_node_module2.createRequire)(typeof __filename === "string" ? __filename : process.cwd());
4536
+ const loaded = [];
4537
+ for (const moduleId of moduleIds) {
4538
+ const plugin = loadPluginFromModule(req, moduleId);
4539
+ if (!plugin) {
4540
+ continue;
4928
4541
  }
4542
+ host.register(plugin);
4543
+ loaded.push(plugin.manifest);
4929
4544
  }
4930
- const root = await resolveWorkspaceRoot3(process.cwd());
4931
- const driversDir = await resolveNativeDriversDir(root);
4932
- const native = await resolveNativeDrivers({ workspaceRoot: root, driversDir });
4933
- const seleniumWebdriverInstalled = hasPackage("selenium-webdriver");
4934
- const geckodriverOk = native.geckodriverOk;
4935
- const chromedriverOk = native.chromedriverOk;
4936
- return {
4937
- playwrightInstalled,
4938
- playwrightLaunchOk,
4939
- seleniumWebdriverInstalled,
4940
- geckodriverOk,
4941
- chromedriverOk,
4942
- appiumInstalled,
4943
- appiumCliOk,
4944
- appiumDriversOk,
4945
- missingAppiumDrivers
4946
- };
4545
+ return loaded;
4546
+ }
4547
+ function registerRuntimePlugins(host, options) {
4548
+ const manifests2 = [];
4549
+ for (const pluginDir of resolvePluginDirs(options?.pluginDir)) {
4550
+ manifests2.push(...registerPluginsFromDirectory(host, pluginDir));
4551
+ }
4552
+ if (manifests2.length > 0) {
4553
+ return manifests2;
4554
+ }
4555
+ const fallbackModuleIds = options?.moduleIds?.length ? options.moduleIds : DEFAULT_PLUGIN_MODULE_IDS;
4556
+ return registerPluginsFromModuleIds(host, fallbackModuleIds);
4947
4557
  }
4948
-
4949
- // ../ada-agent/dist/doctor.js
4950
- var import_promises8 = __toESM(require("node:fs/promises"), 1);
4951
- var import_node_net = __toESM(require("node:net"), 1);
4952
- var import_node_path10 = __toESM(require("node:path"), 1);
4953
- var import_node_url2 = require("node:url");
4954
- var import_node_child_process3 = require("node:child_process");
4955
4558
 
4956
4559
  // ../ada-agent/dist/plugin-registry.js
4957
- function buildPluginHost2() {
4560
+ function buildPluginHost() {
4958
4561
  const host = new PluginHost();
4959
4562
  registerRuntimePlugins(host);
4960
4563
  return host;
@@ -4967,7 +4570,7 @@ function listBuiltInPluginManifests() {
4967
4570
  // ../ada-agent/dist/doctor.js
4968
4571
  async function dirExists2(dirPath) {
4969
4572
  try {
4970
- const stat = await import_promises8.default.stat(dirPath);
4573
+ const stat = await import_promises6.default.stat(dirPath);
4971
4574
  return stat.isDirectory();
4972
4575
  } catch {
4973
4576
  return false;
@@ -4984,11 +4587,11 @@ async function isPortAvailable(host, port) {
4984
4587
  });
4985
4588
  }
4986
4589
  async function runDoctor(config) {
4987
- const root = await resolveWorkspaceRoot3(process.cwd());
4590
+ const root = await resolveWorkspaceRoot2(process.cwd());
4988
4591
  const queue = {
4989
- inboxDir: import_node_path10.default.resolve(root, config.queue.inboxDir),
4990
- processedDir: import_node_path10.default.resolve(root, config.queue.processedDir),
4991
- failedDir: import_node_path10.default.resolve(root, config.queue.failedDir)
4592
+ inboxDir: import_node_path7.default.resolve(root, config.queue.inboxDir),
4593
+ processedDir: import_node_path7.default.resolve(root, config.queue.processedDir),
4594
+ failedDir: import_node_path7.default.resolve(root, config.queue.failedDir)
4992
4595
  };
4993
4596
  const deps = await getDependencyHealth(config);
4994
4597
  const portFree = await isPortAvailable(config.bootstrapUI.host, config.bootstrapUI.port);
@@ -5099,7 +4702,7 @@ async function checkNativeBootstrap(config, root) {
5099
4702
  const hasPathHint = native.command.includes("/") || native.command.includes("\\") || native.command.startsWith(".");
5100
4703
  let reachable = false;
5101
4704
  if (hasPathHint) {
5102
- const resolved = import_node_path10.default.resolve(root, native.command);
4705
+ const resolved = import_node_path7.default.resolve(root, native.command);
5103
4706
  reachable = await fileExists2(resolved);
5104
4707
  } else {
5105
4708
  reachable = await commandExists(native.command);
@@ -5114,7 +4717,7 @@ async function checkNativeBootstrap(config, root) {
5114
4717
  }
5115
4718
  async function fileExists2(targetPath) {
5116
4719
  try {
5117
- await import_promises8.default.access(targetPath);
4720
+ await import_promises6.default.access(targetPath);
5118
4721
  return true;
5119
4722
  } catch {
5120
4723
  return false;
@@ -5122,7 +4725,7 @@ async function fileExists2(targetPath) {
5122
4725
  }
5123
4726
  async function checkAppiumServer(serverUrl) {
5124
4727
  try {
5125
- const parsed = new import_node_url2.URL(serverUrl);
4728
+ const parsed = new import_node_url.URL(serverUrl);
5126
4729
  const port = Number(parsed.port || (parsed.protocol === "https:" ? 443 : 80));
5127
4730
  const host = parsed.hostname;
5128
4731
  const reachable = !await isPortAvailable(host, port);
@@ -5141,7 +4744,7 @@ async function checkAppiumServer(serverUrl) {
5141
4744
  }
5142
4745
 
5143
4746
  // ../ada-agent/dist/setup-cli.js
5144
- var import_promises9 = __toESM(require("node:readline/promises"), 1);
4747
+ var import_promises7 = __toESM(require("node:readline/promises"), 1);
5145
4748
  var import_node_process = require("node:process");
5146
4749
  function parseTags(raw) {
5147
4750
  return raw.split(",").map((s) => s.trim()).filter(Boolean);
@@ -5153,7 +4756,7 @@ function ensureServerUrl(url) {
5153
4756
  return url;
5154
4757
  }
5155
4758
  async function runSetupCli() {
5156
- const rl = import_promises9.default.createInterface({ input: import_node_process.stdin, output: import_node_process.stdout });
4759
+ const rl = import_promises7.default.createInterface({ input: import_node_process.stdin, output: import_node_process.stdout });
5157
4760
  try {
5158
4761
  console.log("[ADA-AGENT] setup wizard (CLI mode)");
5159
4762
  const serverUrl = ensureServerUrl((await rl.question("Server URL (e.g. https://ada-control.example.com): ")).trim());
@@ -5250,7 +4853,7 @@ async function runSetupNative(config) {
5250
4853
  // ../ada-agent/dist/bootstrap-ui.js
5251
4854
  var import_node_http = __toESM(require("node:http"), 1);
5252
4855
  var import_node_crypto = require("node:crypto");
5253
- var import_node_url3 = require("node:url");
4856
+ var import_node_url2 = require("node:url");
5254
4857
 
5255
4858
  // ../ada-agent/dist/setup-state.js
5256
4859
  var PW_TARGET = /* @__PURE__ */ new Set([
@@ -5568,7 +5171,7 @@ function htmlPage(csrfToken, port, host) {
5568
5171
  </html>`;
5569
5172
  }
5570
5173
  function parseFormUrlEncoded(body) {
5571
- const form = new import_node_url3.URLSearchParams(body);
5174
+ const form = new import_node_url2.URLSearchParams(body);
5572
5175
  const deviceTags = (form.get("deviceTags") ?? "").split(",").map((tag) => tag.trim()).filter(Boolean);
5573
5176
  const pw = form.getAll("pw").map((x) => String(x).toLowerCase());
5574
5177
  const rtRaw = form.get("requestTimeoutMs");
@@ -5758,7 +5361,7 @@ async function runSetupUi(config) {
5758
5361
  });
5759
5362
  req.on("end", () => {
5760
5363
  void (async () => {
5761
- const form = new import_node_url3.URLSearchParams(body);
5364
+ const form = new import_node_url2.URLSearchParams(body);
5762
5365
  if (form.get("csrf") !== csrfToken) {
5763
5366
  res.writeHead(403);
5764
5367
  res.end("invalid csrf");
@@ -6131,12 +5734,12 @@ async function createRuntimeTransport(config, secret) {
6131
5734
  }
6132
5735
 
6133
5736
  // ../ada-agent/dist/queue-runner.js
6134
- var import_promises12 = __toESM(require("node:fs/promises"), 1);
6135
- var import_node_path12 = __toESM(require("node:path"), 1);
5737
+ var import_promises10 = __toESM(require("node:fs/promises"), 1);
5738
+ var import_node_path9 = __toESM(require("node:path"), 1);
6136
5739
 
6137
5740
  // ../ada-agent/dist/task-loader.js
6138
- var import_promises10 = __toESM(require("node:fs/promises"), 1);
6139
- function isObject4(value) {
5741
+ var import_promises8 = __toESM(require("node:fs/promises"), 1);
5742
+ function isObject3(value) {
6140
5743
  return typeof value === "object" && value !== null;
6141
5744
  }
6142
5745
  var allowedCommands = /* @__PURE__ */ new Set([
@@ -6168,44 +5771,286 @@ var allowedCommands = /* @__PURE__ */ new Set([
6168
5771
  "closeTab"
6169
5772
  ]);
6170
5773
  function assertTask(value, index) {
6171
- if (!isObject4(value)) {
5774
+ if (!isObject3(value)) {
6172
5775
  throw new Error(`Task[${index}] is not an object.`);
6173
5776
  }
6174
- const requestId = value.requestId;
6175
- const sessionId = value.sessionId;
6176
- const platform = value.platform;
6177
- const command = value.command;
6178
- if (typeof requestId !== "string" || typeof sessionId !== "string") {
6179
- throw new Error(`Task[${index}] requestId/sessionId must be string.`);
5777
+ const requestId = value.requestId;
5778
+ const sessionId = value.sessionId;
5779
+ const platform = value.platform;
5780
+ const command = value.command;
5781
+ if (typeof requestId !== "string" || typeof sessionId !== "string") {
5782
+ throw new Error(`Task[${index}] requestId/sessionId must be string.`);
5783
+ }
5784
+ if (platform !== "web" && platform !== "android" && platform !== "ios" && platform !== "harmony") {
5785
+ throw new Error(`Task[${index}] platform invalid: ${String(platform)}`);
5786
+ }
5787
+ if (typeof command !== "string" || !allowedCommands.has(command)) {
5788
+ throw new Error(`Task[${index}] command invalid: ${String(command)}`);
5789
+ }
5790
+ return {
5791
+ requestId,
5792
+ sessionId,
5793
+ platform,
5794
+ command,
5795
+ payload: isObject3(value.payload) ? value.payload : void 0,
5796
+ idempotencyKey: typeof value.idempotencyKey === "string" ? value.idempotencyKey : void 0
5797
+ };
5798
+ }
5799
+ async function loadTaskFile(filePath) {
5800
+ const raw = await import_promises8.default.readFile(filePath, "utf8");
5801
+ const parsed = JSON.parse(raw);
5802
+ if (!Array.isArray(parsed)) {
5803
+ throw new Error("Task file must be a JSON array.");
5804
+ }
5805
+ return parsed.map((item, idx) => assertTask(item, idx));
5806
+ }
5807
+
5808
+ // ../../packages/core-kernel/src/index.ts
5809
+ var DriverSessionManager = class {
5810
+ sessions = /* @__PURE__ */ new Map();
5811
+ sessionKey(command) {
5812
+ if (command.platform === "web") {
5813
+ const engine = parseWebEngineFromPayload(command.payload);
5814
+ return `web:${engine}:${command.sessionId}`;
5815
+ }
5816
+ return `${command.platform}:${command.sessionId}`;
5817
+ }
5818
+ async getOrCreate(plugin, command) {
5819
+ const key = this.sessionKey(command);
5820
+ const existed = this.sessions.get(key);
5821
+ if (existed) {
5822
+ return existed;
5823
+ }
5824
+ const created = await plugin.createSession(command.platform);
5825
+ this.sessions.set(key, created);
5826
+ return created;
5827
+ }
5828
+ get(command) {
5829
+ const key = this.sessionKey(command);
5830
+ return this.sessions.get(key);
5831
+ }
5832
+ list() {
5833
+ const items = [];
5834
+ for (const [key, session] of this.sessions.entries()) {
5835
+ const parts = key.split(":");
5836
+ if (parts[0] === "web" && parts.length >= 3) {
5837
+ items.push({
5838
+ platform: "web",
5839
+ engine: parts[1],
5840
+ sessionId: parts.slice(2).join(":"),
5841
+ driverSessionId: session.id
5842
+ });
5843
+ continue;
5844
+ }
5845
+ const idx = key.indexOf(":");
5846
+ if (idx <= 0) {
5847
+ continue;
5848
+ }
5849
+ items.push({
5850
+ platform: key.slice(0, idx),
5851
+ sessionId: key.slice(idx + 1),
5852
+ driverSessionId: session.id
5853
+ });
5854
+ }
5855
+ return items;
5856
+ }
5857
+ clear(command) {
5858
+ const key = this.sessionKey(command);
5859
+ const existed = this.sessions.get(key);
5860
+ this.sessions.delete(key);
5861
+ return existed;
5862
+ }
5863
+ clearByPlatformSession(platform, sessionId, options) {
5864
+ if (platform === "web") {
5865
+ const engine = options?.engine ?? parseWebEngineFromPayload(options?.payload);
5866
+ const key2 = `web:${engine}:${sessionId}`;
5867
+ const existed2 = this.sessions.get(key2);
5868
+ this.sessions.delete(key2);
5869
+ return existed2;
5870
+ }
5871
+ const key = `${platform}:${sessionId}`;
5872
+ const existed = this.sessions.get(key);
5873
+ this.sessions.delete(key);
5874
+ return existed;
5875
+ }
5876
+ clearWebSession(sessionId, engine) {
5877
+ const key = `web:${engine}:${sessionId}`;
5878
+ const existed = this.sessions.get(key);
5879
+ this.sessions.delete(key);
5880
+ return existed;
5881
+ }
5882
+ clearAll() {
5883
+ const all = Array.from(this.sessions.values());
5884
+ this.sessions.clear();
5885
+ return all;
5886
+ }
5887
+ };
5888
+ var RetryPolicyEngine = class {
5889
+ constructor(maxAttempts, retryableErrorCodes) {
5890
+ this.maxAttempts = maxAttempts;
5891
+ this.retryableErrorCodes = retryableErrorCodes;
5892
+ }
5893
+ shouldRetry(result, attempt) {
5894
+ if (attempt >= this.maxAttempts) {
5895
+ return false;
5896
+ }
5897
+ if (result.success) {
5898
+ return false;
5899
+ }
5900
+ if (!result.errorCode) {
5901
+ return true;
5902
+ }
5903
+ return this.retryableErrorCodes.has(result.errorCode);
5904
+ }
5905
+ };
5906
+ var ResultAssembler = class {
5907
+ success(requestId, data) {
5908
+ return {
5909
+ requestId,
5910
+ success: true,
5911
+ data
5912
+ };
5913
+ }
5914
+ failure(requestId, errorCode, errorMessage) {
5915
+ return {
5916
+ requestId,
5917
+ success: false,
5918
+ errorCode,
5919
+ errorMessage
5920
+ };
5921
+ }
5922
+ normalize(requestId, result) {
5923
+ return {
5924
+ requestId,
5925
+ success: Boolean(result.success),
5926
+ data: result.data,
5927
+ errorCode: result.errorCode,
5928
+ errorMessage: result.errorMessage
5929
+ };
5930
+ }
5931
+ };
5932
+ var FeatureNegotiator = class {
5933
+ normalize(command) {
5934
+ if (command === "click") {
5935
+ return "tap";
5936
+ }
5937
+ return command;
5938
+ }
5939
+ check(plugin, command) {
5940
+ const expected = this.normalize(command.command);
5941
+ const declared = new Set(plugin.manifest.capabilities.map((item) => this.normalize(item)));
5942
+ if (declared.has(expected)) {
5943
+ return { ok: true };
5944
+ }
5945
+ return {
5946
+ ok: false,
5947
+ code: "DRIVER_CAPABILITY_UNSUPPORTED",
5948
+ message: `Plugin ${plugin.manifest.id} does not support command ${command.command} on platform ${command.platform}`
5949
+ };
5950
+ }
5951
+ };
5952
+ var TaskExecutor = class {
5953
+ constructor(pluginHost, options = {}) {
5954
+ this.pluginHost = pluginHost;
5955
+ this.maxAttempts = Math.max(1, options.maxAttempts ?? 2);
5956
+ this.retry = new RetryPolicyEngine(
5957
+ this.maxAttempts,
5958
+ new Set(options.retryableErrorCodes ?? ["TRANSIENT_DRIVER_ERROR", "NETWORK_TIMEOUT"])
5959
+ );
5960
+ }
5961
+ retry;
5962
+ maxAttempts;
5963
+ sessions = new DriverSessionManager();
5964
+ resultAssembler = new ResultAssembler();
5965
+ featureNegotiator = new FeatureNegotiator();
5966
+ async resolveContext(command) {
5967
+ const plugin = this.pluginHost.resolve(command);
5968
+ await this.pluginHost.ensureInitialized(plugin.manifest.id);
5969
+ const session = await this.sessions.getOrCreate(plugin, command);
5970
+ return { plugin, session };
6180
5971
  }
6181
- if (platform !== "web" && platform !== "android" && platform !== "ios" && platform !== "harmony") {
6182
- throw new Error(`Task[${index}] platform invalid: ${String(platform)}`);
5972
+ async execute(command) {
5973
+ let attempt = 0;
5974
+ let lastFailure = this.resultAssembler.failure(command.requestId, "KERNEL_EXECUTION_FAILED", "unknown error");
5975
+ while (attempt < this.maxAttempts) {
5976
+ attempt += 1;
5977
+ try {
5978
+ const context = await this.resolveContext(command);
5979
+ const feature = this.featureNegotiator.check(context.plugin, command);
5980
+ if (!feature.ok) {
5981
+ return this.resultAssembler.failure(command.requestId, feature.code, feature.message);
5982
+ }
5983
+ const result = await context.plugin.execute(context.session, command);
5984
+ const normalized = this.resultAssembler.normalize(command.requestId, result);
5985
+ if (this.retry.shouldRetry(normalized, attempt)) {
5986
+ this.sessions.clear(command);
5987
+ lastFailure = normalized;
5988
+ continue;
5989
+ }
5990
+ return normalized;
5991
+ } catch (error) {
5992
+ const message = error instanceof Error ? error.message : String(error);
5993
+ const failed = this.resultAssembler.failure(command.requestId, "KERNEL_EXECUTION_FAILED", message);
5994
+ if (this.retry.shouldRetry(failed, attempt)) {
5995
+ this.sessions.clear(command);
5996
+ lastFailure = failed;
5997
+ continue;
5998
+ }
5999
+ return failed;
6000
+ }
6001
+ }
6002
+ return lastFailure;
6183
6003
  }
6184
- if (typeof command !== "string" || !allowedCommands.has(command)) {
6185
- throw new Error(`Task[${index}] command invalid: ${String(command)}`);
6004
+ listSessions() {
6005
+ return this.sessions.list();
6186
6006
  }
6187
- return {
6188
- requestId,
6189
- sessionId,
6190
- platform,
6191
- command,
6192
- payload: isObject4(value.payload) ? value.payload : void 0,
6193
- idempotencyKey: typeof value.idempotencyKey === "string" ? value.idempotencyKey : void 0
6194
- };
6195
- }
6196
- async function loadTaskFile(filePath) {
6197
- const raw = await import_promises10.default.readFile(filePath, "utf8");
6198
- const parsed = JSON.parse(raw);
6199
- if (!Array.isArray(parsed)) {
6200
- throw new Error("Task file must be a JSON array.");
6007
+ async closeSession(platform, sessionId, options) {
6008
+ const plat = platform;
6009
+ const engine = plat === "web" ? options?.engine ?? parseWebEngineFromPayload(options?.payload) : void 0;
6010
+ const plugin = plat === "web" ? this.pluginHost.resolve({
6011
+ requestId: "",
6012
+ sessionId,
6013
+ platform: "web",
6014
+ command: "navigate",
6015
+ payload: { engine, ...options?.payload }
6016
+ }) : this.pluginHost.resolve({
6017
+ requestId: "",
6018
+ sessionId,
6019
+ platform: plat,
6020
+ command: "navigate"
6021
+ });
6022
+ const session = this.sessions.clearByPlatformSession(platform, sessionId, {
6023
+ engine,
6024
+ payload: options?.payload
6025
+ });
6026
+ if (!session) {
6027
+ return false;
6028
+ }
6029
+ if (plugin.destroySession) {
6030
+ await plugin.destroySession(session);
6031
+ }
6032
+ return true;
6201
6033
  }
6202
- return parsed.map((item, idx) => assertTask(item, idx));
6203
- }
6034
+ async closeAllSessions() {
6035
+ const all = this.sessions.list();
6036
+ let closed = 0;
6037
+ for (const item of all) {
6038
+ const ok = await this.closeSession(item.platform, item.sessionId, {
6039
+ engine: item.engine,
6040
+ payload: item.engine ? { engine: item.engine } : void 0
6041
+ });
6042
+ if (ok) {
6043
+ closed += 1;
6044
+ }
6045
+ }
6046
+ return closed;
6047
+ }
6048
+ };
6204
6049
 
6205
6050
  // ../ada-agent/dist/monitoring.js
6206
- var import_promises11 = __toESM(require("node:fs/promises"), 1);
6207
- var import_node_path11 = __toESM(require("node:path"), 1);
6208
- var import_jimp2 = require("jimp");
6051
+ var import_promises9 = __toESM(require("node:fs/promises"), 1);
6052
+ var import_node_path8 = __toESM(require("node:path"), 1);
6053
+ var import_jimp = require("jimp");
6209
6054
  function shouldMonitorCommand(command, config, index) {
6210
6055
  if (!config.monitoring.enabled) {
6211
6056
  return false;
@@ -6241,16 +6086,16 @@ async function executeMonitorScreenshot(command, context) {
6241
6086
  }
6242
6087
  return null;
6243
6088
  }
6244
- function getScreenshotPath2(result) {
6089
+ function getScreenshotPath(result) {
6245
6090
  const pathValue = result.data?.screenshot;
6246
6091
  return typeof pathValue === "string" ? pathValue : null;
6247
6092
  }
6248
6093
  function buildMonitorOutputPath(config, sourcePath, requestId, sessionId) {
6249
- const ext = import_node_path11.default.extname(sourcePath) || ".png";
6094
+ const ext = import_node_path8.default.extname(sourcePath) || ".png";
6250
6095
  if (config.monitoring.groupBySession) {
6251
- return import_node_path11.default.join(config.monitoring.outputDir, sessionId, `${requestId}${ext}`);
6096
+ return import_node_path8.default.join(config.monitoring.outputDir, sessionId, `${requestId}${ext}`);
6252
6097
  }
6253
- return import_node_path11.default.join(config.monitoring.outputDir, `${requestId}${ext}`);
6098
+ return import_node_path8.default.join(config.monitoring.outputDir, `${requestId}${ext}`);
6254
6099
  }
6255
6100
  async function captureOperationMonitor(command, result, index, context) {
6256
6101
  if (!shouldMonitorCommand(command, context.config, index)) {
@@ -6267,16 +6112,16 @@ async function captureOperationMonitor(command, result, index, context) {
6267
6112
  });
6268
6113
  return;
6269
6114
  }
6270
- const sourcePath = getScreenshotPath2(monitorResult);
6115
+ const sourcePath = getScreenshotPath(monitorResult);
6271
6116
  if (!sourcePath) {
6272
6117
  return;
6273
6118
  }
6274
6119
  const targetPath = buildMonitorOutputPath(context.config, sourcePath, command.requestId, command.sessionId);
6275
- await import_promises11.default.mkdir(import_node_path11.default.dirname(targetPath), { recursive: true });
6120
+ await import_promises9.default.mkdir(import_node_path8.default.dirname(targetPath), { recursive: true });
6276
6121
  const { maxWidth, maxHeight, keepAspectRatio } = context.config.monitoring.resolution;
6277
6122
  const mw = Math.max(1, maxWidth);
6278
6123
  const mh = Math.max(1, maxHeight);
6279
- const image = await import_jimp2.Jimp.read(sourcePath);
6124
+ const image = await import_jimp.Jimp.read(sourcePath);
6280
6125
  if (keepAspectRatio) {
6281
6126
  image.scaleToFit({ w: mw, h: mh });
6282
6127
  } else {
@@ -6298,8 +6143,8 @@ async function captureOperationMonitor(command, result, index, context) {
6298
6143
  }
6299
6144
 
6300
6145
  // ../ada-agent/dist/runtime.js
6301
- async function runTaskset2(commands, options = {}) {
6302
- const executor = new TaskExecutor(buildPluginHost2());
6146
+ async function runTaskset(commands, options = {}) {
6147
+ const executor = new TaskExecutor(buildPluginHost());
6303
6148
  const results = [];
6304
6149
  const monitorJobs = [];
6305
6150
  for (const cmd of commands) {
@@ -6331,7 +6176,7 @@ async function runTaskset2(commands, options = {}) {
6331
6176
  return results;
6332
6177
  }
6333
6178
  async function runDemoTaskset(options = {}) {
6334
- await runTaskset2([
6179
+ await runTaskset([
6335
6180
  {
6336
6181
  requestId: "req-web-1",
6337
6182
  sessionId: "session-web",
@@ -6358,23 +6203,23 @@ async function runForegroundLoop(skipDemo = false, options = {}) {
6358
6203
 
6359
6204
  // ../ada-agent/dist/queue-runner.js
6360
6205
  async function ensureDirs(config) {
6361
- await import_promises12.default.mkdir(config.queue.inboxDir, { recursive: true });
6362
- await import_promises12.default.mkdir(config.queue.processedDir, { recursive: true });
6363
- await import_promises12.default.mkdir(config.queue.failedDir, { recursive: true });
6206
+ await import_promises10.default.mkdir(config.queue.inboxDir, { recursive: true });
6207
+ await import_promises10.default.mkdir(config.queue.processedDir, { recursive: true });
6208
+ await import_promises10.default.mkdir(config.queue.failedDir, { recursive: true });
6364
6209
  }
6365
6210
  async function listTaskFiles(inboxDir) {
6366
- const entries = await import_promises12.default.readdir(inboxDir, { withFileTypes: true });
6367
- return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => import_node_path12.default.join(inboxDir, entry.name)).sort((a, b) => a.localeCompare(b));
6211
+ const entries = await import_promises10.default.readdir(inboxDir, { withFileTypes: true });
6212
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => import_node_path9.default.join(inboxDir, entry.name)).sort((a, b) => a.localeCompare(b));
6368
6213
  }
6369
6214
  async function moveTo(targetDir, sourcePath) {
6370
- const fileName = import_node_path12.default.basename(sourcePath);
6371
- const destination = import_node_path12.default.join(targetDir, fileName);
6372
- await import_promises12.default.rename(sourcePath, destination);
6215
+ const fileName = import_node_path9.default.basename(sourcePath);
6216
+ const destination = import_node_path9.default.join(targetDir, fileName);
6217
+ await import_promises10.default.rename(sourcePath, destination);
6373
6218
  }
6374
6219
  async function writeFailedMeta(targetDir, sourcePath, meta) {
6375
- const fileName = `${import_node_path12.default.basename(sourcePath)}.error.json`;
6376
- const destination = import_node_path12.default.join(targetDir, fileName);
6377
- await import_promises12.default.writeFile(destination, JSON.stringify(meta, null, 2), "utf8");
6220
+ const fileName = `${import_node_path9.default.basename(sourcePath)}.error.json`;
6221
+ const destination = import_node_path9.default.join(targetDir, fileName);
6222
+ await import_promises10.default.writeFile(destination, JSON.stringify(meta, null, 2), "utf8");
6378
6223
  }
6379
6224
  async function processQueueOnce(config, options = {}) {
6380
6225
  await ensureDirs(config);
@@ -6387,7 +6232,7 @@ async function processQueueOnce(config, options = {}) {
6387
6232
  attempt += 1;
6388
6233
  try {
6389
6234
  const tasks = await loadTaskFile(file);
6390
- await runTaskset2(tasks, options);
6235
+ await runTaskset(tasks, options);
6391
6236
  await moveTo(config.queue.processedDir, file);
6392
6237
  processedCount += 1;
6393
6238
  log("info", { event: "queue.file.processed", details: { file, attempt } });
@@ -6604,6 +6449,250 @@ async function runStartFlow(options) {
6604
6449
  }
6605
6450
  }
6606
6451
 
6452
+ // src/bootstrap-deps.ts
6453
+ var INSTALL_SCOPE_TOKENS = /* @__PURE__ */ new Set([
6454
+ "playwright",
6455
+ "selenium",
6456
+ "appium",
6457
+ "drivers",
6458
+ "mobile",
6459
+ "android",
6460
+ "ios",
6461
+ "harmony"
6462
+ ]);
6463
+ function isTruthy(v) {
6464
+ const s = String(v ?? "").trim().toLowerCase();
6465
+ return s === "1" || s === "true" || s === "yes" || s === "on";
6466
+ }
6467
+ function isFalsy(v) {
6468
+ const s = String(v ?? "").trim().toLowerCase();
6469
+ return s === "0" || s === "false" || s === "no" || s === "off" || s === "skip" || s === "none";
6470
+ }
6471
+ function parseInstallDepsSpec(raw) {
6472
+ const trimmed = String(raw ?? "").trim();
6473
+ if (!trimmed) {
6474
+ return ["playwright"];
6475
+ }
6476
+ const lower = trimmed.toLowerCase();
6477
+ if (isFalsy(lower)) {
6478
+ return null;
6479
+ }
6480
+ if (lower === "all") {
6481
+ return ["all"];
6482
+ }
6483
+ const scopes = [];
6484
+ for (const part of lower.split(/[,+\s]+/)) {
6485
+ const token = part.trim();
6486
+ if (!token) continue;
6487
+ if (token === "all") return ["all"];
6488
+ if (INSTALL_SCOPE_TOKENS.has(token)) {
6489
+ scopes.push(token);
6490
+ }
6491
+ }
6492
+ return scopes.length > 0 ? scopes : ["playwright"];
6493
+ }
6494
+ function resolveBootstrapInstallDeps(argv2) {
6495
+ const skipFlag = argv2.includes("--skip-install-deps");
6496
+ const skipEnv = isTruthy(process.env.ADA_MCP_SKIP_INSTALL_DEPS);
6497
+ if (skipFlag || skipEnv) {
6498
+ return { skip: true };
6499
+ }
6500
+ const fromArg = argv2.find((x) => x.startsWith("--install-deps="))?.slice("--install-deps=".length);
6501
+ const fromEnv = process.env.ADA_MCP_INSTALL_DEPS;
6502
+ const spec = fromArg !== void 0 && fromArg.length > 0 ? fromArg : fromEnv;
6503
+ const scopes = parseInstallDepsSpec(spec);
6504
+ if (!scopes) {
6505
+ return { skip: true };
6506
+ }
6507
+ const force = argv2.includes("--install-deps-force") || isTruthy(process.env.ADA_MCP_INSTALL_DEPS_FORCE);
6508
+ const extras = {};
6509
+ const gecko = argv2.find((x) => x.startsWith("--geckodriver-version="))?.slice("--geckodriver-version=".length) ?? process.env.ADA_MCP_GECKODRIVER_VERSION;
6510
+ const chrome = argv2.find((x) => x.startsWith("--chromedriver-version="))?.slice("--chromedriver-version=".length) ?? process.env.ADA_MCP_CHROMEDRIVER_VERSION;
6511
+ const nativeDir = argv2.find((x) => x.startsWith("--native-drivers-dir="))?.slice("--native-drivers-dir=".length) ?? process.env.ADA_MCP_NATIVE_DRIVERS_DIR;
6512
+ if (gecko) extras.geckodriverVersion = gecko;
6513
+ if (chrome) extras.chromedriverVersion = chrome;
6514
+ if (nativeDir) extras.nativeDriversDir = nativeDir;
6515
+ return { skip: false, scopes, force, extras };
6516
+ }
6517
+ function ensureDefaultInstallTimeouts() {
6518
+ if (!process.env.ADA_INSTALL_STRATEGY_TIMEOUT_MS?.trim()) {
6519
+ process.env.ADA_INSTALL_STRATEGY_TIMEOUT_MS = "120000";
6520
+ }
6521
+ if (!process.env.ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS?.trim()) {
6522
+ process.env.ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS = "900000";
6523
+ }
6524
+ }
6525
+ async function runBootstrapInstallDeps(argv2) {
6526
+ ensureDefaultInstallTimeouts();
6527
+ const plan = resolveBootstrapInstallDeps(argv2);
6528
+ if (plan.skip) {
6529
+ console.error("[ADA-MCP] dependency bootstrap skipped (--skip-install-deps / ADA_MCP_SKIP_INSTALL_DEPS)");
6530
+ return;
6531
+ }
6532
+ const label = plan.scopes.join(",");
6533
+ console.error(`[ADA-MCP] dependency bootstrap start (scope=${label}, force=${plan.force})`);
6534
+ for (const scope of plan.scopes) {
6535
+ console.error(`[ADA-MCP] installing: ${scope}`);
6536
+ await installDependencies(scope, plan.force, (line) => {
6537
+ console.error(`[ADA-MCP] ${line}`);
6538
+ }, plan.extras);
6539
+ }
6540
+ console.error("[ADA-MCP] dependency bootstrap done");
6541
+ }
6542
+
6543
+ // src/main.ts
6544
+ var import_promises13 = __toESM(require("node:fs/promises"));
6545
+ var import_node_fs3 = require("node:fs");
6546
+ var import_node_net2 = __toESM(require("node:net"));
6547
+ var import_node_path13 = __toESM(require("node:path"));
6548
+ var import_node_child_process5 = require("node:child_process");
6549
+ var import_node_url4 = require("node:url");
6550
+ var import_server = require("@modelcontextprotocol/sdk/server/index.js");
6551
+ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
6552
+ var import_types = require("@modelcontextprotocol/sdk/types.js");
6553
+
6554
+ // src/executor.ts
6555
+ var import_node_fs2 = require("node:fs");
6556
+ var import_node_path10 = __toESM(require("node:path"));
6557
+ var import_node_url3 = require("node:url");
6558
+ var import_meta = {};
6559
+ function ensureBundledPluginDir() {
6560
+ if (process.env.ADA_PLUGIN_DIR?.trim()) {
6561
+ return;
6562
+ }
6563
+ const candidates = [];
6564
+ 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"));
6567
+ } catch {
6568
+ }
6569
+ const dirname = globalThis.__dirname;
6570
+ if (typeof dirname === "string") {
6571
+ candidates.push(import_node_path10.default.join(dirname, "..", "plugins"));
6572
+ }
6573
+ for (const dir of candidates) {
6574
+ if ((0, import_node_fs2.existsSync)(dir)) {
6575
+ process.env.ADA_PLUGIN_DIR = dir;
6576
+ return;
6577
+ }
6578
+ }
6579
+ }
6580
+ function buildPluginHost2() {
6581
+ ensureBundledPluginDir();
6582
+ const host = new PluginHost();
6583
+ registerRuntimePlugins(host);
6584
+ return host;
6585
+ }
6586
+ var manifests = registerRuntimePlugins(new PluginHost());
6587
+ var sharedExecutor = new TaskExecutor(buildPluginHost2());
6588
+ async function runCommand3(command) {
6589
+ return sharedExecutor.execute(command);
6590
+ }
6591
+ async function runTaskset2(commands) {
6592
+ const results = [];
6593
+ for (const command of commands) {
6594
+ results.push(await sharedExecutor.execute(command));
6595
+ }
6596
+ return results;
6597
+ }
6598
+ function listActiveSessions() {
6599
+ return sharedExecutor.listSessions();
6600
+ }
6601
+ async function closeSession(platform, sessionId, options) {
6602
+ return sharedExecutor.closeSession(platform, sessionId, options);
6603
+ }
6604
+ async function closeAllSessions() {
6605
+ return sharedExecutor.closeAllSessions();
6606
+ }
6607
+
6608
+ // src/config.ts
6609
+ var import_promises11 = __toESM(require("node:fs/promises"));
6610
+ var import_node_path11 = __toESM(require("node:path"));
6611
+
6612
+ // 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 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';
6614
+
6615
+ // 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");
6619
+ async function resolveWorkspaceRoot4(startDir = process.cwd()) {
6620
+ return resolveWorkspaceRoot(DEFAULT_CONFIG_RELATIVE2, startDir);
6621
+ }
6622
+ async function loadAgentConfig(cwd = process.cwd()) {
6623
+ let root = await resolveWorkspaceRoot4(cwd);
6624
+ const defaultPath = import_node_path11.default.join(root, DEFAULT_CONFIG_RELATIVE2);
6625
+ let defaultRaw;
6626
+ try {
6627
+ defaultRaw = await import_promises11.default.readFile(defaultPath, "utf8");
6628
+ } catch {
6629
+ defaultRaw = bundledDefaultConfigYaml2;
6630
+ root = import_node_path11.default.dirname(process.execPath);
6631
+ }
6632
+ const defaultConfig2 = jsYaml.load(defaultRaw) ?? {};
6633
+ const effectivePath = import_node_path11.default.join(root, EFFECTIVE_CONFIG_FILE2);
6634
+ try {
6635
+ const effectiveFile = await import_promises11.default.readFile(effectivePath, "utf8");
6636
+ const effectiveConfig = jsYaml.load(effectiveFile) ?? {};
6637
+ return deepMerge(defaultConfig2, effectiveConfig);
6638
+ } catch {
6639
+ return defaultConfig2;
6640
+ }
6641
+ }
6642
+
6643
+ // src/monitoring.ts
6644
+ var import_promises12 = __toESM(require("node:fs/promises"));
6645
+ var import_node_path12 = __toESM(require("node:path"));
6646
+ var import_jimp2 = require("jimp");
6647
+ function getScreenshotPath2(result) {
6648
+ const value = result.data?.screenshot;
6649
+ return typeof value === "string" ? value : null;
6650
+ }
6651
+ function buildOutputPath(options, command) {
6652
+ if (options.groupBySession) {
6653
+ return import_node_path12.default.join(options.outputDir, command.sessionId, `${command.requestId}.png`);
6654
+ }
6655
+ return import_node_path12.default.join(options.outputDir, `${command.requestId}.png`);
6656
+ }
6657
+ async function captureMcpMonitor(command, result, options, runCommand4) {
6658
+ if (!options.enabled) {
6659
+ return null;
6660
+ }
6661
+ if (options.onFailureOnly && result.success) {
6662
+ return null;
6663
+ }
6664
+ if (command.command === "screenshot") {
6665
+ return null;
6666
+ }
6667
+ const shotResult = await runCommand4({
6668
+ requestId: `${command.requestId}-monitor`,
6669
+ sessionId: command.sessionId,
6670
+ platform: command.platform,
6671
+ command: "screenshot",
6672
+ payload: {
6673
+ ...command.payload ?? {},
6674
+ __monitorCapture: true
6675
+ }
6676
+ });
6677
+ if (!shotResult.success) {
6678
+ return null;
6679
+ }
6680
+ const sourcePath = getScreenshotPath2(shotResult);
6681
+ if (!sourcePath) {
6682
+ return null;
6683
+ }
6684
+ const targetPath = buildOutputPath(options, command);
6685
+ await import_promises12.default.mkdir(import_node_path12.default.dirname(targetPath), { recursive: true });
6686
+ const image = await import_jimp2.Jimp.read(sourcePath);
6687
+ if (options.keepAspectRatio) {
6688
+ image.scaleToFit({ w: options.maxWidth, h: options.maxHeight });
6689
+ } else {
6690
+ image.cover({ w: options.maxWidth, h: options.maxHeight });
6691
+ }
6692
+ await image.write(targetPath);
6693
+ return targetPath;
6694
+ }
6695
+
6607
6696
  // src/main.ts
6608
6697
  function textResult(data) {
6609
6698
  return {
@@ -7111,7 +7200,7 @@ function parseInstallScope(v) {
7111
7200
  if (v === "all" || v === "playwright" || v === "selenium" || v === "appium" || v === "drivers" || v === "mobile" || v === "android" || v === "ios" || v === "harmony") {
7112
7201
  return v;
7113
7202
  }
7114
- return "all";
7203
+ return "playwright";
7115
7204
  }
7116
7205
  function parseMonitorOptions(args) {
7117
7206
  const monitor = asRecord3(args.monitor);
@@ -7127,7 +7216,7 @@ function parseMonitorOptions(args) {
7127
7216
  };
7128
7217
  }
7129
7218
  function runMonitorCapture(command, result, options) {
7130
- const job = captureMcpMonitor(command, result, options, runCommand).then(() => void 0);
7219
+ const job = captureMcpMonitor(command, result, options, runCommand3).then(() => void 0);
7131
7220
  if (options.nonBlocking) {
7132
7221
  job.catch(() => void 0);
7133
7222
  return;
@@ -7185,10 +7274,10 @@ async function withTiming(label, fn) {
7185
7274
  async function executeWithTimeout(command, timeoutMs) {
7186
7275
  const effectiveTimeout = typeof timeoutMs === "number" && timeoutMs > 0 ? timeoutMs : 0;
7187
7276
  if (effectiveTimeout <= 0) {
7188
- return runCommand(command);
7277
+ return runCommand3(command);
7189
7278
  }
7190
7279
  return Promise.race([
7191
- runCommand(command),
7280
+ runCommand3(command),
7192
7281
  new Promise((resolve) => {
7193
7282
  setTimeout(() => {
7194
7283
  resolve({
@@ -7594,7 +7683,7 @@ function wireAdaMcpProtocolServer(mcp) {
7594
7683
  only: {
7595
7684
  type: "string",
7596
7685
  enum: ["all", "playwright", "selenium", "mobile", "android", "ios", "harmony", "appium", "drivers"],
7597
- description: "Install scope"
7686
+ description: "Install scope (default playwright when omitted)"
7598
7687
  },
7599
7688
  force: { type: "boolean", description: "Force reinstall selected scope" },
7600
7689
  nativeDriversDir: {
@@ -7970,7 +8059,7 @@ function wireAdaMcpProtocolServer(mcp) {
7970
8059
  script = `(() => (document.body?.innerText || '').slice(0, 5000))()`;
7971
8060
  }
7972
8061
  ensureRiskAllowed("custom", args);
7973
- const result = await runCommand(
8062
+ const result = await runCommand3(
7974
8063
  toCommandEnvelope({
7975
8064
  requestId: `extract-${Date.now()}`,
7976
8065
  sessionId,
@@ -7999,7 +8088,7 @@ function wireAdaMcpProtocolServer(mcp) {
7999
8088
  command = "assertText";
8000
8089
  } else if (type2 === "url") {
8001
8090
  ensureRiskAllowed("custom", args);
8002
- const result2 = await runCommand(
8091
+ const result2 = await runCommand3(
8003
8092
  toCommandEnvelope({
8004
8093
  requestId: `assert-url-${Date.now()}`,
8005
8094
  sessionId,
@@ -8022,7 +8111,7 @@ function wireAdaMcpProtocolServer(mcp) {
8022
8111
  actualUrl: actual
8023
8112
  });
8024
8113
  }
8025
- const result = await runCommand(
8114
+ const result = await runCommand3(
8026
8115
  toCommandEnvelope({
8027
8116
  requestId: `assert-${Date.now()}`,
8028
8117
  sessionId,
@@ -8045,7 +8134,7 @@ function wireAdaMcpProtocolServer(mcp) {
8045
8134
  const payload = asRecord3(args.payload);
8046
8135
  if (type2 === "pageSource") {
8047
8136
  ensureRiskAllowed("custom", args);
8048
- const result2 = await runCommand(
8137
+ const result2 = await runCommand3(
8049
8138
  toCommandEnvelope({
8050
8139
  requestId: `mobile-page-source-${Date.now()}`,
8051
8140
  sessionId,
@@ -8070,7 +8159,7 @@ function wireAdaMcpProtocolServer(mcp) {
8070
8159
  })
8071
8160
  );
8072
8161
  }
8073
- const result = await runCommand(
8162
+ const result = await runCommand3(
8074
8163
  toCommandEnvelope({
8075
8164
  requestId: `mobile-extract-${Date.now()}`,
8076
8165
  sessionId,
@@ -8100,7 +8189,7 @@ function wireAdaMcpProtocolServer(mcp) {
8100
8189
  const type2 = typeof args.type === "string" ? args.type : "visible";
8101
8190
  const payload = asRecord3(args.payload);
8102
8191
  const command = type2 === "text" ? "assertText" : "assertVisible";
8103
- const result = await runCommand(
8192
+ const result = await runCommand3(
8104
8193
  toCommandEnvelope({
8105
8194
  requestId: `mobile-assert-${Date.now()}`,
8106
8195
  sessionId,
@@ -8119,7 +8208,7 @@ function wireAdaMcpProtocolServer(mcp) {
8119
8208
  }
8120
8209
  const taskPath = import_node_path13.default.isAbsolute(file) ? file : import_node_path13.default.resolve(process.cwd(), file);
8121
8210
  const tasks = await loadTaskFile2(taskPath);
8122
- const results = await runTaskset(tasks);
8211
+ const results = await runTaskset2(tasks);
8123
8212
  const monitor = parseMonitorOptions(args);
8124
8213
  const monitorJobs = [];
8125
8214
  for (let i = 0; i < tasks.length; i += 1) {
@@ -8141,7 +8230,7 @@ function wireAdaMcpProtocolServer(mcp) {
8141
8230
  ensureRiskAllowed(normalizeCommand(args.command), args);
8142
8231
  const command = toCommandEnvelope(args);
8143
8232
  await withTiming(`ensureAppiumServerReady(${command.platform})`, () => ensureAppiumServerReady(command.platform));
8144
- const result = await withTiming(`runCommand(${command.platform}:${command.command})`, () => runCommand(command));
8233
+ const result = await withTiming(`runCommand(${command.platform}:${command.command})`, () => runCommand3(command));
8145
8234
  const maybeJob = runMonitorCapture(command, result, parseMonitorOptions(args));
8146
8235
  if (maybeJob) {
8147
8236
  await maybeJob;
@@ -8164,7 +8253,7 @@ function wireAdaMcpProtocolServer(mcp) {
8164
8253
  payload
8165
8254
  };
8166
8255
  await withTiming(`ensureAppiumServerReady(${platform})`, () => ensureAppiumServerReady(platform));
8167
- const result = await withTiming(`runCommand(${platform}:invoke)`, () => runCommand(envelope));
8256
+ const result = await withTiming(`runCommand(${platform}:invoke)`, () => runCommand3(envelope));
8168
8257
  const maybeJob = runMonitorCapture(envelope, result, parseMonitorOptions(args));
8169
8258
  if (maybeJob) {
8170
8259
  await maybeJob;
@@ -8184,7 +8273,7 @@ function wireAdaMcpProtocolServer(mcp) {
8184
8273
  command,
8185
8274
  payload: mergeWebEngineIntoPayload(args)
8186
8275
  });
8187
- const result = await withTiming(`runCommand(web:${command})`, () => runCommand(envelope));
8276
+ const result = await withTiming(`runCommand(web:${command})`, () => runCommand3(envelope));
8188
8277
  const maybeJob = runMonitorCapture(envelope, result, parseMonitorOptions(args));
8189
8278
  if (maybeJob) {
8190
8279
  await maybeJob;
@@ -8206,7 +8295,7 @@ function wireAdaMcpProtocolServer(mcp) {
8206
8295
  command
8207
8296
  });
8208
8297
  await withTiming(`ensureAppiumServerReady(${envelope.platform})`, () => ensureAppiumServerReady(envelope.platform));
8209
- const result = await withTiming(`runCommand(${envelope.platform}:${command})`, () => runCommand(envelope));
8298
+ const result = await withTiming(`runCommand(${envelope.platform}:${command})`, () => runCommand3(envelope));
8210
8299
  const maybeJob = runMonitorCapture(envelope, result, parseMonitorOptions(args));
8211
8300
  if (maybeJob) {
8212
8301
  await maybeJob;
@@ -8248,9 +8337,11 @@ async function startMcpServer() {
8248
8337
  cwd,
8249
8338
  env: {
8250
8339
  ADA_PLAYWRIGHT_HEADLESS: "true",
8340
+ ADA_MCP_INSTALL_DEPS: "playwright",
8251
8341
  ADA_NPM_PROXY_REGISTRY: "https://registry.npmmirror.com",
8252
8342
  ADA_PNPM_PROXY_REGISTRY: "https://registry.npmmirror.com",
8253
- ADA_INSTALL_STRATEGY_TIMEOUT_MS: "30000"
8343
+ ADA_INSTALL_STRATEGY_TIMEOUT_MS: "120000",
8344
+ ADA_PLAYWRIGHT_INSTALL_TIMEOUT_MS: "900000"
8254
8345
  }
8255
8346
  }
8256
8347
  }
@@ -8390,7 +8481,7 @@ async function callLegacyTool(name, args, options) {
8390
8481
  command,
8391
8482
  payload
8392
8483
  };
8393
- return runCommand(envelope);
8484
+ return runCommand3(envelope);
8394
8485
  }
8395
8486
  throw new Error(`unsupported tool: ${name}`);
8396
8487
  }
@@ -8580,12 +8671,18 @@ if (isServerMode) {
8580
8671
  console.error("missing api key, set --api-key=xxx or ADA_MCP_REMOTE_API_KEY");
8581
8672
  process.exit(1);
8582
8673
  }
8583
- void startRemoteServer({ host, port, apiKey, allowRisky, riskyMode, riskyCommands, allowedHosts }).catch((error) => {
8674
+ void (async () => {
8675
+ await runBootstrapInstallDeps(argv);
8676
+ await startRemoteServer({ host, port, apiKey, allowRisky, riskyMode, riskyCommands, allowedHosts });
8677
+ })().catch((error) => {
8584
8678
  console.error(error);
8585
8679
  process.exit(1);
8586
8680
  });
8587
8681
  } else {
8588
- void startMcpServer().catch((error) => {
8682
+ void (async () => {
8683
+ await runBootstrapInstallDeps(argv);
8684
+ await startMcpServer();
8685
+ })().catch((error) => {
8589
8686
  console.error(error);
8590
8687
  process.exit(1);
8591
8688
  });