@agenticmail/enterprise 0.2.1
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/ARCHITECTURE.md +183 -0
- package/agenticmail-enterprise.db +0 -0
- package/dashboards/README.md +120 -0
- package/dashboards/dotnet/Program.cs +261 -0
- package/dashboards/express/app.js +146 -0
- package/dashboards/go/main.go +513 -0
- package/dashboards/html/index.html +535 -0
- package/dashboards/java/AgenticMailDashboard.java +376 -0
- package/dashboards/php/index.php +414 -0
- package/dashboards/python/app.py +273 -0
- package/dashboards/ruby/app.rb +195 -0
- package/dist/chunk-77IDQJL3.js +7 -0
- package/dist/chunk-7RGCCHIT.js +115 -0
- package/dist/chunk-DXNKR3TG.js +1355 -0
- package/dist/chunk-IQWA44WT.js +970 -0
- package/dist/chunk-LCUZGIDH.js +965 -0
- package/dist/chunk-N2JVTNNJ.js +2553 -0
- package/dist/chunk-O462UJBH.js +363 -0
- package/dist/chunk-PNKVD2UK.js +26 -0
- package/dist/cli.js +218 -0
- package/dist/dashboard/index.html +558 -0
- package/dist/db-adapter-DEWEFNIV.js +7 -0
- package/dist/dynamodb-CCGL2E77.js +426 -0
- package/dist/engine/index.js +1261 -0
- package/dist/index.js +522 -0
- package/dist/mongodb-ODTXIVPV.js +319 -0
- package/dist/mysql-RM3S2FV5.js +521 -0
- package/dist/postgres-LN7A6MGQ.js +518 -0
- package/dist/routes-2JEPIIKC.js +441 -0
- package/dist/routes-74ZLKJKP.js +399 -0
- package/dist/server.js +7 -0
- package/dist/sqlite-3K5YOZ4K.js +439 -0
- package/dist/turso-LDWODSDI.js +442 -0
- package/package.json +49 -0
- package/src/admin/routes.ts +331 -0
- package/src/auth/routes.ts +130 -0
- package/src/cli.ts +260 -0
- package/src/dashboard/index.html +558 -0
- package/src/db/adapter.ts +230 -0
- package/src/db/dynamodb.ts +456 -0
- package/src/db/factory.ts +51 -0
- package/src/db/mongodb.ts +360 -0
- package/src/db/mysql.ts +472 -0
- package/src/db/postgres.ts +479 -0
- package/src/db/sql-schema.ts +123 -0
- package/src/db/sqlite.ts +391 -0
- package/src/db/turso.ts +411 -0
- package/src/deploy/fly.ts +368 -0
- package/src/deploy/managed.ts +213 -0
- package/src/engine/activity.ts +474 -0
- package/src/engine/agent-config.ts +429 -0
- package/src/engine/agenticmail-bridge.ts +296 -0
- package/src/engine/approvals.ts +278 -0
- package/src/engine/db-adapter.ts +682 -0
- package/src/engine/db-schema.ts +335 -0
- package/src/engine/deployer.ts +595 -0
- package/src/engine/index.ts +134 -0
- package/src/engine/knowledge.ts +486 -0
- package/src/engine/lifecycle.ts +635 -0
- package/src/engine/openclaw-hook.ts +371 -0
- package/src/engine/routes.ts +528 -0
- package/src/engine/skills.ts +473 -0
- package/src/engine/tenant.ts +345 -0
- package/src/engine/tool-catalog.ts +189 -0
- package/src/index.ts +64 -0
- package/src/lib/resilience.ts +326 -0
- package/src/middleware/index.ts +286 -0
- package/src/server.ts +310 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgenticMail Enterprise Dashboard — Express.js Edition
|
|
3
|
+
*
|
|
4
|
+
* Setup:
|
|
5
|
+
* npm install express express-session
|
|
6
|
+
* node app.js
|
|
7
|
+
*
|
|
8
|
+
* Or: AGENTICMAIL_URL=https://your-company.agenticmail.cloud node app.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const express = require('express');
|
|
12
|
+
const session = require('express-session');
|
|
13
|
+
const { randomUUID } = require('crypto');
|
|
14
|
+
|
|
15
|
+
const app = express();
|
|
16
|
+
const API_URL = process.env.AGENTICMAIL_URL || 'http://localhost:3000';
|
|
17
|
+
|
|
18
|
+
app.use(express.urlencoded({ extended: true }));
|
|
19
|
+
app.use(express.json());
|
|
20
|
+
app.use(session({ secret: randomUUID(), resave: false, saveUninitialized: false }));
|
|
21
|
+
|
|
22
|
+
// ─── API Client ─────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
async function api(path, token, method = 'GET', body) {
|
|
25
|
+
const opts = {
|
|
26
|
+
method, headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
};
|
|
28
|
+
if (token) opts.headers['Authorization'] = `Bearer ${token}`;
|
|
29
|
+
if (body) opts.body = JSON.stringify(body);
|
|
30
|
+
try {
|
|
31
|
+
const r = await fetch(`${API_URL}${path}`, opts);
|
|
32
|
+
return await r.json();
|
|
33
|
+
} catch (e) {
|
|
34
|
+
return { error: e.message };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── Auth Middleware ─────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
function requireAuth(req, res, next) {
|
|
41
|
+
if (!req.session.token) return res.redirect('/login');
|
|
42
|
+
next();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ─── Shared Layout ──────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
function page(p, user, content, flash) {
|
|
48
|
+
const nav = (href, icon, label, key) =>
|
|
49
|
+
`<a href="${href}" class="${p === key ? 'on' : ''}">${icon} <span>${label}</span></a>`;
|
|
50
|
+
const flashHtml = flash ? `<div style="padding:12px 16px;border-radius:8px;margin-bottom:16px;font-size:13px;background:rgba(34,197,94,0.1);border:1px solid #22c55e;color:#22c55e">${flash}</div>` : '';
|
|
51
|
+
return `<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>AgenticMail Enterprise — Express</title>
|
|
52
|
+
<style>*{box-sizing:border-box;margin:0;padding:0}:root{--bg:#0a0a0f;--surface:#12121a;--border:#1e1e2e;--text:#e4e4ef;--dim:#8888a0;--muted:#55556a;--primary:#6366f1;--success:#22c55e;--danger:#ef4444}body{font-family:-apple-system,sans-serif;background:var(--bg);color:var(--text)}.layout{display:flex;min-height:100vh}.sidebar{width:240px;background:var(--surface);border-right:1px solid var(--border);position:fixed;top:0;left:0;bottom:0;display:flex;flex-direction:column}.sh{padding:20px;border-bottom:1px solid var(--border)}.sh h2{font-size:16px}.sh h2 em{font-style:normal;color:var(--primary)}.sh small{font-size:11px;color:var(--muted);display:block;margin-top:2px}.nav{flex:1;padding:8px 0}.ns{font-size:10px;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);padding:12px 20px 4px}.nav a{display:flex;align-items:center;gap:10px;padding:10px 20px;color:var(--dim);text-decoration:none;font-size:13px}.nav a:hover{color:var(--text);background:rgba(255,255,255,0.03)}.nav a.on{color:var(--primary);background:rgba(99,102,241,0.12);border-right:2px solid var(--primary)}.sf{padding:16px 20px;border-top:1px solid var(--border);font-size:12px}.content{flex:1;margin-left:240px;padding:32px;max-width:1100px}h2.t{font-size:22px;font-weight:700;margin-bottom:4px}.desc{font-size:13px;color:var(--dim);margin-bottom:24px}.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;margin-bottom:24px}.stat{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:20px}.stat .l{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:0.06em}.stat .v{font-size:30px;font-weight:700;margin-top:4px}.card{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:20px;margin-bottom:16px}.ct{font-size:13px;color:var(--dim);text-transform:uppercase;letter-spacing:0.05em;font-weight:600;margin-bottom:12px}table{width:100%;border-collapse:collapse;font-size:13px}th{text-align:left;padding:10px 12px;color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:0.05em;border-bottom:1px solid var(--border)}td{padding:12px;border-bottom:1px solid var(--border)}.badge{display:inline-block;padding:2px 10px;border-radius:999px;font-size:11px;font-weight:600}.b-a{background:rgba(34,197,94,0.12);color:var(--success)}.b-r{background:rgba(136,136,160,0.1);color:var(--dim)}.empty{text-align:center;padding:48px 20px;color:var(--muted)}.btn{display:inline-flex;align-items:center;padding:8px 16px;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;border:1px solid var(--border);background:var(--surface);color:var(--text);text-decoration:none}.btn-p{background:var(--primary);border-color:var(--primary);color:#fff}.input{width:100%;padding:10px 14px;background:var(--bg);border:1px solid var(--border);border-radius:8px;color:var(--text);font-size:14px}.fg{margin-bottom:14px}.fl{display:block;font-size:12px;color:var(--dim);margin-bottom:4px}</style></head>
|
|
53
|
+
<body><div class="layout">
|
|
54
|
+
<div class="sidebar"><div class="sh"><h2>🏢 <em>Agentic</em>Mail</h2><small>Enterprise · Express</small></div>
|
|
55
|
+
<div class="nav"><div class="ns">Overview</div>${nav('/', '📊', 'Dashboard', 'dashboard')}
|
|
56
|
+
<div class="ns">Manage</div>${nav('/agents', '🤖', 'Agents', 'agents')}${nav('/users', '👥', 'Users', 'users')}${nav('/api-keys', '🔑', 'API Keys', 'keys')}
|
|
57
|
+
<div class="ns">System</div>${nav('/audit', '📋', 'Audit Log', 'audit')}${nav('/settings', '⚙️', 'Settings', 'settings')}</div>
|
|
58
|
+
<div class="sf"><div style="color:var(--dim)">${esc(user?.name)}</div><div style="color:var(--muted);font-size:11px">${esc(user?.email)}</div><a href="/logout" style="color:var(--muted);font-size:11px;margin-top:6px;display:inline-block">Sign out</a></div></div>
|
|
59
|
+
<div class="content">${flashHtml}${content}</div></div></body></html>`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function esc(s) { return (s || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); }
|
|
63
|
+
function badge(status) {
|
|
64
|
+
const cls = ['active', 'owner', 'admin'].includes(status) ? 'b-a' : 'b-r';
|
|
65
|
+
return `<span class="badge ${cls}">${status}</span>`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── Routes ─────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
app.get('/login', (req, res) => {
|
|
71
|
+
res.send(`<!DOCTYPE html><html><head><meta charset="UTF-8"><title>AgenticMail</title><style>*{box-sizing:border-box;margin:0;padding:0}body{font-family:-apple-system,sans-serif;background:#0a0a0f;color:#e4e4ef;display:flex;align-items:center;justify-content:center;min-height:100vh}.box{width:380px}h1{text-align:center;font-size:22px;margin-bottom:4px}h1 em{font-style:normal;color:#6366f1}.sub{text-align:center;color:#8888a0;font-size:13px;margin-bottom:32px}.fg{margin-bottom:14px}.fl{display:block;font-size:12px;color:#8888a0;margin-bottom:4px}.input{width:100%;padding:10px 14px;background:#12121a;border:1px solid #1e1e2e;border-radius:8px;color:#e4e4ef;font-size:14px;outline:none}.input:focus{border-color:#6366f1}.btn{width:100%;padding:10px;background:#6366f1;border:none;border-radius:8px;color:#fff;font-size:14px;font-weight:600;cursor:pointer}</style></head><body><div class="box"><h1>🏢 <em>AgenticMail</em> Enterprise</h1><p class="sub">Sign in · Express Dashboard</p><form method="POST" action="/login"><div class="fg"><label class="fl">Email</label><input class="input" type="email" name="email" required></div><div class="fg"><label class="fl">Password</label><input class="input" type="password" name="password" required></div><button class="btn" type="submit">Sign In</button></form></div></body></html>`);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
app.post('/login', async (req, res) => {
|
|
75
|
+
const data = await api('/auth/login', null, 'POST', { email: req.body.email, password: req.body.password });
|
|
76
|
+
if (data.token) { req.session.token = data.token; req.session.user = data.user; return res.redirect('/'); }
|
|
77
|
+
res.send(`Login failed: ${data.error}`);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
app.get('/logout', (req, res) => { req.session.destroy(); res.redirect('/login'); });
|
|
81
|
+
|
|
82
|
+
app.get('/', requireAuth, async (req, res) => {
|
|
83
|
+
const [stats, audit] = await Promise.all([api('/api/stats', req.session.token), api('/api/audit?limit=8', req.session.token)]);
|
|
84
|
+
const events = (audit.events || []).map(e => `<div style="padding:10px 0;border-bottom:1px solid var(--border);font-size:13px"><span style="color:var(--primary);font-weight:500">${esc(e.action)}</span> on ${esc(e.resource)}<div style="font-size:11px;color:var(--muted)">${e.timestamp}</div></div>`).join('');
|
|
85
|
+
res.send(page('dashboard', req.session.user,
|
|
86
|
+
`<h2 class="t">Dashboard</h2><p class="desc">Overview</p>` +
|
|
87
|
+
`<div class="stats"><div class="stat"><div class="l">Total Agents</div><div class="v" style="color:var(--primary)">${stats.totalAgents||0}</div></div><div class="stat"><div class="l">Active Agents</div><div class="v" style="color:var(--success)">${stats.activeAgents||0}</div></div><div class="stat"><div class="l">Users</div><div class="v">${stats.totalUsers||0}</div></div><div class="stat"><div class="l">Audit Events</div><div class="v">${stats.totalAuditEvents||0}</div></div></div>` +
|
|
88
|
+
`<div class="card"><div class="ct">Recent Activity</div>${events || '<div class="empty">No activity yet</div>'}</div>`
|
|
89
|
+
));
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
app.get('/agents', requireAuth, async (req, res) => {
|
|
93
|
+
const data = await api('/api/agents', req.session.token);
|
|
94
|
+
const agents = data.agents || [];
|
|
95
|
+
const rows = agents.map(a => `<tr><td style="font-weight:600">${esc(a.name)}</td><td style="color:var(--dim)">${esc(a.email)}</td><td>${a.role}</td><td>${badge(a.status)}</td></tr>`).join('');
|
|
96
|
+
res.send(page('agents', req.session.user,
|
|
97
|
+
`<h2 class="t">Agents</h2><p class="desc">Manage AI agent identities</p>` +
|
|
98
|
+
`<div class="card">${agents.length ? `<table><thead><tr><th>Name</th><th>Email</th><th>Role</th><th>Status</th></tr></thead><tbody>${rows}</tbody></table>` : '<div class="empty">🤖 No agents yet</div>'}</div>`
|
|
99
|
+
));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
app.get('/users', requireAuth, async (req, res) => {
|
|
103
|
+
const data = await api('/api/users', req.session.token);
|
|
104
|
+
const users = data.users || [];
|
|
105
|
+
const rows = users.map(u => `<tr><td style="font-weight:600">${esc(u.name)}</td><td style="color:var(--dim)">${esc(u.email)}</td><td>${badge(u.role)}</td></tr>`).join('');
|
|
106
|
+
res.send(page('users', req.session.user,
|
|
107
|
+
`<h2 class="t">Users</h2><p class="desc">Manage team members</p>` +
|
|
108
|
+
`<div class="card">${users.length ? `<table><thead><tr><th>Name</th><th>Email</th><th>Role</th></tr></thead><tbody>${rows}</tbody></table>` : '<div class="empty">👥 No users yet</div>'}</div>`
|
|
109
|
+
));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
app.get('/api-keys', requireAuth, async (req, res) => {
|
|
113
|
+
const data = await api('/api/api-keys', req.session.token);
|
|
114
|
+
const keys = data.keys || [];
|
|
115
|
+
const rows = keys.map(k => `<tr><td style="font-weight:600">${esc(k.name)}</td><td><code style="font-size:12px">${k.keyPrefix}...</code></td><td>${badge(k.revoked ? 'revoked' : 'active')}</td></tr>`).join('');
|
|
116
|
+
res.send(page('keys', req.session.user,
|
|
117
|
+
`<h2 class="t">API Keys</h2><p class="desc">Manage programmatic access</p>` +
|
|
118
|
+
`<div class="card">${keys.length ? `<table><thead><tr><th>Name</th><th>Key</th><th>Status</th></tr></thead><tbody>${rows}</tbody></table>` : '<div class="empty">🔑 No API keys</div>'}</div>`
|
|
119
|
+
));
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
app.get('/audit', requireAuth, async (req, res) => {
|
|
123
|
+
const p = Math.max(0, parseInt(req.query.p) || 0);
|
|
124
|
+
const data = await api(`/api/audit?limit=25&offset=${p*25}`, req.session.token);
|
|
125
|
+
const events = data.events || [];
|
|
126
|
+
const rows = events.map(e => `<tr><td style="font-size:12px;color:var(--muted)">${e.timestamp}</td><td>${esc(e.actor)}</td><td style="color:var(--primary);font-weight:500">${esc(e.action)}</td><td style="font-size:12px">${esc(e.resource)}</td></tr>`).join('');
|
|
127
|
+
res.send(page('audit', req.session.user,
|
|
128
|
+
`<h2 class="t">Audit Log</h2><p class="desc">${data.total||0} events</p>` +
|
|
129
|
+
`<div class="card">${events.length ? `<table><thead><tr><th>Time</th><th>Actor</th><th>Action</th><th>Resource</th></tr></thead><tbody>${rows}</tbody></table>` : '<div class="empty">📋 No events</div>'}</div>`
|
|
130
|
+
));
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
app.get('/settings', requireAuth, async (req, res) => {
|
|
134
|
+
const s = await api('/api/settings', req.session.token);
|
|
135
|
+
res.send(page('settings', req.session.user,
|
|
136
|
+
`<h2 class="t">Settings</h2><p class="desc">Configure your organization</p>` +
|
|
137
|
+
`<div class="card"><div class="ct">General</div><div style="font-size:13px">Name: ${esc(s.name)}<br>Domain: ${esc(s.domain)}<br>Plan: ${badge((s.plan||'free').toUpperCase())}<br>Subdomain: ${esc(s.subdomain)}.agenticmail.cloud</div></div>`
|
|
138
|
+
));
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const PORT = process.env.PORT || 5001;
|
|
142
|
+
app.listen(PORT, () => {
|
|
143
|
+
console.log(`\n🏢 AgenticMail Enterprise Dashboard (Express.js)`);
|
|
144
|
+
console.log(` API: ${API_URL}`);
|
|
145
|
+
console.log(` Dashboard: http://localhost:${PORT}\n`);
|
|
146
|
+
});
|