nothumanallowed 14.1.65 → 14.1.67
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 +2 -1
- package/src/constants.mjs +1 -1
- package/src/server/index.mjs +13 -0
- package/src/server/routes/email.mjs +10 -3
- package/src/services/email-db.mjs +39 -39
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "14.1.
|
|
3
|
+
"version": "14.1.67",
|
|
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/commands/ui.mjs
CHANGED
|
@@ -22,8 +22,9 @@ export async function cmdUI(args) {
|
|
|
22
22
|
port = parseInt(arg.split('=')[1], 10) || DEFAULT_PORT;
|
|
23
23
|
} else if (arg === '--no-browser') {
|
|
24
24
|
noBrowser = true;
|
|
25
|
-
} else if (arg === '--lan' || arg === '--host') {
|
|
25
|
+
} else if (arg === '--lan' || arg === '-lan' || arg === '--host' || arg === '-host' || arg === 'lan') {
|
|
26
26
|
explicitHost = '0.0.0.0';
|
|
27
|
+
noBrowser = true;
|
|
27
28
|
} else if (arg.startsWith('--host=')) {
|
|
28
29
|
explicitHost = arg.split('=')[1] || '0.0.0.0';
|
|
29
30
|
} else if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(arg)) {
|
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.
|
|
8
|
+
export const VERSION = '14.1.67';
|
|
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/server/index.mjs
CHANGED
|
@@ -391,6 +391,19 @@ export async function startServer({ port = 3847, host = '127.0.0.1', noBrowser =
|
|
|
391
391
|
const { VERSION } = await import('../constants.mjs');
|
|
392
392
|
console.log(`\n ${BOLD}${G}NHA${NC} ${D}v${VERSION}${NC}`);
|
|
393
393
|
console.log(` ${G}✓${NC} Server running on ${G}http://${host}:${port}${NC}`);
|
|
394
|
+
if (host === '0.0.0.0') {
|
|
395
|
+
try {
|
|
396
|
+
const os = await import('os');
|
|
397
|
+
const nets = os.default.networkInterfaces();
|
|
398
|
+
for (const iface of Object.values(nets)) {
|
|
399
|
+
for (const info of iface || []) {
|
|
400
|
+
if (info.family === 'IPv4' && !info.internal) {
|
|
401
|
+
console.log(` ${G}✓${NC} LAN: ${G}http://${info.address}:${port}${NC}`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
} catch {}
|
|
406
|
+
}
|
|
394
407
|
|
|
395
408
|
// ENTERPRISE STARTUP DIAGNOSTIC: Check Google OAuth configuration
|
|
396
409
|
try {
|
|
@@ -440,17 +440,24 @@ export function register(router) {
|
|
|
440
440
|
|
|
441
441
|
router.get('/api/imap/messages', async (req, res) => {
|
|
442
442
|
try {
|
|
443
|
-
const
|
|
443
|
+
const emailDb = await import('../../services/email-db.mjs');
|
|
444
|
+
if (!emailDb.SQLITE_AVAILABLE) {
|
|
445
|
+
return sendJSON(res, 200, { messages: [], total: 0, error: 'SQLite not available' });
|
|
446
|
+
}
|
|
444
447
|
const url = new URL(req.url, 'http://localhost');
|
|
445
448
|
const accountId = url.searchParams.get('accountId');
|
|
449
|
+
if (!accountId) return sendJSON(res, 200, { messages: [], total: 0 });
|
|
446
450
|
const labelId = url.searchParams.get('labelId') || null;
|
|
447
451
|
const folder = url.searchParams.get('folder') || null;
|
|
448
452
|
const limit = parseInt(url.searchParams.get('limit') || '50');
|
|
449
453
|
const offset = parseInt(url.searchParams.get('offset') || '0');
|
|
450
454
|
const search = url.searchParams.get('search') || '';
|
|
451
|
-
const result = listMessages(accountId, labelId, limit, offset, search, folder);
|
|
455
|
+
const result = emailDb.listMessages(accountId, labelId, limit, offset, search, folder);
|
|
452
456
|
sendJSON(res, 200, { messages: result.messages ?? result, total: result.total ?? 0 });
|
|
453
|
-
} catch (e) {
|
|
457
|
+
} catch (e) {
|
|
458
|
+
console.error('[/api/imap/messages] Error:', e.message);
|
|
459
|
+
sendJSON(res, 200, { messages: [], total: 0, error: e.message });
|
|
460
|
+
}
|
|
454
461
|
});
|
|
455
462
|
|
|
456
463
|
router.get('/api/imap/message', async (req, res) => {
|
|
@@ -571,51 +571,51 @@ export function listMessages(accountId, labelId, limit, offset, search, folder)
|
|
|
571
571
|
const params = [accountId];
|
|
572
572
|
|
|
573
573
|
if (labelId) {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
574
|
+
let labelFilterApplied = false;
|
|
575
|
+
try {
|
|
576
|
+
const lbl = db.prepare('SELECT system_type FROM email_labels WHERE id = ?').get(labelId);
|
|
577
|
+
if (lbl?.system_type) {
|
|
578
|
+
// System label — find matching folders by folder_type and name patterns
|
|
579
|
+
const knownNames = {
|
|
580
|
+
inbox: ['inbox'],
|
|
581
|
+
sent: ['sent', 'sent messages', 'sent items', 'posta inviata', 'inviati', 'inviata'],
|
|
582
|
+
drafts: ['drafts', 'draft', 'bozze'],
|
|
583
|
+
spam: ['spam', 'junk', 'junk e-mail', 'posta indesiderata'],
|
|
584
|
+
trash: ['trash', 'cestino', 'deleted', 'deleted messages', 'deleted items'],
|
|
585
|
+
starred: ['starred'],
|
|
586
|
+
archived: ['archived', 'archive', 'all mail'],
|
|
587
|
+
};
|
|
588
|
+
const names = knownNames[lbl.system_type] || [lbl.system_type];
|
|
589
|
+
|
|
590
|
+
// For inbox, use exact path match to avoid INBOX.Sent etc.
|
|
591
|
+
let folderIds = [];
|
|
592
|
+
if (lbl.system_type === 'inbox') {
|
|
593
|
+
const exact = db.prepare('SELECT id FROM email_folders WHERE account_id = ? AND UPPER(path) = ?').get(accountId, 'INBOX');
|
|
594
|
+
if (exact) folderIds = [exact.id];
|
|
595
|
+
}
|
|
596
|
+
// For other types, match by folder_type or name LIKE
|
|
597
|
+
if (folderIds.length === 0) {
|
|
598
|
+
const nameConditions = names.map(() => 'LOWER(f.path) LIKE ? OR LOWER(f.name) LIKE ?').join(' OR ');
|
|
599
|
+
const sql = `SELECT id FROM email_folders f WHERE f.account_id = ? AND (f.folder_type = ? OR ${nameConditions})`;
|
|
600
|
+
const sqlParams = [accountId, lbl.system_type, ...names.flatMap(n => [`%${n}%`, `%${n}%`])];
|
|
601
|
+
folderIds = db.prepare(sql).all(...sqlParams).map(f => f.id);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (folderIds.length > 0) {
|
|
605
|
+
const ph = folderIds.map(() => '?').join(',');
|
|
606
|
+
where += ` AND (m.folder_id IN (${ph}) OR EXISTS (SELECT 1 FROM email_message_labels j WHERE j.message_id = m.id AND j.label_id = ?))`;
|
|
607
|
+
params.push(...folderIds, labelId);
|
|
608
|
+
labelFilterApplied = true;
|
|
609
|
+
}
|
|
601
610
|
}
|
|
611
|
+
} catch { /* email_folders table might not exist on older DBs */ }
|
|
602
612
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
where += ` AND (m.folder_id IN (${placeholders}) OR EXISTS (SELECT 1 FROM email_message_labels j WHERE j.message_id = m.id AND j.label_id = ?))`;
|
|
606
|
-
params.push(...filtered.map(f => f.id), labelId);
|
|
607
|
-
} else {
|
|
608
|
-
// No folder found at all — fallback to message_labels join + LIKE on path
|
|
609
|
-
where += ' AND (EXISTS (SELECT 1 FROM email_message_labels j WHERE j.message_id = m.id AND j.label_id = ?) OR LOWER(m.imap_folder_path) LIKE ?)';
|
|
610
|
-
params.push(labelId, `%${lbl.system_type}%`);
|
|
611
|
-
}
|
|
612
|
-
} else {
|
|
613
|
-
// Custom label — only use message_labels join
|
|
613
|
+
if (!labelFilterApplied) {
|
|
614
|
+
// Fallback: use message_labels join only
|
|
614
615
|
where += ' AND EXISTS (SELECT 1 FROM email_message_labels j WHERE j.message_id = m.id AND j.label_id = ?)';
|
|
615
616
|
params.push(labelId);
|
|
616
617
|
}
|
|
617
618
|
} else if (folder) {
|
|
618
|
-
// Filter by folder path (used by Gmail — messages stored with imap_folder_path like INBOX, SENT, etc.)
|
|
619
619
|
where += ' AND m.imap_folder_path = ?';
|
|
620
620
|
params.push(folder.toUpperCase());
|
|
621
621
|
}
|