nothumanallowed 14.2.5 → 14.2.6
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/package.json +1 -1
- package/src/constants.mjs +1 -1
- package/src/server/routes/webcraft.mjs +87 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "14.2.
|
|
3
|
+
"version": "14.2.6",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/constants.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
|
|
8
|
-
export const VERSION = '14.2.
|
|
8
|
+
export const VERSION = '14.2.6';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -848,20 +848,17 @@ function isFileTruncated(content, filename) {
|
|
|
848
848
|
const trimmed = content.trimEnd();
|
|
849
849
|
const ext = filename.split('.').pop()?.toLowerCase();
|
|
850
850
|
|
|
851
|
-
// Common truncation signs: ends mid-statement without closing bracket/brace
|
|
852
851
|
if (ext === 'js' || ext === 'mjs' || ext === 'ts') {
|
|
853
|
-
// Balanced braces check (fast approximation)
|
|
854
852
|
const open = (trimmed.match(/\{/g) || []).length;
|
|
855
853
|
const close = (trimmed.match(/\}/g) || []).length;
|
|
856
|
-
if (open > close
|
|
857
|
-
// Last meaningful line should not end with an operator or comma
|
|
854
|
+
if (open > close) return true; // ANY unbalanced brace = truncated
|
|
858
855
|
const lastLine = trimmed.split('\n').pop()?.trim() ?? '';
|
|
859
856
|
if (/[,({=+\-*/<>|&]$/.test(lastLine)) return true;
|
|
860
857
|
}
|
|
861
858
|
if (ext === 'css') {
|
|
862
859
|
const open = (trimmed.match(/\{/g) || []).length;
|
|
863
860
|
const close = (trimmed.match(/\}/g) || []).length;
|
|
864
|
-
if (open > close
|
|
861
|
+
if (open > close) return true;
|
|
865
862
|
}
|
|
866
863
|
if (ext === 'html') {
|
|
867
864
|
if (!trimmed.includes('</html>') && !trimmed.includes('</body>')) return true;
|
|
@@ -872,6 +869,69 @@ function isFileTruncated(content, filename) {
|
|
|
872
869
|
return false;
|
|
873
870
|
}
|
|
874
871
|
|
|
872
|
+
/**
|
|
873
|
+
* Deterministic repair of common truncation artifacts.
|
|
874
|
+
* Adds missing closing braces, tags, etc. WITHOUT calling the LLM.
|
|
875
|
+
*/
|
|
876
|
+
function repairTruncation(content, filename) {
|
|
877
|
+
if (!content) return content;
|
|
878
|
+
const ext = filename.split('.').pop()?.toLowerCase();
|
|
879
|
+
let result = content.trimEnd();
|
|
880
|
+
|
|
881
|
+
if (ext === 'js' || ext === 'mjs' || ext === 'ts') {
|
|
882
|
+
// Balance curly braces
|
|
883
|
+
const open = (result.match(/\{/g) || []).length;
|
|
884
|
+
const close = (result.match(/\}/g) || []).length;
|
|
885
|
+
const missing = open - close;
|
|
886
|
+
if (missing > 0 && missing <= 10) {
|
|
887
|
+
result += '\n' + '}\n'.repeat(missing);
|
|
888
|
+
}
|
|
889
|
+
// Balance parentheses
|
|
890
|
+
const openP = (result.match(/\(/g) || []).length;
|
|
891
|
+
const closeP = (result.match(/\)/g) || []).length;
|
|
892
|
+
const missingP = openP - closeP;
|
|
893
|
+
if (missingP > 0 && missingP <= 5) {
|
|
894
|
+
// Find last line and append
|
|
895
|
+
const lines = result.split('\n');
|
|
896
|
+
const lastIdx = lines.length - 1;
|
|
897
|
+
lines[lastIdx] = lines[lastIdx] + ')'.repeat(missingP) + ';';
|
|
898
|
+
result = lines.join('\n');
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
if (ext === 'css') {
|
|
903
|
+
const open = (result.match(/\{/g) || []).length;
|
|
904
|
+
const close = (result.match(/\}/g) || []).length;
|
|
905
|
+
const missing = open - close;
|
|
906
|
+
if (missing > 0 && missing <= 10) {
|
|
907
|
+
result += '\n' + '}\n'.repeat(missing);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
if (ext === 'html' || ext === 'htm') {
|
|
912
|
+
if (!result.includes('</body>')) result += '\n</body>';
|
|
913
|
+
if (!result.includes('</html>')) result += '\n</html>';
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
if (ext === 'json') {
|
|
917
|
+
// Try to fix unclosed JSON
|
|
918
|
+
try { JSON.parse(result); } catch {
|
|
919
|
+
// Count brackets
|
|
920
|
+
const openB = (result.match(/\[/g) || []).length;
|
|
921
|
+
const closeB = (result.match(/\]/g) || []).length;
|
|
922
|
+
const openC = (result.match(/\{/g) || []).length;
|
|
923
|
+
const closeC = (result.match(/\}/g) || []).length;
|
|
924
|
+
// Remove trailing comma if present
|
|
925
|
+
result = result.replace(/,\s*$/, '');
|
|
926
|
+
// Add missing closers
|
|
927
|
+
if (openB > closeB) result += '\n' + ']'.repeat(openB - closeB);
|
|
928
|
+
if (openC > closeC) result += '\n' + '}'.repeat(openC - closeC);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
return result;
|
|
933
|
+
}
|
|
934
|
+
|
|
875
935
|
async function runGenerate(config, projectName, description, blocks, authFields, emit) {
|
|
876
936
|
const blocksDesc = Object.entries(blocks)
|
|
877
937
|
.filter(([, enabled]) => enabled)
|
|
@@ -1058,7 +1118,8 @@ Continue from here:`;
|
|
|
1058
1118
|
}
|
|
1059
1119
|
}
|
|
1060
1120
|
|
|
1061
|
-
|
|
1121
|
+
// Deterministic repair of truncation artifacts (missing braces, tags)
|
|
1122
|
+
fileContent = repairTruncation(rawOutput, fileSpec.name);
|
|
1062
1123
|
syntaxError = null;
|
|
1063
1124
|
|
|
1064
1125
|
const fileTokensOut = countTokens(fileContent);
|
|
@@ -1109,10 +1170,27 @@ Continue from here:`;
|
|
|
1109
1170
|
return isFileTruncated(f.content, f.name);
|
|
1110
1171
|
});
|
|
1111
1172
|
|
|
1112
|
-
if (brokenFiles.length > 0
|
|
1173
|
+
if (brokenFiles.length > 0) {
|
|
1113
1174
|
emit({ type: 'phase', phase: 'autofix', msg: `Post-generation fix: ${brokenFiles.length} file(s) need repair...` });
|
|
1114
1175
|
for (const broken of brokenFiles) {
|
|
1115
1176
|
try {
|
|
1177
|
+
// Step 1: Try deterministic repair first (fast, no LLM call)
|
|
1178
|
+
const deterministicFix = repairTruncation(broken.content, broken.name);
|
|
1179
|
+
let isFixed = false;
|
|
1180
|
+
if (deterministicFix !== broken.content) {
|
|
1181
|
+
// Verify the fix actually resolved the issue
|
|
1182
|
+
if (!isFileTruncated(deterministicFix, broken.name)) {
|
|
1183
|
+
emit({ type: 'status', msg: `Auto-fixed ${broken.name} (deterministic repair)` });
|
|
1184
|
+
broken.content = deterministicFix;
|
|
1185
|
+
const abs = path.join(projectDir, broken.name);
|
|
1186
|
+
fs.writeFileSync(abs, deterministicFix, 'utf-8');
|
|
1187
|
+
isFixed = true;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
if (isFixed) continue;
|
|
1191
|
+
|
|
1192
|
+
// Step 2: If deterministic repair wasn't enough, regenerate with LLM
|
|
1193
|
+
if (brokenFiles.length > 15) continue; // don't LLM-regenerate too many files
|
|
1116
1194
|
emit({ type: 'status', msg: `Regenerating ${broken.name}...` });
|
|
1117
1195
|
let fixedContent = '';
|
|
1118
1196
|
const fixPrompt = `Regenerate this file COMPLETELY. It was truncated or has errors.\n\nFile: ${broken.name}\nProject: ${projectName}\nDescription: ${description}\nFull file list: ${allFileNames}\n\nOutput the COMPLETE file content only, no explanation.`;
|
|
@@ -1121,7 +1199,8 @@ Continue from here:`;
|
|
|
1121
1199
|
}, { max_tokens: 16384 });
|
|
1122
1200
|
fixedContent = fixedContent
|
|
1123
1201
|
.replace(/^```[\w]*\n/, '').replace(/\n```$/, '').trim();
|
|
1124
|
-
|
|
1202
|
+
fixedContent = repairTruncation(fixedContent, broken.name); // repair the regenerated content too
|
|
1203
|
+
if (fixedContent.length > 50) {
|
|
1125
1204
|
broken.content = fixedContent;
|
|
1126
1205
|
const abs = path.join(projectDir, broken.name);
|
|
1127
1206
|
fs.writeFileSync(abs, fixedContent, 'utf-8');
|