@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/CHANGELOG.md +2 -2
- package/dist/index.cjs +231 -86
- package/dist/index.d.cts +13 -18
- package/dist/index.d.ts +13 -18
- package/dist/index.js +231 -79
- package/package.json +1 -1
- package/types/models/agent-session.ts +2 -1
- package/types/models/agent.ts +2 -1
- package/types/models/project.ts +15 -0
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
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
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:::${
|
|
4230
|
-
`arn:aws:s3:::${
|
|
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:
|
|
4241
|
-
...
|
|
4342
|
+
region: config.fileUploads.s3region,
|
|
4343
|
+
...config.fileUploads.s3endpoint && {
|
|
4242
4344
|
forcePathStyle: true,
|
|
4243
|
-
endpoint:
|
|
4345
|
+
endpoint: config.fileUploads.s3endpoint
|
|
4244
4346
|
},
|
|
4245
4347
|
credentials: {
|
|
4246
|
-
accessKeyId:
|
|
4247
|
-
secretAccessKey:
|
|
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:
|
|
4255
|
-
...
|
|
4356
|
+
region: config.fileUploads.s3region,
|
|
4357
|
+
...config.fileUploads.s3endpoint && { endpoint: config.fileUploads.s3endpoint },
|
|
4256
4358
|
credentials: {
|
|
4257
|
-
accessKeyId:
|
|
4258
|
-
secretAccessKey:
|
|
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:
|
|
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:
|
|
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:
|
|
4368
|
-
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) => `${
|
|
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:
|
|
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 = `${
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
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 (
|
|
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
|
|
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
|
|
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: () =>
|
|
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
|
@@ -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' | '
|
|
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 {
|
package/types/models/agent.ts
CHANGED
|
@@ -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;
|