@openqa/cli 1.3.3 → 2.0.0
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 +1 -1
- package/dist/agent/brain/diff-analyzer.js +140 -0
- package/dist/agent/brain/diff-analyzer.js.map +1 -0
- package/dist/agent/brain/llm-cache.js +47 -0
- package/dist/agent/brain/llm-cache.js.map +1 -0
- package/dist/agent/brain/llm-resilience.js +252 -0
- package/dist/agent/brain/llm-resilience.js.map +1 -0
- package/dist/agent/config/index.js +588 -0
- package/dist/agent/config/index.js.map +1 -0
- package/dist/agent/coverage/index.js +74 -0
- package/dist/agent/coverage/index.js.map +1 -0
- package/dist/agent/export/index.js +158 -0
- package/dist/agent/export/index.js.map +1 -0
- package/dist/agent/index-v2.js +2795 -0
- package/dist/agent/index-v2.js.map +1 -0
- package/dist/agent/index.js +387 -55
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/logger.js +41 -0
- package/dist/agent/logger.js.map +1 -0
- package/dist/agent/metrics.js +39 -0
- package/dist/agent/metrics.js.map +1 -0
- package/dist/agent/notifications/index.js +106 -0
- package/dist/agent/notifications/index.js.map +1 -0
- package/dist/agent/openapi/spec.js +338 -0
- package/dist/agent/openapi/spec.js.map +1 -0
- package/dist/agent/tools/project-runner.js +481 -0
- package/dist/agent/tools/project-runner.js.map +1 -0
- package/dist/cli/config.html.js +454 -0
- package/dist/cli/daemon.js +7572 -0
- package/dist/cli/dashboard.html.js +1619 -0
- package/dist/cli/index.js +3624 -1675
- package/dist/cli/kanban.html.js +577 -0
- package/dist/cli/routes.js +895 -0
- package/dist/cli/routes.js.map +1 -0
- package/dist/cli/server.js +3564 -1646
- package/dist/database/index.js +503 -10
- package/dist/database/index.js.map +1 -1
- package/dist/database/sqlite.js +281 -0
- package/dist/database/sqlite.js.map +1 -0
- package/package.json +18 -5
package/dist/agent/index.js
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __esm = (fn, res) => function __init() {
|
|
3
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
var init_esm_shims = __esm({
|
|
10
|
+
"node_modules/tsup/assets/esm_shims.js"() {
|
|
11
|
+
"use strict";
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// database/sqlite.ts
|
|
16
|
+
import Database from "better-sqlite3";
|
|
17
|
+
var init_sqlite = __esm({
|
|
18
|
+
"database/sqlite.ts"() {
|
|
19
|
+
"use strict";
|
|
20
|
+
init_esm_shims();
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
1
24
|
// agent/index.ts
|
|
25
|
+
init_esm_shims();
|
|
2
26
|
import { ReActAgent as ReActAgent2 } from "@orka-js/agent";
|
|
3
27
|
import { OpenAIAdapter as OpenAIAdapter2 } from "@orka-js/openai";
|
|
4
28
|
import { AnthropicAdapter as AnthropicAdapter2 } from "@orka-js/anthropic";
|
|
@@ -7,13 +31,15 @@ import { Tracer } from "@orka-js/observability";
|
|
|
7
31
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
8
32
|
|
|
9
33
|
// database/index.ts
|
|
34
|
+
init_esm_shims();
|
|
35
|
+
init_sqlite();
|
|
10
36
|
import { Low } from "lowdb";
|
|
11
37
|
import { JSONFile } from "lowdb/node";
|
|
12
38
|
import { dirname } from "path";
|
|
13
|
-
import { fileURLToPath } from "url";
|
|
39
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14
40
|
import { mkdirSync } from "fs";
|
|
15
|
-
var
|
|
16
|
-
var
|
|
41
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
42
|
+
var __dirname2 = dirname(__filename2);
|
|
17
43
|
var OpenQADatabase = class {
|
|
18
44
|
constructor(dbPath = "./data/openqa.json") {
|
|
19
45
|
this.dbPath = dbPath;
|
|
@@ -29,7 +55,8 @@ var OpenQADatabase = class {
|
|
|
29
55
|
test_sessions: [],
|
|
30
56
|
actions: [],
|
|
31
57
|
bugs: [],
|
|
32
|
-
kanban_tickets: []
|
|
58
|
+
kanban_tickets: [],
|
|
59
|
+
users: []
|
|
33
60
|
});
|
|
34
61
|
this.db.read();
|
|
35
62
|
if (!this.db.data) {
|
|
@@ -38,7 +65,8 @@ var OpenQADatabase = class {
|
|
|
38
65
|
test_sessions: [],
|
|
39
66
|
actions: [],
|
|
40
67
|
bugs: [],
|
|
41
|
-
kanban_tickets: []
|
|
68
|
+
kanban_tickets: [],
|
|
69
|
+
users: []
|
|
42
70
|
};
|
|
43
71
|
this.db.write();
|
|
44
72
|
}
|
|
@@ -48,6 +76,12 @@ var OpenQADatabase = class {
|
|
|
48
76
|
this.initialize();
|
|
49
77
|
}
|
|
50
78
|
await this.db.read();
|
|
79
|
+
let migrated = false;
|
|
80
|
+
if (!this.db.data.users) {
|
|
81
|
+
this.db.data.users = [];
|
|
82
|
+
migrated = true;
|
|
83
|
+
}
|
|
84
|
+
if (migrated) await this.db.write();
|
|
51
85
|
}
|
|
52
86
|
async getConfig(key) {
|
|
53
87
|
await this.ensureInitialized();
|
|
@@ -171,17 +205,297 @@ var OpenQADatabase = class {
|
|
|
171
205
|
await this.ensureInitialized();
|
|
172
206
|
return this.db.data.kanban_tickets.filter((t) => t.column === column).sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
173
207
|
}
|
|
208
|
+
async deleteKanbanTicket(id) {
|
|
209
|
+
await this.ensureInitialized();
|
|
210
|
+
const index = this.db.data.kanban_tickets.findIndex((t) => t.id === id);
|
|
211
|
+
if (index !== -1) {
|
|
212
|
+
this.db.data.kanban_tickets.splice(index, 1);
|
|
213
|
+
await this.db.write();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
174
216
|
async clearAllConfig() {
|
|
175
217
|
await this.ensureInitialized();
|
|
176
218
|
this.db.data.config = {};
|
|
177
219
|
await this.db.write();
|
|
178
220
|
}
|
|
221
|
+
// Get real data methods - connected to actual database records
|
|
222
|
+
async getActiveAgents() {
|
|
223
|
+
await this.ensureInitialized();
|
|
224
|
+
const sessions = await this.getRecentSessions(1);
|
|
225
|
+
const currentSession = sessions[0];
|
|
226
|
+
const isRunning = currentSession?.status === "running";
|
|
227
|
+
const totalActions = currentSession?.total_actions || 0;
|
|
228
|
+
const agents = [
|
|
229
|
+
{
|
|
230
|
+
name: "Main Agent",
|
|
231
|
+
status: isRunning ? "running" : "idle",
|
|
232
|
+
purpose: "Autonomous QA orchestration",
|
|
233
|
+
performance: totalActions > 0 ? Math.min(100, Math.round(totalActions / 100 * 100)) : 0,
|
|
234
|
+
tasks: totalActions
|
|
235
|
+
}
|
|
236
|
+
];
|
|
237
|
+
if (currentSession && totalActions > 0) {
|
|
238
|
+
const actions = await this.getSessionActions(currentSession.id);
|
|
239
|
+
const actionTypes = actions.reduce((acc, action) => {
|
|
240
|
+
const type = action.type || "unknown";
|
|
241
|
+
acc[type] = (acc[type] || 0) + 1;
|
|
242
|
+
return acc;
|
|
243
|
+
}, {});
|
|
244
|
+
if (actionTypes["navigate"] || actionTypes["click"] || actionTypes["screenshot"]) {
|
|
245
|
+
agents.push({
|
|
246
|
+
name: "Browser Specialist",
|
|
247
|
+
status: isRunning ? "running" : "idle",
|
|
248
|
+
purpose: "UI navigation and interaction",
|
|
249
|
+
performance: Math.round(((actionTypes["navigate"] || 0) + (actionTypes["click"] || 0)) / totalActions * 100),
|
|
250
|
+
tasks: (actionTypes["navigate"] || 0) + (actionTypes["click"] || 0)
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
if (actionTypes["api_call"] || actionTypes["request"]) {
|
|
254
|
+
agents.push({
|
|
255
|
+
name: "API Tester",
|
|
256
|
+
status: isRunning ? "running" : "idle",
|
|
257
|
+
purpose: "API endpoint testing",
|
|
258
|
+
performance: Math.round((actionTypes["api_call"] || actionTypes["request"] || 0) / totalActions * 100),
|
|
259
|
+
tasks: actionTypes["api_call"] || actionTypes["request"] || 0
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (actionTypes["auth"] || actionTypes["login"]) {
|
|
263
|
+
agents.push({
|
|
264
|
+
name: "Auth Specialist",
|
|
265
|
+
status: isRunning ? "running" : "idle",
|
|
266
|
+
purpose: "Authentication testing",
|
|
267
|
+
performance: Math.round((actionTypes["auth"] || actionTypes["login"] || 0) / totalActions * 100),
|
|
268
|
+
tasks: actionTypes["auth"] || actionTypes["login"] || 0
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return agents;
|
|
273
|
+
}
|
|
274
|
+
async getCurrentTasks() {
|
|
275
|
+
await this.ensureInitialized();
|
|
276
|
+
const sessions = await this.getRecentSessions(1);
|
|
277
|
+
const currentSession = sessions[0];
|
|
278
|
+
if (!currentSession) {
|
|
279
|
+
return [];
|
|
280
|
+
}
|
|
281
|
+
const actions = await this.getSessionActions(currentSession.id);
|
|
282
|
+
const recentActions = actions.slice(-10).reverse();
|
|
283
|
+
return recentActions.map((action, index) => ({
|
|
284
|
+
id: action.id,
|
|
285
|
+
name: action.type || "Unknown Action",
|
|
286
|
+
status: index === 0 && currentSession.status === "running" ? "running" : "completed",
|
|
287
|
+
progress: index === 0 && currentSession.status === "running" ? "65%" : "100%",
|
|
288
|
+
agent: "Main Agent",
|
|
289
|
+
started_at: action.timestamp,
|
|
290
|
+
result: action.output || action.description || "Completed"
|
|
291
|
+
}));
|
|
292
|
+
}
|
|
293
|
+
async getCurrentIssues() {
|
|
294
|
+
await this.ensureInitialized();
|
|
295
|
+
const bugs = await this.getAllBugs();
|
|
296
|
+
return bugs.slice(0, 10).map((bug) => ({
|
|
297
|
+
id: bug.id,
|
|
298
|
+
title: bug.title,
|
|
299
|
+
description: bug.description,
|
|
300
|
+
severity: bug.severity || "medium",
|
|
301
|
+
status: bug.status || "open",
|
|
302
|
+
discovered_at: bug.created_at,
|
|
303
|
+
agent: "Main Agent"
|
|
304
|
+
}));
|
|
305
|
+
}
|
|
306
|
+
async pruneOldSessions(maxAgeDays) {
|
|
307
|
+
await this.ensureInitialized();
|
|
308
|
+
const cutoff = new Date(Date.now() - maxAgeDays * 864e5).toISOString();
|
|
309
|
+
const oldSessions = this.db.data.test_sessions.filter((s) => s.started_at < cutoff);
|
|
310
|
+
const oldSessionIds = new Set(oldSessions.map((s) => s.id));
|
|
311
|
+
const actionsBefore = this.db.data.actions.length;
|
|
312
|
+
this.db.data.actions = this.db.data.actions.filter((a) => !oldSessionIds.has(a.session_id));
|
|
313
|
+
const actionsRemoved = actionsBefore - this.db.data.actions.length;
|
|
314
|
+
this.db.data.test_sessions = this.db.data.test_sessions.filter((s) => s.started_at >= cutoff);
|
|
315
|
+
await this.db.write();
|
|
316
|
+
return { sessionsRemoved: oldSessions.length, actionsRemoved };
|
|
317
|
+
}
|
|
318
|
+
async getStorageStats() {
|
|
319
|
+
await this.ensureInitialized();
|
|
320
|
+
return {
|
|
321
|
+
sessions: this.db.data.test_sessions.length,
|
|
322
|
+
actions: this.db.data.actions.length,
|
|
323
|
+
bugs: this.db.data.bugs.length,
|
|
324
|
+
tickets: this.db.data.kanban_tickets.length
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
// ── User management ──────────────────────────────────────────────────────────
|
|
328
|
+
async countUsers() {
|
|
329
|
+
await this.ensureInitialized();
|
|
330
|
+
return this.db.data.users.length;
|
|
331
|
+
}
|
|
332
|
+
async findUserByUsername(username) {
|
|
333
|
+
await this.ensureInitialized();
|
|
334
|
+
return this.db.data.users.find((u) => u.username === username) ?? null;
|
|
335
|
+
}
|
|
336
|
+
async getUserById(id) {
|
|
337
|
+
await this.ensureInitialized();
|
|
338
|
+
return this.db.data.users.find((u) => u.id === id) ?? null;
|
|
339
|
+
}
|
|
340
|
+
async getAllUsers() {
|
|
341
|
+
await this.ensureInitialized();
|
|
342
|
+
return [...this.db.data.users];
|
|
343
|
+
}
|
|
344
|
+
async createUser(data) {
|
|
345
|
+
await this.ensureInitialized();
|
|
346
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
347
|
+
const user = {
|
|
348
|
+
id: `user_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
|
349
|
+
username: data.username,
|
|
350
|
+
passwordHash: data.passwordHash,
|
|
351
|
+
role: data.role,
|
|
352
|
+
createdAt: now,
|
|
353
|
+
updatedAt: now
|
|
354
|
+
};
|
|
355
|
+
this.db.data.users.push(user);
|
|
356
|
+
await this.db.write();
|
|
357
|
+
return user;
|
|
358
|
+
}
|
|
359
|
+
async updateUser(id, updates) {
|
|
360
|
+
await this.ensureInitialized();
|
|
361
|
+
const idx = this.db.data.users.findIndex((u) => u.id === id);
|
|
362
|
+
if (idx !== -1) {
|
|
363
|
+
this.db.data.users[idx] = {
|
|
364
|
+
...this.db.data.users[idx],
|
|
365
|
+
...updates,
|
|
366
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
367
|
+
};
|
|
368
|
+
await this.db.write();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
async deleteUser(id) {
|
|
372
|
+
await this.ensureInitialized();
|
|
373
|
+
const idx = this.db.data.users.findIndex((u) => u.id === id);
|
|
374
|
+
if (idx !== -1) {
|
|
375
|
+
this.db.data.users.splice(idx, 1);
|
|
376
|
+
await this.db.write();
|
|
377
|
+
}
|
|
378
|
+
}
|
|
179
379
|
async close() {
|
|
180
380
|
}
|
|
181
381
|
};
|
|
182
382
|
|
|
183
383
|
// agent/config/index.ts
|
|
384
|
+
init_esm_shims();
|
|
184
385
|
import { config as dotenvConfig } from "dotenv";
|
|
386
|
+
|
|
387
|
+
// agent/config/schema.ts
|
|
388
|
+
init_esm_shims();
|
|
389
|
+
import { z } from "zod";
|
|
390
|
+
var llmConfigSchema = z.object({
|
|
391
|
+
provider: z.enum(["openai", "anthropic", "ollama"]).default("openai"),
|
|
392
|
+
apiKey: z.string().optional(),
|
|
393
|
+
model: z.string().optional(),
|
|
394
|
+
baseUrl: z.string().url().optional()
|
|
395
|
+
});
|
|
396
|
+
var saasConfigSchema = z.object({
|
|
397
|
+
url: z.string().default(""),
|
|
398
|
+
authType: z.enum(["none", "basic", "bearer", "session"]).default("none"),
|
|
399
|
+
username: z.string().optional(),
|
|
400
|
+
password: z.string().optional()
|
|
401
|
+
});
|
|
402
|
+
var githubConfigSchema = z.object({
|
|
403
|
+
token: z.string().min(1, "GITHUB_TOKEN is required when GitHub is configured"),
|
|
404
|
+
owner: z.string().default(""),
|
|
405
|
+
repo: z.string().default("")
|
|
406
|
+
});
|
|
407
|
+
var agentConfigSchema = z.object({
|
|
408
|
+
intervalMs: z.number().int().positive().default(36e5),
|
|
409
|
+
maxIterations: z.number().int().positive().default(20),
|
|
410
|
+
autoStart: z.boolean().default(false)
|
|
411
|
+
});
|
|
412
|
+
var webConfigSchema = z.object({
|
|
413
|
+
port: z.number().int().min(1).max(65535).default(4242),
|
|
414
|
+
host: z.string().default("0.0.0.0")
|
|
415
|
+
});
|
|
416
|
+
var databaseConfigSchema = z.object({
|
|
417
|
+
path: z.string().default("./data/openqa.db")
|
|
418
|
+
});
|
|
419
|
+
var notificationsConfigSchema = z.object({
|
|
420
|
+
slack: z.string().url().optional(),
|
|
421
|
+
discord: z.string().url().optional()
|
|
422
|
+
});
|
|
423
|
+
var openQAConfigSchema = z.object({
|
|
424
|
+
llm: llmConfigSchema,
|
|
425
|
+
saas: saasConfigSchema,
|
|
426
|
+
github: githubConfigSchema.optional(),
|
|
427
|
+
agent: agentConfigSchema,
|
|
428
|
+
web: webConfigSchema,
|
|
429
|
+
database: databaseConfigSchema,
|
|
430
|
+
notifications: notificationsConfigSchema.optional()
|
|
431
|
+
});
|
|
432
|
+
var saasAppConfigSchema = z.object({
|
|
433
|
+
name: z.string().min(1, "SaaS application name is required"),
|
|
434
|
+
description: z.string().min(1, "SaaS application description is required"),
|
|
435
|
+
url: z.string().url("SaaS application URL must be a valid URL"),
|
|
436
|
+
repoUrl: z.string().url().optional(),
|
|
437
|
+
localPath: z.string().optional(),
|
|
438
|
+
techStack: z.array(z.string()).optional(),
|
|
439
|
+
authInfo: z.object({
|
|
440
|
+
type: z.enum(["none", "basic", "oauth", "session"]),
|
|
441
|
+
testCredentials: z.object({
|
|
442
|
+
username: z.string(),
|
|
443
|
+
password: z.string()
|
|
444
|
+
}).optional()
|
|
445
|
+
}).optional(),
|
|
446
|
+
directives: z.array(z.string()).optional()
|
|
447
|
+
});
|
|
448
|
+
function validateConfigSafe(config) {
|
|
449
|
+
const result = openQAConfigSchema.safeParse(config);
|
|
450
|
+
if (result.success) {
|
|
451
|
+
return { success: true, data: result.data };
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
success: false,
|
|
455
|
+
errors: result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`)
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// agent/logger.ts
|
|
460
|
+
init_esm_shims();
|
|
461
|
+
var LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
462
|
+
var MIN_LEVEL = process.env.LOG_LEVEL || "info";
|
|
463
|
+
function shouldLog(level) {
|
|
464
|
+
return LEVELS[level] >= LEVELS[MIN_LEVEL];
|
|
465
|
+
}
|
|
466
|
+
function format(level, message, context) {
|
|
467
|
+
const entry = {
|
|
468
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
469
|
+
level,
|
|
470
|
+
msg: message,
|
|
471
|
+
...context
|
|
472
|
+
};
|
|
473
|
+
return JSON.stringify(entry);
|
|
474
|
+
}
|
|
475
|
+
var logger = {
|
|
476
|
+
debug(message, context) {
|
|
477
|
+
if (shouldLog("debug")) process.stdout.write(format("debug", message, context) + "\n");
|
|
478
|
+
},
|
|
479
|
+
info(message, context) {
|
|
480
|
+
if (shouldLog("info")) process.stdout.write(format("info", message, context) + "\n");
|
|
481
|
+
},
|
|
482
|
+
warn(message, context) {
|
|
483
|
+
if (shouldLog("warn")) process.stderr.write(format("warn", message, context) + "\n");
|
|
484
|
+
},
|
|
485
|
+
error(message, context) {
|
|
486
|
+
if (shouldLog("error")) process.stderr.write(format("error", message, context) + "\n");
|
|
487
|
+
},
|
|
488
|
+
child(defaults) {
|
|
489
|
+
return {
|
|
490
|
+
debug: (msg, ctx) => logger.debug(msg, { ...defaults, ...ctx }),
|
|
491
|
+
info: (msg, ctx) => logger.info(msg, { ...defaults, ...ctx }),
|
|
492
|
+
warn: (msg, ctx) => logger.warn(msg, { ...defaults, ...ctx }),
|
|
493
|
+
error: (msg, ctx) => logger.error(msg, { ...defaults, ...ctx })
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// agent/config/index.ts
|
|
185
499
|
dotenvConfig();
|
|
186
500
|
var ConfigManager = class {
|
|
187
501
|
db = null;
|
|
@@ -190,7 +504,7 @@ var ConfigManager = class {
|
|
|
190
504
|
this.envConfig = this.loadFromEnv();
|
|
191
505
|
}
|
|
192
506
|
loadFromEnv() {
|
|
193
|
-
|
|
507
|
+
const raw = {
|
|
194
508
|
llm: {
|
|
195
509
|
provider: process.env.LLM_PROVIDER || "openai",
|
|
196
510
|
apiKey: process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY,
|
|
@@ -225,6 +539,12 @@ var ConfigManager = class {
|
|
|
225
539
|
discord: process.env.DISCORD_WEBHOOK_URL
|
|
226
540
|
}
|
|
227
541
|
};
|
|
542
|
+
const result = validateConfigSafe(raw);
|
|
543
|
+
if (!result.success) {
|
|
544
|
+
logger.warn("Config validation warnings", { errors: result.errors });
|
|
545
|
+
return raw;
|
|
546
|
+
}
|
|
547
|
+
return result.data;
|
|
228
548
|
}
|
|
229
549
|
getDB() {
|
|
230
550
|
if (!this.db) {
|
|
@@ -238,9 +558,13 @@ var ConfigManager = class {
|
|
|
238
558
|
const keys = key.split(".");
|
|
239
559
|
let value = this.envConfig;
|
|
240
560
|
for (const k of keys) {
|
|
241
|
-
value
|
|
561
|
+
if (value && typeof value === "object") {
|
|
562
|
+
value = value[k];
|
|
563
|
+
} else {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
242
566
|
}
|
|
243
|
-
return value
|
|
567
|
+
return value != null ? String(value) : null;
|
|
244
568
|
}
|
|
245
569
|
async set(key, value) {
|
|
246
570
|
await this.getDB().setConfig(key, value);
|
|
@@ -252,7 +576,7 @@ var ConfigManager = class {
|
|
|
252
576
|
const keys = key.split(".");
|
|
253
577
|
let obj = merged;
|
|
254
578
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
255
|
-
if (!obj[keys[i]]) obj[keys[i]] = {};
|
|
579
|
+
if (!obj[keys[i]] || typeof obj[keys[i]] !== "object") obj[keys[i]] = {};
|
|
256
580
|
obj = obj[keys[i]];
|
|
257
581
|
}
|
|
258
582
|
obj[keys[keys.length - 1]] = value;
|
|
@@ -269,6 +593,7 @@ var ConfigManager = class {
|
|
|
269
593
|
};
|
|
270
594
|
|
|
271
595
|
// agent/tools/browser.ts
|
|
596
|
+
init_esm_shims();
|
|
272
597
|
import { chromium } from "playwright";
|
|
273
598
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
274
599
|
import { join as join2 } from "path";
|
|
@@ -317,7 +642,7 @@ var BrowserTools = class {
|
|
|
317
642
|
});
|
|
318
643
|
return `Successfully navigated to ${url}. Page title: "${title}"`;
|
|
319
644
|
} catch (error) {
|
|
320
|
-
return `Failed to navigate: ${error.message}`;
|
|
645
|
+
return `Failed to navigate: ${error instanceof Error ? error.message : String(error)}`;
|
|
321
646
|
}
|
|
322
647
|
}
|
|
323
648
|
},
|
|
@@ -343,7 +668,7 @@ var BrowserTools = class {
|
|
|
343
668
|
});
|
|
344
669
|
return `Successfully clicked element: ${selector}`;
|
|
345
670
|
} catch (error) {
|
|
346
|
-
return `Failed to click element: ${error.message}`;
|
|
671
|
+
return `Failed to click element: ${error instanceof Error ? error.message : String(error)}`;
|
|
347
672
|
}
|
|
348
673
|
}
|
|
349
674
|
},
|
|
@@ -370,7 +695,7 @@ var BrowserTools = class {
|
|
|
370
695
|
});
|
|
371
696
|
return `Successfully filled input ${selector} with text`;
|
|
372
697
|
} catch (error) {
|
|
373
|
-
return `Failed to fill input: ${error.message}`;
|
|
698
|
+
return `Failed to fill input: ${error instanceof Error ? error.message : String(error)}`;
|
|
374
699
|
}
|
|
375
700
|
}
|
|
376
701
|
},
|
|
@@ -388,17 +713,17 @@ var BrowserTools = class {
|
|
|
388
713
|
if (!this.page) return "Browser not initialized. Navigate to a page first.";
|
|
389
714
|
try {
|
|
390
715
|
const filename = `${Date.now()}_${name}.png`;
|
|
391
|
-
const
|
|
392
|
-
await this.page.screenshot({ path, fullPage: true });
|
|
716
|
+
const path2 = join2(this.screenshotDir, filename);
|
|
717
|
+
await this.page.screenshot({ path: path2, fullPage: true });
|
|
393
718
|
this.db.createAction({
|
|
394
719
|
session_id: this.sessionId,
|
|
395
720
|
type: "screenshot",
|
|
396
721
|
description: `Screenshot: ${name}`,
|
|
397
|
-
screenshot_path:
|
|
722
|
+
screenshot_path: path2
|
|
398
723
|
});
|
|
399
|
-
return `Screenshot saved: ${
|
|
724
|
+
return `Screenshot saved: ${path2}`;
|
|
400
725
|
} catch (error) {
|
|
401
|
-
return `Failed to take screenshot: ${error.message}`;
|
|
726
|
+
return `Failed to take screenshot: ${error instanceof Error ? error.message : String(error)}`;
|
|
402
727
|
}
|
|
403
728
|
}
|
|
404
729
|
},
|
|
@@ -415,7 +740,7 @@ var BrowserTools = class {
|
|
|
415
740
|
const content = await this.page.textContent("body");
|
|
416
741
|
return content?.slice(0, 1e3) || "No content found";
|
|
417
742
|
} catch (error) {
|
|
418
|
-
return `Failed to get content: ${error.message}`;
|
|
743
|
+
return `Failed to get content: ${error instanceof Error ? error.message : String(error)}`;
|
|
419
744
|
}
|
|
420
745
|
}
|
|
421
746
|
},
|
|
@@ -454,6 +779,7 @@ ${errors.join("\n")}`;
|
|
|
454
779
|
};
|
|
455
780
|
|
|
456
781
|
// agent/tools/github.ts
|
|
782
|
+
init_esm_shims();
|
|
457
783
|
import { Octokit } from "@octokit/rest";
|
|
458
784
|
var GitHubTools = class {
|
|
459
785
|
octokit = null;
|
|
@@ -530,7 +856,7 @@ ${screenshot_path ? `**Screenshot:** ${screenshot_path}` : ""}
|
|
|
530
856
|
URL: ${issue.data.html_url}
|
|
531
857
|
Issue #${issue.data.number}`;
|
|
532
858
|
} catch (error) {
|
|
533
|
-
return `\u274C Failed to create GitHub issue: ${error.message}`;
|
|
859
|
+
return `\u274C Failed to create GitHub issue: ${error instanceof Error ? error.message : String(error)}`;
|
|
534
860
|
}
|
|
535
861
|
}
|
|
536
862
|
}
|
|
@@ -539,6 +865,7 @@ Issue #${issue.data.number}`;
|
|
|
539
865
|
};
|
|
540
866
|
|
|
541
867
|
// agent/tools/kanban.ts
|
|
868
|
+
init_esm_shims();
|
|
542
869
|
var KanbanTools = class {
|
|
543
870
|
db;
|
|
544
871
|
sessionId;
|
|
@@ -566,7 +893,7 @@ var KanbanTools = class {
|
|
|
566
893
|
execute: async ({ title, description, priority, column = "to-do", tags = [], screenshot_path }) => {
|
|
567
894
|
try {
|
|
568
895
|
const allTags = ["automated-qa", ...tags];
|
|
569
|
-
const ticket = this.db.createKanbanTicket({
|
|
896
|
+
const ticket = await this.db.createKanbanTicket({
|
|
570
897
|
title,
|
|
571
898
|
description,
|
|
572
899
|
priority,
|
|
@@ -574,7 +901,7 @@ var KanbanTools = class {
|
|
|
574
901
|
tags: JSON.stringify(allTags),
|
|
575
902
|
screenshot_url: screenshot_path
|
|
576
903
|
});
|
|
577
|
-
this.db.createAction({
|
|
904
|
+
await this.db.createAction({
|
|
578
905
|
session_id: this.sessionId,
|
|
579
906
|
type: "kanban_ticket",
|
|
580
907
|
description: `Created Kanban ticket: ${title}`,
|
|
@@ -586,7 +913,7 @@ ID: ${ticket.id}
|
|
|
586
913
|
Column: ${column}
|
|
587
914
|
Priority: ${priority}`;
|
|
588
915
|
} catch (error) {
|
|
589
|
-
return `\u274C Failed to create Kanban ticket: ${error.message}`;
|
|
916
|
+
return `\u274C Failed to create Kanban ticket: ${error instanceof Error ? error.message : String(error)}`;
|
|
590
917
|
}
|
|
591
918
|
}
|
|
592
919
|
},
|
|
@@ -607,10 +934,10 @@ Priority: ${priority}`;
|
|
|
607
934
|
const updates = {};
|
|
608
935
|
if (column) updates.column = column;
|
|
609
936
|
if (priority) updates.priority = priority;
|
|
610
|
-
this.db.updateKanbanTicket(ticket_id, updates);
|
|
937
|
+
await this.db.updateKanbanTicket(ticket_id, updates);
|
|
611
938
|
return `\u2705 Kanban ticket ${ticket_id} updated successfully!`;
|
|
612
939
|
} catch (error) {
|
|
613
|
-
return `\u274C Failed to update Kanban ticket: ${error.message}`;
|
|
940
|
+
return `\u274C Failed to update Kanban ticket: ${error instanceof Error ? error.message : String(error)}`;
|
|
614
941
|
}
|
|
615
942
|
}
|
|
616
943
|
},
|
|
@@ -623,7 +950,7 @@ Priority: ${priority}`;
|
|
|
623
950
|
},
|
|
624
951
|
execute: async () => {
|
|
625
952
|
try {
|
|
626
|
-
const tickets = this.db.getKanbanTickets();
|
|
953
|
+
const tickets = await this.db.getKanbanTickets();
|
|
627
954
|
const byColumn = {
|
|
628
955
|
backlog: tickets.filter((t) => t.column === "backlog"),
|
|
629
956
|
"to-do": tickets.filter((t) => t.column === "to-do"),
|
|
@@ -641,7 +968,7 @@ Total: ${tickets.length} tickets
|
|
|
641
968
|
`.trim();
|
|
642
969
|
return summary;
|
|
643
970
|
} catch (error) {
|
|
644
|
-
return `\u274C Failed to get Kanban board: ${error.message}`;
|
|
971
|
+
return `\u274C Failed to get Kanban board: ${error instanceof Error ? error.message : String(error)}`;
|
|
645
972
|
}
|
|
646
973
|
}
|
|
647
974
|
}
|
|
@@ -650,6 +977,7 @@ Total: ${tickets.length} tickets
|
|
|
650
977
|
};
|
|
651
978
|
|
|
652
979
|
// agent/webhooks/git-listener.ts
|
|
980
|
+
init_esm_shims();
|
|
653
981
|
import { EventEmitter } from "events";
|
|
654
982
|
import { Octokit as Octokit2 } from "@octokit/rest";
|
|
655
983
|
var GitListener = class extends EventEmitter {
|
|
@@ -674,10 +1002,10 @@ var GitListener = class extends EventEmitter {
|
|
|
674
1002
|
async start() {
|
|
675
1003
|
if (this.isRunning) return;
|
|
676
1004
|
this.isRunning = true;
|
|
677
|
-
|
|
1005
|
+
logger.info("GitListener started", { provider: this.config.provider, owner: this.config.owner, repo: this.config.repo });
|
|
678
1006
|
await this.checkInitialState();
|
|
679
1007
|
this.pollInterval = setInterval(() => {
|
|
680
|
-
this.poll().catch(
|
|
1008
|
+
this.poll().catch((e) => logger.error("Poll error", { error: e instanceof Error ? e.message : String(e) }));
|
|
681
1009
|
}, this.config.pollIntervalMs);
|
|
682
1010
|
}
|
|
683
1011
|
stop() {
|
|
@@ -686,7 +1014,7 @@ var GitListener = class extends EventEmitter {
|
|
|
686
1014
|
clearInterval(this.pollInterval);
|
|
687
1015
|
this.pollInterval = null;
|
|
688
1016
|
}
|
|
689
|
-
|
|
1017
|
+
logger.info("GitListener stopped");
|
|
690
1018
|
}
|
|
691
1019
|
async checkInitialState() {
|
|
692
1020
|
try {
|
|
@@ -696,7 +1024,7 @@ var GitListener = class extends EventEmitter {
|
|
|
696
1024
|
await this.checkGitLabState();
|
|
697
1025
|
}
|
|
698
1026
|
} catch (error) {
|
|
699
|
-
|
|
1027
|
+
logger.error("Failed to check initial state", { error: error instanceof Error ? error.message : String(error) });
|
|
700
1028
|
}
|
|
701
1029
|
}
|
|
702
1030
|
async poll() {
|
|
@@ -707,7 +1035,7 @@ var GitListener = class extends EventEmitter {
|
|
|
707
1035
|
await this.pollGitLab();
|
|
708
1036
|
}
|
|
709
1037
|
} catch (error) {
|
|
710
|
-
|
|
1038
|
+
logger.error("Poll error", { error: error instanceof Error ? error.message : String(error) });
|
|
711
1039
|
}
|
|
712
1040
|
}
|
|
713
1041
|
async checkGitHubState() {
|
|
@@ -757,7 +1085,7 @@ var GitListener = class extends EventEmitter {
|
|
|
757
1085
|
this.emit("git-event", event);
|
|
758
1086
|
if (isMerge) {
|
|
759
1087
|
this.emit("merge", event);
|
|
760
|
-
|
|
1088
|
+
logger.info("Merge detected", { branch: this.config.branch, sha: commit.sha.slice(0, 7) });
|
|
761
1089
|
}
|
|
762
1090
|
}
|
|
763
1091
|
if (commits.length > 0) {
|
|
@@ -787,10 +1115,10 @@ var GitListener = class extends EventEmitter {
|
|
|
787
1115
|
this.emit("git-event", event);
|
|
788
1116
|
if (run.conclusion === "success") {
|
|
789
1117
|
this.emit("pipeline-success", event);
|
|
790
|
-
|
|
1118
|
+
logger.info("Pipeline success", { name: run.name, id: run.id });
|
|
791
1119
|
} else {
|
|
792
1120
|
this.emit("pipeline-failure", event);
|
|
793
|
-
|
|
1121
|
+
logger.warn("Pipeline failure", { name: run.name, id: run.id });
|
|
794
1122
|
}
|
|
795
1123
|
}
|
|
796
1124
|
}
|
|
@@ -822,7 +1150,7 @@ var GitListener = class extends EventEmitter {
|
|
|
822
1150
|
this.lastPipelineId = pipelines[0].id.toString();
|
|
823
1151
|
}
|
|
824
1152
|
} catch (error) {
|
|
825
|
-
|
|
1153
|
+
logger.error("GitLab initial state error", { error: error instanceof Error ? error.message : String(error) });
|
|
826
1154
|
}
|
|
827
1155
|
}
|
|
828
1156
|
async pollGitLab() {
|
|
@@ -850,7 +1178,7 @@ var GitListener = class extends EventEmitter {
|
|
|
850
1178
|
this.emit("git-event", event);
|
|
851
1179
|
if (isMerge) {
|
|
852
1180
|
this.emit("merge", event);
|
|
853
|
-
|
|
1181
|
+
logger.info("Merge detected", { branch: this.config.branch, id: commit.id.slice(0, 7) });
|
|
854
1182
|
}
|
|
855
1183
|
}
|
|
856
1184
|
if (commits.length > 0) {
|
|
@@ -878,10 +1206,10 @@ var GitListener = class extends EventEmitter {
|
|
|
878
1206
|
this.emit("git-event", event);
|
|
879
1207
|
if (pipeline.status === "success") {
|
|
880
1208
|
this.emit("pipeline-success", event);
|
|
881
|
-
|
|
1209
|
+
logger.info("Pipeline success", { id: pipeline.id });
|
|
882
1210
|
} else {
|
|
883
1211
|
this.emit("pipeline-failure", event);
|
|
884
|
-
|
|
1212
|
+
logger.warn("Pipeline failure", { id: pipeline.id });
|
|
885
1213
|
}
|
|
886
1214
|
}
|
|
887
1215
|
}
|
|
@@ -889,7 +1217,7 @@ var GitListener = class extends EventEmitter {
|
|
|
889
1217
|
this.lastPipelineId = pipelines[0].id.toString();
|
|
890
1218
|
}
|
|
891
1219
|
} catch (error) {
|
|
892
|
-
|
|
1220
|
+
logger.error("GitLab poll error", { error: error instanceof Error ? error.message : String(error) });
|
|
893
1221
|
}
|
|
894
1222
|
}
|
|
895
1223
|
async setupWebhook(webhookUrl) {
|
|
@@ -937,6 +1265,7 @@ var GitListener = class extends EventEmitter {
|
|
|
937
1265
|
};
|
|
938
1266
|
|
|
939
1267
|
// agent/specialists/index.ts
|
|
1268
|
+
init_esm_shims();
|
|
940
1269
|
import { ReActAgent } from "@orka-js/agent";
|
|
941
1270
|
import { OpenAIAdapter } from "@orka-js/openai";
|
|
942
1271
|
import { AnthropicAdapter } from "@orka-js/anthropic";
|
|
@@ -1058,8 +1387,8 @@ var SpecialistAgentManager = class extends EventEmitter2 {
|
|
|
1058
1387
|
createSpecialist(type, customPrompt) {
|
|
1059
1388
|
const agentId = `${type}_${Date.now()}`;
|
|
1060
1389
|
const systemPrompt = customPrompt || SPECIALIST_PROMPTS[type];
|
|
1390
|
+
const llm = this.createLLMAdapter();
|
|
1061
1391
|
const agent = new ReActAgent({
|
|
1062
|
-
llm: this.createLLMAdapter(),
|
|
1063
1392
|
tools: this.browserTools.getTools(),
|
|
1064
1393
|
maxIterations: 15,
|
|
1065
1394
|
systemPrompt: `${systemPrompt}
|
|
@@ -1070,7 +1399,7 @@ IMPORTANT RULES:
|
|
|
1070
1399
|
- Create GitHub issues for critical/high severity bugs
|
|
1071
1400
|
- Be thorough but efficient
|
|
1072
1401
|
- Stop when you've tested the main scenarios for your specialty`
|
|
1073
|
-
});
|
|
1402
|
+
}, llm);
|
|
1074
1403
|
this.agents.set(agentId, agent);
|
|
1075
1404
|
const status = {
|
|
1076
1405
|
id: agentId,
|
|
@@ -1105,7 +1434,7 @@ IMPORTANT RULES:
|
|
|
1105
1434
|
} catch (error) {
|
|
1106
1435
|
status.status = "failed";
|
|
1107
1436
|
status.completedAt = /* @__PURE__ */ new Date();
|
|
1108
|
-
this.emit("agent-failed", { ...status, error: error.message });
|
|
1437
|
+
this.emit("agent-failed", { ...status, error: error instanceof Error ? error.message : String(error) });
|
|
1109
1438
|
}
|
|
1110
1439
|
}
|
|
1111
1440
|
async runAllSpecialists(targetUrl, types) {
|
|
@@ -1151,6 +1480,7 @@ IMPORTANT RULES:
|
|
|
1151
1480
|
};
|
|
1152
1481
|
|
|
1153
1482
|
// agent/skills/index.ts
|
|
1483
|
+
init_esm_shims();
|
|
1154
1484
|
var DEFAULT_SKILLS = [
|
|
1155
1485
|
{
|
|
1156
1486
|
name: "GDPR Compliance Check",
|
|
@@ -1398,9 +1728,7 @@ var OpenQAAgent = class extends EventEmitter3 {
|
|
|
1398
1728
|
this.sessionId = `session_${Date.now()}`;
|
|
1399
1729
|
await this.db.createSession(this.sessionId, {
|
|
1400
1730
|
config: cfg,
|
|
1401
|
-
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1402
|
-
trigger_type: triggerType,
|
|
1403
|
-
trigger_data: triggerData ? JSON.stringify(triggerData) : null
|
|
1731
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1404
1732
|
});
|
|
1405
1733
|
this.browserTools = new BrowserTools(this.db, this.sessionId);
|
|
1406
1734
|
const githubTools = new GitHubTools(this.db, this.sessionId, cfg.github || {});
|
|
@@ -1451,7 +1779,11 @@ ${skillPrompt}
|
|
|
1451
1779
|
|
|
1452
1780
|
Always provide clear, actionable information with steps to reproduce. Think step by step like a human QA expert.`
|
|
1453
1781
|
};
|
|
1454
|
-
this.agent = new ReActAgent2(
|
|
1782
|
+
this.agent = new ReActAgent2({
|
|
1783
|
+
tools: allTools,
|
|
1784
|
+
maxIterations: cfg.agent.maxIterations,
|
|
1785
|
+
systemPrompt: agentConfig.systemPrompt
|
|
1786
|
+
}, llm, memory);
|
|
1455
1787
|
this.specialistManager = new SpecialistAgentManager(
|
|
1456
1788
|
this.db,
|
|
1457
1789
|
this.sessionId,
|
|
@@ -1468,13 +1800,13 @@ Always provide clear, actionable information with steps to reproduce. Think step
|
|
|
1468
1800
|
if (!this.agent) {
|
|
1469
1801
|
await this.initialize();
|
|
1470
1802
|
}
|
|
1471
|
-
const cfg = this.config.getConfig();
|
|
1803
|
+
const cfg = await this.config.getConfig();
|
|
1472
1804
|
console.log(`\u{1F680} Starting test session for ${cfg.saas.url}`);
|
|
1473
1805
|
try {
|
|
1474
1806
|
const result = await this.agent.run(
|
|
1475
1807
|
`Continue testing the application at ${cfg.saas.url}. Review previous findings, create new test scenarios, and report any issues discovered. Focus on areas not yet tested.`
|
|
1476
1808
|
);
|
|
1477
|
-
this.db.updateSession(this.sessionId, {
|
|
1809
|
+
await this.db.updateSession(this.sessionId, {
|
|
1478
1810
|
status: "completed",
|
|
1479
1811
|
ended_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1480
1812
|
});
|
|
@@ -1482,7 +1814,7 @@ Always provide clear, actionable information with steps to reproduce. Think step
|
|
|
1482
1814
|
return result;
|
|
1483
1815
|
} catch (error) {
|
|
1484
1816
|
console.error("\u274C Session error:", error);
|
|
1485
|
-
this.db.updateSession(this.sessionId, {
|
|
1817
|
+
await this.db.updateSession(this.sessionId, {
|
|
1486
1818
|
status: "failed",
|
|
1487
1819
|
ended_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1488
1820
|
});
|
|
@@ -1499,7 +1831,7 @@ Always provide clear, actionable information with steps to reproduce. Think step
|
|
|
1499
1831
|
return;
|
|
1500
1832
|
}
|
|
1501
1833
|
this.isRunning = true;
|
|
1502
|
-
const cfg = this.config.getConfig();
|
|
1834
|
+
const cfg = await this.config.getConfig();
|
|
1503
1835
|
console.log(`\u{1F916} OpenQA Agent starting in autonomous mode`);
|
|
1504
1836
|
console.log(`\u{1F4CD} Target: ${cfg.saas.url}`);
|
|
1505
1837
|
console.log(`\u23F1\uFE0F Interval: ${cfg.agent.intervalMs}ms (${cfg.agent.intervalMs / 1e3 / 60} minutes)`);
|
|
@@ -1540,7 +1872,7 @@ Always provide clear, actionable information with steps to reproduce. Think step
|
|
|
1540
1872
|
}
|
|
1541
1873
|
// Git integration
|
|
1542
1874
|
async startGitListener() {
|
|
1543
|
-
const cfg = this.config.getConfig();
|
|
1875
|
+
const cfg = await this.config.getConfig();
|
|
1544
1876
|
if (cfg.github?.token && cfg.github?.owner && cfg.github?.repo) {
|
|
1545
1877
|
this.gitListener = new GitListener({
|
|
1546
1878
|
provider: "github",
|
|
@@ -1588,14 +1920,14 @@ Always provide clear, actionable information with steps to reproduce. Think step
|
|
|
1588
1920
|
if (!this.specialistManager) {
|
|
1589
1921
|
await this.initialize();
|
|
1590
1922
|
}
|
|
1591
|
-
const cfg = this.config.getConfig();
|
|
1923
|
+
const cfg = await this.config.getConfig();
|
|
1592
1924
|
await this.specialistManager.runSecuritySuite(cfg.saas.url);
|
|
1593
1925
|
}
|
|
1594
1926
|
async runSpecialist(type) {
|
|
1595
1927
|
if (!this.specialistManager) {
|
|
1596
1928
|
await this.initialize();
|
|
1597
1929
|
}
|
|
1598
|
-
const cfg = this.config.getConfig();
|
|
1930
|
+
const cfg = await this.config.getConfig();
|
|
1599
1931
|
const agentId = this.specialistManager.createSpecialist(type);
|
|
1600
1932
|
await this.specialistManager.runSpecialist(agentId, cfg.saas.url);
|
|
1601
1933
|
}
|
|
@@ -1618,11 +1950,11 @@ Always provide clear, actionable information with steps to reproduce. Think step
|
|
|
1618
1950
|
toggleSkill(id) {
|
|
1619
1951
|
return this.skillManager.toggleSkill(id);
|
|
1620
1952
|
}
|
|
1621
|
-
getStatus() {
|
|
1953
|
+
async getStatus() {
|
|
1622
1954
|
return {
|
|
1623
1955
|
isRunning: this.isRunning,
|
|
1624
1956
|
sessionId: this.sessionId,
|
|
1625
|
-
config: this.config.getConfig(),
|
|
1957
|
+
config: await this.config.getConfig(),
|
|
1626
1958
|
gitListenerActive: !!this.gitListener,
|
|
1627
1959
|
specialists: this.getSpecialistStatuses(),
|
|
1628
1960
|
skills: this.skillManager.getEnabledSkills().length
|