nothumanallowed 8.9.2 → 9.0.1

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.9.2",
3
+ "version": "9.0.1",
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": {
@@ -290,6 +290,25 @@ export async function cmdUI(args) {
290
290
  return;
291
291
  }
292
292
 
293
+ // POST /api/email/mark-read — mark email as read
294
+ if (method === 'POST' && pathname === '/api/email/mark-read') {
295
+ const body = await parseBody(req);
296
+ if (!body.messageId) {
297
+ sendJSON(res, 400, { error: 'messageId required' });
298
+ logRequest(method, pathname, 400, Date.now() - start);
299
+ return;
300
+ }
301
+ try {
302
+ const gmail = await import('../services/google-gmail.mjs');
303
+ await gmail.markAsRead(config, body.messageId);
304
+ sendJSON(res, 200, { ok: true });
305
+ } catch (e) {
306
+ sendJSON(res, 200, { ok: false, error: e.message });
307
+ }
308
+ logRequest(method, pathname, 200, Date.now() - start);
309
+ return;
310
+ }
311
+
293
312
  // POST /api/contacts — create contact
294
313
  if (method === 'POST' && pathname === '/api/contacts') {
295
314
  try {
@@ -837,7 +856,62 @@ export async function cmdUI(args) {
837
856
  }
838
857
  }
839
858
 
840
- // Handle file attachment
859
+ // Handle PDF attachment — send as document to Claude (native PDF support)
860
+ if (body.pdfBase64 && body.pdfName) {
861
+ try {
862
+ const provider = config.llm.provider || 'anthropic';
863
+ const apiKey = config.llm.apiKey;
864
+ const model = config.llm.model;
865
+ const pdfPrompt = body.message || `Read and analyze this PDF document "${body.pdfName}". Extract all text content, summarize key information.`;
866
+ let pdfResponse = '';
867
+
868
+ if (provider === 'anthropic') {
869
+ const r = await fetch('https://api.anthropic.com/v1/messages', {
870
+ method: 'POST',
871
+ headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' },
872
+ body: JSON.stringify({
873
+ model: model || 'claude-sonnet-4-20250514', max_tokens: 8192, system: enrichedSystemPrompt,
874
+ messages: [{ role: 'user', content: [
875
+ { type: 'document', source: { type: 'base64', media_type: 'application/pdf', data: body.pdfBase64 } },
876
+ { type: 'text', text: pdfPrompt },
877
+ ]}],
878
+ }),
879
+ });
880
+ if (!r.ok) throw new Error(`Anthropic ${r.status}: ${(await r.text()).slice(0, 200)}`);
881
+ const d = await r.json();
882
+ pdfResponse = d.content?.[0]?.text || '';
883
+ } else if (provider === 'gemini') {
884
+ const m = model || 'gemini-2.0-flash';
885
+ const r = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${m}:generateContent?key=${apiKey}`, {
886
+ method: 'POST',
887
+ headers: { 'Content-Type': 'application/json' },
888
+ body: JSON.stringify({
889
+ system_instruction: { parts: [{ text: enrichedSystemPrompt }] },
890
+ contents: [{ parts: [
891
+ { inline_data: { mime_type: 'application/pdf', data: body.pdfBase64 } },
892
+ { text: pdfPrompt },
893
+ ]}],
894
+ generationConfig: { maxOutputTokens: 8192 },
895
+ }),
896
+ });
897
+ if (!r.ok) throw new Error(`Gemini ${r.status}`);
898
+ const d = await r.json();
899
+ pdfResponse = d.candidates?.[0]?.content?.parts?.[0]?.text || '';
900
+ } else {
901
+ pdfResponse = `PDF reading requires Anthropic (Claude) or Gemini. Your provider "${provider}" does not support native PDF documents.`;
902
+ }
903
+
904
+ sendJSON(res, 200, { response: pdfResponse });
905
+ logRequest(method, pathname, 200, Date.now() - start);
906
+ return;
907
+ } catch (e) {
908
+ sendJSON(res, 200, { response: null, error: `PDF error: ${e.message}` });
909
+ logRequest(method, pathname, 200, Date.now() - start);
910
+ return;
911
+ }
912
+ }
913
+
914
+ // Handle text file attachment
841
915
  if (body.fileContent && body.fileName) {
842
916
  const filePrompt = body.message
843
917
  ? `User asks about file "${body.fileName}": ${body.message}\n\nFile content:\n${body.fileContent.slice(0, 8000)}`
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.9.2';
8
+ export const VERSION = '9.0.1';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -340,14 +340,28 @@ var chatAttachedImage=null;
340
340
 
341
341
  function handleChatFile(input){
342
342
  var file=input.files&&input.files[0];if(!file)return;
343
- var reader=new FileReader();
344
- reader.onload=function(e){
345
- chatAttachedFile={name:file.name,size:file.size,content:e.target.result};
346
- chatAttachedImage=null;
347
- document.getElementById('chatAttachInfo').style.display='';
348
- document.getElementById('chatAttachName').textContent='📎 '+file.name+' ('+Math.round(file.size/1024)+' KB)';
349
- };
350
- reader.readAsText(file);
343
+ var isPDF=file.name.toLowerCase().endsWith('.pdf')||file.type==='application/pdf';
344
+ if(isPDF){
345
+ // PDF: read as base64 and send as document to LLM
346
+ var reader=new FileReader();
347
+ reader.onload=function(e){
348
+ var base64=e.target.result.split(',')[1];
349
+ chatAttachedFile={name:file.name,size:file.size,content:null,base64:base64,mimeType:'application/pdf',isPDF:true};
350
+ chatAttachedImage=null;
351
+ document.getElementById('chatAttachInfo').style.display='';
352
+ document.getElementById('chatAttachName').textContent='📎 '+file.name+' ('+Math.round(file.size/1024)+' KB)';
353
+ };
354
+ reader.readAsDataURL(file);
355
+ }else{
356
+ var reader=new FileReader();
357
+ reader.onload=function(e){
358
+ chatAttachedFile={name:file.name,size:file.size,content:e.target.result};
359
+ chatAttachedImage=null;
360
+ document.getElementById('chatAttachInfo').style.display='';
361
+ document.getElementById('chatAttachName').textContent='📎 '+file.name+' ('+Math.round(file.size/1024)+' KB)';
362
+ };
363
+ reader.readAsText(file);
364
+ }
351
365
  }
352
366
 
353
367
  function handleChatImage(input){
@@ -385,7 +399,13 @@ function sendChat(){
385
399
  chatHistory.push({role:'assistant',content:'Thinking...'});renderMessages();
386
400
 
387
401
  var payload={message:msg||'Analyze this attachment',history:chatHistory.slice(0,-1)};
388
- if(chatAttachedFile){payload.fileContent=chatAttachedFile.content;payload.fileName=chatAttachedFile.name;}
402
+ if(chatAttachedFile){
403
+ if(chatAttachedFile.isPDF&&chatAttachedFile.base64){
404
+ payload.pdfBase64=chatAttachedFile.base64;payload.pdfName=chatAttachedFile.name;
405
+ }else{
406
+ payload.fileContent=chatAttachedFile.content;payload.fileName=chatAttachedFile.name;
407
+ }
408
+ }
389
409
  if(chatAttachedImage){payload.imageBase64=chatAttachedImage.base64;payload.imageMimeType=chatAttachedImage.mimeType;payload.imageName=chatAttachedImage.name;}
390
410
  clearChatAttach();
391
411
 
@@ -480,6 +500,13 @@ function renderEmails(el){
480
500
  var openEmailId=null;
481
501
  function openEmail(id){
482
502
  openEmailId=id;
503
+ // Mark as read locally + on server
504
+ var emailObj=dash.emails.find(function(e){return e.id===id});
505
+ if(emailObj&&emailObj.isUnread){
506
+ emailObj.isUnread=false;
507
+ updateBadges();
508
+ apiPost('/api/email/mark-read',{messageId:id}).catch(function(){});
509
+ }
483
510
  var el=document.getElementById('content');
484
511
  el.innerHTML='<div style="text-align:center;padding:40px"><div class="spinner"></div><div style="color:var(--dim)">Loading email...</div></div>';
485
512
  apiPost('/api/email/read',{messageId:id}).then(function(r){