apteva 0.4.31 → 0.4.41
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.7907h64p.js +3 -0
- package/dist/ApiDocsPage.k3jjenpq.js +4 -0
- package/dist/App.01nq20st.js +4 -0
- package/dist/App.1maqvamf.js +4 -0
- package/dist/App.2yjrh32f.js +4 -0
- package/dist/App.3qw8nben.js +20 -0
- package/dist/App.7fb3e7mp.js +4 -0
- package/dist/App.7sy3wq8c.js +4 -0
- package/dist/App.apjrmctz.js +57 -0
- package/dist/App.av6t2yhe.js +4 -0
- package/dist/App.jqj5a094.js +46 -0
- package/dist/App.mc7xf85h.js +4 -0
- package/dist/App.myxqcj9x.js +4 -0
- package/dist/App.nm91r1mp.js +13 -0
- package/dist/App.qcknavjz.js +221 -0
- package/dist/App.vc7vfhg4.js +4 -0
- package/dist/App.z4s9zkw5.js +4 -0
- package/dist/ConnectionsPage.z1pw5xe2.js +3 -0
- package/dist/McpPage.8vc97z0b.js +3 -0
- package/dist/SettingsPage.p61bz8kd.js +3 -0
- package/dist/SkillsPage.r9x43g3g.js +3 -0
- package/dist/TasksPage.1e0zkye4.js +3 -0
- package/dist/TelemetryPage.p9vbe4gf.js +3 -0
- package/dist/TestsPage.d4xy504e.js +3 -0
- package/dist/ThreadsPage.m016am3x.js +3 -0
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +8 -7
- package/src/crypto.ts +4 -3
- package/src/db.ts +153 -28
- package/src/integrations/agentdojo.ts +94 -12
- package/src/integrations/index.ts +7 -0
- package/src/mcp-platform.ts +494 -121
- package/src/providers.ts +12 -12
- package/src/routes/api/agent-utils.ts +59 -46
- package/src/routes/api/agents.ts +52 -1
- package/src/routes/api/integrations.ts +11 -5
- package/src/routes/api/mcp.ts +5 -4
- package/src/routes/api/meta-agent.ts +35 -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 +8 -13
- package/src/server.ts +31 -32
- package/src/triggers/agentdojo.ts +2 -2
- package/src/web/App.tsx +18 -10
- package/src/web/components/activity/ActivityPage.tsx +241 -388
- package/src/web/components/agents/AgentCard.tsx +5 -13
- package/src/web/components/common/Icons.tsx +8 -0
- package/src/web/components/common/Select.tsx +4 -3
- package/src/web/components/dashboard/Dashboard.tsx +155 -30
- package/src/web/components/index.ts +1 -1
- package/src/web/components/layout/Sidebar.tsx +7 -1
- package/src/web/components/mcp/IntegrationsPanel.tsx +126 -35
- package/src/web/components/mcp/McpPage.tsx +10 -1
- package/src/web/components/meta-agent/MetaAgent.tsx +4 -2
- package/src/web/components/settings/SettingsPage.tsx +133 -48
- package/src/web/components/tasks/TasksPage.tsx +48 -16
- package/src/web/components/telemetry/TelemetryPage.tsx +184 -0
- package/src/web/components/threads/ThreadsPage.tsx +313 -0
- package/src/web/context/AuthContext.tsx +3 -3
- package/src/web/context/ProjectContext.tsx +3 -3
- package/src/web/context/TelemetryContext.tsx +24 -6
- package/src/web/context/index.ts +1 -1
- package/src/web/styles.css +20 -4
- package/src/web/types.ts +4 -3
- 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.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apteva",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.41",
|
|
4
4
|
"description": "Run AI agents locally. Multi-provider support for Claude, GPT, Gemini, Llama, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -72,14 +72,15 @@
|
|
|
72
72
|
"ink-spinner": "^5.0.0",
|
|
73
73
|
"ink-text-input": "^6.0.0",
|
|
74
74
|
"react": "19",
|
|
75
|
-
"react-dom": "19"
|
|
75
|
+
"react-dom": "19",
|
|
76
|
+
"recharts": "^3.7.0"
|
|
76
77
|
},
|
|
77
78
|
"optionalDependencies": {
|
|
78
|
-
"@apteva/apteva-darwin-arm64": "0.4.
|
|
79
|
-
"@apteva/apteva-darwin-x64": "0.4.
|
|
80
|
-
"@apteva/apteva-linux-arm64": "0.4.
|
|
81
|
-
"@apteva/apteva-linux-x64": "0.4.
|
|
82
|
-
"@apteva/apteva-win32-x64": "0.4.
|
|
79
|
+
"@apteva/apteva-darwin-arm64": "0.4.33",
|
|
80
|
+
"@apteva/apteva-darwin-x64": "0.4.33",
|
|
81
|
+
"@apteva/apteva-linux-arm64": "0.4.33",
|
|
82
|
+
"@apteva/apteva-linux-x64": "0.4.33",
|
|
83
|
+
"@apteva/apteva-win32-x64": "0.4.33",
|
|
83
84
|
"@apteva/agent-darwin-arm64": "^1.33.73",
|
|
84
85
|
"@apteva/agent-darwin-x64": "^1.33.73",
|
|
85
86
|
"@apteva/agent-linux-arm64": "^1.33.73",
|
package/src/crypto.ts
CHANGED
|
@@ -136,12 +136,13 @@ export function validateKeyFormat(provider: string, key: string): { valid: boole
|
|
|
136
136
|
return { valid: false, error: "API key cannot be empty" };
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
// Ollama and
|
|
140
|
-
if (provider === "ollama" || provider === "
|
|
139
|
+
// Ollama and CDP use URLs instead of API keys
|
|
140
|
+
if (provider === "ollama" || provider === "cdp") {
|
|
141
141
|
if (trimmed.startsWith("http://") || trimmed.startsWith("https://") || trimmed.startsWith("ws://") || trimmed.startsWith("wss://")) {
|
|
142
142
|
return { valid: true };
|
|
143
143
|
}
|
|
144
|
-
|
|
144
|
+
const example = provider === "cdp" ? "ws://localhost:9222" : "http://localhost:11434";
|
|
145
|
+
return { valid: false, error: `${provider} requires a valid URL (e.g., ${example})` };
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
// Multi-field providers store JSON objects — validate the inner api_key
|
package/src/db.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface AgentBuiltinTools {
|
|
|
20
20
|
|
|
21
21
|
export interface OperatorConfig {
|
|
22
22
|
enabled: boolean;
|
|
23
|
-
browser_provider?: string; // "
|
|
23
|
+
browser_provider?: string; // "browserengine" | "browserbase" | "steel" | "cdp"
|
|
24
24
|
display_width?: number;
|
|
25
25
|
display_height?: number;
|
|
26
26
|
max_actions_per_turn?: number;
|
|
@@ -328,6 +328,12 @@ export function initDatabase(dataDir: string): Database {
|
|
|
328
328
|
db.run("PRAGMA busy_timeout = 5000");
|
|
329
329
|
db.run("PRAGMA foreign_keys = ON");
|
|
330
330
|
|
|
331
|
+
// Performance PRAGMAs
|
|
332
|
+
db.run("PRAGMA synchronous = NORMAL"); // Safe with WAL, much faster than FULL
|
|
333
|
+
db.run("PRAGMA cache_size = -20000"); // 20MB page cache (negative = KB)
|
|
334
|
+
db.run("PRAGMA mmap_size = 30000000"); // 30MB memory-mapped I/O
|
|
335
|
+
db.run("PRAGMA temp_store = MEMORY"); // Keep temp tables in memory
|
|
336
|
+
|
|
331
337
|
// Run migrations
|
|
332
338
|
runMigrations();
|
|
333
339
|
|
|
@@ -486,6 +492,7 @@ function runMigrations() {
|
|
|
486
492
|
);
|
|
487
493
|
CREATE INDEX IF NOT EXISTS idx_telemetry_agent ON telemetry_events(agent_id);
|
|
488
494
|
CREATE INDEX IF NOT EXISTS idx_telemetry_time ON telemetry_events(timestamp);
|
|
495
|
+
CREATE INDEX IF NOT EXISTS idx_telemetry_agent_time ON telemetry_events(agent_id, timestamp DESC);
|
|
489
496
|
CREATE INDEX IF NOT EXISTS idx_telemetry_category ON telemetry_events(category);
|
|
490
497
|
CREATE INDEX IF NOT EXISTS idx_telemetry_level ON telemetry_events(level);
|
|
491
498
|
CREATE INDEX IF NOT EXISTS idx_telemetry_trace ON telemetry_events(trace_id);
|
|
@@ -835,6 +842,31 @@ function runMigrations() {
|
|
|
835
842
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_provider_keys_unique ON provider_keys(provider_id, COALESCE(project_id, ''));
|
|
836
843
|
`,
|
|
837
844
|
},
|
|
845
|
+
{
|
|
846
|
+
name: "034_repair_provider_keys_project_support",
|
|
847
|
+
sql: `
|
|
848
|
+
-- Repair: migrations 022 and 029 may have failed but were marked as applied.
|
|
849
|
+
-- Recreate provider_keys with project_id support from whatever current state.
|
|
850
|
+
CREATE TABLE IF NOT EXISTS provider_keys_repair (
|
|
851
|
+
id TEXT PRIMARY KEY,
|
|
852
|
+
provider_id TEXT NOT NULL,
|
|
853
|
+
encrypted_key TEXT NOT NULL,
|
|
854
|
+
key_hint TEXT,
|
|
855
|
+
is_valid INTEGER DEFAULT 1,
|
|
856
|
+
last_tested_at TEXT,
|
|
857
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
858
|
+
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
859
|
+
name TEXT
|
|
860
|
+
);
|
|
861
|
+
INSERT OR IGNORE INTO provider_keys_repair (id, provider_id, encrypted_key, key_hint, is_valid, last_tested_at, created_at)
|
|
862
|
+
SELECT id, provider_id, encrypted_key, key_hint, is_valid, last_tested_at, created_at FROM provider_keys;
|
|
863
|
+
DROP TABLE IF EXISTS provider_keys;
|
|
864
|
+
ALTER TABLE provider_keys_repair RENAME TO provider_keys;
|
|
865
|
+
CREATE INDEX IF NOT EXISTS idx_provider_keys_provider ON provider_keys(provider_id);
|
|
866
|
+
CREATE INDEX IF NOT EXISTS idx_provider_keys_project ON provider_keys(project_id);
|
|
867
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_provider_keys_unique ON provider_keys(provider_id, COALESCE(project_id, ''));
|
|
868
|
+
`,
|
|
869
|
+
},
|
|
838
870
|
];
|
|
839
871
|
|
|
840
872
|
// Check which migrations have been applied
|
|
@@ -1056,10 +1088,10 @@ export const AgentDB = {
|
|
|
1056
1088
|
|
|
1057
1089
|
// Find agents that have a specific skill
|
|
1058
1090
|
findBySkill(skillId: string): Agent[] {
|
|
1059
|
-
//
|
|
1091
|
+
// Use json_each to properly search the JSON array (avoids full table scan with LIKE)
|
|
1060
1092
|
const rows = db.query(
|
|
1061
|
-
`SELECT
|
|
1062
|
-
).all(
|
|
1093
|
+
`SELECT DISTINCT a.* FROM agents a, json_each(a.skills) AS s WHERE s.value = ? ORDER BY a.created_at DESC`
|
|
1094
|
+
).all(skillId) as AgentRow[];
|
|
1063
1095
|
return rows.map(rowToAgent);
|
|
1064
1096
|
},
|
|
1065
1097
|
|
|
@@ -1091,14 +1123,23 @@ export const AgentDB = {
|
|
|
1091
1123
|
return row.count;
|
|
1092
1124
|
},
|
|
1093
1125
|
|
|
1094
|
-
//
|
|
1126
|
+
// In-memory cache for decrypted API keys (avoids expensive scryptSync on every request)
|
|
1127
|
+
_apiKeyCache: new Map<string, string>(),
|
|
1128
|
+
|
|
1129
|
+
// Get decrypted API key for an agent (cached)
|
|
1095
1130
|
getApiKey(id: string): string | null {
|
|
1131
|
+
// Check cache first
|
|
1132
|
+
const cached = this._apiKeyCache.get(id);
|
|
1133
|
+
if (cached) return cached;
|
|
1134
|
+
|
|
1096
1135
|
const agent = this.findById(id);
|
|
1097
1136
|
if (!agent || !agent.api_key_encrypted) {
|
|
1098
1137
|
return null;
|
|
1099
1138
|
}
|
|
1100
1139
|
try {
|
|
1101
|
-
|
|
1140
|
+
const key = decrypt(agent.api_key_encrypted);
|
|
1141
|
+
if (key) this._apiKeyCache.set(id, key);
|
|
1142
|
+
return key;
|
|
1102
1143
|
} catch {
|
|
1103
1144
|
return null;
|
|
1104
1145
|
}
|
|
@@ -1118,6 +1159,8 @@ export const AgentDB = {
|
|
|
1118
1159
|
[encrypted, now, id]
|
|
1119
1160
|
);
|
|
1120
1161
|
|
|
1162
|
+
// Update cache
|
|
1163
|
+
this._apiKeyCache.set(id, newApiKey);
|
|
1121
1164
|
return newApiKey;
|
|
1122
1165
|
},
|
|
1123
1166
|
|
|
@@ -1129,7 +1172,9 @@ export const AgentDB = {
|
|
|
1129
1172
|
// If agent already has a key, return it
|
|
1130
1173
|
if (agent.api_key_encrypted) {
|
|
1131
1174
|
try {
|
|
1132
|
-
|
|
1175
|
+
const key = decrypt(agent.api_key_encrypted);
|
|
1176
|
+
if (key) this._apiKeyCache.set(id, key);
|
|
1177
|
+
return key;
|
|
1133
1178
|
} catch {
|
|
1134
1179
|
// Key is corrupted, regenerate
|
|
1135
1180
|
}
|
|
@@ -1395,7 +1440,7 @@ export const ProviderKeysDB = {
|
|
|
1395
1440
|
|
|
1396
1441
|
// Find all keys for a provider (global + all projects)
|
|
1397
1442
|
findAllByProvider(providerId: string): ProviderKey[] {
|
|
1398
|
-
const rows = db.query("SELECT * FROM provider_keys WHERE provider_id = ? ORDER BY project_id
|
|
1443
|
+
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
1444
|
return rows.map(rowToProviderKey);
|
|
1400
1445
|
},
|
|
1401
1446
|
|
|
@@ -1407,7 +1452,7 @@ export const ProviderKeysDB = {
|
|
|
1407
1452
|
|
|
1408
1453
|
// Get all provider keys
|
|
1409
1454
|
findAll(): ProviderKey[] {
|
|
1410
|
-
const rows = db.query("SELECT * FROM provider_keys ORDER BY provider_id, project_id
|
|
1455
|
+
const rows = db.query("SELECT * FROM provider_keys ORDER BY provider_id, (project_id IS NOT NULL), created_at DESC").all() as ProviderKeyRow[];
|
|
1411
1456
|
return rows.map(rowToProviderKey);
|
|
1412
1457
|
},
|
|
1413
1458
|
|
|
@@ -1535,6 +1580,22 @@ export const McpServerDB = {
|
|
|
1535
1580
|
return rows.map(rowToMcpServer);
|
|
1536
1581
|
},
|
|
1537
1582
|
|
|
1583
|
+
// Light version: skips expensive decryption for listing endpoints
|
|
1584
|
+
findAllLight(): McpServer[] {
|
|
1585
|
+
const rows = db.query("SELECT * FROM mcp_servers ORDER BY created_at DESC").all() as McpServerRow[];
|
|
1586
|
+
return rows.map(rowToMcpServerLight);
|
|
1587
|
+
},
|
|
1588
|
+
|
|
1589
|
+
// Light batch load by IDs: skips decryption (used by toApiAgentsBatch)
|
|
1590
|
+
findByIdsLight(ids: string[]): Map<string, McpServer> {
|
|
1591
|
+
if (ids.length === 0) return new Map();
|
|
1592
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
1593
|
+
const rows = db.query(`SELECT * FROM mcp_servers WHERE id IN (${placeholders})`).all(...ids) as McpServerRow[];
|
|
1594
|
+
const map = new Map<string, McpServer>();
|
|
1595
|
+
for (const row of rows) map.set(row.id, rowToMcpServerLight(row));
|
|
1596
|
+
return map;
|
|
1597
|
+
},
|
|
1598
|
+
|
|
1538
1599
|
findRunning(): McpServer[] {
|
|
1539
1600
|
const rows = db.query("SELECT * FROM mcp_servers WHERE status = 'running'").all() as McpServerRow[];
|
|
1540
1601
|
return rows.map(rowToMcpServer);
|
|
@@ -1661,6 +1722,30 @@ export const McpServerDB = {
|
|
|
1661
1722
|
const rows = db.query("SELECT * FROM mcp_servers WHERE project_id IS NULL ORDER BY created_at DESC").all() as McpServerRow[];
|
|
1662
1723
|
return rows.map(rowToMcpServer);
|
|
1663
1724
|
},
|
|
1725
|
+
|
|
1726
|
+
// Light versions (skip decryption) for listing endpoints
|
|
1727
|
+
findByProjectLight(projectId: string | null): McpServer[] {
|
|
1728
|
+
if (projectId === null) {
|
|
1729
|
+
const rows = db.query("SELECT * FROM mcp_servers WHERE project_id IS NULL ORDER BY created_at DESC").all() as McpServerRow[];
|
|
1730
|
+
return rows.map(rowToMcpServerLight);
|
|
1731
|
+
}
|
|
1732
|
+
const rows = db.query("SELECT * FROM mcp_servers WHERE project_id = ? ORDER BY created_at DESC").all(projectId) as McpServerRow[];
|
|
1733
|
+
return rows.map(rowToMcpServerLight);
|
|
1734
|
+
},
|
|
1735
|
+
|
|
1736
|
+
findForAgentLight(agentProjectId: string | null): McpServer[] {
|
|
1737
|
+
if (agentProjectId === null) {
|
|
1738
|
+
const rows = db.query("SELECT * FROM mcp_servers WHERE project_id IS NULL ORDER BY created_at DESC").all() as McpServerRow[];
|
|
1739
|
+
return rows.map(rowToMcpServerLight);
|
|
1740
|
+
}
|
|
1741
|
+
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[];
|
|
1742
|
+
return rows.map(rowToMcpServerLight);
|
|
1743
|
+
},
|
|
1744
|
+
|
|
1745
|
+
findGlobalLight(): McpServer[] {
|
|
1746
|
+
const rows = db.query("SELECT * FROM mcp_servers WHERE project_id IS NULL ORDER BY created_at DESC").all() as McpServerRow[];
|
|
1747
|
+
return rows.map(rowToMcpServerLight);
|
|
1748
|
+
},
|
|
1664
1749
|
};
|
|
1665
1750
|
|
|
1666
1751
|
// MCP Server Tool CRUD operations (for local servers)
|
|
@@ -1792,6 +1877,27 @@ function rowToMcpServer(row: McpServerRow): McpServer {
|
|
|
1792
1877
|
};
|
|
1793
1878
|
}
|
|
1794
1879
|
|
|
1880
|
+
// Light version: skips expensive decryption of env/headers for listing endpoints
|
|
1881
|
+
function rowToMcpServerLight(row: McpServerRow): McpServer {
|
|
1882
|
+
return {
|
|
1883
|
+
id: row.id,
|
|
1884
|
+
name: row.name,
|
|
1885
|
+
type: row.type as McpServer["type"],
|
|
1886
|
+
package: row.package,
|
|
1887
|
+
pip_module: row.pip_module,
|
|
1888
|
+
command: row.command,
|
|
1889
|
+
args: row.args,
|
|
1890
|
+
env: {},
|
|
1891
|
+
url: row.url,
|
|
1892
|
+
headers: {},
|
|
1893
|
+
port: row.port,
|
|
1894
|
+
status: row.status as "stopped" | "running",
|
|
1895
|
+
source: row.source,
|
|
1896
|
+
project_id: row.project_id,
|
|
1897
|
+
created_at: row.created_at,
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1795
1901
|
// Telemetry Event types
|
|
1796
1902
|
// User types
|
|
1797
1903
|
export interface User {
|
|
@@ -1892,26 +1998,30 @@ export const TelemetryDB = {
|
|
|
1892
1998
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1893
1999
|
`);
|
|
1894
2000
|
|
|
2001
|
+
// Wrap in transaction for massive speedup (single fsync instead of one per row)
|
|
1895
2002
|
let inserted = 0;
|
|
1896
|
-
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
2003
|
+
const insertAll = db.transaction(() => {
|
|
2004
|
+
for (const event of events) {
|
|
2005
|
+
const result = stmt.run(
|
|
2006
|
+
event.id,
|
|
2007
|
+
agentId,
|
|
2008
|
+
event.timestamp,
|
|
2009
|
+
event.category,
|
|
2010
|
+
event.type,
|
|
2011
|
+
event.level,
|
|
2012
|
+
event.trace_id || null,
|
|
2013
|
+
event.span_id || null,
|
|
2014
|
+
event.thread_id || null,
|
|
2015
|
+
event.data ? JSON.stringify(event.data) : null,
|
|
2016
|
+
event.metadata ? JSON.stringify(event.metadata) : null,
|
|
2017
|
+
event.duration_ms || null,
|
|
2018
|
+
event.error || null,
|
|
2019
|
+
now
|
|
2020
|
+
);
|
|
2021
|
+
if (result.changes > 0) inserted++;
|
|
2022
|
+
}
|
|
2023
|
+
});
|
|
2024
|
+
insertAll();
|
|
1915
2025
|
return inserted;
|
|
1916
2026
|
},
|
|
1917
2027
|
|
|
@@ -2810,6 +2920,21 @@ export const SubscriptionDB = {
|
|
|
2810
2920
|
return rows.map(rowToSubscription);
|
|
2811
2921
|
},
|
|
2812
2922
|
|
|
2923
|
+
// Batch load subscriptions for multiple agents (1 query instead of N)
|
|
2924
|
+
findByAgentIds(agentIds: string[]): Map<string, Subscription[]> {
|
|
2925
|
+
const result = new Map<string, Subscription[]>();
|
|
2926
|
+
if (agentIds.length === 0) return result;
|
|
2927
|
+
const placeholders = agentIds.map(() => "?").join(",");
|
|
2928
|
+
const rows = db.query(`SELECT * FROM subscriptions WHERE agent_id IN (${placeholders})`).all(...agentIds) as SubscriptionRow[];
|
|
2929
|
+
for (const row of rows) {
|
|
2930
|
+
const sub = rowToSubscription(row);
|
|
2931
|
+
const list = result.get(sub.agent_id) || [];
|
|
2932
|
+
list.push(sub);
|
|
2933
|
+
result.set(sub.agent_id, list);
|
|
2934
|
+
}
|
|
2935
|
+
return result;
|
|
2936
|
+
},
|
|
2937
|
+
|
|
2813
2938
|
findAll(projectId?: string | null): Subscription[] {
|
|
2814
2939
|
if (projectId) {
|
|
2815
2940
|
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
|
}
|
|
@@ -146,8 +186,9 @@ export const AgentDojoProvider: IntegrationProvider = {
|
|
|
146
186
|
redirectUrl: string,
|
|
147
187
|
credentials?: ConnectionCredentials
|
|
148
188
|
): Promise<ConnectionRequest> {
|
|
149
|
-
// OAuth flow: no credentials provided
|
|
150
|
-
|
|
189
|
+
// OAuth flow: no credentials provided (neither apiKey nor multi-field)
|
|
190
|
+
const hasCredentials = credentials?.apiKey || (credentials?.fields && Object.keys(credentials.fields).length > 0);
|
|
191
|
+
if (!hasCredentials) {
|
|
151
192
|
// Init OAuth via MCP API
|
|
152
193
|
const res = await fetch(`${AGENTDOJO_API_BASE}/oauth/init`, {
|
|
153
194
|
method: "POST",
|
|
@@ -181,17 +222,58 @@ export const AgentDojoProvider: IntegrationProvider = {
|
|
|
181
222
|
}
|
|
182
223
|
|
|
183
224
|
// API key flow: store credential directly
|
|
225
|
+
// Support arbitrary credential fields via credentials.fields, fall back to single api_key
|
|
226
|
+
const credentialData = credentials.fields && Object.keys(credentials.fields).length > 0
|
|
227
|
+
? credentials.fields
|
|
228
|
+
: { api_key: credentials.apiKey };
|
|
229
|
+
|
|
230
|
+
// Resolve toolkit slug to actual provider name/id — the credential API
|
|
231
|
+
// needs the provider (e.g. "pushover" id=26), not the toolkit slug
|
|
232
|
+
// (e.g. "pushover-notifications"). Fetch the toolkit to get auth_provider_id.
|
|
233
|
+
let providerName = appSlug;
|
|
234
|
+
let providerId: string | number = appSlug;
|
|
235
|
+
try {
|
|
236
|
+
const tkRes = await fetch(`${AGENTDOJO_API_BASE}/toolkits?include_tools=false`, {
|
|
237
|
+
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
|
|
238
|
+
});
|
|
239
|
+
if (tkRes.ok) {
|
|
240
|
+
const tkData = await tkRes.json();
|
|
241
|
+
const toolkits = tkData.toolkits || tkData.data || [];
|
|
242
|
+
const toolkit = toolkits.find((t: any) => t.name === appSlug || t.slug === appSlug);
|
|
243
|
+
if (toolkit?.auth_provider_id) {
|
|
244
|
+
// Fetch provider details
|
|
245
|
+
const provRes = await fetch(`${AGENTDOJO_API_BASE}/providers?is_active=true`, {
|
|
246
|
+
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
|
|
247
|
+
});
|
|
248
|
+
if (provRes.ok) {
|
|
249
|
+
const provData = await provRes.json();
|
|
250
|
+
const providers = provData.providers || provData.data || [];
|
|
251
|
+
const prov = providers.find((p: any) => p.id === toolkit.auth_provider_id);
|
|
252
|
+
if (prov) {
|
|
253
|
+
providerName = prov.name;
|
|
254
|
+
providerId = prov.id;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} catch (e) {
|
|
260
|
+
console.warn("[AgentDojo] Failed to resolve provider for toolkit:", appSlug, e);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const credBody: Record<string, unknown> = {
|
|
264
|
+
name: providerName,
|
|
265
|
+
provider_id: providerId,
|
|
266
|
+
provider_name: providerName,
|
|
267
|
+
credential_data: credentialData,
|
|
268
|
+
};
|
|
269
|
+
console.log("[AgentDojo] Creating credential:", JSON.stringify(credBody));
|
|
184
270
|
const res = await fetch(`${AGENTDOJO_API_BASE}/credentials`, {
|
|
185
271
|
method: "POST",
|
|
186
272
|
headers: {
|
|
187
273
|
"X-API-Key": apiKey,
|
|
188
274
|
"Content-Type": "application/json",
|
|
189
275
|
},
|
|
190
|
-
body: JSON.stringify(
|
|
191
|
-
provider_id: appSlug,
|
|
192
|
-
provider_name: appSlug,
|
|
193
|
-
credential_data: { api_key: credentials.apiKey },
|
|
194
|
-
}),
|
|
276
|
+
body: JSON.stringify(credBody),
|
|
195
277
|
});
|
|
196
278
|
|
|
197
279
|
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 {
|