nothumanallowed 3.0.2 → 3.1.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 +1 -1
- package/src/commands/ui.mjs +51 -5
- package/src/constants.mjs +1 -1
- package/src/services/ops-pipeline.mjs +8 -2
- package/src/services/web-ui.mjs +27 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents for security, code, DevOps, data & daily ops. Ask agents directly, plan your day with 5 specialist agents, manage tasks, connect Gmail + Calendar.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/commands/ui.mjs
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import http from 'http';
|
|
11
|
+
import os from 'os';
|
|
11
12
|
import { exec } from 'child_process';
|
|
12
13
|
import fs from 'fs';
|
|
13
14
|
import path from 'path';
|
|
@@ -29,7 +30,6 @@ import { info, ok, fail, warn, C, G, D, NC, BOLD } from '../ui.mjs';
|
|
|
29
30
|
// ── Constants ──────────────────────────────────────────────────────────────
|
|
30
31
|
|
|
31
32
|
const DEFAULT_PORT = 3847;
|
|
32
|
-
const HOST = '127.0.0.1';
|
|
33
33
|
|
|
34
34
|
// ── Chat system prompt (reused from chat.mjs) ──────────────────────────────
|
|
35
35
|
|
|
@@ -311,16 +311,20 @@ function logRequest(method, url, statusCode, durationMs) {
|
|
|
311
311
|
// ── Server ───────────────────────────────────────────────────────────────
|
|
312
312
|
|
|
313
313
|
export async function cmdUI(args) {
|
|
314
|
-
// Parse
|
|
314
|
+
// Parse flags
|
|
315
315
|
let port = DEFAULT_PORT;
|
|
316
316
|
let noBrowser = false;
|
|
317
|
+
let lanMode = false;
|
|
317
318
|
for (const arg of args) {
|
|
318
319
|
if (arg.startsWith('--port=')) {
|
|
319
320
|
port = parseInt(arg.split('=')[1], 10) || DEFAULT_PORT;
|
|
320
321
|
} else if (arg === '--no-browser') {
|
|
321
322
|
noBrowser = true;
|
|
323
|
+
} else if (arg === '--lan') {
|
|
324
|
+
lanMode = true;
|
|
322
325
|
}
|
|
323
326
|
}
|
|
327
|
+
const HOST = lanMode ? '0.0.0.0' : '127.0.0.1';
|
|
324
328
|
|
|
325
329
|
const config = loadConfig();
|
|
326
330
|
const htmlPage = getHTML(port);
|
|
@@ -367,6 +371,25 @@ export async function cmdUI(args) {
|
|
|
367
371
|
return;
|
|
368
372
|
}
|
|
369
373
|
|
|
374
|
+
// ── PWA Manifest ────────────────────────────────────────────────
|
|
375
|
+
if (pathname === '/manifest.json') {
|
|
376
|
+
sendJSON(res, 200, {
|
|
377
|
+
name: 'NHA — Operations Console',
|
|
378
|
+
short_name: 'NHA',
|
|
379
|
+
description: '38 AI agents for daily ops. Email, calendar, tasks, security.',
|
|
380
|
+
start_url: '/',
|
|
381
|
+
display: 'standalone',
|
|
382
|
+
background_color: '#0a0a0a',
|
|
383
|
+
theme_color: '#0a0a0a',
|
|
384
|
+
icons: [
|
|
385
|
+
{ src: 'https://nothumanallowed.com/icon-192x192.png', sizes: '192x192', type: 'image/png' },
|
|
386
|
+
{ src: 'https://nothumanallowed.com/icon-512x512.png', sizes: '512x512', type: 'image/png' },
|
|
387
|
+
],
|
|
388
|
+
});
|
|
389
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
370
393
|
// ── Favicon (no-content) ──────────────────────────────────────────
|
|
371
394
|
if (pathname === '/favicon.ico') {
|
|
372
395
|
res.writeHead(204);
|
|
@@ -597,22 +620,45 @@ export async function cmdUI(args) {
|
|
|
597
620
|
});
|
|
598
621
|
|
|
599
622
|
server.listen(port, HOST, () => {
|
|
600
|
-
const
|
|
623
|
+
const localUrl = `http://127.0.0.1:${port}`;
|
|
624
|
+
|
|
625
|
+
// Get LAN IP for mobile access
|
|
626
|
+
let lanUrl = '';
|
|
627
|
+
if (lanMode) {
|
|
628
|
+
const nets = os.networkInterfaces();
|
|
629
|
+
for (const name of Object.keys(nets)) {
|
|
630
|
+
for (const net of nets[name]) {
|
|
631
|
+
if (net.family === 'IPv4' && !net.internal) {
|
|
632
|
+
lanUrl = `http://${net.address}:${port}`;
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (lanUrl) break;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
601
639
|
|
|
602
640
|
console.log('');
|
|
603
641
|
console.log(` ${BOLD}${C}NHA Local Operations Console${NC}`);
|
|
604
642
|
console.log(` ${D}Zero-dependency web interface for your daily ops${NC}`);
|
|
605
643
|
console.log('');
|
|
606
|
-
console.log(` ${G}
|
|
644
|
+
console.log(` ${G}Local:${NC} ${localUrl}`);
|
|
645
|
+
if (lanUrl) {
|
|
646
|
+
console.log(` ${G}Network:${NC} ${lanUrl} ${D}(mobile/tablet)${NC}`);
|
|
647
|
+
}
|
|
607
648
|
console.log(` ${D}Provider:${NC} ${config.llm.provider || 'not set'}`);
|
|
608
649
|
console.log(` ${D}API Key:${NC} ${config.llm.apiKey ? config.llm.apiKey.slice(0, 12) + '...' : '\x1b[0;31mnot set\x1b[0m'}`);
|
|
609
650
|
console.log(` ${D}Agents loaded:${NC} ${agentCards.length}`);
|
|
610
651
|
console.log('');
|
|
652
|
+
if (lanUrl) {
|
|
653
|
+
console.log(` ${D}Open ${lanUrl} on your phone to use NHA from mobile.${NC}`);
|
|
654
|
+
} else {
|
|
655
|
+
console.log(` ${D}Tip: use --lan to access from phone/tablet on same WiFi.${NC}`);
|
|
656
|
+
}
|
|
611
657
|
console.log(` ${D}Press Ctrl+C to stop${NC}`);
|
|
612
658
|
console.log('');
|
|
613
659
|
|
|
614
660
|
if (!noBrowser) {
|
|
615
|
-
openBrowser(
|
|
661
|
+
openBrowser(localUrl);
|
|
616
662
|
}
|
|
617
663
|
});
|
|
618
664
|
|
package/src/constants.mjs
CHANGED
|
@@ -99,7 +99,7 @@ export async function runPlanningPipeline(config, opts = {}) {
|
|
|
99
99
|
if (emails.length > 0) {
|
|
100
100
|
parallelPromises.push(
|
|
101
101
|
callAgent(config, 'saber',
|
|
102
|
-
`
|
|
102
|
+
`Analyze these emails for REAL security threats. Be smart — distinguish between:\n- LEGITIMATE notifications (Google login alerts from the user's own devices, npm publish confirmations, GitHub 2FA, password change confirmations the user initiated) → these are SAFE\n- ACTUAL threats (phishing links, spoofed senders, social engineering, urgent money requests, unknown login locations, credential harvesting) → these are FLAGGED\n\nDo NOT flag routine service notifications as threats. Only flag emails that require the user's immediate security attention.\n\nEMAILS:\n${emailContext}\n\nRespond with a JSON object: { "safe": [indices], "flagged": [{ "index": N, "reason": "..." }], "risk_notes": ["..."] }`,
|
|
103
103
|
).then(r => { agentResults.saber = r; ok('SABER: Email security scan complete'); })
|
|
104
104
|
.catch(e => { warn(`SABER failed: ${e.message}`); agentResults.saber = '{"safe":[],"flagged":[],"risk_notes":["scan failed"]}'; })
|
|
105
105
|
);
|
|
@@ -141,7 +141,13 @@ export async function runPlanningPipeline(config, opts = {}) {
|
|
|
141
141
|
// ── Phase 6: CONDUCTOR — Synthesize daily plan ─────────────────────────
|
|
142
142
|
info('Phase 6: CONDUCTOR synthesizing daily plan...');
|
|
143
143
|
|
|
144
|
-
const conductorPrompt = `You are the NHA Daily Planner. Synthesize intelligence from 4 specialist agents into a structured daily plan.
|
|
144
|
+
const conductorPrompt = `You are the NHA Daily Planner. Synthesize intelligence from 4 specialist agents into a structured, practical daily plan.
|
|
145
|
+
|
|
146
|
+
IMPORTANT GUIDELINES:
|
|
147
|
+
- Be PRACTICAL, not alarmist. Routine notifications (Google login alerts from your own devices, npm publish confirmations, GitHub security notices) are NOT security incidents.
|
|
148
|
+
- Only escalate to "security_alerts" if there is a GENUINE, actionable threat (unknown logins from strange locations, actual phishing, credential leaks).
|
|
149
|
+
- Focus on making the user's day productive, not on creating false urgency.
|
|
150
|
+
- Suggest realistic time blocks based on the actual task complexity.
|
|
145
151
|
|
|
146
152
|
AGENT REPORTS:
|
|
147
153
|
${agentResults.saber ? `\n[SABER — Security Scan]\n${agentResults.saber}` : ''}
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -9,7 +9,13 @@ export function getHTML(port) {
|
|
|
9
9
|
<html lang="en">
|
|
10
10
|
<head>
|
|
11
11
|
<meta charset="utf-8">
|
|
12
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
12
|
+
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
13
|
+
<meta name="mobile-web-app-capable" content="yes">
|
|
14
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
15
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
16
|
+
<meta name="apple-mobile-web-app-title" content="NHA">
|
|
17
|
+
<meta name="theme-color" content="#0a0a0a">
|
|
18
|
+
<link rel="manifest" href="/manifest.json">
|
|
13
19
|
<title>NHA — Local Operations Console</title>
|
|
14
20
|
<style>
|
|
15
21
|
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
@@ -64,8 +70,8 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
64
70
|
.dash-section h2{font-size:13px;color:var(--cyan);margin-bottom:12px;text-transform:uppercase;letter-spacing:1px}
|
|
65
71
|
|
|
66
72
|
/* Chat */
|
|
67
|
-
#chat-view{display:flex;flex-direction:column;height:
|
|
68
|
-
#chat-messages{flex:1;overflow-y:auto;padding:0 0 12px 0}
|
|
73
|
+
#chat-view{display:flex;flex-direction:column;height:calc(100vh - 52px);overflow:hidden}
|
|
74
|
+
#chat-messages{flex:1;overflow-y:auto;padding:0 0 12px 0;-webkit-overflow-scrolling:touch}
|
|
69
75
|
.msg{margin-bottom:12px;display:flex;gap:10px}
|
|
70
76
|
.msg-user .msg-bubble{background:var(--bg3);border:1px solid var(--borderbright);border-radius:8px 8px 2px 8px;padding:10px 14px;max-width:80%;margin-left:auto;color:var(--textbright)}
|
|
71
77
|
.msg-assistant .msg-bubble{background:var(--greendim);border:1px solid var(--green3);border-radius:8px 8px 8px 2px;padding:10px 14px;max-width:85%;color:var(--text);white-space:pre-wrap;word-wrap:break-word}
|
|
@@ -179,9 +185,11 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
179
185
|
/* Mobile */
|
|
180
186
|
#mobile-toggle{display:none;background:none;color:var(--green);font-size:20px;padding:4px 8px}
|
|
181
187
|
@media(max-width:768px){
|
|
182
|
-
#sidebar{position:fixed;left:-260px;top:0;height:100%;z-index:100;transition:left .25s;width:260px}
|
|
188
|
+
#sidebar{position:fixed;left:-260px;top:0;height:100%;z-index:100;transition:left .25s;width:260px;box-shadow:4px 0 20px rgba(0,0,0,0.8)}
|
|
183
189
|
#sidebar.open{left:0}
|
|
190
|
+
#sidebar.open~#main #content::before{content:'';position:fixed;inset:52px 0 0 0;background:rgba(0,0,0,0.6);z-index:50}
|
|
184
191
|
#mobile-toggle{display:block}
|
|
192
|
+
#content{padding:12px}
|
|
185
193
|
.dash-grid{grid-template-columns:1fr}
|
|
186
194
|
.agents-grid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
|
|
187
195
|
.msg-user .msg-bubble,.msg-assistant .msg-bubble{max-width:95%}
|
|
@@ -281,6 +289,13 @@ function switchView(view) {
|
|
|
281
289
|
function toggleSidebar() {
|
|
282
290
|
document.getElementById('sidebar').classList.toggle('open');
|
|
283
291
|
}
|
|
292
|
+
// Close sidebar when tapping outside on mobile
|
|
293
|
+
document.getElementById('main').addEventListener('click', (e) => {
|
|
294
|
+
const sidebar = document.getElementById('sidebar');
|
|
295
|
+
if (sidebar.classList.contains('open')) {
|
|
296
|
+
sidebar.classList.remove('open');
|
|
297
|
+
}
|
|
298
|
+
});
|
|
284
299
|
|
|
285
300
|
// ── Clock ──────────────────────────────────────────────────────────────────
|
|
286
301
|
function updateClock() {
|
|
@@ -773,18 +788,20 @@ document.getElementById('agent-modal').addEventListener('click', e => {
|
|
|
773
788
|
|
|
774
789
|
// ── Init ───────────────────────────────────────────────────────────────────
|
|
775
790
|
async function init() {
|
|
791
|
+
// Show loading immediately
|
|
792
|
+
const el = document.getElementById('content');
|
|
793
|
+
if (el) el.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:60vh;flex-direction:column"><div class="spinner"></div><div style="color:var(--textdim);margin-top:12px">Loading dashboard...</div></div>';
|
|
794
|
+
|
|
776
795
|
try {
|
|
777
796
|
await loadDashboard();
|
|
778
797
|
} catch (e) {
|
|
779
798
|
console.error('Dashboard load error:', e);
|
|
780
799
|
}
|
|
781
|
-
try {
|
|
782
|
-
loadPlan().catch(e => console.error('Plan load error:', e));
|
|
783
|
-
loadAgents().catch(e => console.error('Agents load error:', e));
|
|
784
|
-
} catch (e) {
|
|
785
|
-
console.error('Init error:', e);
|
|
786
|
-
}
|
|
787
800
|
renderView();
|
|
801
|
+
try {
|
|
802
|
+
loadPlan().catch(()=>{});
|
|
803
|
+
loadAgents().catch(()=>{});
|
|
804
|
+
} catch {}
|
|
788
805
|
// Auto-refresh every 2 minutes
|
|
789
806
|
setInterval(() => loadDashboard().then(() => { if (currentView === 'dashboard') renderView(); }).catch(()=>{}), 120000);
|
|
790
807
|
}
|