nothumanallowed 13.5.47 → 13.5.49

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "13.5.47",
3
+ "version": "13.5.49",
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": {
@@ -4067,7 +4067,83 @@ async function transaction(cb) {
4067
4067
  module.exports = { pool, query, transaction };
4068
4068
  `;
4069
4069
  fs.writeFileSync(path.join(sandboxDir, 'server', 'db.js'), dbShim, 'utf8');
4070
- sendLog('🔧 DB shim iniettato (in-memory, no PostgreSQL richiesto)');
4070
+
4071
+ // Sentinel shim — zero external dependencies (no 'ip', 'net', etc.)
4072
+ const sentinelShim = `
4073
+ // NHA WebCraft Sandbox — Sentinel WAF shim (zero dependencies)
4074
+ const _ipWindows = {};
4075
+ function getIP(req) {
4076
+ return (req.headers['x-forwarded-for'] || '').split(',')[0].trim() || req.socket.remoteAddress || '127.0.0.1';
4077
+ }
4078
+ const SQL_RE = new RegExp('(union[\\\\s\\\\S]+select|drop[\\\\s]+table|insert[\\\\s]+into|delete[\\\\s]+from|update[\\\\s]+set|exec[\\\\s]*\\\\(|xp_cmdshell)', 'i');
4079
+ const XSS_RE = new RegExp('(<script|javascript:|onerror=|onload=|eval\\\\()', 'i');
4080
+ const PATH_RE = new RegExp('\\\\.\\\\./');
4081
+ function sentinelMiddleware(req, res, next) {
4082
+ var ip = getIP(req);
4083
+ var now = Date.now();
4084
+ if (!_ipWindows[ip]) _ipWindows[ip] = [];
4085
+ _ipWindows[ip] = _ipWindows[ip].filter(function(t){ return now - t < 60000; });
4086
+ _ipWindows[ip].push(now);
4087
+ if (_ipWindows[ip].length > 120) {
4088
+ process.stderr.write('[sentinel] rate-limit ' + ip + '\\n');
4089
+ return res.status(429).json({ error: 'Too many requests' });
4090
+ }
4091
+ var check = req.url + JSON.stringify(req.body || '');
4092
+ if (SQL_RE.test(check) || XSS_RE.test(check) || PATH_RE.test(check)) {
4093
+ process.stderr.write('[sentinel] blocked ' + ip + ' ' + req.method + ' ' + req.url + '\\n');
4094
+ return res.status(400).json({ error: 'Request blocked' });
4095
+ }
4096
+ next();
4097
+ }
4098
+ module.exports = { sentinelMiddleware };
4099
+ `;
4100
+ fs.mkdirSync(path.join(sandboxDir, 'server', 'middleware'), { recursive: true });
4101
+ fs.writeFileSync(path.join(sandboxDir, 'server', 'middleware', 'sentinel.js'), sentinelShim, 'utf8');
4102
+
4103
+ // Cache shim — zero dependencies (no ioredis), pure in-memory LRU
4104
+ const cacheShim = `
4105
+ // NHA WebCraft Sandbox — Cache shim (in-memory, no Redis required)
4106
+ const _store = new Map();
4107
+ const _MAX = 1000;
4108
+ function evict() { if (_store.size > _MAX) { _store.delete(_store.keys().next().value); } }
4109
+ async function get(key) { var e = _store.get(key); if (!e) return null; if (e.exp && Date.now() > e.exp) { _store.delete(key); return null; } return e.val; }
4110
+ async function set(key, value, ttlSeconds) { evict(); _store.set(key, { val: value, exp: ttlSeconds ? Date.now() + ttlSeconds * 1000 : null }); }
4111
+ async function del(key) { _store.delete(key); }
4112
+ async function exists(key) { return (await get(key)) !== null; }
4113
+ module.exports = { get, set, del, exists };
4114
+ `;
4115
+ fs.mkdirSync(path.join(sandboxDir, 'server', 'services'), { recursive: true });
4116
+ fs.writeFileSync(path.join(sandboxDir, 'server', 'services', 'cache.js'), cacheShim, 'utf8');
4117
+
4118
+ // Patch all generated JS files: fix known wrong require() names
4119
+ // The LLM often uses 'bcrypt' instead of 'bcryptjs', 'pg' instead of nothing, etc.
4120
+ const requireFixes = [
4121
+ [/require\(['"]bcrypt['"]\)/g, "require('bcryptjs')"],
4122
+ [/require\(['"]node-postgres['"]\)/g, "require('bcryptjs')"],
4123
+ [/require\(['"]pg['"]\)/g, "require('./db')"],
4124
+ [/require\(['"]ioredis['"]\)/g, "require('../services/cache')"],
4125
+ [/require\(['"]redis['"]\)/g, "require('../services/cache')"],
4126
+ [/require\(['"]ip['"]\)/g, "({address:()=>'127.0.0.1'})"],
4127
+ [/require\(['"]express-async-errors['"]\)/g, "{}"],
4128
+ [/require\(['"]multer['"]\)/g, "({single:()=>(r,s,n)=>n(),array:()=>(r,s,n)=>n()})"],
4129
+ ];
4130
+ function patchJsFiles(dir) {
4131
+ if (!fs.existsSync(dir)) return;
4132
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
4133
+ const full = path.join(dir, entry.name);
4134
+ if (entry.isDirectory()) { patchJsFiles(full); continue; }
4135
+ if (!entry.name.endsWith('.js')) continue;
4136
+ let src = fs.readFileSync(full, 'utf8');
4137
+ let changed = false;
4138
+ for (const [pat, rep] of requireFixes) {
4139
+ const next = src.replace(pat, rep);
4140
+ if (next !== src) { src = next; changed = true; }
4141
+ }
4142
+ if (changed) fs.writeFileSync(full, src, 'utf8');
4143
+ }
4144
+ }
4145
+ patchJsFiles(path.join(sandboxDir, 'server'));
4146
+ sendLog('🔧 Shim iniettati: DB (in-memory), Sentinel WAF, Cache — require() patchati');
4071
4147
 
4072
4148
  // Patch package.json to remove pg, add only what's needed
4073
4149
  const pkgPath = path.join(sandboxDir, 'package.json');
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.47';
8
+ export const VERSION = '13.5.49';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -6245,6 +6245,7 @@ var wcState = {
6245
6245
  var wcRightTab = 'files';
6246
6246
  var wcMainTab = 'new'; // 'new' | 'projects'
6247
6247
  var wcProjectsList = []; // cached list from server
6248
+ var wcSandboxExpanded = {}; // { phaseKey: true/false }
6248
6249
 
6249
6250
  function wcEsc(s){return s?String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'):''}
6250
6251
 
@@ -6375,6 +6376,7 @@ function wcPickExample(i) {
6375
6376
  function wcTabFiles() { wcRightTab = 'files'; renderWebCraft(document.getElementById('content')); }
6376
6377
  function wcTabPreview() { wcRightTab = 'preview'; renderWebCraft(document.getElementById('content')); }
6377
6378
  function wcOpenSandbox() { if (wcState.sandbox.port) window.open('http://127.0.0.1:' + wcState.sandbox.port, '_blank'); }
6379
+ function wcTogglePhase(key) { wcSandboxExpanded[key] = !wcSandboxExpanded[key]; renderWebCraft(document.getElementById('content')); }
6378
6380
 
6379
6381
  function wcMainTabNew() { wcMainTab = 'new'; renderWebCraft(document.getElementById('content')); }
6380
6382
  function wcMainTabProjects() {
@@ -6568,38 +6570,127 @@ function wcSandboxPanelHtml() {
6568
6570
  if (!wcState.generatedFiles.length) {
6569
6571
  return '<div style="display:flex;align-items:center;justify-content:center;flex:1;color:var(--dim);font-size:13px">Genera prima il progetto</div>';
6570
6572
  }
6571
- var logsHtml = sb.logs.length
6572
- ? '<div id="wcSbLogs" style="flex:1;overflow-y:auto;padding:10px 14px;font-family:var(--mono);font-size:11px;line-height:1.7;color:var(--dim)">' +
6573
- sb.logs.map(function(l){ return '<div>'+wcEsc(l)+'</div>'; }).join('') +
6574
- '</div>'
6575
- : '<div style="flex:1;display:flex;align-items:center;justify-content:center;color:var(--dim);font-size:12px">Premi "Avvia Sandbox" per avviare il server locale</div>';
6576
6573
 
6574
+ // Server ready — show iframe with top bar
6577
6575
  if (sb.port && !sb.running) {
6578
- // Server ready — show iframe
6579
6576
  return '<div style="display:flex;flex-direction:column;flex:1;min-height:0">' +
6580
- '<div style="display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--border);flex-shrink:0">' +
6581
- '<span style="font-size:11px;color:var(--dim);font-family:var(--mono)">http://127.0.0.1:'+sb.port+'</span>' +
6582
- '<span style="font-size:10px;color:var(--dim)">&#8212; ' + wcEsc(sb.dir || '') + '</span>' +
6583
- '<button onclick="wcStopSandbox()" style="margin-left:auto;padding:3px 10px;background:var(--bg3);border:1px solid var(--border2);border-radius:5px;color:var(--red);font-size:11px;cursor:pointer">&#9632; Ferma</button>' +
6584
- '<button onclick="wcOpenSandbox()" style="padding:3px 10px;background:var(--green3);border:none;border-radius:5px;color:var(--bg);font-size:11px;cursor:pointer;font-weight:700">&#8599; Apri</button>' +
6577
+ '<div style="display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--border);flex-shrink:0;background:var(--bg3)">' +
6578
+ '<span style="width:8px;height:8px;border-radius:50%;background:#22c55e;display:inline-block;flex-shrink:0"></span>' +
6579
+ '<span style="font-size:11px;color:var(--text);font-family:var(--mono);font-weight:600">http://127.0.0.1:'+sb.port+'</span>' +
6580
+ '<span style="font-size:10px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1">'+wcEsc(sb.dir||'')+'</span>' +
6581
+ '<button onclick="wcStopSandbox()" style="padding:3px 10px;background:transparent;border:1px solid var(--border2);border-radius:5px;color:var(--dim);font-size:11px;cursor:pointer;flex-shrink:0">&#9632; Ferma</button>' +
6582
+ '<button onclick="wcOpenSandbox()" style="padding:3px 12px;background:var(--green3);border:none;border-radius:5px;color:var(--bg);font-size:11px;cursor:pointer;font-weight:700;flex-shrink:0">&#8599; Apri nel browser</button>' +
6585
6583
  '</div>' +
6586
6584
  '<iframe src="http://127.0.0.1:'+sb.port+'" style="flex:1;border:none;width:100%;background:#fff" sandbox="allow-scripts allow-forms allow-same-origin allow-popups"></iframe>' +
6587
6585
  '</div>';
6588
6586
  }
6589
6587
 
6588
+ // Pre-launch info panel
6589
+ if (!sb.running && !sb.port && !sb.logs.length) {
6590
+ return '<div style="display:flex;flex-direction:column;flex:1;min-height:0;padding:20px">' +
6591
+ '<div style="background:var(--bg3);border:1px solid var(--border);border-radius:10px;padding:16px;max-width:480px">' +
6592
+ '<div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:12px">&#9654; Cosa succede quando avvii la sandbox:</div>' +
6593
+ '<div style="display:flex;flex-direction:column;gap:8px;font-size:11px;color:var(--dim)">' +
6594
+ '<div style="display:flex;gap:8px;align-items:flex-start"><span style="color:var(--green);flex-shrink:0">1.</span><span>I file vengono scritti in <span style="font-family:var(--mono);color:var(--green)">~/.nha/webcraft/'+wcEsc(wcState.projectName||'project')+'</span></span></div>' +
6595
+ '<div style="display:flex;gap:8px;align-items:flex-start"><span style="color:var(--green);flex-shrink:0">2.</span><span>npm install delle dipendenze (solo in quella cartella)</span></div>' +
6596
+ '<div style="display:flex;gap:8px;align-items:flex-start"><span style="color:var(--green);flex-shrink:0">3.</span><span>Il server Express parte su una porta locale casuale</span></div>' +
6597
+ '<div style="display:flex;gap:8px;align-items:flex-start"><span style="color:var(--green);flex-shrink:0">4.</span><span>DB in-memory (no PostgreSQL richiesto) — i dati si azzerano al riavvio</span></div>' +
6598
+ '</div>' +
6599
+ '<div style="margin-top:12px;padding:8px 10px;background:var(--amberdim);border:1px solid var(--amber3);border-radius:6px;font-size:10px;color:var(--amber)">&#9888; Solo locale — nessun dato esce dal tuo Mac</div>' +
6600
+ '</div>' +
6601
+ '</div>';
6602
+ }
6603
+
6604
+ // Running — show structured phases log
6605
+ var phases = [
6606
+ { key: 'files', label: 'Scrittura file', icon: '&#128196;' },
6607
+ { key: 'shims', label: 'Shim iniettati', icon: '&#128295;' },
6608
+ { key: 'pkg', label: 'package.json', icon: '&#128230;' },
6609
+ { key: 'env', label: '.env sandbox', icon: '&#9881;' },
6610
+ { key: 'deps', label: 'Dipendenze', icon: '&#128230;' },
6611
+ { key: 'install', label: 'npm install', icon: '&#9203;' },
6612
+ { key: 'start', label: 'Avvio server', icon: '&#9654;' },
6613
+ ];
6614
+ var logs = sb.logs;
6615
+ // Classify each log line into a phase
6616
+ function phaseOf(l) {
6617
+ if (!l) return 'start';
6618
+ if (l.indexOf('Scrittura') !== -1 || l.indexOf('file...') !== -1 || l[0] === ' ' && l.indexOf('.') !== -1 && l.indexOf('✓') !== -1) return 'files';
6619
+ if (l.indexOf('Shim') !== -1 || l.indexOf('shim') !== -1 || l.indexOf('DB shim') !== -1) return 'shims';
6620
+ if (l.indexOf('package.json') !== -1) return 'pkg';
6621
+ if (l.indexOf('.env') !== -1) return 'env';
6622
+ if (l.indexOf('Dipendenze') !== -1 || l.indexOf('Percorso:') !== -1 || (l.indexOf('•') !== -1 && l.indexOf('@') !== -1)) return 'deps';
6623
+ if (l.indexOf('npm install') !== -1 || l.indexOf('added') !== -1 || l.indexOf('packages') !== -1 || l.indexOf('npm error') !== -1 || l.indexOf('audit') !== -1 || l.indexOf('funding') !== -1 || l.indexOf('vulnerability') !== -1) return 'install';
6624
+ return 'start';
6625
+ }
6626
+ var byPhase = {};
6627
+ logs.forEach(function(l){ var p = phaseOf(l); if (!byPhase[p]) byPhase[p] = []; byPhase[p].push(l); });
6628
+
6629
+ // Check if a phase is done (next phase has lines)
6630
+ var phaseKeys = phases.map(function(p){ return p.key; });
6631
+ function phaseStatus(pk) {
6632
+ var idx = phaseKeys.indexOf(pk);
6633
+ if (!byPhase[pk] || !byPhase[pk].length) return 'pending';
6634
+ // Done if next phase has started or if error
6635
+ for (var i = idx+1; i < phaseKeys.length; i++) {
6636
+ if (byPhase[phaseKeys[i]] && byPhase[phaseKeys[i]].length) return 'done';
6637
+ }
6638
+ if (sb.error) return 'error';
6639
+ return 'active';
6640
+ }
6641
+
6642
+ var statusColor = { done:'var(--green)', active:'var(--amber)', pending:'var(--dim)', error:'var(--red)' };
6643
+ var statusIcon = { done:'&#10003;', active:'&#9203;', pending:'&#9675;', error:'&#10060;' };
6644
+
6645
+ var phasesHtml = phases.map(function(ph){
6646
+ var st = phaseStatus(ph.key);
6647
+ var lines = byPhase[ph.key] || [];
6648
+ var clean = lines.filter(function(l){ return l.indexOf('npm fund') === -1 && l.indexOf('run ') === -1 && l.indexOf('npm audit') === -1; });
6649
+ var isOpen = !!wcSandboxExpanded[ph.key];
6650
+ var hasContent = clean.length > 0;
6651
+
6652
+ // Summary line (always visible)
6653
+ var summary = '';
6654
+ if (ph.key === 'files') {
6655
+ var cnt = clean.filter(function(l){ return l.indexOf('✓') !== -1; }).length;
6656
+ summary = cnt ? cnt + ' file scritti' : '';
6657
+ } else if (ph.key === 'deps') {
6658
+ var dcnt = clean.filter(function(l){ return l.indexOf('•') !== -1; }).length;
6659
+ summary = dcnt ? dcnt + ' dipendenze' : '';
6660
+ } else if (clean.length > 0) {
6661
+ var last = clean.filter(function(l){ return l.trim(); }).slice(-1)[0] || '';
6662
+ summary = wcEsc(last.trim().slice(0, 60));
6663
+ }
6664
+
6665
+ // Expanded detail — all lines
6666
+ var expandedHtml = '';
6667
+ if (isOpen && hasContent) {
6668
+ expandedHtml = '<div style="margin-top:6px;padding:8px;background:var(--bg);border-radius:6px;max-height:180px;overflow-y:auto">' +
6669
+ clean.map(function(l){
6670
+ var col = l.indexOf('❌') !== -1 || l.indexOf('Error') !== -1 ? 'var(--red)' : l.indexOf('✓') !== -1 || l.indexOf('✅') !== -1 ? 'var(--green)' : 'var(--dim)';
6671
+ return '<div style="font-size:10px;font-family:var(--mono);color:'+col+';line-height:1.6;white-space:pre-wrap;word-break:break-all">'+wcEsc(l)+'</div>';
6672
+ }).join('') +
6673
+ '</div>';
6674
+ }
6675
+
6676
+ var clickable = hasContent && st !== 'pending';
6677
+ return '<div style="border-bottom:1px solid var(--border)">' +
6678
+ '<div onclick="'+( clickable ? 'wcTogglePhase('+JSON.stringify(ph.key)+')' : '' )+'" style="display:flex;gap:10px;align-items:center;padding:9px 12px;cursor:'+(clickable?'pointer':'default')+'">' +
6679
+ '<span style="font-size:13px;flex-shrink:0">'+ph.icon+'</span>' +
6680
+ '<div style="flex:1;min-width:0">' +
6681
+ '<div style="font-size:11px;font-weight:600;color:'+(st==='pending'?'var(--dim)':'var(--text)')+'">'+ph.label+'</div>' +
6682
+ (summary && !isOpen ? '<div style="font-size:10px;color:var(--dim);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+summary+'</div>' : '') +
6683
+ '</div>' +
6684
+ (clickable ? '<span style="font-size:10px;color:var(--dim);flex-shrink:0">'+(isOpen?'&#9650;':'&#9660;')+'</span>' : '') +
6685
+ '<span style="font-size:13px;color:'+statusColor[st]+';flex-shrink:0;margin-left:4px">'+statusIcon[st]+'</span>' +
6686
+ '</div>' +
6687
+ (isOpen ? '<div style="padding:0 12px 10px">' + expandedHtml + '</div>' : '') +
6688
+ '</div>';
6689
+ }).join('');
6690
+
6590
6691
  return '<div style="display:flex;flex-direction:column;flex:1;min-height:0">' +
6591
- // Info bar
6592
- (!sb.running && !sb.port ? '<div style="padding:10px 14px;border-bottom:1px solid var(--border);flex-shrink:0;font-size:11px;color:var(--dim);line-height:1.6">' +
6593
- '<div style="font-weight:700;color:var(--text);margin-bottom:4px">Sandbox locale — cosa succede quando avvii:</div>' +
6594
- '<div>&#8226; I file vengono scritti in <span style="font-family:var(--mono);color:var(--green)">~/.nha/webcraft/'+(wcState.projectName||'project')+'</span></div>' +
6595
- '<div>&#8226; npm install delle dipendenze nella stessa cartella (nessuna installazione globale)</div>' +
6596
- '<div>&#8226; Il server Express parte su una porta locale casuale (es. 45123)</div>' +
6597
- '<div>&#8226; Il DB usa un in-memory store (nessun PostgreSQL richiesto)</div>' +
6598
- '<div>&#8226; I dati sandbox sono temporanei e si azzerano al riavvio</div>' +
6599
- '<div style="margin-top:6px;color:var(--amber)">&#9888; Sandbox solo locale — nessun dato esce dal tuo Mac</div>' +
6600
- '</div>' : '') +
6601
- logsHtml +
6602
- (sb.error ? '<div style="padding:8px 14px;color:var(--red);font-size:11px;font-family:var(--mono);flex-shrink:0">&#10060; '+wcEsc(sb.error)+'</div>' : '') +
6692
+ phasesHtml +
6693
+ (sb.error ? '<div style="padding:10px 14px;color:var(--red);font-size:11px;font-family:var(--mono);border-top:1px solid var(--border)">&#10060; '+wcEsc(sb.error)+'</div>' : '') +
6603
6694
  '</div>';
6604
6695
  }
6605
6696