@agenticmail/enterprise 0.5.228 → 0.5.230
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/dist/chunk-DFDEYMA5.js +3759 -0
- package/dist/chunk-EKAHAAGY.js +1224 -0
- package/dist/cli-serve-YEDRINZV.js +114 -0
- package/dist/cli.js +2 -2
- package/dist/dashboard/app.js +2 -2
- package/dist/dashboard/assets/icons/emoji-icons.js +1 -1
- package/dist/dashboard/index.html +8 -0
- package/dist/dashboard/pages/agent-detail/memory.js +1 -1
- package/dist/dashboard/pages/agent-detail/overview.js +1 -1
- package/dist/dashboard/pages/agent-detail/permissions.js +1 -1
- package/dist/dashboard/pages/agent-detail/shared.js +1 -1
- package/dist/dashboard/pages/agent-detail/whatsapp.js +1 -1
- package/dist/dashboard/pages/guardrails.js +2 -2
- package/dist/dashboard/pages/knowledge-contributions.js +1 -1
- package/dist/dashboard/pages/login.js +8 -4
- package/dist/dashboard/pages/settings.js +100 -0
- package/dist/index.js +2 -2
- package/dist/server-FHXIWJUB.js +15 -0
- package/dist/setup-FNMKPXP2.js +20 -0
- package/package.json +1 -1
- package/src/admin/routes.ts +74 -0
- package/src/dashboard/app.js +2 -2
- package/src/dashboard/assets/icons/emoji-icons.js +1 -1
- package/src/dashboard/index.html +8 -0
- package/src/dashboard/pages/agent-detail/memory.js +1 -1
- package/src/dashboard/pages/agent-detail/overview.js +1 -1
- package/src/dashboard/pages/agent-detail/permissions.js +1 -1
- package/src/dashboard/pages/agent-detail/shared.js +1 -1
- package/src/dashboard/pages/agent-detail/whatsapp.js +1 -1
- package/src/dashboard/pages/guardrails.js +2 -2
- package/src/dashboard/pages/knowledge-contributions.js +1 -1
- package/src/dashboard/pages/login.js +8 -4
- package/src/dashboard/pages/settings.js +100 -0
- package/src/server.ts +25 -0
|
@@ -563,7 +563,7 @@ function ConversationsCard(props) {
|
|
|
563
563
|
!loading && convos.length > 0 && h('div', null,
|
|
564
564
|
convos.map(function(c) {
|
|
565
565
|
var initial = (c.name || c.contactId || '?').charAt(0).toUpperCase();
|
|
566
|
-
var colors = ['#6366f1', '#
|
|
566
|
+
var colors = ['#6366f1', '#9d174d', '#f59e0b', '#15803d', '#8b5cf6', '#ef4444', '#06b6d4', '#84cc16'];
|
|
567
567
|
var colorIdx = (initial.charCodeAt(0) || 0) % colors.length;
|
|
568
568
|
return h('div', { key: c.contactId,
|
|
569
569
|
style: { display: 'flex', alignItems: 'center', gap: '12px', padding: '12px', borderRadius: '10px', cursor: 'pointer', transition: 'background 0.15s', marginBottom: '2px' },
|
|
@@ -8,7 +8,7 @@ var POLICY_CATEGORIES = [
|
|
|
8
8
|
{ value: 'code_of_conduct', label: 'Code of Conduct', color: '#6366f1' },
|
|
9
9
|
{ value: 'communication', label: 'Communication', color: '#0ea5e9' },
|
|
10
10
|
{ value: 'data_handling', label: 'Data Handling', color: '#f59e0b' },
|
|
11
|
-
{ value: 'brand_voice', label: 'Brand Voice', color: '#
|
|
11
|
+
{ value: 'brand_voice', label: 'Brand Voice', color: '#9d174d' },
|
|
12
12
|
{ value: 'security', label: 'Security', color: '#ef4444' },
|
|
13
13
|
{ value: 'escalation', label: 'Escalation', color: '#8b5cf6' },
|
|
14
14
|
{ value: 'custom', label: 'Custom', color: '#64748b' },
|
|
@@ -27,7 +27,7 @@ var MEMORY_CATEGORIES = [
|
|
|
27
27
|
{ value: 'correction', label: 'Correction', color: '#f59e0b' },
|
|
28
28
|
{ value: 'skill', label: 'Skill', color: '#8b5cf6' },
|
|
29
29
|
{ value: 'context', label: 'Context', color: '#64748b' },
|
|
30
|
-
{ value: 'reflection', label: 'Reflection', color: '#
|
|
30
|
+
{ value: 'reflection', label: 'Reflection', color: '#9d174d' },
|
|
31
31
|
];
|
|
32
32
|
|
|
33
33
|
var RULE_CATEGORIES = [
|
|
@@ -618,7 +618,7 @@ export function KnowledgeContributionsPage() {
|
|
|
618
618
|
useEffect(function() { if (tab === 'stats') loadTimeline(); }, [tab, loadTimeline]);
|
|
619
619
|
|
|
620
620
|
// SVG chart helpers — clean background, dark mode, hover tooltips
|
|
621
|
-
var CHART_COLORS = ['#6366f1', '#15803d', '#f59e0b', '#ef4444', '#8b5cf6', '#06b6d4', '#
|
|
621
|
+
var CHART_COLORS = ['#6366f1', '#15803d', '#f59e0b', '#ef4444', '#8b5cf6', '#06b6d4', '#9d174d', '#14b8a6'];
|
|
622
622
|
var [tooltip, setTooltip] = useState(null); // { x, y, lines: [] }
|
|
623
623
|
|
|
624
624
|
// Shared tooltip overlay (rendered once, positioned absolutely)
|
|
@@ -2,6 +2,10 @@ import { h, useState, useEffect, useCallback, Fragment } from '../components/uti
|
|
|
2
2
|
import { apiCall, authCall, engineCall } from '../components/utils.js';
|
|
3
3
|
import { I } from '../components/icons.js';
|
|
4
4
|
|
|
5
|
+
var _b = typeof window !== 'undefined' && window.__EM_BRANDING__ || {};
|
|
6
|
+
var _brandLogo = _b.login_logo || _b.logo || _brandLogo;
|
|
7
|
+
var _brandBg = _b.login_bg || null;
|
|
8
|
+
|
|
5
9
|
export function LoginPage({ onLogin }) {
|
|
6
10
|
var [tab, setTab] = useState('password'); // 'password' | 'apikey' | 'sso'
|
|
7
11
|
var [email, setEmail] = useState('');
|
|
@@ -76,10 +80,10 @@ export function LoginPage({ onLogin }) {
|
|
|
76
80
|
// ─── 2FA Verification Screen ──────────────────────────
|
|
77
81
|
|
|
78
82
|
if (needs2fa) {
|
|
79
|
-
return h('div', { className: 'login-page' },
|
|
83
|
+
return h('div', { className: 'login-page', style: _brandBg ? { backgroundImage: 'url(' + _brandBg + ')', backgroundSize: 'cover', backgroundPosition: 'center' } : {} },
|
|
80
84
|
h('div', { className: 'login-card' },
|
|
81
85
|
h('div', { className: 'login-logo' },
|
|
82
|
-
h('img', { src:
|
|
86
|
+
h('img', { src: _brandLogo, alt: 'AgenticMail', style: { width: 48, height: 48, objectFit: 'contain' } }),
|
|
83
87
|
h('h1', null, 'Two-Factor Authentication'),
|
|
84
88
|
h('p', null, 'Enter the code from your authenticator app')
|
|
85
89
|
),
|
|
@@ -108,10 +112,10 @@ export function LoginPage({ onLogin }) {
|
|
|
108
112
|
|
|
109
113
|
// ─── Main Login Screen ────────────────────────────────
|
|
110
114
|
|
|
111
|
-
return h('div', { className: 'login-page' },
|
|
115
|
+
return h('div', { className: 'login-page', style: _brandBg ? { backgroundImage: 'url(' + _brandBg + ')', backgroundSize: 'cover', backgroundPosition: 'center' } : {} },
|
|
112
116
|
h('div', { className: 'login-card' },
|
|
113
117
|
h('div', { className: 'login-logo' },
|
|
114
|
-
h('img', { src:
|
|
118
|
+
h('img', { src: _brandLogo, alt: 'AgenticMail', style: { width: 48, height: 48, objectFit: 'contain' } }),
|
|
115
119
|
h('h1', null, 'AgenticMail Enterprise'),
|
|
116
120
|
h('p', null, 'AI Agent Identity & Management Platform')
|
|
117
121
|
),
|
|
@@ -279,6 +279,106 @@ export function SettingsPage() {
|
|
|
279
279
|
)
|
|
280
280
|
),
|
|
281
281
|
|
|
282
|
+
// ─── Branding & Assets ──────────────────────────────
|
|
283
|
+
h('div', { className: 'card', style: { marginBottom: 16 } },
|
|
284
|
+
h('div', { className: 'card-header' }, h('h3', { style: { display: 'flex', alignItems: 'center' } }, 'Branding & Assets', h(HelpButton, { label: 'Branding & Assets' },
|
|
285
|
+
h('p', null, 'Upload your company logo, favicon, and login page assets. The system automatically generates all required icon sizes (16px, 32px, 48px, 180px, 192px, 512px) and favicon from your logo.'),
|
|
286
|
+
h('p', { style: { marginTop: 8 } }, h('strong', null, 'Supported formats: '), 'PNG, JPG, SVG, WebP, GIF'),
|
|
287
|
+
h('p', { style: { marginTop: 8, padding: 8, background: 'var(--bg-secondary)', borderRadius: 6, fontSize: 13 } }, h('strong', null, 'Tip: '), 'Upload a square PNG logo (512x512 or larger) for best results. The system auto-converts it to favicon.ico and all app icon sizes.')
|
|
288
|
+
))),
|
|
289
|
+
h('div', { className: 'card-body' },
|
|
290
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 } },
|
|
291
|
+
// Company Logo
|
|
292
|
+
h('div', { className: 'form-group' },
|
|
293
|
+
h('label', { className: 'form-label' }, 'Company Logo'),
|
|
294
|
+
h('p', { className: 'form-help', style: { marginBottom: 8 } }, 'Used in dashboard sidebar, emails, and auto-generates favicon + app icons'),
|
|
295
|
+
(settings.branding && settings.branding.logo) && h('div', { style: { marginBottom: 8, padding: 8, background: 'var(--bg-tertiary)', borderRadius: 'var(--radius)', display: 'inline-block' } },
|
|
296
|
+
h('img', { src: settings.branding.logo, style: { maxWidth: 120, maxHeight: 60, objectFit: 'contain' } })
|
|
297
|
+
),
|
|
298
|
+
h('input', { type: 'file', accept: 'image/*', style: { fontSize: 12 }, onChange: function(e) {
|
|
299
|
+
var file = e.target.files && e.target.files[0];
|
|
300
|
+
if (!file) return;
|
|
301
|
+
if (file.size > 5 * 1024 * 1024) { toast('File too large (max 5MB)', 'error'); return; }
|
|
302
|
+
var reader = new FileReader();
|
|
303
|
+
reader.onload = function() {
|
|
304
|
+
apiCall('/settings/branding', { method: 'POST', body: JSON.stringify({ type: 'logo', data: reader.result, filename: file.name }) })
|
|
305
|
+
.then(function(r) { setSettings(function(s) { return Object.assign({}, s, { branding: r.branding }); }); toast('Logo uploaded! Favicon and icons auto-generated. Refresh to see changes.', 'success'); })
|
|
306
|
+
.catch(function(err) { toast(err.message, 'error'); });
|
|
307
|
+
};
|
|
308
|
+
reader.readAsDataURL(file);
|
|
309
|
+
} })
|
|
310
|
+
),
|
|
311
|
+
// Login Page Logo (separate from main logo)
|
|
312
|
+
h('div', { className: 'form-group' },
|
|
313
|
+
h('label', { className: 'form-label' }, 'Login Page Logo'),
|
|
314
|
+
h('p', { className: 'form-help', style: { marginBottom: 8 } }, 'Shown on the login page. Falls back to company logo if not set.'),
|
|
315
|
+
(settings.branding && settings.branding.login_logo) && h('div', { style: { marginBottom: 8, padding: 8, background: 'var(--bg-tertiary)', borderRadius: 'var(--radius)', display: 'inline-block' } },
|
|
316
|
+
h('img', { src: settings.branding.login_logo, style: { maxWidth: 120, maxHeight: 60, objectFit: 'contain' } })
|
|
317
|
+
),
|
|
318
|
+
h('input', { type: 'file', accept: 'image/*', style: { fontSize: 12 }, onChange: function(e) {
|
|
319
|
+
var file = e.target.files && e.target.files[0];
|
|
320
|
+
if (!file) return;
|
|
321
|
+
if (file.size > 5 * 1024 * 1024) { toast('File too large (max 5MB)', 'error'); return; }
|
|
322
|
+
var reader = new FileReader();
|
|
323
|
+
reader.onload = function() {
|
|
324
|
+
apiCall('/settings/branding', { method: 'POST', body: JSON.stringify({ type: 'login_logo', data: reader.result, filename: file.name }) })
|
|
325
|
+
.then(function(r) { setSettings(function(s) { return Object.assign({}, s, { branding: r.branding }); }); toast('Login logo saved!', 'success'); })
|
|
326
|
+
.catch(function(err) { toast(err.message, 'error'); });
|
|
327
|
+
};
|
|
328
|
+
reader.readAsDataURL(file);
|
|
329
|
+
} })
|
|
330
|
+
),
|
|
331
|
+
// Login Background
|
|
332
|
+
h('div', { className: 'form-group' },
|
|
333
|
+
h('label', { className: 'form-label' }, 'Login Page Background'),
|
|
334
|
+
h('p', { className: 'form-help', style: { marginBottom: 8 } }, 'Background image for the login page'),
|
|
335
|
+
(settings.branding && settings.branding.login_bg) && h('div', { style: { marginBottom: 8, padding: 4, background: 'var(--bg-tertiary)', borderRadius: 'var(--radius)', display: 'inline-block' } },
|
|
336
|
+
h('img', { src: settings.branding.login_bg, style: { maxWidth: 160, maxHeight: 80, objectFit: 'cover', borderRadius: 'var(--radius)' } })
|
|
337
|
+
),
|
|
338
|
+
h('input', { type: 'file', accept: 'image/*', style: { fontSize: 12 }, onChange: function(e) {
|
|
339
|
+
var file = e.target.files && e.target.files[0];
|
|
340
|
+
if (!file) return;
|
|
341
|
+
if (file.size > 10 * 1024 * 1024) { toast('File too large (max 10MB)', 'error'); return; }
|
|
342
|
+
var reader = new FileReader();
|
|
343
|
+
reader.onload = function() {
|
|
344
|
+
apiCall('/settings/branding', { method: 'POST', body: JSON.stringify({ type: 'login_bg', data: reader.result, filename: file.name }) })
|
|
345
|
+
.then(function(r) { setSettings(function(s) { return Object.assign({}, s, { branding: r.branding }); }); toast('Login background saved!', 'success'); })
|
|
346
|
+
.catch(function(err) { toast(err.message, 'error'); });
|
|
347
|
+
};
|
|
348
|
+
reader.readAsDataURL(file);
|
|
349
|
+
} })
|
|
350
|
+
),
|
|
351
|
+
// Favicon (manual override)
|
|
352
|
+
h('div', { className: 'form-group' },
|
|
353
|
+
h('label', { className: 'form-label' }, 'Custom Favicon'),
|
|
354
|
+
h('p', { className: 'form-help', style: { marginBottom: 8 } }, 'Override the auto-generated favicon. Upload .ico or .png'),
|
|
355
|
+
(settings.branding && settings.branding.favicon) && h('div', { style: { marginBottom: 8, display: 'inline-flex', alignItems: 'center', gap: 8 } },
|
|
356
|
+
h('img', { src: settings.branding.favicon, style: { width: 32, height: 32, objectFit: 'contain' } }),
|
|
357
|
+
h('span', { style: { fontSize: 11, color: 'var(--text-muted)' } }, 'Current favicon')
|
|
358
|
+
),
|
|
359
|
+
h('input', { type: 'file', accept: '.ico,.png,.svg', style: { fontSize: 12 }, onChange: function(e) {
|
|
360
|
+
var file = e.target.files && e.target.files[0];
|
|
361
|
+
if (!file) return;
|
|
362
|
+
var reader = new FileReader();
|
|
363
|
+
reader.onload = function() {
|
|
364
|
+
apiCall('/settings/branding', { method: 'POST', body: JSON.stringify({ type: 'favicon', data: reader.result, filename: file.name }) })
|
|
365
|
+
.then(function(r) { setSettings(function(s) { return Object.assign({}, s, { branding: r.branding }); }); toast('Favicon saved! Refresh to see changes.', 'success'); })
|
|
366
|
+
.catch(function(err) { toast(err.message, 'error'); });
|
|
367
|
+
};
|
|
368
|
+
reader.readAsDataURL(file);
|
|
369
|
+
} })
|
|
370
|
+
)
|
|
371
|
+
),
|
|
372
|
+
// Current branding status
|
|
373
|
+
(settings.branding && Object.keys(settings.branding).length > 0) && h('div', { style: { marginTop: 12, padding: 10, background: 'var(--bg-tertiary)', borderRadius: 'var(--radius)', fontSize: 12 } },
|
|
374
|
+
h('strong', null, 'Active branding: '),
|
|
375
|
+
Object.keys(settings.branding).filter(function(k) { return settings.branding[k]; }).map(function(k) {
|
|
376
|
+
return h('span', { key: k, style: { display: 'inline-block', padding: '2px 8px', margin: '2px 4px', background: 'var(--success-soft)', color: 'var(--success)', borderRadius: 4, fontSize: 11 } }, k.replace(/_/g, ' '));
|
|
377
|
+
})
|
|
378
|
+
)
|
|
379
|
+
)
|
|
380
|
+
),
|
|
381
|
+
|
|
282
382
|
// ─── Email Signature Template ─────────────────────
|
|
283
383
|
h('div', { className: 'card' },
|
|
284
384
|
h('div', { className: 'card-header' },
|
package/src/server.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type { AppEnv } from './types/hono-env.js';
|
|
|
11
11
|
import { cors } from 'hono/cors';
|
|
12
12
|
import { serve } from '@hono/node-server';
|
|
13
13
|
import { readFileSync, existsSync } from 'fs';
|
|
14
|
+
import { homedir } from 'os';
|
|
14
15
|
import { fileURLToPath } from 'url';
|
|
15
16
|
import { dirname, join } from 'path';
|
|
16
17
|
import { createRequire } from 'module';
|
|
@@ -438,6 +439,15 @@ export function createServer(config: ServerConfig): ServerInstance {
|
|
|
438
439
|
html = html.replace('</head>', injection + '</head>');
|
|
439
440
|
}
|
|
440
441
|
|
|
442
|
+
// Inject branding config
|
|
443
|
+
try {
|
|
444
|
+
const settings0 = await config.db.getSettings();
|
|
445
|
+
if (settings0?.branding) {
|
|
446
|
+
const brandScript = `<script>window.__EM_BRANDING__=${JSON.stringify(settings0.branding)};</script>`;
|
|
447
|
+
html = html.replace('</head>', brandScript + '</head>');
|
|
448
|
+
}
|
|
449
|
+
} catch { /* non-blocking */ }
|
|
450
|
+
|
|
441
451
|
// Inject domain verification status (informational, does not block)
|
|
442
452
|
try {
|
|
443
453
|
const settings = await config.db.getSettings();
|
|
@@ -456,6 +466,21 @@ export function createServer(config: ServerConfig): ServerInstance {
|
|
|
456
466
|
return c.html(html);
|
|
457
467
|
}
|
|
458
468
|
|
|
469
|
+
// Serve branding assets from ~/.agenticmail/branding/
|
|
470
|
+
app.get('/branding/*', (c) => {
|
|
471
|
+
const reqPath = c.req.path.replace('/branding/', '');
|
|
472
|
+
if (reqPath.includes('..')) return c.json({ error: 'Forbidden' }, 403);
|
|
473
|
+
const ext = reqPath.substring(reqPath.lastIndexOf('.'));
|
|
474
|
+
const mimeMap: Record<string, string> = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.svg': 'image/svg+xml', '.ico': 'image/x-icon', '.gif': 'image/gif', '.webp': 'image/webp' };
|
|
475
|
+
const mime = mimeMap[ext];
|
|
476
|
+
if (!mime) return c.json({ error: 'Not found' }, 404);
|
|
477
|
+
const brandDir = join(homedir(), '.agenticmail', 'branding');
|
|
478
|
+
const filePath = join(brandDir, reqPath);
|
|
479
|
+
if (!filePath.startsWith(brandDir) || !existsSync(filePath)) return c.json({ error: 'Not found' }, 404);
|
|
480
|
+
const content = readFileSync(filePath);
|
|
481
|
+
return new Response(content, { status: 200, headers: { 'Content-Type': mime, 'Cache-Control': 'public, max-age=3600' } });
|
|
482
|
+
});
|
|
483
|
+
|
|
459
484
|
app.get('/', (c) => c.redirect('/dashboard'));
|
|
460
485
|
app.get('/dashboard', serveDashboard);
|
|
461
486
|
|