@exulu/backend 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,9 +1,9 @@
1
- ## [1.6.2](https://github.com/Qventu/exulu-backend/compare/v1.6.1...v1.6.2) (2025-08-05)
1
+ # [1.7.0](https://github.com/Qventu/exulu-backend/compare/v1.6.2...v1.7.0) (2025-08-06)
2
2
 
3
3
 
4
- ### Bug Fixes
4
+ ### Features
5
5
 
6
- * remove browserbase from core repo ([371031b](https://github.com/Qventu/exulu-backend/commit/371031ba330b89d619b0b4cfe935521e249d6b29))
6
+ * add support for variable management ([af94446](https://github.com/Qventu/exulu-backend/commit/af94446a5bc12a092fb09397948a430f5527fccd))
7
7
 
8
8
  # [1.1.0](https://github.com/Qventu/exulu-backend/compare/v1.0.1...v1.1.0) (2025-07-30)
9
9
 
package/dist/index.cjs CHANGED
@@ -304,41 +304,50 @@ var bullmqDecorator = async ({
304
304
  };
305
305
 
306
306
  // src/registry/utils/map-types.ts
307
- var mapType = (t, type, name, defaultValue) => {
307
+ var mapType = (t, type, name, defaultValue, unique) => {
308
308
  if (type === "text") {
309
309
  t.text(name);
310
+ if (unique) t.unique(name);
310
311
  return;
311
312
  }
312
313
  if (type === "longText") {
313
314
  t.text(name);
315
+ if (unique) t.unique(name);
314
316
  return;
315
317
  }
316
318
  if (type === "shortText") {
317
319
  t.string(name, 100);
320
+ if (unique) t.unique(name);
318
321
  return;
319
322
  }
320
323
  if (type === "number") {
321
324
  t.float(name);
325
+ if (unique) t.unique(name);
322
326
  return;
323
327
  }
324
328
  if (type === "boolean") {
325
329
  t.boolean(name).defaultTo(defaultValue || false);
330
+ if (unique) t.unique(name);
326
331
  return;
327
332
  }
328
333
  if (type === "code") {
329
334
  t.text(name);
335
+ if (unique) t.unique(name);
330
336
  return;
331
337
  }
332
338
  if (type === "json") {
333
339
  t.jsonb(name);
340
+ if (unique) t.unique(name);
334
341
  return;
335
342
  }
336
343
  if (type === "date") {
337
344
  t.timestamp(name);
345
+ if (unique) t.unique(name);
338
346
  return;
339
347
  }
340
348
  if (type === "uuid") {
341
349
  t.uuid(name);
350
+ if (unique) t.unique(name);
342
351
  return;
343
352
  }
344
353
  throw new Error("Invalid type: " + type);
@@ -437,7 +446,39 @@ var ExuluEvalUtils = {
437
446
  }
438
447
  };
439
448
 
449
+ // src/registry/utils/claude-messages.ts
450
+ var CLAUDE_MESSAGES = {
451
+ anthropic_token_variable_not_encrypted: `
452
+ \x1B[41m -- Anthropic token variable set by your admin is not encrypted. This poses a security risk. Please contact your admin to fix the variable used for your key. --
453
+ \x1B[0m`,
454
+ anthropic_token_variable_not_found: `
455
+ \x1B[41m -- Anthropic token variable not found. Please contact to fix the variable used for your key. --
456
+ \x1B[0m`,
457
+ authentication_error: `
458
+ \x1B[41m -- Authentication error please check your IMP token and try again. --
459
+ \x1B[0m`,
460
+ missing_body: `
461
+ \x1B[41m -- Missing body Anthropic response. --
462
+ \x1B[0m`,
463
+ missing_nextauth_secret: `
464
+ \x1B[41m -- Missing NEXTAUTH_SECRET in environment variables on the server. --
465
+ \x1B[0m`,
466
+ not_enabled: `
467
+ \x1B[31m
468
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
469
+ \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
470
+ \u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
471
+ \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
472
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
473
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
474
+ Intelligence Management Platform
475
+ \x1B[0m
476
+ \x1B[41m -- Your account has not been enabled to use Claude Code, please contact your admin or enable Claude Code in the user settings. --
477
+ \x1B[0m`
478
+ };
479
+
440
480
  // src/registry/classes.ts
481
+ var import_crypto_js = __toESM(require("crypto-js"), 1);
441
482
  function sanitizeToolName(name) {
442
483
  if (typeof name !== "string") return "";
443
484
  let sanitized = name.replace(/[^a-zA-Z0-9_-]+/g, "_");
@@ -447,7 +488,7 @@ function sanitizeToolName(name) {
447
488
  }
448
489
  return sanitized;
449
490
  }
450
- var convertToolsArrayToObject = (tools) => {
491
+ var convertToolsArrayToObject = (tools, configs) => {
451
492
  if (!tools) return {};
452
493
  const sanitizedTools = tools ? tools.map((tool2) => ({
453
494
  ...tool2,
@@ -461,12 +502,57 @@ var convertToolsArrayToObject = (tools) => {
461
502
  };
462
503
  return {
463
504
  ...sanitizedTools?.reduce(
464
- (prev, cur) => ({ ...prev, [cur.name]: cur.tool }),
505
+ (prev, cur) => ({
506
+ ...prev,
507
+ [cur.name]: {
508
+ ...cur.tool,
509
+ execute: async (inputs, options) => {
510
+ if (!cur.tool?.execute) {
511
+ console.error("[EXULU] Tool execute function is undefined.", cur.tool);
512
+ throw new Error("Tool execute function is undefined.");
513
+ }
514
+ let config = configs?.find((config2) => config2.toolId === cur.id);
515
+ if (config) {
516
+ config = await hydrateVariables(config || []);
517
+ }
518
+ return await cur.tool.execute({
519
+ ...inputs,
520
+ // Convert config to object format if a config object
521
+ // is available, after we added the .value property
522
+ // by hydrating it from the variables table.
523
+ config: config ? config.config.reduce((acc, curr) => {
524
+ acc[curr.name] = curr.value;
525
+ return acc;
526
+ }, {}) : {}
527
+ }, options);
528
+ }
529
+ }
530
+ }),
465
531
  {}
466
532
  ),
467
533
  askForConfirmation
468
534
  };
469
535
  };
536
+ var hydrateVariables = async (tool2) => {
537
+ const { db: db3 } = await postgresClient();
538
+ const promises2 = tool2.config.map(async (toolConfig) => {
539
+ const variableName = toolConfig.variable;
540
+ const variable = await db3.from("variables").where({ name: variableName }).first();
541
+ if (!variable) {
542
+ console.error("[EXULU] Variable " + variableName + " not found.");
543
+ throw new Error("Variable " + variableName + " not found.");
544
+ }
545
+ let value = variable.value;
546
+ if (variable.encrypted) {
547
+ const bytes = import_crypto_js.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
548
+ value = bytes.toString(import_crypto_js.default.enc.Utf8);
549
+ }
550
+ toolConfig.value = value;
551
+ });
552
+ await Promise.all(promises2);
553
+ console.log("[EXULU] Variable values retrieved and added to tool config.");
554
+ return tool2;
555
+ };
470
556
  function generateSlug(name) {
471
557
  const normalized = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
472
558
  const lowercase = normalized.toLowerCase();
@@ -523,12 +609,13 @@ var ExuluAgent = class {
523
609
  tool = () => {
524
610
  return new ExuluTool({
525
611
  id: this.id,
526
- name: `${this.name} agent`,
612
+ name: `${this.name}`,
527
613
  type: "agent",
528
614
  inputSchema: import_zod2.z.object({
529
615
  prompt: import_zod2.z.string()
530
616
  }),
531
617
  description: `A function that calls an AI agent named: ${this.name}. The agent does the following: ${this.description}.`,
618
+ config: [],
532
619
  execute: async ({ prompt }) => {
533
620
  return await this.generateSync({
534
621
  prompt,
@@ -540,7 +627,7 @@ var ExuluAgent = class {
540
627
  }
541
628
  });
542
629
  };
543
- generateSync = async ({ messages, prompt, tools, statistics }) => {
630
+ generateSync = async ({ messages, prompt, tools, statistics, configs }) => {
544
631
  if (!this.model) {
545
632
  throw new Error("Model is required for streaming.");
546
633
  }
@@ -556,7 +643,7 @@ var ExuluAgent = class {
556
643
  messages,
557
644
  prompt,
558
645
  maxRetries: 2,
559
- tools: convertToolsArrayToObject(tools),
646
+ tools: convertToolsArrayToObject(tools, configs),
560
647
  maxSteps: 5
561
648
  });
562
649
  if (statistics) {
@@ -570,7 +657,7 @@ var ExuluAgent = class {
570
657
  }
571
658
  return text;
572
659
  };
573
- generateStream = ({ messages, prompt, tools, statistics }) => {
660
+ generateStream = ({ messages, prompt, tools, statistics, configs }) => {
574
661
  if (!this.model) {
575
662
  throw new Error("Model is required for streaming.");
576
663
  }
@@ -586,7 +673,7 @@ var ExuluAgent = class {
586
673
  prompt,
587
674
  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.",
588
675
  maxRetries: 2,
589
- tools: convertToolsArrayToObject(tools),
676
+ tools: convertToolsArrayToObject(tools, configs),
590
677
  maxSteps: 5,
591
678
  onError: (error) => console.error("[EXULU] chat stream error.", error),
592
679
  onFinish: async ({ response, usage }) => {
@@ -942,8 +1029,10 @@ var ExuluTool = class {
942
1029
  inputSchema;
943
1030
  type;
944
1031
  tool;
945
- constructor({ id, name, description, inputSchema, type, execute: execute2 }) {
1032
+ config;
1033
+ constructor({ id, name, description, inputSchema, type, execute: execute2, config }) {
946
1034
  this.id = id;
1035
+ this.config = config;
947
1036
  this.name = name;
948
1037
  this.description = description;
949
1038
  this.inputSchema = inputSchema;
@@ -1351,11 +1440,11 @@ var ExuluContext = class {
1351
1440
  table.text("source");
1352
1441
  table.timestamp("embeddings_updated_at");
1353
1442
  for (const field of this.fields) {
1354
- const { type, name } = field;
1443
+ const { type, name, unique } = field;
1355
1444
  if (!type || !name) {
1356
1445
  continue;
1357
1446
  }
1358
- mapType(table, type, sanitizeName(name));
1447
+ mapType(table, type, sanitizeName(name), void 0, unique);
1359
1448
  }
1360
1449
  table.timestamps(true, true);
1361
1450
  });
@@ -1377,11 +1466,12 @@ var ExuluContext = class {
1377
1466
  tool = () => {
1378
1467
  return new ExuluTool({
1379
1468
  id: this.id,
1380
- name: `${this.name} context`,
1469
+ name: `${this.name}`,
1381
1470
  type: "context",
1382
1471
  inputSchema: import_zod2.z.object({
1383
1472
  query: import_zod2.z.string()
1384
1473
  }),
1474
+ config: [],
1385
1475
  description: `Gets information from the context called: ${this.name}. The context description is: ${this.description}.`,
1386
1476
  execute: async ({ query }) => {
1387
1477
  return await this.getItems({
@@ -1849,7 +1939,7 @@ var import_reflect_metadata = require("reflect-metadata");
1849
1939
  var import_schema = require("@graphql-tools/schema");
1850
1940
  var import_graphql_type_json = __toESM(require("graphql-type-json"), 1);
1851
1941
  var import_graphql = require("graphql");
1852
- var import_crypto_js = __toESM(require("crypto-js"), 1);
1942
+ var import_crypto_js2 = __toESM(require("crypto-js"), 1);
1853
1943
  var GraphQLDate = new import_graphql.GraphQLScalarType({
1854
1944
  name: "Date",
1855
1945
  description: "Date custom scalar type",
@@ -2015,8 +2105,9 @@ function createMutations(table) {
2015
2105
  return results[0];
2016
2106
  },
2017
2107
  [`${tableNamePlural}UpdateOne`]: async (_, args, context, info) => {
2018
- const { db: db3 } = context;
2108
+ const { db: db3, req } = context;
2019
2109
  let { where, input } = args;
2110
+ await validateSuperAdminPermission(tableNamePlural, input, req);
2020
2111
  input = encryptSensitiveFields(input);
2021
2112
  await db3(tableNamePlural).where(where).update({
2022
2113
  ...input,
@@ -2027,9 +2118,10 @@ function createMutations(table) {
2027
2118
  return result;
2028
2119
  },
2029
2120
  [`${tableNamePlural}UpdateOneById`]: async (_, args, context, info) => {
2121
+ const { db: db3, req } = context;
2030
2122
  let { id, input } = args;
2123
+ await validateSuperAdminPermission(tableNamePlural, input, req);
2031
2124
  input = encryptSensitiveFields(input);
2032
- const { db: db3 } = context;
2033
2125
  await db3(tableNamePlural).where({ id }).update({
2034
2126
  ...input,
2035
2127
  updatedAt: /* @__PURE__ */ new Date()
@@ -2166,6 +2258,7 @@ function createQueries(table) {
2166
2258
  };
2167
2259
  }
2168
2260
  function createSDL(tables) {
2261
+ console.log("[EXULU] Creating SDL");
2169
2262
  let typeDefs = `
2170
2263
  scalar JSON
2171
2264
  scalar Date
@@ -2181,6 +2274,7 @@ function createSDL(tables) {
2181
2274
  const tableNamePlural = table.name.plural.toLowerCase();
2182
2275
  const tableNameSingular = table.name.singular.toLowerCase();
2183
2276
  const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
2277
+ console.log("[EXULU] Adding table >>>>>", tableNamePlural);
2184
2278
  typeDefs += `
2185
2279
  ${tableNameSingular}ById(id: ID!): ${tableNameSingular}
2186
2280
  ${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
@@ -2255,15 +2349,23 @@ type JobStatistics {
2255
2349
  console.log("\n");
2256
2350
  return schema;
2257
2351
  }
2258
- var sensitiveFields = ["anthropic_token"];
2259
2352
  var encryptSensitiveFields = (input) => {
2260
- sensitiveFields.forEach((field) => {
2261
- if (input[field]) {
2262
- input[field] = import_crypto_js.default.AES.encrypt(input[field], process.env.NEXTAUTH_SECRET).toString();
2263
- }
2264
- });
2353
+ if (input.value && input.encrypted === true) {
2354
+ input.value = import_crypto_js2.default.AES.encrypt(input.value, process.env.NEXTAUTH_SECRET).toString();
2355
+ }
2265
2356
  return input;
2266
2357
  };
2358
+ var validateSuperAdminPermission = async (tableNamePlural, input, req) => {
2359
+ if (tableNamePlural === "users" && input.super_admin !== void 0) {
2360
+ const authResult = await requestValidators.authenticate(req);
2361
+ if (authResult.error || !authResult.user) {
2362
+ throw new Error("Authentication failed");
2363
+ }
2364
+ if (!authResult.user.super_admin) {
2365
+ throw new Error("Only super administrators can modify super_admin status");
2366
+ }
2367
+ }
2368
+ };
2267
2369
 
2268
2370
  // src/registry/routes.ts
2269
2371
  var import_express5 = require("@as-integrations/express5");
@@ -2601,6 +2703,29 @@ var agentsSchema = {
2601
2703
  }
2602
2704
  ]
2603
2705
  };
2706
+ var variablesSchema = {
2707
+ name: {
2708
+ plural: "variables",
2709
+ singular: "variable"
2710
+ },
2711
+ fields: [
2712
+ {
2713
+ name: "name",
2714
+ type: "text",
2715
+ index: true,
2716
+ unique: true
2717
+ },
2718
+ {
2719
+ name: "value",
2720
+ type: "longText"
2721
+ },
2722
+ {
2723
+ name: "encrypted",
2724
+ type: "boolean",
2725
+ default: false
2726
+ }
2727
+ ]
2728
+ };
2604
2729
 
2605
2730
  // src/registry/uppy.ts
2606
2731
  var import_express = require("express");
@@ -2996,34 +3121,7 @@ var createUppyRoutes = async (app) => {
2996
3121
  // src/registry/routes.ts
2997
3122
  var import_utils2 = require("@apollo/utils.keyvaluecache");
2998
3123
  var import_body_parser = __toESM(require("body-parser"), 1);
2999
- var import_crypto_js2 = __toESM(require("crypto-js"), 1);
3000
-
3001
- // src/registry/utils/claude-messages.ts
3002
- var CLAUDE_MESSAGES = {
3003
- authentication_error: `
3004
- \x1B[41m -- Authentication error please check your IMP token and try again. --
3005
- \x1B[0m`,
3006
- missing_body: `
3007
- \x1B[41m -- Missing body Anthropic response. --
3008
- \x1B[0m`,
3009
- missing_nextauth_secret: `
3010
- \x1B[41m -- Missing NEXTAUTH_SECRET in environment variables on the server. --
3011
- \x1B[0m`,
3012
- not_enabled: `
3013
- \x1B[31m
3014
- \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
3015
- \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
3016
- \u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
3017
- \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
3018
- \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
3019
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
3020
- Intelligence Management Platform
3021
- \x1B[0m
3022
- \x1B[41m -- Your account has not been enabled to use Claude Code, please contact your admin or enable Claude Code in the user settings. --
3023
- \x1B[0m`
3024
- };
3025
-
3026
- // src/registry/routes.ts
3124
+ var import_crypto_js3 = __toESM(require("crypto-js"), 1);
3027
3125
  var REQUEST_SIZE_LIMIT = "50mb";
3028
3126
  var global_queues = {
3029
3127
  logs_cleaner: "logs-cleaner"
@@ -3130,7 +3228,7 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
3130
3228
  } else {
3131
3229
  console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
3132
3230
  }
3133
- const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, agentSessionsSchema, agentMessagesSchema]);
3231
+ const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, agentSessionsSchema, agentMessagesSchema, variablesSchema]);
3134
3232
  console.log("[EXULU] graphql server");
3135
3233
  const server = new import_server3.ApolloServer({
3136
3234
  cache: new import_utils2.InMemoryLRUCache(),
@@ -3965,6 +4063,7 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
3965
4063
  const result = agent.generateStream({
3966
4064
  messages: req.body.messages,
3967
4065
  tools: enabledTools,
4066
+ configs: agentInstance.tools,
3968
4067
  statistics: {
3969
4068
  label: agent.name,
3970
4069
  trigger: "agent"
@@ -3975,7 +4074,8 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
3975
4074
  } else {
3976
4075
  const response = await agent.generateSync({
3977
4076
  messages: req.body.messages,
3978
- tools: enabledTools.map(),
4077
+ tools: enabledTools.map((tool2) => tool2.tool),
4078
+ configs: agentInstance.tools,
3979
4079
  statistics: {
3980
4080
  label: agent.name,
3981
4081
  trigger: "agent"
@@ -4092,8 +4192,25 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
4092
4192
  res.end(Buffer.from(arrayBuffer));
4093
4193
  return;
4094
4194
  }
4095
- const bytes = import_crypto_js2.default.AES.decrypt(authenticationResult.user?.anthropic_token, process.env.NEXTAUTH_SECRET);
4096
- const anthropicApiKey = bytes.toString(import_crypto_js2.default.enc.Utf8);
4195
+ const variableName = authenticationResult.user.anthropic_token;
4196
+ const variable = await db3.from("variables").where({ name: variableName }).first();
4197
+ if (!variable) {
4198
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.anthropic_token_variable_not_found);
4199
+ res.setHeader("Content-Type", "application/json");
4200
+ res.end(Buffer.from(arrayBuffer));
4201
+ return;
4202
+ }
4203
+ let anthropicApiKey = variable.value;
4204
+ if (!variable.encrypted) {
4205
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.anthropic_token_variable_not_encrypted);
4206
+ res.setHeader("Content-Type", "application/json");
4207
+ res.end(Buffer.from(arrayBuffer));
4208
+ return;
4209
+ }
4210
+ if (variable.encrypted) {
4211
+ const bytes = import_crypto_js3.default.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
4212
+ anthropicApiKey = bytes.toString(import_crypto_js3.default.enc.Utf8);
4213
+ }
4097
4214
  const headers = {
4098
4215
  "x-api-key": anthropicApiKey,
4099
4216
  "anthropic-version": "2023-06-01",
@@ -5968,6 +6085,7 @@ var generateApiKey = async (name, email) => {
5968
6085
 
5969
6086
  // src/postgres/init-db.ts
5970
6087
  var up = async function(knex) {
6088
+ console.log("[EXULU] Database up.");
5971
6089
  if (!await knex.schema.hasTable("agent_sessions")) {
5972
6090
  console.log("[EXULU] Creating agent_sessions table.");
5973
6091
  await knex.schema.createTable("agent_sessions", (table) => {
@@ -5975,11 +6093,11 @@ var up = async function(knex) {
5975
6093
  table.timestamp("createdAt").defaultTo(knex.fn.now());
5976
6094
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
5977
6095
  for (const field of agentSessionsSchema.fields) {
5978
- const { type, name, default: defaultValue } = field;
6096
+ const { type, name, default: defaultValue, unique } = field;
5979
6097
  if (!type || !name) {
5980
6098
  continue;
5981
6099
  }
5982
- mapType(table, type, sanitizeName(name), defaultValue);
6100
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
5983
6101
  }
5984
6102
  });
5985
6103
  }
@@ -5990,11 +6108,11 @@ var up = async function(knex) {
5990
6108
  table.timestamp("createdAt").defaultTo(knex.fn.now());
5991
6109
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
5992
6110
  for (const field of agentMessagesSchema.fields) {
5993
- const { type, name, default: defaultValue } = field;
6111
+ const { type, name, default: defaultValue, unique } = field;
5994
6112
  if (!type || !name) {
5995
6113
  continue;
5996
6114
  }
5997
- mapType(table, type, sanitizeName(name), defaultValue);
6115
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
5998
6116
  }
5999
6117
  });
6000
6118
  }
@@ -6005,11 +6123,11 @@ var up = async function(knex) {
6005
6123
  table.timestamp("createdAt").defaultTo(knex.fn.now());
6006
6124
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
6007
6125
  for (const field of rolesSchema.fields) {
6008
- const { type, name, default: defaultValue } = field;
6126
+ const { type, name, default: defaultValue, unique } = field;
6009
6127
  if (!type || !name) {
6010
6128
  continue;
6011
6129
  }
6012
- mapType(table, type, sanitizeName(name), defaultValue);
6130
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6013
6131
  }
6014
6132
  });
6015
6133
  }
@@ -6020,11 +6138,11 @@ var up = async function(knex) {
6020
6138
  table.timestamp("createdAt").defaultTo(knex.fn.now());
6021
6139
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
6022
6140
  for (const field of evalResultsSchema.fields) {
6023
- const { type, name, default: defaultValue } = field;
6141
+ const { type, name, default: defaultValue, unique } = field;
6024
6142
  if (!type || !name) {
6025
6143
  continue;
6026
6144
  }
6027
- mapType(table, type, sanitizeName(name), defaultValue);
6145
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6028
6146
  }
6029
6147
  });
6030
6148
  }
@@ -6035,11 +6153,11 @@ var up = async function(knex) {
6035
6153
  table.timestamp("createdAt").defaultTo(knex.fn.now());
6036
6154
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
6037
6155
  for (const field of statisticsSchema.fields) {
6038
- const { type, name, default: defaultValue } = field;
6156
+ const { type, name, default: defaultValue, unique } = field;
6039
6157
  if (!type || !name) {
6040
6158
  continue;
6041
6159
  }
6042
- mapType(table, type, sanitizeName(name), defaultValue);
6160
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6043
6161
  }
6044
6162
  });
6045
6163
  }
@@ -6050,11 +6168,11 @@ var up = async function(knex) {
6050
6168
  table.timestamp("createdAt").defaultTo(knex.fn.now());
6051
6169
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
6052
6170
  for (const field of jobsSchema.fields) {
6053
- const { type, name, default: defaultValue } = field;
6171
+ const { type, name, default: defaultValue, unique } = field;
6054
6172
  if (!type || !name) {
6055
6173
  continue;
6056
6174
  }
6057
- mapType(table, type, sanitizeName(name), defaultValue);
6175
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6058
6176
  }
6059
6177
  });
6060
6178
  }
@@ -6065,11 +6183,26 @@ var up = async function(knex) {
6065
6183
  table.timestamp("createdAt").defaultTo(knex.fn.now());
6066
6184
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
6067
6185
  for (const field of agentsSchema.fields) {
6068
- const { type, name, default: defaultValue } = field;
6186
+ const { type, name, default: defaultValue, unique } = field;
6187
+ if (!type || !name) {
6188
+ continue;
6189
+ }
6190
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6191
+ }
6192
+ });
6193
+ }
6194
+ if (!await knex.schema.hasTable("variables")) {
6195
+ console.log("[EXULU] Creating variables table.");
6196
+ await knex.schema.createTable("variables", (table) => {
6197
+ table.uuid("id").primary().defaultTo(knex.fn.uuid());
6198
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
6199
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
6200
+ for (const field of variablesSchema.fields) {
6201
+ const { type, name, default: defaultValue, unique } = field;
6069
6202
  if (!type || !name) {
6070
6203
  continue;
6071
6204
  }
6072
- mapType(table, type, sanitizeName(name), defaultValue);
6205
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6073
6206
  }
6074
6207
  });
6075
6208
  }
@@ -6095,14 +6228,14 @@ var up = async function(knex) {
6095
6228
  table.text("image");
6096
6229
  for (const field of usersSchema.fields) {
6097
6230
  console.log("[EXULU] field", field);
6098
- const { type, name, default: defaultValue } = field;
6231
+ const { type, name, default: defaultValue, unique } = field;
6099
6232
  if (name === "id" || name === "name" || name === "email" || name === "emailVerified" || name === "image") {
6100
6233
  continue;
6101
6234
  }
6102
6235
  if (!type || !name) {
6103
6236
  continue;
6104
6237
  }
6105
- mapType(table, type, sanitizeName(name), defaultValue);
6238
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6106
6239
  }
6107
6240
  });
6108
6241
  }
package/dist/index.d.cts CHANGED
@@ -123,6 +123,14 @@ interface ExuluAgentParams {
123
123
  outputSchema?: ZodSchema;
124
124
  rateLimit?: RateLimiterRule;
125
125
  }
126
+ interface ExuluAgentToolConfig {
127
+ toolId: string;
128
+ config: {
129
+ name: string;
130
+ variable: string;
131
+ value?: any;
132
+ }[];
133
+ }
126
134
  declare class ExuluAgent {
127
135
  id: string;
128
136
  name: string;
@@ -142,17 +150,19 @@ declare class ExuluAgent {
142
150
  };
143
151
  constructor({ id, name, description, config, rateLimit, capabilities, type, evals }: ExuluAgentParams);
144
152
  tool: () => ExuluTool;
145
- generateSync: ({ messages, prompt, tools, statistics }: {
153
+ generateSync: ({ messages, prompt, tools, statistics, configs }: {
146
154
  messages?: Message[];
147
155
  prompt?: string;
148
156
  tools?: ExuluTool[];
149
157
  statistics?: ExuluStatisticParams;
158
+ configs?: ExuluAgentToolConfig[];
150
159
  }) => Promise<string>;
151
- generateStream: ({ messages, prompt, tools, statistics }: {
160
+ generateStream: ({ messages, prompt, tools, statistics, configs }: {
152
161
  messages?: Message[];
153
162
  prompt?: string;
154
163
  tools?: ExuluTool[];
155
164
  statistics?: ExuluStatisticParams;
165
+ configs?: ExuluAgentToolConfig[];
156
166
  }) => ai.StreamTextResult<Record<string, Tool>, never>;
157
167
  }
158
168
  type VectorOperationResponse = Promise<{
@@ -303,18 +313,27 @@ declare class ExuluTool {
303
313
  inputSchema?: ZodSchema;
304
314
  type: "context" | "function" | "agent";
305
315
  tool: Tool;
306
- constructor({ id, name, description, inputSchema, type, execute }: {
316
+ config: {
317
+ name: string;
318
+ description: string;
319
+ }[];
320
+ constructor({ id, name, description, inputSchema, type, execute, config }: {
307
321
  id: string;
308
322
  name: string;
309
323
  description: string;
310
324
  inputSchema?: ZodSchema;
311
325
  type: "context" | "function" | "agent";
326
+ config: {
327
+ name: string;
328
+ description: string;
329
+ }[];
312
330
  execute: (inputs: any) => Promise<any>;
313
331
  });
314
332
  }
315
333
  type ExuluContextFieldDefinition = {
316
334
  name: string;
317
335
  type: ExuluFieldTypes;
336
+ unique?: boolean;
318
337
  };
319
338
  declare class ExuluContext {
320
339
  id: string;
@@ -512,6 +531,7 @@ type User = {
512
531
  emailVerified?: string;
513
532
  type?: "api" | "user";
514
533
  anthropic_token?: string;
534
+ super_admin?: boolean;
515
535
  roles?: {
516
536
  id: string;
517
537
  role: string;
package/dist/index.d.ts CHANGED
@@ -123,6 +123,14 @@ interface ExuluAgentParams {
123
123
  outputSchema?: ZodSchema;
124
124
  rateLimit?: RateLimiterRule;
125
125
  }
126
+ interface ExuluAgentToolConfig {
127
+ toolId: string;
128
+ config: {
129
+ name: string;
130
+ variable: string;
131
+ value?: any;
132
+ }[];
133
+ }
126
134
  declare class ExuluAgent {
127
135
  id: string;
128
136
  name: string;
@@ -142,17 +150,19 @@ declare class ExuluAgent {
142
150
  };
143
151
  constructor({ id, name, description, config, rateLimit, capabilities, type, evals }: ExuluAgentParams);
144
152
  tool: () => ExuluTool;
145
- generateSync: ({ messages, prompt, tools, statistics }: {
153
+ generateSync: ({ messages, prompt, tools, statistics, configs }: {
146
154
  messages?: Message[];
147
155
  prompt?: string;
148
156
  tools?: ExuluTool[];
149
157
  statistics?: ExuluStatisticParams;
158
+ configs?: ExuluAgentToolConfig[];
150
159
  }) => Promise<string>;
151
- generateStream: ({ messages, prompt, tools, statistics }: {
160
+ generateStream: ({ messages, prompt, tools, statistics, configs }: {
152
161
  messages?: Message[];
153
162
  prompt?: string;
154
163
  tools?: ExuluTool[];
155
164
  statistics?: ExuluStatisticParams;
165
+ configs?: ExuluAgentToolConfig[];
156
166
  }) => ai.StreamTextResult<Record<string, Tool>, never>;
157
167
  }
158
168
  type VectorOperationResponse = Promise<{
@@ -303,18 +313,27 @@ declare class ExuluTool {
303
313
  inputSchema?: ZodSchema;
304
314
  type: "context" | "function" | "agent";
305
315
  tool: Tool;
306
- constructor({ id, name, description, inputSchema, type, execute }: {
316
+ config: {
317
+ name: string;
318
+ description: string;
319
+ }[];
320
+ constructor({ id, name, description, inputSchema, type, execute, config }: {
307
321
  id: string;
308
322
  name: string;
309
323
  description: string;
310
324
  inputSchema?: ZodSchema;
311
325
  type: "context" | "function" | "agent";
326
+ config: {
327
+ name: string;
328
+ description: string;
329
+ }[];
312
330
  execute: (inputs: any) => Promise<any>;
313
331
  });
314
332
  }
315
333
  type ExuluContextFieldDefinition = {
316
334
  name: string;
317
335
  type: ExuluFieldTypes;
336
+ unique?: boolean;
318
337
  };
319
338
  declare class ExuluContext {
320
339
  id: string;
@@ -512,6 +531,7 @@ type User = {
512
531
  emailVerified?: string;
513
532
  type?: "api" | "user";
514
533
  anthropic_token?: string;
534
+ super_admin?: boolean;
515
535
  roles?: {
516
536
  id: string;
517
537
  role: string;
package/dist/index.js CHANGED
@@ -261,41 +261,50 @@ var bullmqDecorator = async ({
261
261
  };
262
262
 
263
263
  // src/registry/utils/map-types.ts
264
- var mapType = (t, type, name, defaultValue) => {
264
+ var mapType = (t, type, name, defaultValue, unique) => {
265
265
  if (type === "text") {
266
266
  t.text(name);
267
+ if (unique) t.unique(name);
267
268
  return;
268
269
  }
269
270
  if (type === "longText") {
270
271
  t.text(name);
272
+ if (unique) t.unique(name);
271
273
  return;
272
274
  }
273
275
  if (type === "shortText") {
274
276
  t.string(name, 100);
277
+ if (unique) t.unique(name);
275
278
  return;
276
279
  }
277
280
  if (type === "number") {
278
281
  t.float(name);
282
+ if (unique) t.unique(name);
279
283
  return;
280
284
  }
281
285
  if (type === "boolean") {
282
286
  t.boolean(name).defaultTo(defaultValue || false);
287
+ if (unique) t.unique(name);
283
288
  return;
284
289
  }
285
290
  if (type === "code") {
286
291
  t.text(name);
292
+ if (unique) t.unique(name);
287
293
  return;
288
294
  }
289
295
  if (type === "json") {
290
296
  t.jsonb(name);
297
+ if (unique) t.unique(name);
291
298
  return;
292
299
  }
293
300
  if (type === "date") {
294
301
  t.timestamp(name);
302
+ if (unique) t.unique(name);
295
303
  return;
296
304
  }
297
305
  if (type === "uuid") {
298
306
  t.uuid(name);
307
+ if (unique) t.unique(name);
299
308
  return;
300
309
  }
301
310
  throw new Error("Invalid type: " + type);
@@ -394,7 +403,39 @@ var ExuluEvalUtils = {
394
403
  }
395
404
  };
396
405
 
406
+ // src/registry/utils/claude-messages.ts
407
+ var CLAUDE_MESSAGES = {
408
+ anthropic_token_variable_not_encrypted: `
409
+ \x1B[41m -- Anthropic token variable set by your admin is not encrypted. This poses a security risk. Please contact your admin to fix the variable used for your key. --
410
+ \x1B[0m`,
411
+ anthropic_token_variable_not_found: `
412
+ \x1B[41m -- Anthropic token variable not found. Please contact to fix the variable used for your key. --
413
+ \x1B[0m`,
414
+ authentication_error: `
415
+ \x1B[41m -- Authentication error please check your IMP token and try again. --
416
+ \x1B[0m`,
417
+ missing_body: `
418
+ \x1B[41m -- Missing body Anthropic response. --
419
+ \x1B[0m`,
420
+ missing_nextauth_secret: `
421
+ \x1B[41m -- Missing NEXTAUTH_SECRET in environment variables on the server. --
422
+ \x1B[0m`,
423
+ not_enabled: `
424
+ \x1B[31m
425
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
426
+ \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
427
+ \u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
428
+ \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
429
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
430
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
431
+ Intelligence Management Platform
432
+ \x1B[0m
433
+ \x1B[41m -- Your account has not been enabled to use Claude Code, please contact your admin or enable Claude Code in the user settings. --
434
+ \x1B[0m`
435
+ };
436
+
397
437
  // src/registry/classes.ts
438
+ import CryptoJS from "crypto-js";
398
439
  function sanitizeToolName(name) {
399
440
  if (typeof name !== "string") return "";
400
441
  let sanitized = name.replace(/[^a-zA-Z0-9_-]+/g, "_");
@@ -404,7 +445,7 @@ function sanitizeToolName(name) {
404
445
  }
405
446
  return sanitized;
406
447
  }
407
- var convertToolsArrayToObject = (tools) => {
448
+ var convertToolsArrayToObject = (tools, configs) => {
408
449
  if (!tools) return {};
409
450
  const sanitizedTools = tools ? tools.map((tool2) => ({
410
451
  ...tool2,
@@ -418,12 +459,57 @@ var convertToolsArrayToObject = (tools) => {
418
459
  };
419
460
  return {
420
461
  ...sanitizedTools?.reduce(
421
- (prev, cur) => ({ ...prev, [cur.name]: cur.tool }),
462
+ (prev, cur) => ({
463
+ ...prev,
464
+ [cur.name]: {
465
+ ...cur.tool,
466
+ execute: async (inputs, options) => {
467
+ if (!cur.tool?.execute) {
468
+ console.error("[EXULU] Tool execute function is undefined.", cur.tool);
469
+ throw new Error("Tool execute function is undefined.");
470
+ }
471
+ let config = configs?.find((config2) => config2.toolId === cur.id);
472
+ if (config) {
473
+ config = await hydrateVariables(config || []);
474
+ }
475
+ return await cur.tool.execute({
476
+ ...inputs,
477
+ // Convert config to object format if a config object
478
+ // is available, after we added the .value property
479
+ // by hydrating it from the variables table.
480
+ config: config ? config.config.reduce((acc, curr) => {
481
+ acc[curr.name] = curr.value;
482
+ return acc;
483
+ }, {}) : {}
484
+ }, options);
485
+ }
486
+ }
487
+ }),
422
488
  {}
423
489
  ),
424
490
  askForConfirmation
425
491
  };
426
492
  };
493
+ var hydrateVariables = async (tool2) => {
494
+ const { db: db3 } = await postgresClient();
495
+ const promises2 = tool2.config.map(async (toolConfig) => {
496
+ const variableName = toolConfig.variable;
497
+ const variable = await db3.from("variables").where({ name: variableName }).first();
498
+ if (!variable) {
499
+ console.error("[EXULU] Variable " + variableName + " not found.");
500
+ throw new Error("Variable " + variableName + " not found.");
501
+ }
502
+ let value = variable.value;
503
+ if (variable.encrypted) {
504
+ const bytes = CryptoJS.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
505
+ value = bytes.toString(CryptoJS.enc.Utf8);
506
+ }
507
+ toolConfig.value = value;
508
+ });
509
+ await Promise.all(promises2);
510
+ console.log("[EXULU] Variable values retrieved and added to tool config.");
511
+ return tool2;
512
+ };
427
513
  function generateSlug(name) {
428
514
  const normalized = name.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
429
515
  const lowercase = normalized.toLowerCase();
@@ -480,12 +566,13 @@ var ExuluAgent = class {
480
566
  tool = () => {
481
567
  return new ExuluTool({
482
568
  id: this.id,
483
- name: `${this.name} agent`,
569
+ name: `${this.name}`,
484
570
  type: "agent",
485
571
  inputSchema: z.object({
486
572
  prompt: z.string()
487
573
  }),
488
574
  description: `A function that calls an AI agent named: ${this.name}. The agent does the following: ${this.description}.`,
575
+ config: [],
489
576
  execute: async ({ prompt }) => {
490
577
  return await this.generateSync({
491
578
  prompt,
@@ -497,7 +584,7 @@ var ExuluAgent = class {
497
584
  }
498
585
  });
499
586
  };
500
- generateSync = async ({ messages, prompt, tools, statistics }) => {
587
+ generateSync = async ({ messages, prompt, tools, statistics, configs }) => {
501
588
  if (!this.model) {
502
589
  throw new Error("Model is required for streaming.");
503
590
  }
@@ -513,7 +600,7 @@ var ExuluAgent = class {
513
600
  messages,
514
601
  prompt,
515
602
  maxRetries: 2,
516
- tools: convertToolsArrayToObject(tools),
603
+ tools: convertToolsArrayToObject(tools, configs),
517
604
  maxSteps: 5
518
605
  });
519
606
  if (statistics) {
@@ -527,7 +614,7 @@ var ExuluAgent = class {
527
614
  }
528
615
  return text;
529
616
  };
530
- generateStream = ({ messages, prompt, tools, statistics }) => {
617
+ generateStream = ({ messages, prompt, tools, statistics, configs }) => {
531
618
  if (!this.model) {
532
619
  throw new Error("Model is required for streaming.");
533
620
  }
@@ -543,7 +630,7 @@ var ExuluAgent = class {
543
630
  prompt,
544
631
  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.",
545
632
  maxRetries: 2,
546
- tools: convertToolsArrayToObject(tools),
633
+ tools: convertToolsArrayToObject(tools, configs),
547
634
  maxSteps: 5,
548
635
  onError: (error) => console.error("[EXULU] chat stream error.", error),
549
636
  onFinish: async ({ response, usage }) => {
@@ -899,8 +986,10 @@ var ExuluTool = class {
899
986
  inputSchema;
900
987
  type;
901
988
  tool;
902
- constructor({ id, name, description, inputSchema, type, execute: execute2 }) {
989
+ config;
990
+ constructor({ id, name, description, inputSchema, type, execute: execute2, config }) {
903
991
  this.id = id;
992
+ this.config = config;
904
993
  this.name = name;
905
994
  this.description = description;
906
995
  this.inputSchema = inputSchema;
@@ -1308,11 +1397,11 @@ var ExuluContext = class {
1308
1397
  table.text("source");
1309
1398
  table.timestamp("embeddings_updated_at");
1310
1399
  for (const field of this.fields) {
1311
- const { type, name } = field;
1400
+ const { type, name, unique } = field;
1312
1401
  if (!type || !name) {
1313
1402
  continue;
1314
1403
  }
1315
- mapType(table, type, sanitizeName(name));
1404
+ mapType(table, type, sanitizeName(name), void 0, unique);
1316
1405
  }
1317
1406
  table.timestamps(true, true);
1318
1407
  });
@@ -1334,11 +1423,12 @@ var ExuluContext = class {
1334
1423
  tool = () => {
1335
1424
  return new ExuluTool({
1336
1425
  id: this.id,
1337
- name: `${this.name} context`,
1426
+ name: `${this.name}`,
1338
1427
  type: "context",
1339
1428
  inputSchema: z.object({
1340
1429
  query: z.string()
1341
1430
  }),
1431
+ config: [],
1342
1432
  description: `Gets information from the context called: ${this.name}. The context description is: ${this.description}.`,
1343
1433
  execute: async ({ query }) => {
1344
1434
  return await this.getItems({
@@ -1806,7 +1896,7 @@ import "reflect-metadata";
1806
1896
  import { makeExecutableSchema } from "@graphql-tools/schema";
1807
1897
  import GraphQLJSON from "graphql-type-json";
1808
1898
  import { GraphQLScalarType, Kind } from "graphql";
1809
- import CryptoJS from "crypto-js";
1899
+ import CryptoJS2 from "crypto-js";
1810
1900
  var GraphQLDate = new GraphQLScalarType({
1811
1901
  name: "Date",
1812
1902
  description: "Date custom scalar type",
@@ -1972,8 +2062,9 @@ function createMutations(table) {
1972
2062
  return results[0];
1973
2063
  },
1974
2064
  [`${tableNamePlural}UpdateOne`]: async (_, args, context, info) => {
1975
- const { db: db3 } = context;
2065
+ const { db: db3, req } = context;
1976
2066
  let { where, input } = args;
2067
+ await validateSuperAdminPermission(tableNamePlural, input, req);
1977
2068
  input = encryptSensitiveFields(input);
1978
2069
  await db3(tableNamePlural).where(where).update({
1979
2070
  ...input,
@@ -1984,9 +2075,10 @@ function createMutations(table) {
1984
2075
  return result;
1985
2076
  },
1986
2077
  [`${tableNamePlural}UpdateOneById`]: async (_, args, context, info) => {
2078
+ const { db: db3, req } = context;
1987
2079
  let { id, input } = args;
2080
+ await validateSuperAdminPermission(tableNamePlural, input, req);
1988
2081
  input = encryptSensitiveFields(input);
1989
- const { db: db3 } = context;
1990
2082
  await db3(tableNamePlural).where({ id }).update({
1991
2083
  ...input,
1992
2084
  updatedAt: /* @__PURE__ */ new Date()
@@ -2123,6 +2215,7 @@ function createQueries(table) {
2123
2215
  };
2124
2216
  }
2125
2217
  function createSDL(tables) {
2218
+ console.log("[EXULU] Creating SDL");
2126
2219
  let typeDefs = `
2127
2220
  scalar JSON
2128
2221
  scalar Date
@@ -2138,6 +2231,7 @@ function createSDL(tables) {
2138
2231
  const tableNamePlural = table.name.plural.toLowerCase();
2139
2232
  const tableNameSingular = table.name.singular.toLowerCase();
2140
2233
  const tableNameSingularUpperCaseFirst = table.name.singular.charAt(0).toUpperCase() + table.name.singular.slice(1);
2234
+ console.log("[EXULU] Adding table >>>>>", tableNamePlural);
2141
2235
  typeDefs += `
2142
2236
  ${tableNameSingular}ById(id: ID!): ${tableNameSingular}
2143
2237
  ${tableNamePlural}Pagination(limit: Int, page: Int, filters: [Filter${tableNameSingularUpperCaseFirst}], sort: SortBy): ${tableNameSingularUpperCaseFirst}PaginationResult
@@ -2212,15 +2306,23 @@ type JobStatistics {
2212
2306
  console.log("\n");
2213
2307
  return schema;
2214
2308
  }
2215
- var sensitiveFields = ["anthropic_token"];
2216
2309
  var encryptSensitiveFields = (input) => {
2217
- sensitiveFields.forEach((field) => {
2218
- if (input[field]) {
2219
- input[field] = CryptoJS.AES.encrypt(input[field], process.env.NEXTAUTH_SECRET).toString();
2220
- }
2221
- });
2310
+ if (input.value && input.encrypted === true) {
2311
+ input.value = CryptoJS2.AES.encrypt(input.value, process.env.NEXTAUTH_SECRET).toString();
2312
+ }
2222
2313
  return input;
2223
2314
  };
2315
+ var validateSuperAdminPermission = async (tableNamePlural, input, req) => {
2316
+ if (tableNamePlural === "users" && input.super_admin !== void 0) {
2317
+ const authResult = await requestValidators.authenticate(req);
2318
+ if (authResult.error || !authResult.user) {
2319
+ throw new Error("Authentication failed");
2320
+ }
2321
+ if (!authResult.user.super_admin) {
2322
+ throw new Error("Only super administrators can modify super_admin status");
2323
+ }
2324
+ }
2325
+ };
2224
2326
 
2225
2327
  // src/registry/routes.ts
2226
2328
  import { expressMiddleware } from "@as-integrations/express5";
@@ -2558,6 +2660,29 @@ var agentsSchema = {
2558
2660
  }
2559
2661
  ]
2560
2662
  };
2663
+ var variablesSchema = {
2664
+ name: {
2665
+ plural: "variables",
2666
+ singular: "variable"
2667
+ },
2668
+ fields: [
2669
+ {
2670
+ name: "name",
2671
+ type: "text",
2672
+ index: true,
2673
+ unique: true
2674
+ },
2675
+ {
2676
+ name: "value",
2677
+ type: "longText"
2678
+ },
2679
+ {
2680
+ name: "encrypted",
2681
+ type: "boolean",
2682
+ default: false
2683
+ }
2684
+ ]
2685
+ };
2561
2686
 
2562
2687
  // src/registry/uppy.ts
2563
2688
  import "express";
@@ -2953,34 +3078,7 @@ var createUppyRoutes = async (app) => {
2953
3078
  // src/registry/routes.ts
2954
3079
  import { InMemoryLRUCache } from "@apollo/utils.keyvaluecache";
2955
3080
  import bodyParser from "body-parser";
2956
- import CryptoJS2 from "crypto-js";
2957
-
2958
- // src/registry/utils/claude-messages.ts
2959
- var CLAUDE_MESSAGES = {
2960
- authentication_error: `
2961
- \x1B[41m -- Authentication error please check your IMP token and try again. --
2962
- \x1B[0m`,
2963
- missing_body: `
2964
- \x1B[41m -- Missing body Anthropic response. --
2965
- \x1B[0m`,
2966
- missing_nextauth_secret: `
2967
- \x1B[41m -- Missing NEXTAUTH_SECRET in environment variables on the server. --
2968
- \x1B[0m`,
2969
- not_enabled: `
2970
- \x1B[31m
2971
- \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
2972
- \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
2973
- \u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
2974
- \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
2975
- \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
2976
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
2977
- Intelligence Management Platform
2978
- \x1B[0m
2979
- \x1B[41m -- Your account has not been enabled to use Claude Code, please contact your admin or enable Claude Code in the user settings. --
2980
- \x1B[0m`
2981
- };
2982
-
2983
- // src/registry/routes.ts
3081
+ import CryptoJS3 from "crypto-js";
2984
3082
  var REQUEST_SIZE_LIMIT = "50mb";
2985
3083
  var global_queues = {
2986
3084
  logs_cleaner: "logs-cleaner"
@@ -3087,7 +3185,7 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
3087
3185
  } else {
3088
3186
  console.log("===========================", "[EXULU] no redis server configured, not setting up recurring jobs.", "===========================");
3089
3187
  }
3090
- const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, agentSessionsSchema, agentMessagesSchema]);
3188
+ const schema = createSDL([usersSchema, rolesSchema, agentsSchema, jobsSchema, workflowSchema, evalResultsSchema, agentSessionsSchema, agentMessagesSchema, variablesSchema]);
3091
3189
  console.log("[EXULU] graphql server");
3092
3190
  const server = new ApolloServer({
3093
3191
  cache: new InMemoryLRUCache(),
@@ -3922,6 +4020,7 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
3922
4020
  const result = agent.generateStream({
3923
4021
  messages: req.body.messages,
3924
4022
  tools: enabledTools,
4023
+ configs: agentInstance.tools,
3925
4024
  statistics: {
3926
4025
  label: agent.name,
3927
4026
  trigger: "agent"
@@ -3932,7 +4031,8 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
3932
4031
  } else {
3933
4032
  const response = await agent.generateSync({
3934
4033
  messages: req.body.messages,
3935
- tools: enabledTools.map(),
4034
+ tools: enabledTools.map((tool2) => tool2.tool),
4035
+ configs: agentInstance.tools,
3936
4036
  statistics: {
3937
4037
  label: agent.name,
3938
4038
  trigger: "agent"
@@ -4049,8 +4149,25 @@ var createExpressRoutes = async (app, agents, tools, workflows, contexts) => {
4049
4149
  res.end(Buffer.from(arrayBuffer));
4050
4150
  return;
4051
4151
  }
4052
- const bytes = CryptoJS2.AES.decrypt(authenticationResult.user?.anthropic_token, process.env.NEXTAUTH_SECRET);
4053
- const anthropicApiKey = bytes.toString(CryptoJS2.enc.Utf8);
4152
+ const variableName = authenticationResult.user.anthropic_token;
4153
+ const variable = await db3.from("variables").where({ name: variableName }).first();
4154
+ if (!variable) {
4155
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.anthropic_token_variable_not_found);
4156
+ res.setHeader("Content-Type", "application/json");
4157
+ res.end(Buffer.from(arrayBuffer));
4158
+ return;
4159
+ }
4160
+ let anthropicApiKey = variable.value;
4161
+ if (!variable.encrypted) {
4162
+ const arrayBuffer = createCustomAnthropicStreamingMessage(CLAUDE_MESSAGES.anthropic_token_variable_not_encrypted);
4163
+ res.setHeader("Content-Type", "application/json");
4164
+ res.end(Buffer.from(arrayBuffer));
4165
+ return;
4166
+ }
4167
+ if (variable.encrypted) {
4168
+ const bytes = CryptoJS3.AES.decrypt(variable.value, process.env.NEXTAUTH_SECRET);
4169
+ anthropicApiKey = bytes.toString(CryptoJS3.enc.Utf8);
4170
+ }
4054
4171
  const headers = {
4055
4172
  "x-api-key": anthropicApiKey,
4056
4173
  "anthropic-version": "2023-06-01",
@@ -5925,6 +6042,7 @@ var generateApiKey = async (name, email) => {
5925
6042
 
5926
6043
  // src/postgres/init-db.ts
5927
6044
  var up = async function(knex) {
6045
+ console.log("[EXULU] Database up.");
5928
6046
  if (!await knex.schema.hasTable("agent_sessions")) {
5929
6047
  console.log("[EXULU] Creating agent_sessions table.");
5930
6048
  await knex.schema.createTable("agent_sessions", (table) => {
@@ -5932,11 +6050,11 @@ var up = async function(knex) {
5932
6050
  table.timestamp("createdAt").defaultTo(knex.fn.now());
5933
6051
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
5934
6052
  for (const field of agentSessionsSchema.fields) {
5935
- const { type, name, default: defaultValue } = field;
6053
+ const { type, name, default: defaultValue, unique } = field;
5936
6054
  if (!type || !name) {
5937
6055
  continue;
5938
6056
  }
5939
- mapType(table, type, sanitizeName(name), defaultValue);
6057
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
5940
6058
  }
5941
6059
  });
5942
6060
  }
@@ -5947,11 +6065,11 @@ var up = async function(knex) {
5947
6065
  table.timestamp("createdAt").defaultTo(knex.fn.now());
5948
6066
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
5949
6067
  for (const field of agentMessagesSchema.fields) {
5950
- const { type, name, default: defaultValue } = field;
6068
+ const { type, name, default: defaultValue, unique } = field;
5951
6069
  if (!type || !name) {
5952
6070
  continue;
5953
6071
  }
5954
- mapType(table, type, sanitizeName(name), defaultValue);
6072
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
5955
6073
  }
5956
6074
  });
5957
6075
  }
@@ -5962,11 +6080,11 @@ var up = async function(knex) {
5962
6080
  table.timestamp("createdAt").defaultTo(knex.fn.now());
5963
6081
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
5964
6082
  for (const field of rolesSchema.fields) {
5965
- const { type, name, default: defaultValue } = field;
6083
+ const { type, name, default: defaultValue, unique } = field;
5966
6084
  if (!type || !name) {
5967
6085
  continue;
5968
6086
  }
5969
- mapType(table, type, sanitizeName(name), defaultValue);
6087
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
5970
6088
  }
5971
6089
  });
5972
6090
  }
@@ -5977,11 +6095,11 @@ var up = async function(knex) {
5977
6095
  table.timestamp("createdAt").defaultTo(knex.fn.now());
5978
6096
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
5979
6097
  for (const field of evalResultsSchema.fields) {
5980
- const { type, name, default: defaultValue } = field;
6098
+ const { type, name, default: defaultValue, unique } = field;
5981
6099
  if (!type || !name) {
5982
6100
  continue;
5983
6101
  }
5984
- mapType(table, type, sanitizeName(name), defaultValue);
6102
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
5985
6103
  }
5986
6104
  });
5987
6105
  }
@@ -5992,11 +6110,11 @@ var up = async function(knex) {
5992
6110
  table.timestamp("createdAt").defaultTo(knex.fn.now());
5993
6111
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
5994
6112
  for (const field of statisticsSchema.fields) {
5995
- const { type, name, default: defaultValue } = field;
6113
+ const { type, name, default: defaultValue, unique } = field;
5996
6114
  if (!type || !name) {
5997
6115
  continue;
5998
6116
  }
5999
- mapType(table, type, sanitizeName(name), defaultValue);
6117
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6000
6118
  }
6001
6119
  });
6002
6120
  }
@@ -6007,11 +6125,11 @@ var up = async function(knex) {
6007
6125
  table.timestamp("createdAt").defaultTo(knex.fn.now());
6008
6126
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
6009
6127
  for (const field of jobsSchema.fields) {
6010
- const { type, name, default: defaultValue } = field;
6128
+ const { type, name, default: defaultValue, unique } = field;
6011
6129
  if (!type || !name) {
6012
6130
  continue;
6013
6131
  }
6014
- mapType(table, type, sanitizeName(name), defaultValue);
6132
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6015
6133
  }
6016
6134
  });
6017
6135
  }
@@ -6022,11 +6140,26 @@ var up = async function(knex) {
6022
6140
  table.timestamp("createdAt").defaultTo(knex.fn.now());
6023
6141
  table.timestamp("updatedAt").defaultTo(knex.fn.now());
6024
6142
  for (const field of agentsSchema.fields) {
6025
- const { type, name, default: defaultValue } = field;
6143
+ const { type, name, default: defaultValue, unique } = field;
6144
+ if (!type || !name) {
6145
+ continue;
6146
+ }
6147
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6148
+ }
6149
+ });
6150
+ }
6151
+ if (!await knex.schema.hasTable("variables")) {
6152
+ console.log("[EXULU] Creating variables table.");
6153
+ await knex.schema.createTable("variables", (table) => {
6154
+ table.uuid("id").primary().defaultTo(knex.fn.uuid());
6155
+ table.timestamp("createdAt").defaultTo(knex.fn.now());
6156
+ table.timestamp("updatedAt").defaultTo(knex.fn.now());
6157
+ for (const field of variablesSchema.fields) {
6158
+ const { type, name, default: defaultValue, unique } = field;
6026
6159
  if (!type || !name) {
6027
6160
  continue;
6028
6161
  }
6029
- mapType(table, type, sanitizeName(name), defaultValue);
6162
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6030
6163
  }
6031
6164
  });
6032
6165
  }
@@ -6052,14 +6185,14 @@ var up = async function(knex) {
6052
6185
  table.text("image");
6053
6186
  for (const field of usersSchema.fields) {
6054
6187
  console.log("[EXULU] field", field);
6055
- const { type, name, default: defaultValue } = field;
6188
+ const { type, name, default: defaultValue, unique } = field;
6056
6189
  if (name === "id" || name === "name" || name === "email" || name === "emailVerified" || name === "image") {
6057
6190
  continue;
6058
6191
  }
6059
6192
  if (!type || !name) {
6060
6193
  continue;
6061
6194
  }
6062
- mapType(table, type, sanitizeName(name), defaultValue);
6195
+ mapType(table, type, sanitizeName(name), defaultValue, unique);
6063
6196
  }
6064
6197
  });
6065
6198
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exulu/backend",
3
3
  "author": "Qventu Bv.",
4
- "version": "1.6.2",
4
+ "version": "1.7.0",
5
5
  "main": "./dist/index.js",
6
6
  "private": false,
7
7
  "publishConfig": {
@@ -4,6 +4,7 @@ export type User = {
4
4
  emailVerified?: string;
5
5
  type?: "api" | "user"
6
6
  anthropic_token?: string;
7
+ super_admin?: boolean;
7
8
  roles?: {
8
9
  id: string;
9
10
  role: string;