@naisys/supervisor 3.0.0-beta.10
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/bin/naisys-supervisor +2 -0
- package/client-dist/android-chrome-192x192.png +0 -0
- package/client-dist/android-chrome-512x512.png +0 -0
- package/client-dist/apple-touch-icon.png +0 -0
- package/client-dist/assets/index-CKg0vgt5.css +1 -0
- package/client-dist/assets/index-WzoDF0aQ.js +177 -0
- package/client-dist/assets/naisys-logo-CzoPnn5I.webp +0 -0
- package/client-dist/favicon-16x16.png +0 -0
- package/client-dist/favicon-32x32.png +0 -0
- package/client-dist/favicon.ico +0 -0
- package/client-dist/index.html +49 -0
- package/client-dist/site.webmanifest +22 -0
- package/dist/api-reference.js +54 -0
- package/dist/auth-middleware.js +116 -0
- package/dist/database/hubDb.js +26 -0
- package/dist/database/supervisorDb.js +18 -0
- package/dist/error-helpers.js +13 -0
- package/dist/hateoas.js +61 -0
- package/dist/logger.js +11 -0
- package/dist/route-helpers.js +7 -0
- package/dist/routes/admin.js +209 -0
- package/dist/routes/agentChat.js +194 -0
- package/dist/routes/agentConfig.js +265 -0
- package/dist/routes/agentLifecycle.js +350 -0
- package/dist/routes/agentMail.js +171 -0
- package/dist/routes/agentRuns.js +90 -0
- package/dist/routes/agents.js +236 -0
- package/dist/routes/api.js +52 -0
- package/dist/routes/attachments.js +18 -0
- package/dist/routes/auth.js +103 -0
- package/dist/routes/costs.js +51 -0
- package/dist/routes/hosts.js +296 -0
- package/dist/routes/models.js +152 -0
- package/dist/routes/root.js +56 -0
- package/dist/routes/schemas.js +31 -0
- package/dist/routes/status.js +20 -0
- package/dist/routes/users.js +420 -0
- package/dist/routes/variables.js +103 -0
- package/dist/schema-registry.js +23 -0
- package/dist/services/agentConfigService.js +182 -0
- package/dist/services/agentHostStatusService.js +178 -0
- package/dist/services/agentService.js +291 -0
- package/dist/services/attachmentProxyService.js +131 -0
- package/dist/services/browserSocketService.js +78 -0
- package/dist/services/chatService.js +201 -0
- package/dist/services/configExportService.js +61 -0
- package/dist/services/costsService.js +127 -0
- package/dist/services/hostService.js +156 -0
- package/dist/services/hubConnectionService.js +320 -0
- package/dist/services/logFileService.js +11 -0
- package/dist/services/mailService.js +154 -0
- package/dist/services/modelService.js +92 -0
- package/dist/services/runsService.js +168 -0
- package/dist/services/userService.js +147 -0
- package/dist/services/variableService.js +23 -0
- package/dist/supervisorServer.js +221 -0
- package/package.json +79 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { AgentUsernameParamsSchema, ArchiveChatResponseSchema, ChatConversationsRequestSchema, ChatConversationsResponseSchema, ChatMessagesRequestSchema, ChatMessagesResponseSchema, ErrorResponseSchema, SendChatRequestSchema, SendChatResponseSchema, } from "@naisys/supervisor-shared";
|
|
2
|
+
import { hasPermission, requirePermission } from "../auth-middleware.js";
|
|
3
|
+
import { badRequest, notFound } from "../error-helpers.js";
|
|
4
|
+
import { API_PREFIX } from "../hateoas.js";
|
|
5
|
+
import { resolveAgentId } from "../services/agentService.js";
|
|
6
|
+
import { uploadToHub } from "../services/attachmentProxyService.js";
|
|
7
|
+
import { archiveAllChatMessages, getConversations, getMessages, sendChatMessage, } from "../services/chatService.js";
|
|
8
|
+
function sendChatAction(username) {
|
|
9
|
+
return {
|
|
10
|
+
rel: "send",
|
|
11
|
+
href: `${API_PREFIX}/agents/${username}/chat`,
|
|
12
|
+
method: "POST",
|
|
13
|
+
title: "Send Chat Message",
|
|
14
|
+
schema: `${API_PREFIX}/schemas/SendChat`,
|
|
15
|
+
body: { fromId: 0, toIds: [0], message: "" },
|
|
16
|
+
alternateEncoding: {
|
|
17
|
+
contentType: "multipart/form-data",
|
|
18
|
+
description: "Send as multipart to include file attachments",
|
|
19
|
+
fileFields: ["attachments"],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function archiveChatAction(username) {
|
|
24
|
+
return {
|
|
25
|
+
rel: "archive",
|
|
26
|
+
href: `${API_PREFIX}/agents/${username}/chat/archive`,
|
|
27
|
+
method: "POST",
|
|
28
|
+
title: "Archive All Chat Messages",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export default function agentChatRoutes(fastify, _options) {
|
|
32
|
+
// GET /:username/chat — List conversations for agent
|
|
33
|
+
fastify.get("/:username/chat", {
|
|
34
|
+
schema: {
|
|
35
|
+
description: "Get chat conversations for a specific agent",
|
|
36
|
+
tags: ["Chat"],
|
|
37
|
+
params: AgentUsernameParamsSchema,
|
|
38
|
+
querystring: ChatConversationsRequestSchema,
|
|
39
|
+
response: {
|
|
40
|
+
200: ChatConversationsResponseSchema,
|
|
41
|
+
500: ErrorResponseSchema,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}, async (request, reply) => {
|
|
45
|
+
const { username } = request.params;
|
|
46
|
+
const { page, count } = request.query;
|
|
47
|
+
const id = resolveAgentId(username);
|
|
48
|
+
if (!id) {
|
|
49
|
+
return notFound(reply, `Agent '${username}' not found`);
|
|
50
|
+
}
|
|
51
|
+
const { conversations, total } = await getConversations(id, page, count);
|
|
52
|
+
const canSend = hasPermission(request.supervisorUser, "agent_communication");
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
conversations,
|
|
56
|
+
total,
|
|
57
|
+
_actions: canSend
|
|
58
|
+
? [sendChatAction(username), archiveChatAction(username)]
|
|
59
|
+
: undefined,
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
// GET /:username/chat/:participants — Messages in a conversation
|
|
63
|
+
fastify.get("/:username/chat/:participants", {
|
|
64
|
+
schema: {
|
|
65
|
+
description: "Get chat messages for a specific conversation",
|
|
66
|
+
tags: ["Chat"],
|
|
67
|
+
querystring: ChatMessagesRequestSchema,
|
|
68
|
+
response: {
|
|
69
|
+
200: ChatMessagesResponseSchema,
|
|
70
|
+
500: ErrorResponseSchema,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
}, async (request, _reply) => {
|
|
74
|
+
const { username, participants } = request.params;
|
|
75
|
+
const { updatedSince, page, count } = request.query;
|
|
76
|
+
const data = await getMessages(participants, updatedSince, page, count);
|
|
77
|
+
const canSend = hasPermission(request.supervisorUser, "agent_communication");
|
|
78
|
+
return {
|
|
79
|
+
success: true,
|
|
80
|
+
messages: data.messages,
|
|
81
|
+
total: data.total,
|
|
82
|
+
timestamp: data.timestamp,
|
|
83
|
+
_actions: canSend ? [sendChatAction(username)] : undefined,
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
// POST /:username/chat/archive — Archive all chat messages
|
|
87
|
+
fastify.post("/:username/chat/archive", {
|
|
88
|
+
preHandler: [requirePermission("agent_communication")],
|
|
89
|
+
schema: {
|
|
90
|
+
description: "Archive all chat messages for an agent",
|
|
91
|
+
tags: ["Chat"],
|
|
92
|
+
params: AgentUsernameParamsSchema,
|
|
93
|
+
response: {
|
|
94
|
+
200: ArchiveChatResponseSchema,
|
|
95
|
+
500: ErrorResponseSchema,
|
|
96
|
+
},
|
|
97
|
+
security: [{ cookieAuth: [] }],
|
|
98
|
+
},
|
|
99
|
+
}, async (request, reply) => {
|
|
100
|
+
const { username } = request.params;
|
|
101
|
+
const id = resolveAgentId(username);
|
|
102
|
+
if (!id) {
|
|
103
|
+
return notFound(reply, `Agent '${username}' not found`);
|
|
104
|
+
}
|
|
105
|
+
const archivedCount = await archiveAllChatMessages(id);
|
|
106
|
+
return { success: true, archivedCount };
|
|
107
|
+
});
|
|
108
|
+
// POST /:username/chat — Send chat message
|
|
109
|
+
fastify.post("/:username/chat", {
|
|
110
|
+
preHandler: [requirePermission("agent_communication")],
|
|
111
|
+
schema: {
|
|
112
|
+
description: "Send a chat message as an agent with optional attachments. Supports JSON and multipart/form-data",
|
|
113
|
+
tags: ["Chat"],
|
|
114
|
+
params: AgentUsernameParamsSchema,
|
|
115
|
+
// No body schema — multipart requests are parsed manually via request.parts()
|
|
116
|
+
response: {
|
|
117
|
+
200: SendChatResponseSchema,
|
|
118
|
+
400: ErrorResponseSchema,
|
|
119
|
+
500: ErrorResponseSchema,
|
|
120
|
+
},
|
|
121
|
+
security: [{ cookieAuth: [] }],
|
|
122
|
+
},
|
|
123
|
+
}, async (request, reply) => {
|
|
124
|
+
const contentType = request.headers["content-type"];
|
|
125
|
+
let fromId = 0, toIds = [], message = "";
|
|
126
|
+
let attachmentBuffers = [];
|
|
127
|
+
if (contentType?.includes("multipart/form-data")) {
|
|
128
|
+
const parts = request.parts();
|
|
129
|
+
for await (const part of parts) {
|
|
130
|
+
if (part.type === "field") {
|
|
131
|
+
const field = part;
|
|
132
|
+
switch (field.fieldname) {
|
|
133
|
+
case "fromId":
|
|
134
|
+
fromId = Number(field.value);
|
|
135
|
+
break;
|
|
136
|
+
case "toIds":
|
|
137
|
+
try {
|
|
138
|
+
toIds = JSON.parse(field.value);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return badRequest(reply, "toIds must be valid JSON array");
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
case "message":
|
|
145
|
+
message = field.value;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else if (part.type === "file") {
|
|
150
|
+
const file = part;
|
|
151
|
+
if (file.fieldname === "attachments") {
|
|
152
|
+
const buffer = await file.toBuffer();
|
|
153
|
+
attachmentBuffers.push({
|
|
154
|
+
filename: file.filename || "unnamed_file",
|
|
155
|
+
data: buffer,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const body = request.body;
|
|
163
|
+
fromId = body.fromId;
|
|
164
|
+
toIds = body.toIds;
|
|
165
|
+
message = body.message;
|
|
166
|
+
}
|
|
167
|
+
const parsed = SendChatRequestSchema.safeParse({
|
|
168
|
+
fromId,
|
|
169
|
+
toIds,
|
|
170
|
+
message,
|
|
171
|
+
});
|
|
172
|
+
if (!parsed.success) {
|
|
173
|
+
return badRequest(reply, parsed.error.message);
|
|
174
|
+
}
|
|
175
|
+
({ fromId, toIds, message } = parsed.data);
|
|
176
|
+
// Upload attachments to hub and collect IDs
|
|
177
|
+
let attachmentIds;
|
|
178
|
+
if (attachmentBuffers.length > 0) {
|
|
179
|
+
attachmentIds = [];
|
|
180
|
+
for (const att of attachmentBuffers) {
|
|
181
|
+
const id = await uploadToHub(att.data, att.filename, fromId, "mail");
|
|
182
|
+
attachmentIds.push(id);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const result = await sendChatMessage(fromId, toIds, message, attachmentIds);
|
|
186
|
+
if (result.success) {
|
|
187
|
+
return reply.code(200).send(result);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
return reply.code(500).send(result);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=agentChat.js.map
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { AgentConfigFileSchema } from "@naisys/common";
|
|
2
|
+
import { AgentUsernameParamsSchema, ConfigRevisionListResponseSchema, ErrorResponseSchema, ExportAgentConfigResponseSchema, GetAgentConfigResponseSchema, ImportAgentConfigRequestSchema, ImportAgentConfigResponseSchema, UpdateAgentConfigRequestSchema, UpdateAgentConfigResponseSchema, } from "@naisys/supervisor-shared";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import { hasPermission, requirePermission } from "../auth-middleware.js";
|
|
5
|
+
import { badRequest, notFound } from "../error-helpers.js";
|
|
6
|
+
import { API_PREFIX } from "../hateoas.js";
|
|
7
|
+
import { getAgentConfigById, getConfigRevisions, updateAgentConfigById, } from "../services/agentConfigService.js";
|
|
8
|
+
import { resolveAgentId } from "../services/agentService.js";
|
|
9
|
+
import { getAllModelsFromDb } from "../services/modelService.js";
|
|
10
|
+
/** Validate model keys in config against known models. Returns error message or null. */
|
|
11
|
+
async function validateModelKeys(config) {
|
|
12
|
+
const isTemplateVar = (v) => /^\$\{.+\}$/.test(v);
|
|
13
|
+
const allModels = await getAllModelsFromDb();
|
|
14
|
+
const keysOfType = (type) => new Set(allModels
|
|
15
|
+
.filter((r) => r.type === type)
|
|
16
|
+
.map((r) => r.key));
|
|
17
|
+
const validLlmKeys = keysOfType("llm");
|
|
18
|
+
const validImageKeys = keysOfType("image");
|
|
19
|
+
const invalidModels = [];
|
|
20
|
+
if (!isTemplateVar(config.shellModel) &&
|
|
21
|
+
!validLlmKeys.has(config.shellModel)) {
|
|
22
|
+
invalidModels.push(`shellModel: "${config.shellModel}"`);
|
|
23
|
+
}
|
|
24
|
+
if (config.imageModel &&
|
|
25
|
+
!isTemplateVar(config.imageModel) &&
|
|
26
|
+
!validImageKeys.has(config.imageModel)) {
|
|
27
|
+
invalidModels.push(`imageModel: "${config.imageModel}"`);
|
|
28
|
+
}
|
|
29
|
+
if (invalidModels.length > 0) {
|
|
30
|
+
return `Invalid model key(s): ${invalidModels.join(", ")}`;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
export default function agentConfigRoutes(fastify, _options) {
|
|
35
|
+
// GET /:username/config — Get parsed agent config
|
|
36
|
+
fastify.get("/:username/config", {
|
|
37
|
+
schema: {
|
|
38
|
+
description: "Get parsed agent configuration",
|
|
39
|
+
tags: ["Agents"],
|
|
40
|
+
params: AgentUsernameParamsSchema,
|
|
41
|
+
response: {
|
|
42
|
+
200: GetAgentConfigResponseSchema,
|
|
43
|
+
404: ErrorResponseSchema,
|
|
44
|
+
500: ErrorResponseSchema,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
}, async (request, reply) => {
|
|
48
|
+
try {
|
|
49
|
+
const { username } = request.params;
|
|
50
|
+
const id = resolveAgentId(username);
|
|
51
|
+
if (!id) {
|
|
52
|
+
return notFound(reply, `Agent '${username}' not found`);
|
|
53
|
+
}
|
|
54
|
+
const config = await getAgentConfigById(id);
|
|
55
|
+
const canManage = hasPermission(request.supervisorUser, "manage_agents");
|
|
56
|
+
return {
|
|
57
|
+
config,
|
|
58
|
+
_actions: canManage
|
|
59
|
+
? [
|
|
60
|
+
{
|
|
61
|
+
rel: "update",
|
|
62
|
+
href: `${API_PREFIX}/agents/${username}/config`,
|
|
63
|
+
method: "PUT",
|
|
64
|
+
title: "Update Config",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
rel: "import-config",
|
|
68
|
+
href: `${API_PREFIX}/agents/${username}/config/import`,
|
|
69
|
+
method: "POST",
|
|
70
|
+
title: "Import Config",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
rel: "export-config",
|
|
74
|
+
href: `${API_PREFIX}/agents/${username}/config/export`,
|
|
75
|
+
method: "GET",
|
|
76
|
+
title: "Export Config",
|
|
77
|
+
},
|
|
78
|
+
]
|
|
79
|
+
: undefined,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
request.log.error(error, "Error in GET /agents/:username/config route");
|
|
84
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
85
|
+
if (errorMessage.includes("not found")) {
|
|
86
|
+
return notFound(reply, errorMessage);
|
|
87
|
+
}
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
// PUT /:username/config — Update agent config
|
|
92
|
+
fastify.put("/:username/config", {
|
|
93
|
+
preHandler: [requirePermission("manage_agents")],
|
|
94
|
+
schema: {
|
|
95
|
+
description: "Update agent configuration",
|
|
96
|
+
tags: ["Agents"],
|
|
97
|
+
params: AgentUsernameParamsSchema,
|
|
98
|
+
body: UpdateAgentConfigRequestSchema,
|
|
99
|
+
response: {
|
|
100
|
+
200: UpdateAgentConfigResponseSchema,
|
|
101
|
+
400: ErrorResponseSchema,
|
|
102
|
+
404: ErrorResponseSchema,
|
|
103
|
+
500: ErrorResponseSchema,
|
|
104
|
+
},
|
|
105
|
+
security: [{ cookieAuth: [] }],
|
|
106
|
+
},
|
|
107
|
+
}, async (request, reply) => {
|
|
108
|
+
try {
|
|
109
|
+
const { username } = request.params;
|
|
110
|
+
const { config } = request.body;
|
|
111
|
+
const id = resolveAgentId(username);
|
|
112
|
+
if (!id) {
|
|
113
|
+
return notFound(reply, `Agent '${username}' not found`);
|
|
114
|
+
}
|
|
115
|
+
// Validate model keys against known models
|
|
116
|
+
const modelError = await validateModelKeys(config);
|
|
117
|
+
if (modelError) {
|
|
118
|
+
return badRequest(reply, modelError);
|
|
119
|
+
}
|
|
120
|
+
const updatedConfig = await updateAgentConfigById(id, config, true, request.supervisorUser?.id);
|
|
121
|
+
return {
|
|
122
|
+
success: true,
|
|
123
|
+
message: "Agent configuration updated successfully",
|
|
124
|
+
config: updatedConfig,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
request.log.error(error, "Error in PUT /agents/:username/config route");
|
|
129
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
130
|
+
if (errorMessage.includes("not found")) {
|
|
131
|
+
return notFound(reply, errorMessage);
|
|
132
|
+
}
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// GET /:username/config/export — Export agent config as YAML
|
|
137
|
+
fastify.get("/:username/config/export", {
|
|
138
|
+
schema: {
|
|
139
|
+
description: "Export agent configuration as YAML",
|
|
140
|
+
tags: ["Agents"],
|
|
141
|
+
params: AgentUsernameParamsSchema,
|
|
142
|
+
response: {
|
|
143
|
+
200: ExportAgentConfigResponseSchema,
|
|
144
|
+
404: ErrorResponseSchema,
|
|
145
|
+
500: ErrorResponseSchema,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
}, async (request, reply) => {
|
|
149
|
+
try {
|
|
150
|
+
const { username } = request.params;
|
|
151
|
+
const id = resolveAgentId(username);
|
|
152
|
+
if (!id) {
|
|
153
|
+
return notFound(reply, `Agent '${username}' not found`);
|
|
154
|
+
}
|
|
155
|
+
const config = await getAgentConfigById(id);
|
|
156
|
+
const yamlString = yaml.dump(config, { lineWidth: -1 });
|
|
157
|
+
return { yaml: yamlString };
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
request.log.error(error, "Error in GET /agents/:username/config/export route");
|
|
161
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
162
|
+
if (errorMessage.includes("not found")) {
|
|
163
|
+
return notFound(reply, errorMessage);
|
|
164
|
+
}
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
// POST /:username/config/import — Import agent config from YAML
|
|
169
|
+
fastify.post("/:username/config/import", {
|
|
170
|
+
preHandler: [requirePermission("manage_agents")],
|
|
171
|
+
schema: {
|
|
172
|
+
description: "Import agent configuration from YAML",
|
|
173
|
+
tags: ["Agents"],
|
|
174
|
+
params: AgentUsernameParamsSchema,
|
|
175
|
+
body: ImportAgentConfigRequestSchema,
|
|
176
|
+
response: {
|
|
177
|
+
200: ImportAgentConfigResponseSchema,
|
|
178
|
+
400: ErrorResponseSchema,
|
|
179
|
+
404: ErrorResponseSchema,
|
|
180
|
+
500: ErrorResponseSchema,
|
|
181
|
+
},
|
|
182
|
+
security: [{ cookieAuth: [] }],
|
|
183
|
+
},
|
|
184
|
+
}, async (request, reply) => {
|
|
185
|
+
try {
|
|
186
|
+
const { username } = request.params;
|
|
187
|
+
const { yaml: yamlString } = request.body;
|
|
188
|
+
const id = resolveAgentId(username);
|
|
189
|
+
if (!id) {
|
|
190
|
+
return notFound(reply, `Agent '${username}' not found`);
|
|
191
|
+
}
|
|
192
|
+
// Parse YAML
|
|
193
|
+
let parsed;
|
|
194
|
+
try {
|
|
195
|
+
parsed = yaml.load(yamlString);
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
const message = err instanceof Error ? err.message : "Invalid YAML syntax";
|
|
199
|
+
return badRequest(reply, `YAML parse error: ${message}`);
|
|
200
|
+
}
|
|
201
|
+
// Validate against schema
|
|
202
|
+
let config;
|
|
203
|
+
try {
|
|
204
|
+
config = AgentConfigFileSchema.parse(parsed);
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
const message = err instanceof Error ? err.message : "Invalid config structure";
|
|
208
|
+
return badRequest(reply, `Config validation error: ${message}`);
|
|
209
|
+
}
|
|
210
|
+
// Validate model keys
|
|
211
|
+
const modelError = await validateModelKeys(config);
|
|
212
|
+
if (modelError) {
|
|
213
|
+
return badRequest(reply, modelError);
|
|
214
|
+
}
|
|
215
|
+
const updatedConfig = await updateAgentConfigById(id, config, false, request.supervisorUser?.id);
|
|
216
|
+
return {
|
|
217
|
+
success: true,
|
|
218
|
+
message: "Agent configuration imported successfully",
|
|
219
|
+
config: updatedConfig,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
request.log.error(error, "Error in POST /agents/:username/config/import route");
|
|
224
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
225
|
+
if (errorMessage.includes("not found")) {
|
|
226
|
+
return notFound(reply, errorMessage);
|
|
227
|
+
}
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
// GET /:username/config/revisions — List config revision history
|
|
232
|
+
fastify.get("/:username/config/revisions", {
|
|
233
|
+
schema: {
|
|
234
|
+
description: "List config revision history for an agent",
|
|
235
|
+
tags: ["Agents"],
|
|
236
|
+
params: AgentUsernameParamsSchema,
|
|
237
|
+
response: {
|
|
238
|
+
200: ConfigRevisionListResponseSchema,
|
|
239
|
+
404: ErrorResponseSchema,
|
|
240
|
+
500: ErrorResponseSchema,
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
}, async (request, reply) => {
|
|
244
|
+
try {
|
|
245
|
+
const { username } = request.params;
|
|
246
|
+
const id = resolveAgentId(username);
|
|
247
|
+
if (!id) {
|
|
248
|
+
return notFound(reply, `Agent '${username}' not found`);
|
|
249
|
+
}
|
|
250
|
+
const revisions = await getConfigRevisions(id);
|
|
251
|
+
return {
|
|
252
|
+
items: revisions.map((r) => ({
|
|
253
|
+
...r,
|
|
254
|
+
config: yaml.dump(JSON.parse(r.config), { lineWidth: -1 }),
|
|
255
|
+
createdAt: r.createdAt.toISOString(),
|
|
256
|
+
})),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
request.log.error(error, "Error in GET /agents/:username/config/revisions route");
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=agentConfig.js.map
|