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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "6.3.0",
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": {
@@ -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.0';
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
 
@@ -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">&#9881;</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">&#9881;</span> Settings</div>
1029
+ </div>
886
1030
  </nav>
887
1031
 
888
1032
  <div class="header">