@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
|
@@ -75,7 +75,7 @@ function getDashboardHTML() {
|
|
|
75
75
|
.logo-mark {
|
|
76
76
|
width: 34px;
|
|
77
77
|
height: 34px;
|
|
78
|
-
background:
|
|
78
|
+
background: transparent;
|
|
79
79
|
border-radius: 8px;
|
|
80
80
|
display: grid;
|
|
81
81
|
place-items: center;
|
|
@@ -697,7 +697,9 @@ function getDashboardHTML() {
|
|
|
697
697
|
<!-- Sidebar -->
|
|
698
698
|
<aside>
|
|
699
699
|
<div class="logo">
|
|
700
|
-
<div class="logo-mark"
|
|
700
|
+
<div class="logo-mark">
|
|
701
|
+
<img src="https://openqa.orkajs.com/_next/image?url=https%3A%2F%2Forkajs.com%2Floutre-orka-qa.png&w=256&q=75" alt="OpenQA Logo" style="width: 40px; height: 40px;">
|
|
702
|
+
</div>
|
|
701
703
|
<div>
|
|
702
704
|
<div class="logo-name">OpenQA</div>
|
|
703
705
|
<div class="logo-version">v2.1.0 \xB7 OSS</div>
|
|
@@ -707,39 +709,69 @@ function getDashboardHTML() {
|
|
|
707
709
|
<div class="nav-section">
|
|
708
710
|
<div class="nav-label">Overview</div>
|
|
709
711
|
<a class="nav-item active" href="/">
|
|
710
|
-
<span class="icon"
|
|
712
|
+
<span class="icon">
|
|
713
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-gauge-icon lucide-gauge"><path d="m12 14 4-4"/><path d="M3.34 19a10 10 0 1 1 17.32 0"/></svg>
|
|
714
|
+
</span> Dashboard
|
|
711
715
|
</a>
|
|
712
716
|
<a class="nav-item" href="/kanban">
|
|
713
|
-
<span class="icon"
|
|
717
|
+
<span class="icon">
|
|
718
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-dashed-kanban-icon lucide-square-dashed-kanban"><path d="M8 7v7"/><path d="M12 7v4"/><path d="M16 7v9"/><path d="M5 3a2 2 0 0 0-2 2"/><path d="M9 3h1"/><path d="M14 3h1"/><path d="M19 3a2 2 0 0 1 2 2"/><path d="M21 9v1"/><path d="M21 14v1"/><path d="M21 19a2 2 0 0 1-2 2"/><path d="M14 21h1"/><path d="M9 21h1"/><path d="M5 21a2 2 0 0 1-2-2"/><path d="M3 14v1"/><path d="M3 9v1"/></svg>
|
|
719
|
+
</span> Kanban
|
|
714
720
|
<span class="badge" id="kanban-count">0</span>
|
|
715
721
|
</a>
|
|
716
722
|
|
|
717
723
|
<div class="nav-label">Agents</div>
|
|
718
724
|
<a class="nav-item" href="javascript:void(0)" onclick="scrollToSection('agents-table')">
|
|
719
|
-
<span class="icon"
|
|
725
|
+
<span class="icon">
|
|
726
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-activity-icon lucide-activity"><path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2"/></svg>
|
|
727
|
+
</span> Active Agents
|
|
720
728
|
</a>
|
|
721
729
|
<a class="nav-item" href="javascript:void(0)" onclick="switchAgentTab('specialists'); scrollToSection('agents-table')">
|
|
722
|
-
<span class="icon"
|
|
730
|
+
<span class="icon">
|
|
731
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-hat-glasses-icon lucide-hat-glasses"><path d="M14 18a2 2 0 0 0-4 0"/><path d="m19 11-2.11-6.657a2 2 0 0 0-2.752-1.148l-1.276.61A2 2 0 0 1 12 4H8.5a2 2 0 0 0-1.925 1.456L5 11"/><path d="M2 11h20"/><circle cx="17" cy="18" r="3"/><circle cx="7" cy="18" r="3"/></svg>
|
|
732
|
+
</span> Specialists
|
|
723
733
|
</a>
|
|
724
734
|
<a class="nav-item" href="javascript:void(0)" onclick="scrollToSection('interventions-panel')">
|
|
725
|
-
<span class="icon"
|
|
735
|
+
<span class="icon">
|
|
736
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-user-cog-icon lucide-user-cog"><path d="M10 15H6a4 4 0 0 0-4 4v2"/><path d="m14.305 16.53.923-.382"/><path d="m15.228 13.852-.923-.383"/><path d="m16.852 12.228-.383-.923"/><path d="m16.852 17.772-.383.924"/><path d="m19.148 12.228.383-.923"/><path d="m19.53 18.696-.382-.924"/><path d="m20.772 13.852.924-.383"/><path d="m20.772 16.148.924.383"/><circle cx="18" cy="15" r="3"/><circle cx="9" cy="7" r="4"/></svg>
|
|
737
|
+
</span> Interventions
|
|
726
738
|
<span class="badge" id="intervention-count" style="background: var(--red);">0</span>
|
|
727
739
|
</a>
|
|
728
740
|
|
|
729
741
|
<div class="nav-label">Analysis</div>
|
|
730
742
|
<a class="nav-item" href="javascript:void(0)" onclick="scrollToSection('issues-panel')">
|
|
731
|
-
<span class="icon"
|
|
743
|
+
<span class="icon">
|
|
744
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bug-play-icon lucide-bug-play"><path d="M10 19.655A6 6 0 0 1 6 14v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 3.97"/><path d="M14 15.003a1 1 0 0 1 1.517-.859l4.997 2.997a1 1 0 0 1 0 1.718l-4.997 2.997a1 1 0 0 1-1.517-.86z"/>
|
|
745
|
+
<path d="M14.12 3.88 16 2"/>
|
|
746
|
+
<path d="M21 5a4 4 0 0 1-3.55 3.97"/>
|
|
747
|
+
<path d="M3 21a4 4 0 0 1 3.81-4"/>
|
|
748
|
+
<path d="M3 5a4 4 0 0 0 3.55 3.97"/>
|
|
749
|
+
<path d="M6 13H2"/><path d="m8 2 1.88 1.88"/>
|
|
750
|
+
<path d="M9 7.13V6a3 3 0 1 1 6 0v1.13"/>
|
|
751
|
+
</svg>
|
|
752
|
+
</span> Bug Reports
|
|
732
753
|
</a>
|
|
733
754
|
<a class="nav-item" href="javascript:void(0)" onclick="switchChartTab('performance'); scrollToSection('chart-performance')">
|
|
734
|
-
<span class="icon"
|
|
755
|
+
<span class="icon">
|
|
756
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chart-spline-icon lucide-chart-spline"><path d="M3 3v16a2 2 0 0 0 2 2h16"/><path d="M7 16c.5-2 1.5-7 4-7 2 0 2 3 4 3 2.5 0 4.5-5 5-7"/></svg>
|
|
757
|
+
</span> Performance
|
|
735
758
|
</a>
|
|
736
759
|
<a class="nav-item" href="javascript:void(0)" onclick="scrollToSection('activity-list')">
|
|
737
|
-
<span class="icon"
|
|
760
|
+
<span class="icon">
|
|
761
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-scroll-text-icon lucide-scroll-text"><path d="M15 12h-5"/><path d="M15 8h-5"/><path d="M19 17V5a2 2 0 0 0-2-2H4"/><path d="M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3"/></svg>
|
|
762
|
+
</span> Logs
|
|
738
763
|
</a>
|
|
739
764
|
|
|
740
765
|
<div class="nav-label">System</div>
|
|
741
766
|
<a class="nav-item" href="/config">
|
|
742
|
-
<span class="icon"
|
|
767
|
+
<span class="icon">
|
|
768
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-columns3-cog-icon lucide-columns-3-cog"><path d="M10.5 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v5.5"/><path d="m14.3 19.6 1-.4"/><path d="M15 3v7.5"/><path d="m15.2 16.9-.9-.3"/><path d="m16.6 21.7.3-.9"/><path d="m16.8 15.3-.4-1"/><path d="m19.1 15.2.3-.9"/><path d="m19.6 21.7-.4-1"/><path d="m20.7 16.8 1-.4"/><path d="m21.7 19.4-.9-.3"/><path d="M9 3v18"/><circle cx="18" cy="18" r="3"/></svg>
|
|
769
|
+
</span> Config
|
|
770
|
+
</a>
|
|
771
|
+
<a class="nav-item" href="/config/env">
|
|
772
|
+
<span class="icon">
|
|
773
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-columns3-cog-icon lucide-columns-3-cog"><path d="M10.5 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v5.5"/><path d="m14.3 19.6 1-.4"/><path d="M15 3v7.5"/><path d="m15.2 16.9-.9-.3"/><path d="m16.6 21.7.3-.9"/><path d="m16.8 15.3-.4-1"/><path d="m19.1 15.2.3-.9"/><path d="m19.6 21.7-.4-1"/><path d="m20.7 16.8 1-.4"/><path d="m21.7 19.4-.9-.3"/><path d="M9 3v18"/><circle cx="18" cy="18" r="3"/></svg>
|
|
774
|
+
</span> Environment
|
|
743
775
|
</a>
|
|
744
776
|
</div>
|
|
745
777
|
|
|
@@ -771,7 +803,9 @@ function getDashboardHTML() {
|
|
|
771
803
|
<div class="metric-card">
|
|
772
804
|
<div class="metric-header">
|
|
773
805
|
<div class="metric-label">Active Agents</div>
|
|
774
|
-
<div class="metric-icon"
|
|
806
|
+
<div class="metric-icon">
|
|
807
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-terminal-icon lucide-square-terminal"><path d="m7 11 2-2-2-2"/><path d="M11 13h4"/><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/></svg>
|
|
808
|
+
</div>
|
|
775
809
|
</div>
|
|
776
810
|
<div class="metric-value" id="active-agents">0</div>
|
|
777
811
|
<div class="metric-change positive" id="agents-change">\u2191 0 from last hour</div>
|
|
@@ -779,7 +813,9 @@ function getDashboardHTML() {
|
|
|
779
813
|
<div class="metric-card">
|
|
780
814
|
<div class="metric-header">
|
|
781
815
|
<div class="metric-label">Total Actions</div>
|
|
782
|
-
<div class="metric-icon"
|
|
816
|
+
<div class="metric-icon">
|
|
817
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-list-todo-icon lucide-list-todo"><path d="M13 5h8"/><path d="M13 12h8"/><path d="M13 19h8"/><path d="m3 17 2 2 4-4"/><rect x="3" y="4" width="6" height="6" rx="1"/></svg>
|
|
818
|
+
</div>
|
|
783
819
|
</div>
|
|
784
820
|
<div class="metric-value" id="total-actions">0</div>
|
|
785
821
|
<div class="metric-change positive" id="actions-change">\u2191 0% this session</div>
|
|
@@ -787,7 +823,9 @@ function getDashboardHTML() {
|
|
|
787
823
|
<div class="metric-card">
|
|
788
824
|
<div class="metric-header">
|
|
789
825
|
<div class="metric-label">Bugs Found</div>
|
|
790
|
-
<div class="metric-icon"
|
|
826
|
+
<div class="metric-icon">
|
|
827
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-list-todo-icon lucide-list-todo"><path d="M13 5h8"/><path d="M13 12h8"/><path d="M13 19h8"/><path d="m3 17 2 2 4-4"/><rect x="3" y="4" width="6" height="6" rx="1"/></svg>
|
|
828
|
+
</div>
|
|
791
829
|
</div>
|
|
792
830
|
<div class="metric-value" id="bugs-found">0</div>
|
|
793
831
|
<div class="metric-change negative" id="bugs-change">\u2193 0 from yesterday</div>
|
|
@@ -795,7 +833,9 @@ function getDashboardHTML() {
|
|
|
795
833
|
<div class="metric-card">
|
|
796
834
|
<div class="metric-header">
|
|
797
835
|
<div class="metric-label">Success Rate</div>
|
|
798
|
-
<div class="metric-icon"
|
|
836
|
+
<div class="metric-icon">
|
|
837
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-cloud-check-icon lucide-cloud-check"><path d="m17 15-5.5 5.5L9 18"/><path d="M5.516 16.07A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 3.501 7.327"/></svg>
|
|
838
|
+
</div>
|
|
799
839
|
</div>
|
|
800
840
|
<div class="metric-value" id="success-rate">\u2014</div>
|
|
801
841
|
<div class="metric-change positive" id="rate-change">\u2191 0 pts improvement</div>
|
|
@@ -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
|
+
};
|