endorphin-ai 0.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/LICENSE.md +209 -0
- package/README.md +474 -0
- package/bin/endorphin.js +256 -0
- package/examples/endorphin.config.js +22 -0
- package/examples/tests/QE-001-basic-login-test.js +18 -0
- package/examples/tests/sample-test.js +9 -0
- package/examples/tools/.gitkeep +0 -0
- package/framework/config/agent-config.js +53 -0
- package/framework/config/browser-config.js +53 -0
- package/framework/config/paths.js +40 -0
- package/framework/core/agent-setup.js +75 -0
- package/framework/core/browser-framework.js +766 -0
- package/framework/core/config-loader.js +309 -0
- package/framework/core/test-discovery.js +402 -0
- package/framework/core/test-manager.js +343 -0
- package/framework/core/test-recorder.js +302 -0
- package/framework/core/test-runner.js +133 -0
- package/framework/core/test-session.js +98 -0
- package/framework/index.js +44 -0
- package/framework/interactive/enhanced-interactive-recorder.js +223 -0
- package/framework/interactive/index.js +18 -0
- package/framework/interactive/interactive-test-clean.js +33 -0
- package/framework/interactive/interactive-test.js +33 -0
- package/framework/test-framework.js +29 -0
- package/framework/testing/index.js +15 -0
- package/framework/testing/test-interactive-recorder.js +83 -0
- package/framework/testing/test-modular-framework.js +47 -0
- package/framework/testing/verify-test-format.js +58 -0
- package/framework/tools/content.js +67 -0
- package/framework/tools/index.js +52 -0
- package/framework/tools/interaction.js +180 -0
- package/framework/tools/navigation.js +43 -0
- package/framework/tools/utilities.js +99 -0
- package/framework/tools/verification.js +84 -0
- package/package.json +84 -0
package/bin/endorphin.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Endorphin AI CLI - E2E Testing Reinvented with AI
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { dirname, join } from 'path';
|
|
9
|
+
import { readFileSync } from 'fs';
|
|
10
|
+
import { getConfig } from '../framework/core/config-loader.js';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
|
|
15
|
+
// Get package info
|
|
16
|
+
const packagePath = join(__dirname, '..', 'package.json');
|
|
17
|
+
const packageInfo = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
18
|
+
|
|
19
|
+
const args = process.argv.slice(2);
|
|
20
|
+
|
|
21
|
+
// Parse CLI flags into configuration overrides
|
|
22
|
+
function parseCliFlags(args) {
|
|
23
|
+
const flags = {};
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < args.length; i++) {
|
|
26
|
+
const arg = args[i];
|
|
27
|
+
const nextArg = args[i + 1];
|
|
28
|
+
|
|
29
|
+
switch (arg) {
|
|
30
|
+
case '--headless':
|
|
31
|
+
flags.headless = true;
|
|
32
|
+
break;
|
|
33
|
+
case '--no-headless':
|
|
34
|
+
flags.headless = false;
|
|
35
|
+
break;
|
|
36
|
+
case '--viewport':
|
|
37
|
+
if (nextArg && nextArg.includes('x')) {
|
|
38
|
+
const [width, height] = nextArg.split('x').map(Number);
|
|
39
|
+
flags.viewport = { width, height };
|
|
40
|
+
i++; // Skip next argument
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
case '--timeout':
|
|
44
|
+
if (nextArg && !isNaN(nextArg)) {
|
|
45
|
+
flags.timeout = parseInt(nextArg, 10);
|
|
46
|
+
i++;
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
case '--parallel':
|
|
50
|
+
if (nextArg && !isNaN(nextArg)) {
|
|
51
|
+
flags.parallel = parseInt(nextArg, 10);
|
|
52
|
+
i++;
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
case '--model':
|
|
56
|
+
if (nextArg) {
|
|
57
|
+
flags.model = nextArg;
|
|
58
|
+
i++;
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
case '--env':
|
|
62
|
+
case '--environment':
|
|
63
|
+
if (nextArg) {
|
|
64
|
+
flags.environment = nextArg;
|
|
65
|
+
i++;
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
case '--base-url':
|
|
69
|
+
if (nextArg) {
|
|
70
|
+
flags.baseUrl = nextArg;
|
|
71
|
+
i++;
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
case '--temperature':
|
|
75
|
+
if (nextArg && !isNaN(nextArg)) {
|
|
76
|
+
flags.temperature = parseFloat(nextArg);
|
|
77
|
+
i++;
|
|
78
|
+
}
|
|
79
|
+
break;
|
|
80
|
+
case '--retries':
|
|
81
|
+
if (nextArg && !isNaN(nextArg)) {
|
|
82
|
+
flags.maxRetries = parseInt(nextArg, 10);
|
|
83
|
+
i++;
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
case '--tests-dir':
|
|
87
|
+
if (nextArg) {
|
|
88
|
+
flags.testsDirectory = nextArg;
|
|
89
|
+
i++;
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
case '--data-dir':
|
|
93
|
+
if (nextArg) {
|
|
94
|
+
flags.dataDirectory = nextArg;
|
|
95
|
+
i++;
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return flags;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Show help
|
|
105
|
+
function showHelp() {
|
|
106
|
+
console.log(`
|
|
107
|
+
🎉 Endorphin AI v${packageInfo.version} - E2E Testing Reinvented with AI
|
|
108
|
+
|
|
109
|
+
Usage:
|
|
110
|
+
endorphin <command> [options]
|
|
111
|
+
|
|
112
|
+
Commands:
|
|
113
|
+
run test <test-id> Run a specific test (e.g., QE-001)
|
|
114
|
+
run test all Run all tests
|
|
115
|
+
run test --tag <tag> Run tests by tag (e.g., authentication)
|
|
116
|
+
run test --priority <level> Run tests by priority (High, Medium, Low)
|
|
117
|
+
run test-recorder Start interactive test recorder
|
|
118
|
+
list List all available tests
|
|
119
|
+
help Show this help message
|
|
120
|
+
|
|
121
|
+
Options:
|
|
122
|
+
--headless Run browser in headless mode
|
|
123
|
+
--no-headless Run browser with visible UI
|
|
124
|
+
--viewport <WxH> Set browser viewport (e.g., 1920x1080)
|
|
125
|
+
--timeout <ms> Set test timeout in milliseconds
|
|
126
|
+
--parallel <n> Run tests in parallel (default: 1)
|
|
127
|
+
--model <name> Set AI model to use (e.g., gpt-4o-mini)
|
|
128
|
+
--env <environment> Set environment (development/staging/production)
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
endorphin run test QE-001 # Run specific test
|
|
132
|
+
endorphin run test all --headless # Run all tests headless
|
|
133
|
+
endorphin run test --tag smoke --parallel 3 # Run smoke tests in parallel
|
|
134
|
+
endorphin run test --priority High --env staging # Run high priority tests on staging
|
|
135
|
+
endorphin run test-recorder # Start test recorder
|
|
136
|
+
endorphin list # Show all available tests
|
|
137
|
+
|
|
138
|
+
Configuration:
|
|
139
|
+
Create endorphin.config.js in your project root for default settings
|
|
140
|
+
CLI flags override configuration file settings
|
|
141
|
+
Set OPENAI_API_KEY in your .env file or environment variables
|
|
142
|
+
|
|
143
|
+
Documentation: https://github.com/andrewnovykov/endorphin-ai#readme
|
|
144
|
+
`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Main CLI handler
|
|
148
|
+
async function main() {
|
|
149
|
+
try {
|
|
150
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h') || args.includes('help')) {
|
|
151
|
+
showHelp();
|
|
152
|
+
process.exit(0);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
156
|
+
console.log(`Endorphin AI v${packageInfo.version}`);
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Load configuration with CLI flag overrides
|
|
161
|
+
const cliFlags = parseCliFlags(args);
|
|
162
|
+
const config = await getConfig({ cwd: process.cwd(), cliFlags });
|
|
163
|
+
|
|
164
|
+
// Debug: show loaded config if --debug flag is present
|
|
165
|
+
if (args.includes('--debug')) {
|
|
166
|
+
console.log('🔧 Loaded configuration:', JSON.stringify(config, null, 2));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const command = args[0];
|
|
170
|
+
const subcommand = args[1];
|
|
171
|
+
const target = args[2];
|
|
172
|
+
|
|
173
|
+
// Handle list command
|
|
174
|
+
if (command === 'list') {
|
|
175
|
+
console.log('📋 Available Tests:');
|
|
176
|
+
const { listAllTests } = await import('../framework/core/test-discovery.js');
|
|
177
|
+
await listAllTests(config);
|
|
178
|
+
process.exit(0);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Handle run commands
|
|
182
|
+
if (command === 'run') {
|
|
183
|
+
if (subcommand === 'test-recorder') {
|
|
184
|
+
console.log('🎬 Starting Interactive Test Recorder...');
|
|
185
|
+
const { runInteractiveRecorder } = await import('../framework/interactive/enhanced-interactive-recorder.js');
|
|
186
|
+
await runInteractiveRecorder(config);
|
|
187
|
+
process.exit(0);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (subcommand === 'test') {
|
|
191
|
+
// Check for flags
|
|
192
|
+
if (args.includes('--tag')) {
|
|
193
|
+
const tagIndex = args.indexOf('--tag');
|
|
194
|
+
const tag = args[tagIndex + 1];
|
|
195
|
+
if (!tag) {
|
|
196
|
+
console.error('❌ Error: Please specify a tag value');
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
console.log(`🏷️ Running tests with tag: ${tag}`);
|
|
200
|
+
const { runTestsByTag } = await import('../framework/core/test-discovery.js');
|
|
201
|
+
await runTestsByTag(tag, config);
|
|
202
|
+
process.exit(0);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (args.includes('--priority')) {
|
|
206
|
+
const priorityIndex = args.indexOf('--priority');
|
|
207
|
+
const priority = args[priorityIndex + 1];
|
|
208
|
+
if (!priority) {
|
|
209
|
+
console.error('❌ Error: Please specify a priority value');
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
console.log(`🎯 Running tests with priority: ${priority}`);
|
|
213
|
+
const { runTestsByPriority } = await import('../framework/core/test-discovery.js');
|
|
214
|
+
await runTestsByPriority(priority, config);
|
|
215
|
+
process.exit(0);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Handle specific test or "all"
|
|
219
|
+
if (target === 'all') {
|
|
220
|
+
console.log('🚀 Running all tests...');
|
|
221
|
+
const { runAllTests } = await import('../framework/core/test-discovery.js');
|
|
222
|
+
await runAllTests(config);
|
|
223
|
+
process.exit(0);
|
|
224
|
+
} else if (target) {
|
|
225
|
+
console.log(`🧪 Running test: ${target}`);
|
|
226
|
+
const { runSingleTestById } = await import('../framework/core/test-discovery.js');
|
|
227
|
+
await runSingleTestById(target, config);
|
|
228
|
+
process.exit(0);
|
|
229
|
+
} else {
|
|
230
|
+
console.error('❌ Error: Please specify a test ID or "all" (e.g., endorphin run test QE-001)');
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
console.error(`❌ Unknown run command: ${subcommand}`);
|
|
236
|
+
console.log('Use "endorphin help" for usage information');
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
console.error(`❌ Unknown command: ${command}`);
|
|
241
|
+
console.log('Use "endorphin help" for usage information');
|
|
242
|
+
process.exit(1);
|
|
243
|
+
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error('❌ Error:', error.message);
|
|
246
|
+
|
|
247
|
+
if (error.message.includes('OPENAI_API_KEY')) {
|
|
248
|
+
console.log('\n💡 Tip: Make sure to set your OPENAI_API_KEY in your .env file or environment variables');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Run CLI
|
|
256
|
+
main();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
browser: {
|
|
3
|
+
headless: false, // Make sure this is false to see browser
|
|
4
|
+
viewport: { width: 1280, height: 720 },
|
|
5
|
+
timeout: 30000
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
// Results configuration
|
|
9
|
+
results: {
|
|
10
|
+
directory: "./test-results",
|
|
11
|
+
keepHistory: 10,
|
|
12
|
+
format: ["json", "html"],
|
|
13
|
+
screenshots: true,
|
|
14
|
+
recordVideo: false
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
ai: {
|
|
18
|
+
model: "gpt-4o",
|
|
19
|
+
maxRetries: 3,
|
|
20
|
+
temperature: 0.1
|
|
21
|
+
}
|
|
22
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// QE-001: Basic Login Test
|
|
2
|
+
// Description: Test the login functionality with valid credentials
|
|
3
|
+
// Priority: High
|
|
4
|
+
// Tags: authentication, login, smoke
|
|
5
|
+
|
|
6
|
+
export const QE_001 = {
|
|
7
|
+
id: "QE-001",
|
|
8
|
+
name: "Basic Login Test",
|
|
9
|
+
description: "Test the login functionality with valid credentials",
|
|
10
|
+
priority: "High",
|
|
11
|
+
tags: ["authentication", "login", "smoke"],
|
|
12
|
+
site: "https://qafromla.herokuapp.com/",
|
|
13
|
+
testData: {
|
|
14
|
+
email: "papapin888@gmail.com",
|
|
15
|
+
password: "lalalend"
|
|
16
|
+
},
|
|
17
|
+
task: "Navigate to https://qafromla.herokuapp.com/, click on 'Log In' button, fill email field with 'papapin888@gmail.com', fill password field with 'lalalend', click 'Sign In' button, and verify successful login by checking for dashboard or user menu elements."
|
|
18
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const HEALTH_001 = {
|
|
2
|
+
id: "HEALTH-001",
|
|
3
|
+
name: "Framework Health Check",
|
|
4
|
+
description: "Verify basic framework functionality",
|
|
5
|
+
priority: "High",
|
|
6
|
+
tags: ["health", "smoke"],
|
|
7
|
+
site: "https://example.com",
|
|
8
|
+
task: "Navigate to https://example.com and verify page loads successfully"
|
|
9
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Endorphin e2e AI test framework>
|
|
2
|
+
// Copyright (C) 2025 Redstudio Agency
|
|
3
|
+
|
|
4
|
+
// This program is free software: you can redistribute it and/or modify
|
|
5
|
+
// it under the terms of the GNU Affero General Public License as
|
|
6
|
+
// published by the Free Software Foundation, either version 3 of the
|
|
7
|
+
// License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
// This program is distributed in the hope that it will be useful,
|
|
10
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
// GNU Affero General Public License for more details.
|
|
13
|
+
|
|
14
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
import * as dotenv from "dotenv";
|
|
18
|
+
|
|
19
|
+
dotenv.config();
|
|
20
|
+
|
|
21
|
+
export const AGENT_CONFIG = {
|
|
22
|
+
// OpenAI Configuration
|
|
23
|
+
openai: {
|
|
24
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
25
|
+
modelName: "gpt-4o"
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
// Agent behavior settings
|
|
29
|
+
agent: {
|
|
30
|
+
recursionLimit: 150,
|
|
31
|
+
timeout: 5 * 60 * 1000, // 5 minutes
|
|
32
|
+
|
|
33
|
+
// Stop phrases that indicate test completion
|
|
34
|
+
stopPhrases: [
|
|
35
|
+
'test completed',
|
|
36
|
+
'verification complete',
|
|
37
|
+
'login successful',
|
|
38
|
+
'test finished',
|
|
39
|
+
'done',
|
|
40
|
+
'stop',
|
|
41
|
+
'stop - test completed'
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// Test execution settings
|
|
46
|
+
execution: {
|
|
47
|
+
stepDelay: 2000, // 2 seconds between test steps
|
|
48
|
+
maxRetries: 3,
|
|
49
|
+
retryDelay: 1000
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default AGENT_CONFIG;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Endorphin e2e AI test framework>
|
|
2
|
+
// Copyright (C) 2025 Redstudio Agency
|
|
3
|
+
|
|
4
|
+
// This program is free software: you can redistribute it and/or modify
|
|
5
|
+
// it under the terms of the GNU Affero General Public License as
|
|
6
|
+
// published by the Free Software Foundation, either version 3 of the
|
|
7
|
+
// License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
// This program is distributed in the hope that it will be useful,
|
|
10
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
// GNU Affero General Public License for more details.
|
|
13
|
+
|
|
14
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import dotenv from 'dotenv';
|
|
19
|
+
|
|
20
|
+
// Load environment variables
|
|
21
|
+
dotenv.config();
|
|
22
|
+
|
|
23
|
+
export const BROWSER_CONFIG = {
|
|
24
|
+
// Browser launch options
|
|
25
|
+
launchOptions: {
|
|
26
|
+
headless: process.env.HEADLESS === 'true' ? true : false,
|
|
27
|
+
args: ['--start-maximized']
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
// Browser context options
|
|
31
|
+
contextOptions: {
|
|
32
|
+
viewport: { width: 1920, height: 1080 }
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
// Default timeouts (in milliseconds)
|
|
36
|
+
timeouts: {
|
|
37
|
+
navigation: 60000,
|
|
38
|
+
element: 10000,
|
|
39
|
+
screenshot: 5000,
|
|
40
|
+
testExecution: 5 * 60 * 1000 // 5 minutes
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
// Screenshot options
|
|
44
|
+
screenshot: {
|
|
45
|
+
fullPage: false,
|
|
46
|
+
type: 'png'
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// Base URL for testing
|
|
50
|
+
baseUrl: process.env.BASE_URL || 'https://qafromla.herokuapp.com/'
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default BROWSER_CONFIG;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Endorphin e2e AI test framework>
|
|
2
|
+
// Copyright (C) 2025 Redstudio Agency
|
|
3
|
+
|
|
4
|
+
// This program is free software: you can redistribute it and/or modify
|
|
5
|
+
// it under the terms of the GNU Affero General Public License as
|
|
6
|
+
// published by the Free Software Foundation, either version 3 of the
|
|
7
|
+
// License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
// This program is distributed in the hope that it will be useful,
|
|
10
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
// GNU Affero General Public License for more details.
|
|
13
|
+
|
|
14
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
import path from 'path';
|
|
18
|
+
import { fileURLToPath } from 'url';
|
|
19
|
+
|
|
20
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
const __dirname = path.dirname(__filename);
|
|
22
|
+
|
|
23
|
+
// Get project root directory (go up from framework/config/ to project root)
|
|
24
|
+
const projectRoot = path.resolve(__dirname, '..', '..');
|
|
25
|
+
|
|
26
|
+
export const PATHS = {
|
|
27
|
+
// Project directories
|
|
28
|
+
PROJECT_ROOT: projectRoot,
|
|
29
|
+
TEST_RESULT_DIR: path.join(projectRoot, 'test-result'),
|
|
30
|
+
TEST_RECORDER_DIR: path.join(projectRoot, 'test-recorder'),
|
|
31
|
+
TESTS_DIR: path.join(projectRoot, 'tests'),
|
|
32
|
+
|
|
33
|
+
// Framework directories
|
|
34
|
+
FRAMEWORK_DIR: path.join(projectRoot, 'framework'),
|
|
35
|
+
TOOLS_DIR: path.join(projectRoot, 'framework', 'tools'),
|
|
36
|
+
CORE_DIR: path.join(projectRoot, 'framework', 'core'),
|
|
37
|
+
CONFIG_DIR: path.join(projectRoot, 'framework', 'config'),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default PATHS;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Endorphin e2e AI test framework>
|
|
2
|
+
// Copyright (C) 2025 Redstudio Agency
|
|
3
|
+
|
|
4
|
+
// This program is free software: you can redistribute it and/or modify
|
|
5
|
+
// it under the terms of the GNU Affero General Public License as
|
|
6
|
+
// published by the Free Software Foundation, either version 3 of the
|
|
7
|
+
// License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
// This program is distributed in the hope that it will be useful,
|
|
10
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
// GNU Affero General Public License for more details.
|
|
13
|
+
|
|
14
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
import { ChatOpenAI } from "@langchain/openai";
|
|
18
|
+
import { ToolNode } from "@langchain/langgraph/prebuilt";
|
|
19
|
+
import { StateGraph, MessagesAnnotation } from "@langchain/langgraph";
|
|
20
|
+
import { AGENT_CONFIG } from '../config/agent-config.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Setup AI agent with tools and workflow
|
|
24
|
+
* @param {Array} tools - Array of browser automation tools
|
|
25
|
+
* @returns {Object} Compiled agent workflow
|
|
26
|
+
*/
|
|
27
|
+
export async function setupAgent(tools) {
|
|
28
|
+
console.log("🤖 Configuring AI agent with tools...");
|
|
29
|
+
|
|
30
|
+
const toolNode = new ToolNode(tools);
|
|
31
|
+
|
|
32
|
+
const model = new ChatOpenAI({
|
|
33
|
+
openAIApiKey: AGENT_CONFIG.openai.apiKey,
|
|
34
|
+
modelName: AGENT_CONFIG.openai.modelName,
|
|
35
|
+
}).bindTools(tools);
|
|
36
|
+
|
|
37
|
+
function shouldContinue({ messages }) {
|
|
38
|
+
const lastMessage = messages[messages.length - 1];
|
|
39
|
+
|
|
40
|
+
// Check for explicit stop conditions in the message content
|
|
41
|
+
const content = lastMessage.content?.toLowerCase() || '';
|
|
42
|
+
const hasStopPhrase = AGENT_CONFIG.agent.stopPhrases.some(phrase =>
|
|
43
|
+
content.includes(phrase)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
if (hasStopPhrase) {
|
|
47
|
+
console.log(`🛑 Stop condition detected: ${content}`);
|
|
48
|
+
return "__end__";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (lastMessage.tool_calls?.length) {
|
|
52
|
+
return "tools";
|
|
53
|
+
}
|
|
54
|
+
return "__end__";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function callModel(state) {
|
|
58
|
+
const response = await model.invoke(state.messages);
|
|
59
|
+
return { messages: [response] };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const workflow = new StateGraph(MessagesAnnotation)
|
|
63
|
+
.addNode("agent", callModel)
|
|
64
|
+
.addEdge("__start__", "agent")
|
|
65
|
+
.addNode("tools", toolNode)
|
|
66
|
+
.addEdge("tools", "agent")
|
|
67
|
+
.addConditionalEdges("agent", shouldContinue);
|
|
68
|
+
|
|
69
|
+
const agent = workflow.compile({
|
|
70
|
+
recursionLimit: AGENT_CONFIG.agent.recursionLimit,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
console.log("✅ AI agent configured successfully");
|
|
74
|
+
return agent;
|
|
75
|
+
}
|