@agenticmail/enterprise 0.5.200 → 0.5.201
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-EGMBRD3R.js +510 -0
- package/dist/chunk-5C3SCMY5.js +4457 -0
- package/dist/chunk-6OZYUTPL.js +1224 -0
- package/dist/chunk-ZR4Z42HT.js +3679 -0
- package/dist/cli-agent-PLMDHMRR.js +1602 -0
- package/dist/cli-serve-PLBAWN7N.js +114 -0
- package/dist/cli.js +3 -3
- package/dist/dashboard/app.js +3 -0
- package/dist/dashboard/components/icons.js +1 -0
- package/dist/dashboard/pages/task-pipeline.js +455 -0
- package/dist/index.js +3 -3
- package/dist/routes-KHABOHOV.js +13273 -0
- package/dist/runtime-6WFHCG3N.js +45 -0
- package/dist/server-BENJQHTB.js +15 -0
- package/dist/setup-7RQIFV5Y.js +20 -0
- package/package.json +1 -1
- package/src/dashboard/app.js +3 -0
- package/src/dashboard/components/icons.js +1 -0
- package/src/dashboard/pages/task-pipeline.js +455 -0
- package/src/engine/model-fallback.ts +141 -0
- package/src/engine/routes.ts +5 -0
- package/src/engine/task-queue-after-spawn.ts +66 -0
- package/src/engine/task-queue-before-spawn.ts +109 -0
- package/src/engine/task-queue-routes.ts +133 -0
- package/src/engine/task-queue.ts +369 -0
|
@@ -0,0 +1,114 @@
|
|
|
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-K32DV2DR.js");
|
|
97
|
+
const { createServer } = await import("./server-BENJQHTB.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
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
runServe
|
|
114
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -53,14 +53,14 @@ Skill Development:
|
|
|
53
53
|
break;
|
|
54
54
|
case "serve":
|
|
55
55
|
case "start":
|
|
56
|
-
import("./cli-serve-
|
|
56
|
+
import("./cli-serve-PLBAWN7N.js").then((m) => m.runServe(args.slice(1))).catch(fatal);
|
|
57
57
|
break;
|
|
58
58
|
case "agent":
|
|
59
|
-
import("./cli-agent-
|
|
59
|
+
import("./cli-agent-PLMDHMRR.js").then((m) => m.runAgent(args.slice(1))).catch(fatal);
|
|
60
60
|
break;
|
|
61
61
|
case "setup":
|
|
62
62
|
default:
|
|
63
|
-
import("./setup-
|
|
63
|
+
import("./setup-7RQIFV5Y.js").then((m) => m.runSetupWizard()).catch(fatal);
|
|
64
64
|
break;
|
|
65
65
|
}
|
|
66
66
|
function fatal(err) {
|
package/dist/dashboard/app.js
CHANGED
|
@@ -25,6 +25,7 @@ import { KnowledgeContributionsPage } from './pages/knowledge-contributions.js';
|
|
|
25
25
|
import { SkillConnectionsPage } from './pages/skill-connections.js';
|
|
26
26
|
import { VaultPage } from './pages/vault.js';
|
|
27
27
|
import { OrgChartPage } from './pages/org-chart.js';
|
|
28
|
+
import { TaskPipelinePage } from './pages/task-pipeline.js';
|
|
28
29
|
|
|
29
30
|
// ─── Toast System ────────────────────────────────────────
|
|
30
31
|
let toastId = 0;
|
|
@@ -161,6 +162,7 @@ function App() {
|
|
|
161
162
|
]},
|
|
162
163
|
{ section: 'Management', items: [
|
|
163
164
|
{ id: 'org-chart', icon: I.orgChart, label: 'Org Chart' },
|
|
165
|
+
{ id: 'task-pipeline', icon: I.workflow, label: 'Task Pipeline' },
|
|
164
166
|
{ id: 'workforce', icon: I.clock, label: 'Workforce' },
|
|
165
167
|
{ id: 'messages', icon: I.messages, label: 'Messages' },
|
|
166
168
|
{ id: 'guardrails', icon: I.guardrails, label: 'Guardrails' },
|
|
@@ -199,6 +201,7 @@ function App() {
|
|
|
199
201
|
'skill-connections': SkillConnectionsPage,
|
|
200
202
|
vault: VaultPage,
|
|
201
203
|
'org-chart': OrgChartPage,
|
|
204
|
+
'task-pipeline': TaskPipelinePage,
|
|
202
205
|
};
|
|
203
206
|
|
|
204
207
|
const navigateToAgent = (agentId) => { _setSelectedAgentId(agentId); history.pushState(null, '', '/dashboard/agents/' + agentId); };
|
|
@@ -43,6 +43,7 @@ export const I = {
|
|
|
43
43
|
link: () => h('svg', S, h('path', { d: 'M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71' }), h('path', { d: 'M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71' })),
|
|
44
44
|
folder: () => h('svg', S, h('path', { d: 'M22 19a2 2 0 01-2 2H4a2 2 0 01-2-2V5a2 2 0 012-2h5l2 3h9a2 2 0 012 2z' })),
|
|
45
45
|
globe: () => h('svg', S, h('circle', { cx: 12, cy: 12, r: 10 }), h('line', { x1: 2, y1: 12, x2: 22, y2: 12 }), h('path', { d: 'M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z' })),
|
|
46
|
+
workflow: () => h('svg', S, h('rect', { x: 3, y: 3, width: 6, height: 6, rx: 1 }), h('rect', { x: 15, y: 3, width: 6, height: 6, rx: 1 }), h('rect', { x: 9, y: 15, width: 6, height: 6, rx: 1 }), h('line', { x1: 9, y1: 6, x2: 15, y2: 6 }), h('line', { x1: 12, y1: 9, x2: 12, y2: 15 }), h('line', { x1: 6, y1: 9, x2: 6, y2: 18 }), h('line', { x1: 6, y1: 18, x2: 9, y2: 18 }), h('line', { x1: 18, y1: 9, x2: 18, y2: 18 }), h('line', { x1: 18, y1: 18, x2: 15, y2: 18 })),
|
|
46
47
|
orgChart: () => h('svg', S, h('rect', { x: 8, y: 2, width: 8, height: 5, rx: 1 }), h('rect', { x: 1, y: 17, width: 8, height: 5, rx: 1 }), h('rect', { x: 15, y: 17, width: 8, height: 5, rx: 1 }), h('line', { x1: 12, y1: 7, x2: 12, y2: 12 }), h('line', { x1: 5, y1: 12, x2: 19, y2: 12 }), h('line', { x1: 5, y1: 12, x2: 5, y2: 17 }), h('line', { x1: 19, y1: 12, x2: 19, y2: 17 })),
|
|
47
48
|
terminal: () => h('svg', S, h('polyline', { points: '4 17 10 11 4 5' }), h('line', { x1: 12, y1: 19, x2: 20, y2: 19 })),
|
|
48
49
|
chart: () => h('svg', S, h('line', { x1: 18, y1: 20, x2: 18, y2: 10 }), h('line', { x1: 12, y1: 20, x2: 12, y2: 4 }), h('line', { x1: 6, y1: 20, x2: 6, y2: 14 })),
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import { h, useState, useEffect, useCallback, useRef, Fragment, useApp, engineCall, getOrgId } from '../components/utils.js';
|
|
2
|
+
import { I } from '../components/icons.js';
|
|
3
|
+
import { HelpButton } from '../components/help-button.js';
|
|
4
|
+
|
|
5
|
+
// ─── Colors ──────────────────────────────────────────────
|
|
6
|
+
const STATUS_COLORS = {
|
|
7
|
+
created: '#6366f1',
|
|
8
|
+
assigned: '#f59e0b',
|
|
9
|
+
in_progress: '#06b6d4',
|
|
10
|
+
completed: '#22c55e',
|
|
11
|
+
failed: '#ef4444',
|
|
12
|
+
cancelled: '#6b7394',
|
|
13
|
+
};
|
|
14
|
+
const PRIORITY_COLORS = { urgent: '#ef4444', high: '#f59e0b', normal: '#6366f1', low: '#6b7394' };
|
|
15
|
+
const CATEGORY_ICONS = { email: '\u2709', research: '\uD83D\uDD0D', meeting: '\uD83D\uDCC5', workflow: '\u2699', writing: '\u270F', deployment: '\uD83D\uDE80', review: '\u2714', monitoring: '\uD83D\uDCE1', custom: '\uD83D\uDCCB' };
|
|
16
|
+
const BG = '#0a0c14';
|
|
17
|
+
const ACCENT = '#6366f1';
|
|
18
|
+
|
|
19
|
+
// ─── Styles ──────────────────────────────────────────────
|
|
20
|
+
const _h4 = { marginTop: 16, marginBottom: 8, fontSize: 14 };
|
|
21
|
+
const _ul = { paddingLeft: 20, margin: '4px 0 8px' };
|
|
22
|
+
const _tip = { marginTop: 12, padding: 12, background: 'var(--bg-secondary, #1e293b)', borderRadius: 'var(--radius, 8px)', fontSize: 13 };
|
|
23
|
+
|
|
24
|
+
// ─── Pipeline Columns (Kanban) ───────────────────────────
|
|
25
|
+
const COLUMNS = [
|
|
26
|
+
{ key: 'created', label: 'Queued', color: STATUS_COLORS.created },
|
|
27
|
+
{ key: 'assigned', label: 'Assigned', color: STATUS_COLORS.assigned },
|
|
28
|
+
{ key: 'in_progress', label: 'In Progress', color: STATUS_COLORS.in_progress },
|
|
29
|
+
{ key: 'completed', label: 'Completed', color: STATUS_COLORS.completed },
|
|
30
|
+
{ key: 'failed', label: 'Failed', color: STATUS_COLORS.failed },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
function formatDuration(ms) {
|
|
34
|
+
if (!ms) return '-';
|
|
35
|
+
var s = Math.floor(ms / 1000);
|
|
36
|
+
if (s < 60) return s + 's';
|
|
37
|
+
var m = Math.floor(s / 60);
|
|
38
|
+
if (m < 60) return m + 'm ' + (s % 60) + 's';
|
|
39
|
+
var hr = Math.floor(m / 60);
|
|
40
|
+
return hr + 'h ' + (m % 60) + 'm';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function timeAgo(ts) {
|
|
44
|
+
if (!ts) return '-';
|
|
45
|
+
var diff = Date.now() - new Date(ts).getTime();
|
|
46
|
+
if (diff < 5000) return 'just now';
|
|
47
|
+
if (diff < 60000) return Math.floor(diff / 1000) + 's ago';
|
|
48
|
+
if (diff < 3600000) return Math.floor(diff / 60000) + 'm ago';
|
|
49
|
+
if (diff < 86400000) return Math.floor(diff / 3600000) + 'h ago';
|
|
50
|
+
return Math.floor(diff / 86400000) + 'd ago';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ─── Task Card Component ─────────────────────────────────
|
|
54
|
+
function TaskCard({ task, onSelect }) {
|
|
55
|
+
var priColor = PRIORITY_COLORS[task.priority] || PRIORITY_COLORS.normal;
|
|
56
|
+
var catIcon = CATEGORY_ICONS[task.category] || CATEGORY_ICONS.custom;
|
|
57
|
+
|
|
58
|
+
return h('div', {
|
|
59
|
+
onClick: function() { onSelect(task); },
|
|
60
|
+
style: {
|
|
61
|
+
background: 'var(--bg-secondary, #111827)',
|
|
62
|
+
border: '1px solid var(--border, #1e293b)',
|
|
63
|
+
borderLeft: '3px solid ' + priColor,
|
|
64
|
+
borderRadius: 'var(--radius, 8px)',
|
|
65
|
+
padding: 12,
|
|
66
|
+
marginBottom: 8,
|
|
67
|
+
cursor: 'pointer',
|
|
68
|
+
transition: 'transform 0.1s, box-shadow 0.1s',
|
|
69
|
+
},
|
|
70
|
+
onMouseEnter: function(e) { e.currentTarget.style.transform = 'translateY(-1px)'; e.currentTarget.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)'; },
|
|
71
|
+
onMouseLeave: function(e) { e.currentTarget.style.transform = ''; e.currentTarget.style.boxShadow = ''; },
|
|
72
|
+
},
|
|
73
|
+
// Header: icon + title
|
|
74
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 6, marginBottom: 6 } },
|
|
75
|
+
h('span', { style: { fontSize: 14 } }, catIcon),
|
|
76
|
+
h('span', { style: { fontSize: 13, fontWeight: 600, flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, task.title)
|
|
77
|
+
),
|
|
78
|
+
// Agent + time
|
|
79
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', fontSize: 11, color: 'var(--text-muted)' } },
|
|
80
|
+
h('span', null, task.assignedToName || task.assignedTo || '-'),
|
|
81
|
+
h('span', null, timeAgo(task.createdAt))
|
|
82
|
+
),
|
|
83
|
+
// Progress bar (if in progress)
|
|
84
|
+
task.status === 'in_progress' && task.progress > 0 && h('div', { style: { marginTop: 6, height: 3, background: 'var(--border)', borderRadius: 2, overflow: 'hidden' } },
|
|
85
|
+
h('div', { style: { height: '100%', width: task.progress + '%', background: STATUS_COLORS.in_progress, borderRadius: 2, transition: 'width 0.3s' } })
|
|
86
|
+
),
|
|
87
|
+
// Tags
|
|
88
|
+
task.tags && task.tags.length > 0 && h('div', { style: { display: 'flex', gap: 4, marginTop: 6, flexWrap: 'wrap' } },
|
|
89
|
+
task.tags.map(function(tag) {
|
|
90
|
+
return h('span', { key: tag, style: { fontSize: 10, padding: '1px 6px', borderRadius: 10, background: 'var(--accent-soft, rgba(99,102,241,0.15))', color: 'var(--accent, #6366f1)' } }, tag);
|
|
91
|
+
})
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─── Task Detail Modal ───────────────────────────────────
|
|
97
|
+
function TaskDetail({ task, onClose, onCancel }) {
|
|
98
|
+
if (!task) return null;
|
|
99
|
+
var statusColor = STATUS_COLORS[task.status] || '#6b7394';
|
|
100
|
+
|
|
101
|
+
return h('div', { className: 'modal-overlay', onClick: onClose },
|
|
102
|
+
h('div', { className: 'modal', onClick: function(e) { e.stopPropagation(); }, style: { width: 560, maxHeight: '80vh', overflow: 'auto' } },
|
|
103
|
+
h('div', { className: 'modal-header' },
|
|
104
|
+
h('h2', null, task.title),
|
|
105
|
+
h('button', { className: 'btn btn-ghost btn-icon', onClick: onClose }, '\u00D7')
|
|
106
|
+
),
|
|
107
|
+
h('div', { className: 'modal-body', style: { padding: 20 } },
|
|
108
|
+
// Status badge
|
|
109
|
+
h('div', { style: { display: 'flex', gap: 8, marginBottom: 16, alignItems: 'center' } },
|
|
110
|
+
h('span', { style: { padding: '3px 10px', borderRadius: 12, fontSize: 12, fontWeight: 600, background: statusColor + '22', color: statusColor, border: '1px solid ' + statusColor + '44' } },
|
|
111
|
+
task.status.replace('_', ' ').toUpperCase()
|
|
112
|
+
),
|
|
113
|
+
h('span', { style: { padding: '3px 10px', borderRadius: 12, fontSize: 12, background: (PRIORITY_COLORS[task.priority] || '#6366f1') + '22', color: PRIORITY_COLORS[task.priority] || '#6366f1' } },
|
|
114
|
+
task.priority.toUpperCase()
|
|
115
|
+
),
|
|
116
|
+
h('span', { style: { padding: '3px 10px', borderRadius: 12, fontSize: 12, background: 'var(--bg-tertiary)', color: 'var(--text-muted)' } },
|
|
117
|
+
(CATEGORY_ICONS[task.category] || '') + ' ' + task.category
|
|
118
|
+
)
|
|
119
|
+
),
|
|
120
|
+
|
|
121
|
+
// Description
|
|
122
|
+
task.description && h('div', { style: { marginBottom: 16, fontSize: 13, lineHeight: 1.6, color: 'var(--text-secondary)' } }, task.description),
|
|
123
|
+
|
|
124
|
+
// Progress
|
|
125
|
+
task.status === 'in_progress' && h('div', { style: { marginBottom: 16 } },
|
|
126
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 4 } },
|
|
127
|
+
h('span', null, 'Progress'),
|
|
128
|
+
h('span', null, task.progress + '%')
|
|
129
|
+
),
|
|
130
|
+
h('div', { style: { height: 6, background: 'var(--border)', borderRadius: 3, overflow: 'hidden' } },
|
|
131
|
+
h('div', { style: { height: '100%', width: task.progress + '%', background: STATUS_COLORS.in_progress, borderRadius: 3 } })
|
|
132
|
+
)
|
|
133
|
+
),
|
|
134
|
+
|
|
135
|
+
// Metadata grid
|
|
136
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px 24px', fontSize: 13, marginBottom: 16 } },
|
|
137
|
+
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Assigned To'), h('div', null, task.assignedToName || task.assignedTo || '-')),
|
|
138
|
+
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Created By'), h('div', null, task.createdByName || task.createdBy || '-')),
|
|
139
|
+
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Created'), h('div', null, task.createdAt ? new Date(task.createdAt).toLocaleString() : '-')),
|
|
140
|
+
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Started'), h('div', null, task.startedAt ? new Date(task.startedAt).toLocaleString() : '-')),
|
|
141
|
+
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Completed'), h('div', null, task.completedAt ? new Date(task.completedAt).toLocaleString() : '-')),
|
|
142
|
+
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Duration'), h('div', null, formatDuration(task.actualDurationMs))),
|
|
143
|
+
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Model'), h('div', null, task.modelUsed || task.model || '-')),
|
|
144
|
+
h('div', null, h('div', { style: { color: 'var(--text-muted)', fontSize: 11, marginBottom: 2 } }, 'Tokens / Cost'), h('div', null, (task.tokensUsed || 0).toLocaleString() + ' / $' + (task.costUsd || 0).toFixed(4)))
|
|
145
|
+
),
|
|
146
|
+
|
|
147
|
+
// Error
|
|
148
|
+
task.error && h('div', { style: { padding: 12, background: 'rgba(239,68,68,0.1)', border: '1px solid rgba(239,68,68,0.3)', borderRadius: 'var(--radius)', marginBottom: 16, fontSize: 13, color: '#ef4444' } },
|
|
149
|
+
h('strong', null, 'Error: '), task.error
|
|
150
|
+
),
|
|
151
|
+
|
|
152
|
+
// Result
|
|
153
|
+
task.result && h('div', { style: { marginBottom: 16 } },
|
|
154
|
+
h('div', { style: { fontSize: 12, color: 'var(--text-muted)', marginBottom: 4 } }, 'Result'),
|
|
155
|
+
h('pre', { style: { fontSize: 12, background: 'var(--bg-tertiary)', padding: 12, borderRadius: 'var(--radius)', overflow: 'auto', maxHeight: 200 } },
|
|
156
|
+
JSON.stringify(task.result, null, 2)
|
|
157
|
+
)
|
|
158
|
+
),
|
|
159
|
+
|
|
160
|
+
// Actions
|
|
161
|
+
(task.status === 'created' || task.status === 'assigned' || task.status === 'in_progress') && h('div', { style: { display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 16, borderTop: '1px solid var(--border)', paddingTop: 16 } },
|
|
162
|
+
h('button', { className: 'btn btn-danger btn-sm', onClick: function() { onCancel(task.id); } }, 'Cancel Task')
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ─── Stats Bar ───────────────────────────────────────────
|
|
170
|
+
function StatsBar({ stats }) {
|
|
171
|
+
var items = [
|
|
172
|
+
{ label: 'Queued', value: stats.created, color: STATUS_COLORS.created },
|
|
173
|
+
{ label: 'Assigned', value: stats.assigned, color: STATUS_COLORS.assigned },
|
|
174
|
+
{ label: 'In Progress', value: stats.inProgress, color: STATUS_COLORS.in_progress },
|
|
175
|
+
{ label: 'Completed', value: stats.completed, color: STATUS_COLORS.completed },
|
|
176
|
+
{ label: 'Failed', value: stats.failed, color: STATUS_COLORS.failed },
|
|
177
|
+
];
|
|
178
|
+
return h('div', { style: { display: 'flex', gap: 16, marginBottom: 20, flexWrap: 'wrap' } },
|
|
179
|
+
items.map(function(it) {
|
|
180
|
+
return h('div', { key: it.label, style: { background: 'var(--bg-secondary)', border: '1px solid var(--border)', borderRadius: 'var(--radius)', padding: '12px 20px', minWidth: 120, textAlign: 'center' } },
|
|
181
|
+
h('div', { style: { fontSize: 28, fontWeight: 700, color: it.color, lineHeight: 1 } }, it.value),
|
|
182
|
+
h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 4 } }, it.label)
|
|
183
|
+
);
|
|
184
|
+
})
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ─── Main Page Component ─────────────────────────────────
|
|
189
|
+
export function TaskPipelinePage() {
|
|
190
|
+
var { toast } = useApp();
|
|
191
|
+
var [tasks, setTasks] = useState([]);
|
|
192
|
+
var [stats, setStats] = useState({ created: 0, assigned: 0, inProgress: 0, completed: 0, failed: 0, cancelled: 0, total: 0 });
|
|
193
|
+
var [selectedTask, setSelectedTask] = useState(null);
|
|
194
|
+
var [view, setView] = useState('kanban'); // 'kanban' | 'list' | 'timeline'
|
|
195
|
+
var [filter, setFilter] = useState('all'); // 'all' | 'active' | 'completed' | 'failed'
|
|
196
|
+
var [loading, setLoading] = useState(true);
|
|
197
|
+
var eventSourceRef = useRef(null);
|
|
198
|
+
|
|
199
|
+
// Fetch initial data
|
|
200
|
+
var loadData = useCallback(function() {
|
|
201
|
+
setLoading(true);
|
|
202
|
+
Promise.all([
|
|
203
|
+
engineCall('/task-pipeline?limit=200'),
|
|
204
|
+
engineCall('/task-pipeline/stats'),
|
|
205
|
+
]).then(function(results) {
|
|
206
|
+
setTasks(results[0]?.tasks || []);
|
|
207
|
+
setStats(results[1] || stats);
|
|
208
|
+
}).catch(function(err) {
|
|
209
|
+
console.error('[TaskPipeline] load error:', err);
|
|
210
|
+
}).finally(function() { setLoading(false); });
|
|
211
|
+
}, []);
|
|
212
|
+
|
|
213
|
+
// SSE for real-time updates
|
|
214
|
+
useEffect(function() {
|
|
215
|
+
loadData();
|
|
216
|
+
|
|
217
|
+
// Connect to SSE stream
|
|
218
|
+
var baseUrl = window.__ENGINE_BASE || '/engine';
|
|
219
|
+
var es = new EventSource(baseUrl + '/task-pipeline/stream');
|
|
220
|
+
eventSourceRef.current = es;
|
|
221
|
+
|
|
222
|
+
es.onmessage = function(e) {
|
|
223
|
+
try {
|
|
224
|
+
var event = JSON.parse(e.data);
|
|
225
|
+
if (event.type === 'init') {
|
|
226
|
+
// Initial state from SSE
|
|
227
|
+
if (event.tasks) setTasks(function(prev) {
|
|
228
|
+
var map = new Map();
|
|
229
|
+
prev.forEach(function(t) { map.set(t.id, t); });
|
|
230
|
+
event.tasks.forEach(function(t) { map.set(t.id, t); });
|
|
231
|
+
return Array.from(map.values()).sort(function(a, b) { return new Date(b.createdAt) - new Date(a.createdAt); });
|
|
232
|
+
});
|
|
233
|
+
if (event.stats) setStats(event.stats);
|
|
234
|
+
} else if (event.task) {
|
|
235
|
+
// Real-time task event
|
|
236
|
+
setTasks(function(prev) {
|
|
237
|
+
var idx = prev.findIndex(function(t) { return t.id === event.task.id; });
|
|
238
|
+
if (idx >= 0) {
|
|
239
|
+
var next = prev.slice();
|
|
240
|
+
next[idx] = event.task;
|
|
241
|
+
return next;
|
|
242
|
+
}
|
|
243
|
+
return [event.task].concat(prev);
|
|
244
|
+
});
|
|
245
|
+
// Update stats locally
|
|
246
|
+
setStats(function(prev) {
|
|
247
|
+
var s = Object.assign({}, prev);
|
|
248
|
+
if (event.type === 'task_created') { s.created++; s.total++; }
|
|
249
|
+
else if (event.type === 'task_completed') { s.completed++; s.inProgress = Math.max(0, s.inProgress - 1); }
|
|
250
|
+
else if (event.type === 'task_failed') { s.failed++; s.inProgress = Math.max(0, s.inProgress - 1); }
|
|
251
|
+
return s;
|
|
252
|
+
});
|
|
253
|
+
// Update selected task if viewing it
|
|
254
|
+
setSelectedTask(function(prev) {
|
|
255
|
+
if (prev && prev.id === event.task.id) return event.task;
|
|
256
|
+
return prev;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
} catch (err) { /* ignore parse errors */ }
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
es.onerror = function() {
|
|
263
|
+
// Reconnect handled by browser
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
return function() {
|
|
267
|
+
if (es) es.close();
|
|
268
|
+
};
|
|
269
|
+
}, []);
|
|
270
|
+
|
|
271
|
+
// Periodic stats refresh
|
|
272
|
+
useEffect(function() {
|
|
273
|
+
var interval = setInterval(function() {
|
|
274
|
+
engineCall('/task-pipeline/stats').then(function(s) { if (s) setStats(s); }).catch(function() {});
|
|
275
|
+
}, 30000);
|
|
276
|
+
return function() { clearInterval(interval); };
|
|
277
|
+
}, []);
|
|
278
|
+
|
|
279
|
+
var cancelTask = useCallback(function(taskId) {
|
|
280
|
+
engineCall('/task-pipeline/' + taskId + '/cancel', { method: 'POST' })
|
|
281
|
+
.then(function(res) {
|
|
282
|
+
toast('Task cancelled', 'success');
|
|
283
|
+
setSelectedTask(null);
|
|
284
|
+
loadData();
|
|
285
|
+
})
|
|
286
|
+
.catch(function(err) { toast(err.message || 'Failed to cancel', 'error'); });
|
|
287
|
+
}, []);
|
|
288
|
+
|
|
289
|
+
// Filter tasks
|
|
290
|
+
var filteredTasks = tasks.filter(function(t) {
|
|
291
|
+
if (filter === 'active') return t.status === 'created' || t.status === 'assigned' || t.status === 'in_progress';
|
|
292
|
+
if (filter === 'completed') return t.status === 'completed';
|
|
293
|
+
if (filter === 'failed') return t.status === 'failed' || t.status === 'cancelled';
|
|
294
|
+
return true;
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Group by status for kanban
|
|
298
|
+
var columns = {};
|
|
299
|
+
COLUMNS.forEach(function(col) { columns[col.key] = []; });
|
|
300
|
+
filteredTasks.forEach(function(t) {
|
|
301
|
+
if (columns[t.status]) columns[t.status].push(t);
|
|
302
|
+
else if (t.status === 'cancelled' && columns.failed) columns.failed.push(t);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
if (loading) {
|
|
306
|
+
return h('div', { style: { padding: 40, textAlign: 'center', color: 'var(--text-muted)' } }, 'Loading task pipeline...');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return h(Fragment, null,
|
|
310
|
+
// ─── Header ────────────────────────────────────────
|
|
311
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
|
|
312
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 12 } },
|
|
313
|
+
h('h1', { style: { fontSize: 22, fontWeight: 700 } }, I.workflow(), ' Task Pipeline'),
|
|
314
|
+
h(HelpButton, { title: 'Task Pipeline' },
|
|
315
|
+
h('p', null, 'The centralized task pipeline shows every task assigned to agents across your organization in real-time.'),
|
|
316
|
+
h('h4', { style: _h4 }, 'How It Works'),
|
|
317
|
+
h('ul', { style: _ul },
|
|
318
|
+
h('li', null, 'Tasks are automatically recorded when agents are spawned for work'),
|
|
319
|
+
h('li', null, 'The pipeline tracks task status from creation through completion'),
|
|
320
|
+
h('li', null, 'Real-time updates via SSE — no need to refresh'),
|
|
321
|
+
h('li', null, 'Smart metadata extraction categorizes tasks automatically')
|
|
322
|
+
),
|
|
323
|
+
h('h4', { style: _h4 }, 'Task Lifecycle'),
|
|
324
|
+
h('ul', { style: _ul },
|
|
325
|
+
h('li', null, h('strong', null, 'Queued'), ' — Task created, waiting to be assigned'),
|
|
326
|
+
h('li', null, h('strong', null, 'Assigned'), ' — Agent selected, about to start'),
|
|
327
|
+
h('li', null, h('strong', null, 'In Progress'), ' — Agent actively working'),
|
|
328
|
+
h('li', null, h('strong', null, 'Completed'), ' — Task finished successfully'),
|
|
329
|
+
h('li', null, h('strong', null, 'Failed'), ' — Task encountered an error')
|
|
330
|
+
),
|
|
331
|
+
h('div', { style: _tip }, 'Tip: Click any task card to see full details including model used, tokens consumed, duration, and results.')
|
|
332
|
+
)
|
|
333
|
+
),
|
|
334
|
+
h('div', { style: { display: 'flex', gap: 8 } },
|
|
335
|
+
// View toggle
|
|
336
|
+
h('div', { style: { display: 'flex', border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } },
|
|
337
|
+
['kanban', 'list'].map(function(v) {
|
|
338
|
+
return h('button', {
|
|
339
|
+
key: v,
|
|
340
|
+
className: 'btn btn-sm ' + (view === v ? 'btn-primary' : 'btn-ghost'),
|
|
341
|
+
onClick: function() { setView(v); },
|
|
342
|
+
style: { borderRadius: 0, textTransform: 'capitalize', fontSize: 12 }
|
|
343
|
+
}, v);
|
|
344
|
+
})
|
|
345
|
+
),
|
|
346
|
+
// Filter
|
|
347
|
+
h('select', {
|
|
348
|
+
className: 'form-control',
|
|
349
|
+
value: filter,
|
|
350
|
+
onChange: function(e) { setFilter(e.target.value); },
|
|
351
|
+
style: { fontSize: 12, padding: '4px 8px', width: 'auto' }
|
|
352
|
+
},
|
|
353
|
+
h('option', { value: 'all' }, 'All Tasks'),
|
|
354
|
+
h('option', { value: 'active' }, 'Active Only'),
|
|
355
|
+
h('option', { value: 'completed' }, 'Completed'),
|
|
356
|
+
h('option', { value: 'failed' }, 'Failed / Cancelled')
|
|
357
|
+
),
|
|
358
|
+
h('button', { className: 'btn btn-secondary btn-sm', onClick: loadData }, I.refresh(), ' Refresh')
|
|
359
|
+
)
|
|
360
|
+
),
|
|
361
|
+
|
|
362
|
+
// ─── Stats Bar ─────────────────────────────────────
|
|
363
|
+
h(StatsBar, { stats: stats }),
|
|
364
|
+
|
|
365
|
+
// ─── Live indicator ────────────────────────────────
|
|
366
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 6, marginBottom: 16, fontSize: 12, color: 'var(--text-muted)' } },
|
|
367
|
+
h('div', { style: { width: 8, height: 8, borderRadius: '50%', background: '#22c55e', animation: 'pulse 2s infinite' } }),
|
|
368
|
+
'Live — updates in real-time',
|
|
369
|
+
h('span', { style: { marginLeft: 'auto' } }, filteredTasks.length + ' tasks')
|
|
370
|
+
),
|
|
371
|
+
|
|
372
|
+
// ─── Kanban View ───────────────────────────────────
|
|
373
|
+
view === 'kanban' && h('div', {
|
|
374
|
+
style: {
|
|
375
|
+
display: 'grid',
|
|
376
|
+
gridTemplateColumns: 'repeat(' + COLUMNS.length + ', 1fr)',
|
|
377
|
+
gap: 12,
|
|
378
|
+
minHeight: 400,
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
COLUMNS.map(function(col) {
|
|
382
|
+
var colTasks = columns[col.key] || [];
|
|
383
|
+
return h('div', { key: col.key, style: { background: 'var(--bg-primary)', border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden' } },
|
|
384
|
+
// Column header
|
|
385
|
+
h('div', { style: { padding: '10px 12px', borderBottom: '2px solid ' + col.color, display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
|
|
386
|
+
h('span', { style: { fontSize: 13, fontWeight: 600 } }, col.label),
|
|
387
|
+
h('span', { style: { fontSize: 11, padding: '2px 8px', borderRadius: 10, background: col.color + '22', color: col.color, fontWeight: 600 } }, colTasks.length)
|
|
388
|
+
),
|
|
389
|
+
// Cards
|
|
390
|
+
h('div', { style: { padding: 8, maxHeight: 500, overflowY: 'auto' } },
|
|
391
|
+
colTasks.length === 0
|
|
392
|
+
? h('div', { style: { padding: 20, textAlign: 'center', color: 'var(--text-muted)', fontSize: 12 } }, 'No tasks')
|
|
393
|
+
: colTasks.map(function(t) {
|
|
394
|
+
return h(TaskCard, { key: t.id, task: t, onSelect: setSelectedTask });
|
|
395
|
+
})
|
|
396
|
+
)
|
|
397
|
+
);
|
|
398
|
+
})
|
|
399
|
+
),
|
|
400
|
+
|
|
401
|
+
// ─── List View ─────────────────────────────────────
|
|
402
|
+
view === 'list' && h('div', { className: 'card' },
|
|
403
|
+
h('div', { style: { overflowX: 'auto' } },
|
|
404
|
+
h('table', { className: 'table' },
|
|
405
|
+
h('thead', null,
|
|
406
|
+
h('tr', null,
|
|
407
|
+
h('th', null, 'Task'),
|
|
408
|
+
h('th', null, 'Agent'),
|
|
409
|
+
h('th', null, 'Status'),
|
|
410
|
+
h('th', null, 'Priority'),
|
|
411
|
+
h('th', null, 'Category'),
|
|
412
|
+
h('th', null, 'Created'),
|
|
413
|
+
h('th', null, 'Duration'),
|
|
414
|
+
h('th', null, 'Model')
|
|
415
|
+
)
|
|
416
|
+
),
|
|
417
|
+
h('tbody', null,
|
|
418
|
+
filteredTasks.length === 0
|
|
419
|
+
? h('tr', null, h('td', { colSpan: 8, style: { textAlign: 'center', color: 'var(--text-muted)', padding: 40 } }, 'No tasks found'))
|
|
420
|
+
: filteredTasks.map(function(t) {
|
|
421
|
+
var statusColor = STATUS_COLORS[t.status] || '#6b7394';
|
|
422
|
+
return h('tr', {
|
|
423
|
+
key: t.id,
|
|
424
|
+
onClick: function() { setSelectedTask(t); },
|
|
425
|
+
style: { cursor: 'pointer' }
|
|
426
|
+
},
|
|
427
|
+
h('td', { style: { maxWidth: 250, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontWeight: 500 } }, t.title),
|
|
428
|
+
h('td', { style: { fontSize: 13 } }, t.assignedToName || '-'),
|
|
429
|
+
h('td', null,
|
|
430
|
+
h('span', { style: { padding: '2px 8px', borderRadius: 10, fontSize: 11, fontWeight: 600, background: statusColor + '22', color: statusColor } },
|
|
431
|
+
t.status.replace('_', ' ')
|
|
432
|
+
)
|
|
433
|
+
),
|
|
434
|
+
h('td', null,
|
|
435
|
+
h('span', { style: { fontSize: 11, color: PRIORITY_COLORS[t.priority] || '#6366f1' } }, t.priority)
|
|
436
|
+
),
|
|
437
|
+
h('td', { style: { fontSize: 12 } }, (CATEGORY_ICONS[t.category] || '') + ' ' + t.category),
|
|
438
|
+
h('td', { style: { fontSize: 12, color: 'var(--text-muted)' } }, timeAgo(t.createdAt)),
|
|
439
|
+
h('td', { style: { fontSize: 12 } }, formatDuration(t.actualDurationMs)),
|
|
440
|
+
h('td', { style: { fontSize: 12, fontFamily: 'var(--font-mono)' } }, t.modelUsed || t.model || '-')
|
|
441
|
+
);
|
|
442
|
+
})
|
|
443
|
+
)
|
|
444
|
+
)
|
|
445
|
+
)
|
|
446
|
+
),
|
|
447
|
+
|
|
448
|
+
// ─── Task Detail Modal ─────────────────────────────
|
|
449
|
+
selectedTask && h(TaskDetail, {
|
|
450
|
+
task: selectedTask,
|
|
451
|
+
onClose: function() { setSelectedTask(null); },
|
|
452
|
+
onCancel: cancelTask
|
|
453
|
+
})
|
|
454
|
+
);
|
|
455
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
provision,
|
|
9
9
|
runSetupWizard
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-6OZYUTPL.js";
|
|
11
11
|
import {
|
|
12
12
|
AgenticMailManager,
|
|
13
13
|
GoogleEmailProvider,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
executeTool,
|
|
29
29
|
runAgentLoop,
|
|
30
30
|
toolsToDefinitions
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-5C3SCMY5.js";
|
|
32
32
|
import {
|
|
33
33
|
ValidationError,
|
|
34
34
|
auditLogger,
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
requireRole,
|
|
43
43
|
securityHeaders,
|
|
44
44
|
validate
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-ZR4Z42HT.js";
|
|
46
46
|
import "./chunk-OF4MUWWS.js";
|
|
47
47
|
import {
|
|
48
48
|
PROVIDER_REGISTRY,
|