@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.
Files changed (44) hide show
  1. package/README.md +203 -6
  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 +8810 -0
  30. package/dist/cli/dashboard.html.js +1622 -0
  31. package/dist/cli/env-config.js +391 -0
  32. package/dist/cli/env-routes.js +820 -0
  33. package/dist/cli/env.html.js +679 -0
  34. package/dist/cli/index.js +5980 -1896
  35. package/dist/cli/kanban.html.js +577 -0
  36. package/dist/cli/routes.js +895 -0
  37. package/dist/cli/routes.js.map +1 -0
  38. package/dist/cli/server.js +5855 -1860
  39. package/dist/database/index.js +485 -60
  40. package/dist/database/index.js.map +1 -1
  41. package/dist/database/sqlite.js +281 -0
  42. package/dist/database/sqlite.js.map +1 -0
  43. package/install.sh +19 -10
  44. package/package.json +19 -5
@@ -0,0 +1,391 @@
1
+ // cli/env-config.ts
2
+ var ENV_VARIABLES = [
3
+ // ============================================================================
4
+ // LLM CONFIGURATION
5
+ // ============================================================================
6
+ {
7
+ key: "LLM_PROVIDER",
8
+ type: "select",
9
+ category: "llm",
10
+ required: true,
11
+ description: "LLM provider to use for AI operations",
12
+ options: ["openai", "anthropic", "ollama"],
13
+ placeholder: "openai",
14
+ restartRequired: true
15
+ },
16
+ {
17
+ key: "OPENAI_API_KEY",
18
+ type: "password",
19
+ category: "llm",
20
+ required: false,
21
+ description: "OpenAI API key (required if LLM_PROVIDER=openai)",
22
+ placeholder: "sk-...",
23
+ sensitive: true,
24
+ testable: true,
25
+ validation: (value) => {
26
+ if (!value) return { valid: true };
27
+ if (!value.startsWith("sk-")) {
28
+ return { valid: false, error: 'OpenAI API key must start with "sk-"' };
29
+ }
30
+ if (value.length < 20) {
31
+ return { valid: false, error: "API key seems too short" };
32
+ }
33
+ return { valid: true };
34
+ },
35
+ restartRequired: true
36
+ },
37
+ {
38
+ key: "ANTHROPIC_API_KEY",
39
+ type: "password",
40
+ category: "llm",
41
+ required: false,
42
+ description: "Anthropic API key (required if LLM_PROVIDER=anthropic)",
43
+ placeholder: "sk-ant-...",
44
+ sensitive: true,
45
+ testable: true,
46
+ validation: (value) => {
47
+ if (!value) return { valid: true };
48
+ if (!value.startsWith("sk-ant-")) {
49
+ return { valid: false, error: 'Anthropic API key must start with "sk-ant-"' };
50
+ }
51
+ return { valid: true };
52
+ },
53
+ restartRequired: true
54
+ },
55
+ {
56
+ key: "OLLAMA_BASE_URL",
57
+ type: "url",
58
+ category: "llm",
59
+ required: false,
60
+ description: "Ollama server URL (required if LLM_PROVIDER=ollama)",
61
+ placeholder: "http://localhost:11434",
62
+ testable: true,
63
+ validation: (value) => {
64
+ if (!value) return { valid: true };
65
+ try {
66
+ new URL(value);
67
+ return { valid: true };
68
+ } catch {
69
+ return { valid: false, error: "Invalid URL format" };
70
+ }
71
+ },
72
+ restartRequired: true
73
+ },
74
+ {
75
+ key: "LLM_MODEL",
76
+ type: "text",
77
+ category: "llm",
78
+ required: false,
79
+ description: "LLM model to use (e.g., gpt-4, claude-3-opus, llama2)",
80
+ placeholder: "gpt-4",
81
+ restartRequired: true
82
+ },
83
+ // ============================================================================
84
+ // SECURITY
85
+ // ============================================================================
86
+ {
87
+ key: "OPENQA_JWT_SECRET",
88
+ type: "password",
89
+ category: "security",
90
+ required: true,
91
+ description: "Secret key for JWT token signing (min 32 characters)",
92
+ placeholder: "Generate with: openssl rand -hex 32",
93
+ sensitive: true,
94
+ validation: (value) => {
95
+ if (!value) return { valid: false, error: "JWT secret is required" };
96
+ if (value.length < 32) {
97
+ return { valid: false, error: "JWT secret must be at least 32 characters" };
98
+ }
99
+ return { valid: true };
100
+ },
101
+ restartRequired: true
102
+ },
103
+ {
104
+ key: "OPENQA_AUTH_DISABLED",
105
+ type: "boolean",
106
+ category: "security",
107
+ required: false,
108
+ description: "\u26A0\uFE0F DANGER: Disable authentication (NEVER use in production!)",
109
+ placeholder: "false",
110
+ validation: (value) => {
111
+ if (value === "true" && process.env.NODE_ENV === "production") {
112
+ return { valid: false, error: "Cannot disable auth in production!" };
113
+ }
114
+ return { valid: true };
115
+ },
116
+ restartRequired: true
117
+ },
118
+ {
119
+ key: "NODE_ENV",
120
+ type: "select",
121
+ category: "security",
122
+ required: false,
123
+ description: "Node environment (production enables security features)",
124
+ options: ["development", "production", "test"],
125
+ placeholder: "production",
126
+ restartRequired: true
127
+ },
128
+ // ============================================================================
129
+ // TARGET APPLICATION
130
+ // ============================================================================
131
+ {
132
+ key: "SAAS_URL",
133
+ type: "url",
134
+ category: "target",
135
+ required: true,
136
+ description: "URL of the application to test",
137
+ placeholder: "https://your-app.com",
138
+ testable: true,
139
+ validation: (value) => {
140
+ if (!value) return { valid: false, error: "Target URL is required" };
141
+ try {
142
+ const url = new URL(value);
143
+ if (!["http:", "https:"].includes(url.protocol)) {
144
+ return { valid: false, error: "URL must use http or https protocol" };
145
+ }
146
+ return { valid: true };
147
+ } catch {
148
+ return { valid: false, error: "Invalid URL format" };
149
+ }
150
+ }
151
+ },
152
+ {
153
+ key: "SAAS_AUTH_TYPE",
154
+ type: "select",
155
+ category: "target",
156
+ required: false,
157
+ description: "Authentication type for target application",
158
+ options: ["none", "basic", "session"],
159
+ placeholder: "none"
160
+ },
161
+ {
162
+ key: "SAAS_USERNAME",
163
+ type: "text",
164
+ category: "target",
165
+ required: false,
166
+ description: "Username for target application authentication",
167
+ placeholder: "test@example.com"
168
+ },
169
+ {
170
+ key: "SAAS_PASSWORD",
171
+ type: "password",
172
+ category: "target",
173
+ required: false,
174
+ description: "Password for target application authentication",
175
+ placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
176
+ sensitive: true
177
+ },
178
+ // ============================================================================
179
+ // GITHUB INTEGRATION
180
+ // ============================================================================
181
+ {
182
+ key: "GITHUB_TOKEN",
183
+ type: "password",
184
+ category: "github",
185
+ required: false,
186
+ description: "GitHub personal access token for issue creation",
187
+ placeholder: "ghp_...",
188
+ sensitive: true,
189
+ testable: true,
190
+ validation: (value) => {
191
+ if (!value) return { valid: true };
192
+ if (!value.startsWith("ghp_") && !value.startsWith("github_pat_")) {
193
+ return { valid: false, error: 'GitHub token must start with "ghp_" or "github_pat_"' };
194
+ }
195
+ return { valid: true };
196
+ }
197
+ },
198
+ {
199
+ key: "GITHUB_OWNER",
200
+ type: "text",
201
+ category: "github",
202
+ required: false,
203
+ description: "GitHub repository owner/organization",
204
+ placeholder: "your-username"
205
+ },
206
+ {
207
+ key: "GITHUB_REPO",
208
+ type: "text",
209
+ category: "github",
210
+ required: false,
211
+ description: "GitHub repository name",
212
+ placeholder: "your-repo"
213
+ },
214
+ {
215
+ key: "GITHUB_BRANCH",
216
+ type: "text",
217
+ category: "github",
218
+ required: false,
219
+ description: "GitHub branch to monitor",
220
+ placeholder: "main"
221
+ },
222
+ // ============================================================================
223
+ // WEB SERVER
224
+ // ============================================================================
225
+ {
226
+ key: "WEB_PORT",
227
+ type: "number",
228
+ category: "web",
229
+ required: false,
230
+ description: "Port for web server",
231
+ placeholder: "4242",
232
+ validation: (value) => {
233
+ if (!value) return { valid: true };
234
+ const port = parseInt(value, 10);
235
+ if (isNaN(port) || port < 1 || port > 65535) {
236
+ return { valid: false, error: "Port must be between 1 and 65535" };
237
+ }
238
+ return { valid: true };
239
+ },
240
+ restartRequired: true
241
+ },
242
+ {
243
+ key: "WEB_HOST",
244
+ type: "text",
245
+ category: "web",
246
+ required: false,
247
+ description: "Host to bind web server (0.0.0.0 for all interfaces)",
248
+ placeholder: "0.0.0.0",
249
+ restartRequired: true
250
+ },
251
+ {
252
+ key: "CORS_ORIGINS",
253
+ type: "text",
254
+ category: "web",
255
+ required: false,
256
+ description: "Allowed CORS origins (comma-separated)",
257
+ placeholder: "https://your-domain.com,https://app.example.com",
258
+ restartRequired: true
259
+ },
260
+ // ============================================================================
261
+ // AGENT CONFIGURATION
262
+ // ============================================================================
263
+ {
264
+ key: "AGENT_AUTO_START",
265
+ type: "boolean",
266
+ category: "agent",
267
+ required: false,
268
+ description: "Auto-start agent on server launch",
269
+ placeholder: "false"
270
+ },
271
+ {
272
+ key: "AGENT_INTERVAL_MS",
273
+ type: "number",
274
+ category: "agent",
275
+ required: false,
276
+ description: "Agent run interval in milliseconds (1 hour = 3600000)",
277
+ placeholder: "3600000",
278
+ validation: (value) => {
279
+ if (!value) return { valid: true };
280
+ const interval = parseInt(value, 10);
281
+ if (isNaN(interval) || interval < 6e4) {
282
+ return { valid: false, error: "Interval must be at least 60000ms (1 minute)" };
283
+ }
284
+ return { valid: true };
285
+ }
286
+ },
287
+ {
288
+ key: "AGENT_MAX_ITERATIONS",
289
+ type: "number",
290
+ category: "agent",
291
+ required: false,
292
+ description: "Maximum iterations per agent session",
293
+ placeholder: "20",
294
+ validation: (value) => {
295
+ if (!value) return { valid: true };
296
+ const max = parseInt(value, 10);
297
+ if (isNaN(max) || max < 1 || max > 1e3) {
298
+ return { valid: false, error: "Max iterations must be between 1 and 1000" };
299
+ }
300
+ return { valid: true };
301
+ }
302
+ },
303
+ {
304
+ key: "GIT_LISTENER_ENABLED",
305
+ type: "boolean",
306
+ category: "agent",
307
+ required: false,
308
+ description: "Enable git merge/pipeline detection",
309
+ placeholder: "true"
310
+ },
311
+ {
312
+ key: "GIT_POLL_INTERVAL_MS",
313
+ type: "number",
314
+ category: "agent",
315
+ required: false,
316
+ description: "Git polling interval in milliseconds",
317
+ placeholder: "60000"
318
+ },
319
+ // ============================================================================
320
+ // DATABASE
321
+ // ============================================================================
322
+ {
323
+ key: "DB_PATH",
324
+ type: "text",
325
+ category: "database",
326
+ required: false,
327
+ description: "Path to SQLite database file",
328
+ placeholder: "./data/openqa.db",
329
+ restartRequired: true
330
+ },
331
+ // ============================================================================
332
+ // NOTIFICATIONS
333
+ // ============================================================================
334
+ {
335
+ key: "SLACK_WEBHOOK_URL",
336
+ type: "url",
337
+ category: "notifications",
338
+ required: false,
339
+ description: "Slack webhook URL for notifications",
340
+ placeholder: "https://hooks.slack.com/services/...",
341
+ sensitive: true,
342
+ testable: true,
343
+ validation: (value) => {
344
+ if (!value) return { valid: true };
345
+ if (!value.startsWith("https://hooks.slack.com/")) {
346
+ return { valid: false, error: "Invalid Slack webhook URL" };
347
+ }
348
+ return { valid: true };
349
+ }
350
+ },
351
+ {
352
+ key: "DISCORD_WEBHOOK_URL",
353
+ type: "url",
354
+ category: "notifications",
355
+ required: false,
356
+ description: "Discord webhook URL for notifications",
357
+ placeholder: "https://discord.com/api/webhooks/...",
358
+ sensitive: true,
359
+ testable: true,
360
+ validation: (value) => {
361
+ if (!value) return { valid: true };
362
+ if (!value.startsWith("https://discord.com/api/webhooks/")) {
363
+ return { valid: false, error: "Invalid Discord webhook URL" };
364
+ }
365
+ return { valid: true };
366
+ }
367
+ }
368
+ ];
369
+ function getEnvVariablesByCategory(category) {
370
+ return ENV_VARIABLES.filter((v) => v.category === category);
371
+ }
372
+ function getEnvVariable(key) {
373
+ return ENV_VARIABLES.find((v) => v.key === key);
374
+ }
375
+ function validateEnvValue(key, value) {
376
+ const envVar = getEnvVariable(key);
377
+ if (!envVar) return { valid: false, error: "Unknown environment variable" };
378
+ if (envVar.required && !value) {
379
+ return { valid: false, error: "This field is required" };
380
+ }
381
+ if (envVar.validation) {
382
+ return envVar.validation(value);
383
+ }
384
+ return { valid: true };
385
+ }
386
+ export {
387
+ ENV_VARIABLES,
388
+ getEnvVariable,
389
+ getEnvVariablesByCategory,
390
+ validateEnvValue
391
+ };