nothumanallowed 13.5.47 → 13.5.48
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 +49 -1
- package/src/constants.mjs +1 -1
- package/src/services/web-ui.mjs +93 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.5.
|
|
3
|
+
"version": "13.5.48",
|
|
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
|
@@ -4067,7 +4067,55 @@ 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
|
-
|
|
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
|
+
sendLog('🔧 Shim iniettati: DB (in-memory), Sentinel WAF, Cache');
|
|
4071
4119
|
|
|
4072
4120
|
// Patch package.json to remove pg, add only what's needed
|
|
4073
4121
|
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.
|
|
8
|
+
export const VERSION = '13.5.48';
|
|
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
|
@@ -6568,38 +6568,108 @@ function wcSandboxPanelHtml() {
|
|
|
6568
6568
|
if (!wcState.generatedFiles.length) {
|
|
6569
6569
|
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
6570
|
}
|
|
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
6571
|
|
|
6572
|
+
// Server ready — show iframe with top bar
|
|
6577
6573
|
if (sb.port && !sb.running) {
|
|
6578
|
-
// Server ready — show iframe
|
|
6579
6574
|
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="
|
|
6582
|
-
'<span style="font-size:
|
|
6583
|
-
'<
|
|
6584
|
-
'<button onclick="
|
|
6575
|
+
'<div style="display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--border);flex-shrink:0;background:var(--bg3)">' +
|
|
6576
|
+
'<span style="width:8px;height:8px;border-radius:50%;background:#22c55e;display:inline-block;flex-shrink:0"></span>' +
|
|
6577
|
+
'<span style="font-size:11px;color:var(--text);font-family:var(--mono);font-weight:600">http://127.0.0.1:'+sb.port+'</span>' +
|
|
6578
|
+
'<span style="font-size:10px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1">'+wcEsc(sb.dir||'')+'</span>' +
|
|
6579
|
+
'<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">■ Ferma</button>' +
|
|
6580
|
+
'<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">↗ Apri nel browser</button>' +
|
|
6585
6581
|
'</div>' +
|
|
6586
6582
|
'<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
6583
|
'</div>';
|
|
6588
6584
|
}
|
|
6589
6585
|
|
|
6586
|
+
// Pre-launch info panel
|
|
6587
|
+
if (!sb.running && !sb.port && !sb.logs.length) {
|
|
6588
|
+
return '<div style="display:flex;flex-direction:column;flex:1;min-height:0;padding:20px">' +
|
|
6589
|
+
'<div style="background:var(--bg3);border:1px solid var(--border);border-radius:10px;padding:16px;max-width:480px">' +
|
|
6590
|
+
'<div style="font-size:12px;font-weight:700;color:var(--text);margin-bottom:12px">▶ Cosa succede quando avvii la sandbox:</div>' +
|
|
6591
|
+
'<div style="display:flex;flex-direction:column;gap:8px;font-size:11px;color:var(--dim)">' +
|
|
6592
|
+
'<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>' +
|
|
6593
|
+
'<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>' +
|
|
6594
|
+
'<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>' +
|
|
6595
|
+
'<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>' +
|
|
6596
|
+
'</div>' +
|
|
6597
|
+
'<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)">⚠ Solo locale — nessun dato esce dal tuo Mac</div>' +
|
|
6598
|
+
'</div>' +
|
|
6599
|
+
'</div>';
|
|
6600
|
+
}
|
|
6601
|
+
|
|
6602
|
+
// Running — show structured phases log
|
|
6603
|
+
var phases = [
|
|
6604
|
+
{ key: 'files', label: 'Scrittura file', icon: '📄' },
|
|
6605
|
+
{ key: 'shims', label: 'Shim iniettati', icon: '🔧' },
|
|
6606
|
+
{ key: 'pkg', label: 'package.json', icon: '📦' },
|
|
6607
|
+
{ key: 'env', label: '.env sandbox', icon: '⚙' },
|
|
6608
|
+
{ key: 'deps', label: 'Dipendenze', icon: '📦' },
|
|
6609
|
+
{ key: 'install', label: 'npm install', icon: '⏳' },
|
|
6610
|
+
{ key: 'start', label: 'Avvio server', icon: '▶' },
|
|
6611
|
+
];
|
|
6612
|
+
var logs = sb.logs;
|
|
6613
|
+
// Classify each log line into a phase
|
|
6614
|
+
function phaseOf(l) {
|
|
6615
|
+
if (!l) return 'start';
|
|
6616
|
+
if (l.indexOf('Scrittura') !== -1 || l.indexOf('file...') !== -1 || l[0] === ' ' && l.indexOf('.') !== -1 && l.indexOf('✓') !== -1) return 'files';
|
|
6617
|
+
if (l.indexOf('Shim') !== -1 || l.indexOf('shim') !== -1 || l.indexOf('DB shim') !== -1) return 'shims';
|
|
6618
|
+
if (l.indexOf('package.json') !== -1) return 'pkg';
|
|
6619
|
+
if (l.indexOf('.env') !== -1) return 'env';
|
|
6620
|
+
if (l.indexOf('Dipendenze') !== -1 || l.indexOf('Percorso:') !== -1 || (l.indexOf('•') !== -1 && l.indexOf('@') !== -1)) return 'deps';
|
|
6621
|
+
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';
|
|
6622
|
+
return 'start';
|
|
6623
|
+
}
|
|
6624
|
+
var byPhase = {};
|
|
6625
|
+
logs.forEach(function(l){ var p = phaseOf(l); if (!byPhase[p]) byPhase[p] = []; byPhase[p].push(l); });
|
|
6626
|
+
|
|
6627
|
+
// Check if a phase is done (next phase has lines)
|
|
6628
|
+
var phaseKeys = phases.map(function(p){ return p.key; });
|
|
6629
|
+
function phaseStatus(pk) {
|
|
6630
|
+
var idx = phaseKeys.indexOf(pk);
|
|
6631
|
+
if (!byPhase[pk] || !byPhase[pk].length) return 'pending';
|
|
6632
|
+
// Done if next phase has started or if error
|
|
6633
|
+
for (var i = idx+1; i < phaseKeys.length; i++) {
|
|
6634
|
+
if (byPhase[phaseKeys[i]] && byPhase[phaseKeys[i]].length) return 'done';
|
|
6635
|
+
}
|
|
6636
|
+
if (sb.error) return 'error';
|
|
6637
|
+
return 'active';
|
|
6638
|
+
}
|
|
6639
|
+
|
|
6640
|
+
var statusColor = { done:'var(--green)', active:'var(--amber)', pending:'var(--dim)', error:'var(--red)' };
|
|
6641
|
+
var statusIcon = { done:'✓', active:'⏳', pending:'○', error:'❌' };
|
|
6642
|
+
|
|
6643
|
+
var phasesHtml = phases.map(function(ph){
|
|
6644
|
+
var st = phaseStatus(ph.key);
|
|
6645
|
+
var lines = byPhase[ph.key] || [];
|
|
6646
|
+
// Filter noise: skip npm audit/funding lines
|
|
6647
|
+
var clean = lines.filter(function(l){ return l.indexOf('npm fund') === -1 && l.indexOf('run ') === -1 && l.indexOf('npm audit') === -1; });
|
|
6648
|
+
// Collapse file list: show count instead of every file
|
|
6649
|
+
var detail = '';
|
|
6650
|
+
if (ph.key === 'files' && clean.length > 2) {
|
|
6651
|
+
var count = clean.filter(function(l){ return l.indexOf('✓') !== -1; }).length;
|
|
6652
|
+
detail = count ? '<div style="font-size:10px;color:var(--dim);margin-top:2px">'+count+' file scritti</div>' : '';
|
|
6653
|
+
} else if (ph.key === 'deps' && clean.length > 2) {
|
|
6654
|
+
var depCount = clean.filter(function(l){ return l.indexOf('•') !== -1; }).length;
|
|
6655
|
+
detail = depCount ? '<div style="font-size:10px;color:var(--dim);margin-top:2px">'+depCount+' dipendenze</div>' : '';
|
|
6656
|
+
} else if (clean.length > 0) {
|
|
6657
|
+
var shown = clean.filter(function(l){ return l.trim() && l.indexOf('Percorso:') === -1; }).slice(-2);
|
|
6658
|
+
detail = shown.map(function(l){ return '<div style="font-size:10px;font-family:var(--mono);color:var(--dim);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">'+wcEsc(l.trim())+'</div>'; }).join('');
|
|
6659
|
+
}
|
|
6660
|
+
return '<div style="display:flex;gap:10px;align-items:flex-start;padding:8px 12px;border-bottom:1px solid var(--border)">' +
|
|
6661
|
+
'<span style="font-size:13px;flex-shrink:0">'+ph.icon+'</span>' +
|
|
6662
|
+
'<div style="flex:1;min-width:0">' +
|
|
6663
|
+
'<div style="font-size:11px;font-weight:600;color:'+(st==='pending'?'var(--dim)':'var(--text)')+'">'+ph.label+'</div>' +
|
|
6664
|
+
detail +
|
|
6665
|
+
'</div>' +
|
|
6666
|
+
'<span style="font-size:13px;color:'+statusColor[st]+';flex-shrink:0">'+statusIcon[st]+'</span>' +
|
|
6667
|
+
'</div>';
|
|
6668
|
+
}).join('');
|
|
6669
|
+
|
|
6590
6670
|
return '<div style="display:flex;flex-direction:column;flex:1;min-height:0">' +
|
|
6591
|
-
|
|
6592
|
-
(
|
|
6593
|
-
'<div style="font-weight:700;color:var(--text);margin-bottom:4px">Sandbox locale — cosa succede quando avvii:</div>' +
|
|
6594
|
-
'<div>• I file vengono scritti in <span style="font-family:var(--mono);color:var(--green)">~/.nha/webcraft/'+(wcState.projectName||'project')+'</span></div>' +
|
|
6595
|
-
'<div>• npm install delle dipendenze nella stessa cartella (nessuna installazione globale)</div>' +
|
|
6596
|
-
'<div>• Il server Express parte su una porta locale casuale (es. 45123)</div>' +
|
|
6597
|
-
'<div>• Il DB usa un in-memory store (nessun PostgreSQL richiesto)</div>' +
|
|
6598
|
-
'<div>• I dati sandbox sono temporanei e si azzerano al riavvio</div>' +
|
|
6599
|
-
'<div style="margin-top:6px;color:var(--amber)">⚠ 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">❌ '+wcEsc(sb.error)+'</div>' : '') +
|
|
6671
|
+
phasesHtml +
|
|
6672
|
+
(sb.error ? '<div style="padding:10px 14px;color:var(--red);font-size:11px;font-family:var(--mono);border-top:1px solid var(--border)">❌ '+wcEsc(sb.error)+'</div>' : '') +
|
|
6603
6673
|
'</div>';
|
|
6604
6674
|
}
|
|
6605
6675
|
|