@agenticmail/enterprise 0.5.296 → 0.5.297

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.
@@ -0,0 +1,149 @@
1
+ import {
2
+ DomainLock
3
+ } from "./chunk-UPU23ZRG.js";
4
+ import "./chunk-KFQGP6VL.js";
5
+
6
+ // src/domain-lock/cli-verify.ts
7
+ function getFlag(args, name) {
8
+ const idx = args.indexOf(name);
9
+ if (idx !== -1 && args[idx + 1]) return args[idx + 1];
10
+ return void 0;
11
+ }
12
+ function hasFlag(args, name) {
13
+ return args.includes(name);
14
+ }
15
+ function detectDbType(url) {
16
+ const u = url.toLowerCase().trim();
17
+ if (u.startsWith("postgres") || u.startsWith("pg:")) return "postgres";
18
+ if (u.startsWith("mysql")) return "mysql";
19
+ if (u.startsWith("mongodb")) return "mongodb";
20
+ if (u.startsWith("libsql") || u.includes(".turso.io")) return "turso";
21
+ if (u.endsWith(".db") || u.endsWith(".sqlite") || u.endsWith(".sqlite3") || u.startsWith("file:")) return "sqlite";
22
+ return "postgres";
23
+ }
24
+ async function runVerifyDomain(args) {
25
+ const { default: chalk } = await import("chalk");
26
+ const { default: ora } = await import("ora");
27
+ console.log("");
28
+ console.log(chalk.bold(" AgenticMail Enterprise \u2014 Domain Verification"));
29
+ console.log("");
30
+ let domain = getFlag(args, "--domain");
31
+ let dnsChallenge;
32
+ let db = null;
33
+ let dbConnected = false;
34
+ const envDbUrl = process.env.DATABASE_URL;
35
+ if (envDbUrl) {
36
+ const dbType = detectDbType(envDbUrl);
37
+ const spinner = ora(`Connecting to database (${dbType})...`).start();
38
+ try {
39
+ const { createAdapter } = await import("./factory-M6E7YAKW.js");
40
+ db = await createAdapter({ type: dbType, connectionString: envDbUrl });
41
+ await db.migrate();
42
+ const settings = await db.getSettings();
43
+ if (!domain && settings?.domain) domain = settings.domain;
44
+ if (settings?.domainDnsChallenge) dnsChallenge = settings.domainDnsChallenge;
45
+ dbConnected = true;
46
+ spinner.succeed(`Connected to ${dbType} database` + (domain ? ` (domain: ${domain})` : ""));
47
+ } catch (err) {
48
+ spinner.warn(`Could not connect via DATABASE_URL: ${err.message}`);
49
+ db = null;
50
+ }
51
+ }
52
+ if (!dbConnected) {
53
+ const dbPath = getFlag(args, "--db");
54
+ const dbType = getFlag(args, "--db-type") || "sqlite";
55
+ if (dbPath) {
56
+ const spinner = ora(`Connecting to ${dbType} database...`).start();
57
+ try {
58
+ const { createAdapter } = await import("./factory-M6E7YAKW.js");
59
+ db = await createAdapter({ type: dbType, connectionString: dbPath });
60
+ await db.migrate();
61
+ const settings = await db.getSettings();
62
+ if (!domain && settings?.domain) domain = settings.domain;
63
+ if (settings?.domainDnsChallenge) dnsChallenge = settings.domainDnsChallenge;
64
+ dbConnected = true;
65
+ spinner.succeed(`Connected to ${dbType} database`);
66
+ } catch {
67
+ spinner.warn("Could not read local database");
68
+ }
69
+ }
70
+ }
71
+ if (!domain) {
72
+ const { default: inquirer } = await import("inquirer");
73
+ const answer = await inquirer.prompt([{
74
+ type: "input",
75
+ name: "domain",
76
+ message: "Domain to verify:",
77
+ suffix: chalk.dim(" (e.g. agents.yourcompany.com)"),
78
+ validate: (v) => v.includes(".") || "Enter a valid domain",
79
+ filter: (v) => v.trim().toLowerCase()
80
+ }]);
81
+ domain = answer.domain;
82
+ }
83
+ const lock = new DomainLock();
84
+ const maxAttempts = hasFlag(args, "--poll") ? 5 : 1;
85
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
86
+ const spinner = ora(
87
+ maxAttempts > 1 ? `Checking DNS verification (attempt ${attempt}/${maxAttempts})...` : "Checking DNS verification..."
88
+ ).start();
89
+ try {
90
+ const result = await lock.checkVerification(domain);
91
+ if (!result.success) {
92
+ spinner.fail("Verification check failed");
93
+ console.log("");
94
+ console.error(chalk.red(` ${result.error}`));
95
+ console.log("");
96
+ if (db) await db.disconnect();
97
+ process.exit(1);
98
+ }
99
+ if (result.verified) {
100
+ spinner.succeed("Domain verified!");
101
+ if (dbConnected && db) {
102
+ try {
103
+ await db.updateSettings({
104
+ domainStatus: "verified",
105
+ domainVerifiedAt: (/* @__PURE__ */ new Date()).toISOString()
106
+ });
107
+ console.log(chalk.dim(" Database updated with verified status."));
108
+ } catch {
109
+ }
110
+ }
111
+ console.log("");
112
+ console.log(chalk.green.bold(` \u2713 ${domain} is verified and protected.`));
113
+ console.log(chalk.dim(" Your deployment domain is locked. No other instance can claim it."));
114
+ console.log(chalk.dim(" The system now operates 100% offline \u2014 no outbound calls are made."));
115
+ console.log("");
116
+ if (db) await db.disconnect();
117
+ return;
118
+ }
119
+ } catch (err) {
120
+ spinner.warn(`Check failed: ${err.message}`);
121
+ }
122
+ if (attempt < maxAttempts) {
123
+ const waitSpinner = ora(`DNS record not found yet. Retrying in 10 seconds...`).start();
124
+ await new Promise((r) => setTimeout(r, 1e4));
125
+ waitSpinner.stop();
126
+ }
127
+ }
128
+ console.log("");
129
+ console.log(chalk.yellow(" DNS record not detected yet."));
130
+ console.log("");
131
+ console.log(chalk.bold(" Make sure this TXT record exists at your DNS provider:"));
132
+ console.log("");
133
+ console.log(` ${chalk.bold("Host:")} ${chalk.cyan(`_agenticmail-verify.${domain}`)}`);
134
+ console.log(` ${chalk.bold("Type:")} ${chalk.cyan("TXT")}`);
135
+ if (dnsChallenge) {
136
+ console.log(` ${chalk.bold("Value:")} ${chalk.cyan(dnsChallenge)}`);
137
+ } else {
138
+ console.log(` ${chalk.bold("Value:")} ${chalk.dim("(check your dashboard or setup output)")}`);
139
+ }
140
+ console.log("");
141
+ console.log(chalk.dim(" DNS propagation can take up to 48 hours."));
142
+ console.log(chalk.dim(" Run with --poll to retry automatically:"));
143
+ console.log(chalk.dim(` npx @agenticmail/enterprise verify-domain --domain ${domain} --poll`));
144
+ console.log("");
145
+ if (db) await db.disconnect();
146
+ }
147
+ export {
148
+ runVerifyDomain
149
+ };
package/dist/cli.js CHANGED
@@ -14,10 +14,10 @@ switch (command) {
14
14
  import("./cli-submit-skill-LDFJGSKO.js").then((m) => m.runSubmitSkill(args.slice(1))).catch(fatal);
15
15
  break;
16
16
  case "recover":
17
- import("./cli-recover-PMJRFJNY.js").then((m) => m.runRecover(args.slice(1))).catch(fatal);
17
+ import("./cli-recover-NXARYVSH.js").then((m) => m.runRecover(args.slice(1))).catch(fatal);
18
18
  break;
19
19
  case "verify-domain":
20
- import("./cli-verify-HYUKQELV.js").then((m) => m.runVerifyDomain(args.slice(1))).catch(fatal);
20
+ import("./cli-verify-RKB6KQN4.js").then((m) => m.runVerifyDomain(args.slice(1))).catch(fatal);
21
21
  break;
22
22
  case "reset-password":
23
23
  import("./cli-reset-password-SO5Y6MW7.js").then((m) => m.runResetPassword(args.slice(1))).catch(fatal);
@@ -57,14 +57,14 @@ Skill Development:
57
57
  break;
58
58
  case "serve":
59
59
  case "start":
60
- import("./cli-serve-TTN3XR4Q.js").then((m) => m.runServe(args.slice(1))).catch(fatal);
60
+ import("./cli-serve-GTTDOLB7.js").then((m) => m.runServe(args.slice(1))).catch(fatal);
61
61
  break;
62
62
  case "agent":
63
- import("./cli-agent-BCISHZTV.js").then((m) => m.runAgent(args.slice(1))).catch(fatal);
63
+ import("./cli-agent-WSCDFYMU.js").then((m) => m.runAgent(args.slice(1))).catch(fatal);
64
64
  break;
65
65
  case "setup":
66
66
  default:
67
- import("./setup-2VN7D4OT.js").then((m) => m.runSetupWizard()).catch(fatal);
67
+ import("./setup-HK4WDXUL.js").then((m) => m.runSetupWizard()).catch(fatal);
68
68
  break;
69
69
  }
70
70
  function fatal(err) {
@@ -295,7 +295,8 @@ function InlinePermissionPicker({ permissions, pageRegistry, onChange }) {
295
295
  // ─── Users Page ────────────────────────────────────
296
296
 
297
297
  export function UsersPage() {
298
- var { toast } = useApp();
298
+ var app = useApp();
299
+ var toast = app.toast;
299
300
  var [users, setUsers] = useState([]);
300
301
  var [creating, setCreating] = useState(false);
301
302
  var [form, setForm] = useState({ email: '', password: '', name: '', role: 'viewer', permissions: '*' });
@@ -343,22 +344,39 @@ export function UsersPage() {
343
344
  setResetting(false);
344
345
  };
345
346
 
346
- var deleteUser = async function(user) {
347
+ var toggleActive = async function(user) {
348
+ var action = user.isActive === false ? 'reactivate' : 'deactivate';
347
349
  var ok = await showConfirm({
348
- title: 'Delete User',
349
- message: 'Are you sure you want to delete "' + (user.name || user.email) + '"? This cannot be undone.',
350
- warning: 'The user will lose all access immediately.',
351
- danger: true,
352
- confirmText: 'Delete User'
350
+ title: action === 'deactivate' ? 'Deactivate User' : 'Reactivate User',
351
+ message: action === 'deactivate'
352
+ ? 'Deactivate "' + (user.name || user.email) + '"? They will be unable to log in and will see a message to contact their organization.'
353
+ : 'Reactivate "' + (user.name || user.email) + '"? They will be able to log in again.',
354
+ danger: action === 'deactivate',
355
+ confirmText: action === 'deactivate' ? 'Deactivate' : 'Reactivate'
353
356
  });
354
357
  if (!ok) return;
355
358
  try {
356
- await apiCall('/users/' + user.id, { method: 'DELETE' });
357
- toast('User deleted', 'success');
359
+ await apiCall('/users/' + user.id + '/' + action, { method: 'POST' });
360
+ toast('User ' + action + 'd', 'success');
358
361
  load();
359
362
  } catch (e) { toast(e.message, 'error'); }
360
363
  };
361
364
 
365
+ var [deleteStep, setDeleteStep] = useState(0);
366
+ var [deleteTarget, setDeleteTarget] = useState(null);
367
+ var [deleteTyped, setDeleteTyped] = useState('');
368
+
369
+ var startDelete = function(user) { setDeleteTarget(user); setDeleteStep(1); setDeleteTyped(''); };
370
+ var cancelDelete = function() { setDeleteTarget(null); setDeleteStep(0); setDeleteTyped(''); };
371
+
372
+ var confirmDelete = async function() {
373
+ try {
374
+ await apiCall('/users/' + deleteTarget.id, { method: 'DELETE', body: JSON.stringify({ confirmationToken: 'DELETE_USER_' + deleteTarget.email }) });
375
+ toast('User permanently deleted', 'success');
376
+ cancelDelete(); load();
377
+ } catch (e) { toast(e.message, 'error'); }
378
+ };
379
+
362
380
  var openPermissions = async function(user) {
363
381
  try {
364
382
  var d = await apiCall('/users/' + user.id + '/permissions');
@@ -485,18 +503,100 @@ export function UsersPage() {
485
503
  onClose: function() { setPermTarget(null); }
486
504
  }),
487
505
 
506
+ // 5-step delete confirmation modal
507
+ deleteTarget && h(Modal, {
508
+ title: 'Delete User — Step ' + deleteStep + ' of 5',
509
+ onClose: cancelDelete,
510
+ width: 480,
511
+ footer: h(Fragment, null,
512
+ h('button', { className: 'btn btn-secondary', onClick: deleteStep === 1 ? cancelDelete : function() { setDeleteStep(deleteStep - 1); } }, deleteStep === 1 ? 'Cancel' : 'Back'),
513
+ deleteStep < 5
514
+ ? h('button', { className: 'btn btn-' + (deleteStep >= 3 ? 'danger' : 'primary'), onClick: function() { setDeleteStep(deleteStep + 1); } }, 'Continue')
515
+ : h('button', { className: 'btn btn-danger', onClick: confirmDelete, disabled: deleteTyped !== deleteTarget.email }, 'Permanently Delete')
516
+ )
517
+ },
518
+ // Step 1: Warning
519
+ deleteStep === 1 && h('div', null,
520
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 12, padding: 16, background: 'var(--danger-soft, rgba(220,38,38,0.08))', borderRadius: 8, marginBottom: 16 } },
521
+ h('svg', { width: 24, height: 24, viewBox: '0 0 24 24', fill: 'none', stroke: 'var(--danger)', strokeWidth: 2 }, h('path', { d: 'M12 9v4m0 4h.01M10.29 3.86l-8.6 14.86A2 2 0 0 0 3.4 21h17.2a2 2 0 0 0 1.71-2.98L13.71 3.86a2 2 0 0 0-3.42 0z' })),
522
+ h('div', null,
523
+ h('strong', null, 'Permanent Deletion'),
524
+ h('div', { style: { fontSize: 12, color: 'var(--text-muted)', marginTop: 2 } }, 'This action cannot be undone.')
525
+ )
526
+ ),
527
+ h('p', { style: { fontSize: 13 } }, 'You are about to permanently delete the user account for:'),
528
+ h('div', { style: { padding: 12, background: 'var(--bg-tertiary)', borderRadius: 8, marginTop: 8 } },
529
+ h('strong', null, deleteTarget.name || 'Unnamed'), h('br'),
530
+ h('span', { style: { fontSize: 12, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' } }, deleteTarget.email)
531
+ ),
532
+ h('p', { style: { fontSize: 12, color: 'var(--text-muted)', marginTop: 12 } }, 'Consider deactivating instead — deactivated users can be reactivated later.')
533
+ ),
534
+ // Step 2: Data loss
535
+ deleteStep === 2 && h('div', null,
536
+ h('h4', { style: { marginBottom: 12 } }, 'Data That Will Be Lost'),
537
+ h('ul', { style: { paddingLeft: 20, fontSize: 13, lineHeight: 1.8 } },
538
+ h('li', null, 'All login sessions will be terminated immediately'),
539
+ h('li', null, 'Audit log entries will be orphaned (no user reference)'),
540
+ h('li', null, 'Any API keys created by this user will be revoked'),
541
+ h('li', null, 'Permission grants and role assignments will be removed'),
542
+ h('li', null, '2FA configuration and backup codes will be destroyed')
543
+ )
544
+ ),
545
+ // Step 3: Impact
546
+ deleteStep === 3 && h('div', null,
547
+ h('h4', { style: { marginBottom: 12 } }, 'Impact Assessment'),
548
+ h('div', { style: { padding: 12, background: 'var(--warning-soft, rgba(245,158,11,0.08))', borderRadius: 8, fontSize: 13, lineHeight: 1.6 } },
549
+ h('p', null, 'If this user manages or supervises any agents, those agents will lose their manager assignment.'),
550
+ h('p', { style: { marginTop: 8 } }, 'If this user created approval workflows, pending approvals may become orphaned.'),
551
+ h('p', { style: { marginTop: 8 } }, 'Any scheduled tasks or cron jobs created by this user will continue to run but cannot be modified.')
552
+ )
553
+ ),
554
+ // Step 4: Alternative
555
+ deleteStep === 4 && h('div', null,
556
+ h('h4', { style: { marginBottom: 12 } }, 'Are You Sure?'),
557
+ h('div', { style: { padding: 16, background: 'var(--success-soft, rgba(21,128,61,0.08))', borderRadius: 8, marginBottom: 16 } },
558
+ h('strong', null, 'Recommended alternative: Deactivate'),
559
+ h('p', { style: { fontSize: 12, color: 'var(--text-secondary)', marginTop: 4 } }, 'Deactivating blocks login while preserving all data. The user can be reactivated at any time. This is the safe option.')
560
+ ),
561
+ h('div', { style: { padding: 16, background: 'var(--danger-soft, rgba(220,38,38,0.08))', borderRadius: 8 } },
562
+ h('strong', null, 'Permanent deletion'),
563
+ h('p', { style: { fontSize: 12, color: 'var(--text-secondary)', marginTop: 4 } }, 'Removes the user and all associated data forever. There is no recovery.')
564
+ )
565
+ ),
566
+ // Step 5: Type email to confirm
567
+ deleteStep === 5 && h('div', null,
568
+ h('h4', { style: { marginBottom: 12, color: 'var(--danger)' } }, 'Final Confirmation'),
569
+ h('p', { style: { fontSize: 13, marginBottom: 12 } }, 'Type the user\'s email address to confirm permanent deletion:'),
570
+ h('div', { style: { padding: 8, background: 'var(--bg-tertiary)', borderRadius: 6, fontFamily: 'var(--font-mono)', fontSize: 13, textAlign: 'center', marginBottom: 12 } }, deleteTarget.email),
571
+ h('input', {
572
+ className: 'input', type: 'text', value: deleteTyped,
573
+ onChange: function(e) { setDeleteTyped(e.target.value); },
574
+ placeholder: 'Type email to confirm',
575
+ autoFocus: true,
576
+ style: { fontFamily: 'var(--font-mono)', fontSize: 13, borderColor: deleteTyped === deleteTarget.email ? 'var(--danger)' : 'var(--border)' }
577
+ }),
578
+ deleteTyped && deleteTyped !== deleteTarget.email && h('div', { style: { fontSize: 11, color: 'var(--danger)', marginTop: 4 } }, 'Email does not match')
579
+ )
580
+ ),
581
+
488
582
  // Users table
489
583
  h('div', { className: 'card' },
490
584
  h('div', { className: 'card-body-flush' },
491
585
  users.length === 0 ? h('div', { style: { padding: 24, textAlign: 'center', color: 'var(--text-muted)' } }, 'No users')
492
586
  : h('table', null,
493
- h('thead', null, h('tr', null, h('th', null, 'Name'), h('th', null, 'Email'), h('th', null, 'Role'), h('th', null, 'Access'), h('th', null, '2FA'), h('th', null, 'Created'), h('th', { style: { width: 180 } }, 'Actions'))),
587
+ h('thead', null, h('tr', null, h('th', null, 'Name'), h('th', null, 'Email'), h('th', null, 'Role'), h('th', null, 'Status'), h('th', null, 'Access'), h('th', null, '2FA'), h('th', null, 'Created'), h('th', { style: { width: 200 } }, 'Actions'))),
494
588
  h('tbody', null, users.map(function(u) {
495
589
  var isRestricted = u.role === 'member' || u.role === 'viewer';
496
- return h('tr', { key: u.id },
590
+ var isDeactivated = u.isActive === false;
591
+ var isSelf = u.id === ((app || {}).user || {}).id;
592
+ return h('tr', { key: u.id, style: isDeactivated ? { opacity: 0.6 } : {} },
497
593
  h('td', null, h('strong', null, u.name || '-')),
498
594
  h('td', null, h('span', { style: { fontFamily: 'var(--font-mono)', fontSize: 12 } }, u.email)),
499
595
  h('td', null, h('span', { className: 'badge badge-' + (u.role === 'owner' ? 'warning' : u.role === 'admin' ? 'primary' : 'neutral') }, u.role)),
596
+ h('td', null, isDeactivated
597
+ ? h('span', { className: 'badge badge-danger', style: { fontSize: 10 } }, 'Deactivated')
598
+ : h('span', { className: 'badge badge-success', style: { fontSize: 10 } }, 'Active')
599
+ ),
500
600
  h('td', null, permBadge(u)),
501
601
  h('td', null, u.totpEnabled ? h('span', { className: 'badge badge-success' }, 'On') : h('span', { className: 'badge badge-neutral' }, 'Off')),
502
602
  h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, u.createdAt ? new Date(u.createdAt).toLocaleDateString() : '-'),
@@ -509,7 +609,15 @@ export function UsersPage() {
509
609
  style: !isRestricted ? { opacity: 0.4 } : {}
510
610
  }, I.shield()),
511
611
  h('button', { className: 'btn btn-ghost btn-sm', title: 'Reset Password', onClick: function() { setResetTarget(u); setNewPassword(''); } }, I.lock()),
512
- h('button', { className: 'btn btn-ghost btn-sm', title: 'Delete User', onClick: function() { deleteUser(u); }, style: { color: 'var(--danger)' } }, I.trash())
612
+ // Deactivate / Reactivate
613
+ !isSelf && h('button', {
614
+ className: 'btn btn-ghost btn-sm',
615
+ title: isDeactivated ? 'Reactivate User' : 'Deactivate User',
616
+ onClick: function() { toggleActive(u); },
617
+ style: { color: isDeactivated ? 'var(--success, #15803d)' : 'var(--warning, #f59e0b)' }
618
+ }, isDeactivated ? I.check() : I.pause()),
619
+ // Delete (owner only)
620
+ !isSelf && h('button', { className: 'btn btn-ghost btn-sm', title: 'Delete User Permanently', onClick: function() { startDelete(u); }, style: { color: 'var(--danger)' } }, I.trash())
513
621
  )
514
622
  )
515
623
  );
@@ -0,0 +1,9 @@
1
+ import {
2
+ createAdapter,
3
+ getSupportedDatabases
4
+ } from "./chunk-Y3WLLVOK.js";
5
+ import "./chunk-KFQGP6VL.js";
6
+ export {
7
+ createAdapter,
8
+ getSupportedDatabases
9
+ };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  provision,
3
3
  runSetupWizard
4
- } from "./chunk-3YLLWCUC.js";
4
+ } from "./chunk-A5LURBEY.js";
5
5
  import {
6
6
  AgenticMailManager,
7
7
  GoogleEmailProvider,
@@ -42,7 +42,7 @@ import {
42
42
  requireRole,
43
43
  securityHeaders,
44
44
  validate
45
- } from "./chunk-HGSWCMB7.js";
45
+ } from "./chunk-WQRGOMQJ.js";
46
46
  import "./chunk-OF4MUWWS.js";
47
47
  import {
48
48
  PROVIDER_REGISTRY,
@@ -113,7 +113,7 @@ import {
113
113
  import {
114
114
  createAdapter,
115
115
  getSupportedDatabases
116
- } from "./chunk-KWW53O2B.js";
116
+ } from "./chunk-Y3WLLVOK.js";
117
117
  import {
118
118
  AGENTICMAIL_TOOLS,
119
119
  ALL_TOOLS,