@agenticmail/enterprise 0.2.2 → 0.3.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/LICENSE +26 -0
- package/README.md +939 -0
- package/dashboards/dotnet/Program.cs +5 -5
- package/dashboards/express/app.js +5 -5
- package/dashboards/go/main.go +8 -8
- package/dashboards/html/index.html +41 -18
- package/dashboards/java/AgenticMailDashboard.java +5 -5
- package/dashboards/php/index.php +8 -8
- package/dashboards/python/app.py +8 -8
- package/dashboards/ruby/app.rb +7 -7
- package/dashboards/shared-styles.css +57 -0
- package/dist/chunk-BE7MXVLA.js +757 -0
- package/dist/chunk-BS2WCSHO.js +48 -0
- package/dist/chunk-FL3VQBGL.js +757 -0
- package/dist/chunk-GXIEEA2T.js +48 -0
- package/dist/chunk-JLSQOQ5L.js +255 -0
- package/dist/chunk-TVF23PUW.js +338 -0
- package/dist/cli.js +305 -140
- package/dist/dashboard/index.html +833 -510
- package/dist/factory-HINWFYZ3.js +9 -0
- package/dist/factory-V37IG5AT.js +9 -0
- package/dist/index.js +18 -12
- package/dist/managed-RZITNPXG.js +14 -0
- package/dist/server-32YYCI3A.js +8 -0
- package/dist/server-H3C6WUOS.js +8 -0
- package/dist/sqlite-VLKVAJA4.js +442 -0
- package/package.json +28 -3
- package/src/cli.ts +15 -251
- package/src/dashboard/index.html +833 -510
- package/src/db/sqlite.ts +4 -1
- package/src/server.ts +1 -1
- package/src/setup/company.ts +64 -0
- package/src/setup/database.ts +119 -0
- package/src/setup/deployment.ts +50 -0
- package/src/setup/domain.ts +46 -0
- package/src/setup/index.ts +82 -0
- package/src/setup/provision.ts +226 -0
- package/test-integration.mjs +383 -0
- package/agenticmail-enterprise.db +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,58 +1,77 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
createAdapter,
|
|
4
|
-
createServer,
|
|
5
|
-
deployToCloud,
|
|
6
|
-
generateDockerCompose,
|
|
7
|
-
generateFlyToml,
|
|
8
3
|
getSupportedDatabases
|
|
9
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BS2WCSHO.js";
|
|
10
5
|
import "./chunk-PNKVD2UK.js";
|
|
11
6
|
|
|
12
|
-
// src/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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("");
|
|
7
|
+
// src/setup/company.ts
|
|
8
|
+
async function promptCompanyInfo(inquirer, chalk) {
|
|
9
|
+
console.log(chalk.bold.cyan(" Step 1 of 4: Company Info"));
|
|
10
|
+
console.log(chalk.dim(" Tell us about your organization.\n"));
|
|
22
11
|
const { companyName, adminEmail, adminPassword } = await inquirer.prompt([
|
|
23
12
|
{
|
|
24
13
|
type: "input",
|
|
25
14
|
name: "companyName",
|
|
26
15
|
message: "Company name:",
|
|
27
|
-
validate: (v) =>
|
|
16
|
+
validate: (v) => {
|
|
17
|
+
if (!v.trim()) return "Company name is required";
|
|
18
|
+
if (v.length > 100) return "Company name must be under 100 characters";
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
28
21
|
},
|
|
29
22
|
{
|
|
30
23
|
type: "input",
|
|
31
24
|
name: "adminEmail",
|
|
32
25
|
message: "Admin email:",
|
|
33
|
-
validate: (v) =>
|
|
26
|
+
validate: (v) => {
|
|
27
|
+
if (!v.includes("@") || !v.includes(".")) return "Enter a valid email address";
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
34
30
|
},
|
|
35
31
|
{
|
|
36
32
|
type: "password",
|
|
37
33
|
name: "adminPassword",
|
|
38
34
|
message: "Admin password:",
|
|
39
35
|
mask: "*",
|
|
40
|
-
validate: (v) =>
|
|
36
|
+
validate: (v) => {
|
|
37
|
+
if (v.length < 8) return "Password must be at least 8 characters";
|
|
38
|
+
if (!/[A-Z]/.test(v) && !/[0-9]/.test(v)) {
|
|
39
|
+
return "Password should contain at least one uppercase letter or number";
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
41
43
|
}
|
|
42
44
|
]);
|
|
45
|
+
const subdomain = companyName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 63);
|
|
46
|
+
return { companyName, adminEmail, adminPassword, subdomain };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/setup/database.ts
|
|
50
|
+
var CONNECTION_HINTS = {
|
|
51
|
+
postgres: "postgresql://user:pass@host:5432/dbname",
|
|
52
|
+
mysql: "mysql://user:pass@host:3306/dbname",
|
|
53
|
+
mongodb: "mongodb+srv://user:pass@cluster.mongodb.net/dbname",
|
|
54
|
+
supabase: "postgresql://postgres:pass@db.xxxx.supabase.co:5432/postgres",
|
|
55
|
+
neon: "postgresql://user:pass@ep-xxx.us-east-1.aws.neon.tech/dbname?sslmode=require",
|
|
56
|
+
planetscale: 'mysql://user:pass@aws.connect.psdb.cloud/dbname?ssl={"rejectUnauthorized":true}',
|
|
57
|
+
cockroachdb: "postgresql://user:pass@cluster.cockroachlabs.cloud:26257/dbname?sslmode=verify-full"
|
|
58
|
+
};
|
|
59
|
+
async function promptDatabase(inquirer, chalk) {
|
|
60
|
+
console.log("");
|
|
61
|
+
console.log(chalk.bold.cyan(" Step 2 of 4: Database"));
|
|
62
|
+
console.log(chalk.dim(" Where should your data live?\n"));
|
|
43
63
|
const databases = getSupportedDatabases();
|
|
44
64
|
const { dbType } = await inquirer.prompt([
|
|
45
65
|
{
|
|
46
66
|
type: "list",
|
|
47
67
|
name: "dbType",
|
|
48
|
-
message: "Database:",
|
|
68
|
+
message: "Database backend:",
|
|
49
69
|
choices: databases.map((d) => ({
|
|
50
70
|
name: `${d.label} ${chalk.dim(`(${d.group})`)}`,
|
|
51
71
|
value: d.type
|
|
52
72
|
}))
|
|
53
73
|
}
|
|
54
74
|
]);
|
|
55
|
-
let dbConfig = { type: dbType };
|
|
56
75
|
if (dbType === "sqlite") {
|
|
57
76
|
const { dbPath } = await inquirer.prompt([{
|
|
58
77
|
type: "input",
|
|
@@ -60,159 +79,305 @@ async function main() {
|
|
|
60
79
|
message: "Database file path:",
|
|
61
80
|
default: "./agenticmail-enterprise.db"
|
|
62
81
|
}]);
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
{
|
|
68
|
-
|
|
82
|
+
return { type: dbType, connectionString: dbPath };
|
|
83
|
+
}
|
|
84
|
+
if (dbType === "dynamodb") {
|
|
85
|
+
const answers = await inquirer.prompt([
|
|
86
|
+
{
|
|
87
|
+
type: "input",
|
|
88
|
+
name: "region",
|
|
89
|
+
message: "AWS Region:",
|
|
90
|
+
default: "us-east-1"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
type: "input",
|
|
94
|
+
name: "accessKeyId",
|
|
95
|
+
message: "AWS Access Key ID:",
|
|
96
|
+
validate: (v) => v.length > 0 || "Required"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: "password",
|
|
100
|
+
name: "secretAccessKey",
|
|
101
|
+
message: "AWS Secret Access Key:",
|
|
102
|
+
mask: "*",
|
|
103
|
+
validate: (v) => v.length > 0 || "Required"
|
|
104
|
+
}
|
|
69
105
|
]);
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
{
|
|
106
|
+
return { type: dbType, ...answers };
|
|
107
|
+
}
|
|
108
|
+
if (dbType === "turso") {
|
|
109
|
+
const answers = await inquirer.prompt([
|
|
110
|
+
{
|
|
111
|
+
type: "input",
|
|
112
|
+
name: "connectionString",
|
|
113
|
+
message: "Turso database URL:",
|
|
114
|
+
suffix: chalk.dim(" (e.g. libsql://db-org.turso.io)"),
|
|
115
|
+
validate: (v) => v.length > 0 || "Required"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
type: "password",
|
|
119
|
+
name: "authToken",
|
|
120
|
+
message: "Turso auth token:",
|
|
121
|
+
mask: "*",
|
|
122
|
+
validate: (v) => v.length > 0 || "Required"
|
|
123
|
+
}
|
|
75
124
|
]);
|
|
76
|
-
|
|
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;
|
|
125
|
+
return { type: dbType, connectionString: answers.connectionString, authToken: answers.authToken };
|
|
94
126
|
}
|
|
127
|
+
const hint = CONNECTION_HINTS[dbType] || "";
|
|
128
|
+
const { connectionString } = await inquirer.prompt([{
|
|
129
|
+
type: "input",
|
|
130
|
+
name: "connectionString",
|
|
131
|
+
message: "Connection string:",
|
|
132
|
+
suffix: hint ? chalk.dim(` (e.g. ${hint})`) : "",
|
|
133
|
+
validate: (v) => v.length > 0 || "Connection string is required"
|
|
134
|
+
}]);
|
|
135
|
+
return { type: dbType, connectionString };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/setup/deployment.ts
|
|
139
|
+
async function promptDeployment(inquirer, chalk) {
|
|
140
|
+
console.log("");
|
|
141
|
+
console.log(chalk.bold.cyan(" Step 3 of 4: Deployment"));
|
|
142
|
+
console.log(chalk.dim(" Where should your dashboard run?\n"));
|
|
95
143
|
const { deployTarget } = await inquirer.prompt([{
|
|
96
144
|
type: "list",
|
|
97
145
|
name: "deployTarget",
|
|
98
146
|
message: "Deploy to:",
|
|
99
147
|
choices: [
|
|
100
|
-
{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
{
|
|
148
|
+
{
|
|
149
|
+
name: `AgenticMail Cloud ${chalk.dim("(managed, instant URL)")}`,
|
|
150
|
+
value: "cloud"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: `Fly.io ${chalk.dim("(your account)")}`,
|
|
154
|
+
value: "fly"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: `Railway ${chalk.dim("(your account)")}`,
|
|
158
|
+
value: "railway"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: `Docker ${chalk.dim("(self-hosted)")}`,
|
|
162
|
+
value: "docker"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: `Local ${chalk.dim("(dev/testing, runs here)")}`,
|
|
166
|
+
value: "local"
|
|
167
|
+
}
|
|
105
168
|
]
|
|
106
169
|
}]);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
}
|
|
170
|
+
return { target: deployTarget };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/setup/domain.ts
|
|
174
|
+
async function promptDomain(inquirer, chalk, deployTarget) {
|
|
175
|
+
if (deployTarget === "local") {
|
|
176
|
+
return {};
|
|
125
177
|
}
|
|
126
178
|
console.log("");
|
|
179
|
+
console.log(chalk.bold.cyan(" Step 4 of 4: Custom Domain"));
|
|
180
|
+
console.log(chalk.dim(" Optional \u2014 you can add this later.\n"));
|
|
181
|
+
const { wantsDomain } = await inquirer.prompt([{
|
|
182
|
+
type: "confirm",
|
|
183
|
+
name: "wantsDomain",
|
|
184
|
+
message: "Add a custom domain?",
|
|
185
|
+
default: false
|
|
186
|
+
}]);
|
|
187
|
+
if (!wantsDomain) return {};
|
|
188
|
+
const { domain } = await inquirer.prompt([{
|
|
189
|
+
type: "input",
|
|
190
|
+
name: "domain",
|
|
191
|
+
message: "Custom domain:",
|
|
192
|
+
suffix: chalk.dim(" (e.g. agents.acme.com)"),
|
|
193
|
+
validate: (v) => {
|
|
194
|
+
if (!v.includes(".")) return "Enter a valid domain (e.g. agents.acme.com)";
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
}]);
|
|
198
|
+
return { customDomain: domain };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/setup/provision.ts
|
|
202
|
+
import { randomUUID } from "crypto";
|
|
203
|
+
async function provision(config, ora, chalk) {
|
|
127
204
|
const spinner = ora("Connecting to database...").start();
|
|
205
|
+
const jwtSecret = randomUUID() + randomUUID();
|
|
128
206
|
try {
|
|
129
|
-
const
|
|
207
|
+
const { createAdapter } = await import("./factory-HINWFYZ3.js");
|
|
208
|
+
const db = await createAdapter(config.database);
|
|
130
209
|
spinner.text = "Running migrations...";
|
|
131
210
|
await db.migrate();
|
|
132
211
|
spinner.succeed("Database ready");
|
|
133
212
|
spinner.start("Creating company...");
|
|
134
|
-
|
|
135
|
-
name: companyName,
|
|
136
|
-
subdomain,
|
|
137
|
-
domain: customDomain
|
|
213
|
+
await db.updateSettings({
|
|
214
|
+
name: config.company.companyName,
|
|
215
|
+
subdomain: config.company.subdomain,
|
|
216
|
+
domain: config.domain.customDomain
|
|
138
217
|
});
|
|
139
218
|
spinner.succeed("Company created");
|
|
140
219
|
spinner.start("Creating admin account...");
|
|
141
220
|
const admin = await db.createUser({
|
|
142
|
-
email: adminEmail,
|
|
221
|
+
email: config.company.adminEmail,
|
|
143
222
|
name: "Admin",
|
|
144
223
|
role: "owner",
|
|
145
|
-
password: adminPassword
|
|
224
|
+
password: config.company.adminPassword
|
|
146
225
|
});
|
|
147
226
|
await db.logEvent({
|
|
148
227
|
actor: admin.id,
|
|
149
228
|
actorType: "system",
|
|
150
229
|
action: "setup.complete",
|
|
151
|
-
resource: `company:${subdomain}`,
|
|
152
|
-
details: {
|
|
230
|
+
resource: `company:${config.company.subdomain}`,
|
|
231
|
+
details: {
|
|
232
|
+
dbType: config.database.type,
|
|
233
|
+
deployTarget: config.deployTarget,
|
|
234
|
+
companyName: config.company.companyName
|
|
235
|
+
}
|
|
153
236
|
});
|
|
154
237
|
spinner.succeed("Admin account created");
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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("");
|
|
238
|
+
const result = await deploy(config, db, jwtSecret, spinner, chalk);
|
|
239
|
+
return {
|
|
240
|
+
success: true,
|
|
241
|
+
url: result.url,
|
|
242
|
+
jwtSecret,
|
|
243
|
+
db,
|
|
244
|
+
serverClose: result.close
|
|
245
|
+
};
|
|
211
246
|
} catch (err) {
|
|
212
247
|
spinner.fail(`Setup failed: ${err.message}`);
|
|
248
|
+
return {
|
|
249
|
+
success: false,
|
|
250
|
+
error: err.message,
|
|
251
|
+
jwtSecret,
|
|
252
|
+
db: null
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async function deploy(config, db, jwtSecret, spinner, chalk) {
|
|
257
|
+
const { deployTarget, company, database, domain } = config;
|
|
258
|
+
if (deployTarget === "cloud") {
|
|
259
|
+
spinner.start("Deploying to AgenticMail Cloud...");
|
|
260
|
+
const { deployToCloud } = await import("./managed-RZITNPXG.js");
|
|
261
|
+
const result = await deployToCloud({
|
|
262
|
+
subdomain: company.subdomain,
|
|
263
|
+
plan: "free",
|
|
264
|
+
dbType: database.type,
|
|
265
|
+
dbConnectionString: database.connectionString || "",
|
|
266
|
+
jwtSecret
|
|
267
|
+
});
|
|
268
|
+
spinner.succeed(`Deployed to ${result.url}`);
|
|
269
|
+
printCloudSuccess(chalk, result.url, company.adminEmail, domain.customDomain, company.subdomain);
|
|
270
|
+
return { url: result.url };
|
|
271
|
+
}
|
|
272
|
+
if (deployTarget === "docker") {
|
|
273
|
+
const { generateDockerCompose } = await import("./managed-RZITNPXG.js");
|
|
274
|
+
const compose = generateDockerCompose({
|
|
275
|
+
dbType: database.type,
|
|
276
|
+
dbConnectionString: database.connectionString || "",
|
|
277
|
+
port: 3e3,
|
|
278
|
+
jwtSecret
|
|
279
|
+
});
|
|
280
|
+
const { writeFileSync } = await import("fs");
|
|
281
|
+
writeFileSync("docker-compose.yml", compose);
|
|
282
|
+
spinner.succeed("docker-compose.yml generated");
|
|
283
|
+
console.log("");
|
|
284
|
+
console.log(chalk.green.bold(" Docker deployment ready!"));
|
|
285
|
+
console.log("");
|
|
286
|
+
console.log(` Run: ${chalk.cyan("docker compose up -d")}`);
|
|
287
|
+
console.log(` Dashboard: ${chalk.cyan("http://localhost:3000")}`);
|
|
288
|
+
return { url: "http://localhost:3000" };
|
|
289
|
+
}
|
|
290
|
+
if (deployTarget === "fly") {
|
|
291
|
+
const { generateFlyToml } = await import("./managed-RZITNPXG.js");
|
|
292
|
+
const flyToml = generateFlyToml(`am-${company.subdomain}`, "iad");
|
|
293
|
+
const { writeFileSync } = await import("fs");
|
|
294
|
+
writeFileSync("fly.toml", flyToml);
|
|
295
|
+
spinner.succeed("fly.toml generated");
|
|
296
|
+
console.log("");
|
|
297
|
+
console.log(chalk.green.bold(" Fly.io deployment ready!"));
|
|
298
|
+
console.log("");
|
|
299
|
+
console.log(` 1. ${chalk.cyan("fly launch --copy-config")}`);
|
|
300
|
+
console.log(` 2. ${chalk.cyan(`fly secrets set DATABASE_URL="${database.connectionString}" JWT_SECRET="${jwtSecret}"`)}`);
|
|
301
|
+
console.log(` 3. ${chalk.cyan("fly deploy")}`);
|
|
302
|
+
return {};
|
|
303
|
+
}
|
|
304
|
+
if (deployTarget === "railway") {
|
|
305
|
+
const { generateRailwayConfig } = await import("./managed-RZITNPXG.js");
|
|
306
|
+
const railwayConfig = generateRailwayConfig();
|
|
307
|
+
const { writeFileSync } = await import("fs");
|
|
308
|
+
writeFileSync("railway.toml", railwayConfig);
|
|
309
|
+
spinner.succeed("railway.toml generated");
|
|
310
|
+
console.log("");
|
|
311
|
+
console.log(chalk.green.bold(" Railway deployment ready!"));
|
|
312
|
+
console.log("");
|
|
313
|
+
console.log(` 1. ${chalk.cyan("railway init")}`);
|
|
314
|
+
console.log(` 2. ${chalk.cyan("railway link")}`);
|
|
315
|
+
console.log(` 3. ${chalk.cyan("railway up")}`);
|
|
316
|
+
return {};
|
|
317
|
+
}
|
|
318
|
+
spinner.start("Starting local server...");
|
|
319
|
+
const { createServer } = await import("./server-H3C6WUOS.js");
|
|
320
|
+
const server = createServer({ port: 3e3, db, jwtSecret });
|
|
321
|
+
const handle = await server.start();
|
|
322
|
+
spinner.succeed("Server running");
|
|
323
|
+
console.log("");
|
|
324
|
+
console.log(chalk.green.bold(" AgenticMail Enterprise is running!"));
|
|
325
|
+
console.log("");
|
|
326
|
+
console.log(` ${chalk.bold("Dashboard:")} ${chalk.cyan("http://localhost:3000")}`);
|
|
327
|
+
console.log(` ${chalk.bold("API:")} ${chalk.cyan("http://localhost:3000/api")}`);
|
|
328
|
+
console.log(` ${chalk.bold("Admin:")} ${company.adminEmail}`);
|
|
329
|
+
console.log("");
|
|
330
|
+
console.log(chalk.dim(" Press Ctrl+C to stop"));
|
|
331
|
+
return { url: "http://localhost:3000", close: handle.close };
|
|
332
|
+
}
|
|
333
|
+
function printCloudSuccess(chalk, url, adminEmail, customDomain, subdomain) {
|
|
334
|
+
console.log("");
|
|
335
|
+
console.log(chalk.green.bold(" Your dashboard is live!"));
|
|
336
|
+
console.log("");
|
|
337
|
+
console.log(` ${chalk.bold("URL:")} ${chalk.cyan(url)}`);
|
|
338
|
+
console.log(` ${chalk.bold("Admin:")} ${adminEmail}`);
|
|
339
|
+
console.log(` ${chalk.bold("Password:")} (the one you just set)`);
|
|
340
|
+
if (customDomain) {
|
|
341
|
+
console.log("");
|
|
342
|
+
console.log(chalk.dim(` To use ${customDomain}:`));
|
|
343
|
+
console.log(chalk.dim(` Add CNAME: ${customDomain} \u2192 ${subdomain}.agenticmail.cloud`));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// src/setup/index.ts
|
|
348
|
+
async function runSetupWizard() {
|
|
349
|
+
const { default: inquirer } = await import("inquirer");
|
|
350
|
+
const { default: ora } = await import("ora");
|
|
351
|
+
const { default: chalk } = await import("chalk");
|
|
352
|
+
console.log("");
|
|
353
|
+
console.log(chalk.bold(" AgenticMail Enterprise"));
|
|
354
|
+
console.log(chalk.dim(" AI Agent Identity & Email for Organizations"));
|
|
355
|
+
console.log("");
|
|
356
|
+
console.log(chalk.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
357
|
+
console.log("");
|
|
358
|
+
const company = await promptCompanyInfo(inquirer, chalk);
|
|
359
|
+
const database = await promptDatabase(inquirer, chalk);
|
|
360
|
+
const { target: deployTarget } = await promptDeployment(inquirer, chalk);
|
|
361
|
+
const domain = await promptDomain(inquirer, chalk, deployTarget);
|
|
362
|
+
console.log("");
|
|
363
|
+
console.log(chalk.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
364
|
+
console.log("");
|
|
365
|
+
const result = await provision(
|
|
366
|
+
{ company, database, deployTarget, domain },
|
|
367
|
+
ora,
|
|
368
|
+
chalk
|
|
369
|
+
);
|
|
370
|
+
if (!result.success) {
|
|
213
371
|
console.error("");
|
|
214
|
-
console.error(chalk.
|
|
372
|
+
console.error(chalk.red(` Setup failed: ${result.error}`));
|
|
373
|
+
console.error(chalk.dim(" Check your database connection and try again."));
|
|
215
374
|
process.exit(1);
|
|
216
375
|
}
|
|
376
|
+
console.log("");
|
|
217
377
|
}
|
|
218
|
-
|
|
378
|
+
|
|
379
|
+
// src/cli.ts
|
|
380
|
+
runSetupWizard().catch((err) => {
|
|
381
|
+
console.error("Fatal error:", err.message);
|
|
382
|
+
process.exit(1);
|
|
383
|
+
});
|