@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
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page & Tab Registry — Canonical map of all dashboard pages and their tabs.
|
|
3
|
+
* Used for access control, permission management, and frontend rendering.
|
|
4
|
+
*
|
|
5
|
+
* Structure:
|
|
6
|
+
* pageId → { label, section, tabs?: { tabId → label } }
|
|
7
|
+
*
|
|
8
|
+
* Pages without tabs just have the page-level permission.
|
|
9
|
+
* Pages with tabs support granular tab-level control.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export interface PageDef {
|
|
13
|
+
label: string;
|
|
14
|
+
section: 'overview' | 'management' | 'administration';
|
|
15
|
+
description?: string;
|
|
16
|
+
tabs?: Record<string, string>; // tabId → label
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const PAGE_REGISTRY: Record<string, PageDef> = {
|
|
20
|
+
// ─── Overview ────────────────────────────────────
|
|
21
|
+
dashboard: {
|
|
22
|
+
label: 'Dashboard',
|
|
23
|
+
section: 'overview',
|
|
24
|
+
description: 'Main dashboard with key metrics and activity feed',
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
// ─── Management ──────────────────────────────────
|
|
28
|
+
agents: {
|
|
29
|
+
label: 'Agents',
|
|
30
|
+
section: 'management',
|
|
31
|
+
description: 'View and manage AI agents',
|
|
32
|
+
tabs: {
|
|
33
|
+
overview: 'Overview',
|
|
34
|
+
personal: 'Personal Details',
|
|
35
|
+
email: 'Email',
|
|
36
|
+
whatsapp: 'WhatsApp',
|
|
37
|
+
channels: 'Channels',
|
|
38
|
+
configuration: 'Configuration',
|
|
39
|
+
manager: 'Manager',
|
|
40
|
+
tools: 'Tools',
|
|
41
|
+
skills: 'Skills',
|
|
42
|
+
permissions: 'Permissions',
|
|
43
|
+
activity: 'Activity',
|
|
44
|
+
communication: 'Communication',
|
|
45
|
+
workforce: 'Workforce',
|
|
46
|
+
memory: 'Memory',
|
|
47
|
+
guardrails: 'Guardrails',
|
|
48
|
+
autonomy: 'Autonomy',
|
|
49
|
+
budget: 'Budget',
|
|
50
|
+
security: 'Security',
|
|
51
|
+
'tool-security': 'Tool Security',
|
|
52
|
+
deployment: 'Deployment',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
skills: {
|
|
56
|
+
label: 'Skills',
|
|
57
|
+
section: 'management',
|
|
58
|
+
description: 'Manage agent skill packs',
|
|
59
|
+
},
|
|
60
|
+
'community-skills': {
|
|
61
|
+
label: 'Community Skills',
|
|
62
|
+
section: 'management',
|
|
63
|
+
description: 'Browse and install community skill marketplace',
|
|
64
|
+
},
|
|
65
|
+
'skill-connections': {
|
|
66
|
+
label: 'Integrations & MCP',
|
|
67
|
+
section: 'management',
|
|
68
|
+
description: 'MCP servers, built-in integrations, and community skills',
|
|
69
|
+
},
|
|
70
|
+
'database-access': {
|
|
71
|
+
label: 'Database Access',
|
|
72
|
+
section: 'management',
|
|
73
|
+
description: 'Manage database connections and agent access',
|
|
74
|
+
tabs: {
|
|
75
|
+
connections: 'Connections',
|
|
76
|
+
access: 'Agent Access',
|
|
77
|
+
audit: 'Audit Log',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
knowledge: {
|
|
81
|
+
label: 'Knowledge Bases',
|
|
82
|
+
section: 'management',
|
|
83
|
+
description: 'Manage knowledge base documents and collections',
|
|
84
|
+
},
|
|
85
|
+
'knowledge-contributions': {
|
|
86
|
+
label: 'Knowledge Hub',
|
|
87
|
+
section: 'management',
|
|
88
|
+
description: 'Agent contributions to shared knowledge',
|
|
89
|
+
},
|
|
90
|
+
approvals: {
|
|
91
|
+
label: 'Approvals',
|
|
92
|
+
section: 'management',
|
|
93
|
+
description: 'Review and approve pending agent actions',
|
|
94
|
+
},
|
|
95
|
+
'org-chart': {
|
|
96
|
+
label: 'Org Chart',
|
|
97
|
+
section: 'management',
|
|
98
|
+
description: 'Visual agent hierarchy and reporting structure',
|
|
99
|
+
},
|
|
100
|
+
'task-pipeline': {
|
|
101
|
+
label: 'Task Pipeline',
|
|
102
|
+
section: 'management',
|
|
103
|
+
description: 'Track task lifecycle from creation to completion',
|
|
104
|
+
},
|
|
105
|
+
workforce: {
|
|
106
|
+
label: 'Workforce',
|
|
107
|
+
section: 'management',
|
|
108
|
+
description: 'Agent scheduling, workload, and availability',
|
|
109
|
+
},
|
|
110
|
+
messages: {
|
|
111
|
+
label: 'Messages',
|
|
112
|
+
section: 'management',
|
|
113
|
+
description: 'Inter-agent and external message logs',
|
|
114
|
+
},
|
|
115
|
+
guardrails: {
|
|
116
|
+
label: 'Guardrails',
|
|
117
|
+
section: 'management',
|
|
118
|
+
description: 'Global safety policies and content filters',
|
|
119
|
+
},
|
|
120
|
+
journal: {
|
|
121
|
+
label: 'Journal',
|
|
122
|
+
section: 'management',
|
|
123
|
+
description: 'System event journal and decision log',
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
// ─── Administration ──────────────────────────────
|
|
127
|
+
dlp: {
|
|
128
|
+
label: 'DLP',
|
|
129
|
+
section: 'administration',
|
|
130
|
+
description: 'Data loss prevention policies and alerts',
|
|
131
|
+
},
|
|
132
|
+
compliance: {
|
|
133
|
+
label: 'Compliance',
|
|
134
|
+
section: 'administration',
|
|
135
|
+
description: 'Regulatory compliance settings and reports',
|
|
136
|
+
},
|
|
137
|
+
'domain-status': {
|
|
138
|
+
label: 'Domain',
|
|
139
|
+
section: 'administration',
|
|
140
|
+
description: 'Domain configuration and deployment status',
|
|
141
|
+
},
|
|
142
|
+
users: {
|
|
143
|
+
label: 'Users',
|
|
144
|
+
section: 'administration',
|
|
145
|
+
description: 'Manage dashboard users, roles, and permissions',
|
|
146
|
+
},
|
|
147
|
+
vault: {
|
|
148
|
+
label: 'Vault',
|
|
149
|
+
section: 'administration',
|
|
150
|
+
description: 'Encrypted credential storage',
|
|
151
|
+
},
|
|
152
|
+
audit: {
|
|
153
|
+
label: 'Audit Log',
|
|
154
|
+
section: 'administration',
|
|
155
|
+
description: 'Full audit trail of all system actions',
|
|
156
|
+
},
|
|
157
|
+
settings: {
|
|
158
|
+
label: 'Settings',
|
|
159
|
+
section: 'administration',
|
|
160
|
+
description: 'Global platform configuration and branding',
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/** Get all page IDs */
|
|
165
|
+
export function getAllPageIds(): string[] {
|
|
166
|
+
return Object.keys(PAGE_REGISTRY);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Get all tab IDs for a page (empty array if page has no tabs) */
|
|
170
|
+
export function getPageTabs(pageId: string): string[] {
|
|
171
|
+
const page = PAGE_REGISTRY[pageId];
|
|
172
|
+
return page?.tabs ? Object.keys(page.tabs) : [];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Permission grant structure stored per user.
|
|
177
|
+
* If pages is '*', user has access to everything (owner/admin default).
|
|
178
|
+
* Otherwise, it's a map of pageId → true (all tabs) | string[] (specific tabs).
|
|
179
|
+
*/
|
|
180
|
+
export type PermissionGrant = '*' | Record<string, true | string[]>;
|
|
181
|
+
|
|
182
|
+
/** Check if a user has access to a specific page */
|
|
183
|
+
export function hasPageAccess(grants: PermissionGrant, pageId: string): boolean {
|
|
184
|
+
if (grants === '*') return true;
|
|
185
|
+
return pageId in grants;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Check if a user has access to a specific tab within a page */
|
|
189
|
+
export function hasTabAccess(grants: PermissionGrant, pageId: string, tabId: string): boolean {
|
|
190
|
+
if (grants === '*') return true;
|
|
191
|
+
const pageGrant = grants[pageId];
|
|
192
|
+
if (!pageGrant) return false;
|
|
193
|
+
if (pageGrant === true) return true; // all tabs
|
|
194
|
+
return pageGrant.includes(tabId);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Get accessible tab IDs for a page */
|
|
198
|
+
export function getAccessibleTabs(grants: PermissionGrant, pageId: string): string[] | 'all' {
|
|
199
|
+
if (grants === '*') return 'all';
|
|
200
|
+
const pageGrant = grants[pageId];
|
|
201
|
+
if (!pageGrant) return [];
|
|
202
|
+
if (pageGrant === true) return 'all';
|
|
203
|
+
return pageGrant;
|
|
204
|
+
}
|
package/src/admin/routes.ts
CHANGED
|
@@ -549,6 +549,86 @@ export function createAdminRoutes(db: DatabaseAdapter) {
|
|
|
549
549
|
return c.json({ ok: true });
|
|
550
550
|
});
|
|
551
551
|
|
|
552
|
+
// ─── Page Registry (for permission UI) ──────────────
|
|
553
|
+
|
|
554
|
+
api.get('/page-registry', requireRole('admin'), async (c) => {
|
|
555
|
+
const { PAGE_REGISTRY } = await import('./page-registry.js');
|
|
556
|
+
return c.json(PAGE_REGISTRY);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
// ─── User Permissions ──────────────────────────────
|
|
560
|
+
|
|
561
|
+
api.get('/users/:id/permissions', requireRole('admin'), async (c) => {
|
|
562
|
+
const user = await db.getUser(c.req.param('id'));
|
|
563
|
+
if (!user) return c.json({ error: 'User not found' }, 404);
|
|
564
|
+
return c.json({ userId: c.req.param('id'), permissions: user.permissions ?? '*' });
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
api.put('/users/:id/permissions', requireRole('admin'), async (c) => {
|
|
568
|
+
const user = await db.getUser(c.req.param('id'));
|
|
569
|
+
if (!user) return c.json({ error: 'User not found' }, 404);
|
|
570
|
+
|
|
571
|
+
const body = await c.req.json();
|
|
572
|
+
const permissions = body.permissions;
|
|
573
|
+
|
|
574
|
+
// Validate: must be '*' or an object of pageId → true | string[]
|
|
575
|
+
if (permissions !== '*') {
|
|
576
|
+
if (typeof permissions !== 'object' || permissions === null || Array.isArray(permissions)) {
|
|
577
|
+
return c.json({ error: 'permissions must be "*" or an object mapping pageId to true or string[]' }, 400);
|
|
578
|
+
}
|
|
579
|
+
const { PAGE_REGISTRY } = await import('./page-registry.js');
|
|
580
|
+
for (const [pageId, grant] of Object.entries(permissions)) {
|
|
581
|
+
if (!(pageId in PAGE_REGISTRY)) {
|
|
582
|
+
return c.json({ error: `Unknown page: ${pageId}` }, 400);
|
|
583
|
+
}
|
|
584
|
+
if (grant !== true && !Array.isArray(grant)) {
|
|
585
|
+
return c.json({ error: `Permission for "${pageId}" must be true or string[]` }, 400);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const serialized = JSON.stringify(permissions);
|
|
591
|
+
try {
|
|
592
|
+
await (db as any).pool.query(
|
|
593
|
+
'UPDATE users SET permissions = $1, updated_at = NOW() WHERE id = $2',
|
|
594
|
+
[serialized, c.req.param('id')]
|
|
595
|
+
);
|
|
596
|
+
} catch {
|
|
597
|
+
// SQLite/other fallback
|
|
598
|
+
const edb = (db as any).db || (db as any).pool;
|
|
599
|
+
if (edb?.prepare) {
|
|
600
|
+
edb.prepare('UPDATE users SET permissions = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(serialized, c.req.param('id'));
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
await db.logEvent({
|
|
605
|
+
actor: c.get('userId') || 'system',
|
|
606
|
+
actorType: 'user',
|
|
607
|
+
action: 'user.permissions_updated',
|
|
608
|
+
resource: `user:${c.req.param('id')}`,
|
|
609
|
+
details: { permissions, targetEmail: user.email },
|
|
610
|
+
ip: c.req.header('x-forwarded-for')?.split(',')[0]?.trim(),
|
|
611
|
+
}).catch(() => {});
|
|
612
|
+
|
|
613
|
+
return c.json({ ok: true, permissions });
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
// ─── Current User Permissions (for frontend filtering) ──
|
|
617
|
+
|
|
618
|
+
api.get('/me/permissions', async (c) => {
|
|
619
|
+
const userId = c.get('userId' as any);
|
|
620
|
+
const userRole = c.get('userRole' as any);
|
|
621
|
+
if (!userId) return c.json({ error: 'Not authenticated' }, 401);
|
|
622
|
+
|
|
623
|
+
// Owner and admin always get full access
|
|
624
|
+
if (userRole === 'owner' || userRole === 'admin') {
|
|
625
|
+
return c.json({ permissions: '*', role: userRole });
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const user = await db.getUser(userId);
|
|
629
|
+
return c.json({ permissions: user?.permissions ?? '*', role: userRole });
|
|
630
|
+
});
|
|
631
|
+
|
|
552
632
|
// ─── Platform Capabilities ──────────────────────────
|
|
553
633
|
|
|
554
634
|
api.get('/platform-capabilities', requireRole('admin'), async (c) => {
|
package/src/dashboard/app.js
CHANGED
|
@@ -93,6 +93,7 @@ function App() {
|
|
|
93
93
|
const [toasts, setToasts] = useState([]);
|
|
94
94
|
const [user, setUser] = useState(null);
|
|
95
95
|
const [pendingCount, setPendingCount] = useState(0);
|
|
96
|
+
const [permissions, setPermissions] = useState('*'); // '*' = full access, or { pageId: true | ['tab1','tab2'] }
|
|
96
97
|
const [needsSetup, setNeedsSetup] = useState(null);
|
|
97
98
|
const [sidebarPinned, setSidebarPinned] = useState(() => localStorage.getItem('em_sidebar_pinned') === 'true');
|
|
98
99
|
const [sidebarHovered, setSidebarHovered] = useState(false);
|
|
@@ -136,6 +137,7 @@ function App() {
|
|
|
136
137
|
if (!authed) return;
|
|
137
138
|
engineCall('/approvals/pending').then(d => setPendingCount((d.requests || []).length)).catch(() => {});
|
|
138
139
|
apiCall('/settings').then(d => { const s = d.settings || d || {}; if (s.primaryColor) applyBrandColor(s.primaryColor); if (s.orgId) setOrgId(s.orgId); }).catch(() => {});
|
|
140
|
+
apiCall('/me/permissions').then(d => { if (d && d.permissions) setPermissions(d.permissions); }).catch(() => {});
|
|
139
141
|
}, [authed]);
|
|
140
142
|
|
|
141
143
|
const logout = useCallback(() => { authCall('/logout', { method: 'POST' }).catch(() => {}).finally(() => { setAuthed(false); setUser(null); }); }, []);
|
|
@@ -208,10 +210,20 @@ function App() {
|
|
|
208
210
|
};
|
|
209
211
|
|
|
210
212
|
const navigateToAgent = (agentId) => { _setSelectedAgentId(agentId); history.pushState(null, '', '/dashboard/agents/' + agentId); };
|
|
211
|
-
|
|
213
|
+
|
|
214
|
+
// Filter nav based on permissions
|
|
215
|
+
const hasAccess = (pageId) => permissions === '*' || (permissions && pageId in permissions);
|
|
216
|
+
const filteredNav = nav.map(section => ({
|
|
217
|
+
...section,
|
|
218
|
+
items: section.items.filter(item => hasAccess(item.id))
|
|
219
|
+
})).filter(section => section.items.length > 0);
|
|
220
|
+
|
|
221
|
+
// Block access to pages user can't see — show unauthorized page
|
|
222
|
+
const canAccessPage = hasAccess(page);
|
|
223
|
+
const PageComponent = canAccessPage ? (pages[page] || DashboardPage) : null;
|
|
212
224
|
const sidebarClass = 'sidebar' + (sidebarPinned ? ' expanded' : sidebarHovered ? ' hover-expanded' : '') + (mobileMenuOpen ? ' mobile-open' : '');
|
|
213
225
|
|
|
214
|
-
return h(AppContext.Provider, { value: { toast, toasts, user, theme, setPage } },
|
|
226
|
+
return h(AppContext.Provider, { value: { toast, toasts, user, theme, setPage, permissions } },
|
|
215
227
|
h('div', { className: 'app-layout' },
|
|
216
228
|
// Mobile hamburger
|
|
217
229
|
h('button', { className: 'mobile-hamburger', onClick: () => setMobileMenuOpen(true) },
|
|
@@ -231,7 +243,7 @@ function App() {
|
|
|
231
243
|
h('button', { className: 'sidebar-toggle' + (sidebarPinned ? ' pinned' : ''), onClick: toggleSidebarPin, title: sidebarPinned ? 'Unpin sidebar' : 'Pin sidebar' }, sidebarPinned ? I.chevronLeft() : I.panelLeft())
|
|
232
244
|
),
|
|
233
245
|
h('div', { className: 'sidebar-nav' },
|
|
234
|
-
|
|
246
|
+
filteredNav.map((section, si) =>
|
|
235
247
|
h('div', { key: section.section + si, className: 'sidebar-section' },
|
|
236
248
|
h('div', { className: 'sidebar-section-title' }, section.section),
|
|
237
249
|
section.items.map(item =>
|
|
@@ -271,7 +283,29 @@ function App() {
|
|
|
271
283
|
? h(AgentDetailPage, { agentId: selectedAgentId, onBack: () => { _setSelectedAgentId(null); _setPage('agents'); history.pushState(null, '', '/dashboard/agents'); } })
|
|
272
284
|
: page === 'agents'
|
|
273
285
|
? h(AgentsPage, { onSelectAgent: navigateToAgent })
|
|
274
|
-
: h(PageComponent)
|
|
286
|
+
: PageComponent ? h(PageComponent)
|
|
287
|
+
: h('div', { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', minHeight: '60vh', textAlign: 'center', padding: 40 } },
|
|
288
|
+
h('div', { style: { width: 64, height: 64, borderRadius: '50%', background: 'var(--danger-soft, rgba(220,38,38,0.1))', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 20 } },
|
|
289
|
+
h('svg', { width: 32, height: 32, viewBox: '0 0 24 24', fill: 'none', stroke: 'var(--danger, #dc2626)', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' },
|
|
290
|
+
h('rect', { x: 3, y: 11, width: 18, height: 11, rx: 2, ry: 2 }),
|
|
291
|
+
h('path', { d: 'M7 11V7a5 5 0 0 1 10 0v4' })
|
|
292
|
+
)
|
|
293
|
+
),
|
|
294
|
+
h('h2', { style: { fontSize: 20, fontWeight: 700, marginBottom: 8, color: 'var(--text-primary)' } }, 'Access Restricted'),
|
|
295
|
+
h('p', { style: { fontSize: 14, color: 'var(--text-muted)', maxWidth: 400, lineHeight: 1.6, marginBottom: 24 } },
|
|
296
|
+
'You don\'t have permission to access this page. If you believe this is an error, please contact your company administrator to request access.'
|
|
297
|
+
),
|
|
298
|
+
h('div', { style: { display: 'flex', gap: 12 } },
|
|
299
|
+
filteredNav[0]?.items[0] && h('button', {
|
|
300
|
+
className: 'btn btn-primary',
|
|
301
|
+
onClick: () => { setPage(filteredNav[0].items[0].id); history.pushState(null, '', '/dashboard/' + filteredNav[0].items[0].id); }
|
|
302
|
+
}, 'Go to ' + filteredNav[0].items[0].label),
|
|
303
|
+
h('button', {
|
|
304
|
+
className: 'btn btn-secondary',
|
|
305
|
+
onClick: () => { window.location.href = 'mailto:?subject=Access%20Request&body=I%20need%20access%20to%20the%20' + encodeURIComponent(page) + '%20page%20in%20the%20dashboard.'; }
|
|
306
|
+
}, 'Request Access')
|
|
307
|
+
)
|
|
308
|
+
)
|
|
275
309
|
)
|
|
276
310
|
)
|
|
277
311
|
),
|
|
@@ -54,5 +54,7 @@ export const I = {
|
|
|
54
54
|
eyeOff: () => h('svg', S, h('path', { d: 'M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24' }), h('line', { x1: 1, y1: 1, x2: 23, y2: 23 })),
|
|
55
55
|
panelLeft: () => h('svg', S, h('rect', { x: 3, y: 3, width: 18, height: 18, rx: 2 }), h('line', { x1: 9, y1: 3, x2: 9, y2: 21 })),
|
|
56
56
|
chevronLeft: () => h('svg', S, h('polyline', { points: '15 18 9 12 15 6' })),
|
|
57
|
+
chevronRight: () => h('svg', S, h('polyline', { points: '9 18 15 12 9 6' })),
|
|
58
|
+
chevronDown: () => h('svg', S, h('polyline', { points: '6 9 12 15 18 9' })),
|
|
57
59
|
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' })),
|
|
58
60
|
};
|
|
@@ -44,9 +44,19 @@ export function AgentDetailPage(props) {
|
|
|
44
44
|
var _agents = useState([]);
|
|
45
45
|
var agents = _agents[0]; var setAgents = _agents[1];
|
|
46
46
|
|
|
47
|
-
var
|
|
47
|
+
var ALL_TABS = ['overview', 'personal', 'email', 'whatsapp', 'channels', 'configuration', 'manager', 'tools', 'skills', 'permissions', 'activity', 'communication', 'workforce', 'memory', 'guardrails', 'autonomy', 'budget', 'security', 'tool-security', 'deployment'];
|
|
48
48
|
var TAB_LABELS = { 'security': 'Security', 'tool-security': 'Tool Security', 'manager': 'Manager', 'email': 'Email', 'whatsapp': 'WhatsApp', 'channels': 'Channels', 'tools': 'Tools', 'autonomy': 'Autonomy' };
|
|
49
49
|
|
|
50
|
+
// Filter tabs based on user permissions
|
|
51
|
+
var app = useApp();
|
|
52
|
+
var perms = app.permissions || '*';
|
|
53
|
+
var agentGrant = perms === '*' ? true : (perms.agents || false);
|
|
54
|
+
var TABS = ALL_TABS.filter(function(t) {
|
|
55
|
+
if (perms === '*' || agentGrant === true) return true;
|
|
56
|
+
if (Array.isArray(agentGrant)) return agentGrant.indexOf(t) !== -1;
|
|
57
|
+
return false;
|
|
58
|
+
});
|
|
59
|
+
|
|
50
60
|
var load = function() {
|
|
51
61
|
setLoading(true);
|
|
52
62
|
Promise.all([
|