@agenticmail/enterprise 0.2.2 → 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.
- package/README.md +929 -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 +18 -2
- 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/src/cli.ts
CHANGED
|
@@ -1,260 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* AgenticMail Enterprise CLI
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
5
|
* Interactive setup wizard that provisions a cloud-hosted
|
|
6
6
|
* enterprise dashboard for managing AI agent identities.
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
8
|
* Usage: npx @agenticmail/enterprise
|
|
9
|
+
*
|
|
10
|
+
* The wizard is split into modular steps under setup/:
|
|
11
|
+
* 1. Company info → setup/company.ts
|
|
12
|
+
* 2. Database → setup/database.ts
|
|
13
|
+
* 3. Deployment → setup/deployment.ts
|
|
14
|
+
* 4. Custom domain → setup/domain.ts
|
|
15
|
+
* → Provisioning → setup/provision.ts
|
|
16
|
+
* → Orchestrator → setup/index.ts
|
|
9
17
|
*/
|
|
10
18
|
|
|
11
|
-
import {
|
|
12
|
-
import { createAdapter, getSupportedDatabases } from './db/factory.js';
|
|
13
|
-
import { createServer } from './server.js';
|
|
14
|
-
import { deployToCloud, generateDockerCompose, generateFlyToml } from './deploy/managed.js';
|
|
19
|
+
import { runSetupWizard } from './setup/index.js';
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const { default: chalk } = await import('chalk');
|
|
21
|
-
|
|
22
|
-
console.log('');
|
|
23
|
-
console.log(chalk.bold('🏢 AgenticMail Enterprise'));
|
|
24
|
-
console.log(chalk.dim(' AI Agent Identity & Email for Organizations'));
|
|
25
|
-
console.log('');
|
|
26
|
-
|
|
27
|
-
// ─── Step 1: Company Info ───────────────────────────────
|
|
28
|
-
|
|
29
|
-
const { companyName, adminEmail, adminPassword } = await inquirer.prompt([
|
|
30
|
-
{
|
|
31
|
-
type: 'input',
|
|
32
|
-
name: 'companyName',
|
|
33
|
-
message: 'Company name:',
|
|
34
|
-
validate: (v: string) => v.length > 0 || 'Required',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
type: 'input',
|
|
38
|
-
name: 'adminEmail',
|
|
39
|
-
message: 'Admin email:',
|
|
40
|
-
validate: (v: string) => v.includes('@') || 'Must be a valid email',
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
type: 'password',
|
|
44
|
-
name: 'adminPassword',
|
|
45
|
-
message: 'Admin password:',
|
|
46
|
-
mask: '*',
|
|
47
|
-
validate: (v: string) => v.length >= 8 || 'Must be at least 8 characters',
|
|
48
|
-
},
|
|
49
|
-
]);
|
|
50
|
-
|
|
51
|
-
// ─── Step 2: Database ───────────────────────────────────
|
|
52
|
-
|
|
53
|
-
const databases = getSupportedDatabases();
|
|
54
|
-
const { dbType } = await inquirer.prompt([
|
|
55
|
-
{
|
|
56
|
-
type: 'list',
|
|
57
|
-
name: 'dbType',
|
|
58
|
-
message: 'Database:',
|
|
59
|
-
choices: databases.map(d => ({
|
|
60
|
-
name: `${d.label} ${chalk.dim(`(${d.group})`)}`,
|
|
61
|
-
value: d.type,
|
|
62
|
-
})),
|
|
63
|
-
},
|
|
64
|
-
]);
|
|
65
|
-
|
|
66
|
-
let dbConfig: any = { type: dbType };
|
|
67
|
-
|
|
68
|
-
if (dbType === 'sqlite') {
|
|
69
|
-
const { dbPath } = await inquirer.prompt([{
|
|
70
|
-
type: 'input',
|
|
71
|
-
name: 'dbPath',
|
|
72
|
-
message: 'Database file path:',
|
|
73
|
-
default: './agenticmail-enterprise.db',
|
|
74
|
-
}]);
|
|
75
|
-
dbConfig.connectionString = dbPath;
|
|
76
|
-
} else if (dbType === 'dynamodb') {
|
|
77
|
-
const { region, accessKeyId, secretAccessKey } = await inquirer.prompt([
|
|
78
|
-
{ type: 'input', name: 'region', message: 'AWS Region:', default: 'us-east-1' },
|
|
79
|
-
{ type: 'input', name: 'accessKeyId', message: 'AWS Access Key ID:' },
|
|
80
|
-
{ type: 'password', name: 'secretAccessKey', message: 'AWS Secret Access Key:', mask: '*' },
|
|
81
|
-
]);
|
|
82
|
-
dbConfig = { ...dbConfig, region, accessKeyId, secretAccessKey };
|
|
83
|
-
} else if (dbType === 'turso') {
|
|
84
|
-
const { connectionString, authToken } = await inquirer.prompt([
|
|
85
|
-
{ type: 'input', name: 'connectionString', message: 'Turso database URL:', placeholder: 'libsql://...' },
|
|
86
|
-
{ type: 'password', name: 'authToken', message: 'Turso auth token:', mask: '*' },
|
|
87
|
-
]);
|
|
88
|
-
dbConfig = { ...dbConfig, connectionString, authToken };
|
|
89
|
-
} else {
|
|
90
|
-
// All other SQL/NoSQL databases use a connection string
|
|
91
|
-
const hints: Record<string, string> = {
|
|
92
|
-
postgres: 'postgresql://user:pass@host:5432/dbname',
|
|
93
|
-
mysql: 'mysql://user:pass@host:3306/dbname',
|
|
94
|
-
mongodb: 'mongodb+srv://user:pass@cluster.mongodb.net/dbname',
|
|
95
|
-
supabase: 'postgresql://postgres:pass@db.xxxx.supabase.co:5432/postgres',
|
|
96
|
-
neon: 'postgresql://user:pass@ep-xxx.us-east-1.aws.neon.tech/dbname?sslmode=require',
|
|
97
|
-
planetscale: 'mysql://user:pass@aws.connect.psdb.cloud/dbname?ssl={"rejectUnauthorized":true}',
|
|
98
|
-
cockroachdb: 'postgresql://user:pass@cluster.cockroachlabs.cloud:26257/dbname?sslmode=verify-full',
|
|
99
|
-
};
|
|
100
|
-
const { connectionString } = await inquirer.prompt([{
|
|
101
|
-
type: 'input',
|
|
102
|
-
name: 'connectionString',
|
|
103
|
-
message: 'Connection string:',
|
|
104
|
-
suffix: chalk.dim(` (e.g. ${hints[dbType] || ''})`),
|
|
105
|
-
}]);
|
|
106
|
-
dbConfig.connectionString = connectionString;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// ─── Step 3: Deployment ─────────────────────────────────
|
|
110
|
-
|
|
111
|
-
const { deployTarget } = await inquirer.prompt([{
|
|
112
|
-
type: 'list',
|
|
113
|
-
name: 'deployTarget',
|
|
114
|
-
message: 'Deploy to:',
|
|
115
|
-
choices: [
|
|
116
|
-
{ name: `AgenticMail Cloud ${chalk.dim('(managed, instant URL)')}`, value: 'cloud' },
|
|
117
|
-
{ name: `Fly.io ${chalk.dim('(your account)')}`, value: 'fly' },
|
|
118
|
-
{ name: `Railway ${chalk.dim('(your account)')}`, value: 'railway' },
|
|
119
|
-
{ name: `Docker ${chalk.dim('(self-hosted)')}`, value: 'docker' },
|
|
120
|
-
{ name: `Local ${chalk.dim('(dev/testing, runs here)')}`, value: 'local' },
|
|
121
|
-
],
|
|
122
|
-
}]);
|
|
123
|
-
|
|
124
|
-
// Generate subdomain from company name
|
|
125
|
-
const subdomain = companyName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
126
|
-
|
|
127
|
-
// ─── Step 4: Custom Domain (optional) ──────────────────
|
|
128
|
-
|
|
129
|
-
let customDomain: string | undefined;
|
|
130
|
-
if (deployTarget !== 'local') {
|
|
131
|
-
const { wantsDomain } = await inquirer.prompt([{
|
|
132
|
-
type: 'confirm',
|
|
133
|
-
name: 'wantsDomain',
|
|
134
|
-
message: 'Add a custom domain? (can do later)',
|
|
135
|
-
default: false,
|
|
136
|
-
}]);
|
|
137
|
-
if (wantsDomain) {
|
|
138
|
-
const { domain } = await inquirer.prompt([{
|
|
139
|
-
type: 'input',
|
|
140
|
-
name: 'domain',
|
|
141
|
-
message: 'Custom domain:',
|
|
142
|
-
suffix: chalk.dim(' (e.g. agents.acme.com)'),
|
|
143
|
-
}]);
|
|
144
|
-
customDomain = domain;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// ─── Provisioning ──────────────────────────────────────
|
|
149
|
-
|
|
150
|
-
console.log('');
|
|
151
|
-
const spinner = ora('Connecting to database...').start();
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
// Connect to database
|
|
155
|
-
const db = await createAdapter(dbConfig);
|
|
156
|
-
spinner.text = 'Running migrations...';
|
|
157
|
-
await db.migrate();
|
|
158
|
-
spinner.succeed('Database ready');
|
|
159
|
-
|
|
160
|
-
// Create company settings
|
|
161
|
-
spinner.start('Creating company...');
|
|
162
|
-
// Insert initial company settings
|
|
163
|
-
// (Using raw SQL since we don't have a createSettings method)
|
|
164
|
-
const settings = await db.updateSettings({
|
|
165
|
-
name: companyName,
|
|
166
|
-
subdomain,
|
|
167
|
-
domain: customDomain,
|
|
168
|
-
});
|
|
169
|
-
spinner.succeed('Company created');
|
|
170
|
-
|
|
171
|
-
// Create admin user
|
|
172
|
-
spinner.start('Creating admin account...');
|
|
173
|
-
const admin = await db.createUser({
|
|
174
|
-
email: adminEmail,
|
|
175
|
-
name: 'Admin',
|
|
176
|
-
role: 'owner',
|
|
177
|
-
password: adminPassword,
|
|
178
|
-
});
|
|
179
|
-
await db.logEvent({
|
|
180
|
-
actor: admin.id, actorType: 'system', action: 'setup.complete',
|
|
181
|
-
resource: `company:${subdomain}`,
|
|
182
|
-
details: { dbType, deployTarget, companyName },
|
|
183
|
-
});
|
|
184
|
-
spinner.succeed('Admin account created');
|
|
185
|
-
|
|
186
|
-
// Generate JWT secret
|
|
187
|
-
const jwtSecret = randomUUID() + randomUUID();
|
|
188
|
-
|
|
189
|
-
// Deploy
|
|
190
|
-
if (deployTarget === 'cloud') {
|
|
191
|
-
spinner.start('Deploying to AgenticMail Cloud...');
|
|
192
|
-
const result = await deployToCloud({ subdomain, plan: 'free' });
|
|
193
|
-
spinner.succeed(`Deployed to ${result.url}`);
|
|
194
|
-
|
|
195
|
-
console.log('');
|
|
196
|
-
console.log(chalk.green.bold('🎉 Your dashboard is live!'));
|
|
197
|
-
console.log('');
|
|
198
|
-
console.log(` ${chalk.bold('URL:')} ${result.url}`);
|
|
199
|
-
console.log(` ${chalk.bold('Admin:')} ${adminEmail}`);
|
|
200
|
-
console.log(` ${chalk.bold('Password:')} (the one you just set)`);
|
|
201
|
-
if (customDomain) {
|
|
202
|
-
console.log('');
|
|
203
|
-
console.log(chalk.dim(` To use ${customDomain}:`));
|
|
204
|
-
console.log(chalk.dim(` Add CNAME: ${customDomain} → ${subdomain}.agenticmail.cloud`));
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
} else if (deployTarget === 'docker') {
|
|
208
|
-
const compose = generateDockerCompose({
|
|
209
|
-
dbType, dbConnectionString: dbConfig.connectionString || '',
|
|
210
|
-
port: 3000, jwtSecret,
|
|
211
|
-
});
|
|
212
|
-
const { writeFileSync } = await import('fs');
|
|
213
|
-
writeFileSync('docker-compose.yml', compose);
|
|
214
|
-
spinner.succeed('docker-compose.yml generated');
|
|
215
|
-
|
|
216
|
-
console.log('');
|
|
217
|
-
console.log(chalk.green.bold('🐳 Docker deployment ready!'));
|
|
218
|
-
console.log('');
|
|
219
|
-
console.log(' Run: docker compose up -d');
|
|
220
|
-
console.log(' Dashboard: http://localhost:3000');
|
|
221
|
-
|
|
222
|
-
} else if (deployTarget === 'fly') {
|
|
223
|
-
const flyToml = generateFlyToml(`am-${subdomain}`, 'iad');
|
|
224
|
-
const { writeFileSync } = await import('fs');
|
|
225
|
-
writeFileSync('fly.toml', flyToml);
|
|
226
|
-
spinner.succeed('fly.toml generated');
|
|
227
|
-
|
|
228
|
-
console.log('');
|
|
229
|
-
console.log(chalk.green.bold('🪰 Fly.io deployment ready!'));
|
|
230
|
-
console.log('');
|
|
231
|
-
console.log(' Run: fly launch --copy-config');
|
|
232
|
-
console.log(` Then: fly secrets set DATABASE_URL="${dbConfig.connectionString}" JWT_SECRET="${jwtSecret}"`);
|
|
233
|
-
|
|
234
|
-
} else if (deployTarget === 'local') {
|
|
235
|
-
spinner.start('Starting local server...');
|
|
236
|
-
const server = createServer({ port: 3000, db, jwtSecret });
|
|
237
|
-
server.start();
|
|
238
|
-
spinner.succeed('Server running');
|
|
239
|
-
|
|
240
|
-
console.log('');
|
|
241
|
-
console.log(chalk.green.bold('🎉 AgenticMail Enterprise is running!'));
|
|
242
|
-
console.log('');
|
|
243
|
-
console.log(` ${chalk.bold('Dashboard:')} http://localhost:3000`);
|
|
244
|
-
console.log(` ${chalk.bold('API:')} http://localhost:3000/api`);
|
|
245
|
-
console.log(` ${chalk.bold('Admin:')} ${adminEmail}`);
|
|
246
|
-
console.log('');
|
|
247
|
-
console.log(chalk.dim(' Press Ctrl+C to stop'));
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
console.log('');
|
|
251
|
-
|
|
252
|
-
} catch (err: any) {
|
|
253
|
-
spinner.fail(`Setup failed: ${err.message}`);
|
|
254
|
-
console.error('');
|
|
255
|
-
console.error(chalk.dim(err.stack));
|
|
256
|
-
process.exit(1);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
main().catch(console.error);
|
|
21
|
+
runSetupWizard().catch((err) => {
|
|
22
|
+
console.error('Fatal error:', err.message);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
});
|