@ada-mcp/mcp-server 0.1.0 → 0.1.2

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 +79 -6
  2. package/dist/cli.cjs +975 -889
  3. package/package.json +2 -4
package/dist/cli.cjs CHANGED
@@ -1,561 +1,31 @@
1
1
  #!/usr/bin/env node
2
- #!/usr/bin/env node
3
- "use strict";
4
- var __create = Object.create;
5
- var __defProp = Object.defineProperty;
6
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
- var __getOwnPropNames = Object.getOwnPropertyNames;
8
- var __getProtoOf = Object.getPrototypeOf;
9
- var __hasOwnProp = Object.prototype.hasOwnProperty;
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
- // If the importer is in node compatibility mode or this is not an ESM
20
- // file that has been converted to a CommonJS file using a Babel-
21
- // compatible transform (i.e. "__esModule" has not been set), then set
22
- // "default" to the CommonJS "module.exports" for node compatibility.
23
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
- mod
25
- ));
26
-
27
- // src/main.ts
28
- var import_promises13 = __toESM(require("node:fs/promises"));
29
- var import_node_fs3 = require("node:fs");
30
- var import_node_net2 = __toESM(require("node:net"));
31
- var import_node_path13 = __toESM(require("node:path"));
32
- var import_node_child_process5 = require("node:child_process");
33
- var import_node_url4 = require("node:url");
34
- var import_server = require("@modelcontextprotocol/sdk/server/index.js");
35
- var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
36
- var import_types = require("@modelcontextprotocol/sdk/types.js");
37
-
38
- // src/executor.ts
39
- var import_node_fs2 = require("node:fs");
40
- var import_node_path2 = __toESM(require("node:path"));
41
- var import_node_url = require("node:url");
42
-
43
- // ../../packages/driver-rpc/src/index.ts
44
- function parseWebEngineFromPayload(payload) {
45
- const p = asRecord(payload);
46
- const options = asRecord(p.options);
47
- const raw = (getString(p.engine) ?? getString(options.engine) ?? "playwright").toLowerCase();
48
- if (raw === "selenium") {
49
- return "selenium";
50
- }
51
- return "playwright";
52
- }
53
- function manifestWebEngine(manifest) {
54
- if (manifest.engine === "selenium") {
55
- return "selenium";
56
- }
57
- return "playwright";
58
- }
59
- function asRecord(value) {
60
- return typeof value === "object" && value !== null ? value : {};
61
- }
62
- function getString(value) {
63
- return typeof value === "string" && value.length > 0 ? value : void 0;
64
- }
65
-
66
- // ../../packages/core-kernel/src/index.ts
67
- var DriverSessionManager = class {
68
- sessions = /* @__PURE__ */ new Map();
69
- sessionKey(command) {
70
- if (command.platform === "web") {
71
- const engine = parseWebEngineFromPayload(command.payload);
72
- return `web:${engine}:${command.sessionId}`;
73
- }
74
- return `${command.platform}:${command.sessionId}`;
75
- }
76
- async getOrCreate(plugin, command) {
77
- const key = this.sessionKey(command);
78
- const existed = this.sessions.get(key);
79
- if (existed) {
80
- return existed;
81
- }
82
- const created = await plugin.createSession(command.platform);
83
- this.sessions.set(key, created);
84
- return created;
85
- }
86
- get(command) {
87
- const key = this.sessionKey(command);
88
- return this.sessions.get(key);
89
- }
90
- list() {
91
- const items = [];
92
- for (const [key, session] of this.sessions.entries()) {
93
- const parts = key.split(":");
94
- if (parts[0] === "web" && parts.length >= 3) {
95
- items.push({
96
- platform: "web",
97
- engine: parts[1],
98
- sessionId: parts.slice(2).join(":"),
99
- driverSessionId: session.id
100
- });
101
- continue;
102
- }
103
- const idx = key.indexOf(":");
104
- if (idx <= 0) {
105
- continue;
106
- }
107
- items.push({
108
- platform: key.slice(0, idx),
109
- sessionId: key.slice(idx + 1),
110
- driverSessionId: session.id
111
- });
112
- }
113
- return items;
114
- }
115
- clear(command) {
116
- const key = this.sessionKey(command);
117
- const existed = this.sessions.get(key);
118
- this.sessions.delete(key);
119
- return existed;
120
- }
121
- clearByPlatformSession(platform, sessionId, options) {
122
- if (platform === "web") {
123
- const engine = options?.engine ?? parseWebEngineFromPayload(options?.payload);
124
- const key2 = `web:${engine}:${sessionId}`;
125
- const existed2 = this.sessions.get(key2);
126
- this.sessions.delete(key2);
127
- return existed2;
128
- }
129
- const key = `${platform}:${sessionId}`;
130
- const existed = this.sessions.get(key);
131
- this.sessions.delete(key);
132
- return existed;
133
- }
134
- clearWebSession(sessionId, engine) {
135
- const key = `web:${engine}:${sessionId}`;
136
- const existed = this.sessions.get(key);
137
- this.sessions.delete(key);
138
- return existed;
139
- }
140
- clearAll() {
141
- const all = Array.from(this.sessions.values());
142
- this.sessions.clear();
143
- return all;
144
- }
145
- };
146
- var RetryPolicyEngine = class {
147
- constructor(maxAttempts, retryableErrorCodes) {
148
- this.maxAttempts = maxAttempts;
149
- this.retryableErrorCodes = retryableErrorCodes;
150
- }
151
- shouldRetry(result, attempt) {
152
- if (attempt >= this.maxAttempts) {
153
- return false;
154
- }
155
- if (result.success) {
156
- return false;
157
- }
158
- if (!result.errorCode) {
159
- return true;
160
- }
161
- return this.retryableErrorCodes.has(result.errorCode);
162
- }
163
- };
164
- var ResultAssembler = class {
165
- success(requestId, data) {
166
- return {
167
- requestId,
168
- success: true,
169
- data
170
- };
171
- }
172
- failure(requestId, errorCode, errorMessage) {
173
- return {
174
- requestId,
175
- success: false,
176
- errorCode,
177
- errorMessage
178
- };
179
- }
180
- normalize(requestId, result) {
181
- return {
182
- requestId,
183
- success: Boolean(result.success),
184
- data: result.data,
185
- errorCode: result.errorCode,
186
- errorMessage: result.errorMessage
187
- };
188
- }
189
- };
190
- var FeatureNegotiator = class {
191
- normalize(command) {
192
- if (command === "click") {
193
- return "tap";
194
- }
195
- return command;
196
- }
197
- check(plugin, command) {
198
- const expected = this.normalize(command.command);
199
- const declared = new Set(plugin.manifest.capabilities.map((item) => this.normalize(item)));
200
- if (declared.has(expected)) {
201
- return { ok: true };
202
- }
203
- return {
204
- ok: false,
205
- code: "DRIVER_CAPABILITY_UNSUPPORTED",
206
- message: `Plugin ${plugin.manifest.id} does not support command ${command.command} on platform ${command.platform}`
207
- };
208
- }
209
- };
210
- var TaskExecutor = class {
211
- constructor(pluginHost, options = {}) {
212
- this.pluginHost = pluginHost;
213
- this.maxAttempts = Math.max(1, options.maxAttempts ?? 2);
214
- this.retry = new RetryPolicyEngine(
215
- this.maxAttempts,
216
- new Set(options.retryableErrorCodes ?? ["TRANSIENT_DRIVER_ERROR", "NETWORK_TIMEOUT"])
217
- );
218
- }
219
- retry;
220
- maxAttempts;
221
- sessions = new DriverSessionManager();
222
- resultAssembler = new ResultAssembler();
223
- featureNegotiator = new FeatureNegotiator();
224
- async resolveContext(command) {
225
- const plugin = this.pluginHost.resolve(command);
226
- await this.pluginHost.ensureInitialized(plugin.manifest.id);
227
- const session = await this.sessions.getOrCreate(plugin, command);
228
- return { plugin, session };
229
- }
230
- async execute(command) {
231
- let attempt = 0;
232
- let lastFailure = this.resultAssembler.failure(command.requestId, "KERNEL_EXECUTION_FAILED", "unknown error");
233
- while (attempt < this.maxAttempts) {
234
- attempt += 1;
235
- try {
236
- const context = await this.resolveContext(command);
237
- const feature = this.featureNegotiator.check(context.plugin, command);
238
- if (!feature.ok) {
239
- return this.resultAssembler.failure(command.requestId, feature.code, feature.message);
240
- }
241
- const result = await context.plugin.execute(context.session, command);
242
- const normalized = this.resultAssembler.normalize(command.requestId, result);
243
- if (this.retry.shouldRetry(normalized, attempt)) {
244
- this.sessions.clear(command);
245
- lastFailure = normalized;
246
- continue;
247
- }
248
- return normalized;
249
- } catch (error) {
250
- const message = error instanceof Error ? error.message : String(error);
251
- const failed = this.resultAssembler.failure(command.requestId, "KERNEL_EXECUTION_FAILED", message);
252
- if (this.retry.shouldRetry(failed, attempt)) {
253
- this.sessions.clear(command);
254
- lastFailure = failed;
255
- continue;
256
- }
257
- return failed;
258
- }
259
- }
260
- return lastFailure;
261
- }
262
- listSessions() {
263
- return this.sessions.list();
264
- }
265
- async closeSession(platform, sessionId, options) {
266
- const plat = platform;
267
- const engine = plat === "web" ? options?.engine ?? parseWebEngineFromPayload(options?.payload) : void 0;
268
- const plugin = plat === "web" ? this.pluginHost.resolve({
269
- requestId: "",
270
- sessionId,
271
- platform: "web",
272
- command: "navigate",
273
- payload: { engine, ...options?.payload }
274
- }) : this.pluginHost.resolve({
275
- requestId: "",
276
- sessionId,
277
- platform: plat,
278
- command: "navigate"
279
- });
280
- const session = this.sessions.clearByPlatformSession(platform, sessionId, {
281
- engine,
282
- payload: options?.payload
283
- });
284
- if (!session) {
285
- return false;
286
- }
287
- if (plugin.destroySession) {
288
- await plugin.destroySession(session);
289
- }
290
- return true;
291
- }
292
- async closeAllSessions() {
293
- const all = this.sessions.list();
294
- let closed = 0;
295
- for (const item of all) {
296
- const ok = await this.closeSession(item.platform, item.sessionId, {
297
- engine: item.engine,
298
- payload: item.engine ? { engine: item.engine } : void 0
299
- });
300
- if (ok) {
301
- closed += 1;
302
- }
303
- }
304
- return closed;
305
- }
306
- };
307
-
308
- // ../../packages/plugin-host/src/index.ts
309
- var import_node_fs = __toESM(require("node:fs"), 1);
310
- var import_node_path = __toESM(require("node:path"), 1);
311
- var import_node_module = require("node:module");
312
- function assertManifest(plugin) {
313
- const m = plugin.manifest;
314
- if (!m.id || !m.version) {
315
- throw new Error("Invalid plugin manifest: missing id/version");
316
- }
317
- if (!Array.isArray(m.platforms) || m.platforms.length === 0) {
318
- throw new Error(`Invalid plugin manifest (${m.id}): platforms is empty`);
319
- }
320
- if (!Array.isArray(m.capabilities)) {
321
- throw new Error(`Invalid plugin manifest (${m.id}): capabilities must be array`);
322
- }
323
- if (!/^\d+\.\d+\.\d+/.test(m.version)) {
324
- throw new Error(`Invalid plugin manifest (${m.id}): version must look like semver`);
325
- }
326
- }
327
- var PluginHost = class {
328
- plugins = /* @__PURE__ */ new Map();
329
- webEngines = /* @__PURE__ */ new Map();
330
- manifests = /* @__PURE__ */ new Map();
331
- pluginById = /* @__PURE__ */ new Map();
332
- initializedPluginIds = /* @__PURE__ */ new Set();
333
- register(plugin) {
334
- assertManifest(plugin);
335
- if (this.manifests.has(plugin.manifest.id)) {
336
- throw new Error(`Plugin already registered: ${plugin.manifest.id}`);
337
- }
338
- const mobilePlatforms = plugin.manifest.platforms.filter((p) => p !== "web");
339
- if (plugin.manifest.platforms.includes("web")) {
340
- const engine = manifestWebEngine(plugin.manifest);
341
- if (this.webEngines.has(engine)) {
342
- throw new Error(`Web engine already registered: ${engine} (reject ${plugin.manifest.id})`);
343
- }
344
- this.webEngines.set(engine, plugin);
345
- }
346
- for (const platform of mobilePlatforms) {
347
- if (this.plugins.has(platform)) {
348
- throw new Error(`Platform already has plugin (${platform}), reject: ${plugin.manifest.id}`);
349
- }
350
- this.plugins.set(platform, plugin);
351
- }
352
- this.manifests.set(plugin.manifest.id, plugin.manifest);
353
- this.pluginById.set(plugin.manifest.id, plugin);
354
- }
355
- registerWebEngine(plugin) {
356
- this.register(plugin);
357
- }
358
- /** Resolve driver for a command (web routes by payload.engine). */
359
- resolve(command) {
360
- if (command.platform === "web") {
361
- const engine = parseWebEngineFromPayload(command.payload);
362
- const plugin2 = this.webEngines.get(engine);
363
- if (!plugin2) {
364
- if (engine === "selenium") {
365
- throw new Error(
366
- "WEB_ENGINE_SELENIUM_NOT_INSTALLED: register @ada/driver-selenium and ensure GeckoDriver/ChromeDriver is on PATH"
367
- );
368
- }
369
- throw new Error(`WEB_ENGINE_UNKNOWN: ${engine}`);
370
- }
371
- return plugin2;
372
- }
373
- const plugin = this.plugins.get(command.platform);
374
- if (!plugin) {
375
- throw new Error(`No plugin registered for platform: ${command.platform}`);
376
- }
377
- return plugin;
378
- }
379
- /** Legacy: mobile platforms only; web defaults to playwright. */
380
- resolvePlatform(platform) {
381
- if (platform === "web") {
382
- const plugin = this.webEngines.get("playwright");
383
- if (!plugin) {
384
- throw new Error("No web engine registered (playwright)");
385
- }
386
- return plugin;
387
- }
388
- return this.resolve({ requestId: "", sessionId: "", platform, command: "navigate" });
389
- }
390
- listWebEngines() {
391
- return Array.from(this.webEngines.keys());
392
- }
393
- listManifests() {
394
- return Array.from(this.manifests.values());
395
- }
396
- async ensureInitialized(pluginId, timeoutMs = 15e3) {
397
- if (this.initializedPluginIds.has(pluginId)) {
398
- return;
399
- }
400
- const plugin = this.pluginById.get(pluginId);
401
- if (!plugin) {
402
- throw new Error(`Plugin not registered: ${pluginId}`);
403
- }
404
- await Promise.race([
405
- plugin.init(),
406
- new Promise(
407
- (_, reject) => setTimeout(() => reject(new Error(`Plugin init timeout: ${pluginId}`)), timeoutMs)
408
- )
409
- ]);
410
- this.initializedPluginIds.add(pluginId);
411
- }
412
- async healthCheck(timeoutMs = 5e3) {
413
- const items = [];
414
- for (const [id] of this.manifests) {
415
- try {
416
- await this.ensureInitialized(id, timeoutMs);
417
- items.push({ id, ok: true, message: "healthy" });
418
- } catch (error) {
419
- items.push({
420
- id,
421
- ok: false,
422
- message: error instanceof Error ? error.message : String(error)
423
- });
424
- }
425
- }
426
- return items;
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
427
14
  }
15
+ return to;
428
16
  };
429
- var DEFAULT_PLUGIN_MODULE_IDS = [
430
- "@ada/driver-playwright",
431
- "@ada/driver-appium",
432
- "@ada/driver-selenium"
433
- ];
434
- function isPluginModule(mod) {
435
- if (!mod || typeof mod !== "object") {
436
- return false;
437
- }
438
- const m = mod.manifest;
439
- return !!m && typeof m.id === "string" && Array.isArray(m.platforms);
440
- }
441
- function resolvePluginDirs(explicitPluginDir) {
442
- const fromEnv = process.env.ADA_PLUGIN_DIR?.trim();
443
- const execDir = import_node_path.default.dirname(process.execPath);
444
- const cwd = process.cwd();
445
- return Array.from(
446
- new Set(
447
- [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))
448
- )
449
- );
450
- }
451
- function loadPluginFromModule(requireFn, moduleId) {
452
- try {
453
- const loaded = requireFn(moduleId);
454
- const plugin = loaded?.default ?? loaded;
455
- return isPluginModule(plugin) ? plugin : null;
456
- } catch {
457
- return null;
458
- }
459
- }
460
- function registerPluginsFromDirectory(host, pluginDir) {
461
- if (!import_node_fs.default.existsSync(pluginDir)) {
462
- return [];
463
- }
464
- 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));
465
- if (entries.length === 0) {
466
- return [];
467
- }
468
- const loaded = [];
469
- for (const file of entries) {
470
- const requireFn = (0, import_node_module.createRequire)(file);
471
- const plugin = loadPluginFromModule(requireFn, file);
472
- if (!plugin) {
473
- continue;
474
- }
475
- host.register(plugin);
476
- loaded.push(plugin.manifest);
477
- }
478
- return loaded;
479
- }
480
- function registerPluginsFromModuleIds(host, moduleIds) {
481
- const req = (0, import_node_module.createRequire)(typeof __filename === "string" ? __filename : process.cwd());
482
- const loaded = [];
483
- for (const moduleId of moduleIds) {
484
- const plugin = loadPluginFromModule(req, moduleId);
485
- if (!plugin) {
486
- continue;
487
- }
488
- host.register(plugin);
489
- loaded.push(plugin.manifest);
490
- }
491
- return loaded;
492
- }
493
- function registerRuntimePlugins(host, options) {
494
- const manifests2 = [];
495
- for (const pluginDir of resolvePluginDirs(options?.pluginDir)) {
496
- manifests2.push(...registerPluginsFromDirectory(host, pluginDir));
497
- }
498
- if (manifests2.length > 0) {
499
- return manifests2;
500
- }
501
- const fallbackModuleIds = options?.moduleIds?.length ? options.moduleIds : DEFAULT_PLUGIN_MODULE_IDS;
502
- return registerPluginsFromModuleIds(host, fallbackModuleIds);
503
- }
504
-
505
- // src/executor.ts
506
- var import_meta = {};
507
- function ensureBundledPluginDir() {
508
- if (process.env.ADA_PLUGIN_DIR?.trim()) {
509
- return;
510
- }
511
- const candidates = [];
512
- try {
513
- const here = import_node_path2.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
514
- candidates.push(import_node_path2.default.join(here, "..", "plugins"));
515
- } catch {
516
- }
517
- const dirname = globalThis.__dirname;
518
- if (typeof dirname === "string") {
519
- candidates.push(import_node_path2.default.join(dirname, "..", "plugins"));
520
- }
521
- for (const dir of candidates) {
522
- if ((0, import_node_fs2.existsSync)(dir)) {
523
- process.env.ADA_PLUGIN_DIR = dir;
524
- return;
525
- }
526
- }
527
- }
528
- function buildPluginHost() {
529
- ensureBundledPluginDir();
530
- const host = new PluginHost();
531
- registerRuntimePlugins(host);
532
- return host;
533
- }
534
- var manifests = registerRuntimePlugins(new PluginHost());
535
- var sharedExecutor = new TaskExecutor(buildPluginHost());
536
- async function runCommand(command) {
537
- return sharedExecutor.execute(command);
538
- }
539
- async function runTaskset(commands) {
540
- const results = [];
541
- for (const command of commands) {
542
- results.push(await sharedExecutor.execute(command));
543
- }
544
- return results;
545
- }
546
- function listActiveSessions() {
547
- return sharedExecutor.listSessions();
548
- }
549
- async function closeSession(platform, sessionId, options) {
550
- return sharedExecutor.closeSession(platform, sessionId, options);
551
- }
552
- async function closeAllSessions() {
553
- return sharedExecutor.closeAllSessions();
554
- }
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
555
25
 
556
- // src/config.ts
557
- var import_promises2 = __toESM(require("node:fs/promises"));
558
- 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);
559
29
 
560
30
  // ../../node_modules/js-yaml/dist/js-yaml.mjs
561
31
  function isNothing(subject) {
@@ -3183,7 +2653,7 @@ var jsYaml = {
3183
2653
 
3184
2654
  // ../../packages/core-runtime/src/index.ts
3185
2655
  var import_promises = __toESM(require("node:fs/promises"), 1);
3186
- var import_node_path3 = __toESM(require("node:path"), 1);
2656
+ var import_node_path = __toESM(require("node:path"), 1);
3187
2657
  function isObject2(value) {
3188
2658
  return typeof value === "object" && value !== null && !Array.isArray(value);
3189
2659
  }
@@ -3200,9 +2670,9 @@ function deepMerge(left, right) {
3200
2670
  return output2;
3201
2671
  }
3202
2672
  async function resolveWorkspaceRoot(configRelativePath, startDir = process.cwd()) {
3203
- const exeDir = import_node_path3.default.dirname(process.execPath);
2673
+ const exeDir = import_node_path.default.dirname(process.execPath);
3204
2674
  if (exeDir && exeDir !== "." && exeDir.length > 1) {
3205
- const besideExe = import_node_path3.default.join(exeDir, configRelativePath);
2675
+ const besideExe = import_node_path.default.join(exeDir, configRelativePath);
3206
2676
  try {
3207
2677
  await import_promises.default.access(besideExe);
3208
2678
  return exeDir;
@@ -3211,12 +2681,12 @@ async function resolveWorkspaceRoot(configRelativePath, startDir = process.cwd()
3211
2681
  }
3212
2682
  let current = startDir;
3213
2683
  for (let i = 0; i < 10; i += 1) {
3214
- const candidate = import_node_path3.default.join(current, configRelativePath);
2684
+ const candidate = import_node_path.default.join(current, configRelativePath);
3215
2685
  try {
3216
2686
  await import_promises.default.access(candidate);
3217
2687
  return current;
3218
2688
  } catch {
3219
- const parent = import_node_path3.default.dirname(current);
2689
+ const parent = import_node_path.default.dirname(current);
3220
2690
  if (parent === current) {
3221
2691
  break;
3222
2692
  }
@@ -3235,111 +2705,23 @@ function createJsonLogger(source) {
3235
2705
  });
3236
2706
  if (level === "error") {
3237
2707
  console.error(line);
3238
- return;
3239
- }
3240
- if (level === "warn") {
3241
- console.warn(line);
3242
- return;
3243
- }
3244
- console.log(line);
3245
- };
3246
- }
3247
-
3248
- // src/bundled-config.generated.ts
3249
- 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';
3250
-
3251
- // src/config.ts
3252
- var DEFAULT_CONFIG_RELATIVE = import_node_path4.default.join("config", "default.yaml");
3253
- var LOCAL_DATA_DIR = import_node_path4.default.join(".ada-agent");
3254
- var EFFECTIVE_CONFIG_FILE = import_node_path4.default.join(LOCAL_DATA_DIR, "agent.config.yaml");
3255
- async function resolveWorkspaceRoot2(startDir = process.cwd()) {
3256
- return resolveWorkspaceRoot(DEFAULT_CONFIG_RELATIVE, startDir);
3257
- }
3258
- async function loadAgentConfig(cwd = process.cwd()) {
3259
- let root = await resolveWorkspaceRoot2(cwd);
3260
- const defaultPath = import_node_path4.default.join(root, DEFAULT_CONFIG_RELATIVE);
3261
- let defaultRaw;
3262
- try {
3263
- defaultRaw = await import_promises2.default.readFile(defaultPath, "utf8");
3264
- } catch {
3265
- defaultRaw = bundledDefaultConfigYaml;
3266
- root = import_node_path4.default.dirname(process.execPath);
3267
- }
3268
- const defaultConfig2 = jsYaml.load(defaultRaw) ?? {};
3269
- const effectivePath = import_node_path4.default.join(root, EFFECTIVE_CONFIG_FILE);
3270
- try {
3271
- const effectiveFile = await import_promises2.default.readFile(effectivePath, "utf8");
3272
- const effectiveConfig = jsYaml.load(effectiveFile) ?? {};
3273
- return deepMerge(defaultConfig2, effectiveConfig);
3274
- } catch {
3275
- return defaultConfig2;
3276
- }
3277
- }
3278
-
3279
- // src/monitoring.ts
3280
- var import_promises3 = __toESM(require("node:fs/promises"));
3281
- var import_node_path5 = __toESM(require("node:path"));
3282
- var import_jimp = require("jimp");
3283
- function getScreenshotPath(result) {
3284
- const value = result.data?.screenshot;
3285
- return typeof value === "string" ? value : null;
3286
- }
3287
- function buildOutputPath(options, command) {
3288
- if (options.groupBySession) {
3289
- return import_node_path5.default.join(options.outputDir, command.sessionId, `${command.requestId}.png`);
3290
- }
3291
- return import_node_path5.default.join(options.outputDir, `${command.requestId}.png`);
3292
- }
3293
- async function captureMcpMonitor(command, result, options, runCommand4) {
3294
- if (!options.enabled) {
3295
- return null;
3296
- }
3297
- if (options.onFailureOnly && result.success) {
3298
- return null;
3299
- }
3300
- if (command.command === "screenshot") {
3301
- return null;
3302
- }
3303
- const shotResult = await runCommand4({
3304
- requestId: `${command.requestId}-monitor`,
3305
- sessionId: command.sessionId,
3306
- platform: command.platform,
3307
- command: "screenshot",
3308
- payload: {
3309
- ...command.payload ?? {},
3310
- __monitorCapture: true
3311
- }
3312
- });
3313
- if (!shotResult.success) {
3314
- return null;
3315
- }
3316
- const sourcePath = getScreenshotPath(shotResult);
3317
- if (!sourcePath) {
3318
- return null;
3319
- }
3320
- const targetPath = buildOutputPath(options, command);
3321
- await import_promises3.default.mkdir(import_node_path5.default.dirname(targetPath), { recursive: true });
3322
- const image = await import_jimp.Jimp.read(sourcePath);
3323
- if (options.keepAspectRatio) {
3324
- image.scaleToFit({ w: options.maxWidth, h: options.maxHeight });
3325
- } else {
3326
- image.cover({ w: options.maxWidth, h: options.maxHeight });
3327
- }
3328
- await image.write(targetPath);
3329
- return targetPath;
2708
+ return;
2709
+ }
2710
+ if (level === "warn") {
2711
+ console.warn(line);
2712
+ return;
2713
+ }
2714
+ console.log(line);
2715
+ };
3330
2716
  }
3331
2717
 
3332
- // ../ada-agent/dist/config.js
3333
- var import_promises4 = __toESM(require("node:fs/promises"), 1);
3334
- var import_node_path6 = __toESM(require("node:path"), 1);
3335
-
3336
2718
  // ../ada-agent/dist/bundled-config.generated.js
3337
- 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';
3338
2720
 
3339
2721
  // ../ada-agent/dist/config.js
3340
- var DEFAULT_CONFIG_RELATIVE2 = import_node_path6.default.join("config", "default.yaml");
3341
- var LOCAL_DATA_DIR2 = import_node_path6.default.join(".ada-agent");
3342
- 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");
3343
2725
  var defaultConfig = {
3344
2726
  agent: {
3345
2727
  id: "ada-agent-local",
@@ -3438,30 +2820,30 @@ function mergeConfig(base, overrides) {
3438
2820
  appium: { ...base.appium, ...overrides.appium }
3439
2821
  };
3440
2822
  }
3441
- async function resolveWorkspaceRoot3(startDir = process.cwd()) {
3442
- return resolveWorkspaceRoot(DEFAULT_CONFIG_RELATIVE2, startDir);
2823
+ async function resolveWorkspaceRoot2(startDir = process.cwd()) {
2824
+ return resolveWorkspaceRoot(DEFAULT_CONFIG_RELATIVE, startDir);
3443
2825
  }
3444
2826
  async function ensureLocalDataDir(cwd = process.cwd()) {
3445
- const root = await resolveWorkspaceRoot3(cwd);
3446
- const dir = import_node_path6.default.join(root, LOCAL_DATA_DIR2);
3447
- 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 });
3448
2830
  return dir;
3449
2831
  }
3450
2832
  async function loadConfig(cwd = process.cwd()) {
3451
- let root = await resolveWorkspaceRoot3(cwd);
3452
- 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);
3453
2835
  let defaultRaw;
3454
2836
  try {
3455
- defaultRaw = await import_promises4.default.readFile(defaultPath, "utf8");
2837
+ defaultRaw = await import_promises2.default.readFile(defaultPath, "utf8");
3456
2838
  } catch {
3457
- defaultRaw = bundledDefaultConfigYaml2;
3458
- root = import_node_path6.default.dirname(process.execPath);
2839
+ defaultRaw = bundledDefaultConfigYaml;
2840
+ root = import_node_path2.default.dirname(process.execPath);
3459
2841
  }
3460
2842
  const defaultFromFile = jsYaml.load(defaultRaw);
3461
2843
  const mergedDefault = mergeConfig(defaultConfig, defaultFromFile);
3462
- const effectivePath = import_node_path6.default.join(root, EFFECTIVE_CONFIG_FILE2);
2844
+ const effectivePath = import_node_path2.default.join(root, EFFECTIVE_CONFIG_FILE);
3463
2845
  try {
3464
- const effectiveFile = await import_promises4.default.readFile(effectivePath, "utf8");
2846
+ const effectiveFile = await import_promises2.default.readFile(effectivePath, "utf8");
3465
2847
  const effective = jsYaml.load(effectiveFile);
3466
2848
  return mergeConfig(mergedDefault, effective);
3467
2849
  } catch {
@@ -3469,10 +2851,10 @@ async function loadConfig(cwd = process.cwd()) {
3469
2851
  }
3470
2852
  }
3471
2853
  async function saveEffectiveConfig(config, cwd = process.cwd()) {
3472
- const root = await resolveWorkspaceRoot3(cwd);
2854
+ const root = await resolveWorkspaceRoot2(cwd);
3473
2855
  await ensureLocalDataDir(root);
3474
- const effectivePath = import_node_path6.default.join(root, EFFECTIVE_CONFIG_FILE2);
3475
- 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");
3476
2858
  }
3477
2859
  function maskToken(token) {
3478
2860
  if (!token) {
@@ -3485,8 +2867,8 @@ function maskToken(token) {
3485
2867
  }
3486
2868
 
3487
2869
  // ../ada-agent/dist/secrets.js
3488
- var import_promises5 = __toESM(require("node:fs/promises"), 1);
3489
- 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);
3490
2872
  var SECRET_FILE = "secrets.json";
3491
2873
  function resolveProvider(provider) {
3492
2874
  if (provider === "keychain" || provider === "credman") {
@@ -3500,8 +2882,8 @@ async function saveSecret(record, provider, cwd = process.cwd()) {
3500
2882
  return;
3501
2883
  }
3502
2884
  const dir = await ensureLocalDataDir(cwd);
3503
- const file = import_node_path7.default.join(dir, SECRET_FILE);
3504
- 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");
3505
2887
  }
3506
2888
  async function loadSecret(provider, cwd = process.cwd()) {
3507
2889
  const resolved = resolveProvider(provider);
@@ -3510,8 +2892,8 @@ async function loadSecret(provider, cwd = process.cwd()) {
3510
2892
  }
3511
2893
  try {
3512
2894
  const dir = await ensureLocalDataDir(cwd);
3513
- const file = import_node_path7.default.join(dir, SECRET_FILE);
3514
- 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");
3515
2897
  return JSON.parse(raw);
3516
2898
  } catch {
3517
2899
  return null;
@@ -3519,10 +2901,10 @@ async function loadSecret(provider, cwd = process.cwd()) {
3519
2901
  }
3520
2902
 
3521
2903
  // ../ada-agent/dist/dependency-installer.js
3522
- var import_node_module2 = require("node:module");
2904
+ var import_node_module = require("node:module");
3523
2905
  var import_node_child_process2 = require("node:child_process");
3524
- var import_promises7 = __toESM(require("node:fs/promises"), 1);
3525
- 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);
3526
2908
 
3527
2909
  // ../ada-agent/dist/logger.js
3528
2910
  var baseLog = createJsonLogger("ada-agent");
@@ -3532,13 +2914,13 @@ function log(level, payload) {
3532
2914
 
3533
2915
  // ../../packages/native-drivers/src/index.ts
3534
2916
  var import_node_child_process = require("node:child_process");
3535
- var import_promises6 = __toESM(require("node:fs/promises"), 1);
3536
- 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);
3537
2919
  var DEFAULT_NATIVE_DRIVERS_DIR = "dirver";
3538
2920
  var CHROME_FOR_TESTING_JSON = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json";
3539
2921
  async function fileExists(filePath) {
3540
2922
  try {
3541
- await import_promises6.default.access(filePath);
2923
+ await import_promises4.default.access(filePath);
3542
2924
  return true;
3543
2925
  } catch {
3544
2926
  return false;
@@ -3546,19 +2928,19 @@ async function fileExists(filePath) {
3546
2928
  }
3547
2929
  async function dirExists(dirPath) {
3548
2930
  try {
3549
- const stat = await import_promises6.default.stat(dirPath);
2931
+ const stat = await import_promises4.default.stat(dirPath);
3550
2932
  return stat.isDirectory();
3551
2933
  } catch {
3552
2934
  return false;
3553
2935
  }
3554
2936
  }
3555
- async function resolveWorkspaceRoot4(cwd = process.cwd()) {
3556
- let current = import_node_path8.default.resolve(cwd);
2937
+ async function resolveWorkspaceRoot3(cwd = process.cwd()) {
2938
+ let current = import_node_path4.default.resolve(cwd);
3557
2939
  for (let i = 0; i < 8; i += 1) {
3558
- const pkg = import_node_path8.default.join(current, "package.json");
2940
+ const pkg = import_node_path4.default.join(current, "package.json");
3559
2941
  if (await fileExists(pkg)) {
3560
2942
  try {
3561
- const raw = await import_promises6.default.readFile(pkg, "utf8");
2943
+ const raw = await import_promises4.default.readFile(pkg, "utf8");
3562
2944
  const parsed = JSON.parse(raw);
3563
2945
  if (parsed.workspaces) {
3564
2946
  return current;
@@ -3566,27 +2948,27 @@ async function resolveWorkspaceRoot4(cwd = process.cwd()) {
3566
2948
  } catch {
3567
2949
  }
3568
2950
  }
3569
- const parent = import_node_path8.default.dirname(current);
2951
+ const parent = import_node_path4.default.dirname(current);
3570
2952
  if (parent === current) {
3571
2953
  break;
3572
2954
  }
3573
2955
  current = parent;
3574
2956
  }
3575
- return import_node_path8.default.resolve(cwd);
2957
+ return import_node_path4.default.resolve(cwd);
3576
2958
  }
3577
2959
  async function resolveNativeDriversDir(workspaceRoot) {
3578
- const root = workspaceRoot ? import_node_path8.default.resolve(workspaceRoot) : await resolveWorkspaceRoot4();
2960
+ const root = workspaceRoot ? import_node_path4.default.resolve(workspaceRoot) : await resolveWorkspaceRoot3();
3579
2961
  const fromEnv = process.env.ADA_DRIVERS_DIR?.trim();
3580
2962
  if (fromEnv) {
3581
- 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);
3582
2964
  }
3583
2965
  for (const name of [DEFAULT_NATIVE_DRIVERS_DIR, "driver", "drivers"]) {
3584
- const candidate = import_node_path8.default.join(root, name);
2966
+ const candidate = import_node_path4.default.join(root, name);
3585
2967
  if (await dirExists(candidate)) {
3586
2968
  return candidate;
3587
2969
  }
3588
2970
  }
3589
- return import_node_path8.default.join(root, DEFAULT_NATIVE_DRIVERS_DIR);
2971
+ return import_node_path4.default.join(root, DEFAULT_NATIVE_DRIVERS_DIR);
3590
2972
  }
3591
2973
  function platformArchiveSuffix() {
3592
2974
  if (process.platform === "win32") {
@@ -3621,7 +3003,7 @@ async function listExecutablesInDir(driversDir) {
3621
3003
  if (!await dirExists(driversDir)) {
3622
3004
  return [];
3623
3005
  }
3624
- const entries = await import_promises6.default.readdir(driversDir, { withFileTypes: true });
3006
+ const entries = await import_promises4.default.readdir(driversDir, { withFileTypes: true });
3625
3007
  const files = [];
3626
3008
  for (const ent of entries) {
3627
3009
  if (ent.isFile()) {
@@ -3652,16 +3034,16 @@ async function findGeckodriverInDir(driversDir, version) {
3652
3034
  const names = await listLocalGeckodriverCandidates(driversDir);
3653
3035
  const prefer = geckodriverExeName();
3654
3036
  if (names.includes(prefer)) {
3655
- return import_node_path8.default.join(driversDir, prefer);
3037
+ return import_node_path4.default.join(driversDir, prefer);
3656
3038
  }
3657
3039
  if (version) {
3658
3040
  const tagged = names.find((n) => n.includes(version.replace(/^v/, "")));
3659
3041
  if (tagged) {
3660
- return import_node_path8.default.join(driversDir, tagged);
3042
+ return import_node_path4.default.join(driversDir, tagged);
3661
3043
  }
3662
3044
  }
3663
3045
  if (names.length > 0) {
3664
- return import_node_path8.default.join(driversDir, names[0]);
3046
+ return import_node_path4.default.join(driversDir, names[0]);
3665
3047
  }
3666
3048
  return void 0;
3667
3049
  }
@@ -3671,19 +3053,19 @@ async function findChromedriverInDir(driversDir, version) {
3671
3053
  }
3672
3054
  const wantMajor = version && version !== "latest" ? version.replace(/^v/i, "").split(".")[0] : void 0;
3673
3055
  if (wantMajor) {
3674
- const named = import_node_path8.default.join(driversDir, chromedriverExeName(wantMajor));
3056
+ const named = import_node_path4.default.join(driversDir, chromedriverExeName(wantMajor));
3675
3057
  if (await fileExists(named)) {
3676
3058
  return { path: named, version: wantMajor };
3677
3059
  }
3678
3060
  }
3679
- const generic = import_node_path8.default.join(driversDir, chromedriverExeName());
3061
+ const generic = import_node_path4.default.join(driversDir, chromedriverExeName());
3680
3062
  if (await fileExists(generic)) {
3681
3063
  return { path: generic, version: wantMajor ?? "generic" };
3682
3064
  }
3683
3065
  const locals = await listLocalChromedriverVersions(driversDir);
3684
3066
  if (locals.length > 0) {
3685
3067
  const pick = wantMajor && locals.includes(wantMajor) ? wantMajor : locals[0];
3686
- 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 };
3687
3069
  }
3688
3070
  return void 0;
3689
3071
  }
@@ -3750,7 +3132,7 @@ async function commandOnPath(command) {
3750
3132
  child.on("error", () => resolve(false));
3751
3133
  });
3752
3134
  }
3753
- async function runCommand2(command, args) {
3135
+ async function runCommand(command, args) {
3754
3136
  return new Promise((resolve, reject) => {
3755
3137
  const child = (0, import_node_child_process.spawn)(command, args, {
3756
3138
  stdio: "inherit",
@@ -3767,23 +3149,23 @@ async function runCommand2(command, args) {
3767
3149
  });
3768
3150
  }
3769
3151
  async function extractZip(zipPath, destDir) {
3770
- await import_promises6.default.mkdir(destDir, { recursive: true });
3152
+ await import_promises4.default.mkdir(destDir, { recursive: true });
3771
3153
  if (process.platform === "win32") {
3772
3154
  const escapedZip = zipPath.replace(/'/g, "''");
3773
3155
  const escapedDest = destDir.replace(/'/g, "''");
3774
- await runCommand2("powershell", [
3156
+ await runCommand("powershell", [
3775
3157
  "-NoProfile",
3776
3158
  "-Command",
3777
3159
  `Expand-Archive -LiteralPath '${escapedZip}' -DestinationPath '${escapedDest}' -Force`
3778
3160
  ]);
3779
3161
  return;
3780
3162
  }
3781
- await runCommand2("unzip", ["-o", zipPath, "-d", destDir]);
3163
+ await runCommand("unzip", ["-o", zipPath, "-d", destDir]);
3782
3164
  }
3783
3165
  async function findFileRecursive(dir, fileName) {
3784
- const entries = await import_promises6.default.readdir(dir, { withFileTypes: true });
3166
+ const entries = await import_promises4.default.readdir(dir, { withFileTypes: true });
3785
3167
  for (const ent of entries) {
3786
- const full = import_node_path8.default.join(dir, ent.name);
3168
+ const full = import_node_path4.default.join(dir, ent.name);
3787
3169
  if (ent.isFile() && ent.name.toLowerCase() === fileName.toLowerCase()) {
3788
3170
  return full;
3789
3171
  }
@@ -3797,10 +3179,10 @@ async function findFileRecursive(dir, fileName) {
3797
3179
  return void 0;
3798
3180
  }
3799
3181
  async function copyExecutable(src, dest) {
3800
- await import_promises6.default.mkdir(import_node_path8.default.dirname(dest), { recursive: true });
3801
- 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);
3802
3184
  if (process.platform !== "win32") {
3803
- await import_promises6.default.chmod(dest, 493);
3185
+ await import_promises4.default.chmod(dest, 493);
3804
3186
  }
3805
3187
  }
3806
3188
  async function fetchGeckodriverReleaseVersion(requested) {
@@ -3868,8 +3250,8 @@ async function detectInstalledChromeMajorVersion() {
3868
3250
  return void 0;
3869
3251
  }
3870
3252
  const candidates = [
3871
- import_node_path8.default.join(process.env["ProgramFiles"] ?? "C:\\Program Files", "Google", "Chrome", "Application", "chrome.exe"),
3872
- 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(
3873
3255
  process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)",
3874
3256
  "Google",
3875
3257
  "Chrome",
@@ -3917,8 +3299,8 @@ async function downloadToFile(url, destPath) {
3917
3299
  throw new Error(`Download failed ${url}: HTTP ${res.status}`);
3918
3300
  }
3919
3301
  const buf = Buffer.from(await res.arrayBuffer());
3920
- await import_promises6.default.mkdir(import_node_path8.default.dirname(destPath), { recursive: true });
3921
- 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);
3922
3304
  }
3923
3305
  async function downloadGeckodriver(driversDir, versionInput, onLogLine) {
3924
3306
  const tag = await fetchGeckodriverReleaseVersion(versionInput);
@@ -3926,22 +3308,22 @@ async function downloadGeckodriver(driversDir, versionInput, onLogLine) {
3926
3308
  const suffix = platformArchiveSuffix();
3927
3309
  const assetName = `geckodriver-${tag}-${suffix}`.replace("vv", "v");
3928
3310
  const url = `https://github.com/mozilla/geckodriver/releases/download/${tag}/geckodriver-${tag}-${suffix}`;
3929
- await import_promises6.default.mkdir(driversDir, { recursive: true });
3930
- const zipPath = import_node_path8.default.join(driversDir, `_download_geckodriver_${version}.zip`);
3931
- 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}`);
3932
3314
  onLogLine?.(`[selenium] \u4E0B\u8F7D geckodriver ${tag} \uFFFD\uFFFD?${driversDir}`);
3933
3315
  onLogLine?.(`[selenium] URL: ${url}`);
3934
3316
  await downloadToFile(url, zipPath);
3935
- await import_promises6.default.rm(extractDir, { recursive: true, force: true });
3317
+ await import_promises4.default.rm(extractDir, { recursive: true, force: true });
3936
3318
  await extractZip(zipPath, extractDir);
3937
3319
  const found = await findFileRecursive(extractDir, geckodriverExeName());
3938
3320
  if (!found) {
3939
3321
  throw new Error(`geckodriver binary not found after extracting ${assetName}`);
3940
3322
  }
3941
- const dest = import_node_path8.default.join(driversDir, geckodriverExeName());
3323
+ const dest = import_node_path4.default.join(driversDir, geckodriverExeName());
3942
3324
  await copyExecutable(found, dest);
3943
- await import_promises6.default.rm(zipPath, { force: true });
3944
- 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 });
3945
3327
  onLogLine?.(`[selenium] geckodriver \u5DF2\u5199\uFFFD\uFFFD? ${dest}`);
3946
3328
  return { path: dest, version: tag };
3947
3329
  }
@@ -3959,40 +3341,40 @@ async function downloadChromedriver(driversDir, versionInput, onLogLine) {
3959
3341
  if (!url) {
3960
3342
  throw new Error(`No chromedriver download for version ${fullVersion} platform ${platform}`);
3961
3343
  }
3962
- await import_promises6.default.mkdir(driversDir, { recursive: true });
3963
- const zipPath = import_node_path8.default.join(driversDir, `_download_chromedriver_${major}.zip`);
3964
- 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}`);
3965
3347
  onLogLine?.(`[selenium] \u4E0B\u8F7D chromedriver ${fullVersion} (\u4E3B\u7248\uFFFD\uFFFD?${major}) \uFFFD\uFFFD?${driversDir}`);
3966
3348
  onLogLine?.(`[selenium] URL: ${url}`);
3967
3349
  await downloadToFile(url, zipPath);
3968
- await import_promises6.default.rm(extractDir, { recursive: true, force: true });
3350
+ await import_promises4.default.rm(extractDir, { recursive: true, force: true });
3969
3351
  await extractZip(zipPath, extractDir);
3970
3352
  const found = await findFileRecursive(extractDir, chromedriverExeName());
3971
3353
  if (!found) {
3972
3354
  throw new Error(`chromedriver binary not found after extracting ${fullVersion}`);
3973
3355
  }
3974
- const destVersioned = import_node_path8.default.join(driversDir, chromedriverExeName(major));
3356
+ const destVersioned = import_node_path4.default.join(driversDir, chromedriverExeName(major));
3975
3357
  await copyExecutable(found, destVersioned);
3976
3358
  if (versionInput === "latest" || !versionInput) {
3977
- const destGeneric = import_node_path8.default.join(driversDir, chromedriverExeName());
3359
+ const destGeneric = import_node_path4.default.join(driversDir, chromedriverExeName());
3978
3360
  await copyExecutable(found, destGeneric);
3979
3361
  }
3980
- await import_promises6.default.rm(zipPath, { force: true });
3981
- 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 });
3982
3364
  onLogLine?.(`[selenium] chromedriver \u5DF2\u5199\uFFFD\uFFFD? ${destVersioned}`);
3983
3365
  return { path: destVersioned, version: fullVersion, major };
3984
3366
  }
3985
3367
  async function saveNativeDriverManifest(manifest, workspaceRoot) {
3986
- const root = workspaceRoot ?? await resolveWorkspaceRoot4();
3987
- const dir = import_node_path8.default.join(root, ".ada-agent");
3988
- await import_promises6.default.mkdir(dir, { recursive: true });
3989
- const file = import_node_path8.default.join(dir, "native-drivers.json");
3990
- 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");
3991
3373
  }
3992
3374
  async function ensureNativeWebDrivers(options = {}) {
3993
- const root = options.workspaceRoot ?? await resolveWorkspaceRoot4();
3375
+ const root = options.workspaceRoot ?? await resolveWorkspaceRoot3();
3994
3376
  const driversDir = options.driversDir ?? await resolveNativeDriversDir(root);
3995
- await import_promises6.default.mkdir(driversDir, { recursive: true });
3377
+ await import_promises4.default.mkdir(driversDir, { recursive: true });
3996
3378
  const log2 = options.onLogLine;
3997
3379
  log2?.(`[selenium] \u539F\u751F\u9A71\u52A8\u76EE\u5F55: ${driversDir}`);
3998
3380
  const localChromeVersions = await listLocalChromedriverVersions(driversDir);
@@ -4044,7 +3426,7 @@ async function ensureNativeWebDrivers(options = {}) {
4044
3426
  }
4045
3427
 
4046
3428
  // ../ada-agent/dist/dependency-installer.js
4047
- 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"));
4048
3430
  function shouldUseShell(command) {
4049
3431
  if (process.platform !== "win32") {
4050
3432
  return false;
@@ -4059,7 +3441,7 @@ function hasPackage(packageName) {
4059
3441
  return false;
4060
3442
  }
4061
3443
  }
4062
- function runCommand3(command, args, options) {
3444
+ function runCommand2(command, args, options) {
4063
3445
  return new Promise((resolve, reject) => {
4064
3446
  const onLogLine = options?.onLogLine;
4065
3447
  const child = (0, import_node_child_process2.spawn)(command, args, {
@@ -4223,7 +3605,7 @@ async function getAppiumMajorVersion() {
4223
3605
  let version = "";
4224
3606
  try {
4225
3607
  const pkgPath = require2.resolve("appium/package.json");
4226
- const raw = await import_promises7.default.readFile(pkgPath, "utf8");
3608
+ const raw = await import_promises5.default.readFile(pkgPath, "utf8");
4227
3609
  version = String(JSON.parse(raw).version ?? "");
4228
3610
  } catch {
4229
3611
  version = "";
@@ -4425,28 +3807,28 @@ async function runInstallWithPriority(config, packages, onLogLine) {
4425
3807
  const strategies = [
4426
3808
  {
4427
3809
  name: "pnpm",
4428
- run: () => runCommand3("pnpm", ["add", ...packages], {
3810
+ run: () => runCommand2("pnpm", ["add", ...packages], {
4429
3811
  timeoutMs: installStrategyTimeoutMs(),
4430
3812
  onLogLine
4431
3813
  })
4432
3814
  },
4433
3815
  {
4434
3816
  name: "pnpm-proxy",
4435
- run: () => runCommand3("pnpm", ["add", ...packages, "--registry", pnpmProxy], {
3817
+ run: () => runCommand2("pnpm", ["add", ...packages, "--registry", pnpmProxy], {
4436
3818
  timeoutMs: installStrategyTimeoutMs(),
4437
3819
  onLogLine
4438
3820
  })
4439
3821
  },
4440
3822
  {
4441
3823
  name: "npm",
4442
- run: () => runCommand3("npm", ["install", ...packages], {
3824
+ run: () => runCommand2("npm", ["install", ...packages], {
4443
3825
  timeoutMs: installStrategyTimeoutMs(),
4444
3826
  onLogLine
4445
3827
  })
4446
3828
  },
4447
3829
  {
4448
3830
  name: "npm-proxy",
4449
- run: () => runCommand3("npm", ["install", ...packages, "--registry", npmProxy], {
3831
+ run: () => runCommand2("npm", ["install", ...packages, "--registry", npmProxy], {
4450
3832
  timeoutMs: installStrategyTimeoutMs(),
4451
3833
  onLogLine
4452
3834
  })
@@ -4486,14 +3868,14 @@ async function runAppiumDriverInstallWithPriority(config, driver, onLogLine) {
4486
3868
  const strategies = [
4487
3869
  {
4488
3870
  name: "npm",
4489
- run: () => runCommand3("npm", installArgs, {
3871
+ run: () => runCommand2("npm", installArgs, {
4490
3872
  timeoutMs: installStrategyTimeoutMs(),
4491
3873
  onLogLine
4492
3874
  })
4493
3875
  },
4494
3876
  {
4495
3877
  name: "npm-proxy",
4496
- run: () => runCommand3("npm", installArgs, {
3878
+ run: () => runCommand2("npm", installArgs, {
4497
3879
  env: { npm_config_registry: npmProxy },
4498
3880
  timeoutMs: installStrategyTimeoutMs(),
4499
3881
  onLogLine
@@ -4549,7 +3931,7 @@ async function installPlaywrightBrowser(config, onLogLine) {
4549
3931
  const args = targets.length > 0 ? ["exec", "playwright", "install", ...targets] : ["exec", "playwright", "install"];
4550
3932
  const selectedHost = await detectBestPlaywrightHost(config);
4551
3933
  onLogLine?.(`[playwright] \u4E0B\u8F7D\u6D4F\u89C8\u5668\uFF08\u955C\u50CF ${selectedHost}\uFF09\uFF0C\u76EE\u6807: ${targets.length ? targets.join(",") : "all"}`);
4552
- await runCommand3("npm", args, {
3934
+ await runCommand2("npm", args, {
4553
3935
  env: { PLAYWRIGHT_DOWNLOAD_HOST: selectedHost },
4554
3936
  timeoutMs: installStrategyTimeoutMs(),
4555
3937
  onLogLine
@@ -4570,7 +3952,7 @@ async function checkPlaywrightLaunchable() {
4570
3952
  async function verifyAppiumCommand() {
4571
3953
  try {
4572
3954
  const pkgPath = require2.resolve("appium/package.json");
4573
- const raw = await import_promises7.default.readFile(pkgPath, "utf8");
3955
+ const raw = await import_promises5.default.readFile(pkgPath, "utf8");
4574
3956
  const version = String(JSON.parse(raw).version ?? "");
4575
3957
  if (!version) {
4576
3958
  throw new Error("appium version check failed");
@@ -4654,8 +4036,8 @@ function configWithPlaywrightTargets(config, targets) {
4654
4036
  };
4655
4037
  }
4656
4038
  async function ensureSeleniumNativeDrivers(config, options, onLogLine) {
4657
- const root = await resolveWorkspaceRoot3(process.cwd());
4658
- 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");
4659
4041
  const seleniumWebdriverInstalled = hasPackage("selenium-webdriver");
4660
4042
  if (seleniumWebdriverInstalled) {
4661
4043
  onLogLine?.("[selenium] npm \u5305 selenium-webdriver \u5DF2\u5B89\u88C5");
@@ -4688,17 +4070,17 @@ async function ensureSeleniumNativeDrivers(config, options, onLogLine) {
4688
4070
  };
4689
4071
  }
4690
4072
  async function prepareInstallHomes(onLogLine) {
4691
- const root = await resolveWorkspaceRoot3(process.cwd());
4692
- const projectAndroidHome = import_node_path9.default.join(root, "android-sdk");
4693
- 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");
4694
4076
  const envAndroid = (process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? "").trim();
4695
4077
  const envAppium = (process.env.APPIUM_HOME ?? "").trim();
4696
4078
  const androidHome = envAndroid || projectAndroidHome;
4697
4079
  const appiumHome = envAppium || projectAppiumHome;
4698
4080
  const usedProjectFallbackAndroid = !envAndroid;
4699
4081
  const usedProjectFallbackAppium = !envAppium;
4700
- await import_promises7.default.mkdir(androidHome, { recursive: true });
4701
- 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 });
4702
4084
  process.env.ANDROID_HOME = androidHome;
4703
4085
  process.env.ANDROID_SDK_ROOT = androidHome;
4704
4086
  process.env.APPIUM_HOME = appiumHome;
@@ -4730,8 +4112,8 @@ function resolveRequestedDrivers(config, only) {
4730
4112
  async function loadInstallState() {
4731
4113
  try {
4732
4114
  const dir = await ensureLocalDataDir(process.cwd());
4733
- const file = import_node_path9.default.join(dir, "deps-install-state.json");
4734
- 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");
4735
4117
  return JSON.parse(raw);
4736
4118
  } catch {
4737
4119
  return {};
@@ -4739,8 +4121,8 @@ async function loadInstallState() {
4739
4121
  }
4740
4122
  async function saveInstallState(state) {
4741
4123
  const dir = await ensureLocalDataDir(process.cwd());
4742
- const file = import_node_path9.default.join(dir, "deps-install-state.json");
4743
- 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");
4744
4126
  }
4745
4127
  async function ensureDriverDependencies(config, options) {
4746
4128
  const startedAt = Date.now();
@@ -4872,8 +4254,8 @@ async function ensureDriverDependencies(config, options) {
4872
4254
  let chromedriverPath;
4873
4255
  let availableChromedriverMajors;
4874
4256
  if (needSelenium) {
4875
- const root = await resolveWorkspaceRoot3(process.cwd());
4876
- 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");
4877
4259
  availableChromedriverMajors = await listLocalChromedriverVersions(nativeDriversDir);
4878
4260
  const resolved = await resolveNativeDrivers({
4879
4261
  workspaceRoot: root,
@@ -4912,50 +4294,270 @@ async function getDependencyHealth(config) {
4912
4294
  if (playwrightInstalled) {
4913
4295
  playwrightLaunchOk = await checkPlaywrightLaunchable();
4914
4296
  }
4915
- if (appiumInstalled) {
4916
- try {
4917
- const pkgPath = require2.resolve("appium/package.json");
4918
- const raw = await import_promises7.default.readFile(pkgPath, "utf8");
4919
- const version = String(JSON.parse(raw).version ?? "");
4920
- appiumCliOk = version.length > 0;
4921
- } catch {
4922
- 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;
4923
4528
  }
4924
- if (appiumCliOk) {
4925
- const installed = (await getInstalledAppiumDrivers()).map((x) => x.toLowerCase());
4926
- const required = config?.appium?.requiredDrivers && config.appium.requiredDrivers.length > 0 ? config.appium.requiredDrivers.map((x) => x.toLowerCase()) : ["uiautomator2", "xcuitest", "harmonyos"];
4927
- missingAppiumDrivers = required.filter((x) => !installed.includes(x));
4928
- 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;
4929
4541
  }
4542
+ host.register(plugin);
4543
+ loaded.push(plugin.manifest);
4930
4544
  }
4931
- const root = await resolveWorkspaceRoot3(process.cwd());
4932
- const driversDir = await resolveNativeDriversDir(root);
4933
- const native = await resolveNativeDrivers({ workspaceRoot: root, driversDir });
4934
- const seleniumWebdriverInstalled = hasPackage("selenium-webdriver");
4935
- const geckodriverOk = native.geckodriverOk;
4936
- const chromedriverOk = native.chromedriverOk;
4937
- return {
4938
- playwrightInstalled,
4939
- playwrightLaunchOk,
4940
- seleniumWebdriverInstalled,
4941
- geckodriverOk,
4942
- chromedriverOk,
4943
- appiumInstalled,
4944
- appiumCliOk,
4945
- appiumDriversOk,
4946
- missingAppiumDrivers
4947
- };
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);
4948
4557
  }
4949
-
4950
- // ../ada-agent/dist/doctor.js
4951
- var import_promises8 = __toESM(require("node:fs/promises"), 1);
4952
- var import_node_net = __toESM(require("node:net"), 1);
4953
- var import_node_path10 = __toESM(require("node:path"), 1);
4954
- var import_node_url2 = require("node:url");
4955
- var import_node_child_process3 = require("node:child_process");
4956
4558
 
4957
4559
  // ../ada-agent/dist/plugin-registry.js
4958
- function buildPluginHost2() {
4560
+ function buildPluginHost() {
4959
4561
  const host = new PluginHost();
4960
4562
  registerRuntimePlugins(host);
4961
4563
  return host;
@@ -4968,7 +4570,7 @@ function listBuiltInPluginManifests() {
4968
4570
  // ../ada-agent/dist/doctor.js
4969
4571
  async function dirExists2(dirPath) {
4970
4572
  try {
4971
- const stat = await import_promises8.default.stat(dirPath);
4573
+ const stat = await import_promises6.default.stat(dirPath);
4972
4574
  return stat.isDirectory();
4973
4575
  } catch {
4974
4576
  return false;
@@ -4985,11 +4587,11 @@ async function isPortAvailable(host, port) {
4985
4587
  });
4986
4588
  }
4987
4589
  async function runDoctor(config) {
4988
- const root = await resolveWorkspaceRoot3(process.cwd());
4590
+ const root = await resolveWorkspaceRoot2(process.cwd());
4989
4591
  const queue = {
4990
- inboxDir: import_node_path10.default.resolve(root, config.queue.inboxDir),
4991
- processedDir: import_node_path10.default.resolve(root, config.queue.processedDir),
4992
- 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)
4993
4595
  };
4994
4596
  const deps = await getDependencyHealth(config);
4995
4597
  const portFree = await isPortAvailable(config.bootstrapUI.host, config.bootstrapUI.port);
@@ -5100,7 +4702,7 @@ async function checkNativeBootstrap(config, root) {
5100
4702
  const hasPathHint = native.command.includes("/") || native.command.includes("\\") || native.command.startsWith(".");
5101
4703
  let reachable = false;
5102
4704
  if (hasPathHint) {
5103
- const resolved = import_node_path10.default.resolve(root, native.command);
4705
+ const resolved = import_node_path7.default.resolve(root, native.command);
5104
4706
  reachable = await fileExists2(resolved);
5105
4707
  } else {
5106
4708
  reachable = await commandExists(native.command);
@@ -5115,7 +4717,7 @@ async function checkNativeBootstrap(config, root) {
5115
4717
  }
5116
4718
  async function fileExists2(targetPath) {
5117
4719
  try {
5118
- await import_promises8.default.access(targetPath);
4720
+ await import_promises6.default.access(targetPath);
5119
4721
  return true;
5120
4722
  } catch {
5121
4723
  return false;
@@ -5123,7 +4725,7 @@ async function fileExists2(targetPath) {
5123
4725
  }
5124
4726
  async function checkAppiumServer(serverUrl) {
5125
4727
  try {
5126
- const parsed = new import_node_url2.URL(serverUrl);
4728
+ const parsed = new import_node_url.URL(serverUrl);
5127
4729
  const port = Number(parsed.port || (parsed.protocol === "https:" ? 443 : 80));
5128
4730
  const host = parsed.hostname;
5129
4731
  const reachable = !await isPortAvailable(host, port);
@@ -5142,7 +4744,7 @@ async function checkAppiumServer(serverUrl) {
5142
4744
  }
5143
4745
 
5144
4746
  // ../ada-agent/dist/setup-cli.js
5145
- var import_promises9 = __toESM(require("node:readline/promises"), 1);
4747
+ var import_promises7 = __toESM(require("node:readline/promises"), 1);
5146
4748
  var import_node_process = require("node:process");
5147
4749
  function parseTags(raw) {
5148
4750
  return raw.split(",").map((s) => s.trim()).filter(Boolean);
@@ -5154,7 +4756,7 @@ function ensureServerUrl(url) {
5154
4756
  return url;
5155
4757
  }
5156
4758
  async function runSetupCli() {
5157
- 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 });
5158
4760
  try {
5159
4761
  console.log("[ADA-AGENT] setup wizard (CLI mode)");
5160
4762
  const serverUrl = ensureServerUrl((await rl.question("Server URL (e.g. https://ada-control.example.com): ")).trim());
@@ -5251,7 +4853,7 @@ async function runSetupNative(config) {
5251
4853
  // ../ada-agent/dist/bootstrap-ui.js
5252
4854
  var import_node_http = __toESM(require("node:http"), 1);
5253
4855
  var import_node_crypto = require("node:crypto");
5254
- var import_node_url3 = require("node:url");
4856
+ var import_node_url2 = require("node:url");
5255
4857
 
5256
4858
  // ../ada-agent/dist/setup-state.js
5257
4859
  var PW_TARGET = /* @__PURE__ */ new Set([
@@ -5569,7 +5171,7 @@ function htmlPage(csrfToken, port, host) {
5569
5171
  </html>`;
5570
5172
  }
5571
5173
  function parseFormUrlEncoded(body) {
5572
- const form = new import_node_url3.URLSearchParams(body);
5174
+ const form = new import_node_url2.URLSearchParams(body);
5573
5175
  const deviceTags = (form.get("deviceTags") ?? "").split(",").map((tag) => tag.trim()).filter(Boolean);
5574
5176
  const pw = form.getAll("pw").map((x) => String(x).toLowerCase());
5575
5177
  const rtRaw = form.get("requestTimeoutMs");
@@ -5759,7 +5361,7 @@ async function runSetupUi(config) {
5759
5361
  });
5760
5362
  req.on("end", () => {
5761
5363
  void (async () => {
5762
- const form = new import_node_url3.URLSearchParams(body);
5364
+ const form = new import_node_url2.URLSearchParams(body);
5763
5365
  if (form.get("csrf") !== csrfToken) {
5764
5366
  res.writeHead(403);
5765
5367
  res.end("invalid csrf");
@@ -6132,12 +5734,12 @@ async function createRuntimeTransport(config, secret) {
6132
5734
  }
6133
5735
 
6134
5736
  // ../ada-agent/dist/queue-runner.js
6135
- var import_promises12 = __toESM(require("node:fs/promises"), 1);
6136
- 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);
6137
5739
 
6138
5740
  // ../ada-agent/dist/task-loader.js
6139
- var import_promises10 = __toESM(require("node:fs/promises"), 1);
6140
- function isObject4(value) {
5741
+ var import_promises8 = __toESM(require("node:fs/promises"), 1);
5742
+ function isObject3(value) {
6141
5743
  return typeof value === "object" && value !== null;
6142
5744
  }
6143
5745
  var allowedCommands = /* @__PURE__ */ new Set([
@@ -6169,44 +5771,286 @@ var allowedCommands = /* @__PURE__ */ new Set([
6169
5771
  "closeTab"
6170
5772
  ]);
6171
5773
  function assertTask(value, index) {
6172
- if (!isObject4(value)) {
5774
+ if (!isObject3(value)) {
6173
5775
  throw new Error(`Task[${index}] is not an object.`);
6174
5776
  }
6175
- const requestId = value.requestId;
6176
- const sessionId = value.sessionId;
6177
- const platform = value.platform;
6178
- const command = value.command;
6179
- if (typeof requestId !== "string" || typeof sessionId !== "string") {
6180
- 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 };
6181
5971
  }
6182
- if (platform !== "web" && platform !== "android" && platform !== "ios" && platform !== "harmony") {
6183
- 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;
6184
6003
  }
6185
- if (typeof command !== "string" || !allowedCommands.has(command)) {
6186
- throw new Error(`Task[${index}] command invalid: ${String(command)}`);
6004
+ listSessions() {
6005
+ return this.sessions.list();
6187
6006
  }
6188
- return {
6189
- requestId,
6190
- sessionId,
6191
- platform,
6192
- command,
6193
- payload: isObject4(value.payload) ? value.payload : void 0,
6194
- idempotencyKey: typeof value.idempotencyKey === "string" ? value.idempotencyKey : void 0
6195
- };
6196
- }
6197
- async function loadTaskFile(filePath) {
6198
- const raw = await import_promises10.default.readFile(filePath, "utf8");
6199
- const parsed = JSON.parse(raw);
6200
- if (!Array.isArray(parsed)) {
6201
- 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;
6202
6033
  }
6203
- return parsed.map((item, idx) => assertTask(item, idx));
6204
- }
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
+ };
6205
6049
 
6206
6050
  // ../ada-agent/dist/monitoring.js
6207
- var import_promises11 = __toESM(require("node:fs/promises"), 1);
6208
- var import_node_path11 = __toESM(require("node:path"), 1);
6209
- 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");
6210
6054
  function shouldMonitorCommand(command, config, index) {
6211
6055
  if (!config.monitoring.enabled) {
6212
6056
  return false;
@@ -6242,16 +6086,16 @@ async function executeMonitorScreenshot(command, context) {
6242
6086
  }
6243
6087
  return null;
6244
6088
  }
6245
- function getScreenshotPath2(result) {
6089
+ function getScreenshotPath(result) {
6246
6090
  const pathValue = result.data?.screenshot;
6247
6091
  return typeof pathValue === "string" ? pathValue : null;
6248
6092
  }
6249
6093
  function buildMonitorOutputPath(config, sourcePath, requestId, sessionId) {
6250
- const ext = import_node_path11.default.extname(sourcePath) || ".png";
6094
+ const ext = import_node_path8.default.extname(sourcePath) || ".png";
6251
6095
  if (config.monitoring.groupBySession) {
6252
- 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}`);
6253
6097
  }
6254
- return import_node_path11.default.join(config.monitoring.outputDir, `${requestId}${ext}`);
6098
+ return import_node_path8.default.join(config.monitoring.outputDir, `${requestId}${ext}`);
6255
6099
  }
6256
6100
  async function captureOperationMonitor(command, result, index, context) {
6257
6101
  if (!shouldMonitorCommand(command, context.config, index)) {
@@ -6268,16 +6112,16 @@ async function captureOperationMonitor(command, result, index, context) {
6268
6112
  });
6269
6113
  return;
6270
6114
  }
6271
- const sourcePath = getScreenshotPath2(monitorResult);
6115
+ const sourcePath = getScreenshotPath(monitorResult);
6272
6116
  if (!sourcePath) {
6273
6117
  return;
6274
6118
  }
6275
6119
  const targetPath = buildMonitorOutputPath(context.config, sourcePath, command.requestId, command.sessionId);
6276
- 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 });
6277
6121
  const { maxWidth, maxHeight, keepAspectRatio } = context.config.monitoring.resolution;
6278
6122
  const mw = Math.max(1, maxWidth);
6279
6123
  const mh = Math.max(1, maxHeight);
6280
- const image = await import_jimp2.Jimp.read(sourcePath);
6124
+ const image = await import_jimp.Jimp.read(sourcePath);
6281
6125
  if (keepAspectRatio) {
6282
6126
  image.scaleToFit({ w: mw, h: mh });
6283
6127
  } else {
@@ -6299,8 +6143,8 @@ async function captureOperationMonitor(command, result, index, context) {
6299
6143
  }
6300
6144
 
6301
6145
  // ../ada-agent/dist/runtime.js
6302
- async function runTaskset2(commands, options = {}) {
6303
- const executor = new TaskExecutor(buildPluginHost2());
6146
+ async function runTaskset(commands, options = {}) {
6147
+ const executor = new TaskExecutor(buildPluginHost());
6304
6148
  const results = [];
6305
6149
  const monitorJobs = [];
6306
6150
  for (const cmd of commands) {
@@ -6332,7 +6176,7 @@ async function runTaskset2(commands, options = {}) {
6332
6176
  return results;
6333
6177
  }
6334
6178
  async function runDemoTaskset(options = {}) {
6335
- await runTaskset2([
6179
+ await runTaskset([
6336
6180
  {
6337
6181
  requestId: "req-web-1",
6338
6182
  sessionId: "session-web",
@@ -6359,23 +6203,23 @@ async function runForegroundLoop(skipDemo = false, options = {}) {
6359
6203
 
6360
6204
  // ../ada-agent/dist/queue-runner.js
6361
6205
  async function ensureDirs(config) {
6362
- await import_promises12.default.mkdir(config.queue.inboxDir, { recursive: true });
6363
- await import_promises12.default.mkdir(config.queue.processedDir, { recursive: true });
6364
- 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 });
6365
6209
  }
6366
6210
  async function listTaskFiles(inboxDir) {
6367
- const entries = await import_promises12.default.readdir(inboxDir, { withFileTypes: true });
6368
- 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));
6369
6213
  }
6370
6214
  async function moveTo(targetDir, sourcePath) {
6371
- const fileName = import_node_path12.default.basename(sourcePath);
6372
- const destination = import_node_path12.default.join(targetDir, fileName);
6373
- 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);
6374
6218
  }
6375
6219
  async function writeFailedMeta(targetDir, sourcePath, meta) {
6376
- const fileName = `${import_node_path12.default.basename(sourcePath)}.error.json`;
6377
- const destination = import_node_path12.default.join(targetDir, fileName);
6378
- 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");
6379
6223
  }
6380
6224
  async function processQueueOnce(config, options = {}) {
6381
6225
  await ensureDirs(config);
@@ -6388,7 +6232,7 @@ async function processQueueOnce(config, options = {}) {
6388
6232
  attempt += 1;
6389
6233
  try {
6390
6234
  const tasks = await loadTaskFile(file);
6391
- await runTaskset2(tasks, options);
6235
+ await runTaskset(tasks, options);
6392
6236
  await moveTo(config.queue.processedDir, file);
6393
6237
  processedCount += 1;
6394
6238
  log("info", { event: "queue.file.processed", details: { file, attempt } });
@@ -6605,6 +6449,241 @@ async function runStartFlow(options) {
6605
6449
  }
6606
6450
  }
6607
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
+ async function runBootstrapInstallDeps(argv2) {
6518
+ const plan = resolveBootstrapInstallDeps(argv2);
6519
+ if (plan.skip) {
6520
+ console.error("[ADA-MCP] dependency bootstrap skipped (--skip-install-deps / ADA_MCP_SKIP_INSTALL_DEPS)");
6521
+ return;
6522
+ }
6523
+ const label = plan.scopes.join(",");
6524
+ console.error(`[ADA-MCP] dependency bootstrap start (scope=${label}, force=${plan.force})`);
6525
+ for (const scope of plan.scopes) {
6526
+ console.error(`[ADA-MCP] installing: ${scope}`);
6527
+ await installDependencies(scope, plan.force, (line) => {
6528
+ console.error(`[ADA-MCP] ${line}`);
6529
+ }, plan.extras);
6530
+ }
6531
+ console.error("[ADA-MCP] dependency bootstrap done");
6532
+ }
6533
+
6534
+ // src/main.ts
6535
+ var import_promises13 = __toESM(require("node:fs/promises"));
6536
+ var import_node_fs3 = require("node:fs");
6537
+ var import_node_net2 = __toESM(require("node:net"));
6538
+ var import_node_path13 = __toESM(require("node:path"));
6539
+ var import_node_child_process5 = require("node:child_process");
6540
+ var import_node_url4 = require("node:url");
6541
+ var import_server = require("@modelcontextprotocol/sdk/server/index.js");
6542
+ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
6543
+ var import_types = require("@modelcontextprotocol/sdk/types.js");
6544
+
6545
+ // src/executor.ts
6546
+ var import_node_fs2 = require("node:fs");
6547
+ var import_node_path10 = __toESM(require("node:path"));
6548
+ var import_node_url3 = require("node:url");
6549
+ var import_meta = {};
6550
+ function ensureBundledPluginDir() {
6551
+ if (process.env.ADA_PLUGIN_DIR?.trim()) {
6552
+ return;
6553
+ }
6554
+ const candidates = [];
6555
+ try {
6556
+ const here = import_node_path10.default.dirname((0, import_node_url3.fileURLToPath)(import_meta.url));
6557
+ candidates.push(import_node_path10.default.join(here, "..", "plugins"));
6558
+ } catch {
6559
+ }
6560
+ const dirname = globalThis.__dirname;
6561
+ if (typeof dirname === "string") {
6562
+ candidates.push(import_node_path10.default.join(dirname, "..", "plugins"));
6563
+ }
6564
+ for (const dir of candidates) {
6565
+ if ((0, import_node_fs2.existsSync)(dir)) {
6566
+ process.env.ADA_PLUGIN_DIR = dir;
6567
+ return;
6568
+ }
6569
+ }
6570
+ }
6571
+ function buildPluginHost2() {
6572
+ ensureBundledPluginDir();
6573
+ const host = new PluginHost();
6574
+ registerRuntimePlugins(host);
6575
+ return host;
6576
+ }
6577
+ var manifests = registerRuntimePlugins(new PluginHost());
6578
+ var sharedExecutor = new TaskExecutor(buildPluginHost2());
6579
+ async function runCommand3(command) {
6580
+ return sharedExecutor.execute(command);
6581
+ }
6582
+ async function runTaskset2(commands) {
6583
+ const results = [];
6584
+ for (const command of commands) {
6585
+ results.push(await sharedExecutor.execute(command));
6586
+ }
6587
+ return results;
6588
+ }
6589
+ function listActiveSessions() {
6590
+ return sharedExecutor.listSessions();
6591
+ }
6592
+ async function closeSession(platform, sessionId, options) {
6593
+ return sharedExecutor.closeSession(platform, sessionId, options);
6594
+ }
6595
+ async function closeAllSessions() {
6596
+ return sharedExecutor.closeAllSessions();
6597
+ }
6598
+
6599
+ // src/config.ts
6600
+ var import_promises11 = __toESM(require("node:fs/promises"));
6601
+ var import_node_path11 = __toESM(require("node:path"));
6602
+
6603
+ // src/bundled-config.generated.ts
6604
+ 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';
6605
+
6606
+ // src/config.ts
6607
+ var DEFAULT_CONFIG_RELATIVE2 = import_node_path11.default.join("config", "default.yaml");
6608
+ var LOCAL_DATA_DIR2 = import_node_path11.default.join(".ada-agent");
6609
+ var EFFECTIVE_CONFIG_FILE2 = import_node_path11.default.join(LOCAL_DATA_DIR2, "agent.config.yaml");
6610
+ async function resolveWorkspaceRoot4(startDir = process.cwd()) {
6611
+ return resolveWorkspaceRoot(DEFAULT_CONFIG_RELATIVE2, startDir);
6612
+ }
6613
+ async function loadAgentConfig(cwd = process.cwd()) {
6614
+ let root = await resolveWorkspaceRoot4(cwd);
6615
+ const defaultPath = import_node_path11.default.join(root, DEFAULT_CONFIG_RELATIVE2);
6616
+ let defaultRaw;
6617
+ try {
6618
+ defaultRaw = await import_promises11.default.readFile(defaultPath, "utf8");
6619
+ } catch {
6620
+ defaultRaw = bundledDefaultConfigYaml2;
6621
+ root = import_node_path11.default.dirname(process.execPath);
6622
+ }
6623
+ const defaultConfig2 = jsYaml.load(defaultRaw) ?? {};
6624
+ const effectivePath = import_node_path11.default.join(root, EFFECTIVE_CONFIG_FILE2);
6625
+ try {
6626
+ const effectiveFile = await import_promises11.default.readFile(effectivePath, "utf8");
6627
+ const effectiveConfig = jsYaml.load(effectiveFile) ?? {};
6628
+ return deepMerge(defaultConfig2, effectiveConfig);
6629
+ } catch {
6630
+ return defaultConfig2;
6631
+ }
6632
+ }
6633
+
6634
+ // src/monitoring.ts
6635
+ var import_promises12 = __toESM(require("node:fs/promises"));
6636
+ var import_node_path12 = __toESM(require("node:path"));
6637
+ var import_jimp2 = require("jimp");
6638
+ function getScreenshotPath2(result) {
6639
+ const value = result.data?.screenshot;
6640
+ return typeof value === "string" ? value : null;
6641
+ }
6642
+ function buildOutputPath(options, command) {
6643
+ if (options.groupBySession) {
6644
+ return import_node_path12.default.join(options.outputDir, command.sessionId, `${command.requestId}.png`);
6645
+ }
6646
+ return import_node_path12.default.join(options.outputDir, `${command.requestId}.png`);
6647
+ }
6648
+ async function captureMcpMonitor(command, result, options, runCommand4) {
6649
+ if (!options.enabled) {
6650
+ return null;
6651
+ }
6652
+ if (options.onFailureOnly && result.success) {
6653
+ return null;
6654
+ }
6655
+ if (command.command === "screenshot") {
6656
+ return null;
6657
+ }
6658
+ const shotResult = await runCommand4({
6659
+ requestId: `${command.requestId}-monitor`,
6660
+ sessionId: command.sessionId,
6661
+ platform: command.platform,
6662
+ command: "screenshot",
6663
+ payload: {
6664
+ ...command.payload ?? {},
6665
+ __monitorCapture: true
6666
+ }
6667
+ });
6668
+ if (!shotResult.success) {
6669
+ return null;
6670
+ }
6671
+ const sourcePath = getScreenshotPath2(shotResult);
6672
+ if (!sourcePath) {
6673
+ return null;
6674
+ }
6675
+ const targetPath = buildOutputPath(options, command);
6676
+ await import_promises12.default.mkdir(import_node_path12.default.dirname(targetPath), { recursive: true });
6677
+ const image = await import_jimp2.Jimp.read(sourcePath);
6678
+ if (options.keepAspectRatio) {
6679
+ image.scaleToFit({ w: options.maxWidth, h: options.maxHeight });
6680
+ } else {
6681
+ image.cover({ w: options.maxWidth, h: options.maxHeight });
6682
+ }
6683
+ await image.write(targetPath);
6684
+ return targetPath;
6685
+ }
6686
+
6608
6687
  // src/main.ts
6609
6688
  function textResult(data) {
6610
6689
  return {
@@ -7112,7 +7191,7 @@ function parseInstallScope(v) {
7112
7191
  if (v === "all" || v === "playwright" || v === "selenium" || v === "appium" || v === "drivers" || v === "mobile" || v === "android" || v === "ios" || v === "harmony") {
7113
7192
  return v;
7114
7193
  }
7115
- return "all";
7194
+ return "playwright";
7116
7195
  }
7117
7196
  function parseMonitorOptions(args) {
7118
7197
  const monitor = asRecord3(args.monitor);
@@ -7128,7 +7207,7 @@ function parseMonitorOptions(args) {
7128
7207
  };
7129
7208
  }
7130
7209
  function runMonitorCapture(command, result, options) {
7131
- const job = captureMcpMonitor(command, result, options, runCommand).then(() => void 0);
7210
+ const job = captureMcpMonitor(command, result, options, runCommand3).then(() => void 0);
7132
7211
  if (options.nonBlocking) {
7133
7212
  job.catch(() => void 0);
7134
7213
  return;
@@ -7186,10 +7265,10 @@ async function withTiming(label, fn) {
7186
7265
  async function executeWithTimeout(command, timeoutMs) {
7187
7266
  const effectiveTimeout = typeof timeoutMs === "number" && timeoutMs > 0 ? timeoutMs : 0;
7188
7267
  if (effectiveTimeout <= 0) {
7189
- return runCommand(command);
7268
+ return runCommand3(command);
7190
7269
  }
7191
7270
  return Promise.race([
7192
- runCommand(command),
7271
+ runCommand3(command),
7193
7272
  new Promise((resolve) => {
7194
7273
  setTimeout(() => {
7195
7274
  resolve({
@@ -7595,7 +7674,7 @@ function wireAdaMcpProtocolServer(mcp) {
7595
7674
  only: {
7596
7675
  type: "string",
7597
7676
  enum: ["all", "playwright", "selenium", "mobile", "android", "ios", "harmony", "appium", "drivers"],
7598
- description: "Install scope"
7677
+ description: "Install scope (default playwright when omitted)"
7599
7678
  },
7600
7679
  force: { type: "boolean", description: "Force reinstall selected scope" },
7601
7680
  nativeDriversDir: {
@@ -7971,7 +8050,7 @@ function wireAdaMcpProtocolServer(mcp) {
7971
8050
  script = `(() => (document.body?.innerText || '').slice(0, 5000))()`;
7972
8051
  }
7973
8052
  ensureRiskAllowed("custom", args);
7974
- const result = await runCommand(
8053
+ const result = await runCommand3(
7975
8054
  toCommandEnvelope({
7976
8055
  requestId: `extract-${Date.now()}`,
7977
8056
  sessionId,
@@ -8000,7 +8079,7 @@ function wireAdaMcpProtocolServer(mcp) {
8000
8079
  command = "assertText";
8001
8080
  } else if (type2 === "url") {
8002
8081
  ensureRiskAllowed("custom", args);
8003
- const result2 = await runCommand(
8082
+ const result2 = await runCommand3(
8004
8083
  toCommandEnvelope({
8005
8084
  requestId: `assert-url-${Date.now()}`,
8006
8085
  sessionId,
@@ -8023,7 +8102,7 @@ function wireAdaMcpProtocolServer(mcp) {
8023
8102
  actualUrl: actual
8024
8103
  });
8025
8104
  }
8026
- const result = await runCommand(
8105
+ const result = await runCommand3(
8027
8106
  toCommandEnvelope({
8028
8107
  requestId: `assert-${Date.now()}`,
8029
8108
  sessionId,
@@ -8046,7 +8125,7 @@ function wireAdaMcpProtocolServer(mcp) {
8046
8125
  const payload = asRecord3(args.payload);
8047
8126
  if (type2 === "pageSource") {
8048
8127
  ensureRiskAllowed("custom", args);
8049
- const result2 = await runCommand(
8128
+ const result2 = await runCommand3(
8050
8129
  toCommandEnvelope({
8051
8130
  requestId: `mobile-page-source-${Date.now()}`,
8052
8131
  sessionId,
@@ -8071,7 +8150,7 @@ function wireAdaMcpProtocolServer(mcp) {
8071
8150
  })
8072
8151
  );
8073
8152
  }
8074
- const result = await runCommand(
8153
+ const result = await runCommand3(
8075
8154
  toCommandEnvelope({
8076
8155
  requestId: `mobile-extract-${Date.now()}`,
8077
8156
  sessionId,
@@ -8101,7 +8180,7 @@ function wireAdaMcpProtocolServer(mcp) {
8101
8180
  const type2 = typeof args.type === "string" ? args.type : "visible";
8102
8181
  const payload = asRecord3(args.payload);
8103
8182
  const command = type2 === "text" ? "assertText" : "assertVisible";
8104
- const result = await runCommand(
8183
+ const result = await runCommand3(
8105
8184
  toCommandEnvelope({
8106
8185
  requestId: `mobile-assert-${Date.now()}`,
8107
8186
  sessionId,
@@ -8120,7 +8199,7 @@ function wireAdaMcpProtocolServer(mcp) {
8120
8199
  }
8121
8200
  const taskPath = import_node_path13.default.isAbsolute(file) ? file : import_node_path13.default.resolve(process.cwd(), file);
8122
8201
  const tasks = await loadTaskFile2(taskPath);
8123
- const results = await runTaskset(tasks);
8202
+ const results = await runTaskset2(tasks);
8124
8203
  const monitor = parseMonitorOptions(args);
8125
8204
  const monitorJobs = [];
8126
8205
  for (let i = 0; i < tasks.length; i += 1) {
@@ -8142,7 +8221,7 @@ function wireAdaMcpProtocolServer(mcp) {
8142
8221
  ensureRiskAllowed(normalizeCommand(args.command), args);
8143
8222
  const command = toCommandEnvelope(args);
8144
8223
  await withTiming(`ensureAppiumServerReady(${command.platform})`, () => ensureAppiumServerReady(command.platform));
8145
- const result = await withTiming(`runCommand(${command.platform}:${command.command})`, () => runCommand(command));
8224
+ const result = await withTiming(`runCommand(${command.platform}:${command.command})`, () => runCommand3(command));
8146
8225
  const maybeJob = runMonitorCapture(command, result, parseMonitorOptions(args));
8147
8226
  if (maybeJob) {
8148
8227
  await maybeJob;
@@ -8165,7 +8244,7 @@ function wireAdaMcpProtocolServer(mcp) {
8165
8244
  payload
8166
8245
  };
8167
8246
  await withTiming(`ensureAppiumServerReady(${platform})`, () => ensureAppiumServerReady(platform));
8168
- const result = await withTiming(`runCommand(${platform}:invoke)`, () => runCommand(envelope));
8247
+ const result = await withTiming(`runCommand(${platform}:invoke)`, () => runCommand3(envelope));
8169
8248
  const maybeJob = runMonitorCapture(envelope, result, parseMonitorOptions(args));
8170
8249
  if (maybeJob) {
8171
8250
  await maybeJob;
@@ -8185,7 +8264,7 @@ function wireAdaMcpProtocolServer(mcp) {
8185
8264
  command,
8186
8265
  payload: mergeWebEngineIntoPayload(args)
8187
8266
  });
8188
- const result = await withTiming(`runCommand(web:${command})`, () => runCommand(envelope));
8267
+ const result = await withTiming(`runCommand(web:${command})`, () => runCommand3(envelope));
8189
8268
  const maybeJob = runMonitorCapture(envelope, result, parseMonitorOptions(args));
8190
8269
  if (maybeJob) {
8191
8270
  await maybeJob;
@@ -8207,7 +8286,7 @@ function wireAdaMcpProtocolServer(mcp) {
8207
8286
  command
8208
8287
  });
8209
8288
  await withTiming(`ensureAppiumServerReady(${envelope.platform})`, () => ensureAppiumServerReady(envelope.platform));
8210
- const result = await withTiming(`runCommand(${envelope.platform}:${command})`, () => runCommand(envelope));
8289
+ const result = await withTiming(`runCommand(${envelope.platform}:${command})`, () => runCommand3(envelope));
8211
8290
  const maybeJob = runMonitorCapture(envelope, result, parseMonitorOptions(args));
8212
8291
  if (maybeJob) {
8213
8292
  await maybeJob;
@@ -8249,6 +8328,7 @@ async function startMcpServer() {
8249
8328
  cwd,
8250
8329
  env: {
8251
8330
  ADA_PLAYWRIGHT_HEADLESS: "true",
8331
+ ADA_MCP_INSTALL_DEPS: "playwright",
8252
8332
  ADA_NPM_PROXY_REGISTRY: "https://registry.npmmirror.com",
8253
8333
  ADA_PNPM_PROXY_REGISTRY: "https://registry.npmmirror.com",
8254
8334
  ADA_INSTALL_STRATEGY_TIMEOUT_MS: "30000"
@@ -8391,7 +8471,7 @@ async function callLegacyTool(name, args, options) {
8391
8471
  command,
8392
8472
  payload
8393
8473
  };
8394
- return runCommand(envelope);
8474
+ return runCommand3(envelope);
8395
8475
  }
8396
8476
  throw new Error(`unsupported tool: ${name}`);
8397
8477
  }
@@ -8581,12 +8661,18 @@ if (isServerMode) {
8581
8661
  console.error("missing api key, set --api-key=xxx or ADA_MCP_REMOTE_API_KEY");
8582
8662
  process.exit(1);
8583
8663
  }
8584
- void startRemoteServer({ host, port, apiKey, allowRisky, riskyMode, riskyCommands, allowedHosts }).catch((error) => {
8664
+ void (async () => {
8665
+ await runBootstrapInstallDeps(argv);
8666
+ await startRemoteServer({ host, port, apiKey, allowRisky, riskyMode, riskyCommands, allowedHosts });
8667
+ })().catch((error) => {
8585
8668
  console.error(error);
8586
8669
  process.exit(1);
8587
8670
  });
8588
8671
  } else {
8589
- void startMcpServer().catch((error) => {
8672
+ void (async () => {
8673
+ await runBootstrapInstallDeps(argv);
8674
+ await startMcpServer();
8675
+ })().catch((error) => {
8590
8676
  console.error(error);
8591
8677
  process.exit(1);
8592
8678
  });