@agenticmail/enterprise 0.5.280 → 0.5.282
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-HVKMZXEP.js +510 -0
- package/dist/agent-tools-FXT4DWIT.js +7 -0
- package/dist/agent-tools-VQ3MX746.js +13881 -0
- package/dist/browser-tool-VRCAS76F.js +4003 -0
- package/dist/chunk-5Z4QW7FI.js +1036 -0
- package/dist/chunk-KPGXRGQ4.js +4735 -0
- package/dist/chunk-O3RXJSHR.js +3780 -0
- package/dist/chunk-P5EGIVSM.js +1224 -0
- package/dist/chunk-V3AM4LOU.js +2594 -0
- package/dist/chunk-XPOPU7F4.js +177 -0
- package/dist/cli-agent-5OF74Z3A.js +1778 -0
- package/dist/cli-serve-756HRJOT.js +114 -0
- package/dist/cli.js +3 -3
- package/dist/connection-manager-UHQRYZV2.js +7 -0
- package/dist/dashboard/pages/database-access.js +213 -46
- package/dist/index.js +23 -23
- package/dist/meetings-RR72OBSV.js +1 -1
- package/dist/routes-YARSSU26.js +13695 -0
- package/dist/runtime-RILCPBIB.js +45 -0
- package/dist/server-XVIBYRNU.js +15 -0
- package/dist/setup-DAT4LP7A.js +20 -0
- package/package.json +1 -1
- package/src/agent-tools/index.ts +11 -0
- package/src/agent-tools/types.ts +2 -0
- package/src/cli-agent.ts +13 -0
- package/src/dashboard/pages/database-access.js +213 -46
- package/src/database-access/connection-manager.ts +28 -0
- package/src/database-access/routes.ts +23 -1
- package/src/runtime/index.ts +4 -0
- package/src/runtime/types.ts +2 -0
- package/src/server.ts +6 -4
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentRuntime,
|
|
3
|
+
EmailChannel,
|
|
4
|
+
FollowUpScheduler,
|
|
5
|
+
SessionManager,
|
|
6
|
+
SubAgentManager,
|
|
7
|
+
ToolRegistry,
|
|
8
|
+
callLLM,
|
|
9
|
+
createAgentRuntime,
|
|
10
|
+
createNoopHooks,
|
|
11
|
+
createRuntimeHooks,
|
|
12
|
+
estimateMessageTokens,
|
|
13
|
+
estimateTokens,
|
|
14
|
+
executeTool,
|
|
15
|
+
runAgentLoop,
|
|
16
|
+
toolsToDefinitions
|
|
17
|
+
} from "./chunk-KPGXRGQ4.js";
|
|
18
|
+
import {
|
|
19
|
+
PROVIDER_REGISTRY,
|
|
20
|
+
listAllProviders,
|
|
21
|
+
resolveApiKeyForProvider,
|
|
22
|
+
resolveProvider
|
|
23
|
+
} from "./chunk-UF3ZJMJO.js";
|
|
24
|
+
import "./chunk-KFQGP6VL.js";
|
|
25
|
+
export {
|
|
26
|
+
AgentRuntime,
|
|
27
|
+
EmailChannel,
|
|
28
|
+
FollowUpScheduler,
|
|
29
|
+
PROVIDER_REGISTRY,
|
|
30
|
+
SessionManager,
|
|
31
|
+
SubAgentManager,
|
|
32
|
+
ToolRegistry,
|
|
33
|
+
callLLM,
|
|
34
|
+
createAgentRuntime,
|
|
35
|
+
createNoopHooks,
|
|
36
|
+
createRuntimeHooks,
|
|
37
|
+
estimateMessageTokens,
|
|
38
|
+
estimateTokens,
|
|
39
|
+
executeTool,
|
|
40
|
+
listAllProviders,
|
|
41
|
+
resolveApiKeyForProvider,
|
|
42
|
+
resolveProvider,
|
|
43
|
+
runAgentLoop,
|
|
44
|
+
toolsToDefinitions
|
|
45
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createServer
|
|
3
|
+
} from "./chunk-O3RXJSHR.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-P5EGIVSM.js";
|
|
10
|
+
import "./chunk-ULRBF2T7.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/agent-tools/index.ts
CHANGED
|
@@ -305,6 +305,17 @@ export async function createAllTools(options?: AllToolsOptions): Promise<AnyAgen
|
|
|
305
305
|
var visualMemoryTools = createVisualMemoryTools(options || {});
|
|
306
306
|
rawTools = rawTools.concat(visualMemoryTools as any);
|
|
307
307
|
|
|
308
|
+
// Enterprise Database Access tools (real multi-DB connections via DatabaseConnectionManager)
|
|
309
|
+
if ((options as any)?.databaseManager && options?.agentId) {
|
|
310
|
+
try {
|
|
311
|
+
const { createDatabaseTools: createDbAccessTools } = await import('../database-access/agent-tools.js');
|
|
312
|
+
const dbAccessTools = createDbAccessTools((options as any).databaseManager, options.agentId);
|
|
313
|
+
rawTools = rawTools.concat(dbAccessTools as any);
|
|
314
|
+
} catch (e: any) {
|
|
315
|
+
console.warn('[tools] Failed to load database access tools:', e.message);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
308
319
|
// Enterprise tools (7 skills)
|
|
309
320
|
var enterpriseTools: AnyAgentTool[] = [
|
|
310
321
|
...createDatabaseTools(options),
|
package/src/agent-tools/types.ts
CHANGED
|
@@ -128,6 +128,8 @@ export interface ToolCreationOptions {
|
|
|
128
128
|
};
|
|
129
129
|
/** Engine database reference for tools that need DB access */
|
|
130
130
|
engineDb?: any;
|
|
131
|
+
/** Database Connection Manager for enterprise database access */
|
|
132
|
+
databaseManager?: any;
|
|
131
133
|
/** Middleware configuration for cross-cutting concerns */
|
|
132
134
|
middleware?: {
|
|
133
135
|
audit?: {
|
package/src/cli-agent.ts
CHANGED
|
@@ -586,6 +586,19 @@ export async function runAgent(_args: string[]) {
|
|
|
586
586
|
console.warn(`[agent] MCP Process Manager init failed (non-fatal): ${e.message}`);
|
|
587
587
|
}
|
|
588
588
|
|
|
589
|
+
// ─── Database Connection Manager ───────────────────
|
|
590
|
+
// Enables agents to query external databases they've been granted access to
|
|
591
|
+
try {
|
|
592
|
+
const { DatabaseConnectionManager } = await import('./database-access/connection-manager.js');
|
|
593
|
+
const vault = (runtime as any).config?.vault;
|
|
594
|
+
const dbManager = new DatabaseConnectionManager({ vault });
|
|
595
|
+
await dbManager.setDb(engineDb);
|
|
596
|
+
(runtime as any).config.databaseManager = dbManager;
|
|
597
|
+
console.log(`[agent] Database Connection Manager started`);
|
|
598
|
+
} catch (e: any) {
|
|
599
|
+
console.warn(`[agent] Database Connection Manager init failed (non-fatal): ${e.message}`);
|
|
600
|
+
}
|
|
601
|
+
|
|
589
602
|
await runtime.start();
|
|
590
603
|
const runtimeApp = runtime.getApp();
|
|
591
604
|
|
|
@@ -175,9 +175,12 @@ export function DatabaseAccessPage() {
|
|
|
175
175
|
|
|
176
176
|
// Tabs
|
|
177
177
|
h('div', { style: s.tabs },
|
|
178
|
-
h('div', { style: tab === 'connections' ? s.tabActive : s.tab, onClick: function() { setTab('connections'); } },
|
|
179
|
-
|
|
180
|
-
h('div', { style: tab === '
|
|
178
|
+
h('div', { style: tab === 'connections' ? s.tabActive : s.tab, onClick: function() { setTab('connections'); } },
|
|
179
|
+
h('span', { style: css('display: inline-flex; align-items: center; gap: 6px;') }, h('span', { style: css('display: flex; transform: scale(0.7);') }, I.database()), 'Connections')),
|
|
180
|
+
h('div', { style: tab === 'agents' ? s.tabActive : s.tab, onClick: function() { setTab('agents'); } },
|
|
181
|
+
h('span', { style: css('display: inline-flex; align-items: center; gap: 6px;') }, h('span', { style: css('display: flex; transform: scale(0.7);') }, I.shield()), 'Agent Access')),
|
|
182
|
+
h('div', { style: tab === 'audit' ? s.tabActive : s.tab, onClick: function() { setTab('audit'); } },
|
|
183
|
+
h('span', { style: css('display: inline-flex; align-items: center; gap: 6px;') }, h('span', { style: css('display: flex; transform: scale(0.7);') }, I.audit()), 'Audit Log')),
|
|
181
184
|
),
|
|
182
185
|
|
|
183
186
|
// Content
|
|
@@ -305,7 +308,45 @@ function AgentAccessTab(props) {
|
|
|
305
308
|
|
|
306
309
|
// ─── Audit Tab ───────────────────────────────────────────────────────────────
|
|
307
310
|
|
|
311
|
+
var AUDIT_PAGE_SIZE = 15;
|
|
312
|
+
|
|
308
313
|
function AuditTab(props) {
|
|
314
|
+
var [search, setSearch] = useState('');
|
|
315
|
+
var [opFilter, setOpFilter] = useState('all');
|
|
316
|
+
var [statusFilter, setStatusFilter] = useState('all');
|
|
317
|
+
var [agentFilter, setAgentFilter] = useState('all');
|
|
318
|
+
var [page, setPage] = useState(0);
|
|
319
|
+
var [expanded, setExpanded] = useState(null);
|
|
320
|
+
|
|
321
|
+
// Get unique agents from audit log
|
|
322
|
+
var agents = [];
|
|
323
|
+
var agentSet = {};
|
|
324
|
+
props.auditLog.forEach(function(e) {
|
|
325
|
+
var name = e.agent_name || e.agent_id;
|
|
326
|
+
if (name && !agentSet[name]) { agentSet[name] = true; agents.push(name); }
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Filter entries
|
|
330
|
+
var filtered = props.auditLog.filter(function(e) {
|
|
331
|
+
if (opFilter !== 'all' && e.operation !== opFilter) return false;
|
|
332
|
+
if (statusFilter === 'ok' && !e.success) return false;
|
|
333
|
+
if (statusFilter === 'fail' && e.success) return false;
|
|
334
|
+
if (agentFilter !== 'all' && (e.agent_name || e.agent_id) !== agentFilter) return false;
|
|
335
|
+
if (search) {
|
|
336
|
+
var q = search.toLowerCase();
|
|
337
|
+
var haystack = ((e.query || '') + ' ' + (e.agent_name || '') + ' ' + (e.connection_name || '') + ' ' + (e.error || '')).toLowerCase();
|
|
338
|
+
if (haystack.indexOf(q) < 0) return false;
|
|
339
|
+
}
|
|
340
|
+
return true;
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
var totalPages = Math.max(1, Math.ceil(filtered.length / AUDIT_PAGE_SIZE));
|
|
344
|
+
if (page >= totalPages) page = totalPages - 1;
|
|
345
|
+
var paged = filtered.slice(page * AUDIT_PAGE_SIZE, (page + 1) * AUDIT_PAGE_SIZE);
|
|
346
|
+
|
|
347
|
+
// Reset page when filters change
|
|
348
|
+
var resetPage = function() { setPage(0); };
|
|
349
|
+
|
|
309
350
|
if (props.auditLog.length === 0) {
|
|
310
351
|
return h('div', { style: s.emptyState },
|
|
311
352
|
h('div', { style: s.emptyIcon }, I.audit()),
|
|
@@ -314,37 +355,114 @@ function AuditTab(props) {
|
|
|
314
355
|
);
|
|
315
356
|
}
|
|
316
357
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
h('
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
358
|
+
var opColor = function(op) {
|
|
359
|
+
return op === 'read' ? 'var(--success)' : op === 'write' ? 'var(--warning)' : op === 'delete' ? 'var(--danger)' : op === 'schema' ? 'var(--accent)' : 'var(--text-muted)';
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
var filterBar = css('display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; align-items: center;');
|
|
363
|
+
var filterSelect = css('padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--bg-secondary); color: var(--text-primary); font-size: 12px;');
|
|
364
|
+
var searchInput = css('padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); background: var(--bg-primary); color: var(--text-primary); font-size: 12px; flex: 1; min-width: 180px;');
|
|
365
|
+
var countBadge = css('font-size: 11px; color: var(--text-muted); margin-left: auto; white-space: nowrap;');
|
|
366
|
+
|
|
367
|
+
return h('div', null,
|
|
368
|
+
// Filter bar
|
|
369
|
+
h('div', { style: filterBar },
|
|
370
|
+
h('div', { style: css('display: flex; align-items: center; gap: 4px; color: var(--text-muted);') },
|
|
371
|
+
h('span', { style: css('display: flex; transform: scale(0.65);') }, I.search()),
|
|
372
|
+
),
|
|
373
|
+
h('input', { style: searchInput, placeholder: 'Search queries, agents, databases, errors...', value: search, onInput: function(e) { setSearch(e.target.value); resetPage(); } }),
|
|
374
|
+
h('select', { style: filterSelect, value: opFilter, onChange: function(e) { setOpFilter(e.target.value); resetPage(); } },
|
|
375
|
+
h('option', { value: 'all' }, 'All Operations'),
|
|
376
|
+
h('option', { value: 'read' }, 'Read'),
|
|
377
|
+
h('option', { value: 'write' }, 'Write'),
|
|
378
|
+
h('option', { value: 'delete' }, 'Delete'),
|
|
379
|
+
h('option', { value: 'schema' }, 'Schema'),
|
|
380
|
+
h('option', { value: 'execute' }, 'Execute'),
|
|
381
|
+
),
|
|
382
|
+
h('select', { style: filterSelect, value: statusFilter, onChange: function(e) { setStatusFilter(e.target.value); resetPage(); } },
|
|
383
|
+
h('option', { value: 'all' }, 'All Status'),
|
|
384
|
+
h('option', { value: 'ok' }, 'Success'),
|
|
385
|
+
h('option', { value: 'fail' }, 'Failed'),
|
|
386
|
+
),
|
|
387
|
+
agents.length > 1 && h('select', { style: filterSelect, value: agentFilter, onChange: function(e) { setAgentFilter(e.target.value); resetPage(); } },
|
|
388
|
+
h('option', { value: 'all' }, 'All Agents'),
|
|
389
|
+
agents.map(function(a) { return h('option', { key: a, value: a }, a); })
|
|
390
|
+
),
|
|
391
|
+
h('span', { style: countBadge }, filtered.length + ' of ' + props.auditLog.length + ' entries'),
|
|
392
|
+
h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '11px' }), onClick: props.onRefresh }, 'Refresh'),
|
|
393
|
+
),
|
|
394
|
+
|
|
395
|
+
// Table
|
|
396
|
+
h('div', { style: css('overflow-x: auto; border: 1px solid var(--border); border-radius: 8px;') },
|
|
397
|
+
h('table', { style: s.auditTable },
|
|
398
|
+
h('thead', null, h('tr', null,
|
|
399
|
+
h('th', { style: s.auditTh }, 'Time'),
|
|
400
|
+
h('th', { style: s.auditTh }, 'Agent'),
|
|
401
|
+
h('th', { style: s.auditTh }, 'Database'),
|
|
402
|
+
h('th', { style: s.auditTh }, 'Operation'),
|
|
403
|
+
h('th', { style: s.auditTh }, 'Query'),
|
|
404
|
+
h('th', { style: s.auditTh }, 'Rows'),
|
|
405
|
+
h('th', { style: s.auditTh }, 'Latency'),
|
|
406
|
+
h('th', { style: s.auditTh }, 'Status'),
|
|
407
|
+
)),
|
|
408
|
+
h('tbody', null,
|
|
409
|
+
paged.length === 0 && h('tr', null,
|
|
410
|
+
h('td', { colSpan: 8, style: Object.assign({}, s.auditTd, { textAlign: 'center', color: 'var(--text-muted)', padding: '24px' }) }, 'No entries match your filters')
|
|
411
|
+
),
|
|
412
|
+
paged.map(function(entry) {
|
|
413
|
+
var isExpanded = expanded === entry.id;
|
|
414
|
+
return h(Fragment, { key: entry.id },
|
|
415
|
+
h('tr', { style: css('cursor: pointer; transition: background 0.1s;'), onClick: function() { setExpanded(isExpanded ? null : entry.id); } },
|
|
416
|
+
h('td', { style: s.auditTd }, new Date(entry.timestamp).toLocaleString()),
|
|
417
|
+
h('td', { style: Object.assign({}, s.auditTd, { fontWeight: 500 }) }, entry.agent_name || (entry.agent_id ? entry.agent_id.slice(0, 8) + '...' : '—')),
|
|
418
|
+
h('td', { style: s.auditTd }, entry.connection_name || (entry.connection_id ? entry.connection_id.slice(0, 8) + '...' : '—')),
|
|
419
|
+
h('td', { style: Object.assign({}, s.auditTd, { fontWeight: 600, color: opColor(entry.operation) }) }, entry.operation),
|
|
420
|
+
h('td', { style: Object.assign({}, s.auditTd, { fontFamily: 'monospace', fontSize: '11px', maxWidth: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }) }, entry.query),
|
|
421
|
+
h('td', { style: Object.assign({}, s.auditTd, { textAlign: 'right' }) }, entry.rows_affected != null ? entry.rows_affected : '—'),
|
|
422
|
+
h('td', { style: Object.assign({}, s.auditTd, { textAlign: 'right', whiteSpace: 'nowrap' }) }, entry.execution_time_ms != null ? entry.execution_time_ms + 'ms' : '—'),
|
|
423
|
+
h('td', { style: s.auditTd }, entry.success
|
|
424
|
+
? h('span', { style: Object.assign({}, s.badge, s.badgeActive) }, 'OK')
|
|
425
|
+
: h('span', { style: Object.assign({}, s.badge, s.badgeError) }, 'FAIL')
|
|
426
|
+
),
|
|
427
|
+
),
|
|
428
|
+
// Expanded row detail
|
|
429
|
+
isExpanded && h('tr', null,
|
|
430
|
+
h('td', { colSpan: 8, style: css('padding: 12px 16px; background: var(--bg-secondary); border-bottom: 1px solid var(--border);') },
|
|
431
|
+
h('div', { style: css('display: grid; grid-template-columns: 1fr 1fr; gap: 12px; font-size: 12px;') },
|
|
432
|
+
h('div', null,
|
|
433
|
+
h('div', { style: css('font-weight: 600; margin-bottom: 4px; color: var(--text-muted);') }, 'Full Query'),
|
|
434
|
+
h('pre', { style: css('margin: 0; padding: 8px; background: var(--bg-primary); border-radius: 6px; overflow-x: auto; font-size: 11px; white-space: pre-wrap; word-break: break-all; max-height: 200px;') }, entry.query || '—'),
|
|
435
|
+
),
|
|
436
|
+
h('div', null,
|
|
437
|
+
h('div', { style: css('font-weight: 600; margin-bottom: 4px; color: var(--text-muted);') }, 'Details'),
|
|
438
|
+
h('div', { style: css('display: flex; flex-direction: column; gap: 4px;') },
|
|
439
|
+
h('div', null, h('strong', null, 'Agent ID: '), entry.agent_id || '—'),
|
|
440
|
+
h('div', null, h('strong', null, 'Connection ID: '), entry.connection_id || '—'),
|
|
441
|
+
h('div', null, h('strong', null, 'Rows Affected: '), entry.rows_affected != null ? String(entry.rows_affected) : '—'),
|
|
442
|
+
h('div', null, h('strong', null, 'Execution Time: '), entry.execution_time_ms != null ? entry.execution_time_ms + 'ms' : '—'),
|
|
443
|
+
h('div', null, h('strong', null, 'IP: '), entry.ip_address || '—'),
|
|
444
|
+
!entry.success && entry.error && h('div', { style: css('margin-top: 4px; padding: 6px 8px; background: rgba(239,68,68,0.1); border-radius: 4px; color: var(--danger);') },
|
|
445
|
+
h('strong', null, 'Error: '), entry.error
|
|
446
|
+
),
|
|
447
|
+
),
|
|
448
|
+
),
|
|
449
|
+
),
|
|
450
|
+
),
|
|
451
|
+
),
|
|
452
|
+
);
|
|
453
|
+
})
|
|
454
|
+
),
|
|
346
455
|
),
|
|
347
456
|
),
|
|
457
|
+
|
|
458
|
+
// Pagination
|
|
459
|
+
totalPages > 1 && h('div', { style: css('display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 16px;') },
|
|
460
|
+
h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page === 0, onClick: function() { setPage(0); } }, '«'),
|
|
461
|
+
h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page === 0, onClick: function() { setPage(page - 1); } }, '‹'),
|
|
462
|
+
h('span', { style: css('font-size: 12px; color: var(--text-secondary);') }, 'Page ' + (page + 1) + ' of ' + totalPages),
|
|
463
|
+
h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page >= totalPages - 1, onClick: function() { setPage(page + 1); } }, '›'),
|
|
464
|
+
h('button', { style: Object.assign({}, s.btn, { padding: '4px 10px', fontSize: '12px' }), disabled: page >= totalPages - 1, onClick: function() { setPage(totalPages - 1); } }, '»'),
|
|
465
|
+
),
|
|
348
466
|
);
|
|
349
467
|
}
|
|
350
468
|
|
|
@@ -355,26 +473,63 @@ function AddConnectionModal(props) {
|
|
|
355
473
|
var [dbType, setDbType] = useState('');
|
|
356
474
|
var [form, setForm] = useState({ name: '', host: '', port: '', database: '', username: '', password: '', connectionString: '', ssl: false, description: '' });
|
|
357
475
|
var [saving, setSaving] = useState(false);
|
|
476
|
+
var [testing, setTesting] = useState(false);
|
|
477
|
+
var [testResult, setTestResult] = useState(null); // { success, error, latencyMs }
|
|
358
478
|
|
|
359
|
-
var set = function(key, val) { setForm(function(f) { var n = Object.assign({}, f); n[key] = val; return n; }); };
|
|
479
|
+
var set = function(key, val) { setForm(function(f) { var n = Object.assign({}, f); n[key] = val; return n; }); setTestResult(null); };
|
|
360
480
|
|
|
361
481
|
var isConnString = form.connectionString.length > 0;
|
|
362
482
|
|
|
483
|
+
var buildBody = function() {
|
|
484
|
+
var body = { type: dbType, name: form.name || (ALL_DB_TYPES.find(function(t) { return t.value === dbType; })?.label + ' Connection'), description: form.description, status: 'inactive' };
|
|
485
|
+
if (isConnString) {
|
|
486
|
+
body.connectionString = form.connectionString;
|
|
487
|
+
} else {
|
|
488
|
+
body.host = form.host;
|
|
489
|
+
body.port = form.port ? parseInt(form.port) : undefined;
|
|
490
|
+
body.database = form.database;
|
|
491
|
+
body.username = form.username;
|
|
492
|
+
body.password = form.password;
|
|
493
|
+
body.ssl = form.ssl;
|
|
494
|
+
}
|
|
495
|
+
return body;
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
var testConnection = async function() {
|
|
499
|
+
setTesting(true);
|
|
500
|
+
setTestResult(null);
|
|
501
|
+
try {
|
|
502
|
+
var result = await engineCall('/database/connections/test', { method: 'POST', body: JSON.stringify(buildBody()) });
|
|
503
|
+
setTestResult(result);
|
|
504
|
+
} catch (e) {
|
|
505
|
+
setTestResult({ success: false, error: e.message || 'Connection test failed' });
|
|
506
|
+
}
|
|
507
|
+
setTesting(false);
|
|
508
|
+
};
|
|
509
|
+
|
|
363
510
|
var save = async function() {
|
|
511
|
+
// Test connection first if not already tested successfully
|
|
512
|
+
if (!testResult || !testResult.success) {
|
|
513
|
+
setTesting(true);
|
|
514
|
+
setTestResult(null);
|
|
515
|
+
try {
|
|
516
|
+
var result = await engineCall('/database/connections/test', { method: 'POST', body: JSON.stringify(buildBody()) });
|
|
517
|
+
setTestResult(result);
|
|
518
|
+
if (!result.success) {
|
|
519
|
+
setTesting(false);
|
|
520
|
+
return; // Don't save if test fails
|
|
521
|
+
}
|
|
522
|
+
} catch (e) {
|
|
523
|
+
setTestResult({ success: false, error: e.message || 'Connection test failed' });
|
|
524
|
+
setTesting(false);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
setTesting(false);
|
|
528
|
+
}
|
|
529
|
+
|
|
364
530
|
setSaving(true);
|
|
365
531
|
try {
|
|
366
|
-
|
|
367
|
-
if (isConnString) {
|
|
368
|
-
body.connectionString = form.connectionString;
|
|
369
|
-
} else {
|
|
370
|
-
body.host = form.host;
|
|
371
|
-
body.port = form.port ? parseInt(form.port) : undefined;
|
|
372
|
-
body.database = form.database;
|
|
373
|
-
body.username = form.username;
|
|
374
|
-
body.password = form.password;
|
|
375
|
-
body.ssl = form.ssl;
|
|
376
|
-
}
|
|
377
|
-
await engineCall('/database/connections', { method: 'POST', body: JSON.stringify(body) });
|
|
532
|
+
await engineCall('/database/connections', { method: 'POST', body: JSON.stringify(buildBody()) });
|
|
378
533
|
props.onSave();
|
|
379
534
|
props.onClose();
|
|
380
535
|
} catch (e) { alert('Failed: ' + e.message); }
|
|
@@ -454,9 +609,21 @@ function AddConnectionModal(props) {
|
|
|
454
609
|
h('div', { style: s.label }, 'Description (optional)'),
|
|
455
610
|
h('input', { style: s.input, placeholder: 'What is this database used for?', value: form.description, onInput: function(e) { set('description', e.target.value); } }),
|
|
456
611
|
),
|
|
612
|
+
// Connection test result
|
|
613
|
+
testResult && h('div', { style: css('padding: 8px 12px; border-radius: 6px; font-size: 12px; ' + (testResult.success
|
|
614
|
+
? 'background: rgba(21,128,61,0.1); color: var(--success); border: 1px solid rgba(21,128,61,0.3);'
|
|
615
|
+
: 'background: rgba(239,68,68,0.1); color: var(--danger); border: 1px solid rgba(239,68,68,0.3);')) },
|
|
616
|
+
testResult.success
|
|
617
|
+
? 'Connection successful! (' + testResult.latencyMs + 'ms)'
|
|
618
|
+
: 'Connection failed: ' + (testResult.error || 'Unknown error'),
|
|
619
|
+
),
|
|
620
|
+
|
|
457
621
|
h('div', { style: css('display: flex; justify-content: space-between; margin-top: 8px;') },
|
|
458
622
|
h('button', { style: s.btn, onClick: function() { setStep(1); } }, '← Back'),
|
|
459
|
-
h('
|
|
623
|
+
h('div', { style: css('display: flex; gap: 8px;') },
|
|
624
|
+
h('button', { style: s.btn, disabled: testing || saving || (!isConnString && !form.host), onClick: testConnection }, testing ? 'Testing...' : 'Test Connection'),
|
|
625
|
+
h('button', { style: s.btnPrimary, disabled: testing || saving || (!isConnString && !form.host), onClick: save }, saving ? 'Saving...' : testing ? 'Testing...' : 'Add Connection'),
|
|
626
|
+
),
|
|
460
627
|
),
|
|
461
628
|
),
|
|
462
629
|
),
|
|
@@ -534,6 +534,34 @@ export class DatabaseConnectionManager {
|
|
|
534
534
|
}
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
+
/**
|
|
538
|
+
* Test connection parameters without saving — creates a temporary connection,
|
|
539
|
+
* pings it, and immediately destroys it.
|
|
540
|
+
*/
|
|
541
|
+
async testConnectionParams(params: {
|
|
542
|
+
type: string; host?: string; port?: number; database?: string;
|
|
543
|
+
username?: string; password?: string; connectionString?: string; ssl?: boolean;
|
|
544
|
+
}): Promise<{ success: boolean; latencyMs: number; error?: string }> {
|
|
545
|
+
const startMs = Date.now();
|
|
546
|
+
const driver = this.drivers.get(params.type as any);
|
|
547
|
+
if (!driver) return { success: false, latencyMs: 0, error: `No driver for database type: ${params.type}` };
|
|
548
|
+
|
|
549
|
+
let connection: any;
|
|
550
|
+
try {
|
|
551
|
+
connection = await driver.connect(
|
|
552
|
+
{ type: params.type as any, host: params.host, port: params.port, database: params.database, ssl: params.ssl } as any,
|
|
553
|
+
{ password: params.password, connectionString: params.connectionString },
|
|
554
|
+
);
|
|
555
|
+
const alive = await connection.ping();
|
|
556
|
+
const latencyMs = Date.now() - startMs;
|
|
557
|
+
return { success: alive, latencyMs };
|
|
558
|
+
} catch (err: any) {
|
|
559
|
+
return { success: false, latencyMs: Date.now() - startMs, error: err.message };
|
|
560
|
+
} finally {
|
|
561
|
+
try { if (connection?.close) await connection.close(); } catch {}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
537
565
|
// ─── Pool Management ───────────────────────────────────────────────────────
|
|
538
566
|
|
|
539
567
|
private async getPooledConnection(connectionId: string): Promise<DatabaseConnection> {
|
|
@@ -88,7 +88,29 @@ export function createDatabaseAccessRoutes(manager: DatabaseConnectionManager) {
|
|
|
88
88
|
return c.json({ ok: true });
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
/** Test
|
|
91
|
+
/** Test connection parameters before saving (no persistence) — must come before :id routes */
|
|
92
|
+
router.post('/connections/test', async (c) => {
|
|
93
|
+
const body = await c.req.json();
|
|
94
|
+
if (!body.type) return c.json({ error: 'type is required' }, 400);
|
|
95
|
+
const startMs = Date.now();
|
|
96
|
+
try {
|
|
97
|
+
const result = await manager.testConnectionParams({
|
|
98
|
+
type: body.type,
|
|
99
|
+
host: body.host,
|
|
100
|
+
port: body.port,
|
|
101
|
+
database: body.database,
|
|
102
|
+
username: body.username,
|
|
103
|
+
password: body.password,
|
|
104
|
+
connectionString: body.connectionString,
|
|
105
|
+
ssl: body.ssl,
|
|
106
|
+
});
|
|
107
|
+
return c.json(result);
|
|
108
|
+
} catch (e: any) {
|
|
109
|
+
return c.json({ success: false, latencyMs: Date.now() - startMs, error: e.message || 'Connection test failed' });
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
/** Test an existing database connection */
|
|
92
114
|
router.post('/connections/:id/test', async (c) => {
|
|
93
115
|
const result = await manager.testConnection(c.req.param('id'));
|
|
94
116
|
return c.json(result);
|
package/src/runtime/index.ts
CHANGED
|
@@ -228,6 +228,10 @@ export class AgentRuntime {
|
|
|
228
228
|
if (this.config.mcpProcessManager) {
|
|
229
229
|
base.mcpProcessManager = this.config.mcpProcessManager;
|
|
230
230
|
}
|
|
231
|
+
// Database Connection Manager — enterprise database access tools
|
|
232
|
+
if (this.config.databaseManager) {
|
|
233
|
+
base.databaseManager = this.config.databaseManager;
|
|
234
|
+
}
|
|
231
235
|
return base;
|
|
232
236
|
}
|
|
233
237
|
|
package/src/runtime/types.ts
CHANGED
|
@@ -140,6 +140,8 @@ export interface RuntimeConfig {
|
|
|
140
140
|
permissionEngine?: any;
|
|
141
141
|
/** MCP Process Manager for external MCP server tools */
|
|
142
142
|
mcpProcessManager?: import('../engine/mcp-process-manager.js').McpProcessManager;
|
|
143
|
+
/** Database Connection Manager for enterprise database access tools */
|
|
144
|
+
databaseManager?: import('../database-access/connection-manager.js').DatabaseConnectionManager;
|
|
143
145
|
/** Knowledge base engine for RAG search tools */
|
|
144
146
|
knowledgeEngine?: any;
|
|
145
147
|
/** Real-time agent status tracker */
|
package/src/server.ts
CHANGED
|
@@ -355,7 +355,7 @@ export function createServer(config: ServerConfig): ServerInstance {
|
|
|
355
355
|
};
|
|
356
356
|
}
|
|
357
357
|
} catch {}
|
|
358
|
-
const { vault: vaultRef, permissionEngine: permRef } = await import('./engine/routes.js');
|
|
358
|
+
const { vault: vaultRef, permissionEngine: permRef, databaseManager: dbMgr } = await import('./engine/routes.js');
|
|
359
359
|
const runtime = createAgentRuntime({
|
|
360
360
|
engineDb,
|
|
361
361
|
adminDb: config.db,
|
|
@@ -367,7 +367,8 @@ export function createServer(config: ServerConfig): ServerInstance {
|
|
|
367
367
|
agentMemoryManager: agentMemoryMgr,
|
|
368
368
|
vault: vaultRef,
|
|
369
369
|
permissionEngine: permRef,
|
|
370
|
-
|
|
370
|
+
databaseManager: dbMgr,
|
|
371
|
+
} as any);
|
|
371
372
|
await runtime.start();
|
|
372
373
|
const runtimeApp = runtime.getApp();
|
|
373
374
|
if (runtimeApp) {
|
|
@@ -620,7 +621,7 @@ export function createServer(config: ServerConfig): ServerInstance {
|
|
|
620
621
|
};
|
|
621
622
|
}
|
|
622
623
|
} catch {}
|
|
623
|
-
const { vault: vaultRef2, permissionEngine: permRef2 } = await import('./engine/routes.js');
|
|
624
|
+
const { vault: vaultRef2, permissionEngine: permRef2, databaseManager: dbMgr2 } = await import('./engine/routes.js');
|
|
624
625
|
const runtime = createAgentRuntime({
|
|
625
626
|
engineDb,
|
|
626
627
|
adminDb: config.db,
|
|
@@ -633,7 +634,8 @@ export function createServer(config: ServerConfig): ServerInstance {
|
|
|
633
634
|
vault: vaultRef2,
|
|
634
635
|
permissionEngine: permRef2,
|
|
635
636
|
hierarchyManager: (await import('./engine/routes.js')).hierarchyManager ?? undefined,
|
|
636
|
-
|
|
637
|
+
databaseManager: dbMgr2,
|
|
638
|
+
} as any);
|
|
637
639
|
await runtime.start();
|
|
638
640
|
const runtimeApp = runtime.getApp();
|
|
639
641
|
if (runtimeApp) mountRuntimeApp(runtimeApp);
|