@agenticmail/enterprise 0.5.291 → 0.5.293
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/agent-heartbeat-W2XLGPOG.js +510 -0
- package/dist/agent-tools-H7BYL7A6.js +13881 -0
- package/dist/chunk-64SXJMJI.js +3841 -0
- package/dist/chunk-67AD7SKP.js +4739 -0
- package/dist/chunk-I3AODI5Z.js +1519 -0
- package/dist/chunk-NPR5DIPX.js +48 -0
- package/dist/cli-agent-5JNS5J2U.js +1778 -0
- package/dist/cli-recover-2U3N37CE.js +487 -0
- package/dist/cli-serve-QM2D6TYP.js +143 -0
- package/dist/cli-verify-R32RJGVW.js +149 -0
- package/dist/cli.js +5 -5
- package/dist/dashboard/app.js +38 -4
- package/dist/dashboard/components/icons.js +2 -0
- package/dist/dashboard/pages/agent-detail/index.js +11 -1
- package/dist/dashboard/pages/users.js +282 -13
- package/dist/factory-KWNTMIVU.js +9 -0
- package/dist/index.js +17 -17
- package/dist/page-registry-OZYEX3Q3.js +178 -0
- package/dist/postgres-CRAQ7OOV.js +760 -0
- package/dist/routes-6DD25A5C.js +13695 -0
- package/dist/runtime-HKTQ22HR.js +45 -0
- package/dist/server-A37MVNDZ.js +15 -0
- package/dist/setup-XI4ZTR4B.js +20 -0
- package/dist/sqlite-RVBJTDQC.js +495 -0
- package/package.json +1 -1
- package/src/admin/page-registry.ts +204 -0
- package/src/admin/routes.ts +80 -0
- package/src/dashboard/app.js +38 -4
- package/src/dashboard/components/icons.js +2 -0
- package/src/dashboard/pages/agent-detail/index.js +11 -1
- package/src/dashboard/pages/users.js +282 -13
- package/src/db/adapter.ts +1 -0
- package/src/db/postgres.ts +2 -0
- package/src/db/sqlite.ts +4 -0
|
@@ -3,17 +3,232 @@ import { I } from '../components/icons.js';
|
|
|
3
3
|
import { Modal } from '../components/modal.js';
|
|
4
4
|
import { HelpButton } from '../components/help-button.js';
|
|
5
5
|
|
|
6
|
+
// ─── Permission Editor Component ───────────────────
|
|
7
|
+
|
|
8
|
+
function PermissionEditor({ userId, userName, currentPerms, pageRegistry, onSave, onClose }) {
|
|
9
|
+
// Deep clone perms
|
|
10
|
+
var [grants, setGrants] = useState(function() {
|
|
11
|
+
if (currentPerms === '*') {
|
|
12
|
+
// Start with all pages selected (all tabs)
|
|
13
|
+
var all = {};
|
|
14
|
+
Object.keys(pageRegistry).forEach(function(pid) { all[pid] = true; });
|
|
15
|
+
return all;
|
|
16
|
+
}
|
|
17
|
+
// Clone existing
|
|
18
|
+
var c = {};
|
|
19
|
+
Object.keys(currentPerms || {}).forEach(function(pid) {
|
|
20
|
+
var g = currentPerms[pid];
|
|
21
|
+
c[pid] = g === true ? true : (Array.isArray(g) ? g.slice() : true);
|
|
22
|
+
});
|
|
23
|
+
return c;
|
|
24
|
+
});
|
|
25
|
+
var [fullAccess, setFullAccess] = useState(currentPerms === '*');
|
|
26
|
+
var [saving, setSaving] = useState(false);
|
|
27
|
+
var [expandedPage, setExpandedPage] = useState(null);
|
|
28
|
+
|
|
29
|
+
var sections = { overview: [], management: [], administration: [] };
|
|
30
|
+
Object.keys(pageRegistry).forEach(function(pid) {
|
|
31
|
+
var page = pageRegistry[pid];
|
|
32
|
+
if (sections[page.section]) sections[page.section].push(pid);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
var togglePage = function(pid) {
|
|
36
|
+
setGrants(function(g) {
|
|
37
|
+
var next = Object.assign({}, g);
|
|
38
|
+
if (next[pid]) {
|
|
39
|
+
delete next[pid];
|
|
40
|
+
if (expandedPage === pid) setExpandedPage(null);
|
|
41
|
+
} else {
|
|
42
|
+
next[pid] = true;
|
|
43
|
+
}
|
|
44
|
+
return next;
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
var toggleTab = function(pid, tabId) {
|
|
49
|
+
setGrants(function(g) {
|
|
50
|
+
var next = Object.assign({}, g);
|
|
51
|
+
var current = next[pid];
|
|
52
|
+
var allTabs = Object.keys(pageRegistry[pid].tabs || {});
|
|
53
|
+
|
|
54
|
+
if (current === true) {
|
|
55
|
+
// Was all tabs — remove this one
|
|
56
|
+
var remaining = allTabs.filter(function(t) { return t !== tabId; });
|
|
57
|
+
next[pid] = remaining.length > 0 ? remaining : true;
|
|
58
|
+
} else if (Array.isArray(current)) {
|
|
59
|
+
var idx = current.indexOf(tabId);
|
|
60
|
+
if (idx >= 0) {
|
|
61
|
+
var arr = current.filter(function(t) { return t !== tabId; });
|
|
62
|
+
if (arr.length === 0) delete next[pid]; // no tabs = remove page
|
|
63
|
+
else next[pid] = arr;
|
|
64
|
+
} else {
|
|
65
|
+
var newArr = current.concat([tabId]);
|
|
66
|
+
if (newArr.length === allTabs.length) next[pid] = true; // all tabs = page access
|
|
67
|
+
else next[pid] = newArr;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return next;
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
var isPageChecked = function(pid) { return !!grants[pid]; };
|
|
75
|
+
var isTabChecked = function(pid, tabId) {
|
|
76
|
+
var g = grants[pid];
|
|
77
|
+
if (!g) return false;
|
|
78
|
+
if (g === true) return true;
|
|
79
|
+
return Array.isArray(g) && g.indexOf(tabId) >= 0;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
var toggleFullAccess = function() {
|
|
83
|
+
var newVal = !fullAccess;
|
|
84
|
+
setFullAccess(newVal);
|
|
85
|
+
if (newVal) {
|
|
86
|
+
var all = {};
|
|
87
|
+
Object.keys(pageRegistry).forEach(function(pid) { all[pid] = true; });
|
|
88
|
+
setGrants(all);
|
|
89
|
+
} else {
|
|
90
|
+
setGrants({});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
var selectAll = function() {
|
|
95
|
+
var all = {};
|
|
96
|
+
Object.keys(pageRegistry).forEach(function(pid) { all[pid] = true; });
|
|
97
|
+
setGrants(all);
|
|
98
|
+
};
|
|
99
|
+
var selectNone = function() { setGrants({}); };
|
|
100
|
+
|
|
101
|
+
var doSave = async function() {
|
|
102
|
+
setSaving(true);
|
|
103
|
+
try {
|
|
104
|
+
var permsToSave = fullAccess ? '*' : grants;
|
|
105
|
+
await onSave(permsToSave);
|
|
106
|
+
} catch(e) { /* handled by parent */ }
|
|
107
|
+
setSaving(false);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
var sectionLabels = { overview: 'Overview', management: 'Management', administration: 'Administration' };
|
|
111
|
+
|
|
112
|
+
var _cs = { display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px', borderRadius: 6, cursor: 'pointer', fontSize: 13, transition: 'background 0.15s' };
|
|
113
|
+
var _csHover = Object.assign({}, _cs, { background: 'var(--bg-tertiary)' });
|
|
114
|
+
var _tabRow = { display: 'flex', alignItems: 'center', gap: 8, padding: '4px 12px 4px 40px', fontSize: 12, color: 'var(--text-secondary)', cursor: 'pointer' };
|
|
115
|
+
var _checkbox = { width: 16, height: 16, accentColor: 'var(--primary)', cursor: 'pointer' };
|
|
116
|
+
var _sectionTitle = { fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)', padding: '16px 12px 4px', marginTop: 4 };
|
|
117
|
+
|
|
118
|
+
return h(Modal, {
|
|
119
|
+
title: 'Edit Permissions — ' + (userName || 'User'),
|
|
120
|
+
onClose: onClose,
|
|
121
|
+
width: 560,
|
|
122
|
+
footer: h(Fragment, null,
|
|
123
|
+
h('button', { className: 'btn btn-secondary', onClick: onClose }, 'Cancel'),
|
|
124
|
+
h('button', { className: 'btn btn-primary', onClick: doSave, disabled: saving }, saving ? 'Saving...' : 'Save Permissions')
|
|
125
|
+
)
|
|
126
|
+
},
|
|
127
|
+
// Full access toggle
|
|
128
|
+
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 12px', marginBottom: 12, background: fullAccess ? 'var(--success-soft, rgba(21,128,61,0.1))' : 'var(--bg-tertiary)', borderRadius: 8, border: '1px solid ' + (fullAccess ? 'var(--success, #15803d)' : 'var(--border)') } },
|
|
129
|
+
h('div', null,
|
|
130
|
+
h('strong', { style: { fontSize: 13 } }, 'Full Access'),
|
|
131
|
+
h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2 } }, 'Owner and Admin roles always have full access')
|
|
132
|
+
),
|
|
133
|
+
h('input', { type: 'checkbox', checked: fullAccess, onChange: toggleFullAccess, style: Object.assign({}, _checkbox, { width: 20, height: 20 }) })
|
|
134
|
+
),
|
|
135
|
+
|
|
136
|
+
// Quick actions
|
|
137
|
+
!fullAccess && h('div', { style: { display: 'flex', gap: 8, marginBottom: 12 } },
|
|
138
|
+
h('button', { className: 'btn btn-ghost btn-sm', onClick: selectAll, style: { fontSize: 11 } }, 'Select All'),
|
|
139
|
+
h('button', { className: 'btn btn-ghost btn-sm', onClick: selectNone, style: { fontSize: 11 } }, 'Select None')
|
|
140
|
+
),
|
|
141
|
+
|
|
142
|
+
// Page/tab list grouped by section
|
|
143
|
+
!fullAccess && h('div', { style: { maxHeight: 400, overflowY: 'auto', border: '1px solid var(--border)', borderRadius: 8 } },
|
|
144
|
+
Object.keys(sections).map(function(sectionKey) {
|
|
145
|
+
var pageIds = sections[sectionKey];
|
|
146
|
+
if (pageIds.length === 0) return null;
|
|
147
|
+
return h(Fragment, { key: sectionKey },
|
|
148
|
+
h('div', { style: _sectionTitle }, sectionLabels[sectionKey]),
|
|
149
|
+
pageIds.map(function(pid) {
|
|
150
|
+
var page = pageRegistry[pid];
|
|
151
|
+
var hasTabs = page.tabs && Object.keys(page.tabs).length > 0;
|
|
152
|
+
var isExpanded = expandedPage === pid;
|
|
153
|
+
var checked = isPageChecked(pid);
|
|
154
|
+
var tabCount = hasTabs ? Object.keys(page.tabs).length : 0;
|
|
155
|
+
var checkedTabCount = 0;
|
|
156
|
+
if (hasTabs && checked) {
|
|
157
|
+
if (grants[pid] === true) checkedTabCount = tabCount;
|
|
158
|
+
else if (Array.isArray(grants[pid])) checkedTabCount = grants[pid].length;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return h(Fragment, { key: pid },
|
|
162
|
+
h('div', {
|
|
163
|
+
style: Object.assign({}, _cs, checked ? { background: 'var(--bg-tertiary)' } : {}),
|
|
164
|
+
onClick: function(e) {
|
|
165
|
+
// Don't toggle page when clicking the expand arrow
|
|
166
|
+
if (e.target.tagName === 'svg' || e.target.tagName === 'path' || e.target.closest?.('[data-expand]')) return;
|
|
167
|
+
togglePage(pid);
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
h('input', { type: 'checkbox', checked: checked, readOnly: true, style: _checkbox }),
|
|
171
|
+
h('div', { style: { flex: 1 } },
|
|
172
|
+
h('div', { style: { fontWeight: 500 } }, page.label),
|
|
173
|
+
page.description && h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 1 } }, page.description)
|
|
174
|
+
),
|
|
175
|
+
hasTabs && h('div', { style: { display: 'flex', alignItems: 'center', gap: 6 } },
|
|
176
|
+
checked && h('span', { style: { fontSize: 10, color: 'var(--text-muted)', whiteSpace: 'nowrap' } },
|
|
177
|
+
checkedTabCount + '/' + tabCount + ' tabs'
|
|
178
|
+
),
|
|
179
|
+
h('button', {
|
|
180
|
+
'data-expand': true,
|
|
181
|
+
className: 'btn btn-ghost btn-sm',
|
|
182
|
+
style: { padding: '2px 4px', minWidth: 0 },
|
|
183
|
+
onClick: function(e) { e.stopPropagation(); setExpandedPage(isExpanded ? null : pid); }
|
|
184
|
+
}, isExpanded ? I.chevronDown() : I.chevronRight())
|
|
185
|
+
)
|
|
186
|
+
),
|
|
187
|
+
// Expanded tabs
|
|
188
|
+
hasTabs && isExpanded && checked && Object.keys(page.tabs).map(function(tabId) {
|
|
189
|
+
return h('div', {
|
|
190
|
+
key: tabId,
|
|
191
|
+
style: _tabRow,
|
|
192
|
+
onClick: function() { toggleTab(pid, tabId); }
|
|
193
|
+
},
|
|
194
|
+
h('input', { type: 'checkbox', checked: isTabChecked(pid, tabId), readOnly: true, style: _checkbox }),
|
|
195
|
+
h('span', null, page.tabs[tabId])
|
|
196
|
+
);
|
|
197
|
+
})
|
|
198
|
+
);
|
|
199
|
+
})
|
|
200
|
+
);
|
|
201
|
+
})
|
|
202
|
+
),
|
|
203
|
+
|
|
204
|
+
// Summary
|
|
205
|
+
h('div', { style: { marginTop: 12, padding: 8, background: 'var(--bg-tertiary)', borderRadius: 6, fontSize: 11, color: 'var(--text-muted)' } },
|
|
206
|
+
fullAccess
|
|
207
|
+
? 'This user has full access to all pages and tabs.'
|
|
208
|
+
: 'Access to ' + Object.keys(grants).length + ' of ' + Object.keys(pageRegistry).length + ' pages.'
|
|
209
|
+
)
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ─── Users Page ────────────────────────────────────
|
|
214
|
+
|
|
6
215
|
export function UsersPage() {
|
|
7
216
|
var { toast } = useApp();
|
|
8
217
|
var [users, setUsers] = useState([]);
|
|
9
218
|
var [creating, setCreating] = useState(false);
|
|
10
219
|
var [form, setForm] = useState({ email: '', password: '', name: '', role: 'viewer' });
|
|
11
|
-
var [resetTarget, setResetTarget] = useState(null);
|
|
220
|
+
var [resetTarget, setResetTarget] = useState(null);
|
|
12
221
|
var [newPassword, setNewPassword] = useState('');
|
|
13
222
|
var [resetting, setResetting] = useState(false);
|
|
223
|
+
var [permTarget, setPermTarget] = useState(null); // user object for permission editing
|
|
224
|
+
var [permGrants, setPermGrants] = useState('*'); // current permissions for target
|
|
225
|
+
var [pageRegistry, setPageRegistry] = useState(null); // page/tab registry from backend
|
|
14
226
|
|
|
15
227
|
var load = function() { apiCall('/users').then(function(d) { setUsers(d.users || d || []); }).catch(function() {}); };
|
|
16
|
-
useEffect(function() {
|
|
228
|
+
useEffect(function() {
|
|
229
|
+
load();
|
|
230
|
+
apiCall('/page-registry').then(function(d) { setPageRegistry(d); }).catch(function() {});
|
|
231
|
+
}, []);
|
|
17
232
|
|
|
18
233
|
var create = async function() {
|
|
19
234
|
try {
|
|
@@ -50,6 +265,23 @@ export function UsersPage() {
|
|
|
50
265
|
} catch (e) { toast(e.message, 'error'); }
|
|
51
266
|
};
|
|
52
267
|
|
|
268
|
+
var openPermissions = async function(user) {
|
|
269
|
+
try {
|
|
270
|
+
var d = await apiCall('/users/' + user.id + '/permissions');
|
|
271
|
+
setPermGrants(d.permissions || '*');
|
|
272
|
+
setPermTarget(user);
|
|
273
|
+
} catch (e) { toast('Failed to load permissions', 'error'); }
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
var savePermissions = async function(newPerms) {
|
|
277
|
+
try {
|
|
278
|
+
await apiCall('/users/' + permTarget.id + '/permissions', { method: 'PUT', body: JSON.stringify({ permissions: newPerms }) });
|
|
279
|
+
toast('Permissions updated for ' + (permTarget.name || permTarget.email), 'success');
|
|
280
|
+
setPermTarget(null);
|
|
281
|
+
load();
|
|
282
|
+
} catch (e) { toast(e.message, 'error'); throw e; }
|
|
283
|
+
};
|
|
284
|
+
|
|
53
285
|
var generatePassword = function() {
|
|
54
286
|
var chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789!@#$%';
|
|
55
287
|
var pw = '';
|
|
@@ -57,20 +289,36 @@ export function UsersPage() {
|
|
|
57
289
|
setNewPassword(pw);
|
|
58
290
|
};
|
|
59
291
|
|
|
292
|
+
// Permission badge for display
|
|
293
|
+
var permBadge = function(user) {
|
|
294
|
+
if (user.role === 'owner' || user.role === 'admin') {
|
|
295
|
+
return h('span', { className: 'badge badge-success', style: { fontSize: 10 } }, 'Full');
|
|
296
|
+
}
|
|
297
|
+
var p = user.permissions;
|
|
298
|
+
if (!p || p === '*' || p === '"*"') return h('span', { className: 'badge badge-success', style: { fontSize: 10 } }, 'Full');
|
|
299
|
+
try {
|
|
300
|
+
var parsed = typeof p === 'string' ? JSON.parse(p) : p;
|
|
301
|
+
if (parsed === '*') return h('span', { className: 'badge badge-success', style: { fontSize: 10 } }, 'Full');
|
|
302
|
+
var count = Object.keys(parsed).length;
|
|
303
|
+
var total = pageRegistry ? Object.keys(pageRegistry).length : '?';
|
|
304
|
+
return h('span', { className: 'badge badge-warning', style: { fontSize: 10 } }, count + '/' + total + ' pages');
|
|
305
|
+
} catch { return h('span', { className: 'badge badge-neutral', style: { fontSize: 10 } }, 'Custom'); }
|
|
306
|
+
};
|
|
307
|
+
|
|
60
308
|
return h(Fragment, null,
|
|
61
309
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
|
|
62
310
|
h('div', null, h('h1', { style: { fontSize: 20, fontWeight: 700, display: 'flex', alignItems: 'center' } }, 'Users', h(HelpButton, { label: 'Users' },
|
|
63
311
|
h('p', null, 'Manage dashboard users who can access and administer the AgenticMail Enterprise console. Each user has a role that controls what they can see and do.'),
|
|
64
312
|
h('h4', { style: { marginTop: 16, marginBottom: 8, fontSize: 14 } }, 'Roles'),
|
|
65
313
|
h('ul', { style: { paddingLeft: 20, margin: '4px 0 8px' } },
|
|
66
|
-
h('li', null, h('strong', null, 'Owner'), ' — Full access
|
|
67
|
-
h('li', null, h('strong', null, 'Admin'), ' —
|
|
68
|
-
h('li', null, h('strong', null, 'Member'), ' —
|
|
69
|
-
h('li', null, h('strong', null, 'Viewer'), ' — Read-only
|
|
314
|
+
h('li', null, h('strong', null, 'Owner'), ' — Full access to everything. Cannot be restricted.'),
|
|
315
|
+
h('li', null, h('strong', null, 'Admin'), ' — Full access to everything. Cannot be restricted.'),
|
|
316
|
+
h('li', null, h('strong', null, 'Member'), ' — Access based on page permissions. Set via the shield icon.'),
|
|
317
|
+
h('li', null, h('strong', null, 'Viewer'), ' — Read-only. Access based on page permissions.')
|
|
70
318
|
),
|
|
71
|
-
h('h4', { style: { marginTop: 16, marginBottom: 8, fontSize: 14 } }, '
|
|
72
|
-
h('p', null, '
|
|
73
|
-
h('div', { style: { marginTop: 12, padding: 12, background: 'var(--bg-secondary, #1e293b)', borderRadius: 'var(--radius, 8px)', fontSize: 13 } }, h('strong', null, 'Tip: '), '
|
|
319
|
+
h('h4', { style: { marginTop: 16, marginBottom: 8, fontSize: 14 } }, 'Page Permissions'),
|
|
320
|
+
h('p', null, 'Click the shield icon on a Member or Viewer to control which pages and tabs they can see. Pages with tabs (like Agents) allow tab-level control.'),
|
|
321
|
+
h('div', { style: { marginTop: 12, padding: 12, background: 'var(--bg-secondary, #1e293b)', borderRadius: 'var(--radius, 8px)', fontSize: 13 } }, h('strong', null, 'Tip: '), 'Owner and Admin users always have full access — permissions only apply to Member and Viewer roles.')
|
|
74
322
|
)), h('p', { style: { color: 'var(--text-muted)', fontSize: 13 } }, 'Manage team members and their access')),
|
|
75
323
|
h('button', { className: 'btn btn-primary', onClick: function() { setCreating(true); } }, I.plus(), ' Add User')
|
|
76
324
|
),
|
|
@@ -80,7 +328,10 @@ export function UsersPage() {
|
|
|
80
328
|
h('div', { className: 'form-group' }, h('label', { className: 'form-label' }, 'Name'), h('input', { className: 'input', value: form.name, onChange: function(e) { setForm(function(f) { return Object.assign({}, f, { name: e.target.value }); }); } })),
|
|
81
329
|
h('div', { className: 'form-group' }, h('label', { className: 'form-label' }, 'Email *'), h('input', { className: 'input', type: 'email', value: form.email, onChange: function(e) { setForm(function(f) { return Object.assign({}, f, { email: e.target.value }); }); } })),
|
|
82
330
|
h('div', { className: 'form-group' }, h('label', { className: 'form-label' }, 'Password *'), h('input', { className: 'input', type: 'password', value: form.password, onChange: function(e) { setForm(function(f) { return Object.assign({}, f, { password: e.target.value }); }); } })),
|
|
83
|
-
h('div', { className: 'form-group' }, h('label', { className: 'form-label' }, 'Role'), h('select', { className: 'input', value: form.role, onChange: function(e) { setForm(function(f) { return Object.assign({}, f, { role: e.target.value }); }); } }, h('option', { value: 'viewer' }, 'Viewer'), h('option', { value: 'member' }, 'Member'), h('option', { value: 'admin' }, 'Admin'), h('option', { value: 'owner' }, 'Owner')))
|
|
331
|
+
h('div', { className: 'form-group' }, h('label', { className: 'form-label' }, 'Role'), h('select', { className: 'input', value: form.role, onChange: function(e) { setForm(function(f) { return Object.assign({}, f, { role: e.target.value }); }); } }, h('option', { value: 'viewer' }, 'Viewer'), h('option', { value: 'member' }, 'Member'), h('option', { value: 'admin' }, 'Admin'), h('option', { value: 'owner' }, 'Owner'))),
|
|
332
|
+
(form.role === 'member' || form.role === 'viewer') && h('div', { style: { marginTop: 8, padding: 10, background: 'var(--info-soft)', borderRadius: 'var(--radius)', fontSize: 12, color: 'var(--info)' } },
|
|
333
|
+
'After creating this user, click the shield icon to set their page permissions. By default, new Member/Viewer users have full access.'
|
|
334
|
+
)
|
|
84
335
|
),
|
|
85
336
|
|
|
86
337
|
// Reset password modal
|
|
@@ -93,7 +344,7 @@ export function UsersPage() {
|
|
|
93
344
|
)
|
|
94
345
|
},
|
|
95
346
|
h('div', { style: { marginBottom: 16 } },
|
|
96
|
-
h('p', { style: { fontSize: 13, color: 'var(--text-secondary)' } }, 'Set a new password for ', h('strong', null, resetTarget.name || resetTarget.email), '.
|
|
347
|
+
h('p', { style: { fontSize: 13, color: 'var(--text-secondary)' } }, 'Set a new password for ', h('strong', null, resetTarget.name || resetTarget.email), '.'),
|
|
97
348
|
resetTarget.totpEnabled && h('div', { style: { marginTop: 8, padding: 8, background: 'var(--info-soft)', borderRadius: 'var(--radius)', fontSize: 12, color: 'var(--info)' } }, 'This user has 2FA enabled. Password reset will not affect their 2FA setup.')
|
|
98
349
|
),
|
|
99
350
|
h('div', { className: 'form-group' },
|
|
@@ -104,25 +355,43 @@ export function UsersPage() {
|
|
|
104
355
|
)
|
|
105
356
|
),
|
|
106
357
|
newPassword && h('div', { style: { marginTop: 8, padding: 8, background: 'var(--bg-tertiary)', borderRadius: 'var(--radius)', fontSize: 12, color: 'var(--text-muted)' } },
|
|
107
|
-
'Make sure to share this password securely with the user.
|
|
358
|
+
'Make sure to share this password securely with the user.'
|
|
108
359
|
)
|
|
109
360
|
),
|
|
110
361
|
|
|
362
|
+
// Permission editor modal
|
|
363
|
+
permTarget && pageRegistry && h(PermissionEditor, {
|
|
364
|
+
userId: permTarget.id,
|
|
365
|
+
userName: permTarget.name || permTarget.email,
|
|
366
|
+
currentPerms: permGrants,
|
|
367
|
+
pageRegistry: pageRegistry,
|
|
368
|
+
onSave: savePermissions,
|
|
369
|
+
onClose: function() { setPermTarget(null); }
|
|
370
|
+
}),
|
|
371
|
+
|
|
111
372
|
// Users table
|
|
112
373
|
h('div', { className: 'card' },
|
|
113
374
|
h('div', { className: 'card-body-flush' },
|
|
114
375
|
users.length === 0 ? h('div', { style: { padding: 24, textAlign: 'center', color: 'var(--text-muted)' } }, 'No users')
|
|
115
376
|
: h('table', null,
|
|
116
|
-
h('thead', null, h('tr', null, h('th', null, 'Name'), h('th', null, 'Email'), h('th', null, 'Role'), h('th', null, '2FA'), h('th', null, 'Created'), h('th', { style: { width:
|
|
377
|
+
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'))),
|
|
117
378
|
h('tbody', null, users.map(function(u) {
|
|
379
|
+
var isRestricted = u.role === 'member' || u.role === 'viewer';
|
|
118
380
|
return h('tr', { key: u.id },
|
|
119
381
|
h('td', null, h('strong', null, u.name || '-')),
|
|
120
382
|
h('td', null, h('span', { style: { fontFamily: 'var(--font-mono)', fontSize: 12 } }, u.email)),
|
|
121
383
|
h('td', null, h('span', { className: 'badge badge-' + (u.role === 'owner' ? 'warning' : u.role === 'admin' ? 'primary' : 'neutral') }, u.role)),
|
|
384
|
+
h('td', null, permBadge(u)),
|
|
122
385
|
h('td', null, u.totpEnabled ? h('span', { className: 'badge badge-success' }, 'On') : h('span', { className: 'badge badge-neutral' }, 'Off')),
|
|
123
386
|
h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, u.createdAt ? new Date(u.createdAt).toLocaleDateString() : '-'),
|
|
124
387
|
h('td', null,
|
|
125
388
|
h('div', { style: { display: 'flex', gap: 4 } },
|
|
389
|
+
h('button', {
|
|
390
|
+
className: 'btn btn-ghost btn-sm',
|
|
391
|
+
title: isRestricted ? 'Edit Permissions' : 'Permissions (Owner/Admin have full access)',
|
|
392
|
+
onClick: function() { openPermissions(u); },
|
|
393
|
+
style: !isRestricted ? { opacity: 0.4 } : {}
|
|
394
|
+
}, I.shield()),
|
|
126
395
|
h('button', { className: 'btn btn-ghost btn-sm', title: 'Reset Password', onClick: function() { setResetTarget(u); setNewPassword(''); } }, I.lock()),
|
|
127
396
|
h('button', { className: 'btn btn-ghost btn-sm', title: 'Delete User', onClick: function() { deleteUser(u); }, style: { color: 'var(--danger)' } }, I.trash())
|
|
128
397
|
)
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
provision,
|
|
3
|
+
runSetupWizard
|
|
4
|
+
} from "./chunk-I3AODI5Z.js";
|
|
1
5
|
import {
|
|
2
6
|
AgenticMailManager,
|
|
3
7
|
GoogleEmailProvider,
|
|
@@ -10,10 +14,6 @@ import {
|
|
|
10
14
|
generateEnvFile,
|
|
11
15
|
generateFlyToml
|
|
12
16
|
} from "./chunk-CMJJLSUB.js";
|
|
13
|
-
import {
|
|
14
|
-
provision,
|
|
15
|
-
runSetupWizard
|
|
16
|
-
} from "./chunk-S3WOS4UN.js";
|
|
17
17
|
import {
|
|
18
18
|
AgentRuntime,
|
|
19
19
|
EmailChannel,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
executeTool,
|
|
29
29
|
runAgentLoop,
|
|
30
30
|
toolsToDefinitions
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-67AD7SKP.js";
|
|
32
32
|
import {
|
|
33
33
|
ValidationError,
|
|
34
34
|
auditLogger,
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
requireRole,
|
|
43
43
|
securityHeaders,
|
|
44
44
|
validate
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-64SXJMJI.js";
|
|
46
46
|
import "./chunk-OF4MUWWS.js";
|
|
47
47
|
import {
|
|
48
48
|
PROVIDER_REGISTRY,
|
|
@@ -50,6 +50,16 @@ import {
|
|
|
50
50
|
resolveApiKeyForProvider,
|
|
51
51
|
resolveProvider
|
|
52
52
|
} from "./chunk-UF3ZJMJO.js";
|
|
53
|
+
import {
|
|
54
|
+
ENGINE_TABLES,
|
|
55
|
+
ENGINE_TABLES_POSTGRES,
|
|
56
|
+
EngineDatabase,
|
|
57
|
+
MIGRATIONS,
|
|
58
|
+
MIGRATIONS_TABLE,
|
|
59
|
+
MIGRATIONS_TABLE_POSTGRES,
|
|
60
|
+
sqliteToMySQL,
|
|
61
|
+
sqliteToPostgres
|
|
62
|
+
} from "./chunk-KKTVGBSC.js";
|
|
53
63
|
import {
|
|
54
64
|
ActionJournal,
|
|
55
65
|
ActivityTracker,
|
|
@@ -70,16 +80,6 @@ import {
|
|
|
70
80
|
init_guardrails
|
|
71
81
|
} from "./chunk-LOBBAW6U.js";
|
|
72
82
|
import "./chunk-3OC6RH7W.js";
|
|
73
|
-
import {
|
|
74
|
-
ENGINE_TABLES,
|
|
75
|
-
ENGINE_TABLES_POSTGRES,
|
|
76
|
-
EngineDatabase,
|
|
77
|
-
MIGRATIONS,
|
|
78
|
-
MIGRATIONS_TABLE,
|
|
79
|
-
MIGRATIONS_TABLE_POSTGRES,
|
|
80
|
-
sqliteToMySQL,
|
|
81
|
-
sqliteToPostgres
|
|
82
|
-
} from "./chunk-KKTVGBSC.js";
|
|
83
83
|
import {
|
|
84
84
|
BUILTIN_SKILLS,
|
|
85
85
|
PRESET_PROFILES,
|
|
@@ -113,7 +113,7 @@ import {
|
|
|
113
113
|
import {
|
|
114
114
|
createAdapter,
|
|
115
115
|
getSupportedDatabases
|
|
116
|
-
} from "./chunk-
|
|
116
|
+
} from "./chunk-NPR5DIPX.js";
|
|
117
117
|
import {
|
|
118
118
|
AGENTICMAIL_TOOLS,
|
|
119
119
|
ALL_TOOLS,
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import "./chunk-KFQGP6VL.js";
|
|
2
|
+
|
|
3
|
+
// src/admin/page-registry.ts
|
|
4
|
+
var PAGE_REGISTRY = {
|
|
5
|
+
// ─── Overview ────────────────────────────────────
|
|
6
|
+
dashboard: {
|
|
7
|
+
label: "Dashboard",
|
|
8
|
+
section: "overview",
|
|
9
|
+
description: "Main dashboard with key metrics and activity feed"
|
|
10
|
+
},
|
|
11
|
+
// ─── Management ──────────────────────────────────
|
|
12
|
+
agents: {
|
|
13
|
+
label: "Agents",
|
|
14
|
+
section: "management",
|
|
15
|
+
description: "View and manage AI agents",
|
|
16
|
+
tabs: {
|
|
17
|
+
overview: "Overview",
|
|
18
|
+
personal: "Personal Details",
|
|
19
|
+
email: "Email",
|
|
20
|
+
whatsapp: "WhatsApp",
|
|
21
|
+
channels: "Channels",
|
|
22
|
+
configuration: "Configuration",
|
|
23
|
+
manager: "Manager",
|
|
24
|
+
tools: "Tools",
|
|
25
|
+
skills: "Skills",
|
|
26
|
+
permissions: "Permissions",
|
|
27
|
+
activity: "Activity",
|
|
28
|
+
communication: "Communication",
|
|
29
|
+
workforce: "Workforce",
|
|
30
|
+
memory: "Memory",
|
|
31
|
+
guardrails: "Guardrails",
|
|
32
|
+
autonomy: "Autonomy",
|
|
33
|
+
budget: "Budget",
|
|
34
|
+
security: "Security",
|
|
35
|
+
"tool-security": "Tool Security",
|
|
36
|
+
deployment: "Deployment"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
skills: {
|
|
40
|
+
label: "Skills",
|
|
41
|
+
section: "management",
|
|
42
|
+
description: "Manage agent skill packs"
|
|
43
|
+
},
|
|
44
|
+
"community-skills": {
|
|
45
|
+
label: "Community Skills",
|
|
46
|
+
section: "management",
|
|
47
|
+
description: "Browse and install community skill marketplace"
|
|
48
|
+
},
|
|
49
|
+
"skill-connections": {
|
|
50
|
+
label: "Integrations & MCP",
|
|
51
|
+
section: "management",
|
|
52
|
+
description: "MCP servers, built-in integrations, and community skills"
|
|
53
|
+
},
|
|
54
|
+
"database-access": {
|
|
55
|
+
label: "Database Access",
|
|
56
|
+
section: "management",
|
|
57
|
+
description: "Manage database connections and agent access",
|
|
58
|
+
tabs: {
|
|
59
|
+
connections: "Connections",
|
|
60
|
+
access: "Agent Access",
|
|
61
|
+
audit: "Audit Log"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
knowledge: {
|
|
65
|
+
label: "Knowledge Bases",
|
|
66
|
+
section: "management",
|
|
67
|
+
description: "Manage knowledge base documents and collections"
|
|
68
|
+
},
|
|
69
|
+
"knowledge-contributions": {
|
|
70
|
+
label: "Knowledge Hub",
|
|
71
|
+
section: "management",
|
|
72
|
+
description: "Agent contributions to shared knowledge"
|
|
73
|
+
},
|
|
74
|
+
approvals: {
|
|
75
|
+
label: "Approvals",
|
|
76
|
+
section: "management",
|
|
77
|
+
description: "Review and approve pending agent actions"
|
|
78
|
+
},
|
|
79
|
+
"org-chart": {
|
|
80
|
+
label: "Org Chart",
|
|
81
|
+
section: "management",
|
|
82
|
+
description: "Visual agent hierarchy and reporting structure"
|
|
83
|
+
},
|
|
84
|
+
"task-pipeline": {
|
|
85
|
+
label: "Task Pipeline",
|
|
86
|
+
section: "management",
|
|
87
|
+
description: "Track task lifecycle from creation to completion"
|
|
88
|
+
},
|
|
89
|
+
workforce: {
|
|
90
|
+
label: "Workforce",
|
|
91
|
+
section: "management",
|
|
92
|
+
description: "Agent scheduling, workload, and availability"
|
|
93
|
+
},
|
|
94
|
+
messages: {
|
|
95
|
+
label: "Messages",
|
|
96
|
+
section: "management",
|
|
97
|
+
description: "Inter-agent and external message logs"
|
|
98
|
+
},
|
|
99
|
+
guardrails: {
|
|
100
|
+
label: "Guardrails",
|
|
101
|
+
section: "management",
|
|
102
|
+
description: "Global safety policies and content filters"
|
|
103
|
+
},
|
|
104
|
+
journal: {
|
|
105
|
+
label: "Journal",
|
|
106
|
+
section: "management",
|
|
107
|
+
description: "System event journal and decision log"
|
|
108
|
+
},
|
|
109
|
+
// ─── Administration ──────────────────────────────
|
|
110
|
+
dlp: {
|
|
111
|
+
label: "DLP",
|
|
112
|
+
section: "administration",
|
|
113
|
+
description: "Data loss prevention policies and alerts"
|
|
114
|
+
},
|
|
115
|
+
compliance: {
|
|
116
|
+
label: "Compliance",
|
|
117
|
+
section: "administration",
|
|
118
|
+
description: "Regulatory compliance settings and reports"
|
|
119
|
+
},
|
|
120
|
+
"domain-status": {
|
|
121
|
+
label: "Domain",
|
|
122
|
+
section: "administration",
|
|
123
|
+
description: "Domain configuration and deployment status"
|
|
124
|
+
},
|
|
125
|
+
users: {
|
|
126
|
+
label: "Users",
|
|
127
|
+
section: "administration",
|
|
128
|
+
description: "Manage dashboard users, roles, and permissions"
|
|
129
|
+
},
|
|
130
|
+
vault: {
|
|
131
|
+
label: "Vault",
|
|
132
|
+
section: "administration",
|
|
133
|
+
description: "Encrypted credential storage"
|
|
134
|
+
},
|
|
135
|
+
audit: {
|
|
136
|
+
label: "Audit Log",
|
|
137
|
+
section: "administration",
|
|
138
|
+
description: "Full audit trail of all system actions"
|
|
139
|
+
},
|
|
140
|
+
settings: {
|
|
141
|
+
label: "Settings",
|
|
142
|
+
section: "administration",
|
|
143
|
+
description: "Global platform configuration and branding"
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
function getAllPageIds() {
|
|
147
|
+
return Object.keys(PAGE_REGISTRY);
|
|
148
|
+
}
|
|
149
|
+
function getPageTabs(pageId) {
|
|
150
|
+
const page = PAGE_REGISTRY[pageId];
|
|
151
|
+
return page?.tabs ? Object.keys(page.tabs) : [];
|
|
152
|
+
}
|
|
153
|
+
function hasPageAccess(grants, pageId) {
|
|
154
|
+
if (grants === "*") return true;
|
|
155
|
+
return pageId in grants;
|
|
156
|
+
}
|
|
157
|
+
function hasTabAccess(grants, pageId, tabId) {
|
|
158
|
+
if (grants === "*") return true;
|
|
159
|
+
const pageGrant = grants[pageId];
|
|
160
|
+
if (!pageGrant) return false;
|
|
161
|
+
if (pageGrant === true) return true;
|
|
162
|
+
return pageGrant.includes(tabId);
|
|
163
|
+
}
|
|
164
|
+
function getAccessibleTabs(grants, pageId) {
|
|
165
|
+
if (grants === "*") return "all";
|
|
166
|
+
const pageGrant = grants[pageId];
|
|
167
|
+
if (!pageGrant) return [];
|
|
168
|
+
if (pageGrant === true) return "all";
|
|
169
|
+
return pageGrant;
|
|
170
|
+
}
|
|
171
|
+
export {
|
|
172
|
+
PAGE_REGISTRY,
|
|
173
|
+
getAccessibleTabs,
|
|
174
|
+
getAllPageIds,
|
|
175
|
+
getPageTabs,
|
|
176
|
+
hasPageAccess,
|
|
177
|
+
hasTabAccess
|
|
178
|
+
};
|