nothumanallowed 9.5.1 → 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.1",
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.1';
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
 
@@ -318,7 +318,7 @@ function loadDash(){
318
318
  apiGet('/api/status').then(function(r){dash.status=r;render()});
319
319
  apiGet('/api/tasks').then(function(r){dash.tasks=(r&&r.tasks)||[];dashLoaded.tasks=true;updateBadges();render()});
320
320
  apiGet('/api/calendar').then(function(r){dash.events=(r&&r.events)||[];dashLoaded.events=true;updateBadges();render()});
321
- return apiGet('/api/emails').then(function(r){dash.emails=(r&&r.emails)||[];dashLoaded.emails=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(){
@@ -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){