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