nothumanallowed 9.5.0 → 9.5.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": "9.5.0",
3
+ "version": "9.5.2",
4
4
  "description": "NotHumanAllowed — 38 AI agents + 58 tools + browser automation + web search. Streaming chat, headless Chrome CDP, multi-conversation, export. Gmail, Calendar, Drive, GitHub, Notion, Slack. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -586,28 +586,50 @@ export async function cmdUI(args) {
586
586
  return;
587
587
  }
588
588
 
589
- // GET /api/emails?filter=unread|all (default: all inbox)
589
+ // GET /api/emails?page=0&pageSize=25&filter=unread|all
590
590
  if (method === 'GET' && pathname === '/api/emails') {
591
591
  try {
592
592
  const filter = url.searchParams.get('filter');
593
- let emails;
593
+ const page = parseInt(url.searchParams.get('page') || '0', 10);
594
+ const pageSize = parseInt(url.searchParams.get('pageSize') || '25', 10);
595
+
594
596
  if (filter === 'unread') {
595
- emails = await getUnreadImportant(config, 20);
597
+ const emails = await getUnreadImportant(config, pageSize);
598
+ sendJSON(res, 200, { emails, page, hasMore: false });
596
599
  } else {
597
- // Show all recent inbox emails (read + unread)
598
600
  const gm = await import('../services/google-gmail.mjs');
599
- const msgRefs = await gm.listMessages(config, 'in:inbox', 50);
600
- emails = [];
601
- for (const ref of msgRefs.slice(0, 50)) {
602
- try {
603
- const msg = await gm.getMessage(config, ref.id);
604
- emails.push(msg);
605
- } catch { /* skip */ }
601
+ // Fetch more refs than needed so we know if there are more pages
602
+ const totalToFetch = (page + 1) * pageSize + 1;
603
+ const msgRefs = await gm.listMessages(config, 'in:inbox', totalToFetch);
604
+
605
+ // Slice for current page
606
+ const pageRefs = msgRefs.slice(page * pageSize, (page + 1) * pageSize);
607
+ const hasMore = msgRefs.length > (page + 1) * pageSize;
608
+
609
+ // Fetch message details (parallel, batches of 5 for speed)
610
+ const emails = [];
611
+ for (let i = 0; i < pageRefs.length; i += 5) {
612
+ const batch = pageRefs.slice(i, i + 5);
613
+ const results = await Promise.allSettled(
614
+ batch.map(ref => gm.getMessage(config, ref.id))
615
+ );
616
+ for (const r of results) {
617
+ if (r.status === 'fulfilled') emails.push(r.value);
618
+ }
619
+ }
620
+
621
+ // Cache emails in memory for the session
622
+ if (!config._emailCache) config._emailCache = [];
623
+ for (const em of emails) {
624
+ if (!config._emailCache.find(c => c.id === em.id)) {
625
+ config._emailCache.push(em);
626
+ }
606
627
  }
628
+
629
+ sendJSON(res, 200, { emails, page, hasMore, totalCached: config._emailCache?.length || 0 });
607
630
  }
608
- sendJSON(res, 200, { emails });
609
631
  } catch (e) {
610
- sendJSON(res, 200, { emails: [], error: e.message });
632
+ sendJSON(res, 200, { emails: [], error: e.message, page: 0, hasMore: false });
611
633
  }
612
634
  logRequest(method, pathname, 200, Date.now() - start);
613
635
  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 = '9.5.0';
8
+ export const VERSION = '9.5.2';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -314,11 +314,11 @@ function apiPatch(p){return fetch(API+p,{method:'PATCH'}).then(function(r){retur
314
314
 
315
315
  // ---- LOAD DATA ----
316
316
  function loadDash(){
317
- return Promise.all([apiGet('/api/status'),apiGet('/api/emails'),apiGet('/api/calendar'),apiGet('/api/tasks')]).then(function(r){
318
- dash.status=r[0];dash.emails=(r[1]&&r[1].emails)||[];dash.events=(r[2]&&r[2].events)||[];dash.tasks=(r[3]&&r[3].tasks)||[];
319
- dashLoaded.emails=true;dashLoaded.events=true;dashLoaded.tasks=true;
320
- updateBadges();
321
- });
317
+ // Load each API independently — render as each arrives (emails are slow)
318
+ apiGet('/api/status').then(function(r){dash.status=r;render()});
319
+ apiGet('/api/tasks').then(function(r){dash.tasks=(r&&r.tasks)||[];dashLoaded.tasks=true;updateBadges();render()});
320
+ apiGet('/api/calendar').then(function(r){dash.events=(r&&r.events)||[];dashLoaded.events=true;updateBadges();render()});
321
+ return apiGet('/api/emails?page=0&pageSize=25').then(function(r){dash.emails=(r&&r.emails)||[];dash._emailHasMore=r&&r.hasMore;dashLoaded.emails=true;emailPage=0;updateBadges();render()});
322
322
  }
323
323
  function loadAgents(){return apiGet('/api/agents').then(function(r){agentsList=(r&&r.agents)||[]})}
324
324
  function updateBadges(){
@@ -361,14 +361,14 @@ function render(){
361
361
 
362
362
  // ---- DASHBOARD ----
363
363
  function renderDash(el){
364
- if(!dashLoaded.emails){el.innerHTML=loadingHTML('dashboard');return}
364
+ if(!dashLoaded.tasks&&!dashLoaded.events&&!dashLoaded.emails){el.innerHTML=loadingHTML('dashboard');return}
365
365
  var t=dash.tasks,e=dash.emails,ev=dash.events;
366
366
  var done=t.filter(function(x){return x.status==='done'}).length;
367
367
  var pend=t.length-done;
368
368
  var pct=t.length>0?Math.round(done/t.length*100):0;
369
369
  var h='<div class="dash-grid">'+
370
370
  '<div class="card"><div class="card__title">Tasks</div><div class="card__value">'+pend+'</div><div class="card__sub">'+done+'/'+t.length+' done ('+pct+'%)</div></div>'+
371
- '<div class="card"><div class="card__title">Emails</div><div class="card__value">'+e.length+'</div><div class="card__sub">'+(e.length>0?esc(e[0].from):'Inbox zero')+'</div></div>'+
371
+ '<div class="card"><div class="card__title">Emails</div><div class="card__value">'+(dashLoaded.emails?e.length:'<span class="spinner" style="width:14px;height:14px;display:inline-block;vertical-align:middle"></span>')+'</div><div class="card__sub">'+(dashLoaded.emails?(e.length>0?esc(e[0].from):'Inbox zero'):'Loading...')+'</div></div>'+
372
372
  '<div class="card"><div class="card__title">Events</div><div class="card__value">'+ev.length+'</div><div class="card__sub">'+(ev.length>0?esc(ev[0].summary):'No events')+'</div></div>'+
373
373
  '<div class="card"><div class="card__title">Agents</div><div class="card__value">38</div><div class="card__sub">Ready</div></div>'+
374
374
  '</div>';
@@ -675,15 +675,37 @@ function renderEmails(el){
675
675
  if(e.length===0){el.innerHTML='<div class="card" style="text-align:center;color:var(--dim);padding:30px">Inbox zero — no emails</div>';return}
676
676
  var unreadCount=e.filter(function(x){return x.isUnread}).length;
677
677
  var h='<div style="display:flex;gap:8px;margin-bottom:10px;align-items:center">';
678
- h+='<span style="font-size:12px;color:var(--dim)">'+e.length+' emails'+( unreadCount>0?' ('+unreadCount+' unread)':'')+'</span>';
678
+ h+='<span style="font-size:12px;color:var(--dim)">'+e.length+' emails'+(unreadCount>0?' ('+unreadCount+' unread)':'')+'</span>';
679
679
  if(unreadCount>0)h+='<button class="btn btn--secondary" style="font-size:10px;padding:4px 10px" onclick="markAllEmailsRead()">Mark all read</button>';
680
680
  h+='</div>';
681
681
  e.forEach(function(x){
682
682
  var unreadStyle=x.isUnread?'border-left:3px solid var(--green);font-weight:700':'border-left:3px solid transparent;opacity:0.7';
683
683
  h+='<div class="card email" style="cursor:pointer;'+unreadStyle+'" onclick="openEmail(\\x27'+esc(x.id)+'\\x27)"><div class="email__header"><span class="email__from">'+esc(x.from)+'</span><span class="email__date">'+esc(x.date)+(x.isUnread?' <span style="color:var(--green);font-size:9px">NEW</span>':'')+'</span></div><div class="email__subject">'+esc(x.subject)+'</div><div class="email__snippet" style="font-weight:400">'+esc((x.snippet||'').slice(0,150))+'</div></div>';
684
684
  });
685
+ // Load More button
686
+ if(dash._emailHasMore!==false){
687
+ h+='<button id="loadMoreEmails" onclick="loadMoreEmails()" style="width:100%;padding:12px;margin-top:8px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);color:var(--cyan);font-family:var(--font);font-size:12px;cursor:pointer;font-weight:700">Load More Emails</button>';
688
+ }
685
689
  el.innerHTML=h;
686
690
  }
691
+ var emailPage=0;
692
+ function loadMoreEmails(){
693
+ var btn=document.getElementById('loadMoreEmails');
694
+ if(btn){btn.textContent='Loading...';btn.disabled=true;}
695
+ emailPage++;
696
+ apiGet('/api/emails?page='+emailPage+'&pageSize=25').then(function(r){
697
+ if(r&&r.emails){
698
+ for(var i=0;i<r.emails.length;i++){
699
+ if(!dash.emails.find(function(e){return e.id===r.emails[i].id})){
700
+ dash.emails.push(r.emails[i]);
701
+ }
702
+ }
703
+ dash._emailHasMore=r.hasMore;
704
+ updateBadges();
705
+ render();
706
+ }
707
+ });
708
+ }
687
709
  function markAllEmailsRead(){
688
710
  apiPost('/api/email/mark-all-read',{}).then(function(r){
689
711
  if(r&&r.ok){