@vatzzza/botintern 1.0.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/.github/workflows/npm-publish.yml +33 -0
- package/README.md +15 -0
- package/bun.lock +80 -0
- package/index.js +425 -0
- package/lib/ai.js +531 -0
- package/lib/aiFileParser.js +54 -0
- package/lib/fileTreeParser.js +105 -0
- package/lib/loop.js +174 -0
- package/lib/registerAndFetchKey.js +75 -0
- package/lib/runPlaywright.js +39 -0
- package/lib/runTests.js +418 -0
- package/lib/scan.js +58 -0
- package/package.json +29 -0
- package/tsconfig.json +29 -0
- package/utils/errorExtractor.js +33 -0
- package/utils/setupPlaywright.js +29 -0
- package/utils/yaml-example.txt +21 -0
- package/utils/yamlGuardrails.js +130 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Validates YAML structure and checks for common issues
|
|
7
|
+
*/
|
|
8
|
+
export function validateYamlStructure(yaml) {
|
|
9
|
+
const requiredSections = ['meta', 'scenarios'];
|
|
10
|
+
const lines = yaml.split('\n');
|
|
11
|
+
|
|
12
|
+
// Check for invalid keys that should be preserved from original
|
|
13
|
+
const invalidKeys = ['actions', 'assert_visible', 'assert_text', 'selector'];
|
|
14
|
+
const foundInvalidKeys = [];
|
|
15
|
+
|
|
16
|
+
for (const line of lines) {
|
|
17
|
+
for (const key of invalidKeys) {
|
|
18
|
+
if (line.includes(`${key}:`) || line.includes(`${key} `)) {
|
|
19
|
+
foundInvalidKeys.push(key);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
isValid: foundInvalidKeys.length === 0,
|
|
26
|
+
invalidKeys: foundInvalidKeys,
|
|
27
|
+
hasRequiredSections: requiredSections.some(section => yaml.includes(`${section}:`)),
|
|
28
|
+
lineCount: lines.length
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates a timestamped backup of the YAML file
|
|
34
|
+
*/
|
|
35
|
+
export function createBackup(filePath, content) {
|
|
36
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
37
|
+
const backupPath = filePath.replace('.yaml', `.backup.${timestamp}.yaml`);
|
|
38
|
+
fs.writeFileSync(backupPath, content);
|
|
39
|
+
return backupPath;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Performs safe YAML update preserving working sections
|
|
44
|
+
*/
|
|
45
|
+
export function safeYamlUpdate(originalYaml, newYaml) {
|
|
46
|
+
// Preserve meta section completely unless user specifically asks to change it
|
|
47
|
+
const originalMeta = originalYaml.match(/meta:([\s\S]*?)(?=\n\w+:|$)/);
|
|
48
|
+
const newMeta = newYaml.match(/meta:([\s\S]*?)(?=\n\w+:|$)/);
|
|
49
|
+
|
|
50
|
+
let finalYaml = newYaml;
|
|
51
|
+
|
|
52
|
+
// If original has meta and new doesn't have baseUrl changes, preserve original
|
|
53
|
+
if (originalMeta && !newYaml.includes('baseUrl:')) {
|
|
54
|
+
finalYaml = newYaml.replace(/meta:([\s\S]*?)(?=\n\w+:|$)/, originalMeta[0]);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return finalYaml;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Calculates diff between original and new YAML to validate changes
|
|
62
|
+
*/
|
|
63
|
+
export function calculateYamlDiff(original, updated) {
|
|
64
|
+
if (!original || !original.trim()) {
|
|
65
|
+
return {
|
|
66
|
+
changes: updated ? ['Full file created'] : [],
|
|
67
|
+
isMinimal: false,
|
|
68
|
+
changePercentage: 100
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const originalLines = original.split('\n').filter(line => line.trim());
|
|
73
|
+
const updatedLines = updated.split('\n').filter(line => line.trim());
|
|
74
|
+
|
|
75
|
+
const unchanged = [];
|
|
76
|
+
const modified = [];
|
|
77
|
+
const added = [];
|
|
78
|
+
const removed = [];
|
|
79
|
+
|
|
80
|
+
// Simple diff analysis
|
|
81
|
+
for (const line of updatedLines) {
|
|
82
|
+
if (originalLines.includes(line)) {
|
|
83
|
+
unchanged.push(line);
|
|
84
|
+
} else {
|
|
85
|
+
added.push(line);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const line of originalLines) {
|
|
90
|
+
if (!updatedLines.includes(line)) {
|
|
91
|
+
removed.push(line);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const totalLines = Math.max(originalLines.length, updatedLines.length);
|
|
96
|
+
const changePercentage = totalLines > 0 ? ((added.length + removed.length) / totalLines) * 100 : 0;
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
changes: [
|
|
100
|
+
...added.map(line => `+ ${line}`),
|
|
101
|
+
...removed.map(line => `- ${line}`)
|
|
102
|
+
],
|
|
103
|
+
isMinimal: changePercentage < 50, // Consider minimal if less than 50% changed
|
|
104
|
+
changePercentage,
|
|
105
|
+
stats: {
|
|
106
|
+
total: totalLines,
|
|
107
|
+
unchanged: unchanged.length,
|
|
108
|
+
added: added.length,
|
|
109
|
+
removed: removed.length
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Validates that changes are minimal and necessary
|
|
116
|
+
*/
|
|
117
|
+
export function validateMinimalChanges(original, updated, userPrompt) {
|
|
118
|
+
const diff = calculateYamlDiff(original, updated);
|
|
119
|
+
|
|
120
|
+
// If user provided specific prompt, allow more changes
|
|
121
|
+
const maxChangePercentage = userPrompt && userPrompt.trim() ? 75 : 30;
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
isValid: diff.isMinimal || diff.changePercentage <= maxChangePercentage,
|
|
125
|
+
diff,
|
|
126
|
+
recommendation: diff.changePercentage > maxChangePercentage
|
|
127
|
+
? `🚨 Large change detected: ${diff.changePercentage.toFixed(1)}% modified. Consider more targeted edits.`
|
|
128
|
+
: `✅ Minimal changes: ${diff.changePercentage.toFixed(1)}% modified.`
|
|
129
|
+
};
|
|
130
|
+
}
|