nothumanallowed 8.7.0 → 8.8.0

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": "8.7.0",
3
+ "version": "8.8.0",
4
4
  "description": "NotHumanAllowed — 38 AI agents + unified productivity suite. Gmail, Calendar, Drive, Contacts, Tasks, GitHub, Notion, Slack, voice chat, smart scheduler. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -819,6 +819,91 @@ export async function cmdUI(args) {
819
819
  return;
820
820
  }
821
821
 
822
+ // POST /api/agents — create custom agent
823
+ if (method === 'POST' && pathname === '/api/agents') {
824
+ const body = await parseBody(req);
825
+ const name = (body.name || '').toLowerCase().replace(/[^a-z0-9_-]/g, '');
826
+ const tagline = body.tagline || '';
827
+ const systemPrompt = body.systemPrompt || '';
828
+ if (!name || !tagline || !systemPrompt) {
829
+ sendJSON(res, 400, { error: 'name, tagline, and systemPrompt required' });
830
+ logRequest(method, pathname, 400, Date.now() - start);
831
+ return;
832
+ }
833
+ const agentFile = path.join(AGENTS_DIR, `${name}.mjs`);
834
+ if (fs.existsSync(agentFile)) {
835
+ sendJSON(res, 409, { error: `Agent "${name}" already exists` });
836
+ logRequest(method, pathname, 409, Date.now() - start);
837
+ return;
838
+ }
839
+ const content = `// NHA Custom Agent: ${name}\n// Created: ${new Date().toISOString()}\n\nexport const CARD = {\n name: '${name}',\n displayName: '${name.toUpperCase()}',\n category: 'custom',\n tagline: '${tagline.replace(/'/g, "\\'")}',\n};\n\nexport const SYSTEM_PROMPT = \`${systemPrompt.replace(/`/g, '\\`')}\`;\n`;
840
+ if (!fs.existsSync(AGENTS_DIR)) fs.mkdirSync(AGENTS_DIR, { recursive: true });
841
+ fs.writeFileSync(agentFile, content, 'utf-8');
842
+ agentCards.push({ name, displayName: name.toUpperCase(), category: 'custom', tagline });
843
+ sendJSON(res, 201, { ok: true, agent: { name, category: 'custom', tagline } });
844
+ logRequest(method, pathname, 201, Date.now() - start);
845
+ return;
846
+ }
847
+
848
+ // PUT /api/agents/:name — edit agent
849
+ if (method === 'PUT' && pathname.startsWith('/api/agents/')) {
850
+ const name = pathname.split('/')[3];
851
+ const body = await parseBody(req);
852
+ const agentFile = path.join(AGENTS_DIR, `${name}.mjs`);
853
+ if (!fs.existsSync(agentFile)) {
854
+ sendJSON(res, 404, { error: `Agent "${name}" not found` });
855
+ logRequest(method, pathname, 404, Date.now() - start);
856
+ return;
857
+ }
858
+ const tagline = body.tagline || '';
859
+ const systemPrompt = body.systemPrompt || '';
860
+ if (!tagline || !systemPrompt) {
861
+ sendJSON(res, 400, { error: 'tagline and systemPrompt required' });
862
+ logRequest(method, pathname, 400, Date.now() - start);
863
+ return;
864
+ }
865
+ const content = `// NHA Custom Agent: ${name}\n// Updated: ${new Date().toISOString()}\n\nexport const CARD = {\n name: '${name}',\n displayName: '${name.toUpperCase()}',\n category: '${body.category || 'custom'}',\n tagline: '${tagline.replace(/'/g, "\\'")}',\n};\n\nexport const SYSTEM_PROMPT = \`${systemPrompt.replace(/`/g, '\\`')}\`;\n`;
866
+ fs.writeFileSync(agentFile, content, 'utf-8');
867
+ const idx = agentCards.findIndex(a => a.name === name);
868
+ if (idx >= 0) { agentCards[idx].tagline = tagline; }
869
+ sendJSON(res, 200, { ok: true });
870
+ logRequest(method, pathname, 200, Date.now() - start);
871
+ return;
872
+ }
873
+
874
+ // DELETE /api/agents/:name — delete agent
875
+ if (method === 'DELETE' && pathname.startsWith('/api/agents/')) {
876
+ const name = pathname.split('/')[3];
877
+ const agentFile = path.join(AGENTS_DIR, `${name}.mjs`);
878
+ if (!fs.existsSync(agentFile)) {
879
+ sendJSON(res, 404, { error: `Agent "${name}" not found` });
880
+ logRequest(method, pathname, 404, Date.now() - start);
881
+ return;
882
+ }
883
+ fs.unlinkSync(agentFile);
884
+ const idx = agentCards.findIndex(a => a.name === name);
885
+ if (idx >= 0) agentCards.splice(idx, 1);
886
+ sendJSON(res, 200, { ok: true });
887
+ logRequest(method, pathname, 200, Date.now() - start);
888
+ return;
889
+ }
890
+
891
+ // GET /api/agents/:name — get agent details (system prompt)
892
+ if (method === 'GET' && pathname.startsWith('/api/agents/') && pathname.split('/').length === 4) {
893
+ const name = pathname.split('/')[3];
894
+ const agentFile = path.join(AGENTS_DIR, `${name}.mjs`);
895
+ if (!fs.existsSync(agentFile)) {
896
+ sendJSON(res, 404, { error: `Agent "${name}" not found` });
897
+ logRequest(method, pathname, 404, Date.now() - start);
898
+ return;
899
+ }
900
+ const src = fs.readFileSync(agentFile, 'utf-8');
901
+ const parsed = parseAgentFile(src, name);
902
+ sendJSON(res, 200, { name, category: parsed.card?.category || 'custom', tagline: parsed.card?.tagline || '', systemPrompt: parsed.systemPrompt || '' });
903
+ logRequest(method, pathname, 200, Date.now() - start);
904
+ return;
905
+ }
906
+
822
907
  // POST /api/ask — agent call with personal context (email, calendar, tasks)
823
908
  if (method === 'POST' && pathname === '/api/ask') {
824
909
  const body = await parseBody(req);
@@ -834,7 +919,9 @@ export async function cmdUI(args) {
834
919
  return;
835
920
  }
836
921
 
837
- if (!AGENTS.includes(body.agent)) {
922
+ // Allow both built-in and custom agents
923
+ const agentFile = path.join(AGENTS_DIR, `${body.agent}.mjs`);
924
+ if (!AGENTS.includes(body.agent) && !fs.existsSync(agentFile)) {
838
925
  sendJSON(res, 400, { error: `Unknown agent: ${body.agent}` });
839
926
  logRequest(method, pathname, 400, Date.now() - start);
840
927
  return;
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 = '8.4.0';
8
+ export const VERSION = '8.8.0';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -1086,22 +1086,68 @@ function renderAgents(el){
1086
1086
 
1087
1087
  var filtered=agentFilter?agentsList.filter(function(a){return a.category===agentFilter}):agentsList;
1088
1088
 
1089
+ h+='<div style="margin-bottom:10px"><button class="btn btn--primary" style="font-size:11px" onclick="showCreateAgentForm()">+ Create Agent</button></div>';
1089
1090
  h+='<div class="agents-grid">';
1090
1091
  filtered.forEach(function(a){
1091
1092
  var name=a.name||a.agentName;
1092
1093
  var display=a.displayName||name;
1093
1094
  var icon=AGENT_ICONS[name.toLowerCase()]||'\\u{1F916}';
1094
1095
  var desc=AGENT_DESCRIPTIONS[name.toLowerCase()]||a.tagline||a.description||'';
1095
- h+='<div class="card agent-card" onclick="openAgent(\\''+esc(name)+'\\',\\''+esc(display)+'\\')">'+
1096
+ var isCustom=a.category==='custom';
1097
+ h+='<div class="card agent-card" style="position:relative">'+
1098
+ '<div style="flex:1;cursor:pointer" onclick="openAgent(\\''+esc(name)+'\\',\\''+esc(display)+'\\')">'+
1099
+ '<div style="display:flex;align-items:center;gap:8px">'+
1096
1100
  '<div class="agent-card__icon">'+icon+'</div>'+
1097
1101
  '<div class="agent-card__body"><div class="agent-card__name">'+esc(display)+'</div>'+
1098
1102
  '<div class="agent-card__tagline">'+esc(desc)+'</div></div>'+
1103
+ '</div></div>'+
1104
+ '<div style="display:flex;gap:4px;flex-shrink:0">'+
1105
+ '<button onclick="editAgent(\\''+esc(name)+'\\')" style="background:none;border:none;cursor:pointer;font-size:12px;padding:2px" title="Edit">\\u{270F}\\u{FE0F}</button>'+
1106
+ '<button onclick="deleteAgent(\\''+esc(name)+'\\')" style="background:none;border:none;cursor:pointer;font-size:12px;padding:2px;color:#f44" title="Delete">\\u{1F5D1}</button>'+
1107
+ '</div>'+
1099
1108
  '</div>';
1100
1109
  });
1101
1110
  h+='</div>';
1102
1111
  el.innerHTML=h;
1103
1112
  }
1104
1113
  var agentFilter=null;
1114
+
1115
+ function showCreateAgentForm(){
1116
+ var name=prompt('Agent name (lowercase, no spaces):');
1117
+ if(!name)return;
1118
+ name=name.toLowerCase().replace(/[^a-z0-9_-]/g,'');
1119
+ if(!name)return;
1120
+ var tagline=prompt('Tagline (short description):');
1121
+ if(!tagline)return;
1122
+ var sysPrompt=prompt('System prompt (agent personality & instructions):');
1123
+ if(!sysPrompt)return;
1124
+ apiPost('/api/agents',{name:name,tagline:tagline,systemPrompt:sysPrompt}).then(function(r){
1125
+ if(r&&r.ok){showToast('success','Agent Created',name.toUpperCase()+' is ready to use');loadView('agents');}
1126
+ else{alert('Error: '+(r&&r.error||'Unknown'));}
1127
+ });
1128
+ }
1129
+
1130
+ function editAgent(name){
1131
+ fetch('/api/agents/'+name).then(function(r){return r.json()}).then(function(data){
1132
+ var newTagline=prompt('Tagline:',data.tagline||'');
1133
+ if(newTagline===null)return;
1134
+ var newPrompt=prompt('System prompt:',data.systemPrompt||'');
1135
+ if(newPrompt===null)return;
1136
+ fetch('/api/agents/'+name,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({tagline:newTagline,systemPrompt:newPrompt,category:data.category||'custom'})}).then(function(r){return r.json()}).then(function(r){
1137
+ if(r&&r.ok){showToast('success','Agent Updated',name.toUpperCase()+' updated');loadView('agents');}
1138
+ else{alert('Error: '+(r&&r.error||'Unknown'));}
1139
+ });
1140
+ });
1141
+ }
1142
+
1143
+ function deleteAgent(name){
1144
+ if(!confirm('Delete agent "'+name+'"? This cannot be undone.'))return;
1145
+ fetch('/api/agents/'+name,{method:'DELETE'}).then(function(r){return r.json()}).then(function(r){
1146
+ if(r&&r.ok){showToast('success','Agent Deleted',name+' removed');loadView('agents');}
1147
+ else{alert('Error: '+(r&&r.error||'Unknown'));}
1148
+ });
1149
+ }
1150
+
1105
1151
  function openAgent(name,display){
1106
1152
  selectedAgent=name;
1107
1153
  attachedFileContent=null;attachedFileName=null;