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 +1 -1
- package/src/commands/ui.mjs +35 -13
- package/src/constants.mjs +1 -1
- package/src/services/web-ui.mjs +30 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "9.5.
|
|
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": {
|
package/src/commands/ui.mjs
CHANGED
|
@@ -586,28 +586,50 @@ export async function cmdUI(args) {
|
|
|
586
586
|
return;
|
|
587
587
|
}
|
|
588
588
|
|
|
589
|
-
// GET /api/emails?filter=unread|all
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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.
|
|
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
|
|
package/src/services/web-ui.mjs
CHANGED
|
@@ -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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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'+(
|
|
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){
|