nothumanallowed 14.2.7 → 14.3.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/package.json +1 -1
- package/src/constants.mjs +1 -1
- package/src/server/routes/webcraft.mjs +41 -18
- package/src/ui-dist/assets/{index-DJhcU-P3.css → index-CVtta8J-.css} +1 -1
- package/src/ui-dist/assets/index-QFXhoDBT.js +744 -0
- package/src/ui-dist/index.html +2 -2
- package/src/ui-dist/assets/index-CbMkl-R7.js +0 -736
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.3.0",
|
|
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.
|
|
8
|
+
export const VERSION = '14.3.0';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -826,19 +826,6 @@ function countTokens(text) {
|
|
|
826
826
|
return Math.ceil((text || '').length / 4);
|
|
827
827
|
}
|
|
828
828
|
|
|
829
|
-
/**
|
|
830
|
-
* Simulated streaming: for providers that return full text at once (NHA/Liara),
|
|
831
|
-
* we split the text into ~20-char chunks and emit them with setImmediate gaps
|
|
832
|
-
* so the browser receives a real byte-by-byte stream over SSE.
|
|
833
|
-
*/
|
|
834
|
-
async function emitTextAsStream(text, onChunk) {
|
|
835
|
-
const CHUNK_SIZE = 20;
|
|
836
|
-
for (let i = 0; i < text.length; i += CHUNK_SIZE) {
|
|
837
|
-
onChunk(text.slice(i, i + CHUNK_SIZE));
|
|
838
|
-
await new Promise((r) => setImmediate(r));
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
|
|
842
829
|
/**
|
|
843
830
|
* Detects if an LLM output appears to be truncated / incomplete.
|
|
844
831
|
* Returns true if the file likely needs a continuation call.
|
|
@@ -932,7 +919,33 @@ function repairTruncation(content, filename) {
|
|
|
932
919
|
return result;
|
|
933
920
|
}
|
|
934
921
|
|
|
935
|
-
|
|
922
|
+
/** Severe truncation = needs LLM continuation. Minor diffs handled by repairTruncation(). */
|
|
923
|
+
function _isSeverelyTruncated(content, filename) {
|
|
924
|
+
if (!content || content.length < 10) return true;
|
|
925
|
+
const trimmed = content.trimEnd();
|
|
926
|
+
const ext = filename.split('.').pop()?.toLowerCase();
|
|
927
|
+
// Last line ends mid-expression (clear cut-off)
|
|
928
|
+
const lastLine = trimmed.split('\n').pop()?.trim() ?? '';
|
|
929
|
+
if (/[,({=+\-*/<>|&:]$/.test(lastLine) && lastLine.length > 3) return true;
|
|
930
|
+
// Large brace imbalance (>3) — repairTruncation can't reliably fix this
|
|
931
|
+
if (ext === 'js' || ext === 'mjs' || ext === 'ts') {
|
|
932
|
+
const diff = (trimmed.match(/\{/g) || []).length - (trimmed.match(/\}/g) || []).length;
|
|
933
|
+
if (diff > 3) return true;
|
|
934
|
+
}
|
|
935
|
+
if (ext === 'css') {
|
|
936
|
+
const diff = (trimmed.match(/\{/g) || []).length - (trimmed.match(/\}/g) || []).length;
|
|
937
|
+
if (diff > 3) return true;
|
|
938
|
+
}
|
|
939
|
+
// HTML completely missing closing tags (not just </html> — entire sections missing)
|
|
940
|
+
if (ext === 'html' && !trimmed.includes('</body>') && !trimmed.includes('</html>') && trimmed.length > 500) {
|
|
941
|
+
// Check if it ends mid-tag
|
|
942
|
+
const lastAngle = trimmed.lastIndexOf('<');
|
|
943
|
+
if (lastAngle > trimmed.lastIndexOf('>')) return true; // mid-tag
|
|
944
|
+
}
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
async function runGenerate(config, projectName, description, blocks, authFields, emit, abortSignal) {
|
|
936
949
|
const blocksDesc = Object.entries(blocks)
|
|
937
950
|
.filter(([, enabled]) => enabled)
|
|
938
951
|
.map(([key]) => key)
|
|
@@ -1079,6 +1092,12 @@ ${prevContext ? `Recent files generated (for consistency):\n${prevContext}\n\n`
|
|
|
1079
1092
|
let syntaxError = null;
|
|
1080
1093
|
const fileTokensIn = countTokens(fileSys) + countTokens(filePrompt);
|
|
1081
1094
|
|
|
1095
|
+
// Check abort before each file
|
|
1096
|
+
if (abortSignal?.aborted) {
|
|
1097
|
+
emit({ type: 'status', msg: 'Generation stopped by user.' });
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1082
1101
|
// Retry loop — up to 2 attempts per file
|
|
1083
1102
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1084
1103
|
try {
|
|
@@ -1095,8 +1114,8 @@ ${prevContext ? `Recent files generated (for consistency):\n${prevContext}\n\n`
|
|
|
1095
1114
|
.replace(/^```[\w]*\n/, '').replace(/\n```$/, '')
|
|
1096
1115
|
.replace(/^```[\w]*\r\n/, '').replace(/\r\n```$/, '').trim();
|
|
1097
1116
|
|
|
1098
|
-
// Continuation loop —
|
|
1099
|
-
for (let contRound = 0; contRound <
|
|
1117
|
+
// Continuation loop — only for SEVERE truncation (>3 missing braces or mid-line cut)
|
|
1118
|
+
for (let contRound = 0; contRound < 2 && !abortSignal?.aborted && _isSeverelyTruncated(rawOutput, fileSpec.name); contRound++) {
|
|
1100
1119
|
emit({ type: 'file_chunk', name: fileSpec.name, chunk: `\n/* ... continuing (${contRound + 1}) ... */\n`, fi: fi + 1, total: filePlan.length });
|
|
1101
1120
|
const contPrompt = `The file ${fileSpec.name} was truncated. Continue EXACTLY from the last line. Output ONLY the remaining code (no repetition, no explanation):
|
|
1102
1121
|
|
|
@@ -1325,10 +1344,14 @@ export function register(router) {
|
|
|
1325
1344
|
if (!projectName || !description) return sendError(res, 400, 'projectName and description required');
|
|
1326
1345
|
|
|
1327
1346
|
const sse = sendSSE(res);
|
|
1347
|
+
// Abort signal: triggered when client disconnects (user clicks Stop)
|
|
1348
|
+
const ac = new AbortController();
|
|
1349
|
+
req.on('close', () => ac.abort());
|
|
1350
|
+
res.on('close', () => ac.abort());
|
|
1328
1351
|
try {
|
|
1329
|
-
await runGenerate(config, projectName, description, blocks, authFields, sse.send);
|
|
1352
|
+
await runGenerate(config, projectName, description, blocks, authFields, sse.send, ac.signal);
|
|
1330
1353
|
} catch (e) {
|
|
1331
|
-
sse.send({ type: 'error', msg: e.message });
|
|
1354
|
+
if (e.name !== 'AbortError') sse.send({ type: 'error', msg: e.message });
|
|
1332
1355
|
}
|
|
1333
1356
|
sse.end();
|
|
1334
1357
|
});
|