agentgui 1.0.78 → 1.0.80
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/.prd +0 -0
- package/CLAUDE.md +98 -1874
- package/package.json +2 -4
- package/static/app.js +115 -2
- package/.prd-browser +0 -607
- package/browser-test.js +0 -409
- package/run-e2e-test.sh +0 -88
- package/test-browser.js +0 -457
- package/test-runner.js +0 -182
package/test-browser.js
DELETED
|
@@ -1,457 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Browser Testing Harness
|
|
5
|
-
* Manages repository cloning, Claude Code execution, and browser testing
|
|
6
|
-
* for streaming event visualization
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const { execSync, spawn } = require('child_process');
|
|
12
|
-
const os = require('os');
|
|
13
|
-
|
|
14
|
-
class BrowserTestHarness {
|
|
15
|
-
constructor(config = {}) {
|
|
16
|
-
this.config = {
|
|
17
|
-
baseDir: config.baseDir || path.join(os.tmpdir(), 'agentgui-test'),
|
|
18
|
-
serverPort: config.serverPort || 3000,
|
|
19
|
-
serverUrl: config.serverUrl || 'http://localhost:3000',
|
|
20
|
-
baseURL: config.baseURL || '/gm',
|
|
21
|
-
timeout: config.timeout || 30 * 60 * 1000, // 30 minutes
|
|
22
|
-
concurrency: config.concurrency || 2,
|
|
23
|
-
...config
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
this.repos = [];
|
|
27
|
-
this.executionLog = [];
|
|
28
|
-
this.eventCounts = {};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Initialize test environment
|
|
33
|
-
*/
|
|
34
|
-
async init() {
|
|
35
|
-
console.log('Initializing browser test harness');
|
|
36
|
-
|
|
37
|
-
// Ensure base directory
|
|
38
|
-
if (!fs.existsSync(this.config.baseDir)) {
|
|
39
|
-
fs.mkdirSync(this.config.baseDir, { recursive: true });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
console.log(`Test directory: ${this.config.baseDir}`);
|
|
43
|
-
return this;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Clone a repository
|
|
48
|
-
*/
|
|
49
|
-
async cloneRepository(url, name) {
|
|
50
|
-
console.log(`Cloning repository: ${url}`);
|
|
51
|
-
|
|
52
|
-
const repoPath = path.join(this.config.baseDir, name);
|
|
53
|
-
|
|
54
|
-
// Check if already cloned
|
|
55
|
-
if (fs.existsSync(repoPath)) {
|
|
56
|
-
console.log(`Repository already exists: ${repoPath}`);
|
|
57
|
-
this.repos.push({
|
|
58
|
-
url,
|
|
59
|
-
name,
|
|
60
|
-
path: repoPath,
|
|
61
|
-
status: 'ready'
|
|
62
|
-
});
|
|
63
|
-
return repoPath;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
execSync(`git clone --depth 1 ${url} ${repoPath}`, {
|
|
68
|
-
stdio: 'pipe',
|
|
69
|
-
timeout: 60000
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const repo = {
|
|
73
|
-
url,
|
|
74
|
-
name,
|
|
75
|
-
path: repoPath,
|
|
76
|
-
status: 'cloned',
|
|
77
|
-
fileCount: this.countFiles(repoPath),
|
|
78
|
-
languages: this.detectLanguages(repoPath)
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
this.repos.push(repo);
|
|
82
|
-
console.log(`Repository cloned successfully: ${repo.fileCount} files`);
|
|
83
|
-
return repoPath;
|
|
84
|
-
} catch (error) {
|
|
85
|
-
console.error(`Failed to clone repository: ${error.message}`);
|
|
86
|
-
throw error;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Count files in repository
|
|
92
|
-
*/
|
|
93
|
-
countFiles(dirPath) {
|
|
94
|
-
let count = 0;
|
|
95
|
-
const walk = (dir) => {
|
|
96
|
-
try {
|
|
97
|
-
const files = fs.readdirSync(dir);
|
|
98
|
-
for (const file of files) {
|
|
99
|
-
const fullPath = path.join(dir, file);
|
|
100
|
-
if (fs.statSync(fullPath).isDirectory()) {
|
|
101
|
-
if (!file.startsWith('.') && file !== 'node_modules') {
|
|
102
|
-
walk(fullPath);
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
count++;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
} catch (e) {
|
|
109
|
-
// Ignore
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
walk(dirPath);
|
|
113
|
-
return count;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Detect languages in repository
|
|
118
|
-
*/
|
|
119
|
-
detectLanguages(dirPath) {
|
|
120
|
-
const langMap = {
|
|
121
|
-
'.js': 'JavaScript',
|
|
122
|
-
'.ts': 'TypeScript',
|
|
123
|
-
'.tsx': 'TypeScript',
|
|
124
|
-
'.jsx': 'JavaScript',
|
|
125
|
-
'.py': 'Python',
|
|
126
|
-
'.java': 'Java',
|
|
127
|
-
'.cpp': 'C++',
|
|
128
|
-
'.c': 'C',
|
|
129
|
-
'.cs': 'C#',
|
|
130
|
-
'.go': 'Go',
|
|
131
|
-
'.rs': 'Rust',
|
|
132
|
-
'.rb': 'Ruby',
|
|
133
|
-
'.php': 'PHP',
|
|
134
|
-
'.json': 'JSON'
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const languages = new Set();
|
|
138
|
-
const walk = (dir) => {
|
|
139
|
-
try {
|
|
140
|
-
const files = fs.readdirSync(dir);
|
|
141
|
-
for (const file of files) {
|
|
142
|
-
if (file.startsWith('.') || file === 'node_modules') continue;
|
|
143
|
-
const fullPath = path.join(dir, file);
|
|
144
|
-
if (fs.statSync(fullPath).isDirectory()) {
|
|
145
|
-
walk(fullPath);
|
|
146
|
-
} else {
|
|
147
|
-
const ext = path.extname(file).toLowerCase();
|
|
148
|
-
if (langMap[ext]) {
|
|
149
|
-
languages.add(langMap[ext]);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
} catch (e) {
|
|
154
|
-
// Ignore
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
walk(dirPath);
|
|
158
|
-
return Array.from(languages);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Execute Claude Code on repository
|
|
163
|
-
*/
|
|
164
|
-
async executeClaudeCode(repoPath, command, agentId = 'claude-code') {
|
|
165
|
-
console.log(`Executing Claude Code: ${command.substring(0, 50)}...`);
|
|
166
|
-
|
|
167
|
-
const execution = {
|
|
168
|
-
repoPath,
|
|
169
|
-
command,
|
|
170
|
-
agentId,
|
|
171
|
-
startTime: Date.now(),
|
|
172
|
-
events: [],
|
|
173
|
-
status: 'running'
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
try {
|
|
177
|
-
// Execute Claude Code with streaming output
|
|
178
|
-
const result = await this.runClaudeCode(repoPath, command, agentId);
|
|
179
|
-
|
|
180
|
-
execution.endTime = Date.now();
|
|
181
|
-
execution.duration = execution.endTime - execution.startTime;
|
|
182
|
-
execution.eventCount = result.events.length;
|
|
183
|
-
execution.events = result.events;
|
|
184
|
-
execution.status = 'completed';
|
|
185
|
-
execution.output = result.output;
|
|
186
|
-
execution.error = null;
|
|
187
|
-
} catch (error) {
|
|
188
|
-
execution.endTime = Date.now();
|
|
189
|
-
execution.duration = execution.endTime - execution.startTime;
|
|
190
|
-
execution.status = 'failed';
|
|
191
|
-
execution.error = error.message;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
this.executionLog.push(execution);
|
|
195
|
-
return execution;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Run Claude Code command
|
|
200
|
-
*/
|
|
201
|
-
async runClaudeCode(cwd, command, agentId) {
|
|
202
|
-
return new Promise((resolve, reject) => {
|
|
203
|
-
try {
|
|
204
|
-
const cmd = agentId === 'claude-code' ? 'claude' : 'opencode';
|
|
205
|
-
const fullCommand = `${cmd} ${command}`;
|
|
206
|
-
|
|
207
|
-
console.log(`Running: ${fullCommand} (cwd: ${cwd})`);
|
|
208
|
-
|
|
209
|
-
const proc = spawn('sh', ['-c', fullCommand], {
|
|
210
|
-
cwd,
|
|
211
|
-
timeout: this.config.timeout,
|
|
212
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
let stdout = '';
|
|
216
|
-
let stderr = '';
|
|
217
|
-
|
|
218
|
-
proc.stdout.on('data', (data) => {
|
|
219
|
-
stdout += data.toString();
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
proc.stderr.on('data', (data) => {
|
|
223
|
-
stderr += data.toString();
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
proc.on('close', (code) => {
|
|
227
|
-
const events = this.parseStreamingOutput(stdout + stderr);
|
|
228
|
-
|
|
229
|
-
resolve({
|
|
230
|
-
events,
|
|
231
|
-
output: stdout,
|
|
232
|
-
stderr,
|
|
233
|
-
exitCode: code
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
proc.on('error', (error) => {
|
|
238
|
-
reject(error);
|
|
239
|
-
});
|
|
240
|
-
} catch (error) {
|
|
241
|
-
reject(error);
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Parse streaming output to extract events
|
|
248
|
-
*/
|
|
249
|
-
parseStreamingOutput(output) {
|
|
250
|
-
const events = [];
|
|
251
|
-
|
|
252
|
-
// Try to parse JSON stream (stream-json format)
|
|
253
|
-
const lines = output.split('\n');
|
|
254
|
-
for (const line of lines) {
|
|
255
|
-
if (!line.trim()) continue;
|
|
256
|
-
try {
|
|
257
|
-
const json = JSON.parse(line);
|
|
258
|
-
if (json.type) {
|
|
259
|
-
events.push(json);
|
|
260
|
-
this.eventCounts[json.type] = (this.eventCounts[json.type] || 0) + 1;
|
|
261
|
-
}
|
|
262
|
-
} catch (e) {
|
|
263
|
-
// Not JSON, skip
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return events;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Simulate browser test scenario
|
|
272
|
-
*/
|
|
273
|
-
async testScenario(name, repoPath, command, expectedEventTypes = []) {
|
|
274
|
-
console.log(`\n=== Test Scenario: ${name} ===`);
|
|
275
|
-
|
|
276
|
-
try {
|
|
277
|
-
const execution = await this.executeClaudeCode(repoPath, command);
|
|
278
|
-
|
|
279
|
-
// Verify events
|
|
280
|
-
const eventTypes = execution.events.map(e => e.type);
|
|
281
|
-
const hasExpectedEvents = expectedEventTypes.every(type =>
|
|
282
|
-
eventTypes.includes(type)
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
const result = {
|
|
286
|
-
name,
|
|
287
|
-
status: execution.status,
|
|
288
|
-
duration: execution.duration,
|
|
289
|
-
eventCount: execution.eventCount,
|
|
290
|
-
eventTypes,
|
|
291
|
-
hasExpectedEvents,
|
|
292
|
-
passed: execution.status === 'completed' && hasExpectedEvents,
|
|
293
|
-
error: execution.error
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
console.log(`Result: ${result.passed ? 'PASSED' : 'FAILED'}`);
|
|
297
|
-
console.log(`Duration: ${(result.duration / 1000).toFixed(2)}s`);
|
|
298
|
-
console.log(`Events: ${result.eventCount} (types: ${eventTypes.join(', ')})`);
|
|
299
|
-
|
|
300
|
-
return result;
|
|
301
|
-
} catch (error) {
|
|
302
|
-
console.error(`Test failed with error: ${error.message}`);
|
|
303
|
-
return {
|
|
304
|
-
name,
|
|
305
|
-
status: 'error',
|
|
306
|
-
passed: false,
|
|
307
|
-
error: error.message
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Run all test scenarios
|
|
314
|
-
*/
|
|
315
|
-
async runAllScenarios() {
|
|
316
|
-
console.log('\n\n=== BROWSER TEST EXECUTION ===\n');
|
|
317
|
-
|
|
318
|
-
const results = [];
|
|
319
|
-
|
|
320
|
-
// Clone test repositories
|
|
321
|
-
console.log('\n--- Repository Setup ---');
|
|
322
|
-
const repoURLs = [
|
|
323
|
-
{ url: 'https://github.com/lodash/lodash', name: 'lodash' },
|
|
324
|
-
{ url: 'https://github.com/requests/requests', name: 'requests' },
|
|
325
|
-
{ url: 'https://github.com/kubernetes/kubernetes', name: 'kubernetes' }
|
|
326
|
-
];
|
|
327
|
-
|
|
328
|
-
for (const repo of repoURLs) {
|
|
329
|
-
try {
|
|
330
|
-
await this.cloneRepository(repo.url, repo.name);
|
|
331
|
-
} catch (error) {
|
|
332
|
-
console.error(`Failed to clone ${repo.name}: ${error.message}`);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Run test scenarios
|
|
337
|
-
console.log('\n--- Test Scenarios ---');
|
|
338
|
-
|
|
339
|
-
if (this.repos.length > 0) {
|
|
340
|
-
const repo1 = this.repos[0];
|
|
341
|
-
if (repo1.path) {
|
|
342
|
-
// Test 1: Analyze files
|
|
343
|
-
results.push(await this.testScenario(
|
|
344
|
-
'Analyze JavaScript files',
|
|
345
|
-
repo1.path,
|
|
346
|
-
'ls -la | head -20',
|
|
347
|
-
['file_read', 'command_execute']
|
|
348
|
-
));
|
|
349
|
-
|
|
350
|
-
// Test 2: View file structure
|
|
351
|
-
results.push(await this.testScenario(
|
|
352
|
-
'Explore directory structure',
|
|
353
|
-
repo1.path,
|
|
354
|
-
'find . -name "*.md" | head -10',
|
|
355
|
-
['command_execute']
|
|
356
|
-
));
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return results;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Generate test report
|
|
365
|
-
*/
|
|
366
|
-
generateReport() {
|
|
367
|
-
const passed = this.executionLog.filter(e => e.status === 'completed').length;
|
|
368
|
-
const failed = this.executionLog.filter(e => e.status === 'failed').length;
|
|
369
|
-
const totalEvents = Object.values(this.eventCounts).reduce((a, b) => a + b, 0);
|
|
370
|
-
|
|
371
|
-
const report = {
|
|
372
|
-
timestamp: new Date().toISOString(),
|
|
373
|
-
summary: {
|
|
374
|
-
totalExecutions: this.executionLog.length,
|
|
375
|
-
passed,
|
|
376
|
-
failed,
|
|
377
|
-
successRate: this.executionLog.length > 0 ? (passed / this.executionLog.length * 100).toFixed(2) + '%' : 'N/A'
|
|
378
|
-
},
|
|
379
|
-
repositories: this.repos.map(r => ({
|
|
380
|
-
name: r.name,
|
|
381
|
-
fileCount: r.fileCount,
|
|
382
|
-
languages: r.languages,
|
|
383
|
-
path: r.path
|
|
384
|
-
})),
|
|
385
|
-
events: {
|
|
386
|
-
totalCount: totalEvents,
|
|
387
|
-
byType: this.eventCounts
|
|
388
|
-
},
|
|
389
|
-
executions: this.executionLog.map(e => ({
|
|
390
|
-
command: e.command,
|
|
391
|
-
status: e.status,
|
|
392
|
-
duration: `${(e.duration / 1000).toFixed(2)}s`,
|
|
393
|
-
eventCount: e.eventCount,
|
|
394
|
-
error: e.error
|
|
395
|
-
}))
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
return report;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Save report to file
|
|
403
|
-
*/
|
|
404
|
-
saveReport(filepath) {
|
|
405
|
-
const report = this.generateReport();
|
|
406
|
-
fs.writeFileSync(filepath, JSON.stringify(report, null, 2));
|
|
407
|
-
console.log(`Report saved: ${filepath}`);
|
|
408
|
-
return report;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Cleanup test environment
|
|
413
|
-
*/
|
|
414
|
-
async cleanup() {
|
|
415
|
-
console.log('\nCleaning up test environment...');
|
|
416
|
-
|
|
417
|
-
try {
|
|
418
|
-
if (fs.existsSync(this.config.baseDir)) {
|
|
419
|
-
execSync(`rm -rf ${this.config.baseDir}`, { timeout: 30000 });
|
|
420
|
-
console.log('Test directory cleaned up');
|
|
421
|
-
}
|
|
422
|
-
} catch (error) {
|
|
423
|
-
console.error(`Cleanup error: ${error.message}`);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// CLI usage
|
|
429
|
-
if (require.main === module) {
|
|
430
|
-
(async () => {
|
|
431
|
-
const harness = new BrowserTestHarness({
|
|
432
|
-
baseDir: path.join(os.tmpdir(), 'agentgui-test-' + Date.now())
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
try {
|
|
436
|
-
await harness.init();
|
|
437
|
-
const results = await harness.runAllScenarios();
|
|
438
|
-
|
|
439
|
-
// Save report
|
|
440
|
-
const reportPath = path.join(process.cwd(), 'test-report.json');
|
|
441
|
-
const report = harness.saveReport(reportPath);
|
|
442
|
-
|
|
443
|
-
console.log('\n=== FINAL REPORT ===');
|
|
444
|
-
console.log(JSON.stringify(report.summary, null, 2));
|
|
445
|
-
|
|
446
|
-
// Cleanup
|
|
447
|
-
await harness.cleanup();
|
|
448
|
-
|
|
449
|
-
process.exit(results.every(r => r.passed) ? 0 : 1);
|
|
450
|
-
} catch (error) {
|
|
451
|
-
console.error('Fatal error:', error);
|
|
452
|
-
process.exit(1);
|
|
453
|
-
}
|
|
454
|
-
})();
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
module.exports = BrowserTestHarness;
|
package/test-runner.js
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import http from 'http';
|
|
2
|
-
import { spawn, execSync } from 'child_process';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
6
|
-
|
|
7
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
|
|
9
|
-
const results = {
|
|
10
|
-
phase1: { status: 'PENDING', details: '' },
|
|
11
|
-
phase2: { status: 'PENDING', details: '' },
|
|
12
|
-
phase3: { status: 'PENDING', details: '' },
|
|
13
|
-
phase4: { status: 'PENDING', details: '' },
|
|
14
|
-
phase5: { status: 'PENDING', details: '' },
|
|
15
|
-
phase6: { status: 'PENDING', details: '' },
|
|
16
|
-
phase7: { status: 'PENDING', details: '' },
|
|
17
|
-
phase8: { status: 'PENDING', details: '' },
|
|
18
|
-
phase9: { status: 'PENDING', details: '' },
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
function log(phase, message) {
|
|
22
|
-
const timestamp = new Date().toISOString();
|
|
23
|
-
console.log(`[${timestamp}] [${phase}] ${message}`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function checkServerRunning() {
|
|
27
|
-
return new Promise((resolve) => {
|
|
28
|
-
const req = http.get('http://localhost:3000', (res) => {
|
|
29
|
-
resolve(res.statusCode === 200 || res.statusCode === 302);
|
|
30
|
-
res.resume();
|
|
31
|
-
});
|
|
32
|
-
req.on('error', () => resolve(false));
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function startServer() {
|
|
37
|
-
return new Promise((resolve, reject) => {
|
|
38
|
-
log('PHASE1', 'Starting server...');
|
|
39
|
-
const proc = spawn('node', ['server.js'], {
|
|
40
|
-
cwd: __dirname,
|
|
41
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
42
|
-
detached: false
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
let serverReady = false;
|
|
46
|
-
let output = '';
|
|
47
|
-
|
|
48
|
-
proc.stdout.on('data', (chunk) => {
|
|
49
|
-
output += chunk.toString();
|
|
50
|
-
if (output.includes('Server running on port 3000') || output.includes('listening')) {
|
|
51
|
-
if (!serverReady) {
|
|
52
|
-
serverReady = true;
|
|
53
|
-
log('PHASE1', 'Server appears to be ready, waiting for confirmation...');
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
proc.stderr.on('data', (chunk) => {
|
|
59
|
-
console.error(`[SERVER] ${chunk.toString()}`);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
setTimeout(async () => {
|
|
63
|
-
const isRunning = await checkServerRunning();
|
|
64
|
-
if (isRunning) {
|
|
65
|
-
log('PHASE1', 'Server is responding to requests');
|
|
66
|
-
resolve(proc);
|
|
67
|
-
} else {
|
|
68
|
-
reject(new Error('Server failed to start after timeout'));
|
|
69
|
-
}
|
|
70
|
-
}, 5000);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async function cloneRepositories() {
|
|
75
|
-
return new Promise((resolve, reject) => {
|
|
76
|
-
log('PHASE3', 'Creating /tmp/test-repos directory');
|
|
77
|
-
try {
|
|
78
|
-
if (!fs.existsSync('/tmp/test-repos')) {
|
|
79
|
-
fs.mkdirSync('/tmp/test-repos', { recursive: true });
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
log('PHASE3', 'Cloning lodash repository...');
|
|
83
|
-
try {
|
|
84
|
-
execSync('git clone https://github.com/lodash/lodash /tmp/test-repos/lodash 2>&1', {
|
|
85
|
-
timeout: 60000,
|
|
86
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
87
|
-
});
|
|
88
|
-
log('PHASE3', 'Lodash cloned successfully');
|
|
89
|
-
} catch (e) {
|
|
90
|
-
log('PHASE3', `Lodash clone output: ${e.toString().substring(0, 500)}`);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
log('PHASE3', 'Cloning chalk repository...');
|
|
94
|
-
try {
|
|
95
|
-
execSync('git clone https://github.com/chalk/chalk /tmp/test-repos/chalk 2>&1', {
|
|
96
|
-
timeout: 60000,
|
|
97
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
98
|
-
});
|
|
99
|
-
log('PHASE3', 'Chalk cloned successfully');
|
|
100
|
-
} catch (e) {
|
|
101
|
-
log('PHASE3', `Chalk clone output: ${e.toString().substring(0, 500)}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const lodashExists = fs.existsSync('/tmp/test-repos/lodash/README.md');
|
|
105
|
-
const chalkExists = fs.existsSync('/tmp/test-repos/chalk/README.md');
|
|
106
|
-
|
|
107
|
-
if (lodashExists && chalkExists) {
|
|
108
|
-
log('PHASE3', 'Both repositories cloned successfully');
|
|
109
|
-
resolve({ lodashExists, chalkExists });
|
|
110
|
-
} else {
|
|
111
|
-
log('PHASE3', `Lodash: ${lodashExists}, Chalk: ${chalkExists}`);
|
|
112
|
-
reject(new Error('Repository clone verification failed'));
|
|
113
|
-
}
|
|
114
|
-
} catch (e) {
|
|
115
|
-
reject(e);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async function verifyServer() {
|
|
121
|
-
return new Promise((resolve) => {
|
|
122
|
-
const req = http.get('http://localhost:3000', (res) => {
|
|
123
|
-
log('PHASE1', `Server responded with status ${res.statusCode}`);
|
|
124
|
-
resolve(res.statusCode === 200 || res.statusCode === 302);
|
|
125
|
-
res.resume();
|
|
126
|
-
});
|
|
127
|
-
req.on('error', (err) => {
|
|
128
|
-
log('PHASE1', `Server connection error: ${err.message}`);
|
|
129
|
-
resolve(false);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function executePhases() {
|
|
135
|
-
try {
|
|
136
|
-
// PHASE 1 & 3: Start server and clone repos (parallel, but we'll do sequentially for now)
|
|
137
|
-
try {
|
|
138
|
-
const running = await checkServerRunning();
|
|
139
|
-
if (running) {
|
|
140
|
-
log('PHASE1', 'Server is already running on port 3000');
|
|
141
|
-
results.phase1.status = 'PASS';
|
|
142
|
-
results.phase1.details = 'Server verified running on port 3000';
|
|
143
|
-
} else {
|
|
144
|
-
log('PHASE1', 'Server not running, attempting to start...');
|
|
145
|
-
await startServer();
|
|
146
|
-
const verified = await verifyServer();
|
|
147
|
-
results.phase1.status = verified ? 'PASS' : 'FAIL';
|
|
148
|
-
results.phase1.details = verified ? 'Server started and verified' : 'Server failed verification';
|
|
149
|
-
}
|
|
150
|
-
} catch (e) {
|
|
151
|
-
results.phase1.status = 'FAIL';
|
|
152
|
-
results.phase1.details = e.message;
|
|
153
|
-
log('PHASE1', `ERROR: ${e.message}`);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// PHASE 3: Clone repositories
|
|
157
|
-
try {
|
|
158
|
-
const repos = await cloneRepositories();
|
|
159
|
-
results.phase3.status = 'PASS';
|
|
160
|
-
results.phase3.details = `Lodash: ${repos.lodashExists}, Chalk: ${repos.chalkExists}`;
|
|
161
|
-
} catch (e) {
|
|
162
|
-
results.phase3.status = 'FAIL';
|
|
163
|
-
results.phase3.details = e.message;
|
|
164
|
-
log('PHASE3', `ERROR: ${e.message}`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Write preliminary results
|
|
168
|
-
fs.writeFileSync('/home/user/agentgui/TEST_RESULTS_PHASE1.json', JSON.stringify(results, null, 2));
|
|
169
|
-
log('MAIN', 'Phase 1 and 3 complete. Preliminary results saved.');
|
|
170
|
-
log('MAIN', 'Ready for browser-based testing (Phases 2, 4-8)');
|
|
171
|
-
|
|
172
|
-
return results;
|
|
173
|
-
} catch (e) {
|
|
174
|
-
console.error('Test execution failed:', e);
|
|
175
|
-
throw e;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
executePhases().catch(e => {
|
|
180
|
-
console.error('Fatal error:', e);
|
|
181
|
-
process.exit(1);
|
|
182
|
-
});
|