apteva 0.4.41 → 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.7fb3e7mp.js → App.qzbx5wtj.js} +1 -1
- 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 +8 -8
- package/src/db.ts +19 -9
- package/src/integrations/agentdojo.ts +1 -0
- package/src/mcp-platform.ts +418 -63
- package/src/openapi.ts +96 -0
- package/src/providers.ts +50 -24
- package/src/routes/api/agent-utils.ts +0 -1
- package/src/routes/api/agents.ts +19 -1
- package/src/routes/api/meta-agent.ts +2 -0
- package/src/routes/api/system.ts +90 -1
- package/src/routes/api/telemetry.ts +19 -1
- package/src/routes/share.ts +85 -0
- package/src/server.ts +12 -0
- package/src/web/App.tsx +89 -11
- package/src/web/components/activity/ActivityPage.tsx +14 -14
- package/src/web/components/agents/AgentCard.tsx +14 -14
- 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/LoadingSpinner.tsx +2 -2
- package/src/web/components/common/Modal.tsx +8 -8
- package/src/web/components/common/Select.tsx +9 -9
- 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 +32 -32
- package/src/web/components/layout/Header.tsx +50 -34
- package/src/web/components/layout/Sidebar.tsx +34 -15
- package/src/web/components/mcp/IntegrationsPanel.tsx +40 -40
- package/src/web/components/mcp/McpPage.tsx +208 -208
- package/src/web/components/meta-agent/MetaAgent.tsx +12 -10
- package/src/web/components/onboarding/OnboardingWizard.tsx +25 -25
- package/src/web/components/settings/SettingsPage.tsx +258 -175
- package/src/web/components/skills/SkillsPage.tsx +88 -88
- package/src/web/components/tasks/TasksPage.tsx +339 -54
- package/src/web/components/telemetry/TelemetryPage.tsx +135 -64
- package/src/web/components/tests/TestsPage.tsx +50 -50
- package/src/web/components/threads/ThreadsPage.tsx +23 -21
- package/src/web/context/ProjectContext.tsx +6 -1
- package/src/web/context/ThemeContext.tsx +69 -0
- package/src/web/context/index.ts +2 -0
- package/src/web/styles.css +5 -3
- package/src/web/themes.ts +99 -0
- package/src/web/types.ts +0 -4
- package/dist/ActivityPage.7907h64p.js +0 -3
- package/dist/ApiDocsPage.k3jjenpq.js +0 -4
- package/dist/App.01nq20st.js +0 -4
- package/dist/App.1maqvamf.js +0 -4
- package/dist/App.2yjrh32f.js +0 -4
- package/dist/App.3qw8nben.js +0 -20
- package/dist/App.7sy3wq8c.js +0 -4
- package/dist/App.apjrmctz.js +0 -57
- package/dist/App.av6t2yhe.js +0 -4
- package/dist/App.jqj5a094.js +0 -46
- package/dist/App.mc7xf85h.js +0 -4
- package/dist/App.myxqcj9x.js +0 -4
- package/dist/App.nm91r1mp.js +0 -13
- package/dist/App.p02f4ret.js +0 -1
- package/dist/App.qcknavjz.js +0 -221
- package/dist/App.vc7vfhg4.js +0 -4
- package/dist/App.z4s9zkw5.js +0 -4
- package/dist/ConnectionsPage.z1pw5xe2.js +0 -3
- package/dist/McpPage.8vc97z0b.js +0 -3
- package/dist/SettingsPage.p61bz8kd.js +0 -3
- package/dist/SkillsPage.r9x43g3g.js +0 -3
- package/dist/TasksPage.1e0zkye4.js +0 -3
- package/dist/TelemetryPage.p9vbe4gf.js +0 -3
- package/dist/TestsPage.d4xy504e.js +0 -3
- package/dist/ThreadsPage.m016am3x.js +0 -3
package/src/openapi.ts
CHANGED
|
@@ -1006,6 +1006,40 @@ while (true) {
|
|
|
1006
1006
|
},
|
|
1007
1007
|
},
|
|
1008
1008
|
},
|
|
1009
|
+
"/tasks/{agentId}": {
|
|
1010
|
+
post: {
|
|
1011
|
+
tags: ["Tasks"],
|
|
1012
|
+
summary: "Create a task on an agent",
|
|
1013
|
+
description: "Create a new task on a running agent. The agent must have the tasks feature enabled.",
|
|
1014
|
+
parameters: [
|
|
1015
|
+
{ name: "agentId", in: "path", required: true, schema: { type: "string" }, description: "Agent ID to create the task on" },
|
|
1016
|
+
],
|
|
1017
|
+
requestBody: {
|
|
1018
|
+
required: true,
|
|
1019
|
+
content: {
|
|
1020
|
+
"application/json": {
|
|
1021
|
+
schema: {
|
|
1022
|
+
type: "object",
|
|
1023
|
+
properties: {
|
|
1024
|
+
title: { type: "string", description: "Task title" },
|
|
1025
|
+
description: { type: "string", description: "Task description" },
|
|
1026
|
+
type: { type: "string", enum: ["once", "recurring"], default: "once" },
|
|
1027
|
+
priority: { type: "integer", minimum: 1, maximum: 10, default: 5 },
|
|
1028
|
+
execute_at: { type: "string", format: "date-time", description: "Scheduled execution time (for one-time tasks)" },
|
|
1029
|
+
recurrence: { type: "string", description: "Cron expression (for recurring tasks)" },
|
|
1030
|
+
},
|
|
1031
|
+
required: ["title"],
|
|
1032
|
+
},
|
|
1033
|
+
},
|
|
1034
|
+
},
|
|
1035
|
+
},
|
|
1036
|
+
responses: {
|
|
1037
|
+
"201": { description: "Task created successfully" },
|
|
1038
|
+
"400": { description: "Agent is not running or invalid input" },
|
|
1039
|
+
"404": { description: "Agent not found" },
|
|
1040
|
+
},
|
|
1041
|
+
},
|
|
1042
|
+
},
|
|
1009
1043
|
"/tasks/{agentId}/{taskId}": {
|
|
1010
1044
|
get: {
|
|
1011
1045
|
tags: ["Tasks"],
|
|
@@ -1033,6 +1067,68 @@ while (true) {
|
|
|
1033
1067
|
"404": { description: "Agent not found" },
|
|
1034
1068
|
},
|
|
1035
1069
|
},
|
|
1070
|
+
put: {
|
|
1071
|
+
tags: ["Tasks"],
|
|
1072
|
+
summary: "Update a task on an agent",
|
|
1073
|
+
description: "Update an existing task on a running agent.",
|
|
1074
|
+
parameters: [
|
|
1075
|
+
{ name: "agentId", in: "path", required: true, schema: { type: "string" }, description: "Agent ID" },
|
|
1076
|
+
{ name: "taskId", in: "path", required: true, schema: { type: "string" }, description: "Task ID to update" },
|
|
1077
|
+
],
|
|
1078
|
+
requestBody: {
|
|
1079
|
+
required: true,
|
|
1080
|
+
content: {
|
|
1081
|
+
"application/json": {
|
|
1082
|
+
schema: {
|
|
1083
|
+
type: "object",
|
|
1084
|
+
properties: {
|
|
1085
|
+
title: { type: "string" },
|
|
1086
|
+
description: { type: "string" },
|
|
1087
|
+
type: { type: "string", enum: ["once", "recurring"] },
|
|
1088
|
+
priority: { type: "integer", minimum: 1, maximum: 10 },
|
|
1089
|
+
execute_at: { type: "string", format: "date-time" },
|
|
1090
|
+
recurrence: { type: "string" },
|
|
1091
|
+
},
|
|
1092
|
+
},
|
|
1093
|
+
},
|
|
1094
|
+
},
|
|
1095
|
+
},
|
|
1096
|
+
responses: {
|
|
1097
|
+
"200": { description: "Task updated" },
|
|
1098
|
+
"400": { description: "Agent is not running or invalid input" },
|
|
1099
|
+
"404": { description: "Agent or task not found" },
|
|
1100
|
+
},
|
|
1101
|
+
},
|
|
1102
|
+
delete: {
|
|
1103
|
+
tags: ["Tasks"],
|
|
1104
|
+
summary: "Delete a task on an agent",
|
|
1105
|
+
description: "Delete a task from a running agent.",
|
|
1106
|
+
parameters: [
|
|
1107
|
+
{ name: "agentId", in: "path", required: true, schema: { type: "string" }, description: "Agent ID" },
|
|
1108
|
+
{ name: "taskId", in: "path", required: true, schema: { type: "string" }, description: "Task ID to delete" },
|
|
1109
|
+
],
|
|
1110
|
+
responses: {
|
|
1111
|
+
"200": { description: "Task deleted" },
|
|
1112
|
+
"400": { description: "Agent is not running" },
|
|
1113
|
+
"404": { description: "Agent or task not found" },
|
|
1114
|
+
},
|
|
1115
|
+
},
|
|
1116
|
+
},
|
|
1117
|
+
"/tasks/{agentId}/{taskId}/execute": {
|
|
1118
|
+
post: {
|
|
1119
|
+
tags: ["Tasks"],
|
|
1120
|
+
summary: "Execute a task immediately",
|
|
1121
|
+
description: "Immediately execute a task on a running agent, regardless of its schedule.",
|
|
1122
|
+
parameters: [
|
|
1123
|
+
{ name: "agentId", in: "path", required: true, schema: { type: "string" }, description: "Agent ID" },
|
|
1124
|
+
{ name: "taskId", in: "path", required: true, schema: { type: "string" }, description: "Task ID to execute" },
|
|
1125
|
+
],
|
|
1126
|
+
responses: {
|
|
1127
|
+
"200": { description: "Task execution started" },
|
|
1128
|
+
"400": { description: "Agent is not running" },
|
|
1129
|
+
"404": { description: "Agent or task not found" },
|
|
1130
|
+
},
|
|
1131
|
+
},
|
|
1036
1132
|
},
|
|
1037
1133
|
"/mcp/servers": {
|
|
1038
1134
|
get: {
|
package/src/providers.ts
CHANGED
|
@@ -12,9 +12,9 @@ export const PROVIDERS = {
|
|
|
12
12
|
docsUrl: "https://console.anthropic.com/settings/keys",
|
|
13
13
|
testEndpoint: "https://api.anthropic.com/v1/messages",
|
|
14
14
|
models: [
|
|
15
|
-
{ value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", recommended: true },
|
|
16
|
-
{ value: "claude-sonnet-4-5", label: "Claude Sonnet 4.5" },
|
|
17
|
-
{ value: "claude-haiku-4-5", label: "Claude Haiku 4.5 (Fast)" },
|
|
15
|
+
{ value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", recommended: true, input_cost: 3, output_cost: 15 },
|
|
16
|
+
{ value: "claude-sonnet-4-5", label: "Claude Sonnet 4.5", input_cost: 3, output_cost: 15 },
|
|
17
|
+
{ value: "claude-haiku-4-5", label: "Claude Haiku 4.5 (Fast)", input_cost: 0.8, output_cost: 4 },
|
|
18
18
|
],
|
|
19
19
|
},
|
|
20
20
|
openai: {
|
|
@@ -26,8 +26,8 @@ export const PROVIDERS = {
|
|
|
26
26
|
docsUrl: "https://platform.openai.com/api-keys",
|
|
27
27
|
testEndpoint: "https://api.openai.com/v1/models",
|
|
28
28
|
models: [
|
|
29
|
-
{ value: "gpt-4o", label: "GPT-4o", recommended: true },
|
|
30
|
-
{ value: "gpt-4o-mini", label: "GPT-4o Mini (Fast)" },
|
|
29
|
+
{ value: "gpt-4o", label: "GPT-4o", recommended: true, input_cost: 2.5, output_cost: 10 },
|
|
30
|
+
{ value: "gpt-4o-mini", label: "GPT-4o Mini (Fast)", input_cost: 0.15, output_cost: 0.6 },
|
|
31
31
|
],
|
|
32
32
|
},
|
|
33
33
|
groq: {
|
|
@@ -39,8 +39,8 @@ export const PROVIDERS = {
|
|
|
39
39
|
docsUrl: "https://console.groq.com/keys",
|
|
40
40
|
testEndpoint: "https://api.groq.com/openai/v1/models",
|
|
41
41
|
models: [
|
|
42
|
-
{ value: "llama-3.3-70b-versatile", label: "Llama 3.3 70B", recommended: true },
|
|
43
|
-
{ value: "llama-3.1-8b-instant", label: "Llama 3.1 8B (Fast)" },
|
|
42
|
+
{ value: "llama-3.3-70b-versatile", label: "Llama 3.3 70B", recommended: true, input_cost: 0, output_cost: 0 },
|
|
43
|
+
{ value: "llama-3.1-8b-instant", label: "Llama 3.1 8B (Fast)", input_cost: 0, output_cost: 0 },
|
|
44
44
|
],
|
|
45
45
|
},
|
|
46
46
|
gemini: {
|
|
@@ -52,8 +52,8 @@ export const PROVIDERS = {
|
|
|
52
52
|
docsUrl: "https://aistudio.google.com/app/apikey",
|
|
53
53
|
testEndpoint: "https://generativelanguage.googleapis.com/v1/models",
|
|
54
54
|
models: [
|
|
55
|
-
{ value: "gemini-3-pro-preview", label: "Gemini 3 Pro Preview (Latest)", recommended: true },
|
|
56
|
-
{ value: "gemini-3-flash-preview", label: "Gemini 3 Flash Preview (Fast)" },
|
|
55
|
+
{ value: "gemini-3-pro-preview", label: "Gemini 3 Pro Preview (Latest)", recommended: true, input_cost: 2, output_cost: 12 },
|
|
56
|
+
{ value: "gemini-3-flash-preview", label: "Gemini 3 Flash Preview (Fast)", input_cost: 0.5, output_cost: 3 },
|
|
57
57
|
],
|
|
58
58
|
},
|
|
59
59
|
xai: {
|
|
@@ -65,8 +65,8 @@ export const PROVIDERS = {
|
|
|
65
65
|
docsUrl: "https://console.x.ai/",
|
|
66
66
|
testEndpoint: "https://api.x.ai/v1/models",
|
|
67
67
|
models: [
|
|
68
|
-
{ value: "grok-2", label: "Grok 2", recommended: true },
|
|
69
|
-
{ value: "grok-2-mini", label: "Grok 2 Mini (Fast)" },
|
|
68
|
+
{ value: "grok-2", label: "Grok 2", recommended: true, input_cost: 2, output_cost: 10 },
|
|
69
|
+
{ value: "grok-2-mini", label: "Grok 2 Mini (Fast)", input_cost: 0.3, output_cost: 1 },
|
|
70
70
|
],
|
|
71
71
|
},
|
|
72
72
|
together: {
|
|
@@ -78,8 +78,8 @@ export const PROVIDERS = {
|
|
|
78
78
|
docsUrl: "https://api.together.xyz/settings/api-keys",
|
|
79
79
|
testEndpoint: "https://api.together.xyz/v1/models",
|
|
80
80
|
models: [
|
|
81
|
-
{ value: "moonshotai/Kimi-K2.5", label: "Kimi K2.5", recommended: true },
|
|
82
|
-
{ value: "moonshotai/Kimi-K2-Thinking", label: "Kimi K2 Thinking (Reasoning)" },
|
|
81
|
+
{ value: "moonshotai/Kimi-K2.5", label: "Kimi K2.5", recommended: true, input_cost: 1, output_cost: 4 },
|
|
82
|
+
{ value: "moonshotai/Kimi-K2-Thinking", label: "Kimi K2 Thinking (Reasoning)", input_cost: 1, output_cost: 4 },
|
|
83
83
|
],
|
|
84
84
|
},
|
|
85
85
|
fireworks: {
|
|
@@ -91,10 +91,10 @@ export const PROVIDERS = {
|
|
|
91
91
|
docsUrl: "https://fireworks.ai/api-keys",
|
|
92
92
|
testEndpoint: "https://api.fireworks.ai/inference/v1/models",
|
|
93
93
|
models: [
|
|
94
|
-
{ value: "accounts/fireworks/models/kimi-k2p5", label: "Kimi K2.5", recommended: true },
|
|
95
|
-
{ value: "accounts/fireworks/models/kimi-k2-thinking", label: "Kimi K2 Thinking (Reasoning)" },
|
|
96
|
-
{ value: "accounts/fireworks/models/minimax-m2p5", label: "MiniMax M2.5" },
|
|
97
|
-
{ value: "accounts/fireworks/models/glm-5", label: "GLM 5" },
|
|
94
|
+
{ value: "accounts/fireworks/models/kimi-k2p5", label: "Kimi K2.5", recommended: true, input_cost: 1, output_cost: 4 },
|
|
95
|
+
{ value: "accounts/fireworks/models/kimi-k2-thinking", label: "Kimi K2 Thinking (Reasoning)", input_cost: 1, output_cost: 4 },
|
|
96
|
+
{ value: "accounts/fireworks/models/minimax-m2p5", label: "MiniMax M2.5", input_cost: 1, output_cost: 4 },
|
|
97
|
+
{ value: "accounts/fireworks/models/glm-5", label: "GLM 5", input_cost: 1, output_cost: 4 },
|
|
98
98
|
],
|
|
99
99
|
},
|
|
100
100
|
moonshot: {
|
|
@@ -106,8 +106,24 @@ export const PROVIDERS = {
|
|
|
106
106
|
docsUrl: "https://platform.moonshot.cn/console/api-keys",
|
|
107
107
|
testEndpoint: "https://api.moonshot.cn/v1/models",
|
|
108
108
|
models: [
|
|
109
|
-
{ value: "moonshot-v1-128k", label: "Kimi 128K", recommended: true },
|
|
110
|
-
{ value: "moonshot-v1-32k", label: "Kimi 32K (Fast)" },
|
|
109
|
+
{ value: "moonshot-v1-128k", label: "Kimi 128K", recommended: true, input_cost: 1, output_cost: 4 },
|
|
110
|
+
{ value: "moonshot-v1-32k", label: "Kimi 32K (Fast)", input_cost: 0.5, output_cost: 2 },
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
venice: {
|
|
114
|
+
id: "venice",
|
|
115
|
+
name: "Venice",
|
|
116
|
+
displayName: "Venice AI",
|
|
117
|
+
type: "llm" as const,
|
|
118
|
+
envVar: "VENICE_API_KEY",
|
|
119
|
+
docsUrl: "https://docs.venice.ai/overview/pricing",
|
|
120
|
+
testEndpoint: "https://api.venice.ai/api/v1/models",
|
|
121
|
+
models: [
|
|
122
|
+
{ value: "llama-3.3-70b", label: "Llama 3.3 70B", recommended: true, input_cost: 0.7, output_cost: 2.8 },
|
|
123
|
+
{ value: "olafangensan-glm-4.7-flash-heretic", label: "GLM 4.7 Flash Heretic", input_cost: 0.14, output_cost: 0.8 },
|
|
124
|
+
{ value: "qwen3-235b-a22b-instruct-2507", label: "Qwen 3 235B Instruct", input_cost: 0.15, output_cost: 0.75 },
|
|
125
|
+
{ value: "deepseek-v3.2", label: "DeepSeek V3.2", input_cost: 0.4, output_cost: 1 },
|
|
126
|
+
{ value: "venice-uncensored", label: "Venice Uncensored 1.1", input_cost: 0.2, output_cost: 0.9 },
|
|
111
127
|
],
|
|
112
128
|
},
|
|
113
129
|
ollama: {
|
|
@@ -122,11 +138,11 @@ export const PROVIDERS = {
|
|
|
122
138
|
defaultBaseUrl: "http://localhost:11434",
|
|
123
139
|
models: [
|
|
124
140
|
// Default models - actual list fetched dynamically from Ollama
|
|
125
|
-
{ value: "llama3.3", label: "Llama 3.3 (70B)", recommended: true },
|
|
126
|
-
{ value: "llama3.2", label: "Llama 3.2 (3B)" },
|
|
127
|
-
{ value: "qwen2.5", label: "Qwen 2.5" },
|
|
128
|
-
{ value: "mistral", label: "Mistral" },
|
|
129
|
-
{ value: "deepseek-r1", label: "DeepSeek R1" },
|
|
141
|
+
{ value: "llama3.3", label: "Llama 3.3 (70B)", recommended: true, input_cost: 0, output_cost: 0 },
|
|
142
|
+
{ value: "llama3.2", label: "Llama 3.2 (3B)", input_cost: 0, output_cost: 0 },
|
|
143
|
+
{ value: "qwen2.5", label: "Qwen 2.5", input_cost: 0, output_cost: 0 },
|
|
144
|
+
{ value: "mistral", label: "Mistral", input_cost: 0, output_cost: 0 },
|
|
145
|
+
{ value: "deepseek-r1", label: "DeepSeek R1", input_cost: 0, output_cost: 0 },
|
|
130
146
|
],
|
|
131
147
|
},
|
|
132
148
|
// Browser Providers
|
|
@@ -217,6 +233,16 @@ export const PROVIDERS = {
|
|
|
217
233
|
|
|
218
234
|
export type ProviderId = keyof typeof PROVIDERS;
|
|
219
235
|
|
|
236
|
+
/** Get cost per 1M tokens for a given provider + model. Returns { input_cost, output_cost } in USD. */
|
|
237
|
+
export function getModelCost(provider: string, model: string): { input_cost: number; output_cost: number } {
|
|
238
|
+
const providerDef = PROVIDERS[provider as ProviderId];
|
|
239
|
+
if (!providerDef || !("models" in providerDef)) return { input_cost: 0, output_cost: 0 };
|
|
240
|
+
const modelDef = (providerDef.models as ReadonlyArray<{ value: string; input_cost?: number; output_cost?: number }>)
|
|
241
|
+
.find(m => m.value === model);
|
|
242
|
+
if (!modelDef) return { input_cost: 0, output_cost: 0 };
|
|
243
|
+
return { input_cost: modelDef.input_cost ?? 0, output_cost: modelDef.output_cost ?? 0 };
|
|
244
|
+
}
|
|
245
|
+
|
|
220
246
|
// Provider Keys Management
|
|
221
247
|
export const ProviderKeys = {
|
|
222
248
|
// Save an API key (encrypts before storing)
|
|
@@ -337,7 +337,6 @@ export function buildAgentConfig(agent: Agent, providerKey: string) {
|
|
|
337
337
|
const baseUrl = process.env.PUBLIC_URL || `http://localhost:${process.env.PORT || 4280}`;
|
|
338
338
|
return {
|
|
339
339
|
enabled: multiAgentConfig.enabled,
|
|
340
|
-
mode: multiAgentConfig.mode || "worker",
|
|
341
340
|
group: multiAgentConfig.group || agent.project_id || undefined,
|
|
342
341
|
// This agent's reachable URL for peer communication
|
|
343
342
|
url: `http://localhost:${agent.port}`,
|
package/src/routes/api/agents.ts
CHANGED
|
@@ -270,6 +270,25 @@ export async function handleAgentRoutes(
|
|
|
270
270
|
});
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
// ==================== SHARE LINK ====================
|
|
274
|
+
|
|
275
|
+
// GET /api/agents/:id/share-token - Get the share token for this agent
|
|
276
|
+
const shareTokenMatch = path.match(/^\/api\/agents\/([^/]+)\/share-token$/);
|
|
277
|
+
if (shareTokenMatch && method === "GET") {
|
|
278
|
+
const agent = AgentDB.findById(shareTokenMatch[1]);
|
|
279
|
+
if (!agent) {
|
|
280
|
+
return json({ error: "Agent not found" }, 404);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const { getShareToken } = await import("../share");
|
|
284
|
+
const token = getShareToken(agent.id);
|
|
285
|
+
if (!token) {
|
|
286
|
+
return json({ error: "Could not generate share token" }, 500);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return json({ token });
|
|
290
|
+
}
|
|
291
|
+
|
|
273
292
|
// ==================== AGENT LIFECYCLE ====================
|
|
274
293
|
|
|
275
294
|
// POST /api/agents/:id/start - Start an agent
|
|
@@ -840,7 +859,6 @@ export async function handleAgentRoutes(
|
|
|
840
859
|
id: a.id,
|
|
841
860
|
name: a.name,
|
|
842
861
|
url: `http://localhost:${a.port}`,
|
|
843
|
-
mode: agentConfig.mode || "worker",
|
|
844
862
|
group: agentConfig.group || a.project_id,
|
|
845
863
|
};
|
|
846
864
|
});
|
package/src/routes/api/system.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { json } from "./helpers";
|
|
2
|
-
import { META_AGENT_ENABLED, fetchFromAgent, startAgentProcess, setAgentStatus } from "./agent-utils";
|
|
2
|
+
import { META_AGENT_ENABLED, fetchFromAgent, agentFetch, startAgentProcess, setAgentStatus } from "./agent-utils";
|
|
3
3
|
import { AgentDB } from "../../db";
|
|
4
4
|
import { ProviderKeys } from "../../providers";
|
|
5
5
|
import { agentProcesses, getBinaryStatus, BIN_DIR } from "../../server";
|
|
@@ -44,6 +44,7 @@ export async function handleSystemRoutes(
|
|
|
44
44
|
return json({
|
|
45
45
|
projects: process.env.PROJECTS_ENABLED === "true",
|
|
46
46
|
metaAgent: process.env.META_AGENT_ENABLED === "true",
|
|
47
|
+
costTracking: process.env.COST_TRACKING_ENABLED !== "false",
|
|
47
48
|
});
|
|
48
49
|
}
|
|
49
50
|
|
|
@@ -180,6 +181,94 @@ export async function handleSystemRoutes(
|
|
|
180
181
|
return json({ task: { ...data, agentId: agent.id, agentName: agent.name } });
|
|
181
182
|
}
|
|
182
183
|
|
|
184
|
+
// POST /api/tasks/:agentId/:taskId/execute - Execute a task immediately
|
|
185
|
+
const executeTaskMatch = path.match(/^\/api\/tasks\/([^/]+)\/([^/]+)\/execute$/);
|
|
186
|
+
if (executeTaskMatch && method === "POST") {
|
|
187
|
+
const [, agentId, taskId] = executeTaskMatch;
|
|
188
|
+
const agent = AgentDB.findById(agentId);
|
|
189
|
+
if (!agent) return json({ error: "Agent not found" }, 404);
|
|
190
|
+
if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const res = await agentFetch(agentId, agent.port, `/tasks/${taskId}/execute`, {
|
|
194
|
+
method: "POST",
|
|
195
|
+
signal: AbortSignal.timeout(5000),
|
|
196
|
+
});
|
|
197
|
+
const data = await res.json();
|
|
198
|
+
if (!res.ok) return json({ error: data.error || `HTTP ${res.status}` }, res.status);
|
|
199
|
+
return json(data);
|
|
200
|
+
} catch (err) {
|
|
201
|
+
return json({ error: `Failed to execute task: ${err}` }, 500);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// POST /api/tasks/:agentId - Create a task on an agent
|
|
206
|
+
const createTaskMatch = path.match(/^\/api\/tasks\/([^/]+)$/);
|
|
207
|
+
if (createTaskMatch && method === "POST") {
|
|
208
|
+
const agentId = createTaskMatch[1];
|
|
209
|
+
const agent = AgentDB.findById(agentId);
|
|
210
|
+
if (!agent) return json({ error: "Agent not found" }, 404);
|
|
211
|
+
if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const body = await req.json();
|
|
215
|
+
const res = await agentFetch(agentId, agent.port, "/tasks", {
|
|
216
|
+
method: "POST",
|
|
217
|
+
headers: { "Content-Type": "application/json" },
|
|
218
|
+
body: JSON.stringify(body),
|
|
219
|
+
signal: AbortSignal.timeout(5000),
|
|
220
|
+
});
|
|
221
|
+
const data = await res.json();
|
|
222
|
+
if (!res.ok) return json({ error: data.error || `HTTP ${res.status}` }, res.status);
|
|
223
|
+
return json(data, 201);
|
|
224
|
+
} catch (err) {
|
|
225
|
+
return json({ error: `Failed to create task: ${err}` }, 500);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// PUT /api/tasks/:agentId/:taskId - Update a task on an agent
|
|
230
|
+
if (singleTaskMatch && method === "PUT") {
|
|
231
|
+
const [, agentId, taskId] = singleTaskMatch;
|
|
232
|
+
const agent = AgentDB.findById(agentId);
|
|
233
|
+
if (!agent) return json({ error: "Agent not found" }, 404);
|
|
234
|
+
if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
const body = await req.json();
|
|
238
|
+
const res = await agentFetch(agentId, agent.port, `/tasks/${taskId}`, {
|
|
239
|
+
method: "PUT",
|
|
240
|
+
headers: { "Content-Type": "application/json" },
|
|
241
|
+
body: JSON.stringify(body),
|
|
242
|
+
signal: AbortSignal.timeout(5000),
|
|
243
|
+
});
|
|
244
|
+
const data = await res.json();
|
|
245
|
+
if (!res.ok) return json({ error: data.error || `HTTP ${res.status}` }, res.status);
|
|
246
|
+
return json(data);
|
|
247
|
+
} catch (err) {
|
|
248
|
+
return json({ error: `Failed to update task: ${err}` }, 500);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// DELETE /api/tasks/:agentId/:taskId - Delete a task on an agent
|
|
253
|
+
if (singleTaskMatch && method === "DELETE") {
|
|
254
|
+
const [, agentId, taskId] = singleTaskMatch;
|
|
255
|
+
const agent = AgentDB.findById(agentId);
|
|
256
|
+
if (!agent) return json({ error: "Agent not found" }, 404);
|
|
257
|
+
if (agent.status !== "running" || !agent.port) return json({ error: "Agent is not running" }, 400);
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
const res = await agentFetch(agentId, agent.port, `/tasks/${taskId}`, {
|
|
261
|
+
method: "DELETE",
|
|
262
|
+
signal: AbortSignal.timeout(5000),
|
|
263
|
+
});
|
|
264
|
+
const data = await res.json();
|
|
265
|
+
if (!res.ok) return json({ error: data.error || `HTTP ${res.status}` }, res.status);
|
|
266
|
+
return json(data);
|
|
267
|
+
} catch (err) {
|
|
268
|
+
return json({ error: `Failed to delete task: ${err}` }, 500);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
183
272
|
// GET /api/dashboard - Get dashboard statistics
|
|
184
273
|
if (path === "/api/dashboard" && method === "GET") {
|
|
185
274
|
const url = new URL(req.url);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { json } from "./helpers";
|
|
2
|
-
import { TelemetryDB } from "../../db";
|
|
2
|
+
import { TelemetryDB, AgentDB } from "../../db";
|
|
3
|
+
import { getModelCost } from "../../providers";
|
|
3
4
|
import { telemetryBroadcaster, type TelemetryEvent } from "../../server";
|
|
4
5
|
|
|
5
6
|
export async function handleTelemetryRoutes(
|
|
@@ -35,6 +36,23 @@ export async function handleTelemetryRoutes(
|
|
|
35
36
|
|
|
36
37
|
// Filter out debug events - too noisy
|
|
37
38
|
const filteredEvents = body.events.filter(e => e.level !== "debug");
|
|
39
|
+
|
|
40
|
+
// Compute cost per LLM event if cost tracking is enabled
|
|
41
|
+
const costTrackingEnabled = process.env.COST_TRACKING_ENABLED !== "false";
|
|
42
|
+
if (costTrackingEnabled) {
|
|
43
|
+
const agent = AgentDB.findById(body.agent_id);
|
|
44
|
+
if (agent) {
|
|
45
|
+
const pricing = getModelCost(agent.provider, agent.model);
|
|
46
|
+
for (const event of filteredEvents) {
|
|
47
|
+
if (event.category === "LLM" && event.data) {
|
|
48
|
+
const inputTokens = (event.data.input_tokens as number) || 0;
|
|
49
|
+
const outputTokens = (event.data.output_tokens as number) || 0;
|
|
50
|
+
(event as any).cost = (inputTokens * pricing.input_cost + outputTokens * pricing.output_cost) / 1_000_000;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
38
56
|
const inserted = TelemetryDB.insertBatch(body.agent_id, filteredEvents);
|
|
39
57
|
|
|
40
58
|
// Broadcast to SSE clients
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import { AgentDB, type Agent } from "../db";
|
|
3
|
+
import { agentFetch } from "./api/agent-utils";
|
|
4
|
+
|
|
5
|
+
function deriveShareToken(apiKey: string, agentId: string): string {
|
|
6
|
+
return createHash("sha256")
|
|
7
|
+
.update(apiKey + ":" + agentId + ":share")
|
|
8
|
+
.digest("hex")
|
|
9
|
+
.substring(0, 32);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function findAgentByShareToken(token: string): Agent | null {
|
|
13
|
+
const agents = AgentDB.findAll();
|
|
14
|
+
for (const agent of agents) {
|
|
15
|
+
const apiKey = AgentDB.getApiKey(agent.id);
|
|
16
|
+
if (!apiKey) continue;
|
|
17
|
+
if (deriveShareToken(apiKey, agent.id) === token) return agent;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Get the share token for an agent (used by API route for the UI) */
|
|
23
|
+
export function getShareToken(agentId: string): string | null {
|
|
24
|
+
const apiKey = AgentDB.getApiKey(agentId);
|
|
25
|
+
if (!apiKey) return null;
|
|
26
|
+
return deriveShareToken(apiKey, agentId);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function handleShareRequest(req: Request, path: string): Promise<Response | null> {
|
|
30
|
+
// Match /share/<32 hex chars> sub-paths for API calls
|
|
31
|
+
const infoMatch = path.match(/^\/share\/([a-f0-9]{32})\/info$/);
|
|
32
|
+
const chatMatch = path.match(/^\/share\/([a-f0-9]{32})\/chat$/);
|
|
33
|
+
|
|
34
|
+
if (!infoMatch && !chatMatch) return null;
|
|
35
|
+
|
|
36
|
+
const token = (infoMatch || chatMatch)![1];
|
|
37
|
+
const agent = findAgentByShareToken(token);
|
|
38
|
+
|
|
39
|
+
// Intentionally vague 404 — don't reveal whether token exists
|
|
40
|
+
if (!agent) {
|
|
41
|
+
return new Response("Not found", { status: 404 });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// GET /share/:token/info — agent info (no secrets)
|
|
45
|
+
if (infoMatch && req.method === "GET") {
|
|
46
|
+
return Response.json({
|
|
47
|
+
name: agent.name,
|
|
48
|
+
status: agent.status,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// POST /share/:token/chat — proxy to agent
|
|
53
|
+
if (chatMatch && req.method === "POST") {
|
|
54
|
+
if (agent.status !== "running" || !agent.port) {
|
|
55
|
+
return Response.json({ error: "Agent is currently offline" }, { status: 503 });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const body = await req.json();
|
|
60
|
+
const response = await agentFetch(agent.id, agent.port, "/chat", {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: { "Content-Type": "application/json" },
|
|
63
|
+
body: JSON.stringify(body),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const errorText = await response.text();
|
|
68
|
+
return Response.json({ error: `Agent error: ${errorText}` }, { status: response.status });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return new Response(response.body, {
|
|
72
|
+
status: 200,
|
|
73
|
+
headers: {
|
|
74
|
+
"Content-Type": response.headers.get("Content-Type") || "text/event-stream",
|
|
75
|
+
"Cache-Control": "no-cache",
|
|
76
|
+
"Connection": "keep-alive",
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
} catch (err) {
|
|
80
|
+
return Response.json({ error: "Failed to connect to agent" }, { status: 500 });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return null;
|
|
85
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Server, type Subprocess } from "bun";
|
|
2
2
|
import { handleApiRequest } from "./routes/api";
|
|
3
3
|
import { handleAuthRequest } from "./routes/auth";
|
|
4
|
+
import { handleShareRequest } from "./routes/share";
|
|
4
5
|
import { serveStatic } from "./routes/static";
|
|
5
6
|
import { join } from "path";
|
|
6
7
|
import { homedir } from "os";
|
|
@@ -421,6 +422,17 @@ const server = Bun.serve({
|
|
|
421
422
|
return response;
|
|
422
423
|
}
|
|
423
424
|
|
|
425
|
+
// Share routes (public, token-authenticated)
|
|
426
|
+
if (path.startsWith("/share/")) {
|
|
427
|
+
const shareResponse = await handleShareRequest(req, path);
|
|
428
|
+
if (shareResponse) {
|
|
429
|
+
Object.entries(corsHeaders).forEach(([key, value]) => {
|
|
430
|
+
shareResponse.headers.set(key, value);
|
|
431
|
+
});
|
|
432
|
+
return shareResponse;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
424
436
|
// Serve static files (React app)
|
|
425
437
|
return serveStatic(req, path);
|
|
426
438
|
},
|