@agenticmail/enterprise 0.2.1 → 0.3.0

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.
Files changed (41) hide show
  1. package/README.md +929 -0
  2. package/dashboards/dotnet/Program.cs +5 -5
  3. package/dashboards/express/app.js +5 -5
  4. package/dashboards/go/main.go +8 -8
  5. package/dashboards/html/index.html +41 -18
  6. package/dashboards/java/AgenticMailDashboard.java +5 -5
  7. package/dashboards/php/index.php +8 -8
  8. package/dashboards/python/app.py +8 -8
  9. package/dashboards/ruby/app.rb +7 -7
  10. package/dashboards/shared-styles.css +57 -0
  11. package/dist/{chunk-LCUZGIDH.js → chunk-BE7MXVLA.js} +48 -256
  12. package/dist/chunk-BS2WCSHO.js +48 -0
  13. package/dist/chunk-FL3VQBGL.js +757 -0
  14. package/dist/chunk-GXIEEA2T.js +48 -0
  15. package/dist/chunk-JLSQOQ5L.js +255 -0
  16. package/dist/{chunk-O462UJBH.js → chunk-TVF23PUW.js} +23 -48
  17. package/dist/{chunk-DXNKR3TG.js → chunk-YFDSE4BW.js} +1 -1
  18. package/dist/cli.js +305 -140
  19. package/dist/dashboard/index.html +833 -510
  20. package/dist/factory-HINWFYZ3.js +9 -0
  21. package/dist/factory-V37IG5AT.js +9 -0
  22. package/dist/index.js +18 -12
  23. package/dist/managed-RZITNPXG.js +14 -0
  24. package/dist/{server.js → server-32YYCI3A.js} +2 -1
  25. package/dist/server-H3C6WUOS.js +8 -0
  26. package/dist/sqlite-VLKVAJA4.js +442 -0
  27. package/package.json +18 -2
  28. package/src/cli.ts +15 -251
  29. package/src/dashboard/index.html +833 -510
  30. package/src/db/sqlite.ts +4 -1
  31. package/src/server.ts +1 -1
  32. package/src/setup/company.ts +64 -0
  33. package/src/setup/database.ts +119 -0
  34. package/src/setup/deployment.ts +50 -0
  35. package/src/setup/domain.ts +46 -0
  36. package/src/setup/index.ts +82 -0
  37. package/src/setup/provision.ts +226 -0
  38. package/test-integration.mjs +383 -0
  39. package/agenticmail-enterprise.db +0 -0
  40. package/dist/engine/index.js +0 -1261
  41. package/dist/routes-74ZLKJKP.js +0 -399
@@ -0,0 +1,57 @@
1
+ /*
2
+ * AgenticMail Enterprise Dashboard — Shared Styles
3
+ * Used by all language implementations (HTML, PHP, Python, Ruby, Express, Go, Java, .NET)
4
+ *
5
+ * Brand: 🎀 AgenticMail — Pink ribbon accent
6
+ * Supports: dark mode, light mode, system auto-detect
7
+ * Customizable: override --accent and --bg-* CSS variables to match your brand
8
+ */
9
+
10
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
11
+
12
+ :root {
13
+ --font: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', system-ui, sans-serif;
14
+ --mono: 'SF Mono', 'Fira Code', 'Consolas', monospace;
15
+ --radius: 6px; --radius-lg: 10px;
16
+ --transition: 150ms cubic-bezier(0.4, 0, 0.2, 1);
17
+ color-scheme: light dark;
18
+ }
19
+
20
+ /* Light Mode */
21
+ :root, [data-theme="light"] {
22
+ --bg-0: #f8f9fa; --bg-1: #ffffff; --bg-2: #f1f3f5; --bg-3: #e9ecef;
23
+ --border: #dee2e6; --border-subtle: #e9ecef;
24
+ --text-0: #212529; --text-1: #495057; --text-2: #868e96; --text-3: #adb5bd;
25
+ --accent: #e84393; --accent-hover: #d63384; --accent-subtle: rgba(232,67,147,0.08); --accent-text: #fff;
26
+ --success: #2b8a3e; --success-subtle: rgba(43,138,62,0.08);
27
+ --warning: #e67700; --warning-subtle: rgba(230,119,0,0.08);
28
+ --danger: #c92a2a; --danger-subtle: rgba(201,42,42,0.08);
29
+ --shadow: 0 1px 3px rgba(0,0,0,0.08);
30
+ }
31
+
32
+ /* Dark Mode */
33
+ [data-theme="dark"] {
34
+ --bg-0: #0f1114; --bg-1: #16181d; --bg-2: #1c1f26; --bg-3: #252930;
35
+ --border: #2c3038; --border-subtle: #23272e;
36
+ --text-0: #e1e4e8; --text-1: #b0b8c4; --text-2: #6b7280; --text-3: #454d5a;
37
+ --accent: #f06595; --accent-hover: #f783ac; --accent-subtle: rgba(240,101,149,0.1); --accent-text: #0f1114;
38
+ --success: #37b24d; --success-subtle: rgba(55,178,77,0.1);
39
+ --warning: #f08c00; --warning-subtle: rgba(240,140,0,0.1);
40
+ --danger: #f03e3e; --danger-subtle: rgba(240,62,62,0.1);
41
+ --shadow: 0 1px 3px rgba(0,0,0,0.3);
42
+ }
43
+
44
+ @media (prefers-color-scheme: dark) {
45
+ :root:not([data-theme="light"]) {
46
+ --bg-0: #0f1114; --bg-1: #16181d; --bg-2: #1c1f26; --bg-3: #252930;
47
+ --border: #2c3038; --border-subtle: #23272e;
48
+ --text-0: #e1e4e8; --text-1: #b0b8c4; --text-2: #6b7280; --text-3: #454d5a;
49
+ --accent: #f06595; --accent-hover: #f783ac; --accent-subtle: rgba(240,101,149,0.1); --accent-text: #0f1114;
50
+ --success: #37b24d; --success-subtle: rgba(55,178,77,0.1);
51
+ --warning: #f08c00; --warning-subtle: rgba(240,140,0,0.1);
52
+ --danger: #f03e3e; --danger-subtle: rgba(240,62,62,0.1);
53
+ --shadow: 0 1px 3px rgba(0,0,0,0.3);
54
+ }
55
+ }
56
+
57
+ body { font-family: var(--font); background: var(--bg-0); color: var(--text-0); line-height: 1.5; font-size: 14px; -webkit-font-smoothing: antialiased; }
@@ -1,3 +1,10 @@
1
+ import {
2
+ CircuitBreaker,
3
+ HealthMonitor,
4
+ KeyedRateLimiter,
5
+ requestId
6
+ } from "./chunk-JLSQOQ5L.js";
7
+
1
8
  // src/server.ts
2
9
  import { Hono as Hono3 } from "hono";
3
10
  import { cors } from "hono/cors";
@@ -9,252 +16,6 @@ import { dirname, join } from "path";
9
16
  // src/admin/routes.ts
10
17
  import { Hono } from "hono";
11
18
 
12
- // src/lib/resilience.ts
13
- var DEFAULT_RETRY = {
14
- maxAttempts: 3,
15
- baseDelayMs: 500,
16
- maxDelayMs: 3e4,
17
- backoffMultiplier: 2
18
- };
19
- async function withRetry(fn, opts = {}) {
20
- const config = { ...DEFAULT_RETRY, ...opts };
21
- let lastError = new Error("No attempts made");
22
- for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
23
- try {
24
- return await fn();
25
- } catch (err) {
26
- lastError = err;
27
- if (attempt === config.maxAttempts) break;
28
- if (config.retryableErrors && !config.retryableErrors(err)) break;
29
- const delay = Math.min(
30
- config.baseDelayMs * Math.pow(config.backoffMultiplier, attempt - 1) + Math.random() * 200,
31
- config.maxDelayMs
32
- );
33
- config.onRetry?.(attempt, err, delay);
34
- await sleep(delay);
35
- }
36
- }
37
- throw lastError;
38
- }
39
- var CircuitBreaker = class {
40
- state = "closed";
41
- failures = 0;
42
- successes = 0;
43
- lastFailureTime = 0;
44
- opts;
45
- constructor(opts = {}) {
46
- this.opts = {
47
- failureThreshold: opts.failureThreshold ?? 5,
48
- recoveryTimeMs: opts.recoveryTimeMs ?? 3e4,
49
- successThreshold: opts.successThreshold ?? 2,
50
- timeout: opts.timeout
51
- };
52
- }
53
- async execute(fn) {
54
- if (this.state === "open") {
55
- if (Date.now() - this.lastFailureTime >= this.opts.recoveryTimeMs) {
56
- this.state = "half-open";
57
- this.successes = 0;
58
- } else {
59
- throw new CircuitOpenError(
60
- `Circuit breaker is open. Retry after ${this.opts.recoveryTimeMs}ms`
61
- );
62
- }
63
- }
64
- try {
65
- const result = this.opts.timeout ? await withTimeout(fn(), this.opts.timeout) : await fn();
66
- this.onSuccess();
67
- return result;
68
- } catch (err) {
69
- this.onFailure();
70
- throw err;
71
- }
72
- }
73
- onSuccess() {
74
- if (this.state === "half-open") {
75
- this.successes++;
76
- if (this.successes >= this.opts.successThreshold) {
77
- this.state = "closed";
78
- this.failures = 0;
79
- }
80
- } else {
81
- this.failures = 0;
82
- }
83
- }
84
- onFailure() {
85
- this.failures++;
86
- this.lastFailureTime = Date.now();
87
- if (this.failures >= this.opts.failureThreshold) {
88
- this.state = "open";
89
- }
90
- }
91
- getState() {
92
- return this.state;
93
- }
94
- reset() {
95
- this.state = "closed";
96
- this.failures = 0;
97
- this.successes = 0;
98
- }
99
- };
100
- var CircuitOpenError = class extends Error {
101
- constructor(message) {
102
- super(message);
103
- this.name = "CircuitOpenError";
104
- }
105
- };
106
- var RateLimiter = class {
107
- tokens;
108
- lastRefill;
109
- opts;
110
- constructor(opts) {
111
- this.opts = {
112
- maxTokens: opts.maxTokens,
113
- refillRate: opts.refillRate,
114
- refillIntervalMs: opts.refillIntervalMs ?? 1e3
115
- };
116
- this.tokens = this.opts.maxTokens;
117
- this.lastRefill = Date.now();
118
- }
119
- /**
120
- * Try to consume a token. Returns true if allowed, false if rate limited.
121
- */
122
- tryConsume(count = 1) {
123
- this.refill();
124
- if (this.tokens >= count) {
125
- this.tokens -= count;
126
- return true;
127
- }
128
- return false;
129
- }
130
- /**
131
- * Get time in ms until next token is available.
132
- */
133
- getRetryAfterMs() {
134
- this.refill();
135
- if (this.tokens >= 1) return 0;
136
- const tokensNeeded = 1 - this.tokens;
137
- return Math.ceil(tokensNeeded / this.opts.refillRate * 1e3);
138
- }
139
- refill() {
140
- const now = Date.now();
141
- const elapsed = now - this.lastRefill;
142
- const tokensToAdd = elapsed / 1e3 * this.opts.refillRate;
143
- this.tokens = Math.min(this.opts.maxTokens, this.tokens + tokensToAdd);
144
- this.lastRefill = now;
145
- }
146
- };
147
- var KeyedRateLimiter = class {
148
- limiters = /* @__PURE__ */ new Map();
149
- opts;
150
- cleanupTimer = null;
151
- constructor(opts) {
152
- this.opts = opts;
153
- this.cleanupTimer = setInterval(() => this.cleanup(), 5 * 6e4);
154
- if (this.cleanupTimer && typeof this.cleanupTimer === "object" && "unref" in this.cleanupTimer) {
155
- this.cleanupTimer.unref();
156
- }
157
- }
158
- tryConsume(key, count = 1) {
159
- let limiter = this.limiters.get(key);
160
- if (!limiter) {
161
- limiter = new RateLimiter(this.opts);
162
- this.limiters.set(key, limiter);
163
- }
164
- return limiter.tryConsume(count);
165
- }
166
- getRetryAfterMs(key) {
167
- const limiter = this.limiters.get(key);
168
- return limiter ? limiter.getRetryAfterMs() : 0;
169
- }
170
- cleanup() {
171
- for (const [key, limiter] of this.limiters) {
172
- if (limiter.getRetryAfterMs() === 0) {
173
- this.limiters.delete(key);
174
- }
175
- }
176
- }
177
- destroy() {
178
- if (this.cleanupTimer) clearInterval(this.cleanupTimer);
179
- this.limiters.clear();
180
- }
181
- };
182
- var HealthMonitor = class {
183
- healthy = true;
184
- consecutiveFailures = 0;
185
- consecutiveSuccesses = 0;
186
- timer = null;
187
- check;
188
- opts;
189
- listeners = [];
190
- constructor(check, opts = {}) {
191
- this.check = check;
192
- this.opts = {
193
- intervalMs: opts.intervalMs ?? 3e4,
194
- timeoutMs: opts.timeoutMs ?? 5e3,
195
- unhealthyThreshold: opts.unhealthyThreshold ?? 3,
196
- healthyThreshold: opts.healthyThreshold ?? 2
197
- };
198
- }
199
- start() {
200
- if (this.timer) return;
201
- this.timer = setInterval(() => this.runCheck(), this.opts.intervalMs);
202
- if (this.timer && typeof this.timer === "object" && "unref" in this.timer) {
203
- this.timer.unref();
204
- }
205
- }
206
- stop() {
207
- if (this.timer) {
208
- clearInterval(this.timer);
209
- this.timer = null;
210
- }
211
- }
212
- isHealthy() {
213
- return this.healthy;
214
- }
215
- onStatusChange(fn) {
216
- this.listeners.push(fn);
217
- }
218
- async runCheck() {
219
- try {
220
- await withTimeout(this.check(), this.opts.timeoutMs);
221
- this.consecutiveFailures = 0;
222
- this.consecutiveSuccesses++;
223
- if (!this.healthy && this.consecutiveSuccesses >= this.opts.healthyThreshold) {
224
- this.healthy = true;
225
- this.listeners.forEach((fn) => fn(true));
226
- }
227
- } catch {
228
- this.consecutiveSuccesses = 0;
229
- this.consecutiveFailures++;
230
- if (this.healthy && this.consecutiveFailures >= this.opts.unhealthyThreshold) {
231
- this.healthy = false;
232
- this.listeners.forEach((fn) => fn(false));
233
- }
234
- }
235
- }
236
- };
237
- function sleep(ms) {
238
- return new Promise((resolve) => setTimeout(resolve, ms));
239
- }
240
- function withTimeout(promise, ms) {
241
- return new Promise((resolve, reject) => {
242
- const timer = setTimeout(() => reject(new Error(`Operation timed out after ${ms}ms`)), ms);
243
- promise.then((v) => {
244
- clearTimeout(timer);
245
- resolve(v);
246
- }).catch((e) => {
247
- clearTimeout(timer);
248
- reject(e);
249
- });
250
- });
251
- }
252
- var counter = 0;
253
- var prefix = Math.random().toString(36).substring(2, 8);
254
- function requestId() {
255
- return `${prefix}-${(++counter).toString(36)}-${Date.now().toString(36)}`;
256
- }
257
-
258
19
  // src/middleware/index.ts
259
20
  function requestIdMiddleware() {
260
21
  return async (c, next) => {
@@ -817,7 +578,7 @@ function createServer(config) {
817
578
  }
818
579
  app.get("/health", (c) => c.json({
819
580
  status: "ok",
820
- version: "0.2.0",
581
+ version: "0.3.0",
821
582
  uptime: process.uptime()
822
583
  }));
823
584
  app.get("/ready", async (c) => {
@@ -863,9 +624,45 @@ function createServer(config) {
863
624
  api.use("*", auditLogger(config.db));
864
625
  const adminRoutes = createAdminRoutes(config.db);
865
626
  api.route("/", adminRoutes);
627
+ let engineInitialized = false;
866
628
  api.all("/engine/*", async (c, next) => {
867
629
  try {
868
- const { engineRoutes } = await import("./routes-74ZLKJKP.js");
630
+ const { engineRoutes, setEngineDb } = await import("./routes-2JEPIIKC.js");
631
+ const { EngineDatabase } = await import("./db-adapter-DEWEFNIV.js");
632
+ if (!engineInitialized) {
633
+ const dbType = config.db.type || config.db.config?.type || "sqlite";
634
+ const dialectMap = {
635
+ sqlite: "sqlite",
636
+ postgres: "postgres",
637
+ postgresql: "postgres",
638
+ mysql: "mysql",
639
+ mariadb: "mysql",
640
+ turso: "turso",
641
+ libsql: "turso",
642
+ mongodb: "mongodb",
643
+ dynamodb: "dynamodb"
644
+ };
645
+ const dialect = dialectMap[dbType] || "sqlite";
646
+ const engineDbWrapper = {
647
+ run: async (sql, params) => {
648
+ await config.db.run?.(sql, params) ?? config.db.query?.(sql, params);
649
+ },
650
+ get: async (sql, params) => {
651
+ if (config.db.get) return config.db.get(sql, params);
652
+ const rows = await config.db.query?.(sql, params) ?? [];
653
+ return rows[0];
654
+ },
655
+ all: async (sql, params) => {
656
+ if (config.db.all) return config.db.all(sql, params);
657
+ return await config.db.query?.(sql, params) ?? [];
658
+ }
659
+ };
660
+ const engineDb = new EngineDatabase(engineDbWrapper, dialect, config.db.rawDriver);
661
+ const migrationResult = await engineDb.migrate();
662
+ console.log(`[engine] Migrations: ${migrationResult.applied} applied, ${migrationResult.total} total`);
663
+ setEngineDb(engineDb);
664
+ engineInitialized = true;
665
+ }
869
666
  const subPath = c.req.path.replace(/^\/api\/engine/, "") || "/";
870
667
  const subReq = new Request(new URL(subPath, "http://localhost"), {
871
668
  method: c.req.method,
@@ -873,8 +670,9 @@ function createServer(config) {
873
670
  body: c.req.method !== "GET" && c.req.method !== "HEAD" ? c.req.raw.body : void 0
874
671
  });
875
672
  return engineRoutes.fetch(subReq);
876
- } catch {
877
- return c.json({ error: "Engine module not available" }, 501);
673
+ } catch (e) {
674
+ console.error("[engine] Error:", e.message);
675
+ return c.json({ error: "Engine module not available", detail: e.message }, 501);
878
676
  }
879
677
  });
880
678
  app.route("/api", api);
@@ -944,12 +742,6 @@ function createServer(config) {
944
742
  }
945
743
 
946
744
  export {
947
- withRetry,
948
- CircuitBreaker,
949
- CircuitOpenError,
950
- RateLimiter,
951
- KeyedRateLimiter,
952
- HealthMonitor,
953
745
  requestIdMiddleware,
954
746
  requestLogger,
955
747
  rateLimiter,
@@ -0,0 +1,48 @@
1
+ // src/db/factory.ts
2
+ var ADAPTER_MAP = {
3
+ postgres: () => import("./postgres-LN7A6MGQ.js").then((m) => m.PostgresAdapter),
4
+ supabase: () => import("./postgres-LN7A6MGQ.js").then((m) => m.PostgresAdapter),
5
+ // Supabase IS Postgres
6
+ neon: () => import("./postgres-LN7A6MGQ.js").then((m) => m.PostgresAdapter),
7
+ // Neon IS Postgres
8
+ cockroachdb: () => import("./postgres-LN7A6MGQ.js").then((m) => m.PostgresAdapter),
9
+ // CockroachDB is PG-compatible
10
+ mysql: () => import("./mysql-RM3S2FV5.js").then((m) => m.MysqlAdapter),
11
+ planetscale: () => import("./mysql-RM3S2FV5.js").then((m) => m.MysqlAdapter),
12
+ // PlanetScale IS MySQL
13
+ mongodb: () => import("./mongodb-ODTXIVPV.js").then((m) => m.MongoAdapter),
14
+ sqlite: () => import("./sqlite-VLKVAJA4.js").then((m) => m.SqliteAdapter),
15
+ turso: () => import("./turso-LDWODSDI.js").then((m) => m.TursoAdapter),
16
+ dynamodb: () => import("./dynamodb-CCGL2E77.js").then((m) => m.DynamoAdapter)
17
+ };
18
+ async function createAdapter(config) {
19
+ const loader = ADAPTER_MAP[config.type];
20
+ if (!loader) {
21
+ throw new Error(
22
+ `Unsupported database type: "${config.type}". Supported: ${Object.keys(ADAPTER_MAP).join(", ")}`
23
+ );
24
+ }
25
+ const AdapterClass = await loader();
26
+ const adapter = new AdapterClass();
27
+ await adapter.connect(config);
28
+ return adapter;
29
+ }
30
+ function getSupportedDatabases() {
31
+ return [
32
+ { type: "postgres", label: "PostgreSQL", group: "SQL" },
33
+ { type: "mysql", label: "MySQL / MariaDB", group: "SQL" },
34
+ { type: "sqlite", label: "SQLite (embedded, dev/small)", group: "SQL" },
35
+ { type: "mongodb", label: "MongoDB", group: "NoSQL" },
36
+ { type: "turso", label: "Turso (LibSQL, edge)", group: "Edge" },
37
+ { type: "dynamodb", label: "DynamoDB (AWS)", group: "Cloud" },
38
+ { type: "supabase", label: "Supabase (managed Postgres)", group: "Cloud" },
39
+ { type: "neon", label: "Neon (serverless Postgres)", group: "Cloud" },
40
+ { type: "planetscale", label: "PlanetScale (managed MySQL)", group: "Cloud" },
41
+ { type: "cockroachdb", label: "CockroachDB", group: "Distributed" }
42
+ ];
43
+ }
44
+
45
+ export {
46
+ createAdapter,
47
+ getSupportedDatabases
48
+ };