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