@exulu/backend 1.13.0 → 1.14.1

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.cjs CHANGED
@@ -121,15 +121,15 @@ var import_ai = require("ai");
121
121
 
122
122
  // types/enums/statistics.ts
123
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"
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
133
  };
134
134
 
135
135
  // types/enums/eval-types.ts
@@ -304,58 +304,81 @@ var bullmqDecorator = async ({
304
304
 
305
305
  // src/registry/utils/map-types.ts
306
306
  var mapType = (t, type, name, defaultValue, unique) => {
307
- if (type === "text") {
308
- t.text(name);
307
+ if (type === "text" || type === "enum") {
308
+ if (defaultValue) {
309
+ t.text(name).defaultTo(defaultValue);
310
+ } else {
311
+ t.text(name);
312
+ }
309
313
  if (unique) t.unique(name);
310
- if (defaultValue) t.defaultTo(defaultValue);
311
314
  return;
312
315
  }
313
316
  if (type === "longText") {
314
- t.text(name);
317
+ if (defaultValue) {
318
+ t.text(name).defaultTo(defaultValue);
319
+ } else {
320
+ t.text(name);
321
+ }
315
322
  if (unique) t.unique(name);
316
- if (defaultValue) t.defaultTo(defaultValue);
317
323
  return;
318
324
  }
319
325
  if (type === "shortText") {
320
- t.string(name, 100);
326
+ if (defaultValue) {
327
+ t.string(name, 100).defaultTo(defaultValue);
328
+ } else {
329
+ t.string(name, 100);
330
+ }
321
331
  if (unique) t.unique(name);
322
- if (defaultValue) t.defaultTo(defaultValue);
323
332
  return;
324
333
  }
325
334
  if (type === "number") {
326
- t.float(name);
335
+ if (defaultValue) {
336
+ t.float(name).defaultTo(defaultValue);
337
+ } else {
338
+ t.float(name);
339
+ }
327
340
  if (unique) t.unique(name);
328
- if (defaultValue) t.defaultTo(defaultValue);
329
341
  return;
330
342
  }
331
343
  if (type === "boolean") {
332
344
  t.boolean(name).defaultTo(defaultValue || false);
333
345
  if (unique) t.unique(name);
334
- if (defaultValue) t.defaultTo(defaultValue);
335
346
  return;
336
347
  }
337
348
  if (type === "code") {
338
- t.text(name);
349
+ if (defaultValue) {
350
+ t.text(name).defaultTo(defaultValue);
351
+ } else {
352
+ t.text(name);
353
+ }
339
354
  if (unique) t.unique(name);
340
- if (defaultValue) t.defaultTo(defaultValue);
341
355
  return;
342
356
  }
343
357
  if (type === "json") {
344
- t.jsonb(name);
358
+ if (defaultValue) {
359
+ t.jsonb(name).defaultTo(defaultValue);
360
+ } else {
361
+ t.jsonb(name);
362
+ }
345
363
  if (unique) t.unique(name);
346
- if (defaultValue) t.defaultTo(defaultValue);
347
364
  return;
348
365
  }
349
366
  if (type === "date") {
350
- t.timestamp(name);
367
+ if (defaultValue) {
368
+ t.timestamp(name).defaultTo(defaultValue);
369
+ } else {
370
+ t.timestamp(name);
371
+ }
351
372
  if (unique) t.unique(name);
352
- if (defaultValue) t.defaultTo(defaultValue);
353
373
  return;
354
374
  }
355
375
  if (type === "uuid") {
356
- t.uuid(name);
376
+ if (defaultValue) {
377
+ t.uuid(name).defaultTo(defaultValue);
378
+ } else {
379
+ t.uuid(name);
380
+ }
357
381
  if (unique) t.unique(name);
358
- if (defaultValue) t.defaultTo(defaultValue);
359
382
  return;
360
383
  }
361
384
  throw new Error("Invalid type: " + type);
@@ -445,6 +468,7 @@ var ExuluEvalUtils = {
445
468
 
446
469
  // src/registry/classes.ts
447
470
  var import_crypto_js = __toESM(require("crypto-js"), 1);
471
+ var import_express = require("express");
448
472
  function sanitizeToolName(name) {
449
473
  if (typeof name !== "string") return "";
450
474
  let sanitized = name.replace(/[^a-zA-Z0-9_-]+/g, "_");
@@ -463,7 +487,7 @@ var convertToolsArrayToObject = (tools, configs, providerApiKey) => {
463
487
  console.log("[EXULU] Sanitized tools", sanitizedTools);
464
488
  const askForConfirmation = {
465
489
  description: "Ask the user for confirmation.",
466
- parameters: import_zod2.z.object({
490
+ inputSchema: import_zod2.z.object({
467
491
  message: import_zod2.z.string().describe("The message to ask for confirmation.")
468
492
  })
469
493
  };
@@ -574,10 +598,12 @@ var ExuluAgent = class {
574
598
  this.model = this.config?.model;
575
599
  }
576
600
  get providerName() {
577
- return this.config?.model?.create({ apiKey: "" })?.provider || "";
601
+ const model = this.config?.model?.create({ apiKey: "" });
602
+ return typeof model === "string" ? model : model?.provider || "";
578
603
  }
579
604
  get modelName() {
580
- return this.config?.model?.create({ apiKey: "" })?.modelId || "";
605
+ const model = this.config?.model?.create({ apiKey: "" });
606
+ return typeof model === "string" ? model : model?.modelId || "";
581
607
  }
582
608
  // Exports the agent as a tool that can be used by another agent
583
609
  // todo test this
@@ -603,28 +629,44 @@ var ExuluAgent = class {
603
629
  }
604
630
  });
605
631
  };
606
- generateSync = async ({ messages, prompt, tools, statistics, toolConfigs, providerApiKey }) => {
632
+ generateSync = async ({ prompt, user, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
607
633
  if (!this.model) {
608
634
  throw new Error("Model is required for streaming.");
609
635
  }
610
636
  if (!this.config) {
611
637
  throw new Error("Config is required for generating.");
612
638
  }
613
- if (prompt && messages) {
614
- throw new Error("Prompt and messages cannot be provided at the same time.");
639
+ if (prompt && message) {
640
+ throw new Error("Message and prompt cannot be provided at the same time.");
615
641
  }
616
642
  const model = this.model.create({
617
643
  apiKey: providerApiKey
618
644
  });
645
+ let messages = [];
646
+ if (message && session && user) {
647
+ const previousMessages = await getAgentMessages({
648
+ session,
649
+ user,
650
+ limit: 50,
651
+ page: 1
652
+ });
653
+ const previousMessagesContent = previousMessages.map((message2) => JSON.parse(message2.content));
654
+ messages = await (0, import_ai.validateUIMessages)({
655
+ // append the new message to the previous messages:
656
+ messages: [...previousMessagesContent, message]
657
+ });
658
+ }
659
+ console.log("[EXULU] Model provider key", providerApiKey);
660
+ console.log("[EXULU] Tool configs", toolConfigs);
619
661
  const { text } = await (0, import_ai.generateText)({
620
662
  model,
621
663
  // Should be a LanguageModelV1
622
664
  system: "You are a helpful assistant. When you use a tool to answer a question do not explicitly comment on the result of the tool call unless the user has explicitly you to do something with the result.",
623
- messages,
665
+ messages: messages ? (0, import_ai.convertToModelMessages)(messages) : void 0,
624
666
  prompt,
625
667
  maxRetries: 2,
626
668
  tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey),
627
- maxSteps: 5
669
+ stopWhen: [(0, import_ai.stepCountIs)(5)]
628
670
  });
629
671
  if (statistics) {
630
672
  await updateStatistic({
@@ -637,36 +679,61 @@ var ExuluAgent = class {
637
679
  }
638
680
  return text;
639
681
  };
640
- generateStream = ({ messages, prompt, tools, statistics, toolConfigs, providerApiKey }) => {
682
+ generateStream = async ({ express: express3, user, session, message, tools, statistics, toolConfigs, providerApiKey }) => {
641
683
  if (!this.model) {
642
684
  throw new Error("Model is required for streaming.");
643
685
  }
644
686
  if (!this.config) {
645
687
  throw new Error("Config is required for generating.");
646
688
  }
647
- if (prompt && messages) {
648
- throw new Error("Prompt and messages cannot be provided at the same time.");
689
+ if (!message) {
690
+ throw new Error("Message is required for streaming.");
649
691
  }
650
692
  const model = this.model.create({
651
693
  apiKey: providerApiKey
652
694
  });
695
+ let messages = [];
696
+ const previousMessages = await getAgentMessages({
697
+ session,
698
+ user,
699
+ limit: 50,
700
+ page: 1
701
+ });
702
+ const previousMessagesContent = previousMessages.map((message2) => JSON.parse(message2.content));
703
+ messages = await (0, import_ai.validateUIMessages)({
704
+ // append the new message to the previous messages:
705
+ messages: [...previousMessagesContent, message]
706
+ });
653
707
  console.log("[EXULU] Model provider key", providerApiKey);
654
708
  console.log("[EXULU] Tool configs", toolConfigs);
655
- return (0, import_ai.streamText)({
709
+ const result = (0, import_ai.streamText)({
656
710
  model,
657
711
  // Should be a LanguageModelV1
658
- messages,
659
- prompt,
712
+ messages: messages ? (0, import_ai.convertToModelMessages)(messages) : void 0,
660
713
  system: "You are a helpful assistant. When you use a tool to answer a question do not explicitly comment on the result of the tool call unless the user has explicitly you to do something with the result.",
661
714
  maxRetries: 2,
662
715
  tools: convertToolsArrayToObject(tools, toolConfigs, providerApiKey),
663
- maxSteps: 5,
664
716
  onError: (error) => console.error("[EXULU] chat stream error.", error),
665
- onFinish: async ({ response, usage }) => {
717
+ stopWhen: [(0, import_ai.stepCountIs)(5)]
718
+ });
719
+ result.consumeStream();
720
+ result.pipeUIMessageStreamToResponse(express3.res, {
721
+ originalMessages: messages,
722
+ sendReasoning: true,
723
+ generateMessageId: (0, import_ai.createIdGenerator)({
724
+ prefix: "msg_",
725
+ size: 16
726
+ }),
727
+ onFinish: async ({ messages: messages2 }) => {
666
728
  console.info(
667
729
  "[EXULU] chat stream finished.",
668
- usage
730
+ messages2
669
731
  );
732
+ await saveChat({
733
+ session,
734
+ user,
735
+ messages: messages2
736
+ });
670
737
  if (statistics) {
671
738
  await updateStatistic({
672
739
  name: "count",
@@ -678,8 +745,26 @@ var ExuluAgent = class {
678
745
  }
679
746
  }
680
747
  });
748
+ return;
681
749
  };
682
750
  };
751
+ var getAgentMessages = async ({ session, user, limit, page }) => {
752
+ const { db: db3 } = await postgresClient();
753
+ const messages = await db3.from("agent_messages").where({ session, user }).limit(limit).offset(page * limit);
754
+ return messages;
755
+ };
756
+ var saveChat = async ({ session, user, messages }) => {
757
+ const { db: db3 } = await postgresClient();
758
+ const promises2 = messages.map((message) => {
759
+ return db3.from("agent_messages").insert({
760
+ session,
761
+ user,
762
+ content: JSON.stringify(message),
763
+ title: message.role === "user" ? "User" : "Assistant"
764
+ });
765
+ });
766
+ await Promise.all(promises2);
767
+ };
683
768
  var ExuluEmbedder = class {
684
769
  id;
685
770
  name;
@@ -930,7 +1015,7 @@ var ExuluTool = class {
930
1015
  this.type = type;
931
1016
  this.tool = (0, import_ai.tool)({
932
1017
  description,
933
- parameters: inputSchema || import_zod2.z.object({}),
1018
+ inputSchema: inputSchema || import_zod2.z.object({}),
934
1019
  execute: execute2
935
1020
  });
936
1021
  }
@@ -1419,22 +1504,27 @@ var ExuluSource = class {
1419
1504
  var updateStatistic = async (statistic) => {
1420
1505
  const currentDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1421
1506
  const { db: db3 } = await postgresClient();
1422
- const existing = await db3.from("statistics").where({
1507
+ const existing = await db3.from("tracking").where({
1508
+ ...statistic.user ? { user: statistic.user } : {},
1509
+ ...statistic.role ? { role: statistic.role } : {},
1423
1510
  name: statistic.name,
1424
1511
  label: statistic.label,
1425
1512
  type: statistic.type,
1426
1513
  createdAt: currentDate
1427
1514
  }).first();
1515
+ console.log("!!! existing !!!", existing);
1428
1516
  if (!existing) {
1429
- await db3.from("statistics").insert({
1517
+ await db3.from("tracking").insert({
1430
1518
  name: statistic.name,
1431
1519
  label: statistic.label,
1432
1520
  type: statistic.type,
1433
1521
  total: statistic.count ?? 1,
1434
- createdAt: currentDate
1522
+ createdAt: currentDate,
1523
+ ...statistic.user ? { user: statistic.user } : {},
1524
+ ...statistic.role ? { role: statistic.role } : {}
1435
1525
  });
1436
1526
  } else {
1437
- await db3.from("statistics").update({
1527
+ await db3.from("tracking").update({
1438
1528
  total: db3.raw("total + ?", [statistic.count ?? 1])
1439
1529
  }).where({
1440
1530
  name: statistic.name,
@@ -1446,10 +1536,10 @@ var updateStatistic = async (statistic) => {
1446
1536
  };
1447
1537
 
1448
1538
  // src/registry/index.ts
1449
- var import_express6 = require("express");
1539
+ var import_express7 = require("express");
1450
1540
 
1451
1541
  // src/registry/routes.ts
1452
- var import_express2 = require("express");
1542
+ var import_express3 = require("express");
1453
1543
 
1454
1544
  // src/registry/rate-limiter.ts
1455
1545
  var rateLimiter = async (key, windowSeconds, limit, points) => {
@@ -1773,25 +1863,25 @@ var requestValidators = {
1773
1863
  message: "Missing body."
1774
1864
  };
1775
1865
  }
1776
- if (!req.body.threadId) {
1866
+ if (!req.headers["user"]) {
1777
1867
  return {
1778
1868
  error: true,
1779
1869
  code: 400,
1780
- message: "Missing threadId in body."
1870
+ message: 'Missing "user" property in headers.'
1781
1871
  };
1782
1872
  }
1783
- if (!req.body.resourceId) {
1873
+ if (!req.headers["session"]) {
1784
1874
  return {
1785
1875
  error: true,
1786
1876
  code: 400,
1787
- message: "Missing resourceId in body."
1877
+ message: 'Missing "session" property in headers.'
1788
1878
  };
1789
1879
  }
1790
- if (!req.body.messages) {
1880
+ if (!req.body.message) {
1791
1881
  return {
1792
1882
  error: true,
1793
1883
  code: 400,
1794
- message: 'Missing "messages" property in body.'
1884
+ message: 'Missing "message" property in body.'
1795
1885
  };
1796
1886
  }
1797
1887
  return {
@@ -1840,7 +1930,7 @@ var VectorMethodEnum = {
1840
1930
  };
1841
1931
 
1842
1932
  // src/registry/routes.ts
1843
- var import_express3 = __toESM(require("express"), 1);
1933
+ var import_express4 = __toESM(require("express"), 1);
1844
1934
  var import_server3 = require("@apollo/server");
1845
1935
  var Papa = __toESM(require("papaparse"), 1);
1846
1936
  var import_cors = __toESM(require("cors"), 1);
@@ -1895,6 +1985,9 @@ var map = (field) => {
1895
1985
  case "code":
1896
1986
  type = "String";
1897
1987
  break;
1988
+ case "enum":
1989
+ type = field.enumValues ? `${field.name}Enum` : "String";
1990
+ break;
1898
1991
  case "number":
1899
1992
  type = "Float";
1900
1993
  break;
@@ -1913,6 +2006,16 @@ var map = (field) => {
1913
2006
  return type;
1914
2007
  };
1915
2008
  function createTypeDefs(table) {
2009
+ const enumDefs = table.fields.filter((field) => field.type === "enum" && field.enumValues).map((field) => {
2010
+ const enumValues = field.enumValues.map((value) => {
2011
+ const sanitized = String(value).replace(/[^a-zA-Z0-9_]/g, "_").replace(/^[0-9]/, "_$&").toUpperCase();
2012
+ return ` ${sanitized}`;
2013
+ }).join("\n");
2014
+ return `
2015
+ enum ${field.name}Enum {
2016
+ ${enumValues}
2017
+ }`;
2018
+ }).join("\n");
1916
2019
  const fields = table.fields.map((field) => {
1917
2020
  let type;
1918
2021
  type = map(field);
@@ -1924,8 +2027,6 @@ function createTypeDefs(table) {
1924
2027
  type ${table.name.singular} {
1925
2028
  ${fields.join("\n")}
1926
2029
  ${table.fields.find((field) => field.name === "id") ? "" : "id: ID!"}
1927
- createdAt: Date!
1928
- updatedAt: Date!
1929
2030
  ${rbacField}
1930
2031
  }
1931
2032
  `;
@@ -1936,39 +2037,62 @@ ${table.fields.map((f) => ` ${f.name}: ${map(f)}`).join("\n")}
1936
2037
  ${rbacInputField}
1937
2038
  }
1938
2039
  `;
1939
- return typeDef + inputDef;
2040
+ return enumDefs + typeDef + inputDef;
1940
2041
  }
1941
2042
  function createFilterTypeDefs(table) {
1942
2043
  const fieldFilters = table.fields.map((field) => {
1943
2044
  let type;
1944
- type = map(field);
2045
+ if (field.type === "enum" && field.enumValues) {
2046
+ type = `${field.name}Enum`;
2047
+ } else {
2048
+ type = map(field);
2049
+ }
1945
2050
  return `
1946
2051
  ${field.name}: FilterOperator${type}`;
1947
2052
  });
1948
2053
  const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
2054
+ const enumFilterOperators = table.fields.filter((field) => field.type === "enum" && field.enumValues).map((field) => {
2055
+ const enumTypeName = `${field.name}Enum`;
2056
+ return `
2057
+ input FilterOperator${enumTypeName} {
2058
+ eq: ${enumTypeName}
2059
+ ne: ${enumTypeName}
2060
+ in: [${enumTypeName}]
2061
+ and: [FilterOperator${enumTypeName}]
2062
+ or: [FilterOperator${enumTypeName}]
2063
+ }`;
2064
+ }).join("\n");
1949
2065
  const operatorTypes = `
1950
2066
  input FilterOperatorString {
1951
2067
  eq: String
1952
2068
  ne: String
1953
2069
  in: [String]
1954
2070
  contains: String
2071
+ and: [FilterOperatorString]
2072
+ or: [FilterOperatorString]
1955
2073
  }
1956
2074
 
1957
2075
  input FilterOperatorDate {
1958
2076
  lte: Date
1959
2077
  gte: Date
2078
+ and: [FilterOperatorDate]
2079
+ or: [FilterOperatorDate]
1960
2080
  }
1961
2081
 
1962
2082
  input FilterOperatorFloat {
1963
2083
  eq: Float
1964
2084
  ne: Float
1965
2085
  in: [Float]
2086
+ and: [FilterOperatorFloat]
2087
+ or: [FilterOperatorFloat]
1966
2088
  }
1967
2089
 
1968
2090
  input FilterOperatorBoolean {
1969
2091
  eq: Boolean
1970
2092
  ne: Boolean
1971
2093
  in: [Boolean]
2094
+ and: [FilterOperatorBoolean]
2095
+ or: [FilterOperatorBoolean]
1972
2096
  }
1973
2097
 
1974
2098
  input FilterOperatorJSON {
@@ -1987,6 +2111,8 @@ enum SortDirection {
1987
2111
  DESC
1988
2112
  }
1989
2113
 
2114
+ ${enumFilterOperators}
2115
+
1990
2116
  input Filter${tableNameSingularUpperCaseFirst} {
1991
2117
  ${fieldFilters.join("\n")}
1992
2118
  }`;
@@ -2058,6 +2184,17 @@ function createMutations(table) {
2058
2184
  const validateWriteAccess = async (id, context) => {
2059
2185
  try {
2060
2186
  const { db: db3, req, user } = context;
2187
+ if (user.super_admin === true) {
2188
+ return true;
2189
+ }
2190
+ if (!user.role || !(table.name.plural === "agents" && user.role.agents === "write") && !(table.name.plural === "workflow_templates" && user.role.workflows === "write") && !(table.name.plural === "variables" && user.role.variables === "write") && !(table.name.plural === "users" && user.role.users === "write")) {
2191
+ console.error("Access control error: no role found for current user or no access to entity type.");
2192
+ throw new Error("Access control error: no role found for current user or no access to entity type.");
2193
+ }
2194
+ const hasRBAC = table.RBAC === true;
2195
+ if (!hasRBAC) {
2196
+ return true;
2197
+ }
2061
2198
  const record = await db3.from(tableNamePlural).select(["rights_mode", "created_by"]).where({ id }).first();
2062
2199
  if (!record) {
2063
2200
  throw new Error("Record not found");
@@ -2067,17 +2204,6 @@ function createMutations(table) {
2067
2204
  throw new Error("You are not authorized to edit this record");
2068
2205
  }
2069
2206
  }
2070
- const hasRBAC = table.RBAC === true;
2071
- if (!hasRBAC) {
2072
- return true;
2073
- }
2074
- if (user.super_admin === true) {
2075
- return true;
2076
- }
2077
- if (!user.role || !(table.name.plural === "agents" && user.role.agents === "write") && !(table.name.plural === "workflow_templates" && user.role.workflows === "write") && !(table.name.plural === "variables" && user.role.variables === "write") && !(table.name.plural === "users" && user.role.users === "write")) {
2078
- console.error("Access control error: no role found for current user or no access to entity type.");
2079
- throw new Error("Access control error: no role found for current user or no access to entity type.");
2080
- }
2081
2207
  if (record.rights_mode === "public") {
2082
2208
  return true;
2083
2209
  }
@@ -2258,15 +2384,15 @@ function createMutations(table) {
2258
2384
  var applyAccessControl = (table, user, query) => {
2259
2385
  console.log("table", table);
2260
2386
  const tableNamePlural = table.name.plural.toLowerCase();
2261
- const hasRBAC = table.RBAC === true;
2262
- if (!hasRBAC) {
2387
+ if (!user.super_admin && table.name.plural === "jobs") {
2388
+ query = query.where("created_by", user.id);
2263
2389
  return query;
2264
2390
  }
2265
- if (user.super_admin === true) {
2391
+ const hasRBAC = table.RBAC === true;
2392
+ if (!hasRBAC) {
2266
2393
  return query;
2267
2394
  }
2268
- if (table.name.plural === "jobs") {
2269
- query = query.where("created_by", user.id);
2395
+ if (table.name.plural !== "agent_sessions" && user.super_admin === true) {
2270
2396
  return query;
2271
2397
  }
2272
2398
  if (!user.role || !(table.name.plural === "agents" && (user.role.agents === "read" || user.role.agents === "write")) && !(table.name.plural === "workflow_templates" && (user.role.workflows === "read" || user.role.workflows === "write")) && !(table.name.plural === "variables" && (user.role.variables === "read" || user.role.variables === "write")) && !(table.name.plural === "users" && (user.role.users === "read" || user.role.users === "write"))) {
@@ -2297,6 +2423,27 @@ var applyAccessControl = (table, user, query) => {
2297
2423
  }
2298
2424
  return query;
2299
2425
  };
2426
+ var converOperatorToQuery = (query, fieldName, operators) => {
2427
+ if (operators.eq !== void 0) {
2428
+ query = query.where(fieldName, operators.eq);
2429
+ }
2430
+ if (operators.ne !== void 0) {
2431
+ query = query.whereRaw(`?? IS DISTINCT FROM ?`, [fieldName, operators.ne]);
2432
+ }
2433
+ if (operators.in !== void 0) {
2434
+ query = query.whereIn(fieldName, operators.in);
2435
+ }
2436
+ if (operators.contains !== void 0) {
2437
+ query = query.where(fieldName, "like", `%${operators.contains}%`);
2438
+ }
2439
+ if (operators.lte !== void 0) {
2440
+ query = query.where(fieldName, "<=", operators.lte);
2441
+ }
2442
+ if (operators.gte !== void 0) {
2443
+ query = query.where(fieldName, ">=", operators.gte);
2444
+ }
2445
+ return query;
2446
+ };
2300
2447
  function createQueries(table) {
2301
2448
  const tableNamePlural = table.name.plural.toLowerCase();
2302
2449
  const tableNameSingular = table.name.singular.toLowerCase();
@@ -2304,18 +2451,19 @@ function createQueries(table) {
2304
2451
  filters.forEach((filter) => {
2305
2452
  Object.entries(filter).forEach(([fieldName, operators]) => {
2306
2453
  if (operators) {
2307
- if (operators.eq !== void 0) {
2308
- query = query.where(fieldName, operators.eq);
2309
- }
2310
- if (operators.ne !== void 0) {
2311
- query = query.whereRaw(`?? IS DISTINCT FROM ?`, [fieldName, operators.ne]);
2312
- }
2313
- if (operators.in !== void 0) {
2314
- query = query.whereIn(fieldName, operators.in);
2454
+ if (operators.and !== void 0) {
2455
+ console.log("operators.and", operators.and);
2456
+ operators.and.forEach((operator) => {
2457
+ query = converOperatorToQuery(query, fieldName, operator);
2458
+ });
2315
2459
  }
2316
- if (operators.contains !== void 0) {
2317
- query = query.where(fieldName, "like", `%${operators.contains}%`);
2460
+ if (operators.or !== void 0) {
2461
+ operators.or.forEach((operator) => {
2462
+ query = converOperatorToQuery(query, fieldName, operator);
2463
+ });
2318
2464
  }
2465
+ query = converOperatorToQuery(query, fieldName, operators);
2466
+ console.log("query", query);
2319
2467
  }
2320
2468
  });
2321
2469
  });
@@ -2388,57 +2536,36 @@ function createQueries(table) {
2388
2536
  query = applyFilters(query, filters);
2389
2537
  query = applyAccessControl(table, context.user, query);
2390
2538
  if (groupBy) {
2391
- const results = await query.select(groupBy).count("* as count").groupBy(groupBy);
2539
+ query = query.select(groupBy).groupBy(groupBy);
2540
+ if (tableNamePlural === "tracking") {
2541
+ query = query.sum("total as count");
2542
+ } else {
2543
+ query = query.count("* as count");
2544
+ }
2545
+ const results = await query;
2546
+ console.log("!!! results !!!", results);
2392
2547
  return results.map((r) => ({
2393
2548
  group: r[groupBy],
2394
- count: Number(r.count)
2549
+ count: r.count ? Number(r.count) : 0
2395
2550
  }));
2396
2551
  } else {
2397
- const [{ count }] = await query.count("* as count");
2398
- return [{
2399
- group: "total",
2400
- count: Number(count)
2401
- }];
2402
- }
2403
- },
2404
- // Add jobStatistics query for jobs table (backward compatibility)
2405
- ...tableNamePlural === "jobs" ? {
2406
- jobStatistics: async (_, args, context, info) => {
2407
- const { user, agent, from, to } = args;
2408
- const { db: db3 } = context;
2409
- let query = db3("jobs");
2410
- if (user) {
2411
- query = query.where("user", user);
2412
- }
2413
- if (agent) {
2414
- query = query.where("agent", agent);
2415
- }
2416
- if (from) {
2417
- query = query.where("createdAt", ">=", from);
2418
- }
2419
- if (to) {
2420
- query = query.where("createdAt", "<=", to);
2552
+ if (tableNamePlural === "tracking") {
2553
+ query = query.sum("total as count");
2554
+ const [{ count }] = await query.sum("total as count");
2555
+ console.log("!!! count !!!", count);
2556
+ return [{
2557
+ group: "total",
2558
+ count: count ? Number(count) : 0
2559
+ }];
2560
+ } else {
2561
+ const [{ count }] = await query.count("* as count");
2562
+ return [{
2563
+ group: "total",
2564
+ count: count ? Number(count) : 0
2565
+ }];
2421
2566
  }
2422
- query = applyAccessControl(table, context.user, query);
2423
- const runningQuery = query.clone().whereIn("status", ["active", "waiting", "delayed", "paused"]);
2424
- const [{ runningCount }] = await runningQuery.count("* as runningCount");
2425
- const erroredQuery = query.clone().whereIn("status", ["failed", "stuck"]);
2426
- const [{ erroredCount }] = await erroredQuery.count("* as erroredCount");
2427
- const completedQuery = query.clone().where("status", "completed");
2428
- const [{ completedCount }] = await completedQuery.count("* as completedCount");
2429
- const failedQuery = query.clone().where("status", "failed");
2430
- const [{ failedCount }] = await failedQuery.count("* as failedCount");
2431
- const durationQuery = query.clone().where("status", "completed").whereNotNull("duration").select(db3.raw('AVG("duration") as averageDuration'));
2432
- const [{ averageDuration }] = await durationQuery;
2433
- return {
2434
- runningCount: Number(runningCount),
2435
- erroredCount: Number(erroredCount),
2436
- completedCount: Number(completedCount),
2437
- failedCount: Number(failedCount),
2438
- averageDuration: averageDuration ? Number(averageDuration) : 0
2439
- };
2440
2567
  }
2441
- } : {}
2568
+ }
2442
2569
  };
2443
2570
  }
2444
2571
  var RBACResolver = async (db3, table, entityName, resourceId, rights_mode) => {
@@ -2458,6 +2585,16 @@ var RBACResolver = async (db3, table, entityName, resourceId, rights_mode) => {
2458
2585
  };
2459
2586
  };
2460
2587
  function createSDL(tables) {
2588
+ tables.forEach((table) => {
2589
+ table.fields.push({
2590
+ name: "createdAt",
2591
+ type: "date"
2592
+ });
2593
+ table.fields.push({
2594
+ name: "updatedAt",
2595
+ type: "date"
2596
+ });
2597
+ });
2461
2598
  console.log("[EXULU] Creating SDL");
2462
2599
  let typeDefs = `
2463
2600
  scalar JSON
@@ -2514,7 +2651,6 @@ function createSDL(tables) {
2514
2651
  ${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
2515
2652
  ${tableNameSingular}One(filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingular}
2516
2653
  ${tableNamePlural}Statistics(filters: [Filter${tableNameSingularUpperCaseFirst}], groupBy: String): [StatisticsResult]!
2517
- ${tableNamePlural === "jobs" ? `jobStatistics(user: ID, agent: String, from: String, to: String): JobStatistics` : ""}
2518
2654
  `;
2519
2655
  mutationDefs += `
2520
2656
  ${tableNamePlural}CreateOne(input: ${tableNameSingular}Input!): ${tableNameSingular}
@@ -2539,17 +2675,6 @@ type PageInfo {
2539
2675
  hasNextPage: Boolean!
2540
2676
  }
2541
2677
  `;
2542
- if (tableNamePlural === "jobs") {
2543
- modelDefs += `
2544
- type JobStatistics {
2545
- runningCount: Int!
2546
- erroredCount: Int!
2547
- completedCount: Int!
2548
- failedCount: Int!
2549
- averageDuration: Float!
2550
- }
2551
- `;
2552
- }
2553
2678
  Object.assign(resolvers.Query, createQueries(table));
2554
2679
  Object.assign(resolvers.Mutation, createMutations(table));
2555
2680
  if (table.RBAC) {
@@ -2641,6 +2766,10 @@ var agentMessagesSchema = {
2641
2766
  name: "title",
2642
2767
  type: "text"
2643
2768
  },
2769
+ {
2770
+ name: "user",
2771
+ type: "number"
2772
+ },
2644
2773
  {
2645
2774
  name: "session",
2646
2775
  type: "text"
@@ -2877,6 +3006,10 @@ var rolesSchema = {
2877
3006
  type: "text"
2878
3007
  // write | read access to agents
2879
3008
  },
3009
+ {
3010
+ name: "api",
3011
+ type: "text"
3012
+ },
2880
3013
  {
2881
3014
  name: "workflows",
2882
3015
  type: "text"
@@ -2896,8 +3029,8 @@ var rolesSchema = {
2896
3029
  };
2897
3030
  var statisticsSchema = {
2898
3031
  name: {
2899
- plural: "statistics",
2900
- singular: "statistic"
3032
+ plural: "tracking",
3033
+ singular: "tracking"
2901
3034
  },
2902
3035
  fields: [
2903
3036
  {
@@ -2910,11 +3043,20 @@ var statisticsSchema = {
2910
3043
  },
2911
3044
  {
2912
3045
  name: "type",
2913
- type: "text"
3046
+ type: "enum",
3047
+ enumValues: Object.values(STATISTICS_TYPE_ENUM)
2914
3048
  },
2915
3049
  {
2916
3050
  name: "total",
2917
3051
  type: "number"
3052
+ },
3053
+ {
3054
+ name: "user",
3055
+ type: "number"
3056
+ },
3057
+ {
3058
+ name: "role",
3059
+ type: "uuid"
2918
3060
  }
2919
3061
  ]
2920
3062
  };
@@ -2975,6 +3117,7 @@ var jobsSchema = {
2975
3117
  plural: "jobs",
2976
3118
  singular: "job"
2977
3119
  },
3120
+ RBAC: true,
2978
3121
  fields: [
2979
3122
  {
2980
3123
  name: "redis",
@@ -2984,10 +3127,6 @@ var jobsSchema = {
2984
3127
  name: "session",
2985
3128
  type: "text"
2986
3129
  },
2987
- {
2988
- name: "created_by",
2989
- type: "number"
2990
- },
2991
3130
  {
2992
3131
  name: "status",
2993
3132
  type: "text"
@@ -3113,7 +3252,7 @@ var coreSchemas = {
3113
3252
  };
3114
3253
 
3115
3254
  // src/registry/uppy.ts
3116
- var import_express = require("express");
3255
+ var import_express2 = require("express");
3117
3256
  var createUppyRoutes = async (app) => {
3118
3257
  const {
3119
3258
  S3Client,
@@ -3587,7 +3726,7 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
3587
3726
  optionsSuccessStatus: 200
3588
3727
  // some legacy browsers (IE11, various SmartTVs) choke on 204
3589
3728
  };
3590
- app.use(import_express3.default.json({ limit: REQUEST_SIZE_LIMIT }));
3729
+ app.use(import_express4.default.json({ limit: REQUEST_SIZE_LIMIT }));
3591
3730
  app.use((0, import_cors.default)(corsOptions));
3592
3731
  app.use(import_body_parser.default.urlencoded({ extended: true, limit: REQUEST_SIZE_LIMIT }));
3593
3732
  app.use(import_body_parser.default.json({ limit: REQUEST_SIZE_LIMIT }));
@@ -3630,11 +3769,8 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
3630
3769
  { route: "/agents/:id", method: "GET", note: "Get specific agent" },
3631
3770
  { route: "/contexts", method: "GET", note: "List all contexts" },
3632
3771
  { route: "/contexts/:id", method: "GET", note: "Get specific context" },
3633
- { route: "/contexts/statistics", method: "GET", note: "Get context statistics" },
3634
3772
  { route: "/tools", method: "GET", note: "List all tools" },
3635
3773
  { route: "/tools/:id", method: "GET", note: "Get specific tool" },
3636
- { route: "/statistics/timeseries", method: "POST", note: "Get time series statistics" },
3637
- { route: "/statistics/totals", method: "POST", note: "Get totals statistics" },
3638
3774
  { route: "/items/:context", method: "POST", note: "Create new item in context" },
3639
3775
  { route: "/items/:context", method: "GET", note: "Get items from context" },
3640
3776
  { route: "/items/export/:context", method: "GET", note: "Export items from context" },
@@ -3672,7 +3808,7 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
3672
3808
  app.use(
3673
3809
  "/graphql",
3674
3810
  (0, import_cors.default)(corsOptions),
3675
- import_express3.default.json({ limit: REQUEST_SIZE_LIMIT }),
3811
+ import_express4.default.json({ limit: REQUEST_SIZE_LIMIT }),
3676
3812
  (0, import_express5.expressMiddleware)(server, {
3677
3813
  context: async ({ req }) => {
3678
3814
  const authenticationResult = await requestValidators.authenticate(req);
@@ -4167,115 +4303,6 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
4167
4303
  authenticated: true
4168
4304
  });
4169
4305
  });
4170
- console.log("[EXULU] statistics timeseries");
4171
- app.post("/statistics/timeseries", async (req, res) => {
4172
- const authenticationResult = await requestValidators.authenticate(req);
4173
- if (!authenticationResult.user?.id) {
4174
- res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
4175
- return;
4176
- }
4177
- const { db: db3 } = await postgresClient();
4178
- const type = req.body.type;
4179
- if (!Object.values(STATISTICS_TYPE_ENUM).includes(type)) {
4180
- res.status(400).json({
4181
- message: "Invalid type, must be one of: " + Object.values(STATISTICS_TYPE_ENUM).join(", ")
4182
- });
4183
- return;
4184
- }
4185
- let from = new Date(req.body.from);
4186
- let to = new Date(req.body.to);
4187
- if (!from || !to) {
4188
- from = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
4189
- to = /* @__PURE__ */ new Date();
4190
- }
4191
- const query = db3.from("statistics").select("*");
4192
- query.where("name", "count");
4193
- query.andWhere("type", type);
4194
- query.andWhere("createdAt", ">=", from);
4195
- query.andWhere("createdAt", "<=", to);
4196
- const results = await query;
4197
- const dates = [];
4198
- for (let i = 0; i < (to.getTime() - from.getTime()) / (1e3 * 60 * 60 * 24); i++) {
4199
- dates.push(new Date(from.getTime() + i * (1e3 * 60 * 60 * 24)));
4200
- }
4201
- const data = dates.map((date) => {
4202
- const result = results.find((result2) => result2.date === date);
4203
- if (result) {
4204
- return result;
4205
- }
4206
- return {
4207
- date,
4208
- count: 0
4209
- };
4210
- });
4211
- res.status(200).json({
4212
- data,
4213
- filter: {
4214
- from,
4215
- to
4216
- }
4217
- });
4218
- });
4219
- console.log("[EXULU] statistics totals");
4220
- app.post("/statistics/totals", async (req, res) => {
4221
- const authenticationResult = await requestValidators.authenticate(req);
4222
- if (!authenticationResult.user?.id) {
4223
- res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
4224
- return;
4225
- }
4226
- const { db: db3 } = await postgresClient();
4227
- let from = new Date(req.body.from);
4228
- let to = new Date(req.body.to);
4229
- if (!from || !to) {
4230
- from = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3);
4231
- to = /* @__PURE__ */ new Date();
4232
- }
4233
- let promises2 = Object.values(STATISTICS_TYPE_ENUM).map(async (type) => {
4234
- const result = await db3.from("statistics").where("name", "count").andWhere("type", type).andWhere("createdAt", ">=", from).andWhere("createdAt", "<=", to).sum("total as total");
4235
- return {
4236
- [type]: result[0]?.total || 0
4237
- };
4238
- });
4239
- const results = await Promise.all(promises2);
4240
- res.status(200).json({
4241
- data: { ...Object.assign({}, ...results) },
4242
- filter: {
4243
- from,
4244
- to
4245
- }
4246
- });
4247
- });
4248
- console.log("[EXULU] contexts statistics");
4249
- app.get("/contexts/statistics", async (req, res) => {
4250
- const authenticationResult = await requestValidators.authenticate(req);
4251
- if (!authenticationResult.user?.id) {
4252
- res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
4253
- return;
4254
- }
4255
- const { db: db3 } = await postgresClient();
4256
- const statistics = await db3("statistics").where("name", "count").andWhere("type", "context.retrieve").sum("total as total").first();
4257
- const response = await db3("jobs").select(db3.raw(`to_char("createdAt", 'YYYY-MM-DD') as date`)).count("* as count").where("type", "embedder").groupByRaw(`to_char("createdAt", 'YYYY-MM-DD')`).then((rows) => ({
4258
- jobs: rows
4259
- }));
4260
- let jobs = [];
4261
- if (response[0]) {
4262
- jobs = response[0].jobs.map((job) => ({
4263
- date: job.id,
4264
- count: job.count
4265
- }));
4266
- }
4267
- const embeddingsCountResult = await db3("jobs").where("type", "embedder").count("* as count").first();
4268
- res.status(200).json({
4269
- active: contexts.filter((context) => context.active).length,
4270
- inactive: contexts.filter((context) => !context.active).length,
4271
- sources: contexts.reduce((acc, context) => acc + context.sources.get().length, 0),
4272
- queries: statistics?.total || 0,
4273
- jobs,
4274
- totals: {
4275
- embeddings: embeddingsCountResult?.count || 0
4276
- }
4277
- });
4278
- });
4279
4306
  console.log("[EXULU] context by id");
4280
4307
  app.get(`/contexts/:id`, async (req, res) => {
4281
4308
  const authenticationResult = await requestValidators.authenticate(req);
@@ -4446,7 +4473,11 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
4446
4473
  return;
4447
4474
  }
4448
4475
  }
4449
- const stream = req.headers["stream"] || false;
4476
+ const headers = {
4477
+ stream: req.headers["stream"] === "true" || false,
4478
+ user: req.headers["user"] || null,
4479
+ session: req.headers["session"] || null
4480
+ };
4450
4481
  const requestValidationResult = requestValidators.agents(req);
4451
4482
  if (requestValidationResult.error) {
4452
4483
  res.status(requestValidationResult.code || 500).json({ detail: `${requestValidationResult.message}` });
@@ -4457,6 +4488,13 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
4457
4488
  res.status(authenticationResult.code || 500).json({ detail: `${authenticationResult.message}` });
4458
4489
  return;
4459
4490
  }
4491
+ const user = authenticationResult.user;
4492
+ if (user.type !== "api" && !user.super_admin && req.body.resourceId !== user.id) {
4493
+ res.status(400).json({
4494
+ message: "The provided user id in the resourceId field is not the same as the authenticated user. Only super admins and API users can impersonate other users."
4495
+ });
4496
+ return;
4497
+ }
4460
4498
  console.log("[EXULU] agent tools", agentInstance.tools);
4461
4499
  const enabledTools = agentInstance.tools.map(({ config, toolId }) => tools.find(({ id }) => id === toolId)).filter(Boolean);
4462
4500
  console.log("[EXULU] enabled tools", enabledTools);
@@ -4479,9 +4517,15 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
4479
4517
  const bytes = import_crypto_js3.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
4480
4518
  providerApiKey = bytes.toString(import_crypto_js3.default.enc.Utf8);
4481
4519
  }
4482
- if (!!stream) {
4483
- const result = agent.generateStream({
4484
- messages: req.body.messages,
4520
+ if (!!headers.stream) {
4521
+ await agent.generateStream({
4522
+ express: {
4523
+ res,
4524
+ req
4525
+ },
4526
+ user: headers.user,
4527
+ session: headers.session,
4528
+ message: req.body.message,
4485
4529
  tools: enabledTools,
4486
4530
  providerApiKey,
4487
4531
  toolConfigs: agentInstance.tools,
@@ -4490,11 +4534,12 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
4490
4534
  trigger: "agent"
4491
4535
  }
4492
4536
  });
4493
- result.pipeDataStreamToResponse(res);
4494
4537
  return;
4495
4538
  } else {
4496
4539
  const response = await agent.generateSync({
4497
- messages: req.body.messages,
4540
+ user: headers.user,
4541
+ session: headers.session,
4542
+ message: req.body.message,
4498
4543
  tools: enabledTools.map((tool2) => tool2.tool),
4499
4544
  providerApiKey,
4500
4545
  toolConfigs: agentInstance.tools,
@@ -4516,7 +4561,7 @@ var createExpressRoutes = async (app, agents, tools, contexts) => {
4516
4561
  console.log("Routes:");
4517
4562
  console.table(routeLogs);
4518
4563
  const TARGET_API = "https://api.anthropic.com";
4519
- app.use("/gateway/anthropic/:id", import_express3.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
4564
+ app.use("/gateway/anthropic/:id", import_express4.default.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
4520
4565
  const path3 = req.url;
4521
4566
  const url = `${TARGET_API}${path3}`;
4522
4567
  try {
@@ -4861,7 +4906,7 @@ var import_node_crypto = require("crypto");
4861
4906
  var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
4862
4907
  var import_types = require("@modelcontextprotocol/sdk/types.js");
4863
4908
  var import_zod3 = require("zod");
4864
- var import_express4 = require("express");
4909
+ var import_express6 = require("express");
4865
4910
  var SESSION_ID_HEADER = "mcp-session-id";
4866
4911
  var ExuluMCP = class {
4867
4912
  server;
@@ -4999,7 +5044,7 @@ ${code}`
4999
5044
  };
5000
5045
 
5001
5046
  // src/registry/index.ts
5002
- var import_express7 = __toESM(require("express"), 1);
5047
+ var import_express8 = __toESM(require("express"), 1);
5003
5048
 
5004
5049
  // src/templates/agents/claude-code.ts
5005
5050
  var agentId = "0832-5178-1145-2194";
@@ -5019,7 +5064,6 @@ var defaultAgent = new ExuluAgent({
5019
5064
  description: `Basic agent without any defined tools, that can support MCP's.`,
5020
5065
  type: "agent",
5021
5066
  capabilities: {
5022
- tools: false,
5023
5067
  images: [],
5024
5068
  files: [],
5025
5069
  audio: [],
@@ -5031,10 +5075,10 @@ var defaultAgent = new ExuluAgent({
5031
5075
  instructions: "You are a helpful assistant.",
5032
5076
  model: {
5033
5077
  create: ({ apiKey }) => {
5034
- const anthropic2 = (0, import_anthropic.createAnthropic)({
5078
+ const anthropic = (0, import_anthropic.createAnthropic)({
5035
5079
  apiKey
5036
5080
  });
5037
- return anthropic2("claude-4-opus-20250514");
5081
+ return anthropic.languageModel("claude-4-opus-20250514");
5038
5082
  }
5039
5083
  // todo add a field of type string that adds a dropdown list from which the user can select the model
5040
5084
  // todo for each model, check which provider is used, and require the admin to add one or multiple
@@ -5080,7 +5124,7 @@ var ExuluApp = class {
5080
5124
  ];
5081
5125
  this._queues = [...new Set(queues2.filter((o) => !!o))];
5082
5126
  if (!this._expressApp) {
5083
- this._expressApp = (0, import_express7.default)();
5127
+ this._expressApp = (0, import_express8.default)();
5084
5128
  await this.server.express.init();
5085
5129
  console.log("[EXULU] Express app initialized.");
5086
5130
  }
@@ -6575,6 +6619,13 @@ var ExuluJobs = {
6575
6619
  var db2 = {
6576
6620
  init: async () => {
6577
6621
  await execute();
6622
+ },
6623
+ api: {
6624
+ key: {
6625
+ generate: async (name, email) => {
6626
+ return await generateApiKey(name, email);
6627
+ }
6628
+ }
6578
6629
  }
6579
6630
  };
6580
6631
  var ExuluChunkers = {