@openqa/cli 1.0.3 → 1.0.5
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/dist/cli/{index.cjs → index.js} +128 -156
- package/package.json +14 -14
|
@@ -1,62 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
#!/usr/bin/env node
|
|
3
|
-
"use strict";
|
|
4
|
-
var __create = Object.create;
|
|
5
|
-
var __defProp = Object.defineProperty;
|
|
6
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
19
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
20
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
21
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
22
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
23
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
|
-
mod
|
|
25
|
-
));
|
|
26
|
-
|
|
27
|
-
// node_modules/tsup/assets/cjs_shims.js
|
|
28
|
-
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
29
|
-
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
30
2
|
|
|
31
3
|
// cli/index.ts
|
|
32
|
-
|
|
4
|
+
import { Command } from "commander";
|
|
33
5
|
|
|
34
6
|
// agent/index.ts
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
7
|
+
import { ReActAgent as ReActAgent2 } from "@orka-js/agent";
|
|
8
|
+
import { OpenAIAdapter as OpenAIAdapter2 } from "@orka-js/openai";
|
|
9
|
+
import { AnthropicAdapter as AnthropicAdapter2 } from "@orka-js/anthropic";
|
|
10
|
+
import { SessionMemory } from "@orka-js/memory-store";
|
|
11
|
+
import { Tracer } from "@orka-js/observability";
|
|
12
|
+
import { EventEmitter as EventEmitter3 } from "events";
|
|
41
13
|
|
|
42
14
|
// database/index.ts
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
var
|
|
49
|
-
var __dirname =
|
|
15
|
+
import Database from "better-sqlite3";
|
|
16
|
+
import { readFileSync } from "fs";
|
|
17
|
+
import { join, dirname } from "path";
|
|
18
|
+
import { fileURLToPath } from "url";
|
|
19
|
+
import { mkdirSync } from "fs";
|
|
20
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
var __dirname = dirname(__filename);
|
|
50
22
|
var OpenQADatabase = class {
|
|
51
23
|
db;
|
|
52
24
|
constructor(dbPath = "./data/openqa.db") {
|
|
53
|
-
const dir =
|
|
54
|
-
|
|
55
|
-
this.db = new
|
|
25
|
+
const dir = dirname(dbPath);
|
|
26
|
+
mkdirSync(dir, { recursive: true });
|
|
27
|
+
this.db = new Database(dbPath);
|
|
56
28
|
this.initialize();
|
|
57
29
|
}
|
|
58
30
|
initialize() {
|
|
59
|
-
const schema =
|
|
31
|
+
const schema = readFileSync(join(__dirname, "schema.sql"), "utf-8");
|
|
60
32
|
this.db.exec(schema);
|
|
61
33
|
}
|
|
62
34
|
getConfig(key) {
|
|
@@ -161,8 +133,8 @@ var OpenQADatabase = class {
|
|
|
161
133
|
};
|
|
162
134
|
|
|
163
135
|
// agent/config/index.ts
|
|
164
|
-
|
|
165
|
-
(
|
|
136
|
+
import { config as dotenvConfig } from "dotenv";
|
|
137
|
+
dotenvConfig();
|
|
166
138
|
var ConfigManager = class {
|
|
167
139
|
db;
|
|
168
140
|
envConfig;
|
|
@@ -240,9 +212,9 @@ var ConfigManager = class {
|
|
|
240
212
|
};
|
|
241
213
|
|
|
242
214
|
// agent/tools/browser.ts
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
215
|
+
import { chromium } from "playwright";
|
|
216
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
217
|
+
import { join as join2 } from "path";
|
|
246
218
|
var BrowserTools = class {
|
|
247
219
|
browser = null;
|
|
248
220
|
page = null;
|
|
@@ -252,10 +224,10 @@ var BrowserTools = class {
|
|
|
252
224
|
constructor(db, sessionId) {
|
|
253
225
|
this.db = db;
|
|
254
226
|
this.sessionId = sessionId;
|
|
255
|
-
(
|
|
227
|
+
mkdirSync2(this.screenshotDir, { recursive: true });
|
|
256
228
|
}
|
|
257
229
|
async initialize() {
|
|
258
|
-
this.browser = await
|
|
230
|
+
this.browser = await chromium.launch({ headless: true });
|
|
259
231
|
const context = await this.browser.newContext({
|
|
260
232
|
viewport: { width: 1920, height: 1080 },
|
|
261
233
|
userAgent: "OpenQA/1.0 (Automated Testing Agent)"
|
|
@@ -359,7 +331,7 @@ var BrowserTools = class {
|
|
|
359
331
|
if (!this.page) return "Browser not initialized. Navigate to a page first.";
|
|
360
332
|
try {
|
|
361
333
|
const filename = `${Date.now()}_${name}.png`;
|
|
362
|
-
const path = (
|
|
334
|
+
const path = join2(this.screenshotDir, filename);
|
|
363
335
|
await this.page.screenshot({ path, fullPage: true });
|
|
364
336
|
this.db.createAction({
|
|
365
337
|
session_id: this.sessionId,
|
|
@@ -425,7 +397,7 @@ ${errors.join("\n")}`;
|
|
|
425
397
|
};
|
|
426
398
|
|
|
427
399
|
// agent/tools/github.ts
|
|
428
|
-
|
|
400
|
+
import { Octokit } from "@octokit/rest";
|
|
429
401
|
var GitHubTools = class {
|
|
430
402
|
octokit = null;
|
|
431
403
|
db;
|
|
@@ -436,7 +408,7 @@ var GitHubTools = class {
|
|
|
436
408
|
this.sessionId = sessionId;
|
|
437
409
|
this.config = config;
|
|
438
410
|
if (config.token) {
|
|
439
|
-
this.octokit = new
|
|
411
|
+
this.octokit = new Octokit({ auth: config.token });
|
|
440
412
|
}
|
|
441
413
|
}
|
|
442
414
|
getTools() {
|
|
@@ -621,9 +593,9 @@ Total: ${tickets.length} tickets
|
|
|
621
593
|
};
|
|
622
594
|
|
|
623
595
|
// agent/webhooks/git-listener.ts
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
var GitListener = class extends
|
|
596
|
+
import { EventEmitter } from "events";
|
|
597
|
+
import { Octokit as Octokit2 } from "@octokit/rest";
|
|
598
|
+
var GitListener = class extends EventEmitter {
|
|
627
599
|
config;
|
|
628
600
|
octokit = null;
|
|
629
601
|
lastCommitSha = null;
|
|
@@ -639,7 +611,7 @@ var GitListener = class extends import_events.EventEmitter {
|
|
|
639
611
|
...config
|
|
640
612
|
};
|
|
641
613
|
if (config.provider === "github" && config.token) {
|
|
642
|
-
this.octokit = new
|
|
614
|
+
this.octokit = new Octokit2({ auth: config.token });
|
|
643
615
|
}
|
|
644
616
|
}
|
|
645
617
|
async start() {
|
|
@@ -908,10 +880,10 @@ var GitListener = class extends import_events.EventEmitter {
|
|
|
908
880
|
};
|
|
909
881
|
|
|
910
882
|
// agent/specialists/index.ts
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
883
|
+
import { ReActAgent } from "@orka-js/agent";
|
|
884
|
+
import { OpenAIAdapter } from "@orka-js/openai";
|
|
885
|
+
import { AnthropicAdapter } from "@orka-js/anthropic";
|
|
886
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
915
887
|
var SPECIALIST_PROMPTS = {
|
|
916
888
|
"form-tester": `You are a Form Testing Specialist. Your mission:
|
|
917
889
|
- Find all forms on the page (login, signup, contact, search, etc.)
|
|
@@ -1000,7 +972,7 @@ var SPECIALIST_PROMPTS = {
|
|
|
1000
972
|
- Test pagination and infinite scroll
|
|
1001
973
|
- Report navigation issues with affected URLs`
|
|
1002
974
|
};
|
|
1003
|
-
var SpecialistAgentManager = class extends
|
|
975
|
+
var SpecialistAgentManager = class extends EventEmitter2 {
|
|
1004
976
|
agents = /* @__PURE__ */ new Map();
|
|
1005
977
|
agentStatuses = /* @__PURE__ */ new Map();
|
|
1006
978
|
db;
|
|
@@ -1016,12 +988,12 @@ var SpecialistAgentManager = class extends import_events2.EventEmitter {
|
|
|
1016
988
|
}
|
|
1017
989
|
createLLMAdapter() {
|
|
1018
990
|
if (this.llmConfig.provider === "anthropic") {
|
|
1019
|
-
return new
|
|
991
|
+
return new AnthropicAdapter({
|
|
1020
992
|
apiKey: this.llmConfig.apiKey,
|
|
1021
993
|
model: this.llmConfig.model || "claude-3-5-sonnet-20241022"
|
|
1022
994
|
});
|
|
1023
995
|
}
|
|
1024
|
-
return new
|
|
996
|
+
return new OpenAIAdapter({
|
|
1025
997
|
apiKey: this.llmConfig.apiKey,
|
|
1026
998
|
model: this.llmConfig.model || "gpt-4"
|
|
1027
999
|
});
|
|
@@ -1029,7 +1001,7 @@ var SpecialistAgentManager = class extends import_events2.EventEmitter {
|
|
|
1029
1001
|
createSpecialist(type, customPrompt) {
|
|
1030
1002
|
const agentId = `${type}_${Date.now()}`;
|
|
1031
1003
|
const systemPrompt = customPrompt || SPECIALIST_PROMPTS[type];
|
|
1032
|
-
const agent = new
|
|
1004
|
+
const agent = new ReActAgent({
|
|
1033
1005
|
llm: this.createLLMAdapter(),
|
|
1034
1006
|
tools: this.browserTools.getTools(),
|
|
1035
1007
|
maxIterations: 15,
|
|
@@ -1338,7 +1310,7 @@ Remember to report findings from each skill separately.
|
|
|
1338
1310
|
};
|
|
1339
1311
|
|
|
1340
1312
|
// agent/index.ts
|
|
1341
|
-
var OpenQAAgent = class extends
|
|
1313
|
+
var OpenQAAgent = class extends EventEmitter3 {
|
|
1342
1314
|
agent = null;
|
|
1343
1315
|
db;
|
|
1344
1316
|
config;
|
|
@@ -1360,13 +1332,13 @@ var OpenQAAgent = class extends import_events3.EventEmitter {
|
|
|
1360
1332
|
const cfg = this.config.getConfig();
|
|
1361
1333
|
switch (cfg.llm.provider) {
|
|
1362
1334
|
case "anthropic":
|
|
1363
|
-
return new
|
|
1335
|
+
return new AnthropicAdapter2({
|
|
1364
1336
|
apiKey: cfg.llm.apiKey || process.env.ANTHROPIC_API_KEY,
|
|
1365
1337
|
model: cfg.llm.model || "claude-3-5-sonnet-20241022"
|
|
1366
1338
|
});
|
|
1367
1339
|
case "openai":
|
|
1368
1340
|
default:
|
|
1369
|
-
return new
|
|
1341
|
+
return new OpenAIAdapter2({
|
|
1370
1342
|
apiKey: cfg.llm.apiKey || process.env.OPENAI_API_KEY,
|
|
1371
1343
|
model: cfg.llm.model || "gpt-4"
|
|
1372
1344
|
});
|
|
@@ -1390,8 +1362,8 @@ var OpenQAAgent = class extends import_events3.EventEmitter {
|
|
|
1390
1362
|
...kanbanTools.getTools()
|
|
1391
1363
|
];
|
|
1392
1364
|
const llm = this.createLLMAdapter();
|
|
1393
|
-
const memory = new
|
|
1394
|
-
const tracer = new
|
|
1365
|
+
const memory = new SessionMemory({ maxMessages: 50 });
|
|
1366
|
+
const tracer = new Tracer({ logLevel: "info" });
|
|
1395
1367
|
const enabledSkills = this.skillManager.getEnabledSkills();
|
|
1396
1368
|
const skillPrompt = this.skillManager.generateSkillPrompt(enabledSkills);
|
|
1397
1369
|
const agentConfig = {
|
|
@@ -1430,7 +1402,7 @@ ${skillPrompt}
|
|
|
1430
1402
|
|
|
1431
1403
|
Always provide clear, actionable information with steps to reproduce. Think step by step like a human QA expert.`
|
|
1432
1404
|
};
|
|
1433
|
-
this.agent = new
|
|
1405
|
+
this.agent = new ReActAgent2(agentConfig, llm, memory);
|
|
1434
1406
|
this.specialistManager = new SpecialistAgentManager(
|
|
1435
1407
|
this.db,
|
|
1436
1408
|
this.sessionId,
|
|
@@ -1610,84 +1582,84 @@ Always provide clear, actionable information with steps to reproduce. Think step
|
|
|
1610
1582
|
};
|
|
1611
1583
|
|
|
1612
1584
|
// cli/index.ts
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
var program = new
|
|
1585
|
+
import { spawn } from "child_process";
|
|
1586
|
+
import { writeFileSync, readFileSync as readFileSync2, existsSync, unlinkSync } from "fs";
|
|
1587
|
+
import { join as join3 } from "path";
|
|
1588
|
+
import chalk from "chalk";
|
|
1589
|
+
import ora from "ora";
|
|
1590
|
+
var program = new Command();
|
|
1619
1591
|
var PID_FILE = "./data/openqa.pid";
|
|
1620
1592
|
program.name("openqa").description("OpenQA - Autonomous QA Testing Agent powered by Orka.js").version("1.0.0");
|
|
1621
1593
|
program.command("start").description("Start the OpenQA agent and web UI").option("-d, --daemon", "Run in daemon mode").action(async (options) => {
|
|
1622
|
-
const spinner = (
|
|
1594
|
+
const spinner = ora("Starting OpenQA...").start();
|
|
1623
1595
|
try {
|
|
1624
|
-
if (
|
|
1625
|
-
const pid = (
|
|
1596
|
+
if (existsSync(PID_FILE)) {
|
|
1597
|
+
const pid = readFileSync2(PID_FILE, "utf-8").trim();
|
|
1626
1598
|
try {
|
|
1627
1599
|
process.kill(parseInt(pid), 0);
|
|
1628
|
-
spinner.fail(
|
|
1629
|
-
console.log(
|
|
1630
|
-
console.log(
|
|
1600
|
+
spinner.fail(chalk.red("OpenQA is already running"));
|
|
1601
|
+
console.log(chalk.yellow(`PID: ${pid}`));
|
|
1602
|
+
console.log(chalk.cyan('Run "openqa stop" to stop it first'));
|
|
1631
1603
|
process.exit(1);
|
|
1632
1604
|
} catch {
|
|
1633
|
-
|
|
1605
|
+
unlinkSync(PID_FILE);
|
|
1634
1606
|
}
|
|
1635
1607
|
}
|
|
1636
1608
|
if (options.daemon) {
|
|
1637
|
-
const child =
|
|
1609
|
+
const child = spawn("node", [join3(process.cwd(), "dist/cli/daemon.js")], {
|
|
1638
1610
|
detached: true,
|
|
1639
1611
|
stdio: "ignore"
|
|
1640
1612
|
});
|
|
1641
1613
|
child.unref();
|
|
1642
|
-
|
|
1643
|
-
spinner.succeed(
|
|
1644
|
-
console.log(
|
|
1614
|
+
writeFileSync(PID_FILE, child.pid.toString());
|
|
1615
|
+
spinner.succeed(chalk.green("OpenQA started in daemon mode"));
|
|
1616
|
+
console.log(chalk.cyan(`PID: ${child.pid}`));
|
|
1645
1617
|
} else {
|
|
1646
|
-
spinner.succeed(
|
|
1618
|
+
spinner.succeed(chalk.green("OpenQA started"));
|
|
1647
1619
|
const agent = new OpenQAAgent();
|
|
1648
1620
|
const config = new ConfigManager();
|
|
1649
1621
|
const cfg = config.getConfig();
|
|
1650
|
-
console.log(
|
|
1651
|
-
console.log(
|
|
1652
|
-
console.log(
|
|
1653
|
-
console.log(
|
|
1654
|
-
console.log(
|
|
1655
|
-
console.log(
|
|
1656
|
-
console.log(
|
|
1657
|
-
console.log(
|
|
1622
|
+
console.log(chalk.cyan("\n\u{1F4CA} OpenQA Status:"));
|
|
1623
|
+
console.log(chalk.white(` Agent: Running`));
|
|
1624
|
+
console.log(chalk.white(` Target: ${cfg.saas.url || "Not configured"}`));
|
|
1625
|
+
console.log(chalk.white(` Web UI: http://localhost:${cfg.web.port}`));
|
|
1626
|
+
console.log(chalk.white(` DevTools: http://localhost:${cfg.web.port}`));
|
|
1627
|
+
console.log(chalk.white(` Kanban: http://localhost:${cfg.web.port}/kanban`));
|
|
1628
|
+
console.log(chalk.white(` Config: http://localhost:${cfg.web.port}/config`));
|
|
1629
|
+
console.log(chalk.gray("\nPress Ctrl+C to stop\n"));
|
|
1658
1630
|
if (cfg.agent.autoStart) {
|
|
1659
1631
|
await agent.startAutonomous();
|
|
1660
1632
|
} else {
|
|
1661
|
-
console.log(
|
|
1662
|
-
console.log(
|
|
1633
|
+
console.log(chalk.yellow("Auto-start disabled. Agent is idle."));
|
|
1634
|
+
console.log(chalk.cyan("Set AGENT_AUTO_START=true to enable autonomous mode"));
|
|
1663
1635
|
}
|
|
1664
1636
|
}
|
|
1665
1637
|
} catch (error) {
|
|
1666
|
-
spinner.fail(
|
|
1667
|
-
console.error(
|
|
1638
|
+
spinner.fail(chalk.red("Failed to start OpenQA"));
|
|
1639
|
+
console.error(chalk.red(error.message));
|
|
1668
1640
|
process.exit(1);
|
|
1669
1641
|
}
|
|
1670
1642
|
});
|
|
1671
1643
|
program.command("stop").description("Stop the OpenQA agent").action(() => {
|
|
1672
|
-
const spinner = (
|
|
1644
|
+
const spinner = ora("Stopping OpenQA...").start();
|
|
1673
1645
|
try {
|
|
1674
|
-
if (!
|
|
1675
|
-
spinner.fail(
|
|
1646
|
+
if (!existsSync(PID_FILE)) {
|
|
1647
|
+
spinner.fail(chalk.red("OpenQA is not running"));
|
|
1676
1648
|
process.exit(1);
|
|
1677
1649
|
}
|
|
1678
|
-
const pid = (
|
|
1650
|
+
const pid = readFileSync2(PID_FILE, "utf-8").trim();
|
|
1679
1651
|
try {
|
|
1680
1652
|
process.kill(parseInt(pid), "SIGTERM");
|
|
1681
|
-
|
|
1682
|
-
spinner.succeed(
|
|
1653
|
+
unlinkSync(PID_FILE);
|
|
1654
|
+
spinner.succeed(chalk.green("OpenQA stopped"));
|
|
1683
1655
|
} catch (error) {
|
|
1684
|
-
spinner.fail(
|
|
1685
|
-
console.error(
|
|
1686
|
-
|
|
1656
|
+
spinner.fail(chalk.red("Failed to stop OpenQA"));
|
|
1657
|
+
console.error(chalk.red("Process not found. Cleaning up PID file..."));
|
|
1658
|
+
unlinkSync(PID_FILE);
|
|
1687
1659
|
}
|
|
1688
1660
|
} catch (error) {
|
|
1689
|
-
spinner.fail(
|
|
1690
|
-
console.error(
|
|
1661
|
+
spinner.fail(chalk.red("Failed to stop OpenQA"));
|
|
1662
|
+
console.error(chalk.red(error.message));
|
|
1691
1663
|
process.exit(1);
|
|
1692
1664
|
}
|
|
1693
1665
|
});
|
|
@@ -1695,38 +1667,38 @@ program.command("status").description("Show OpenQA status").action(() => {
|
|
|
1695
1667
|
const config = new ConfigManager();
|
|
1696
1668
|
const cfg = config.getConfig();
|
|
1697
1669
|
const db = new OpenQADatabase(cfg.database.path);
|
|
1698
|
-
console.log(
|
|
1699
|
-
if (
|
|
1700
|
-
const pid = (
|
|
1670
|
+
console.log(chalk.cyan.bold("\n\u{1F4CA} OpenQA Status\n"));
|
|
1671
|
+
if (existsSync(PID_FILE)) {
|
|
1672
|
+
const pid = readFileSync2(PID_FILE, "utf-8").trim();
|
|
1701
1673
|
try {
|
|
1702
1674
|
process.kill(parseInt(pid), 0);
|
|
1703
|
-
console.log(
|
|
1704
|
-
console.log(
|
|
1675
|
+
console.log(chalk.green("\u2713 Agent: Running"));
|
|
1676
|
+
console.log(chalk.white(` PID: ${pid}`));
|
|
1705
1677
|
} catch {
|
|
1706
|
-
console.log(
|
|
1707
|
-
|
|
1678
|
+
console.log(chalk.red("\u2717 Agent: Stopped (stale PID file)"));
|
|
1679
|
+
unlinkSync(PID_FILE);
|
|
1708
1680
|
}
|
|
1709
1681
|
} else {
|
|
1710
|
-
console.log(
|
|
1711
|
-
}
|
|
1712
|
-
console.log(
|
|
1713
|
-
console.log(
|
|
1714
|
-
console.log(
|
|
1715
|
-
console.log(
|
|
1716
|
-
console.log(
|
|
1717
|
-
console.log(
|
|
1718
|
-
console.log(
|
|
1719
|
-
console.log(
|
|
1720
|
-
console.log(
|
|
1682
|
+
console.log(chalk.red("\u2717 Agent: Stopped"));
|
|
1683
|
+
}
|
|
1684
|
+
console.log(chalk.cyan("\n\u{1F310} Web Interfaces:"));
|
|
1685
|
+
console.log(chalk.white(` DevTools: http://localhost:${cfg.web.port}`));
|
|
1686
|
+
console.log(chalk.white(` Kanban: http://localhost:${cfg.web.port}/kanban`));
|
|
1687
|
+
console.log(chalk.white(` Config: http://localhost:${cfg.web.port}/config`));
|
|
1688
|
+
console.log(chalk.cyan("\n\u2699\uFE0F Configuration:"));
|
|
1689
|
+
console.log(chalk.white(` LLM Provider: ${cfg.llm.provider}`));
|
|
1690
|
+
console.log(chalk.white(` Target SaaS: ${cfg.saas.url || "Not configured"}`));
|
|
1691
|
+
console.log(chalk.white(` GitHub: ${cfg.github?.token ? "Configured" : "Not configured"}`));
|
|
1692
|
+
console.log(chalk.white(` Test Interval: ${cfg.agent.intervalMs / 1e3 / 60} minutes`));
|
|
1721
1693
|
const sessions = db.getRecentSessions(5);
|
|
1722
|
-
console.log(
|
|
1694
|
+
console.log(chalk.cyan(`
|
|
1723
1695
|
\u{1F4CB} Recent Sessions (${sessions.length}):`));
|
|
1724
1696
|
sessions.forEach((s) => {
|
|
1725
|
-
const status = s.status === "completed" ?
|
|
1726
|
-
console.log(
|
|
1697
|
+
const status = s.status === "completed" ? chalk.green("\u2713") : s.status === "failed" ? chalk.red("\u2717") : chalk.yellow("\u27F3");
|
|
1698
|
+
console.log(chalk.white(` ${status} ${s.id} - ${s.bugs_found} bugs found`));
|
|
1727
1699
|
});
|
|
1728
1700
|
const bugs = db.getBugsByStatus("open");
|
|
1729
|
-
console.log(
|
|
1701
|
+
console.log(chalk.cyan(`
|
|
1730
1702
|
\u{1F41B} Open Bugs: ${bugs.length}`));
|
|
1731
1703
|
const tickets = db.getKanbanTickets();
|
|
1732
1704
|
const byColumn = {
|
|
@@ -1735,42 +1707,42 @@ program.command("status").description("Show OpenQA status").action(() => {
|
|
|
1735
1707
|
"in-progress": tickets.filter((t) => t.column === "in-progress").length,
|
|
1736
1708
|
done: tickets.filter((t) => t.column === "done").length
|
|
1737
1709
|
};
|
|
1738
|
-
console.log(
|
|
1739
|
-
console.log(
|
|
1740
|
-
console.log(
|
|
1741
|
-
console.log(
|
|
1742
|
-
console.log(
|
|
1710
|
+
console.log(chalk.cyan("\n\u{1F4CA} Kanban Board:"));
|
|
1711
|
+
console.log(chalk.white(` Backlog: ${byColumn.backlog}`));
|
|
1712
|
+
console.log(chalk.white(` To Do: ${byColumn["to-do"]}`));
|
|
1713
|
+
console.log(chalk.white(` In Progress: ${byColumn["in-progress"]}`));
|
|
1714
|
+
console.log(chalk.white(` Done: ${byColumn.done}`));
|
|
1743
1715
|
console.log("");
|
|
1744
1716
|
});
|
|
1745
1717
|
program.command("config").description("Manage configuration").argument("[action]", "Action: get, set, list").argument("[key]", "Configuration key (e.g., llm.provider)").argument("[value]", "Configuration value").action((action, key, value) => {
|
|
1746
1718
|
const config = new ConfigManager();
|
|
1747
1719
|
if (!action || action === "list") {
|
|
1748
1720
|
const cfg = config.getConfig();
|
|
1749
|
-
console.log(
|
|
1721
|
+
console.log(chalk.cyan.bold("\n\u2699\uFE0F OpenQA Configuration\n"));
|
|
1750
1722
|
console.log(JSON.stringify(cfg, null, 2));
|
|
1751
1723
|
console.log("");
|
|
1752
1724
|
return;
|
|
1753
1725
|
}
|
|
1754
1726
|
if (action === "get") {
|
|
1755
1727
|
if (!key) {
|
|
1756
|
-
console.error(
|
|
1728
|
+
console.error(chalk.red('Error: key is required for "get" action'));
|
|
1757
1729
|
process.exit(1);
|
|
1758
1730
|
}
|
|
1759
1731
|
const val = config.get(key);
|
|
1760
|
-
console.log(
|
|
1732
|
+
console.log(chalk.cyan(`${key}:`), chalk.white(val || "Not set"));
|
|
1761
1733
|
return;
|
|
1762
1734
|
}
|
|
1763
1735
|
if (action === "set") {
|
|
1764
1736
|
if (!key || !value) {
|
|
1765
|
-
console.error(
|
|
1737
|
+
console.error(chalk.red('Error: key and value are required for "set" action'));
|
|
1766
1738
|
process.exit(1);
|
|
1767
1739
|
}
|
|
1768
1740
|
config.set(key, value);
|
|
1769
|
-
console.log(
|
|
1741
|
+
console.log(chalk.green(`\u2713 Set ${key} = ${value}`));
|
|
1770
1742
|
return;
|
|
1771
1743
|
}
|
|
1772
|
-
console.error(
|
|
1773
|
-
console.log(
|
|
1744
|
+
console.error(chalk.red(`Unknown action: ${action}`));
|
|
1745
|
+
console.log(chalk.cyan("Available actions: get, set, list"));
|
|
1774
1746
|
process.exit(1);
|
|
1775
1747
|
});
|
|
1776
1748
|
program.command("logs").description("Show agent logs").option("-f, --follow", "Follow log output").option("-n, --lines <number>", "Number of lines to show", "50").action((options) => {
|
|
@@ -1779,25 +1751,25 @@ program.command("logs").description("Show agent logs").option("-f, --follow", "F
|
|
|
1779
1751
|
const db = new OpenQADatabase(cfg.database.path);
|
|
1780
1752
|
const sessions = db.getRecentSessions(1);
|
|
1781
1753
|
if (sessions.length === 0) {
|
|
1782
|
-
console.log(
|
|
1754
|
+
console.log(chalk.yellow("No sessions found"));
|
|
1783
1755
|
return;
|
|
1784
1756
|
}
|
|
1785
1757
|
const session = sessions[0];
|
|
1786
1758
|
const actions = db.getSessionActions(session.id);
|
|
1787
|
-
console.log(
|
|
1759
|
+
console.log(chalk.cyan.bold(`
|
|
1788
1760
|
\u{1F4CB} Session Logs: ${session.id}
|
|
1789
1761
|
`));
|
|
1790
|
-
console.log(
|
|
1791
|
-
console.log(
|
|
1792
|
-
console.log(
|
|
1762
|
+
console.log(chalk.white(`Status: ${session.status}`));
|
|
1763
|
+
console.log(chalk.white(`Started: ${session.started_at}`));
|
|
1764
|
+
console.log(chalk.white(`Actions: ${actions.length}
|
|
1793
1765
|
`));
|
|
1794
1766
|
const limit = parseInt(options.lines);
|
|
1795
1767
|
const displayActions = actions.slice(0, limit);
|
|
1796
1768
|
displayActions.forEach((action) => {
|
|
1797
1769
|
const icon = action.type === "navigate" ? "\u{1F310}" : action.type === "click" ? "\u{1F446}" : action.type === "fill" ? "\u2328\uFE0F" : action.type === "screenshot" ? "\u{1F4F8}" : action.type === "github_issue" ? "\u{1F41B}" : action.type === "kanban_ticket" ? "\u{1F4CB}" : "\u2022";
|
|
1798
|
-
console.log(
|
|
1770
|
+
console.log(chalk.gray(`[${action.timestamp}]`), icon, chalk.white(action.description));
|
|
1799
1771
|
if (action.output) {
|
|
1800
|
-
console.log(
|
|
1772
|
+
console.log(chalk.gray(` \u2192 ${action.output}`));
|
|
1801
1773
|
}
|
|
1802
1774
|
});
|
|
1803
1775
|
console.log("");
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openqa/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Autonomous QA testing agent powered by Orka.js",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/cli/index.
|
|
6
|
+
"main": "./dist/cli/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"openqa": "./dist/cli/index.
|
|
8
|
+
"openqa": "./dist/cli/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
@@ -46,28 +46,28 @@
|
|
|
46
46
|
"url": "https://github.com/Orka-Community/OpenQA/issues"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
+
"@octokit/rest": "^20.0.2",
|
|
49
50
|
"@orka-js/agent": "^1.0.0",
|
|
50
|
-
"@orka-js/openai": "^1.0.0",
|
|
51
51
|
"@orka-js/anthropic": "^1.0.0",
|
|
52
52
|
"@orka-js/memory-store": "^1.0.0",
|
|
53
53
|
"@orka-js/observability": "^1.0.0",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
54
|
+
"@orka-js/openai": "^1.0.0",
|
|
55
|
+
"better-sqlite3": "^12.8.0",
|
|
56
|
+
"chalk": "^5.3.0",
|
|
57
57
|
"commander": "^11.1.0",
|
|
58
|
-
"express": "^4.18.2",
|
|
59
|
-
"ws": "^8.16.0",
|
|
60
58
|
"dotenv": "^16.3.1",
|
|
61
|
-
"
|
|
62
|
-
"ora": "^8.0.1"
|
|
59
|
+
"express": "^4.18.2",
|
|
60
|
+
"ora": "^8.0.1",
|
|
61
|
+
"playwright": "^1.40.0",
|
|
62
|
+
"ws": "^8.16.0"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@types/
|
|
65
|
+
"@types/better-sqlite3": "^7.6.8",
|
|
66
66
|
"@types/express": "^4.17.21",
|
|
67
|
+
"@types/node": "^20.10.6",
|
|
67
68
|
"@types/ws": "^8.5.10",
|
|
68
|
-
"@types/better-sqlite3": "^7.6.8",
|
|
69
|
-
"tsx": "^4.7.0",
|
|
70
69
|
"tsup": "^8.0.1",
|
|
70
|
+
"tsx": "^4.7.0",
|
|
71
71
|
"typescript": "^5.3.3"
|
|
72
72
|
}
|
|
73
73
|
}
|