@openqa/cli 1.3.4 → 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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/brain/diff-analyzer.js +140 -0
  3. package/dist/agent/brain/diff-analyzer.js.map +1 -0
  4. package/dist/agent/brain/llm-cache.js +47 -0
  5. package/dist/agent/brain/llm-cache.js.map +1 -0
  6. package/dist/agent/brain/llm-resilience.js +252 -0
  7. package/dist/agent/brain/llm-resilience.js.map +1 -0
  8. package/dist/agent/config/index.js +588 -0
  9. package/dist/agent/config/index.js.map +1 -0
  10. package/dist/agent/coverage/index.js +74 -0
  11. package/dist/agent/coverage/index.js.map +1 -0
  12. package/dist/agent/export/index.js +158 -0
  13. package/dist/agent/export/index.js.map +1 -0
  14. package/dist/agent/index-v2.js +2795 -0
  15. package/dist/agent/index-v2.js.map +1 -0
  16. package/dist/agent/index.js +369 -105
  17. package/dist/agent/index.js.map +1 -1
  18. package/dist/agent/logger.js +41 -0
  19. package/dist/agent/logger.js.map +1 -0
  20. package/dist/agent/metrics.js +39 -0
  21. package/dist/agent/metrics.js.map +1 -0
  22. package/dist/agent/notifications/index.js +106 -0
  23. package/dist/agent/notifications/index.js.map +1 -0
  24. package/dist/agent/openapi/spec.js +338 -0
  25. package/dist/agent/openapi/spec.js.map +1 -0
  26. package/dist/agent/tools/project-runner.js +481 -0
  27. package/dist/agent/tools/project-runner.js.map +1 -0
  28. package/dist/cli/config.html.js +454 -0
  29. package/dist/cli/daemon.js +7572 -0
  30. package/dist/cli/dashboard.html.js +1619 -0
  31. package/dist/cli/index.js +3492 -1622
  32. package/dist/cli/kanban.html.js +577 -0
  33. package/dist/cli/routes.js +895 -0
  34. package/dist/cli/routes.js.map +1 -0
  35. package/dist/cli/server.js +3469 -1630
  36. package/dist/database/index.js +485 -60
  37. package/dist/database/index.js.map +1 -1
  38. package/dist/database/sqlite.js +281 -0
  39. package/dist/database/sqlite.js.map +1 -0
  40. package/package.json +18 -5
@@ -1,11 +1,328 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // node_modules/tsup/assets/esm_shims.js
12
+ import path from "path";
13
+ import { fileURLToPath } from "url";
14
+ var init_esm_shims = __esm({
15
+ "node_modules/tsup/assets/esm_shims.js"() {
16
+ "use strict";
17
+ }
18
+ });
19
+
20
+ // database/sqlite.ts
21
+ var sqlite_exports = {};
22
+ __export(sqlite_exports, {
23
+ OpenQASQLiteDatabase: () => OpenQASQLiteDatabase
24
+ });
25
+ import Database from "better-sqlite3";
26
+ import { mkdirSync } from "fs";
27
+ import { dirname } from "path";
28
+ var OpenQASQLiteDatabase;
29
+ var init_sqlite = __esm({
30
+ "database/sqlite.ts"() {
31
+ "use strict";
32
+ init_esm_shims();
33
+ OpenQASQLiteDatabase = class {
34
+ db;
35
+ constructor(dbPath = "./data/openqa.db") {
36
+ mkdirSync(dirname(dbPath), { recursive: true });
37
+ this.db = new Database(dbPath);
38
+ this.db.pragma("journal_mode = WAL");
39
+ this.db.pragma("foreign_keys = ON");
40
+ this.migrate();
41
+ }
42
+ migrate() {
43
+ this.db.exec(`
44
+ CREATE TABLE IF NOT EXISTS config (
45
+ key TEXT PRIMARY KEY,
46
+ value TEXT NOT NULL
47
+ );
48
+
49
+ CREATE TABLE IF NOT EXISTS test_sessions (
50
+ id TEXT PRIMARY KEY,
51
+ started_at TEXT NOT NULL,
52
+ ended_at TEXT,
53
+ status TEXT NOT NULL DEFAULT 'running',
54
+ total_actions INTEGER NOT NULL DEFAULT 0,
55
+ bugs_found INTEGER NOT NULL DEFAULT 0,
56
+ metadata TEXT
57
+ );
58
+
59
+ CREATE TABLE IF NOT EXISTS actions (
60
+ id TEXT PRIMARY KEY,
61
+ session_id TEXT NOT NULL,
62
+ timestamp TEXT NOT NULL,
63
+ type TEXT NOT NULL,
64
+ description TEXT NOT NULL,
65
+ input TEXT,
66
+ output TEXT,
67
+ screenshot_path TEXT,
68
+ FOREIGN KEY (session_id) REFERENCES test_sessions(id)
69
+ );
70
+
71
+ CREATE TABLE IF NOT EXISTS bugs (
72
+ id TEXT PRIMARY KEY,
73
+ session_id TEXT NOT NULL,
74
+ title TEXT NOT NULL,
75
+ description TEXT NOT NULL,
76
+ severity TEXT NOT NULL DEFAULT 'medium',
77
+ status TEXT NOT NULL DEFAULT 'open',
78
+ github_issue_url TEXT,
79
+ screenshot_path TEXT,
80
+ created_at TEXT NOT NULL,
81
+ updated_at TEXT NOT NULL,
82
+ FOREIGN KEY (session_id) REFERENCES test_sessions(id)
83
+ );
84
+
85
+ CREATE TABLE IF NOT EXISTS kanban_tickets (
86
+ id TEXT PRIMARY KEY,
87
+ bug_id TEXT,
88
+ title TEXT NOT NULL,
89
+ description TEXT NOT NULL,
90
+ priority TEXT NOT NULL DEFAULT 'medium',
91
+ column TEXT NOT NULL DEFAULT 'backlog',
92
+ tags TEXT,
93
+ screenshot_url TEXT,
94
+ created_at TEXT NOT NULL,
95
+ updated_at TEXT NOT NULL
96
+ );
97
+ `);
98
+ }
99
+ // ── Config ──
100
+ async getConfig(key) {
101
+ const row = this.db.prepare("SELECT value FROM config WHERE key = ?").get(key);
102
+ return row?.value ?? null;
103
+ }
104
+ async setConfig(key, value) {
105
+ this.db.prepare("INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)").run(key, value);
106
+ }
107
+ async getAllConfig() {
108
+ const rows = this.db.prepare("SELECT key, value FROM config").all();
109
+ return Object.fromEntries(rows.map((r) => [r.key, r.value]));
110
+ }
111
+ async clearAllConfig() {
112
+ this.db.prepare("DELETE FROM config").run();
113
+ }
114
+ // ── Sessions ──
115
+ async createSession(id, metadata) {
116
+ const session = {
117
+ id,
118
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
119
+ status: "running",
120
+ total_actions: 0,
121
+ bugs_found: 0,
122
+ metadata: metadata ? JSON.stringify(metadata) : void 0
123
+ };
124
+ this.db.prepare(`
125
+ INSERT INTO test_sessions (id, started_at, status, total_actions, bugs_found, metadata)
126
+ VALUES (?, ?, ?, ?, ?, ?)
127
+ `).run(session.id, session.started_at, session.status, session.total_actions, session.bugs_found, session.metadata ?? null);
128
+ return session;
129
+ }
130
+ async getSession(id) {
131
+ return this.db.prepare("SELECT * FROM test_sessions WHERE id = ?").get(id) ?? null;
132
+ }
133
+ async updateSession(id, updates) {
134
+ const fields = Object.keys(updates).map((k) => `${k} = ?`).join(", ");
135
+ const values = [...Object.values(updates), id];
136
+ this.db.prepare(`UPDATE test_sessions SET ${fields} WHERE id = ?`).run(...values);
137
+ }
138
+ async getRecentSessions(limit = 10) {
139
+ return this.db.prepare("SELECT * FROM test_sessions ORDER BY started_at DESC LIMIT ?").all(limit);
140
+ }
141
+ // ── Actions ──
142
+ async createAction(action) {
143
+ const newAction = {
144
+ id: `action_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
145
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
146
+ ...action
147
+ };
148
+ this.db.prepare(`
149
+ INSERT INTO actions (id, session_id, timestamp, type, description, input, output, screenshot_path)
150
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
151
+ `).run(
152
+ newAction.id,
153
+ newAction.session_id,
154
+ newAction.timestamp,
155
+ newAction.type,
156
+ newAction.description,
157
+ newAction.input ?? null,
158
+ newAction.output ?? null,
159
+ newAction.screenshot_path ?? null
160
+ );
161
+ return newAction;
162
+ }
163
+ async getSessionActions(sessionId) {
164
+ return this.db.prepare("SELECT * FROM actions WHERE session_id = ? ORDER BY timestamp DESC").all(sessionId);
165
+ }
166
+ // ── Bugs ──
167
+ async createBug(bug) {
168
+ const newBug = {
169
+ id: `bug_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
170
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
171
+ updated_at: (/* @__PURE__ */ new Date()).toISOString(),
172
+ ...bug
173
+ };
174
+ this.db.prepare(`
175
+ INSERT INTO bugs (id, session_id, title, description, severity, status, github_issue_url, screenshot_path, created_at, updated_at)
176
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
177
+ `).run(
178
+ newBug.id,
179
+ newBug.session_id,
180
+ newBug.title,
181
+ newBug.description,
182
+ newBug.severity,
183
+ newBug.status,
184
+ newBug.github_issue_url ?? null,
185
+ newBug.screenshot_path ?? null,
186
+ newBug.created_at,
187
+ newBug.updated_at
188
+ );
189
+ return newBug;
190
+ }
191
+ async updateBug(id, updates) {
192
+ const patch = { ...updates, updated_at: (/* @__PURE__ */ new Date()).toISOString() };
193
+ const fields = Object.keys(patch).map((k) => `${k} = ?`).join(", ");
194
+ const values = [...Object.values(patch), id];
195
+ this.db.prepare(`UPDATE bugs SET ${fields} WHERE id = ?`).run(...values);
196
+ }
197
+ async getAllBugs() {
198
+ return this.db.prepare("SELECT * FROM bugs ORDER BY created_at DESC").all();
199
+ }
200
+ async getBugsByStatus(status) {
201
+ return this.db.prepare("SELECT * FROM bugs WHERE status = ? ORDER BY created_at DESC").all(status);
202
+ }
203
+ // ── Kanban ──
204
+ async createKanbanTicket(ticket) {
205
+ const newTicket = {
206
+ id: `ticket_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
207
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
208
+ updated_at: (/* @__PURE__ */ new Date()).toISOString(),
209
+ ...ticket
210
+ };
211
+ this.db.prepare(`
212
+ INSERT INTO kanban_tickets (id, bug_id, title, description, priority, column, tags, screenshot_url, created_at, updated_at)
213
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
214
+ `).run(
215
+ newTicket.id,
216
+ newTicket.bug_id ?? null,
217
+ newTicket.title,
218
+ newTicket.description,
219
+ newTicket.priority,
220
+ newTicket.column,
221
+ newTicket.tags ?? null,
222
+ newTicket.screenshot_url ?? null,
223
+ newTicket.created_at,
224
+ newTicket.updated_at
225
+ );
226
+ return newTicket;
227
+ }
228
+ async updateKanbanTicket(id, updates) {
229
+ const patch = { ...updates, updated_at: (/* @__PURE__ */ new Date()).toISOString() };
230
+ const fields = Object.keys(patch).map((k) => `"${k}" = ?`).join(", ");
231
+ const values = [...Object.values(patch), id];
232
+ this.db.prepare(`UPDATE kanban_tickets SET ${fields} WHERE id = ?`).run(...values);
233
+ }
234
+ async getKanbanTickets() {
235
+ return this.db.prepare("SELECT * FROM kanban_tickets ORDER BY created_at DESC").all();
236
+ }
237
+ async getKanbanTicketsByColumn(column) {
238
+ return this.db.prepare('SELECT * FROM kanban_tickets WHERE "column" = ? ORDER BY created_at DESC').all(column);
239
+ }
240
+ async deleteKanbanTicket(id) {
241
+ this.db.prepare("DELETE FROM kanban_tickets WHERE id = ?").run(id);
242
+ }
243
+ // ── Storage / Cleanup ──
244
+ async pruneOldSessions(maxAgeDays) {
245
+ const cutoff = new Date(Date.now() - maxAgeDays * 864e5).toISOString();
246
+ const oldSessions = this.db.prepare("SELECT id FROM test_sessions WHERE started_at < ?").all(cutoff);
247
+ const ids = oldSessions.map((s) => s.id);
248
+ if (ids.length === 0) return { sessionsRemoved: 0, actionsRemoved: 0 };
249
+ const placeholders = ids.map(() => "?").join(", ");
250
+ const actionsResult = this.db.prepare(`DELETE FROM actions WHERE session_id IN (${placeholders})`).run(...ids);
251
+ this.db.prepare(`DELETE FROM test_sessions WHERE id IN (${placeholders})`).run(...ids);
252
+ return { sessionsRemoved: ids.length, actionsRemoved: actionsResult.changes };
253
+ }
254
+ async getStorageStats() {
255
+ const count = (table) => this.db.prepare(`SELECT COUNT(*) as n FROM ${table}`).get().n;
256
+ return {
257
+ sessions: count("test_sessions"),
258
+ actions: count("actions"),
259
+ bugs: count("bugs"),
260
+ tickets: count("kanban_tickets")
261
+ };
262
+ }
263
+ // ── Compat helpers (used by daemon.ts) ──
264
+ async getActiveAgents() {
265
+ const sessions = await this.getRecentSessions(1);
266
+ const current = sessions[0];
267
+ const isRunning = current?.status === "running";
268
+ return [{
269
+ name: "Main Agent",
270
+ status: isRunning ? "running" : "idle",
271
+ purpose: "Autonomous QA orchestration",
272
+ performance: current ? Math.min(100, Math.round(current.total_actions / 100 * 100)) : 0,
273
+ tasks: current?.total_actions ?? 0
274
+ }];
275
+ }
276
+ async getCurrentTasks() {
277
+ const sessions = await this.getRecentSessions(1);
278
+ if (!sessions[0]) return [];
279
+ const actions = await this.getSessionActions(sessions[0].id);
280
+ return actions.slice(0, 10).map((a, i) => ({
281
+ id: a.id,
282
+ name: a.type,
283
+ status: i === 0 && sessions[0].status === "running" ? "running" : "completed",
284
+ progress: i === 0 && sessions[0].status === "running" ? "65%" : "100%",
285
+ agent: "Main Agent",
286
+ started_at: a.timestamp,
287
+ result: a.output || a.description
288
+ }));
289
+ }
290
+ async getCurrentIssues() {
291
+ const bugs = await this.getAllBugs();
292
+ return bugs.slice(0, 10).map((b) => ({
293
+ id: b.id,
294
+ title: b.title,
295
+ description: b.description,
296
+ severity: b.severity,
297
+ status: b.status,
298
+ discovered_at: b.created_at,
299
+ agent: "Main Agent"
300
+ }));
301
+ }
302
+ async close() {
303
+ this.db.close();
304
+ }
305
+ };
306
+ }
307
+ });
308
+
1
309
  // database/index.ts
310
+ init_esm_shims();
311
+ init_sqlite();
2
312
  import { Low } from "lowdb";
3
313
  import { JSONFile } from "lowdb/node";
4
- import { dirname } from "path";
5
- import { fileURLToPath } from "url";
6
- import { mkdirSync } from "fs";
7
- var __filename = fileURLToPath(import.meta.url);
8
- var __dirname = dirname(__filename);
314
+ import { dirname as dirname2 } from "path";
315
+ import { fileURLToPath as fileURLToPath2 } from "url";
316
+ import { mkdirSync as mkdirSync2 } from "fs";
317
+ async function createDatabase(path2) {
318
+ if (path2.endsWith(".db")) {
319
+ const { OpenQASQLiteDatabase: OpenQASQLiteDatabase2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
320
+ return new OpenQASQLiteDatabase2(path2);
321
+ }
322
+ return new OpenQADatabase(path2);
323
+ }
324
+ var __filename2 = fileURLToPath2(import.meta.url);
325
+ var __dirname2 = dirname2(__filename2);
9
326
  var OpenQADatabase = class {
10
327
  constructor(dbPath = "./data/openqa.json") {
11
328
  this.dbPath = dbPath;
@@ -13,15 +330,16 @@ var OpenQADatabase = class {
13
330
  }
14
331
  db = null;
15
332
  initialize() {
16
- const dir = dirname(this.dbPath);
17
- mkdirSync(dir, { recursive: true });
333
+ const dir = dirname2(this.dbPath);
334
+ mkdirSync2(dir, { recursive: true });
18
335
  const adapter = new JSONFile(this.dbPath);
19
336
  this.db = new Low(adapter, {
20
337
  config: {},
21
338
  test_sessions: [],
22
339
  actions: [],
23
340
  bugs: [],
24
- kanban_tickets: []
341
+ kanban_tickets: [],
342
+ users: []
25
343
  });
26
344
  this.db.read();
27
345
  if (!this.db.data) {
@@ -30,7 +348,8 @@ var OpenQADatabase = class {
30
348
  test_sessions: [],
31
349
  actions: [],
32
350
  bugs: [],
33
- kanban_tickets: []
351
+ kanban_tickets: [],
352
+ users: []
34
353
  };
35
354
  this.db.write();
36
355
  }
@@ -40,6 +359,12 @@ var OpenQADatabase = class {
40
359
  this.initialize();
41
360
  }
42
361
  await this.db.read();
362
+ let migrated = false;
363
+ if (!this.db.data.users) {
364
+ this.db.data.users = [];
365
+ migrated = true;
366
+ }
367
+ if (migrated) await this.db.write();
43
368
  }
44
369
  async getConfig(key) {
45
370
  await this.ensureInitialized();
@@ -163,83 +488,183 @@ var OpenQADatabase = class {
163
488
  await this.ensureInitialized();
164
489
  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());
165
490
  }
491
+ async deleteKanbanTicket(id) {
492
+ await this.ensureInitialized();
493
+ const index = this.db.data.kanban_tickets.findIndex((t) => t.id === id);
494
+ if (index !== -1) {
495
+ this.db.data.kanban_tickets.splice(index, 1);
496
+ await this.db.write();
497
+ }
498
+ }
166
499
  async clearAllConfig() {
167
500
  await this.ensureInitialized();
168
501
  this.db.data.config = {};
169
502
  await this.db.write();
170
503
  }
171
- // Get real data methods
504
+ // Get real data methods - connected to actual database records
172
505
  async getActiveAgents() {
173
506
  await this.ensureInitialized();
174
507
  const sessions = await this.getRecentSessions(1);
175
508
  const currentSession = sessions[0];
176
- if (!currentSession) {
177
- return [{ name: "Main Agent", status: "idle", purpose: "Autonomous testing", performance: 0, tasks: 0 }];
178
- }
179
- return [
180
- { name: "Main Agent", status: "running", purpose: "Autonomous testing", performance: 85, tasks: currentSession.total_actions || 0 },
181
- { name: "Browser Specialist", status: "running", purpose: "UI testing", performance: 92, tasks: Math.floor((currentSession.total_actions || 0) * 0.3) },
182
- { name: "API Tester", status: "running", purpose: "API testing", performance: 78, tasks: Math.floor((currentSession.total_actions || 0) * 0.2) },
183
- { name: "Auth Specialist", status: "idle", purpose: "Authentication testing", performance: 95, tasks: Math.floor((currentSession.total_actions || 0) * 0.1) },
184
- { name: "UI Tester", status: "running", purpose: "User interface testing", performance: 88, tasks: Math.floor((currentSession.total_actions || 0) * 0.25) },
185
- { name: "Security Scanner", status: "idle", purpose: "Security testing", performance: 91, tasks: Math.floor((currentSession.total_actions || 0) * 0.15) }
509
+ const isRunning = currentSession?.status === "running";
510
+ const totalActions = currentSession?.total_actions || 0;
511
+ const agents = [
512
+ {
513
+ name: "Main Agent",
514
+ status: isRunning ? "running" : "idle",
515
+ purpose: "Autonomous QA orchestration",
516
+ performance: totalActions > 0 ? Math.min(100, Math.round(totalActions / 100 * 100)) : 0,
517
+ tasks: totalActions
518
+ }
186
519
  ];
520
+ if (currentSession && totalActions > 0) {
521
+ const actions = await this.getSessionActions(currentSession.id);
522
+ const actionTypes = actions.reduce((acc, action) => {
523
+ const type = action.type || "unknown";
524
+ acc[type] = (acc[type] || 0) + 1;
525
+ return acc;
526
+ }, {});
527
+ if (actionTypes["navigate"] || actionTypes["click"] || actionTypes["screenshot"]) {
528
+ agents.push({
529
+ name: "Browser Specialist",
530
+ status: isRunning ? "running" : "idle",
531
+ purpose: "UI navigation and interaction",
532
+ performance: Math.round(((actionTypes["navigate"] || 0) + (actionTypes["click"] || 0)) / totalActions * 100),
533
+ tasks: (actionTypes["navigate"] || 0) + (actionTypes["click"] || 0)
534
+ });
535
+ }
536
+ if (actionTypes["api_call"] || actionTypes["request"]) {
537
+ agents.push({
538
+ name: "API Tester",
539
+ status: isRunning ? "running" : "idle",
540
+ purpose: "API endpoint testing",
541
+ performance: Math.round((actionTypes["api_call"] || actionTypes["request"] || 0) / totalActions * 100),
542
+ tasks: actionTypes["api_call"] || actionTypes["request"] || 0
543
+ });
544
+ }
545
+ if (actionTypes["auth"] || actionTypes["login"]) {
546
+ agents.push({
547
+ name: "Auth Specialist",
548
+ status: isRunning ? "running" : "idle",
549
+ purpose: "Authentication testing",
550
+ performance: Math.round((actionTypes["auth"] || actionTypes["login"] || 0) / totalActions * 100),
551
+ tasks: actionTypes["auth"] || actionTypes["login"] || 0
552
+ });
553
+ }
554
+ }
555
+ return agents;
187
556
  }
188
557
  async getCurrentTasks() {
189
558
  await this.ensureInitialized();
190
559
  const sessions = await this.getRecentSessions(1);
191
560
  const currentSession = sessions[0];
192
- if (!currentSession || currentSession.status === "completed") {
561
+ if (!currentSession) {
193
562
  return [];
194
563
  }
195
- const tasks = [];
196
- const taskTypes = ["Scan Application", "Test Authentication", "Generate Tests", "Analyze Results", "Create Reports"];
197
- for (let i = 0; i < Math.min(5, Math.floor((currentSession.total_actions || 0) / 10)); i++) {
198
- const taskType = taskTypes[i % taskTypes.length];
199
- const status = i === 0 ? "running" : i === 1 ? "pending" : "completed";
200
- const progress = status === "completed" ? "100%" : status === "running" ? "65%" : "0%";
201
- tasks.push({
202
- id: `task_${i + 1}`,
203
- name: taskType,
204
- status,
205
- progress,
206
- agent: ["Main Agent", "Browser Specialist", "API Tester", "UI Tester"][i % 4],
207
- started_at: new Date(Date.now() - i * 10 * 60 * 1e3).toISOString(),
208
- result: status === "completed" ? "Successfully completed task execution" : null
209
- });
210
- }
211
- return tasks;
564
+ const actions = await this.getSessionActions(currentSession.id);
565
+ const recentActions = actions.slice(-10).reverse();
566
+ return recentActions.map((action, index) => ({
567
+ id: action.id,
568
+ name: action.type || "Unknown Action",
569
+ status: index === 0 && currentSession.status === "running" ? "running" : "completed",
570
+ progress: index === 0 && currentSession.status === "running" ? "65%" : "100%",
571
+ agent: "Main Agent",
572
+ started_at: action.timestamp,
573
+ result: action.output || action.description || "Completed"
574
+ }));
212
575
  }
213
576
  async getCurrentIssues() {
214
577
  await this.ensureInitialized();
215
- const sessions = await this.getRecentSessions(1);
216
- const currentSession = sessions[0];
217
- if (!currentSession) {
218
- return [];
578
+ const bugs = await this.getAllBugs();
579
+ return bugs.slice(0, 10).map((bug) => ({
580
+ id: bug.id,
581
+ title: bug.title,
582
+ description: bug.description,
583
+ severity: bug.severity || "medium",
584
+ status: bug.status || "open",
585
+ discovered_at: bug.created_at,
586
+ agent: "Main Agent"
587
+ }));
588
+ }
589
+ async pruneOldSessions(maxAgeDays) {
590
+ await this.ensureInitialized();
591
+ const cutoff = new Date(Date.now() - maxAgeDays * 864e5).toISOString();
592
+ const oldSessions = this.db.data.test_sessions.filter((s) => s.started_at < cutoff);
593
+ const oldSessionIds = new Set(oldSessions.map((s) => s.id));
594
+ const actionsBefore = this.db.data.actions.length;
595
+ this.db.data.actions = this.db.data.actions.filter((a) => !oldSessionIds.has(a.session_id));
596
+ const actionsRemoved = actionsBefore - this.db.data.actions.length;
597
+ this.db.data.test_sessions = this.db.data.test_sessions.filter((s) => s.started_at >= cutoff);
598
+ await this.db.write();
599
+ return { sessionsRemoved: oldSessions.length, actionsRemoved };
600
+ }
601
+ async getStorageStats() {
602
+ await this.ensureInitialized();
603
+ return {
604
+ sessions: this.db.data.test_sessions.length,
605
+ actions: this.db.data.actions.length,
606
+ bugs: this.db.data.bugs.length,
607
+ tickets: this.db.data.kanban_tickets.length
608
+ };
609
+ }
610
+ // ── User management ──────────────────────────────────────────────────────────
611
+ async countUsers() {
612
+ await this.ensureInitialized();
613
+ return this.db.data.users.length;
614
+ }
615
+ async findUserByUsername(username) {
616
+ await this.ensureInitialized();
617
+ return this.db.data.users.find((u) => u.username === username) ?? null;
618
+ }
619
+ async getUserById(id) {
620
+ await this.ensureInitialized();
621
+ return this.db.data.users.find((u) => u.id === id) ?? null;
622
+ }
623
+ async getAllUsers() {
624
+ await this.ensureInitialized();
625
+ return [...this.db.data.users];
626
+ }
627
+ async createUser(data) {
628
+ await this.ensureInitialized();
629
+ const now = (/* @__PURE__ */ new Date()).toISOString();
630
+ const user = {
631
+ id: `user_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
632
+ username: data.username,
633
+ passwordHash: data.passwordHash,
634
+ role: data.role,
635
+ createdAt: now,
636
+ updatedAt: now
637
+ };
638
+ this.db.data.users.push(user);
639
+ await this.db.write();
640
+ return user;
641
+ }
642
+ async updateUser(id, updates) {
643
+ await this.ensureInitialized();
644
+ const idx = this.db.data.users.findIndex((u) => u.id === id);
645
+ if (idx !== -1) {
646
+ this.db.data.users[idx] = {
647
+ ...this.db.data.users[idx],
648
+ ...updates,
649
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
650
+ };
651
+ await this.db.write();
219
652
  }
220
- const issues = [];
221
- const bugCount = currentSession.bugs_found || 0;
222
- if (bugCount > 0) {
223
- const issueTypes = ["Critical Security Issue", "Performance Bottleneck", "UI Bug", "API Error", "Authentication Flaw"];
224
- const severities = ["critical", "high", "medium", "low"];
225
- for (let i = 0; i < Math.min(bugCount, 5); i++) {
226
- issues.push({
227
- id: `issue_${i + 1}`,
228
- title: issueTypes[i % issueTypes.length],
229
- description: `Issue detected during automated testing session`,
230
- severity: severities[i % severities.length],
231
- status: "open",
232
- discovered_at: new Date(Date.now() - i * 30 * 60 * 1e3).toISOString(),
233
- agent: ["Main Agent", "Browser Specialist", "API Tester"][i % 3]
234
- });
235
- }
653
+ }
654
+ async deleteUser(id) {
655
+ await this.ensureInitialized();
656
+ const idx = this.db.data.users.findIndex((u) => u.id === id);
657
+ if (idx !== -1) {
658
+ this.db.data.users.splice(idx, 1);
659
+ await this.db.write();
236
660
  }
237
- return issues;
238
661
  }
239
662
  async close() {
240
663
  }
241
664
  };
242
665
  export {
243
- OpenQADatabase
666
+ OpenQADatabase,
667
+ OpenQASQLiteDatabase,
668
+ createDatabase
244
669
  };
245
670
  //# sourceMappingURL=index.js.map