nothumanallowed 6.3.5 → 6.4.1

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": "6.3.5",
3
+ "version": "6.4.1",
4
4
  "description": "NotHumanAllowed — 38 AI agents for security, code, DevOps, data & daily ops. Per-agent memory, Telegram + Discord auto-responder, proactive intelligence daemon, voice chat, plugin system.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -533,8 +533,8 @@ export async function cmdUI(args) {
533
533
  return;
534
534
  }
535
535
 
536
- // ── Favicon (no-content) ──────────────────────────────────────────
537
- if (pathname === '/favicon.ico') {
536
+ // ── Favicon + Apple touch icons (no-content, suppress 404) ────────
537
+ if (pathname === '/favicon.ico' || pathname.startsWith('/apple-touch-icon')) {
538
538
  res.writeHead(204);
539
539
  res.end();
540
540
  return;
@@ -598,6 +598,24 @@ export async function cmdUI(args) {
598
598
  return;
599
599
  }
600
600
 
601
+ // POST /api/email/read — read full email by ID
602
+ if (method === 'POST' && pathname === '/api/email/read') {
603
+ const body = await parseBody(req);
604
+ if (!body.messageId) {
605
+ sendJSON(res, 400, { error: 'messageId required' });
606
+ logRequest(method, pathname, 400, Date.now() - start);
607
+ return;
608
+ }
609
+ try {
610
+ const msg = await getMessage(config, body.messageId);
611
+ sendJSON(res, 200, { message: msg });
612
+ } catch (e) {
613
+ sendJSON(res, 200, { error: e.message });
614
+ }
615
+ logRequest(method, pathname, 200, Date.now() - start);
616
+ return;
617
+ }
618
+
601
619
  // GET /api/emails
602
620
  if (method === 'GET' && pathname === '/api/emails') {
603
621
  try {
@@ -869,20 +887,11 @@ export async function cmdUI(args) {
869
887
 
870
888
  const server = http.createServer(handleRequest);
871
889
 
872
- // ── WebSocket server on port 3848 (so the frontend can connect) ──────
873
- const wsServer = http.createServer((req, res) => {
874
- if (req.url === '/health') {
875
- res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
876
- res.end(JSON.stringify({ ok: true }));
877
- return;
878
- }
879
- res.writeHead(404);
880
- res.end();
881
- });
882
-
890
+ // ── WebSocket integrated in main server (same port, /ws path) ──────
883
891
  const wsClients = new Set();
884
892
 
885
- wsServer.on('upgrade', (req, socket, head) => {
893
+ server.on('upgrade', (req, socket, head) => {
894
+ if (req.url !== '/ws') { socket.destroy(); return; }
886
895
  const key = req.headers['sec-websocket-key'];
887
896
  if (!key) { socket.destroy(); return; }
888
897
 
@@ -911,15 +920,6 @@ export async function cmdUI(args) {
911
920
  socket.on('error', () => wsClients.delete(socket));
912
921
  });
913
922
 
914
- wsServer.on('error', (err) => {
915
- if (err.code === 'EADDRINUSE') {
916
- // Daemon already has it — that's fine, frontend will connect to daemon
917
- return;
918
- }
919
- });
920
-
921
- wsServer.listen(3848, HOST, () => {});
922
-
923
923
  server.on('error', (err) => {
924
924
  if (err.code === 'EADDRINUSE') {
925
925
  fail(`Port ${port} is already in use. Try: nha ui --port=${port + 1}`);
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 = '6.3.5';
8
+ export const VERSION = '6.4.1';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -373,10 +373,50 @@ function renderEmails(el){
373
373
  var e=dash.emails;
374
374
  if(e.length===0){el.innerHTML='<div class="card" style="text-align:center;color:var(--dim);padding:30px">No unread emails</div>';return}
375
375
  var h='';e.forEach(function(x){
376
- h+='<div class="card email"><div class="email__header"><span class="email__from">'+esc(x.from)+'</span><span class="email__date">'+esc(x.date)+'</span></div><div class="email__subject">'+esc(x.subject)+'</div><div class="email__snippet">'+esc((x.snippet||'').slice(0,150))+'</div></div>';
376
+ h+='<div class="card email" style="cursor:pointer" onclick="openEmail(\\x27'+esc(x.id)+'\\x27)"><div class="email__header"><span class="email__from">'+esc(x.from)+'</span><span class="email__date">'+esc(x.date)+'</span></div><div class="email__subject">'+esc(x.subject)+'</div><div class="email__snippet">'+esc((x.snippet||'').slice(0,150))+'</div><div style="font-size:10px;color:var(--green3);margin-top:6px">Click to read full email</div></div>';
377
377
  });
378
378
  el.innerHTML=h;
379
379
  }
380
+ var openEmailId=null;
381
+ function openEmail(id){
382
+ openEmailId=id;
383
+ var el=document.getElementById('content');
384
+ el.innerHTML='<div style="text-align:center;padding:40px"><div class="spinner"></div><div style="color:var(--dim)">Loading email...</div></div>';
385
+ apiPost('/api/email/read',{messageId:id}).then(function(r){
386
+ if(!r||r.error){el.innerHTML='<div class="card" style="color:var(--red);padding:20px">Error: '+(r&&r.error||'Failed to load email')+'</div>';return}
387
+ var m=r.message||r;
388
+ var h='<div style="margin-bottom:12px"><button class="btn btn--secondary" onclick="switchView(\\x27emails\\x27)" style="font-size:11px">&larr; Back to inbox</button></div>';
389
+ h+='<div class="card" style="padding:20px">';
390
+ h+='<div style="margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid var(--border)">';
391
+ h+='<div style="font-size:16px;font-weight:700;color:var(--bright);margin-bottom:8px">'+esc(m.subject||'(no subject)')+'</div>';
392
+ h+='<div style="font-size:12px;color:var(--dim);margin-bottom:4px"><strong style="color:var(--text)">From:</strong> '+esc(m.from||'')+'</div>';
393
+ h+='<div style="font-size:12px;color:var(--dim);margin-bottom:4px"><strong style="color:var(--text)">To:</strong> '+esc(m.to||'')+'</div>';
394
+ h+='<div style="font-size:12px;color:var(--dim)"><strong style="color:var(--text)">Date:</strong> '+esc(m.date||'')+'</div>';
395
+ h+='</div>';
396
+ h+='<div style="font-size:13px;line-height:1.7;color:var(--text);white-space:pre-wrap;word-wrap:break-word">'+esc(m.body||m.snippet||'(no content)')+'</div>';
397
+ h+='</div>';
398
+ // Action buttons
399
+ h+='<div style="display:flex;gap:8px;margin-top:12px">';
400
+ h+='<button class="btn btn--primary" onclick="replyToEmail(\\x27'+esc(id)+'\\x27)" style="font-size:12px">Reply</button>';
401
+ h+='<button class="btn btn--secondary" onclick="askAgentAboutEmail(\\x27'+esc(id)+'\\x27)" style="font-size:12px">Ask SABER to scan</button>';
402
+ h+='</div>';
403
+ el.innerHTML=h;
404
+ });
405
+ }
406
+ function replyToEmail(id){
407
+ switchView('chat');
408
+ setTimeout(function(){
409
+ var inp=document.getElementById('chatInput');
410
+ if(inp){inp.value='Reply to email '+id+': ';inp.focus()}
411
+ },200);
412
+ }
413
+ function askAgentAboutEmail(id){
414
+ switchView('chat');
415
+ setTimeout(function(){
416
+ var inp=document.getElementById('chatInput');
417
+ if(inp){inp.value='Scan email '+id+' for phishing or security threats';inp.focus()}
418
+ },200);
419
+ }
380
420
 
381
421
  // ---- CALENDAR (monthly grid + day detail modal) ----
382
422
  var calYear, calMonth;
@@ -684,7 +724,7 @@ function renderSettings(el) {
684
724
  }
685
725
 
686
726
  function settingsSection(id, title, desc, fields) {
687
- var h = '<div class="card" style="margin-bottom:16px" id="settings-' + id + '">' +
727
+ var h = '<form class="card" style="margin-bottom:16px" id="settings-' + id + '" onsubmit="event.preventDefault();saveSettingsSection(\\x27' + id + '\\x27)">' +
688
728
  '<div class="card__title" style="color:var(--green);font-size:14px;margin-bottom:4px">' + esc(title) + '</div>' +
689
729
  '<div style="font-size:11px;color:var(--dim);margin-bottom:12px">' + esc(desc) + '</div>';
690
730
 
@@ -713,7 +753,7 @@ function settingsSection(id, title, desc, fields) {
713
753
  '<span id="settings-status-' + id + '" style="font-size:11px;color:var(--dim)"></span>' +
714
754
  '</div>';
715
755
 
716
- h += '</div>';
756
+ h += '</form>';
717
757
  return h;
718
758
  }
719
759
 
@@ -840,7 +880,7 @@ var ws = null;
840
880
  var wsReconnectTimer = null;
841
881
  function connectWebSocket() {
842
882
  try {
843
- ws = new WebSocket('ws://' + window.location.hostname + ':3848');
883
+ ws = new WebSocket('ws://' + window.location.host + '/ws');
844
884
  } catch(e) { return; }
845
885
 
846
886
  ws.onopen = function() {