aimeat 1.6.1 → 1.7.0
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/locales/en.json +212 -6
- package/dist/locales/fi.json +212 -6
- package/dist/public/css/views/agents-detail.css +563 -0
- package/dist/public/css/views/foundry.css +1 -0
- package/dist/public/css/views/marketplace.css +1 -0
- package/dist/public/css/views/profile.css +2 -0
- package/dist/public/js/services/admin.js +13 -0
- package/dist/public/js/services/agent-activity.js +20 -0
- package/dist/public/js/services/agent-capabilities.js +15 -0
- package/dist/public/js/services/agent-directives.js +28 -0
- package/dist/public/js/services/agent-messages.js +34 -0
- package/dist/public/js/services/agent-services.js +17 -0
- package/dist/public/js/services/agent-tasks.js +46 -0
- package/dist/public/js/services/memory.js +6 -3
- package/dist/public/js/services/sharing-groups.js +51 -0
- package/dist/public/spa.html +21 -2
- package/dist/public/views/admin/agent-tasks-tab.js +156 -0
- package/dist/public/views/admin/federation-tab.js +2 -2
- package/dist/public/views/admin/memory-tab.js +1 -0
- package/dist/public/views/admin/overview-tab.js +25 -0
- package/dist/public/views/admin/sharing-groups-tab.js +86 -0
- package/dist/public/views/admin.js +4 -0
- package/dist/public/views/profile/access-tab.js +510 -5
- package/dist/public/views/profile/agents-activity-subtab.js +242 -0
- package/dist/public/views/profile/agents-capabilities-subtab.js +165 -0
- package/dist/public/views/profile/agents-directives-subtab.js +292 -0
- package/dist/public/views/profile/agents-messages-subtab.js +222 -0
- package/dist/public/views/profile/agents-services-subtab.js +111 -0
- package/dist/public/views/profile/agents-tab.js +94 -47
- package/dist/public/views/profile/agents-tasks-subtab.js +254 -0
- package/dist/public/views/profile/knowledge-tab.js +5 -5
- package/dist/public/views/profile/memory-tab.js +90 -25
- package/dist/src/config.d.ts +5 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +9 -2
- package/dist/src/config.js.map +1 -1
- package/dist/src/mcp/agent-capabilities.d.ts +18 -0
- package/dist/src/mcp/agent-capabilities.d.ts.map +1 -0
- package/dist/src/mcp/agent-capabilities.js +85 -0
- package/dist/src/mcp/agent-capabilities.js.map +1 -0
- package/dist/src/mcp/agent-messages.d.ts +15 -0
- package/dist/src/mcp/agent-messages.d.ts.map +1 -0
- package/dist/src/mcp/agent-messages.js +81 -0
- package/dist/src/mcp/agent-messages.js.map +1 -0
- package/dist/src/mcp/agent-tasks.d.ts +16 -0
- package/dist/src/mcp/agent-tasks.d.ts.map +1 -0
- package/dist/src/mcp/agent-tasks.js +350 -0
- package/dist/src/mcp/agent-tasks.js.map +1 -0
- package/dist/src/mcp/core.d.ts.map +1 -1
- package/dist/src/mcp/core.js +8 -4
- package/dist/src/mcp/core.js.map +1 -1
- package/dist/src/mcp/index.d.ts.map +1 -1
- package/dist/src/mcp/index.js +8 -0
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/mcp/memory-extended.js +1 -1
- package/dist/src/mcp/memory-extended.js.map +1 -1
- package/dist/src/mcp/sharing-groups.d.ts +16 -0
- package/dist/src/mcp/sharing-groups.d.ts.map +1 -0
- package/dist/src/mcp/sharing-groups.js +240 -0
- package/dist/src/mcp/sharing-groups.js.map +1 -0
- package/dist/src/models/agent-capabilities-schemas.d.ts +20 -0
- package/dist/src/models/agent-capabilities-schemas.d.ts.map +1 -0
- package/dist/src/models/agent-capabilities-schemas.js +16 -0
- package/dist/src/models/agent-capabilities-schemas.js.map +1 -0
- package/dist/src/models/agent-directives-schemas.d.ts +44 -0
- package/dist/src/models/agent-directives-schemas.d.ts.map +1 -0
- package/dist/src/models/agent-directives-schemas.js +41 -0
- package/dist/src/models/agent-directives-schemas.js.map +1 -0
- package/dist/src/models/agent-message-schemas.d.ts +32 -0
- package/dist/src/models/agent-message-schemas.d.ts.map +1 -0
- package/dist/src/models/agent-message-schemas.js +25 -0
- package/dist/src/models/agent-message-schemas.js.map +1 -0
- package/dist/src/models/agent-task-schemas.d.ts +133 -0
- package/dist/src/models/agent-task-schemas.d.ts.map +1 -0
- package/dist/src/models/agent-task-schemas.js +84 -0
- package/dist/src/models/agent-task-schemas.js.map +1 -0
- package/dist/src/models/schemas.d.ts +5 -0
- package/dist/src/models/schemas.d.ts.map +1 -1
- package/dist/src/models/schemas.js +5 -3
- package/dist/src/models/schemas.js.map +1 -1
- package/dist/src/models/sharing-group-schemas.d.ts +52 -0
- package/dist/src/models/sharing-group-schemas.d.ts.map +1 -0
- package/dist/src/models/sharing-group-schemas.js +46 -0
- package/dist/src/models/sharing-group-schemas.js.map +1 -0
- package/dist/src/routes/admin-agent-tasks.d.ts +12 -0
- package/dist/src/routes/admin-agent-tasks.d.ts.map +1 -0
- package/dist/src/routes/admin-agent-tasks.js +62 -0
- package/dist/src/routes/admin-agent-tasks.js.map +1 -0
- package/dist/src/routes/admin-monitoring.d.ts +2 -1
- package/dist/src/routes/admin-monitoring.d.ts.map +1 -1
- package/dist/src/routes/admin-monitoring.js +75 -2
- package/dist/src/routes/admin-monitoring.js.map +1 -1
- package/dist/src/routes/admin-sharing-groups.d.ts +12 -0
- package/dist/src/routes/admin-sharing-groups.d.ts.map +1 -0
- package/dist/src/routes/admin-sharing-groups.js +37 -0
- package/dist/src/routes/admin-sharing-groups.js.map +1 -0
- package/dist/src/routes/admin.d.ts +1 -1
- package/dist/src/routes/admin.d.ts.map +1 -1
- package/dist/src/routes/admin.js +2 -2
- package/dist/src/routes/admin.js.map +1 -1
- package/dist/src/routes/agent-activity.d.ts +17 -0
- package/dist/src/routes/agent-activity.d.ts.map +1 -0
- package/dist/src/routes/agent-activity.js +130 -0
- package/dist/src/routes/agent-activity.js.map +1 -0
- package/dist/src/routes/agent-capabilities.d.ts +17 -0
- package/dist/src/routes/agent-capabilities.d.ts.map +1 -0
- package/dist/src/routes/agent-capabilities.js +106 -0
- package/dist/src/routes/agent-capabilities.js.map +1 -0
- package/dist/src/routes/agent-directives.d.ts +17 -0
- package/dist/src/routes/agent-directives.d.ts.map +1 -0
- package/dist/src/routes/agent-directives.js +206 -0
- package/dist/src/routes/agent-directives.js.map +1 -0
- package/dist/src/routes/agent-integration.d.ts +17 -0
- package/dist/src/routes/agent-integration.d.ts.map +1 -0
- package/dist/src/routes/agent-integration.js +169 -0
- package/dist/src/routes/agent-integration.js.map +1 -0
- package/dist/src/routes/agent-messages.d.ts +17 -0
- package/dist/src/routes/agent-messages.d.ts.map +1 -0
- package/dist/src/routes/agent-messages.js +168 -0
- package/dist/src/routes/agent-messages.js.map +1 -0
- package/dist/src/routes/agent-tasks.d.ts +24 -0
- package/dist/src/routes/agent-tasks.d.ts.map +1 -0
- package/dist/src/routes/agent-tasks.js +425 -0
- package/dist/src/routes/agent-tasks.js.map +1 -0
- package/dist/src/routes/capabilities.js +4 -4
- package/dist/src/routes/capabilities.js.map +1 -1
- package/dist/src/routes/cortex.d.ts.map +1 -1
- package/dist/src/routes/cortex.js +1 -2
- package/dist/src/routes/cortex.js.map +1 -1
- package/dist/src/routes/extensions.d.ts.map +1 -1
- package/dist/src/routes/extensions.js +1 -2
- package/dist/src/routes/extensions.js.map +1 -1
- package/dist/src/routes/generator.d.ts.map +1 -1
- package/dist/src/routes/generator.js +33 -5
- package/dist/src/routes/generator.js.map +1 -1
- package/dist/src/routes/knowledge.d.ts.map +1 -1
- package/dist/src/routes/knowledge.js +4 -3
- package/dist/src/routes/knowledge.js.map +1 -1
- package/dist/src/routes/memory.d.ts.map +1 -1
- package/dist/src/routes/memory.js +8 -4
- package/dist/src/routes/memory.js.map +1 -1
- package/dist/src/routes/permissions.js +1 -1
- package/dist/src/routes/permissions.js.map +1 -1
- package/dist/src/routes/prompts.d.ts +9 -0
- package/dist/src/routes/prompts.d.ts.map +1 -1
- package/dist/src/routes/prompts.js +57 -1
- package/dist/src/routes/prompts.js.map +1 -1
- package/dist/src/routes/sharing-groups.d.ts +20 -0
- package/dist/src/routes/sharing-groups.d.ts.map +1 -0
- package/dist/src/routes/sharing-groups.js +297 -0
- package/dist/src/routes/sharing-groups.js.map +1 -0
- package/dist/src/routes/stats.d.ts +1 -0
- package/dist/src/routes/stats.d.ts.map +1 -1
- package/dist/src/routes/stats.js +26 -1
- package/dist/src/routes/stats.js.map +1 -1
- package/dist/src/routes/storage-files.d.ts.map +1 -1
- package/dist/src/routes/storage-files.js +4 -1
- package/dist/src/routes/storage-files.js.map +1 -1
- package/dist/src/routes/work.d.ts.map +1 -1
- package/dist/src/routes/work.js +3 -0
- package/dist/src/routes/work.js.map +1 -1
- package/dist/src/schemas/knowledge-package.js +1 -1
- package/dist/src/schemas/knowledge-package.js.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +21 -1
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
- package/dist/src/services/activity-recorder.d.ts +16 -0
- package/dist/src/services/activity-recorder.d.ts.map +1 -0
- package/dist/src/services/activity-recorder.js +59 -0
- package/dist/src/services/activity-recorder.js.map +1 -0
- package/dist/src/services/config-schema.d.ts.map +1 -1
- package/dist/src/services/config-schema.js +5 -0
- package/dist/src/services/config-schema.js.map +1 -1
- package/dist/src/services/consent.d.ts +1 -1
- package/dist/src/services/consent.d.ts.map +1 -1
- package/dist/src/services/consent.js +28 -1
- package/dist/src/services/consent.js.map +1 -1
- package/dist/src/services/core-jobs.d.ts.map +1 -1
- package/dist/src/services/core-jobs.js +5 -0
- package/dist/src/services/core-jobs.js.map +1 -1
- package/dist/src/services/job-seeding.d.ts.map +1 -1
- package/dist/src/services/job-seeding.js +2 -0
- package/dist/src/services/job-seeding.js.map +1 -1
- package/dist/src/services/stats.d.ts +1 -0
- package/dist/src/services/stats.d.ts.map +1 -1
- package/dist/src/services/stats.js +2 -1
- package/dist/src/services/stats.js.map +1 -1
- package/dist/src/services/task-stall-detector.d.ts +10 -0
- package/dist/src/services/task-stall-detector.d.ts.map +1 -0
- package/dist/src/services/task-stall-detector.js +38 -0
- package/dist/src/services/task-stall-detector.js.map +1 -0
- package/dist/src/services/work-task-bridge.d.ts +10 -0
- package/dist/src/services/work-task-bridge.d.ts.map +1 -0
- package/dist/src/services/work-task-bridge.js +53 -0
- package/dist/src/services/work-task-bridge.js.map +1 -0
- package/dist/src/storage/interface.d.ts +170 -5
- package/dist/src/storage/interface.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.d.ts +95 -2
- package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.js +657 -6
- package/dist/src/storage/providers/mongodb/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/index.d.ts +73 -1
- package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/index.js +131 -3
- package/dist/src/storage/providers/sqlite/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/repos/agent-activity.d.ts +14 -0
- package/dist/src/storage/providers/sqlite/repos/agent-activity.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/agent-activity.js +44 -0
- package/dist/src/storage/providers/sqlite/repos/agent-activity.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/agent-directives.d.ts +14 -0
- package/dist/src/storage/providers/sqlite/repos/agent-directives.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/agent-directives.js +65 -0
- package/dist/src/storage/providers/sqlite/repos/agent-directives.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/agent-message.d.ts +28 -0
- package/dist/src/storage/providers/sqlite/repos/agent-message.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/agent-message.js +93 -0
- package/dist/src/storage/providers/sqlite/repos/agent-message.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/agent-task.d.ts +45 -0
- package/dist/src/storage/providers/sqlite/repos/agent-task.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/agent-task.js +155 -0
- package/dist/src/storage/providers/sqlite/repos/agent-task.js.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/sharing-group.d.ts +16 -0
- package/dist/src/storage/providers/sqlite/repos/sharing-group.d.ts.map +1 -0
- package/dist/src/storage/providers/sqlite/repos/sharing-group.js +68 -0
- package/dist/src/storage/providers/sqlite/repos/sharing-group.js.map +1 -0
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.js +102 -0
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/dist/src/storage/repositories/agent-activity.repository.d.ts +15 -0
- package/dist/src/storage/repositories/agent-activity.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/agent-activity.repository.js +8 -0
- package/dist/src/storage/repositories/agent-activity.repository.js.map +1 -0
- package/dist/src/storage/repositories/agent-directives.repository.d.ts +15 -0
- package/dist/src/storage/repositories/agent-directives.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/agent-directives.repository.js +8 -0
- package/dist/src/storage/repositories/agent-directives.repository.js.map +1 -0
- package/dist/src/storage/repositories/agent-message.repository.d.ts +29 -0
- package/dist/src/storage/repositories/agent-message.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/agent-message.repository.js +8 -0
- package/dist/src/storage/repositories/agent-message.repository.js.map +1 -0
- package/dist/src/storage/repositories/agent-task.repository.d.ts +46 -0
- package/dist/src/storage/repositories/agent-task.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/agent-task.repository.js +8 -0
- package/dist/src/storage/repositories/agent-task.repository.js.map +1 -0
- package/dist/src/storage/repositories/index.d.ts +5 -0
- package/dist/src/storage/repositories/index.d.ts.map +1 -1
- package/dist/src/storage/repositories/sharing-group.repository.d.ts +17 -0
- package/dist/src/storage/repositories/sharing-group.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/sharing-group.repository.js +8 -0
- package/dist/src/storage/repositories/sharing-group.repository.js.map +1 -0
- package/package.json +1 -1
- package/prisma/schema.prisma +102 -0
|
@@ -1,20 +1,522 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file access-tab.js
|
|
3
3
|
* @description Profile tab displaying session info, public key, owner key,
|
|
4
|
-
* MCP endpoint details,
|
|
4
|
+
* MCP endpoint details, federation access management, sharing groups,
|
|
5
|
+
* and owner-level agent defaults.
|
|
6
|
+
* @structure
|
|
7
|
+
* - AccessTab (default export) -- main tab component
|
|
8
|
+
* - SharingGroupsSection -- CRUD for sharing groups with expandable member lists
|
|
9
|
+
* - AgentDefaultsSection -- owner-level default rules and token budget for agents
|
|
5
10
|
* @version-history
|
|
6
|
-
* v1.0.0
|
|
7
|
-
* v1.1.0
|
|
8
|
-
* v1.2.0
|
|
11
|
+
* v1.0.0 -- 2026-03-16 -- Initial access tab
|
|
12
|
+
* v1.1.0 -- 2026-03-17 -- Replace inline styles with CSS classes
|
|
13
|
+
* v1.2.0 -- 2026-05-21 -- Add Federation Access section for auth consent management
|
|
14
|
+
* v1.3.0 -- 2026-05-21 -- Add Sharing Groups and Agent Defaults sections
|
|
9
15
|
*/
|
|
10
16
|
import { h } from 'preact';
|
|
11
|
-
import { useState, useEffect } from 'preact/hooks';
|
|
17
|
+
import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
|
|
12
18
|
import htm from 'htm';
|
|
13
19
|
const html = htm.bind(h);
|
|
14
20
|
import { t } from '/js/i18n.js';
|
|
15
21
|
import { escHtml, copyToClipboard } from '/js/utils.js';
|
|
16
22
|
import { getNodeUrl } from '/js/services/auth.js';
|
|
17
23
|
import { apiGet, apiPost, apiDelete } from '/js/api.js';
|
|
24
|
+
import { useConfirm } from '/components/Modal.js';
|
|
25
|
+
import * as groupsApi from '/js/services/sharing-groups.js';
|
|
26
|
+
import { getOwnerDefaults, upsertOwnerDefaults } from '/js/services/agent-directives.js';
|
|
27
|
+
|
|
28
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
29
|
+
Sharing Groups Section
|
|
30
|
+
═══════════════════════════════════════════════════════════════════ */
|
|
31
|
+
|
|
32
|
+
function SharingGroupsSection({ showToast }) {
|
|
33
|
+
const { confirm, ConfirmUI } = useConfirm();
|
|
34
|
+
const [groups, setGroups] = useState(null);
|
|
35
|
+
const [expandedId, setExpandedId] = useState(null);
|
|
36
|
+
const [showCreate, setShowCreate] = useState(false);
|
|
37
|
+
const [creating, setCreating] = useState(false);
|
|
38
|
+
|
|
39
|
+
// Create form
|
|
40
|
+
const [formName, setFormName] = useState('');
|
|
41
|
+
const [formDesc, setFormDesc] = useState('');
|
|
42
|
+
|
|
43
|
+
// Add-member form (per-group)
|
|
44
|
+
const [addingTo, setAddingTo] = useState(null);
|
|
45
|
+
const [memberIdent, setMemberIdent] = useState('');
|
|
46
|
+
const [memberType, setMemberType] = useState('ghii');
|
|
47
|
+
const [memberRead, setMemberRead] = useState(true);
|
|
48
|
+
const [memberWrite, setMemberWrite] = useState(false);
|
|
49
|
+
|
|
50
|
+
// Edit group (inline)
|
|
51
|
+
const [editingId, setEditingId] = useState(null);
|
|
52
|
+
const [editName, setEditName] = useState('');
|
|
53
|
+
const [editDesc, setEditDesc] = useState('');
|
|
54
|
+
const [saving, setSaving] = useState(false);
|
|
55
|
+
|
|
56
|
+
const loadGroups = useCallback(async () => {
|
|
57
|
+
try {
|
|
58
|
+
const resp = await groupsApi.listGroups();
|
|
59
|
+
setGroups(resp?.data?.groups || []);
|
|
60
|
+
} catch {
|
|
61
|
+
setGroups([]);
|
|
62
|
+
}
|
|
63
|
+
}, []);
|
|
64
|
+
|
|
65
|
+
useEffect(() => { loadGroups(); }, [loadGroups]);
|
|
66
|
+
|
|
67
|
+
// Live update listener
|
|
68
|
+
const liveRef = useRef(loadGroups);
|
|
69
|
+
liveRef.current = loadGroups;
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const handler = () => liveRef.current();
|
|
72
|
+
window.addEventListener('aimeat-live-update', handler);
|
|
73
|
+
return () => window.removeEventListener('aimeat-live-update', handler);
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
const handleCreate = useCallback(async () => {
|
|
77
|
+
if (!formName.trim()) {
|
|
78
|
+
showToast(t('profile.access.sgNameRequired') || 'Group name is required');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
setCreating(true);
|
|
82
|
+
try {
|
|
83
|
+
await groupsApi.createGroup({
|
|
84
|
+
name: formName.trim(),
|
|
85
|
+
description: formDesc.trim() || undefined,
|
|
86
|
+
members: [],
|
|
87
|
+
default_permissions: { read: true, write: false },
|
|
88
|
+
});
|
|
89
|
+
showToast(t('profile.access.sgCreated') || 'Sharing group created');
|
|
90
|
+
setShowCreate(false);
|
|
91
|
+
setFormName('');
|
|
92
|
+
setFormDesc('');
|
|
93
|
+
loadGroups();
|
|
94
|
+
} catch (e) {
|
|
95
|
+
showToast(e.message || (t('profile.access.sgCreateError') || 'Failed to create group'));
|
|
96
|
+
} finally {
|
|
97
|
+
setCreating(false);
|
|
98
|
+
}
|
|
99
|
+
}, [formName, formDesc, showToast, loadGroups]);
|
|
100
|
+
|
|
101
|
+
const handleDelete = useCallback((id, name) => {
|
|
102
|
+
confirm(
|
|
103
|
+
(t('profile.access.sgConfirmDelete') || 'Delete group "{name}"? This cannot be undone.').replace('{name}', name),
|
|
104
|
+
async () => {
|
|
105
|
+
try {
|
|
106
|
+
await groupsApi.deleteGroup(id);
|
|
107
|
+
showToast(t('profile.access.sgDeleted') || 'Sharing group deleted');
|
|
108
|
+
if (expandedId === id) setExpandedId(null);
|
|
109
|
+
loadGroups();
|
|
110
|
+
} catch (e) {
|
|
111
|
+
showToast(e.message || (t('profile.access.sgDeleteError') || 'Failed to delete group'));
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{ danger: true },
|
|
115
|
+
);
|
|
116
|
+
}, [confirm, showToast, loadGroups, expandedId]);
|
|
117
|
+
|
|
118
|
+
const startEdit = useCallback((group) => {
|
|
119
|
+
setEditingId(group.id);
|
|
120
|
+
setEditName(group.name);
|
|
121
|
+
setEditDesc(group.description || '');
|
|
122
|
+
}, []);
|
|
123
|
+
|
|
124
|
+
const handleUpdate = useCallback(async (id) => {
|
|
125
|
+
if (!editName.trim()) {
|
|
126
|
+
showToast(t('profile.access.sgNameRequired') || 'Group name is required');
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
setSaving(true);
|
|
130
|
+
try {
|
|
131
|
+
await groupsApi.updateGroup(id, {
|
|
132
|
+
name: editName.trim(),
|
|
133
|
+
description: editDesc.trim() || undefined,
|
|
134
|
+
});
|
|
135
|
+
showToast(t('profile.access.sgUpdated') || 'Group updated');
|
|
136
|
+
setEditingId(null);
|
|
137
|
+
loadGroups();
|
|
138
|
+
} catch (e) {
|
|
139
|
+
showToast(e.message || (t('profile.access.sgUpdateError') || 'Failed to update group'));
|
|
140
|
+
} finally {
|
|
141
|
+
setSaving(false);
|
|
142
|
+
}
|
|
143
|
+
}, [editName, editDesc, showToast, loadGroups]);
|
|
144
|
+
|
|
145
|
+
const handleAddMember = useCallback(async (groupId) => {
|
|
146
|
+
if (!memberIdent.trim()) {
|
|
147
|
+
showToast(t('profile.access.sgMemberRequired') || 'Identifier is required');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
await groupsApi.addMember(groupId, {
|
|
152
|
+
identifier: memberIdent.trim(),
|
|
153
|
+
identifier_type: memberType,
|
|
154
|
+
permissions: { read: memberRead, write: memberWrite },
|
|
155
|
+
});
|
|
156
|
+
showToast(t('profile.access.sgMemberAdded') || 'Member added');
|
|
157
|
+
setMemberIdent('');
|
|
158
|
+
setAddingTo(null);
|
|
159
|
+
loadGroups();
|
|
160
|
+
} catch (e) {
|
|
161
|
+
showToast(e.message || (t('profile.access.sgMemberAddError') || 'Failed to add member'));
|
|
162
|
+
}
|
|
163
|
+
}, [memberIdent, memberType, memberRead, memberWrite, showToast, loadGroups]);
|
|
164
|
+
|
|
165
|
+
const handleRemoveMember = useCallback((groupId, identifier) => {
|
|
166
|
+
confirm(
|
|
167
|
+
(t('profile.access.sgConfirmRemoveMember') || 'Remove "{name}" from this group?').replace('{name}', identifier),
|
|
168
|
+
async () => {
|
|
169
|
+
try {
|
|
170
|
+
await groupsApi.removeMember(groupId, identifier);
|
|
171
|
+
showToast(t('profile.access.sgMemberRemoved') || 'Member removed');
|
|
172
|
+
loadGroups();
|
|
173
|
+
} catch (e) {
|
|
174
|
+
showToast(e.message || (t('profile.access.sgMemberRemoveError') || 'Failed to remove member'));
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
{ danger: true },
|
|
178
|
+
);
|
|
179
|
+
}, [confirm, showToast, loadGroups]);
|
|
180
|
+
|
|
181
|
+
const renderMemberRow = (groupId, member) => html`
|
|
182
|
+
<div class="mem-item" key=${member.identifier}>
|
|
183
|
+
<span class="mem-key">${escHtml(member.identifier)}</span>
|
|
184
|
+
<span class="badge ${member.identifierType === 'gaii' ? 'badge-info' : 'badge-muted'}">${member.identifierType}</span>
|
|
185
|
+
<span class="badge ${member.permissions?.read ? 'badge-success' : 'badge-muted'}">
|
|
186
|
+
${t('profile.access.sgRead') || 'read'}
|
|
187
|
+
</span>
|
|
188
|
+
<span class="badge ${member.permissions?.write ? 'badge-success' : 'badge-muted'}">
|
|
189
|
+
${t('profile.access.sgWrite') || 'write'}
|
|
190
|
+
</span>
|
|
191
|
+
<button class="btn-ghost btn-danger btn-sm" onClick=${(e) => { e.stopPropagation(); handleRemoveMember(groupId, member.identifier); }}>
|
|
192
|
+
${t('profile.access.sgRemove') || 'Remove'}
|
|
193
|
+
</button>
|
|
194
|
+
</div>
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
const renderGroupCard = (group) => {
|
|
198
|
+
const isExpanded = expandedId === group.id;
|
|
199
|
+
const isEditing = editingId === group.id;
|
|
200
|
+
const memberCount = (group.members || []).length;
|
|
201
|
+
|
|
202
|
+
return html`
|
|
203
|
+
<div class="card ${isExpanded ? 'card-expanded' : ''}" key=${group.id}>
|
|
204
|
+
<div class="card-header card-clickable" onClick=${() => setExpandedId(isExpanded ? null : group.id)}>
|
|
205
|
+
<span class="expand-icon">${isExpanded ? '▼' : '▶'}</span>
|
|
206
|
+
<div class="card-title">${escHtml(group.name)}</div>
|
|
207
|
+
<span class="badge badge-muted">${memberCount} ${t('profile.access.sgMembers') || 'members'}</span>
|
|
208
|
+
</div>
|
|
209
|
+
${group.description && html`
|
|
210
|
+
<div class="card-subtitle">${escHtml(group.description)}</div>
|
|
211
|
+
`}
|
|
212
|
+
|
|
213
|
+
${isExpanded && !isEditing && html`
|
|
214
|
+
<div class="card-detail">
|
|
215
|
+
<div class="detail-grid">
|
|
216
|
+
<div class="detail-item">
|
|
217
|
+
<span class="detail-label">${t('profile.access.sgDefaultPerms') || 'Default permissions'}</span>
|
|
218
|
+
<span class="detail-value">
|
|
219
|
+
${group.defaultPermissions?.read ? (t('profile.access.sgRead') || 'read') : ''} ${group.defaultPermissions?.write ? (t('profile.access.sgWrite') || 'write') : ''}
|
|
220
|
+
</span>
|
|
221
|
+
</div>
|
|
222
|
+
${group.createdAt && html`
|
|
223
|
+
<div class="detail-item">
|
|
224
|
+
<span class="detail-label">${t('profile.access.sgCreatedAt') || 'Created'}</span>
|
|
225
|
+
<span class="detail-value">${new Date(group.createdAt).toLocaleDateString()}</span>
|
|
226
|
+
</div>
|
|
227
|
+
`}
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<h4 class="card-h3 mt-section">${t('profile.access.sgMemberList') || 'Members'}</h4>
|
|
231
|
+
${memberCount === 0
|
|
232
|
+
? html`<div class="empty">${t('profile.access.sgNoMembers') || 'No members yet'}</div>`
|
|
233
|
+
: (group.members || []).map(m => renderMemberRow(group.id, m))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
${addingTo === group.id ? html`
|
|
237
|
+
<div class="create-form">
|
|
238
|
+
<div class="form-row">
|
|
239
|
+
<label>${t('profile.access.sgMemberIdentifier') || 'Identifier (GHII or GAII)'}</label>
|
|
240
|
+
<input type="text" class="input-field input-sm"
|
|
241
|
+
placeholder=${'alice@node-id'}
|
|
242
|
+
value=${memberIdent} onInput=${e => setMemberIdent(e.target.value)}
|
|
243
|
+
onKeyDown=${e => e.key === 'Enter' && handleAddMember(group.id)} />
|
|
244
|
+
</div>
|
|
245
|
+
<div class="form-row">
|
|
246
|
+
<label>${t('profile.access.sgMemberType') || 'Type'}</label>
|
|
247
|
+
<select class="input-field input-sm" value=${memberType} onChange=${e => setMemberType(e.target.value)}>
|
|
248
|
+
<option value="ghii">GHII</option>
|
|
249
|
+
<option value="gaii">GAII</option>
|
|
250
|
+
</select>
|
|
251
|
+
</div>
|
|
252
|
+
<div class="flex-row-wrap">
|
|
253
|
+
<label class="flex-row">
|
|
254
|
+
<input type="checkbox" checked=${memberRead} onChange=${() => setMemberRead(!memberRead)} />
|
|
255
|
+
${t('profile.access.sgRead') || 'Read'}
|
|
256
|
+
</label>
|
|
257
|
+
<label class="flex-row">
|
|
258
|
+
<input type="checkbox" checked=${memberWrite} onChange=${() => setMemberWrite(!memberWrite)} />
|
|
259
|
+
${t('profile.access.sgWrite') || 'Write'}
|
|
260
|
+
</label>
|
|
261
|
+
</div>
|
|
262
|
+
<div class="form-actions">
|
|
263
|
+
<button class="btn-primary btn-sm" onClick=${() => handleAddMember(group.id)}>
|
|
264
|
+
${t('profile.access.sgAddMember') || 'Add'}
|
|
265
|
+
</button>
|
|
266
|
+
<button class="btn-ghost btn-sm" onClick=${() => setAddingTo(null)}>
|
|
267
|
+
${t('profile.access.sgCancel') || 'Cancel'}
|
|
268
|
+
</button>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
` : html`
|
|
272
|
+
<div class="card-actions">
|
|
273
|
+
<button class="btn-outline btn-sm" onClick=${(e) => { e.stopPropagation(); setAddingTo(group.id); setMemberIdent(''); setMemberType('ghii'); setMemberRead(true); setMemberWrite(false); }}>
|
|
274
|
+
${t('profile.access.sgAddMember') || 'Add Member'}
|
|
275
|
+
</button>
|
|
276
|
+
<button class="btn-outline btn-sm" onClick=${(e) => { e.stopPropagation(); startEdit(group); }}>
|
|
277
|
+
${t('profile.access.sgEdit') || 'Edit'}
|
|
278
|
+
</button>
|
|
279
|
+
<button class="btn-danger-solid btn-sm" onClick=${(e) => { e.stopPropagation(); handleDelete(group.id, group.name); }}>
|
|
280
|
+
${t('profile.access.sgDelete') || 'Delete'}
|
|
281
|
+
</button>
|
|
282
|
+
</div>
|
|
283
|
+
`}
|
|
284
|
+
</div>
|
|
285
|
+
`}
|
|
286
|
+
|
|
287
|
+
${isExpanded && isEditing && html`
|
|
288
|
+
<div class="card-detail" onClick=${(e) => e.stopPropagation()}>
|
|
289
|
+
<div class="flex-col">
|
|
290
|
+
<div class="form-row">
|
|
291
|
+
<label>${t('profile.access.sgGroupName') || 'Group name'}</label>
|
|
292
|
+
<input type="text" class="input-field input-sm"
|
|
293
|
+
value=${editName} onInput=${e => setEditName(e.target.value)} />
|
|
294
|
+
</div>
|
|
295
|
+
<div class="form-row">
|
|
296
|
+
<label>${t('profile.access.sgDescription') || 'Description'}</label>
|
|
297
|
+
<textarea class="input-field input-sm" rows="2"
|
|
298
|
+
value=${editDesc} onInput=${e => setEditDesc(e.target.value)} />
|
|
299
|
+
</div>
|
|
300
|
+
<div class="form-actions">
|
|
301
|
+
<button class="btn-primary btn-sm" onClick=${() => handleUpdate(group.id)} disabled=${saving}>
|
|
302
|
+
${saving ? '...' : (t('profile.access.sgSave') || 'Save')}
|
|
303
|
+
</button>
|
|
304
|
+
<button class="btn-ghost btn-sm" onClick=${() => setEditingId(null)}>
|
|
305
|
+
${t('profile.access.sgCancel') || 'Cancel'}
|
|
306
|
+
</button>
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
`}
|
|
311
|
+
</div>
|
|
312
|
+
`;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
return html`
|
|
316
|
+
<h3 class="card-h3 mt-section">\u{1F465} ${t('profile.access.sgTitle') || 'Sharing Groups'}</h3>
|
|
317
|
+
<div class="section-desc">${t('profile.access.sgDesc') || 'Define groups of identities to share memory entries with. Assign a sharing group to memory keys to control who can read or write.'}</div>
|
|
318
|
+
|
|
319
|
+
${groups === null
|
|
320
|
+
? html`<div class="empty">${t('profile.access.sgLoading') || 'Loading...'}</div>`
|
|
321
|
+
: groups.length === 0
|
|
322
|
+
? html`<div class="empty">${t('profile.access.sgEmpty') || 'No sharing groups yet.'}</div>`
|
|
323
|
+
: groups.map(renderGroupCard)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
${!showCreate ? html`
|
|
327
|
+
<div class="mb-1">
|
|
328
|
+
<button class="btn-primary" onClick=${() => setShowCreate(true)}>
|
|
329
|
+
${t('profile.access.sgCreate') || 'New Group'}
|
|
330
|
+
</button>
|
|
331
|
+
</div>
|
|
332
|
+
` : html`
|
|
333
|
+
<div class="create-form">
|
|
334
|
+
<h4 class="card-h3 mb-half">${t('profile.access.sgCreateTitle') || 'Create Sharing Group'}</h4>
|
|
335
|
+
<div class="flex-col">
|
|
336
|
+
<div class="form-row">
|
|
337
|
+
<label>${t('profile.access.sgGroupName') || 'Group name'}</label>
|
|
338
|
+
<input type="text" class="input-field input-sm"
|
|
339
|
+
placeholder=${t('profile.access.sgNamePlaceholder') || 'e.g. Team Alpha'}
|
|
340
|
+
value=${formName} onInput=${e => setFormName(e.target.value)}
|
|
341
|
+
onKeyDown=${e => e.key === 'Enter' && handleCreate()} />
|
|
342
|
+
</div>
|
|
343
|
+
<div class="form-row">
|
|
344
|
+
<label>${t('profile.access.sgDescription') || 'Description (optional)'}</label>
|
|
345
|
+
<textarea class="input-field input-sm" rows="2"
|
|
346
|
+
placeholder=${t('profile.access.sgDescPlaceholder') || 'What is this group for?'}
|
|
347
|
+
value=${formDesc} onInput=${e => setFormDesc(e.target.value)} />
|
|
348
|
+
</div>
|
|
349
|
+
<div class="form-actions">
|
|
350
|
+
<button class="btn-primary btn-sm" onClick=${handleCreate} disabled=${creating}>
|
|
351
|
+
${creating ? '...' : (t('profile.access.sgCreateBtn') || 'Create')}
|
|
352
|
+
</button>
|
|
353
|
+
<button class="btn-ghost btn-sm" onClick=${() => setShowCreate(false)}>
|
|
354
|
+
${t('profile.access.sgCancel') || 'Cancel'}
|
|
355
|
+
</button>
|
|
356
|
+
</div>
|
|
357
|
+
</div>
|
|
358
|
+
</div>
|
|
359
|
+
`}
|
|
360
|
+
<${ConfirmUI} />
|
|
361
|
+
`;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
365
|
+
Agent Defaults Section
|
|
366
|
+
═══════════════════════════════════════════════════════════════════ */
|
|
367
|
+
|
|
368
|
+
function AgentDefaultsSection({ showToast }) {
|
|
369
|
+
const [defaults, setDefaults] = useState(null);
|
|
370
|
+
const [editing, setEditing] = useState(false);
|
|
371
|
+
const [saving, setSaving] = useState(false);
|
|
372
|
+
|
|
373
|
+
// Edit state
|
|
374
|
+
const [editRules, setEditRules] = useState([]);
|
|
375
|
+
const [editBudget, setEditBudget] = useState('');
|
|
376
|
+
const [newRule, setNewRule] = useState('');
|
|
377
|
+
|
|
378
|
+
const loadDefaults = useCallback(async () => {
|
|
379
|
+
try {
|
|
380
|
+
const resp = await getOwnerDefaults();
|
|
381
|
+
setDefaults(resp?.data?.defaults || null);
|
|
382
|
+
} catch {
|
|
383
|
+
setDefaults(null);
|
|
384
|
+
}
|
|
385
|
+
}, []);
|
|
386
|
+
|
|
387
|
+
useEffect(() => { loadDefaults(); }, [loadDefaults]);
|
|
388
|
+
|
|
389
|
+
// Live update listener
|
|
390
|
+
const liveRef = useRef(loadDefaults);
|
|
391
|
+
liveRef.current = loadDefaults;
|
|
392
|
+
useEffect(() => {
|
|
393
|
+
const handler = () => liveRef.current();
|
|
394
|
+
window.addEventListener('aimeat-live-update', handler);
|
|
395
|
+
return () => window.removeEventListener('aimeat-live-update', handler);
|
|
396
|
+
}, []);
|
|
397
|
+
|
|
398
|
+
const startEdit = useCallback(() => {
|
|
399
|
+
const rules = defaults?.rules || [];
|
|
400
|
+
const budget = defaults?.default_token_budget;
|
|
401
|
+
setEditRules([...rules]);
|
|
402
|
+
setEditBudget(budget != null ? String(budget) : '');
|
|
403
|
+
setNewRule('');
|
|
404
|
+
setEditing(true);
|
|
405
|
+
}, [defaults]);
|
|
406
|
+
|
|
407
|
+
const handleSave = useCallback(async () => {
|
|
408
|
+
setSaving(true);
|
|
409
|
+
try {
|
|
410
|
+
const budgetVal = editBudget.trim() ? parseInt(editBudget, 10) : undefined;
|
|
411
|
+
if (editBudget.trim() && (isNaN(budgetVal) || budgetVal < 0)) {
|
|
412
|
+
showToast(t('profile.access.adInvalidBudget') || 'Token budget must be a non-negative number');
|
|
413
|
+
setSaving(false);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
await upsertOwnerDefaults({
|
|
417
|
+
rules: editRules,
|
|
418
|
+
default_token_budget: budgetVal,
|
|
419
|
+
default_memory_areas: defaults?.default_memory_areas || [],
|
|
420
|
+
});
|
|
421
|
+
showToast(t('profile.access.adSaved') || 'Agent defaults saved');
|
|
422
|
+
setEditing(false);
|
|
423
|
+
loadDefaults();
|
|
424
|
+
} catch (e) {
|
|
425
|
+
showToast(e.message || (t('profile.access.adSaveError') || 'Failed to save defaults'));
|
|
426
|
+
} finally {
|
|
427
|
+
setSaving(false);
|
|
428
|
+
}
|
|
429
|
+
}, [editRules, editBudget, defaults, showToast, loadDefaults]);
|
|
430
|
+
|
|
431
|
+
const addRule = useCallback(() => {
|
|
432
|
+
if (!newRule.trim()) return;
|
|
433
|
+
setEditRules(prev => [...prev, newRule.trim()]);
|
|
434
|
+
setNewRule('');
|
|
435
|
+
}, [newRule]);
|
|
436
|
+
|
|
437
|
+
const removeRule = useCallback((idx) => {
|
|
438
|
+
setEditRules(prev => prev.filter((_, i) => i !== idx));
|
|
439
|
+
}, []);
|
|
440
|
+
|
|
441
|
+
const rules = defaults?.rules || [];
|
|
442
|
+
const budget = defaults?.default_token_budget;
|
|
443
|
+
|
|
444
|
+
return html`
|
|
445
|
+
<h3 class="card-h3 mt-section">\u{1F916} ${t('profile.access.adTitle') || 'Agent Defaults'}</h3>
|
|
446
|
+
<div class="section-desc">${t('profile.access.adDesc') || 'Owner-level defaults that apply to all your agents unless overridden by per-agent directives.'}</div>
|
|
447
|
+
|
|
448
|
+
${!editing ? html`
|
|
449
|
+
<div class="card">
|
|
450
|
+
<div class="flex-between mb-half">
|
|
451
|
+
<div class="card-title">${t('profile.access.adRules') || 'Default Rules'}</div>
|
|
452
|
+
<button class="btn-outline btn-sm" onClick=${startEdit}>
|
|
453
|
+
${t('profile.access.adEdit') || 'Edit'}
|
|
454
|
+
</button>
|
|
455
|
+
</div>
|
|
456
|
+
|
|
457
|
+
${rules.length === 0
|
|
458
|
+
? html`<div class="empty">${t('profile.access.adNoRules') || 'No default rules set.'}</div>`
|
|
459
|
+
: rules.map((rule, i) => html`
|
|
460
|
+
<div class="mem-item" key=${i}>
|
|
461
|
+
<span class="mem-key">${escHtml(rule)}</span>
|
|
462
|
+
</div>
|
|
463
|
+
`)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
<div class="mem-item">
|
|
467
|
+
<span class="mem-key">${t('profile.access.adTokenBudget') || 'Token Budget'}</span>
|
|
468
|
+
<span>${budget != null ? budget.toLocaleString() : (t('profile.access.adUnlimited') || 'Unlimited')}</span>
|
|
469
|
+
</div>
|
|
470
|
+
</div>
|
|
471
|
+
` : html`
|
|
472
|
+
<div class="create-form">
|
|
473
|
+
<h4 class="card-h3 mb-half">${t('profile.access.adEditTitle') || 'Edit Agent Defaults'}</h4>
|
|
474
|
+
<div class="flex-col">
|
|
475
|
+
<div class="form-row">
|
|
476
|
+
<label>${t('profile.access.adRules') || 'Rules'}</label>
|
|
477
|
+
${editRules.map((rule, i) => html`
|
|
478
|
+
<div class="mem-item" key=${i}>
|
|
479
|
+
<span class="mem-key">${escHtml(rule)}</span>
|
|
480
|
+
<button class="btn-ghost btn-danger btn-sm" onClick=${() => removeRule(i)}>
|
|
481
|
+
${t('profile.access.adRemoveRule') || 'Remove'}
|
|
482
|
+
</button>
|
|
483
|
+
</div>
|
|
484
|
+
`)}
|
|
485
|
+
<div class="flex-row">
|
|
486
|
+
<input type="text" class="input-field input-sm"
|
|
487
|
+
placeholder=${t('profile.access.adRulePlaceholder') || 'Add a rule...'}
|
|
488
|
+
value=${newRule} onInput=${e => setNewRule(e.target.value)}
|
|
489
|
+
onKeyDown=${e => e.key === 'Enter' && addRule()} />
|
|
490
|
+
<button class="btn-outline btn-sm" onClick=${addRule}>
|
|
491
|
+
${t('profile.access.adAddRule') || 'Add'}
|
|
492
|
+
</button>
|
|
493
|
+
</div>
|
|
494
|
+
</div>
|
|
495
|
+
|
|
496
|
+
<div class="form-row">
|
|
497
|
+
<label>${t('profile.access.adTokenBudget') || 'Token Budget'}</label>
|
|
498
|
+
<input type="number" class="input-field input-sm" min="0"
|
|
499
|
+
placeholder=${t('profile.access.adBudgetPlaceholder') || 'Leave empty for unlimited'}
|
|
500
|
+
value=${editBudget} onInput=${e => setEditBudget(e.target.value)} />
|
|
501
|
+
</div>
|
|
502
|
+
|
|
503
|
+
<div class="form-actions">
|
|
504
|
+
<button class="btn-primary btn-sm" onClick=${handleSave} disabled=${saving}>
|
|
505
|
+
${saving ? '...' : (t('profile.access.adSave') || 'Save')}
|
|
506
|
+
</button>
|
|
507
|
+
<button class="btn-ghost btn-sm" onClick=${() => setEditing(false)}>
|
|
508
|
+
${t('profile.access.adCancel') || 'Cancel'}
|
|
509
|
+
</button>
|
|
510
|
+
</div>
|
|
511
|
+
</div>
|
|
512
|
+
</div>
|
|
513
|
+
`}
|
|
514
|
+
`;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
518
|
+
Main Access Tab
|
|
519
|
+
═══════════════════════════════════════════════════════════════════ */
|
|
18
520
|
|
|
19
521
|
export default function AccessTab({ session, showToast }) {
|
|
20
522
|
const NODE_URL = getNodeUrl();
|
|
@@ -157,5 +659,8 @@ export default function AccessTab({ session, showToast }) {
|
|
|
157
659
|
</button>
|
|
158
660
|
</div>
|
|
159
661
|
</div>
|
|
662
|
+
|
|
663
|
+
<${SharingGroupsSection} showToast=${showToast} />
|
|
664
|
+
<${AgentDefaultsSection} showToast=${showToast} />
|
|
160
665
|
`;
|
|
161
666
|
}
|