nothumanallowed 6.3.0 → 6.3.2
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 +145 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.2",
|
|
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.2';
|
|
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,145 @@ 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
|
+
settingsSection('profile', 'User Profile', 'Agents use this when you say "my home", "my city", etc.', [
|
|
658
|
+
['name', 'Name', 'Your full name'],
|
|
659
|
+
['email', 'Email', 'your@email.com'],
|
|
660
|
+
['phone', 'Phone', '+39 ...'],
|
|
661
|
+
['home-address', 'Home Address', 'Via Roma 1, Modena'],
|
|
662
|
+
['work-address', 'Work Address', 'Via Ufficio 10, Modena'],
|
|
663
|
+
['city', 'City', 'Modena'],
|
|
664
|
+
['country', 'Country', 'Italy'],
|
|
665
|
+
['company', 'Company', 'Your company'],
|
|
666
|
+
['role', 'Role', 'Your role'],
|
|
667
|
+
['profile-notes', 'Notes', 'Anything agents should know about you'],
|
|
668
|
+
]) +
|
|
669
|
+
settingsSection('llm', 'LLM Provider', 'The AI model that powers your agents.', [
|
|
670
|
+
['provider', 'Provider', 'anthropic / openai / gemini / deepseek / grok / mistral'],
|
|
671
|
+
['key', 'API Key', 'sk-ant-api03-...', true],
|
|
672
|
+
['model', 'Model', 'Leave empty for default'],
|
|
673
|
+
]) +
|
|
674
|
+
settingsSection('responder', 'Message Responder', 'Auto-reply to Telegram and Discord messages.', [
|
|
675
|
+
['telegram-bot-token', 'Telegram Bot Token', 'Get from @BotFather', true],
|
|
676
|
+
['discord-bot-token', 'Discord Bot Token', 'From Discord Developer Portal', true],
|
|
677
|
+
]) +
|
|
678
|
+
settingsSection('ops', 'Daily Operations', 'Configure daily plan and alert timing.', [
|
|
679
|
+
['plan-time', 'Daily Plan Time', '07:00'],
|
|
680
|
+
['summary-time', 'Summary Time', '18:00'],
|
|
681
|
+
['meeting-alert', 'Meeting Alert (minutes)', '30'],
|
|
682
|
+
]) +
|
|
683
|
+
'</div>';
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function settingsSection(id, title, desc, fields) {
|
|
687
|
+
var h = '<div class="card" style="margin-bottom:16px" id="settings-' + id + '">' +
|
|
688
|
+
'<div class="card__title" style="color:var(--green);font-size:14px;margin-bottom:4px">' + esc(title) + '</div>' +
|
|
689
|
+
'<div style="font-size:11px;color:var(--dim);margin-bottom:12px">' + esc(desc) + '</div>';
|
|
690
|
+
|
|
691
|
+
for (var i = 0; i < fields.length; i++) {
|
|
692
|
+
var f = fields[i];
|
|
693
|
+
var key = f[0], label = f[1], placeholder = f[2], isSecret = f[3] || false;
|
|
694
|
+
var currentVal = '';
|
|
695
|
+
try { var cfg = JSON.parse(localStorage.getItem('nha_config_cache') || '{}'); currentVal = cfg[key] || ''; } catch(e) {}
|
|
696
|
+
|
|
697
|
+
h += '<div style="margin-bottom:10px">' +
|
|
698
|
+
'<label style="display:block;font-size:11px;color:var(--dim);margin-bottom:3px">' + esc(label) + '</label>' +
|
|
699
|
+
'<input type="' + (isSecret ? 'password' : 'text') + '" ' +
|
|
700
|
+
'value="' + esc(currentVal) + '" ' +
|
|
701
|
+
'placeholder="' + esc(placeholder) + '" ' +
|
|
702
|
+
'style="width:100%;padding:8px 12px;font-size:13px" ' +
|
|
703
|
+
'data-config-key="' + esc(key) + '" ' +
|
|
704
|
+
'data-section="' + esc(id) + '">' +
|
|
705
|
+
'</div>';
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
h += '<div style="display:flex;align-items:center;gap:12px;margin-top:14px">' +
|
|
709
|
+
'<button onclick="saveSettingsSection(\\x27' + id + '\\x27)" ' +
|
|
710
|
+
'style="background:var(--green3);color:var(--bg);padding:8px 24px;border-radius:var(--r);font-weight:700;font-size:13px;cursor:pointer">' +
|
|
711
|
+
'Save' +
|
|
712
|
+
'</button>' +
|
|
713
|
+
'<span id="settings-status-' + id + '" style="font-size:11px;color:var(--dim)"></span>' +
|
|
714
|
+
'</div>';
|
|
715
|
+
|
|
716
|
+
h += '</div>';
|
|
717
|
+
return h;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function saveSettingsSection(sectionId) {
|
|
721
|
+
var inputs = document.querySelectorAll('input[data-section="' + sectionId + '"]');
|
|
722
|
+
var statusEl = document.getElementById('settings-status-' + sectionId);
|
|
723
|
+
if (statusEl) { statusEl.textContent = 'Saving...'; statusEl.style.color = 'var(--amber)'; }
|
|
724
|
+
|
|
725
|
+
var promises = [];
|
|
726
|
+
inputs.forEach(function(input) {
|
|
727
|
+
var key = input.dataset.configKey;
|
|
728
|
+
var val = input.value.trim();
|
|
729
|
+
if (!key) return;
|
|
730
|
+
promises.push(
|
|
731
|
+
apiPost('/api/config', { key: key, value: val }).then(function(r) {
|
|
732
|
+
if (r && r.ok) {
|
|
733
|
+
try {
|
|
734
|
+
var cfg = JSON.parse(localStorage.getItem('nha_config_cache') || '{}');
|
|
735
|
+
cfg[key] = val;
|
|
736
|
+
localStorage.setItem('nha_config_cache', JSON.stringify(cfg));
|
|
737
|
+
} catch(e) {}
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
740
|
+
return false;
|
|
741
|
+
})
|
|
742
|
+
);
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
Promise.all(promises).then(function(results) {
|
|
746
|
+
var allOk = results.every(function(r) { return r; });
|
|
747
|
+
if (statusEl) {
|
|
748
|
+
if (allOk) {
|
|
749
|
+
statusEl.textContent = 'Saved!';
|
|
750
|
+
statusEl.style.color = 'var(--green)';
|
|
751
|
+
} else {
|
|
752
|
+
statusEl.textContent = 'Some fields failed to save.';
|
|
753
|
+
statusEl.style.color = 'var(--red)';
|
|
754
|
+
}
|
|
755
|
+
setTimeout(function() { statusEl.textContent = ''; }, 3000);
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
|
|
620
760
|
var attachedFileContent = null;
|
|
621
761
|
var attachedFileName = null;
|
|
622
762
|
|
|
@@ -883,6 +1023,10 @@ init();
|
|
|
883
1023
|
<div class="sidebar__label">AI</div>
|
|
884
1024
|
<div class="nav-item" data-view="agents" onclick="switchView('agents')"><span class="nav-item__icon">⚙</span> Agents</div>
|
|
885
1025
|
</div>
|
|
1026
|
+
<div class="sidebar__section">
|
|
1027
|
+
<div class="sidebar__label">CONFIG</div>
|
|
1028
|
+
<div class="nav-item" data-view="settings" onclick="switchView('settings')"><span class="nav-item__icon">⚙</span> Settings</div>
|
|
1029
|
+
</div>
|
|
886
1030
|
</nav>
|
|
887
1031
|
|
|
888
1032
|
<div class="header">
|