@openqa/cli 1.3.4 → 2.1.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 +203 -6
- 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 +369 -105
- 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 +8810 -0
- package/dist/cli/dashboard.html.js +1622 -0
- package/dist/cli/env-config.js +391 -0
- package/dist/cli/env-routes.js +820 -0
- package/dist/cli/env.html.js +679 -0
- package/dist/cli/index.js +5980 -1896
- 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 +5855 -1860
- package/dist/database/index.js +485 -60
- package/dist/database/index.js.map +1 -1
- package/dist/database/sqlite.js +281 -0
- package/dist/database/sqlite.js.map +1 -0
- package/install.sh +19 -10
- package/package.json +19 -5
|
@@ -0,0 +1,588 @@
|
|
|
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
|
+
|
|
24
|
+
// agent/config/index.ts
|
|
25
|
+
init_esm_shims();
|
|
26
|
+
import { config as dotenvConfig } from "dotenv";
|
|
27
|
+
|
|
28
|
+
// database/index.ts
|
|
29
|
+
init_esm_shims();
|
|
30
|
+
init_sqlite();
|
|
31
|
+
import { Low } from "lowdb";
|
|
32
|
+
import { JSONFile } from "lowdb/node";
|
|
33
|
+
import { dirname } from "path";
|
|
34
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
35
|
+
import { mkdirSync } from "fs";
|
|
36
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
37
|
+
var __dirname2 = dirname(__filename2);
|
|
38
|
+
var OpenQADatabase = class {
|
|
39
|
+
constructor(dbPath = "./data/openqa.json") {
|
|
40
|
+
this.dbPath = dbPath;
|
|
41
|
+
this.initialize();
|
|
42
|
+
}
|
|
43
|
+
db = null;
|
|
44
|
+
initialize() {
|
|
45
|
+
const dir = dirname(this.dbPath);
|
|
46
|
+
mkdirSync(dir, { recursive: true });
|
|
47
|
+
const adapter = new JSONFile(this.dbPath);
|
|
48
|
+
this.db = new Low(adapter, {
|
|
49
|
+
config: {},
|
|
50
|
+
test_sessions: [],
|
|
51
|
+
actions: [],
|
|
52
|
+
bugs: [],
|
|
53
|
+
kanban_tickets: [],
|
|
54
|
+
users: []
|
|
55
|
+
});
|
|
56
|
+
this.db.read();
|
|
57
|
+
if (!this.db.data) {
|
|
58
|
+
this.db.data = {
|
|
59
|
+
config: {},
|
|
60
|
+
test_sessions: [],
|
|
61
|
+
actions: [],
|
|
62
|
+
bugs: [],
|
|
63
|
+
kanban_tickets: [],
|
|
64
|
+
users: []
|
|
65
|
+
};
|
|
66
|
+
this.db.write();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async ensureInitialized() {
|
|
70
|
+
if (!this.db) {
|
|
71
|
+
this.initialize();
|
|
72
|
+
}
|
|
73
|
+
await this.db.read();
|
|
74
|
+
let migrated = false;
|
|
75
|
+
if (!this.db.data.users) {
|
|
76
|
+
this.db.data.users = [];
|
|
77
|
+
migrated = true;
|
|
78
|
+
}
|
|
79
|
+
if (migrated) await this.db.write();
|
|
80
|
+
}
|
|
81
|
+
async getConfig(key) {
|
|
82
|
+
await this.ensureInitialized();
|
|
83
|
+
return this.db.data.config[key] || null;
|
|
84
|
+
}
|
|
85
|
+
async setConfig(key, value) {
|
|
86
|
+
await this.ensureInitialized();
|
|
87
|
+
this.db.data.config[key] = value;
|
|
88
|
+
await this.db.write();
|
|
89
|
+
}
|
|
90
|
+
async getAllConfig() {
|
|
91
|
+
await this.ensureInitialized();
|
|
92
|
+
return this.db.data.config;
|
|
93
|
+
}
|
|
94
|
+
async createSession(id, metadata) {
|
|
95
|
+
await this.ensureInitialized();
|
|
96
|
+
const session = {
|
|
97
|
+
id,
|
|
98
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
99
|
+
status: "running",
|
|
100
|
+
total_actions: 0,
|
|
101
|
+
bugs_found: 0,
|
|
102
|
+
metadata: metadata ? JSON.stringify(metadata) : void 0
|
|
103
|
+
};
|
|
104
|
+
this.db.data.test_sessions.push(session);
|
|
105
|
+
await this.db.write();
|
|
106
|
+
return session;
|
|
107
|
+
}
|
|
108
|
+
async getSession(id) {
|
|
109
|
+
await this.ensureInitialized();
|
|
110
|
+
return this.db.data.test_sessions.find((s) => s.id === id) || null;
|
|
111
|
+
}
|
|
112
|
+
async updateSession(id, updates) {
|
|
113
|
+
await this.ensureInitialized();
|
|
114
|
+
const index = this.db.data.test_sessions.findIndex((s) => s.id === id);
|
|
115
|
+
if (index !== -1) {
|
|
116
|
+
this.db.data.test_sessions[index] = { ...this.db.data.test_sessions[index], ...updates };
|
|
117
|
+
await this.db.write();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async getRecentSessions(limit = 10) {
|
|
121
|
+
await this.ensureInitialized();
|
|
122
|
+
return this.db.data.test_sessions.sort((a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime()).slice(0, limit);
|
|
123
|
+
}
|
|
124
|
+
async createAction(action) {
|
|
125
|
+
await this.ensureInitialized();
|
|
126
|
+
const newAction = {
|
|
127
|
+
id: `action_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
128
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
129
|
+
...action
|
|
130
|
+
};
|
|
131
|
+
this.db.data.actions.push(newAction);
|
|
132
|
+
await this.db.write();
|
|
133
|
+
return newAction;
|
|
134
|
+
}
|
|
135
|
+
async getSessionActions(sessionId) {
|
|
136
|
+
await this.ensureInitialized();
|
|
137
|
+
return this.db.data.actions.filter((a) => a.session_id === sessionId).sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
138
|
+
}
|
|
139
|
+
async createBug(bug) {
|
|
140
|
+
await this.ensureInitialized();
|
|
141
|
+
const newBug = {
|
|
142
|
+
id: `bug_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
143
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
144
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
145
|
+
...bug
|
|
146
|
+
};
|
|
147
|
+
this.db.data.bugs.push(newBug);
|
|
148
|
+
await this.db.write();
|
|
149
|
+
return newBug;
|
|
150
|
+
}
|
|
151
|
+
async updateBug(id, updates) {
|
|
152
|
+
await this.ensureInitialized();
|
|
153
|
+
const index = this.db.data.bugs.findIndex((b) => b.id === id);
|
|
154
|
+
if (index !== -1) {
|
|
155
|
+
this.db.data.bugs[index] = {
|
|
156
|
+
...this.db.data.bugs[index],
|
|
157
|
+
...updates,
|
|
158
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
159
|
+
};
|
|
160
|
+
await this.db.write();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async getAllBugs() {
|
|
164
|
+
await this.ensureInitialized();
|
|
165
|
+
return this.db.data.bugs.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
166
|
+
}
|
|
167
|
+
async getBugsByStatus(status) {
|
|
168
|
+
await this.ensureInitialized();
|
|
169
|
+
return this.db.data.bugs.filter((b) => b.status === status).sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
170
|
+
}
|
|
171
|
+
async createKanbanTicket(ticket) {
|
|
172
|
+
await this.ensureInitialized();
|
|
173
|
+
const newTicket = {
|
|
174
|
+
id: `ticket_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
175
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
176
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
177
|
+
...ticket
|
|
178
|
+
};
|
|
179
|
+
this.db.data.kanban_tickets.push(newTicket);
|
|
180
|
+
await this.db.write();
|
|
181
|
+
return newTicket;
|
|
182
|
+
}
|
|
183
|
+
async updateKanbanTicket(id, updates) {
|
|
184
|
+
await this.ensureInitialized();
|
|
185
|
+
const index = this.db.data.kanban_tickets.findIndex((t) => t.id === id);
|
|
186
|
+
if (index !== -1) {
|
|
187
|
+
this.db.data.kanban_tickets[index] = {
|
|
188
|
+
...this.db.data.kanban_tickets[index],
|
|
189
|
+
...updates,
|
|
190
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
191
|
+
};
|
|
192
|
+
await this.db.write();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async getKanbanTickets() {
|
|
196
|
+
await this.ensureInitialized();
|
|
197
|
+
return this.db.data.kanban_tickets.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
198
|
+
}
|
|
199
|
+
async getKanbanTicketsByColumn(column) {
|
|
200
|
+
await this.ensureInitialized();
|
|
201
|
+
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());
|
|
202
|
+
}
|
|
203
|
+
async deleteKanbanTicket(id) {
|
|
204
|
+
await this.ensureInitialized();
|
|
205
|
+
const index = this.db.data.kanban_tickets.findIndex((t) => t.id === id);
|
|
206
|
+
if (index !== -1) {
|
|
207
|
+
this.db.data.kanban_tickets.splice(index, 1);
|
|
208
|
+
await this.db.write();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async clearAllConfig() {
|
|
212
|
+
await this.ensureInitialized();
|
|
213
|
+
this.db.data.config = {};
|
|
214
|
+
await this.db.write();
|
|
215
|
+
}
|
|
216
|
+
// Get real data methods - connected to actual database records
|
|
217
|
+
async getActiveAgents() {
|
|
218
|
+
await this.ensureInitialized();
|
|
219
|
+
const sessions = await this.getRecentSessions(1);
|
|
220
|
+
const currentSession = sessions[0];
|
|
221
|
+
const isRunning = currentSession?.status === "running";
|
|
222
|
+
const totalActions = currentSession?.total_actions || 0;
|
|
223
|
+
const agents = [
|
|
224
|
+
{
|
|
225
|
+
name: "Main Agent",
|
|
226
|
+
status: isRunning ? "running" : "idle",
|
|
227
|
+
purpose: "Autonomous QA orchestration",
|
|
228
|
+
performance: totalActions > 0 ? Math.min(100, Math.round(totalActions / 100 * 100)) : 0,
|
|
229
|
+
tasks: totalActions
|
|
230
|
+
}
|
|
231
|
+
];
|
|
232
|
+
if (currentSession && totalActions > 0) {
|
|
233
|
+
const actions = await this.getSessionActions(currentSession.id);
|
|
234
|
+
const actionTypes = actions.reduce((acc, action) => {
|
|
235
|
+
const type = action.type || "unknown";
|
|
236
|
+
acc[type] = (acc[type] || 0) + 1;
|
|
237
|
+
return acc;
|
|
238
|
+
}, {});
|
|
239
|
+
if (actionTypes["navigate"] || actionTypes["click"] || actionTypes["screenshot"]) {
|
|
240
|
+
agents.push({
|
|
241
|
+
name: "Browser Specialist",
|
|
242
|
+
status: isRunning ? "running" : "idle",
|
|
243
|
+
purpose: "UI navigation and interaction",
|
|
244
|
+
performance: Math.round(((actionTypes["navigate"] || 0) + (actionTypes["click"] || 0)) / totalActions * 100),
|
|
245
|
+
tasks: (actionTypes["navigate"] || 0) + (actionTypes["click"] || 0)
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
if (actionTypes["api_call"] || actionTypes["request"]) {
|
|
249
|
+
agents.push({
|
|
250
|
+
name: "API Tester",
|
|
251
|
+
status: isRunning ? "running" : "idle",
|
|
252
|
+
purpose: "API endpoint testing",
|
|
253
|
+
performance: Math.round((actionTypes["api_call"] || actionTypes["request"] || 0) / totalActions * 100),
|
|
254
|
+
tasks: actionTypes["api_call"] || actionTypes["request"] || 0
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
if (actionTypes["auth"] || actionTypes["login"]) {
|
|
258
|
+
agents.push({
|
|
259
|
+
name: "Auth Specialist",
|
|
260
|
+
status: isRunning ? "running" : "idle",
|
|
261
|
+
purpose: "Authentication testing",
|
|
262
|
+
performance: Math.round((actionTypes["auth"] || actionTypes["login"] || 0) / totalActions * 100),
|
|
263
|
+
tasks: actionTypes["auth"] || actionTypes["login"] || 0
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return agents;
|
|
268
|
+
}
|
|
269
|
+
async getCurrentTasks() {
|
|
270
|
+
await this.ensureInitialized();
|
|
271
|
+
const sessions = await this.getRecentSessions(1);
|
|
272
|
+
const currentSession = sessions[0];
|
|
273
|
+
if (!currentSession) {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
const actions = await this.getSessionActions(currentSession.id);
|
|
277
|
+
const recentActions = actions.slice(-10).reverse();
|
|
278
|
+
return recentActions.map((action, index) => ({
|
|
279
|
+
id: action.id,
|
|
280
|
+
name: action.type || "Unknown Action",
|
|
281
|
+
status: index === 0 && currentSession.status === "running" ? "running" : "completed",
|
|
282
|
+
progress: index === 0 && currentSession.status === "running" ? "65%" : "100%",
|
|
283
|
+
agent: "Main Agent",
|
|
284
|
+
started_at: action.timestamp,
|
|
285
|
+
result: action.output || action.description || "Completed"
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
async getCurrentIssues() {
|
|
289
|
+
await this.ensureInitialized();
|
|
290
|
+
const bugs = await this.getAllBugs();
|
|
291
|
+
return bugs.slice(0, 10).map((bug) => ({
|
|
292
|
+
id: bug.id,
|
|
293
|
+
title: bug.title,
|
|
294
|
+
description: bug.description,
|
|
295
|
+
severity: bug.severity || "medium",
|
|
296
|
+
status: bug.status || "open",
|
|
297
|
+
discovered_at: bug.created_at,
|
|
298
|
+
agent: "Main Agent"
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
async pruneOldSessions(maxAgeDays) {
|
|
302
|
+
await this.ensureInitialized();
|
|
303
|
+
const cutoff = new Date(Date.now() - maxAgeDays * 864e5).toISOString();
|
|
304
|
+
const oldSessions = this.db.data.test_sessions.filter((s) => s.started_at < cutoff);
|
|
305
|
+
const oldSessionIds = new Set(oldSessions.map((s) => s.id));
|
|
306
|
+
const actionsBefore = this.db.data.actions.length;
|
|
307
|
+
this.db.data.actions = this.db.data.actions.filter((a) => !oldSessionIds.has(a.session_id));
|
|
308
|
+
const actionsRemoved = actionsBefore - this.db.data.actions.length;
|
|
309
|
+
this.db.data.test_sessions = this.db.data.test_sessions.filter((s) => s.started_at >= cutoff);
|
|
310
|
+
await this.db.write();
|
|
311
|
+
return { sessionsRemoved: oldSessions.length, actionsRemoved };
|
|
312
|
+
}
|
|
313
|
+
async getStorageStats() {
|
|
314
|
+
await this.ensureInitialized();
|
|
315
|
+
return {
|
|
316
|
+
sessions: this.db.data.test_sessions.length,
|
|
317
|
+
actions: this.db.data.actions.length,
|
|
318
|
+
bugs: this.db.data.bugs.length,
|
|
319
|
+
tickets: this.db.data.kanban_tickets.length
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
// ── User management ──────────────────────────────────────────────────────────
|
|
323
|
+
async countUsers() {
|
|
324
|
+
await this.ensureInitialized();
|
|
325
|
+
return this.db.data.users.length;
|
|
326
|
+
}
|
|
327
|
+
async findUserByUsername(username) {
|
|
328
|
+
await this.ensureInitialized();
|
|
329
|
+
return this.db.data.users.find((u) => u.username === username) ?? null;
|
|
330
|
+
}
|
|
331
|
+
async getUserById(id) {
|
|
332
|
+
await this.ensureInitialized();
|
|
333
|
+
return this.db.data.users.find((u) => u.id === id) ?? null;
|
|
334
|
+
}
|
|
335
|
+
async getAllUsers() {
|
|
336
|
+
await this.ensureInitialized();
|
|
337
|
+
return [...this.db.data.users];
|
|
338
|
+
}
|
|
339
|
+
async createUser(data) {
|
|
340
|
+
await this.ensureInitialized();
|
|
341
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
342
|
+
const user = {
|
|
343
|
+
id: `user_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
|
344
|
+
username: data.username,
|
|
345
|
+
passwordHash: data.passwordHash,
|
|
346
|
+
role: data.role,
|
|
347
|
+
createdAt: now,
|
|
348
|
+
updatedAt: now
|
|
349
|
+
};
|
|
350
|
+
this.db.data.users.push(user);
|
|
351
|
+
await this.db.write();
|
|
352
|
+
return user;
|
|
353
|
+
}
|
|
354
|
+
async updateUser(id, updates) {
|
|
355
|
+
await this.ensureInitialized();
|
|
356
|
+
const idx = this.db.data.users.findIndex((u) => u.id === id);
|
|
357
|
+
if (idx !== -1) {
|
|
358
|
+
this.db.data.users[idx] = {
|
|
359
|
+
...this.db.data.users[idx],
|
|
360
|
+
...updates,
|
|
361
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
362
|
+
};
|
|
363
|
+
await this.db.write();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async deleteUser(id) {
|
|
367
|
+
await this.ensureInitialized();
|
|
368
|
+
const idx = this.db.data.users.findIndex((u) => u.id === id);
|
|
369
|
+
if (idx !== -1) {
|
|
370
|
+
this.db.data.users.splice(idx, 1);
|
|
371
|
+
await this.db.write();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async close() {
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// agent/config/schema.ts
|
|
379
|
+
init_esm_shims();
|
|
380
|
+
import { z } from "zod";
|
|
381
|
+
var llmConfigSchema = z.object({
|
|
382
|
+
provider: z.enum(["openai", "anthropic", "ollama"]).default("openai"),
|
|
383
|
+
apiKey: z.string().optional(),
|
|
384
|
+
model: z.string().optional(),
|
|
385
|
+
baseUrl: z.string().url().optional()
|
|
386
|
+
});
|
|
387
|
+
var saasConfigSchema = z.object({
|
|
388
|
+
url: z.string().default(""),
|
|
389
|
+
authType: z.enum(["none", "basic", "bearer", "session"]).default("none"),
|
|
390
|
+
username: z.string().optional(),
|
|
391
|
+
password: z.string().optional()
|
|
392
|
+
});
|
|
393
|
+
var githubConfigSchema = z.object({
|
|
394
|
+
token: z.string().min(1, "GITHUB_TOKEN is required when GitHub is configured"),
|
|
395
|
+
owner: z.string().default(""),
|
|
396
|
+
repo: z.string().default("")
|
|
397
|
+
});
|
|
398
|
+
var agentConfigSchema = z.object({
|
|
399
|
+
intervalMs: z.number().int().positive().default(36e5),
|
|
400
|
+
maxIterations: z.number().int().positive().default(20),
|
|
401
|
+
autoStart: z.boolean().default(false)
|
|
402
|
+
});
|
|
403
|
+
var webConfigSchema = z.object({
|
|
404
|
+
port: z.number().int().min(1).max(65535).default(4242),
|
|
405
|
+
host: z.string().default("0.0.0.0")
|
|
406
|
+
});
|
|
407
|
+
var databaseConfigSchema = z.object({
|
|
408
|
+
path: z.string().default("./data/openqa.db")
|
|
409
|
+
});
|
|
410
|
+
var notificationsConfigSchema = z.object({
|
|
411
|
+
slack: z.string().url().optional(),
|
|
412
|
+
discord: z.string().url().optional()
|
|
413
|
+
});
|
|
414
|
+
var openQAConfigSchema = z.object({
|
|
415
|
+
llm: llmConfigSchema,
|
|
416
|
+
saas: saasConfigSchema,
|
|
417
|
+
github: githubConfigSchema.optional(),
|
|
418
|
+
agent: agentConfigSchema,
|
|
419
|
+
web: webConfigSchema,
|
|
420
|
+
database: databaseConfigSchema,
|
|
421
|
+
notifications: notificationsConfigSchema.optional()
|
|
422
|
+
});
|
|
423
|
+
var saasAppConfigSchema = z.object({
|
|
424
|
+
name: z.string().min(1, "SaaS application name is required"),
|
|
425
|
+
description: z.string().min(1, "SaaS application description is required"),
|
|
426
|
+
url: z.string().url("SaaS application URL must be a valid URL"),
|
|
427
|
+
repoUrl: z.string().url().optional(),
|
|
428
|
+
localPath: z.string().optional(),
|
|
429
|
+
techStack: z.array(z.string()).optional(),
|
|
430
|
+
authInfo: z.object({
|
|
431
|
+
type: z.enum(["none", "basic", "oauth", "session"]),
|
|
432
|
+
testCredentials: z.object({
|
|
433
|
+
username: z.string(),
|
|
434
|
+
password: z.string()
|
|
435
|
+
}).optional()
|
|
436
|
+
}).optional(),
|
|
437
|
+
directives: z.array(z.string()).optional()
|
|
438
|
+
});
|
|
439
|
+
function validateConfigSafe(config) {
|
|
440
|
+
const result = openQAConfigSchema.safeParse(config);
|
|
441
|
+
if (result.success) {
|
|
442
|
+
return { success: true, data: result.data };
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
success: false,
|
|
446
|
+
errors: result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`)
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// agent/logger.ts
|
|
451
|
+
init_esm_shims();
|
|
452
|
+
var LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
453
|
+
var MIN_LEVEL = process.env.LOG_LEVEL || "info";
|
|
454
|
+
function shouldLog(level) {
|
|
455
|
+
return LEVELS[level] >= LEVELS[MIN_LEVEL];
|
|
456
|
+
}
|
|
457
|
+
function format(level, message, context) {
|
|
458
|
+
const entry = {
|
|
459
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
460
|
+
level,
|
|
461
|
+
msg: message,
|
|
462
|
+
...context
|
|
463
|
+
};
|
|
464
|
+
return JSON.stringify(entry);
|
|
465
|
+
}
|
|
466
|
+
var logger = {
|
|
467
|
+
debug(message, context) {
|
|
468
|
+
if (shouldLog("debug")) process.stdout.write(format("debug", message, context) + "\n");
|
|
469
|
+
},
|
|
470
|
+
info(message, context) {
|
|
471
|
+
if (shouldLog("info")) process.stdout.write(format("info", message, context) + "\n");
|
|
472
|
+
},
|
|
473
|
+
warn(message, context) {
|
|
474
|
+
if (shouldLog("warn")) process.stderr.write(format("warn", message, context) + "\n");
|
|
475
|
+
},
|
|
476
|
+
error(message, context) {
|
|
477
|
+
if (shouldLog("error")) process.stderr.write(format("error", message, context) + "\n");
|
|
478
|
+
},
|
|
479
|
+
child(defaults) {
|
|
480
|
+
return {
|
|
481
|
+
debug: (msg, ctx) => logger.debug(msg, { ...defaults, ...ctx }),
|
|
482
|
+
info: (msg, ctx) => logger.info(msg, { ...defaults, ...ctx }),
|
|
483
|
+
warn: (msg, ctx) => logger.warn(msg, { ...defaults, ...ctx }),
|
|
484
|
+
error: (msg, ctx) => logger.error(msg, { ...defaults, ...ctx })
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
// agent/config/index.ts
|
|
490
|
+
dotenvConfig();
|
|
491
|
+
var ConfigManager = class {
|
|
492
|
+
db = null;
|
|
493
|
+
envConfig;
|
|
494
|
+
constructor(dbPath) {
|
|
495
|
+
this.envConfig = this.loadFromEnv();
|
|
496
|
+
}
|
|
497
|
+
loadFromEnv() {
|
|
498
|
+
const raw = {
|
|
499
|
+
llm: {
|
|
500
|
+
provider: process.env.LLM_PROVIDER || "openai",
|
|
501
|
+
apiKey: process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY,
|
|
502
|
+
model: process.env.LLM_MODEL,
|
|
503
|
+
baseUrl: process.env.OLLAMA_BASE_URL
|
|
504
|
+
},
|
|
505
|
+
saas: {
|
|
506
|
+
url: process.env.SAAS_URL || "",
|
|
507
|
+
authType: process.env.SAAS_AUTH_TYPE || "none",
|
|
508
|
+
username: process.env.SAAS_USERNAME,
|
|
509
|
+
password: process.env.SAAS_PASSWORD
|
|
510
|
+
},
|
|
511
|
+
github: process.env.GITHUB_TOKEN ? {
|
|
512
|
+
token: process.env.GITHUB_TOKEN,
|
|
513
|
+
owner: process.env.GITHUB_OWNER || "",
|
|
514
|
+
repo: process.env.GITHUB_REPO || ""
|
|
515
|
+
} : void 0,
|
|
516
|
+
agent: {
|
|
517
|
+
intervalMs: parseInt(process.env.AGENT_INTERVAL_MS || "3600000"),
|
|
518
|
+
maxIterations: parseInt(process.env.AGENT_MAX_ITERATIONS || "20"),
|
|
519
|
+
autoStart: process.env.AGENT_AUTO_START === "true"
|
|
520
|
+
},
|
|
521
|
+
web: {
|
|
522
|
+
port: parseInt(process.env.WEB_PORT || "4242"),
|
|
523
|
+
host: process.env.WEB_HOST || "0.0.0.0"
|
|
524
|
+
},
|
|
525
|
+
database: {
|
|
526
|
+
path: process.env.DB_PATH || "./data/openqa.db"
|
|
527
|
+
},
|
|
528
|
+
notifications: {
|
|
529
|
+
slack: process.env.SLACK_WEBHOOK_URL,
|
|
530
|
+
discord: process.env.DISCORD_WEBHOOK_URL
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
const result = validateConfigSafe(raw);
|
|
534
|
+
if (!result.success) {
|
|
535
|
+
logger.warn("Config validation warnings", { errors: result.errors });
|
|
536
|
+
return raw;
|
|
537
|
+
}
|
|
538
|
+
return result.data;
|
|
539
|
+
}
|
|
540
|
+
getDB() {
|
|
541
|
+
if (!this.db) {
|
|
542
|
+
this.db = new OpenQADatabase("./data/openqa.json");
|
|
543
|
+
}
|
|
544
|
+
return this.db;
|
|
545
|
+
}
|
|
546
|
+
async get(key) {
|
|
547
|
+
const dbValue = await this.getDB().getConfig(key);
|
|
548
|
+
if (dbValue) return dbValue;
|
|
549
|
+
const keys = key.split(".");
|
|
550
|
+
let value = this.envConfig;
|
|
551
|
+
for (const k of keys) {
|
|
552
|
+
if (value && typeof value === "object") {
|
|
553
|
+
value = value[k];
|
|
554
|
+
} else {
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return value != null ? String(value) : null;
|
|
559
|
+
}
|
|
560
|
+
async set(key, value) {
|
|
561
|
+
await this.getDB().setConfig(key, value);
|
|
562
|
+
}
|
|
563
|
+
async getAll() {
|
|
564
|
+
const dbConfig = await this.getDB().getAllConfig();
|
|
565
|
+
const merged = { ...this.envConfig };
|
|
566
|
+
for (const [key, value] of Object.entries(dbConfig)) {
|
|
567
|
+
const keys = key.split(".");
|
|
568
|
+
let obj = merged;
|
|
569
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
570
|
+
if (!obj[keys[i]] || typeof obj[keys[i]] !== "object") obj[keys[i]] = {};
|
|
571
|
+
obj = obj[keys[i]];
|
|
572
|
+
}
|
|
573
|
+
obj[keys[keys.length - 1]] = value;
|
|
574
|
+
}
|
|
575
|
+
return merged;
|
|
576
|
+
}
|
|
577
|
+
async getConfig() {
|
|
578
|
+
return await this.getAll();
|
|
579
|
+
}
|
|
580
|
+
// Synchronous version that only uses env vars (no DB)
|
|
581
|
+
getConfigSync() {
|
|
582
|
+
return this.envConfig;
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
export {
|
|
586
|
+
ConfigManager
|
|
587
|
+
};
|
|
588
|
+
//# sourceMappingURL=index.js.map
|