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