apteva 0.4.32 → 0.4.44
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/ActivityPage.c48n83h2.js +3 -0
- package/dist/ApiDocsPage.yzcxx5ax.js +4 -0
- package/dist/App.09yb8t0b.js +1 -0
- package/dist/App.152mbs1r.js +4 -0
- package/dist/App.3a67nx9w.js +4 -0
- package/dist/App.9epx6785.js +4 -0
- package/dist/App.d8955awp.js +4 -0
- package/dist/App.drwb57jq.js +4 -0
- package/dist/App.gssbmajb.js +4 -0
- package/dist/App.qw70pc29.js +53 -0
- package/dist/App.qzbx5wtj.js +4 -0
- package/dist/App.r5serxkt.js +8 -0
- package/dist/App.tpmp9020.js +20 -0
- package/dist/App.v2wb4d7d.js +61 -0
- package/dist/App.vxmaaj0m.js +13 -0
- package/dist/App.w4p2tda9.js +4 -0
- package/dist/App.wv2ng55q.js +221 -0
- package/dist/App.yncnrn0f.js +4 -0
- package/dist/ConnectionsPage.k6cspyqq.js +3 -0
- package/dist/McpPage.cdxm48xj.js +3 -0
- package/dist/SettingsPage.evpv7c2y.js +3 -0
- package/dist/SkillsPage.pvzp6c1a.js +3 -0
- package/dist/TasksPage.6jnvbpsy.js +3 -0
- package/dist/TelemetryPage.t7vk24zc.js +3 -0
- package/dist/TestsPage.5x6658aa.js +3 -0
- package/dist/ThreadsPage.3fvhtevh.js +3 -0
- package/dist/apteva-kit.css +1 -1
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +10 -9
- package/src/crypto.ts +4 -3
- package/src/db.ts +171 -36
- package/src/integrations/agentdojo.ts +95 -12
- package/src/integrations/index.ts +7 -0
- package/src/mcp-platform.ts +870 -142
- package/src/openapi.ts +96 -0
- package/src/providers.ts +60 -34
- package/src/routes/api/agent-utils.ts +59 -47
- package/src/routes/api/agents.ts +71 -2
- package/src/routes/api/integrations.ts +11 -5
- package/src/routes/api/mcp.ts +5 -4
- package/src/routes/api/meta-agent.ts +37 -1
- package/src/routes/api/projects.ts +3 -3
- package/src/routes/api/providers.ts +121 -30
- package/src/routes/api/skills.ts +2 -3
- package/src/routes/api/system.ts +98 -14
- package/src/routes/api/telemetry.ts +19 -1
- package/src/routes/share.ts +85 -0
- package/src/server.ts +43 -32
- package/src/triggers/agentdojo.ts +2 -2
- package/src/web/App.tsx +107 -21
- package/src/web/components/activity/ActivityPage.tsx +242 -389
- package/src/web/components/agents/AgentCard.tsx +19 -27
- package/src/web/components/agents/AgentPanel.tsx +358 -198
- package/src/web/components/agents/AgentsView.tsx +4 -4
- package/src/web/components/agents/CreateAgentModal.tsx +21 -79
- package/src/web/components/api/ApiDocsPage.tsx +66 -66
- package/src/web/components/auth/CreateAccountStep.tsx +16 -16
- package/src/web/components/auth/LoginPage.tsx +10 -10
- package/src/web/components/common/Icons.tsx +8 -0
- package/src/web/components/common/LoadingSpinner.tsx +2 -2
- package/src/web/components/common/Modal.tsx +8 -8
- package/src/web/components/common/Select.tsx +11 -10
- package/src/web/components/connections/ConnectionsPage.tsx +4 -4
- package/src/web/components/connections/IntegrationsTab.tsx +18 -18
- package/src/web/components/connections/OverviewTab.tsx +13 -13
- package/src/web/components/connections/TriggersTab.tsx +99 -99
- package/src/web/components/dashboard/Dashboard.tsx +177 -52
- package/src/web/components/index.ts +1 -1
- package/src/web/components/layout/Header.tsx +50 -34
- package/src/web/components/layout/Sidebar.tsx +41 -16
- package/src/web/components/mcp/IntegrationsPanel.tsx +160 -69
- package/src/web/components/mcp/McpPage.tsx +218 -209
- package/src/web/components/meta-agent/MetaAgent.tsx +15 -11
- package/src/web/components/onboarding/OnboardingWizard.tsx +25 -25
- package/src/web/components/settings/SettingsPage.tsx +389 -221
- package/src/web/components/skills/SkillsPage.tsx +88 -88
- package/src/web/components/tasks/TasksPage.tsx +385 -68
- package/src/web/components/telemetry/TelemetryPage.tsx +294 -39
- package/src/web/components/tests/TestsPage.tsx +50 -50
- package/src/web/components/threads/ThreadsPage.tsx +315 -0
- package/src/web/context/AuthContext.tsx +3 -3
- package/src/web/context/ProjectContext.tsx +8 -3
- package/src/web/context/TelemetryContext.tsx +24 -6
- package/src/web/context/ThemeContext.tsx +69 -0
- package/src/web/context/index.ts +3 -1
- package/src/web/styles.css +25 -7
- package/src/web/themes.ts +99 -0
- package/src/web/types.ts +4 -7
- package/dist/ActivityPage.41nbye4r.js +0 -3
- package/dist/ApiDocsPage.4smnt8m3.js +0 -4
- package/dist/App.0sbax9et.js +0 -4
- package/dist/App.0ws427h8.js +0 -4
- package/dist/App.6q6bar8b.js +0 -4
- package/dist/App.80301vdb.js +0 -4
- package/dist/App.af2wg84v.js +0 -267
- package/dist/App.ca1rz1ph.js +0 -4
- package/dist/App.ensa6z0r.js +0 -4
- package/dist/App.f8g7tych.js +0 -13
- package/dist/App.mvtqv6qc.js +0 -20
- package/dist/App.ncgc9cxy.js +0 -4
- package/dist/App.p02f4ret.js +0 -1
- package/dist/App.p0fb1pds.js +0 -4
- package/dist/App.pmaq48sj.js +0 -4
- package/dist/App.yv87t9m5.js +0 -4
- package/dist/App.zjmfm8p6.js +0 -4
- package/dist/ConnectionsPage.anb3rv9a.js +0 -3
- package/dist/McpPage.y396h6fy.js +0 -3
- package/dist/SettingsPage.p1hc60gk.js +0 -3
- package/dist/SkillsPage.yj3xdsay.js +0 -3
- package/dist/TasksPage.sjv0khtv.js +0 -3
- package/dist/TelemetryPage.2qm4w16r.js +0 -3
- package/dist/TestsPage.zzs4qfj8.js +0 -3
package/src/db.ts
CHANGED
|
@@ -5,11 +5,8 @@ import { encrypt, decrypt, encryptObject, decryptObject } from "./crypto";
|
|
|
5
5
|
import { randomBytes, createHash } from "crypto";
|
|
6
6
|
|
|
7
7
|
// Types
|
|
8
|
-
export type AgentMode = "coordinator" | "worker";
|
|
9
|
-
|
|
10
8
|
export interface MultiAgentConfig {
|
|
11
9
|
enabled: boolean;
|
|
12
|
-
mode?: AgentMode;
|
|
13
10
|
group?: string; // Defaults to projectId if not specified
|
|
14
11
|
}
|
|
15
12
|
|
|
@@ -20,7 +17,7 @@ export interface AgentBuiltinTools {
|
|
|
20
17
|
|
|
21
18
|
export interface OperatorConfig {
|
|
22
19
|
enabled: boolean;
|
|
23
|
-
browser_provider?: string; // "
|
|
20
|
+
browser_provider?: string; // "browserengine" | "browserbase" | "steel" | "cdp"
|
|
24
21
|
display_width?: number;
|
|
25
22
|
display_height?: number;
|
|
26
23
|
max_actions_per_turn?: number;
|
|
@@ -67,7 +64,6 @@ export function getMultiAgentConfig(features: AgentFeatures, projectId?: string
|
|
|
67
64
|
if (typeof agents === "boolean") {
|
|
68
65
|
return {
|
|
69
66
|
enabled: agents,
|
|
70
|
-
mode: "worker",
|
|
71
67
|
group: projectId || undefined,
|
|
72
68
|
};
|
|
73
69
|
}
|
|
@@ -328,6 +324,12 @@ export function initDatabase(dataDir: string): Database {
|
|
|
328
324
|
db.run("PRAGMA busy_timeout = 5000");
|
|
329
325
|
db.run("PRAGMA foreign_keys = ON");
|
|
330
326
|
|
|
327
|
+
// Performance PRAGMAs
|
|
328
|
+
db.run("PRAGMA synchronous = NORMAL"); // Safe with WAL, much faster than FULL
|
|
329
|
+
db.run("PRAGMA cache_size = -20000"); // 20MB page cache (negative = KB)
|
|
330
|
+
db.run("PRAGMA mmap_size = 30000000"); // 30MB memory-mapped I/O
|
|
331
|
+
db.run("PRAGMA temp_store = MEMORY"); // Keep temp tables in memory
|
|
332
|
+
|
|
331
333
|
// Run migrations
|
|
332
334
|
runMigrations();
|
|
333
335
|
|
|
@@ -486,6 +488,7 @@ function runMigrations() {
|
|
|
486
488
|
);
|
|
487
489
|
CREATE INDEX IF NOT EXISTS idx_telemetry_agent ON telemetry_events(agent_id);
|
|
488
490
|
CREATE INDEX IF NOT EXISTS idx_telemetry_time ON telemetry_events(timestamp);
|
|
491
|
+
CREATE INDEX IF NOT EXISTS idx_telemetry_agent_time ON telemetry_events(agent_id, timestamp DESC);
|
|
489
492
|
CREATE INDEX IF NOT EXISTS idx_telemetry_category ON telemetry_events(category);
|
|
490
493
|
CREATE INDEX IF NOT EXISTS idx_telemetry_level ON telemetry_events(level);
|
|
491
494
|
CREATE INDEX IF NOT EXISTS idx_telemetry_trace ON telemetry_events(trace_id);
|
|
@@ -835,6 +838,37 @@ function runMigrations() {
|
|
|
835
838
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_provider_keys_unique ON provider_keys(provider_id, COALESCE(project_id, ''));
|
|
836
839
|
`,
|
|
837
840
|
},
|
|
841
|
+
{
|
|
842
|
+
name: "034_repair_provider_keys_project_support",
|
|
843
|
+
sql: `
|
|
844
|
+
-- Repair: migrations 022 and 029 may have failed but were marked as applied.
|
|
845
|
+
-- Recreate provider_keys with project_id support from whatever current state.
|
|
846
|
+
CREATE TABLE IF NOT EXISTS provider_keys_repair (
|
|
847
|
+
id TEXT PRIMARY KEY,
|
|
848
|
+
provider_id TEXT NOT NULL,
|
|
849
|
+
encrypted_key TEXT NOT NULL,
|
|
850
|
+
key_hint TEXT,
|
|
851
|
+
is_valid INTEGER DEFAULT 1,
|
|
852
|
+
last_tested_at TEXT,
|
|
853
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
854
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
855
|
+
name TEXT
|
|
856
|
+
);
|
|
857
|
+
INSERT OR IGNORE INTO provider_keys_repair (id, provider_id, encrypted_key, key_hint, is_valid, last_tested_at, created_at)
|
|
858
|
+
SELECT id, provider_id, encrypted_key, key_hint, is_valid, last_tested_at, created_at FROM provider_keys;
|
|
859
|
+
DROP TABLE IF EXISTS provider_keys;
|
|
860
|
+
ALTER TABLE provider_keys_repair RENAME TO provider_keys;
|
|
861
|
+
CREATE INDEX IF NOT EXISTS idx_provider_keys_provider ON provider_keys(provider_id);
|
|
862
|
+
CREATE INDEX IF NOT EXISTS idx_provider_keys_project ON provider_keys(project_id);
|
|
863
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_provider_keys_unique ON provider_keys(provider_id, COALESCE(project_id, ''));
|
|
864
|
+
`,
|
|
865
|
+
},
|
|
866
|
+
{
|
|
867
|
+
name: "035_add_telemetry_cost",
|
|
868
|
+
sql: `
|
|
869
|
+
ALTER TABLE telemetry_events ADD COLUMN cost REAL DEFAULT 0;
|
|
870
|
+
`,
|
|
871
|
+
},
|
|
838
872
|
];
|
|
839
873
|
|
|
840
874
|
// Check which migrations have been applied
|
|
@@ -1056,10 +1090,10 @@ export const AgentDB = {
|
|
|
1056
1090
|
|
|
1057
1091
|
// Find agents that have a specific skill
|
|
1058
1092
|
findBySkill(skillId: string): Agent[] {
|
|
1059
|
-
//
|
|
1093
|
+
// Use json_each to properly search the JSON array (avoids full table scan with LIKE)
|
|
1060
1094
|
const rows = db.query(
|
|
1061
|
-
`SELECT
|
|
1062
|
-
).all(
|
|
1095
|
+
`SELECT DISTINCT a.* FROM agents a, json_each(a.skills) AS s WHERE s.value = ? ORDER BY a.created_at DESC`
|
|
1096
|
+
).all(skillId) as AgentRow[];
|
|
1063
1097
|
return rows.map(rowToAgent);
|
|
1064
1098
|
},
|
|
1065
1099
|
|
|
@@ -1091,14 +1125,23 @@ export const AgentDB = {
|
|
|
1091
1125
|
return row.count;
|
|
1092
1126
|
},
|
|
1093
1127
|
|
|
1094
|
-
//
|
|
1128
|
+
// In-memory cache for decrypted API keys (avoids expensive scryptSync on every request)
|
|
1129
|
+
_apiKeyCache: new Map<string, string>(),
|
|
1130
|
+
|
|
1131
|
+
// Get decrypted API key for an agent (cached)
|
|
1095
1132
|
getApiKey(id: string): string | null {
|
|
1133
|
+
// Check cache first
|
|
1134
|
+
const cached = this._apiKeyCache.get(id);
|
|
1135
|
+
if (cached) return cached;
|
|
1136
|
+
|
|
1096
1137
|
const agent = this.findById(id);
|
|
1097
1138
|
if (!agent || !agent.api_key_encrypted) {
|
|
1098
1139
|
return null;
|
|
1099
1140
|
}
|
|
1100
1141
|
try {
|
|
1101
|
-
|
|
1142
|
+
const key = decrypt(agent.api_key_encrypted);
|
|
1143
|
+
if (key) this._apiKeyCache.set(id, key);
|
|
1144
|
+
return key;
|
|
1102
1145
|
} catch {
|
|
1103
1146
|
return null;
|
|
1104
1147
|
}
|
|
@@ -1118,6 +1161,8 @@ export const AgentDB = {
|
|
|
1118
1161
|
[encrypted, now, id]
|
|
1119
1162
|
);
|
|
1120
1163
|
|
|
1164
|
+
// Update cache
|
|
1165
|
+
this._apiKeyCache.set(id, newApiKey);
|
|
1121
1166
|
return newApiKey;
|
|
1122
1167
|
},
|
|
1123
1168
|
|
|
@@ -1129,7 +1174,9 @@ export const AgentDB = {
|
|
|
1129
1174
|
// If agent already has a key, return it
|
|
1130
1175
|
if (agent.api_key_encrypted) {
|
|
1131
1176
|
try {
|
|
1132
|
-
|
|
1177
|
+
const key = decrypt(agent.api_key_encrypted);
|
|
1178
|
+
if (key) this._apiKeyCache.set(id, key);
|
|
1179
|
+
return key;
|
|
1133
1180
|
} catch {
|
|
1134
1181
|
// Key is corrupted, regenerate
|
|
1135
1182
|
}
|
|
@@ -1395,7 +1442,7 @@ export const ProviderKeysDB = {
|
|
|
1395
1442
|
|
|
1396
1443
|
// Find all keys for a provider (global + all projects)
|
|
1397
1444
|
findAllByProvider(providerId: string): ProviderKey[] {
|
|
1398
|
-
const rows = db.query("SELECT * FROM provider_keys WHERE provider_id = ? ORDER BY project_id
|
|
1445
|
+
const rows = db.query("SELECT * FROM provider_keys WHERE provider_id = ? ORDER BY (project_id IS NOT NULL), created_at DESC").all(providerId) as ProviderKeyRow[];
|
|
1399
1446
|
return rows.map(rowToProviderKey);
|
|
1400
1447
|
},
|
|
1401
1448
|
|
|
@@ -1407,7 +1454,7 @@ export const ProviderKeysDB = {
|
|
|
1407
1454
|
|
|
1408
1455
|
// Get all provider keys
|
|
1409
1456
|
findAll(): ProviderKey[] {
|
|
1410
|
-
const rows = db.query("SELECT * FROM provider_keys ORDER BY provider_id, project_id
|
|
1457
|
+
const rows = db.query("SELECT * FROM provider_keys ORDER BY provider_id, (project_id IS NOT NULL), created_at DESC").all() as ProviderKeyRow[];
|
|
1411
1458
|
return rows.map(rowToProviderKey);
|
|
1412
1459
|
},
|
|
1413
1460
|
|
|
@@ -1535,6 +1582,22 @@ export const McpServerDB = {
|
|
|
1535
1582
|
return rows.map(rowToMcpServer);
|
|
1536
1583
|
},
|
|
1537
1584
|
|
|
1585
|
+
// Light version: skips expensive decryption for listing endpoints
|
|
1586
|
+
findAllLight(): McpServer[] {
|
|
1587
|
+
const rows = db.query("SELECT * FROM mcp_servers ORDER BY created_at DESC").all() as McpServerRow[];
|
|
1588
|
+
return rows.map(rowToMcpServerLight);
|
|
1589
|
+
},
|
|
1590
|
+
|
|
1591
|
+
// Light batch load by IDs: skips decryption (used by toApiAgentsBatch)
|
|
1592
|
+
findByIdsLight(ids: string[]): Map<string, McpServer> {
|
|
1593
|
+
if (ids.length === 0) return new Map();
|
|
1594
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
1595
|
+
const rows = db.query(`SELECT * FROM mcp_servers WHERE id IN (${placeholders})`).all(...ids) as McpServerRow[];
|
|
1596
|
+
const map = new Map<string, McpServer>();
|
|
1597
|
+
for (const row of rows) map.set(row.id, rowToMcpServerLight(row));
|
|
1598
|
+
return map;
|
|
1599
|
+
},
|
|
1600
|
+
|
|
1538
1601
|
findRunning(): McpServer[] {
|
|
1539
1602
|
const rows = db.query("SELECT * FROM mcp_servers WHERE status = 'running'").all() as McpServerRow[];
|
|
1540
1603
|
return rows.map(rowToMcpServer);
|
|
@@ -1661,6 +1724,30 @@ export const McpServerDB = {
|
|
|
1661
1724
|
const rows = db.query("SELECT * FROM mcp_servers WHERE project_id IS NULL ORDER BY created_at DESC").all() as McpServerRow[];
|
|
1662
1725
|
return rows.map(rowToMcpServer);
|
|
1663
1726
|
},
|
|
1727
|
+
|
|
1728
|
+
// Light versions (skip decryption) for listing endpoints
|
|
1729
|
+
findByProjectLight(projectId: string | null): McpServer[] {
|
|
1730
|
+
if (projectId === null) {
|
|
1731
|
+
const rows = db.query("SELECT * FROM mcp_servers WHERE project_id IS NULL ORDER BY created_at DESC").all() as McpServerRow[];
|
|
1732
|
+
return rows.map(rowToMcpServerLight);
|
|
1733
|
+
}
|
|
1734
|
+
const rows = db.query("SELECT * FROM mcp_servers WHERE project_id = ? ORDER BY created_at DESC").all(projectId) as McpServerRow[];
|
|
1735
|
+
return rows.map(rowToMcpServerLight);
|
|
1736
|
+
},
|
|
1737
|
+
|
|
1738
|
+
findForAgentLight(agentProjectId: string | null): McpServer[] {
|
|
1739
|
+
if (agentProjectId === null) {
|
|
1740
|
+
const rows = db.query("SELECT * FROM mcp_servers WHERE project_id IS NULL ORDER BY created_at DESC").all() as McpServerRow[];
|
|
1741
|
+
return rows.map(rowToMcpServerLight);
|
|
1742
|
+
}
|
|
1743
|
+
const rows = db.query("SELECT * FROM mcp_servers WHERE project_id IS NULL OR project_id = ? ORDER BY created_at DESC").all(agentProjectId) as McpServerRow[];
|
|
1744
|
+
return rows.map(rowToMcpServerLight);
|
|
1745
|
+
},
|
|
1746
|
+
|
|
1747
|
+
findGlobalLight(): McpServer[] {
|
|
1748
|
+
const rows = db.query("SELECT * FROM mcp_servers WHERE project_id IS NULL ORDER BY created_at DESC").all() as McpServerRow[];
|
|
1749
|
+
return rows.map(rowToMcpServerLight);
|
|
1750
|
+
},
|
|
1664
1751
|
};
|
|
1665
1752
|
|
|
1666
1753
|
// MCP Server Tool CRUD operations (for local servers)
|
|
@@ -1792,6 +1879,27 @@ function rowToMcpServer(row: McpServerRow): McpServer {
|
|
|
1792
1879
|
};
|
|
1793
1880
|
}
|
|
1794
1881
|
|
|
1882
|
+
// Light version: skips expensive decryption of env/headers for listing endpoints
|
|
1883
|
+
function rowToMcpServerLight(row: McpServerRow): McpServer {
|
|
1884
|
+
return {
|
|
1885
|
+
id: row.id,
|
|
1886
|
+
name: row.name,
|
|
1887
|
+
type: row.type as McpServer["type"],
|
|
1888
|
+
package: row.package,
|
|
1889
|
+
pip_module: row.pip_module,
|
|
1890
|
+
command: row.command,
|
|
1891
|
+
args: row.args,
|
|
1892
|
+
env: {},
|
|
1893
|
+
url: row.url,
|
|
1894
|
+
headers: {},
|
|
1895
|
+
port: row.port,
|
|
1896
|
+
status: row.status as "stopped" | "running",
|
|
1897
|
+
source: row.source,
|
|
1898
|
+
project_id: row.project_id,
|
|
1899
|
+
created_at: row.created_at,
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1795
1903
|
// Telemetry Event types
|
|
1796
1904
|
// User types
|
|
1797
1905
|
export interface User {
|
|
@@ -1884,34 +1992,40 @@ export const TelemetryDB = {
|
|
|
1884
1992
|
metadata?: Record<string, unknown>;
|
|
1885
1993
|
duration_ms?: number;
|
|
1886
1994
|
error?: string;
|
|
1995
|
+
cost?: number;
|
|
1887
1996
|
}>): number {
|
|
1888
1997
|
const now = new Date().toISOString();
|
|
1889
1998
|
const stmt = db.prepare(`
|
|
1890
1999
|
INSERT OR IGNORE INTO telemetry_events
|
|
1891
|
-
(id, agent_id, timestamp, category, type, level, trace_id, span_id, thread_id, data, metadata, duration_ms, error, received_at)
|
|
1892
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2000
|
+
(id, agent_id, timestamp, category, type, level, trace_id, span_id, thread_id, data, metadata, duration_ms, error, received_at, cost)
|
|
2001
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1893
2002
|
`);
|
|
1894
2003
|
|
|
2004
|
+
// Wrap in transaction for massive speedup (single fsync instead of one per row)
|
|
1895
2005
|
let inserted = 0;
|
|
1896
|
-
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
2006
|
+
const insertAll = db.transaction(() => {
|
|
2007
|
+
for (const event of events) {
|
|
2008
|
+
const result = stmt.run(
|
|
2009
|
+
event.id,
|
|
2010
|
+
agentId,
|
|
2011
|
+
event.timestamp,
|
|
2012
|
+
event.category,
|
|
2013
|
+
event.type,
|
|
2014
|
+
event.level,
|
|
2015
|
+
event.trace_id || null,
|
|
2016
|
+
event.span_id || null,
|
|
2017
|
+
event.thread_id || null,
|
|
2018
|
+
event.data ? JSON.stringify(event.data) : null,
|
|
2019
|
+
event.metadata ? JSON.stringify(event.metadata) : null,
|
|
2020
|
+
event.duration_ms || null,
|
|
2021
|
+
event.error || null,
|
|
2022
|
+
now,
|
|
2023
|
+
event.cost || 0
|
|
2024
|
+
);
|
|
2025
|
+
if (result.changes > 0) inserted++;
|
|
2026
|
+
}
|
|
2027
|
+
});
|
|
2028
|
+
insertAll();
|
|
1915
2029
|
return inserted;
|
|
1916
2030
|
},
|
|
1917
2031
|
|
|
@@ -1998,6 +2112,7 @@ export const TelemetryDB = {
|
|
|
1998
2112
|
llm_calls: number;
|
|
1999
2113
|
tool_calls: number;
|
|
2000
2114
|
errors: number;
|
|
2115
|
+
cost: number;
|
|
2001
2116
|
}> {
|
|
2002
2117
|
const conditions: string[] = [];
|
|
2003
2118
|
const params: unknown[] = [];
|
|
@@ -2048,7 +2163,8 @@ export const TelemetryDB = {
|
|
|
2048
2163
|
COALESCE(SUM(CASE WHEN t.category = 'LLM' THEN json_extract(t.data, '$.output_tokens') ELSE 0 END), 0) as output_tokens,
|
|
2049
2164
|
COALESCE(SUM(CASE WHEN t.category = 'LLM' THEN 1 ELSE 0 END), 0) as llm_calls,
|
|
2050
2165
|
COALESCE(SUM(CASE WHEN t.category = 'TOOL' THEN 1 ELSE 0 END), 0) as tool_calls,
|
|
2051
|
-
COALESCE(SUM(CASE WHEN t.level = 'error' THEN 1 ELSE 0 END), 0) as errors
|
|
2166
|
+
COALESCE(SUM(CASE WHEN t.level = 'error' THEN 1 ELSE 0 END), 0) as errors,
|
|
2167
|
+
COALESCE(SUM(t.cost), 0) as cost
|
|
2052
2168
|
${fromClause}
|
|
2053
2169
|
${where}
|
|
2054
2170
|
${groupBy}
|
|
@@ -2062,6 +2178,7 @@ export const TelemetryDB = {
|
|
|
2062
2178
|
llm_calls: number;
|
|
2063
2179
|
tool_calls: number;
|
|
2064
2180
|
errors: number;
|
|
2181
|
+
cost: number;
|
|
2065
2182
|
}>;
|
|
2066
2183
|
},
|
|
2067
2184
|
|
|
@@ -2073,6 +2190,7 @@ export const TelemetryDB = {
|
|
|
2073
2190
|
total_errors: number;
|
|
2074
2191
|
total_input_tokens: number;
|
|
2075
2192
|
total_output_tokens: number;
|
|
2193
|
+
total_cost: number;
|
|
2076
2194
|
} {
|
|
2077
2195
|
const conditions: string[] = [];
|
|
2078
2196
|
const params: unknown[] = [];
|
|
@@ -2103,7 +2221,8 @@ export const TelemetryDB = {
|
|
|
2103
2221
|
COALESCE(SUM(CASE WHEN t.category = 'TOOL' THEN 1 ELSE 0 END), 0) as total_tool_calls,
|
|
2104
2222
|
COALESCE(SUM(CASE WHEN t.level = 'error' THEN 1 ELSE 0 END), 0) as total_errors,
|
|
2105
2223
|
COALESCE(SUM(CASE WHEN t.category = 'LLM' THEN json_extract(t.data, '$.input_tokens') ELSE 0 END), 0) as total_input_tokens,
|
|
2106
|
-
COALESCE(SUM(CASE WHEN t.category = 'LLM' THEN json_extract(t.data, '$.output_tokens') ELSE 0 END), 0) as total_output_tokens
|
|
2224
|
+
COALESCE(SUM(CASE WHEN t.category = 'LLM' THEN json_extract(t.data, '$.output_tokens') ELSE 0 END), 0) as total_output_tokens,
|
|
2225
|
+
COALESCE(SUM(t.cost), 0) as total_cost
|
|
2107
2226
|
${fromClause}
|
|
2108
2227
|
${where}
|
|
2109
2228
|
`;
|
|
@@ -2115,6 +2234,7 @@ export const TelemetryDB = {
|
|
|
2115
2234
|
total_errors: number;
|
|
2116
2235
|
total_input_tokens: number;
|
|
2117
2236
|
total_output_tokens: number;
|
|
2237
|
+
total_cost: number;
|
|
2118
2238
|
};
|
|
2119
2239
|
},
|
|
2120
2240
|
|
|
@@ -2810,6 +2930,21 @@ export const SubscriptionDB = {
|
|
|
2810
2930
|
return rows.map(rowToSubscription);
|
|
2811
2931
|
},
|
|
2812
2932
|
|
|
2933
|
+
// Batch load subscriptions for multiple agents (1 query instead of N)
|
|
2934
|
+
findByAgentIds(agentIds: string[]): Map<string, Subscription[]> {
|
|
2935
|
+
const result = new Map<string, Subscription[]>();
|
|
2936
|
+
if (agentIds.length === 0) return result;
|
|
2937
|
+
const placeholders = agentIds.map(() => "?").join(",");
|
|
2938
|
+
const rows = db.query(`SELECT * FROM subscriptions WHERE agent_id IN (${placeholders})`).all(...agentIds) as SubscriptionRow[];
|
|
2939
|
+
for (const row of rows) {
|
|
2940
|
+
const sub = rowToSubscription(row);
|
|
2941
|
+
const list = result.get(sub.agent_id) || [];
|
|
2942
|
+
list.push(sub);
|
|
2943
|
+
result.set(sub.agent_id, list);
|
|
2944
|
+
}
|
|
2945
|
+
return result;
|
|
2946
|
+
},
|
|
2947
|
+
|
|
2813
2948
|
findAll(projectId?: string | null): Subscription[] {
|
|
2814
2949
|
if (projectId) {
|
|
2815
2950
|
const rows = db.query("SELECT * FROM subscriptions WHERE project_id = ? ORDER BY created_at DESC").all(projectId) as SubscriptionRow[];
|
|
@@ -56,18 +56,30 @@ export const AgentDojoProvider: IntegrationProvider = {
|
|
|
56
56
|
console.error("AgentDojo listApps providers error:", providersRes.status);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
// Index providers by name for quick lookup
|
|
59
|
+
// Index providers by id and name for quick lookup
|
|
60
|
+
const providerById = new Map<number, any>();
|
|
60
61
|
const providerByName = new Map<string, any>();
|
|
61
62
|
for (const p of providers) {
|
|
63
|
+
if (p.id) providerById.set(p.id, p);
|
|
62
64
|
providerByName.set(p.name, p);
|
|
63
65
|
if (p.display_name) providerByName.set(p.display_name.toLowerCase(), p);
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
// Map toolkits to apps, enriching auth info from providers
|
|
68
|
+
// Map toolkits to apps, enriching auth info from providers.
|
|
69
|
+
// Use auth_provider_id from the toolkit to find the parent provider —
|
|
70
|
+
// one provider can serve many toolkits (e.g. provider "omnikit" id=49
|
|
71
|
+
// serves "omnikit-messaging", "omnikit-cms", "omnikit-billing", etc.)
|
|
67
72
|
const apps: IntegrationApp[] = toolkits.map((toolkit: any) => {
|
|
68
73
|
const name = toolkit.name || toolkit.slug;
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
|
|
75
|
+
// Primary: match via auth_provider_id (reliable link from API)
|
|
76
|
+
let provider = toolkit.auth_provider_id
|
|
77
|
+
? providerById.get(toolkit.auth_provider_id)
|
|
78
|
+
: null;
|
|
79
|
+
// Fallback: match by name
|
|
80
|
+
if (!provider) {
|
|
81
|
+
provider = providerByName.get(name) || providerByName.get(name?.toLowerCase());
|
|
82
|
+
}
|
|
71
83
|
|
|
72
84
|
let authSchemes: string[];
|
|
73
85
|
if (provider) {
|
|
@@ -78,6 +90,19 @@ export const AgentDojoProvider: IntegrationProvider = {
|
|
|
78
90
|
authSchemes = ["NONE"];
|
|
79
91
|
}
|
|
80
92
|
|
|
93
|
+
// Build credential fields from provider auth_config
|
|
94
|
+
let credentialFields: IntegrationApp["credentialFields"] | undefined;
|
|
95
|
+
if (provider?.auth_config?.required_fields) {
|
|
96
|
+
const descriptions = provider.auth_config.field_descriptions || {};
|
|
97
|
+
const required = new Set(provider.auth_config.required_fields || []);
|
|
98
|
+
const allFields = [...(provider.auth_config.required_fields || []), ...(provider.auth_config.optional_fields || [])];
|
|
99
|
+
credentialFields = allFields.map((f: string) => ({
|
|
100
|
+
name: f,
|
|
101
|
+
description: descriptions[f] || undefined,
|
|
102
|
+
required: required.has(f),
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
|
|
81
106
|
return {
|
|
82
107
|
id: String(toolkit.id),
|
|
83
108
|
name: toolkit.display_name || toolkit.name,
|
|
@@ -86,13 +111,27 @@ export const AgentDojoProvider: IntegrationProvider = {
|
|
|
86
111
|
logo: provider?.favicon || toolkit.icon_url || null,
|
|
87
112
|
categories: [],
|
|
88
113
|
authSchemes,
|
|
114
|
+
providerSlug: provider?.name || undefined,
|
|
115
|
+
credentialFields,
|
|
89
116
|
};
|
|
90
117
|
});
|
|
91
118
|
|
|
92
119
|
// Also add any providers that don't match a toolkit (standalone OAuth providers)
|
|
93
120
|
const toolkitNames = new Set(toolkits.map((t: any) => t.name));
|
|
121
|
+
const toolkitProviderIds = new Set(toolkits.map((t: any) => t.auth_provider_id).filter(Boolean));
|
|
94
122
|
for (const p of providers) {
|
|
95
|
-
if (!toolkitNames.has(p.name)) {
|
|
123
|
+
if (!toolkitNames.has(p.name) && !toolkitProviderIds.has(p.id)) {
|
|
124
|
+
let credentialFields: IntegrationApp["credentialFields"] | undefined;
|
|
125
|
+
if (p.auth_config?.required_fields) {
|
|
126
|
+
const descriptions = p.auth_config.field_descriptions || {};
|
|
127
|
+
const required = new Set(p.auth_config.required_fields || []);
|
|
128
|
+
const allFields = [...(p.auth_config.required_fields || []), ...(p.auth_config.optional_fields || [])];
|
|
129
|
+
credentialFields = allFields.map((f: string) => ({
|
|
130
|
+
name: f,
|
|
131
|
+
description: descriptions[f] || undefined,
|
|
132
|
+
required: required.has(f),
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
96
135
|
apps.push({
|
|
97
136
|
id: String(p.id),
|
|
98
137
|
name: p.display_name || p.name,
|
|
@@ -101,6 +140,7 @@ export const AgentDojoProvider: IntegrationProvider = {
|
|
|
101
140
|
logo: p.favicon || p.icon_url || null,
|
|
102
141
|
categories: [],
|
|
103
142
|
authSchemes: mapAuthSchemes(p.provider_type),
|
|
143
|
+
credentialFields,
|
|
104
144
|
});
|
|
105
145
|
}
|
|
106
146
|
}
|
|
@@ -124,6 +164,7 @@ export const AgentDojoProvider: IntegrationProvider = {
|
|
|
124
164
|
|
|
125
165
|
const data = await res.json();
|
|
126
166
|
const credentials = data.data || data.credentials || [];
|
|
167
|
+
console.log(`[AgentDojo] listConnectedAccounts: got ${credentials.length} credentials`, JSON.stringify(credentials.map((c: any) => ({ id: c.id, name: c.name, provider_name: c.provider_name, credential_type: c.credential_type }))));
|
|
127
168
|
|
|
128
169
|
return credentials.map((cred: any) => ({
|
|
129
170
|
id: String(cred.id),
|
|
@@ -146,8 +187,9 @@ export const AgentDojoProvider: IntegrationProvider = {
|
|
|
146
187
|
redirectUrl: string,
|
|
147
188
|
credentials?: ConnectionCredentials
|
|
148
189
|
): Promise<ConnectionRequest> {
|
|
149
|
-
// OAuth flow: no credentials provided
|
|
150
|
-
|
|
190
|
+
// OAuth flow: no credentials provided (neither apiKey nor multi-field)
|
|
191
|
+
const hasCredentials = credentials?.apiKey || (credentials?.fields && Object.keys(credentials.fields).length > 0);
|
|
192
|
+
if (!hasCredentials) {
|
|
151
193
|
// Init OAuth via MCP API
|
|
152
194
|
const res = await fetch(`${AGENTDOJO_API_BASE}/oauth/init`, {
|
|
153
195
|
method: "POST",
|
|
@@ -181,17 +223,58 @@ export const AgentDojoProvider: IntegrationProvider = {
|
|
|
181
223
|
}
|
|
182
224
|
|
|
183
225
|
// API key flow: store credential directly
|
|
226
|
+
// Support arbitrary credential fields via credentials.fields, fall back to single api_key
|
|
227
|
+
const credentialData = credentials.fields && Object.keys(credentials.fields).length > 0
|
|
228
|
+
? credentials.fields
|
|
229
|
+
: { api_key: credentials.apiKey };
|
|
230
|
+
|
|
231
|
+
// Resolve toolkit slug to actual provider name/id — the credential API
|
|
232
|
+
// needs the provider (e.g. "pushover" id=26), not the toolkit slug
|
|
233
|
+
// (e.g. "pushover-notifications"). Fetch the toolkit to get auth_provider_id.
|
|
234
|
+
let providerName = appSlug;
|
|
235
|
+
let providerId: string | number = appSlug;
|
|
236
|
+
try {
|
|
237
|
+
const tkRes = await fetch(`${AGENTDOJO_API_BASE}/toolkits?include_tools=false`, {
|
|
238
|
+
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
|
|
239
|
+
});
|
|
240
|
+
if (tkRes.ok) {
|
|
241
|
+
const tkData = await tkRes.json();
|
|
242
|
+
const toolkits = tkData.toolkits || tkData.data || [];
|
|
243
|
+
const toolkit = toolkits.find((t: any) => t.name === appSlug || t.slug === appSlug);
|
|
244
|
+
if (toolkit?.auth_provider_id) {
|
|
245
|
+
// Fetch provider details
|
|
246
|
+
const provRes = await fetch(`${AGENTDOJO_API_BASE}/providers?is_active=true`, {
|
|
247
|
+
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
|
|
248
|
+
});
|
|
249
|
+
if (provRes.ok) {
|
|
250
|
+
const provData = await provRes.json();
|
|
251
|
+
const providers = provData.providers || provData.data || [];
|
|
252
|
+
const prov = providers.find((p: any) => p.id === toolkit.auth_provider_id);
|
|
253
|
+
if (prov) {
|
|
254
|
+
providerName = prov.name;
|
|
255
|
+
providerId = prov.id;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} catch (e) {
|
|
261
|
+
console.warn("[AgentDojo] Failed to resolve provider for toolkit:", appSlug, e);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const credBody: Record<string, unknown> = {
|
|
265
|
+
name: providerName,
|
|
266
|
+
provider_id: providerId,
|
|
267
|
+
provider_name: providerName,
|
|
268
|
+
credential_data: credentialData,
|
|
269
|
+
};
|
|
270
|
+
console.log("[AgentDojo] Creating credential:", JSON.stringify(credBody));
|
|
184
271
|
const res = await fetch(`${AGENTDOJO_API_BASE}/credentials`, {
|
|
185
272
|
method: "POST",
|
|
186
273
|
headers: {
|
|
187
274
|
"X-API-Key": apiKey,
|
|
188
275
|
"Content-Type": "application/json",
|
|
189
276
|
},
|
|
190
|
-
body: JSON.stringify(
|
|
191
|
-
provider_id: appSlug,
|
|
192
|
-
provider_name: appSlug,
|
|
193
|
-
credential_data: { api_key: credentials.apiKey },
|
|
194
|
-
}),
|
|
277
|
+
body: JSON.stringify(credBody),
|
|
195
278
|
});
|
|
196
279
|
|
|
197
280
|
if (!res.ok) {
|
|
@@ -9,6 +9,12 @@ export interface IntegrationApp {
|
|
|
9
9
|
logo: string | null;
|
|
10
10
|
categories: string[];
|
|
11
11
|
authSchemes: string[]; // e.g., ["OAUTH2", "API_KEY"]
|
|
12
|
+
providerSlug?: string; // The underlying provider name (one provider can serve multiple toolkits)
|
|
13
|
+
credentialFields?: { // Fields required for API_KEY auth (from provider auth_config)
|
|
14
|
+
name: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
required?: boolean;
|
|
17
|
+
}[];
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
export interface ConnectedAccount {
|
|
@@ -32,6 +38,7 @@ export interface ConnectionCredentials {
|
|
|
32
38
|
bearerToken?: string;
|
|
33
39
|
username?: string;
|
|
34
40
|
password?: string;
|
|
41
|
+
fields?: Record<string, string>; // Arbitrary credential fields (e.g. { appToken: "...", userKey: "..." })
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
export interface IntegrationProvider {
|