nothumanallowed 14.1.37 → 14.1.38

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": "14.1.37",
3
+ "version": "14.1.38",
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": {
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 = '14.1.37';
8
+ export const VERSION = '14.1.38';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -34,7 +34,7 @@ export function register(router) {
34
34
 
35
35
  const url = new URL(req.url, 'http://localhost');
36
36
  const folder = url.searchParams.get('folder') || 'inbox';
37
- const limit = parseInt(url.searchParams.get('pageSize') || url.searchParams.get('limit') || '50');
37
+ const limit = parseInt(url.searchParams.get('pageSize') || url.searchParams.get('limit') || '200');
38
38
  const offset = parseInt(url.searchParams.get('page') || url.searchParams.get('offset') || '0') * (url.searchParams.get('page') ? limit : 1);
39
39
  try {
40
40
  // getUnreadImportant returns fully-parsed messages (subject, from, snippet, etc.)
@@ -348,6 +348,25 @@ export function setSyncStatus(accountId, status, error) {
348
348
  .run(status, error || null, accountId);
349
349
  }
350
350
 
351
+ export function ensureGoogleAccount(data) {
352
+ const db = getDb();
353
+ const existing = db.prepare('SELECT * FROM email_accounts WHERE id = ? OR (type = ? AND email_address = ?)').get(data.id, 'google', data.email_address);
354
+ if (existing) {
355
+ return existing;
356
+ }
357
+
358
+ const id = data.id || 'google';
359
+ db.prepare(`
360
+ INSERT OR REPLACE INTO email_accounts (id, type, email_address, display_name, from_name, imap_host, imap_port, sort_order, is_active)
361
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
362
+ `).run(id, 'google', data.email_address, data.display_name, data.display_name,
363
+ data.imap_host, data.imap_port || 993, data.sort_order || 0, data.is_active || 1);
364
+
365
+ // Seed system labels for new Google account
366
+ seedSystemLabels(db);
367
+ return db.prepare('SELECT * FROM email_accounts WHERE id = ?').get(id);
368
+ }
369
+
351
370
  // ── FOLDER CRUD ────────────────────────────────────────────────────────────
352
371
 
353
372
  export function upsertFolder(accountId, path, name, folderType, uidValidity, lastUid) {
@@ -491,6 +510,17 @@ export function insertMessage(data) {
491
510
  return id;
492
511
  }
493
512
 
513
+ // Alias for Gmail API integration
514
+ export function saveMessage(accountId, data) {
515
+ return insertMessage({
516
+ ...data,
517
+ account_id: accountId,
518
+ imap_folder_path: 'INBOX',
519
+ uid: Date.now(), // Fake UID for Gmail API messages
520
+ source: 'gmail_api'
521
+ });
522
+ }
523
+
494
524
  export function insertAttachments(messageId, attachments) {
495
525
  const db = getDb();
496
526
  const insert = db.prepare(`
@@ -91,7 +91,7 @@ export async function getUnreadImportant(config, maxResults = 30) {
91
91
  }
92
92
 
93
93
  // Cache messages locally
94
- cacheMessages(messages);
94
+ await cacheMessages(messages);
95
95
  return messages;
96
96
  }
97
97
 
@@ -352,12 +352,64 @@ function parseMessage(raw) {
352
352
 
353
353
  // ── Local Cache ────────────────────────────────────────────────────────────
354
354
 
355
- function cacheMessages(messages) {
355
+ async function cacheMessages(messages) {
356
+ // Cache as JSON files (legacy ops system)
356
357
  fs.mkdirSync(INBOX_DIR, { recursive: true });
357
358
  for (const msg of messages) {
358
359
  const file = path.join(INBOX_DIR, `${msg.id}.json`);
359
360
  fs.writeFileSync(file, JSON.stringify(msg, null, 2), { mode: 0o600 });
360
361
  }
362
+
363
+ // ENTERPRISE LOCAL-FIRST: Also save to IMAP database for Web UI offline access
364
+ try {
365
+ const { ensureGoogleAccount, saveMessage } = await import('./email-db.mjs');
366
+
367
+ // Ensure Google account exists in IMAP database
368
+ const googleAccount = ensureGoogleAccount({
369
+ id: 'google',
370
+ display_name: 'Gmail',
371
+ email_address: messages[0]?.to || 'gmail@account',
372
+ imap_host: 'gmail.googleapis.com',
373
+ imap_port: 993,
374
+ imap_secure: true,
375
+ is_active: 1
376
+ });
377
+
378
+ // Save each message to IMAP database
379
+ for (const msg of messages) {
380
+ saveMessage(googleAccount.id, {
381
+ message_id: msg.id,
382
+ thread_id: msg.threadId || msg.id,
383
+ subject: msg.subject || '(no subject)',
384
+ from_name: extractName(msg.from),
385
+ from_address: extractEmail(msg.from),
386
+ to_addresses: msg.to || '',
387
+ body_text: msg.body || '',
388
+ body_preview: msg.snippet || '',
389
+ internal_date: new Date(msg.date || Date.now()).toISOString(),
390
+ is_read: !msg.isUnread,
391
+ is_starred: !!msg.isImportant,
392
+ labels: (msg.labels || []).join(','),
393
+ has_attachments: false
394
+ });
395
+ }
396
+ } catch (e) {
397
+ // Fallback gracefully if IMAP DB is not available
398
+ console.warn('[GMAIL CACHE] Failed to save to IMAP database:', e.message);
399
+ }
400
+ }
401
+
402
+ // Helper functions for email parsing
403
+ function extractName(emailField) {
404
+ if (!emailField) return '';
405
+ const match = emailField.match(/^(.+?)\s*<.+>$/);
406
+ return match ? match[1].trim().replace(/['"]/g, '') : '';
407
+ }
408
+
409
+ function extractEmail(emailField) {
410
+ if (!emailField) return '';
411
+ const match = emailField.match(/<(.+?)>$/);
412
+ return match ? match[1] : emailField;
361
413
  }
362
414
 
363
415
  /**