nothumanallowed 13.5.113 → 13.5.115

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.
@@ -66,6 +66,7 @@ export const DESTRUCTIVE_ACTIONS = new Set([
66
66
  'gmail_send_attach',
67
67
  'gmail_reply',
68
68
  'gmail_delete',
69
+ 'imap_send',
69
70
  'calendar_create',
70
71
  'calendar_move',
71
72
  'calendar_update',
@@ -371,9 +372,37 @@ TOOLS:
371
372
  63. screen_analyze(question: string)
372
373
  Capture the screen AND analyze it with vision. Combines capture + question.
373
374
 
375
+ --- IMAP EMAIL (custom accounts) ---
376
+
377
+ 64. imap_list(accountId: string, labelId?: string, search?: string, limit?: number)
378
+ List emails from a custom IMAP account stored in local DB. Use imap_accounts() to get accountId.
379
+ labelId is optional — pass the label id to filter (e.g. inbox system label). search is full-text.
380
+ Returns: [{id, subject, from_address, from_name, internal_date, body_preview, is_read, is_starred}]
381
+
382
+ 65. imap_accounts()
383
+ List all configured IMAP accounts. Returns [{id, type, email_address, display_name, sync_status}].
384
+ Use the id from here as accountId in other imap_* tools.
385
+
386
+ 66. imap_read(messageId: string)
387
+ Read a full email message from the local DB by its id. Returns subject, from, to, body_text, body_html, attachments.
388
+
389
+ 67. imap_send(accountId: string, to: string, subject: string, bodyHtml: string, cc?: string, inReplyTo?: string)
390
+ Send an email via SMTP from a configured IMAP account. ALWAYS confirm with user before sending.
391
+ bodyHtml can contain HTML. inReplyTo is the Message-ID of the original email for threading.
392
+
393
+ 68. imap_sync(accountId: string)
394
+ Trigger an incremental IMAP sync for an account. Fetches new messages into local DB.
395
+ Run this before imap_list if you need fresh data.
396
+
397
+ 69. imap_labels(accountId: string)
398
+ List all labels for an IMAP account (system + user-defined). Returns [{id, name, system_type, color, unread_count}].
399
+
400
+ 70. imap_mark_read(messageId: string, isRead?: boolean)
401
+ Mark a local message as read or unread. Does NOT touch the IMAP server. Default isRead=true.
402
+
374
403
  --- CANVAS ---
375
404
 
376
- 64. canvas_render(html: string, title?: string)
405
+ 71. canvas_render(html: string, title?: string)
377
406
  Render HTML in the web UI canvas panel. Show charts, tables, diagrams, reports.
378
407
  ALWAYS use Chart.js from CDN for charts and graphs — never build charts with raw HTML/CSS.
379
408
  Template for charts:
@@ -846,6 +875,71 @@ export async function executeTool(action, params, config) {
846
875
  return `Email sent to ${params.to} with attachment "${downloaded.name}" (${formatFileSize(downloaded.size)}).`;
847
876
  }
848
877
 
878
+ // ── IMAP Email (custom accounts) ─────────────────────────────────────
879
+ case 'imap_accounts': {
880
+ const { listAccounts } = await import('./email-db.mjs');
881
+ const accs = listAccounts();
882
+ if (!accs.length) return 'No IMAP accounts configured. Ask the user to add one in Settings > Email Accounts.';
883
+ return accs.map(a => `[${a.id}] ${a.display_name} <${a.email_address}> — ${a.sync_status}`).join('\n');
884
+ }
885
+
886
+ case 'imap_sync': {
887
+ if (!params.accountId) return 'accountId required.';
888
+ const { syncAccount } = await import('./email-imap.mjs');
889
+ syncAccount(params.accountId).catch(e => console.error('[imap_sync]', e.message));
890
+ return `Sync started for account ${params.accountId}. New messages will appear in a few seconds.`;
891
+ }
892
+
893
+ case 'imap_labels': {
894
+ if (!params.accountId) return 'accountId required.';
895
+ const { listLabels } = await import('./email-db.mjs');
896
+ const labels = listLabels(params.accountId);
897
+ if (!labels.length) return 'No labels found.';
898
+ return labels.map(l => `[${l.id}] ${l.name}${l.system_type ? ' (' + l.system_type + ')' : ''}${l.unread_count > 0 ? ' — ' + l.unread_count + ' unread' : ''}`).join('\n');
899
+ }
900
+
901
+ case 'imap_list': {
902
+ if (!params.accountId) return 'accountId required. Use imap_accounts() first.';
903
+ const { listMessages: imapListMessages } = await import('./email-db.mjs');
904
+ const result = imapListMessages(params.accountId, params.labelId || null, params.limit || 20, 0, params.search || null);
905
+ if (!result.messages.length) return 'No messages found.';
906
+ return result.messages.map(m =>
907
+ `[${m.id}] ${m.is_read ? '' : '[UNREAD] '}From: ${m.from_name || m.from_address} | ${m.subject} | ${(m.internal_date || '').slice(0, 10)}\n Preview: ${(m.body_preview || '').slice(0, 120)}`
908
+ ).join('\n\n') + `\n\n(${result.total} total)`;
909
+ }
910
+
911
+ case 'imap_read': {
912
+ if (!params.messageId) return 'messageId required.';
913
+ const { getMessage: imapGetMessage, markRead: imapMarkRead } = await import('./email-db.mjs');
914
+ const msg = imapGetMessage(params.messageId);
915
+ if (!msg) return 'Message not found.';
916
+ imapMarkRead(params.messageId, true);
917
+ const to = (() => { try { const a = JSON.parse(msg.to_addresses || '[]'); return a.map(x => x.address || x).join(', '); } catch { return msg.to_addresses || ''; } })();
918
+ const body = msg.body_reply_only || msg.body_text || msg.body_preview || '(empty)';
919
+ return `Subject: ${msg.subject}\nFrom: ${msg.from_name ? msg.from_name + ' <' + msg.from_address + '>' : msg.from_address}\nTo: ${to}\nDate: ${msg.internal_date}\n\n${body.slice(0, 3000)}`;
920
+ }
921
+
922
+ case 'imap_send': {
923
+ if (!params.accountId || !params.to || !params.subject) return 'accountId, to, subject required.';
924
+ const { sendEmail: imapSendEmail } = await import('./email-smtp.mjs');
925
+ const result = await imapSendEmail(params.accountId, {
926
+ to: params.to,
927
+ cc: params.cc || null,
928
+ subject: params.subject,
929
+ bodyHtml: params.bodyHtml || params.body || '',
930
+ bodyText: params.bodyText || null,
931
+ inReplyTo: params.inReplyTo || null,
932
+ });
933
+ return `Email sent successfully. Message-ID: ${result.messageId}`;
934
+ }
935
+
936
+ case 'imap_mark_read': {
937
+ if (!params.messageId) return 'messageId required.';
938
+ const { markRead: imapMarkRead2 } = await import('./email-db.mjs');
939
+ imapMarkRead2(params.messageId, params.isRead !== false);
940
+ return `Message marked as ${params.isRead !== false ? 'read' : 'unread'}.`;
941
+ }
942
+
849
943
  // ── Calendar ──────────────────────────────────────────────────────────
850
944
  case 'calendar_today': {
851
945
  const events = await getTodayEvents(config);