@agenticmail/enterprise 0.5.300 → 0.5.301

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-XEBV2VGZ.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-XEBV2VGZ.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-MFF5AXLN.js").then((m) => m.runRecover(args.slice(1))).catch(fatal);
17
+ import("./cli-recover-WS27YEB7.js").then((m) => m.runRecover(args.slice(1))).catch(fatal);
18
18
  break;
19
19
  case "verify-domain":
20
- import("./cli-verify-BTSN7SQ2.js").then((m) => m.runVerifyDomain(args.slice(1))).catch(fatal);
20
+ import("./cli-verify-CVYMUGKX.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-BXI7TTNN.js").then((m) => m.runServe(args.slice(1))).catch(fatal);
60
+ import("./cli-serve-HVULKNQH.js").then((m) => m.runServe(args.slice(1))).catch(fatal);
61
61
  break;
62
62
  case "agent":
63
- import("./cli-agent-4DVE3NDA.js").then((m) => m.runAgent(args.slice(1))).catch(fatal);
63
+ import("./cli-agent-4GZGC6XO.js").then((m) => m.runAgent(args.slice(1))).catch(fatal);
64
64
  break;
65
65
  case "setup":
66
66
  default:
67
- import("./setup-KIAPXKEX.js").then((m) => m.runSetupWizard()).catch(fatal);
67
+ import("./setup-JUB67BUU.js").then((m) => m.runSetupWizard()).catch(fatal);
68
68
  break;
69
69
  }
70
70
  function fatal(err) {
@@ -41,8 +41,18 @@ export function OrganizationsPage() {
41
41
  var femail = _femail[0]; var setFemail = _femail[1];
42
42
  var _fdesc = useState('');
43
43
  var fdesc = _fdesc[0]; var setFdesc = _fdesc[1];
44
+ var _fbilling = useState('');
45
+ var fbilling = _fbilling[0]; var setFbilling = _fbilling[1];
46
+ var _fcurrency = useState('USD');
47
+ var fcurrency = _fcurrency[0]; var setFcurrency = _fcurrency[1];
44
48
  var _slugManual = useState(false);
45
49
  var slugManual = _slugManual[0]; var setSlugManual = _slugManual[1];
50
+ var _detailTab = useState('agents');
51
+ var detailTab = _detailTab[0]; var setDetailTab = _detailTab[1];
52
+ var _billingSummary = useState([]);
53
+ var billingSummary = _billingSummary[0]; var setBillingSummary = _billingSummary[1];
54
+ var _billingRecords = useState([]);
55
+ var billingRecords = _billingRecords[0]; var setBillingRecords = _billingRecords[1];
46
56
 
47
57
  var loadOrgs = useCallback(function() {
48
58
  setLoading(true);
@@ -62,29 +72,33 @@ export function OrganizationsPage() {
62
72
  };
63
73
 
64
74
  var openCreate = function() {
65
- setFname(''); setFslug(''); setFcontact(''); setFemail(''); setFdesc(''); setSlugManual(false);
75
+ setFname(''); setFslug(''); setFcontact(''); setFemail(''); setFdesc(''); setFbilling(''); setFcurrency('USD'); setSlugManual(false);
66
76
  setShowCreate(true);
67
77
  };
68
78
 
69
79
  var openEdit = function(org) {
70
80
  setFname(org.name || ''); setFslug(org.slug || ''); setFcontact(org.contact_name || ''); setFemail(org.contact_email || ''); setFdesc(org.description || '');
81
+ setFbilling(org.billing_rate_per_agent ? String(org.billing_rate_per_agent) : ''); setFcurrency(org.currency || 'USD');
71
82
  setEditOrg(org);
72
83
  };
73
84
 
74
85
  var openDetail = function(org) {
75
86
  setDetailOrg(org);
87
+ setDetailTab('agents');
76
88
  loadAllAgents();
77
89
  apiCall('/organizations/' + org.id).then(function(data) {
78
90
  setDetailAgents(data.agents || []);
79
91
  setDetailOrg(data);
80
92
  }).catch(function(err) { toast(err.message, 'error'); });
93
+ apiCall('/organizations/' + org.id + '/billing-summary').then(function(d) { setBillingSummary(d.summary || []); }).catch(function() {});
94
+ apiCall('/organizations/' + org.id + '/billing').then(function(d) { setBillingRecords(d.records || []); }).catch(function() {});
81
95
  };
82
96
 
83
97
  var doCreate = function() {
84
98
  setActing('create');
85
99
  apiCall('/organizations', {
86
100
  method: 'POST',
87
- body: JSON.stringify({ name: fname, slug: fslug, contact_name: fcontact, contact_email: femail, description: fdesc })
101
+ body: JSON.stringify({ name: fname, slug: fslug, contact_name: fcontact, contact_email: femail, description: fdesc, billing_rate_per_agent: fbilling ? parseFloat(fbilling) : 0, currency: fcurrency })
88
102
  }).then(function() {
89
103
  toast('Organization created', 'success');
90
104
  setShowCreate(false);
@@ -97,7 +111,7 @@ export function OrganizationsPage() {
97
111
  setActing('edit');
98
112
  apiCall('/organizations/' + editOrg.id, {
99
113
  method: 'PATCH',
100
- body: JSON.stringify({ name: fname, contact_name: fcontact, contact_email: femail, description: fdesc })
114
+ body: JSON.stringify({ name: fname, contact_name: fcontact, contact_email: femail, description: fdesc, billing_rate_per_agent: fbilling ? parseFloat(fbilling) : 0, currency: fcurrency })
101
115
  }).then(function() {
102
116
  toast('Organization updated', 'success');
103
117
  setEditOrg(null);
@@ -209,8 +223,8 @@ export function OrganizationsPage() {
209
223
  org.description && h('div', { style: { fontSize: 13, color: 'var(--text-secondary)', marginBottom: 12, lineHeight: 1.5 } }, org.description),
210
224
  h('div', { style: { display: 'flex', gap: 16, fontSize: 12, color: 'var(--text-muted)' } },
211
225
  h('span', null, I.agents(), ' ', (org.agent_count || 0), ' agent', (org.agent_count || 0) !== 1 ? 's' : ''),
212
- org.contact_email && h('span', null, I.mail(), ' ', org.contact_email),
213
- org.created_at && h('span', null, new Date(org.created_at).toLocaleDateString())
226
+ org.billing_rate_per_agent > 0 && h('span', { style: { fontWeight: 600, color: 'var(--success, #15803d)' } }, (org.currency || '$') + ' ', parseFloat(org.billing_rate_per_agent).toFixed(2), '/agent/mo'),
227
+ org.contact_email && h('span', null, I.mail(), ' ', org.contact_email)
214
228
  ),
215
229
  h('div', { style: { display: 'flex', gap: 6, marginTop: 12, borderTop: '1px solid var(--border)', paddingTop: 10 }, onClick: function(e) { e.stopPropagation(); } },
216
230
  h('button', { className: 'btn btn-ghost btn-sm', onClick: function() { openEdit(org); } }, I.edit(), ' Edit'),
@@ -229,24 +243,36 @@ export function OrganizationsPage() {
229
243
  h('div', { style: { display: 'flex', flexDirection: 'column', gap: 14, padding: 4 } },
230
244
  h('div', null,
231
245
  h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Name *'),
232
- h('input', { className: 'input', value: fname, onInput: function(e) { setFname(e.target.value); if (!slugManual) setFslug(slugify(e.target.value)); }, placeholder: 'Acme Corporation' })
246
+ h('input', { className: 'input', value: fname, onInput: function(e) { setFname(e.target.value); if (!slugManual) setFslug(slugify(e.target.value)); }, placeholder: 'AgenticMail' })
233
247
  ),
234
248
  h('div', null,
235
249
  h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Slug *'),
236
- h('input', { className: 'input', value: fslug, onInput: function(e) { setFslug(e.target.value); setSlugManual(true); }, placeholder: 'acme-corporation', style: { fontFamily: 'var(--font-mono, monospace)' } })
250
+ h('input', { className: 'input', value: fslug, onInput: function(e) { setFslug(e.target.value); setSlugManual(true); }, placeholder: 'agenticmail', style: { fontFamily: 'var(--font-mono, monospace)' } })
237
251
  ),
238
252
  h('div', null,
239
253
  h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Contact Name'),
240
- h('input', { className: 'input', value: fcontact, onInput: function(e) { setFcontact(e.target.value); }, placeholder: 'John Doe' })
254
+ h('input', { className: 'input', value: fcontact, onInput: function(e) { setFcontact(e.target.value); }, placeholder: 'Ope Olatunji' })
241
255
  ),
242
256
  h('div', null,
243
257
  h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Contact Email'),
244
- h('input', { className: 'input', type: 'email', value: femail, onInput: function(e) { setFemail(e.target.value); }, placeholder: 'john@acme.com' })
258
+ h('input', { className: 'input', type: 'email', value: femail, onInput: function(e) { setFemail(e.target.value); }, placeholder: 'ope@agenticmail.io' })
245
259
  ),
246
260
  h('div', null,
247
261
  h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Description'),
248
262
  h('textarea', { className: 'input', value: fdesc, onInput: function(e) { setFdesc(e.target.value); }, placeholder: 'Brief description...', rows: 3, style: { resize: 'vertical' } })
249
263
  ),
264
+ h('div', { style: { display: 'flex', gap: 12 } },
265
+ h('div', { style: { flex: 1 } },
266
+ h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Billing Rate / Agent / Month'),
267
+ h('input', { className: 'input', type: 'number', step: '0.01', min: '0', value: fbilling, onInput: function(e) { setFbilling(e.target.value); }, placeholder: '0.00' })
268
+ ),
269
+ h('div', { style: { width: 100 } },
270
+ h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Currency'),
271
+ h('select', { className: 'input', value: fcurrency, onChange: function(e) { setFcurrency(e.target.value); } },
272
+ h('option', { value: 'USD' }, 'USD'), h('option', { value: 'EUR' }, 'EUR'), h('option', { value: 'GBP' }, 'GBP'), h('option', { value: 'NGN' }, 'NGN'), h('option', { value: 'CAD' }, 'CAD'), h('option', { value: 'AUD' }, 'AUD')
273
+ )
274
+ )
275
+ ),
250
276
  h('div', { style: { display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 8 } },
251
277
  h('button', { className: 'btn btn-secondary', onClick: function() { setShowCreate(false); } }, 'Cancel'),
252
278
  h('button', { className: 'btn btn-primary', disabled: !fname || !fslug || acting === 'create', onClick: doCreate }, acting === 'create' ? 'Creating...' : 'Create')
@@ -277,6 +303,18 @@ export function OrganizationsPage() {
277
303
  h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Description'),
278
304
  h('textarea', { className: 'input', value: fdesc, onInput: function(e) { setFdesc(e.target.value); }, rows: 3, style: { resize: 'vertical' } })
279
305
  ),
306
+ h('div', { style: { display: 'flex', gap: 12 } },
307
+ h('div', { style: { flex: 1 } },
308
+ h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Billing Rate / Agent / Month'),
309
+ h('input', { className: 'input', type: 'number', step: '0.01', min: '0', value: fbilling, onInput: function(e) { setFbilling(e.target.value); }, placeholder: '0.00' })
310
+ ),
311
+ h('div', { style: { width: 100 } },
312
+ h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Currency'),
313
+ h('select', { className: 'input', value: fcurrency, onChange: function(e) { setFcurrency(e.target.value); } },
314
+ h('option', { value: 'USD' }, 'USD'), h('option', { value: 'EUR' }, 'EUR'), h('option', { value: 'GBP' }, 'GBP'), h('option', { value: 'NGN' }, 'NGN'), h('option', { value: 'CAD' }, 'CAD'), h('option', { value: 'AUD' }, 'AUD')
315
+ )
316
+ )
317
+ ),
280
318
  h('div', { style: { display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 8 } },
281
319
  h('button', { className: 'btn btn-secondary', onClick: function() { setEditOrg(null); } }, 'Cancel'),
282
320
  h('button', { className: 'btn btn-primary', disabled: !fname || acting === 'edit', onClick: doEdit }, acting === 'edit' ? 'Saving...' : 'Save Changes')
@@ -285,10 +323,10 @@ export function OrganizationsPage() {
285
323
  ),
286
324
 
287
325
  // Detail Modal
288
- detailOrg && h(Modal, { title: detailOrg.name || 'Organization Detail', onClose: function() { setDetailOrg(null); }, wide: true },
326
+ detailOrg && h(Modal, { title: detailOrg.name || 'Organization Detail', onClose: function() { setDetailOrg(null); }, width: 700 },
289
327
  h('div', { style: { padding: 4 } },
290
328
  // Org info
291
- h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, marginBottom: 20 } },
329
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16, marginBottom: 16 } },
292
330
  h('div', null,
293
331
  h('div', { style: { fontSize: 11, color: 'var(--text-muted)', textTransform: 'uppercase', fontWeight: 600, marginBottom: 4 } }, 'Slug'),
294
332
  h('div', { style: { fontFamily: 'var(--font-mono, monospace)', fontSize: 13 } }, detailOrg.slug)
@@ -303,10 +341,29 @@ export function OrganizationsPage() {
303
341
  detailOrg.contact_email && h('div', { style: { fontSize: 12, color: 'var(--text-muted)' } }, detailOrg.contact_email)
304
342
  )
305
343
  ),
344
+ // Billing rate in header
345
+ detailOrg.billing_rate_per_agent > 0 && h('div', { style: { display: 'flex', alignItems: 'center', gap: 16, padding: '10px 14px', background: 'var(--success-soft, rgba(21,128,61,0.06))', borderRadius: 8, marginBottom: 16, fontSize: 13 } },
346
+ h('div', null, h('strong', null, 'Rate: '), (detailOrg.currency || 'USD') + ' ' + parseFloat(detailOrg.billing_rate_per_agent).toFixed(2) + '/agent/month'),
347
+ h('div', null, h('strong', null, 'Monthly Revenue: '), (detailOrg.currency || 'USD') + ' ' + (parseFloat(detailOrg.billing_rate_per_agent) * detailAgents.length).toFixed(2)),
348
+ h('div', null, h('strong', null, 'Agents: '), detailAgents.length)
349
+ ),
350
+
306
351
  detailOrg.description && h('div', { style: { fontSize: 13, color: 'var(--text-secondary)', marginBottom: 16, padding: 12, background: 'var(--bg-tertiary)', borderRadius: 'var(--radius)' } }, detailOrg.description),
307
352
 
308
- // Linked agents
309
- h('div', { style: { fontSize: 14, fontWeight: 700, marginBottom: 10 } }, 'Linked Agents (' + detailAgents.length + ')'),
353
+ // Tabs
354
+ h('div', { style: { display: 'flex', gap: 0, borderBottom: '1px solid var(--border)', marginBottom: 16 } },
355
+ ['agents', 'billing'].map(function(t) {
356
+ return h('button', {
357
+ key: t, type: 'button',
358
+ style: { padding: '8px 16px', fontSize: 13, fontWeight: 600, background: 'none', border: 'none', cursor: 'pointer', color: detailTab === t ? 'var(--primary)' : 'var(--text-muted)', borderBottom: detailTab === t ? '2px solid var(--primary)' : '2px solid transparent', fontFamily: 'var(--font)' },
359
+ onClick: function() { setDetailTab(t); }
360
+ }, t === 'agents' ? 'Agents (' + detailAgents.length + ')' : 'Billing & Costs');
361
+ })
362
+ ),
363
+
364
+ // ── Agents Tab ────────────────────────────
365
+ detailTab === 'agents' && h(Fragment, null,
366
+ h('div', { style: { fontSize: 14, fontWeight: 700, marginBottom: 10 } }, 'Linked Agents'),
310
367
  detailAgents.length > 0
311
368
  ? h('div', { style: { display: 'flex', flexDirection: 'column', gap: 6, marginBottom: 16 } },
312
369
  detailAgents.map(function(a) {
@@ -339,6 +396,102 @@ export function OrganizationsPage() {
339
396
  ),
340
397
  h('button', { className: 'btn btn-primary btn-sm', disabled: !assignAgentId || acting === 'assign', onClick: doAssignAgent }, acting === 'assign' ? 'Assigning...' : 'Assign')
341
398
  )
399
+ ), // end agents tab
400
+
401
+ // ── Billing Tab ───────────────────────────
402
+ detailTab === 'billing' && h(Fragment, null,
403
+ // Revenue vs Cost chart
404
+ billingSummary.length > 0 && h('div', { style: { marginBottom: 20 } },
405
+ h('div', { style: { fontSize: 14, fontWeight: 700, marginBottom: 12 } }, 'Revenue vs Cost'),
406
+ h('div', { style: { display: 'flex', alignItems: 'flex-end', gap: 4, height: 160, padding: '0 8px', borderBottom: '1px solid var(--border)' } },
407
+ billingSummary.map(function(m, i) {
408
+ var rev = parseFloat(m.total_revenue) || 0;
409
+ var cost = parseFloat(m.total_cost) || 0;
410
+ var maxVal = Math.max.apply(null, billingSummary.map(function(s) { return Math.max(parseFloat(s.total_revenue) || 0, parseFloat(s.total_cost) || 0); })) || 1;
411
+ var revH = Math.max(4, (rev / maxVal) * 140);
412
+ var costH = Math.max(4, (cost / maxVal) * 140);
413
+ var profit = rev - cost;
414
+ return h('div', { key: i, style: { flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 } },
415
+ h('div', { style: { display: 'flex', alignItems: 'flex-end', gap: 2, height: 144 } },
416
+ h('div', { title: 'Revenue: ' + rev.toFixed(2), style: { width: 14, height: revH, background: 'var(--success, #15803d)', borderRadius: '3px 3px 0 0', minHeight: 4 } }),
417
+ h('div', { title: 'Cost: ' + cost.toFixed(2), style: { width: 14, height: costH, background: 'var(--danger, #dc2626)', borderRadius: '3px 3px 0 0', minHeight: 4, opacity: 0.7 } })
418
+ ),
419
+ h('div', { style: { fontSize: 9, color: 'var(--text-muted)', marginTop: 4, whiteSpace: 'nowrap' } }, m.month ? m.month.slice(5) : ''),
420
+ h('div', { style: { fontSize: 9, color: profit >= 0 ? 'var(--success, #15803d)' : 'var(--danger)', fontWeight: 600 } }, profit >= 0 ? '+' + profit.toFixed(0) : profit.toFixed(0))
421
+ );
422
+ })
423
+ ),
424
+ h('div', { style: { display: 'flex', gap: 16, marginTop: 8, fontSize: 11 } },
425
+ h('span', { style: { display: 'flex', alignItems: 'center', gap: 4 } }, h('span', { style: { width: 10, height: 10, borderRadius: 2, background: 'var(--success, #15803d)', display: 'inline-block' } }), 'Revenue'),
426
+ h('span', { style: { display: 'flex', alignItems: 'center', gap: 4 } }, h('span', { style: { width: 10, height: 10, borderRadius: 2, background: 'var(--danger)', opacity: 0.7, display: 'inline-block' } }), 'Token Cost'),
427
+ (function() {
428
+ var totRev = billingSummary.reduce(function(a, m) { return a + (parseFloat(m.total_revenue) || 0); }, 0);
429
+ var totCost = billingSummary.reduce(function(a, m) { return a + (parseFloat(m.total_cost) || 0); }, 0);
430
+ return h('span', { style: { marginLeft: 'auto', fontWeight: 600, color: (totRev - totCost) >= 0 ? 'var(--success, #15803d)' : 'var(--danger)' } },
431
+ 'Net: ' + (detailOrg.currency || 'USD') + ' ' + (totRev - totCost).toFixed(2)
432
+ );
433
+ })()
434
+ )
435
+ ),
436
+
437
+ billingSummary.length === 0 && h('div', { style: { padding: 24, textAlign: 'center', color: 'var(--text-muted)', fontSize: 13, background: 'var(--bg-tertiary)', borderRadius: 8, marginBottom: 16 } },
438
+ 'No billing data yet. Billing records are created as agents process tasks and accumulate token costs.'
439
+ ),
440
+
441
+ // Stats summary
442
+ (function() {
443
+ var totRev = billingSummary.reduce(function(a, m) { return a + (parseFloat(m.total_revenue) || 0); }, 0);
444
+ var totCost = billingSummary.reduce(function(a, m) { return a + (parseFloat(m.total_cost) || 0); }, 0);
445
+ var totIn = billingSummary.reduce(function(a, m) { return a + (parseInt(m.total_input_tokens) || 0); }, 0);
446
+ var totOut = billingSummary.reduce(function(a, m) { return a + (parseInt(m.total_output_tokens) || 0); }, 0);
447
+ var margin = totRev > 0 ? ((totRev - totCost) / totRev * 100) : 0;
448
+ return h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12, marginBottom: 16 } },
449
+ h('div', { style: { padding: 12, background: 'var(--bg-tertiary)', borderRadius: 8, textAlign: 'center' } },
450
+ h('div', { style: { fontSize: 18, fontWeight: 700, color: 'var(--success, #15803d)' } }, (detailOrg.currency || 'USD') + ' ' + totRev.toFixed(2)),
451
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)' } }, 'Total Revenue')
452
+ ),
453
+ h('div', { style: { padding: 12, background: 'var(--bg-tertiary)', borderRadius: 8, textAlign: 'center' } },
454
+ h('div', { style: { fontSize: 18, fontWeight: 700, color: 'var(--danger)' } }, (detailOrg.currency || 'USD') + ' ' + totCost.toFixed(4)),
455
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)' } }, 'Token Cost')
456
+ ),
457
+ h('div', { style: { padding: 12, background: 'var(--bg-tertiary)', borderRadius: 8, textAlign: 'center' } },
458
+ h('div', { style: { fontSize: 18, fontWeight: 700, color: margin >= 0 ? 'var(--success, #15803d)' : 'var(--danger)' } }, margin.toFixed(1) + '%'),
459
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)' } }, 'Margin')
460
+ ),
461
+ h('div', { style: { padding: 12, background: 'var(--bg-tertiary)', borderRadius: 8, textAlign: 'center' } },
462
+ h('div', { style: { fontSize: 18, fontWeight: 700 } }, ((totIn + totOut) / 1000).toFixed(1) + 'K'),
463
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)' } }, 'Total Tokens')
464
+ )
465
+ );
466
+ })(),
467
+
468
+ // Per-agent breakdown table
469
+ billingRecords.length > 0 && h(Fragment, null,
470
+ h('div', { style: { fontSize: 14, fontWeight: 700, marginBottom: 8 } }, 'Records'),
471
+ h('div', { style: { overflowX: 'auto' } },
472
+ h('table', null,
473
+ h('thead', null, h('tr', null,
474
+ h('th', null, 'Month'), h('th', null, 'Agent'), h('th', { style: { textAlign: 'right' } }, 'Revenue'), h('th', { style: { textAlign: 'right' } }, 'Token Cost'), h('th', { style: { textAlign: 'right' } }, 'Profit'), h('th', { style: { textAlign: 'right' } }, 'Tokens')
475
+ )),
476
+ h('tbody', null,
477
+ billingRecords.map(function(r, i) {
478
+ var rev = parseFloat(r.revenue) || 0;
479
+ var cost = parseFloat(r.token_cost) || 0;
480
+ var agent = detailAgents.find(function(a) { return a.id === r.agent_id; });
481
+ return h('tr', { key: i },
482
+ h('td', { style: { fontFamily: 'var(--font-mono)', fontSize: 12 } }, r.month),
483
+ h('td', null, agent ? agent.name : (r.agent_id ? r.agent_id.slice(0, 8) : 'All')),
484
+ h('td', { style: { textAlign: 'right', color: 'var(--success, #15803d)' } }, rev.toFixed(2)),
485
+ h('td', { style: { textAlign: 'right', color: 'var(--danger)' } }, cost.toFixed(4)),
486
+ h('td', { style: { textAlign: 'right', fontWeight: 600, color: (rev - cost) >= 0 ? 'var(--success, #15803d)' : 'var(--danger)' } }, (rev - cost).toFixed(2)),
487
+ h('td', { style: { textAlign: 'right', fontSize: 12, color: 'var(--text-muted)' } }, ((parseInt(r.input_tokens) || 0) + (parseInt(r.output_tokens) || 0)).toLocaleString())
488
+ );
489
+ })
490
+ )
491
+ )
492
+ )
493
+ )
494
+ ) // end billing tab
342
495
  )
343
496
  )
344
497
  );
@@ -0,0 +1,9 @@
1
+ import {
2
+ createAdapter,
3
+ getSupportedDatabases
4
+ } from "./chunk-VLVRDLYO.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-633X6XDY.js";
4
+ } from "./chunk-QKQWDX6M.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-7FEEWUNJ.js";
45
+ } from "./chunk-VMVOJFCX.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-55QVWFKC.js";
116
+ } from "./chunk-VLVRDLYO.js";
117
117
  import {
118
118
  AGENTICMAIL_TOOLS,
119
119
  ALL_TOOLS,