@exulu/backend 1.22.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/index.ts
9
2
  import "dotenv/config";
10
3
 
@@ -762,6 +755,10 @@ var agentSessionsSchema = {
762
755
  {
763
756
  name: "title",
764
757
  type: "text"
758
+ },
759
+ {
760
+ name: "project",
761
+ type: "uuid"
765
762
  }
766
763
  ]
767
764
  };
@@ -839,6 +836,37 @@ var workflowTemplatesSchema = {
839
836
  }
840
837
  ]
841
838
  };
839
+ var projectsSchema = {
840
+ type: "projects",
841
+ name: {
842
+ plural: "projects",
843
+ singular: "project"
844
+ },
845
+ RBAC: true,
846
+ fields: [
847
+ {
848
+ name: "name",
849
+ type: "text",
850
+ required: true
851
+ },
852
+ {
853
+ name: "description",
854
+ type: "text"
855
+ },
856
+ {
857
+ name: "image",
858
+ type: "text"
859
+ },
860
+ {
861
+ name: "custom_instructions",
862
+ type: "longText"
863
+ },
864
+ {
865
+ name: "context_files",
866
+ type: "json"
867
+ }
868
+ ]
869
+ };
842
870
  var agentsSchema = {
843
871
  type: "agents",
844
872
  name: {
@@ -898,6 +926,10 @@ var usersSchema = {
898
926
  name: "favourite_agents",
899
927
  type: "json"
900
928
  },
929
+ {
930
+ name: "favourite_projects",
931
+ type: "json"
932
+ },
901
933
  {
902
934
  name: "firstname",
903
935
  type: "text"
@@ -1185,6 +1217,10 @@ var rbacSchema = {
1185
1217
  name: "user_id",
1186
1218
  type: "number"
1187
1219
  },
1220
+ {
1221
+ name: "project_id",
1222
+ type: "uuid"
1223
+ },
1188
1224
  {
1189
1225
  name: "rights",
1190
1226
  type: "text",
@@ -1216,6 +1252,7 @@ var coreSchemas = {
1216
1252
  agentsSchema: () => addRBACfields(agentsSchema),
1217
1253
  agentMessagesSchema: () => addRBACfields(agentMessagesSchema),
1218
1254
  agentSessionsSchema: () => addRBACfields(agentSessionsSchema),
1255
+ projectsSchema: () => addRBACfields(projectsSchema),
1219
1256
  usersSchema: () => addRBACfields(usersSchema),
1220
1257
  rolesSchema: () => addRBACfields(rolesSchema),
1221
1258
  statisticsSchema: () => addRBACfields(statisticsSchema),
@@ -1454,7 +1491,7 @@ var getRequestedFields = (info) => {
1454
1491
  return fields.filter((field) => field !== "pageInfo" && field !== "items" && field !== "RBAC");
1455
1492
  };
1456
1493
  var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRbacRecords) => {
1457
- const { users = [], roles = [] } = rbacData;
1494
+ const { users = [], roles = [], projects = [] } = rbacData;
1458
1495
  if (!existingRbacRecords) {
1459
1496
  existingRbacRecords = await db3.from("rbac").where({
1460
1497
  entity: entityName,
@@ -1463,18 +1500,25 @@ var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRba
1463
1500
  }
1464
1501
  const newUserRecords = new Set(users.map((u) => `${u.id}:${u.rights}`));
1465
1502
  const newRoleRecords = new Set(roles.map((r) => `${r.id}:${r.rights}`));
1503
+ const newProjectRecords = new Set(projects.map((p) => `${p.id}:${p.rights}`));
1466
1504
  const existingUserRecords = new Set(existingRbacRecords.filter((r) => r.access_type === "User").map((r) => `${r.user_id}:${r.rights}`));
1467
1505
  const existingRoleRecords = new Set(existingRbacRecords.filter((r) => r.access_type === "Role").map((r) => `${r.role_id}:${r.rights}`));
1506
+ const existingProjectRecords = new Set(existingRbacRecords.filter((r) => r.access_type === "Project").map((r) => `${r.project_id}:${r.rights}`));
1468
1507
  const usersToCreate = users.filter((u) => !existingUserRecords.has(`${u.id}:${u.rights}`));
1469
1508
  const rolesToCreate = roles.filter((r) => !existingRoleRecords.has(`${r.id}:${r.rights}`));
1509
+ const projectsToCreate = projects.filter((p) => !existingProjectRecords.has(`${p.id}:${p.rights}`));
1470
1510
  const usersToRemove = existingRbacRecords.filter((r) => r.access_type === "User" && !newUserRecords.has(`${r.user_id}:${r.rights}`));
1471
1511
  const rolesToRemove = existingRbacRecords.filter((r) => r.access_type === "Role" && !newRoleRecords.has(`${r.role_id}:${r.rights}`));
1512
+ const projectsToRemove = existingRbacRecords.filter((r) => r.access_type === "Project" && !newProjectRecords.has(`${r.project_id}:${r.rights}`));
1472
1513
  if (usersToRemove.length > 0) {
1473
1514
  await db3.from("rbac").whereIn("id", usersToRemove.map((r) => r.id)).del();
1474
1515
  }
1475
1516
  if (rolesToRemove.length > 0) {
1476
1517
  await db3.from("rbac").whereIn("id", rolesToRemove.map((r) => r.id)).del();
1477
1518
  }
1519
+ if (projectsToRemove.length > 0) {
1520
+ await db3.from("rbac").whereIn("id", projectsToRemove.map((r) => r.id)).del();
1521
+ }
1478
1522
  const recordsToInsert = [];
1479
1523
  usersToCreate.forEach((user) => {
1480
1524
  recordsToInsert.push({
@@ -1498,6 +1542,17 @@ var handleRBACUpdate = async (db3, entityName, resourceId, rbacData, existingRba
1498
1542
  updatedAt: /* @__PURE__ */ new Date()
1499
1543
  });
1500
1544
  });
1545
+ projectsToCreate.forEach((project) => {
1546
+ recordsToInsert.push({
1547
+ entity: entityName,
1548
+ access_type: "Project",
1549
+ target_resource_id: resourceId,
1550
+ project_id: project.id,
1551
+ rights: project.rights,
1552
+ createdAt: /* @__PURE__ */ new Date(),
1553
+ updatedAt: /* @__PURE__ */ new Date()
1554
+ });
1555
+ });
1501
1556
  if (recordsToInsert.length > 0) {
1502
1557
  await db3.from("rbac").insert(recordsToInsert);
1503
1558
  }
@@ -1562,6 +1617,52 @@ function createMutations(table, agents, contexts, tools) {
1562
1617
  }
1563
1618
  throw new Error("Insufficient role permissions to edit this record");
1564
1619
  }
1620
+ if (record.rights_mode === "projects") {
1621
+ const projects = await db3.from("rbac").where({
1622
+ entity: table.name.singular,
1623
+ target_resource_id: id,
1624
+ access_type: "Project",
1625
+ rights: "write"
1626
+ });
1627
+ if (projects.length === 0) {
1628
+ throw new Error("Entity ${table.name.singular} has its rights mode set to projects, but is not shared with any projects.");
1629
+ }
1630
+ const checks = await Promise.all(projects.map(async (project) => {
1631
+ if (project.rights_mode === "private" && project.created_by !== user.id) {
1632
+ return false;
1633
+ }
1634
+ if (project.rights_mode === "users") {
1635
+ const rbacRecord = await db3.from("rbac").where({
1636
+ entity: "project",
1637
+ target_resource_id: project.id,
1638
+ access_type: "User",
1639
+ user_id: user.id,
1640
+ rights: "write"
1641
+ }).first();
1642
+ if (rbacRecord) {
1643
+ return true;
1644
+ }
1645
+ return false;
1646
+ }
1647
+ if (record.rights_mode === "roles" && user.role) {
1648
+ const rbacRecord = await db3.from("rbac").where({
1649
+ entity: "project",
1650
+ target_resource_id: project.id,
1651
+ access_type: "Role",
1652
+ role_id: user.role,
1653
+ rights: "write"
1654
+ }).first();
1655
+ if (rbacRecord) {
1656
+ return true;
1657
+ }
1658
+ return false;
1659
+ }
1660
+ return false;
1661
+ }));
1662
+ if (checks.some((check) => check)) {
1663
+ return true;
1664
+ }
1665
+ }
1565
1666
  throw new Error("Insufficient permissions to edit this record");
1566
1667
  } catch (error) {
1567
1668
  console.error("Write access validation error:", error);
@@ -2549,13 +2650,16 @@ var RBACResolver = async (db3, entityName, resourceId, rights_mode) => {
2549
2650
  }).select("*");
2550
2651
  const users = rbacRecords.filter((r) => r.access_type === "User")?.map((r) => ({ id: r.user_id, rights: r.rights }));
2551
2652
  const roles = rbacRecords.filter((r) => r.access_type === "Role")?.map((r) => ({ id: r.role_id, rights: r.rights }));
2653
+ const projects = rbacRecords.filter((r) => r.access_type === "Project")?.map((r) => ({ id: r.project_id, rights: r.rights }));
2552
2654
  let type = rights_mode || "private";
2553
2655
  if (type === "users" && users.length === 0) type = "private";
2554
2656
  if (type === "roles" && roles.length === 0) type = "private";
2657
+ if (type === "projects" && projects.length === 0) type = "private";
2555
2658
  return {
2556
2659
  type,
2557
2660
  users,
2558
- roles
2661
+ roles,
2662
+ projects
2559
2663
  };
2560
2664
  };
2561
2665
  var contextToTableDefinition = (context) => {
@@ -2648,6 +2752,7 @@ function createSDL(tables, contexts, agents, tools) {
2648
2752
  type: String!
2649
2753
  users: [RBACUser!]
2650
2754
  roles: [RBACRole!]
2755
+ projects: [RBACProject!]
2651
2756
  }
2652
2757
 
2653
2758
  type RBACUser {
@@ -2660,9 +2765,15 @@ function createSDL(tables, contexts, agents, tools) {
2660
2765
  rights: String!
2661
2766
  }
2662
2767
 
2768
+ type RBACProject {
2769
+ id: ID!
2770
+ rights: String!
2771
+ }
2772
+
2663
2773
  input RBACInput {
2664
2774
  users: [RBACUserInput!]
2665
2775
  roles: [RBACRoleInput!]
2776
+ projects: [RBACProjectInput!]
2666
2777
  }
2667
2778
 
2668
2779
  input RBACUserInput {
@@ -2675,6 +2786,11 @@ function createSDL(tables, contexts, agents, tools) {
2675
2786
  rights: String!
2676
2787
  }
2677
2788
 
2789
+ input RBACProjectInput {
2790
+ id: ID!
2791
+ rights: String!
2792
+ }
2793
+
2678
2794
  type Query {
2679
2795
  `;
2680
2796
  let mutationDefs = `
@@ -3085,21 +3201,6 @@ function generateSlug(name) {
3085
3201
  const slug = lowercase.replace(/[\W_]+/g, "-").replace(/^-+|-+$/g, "");
3086
3202
  return slug;
3087
3203
  }
3088
- var ExuluZodFileType = ({
3089
- name,
3090
- label,
3091
- description,
3092
- allowedFileTypes
3093
- }) => {
3094
- return z.object({
3095
- [`exulu_file_${name}`]: z.string().describe(JSON.stringify({
3096
- label,
3097
- isFile: true,
3098
- description,
3099
- allowedFileTypes
3100
- }))
3101
- });
3102
- };
3103
3204
  var ExuluAgent2 = class {
3104
3205
  // Must begin with a letter (a-z) or underscore (_). Subsequent characters in a name can be letters, digits (0-9), or
3105
3206
  // underscores and be a max length of 80 characters and at least 5 characters long.
@@ -4200,23 +4301,24 @@ import { expressMiddleware } from "@as-integrations/express5";
4200
4301
 
4201
4302
  // src/registry/uppy.ts
4202
4303
  import "express";
4203
- var createUppyRoutes = async (app) => {
4204
- const {
4205
- S3Client,
4206
- AbortMultipartUploadCommand,
4207
- CompleteMultipartUploadCommand,
4208
- CreateMultipartUploadCommand,
4209
- GetObjectCommand,
4210
- ListPartsCommand,
4211
- PutObjectCommand,
4212
- UploadPartCommand,
4213
- ListObjectsV2Command
4214
- } = __require("@aws-sdk/client-s3");
4215
- const { getSignedUrl } = __require("@aws-sdk/s3-request-presigner");
4216
- const {
4217
- STSClient,
4218
- GetFederationTokenCommand
4219
- } = __require("@aws-sdk/client-sts");
4304
+ import {
4305
+ S3Client,
4306
+ AbortMultipartUploadCommand,
4307
+ CompleteMultipartUploadCommand,
4308
+ CreateMultipartUploadCommand,
4309
+ GetObjectCommand,
4310
+ ListPartsCommand,
4311
+ PutObjectCommand,
4312
+ UploadPartCommand,
4313
+ ListObjectsV2Command
4314
+ } from "@aws-sdk/client-s3";
4315
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
4316
+ import {
4317
+ STSClient,
4318
+ GetFederationTokenCommand
4319
+ } from "@aws-sdk/client-sts";
4320
+ import { randomUUID } from "crypto";
4321
+ var createUppyRoutes = async (app, config) => {
4220
4322
  const policy = {
4221
4323
  Version: "2012-10-17",
4222
4324
  Statement: [
@@ -4226,8 +4328,8 @@ var createUppyRoutes = async (app) => {
4226
4328
  "s3:PutObject"
4227
4329
  ],
4228
4330
  Resource: [
4229
- `arn:aws:s3:::${process.env.COMPANION_S3_BUCKET}/*`,
4230
- `arn:aws:s3:::${process.env.COMPANION_S3_BUCKET}`
4331
+ `arn:aws:s3:::${config.fileUploads.s3Bucket}/*`,
4332
+ `arn:aws:s3:::${config.fileUploads.s3Bucket}`
4231
4333
  ]
4232
4334
  }
4233
4335
  ]
@@ -4237,25 +4339,25 @@ var createUppyRoutes = async (app) => {
4237
4339
  const expiresIn = 60 * 60 * 24 * 1;
4238
4340
  function getS3Client() {
4239
4341
  s3Client ??= new S3Client({
4240
- region: process.env.COMPANION_S3_REGION,
4241
- ...process.env.COMPANION_S3_ENDPOINT && {
4342
+ region: config.fileUploads.s3region,
4343
+ ...config.fileUploads.s3endpoint && {
4242
4344
  forcePathStyle: true,
4243
- endpoint: process.env.COMPANION_S3_ENDPOINT
4345
+ endpoint: config.fileUploads.s3endpoint
4244
4346
  },
4245
4347
  credentials: {
4246
- accessKeyId: process.env.COMPANION_S3_KEY,
4247
- secretAccessKey: process.env.COMPANION_S3_SECRET
4348
+ accessKeyId: config.fileUploads.s3key,
4349
+ secretAccessKey: config.fileUploads.s3secret
4248
4350
  }
4249
4351
  });
4250
4352
  return s3Client;
4251
4353
  }
4252
4354
  function getSTSClient() {
4253
4355
  stsClient ??= new STSClient({
4254
- region: process.env.COMPANION_S3_REGION,
4255
- ...process.env.COMPANION_S3_ENDPOINT && { endpoint: process.env.COMPANION_S3_ENDPOINT },
4356
+ region: config.fileUploads.s3region,
4357
+ ...config.fileUploads.s3endpoint && { endpoint: config.fileUploads.s3endpoint },
4256
4358
  credentials: {
4257
- accessKeyId: process.env.COMPANION_S3_KEY,
4258
- secretAccessKey: process.env.COMPANION_S3_SECRET
4359
+ accessKeyId: config.fileUploads.s3key,
4360
+ secretAccessKey: config.fileUploads.s3secret
4259
4361
  }
4260
4362
  });
4261
4363
  return stsClient;
@@ -4288,7 +4390,7 @@ var createUppyRoutes = async (app) => {
4288
4390
  }
4289
4391
  try {
4290
4392
  const command = new ListObjectsV2Command({
4291
- Bucket: process.env.COMPANION_S3_BUCKET,
4393
+ Bucket: config.fileUploads.s3Bucket,
4292
4394
  Prefix: prefix,
4293
4395
  MaxKeys: 1e3
4294
4396
  // Adjust this value based on your needs
@@ -4340,7 +4442,7 @@ var createUppyRoutes = async (app) => {
4340
4442
  const url = await getSignedUrl(
4341
4443
  getS3Client(),
4342
4444
  new GetObjectCommand({
4343
- Bucket: process.env.COMPANION_S3_BUCKET,
4445
+ Bucket: config.fileUploads.s3Bucket,
4344
4446
  Key: key
4345
4447
  }),
4346
4448
  { expiresIn }
@@ -4364,8 +4466,8 @@ var createUppyRoutes = async (app) => {
4364
4466
  res.setHeader("Cache-Control", `public,max-age=${expiresIn}`);
4365
4467
  res.json({
4366
4468
  credentials: response.Credentials,
4367
- bucket: process.env.COMPANION_S3_BUCKET,
4368
- region: process.env.COMPANION_S3_REGION
4469
+ bucket: config.fileUploads.s3Bucket,
4470
+ region: config.fileUploads.s3region
4369
4471
  });
4370
4472
  }, next);
4371
4473
  });
@@ -4382,7 +4484,7 @@ var createUppyRoutes = async (app) => {
4382
4484
  contentType: params.type
4383
4485
  };
4384
4486
  };
4385
- const generateS3Key = (filename) => `${crypto.randomUUID()}-${filename}`;
4487
+ const generateS3Key = (filename) => `${randomUUID()}-${filename}`;
4386
4488
  const signOnServer = async (req, res, next) => {
4387
4489
  const apikey = req.headers["exulu-api-key"] || null;
4388
4490
  const { db: db3 } = await postgresClient();
@@ -4411,7 +4513,7 @@ var createUppyRoutes = async (app) => {
4411
4513
  getSignedUrl(
4412
4514
  getS3Client(),
4413
4515
  new PutObjectCommand({
4414
- Bucket: process.env.COMPANION_S3_BUCKET,
4516
+ Bucket: config.fileUploads.s3Bucket,
4415
4517
  Key: folder + key,
4416
4518
  ContentType: contentType
4417
4519
  }),
@@ -4419,6 +4521,7 @@ var createUppyRoutes = async (app) => {
4419
4521
  ).then((url) => {
4420
4522
  res.setHeader("Access-Control-Allow-Origin", "*");
4421
4523
  res.json({
4524
+ key,
4422
4525
  url,
4423
4526
  method: "PUT"
4424
4527
  });
@@ -4455,7 +4558,7 @@ var createUppyRoutes = async (app) => {
4455
4558
  if (typeof type !== "string") {
4456
4559
  return res.status(400).json({ error: "s3: content type must be a string" });
4457
4560
  }
4458
- const key = `${crypto.randomUUID()}-${filename}`;
4561
+ const key = `${randomUUID()}-${filename}`;
4459
4562
  let folder = "";
4460
4563
  if (authenticationResult.user.type === "api") {
4461
4564
  folder = `api/`;
@@ -4463,7 +4566,7 @@ var createUppyRoutes = async (app) => {
4463
4566
  folder = `${authenticationResult.user.id}/`;
4464
4567
  }
4465
4568
  const params = {
4466
- Bucket: process.env.COMPANION_S3_BUCKET,
4569
+ Bucket: config.fileUploads.s3Bucket,
4467
4570
  Key: folder + key,
4468
4571
  ContentType: type,
4469
4572
  Metadata: metadata
@@ -4476,7 +4579,7 @@ var createUppyRoutes = async (app) => {
4476
4579
  }
4477
4580
  res.setHeader("Access-Control-Allow-Origin", "*");
4478
4581
  res.json({
4479
- key: data.Key,
4582
+ key,
4480
4583
  uploadId: data.UploadId
4481
4584
  });
4482
4585
  });
@@ -4495,10 +4598,10 @@ var createUppyRoutes = async (app) => {
4495
4598
  return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' });
4496
4599
  }
4497
4600
  return getSignedUrl(getS3Client(), new UploadPartCommand({
4498
- Bucket: process.env.COMPANION_S3_BUCKET,
4601
+ Bucket: config.fileUploads.s3Bucket,
4499
4602
  Key: key,
4500
4603
  UploadId: uploadId,
4501
- PartNumber: partNumber,
4604
+ PartNumber: Number(partNumber),
4502
4605
  Body: ""
4503
4606
  }), { expiresIn }).then((url) => {
4504
4607
  res.setHeader("Access-Control-Allow-Origin", "*");
@@ -4516,7 +4619,7 @@ var createUppyRoutes = async (app) => {
4516
4619
  const parts = [];
4517
4620
  function listPartsPage(startAt) {
4518
4621
  client2.send(new ListPartsCommand({
4519
- Bucket: process.env.COMPANION_S3_BUCKET,
4622
+ Bucket: config.fileUploads.s3Bucket,
4520
4623
  Key: key,
4521
4624
  UploadId: uploadId,
4522
4625
  PartNumberMarker: startAt
@@ -4550,7 +4653,7 @@ var createUppyRoutes = async (app) => {
4550
4653
  return res.status(400).json({ error: "s3: `parts` must be an array of {ETag, PartNumber} objects." });
4551
4654
  }
4552
4655
  return client2.send(new CompleteMultipartUploadCommand({
4553
- Bucket: process.env.COMPANION_S3_BUCKET,
4656
+ Bucket: config.fileUploads.s3Bucket,
4554
4657
  Key: key,
4555
4658
  UploadId: uploadId,
4556
4659
  MultipartUpload: {
@@ -4563,6 +4666,7 @@ var createUppyRoutes = async (app) => {
4563
4666
  }
4564
4667
  res.setHeader("Access-Control-Allow-Origin", "*");
4565
4668
  res.json({
4669
+ key,
4566
4670
  location: data.Location
4567
4671
  });
4568
4672
  });
@@ -4575,7 +4679,7 @@ var createUppyRoutes = async (app) => {
4575
4679
  return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' });
4576
4680
  }
4577
4681
  return client2.send(new AbortMultipartUploadCommand({
4578
- Bucket: process.env.COMPANION_S3_BUCKET,
4682
+ Bucket: config.fileUploads.s3Bucket,
4579
4683
  Key: key,
4580
4684
  UploadId: uploadId
4581
4685
  }), (err) => {
@@ -4583,7 +4687,9 @@ var createUppyRoutes = async (app) => {
4583
4687
  next(err);
4584
4688
  return;
4585
4689
  }
4586
- res.json({});
4690
+ res.json({
4691
+ key
4692
+ });
4587
4693
  });
4588
4694
  });
4589
4695
  return app;
@@ -4619,7 +4725,7 @@ var CLAUDE_MESSAGES = {
4619
4725
  // src/registry/routes.ts
4620
4726
  import OpenAI from "openai";
4621
4727
  import fs2 from "fs";
4622
- import { randomUUID } from "crypto";
4728
+ import { randomUUID as randomUUID2 } from "crypto";
4623
4729
  import "@opentelemetry/api";
4624
4730
  var REQUEST_SIZE_LIMIT = "50mb";
4625
4731
  var global_queues = {
@@ -4627,6 +4733,7 @@ var global_queues = {
4627
4733
  };
4628
4734
  var {
4629
4735
  agentsSchema: agentsSchema2,
4736
+ projectsSchema: projectsSchema2,
4630
4737
  evalResultsSchema: evalResultsSchema2,
4631
4738
  jobsSchema: jobsSchema2,
4632
4739
  agentSessionsSchema: agentSessionsSchema2,
@@ -4701,6 +4808,7 @@ var createExpressRoutes = async (app, logger, agents, tools, contexts, config, t
4701
4808
  usersSchema2(),
4702
4809
  rolesSchema2(),
4703
4810
  agentsSchema2(),
4811
+ projectsSchema2(),
4704
4812
  jobsSchema2(),
4705
4813
  evalResultsSchema2(),
4706
4814
  agentSessionsSchema2(),
@@ -4826,7 +4934,7 @@ Mood: friendly and intelligent.
4826
4934
  return;
4827
4935
  }
4828
4936
  const image_bytes = Buffer.from(image_base64, "base64");
4829
- const uuid = randomUUID();
4937
+ const uuid = randomUUID2();
4830
4938
  if (!fs2.existsSync("public")) {
4831
4939
  fs2.mkdirSync("public");
4832
4940
  }
@@ -5055,10 +5163,10 @@ Mood: friendly and intelligent.
5055
5163
  }
5056
5164
  });
5057
5165
  });
5058
- if (process.env.COMPANION_S3_REGION && process.env.COMPANION_S3_KEY && process.env.COMPANION_S3_SECRET) {
5059
- await createUppyRoutes(app);
5166
+ if (config?.fileUploads?.s3region && config?.fileUploads?.s3key && config?.fileUploads?.s3secret && config?.fileUploads?.s3Bucket) {
5167
+ await createUppyRoutes(app, config);
5060
5168
  } else {
5061
- console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in the environment.");
5169
+ console.log("[EXULU] skipping uppy file upload routes, because no S3 compatible region, key or secret is set in ExuluApp instance.");
5062
5170
  }
5063
5171
  const TARGET_API = "https://api.anthropic.com";
5064
5172
  app.use("/gateway/anthropic/:id", express.raw({ type: "*/*", limit: REQUEST_SIZE_LIMIT }), async (req, res) => {
@@ -5330,7 +5438,7 @@ var createLogsCleanerWorker = (logsDir) => {
5330
5438
 
5331
5439
  // src/mcp/index.ts
5332
5440
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
5333
- import { randomUUID as randomUUID2 } from "crypto";
5441
+ import { randomUUID as randomUUID3 } from "crypto";
5334
5442
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
5335
5443
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
5336
5444
  import { z as z2 } from "zod";
@@ -5423,7 +5531,7 @@ ${code}`
5423
5531
  transport = this.transports[sessionId];
5424
5532
  } else if (!sessionId && isInitializeRequest(req.body)) {
5425
5533
  transport = new StreamableHTTPServerTransport({
5426
- sessionIdGenerator: () => randomUUID2(),
5534
+ sessionIdGenerator: () => randomUUID3(),
5427
5535
  onsessioninitialized: (sessionId2) => {
5428
5536
  this.transports[sessionId2] = transport;
5429
5537
  }
@@ -5482,8 +5590,8 @@ var defaultAgent = new ExuluAgent2({
5482
5590
  type: "agent",
5483
5591
  capabilities: {
5484
5592
  text: true,
5485
- images: [],
5486
- files: [],
5593
+ images: [".png", ".jpg", ".jpeg", ".webp"],
5594
+ files: [".pdf", ".docx", ".xlsx", ".xls", ".csv", ".pptx", ".ppt"],
5487
5595
  audio: [],
5488
5596
  video: []
5489
5597
  },
@@ -5580,6 +5688,49 @@ var projectsContext = new ExuluContext({
5580
5688
  active: true
5581
5689
  });
5582
5690
 
5691
+ // src/templates/contexts/files.ts
5692
+ var filesContext = new ExuluContext({
5693
+ id: "files_default_context",
5694
+ name: "Files",
5695
+ description: "Files that can be used with Exulu agents.",
5696
+ configuration: {
5697
+ defaultRightsMode: "private",
5698
+ calculateVectors: "manual"
5699
+ },
5700
+ fields: [
5701
+ {
5702
+ name: "type",
5703
+ type: "text"
5704
+ },
5705
+ {
5706
+ name: "s3bucket",
5707
+ type: "text"
5708
+ },
5709
+ {
5710
+ name: "s3region",
5711
+ type: "text"
5712
+ },
5713
+ {
5714
+ name: "url",
5715
+ type: "text"
5716
+ },
5717
+ {
5718
+ name: "s3key",
5719
+ // ID of the file in S3 storage
5720
+ type: "text"
5721
+ },
5722
+ {
5723
+ name: "s3endpoint",
5724
+ type: "text"
5725
+ },
5726
+ {
5727
+ name: "content",
5728
+ type: "longText"
5729
+ }
5730
+ ],
5731
+ active: true
5732
+ });
5733
+
5583
5734
  // src/registry/index.ts
5584
5735
  var isValidPostgresName = (id) => {
5585
5736
  console.log("[EXULU] validating context id.", id);
@@ -5603,7 +5754,8 @@ var ExuluApp = class {
5603
5754
  this._contexts = {
5604
5755
  ...contexts,
5605
5756
  projectsContext,
5606
- codeStandardsContext
5757
+ codeStandardsContext,
5758
+ filesContext
5607
5759
  };
5608
5760
  this._agents = [
5609
5761
  claudeCodeAgent,
@@ -7016,7 +7168,7 @@ var generateApiKey = async (name, email) => {
7016
7168
  };
7017
7169
 
7018
7170
  // src/postgres/init-db.ts
7019
- var { agentsSchema: agentsSchema3, evalResultsSchema: evalResultsSchema3, jobsSchema: jobsSchema3, agentSessionsSchema: agentSessionsSchema3, agentMessagesSchema: agentMessagesSchema3, rolesSchema: rolesSchema3, usersSchema: usersSchema3, statisticsSchema: statisticsSchema3, variablesSchema: variablesSchema3, workflowTemplatesSchema: workflowTemplatesSchema3, rbacSchema: rbacSchema3 } = coreSchemas.get();
7171
+ var { agentsSchema: agentsSchema3, evalResultsSchema: evalResultsSchema3, jobsSchema: jobsSchema3, agentSessionsSchema: agentSessionsSchema3, agentMessagesSchema: agentMessagesSchema3, rolesSchema: rolesSchema3, usersSchema: usersSchema3, statisticsSchema: statisticsSchema3, variablesSchema: variablesSchema3, workflowTemplatesSchema: workflowTemplatesSchema3, rbacSchema: rbacSchema3, projectsSchema: projectsSchema3 } = coreSchemas.get();
7020
7172
  var addMissingFields = async (knex, tableName, fields, skipFields = []) => {
7021
7173
  for (const field of fields) {
7022
7174
  const { type, name, default: defaultValue, unique } = field;
@@ -7045,6 +7197,7 @@ var up = async function(knex) {
7045
7197
  rolesSchema3(),
7046
7198
  evalResultsSchema3(),
7047
7199
  statisticsSchema3(),
7200
+ projectsSchema3(),
7048
7201
  jobsSchema3(),
7049
7202
  rbacSchema3(),
7050
7203
  agentsSchema3(),
@@ -7294,6 +7447,5 @@ export {
7294
7447
  ExuluOtel,
7295
7448
  queues as ExuluQueues,
7296
7449
  ExuluTool2 as ExuluTool,
7297
- ExuluZodFileType,
7298
7450
  db2 as db
7299
7451
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exulu/backend",
3
3
  "author": "Qventu Bv.",
4
- "version": "1.22.0",
4
+ "version": "1.23.0",
5
5
  "main": "./dist/index.js",
6
6
  "private": false,
7
7
  "publishConfig": {
@@ -7,11 +7,12 @@ export interface AgentSession {
7
7
  resourceId: string;
8
8
  title: string;
9
9
  created_by: string;
10
- rights_mode: 'private' | 'users' | 'roles' | 'public' | 'project'
10
+ rights_mode: 'private' | 'users' | 'roles' | 'public' | 'projects'
11
11
  RBAC?: {
12
12
  type?: string;
13
13
  users?: Array<{ id: string; rights: 'read' | 'write' }>;
14
14
  roles?: Array<{ id: string; rights: 'read' | 'write' }>;
15
+ projects?: Array<{ id: string; rights: 'read' | 'write' }>;
15
16
  };
16
17
  }
17
18
  export interface AgentMessage {
@@ -37,12 +37,13 @@ export interface Agent {
37
37
  video: string[];
38
38
  }
39
39
  // New RBAC fields
40
- rights_mode?: 'private' | 'users' | 'roles' | 'public';
40
+ rights_mode?: 'private' | 'users' | 'roles' | 'public' | 'projects';
41
41
  created_by?: string;
42
42
  RBAC?: {
43
43
  type?: string;
44
44
  users?: Array<{ id: string; rights: 'read' | 'write' }>;
45
45
  roles?: Array<{ id: string; rights: 'read' | 'write' }>;
46
+ projects?: Array<{ id: string; rights: 'read' | 'write' }>;
46
47
  };
47
48
  createdAt?: string;
48
49
  updatedAt?: string;