nothumanallowed 6.3.0 → 6.3.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 +38 -0
- package/src/constants.mjs +1 -1
- package/src/services/web-ui.mjs +117 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.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": {
|
package/src/commands/ui.mjs
CHANGED
|
@@ -559,6 +559,44 @@ export async function cmdUI(args) {
|
|
|
559
559
|
return;
|
|
560
560
|
}
|
|
561
561
|
|
|
562
|
+
// POST /api/config — save a config value from the web UI
|
|
563
|
+
if (method === 'POST' && pathname === '/api/config') {
|
|
564
|
+
const body = await parseBody(req);
|
|
565
|
+
if (!body.key) {
|
|
566
|
+
sendJSON(res, 400, { error: 'key required' });
|
|
567
|
+
logRequest(method, pathname, 400, Date.now() - start);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
const { setConfigValue, loadConfig: reloadConfig } = await import('../config.mjs');
|
|
571
|
+
const success = setConfigValue(body.key, body.value || '');
|
|
572
|
+
if (success) {
|
|
573
|
+
// Reload config in memory so the chat system picks up changes immediately
|
|
574
|
+
const newConfig = reloadConfig();
|
|
575
|
+
Object.assign(config, newConfig);
|
|
576
|
+
}
|
|
577
|
+
sendJSON(res, 200, { ok: success, key: body.key });
|
|
578
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// GET /api/config — read config values for settings UI
|
|
583
|
+
if (method === 'GET' && pathname === '/api/config') {
|
|
584
|
+
// Return non-sensitive config for the settings form
|
|
585
|
+
sendJSON(res, 200, {
|
|
586
|
+
profile: config.profile || {},
|
|
587
|
+
provider: config.llm?.provider || '',
|
|
588
|
+
model: config.llm?.model || '',
|
|
589
|
+
hasApiKey: !!config.llm?.apiKey,
|
|
590
|
+
planTime: config.ops?.planTime || '07:00',
|
|
591
|
+
summaryTime: config.ops?.summaryTime || '18:00',
|
|
592
|
+
meetingAlert: config.ops?.meetingAlertMinutes || 30,
|
|
593
|
+
hasTelegram: !!config.responder?.telegram?.token,
|
|
594
|
+
hasDiscord: !!config.responder?.discord?.token,
|
|
595
|
+
});
|
|
596
|
+
logRequest(method, pathname, 200, Date.now() - start);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
|
|
562
600
|
// GET /api/emails
|
|
563
601
|
if (method === 'GET' && pathname === '/api/emails') {
|
|
564
602
|
try {
|
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.
|
|
8
|
+
export const VERSION = '6.3.1';
|
|
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
|
@@ -194,7 +194,7 @@ function switchView(v) {
|
|
|
194
194
|
document.querySelectorAll('.nav-item').forEach(function(el){
|
|
195
195
|
if(el.dataset.view===v){el.classList.add('nav-item--active')}else{el.classList.remove('nav-item--active')}
|
|
196
196
|
});
|
|
197
|
-
var titles = {dashboard:'Dashboard',chat:'Chat',plan:'Daily Plan',tasks:'Tasks',emails:'Emails',calendar:'Calendar',agents:'Agents'};
|
|
197
|
+
var titles = {dashboard:'Dashboard',chat:'Chat',plan:'Daily Plan',tasks:'Tasks',emails:'Emails',calendar:'Calendar',agents:'Agents',settings:'Settings'};
|
|
198
198
|
document.getElementById('headerTitle').textContent = titles[v]||v;
|
|
199
199
|
closeSidebar();
|
|
200
200
|
render();
|
|
@@ -257,6 +257,7 @@ function render(){
|
|
|
257
257
|
case 'emails':renderEmails(el);break;
|
|
258
258
|
case 'calendar':renderCalendar(el);break;
|
|
259
259
|
case 'agents':renderAgents(el);break;
|
|
260
|
+
case 'settings':renderSettings(el);break;
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
|
|
@@ -617,6 +618,117 @@ function openAgent(name,display){
|
|
|
617
618
|
function closeModal(){
|
|
618
619
|
document.getElementById('agentModal').classList.remove('modal-overlay--open');
|
|
619
620
|
}
|
|
621
|
+
|
|
622
|
+
// ---- SETTINGS ----
|
|
623
|
+
var settingsLoaded = false;
|
|
624
|
+
var settingsData = {};
|
|
625
|
+
|
|
626
|
+
function renderSettings(el) {
|
|
627
|
+
if (!settingsLoaded) {
|
|
628
|
+
el.innerHTML = '<div style="text-align:center;padding:40px"><div class="spinner"></div><div style="color:var(--dim)">Loading settings...</div></div>';
|
|
629
|
+
apiGet('/api/config').then(function(r) {
|
|
630
|
+
settingsData = r || {};
|
|
631
|
+
// Populate cache for fields
|
|
632
|
+
var cache = {};
|
|
633
|
+
var p = settingsData.profile || {};
|
|
634
|
+
if (p.name) cache['name'] = p.name;
|
|
635
|
+
if (p.email) cache['email'] = p.email;
|
|
636
|
+
if (p.phone) cache['phone'] = p.phone;
|
|
637
|
+
if (p.homeAddress) cache['home-address'] = p.homeAddress;
|
|
638
|
+
if (p.workAddress) cache['work-address'] = p.workAddress;
|
|
639
|
+
if (p.city) cache['city'] = p.city;
|
|
640
|
+
if (p.country) cache['country'] = p.country;
|
|
641
|
+
if (p.company) cache['company'] = p.company;
|
|
642
|
+
if (p.role) cache['role'] = p.role;
|
|
643
|
+
if (p.notes) cache['profile-notes'] = p.notes;
|
|
644
|
+
if (settingsData.provider) cache['provider'] = settingsData.provider;
|
|
645
|
+
if (settingsData.model) cache['model'] = settingsData.model;
|
|
646
|
+
if (settingsData.planTime) cache['plan-time'] = settingsData.planTime;
|
|
647
|
+
if (settingsData.summaryTime) cache['summary-time'] = settingsData.summaryTime;
|
|
648
|
+
if (settingsData.meetingAlert) cache['meeting-alert'] = String(settingsData.meetingAlert);
|
|
649
|
+
localStorage.setItem('nha_config_cache', JSON.stringify(cache));
|
|
650
|
+
settingsLoaded = true;
|
|
651
|
+
renderSettings(el);
|
|
652
|
+
});
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
el.innerHTML = '<div style="max-width:600px;margin:0 auto">' +
|
|
657
|
+
'<div class="card" style="margin-bottom:16px">' +
|
|
658
|
+
'<div class="card__title" style="color:var(--green);font-size:14px;margin-bottom:12px">User Profile</div>' +
|
|
659
|
+
'<div style="font-size:11px;color:var(--dim);margin-bottom:12px">Agents use this info when you say "my home", "my city", etc.</div>' +
|
|
660
|
+
settingsField('name', 'Name', 'Your full name') +
|
|
661
|
+
settingsField('email', 'Email', 'your@email.com') +
|
|
662
|
+
settingsField('phone', 'Phone', '+39 ...') +
|
|
663
|
+
settingsField('home-address', 'Home Address', 'Via Roma 1, Modena') +
|
|
664
|
+
settingsField('work-address', 'Work Address', 'Via Ufficio 10, Modena') +
|
|
665
|
+
settingsField('city', 'City', 'Modena') +
|
|
666
|
+
settingsField('country', 'Country', 'Italy') +
|
|
667
|
+
settingsField('company', 'Company', 'Your company') +
|
|
668
|
+
settingsField('role', 'Role', 'Your role') +
|
|
669
|
+
settingsField('profile-notes', 'Notes', 'Anything agents should know about you') +
|
|
670
|
+
'</div>' +
|
|
671
|
+
'<div class="card" style="margin-bottom:16px">' +
|
|
672
|
+
'<div class="card__title" style="color:var(--green);font-size:14px;margin-bottom:12px">LLM Provider</div>' +
|
|
673
|
+
settingsField('provider', 'Provider', 'anthropic / openai / gemini / deepseek / grok / mistral') +
|
|
674
|
+
settingsField('key', 'API Key', 'sk-ant-api03-...', true) +
|
|
675
|
+
settingsField('model', 'Model', 'Leave empty for default') +
|
|
676
|
+
'</div>' +
|
|
677
|
+
'<div class="card" style="margin-bottom:16px">' +
|
|
678
|
+
'<div class="card__title" style="color:var(--green);font-size:14px;margin-bottom:12px">Message Responder</div>' +
|
|
679
|
+
settingsField('telegram-bot-token', 'Telegram Bot Token', 'Get from @BotFather', true) +
|
|
680
|
+
settingsField('discord-bot-token', 'Discord Bot Token', 'From Discord Developer Portal', true) +
|
|
681
|
+
'</div>' +
|
|
682
|
+
'<div class="card" style="margin-bottom:16px">' +
|
|
683
|
+
'<div class="card__title" style="color:var(--green);font-size:14px;margin-bottom:12px">Daily Operations</div>' +
|
|
684
|
+
settingsField('plan-time', 'Daily Plan Time', '07:00') +
|
|
685
|
+
settingsField('summary-time', 'Summary Time', '18:00') +
|
|
686
|
+
settingsField('meeting-alert', 'Meeting Alert (minutes)', '30') +
|
|
687
|
+
'</div>' +
|
|
688
|
+
'<div style="text-align:center;margin-top:16px"><span style="color:var(--dim);font-size:10px">Changes are saved automatically when you press Enter or click outside a field.</span></div>' +
|
|
689
|
+
'</div>';
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function settingsField(key, label, placeholder, isSecret) {
|
|
693
|
+
var currentVal = '';
|
|
694
|
+
try {
|
|
695
|
+
var cfg = JSON.parse(localStorage.getItem('nha_config_cache') || '{}');
|
|
696
|
+
currentVal = cfg[key] || '';
|
|
697
|
+
} catch(e) {}
|
|
698
|
+
|
|
699
|
+
return '<div style="margin-bottom:10px">' +
|
|
700
|
+
'<label style="display:block;font-size:11px;color:var(--dim);margin-bottom:3px">' + esc(label) + '</label>' +
|
|
701
|
+
'<input type="' + (isSecret ? 'password' : 'text') + '" ' +
|
|
702
|
+
'value="' + esc(currentVal) + '" ' +
|
|
703
|
+
'placeholder="' + esc(placeholder) + '" ' +
|
|
704
|
+
'style="width:100%;padding:8px 12px;font-size:13px" ' +
|
|
705
|
+
'data-config-key="' + esc(key) + '" ' +
|
|
706
|
+
'onchange="saveSettingsField(this)" ' +
|
|
707
|
+
'onkeydown="if(event.key===\\x27Enter\\x27)this.blur()">' +
|
|
708
|
+
'</div>';
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function saveSettingsField(input) {
|
|
712
|
+
var key = input.dataset.configKey;
|
|
713
|
+
var val = input.value.trim();
|
|
714
|
+
if (!key) return;
|
|
715
|
+
apiPost('/api/config', { key: key, value: val }).then(function(r) {
|
|
716
|
+
if (r && r.ok) {
|
|
717
|
+
input.style.borderColor = 'var(--green)';
|
|
718
|
+
setTimeout(function() { input.style.borderColor = 'var(--border)'; }, 1500);
|
|
719
|
+
// Update local cache
|
|
720
|
+
try {
|
|
721
|
+
var cfg = JSON.parse(localStorage.getItem('nha_config_cache') || '{}');
|
|
722
|
+
cfg[key] = val;
|
|
723
|
+
localStorage.setItem('nha_config_cache', JSON.stringify(cfg));
|
|
724
|
+
} catch(e) {}
|
|
725
|
+
} else {
|
|
726
|
+
input.style.borderColor = 'var(--red)';
|
|
727
|
+
setTimeout(function() { input.style.borderColor = 'var(--border)'; }, 2000);
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
|
|
620
732
|
var attachedFileContent = null;
|
|
621
733
|
var attachedFileName = null;
|
|
622
734
|
|
|
@@ -883,6 +995,10 @@ init();
|
|
|
883
995
|
<div class="sidebar__label">AI</div>
|
|
884
996
|
<div class="nav-item" data-view="agents" onclick="switchView('agents')"><span class="nav-item__icon">⚙</span> Agents</div>
|
|
885
997
|
</div>
|
|
998
|
+
<div class="sidebar__section">
|
|
999
|
+
<div class="sidebar__label">CONFIG</div>
|
|
1000
|
+
<div class="nav-item" data-view="settings" onclick="switchView('settings')"><span class="nav-item__icon">⚙</span> Settings</div>
|
|
1001
|
+
</div>
|
|
886
1002
|
</nav>
|
|
887
1003
|
|
|
888
1004
|
<div class="header">
|