nothumanallowed 13.2.58 → 13.2.60

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": "13.2.58",
3
+ "version": "13.2.60",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2877,24 +2877,57 @@ export async function cmdUI(args) {
2877
2877
  sendToken('[Reading GitHub...] ');
2878
2878
  try {
2879
2879
  const gh = await import('../services/github.mjs');
2880
- const issues = await withTimeout(gh.listIssues(config, config.githubRepo || '', 10), 'GitHubAgent');
2881
- toolData = typeof issues === 'string' ? issues : JSON.stringify(issues);
2880
+ if (!config.github?.token) {
2881
+ toolData = 'GitHub token not configured. Run: nha config set github-token YOUR_PAT';
2882
+ } else {
2883
+ const parts = [];
2884
+ // Notifications (always available)
2885
+ try {
2886
+ const notifs = await withTimeout(gh.listNotifications(config, 15), 'GitHubAgent-notifs');
2887
+ if (notifs) parts.push('## GitHub Notifications\n' + notifs);
2888
+ } catch (e) { /* skip */ }
2889
+ // Issues/PRs on configured repo if available
2890
+ const repo = config.github?.defaultRepo || '';
2891
+ if (repo) {
2892
+ try {
2893
+ const issues = await withTimeout(gh.listIssues(config, repo, 'open', 10), 'GitHubAgent-issues');
2894
+ if (issues) parts.push('## Open Issues (' + repo + ')\n' + issues);
2895
+ } catch (e) { /* skip */ }
2896
+ try {
2897
+ const prs = await withTimeout(gh.listPRs(config, repo, 'open', 10), 'GitHubAgent-prs');
2898
+ if (prs) parts.push('## Open PRs (' + repo + ')\n' + prs);
2899
+ } catch (e) { /* skip */ }
2900
+ }
2901
+ toolData = parts.length > 0 ? parts.join('\n\n') : 'No GitHub data available.';
2902
+ }
2882
2903
  } catch (e) { toolData = `GitHub read failed: ${e.message}`; }
2883
2904
 
2884
2905
  } else if (agent === 'NotionAgent') {
2885
2906
  sendToken('[Searching Notion...] ');
2886
2907
  try {
2887
2908
  const nt = await import('../services/notion.mjs');
2888
- const results = await withTimeout(nt.search(config, stepPrompt, 10), 'NotionAgent');
2889
- toolData = typeof results === 'string' ? results : JSON.stringify(results);
2909
+ if (!config.notion?.token) {
2910
+ toolData = 'Notion token not configured. Run: nha config set notion-token YOUR_TOKEN';
2911
+ } else {
2912
+ const results = await withTimeout(nt.search(config, stepPrompt, 10), 'NotionAgent');
2913
+ toolData = typeof results === 'string' ? results : JSON.stringify(results);
2914
+ }
2890
2915
  } catch (e) { toolData = `Notion search failed: ${e.message}`; }
2891
2916
 
2892
2917
  } else if (agent === 'SlackAgent') {
2893
2918
  sendToken('[Reading Slack...] ');
2894
2919
  try {
2895
2920
  const sl = await import('../services/slack.mjs');
2896
- const channels = await withTimeout(sl.listChannels(config, 10), 'SlackAgent');
2897
- toolData = typeof channels === 'string' ? channels : JSON.stringify(channels);
2921
+ if (!config.slack?.token) {
2922
+ toolData = 'Slack token not configured. Run: nha config set slack-token xoxb-YOUR_TOKEN';
2923
+ } else {
2924
+ const parts = [];
2925
+ try {
2926
+ const channels = await withTimeout(sl.listChannels(config, 10), 'SlackAgent-channels');
2927
+ if (channels) parts.push('## Slack Channels\n' + (typeof channels === 'string' ? channels : JSON.stringify(channels)));
2928
+ } catch (e) { /* skip */ }
2929
+ toolData = parts.length > 0 ? parts.join('\n\n') : 'No Slack data available.';
2930
+ }
2898
2931
  } catch (e) { toolData = `Slack read failed: ${e.message}`; }
2899
2932
 
2900
2933
  } else if (agent === 'DriveAgent') {
package/src/config.mjs CHANGED
@@ -102,6 +102,7 @@ const DEFAULT_CONFIG = {
102
102
  },
103
103
  github: {
104
104
  token: '',
105
+ defaultRepo: '',
105
106
  },
106
107
  notion: {
107
108
  token: '',
@@ -264,6 +265,8 @@ export function setConfigValue(key, value) {
264
265
  'proactive-deadlines': 'ops.proactive.deadlines',
265
266
  'github-token': 'github.token',
266
267
  'gh-token': 'github.token',
268
+ 'github-repo': 'github.defaultRepo',
269
+ 'gh-repo': 'github.defaultRepo',
267
270
  'notion-token': 'notion.token',
268
271
  'slack-token': 'slack.token',
269
272
  'name': 'profile.name',
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 = '13.2.58';
8
+ export const VERSION = '13.2.60';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -1180,15 +1180,19 @@ function loadMonthEvents(){
1180
1180
  function calPrev(){calMonth--;if(calMonth<0){calMonth=11;calYear--}renderCalendar(document.getElementById('content'))}
1181
1181
  function calNext(){calMonth++;if(calMonth>11){calMonth=0;calYear++}renderCalendar(document.getElementById('content'))}
1182
1182
 
1183
+ var _calDayEvts=[];
1184
+ var _calDayStr='';
1183
1185
  function openDayDetail(dateStr){
1184
1186
  var evts=calEventsCache[dateStr]||[];
1187
+ _calDayEvts=evts;
1188
+ _calDayStr=dateStr;
1185
1189
  var dayLabel=new Date(dateStr+'T12:00:00').toLocaleDateString('en',{weekday:'long',month:'long',day:'numeric',year:'numeric'});
1186
1190
 
1187
1191
  function buildDayHtml(){
1188
1192
  var h='<h2 style="color:var(--green);margin-bottom:4px">'+esc(dayLabel)+'</h2>';
1189
1193
  h+='<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px">';
1190
1194
  h+='<span style="color:var(--dim);font-size:11px">'+dateStr+'</span>';
1191
- h+='<button onclick="openEventForm(null,'+JSON.stringify(dateStr)+')" style="margin-left:auto;background:var(--green3);color:var(--bg);padding:5px 12px;border-radius:var(--r);font-size:12px;font-weight:700">+ Add Event</button>';
1195
+ h+='<button onclick="openEventForm(null,_calDayStr)" style="margin-left:auto;background:var(--green3);color:var(--bg);padding:5px 12px;border-radius:var(--r);font-size:12px;font-weight:700">+ Add Event</button>';
1192
1196
  h+='</div>';
1193
1197
  if(evts.length===0){
1194
1198
  h+='<div style="color:var(--dim);padding:20px;text-align:center">No events on this day</div>';
@@ -1202,8 +1206,8 @@ function openDayDetail(dateStr){
1202
1206
  h+='<div style="color:var(--bright);font-size:15px;font-weight:700;margin-bottom:6px">'+esc(x.summary)+'</div></div>';
1203
1207
  if(x.id){
1204
1208
  h+='<div style="display:flex;gap:4px;flex-shrink:0">';
1205
- h+='<button onclick="openEventForm('+JSON.stringify({id:x.id,calId:calId,summary:x.summary,description:x.description||'',location:x.location||'',start:x.start,end:x.end,isAllDay:x.isAllDay})+','+JSON.stringify(dateStr)+')" style="background:var(--bg2);border:1px solid var(--border);color:var(--text);padding:3px 8px;border-radius:4px;font-size:11px">Edit</button>';
1206
- h+='<button onclick="deleteCalEvent('+JSON.stringify(calId)+','+JSON.stringify(x.id)+','+JSON.stringify(dateStr)+')" style="background:var(--bg2);border:1px solid var(--red);color:var(--red);padding:3px 8px;border-radius:4px;font-size:11px">Delete</button>';
1209
+ h+='<button onclick="openEventFormByIdx('+idx+')" style="background:var(--bg2);border:1px solid var(--border);color:var(--text);padding:3px 8px;border-radius:4px;font-size:11px">Edit</button>';
1210
+ h+='<button onclick="deleteCalEventByIdx('+idx+')" style="background:var(--bg2);border:1px solid var(--red);color:var(--red);padding:3px 8px;border-radius:4px;font-size:11px">Delete</button>';
1207
1211
  h+='</div>';
1208
1212
  }
1209
1213
  h+='</div>';
@@ -1253,10 +1257,20 @@ function refreshDayDetail(dateStr){
1253
1257
 
1254
1258
  function deleteCalEvent(calId,eventId,dateStr){
1255
1259
  if(!confirm('Delete this event?'))return;
1256
- apiPost('/api/calendar/'+encodeURIComponent(calId)+'/'+encodeURIComponent(eventId),null,'DELETE').then(function(){
1260
+ apiPost('/api/calendar/'+encodeURIComponent(calId)+'/'+encodeURIComponent(eventId),{},'DELETE').then(function(){
1257
1261
  refreshDayDetail(dateStr);
1258
1262
  }).catch(function(e){alert('Error: '+e.message);});
1259
1263
  }
1264
+ function deleteCalEventByIdx(idx){
1265
+ var x=_calDayEvts[idx];if(!x)return;
1266
+ var calId=x.calendarId||'primary';
1267
+ deleteCalEvent(calId,x.id,_calDayStr);
1268
+ }
1269
+ function openEventFormByIdx(idx){
1270
+ var x=_calDayEvts[idx];if(!x)return;
1271
+ var calId=x.calendarId||'primary';
1272
+ openEventForm({id:x.id,calId:calId,summary:x.summary,description:x.description||'',location:x.location||'',start:x.start,end:x.end,isAllDay:x.isAllDay},_calDayStr);
1273
+ }
1260
1274
 
1261
1275
  function openEventForm(evt,dateStr){
1262
1276
  var isEdit=evt&&evt.id;
@@ -1326,7 +1340,7 @@ function renderGitHub(el){
1326
1340
  // Header: user profile + repo input
1327
1341
  var userHtml='';
1328
1342
  if(user&&user.login){
1329
- userHtml='<div style="display:flex;align-items:center;gap:10px;margin-bottom:14px;padding:10px 14px;background:var(--bg2);border-radius:var(--r);border:1px solid var(--border)">'+(user.avatar?'<img src="'+esc(user.avatar)+'" style="width:36px;height:36px;border-radius:50%;object-fit:cover" alt="">':'')+'<div><div style="font-weight:700;font-size:13px;color:var(--green)">@'+esc(user.login)+'</div>'+(user.name?'<div style="font-size:11px;color:var(--dim)">'+esc(user.name)+'</div>':'')+'</div></div>';
1343
+ userHtml='<div style="display:flex;align-items:center;gap:10px;margin-bottom:14px;padding:10px 14px;background:var(--bg2);border-radius:var(--r);border:1px solid var(--border)">'+(user.avatar?'<img src="'+esc(user.avatar)+'" style="width:36px;height:36px;border-radius:50%;object-fit:cover" alt="">':'')+'<div style="flex:1"><div style="font-weight:700;font-size:13px;color:var(--green)">@'+esc(user.login)+'</div>'+(user.name?'<div style="font-size:11px;color:var(--dim)">'+esc(user.name)+'</div>':'')+'</div><button onclick="disconnectService(\\x27github-token\\x27,function(el){ghData=null;renderGitHub(el)})" style="background:var(--bg3);border:1px solid var(--red);color:var(--red);padding:4px 10px;border-radius:var(--r);font-size:11px">Disconnect</button></div>';
1330
1344
  }
1331
1345
  var h=userHtml+'<div style="display:flex;gap:8px;margin-bottom:8px;flex-wrap:wrap"><input type="text" id="ghRepo" placeholder="owner/repo" value="'+esc(ghRepo)+'" style="flex:1;min-width:180px;font-size:13px;padding:10px 14px" onkeydown="if(event.key===\\x27Enter\\x27)loadGhIssues()"><button onclick="loadGhIssues()" style="background:var(--green3);color:var(--bg);padding:8px 16px;border-radius:var(--r);font-weight:700;font-size:12px">Issues</button><button onclick="loadGhPRs()" style="background:var(--cyan);color:var(--bg);padding:8px 16px;border-radius:var(--r);font-weight:700;font-size:12px">PRs</button></div>';
1332
1346
  // My repos as clickable pills
@@ -1366,17 +1380,19 @@ function renderGitHub(el){
1366
1380
  renderGhData(r);
1367
1381
  });
1368
1382
  }
1369
- function loadGhIssues(){var inp=document.getElementById('ghRepo');if(!inp||!inp.value.trim())return;ghRepo=inp.value.trim();var el=document.getElementById('content');el.innerHTML='<div style="text-align:center;padding:40px"><div class="spinner"></div></div>';apiGet('/api/github/issues?repo='+encodeURIComponent(ghRepo)).then(function(r){if(ghData){ghData.issues=r.issues||[];ghData.prs=ghData.prs||[];ghData.repo=r.repo}else{ghData={issues:r.issues||[],prs:[],notifications:[]}}renderGitHub(document.getElementById('content'))})}
1383
+ function loadGhIssues(){var inp=document.getElementById('ghRepo');if(!inp||!inp.value.trim())return;ghRepo=inp.value.trim();apiPost('/api/config',{key:'github-repo',value:ghRepo}).catch(function(){});var el=document.getElementById('content');el.innerHTML='<div style="text-align:center;padding:40px"><div class="spinner"></div></div>';apiGet('/api/github/issues?repo='+encodeURIComponent(ghRepo)).then(function(r){if(ghData){ghData.issues=r.issues||[];ghData.prs=ghData.prs||[];ghData.repo=r.repo}else{ghData={issues:r.issues||[],prs:[],notifications:[]}}renderGitHub(document.getElementById('content'))})}
1370
1384
  function loadGhPRs(){var inp=document.getElementById('ghRepo');if(!inp||!inp.value.trim())return;ghRepo=inp.value.trim();var el=document.getElementById('content');el.innerHTML='<div style="text-align:center;padding:40px"><div class="spinner"></div></div>';apiGet('/api/github/prs?repo='+encodeURIComponent(ghRepo)).then(function(r){if(ghData){ghData.prs=r.prs||[];ghData.issues=ghData.issues||[];ghData.repo=r.repo}else{ghData={prs:r.prs||[],issues:[],notifications:[]}}renderGitHub(document.getElementById('content'))})}
1371
1385
  function ghMarkRead(){apiPost('/api/github/mark-read',{}).then(function(){if(ghData)ghData.notifications=[];renderGitHub(document.getElementById('content'))})}
1372
1386
 
1373
1387
  // ---- NOTION ----
1374
1388
  function renderNotion(el){
1375
1389
  apiGet('/api/notion/search?q=').then(function(r){
1376
- var banner=r&&r.error?setupBanner('Notion','nha config set notion-token YOUR_INTEGRATION_TOKEN')+'<div style="color:var(--dim);font-size:12px;padding:8px 0">Get an Integration Token from notion.so/my-integrations → New integration → Internal → copy Secret</div>':'';
1377
- el.innerHTML=banner+'<div style="display:flex;gap:8px;margin-bottom:16px"><input type="text" id="notionQuery" placeholder="Search Notion pages..." style="flex:1;font-size:13px;padding:10px 14px" onkeydown="if(event.key===\\x27Enter\\x27)searchNotion()"><button onclick="searchNotion()" style="background:var(--green3);color:var(--bg);padding:8px 16px;border-radius:var(--r);font-weight:700;font-size:12px">Search</button></div><div id="notionResults"></div>';
1390
+ var isOk=!(r&&r.error);
1391
+ var banner=isOk?'':''+setupBanner('Notion','nha config set notion-token YOUR_INTEGRATION_TOKEN')+'<div style="color:var(--dim);font-size:12px;padding:8px 0">Get an Integration Token from notion.so/my-integrations New integration → Internal → copy Secret</div>';
1392
+ var disconnectBtn=isOk?'<button onclick="disconnectService(\\x27notion-token\\x27,renderNotion)" style="background:var(--bg3);border:1px solid var(--red);color:var(--red);padding:4px 10px;border-radius:var(--r);font-size:11px;margin-bottom:12px">Disconnect Notion</button>':'';
1393
+ el.innerHTML=banner+disconnectBtn+'<div style="display:flex;gap:8px;margin-bottom:16px"><input type="text" id="notionQuery" placeholder="Search Notion pages..." style="flex:1;font-size:13px;padding:10px 14px" onkeydown="if(event.key===\\x27Enter\\x27)searchNotion()">'+(isOk?'<button onclick="searchNotion()" style="background:var(--green3);color:var(--bg);padding:8px 16px;border-radius:var(--r);font-weight:700;font-size:12px">Search</button>':'<button style="background:var(--bg3);color:var(--dim);padding:8px 16px;border-radius:var(--r);font-size:12px" disabled>Search</button>')+'</div><div id="notionResults"></div>';
1378
1394
  }).catch(function(){
1379
- el.innerHTML=setupBanner('Notion','nha config set notion-token YOUR_INTEGRATION_TOKEN')+'<div style="display:flex;gap:8px;margin-bottom:16px"><input type="text" id="notionQuery" placeholder="Search Notion pages..." style="flex:1;font-size:13px;padding:10px 14px"><button style="background:var(--green3);color:var(--bg);padding:8px 16px;border-radius:var(--r);font-weight:700;font-size:12px" disabled>Search</button></div>';
1395
+ el.innerHTML=setupBanner('Notion','nha config set notion-token YOUR_INTEGRATION_TOKEN')+'<div style="display:flex;gap:8px;margin-bottom:16px"><input type="text" id="notionQuery" placeholder="Search Notion pages..." style="flex:1;font-size:13px;padding:10px 14px"><button style="background:var(--bg3);color:var(--dim);padding:8px 16px;border-radius:var(--r);font-size:12px" disabled>Search</button></div>';
1380
1396
  });
1381
1397
  }
1382
1398
  function searchNotion(){
@@ -1399,6 +1415,13 @@ function loadNotionPage(id){
1399
1415
  }
1400
1416
 
1401
1417
  // ---- SLACK ----
1418
+ function disconnectService(configKey,renderFn){
1419
+ if(!confirm('Remove this connection? You can reconnect anytime.'))return;
1420
+ apiPost('/api/config',{key:configKey,value:''}).then(function(){
1421
+ var el=document.getElementById('content');
1422
+ if(el&&renderFn)renderFn(el);
1423
+ }).catch(function(){});
1424
+ }
1402
1425
  function setupBanner(service,cmd){return '<div style="display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--bg2);border:1px solid var(--border);border-left:3px solid var(--amber);border-radius:var(--r);margin-bottom:14px;font-size:12px"><span style="font-size:20px">&#128274;</span><div><div style="color:var(--fg);font-weight:600;margin-bottom:2px">'+esc(service)+' not configured</div><div style="color:var(--dim);font-family:var(--mono);font-size:11px">'+esc(cmd)+'</div></div></div>';}
1403
1426
  var slackData=null;
1404
1427
  function renderSlack(el){
@@ -1407,7 +1430,7 @@ function renderSlack(el){
1407
1430
  if(r&&r.error){el.innerHTML=setupBanner('Slack','nha config set slack-token xoxb-YOUR_TOKEN')+'<div style="color:var(--dim);font-size:12px;padding:8px 0">Get a Bot Token from api.slack.com/apps → OAuth &amp; Permissions → Bot Token Scopes: channels:read, channels:history, users:read</div>';return}
1408
1431
  slackData=r;
1409
1432
  var channels=r.channels||[];
1410
- var h='<div class="section-title">Channels ('+channels.length+')</div>';
1433
+ var h='<div style="display:flex;align-items:center;margin-bottom:8px"><div class="section-title" style="margin:0;flex:1">Channels ('+channels.length+')</div><button onclick="disconnectService(\\x27slack-token\\x27,renderSlack)" style="background:var(--bg3);border:1px solid var(--red);color:var(--red);padding:4px 10px;border-radius:var(--r);font-size:11px">Disconnect</button></div>';
1411
1434
  if(channels.length===0){h+='<div class="card" style="text-align:center;color:var(--dim);padding:20px">No channels found</div>'}
1412
1435
  channels.forEach(function(c){h+='<div class="card" style="padding:10px 14px;cursor:pointer" onclick="loadSlackChannel(\\x27'+esc(c.id)+'\\x27,\\x27'+esc(c.name)+'\\x27)"><span style="color:var(--green);font-weight:700">#'+esc(c.name)+'</span> <span style="font-size:10px;color:var(--dim)">'+c.members+' members</span>'+(c.purpose?'<div style="font-size:11px;color:var(--dim);margin-top:2px">'+esc(c.purpose)+'</div>':'')+'</div>'});
1413
1436
  h+='<div id="slackMessages"></div>';