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