@agenticmail/enterprise 0.5.304 → 0.5.306
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-4QGERCWK.js +4400 -0
- package/dist/chunk-N5JFOIJN.js +1519 -0
- package/dist/cli-serve-KZT4WNKR.js +143 -0
- package/dist/cli.js +2 -2
- package/dist/dashboard/pages/approvals.js +2 -2
- package/dist/dashboard/pages/dashboard.js +6 -5
- package/dist/dashboard/pages/guardrails.js +2 -2
- package/dist/dashboard/pages/journal.js +2 -2
- package/dist/dashboard/pages/knowledge-contributions.js +1 -1
- package/dist/dashboard/pages/knowledge.js +14 -5
- package/dist/dashboard/pages/messages.js +2 -2
- package/dist/dashboard/pages/org-chart.js +2 -2
- package/dist/dashboard/pages/task-pipeline.js +17 -7
- package/dist/dashboard/pages/users.js +90 -1
- package/dist/dashboard/pages/workforce.js +2 -2
- package/dist/index.js +2 -2
- package/dist/server-43HP7BAB.js +15 -0
- package/dist/setup-2NQBX5FF.js +20 -0
- package/package.json +1 -1
- package/src/admin/routes.ts +8 -2
- package/src/dashboard/pages/approvals.js +2 -2
- package/src/dashboard/pages/dashboard.js +6 -5
- package/src/dashboard/pages/guardrails.js +2 -2
- package/src/dashboard/pages/journal.js +2 -2
- package/src/dashboard/pages/knowledge-contributions.js +1 -1
- package/src/dashboard/pages/knowledge.js +14 -5
- package/src/dashboard/pages/messages.js +2 -2
- package/src/dashboard/pages/org-chart.js +2 -2
- package/src/dashboard/pages/task-pipeline.js +17 -7
- package/src/dashboard/pages/users.js +66 -1
- package/src/dashboard/pages/workforce.js +2 -2
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import "./chunk-KFQGP6VL.js";
|
|
2
|
+
|
|
3
|
+
// src/cli-serve.ts
|
|
4
|
+
import { existsSync, readFileSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
function loadEnvFile() {
|
|
8
|
+
const candidates = [
|
|
9
|
+
join(process.cwd(), ".env"),
|
|
10
|
+
join(homedir(), ".agenticmail", ".env")
|
|
11
|
+
];
|
|
12
|
+
for (const envPath of candidates) {
|
|
13
|
+
if (!existsSync(envPath)) continue;
|
|
14
|
+
try {
|
|
15
|
+
const content = readFileSync(envPath, "utf8");
|
|
16
|
+
for (const line of content.split("\n")) {
|
|
17
|
+
const trimmed = line.trim();
|
|
18
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
19
|
+
const eq = trimmed.indexOf("=");
|
|
20
|
+
if (eq < 0) continue;
|
|
21
|
+
const key = trimmed.slice(0, eq).trim();
|
|
22
|
+
let val = trimmed.slice(eq + 1).trim();
|
|
23
|
+
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
24
|
+
val = val.slice(1, -1);
|
|
25
|
+
}
|
|
26
|
+
if (!process.env[key]) process.env[key] = val;
|
|
27
|
+
}
|
|
28
|
+
console.log(`Loaded config from ${envPath}`);
|
|
29
|
+
return;
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function ensureSecrets() {
|
|
35
|
+
const { randomUUID } = await import("crypto");
|
|
36
|
+
const envDir = join(homedir(), ".agenticmail");
|
|
37
|
+
const envPath = join(envDir, ".env");
|
|
38
|
+
let dirty = false;
|
|
39
|
+
if (!process.env.JWT_SECRET) {
|
|
40
|
+
process.env.JWT_SECRET = randomUUID() + randomUUID();
|
|
41
|
+
dirty = true;
|
|
42
|
+
console.log("[startup] Generated new JWT_SECRET (existing sessions will need to re-login)");
|
|
43
|
+
}
|
|
44
|
+
if (!process.env.AGENTICMAIL_VAULT_KEY) {
|
|
45
|
+
process.env.AGENTICMAIL_VAULT_KEY = randomUUID() + randomUUID();
|
|
46
|
+
dirty = true;
|
|
47
|
+
console.log("[startup] Generated new AGENTICMAIL_VAULT_KEY");
|
|
48
|
+
console.log("[startup] \u26A0\uFE0F Previously encrypted credentials will need to be re-entered in the dashboard");
|
|
49
|
+
}
|
|
50
|
+
if (dirty) {
|
|
51
|
+
try {
|
|
52
|
+
if (!existsSync(envDir)) {
|
|
53
|
+
const { mkdirSync } = await import("fs");
|
|
54
|
+
mkdirSync(envDir, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
const { appendFileSync } = await import("fs");
|
|
57
|
+
const lines = [];
|
|
58
|
+
let existing = "";
|
|
59
|
+
if (existsSync(envPath)) {
|
|
60
|
+
existing = readFileSync(envPath, "utf8");
|
|
61
|
+
}
|
|
62
|
+
if (!existing.includes("JWT_SECRET=")) {
|
|
63
|
+
lines.push(`JWT_SECRET=${process.env.JWT_SECRET}`);
|
|
64
|
+
}
|
|
65
|
+
if (!existing.includes("AGENTICMAIL_VAULT_KEY=")) {
|
|
66
|
+
lines.push(`AGENTICMAIL_VAULT_KEY=${process.env.AGENTICMAIL_VAULT_KEY}`);
|
|
67
|
+
}
|
|
68
|
+
if (lines.length) {
|
|
69
|
+
appendFileSync(envPath, "\n" + lines.join("\n") + "\n", { mode: 384 });
|
|
70
|
+
console.log(`[startup] Saved secrets to ${envPath}`);
|
|
71
|
+
}
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.warn(`[startup] Could not save secrets to ${envPath}: ${e.message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function runServe(_args) {
|
|
78
|
+
loadEnvFile();
|
|
79
|
+
const DATABASE_URL = process.env.DATABASE_URL;
|
|
80
|
+
const PORT = parseInt(process.env.PORT || "8080", 10);
|
|
81
|
+
await ensureSecrets();
|
|
82
|
+
const JWT_SECRET = process.env.JWT_SECRET;
|
|
83
|
+
const VAULT_KEY = process.env.AGENTICMAIL_VAULT_KEY;
|
|
84
|
+
if (!DATABASE_URL) {
|
|
85
|
+
console.error("ERROR: DATABASE_URL is required.");
|
|
86
|
+
console.error("");
|
|
87
|
+
console.error("Set it via environment variable or .env file:");
|
|
88
|
+
console.error(" DATABASE_URL=postgresql://user:pass@host:5432/db npx @agenticmail/enterprise start");
|
|
89
|
+
console.error("");
|
|
90
|
+
console.error("Or create a .env file (in cwd or ~/.agenticmail/.env):");
|
|
91
|
+
console.error(" DATABASE_URL=postgresql://user:pass@host:5432/db");
|
|
92
|
+
console.error(" JWT_SECRET=your-secret-here");
|
|
93
|
+
console.error(" PORT=3200");
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
const { createAdapter } = await import("./factory-QYGGXVYW.js");
|
|
97
|
+
const { createServer } = await import("./server-43HP7BAB.js");
|
|
98
|
+
const db = await createAdapter({
|
|
99
|
+
type: DATABASE_URL.startsWith("postgres") ? "postgres" : "sqlite",
|
|
100
|
+
connectionString: DATABASE_URL
|
|
101
|
+
});
|
|
102
|
+
await db.migrate();
|
|
103
|
+
const server = createServer({
|
|
104
|
+
port: PORT,
|
|
105
|
+
db,
|
|
106
|
+
jwtSecret: JWT_SECRET,
|
|
107
|
+
corsOrigins: ["*"]
|
|
108
|
+
});
|
|
109
|
+
await server.start();
|
|
110
|
+
console.log(`AgenticMail Enterprise server running on :${PORT}`);
|
|
111
|
+
const tunnelToken = process.env.CLOUDFLARED_TOKEN;
|
|
112
|
+
if (tunnelToken) {
|
|
113
|
+
try {
|
|
114
|
+
const { execSync, spawn } = await import("child_process");
|
|
115
|
+
try {
|
|
116
|
+
execSync("which cloudflared", { timeout: 3e3 });
|
|
117
|
+
} catch {
|
|
118
|
+
console.log("[startup] cloudflared not found \u2014 skipping tunnel auto-start");
|
|
119
|
+
console.log("[startup] Install cloudflared to enable tunnel: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
execSync('pgrep -f "cloudflared.*tunnel.*run"', { timeout: 3e3 });
|
|
124
|
+
console.log("[startup] cloudflared tunnel already running");
|
|
125
|
+
return;
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
const subdomain = process.env.AGENTICMAIL_SUBDOMAIN || process.env.AGENTICMAIL_DOMAIN || "";
|
|
129
|
+
console.log(`[startup] Starting cloudflared tunnel${subdomain ? ` for ${subdomain}.agenticmail.io` : ""}...`);
|
|
130
|
+
const child = spawn("cloudflared", ["tunnel", "--no-autoupdate", "run", "--token", tunnelToken], {
|
|
131
|
+
detached: true,
|
|
132
|
+
stdio: "ignore"
|
|
133
|
+
});
|
|
134
|
+
child.unref();
|
|
135
|
+
console.log("[startup] cloudflared tunnel started (pid " + child.pid + ")");
|
|
136
|
+
} catch (e) {
|
|
137
|
+
console.warn("[startup] Could not auto-start cloudflared: " + e.message);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export {
|
|
142
|
+
runServe
|
|
143
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -57,14 +57,14 @@ Skill Development:
|
|
|
57
57
|
break;
|
|
58
58
|
case "serve":
|
|
59
59
|
case "start":
|
|
60
|
-
import("./cli-serve-
|
|
60
|
+
import("./cli-serve-KZT4WNKR.js").then((m) => m.runServe(args.slice(1))).catch(fatal);
|
|
61
61
|
break;
|
|
62
62
|
case "agent":
|
|
63
63
|
import("./cli-agent-YSQVDBOZ.js").then((m) => m.runAgent(args.slice(1))).catch(fatal);
|
|
64
64
|
break;
|
|
65
65
|
case "setup":
|
|
66
66
|
default:
|
|
67
|
-
import("./setup-
|
|
67
|
+
import("./setup-2NQBX5FF.js").then((m) => m.runSetupWizard()).catch(fatal);
|
|
68
68
|
break;
|
|
69
69
|
}
|
|
70
70
|
function fatal(err) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h, useState, useEffect, Fragment, useApp, engineCall, showConfirm, buildAgentEmailMap, buildAgentDataMap, resolveAgentEmail, renderAgentBadge, getOrgId } from '../components/utils.js';
|
|
1
|
+
import { h, useState, useEffect, Fragment, useApp, engineCall, showConfirm, buildAgentEmailMap, buildAgentDataMap, resolveAgentEmail, renderAgentBadge, getOrgId } , apiCall } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { HelpButton } from '../components/help-button.js';
|
|
4
4
|
import { useOrgContext } from '../components/org-switcher.js';
|
|
@@ -16,7 +16,7 @@ export function ApprovalsPage() {
|
|
|
16
16
|
const load = () => {
|
|
17
17
|
engineCall('/approvals/pending').then(d => setPending(d.requests || [])).catch(() => {});
|
|
18
18
|
engineCall('/approvals/history?limit=50').then(d => setHistory(d.requests || [])).catch(() => {});
|
|
19
|
-
|
|
19
|
+
apiCall('/agents' + (orgCtx.selectedOrgId ? '?clientOrgId=' + orgCtx.selectedOrgId : '')).then(d => setAgents(d.agents || [])).catch(() => {});
|
|
20
20
|
};
|
|
21
21
|
useEffect(() => { load(); }, []);
|
|
22
22
|
|
|
@@ -48,7 +48,7 @@ export function SetupChecklist({ onNavigate }) {
|
|
|
48
48
|
|
|
49
49
|
export function DashboardPage() {
|
|
50
50
|
var orgCtx = useOrgContext();
|
|
51
|
-
var
|
|
51
|
+
var clientOrgFilter = orgCtx.selectedOrgId || '';
|
|
52
52
|
const [stats, setStats] = useState(null);
|
|
53
53
|
const [agents, setAgents] = useState([]);
|
|
54
54
|
const [events, setEvents] = useState([]);
|
|
@@ -59,11 +59,12 @@ export function DashboardPage() {
|
|
|
59
59
|
var selectedEvent = _selectedEvent[0]; var setSelectedEvent = _selectedEvent[1];
|
|
60
60
|
|
|
61
61
|
useEffect(() => {
|
|
62
|
+
var agentUrl = clientOrgFilter ? '/agents?clientOrgId=' + clientOrgFilter : '/agents';
|
|
62
63
|
apiCall('/stats').then(setStats).catch(() => {});
|
|
63
|
-
apiCall(
|
|
64
|
-
engineCall('/agents?orgId=' +
|
|
64
|
+
apiCall(agentUrl).then(d => setAgents(d.agents || d || [])).catch(() => {});
|
|
65
|
+
engineCall('/agents?orgId=' + getOrgId()).then(d => setEngineAgents(d.agents || [])).catch(() => {});
|
|
65
66
|
engineCall('/activity/events?limit=10').then(d => setEvents(d.events || [])).catch(() => {});
|
|
66
|
-
}, [
|
|
67
|
+
}, [clientOrgFilter]);
|
|
67
68
|
|
|
68
69
|
// Merge admin + engine agents; engine agents (appended last) win in the data map
|
|
69
70
|
var mergedForMap = [].concat(agents, engineAgents);
|
|
@@ -81,7 +82,7 @@ export function DashboardPage() {
|
|
|
81
82
|
h('div', { className: 'stat-grid' },
|
|
82
83
|
h('div', { className: 'stat-card' }, h('div', { className: 'stat-label', style: { display: 'flex', alignItems: 'center' } }, 'Total Agents', h(HelpButton, { label: 'Total Agents' },
|
|
83
84
|
h('p', null, 'The total number of agents created in your organization, including active, paused, and archived agents.')
|
|
84
|
-
)), h('div', { className: 'stat-value' }, stats?.totalAgents ?? agents.length ?? '-')),
|
|
85
|
+
)), h('div', { className: 'stat-value' }, clientOrgFilter ? agents.length : (stats?.totalAgents ?? agents.length ?? '-'))),
|
|
85
86
|
h('div', { className: 'stat-card' }, h('div', { className: 'stat-label', style: { display: 'flex', alignItems: 'center' } }, 'Active Agents', h(HelpButton, { label: 'Active Agents' },
|
|
86
87
|
h('p', null, 'Agents currently running and available to process tasks. If this is lower than Total Agents, some agents may be paused or archived.')
|
|
87
88
|
)), h('div', { className: 'stat-value', style: { color: 'var(--success)' } }, (stats?.activeAgents ?? agents.filter(function(a) { return a.status === 'active'; }).length) || '-')),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h, useState, useEffect, useCallback, Fragment, useApp, engineCall, buildAgentEmailMap, resolveAgentEmail, buildAgentDataMap, renderAgentBadge, getOrgId } from '../components/utils.js';
|
|
1
|
+
import { h, useState, useEffect, useCallback, Fragment, useApp, engineCall, buildAgentEmailMap, resolveAgentEmail, buildAgentDataMap, renderAgentBadge, getOrgId } , apiCall } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { HelpButton } from '../components/help-button.js';
|
|
4
4
|
import { useOrgContext } from '../components/org-switcher.js';
|
|
@@ -104,7 +104,7 @@ export function GuardrailsPage() {
|
|
|
104
104
|
var _ag = useState([]);
|
|
105
105
|
var agents = _ag[0]; var setAgents = _ag[1];
|
|
106
106
|
useEffect(function() {
|
|
107
|
-
|
|
107
|
+
apiCall('/agents' + (orgCtx.selectedOrgId ? '?clientOrgId=' + orgCtx.selectedOrgId : '')).then(function(d) { setAgents(d.agents || []); }).catch(function() {});
|
|
108
108
|
}, []);
|
|
109
109
|
|
|
110
110
|
var TABS = [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h, useState, useEffect, Fragment, useApp, engineCall, buildAgentEmailMap, buildAgentDataMap, resolveAgentEmail, renderAgentBadge, getOrgId } from '../components/utils.js';
|
|
1
|
+
import { h, useState, useEffect, Fragment, useApp, engineCall, buildAgentEmailMap, buildAgentDataMap, resolveAgentEmail, renderAgentBadge, getOrgId } , apiCall } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { HelpButton } from '../components/help-button.js';
|
|
4
4
|
import { useOrgContext } from '../components/org-switcher.js';
|
|
@@ -16,7 +16,7 @@ export function JournalPage() {
|
|
|
16
16
|
const load = () => {
|
|
17
17
|
engineCall('/journal?orgId=' + effectiveOrgId + '&limit=50').then(d => { setEntries(d.entries || []); setTotal(d.total || 0); }).catch(() => {});
|
|
18
18
|
engineCall('/journal/stats/default').then(d => setStats(d)).catch(() => {});
|
|
19
|
-
|
|
19
|
+
apiCall('/agents' + (orgCtx.selectedOrgId ? '?clientOrgId=' + orgCtx.selectedOrgId : '')).then(d => setAgents(d.agents || [])).catch(() => {});
|
|
20
20
|
};
|
|
21
21
|
useEffect(load, []);
|
|
22
22
|
|
|
@@ -42,7 +42,7 @@ export function KnowledgeContributionsPage() {
|
|
|
42
42
|
var [searchAgentFilter, setSearchAgentFilter] = useState('');
|
|
43
43
|
|
|
44
44
|
// Effective org ID: uses client org if selected, else default
|
|
45
|
-
var effectiveOrgId = orgCtx.selectedOrgId ||
|
|
45
|
+
var effectiveOrgId = orgCtx.selectedOrgId || getOrgId();
|
|
46
46
|
|
|
47
47
|
var loadBases = useCallback(function() {
|
|
48
48
|
Promise.all([
|
|
@@ -17,7 +17,7 @@ export function KnowledgeBasePage() {
|
|
|
17
17
|
const [chunks, setChunks] = useState([]);
|
|
18
18
|
const [selectedDoc, setSelectedDoc] = useState(null);
|
|
19
19
|
const [editing, setEditing] = useState(false);
|
|
20
|
-
const [editForm, setEditForm] = useState({ name: '', description: '' });
|
|
20
|
+
const [editForm, setEditForm] = useState({ name: '', description: '', clientOrgId: '' });
|
|
21
21
|
const [loading, setLoading] = useState(false);
|
|
22
22
|
const [showImport, setShowImport] = useState(false);
|
|
23
23
|
const [importKb, setImportKb] = useState(null);
|
|
@@ -54,7 +54,7 @@ export function KnowledgeBasePage() {
|
|
|
54
54
|
setDocs(kbData.documents || []);
|
|
55
55
|
setChunks([]);
|
|
56
56
|
setSelectedDoc(null);
|
|
57
|
-
setEditForm({ name: kbData.name || '', description: kbData.description || '' });
|
|
57
|
+
setEditForm({ name: kbData.name || '', description: kbData.description || '', clientOrgId: kbData.clientOrgId || '' });
|
|
58
58
|
} catch (e) {
|
|
59
59
|
toast('Failed to load knowledge base: ' + e.message, 'error');
|
|
60
60
|
}
|
|
@@ -122,9 +122,9 @@ export function KnowledgeBasePage() {
|
|
|
122
122
|
const saveEdit = async () => {
|
|
123
123
|
if (!selected) return;
|
|
124
124
|
try {
|
|
125
|
-
await engineCall('/knowledge-bases/' + selected.id, { method: 'PUT', body: JSON.stringify({ name: editForm.name, description: editForm.description }) });
|
|
125
|
+
await engineCall('/knowledge-bases/' + selected.id, { method: 'PUT', body: JSON.stringify({ name: editForm.name, description: editForm.description, clientOrgId: editForm.clientOrgId || null }) });
|
|
126
126
|
toast('Knowledge base updated', 'success');
|
|
127
|
-
setSelected(s => ({ ...s, name: editForm.name, description: editForm.description }));
|
|
127
|
+
setSelected(s => ({ ...s, name: editForm.name, description: editForm.description, clientOrgId: editForm.clientOrgId }));
|
|
128
128
|
setEditing(false);
|
|
129
129
|
load();
|
|
130
130
|
} catch (e) { toast(e.message, 'error'); }
|
|
@@ -171,7 +171,16 @@ export function KnowledgeBasePage() {
|
|
|
171
171
|
h('div', { className: 'card', style: { marginBottom: 16 } },
|
|
172
172
|
h('div', { className: 'card-body' },
|
|
173
173
|
editing
|
|
174
|
-
? h(
|
|
174
|
+
? h(Fragment, null,
|
|
175
|
+
h('textarea', { className: 'input', rows: 3, value: editForm.description, onChange: e => setEditForm(f => ({ ...f, description: e.target.value })), placeholder: 'Knowledge base description...', style: { marginBottom: 8 } }),
|
|
176
|
+
clientOrgs.length > 0 && h('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
177
|
+
h('label', { style: { fontSize: 12, fontWeight: 600, whiteSpace: 'nowrap' } }, 'Organization:'),
|
|
178
|
+
h('select', { className: 'input', value: editForm.clientOrgId, onChange: e => setEditForm(f => ({ ...f, clientOrgId: e.target.value })), style: { fontSize: 12, maxWidth: 250 } },
|
|
179
|
+
h('option', { value: '' }, 'My Organization (internal)'),
|
|
180
|
+
clientOrgs.filter(o => o.is_active !== false).map(o => h('option', { key: o.id, value: o.id }, o.name))
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
)
|
|
175
184
|
: h('p', { style: { color: 'var(--text-secondary)', fontSize: 13, margin: 0 } }, selected.description || 'No description'),
|
|
176
185
|
h('div', { style: { display: 'flex', gap: 12, marginTop: 12, fontSize: 12, color: 'var(--text-muted)' } },
|
|
177
186
|
h('span', null, 'ID: ', h('code', null, selected.id)),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h, useState, useEffect, useRef, Fragment, useApp, engineCall, buildAgentEmailMap, resolveAgentEmail, buildAgentDataMap, renderAgentBadge, getOrgId } from '../components/utils.js';
|
|
1
|
+
import { h, useState, useEffect, useRef, Fragment, useApp, engineCall, buildAgentEmailMap, resolveAgentEmail, buildAgentDataMap, renderAgentBadge, getOrgId } , apiCall } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { HelpButton } from '../components/help-button.js';
|
|
4
4
|
import { useOrgContext } from '../components/org-switcher.js';
|
|
@@ -22,7 +22,7 @@ export function MessagesPage() {
|
|
|
22
22
|
engineCall('/messages?orgId=' + effectiveOrgId + '&limit=100').then(d => setMessages(d.messages || [])).catch(() => {});
|
|
23
23
|
};
|
|
24
24
|
const loadAgents = () => {
|
|
25
|
-
|
|
25
|
+
apiCall('/agents' + (orgCtx.selectedOrgId ? '?clientOrgId=' + orgCtx.selectedOrgId : '')).then(d => setAgents(d.agents || [])).catch(() => {});
|
|
26
26
|
};
|
|
27
27
|
const loadTopology = () => {
|
|
28
28
|
engineCall('/messages/topology?orgId=' + effectiveOrgId).then(d => setTopology(d.topology || null)).catch(() => {});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h, useState, useEffect, useCallback, useRef, Fragment, useApp, engineCall, getOrgId } from '../components/utils.js';
|
|
1
|
+
import { h, useState, useEffect, useCallback, useRef, Fragment, useApp, engineCall, getOrgId } , apiCall } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { HelpButton } from '../components/help-button.js';
|
|
4
4
|
import { useOrgContext } from '../components/org-switcher.js';
|
|
@@ -154,7 +154,7 @@ export function OrgChartPage() {
|
|
|
154
154
|
setLoading(true); setError(null);
|
|
155
155
|
Promise.all([
|
|
156
156
|
engineCall('/hierarchy/org-chart').catch(function() { return null; }),
|
|
157
|
-
|
|
157
|
+
apiCall('/agents' + (orgCtx.selectedOrgId ? '?clientOrgId=' + orgCtx.selectedOrgId : '')).catch(function() { return { agents: [] }; }),
|
|
158
158
|
]).then(function(res) {
|
|
159
159
|
var hierRes = res[0]; var agentRes = res[1];
|
|
160
160
|
var avatarMap = {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h, useState, useEffect, useCallback, useRef, Fragment, useApp, engineCall, getOrgId } from '../components/utils.js';
|
|
1
|
+
import { h, useState, useEffect, useCallback, useRef, Fragment, useApp, engineCall, getOrgId } , apiCall } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { HelpButton } from '../components/help-button.js';
|
|
4
4
|
import { useOrgContext } from '../components/org-switcher.js';
|
|
@@ -440,7 +440,7 @@ export function TaskPipelinePage() {
|
|
|
440
440
|
var app = useApp();
|
|
441
441
|
var toast = app.toast;
|
|
442
442
|
var orgCtx = useOrgContext();
|
|
443
|
-
var effectiveOrgId = orgCtx.selectedOrgId ||
|
|
443
|
+
var effectiveOrgId = orgCtx.selectedOrgId || getOrgId();
|
|
444
444
|
var _tasks = useState([]);
|
|
445
445
|
var tasks = _tasks[0]; var setTasks = _tasks[1];
|
|
446
446
|
var _stats = useState({ created: 0, assigned: 0, inProgress: 0, completed: 0, failed: 0, cancelled: 0, total: 0, todayCompleted: 0, todayFailed: 0, todayCreated: 0, avgDurationMs: 0, totalCost: 0, totalTokens: 0, topAgents: [] });
|
|
@@ -477,16 +477,26 @@ export function TaskPipelinePage() {
|
|
|
477
477
|
Promise.all([
|
|
478
478
|
engineCall('/task-pipeline?limit=200'),
|
|
479
479
|
engineCall('/task-pipeline/stats'),
|
|
480
|
-
|
|
480
|
+
apiCall('/agents' + (orgCtx.selectedOrgId ? '?clientOrgId=' + orgCtx.selectedOrgId : '')).catch(function() { return { agents: [] }; }),
|
|
481
481
|
]).then(function(res) {
|
|
482
|
-
|
|
483
|
-
|
|
482
|
+
var allTasks = res[0]?.tasks || [];
|
|
483
|
+
var orgAgents = res[2]?.agents || [];
|
|
484
484
|
// Build agent avatar/name map
|
|
485
485
|
var map = {};
|
|
486
|
-
|
|
487
|
-
map[a.id] = { name: a.config?.name || a.id, avatar: a.config?.identity?.avatar || a.config?.avatar || a.config?.persona?.avatar || null };
|
|
486
|
+
orgAgents.forEach(function(a) {
|
|
487
|
+
map[a.id] = { name: a.config?.name || a.name || a.id, avatar: a.config?.identity?.avatar || a.config?.avatar || a.config?.persona?.avatar || null };
|
|
488
488
|
});
|
|
489
489
|
setAgentMap(map);
|
|
490
|
+
// Filter tasks by org's agents when org is selected
|
|
491
|
+
if (orgCtx.selectedOrgId && orgAgents.length > 0) {
|
|
492
|
+
var agentIds = {};
|
|
493
|
+
orgAgents.forEach(function(a) { agentIds[a.id] = true; });
|
|
494
|
+
allTasks = allTasks.filter(function(t) { return agentIds[t.assignedAgent] || agentIds[t.createdBy]; });
|
|
495
|
+
} else if (orgCtx.selectedOrgId && orgAgents.length === 0) {
|
|
496
|
+
allTasks = []; // No agents in this org = no tasks
|
|
497
|
+
}
|
|
498
|
+
setTasks(allTasks);
|
|
499
|
+
setStats(res[1] || stats);
|
|
490
500
|
}).catch(function(err) { console.error('[TaskPipeline]', err); })
|
|
491
501
|
.finally(function() { setLoading(false); });
|
|
492
502
|
}, [effectiveOrgId]);
|
|
@@ -235,6 +235,30 @@ function PermissionEditor({ userId, userName, currentPerms, pageRegistry, onSave
|
|
|
235
235
|
})
|
|
236
236
|
),
|
|
237
237
|
|
|
238
|
+
// ─── Organization Assignment ──────────────────────
|
|
239
|
+
permOrgs.length > 0 && h('div', { style: { marginTop: 16, padding: '12px 14px', background: 'var(--bg-tertiary)', borderRadius: 8 } },
|
|
240
|
+
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 } },
|
|
241
|
+
h('div', null,
|
|
242
|
+
h('strong', { style: { fontSize: 13 } }, 'Client Organization'),
|
|
243
|
+
h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 1 } }, 'Bind this user to a client org — they will only see that org\'s agents and data')
|
|
244
|
+
)
|
|
245
|
+
),
|
|
246
|
+
h('select', {
|
|
247
|
+
className: 'input',
|
|
248
|
+
value: userOrgId,
|
|
249
|
+
onChange: function(e) { setUserOrgId(e.target.value); },
|
|
250
|
+
style: { fontSize: 13 }
|
|
251
|
+
},
|
|
252
|
+
h('option', { value: '' }, 'None (Internal User)'),
|
|
253
|
+
permOrgs.filter(function(o) { return o.is_active !== false; }).map(function(o) {
|
|
254
|
+
return h('option', { key: o.id, value: o.id }, o.name);
|
|
255
|
+
})
|
|
256
|
+
),
|
|
257
|
+
userOrgId && h('div', { style: { fontSize: 11, color: 'var(--warning, #f59e0b)', marginTop: 6 } },
|
|
258
|
+
'This user will only see agents and data belonging to this organization. The org switcher will be locked for them.'
|
|
259
|
+
)
|
|
260
|
+
),
|
|
261
|
+
|
|
238
262
|
// ─── Agent Access ──────────────────────────
|
|
239
263
|
!fullAccess && h('div', { style: { marginTop: 16 } },
|
|
240
264
|
h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 } },
|
|
@@ -470,6 +494,21 @@ export function UsersPage() {
|
|
|
470
494
|
setResetting(false);
|
|
471
495
|
};
|
|
472
496
|
|
|
497
|
+
var openEditUser = function(u) {
|
|
498
|
+
setEditUser(u);
|
|
499
|
+
setEditForm({ name: u.name || '', role: u.role || 'viewer', clientOrgId: u.clientOrgId || '' });
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
var doEditUser = async function() {
|
|
503
|
+
if (!editUser) return;
|
|
504
|
+
try {
|
|
505
|
+
await apiCall('/users/' + editUser.id, { method: 'PATCH', body: JSON.stringify({ name: editForm.name, role: editForm.role, clientOrgId: editForm.clientOrgId || null }) });
|
|
506
|
+
toast('User updated', 'success');
|
|
507
|
+
setEditUser(null);
|
|
508
|
+
load();
|
|
509
|
+
} catch (e) { toast(e.message || 'Update failed', 'error'); }
|
|
510
|
+
};
|
|
511
|
+
|
|
473
512
|
var toggleActive = async function(user) {
|
|
474
513
|
var action = user.isActive === false ? 'reactivate' : 'deactivate';
|
|
475
514
|
var ok = await showConfirm({
|
|
@@ -491,6 +530,8 @@ export function UsersPage() {
|
|
|
491
530
|
var [deleteStep, setDeleteStep] = useState(0);
|
|
492
531
|
var [deleteTarget, setDeleteTarget] = useState(null);
|
|
493
532
|
var [deleteTyped, setDeleteTyped] = useState('');
|
|
533
|
+
var [editUser, setEditUser] = useState(null);
|
|
534
|
+
var [editForm, setEditForm] = useState({ name: '', role: '', clientOrgId: '' });
|
|
494
535
|
|
|
495
536
|
var startDelete = function(user) { setDeleteTarget(user); setDeleteStep(1); setDeleteTyped(''); };
|
|
496
537
|
var cancelDelete = function() { setDeleteTarget(null); setDeleteStep(0); setDeleteTyped(''); };
|
|
@@ -631,13 +672,60 @@ export function UsersPage() {
|
|
|
631
672
|
),
|
|
632
673
|
|
|
633
674
|
// Permission editor modal
|
|
675
|
+
// Edit User modal
|
|
676
|
+
editUser && h(Modal, {
|
|
677
|
+
title: 'Edit User — ' + (editUser.name || editUser.email),
|
|
678
|
+
onClose: function() { setEditUser(null); },
|
|
679
|
+
width: 420,
|
|
680
|
+
footer: h(Fragment, null,
|
|
681
|
+
h('button', { className: 'btn btn-secondary', onClick: function() { setEditUser(null); } }, 'Cancel'),
|
|
682
|
+
h('button', { className: 'btn btn-primary', onClick: doEditUser }, 'Save Changes')
|
|
683
|
+
)
|
|
684
|
+
},
|
|
685
|
+
h('div', { className: 'form-group' },
|
|
686
|
+
h('label', { className: 'form-label' }, 'Name'),
|
|
687
|
+
h('input', { className: 'input', value: editForm.name, onChange: function(e) { setEditForm(function(f) { return Object.assign({}, f, { name: e.target.value }); }); } })
|
|
688
|
+
),
|
|
689
|
+
h('div', { className: 'form-group' },
|
|
690
|
+
h('label', { className: 'form-label' }, 'Role'),
|
|
691
|
+
h('select', { className: 'input', value: editForm.role, onChange: function(e) { setEditForm(function(f) { return Object.assign({}, f, { role: e.target.value }); }); } },
|
|
692
|
+
h('option', { value: 'viewer' }, 'Viewer'),
|
|
693
|
+
h('option', { value: 'member' }, 'Member'),
|
|
694
|
+
h('option', { value: 'admin' }, 'Admin'),
|
|
695
|
+
h('option', { value: 'owner' }, 'Owner')
|
|
696
|
+
),
|
|
697
|
+
h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } },
|
|
698
|
+
editForm.role === 'owner' ? 'Full access to everything. Cannot be restricted.' :
|
|
699
|
+
editForm.role === 'admin' ? 'Full access by default. Can be restricted via permissions.' :
|
|
700
|
+
editForm.role === 'member' ? 'Access controlled by permissions. Can view and act on assigned pages.' :
|
|
701
|
+
'Read-only access. Can view but not modify.'
|
|
702
|
+
)
|
|
703
|
+
),
|
|
704
|
+
clientOrgs.length > 0 && h('div', { className: 'form-group' },
|
|
705
|
+
h('label', { className: 'form-label' }, 'Client Organization'),
|
|
706
|
+
h('select', { className: 'input', value: editForm.clientOrgId, onChange: function(e) { setEditForm(function(f) { return Object.assign({}, f, { clientOrgId: e.target.value }); }); } },
|
|
707
|
+
h('option', { value: '' }, 'None (Internal User)'),
|
|
708
|
+
clientOrgs.filter(function(o) { return o.is_active !== false; }).map(function(o) {
|
|
709
|
+
return h('option', { key: o.id, value: o.id }, o.name);
|
|
710
|
+
})
|
|
711
|
+
),
|
|
712
|
+
editForm.clientOrgId && h('div', { style: { fontSize: 11, color: 'var(--warning, #f59e0b)', marginTop: 4 } },
|
|
713
|
+
'This user will only see agents and data from this organization.'
|
|
714
|
+
)
|
|
715
|
+
),
|
|
716
|
+
h('div', { style: { fontSize: 12, color: 'var(--text-muted)', padding: '8px 0', borderTop: '1px solid var(--border)', marginTop: 8 } },
|
|
717
|
+
'Email: ', h('code', null, editUser.email), ' (cannot be changed)'
|
|
718
|
+
)
|
|
719
|
+
),
|
|
720
|
+
|
|
634
721
|
permTarget && pageRegistry && h(PermissionEditor, {
|
|
635
722
|
userId: permTarget.id,
|
|
636
723
|
userName: permTarget.name || permTarget.email,
|
|
637
724
|
currentPerms: permGrants,
|
|
638
725
|
pageRegistry: pageRegistry,
|
|
639
726
|
onSave: savePermissions,
|
|
640
|
-
onClose: function() { setPermTarget(null); }
|
|
727
|
+
onClose: function() { setPermTarget(null); },
|
|
728
|
+
userObj: permTarget
|
|
641
729
|
}),
|
|
642
730
|
|
|
643
731
|
// 5-step delete confirmation modal
|
|
@@ -743,6 +831,7 @@ export function UsersPage() {
|
|
|
743
831
|
h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, u.createdAt ? new Date(u.createdAt).toLocaleDateString() : '-'),
|
|
744
832
|
h('td', null,
|
|
745
833
|
h('div', { style: { display: 'flex', gap: 4 } },
|
|
834
|
+
h('button', { className: 'btn btn-ghost btn-sm', title: 'Edit User', onClick: function() { openEditUser(u); } }, I.edit()),
|
|
746
835
|
h('button', {
|
|
747
836
|
className: 'btn btn-ghost btn-sm',
|
|
748
837
|
title: isRestricted ? 'Edit Permissions' : 'Permissions (Owner/Admin have full access)',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h, useState, useEffect, useCallback, Fragment, useApp, engineCall, buildAgentEmailMap, resolveAgentEmail, buildAgentDataMap, renderAgentBadge, getOrgId } from '../components/utils.js';
|
|
1
|
+
import { h, useState, useEffect, useCallback, Fragment, useApp, engineCall, buildAgentEmailMap, resolveAgentEmail, buildAgentDataMap, renderAgentBadge, getOrgId } , apiCall } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { TimezoneSelect } from '../components/timezones.js';
|
|
4
4
|
import { HelpButton } from '../components/help-button.js';
|
|
@@ -47,7 +47,7 @@ export function WorkforcePage() {
|
|
|
47
47
|
setSchedules(schedulesRes.schedules || []);
|
|
48
48
|
setBudgetData(budgetRes);
|
|
49
49
|
setClockRecords(recordsRes.records || []);
|
|
50
|
-
|
|
50
|
+
apiCall('/agents' + (orgCtx.selectedOrgId ? '?clientOrgId=' + orgCtx.selectedOrgId : '')).then(d => setAgents(d.agents || [])).catch(() => {});
|
|
51
51
|
} catch (err) { toast('Failed to load workforce data', 'error'); }
|
|
52
52
|
setLoading(false);
|
|
53
53
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
provision,
|
|
3
3
|
runSetupWizard
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-N5JFOIJN.js";
|
|
5
5
|
import {
|
|
6
6
|
AgenticMailManager,
|
|
7
7
|
GoogleEmailProvider,
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
requireRole,
|
|
43
43
|
securityHeaders,
|
|
44
44
|
validate
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-4QGERCWK.js";
|
|
46
46
|
import "./chunk-OF4MUWWS.js";
|
|
47
47
|
import {
|
|
48
48
|
PROVIDER_REGISTRY,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createServer
|
|
3
|
+
} from "./chunk-4QGERCWK.js";
|
|
4
|
+
import "./chunk-OF4MUWWS.js";
|
|
5
|
+
import "./chunk-UF3ZJMJO.js";
|
|
6
|
+
import "./chunk-3OC6RH7W.js";
|
|
7
|
+
import "./chunk-2DDKGTD6.js";
|
|
8
|
+
import "./chunk-YVK6F5OD.js";
|
|
9
|
+
import "./chunk-MKRNEM5A.js";
|
|
10
|
+
import "./chunk-DRXMYYKN.js";
|
|
11
|
+
import "./chunk-6WSX7QXF.js";
|
|
12
|
+
import "./chunk-KFQGP6VL.js";
|
|
13
|
+
export {
|
|
14
|
+
createServer
|
|
15
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
promptCompanyInfo,
|
|
3
|
+
promptDatabase,
|
|
4
|
+
promptDeployment,
|
|
5
|
+
promptDomain,
|
|
6
|
+
promptRegistration,
|
|
7
|
+
provision,
|
|
8
|
+
runSetupWizard
|
|
9
|
+
} from "./chunk-N5JFOIJN.js";
|
|
10
|
+
import "./chunk-QCGSFWKU.js";
|
|
11
|
+
import "./chunk-KFQGP6VL.js";
|
|
12
|
+
export {
|
|
13
|
+
promptCompanyInfo,
|
|
14
|
+
promptDatabase,
|
|
15
|
+
promptDeployment,
|
|
16
|
+
promptDomain,
|
|
17
|
+
promptRegistration,
|
|
18
|
+
provision,
|
|
19
|
+
runSetupWizard
|
|
20
|
+
};
|
package/package.json
CHANGED
package/src/admin/routes.ts
CHANGED
|
@@ -216,10 +216,16 @@ export function createAdminRoutes(db: DatabaseAdapter) {
|
|
|
216
216
|
|
|
217
217
|
api.get('/agents', async (c) => {
|
|
218
218
|
const status = c.req.query('status') as any;
|
|
219
|
+
const clientOrgId = c.req.query('clientOrgId') || '';
|
|
219
220
|
const limit = Math.min(parseInt(c.req.query('limit') || '50'), 200);
|
|
220
221
|
const offset = Math.max(parseInt(c.req.query('offset') || '0'), 0);
|
|
221
|
-
|
|
222
|
-
|
|
222
|
+
let agents = await db.listAgents({ status, limit, offset });
|
|
223
|
+
let total = await db.countAgents(status);
|
|
224
|
+
// Filter by client org if requested
|
|
225
|
+
if (clientOrgId) {
|
|
226
|
+
agents = agents.filter((a: any) => a.client_org_id === clientOrgId);
|
|
227
|
+
total = agents.length;
|
|
228
|
+
}
|
|
223
229
|
return c.json({ agents, total, limit, offset });
|
|
224
230
|
});
|
|
225
231
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h, useState, useEffect, Fragment, useApp, engineCall, showConfirm, buildAgentEmailMap, buildAgentDataMap, resolveAgentEmail, renderAgentBadge, getOrgId } from '../components/utils.js';
|
|
1
|
+
import { h, useState, useEffect, Fragment, useApp, engineCall, showConfirm, buildAgentEmailMap, buildAgentDataMap, resolveAgentEmail, renderAgentBadge, getOrgId } , apiCall } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { HelpButton } from '../components/help-button.js';
|
|
4
4
|
import { useOrgContext } from '../components/org-switcher.js';
|
|
@@ -16,7 +16,7 @@ export function ApprovalsPage() {
|
|
|
16
16
|
const load = () => {
|
|
17
17
|
engineCall('/approvals/pending').then(d => setPending(d.requests || [])).catch(() => {});
|
|
18
18
|
engineCall('/approvals/history?limit=50').then(d => setHistory(d.requests || [])).catch(() => {});
|
|
19
|
-
|
|
19
|
+
apiCall('/agents' + (orgCtx.selectedOrgId ? '?clientOrgId=' + orgCtx.selectedOrgId : '')).then(d => setAgents(d.agents || [])).catch(() => {});
|
|
20
20
|
};
|
|
21
21
|
useEffect(() => { load(); }, []);
|
|
22
22
|
|