@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,363 @@
|
|
|
1
|
+
import {
|
|
2
|
+
withRetry
|
|
3
|
+
} from "./chunk-LCUZGIDH.js";
|
|
4
|
+
|
|
5
|
+
// src/db/factory.ts
|
|
6
|
+
var ADAPTER_MAP = {
|
|
7
|
+
postgres: () => import("./postgres-LN7A6MGQ.js").then((m) => m.PostgresAdapter),
|
|
8
|
+
supabase: () => import("./postgres-LN7A6MGQ.js").then((m) => m.PostgresAdapter),
|
|
9
|
+
// Supabase IS Postgres
|
|
10
|
+
neon: () => import("./postgres-LN7A6MGQ.js").then((m) => m.PostgresAdapter),
|
|
11
|
+
// Neon IS Postgres
|
|
12
|
+
cockroachdb: () => import("./postgres-LN7A6MGQ.js").then((m) => m.PostgresAdapter),
|
|
13
|
+
// CockroachDB is PG-compatible
|
|
14
|
+
mysql: () => import("./mysql-RM3S2FV5.js").then((m) => m.MysqlAdapter),
|
|
15
|
+
planetscale: () => import("./mysql-RM3S2FV5.js").then((m) => m.MysqlAdapter),
|
|
16
|
+
// PlanetScale IS MySQL
|
|
17
|
+
mongodb: () => import("./mongodb-ODTXIVPV.js").then((m) => m.MongoAdapter),
|
|
18
|
+
sqlite: () => import("./sqlite-3K5YOZ4K.js").then((m) => m.SqliteAdapter),
|
|
19
|
+
turso: () => import("./turso-LDWODSDI.js").then((m) => m.TursoAdapter),
|
|
20
|
+
dynamodb: () => import("./dynamodb-CCGL2E77.js").then((m) => m.DynamoAdapter)
|
|
21
|
+
};
|
|
22
|
+
async function createAdapter(config) {
|
|
23
|
+
const loader = ADAPTER_MAP[config.type];
|
|
24
|
+
if (!loader) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`Unsupported database type: "${config.type}". Supported: ${Object.keys(ADAPTER_MAP).join(", ")}`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
const AdapterClass = await loader();
|
|
30
|
+
const adapter = new AdapterClass();
|
|
31
|
+
await adapter.connect(config);
|
|
32
|
+
return adapter;
|
|
33
|
+
}
|
|
34
|
+
function getSupportedDatabases() {
|
|
35
|
+
return [
|
|
36
|
+
{ type: "postgres", label: "PostgreSQL", group: "SQL" },
|
|
37
|
+
{ type: "mysql", label: "MySQL / MariaDB", group: "SQL" },
|
|
38
|
+
{ type: "sqlite", label: "SQLite (embedded, dev/small)", group: "SQL" },
|
|
39
|
+
{ type: "mongodb", label: "MongoDB", group: "NoSQL" },
|
|
40
|
+
{ type: "turso", label: "Turso (LibSQL, edge)", group: "Edge" },
|
|
41
|
+
{ type: "dynamodb", label: "DynamoDB (AWS)", group: "Cloud" },
|
|
42
|
+
{ type: "supabase", label: "Supabase (managed Postgres)", group: "Cloud" },
|
|
43
|
+
{ type: "neon", label: "Neon (serverless Postgres)", group: "Cloud" },
|
|
44
|
+
{ type: "planetscale", label: "PlanetScale (managed MySQL)", group: "Cloud" },
|
|
45
|
+
{ type: "cockroachdb", label: "CockroachDB", group: "Distributed" }
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/deploy/fly.ts
|
|
50
|
+
var FLY_API = "https://api.machines.dev";
|
|
51
|
+
var DEFAULT_IMAGE = "agenticmail/enterprise:latest";
|
|
52
|
+
async function flyRequest(path, opts) {
|
|
53
|
+
const resp = await fetch(`${FLY_API}${path}`, {
|
|
54
|
+
method: opts.method || "GET",
|
|
55
|
+
headers: {
|
|
56
|
+
Authorization: `Bearer ${opts.apiToken}`,
|
|
57
|
+
"Content-Type": "application/json"
|
|
58
|
+
},
|
|
59
|
+
body: opts.body ? JSON.stringify(opts.body) : void 0,
|
|
60
|
+
signal: AbortSignal.timeout(3e4)
|
|
61
|
+
});
|
|
62
|
+
if (!resp.ok) {
|
|
63
|
+
const text = await resp.text().catch(() => "Unknown error");
|
|
64
|
+
throw new Error(`Fly.io API error (${resp.status}): ${text}`);
|
|
65
|
+
}
|
|
66
|
+
return resp.json();
|
|
67
|
+
}
|
|
68
|
+
async function createApp(name, fly) {
|
|
69
|
+
await flyRequest("/v1/apps", {
|
|
70
|
+
method: "POST",
|
|
71
|
+
apiToken: fly.apiToken,
|
|
72
|
+
body: {
|
|
73
|
+
app_name: name,
|
|
74
|
+
org_slug: fly.org || "personal"
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async function createMachine(appName, config, fly) {
|
|
79
|
+
const region = fly.regions?.[0] || "iad";
|
|
80
|
+
const env = {
|
|
81
|
+
PORT: "3000",
|
|
82
|
+
NODE_ENV: "production",
|
|
83
|
+
DATABASE_TYPE: config.dbType,
|
|
84
|
+
DATABASE_URL: config.dbConnectionString,
|
|
85
|
+
JWT_SECRET: config.jwtSecret
|
|
86
|
+
};
|
|
87
|
+
if (config.smtpHost) env.SMTP_HOST = config.smtpHost;
|
|
88
|
+
if (config.smtpPort) env.SMTP_PORT = String(config.smtpPort);
|
|
89
|
+
if (config.smtpUser) env.SMTP_USER = config.smtpUser;
|
|
90
|
+
if (config.smtpPass) env.SMTP_PASS = config.smtpPass;
|
|
91
|
+
const result = await flyRequest(`/v1/apps/${appName}/machines`, {
|
|
92
|
+
method: "POST",
|
|
93
|
+
apiToken: fly.apiToken,
|
|
94
|
+
body: {
|
|
95
|
+
name: `${appName}-web`,
|
|
96
|
+
region,
|
|
97
|
+
config: {
|
|
98
|
+
image: fly.image || DEFAULT_IMAGE,
|
|
99
|
+
env,
|
|
100
|
+
services: [
|
|
101
|
+
{
|
|
102
|
+
ports: [
|
|
103
|
+
{ port: 443, handlers: ["tls", "http"] },
|
|
104
|
+
{ port: 80, handlers: ["http"] }
|
|
105
|
+
],
|
|
106
|
+
protocol: "tcp",
|
|
107
|
+
internal_port: 3e3,
|
|
108
|
+
concurrency: {
|
|
109
|
+
type: "connections",
|
|
110
|
+
hard_limit: 100,
|
|
111
|
+
soft_limit: 80
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
checks: {
|
|
116
|
+
health: {
|
|
117
|
+
type: "http",
|
|
118
|
+
port: 3e3,
|
|
119
|
+
path: "/health",
|
|
120
|
+
interval: "30s",
|
|
121
|
+
timeout: "5s",
|
|
122
|
+
grace_period: "10s"
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
guest: {
|
|
126
|
+
cpu_kind: config.cpuKind || "shared",
|
|
127
|
+
cpus: config.cpus || 1,
|
|
128
|
+
memory_mb: config.memoryMb || 256
|
|
129
|
+
},
|
|
130
|
+
auto_destroy: false,
|
|
131
|
+
restart: {
|
|
132
|
+
policy: "always",
|
|
133
|
+
max_retries: 5
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
return { id: result.id, region: result.region || region };
|
|
139
|
+
}
|
|
140
|
+
async function allocateIp(appName, fly) {
|
|
141
|
+
try {
|
|
142
|
+
const resp = await fetch("https://api.fly.io/graphql", {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: {
|
|
145
|
+
Authorization: `Bearer ${fly.apiToken}`,
|
|
146
|
+
"Content-Type": "application/json"
|
|
147
|
+
},
|
|
148
|
+
body: JSON.stringify({
|
|
149
|
+
query: `mutation($input: AllocateIPAddressInput!) {
|
|
150
|
+
allocateIpAddress(input: $input) {
|
|
151
|
+
ipAddress { id address type region createdAt }
|
|
152
|
+
}
|
|
153
|
+
}`,
|
|
154
|
+
variables: {
|
|
155
|
+
input: { appId: appName, type: "v4", region: "" }
|
|
156
|
+
}
|
|
157
|
+
}),
|
|
158
|
+
signal: AbortSignal.timeout(15e3)
|
|
159
|
+
});
|
|
160
|
+
const data = await resp.json();
|
|
161
|
+
const v4 = data?.data?.allocateIpAddress?.ipAddress?.address;
|
|
162
|
+
return { v4 };
|
|
163
|
+
} catch {
|
|
164
|
+
return {};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function addCertificate(appName, hostname, fly) {
|
|
168
|
+
await fetch("https://api.fly.io/graphql", {
|
|
169
|
+
method: "POST",
|
|
170
|
+
headers: {
|
|
171
|
+
Authorization: `Bearer ${fly.apiToken}`,
|
|
172
|
+
"Content-Type": "application/json"
|
|
173
|
+
},
|
|
174
|
+
body: JSON.stringify({
|
|
175
|
+
query: `mutation($appId: ID!, $hostname: String!) {
|
|
176
|
+
addCertificate(appId: $appId, hostname: $hostname) {
|
|
177
|
+
certificate { hostname configured }
|
|
178
|
+
}
|
|
179
|
+
}`,
|
|
180
|
+
variables: { appId: appName, hostname }
|
|
181
|
+
}),
|
|
182
|
+
signal: AbortSignal.timeout(15e3)
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
async function deployToFly(config, fly) {
|
|
186
|
+
const appName = `am-${config.subdomain}`;
|
|
187
|
+
const domain = `${config.subdomain}.agenticmail.cloud`;
|
|
188
|
+
try {
|
|
189
|
+
console.log(` Creating app: ${appName}...`);
|
|
190
|
+
await withRetry(() => createApp(appName, fly), {
|
|
191
|
+
maxAttempts: 2,
|
|
192
|
+
retryableErrors: (err) => !err.message.includes("already exists")
|
|
193
|
+
});
|
|
194
|
+
console.log(` Deploying machine...`);
|
|
195
|
+
const machine = await withRetry(() => createMachine(appName, config, fly), {
|
|
196
|
+
maxAttempts: 3,
|
|
197
|
+
baseDelayMs: 2e3
|
|
198
|
+
});
|
|
199
|
+
console.log(` Allocating IP address...`);
|
|
200
|
+
const ips = await allocateIp(appName, fly);
|
|
201
|
+
console.log(` Setting up TLS for ${domain}...`);
|
|
202
|
+
await addCertificate(appName, domain, fly).catch(() => {
|
|
203
|
+
});
|
|
204
|
+
return {
|
|
205
|
+
appName,
|
|
206
|
+
url: `https://${domain}`,
|
|
207
|
+
ipv4: ips.v4,
|
|
208
|
+
ipv6: ips.v6,
|
|
209
|
+
region: machine.region,
|
|
210
|
+
machineId: machine.id,
|
|
211
|
+
status: "started"
|
|
212
|
+
};
|
|
213
|
+
} catch (err) {
|
|
214
|
+
return {
|
|
215
|
+
appName,
|
|
216
|
+
url: `https://${domain}`,
|
|
217
|
+
region: fly.regions?.[0] || "iad",
|
|
218
|
+
machineId: "",
|
|
219
|
+
status: "error",
|
|
220
|
+
error: err.message
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/deploy/managed.ts
|
|
226
|
+
async function deployToCloud(config, flyToken) {
|
|
227
|
+
const token = flyToken || process.env.FLY_API_TOKEN;
|
|
228
|
+
if (!token) {
|
|
229
|
+
return {
|
|
230
|
+
url: `https://${config.subdomain}.agenticmail.cloud`,
|
|
231
|
+
appName: `am-${config.subdomain}`,
|
|
232
|
+
region: config.region || "iad",
|
|
233
|
+
status: "pending",
|
|
234
|
+
error: "FLY_API_TOKEN not set. Set it to enable cloud deployment."
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const flyConfig = {
|
|
238
|
+
apiToken: token,
|
|
239
|
+
org: process.env.FLY_ORG || "agenticmail",
|
|
240
|
+
regions: [config.region || "iad"]
|
|
241
|
+
};
|
|
242
|
+
const appConfig = {
|
|
243
|
+
subdomain: config.subdomain,
|
|
244
|
+
dbType: config.dbType,
|
|
245
|
+
dbConnectionString: config.dbConnectionString,
|
|
246
|
+
jwtSecret: config.jwtSecret,
|
|
247
|
+
memoryMb: config.plan === "free" ? 256 : config.plan === "team" ? 512 : 1024,
|
|
248
|
+
cpuKind: config.plan === "enterprise" ? "performance" : "shared",
|
|
249
|
+
cpus: config.plan === "enterprise" ? 2 : 1
|
|
250
|
+
};
|
|
251
|
+
const result = await deployToFly(appConfig, flyConfig);
|
|
252
|
+
return {
|
|
253
|
+
url: result.url,
|
|
254
|
+
appName: result.appName,
|
|
255
|
+
region: result.region,
|
|
256
|
+
status: result.status === "error" ? "error" : "deployed",
|
|
257
|
+
error: result.error
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function generateDockerCompose(opts) {
|
|
261
|
+
const env = [
|
|
262
|
+
` - NODE_ENV=production`,
|
|
263
|
+
` - DATABASE_TYPE=${opts.dbType}`,
|
|
264
|
+
` - DATABASE_URL=${opts.dbConnectionString}`,
|
|
265
|
+
` - JWT_SECRET=${opts.jwtSecret}`,
|
|
266
|
+
` - PORT=3000`
|
|
267
|
+
];
|
|
268
|
+
if (opts.smtpHost) {
|
|
269
|
+
env.push(` - SMTP_HOST=${opts.smtpHost}`);
|
|
270
|
+
env.push(` - SMTP_PORT=${opts.smtpPort || 587}`);
|
|
271
|
+
if (opts.smtpUser) env.push(` - SMTP_USER=${opts.smtpUser}`);
|
|
272
|
+
if (opts.smtpPass) env.push(` - SMTP_PASS=${opts.smtpPass}`);
|
|
273
|
+
}
|
|
274
|
+
return `# AgenticMail Enterprise \u2014 Docker Compose
|
|
275
|
+
# Generated at ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
276
|
+
#
|
|
277
|
+
# Usage:
|
|
278
|
+
# docker compose up -d
|
|
279
|
+
# open http://localhost:${opts.port}
|
|
280
|
+
|
|
281
|
+
version: "3.8"
|
|
282
|
+
|
|
283
|
+
services:
|
|
284
|
+
agenticmail:
|
|
285
|
+
image: agenticmail/enterprise:latest
|
|
286
|
+
ports:
|
|
287
|
+
- "${opts.port}:3000"
|
|
288
|
+
environment:
|
|
289
|
+
${env.join("\n")}
|
|
290
|
+
restart: unless-stopped
|
|
291
|
+
healthcheck:
|
|
292
|
+
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
|
|
293
|
+
interval: 30s
|
|
294
|
+
timeout: 10s
|
|
295
|
+
retries: 3
|
|
296
|
+
start_period: 15s
|
|
297
|
+
deploy:
|
|
298
|
+
resources:
|
|
299
|
+
limits:
|
|
300
|
+
memory: 512M
|
|
301
|
+
cpus: '1.0'
|
|
302
|
+
reservations:
|
|
303
|
+
memory: 128M
|
|
304
|
+
logging:
|
|
305
|
+
driver: json-file
|
|
306
|
+
options:
|
|
307
|
+
max-size: "10m"
|
|
308
|
+
max-file: "3"
|
|
309
|
+
`;
|
|
310
|
+
}
|
|
311
|
+
function generateFlyToml(appName, region) {
|
|
312
|
+
return `# AgenticMail Enterprise \u2014 Fly.io Config
|
|
313
|
+
# Generated at ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
314
|
+
#
|
|
315
|
+
# Deploy:
|
|
316
|
+
# fly launch --copy-config
|
|
317
|
+
# fly secrets set DATABASE_URL="..." JWT_SECRET="..."
|
|
318
|
+
# fly deploy
|
|
319
|
+
|
|
320
|
+
app = "${appName}"
|
|
321
|
+
primary_region = "${region}"
|
|
322
|
+
|
|
323
|
+
[build]
|
|
324
|
+
image = "agenticmail/enterprise:latest"
|
|
325
|
+
|
|
326
|
+
[env]
|
|
327
|
+
PORT = "3000"
|
|
328
|
+
NODE_ENV = "production"
|
|
329
|
+
|
|
330
|
+
[http_service]
|
|
331
|
+
internal_port = 3000
|
|
332
|
+
force_https = true
|
|
333
|
+
auto_stop_machines = "stop"
|
|
334
|
+
auto_start_machines = true
|
|
335
|
+
min_machines_running = 1
|
|
336
|
+
|
|
337
|
+
[http_service.concurrency]
|
|
338
|
+
type = "connections"
|
|
339
|
+
hard_limit = 100
|
|
340
|
+
soft_limit = 80
|
|
341
|
+
|
|
342
|
+
[checks]
|
|
343
|
+
[checks.health]
|
|
344
|
+
type = "http"
|
|
345
|
+
port = 3000
|
|
346
|
+
path = "/health"
|
|
347
|
+
interval = "30s"
|
|
348
|
+
timeout = "5s"
|
|
349
|
+
grace_period = "10s"
|
|
350
|
+
|
|
351
|
+
[[vm]]
|
|
352
|
+
size = "shared-cpu-1x"
|
|
353
|
+
memory = "256mb"
|
|
354
|
+
`;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export {
|
|
358
|
+
createAdapter,
|
|
359
|
+
getSupportedDatabases,
|
|
360
|
+
deployToCloud,
|
|
361
|
+
generateDockerCompose,
|
|
362
|
+
generateFlyToml
|
|
363
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __esm = (fn, res) => function __init() {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
};
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
__esm,
|
|
24
|
+
__export,
|
|
25
|
+
__toCommonJS
|
|
26
|
+
};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createAdapter,
|
|
4
|
+
createServer,
|
|
5
|
+
deployToCloud,
|
|
6
|
+
generateDockerCompose,
|
|
7
|
+
generateFlyToml,
|
|
8
|
+
getSupportedDatabases
|
|
9
|
+
} from "./chunk-DXNKR3TG.js";
|
|
10
|
+
import "./chunk-PNKVD2UK.js";
|
|
11
|
+
|
|
12
|
+
// src/cli.ts
|
|
13
|
+
import { randomUUID } from "crypto";
|
|
14
|
+
async function main() {
|
|
15
|
+
const { default: inquirer } = await import("inquirer");
|
|
16
|
+
const { default: ora } = await import("ora");
|
|
17
|
+
const { default: chalk } = await import("chalk");
|
|
18
|
+
console.log("");
|
|
19
|
+
console.log(chalk.bold("\u{1F3E2} AgenticMail Enterprise"));
|
|
20
|
+
console.log(chalk.dim(" AI Agent Identity & Email for Organizations"));
|
|
21
|
+
console.log("");
|
|
22
|
+
const { companyName, adminEmail, adminPassword } = await inquirer.prompt([
|
|
23
|
+
{
|
|
24
|
+
type: "input",
|
|
25
|
+
name: "companyName",
|
|
26
|
+
message: "Company name:",
|
|
27
|
+
validate: (v) => v.length > 0 || "Required"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: "input",
|
|
31
|
+
name: "adminEmail",
|
|
32
|
+
message: "Admin email:",
|
|
33
|
+
validate: (v) => v.includes("@") || "Must be a valid email"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: "password",
|
|
37
|
+
name: "adminPassword",
|
|
38
|
+
message: "Admin password:",
|
|
39
|
+
mask: "*",
|
|
40
|
+
validate: (v) => v.length >= 8 || "Must be at least 8 characters"
|
|
41
|
+
}
|
|
42
|
+
]);
|
|
43
|
+
const databases = getSupportedDatabases();
|
|
44
|
+
const { dbType } = await inquirer.prompt([
|
|
45
|
+
{
|
|
46
|
+
type: "list",
|
|
47
|
+
name: "dbType",
|
|
48
|
+
message: "Database:",
|
|
49
|
+
choices: databases.map((d) => ({
|
|
50
|
+
name: `${d.label} ${chalk.dim(`(${d.group})`)}`,
|
|
51
|
+
value: d.type
|
|
52
|
+
}))
|
|
53
|
+
}
|
|
54
|
+
]);
|
|
55
|
+
let dbConfig = { type: dbType };
|
|
56
|
+
if (dbType === "sqlite") {
|
|
57
|
+
const { dbPath } = await inquirer.prompt([{
|
|
58
|
+
type: "input",
|
|
59
|
+
name: "dbPath",
|
|
60
|
+
message: "Database file path:",
|
|
61
|
+
default: "./agenticmail-enterprise.db"
|
|
62
|
+
}]);
|
|
63
|
+
dbConfig.connectionString = dbPath;
|
|
64
|
+
} else if (dbType === "dynamodb") {
|
|
65
|
+
const { region, accessKeyId, secretAccessKey } = await inquirer.prompt([
|
|
66
|
+
{ type: "input", name: "region", message: "AWS Region:", default: "us-east-1" },
|
|
67
|
+
{ type: "input", name: "accessKeyId", message: "AWS Access Key ID:" },
|
|
68
|
+
{ type: "password", name: "secretAccessKey", message: "AWS Secret Access Key:", mask: "*" }
|
|
69
|
+
]);
|
|
70
|
+
dbConfig = { ...dbConfig, region, accessKeyId, secretAccessKey };
|
|
71
|
+
} else if (dbType === "turso") {
|
|
72
|
+
const { connectionString, authToken } = await inquirer.prompt([
|
|
73
|
+
{ type: "input", name: "connectionString", message: "Turso database URL:", placeholder: "libsql://..." },
|
|
74
|
+
{ type: "password", name: "authToken", message: "Turso auth token:", mask: "*" }
|
|
75
|
+
]);
|
|
76
|
+
dbConfig = { ...dbConfig, connectionString, authToken };
|
|
77
|
+
} else {
|
|
78
|
+
const hints = {
|
|
79
|
+
postgres: "postgresql://user:pass@host:5432/dbname",
|
|
80
|
+
mysql: "mysql://user:pass@host:3306/dbname",
|
|
81
|
+
mongodb: "mongodb+srv://user:pass@cluster.mongodb.net/dbname",
|
|
82
|
+
supabase: "postgresql://postgres:pass@db.xxxx.supabase.co:5432/postgres",
|
|
83
|
+
neon: "postgresql://user:pass@ep-xxx.us-east-1.aws.neon.tech/dbname?sslmode=require",
|
|
84
|
+
planetscale: 'mysql://user:pass@aws.connect.psdb.cloud/dbname?ssl={"rejectUnauthorized":true}',
|
|
85
|
+
cockroachdb: "postgresql://user:pass@cluster.cockroachlabs.cloud:26257/dbname?sslmode=verify-full"
|
|
86
|
+
};
|
|
87
|
+
const { connectionString } = await inquirer.prompt([{
|
|
88
|
+
type: "input",
|
|
89
|
+
name: "connectionString",
|
|
90
|
+
message: "Connection string:",
|
|
91
|
+
suffix: chalk.dim(` (e.g. ${hints[dbType] || ""})`)
|
|
92
|
+
}]);
|
|
93
|
+
dbConfig.connectionString = connectionString;
|
|
94
|
+
}
|
|
95
|
+
const { deployTarget } = await inquirer.prompt([{
|
|
96
|
+
type: "list",
|
|
97
|
+
name: "deployTarget",
|
|
98
|
+
message: "Deploy to:",
|
|
99
|
+
choices: [
|
|
100
|
+
{ name: `AgenticMail Cloud ${chalk.dim("(managed, instant URL)")}`, value: "cloud" },
|
|
101
|
+
{ name: `Fly.io ${chalk.dim("(your account)")}`, value: "fly" },
|
|
102
|
+
{ name: `Railway ${chalk.dim("(your account)")}`, value: "railway" },
|
|
103
|
+
{ name: `Docker ${chalk.dim("(self-hosted)")}`, value: "docker" },
|
|
104
|
+
{ name: `Local ${chalk.dim("(dev/testing, runs here)")}`, value: "local" }
|
|
105
|
+
]
|
|
106
|
+
}]);
|
|
107
|
+
const subdomain = companyName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
108
|
+
let customDomain;
|
|
109
|
+
if (deployTarget !== "local") {
|
|
110
|
+
const { wantsDomain } = await inquirer.prompt([{
|
|
111
|
+
type: "confirm",
|
|
112
|
+
name: "wantsDomain",
|
|
113
|
+
message: "Add a custom domain? (can do later)",
|
|
114
|
+
default: false
|
|
115
|
+
}]);
|
|
116
|
+
if (wantsDomain) {
|
|
117
|
+
const { domain } = await inquirer.prompt([{
|
|
118
|
+
type: "input",
|
|
119
|
+
name: "domain",
|
|
120
|
+
message: "Custom domain:",
|
|
121
|
+
suffix: chalk.dim(" (e.g. agents.acme.com)")
|
|
122
|
+
}]);
|
|
123
|
+
customDomain = domain;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
console.log("");
|
|
127
|
+
const spinner = ora("Connecting to database...").start();
|
|
128
|
+
try {
|
|
129
|
+
const db = await createAdapter(dbConfig);
|
|
130
|
+
spinner.text = "Running migrations...";
|
|
131
|
+
await db.migrate();
|
|
132
|
+
spinner.succeed("Database ready");
|
|
133
|
+
spinner.start("Creating company...");
|
|
134
|
+
const settings = await db.updateSettings({
|
|
135
|
+
name: companyName,
|
|
136
|
+
subdomain,
|
|
137
|
+
domain: customDomain
|
|
138
|
+
});
|
|
139
|
+
spinner.succeed("Company created");
|
|
140
|
+
spinner.start("Creating admin account...");
|
|
141
|
+
const admin = await db.createUser({
|
|
142
|
+
email: adminEmail,
|
|
143
|
+
name: "Admin",
|
|
144
|
+
role: "owner",
|
|
145
|
+
password: adminPassword
|
|
146
|
+
});
|
|
147
|
+
await db.logEvent({
|
|
148
|
+
actor: admin.id,
|
|
149
|
+
actorType: "system",
|
|
150
|
+
action: "setup.complete",
|
|
151
|
+
resource: `company:${subdomain}`,
|
|
152
|
+
details: { dbType, deployTarget, companyName }
|
|
153
|
+
});
|
|
154
|
+
spinner.succeed("Admin account created");
|
|
155
|
+
const jwtSecret = randomUUID() + randomUUID();
|
|
156
|
+
if (deployTarget === "cloud") {
|
|
157
|
+
spinner.start("Deploying to AgenticMail Cloud...");
|
|
158
|
+
const result = await deployToCloud({ subdomain, plan: "free" });
|
|
159
|
+
spinner.succeed(`Deployed to ${result.url}`);
|
|
160
|
+
console.log("");
|
|
161
|
+
console.log(chalk.green.bold("\u{1F389} Your dashboard is live!"));
|
|
162
|
+
console.log("");
|
|
163
|
+
console.log(` ${chalk.bold("URL:")} ${result.url}`);
|
|
164
|
+
console.log(` ${chalk.bold("Admin:")} ${adminEmail}`);
|
|
165
|
+
console.log(` ${chalk.bold("Password:")} (the one you just set)`);
|
|
166
|
+
if (customDomain) {
|
|
167
|
+
console.log("");
|
|
168
|
+
console.log(chalk.dim(` To use ${customDomain}:`));
|
|
169
|
+
console.log(chalk.dim(` Add CNAME: ${customDomain} \u2192 ${subdomain}.agenticmail.cloud`));
|
|
170
|
+
}
|
|
171
|
+
} else if (deployTarget === "docker") {
|
|
172
|
+
const compose = generateDockerCompose({
|
|
173
|
+
dbType,
|
|
174
|
+
dbConnectionString: dbConfig.connectionString || "",
|
|
175
|
+
port: 3e3,
|
|
176
|
+
jwtSecret
|
|
177
|
+
});
|
|
178
|
+
const { writeFileSync } = await import("fs");
|
|
179
|
+
writeFileSync("docker-compose.yml", compose);
|
|
180
|
+
spinner.succeed("docker-compose.yml generated");
|
|
181
|
+
console.log("");
|
|
182
|
+
console.log(chalk.green.bold("\u{1F433} Docker deployment ready!"));
|
|
183
|
+
console.log("");
|
|
184
|
+
console.log(" Run: docker compose up -d");
|
|
185
|
+
console.log(" Dashboard: http://localhost:3000");
|
|
186
|
+
} else if (deployTarget === "fly") {
|
|
187
|
+
const flyToml = generateFlyToml(`am-${subdomain}`, "iad");
|
|
188
|
+
const { writeFileSync } = await import("fs");
|
|
189
|
+
writeFileSync("fly.toml", flyToml);
|
|
190
|
+
spinner.succeed("fly.toml generated");
|
|
191
|
+
console.log("");
|
|
192
|
+
console.log(chalk.green.bold("\u{1FAB0} Fly.io deployment ready!"));
|
|
193
|
+
console.log("");
|
|
194
|
+
console.log(" Run: fly launch --copy-config");
|
|
195
|
+
console.log(` Then: fly secrets set DATABASE_URL="${dbConfig.connectionString}" JWT_SECRET="${jwtSecret}"`);
|
|
196
|
+
} else if (deployTarget === "local") {
|
|
197
|
+
spinner.start("Starting local server...");
|
|
198
|
+
const server = createServer({ port: 3e3, db, jwtSecret });
|
|
199
|
+
server.start();
|
|
200
|
+
spinner.succeed("Server running");
|
|
201
|
+
console.log("");
|
|
202
|
+
console.log(chalk.green.bold("\u{1F389} AgenticMail Enterprise is running!"));
|
|
203
|
+
console.log("");
|
|
204
|
+
console.log(` ${chalk.bold("Dashboard:")} http://localhost:3000`);
|
|
205
|
+
console.log(` ${chalk.bold("API:")} http://localhost:3000/api`);
|
|
206
|
+
console.log(` ${chalk.bold("Admin:")} ${adminEmail}`);
|
|
207
|
+
console.log("");
|
|
208
|
+
console.log(chalk.dim(" Press Ctrl+C to stop"));
|
|
209
|
+
}
|
|
210
|
+
console.log("");
|
|
211
|
+
} catch (err) {
|
|
212
|
+
spinner.fail(`Setup failed: ${err.message}`);
|
|
213
|
+
console.error("");
|
|
214
|
+
console.error(chalk.dim(err.stack));
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
main().catch(console.error);
|