@agenticmail/enterprise 0.5.299 → 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.
- package/dist/chunk-QKQWDX6M.js +1519 -0
- package/dist/chunk-VLVRDLYO.js +48 -0
- package/dist/chunk-VMVOJFCX.js +4338 -0
- package/dist/cli-agent-4GZGC6XO.js +1778 -0
- package/dist/cli-recover-WS27YEB7.js +487 -0
- package/dist/cli-serve-HVULKNQH.js +143 -0
- package/dist/cli-verify-CVYMUGKX.js +149 -0
- package/dist/cli.js +5 -5
- package/dist/dashboard/components/icons.js +1 -0
- package/dist/dashboard/pages/organizations.js +182 -19
- package/dist/factory-XEBV2VGZ.js +9 -0
- package/dist/index.js +3 -3
- package/dist/postgres-NZBDKOQR.js +816 -0
- package/dist/server-3HZEV5X2.js +15 -0
- package/dist/setup-JUB67BUU.js +20 -0
- package/dist/sqlite-INPN4DQN.js +545 -0
- package/package.json +1 -1
- package/src/admin/routes.ts +94 -5
- package/src/dashboard/components/icons.js +1 -0
- package/src/dashboard/pages/organizations.js +182 -19
- package/src/db/postgres.ts +17 -0
- package/src/db/sqlite.ts +8 -0
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
67
|
+
import("./setup-JUB67BUU.js").then((m) => m.runSetupWizard()).catch(fatal);
|
|
68
68
|
break;
|
|
69
69
|
}
|
|
70
70
|
function fatal(err) {
|
|
@@ -56,6 +56,7 @@ export const I = {
|
|
|
56
56
|
chevronLeft: () => h('svg', S, h('polyline', { points: '15 18 9 12 15 6' })),
|
|
57
57
|
chevronRight: () => h('svg', S, h('polyline', { points: '9 18 15 12 9 6' })),
|
|
58
58
|
chevronDown: () => h('svg', S, h('polyline', { points: '6 9 12 15 18 9' })),
|
|
59
|
+
mail: () => h('svg', S, h('path', { d: 'M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z' }), h('polyline', { points: '22,6 12,13 2,6' })),
|
|
59
60
|
building: () => h('svg', S, h('path', { d: 'M3 21h18M3 10h18M3 7l9-4 9 4M4 10v11M20 10v11M8 14v.01M12 14v.01M16 14v.01M8 18v.01M12 18v.01M16 18v.01' })),
|
|
60
61
|
edit: () => h('svg', S, h('path', { d: 'M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7' }), h('path', { d: 'M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z' })),
|
|
61
62
|
};
|
|
@@ -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);
|
|
@@ -158,8 +172,9 @@ export function OrganizationsPage() {
|
|
|
158
172
|
.finally(function() { setActing(''); });
|
|
159
173
|
};
|
|
160
174
|
|
|
161
|
-
|
|
162
|
-
|
|
175
|
+
// Show agents not already in THIS org (includes unassigned AND agents from other orgs)
|
|
176
|
+
var assignableAgents = allAgents.filter(function(a) {
|
|
177
|
+
return detailAgents.every(function(da) { return da.id !== a.id; });
|
|
163
178
|
});
|
|
164
179
|
|
|
165
180
|
if (loading) return h('div', { style: { padding: 40, textAlign: 'center', color: 'var(--text-muted)' } }, 'Loading organizations...');
|
|
@@ -185,7 +200,11 @@ export function OrganizationsPage() {
|
|
|
185
200
|
// Org cards
|
|
186
201
|
orgs.length === 0
|
|
187
202
|
? h('div', { className: 'card', style: { textAlign: 'center', padding: 40 } },
|
|
188
|
-
h('div', { style: {
|
|
203
|
+
h('div', { style: { width: 48, height: 48, margin: '0 auto 12px', borderRadius: '50%', background: 'var(--bg-tertiary)', display: 'flex', alignItems: 'center', justifyContent: 'center' } },
|
|
204
|
+
h('svg', { width: 28, height: 28, viewBox: '0 0 24 24', fill: 'none', stroke: 'var(--text-muted)', strokeWidth: 1.5, strokeLinecap: 'round', strokeLinejoin: 'round' },
|
|
205
|
+
h('path', { d: 'M3 21h18M3 10h18M3 7l9-4 9 4M4 10v11M20 10v11M8 14v.01M12 14v.01M16 14v.01M8 18v.01M12 18v.01M16 18v.01' })
|
|
206
|
+
)
|
|
207
|
+
),
|
|
189
208
|
h('div', { style: { fontSize: 15, fontWeight: 600, marginBottom: 4 } }, 'No organizations yet'),
|
|
190
209
|
h('div', { style: { color: 'var(--text-muted)', fontSize: 13, marginBottom: 16 } }, 'Create your first client organization to start managing multi-tenant agent deployments.'),
|
|
191
210
|
h('button', { className: 'btn btn-primary', onClick: openCreate }, I.plus(), ' Create Organization')
|
|
@@ -204,8 +223,8 @@ export function OrganizationsPage() {
|
|
|
204
223
|
org.description && h('div', { style: { fontSize: 13, color: 'var(--text-secondary)', marginBottom: 12, lineHeight: 1.5 } }, org.description),
|
|
205
224
|
h('div', { style: { display: 'flex', gap: 16, fontSize: 12, color: 'var(--text-muted)' } },
|
|
206
225
|
h('span', null, I.agents(), ' ', (org.agent_count || 0), ' agent', (org.agent_count || 0) !== 1 ? 's' : ''),
|
|
207
|
-
org.
|
|
208
|
-
org.
|
|
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)
|
|
209
228
|
),
|
|
210
229
|
h('div', { style: { display: 'flex', gap: 6, marginTop: 12, borderTop: '1px solid var(--border)', paddingTop: 10 }, onClick: function(e) { e.stopPropagation(); } },
|
|
211
230
|
h('button', { className: 'btn btn-ghost btn-sm', onClick: function() { openEdit(org); } }, I.edit(), ' Edit'),
|
|
@@ -224,24 +243,36 @@ export function OrganizationsPage() {
|
|
|
224
243
|
h('div', { style: { display: 'flex', flexDirection: 'column', gap: 14, padding: 4 } },
|
|
225
244
|
h('div', null,
|
|
226
245
|
h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Name *'),
|
|
227
|
-
h('input', { className: 'input', value: fname, onInput: function(e) { setFname(e.target.value); if (!slugManual) setFslug(slugify(e.target.value)); }, placeholder: '
|
|
246
|
+
h('input', { className: 'input', value: fname, onInput: function(e) { setFname(e.target.value); if (!slugManual) setFslug(slugify(e.target.value)); }, placeholder: 'AgenticMail' })
|
|
228
247
|
),
|
|
229
248
|
h('div', null,
|
|
230
249
|
h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Slug *'),
|
|
231
|
-
h('input', { className: 'input', value: fslug, onInput: function(e) { setFslug(e.target.value); setSlugManual(true); }, placeholder: '
|
|
250
|
+
h('input', { className: 'input', value: fslug, onInput: function(e) { setFslug(e.target.value); setSlugManual(true); }, placeholder: 'agenticmail', style: { fontFamily: 'var(--font-mono, monospace)' } })
|
|
232
251
|
),
|
|
233
252
|
h('div', null,
|
|
234
253
|
h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Contact Name'),
|
|
235
|
-
h('input', { className: 'input', value: fcontact, onInput: function(e) { setFcontact(e.target.value); }, placeholder: '
|
|
254
|
+
h('input', { className: 'input', value: fcontact, onInput: function(e) { setFcontact(e.target.value); }, placeholder: 'Ope Olatunji' })
|
|
236
255
|
),
|
|
237
256
|
h('div', null,
|
|
238
257
|
h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Contact Email'),
|
|
239
|
-
h('input', { className: 'input', type: 'email', value: femail, onInput: function(e) { setFemail(e.target.value); }, placeholder: '
|
|
258
|
+
h('input', { className: 'input', type: 'email', value: femail, onInput: function(e) { setFemail(e.target.value); }, placeholder: 'ope@agenticmail.io' })
|
|
240
259
|
),
|
|
241
260
|
h('div', null,
|
|
242
261
|
h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Description'),
|
|
243
262
|
h('textarea', { className: 'input', value: fdesc, onInput: function(e) { setFdesc(e.target.value); }, placeholder: 'Brief description...', rows: 3, style: { resize: 'vertical' } })
|
|
244
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
|
+
),
|
|
245
276
|
h('div', { style: { display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 8 } },
|
|
246
277
|
h('button', { className: 'btn btn-secondary', onClick: function() { setShowCreate(false); } }, 'Cancel'),
|
|
247
278
|
h('button', { className: 'btn btn-primary', disabled: !fname || !fslug || acting === 'create', onClick: doCreate }, acting === 'create' ? 'Creating...' : 'Create')
|
|
@@ -272,6 +303,18 @@ export function OrganizationsPage() {
|
|
|
272
303
|
h('label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, 'Description'),
|
|
273
304
|
h('textarea', { className: 'input', value: fdesc, onInput: function(e) { setFdesc(e.target.value); }, rows: 3, style: { resize: 'vertical' } })
|
|
274
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
|
+
),
|
|
275
318
|
h('div', { style: { display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 8 } },
|
|
276
319
|
h('button', { className: 'btn btn-secondary', onClick: function() { setEditOrg(null); } }, 'Cancel'),
|
|
277
320
|
h('button', { className: 'btn btn-primary', disabled: !fname || acting === 'edit', onClick: doEdit }, acting === 'edit' ? 'Saving...' : 'Save Changes')
|
|
@@ -280,10 +323,10 @@ export function OrganizationsPage() {
|
|
|
280
323
|
),
|
|
281
324
|
|
|
282
325
|
// Detail Modal
|
|
283
|
-
detailOrg && h(Modal, { title: detailOrg.name || 'Organization Detail', onClose: function() { setDetailOrg(null); },
|
|
326
|
+
detailOrg && h(Modal, { title: detailOrg.name || 'Organization Detail', onClose: function() { setDetailOrg(null); }, width: 700 },
|
|
284
327
|
h('div', { style: { padding: 4 } },
|
|
285
328
|
// Org info
|
|
286
|
-
h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, marginBottom:
|
|
329
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16, marginBottom: 16 } },
|
|
287
330
|
h('div', null,
|
|
288
331
|
h('div', { style: { fontSize: 11, color: 'var(--text-muted)', textTransform: 'uppercase', fontWeight: 600, marginBottom: 4 } }, 'Slug'),
|
|
289
332
|
h('div', { style: { fontFamily: 'var(--font-mono, monospace)', fontSize: 13 } }, detailOrg.slug)
|
|
@@ -298,10 +341,29 @@ export function OrganizationsPage() {
|
|
|
298
341
|
detailOrg.contact_email && h('div', { style: { fontSize: 12, color: 'var(--text-muted)' } }, detailOrg.contact_email)
|
|
299
342
|
)
|
|
300
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
|
+
|
|
301
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),
|
|
302
352
|
|
|
303
|
-
//
|
|
304
|
-
h('div', { style: {
|
|
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'),
|
|
305
367
|
detailAgents.length > 0
|
|
306
368
|
? h('div', { style: { display: 'flex', flexDirection: 'column', gap: 6, marginBottom: 16 } },
|
|
307
369
|
detailAgents.map(function(a) {
|
|
@@ -322,13 +384,114 @@ export function OrganizationsPage() {
|
|
|
322
384
|
h('div', { style: { fontSize: 14, fontWeight: 700, marginBottom: 8 } }, 'Assign Agent'),
|
|
323
385
|
h('div', { style: { display: 'flex', gap: 8 } },
|
|
324
386
|
h('select', { className: 'input', value: assignAgentId, onChange: function(e) { setAssignAgentId(e.target.value); }, style: { flex: 1 } },
|
|
325
|
-
h('option', { value: '' }, '— Select an
|
|
326
|
-
|
|
327
|
-
|
|
387
|
+
h('option', { value: '' }, '— Select an agent to assign —'),
|
|
388
|
+
assignableAgents.map(function(a) {
|
|
389
|
+
var label = a.name + (a.role ? ' (' + a.role + ')' : '');
|
|
390
|
+
if (a.client_org_id) {
|
|
391
|
+
var fromOrg = orgs.find(function(o) { return o.id === a.client_org_id; });
|
|
392
|
+
label += fromOrg ? ' [from ' + fromOrg.name + ']' : ' [assigned elsewhere]';
|
|
393
|
+
}
|
|
394
|
+
return h('option', { key: a.id, value: a.id }, label);
|
|
328
395
|
})
|
|
329
396
|
),
|
|
330
397
|
h('button', { className: 'btn btn-primary btn-sm', disabled: !assignAgentId || acting === 'assign', onClick: doAssignAgent }, acting === 'assign' ? 'Assigning...' : 'Assign')
|
|
331
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
|
|
332
495
|
)
|
|
333
496
|
)
|
|
334
497
|
);
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
provision,
|
|
3
3
|
runSetupWizard
|
|
4
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
116
|
+
} from "./chunk-VLVRDLYO.js";
|
|
117
117
|
import {
|
|
118
118
|
AGENTICMAIL_TOOLS,
|
|
119
119
|
ALL_TOOLS,
|