@axiom-lattice/gateway 2.1.28 → 2.1.30
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/.turbo/turbo-build.log +12 -8
- package/AGENTS.md +50 -0
- package/CHANGELOG.md +20 -0
- package/dist/chunk-FSASG3SB.mjs +94 -0
- package/dist/chunk-FSASG3SB.mjs.map +1 -0
- package/dist/config-F3FCBSPH.mjs +9 -0
- package/dist/config-F3FCBSPH.mjs.map +1 -0
- package/dist/index.js +1996 -209
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1878 -186
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/controllers/auth.ts +443 -0
- package/src/controllers/database-configs.ts +432 -0
- package/src/controllers/metrics-configs.ts +989 -0
- package/src/controllers/run.ts +6 -0
- package/src/controllers/tenants.ts +121 -0
- package/src/controllers/users.ts +135 -0
- package/src/controllers/workspace.ts +598 -0
- package/src/index.ts +2 -10
- package/src/routes/index.ts +21 -0
- package/src/services/agent_service.ts +71 -5
package/dist/index.js
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,6 +30,106 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
33
|
+
// src/config.ts
|
|
34
|
+
var config_exports = {};
|
|
35
|
+
__export(config_exports, {
|
|
36
|
+
config: () => config,
|
|
37
|
+
configService: () => configService
|
|
38
|
+
});
|
|
39
|
+
var ConfigService, configService, config;
|
|
40
|
+
var init_config = __esm({
|
|
41
|
+
"src/config.ts"() {
|
|
42
|
+
"use strict";
|
|
43
|
+
ConfigService = class {
|
|
44
|
+
constructor() {
|
|
45
|
+
this.config = this.loadFromEnv();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Load configuration from environment variables
|
|
49
|
+
*/
|
|
50
|
+
loadFromEnv() {
|
|
51
|
+
return {
|
|
52
|
+
port: process.env.PORT ? Number(process.env.PORT) : void 0,
|
|
53
|
+
queueServiceType: process.env.QUEUE_SERVICE_TYPE,
|
|
54
|
+
redisUrl: process.env.REDIS_URL,
|
|
55
|
+
redisPassword: process.env.REDIS_PASSWORD,
|
|
56
|
+
queueName: process.env.QUEUE_NAME
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Update configuration from JSON object
|
|
61
|
+
* This will update both the internal config and process.env
|
|
62
|
+
*/
|
|
63
|
+
updateConfig(jsonConfig) {
|
|
64
|
+
for (const [key, value] of Object.entries(jsonConfig)) {
|
|
65
|
+
if (value !== null && value !== void 0) {
|
|
66
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
67
|
+
for (const [nestedKey, nestedValue] of Object.entries(value)) {
|
|
68
|
+
const envKey = `${key.toUpperCase()}_${nestedKey.toUpperCase()}`;
|
|
69
|
+
process.env[envKey] = String(nestedValue);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
process.env[key.toUpperCase()] = String(value);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
this.config = this.loadFromEnv();
|
|
77
|
+
this.config = this.deepMerge(this.config, jsonConfig);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Deep merge two objects
|
|
81
|
+
*/
|
|
82
|
+
deepMerge(target, source) {
|
|
83
|
+
const output = { ...target };
|
|
84
|
+
if (this.isObject(target) && this.isObject(source)) {
|
|
85
|
+
Object.keys(source).forEach((key) => {
|
|
86
|
+
if (this.isObject(source[key])) {
|
|
87
|
+
if (!(key in target)) {
|
|
88
|
+
Object.assign(output, { [key]: source[key] });
|
|
89
|
+
} else {
|
|
90
|
+
output[key] = this.deepMerge(target[key], source[key]);
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
Object.assign(output, { [key]: source[key] });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return output;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check if value is a plain object
|
|
101
|
+
*/
|
|
102
|
+
isObject(item) {
|
|
103
|
+
return item && typeof item === "object" && !Array.isArray(item);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get current configuration
|
|
107
|
+
*/
|
|
108
|
+
getConfig() {
|
|
109
|
+
return { ...this.config };
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
configService = new ConfigService();
|
|
113
|
+
config = {
|
|
114
|
+
get port() {
|
|
115
|
+
return configService.getConfig().port;
|
|
116
|
+
},
|
|
117
|
+
get queueServiceType() {
|
|
118
|
+
return configService.getConfig().queueServiceType;
|
|
119
|
+
},
|
|
120
|
+
get redisUrl() {
|
|
121
|
+
return configService.getConfig().redisUrl;
|
|
122
|
+
},
|
|
123
|
+
get redisPassword() {
|
|
124
|
+
return configService.getConfig().redisPassword;
|
|
125
|
+
},
|
|
126
|
+
get queueName() {
|
|
127
|
+
return configService.getConfig().queueName;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
30
133
|
// src/index.ts
|
|
31
134
|
var index_exports = {};
|
|
32
135
|
__export(index_exports, {
|
|
@@ -44,6 +147,31 @@ var import_messages = require("@langchain/core/messages");
|
|
|
44
147
|
var import_langgraph = require("@langchain/langgraph");
|
|
45
148
|
var import_uuid = require("uuid");
|
|
46
149
|
var import_core = require("@axiom-lattice/core");
|
|
150
|
+
async function fetchDatabaseConfigs(baseURL, apiKey, tenantId) {
|
|
151
|
+
try {
|
|
152
|
+
const headers = {};
|
|
153
|
+
if (apiKey) {
|
|
154
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
155
|
+
}
|
|
156
|
+
if (tenantId) {
|
|
157
|
+
headers["x-tenant-id"] = tenantId;
|
|
158
|
+
}
|
|
159
|
+
const response = await fetch(`${baseURL}/api/database-configs`, { headers });
|
|
160
|
+
if (response.ok) {
|
|
161
|
+
const data = await response.json();
|
|
162
|
+
if (data.success && data.data && Array.isArray(data.data.records)) {
|
|
163
|
+
return data.data.records.map((record) => ({
|
|
164
|
+
key: record.key,
|
|
165
|
+
name: record.name,
|
|
166
|
+
description: record.description
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error("Failed to fetch database configs:", error);
|
|
172
|
+
}
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
47
175
|
function getOrCreateChunkBuffer() {
|
|
48
176
|
if (!(0, import_core.hasChunkBuffer)("default")) {
|
|
49
177
|
const buffer = new import_core.InMemoryChunkBuffer({
|
|
@@ -61,6 +189,8 @@ async function agent_invoke({
|
|
|
61
189
|
thread_id,
|
|
62
190
|
assistant_id,
|
|
63
191
|
tenant_id,
|
|
192
|
+
workspace_id,
|
|
193
|
+
project_id,
|
|
64
194
|
command,
|
|
65
195
|
run_id
|
|
66
196
|
}) {
|
|
@@ -72,10 +202,19 @@ async function agent_invoke({
|
|
|
72
202
|
if (!runnable_agent) {
|
|
73
203
|
throw new Error(`Agent ${assistant_id} not found`);
|
|
74
204
|
}
|
|
205
|
+
const { configService: configService2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
206
|
+
const gatewayConfig = configService2.getConfig();
|
|
207
|
+
const databaseConfigs = await fetchDatabaseConfigs(
|
|
208
|
+
gatewayConfig.baseURL || "http://localhost:4001",
|
|
209
|
+
void 0,
|
|
210
|
+
tenant_id
|
|
211
|
+
);
|
|
212
|
+
global.__DATABASE_CONFIGS__ = databaseConfigs;
|
|
75
213
|
const runConfig = {
|
|
76
214
|
...agentLattice?.config?.runConfig || {},
|
|
77
215
|
assistant_id,
|
|
78
|
-
|
|
216
|
+
workspaceId: workspace_id,
|
|
217
|
+
projectId: project_id
|
|
79
218
|
};
|
|
80
219
|
const result = await runnable_agent.invoke(
|
|
81
220
|
command ? new import_langgraph.Command(command) : { ...rest, messages, "x-tenant-id": tenant_id },
|
|
@@ -84,11 +223,12 @@ async function agent_invoke({
|
|
|
84
223
|
thread_id,
|
|
85
224
|
run_id: run_id || (0, import_uuid.v4)(),
|
|
86
225
|
"x-tenant-id": tenant_id,
|
|
226
|
+
"x-workspace-id": workspace_id,
|
|
227
|
+
"x-project-id": project_id,
|
|
87
228
|
"x-request-id": run_id,
|
|
88
229
|
"x-thread-id": thread_id,
|
|
89
230
|
"x-assistant-id": assistant_id,
|
|
90
231
|
runConfig
|
|
91
|
-
// Inject runConfig for tools to access
|
|
92
232
|
},
|
|
93
233
|
recursionLimit: 200
|
|
94
234
|
}
|
|
@@ -107,6 +247,8 @@ async function agent_stream({
|
|
|
107
247
|
thread_id,
|
|
108
248
|
command,
|
|
109
249
|
tenant_id,
|
|
250
|
+
workspace_id,
|
|
251
|
+
project_id,
|
|
110
252
|
assistant_id,
|
|
111
253
|
run_id
|
|
112
254
|
}) {
|
|
@@ -119,10 +261,19 @@ async function agent_stream({
|
|
|
119
261
|
messages = [humanMessage];
|
|
120
262
|
}
|
|
121
263
|
const chunkBuffer = getOrCreateChunkBuffer();
|
|
264
|
+
const { configService: configService2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
265
|
+
const gatewayConfig = configService2.getConfig();
|
|
266
|
+
const databaseConfigs = await fetchDatabaseConfigs(
|
|
267
|
+
gatewayConfig.baseURL || "http://localhost:4001",
|
|
268
|
+
void 0,
|
|
269
|
+
tenant_id
|
|
270
|
+
);
|
|
271
|
+
global.__DATABASE_CONFIGS__ = databaseConfigs;
|
|
122
272
|
const runConfig = {
|
|
123
273
|
...agentLattice?.config?.runConfig || {},
|
|
124
274
|
assistant_id,
|
|
125
|
-
|
|
275
|
+
workspaceId: workspace_id,
|
|
276
|
+
projectId: project_id
|
|
126
277
|
};
|
|
127
278
|
try {
|
|
128
279
|
if (!runnable_agent) {
|
|
@@ -139,6 +290,8 @@ async function agent_stream({
|
|
|
139
290
|
thread_id,
|
|
140
291
|
run_id: run_id || (0, import_uuid.v4)(),
|
|
141
292
|
"x-tenant-id": tenant_id,
|
|
293
|
+
"x-workspace-id": workspace_id,
|
|
294
|
+
"x-project-id": project_id,
|
|
142
295
|
"x-request-id": run_id,
|
|
143
296
|
"x-thread-id": thread_id,
|
|
144
297
|
"x-assistant-id": assistant_id,
|
|
@@ -272,12 +425,12 @@ async function resume_stream({
|
|
|
272
425
|
var import_core2 = require("@axiom-lattice/core");
|
|
273
426
|
var import_crypto = require("crypto");
|
|
274
427
|
var import_core3 = require("@axiom-lattice/core");
|
|
275
|
-
function convertAgentConfigToAssistant(
|
|
428
|
+
function convertAgentConfigToAssistant(config2) {
|
|
276
429
|
return {
|
|
277
|
-
id:
|
|
278
|
-
name:
|
|
279
|
-
description:
|
|
280
|
-
graphDefinition:
|
|
430
|
+
id: config2.key,
|
|
431
|
+
name: config2.name,
|
|
432
|
+
description: config2.description,
|
|
433
|
+
graphDefinition: config2,
|
|
281
434
|
// Store the full config as graphDefinition
|
|
282
435
|
createdAt: /* @__PURE__ */ new Date(0),
|
|
283
436
|
// Code-configured agents have no creation date
|
|
@@ -317,7 +470,7 @@ async function getAssistant(request, reply) {
|
|
|
317
470
|
let assistant = await assistantStore.getAssistantById(id);
|
|
318
471
|
if (!assistant) {
|
|
319
472
|
const agentConfigs = await (0, import_core3.getAllAgentConfigs)();
|
|
320
|
-
const agentConfig = agentConfigs.find((
|
|
473
|
+
const agentConfig = agentConfigs.find((config2) => config2.key === id);
|
|
321
474
|
if (agentConfig) {
|
|
322
475
|
assistant = convertAgentConfigToAssistant(agentConfig);
|
|
323
476
|
}
|
|
@@ -392,7 +545,7 @@ async function deleteAssistant(request, reply) {
|
|
|
392
545
|
const storeLattice = (0, import_core2.getStoreLattice)("default", "assistant");
|
|
393
546
|
const assistantStore = storeLattice.store;
|
|
394
547
|
const agentConfigs = await (0, import_core3.getAllAgentConfigs)();
|
|
395
|
-
const isCodeConfigured = agentConfigs.some((
|
|
548
|
+
const isCodeConfigured = agentConfigs.some((config2) => config2.key === id);
|
|
396
549
|
if (isCodeConfigured) {
|
|
397
550
|
const exists2 = await assistantStore.hasAssistant(id);
|
|
398
551
|
if (!exists2) {
|
|
@@ -456,6 +609,8 @@ var createRun = async (request, reply) => {
|
|
|
456
609
|
...input
|
|
457
610
|
} = request.body;
|
|
458
611
|
const tenant_id = request.headers["x-tenant-id"];
|
|
612
|
+
const workspace_id = request.headers["x-workspace-id"];
|
|
613
|
+
const project_id = request.headers["x-project-id"];
|
|
459
614
|
const x_request_id = request.headers["x-request-id"] || (0, import_uuid2.v4)();
|
|
460
615
|
if (!assistant_id) {
|
|
461
616
|
reply.status(400).send({
|
|
@@ -471,6 +626,8 @@ var createRun = async (request, reply) => {
|
|
|
471
626
|
thread_id,
|
|
472
627
|
command,
|
|
473
628
|
tenant_id,
|
|
629
|
+
workspace_id,
|
|
630
|
+
project_id,
|
|
474
631
|
run_id: x_request_id
|
|
475
632
|
});
|
|
476
633
|
reply.hijack();
|
|
@@ -498,6 +655,8 @@ var createRun = async (request, reply) => {
|
|
|
498
655
|
command,
|
|
499
656
|
thread_id,
|
|
500
657
|
tenant_id,
|
|
658
|
+
workspace_id,
|
|
659
|
+
project_id,
|
|
501
660
|
run_id: x_request_id
|
|
502
661
|
});
|
|
503
662
|
reply.status(200).send(result);
|
|
@@ -1044,77 +1203,8 @@ async function resumeScheduledTask(request, reply) {
|
|
|
1044
1203
|
}
|
|
1045
1204
|
}
|
|
1046
1205
|
|
|
1047
|
-
// src/config.ts
|
|
1048
|
-
|
|
1049
|
-
constructor() {
|
|
1050
|
-
this.config = this.loadFromEnv();
|
|
1051
|
-
}
|
|
1052
|
-
/**
|
|
1053
|
-
* Load configuration from environment variables
|
|
1054
|
-
*/
|
|
1055
|
-
loadFromEnv() {
|
|
1056
|
-
return {
|
|
1057
|
-
port: process.env.PORT ? Number(process.env.PORT) : void 0,
|
|
1058
|
-
queueServiceType: process.env.QUEUE_SERVICE_TYPE,
|
|
1059
|
-
redisUrl: process.env.REDIS_URL,
|
|
1060
|
-
redisPassword: process.env.REDIS_PASSWORD,
|
|
1061
|
-
queueName: process.env.QUEUE_NAME
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
|
-
/**
|
|
1065
|
-
* Update configuration from JSON object
|
|
1066
|
-
* This will update both the internal config and process.env
|
|
1067
|
-
*/
|
|
1068
|
-
updateConfig(jsonConfig) {
|
|
1069
|
-
for (const [key, value] of Object.entries(jsonConfig)) {
|
|
1070
|
-
if (value !== null && value !== void 0) {
|
|
1071
|
-
if (typeof value === "object" && !Array.isArray(value)) {
|
|
1072
|
-
for (const [nestedKey, nestedValue] of Object.entries(value)) {
|
|
1073
|
-
const envKey = `${key.toUpperCase()}_${nestedKey.toUpperCase()}`;
|
|
1074
|
-
process.env[envKey] = String(nestedValue);
|
|
1075
|
-
}
|
|
1076
|
-
} else {
|
|
1077
|
-
process.env[key.toUpperCase()] = String(value);
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
this.config = this.loadFromEnv();
|
|
1082
|
-
this.config = this.deepMerge(this.config, jsonConfig);
|
|
1083
|
-
}
|
|
1084
|
-
/**
|
|
1085
|
-
* Deep merge two objects
|
|
1086
|
-
*/
|
|
1087
|
-
deepMerge(target, source) {
|
|
1088
|
-
const output = { ...target };
|
|
1089
|
-
if (this.isObject(target) && this.isObject(source)) {
|
|
1090
|
-
Object.keys(source).forEach((key) => {
|
|
1091
|
-
if (this.isObject(source[key])) {
|
|
1092
|
-
if (!(key in target)) {
|
|
1093
|
-
Object.assign(output, { [key]: source[key] });
|
|
1094
|
-
} else {
|
|
1095
|
-
output[key] = this.deepMerge(target[key], source[key]);
|
|
1096
|
-
}
|
|
1097
|
-
} else {
|
|
1098
|
-
Object.assign(output, { [key]: source[key] });
|
|
1099
|
-
}
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
return output;
|
|
1103
|
-
}
|
|
1104
|
-
/**
|
|
1105
|
-
* Check if value is a plain object
|
|
1106
|
-
*/
|
|
1107
|
-
isObject(item) {
|
|
1108
|
-
return item && typeof item === "object" && !Array.isArray(item);
|
|
1109
|
-
}
|
|
1110
|
-
/**
|
|
1111
|
-
* Get current configuration
|
|
1112
|
-
*/
|
|
1113
|
-
getConfig() {
|
|
1114
|
-
return { ...this.config };
|
|
1115
|
-
}
|
|
1116
|
-
};
|
|
1117
|
-
var configService = new ConfigService();
|
|
1206
|
+
// src/controllers/config.ts
|
|
1207
|
+
init_config();
|
|
1118
1208
|
|
|
1119
1209
|
// src/services/queue_service.ts
|
|
1120
1210
|
var import_core7 = require("@axiom-lattice/core");
|
|
@@ -1126,7 +1216,7 @@ var setQueueServiceType = (type) => {
|
|
|
1126
1216
|
queueServiceType = type;
|
|
1127
1217
|
console.log(`Queue service type set to: ${type}`);
|
|
1128
1218
|
const queueName = process.env.QUEUE_NAME || "tasks";
|
|
1129
|
-
const
|
|
1219
|
+
const config2 = {
|
|
1130
1220
|
name: "Default Queue Service",
|
|
1131
1221
|
description: `Default ${type} queue service`,
|
|
1132
1222
|
type: type === "redis" ? import_protocols.QueueType.REDIS : import_protocols.QueueType.MEMORY,
|
|
@@ -1146,7 +1236,7 @@ var setQueueServiceType = (type) => {
|
|
|
1146
1236
|
redisPassword: process.env.REDIS_PASSWORD
|
|
1147
1237
|
});
|
|
1148
1238
|
}
|
|
1149
|
-
(0, import_core7.registerQueueLattice)(DEFAULT_QUEUE_KEY,
|
|
1239
|
+
(0, import_core7.registerQueueLattice)(DEFAULT_QUEUE_KEY, config2, client);
|
|
1150
1240
|
};
|
|
1151
1241
|
var getQueueService = () => {
|
|
1152
1242
|
if (!import_core7.queueLatticeManager.hasLattice(DEFAULT_QUEUE_KEY)) {
|
|
@@ -1238,18 +1328,18 @@ async function getModels(request, reply) {
|
|
|
1238
1328
|
try {
|
|
1239
1329
|
const allLattices = import_core8.modelLatticeManager.getAllLattices();
|
|
1240
1330
|
const models = allLattices.map((lattice) => {
|
|
1241
|
-
const
|
|
1331
|
+
const config2 = lattice.client.config || {};
|
|
1242
1332
|
return {
|
|
1243
1333
|
key: lattice.key,
|
|
1244
|
-
model:
|
|
1245
|
-
provider:
|
|
1246
|
-
streaming:
|
|
1247
|
-
apiKey:
|
|
1248
|
-
baseURL:
|
|
1249
|
-
maxTokens:
|
|
1250
|
-
temperature:
|
|
1251
|
-
timeout:
|
|
1252
|
-
maxRetries:
|
|
1334
|
+
model: config2.model || "",
|
|
1335
|
+
provider: config2.provider || "openai",
|
|
1336
|
+
streaming: config2.streaming || false,
|
|
1337
|
+
apiKey: config2.apiKey || "",
|
|
1338
|
+
baseURL: config2.baseURL || "",
|
|
1339
|
+
maxTokens: config2.maxTokens,
|
|
1340
|
+
temperature: config2.temperature,
|
|
1341
|
+
timeout: config2.timeout,
|
|
1342
|
+
maxRetries: config2.maxRetries
|
|
1253
1343
|
};
|
|
1254
1344
|
});
|
|
1255
1345
|
return reply.send({
|
|
@@ -1709,15 +1799,15 @@ async function getToolConfigs(request, reply) {
|
|
|
1709
1799
|
try {
|
|
1710
1800
|
const allLattices = import_core11.toolLatticeManager.getAllLattices();
|
|
1711
1801
|
const toolConfigs = allLattices.map((lattice) => {
|
|
1712
|
-
const
|
|
1713
|
-
const serializedSchema =
|
|
1802
|
+
const config2 = { ...lattice.config };
|
|
1803
|
+
const serializedSchema = config2.schema ? serializeSchema(config2.schema) : void 0;
|
|
1714
1804
|
return {
|
|
1715
1805
|
id: lattice.key,
|
|
1716
|
-
name:
|
|
1717
|
-
description:
|
|
1806
|
+
name: config2.name,
|
|
1807
|
+
description: config2.description,
|
|
1718
1808
|
schema: serializedSchema,
|
|
1719
|
-
returnDirect:
|
|
1720
|
-
needUserApprove:
|
|
1809
|
+
returnDirect: config2.returnDirect,
|
|
1810
|
+
needUserApprove: config2.needUserApprove
|
|
1721
1811
|
};
|
|
1722
1812
|
});
|
|
1723
1813
|
return reply.send({
|
|
@@ -2205,8 +2295,8 @@ var sandboxService = new SandboxService();
|
|
|
2205
2295
|
|
|
2206
2296
|
// src/controllers/sandbox.ts
|
|
2207
2297
|
var import_core13 = require("@axiom-lattice/core");
|
|
2208
|
-
function getFilenameFromPath(
|
|
2209
|
-
const segments =
|
|
2298
|
+
function getFilenameFromPath(path2) {
|
|
2299
|
+
const segments = path2.replace(/\/+$/, "").split("/");
|
|
2210
2300
|
return segments[segments.length - 1] || "download";
|
|
2211
2301
|
}
|
|
2212
2302
|
var EXT_TO_MIME = {
|
|
@@ -2257,10 +2347,10 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
2257
2347
|
const pathValue = pathEntry && typeof pathEntry === "object" && "value" in pathEntry ? String(pathEntry.value) : typeof pathEntry === "string" ? pathEntry : void 0;
|
|
2258
2348
|
const formData = new FormData();
|
|
2259
2349
|
formData.append("file", new Blob([buffer]), data.filename ?? "file");
|
|
2260
|
-
const
|
|
2350
|
+
const path2 = `/home/gem/uploads/${pathValue ? pathValue : ""}${data.filename}`;
|
|
2261
2351
|
const uploadResult = await sandbox.file.uploadFile({
|
|
2262
2352
|
file: buffer,
|
|
2263
|
-
path
|
|
2353
|
+
path: path2
|
|
2264
2354
|
});
|
|
2265
2355
|
if (!uploadResult.ok) {
|
|
2266
2356
|
return reply.status(502).send({ error: `Upload error: ${uploadResult.error}` });
|
|
@@ -2343,105 +2433,1801 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
2343
2433
|
);
|
|
2344
2434
|
}
|
|
2345
2435
|
|
|
2346
|
-
// src/
|
|
2347
|
-
var
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
"
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
)
|
|
2436
|
+
// src/controllers/workspace.ts
|
|
2437
|
+
var fs = __toESM(require("fs/promises"));
|
|
2438
|
+
var path = __toESM(require("path"));
|
|
2439
|
+
var import_stream2 = require("stream");
|
|
2440
|
+
var import_core14 = require("@axiom-lattice/core");
|
|
2441
|
+
var import_core15 = require("@axiom-lattice/core");
|
|
2442
|
+
var import_core16 = require("@axiom-lattice/core");
|
|
2443
|
+
var import_uuid3 = require("uuid");
|
|
2444
|
+
var WorkspaceController = class {
|
|
2445
|
+
constructor() {
|
|
2446
|
+
this.workspaceStore = (0, import_core14.getStoreLattice)("default", "workspace").store;
|
|
2447
|
+
this.projectStore = (0, import_core14.getStoreLattice)("default", "project").store;
|
|
2448
|
+
}
|
|
2449
|
+
getTenantId(request) {
|
|
2450
|
+
return request.headers["x-tenant-id"] || "default";
|
|
2451
|
+
}
|
|
2452
|
+
// ==================== Workspace CRUD ====================
|
|
2453
|
+
async listWorkspaces(request, reply) {
|
|
2454
|
+
const tenantId = this.getTenantId(request);
|
|
2455
|
+
const workspaces = await this.workspaceStore.getAllWorkspaces(tenantId);
|
|
2456
|
+
return { success: true, data: workspaces };
|
|
2457
|
+
}
|
|
2458
|
+
async createWorkspace(request, reply) {
|
|
2459
|
+
const tenantId = this.getTenantId(request);
|
|
2460
|
+
const data = request.body;
|
|
2461
|
+
const id = (0, import_uuid3.v4)();
|
|
2462
|
+
const workspace = await this.workspaceStore.createWorkspace(
|
|
2463
|
+
tenantId,
|
|
2464
|
+
id,
|
|
2465
|
+
data
|
|
2466
|
+
);
|
|
2467
|
+
return reply.status(201).send({ success: true, data: workspace });
|
|
2468
|
+
}
|
|
2469
|
+
async getWorkspace(request, reply) {
|
|
2470
|
+
const tenantId = this.getTenantId(request);
|
|
2471
|
+
const { workspaceId } = request.params;
|
|
2472
|
+
const workspace = await this.workspaceStore.getWorkspaceById(
|
|
2473
|
+
tenantId,
|
|
2474
|
+
workspaceId
|
|
2475
|
+
);
|
|
2476
|
+
if (!workspace) {
|
|
2477
|
+
return reply.status(404).send({ success: false, error: "Workspace not found" });
|
|
2478
|
+
}
|
|
2479
|
+
return { success: true, data: workspace };
|
|
2480
|
+
}
|
|
2481
|
+
async updateWorkspace(request, reply) {
|
|
2482
|
+
const tenantId = this.getTenantId(request);
|
|
2483
|
+
const { workspaceId } = request.params;
|
|
2484
|
+
const updates = request.body;
|
|
2485
|
+
const workspace = await this.workspaceStore.updateWorkspace(
|
|
2486
|
+
tenantId,
|
|
2487
|
+
workspaceId,
|
|
2488
|
+
updates
|
|
2489
|
+
);
|
|
2490
|
+
if (!workspace) {
|
|
2491
|
+
return reply.status(404).send({ success: false, error: "Workspace not found" });
|
|
2492
|
+
}
|
|
2493
|
+
return { success: true, data: workspace };
|
|
2494
|
+
}
|
|
2495
|
+
async deleteWorkspace(request, reply) {
|
|
2496
|
+
const tenantId = this.getTenantId(request);
|
|
2497
|
+
const { workspaceId } = request.params;
|
|
2498
|
+
const deleted = await this.workspaceStore.deleteWorkspace(
|
|
2499
|
+
tenantId,
|
|
2500
|
+
workspaceId
|
|
2501
|
+
);
|
|
2502
|
+
if (!deleted) {
|
|
2503
|
+
return reply.status(404).send({ success: false, error: "Workspace not found" });
|
|
2504
|
+
}
|
|
2505
|
+
return { success: true };
|
|
2506
|
+
}
|
|
2507
|
+
// ==================== Project CRUD ====================
|
|
2508
|
+
async listProjects(request, reply) {
|
|
2509
|
+
const tenantId = this.getTenantId(request);
|
|
2510
|
+
const { workspaceId } = request.params;
|
|
2511
|
+
const projects = await this.projectStore.getProjectsByWorkspace(
|
|
2512
|
+
tenantId,
|
|
2513
|
+
workspaceId
|
|
2514
|
+
);
|
|
2515
|
+
return { success: true, data: projects };
|
|
2516
|
+
}
|
|
2517
|
+
async createProject(request, reply) {
|
|
2518
|
+
const tenantId = this.getTenantId(request);
|
|
2519
|
+
const { workspaceId } = request.params;
|
|
2520
|
+
const data = request.body;
|
|
2521
|
+
const id = (0, import_uuid3.v4)();
|
|
2522
|
+
const project = await this.projectStore.createProject(
|
|
2523
|
+
tenantId,
|
|
2524
|
+
workspaceId,
|
|
2525
|
+
id,
|
|
2526
|
+
data
|
|
2527
|
+
);
|
|
2528
|
+
return reply.status(201).send({ success: true, data: project });
|
|
2529
|
+
}
|
|
2530
|
+
async getProject(request, reply) {
|
|
2531
|
+
const tenantId = this.getTenantId(request);
|
|
2532
|
+
const { projectId } = request.params;
|
|
2533
|
+
const project = await this.projectStore.getProjectById(tenantId, projectId);
|
|
2534
|
+
if (!project) {
|
|
2535
|
+
return reply.status(404).send({ success: false, error: "Project not found" });
|
|
2536
|
+
}
|
|
2537
|
+
return { success: true, data: project };
|
|
2538
|
+
}
|
|
2539
|
+
async updateProject(request, reply) {
|
|
2540
|
+
const tenantId = this.getTenantId(request);
|
|
2541
|
+
const { projectId } = request.params;
|
|
2542
|
+
const updates = request.body;
|
|
2543
|
+
const project = await this.projectStore.updateProject(
|
|
2544
|
+
tenantId,
|
|
2545
|
+
projectId,
|
|
2546
|
+
updates
|
|
2547
|
+
);
|
|
2548
|
+
if (!project) {
|
|
2549
|
+
return reply.status(404).send({ success: false, error: "Project not found" });
|
|
2550
|
+
}
|
|
2551
|
+
return { success: true, data: project };
|
|
2552
|
+
}
|
|
2553
|
+
async deleteProject(request, reply) {
|
|
2554
|
+
const tenantId = this.getTenantId(request);
|
|
2555
|
+
const { projectId } = request.params;
|
|
2556
|
+
const deleted = await this.projectStore.deleteProject(tenantId, projectId);
|
|
2557
|
+
if (!deleted) {
|
|
2558
|
+
return reply.status(404).send({ success: false, error: "Project not found" });
|
|
2559
|
+
}
|
|
2560
|
+
return { success: true };
|
|
2561
|
+
}
|
|
2562
|
+
// ==================== File Operations ====================
|
|
2563
|
+
async getBackend(workspaceId, projectId) {
|
|
2564
|
+
const tenantId = "default";
|
|
2565
|
+
const workspace = await this.workspaceStore.getWorkspaceById(
|
|
2566
|
+
tenantId,
|
|
2567
|
+
workspaceId
|
|
2568
|
+
);
|
|
2569
|
+
if (!workspace) {
|
|
2570
|
+
throw new Error("Workspace not found");
|
|
2571
|
+
}
|
|
2572
|
+
if (workspace.storageType === "sandbox") {
|
|
2573
|
+
const sandboxManager = (0, import_core16.getSandBoxManager)("default");
|
|
2574
|
+
const sandboxName = "global";
|
|
2575
|
+
const sandbox = await sandboxManager.createSandbox(sandboxName);
|
|
2576
|
+
return { backend: new import_core15.SandboxFilesystem({
|
|
2577
|
+
sandboxInstance: sandbox,
|
|
2578
|
+
workingDirectory: `/workspaces/${workspaceId}/${projectId}`
|
|
2579
|
+
}), workspace };
|
|
2580
|
+
} else {
|
|
2581
|
+
return { backend: new import_core15.FilesystemBackend({
|
|
2582
|
+
rootDir: `/lattice_store/workspaces/${workspaceId}/${projectId}`,
|
|
2583
|
+
virtualMode: true
|
|
2584
|
+
}), workspace };
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
getFilenameFromPath(filePath) {
|
|
2588
|
+
const segments = filePath.split("/");
|
|
2589
|
+
return segments[segments.length - 1] || "download";
|
|
2590
|
+
}
|
|
2591
|
+
getMimeType(filename) {
|
|
2592
|
+
const ext = filename.split(".").pop()?.toLowerCase() || "";
|
|
2593
|
+
const mimeTypes = {
|
|
2594
|
+
pdf: "application/pdf",
|
|
2595
|
+
csv: "text/csv",
|
|
2596
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
2597
|
+
xls: "application/vnd.ms-excel",
|
|
2598
|
+
txt: "text/plain",
|
|
2599
|
+
json: "application/json",
|
|
2600
|
+
png: "image/png",
|
|
2601
|
+
jpg: "image/jpeg",
|
|
2602
|
+
jpeg: "image/jpeg",
|
|
2603
|
+
gif: "image/gif",
|
|
2604
|
+
svg: "image/svg+xml",
|
|
2605
|
+
html: "text/html",
|
|
2606
|
+
htm: "text/html",
|
|
2607
|
+
mp3: "audio/mpeg",
|
|
2608
|
+
mp4: "video/mp4",
|
|
2609
|
+
webm: "video/webm"
|
|
2610
|
+
};
|
|
2611
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
2612
|
+
}
|
|
2613
|
+
async downloadFile(request, reply) {
|
|
2614
|
+
const { workspaceId, projectId } = request.params;
|
|
2615
|
+
const filePath = request.query.path;
|
|
2616
|
+
if (!filePath) {
|
|
2617
|
+
return reply.status(400).send({ success: false, error: "Path is required" });
|
|
2618
|
+
}
|
|
2619
|
+
try {
|
|
2620
|
+
const { workspace } = await this.getBackend(workspaceId, projectId);
|
|
2621
|
+
const resolvedPath = filePath.startsWith("/") ? filePath : `/${filePath}`;
|
|
2622
|
+
if (workspace.storageType === "sandbox") {
|
|
2623
|
+
const sandboxManager = (0, import_core16.getSandBoxManager)("default");
|
|
2624
|
+
const sandbox = await sandboxManager.createSandbox("global");
|
|
2625
|
+
const realPath = path.join("/home/gem/workspaces", workspaceId, projectId, resolvedPath);
|
|
2626
|
+
const filename2 = this.getFilenameFromPath(resolvedPath);
|
|
2627
|
+
const inferredContentType = this.getMimeType(filename2);
|
|
2628
|
+
const downloadResult = await sandbox.file.downloadFile({
|
|
2629
|
+
path: realPath
|
|
2630
|
+
});
|
|
2631
|
+
if (!downloadResult.ok) {
|
|
2632
|
+
return reply.status(502).send({
|
|
2633
|
+
success: false,
|
|
2634
|
+
error: `Download error: ${JSON.stringify(downloadResult.error)}`
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
const body = downloadResult.body;
|
|
2638
|
+
if (typeof body?.stream === "function") {
|
|
2639
|
+
const webStream = body.stream();
|
|
2640
|
+
const nodeStream = import_stream2.Readable.fromWeb(webStream);
|
|
2641
|
+
const contentType2 = body.contentType ?? inferredContentType;
|
|
2642
|
+
const contentDisposition2 = body.contentDisposition ?? `inline; filename*=UTF-8''${encodeURIComponent(filename2)}`;
|
|
2643
|
+
return reply.status(200).type(contentType2).header("Content-Disposition", contentDisposition2).send(nodeStream);
|
|
2644
|
+
}
|
|
2645
|
+
const bodyUnknown = downloadResult.body;
|
|
2646
|
+
let buf;
|
|
2647
|
+
let contentType = inferredContentType;
|
|
2648
|
+
let contentDisposition = `inline; filename*=UTF-8''${encodeURIComponent(filename2)}`;
|
|
2649
|
+
if (bodyUnknown instanceof ArrayBuffer) {
|
|
2650
|
+
buf = Buffer.from(bodyUnknown);
|
|
2651
|
+
} else if (bodyUnknown instanceof Buffer) {
|
|
2652
|
+
buf = bodyUnknown;
|
|
2653
|
+
} else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
|
|
2654
|
+
const res = bodyUnknown;
|
|
2655
|
+
buf = Buffer.from(await res.arrayBuffer());
|
|
2656
|
+
if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
|
|
2657
|
+
if (res.headers?.get("content-disposition")) contentDisposition = res.headers.get("content-disposition");
|
|
2658
|
+
} else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
|
|
2659
|
+
const blob = await bodyUnknown.blob();
|
|
2660
|
+
buf = Buffer.from(await blob.arrayBuffer());
|
|
2661
|
+
} else {
|
|
2662
|
+
return reply.status(502).send({ success: false, error: "Unexpected download response format" });
|
|
2663
|
+
}
|
|
2664
|
+
return reply.status(200).type(contentType).header("Content-Disposition", contentDisposition).send(buf);
|
|
2665
|
+
}
|
|
2666
|
+
const { backend } = await this.getBackend(workspaceId, projectId);
|
|
2667
|
+
const content = await backend.read(resolvedPath, 0, Infinity);
|
|
2668
|
+
const filename = this.getFilenameFromPath(resolvedPath);
|
|
2669
|
+
const mimeType = this.getMimeType(filename);
|
|
2670
|
+
const buffer = Buffer.from(content, "utf-8");
|
|
2671
|
+
return reply.status(200).type(mimeType).header("Content-Disposition", `inline; filename*=UTF-8''${encodeURIComponent(filename)}`).send(buffer);
|
|
2672
|
+
} catch (error) {
|
|
2673
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2674
|
+
return reply.status(502).send({ success: false, error: `Download proxy error: ${message}` });
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
async listPath(request) {
|
|
2678
|
+
const { workspaceId, projectId } = request.params;
|
|
2679
|
+
const path2 = request.query.path || "/";
|
|
2680
|
+
const { backend } = await this.getBackend(workspaceId, projectId);
|
|
2681
|
+
const files = await backend.lsInfo(path2);
|
|
2682
|
+
return { success: true, data: files };
|
|
2683
|
+
}
|
|
2684
|
+
async readFile(request) {
|
|
2685
|
+
const { workspaceId, projectId } = request.params;
|
|
2686
|
+
const { path: path2, offset = 0, limit = 1e3 } = request.query;
|
|
2687
|
+
const { backend } = await this.getBackend(workspaceId, projectId);
|
|
2688
|
+
const content = await backend.read(path2, Number(offset), Number(limit));
|
|
2689
|
+
return { success: true, data: { content, offset, limit } };
|
|
2690
|
+
}
|
|
2691
|
+
/**
|
|
2692
|
+
* Upload a file to the workspace project storage.
|
|
2693
|
+
*
|
|
2694
|
+
* Route: POST /api/workspaces/:workspaceId/projects/:projectId/uploadfile
|
|
2695
|
+
* Accepts multipart/form-data with:
|
|
2696
|
+
* - `file`: the file to upload (required)
|
|
2697
|
+
* - `path`: optional directory path relative to project root (e.g., "docs", "src/utils")
|
|
2698
|
+
* For sandbox storage, delegates to the sandbox upload API.
|
|
2699
|
+
* For filesystem storage, writes directly under /lattice_store/workspaces/{workspaceId}/{projectId}.
|
|
2700
|
+
*/
|
|
2701
|
+
async uploadFile(request, reply) {
|
|
2702
|
+
const tenantId = this.getTenantId(request);
|
|
2703
|
+
const { workspaceId, projectId } = request.params;
|
|
2704
|
+
const workspace = await this.workspaceStore.getWorkspaceById(
|
|
2705
|
+
tenantId,
|
|
2706
|
+
workspaceId
|
|
2707
|
+
);
|
|
2708
|
+
if (!workspace) {
|
|
2709
|
+
return reply.status(404).send({ success: false, error: "Workspace not found" });
|
|
2710
|
+
}
|
|
2711
|
+
try {
|
|
2712
|
+
const data = await request.file();
|
|
2713
|
+
if (!data) {
|
|
2714
|
+
return reply.status(400).send({ success: false, error: "No file in request" });
|
|
2715
|
+
}
|
|
2716
|
+
const buffer = await data.toBuffer();
|
|
2717
|
+
const filename = data.filename || "file";
|
|
2718
|
+
const pathEntry = data.fields?.path;
|
|
2719
|
+
const pathValue = pathEntry && typeof pathEntry === "object" && "value" in pathEntry ? String(pathEntry.value) : typeof pathEntry === "string" ? pathEntry : void 0;
|
|
2720
|
+
if (pathValue && !/^[a-zA-Z0-9_./-]+$/.test(pathValue)) {
|
|
2721
|
+
return reply.status(400).send({ success: false, error: "Invalid path parameter" });
|
|
2722
|
+
}
|
|
2723
|
+
if (workspace.storageType === "sandbox") {
|
|
2724
|
+
const sandboxManager = (0, import_core16.getSandBoxManager)("default");
|
|
2725
|
+
const sandboxName = "global";
|
|
2726
|
+
const sandbox = await sandboxManager.createSandbox(sandboxName);
|
|
2727
|
+
const baseDir = path.join("/home/gem/workspaces", workspaceId, projectId);
|
|
2728
|
+
const realPath = pathValue ? path.join(baseDir, pathValue, filename) : path.join(baseDir, filename);
|
|
2729
|
+
const uploadResult = await sandbox.file.uploadFile({
|
|
2730
|
+
file: buffer,
|
|
2731
|
+
path: realPath
|
|
2732
|
+
}, { timeoutInSeconds: 300 });
|
|
2733
|
+
if (!uploadResult.ok) {
|
|
2734
|
+
return reply.status(502).send({
|
|
2735
|
+
success: false,
|
|
2736
|
+
error: `Upload error: ${JSON.stringify(uploadResult.error)}`
|
|
2737
|
+
});
|
|
2738
|
+
}
|
|
2739
|
+
const relativePath = uploadResult.body?.data?.file_path?.replace(path.join("/home/gem/workspaces", workspaceId, projectId), "") || (pathValue ? `/${pathValue}/${filename}` : `/${filename}`);
|
|
2740
|
+
const result2 = {
|
|
2741
|
+
path: relativePath,
|
|
2742
|
+
name: filename,
|
|
2743
|
+
is_dir: false,
|
|
2744
|
+
size: buffer.length,
|
|
2745
|
+
modified_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2746
|
+
};
|
|
2747
|
+
return reply.status(200).send({ success: true, data: result2 });
|
|
2748
|
+
}
|
|
2749
|
+
const rootDir = `/lattice_store/workspaces/${workspaceId}/${projectId}`;
|
|
2750
|
+
const targetDir = pathValue ? path.join(rootDir, pathValue) : rootDir;
|
|
2751
|
+
const targetPath = path.join(targetDir, filename);
|
|
2752
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
2753
|
+
await fs.writeFile(targetPath, buffer);
|
|
2754
|
+
const stat2 = await fs.stat(targetPath);
|
|
2755
|
+
const result = {
|
|
2756
|
+
path: pathValue ? `/${pathValue}/${filename}` : `/${filename}`,
|
|
2757
|
+
name: filename,
|
|
2758
|
+
is_dir: false,
|
|
2759
|
+
size: stat2.size,
|
|
2760
|
+
modified_at: stat2.mtime.toISOString()
|
|
2761
|
+
};
|
|
2762
|
+
return reply.status(200).send({ success: true, data: result });
|
|
2763
|
+
} catch (error) {
|
|
2764
|
+
return reply.status(500).send({
|
|
2765
|
+
success: false,
|
|
2766
|
+
error: `Upload handler error: ${error?.message || String(error)}`
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
};
|
|
2771
|
+
function registerWorkspaceRoutes(app2) {
|
|
2772
|
+
const controller = new WorkspaceController();
|
|
2773
|
+
app2.get("/api/workspaces", controller.listWorkspaces.bind(controller));
|
|
2774
|
+
app2.post("/api/workspaces", controller.createWorkspace.bind(controller));
|
|
2360
2775
|
app2.get(
|
|
2361
|
-
"/api/
|
|
2362
|
-
|
|
2363
|
-
getMemoryItem
|
|
2364
|
-
);
|
|
2365
|
-
app2.put(
|
|
2366
|
-
"/api/assistants/:assistantId/memory/:key",
|
|
2367
|
-
{ schema: setMemoryItemSchema },
|
|
2368
|
-
setMemoryItem
|
|
2776
|
+
"/api/workspaces/:workspaceId",
|
|
2777
|
+
controller.getWorkspace.bind(controller)
|
|
2369
2778
|
);
|
|
2370
|
-
app2.
|
|
2371
|
-
"/api/
|
|
2372
|
-
|
|
2373
|
-
deleteMemoryItem
|
|
2779
|
+
app2.patch(
|
|
2780
|
+
"/api/workspaces/:workspaceId",
|
|
2781
|
+
controller.updateWorkspace.bind(controller)
|
|
2374
2782
|
);
|
|
2375
2783
|
app2.delete(
|
|
2376
|
-
"/api/
|
|
2377
|
-
|
|
2378
|
-
clearMemory
|
|
2784
|
+
"/api/workspaces/:workspaceId",
|
|
2785
|
+
controller.deleteWorkspace.bind(controller)
|
|
2379
2786
|
);
|
|
2380
|
-
app2.get("/api/assistants", getAssistantList);
|
|
2381
|
-
app2.get("/api/assistants/:id", getAssistant);
|
|
2382
|
-
app2.post("/api/assistants", createAssistant);
|
|
2383
|
-
app2.put("/api/assistants/:id", updateAssistant);
|
|
2384
|
-
app2.delete("/api/assistants/:id", deleteAssistant);
|
|
2385
2787
|
app2.get(
|
|
2386
|
-
"/api/
|
|
2387
|
-
|
|
2388
|
-
getAgentGraph
|
|
2788
|
+
"/api/workspaces/:workspaceId/projects",
|
|
2789
|
+
controller.listProjects.bind(controller)
|
|
2389
2790
|
);
|
|
2390
2791
|
app2.post(
|
|
2391
|
-
"/api/
|
|
2392
|
-
|
|
2393
|
-
triggerAgentTask
|
|
2792
|
+
"/api/workspaces/:workspaceId/projects",
|
|
2793
|
+
controller.createProject.bind(controller)
|
|
2394
2794
|
);
|
|
2395
|
-
app2.get("/api/assistants/:assistantId/threads", getThreadList);
|
|
2396
2795
|
app2.get(
|
|
2397
|
-
"/api/
|
|
2398
|
-
|
|
2796
|
+
"/api/workspaces/:workspaceId/projects/:projectId",
|
|
2797
|
+
controller.getProject.bind(controller)
|
|
2399
2798
|
);
|
|
2400
|
-
app2.
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
updateThread
|
|
2799
|
+
app2.patch(
|
|
2800
|
+
"/api/workspaces/:workspaceId/projects/:projectId",
|
|
2801
|
+
controller.updateProject.bind(controller)
|
|
2404
2802
|
);
|
|
2405
2803
|
app2.delete(
|
|
2406
|
-
"/api/
|
|
2407
|
-
|
|
2804
|
+
"/api/workspaces/:workspaceId/projects/:projectId",
|
|
2805
|
+
controller.deleteProject.bind(controller)
|
|
2408
2806
|
);
|
|
2409
2807
|
app2.get(
|
|
2410
|
-
"/api/
|
|
2411
|
-
|
|
2412
|
-
getConfig
|
|
2413
|
-
);
|
|
2414
|
-
app2.put(
|
|
2415
|
-
"/api/config",
|
|
2416
|
-
{ schema: updateConfigSchema },
|
|
2417
|
-
updateConfig
|
|
2808
|
+
"/api/workspaces/:workspaceId/projects/:projectId/listpath",
|
|
2809
|
+
controller.listPath.bind(controller)
|
|
2418
2810
|
);
|
|
2419
|
-
app2.get("/api/models", getModels);
|
|
2420
|
-
app2.put("/api/models", updateModels);
|
|
2421
|
-
app2.get("/api/tools", getToolConfigs);
|
|
2422
2811
|
app2.get(
|
|
2423
|
-
"/
|
|
2424
|
-
|
|
2425
|
-
getHealth
|
|
2812
|
+
"/api/workspaces/:workspaceId/projects/:projectId/readfile",
|
|
2813
|
+
controller.readFile.bind(controller)
|
|
2426
2814
|
);
|
|
2427
|
-
app2.
|
|
2428
|
-
"/api/
|
|
2429
|
-
|
|
2815
|
+
app2.post(
|
|
2816
|
+
"/api/workspaces/:workspaceId/projects/:projectId/uploadfile",
|
|
2817
|
+
controller.uploadFile.bind(controller)
|
|
2430
2818
|
);
|
|
2431
|
-
app2.get("/api/schedules/:taskId", getScheduledTask);
|
|
2432
|
-
app2.post("/api/schedules/:taskId/cancel", cancelScheduledTask);
|
|
2433
|
-
app2.post("/api/schedules/:taskId/pause", pauseScheduledTask);
|
|
2434
|
-
app2.post("/api/schedules/:taskId/resume", resumeScheduledTask);
|
|
2435
|
-
app2.get("/api/skills", getSkillList);
|
|
2436
2819
|
app2.get(
|
|
2437
|
-
"/api/
|
|
2438
|
-
|
|
2439
|
-
);
|
|
2440
|
-
app2.post(
|
|
2441
|
-
"/api/skills",
|
|
2442
|
-
createSkill
|
|
2820
|
+
"/api/workspaces/:workspaceId/projects/:projectId/downloadfile",
|
|
2821
|
+
controller.downloadFile.bind(controller)
|
|
2443
2822
|
);
|
|
2444
|
-
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
// src/controllers/database-configs.ts
|
|
2826
|
+
var import_core17 = require("@axiom-lattice/core");
|
|
2827
|
+
var import_crypto3 = require("crypto");
|
|
2828
|
+
function getTenantId(request) {
|
|
2829
|
+
return request.headers["x-tenant-id"] || "default";
|
|
2830
|
+
}
|
|
2831
|
+
async function getDatabaseConfigList(request, reply) {
|
|
2832
|
+
const tenantId = getTenantId(request);
|
|
2833
|
+
try {
|
|
2834
|
+
const storeLattice = (0, import_core17.getStoreLattice)("default", "database");
|
|
2835
|
+
const store = storeLattice.store;
|
|
2836
|
+
const configs = await store.getAllConfigs(tenantId);
|
|
2837
|
+
console.log("Backend: getAllConfigs returned:", configs);
|
|
2838
|
+
if (configs.length > 0) {
|
|
2839
|
+
console.log("Backend: First config key:", configs[0].key);
|
|
2840
|
+
}
|
|
2841
|
+
return {
|
|
2842
|
+
success: true,
|
|
2843
|
+
message: "Database configurations retrieved successfully",
|
|
2844
|
+
data: {
|
|
2845
|
+
records: configs,
|
|
2846
|
+
total: configs.length
|
|
2847
|
+
}
|
|
2848
|
+
};
|
|
2849
|
+
} catch (error) {
|
|
2850
|
+
console.error("Failed to get database configs:", error);
|
|
2851
|
+
return {
|
|
2852
|
+
success: false,
|
|
2853
|
+
message: "Failed to retrieve database configurations",
|
|
2854
|
+
data: {
|
|
2855
|
+
records: [],
|
|
2856
|
+
total: 0
|
|
2857
|
+
}
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
async function getDatabaseConfig(request, reply) {
|
|
2862
|
+
const tenantId = getTenantId(request);
|
|
2863
|
+
const { key } = request.params;
|
|
2864
|
+
try {
|
|
2865
|
+
const storeLattice = (0, import_core17.getStoreLattice)("default", "database");
|
|
2866
|
+
const store = storeLattice.store;
|
|
2867
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
2868
|
+
if (!config2) {
|
|
2869
|
+
return {
|
|
2870
|
+
success: false,
|
|
2871
|
+
message: "Database configuration not found"
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
return {
|
|
2875
|
+
success: true,
|
|
2876
|
+
message: "Database configuration retrieved successfully",
|
|
2877
|
+
data: config2
|
|
2878
|
+
};
|
|
2879
|
+
} catch (error) {
|
|
2880
|
+
console.error("Failed to get database config:", error);
|
|
2881
|
+
return {
|
|
2882
|
+
success: false,
|
|
2883
|
+
message: "Failed to retrieve database configuration"
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
async function createDatabaseConfig(request, reply) {
|
|
2888
|
+
const tenantId = getTenantId(request);
|
|
2889
|
+
const body = request.body;
|
|
2890
|
+
try {
|
|
2891
|
+
const storeLattice = (0, import_core17.getStoreLattice)("default", "database");
|
|
2892
|
+
const store = storeLattice.store;
|
|
2893
|
+
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
2894
|
+
if (existing) {
|
|
2895
|
+
reply.code(409);
|
|
2896
|
+
return {
|
|
2897
|
+
success: false,
|
|
2898
|
+
message: "Database configuration with this key already exists"
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2901
|
+
const id = body.id || (0, import_crypto3.randomUUID)();
|
|
2902
|
+
const config2 = await store.createConfig(tenantId, id, body);
|
|
2903
|
+
try {
|
|
2904
|
+
import_core17.sqlDatabaseManager.registerDatabase(config2.key, config2.config);
|
|
2905
|
+
} catch (error) {
|
|
2906
|
+
console.warn("Failed to auto-register database:", error);
|
|
2907
|
+
}
|
|
2908
|
+
reply.code(201);
|
|
2909
|
+
return {
|
|
2910
|
+
success: true,
|
|
2911
|
+
message: "Database configuration created successfully",
|
|
2912
|
+
data: config2
|
|
2913
|
+
};
|
|
2914
|
+
} catch (error) {
|
|
2915
|
+
console.error("Failed to create database config:", error);
|
|
2916
|
+
return {
|
|
2917
|
+
success: false,
|
|
2918
|
+
message: "Failed to create database configuration"
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
async function updateDatabaseConfig(request, reply) {
|
|
2923
|
+
const tenantId = getTenantId(request);
|
|
2924
|
+
const { key } = request.params;
|
|
2925
|
+
const updates = request.body;
|
|
2926
|
+
try {
|
|
2927
|
+
const storeLattice = (0, import_core17.getStoreLattice)("default", "database");
|
|
2928
|
+
const store = storeLattice.store;
|
|
2929
|
+
const existing = await store.getConfigByKey(tenantId, key);
|
|
2930
|
+
if (!existing) {
|
|
2931
|
+
reply.code(404);
|
|
2932
|
+
return {
|
|
2933
|
+
success: false,
|
|
2934
|
+
message: "Database configuration not found"
|
|
2935
|
+
};
|
|
2936
|
+
}
|
|
2937
|
+
const updated = await store.updateConfig(tenantId, existing.id, updates);
|
|
2938
|
+
if (!updated) {
|
|
2939
|
+
return {
|
|
2940
|
+
success: false,
|
|
2941
|
+
message: "Failed to update database configuration"
|
|
2942
|
+
};
|
|
2943
|
+
}
|
|
2944
|
+
if (updates.config) {
|
|
2945
|
+
try {
|
|
2946
|
+
import_core17.sqlDatabaseManager.registerDatabase(updated.key, updated.config);
|
|
2947
|
+
} catch (error) {
|
|
2948
|
+
console.warn("Failed to re-register database:", error);
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
return {
|
|
2952
|
+
success: true,
|
|
2953
|
+
message: "Database configuration updated successfully",
|
|
2954
|
+
data: updated
|
|
2955
|
+
};
|
|
2956
|
+
} catch (error) {
|
|
2957
|
+
console.error("Failed to update database config:", error);
|
|
2958
|
+
return {
|
|
2959
|
+
success: false,
|
|
2960
|
+
message: "Failed to update database configuration"
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
async function deleteDatabaseConfig(request, reply) {
|
|
2965
|
+
const tenantId = getTenantId(request);
|
|
2966
|
+
const { keyOrId } = request.params;
|
|
2967
|
+
try {
|
|
2968
|
+
const storeLattice = (0, import_core17.getStoreLattice)("default", "database");
|
|
2969
|
+
const store = storeLattice.store;
|
|
2970
|
+
console.log("Delete request - keyOrId:", keyOrId);
|
|
2971
|
+
let config2 = await store.getConfigByKey(tenantId, keyOrId);
|
|
2972
|
+
let configKey = keyOrId;
|
|
2973
|
+
if (!config2) {
|
|
2974
|
+
config2 = await store.getConfigById(tenantId, keyOrId);
|
|
2975
|
+
if (config2) {
|
|
2976
|
+
configKey = config2.key;
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
if (!config2) {
|
|
2980
|
+
reply.code(404);
|
|
2981
|
+
return {
|
|
2982
|
+
success: false,
|
|
2983
|
+
message: "Database configuration not found"
|
|
2984
|
+
};
|
|
2985
|
+
}
|
|
2986
|
+
console.log("Found config to delete:", { id: config2.id, key: config2.key });
|
|
2987
|
+
const deleted = await store.deleteConfig(tenantId, config2.id);
|
|
2988
|
+
if (!deleted) {
|
|
2989
|
+
return {
|
|
2990
|
+
success: false,
|
|
2991
|
+
message: "Failed to delete database configuration"
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
try {
|
|
2995
|
+
if (import_core17.sqlDatabaseManager.hasDatabase(configKey)) {
|
|
2996
|
+
await import_core17.sqlDatabaseManager.removeDatabase(configKey);
|
|
2997
|
+
}
|
|
2998
|
+
} catch (error) {
|
|
2999
|
+
console.warn("Failed to remove from SqlDatabaseManager:", error);
|
|
3000
|
+
}
|
|
3001
|
+
return {
|
|
3002
|
+
success: true,
|
|
3003
|
+
message: "Database configuration deleted successfully"
|
|
3004
|
+
};
|
|
3005
|
+
} catch (error) {
|
|
3006
|
+
console.error("Failed to delete database config:", error);
|
|
3007
|
+
return {
|
|
3008
|
+
success: false,
|
|
3009
|
+
message: "Failed to delete database configuration"
|
|
3010
|
+
};
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
async function testDatabaseConnection(request, reply) {
|
|
3014
|
+
const tenantId = getTenantId(request);
|
|
3015
|
+
const { key } = request.params;
|
|
3016
|
+
try {
|
|
3017
|
+
const storeLattice = (0, import_core17.getStoreLattice)("default", "database");
|
|
3018
|
+
const store = storeLattice.store;
|
|
3019
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3020
|
+
if (!config2) {
|
|
3021
|
+
reply.code(404);
|
|
3022
|
+
return {
|
|
3023
|
+
success: false,
|
|
3024
|
+
message: "Database configuration not found"
|
|
3025
|
+
};
|
|
3026
|
+
}
|
|
3027
|
+
const testKey = `__test_${key}_${Date.now()}`;
|
|
3028
|
+
import_core17.sqlDatabaseManager.registerDatabase(testKey, config2.config);
|
|
3029
|
+
const startTime = Date.now();
|
|
3030
|
+
const db = import_core17.sqlDatabaseManager.getDatabase(testKey);
|
|
3031
|
+
try {
|
|
3032
|
+
await db.connect();
|
|
3033
|
+
await db.listTables();
|
|
3034
|
+
const latency = Date.now() - startTime;
|
|
3035
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
3036
|
+
await db.disconnect();
|
|
3037
|
+
await import_core17.sqlDatabaseManager.removeDatabase(testKey);
|
|
3038
|
+
return {
|
|
3039
|
+
success: true,
|
|
3040
|
+
message: "Connection test successful",
|
|
3041
|
+
data: {
|
|
3042
|
+
connected: true,
|
|
3043
|
+
latency
|
|
3044
|
+
}
|
|
3045
|
+
};
|
|
3046
|
+
} catch (error) {
|
|
3047
|
+
try {
|
|
3048
|
+
await db.disconnect();
|
|
3049
|
+
await import_core17.sqlDatabaseManager.removeDatabase(testKey);
|
|
3050
|
+
} catch {
|
|
3051
|
+
}
|
|
3052
|
+
return {
|
|
3053
|
+
success: true,
|
|
3054
|
+
message: "Connection test failed",
|
|
3055
|
+
data: {
|
|
3056
|
+
connected: false,
|
|
3057
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
3058
|
+
}
|
|
3059
|
+
};
|
|
3060
|
+
}
|
|
3061
|
+
} catch (error) {
|
|
3062
|
+
console.error("Failed to test database connection:", error);
|
|
3063
|
+
return {
|
|
3064
|
+
success: false,
|
|
3065
|
+
message: "Failed to test database connection",
|
|
3066
|
+
data: {
|
|
3067
|
+
connected: false,
|
|
3068
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
3069
|
+
}
|
|
3070
|
+
};
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
function registerDatabaseConfigRoutes(app2) {
|
|
3074
|
+
app2.get(
|
|
3075
|
+
"/api/database-configs",
|
|
3076
|
+
getDatabaseConfigList
|
|
3077
|
+
);
|
|
3078
|
+
app2.get(
|
|
3079
|
+
"/api/database-configs/:key",
|
|
3080
|
+
getDatabaseConfig
|
|
3081
|
+
);
|
|
3082
|
+
app2.post(
|
|
3083
|
+
"/api/database-configs",
|
|
3084
|
+
createDatabaseConfig
|
|
3085
|
+
);
|
|
3086
|
+
app2.put(
|
|
3087
|
+
"/api/database-configs/:key",
|
|
3088
|
+
updateDatabaseConfig
|
|
3089
|
+
);
|
|
3090
|
+
app2.delete(
|
|
3091
|
+
"/api/database-configs/:keyOrId",
|
|
3092
|
+
deleteDatabaseConfig
|
|
3093
|
+
);
|
|
3094
|
+
app2.post(
|
|
3095
|
+
"/api/database-configs/:key/test",
|
|
3096
|
+
testDatabaseConnection
|
|
3097
|
+
);
|
|
3098
|
+
}
|
|
3099
|
+
|
|
3100
|
+
// src/controllers/metrics-configs.ts
|
|
3101
|
+
var import_core18 = require("@axiom-lattice/core");
|
|
3102
|
+
var import_crypto4 = require("crypto");
|
|
3103
|
+
function getTenantId2(request) {
|
|
3104
|
+
return request.headers["x-tenant-id"] || "default";
|
|
3105
|
+
}
|
|
3106
|
+
async function getMetricsServerConfigList(request, reply) {
|
|
3107
|
+
const tenantId = getTenantId2(request);
|
|
3108
|
+
try {
|
|
3109
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3110
|
+
const store = storeLattice.store;
|
|
3111
|
+
const configs = await store.getAllConfigs(tenantId);
|
|
3112
|
+
return {
|
|
3113
|
+
success: true,
|
|
3114
|
+
message: "Metrics server configurations retrieved successfully",
|
|
3115
|
+
data: {
|
|
3116
|
+
records: configs,
|
|
3117
|
+
total: configs.length
|
|
3118
|
+
}
|
|
3119
|
+
};
|
|
3120
|
+
} catch (error) {
|
|
3121
|
+
console.error("Failed to get metrics server configs:", error);
|
|
3122
|
+
return {
|
|
3123
|
+
success: false,
|
|
3124
|
+
message: "Failed to retrieve metrics server configurations",
|
|
3125
|
+
data: {
|
|
3126
|
+
records: [],
|
|
3127
|
+
total: 0
|
|
3128
|
+
}
|
|
3129
|
+
};
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
async function getMetricsServerConfig(request, reply) {
|
|
3133
|
+
const tenantId = getTenantId2(request);
|
|
3134
|
+
const { key } = request.params;
|
|
3135
|
+
try {
|
|
3136
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3137
|
+
const store = storeLattice.store;
|
|
3138
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3139
|
+
if (!config2) {
|
|
3140
|
+
return {
|
|
3141
|
+
success: false,
|
|
3142
|
+
message: "Metrics server configuration not found"
|
|
3143
|
+
};
|
|
3144
|
+
}
|
|
3145
|
+
return {
|
|
3146
|
+
success: true,
|
|
3147
|
+
message: "Metrics server configuration retrieved successfully",
|
|
3148
|
+
data: config2
|
|
3149
|
+
};
|
|
3150
|
+
} catch (error) {
|
|
3151
|
+
console.error("Failed to get metrics server config:", error);
|
|
3152
|
+
return {
|
|
3153
|
+
success: false,
|
|
3154
|
+
message: "Failed to retrieve metrics server configuration"
|
|
3155
|
+
};
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
async function createMetricsServerConfig(request, reply) {
|
|
3159
|
+
const tenantId = getTenantId2(request);
|
|
3160
|
+
const body = request.body;
|
|
3161
|
+
try {
|
|
3162
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3163
|
+
const store = storeLattice.store;
|
|
3164
|
+
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
3165
|
+
if (existing) {
|
|
3166
|
+
reply.code(409);
|
|
3167
|
+
return {
|
|
3168
|
+
success: false,
|
|
3169
|
+
message: "Metrics server configuration with this key already exists"
|
|
3170
|
+
};
|
|
3171
|
+
}
|
|
3172
|
+
if (body.config.type === "semantic" && !body.selectedDataSources) {
|
|
3173
|
+
reply.code(400);
|
|
3174
|
+
return {
|
|
3175
|
+
success: false,
|
|
3176
|
+
message: "selectedDataSources is required for semantic metrics servers"
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
const id = body.id || (0, import_crypto4.randomUUID)();
|
|
3180
|
+
const configData = {
|
|
3181
|
+
key: body.key,
|
|
3182
|
+
name: body.name,
|
|
3183
|
+
description: body.description,
|
|
3184
|
+
config: body.config.type === "semantic" ? {
|
|
3185
|
+
...body.config,
|
|
3186
|
+
selectedDataSources: body.selectedDataSources || []
|
|
3187
|
+
} : body.config
|
|
3188
|
+
};
|
|
3189
|
+
const config2 = await store.createConfig(tenantId, id, configData);
|
|
3190
|
+
try {
|
|
3191
|
+
import_core18.metricsServerManager.registerServer(config2.key, config2.config);
|
|
3192
|
+
} catch (error) {
|
|
3193
|
+
console.warn("Failed to auto-register metrics server:", error);
|
|
3194
|
+
}
|
|
3195
|
+
reply.code(201);
|
|
3196
|
+
return {
|
|
3197
|
+
success: true,
|
|
3198
|
+
message: "Metrics server configuration created successfully",
|
|
3199
|
+
data: config2
|
|
3200
|
+
};
|
|
3201
|
+
} catch (error) {
|
|
3202
|
+
console.error("Failed to create metrics server config:", error);
|
|
3203
|
+
return {
|
|
3204
|
+
success: false,
|
|
3205
|
+
message: "Failed to create metrics server configuration"
|
|
3206
|
+
};
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
async function updateMetricsServerConfig(request, reply) {
|
|
3210
|
+
const tenantId = getTenantId2(request);
|
|
3211
|
+
const { key } = request.params;
|
|
3212
|
+
const updates = request.body;
|
|
3213
|
+
try {
|
|
3214
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3215
|
+
const store = storeLattice.store;
|
|
3216
|
+
const existing = await store.getConfigByKey(tenantId, key);
|
|
3217
|
+
if (!existing) {
|
|
3218
|
+
reply.code(404);
|
|
3219
|
+
return {
|
|
3220
|
+
success: false,
|
|
3221
|
+
message: "Metrics server configuration not found"
|
|
3222
|
+
};
|
|
3223
|
+
}
|
|
3224
|
+
const isSemantic = updates.config?.type === "semantic" || existing.config.type === "semantic" && !updates.config?.type;
|
|
3225
|
+
if (isSemantic && updates.selectedDataSources !== void 0) {
|
|
3226
|
+
updates.config = {
|
|
3227
|
+
...existing.config,
|
|
3228
|
+
...updates.config,
|
|
3229
|
+
type: "semantic",
|
|
3230
|
+
selectedDataSources: updates.selectedDataSources
|
|
3231
|
+
};
|
|
3232
|
+
}
|
|
3233
|
+
const updated = await store.updateConfig(tenantId, existing.id, updates);
|
|
3234
|
+
if (!updated) {
|
|
3235
|
+
return {
|
|
3236
|
+
success: false,
|
|
3237
|
+
message: "Failed to update metrics server configuration"
|
|
3238
|
+
};
|
|
3239
|
+
}
|
|
3240
|
+
if (updates.config) {
|
|
3241
|
+
try {
|
|
3242
|
+
import_core18.metricsServerManager.registerServer(updated.key, updated.config);
|
|
3243
|
+
} catch (error) {
|
|
3244
|
+
console.warn("Failed to re-register metrics server:", error);
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
return {
|
|
3248
|
+
success: true,
|
|
3249
|
+
message: "Metrics server configuration updated successfully",
|
|
3250
|
+
data: updated
|
|
3251
|
+
};
|
|
3252
|
+
} catch (error) {
|
|
3253
|
+
console.error("Failed to update metrics server config:", error);
|
|
3254
|
+
return {
|
|
3255
|
+
success: false,
|
|
3256
|
+
message: "Failed to update metrics server configuration"
|
|
3257
|
+
};
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
async function deleteMetricsServerConfig(request, reply) {
|
|
3261
|
+
const tenantId = getTenantId2(request);
|
|
3262
|
+
const { keyOrId } = request.params;
|
|
3263
|
+
try {
|
|
3264
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3265
|
+
const store = storeLattice.store;
|
|
3266
|
+
let config2 = await store.getConfigByKey(tenantId, keyOrId);
|
|
3267
|
+
let configKey = keyOrId;
|
|
3268
|
+
if (!config2) {
|
|
3269
|
+
config2 = await store.getConfigById(tenantId, keyOrId);
|
|
3270
|
+
if (config2) {
|
|
3271
|
+
configKey = config2.key;
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
if (!config2) {
|
|
3275
|
+
reply.code(404);
|
|
3276
|
+
return {
|
|
3277
|
+
success: false,
|
|
3278
|
+
message: "Metrics server configuration not found"
|
|
3279
|
+
};
|
|
3280
|
+
}
|
|
3281
|
+
const deleted = await store.deleteConfig(tenantId, config2.id);
|
|
3282
|
+
if (!deleted) {
|
|
3283
|
+
return {
|
|
3284
|
+
success: false,
|
|
3285
|
+
message: "Failed to delete metrics server configuration"
|
|
3286
|
+
};
|
|
3287
|
+
}
|
|
3288
|
+
try {
|
|
3289
|
+
if (import_core18.metricsServerManager.hasServer(configKey)) {
|
|
3290
|
+
import_core18.metricsServerManager.removeServer(configKey);
|
|
3291
|
+
}
|
|
3292
|
+
} catch (error) {
|
|
3293
|
+
console.warn("Failed to remove from MetricsServerManager:", error);
|
|
3294
|
+
}
|
|
3295
|
+
return {
|
|
3296
|
+
success: true,
|
|
3297
|
+
message: "Metrics server configuration deleted successfully"
|
|
3298
|
+
};
|
|
3299
|
+
} catch (error) {
|
|
3300
|
+
console.error("Failed to delete metrics server config:", error);
|
|
3301
|
+
return {
|
|
3302
|
+
success: false,
|
|
3303
|
+
message: "Failed to delete metrics server configuration"
|
|
3304
|
+
};
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
async function testMetricsServerConnection(request, reply) {
|
|
3308
|
+
const tenantId = getTenantId2(request);
|
|
3309
|
+
const { key } = request.params;
|
|
3310
|
+
try {
|
|
3311
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3312
|
+
const store = storeLattice.store;
|
|
3313
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3314
|
+
if (!config2) {
|
|
3315
|
+
reply.code(404);
|
|
3316
|
+
return {
|
|
3317
|
+
success: false,
|
|
3318
|
+
message: "Metrics server configuration not found"
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
3321
|
+
const testKey = `__test_${key}_${Date.now()}`;
|
|
3322
|
+
import_core18.metricsServerManager.registerServer(testKey, config2.config);
|
|
3323
|
+
try {
|
|
3324
|
+
const client = import_core18.metricsServerManager.getClient(testKey);
|
|
3325
|
+
const result = await client.testConnection();
|
|
3326
|
+
import_core18.metricsServerManager.removeServer(testKey);
|
|
3327
|
+
return {
|
|
3328
|
+
success: true,
|
|
3329
|
+
message: result.connected ? "Connection test successful" : "Connection test failed",
|
|
3330
|
+
data: result
|
|
3331
|
+
};
|
|
3332
|
+
} catch (error) {
|
|
3333
|
+
try {
|
|
3334
|
+
import_core18.metricsServerManager.removeServer(testKey);
|
|
3335
|
+
} catch {
|
|
3336
|
+
}
|
|
3337
|
+
return {
|
|
3338
|
+
success: true,
|
|
3339
|
+
message: "Connection test failed",
|
|
3340
|
+
data: {
|
|
3341
|
+
connected: false,
|
|
3342
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
3345
|
+
}
|
|
3346
|
+
} catch (error) {
|
|
3347
|
+
console.error("Failed to test metrics server connection:", error);
|
|
3348
|
+
return {
|
|
3349
|
+
success: false,
|
|
3350
|
+
message: "Failed to test metrics server connection",
|
|
3351
|
+
data: {
|
|
3352
|
+
connected: false,
|
|
3353
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
3354
|
+
}
|
|
3355
|
+
};
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
async function listAvailableMetrics(request, reply) {
|
|
3359
|
+
const tenantId = getTenantId2(request);
|
|
3360
|
+
const { key } = request.params;
|
|
3361
|
+
try {
|
|
3362
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3363
|
+
const store = storeLattice.store;
|
|
3364
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3365
|
+
if (!config2) {
|
|
3366
|
+
reply.code(404);
|
|
3367
|
+
return {
|
|
3368
|
+
success: false,
|
|
3369
|
+
message: "Metrics server configuration not found"
|
|
3370
|
+
};
|
|
3371
|
+
}
|
|
3372
|
+
if (!import_core18.metricsServerManager.hasServer(key)) {
|
|
3373
|
+
import_core18.metricsServerManager.registerServer(key, config2.config);
|
|
3374
|
+
}
|
|
3375
|
+
const client = import_core18.metricsServerManager.getClient(key);
|
|
3376
|
+
const metrics = await client.listMetrics();
|
|
3377
|
+
return {
|
|
3378
|
+
success: true,
|
|
3379
|
+
message: "Metrics retrieved successfully",
|
|
3380
|
+
data: {
|
|
3381
|
+
metrics: metrics.map((m) => ({
|
|
3382
|
+
name: m.name,
|
|
3383
|
+
type: m.type,
|
|
3384
|
+
description: m.description
|
|
3385
|
+
}))
|
|
3386
|
+
}
|
|
3387
|
+
};
|
|
3388
|
+
} catch (error) {
|
|
3389
|
+
console.error("Failed to list metrics:", error);
|
|
3390
|
+
return {
|
|
3391
|
+
success: false,
|
|
3392
|
+
message: "Failed to retrieve metrics"
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
async function queryMetricsData(request, reply) {
|
|
3397
|
+
const tenantId = getTenantId2(request);
|
|
3398
|
+
const { key } = request.params;
|
|
3399
|
+
const { metricName, startTime, endTime, step, labels } = request.body;
|
|
3400
|
+
try {
|
|
3401
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3402
|
+
const store = storeLattice.store;
|
|
3403
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3404
|
+
if (!config2) {
|
|
3405
|
+
reply.code(404);
|
|
3406
|
+
return {
|
|
3407
|
+
success: false,
|
|
3408
|
+
message: "Metrics server configuration not found"
|
|
3409
|
+
};
|
|
3410
|
+
}
|
|
3411
|
+
if (!metricName) {
|
|
3412
|
+
reply.code(400);
|
|
3413
|
+
return {
|
|
3414
|
+
success: false,
|
|
3415
|
+
message: "metricName is required"
|
|
3416
|
+
};
|
|
3417
|
+
}
|
|
3418
|
+
if (!import_core18.metricsServerManager.hasServer(key)) {
|
|
3419
|
+
import_core18.metricsServerManager.registerServer(key, config2.config);
|
|
3420
|
+
}
|
|
3421
|
+
const client = import_core18.metricsServerManager.getClient(key);
|
|
3422
|
+
const result = await client.queryMetricData(metricName, {
|
|
3423
|
+
startTime,
|
|
3424
|
+
endTime,
|
|
3425
|
+
step,
|
|
3426
|
+
labels
|
|
3427
|
+
});
|
|
3428
|
+
return {
|
|
3429
|
+
success: true,
|
|
3430
|
+
message: "Metric data retrieved successfully",
|
|
3431
|
+
data: {
|
|
3432
|
+
metricName: result.metricName,
|
|
3433
|
+
dataPoints: result.dataPoints
|
|
3434
|
+
}
|
|
3435
|
+
};
|
|
3436
|
+
} catch (error) {
|
|
3437
|
+
console.error("Failed to query metric data:", error);
|
|
3438
|
+
return {
|
|
3439
|
+
success: false,
|
|
3440
|
+
message: "Failed to query metric data"
|
|
3441
|
+
};
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
async function getDataSources(request, reply) {
|
|
3445
|
+
const tenantId = getTenantId2(request);
|
|
3446
|
+
const { key } = request.params;
|
|
3447
|
+
try {
|
|
3448
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3449
|
+
const store = storeLattice.store;
|
|
3450
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3451
|
+
if (!config2) {
|
|
3452
|
+
reply.code(404);
|
|
3453
|
+
return {
|
|
3454
|
+
success: false,
|
|
3455
|
+
message: "Metrics server configuration not found"
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
if (config2.config.type !== "semantic") {
|
|
3459
|
+
reply.code(400);
|
|
3460
|
+
return {
|
|
3461
|
+
success: false,
|
|
3462
|
+
message: "This endpoint is only available for semantic metrics servers"
|
|
3463
|
+
};
|
|
3464
|
+
}
|
|
3465
|
+
const semanticConfig = config2.config;
|
|
3466
|
+
const client = new import_core18.SemanticMetricsClient(semanticConfig);
|
|
3467
|
+
const datasources = await client.getDataSources();
|
|
3468
|
+
return {
|
|
3469
|
+
success: true,
|
|
3470
|
+
message: "Data sources retrieved successfully",
|
|
3471
|
+
data: {
|
|
3472
|
+
datasources
|
|
3473
|
+
}
|
|
3474
|
+
};
|
|
3475
|
+
} catch (error) {
|
|
3476
|
+
console.error("Failed to get data sources:", error);
|
|
3477
|
+
return {
|
|
3478
|
+
success: false,
|
|
3479
|
+
message: "Failed to retrieve data sources"
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
async function getDatasourceMetrics(request, reply) {
|
|
3484
|
+
const tenantId = getTenantId2(request);
|
|
3485
|
+
const { key, datasourceId } = request.params;
|
|
3486
|
+
try {
|
|
3487
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3488
|
+
const store = storeLattice.store;
|
|
3489
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3490
|
+
if (!config2) {
|
|
3491
|
+
reply.code(404);
|
|
3492
|
+
return {
|
|
3493
|
+
success: false,
|
|
3494
|
+
message: "Metrics server configuration not found"
|
|
3495
|
+
};
|
|
3496
|
+
}
|
|
3497
|
+
if (config2.config.type !== "semantic") {
|
|
3498
|
+
reply.code(400);
|
|
3499
|
+
return {
|
|
3500
|
+
success: false,
|
|
3501
|
+
message: "This endpoint is only available for semantic metrics servers"
|
|
3502
|
+
};
|
|
3503
|
+
}
|
|
3504
|
+
const semanticConfig = config2.config;
|
|
3505
|
+
const client = new import_core18.SemanticMetricsClient(semanticConfig);
|
|
3506
|
+
const metrics = await client.getDatasourceMetrics(datasourceId);
|
|
3507
|
+
return {
|
|
3508
|
+
success: true,
|
|
3509
|
+
message: "Datasource metrics retrieved successfully",
|
|
3510
|
+
data: metrics
|
|
3511
|
+
};
|
|
3512
|
+
} catch (error) {
|
|
3513
|
+
console.error("Failed to get datasource metrics:", error);
|
|
3514
|
+
return {
|
|
3515
|
+
success: false,
|
|
3516
|
+
message: "Failed to retrieve datasource metrics"
|
|
3517
|
+
};
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
async function querySemanticMetrics(request, reply) {
|
|
3521
|
+
const tenantId = getTenantId2(request);
|
|
3522
|
+
const { key } = request.params;
|
|
3523
|
+
const body = request.body;
|
|
3524
|
+
try {
|
|
3525
|
+
const storeLattice = (0, import_core18.getStoreLattice)("default", "metrics");
|
|
3526
|
+
const store = storeLattice.store;
|
|
3527
|
+
const config2 = await store.getConfigByKey(tenantId, key);
|
|
3528
|
+
if (!config2) {
|
|
3529
|
+
reply.code(404);
|
|
3530
|
+
return {
|
|
3531
|
+
success: false,
|
|
3532
|
+
message: "Metrics server configuration not found"
|
|
3533
|
+
};
|
|
3534
|
+
}
|
|
3535
|
+
if (config2.config.type !== "semantic") {
|
|
3536
|
+
reply.code(400);
|
|
3537
|
+
return {
|
|
3538
|
+
success: false,
|
|
3539
|
+
message: "This endpoint is only available for semantic metrics servers"
|
|
3540
|
+
};
|
|
3541
|
+
}
|
|
3542
|
+
if (!body.datasourceId || !body.metrics || body.metrics.length === 0) {
|
|
3543
|
+
reply.code(400);
|
|
3544
|
+
return {
|
|
3545
|
+
success: false,
|
|
3546
|
+
message: "datasourceId and metrics array are required"
|
|
3547
|
+
};
|
|
3548
|
+
}
|
|
3549
|
+
const semanticConfig = config2.config;
|
|
3550
|
+
const client = new import_core18.SemanticMetricsClient(semanticConfig);
|
|
3551
|
+
const result = await client.semanticQuery(body);
|
|
3552
|
+
return {
|
|
3553
|
+
success: true,
|
|
3554
|
+
message: "Semantic query executed successfully",
|
|
3555
|
+
data: {
|
|
3556
|
+
datasourceId: result.datasourceId,
|
|
3557
|
+
metrics: result.metrics,
|
|
3558
|
+
dataPoints: result.dataPoints,
|
|
3559
|
+
metadata: result.metadata
|
|
3560
|
+
}
|
|
3561
|
+
};
|
|
3562
|
+
} catch (error) {
|
|
3563
|
+
console.error("Failed to query semantic metrics:", error);
|
|
3564
|
+
return {
|
|
3565
|
+
success: false,
|
|
3566
|
+
message: "Failed to query semantic metrics"
|
|
3567
|
+
};
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
async function testSemanticDataSources(request, reply) {
|
|
3571
|
+
const body = request.body;
|
|
3572
|
+
try {
|
|
3573
|
+
if (!body.serverUrl) {
|
|
3574
|
+
reply.code(400);
|
|
3575
|
+
return {
|
|
3576
|
+
success: false,
|
|
3577
|
+
message: "serverUrl is required"
|
|
3578
|
+
};
|
|
3579
|
+
}
|
|
3580
|
+
const testConfig = {
|
|
3581
|
+
type: "semantic",
|
|
3582
|
+
serverUrl: body.serverUrl,
|
|
3583
|
+
apiKey: body.apiKey,
|
|
3584
|
+
username: body.username,
|
|
3585
|
+
password: body.password,
|
|
3586
|
+
headers: body.headers
|
|
3587
|
+
};
|
|
3588
|
+
const client = new import_core18.SemanticMetricsClient(testConfig);
|
|
3589
|
+
const datasources = await client.getDataSources();
|
|
3590
|
+
return {
|
|
3591
|
+
success: true,
|
|
3592
|
+
message: "Data sources retrieved successfully",
|
|
3593
|
+
data: {
|
|
3594
|
+
datasources
|
|
3595
|
+
}
|
|
3596
|
+
};
|
|
3597
|
+
} catch (error) {
|
|
3598
|
+
console.error("Failed to test datasources:", error);
|
|
3599
|
+
return {
|
|
3600
|
+
success: false,
|
|
3601
|
+
message: `Failed to retrieve data sources: ${error instanceof Error ? error.message : String(error)}`
|
|
3602
|
+
};
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
async function testDatasourceMetrics(request, reply) {
|
|
3606
|
+
const { datasourceId } = request.params;
|
|
3607
|
+
const body = request.body;
|
|
3608
|
+
try {
|
|
3609
|
+
if (!body.serverUrl) {
|
|
3610
|
+
reply.code(400);
|
|
3611
|
+
return {
|
|
3612
|
+
success: false,
|
|
3613
|
+
message: "serverUrl is required"
|
|
3614
|
+
};
|
|
3615
|
+
}
|
|
3616
|
+
const testConfig = {
|
|
3617
|
+
type: "semantic",
|
|
3618
|
+
serverUrl: body.serverUrl,
|
|
3619
|
+
apiKey: body.apiKey,
|
|
3620
|
+
username: body.username,
|
|
3621
|
+
password: body.password,
|
|
3622
|
+
headers: body.headers
|
|
3623
|
+
};
|
|
3624
|
+
const client = new import_core18.SemanticMetricsClient(testConfig);
|
|
3625
|
+
const metrics = await client.getDatasourceMetrics(datasourceId);
|
|
3626
|
+
return {
|
|
3627
|
+
success: true,
|
|
3628
|
+
message: "Datasource metrics retrieved successfully",
|
|
3629
|
+
data: metrics
|
|
3630
|
+
};
|
|
3631
|
+
} catch (error) {
|
|
3632
|
+
console.error("Failed to test datasource metrics:", error);
|
|
3633
|
+
return {
|
|
3634
|
+
success: false,
|
|
3635
|
+
message: `Failed to retrieve datasource metrics: ${error instanceof Error ? error.message : String(error)}`
|
|
3636
|
+
};
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3639
|
+
function registerMetricsServerConfigRoutes(app2) {
|
|
3640
|
+
app2.get("/api/metrics-configs", getMetricsServerConfigList);
|
|
3641
|
+
app2.get("/api/metrics-configs/:key", getMetricsServerConfig);
|
|
3642
|
+
app2.post("/api/metrics-configs", createMetricsServerConfig);
|
|
3643
|
+
app2.put("/api/metrics-configs/:key", updateMetricsServerConfig);
|
|
3644
|
+
app2.delete("/api/metrics-configs/:keyOrId", deleteMetricsServerConfig);
|
|
3645
|
+
app2.post("/api/metrics-configs/:key/test", testMetricsServerConnection);
|
|
3646
|
+
app2.get("/api/metrics-configs/:key/metrics", listAvailableMetrics);
|
|
3647
|
+
app2.post("/api/metrics-configs/:key/query", queryMetricsData);
|
|
3648
|
+
app2.get("/api/metrics-configs/:key/datasources", getDataSources);
|
|
3649
|
+
app2.get("/api/metrics-configs/:key/datasources/:datasourceId/meta", getDatasourceMetrics);
|
|
3650
|
+
app2.post("/api/metrics-configs/:key/semantic-query", querySemanticMetrics);
|
|
3651
|
+
app2.post("/api/metrics-configs/test-datasources", testSemanticDataSources);
|
|
3652
|
+
app2.post("/api/metrics-configs/test-datasources/:datasourceId/meta", testDatasourceMetrics);
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3655
|
+
// src/controllers/users.ts
|
|
3656
|
+
var import_core19 = require("@axiom-lattice/core");
|
|
3657
|
+
var import_uuid4 = require("uuid");
|
|
3658
|
+
var UsersController = class {
|
|
3659
|
+
constructor() {
|
|
3660
|
+
this.userStore = (0, import_core19.getStoreLattice)("default", "user").store;
|
|
3661
|
+
}
|
|
3662
|
+
async listUsers(request, reply) {
|
|
3663
|
+
const { email } = request.query;
|
|
3664
|
+
if (email) {
|
|
3665
|
+
const user = await this.userStore.getUserByEmail(email);
|
|
3666
|
+
return { success: true, data: user ? [user] : [] };
|
|
3667
|
+
}
|
|
3668
|
+
const users = await this.userStore.getAllUsers();
|
|
3669
|
+
return { success: true, data: users };
|
|
3670
|
+
}
|
|
3671
|
+
async createUser(request, reply) {
|
|
3672
|
+
const data = request.body;
|
|
3673
|
+
const id = (0, import_uuid4.v4)();
|
|
3674
|
+
const existingUser = await this.userStore.getUserByEmail(data.email);
|
|
3675
|
+
if (existingUser) {
|
|
3676
|
+
return reply.status(409).send({
|
|
3677
|
+
success: false,
|
|
3678
|
+
error: `User with email ${data.email} already exists`
|
|
3679
|
+
});
|
|
3680
|
+
}
|
|
3681
|
+
const user = await this.userStore.createUser(id, data);
|
|
3682
|
+
return reply.status(201).send({ success: true, data: user });
|
|
3683
|
+
}
|
|
3684
|
+
async getUser(request, reply) {
|
|
3685
|
+
const { userId } = request.params;
|
|
3686
|
+
const user = await this.userStore.getUserById(userId);
|
|
3687
|
+
if (!user) {
|
|
3688
|
+
return reply.status(404).send({
|
|
3689
|
+
success: false,
|
|
3690
|
+
error: "User not found"
|
|
3691
|
+
});
|
|
3692
|
+
}
|
|
3693
|
+
return { success: true, data: user };
|
|
3694
|
+
}
|
|
3695
|
+
async updateUser(request, reply) {
|
|
3696
|
+
const { userId } = request.params;
|
|
3697
|
+
const updates = request.body;
|
|
3698
|
+
if (updates.email) {
|
|
3699
|
+
const existingUser = await this.userStore.getUserByEmail(updates.email);
|
|
3700
|
+
if (existingUser && existingUser.id !== userId) {
|
|
3701
|
+
return reply.status(409).send({
|
|
3702
|
+
success: false,
|
|
3703
|
+
error: `User with email ${updates.email} already exists`
|
|
3704
|
+
});
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
const user = await this.userStore.updateUser(userId, updates);
|
|
3708
|
+
if (!user) {
|
|
3709
|
+
return reply.status(404).send({
|
|
3710
|
+
success: false,
|
|
3711
|
+
error: "User not found"
|
|
3712
|
+
});
|
|
3713
|
+
}
|
|
3714
|
+
return { success: true, data: user };
|
|
3715
|
+
}
|
|
3716
|
+
async deleteUser(request, reply) {
|
|
3717
|
+
const { userId } = request.params;
|
|
3718
|
+
const deleted = await this.userStore.deleteUser(userId);
|
|
3719
|
+
if (!deleted) {
|
|
3720
|
+
return reply.status(404).send({
|
|
3721
|
+
success: false,
|
|
3722
|
+
error: "User not found"
|
|
3723
|
+
});
|
|
3724
|
+
}
|
|
3725
|
+
return { success: true };
|
|
3726
|
+
}
|
|
3727
|
+
};
|
|
3728
|
+
function registerUserRoutes(app2) {
|
|
3729
|
+
const controller = new UsersController();
|
|
3730
|
+
app2.get("/api/users", controller.listUsers.bind(controller));
|
|
3731
|
+
app2.post("/api/users", controller.createUser.bind(controller));
|
|
3732
|
+
app2.get("/api/users/:userId", controller.getUser.bind(controller));
|
|
3733
|
+
app2.patch("/api/users/:userId", controller.updateUser.bind(controller));
|
|
3734
|
+
app2.delete("/api/users/:userId", controller.deleteUser.bind(controller));
|
|
3735
|
+
}
|
|
3736
|
+
|
|
3737
|
+
// src/controllers/tenants.ts
|
|
3738
|
+
var import_core20 = require("@axiom-lattice/core");
|
|
3739
|
+
var import_uuid5 = require("uuid");
|
|
3740
|
+
var TenantsController = class {
|
|
3741
|
+
constructor() {
|
|
3742
|
+
this.tenantStore = (0, import_core20.getStoreLattice)("default", "tenant").store;
|
|
3743
|
+
}
|
|
3744
|
+
// ==================== Tenant CRUD ====================
|
|
3745
|
+
async listTenants(request, reply) {
|
|
3746
|
+
const tenants = await this.tenantStore.getAllTenants();
|
|
3747
|
+
return { success: true, data: tenants };
|
|
3748
|
+
}
|
|
3749
|
+
async createTenant(request, reply) {
|
|
3750
|
+
const data = request.body;
|
|
3751
|
+
const id = (0, import_uuid5.v4)();
|
|
3752
|
+
const tenant = await this.tenantStore.createTenant(id, data);
|
|
3753
|
+
return reply.status(201).send({ success: true, data: tenant });
|
|
3754
|
+
}
|
|
3755
|
+
async getTenant(request, reply) {
|
|
3756
|
+
const { tenantId } = request.params;
|
|
3757
|
+
const tenant = await this.tenantStore.getTenantById(tenantId);
|
|
3758
|
+
if (!tenant) {
|
|
3759
|
+
return reply.status(404).send({
|
|
3760
|
+
success: false,
|
|
3761
|
+
error: "Tenant not found"
|
|
3762
|
+
});
|
|
3763
|
+
}
|
|
3764
|
+
return { success: true, data: tenant };
|
|
3765
|
+
}
|
|
3766
|
+
async updateTenant(request, reply) {
|
|
3767
|
+
const { tenantId } = request.params;
|
|
3768
|
+
const updates = request.body;
|
|
3769
|
+
const tenant = await this.tenantStore.updateTenant(tenantId, updates);
|
|
3770
|
+
if (!tenant) {
|
|
3771
|
+
return reply.status(404).send({
|
|
3772
|
+
success: false,
|
|
3773
|
+
error: "Tenant not found"
|
|
3774
|
+
});
|
|
3775
|
+
}
|
|
3776
|
+
return { success: true, data: tenant };
|
|
3777
|
+
}
|
|
3778
|
+
async deleteTenant(request, reply) {
|
|
3779
|
+
const { tenantId } = request.params;
|
|
3780
|
+
if (tenantId === "default") {
|
|
3781
|
+
return reply.status(403).send({
|
|
3782
|
+
success: false,
|
|
3783
|
+
error: "Cannot delete the default tenant"
|
|
3784
|
+
});
|
|
3785
|
+
}
|
|
3786
|
+
const deleted = await this.tenantStore.deleteTenant(tenantId);
|
|
3787
|
+
if (!deleted) {
|
|
3788
|
+
return reply.status(404).send({
|
|
3789
|
+
success: false,
|
|
3790
|
+
error: "Tenant not found"
|
|
3791
|
+
});
|
|
3792
|
+
}
|
|
3793
|
+
return { success: true };
|
|
3794
|
+
}
|
|
3795
|
+
};
|
|
3796
|
+
function registerTenantRoutes(app2) {
|
|
3797
|
+
const controller = new TenantsController();
|
|
3798
|
+
app2.get("/api/tenants", controller.listTenants.bind(controller));
|
|
3799
|
+
app2.post("/api/tenants", controller.createTenant.bind(controller));
|
|
3800
|
+
app2.get("/api/tenants/:tenantId", controller.getTenant.bind(controller));
|
|
3801
|
+
app2.patch("/api/tenants/:tenantId", controller.updateTenant.bind(controller));
|
|
3802
|
+
app2.delete("/api/tenants/:tenantId", controller.deleteTenant.bind(controller));
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3805
|
+
// src/controllers/auth.ts
|
|
3806
|
+
var import_core21 = require("@axiom-lattice/core");
|
|
3807
|
+
var import_uuid6 = require("uuid");
|
|
3808
|
+
var defaultAuthConfig = {
|
|
3809
|
+
autoApproveUsers: true,
|
|
3810
|
+
allowTenantRegistration: true,
|
|
3811
|
+
tokenExpiration: 86400
|
|
3812
|
+
};
|
|
3813
|
+
var AuthController = class {
|
|
3814
|
+
constructor(config2 = {}) {
|
|
3815
|
+
this.userStore = (0, import_core21.getStoreLattice)("default", "user").store;
|
|
3816
|
+
this.tenantStore = (0, import_core21.getStoreLattice)("default", "tenant").store;
|
|
3817
|
+
this.userTenantLinkStore = (0, import_core21.getStoreLattice)("default", "userTenantLink").store;
|
|
3818
|
+
this.config = { ...defaultAuthConfig, ...config2 };
|
|
3819
|
+
}
|
|
3820
|
+
async register(request, reply) {
|
|
3821
|
+
const { email, password, name } = request.body;
|
|
3822
|
+
try {
|
|
3823
|
+
const existingUser = await this.userStore.getUserByEmail(email);
|
|
3824
|
+
if (existingUser) {
|
|
3825
|
+
return reply.status(409).send({
|
|
3826
|
+
success: false,
|
|
3827
|
+
error: "User with this email already exists"
|
|
3828
|
+
});
|
|
3829
|
+
}
|
|
3830
|
+
const userId = (0, import_uuid6.v4)();
|
|
3831
|
+
const userStatus = this.config.autoApproveUsers ? "active" : "pending";
|
|
3832
|
+
const userData = {
|
|
3833
|
+
email,
|
|
3834
|
+
name,
|
|
3835
|
+
status: userStatus,
|
|
3836
|
+
metadata: {
|
|
3837
|
+
passwordHash: await this.hashPassword(password)
|
|
3838
|
+
}
|
|
3839
|
+
};
|
|
3840
|
+
const user = await this.userStore.createUser(userId, userData);
|
|
3841
|
+
return reply.status(201).send({
|
|
3842
|
+
success: true,
|
|
3843
|
+
message: this.config.autoApproveUsers ? "Registration successful" : "Registration successful. Please wait for admin approval.",
|
|
3844
|
+
data: {
|
|
3845
|
+
user: {
|
|
3846
|
+
id: user.id,
|
|
3847
|
+
email: user.email,
|
|
3848
|
+
name: user.name,
|
|
3849
|
+
status: user.status
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
});
|
|
3853
|
+
} catch (error) {
|
|
3854
|
+
console.error("Registration error:", error);
|
|
3855
|
+
return reply.status(500).send({
|
|
3856
|
+
success: false,
|
|
3857
|
+
error: "Registration failed"
|
|
3858
|
+
});
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
async login(request, reply) {
|
|
3862
|
+
const { email, password } = request.body;
|
|
3863
|
+
try {
|
|
3864
|
+
const user = await this.userStore.getUserByEmail(email);
|
|
3865
|
+
if (!user) {
|
|
3866
|
+
return reply.status(401).send({
|
|
3867
|
+
success: false,
|
|
3868
|
+
error: "Invalid credentials"
|
|
3869
|
+
});
|
|
3870
|
+
}
|
|
3871
|
+
if (user.status !== "active") {
|
|
3872
|
+
return reply.status(403).send({
|
|
3873
|
+
success: false,
|
|
3874
|
+
error: `Account is ${user.status}. Please wait for admin approval.`
|
|
3875
|
+
});
|
|
3876
|
+
}
|
|
3877
|
+
const isValidPassword = await this.verifyPassword(
|
|
3878
|
+
password,
|
|
3879
|
+
user.metadata?.passwordHash || ""
|
|
3880
|
+
);
|
|
3881
|
+
if (!isValidPassword) {
|
|
3882
|
+
return reply.status(401).send({
|
|
3883
|
+
success: false,
|
|
3884
|
+
error: "Invalid credentials"
|
|
3885
|
+
});
|
|
3886
|
+
}
|
|
3887
|
+
const tenantLinks = await this.userTenantLinkStore.getTenantsByUser(user.id);
|
|
3888
|
+
const token = await this.generateToken(user.id);
|
|
3889
|
+
return {
|
|
3890
|
+
success: true,
|
|
3891
|
+
data: {
|
|
3892
|
+
user: {
|
|
3893
|
+
id: user.id,
|
|
3894
|
+
email: user.email,
|
|
3895
|
+
name: user.name,
|
|
3896
|
+
status: user.status
|
|
3897
|
+
},
|
|
3898
|
+
tenants: tenantLinks.map((link) => ({
|
|
3899
|
+
id: link.tenantId,
|
|
3900
|
+
role: link.role
|
|
3901
|
+
})),
|
|
3902
|
+
token,
|
|
3903
|
+
requiresTenantSelection: tenantLinks.length > 1,
|
|
3904
|
+
hasTenants: tenantLinks.length > 0
|
|
3905
|
+
}
|
|
3906
|
+
};
|
|
3907
|
+
} catch (error) {
|
|
3908
|
+
console.error("Login error:", error);
|
|
3909
|
+
return reply.status(500).send({
|
|
3910
|
+
success: false,
|
|
3911
|
+
error: "Login failed"
|
|
3912
|
+
});
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
async getUserTenants(request, reply) {
|
|
3916
|
+
const userId = request.user?.id;
|
|
3917
|
+
if (!userId) {
|
|
3918
|
+
return reply.status(401).send({
|
|
3919
|
+
success: false,
|
|
3920
|
+
error: "Unauthorized"
|
|
3921
|
+
});
|
|
3922
|
+
}
|
|
3923
|
+
try {
|
|
3924
|
+
const links = await this.userTenantLinkStore.getTenantsByUser(userId);
|
|
3925
|
+
const tenantsWithDetails = await Promise.all(
|
|
3926
|
+
links.map(async (link) => {
|
|
3927
|
+
const tenant = await this.tenantStore.getTenantById(link.tenantId);
|
|
3928
|
+
return {
|
|
3929
|
+
...link,
|
|
3930
|
+
tenant: tenant || null
|
|
3931
|
+
};
|
|
3932
|
+
})
|
|
3933
|
+
);
|
|
3934
|
+
return {
|
|
3935
|
+
success: true,
|
|
3936
|
+
data: tenantsWithDetails
|
|
3937
|
+
};
|
|
3938
|
+
} catch (error) {
|
|
3939
|
+
console.error("Get user tenants error:", error);
|
|
3940
|
+
return reply.status(500).send({
|
|
3941
|
+
success: false,
|
|
3942
|
+
error: "Failed to get user tenants"
|
|
3943
|
+
});
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
async selectTenant(request, reply) {
|
|
3947
|
+
const userId = request.user?.id;
|
|
3948
|
+
const { tenantId } = request.body;
|
|
3949
|
+
if (!userId) {
|
|
3950
|
+
return reply.status(401).send({
|
|
3951
|
+
success: false,
|
|
3952
|
+
error: "Unauthorized"
|
|
3953
|
+
});
|
|
3954
|
+
}
|
|
3955
|
+
try {
|
|
3956
|
+
const hasLink = await this.userTenantLinkStore.hasLink(userId, tenantId);
|
|
3957
|
+
if (!hasLink) {
|
|
3958
|
+
return reply.status(403).send({
|
|
3959
|
+
success: false,
|
|
3960
|
+
error: "You do not have access to this tenant"
|
|
3961
|
+
});
|
|
3962
|
+
}
|
|
3963
|
+
const tenant = await this.tenantStore.getTenantById(tenantId);
|
|
3964
|
+
if (!tenant) {
|
|
3965
|
+
return reply.status(404).send({
|
|
3966
|
+
success: false,
|
|
3967
|
+
error: "Tenant not found"
|
|
3968
|
+
});
|
|
3969
|
+
}
|
|
3970
|
+
const token = await this.generateToken(userId, tenantId);
|
|
3971
|
+
return {
|
|
3972
|
+
success: true,
|
|
3973
|
+
data: {
|
|
3974
|
+
tenant: {
|
|
3975
|
+
id: tenant.id,
|
|
3976
|
+
name: tenant.name,
|
|
3977
|
+
status: tenant.status
|
|
3978
|
+
},
|
|
3979
|
+
token
|
|
3980
|
+
}
|
|
3981
|
+
};
|
|
3982
|
+
} catch (error) {
|
|
3983
|
+
console.error("Select tenant error:", error);
|
|
3984
|
+
return reply.status(500).send({
|
|
3985
|
+
success: false,
|
|
3986
|
+
error: "Failed to select tenant"
|
|
3987
|
+
});
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
async approveUser(request, reply) {
|
|
3991
|
+
const { userId, approved } = request.body;
|
|
3992
|
+
try {
|
|
3993
|
+
const user = await this.userStore.getUserById(userId);
|
|
3994
|
+
if (!user) {
|
|
3995
|
+
return reply.status(404).send({
|
|
3996
|
+
success: false,
|
|
3997
|
+
error: "User not found"
|
|
3998
|
+
});
|
|
3999
|
+
}
|
|
4000
|
+
if (user.status !== "pending") {
|
|
4001
|
+
return reply.status(400).send({
|
|
4002
|
+
success: false,
|
|
4003
|
+
error: `User is already ${user.status}`
|
|
4004
|
+
});
|
|
4005
|
+
}
|
|
4006
|
+
const updatedUser = await this.userStore.updateUser(userId, {
|
|
4007
|
+
status: approved ? "active" : "suspended"
|
|
4008
|
+
});
|
|
4009
|
+
return {
|
|
4010
|
+
success: true,
|
|
4011
|
+
data: updatedUser
|
|
4012
|
+
};
|
|
4013
|
+
} catch (error) {
|
|
4014
|
+
console.error("Approval error:", error);
|
|
4015
|
+
return reply.status(500).send({
|
|
4016
|
+
success: false,
|
|
4017
|
+
error: "Approval failed"
|
|
4018
|
+
});
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
async listPendingUsers(request, reply) {
|
|
4022
|
+
try {
|
|
4023
|
+
const users = await this.userStore.getAllUsers();
|
|
4024
|
+
const pendingUsers = users.filter((u) => u.status === "pending");
|
|
4025
|
+
return {
|
|
4026
|
+
success: true,
|
|
4027
|
+
data: pendingUsers
|
|
4028
|
+
};
|
|
4029
|
+
} catch (error) {
|
|
4030
|
+
console.error("List pending users error:", error);
|
|
4031
|
+
return reply.status(500).send({
|
|
4032
|
+
success: false,
|
|
4033
|
+
error: "Failed to list pending users"
|
|
4034
|
+
});
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
async assignTenant(request, reply) {
|
|
4038
|
+
const { userId, tenantId, role = "member" } = request.body;
|
|
4039
|
+
try {
|
|
4040
|
+
const user = await this.userStore.getUserById(userId);
|
|
4041
|
+
if (!user) {
|
|
4042
|
+
return reply.status(404).send({
|
|
4043
|
+
success: false,
|
|
4044
|
+
error: "User not found"
|
|
4045
|
+
});
|
|
4046
|
+
}
|
|
4047
|
+
const tenant = await this.tenantStore.getTenantById(tenantId);
|
|
4048
|
+
if (!tenant) {
|
|
4049
|
+
return reply.status(404).send({
|
|
4050
|
+
success: false,
|
|
4051
|
+
error: "Tenant not found"
|
|
4052
|
+
});
|
|
4053
|
+
}
|
|
4054
|
+
const link = await this.userTenantLinkStore.createLink({
|
|
4055
|
+
userId,
|
|
4056
|
+
tenantId,
|
|
4057
|
+
role
|
|
4058
|
+
});
|
|
4059
|
+
return {
|
|
4060
|
+
success: true,
|
|
4061
|
+
data: link
|
|
4062
|
+
};
|
|
4063
|
+
} catch (error) {
|
|
4064
|
+
console.error("Assign tenant error:", error);
|
|
4065
|
+
return reply.status(500).send({
|
|
4066
|
+
success: false,
|
|
4067
|
+
error: "Failed to assign tenant"
|
|
4068
|
+
});
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
async hashPassword(password) {
|
|
4072
|
+
const encoder = new TextEncoder();
|
|
4073
|
+
const data = encoder.encode(password + "salt");
|
|
4074
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
4075
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
4076
|
+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
4077
|
+
}
|
|
4078
|
+
async verifyPassword(password, hash) {
|
|
4079
|
+
const hashedPassword = await this.hashPassword(password);
|
|
4080
|
+
return hashedPassword === hash;
|
|
4081
|
+
}
|
|
4082
|
+
async generateToken(userId, tenantId) {
|
|
4083
|
+
const payload = {
|
|
4084
|
+
userId,
|
|
4085
|
+
exp: Date.now() + this.config.tokenExpiration * 1e3
|
|
4086
|
+
};
|
|
4087
|
+
if (tenantId) {
|
|
4088
|
+
payload.tenantId = tenantId;
|
|
4089
|
+
}
|
|
4090
|
+
return btoa(JSON.stringify(payload));
|
|
4091
|
+
}
|
|
4092
|
+
};
|
|
4093
|
+
function registerAuthRoutes(app2, config2) {
|
|
4094
|
+
const controller = new AuthController(config2);
|
|
4095
|
+
const authHook = async (request, reply) => {
|
|
4096
|
+
const authHeader = request.headers.authorization;
|
|
4097
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
4098
|
+
return reply.status(401).send({
|
|
4099
|
+
success: false,
|
|
4100
|
+
error: "Unauthorized - Missing or invalid token"
|
|
4101
|
+
});
|
|
4102
|
+
}
|
|
4103
|
+
const token = authHeader.substring(7);
|
|
4104
|
+
try {
|
|
4105
|
+
const payload = JSON.parse(atob(token));
|
|
4106
|
+
if (payload.exp && payload.exp < Date.now()) {
|
|
4107
|
+
return reply.status(401).send({
|
|
4108
|
+
success: false,
|
|
4109
|
+
error: "Unauthorized - Token expired"
|
|
4110
|
+
});
|
|
4111
|
+
}
|
|
4112
|
+
request.user = {
|
|
4113
|
+
id: payload.userId,
|
|
4114
|
+
tenantId: payload.tenantId
|
|
4115
|
+
};
|
|
4116
|
+
} catch {
|
|
4117
|
+
return reply.status(401).send({
|
|
4118
|
+
success: false,
|
|
4119
|
+
error: "Unauthorized - Invalid token"
|
|
4120
|
+
});
|
|
4121
|
+
}
|
|
4122
|
+
};
|
|
4123
|
+
app2.post("/api/auth/register", controller.register.bind(controller));
|
|
4124
|
+
app2.post("/api/auth/login", controller.login.bind(controller));
|
|
4125
|
+
app2.get("/api/auth/tenants", { preHandler: authHook }, (req, res) => controller.getUserTenants(req, res));
|
|
4126
|
+
app2.post("/api/auth/select-tenant", { preHandler: authHook }, (req, res) => controller.selectTenant(req, res));
|
|
4127
|
+
app2.post("/api/auth/approve", { preHandler: authHook }, (req, res) => controller.approveUser(req, res));
|
|
4128
|
+
app2.get("/api/auth/pending", { preHandler: authHook }, (req, res) => controller.listPendingUsers(req, res));
|
|
4129
|
+
app2.post("/api/auth/assign-tenant", { preHandler: authHook }, (req, res) => controller.assignTenant(req, res));
|
|
4130
|
+
}
|
|
4131
|
+
|
|
4132
|
+
// src/routes/index.ts
|
|
4133
|
+
var registerLatticeRoutes = (app2) => {
|
|
4134
|
+
app2.post("/api/runs", createRun);
|
|
4135
|
+
app2.post("/api/resume_stream", resumeStream);
|
|
4136
|
+
app2.get(
|
|
4137
|
+
"/api/assistants/:assistantId/:thread_id/memory",
|
|
4138
|
+
{ schema: getAllMemoryItemsSchema },
|
|
4139
|
+
getAllMemoryItems
|
|
4140
|
+
);
|
|
4141
|
+
app2.get(
|
|
4142
|
+
"/api/assistants/:assistantId/:thread_id/state",
|
|
4143
|
+
{ schema: getAgentStateSchema },
|
|
4144
|
+
getAgentState
|
|
4145
|
+
);
|
|
4146
|
+
app2.get(
|
|
4147
|
+
"/api/assistants/:assistantId/memory/:key",
|
|
4148
|
+
{ schema: getMemoryItemSchema },
|
|
4149
|
+
getMemoryItem
|
|
4150
|
+
);
|
|
4151
|
+
app2.put(
|
|
4152
|
+
"/api/assistants/:assistantId/memory/:key",
|
|
4153
|
+
{ schema: setMemoryItemSchema },
|
|
4154
|
+
setMemoryItem
|
|
4155
|
+
);
|
|
4156
|
+
app2.delete(
|
|
4157
|
+
"/api/assistants/:assistantId/memory/:key",
|
|
4158
|
+
{ schema: deleteMemoryItemSchema },
|
|
4159
|
+
deleteMemoryItem
|
|
4160
|
+
);
|
|
4161
|
+
app2.delete(
|
|
4162
|
+
"/api/assistants/:assistantId/memory",
|
|
4163
|
+
{ schema: clearMemorySchema },
|
|
4164
|
+
clearMemory
|
|
4165
|
+
);
|
|
4166
|
+
app2.get("/api/assistants", getAssistantList);
|
|
4167
|
+
app2.get("/api/assistants/:id", getAssistant);
|
|
4168
|
+
app2.post("/api/assistants", createAssistant);
|
|
4169
|
+
app2.put("/api/assistants/:id", updateAssistant);
|
|
4170
|
+
app2.delete("/api/assistants/:id", deleteAssistant);
|
|
4171
|
+
app2.get(
|
|
4172
|
+
"/api/assistants/:assistantId/graph",
|
|
4173
|
+
{ schema: getAgentGraphSchema },
|
|
4174
|
+
getAgentGraph
|
|
4175
|
+
);
|
|
4176
|
+
app2.post(
|
|
4177
|
+
"/api/agent-tasks/trigger",
|
|
4178
|
+
{ schema: triggerAgentTaskSchema },
|
|
4179
|
+
triggerAgentTask
|
|
4180
|
+
);
|
|
4181
|
+
app2.get("/api/assistants/:assistantId/threads", getThreadList);
|
|
4182
|
+
app2.get(
|
|
4183
|
+
"/api/assistants/:assistantId/threads/:threadId",
|
|
4184
|
+
getThread
|
|
4185
|
+
);
|
|
4186
|
+
app2.post("/api/assistants/:assistantId/threads", createThread);
|
|
4187
|
+
app2.put(
|
|
4188
|
+
"/api/assistants/:assistantId/threads/:threadId",
|
|
4189
|
+
updateThread
|
|
4190
|
+
);
|
|
4191
|
+
app2.delete(
|
|
4192
|
+
"/api/assistants/:assistantId/threads/:threadId",
|
|
4193
|
+
deleteThread
|
|
4194
|
+
);
|
|
4195
|
+
app2.get(
|
|
4196
|
+
"/api/config",
|
|
4197
|
+
{ schema: getConfigSchema },
|
|
4198
|
+
getConfig
|
|
4199
|
+
);
|
|
4200
|
+
app2.put(
|
|
4201
|
+
"/api/config",
|
|
4202
|
+
{ schema: updateConfigSchema },
|
|
4203
|
+
updateConfig
|
|
4204
|
+
);
|
|
4205
|
+
app2.get("/api/models", getModels);
|
|
4206
|
+
app2.put("/api/models", updateModels);
|
|
4207
|
+
app2.get("/api/tools", getToolConfigs);
|
|
4208
|
+
app2.get(
|
|
4209
|
+
"/health",
|
|
4210
|
+
{ schema: getHealthSchema },
|
|
4211
|
+
getHealth
|
|
4212
|
+
);
|
|
4213
|
+
app2.get(
|
|
4214
|
+
"/api/assistants/:assistantId/threads/:threadId/schedules",
|
|
4215
|
+
getThreadSchedules
|
|
4216
|
+
);
|
|
4217
|
+
app2.get("/api/schedules/:taskId", getScheduledTask);
|
|
4218
|
+
app2.post("/api/schedules/:taskId/cancel", cancelScheduledTask);
|
|
4219
|
+
app2.post("/api/schedules/:taskId/pause", pauseScheduledTask);
|
|
4220
|
+
app2.post("/api/schedules/:taskId/resume", resumeScheduledTask);
|
|
4221
|
+
app2.get("/api/skills", getSkillList);
|
|
4222
|
+
app2.get(
|
|
4223
|
+
"/api/skills/:id",
|
|
4224
|
+
getSkill
|
|
4225
|
+
);
|
|
4226
|
+
app2.post(
|
|
4227
|
+
"/api/skills",
|
|
4228
|
+
createSkill
|
|
4229
|
+
);
|
|
4230
|
+
app2.put(
|
|
2445
4231
|
"/api/skills/:id",
|
|
2446
4232
|
updateSkill
|
|
2447
4233
|
);
|
|
@@ -2462,6 +4248,15 @@ var registerLatticeRoutes = (app2) => {
|
|
|
2462
4248
|
filterSkillsByLicense
|
|
2463
4249
|
);
|
|
2464
4250
|
registerSandboxProxyRoutes(app2);
|
|
4251
|
+
registerWorkspaceRoutes(app2);
|
|
4252
|
+
registerDatabaseConfigRoutes(app2);
|
|
4253
|
+
registerMetricsServerConfigRoutes(app2);
|
|
4254
|
+
registerUserRoutes(app2);
|
|
4255
|
+
registerTenantRoutes(app2);
|
|
4256
|
+
registerAuthRoutes(app2, {
|
|
4257
|
+
autoApproveUsers: process.env.AUTO_APPROVE_USERS !== "false",
|
|
4258
|
+
allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false"
|
|
4259
|
+
});
|
|
2465
4260
|
};
|
|
2466
4261
|
|
|
2467
4262
|
// src/swagger.ts
|
|
@@ -2527,7 +4322,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
|
|
|
2527
4322
|
};
|
|
2528
4323
|
|
|
2529
4324
|
// src/services/agent_task_consumer.ts
|
|
2530
|
-
var
|
|
4325
|
+
var import_core22 = require("@axiom-lattice/core");
|
|
2531
4326
|
var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
2532
4327
|
const {
|
|
2533
4328
|
assistant_id,
|
|
@@ -2591,7 +4386,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
2591
4386
|
}
|
|
2592
4387
|
if (callback_event) {
|
|
2593
4388
|
const state = await agent_state({ assistant_id, thread_id });
|
|
2594
|
-
|
|
4389
|
+
import_core22.eventBus.publish(callback_event, {
|
|
2595
4390
|
success: true,
|
|
2596
4391
|
state,
|
|
2597
4392
|
config: { assistant_id, thread_id, tenant_id }
|
|
@@ -2605,7 +4400,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
2605
4400
|
await response.text();
|
|
2606
4401
|
if (callback_event) {
|
|
2607
4402
|
const state = await agent_state({ assistant_id, thread_id });
|
|
2608
|
-
|
|
4403
|
+
import_core22.eventBus.publish(callback_event, {
|
|
2609
4404
|
success: true,
|
|
2610
4405
|
state,
|
|
2611
4406
|
config: { assistant_id, thread_id, tenant_id }
|
|
@@ -2632,7 +4427,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
2632
4427
|
return handleAgentTask(taskRequest, nextRetryCount);
|
|
2633
4428
|
}
|
|
2634
4429
|
if (callback_event) {
|
|
2635
|
-
|
|
4430
|
+
import_core22.eventBus.publish(callback_event, {
|
|
2636
4431
|
success: false,
|
|
2637
4432
|
error: error instanceof Error ? error.message : String(error),
|
|
2638
4433
|
config: { assistant_id, thread_id, tenant_id }
|
|
@@ -2670,7 +4465,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
2670
4465
|
* 初始化事件监听和队列轮询
|
|
2671
4466
|
*/
|
|
2672
4467
|
initialize() {
|
|
2673
|
-
|
|
4468
|
+
import_core22.eventBus.subscribe(import_core22.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
|
|
2674
4469
|
this.startPollingQueue();
|
|
2675
4470
|
console.log("Agent\u4EFB\u52A1\u6D88\u8D39\u8005\u5DF2\u542F\u52A8\u5E76\u76D1\u542C\u4EFB\u52A1\u4E8B\u4EF6\u548C\u961F\u5217");
|
|
2676
4471
|
}
|
|
@@ -2789,7 +4584,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
2789
4584
|
handleAgentTask(taskRequest).catch((error) => {
|
|
2790
4585
|
console.error("\u5904\u7406Agent\u4EFB\u52A1\u65F6\u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:", error);
|
|
2791
4586
|
if (taskRequest.callback_event) {
|
|
2792
|
-
|
|
4587
|
+
import_core22.eventBus.publish(taskRequest.callback_event, {
|
|
2793
4588
|
success: false,
|
|
2794
4589
|
error: error instanceof Error ? error.message : String(error),
|
|
2795
4590
|
config: {
|
|
@@ -2809,7 +4604,7 @@ _AgentTaskConsumer.agent_run_endpoint = "http://localhost:4001/api/runs";
|
|
|
2809
4604
|
var AgentTaskConsumer = _AgentTaskConsumer;
|
|
2810
4605
|
|
|
2811
4606
|
// src/index.ts
|
|
2812
|
-
var
|
|
4607
|
+
var import_core23 = require("@axiom-lattice/core");
|
|
2813
4608
|
var import_protocols2 = require("@axiom-lattice/protocols");
|
|
2814
4609
|
process.on("unhandledRejection", (reason, promise) => {
|
|
2815
4610
|
console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
|
|
@@ -2823,12 +4618,12 @@ var DEFAULT_LOGGER_CONFIG = {
|
|
|
2823
4618
|
};
|
|
2824
4619
|
var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
|
|
2825
4620
|
var logger = loggerLattice.client;
|
|
2826
|
-
function initializeLogger(
|
|
2827
|
-
if (
|
|
2828
|
-
|
|
4621
|
+
function initializeLogger(config2) {
|
|
4622
|
+
if (import_core23.loggerLatticeManager.hasLattice("default")) {
|
|
4623
|
+
import_core23.loggerLatticeManager.removeLattice("default");
|
|
2829
4624
|
}
|
|
2830
|
-
(0,
|
|
2831
|
-
return (0,
|
|
4625
|
+
(0, import_core23.registerLoggerLattice)("default", config2);
|
|
4626
|
+
return (0, import_core23.getLoggerLattice)("default");
|
|
2832
4627
|
}
|
|
2833
4628
|
var app = (0, import_fastify.default)({
|
|
2834
4629
|
logger: false,
|
|
@@ -2868,16 +4663,8 @@ app.addHook("onResponse", (request, reply, done) => {
|
|
|
2868
4663
|
});
|
|
2869
4664
|
app.register(import_cors.default, {
|
|
2870
4665
|
origin: true,
|
|
2871
|
-
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
2872
|
-
allowedHeaders:
|
|
2873
|
-
"Content-Type",
|
|
2874
|
-
"Authorization",
|
|
2875
|
-
"X-Requested-With",
|
|
2876
|
-
"x-tenant-id",
|
|
2877
|
-
"x-request-id",
|
|
2878
|
-
"x-assistant-id",
|
|
2879
|
-
"x-thread-id"
|
|
2880
|
-
],
|
|
4666
|
+
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
|
|
4667
|
+
allowedHeaders: "*",
|
|
2881
4668
|
exposedHeaders: ["Content-Type"],
|
|
2882
4669
|
credentials: true
|
|
2883
4670
|
});
|
|
@@ -2913,23 +4700,23 @@ app.setErrorHandler((error, request, reply) => {
|
|
|
2913
4700
|
error: error.message || "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF"
|
|
2914
4701
|
});
|
|
2915
4702
|
});
|
|
2916
|
-
var start = async (
|
|
4703
|
+
var start = async (config2) => {
|
|
2917
4704
|
try {
|
|
2918
|
-
if (
|
|
4705
|
+
if (config2?.loggerConfig) {
|
|
2919
4706
|
const loggerConfig = {
|
|
2920
4707
|
...DEFAULT_LOGGER_CONFIG,
|
|
2921
|
-
...
|
|
4708
|
+
...config2.loggerConfig,
|
|
2922
4709
|
// Merge file config if provided
|
|
2923
|
-
file:
|
|
4710
|
+
file: config2.loggerConfig.file || DEFAULT_LOGGER_CONFIG.file
|
|
2924
4711
|
};
|
|
2925
4712
|
loggerLattice = initializeLogger(loggerConfig);
|
|
2926
4713
|
logger = loggerLattice.client;
|
|
2927
4714
|
}
|
|
2928
4715
|
app.decorate("loggerLattice", loggerLattice);
|
|
2929
|
-
const target_port =
|
|
4716
|
+
const target_port = config2?.port || Number(process.env.PORT) || 4001;
|
|
2930
4717
|
await app.listen({ port: target_port, host: "0.0.0.0" });
|
|
2931
4718
|
logger.info(`Lattice Gateway is running on port: ${target_port}`);
|
|
2932
|
-
const queueServiceConfig =
|
|
4719
|
+
const queueServiceConfig = config2?.queueServiceConfig;
|
|
2933
4720
|
if (queueServiceConfig) {
|
|
2934
4721
|
setQueueServiceType(queueServiceConfig.type);
|
|
2935
4722
|
if (queueServiceConfig.defaultStartPollingQueue) {
|