@openqa/cli 2.0.0 → 2.1.1
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 +202 -5
- package/dist/agent/index-v2.js +33 -55
- package/dist/agent/index-v2.js.map +1 -1
- package/dist/agent/index.js +85 -116
- package/dist/agent/index.js.map +1 -1
- package/dist/cli/daemon.js +1530 -277
- package/dist/cli/dashboard.html.js +55 -15
- 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 +4568 -2317
- package/dist/cli/server.js +2212 -19
- package/install.sh +19 -10
- package/package.json +2 -1
package/dist/agent/index.js
CHANGED
|
@@ -26,7 +26,7 @@ init_esm_shims();
|
|
|
26
26
|
import { ReActAgent as ReActAgent2 } from "@orka-js/agent";
|
|
27
27
|
import { OpenAIAdapter as OpenAIAdapter2 } from "@orka-js/openai";
|
|
28
28
|
import { AnthropicAdapter as AnthropicAdapter2 } from "@orka-js/anthropic";
|
|
29
|
-
import {
|
|
29
|
+
import { Memory } from "@orka-js/memory-store";
|
|
30
30
|
import { Tracer } from "@orka-js/observability";
|
|
31
31
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
32
32
|
|
|
@@ -621,13 +621,9 @@ var BrowserTools = class {
|
|
|
621
621
|
{
|
|
622
622
|
name: "navigate_to_page",
|
|
623
623
|
description: "Navigate to a specific URL in the application",
|
|
624
|
-
parameters:
|
|
625
|
-
type: "
|
|
626
|
-
|
|
627
|
-
url: { type: "string", description: "The URL to navigate to" }
|
|
628
|
-
},
|
|
629
|
-
required: ["url"]
|
|
630
|
-
},
|
|
624
|
+
parameters: [
|
|
625
|
+
{ name: "url", type: "string", description: "The URL to navigate to", required: true }
|
|
626
|
+
],
|
|
631
627
|
execute: async ({ url }) => {
|
|
632
628
|
if (!this.page) await this.initialize();
|
|
633
629
|
try {
|
|
@@ -640,24 +636,20 @@ var BrowserTools = class {
|
|
|
640
636
|
input: url,
|
|
641
637
|
output: `Page title: ${title}`
|
|
642
638
|
});
|
|
643
|
-
return `Successfully navigated to ${url}. Page title: "${title}"
|
|
639
|
+
return { output: `Successfully navigated to ${url}. Page title: "${title}"` };
|
|
644
640
|
} catch (error) {
|
|
645
|
-
return `Failed to navigate: ${error instanceof Error ? error.message : String(error)}
|
|
641
|
+
return { output: `Failed to navigate: ${error instanceof Error ? error.message : String(error)}`, error: error instanceof Error ? error.message : String(error) };
|
|
646
642
|
}
|
|
647
643
|
}
|
|
648
644
|
},
|
|
649
645
|
{
|
|
650
646
|
name: "click_element",
|
|
651
647
|
description: "Click on an element using a CSS selector",
|
|
652
|
-
parameters:
|
|
653
|
-
type: "
|
|
654
|
-
|
|
655
|
-
selector: { type: "string", description: "CSS selector of the element to click" }
|
|
656
|
-
},
|
|
657
|
-
required: ["selector"]
|
|
658
|
-
},
|
|
648
|
+
parameters: [
|
|
649
|
+
{ name: "selector", type: "string", description: "CSS selector of the element to click", required: true }
|
|
650
|
+
],
|
|
659
651
|
execute: async ({ selector }) => {
|
|
660
|
-
if (!this.page) return "Browser not initialized. Navigate to a page first.";
|
|
652
|
+
if (!this.page) return { output: "Browser not initialized. Navigate to a page first.", error: "Browser not initialized" };
|
|
661
653
|
try {
|
|
662
654
|
await this.page.click(selector, { timeout: 5e3 });
|
|
663
655
|
this.db.createAction({
|
|
@@ -666,25 +658,21 @@ var BrowserTools = class {
|
|
|
666
658
|
description: `Clicked element: ${selector}`,
|
|
667
659
|
input: selector
|
|
668
660
|
});
|
|
669
|
-
return `Successfully clicked element: ${selector}
|
|
661
|
+
return { output: `Successfully clicked element: ${selector}` };
|
|
670
662
|
} catch (error) {
|
|
671
|
-
return `Failed to click element: ${error instanceof Error ? error.message : String(error)}
|
|
663
|
+
return { output: `Failed to click element: ${error instanceof Error ? error.message : String(error)}`, error: error instanceof Error ? error.message : String(error) };
|
|
672
664
|
}
|
|
673
665
|
}
|
|
674
666
|
},
|
|
675
667
|
{
|
|
676
668
|
name: "fill_input",
|
|
677
669
|
description: "Fill an input field with text",
|
|
678
|
-
parameters:
|
|
679
|
-
type: "
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
text: { type: "string", description: "Text to fill in the input" }
|
|
683
|
-
},
|
|
684
|
-
required: ["selector", "text"]
|
|
685
|
-
},
|
|
670
|
+
parameters: [
|
|
671
|
+
{ name: "selector", type: "string", description: "CSS selector of the input field", required: true },
|
|
672
|
+
{ name: "text", type: "string", description: "Text to fill in the input", required: true }
|
|
673
|
+
],
|
|
686
674
|
execute: async ({ selector, text }) => {
|
|
687
|
-
if (!this.page) return "Browser not initialized. Navigate to a page first.";
|
|
675
|
+
if (!this.page) return { output: "Browser not initialized. Navigate to a page first.", error: "Browser not initialized" };
|
|
688
676
|
try {
|
|
689
677
|
await this.page.fill(selector, text);
|
|
690
678
|
this.db.createAction({
|
|
@@ -693,24 +681,20 @@ var BrowserTools = class {
|
|
|
693
681
|
description: `Filled input ${selector}`,
|
|
694
682
|
input: `${selector} = ${text}`
|
|
695
683
|
});
|
|
696
|
-
return `Successfully filled input ${selector} with text
|
|
684
|
+
return { output: `Successfully filled input ${selector} with text` };
|
|
697
685
|
} catch (error) {
|
|
698
|
-
return `Failed to fill input: ${error instanceof Error ? error.message : String(error)}
|
|
686
|
+
return { output: `Failed to fill input: ${error instanceof Error ? error.message : String(error)}`, error: error instanceof Error ? error.message : String(error) };
|
|
699
687
|
}
|
|
700
688
|
}
|
|
701
689
|
},
|
|
702
690
|
{
|
|
703
691
|
name: "take_screenshot",
|
|
704
692
|
description: "Take a screenshot of the current page for evidence",
|
|
705
|
-
parameters:
|
|
706
|
-
type: "
|
|
707
|
-
|
|
708
|
-
name: { type: "string", description: "Name for the screenshot file" }
|
|
709
|
-
},
|
|
710
|
-
required: ["name"]
|
|
711
|
-
},
|
|
693
|
+
parameters: [
|
|
694
|
+
{ name: "name", type: "string", description: "Name for the screenshot file", required: true }
|
|
695
|
+
],
|
|
712
696
|
execute: async ({ name }) => {
|
|
713
|
-
if (!this.page) return "Browser not initialized. Navigate to a page first.";
|
|
697
|
+
if (!this.page) return { output: "Browser not initialized. Navigate to a page first.", error: "Browser not initialized" };
|
|
714
698
|
try {
|
|
715
699
|
const filename = `${Date.now()}_${name}.png`;
|
|
716
700
|
const path2 = join2(this.screenshotDir, filename);
|
|
@@ -721,38 +705,32 @@ var BrowserTools = class {
|
|
|
721
705
|
description: `Screenshot: ${name}`,
|
|
722
706
|
screenshot_path: path2
|
|
723
707
|
});
|
|
724
|
-
return `Screenshot saved: ${path2}
|
|
708
|
+
return { output: `Screenshot saved: ${path2}` };
|
|
725
709
|
} catch (error) {
|
|
726
|
-
return `Failed to take screenshot: ${error instanceof Error ? error.message : String(error)}
|
|
710
|
+
return { output: `Failed to take screenshot: ${error instanceof Error ? error.message : String(error)}`, error: error instanceof Error ? error.message : String(error) };
|
|
727
711
|
}
|
|
728
712
|
}
|
|
729
713
|
},
|
|
730
714
|
{
|
|
731
715
|
name: "get_page_content",
|
|
732
716
|
description: "Get the text content of the current page",
|
|
733
|
-
parameters:
|
|
734
|
-
type: "object",
|
|
735
|
-
properties: {}
|
|
736
|
-
},
|
|
717
|
+
parameters: [],
|
|
737
718
|
execute: async () => {
|
|
738
|
-
if (!this.page) return "Browser not initialized. Navigate to a page first.";
|
|
719
|
+
if (!this.page) return { output: "Browser not initialized. Navigate to a page first.", error: "Browser not initialized" };
|
|
739
720
|
try {
|
|
740
721
|
const content = await this.page.textContent("body");
|
|
741
|
-
return content?.slice(0, 1e3) || "No content found";
|
|
722
|
+
return { output: content?.slice(0, 1e3) || "No content found" };
|
|
742
723
|
} catch (error) {
|
|
743
|
-
return `Failed to get content: ${error instanceof Error ? error.message : String(error)}
|
|
724
|
+
return { output: `Failed to get content: ${error instanceof Error ? error.message : String(error)}`, error: error instanceof Error ? error.message : String(error) };
|
|
744
725
|
}
|
|
745
726
|
}
|
|
746
727
|
},
|
|
747
728
|
{
|
|
748
729
|
name: "check_console_errors",
|
|
749
730
|
description: "Check for JavaScript console errors on the page",
|
|
750
|
-
parameters:
|
|
751
|
-
type: "object",
|
|
752
|
-
properties: {}
|
|
753
|
-
},
|
|
731
|
+
parameters: [],
|
|
754
732
|
execute: async () => {
|
|
755
|
-
if (!this.page) return "Browser not initialized. Navigate to a page first.";
|
|
733
|
+
if (!this.page) return { output: "Browser not initialized. Navigate to a page first.", error: "Browser not initialized" };
|
|
756
734
|
const errors = [];
|
|
757
735
|
this.page.on("console", (msg) => {
|
|
758
736
|
if (msg.type() === "error") {
|
|
@@ -761,10 +739,10 @@ var BrowserTools = class {
|
|
|
761
739
|
});
|
|
762
740
|
await this.page.waitForTimeout(2e3);
|
|
763
741
|
if (errors.length > 0) {
|
|
764
|
-
return `Found ${errors.length} console errors:
|
|
765
|
-
${errors.join("\n")}
|
|
742
|
+
return { output: `Found ${errors.length} console errors:
|
|
743
|
+
${errors.join("\n")}` };
|
|
766
744
|
}
|
|
767
|
-
return "No console errors detected";
|
|
745
|
+
return { output: "No console errors detected" };
|
|
768
746
|
}
|
|
769
747
|
}
|
|
770
748
|
];
|
|
@@ -799,20 +777,16 @@ var GitHubTools = class {
|
|
|
799
777
|
{
|
|
800
778
|
name: "create_github_issue",
|
|
801
779
|
description: "Create a GitHub issue when a critical bug is found. Use this for bugs that require developer attention.",
|
|
802
|
-
parameters:
|
|
803
|
-
type: "
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
screenshot_path: { type: "string", description: "Path to screenshot evidence" }
|
|
810
|
-
},
|
|
811
|
-
required: ["title", "body", "severity"]
|
|
812
|
-
},
|
|
780
|
+
parameters: [
|
|
781
|
+
{ name: "title", type: "string", description: "Issue title (concise and descriptive)", required: true },
|
|
782
|
+
{ name: "body", type: "string", description: "Detailed description with steps to reproduce", required: true },
|
|
783
|
+
{ name: "severity", type: "string", description: "Bug severity (low, medium, high, critical)", required: true },
|
|
784
|
+
{ name: "labels", type: "string", description: "Comma-separated labels for the issue", required: false },
|
|
785
|
+
{ name: "screenshot_path", type: "string", description: "Path to screenshot evidence", required: false }
|
|
786
|
+
],
|
|
813
787
|
execute: async ({ title, body, severity, labels = [], screenshot_path }) => {
|
|
814
788
|
if (!this.octokit || !this.config.owner || !this.config.repo) {
|
|
815
|
-
return "GitHub not configured. Please set GITHUB_TOKEN, GITHUB_OWNER, and GITHUB_REPO.";
|
|
789
|
+
return { output: "GitHub not configured. Please set GITHUB_TOKEN, GITHUB_OWNER, and GITHUB_REPO.", error: "GitHub not configured" };
|
|
816
790
|
}
|
|
817
791
|
try {
|
|
818
792
|
const severityLabel = `severity: ${severity}`;
|
|
@@ -852,11 +826,11 @@ ${screenshot_path ? `**Screenshot:** ${screenshot_path}` : ""}
|
|
|
852
826
|
github_issue_url: issue.data.html_url,
|
|
853
827
|
screenshot_path
|
|
854
828
|
});
|
|
855
|
-
return `\u2705 GitHub issue created successfully!
|
|
829
|
+
return { output: `\u2705 GitHub issue created successfully!
|
|
856
830
|
URL: ${issue.data.html_url}
|
|
857
|
-
Issue #${issue.data.number}
|
|
831
|
+
Issue #${issue.data.number}` };
|
|
858
832
|
} catch (error) {
|
|
859
|
-
return `\u274C Failed to create GitHub issue: ${error instanceof Error ? error.message : String(error)}
|
|
833
|
+
return { output: `\u274C Failed to create GitHub issue: ${error instanceof Error ? error.message : String(error)}`, error: error instanceof Error ? error.message : String(error) };
|
|
860
834
|
}
|
|
861
835
|
}
|
|
862
836
|
}
|
|
@@ -878,18 +852,14 @@ var KanbanTools = class {
|
|
|
878
852
|
{
|
|
879
853
|
name: "create_kanban_ticket",
|
|
880
854
|
description: "Create a ticket on the internal Kanban board for QA tracking. Use this for bugs, improvements, or test findings.",
|
|
881
|
-
parameters:
|
|
882
|
-
type: "
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
screenshot_path: { type: "string", description: "Path to screenshot evidence" }
|
|
890
|
-
},
|
|
891
|
-
required: ["title", "description", "priority"]
|
|
892
|
-
},
|
|
855
|
+
parameters: [
|
|
856
|
+
{ name: "title", type: "string", description: "Ticket title", required: true },
|
|
857
|
+
{ name: "description", type: "string", description: "Detailed description", required: true },
|
|
858
|
+
{ name: "priority", type: "string", description: "Ticket priority (low, medium, high, critical)", required: true },
|
|
859
|
+
{ name: "column", type: "string", description: "Kanban column (backlog, to-do, in-progress, done)", required: false },
|
|
860
|
+
{ name: "tags", type: "string", description: "Comma-separated tags for categorization", required: false },
|
|
861
|
+
{ name: "screenshot_path", type: "string", description: "Path to screenshot evidence", required: false }
|
|
862
|
+
],
|
|
893
863
|
execute: async ({ title, description, priority, column = "to-do", tags = [], screenshot_path }) => {
|
|
894
864
|
try {
|
|
895
865
|
const allTags = ["automated-qa", ...tags];
|
|
@@ -908,46 +878,39 @@ var KanbanTools = class {
|
|
|
908
878
|
input: JSON.stringify({ title, priority, column }),
|
|
909
879
|
output: ticket.id
|
|
910
880
|
});
|
|
911
|
-
return `\u2705 Kanban ticket created successfully!
|
|
881
|
+
return { output: `\u2705 Kanban ticket created successfully!
|
|
912
882
|
ID: ${ticket.id}
|
|
913
883
|
Column: ${column}
|
|
914
|
-
Priority: ${priority}
|
|
884
|
+
Priority: ${priority}` };
|
|
915
885
|
} catch (error) {
|
|
916
|
-
return `\u274C Failed to create Kanban ticket: ${error instanceof Error ? error.message : String(error)}
|
|
886
|
+
return { output: `\u274C Failed to create Kanban ticket: ${error instanceof Error ? error.message : String(error)}`, error: error instanceof Error ? error.message : String(error) };
|
|
917
887
|
}
|
|
918
888
|
}
|
|
919
889
|
},
|
|
920
890
|
{
|
|
921
891
|
name: "update_kanban_ticket",
|
|
922
892
|
description: "Update an existing Kanban ticket (move columns, change priority, etc.)",
|
|
923
|
-
parameters:
|
|
924
|
-
type: "
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
priority: { type: "string", enum: ["low", "medium", "high", "critical"], description: "New priority" }
|
|
929
|
-
},
|
|
930
|
-
required: ["ticket_id"]
|
|
931
|
-
},
|
|
893
|
+
parameters: [
|
|
894
|
+
{ name: "ticket_id", type: "string", description: "ID of the ticket to update", required: true },
|
|
895
|
+
{ name: "column", type: "string", description: "New column (backlog, to-do, in-progress, done)", required: false },
|
|
896
|
+
{ name: "priority", type: "string", description: "New priority (low, medium, high, critical)", required: false }
|
|
897
|
+
],
|
|
932
898
|
execute: async ({ ticket_id, column, priority }) => {
|
|
933
899
|
try {
|
|
934
900
|
const updates = {};
|
|
935
901
|
if (column) updates.column = column;
|
|
936
902
|
if (priority) updates.priority = priority;
|
|
937
903
|
await this.db.updateKanbanTicket(ticket_id, updates);
|
|
938
|
-
return `\u2705 Kanban ticket ${ticket_id} updated successfully
|
|
904
|
+
return { output: `\u2705 Kanban ticket ${ticket_id} updated successfully!` };
|
|
939
905
|
} catch (error) {
|
|
940
|
-
return `\u274C Failed to update Kanban ticket: ${error instanceof Error ? error.message : String(error)}
|
|
906
|
+
return { output: `\u274C Failed to update Kanban ticket: ${error instanceof Error ? error.message : String(error)}`, error: error instanceof Error ? error.message : String(error) };
|
|
941
907
|
}
|
|
942
908
|
}
|
|
943
909
|
},
|
|
944
910
|
{
|
|
945
911
|
name: "get_kanban_board",
|
|
946
912
|
description: "Get all tickets from the Kanban board to see current status",
|
|
947
|
-
parameters:
|
|
948
|
-
type: "object",
|
|
949
|
-
properties: {}
|
|
950
|
-
},
|
|
913
|
+
parameters: [],
|
|
951
914
|
execute: async () => {
|
|
952
915
|
try {
|
|
953
916
|
const tickets = await this.db.getKanbanTickets();
|
|
@@ -966,9 +929,9 @@ Priority: ${priority}`;
|
|
|
966
929
|
|
|
967
930
|
Total: ${tickets.length} tickets
|
|
968
931
|
`.trim();
|
|
969
|
-
return summary;
|
|
932
|
+
return { output: summary };
|
|
970
933
|
} catch (error) {
|
|
971
|
-
return `\u274C Failed to get Kanban board: ${error instanceof Error ? error.message : String(error)}
|
|
934
|
+
return { output: `\u274C Failed to get Kanban board: ${error instanceof Error ? error.message : String(error)}`, error: error instanceof Error ? error.message : String(error) };
|
|
972
935
|
}
|
|
973
936
|
}
|
|
974
937
|
}
|
|
@@ -1739,7 +1702,7 @@ var OpenQAAgent = class extends EventEmitter3 {
|
|
|
1739
1702
|
...kanbanTools.getTools()
|
|
1740
1703
|
];
|
|
1741
1704
|
const llm = this.createLLMAdapter();
|
|
1742
|
-
const memory = new
|
|
1705
|
+
const memory = new Memory({ maxMessages: 50 });
|
|
1743
1706
|
const tracer = new Tracer({ logLevel: "info" });
|
|
1744
1707
|
const enabledSkills = this.skillManager.getEnabledSkills();
|
|
1745
1708
|
const skillPrompt = this.skillManager.generateSkillPrompt(enabledSkills);
|
|
@@ -1780,8 +1743,9 @@ ${skillPrompt}
|
|
|
1780
1743
|
Always provide clear, actionable information with steps to reproduce. Think step by step like a human QA expert.`
|
|
1781
1744
|
};
|
|
1782
1745
|
this.agent = new ReActAgent2({
|
|
1746
|
+
goal: agentConfig.goal,
|
|
1783
1747
|
tools: allTools,
|
|
1784
|
-
|
|
1748
|
+
maxSteps: cfg.agent.maxIterations,
|
|
1785
1749
|
systemPrompt: agentConfig.systemPrompt
|
|
1786
1750
|
}, llm, memory);
|
|
1787
1751
|
this.specialistManager = new SpecialistAgentManager(
|
|
@@ -1882,17 +1846,22 @@ Always provide clear, actionable information with steps to reproduce. Think step
|
|
|
1882
1846
|
branch: "main",
|
|
1883
1847
|
pollIntervalMs: 6e4
|
|
1884
1848
|
});
|
|
1885
|
-
} else
|
|
1886
|
-
const
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1849
|
+
} else {
|
|
1850
|
+
const gitlabToken = await this.config.get("gitlab.token");
|
|
1851
|
+
const gitlabProject = await this.config.get("gitlab.project");
|
|
1852
|
+
if (gitlabToken && gitlabProject) {
|
|
1853
|
+
const [owner, repo] = gitlabProject.split("/");
|
|
1854
|
+
const gitlabUrl = await this.config.get("gitlab.url");
|
|
1855
|
+
this.gitListener = new GitListener({
|
|
1856
|
+
provider: "gitlab",
|
|
1857
|
+
token: gitlabToken,
|
|
1858
|
+
owner,
|
|
1859
|
+
repo,
|
|
1860
|
+
branch: "main",
|
|
1861
|
+
pollIntervalMs: 6e4,
|
|
1862
|
+
gitlabUrl: gitlabUrl || "https://gitlab.com"
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1896
1865
|
}
|
|
1897
1866
|
if (this.gitListener) {
|
|
1898
1867
|
this.gitListener.on("merge", async (event) => {
|