nothumanallowed 14.2.5 → 14.2.7
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/cli.mjs +23 -3
- package/src/constants.mjs +1 -1
- package/src/server/routes/webcraft.mjs +87 -8
- package/src/services/tool-executor.mjs +6 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "14.2.
|
|
3
|
+
"version": "14.2.7",
|
|
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/cli.mjs
CHANGED
|
@@ -200,14 +200,34 @@ export async function main(argv) {
|
|
|
200
200
|
return cmdHelp();
|
|
201
201
|
|
|
202
202
|
default: {
|
|
203
|
-
// Check if a plugin handles this command before falling through
|
|
203
|
+
// Check if a plugin handles this command before falling through
|
|
204
204
|
const pluginMatch = await findPluginForCommand(cmd);
|
|
205
205
|
if (pluginMatch && pluginMatch.plugin.run) {
|
|
206
206
|
const { cmdPlugin: runPlugin } = await import('./commands/plugin.mjs');
|
|
207
207
|
return runPlugin(['run', cmd, ...args]);
|
|
208
208
|
}
|
|
209
|
-
// Try as Legion command passthrough
|
|
210
|
-
|
|
209
|
+
// Try as Legion command passthrough (only if legion is installed)
|
|
210
|
+
try {
|
|
211
|
+
const { LEGION_FILE } = await import('./constants.mjs');
|
|
212
|
+
const { existsSync } = await import('fs');
|
|
213
|
+
if (existsSync(LEGION_FILE)) {
|
|
214
|
+
return spawnCore('legion', [cmd, ...args]);
|
|
215
|
+
}
|
|
216
|
+
} catch {}
|
|
217
|
+
// Unknown command — show helpful error
|
|
218
|
+
fail(`Unknown command: ${cmd}`);
|
|
219
|
+
console.log('');
|
|
220
|
+
info('Common commands:');
|
|
221
|
+
console.log(' nha chat Chat with AI');
|
|
222
|
+
console.log(' nha ui Open web UI');
|
|
223
|
+
console.log(' nha start Start ops daemon (Telegram/Discord)');
|
|
224
|
+
console.log(' nha stop Stop ops daemon');
|
|
225
|
+
console.log(' nha restart Restart ops daemon');
|
|
226
|
+
console.log(' nha status Check daemon status');
|
|
227
|
+
console.log(' nha update Update agents + npm package');
|
|
228
|
+
console.log(' nha config set <k> <v> Set configuration');
|
|
229
|
+
console.log(' nha help Full command list');
|
|
230
|
+
return;
|
|
211
231
|
}
|
|
212
232
|
}
|
|
213
233
|
}
|
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.7';
|
|
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');
|
|
@@ -161,8 +161,10 @@ TOOLS:
|
|
|
161
161
|
NEVER use calendar_find with month names — use calendar_month instead.
|
|
162
162
|
|
|
163
163
|
14. calendar_create(summary: string, start: string, end: string, attendees?: string[], description?: string)
|
|
164
|
-
Create a calendar event. start/end are ISO 8601 datetime strings.
|
|
165
|
-
|
|
164
|
+
Create a NEW calendar event. start/end are ISO 8601 datetime strings.
|
|
165
|
+
Use this when the user says: "inserisci", "aggiungi", "crea", "metti", "fissa", "prenota", "add", "create", "schedule", "book".
|
|
166
|
+
IMPORTANT: When user says "inserisci appuntamento" or "crea evento" → use calendar_create, NOT calendar_find.
|
|
167
|
+
Extract the summary, date, and time from the user message. If end time is not specified, default to 1 hour after start.
|
|
166
168
|
|
|
167
169
|
15. calendar_move(eventId: string, newStart: string, newEnd: string)
|
|
168
170
|
Reschedule an event. ALWAYS confirm before moving.
|
|
@@ -625,7 +627,8 @@ RULES:
|
|
|
625
627
|
- For write/send/delete operations (gmail_send, gmail_reply, gmail_delete, calendar_create, calendar_move, calendar_update, contact_delete, task_done, notify_remind, file_write), DESCRIBE what you're about to do and include the JSON block so the system can ask the user for confirmation.
|
|
626
628
|
- For schedule_meeting and schedule_draft_email, execute immediately — these are read operations that suggest slots.
|
|
627
629
|
- When presenting email results, show From, Subject, Date, and a brief snippet. Never dump raw JSON.
|
|
628
|
-
- When presenting calendar events, show Time, Title, Location/Link. Format times in a human-readable way.
|
|
630
|
+
- When presenting calendar events, show Time, Title, Location/Link. Format times in a human-readable way. NEVER show raw eventId to the user — it's internal.
|
|
631
|
+
- When confirming a created event, say something like "Ho creato l'appuntamento 'X' per il giorno Y alle ore Z." — natural, human, no IDs.
|
|
629
632
|
- When presenting tasks, show ID, Description, Priority, Status.
|
|
630
633
|
- When presenting slot proposals, show day, date, time range, and travel info clearly.
|
|
631
634
|
- If you need multiple actions in sequence (e.g., read an email then reply), do them ONE AT A TIME — wait for the result of each before proceeding.
|