@exulu/backend 1.4.0 → 1.5.0
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/CHANGELOG.md +2 -2
- package/dist/index.cjs +1305 -1299
- package/dist/index.d.cts +1 -7
- package/dist/index.d.ts +1 -7
- package/dist/index.js +1305 -1298
- package/package.json +1 -1
- package/README.md +0 -176
package/dist/index.js
CHANGED
|
@@ -69,6 +69,32 @@ var validateJob = (job) => {
|
|
|
69
69
|
return job;
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
// src/registry/classes.ts
|
|
73
|
+
import "zod";
|
|
74
|
+
import "bullmq";
|
|
75
|
+
import { z } from "zod";
|
|
76
|
+
import * as fs from "fs";
|
|
77
|
+
import * as path from "path";
|
|
78
|
+
import { generateObject, generateText, streamText, tool } from "ai";
|
|
79
|
+
|
|
80
|
+
// types/enums/statistics.ts
|
|
81
|
+
var STATISTICS_TYPE_ENUM = {
|
|
82
|
+
CONTEXT_RETRIEVE: "context.retrieve",
|
|
83
|
+
SOURCE_UPDATE: "source.update",
|
|
84
|
+
EMBEDDER_UPSERT: "embedder.upsert",
|
|
85
|
+
EMBEDDER_GENERATE: "embedder.generate",
|
|
86
|
+
EMBEDDER_DELETE: "embedder.delete",
|
|
87
|
+
WORKFLOW_RUN: "workflow.run",
|
|
88
|
+
CONTEXT_UPSERT: "context.upsert",
|
|
89
|
+
TOOL_CALL: "tool.call",
|
|
90
|
+
AGENT_RUN: "agent.run"
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// types/enums/eval-types.ts
|
|
94
|
+
var EVAL_TYPES_ENUM = {
|
|
95
|
+
llm_as_judge: "llm_as_judge"
|
|
96
|
+
};
|
|
97
|
+
|
|
72
98
|
// src/postgres/client.ts
|
|
73
99
|
import Knex from "knex";
|
|
74
100
|
import "knex";
|
|
@@ -77,7 +103,12 @@ var db = {};
|
|
|
77
103
|
async function postgresClient() {
|
|
78
104
|
if (!db["exulu"]) {
|
|
79
105
|
try {
|
|
80
|
-
console.log("[EXULU]
|
|
106
|
+
console.log("[EXULU] Connecting to exulu database.");
|
|
107
|
+
console.log("[EXULU] POSTGRES_DB_HOST:", process.env.POSTGRES_DB_HOST);
|
|
108
|
+
console.log("[EXULU] POSTGRES_DB_PORT:", process.env.POSTGRES_DB_PORT);
|
|
109
|
+
console.log("[EXULU] POSTGRES_DB_USER:", process.env.POSTGRES_DB_USER);
|
|
110
|
+
console.log("[EXULU] POSTGRES_DB_PASSWORD:", process.env.POSTGRES_DB_PASSWORD);
|
|
111
|
+
console.log("[EXULU] POSTGRES_DB_SSL:", process.env.POSTGRES_DB_SSL);
|
|
81
112
|
const knex = Knex({
|
|
82
113
|
client: "pg",
|
|
83
114
|
connection: {
|
|
@@ -101,745 +132,142 @@ async function postgresClient() {
|
|
|
101
132
|
};
|
|
102
133
|
}
|
|
103
134
|
|
|
104
|
-
// src/
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
{
|
|
132
|
-
name: "agent",
|
|
133
|
-
type: "uuid"
|
|
134
|
-
},
|
|
135
|
+
// src/registry/classes.ts
|
|
136
|
+
import pgvector2 from "pgvector/knex";
|
|
137
|
+
|
|
138
|
+
// src/registry/decoraters/bullmq.ts
|
|
139
|
+
import "bullmq";
|
|
140
|
+
import { v4 as uuidv4 } from "uuid";
|
|
141
|
+
var bullmqDecorator = async ({
|
|
142
|
+
label,
|
|
143
|
+
type,
|
|
144
|
+
workflow,
|
|
145
|
+
embedder,
|
|
146
|
+
inputs,
|
|
147
|
+
queue,
|
|
148
|
+
user,
|
|
149
|
+
agent,
|
|
150
|
+
session,
|
|
151
|
+
configuration,
|
|
152
|
+
updater,
|
|
153
|
+
context,
|
|
154
|
+
steps,
|
|
155
|
+
source,
|
|
156
|
+
documents,
|
|
157
|
+
trigger,
|
|
158
|
+
item
|
|
159
|
+
}) => {
|
|
160
|
+
const redisId = uuidv4();
|
|
161
|
+
const job = await queue.add(
|
|
162
|
+
`${embedder || workflow}`,
|
|
135
163
|
{
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
164
|
+
type: `${type}`,
|
|
165
|
+
...embedder && { embedder },
|
|
166
|
+
...workflow && { workflow },
|
|
167
|
+
...configuration && { configuration },
|
|
168
|
+
...updater && { updater },
|
|
169
|
+
...context && { context },
|
|
170
|
+
...source && { source },
|
|
171
|
+
...documents && { documents },
|
|
172
|
+
...steps && { steps },
|
|
173
|
+
...trigger && { trigger },
|
|
174
|
+
...item && { item },
|
|
175
|
+
agent,
|
|
176
|
+
user,
|
|
177
|
+
inputs,
|
|
178
|
+
label,
|
|
179
|
+
session
|
|
139
180
|
},
|
|
140
181
|
{
|
|
141
|
-
|
|
142
|
-
type: "text"
|
|
182
|
+
jobId: redisId
|
|
143
183
|
}
|
|
144
|
-
|
|
184
|
+
);
|
|
185
|
+
const { db: db2 } = await postgresClient();
|
|
186
|
+
const now = /* @__PURE__ */ new Date();
|
|
187
|
+
console.log("[EXULU] scheduling new job", inputs);
|
|
188
|
+
const insertData = {
|
|
189
|
+
name: `${label}`,
|
|
190
|
+
redis: job.id,
|
|
191
|
+
status: "waiting",
|
|
192
|
+
type,
|
|
193
|
+
inputs,
|
|
194
|
+
agent,
|
|
195
|
+
item,
|
|
196
|
+
createdAt: now,
|
|
197
|
+
updatedAt: now,
|
|
198
|
+
user,
|
|
199
|
+
session,
|
|
200
|
+
...embedder && { embedder },
|
|
201
|
+
...workflow && { workflow },
|
|
202
|
+
...configuration && { configuration },
|
|
203
|
+
...steps && { steps },
|
|
204
|
+
...updater && { updater },
|
|
205
|
+
...context && { context },
|
|
206
|
+
...source && { source },
|
|
207
|
+
...documents && { documents: documents.map((doc2) => doc2.id) },
|
|
208
|
+
...trigger && { trigger }
|
|
209
|
+
};
|
|
210
|
+
await db2("jobs").insert(insertData).onConflict("redis").merge({
|
|
211
|
+
...insertData,
|
|
212
|
+
updatedAt: now
|
|
213
|
+
// Only updatedAt changes on updates
|
|
214
|
+
});
|
|
215
|
+
const doc = await db2.from("jobs").where({ redis: job.id }).first();
|
|
216
|
+
if (!doc?.id) {
|
|
217
|
+
throw new Error("Failed to get job ID after insert/update");
|
|
218
|
+
}
|
|
219
|
+
console.log("[EXULU] created job", doc?.id);
|
|
220
|
+
return {
|
|
221
|
+
...job,
|
|
222
|
+
id: doc?.id,
|
|
223
|
+
redis: job.id
|
|
224
|
+
};
|
|
145
225
|
};
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
name: "status",
|
|
189
|
-
type: "text"
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
name: "emailVerified",
|
|
193
|
-
type: "text"
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
name: "apikey",
|
|
197
|
-
type: "text"
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
name: "last_used",
|
|
201
|
-
type: "date"
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
name: "anthropic_token",
|
|
205
|
-
type: "text"
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
name: "role",
|
|
209
|
-
type: "uuid"
|
|
210
|
-
}
|
|
211
|
-
]
|
|
226
|
+
|
|
227
|
+
// src/registry/utils/map-types.ts
|
|
228
|
+
var mapType = (t, type, name, defaultValue) => {
|
|
229
|
+
if (type === "text") {
|
|
230
|
+
t.text(name);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (type === "longText") {
|
|
234
|
+
t.text(name);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (type === "shortText") {
|
|
238
|
+
t.string(name, 100);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (type === "number") {
|
|
242
|
+
t.float(name);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (type === "boolean") {
|
|
246
|
+
t.boolean(name).defaultTo(defaultValue || false);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (type === "code") {
|
|
250
|
+
t.text(name);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (type === "json") {
|
|
254
|
+
t.jsonb(name);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (type === "date") {
|
|
258
|
+
t.timestamp(name);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (type === "uuid") {
|
|
262
|
+
t.uuid(name);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
throw new Error("Invalid type: " + type);
|
|
212
266
|
};
|
|
213
|
-
var rolesSchema = {
|
|
214
|
-
name: {
|
|
215
|
-
plural: "roles",
|
|
216
|
-
singular: "role"
|
|
217
|
-
},
|
|
218
|
-
fields: [
|
|
219
|
-
{
|
|
220
|
-
name: "name",
|
|
221
|
-
type: "text"
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
name: "is_admin",
|
|
225
|
-
type: "boolean",
|
|
226
|
-
default: false
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
name: "agents",
|
|
230
|
-
type: "json"
|
|
231
|
-
}
|
|
232
|
-
]
|
|
233
|
-
};
|
|
234
|
-
var statisticsSchema = {
|
|
235
|
-
name: {
|
|
236
|
-
plural: "statistics",
|
|
237
|
-
singular: "statistic"
|
|
238
|
-
},
|
|
239
|
-
fields: [
|
|
240
|
-
{
|
|
241
|
-
name: "name",
|
|
242
|
-
type: "text"
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
name: "label",
|
|
246
|
-
type: "text"
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
name: "type",
|
|
250
|
-
type: "text"
|
|
251
|
-
},
|
|
252
|
-
{
|
|
253
|
-
name: "total",
|
|
254
|
-
type: "number"
|
|
255
|
-
}
|
|
256
|
-
]
|
|
257
|
-
};
|
|
258
|
-
var workflowSchema = {
|
|
259
|
-
name: {
|
|
260
|
-
plural: "workflows",
|
|
261
|
-
singular: "workflow"
|
|
262
|
-
},
|
|
263
|
-
fields: [
|
|
264
|
-
{
|
|
265
|
-
name: "workflow_name",
|
|
266
|
-
type: "text"
|
|
267
|
-
},
|
|
268
|
-
{
|
|
269
|
-
name: "run_id",
|
|
270
|
-
type: "text"
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
name: "snapshot",
|
|
274
|
-
type: "text"
|
|
275
|
-
}
|
|
276
|
-
]
|
|
277
|
-
};
|
|
278
|
-
var evalResultsSchema = {
|
|
279
|
-
name: {
|
|
280
|
-
plural: "eval_results",
|
|
281
|
-
singular: "eval_result"
|
|
282
|
-
},
|
|
283
|
-
fields: [
|
|
284
|
-
{
|
|
285
|
-
name: "input",
|
|
286
|
-
type: "longText"
|
|
287
|
-
},
|
|
288
|
-
{
|
|
289
|
-
name: "output",
|
|
290
|
-
type: "longText"
|
|
291
|
-
},
|
|
292
|
-
{
|
|
293
|
-
name: "duration",
|
|
294
|
-
type: "number"
|
|
295
|
-
},
|
|
296
|
-
{
|
|
297
|
-
name: "category",
|
|
298
|
-
type: "text"
|
|
299
|
-
},
|
|
300
|
-
{
|
|
301
|
-
name: "metadata",
|
|
302
|
-
type: "json"
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
name: "result",
|
|
306
|
-
type: "number"
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
name: "agent_id",
|
|
310
|
-
type: "uuid"
|
|
311
|
-
},
|
|
312
|
-
{
|
|
313
|
-
name: "workflow_id",
|
|
314
|
-
type: "uuid"
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
name: "eval_type",
|
|
318
|
-
type: "text"
|
|
319
|
-
},
|
|
320
|
-
{
|
|
321
|
-
name: "eval_name",
|
|
322
|
-
type: "text"
|
|
323
|
-
},
|
|
324
|
-
{
|
|
325
|
-
name: "comment",
|
|
326
|
-
type: "longText"
|
|
327
|
-
}
|
|
328
|
-
]
|
|
329
|
-
};
|
|
330
|
-
var jobsSchema = {
|
|
331
|
-
name: {
|
|
332
|
-
plural: "jobs",
|
|
333
|
-
singular: "job"
|
|
334
|
-
},
|
|
335
|
-
fields: [
|
|
336
|
-
{
|
|
337
|
-
name: "redis",
|
|
338
|
-
type: "text"
|
|
339
|
-
},
|
|
340
|
-
{
|
|
341
|
-
name: "session",
|
|
342
|
-
type: "text"
|
|
343
|
-
},
|
|
344
|
-
{
|
|
345
|
-
name: "status",
|
|
346
|
-
type: "text"
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
name: "type",
|
|
350
|
-
type: "text"
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
name: "result",
|
|
354
|
-
type: "longText"
|
|
355
|
-
},
|
|
356
|
-
{
|
|
357
|
-
name: "name",
|
|
358
|
-
type: "text"
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
name: "agent",
|
|
362
|
-
type: "uuid"
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
name: "workflow",
|
|
366
|
-
type: "uuid"
|
|
367
|
-
},
|
|
368
|
-
{
|
|
369
|
-
name: "user",
|
|
370
|
-
// next auth stores users with id type SERIAL, so we need to use number
|
|
371
|
-
type: "number"
|
|
372
|
-
},
|
|
373
|
-
{
|
|
374
|
-
name: "item",
|
|
375
|
-
type: "uuid"
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
name: "steps",
|
|
379
|
-
type: "number"
|
|
380
|
-
},
|
|
381
|
-
{
|
|
382
|
-
name: "inputs",
|
|
383
|
-
type: "json"
|
|
384
|
-
},
|
|
385
|
-
{
|
|
386
|
-
name: "finished_at",
|
|
387
|
-
type: "date"
|
|
388
|
-
},
|
|
389
|
-
{
|
|
390
|
-
name: "duration",
|
|
391
|
-
type: "number"
|
|
392
|
-
}
|
|
393
|
-
]
|
|
394
|
-
};
|
|
395
|
-
var agentsSchema = {
|
|
396
|
-
name: {
|
|
397
|
-
plural: "agents",
|
|
398
|
-
singular: "agent"
|
|
399
|
-
},
|
|
400
|
-
fields: [
|
|
401
|
-
{
|
|
402
|
-
name: "name",
|
|
403
|
-
type: "text"
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
name: "description",
|
|
407
|
-
type: "text"
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
name: "extensions",
|
|
411
|
-
type: "json"
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
name: "backend",
|
|
415
|
-
type: "text"
|
|
416
|
-
},
|
|
417
|
-
{
|
|
418
|
-
name: "type",
|
|
419
|
-
type: "text"
|
|
420
|
-
},
|
|
421
|
-
{
|
|
422
|
-
name: "active",
|
|
423
|
-
type: "boolean",
|
|
424
|
-
default: false
|
|
425
|
-
},
|
|
426
|
-
{
|
|
427
|
-
name: "public",
|
|
428
|
-
type: "boolean",
|
|
429
|
-
default: false
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
name: "tools",
|
|
433
|
-
type: "json"
|
|
434
|
-
}
|
|
435
|
-
]
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
// src/registry/utils/map-types.ts
|
|
439
|
-
var mapType = (t, type, name, defaultValue) => {
|
|
440
|
-
if (type === "text") {
|
|
441
|
-
t.text(name);
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
if (type === "longText") {
|
|
445
|
-
t.text(name);
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
if (type === "shortText") {
|
|
449
|
-
t.string(name, 100);
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
if (type === "number") {
|
|
453
|
-
t.float(name);
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
if (type === "boolean") {
|
|
457
|
-
t.boolean(name).defaultTo(defaultValue || false);
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
if (type === "code") {
|
|
461
|
-
t.text(name);
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
if (type === "json") {
|
|
465
|
-
t.jsonb(name);
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
if (type === "date") {
|
|
469
|
-
t.timestamp(name);
|
|
470
|
-
return;
|
|
471
|
-
}
|
|
472
|
-
if (type === "uuid") {
|
|
473
|
-
t.uuid(name);
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
throw new Error("Invalid type: " + type);
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
// src/registry/utils/sanitize-name.ts
|
|
480
|
-
var sanitizeName = (name) => {
|
|
481
|
-
return name.toLowerCase().replace(/ /g, "_");
|
|
482
|
-
};
|
|
483
|
-
|
|
484
|
-
// src/auth/generate-key.ts
|
|
485
|
-
import bcrypt from "bcryptjs";
|
|
486
|
-
var SALT_ROUNDS = 12;
|
|
487
|
-
async function encryptString(string) {
|
|
488
|
-
const hash = await bcrypt.hash(string, SALT_ROUNDS);
|
|
489
|
-
return hash;
|
|
490
|
-
}
|
|
491
|
-
var generateApiKey = async (name, email) => {
|
|
492
|
-
const { db: db2 } = await postgresClient();
|
|
493
|
-
console.log("[EXULU] Inserting default user and admin role.");
|
|
494
|
-
const existingRole = await db2.from("roles").where({ name: "admin" }).first();
|
|
495
|
-
let roleId;
|
|
496
|
-
if (!existingRole) {
|
|
497
|
-
console.log("[EXULU] Creating default admin role.");
|
|
498
|
-
const role = await db2.from("roles").insert({
|
|
499
|
-
name: "admin",
|
|
500
|
-
is_admin: true,
|
|
501
|
-
agents: []
|
|
502
|
-
}).returning("id");
|
|
503
|
-
roleId = role[0].id;
|
|
504
|
-
} else {
|
|
505
|
-
roleId = existingRole.id;
|
|
506
|
-
}
|
|
507
|
-
const newKeyName = name;
|
|
508
|
-
const plainKey = `sk_${Math.random().toString(36).substring(2, 15)}_${Math.random().toString(36).substring(2, 15)}`;
|
|
509
|
-
const postFix = `/${newKeyName.toLowerCase().trim().replaceAll(" ", "_")}`;
|
|
510
|
-
const encryptedKey = await encryptString(plainKey);
|
|
511
|
-
const existingApiUser = await db2.from("users").where({ email }).first();
|
|
512
|
-
if (!existingApiUser) {
|
|
513
|
-
console.log("[EXULU] Creating default api user.");
|
|
514
|
-
await db2.from("users").insert({
|
|
515
|
-
name,
|
|
516
|
-
email,
|
|
517
|
-
super_admin: true,
|
|
518
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
519
|
-
updatedAt: /* @__PURE__ */ new Date(),
|
|
520
|
-
type: "api",
|
|
521
|
-
emailVerified: /* @__PURE__ */ new Date(),
|
|
522
|
-
apikey: `${encryptedKey}${postFix}`,
|
|
523
|
-
// password: "admin", todo add this again when we implement password auth / encryption as alternative to OTP
|
|
524
|
-
role: roleId
|
|
525
|
-
});
|
|
526
|
-
console.log("[EXULU] Default api user created. Key: ", `${plainKey}${postFix}`);
|
|
527
|
-
} else {
|
|
528
|
-
console.log("[EXULU] API user with that name already exists.");
|
|
529
|
-
}
|
|
530
|
-
console.log("[EXULU] Key generated, copy and use the plain key from here, you will not be able to access it again.");
|
|
531
|
-
console.log("[EXULU] Key: ", `${plainKey}${postFix}`);
|
|
532
|
-
return {
|
|
533
|
-
key: `${plainKey}${postFix}`
|
|
534
|
-
};
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
// src/postgres/init-db.ts
|
|
538
|
-
var up = async function(knex) {
|
|
539
|
-
if (!await knex.schema.hasTable("agent_sessions")) {
|
|
540
|
-
await knex.schema.createTable("agent_sessions", (table) => {
|
|
541
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
542
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
543
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
544
|
-
for (const field of agentSessionsSchema.fields) {
|
|
545
|
-
const { type, name, default: defaultValue } = field;
|
|
546
|
-
if (!type || !name) {
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
mapType(table, type, sanitizeName(name), defaultValue);
|
|
550
|
-
}
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
if (!await knex.schema.hasTable("agent_messages")) {
|
|
554
|
-
await knex.schema.createTable("agent_messages", (table) => {
|
|
555
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
556
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
557
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
558
|
-
for (const field of agentMessagesSchema.fields) {
|
|
559
|
-
const { type, name, default: defaultValue } = field;
|
|
560
|
-
if (!type || !name) {
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
mapType(table, type, sanitizeName(name), defaultValue);
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
if (!await knex.schema.hasTable("roles")) {
|
|
568
|
-
await knex.schema.createTable("roles", (table) => {
|
|
569
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
570
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
571
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
572
|
-
for (const field of rolesSchema.fields) {
|
|
573
|
-
const { type, name, default: defaultValue } = field;
|
|
574
|
-
if (!type || !name) {
|
|
575
|
-
continue;
|
|
576
|
-
}
|
|
577
|
-
mapType(table, type, sanitizeName(name), defaultValue);
|
|
578
|
-
}
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
if (!await knex.schema.hasTable("eval_results")) {
|
|
582
|
-
await knex.schema.createTable("eval_results", (table) => {
|
|
583
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
584
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
585
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
586
|
-
for (const field of evalResultsSchema.fields) {
|
|
587
|
-
const { type, name, default: defaultValue } = field;
|
|
588
|
-
if (!type || !name) {
|
|
589
|
-
continue;
|
|
590
|
-
}
|
|
591
|
-
mapType(table, type, sanitizeName(name), defaultValue);
|
|
592
|
-
}
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
if (!await knex.schema.hasTable("statistics")) {
|
|
596
|
-
await knex.schema.createTable("statistics", (table) => {
|
|
597
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
598
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
599
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
600
|
-
for (const field of statisticsSchema.fields) {
|
|
601
|
-
const { type, name, default: defaultValue } = field;
|
|
602
|
-
if (!type || !name) {
|
|
603
|
-
continue;
|
|
604
|
-
}
|
|
605
|
-
mapType(table, type, sanitizeName(name), defaultValue);
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
if (!await knex.schema.hasTable("jobs")) {
|
|
610
|
-
await knex.schema.createTable("jobs", (table) => {
|
|
611
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
612
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
613
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
614
|
-
for (const field of jobsSchema.fields) {
|
|
615
|
-
const { type, name, default: defaultValue } = field;
|
|
616
|
-
if (!type || !name) {
|
|
617
|
-
continue;
|
|
618
|
-
}
|
|
619
|
-
mapType(table, type, sanitizeName(name), defaultValue);
|
|
620
|
-
}
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
if (!await knex.schema.hasTable("agents")) {
|
|
624
|
-
await knex.schema.createTable("agents", (table) => {
|
|
625
|
-
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
626
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
627
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
628
|
-
for (const field of agentsSchema.fields) {
|
|
629
|
-
const { type, name, default: defaultValue } = field;
|
|
630
|
-
if (!type || !name) {
|
|
631
|
-
continue;
|
|
632
|
-
}
|
|
633
|
-
mapType(table, type, sanitizeName(name), defaultValue);
|
|
634
|
-
}
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
if (!await knex.schema.hasTable("verification_token")) {
|
|
638
|
-
await knex.schema.createTable("verification_token", (table) => {
|
|
639
|
-
table.text("identifier").notNullable();
|
|
640
|
-
table.timestamp("expires", { useTz: true }).notNullable();
|
|
641
|
-
table.text("token").notNullable();
|
|
642
|
-
table.primary(["identifier", "token"]);
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
if (!await knex.schema.hasTable("users")) {
|
|
646
|
-
await knex.schema.createTable("users", (table) => {
|
|
647
|
-
table.increments("id").primary();
|
|
648
|
-
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
649
|
-
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
650
|
-
table.string("name", 255);
|
|
651
|
-
table.string("password", 255);
|
|
652
|
-
table.string("email", 255);
|
|
653
|
-
table.timestamp("emailVerified", { useTz: true });
|
|
654
|
-
table.text("image");
|
|
655
|
-
for (const field of usersSchema.fields) {
|
|
656
|
-
console.log("[EXULU] field", field);
|
|
657
|
-
const { type, name, default: defaultValue } = field;
|
|
658
|
-
if (name === "id" || name === "name" || name === "email" || name === "emailVerified" || name === "image") {
|
|
659
|
-
continue;
|
|
660
|
-
}
|
|
661
|
-
if (!type || !name) {
|
|
662
|
-
continue;
|
|
663
|
-
}
|
|
664
|
-
mapType(table, type, sanitizeName(name), defaultValue);
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
if (!await knex.schema.hasTable("accounts")) {
|
|
669
|
-
await knex.schema.createTable("accounts", (table) => {
|
|
670
|
-
table.increments("id").primary();
|
|
671
|
-
table.integer("userId").notNullable();
|
|
672
|
-
table.string("type", 255).notNullable();
|
|
673
|
-
table.string("provider", 255).notNullable();
|
|
674
|
-
table.string("providerAccountId", 255).notNullable();
|
|
675
|
-
table.text("refresh_token");
|
|
676
|
-
table.text("access_token");
|
|
677
|
-
table.bigInteger("expires_at");
|
|
678
|
-
table.text("id_token");
|
|
679
|
-
table.text("scope");
|
|
680
|
-
table.text("session_state");
|
|
681
|
-
table.text("token_type");
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
};
|
|
685
|
-
var execute = async () => {
|
|
686
|
-
console.log("[EXULU] Initializing database.");
|
|
687
|
-
const { db: db2 } = await postgresClient();
|
|
688
|
-
await up(db2);
|
|
689
|
-
console.log("[EXULU] Inserting default user and admin role.");
|
|
690
|
-
const existingRole = await db2.from("roles").where({ name: "admin" }).first();
|
|
691
|
-
let roleId;
|
|
692
|
-
if (!existingRole) {
|
|
693
|
-
console.log("[EXULU] Creating default admin role.");
|
|
694
|
-
const role = await db2.from("roles").insert({
|
|
695
|
-
name: "admin",
|
|
696
|
-
is_admin: true,
|
|
697
|
-
agents: JSON.stringify([])
|
|
698
|
-
}).returning("id");
|
|
699
|
-
roleId = role[0].id;
|
|
700
|
-
} else {
|
|
701
|
-
roleId = existingRole.id;
|
|
702
|
-
}
|
|
703
|
-
const existingUser = await db2.from("users").where({ email: "admin@exulu.com" }).first();
|
|
704
|
-
if (!existingUser) {
|
|
705
|
-
const password = await encryptString("admin");
|
|
706
|
-
console.log("[EXULU] Creating default admin user.");
|
|
707
|
-
await db2.from("users").insert({
|
|
708
|
-
name: "exulu",
|
|
709
|
-
email: "admin@exulu.com",
|
|
710
|
-
super_admin: true,
|
|
711
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
712
|
-
emailVerified: /* @__PURE__ */ new Date(),
|
|
713
|
-
updatedAt: /* @__PURE__ */ new Date(),
|
|
714
|
-
password,
|
|
715
|
-
type: "user",
|
|
716
|
-
role: roleId
|
|
717
|
-
});
|
|
718
|
-
}
|
|
719
|
-
const { key } = await generateApiKey("exulu", "api@exulu.com");
|
|
720
|
-
console.log("[EXULU] Database initialized.");
|
|
721
|
-
console.log("[EXULU] Default api key: ", `${key}`);
|
|
722
|
-
console.log("[EXULU] Default password if using password auth: ", `admin`);
|
|
723
|
-
console.log("[EXULU] Default email if using password auth: ", `admin@exulu.com`);
|
|
724
|
-
return;
|
|
725
|
-
};
|
|
726
|
-
|
|
727
|
-
// src/registry/classes.ts
|
|
728
|
-
import "zod";
|
|
729
|
-
import "bullmq";
|
|
730
|
-
import { z } from "zod";
|
|
731
|
-
import * as fs from "fs";
|
|
732
|
-
import * as path from "path";
|
|
733
|
-
import { generateObject, generateText, streamText, tool } from "ai";
|
|
734
|
-
|
|
735
|
-
// types/enums/statistics.ts
|
|
736
|
-
var STATISTICS_TYPE_ENUM = {
|
|
737
|
-
CONTEXT_RETRIEVE: "context.retrieve",
|
|
738
|
-
SOURCE_UPDATE: "source.update",
|
|
739
|
-
EMBEDDER_UPSERT: "embedder.upsert",
|
|
740
|
-
EMBEDDER_GENERATE: "embedder.generate",
|
|
741
|
-
EMBEDDER_DELETE: "embedder.delete",
|
|
742
|
-
WORKFLOW_RUN: "workflow.run",
|
|
743
|
-
CONTEXT_UPSERT: "context.upsert",
|
|
744
|
-
TOOL_CALL: "tool.call",
|
|
745
|
-
AGENT_RUN: "agent.run"
|
|
746
|
-
};
|
|
747
|
-
|
|
748
|
-
// types/enums/eval-types.ts
|
|
749
|
-
var EVAL_TYPES_ENUM = {
|
|
750
|
-
llm_as_judge: "llm_as_judge"
|
|
751
|
-
};
|
|
752
|
-
|
|
753
|
-
// src/registry/classes.ts
|
|
754
|
-
import pgvector2 from "pgvector/knex";
|
|
755
267
|
|
|
756
|
-
// src/registry/
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
var bullmqDecorator = async ({
|
|
760
|
-
label,
|
|
761
|
-
type,
|
|
762
|
-
workflow,
|
|
763
|
-
embedder,
|
|
764
|
-
inputs,
|
|
765
|
-
queue,
|
|
766
|
-
user,
|
|
767
|
-
agent,
|
|
768
|
-
session,
|
|
769
|
-
configuration,
|
|
770
|
-
updater,
|
|
771
|
-
context,
|
|
772
|
-
steps,
|
|
773
|
-
source,
|
|
774
|
-
documents,
|
|
775
|
-
trigger,
|
|
776
|
-
item
|
|
777
|
-
}) => {
|
|
778
|
-
const redisId = uuidv4();
|
|
779
|
-
const job = await queue.add(
|
|
780
|
-
`${embedder || workflow}`,
|
|
781
|
-
{
|
|
782
|
-
type: `${type}`,
|
|
783
|
-
...embedder && { embedder },
|
|
784
|
-
...workflow && { workflow },
|
|
785
|
-
...configuration && { configuration },
|
|
786
|
-
...updater && { updater },
|
|
787
|
-
...context && { context },
|
|
788
|
-
...source && { source },
|
|
789
|
-
...documents && { documents },
|
|
790
|
-
...steps && { steps },
|
|
791
|
-
...trigger && { trigger },
|
|
792
|
-
...item && { item },
|
|
793
|
-
agent,
|
|
794
|
-
user,
|
|
795
|
-
inputs,
|
|
796
|
-
label,
|
|
797
|
-
session
|
|
798
|
-
},
|
|
799
|
-
{
|
|
800
|
-
jobId: redisId
|
|
801
|
-
}
|
|
802
|
-
);
|
|
803
|
-
const { db: db2 } = await postgresClient();
|
|
804
|
-
const now = /* @__PURE__ */ new Date();
|
|
805
|
-
console.log("[EXULU] scheduling new job", inputs);
|
|
806
|
-
const insertData = {
|
|
807
|
-
name: `${label}`,
|
|
808
|
-
redis: job.id,
|
|
809
|
-
status: "waiting",
|
|
810
|
-
type,
|
|
811
|
-
inputs,
|
|
812
|
-
agent,
|
|
813
|
-
item,
|
|
814
|
-
createdAt: now,
|
|
815
|
-
updatedAt: now,
|
|
816
|
-
user,
|
|
817
|
-
session,
|
|
818
|
-
...embedder && { embedder },
|
|
819
|
-
...workflow && { workflow },
|
|
820
|
-
...configuration && { configuration },
|
|
821
|
-
...steps && { steps },
|
|
822
|
-
...updater && { updater },
|
|
823
|
-
...context && { context },
|
|
824
|
-
...source && { source },
|
|
825
|
-
...documents && { documents: documents.map((doc2) => doc2.id) },
|
|
826
|
-
...trigger && { trigger }
|
|
827
|
-
};
|
|
828
|
-
await db2("jobs").insert(insertData).onConflict("redis").merge({
|
|
829
|
-
...insertData,
|
|
830
|
-
updatedAt: now
|
|
831
|
-
// Only updatedAt changes on updates
|
|
832
|
-
});
|
|
833
|
-
const doc = await db2.from("jobs").where({ redis: job.id }).first();
|
|
834
|
-
if (!doc?.id) {
|
|
835
|
-
throw new Error("Failed to get job ID after insert/update");
|
|
836
|
-
}
|
|
837
|
-
console.log("[EXULU] created job", doc?.id);
|
|
838
|
-
return {
|
|
839
|
-
...job,
|
|
840
|
-
id: doc?.id,
|
|
841
|
-
redis: job.id
|
|
842
|
-
};
|
|
268
|
+
// src/registry/utils/sanitize-name.ts
|
|
269
|
+
var sanitizeName = (name) => {
|
|
270
|
+
return name.toLowerCase().replace(/ /g, "_");
|
|
843
271
|
};
|
|
844
272
|
|
|
845
273
|
// types/enums/jobs.ts
|
|
@@ -2020,7 +1448,7 @@ var getToken = async (authHeader) => {
|
|
|
2020
1448
|
};
|
|
2021
1449
|
|
|
2022
1450
|
// src/auth/auth.ts
|
|
2023
|
-
import
|
|
1451
|
+
import bcrypt from "bcryptjs";
|
|
2024
1452
|
var authentication = async ({
|
|
2025
1453
|
apikey,
|
|
2026
1454
|
authtoken,
|
|
@@ -2115,7 +1543,7 @@ var authentication = async ({
|
|
|
2115
1543
|
for (const user of filtered) {
|
|
2116
1544
|
const user_key_last_slash_index = user.apikey.lastIndexOf("/");
|
|
2117
1545
|
const user_key_compare_value = user.apikey.substring(0, user_key_last_slash_index);
|
|
2118
|
-
const isMatch = await
|
|
1546
|
+
const isMatch = await bcrypt.compare(request_key_compare_value, user_key_compare_value);
|
|
2119
1547
|
if (isMatch) {
|
|
2120
1548
|
await db2.from("users").where({ id: user.id }).update({
|
|
2121
1549
|
last_used: /* @__PURE__ */ new Date()
|
|
@@ -2172,595 +1600,929 @@ var requestValidators = {
|
|
|
2172
1600
|
message: "Missing body."
|
|
2173
1601
|
};
|
|
2174
1602
|
}
|
|
2175
|
-
if (!req.body.agent) {
|
|
2176
|
-
return {
|
|
2177
|
-
error: true,
|
|
2178
|
-
code: 400,
|
|
2179
|
-
message: "Missing agent in body."
|
|
2180
|
-
};
|
|
1603
|
+
if (!req.body.agent) {
|
|
1604
|
+
return {
|
|
1605
|
+
error: true,
|
|
1606
|
+
code: 400,
|
|
1607
|
+
message: "Missing agent in body."
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
if (!req.body.session) {
|
|
1611
|
+
return {
|
|
1612
|
+
error: true,
|
|
1613
|
+
code: 400,
|
|
1614
|
+
message: "Missing session in body."
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
if (!req.body.inputs) {
|
|
1618
|
+
return {
|
|
1619
|
+
error: true,
|
|
1620
|
+
code: 400,
|
|
1621
|
+
message: "Missing inputs in body."
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
if (!req.body.label) {
|
|
1625
|
+
return {
|
|
1626
|
+
error: true,
|
|
1627
|
+
code: 400,
|
|
1628
|
+
message: "Missing label for job in body."
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
return {
|
|
1632
|
+
error: false
|
|
1633
|
+
};
|
|
1634
|
+
},
|
|
1635
|
+
embedders: (req, configuration) => {
|
|
1636
|
+
const contentType = req.headers["content-type"] || "";
|
|
1637
|
+
if (!contentType.includes("application/json")) {
|
|
1638
|
+
return {
|
|
1639
|
+
error: true,
|
|
1640
|
+
code: 400,
|
|
1641
|
+
message: "Unsupported content type."
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
if (!req.body) {
|
|
1645
|
+
return {
|
|
1646
|
+
error: true,
|
|
1647
|
+
code: 400,
|
|
1648
|
+
message: "Missing body."
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
if (!req.body.inputs) {
|
|
1652
|
+
return {
|
|
1653
|
+
error: true,
|
|
1654
|
+
code: 400,
|
|
1655
|
+
message: "Missing inputs."
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
if (!req.body.label) {
|
|
1659
|
+
return {
|
|
1660
|
+
error: true,
|
|
1661
|
+
code: 400,
|
|
1662
|
+
message: "Missing label for job in body."
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
if (configuration) {
|
|
1666
|
+
for (const key in configuration) {
|
|
1667
|
+
if (!req.body.configuration[key]) {
|
|
1668
|
+
return {
|
|
1669
|
+
error: true,
|
|
1670
|
+
code: 400,
|
|
1671
|
+
message: `Missing ${key} in body.configuration.`
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
return {
|
|
1677
|
+
error: false
|
|
1678
|
+
};
|
|
1679
|
+
},
|
|
1680
|
+
agents: (req) => {
|
|
1681
|
+
const contentType = req.headers["content-type"] || "";
|
|
1682
|
+
if (!contentType.includes("application/json")) {
|
|
1683
|
+
return {
|
|
1684
|
+
error: true,
|
|
1685
|
+
code: 400,
|
|
1686
|
+
message: "Unsupported content type."
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
if (!req.body) {
|
|
1690
|
+
return {
|
|
1691
|
+
error: true,
|
|
1692
|
+
code: 400,
|
|
1693
|
+
message: "Missing body."
|
|
1694
|
+
};
|
|
1695
|
+
}
|
|
1696
|
+
if (!req.body.threadId) {
|
|
1697
|
+
return {
|
|
1698
|
+
error: true,
|
|
1699
|
+
code: 400,
|
|
1700
|
+
message: "Missing threadId in body."
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
if (!req.body.resourceId) {
|
|
1704
|
+
return {
|
|
1705
|
+
error: true,
|
|
1706
|
+
code: 400,
|
|
1707
|
+
message: "Missing resourceId in body."
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
if (!req.body.messages) {
|
|
1711
|
+
return {
|
|
1712
|
+
error: true,
|
|
1713
|
+
code: 400,
|
|
1714
|
+
message: 'Missing "messages" property in body.'
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
return {
|
|
1718
|
+
error: false
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
};
|
|
1722
|
+
|
|
1723
|
+
// src/registry/routes.ts
|
|
1724
|
+
import { zerialize } from "zodex";
|
|
1725
|
+
|
|
1726
|
+
// src/bullmq/queues.ts
|
|
1727
|
+
import { Queue as Queue3 } from "bullmq";
|
|
1728
|
+
var ExuluQueues = class {
|
|
1729
|
+
queues;
|
|
1730
|
+
constructor() {
|
|
1731
|
+
this.queues = [];
|
|
1732
|
+
}
|
|
1733
|
+
queue(name) {
|
|
1734
|
+
return this.queues.find((x) => x.name === name);
|
|
1735
|
+
}
|
|
1736
|
+
use(name) {
|
|
1737
|
+
const existing = this.queues.find((x) => x.name === name);
|
|
1738
|
+
if (existing) {
|
|
1739
|
+
return existing;
|
|
1740
|
+
}
|
|
1741
|
+
if (!redisServer.host?.length || !redisServer.port?.length) {
|
|
1742
|
+
console.error(`[EXULU] no redis server configured, but you are trying to use a queue ( ${name}), likely in an agent or workflow (look for ExuluQueues.use() ).`);
|
|
1743
|
+
throw new Error(`[EXULU] no redis server configured.`);
|
|
1744
|
+
}
|
|
1745
|
+
const newQueue = new Queue3(`${name}`, { connection: redisServer });
|
|
1746
|
+
this.queues.push(newQueue);
|
|
1747
|
+
return newQueue;
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
var queues = new ExuluQueues();
|
|
1751
|
+
|
|
1752
|
+
// types/models/vector-methods.ts
|
|
1753
|
+
var VectorMethodEnum = {
|
|
1754
|
+
"cosineDistance": "cosineDistance",
|
|
1755
|
+
"l1Distance": "l1Distance",
|
|
1756
|
+
"l2Distance": "l2Distance",
|
|
1757
|
+
"hammingDistance": "hammingDistance",
|
|
1758
|
+
"jaccardDistance": "jaccardDistance",
|
|
1759
|
+
"maxInnerProduct": "maxInnerProduct"
|
|
1760
|
+
};
|
|
1761
|
+
|
|
1762
|
+
// src/registry/routes.ts
|
|
1763
|
+
import express from "express";
|
|
1764
|
+
import { ApolloServer } from "@apollo/server";
|
|
1765
|
+
import * as Papa from "papaparse";
|
|
1766
|
+
import cors from "cors";
|
|
1767
|
+
import "reflect-metadata";
|
|
1768
|
+
|
|
1769
|
+
// src/registry/utils/graphql.ts
|
|
1770
|
+
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
1771
|
+
import GraphQLJSON from "graphql-type-json";
|
|
1772
|
+
import { GraphQLScalarType, Kind } from "graphql";
|
|
1773
|
+
import CryptoJS from "crypto-js";
|
|
1774
|
+
var GraphQLDate = new GraphQLScalarType({
|
|
1775
|
+
name: "Date",
|
|
1776
|
+
description: "Date custom scalar type",
|
|
1777
|
+
serialize(value) {
|
|
1778
|
+
if (value instanceof Date) {
|
|
1779
|
+
return value.toISOString();
|
|
1780
|
+
}
|
|
1781
|
+
if (typeof value === "number") {
|
|
1782
|
+
return new Date(value).toISOString();
|
|
2181
1783
|
}
|
|
2182
|
-
if (
|
|
2183
|
-
return
|
|
2184
|
-
error: true,
|
|
2185
|
-
code: 400,
|
|
2186
|
-
message: "Missing session in body."
|
|
2187
|
-
};
|
|
1784
|
+
if (typeof value === "string") {
|
|
1785
|
+
return new Date(value).toISOString();
|
|
2188
1786
|
}
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
};
|
|
1787
|
+
return value;
|
|
1788
|
+
},
|
|
1789
|
+
parseValue(value) {
|
|
1790
|
+
if (typeof value === "string") {
|
|
1791
|
+
return new Date(value);
|
|
2195
1792
|
}
|
|
2196
|
-
if (
|
|
2197
|
-
return
|
|
2198
|
-
error: true,
|
|
2199
|
-
code: 400,
|
|
2200
|
-
message: "Missing label for job in body."
|
|
2201
|
-
};
|
|
1793
|
+
if (typeof value === "number") {
|
|
1794
|
+
return new Date(value);
|
|
2202
1795
|
}
|
|
2203
|
-
return
|
|
2204
|
-
error: false
|
|
2205
|
-
};
|
|
1796
|
+
return value;
|
|
2206
1797
|
},
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
return {
|
|
2211
|
-
error: true,
|
|
2212
|
-
code: 400,
|
|
2213
|
-
message: "Unsupported content type."
|
|
2214
|
-
};
|
|
1798
|
+
parseLiteral(ast) {
|
|
1799
|
+
if (ast.kind === Kind.STRING) {
|
|
1800
|
+
return new Date(ast.value);
|
|
2215
1801
|
}
|
|
2216
|
-
if (
|
|
2217
|
-
return
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
1802
|
+
if (ast.kind === Kind.INT) {
|
|
1803
|
+
return new Date(parseInt(ast.value, 10));
|
|
1804
|
+
}
|
|
1805
|
+
return null;
|
|
1806
|
+
}
|
|
1807
|
+
});
|
|
1808
|
+
var map = (field) => {
|
|
1809
|
+
let type;
|
|
1810
|
+
switch (field.type) {
|
|
1811
|
+
case "text":
|
|
1812
|
+
case "shortText":
|
|
1813
|
+
case "longText":
|
|
1814
|
+
case "code":
|
|
1815
|
+
type = "String";
|
|
1816
|
+
break;
|
|
1817
|
+
case "number":
|
|
1818
|
+
type = "Float";
|
|
1819
|
+
break;
|
|
1820
|
+
case "boolean":
|
|
1821
|
+
type = "Boolean";
|
|
1822
|
+
break;
|
|
1823
|
+
case "json":
|
|
1824
|
+
type = "JSON";
|
|
1825
|
+
break;
|
|
1826
|
+
case "date":
|
|
1827
|
+
type = "Date";
|
|
1828
|
+
break;
|
|
1829
|
+
default:
|
|
1830
|
+
type = "String";
|
|
1831
|
+
}
|
|
1832
|
+
return type;
|
|
1833
|
+
};
|
|
1834
|
+
function createTypeDefs(table) {
|
|
1835
|
+
const fields = table.fields.map((field) => {
|
|
1836
|
+
let type;
|
|
1837
|
+
type = map(field);
|
|
1838
|
+
const required = field.required ? "!" : "";
|
|
1839
|
+
return ` ${field.name}: ${type}${required}`;
|
|
1840
|
+
});
|
|
1841
|
+
const typeDef = `
|
|
1842
|
+
type ${table.name.singular} {
|
|
1843
|
+
${fields.join("\n")}
|
|
1844
|
+
id: ID!
|
|
1845
|
+
createdAt: Date!
|
|
1846
|
+
updatedAt: Date!
|
|
1847
|
+
}
|
|
1848
|
+
`;
|
|
1849
|
+
const inputDef = `
|
|
1850
|
+
input ${table.name.singular}Input {
|
|
1851
|
+
${table.fields.map((f) => ` ${f.name}: ${map(f)}`).join("\n")}
|
|
1852
|
+
}
|
|
1853
|
+
`;
|
|
1854
|
+
return typeDef + inputDef;
|
|
1855
|
+
}
|
|
1856
|
+
function createFilterTypeDefs(table) {
|
|
1857
|
+
const fieldFilters = table.fields.map((field) => {
|
|
1858
|
+
let type;
|
|
1859
|
+
type = map(field);
|
|
1860
|
+
return `
|
|
1861
|
+
${field.name}: FilterOperator${type}`;
|
|
1862
|
+
});
|
|
1863
|
+
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
1864
|
+
const operatorTypes = `
|
|
1865
|
+
input FilterOperatorString {
|
|
1866
|
+
eq: String
|
|
1867
|
+
ne: String
|
|
1868
|
+
in: [String]
|
|
1869
|
+
contains: String
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
input FilterOperatorDate {
|
|
1873
|
+
lte: Date
|
|
1874
|
+
gte: Date
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
input FilterOperatorFloat {
|
|
1878
|
+
eq: Float
|
|
1879
|
+
ne: Float
|
|
1880
|
+
in: [Float]
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
input FilterOperatorBoolean {
|
|
1884
|
+
eq: Boolean
|
|
1885
|
+
ne: Boolean
|
|
1886
|
+
in: [Boolean]
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
input FilterOperatorJSON {
|
|
1890
|
+
eq: JSON
|
|
1891
|
+
ne: JSON
|
|
1892
|
+
in: [JSON]
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
input SortBy {
|
|
1896
|
+
field: String!
|
|
1897
|
+
direction: SortDirection!
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
enum SortDirection {
|
|
1901
|
+
ASC
|
|
1902
|
+
DESC
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
input Filter${tableNameSingularUpperCaseFirst} {
|
|
1906
|
+
${fieldFilters.join("\n")}
|
|
1907
|
+
}`;
|
|
1908
|
+
return operatorTypes;
|
|
1909
|
+
}
|
|
1910
|
+
var getRequestedFields = (info) => {
|
|
1911
|
+
const selections = info.operation.selectionSet.selections[0].selectionSet.selections;
|
|
1912
|
+
const itemsSelection = selections.find((s) => s.name.value === "items");
|
|
1913
|
+
const fields = itemsSelection ? Object.keys(itemsSelection.selectionSet.selections.reduce((acc, field) => {
|
|
1914
|
+
acc[field.name.value] = true;
|
|
1915
|
+
return acc;
|
|
1916
|
+
}, {})) : Object.keys(selections.reduce((acc, field) => {
|
|
1917
|
+
acc[field.name.value] = true;
|
|
1918
|
+
return acc;
|
|
1919
|
+
}, {}));
|
|
1920
|
+
return fields.filter((field) => field !== "pageInfo" && field !== "items");
|
|
1921
|
+
};
|
|
1922
|
+
function createMutations(table) {
|
|
1923
|
+
const tableNamePlural = table.name.plural.toLowerCase();
|
|
1924
|
+
const tableNameSingular = table.name.singular.toLowerCase();
|
|
1925
|
+
return {
|
|
1926
|
+
[`${tableNamePlural}CreateOne`]: async (_, args, context, info) => {
|
|
1927
|
+
const { db: db2 } = context;
|
|
1928
|
+
const requestedFields = getRequestedFields(info);
|
|
1929
|
+
let { input } = args;
|
|
1930
|
+
input = encryptSensitiveFields(input);
|
|
1931
|
+
const results = await db2(tableNamePlural).insert({
|
|
1932
|
+
...input,
|
|
1933
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1934
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1935
|
+
}).returning(requestedFields);
|
|
1936
|
+
return results[0];
|
|
1937
|
+
},
|
|
1938
|
+
[`${tableNamePlural}UpdateOne`]: async (_, args, context, info) => {
|
|
1939
|
+
const { db: db2 } = context;
|
|
1940
|
+
let { where, input } = args;
|
|
1941
|
+
input = encryptSensitiveFields(input);
|
|
1942
|
+
await db2(tableNamePlural).where(where).update({
|
|
1943
|
+
...input,
|
|
1944
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1945
|
+
});
|
|
1946
|
+
const requestedFields = getRequestedFields(info);
|
|
1947
|
+
const result = await db2.from(tableNamePlural).select(requestedFields).where(where).first();
|
|
1948
|
+
return result;
|
|
1949
|
+
},
|
|
1950
|
+
[`${tableNamePlural}UpdateOneById`]: async (_, args, context, info) => {
|
|
1951
|
+
let { id, input } = args;
|
|
1952
|
+
input = encryptSensitiveFields(input);
|
|
1953
|
+
const { db: db2 } = context;
|
|
1954
|
+
await db2(tableNamePlural).where({ id }).update({
|
|
1955
|
+
...input,
|
|
1956
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1957
|
+
});
|
|
1958
|
+
const requestedFields = getRequestedFields(info);
|
|
1959
|
+
const result = await db2.from(tableNamePlural).select(requestedFields).where({ id }).first();
|
|
1960
|
+
return result;
|
|
1961
|
+
},
|
|
1962
|
+
[`${tableNamePlural}RemoveOne`]: async (_, args, context, info) => {
|
|
1963
|
+
const { db: db2 } = context;
|
|
1964
|
+
const { where } = args;
|
|
1965
|
+
const requestedFields = getRequestedFields(info);
|
|
1966
|
+
const result = await db2.from(tableNamePlural).select(requestedFields).where(where).first();
|
|
1967
|
+
await db2(tableNamePlural).where(where).del();
|
|
1968
|
+
return result;
|
|
1969
|
+
},
|
|
1970
|
+
[`${tableNamePlural}RemoveOneById`]: async (_, args, context, info) => {
|
|
1971
|
+
const { id } = args;
|
|
1972
|
+
const { db: db2 } = context;
|
|
1973
|
+
const requestedFields = getRequestedFields(info);
|
|
1974
|
+
const result = await db2.from(tableNamePlural).select(requestedFields).where({ id }).first();
|
|
1975
|
+
await db2(tableNamePlural).where({ id }).del();
|
|
1976
|
+
return result;
|
|
2222
1977
|
}
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
function createQueries(table) {
|
|
1981
|
+
const tableNamePlural = table.name.plural.toLowerCase();
|
|
1982
|
+
const tableNameSingular = table.name.singular.toLowerCase();
|
|
1983
|
+
const applyFilters = (query, filters) => {
|
|
1984
|
+
filters.forEach((filter) => {
|
|
1985
|
+
Object.entries(filter).forEach(([fieldName, operators]) => {
|
|
1986
|
+
if (operators) {
|
|
1987
|
+
if (operators.eq !== void 0) {
|
|
1988
|
+
query = query.where(fieldName, operators.eq);
|
|
1989
|
+
}
|
|
1990
|
+
if (operators.ne !== void 0) {
|
|
1991
|
+
query = query.whereRaw(`?? IS DISTINCT FROM ?`, [fieldName, operators.ne]);
|
|
1992
|
+
}
|
|
1993
|
+
if (operators.in !== void 0) {
|
|
1994
|
+
query = query.whereIn(fieldName, operators.in);
|
|
1995
|
+
}
|
|
1996
|
+
if (operators.contains !== void 0) {
|
|
1997
|
+
query = query.where(fieldName, "like", `%${operators.contains}%`);
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
});
|
|
2001
|
+
});
|
|
2002
|
+
return query;
|
|
2003
|
+
};
|
|
2004
|
+
const applySorting = (query, sort) => {
|
|
2005
|
+
if (sort) {
|
|
2006
|
+
query = query.orderBy(sort.field, sort.direction.toLowerCase());
|
|
2229
2007
|
}
|
|
2230
|
-
|
|
2008
|
+
return query;
|
|
2009
|
+
};
|
|
2010
|
+
return {
|
|
2011
|
+
[`${tableNameSingular}ById`]: async (_, args, context, info) => {
|
|
2012
|
+
const { db: db2 } = context;
|
|
2013
|
+
const requestedFields = getRequestedFields(info);
|
|
2014
|
+
const result = await db2.from(tableNamePlural).select(requestedFields).where({ id: args.id }).first();
|
|
2015
|
+
return result;
|
|
2016
|
+
},
|
|
2017
|
+
[`${tableNameSingular}One`]: async (_, args, context, info) => {
|
|
2018
|
+
const { filters = [], sort } = args;
|
|
2019
|
+
const { db: db2 } = context;
|
|
2020
|
+
const requestedFields = getRequestedFields(info);
|
|
2021
|
+
let query = db2.from(tableNamePlural).select(requestedFields);
|
|
2022
|
+
query = applyFilters(query, filters);
|
|
2023
|
+
query = applySorting(query, sort);
|
|
2024
|
+
const result = await query.first();
|
|
2025
|
+
return result;
|
|
2026
|
+
},
|
|
2027
|
+
[`${tableNamePlural}Pagination`]: async (_, args, context, info) => {
|
|
2028
|
+
const { limit = 10, page = 0, filters = [], sort } = args;
|
|
2029
|
+
const { db: db2 } = context;
|
|
2030
|
+
let baseQuery = db2(tableNamePlural);
|
|
2031
|
+
baseQuery = applyFilters(baseQuery, filters);
|
|
2032
|
+
const [{ count }] = await baseQuery.clone().count("* as count");
|
|
2033
|
+
const itemCount = Number(count);
|
|
2034
|
+
const pageCount = Math.ceil(itemCount / limit);
|
|
2035
|
+
const currentPage = page;
|
|
2036
|
+
const hasPreviousPage = currentPage > 1;
|
|
2037
|
+
const hasNextPage = currentPage < pageCount - 1;
|
|
2038
|
+
let dataQuery = baseQuery.clone();
|
|
2039
|
+
const requestedFields = getRequestedFields(info);
|
|
2040
|
+
dataQuery = applySorting(dataQuery, sort);
|
|
2041
|
+
if (page > 1) {
|
|
2042
|
+
dataQuery = dataQuery.offset((page - 1) * limit);
|
|
2043
|
+
}
|
|
2044
|
+
const items = await dataQuery.select(requestedFields).limit(limit);
|
|
2231
2045
|
return {
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2046
|
+
pageInfo: {
|
|
2047
|
+
pageCount,
|
|
2048
|
+
itemCount,
|
|
2049
|
+
currentPage,
|
|
2050
|
+
hasPreviousPage,
|
|
2051
|
+
hasNextPage
|
|
2052
|
+
},
|
|
2053
|
+
items
|
|
2235
2054
|
};
|
|
2236
|
-
}
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2055
|
+
},
|
|
2056
|
+
// Add jobStatistics query for jobs table
|
|
2057
|
+
...tableNamePlural === "jobs" ? {
|
|
2058
|
+
jobStatistics: async (_, args, context, info) => {
|
|
2059
|
+
const { user, agent, from, to } = args;
|
|
2060
|
+
const { db: db2 } = context;
|
|
2061
|
+
let query = db2("jobs");
|
|
2062
|
+
if (user) {
|
|
2063
|
+
query = query.where("user", user);
|
|
2064
|
+
}
|
|
2065
|
+
if (agent) {
|
|
2066
|
+
query = query.where("agent", agent);
|
|
2067
|
+
}
|
|
2068
|
+
if (from) {
|
|
2069
|
+
query = query.where("createdAt", ">=", from);
|
|
2070
|
+
}
|
|
2071
|
+
if (to) {
|
|
2072
|
+
query = query.where("createdAt", "<=", to);
|
|
2245
2073
|
}
|
|
2074
|
+
const completedQuery = query.clone().where("status", "completed");
|
|
2075
|
+
const [{ completedCount }] = await completedQuery.count("* as completedCount");
|
|
2076
|
+
const failedQuery = query.clone().where("status", "failed");
|
|
2077
|
+
const [{ failedCount }] = await failedQuery.count("* as failedCount");
|
|
2078
|
+
const durationQuery = query.clone().where("status", "completed").whereNotNull("duration").select(db2.raw('AVG("duration") as averageDuration'));
|
|
2079
|
+
const [{ averageDuration }] = await durationQuery;
|
|
2080
|
+
return {
|
|
2081
|
+
completedCount: Number(completedCount),
|
|
2082
|
+
failedCount: Number(failedCount),
|
|
2083
|
+
averageDuration: averageDuration ? Number(averageDuration) : 0
|
|
2084
|
+
};
|
|
2246
2085
|
}
|
|
2086
|
+
} : {}
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
function createSDL(tables) {
|
|
2090
|
+
let typeDefs = `
|
|
2091
|
+
scalar JSON
|
|
2092
|
+
scalar Date
|
|
2093
|
+
|
|
2094
|
+
type Query {
|
|
2095
|
+
`;
|
|
2096
|
+
let mutationDefs = `
|
|
2097
|
+
type Mutation {
|
|
2098
|
+
`;
|
|
2099
|
+
let modelDefs = "";
|
|
2100
|
+
const resolvers = { JSON: GraphQLJSON, Date: GraphQLDate, Query: {}, Mutation: {} };
|
|
2101
|
+
for (const table of tables) {
|
|
2102
|
+
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2103
|
+
const tableNameSingular = table.name.singular.toLowerCase();
|
|
2104
|
+
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
2105
|
+
typeDefs += `
|
|
2106
|
+
${tableNameSingular}ById(id: ID!): ${tableNameSingular}
|
|
2107
|
+
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
2108
|
+
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
2109
|
+
${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
|
|
2110
|
+
`;
|
|
2111
|
+
mutationDefs += `
|
|
2112
|
+
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!): ${tableNameSingular}
|
|
2113
|
+
${tableNamePlural}UpdateOne(where: JSON!, input: ${tableNameSingular}Input!): ${tableNameSingular}
|
|
2114
|
+
${tableNamePlural}UpdateOneById(id: ID!, input: ${tableNameSingular}Input!): ${tableNameSingular}
|
|
2115
|
+
${tableNamePlural}RemoveOne(where: JSON!): ${tableNameSingular}
|
|
2116
|
+
${tableNamePlural}RemoveOneById(id: ID!): ${tableNameSingular}
|
|
2117
|
+
`;
|
|
2118
|
+
modelDefs += createTypeDefs(table);
|
|
2119
|
+
modelDefs += createFilterTypeDefs(table);
|
|
2120
|
+
modelDefs += `
|
|
2121
|
+
type ${tableNameSingularUpperCaseFirst}PaginationResult {
|
|
2122
|
+
pageInfo: PageInfo!
|
|
2123
|
+
items: [${tableNameSingular}]!
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
type PageInfo {
|
|
2127
|
+
pageCount: Int!
|
|
2128
|
+
itemCount: Int!
|
|
2129
|
+
currentPage: Int!
|
|
2130
|
+
hasPreviousPage: Boolean!
|
|
2131
|
+
hasNextPage: Boolean!
|
|
2132
|
+
}
|
|
2133
|
+
`;
|
|
2134
|
+
if (tableNamePlural === "jobs") {
|
|
2135
|
+
modelDefs += `
|
|
2136
|
+
type JobStatistics {
|
|
2137
|
+
completedCount: Int!
|
|
2138
|
+
failedCount: Int!
|
|
2139
|
+
averageDuration: Float!
|
|
2140
|
+
}
|
|
2141
|
+
`;
|
|
2247
2142
|
}
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
}
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2143
|
+
Object.assign(resolvers.Query, createQueries(table));
|
|
2144
|
+
Object.assign(resolvers.Mutation, createMutations(table));
|
|
2145
|
+
}
|
|
2146
|
+
typeDefs += "}\n";
|
|
2147
|
+
mutationDefs += "}\n";
|
|
2148
|
+
const fullSDL = typeDefs + mutationDefs + modelDefs;
|
|
2149
|
+
const schema = makeExecutableSchema({
|
|
2150
|
+
typeDefs: fullSDL,
|
|
2151
|
+
resolvers
|
|
2152
|
+
});
|
|
2153
|
+
console.log("\n\u{1F4CA} GraphQL Schema Overview\n");
|
|
2154
|
+
const queriesTable = Object.keys(resolvers.Query).map((query) => ({
|
|
2155
|
+
"Operation Type": "Query",
|
|
2156
|
+
"Name": query,
|
|
2157
|
+
"Description": "Retrieves data"
|
|
2158
|
+
}));
|
|
2159
|
+
const mutationsTable = Object.keys(resolvers.Mutation).map((mutation) => ({
|
|
2160
|
+
"Operation Type": "Mutation",
|
|
2161
|
+
"Name": mutation,
|
|
2162
|
+
"Description": "Modifies data"
|
|
2163
|
+
}));
|
|
2164
|
+
const typesTable = tables.flatMap(
|
|
2165
|
+
(table) => table.fields.map((field) => ({
|
|
2166
|
+
"Type": table.name.singular,
|
|
2167
|
+
"Field": field.name,
|
|
2168
|
+
"Field Type": field.type,
|
|
2169
|
+
"Required": field.required ? "Yes" : "No"
|
|
2170
|
+
}))
|
|
2171
|
+
);
|
|
2172
|
+
console.log("\u{1F50D} Operations:");
|
|
2173
|
+
console.table([...queriesTable, ...mutationsTable]);
|
|
2174
|
+
console.log("\n\u{1F4DD} Types and Fields:");
|
|
2175
|
+
console.table(typesTable);
|
|
2176
|
+
console.log("\n");
|
|
2177
|
+
return schema;
|
|
2178
|
+
}
|
|
2179
|
+
var sensitiveFields = ["anthropic_token"];
|
|
2180
|
+
var encryptSensitiveFields = (input) => {
|
|
2181
|
+
sensitiveFields.forEach((field) => {
|
|
2182
|
+
if (input[field]) {
|
|
2183
|
+
input[field] = CryptoJS.AES.encrypt(input[field], process.env.NEXTAUTH_SECRET).toString();
|
|
2288
2184
|
}
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
};
|
|
2292
|
-
}
|
|
2185
|
+
});
|
|
2186
|
+
return input;
|
|
2293
2187
|
};
|
|
2294
2188
|
|
|
2295
2189
|
// src/registry/routes.ts
|
|
2296
|
-
import {
|
|
2190
|
+
import { expressMiddleware } from "@as-integrations/express5";
|
|
2297
2191
|
|
|
2298
|
-
// src/
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
}
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2192
|
+
// src/postgres/core-schema.ts
|
|
2193
|
+
var agentMessagesSchema = {
|
|
2194
|
+
name: {
|
|
2195
|
+
plural: "agent_messages",
|
|
2196
|
+
singular: "agent_message"
|
|
2197
|
+
},
|
|
2198
|
+
fields: [
|
|
2199
|
+
{
|
|
2200
|
+
name: "content",
|
|
2201
|
+
type: "text"
|
|
2202
|
+
},
|
|
2203
|
+
{
|
|
2204
|
+
name: "title",
|
|
2205
|
+
type: "text"
|
|
2206
|
+
},
|
|
2207
|
+
{
|
|
2208
|
+
name: "session",
|
|
2209
|
+
type: "text"
|
|
2316
2210
|
}
|
|
2317
|
-
|
|
2318
|
-
this.queues.push(newQueue);
|
|
2319
|
-
return newQueue;
|
|
2320
|
-
}
|
|
2211
|
+
]
|
|
2321
2212
|
};
|
|
2322
|
-
var
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2213
|
+
var agentSessionsSchema = {
|
|
2214
|
+
name: {
|
|
2215
|
+
plural: "agent_sessions",
|
|
2216
|
+
singular: "agent_session"
|
|
2217
|
+
},
|
|
2218
|
+
fields: [
|
|
2219
|
+
{
|
|
2220
|
+
name: "agent",
|
|
2221
|
+
type: "uuid"
|
|
2222
|
+
},
|
|
2223
|
+
{
|
|
2224
|
+
name: "user",
|
|
2225
|
+
// next auth stores users with id type SERIAL, so we need to use number
|
|
2226
|
+
type: "number"
|
|
2227
|
+
},
|
|
2228
|
+
{
|
|
2229
|
+
name: "title",
|
|
2230
|
+
type: "text"
|
|
2231
|
+
}
|
|
2232
|
+
]
|
|
2332
2233
|
};
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2234
|
+
var usersSchema = {
|
|
2235
|
+
name: {
|
|
2236
|
+
plural: "users",
|
|
2237
|
+
singular: "user"
|
|
2238
|
+
},
|
|
2239
|
+
fields: [
|
|
2240
|
+
{
|
|
2241
|
+
name: "firstname",
|
|
2242
|
+
type: "text"
|
|
2243
|
+
},
|
|
2244
|
+
{
|
|
2245
|
+
name: "name",
|
|
2246
|
+
type: "text"
|
|
2247
|
+
},
|
|
2248
|
+
{
|
|
2249
|
+
name: "lastname",
|
|
2250
|
+
type: "text"
|
|
2251
|
+
},
|
|
2252
|
+
{
|
|
2253
|
+
name: "email",
|
|
2254
|
+
type: "text",
|
|
2255
|
+
index: true
|
|
2256
|
+
},
|
|
2257
|
+
{
|
|
2258
|
+
name: "temporary_token",
|
|
2259
|
+
type: "text"
|
|
2260
|
+
},
|
|
2261
|
+
{
|
|
2262
|
+
name: "type",
|
|
2263
|
+
type: "text",
|
|
2264
|
+
index: true
|
|
2265
|
+
},
|
|
2266
|
+
{
|
|
2267
|
+
name: "profile_image",
|
|
2268
|
+
type: "text"
|
|
2269
|
+
},
|
|
2270
|
+
{
|
|
2271
|
+
name: "super_admin",
|
|
2272
|
+
type: "boolean",
|
|
2273
|
+
default: false
|
|
2274
|
+
},
|
|
2275
|
+
{
|
|
2276
|
+
name: "status",
|
|
2277
|
+
type: "text"
|
|
2278
|
+
},
|
|
2279
|
+
{
|
|
2280
|
+
name: "emailVerified",
|
|
2281
|
+
type: "text"
|
|
2282
|
+
},
|
|
2283
|
+
{
|
|
2284
|
+
name: "apikey",
|
|
2285
|
+
type: "text"
|
|
2286
|
+
},
|
|
2287
|
+
{
|
|
2288
|
+
name: "last_used",
|
|
2289
|
+
type: "date"
|
|
2290
|
+
},
|
|
2291
|
+
{
|
|
2292
|
+
name: "anthropic_token",
|
|
2293
|
+
type: "text"
|
|
2294
|
+
},
|
|
2295
|
+
{
|
|
2296
|
+
name: "role",
|
|
2297
|
+
type: "uuid"
|
|
2352
2298
|
}
|
|
2353
|
-
|
|
2354
|
-
|
|
2299
|
+
]
|
|
2300
|
+
};
|
|
2301
|
+
var rolesSchema = {
|
|
2302
|
+
name: {
|
|
2303
|
+
plural: "roles",
|
|
2304
|
+
singular: "role"
|
|
2305
|
+
},
|
|
2306
|
+
fields: [
|
|
2307
|
+
{
|
|
2308
|
+
name: "name",
|
|
2309
|
+
type: "text"
|
|
2310
|
+
},
|
|
2311
|
+
{
|
|
2312
|
+
name: "is_admin",
|
|
2313
|
+
type: "boolean",
|
|
2314
|
+
default: false
|
|
2315
|
+
},
|
|
2316
|
+
{
|
|
2317
|
+
name: "agents",
|
|
2318
|
+
type: "json"
|
|
2355
2319
|
}
|
|
2356
|
-
|
|
2357
|
-
|
|
2320
|
+
]
|
|
2321
|
+
};
|
|
2322
|
+
var statisticsSchema = {
|
|
2323
|
+
name: {
|
|
2324
|
+
plural: "statistics",
|
|
2325
|
+
singular: "statistic"
|
|
2326
|
+
},
|
|
2327
|
+
fields: [
|
|
2328
|
+
{
|
|
2329
|
+
name: "name",
|
|
2330
|
+
type: "text"
|
|
2331
|
+
},
|
|
2332
|
+
{
|
|
2333
|
+
name: "label",
|
|
2334
|
+
type: "text"
|
|
2335
|
+
},
|
|
2336
|
+
{
|
|
2337
|
+
name: "type",
|
|
2338
|
+
type: "text"
|
|
2339
|
+
},
|
|
2340
|
+
{
|
|
2341
|
+
name: "total",
|
|
2342
|
+
type: "number"
|
|
2358
2343
|
}
|
|
2359
|
-
|
|
2344
|
+
]
|
|
2345
|
+
};
|
|
2346
|
+
var workflowSchema = {
|
|
2347
|
+
name: {
|
|
2348
|
+
plural: "workflows",
|
|
2349
|
+
singular: "workflow"
|
|
2360
2350
|
},
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2351
|
+
fields: [
|
|
2352
|
+
{
|
|
2353
|
+
name: "workflow_name",
|
|
2354
|
+
type: "text"
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
name: "run_id",
|
|
2358
|
+
type: "text"
|
|
2359
|
+
},
|
|
2360
|
+
{
|
|
2361
|
+
name: "snapshot",
|
|
2362
|
+
type: "text"
|
|
2364
2363
|
}
|
|
2365
|
-
|
|
2366
|
-
|
|
2364
|
+
]
|
|
2365
|
+
};
|
|
2366
|
+
var evalResultsSchema = {
|
|
2367
|
+
name: {
|
|
2368
|
+
plural: "eval_results",
|
|
2369
|
+
singular: "eval_result"
|
|
2370
|
+
},
|
|
2371
|
+
fields: [
|
|
2372
|
+
{
|
|
2373
|
+
name: "input",
|
|
2374
|
+
type: "longText"
|
|
2375
|
+
},
|
|
2376
|
+
{
|
|
2377
|
+
name: "output",
|
|
2378
|
+
type: "longText"
|
|
2379
|
+
},
|
|
2380
|
+
{
|
|
2381
|
+
name: "duration",
|
|
2382
|
+
type: "number"
|
|
2383
|
+
},
|
|
2384
|
+
{
|
|
2385
|
+
name: "category",
|
|
2386
|
+
type: "text"
|
|
2387
|
+
},
|
|
2388
|
+
{
|
|
2389
|
+
name: "metadata",
|
|
2390
|
+
type: "json"
|
|
2391
|
+
},
|
|
2392
|
+
{
|
|
2393
|
+
name: "result",
|
|
2394
|
+
type: "number"
|
|
2395
|
+
},
|
|
2396
|
+
{
|
|
2397
|
+
name: "agent_id",
|
|
2398
|
+
type: "uuid"
|
|
2399
|
+
},
|
|
2400
|
+
{
|
|
2401
|
+
name: "workflow_id",
|
|
2402
|
+
type: "uuid"
|
|
2403
|
+
},
|
|
2404
|
+
{
|
|
2405
|
+
name: "eval_type",
|
|
2406
|
+
type: "text"
|
|
2407
|
+
},
|
|
2408
|
+
{
|
|
2409
|
+
name: "eval_name",
|
|
2410
|
+
type: "text"
|
|
2411
|
+
},
|
|
2412
|
+
{
|
|
2413
|
+
name: "comment",
|
|
2414
|
+
type: "longText"
|
|
2367
2415
|
}
|
|
2368
|
-
|
|
2416
|
+
]
|
|
2417
|
+
};
|
|
2418
|
+
var jobsSchema = {
|
|
2419
|
+
name: {
|
|
2420
|
+
plural: "jobs",
|
|
2421
|
+
singular: "job"
|
|
2369
2422
|
},
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2423
|
+
fields: [
|
|
2424
|
+
{
|
|
2425
|
+
name: "redis",
|
|
2426
|
+
type: "text"
|
|
2427
|
+
},
|
|
2428
|
+
{
|
|
2429
|
+
name: "session",
|
|
2430
|
+
type: "text"
|
|
2431
|
+
},
|
|
2432
|
+
{
|
|
2433
|
+
name: "status",
|
|
2434
|
+
type: "text"
|
|
2435
|
+
},
|
|
2436
|
+
{
|
|
2437
|
+
name: "type",
|
|
2438
|
+
type: "text"
|
|
2439
|
+
},
|
|
2440
|
+
{
|
|
2441
|
+
name: "result",
|
|
2442
|
+
type: "longText"
|
|
2443
|
+
},
|
|
2444
|
+
{
|
|
2445
|
+
name: "name",
|
|
2446
|
+
type: "text"
|
|
2447
|
+
},
|
|
2448
|
+
{
|
|
2449
|
+
name: "agent",
|
|
2450
|
+
type: "uuid"
|
|
2451
|
+
},
|
|
2452
|
+
{
|
|
2453
|
+
name: "workflow",
|
|
2454
|
+
type: "uuid"
|
|
2455
|
+
},
|
|
2456
|
+
{
|
|
2457
|
+
name: "user",
|
|
2458
|
+
// next auth stores users with id type SERIAL, so we need to use number
|
|
2459
|
+
type: "number"
|
|
2460
|
+
},
|
|
2461
|
+
{
|
|
2462
|
+
name: "item",
|
|
2463
|
+
type: "uuid"
|
|
2464
|
+
},
|
|
2465
|
+
{
|
|
2466
|
+
name: "steps",
|
|
2467
|
+
type: "number"
|
|
2468
|
+
},
|
|
2469
|
+
{
|
|
2470
|
+
name: "inputs",
|
|
2471
|
+
type: "json"
|
|
2472
|
+
},
|
|
2473
|
+
{
|
|
2474
|
+
name: "finished_at",
|
|
2475
|
+
type: "date"
|
|
2476
|
+
},
|
|
2477
|
+
{
|
|
2478
|
+
name: "duration",
|
|
2479
|
+
type: "number"
|
|
2376
2480
|
}
|
|
2377
|
-
|
|
2378
|
-
}
|
|
2379
|
-
});
|
|
2380
|
-
var map = (field) => {
|
|
2381
|
-
let type;
|
|
2382
|
-
switch (field.type) {
|
|
2383
|
-
case "text":
|
|
2384
|
-
case "shortText":
|
|
2385
|
-
case "longText":
|
|
2386
|
-
case "code":
|
|
2387
|
-
type = "String";
|
|
2388
|
-
break;
|
|
2389
|
-
case "number":
|
|
2390
|
-
type = "Float";
|
|
2391
|
-
break;
|
|
2392
|
-
case "boolean":
|
|
2393
|
-
type = "Boolean";
|
|
2394
|
-
break;
|
|
2395
|
-
case "json":
|
|
2396
|
-
type = "JSON";
|
|
2397
|
-
break;
|
|
2398
|
-
case "date":
|
|
2399
|
-
type = "Date";
|
|
2400
|
-
break;
|
|
2401
|
-
default:
|
|
2402
|
-
type = "String";
|
|
2403
|
-
}
|
|
2404
|
-
return type;
|
|
2405
|
-
};
|
|
2406
|
-
function createTypeDefs(table) {
|
|
2407
|
-
const fields = table.fields.map((field) => {
|
|
2408
|
-
let type;
|
|
2409
|
-
type = map(field);
|
|
2410
|
-
const required = field.required ? "!" : "";
|
|
2411
|
-
return ` ${field.name}: ${type}${required}`;
|
|
2412
|
-
});
|
|
2413
|
-
const typeDef = `
|
|
2414
|
-
type ${table.name.singular} {
|
|
2415
|
-
${fields.join("\n")}
|
|
2416
|
-
id: ID!
|
|
2417
|
-
createdAt: Date!
|
|
2418
|
-
updatedAt: Date!
|
|
2419
|
-
}
|
|
2420
|
-
`;
|
|
2421
|
-
const inputDef = `
|
|
2422
|
-
input ${table.name.singular}Input {
|
|
2423
|
-
${table.fields.map((f) => ` ${f.name}: ${map(f)}`).join("\n")}
|
|
2424
|
-
}
|
|
2425
|
-
`;
|
|
2426
|
-
return typeDef + inputDef;
|
|
2427
|
-
}
|
|
2428
|
-
function createFilterTypeDefs(table) {
|
|
2429
|
-
const fieldFilters = table.fields.map((field) => {
|
|
2430
|
-
let type;
|
|
2431
|
-
type = map(field);
|
|
2432
|
-
return `
|
|
2433
|
-
${field.name}: FilterOperator${type}`;
|
|
2434
|
-
});
|
|
2435
|
-
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
2436
|
-
const operatorTypes = `
|
|
2437
|
-
input FilterOperatorString {
|
|
2438
|
-
eq: String
|
|
2439
|
-
ne: String
|
|
2440
|
-
in: [String]
|
|
2441
|
-
contains: String
|
|
2442
|
-
}
|
|
2443
|
-
|
|
2444
|
-
input FilterOperatorDate {
|
|
2445
|
-
lte: Date
|
|
2446
|
-
gte: Date
|
|
2447
|
-
}
|
|
2448
|
-
|
|
2449
|
-
input FilterOperatorFloat {
|
|
2450
|
-
eq: Float
|
|
2451
|
-
ne: Float
|
|
2452
|
-
in: [Float]
|
|
2453
|
-
}
|
|
2454
|
-
|
|
2455
|
-
input FilterOperatorBoolean {
|
|
2456
|
-
eq: Boolean
|
|
2457
|
-
ne: Boolean
|
|
2458
|
-
in: [Boolean]
|
|
2459
|
-
}
|
|
2460
|
-
|
|
2461
|
-
input FilterOperatorJSON {
|
|
2462
|
-
eq: JSON
|
|
2463
|
-
ne: JSON
|
|
2464
|
-
in: [JSON]
|
|
2465
|
-
}
|
|
2466
|
-
|
|
2467
|
-
input SortBy {
|
|
2468
|
-
field: String!
|
|
2469
|
-
direction: SortDirection!
|
|
2470
|
-
}
|
|
2471
|
-
|
|
2472
|
-
enum SortDirection {
|
|
2473
|
-
ASC
|
|
2474
|
-
DESC
|
|
2475
|
-
}
|
|
2476
|
-
|
|
2477
|
-
input Filter${tableNameSingularUpperCaseFirst} {
|
|
2478
|
-
${fieldFilters.join("\n")}
|
|
2479
|
-
}`;
|
|
2480
|
-
return operatorTypes;
|
|
2481
|
-
}
|
|
2482
|
-
var getRequestedFields = (info) => {
|
|
2483
|
-
const selections = info.operation.selectionSet.selections[0].selectionSet.selections;
|
|
2484
|
-
const itemsSelection = selections.find((s) => s.name.value === "items");
|
|
2485
|
-
const fields = itemsSelection ? Object.keys(itemsSelection.selectionSet.selections.reduce((acc, field) => {
|
|
2486
|
-
acc[field.name.value] = true;
|
|
2487
|
-
return acc;
|
|
2488
|
-
}, {})) : Object.keys(selections.reduce((acc, field) => {
|
|
2489
|
-
acc[field.name.value] = true;
|
|
2490
|
-
return acc;
|
|
2491
|
-
}, {}));
|
|
2492
|
-
return fields.filter((field) => field !== "pageInfo" && field !== "items");
|
|
2481
|
+
]
|
|
2493
2482
|
};
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
const results = await db2(tableNamePlural).insert({
|
|
2504
|
-
...input,
|
|
2505
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
2506
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
2507
|
-
}).returning(requestedFields);
|
|
2508
|
-
return results[0];
|
|
2509
|
-
},
|
|
2510
|
-
[`${tableNamePlural}UpdateOne`]: async (_, args, context, info) => {
|
|
2511
|
-
const { db: db2 } = context;
|
|
2512
|
-
let { where, input } = args;
|
|
2513
|
-
input = encryptSensitiveFields(input);
|
|
2514
|
-
await db2(tableNamePlural).where(where).update({
|
|
2515
|
-
...input,
|
|
2516
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
2517
|
-
});
|
|
2518
|
-
const requestedFields = getRequestedFields(info);
|
|
2519
|
-
const result = await db2.from(tableNamePlural).select(requestedFields).where(where).first();
|
|
2520
|
-
return result;
|
|
2483
|
+
var agentsSchema = {
|
|
2484
|
+
name: {
|
|
2485
|
+
plural: "agents",
|
|
2486
|
+
singular: "agent"
|
|
2487
|
+
},
|
|
2488
|
+
fields: [
|
|
2489
|
+
{
|
|
2490
|
+
name: "name",
|
|
2491
|
+
type: "text"
|
|
2521
2492
|
},
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
const { db: db2 } = context;
|
|
2526
|
-
await db2(tableNamePlural).where({ id }).update({
|
|
2527
|
-
...input,
|
|
2528
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
2529
|
-
});
|
|
2530
|
-
const requestedFields = getRequestedFields(info);
|
|
2531
|
-
const result = await db2.from(tableNamePlural).select(requestedFields).where({ id }).first();
|
|
2532
|
-
return result;
|
|
2493
|
+
{
|
|
2494
|
+
name: "description",
|
|
2495
|
+
type: "text"
|
|
2533
2496
|
},
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
const requestedFields = getRequestedFields(info);
|
|
2538
|
-
const result = await db2.from(tableNamePlural).select(requestedFields).where(where).first();
|
|
2539
|
-
await db2(tableNamePlural).where(where).del();
|
|
2540
|
-
return result;
|
|
2497
|
+
{
|
|
2498
|
+
name: "extensions",
|
|
2499
|
+
type: "json"
|
|
2541
2500
|
},
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
const requestedFields = getRequestedFields(info);
|
|
2546
|
-
const result = await db2.from(tableNamePlural).select(requestedFields).where({ id }).first();
|
|
2547
|
-
await db2(tableNamePlural).where({ id }).del();
|
|
2548
|
-
return result;
|
|
2549
|
-
}
|
|
2550
|
-
};
|
|
2551
|
-
}
|
|
2552
|
-
function createQueries(table) {
|
|
2553
|
-
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2554
|
-
const tableNameSingular = table.name.singular.toLowerCase();
|
|
2555
|
-
const applyFilters = (query, filters) => {
|
|
2556
|
-
filters.forEach((filter) => {
|
|
2557
|
-
Object.entries(filter).forEach(([fieldName, operators]) => {
|
|
2558
|
-
if (operators) {
|
|
2559
|
-
if (operators.eq !== void 0) {
|
|
2560
|
-
query = query.where(fieldName, operators.eq);
|
|
2561
|
-
}
|
|
2562
|
-
if (operators.ne !== void 0) {
|
|
2563
|
-
query = query.whereRaw(`?? IS DISTINCT FROM ?`, [fieldName, operators.ne]);
|
|
2564
|
-
}
|
|
2565
|
-
if (operators.in !== void 0) {
|
|
2566
|
-
query = query.whereIn(fieldName, operators.in);
|
|
2567
|
-
}
|
|
2568
|
-
if (operators.contains !== void 0) {
|
|
2569
|
-
query = query.where(fieldName, "like", `%${operators.contains}%`);
|
|
2570
|
-
}
|
|
2571
|
-
}
|
|
2572
|
-
});
|
|
2573
|
-
});
|
|
2574
|
-
return query;
|
|
2575
|
-
};
|
|
2576
|
-
const applySorting = (query, sort) => {
|
|
2577
|
-
if (sort) {
|
|
2578
|
-
query = query.orderBy(sort.field, sort.direction.toLowerCase());
|
|
2579
|
-
}
|
|
2580
|
-
return query;
|
|
2581
|
-
};
|
|
2582
|
-
return {
|
|
2583
|
-
[`${tableNameSingular}ById`]: async (_, args, context, info) => {
|
|
2584
|
-
const { db: db2 } = context;
|
|
2585
|
-
const requestedFields = getRequestedFields(info);
|
|
2586
|
-
const result = await db2.from(tableNamePlural).select(requestedFields).where({ id: args.id }).first();
|
|
2587
|
-
return result;
|
|
2501
|
+
{
|
|
2502
|
+
name: "backend",
|
|
2503
|
+
type: "text"
|
|
2588
2504
|
},
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
const requestedFields = getRequestedFields(info);
|
|
2593
|
-
let query = db2.from(tableNamePlural).select(requestedFields);
|
|
2594
|
-
query = applyFilters(query, filters);
|
|
2595
|
-
query = applySorting(query, sort);
|
|
2596
|
-
const result = await query.first();
|
|
2597
|
-
return result;
|
|
2505
|
+
{
|
|
2506
|
+
name: "type",
|
|
2507
|
+
type: "text"
|
|
2598
2508
|
},
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
baseQuery = applyFilters(baseQuery, filters);
|
|
2604
|
-
const [{ count }] = await baseQuery.clone().count("* as count");
|
|
2605
|
-
const itemCount = Number(count);
|
|
2606
|
-
const pageCount = Math.ceil(itemCount / limit);
|
|
2607
|
-
const currentPage = page;
|
|
2608
|
-
const hasPreviousPage = currentPage > 1;
|
|
2609
|
-
const hasNextPage = currentPage < pageCount - 1;
|
|
2610
|
-
let dataQuery = baseQuery.clone();
|
|
2611
|
-
const requestedFields = getRequestedFields(info);
|
|
2612
|
-
dataQuery = applySorting(dataQuery, sort);
|
|
2613
|
-
if (page > 1) {
|
|
2614
|
-
dataQuery = dataQuery.offset((page - 1) * limit);
|
|
2615
|
-
}
|
|
2616
|
-
const items = await dataQuery.select(requestedFields).limit(limit);
|
|
2617
|
-
return {
|
|
2618
|
-
pageInfo: {
|
|
2619
|
-
pageCount,
|
|
2620
|
-
itemCount,
|
|
2621
|
-
currentPage,
|
|
2622
|
-
hasPreviousPage,
|
|
2623
|
-
hasNextPage
|
|
2624
|
-
},
|
|
2625
|
-
items
|
|
2626
|
-
};
|
|
2509
|
+
{
|
|
2510
|
+
name: "active",
|
|
2511
|
+
type: "boolean",
|
|
2512
|
+
default: false
|
|
2627
2513
|
},
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
}
|
|
2637
|
-
if (agent) {
|
|
2638
|
-
query = query.where("agent", agent);
|
|
2639
|
-
}
|
|
2640
|
-
if (from) {
|
|
2641
|
-
query = query.where("createdAt", ">=", from);
|
|
2642
|
-
}
|
|
2643
|
-
if (to) {
|
|
2644
|
-
query = query.where("createdAt", "<=", to);
|
|
2645
|
-
}
|
|
2646
|
-
const completedQuery = query.clone().where("status", "completed");
|
|
2647
|
-
const [{ completedCount }] = await completedQuery.count("* as completedCount");
|
|
2648
|
-
const failedQuery = query.clone().where("status", "failed");
|
|
2649
|
-
const [{ failedCount }] = await failedQuery.count("* as failedCount");
|
|
2650
|
-
const durationQuery = query.clone().where("status", "completed").whereNotNull("duration").select(db2.raw('AVG("duration") as averageDuration'));
|
|
2651
|
-
const [{ averageDuration }] = await durationQuery;
|
|
2652
|
-
return {
|
|
2653
|
-
completedCount: Number(completedCount),
|
|
2654
|
-
failedCount: Number(failedCount),
|
|
2655
|
-
averageDuration: averageDuration ? Number(averageDuration) : 0
|
|
2656
|
-
};
|
|
2657
|
-
}
|
|
2658
|
-
} : {}
|
|
2659
|
-
};
|
|
2660
|
-
}
|
|
2661
|
-
function createSDL(tables) {
|
|
2662
|
-
let typeDefs = `
|
|
2663
|
-
scalar JSON
|
|
2664
|
-
scalar Date
|
|
2665
|
-
|
|
2666
|
-
type Query {
|
|
2667
|
-
`;
|
|
2668
|
-
let mutationDefs = `
|
|
2669
|
-
type Mutation {
|
|
2670
|
-
`;
|
|
2671
|
-
let modelDefs = "";
|
|
2672
|
-
const resolvers = { JSON: GraphQLJSON, Date: GraphQLDate, Query: {}, Mutation: {} };
|
|
2673
|
-
for (const table of tables) {
|
|
2674
|
-
const tableNamePlural = table.name.plural.toLowerCase();
|
|
2675
|
-
const tableNameSingular = table.name.singular.toLowerCase();
|
|
2676
|
-
const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
|
|
2677
|
-
typeDefs += `
|
|
2678
|
-
${tableNameSingular}ById(id: ID!): ${tableNameSingular}
|
|
2679
|
-
${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
|
|
2680
|
-
${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
|
|
2681
|
-
${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
|
|
2682
|
-
`;
|
|
2683
|
-
mutationDefs += `
|
|
2684
|
-
${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!): ${tableNameSingular}
|
|
2685
|
-
${tableNamePlural}UpdateOne(where: JSON!, input: ${tableNameSingular}Input!): ${tableNameSingular}
|
|
2686
|
-
${tableNamePlural}UpdateOneById(id: ID!, input: ${tableNameSingular}Input!): ${tableNameSingular}
|
|
2687
|
-
${tableNamePlural}RemoveOne(where: JSON!): ${tableNameSingular}
|
|
2688
|
-
${tableNamePlural}RemoveOneById(id: ID!): ${tableNameSingular}
|
|
2689
|
-
`;
|
|
2690
|
-
modelDefs += createTypeDefs(table);
|
|
2691
|
-
modelDefs += createFilterTypeDefs(table);
|
|
2692
|
-
modelDefs += `
|
|
2693
|
-
type ${tableNameSingularUpperCaseFirst}PaginationResult {
|
|
2694
|
-
pageInfo: PageInfo!
|
|
2695
|
-
items: [${tableNameSingular}]!
|
|
2696
|
-
}
|
|
2697
|
-
|
|
2698
|
-
type PageInfo {
|
|
2699
|
-
pageCount: Int!
|
|
2700
|
-
itemCount: Int!
|
|
2701
|
-
currentPage: Int!
|
|
2702
|
-
hasPreviousPage: Boolean!
|
|
2703
|
-
hasNextPage: Boolean!
|
|
2704
|
-
}
|
|
2705
|
-
`;
|
|
2706
|
-
if (tableNamePlural === "jobs") {
|
|
2707
|
-
modelDefs += `
|
|
2708
|
-
type JobStatistics {
|
|
2709
|
-
completedCount: Int!
|
|
2710
|
-
failedCount: Int!
|
|
2711
|
-
averageDuration: Float!
|
|
2712
|
-
}
|
|
2713
|
-
`;
|
|
2714
|
-
}
|
|
2715
|
-
Object.assign(resolvers.Query, createQueries(table));
|
|
2716
|
-
Object.assign(resolvers.Mutation, createMutations(table));
|
|
2717
|
-
}
|
|
2718
|
-
typeDefs += "}\n";
|
|
2719
|
-
mutationDefs += "}\n";
|
|
2720
|
-
const fullSDL = typeDefs + mutationDefs + modelDefs;
|
|
2721
|
-
const schema = makeExecutableSchema({
|
|
2722
|
-
typeDefs: fullSDL,
|
|
2723
|
-
resolvers
|
|
2724
|
-
});
|
|
2725
|
-
console.log("\n\u{1F4CA} GraphQL Schema Overview\n");
|
|
2726
|
-
const queriesTable = Object.keys(resolvers.Query).map((query) => ({
|
|
2727
|
-
"Operation Type": "Query",
|
|
2728
|
-
"Name": query,
|
|
2729
|
-
"Description": "Retrieves data"
|
|
2730
|
-
}));
|
|
2731
|
-
const mutationsTable = Object.keys(resolvers.Mutation).map((mutation) => ({
|
|
2732
|
-
"Operation Type": "Mutation",
|
|
2733
|
-
"Name": mutation,
|
|
2734
|
-
"Description": "Modifies data"
|
|
2735
|
-
}));
|
|
2736
|
-
const typesTable = tables.flatMap(
|
|
2737
|
-
(table) => table.fields.map((field) => ({
|
|
2738
|
-
"Type": table.name.singular,
|
|
2739
|
-
"Field": field.name,
|
|
2740
|
-
"Field Type": field.type,
|
|
2741
|
-
"Required": field.required ? "Yes" : "No"
|
|
2742
|
-
}))
|
|
2743
|
-
);
|
|
2744
|
-
console.log("\u{1F50D} Operations:");
|
|
2745
|
-
console.table([...queriesTable, ...mutationsTable]);
|
|
2746
|
-
console.log("\n\u{1F4DD} Types and Fields:");
|
|
2747
|
-
console.table(typesTable);
|
|
2748
|
-
console.log("\n");
|
|
2749
|
-
return schema;
|
|
2750
|
-
}
|
|
2751
|
-
var sensitiveFields = ["anthropic_token"];
|
|
2752
|
-
var encryptSensitiveFields = (input) => {
|
|
2753
|
-
sensitiveFields.forEach((field) => {
|
|
2754
|
-
if (input[field]) {
|
|
2755
|
-
input[field] = CryptoJS.AES.encrypt(input[field], process.env.NEXTAUTH_SECRET).toString();
|
|
2514
|
+
{
|
|
2515
|
+
name: "public",
|
|
2516
|
+
type: "boolean",
|
|
2517
|
+
default: false
|
|
2518
|
+
},
|
|
2519
|
+
{
|
|
2520
|
+
name: "tools",
|
|
2521
|
+
type: "json"
|
|
2756
2522
|
}
|
|
2757
|
-
|
|
2758
|
-
return input;
|
|
2523
|
+
]
|
|
2759
2524
|
};
|
|
2760
2525
|
|
|
2761
|
-
// src/registry/routes.ts
|
|
2762
|
-
import { expressMiddleware } from "@as-integrations/express5";
|
|
2763
|
-
|
|
2764
2526
|
// src/registry/uppy.ts
|
|
2765
2527
|
import "express";
|
|
2766
2528
|
var createUppyRoutes = async (app) => {
|
|
@@ -4342,14 +4104,14 @@ var preprocessInputs = async (data) => {
|
|
|
4342
4104
|
return data;
|
|
4343
4105
|
};
|
|
4344
4106
|
var getPresignedFileUrl = async (key) => {
|
|
4345
|
-
if (!process.env.
|
|
4346
|
-
throw new Error("Missing process.env.
|
|
4107
|
+
if (!process.env.NEXT_BACKEND) {
|
|
4108
|
+
throw new Error("Missing process.env.NEXT_BACKEND");
|
|
4347
4109
|
}
|
|
4348
4110
|
if (!process.env.INTERNAL_SECRET) {
|
|
4349
|
-
throw new Error("Missing process.env.
|
|
4111
|
+
throw new Error("Missing process.env.NEXT_BACKEND");
|
|
4350
4112
|
}
|
|
4351
4113
|
console.log(`[EXULU] fetching presigned url for file with key: ${key}`);
|
|
4352
|
-
let url = `${process.env.
|
|
4114
|
+
let url = `${process.env.NEXT_BACKEND}/s3/download?key=${key}`;
|
|
4353
4115
|
const response = await fetch(url, {
|
|
4354
4116
|
method: "GET",
|
|
4355
4117
|
headers: {
|
|
@@ -4899,6 +4661,259 @@ var getTicket = new ExuluTool({
|
|
|
4899
4661
|
}
|
|
4900
4662
|
});
|
|
4901
4663
|
|
|
4664
|
+
// src/auth/generate-key.ts
|
|
4665
|
+
import bcrypt2 from "bcryptjs";
|
|
4666
|
+
var SALT_ROUNDS = 12;
|
|
4667
|
+
async function encryptString(string) {
|
|
4668
|
+
const hash = await bcrypt2.hash(string, SALT_ROUNDS);
|
|
4669
|
+
return hash;
|
|
4670
|
+
}
|
|
4671
|
+
var generateApiKey = async (name, email) => {
|
|
4672
|
+
const { db: db2 } = await postgresClient();
|
|
4673
|
+
console.log("[EXULU] Inserting default user and admin role.");
|
|
4674
|
+
const existingRole = await db2.from("roles").where({ name: "admin" }).first();
|
|
4675
|
+
let roleId;
|
|
4676
|
+
if (!existingRole) {
|
|
4677
|
+
console.log("[EXULU] Creating default admin role.");
|
|
4678
|
+
const role = await db2.from("roles").insert({
|
|
4679
|
+
name: "admin",
|
|
4680
|
+
is_admin: true,
|
|
4681
|
+
agents: []
|
|
4682
|
+
}).returning("id");
|
|
4683
|
+
roleId = role[0].id;
|
|
4684
|
+
} else {
|
|
4685
|
+
roleId = existingRole.id;
|
|
4686
|
+
}
|
|
4687
|
+
const newKeyName = name;
|
|
4688
|
+
const plainKey = `sk_${Math.random().toString(36).substring(2, 15)}_${Math.random().toString(36).substring(2, 15)}`;
|
|
4689
|
+
const postFix = `/${newKeyName.toLowerCase().trim().replaceAll(" ", "_")}`;
|
|
4690
|
+
const encryptedKey = await encryptString(plainKey);
|
|
4691
|
+
const existingApiUser = await db2.from("users").where({ email }).first();
|
|
4692
|
+
if (!existingApiUser) {
|
|
4693
|
+
console.log("[EXULU] Creating default api user.");
|
|
4694
|
+
await db2.from("users").insert({
|
|
4695
|
+
name,
|
|
4696
|
+
email,
|
|
4697
|
+
super_admin: true,
|
|
4698
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
4699
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
4700
|
+
type: "api",
|
|
4701
|
+
emailVerified: /* @__PURE__ */ new Date(),
|
|
4702
|
+
apikey: `${encryptedKey}${postFix}`,
|
|
4703
|
+
// password: "admin", todo add this again when we implement password auth / encryption as alternative to OTP
|
|
4704
|
+
role: roleId
|
|
4705
|
+
});
|
|
4706
|
+
console.log("[EXULU] Default api user created. Key: ", `${plainKey}${postFix}`);
|
|
4707
|
+
} else {
|
|
4708
|
+
console.log("[EXULU] API user with that name already exists.");
|
|
4709
|
+
}
|
|
4710
|
+
console.log("[EXULU] Key generated, copy and use the plain key from here, you will not be able to access it again.");
|
|
4711
|
+
console.log("[EXULU] Key: ", `${plainKey}${postFix}`);
|
|
4712
|
+
return {
|
|
4713
|
+
key: `${plainKey}${postFix}`
|
|
4714
|
+
};
|
|
4715
|
+
};
|
|
4716
|
+
|
|
4717
|
+
// src/postgres/init-db.ts
|
|
4718
|
+
var up = async function(knex) {
|
|
4719
|
+
if (!await knex.schema.hasTable("agent_sessions")) {
|
|
4720
|
+
console.log("[EXULU] Creating agent_sessions table.");
|
|
4721
|
+
await knex.schema.createTable("agent_sessions", (table) => {
|
|
4722
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
4723
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
4724
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
4725
|
+
for (const field of agentSessionsSchema.fields) {
|
|
4726
|
+
const { type, name, default: defaultValue } = field;
|
|
4727
|
+
if (!type || !name) {
|
|
4728
|
+
continue;
|
|
4729
|
+
}
|
|
4730
|
+
mapType(table, type, sanitizeName(name), defaultValue);
|
|
4731
|
+
}
|
|
4732
|
+
});
|
|
4733
|
+
}
|
|
4734
|
+
if (!await knex.schema.hasTable("agent_messages")) {
|
|
4735
|
+
console.log("[EXULU] Creating agent_messages table.");
|
|
4736
|
+
await knex.schema.createTable("agent_messages", (table) => {
|
|
4737
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
4738
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
4739
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
4740
|
+
for (const field of agentMessagesSchema.fields) {
|
|
4741
|
+
const { type, name, default: defaultValue } = field;
|
|
4742
|
+
if (!type || !name) {
|
|
4743
|
+
continue;
|
|
4744
|
+
}
|
|
4745
|
+
mapType(table, type, sanitizeName(name), defaultValue);
|
|
4746
|
+
}
|
|
4747
|
+
});
|
|
4748
|
+
}
|
|
4749
|
+
if (!await knex.schema.hasTable("roles")) {
|
|
4750
|
+
console.log("[EXULU] Creating roles table.");
|
|
4751
|
+
await knex.schema.createTable("roles", (table) => {
|
|
4752
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
4753
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
4754
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
4755
|
+
for (const field of rolesSchema.fields) {
|
|
4756
|
+
const { type, name, default: defaultValue } = field;
|
|
4757
|
+
if (!type || !name) {
|
|
4758
|
+
continue;
|
|
4759
|
+
}
|
|
4760
|
+
mapType(table, type, sanitizeName(name), defaultValue);
|
|
4761
|
+
}
|
|
4762
|
+
});
|
|
4763
|
+
}
|
|
4764
|
+
if (!await knex.schema.hasTable("eval_results")) {
|
|
4765
|
+
console.log("[EXULU] Creating eval_results table.");
|
|
4766
|
+
await knex.schema.createTable("eval_results", (table) => {
|
|
4767
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
4768
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
4769
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
4770
|
+
for (const field of evalResultsSchema.fields) {
|
|
4771
|
+
const { type, name, default: defaultValue } = field;
|
|
4772
|
+
if (!type || !name) {
|
|
4773
|
+
continue;
|
|
4774
|
+
}
|
|
4775
|
+
mapType(table, type, sanitizeName(name), defaultValue);
|
|
4776
|
+
}
|
|
4777
|
+
});
|
|
4778
|
+
}
|
|
4779
|
+
if (!await knex.schema.hasTable("statistics")) {
|
|
4780
|
+
console.log("[EXULU] Creating statistics table.");
|
|
4781
|
+
await knex.schema.createTable("statistics", (table) => {
|
|
4782
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
4783
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
4784
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
4785
|
+
for (const field of statisticsSchema.fields) {
|
|
4786
|
+
const { type, name, default: defaultValue } = field;
|
|
4787
|
+
if (!type || !name) {
|
|
4788
|
+
continue;
|
|
4789
|
+
}
|
|
4790
|
+
mapType(table, type, sanitizeName(name), defaultValue);
|
|
4791
|
+
}
|
|
4792
|
+
});
|
|
4793
|
+
}
|
|
4794
|
+
if (!await knex.schema.hasTable("jobs")) {
|
|
4795
|
+
console.log("[EXULU] Creating jobs table.");
|
|
4796
|
+
await knex.schema.createTable("jobs", (table) => {
|
|
4797
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
4798
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
4799
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
4800
|
+
for (const field of jobsSchema.fields) {
|
|
4801
|
+
const { type, name, default: defaultValue } = field;
|
|
4802
|
+
if (!type || !name) {
|
|
4803
|
+
continue;
|
|
4804
|
+
}
|
|
4805
|
+
mapType(table, type, sanitizeName(name), defaultValue);
|
|
4806
|
+
}
|
|
4807
|
+
});
|
|
4808
|
+
}
|
|
4809
|
+
if (!await knex.schema.hasTable("agents")) {
|
|
4810
|
+
console.log("[EXULU] Creating agents table.");
|
|
4811
|
+
await knex.schema.createTable("agents", (table) => {
|
|
4812
|
+
table.uuid("id").primary().defaultTo(knex.fn.uuid());
|
|
4813
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
4814
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
4815
|
+
for (const field of agentsSchema.fields) {
|
|
4816
|
+
const { type, name, default: defaultValue } = field;
|
|
4817
|
+
if (!type || !name) {
|
|
4818
|
+
continue;
|
|
4819
|
+
}
|
|
4820
|
+
mapType(table, type, sanitizeName(name), defaultValue);
|
|
4821
|
+
}
|
|
4822
|
+
});
|
|
4823
|
+
}
|
|
4824
|
+
if (!await knex.schema.hasTable("verification_token")) {
|
|
4825
|
+
console.log("[EXULU] Creating verification_token table.");
|
|
4826
|
+
await knex.schema.createTable("verification_token", (table) => {
|
|
4827
|
+
table.text("identifier").notNullable();
|
|
4828
|
+
table.timestamp("expires", { useTz: true }).notNullable();
|
|
4829
|
+
table.text("token").notNullable();
|
|
4830
|
+
table.primary(["identifier", "token"]);
|
|
4831
|
+
});
|
|
4832
|
+
}
|
|
4833
|
+
if (!await knex.schema.hasTable("users")) {
|
|
4834
|
+
console.log("[EXULU] Creating users table.");
|
|
4835
|
+
await knex.schema.createTable("users", (table) => {
|
|
4836
|
+
table.increments("id").primary();
|
|
4837
|
+
table.timestamp("createdAt").defaultTo(knex.fn.now());
|
|
4838
|
+
table.timestamp("updatedAt").defaultTo(knex.fn.now());
|
|
4839
|
+
table.string("name", 255);
|
|
4840
|
+
table.string("password", 255);
|
|
4841
|
+
table.string("email", 255);
|
|
4842
|
+
table.timestamp("emailVerified", { useTz: true });
|
|
4843
|
+
table.text("image");
|
|
4844
|
+
for (const field of usersSchema.fields) {
|
|
4845
|
+
console.log("[EXULU] field", field);
|
|
4846
|
+
const { type, name, default: defaultValue } = field;
|
|
4847
|
+
if (name === "id" || name === "name" || name === "email" || name === "emailVerified" || name === "image") {
|
|
4848
|
+
continue;
|
|
4849
|
+
}
|
|
4850
|
+
if (!type || !name) {
|
|
4851
|
+
continue;
|
|
4852
|
+
}
|
|
4853
|
+
mapType(table, type, sanitizeName(name), defaultValue);
|
|
4854
|
+
}
|
|
4855
|
+
});
|
|
4856
|
+
}
|
|
4857
|
+
if (!await knex.schema.hasTable("accounts")) {
|
|
4858
|
+
console.log("[EXULU] Creating accounts table.");
|
|
4859
|
+
await knex.schema.createTable("accounts", (table) => {
|
|
4860
|
+
table.increments("id").primary();
|
|
4861
|
+
table.integer("userId").notNullable();
|
|
4862
|
+
table.string("type", 255).notNullable();
|
|
4863
|
+
table.string("provider", 255).notNullable();
|
|
4864
|
+
table.string("providerAccountId", 255).notNullable();
|
|
4865
|
+
table.text("refresh_token");
|
|
4866
|
+
table.text("access_token");
|
|
4867
|
+
table.bigInteger("expires_at");
|
|
4868
|
+
table.text("id_token");
|
|
4869
|
+
table.text("scope");
|
|
4870
|
+
table.text("session_state");
|
|
4871
|
+
table.text("token_type");
|
|
4872
|
+
});
|
|
4873
|
+
}
|
|
4874
|
+
};
|
|
4875
|
+
var execute = async () => {
|
|
4876
|
+
const { db: db2 } = await postgresClient();
|
|
4877
|
+
console.log("[EXULU] Checking Exulu IMP database status.");
|
|
4878
|
+
await up(db2);
|
|
4879
|
+
console.log("[EXULU] Inserting default user and admin role.");
|
|
4880
|
+
const existingRole = await db2.from("roles").where({ name: "admin" }).first();
|
|
4881
|
+
let roleId;
|
|
4882
|
+
if (!existingRole) {
|
|
4883
|
+
console.log("[EXULU] Creating default admin role.");
|
|
4884
|
+
const role = await db2.from("roles").insert({
|
|
4885
|
+
name: "admin",
|
|
4886
|
+
is_admin: true,
|
|
4887
|
+
agents: JSON.stringify([])
|
|
4888
|
+
}).returning("id");
|
|
4889
|
+
roleId = role[0].id;
|
|
4890
|
+
} else {
|
|
4891
|
+
roleId = existingRole.id;
|
|
4892
|
+
}
|
|
4893
|
+
const existingUser = await db2.from("users").where({ email: "admin@exulu.com" }).first();
|
|
4894
|
+
if (!existingUser) {
|
|
4895
|
+
const password = await encryptString("admin");
|
|
4896
|
+
console.log("[EXULU] Creating default admin user.");
|
|
4897
|
+
await db2.from("users").insert({
|
|
4898
|
+
name: "exulu",
|
|
4899
|
+
email: "admin@exulu.com",
|
|
4900
|
+
super_admin: true,
|
|
4901
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
4902
|
+
emailVerified: /* @__PURE__ */ new Date(),
|
|
4903
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
4904
|
+
password,
|
|
4905
|
+
type: "user",
|
|
4906
|
+
role: roleId
|
|
4907
|
+
});
|
|
4908
|
+
}
|
|
4909
|
+
const { key } = await generateApiKey("exulu", "api@exulu.com");
|
|
4910
|
+
console.log("[EXULU] Database initialized.");
|
|
4911
|
+
console.log("[EXULU] Default api key: ", `${key}`);
|
|
4912
|
+
console.log("[EXULU] Default password if using password auth: ", `admin`);
|
|
4913
|
+
console.log("[EXULU] Default email if using password auth: ", `admin@exulu.com`);
|
|
4914
|
+
return;
|
|
4915
|
+
};
|
|
4916
|
+
|
|
4902
4917
|
// src/registry/index.ts
|
|
4903
4918
|
var ExuluApp = class {
|
|
4904
4919
|
_agents = [];
|
|
@@ -4913,6 +4928,7 @@ var ExuluApp = class {
|
|
|
4913
4928
|
// Factory function so we can async
|
|
4914
4929
|
// initialize the MCP server if needed.
|
|
4915
4930
|
create = async ({ contexts, agents, workflows, config, tools }) => {
|
|
4931
|
+
await execute();
|
|
4916
4932
|
this._workflows = workflows ?? [];
|
|
4917
4933
|
this._contexts = contexts ?? {};
|
|
4918
4934
|
this._agents = [
|
|
@@ -6228,14 +6244,6 @@ var ExuluChunkers = {
|
|
|
6228
6244
|
rules: RecursiveRules
|
|
6229
6245
|
}
|
|
6230
6246
|
};
|
|
6231
|
-
var ExuluDatabase = {
|
|
6232
|
-
init: async () => {
|
|
6233
|
-
await execute();
|
|
6234
|
-
},
|
|
6235
|
-
generateApiKey: async (name, email) => {
|
|
6236
|
-
return await generateApiKey(name, email);
|
|
6237
|
-
}
|
|
6238
|
-
};
|
|
6239
6247
|
export {
|
|
6240
6248
|
JOB_STATUS_ENUM as EXULU_JOB_STATUS_ENUM,
|
|
6241
6249
|
STATISTICS_TYPE_ENUM as EXULU_STATISTICS_TYPE_ENUM,
|
|
@@ -6244,7 +6252,6 @@ export {
|
|
|
6244
6252
|
authentication as ExuluAuthentication,
|
|
6245
6253
|
ExuluChunkers,
|
|
6246
6254
|
ExuluContext,
|
|
6247
|
-
ExuluDatabase,
|
|
6248
6255
|
ExuluEmbedder,
|
|
6249
6256
|
ExuluEval,
|
|
6250
6257
|
ExuluJobs,
|