nothumanallowed 13.5.53 → 13.5.55
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/commands/ui.mjs +114 -14
- package/src/constants.mjs +1 -1
- package/src/services/web-ui.mjs +13 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.5.
|
|
3
|
+
"version": "13.5.55",
|
|
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/commands/ui.mjs
CHANGED
|
@@ -3913,11 +3913,59 @@ ${completedHeadings ? `## SECTIONS ALREADY WRITTEN (headings only):\n${completed
|
|
|
3913
3913
|
// Write meta
|
|
3914
3914
|
const meta = { projectName: projName, description: body.description || '', files: (body.files || []).map(f => f.name), createdAt: new Date().toISOString() };
|
|
3915
3915
|
fs.writeFileSync(path.join(projDir, 'webcraft-meta.json'), JSON.stringify(meta, null, 2), 'utf8');
|
|
3916
|
+
// Write agent memory file — always included as first context for WebCraft Agent
|
|
3917
|
+
const agentMd = [
|
|
3918
|
+
'# WebCraft Agent Memory — ' + projName,
|
|
3919
|
+
'> Auto-generated. Edit to give the agent persistent context about this project.',
|
|
3920
|
+
'',
|
|
3921
|
+
'## Progetto',
|
|
3922
|
+
'- **Nome**: ' + projName,
|
|
3923
|
+
'- **Descrizione**: ' + (body.description || ''),
|
|
3924
|
+
'- **Creato**: ' + new Date().toISOString(),
|
|
3925
|
+
'',
|
|
3926
|
+
'## Stack',
|
|
3927
|
+
'- Server: Express.js (Node.js)',
|
|
3928
|
+
'- Auth: JWT (access 15min, refresh 7d httpOnly cookie) + bcryptjs cost 12',
|
|
3929
|
+
'- DB: PostgreSQL (sandbox: in-memory shim in server/db.js)',
|
|
3930
|
+
'- CSS: BEM methodology',
|
|
3931
|
+
'- Security: helmet, express-rate-limit, custom sentinel WAF middleware',
|
|
3932
|
+
'',
|
|
3933
|
+
'## File principali',
|
|
3934
|
+
(body.files || []).map(f => '- ' + f.name).join('\n'),
|
|
3935
|
+
'',
|
|
3936
|
+
'## Note agente',
|
|
3937
|
+
'(aggiungi qui note per sessioni future — es. "usa Tailwind invece di BEM", "API key in .env.local")',
|
|
3938
|
+
].join('\n');
|
|
3939
|
+
fs.writeFileSync(path.join(projDir, 'webcraft-agent.md'), agentMd, 'utf8');
|
|
3916
3940
|
sendJSON(res, 200, { ok: true, dir: projDir });
|
|
3917
3941
|
logRequest(method, pathname, 200, Date.now() - start);
|
|
3918
3942
|
return;
|
|
3919
3943
|
}
|
|
3920
3944
|
|
|
3945
|
+
// POST /api/studio/webcraft/projects/chat/save { projectName, chat[] }
|
|
3946
|
+
if (pathname === '/api/studio/webcraft/projects/chat/save' && method === 'POST') {
|
|
3947
|
+
const body = await parseBody(req, 2 * 1024 * 1024);
|
|
3948
|
+
const projName = (body.projectName || '').replace(/[^a-zA-Z0-9_-]/g, '-');
|
|
3949
|
+
if (!projName) { sendJSON(res, 400, { error: 'projectName required' }); return; }
|
|
3950
|
+
const projDir = path.join(os.homedir(), '.nha', 'webcraft', projName);
|
|
3951
|
+
fs.mkdirSync(projDir, { recursive: true });
|
|
3952
|
+
fs.writeFileSync(path.join(projDir, 'webcraft-chat.json'), JSON.stringify(body.chat || [], null, 2), 'utf8');
|
|
3953
|
+
sendJSON(res, 200, { ok: true });
|
|
3954
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
3955
|
+
return;
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
// GET /api/studio/webcraft/projects/chat/load/:name
|
|
3959
|
+
if (pathname.startsWith('/api/studio/webcraft/projects/chat/load/') && method === 'GET') {
|
|
3960
|
+
const projName = decodeURIComponent(pathname.replace('/api/studio/webcraft/projects/chat/load/', '')).replace(/[^a-zA-Z0-9_-]/g, '');
|
|
3961
|
+
const chatPath = path.join(os.homedir(), '.nha', 'webcraft', projName, 'webcraft-chat.json');
|
|
3962
|
+
if (!fs.existsSync(chatPath)) { sendJSON(res, 200, { chat: [] }); return; }
|
|
3963
|
+
const chat = JSON.parse(fs.readFileSync(chatPath, 'utf8'));
|
|
3964
|
+
sendJSON(res, 200, { chat });
|
|
3965
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
3966
|
+
return;
|
|
3967
|
+
}
|
|
3968
|
+
|
|
3921
3969
|
// GET /api/studio/webcraft/projects/load/:name → { projectName, description, files[] }
|
|
3922
3970
|
if (pathname.startsWith('/api/studio/webcraft/projects/load/') && method === 'GET') {
|
|
3923
3971
|
const projName = decodeURIComponent(pathname.replace('/api/studio/webcraft/projects/load/', '')).replace(/[^a-zA-Z0-9_-]/g, '');
|
|
@@ -4147,6 +4195,13 @@ module.exports = { get, set, del, exists };
|
|
|
4147
4195
|
[/require\(['"]\.\/config\/database['"]\)/g, "require('./db')"],
|
|
4148
4196
|
[/require\(['"]\.\.\/config\/db['"]\)/g, "require('../db')"],
|
|
4149
4197
|
[/require\(['"]\.\/config\/db['"]\)/g, "require('./db')"],
|
|
4198
|
+
// email utils: LLM puts utils/email but file is in services/email
|
|
4199
|
+
[/require\(['"]\.\.\/utils\/email['"]\)/g, "require('../services/email')"],
|
|
4200
|
+
[/require\(['"]\.\/utils\/email['"]\)/g, "require('./services/email')"],
|
|
4201
|
+
// config module: LLM generates require('../../config') or require('../config')
|
|
4202
|
+
[/require\(['"]\.\.\/\.\.\/config['"]\)/g, "{env:process.env}"],
|
|
4203
|
+
[/require\(['"]\.\.\/config['"]\)/g, "{env:process.env}"],
|
|
4204
|
+
[/require\(['"]\.\/config['"]\)/g, "{env:process.env}"],
|
|
4150
4205
|
];
|
|
4151
4206
|
function patchJsFiles(dir) {
|
|
4152
4207
|
if (!fs.existsSync(dir)) return;
|
|
@@ -4350,6 +4405,10 @@ module.exports = { get, set, del, exists };
|
|
|
4350
4405
|
const sendEv = (data) => { try { res.write(`data: ${JSON.stringify(data)}\n\n`); } catch {} };
|
|
4351
4406
|
|
|
4352
4407
|
try {
|
|
4408
|
+
// Always read agent memory file first if present
|
|
4409
|
+
const agentMemoryPath = path.join(sandboxDir, 'webcraft-agent.md');
|
|
4410
|
+
const agentMemory = fs.existsSync(agentMemoryPath) ? fs.readFileSync(agentMemoryPath, 'utf8') : '';
|
|
4411
|
+
|
|
4353
4412
|
// Read all project files to give agent full context
|
|
4354
4413
|
const allFiles = [];
|
|
4355
4414
|
if (fs.existsSync(sandboxDir)) {
|
|
@@ -4369,9 +4428,36 @@ module.exports = { get, set, del, exists };
|
|
|
4369
4428
|
walkDir(sandboxDir, '');
|
|
4370
4429
|
}
|
|
4371
4430
|
|
|
4372
|
-
const fileContext = allFiles.map(f => `### FILE: ${f.name}\n\`\`\`\n${f.content}\n\`\`\``).join('\n\n');
|
|
4373
4431
|
const fileList = allFiles.map(f => f.name).join('\n');
|
|
4374
4432
|
|
|
4433
|
+
// Smart file selection: include only relevant files to avoid overflowing context window.
|
|
4434
|
+
// Always include: package.json, server/index.js, .env.example (structure/deps context).
|
|
4435
|
+
// Also include: any file whose name appears in the user message, plus error-related files.
|
|
4436
|
+
const msgLower = (message || '').toLowerCase();
|
|
4437
|
+
const alwaysInclude = ['package.json', 'server/index.js', '.env.example'];
|
|
4438
|
+
const relevantFiles = allFiles.filter(f => {
|
|
4439
|
+
const nameLower = f.name.toLowerCase();
|
|
4440
|
+
if (alwaysInclude.some(a => nameLower.endsWith(a))) return true;
|
|
4441
|
+
// Include if file name or path fragment mentioned in message
|
|
4442
|
+
const parts = f.name.split('/');
|
|
4443
|
+
if (parts.some(p => msgLower.includes(p.replace(/\.[^.]+$/, '').toLowerCase()))) return true;
|
|
4444
|
+
// Include server JS files if message mentions "error", "errore", "fix", "crash", "module"
|
|
4445
|
+
if (/errore|error|fix|crash|module|require|import/i.test(message) && f.name.startsWith('server/') && f.name.endsWith('.js')) return true;
|
|
4446
|
+
return false;
|
|
4447
|
+
});
|
|
4448
|
+
// Cap total context at ~24KB to stay within 7B model limits
|
|
4449
|
+
let contextBudget = 24 * 1024;
|
|
4450
|
+
const selectedFiles = [];
|
|
4451
|
+
for (const f of relevantFiles) {
|
|
4452
|
+
if (contextBudget <= 0) break;
|
|
4453
|
+
selectedFiles.push(f);
|
|
4454
|
+
contextBudget -= f.content.length;
|
|
4455
|
+
}
|
|
4456
|
+
const fileContext = selectedFiles.map(f => `### FILE: ${f.name}\n\`\`\`\n${f.content}\n\`\`\``).join('\n\n');
|
|
4457
|
+
const includedNote = selectedFiles.length < allFiles.length
|
|
4458
|
+
? `\n(Showing ${selectedFiles.length}/${allFiles.length} files most relevant to the request. Ask to read others explicitly.)`
|
|
4459
|
+
: '';
|
|
4460
|
+
|
|
4375
4461
|
// Build attachment context (images/PDF)
|
|
4376
4462
|
const attachCtx = (attachments || []).map((a, i) => `[Allegato ${i+1}: ${a.name || 'file'}, type=${a.mimeType}]`).join('\n');
|
|
4377
4463
|
|
|
@@ -4380,7 +4466,7 @@ Il tuo lavoro e di correggere, migliorare ed espandere il codice del progetto sa
|
|
|
4380
4466
|
|
|
4381
4467
|
PROGETTO ATTIVO: ${projectName}
|
|
4382
4468
|
PERCORSO: ${sandboxDir}
|
|
4383
|
-
|
|
4469
|
+
${agentMemory ? '\nMEMORIA PROGETTO:\n' + agentMemory + '\n' : ''}
|
|
4384
4470
|
FILE DISPONIBILI:
|
|
4385
4471
|
${fileList}
|
|
4386
4472
|
|
|
@@ -4405,23 +4491,37 @@ REGOLE CRITICHE:
|
|
|
4405
4491
|
- Se usi immagini allegate, descrivile e usale come contesto per il fix`;
|
|
4406
4492
|
|
|
4407
4493
|
const userMsg = message + (attachCtx ? '\n\nAllegati:\n' + attachCtx : '') +
|
|
4408
|
-
'\n\n--- CONTENUTO FILE
|
|
4494
|
+
'\n\n--- CONTENUTO FILE ---' + includedNote + '\n' + fileContext;
|
|
4409
4495
|
|
|
4410
4496
|
// Call LLM - stream tokens
|
|
4411
4497
|
let fullResponse = '';
|
|
4412
4498
|
const visionAttachments = (attachments || []).filter(a => a.base64 && (a.mimeType || '').startsWith('image/'));
|
|
4413
4499
|
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4500
|
+
try {
|
|
4501
|
+
if (visionAttachments.length > 0) {
|
|
4502
|
+
const { callLLMVision } = await import('../services/llm.mjs');
|
|
4503
|
+
const va = visionAttachments[0];
|
|
4504
|
+
fullResponse = await callLLMVision(config, systemPrompt, userMsg, { base64: va.base64, mimeType: va.mimeType });
|
|
4505
|
+
sendEv({ type: 'text', token: fullResponse });
|
|
4506
|
+
} else {
|
|
4507
|
+
await callLLMStream(config, systemPrompt, userMsg, (token) => {
|
|
4508
|
+
fullResponse += token;
|
|
4509
|
+
sendEv({ type: 'text', token });
|
|
4510
|
+
}, { max_tokens: 4096 });
|
|
4511
|
+
}
|
|
4512
|
+
} catch (llmErr) {
|
|
4513
|
+
const errMsg = llmErr.message || String(llmErr);
|
|
4514
|
+
// Surface a clean message instead of raw HTML
|
|
4515
|
+
if (errMsg.includes('502') || errMsg.includes('Bad Gateway')) {
|
|
4516
|
+
sendEv({ type: 'text', token: 'Liara non disponibile al momento (502). Riprova tra qualche secondo, oppure configura una tua API key con: nha config set provider anthropic && nha config set key sk-...' });
|
|
4517
|
+
} else if (errMsg.includes('429') || errMsg.includes('rate limit')) {
|
|
4518
|
+
sendEv({ type: 'text', token: 'Rate limit raggiunto su Liara (max 3 auto-fix per 5 minuti con il piano free). Attendi qualche minuto o usa una tua API key.' });
|
|
4519
|
+
} else {
|
|
4520
|
+
sendEv({ type: 'text', token: 'Errore LLM: ' + errMsg.slice(0, 200) });
|
|
4521
|
+
}
|
|
4522
|
+
sendEv({ type: 'done', changed: false, toolCount: 0 });
|
|
4523
|
+
res.end();
|
|
4524
|
+
return;
|
|
4425
4525
|
}
|
|
4426
4526
|
|
|
4427
4527
|
// Parse and execute tool calls from response
|
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 = '13.5.
|
|
8
|
+
export const VERSION = '13.5.55';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -6572,10 +6572,13 @@ async function wcChatSend() {
|
|
|
6572
6572
|
}
|
|
6573
6573
|
} else if (ev.type === 'done') {
|
|
6574
6574
|
wcChatRunning = false;
|
|
6575
|
-
if (ev.changed) {
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6575
|
+
if (ev.changed) { wcReloadProjectFiles(); }
|
|
6576
|
+
// Persist chat to disk
|
|
6577
|
+
fetch(API + '/api/studio/webcraft/projects/chat/save', {
|
|
6578
|
+
method: 'POST',
|
|
6579
|
+
headers: {'Content-Type':'application/json'},
|
|
6580
|
+
body: JSON.stringify({ projectName: wcState.projectName, chat: wcChat })
|
|
6581
|
+
}).catch(function(){});
|
|
6579
6582
|
} else if (ev.type === 'restart_sandbox') {
|
|
6580
6583
|
wcStartSandbox();
|
|
6581
6584
|
} else if (ev.type === 'error') {
|
|
@@ -6731,7 +6734,13 @@ async function wcLoadProject(pi) {
|
|
|
6731
6734
|
wcState.activeFile = 0;
|
|
6732
6735
|
wcMainTab = 'new';
|
|
6733
6736
|
wcRightTab = 'files';
|
|
6737
|
+
// Load persisted chat history
|
|
6738
|
+
try {
|
|
6739
|
+
var cr = await fetch(API + '/api/studio/webcraft/projects/chat/load/' + encodeURIComponent(wcState.projectName));
|
|
6740
|
+
if (cr.ok) { var cd = await cr.json(); wcChat = cd.chat || []; }
|
|
6741
|
+
} catch(_) { wcChat = []; }
|
|
6734
6742
|
renderWebCraft(document.getElementById('content'));
|
|
6743
|
+
wcScrollChatToBottom();
|
|
6735
6744
|
}
|
|
6736
6745
|
|
|
6737
6746
|
async function wcDeleteProject(pi) {
|