@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.js
CHANGED
|
@@ -623,11 +623,13 @@ async function getThreadList(request, reply) {
|
|
|
623
623
|
const tenantId = getTenantId2(request);
|
|
624
624
|
const { assistantId } = request.params;
|
|
625
625
|
const metadataFilter = {};
|
|
626
|
-
|
|
627
|
-
|
|
626
|
+
const workspaceId = request.headers["x-workspace-id"];
|
|
627
|
+
const projectId = request.headers["x-project-id"];
|
|
628
|
+
if (workspaceId) {
|
|
629
|
+
metadataFilter.workspaceId = workspaceId;
|
|
628
630
|
}
|
|
629
|
-
if (
|
|
630
|
-
metadataFilter.projectId =
|
|
631
|
+
if (projectId) {
|
|
632
|
+
metadataFilter.projectId = projectId;
|
|
631
633
|
}
|
|
632
634
|
const storeLattice = (0, import_core6.getStoreLattice)("default", "thread");
|
|
633
635
|
const threadStore = storeLattice.store;
|
|
@@ -1267,16 +1269,63 @@ async function getHealth(request, reply) {
|
|
|
1267
1269
|
}
|
|
1268
1270
|
}
|
|
1269
1271
|
|
|
1270
|
-
// src/
|
|
1272
|
+
// src/services/skill_service.ts
|
|
1271
1273
|
var import_core10 = require("@axiom-lattice/core");
|
|
1272
|
-
var import_core11 = require("@axiom-lattice/core");
|
|
1273
1274
|
function getTenantId4(request) {
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1275
|
+
return request.user?.tenantId || request.headers["x-tenant-id"] || "default";
|
|
1276
|
+
}
|
|
1277
|
+
function getWorkspaceId(request) {
|
|
1278
|
+
return request.headers["x-workspace-id"];
|
|
1279
|
+
}
|
|
1280
|
+
function getProjectId(request) {
|
|
1281
|
+
return request.headers["x-project-id"];
|
|
1282
|
+
}
|
|
1283
|
+
function buildContext(request) {
|
|
1284
|
+
return {
|
|
1285
|
+
workspaceId: getWorkspaceId(request),
|
|
1286
|
+
projectId: getProjectId(request)
|
|
1287
|
+
};
|
|
1279
1288
|
}
|
|
1289
|
+
var SkillService = class {
|
|
1290
|
+
constructor() {
|
|
1291
|
+
this.store = new import_core10.SandboxSkillStore();
|
|
1292
|
+
}
|
|
1293
|
+
async getAllSkills(request) {
|
|
1294
|
+
const tenantId = getTenantId4(request);
|
|
1295
|
+
return this.store.getAllSkills(tenantId, buildContext(request));
|
|
1296
|
+
}
|
|
1297
|
+
async getSkillById(request, id) {
|
|
1298
|
+
const tenantId = getTenantId4(request);
|
|
1299
|
+
return this.store.getSkillById(tenantId, id, buildContext(request));
|
|
1300
|
+
}
|
|
1301
|
+
async createSkill(request, id, data) {
|
|
1302
|
+
const tenantId = getTenantId4(request);
|
|
1303
|
+
return this.store.createSkill(tenantId, id, data, buildContext(request));
|
|
1304
|
+
}
|
|
1305
|
+
async updateSkill(request, id, updates) {
|
|
1306
|
+
const tenantId = getTenantId4(request);
|
|
1307
|
+
return this.store.updateSkill(tenantId, id, updates, buildContext(request));
|
|
1308
|
+
}
|
|
1309
|
+
async deleteSkill(request, id) {
|
|
1310
|
+
const tenantId = getTenantId4(request);
|
|
1311
|
+
return this.store.deleteSkill(tenantId, id, buildContext(request));
|
|
1312
|
+
}
|
|
1313
|
+
async searchByMetadata(request, key, value) {
|
|
1314
|
+
const tenantId = getTenantId4(request);
|
|
1315
|
+
return this.store.searchByMetadata(tenantId, key, value, buildContext(request));
|
|
1316
|
+
}
|
|
1317
|
+
async filterByCompatibility(request, compatibility) {
|
|
1318
|
+
const tenantId = getTenantId4(request);
|
|
1319
|
+
return this.store.filterByCompatibility(tenantId, compatibility, buildContext(request));
|
|
1320
|
+
}
|
|
1321
|
+
async filterByLicense(request, license) {
|
|
1322
|
+
const tenantId = getTenantId4(request);
|
|
1323
|
+
return this.store.filterByLicense(tenantId, license, buildContext(request));
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
|
|
1327
|
+
// src/controllers/skills.ts
|
|
1328
|
+
var skillService = new SkillService();
|
|
1280
1329
|
function serializeSkill(skill) {
|
|
1281
1330
|
const serialized = {
|
|
1282
1331
|
id: skill.id,
|
|
@@ -1287,192 +1336,100 @@ function serializeSkill(skill) {
|
|
|
1287
1336
|
metadata: skill.metadata || {},
|
|
1288
1337
|
content: skill.content,
|
|
1289
1338
|
subSkills: skill.subSkills,
|
|
1290
|
-
createdAt: skill.createdAt instanceof Date ? skill.createdAt.toISOString() :
|
|
1291
|
-
updatedAt: skill.updatedAt instanceof Date ? skill.updatedAt.toISOString() :
|
|
1339
|
+
createdAt: skill.createdAt instanceof Date ? skill.createdAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
|
|
1340
|
+
updatedAt: skill.updatedAt instanceof Date ? skill.updatedAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
|
|
1292
1341
|
};
|
|
1293
1342
|
Object.keys(serialized).forEach((key) => {
|
|
1294
|
-
if (serialized[key] === void 0)
|
|
1295
|
-
delete serialized[key];
|
|
1296
|
-
}
|
|
1343
|
+
if (serialized[key] === void 0) delete serialized[key];
|
|
1297
1344
|
});
|
|
1298
1345
|
return serialized;
|
|
1299
1346
|
}
|
|
1300
1347
|
async function getSkillList(request, reply) {
|
|
1301
1348
|
try {
|
|
1302
|
-
const
|
|
1303
|
-
const storeLattice = (0, import_core10.getStoreLattice)("default", "skill");
|
|
1304
|
-
const skillStore = storeLattice.store;
|
|
1305
|
-
const skills = await skillStore.getAllSkills(tenantId);
|
|
1306
|
-
const serializedSkills = skills.map(serializeSkill);
|
|
1349
|
+
const skills = await skillService.getAllSkills(request);
|
|
1307
1350
|
return {
|
|
1308
1351
|
success: true,
|
|
1309
1352
|
message: "Successfully retrieved skill list",
|
|
1310
|
-
data: {
|
|
1311
|
-
records: serializedSkills,
|
|
1312
|
-
total: serializedSkills.length
|
|
1313
|
-
}
|
|
1353
|
+
data: { records: skills.map(serializeSkill), total: skills.length }
|
|
1314
1354
|
};
|
|
1315
1355
|
} catch (error) {
|
|
1316
1356
|
return reply.status(500).send({
|
|
1317
1357
|
success: false,
|
|
1318
1358
|
message: `Failed to retrieve skills: ${error.message}`,
|
|
1319
|
-
data: {
|
|
1320
|
-
records: [],
|
|
1321
|
-
total: 0
|
|
1322
|
-
}
|
|
1359
|
+
data: { records: [], total: 0 }
|
|
1323
1360
|
});
|
|
1324
1361
|
}
|
|
1325
1362
|
}
|
|
1326
1363
|
async function getSkill(request, reply) {
|
|
1327
1364
|
try {
|
|
1328
|
-
const tenantId = getTenantId4(request);
|
|
1329
1365
|
const { id } = request.params;
|
|
1330
|
-
const
|
|
1331
|
-
const skillStore = storeLattice.store;
|
|
1332
|
-
const skill = await skillStore.getSkillById(tenantId, id);
|
|
1366
|
+
const skill = await skillService.getSkillById(request, id);
|
|
1333
1367
|
if (!skill) {
|
|
1334
|
-
return reply.status(404).send({
|
|
1335
|
-
success: false,
|
|
1336
|
-
message: "Skill not found"
|
|
1337
|
-
});
|
|
1368
|
+
return reply.status(404).send({ success: false, message: "Skill not found" });
|
|
1338
1369
|
}
|
|
1339
|
-
return {
|
|
1340
|
-
success: true,
|
|
1341
|
-
message: "Successfully retrieved skill",
|
|
1342
|
-
data: serializeSkill(skill)
|
|
1343
|
-
};
|
|
1370
|
+
return { success: true, message: "Successfully retrieved skill", data: serializeSkill(skill) };
|
|
1344
1371
|
} catch (error) {
|
|
1345
|
-
return reply.status(500).send({
|
|
1346
|
-
success: false,
|
|
1347
|
-
message: `Failed to retrieve skill: ${error.message}`
|
|
1348
|
-
});
|
|
1372
|
+
return reply.status(500).send({ success: false, message: `Failed to retrieve skill: ${error.message}` });
|
|
1349
1373
|
}
|
|
1350
1374
|
}
|
|
1351
1375
|
async function createSkill(request, reply) {
|
|
1352
1376
|
try {
|
|
1353
1377
|
const data = request.body;
|
|
1354
1378
|
if (!data.name) {
|
|
1355
|
-
return reply.status(400).send({
|
|
1356
|
-
success: false,
|
|
1357
|
-
message: "name is required"
|
|
1358
|
-
});
|
|
1379
|
+
return reply.status(400).send({ success: false, message: "name is required" });
|
|
1359
1380
|
}
|
|
1360
1381
|
if (!data.description) {
|
|
1361
|
-
return reply.status(400).send({
|
|
1362
|
-
success: false,
|
|
1363
|
-
message: "description is required"
|
|
1364
|
-
});
|
|
1365
|
-
}
|
|
1366
|
-
try {
|
|
1367
|
-
(0, import_core11.validateSkillName)(data.name);
|
|
1368
|
-
} catch (error) {
|
|
1369
|
-
return reply.status(400).send({
|
|
1370
|
-
success: false,
|
|
1371
|
-
message: error.message || "Invalid skill name format"
|
|
1372
|
-
});
|
|
1382
|
+
return reply.status(400).send({ success: false, message: "description is required" });
|
|
1373
1383
|
}
|
|
1374
1384
|
const id = request.body.id || data.name;
|
|
1375
1385
|
if (id !== data.name) {
|
|
1376
1386
|
return reply.status(400).send({
|
|
1377
1387
|
success: false,
|
|
1378
|
-
message: `id "${id}" must equal name "${data.name}"
|
|
1379
|
-
});
|
|
1380
|
-
}
|
|
1381
|
-
const tenantId = getTenantId4(request);
|
|
1382
|
-
const storeLattice = (0, import_core10.getStoreLattice)("default", "skill");
|
|
1383
|
-
const skillStore = storeLattice.store;
|
|
1384
|
-
const exists = await skillStore.hasSkill(tenantId, id);
|
|
1385
|
-
if (exists) {
|
|
1386
|
-
return reply.status(409).send({
|
|
1387
|
-
success: false,
|
|
1388
|
-
message: `Skill with id "${id}" already exists`
|
|
1388
|
+
message: `id "${id}" must equal name "${data.name}"`
|
|
1389
1389
|
});
|
|
1390
1390
|
}
|
|
1391
|
-
const
|
|
1391
|
+
const skill = await skillService.createSkill(request, id, data);
|
|
1392
1392
|
return reply.status(201).send({
|
|
1393
1393
|
success: true,
|
|
1394
1394
|
message: "Successfully created skill",
|
|
1395
|
-
data: serializeSkill(
|
|
1395
|
+
data: serializeSkill(skill)
|
|
1396
1396
|
});
|
|
1397
1397
|
} catch (error) {
|
|
1398
|
-
|
|
1399
|
-
success: false,
|
|
1400
|
-
|
|
1401
|
-
|
|
1398
|
+
if (error.message?.includes("already exists")) {
|
|
1399
|
+
return reply.status(409).send({ success: false, message: error.message });
|
|
1400
|
+
}
|
|
1401
|
+
if (error.message?.includes("must equal name") || error.message?.includes("Invalid skill name")) {
|
|
1402
|
+
return reply.status(400).send({ success: false, message: error.message });
|
|
1403
|
+
}
|
|
1404
|
+
return reply.status(500).send({ success: false, message: `Failed to create skill: ${error.message}` });
|
|
1402
1405
|
}
|
|
1403
1406
|
}
|
|
1404
1407
|
async function updateSkill(request, reply) {
|
|
1405
1408
|
try {
|
|
1406
1409
|
const { id } = request.params;
|
|
1407
1410
|
const updates = request.body;
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
} catch (error) {
|
|
1412
|
-
return reply.status(400).send({
|
|
1413
|
-
success: false,
|
|
1414
|
-
message: error.message || "Invalid skill name format"
|
|
1415
|
-
});
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
const tenantId = getTenantId4(request);
|
|
1419
|
-
const storeLattice = (0, import_core10.getStoreLattice)("default", "skill");
|
|
1420
|
-
const skillStore = storeLattice.store;
|
|
1421
|
-
const exists = await skillStore.hasSkill(tenantId, id);
|
|
1422
|
-
if (!exists) {
|
|
1423
|
-
return reply.status(404).send({
|
|
1424
|
-
success: false,
|
|
1425
|
-
message: "Skill not found"
|
|
1426
|
-
});
|
|
1427
|
-
}
|
|
1428
|
-
const updatedSkill = await skillStore.updateSkill(tenantId, id, updates);
|
|
1429
|
-
if (!updatedSkill) {
|
|
1430
|
-
return reply.status(500).send({
|
|
1431
|
-
success: false,
|
|
1432
|
-
message: "Failed to update skill"
|
|
1433
|
-
});
|
|
1411
|
+
const skill = await skillService.updateSkill(request, id, updates);
|
|
1412
|
+
if (!skill) {
|
|
1413
|
+
return reply.status(404).send({ success: false, message: "Skill not found" });
|
|
1434
1414
|
}
|
|
1435
|
-
return {
|
|
1436
|
-
success: true,
|
|
1437
|
-
message: "Successfully updated skill",
|
|
1438
|
-
data: serializeSkill(updatedSkill)
|
|
1439
|
-
};
|
|
1415
|
+
return { success: true, message: "Successfully updated skill", data: serializeSkill(skill) };
|
|
1440
1416
|
} catch (error) {
|
|
1441
|
-
|
|
1442
|
-
success: false,
|
|
1443
|
-
|
|
1444
|
-
});
|
|
1417
|
+
if (error.message?.includes("Invalid skill name")) {
|
|
1418
|
+
return reply.status(400).send({ success: false, message: error.message });
|
|
1419
|
+
}
|
|
1420
|
+
return reply.status(500).send({ success: false, message: `Failed to update skill: ${error.message}` });
|
|
1445
1421
|
}
|
|
1446
1422
|
}
|
|
1447
1423
|
async function deleteSkill(request, reply) {
|
|
1448
1424
|
try {
|
|
1449
1425
|
const { id } = request.params;
|
|
1450
|
-
const
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
const exists = await skillStore.hasSkill(tenantId, id);
|
|
1454
|
-
if (!exists) {
|
|
1455
|
-
return reply.status(404).send({
|
|
1456
|
-
success: false,
|
|
1457
|
-
message: "Skill not found"
|
|
1458
|
-
});
|
|
1459
|
-
}
|
|
1460
|
-
const deleted = await skillStore.deleteSkill(tenantId, id);
|
|
1461
|
-
if (!deleted) {
|
|
1462
|
-
return reply.status(500).send({
|
|
1463
|
-
success: false,
|
|
1464
|
-
message: "Failed to delete skill"
|
|
1465
|
-
});
|
|
1426
|
+
const result = await skillService.deleteSkill(request, id);
|
|
1427
|
+
if (!result) {
|
|
1428
|
+
return reply.status(404).send({ success: false, message: "Skill not found" });
|
|
1466
1429
|
}
|
|
1467
|
-
return {
|
|
1468
|
-
success: true,
|
|
1469
|
-
message: "Successfully deleted skill"
|
|
1470
|
-
};
|
|
1430
|
+
return { success: true, message: "Successfully deleted skill" };
|
|
1471
1431
|
} catch (error) {
|
|
1472
|
-
return reply.status(500).send({
|
|
1473
|
-
success: false,
|
|
1474
|
-
message: `Failed to delete skill: ${error.message}`
|
|
1475
|
-
});
|
|
1432
|
+
return reply.status(500).send({ success: false, message: `Failed to delete skill: ${error.message}` });
|
|
1476
1433
|
}
|
|
1477
1434
|
}
|
|
1478
1435
|
async function searchSkillsByMetadata(request, reply) {
|
|
@@ -1482,33 +1439,20 @@ async function searchSkillsByMetadata(request, reply) {
|
|
|
1482
1439
|
return reply.status(400).send({
|
|
1483
1440
|
success: false,
|
|
1484
1441
|
message: "key and value query parameters are required",
|
|
1485
|
-
data: {
|
|
1486
|
-
records: [],
|
|
1487
|
-
total: 0
|
|
1488
|
-
}
|
|
1442
|
+
data: { records: [], total: 0 }
|
|
1489
1443
|
});
|
|
1490
1444
|
}
|
|
1491
|
-
const
|
|
1492
|
-
const storeLattice = (0, import_core10.getStoreLattice)("default", "skill");
|
|
1493
|
-
const skillStore = storeLattice.store;
|
|
1494
|
-
const skills = await skillStore.searchByMetadata(tenantId, key, value);
|
|
1495
|
-
const serializedSkills = skills.map(serializeSkill);
|
|
1445
|
+
const skills = await skillService.searchByMetadata(request, key, value);
|
|
1496
1446
|
return {
|
|
1497
1447
|
success: true,
|
|
1498
1448
|
message: "Successfully searched skills",
|
|
1499
|
-
data: {
|
|
1500
|
-
records: serializedSkills,
|
|
1501
|
-
total: serializedSkills.length
|
|
1502
|
-
}
|
|
1449
|
+
data: { records: skills.map(serializeSkill), total: skills.length }
|
|
1503
1450
|
};
|
|
1504
1451
|
} catch (error) {
|
|
1505
1452
|
return reply.status(500).send({
|
|
1506
1453
|
success: false,
|
|
1507
1454
|
message: `Failed to search skills: ${error.message}`,
|
|
1508
|
-
data: {
|
|
1509
|
-
records: [],
|
|
1510
|
-
total: 0
|
|
1511
|
-
}
|
|
1455
|
+
data: { records: [], total: 0 }
|
|
1512
1456
|
});
|
|
1513
1457
|
}
|
|
1514
1458
|
}
|
|
@@ -1519,33 +1463,20 @@ async function filterSkillsByCompatibility(request, reply) {
|
|
|
1519
1463
|
return reply.status(400).send({
|
|
1520
1464
|
success: false,
|
|
1521
1465
|
message: "compatibility query parameter is required",
|
|
1522
|
-
data: {
|
|
1523
|
-
records: [],
|
|
1524
|
-
total: 0
|
|
1525
|
-
}
|
|
1466
|
+
data: { records: [], total: 0 }
|
|
1526
1467
|
});
|
|
1527
1468
|
}
|
|
1528
|
-
const
|
|
1529
|
-
const storeLattice = (0, import_core10.getStoreLattice)("default", "skill");
|
|
1530
|
-
const skillStore = storeLattice.store;
|
|
1531
|
-
const skills = await skillStore.filterByCompatibility(tenantId, compatibility);
|
|
1532
|
-
const serializedSkills = skills.map(serializeSkill);
|
|
1469
|
+
const skills = await skillService.filterByCompatibility(request, compatibility);
|
|
1533
1470
|
return {
|
|
1534
1471
|
success: true,
|
|
1535
1472
|
message: "Successfully filtered skills",
|
|
1536
|
-
data: {
|
|
1537
|
-
records: serializedSkills,
|
|
1538
|
-
total: serializedSkills.length
|
|
1539
|
-
}
|
|
1473
|
+
data: { records: skills.map(serializeSkill), total: skills.length }
|
|
1540
1474
|
};
|
|
1541
1475
|
} catch (error) {
|
|
1542
1476
|
return reply.status(500).send({
|
|
1543
1477
|
success: false,
|
|
1544
1478
|
message: `Failed to filter skills: ${error.message}`,
|
|
1545
|
-
data: {
|
|
1546
|
-
records: [],
|
|
1547
|
-
total: 0
|
|
1548
|
-
}
|
|
1479
|
+
data: { records: [], total: 0 }
|
|
1549
1480
|
});
|
|
1550
1481
|
}
|
|
1551
1482
|
}
|
|
@@ -1556,39 +1487,26 @@ async function filterSkillsByLicense(request, reply) {
|
|
|
1556
1487
|
return reply.status(400).send({
|
|
1557
1488
|
success: false,
|
|
1558
1489
|
message: "license query parameter is required",
|
|
1559
|
-
data: {
|
|
1560
|
-
records: [],
|
|
1561
|
-
total: 0
|
|
1562
|
-
}
|
|
1490
|
+
data: { records: [], total: 0 }
|
|
1563
1491
|
});
|
|
1564
1492
|
}
|
|
1565
|
-
const
|
|
1566
|
-
const storeLattice = (0, import_core10.getStoreLattice)("default", "skill");
|
|
1567
|
-
const skillStore = storeLattice.store;
|
|
1568
|
-
const skills = await skillStore.filterByLicense(tenantId, license);
|
|
1569
|
-
const serializedSkills = skills.map(serializeSkill);
|
|
1493
|
+
const skills = await skillService.filterByLicense(request, license);
|
|
1570
1494
|
return {
|
|
1571
1495
|
success: true,
|
|
1572
1496
|
message: "Successfully filtered skills",
|
|
1573
|
-
data: {
|
|
1574
|
-
records: serializedSkills,
|
|
1575
|
-
total: serializedSkills.length
|
|
1576
|
-
}
|
|
1497
|
+
data: { records: skills.map(serializeSkill), total: skills.length }
|
|
1577
1498
|
};
|
|
1578
1499
|
} catch (error) {
|
|
1579
1500
|
return reply.status(500).send({
|
|
1580
1501
|
success: false,
|
|
1581
1502
|
message: `Failed to filter skills: ${error.message}`,
|
|
1582
|
-
data: {
|
|
1583
|
-
records: [],
|
|
1584
|
-
total: 0
|
|
1585
|
-
}
|
|
1503
|
+
data: { records: [], total: 0 }
|
|
1586
1504
|
});
|
|
1587
1505
|
}
|
|
1588
1506
|
}
|
|
1589
1507
|
|
|
1590
1508
|
// src/controllers/tools.ts
|
|
1591
|
-
var
|
|
1509
|
+
var import_core11 = require("@axiom-lattice/core");
|
|
1592
1510
|
function serializeSchema(schema) {
|
|
1593
1511
|
if (!schema) {
|
|
1594
1512
|
return void 0;
|
|
@@ -1632,7 +1550,7 @@ function serializeSchema(schema) {
|
|
|
1632
1550
|
}
|
|
1633
1551
|
async function getToolConfigs(request, reply) {
|
|
1634
1552
|
try {
|
|
1635
|
-
const allLattices =
|
|
1553
|
+
const allLattices = import_core11.toolLatticeManager.getAllLattices();
|
|
1636
1554
|
const toolConfigs = allLattices.map((lattice) => {
|
|
1637
1555
|
const config = { ...lattice.config };
|
|
1638
1556
|
const serializedSchema = config.schema ? serializeSchema(config.schema) : void 0;
|
|
@@ -1670,7 +1588,7 @@ async function getToolConfigs(request, reply) {
|
|
|
1670
1588
|
}
|
|
1671
1589
|
|
|
1672
1590
|
// src/controllers/data-query.ts
|
|
1673
|
-
var
|
|
1591
|
+
var import_core12 = require("@axiom-lattice/core");
|
|
1674
1592
|
function getTenantId5(request) {
|
|
1675
1593
|
return request.headers["x-tenant-id"] || "default";
|
|
1676
1594
|
}
|
|
@@ -1701,7 +1619,7 @@ async function executeDataQuery(request, reply) {
|
|
|
1701
1619
|
message: "Cannot provide both metrics and customSql. Use one query type only."
|
|
1702
1620
|
};
|
|
1703
1621
|
}
|
|
1704
|
-
const storeLattice = (0,
|
|
1622
|
+
const storeLattice = (0, import_core12.getStoreLattice)("default", "metrics");
|
|
1705
1623
|
const store = storeLattice.store;
|
|
1706
1624
|
if (!body.serverKey) {
|
|
1707
1625
|
reply.code(400);
|
|
@@ -1732,14 +1650,14 @@ async function executeDataQuery(request, reply) {
|
|
|
1732
1650
|
message: "datasourceId is required"
|
|
1733
1651
|
};
|
|
1734
1652
|
}
|
|
1735
|
-
if (!
|
|
1653
|
+
if (!import_core12.metricsServerManager.hasServer(tenantId, body.serverKey)) {
|
|
1736
1654
|
reply.code(400);
|
|
1737
1655
|
return {
|
|
1738
1656
|
success: false,
|
|
1739
1657
|
message: `Metrics server not registered: ${body.serverKey}. Please register the server first.`
|
|
1740
1658
|
};
|
|
1741
1659
|
}
|
|
1742
|
-
const client =
|
|
1660
|
+
const client = import_core12.metricsServerManager.getClient(tenantId, body.serverKey);
|
|
1743
1661
|
if (isSemanticQuery) {
|
|
1744
1662
|
return await executeSemanticQuery(client, body, reply);
|
|
1745
1663
|
} else {
|
|
@@ -2147,7 +2065,7 @@ var getHealthSchema = {
|
|
|
2147
2065
|
};
|
|
2148
2066
|
|
|
2149
2067
|
// src/controllers/thread_status.ts
|
|
2150
|
-
var
|
|
2068
|
+
var import_core13 = require("@axiom-lattice/core");
|
|
2151
2069
|
async function removePendingMessageHandler(request, reply) {
|
|
2152
2070
|
try {
|
|
2153
2071
|
const { assistant_id, thread_id, message_id } = request.params;
|
|
@@ -2160,7 +2078,7 @@ async function removePendingMessageHandler(request, reply) {
|
|
|
2160
2078
|
if (!assistant_id) {
|
|
2161
2079
|
return reply.code(400).send({ error: "Missing assistant_id parameter" });
|
|
2162
2080
|
}
|
|
2163
|
-
const agent =
|
|
2081
|
+
const agent = import_core13.agentInstanceManager.getAgent({
|
|
2164
2082
|
assistant_id,
|
|
2165
2083
|
thread_id,
|
|
2166
2084
|
tenant_id,
|
|
@@ -2193,11 +2111,8 @@ async function removePendingMessageHandler(request, reply) {
|
|
|
2193
2111
|
}
|
|
2194
2112
|
}
|
|
2195
2113
|
|
|
2196
|
-
// src/controllers/sandbox.ts
|
|
2197
|
-
var import_stream = require("stream");
|
|
2198
|
-
|
|
2199
2114
|
// src/services/sandbox_service.ts
|
|
2200
|
-
var
|
|
2115
|
+
var import_core14 = require("@axiom-lattice/core");
|
|
2201
2116
|
var ERROR_HTML = `<!DOCTYPE html>
|
|
2202
2117
|
<html lang="zh-CN">
|
|
2203
2118
|
<head>
|
|
@@ -2289,7 +2204,7 @@ var ERROR_HTML = `<!DOCTYPE html>
|
|
|
2289
2204
|
</div>
|
|
2290
2205
|
<div class="info-item">
|
|
2291
2206
|
<span class="label">\u9694\u79BB\u7EA7\u522B</span>
|
|
2292
|
-
<span class="value" id="
|
|
2207
|
+
<span class="value" id="vmIsolation">-</span>
|
|
2293
2208
|
</div>
|
|
2294
2209
|
<div class="info-item">
|
|
2295
2210
|
<span class="label">\u9519\u8BEF\u4FE1\u606F</span>
|
|
@@ -2302,14 +2217,14 @@ var ERROR_HTML = `<!DOCTYPE html>
|
|
|
2302
2217
|
const params = new URLSearchParams(window.location.search);
|
|
2303
2218
|
document.getElementById('assistantId').textContent = params.get('assistantId') || '-';
|
|
2304
2219
|
document.getElementById('threadId').textContent = params.get('threadId') || '-';
|
|
2305
|
-
document.getElementById('
|
|
2220
|
+
document.getElementById('vmIsolation').textContent = params.get('vmIsolation') || '-';
|
|
2306
2221
|
document.getElementById('errorMsg').textContent = params.get('error') || '\u672A\u77E5\u9519\u8BEF';
|
|
2307
2222
|
</script>
|
|
2308
2223
|
</body>
|
|
2309
2224
|
</html>`;
|
|
2310
2225
|
var SandboxService = class {
|
|
2311
|
-
|
|
2312
|
-
const agentLattice =
|
|
2226
|
+
getFilesystemVmIsolation(tenantId, assistantId) {
|
|
2227
|
+
const agentLattice = import_core14.agentLatticeManager.getAgentLatticeWithTenant(tenantId, assistantId);
|
|
2313
2228
|
if (!agentLattice) {
|
|
2314
2229
|
return null;
|
|
2315
2230
|
}
|
|
@@ -2318,27 +2233,26 @@ var SandboxService = class {
|
|
|
2318
2233
|
return null;
|
|
2319
2234
|
}
|
|
2320
2235
|
const config = filesystemConfig.config;
|
|
2321
|
-
return config?.
|
|
2236
|
+
return config?.vmIsolation || null;
|
|
2322
2237
|
}
|
|
2323
|
-
computeSandboxName(assistantId, threadId,
|
|
2324
|
-
|
|
2325
|
-
switch (isolatedLevel) {
|
|
2238
|
+
computeSandboxName(assistantId, threadId, vmIsolation, tenantId, workspaceId, projectId) {
|
|
2239
|
+
switch (vmIsolation) {
|
|
2326
2240
|
case "agent":
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2241
|
+
return (0, import_core14.normalizeSandboxName)(`${tenantId ?? "default"}-${assistantId}`);
|
|
2242
|
+
case "project":
|
|
2243
|
+
return (0, import_core14.normalizeSandboxName)(
|
|
2244
|
+
`${tenantId ?? "default"}-${workspaceId ?? "default"}-${projectId ?? "default"}`
|
|
2245
|
+
);
|
|
2332
2246
|
case "global":
|
|
2333
2247
|
default:
|
|
2334
|
-
|
|
2335
|
-
break;
|
|
2248
|
+
return "global";
|
|
2336
2249
|
}
|
|
2337
|
-
|
|
2250
|
+
}
|
|
2251
|
+
getBrowserSandboxBaseURL() {
|
|
2252
|
+
return process.env.AGENT_INFRA_SANDBOX_BASE_URL || "http://localhost:8080";
|
|
2338
2253
|
}
|
|
2339
2254
|
getTargetUrl(sandboxName) {
|
|
2340
|
-
|
|
2341
|
-
return `${sandboxManager.getBaseURL()}/sandbox/${sandboxName}`;
|
|
2255
|
+
return `${this.getBrowserSandboxBaseURL()}/sandbox/${sandboxName}`;
|
|
2342
2256
|
}
|
|
2343
2257
|
async getVncHtml(sandboxName) {
|
|
2344
2258
|
const response = await fetch(`${this.getTargetUrl(sandboxName)}/vnc/index.html`);
|
|
@@ -2374,15 +2288,15 @@ var SandboxService = class {
|
|
|
2374
2288
|
);
|
|
2375
2289
|
return rewritten;
|
|
2376
2290
|
}
|
|
2377
|
-
generateErrorHtml(assistantId, threadId,
|
|
2291
|
+
generateErrorHtml(assistantId, threadId, vmIsolation, errorMessage) {
|
|
2378
2292
|
const encodedError = encodeURIComponent(errorMessage);
|
|
2379
|
-
return ERROR_HTML.replace("{assistantId}", assistantId).replace("{threadId}", threadId).replace("{
|
|
2293
|
+
return ERROR_HTML.replace("{assistantId}", assistantId).replace("{threadId}", threadId).replace("{vmIsolation}", vmIsolation).replace("{errorMessage}", errorMessage);
|
|
2380
2294
|
}
|
|
2381
2295
|
};
|
|
2382
2296
|
var sandboxService = new SandboxService();
|
|
2383
2297
|
|
|
2384
2298
|
// src/controllers/sandbox.ts
|
|
2385
|
-
var
|
|
2299
|
+
var import_core15 = require("@axiom-lattice/core");
|
|
2386
2300
|
function getFilenameFromPath(path3) {
|
|
2387
2301
|
const segments = path3.replace(/\/+$/, "").split("/");
|
|
2388
2302
|
return segments[segments.length - 1] || "download";
|
|
@@ -2415,16 +2329,16 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
2415
2329
|
console.log("[Sandbox Upload] Route matched:", request.url);
|
|
2416
2330
|
const { assistantId, threadId } = request.params;
|
|
2417
2331
|
const tenantId = request.headers["x-tenant-id"] || "default";
|
|
2418
|
-
const
|
|
2419
|
-
if (!
|
|
2332
|
+
const vmIsolation = sandboxService.getFilesystemVmIsolation(tenantId, assistantId);
|
|
2333
|
+
if (!vmIsolation) {
|
|
2420
2334
|
return reply.status(500).send({ error: "Assistant sandbox config not found" });
|
|
2421
2335
|
}
|
|
2422
2336
|
const sandboxName = sandboxService.computeSandboxName(
|
|
2423
2337
|
assistantId,
|
|
2424
2338
|
threadId,
|
|
2425
|
-
|
|
2339
|
+
vmIsolation
|
|
2426
2340
|
);
|
|
2427
|
-
const sandboxManager = (0,
|
|
2341
|
+
const sandboxManager = (0, import_core15.getSandBoxManager)();
|
|
2428
2342
|
const sandbox = await sandboxManager.createSandbox(sandboxName);
|
|
2429
2343
|
try {
|
|
2430
2344
|
const data = await request.file();
|
|
@@ -2434,17 +2348,14 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
2434
2348
|
const buffer = await data.toBuffer();
|
|
2435
2349
|
const pathEntry = data.fields?.path;
|
|
2436
2350
|
const pathValue = pathEntry && typeof pathEntry === "object" && "value" in pathEntry ? String(pathEntry.value) : typeof pathEntry === "string" ? pathEntry : void 0;
|
|
2437
|
-
const
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
});
|
|
2444
|
-
if (!uploadResult.ok) {
|
|
2445
|
-
return reply.status(502).send({ error: `Upload error: ${uploadResult.error}` });
|
|
2351
|
+
const filePath = `~/uploads/${pathValue ? pathValue : ""}${data.filename}`;
|
|
2352
|
+
try {
|
|
2353
|
+
await sandbox.file.uploadFile({ file: filePath, data: buffer });
|
|
2354
|
+
} catch (err) {
|
|
2355
|
+
reply.status(500).send({ error: String(err) });
|
|
2356
|
+
return;
|
|
2446
2357
|
}
|
|
2447
|
-
const relativePath =
|
|
2358
|
+
const relativePath = filePath.replace(`~/`, "");
|
|
2448
2359
|
const result = { id: relativePath, name: data.filename, size: buffer.length };
|
|
2449
2360
|
return reply.status(200).send({ message: "File uploaded successfully", ...result });
|
|
2450
2361
|
} catch (error) {
|
|
@@ -2462,59 +2373,30 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
2462
2373
|
if (!filePath || typeof filePath !== "string") {
|
|
2463
2374
|
return reply.status(400).send({ error: "Query parameter 'path' is required" });
|
|
2464
2375
|
}
|
|
2465
|
-
const
|
|
2466
|
-
if (!
|
|
2467
|
-
return reply.status(500).send({ error: "Assistant filesystem
|
|
2376
|
+
const vmIsolation = sandboxService.getFilesystemVmIsolation(tenantId, assistantId);
|
|
2377
|
+
if (!vmIsolation) {
|
|
2378
|
+
return reply.status(500).send({ error: "Assistant filesystem vmIsolation not found" });
|
|
2468
2379
|
}
|
|
2469
2380
|
const sandboxName = sandboxService.computeSandboxName(
|
|
2470
2381
|
assistantId,
|
|
2471
2382
|
threadId,
|
|
2472
|
-
|
|
2383
|
+
vmIsolation
|
|
2473
2384
|
);
|
|
2474
|
-
const sandboxManager = (0,
|
|
2385
|
+
const sandboxManager = (0, import_core15.getSandBoxManager)();
|
|
2475
2386
|
const sandbox = await sandboxManager.createSandbox(sandboxName);
|
|
2476
2387
|
try {
|
|
2477
|
-
const resolvedPath = filePath.startsWith("
|
|
2388
|
+
const resolvedPath = filePath.startsWith("~/") ? filePath : `~/${filePath.replace(/^\//, "")}`;
|
|
2478
2389
|
const filename = getFilenameFromPath(resolvedPath);
|
|
2479
2390
|
const inferredContentType = getContentTypeFromFilename(filename);
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
return reply.status(502).send({
|
|
2485
|
-
error: `Download error: ${JSON.stringify(downloadResult.error)}`
|
|
2486
|
-
});
|
|
2487
|
-
}
|
|
2488
|
-
const body = downloadResult.body;
|
|
2489
|
-
if (typeof body?.stream === "function") {
|
|
2490
|
-
const webStream = body.stream();
|
|
2491
|
-
const nodeStream = import_stream.Readable.fromWeb(webStream);
|
|
2492
|
-
const contentType2 = body.contentType ?? inferredContentType;
|
|
2493
|
-
const contentDisposition2 = body.contentDisposition ?? `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
|
|
2494
|
-
reply = reply.status(200).type(contentType2).header("Content-Disposition", contentDisposition2).send(nodeStream);
|
|
2391
|
+
try {
|
|
2392
|
+
const buf = await sandbox.file.downloadFile({ file: resolvedPath });
|
|
2393
|
+
const contentDisposition = `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
|
|
2394
|
+
reply = reply.status(200).type(inferredContentType).header("Content-Disposition", contentDisposition).send(buf);
|
|
2495
2395
|
return reply;
|
|
2396
|
+
} catch (err) {
|
|
2397
|
+
reply.status(500).send({ error: String(err) });
|
|
2398
|
+
return;
|
|
2496
2399
|
}
|
|
2497
|
-
const bodyUnknown = downloadResult.body;
|
|
2498
|
-
let buf;
|
|
2499
|
-
let contentType = inferredContentType;
|
|
2500
|
-
let contentDisposition = `inline; filename="${filename.replace(/"/g, '\\"')}"; filename*=UTF-8''${encodeURIComponent(filename)}`;
|
|
2501
|
-
if (bodyUnknown instanceof ArrayBuffer) {
|
|
2502
|
-
buf = Buffer.from(bodyUnknown);
|
|
2503
|
-
} else if (bodyUnknown instanceof Buffer) {
|
|
2504
|
-
buf = bodyUnknown;
|
|
2505
|
-
} else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
|
|
2506
|
-
const res = bodyUnknown;
|
|
2507
|
-
buf = Buffer.from(await res.arrayBuffer());
|
|
2508
|
-
if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
|
|
2509
|
-
if (res.headers?.get("content-disposition")) contentDisposition = res.headers.get("content-disposition");
|
|
2510
|
-
} else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
|
|
2511
|
-
const blob = await bodyUnknown.blob();
|
|
2512
|
-
buf = Buffer.from(await blob.arrayBuffer());
|
|
2513
|
-
} else {
|
|
2514
|
-
return reply.status(502).send({ error: "Unexpected download response format" });
|
|
2515
|
-
}
|
|
2516
|
-
reply = reply.status(200).type(contentType).header("Content-Disposition", contentDisposition).send(buf);
|
|
2517
|
-
return reply;
|
|
2518
2400
|
} catch (error) {
|
|
2519
2401
|
const message = error instanceof Error ? error.message : String(error);
|
|
2520
2402
|
return reply.status(502).send({ error: `Download proxy error: ${message}` });
|
|
@@ -2526,15 +2408,14 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
2526
2408
|
// src/controllers/workspace.ts
|
|
2527
2409
|
var fs = __toESM(require("fs/promises"));
|
|
2528
2410
|
var path = __toESM(require("path"));
|
|
2529
|
-
var
|
|
2411
|
+
var import_core16 = require("@axiom-lattice/core");
|
|
2530
2412
|
var import_core17 = require("@axiom-lattice/core");
|
|
2531
2413
|
var import_core18 = require("@axiom-lattice/core");
|
|
2532
|
-
var import_core19 = require("@axiom-lattice/core");
|
|
2533
2414
|
var import_uuid2 = require("uuid");
|
|
2534
2415
|
var WorkspaceController = class {
|
|
2535
2416
|
constructor() {
|
|
2536
|
-
this.workspaceStore = (0,
|
|
2537
|
-
this.projectStore = (0,
|
|
2417
|
+
this.workspaceStore = (0, import_core16.getStoreLattice)("default", "workspace").store;
|
|
2418
|
+
this.projectStore = (0, import_core16.getStoreLattice)("default", "project").store;
|
|
2538
2419
|
}
|
|
2539
2420
|
getTenantId(request) {
|
|
2540
2421
|
const userTenantId = request.user?.tenantId;
|
|
@@ -2667,19 +2548,23 @@ var WorkspaceController = class {
|
|
|
2667
2548
|
throw new Error("Workspace not found");
|
|
2668
2549
|
}
|
|
2669
2550
|
if (workspace.storageType === "sandbox") {
|
|
2670
|
-
const sandboxManager = (0,
|
|
2671
|
-
const
|
|
2672
|
-
|
|
2551
|
+
const sandboxManager = (0, import_core18.getSandBoxManager)();
|
|
2552
|
+
const sandbox = await sandboxManager.getSandboxFromConfig({
|
|
2553
|
+
assistant_id: "",
|
|
2554
|
+
thread_id: "",
|
|
2555
|
+
tenantId,
|
|
2556
|
+
workspaceId,
|
|
2557
|
+
projectId
|
|
2558
|
+
});
|
|
2673
2559
|
return {
|
|
2674
|
-
backend: new
|
|
2675
|
-
sandboxInstance: sandbox
|
|
2676
|
-
workingDirectory: `/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`
|
|
2560
|
+
backend: new import_core17.SandboxFilesystem({
|
|
2561
|
+
sandboxInstance: sandbox
|
|
2677
2562
|
}),
|
|
2678
2563
|
workspace
|
|
2679
2564
|
};
|
|
2680
2565
|
} else {
|
|
2681
2566
|
return {
|
|
2682
|
-
backend: new
|
|
2567
|
+
backend: new import_core17.FilesystemBackend({
|
|
2683
2568
|
rootDir: `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`,
|
|
2684
2569
|
virtualMode: true
|
|
2685
2570
|
}),
|
|
@@ -2722,50 +2607,26 @@ var WorkspaceController = class {
|
|
|
2722
2607
|
}
|
|
2723
2608
|
try {
|
|
2724
2609
|
const { workspace } = await this.getBackend(tenantId, workspaceId, projectId);
|
|
2725
|
-
const resolvedPath = filePath
|
|
2610
|
+
const resolvedPath = filePath;
|
|
2726
2611
|
if (workspace.storageType === "sandbox") {
|
|
2727
|
-
const sandboxManager = (0,
|
|
2728
|
-
const sandbox = await sandboxManager.
|
|
2729
|
-
|
|
2612
|
+
const sandboxManager = (0, import_core18.getSandBoxManager)();
|
|
2613
|
+
const sandbox = await sandboxManager.getSandboxFromConfig({
|
|
2614
|
+
assistant_id: "",
|
|
2615
|
+
thread_id: "",
|
|
2616
|
+
tenantId,
|
|
2617
|
+
workspaceId,
|
|
2618
|
+
projectId
|
|
2619
|
+
});
|
|
2620
|
+
const realPath = resolvedPath;
|
|
2730
2621
|
const filename2 = this.getFilenameFromPath(resolvedPath);
|
|
2731
2622
|
const inferredContentType = this.getMimeType(filename2);
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
error: `Download error: ${JSON.stringify(downloadResult.error)}`
|
|
2739
|
-
});
|
|
2740
|
-
}
|
|
2741
|
-
const body = downloadResult.body;
|
|
2742
|
-
if (typeof body?.stream === "function") {
|
|
2743
|
-
const webStream = body.stream();
|
|
2744
|
-
const nodeStream = import_stream2.Readable.fromWeb(webStream);
|
|
2745
|
-
const contentType2 = body.contentType ?? inferredContentType;
|
|
2746
|
-
const contentDisposition2 = body.contentDisposition ?? `attachment; filename*=UTF-8''${encodeURIComponent(filename2)}`;
|
|
2747
|
-
return reply.status(200).type(contentType2).header("Content-Disposition", contentDisposition2).send(nodeStream);
|
|
2748
|
-
}
|
|
2749
|
-
const bodyUnknown = downloadResult.body;
|
|
2750
|
-
let buf;
|
|
2751
|
-
let contentType = inferredContentType;
|
|
2752
|
-
let contentDisposition = `attachment; filename*=UTF-8''${encodeURIComponent(filename2)}`;
|
|
2753
|
-
if (bodyUnknown instanceof ArrayBuffer) {
|
|
2754
|
-
buf = Buffer.from(bodyUnknown);
|
|
2755
|
-
} else if (bodyUnknown instanceof Buffer) {
|
|
2756
|
-
buf = bodyUnknown;
|
|
2757
|
-
} else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
|
|
2758
|
-
const res = bodyUnknown;
|
|
2759
|
-
buf = Buffer.from(await res.arrayBuffer());
|
|
2760
|
-
if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
|
|
2761
|
-
if (res.headers?.get("content-disposition")) contentDisposition = res.headers.get("content-disposition");
|
|
2762
|
-
} else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
|
|
2763
|
-
const blob = await bodyUnknown.blob();
|
|
2764
|
-
buf = Buffer.from(await blob.arrayBuffer());
|
|
2765
|
-
} else {
|
|
2766
|
-
return reply.status(502).send({ success: false, error: "Unexpected download response format" });
|
|
2623
|
+
try {
|
|
2624
|
+
const buf = await sandbox.file.downloadFile({ file: realPath });
|
|
2625
|
+
const contentDisposition = `attachment; filename*=UTF-8''${encodeURIComponent(filename2)}`;
|
|
2626
|
+
return reply.status(200).type(inferredContentType).header("Content-Disposition", contentDisposition).send(buf);
|
|
2627
|
+
} catch (err) {
|
|
2628
|
+
return reply.status(502).send({ success: false, error: String(err) });
|
|
2767
2629
|
}
|
|
2768
|
-
return reply.status(200).type(contentType).header("Content-Disposition", contentDisposition).send(buf);
|
|
2769
2630
|
}
|
|
2770
2631
|
const { backend } = await this.getBackend(tenantId, workspaceId, projectId);
|
|
2771
2632
|
const content = await backend.read(resolvedPath, 0, Infinity);
|
|
@@ -2791,36 +2652,27 @@ var WorkspaceController = class {
|
|
|
2791
2652
|
}
|
|
2792
2653
|
try {
|
|
2793
2654
|
const { workspace } = await this.getBackend(tenantId, workspaceId, projectId);
|
|
2794
|
-
const resolvedPath = filePath
|
|
2655
|
+
const resolvedPath = filePath;
|
|
2795
2656
|
if (workspace.storageType === "sandbox") {
|
|
2796
|
-
const sandboxManager = (0,
|
|
2797
|
-
const sandbox = await sandboxManager.
|
|
2798
|
-
|
|
2657
|
+
const sandboxManager = (0, import_core18.getSandBoxManager)();
|
|
2658
|
+
const sandbox = await sandboxManager.getSandboxFromConfig({
|
|
2659
|
+
assistant_id: "",
|
|
2660
|
+
thread_id: "",
|
|
2661
|
+
tenantId,
|
|
2662
|
+
workspaceId,
|
|
2663
|
+
projectId
|
|
2664
|
+
});
|
|
2665
|
+
const realPath = resolvedPath;
|
|
2799
2666
|
const filename2 = this.getFilenameFromPath(resolvedPath);
|
|
2800
2667
|
const inferredContentType = this.getMimeType(filename2);
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
}
|
|
2810
|
-
const body = downloadResult.body;
|
|
2811
|
-
if (typeof body?.stream === "function") {
|
|
2812
|
-
const webStream = body.stream();
|
|
2813
|
-
const nodeStream = import_stream2.Readable.fromWeb(webStream);
|
|
2814
|
-
const contentType2 = body.contentType ?? inferredContentType;
|
|
2815
|
-
console.log(`[viewFile] Sandbox returned stream, contentType: ${contentType2}, filename: ${filename2}`);
|
|
2816
|
-
const isHtml2 = contentType2?.toLowerCase().includes("text/html") || filename2.toLowerCase().endsWith(".html") || filename2.toLowerCase().endsWith(".htm");
|
|
2817
|
-
if (isHtml2) {
|
|
2818
|
-
console.log(`[viewFile] HTML stream detected, collecting for context injection`);
|
|
2819
|
-
const chunks = [];
|
|
2820
|
-
for await (const chunk of nodeStream) {
|
|
2821
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2822
|
-
}
|
|
2823
|
-
let content2 = Buffer.concat(chunks).toString("utf-8");
|
|
2668
|
+
try {
|
|
2669
|
+
const buf = await sandbox.file.downloadFile({ file: realPath });
|
|
2670
|
+
let contentType = inferredContentType;
|
|
2671
|
+
const isHtml = contentType?.toLowerCase().includes("text/html") || filename2.toLowerCase().endsWith(".html") || filename2.toLowerCase().endsWith(".htm");
|
|
2672
|
+
let outputBuf = buf;
|
|
2673
|
+
if (isHtml) {
|
|
2674
|
+
console.log(`[viewFile] Injecting AI2APP context for sandbox HTML file: ${filename2}, tenantId: ${tenantId}, contentType: ${contentType}`);
|
|
2675
|
+
let content2 = buf.toString("utf-8");
|
|
2824
2676
|
const contextScript = `<script>window.__AI2APP_CONTEXT__=${JSON.stringify({
|
|
2825
2677
|
tenantId,
|
|
2826
2678
|
workspaceId,
|
|
@@ -2829,58 +2681,20 @@ var WorkspaceController = class {
|
|
|
2829
2681
|
})};</script>`;
|
|
2830
2682
|
if (content2.toLowerCase().includes("</head>")) {
|
|
2831
2683
|
content2 = content2.replace(/<\/head>/i, `${contextScript}</head>`);
|
|
2832
|
-
console.log(`[viewFile] Context script injected before </head
|
|
2684
|
+
console.log(`[viewFile] Context script injected before </head>`);
|
|
2833
2685
|
} else if (content2.toLowerCase().includes("<html>")) {
|
|
2834
2686
|
content2 = content2.replace(/<html>/i, `<html>${contextScript}`);
|
|
2835
|
-
console.log(`[viewFile] Context script injected after <html
|
|
2687
|
+
console.log(`[viewFile] Context script injected after <html>`);
|
|
2836
2688
|
} else {
|
|
2837
2689
|
content2 = contextScript + content2;
|
|
2838
|
-
console.log(`[viewFile] Context script prepended to content
|
|
2690
|
+
console.log(`[viewFile] Context script prepended to content`);
|
|
2839
2691
|
}
|
|
2840
|
-
|
|
2841
|
-
}
|
|
2842
|
-
return reply.status(200).type(contentType2).header("Content-Disposition", "inline").send(nodeStream);
|
|
2843
|
-
}
|
|
2844
|
-
const bodyUnknown = downloadResult.body;
|
|
2845
|
-
let buf;
|
|
2846
|
-
let contentType = inferredContentType;
|
|
2847
|
-
if (bodyUnknown instanceof ArrayBuffer) {
|
|
2848
|
-
buf = Buffer.from(bodyUnknown);
|
|
2849
|
-
} else if (bodyUnknown instanceof Buffer) {
|
|
2850
|
-
buf = bodyUnknown;
|
|
2851
|
-
} else if (bodyUnknown && typeof bodyUnknown.arrayBuffer === "function") {
|
|
2852
|
-
const res = bodyUnknown;
|
|
2853
|
-
buf = Buffer.from(await res.arrayBuffer());
|
|
2854
|
-
if (res.headers?.get("content-type")) contentType = res.headers.get("content-type");
|
|
2855
|
-
} else if (bodyUnknown && typeof bodyUnknown.blob === "function") {
|
|
2856
|
-
const blob = await bodyUnknown.blob();
|
|
2857
|
-
buf = Buffer.from(await blob.arrayBuffer());
|
|
2858
|
-
} else {
|
|
2859
|
-
return reply.status(502).send({ success: false, error: "Unexpected view response format" });
|
|
2860
|
-
}
|
|
2861
|
-
const isHtml = contentType?.toLowerCase().includes("text/html") || filename2.toLowerCase().endsWith(".html") || filename2.toLowerCase().endsWith(".htm");
|
|
2862
|
-
if (isHtml) {
|
|
2863
|
-
console.log(`[viewFile] Injecting AI2APP context for sandbox HTML file: ${filename2}, tenantId: ${tenantId}, contentType: ${contentType}`);
|
|
2864
|
-
let content2 = buf.toString("utf-8");
|
|
2865
|
-
const contextScript = `<script>window.__AI2APP_CONTEXT__=${JSON.stringify({
|
|
2866
|
-
tenantId,
|
|
2867
|
-
workspaceId,
|
|
2868
|
-
projectId,
|
|
2869
|
-
timestamp: Date.now()
|
|
2870
|
-
})};</script>`;
|
|
2871
|
-
if (content2.toLowerCase().includes("</head>")) {
|
|
2872
|
-
content2 = content2.replace(/<\/head>/i, `${contextScript}</head>`);
|
|
2873
|
-
console.log(`[viewFile] Context script injected before </head>`);
|
|
2874
|
-
} else if (content2.toLowerCase().includes("<html>")) {
|
|
2875
|
-
content2 = content2.replace(/<html>/i, `<html>${contextScript}`);
|
|
2876
|
-
console.log(`[viewFile] Context script injected after <html>`);
|
|
2877
|
-
} else {
|
|
2878
|
-
content2 = contextScript + content2;
|
|
2879
|
-
console.log(`[viewFile] Context script prepended to content`);
|
|
2692
|
+
outputBuf = Buffer.from(content2, "utf-8");
|
|
2880
2693
|
}
|
|
2881
|
-
|
|
2694
|
+
return reply.status(200).type(contentType).header("Content-Disposition", "inline").send(outputBuf);
|
|
2695
|
+
} catch (err) {
|
|
2696
|
+
return reply.status(502).send({ success: false, error: String(err) });
|
|
2882
2697
|
}
|
|
2883
|
-
return reply.status(200).type(contentType).header("Content-Disposition", "inline").send(buf);
|
|
2884
2698
|
}
|
|
2885
2699
|
const { backend } = await this.getBackend(tenantId, workspaceId, projectId);
|
|
2886
2700
|
const content = await backend.read(resolvedPath, 0, Infinity);
|
|
@@ -2959,26 +2773,25 @@ var WorkspaceController = class {
|
|
|
2959
2773
|
const filename = data.filename || "file";
|
|
2960
2774
|
const pathEntry = data.fields?.path;
|
|
2961
2775
|
const pathValue = pathEntry && typeof pathEntry === "object" && "value" in pathEntry ? String(pathEntry.value) : typeof pathEntry === "string" ? pathEntry : void 0;
|
|
2962
|
-
if (pathValue && !/^[a-zA-Z0-9_
|
|
2776
|
+
if (pathValue && !/^[a-zA-Z0-9_./~\-]+$/.test(pathValue)) {
|
|
2963
2777
|
return reply.status(400).send({ success: false, error: "Invalid path parameter" });
|
|
2964
2778
|
}
|
|
2965
2779
|
if (workspace.storageType === "sandbox") {
|
|
2966
|
-
const sandboxManager = (0,
|
|
2967
|
-
const
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
});
|
|
2780
|
+
const sandboxManager = (0, import_core18.getSandBoxManager)();
|
|
2781
|
+
const sandbox = await sandboxManager.getSandboxFromConfig({
|
|
2782
|
+
assistant_id: "",
|
|
2783
|
+
thread_id: "",
|
|
2784
|
+
tenantId,
|
|
2785
|
+
workspaceId,
|
|
2786
|
+
projectId
|
|
2787
|
+
});
|
|
2788
|
+
const realPath = pathValue ? path.posix.join(pathValue, filename) : filename;
|
|
2789
|
+
try {
|
|
2790
|
+
await sandbox.file.uploadFile({ file: realPath, data: buffer });
|
|
2791
|
+
} catch (err) {
|
|
2792
|
+
return reply.status(500).send({ success: false, error: String(err) });
|
|
2980
2793
|
}
|
|
2981
|
-
const relativePath =
|
|
2794
|
+
const relativePath = pathValue ? path.posix.join(pathValue, filename) : `/${filename}`;
|
|
2982
2795
|
const result2 = {
|
|
2983
2796
|
path: relativePath,
|
|
2984
2797
|
name: filename,
|
|
@@ -3069,7 +2882,7 @@ function registerWorkspaceRoutes(app2) {
|
|
|
3069
2882
|
}
|
|
3070
2883
|
|
|
3071
2884
|
// src/controllers/database-configs.ts
|
|
3072
|
-
var
|
|
2885
|
+
var import_core19 = require("@axiom-lattice/core");
|
|
3073
2886
|
var import_crypto3 = require("crypto");
|
|
3074
2887
|
function getTenantId6(request) {
|
|
3075
2888
|
const userTenantId = request.user?.tenantId;
|
|
@@ -3081,7 +2894,7 @@ function getTenantId6(request) {
|
|
|
3081
2894
|
async function getDatabaseConfigList(request, reply) {
|
|
3082
2895
|
const tenantId = getTenantId6(request);
|
|
3083
2896
|
try {
|
|
3084
|
-
const storeLattice = (0,
|
|
2897
|
+
const storeLattice = (0, import_core19.getStoreLattice)("default", "database");
|
|
3085
2898
|
const store = storeLattice.store;
|
|
3086
2899
|
const configs = await store.getAllConfigs(tenantId);
|
|
3087
2900
|
console.log("Backend: getAllConfigs returned:", configs);
|
|
@@ -3112,7 +2925,7 @@ async function getDatabaseConfig(request, reply) {
|
|
|
3112
2925
|
const tenantId = getTenantId6(request);
|
|
3113
2926
|
const { key } = request.params;
|
|
3114
2927
|
try {
|
|
3115
|
-
const storeLattice = (0,
|
|
2928
|
+
const storeLattice = (0, import_core19.getStoreLattice)("default", "database");
|
|
3116
2929
|
const store = storeLattice.store;
|
|
3117
2930
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3118
2931
|
if (!config) {
|
|
@@ -3138,7 +2951,7 @@ async function createDatabaseConfig(request, reply) {
|
|
|
3138
2951
|
const tenantId = getTenantId6(request);
|
|
3139
2952
|
const body = request.body;
|
|
3140
2953
|
try {
|
|
3141
|
-
const storeLattice = (0,
|
|
2954
|
+
const storeLattice = (0, import_core19.getStoreLattice)("default", "database");
|
|
3142
2955
|
const store = storeLattice.store;
|
|
3143
2956
|
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
3144
2957
|
if (existing) {
|
|
@@ -3151,7 +2964,7 @@ async function createDatabaseConfig(request, reply) {
|
|
|
3151
2964
|
const id = body.id || (0, import_crypto3.randomUUID)();
|
|
3152
2965
|
const config = await store.createConfig(tenantId, id, body);
|
|
3153
2966
|
try {
|
|
3154
|
-
|
|
2967
|
+
import_core19.sqlDatabaseManager.registerDatabase(tenantId, config.key, config.config);
|
|
3155
2968
|
} catch (error) {
|
|
3156
2969
|
console.warn("Failed to auto-register database:", error);
|
|
3157
2970
|
}
|
|
@@ -3174,7 +2987,7 @@ async function updateDatabaseConfig(request, reply) {
|
|
|
3174
2987
|
const { key } = request.params;
|
|
3175
2988
|
const updates = request.body;
|
|
3176
2989
|
try {
|
|
3177
|
-
const storeLattice = (0,
|
|
2990
|
+
const storeLattice = (0, import_core19.getStoreLattice)("default", "database");
|
|
3178
2991
|
const store = storeLattice.store;
|
|
3179
2992
|
const existing = await store.getConfigByKey(tenantId, key);
|
|
3180
2993
|
if (!existing) {
|
|
@@ -3193,7 +3006,7 @@ async function updateDatabaseConfig(request, reply) {
|
|
|
3193
3006
|
}
|
|
3194
3007
|
if (updates.config) {
|
|
3195
3008
|
try {
|
|
3196
|
-
|
|
3009
|
+
import_core19.sqlDatabaseManager.registerDatabase(tenantId, updated.key, updated.config);
|
|
3197
3010
|
} catch (error) {
|
|
3198
3011
|
console.warn("Failed to re-register database:", error);
|
|
3199
3012
|
}
|
|
@@ -3215,7 +3028,7 @@ async function deleteDatabaseConfig(request, reply) {
|
|
|
3215
3028
|
const tenantId = getTenantId6(request);
|
|
3216
3029
|
const { keyOrId } = request.params;
|
|
3217
3030
|
try {
|
|
3218
|
-
const storeLattice = (0,
|
|
3031
|
+
const storeLattice = (0, import_core19.getStoreLattice)("default", "database");
|
|
3219
3032
|
const store = storeLattice.store;
|
|
3220
3033
|
console.log("Delete request - keyOrId:", keyOrId);
|
|
3221
3034
|
let config = await store.getConfigByKey(tenantId, keyOrId);
|
|
@@ -3242,8 +3055,8 @@ async function deleteDatabaseConfig(request, reply) {
|
|
|
3242
3055
|
};
|
|
3243
3056
|
}
|
|
3244
3057
|
try {
|
|
3245
|
-
if (
|
|
3246
|
-
await
|
|
3058
|
+
if (import_core19.sqlDatabaseManager.hasDatabase(tenantId, configKey)) {
|
|
3059
|
+
await import_core19.sqlDatabaseManager.removeDatabase(tenantId, configKey);
|
|
3247
3060
|
}
|
|
3248
3061
|
} catch (error) {
|
|
3249
3062
|
console.warn("Failed to remove from SqlDatabaseManager:", error);
|
|
@@ -3264,7 +3077,7 @@ async function testDatabaseConnection(request, reply) {
|
|
|
3264
3077
|
const tenantId = getTenantId6(request);
|
|
3265
3078
|
const { key } = request.params;
|
|
3266
3079
|
try {
|
|
3267
|
-
const storeLattice = (0,
|
|
3080
|
+
const storeLattice = (0, import_core19.getStoreLattice)("default", "database");
|
|
3268
3081
|
const store = storeLattice.store;
|
|
3269
3082
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3270
3083
|
if (!config) {
|
|
@@ -3275,16 +3088,16 @@ async function testDatabaseConnection(request, reply) {
|
|
|
3275
3088
|
};
|
|
3276
3089
|
}
|
|
3277
3090
|
const testKey = `__test_${key}_${Date.now()}`;
|
|
3278
|
-
|
|
3091
|
+
import_core19.sqlDatabaseManager.registerDatabase(tenantId, testKey, config.config);
|
|
3279
3092
|
const startTime = Date.now();
|
|
3280
|
-
const db = await
|
|
3093
|
+
const db = await import_core19.sqlDatabaseManager.getDatabase(tenantId, testKey);
|
|
3281
3094
|
try {
|
|
3282
3095
|
await db.connect();
|
|
3283
3096
|
await db.listTables();
|
|
3284
3097
|
const latency = Date.now() - startTime;
|
|
3285
3098
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
3286
3099
|
await db.disconnect();
|
|
3287
|
-
await
|
|
3100
|
+
await import_core19.sqlDatabaseManager.removeDatabase(tenantId, testKey);
|
|
3288
3101
|
return {
|
|
3289
3102
|
success: true,
|
|
3290
3103
|
message: "Connection test successful",
|
|
@@ -3296,7 +3109,7 @@ async function testDatabaseConnection(request, reply) {
|
|
|
3296
3109
|
} catch (error) {
|
|
3297
3110
|
try {
|
|
3298
3111
|
await db.disconnect();
|
|
3299
|
-
await
|
|
3112
|
+
await import_core19.sqlDatabaseManager.removeDatabase(tenantId, testKey);
|
|
3300
3113
|
} catch {
|
|
3301
3114
|
}
|
|
3302
3115
|
return {
|
|
@@ -3348,7 +3161,7 @@ function registerDatabaseConfigRoutes(app2) {
|
|
|
3348
3161
|
}
|
|
3349
3162
|
|
|
3350
3163
|
// src/controllers/metrics-configs.ts
|
|
3351
|
-
var
|
|
3164
|
+
var import_core20 = require("@axiom-lattice/core");
|
|
3352
3165
|
var import_crypto4 = require("crypto");
|
|
3353
3166
|
function getTenantId7(request) {
|
|
3354
3167
|
const userTenantId = request.user?.tenantId;
|
|
@@ -3360,7 +3173,7 @@ function getTenantId7(request) {
|
|
|
3360
3173
|
async function getMetricsServerConfigList(request, reply) {
|
|
3361
3174
|
const tenantId = getTenantId7(request);
|
|
3362
3175
|
try {
|
|
3363
|
-
const storeLattice = (0,
|
|
3176
|
+
const storeLattice = (0, import_core20.getStoreLattice)("default", "metrics");
|
|
3364
3177
|
const store = storeLattice.store;
|
|
3365
3178
|
const configs = await store.getAllConfigs(tenantId);
|
|
3366
3179
|
return {
|
|
@@ -3387,7 +3200,7 @@ async function getMetricsServerConfig(request, reply) {
|
|
|
3387
3200
|
const tenantId = getTenantId7(request);
|
|
3388
3201
|
const { key } = request.params;
|
|
3389
3202
|
try {
|
|
3390
|
-
const storeLattice = (0,
|
|
3203
|
+
const storeLattice = (0, import_core20.getStoreLattice)("default", "metrics");
|
|
3391
3204
|
const store = storeLattice.store;
|
|
3392
3205
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3393
3206
|
if (!config) {
|
|
@@ -3413,7 +3226,7 @@ async function createMetricsServerConfig(request, reply) {
|
|
|
3413
3226
|
const tenantId = getTenantId7(request);
|
|
3414
3227
|
const body = request.body;
|
|
3415
3228
|
try {
|
|
3416
|
-
const storeLattice = (0,
|
|
3229
|
+
const storeLattice = (0, import_core20.getStoreLattice)("default", "metrics");
|
|
3417
3230
|
const store = storeLattice.store;
|
|
3418
3231
|
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
3419
3232
|
if (existing) {
|
|
@@ -3442,7 +3255,7 @@ async function createMetricsServerConfig(request, reply) {
|
|
|
3442
3255
|
};
|
|
3443
3256
|
const config = await store.createConfig(tenantId, id, configData);
|
|
3444
3257
|
try {
|
|
3445
|
-
|
|
3258
|
+
import_core20.metricsServerManager.registerServer(tenantId, config.key, config.config);
|
|
3446
3259
|
} catch (error) {
|
|
3447
3260
|
console.warn("Failed to auto-register metrics server:", error);
|
|
3448
3261
|
}
|
|
@@ -3465,7 +3278,7 @@ async function updateMetricsServerConfig(request, reply) {
|
|
|
3465
3278
|
const { key } = request.params;
|
|
3466
3279
|
const updates = request.body;
|
|
3467
3280
|
try {
|
|
3468
|
-
const storeLattice = (0,
|
|
3281
|
+
const storeLattice = (0, import_core20.getStoreLattice)("default", "metrics");
|
|
3469
3282
|
const store = storeLattice.store;
|
|
3470
3283
|
const existing = await store.getConfigByKey(tenantId, key);
|
|
3471
3284
|
if (!existing) {
|
|
@@ -3493,7 +3306,7 @@ async function updateMetricsServerConfig(request, reply) {
|
|
|
3493
3306
|
}
|
|
3494
3307
|
if (updates.config) {
|
|
3495
3308
|
try {
|
|
3496
|
-
|
|
3309
|
+
import_core20.metricsServerManager.registerServer(tenantId, updated.key, updated.config);
|
|
3497
3310
|
} catch (error) {
|
|
3498
3311
|
console.warn("Failed to re-register metrics server:", error);
|
|
3499
3312
|
}
|
|
@@ -3515,7 +3328,7 @@ async function deleteMetricsServerConfig(request, reply) {
|
|
|
3515
3328
|
const tenantId = getTenantId7(request);
|
|
3516
3329
|
const { keyOrId } = request.params;
|
|
3517
3330
|
try {
|
|
3518
|
-
const storeLattice = (0,
|
|
3331
|
+
const storeLattice = (0, import_core20.getStoreLattice)("default", "metrics");
|
|
3519
3332
|
const store = storeLattice.store;
|
|
3520
3333
|
let config = await store.getConfigByKey(tenantId, keyOrId);
|
|
3521
3334
|
let configKey = keyOrId;
|
|
@@ -3540,8 +3353,8 @@ async function deleteMetricsServerConfig(request, reply) {
|
|
|
3540
3353
|
};
|
|
3541
3354
|
}
|
|
3542
3355
|
try {
|
|
3543
|
-
if (
|
|
3544
|
-
|
|
3356
|
+
if (import_core20.metricsServerManager.hasServer(tenantId, configKey)) {
|
|
3357
|
+
import_core20.metricsServerManager.removeServer(tenantId, configKey);
|
|
3545
3358
|
}
|
|
3546
3359
|
} catch (error) {
|
|
3547
3360
|
console.warn("Failed to remove from MetricsServerManager:", error);
|
|
@@ -3562,7 +3375,7 @@ async function testMetricsServerConnection(request, reply) {
|
|
|
3562
3375
|
const tenantId = getTenantId7(request);
|
|
3563
3376
|
const { key } = request.params;
|
|
3564
3377
|
try {
|
|
3565
|
-
const storeLattice = (0,
|
|
3378
|
+
const storeLattice = (0, import_core20.getStoreLattice)("default", "metrics");
|
|
3566
3379
|
const store = storeLattice.store;
|
|
3567
3380
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3568
3381
|
if (!config) {
|
|
@@ -3573,11 +3386,11 @@ async function testMetricsServerConnection(request, reply) {
|
|
|
3573
3386
|
};
|
|
3574
3387
|
}
|
|
3575
3388
|
const testKey = `__test_${key}_${Date.now()}`;
|
|
3576
|
-
|
|
3389
|
+
import_core20.metricsServerManager.registerServer(tenantId, testKey, config.config);
|
|
3577
3390
|
try {
|
|
3578
|
-
const client =
|
|
3391
|
+
const client = import_core20.metricsServerManager.getClient(tenantId, testKey);
|
|
3579
3392
|
const result = await client.testConnection();
|
|
3580
|
-
|
|
3393
|
+
import_core20.metricsServerManager.removeServer(tenantId, testKey);
|
|
3581
3394
|
return {
|
|
3582
3395
|
success: true,
|
|
3583
3396
|
message: result.connected ? "Connection test successful" : "Connection test failed",
|
|
@@ -3585,7 +3398,7 @@ async function testMetricsServerConnection(request, reply) {
|
|
|
3585
3398
|
};
|
|
3586
3399
|
} catch (error) {
|
|
3587
3400
|
try {
|
|
3588
|
-
|
|
3401
|
+
import_core20.metricsServerManager.removeServer(tenantId, testKey);
|
|
3589
3402
|
} catch {
|
|
3590
3403
|
}
|
|
3591
3404
|
return {
|
|
@@ -3613,7 +3426,7 @@ async function listAvailableMetrics(request, reply) {
|
|
|
3613
3426
|
const tenantId = getTenantId7(request);
|
|
3614
3427
|
const { key } = request.params;
|
|
3615
3428
|
try {
|
|
3616
|
-
const storeLattice = (0,
|
|
3429
|
+
const storeLattice = (0, import_core20.getStoreLattice)("default", "metrics");
|
|
3617
3430
|
const store = storeLattice.store;
|
|
3618
3431
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3619
3432
|
if (!config) {
|
|
@@ -3623,10 +3436,10 @@ async function listAvailableMetrics(request, reply) {
|
|
|
3623
3436
|
message: "Metrics server configuration not found"
|
|
3624
3437
|
};
|
|
3625
3438
|
}
|
|
3626
|
-
if (!
|
|
3627
|
-
|
|
3439
|
+
if (!import_core20.metricsServerManager.hasServer(tenantId, key)) {
|
|
3440
|
+
import_core20.metricsServerManager.registerServer(tenantId, key, config.config);
|
|
3628
3441
|
}
|
|
3629
|
-
const client =
|
|
3442
|
+
const client = import_core20.metricsServerManager.getClient(tenantId, key);
|
|
3630
3443
|
const metrics = await client.listMetrics();
|
|
3631
3444
|
return {
|
|
3632
3445
|
success: true,
|
|
@@ -3652,7 +3465,7 @@ async function queryMetricsData(request, reply) {
|
|
|
3652
3465
|
const { key } = request.params;
|
|
3653
3466
|
const { metricName, startTime, endTime, step, labels } = request.body;
|
|
3654
3467
|
try {
|
|
3655
|
-
const storeLattice = (0,
|
|
3468
|
+
const storeLattice = (0, import_core20.getStoreLattice)("default", "metrics");
|
|
3656
3469
|
const store = storeLattice.store;
|
|
3657
3470
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3658
3471
|
if (!config) {
|
|
@@ -3669,10 +3482,10 @@ async function queryMetricsData(request, reply) {
|
|
|
3669
3482
|
message: "metricName is required"
|
|
3670
3483
|
};
|
|
3671
3484
|
}
|
|
3672
|
-
if (!
|
|
3673
|
-
|
|
3485
|
+
if (!import_core20.metricsServerManager.hasServer(tenantId, key)) {
|
|
3486
|
+
import_core20.metricsServerManager.registerServer(tenantId, key, config.config);
|
|
3674
3487
|
}
|
|
3675
|
-
const client =
|
|
3488
|
+
const client = import_core20.metricsServerManager.getClient(tenantId, key);
|
|
3676
3489
|
const result = await client.queryMetricData(metricName, {
|
|
3677
3490
|
startTime,
|
|
3678
3491
|
endTime,
|
|
@@ -3699,7 +3512,7 @@ async function getDataSources(request, reply) {
|
|
|
3699
3512
|
const tenantId = getTenantId7(request);
|
|
3700
3513
|
const { key } = request.params;
|
|
3701
3514
|
try {
|
|
3702
|
-
const storeLattice = (0,
|
|
3515
|
+
const storeLattice = (0, import_core20.getStoreLattice)("default", "metrics");
|
|
3703
3516
|
const store = storeLattice.store;
|
|
3704
3517
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3705
3518
|
if (!config) {
|
|
@@ -3717,7 +3530,7 @@ async function getDataSources(request, reply) {
|
|
|
3717
3530
|
};
|
|
3718
3531
|
}
|
|
3719
3532
|
const semanticConfig = config.config;
|
|
3720
|
-
const client = new
|
|
3533
|
+
const client = new import_core20.SemanticMetricsClient(semanticConfig);
|
|
3721
3534
|
const allDatasources = await client.getDataSources();
|
|
3722
3535
|
const selectedIds = semanticConfig.selectedDataSources || [];
|
|
3723
3536
|
const filteredDatasources = selectedIds.length > 0 ? allDatasources.filter((ds) => selectedIds.includes(String(ds.id))) : allDatasources;
|
|
@@ -3740,7 +3553,7 @@ async function getDatasourceMetrics(request, reply) {
|
|
|
3740
3553
|
const tenantId = getTenantId7(request);
|
|
3741
3554
|
const { key, datasourceId } = request.params;
|
|
3742
3555
|
try {
|
|
3743
|
-
const storeLattice = (0,
|
|
3556
|
+
const storeLattice = (0, import_core20.getStoreLattice)("default", "metrics");
|
|
3744
3557
|
const store = storeLattice.store;
|
|
3745
3558
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3746
3559
|
if (!config) {
|
|
@@ -3758,7 +3571,7 @@ async function getDatasourceMetrics(request, reply) {
|
|
|
3758
3571
|
};
|
|
3759
3572
|
}
|
|
3760
3573
|
const semanticConfig = config.config;
|
|
3761
|
-
const client = new
|
|
3574
|
+
const client = new import_core20.SemanticMetricsClient(semanticConfig);
|
|
3762
3575
|
const metrics = await client.getDatasourceMetrics(datasourceId);
|
|
3763
3576
|
return {
|
|
3764
3577
|
success: true,
|
|
@@ -3778,7 +3591,7 @@ async function querySemanticMetrics(request, reply) {
|
|
|
3778
3591
|
const { key } = request.params;
|
|
3779
3592
|
const body = request.body;
|
|
3780
3593
|
try {
|
|
3781
|
-
const storeLattice = (0,
|
|
3594
|
+
const storeLattice = (0, import_core20.getStoreLattice)("default", "metrics");
|
|
3782
3595
|
const store = storeLattice.store;
|
|
3783
3596
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3784
3597
|
if (!config) {
|
|
@@ -3803,7 +3616,7 @@ async function querySemanticMetrics(request, reply) {
|
|
|
3803
3616
|
};
|
|
3804
3617
|
}
|
|
3805
3618
|
const semanticConfig = config.config;
|
|
3806
|
-
const client = new
|
|
3619
|
+
const client = new import_core20.SemanticMetricsClient(semanticConfig);
|
|
3807
3620
|
const result = await client.semanticQuery(body);
|
|
3808
3621
|
const columnNames = result.columns.map((col) => col.name);
|
|
3809
3622
|
const allDataPoints = [];
|
|
@@ -3863,7 +3676,7 @@ async function testSemanticDataSources(request, reply) {
|
|
|
3863
3676
|
password: body.password,
|
|
3864
3677
|
headers: body.headers
|
|
3865
3678
|
};
|
|
3866
|
-
const client = new
|
|
3679
|
+
const client = new import_core20.SemanticMetricsClient(testConfig);
|
|
3867
3680
|
const datasources = await client.getDataSources();
|
|
3868
3681
|
return {
|
|
3869
3682
|
success: true,
|
|
@@ -3899,7 +3712,7 @@ async function testDatasourceMetrics(request, reply) {
|
|
|
3899
3712
|
password: body.password,
|
|
3900
3713
|
headers: body.headers
|
|
3901
3714
|
};
|
|
3902
|
-
const client = new
|
|
3715
|
+
const client = new import_core20.SemanticMetricsClient(testConfig);
|
|
3903
3716
|
const metrics = await client.getDatasourceMetrics(datasourceId);
|
|
3904
3717
|
return {
|
|
3905
3718
|
success: true,
|
|
@@ -3931,7 +3744,7 @@ function registerMetricsServerConfigRoutes(app2) {
|
|
|
3931
3744
|
}
|
|
3932
3745
|
|
|
3933
3746
|
// src/controllers/mcp-configs.ts
|
|
3934
|
-
var
|
|
3747
|
+
var import_core21 = require("@axiom-lattice/core");
|
|
3935
3748
|
var import_crypto5 = require("crypto");
|
|
3936
3749
|
function getTenantId8(request) {
|
|
3937
3750
|
const userTenantId = request.user?.tenantId;
|
|
@@ -3943,7 +3756,7 @@ function getTenantId8(request) {
|
|
|
3943
3756
|
async function getMcpServerConfigList(request, reply) {
|
|
3944
3757
|
const tenantId = getTenantId8(request);
|
|
3945
3758
|
try {
|
|
3946
|
-
const storeLattice = (0,
|
|
3759
|
+
const storeLattice = (0, import_core21.getStoreLattice)("default", "mcp");
|
|
3947
3760
|
const store = storeLattice.store;
|
|
3948
3761
|
const configs = await store.getAllConfigs(tenantId);
|
|
3949
3762
|
return {
|
|
@@ -3970,7 +3783,7 @@ async function getMcpServerConfig(request, reply) {
|
|
|
3970
3783
|
const tenantId = getTenantId8(request);
|
|
3971
3784
|
const { key } = request.params;
|
|
3972
3785
|
try {
|
|
3973
|
-
const storeLattice = (0,
|
|
3786
|
+
const storeLattice = (0, import_core21.getStoreLattice)("default", "mcp");
|
|
3974
3787
|
const store = storeLattice.store;
|
|
3975
3788
|
const config = await store.getConfigByKey(tenantId, key);
|
|
3976
3789
|
if (!config) {
|
|
@@ -3996,7 +3809,7 @@ async function createMcpServerConfig(request, reply) {
|
|
|
3996
3809
|
const tenantId = getTenantId8(request);
|
|
3997
3810
|
const body = request.body;
|
|
3998
3811
|
try {
|
|
3999
|
-
const storeLattice = (0,
|
|
3812
|
+
const storeLattice = (0, import_core21.getStoreLattice)("default", "mcp");
|
|
4000
3813
|
const store = storeLattice.store;
|
|
4001
3814
|
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
4002
3815
|
if (existing) {
|
|
@@ -4036,7 +3849,7 @@ async function updateMcpServerConfig(request, reply) {
|
|
|
4036
3849
|
const { key } = request.params;
|
|
4037
3850
|
const updates = request.body;
|
|
4038
3851
|
try {
|
|
4039
|
-
const storeLattice = (0,
|
|
3852
|
+
const storeLattice = (0, import_core21.getStoreLattice)("default", "mcp");
|
|
4040
3853
|
const store = storeLattice.store;
|
|
4041
3854
|
const existing = await store.getConfigByKey(tenantId, key);
|
|
4042
3855
|
if (!existing) {
|
|
@@ -4056,8 +3869,8 @@ async function updateMcpServerConfig(request, reply) {
|
|
|
4056
3869
|
}
|
|
4057
3870
|
if (shouldReconnect) {
|
|
4058
3871
|
try {
|
|
4059
|
-
if (
|
|
4060
|
-
await
|
|
3872
|
+
if (import_core21.mcpManager.hasServer(key)) {
|
|
3873
|
+
await import_core21.mcpManager.removeServer(key);
|
|
4061
3874
|
}
|
|
4062
3875
|
await connectAndRegisterTools(updated);
|
|
4063
3876
|
await store.updateConfig(tenantId, existing.id, { status: "connected" });
|
|
@@ -4085,7 +3898,7 @@ async function deleteMcpServerConfig(request, reply) {
|
|
|
4085
3898
|
const tenantId = getTenantId8(request);
|
|
4086
3899
|
const { keyOrId } = request.params;
|
|
4087
3900
|
try {
|
|
4088
|
-
const storeLattice = (0,
|
|
3901
|
+
const storeLattice = (0, import_core21.getStoreLattice)("default", "mcp");
|
|
4089
3902
|
const store = storeLattice.store;
|
|
4090
3903
|
let config = await store.getConfigByKey(tenantId, keyOrId);
|
|
4091
3904
|
let configKey = keyOrId;
|
|
@@ -4103,8 +3916,8 @@ async function deleteMcpServerConfig(request, reply) {
|
|
|
4103
3916
|
};
|
|
4104
3917
|
}
|
|
4105
3918
|
try {
|
|
4106
|
-
if (
|
|
4107
|
-
await
|
|
3919
|
+
if (import_core21.mcpManager.hasServer(configKey)) {
|
|
3920
|
+
await import_core21.mcpManager.removeServer(configKey);
|
|
4108
3921
|
}
|
|
4109
3922
|
} catch (error) {
|
|
4110
3923
|
console.warn("Failed to remove from MCP manager:", error);
|
|
@@ -4132,7 +3945,7 @@ async function testMcpServerConnection(request, reply) {
|
|
|
4132
3945
|
const tenantId = getTenantId8(request);
|
|
4133
3946
|
const { key } = request.params;
|
|
4134
3947
|
try {
|
|
4135
|
-
const storeLattice = (0,
|
|
3948
|
+
const storeLattice = (0, import_core21.getStoreLattice)("default", "mcp");
|
|
4136
3949
|
const store = storeLattice.store;
|
|
4137
3950
|
const config = await store.getConfigByKey(tenantId, key);
|
|
4138
3951
|
if (!config) {
|
|
@@ -4146,11 +3959,11 @@ async function testMcpServerConnection(request, reply) {
|
|
|
4146
3959
|
try {
|
|
4147
3960
|
const testKey = `__test_${key}_${Date.now()}`;
|
|
4148
3961
|
const connection = convertToConnection(config.config);
|
|
4149
|
-
|
|
4150
|
-
await
|
|
4151
|
-
const tools = await
|
|
3962
|
+
import_core21.mcpManager.addServer(testKey, connection);
|
|
3963
|
+
await import_core21.mcpManager.connect();
|
|
3964
|
+
const tools = await import_core21.mcpManager.getAllTools();
|
|
4152
3965
|
const latency = Date.now() - startTime;
|
|
4153
|
-
await
|
|
3966
|
+
await import_core21.mcpManager.removeServer(testKey);
|
|
4154
3967
|
return {
|
|
4155
3968
|
success: true,
|
|
4156
3969
|
message: "Connection test successful",
|
|
@@ -4185,7 +3998,7 @@ async function listMcpServerTools(request, reply) {
|
|
|
4185
3998
|
const tenantId = getTenantId8(request);
|
|
4186
3999
|
const { key } = request.params;
|
|
4187
4000
|
try {
|
|
4188
|
-
const storeLattice = (0,
|
|
4001
|
+
const storeLattice = (0, import_core21.getStoreLattice)("default", "mcp");
|
|
4189
4002
|
const store = storeLattice.store;
|
|
4190
4003
|
const config = await store.getConfigByKey(tenantId, key);
|
|
4191
4004
|
if (!config) {
|
|
@@ -4195,10 +4008,10 @@ async function listMcpServerTools(request, reply) {
|
|
|
4195
4008
|
message: "MCP server configuration not found"
|
|
4196
4009
|
};
|
|
4197
4010
|
}
|
|
4198
|
-
if (!
|
|
4011
|
+
if (!import_core21.mcpManager.hasServer(key)) {
|
|
4199
4012
|
await connectAndRegisterTools(config);
|
|
4200
4013
|
}
|
|
4201
|
-
const tools = await
|
|
4014
|
+
const tools = await import_core21.mcpManager.getAllTools();
|
|
4202
4015
|
return {
|
|
4203
4016
|
success: true,
|
|
4204
4017
|
message: "Tools retrieved successfully",
|
|
@@ -4218,7 +4031,7 @@ async function connectMcpServer(request, reply) {
|
|
|
4218
4031
|
const tenantId = getTenantId8(request);
|
|
4219
4032
|
const { key } = request.params;
|
|
4220
4033
|
try {
|
|
4221
|
-
const storeLattice = (0,
|
|
4034
|
+
const storeLattice = (0, import_core21.getStoreLattice)("default", "mcp");
|
|
4222
4035
|
const store = storeLattice.store;
|
|
4223
4036
|
const config = await store.getConfigByKey(tenantId, key);
|
|
4224
4037
|
if (!config) {
|
|
@@ -4239,7 +4052,7 @@ async function connectMcpServer(request, reply) {
|
|
|
4239
4052
|
};
|
|
4240
4053
|
} catch (error) {
|
|
4241
4054
|
console.error("Failed to connect MCP server:", error);
|
|
4242
|
-
const storeLattice = (0,
|
|
4055
|
+
const storeLattice = (0, import_core21.getStoreLattice)("default", "mcp");
|
|
4243
4056
|
const store = storeLattice.store;
|
|
4244
4057
|
const config = await store.getConfigByKey(tenantId, key);
|
|
4245
4058
|
if (config) {
|
|
@@ -4255,7 +4068,7 @@ async function disconnectMcpServer(request, reply) {
|
|
|
4255
4068
|
const tenantId = getTenantId8(request);
|
|
4256
4069
|
const { key } = request.params;
|
|
4257
4070
|
try {
|
|
4258
|
-
const storeLattice = (0,
|
|
4071
|
+
const storeLattice = (0, import_core21.getStoreLattice)("default", "mcp");
|
|
4259
4072
|
const store = storeLattice.store;
|
|
4260
4073
|
const config = await store.getConfigByKey(tenantId, key);
|
|
4261
4074
|
if (!config) {
|
|
@@ -4265,8 +4078,8 @@ async function disconnectMcpServer(request, reply) {
|
|
|
4265
4078
|
message: "MCP server configuration not found"
|
|
4266
4079
|
};
|
|
4267
4080
|
}
|
|
4268
|
-
if (
|
|
4269
|
-
await
|
|
4081
|
+
if (import_core21.mcpManager.hasServer(key)) {
|
|
4082
|
+
await import_core21.mcpManager.removeServer(key);
|
|
4270
4083
|
}
|
|
4271
4084
|
const updated = await store.updateConfig(tenantId, config.id, {
|
|
4272
4085
|
status: "disconnected"
|
|
@@ -4296,10 +4109,10 @@ async function testMcpServerTools(request, reply) {
|
|
|
4296
4109
|
}
|
|
4297
4110
|
const testKey = `__test_${Date.now()}`;
|
|
4298
4111
|
const connection = convertToConnection(body.config);
|
|
4299
|
-
|
|
4300
|
-
await
|
|
4301
|
-
const tools = await
|
|
4302
|
-
await
|
|
4112
|
+
import_core21.mcpManager.addServer(testKey, connection);
|
|
4113
|
+
await import_core21.mcpManager.connect();
|
|
4114
|
+
const tools = await import_core21.mcpManager.getAllTools();
|
|
4115
|
+
await import_core21.mcpManager.removeServer(testKey);
|
|
4303
4116
|
return {
|
|
4304
4117
|
success: true,
|
|
4305
4118
|
message: "Tools retrieved successfully",
|
|
@@ -4336,14 +4149,14 @@ function convertToConnection(config) {
|
|
|
4336
4149
|
}
|
|
4337
4150
|
async function connectAndRegisterTools(config) {
|
|
4338
4151
|
const connection = convertToConnection(config.config);
|
|
4339
|
-
|
|
4340
|
-
await
|
|
4341
|
-
const allTools = await
|
|
4152
|
+
import_core21.mcpManager.addServer(config.key, connection);
|
|
4153
|
+
await import_core21.mcpManager.connect();
|
|
4154
|
+
const allTools = await import_core21.mcpManager.getAllTools();
|
|
4342
4155
|
const selectedTools = allTools.filter(
|
|
4343
4156
|
(tool) => config.selectedTools.includes(tool.name)
|
|
4344
4157
|
);
|
|
4345
4158
|
for (const tool of selectedTools) {
|
|
4346
|
-
|
|
4159
|
+
import_core21.toolLatticeManager.registerExistingTool(tool.name, tool);
|
|
4347
4160
|
}
|
|
4348
4161
|
}
|
|
4349
4162
|
function registerMcpServerConfigRoutes(app2) {
|
|
@@ -4360,11 +4173,11 @@ function registerMcpServerConfigRoutes(app2) {
|
|
|
4360
4173
|
}
|
|
4361
4174
|
|
|
4362
4175
|
// src/controllers/users.ts
|
|
4363
|
-
var
|
|
4176
|
+
var import_core22 = require("@axiom-lattice/core");
|
|
4364
4177
|
var import_uuid3 = require("uuid");
|
|
4365
4178
|
var UsersController = class {
|
|
4366
4179
|
constructor() {
|
|
4367
|
-
this.userStore = (0,
|
|
4180
|
+
this.userStore = (0, import_core22.getStoreLattice)("default", "user").store;
|
|
4368
4181
|
}
|
|
4369
4182
|
async listUsers(request, reply) {
|
|
4370
4183
|
const { email } = request.query;
|
|
@@ -4442,11 +4255,11 @@ function registerUserRoutes(app2) {
|
|
|
4442
4255
|
}
|
|
4443
4256
|
|
|
4444
4257
|
// src/controllers/tenants.ts
|
|
4445
|
-
var
|
|
4258
|
+
var import_core23 = require("@axiom-lattice/core");
|
|
4446
4259
|
var import_uuid4 = require("uuid");
|
|
4447
4260
|
var TenantsController = class {
|
|
4448
4261
|
constructor() {
|
|
4449
|
-
this.tenantStore = (0,
|
|
4262
|
+
this.tenantStore = (0, import_core23.getStoreLattice)("default", "tenant").store;
|
|
4450
4263
|
}
|
|
4451
4264
|
// ==================== Tenant CRUD ====================
|
|
4452
4265
|
async listTenants(request, reply) {
|
|
@@ -4510,7 +4323,7 @@ function registerTenantRoutes(app2) {
|
|
|
4510
4323
|
}
|
|
4511
4324
|
|
|
4512
4325
|
// src/controllers/auth.ts
|
|
4513
|
-
var
|
|
4326
|
+
var import_core24 = require("@axiom-lattice/core");
|
|
4514
4327
|
var import_uuid5 = require("uuid");
|
|
4515
4328
|
var defaultAuthConfig = {
|
|
4516
4329
|
autoApproveUsers: true,
|
|
@@ -4519,9 +4332,9 @@ var defaultAuthConfig = {
|
|
|
4519
4332
|
};
|
|
4520
4333
|
var AuthController = class {
|
|
4521
4334
|
constructor(config = {}) {
|
|
4522
|
-
this.userStore = (0,
|
|
4523
|
-
this.tenantStore = (0,
|
|
4524
|
-
this.userTenantLinkStore = (0,
|
|
4335
|
+
this.userStore = (0, import_core24.getStoreLattice)("default", "user").store;
|
|
4336
|
+
this.tenantStore = (0, import_core24.getStoreLattice)("default", "tenant").store;
|
|
4337
|
+
this.userTenantLinkStore = (0, import_core24.getStoreLattice)("default", "userTenantLink").store;
|
|
4525
4338
|
this.config = { ...defaultAuthConfig, ...config };
|
|
4526
4339
|
}
|
|
4527
4340
|
async register(request, reply) {
|
|
@@ -4892,6 +4705,695 @@ function registerAuthRoutes(app2, config) {
|
|
|
4892
4705
|
);
|
|
4893
4706
|
}
|
|
4894
4707
|
|
|
4708
|
+
// src/channels/lark/routes.ts
|
|
4709
|
+
var import_core26 = require("@axiom-lattice/core");
|
|
4710
|
+
var import_pg_stores = require("@axiom-lattice/pg-stores");
|
|
4711
|
+
|
|
4712
|
+
// src/channels/lark/parser.ts
|
|
4713
|
+
function parseLarkMessageEvent(payload) {
|
|
4714
|
+
const raw = payload;
|
|
4715
|
+
if (raw.header?.event_type !== "im.message.receive_v1") {
|
|
4716
|
+
return null;
|
|
4717
|
+
}
|
|
4718
|
+
const message = raw.event?.message;
|
|
4719
|
+
const openId = raw.event?.sender?.sender_id?.open_id;
|
|
4720
|
+
if (!message || !openId || message.message_type !== "text") {
|
|
4721
|
+
return null;
|
|
4722
|
+
}
|
|
4723
|
+
const parsedContent = parseLarkTextContent(message.content);
|
|
4724
|
+
if (!message.message_id || !message.chat_id || !parsedContent) {
|
|
4725
|
+
return null;
|
|
4726
|
+
}
|
|
4727
|
+
return {
|
|
4728
|
+
messageId: message.message_id,
|
|
4729
|
+
openId,
|
|
4730
|
+
chatId: message.chat_id,
|
|
4731
|
+
chatType: normalizeChatType(message.chat_type),
|
|
4732
|
+
text: parsedContent
|
|
4733
|
+
};
|
|
4734
|
+
}
|
|
4735
|
+
function parseLarkTextContent(content) {
|
|
4736
|
+
if (!content) {
|
|
4737
|
+
return null;
|
|
4738
|
+
}
|
|
4739
|
+
try {
|
|
4740
|
+
const parsed = JSON.parse(content);
|
|
4741
|
+
return typeof parsed.text === "string" ? parsed.text : null;
|
|
4742
|
+
} catch {
|
|
4743
|
+
return null;
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
function normalizeChatType(chatType) {
|
|
4747
|
+
return chatType === "p2p" ? "direct" : "group";
|
|
4748
|
+
}
|
|
4749
|
+
|
|
4750
|
+
// src/channels/lark/controller.ts
|
|
4751
|
+
function createLarkEventHandler(dependencies) {
|
|
4752
|
+
return async function handleLarkEvent2(request, reply) {
|
|
4753
|
+
const installationId = request.params?.installationId;
|
|
4754
|
+
if (!installationId) {
|
|
4755
|
+
reply.status(400).send({ success: false, message: "Missing installationId" });
|
|
4756
|
+
return;
|
|
4757
|
+
}
|
|
4758
|
+
const config = await dependencies.getInstallationConfig(installationId);
|
|
4759
|
+
if (!config) {
|
|
4760
|
+
reply.status(404).send({ success: false, message: "Lark installation not found" });
|
|
4761
|
+
return;
|
|
4762
|
+
}
|
|
4763
|
+
const body = dependencies.parseRequestBody(
|
|
4764
|
+
request.body,
|
|
4765
|
+
config.encryptKey
|
|
4766
|
+
);
|
|
4767
|
+
if (!dependencies.verifyParsedBody(body, config)) {
|
|
4768
|
+
reply.status(401).send({ success: false, message: "Invalid Lark request" });
|
|
4769
|
+
return;
|
|
4770
|
+
}
|
|
4771
|
+
if (body.type === "url_verification" && body.challenge) {
|
|
4772
|
+
reply.status(200).send({ challenge: body.challenge });
|
|
4773
|
+
return;
|
|
4774
|
+
}
|
|
4775
|
+
const parsed = dependencies.parseEvent(request.body);
|
|
4776
|
+
if (!parsed) {
|
|
4777
|
+
reply.status(200).send({ success: true, ignored: true });
|
|
4778
|
+
return;
|
|
4779
|
+
}
|
|
4780
|
+
const receipt = await dependencies.claimInboundReceipt({
|
|
4781
|
+
channel: "lark",
|
|
4782
|
+
channelAppId: config.appId,
|
|
4783
|
+
externalMessageId: parsed.messageId,
|
|
4784
|
+
tenantId: config.tenantId
|
|
4785
|
+
});
|
|
4786
|
+
if (!receipt.accepted) {
|
|
4787
|
+
reply.status(200).send(
|
|
4788
|
+
receipt.status === "processing" ? { success: true, processing: true } : { success: true, duplicate: true }
|
|
4789
|
+
);
|
|
4790
|
+
return;
|
|
4791
|
+
}
|
|
4792
|
+
try {
|
|
4793
|
+
const { threadId } = await dependencies.resolveThread({
|
|
4794
|
+
channel: "lark",
|
|
4795
|
+
channelAppId: config.appId,
|
|
4796
|
+
tenantId: config.tenantId,
|
|
4797
|
+
assistantId: config.assistantId,
|
|
4798
|
+
mappingMode: config.mappingMode,
|
|
4799
|
+
openId: parsed.openId,
|
|
4800
|
+
chatId: parsed.chatId,
|
|
4801
|
+
chatType: parsed.chatType,
|
|
4802
|
+
messageId: parsed.messageId,
|
|
4803
|
+
workspaceId: config.workspaceId,
|
|
4804
|
+
projectId: config.projectId
|
|
4805
|
+
});
|
|
4806
|
+
const text = await dependencies.runAgentAndCollectText({
|
|
4807
|
+
tenantId: config.tenantId,
|
|
4808
|
+
assistantId: config.assistantId,
|
|
4809
|
+
threadId,
|
|
4810
|
+
text: parsed.text,
|
|
4811
|
+
workspaceId: config.workspaceId,
|
|
4812
|
+
projectId: config.projectId
|
|
4813
|
+
});
|
|
4814
|
+
await dependencies.sendTextReply({
|
|
4815
|
+
chatId: parsed.chatId,
|
|
4816
|
+
text,
|
|
4817
|
+
config
|
|
4818
|
+
});
|
|
4819
|
+
await dependencies.markInboundReceiptCompleted({
|
|
4820
|
+
channel: "lark",
|
|
4821
|
+
channelAppId: config.appId,
|
|
4822
|
+
externalMessageId: parsed.messageId,
|
|
4823
|
+
tenantId: config.tenantId,
|
|
4824
|
+
threadId
|
|
4825
|
+
});
|
|
4826
|
+
reply.status(200).send({ success: true, threadId });
|
|
4827
|
+
} catch (error) {
|
|
4828
|
+
await dependencies.markInboundReceiptFailed({
|
|
4829
|
+
channel: "lark",
|
|
4830
|
+
channelAppId: config.appId,
|
|
4831
|
+
externalMessageId: parsed.messageId,
|
|
4832
|
+
tenantId: config.tenantId
|
|
4833
|
+
});
|
|
4834
|
+
throw error;
|
|
4835
|
+
}
|
|
4836
|
+
};
|
|
4837
|
+
}
|
|
4838
|
+
var handleLarkEvent = createLarkEventHandler({
|
|
4839
|
+
getInstallationConfig: async () => null,
|
|
4840
|
+
parseRequestBody: (body) => body || {},
|
|
4841
|
+
verifyParsedBody: () => true,
|
|
4842
|
+
parseEvent: parseLarkMessageEvent,
|
|
4843
|
+
claimInboundReceipt: async () => ({ accepted: true, status: "processing" }),
|
|
4844
|
+
markInboundReceiptCompleted: async () => void 0,
|
|
4845
|
+
markInboundReceiptFailed: async () => void 0,
|
|
4846
|
+
resolveThread: async () => ({ threadId: "" }),
|
|
4847
|
+
runAgentAndCollectText: async () => "",
|
|
4848
|
+
sendTextReply: async () => void 0
|
|
4849
|
+
});
|
|
4850
|
+
|
|
4851
|
+
// src/channels/lark/config.ts
|
|
4852
|
+
function loadLarkIngressConfig() {
|
|
4853
|
+
return {
|
|
4854
|
+
enabled: process.env.LARK_ENABLED !== "false",
|
|
4855
|
+
appId: process.env.LARK_APP_ID || "",
|
|
4856
|
+
appSecret: process.env.LARK_APP_SECRET || "",
|
|
4857
|
+
verificationToken: process.env.LARK_VERIFICATION_TOKEN,
|
|
4858
|
+
encryptKey: process.env.LARK_ENCRYPT_KEY,
|
|
4859
|
+
tenantId: process.env.LARK_TENANT_ID || "default",
|
|
4860
|
+
assistantId: process.env.LARK_ASSISTANT_ID || "default_agent",
|
|
4861
|
+
workspaceId: process.env.LARK_WORKSPACE_ID,
|
|
4862
|
+
projectId: process.env.LARK_PROJECT_ID,
|
|
4863
|
+
mappingMode: process.env.LARK_MAPPING_MODE || "hybrid"
|
|
4864
|
+
};
|
|
4865
|
+
}
|
|
4866
|
+
function isLarkIngressEnabled(config) {
|
|
4867
|
+
return config.enabled;
|
|
4868
|
+
}
|
|
4869
|
+
|
|
4870
|
+
// src/channels/lark/mapping-service.ts
|
|
4871
|
+
var import_crypto6 = require("crypto");
|
|
4872
|
+
function createChannelThreadMappingService(deps) {
|
|
4873
|
+
return {
|
|
4874
|
+
async getOrCreateThread(input) {
|
|
4875
|
+
const externalSubjectKey = buildExternalSubjectKey(input);
|
|
4876
|
+
const existing = await deps.mappingStore.getMappingBySubject({
|
|
4877
|
+
channel: input.channel,
|
|
4878
|
+
channelAppId: input.channelAppId,
|
|
4879
|
+
tenantId: input.tenantId,
|
|
4880
|
+
assistantId: input.assistantId,
|
|
4881
|
+
externalSubjectKey
|
|
4882
|
+
});
|
|
4883
|
+
if (existing) {
|
|
4884
|
+
return { threadId: existing.threadId };
|
|
4885
|
+
}
|
|
4886
|
+
const threadId = (deps.uuid || import_crypto6.randomUUID)();
|
|
4887
|
+
await deps.threadStore.createThread(input.tenantId, input.assistantId, threadId, {
|
|
4888
|
+
metadata: {
|
|
4889
|
+
tenantId: input.tenantId,
|
|
4890
|
+
workspaceId: input.workspaceId,
|
|
4891
|
+
projectId: input.projectId,
|
|
4892
|
+
channel: input.channel,
|
|
4893
|
+
larkChatId: input.chatId,
|
|
4894
|
+
larkOpenId: input.openId
|
|
4895
|
+
}
|
|
4896
|
+
});
|
|
4897
|
+
try {
|
|
4898
|
+
await deps.mappingStore.createMapping({
|
|
4899
|
+
channel: input.channel,
|
|
4900
|
+
channelAppId: input.channelAppId,
|
|
4901
|
+
tenantId: input.tenantId,
|
|
4902
|
+
assistantId: input.assistantId,
|
|
4903
|
+
mappingMode: input.mappingMode,
|
|
4904
|
+
externalSubjectType: resolveSubjectType(input.mappingMode, input.chatType) === "user" ? "user" : "chat",
|
|
4905
|
+
externalSubjectKey,
|
|
4906
|
+
larkOpenId: input.openId,
|
|
4907
|
+
larkChatId: input.chatId,
|
|
4908
|
+
larkMessageId: input.messageId,
|
|
4909
|
+
threadId
|
|
4910
|
+
});
|
|
4911
|
+
} catch (error) {
|
|
4912
|
+
if (!isUniqueViolation(error)) {
|
|
4913
|
+
throw error;
|
|
4914
|
+
}
|
|
4915
|
+
const canonical = await deps.mappingStore.getMappingBySubject({
|
|
4916
|
+
channel: input.channel,
|
|
4917
|
+
channelAppId: input.channelAppId,
|
|
4918
|
+
tenantId: input.tenantId,
|
|
4919
|
+
assistantId: input.assistantId,
|
|
4920
|
+
externalSubjectKey
|
|
4921
|
+
});
|
|
4922
|
+
if (!canonical) {
|
|
4923
|
+
throw error;
|
|
4924
|
+
}
|
|
4925
|
+
await deps.threadStore.deleteThread(input.tenantId, threadId);
|
|
4926
|
+
return { threadId: canonical.threadId };
|
|
4927
|
+
}
|
|
4928
|
+
return { threadId };
|
|
4929
|
+
}
|
|
4930
|
+
};
|
|
4931
|
+
}
|
|
4932
|
+
function isUniqueViolation(error) {
|
|
4933
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
|
|
4934
|
+
}
|
|
4935
|
+
function buildExternalSubjectKey(input) {
|
|
4936
|
+
const subjectType = resolveSubjectType(input.mappingMode, input.chatType);
|
|
4937
|
+
const subjectValue = subjectType === "user" ? input.openId : input.chatId;
|
|
4938
|
+
return [
|
|
4939
|
+
input.channel,
|
|
4940
|
+
input.channelAppId,
|
|
4941
|
+
`tenant:${input.tenantId}`,
|
|
4942
|
+
`assistant:${input.assistantId}`,
|
|
4943
|
+
`${subjectType}:${subjectValue}`
|
|
4944
|
+
].join(":");
|
|
4945
|
+
}
|
|
4946
|
+
function resolveSubjectType(mappingMode, chatType) {
|
|
4947
|
+
if (mappingMode === "user") {
|
|
4948
|
+
return "user";
|
|
4949
|
+
}
|
|
4950
|
+
if (mappingMode === "group") {
|
|
4951
|
+
return "chat";
|
|
4952
|
+
}
|
|
4953
|
+
return chatType === "direct" ? "user" : "chat";
|
|
4954
|
+
}
|
|
4955
|
+
|
|
4956
|
+
// src/channels/lark/runner.ts
|
|
4957
|
+
var import_core25 = require("@axiom-lattice/core");
|
|
4958
|
+
var import_protocols4 = require("@axiom-lattice/protocols");
|
|
4959
|
+
|
|
4960
|
+
// src/channels/lark/aggregator.ts
|
|
4961
|
+
var import_protocols3 = require("@axiom-lattice/protocols");
|
|
4962
|
+
function aggregateLarkReply(messageId, chunks) {
|
|
4963
|
+
return chunks.filter(
|
|
4964
|
+
(chunk) => chunk.type === import_protocols3.MessageChunkTypes.AI && chunk.data.id === messageId
|
|
4965
|
+
).map((chunk) => chunk.data.content || "").join("").trim();
|
|
4966
|
+
}
|
|
4967
|
+
|
|
4968
|
+
// src/channels/lark/runner.ts
|
|
4969
|
+
async function runAgentAndCollectLarkReply(input) {
|
|
4970
|
+
const agent = import_core25.agentInstanceManager.getAgent({
|
|
4971
|
+
tenant_id: input.tenantId,
|
|
4972
|
+
assistant_id: input.assistantId,
|
|
4973
|
+
thread_id: input.threadId,
|
|
4974
|
+
workspace_id: input.workspaceId,
|
|
4975
|
+
project_id: input.projectId
|
|
4976
|
+
});
|
|
4977
|
+
const result = await agent.addMessage({
|
|
4978
|
+
input: {
|
|
4979
|
+
message: input.text
|
|
4980
|
+
}
|
|
4981
|
+
});
|
|
4982
|
+
const chunks = [];
|
|
4983
|
+
const stream = agent.chunkStream(result.messageId, [
|
|
4984
|
+
import_protocols4.MessageChunkTypes.MESSAGE_COMPLETED
|
|
4985
|
+
]);
|
|
4986
|
+
for await (const chunk of stream) {
|
|
4987
|
+
chunks.push(chunk);
|
|
4988
|
+
}
|
|
4989
|
+
return aggregateLarkReply(result.messageId, chunks);
|
|
4990
|
+
}
|
|
4991
|
+
|
|
4992
|
+
// src/channels/lark/sender.ts
|
|
4993
|
+
function createLarkSender(config, client = createDefaultLarkClient(config)) {
|
|
4994
|
+
return {
|
|
4995
|
+
async sendTextReply(input) {
|
|
4996
|
+
const response = await client.im.v1.message.create({
|
|
4997
|
+
params: {
|
|
4998
|
+
receive_id_type: "chat_id"
|
|
4999
|
+
},
|
|
5000
|
+
data: {
|
|
5001
|
+
receive_id: input.chatId,
|
|
5002
|
+
msg_type: "text",
|
|
5003
|
+
content: JSON.stringify({ text: input.text })
|
|
5004
|
+
}
|
|
5005
|
+
});
|
|
5006
|
+
if (response.code && response.code !== 0) {
|
|
5007
|
+
throw new Error("Failed to send Lark reply");
|
|
5008
|
+
}
|
|
5009
|
+
}
|
|
5010
|
+
};
|
|
5011
|
+
}
|
|
5012
|
+
function createDefaultLarkClient(config) {
|
|
5013
|
+
const Lark = require("@larksuiteoapi/node-sdk");
|
|
5014
|
+
return new Lark.Client({
|
|
5015
|
+
appId: config.appId,
|
|
5016
|
+
appSecret: config.appSecret
|
|
5017
|
+
});
|
|
5018
|
+
}
|
|
5019
|
+
|
|
5020
|
+
// src/channels/lark/verification.ts
|
|
5021
|
+
var import_crypto7 = __toESM(require("crypto"));
|
|
5022
|
+
function parseLarkRequestBody(body, encryptKey) {
|
|
5023
|
+
const parsed = body || {};
|
|
5024
|
+
if (encryptKey && typeof parsed.encrypt === "string") {
|
|
5025
|
+
return decryptLarkPayload(encryptKey, parsed.encrypt);
|
|
5026
|
+
}
|
|
5027
|
+
return parsed;
|
|
5028
|
+
}
|
|
5029
|
+
function decryptLarkPayload(encryptKey, encryptedPayload) {
|
|
5030
|
+
const key = import_crypto7.default.createHash("sha256").update(encryptKey).digest();
|
|
5031
|
+
const buffer = Buffer.from(encryptedPayload, "base64");
|
|
5032
|
+
const iv = buffer.subarray(0, 16);
|
|
5033
|
+
const ciphertext = buffer.subarray(16);
|
|
5034
|
+
const decipher = import_crypto7.default.createDecipheriv("aes-256-cbc", key, iv);
|
|
5035
|
+
const plaintext = Buffer.concat([
|
|
5036
|
+
decipher.update(ciphertext),
|
|
5037
|
+
decipher.final()
|
|
5038
|
+
]).toString("utf8");
|
|
5039
|
+
return JSON.parse(plaintext);
|
|
5040
|
+
}
|
|
5041
|
+
function createLarkRequestVerifier(config) {
|
|
5042
|
+
return function verifyRequest(request) {
|
|
5043
|
+
const body = parseLarkRequestBody(request.body, config.encryptKey);
|
|
5044
|
+
return verifyLarkParsedBody(body, config);
|
|
5045
|
+
};
|
|
5046
|
+
}
|
|
5047
|
+
function verifyLarkParsedBody(body, config) {
|
|
5048
|
+
if (!config.verificationToken) {
|
|
5049
|
+
return true;
|
|
5050
|
+
}
|
|
5051
|
+
return extractVerificationToken(body) === config.verificationToken;
|
|
5052
|
+
}
|
|
5053
|
+
function extractVerificationToken(body) {
|
|
5054
|
+
if (typeof body.token === "string") {
|
|
5055
|
+
return body.token;
|
|
5056
|
+
}
|
|
5057
|
+
if (typeof body.header?.token === "string") {
|
|
5058
|
+
return body.header.token;
|
|
5059
|
+
}
|
|
5060
|
+
return void 0;
|
|
5061
|
+
}
|
|
5062
|
+
|
|
5063
|
+
// src/channels/lark/routes.ts
|
|
5064
|
+
function registerLarkChannelRoutes(app2, dependencies) {
|
|
5065
|
+
const config = loadLarkIngressConfig();
|
|
5066
|
+
if (!dependencies && !isLarkIngressEnabled(config)) {
|
|
5067
|
+
return;
|
|
5068
|
+
}
|
|
5069
|
+
const handlerDependencies = dependencies || createDefaultLarkDependencies();
|
|
5070
|
+
app2.post(
|
|
5071
|
+
"/api/channels/lark/installations/:installationId/events",
|
|
5072
|
+
createLarkEventHandler({
|
|
5073
|
+
...handlerDependencies
|
|
5074
|
+
})
|
|
5075
|
+
);
|
|
5076
|
+
}
|
|
5077
|
+
function createDefaultLarkDependencies() {
|
|
5078
|
+
const installationStore = new import_pg_stores.PostgreSQLChannelInstallationStore({
|
|
5079
|
+
poolConfig: getDatabaseUrl()
|
|
5080
|
+
});
|
|
5081
|
+
const threadStore = (0, import_core26.getStoreLattice)("default", "thread").store;
|
|
5082
|
+
const mappingStore = new import_pg_stores.ChannelIdentityMappingStore({
|
|
5083
|
+
poolConfig: getDatabaseUrl()
|
|
5084
|
+
});
|
|
5085
|
+
const mappingService = createChannelThreadMappingService({
|
|
5086
|
+
mappingStore,
|
|
5087
|
+
threadStore
|
|
5088
|
+
});
|
|
5089
|
+
return {
|
|
5090
|
+
getInstallationConfig: async (installationId) => {
|
|
5091
|
+
const installation = await installationStore.getInstallationById(
|
|
5092
|
+
installationId
|
|
5093
|
+
);
|
|
5094
|
+
if (!installation || installation.channel !== "lark") {
|
|
5095
|
+
return null;
|
|
5096
|
+
}
|
|
5097
|
+
return {
|
|
5098
|
+
enabled: true,
|
|
5099
|
+
installationId: installation.id,
|
|
5100
|
+
tenantId: installation.tenantId,
|
|
5101
|
+
assistantId: installation.config.assistantId,
|
|
5102
|
+
appId: installation.config.appId,
|
|
5103
|
+
appSecret: installation.config.appSecret,
|
|
5104
|
+
verificationToken: installation.config.verificationToken,
|
|
5105
|
+
encryptKey: installation.config.encryptKey,
|
|
5106
|
+
workspaceId: installation.config.workspaceId,
|
|
5107
|
+
projectId: installation.config.projectId,
|
|
5108
|
+
mappingMode: installation.config.mappingMode
|
|
5109
|
+
};
|
|
5110
|
+
},
|
|
5111
|
+
parseRequestBody: (body, encryptKey) => parseLarkRequestBody(body, encryptKey),
|
|
5112
|
+
verifyParsedBody: (body, config) => {
|
|
5113
|
+
if (!config.verificationToken) {
|
|
5114
|
+
return true;
|
|
5115
|
+
}
|
|
5116
|
+
return createLarkRequestVerifier(config)({
|
|
5117
|
+
body
|
|
5118
|
+
});
|
|
5119
|
+
},
|
|
5120
|
+
parseEvent: parseLarkMessageEvent,
|
|
5121
|
+
claimInboundReceipt: (input) => mappingStore.claimInboundReceipt(input),
|
|
5122
|
+
markInboundReceiptCompleted: (input) => mappingStore.markInboundReceiptCompleted(input),
|
|
5123
|
+
markInboundReceiptFailed: (input) => mappingStore.markInboundReceiptFailed(input),
|
|
5124
|
+
resolveThread: (input) => mappingService.getOrCreateThread(input),
|
|
5125
|
+
runAgentAndCollectText: ({ tenantId, assistantId, threadId, text, workspaceId, projectId }) => runAgentAndCollectLarkReply({
|
|
5126
|
+
tenantId,
|
|
5127
|
+
assistantId,
|
|
5128
|
+
threadId,
|
|
5129
|
+
text,
|
|
5130
|
+
workspaceId,
|
|
5131
|
+
projectId
|
|
5132
|
+
}),
|
|
5133
|
+
sendTextReply: async ({ chatId, text, config }) => {
|
|
5134
|
+
const sender = createLarkSender({
|
|
5135
|
+
appId: config.appId,
|
|
5136
|
+
appSecret: config.appSecret
|
|
5137
|
+
});
|
|
5138
|
+
await sender.sendTextReply({ chatId, text });
|
|
5139
|
+
}
|
|
5140
|
+
};
|
|
5141
|
+
}
|
|
5142
|
+
function getDatabaseUrl() {
|
|
5143
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
5144
|
+
if (!databaseUrl) {
|
|
5145
|
+
throw new Error("DATABASE_URL is required for Lark channel ingress");
|
|
5146
|
+
}
|
|
5147
|
+
return databaseUrl;
|
|
5148
|
+
}
|
|
5149
|
+
|
|
5150
|
+
// src/channels/routes.ts
|
|
5151
|
+
var channelRouteRegistrars = [
|
|
5152
|
+
(app2, dependencies) => registerLarkChannelRoutes(app2, dependencies.lark)
|
|
5153
|
+
];
|
|
5154
|
+
function registerChannelRoutes(app2, dependencies = {}) {
|
|
5155
|
+
for (const registerRoutes of channelRouteRegistrars) {
|
|
5156
|
+
registerRoutes(app2, dependencies);
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5159
|
+
|
|
5160
|
+
// src/controllers/channel-installations.ts
|
|
5161
|
+
var import_crypto8 = require("crypto");
|
|
5162
|
+
function getTenantId9(request) {
|
|
5163
|
+
const userTenantId = request.user?.tenantId;
|
|
5164
|
+
if (userTenantId) {
|
|
5165
|
+
return userTenantId;
|
|
5166
|
+
}
|
|
5167
|
+
return request.headers["x-tenant-id"] || "default";
|
|
5168
|
+
}
|
|
5169
|
+
function getInstallationStore() {
|
|
5170
|
+
const { PostgreSQLChannelInstallationStore: PostgreSQLChannelInstallationStore2 } = require("@axiom-lattice/pg-stores");
|
|
5171
|
+
const databaseUrl = process.env.DATABASE_URL;
|
|
5172
|
+
if (!databaseUrl) {
|
|
5173
|
+
throw new Error("DATABASE_URL is required for channel installation store");
|
|
5174
|
+
}
|
|
5175
|
+
return new PostgreSQLChannelInstallationStore2({
|
|
5176
|
+
poolConfig: databaseUrl
|
|
5177
|
+
});
|
|
5178
|
+
}
|
|
5179
|
+
async function getChannelInstallationList(request, reply) {
|
|
5180
|
+
const tenantId = getTenantId9(request);
|
|
5181
|
+
const { channel } = request.query;
|
|
5182
|
+
try {
|
|
5183
|
+
const store = getInstallationStore();
|
|
5184
|
+
const installations = await store.getInstallationsByTenant(tenantId, channel);
|
|
5185
|
+
return {
|
|
5186
|
+
success: true,
|
|
5187
|
+
message: "Channel installations retrieved successfully",
|
|
5188
|
+
data: {
|
|
5189
|
+
records: installations,
|
|
5190
|
+
total: installations.length
|
|
5191
|
+
}
|
|
5192
|
+
};
|
|
5193
|
+
} catch (error) {
|
|
5194
|
+
console.error("Failed to get channel installations:", error);
|
|
5195
|
+
return {
|
|
5196
|
+
success: false,
|
|
5197
|
+
message: "Failed to retrieve channel installations",
|
|
5198
|
+
data: {
|
|
5199
|
+
records: [],
|
|
5200
|
+
total: 0
|
|
5201
|
+
}
|
|
5202
|
+
};
|
|
5203
|
+
}
|
|
5204
|
+
}
|
|
5205
|
+
async function getChannelInstallation(request, reply) {
|
|
5206
|
+
const tenantId = getTenantId9(request);
|
|
5207
|
+
const { installationId } = request.params;
|
|
5208
|
+
try {
|
|
5209
|
+
const store = getInstallationStore();
|
|
5210
|
+
const installation = await store.getInstallationById(installationId);
|
|
5211
|
+
if (!installation) {
|
|
5212
|
+
reply.code(404);
|
|
5213
|
+
return {
|
|
5214
|
+
success: false,
|
|
5215
|
+
message: "Channel installation not found"
|
|
5216
|
+
};
|
|
5217
|
+
}
|
|
5218
|
+
if (installation.tenantId !== tenantId) {
|
|
5219
|
+
reply.code(403);
|
|
5220
|
+
return {
|
|
5221
|
+
success: false,
|
|
5222
|
+
message: "Access denied"
|
|
5223
|
+
};
|
|
5224
|
+
}
|
|
5225
|
+
return {
|
|
5226
|
+
success: true,
|
|
5227
|
+
message: "Channel installation retrieved successfully",
|
|
5228
|
+
data: installation
|
|
5229
|
+
};
|
|
5230
|
+
} catch (error) {
|
|
5231
|
+
console.error("Failed to get channel installation:", error);
|
|
5232
|
+
return {
|
|
5233
|
+
success: false,
|
|
5234
|
+
message: "Failed to retrieve channel installation"
|
|
5235
|
+
};
|
|
5236
|
+
}
|
|
5237
|
+
}
|
|
5238
|
+
async function createChannelInstallation(request, reply) {
|
|
5239
|
+
const tenantId = getTenantId9(request);
|
|
5240
|
+
const body = request.body;
|
|
5241
|
+
try {
|
|
5242
|
+
if (!body.channel) {
|
|
5243
|
+
reply.code(400);
|
|
5244
|
+
return {
|
|
5245
|
+
success: false,
|
|
5246
|
+
message: "Channel type is required"
|
|
5247
|
+
};
|
|
5248
|
+
}
|
|
5249
|
+
if (!body.config) {
|
|
5250
|
+
reply.code(400);
|
|
5251
|
+
return {
|
|
5252
|
+
success: false,
|
|
5253
|
+
message: "Configuration is required"
|
|
5254
|
+
};
|
|
5255
|
+
}
|
|
5256
|
+
if (body.channel === "lark") {
|
|
5257
|
+
const larkConfig = body.config;
|
|
5258
|
+
if (!larkConfig.appId || !larkConfig.appSecret) {
|
|
5259
|
+
reply.code(400);
|
|
5260
|
+
return {
|
|
5261
|
+
success: false,
|
|
5262
|
+
message: "appId and appSecret are required for Lark installations"
|
|
5263
|
+
};
|
|
5264
|
+
}
|
|
5265
|
+
if (!larkConfig.assistantId) {
|
|
5266
|
+
reply.code(400);
|
|
5267
|
+
return {
|
|
5268
|
+
success: false,
|
|
5269
|
+
message: "assistantId is required for Lark installations"
|
|
5270
|
+
};
|
|
5271
|
+
}
|
|
5272
|
+
}
|
|
5273
|
+
const store = getInstallationStore();
|
|
5274
|
+
const installationId = body.id || (0, import_crypto8.randomUUID)();
|
|
5275
|
+
const installation = await store.createInstallation(
|
|
5276
|
+
tenantId,
|
|
5277
|
+
installationId,
|
|
5278
|
+
body
|
|
5279
|
+
);
|
|
5280
|
+
reply.code(201);
|
|
5281
|
+
return {
|
|
5282
|
+
success: true,
|
|
5283
|
+
message: "Channel installation created successfully",
|
|
5284
|
+
data: installation
|
|
5285
|
+
};
|
|
5286
|
+
} catch (error) {
|
|
5287
|
+
console.error("Failed to create channel installation:", error);
|
|
5288
|
+
if (error.message?.includes("duplicate") || error.code === "23505") {
|
|
5289
|
+
reply.code(409);
|
|
5290
|
+
return {
|
|
5291
|
+
success: false,
|
|
5292
|
+
message: "Channel installation with this ID already exists"
|
|
5293
|
+
};
|
|
5294
|
+
}
|
|
5295
|
+
return {
|
|
5296
|
+
success: false,
|
|
5297
|
+
message: "Failed to create channel installation"
|
|
5298
|
+
};
|
|
5299
|
+
}
|
|
5300
|
+
}
|
|
5301
|
+
async function updateChannelInstallation(request, reply) {
|
|
5302
|
+
const tenantId = getTenantId9(request);
|
|
5303
|
+
const { installationId } = request.params;
|
|
5304
|
+
const body = request.body;
|
|
5305
|
+
try {
|
|
5306
|
+
const store = getInstallationStore();
|
|
5307
|
+
const existing = await store.getInstallationById(installationId);
|
|
5308
|
+
if (!existing) {
|
|
5309
|
+
reply.code(404);
|
|
5310
|
+
return {
|
|
5311
|
+
success: false,
|
|
5312
|
+
message: "Channel installation not found"
|
|
5313
|
+
};
|
|
5314
|
+
}
|
|
5315
|
+
if (existing.tenantId !== tenantId) {
|
|
5316
|
+
reply.code(403);
|
|
5317
|
+
return {
|
|
5318
|
+
success: false,
|
|
5319
|
+
message: "Access denied"
|
|
5320
|
+
};
|
|
5321
|
+
}
|
|
5322
|
+
const installation = await store.updateInstallation(
|
|
5323
|
+
tenantId,
|
|
5324
|
+
installationId,
|
|
5325
|
+
body
|
|
5326
|
+
);
|
|
5327
|
+
if (!installation) {
|
|
5328
|
+
reply.code(404);
|
|
5329
|
+
return {
|
|
5330
|
+
success: false,
|
|
5331
|
+
message: "Channel installation not found"
|
|
5332
|
+
};
|
|
5333
|
+
}
|
|
5334
|
+
return {
|
|
5335
|
+
success: true,
|
|
5336
|
+
message: "Channel installation updated successfully",
|
|
5337
|
+
data: installation
|
|
5338
|
+
};
|
|
5339
|
+
} catch (error) {
|
|
5340
|
+
console.error("Failed to update channel installation:", error);
|
|
5341
|
+
return {
|
|
5342
|
+
success: false,
|
|
5343
|
+
message: "Failed to update channel installation"
|
|
5344
|
+
};
|
|
5345
|
+
}
|
|
5346
|
+
}
|
|
5347
|
+
async function deleteChannelInstallation(request, reply) {
|
|
5348
|
+
const tenantId = getTenantId9(request);
|
|
5349
|
+
const { installationId } = request.params;
|
|
5350
|
+
try {
|
|
5351
|
+
const store = getInstallationStore();
|
|
5352
|
+
const existing = await store.getInstallationById(installationId);
|
|
5353
|
+
if (!existing) {
|
|
5354
|
+
reply.code(404);
|
|
5355
|
+
return {
|
|
5356
|
+
success: false,
|
|
5357
|
+
message: "Channel installation not found"
|
|
5358
|
+
};
|
|
5359
|
+
}
|
|
5360
|
+
if (existing.tenantId !== tenantId) {
|
|
5361
|
+
reply.code(403);
|
|
5362
|
+
return {
|
|
5363
|
+
success: false,
|
|
5364
|
+
message: "Access denied"
|
|
5365
|
+
};
|
|
5366
|
+
}
|
|
5367
|
+
const deleted = await store.deleteInstallation(tenantId, installationId);
|
|
5368
|
+
if (!deleted) {
|
|
5369
|
+
reply.code(404);
|
|
5370
|
+
return {
|
|
5371
|
+
success: false,
|
|
5372
|
+
message: "Channel installation not found"
|
|
5373
|
+
};
|
|
5374
|
+
}
|
|
5375
|
+
return {
|
|
5376
|
+
success: true,
|
|
5377
|
+
message: "Channel installation deleted successfully"
|
|
5378
|
+
};
|
|
5379
|
+
} catch (error) {
|
|
5380
|
+
console.error("Failed to delete channel installation:", error);
|
|
5381
|
+
return {
|
|
5382
|
+
success: false,
|
|
5383
|
+
message: "Failed to delete channel installation"
|
|
5384
|
+
};
|
|
5385
|
+
}
|
|
5386
|
+
}
|
|
5387
|
+
|
|
5388
|
+
// src/routes/channel-installations.ts
|
|
5389
|
+
function registerChannelInstallationRoutes(app2) {
|
|
5390
|
+
app2.get("/api/channel-installations", getChannelInstallationList);
|
|
5391
|
+
app2.get("/api/channel-installations/:installationId", getChannelInstallation);
|
|
5392
|
+
app2.post("/api/channel-installations", createChannelInstallation);
|
|
5393
|
+
app2.put("/api/channel-installations/:installationId", updateChannelInstallation);
|
|
5394
|
+
app2.delete("/api/channel-installations/:installationId", deleteChannelInstallation);
|
|
5395
|
+
}
|
|
5396
|
+
|
|
4895
5397
|
// src/routes/index.ts
|
|
4896
5398
|
var registerLatticeRoutes = (app2) => {
|
|
4897
5399
|
app2.post("/api/runs", createRun);
|
|
@@ -5027,6 +5529,8 @@ var registerLatticeRoutes = (app2) => {
|
|
|
5027
5529
|
autoApproveUsers: process.env.AUTO_APPROVE_USERS !== "false",
|
|
5028
5530
|
allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false"
|
|
5029
5531
|
});
|
|
5532
|
+
registerChannelRoutes(app2);
|
|
5533
|
+
registerChannelInstallationRoutes(app2);
|
|
5030
5534
|
app2.delete(
|
|
5031
5535
|
"/api/assistants/:assistant_id/threads/:thread_id/pending-messages/:message_id",
|
|
5032
5536
|
removePendingMessageHandler
|
|
@@ -5096,7 +5600,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
|
|
|
5096
5600
|
};
|
|
5097
5601
|
|
|
5098
5602
|
// src/services/agent_task_consumer.ts
|
|
5099
|
-
var
|
|
5603
|
+
var import_core27 = require("@axiom-lattice/core");
|
|
5100
5604
|
var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
5101
5605
|
const {
|
|
5102
5606
|
assistant_id,
|
|
@@ -5111,18 +5615,18 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
5111
5615
|
console.log(
|
|
5112
5616
|
`\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
|
|
5113
5617
|
);
|
|
5114
|
-
const agent =
|
|
5115
|
-
await agent.addMessage({ input, command, custom_run_config: runConfig },
|
|
5618
|
+
const agent = import_core27.agentInstanceManager.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
|
|
5619
|
+
await agent.addMessage({ input, command, custom_run_config: runConfig }, import_core27.QueueMode.STEER);
|
|
5116
5620
|
if (callback_event) {
|
|
5117
5621
|
agent.subscribeOnce("message:completed", (evt) => {
|
|
5118
|
-
|
|
5622
|
+
import_core27.eventBus.publish(callback_event, {
|
|
5119
5623
|
success: true,
|
|
5120
5624
|
state: evt.state,
|
|
5121
5625
|
config: { assistant_id, thread_id, tenant_id }
|
|
5122
5626
|
});
|
|
5123
5627
|
});
|
|
5124
5628
|
agent.subscribeOnce("message:interrupted", (evt) => {
|
|
5125
|
-
|
|
5629
|
+
import_core27.eventBus.publish(callback_event, {
|
|
5126
5630
|
success: true,
|
|
5127
5631
|
state: evt.state,
|
|
5128
5632
|
config: { assistant_id, thread_id, tenant_id }
|
|
@@ -5146,7 +5650,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
5146
5650
|
return handleAgentTask(taskRequest, nextRetryCount);
|
|
5147
5651
|
}
|
|
5148
5652
|
if (callback_event) {
|
|
5149
|
-
|
|
5653
|
+
import_core27.eventBus.publish(callback_event, {
|
|
5150
5654
|
success: false,
|
|
5151
5655
|
error: error instanceof Error ? error.message : String(error),
|
|
5152
5656
|
config: { assistant_id, thread_id, tenant_id }
|
|
@@ -5184,7 +5688,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
5184
5688
|
* 初始化事件监听和队列轮询
|
|
5185
5689
|
*/
|
|
5186
5690
|
initialize() {
|
|
5187
|
-
|
|
5691
|
+
import_core27.eventBus.subscribe(import_core27.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
|
|
5188
5692
|
this.startPollingQueue();
|
|
5189
5693
|
console.log("Agent\u4EFB\u52A1\u6D88\u8D39\u8005\u5DF2\u542F\u52A8\u5E76\u76D1\u542C\u4EFB\u52A1\u4E8B\u4EF6\u548C\u961F\u5217");
|
|
5190
5694
|
}
|
|
@@ -5303,7 +5807,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
5303
5807
|
handleAgentTask(taskRequest).catch((error) => {
|
|
5304
5808
|
console.error("\u5904\u7406Agent\u4EFB\u52A1\u65F6\u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:", error);
|
|
5305
5809
|
if (taskRequest.callback_event) {
|
|
5306
|
-
|
|
5810
|
+
import_core27.eventBus.publish(taskRequest.callback_event, {
|
|
5307
5811
|
success: false,
|
|
5308
5812
|
error: error instanceof Error ? error.message : String(error),
|
|
5309
5813
|
config: {
|
|
@@ -5323,26 +5827,26 @@ _AgentTaskConsumer.agent_run_endpoint = "http://localhost:4001/api/runs";
|
|
|
5323
5827
|
var AgentTaskConsumer = _AgentTaskConsumer;
|
|
5324
5828
|
|
|
5325
5829
|
// src/index.ts
|
|
5326
|
-
var
|
|
5327
|
-
var
|
|
5830
|
+
var import_core28 = require("@axiom-lattice/core");
|
|
5831
|
+
var import_protocols5 = require("@axiom-lattice/protocols");
|
|
5328
5832
|
process.on("unhandledRejection", (reason, promise) => {
|
|
5329
5833
|
console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
|
|
5330
5834
|
});
|
|
5331
5835
|
var DEFAULT_LOGGER_CONFIG = {
|
|
5332
5836
|
name: "default",
|
|
5333
5837
|
description: "Default logger for lattice-gateway service",
|
|
5334
|
-
type:
|
|
5838
|
+
type: import_protocols5.LoggerType.PINO,
|
|
5335
5839
|
serviceName: "lattice/gateway",
|
|
5336
5840
|
loggerName: "lattice/gateway"
|
|
5337
5841
|
};
|
|
5338
5842
|
var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
|
|
5339
5843
|
var logger = loggerLattice.client;
|
|
5340
5844
|
function initializeLogger(config) {
|
|
5341
|
-
if (
|
|
5342
|
-
|
|
5845
|
+
if (import_core28.loggerLatticeManager.hasLattice("default")) {
|
|
5846
|
+
import_core28.loggerLatticeManager.removeLattice("default");
|
|
5343
5847
|
}
|
|
5344
|
-
(0,
|
|
5345
|
-
return (0,
|
|
5848
|
+
(0, import_core28.registerLoggerLattice)("default", config);
|
|
5849
|
+
return (0, import_core28.getLoggerLattice)("default");
|
|
5346
5850
|
}
|
|
5347
5851
|
var app = (0, import_fastify.default)({
|
|
5348
5852
|
logger: false,
|
|
@@ -5434,6 +5938,22 @@ app.setErrorHandler((error, request, reply) => {
|
|
|
5434
5938
|
error: error.message || "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF"
|
|
5435
5939
|
});
|
|
5436
5940
|
});
|
|
5941
|
+
function getConfiguredSandboxProvider() {
|
|
5942
|
+
const sandboxProviderType = process.env.SANDBOX_PROVIDER_TYPE || "microsandbox";
|
|
5943
|
+
return (0, import_core28.createSandboxProvider)({
|
|
5944
|
+
type: sandboxProviderType,
|
|
5945
|
+
remoteBaseURL: process.env.SANDBOX_BASE_URL,
|
|
5946
|
+
microsandboxServiceBaseURL: process.env.MICROSANDBOX_SERVICE_BASE_URL,
|
|
5947
|
+
e2bApiKey: process.env.E2B_API_KEY,
|
|
5948
|
+
e2bTemplate: process.env.E2B_TEMPLATE,
|
|
5949
|
+
e2bTimeoutMs: process.env.E2B_TIMEOUT_MS ? parseInt(process.env.E2B_TIMEOUT_MS, 10) : void 0,
|
|
5950
|
+
daytonaApiKey: process.env.DAYTONA_API_KEY,
|
|
5951
|
+
daytonaApiUrl: process.env.DAYTONA_API_URL,
|
|
5952
|
+
daytonaTarget: process.env.DAYTONA_TARGET,
|
|
5953
|
+
daytonaTimeout: process.env.DAYTONA_TIMEOUT ? parseInt(process.env.DAYTONA_TIMEOUT, 10) : void 0,
|
|
5954
|
+
daytonaVolumeName: process.env.DAYTONA_VOLUME_NAME
|
|
5955
|
+
});
|
|
5956
|
+
}
|
|
5437
5957
|
var start = async (config) => {
|
|
5438
5958
|
try {
|
|
5439
5959
|
if (config?.loggerConfig) {
|
|
@@ -5449,19 +5969,16 @@ var start = async (config) => {
|
|
|
5449
5969
|
app.decorate("loggerLattice", loggerLattice);
|
|
5450
5970
|
registerLatticeRoutes(app);
|
|
5451
5971
|
try {
|
|
5452
|
-
const storeLattice = (0,
|
|
5972
|
+
const storeLattice = (0, import_core28.getStoreLattice)("default", "database");
|
|
5453
5973
|
const store = storeLattice.store;
|
|
5454
|
-
|
|
5974
|
+
import_core28.sqlDatabaseManager.setConfigStore(store);
|
|
5455
5975
|
logger.info("Database config store set for SqlDatabaseManager");
|
|
5456
5976
|
} catch (error) {
|
|
5457
5977
|
logger.warn("Failed to set database config store: " + (error instanceof Error ? error.message : String(error)));
|
|
5458
5978
|
}
|
|
5459
|
-
if (!
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
baseURL: sandboxBaseURL
|
|
5463
|
-
});
|
|
5464
|
-
logger.info(`Registered sandbox manager with baseURL: ${sandboxBaseURL}`);
|
|
5979
|
+
if (!import_core28.sandboxLatticeManager.hasLattice("default")) {
|
|
5980
|
+
import_core28.sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
|
|
5981
|
+
logger.info("Registered sandbox manager from env configuration");
|
|
5465
5982
|
}
|
|
5466
5983
|
const target_port = config?.port || Number(process.env.PORT) || 4001;
|
|
5467
5984
|
await app.listen({ port: target_port, host: "0.0.0.0" });
|
|
@@ -5481,7 +5998,7 @@ var start = async (config) => {
|
|
|
5481
5998
|
}
|
|
5482
5999
|
try {
|
|
5483
6000
|
logger.info("Starting agent instance recovery...");
|
|
5484
|
-
const restoreStats = await
|
|
6001
|
+
const restoreStats = await import_core28.agentInstanceManager.restore();
|
|
5485
6002
|
logger.info(`Agent recovery complete: ${restoreStats.restored} threads restored, ${restoreStats.errors} errors`);
|
|
5486
6003
|
} catch (error) {
|
|
5487
6004
|
logger.error("Agent recovery failed", { error });
|