nothumanallowed 13.5.126 → 13.5.128

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": "13.5.126",
3
+ "version": "13.5.128",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -57,9 +57,11 @@
57
57
  "check-bundle": "node check-bundle.mjs"
58
58
  },
59
59
  "dependencies": {
60
- "better-sqlite3": "^12.9.0",
61
60
  "imapflow": "^1.3.3",
62
61
  "mailparser": "^3.9.8",
63
62
  "ws": "^8.18.0"
63
+ },
64
+ "optionalDependencies": {
65
+ "better-sqlite3": "^12.9.0"
64
66
  }
65
67
  }
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 = '13.5.126';
8
+ export const VERSION = '13.5.128';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -387,6 +387,18 @@ export function getSystemLabel(accountId, systemType) {
387
387
  return getDb().prepare('SELECT * FROM email_labels WHERE account_id = ? AND system_type = ?').get(accountId, systemType);
388
388
  }
389
389
 
390
+ /** Idempotent — seeds any missing system labels for a single account. */
391
+ export function ensureLabelsForAccount(accountId) {
392
+ const db = getDb();
393
+ const insert = db.prepare(`
394
+ INSERT OR IGNORE INTO email_labels (id, account_id, name, system_type, is_system, color, icon, sort_order, path)
395
+ VALUES (?, ?, ?, ?, 1, ?, ?, ?, ?)
396
+ `);
397
+ for (const lbl of SYSTEM_LABELS) {
398
+ insert.run(randomUUID(), accountId, lbl.name, lbl.system_type, lbl.color, lbl.icon, lbl.sort_order, lbl.system_type);
399
+ }
400
+ }
401
+
390
402
  export function createLabel(accountId, name, color, parentId) {
391
403
  const db = getDb();
392
404
  const id = randomUUID();
@@ -503,8 +515,16 @@ export function listMessages(accountId, labelId, limit, offset, search) {
503
515
  const params = [accountId];
504
516
 
505
517
  if (labelId) {
506
- where += ' AND EXISTS (SELECT 1 FROM email_message_labels j WHERE j.message_id = m.id AND j.label_id = ?)';
507
- params.push(labelId);
518
+ // Also include messages saved with matching imap_folder_path in case label link was missing
519
+ const lbl = db.prepare('SELECT system_type FROM email_labels WHERE id = ?').get(labelId);
520
+ const folderFallback = lbl?.system_type ? lbl.system_type.charAt(0).toUpperCase() + lbl.system_type.slice(1) : null;
521
+ if (folderFallback) {
522
+ where += ' AND (EXISTS (SELECT 1 FROM email_message_labels j WHERE j.message_id = m.id AND j.label_id = ?) OR m.imap_folder_path = ?)';
523
+ params.push(labelId, folderFallback);
524
+ } else {
525
+ where += ' AND EXISTS (SELECT 1 FROM email_message_labels j WHERE j.message_id = m.id AND j.label_id = ?)';
526
+ params.push(labelId);
527
+ }
508
528
  }
509
529
  if (search) {
510
530
  where += ' AND (m.subject LIKE ? OR m.from_address LIKE ? OR m.from_name LIKE ? OR m.body_preview LIKE ?)';
@@ -9,7 +9,7 @@ import { createTransport } from 'nodemailer';
9
9
  import { randomUUID } from 'crypto';
10
10
  import {
11
11
  getAccountCredentials, insertMessage, addMessageToLabel, getSystemLabel,
12
- insertAttachments, getDb,
12
+ ensureLabelsForAccount, insertAttachments, getDb,
13
13
  } from './email-db.mjs';
14
14
  import { createHash } from 'crypto';
15
15
 
@@ -97,6 +97,8 @@ export async function sendEmail(accountId, opts) {
97
97
  // ── Save to local DB as "sent" ────────────────────────────────────────
98
98
  const now = new Date().toISOString();
99
99
  const tid = threadId(opts.inReplyTo || msgId);
100
+ // Ensure system labels exist for this account (first send may precede first sync)
101
+ ensureLabelsForAccount(accountId);
100
102
  const sentLabel = getSystemLabel(accountId, 'sent');
101
103
 
102
104
  const dbId = insertMessage({
@@ -988,15 +988,19 @@ export async function executeTool(action, params, config) {
988
988
  case 'imap_send': {
989
989
  if (!params.accountId || !params.to || !params.subject) return 'accountId, to, subject required.';
990
990
  const { sendEmail: imapSendEmail } = await import('./email-smtp.mjs');
991
- const result = await imapSendEmail(params.accountId, {
992
- to: params.to,
993
- cc: params.cc || null,
994
- subject: params.subject,
995
- bodyHtml: params.bodyHtml || params.body || '',
996
- bodyText: params.bodyText || null,
997
- inReplyTo: params.inReplyTo || null,
998
- });
999
- return `Email sent successfully. Message-ID: ${result.messageId}`;
991
+ try {
992
+ const result = await imapSendEmail(params.accountId, {
993
+ to: params.to,
994
+ cc: params.cc || null,
995
+ subject: params.subject,
996
+ bodyHtml: params.bodyHtml || params.body || '',
997
+ bodyText: params.bodyText || null,
998
+ inReplyTo: params.inReplyTo || null,
999
+ });
1000
+ return `✅ Email sent and saved to Sent folder. Message-ID: ${result.messageId}`;
1001
+ } catch (e) {
1002
+ return `❌ SEND FAILED — the email was NOT delivered. Error: ${e.message}. Tell the user the send failed and show the exact error.`;
1003
+ }
1000
1004
  }
1001
1005
 
1002
1006
  case 'imap_mark_read': {
@@ -1016,15 +1020,19 @@ export async function executeTool(action, params, config) {
1016
1020
  let refs = [];
1017
1021
  try { refs = JSON.parse(orig.references_list || '[]'); } catch {}
1018
1022
  if (orig.message_id) refs.push(orig.message_id);
1019
- const result = await imapSendReply(params.accountId, {
1020
- to: orig.from_address,
1021
- cc: params.cc || null,
1022
- subject: replySubject,
1023
- bodyHtml: params.bodyHtml,
1024
- inReplyTo: orig.message_id || null,
1025
- references: refs,
1026
- });
1027
- return `Reply sent to ${orig.from_address}. Message-ID: ${result.messageId}`;
1023
+ try {
1024
+ const result = await imapSendReply(params.accountId, {
1025
+ to: orig.from_address,
1026
+ cc: params.cc || null,
1027
+ subject: replySubject,
1028
+ bodyHtml: params.bodyHtml,
1029
+ inReplyTo: orig.message_id || null,
1030
+ references: refs,
1031
+ });
1032
+ return `✅ Reply sent to ${orig.from_address} and saved to Sent folder. Message-ID: ${result.messageId}`;
1033
+ } catch (e) {
1034
+ return `❌ SEND FAILED — reply was NOT delivered. Error: ${e.message}. Tell the user the send failed and show the exact error.`;
1035
+ }
1028
1036
  }
1029
1037
 
1030
1038
  case 'imap_thread': {
@@ -1089,8 +1097,12 @@ export async function executeTool(action, params, config) {
1089
1097
  const applyVars = (str, vars) => Object.entries(vars).reduce((s, [k, v]) => s.split('[' + k + ']').join(v || ''), str);
1090
1098
  const subject = applyVars(tpl.subject, params.vars);
1091
1099
  const html = applyVars(tpl.html, params.vars);
1092
- const result = await imapSendTpl(params.accountId, { to: params.to, subject, bodyHtml: html });
1093
- return `Template email "${params.templateId}" sent to ${params.to}. Message-ID: ${result.messageId}`;
1100
+ try {
1101
+ const result = await imapSendTpl(params.accountId, { to: params.to, subject, bodyHtml: html });
1102
+ return `✅ Template email "${params.templateId}" sent to ${params.to} and saved to Sent folder. Message-ID: ${result.messageId}`;
1103
+ } catch (e) {
1104
+ return `❌ SEND FAILED — email was NOT delivered. Error: ${e.message}. Tell the user the send failed and show the exact error.`;
1105
+ }
1094
1106
  }
1095
1107
 
1096
1108
  case 'imap_bulk_send': {
@@ -1639,7 +1639,11 @@ function emailSend() {
1639
1639
  }).then(function(r) {
1640
1640
  if (r.ok) {
1641
1641
  if (status) { status.textContent = 'Sent!'; status.style.color = 'var(--green)'; }
1642
- setTimeout(emailCloseCompose, 800);
1642
+ setTimeout(function() {
1643
+ emailCloseCompose();
1644
+ // Reload current label to show the sent message if we are on Sent
1645
+ emailLoadMessages();
1646
+ }, 800);
1643
1647
  } else {
1644
1648
  if (status) { status.textContent = r.error || 'Error'; status.style.color = 'var(--red)'; }
1645
1649
  }