@axiom-lattice/gateway 2.1.53 → 2.1.55
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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +19 -0
- package/dist/index.js +1043 -526
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +977 -449
- package/dist/index.mjs.map +1 -1
- package/jest.config.js +5 -0
- package/package.json +6 -5
- package/src/__tests__/__mocks__/e2b.ts +1 -0
- package/src/__tests__/channel-installations.test.ts +199 -0
- package/src/__tests__/sandbox-provider-registration.test.ts +74 -0
- package/src/__tests__/workspace.test.ts +119 -8
- package/src/channels/__tests__/routes.test.ts +71 -0
- package/src/channels/lark/README.md +187 -0
- package/src/channels/lark/__tests__/aggregator.test.ts +23 -0
- package/src/channels/lark/__tests__/controller.test.ts +270 -0
- package/src/channels/lark/__tests__/mapping-service.test.ts +118 -0
- package/src/channels/lark/__tests__/parser.test.ts +72 -0
- package/src/channels/lark/__tests__/sender.test.ts +37 -0
- package/src/channels/lark/__tests__/verification.test.ts +157 -0
- package/src/channels/lark/aggregator.ts +16 -0
- package/src/channels/lark/config.ts +44 -0
- package/src/channels/lark/controller.ts +189 -0
- package/src/channels/lark/mapping-service.ts +138 -0
- package/src/channels/lark/parser.ts +68 -0
- package/src/channels/lark/routes.ts +121 -0
- package/src/channels/lark/runner.ts +37 -0
- package/src/channels/lark/sender.ts +58 -0
- package/src/channels/lark/types.ts +33 -0
- package/src/channels/lark/verification.ts +67 -0
- package/src/channels/routes.ts +25 -0
- package/src/controllers/channel-installations.ts +354 -0
- package/src/controllers/sandbox.ts +30 -80
- package/src/controllers/skills.ts +71 -321
- package/src/controllers/threads.ts +8 -6
- package/src/controllers/workspace.ts +64 -179
- package/src/index.ts +28 -5
- package/src/routes/channel-installations.ts +33 -0
- package/src/routes/index.ts +6 -0
- package/src/schemas/index.ts +2 -2
- package/src/services/sandbox_service.ts +21 -21
- package/src/services/skill_service.ts +97 -0
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
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
|
+
|
|
1
8
|
// src/index.ts
|
|
2
9
|
import fastify from "fastify";
|
|
3
10
|
import cors from "@fastify/cors";
|
|
@@ -591,11 +598,13 @@ async function getThreadList(request, reply) {
|
|
|
591
598
|
const tenantId = getTenantId2(request);
|
|
592
599
|
const { assistantId } = request.params;
|
|
593
600
|
const metadataFilter = {};
|
|
594
|
-
|
|
595
|
-
|
|
601
|
+
const workspaceId = request.headers["x-workspace-id"];
|
|
602
|
+
const projectId = request.headers["x-project-id"];
|
|
603
|
+
if (workspaceId) {
|
|
604
|
+
metadataFilter.workspaceId = workspaceId;
|
|
596
605
|
}
|
|
597
|
-
if (
|
|
598
|
-
metadataFilter.projectId =
|
|
606
|
+
if (projectId) {
|
|
607
|
+
metadataFilter.projectId = projectId;
|
|
599
608
|
}
|
|
600
609
|
const storeLattice = getStoreLattice2("default", "thread");
|
|
601
610
|
const threadStore = storeLattice.store;
|
|
@@ -1239,16 +1248,63 @@ async function getHealth(request, reply) {
|
|
|
1239
1248
|
}
|
|
1240
1249
|
}
|
|
1241
1250
|
|
|
1242
|
-
// src/
|
|
1243
|
-
import {
|
|
1244
|
-
import { validateSkillName } from "@axiom-lattice/core";
|
|
1251
|
+
// src/services/skill_service.ts
|
|
1252
|
+
import { SandboxSkillStore } from "@axiom-lattice/core";
|
|
1245
1253
|
function getTenantId4(request) {
|
|
1246
|
-
|
|
1247
|
-
if (userTenantId) {
|
|
1248
|
-
return userTenantId;
|
|
1249
|
-
}
|
|
1250
|
-
return request.headers["x-tenant-id"] || "default";
|
|
1254
|
+
return request.user?.tenantId || request.headers["x-tenant-id"] || "default";
|
|
1251
1255
|
}
|
|
1256
|
+
function getWorkspaceId(request) {
|
|
1257
|
+
return request.headers["x-workspace-id"];
|
|
1258
|
+
}
|
|
1259
|
+
function getProjectId(request) {
|
|
1260
|
+
return request.headers["x-project-id"];
|
|
1261
|
+
}
|
|
1262
|
+
function buildContext(request) {
|
|
1263
|
+
return {
|
|
1264
|
+
workspaceId: getWorkspaceId(request),
|
|
1265
|
+
projectId: getProjectId(request)
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
var SkillService = class {
|
|
1269
|
+
constructor() {
|
|
1270
|
+
this.store = new SandboxSkillStore();
|
|
1271
|
+
}
|
|
1272
|
+
async getAllSkills(request) {
|
|
1273
|
+
const tenantId = getTenantId4(request);
|
|
1274
|
+
return this.store.getAllSkills(tenantId, buildContext(request));
|
|
1275
|
+
}
|
|
1276
|
+
async getSkillById(request, id) {
|
|
1277
|
+
const tenantId = getTenantId4(request);
|
|
1278
|
+
return this.store.getSkillById(tenantId, id, buildContext(request));
|
|
1279
|
+
}
|
|
1280
|
+
async createSkill(request, id, data) {
|
|
1281
|
+
const tenantId = getTenantId4(request);
|
|
1282
|
+
return this.store.createSkill(tenantId, id, data, buildContext(request));
|
|
1283
|
+
}
|
|
1284
|
+
async updateSkill(request, id, updates) {
|
|
1285
|
+
const tenantId = getTenantId4(request);
|
|
1286
|
+
return this.store.updateSkill(tenantId, id, updates, buildContext(request));
|
|
1287
|
+
}
|
|
1288
|
+
async deleteSkill(request, id) {
|
|
1289
|
+
const tenantId = getTenantId4(request);
|
|
1290
|
+
return this.store.deleteSkill(tenantId, id, buildContext(request));
|
|
1291
|
+
}
|
|
1292
|
+
async searchByMetadata(request, key, value) {
|
|
1293
|
+
const tenantId = getTenantId4(request);
|
|
1294
|
+
return this.store.searchByMetadata(tenantId, key, value, buildContext(request));
|
|
1295
|
+
}
|
|
1296
|
+
async filterByCompatibility(request, compatibility) {
|
|
1297
|
+
const tenantId = getTenantId4(request);
|
|
1298
|
+
return this.store.filterByCompatibility(tenantId, compatibility, buildContext(request));
|
|
1299
|
+
}
|
|
1300
|
+
async filterByLicense(request, license) {
|
|
1301
|
+
const tenantId = getTenantId4(request);
|
|
1302
|
+
return this.store.filterByLicense(tenantId, license, buildContext(request));
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1305
|
+
|
|
1306
|
+
// src/controllers/skills.ts
|
|
1307
|
+
var skillService = new SkillService();
|
|
1252
1308
|
function serializeSkill(skill) {
|
|
1253
1309
|
const serialized = {
|
|
1254
1310
|
id: skill.id,
|
|
@@ -1259,192 +1315,100 @@ function serializeSkill(skill) {
|
|
|
1259
1315
|
metadata: skill.metadata || {},
|
|
1260
1316
|
content: skill.content,
|
|
1261
1317
|
subSkills: skill.subSkills,
|
|
1262
|
-
createdAt: skill.createdAt instanceof Date ? skill.createdAt.toISOString() :
|
|
1263
|
-
updatedAt: skill.updatedAt instanceof Date ? skill.updatedAt.toISOString() :
|
|
1318
|
+
createdAt: skill.createdAt instanceof Date ? skill.createdAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
|
|
1319
|
+
updatedAt: skill.updatedAt instanceof Date ? skill.updatedAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
|
|
1264
1320
|
};
|
|
1265
1321
|
Object.keys(serialized).forEach((key) => {
|
|
1266
|
-
if (serialized[key] === void 0)
|
|
1267
|
-
delete serialized[key];
|
|
1268
|
-
}
|
|
1322
|
+
if (serialized[key] === void 0) delete serialized[key];
|
|
1269
1323
|
});
|
|
1270
1324
|
return serialized;
|
|
1271
1325
|
}
|
|
1272
1326
|
async function getSkillList(request, reply) {
|
|
1273
1327
|
try {
|
|
1274
|
-
const
|
|
1275
|
-
const storeLattice = getStoreLattice3("default", "skill");
|
|
1276
|
-
const skillStore = storeLattice.store;
|
|
1277
|
-
const skills = await skillStore.getAllSkills(tenantId);
|
|
1278
|
-
const serializedSkills = skills.map(serializeSkill);
|
|
1328
|
+
const skills = await skillService.getAllSkills(request);
|
|
1279
1329
|
return {
|
|
1280
1330
|
success: true,
|
|
1281
1331
|
message: "Successfully retrieved skill list",
|
|
1282
|
-
data: {
|
|
1283
|
-
records: serializedSkills,
|
|
1284
|
-
total: serializedSkills.length
|
|
1285
|
-
}
|
|
1332
|
+
data: { records: skills.map(serializeSkill), total: skills.length }
|
|
1286
1333
|
};
|
|
1287
1334
|
} catch (error) {
|
|
1288
1335
|
return reply.status(500).send({
|
|
1289
1336
|
success: false,
|
|
1290
1337
|
message: `Failed to retrieve skills: ${error.message}`,
|
|
1291
|
-
data: {
|
|
1292
|
-
records: [],
|
|
1293
|
-
total: 0
|
|
1294
|
-
}
|
|
1338
|
+
data: { records: [], total: 0 }
|
|
1295
1339
|
});
|
|
1296
1340
|
}
|
|
1297
1341
|
}
|
|
1298
1342
|
async function getSkill(request, reply) {
|
|
1299
1343
|
try {
|
|
1300
|
-
const tenantId = getTenantId4(request);
|
|
1301
1344
|
const { id } = request.params;
|
|
1302
|
-
const
|
|
1303
|
-
const skillStore = storeLattice.store;
|
|
1304
|
-
const skill = await skillStore.getSkillById(tenantId, id);
|
|
1345
|
+
const skill = await skillService.getSkillById(request, id);
|
|
1305
1346
|
if (!skill) {
|
|
1306
|
-
return reply.status(404).send({
|
|
1307
|
-
success: false,
|
|
1308
|
-
message: "Skill not found"
|
|
1309
|
-
});
|
|
1347
|
+
return reply.status(404).send({ success: false, message: "Skill not found" });
|
|
1310
1348
|
}
|
|
1311
|
-
return {
|
|
1312
|
-
success: true,
|
|
1313
|
-
message: "Successfully retrieved skill",
|
|
1314
|
-
data: serializeSkill(skill)
|
|
1315
|
-
};
|
|
1349
|
+
return { success: true, message: "Successfully retrieved skill", data: serializeSkill(skill) };
|
|
1316
1350
|
} catch (error) {
|
|
1317
|
-
return reply.status(500).send({
|
|
1318
|
-
success: false,
|
|
1319
|
-
message: `Failed to retrieve skill: ${error.message}`
|
|
1320
|
-
});
|
|
1351
|
+
return reply.status(500).send({ success: false, message: `Failed to retrieve skill: ${error.message}` });
|
|
1321
1352
|
}
|
|
1322
1353
|
}
|
|
1323
1354
|
async function createSkill(request, reply) {
|
|
1324
1355
|
try {
|
|
1325
1356
|
const data = request.body;
|
|
1326
1357
|
if (!data.name) {
|
|
1327
|
-
return reply.status(400).send({
|
|
1328
|
-
success: false,
|
|
1329
|
-
message: "name is required"
|
|
1330
|
-
});
|
|
1358
|
+
return reply.status(400).send({ success: false, message: "name is required" });
|
|
1331
1359
|
}
|
|
1332
1360
|
if (!data.description) {
|
|
1333
|
-
return reply.status(400).send({
|
|
1334
|
-
success: false,
|
|
1335
|
-
message: "description is required"
|
|
1336
|
-
});
|
|
1337
|
-
}
|
|
1338
|
-
try {
|
|
1339
|
-
validateSkillName(data.name);
|
|
1340
|
-
} catch (error) {
|
|
1341
|
-
return reply.status(400).send({
|
|
1342
|
-
success: false,
|
|
1343
|
-
message: error.message || "Invalid skill name format"
|
|
1344
|
-
});
|
|
1361
|
+
return reply.status(400).send({ success: false, message: "description is required" });
|
|
1345
1362
|
}
|
|
1346
1363
|
const id = request.body.id || data.name;
|
|
1347
1364
|
if (id !== data.name) {
|
|
1348
1365
|
return reply.status(400).send({
|
|
1349
1366
|
success: false,
|
|
1350
|
-
message: `id "${id}" must equal name "${data.name}"
|
|
1367
|
+
message: `id "${id}" must equal name "${data.name}"`
|
|
1351
1368
|
});
|
|
1352
1369
|
}
|
|
1353
|
-
const
|
|
1354
|
-
const storeLattice = getStoreLattice3("default", "skill");
|
|
1355
|
-
const skillStore = storeLattice.store;
|
|
1356
|
-
const exists = await skillStore.hasSkill(tenantId, id);
|
|
1357
|
-
if (exists) {
|
|
1358
|
-
return reply.status(409).send({
|
|
1359
|
-
success: false,
|
|
1360
|
-
message: `Skill with id "${id}" already exists`
|
|
1361
|
-
});
|
|
1362
|
-
}
|
|
1363
|
-
const newSkill = await skillStore.createSkill(tenantId, id, data);
|
|
1370
|
+
const skill = await skillService.createSkill(request, id, data);
|
|
1364
1371
|
return reply.status(201).send({
|
|
1365
1372
|
success: true,
|
|
1366
1373
|
message: "Successfully created skill",
|
|
1367
|
-
data: serializeSkill(
|
|
1374
|
+
data: serializeSkill(skill)
|
|
1368
1375
|
});
|
|
1369
1376
|
} catch (error) {
|
|
1370
|
-
|
|
1371
|
-
success: false,
|
|
1372
|
-
|
|
1373
|
-
|
|
1377
|
+
if (error.message?.includes("already exists")) {
|
|
1378
|
+
return reply.status(409).send({ success: false, message: error.message });
|
|
1379
|
+
}
|
|
1380
|
+
if (error.message?.includes("must equal name") || error.message?.includes("Invalid skill name")) {
|
|
1381
|
+
return reply.status(400).send({ success: false, message: error.message });
|
|
1382
|
+
}
|
|
1383
|
+
return reply.status(500).send({ success: false, message: `Failed to create skill: ${error.message}` });
|
|
1374
1384
|
}
|
|
1375
1385
|
}
|
|
1376
1386
|
async function updateSkill(request, reply) {
|
|
1377
1387
|
try {
|
|
1378
1388
|
const { id } = request.params;
|
|
1379
1389
|
const updates = request.body;
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
} catch (error) {
|
|
1384
|
-
return reply.status(400).send({
|
|
1385
|
-
success: false,
|
|
1386
|
-
message: error.message || "Invalid skill name format"
|
|
1387
|
-
});
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
const tenantId = getTenantId4(request);
|
|
1391
|
-
const storeLattice = getStoreLattice3("default", "skill");
|
|
1392
|
-
const skillStore = storeLattice.store;
|
|
1393
|
-
const exists = await skillStore.hasSkill(tenantId, id);
|
|
1394
|
-
if (!exists) {
|
|
1395
|
-
return reply.status(404).send({
|
|
1396
|
-
success: false,
|
|
1397
|
-
message: "Skill not found"
|
|
1398
|
-
});
|
|
1399
|
-
}
|
|
1400
|
-
const updatedSkill = await skillStore.updateSkill(tenantId, id, updates);
|
|
1401
|
-
if (!updatedSkill) {
|
|
1402
|
-
return reply.status(500).send({
|
|
1403
|
-
success: false,
|
|
1404
|
-
message: "Failed to update skill"
|
|
1405
|
-
});
|
|
1390
|
+
const skill = await skillService.updateSkill(request, id, updates);
|
|
1391
|
+
if (!skill) {
|
|
1392
|
+
return reply.status(404).send({ success: false, message: "Skill not found" });
|
|
1406
1393
|
}
|
|
1407
|
-
return {
|
|
1408
|
-
success: true,
|
|
1409
|
-
message: "Successfully updated skill",
|
|
1410
|
-
data: serializeSkill(updatedSkill)
|
|
1411
|
-
};
|
|
1394
|
+
return { success: true, message: "Successfully updated skill", data: serializeSkill(skill) };
|
|
1412
1395
|
} catch (error) {
|
|
1413
|
-
|
|
1414
|
-
success: false,
|
|
1415
|
-
|
|
1416
|
-
});
|
|
1396
|
+
if (error.message?.includes("Invalid skill name")) {
|
|
1397
|
+
return reply.status(400).send({ success: false, message: error.message });
|
|
1398
|
+
}
|
|
1399
|
+
return reply.status(500).send({ success: false, message: `Failed to update skill: ${error.message}` });
|
|
1417
1400
|
}
|
|
1418
1401
|
}
|
|
1419
1402
|
async function deleteSkill(request, reply) {
|
|
1420
1403
|
try {
|
|
1421
1404
|
const { id } = request.params;
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
const exists = await skillStore.hasSkill(tenantId, id);
|
|
1426
|
-
if (!exists) {
|
|
1427
|
-
return reply.status(404).send({
|
|
1428
|
-
success: false,
|
|
1429
|
-
message: "Skill not found"
|
|
1430
|
-
});
|
|
1431
|
-
}
|
|
1432
|
-
const deleted = await skillStore.deleteSkill(tenantId, id);
|
|
1433
|
-
if (!deleted) {
|
|
1434
|
-
return reply.status(500).send({
|
|
1435
|
-
success: false,
|
|
1436
|
-
message: "Failed to delete skill"
|
|
1437
|
-
});
|
|
1405
|
+
const result = await skillService.deleteSkill(request, id);
|
|
1406
|
+
if (!result) {
|
|
1407
|
+
return reply.status(404).send({ success: false, message: "Skill not found" });
|
|
1438
1408
|
}
|
|
1439
|
-
return {
|
|
1440
|
-
success: true,
|
|
1441
|
-
message: "Successfully deleted skill"
|
|
1442
|
-
};
|
|
1409
|
+
return { success: true, message: "Successfully deleted skill" };
|
|
1443
1410
|
} catch (error) {
|
|
1444
|
-
return reply.status(500).send({
|
|
1445
|
-
success: false,
|
|
1446
|
-
message: `Failed to delete skill: ${error.message}`
|
|
1447
|
-
});
|
|
1411
|
+
return reply.status(500).send({ success: false, message: `Failed to delete skill: ${error.message}` });
|
|
1448
1412
|
}
|
|
1449
1413
|
}
|
|
1450
1414
|
async function searchSkillsByMetadata(request, reply) {
|
|
@@ -1454,33 +1418,20 @@ async function searchSkillsByMetadata(request, reply) {
|
|
|
1454
1418
|
return reply.status(400).send({
|
|
1455
1419
|
success: false,
|
|
1456
1420
|
message: "key and value query parameters are required",
|
|
1457
|
-
data: {
|
|
1458
|
-
records: [],
|
|
1459
|
-
total: 0
|
|
1460
|
-
}
|
|
1421
|
+
data: { records: [], total: 0 }
|
|
1461
1422
|
});
|
|
1462
1423
|
}
|
|
1463
|
-
const
|
|
1464
|
-
const storeLattice = getStoreLattice3("default", "skill");
|
|
1465
|
-
const skillStore = storeLattice.store;
|
|
1466
|
-
const skills = await skillStore.searchByMetadata(tenantId, key, value);
|
|
1467
|
-
const serializedSkills = skills.map(serializeSkill);
|
|
1424
|
+
const skills = await skillService.searchByMetadata(request, key, value);
|
|
1468
1425
|
return {
|
|
1469
1426
|
success: true,
|
|
1470
1427
|
message: "Successfully searched skills",
|
|
1471
|
-
data: {
|
|
1472
|
-
records: serializedSkills,
|
|
1473
|
-
total: serializedSkills.length
|
|
1474
|
-
}
|
|
1428
|
+
data: { records: skills.map(serializeSkill), total: skills.length }
|
|
1475
1429
|
};
|
|
1476
1430
|
} catch (error) {
|
|
1477
1431
|
return reply.status(500).send({
|
|
1478
1432
|
success: false,
|
|
1479
1433
|
message: `Failed to search skills: ${error.message}`,
|
|
1480
|
-
data: {
|
|
1481
|
-
records: [],
|
|
1482
|
-
total: 0
|
|
1483
|
-
}
|
|
1434
|
+
data: { records: [], total: 0 }
|
|
1484
1435
|
});
|
|
1485
1436
|
}
|
|
1486
1437
|
}
|
|
@@ -1491,33 +1442,20 @@ async function filterSkillsByCompatibility(request, reply) {
|
|
|
1491
1442
|
return reply.status(400).send({
|
|
1492
1443
|
success: false,
|
|
1493
1444
|
message: "compatibility query parameter is required",
|
|
1494
|
-
data: {
|
|
1495
|
-
records: [],
|
|
1496
|
-
total: 0
|
|
1497
|
-
}
|
|
1445
|
+
data: { records: [], total: 0 }
|
|
1498
1446
|
});
|
|
1499
1447
|
}
|
|
1500
|
-
const
|
|
1501
|
-
const storeLattice = getStoreLattice3("default", "skill");
|
|
1502
|
-
const skillStore = storeLattice.store;
|
|
1503
|
-
const skills = await skillStore.filterByCompatibility(tenantId, compatibility);
|
|
1504
|
-
const serializedSkills = skills.map(serializeSkill);
|
|
1448
|
+
const skills = await skillService.filterByCompatibility(request, compatibility);
|
|
1505
1449
|
return {
|
|
1506
1450
|
success: true,
|
|
1507
1451
|
message: "Successfully filtered skills",
|
|
1508
|
-
data: {
|
|
1509
|
-
records: serializedSkills,
|
|
1510
|
-
total: serializedSkills.length
|
|
1511
|
-
}
|
|
1452
|
+
data: { records: skills.map(serializeSkill), total: skills.length }
|
|
1512
1453
|
};
|
|
1513
1454
|
} catch (error) {
|
|
1514
1455
|
return reply.status(500).send({
|
|
1515
1456
|
success: false,
|
|
1516
1457
|
message: `Failed to filter skills: ${error.message}`,
|
|
1517
|
-
data: {
|
|
1518
|
-
records: [],
|
|
1519
|
-
total: 0
|
|
1520
|
-
}
|
|
1458
|
+
data: { records: [], total: 0 }
|
|
1521
1459
|
});
|
|
1522
1460
|
}
|
|
1523
1461
|
}
|
|
@@ -1528,33 +1466,20 @@ async function filterSkillsByLicense(request, reply) {
|
|
|
1528
1466
|
return reply.status(400).send({
|
|
1529
1467
|
success: false,
|
|
1530
1468
|
message: "license query parameter is required",
|
|
1531
|
-
data: {
|
|
1532
|
-
records: [],
|
|
1533
|
-
total: 0
|
|
1534
|
-
}
|
|
1469
|
+
data: { records: [], total: 0 }
|
|
1535
1470
|
});
|
|
1536
1471
|
}
|
|
1537
|
-
const
|
|
1538
|
-
const storeLattice = getStoreLattice3("default", "skill");
|
|
1539
|
-
const skillStore = storeLattice.store;
|
|
1540
|
-
const skills = await skillStore.filterByLicense(tenantId, license);
|
|
1541
|
-
const serializedSkills = skills.map(serializeSkill);
|
|
1472
|
+
const skills = await skillService.filterByLicense(request, license);
|
|
1542
1473
|
return {
|
|
1543
1474
|
success: true,
|
|
1544
1475
|
message: "Successfully filtered skills",
|
|
1545
|
-
data: {
|
|
1546
|
-
records: serializedSkills,
|
|
1547
|
-
total: serializedSkills.length
|
|
1548
|
-
}
|
|
1476
|
+
data: { records: skills.map(serializeSkill), total: skills.length }
|
|
1549
1477
|
};
|
|
1550
1478
|
} catch (error) {
|
|
1551
1479
|
return reply.status(500).send({
|
|
1552
1480
|
success: false,
|
|
1553
1481
|
message: `Failed to filter skills: ${error.message}`,
|
|
1554
|
-
data: {
|
|
1555
|
-
records: [],
|
|
1556
|
-
total: 0
|
|
1557
|
-
}
|
|
1482
|
+
data: { records: [], total: 0 }
|
|
1558
1483
|
});
|
|
1559
1484
|
}
|
|
1560
1485
|
}
|
|
@@ -1643,7 +1568,7 @@ async function getToolConfigs(request, reply) {
|
|
|
1643
1568
|
|
|
1644
1569
|
// src/controllers/data-query.ts
|
|
1645
1570
|
import {
|
|
1646
|
-
getStoreLattice as
|
|
1571
|
+
getStoreLattice as getStoreLattice3,
|
|
1647
1572
|
metricsServerManager
|
|
1648
1573
|
} from "@axiom-lattice/core";
|
|
1649
1574
|
function getTenantId5(request) {
|
|
@@ -1676,7 +1601,7 @@ async function executeDataQuery(request, reply) {
|
|
|
1676
1601
|
message: "Cannot provide both metrics and customSql. Use one query type only."
|
|
1677
1602
|
};
|
|
1678
1603
|
}
|
|
1679
|
-
const storeLattice =
|
|
1604
|
+
const storeLattice = getStoreLattice3("default", "metrics");
|
|
1680
1605
|
const store = storeLattice.store;
|
|
1681
1606
|
if (!body.serverKey) {
|
|
1682
1607
|
reply.code(400);
|
|
@@ -2168,11 +2093,8 @@ async function removePendingMessageHandler(request, reply) {
|
|
|
2168
2093
|
}
|
|
2169
2094
|
}
|
|
2170
2095
|
|
|
2171
|
-
// src/controllers/sandbox.ts
|
|
2172
|
-
import { Readable } from "stream";
|
|
2173
|
-
|
|
2174
2096
|
// src/services/sandbox_service.ts
|
|
2175
|
-
import { agentLatticeManager as agentLatticeManager3,
|
|
2097
|
+
import { agentLatticeManager as agentLatticeManager3, normalizeSandboxName } from "@axiom-lattice/core";
|
|
2176
2098
|
var ERROR_HTML = `<!DOCTYPE html>
|
|
2177
2099
|
<html lang="zh-CN">
|
|
2178
2100
|
<head>
|
|
@@ -2264,7 +2186,7 @@ var ERROR_HTML = `<!DOCTYPE html>
|
|
|
2264
2186
|
</div>
|
|
2265
2187
|
<div class="info-item">
|
|
2266
2188
|
<span class="label">\u9694\u79BB\u7EA7\u522B</span>
|
|
2267
|
-
<span class="value" id="
|
|
2189
|
+
<span class="value" id="vmIsolation">-</span>
|
|
2268
2190
|
</div>
|
|
2269
2191
|
<div class="info-item">
|
|
2270
2192
|
<span class="label">\u9519\u8BEF\u4FE1\u606F</span>
|
|
@@ -2277,13 +2199,13 @@ var ERROR_HTML = `<!DOCTYPE html>
|
|
|
2277
2199
|
const params = new URLSearchParams(window.location.search);
|
|
2278
2200
|
document.getElementById('assistantId').textContent = params.get('assistantId') || '-';
|
|
2279
2201
|
document.getElementById('threadId').textContent = params.get('threadId') || '-';
|
|
2280
|
-
document.getElementById('
|
|
2202
|
+
document.getElementById('vmIsolation').textContent = params.get('vmIsolation') || '-';
|
|
2281
2203
|
document.getElementById('errorMsg').textContent = params.get('error') || '\u672A\u77E5\u9519\u8BEF';
|
|
2282
2204
|
</script>
|
|
2283
2205
|
</body>
|
|
2284
2206
|
</html>`;
|
|
2285
2207
|
var SandboxService = class {
|
|
2286
|
-
|
|
2208
|
+
getFilesystemVmIsolation(tenantId, assistantId) {
|
|
2287
2209
|
const agentLattice = agentLatticeManager3.getAgentLatticeWithTenant(tenantId, assistantId);
|
|
2288
2210
|
if (!agentLattice) {
|
|
2289
2211
|
return null;
|
|
@@ -2293,27 +2215,26 @@ var SandboxService = class {
|
|
|
2293
2215
|
return null;
|
|
2294
2216
|
}
|
|
2295
2217
|
const config = filesystemConfig.config;
|
|
2296
|
-
return config?.
|
|
2218
|
+
return config?.vmIsolation || null;
|
|
2297
2219
|
}
|
|
2298
|
-
computeSandboxName(assistantId, threadId,
|
|
2299
|
-
|
|
2300
|
-
switch (isolatedLevel) {
|
|
2220
|
+
computeSandboxName(assistantId, threadId, vmIsolation, tenantId, workspaceId, projectId) {
|
|
2221
|
+
switch (vmIsolation) {
|
|
2301
2222
|
case "agent":
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2223
|
+
return normalizeSandboxName(`${tenantId ?? "default"}-${assistantId}`);
|
|
2224
|
+
case "project":
|
|
2225
|
+
return normalizeSandboxName(
|
|
2226
|
+
`${tenantId ?? "default"}-${workspaceId ?? "default"}-${projectId ?? "default"}`
|
|
2227
|
+
);
|
|
2307
2228
|
case "global":
|
|
2308
2229
|
default:
|
|
2309
|
-
|
|
2310
|
-
break;
|
|
2230
|
+
return "global";
|
|
2311
2231
|
}
|
|
2312
|
-
|
|
2232
|
+
}
|
|
2233
|
+
getBrowserSandboxBaseURL() {
|
|
2234
|
+
return process.env.AGENT_INFRA_SANDBOX_BASE_URL || "http://localhost:8080";
|
|
2313
2235
|
}
|
|
2314
2236
|
getTargetUrl(sandboxName) {
|
|
2315
|
-
|
|
2316
|
-
return `${sandboxManager.getBaseURL()}/sandbox/${sandboxName}`;
|
|
2237
|
+
return `${this.getBrowserSandboxBaseURL()}/sandbox/${sandboxName}`;
|
|
2317
2238
|
}
|
|
2318
2239
|
async getVncHtml(sandboxName) {
|
|
2319
2240
|
const response = await fetch(`${this.getTargetUrl(sandboxName)}/vnc/index.html`);
|
|
@@ -2349,9 +2270,9 @@ var SandboxService = class {
|
|
|
2349
2270
|
);
|
|
2350
2271
|
return rewritten;
|
|
2351
2272
|
}
|
|
2352
|
-
generateErrorHtml(assistantId, threadId,
|
|
2273
|
+
generateErrorHtml(assistantId, threadId, vmIsolation, errorMessage) {
|
|
2353
2274
|
const encodedError = encodeURIComponent(errorMessage);
|
|
2354
|
-
return ERROR_HTML.replace("{assistantId}", assistantId).replace("{threadId}", threadId).replace("{
|
|
2275
|
+
return ERROR_HTML.replace("{assistantId}", assistantId).replace("{threadId}", threadId).replace("{vmIsolation}", vmIsolation).replace("{errorMessage}", errorMessage);
|
|
2355
2276
|
}
|
|
2356
2277
|
};
|
|
2357
2278
|
var sandboxService = new SandboxService();
|
|
@@ -2390,14 +2311,14 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
2390
2311
|
console.log("[Sandbox Upload] Route matched:", request.url);
|
|
2391
2312
|
const { assistantId, threadId } = request.params;
|
|
2392
2313
|
const tenantId = request.headers["x-tenant-id"] || "default";
|
|
2393
|
-
const
|
|
2394
|
-
if (!
|
|
2314
|
+
const vmIsolation = sandboxService.getFilesystemVmIsolation(tenantId, assistantId);
|
|
2315
|
+
if (!vmIsolation) {
|
|
2395
2316
|
return reply.status(500).send({ error: "Assistant sandbox config not found" });
|
|
2396
2317
|
}
|
|
2397
2318
|
const sandboxName = sandboxService.computeSandboxName(
|
|
2398
2319
|
assistantId,
|
|
2399
2320
|
threadId,
|
|
2400
|
-
|
|
2321
|
+
vmIsolation
|
|
2401
2322
|
);
|
|
2402
2323
|
const sandboxManager = getSandBoxManager2();
|
|
2403
2324
|
const sandbox = await sandboxManager.createSandbox(sandboxName);
|
|
@@ -2409,17 +2330,14 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
2409
2330
|
const buffer = await data.toBuffer();
|
|
2410
2331
|
const pathEntry = data.fields?.path;
|
|
2411
2332
|
const pathValue = pathEntry && typeof pathEntry === "object" && "value" in pathEntry ? String(pathEntry.value) : typeof pathEntry === "string" ? pathEntry : void 0;
|
|
2412
|
-
const
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
});
|
|
2419
|
-
if (!uploadResult.ok) {
|
|
2420
|
-
return reply.status(502).send({ error: `Upload error: ${uploadResult.error}` });
|
|
2333
|
+
const filePath = `~/uploads/${pathValue ? pathValue : ""}${data.filename}`;
|
|
2334
|
+
try {
|
|
2335
|
+
await sandbox.file.uploadFile({ file: filePath, data: buffer });
|
|
2336
|
+
} catch (err) {
|
|
2337
|
+
reply.status(500).send({ error: String(err) });
|
|
2338
|
+
return;
|
|
2421
2339
|
}
|
|
2422
|
-
const relativePath =
|
|
2340
|
+
const relativePath = filePath.replace(`~/`, "");
|
|
2423
2341
|
const result = { id: relativePath, name: data.filename, size: buffer.length };
|
|
2424
2342
|
return reply.status(200).send({ message: "File uploaded successfully", ...result });
|
|
2425
2343
|
} catch (error) {
|
|
@@ -2437,59 +2355,30 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
2437
2355
|
if (!filePath || typeof filePath !== "string") {
|
|
2438
2356
|
return reply.status(400).send({ error: "Query parameter 'path' is required" });
|
|
2439
2357
|
}
|
|
2440
|
-
const
|
|
2441
|
-
if (!
|
|
2442
|
-
return reply.status(500).send({ error: "Assistant filesystem
|
|
2358
|
+
const vmIsolation = sandboxService.getFilesystemVmIsolation(tenantId, assistantId);
|
|
2359
|
+
if (!vmIsolation) {
|
|
2360
|
+
return reply.status(500).send({ error: "Assistant filesystem vmIsolation not found" });
|
|
2443
2361
|
}
|
|
2444
2362
|
const sandboxName = sandboxService.computeSandboxName(
|
|
2445
2363
|
assistantId,
|
|
2446
2364
|
threadId,
|
|
2447
|
-
|
|
2365
|
+
vmIsolation
|
|
2448
2366
|
);
|
|
2449
2367
|
const sandboxManager = getSandBoxManager2();
|
|
2450
2368
|
const sandbox = await sandboxManager.createSandbox(sandboxName);
|
|
2451
2369
|
try {
|
|
2452
|
-
const resolvedPath = filePath.startsWith("
|
|
2370
|
+
const resolvedPath = filePath.startsWith("~/") ? filePath : `~/${filePath.replace(/^\//, "")}`;
|
|
2453
2371
|
const filename = getFilenameFromPath(resolvedPath);
|
|
2454
2372
|
const inferredContentType = getContentTypeFromFilename(filename);
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
return reply.status(502).send({
|
|
2460
|
-
error: `Download error: ${JSON.stringify(downloadResult.error)}`
|
|
2461
|
-
});
|
|
2462
|
-
}
|
|
2463
|
-
const body = downloadResult.body;
|
|
2464
|
-
if (typeof body?.stream === "function") {
|
|
2465
|
-
const webStream = body.stream();
|
|
2466
|
-
const nodeStream = Readable.fromWeb(webStream);
|
|
2467
|
-
const contentType2 = body.contentType ?? inferredContentType;
|
|
2468
|
-
const contentDisposition2 = body.contentDisposition ?? `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
|
|
2469
|
-
reply = reply.status(200).type(contentType2).header("Content-Disposition", contentDisposition2).send(nodeStream);
|
|
2373
|
+
try {
|
|
2374
|
+
const buf = await sandbox.file.downloadFile({ file: resolvedPath });
|
|
2375
|
+
const contentDisposition = `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
|
|
2376
|
+
reply = reply.status(200).type(inferredContentType).header("Content-Disposition", contentDisposition).send(buf);
|
|
2470
2377
|
return reply;
|
|
2378
|
+
} catch (err) {
|
|
2379
|
+
reply.status(500).send({ error: String(err) });
|
|
2380
|
+
return;
|
|
2471
2381
|
}
|
|
2472
|
-
const bodyUnknown = downloadResult.body;
|
|
2473
|
-
let buf;
|
|
2474
|
-
let contentType = inferredContentType;
|
|
2475
|
-
let contentDisposition = `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
|
|
2476
|
-
if (bodyUnknown instanceof ArrayBuffer) {
|
|
2477
|
-
buf = Buffer.from(bodyUnknown);
|
|
2478
|
-
} else if (bodyUnknown instanceof Buffer) {
|
|
2479
|
-
buf = bodyUnknown;
|
|
2480
|
-
} else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
|
|
2481
|
-
const res = bodyUnknown;
|
|
2482
|
-
buf = Buffer.from(await res.arrayBuffer());
|
|
2483
|
-
if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
|
|
2484
|
-
if (res.headers?.get("content-disposition")) contentDisposition = res.headers.get("content-disposition");
|
|
2485
|
-
} else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
|
|
2486
|
-
const blob = await bodyUnknown.blob();
|
|
2487
|
-
buf = Buffer.from(await blob.arrayBuffer());
|
|
2488
|
-
} else {
|
|
2489
|
-
return reply.status(502).send({ error: "Unexpected download response format" });
|
|
2490
|
-
}
|
|
2491
|
-
reply = reply.status(200).type(contentType).header("Content-Disposition", contentDisposition).send(buf);
|
|
2492
|
-
return reply;
|
|
2493
2382
|
} catch (error) {
|
|
2494
2383
|
const message = error instanceof Error ? error.message : String(error);
|
|
2495
2384
|
return reply.status(502).send({ error: `Download proxy error: ${message}` });
|
|
@@ -2501,15 +2390,14 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
2501
2390
|
// src/controllers/workspace.ts
|
|
2502
2391
|
import * as fs from "fs/promises";
|
|
2503
2392
|
import * as path from "path";
|
|
2504
|
-
import {
|
|
2505
|
-
import { getStoreLattice as getStoreLattice5 } from "@axiom-lattice/core";
|
|
2393
|
+
import { getStoreLattice as getStoreLattice4 } from "@axiom-lattice/core";
|
|
2506
2394
|
import { SandboxFilesystem, FilesystemBackend } from "@axiom-lattice/core";
|
|
2507
2395
|
import { getSandBoxManager as getSandBoxManager3 } from "@axiom-lattice/core";
|
|
2508
2396
|
import { v4 as uuidv4 } from "uuid";
|
|
2509
2397
|
var WorkspaceController = class {
|
|
2510
2398
|
constructor() {
|
|
2511
|
-
this.workspaceStore =
|
|
2512
|
-
this.projectStore =
|
|
2399
|
+
this.workspaceStore = getStoreLattice4("default", "workspace").store;
|
|
2400
|
+
this.projectStore = getStoreLattice4("default", "project").store;
|
|
2513
2401
|
}
|
|
2514
2402
|
getTenantId(request) {
|
|
2515
2403
|
const userTenantId = request.user?.tenantId;
|
|
@@ -2643,12 +2531,16 @@ var WorkspaceController = class {
|
|
|
2643
2531
|
}
|
|
2644
2532
|
if (workspace.storageType === "sandbox") {
|
|
2645
2533
|
const sandboxManager = getSandBoxManager3();
|
|
2646
|
-
const
|
|
2647
|
-
|
|
2534
|
+
const sandbox = await sandboxManager.getSandboxFromConfig({
|
|
2535
|
+
assistant_id: "",
|
|
2536
|
+
thread_id: "",
|
|
2537
|
+
tenantId,
|
|
2538
|
+
workspaceId,
|
|
2539
|
+
projectId
|
|
2540
|
+
});
|
|
2648
2541
|
return {
|
|
2649
2542
|
backend: new SandboxFilesystem({
|
|
2650
|
-
sandboxInstance: sandbox
|
|
2651
|
-
workingDirectory: `/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`
|
|
2543
|
+
sandboxInstance: sandbox
|
|
2652
2544
|
}),
|
|
2653
2545
|
workspace
|
|
2654
2546
|
};
|
|
@@ -2697,50 +2589,26 @@ var WorkspaceController = class {
|
|
|
2697
2589
|
}
|
|
2698
2590
|
try {
|
|
2699
2591
|
const { workspace } = await this.getBackend(tenantId, workspaceId, projectId);
|
|
2700
|
-
const resolvedPath = filePath
|
|
2592
|
+
const resolvedPath = filePath;
|
|
2701
2593
|
if (workspace.storageType === "sandbox") {
|
|
2702
2594
|
const sandboxManager = getSandBoxManager3();
|
|
2703
|
-
const sandbox = await sandboxManager.
|
|
2704
|
-
|
|
2595
|
+
const sandbox = await sandboxManager.getSandboxFromConfig({
|
|
2596
|
+
assistant_id: "",
|
|
2597
|
+
thread_id: "",
|
|
2598
|
+
tenantId,
|
|
2599
|
+
workspaceId,
|
|
2600
|
+
projectId
|
|
2601
|
+
});
|
|
2602
|
+
const realPath = resolvedPath;
|
|
2705
2603
|
const filename2 = this.getFilenameFromPath(resolvedPath);
|
|
2706
2604
|
const inferredContentType = this.getMimeType(filename2);
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
error: `Download error: ${JSON.stringify(downloadResult.error)}`
|
|
2714
|
-
});
|
|
2605
|
+
try {
|
|
2606
|
+
const buf = await sandbox.file.downloadFile({ file: realPath });
|
|
2607
|
+
const contentDisposition = `attachment; filename*=UTF-8''${encodeURIComponent(filename2)}`;
|
|
2608
|
+
return reply.status(200).type(inferredContentType).header("Content-Disposition", contentDisposition).send(buf);
|
|
2609
|
+
} catch (err) {
|
|
2610
|
+
return reply.status(502).send({ success: false, error: String(err) });
|
|
2715
2611
|
}
|
|
2716
|
-
const body = downloadResult.body;
|
|
2717
|
-
if (typeof body?.stream === "function") {
|
|
2718
|
-
const webStream = body.stream();
|
|
2719
|
-
const nodeStream = Readable2.fromWeb(webStream);
|
|
2720
|
-
const contentType2 = body.contentType ?? inferredContentType;
|
|
2721
|
-
const contentDisposition2 = body.contentDisposition ?? `attachment; filename*=UTF-8''${encodeURIComponent(filename2)}`;
|
|
2722
|
-
return reply.status(200).type(contentType2).header("Content-Disposition", contentDisposition2).send(nodeStream);
|
|
2723
|
-
}
|
|
2724
|
-
const bodyUnknown = downloadResult.body;
|
|
2725
|
-
let buf;
|
|
2726
|
-
let contentType = inferredContentType;
|
|
2727
|
-
let contentDisposition = `attachment; filename*=UTF-8''${encodeURIComponent(filename2)}`;
|
|
2728
|
-
if (bodyUnknown instanceof ArrayBuffer) {
|
|
2729
|
-
buf = Buffer.from(bodyUnknown);
|
|
2730
|
-
} else if (bodyUnknown instanceof Buffer) {
|
|
2731
|
-
buf = bodyUnknown;
|
|
2732
|
-
} else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
|
|
2733
|
-
const res = bodyUnknown;
|
|
2734
|
-
buf = Buffer.from(await res.arrayBuffer());
|
|
2735
|
-
if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
|
|
2736
|
-
if (res.headers?.get("content-disposition")) contentDisposition = res.headers.get("content-disposition");
|
|
2737
|
-
} else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
|
|
2738
|
-
const blob = await bodyUnknown.blob();
|
|
2739
|
-
buf = Buffer.from(await blob.arrayBuffer());
|
|
2740
|
-
} else {
|
|
2741
|
-
return reply.status(502).send({ success: false, error: "Unexpected download response format" });
|
|
2742
|
-
}
|
|
2743
|
-
return reply.status(200).type(contentType).header("Content-Disposition", contentDisposition).send(buf);
|
|
2744
2612
|
}
|
|
2745
2613
|
const { backend } = await this.getBackend(tenantId, workspaceId, projectId);
|
|
2746
2614
|
const content = await backend.read(resolvedPath, 0, Infinity);
|
|
@@ -2766,36 +2634,27 @@ var WorkspaceController = class {
|
|
|
2766
2634
|
}
|
|
2767
2635
|
try {
|
|
2768
2636
|
const { workspace } = await this.getBackend(tenantId, workspaceId, projectId);
|
|
2769
|
-
const resolvedPath = filePath
|
|
2637
|
+
const resolvedPath = filePath;
|
|
2770
2638
|
if (workspace.storageType === "sandbox") {
|
|
2771
2639
|
const sandboxManager = getSandBoxManager3();
|
|
2772
|
-
const sandbox = await sandboxManager.
|
|
2773
|
-
|
|
2640
|
+
const sandbox = await sandboxManager.getSandboxFromConfig({
|
|
2641
|
+
assistant_id: "",
|
|
2642
|
+
thread_id: "",
|
|
2643
|
+
tenantId,
|
|
2644
|
+
workspaceId,
|
|
2645
|
+
projectId
|
|
2646
|
+
});
|
|
2647
|
+
const realPath = resolvedPath;
|
|
2774
2648
|
const filename2 = this.getFilenameFromPath(resolvedPath);
|
|
2775
2649
|
const inferredContentType = this.getMimeType(filename2);
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
}
|
|
2785
|
-
const body = downloadResult.body;
|
|
2786
|
-
if (typeof body?.stream === "function") {
|
|
2787
|
-
const webStream = body.stream();
|
|
2788
|
-
const nodeStream = Readable2.fromWeb(webStream);
|
|
2789
|
-
const contentType2 = body.contentType ?? inferredContentType;
|
|
2790
|
-
console.log(`[viewFile] Sandbox returned stream, contentType: ${contentType2}, filename: ${filename2}`);
|
|
2791
|
-
const isHtml2 = contentType2?.toLowerCase().includes("text/html") || filename2.toLowerCase().endsWith(".html") || filename2.toLowerCase().endsWith(".htm");
|
|
2792
|
-
if (isHtml2) {
|
|
2793
|
-
console.log(`[viewFile] HTML stream detected, collecting for context injection`);
|
|
2794
|
-
const chunks = [];
|
|
2795
|
-
for await (const chunk of nodeStream) {
|
|
2796
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2797
|
-
}
|
|
2798
|
-
let content2 = Buffer.concat(chunks).toString("utf-8");
|
|
2650
|
+
try {
|
|
2651
|
+
const buf = await sandbox.file.downloadFile({ file: realPath });
|
|
2652
|
+
let contentType = inferredContentType;
|
|
2653
|
+
const isHtml = contentType?.toLowerCase().includes("text/html") || filename2.toLowerCase().endsWith(".html") || filename2.toLowerCase().endsWith(".htm");
|
|
2654
|
+
let outputBuf = buf;
|
|
2655
|
+
if (isHtml) {
|
|
2656
|
+
console.log(`[viewFile] Injecting AI2APP context for sandbox HTML file: ${filename2}, tenantId: ${tenantId}, contentType: ${contentType}`);
|
|
2657
|
+
let content2 = buf.toString("utf-8");
|
|
2799
2658
|
const contextScript = `<script>window.__AI2APP_CONTEXT__=${JSON.stringify({
|
|
2800
2659
|
tenantId,
|
|
2801
2660
|
workspaceId,
|
|
@@ -2804,58 +2663,20 @@ var WorkspaceController = class {
|
|
|
2804
2663
|
})};</script>`;
|
|
2805
2664
|
if (content2.toLowerCase().includes("</head>")) {
|
|
2806
2665
|
content2 = content2.replace(/<\/head>/i, `${contextScript}</head>`);
|
|
2807
|
-
console.log(`[viewFile] Context script injected before </head
|
|
2666
|
+
console.log(`[viewFile] Context script injected before </head>`);
|
|
2808
2667
|
} else if (content2.toLowerCase().includes("<html>")) {
|
|
2809
2668
|
content2 = content2.replace(/<html>/i, `<html>${contextScript}`);
|
|
2810
|
-
console.log(`[viewFile] Context script injected after <html
|
|
2669
|
+
console.log(`[viewFile] Context script injected after <html>`);
|
|
2811
2670
|
} else {
|
|
2812
2671
|
content2 = contextScript + content2;
|
|
2813
|
-
console.log(`[viewFile] Context script prepended to content
|
|
2672
|
+
console.log(`[viewFile] Context script prepended to content`);
|
|
2814
2673
|
}
|
|
2815
|
-
|
|
2816
|
-
}
|
|
2817
|
-
return reply.status(200).type(contentType2).header("Content-Disposition", "inline").send(nodeStream);
|
|
2818
|
-
}
|
|
2819
|
-
const bodyUnknown = downloadResult.body;
|
|
2820
|
-
let buf;
|
|
2821
|
-
let contentType = inferredContentType;
|
|
2822
|
-
if (bodyUnknown instanceof ArrayBuffer) {
|
|
2823
|
-
buf = Buffer.from(bodyUnknown);
|
|
2824
|
-
} else if (bodyUnknown instanceof Buffer) {
|
|
2825
|
-
buf = bodyUnknown;
|
|
2826
|
-
} else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
|
|
2827
|
-
const res = bodyUnknown;
|
|
2828
|
-
buf = Buffer.from(await res.arrayBuffer());
|
|
2829
|
-
if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
|
|
2830
|
-
} else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
|
|
2831
|
-
const blob = await bodyUnknown.blob();
|
|
2832
|
-
buf = Buffer.from(await blob.arrayBuffer());
|
|
2833
|
-
} else {
|
|
2834
|
-
return reply.status(502).send({ success: false, error: "Unexpected view response format" });
|
|
2835
|
-
}
|
|
2836
|
-
const isHtml = contentType?.toLowerCase().includes("text/html") || filename2.toLowerCase().endsWith(".html") || filename2.toLowerCase().endsWith(".htm");
|
|
2837
|
-
if (isHtml) {
|
|
2838
|
-
console.log(`[viewFile] Injecting AI2APP context for sandbox HTML file: ${filename2}, tenantId: ${tenantId}, contentType: ${contentType}`);
|
|
2839
|
-
let content2 = buf.toString("utf-8");
|
|
2840
|
-
const contextScript = `<script>window.__AI2APP_CONTEXT__=${JSON.stringify({
|
|
2841
|
-
tenantId,
|
|
2842
|
-
workspaceId,
|
|
2843
|
-
projectId,
|
|
2844
|
-
timestamp: Date.now()
|
|
2845
|
-
})};</script>`;
|
|
2846
|
-
if (content2.toLowerCase().includes("</head>")) {
|
|
2847
|
-
content2 = content2.replace(/<\/head>/i, `${contextScript}</head>`);
|
|
2848
|
-
console.log(`[viewFile] Context script injected before </head>`);
|
|
2849
|
-
} else if (content2.toLowerCase().includes("<html>")) {
|
|
2850
|
-
content2 = content2.replace(/<html>/i, `<html>${contextScript}`);
|
|
2851
|
-
console.log(`[viewFile] Context script injected after <html>`);
|
|
2852
|
-
} else {
|
|
2853
|
-
content2 = contextScript + content2;
|
|
2854
|
-
console.log(`[viewFile] Context script prepended to content`);
|
|
2674
|
+
outputBuf = Buffer.from(content2, "utf-8");
|
|
2855
2675
|
}
|
|
2856
|
-
|
|
2676
|
+
return reply.status(200).type(contentType).header("Content-Disposition", "inline").send(outputBuf);
|
|
2677
|
+
} catch (err) {
|
|
2678
|
+
return reply.status(502).send({ success: false, error: String(err) });
|
|
2857
2679
|
}
|
|
2858
|
-
return reply.status(200).type(contentType).header("Content-Disposition", "inline").send(buf);
|
|
2859
2680
|
}
|
|
2860
2681
|
const { backend } = await this.getBackend(tenantId, workspaceId, projectId);
|
|
2861
2682
|
const content = await backend.read(resolvedPath, 0, Infinity);
|
|
@@ -2934,26 +2755,25 @@ var WorkspaceController = class {
|
|
|
2934
2755
|
const filename = data.filename || "file";
|
|
2935
2756
|
const pathEntry = data.fields?.path;
|
|
2936
2757
|
const pathValue = pathEntry && typeof pathEntry === "object" && "value" in pathEntry ? String(pathEntry.value) : typeof pathEntry === "string" ? pathEntry : void 0;
|
|
2937
|
-
if (pathValue && !/^[a-zA-Z0-9_
|
|
2758
|
+
if (pathValue && !/^[a-zA-Z0-9_./~\-]+$/.test(pathValue)) {
|
|
2938
2759
|
return reply.status(400).send({ success: false, error: "Invalid path parameter" });
|
|
2939
2760
|
}
|
|
2940
2761
|
if (workspace.storageType === "sandbox") {
|
|
2941
2762
|
const sandboxManager = getSandBoxManager3();
|
|
2942
|
-
const
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
});
|
|
2763
|
+
const sandbox = await sandboxManager.getSandboxFromConfig({
|
|
2764
|
+
assistant_id: "",
|
|
2765
|
+
thread_id: "",
|
|
2766
|
+
tenantId,
|
|
2767
|
+
workspaceId,
|
|
2768
|
+
projectId
|
|
2769
|
+
});
|
|
2770
|
+
const realPath = pathValue ? path.posix.join(pathValue, filename) : filename;
|
|
2771
|
+
try {
|
|
2772
|
+
await sandbox.file.uploadFile({ file: realPath, data: buffer });
|
|
2773
|
+
} catch (err) {
|
|
2774
|
+
return reply.status(500).send({ success: false, error: String(err) });
|
|
2955
2775
|
}
|
|
2956
|
-
const relativePath =
|
|
2776
|
+
const relativePath = pathValue ? path.posix.join(pathValue, filename) : `/${filename}`;
|
|
2957
2777
|
const result2 = {
|
|
2958
2778
|
path: relativePath,
|
|
2959
2779
|
name: filename,
|
|
@@ -3045,7 +2865,7 @@ function registerWorkspaceRoutes(app2) {
|
|
|
3045
2865
|
|
|
3046
2866
|
// src/controllers/database-configs.ts
|
|
3047
2867
|
import {
|
|
3048
|
-
getStoreLattice as
|
|
2868
|
+
getStoreLattice as getStoreLattice5,
|
|
3049
2869
|
sqlDatabaseManager
|
|
3050
2870
|
} from "@axiom-lattice/core";
|
|
3051
2871
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
@@ -3059,7 +2879,7 @@ function getTenantId6(request) {
|
|
|
3059
2879
|
async function getDatabaseConfigList(request, reply) {
|
|
3060
2880
|
const tenantId = getTenantId6(request);
|
|
3061
2881
|
try {
|
|
3062
|
-
const storeLattice =
|
|
2882
|
+
const storeLattice = getStoreLattice5("default", "database");
|
|
3063
2883
|
const store = storeLattice.store;
|
|
3064
2884
|
const configs = await store.getAllConfigs(tenantId);
|
|
3065
2885
|
console.log("Backend: getAllConfigs returned:", configs);
|
|
@@ -3090,7 +2910,7 @@ async function getDatabaseConfig(request, reply) {
|
|
|
3090
2910
|
const tenantId = getTenantId6(request);
|
|
3091
2911
|
const { key } = request.params;
|
|
3092
2912
|
try {
|
|
3093
|
-
const storeLattice =
|
|
2913
|
+
const storeLattice = getStoreLattice5("default", "database");
|
|
3094
2914
|
const store = storeLattice.store;
|
|
3095
2915
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3096
2916
|
if (!config) {
|
|
@@ -3116,7 +2936,7 @@ async function createDatabaseConfig(request, reply) {
|
|
|
3116
2936
|
const tenantId = getTenantId6(request);
|
|
3117
2937
|
const body = request.body;
|
|
3118
2938
|
try {
|
|
3119
|
-
const storeLattice =
|
|
2939
|
+
const storeLattice = getStoreLattice5("default", "database");
|
|
3120
2940
|
const store = storeLattice.store;
|
|
3121
2941
|
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
3122
2942
|
if (existing) {
|
|
@@ -3152,7 +2972,7 @@ async function updateDatabaseConfig(request, reply) {
|
|
|
3152
2972
|
const { key } = request.params;
|
|
3153
2973
|
const updates = request.body;
|
|
3154
2974
|
try {
|
|
3155
|
-
const storeLattice =
|
|
2975
|
+
const storeLattice = getStoreLattice5("default", "database");
|
|
3156
2976
|
const store = storeLattice.store;
|
|
3157
2977
|
const existing = await store.getConfigByKey(tenantId, key);
|
|
3158
2978
|
if (!existing) {
|
|
@@ -3193,7 +3013,7 @@ async function deleteDatabaseConfig(request, reply) {
|
|
|
3193
3013
|
const tenantId = getTenantId6(request);
|
|
3194
3014
|
const { keyOrId } = request.params;
|
|
3195
3015
|
try {
|
|
3196
|
-
const storeLattice =
|
|
3016
|
+
const storeLattice = getStoreLattice5("default", "database");
|
|
3197
3017
|
const store = storeLattice.store;
|
|
3198
3018
|
console.log("Delete request - keyOrId:", keyOrId);
|
|
3199
3019
|
let config = await store.getConfigByKey(tenantId, keyOrId);
|
|
@@ -3242,7 +3062,7 @@ async function testDatabaseConnection(request, reply) {
|
|
|
3242
3062
|
const tenantId = getTenantId6(request);
|
|
3243
3063
|
const { key } = request.params;
|
|
3244
3064
|
try {
|
|
3245
|
-
const storeLattice =
|
|
3065
|
+
const storeLattice = getStoreLattice5("default", "database");
|
|
3246
3066
|
const store = storeLattice.store;
|
|
3247
3067
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3248
3068
|
if (!config) {
|
|
@@ -3327,7 +3147,7 @@ function registerDatabaseConfigRoutes(app2) {
|
|
|
3327
3147
|
|
|
3328
3148
|
// src/controllers/metrics-configs.ts
|
|
3329
3149
|
import {
|
|
3330
|
-
getStoreLattice as
|
|
3150
|
+
getStoreLattice as getStoreLattice6,
|
|
3331
3151
|
metricsServerManager as metricsServerManager2,
|
|
3332
3152
|
SemanticMetricsClient as SemanticMetricsClient2
|
|
3333
3153
|
} from "@axiom-lattice/core";
|
|
@@ -3342,7 +3162,7 @@ function getTenantId7(request) {
|
|
|
3342
3162
|
async function getMetricsServerConfigList(request, reply) {
|
|
3343
3163
|
const tenantId = getTenantId7(request);
|
|
3344
3164
|
try {
|
|
3345
|
-
const storeLattice =
|
|
3165
|
+
const storeLattice = getStoreLattice6("default", "metrics");
|
|
3346
3166
|
const store = storeLattice.store;
|
|
3347
3167
|
const configs = await store.getAllConfigs(tenantId);
|
|
3348
3168
|
return {
|
|
@@ -3369,7 +3189,7 @@ async function getMetricsServerConfig(request, reply) {
|
|
|
3369
3189
|
const tenantId = getTenantId7(request);
|
|
3370
3190
|
const { key } = request.params;
|
|
3371
3191
|
try {
|
|
3372
|
-
const storeLattice =
|
|
3192
|
+
const storeLattice = getStoreLattice6("default", "metrics");
|
|
3373
3193
|
const store = storeLattice.store;
|
|
3374
3194
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3375
3195
|
if (!config) {
|
|
@@ -3395,7 +3215,7 @@ async function createMetricsServerConfig(request, reply) {
|
|
|
3395
3215
|
const tenantId = getTenantId7(request);
|
|
3396
3216
|
const body = request.body;
|
|
3397
3217
|
try {
|
|
3398
|
-
const storeLattice =
|
|
3218
|
+
const storeLattice = getStoreLattice6("default", "metrics");
|
|
3399
3219
|
const store = storeLattice.store;
|
|
3400
3220
|
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
3401
3221
|
if (existing) {
|
|
@@ -3447,7 +3267,7 @@ async function updateMetricsServerConfig(request, reply) {
|
|
|
3447
3267
|
const { key } = request.params;
|
|
3448
3268
|
const updates = request.body;
|
|
3449
3269
|
try {
|
|
3450
|
-
const storeLattice =
|
|
3270
|
+
const storeLattice = getStoreLattice6("default", "metrics");
|
|
3451
3271
|
const store = storeLattice.store;
|
|
3452
3272
|
const existing = await store.getConfigByKey(tenantId, key);
|
|
3453
3273
|
if (!existing) {
|
|
@@ -3497,7 +3317,7 @@ async function deleteMetricsServerConfig(request, reply) {
|
|
|
3497
3317
|
const tenantId = getTenantId7(request);
|
|
3498
3318
|
const { keyOrId } = request.params;
|
|
3499
3319
|
try {
|
|
3500
|
-
const storeLattice =
|
|
3320
|
+
const storeLattice = getStoreLattice6("default", "metrics");
|
|
3501
3321
|
const store = storeLattice.store;
|
|
3502
3322
|
let config = await store.getConfigByKey(tenantId, keyOrId);
|
|
3503
3323
|
let configKey = keyOrId;
|
|
@@ -3544,7 +3364,7 @@ async function testMetricsServerConnection(request, reply) {
|
|
|
3544
3364
|
const tenantId = getTenantId7(request);
|
|
3545
3365
|
const { key } = request.params;
|
|
3546
3366
|
try {
|
|
3547
|
-
const storeLattice =
|
|
3367
|
+
const storeLattice = getStoreLattice6("default", "metrics");
|
|
3548
3368
|
const store = storeLattice.store;
|
|
3549
3369
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3550
3370
|
if (!config) {
|
|
@@ -3595,7 +3415,7 @@ async function listAvailableMetrics(request, reply) {
|
|
|
3595
3415
|
const tenantId = getTenantId7(request);
|
|
3596
3416
|
const { key } = request.params;
|
|
3597
3417
|
try {
|
|
3598
|
-
const storeLattice =
|
|
3418
|
+
const storeLattice = getStoreLattice6("default", "metrics");
|
|
3599
3419
|
const store = storeLattice.store;
|
|
3600
3420
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3601
3421
|
if (!config) {
|
|
@@ -3634,7 +3454,7 @@ async function queryMetricsData(request, reply) {
|
|
|
3634
3454
|
const { key } = request.params;
|
|
3635
3455
|
const { metricName, startTime, endTime, step, labels } = request.body;
|
|
3636
3456
|
try {
|
|
3637
|
-
const storeLattice =
|
|
3457
|
+
const storeLattice = getStoreLattice6("default", "metrics");
|
|
3638
3458
|
const store = storeLattice.store;
|
|
3639
3459
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3640
3460
|
if (!config) {
|
|
@@ -3681,7 +3501,7 @@ async function getDataSources(request, reply) {
|
|
|
3681
3501
|
const tenantId = getTenantId7(request);
|
|
3682
3502
|
const { key } = request.params;
|
|
3683
3503
|
try {
|
|
3684
|
-
const storeLattice =
|
|
3504
|
+
const storeLattice = getStoreLattice6("default", "metrics");
|
|
3685
3505
|
const store = storeLattice.store;
|
|
3686
3506
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3687
3507
|
if (!config) {
|
|
@@ -3722,7 +3542,7 @@ async function getDatasourceMetrics(request, reply) {
|
|
|
3722
3542
|
const tenantId = getTenantId7(request);
|
|
3723
3543
|
const { key, datasourceId } = request.params;
|
|
3724
3544
|
try {
|
|
3725
|
-
const storeLattice =
|
|
3545
|
+
const storeLattice = getStoreLattice6("default", "metrics");
|
|
3726
3546
|
const store = storeLattice.store;
|
|
3727
3547
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3728
3548
|
if (!config) {
|
|
@@ -3760,7 +3580,7 @@ async function querySemanticMetrics(request, reply) {
|
|
|
3760
3580
|
const { key } = request.params;
|
|
3761
3581
|
const body = request.body;
|
|
3762
3582
|
try {
|
|
3763
|
-
const storeLattice =
|
|
3583
|
+
const storeLattice = getStoreLattice6("default", "metrics");
|
|
3764
3584
|
const store = storeLattice.store;
|
|
3765
3585
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3766
3586
|
if (!config) {
|
|
@@ -3914,7 +3734,7 @@ function registerMetricsServerConfigRoutes(app2) {
|
|
|
3914
3734
|
|
|
3915
3735
|
// src/controllers/mcp-configs.ts
|
|
3916
3736
|
import {
|
|
3917
|
-
getStoreLattice as
|
|
3737
|
+
getStoreLattice as getStoreLattice7,
|
|
3918
3738
|
mcpManager,
|
|
3919
3739
|
toolLatticeManager as toolLatticeManager2
|
|
3920
3740
|
} from "@axiom-lattice/core";
|
|
@@ -3929,7 +3749,7 @@ function getTenantId8(request) {
|
|
|
3929
3749
|
async function getMcpServerConfigList(request, reply) {
|
|
3930
3750
|
const tenantId = getTenantId8(request);
|
|
3931
3751
|
try {
|
|
3932
|
-
const storeLattice =
|
|
3752
|
+
const storeLattice = getStoreLattice7("default", "mcp");
|
|
3933
3753
|
const store = storeLattice.store;
|
|
3934
3754
|
const configs = await store.getAllConfigs(tenantId);
|
|
3935
3755
|
return {
|
|
@@ -3956,7 +3776,7 @@ async function getMcpServerConfig(request, reply) {
|
|
|
3956
3776
|
const tenantId = getTenantId8(request);
|
|
3957
3777
|
const { key } = request.params;
|
|
3958
3778
|
try {
|
|
3959
|
-
const storeLattice =
|
|
3779
|
+
const storeLattice = getStoreLattice7("default", "mcp");
|
|
3960
3780
|
const store = storeLattice.store;
|
|
3961
3781
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3962
3782
|
if (!config) {
|
|
@@ -3982,7 +3802,7 @@ async function createMcpServerConfig(request, reply) {
|
|
|
3982
3802
|
const tenantId = getTenantId8(request);
|
|
3983
3803
|
const body = request.body;
|
|
3984
3804
|
try {
|
|
3985
|
-
const storeLattice =
|
|
3805
|
+
const storeLattice = getStoreLattice7("default", "mcp");
|
|
3986
3806
|
const store = storeLattice.store;
|
|
3987
3807
|
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
3988
3808
|
if (existing) {
|
|
@@ -4022,7 +3842,7 @@ async function updateMcpServerConfig(request, reply) {
|
|
|
4022
3842
|
const { key } = request.params;
|
|
4023
3843
|
const updates = request.body;
|
|
4024
3844
|
try {
|
|
4025
|
-
const storeLattice =
|
|
3845
|
+
const storeLattice = getStoreLattice7("default", "mcp");
|
|
4026
3846
|
const store = storeLattice.store;
|
|
4027
3847
|
const existing = await store.getConfigByKey(tenantId, key);
|
|
4028
3848
|
if (!existing) {
|
|
@@ -4071,7 +3891,7 @@ async function deleteMcpServerConfig(request, reply) {
|
|
|
4071
3891
|
const tenantId = getTenantId8(request);
|
|
4072
3892
|
const { keyOrId } = request.params;
|
|
4073
3893
|
try {
|
|
4074
|
-
const storeLattice =
|
|
3894
|
+
const storeLattice = getStoreLattice7("default", "mcp");
|
|
4075
3895
|
const store = storeLattice.store;
|
|
4076
3896
|
let config = await store.getConfigByKey(tenantId, keyOrId);
|
|
4077
3897
|
let configKey = keyOrId;
|
|
@@ -4118,7 +3938,7 @@ async function testMcpServerConnection(request, reply) {
|
|
|
4118
3938
|
const tenantId = getTenantId8(request);
|
|
4119
3939
|
const { key } = request.params;
|
|
4120
3940
|
try {
|
|
4121
|
-
const storeLattice =
|
|
3941
|
+
const storeLattice = getStoreLattice7("default", "mcp");
|
|
4122
3942
|
const store = storeLattice.store;
|
|
4123
3943
|
const config = await store.getConfigByKey(tenantId, key);
|
|
4124
3944
|
if (!config) {
|
|
@@ -4171,7 +3991,7 @@ async function listMcpServerTools(request, reply) {
|
|
|
4171
3991
|
const tenantId = getTenantId8(request);
|
|
4172
3992
|
const { key } = request.params;
|
|
4173
3993
|
try {
|
|
4174
|
-
const storeLattice =
|
|
3994
|
+
const storeLattice = getStoreLattice7("default", "mcp");
|
|
4175
3995
|
const store = storeLattice.store;
|
|
4176
3996
|
const config = await store.getConfigByKey(tenantId, key);
|
|
4177
3997
|
if (!config) {
|
|
@@ -4204,7 +4024,7 @@ async function connectMcpServer(request, reply) {
|
|
|
4204
4024
|
const tenantId = getTenantId8(request);
|
|
4205
4025
|
const { key } = request.params;
|
|
4206
4026
|
try {
|
|
4207
|
-
const storeLattice =
|
|
4027
|
+
const storeLattice = getStoreLattice7("default", "mcp");
|
|
4208
4028
|
const store = storeLattice.store;
|
|
4209
4029
|
const config = await store.getConfigByKey(tenantId, key);
|
|
4210
4030
|
if (!config) {
|
|
@@ -4225,7 +4045,7 @@ async function connectMcpServer(request, reply) {
|
|
|
4225
4045
|
};
|
|
4226
4046
|
} catch (error) {
|
|
4227
4047
|
console.error("Failed to connect MCP server:", error);
|
|
4228
|
-
const storeLattice =
|
|
4048
|
+
const storeLattice = getStoreLattice7("default", "mcp");
|
|
4229
4049
|
const store = storeLattice.store;
|
|
4230
4050
|
const config = await store.getConfigByKey(tenantId, key);
|
|
4231
4051
|
if (config) {
|
|
@@ -4241,7 +4061,7 @@ async function disconnectMcpServer(request, reply) {
|
|
|
4241
4061
|
const tenantId = getTenantId8(request);
|
|
4242
4062
|
const { key } = request.params;
|
|
4243
4063
|
try {
|
|
4244
|
-
const storeLattice =
|
|
4064
|
+
const storeLattice = getStoreLattice7("default", "mcp");
|
|
4245
4065
|
const store = storeLattice.store;
|
|
4246
4066
|
const config = await store.getConfigByKey(tenantId, key);
|
|
4247
4067
|
if (!config) {
|
|
@@ -4346,11 +4166,11 @@ function registerMcpServerConfigRoutes(app2) {
|
|
|
4346
4166
|
}
|
|
4347
4167
|
|
|
4348
4168
|
// src/controllers/users.ts
|
|
4349
|
-
import { getStoreLattice as
|
|
4169
|
+
import { getStoreLattice as getStoreLattice8 } from "@axiom-lattice/core";
|
|
4350
4170
|
import { v4 as uuidv42 } from "uuid";
|
|
4351
4171
|
var UsersController = class {
|
|
4352
4172
|
constructor() {
|
|
4353
|
-
this.userStore =
|
|
4173
|
+
this.userStore = getStoreLattice8("default", "user").store;
|
|
4354
4174
|
}
|
|
4355
4175
|
async listUsers(request, reply) {
|
|
4356
4176
|
const { email } = request.query;
|
|
@@ -4428,11 +4248,11 @@ function registerUserRoutes(app2) {
|
|
|
4428
4248
|
}
|
|
4429
4249
|
|
|
4430
4250
|
// src/controllers/tenants.ts
|
|
4431
|
-
import { getStoreLattice as
|
|
4251
|
+
import { getStoreLattice as getStoreLattice9 } from "@axiom-lattice/core";
|
|
4432
4252
|
import { v4 as uuidv43 } from "uuid";
|
|
4433
4253
|
var TenantsController = class {
|
|
4434
4254
|
constructor() {
|
|
4435
|
-
this.tenantStore =
|
|
4255
|
+
this.tenantStore = getStoreLattice9("default", "tenant").store;
|
|
4436
4256
|
}
|
|
4437
4257
|
// ==================== Tenant CRUD ====================
|
|
4438
4258
|
async listTenants(request, reply) {
|
|
@@ -4496,7 +4316,7 @@ function registerTenantRoutes(app2) {
|
|
|
4496
4316
|
}
|
|
4497
4317
|
|
|
4498
4318
|
// src/controllers/auth.ts
|
|
4499
|
-
import { getStoreLattice as
|
|
4319
|
+
import { getStoreLattice as getStoreLattice10 } from "@axiom-lattice/core";
|
|
4500
4320
|
import { v4 as uuidv44 } from "uuid";
|
|
4501
4321
|
var defaultAuthConfig = {
|
|
4502
4322
|
autoApproveUsers: true,
|
|
@@ -4505,9 +4325,9 @@ var defaultAuthConfig = {
|
|
|
4505
4325
|
};
|
|
4506
4326
|
var AuthController = class {
|
|
4507
4327
|
constructor(config = {}) {
|
|
4508
|
-
this.userStore =
|
|
4509
|
-
this.tenantStore =
|
|
4510
|
-
this.userTenantLinkStore =
|
|
4328
|
+
this.userStore = getStoreLattice10("default", "user").store;
|
|
4329
|
+
this.tenantStore = getStoreLattice10("default", "tenant").store;
|
|
4330
|
+
this.userTenantLinkStore = getStoreLattice10("default", "userTenantLink").store;
|
|
4511
4331
|
this.config = { ...defaultAuthConfig, ...config };
|
|
4512
4332
|
}
|
|
4513
4333
|
async register(request, reply) {
|
|
@@ -4878,6 +4698,698 @@ function registerAuthRoutes(app2, config) {
|
|
|
4878
4698
|
);
|
|
4879
4699
|
}
|
|
4880
4700
|
|
|
4701
|
+
// src/channels/lark/routes.ts
|
|
4702
|
+
import { getStoreLattice as getStoreLattice11 } from "@axiom-lattice/core";
|
|
4703
|
+
import {
|
|
4704
|
+
ChannelIdentityMappingStore,
|
|
4705
|
+
PostgreSQLChannelInstallationStore
|
|
4706
|
+
} from "@axiom-lattice/pg-stores";
|
|
4707
|
+
|
|
4708
|
+
// src/channels/lark/parser.ts
|
|
4709
|
+
function parseLarkMessageEvent(payload) {
|
|
4710
|
+
const raw = payload;
|
|
4711
|
+
if (raw.header?.event_type !== "im.message.receive_v1") {
|
|
4712
|
+
return null;
|
|
4713
|
+
}
|
|
4714
|
+
const message = raw.event?.message;
|
|
4715
|
+
const openId = raw.event?.sender?.sender_id?.open_id;
|
|
4716
|
+
if (!message || !openId || message.message_type !== "text") {
|
|
4717
|
+
return null;
|
|
4718
|
+
}
|
|
4719
|
+
const parsedContent = parseLarkTextContent(message.content);
|
|
4720
|
+
if (!message.message_id || !message.chat_id || !parsedContent) {
|
|
4721
|
+
return null;
|
|
4722
|
+
}
|
|
4723
|
+
return {
|
|
4724
|
+
messageId: message.message_id,
|
|
4725
|
+
openId,
|
|
4726
|
+
chatId: message.chat_id,
|
|
4727
|
+
chatType: normalizeChatType(message.chat_type),
|
|
4728
|
+
text: parsedContent
|
|
4729
|
+
};
|
|
4730
|
+
}
|
|
4731
|
+
function parseLarkTextContent(content) {
|
|
4732
|
+
if (!content) {
|
|
4733
|
+
return null;
|
|
4734
|
+
}
|
|
4735
|
+
try {
|
|
4736
|
+
const parsed = JSON.parse(content);
|
|
4737
|
+
return typeof parsed.text === "string" ? parsed.text : null;
|
|
4738
|
+
} catch {
|
|
4739
|
+
return null;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
function normalizeChatType(chatType) {
|
|
4743
|
+
return chatType === "p2p" ? "direct" : "group";
|
|
4744
|
+
}
|
|
4745
|
+
|
|
4746
|
+
// src/channels/lark/controller.ts
|
|
4747
|
+
function createLarkEventHandler(dependencies) {
|
|
4748
|
+
return async function handleLarkEvent2(request, reply) {
|
|
4749
|
+
const installationId = request.params?.installationId;
|
|
4750
|
+
if (!installationId) {
|
|
4751
|
+
reply.status(400).send({ success: false, message: "Missing installationId" });
|
|
4752
|
+
return;
|
|
4753
|
+
}
|
|
4754
|
+
const config = await dependencies.getInstallationConfig(installationId);
|
|
4755
|
+
if (!config) {
|
|
4756
|
+
reply.status(404).send({ success: false, message: "Lark installation not found" });
|
|
4757
|
+
return;
|
|
4758
|
+
}
|
|
4759
|
+
const body = dependencies.parseRequestBody(
|
|
4760
|
+
request.body,
|
|
4761
|
+
config.encryptKey
|
|
4762
|
+
);
|
|
4763
|
+
if (!dependencies.verifyParsedBody(body, config)) {
|
|
4764
|
+
reply.status(401).send({ success: false, message: "Invalid Lark request" });
|
|
4765
|
+
return;
|
|
4766
|
+
}
|
|
4767
|
+
if (body.type === "url_verification" && body.challenge) {
|
|
4768
|
+
reply.status(200).send({ challenge: body.challenge });
|
|
4769
|
+
return;
|
|
4770
|
+
}
|
|
4771
|
+
const parsed = dependencies.parseEvent(request.body);
|
|
4772
|
+
if (!parsed) {
|
|
4773
|
+
reply.status(200).send({ success: true, ignored: true });
|
|
4774
|
+
return;
|
|
4775
|
+
}
|
|
4776
|
+
const receipt = await dependencies.claimInboundReceipt({
|
|
4777
|
+
channel: "lark",
|
|
4778
|
+
channelAppId: config.appId,
|
|
4779
|
+
externalMessageId: parsed.messageId,
|
|
4780
|
+
tenantId: config.tenantId
|
|
4781
|
+
});
|
|
4782
|
+
if (!receipt.accepted) {
|
|
4783
|
+
reply.status(200).send(
|
|
4784
|
+
receipt.status === "processing" ? { success: true, processing: true } : { success: true, duplicate: true }
|
|
4785
|
+
);
|
|
4786
|
+
return;
|
|
4787
|
+
}
|
|
4788
|
+
try {
|
|
4789
|
+
const { threadId } = await dependencies.resolveThread({
|
|
4790
|
+
channel: "lark",
|
|
4791
|
+
channelAppId: config.appId,
|
|
4792
|
+
tenantId: config.tenantId,
|
|
4793
|
+
assistantId: config.assistantId,
|
|
4794
|
+
mappingMode: config.mappingMode,
|
|
4795
|
+
openId: parsed.openId,
|
|
4796
|
+
chatId: parsed.chatId,
|
|
4797
|
+
chatType: parsed.chatType,
|
|
4798
|
+
messageId: parsed.messageId,
|
|
4799
|
+
workspaceId: config.workspaceId,
|
|
4800
|
+
projectId: config.projectId
|
|
4801
|
+
});
|
|
4802
|
+
const text = await dependencies.runAgentAndCollectText({
|
|
4803
|
+
tenantId: config.tenantId,
|
|
4804
|
+
assistantId: config.assistantId,
|
|
4805
|
+
threadId,
|
|
4806
|
+
text: parsed.text,
|
|
4807
|
+
workspaceId: config.workspaceId,
|
|
4808
|
+
projectId: config.projectId
|
|
4809
|
+
});
|
|
4810
|
+
await dependencies.sendTextReply({
|
|
4811
|
+
chatId: parsed.chatId,
|
|
4812
|
+
text,
|
|
4813
|
+
config
|
|
4814
|
+
});
|
|
4815
|
+
await dependencies.markInboundReceiptCompleted({
|
|
4816
|
+
channel: "lark",
|
|
4817
|
+
channelAppId: config.appId,
|
|
4818
|
+
externalMessageId: parsed.messageId,
|
|
4819
|
+
tenantId: config.tenantId,
|
|
4820
|
+
threadId
|
|
4821
|
+
});
|
|
4822
|
+
reply.status(200).send({ success: true, threadId });
|
|
4823
|
+
} catch (error) {
|
|
4824
|
+
await dependencies.markInboundReceiptFailed({
|
|
4825
|
+
channel: "lark",
|
|
4826
|
+
channelAppId: config.appId,
|
|
4827
|
+
externalMessageId: parsed.messageId,
|
|
4828
|
+
tenantId: config.tenantId
|
|
4829
|
+
});
|
|
4830
|
+
throw error;
|
|
4831
|
+
}
|
|
4832
|
+
};
|
|
4833
|
+
}
|
|
4834
|
+
var handleLarkEvent = createLarkEventHandler({
|
|
4835
|
+
getInstallationConfig: async () => null,
|
|
4836
|
+
parseRequestBody: (body) => body || {},
|
|
4837
|
+
verifyParsedBody: () => true,
|
|
4838
|
+
parseEvent: parseLarkMessageEvent,
|
|
4839
|
+
claimInboundReceipt: async () => ({ accepted: true, status: "processing" }),
|
|
4840
|
+
markInboundReceiptCompleted: async () => void 0,
|
|
4841
|
+
markInboundReceiptFailed: async () => void 0,
|
|
4842
|
+
resolveThread: async () => ({ threadId: "" }),
|
|
4843
|
+
runAgentAndCollectText: async () => "",
|
|
4844
|
+
sendTextReply: async () => void 0
|
|
4845
|
+
});
|
|
4846
|
+
|
|
4847
|
+
// src/channels/lark/config.ts
|
|
4848
|
+
function loadLarkIngressConfig() {
|
|
4849
|
+
return {
|
|
4850
|
+
enabled: process.env.LARK_ENABLED !== "false",
|
|
4851
|
+
appId: process.env.LARK_APP_ID || "",
|
|
4852
|
+
appSecret: process.env.LARK_APP_SECRET || "",
|
|
4853
|
+
verificationToken: process.env.LARK_VERIFICATION_TOKEN,
|
|
4854
|
+
encryptKey: process.env.LARK_ENCRYPT_KEY,
|
|
4855
|
+
tenantId: process.env.LARK_TENANT_ID || "default",
|
|
4856
|
+
assistantId: process.env.LARK_ASSISTANT_ID || "default_agent",
|
|
4857
|
+
workspaceId: process.env.LARK_WORKSPACE_ID,
|
|
4858
|
+
projectId: process.env.LARK_PROJECT_ID,
|
|
4859
|
+
mappingMode: process.env.LARK_MAPPING_MODE || "hybrid"
|
|
4860
|
+
};
|
|
4861
|
+
}
|
|
4862
|
+
function isLarkIngressEnabled(config) {
|
|
4863
|
+
return config.enabled;
|
|
4864
|
+
}
|
|
4865
|
+
|
|
4866
|
+
// src/channels/lark/mapping-service.ts
|
|
4867
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
4868
|
+
function createChannelThreadMappingService(deps) {
|
|
4869
|
+
return {
|
|
4870
|
+
async getOrCreateThread(input) {
|
|
4871
|
+
const externalSubjectKey = buildExternalSubjectKey(input);
|
|
4872
|
+
const existing = await deps.mappingStore.getMappingBySubject({
|
|
4873
|
+
channel: input.channel,
|
|
4874
|
+
channelAppId: input.channelAppId,
|
|
4875
|
+
tenantId: input.tenantId,
|
|
4876
|
+
assistantId: input.assistantId,
|
|
4877
|
+
externalSubjectKey
|
|
4878
|
+
});
|
|
4879
|
+
if (existing) {
|
|
4880
|
+
return { threadId: existing.threadId };
|
|
4881
|
+
}
|
|
4882
|
+
const threadId = (deps.uuid || randomUUID6)();
|
|
4883
|
+
await deps.threadStore.createThread(input.tenantId, input.assistantId, threadId, {
|
|
4884
|
+
metadata: {
|
|
4885
|
+
tenantId: input.tenantId,
|
|
4886
|
+
workspaceId: input.workspaceId,
|
|
4887
|
+
projectId: input.projectId,
|
|
4888
|
+
channel: input.channel,
|
|
4889
|
+
larkChatId: input.chatId,
|
|
4890
|
+
larkOpenId: input.openId
|
|
4891
|
+
}
|
|
4892
|
+
});
|
|
4893
|
+
try {
|
|
4894
|
+
await deps.mappingStore.createMapping({
|
|
4895
|
+
channel: input.channel,
|
|
4896
|
+
channelAppId: input.channelAppId,
|
|
4897
|
+
tenantId: input.tenantId,
|
|
4898
|
+
assistantId: input.assistantId,
|
|
4899
|
+
mappingMode: input.mappingMode,
|
|
4900
|
+
externalSubjectType: resolveSubjectType(input.mappingMode, input.chatType) === "user" ? "user" : "chat",
|
|
4901
|
+
externalSubjectKey,
|
|
4902
|
+
larkOpenId: input.openId,
|
|
4903
|
+
larkChatId: input.chatId,
|
|
4904
|
+
larkMessageId: input.messageId,
|
|
4905
|
+
threadId
|
|
4906
|
+
});
|
|
4907
|
+
} catch (error) {
|
|
4908
|
+
if (!isUniqueViolation(error)) {
|
|
4909
|
+
throw error;
|
|
4910
|
+
}
|
|
4911
|
+
const canonical = await deps.mappingStore.getMappingBySubject({
|
|
4912
|
+
channel: input.channel,
|
|
4913
|
+
channelAppId: input.channelAppId,
|
|
4914
|
+
tenantId: input.tenantId,
|
|
4915
|
+
assistantId: input.assistantId,
|
|
4916
|
+
externalSubjectKey
|
|
4917
|
+
});
|
|
4918
|
+
if (!canonical) {
|
|
4919
|
+
throw error;
|
|
4920
|
+
}
|
|
4921
|
+
await deps.threadStore.deleteThread(input.tenantId, threadId);
|
|
4922
|
+
return { threadId: canonical.threadId };
|
|
4923
|
+
}
|
|
4924
|
+
return { threadId };
|
|
4925
|
+
}
|
|
4926
|
+
};
|
|
4927
|
+
}
|
|
4928
|
+
function isUniqueViolation(error) {
|
|
4929
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
|
|
4930
|
+
}
|
|
4931
|
+
function buildExternalSubjectKey(input) {
|
|
4932
|
+
const subjectType = resolveSubjectType(input.mappingMode, input.chatType);
|
|
4933
|
+
const subjectValue = subjectType === "user" ? input.openId : input.chatId;
|
|
4934
|
+
return [
|
|
4935
|
+
input.channel,
|
|
4936
|
+
input.channelAppId,
|
|
4937
|
+
`tenant:${input.tenantId}`,
|
|
4938
|
+
`assistant:${input.assistantId}`,
|
|
4939
|
+
`${subjectType}:${subjectValue}`
|
|
4940
|
+
].join(":");
|
|
4941
|
+
}
|
|
4942
|
+
function resolveSubjectType(mappingMode, chatType) {
|
|
4943
|
+
if (mappingMode === "user") {
|
|
4944
|
+
return "user";
|
|
4945
|
+
}
|
|
4946
|
+
if (mappingMode === "group") {
|
|
4947
|
+
return "chat";
|
|
4948
|
+
}
|
|
4949
|
+
return chatType === "direct" ? "user" : "chat";
|
|
4950
|
+
}
|
|
4951
|
+
|
|
4952
|
+
// src/channels/lark/runner.ts
|
|
4953
|
+
import { agentInstanceManager as agentInstanceManager5 } from "@axiom-lattice/core";
|
|
4954
|
+
import { MessageChunkTypes as MessageChunkTypes3 } from "@axiom-lattice/protocols";
|
|
4955
|
+
|
|
4956
|
+
// src/channels/lark/aggregator.ts
|
|
4957
|
+
import { MessageChunkTypes as MessageChunkTypes2 } from "@axiom-lattice/protocols";
|
|
4958
|
+
function aggregateLarkReply(messageId, chunks) {
|
|
4959
|
+
return chunks.filter(
|
|
4960
|
+
(chunk) => chunk.type === MessageChunkTypes2.AI && chunk.data.id === messageId
|
|
4961
|
+
).map((chunk) => chunk.data.content || "").join("").trim();
|
|
4962
|
+
}
|
|
4963
|
+
|
|
4964
|
+
// src/channels/lark/runner.ts
|
|
4965
|
+
async function runAgentAndCollectLarkReply(input) {
|
|
4966
|
+
const agent = agentInstanceManager5.getAgent({
|
|
4967
|
+
tenant_id: input.tenantId,
|
|
4968
|
+
assistant_id: input.assistantId,
|
|
4969
|
+
thread_id: input.threadId,
|
|
4970
|
+
workspace_id: input.workspaceId,
|
|
4971
|
+
project_id: input.projectId
|
|
4972
|
+
});
|
|
4973
|
+
const result = await agent.addMessage({
|
|
4974
|
+
input: {
|
|
4975
|
+
message: input.text
|
|
4976
|
+
}
|
|
4977
|
+
});
|
|
4978
|
+
const chunks = [];
|
|
4979
|
+
const stream = agent.chunkStream(result.messageId, [
|
|
4980
|
+
MessageChunkTypes3.MESSAGE_COMPLETED
|
|
4981
|
+
]);
|
|
4982
|
+
for await (const chunk of stream) {
|
|
4983
|
+
chunks.push(chunk);
|
|
4984
|
+
}
|
|
4985
|
+
return aggregateLarkReply(result.messageId, chunks);
|
|
4986
|
+
}
|
|
4987
|
+
|
|
4988
|
+
// src/channels/lark/sender.ts
|
|
4989
|
+
function createLarkSender(config, client = createDefaultLarkClient(config)) {
|
|
4990
|
+
return {
|
|
4991
|
+
async sendTextReply(input) {
|
|
4992
|
+
const response = await client.im.v1.message.create({
|
|
4993
|
+
params: {
|
|
4994
|
+
receive_id_type: "chat_id"
|
|
4995
|
+
},
|
|
4996
|
+
data: {
|
|
4997
|
+
receive_id: input.chatId,
|
|
4998
|
+
msg_type: "text",
|
|
4999
|
+
content: JSON.stringify({ text: input.text })
|
|
5000
|
+
}
|
|
5001
|
+
});
|
|
5002
|
+
if (response.code && response.code !== 0) {
|
|
5003
|
+
throw new Error("Failed to send Lark reply");
|
|
5004
|
+
}
|
|
5005
|
+
}
|
|
5006
|
+
};
|
|
5007
|
+
}
|
|
5008
|
+
function createDefaultLarkClient(config) {
|
|
5009
|
+
const Lark = __require("@larksuiteoapi/node-sdk");
|
|
5010
|
+
return new Lark.Client({
|
|
5011
|
+
appId: config.appId,
|
|
5012
|
+
appSecret: config.appSecret
|
|
5013
|
+
});
|
|
5014
|
+
}
|
|
5015
|
+
|
|
5016
|
+
// src/channels/lark/verification.ts
|
|
5017
|
+
import crypto2 from "crypto";
|
|
5018
|
+
function parseLarkRequestBody(body, encryptKey) {
|
|
5019
|
+
const parsed = body || {};
|
|
5020
|
+
if (encryptKey && typeof parsed.encrypt === "string") {
|
|
5021
|
+
return decryptLarkPayload(encryptKey, parsed.encrypt);
|
|
5022
|
+
}
|
|
5023
|
+
return parsed;
|
|
5024
|
+
}
|
|
5025
|
+
function decryptLarkPayload(encryptKey, encryptedPayload) {
|
|
5026
|
+
const key = crypto2.createHash("sha256").update(encryptKey).digest();
|
|
5027
|
+
const buffer = Buffer.from(encryptedPayload, "base64");
|
|
5028
|
+
const iv = buffer.subarray(0, 16);
|
|
5029
|
+
const ciphertext = buffer.subarray(16);
|
|
5030
|
+
const decipher = crypto2.createDecipheriv("aes-256-cbc", key, iv);
|
|
5031
|
+
const plaintext = Buffer.concat([
|
|
5032
|
+
decipher.update(ciphertext),
|
|
5033
|
+
decipher.final()
|
|
5034
|
+
]).toString("utf8");
|
|
5035
|
+
return JSON.parse(plaintext);
|
|
5036
|
+
}
|
|
5037
|
+
function createLarkRequestVerifier(config) {
|
|
5038
|
+
return function verifyRequest(request) {
|
|
5039
|
+
const body = parseLarkRequestBody(request.body, config.encryptKey);
|
|
5040
|
+
return verifyLarkParsedBody(body, config);
|
|
5041
|
+
};
|
|
5042
|
+
}
|
|
5043
|
+
function verifyLarkParsedBody(body, config) {
|
|
5044
|
+
if (!config.verificationToken) {
|
|
5045
|
+
return true;
|
|
5046
|
+
}
|
|
5047
|
+
return extractVerificationToken(body) === config.verificationToken;
|
|
5048
|
+
}
|
|
5049
|
+
function extractVerificationToken(body) {
|
|
5050
|
+
if (typeof body.token === "string") {
|
|
5051
|
+
return body.token;
|
|
5052
|
+
}
|
|
5053
|
+
if (typeof body.header?.token === "string") {
|
|
5054
|
+
return body.header.token;
|
|
5055
|
+
}
|
|
5056
|
+
return void 0;
|
|
5057
|
+
}
|
|
5058
|
+
|
|
5059
|
+
// src/channels/lark/routes.ts
|
|
5060
|
+
function registerLarkChannelRoutes(app2, dependencies) {
|
|
5061
|
+
const config = loadLarkIngressConfig();
|
|
5062
|
+
if (!dependencies && !isLarkIngressEnabled(config)) {
|
|
5063
|
+
return;
|
|
5064
|
+
}
|
|
5065
|
+
const handlerDependencies = dependencies || createDefaultLarkDependencies();
|
|
5066
|
+
app2.post(
|
|
5067
|
+
"/api/channels/lark/installations/:installationId/events",
|
|
5068
|
+
createLarkEventHandler({
|
|
5069
|
+
...handlerDependencies
|
|
5070
|
+
})
|
|
5071
|
+
);
|
|
5072
|
+
}
|
|
5073
|
+
function createDefaultLarkDependencies() {
|
|
5074
|
+
const installationStore = new PostgreSQLChannelInstallationStore({
|
|
5075
|
+
poolConfig: getDatabaseUrl()
|
|
5076
|
+
});
|
|
5077
|
+
const threadStore = getStoreLattice11("default", "thread").store;
|
|
5078
|
+
const mappingStore = new ChannelIdentityMappingStore({
|
|
5079
|
+
poolConfig: getDatabaseUrl()
|
|
5080
|
+
});
|
|
5081
|
+
const mappingService = createChannelThreadMappingService({
|
|
5082
|
+
mappingStore,
|
|
5083
|
+
threadStore
|
|
5084
|
+
});
|
|
5085
|
+
return {
|
|
5086
|
+
getInstallationConfig: async (installationId) => {
|
|
5087
|
+
const installation = await installationStore.getInstallationById(
|
|
5088
|
+
installationId
|
|
5089
|
+
);
|
|
5090
|
+
if (!installation || installation.channel !== "lark") {
|
|
5091
|
+
return null;
|
|
5092
|
+
}
|
|
5093
|
+
return {
|
|
5094
|
+
enabled: true,
|
|
5095
|
+
installationId: installation.id,
|
|
5096
|
+
tenantId: installation.tenantId,
|
|
5097
|
+
assistantId: installation.config.assistantId,
|
|
5098
|
+
appId: installation.config.appId,
|
|
5099
|
+
appSecret: installation.config.appSecret,
|
|
5100
|
+
verificationToken: installation.config.verificationToken,
|
|
5101
|
+
encryptKey: installation.config.encryptKey,
|
|
5102
|
+
workspaceId: installation.config.workspaceId,
|
|
5103
|
+
projectId: installation.config.projectId,
|
|
5104
|
+
mappingMode: installation.config.mappingMode
|
|
5105
|
+
};
|
|
5106
|
+
},
|
|
5107
|
+
parseRequestBody: (body, encryptKey) => parseLarkRequestBody(body, encryptKey),
|
|
5108
|
+
verifyParsedBody: (body, config) => {
|
|
5109
|
+
if (!config.verificationToken) {
|
|
5110
|
+
return true;
|
|
5111
|
+
}
|
|
5112
|
+
return createLarkRequestVerifier(config)({
|
|
5113
|
+
body
|
|
5114
|
+
});
|
|
5115
|
+
},
|
|
5116
|
+
parseEvent: parseLarkMessageEvent,
|
|
5117
|
+
claimInboundReceipt: (input) => mappingStore.claimInboundReceipt(input),
|
|
5118
|
+
markInboundReceiptCompleted: (input) => mappingStore.markInboundReceiptCompleted(input),
|
|
5119
|
+
markInboundReceiptFailed: (input) => mappingStore.markInboundReceiptFailed(input),
|
|
5120
|
+
resolveThread: (input) => mappingService.getOrCreateThread(input),
|
|
5121
|
+
runAgentAndCollectText: ({ tenantId, assistantId, threadId, text, workspaceId, projectId }) => runAgentAndCollectLarkReply({
|
|
5122
|
+
tenantId,
|
|
5123
|
+
assistantId,
|
|
5124
|
+
threadId,
|
|
5125
|
+
text,
|
|
5126
|
+
workspaceId,
|
|
5127
|
+
projectId
|
|
5128
|
+
}),
|
|
5129
|
+
sendTextReply: async ({ chatId, text, config }) => {
|
|
5130
|
+
const sender = createLarkSender({
|
|
5131
|
+
appId: config.appId,
|
|
5132
|
+
appSecret: config.appSecret
|
|
5133
|
+
});
|
|
5134
|
+
await sender.sendTextReply({ chatId, text });
|
|
5135
|
+
}
|
|
5136
|
+
};
|
|
5137
|
+
}
|
|
5138
|
+
function getDatabaseUrl() {
|
|
5139
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
5140
|
+
if (!databaseUrl) {
|
|
5141
|
+
throw new Error("DATABASE_URL is required for Lark channel ingress");
|
|
5142
|
+
}
|
|
5143
|
+
return databaseUrl;
|
|
5144
|
+
}
|
|
5145
|
+
|
|
5146
|
+
// src/channels/routes.ts
|
|
5147
|
+
var channelRouteRegistrars = [
|
|
5148
|
+
(app2, dependencies) => registerLarkChannelRoutes(app2, dependencies.lark)
|
|
5149
|
+
];
|
|
5150
|
+
function registerChannelRoutes(app2, dependencies = {}) {
|
|
5151
|
+
for (const registerRoutes of channelRouteRegistrars) {
|
|
5152
|
+
registerRoutes(app2, dependencies);
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
5155
|
+
|
|
5156
|
+
// src/controllers/channel-installations.ts
|
|
5157
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
5158
|
+
function getTenantId9(request) {
|
|
5159
|
+
const userTenantId = request.user?.tenantId;
|
|
5160
|
+
if (userTenantId) {
|
|
5161
|
+
return userTenantId;
|
|
5162
|
+
}
|
|
5163
|
+
return request.headers["x-tenant-id"] || "default";
|
|
5164
|
+
}
|
|
5165
|
+
function getInstallationStore() {
|
|
5166
|
+
const { PostgreSQLChannelInstallationStore: PostgreSQLChannelInstallationStore2 } = __require("@axiom-lattice/pg-stores");
|
|
5167
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
5168
|
+
if (!databaseUrl) {
|
|
5169
|
+
throw new Error("DATABASE_URL is required for channel installation store");
|
|
5170
|
+
}
|
|
5171
|
+
return new PostgreSQLChannelInstallationStore2({
|
|
5172
|
+
poolConfig: databaseUrl
|
|
5173
|
+
});
|
|
5174
|
+
}
|
|
5175
|
+
async function getChannelInstallationList(request, reply) {
|
|
5176
|
+
const tenantId = getTenantId9(request);
|
|
5177
|
+
const { channel } = request.query;
|
|
5178
|
+
try {
|
|
5179
|
+
const store = getInstallationStore();
|
|
5180
|
+
const installations = await store.getInstallationsByTenant(tenantId, channel);
|
|
5181
|
+
return {
|
|
5182
|
+
success: true,
|
|
5183
|
+
message: "Channel installations retrieved successfully",
|
|
5184
|
+
data: {
|
|
5185
|
+
records: installations,
|
|
5186
|
+
total: installations.length
|
|
5187
|
+
}
|
|
5188
|
+
};
|
|
5189
|
+
} catch (error) {
|
|
5190
|
+
console.error("Failed to get channel installations:", error);
|
|
5191
|
+
return {
|
|
5192
|
+
success: false,
|
|
5193
|
+
message: "Failed to retrieve channel installations",
|
|
5194
|
+
data: {
|
|
5195
|
+
records: [],
|
|
5196
|
+
total: 0
|
|
5197
|
+
}
|
|
5198
|
+
};
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5201
|
+
async function getChannelInstallation(request, reply) {
|
|
5202
|
+
const tenantId = getTenantId9(request);
|
|
5203
|
+
const { installationId } = request.params;
|
|
5204
|
+
try {
|
|
5205
|
+
const store = getInstallationStore();
|
|
5206
|
+
const installation = await store.getInstallationById(installationId);
|
|
5207
|
+
if (!installation) {
|
|
5208
|
+
reply.code(404);
|
|
5209
|
+
return {
|
|
5210
|
+
success: false,
|
|
5211
|
+
message: "Channel installation not found"
|
|
5212
|
+
};
|
|
5213
|
+
}
|
|
5214
|
+
if (installation.tenantId !== tenantId) {
|
|
5215
|
+
reply.code(403);
|
|
5216
|
+
return {
|
|
5217
|
+
success: false,
|
|
5218
|
+
message: "Access denied"
|
|
5219
|
+
};
|
|
5220
|
+
}
|
|
5221
|
+
return {
|
|
5222
|
+
success: true,
|
|
5223
|
+
message: "Channel installation retrieved successfully",
|
|
5224
|
+
data: installation
|
|
5225
|
+
};
|
|
5226
|
+
} catch (error) {
|
|
5227
|
+
console.error("Failed to get channel installation:", error);
|
|
5228
|
+
return {
|
|
5229
|
+
success: false,
|
|
5230
|
+
message: "Failed to retrieve channel installation"
|
|
5231
|
+
};
|
|
5232
|
+
}
|
|
5233
|
+
}
|
|
5234
|
+
async function createChannelInstallation(request, reply) {
|
|
5235
|
+
const tenantId = getTenantId9(request);
|
|
5236
|
+
const body = request.body;
|
|
5237
|
+
try {
|
|
5238
|
+
if (!body.channel) {
|
|
5239
|
+
reply.code(400);
|
|
5240
|
+
return {
|
|
5241
|
+
success: false,
|
|
5242
|
+
message: "Channel type is required"
|
|
5243
|
+
};
|
|
5244
|
+
}
|
|
5245
|
+
if (!body.config) {
|
|
5246
|
+
reply.code(400);
|
|
5247
|
+
return {
|
|
5248
|
+
success: false,
|
|
5249
|
+
message: "Configuration is required"
|
|
5250
|
+
};
|
|
5251
|
+
}
|
|
5252
|
+
if (body.channel === "lark") {
|
|
5253
|
+
const larkConfig = body.config;
|
|
5254
|
+
if (!larkConfig.appId || !larkConfig.appSecret) {
|
|
5255
|
+
reply.code(400);
|
|
5256
|
+
return {
|
|
5257
|
+
success: false,
|
|
5258
|
+
message: "appId and appSecret are required for Lark installations"
|
|
5259
|
+
};
|
|
5260
|
+
}
|
|
5261
|
+
if (!larkConfig.assistantId) {
|
|
5262
|
+
reply.code(400);
|
|
5263
|
+
return {
|
|
5264
|
+
success: false,
|
|
5265
|
+
message: "assistantId is required for Lark installations"
|
|
5266
|
+
};
|
|
5267
|
+
}
|
|
5268
|
+
}
|
|
5269
|
+
const store = getInstallationStore();
|
|
5270
|
+
const installationId = body.id || randomUUID7();
|
|
5271
|
+
const installation = await store.createInstallation(
|
|
5272
|
+
tenantId,
|
|
5273
|
+
installationId,
|
|
5274
|
+
body
|
|
5275
|
+
);
|
|
5276
|
+
reply.code(201);
|
|
5277
|
+
return {
|
|
5278
|
+
success: true,
|
|
5279
|
+
message: "Channel installation created successfully",
|
|
5280
|
+
data: installation
|
|
5281
|
+
};
|
|
5282
|
+
} catch (error) {
|
|
5283
|
+
console.error("Failed to create channel installation:", error);
|
|
5284
|
+
if (error.message?.includes("duplicate") || error.code === "23505") {
|
|
5285
|
+
reply.code(409);
|
|
5286
|
+
return {
|
|
5287
|
+
success: false,
|
|
5288
|
+
message: "Channel installation with this ID already exists"
|
|
5289
|
+
};
|
|
5290
|
+
}
|
|
5291
|
+
return {
|
|
5292
|
+
success: false,
|
|
5293
|
+
message: "Failed to create channel installation"
|
|
5294
|
+
};
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
5297
|
+
async function updateChannelInstallation(request, reply) {
|
|
5298
|
+
const tenantId = getTenantId9(request);
|
|
5299
|
+
const { installationId } = request.params;
|
|
5300
|
+
const body = request.body;
|
|
5301
|
+
try {
|
|
5302
|
+
const store = getInstallationStore();
|
|
5303
|
+
const existing = await store.getInstallationById(installationId);
|
|
5304
|
+
if (!existing) {
|
|
5305
|
+
reply.code(404);
|
|
5306
|
+
return {
|
|
5307
|
+
success: false,
|
|
5308
|
+
message: "Channel installation not found"
|
|
5309
|
+
};
|
|
5310
|
+
}
|
|
5311
|
+
if (existing.tenantId !== tenantId) {
|
|
5312
|
+
reply.code(403);
|
|
5313
|
+
return {
|
|
5314
|
+
success: false,
|
|
5315
|
+
message: "Access denied"
|
|
5316
|
+
};
|
|
5317
|
+
}
|
|
5318
|
+
const installation = await store.updateInstallation(
|
|
5319
|
+
tenantId,
|
|
5320
|
+
installationId,
|
|
5321
|
+
body
|
|
5322
|
+
);
|
|
5323
|
+
if (!installation) {
|
|
5324
|
+
reply.code(404);
|
|
5325
|
+
return {
|
|
5326
|
+
success: false,
|
|
5327
|
+
message: "Channel installation not found"
|
|
5328
|
+
};
|
|
5329
|
+
}
|
|
5330
|
+
return {
|
|
5331
|
+
success: true,
|
|
5332
|
+
message: "Channel installation updated successfully",
|
|
5333
|
+
data: installation
|
|
5334
|
+
};
|
|
5335
|
+
} catch (error) {
|
|
5336
|
+
console.error("Failed to update channel installation:", error);
|
|
5337
|
+
return {
|
|
5338
|
+
success: false,
|
|
5339
|
+
message: "Failed to update channel installation"
|
|
5340
|
+
};
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
async function deleteChannelInstallation(request, reply) {
|
|
5344
|
+
const tenantId = getTenantId9(request);
|
|
5345
|
+
const { installationId } = request.params;
|
|
5346
|
+
try {
|
|
5347
|
+
const store = getInstallationStore();
|
|
5348
|
+
const existing = await store.getInstallationById(installationId);
|
|
5349
|
+
if (!existing) {
|
|
5350
|
+
reply.code(404);
|
|
5351
|
+
return {
|
|
5352
|
+
success: false,
|
|
5353
|
+
message: "Channel installation not found"
|
|
5354
|
+
};
|
|
5355
|
+
}
|
|
5356
|
+
if (existing.tenantId !== tenantId) {
|
|
5357
|
+
reply.code(403);
|
|
5358
|
+
return {
|
|
5359
|
+
success: false,
|
|
5360
|
+
message: "Access denied"
|
|
5361
|
+
};
|
|
5362
|
+
}
|
|
5363
|
+
const deleted = await store.deleteInstallation(tenantId, installationId);
|
|
5364
|
+
if (!deleted) {
|
|
5365
|
+
reply.code(404);
|
|
5366
|
+
return {
|
|
5367
|
+
success: false,
|
|
5368
|
+
message: "Channel installation not found"
|
|
5369
|
+
};
|
|
5370
|
+
}
|
|
5371
|
+
return {
|
|
5372
|
+
success: true,
|
|
5373
|
+
message: "Channel installation deleted successfully"
|
|
5374
|
+
};
|
|
5375
|
+
} catch (error) {
|
|
5376
|
+
console.error("Failed to delete channel installation:", error);
|
|
5377
|
+
return {
|
|
5378
|
+
success: false,
|
|
5379
|
+
message: "Failed to delete channel installation"
|
|
5380
|
+
};
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
|
|
5384
|
+
// src/routes/channel-installations.ts
|
|
5385
|
+
function registerChannelInstallationRoutes(app2) {
|
|
5386
|
+
app2.get("/api/channel-installations", getChannelInstallationList);
|
|
5387
|
+
app2.get("/api/channel-installations/:installationId", getChannelInstallation);
|
|
5388
|
+
app2.post("/api/channel-installations", createChannelInstallation);
|
|
5389
|
+
app2.put("/api/channel-installations/:installationId", updateChannelInstallation);
|
|
5390
|
+
app2.delete("/api/channel-installations/:installationId", deleteChannelInstallation);
|
|
5391
|
+
}
|
|
5392
|
+
|
|
4881
5393
|
// src/routes/index.ts
|
|
4882
5394
|
var registerLatticeRoutes = (app2) => {
|
|
4883
5395
|
app2.post("/api/runs", createRun);
|
|
@@ -5013,6 +5525,8 @@ var registerLatticeRoutes = (app2) => {
|
|
|
5013
5525
|
autoApproveUsers: process.env.AUTO_APPROVE_USERS !== "false",
|
|
5014
5526
|
allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false"
|
|
5015
5527
|
});
|
|
5528
|
+
registerChannelRoutes(app2);
|
|
5529
|
+
registerChannelInstallationRoutes(app2);
|
|
5016
5530
|
app2.delete(
|
|
5017
5531
|
"/api/assistants/:assistant_id/threads/:thread_id/pending-messages/:message_id",
|
|
5018
5532
|
removePendingMessageHandler
|
|
@@ -5082,7 +5596,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
|
|
|
5082
5596
|
};
|
|
5083
5597
|
|
|
5084
5598
|
// src/services/agent_task_consumer.ts
|
|
5085
|
-
import { eventBus as eventBus2, AGENT_TASK_EVENT, agentInstanceManager as
|
|
5599
|
+
import { eventBus as eventBus2, AGENT_TASK_EVENT, agentInstanceManager as agentInstanceManager6, QueueMode as QueueMode2 } from "@axiom-lattice/core";
|
|
5086
5600
|
var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
5087
5601
|
const {
|
|
5088
5602
|
assistant_id,
|
|
@@ -5097,7 +5611,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
5097
5611
|
console.log(
|
|
5098
5612
|
`\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
|
|
5099
5613
|
);
|
|
5100
|
-
const agent =
|
|
5614
|
+
const agent = agentInstanceManager6.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
|
|
5101
5615
|
await agent.addMessage({ input, command, custom_run_config: runConfig }, QueueMode2.STEER);
|
|
5102
5616
|
if (callback_event) {
|
|
5103
5617
|
agent.subscribeOnce("message:completed", (evt) => {
|
|
@@ -5316,7 +5830,8 @@ import {
|
|
|
5316
5830
|
sandboxLatticeManager as sandboxLatticeManager2,
|
|
5317
5831
|
sqlDatabaseManager as sqlDatabaseManager2,
|
|
5318
5832
|
getStoreLattice as getStoreLattice12,
|
|
5319
|
-
agentInstanceManager as
|
|
5833
|
+
agentInstanceManager as agentInstanceManager7,
|
|
5834
|
+
createSandboxProvider
|
|
5320
5835
|
} from "@axiom-lattice/core";
|
|
5321
5836
|
import {
|
|
5322
5837
|
LoggerType
|
|
@@ -5430,6 +5945,22 @@ app.setErrorHandler((error, request, reply) => {
|
|
|
5430
5945
|
error: error.message || "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF"
|
|
5431
5946
|
});
|
|
5432
5947
|
});
|
|
5948
|
+
function getConfiguredSandboxProvider() {
|
|
5949
|
+
const sandboxProviderType = process.env.SANDBOX_PROVIDER_TYPE || "microsandbox";
|
|
5950
|
+
return createSandboxProvider({
|
|
5951
|
+
type: sandboxProviderType,
|
|
5952
|
+
remoteBaseURL: process.env.SANDBOX_BASE_URL,
|
|
5953
|
+
microsandboxServiceBaseURL: process.env.MICROSANDBOX_SERVICE_BASE_URL,
|
|
5954
|
+
e2bApiKey: process.env.E2B_API_KEY,
|
|
5955
|
+
e2bTemplate: process.env.E2B_TEMPLATE,
|
|
5956
|
+
e2bTimeoutMs: process.env.E2B_TIMEOUT_MS ? parseInt(process.env.E2B_TIMEOUT_MS, 10) : void 0,
|
|
5957
|
+
daytonaApiKey: process.env.DAYTONA_API_KEY,
|
|
5958
|
+
daytonaApiUrl: process.env.DAYTONA_API_URL,
|
|
5959
|
+
daytonaTarget: process.env.DAYTONA_TARGET,
|
|
5960
|
+
daytonaTimeout: process.env.DAYTONA_TIMEOUT ? parseInt(process.env.DAYTONA_TIMEOUT, 10) : void 0,
|
|
5961
|
+
daytonaVolumeName: process.env.DAYTONA_VOLUME_NAME
|
|
5962
|
+
});
|
|
5963
|
+
}
|
|
5433
5964
|
var start = async (config) => {
|
|
5434
5965
|
try {
|
|
5435
5966
|
if (config?.loggerConfig) {
|
|
@@ -5453,11 +5984,8 @@ var start = async (config) => {
|
|
|
5453
5984
|
logger.warn("Failed to set database config store: " + (error instanceof Error ? error.message : String(error)));
|
|
5454
5985
|
}
|
|
5455
5986
|
if (!sandboxLatticeManager2.hasLattice("default")) {
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
baseURL: sandboxBaseURL
|
|
5459
|
-
});
|
|
5460
|
-
logger.info(`Registered sandbox manager with baseURL: ${sandboxBaseURL}`);
|
|
5987
|
+
sandboxLatticeManager2.registerLattice("default", getConfiguredSandboxProvider());
|
|
5988
|
+
logger.info("Registered sandbox manager from env configuration");
|
|
5461
5989
|
}
|
|
5462
5990
|
const target_port = config?.port || Number(process.env.PORT) || 4001;
|
|
5463
5991
|
await app.listen({ port: target_port, host: "0.0.0.0" });
|
|
@@ -5477,7 +6005,7 @@ var start = async (config) => {
|
|
|
5477
6005
|
}
|
|
5478
6006
|
try {
|
|
5479
6007
|
logger.info("Starting agent instance recovery...");
|
|
5480
|
-
const restoreStats = await
|
|
6008
|
+
const restoreStats = await agentInstanceManager7.restore();
|
|
5481
6009
|
logger.info(`Agent recovery complete: ${restoreStats.restored} threads restored, ${restoreStats.errors} errors`);
|
|
5482
6010
|
} catch (error) {
|
|
5483
6011
|
logger.error("Agent recovery failed", { error });
|