@axiom-lattice/gateway 3.0.10 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/A2AAuthService-DK5X6VIL.mjs +11 -0
- package/dist/A2AAuthService-DK5X6VIL.mjs.map +1 -0
- package/dist/a2a-7N3WDAOP.mjs +147 -0
- package/dist/a2a-7N3WDAOP.mjs.map +1 -0
- package/dist/a2a-standard-U2XYG45L.mjs +1376 -0
- package/dist/a2a-standard-U2XYG45L.mjs.map +1 -0
- package/dist/chunk-2HOCF46L.mjs +143 -0
- package/dist/chunk-2HOCF46L.mjs.map +1 -0
- package/dist/chunk-IDOEIBCF.mjs +165 -0
- package/dist/chunk-IDOEIBCF.mjs.map +1 -0
- package/dist/index.js +3445 -1633
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1625 -1056
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -6
- package/dist/a2a-QMHEH3ZT.mjs +0 -615
- package/dist/a2a-QMHEH3ZT.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -6,6 +6,15 @@ import {
|
|
|
6
6
|
getQrCode,
|
|
7
7
|
getQrCodeStatus
|
|
8
8
|
} from "./chunk-6CUQGDJI.mjs";
|
|
9
|
+
import {
|
|
10
|
+
PathOutsideRootError,
|
|
11
|
+
ProjectFileValidationError,
|
|
12
|
+
filesystemRootDirectory,
|
|
13
|
+
isBinaryContentType,
|
|
14
|
+
isPathWithinRoot,
|
|
15
|
+
resolvePathWithinRoot,
|
|
16
|
+
saveProjectFile
|
|
17
|
+
} from "./chunk-IDOEIBCF.mjs";
|
|
9
18
|
import {
|
|
10
19
|
getContentTypeFromFilename,
|
|
11
20
|
getFilenameFromPath
|
|
@@ -59,8 +68,8 @@ function resolveLocalA2AWorkingDirectory(options) {
|
|
|
59
68
|
}
|
|
60
69
|
function validateWorkingDirectory(cwd) {
|
|
61
70
|
try {
|
|
62
|
-
const
|
|
63
|
-
if (!
|
|
71
|
+
const stat = statSync(cwd);
|
|
72
|
+
if (!stat.isDirectory()) {
|
|
64
73
|
return `Working directory is not a directory: ${cwd}`;
|
|
65
74
|
}
|
|
66
75
|
return null;
|
|
@@ -739,17 +748,17 @@ async function getAssistantStats(request, reply) {
|
|
|
739
748
|
try {
|
|
740
749
|
const threads = await threadStore.getThreadsByAssistantId(tenantId, assistant.id);
|
|
741
750
|
threadCount = threads.length;
|
|
742
|
-
for (const
|
|
743
|
-
const created = new Date(
|
|
751
|
+
for (const t2 of threads) {
|
|
752
|
+
const created = new Date(t2.createdAt).getTime();
|
|
744
753
|
if (created >= now - ms7d) last7d++;
|
|
745
754
|
if (created >= now - ms30d) last30d++;
|
|
746
755
|
const idx7 = Math.floor((now - created) / 864e5);
|
|
747
756
|
if (idx7 >= 0 && idx7 < 7) daily7d[6 - idx7]++;
|
|
748
757
|
const idx30 = Math.floor((now - created) / 864e5);
|
|
749
758
|
if (idx30 >= 0 && idx30 < 30) daily30d[29 - idx30]++;
|
|
750
|
-
const updated = new Date(
|
|
759
|
+
const updated = new Date(t2.updatedAt).getTime();
|
|
751
760
|
if (lastActive === null || updated > lastActive.getTime()) {
|
|
752
|
-
lastActive = new Date(
|
|
761
|
+
lastActive = new Date(t2.updatedAt);
|
|
753
762
|
}
|
|
754
763
|
}
|
|
755
764
|
} catch {
|
|
@@ -895,7 +904,7 @@ var createRun = async (request, reply) => {
|
|
|
895
904
|
|
|
896
905
|
`);
|
|
897
906
|
if (!success) {
|
|
898
|
-
await new Promise((
|
|
907
|
+
await new Promise((resolve2) => reply.raw.once("drain", resolve2));
|
|
899
908
|
}
|
|
900
909
|
}
|
|
901
910
|
} catch (error) {
|
|
@@ -2183,11 +2192,11 @@ var CollectionService = class {
|
|
|
2183
2192
|
async getAllCollections(request) {
|
|
2184
2193
|
const tenantId = getTenantId5(request);
|
|
2185
2194
|
const collections = await collectionLatticeManager.getAllCollections(tenantId);
|
|
2186
|
-
for (const
|
|
2195
|
+
for (const c2 of collections) {
|
|
2187
2196
|
try {
|
|
2188
|
-
|
|
2197
|
+
c2._entryCount = await getCollectionEntryCount(c2.name, c2.embeddingKey, c2.tenantId);
|
|
2189
2198
|
} catch {
|
|
2190
|
-
|
|
2199
|
+
c2._entryCount = 0;
|
|
2191
2200
|
}
|
|
2192
2201
|
}
|
|
2193
2202
|
return collections;
|
|
@@ -3041,7 +3050,7 @@ async function getInboxItems(request, reply) {
|
|
|
3041
3050
|
const runStatus = await agent.getRunStatus();
|
|
3042
3051
|
if (runStatus !== "interrupted") return [];
|
|
3043
3052
|
const state = await agent.getCurrentState();
|
|
3044
|
-
const interrupts = state.tasks?.flatMap((
|
|
3053
|
+
const interrupts = state.tasks?.flatMap((t2) => t2.interrupts || []) || [];
|
|
3045
3054
|
return interrupts.map((i) => ({
|
|
3046
3055
|
runId: r.id,
|
|
3047
3056
|
assistantId: r.assistantId,
|
|
@@ -3398,7 +3407,8 @@ async function createPersonalAssistant(request, reply) {
|
|
|
3398
3407
|
const wsId = getWorkspaceId2(request);
|
|
3399
3408
|
await projectStore.createProject(tenantId, wsId, projectId, {
|
|
3400
3409
|
name: `${name}'s Space`,
|
|
3401
|
-
description: "Personal workspace for your assistant"
|
|
3410
|
+
description: "Personal workspace for your assistant",
|
|
3411
|
+
kind: "personal"
|
|
3402
3412
|
});
|
|
3403
3413
|
const config = PersonalAssistantConfig.get();
|
|
3404
3414
|
config.key = assistantId;
|
|
@@ -3448,8 +3458,8 @@ async function deletePersonalAssistant(request, reply) {
|
|
|
3448
3458
|
const threadStore = getStoreLattice5("default", "thread").store;
|
|
3449
3459
|
try {
|
|
3450
3460
|
const threads = await threadStore.getThreadsByAssistantId(tenantId, assistant.id);
|
|
3451
|
-
for (const
|
|
3452
|
-
await threadStore.deleteThread(tenantId,
|
|
3461
|
+
for (const t2 of threads) {
|
|
3462
|
+
await threadStore.deleteThread(tenantId, t2.id);
|
|
3453
3463
|
}
|
|
3454
3464
|
} catch {
|
|
3455
3465
|
}
|
|
@@ -3469,170 +3479,19 @@ async function deletePersonalAssistant(request, reply) {
|
|
|
3469
3479
|
return { success: true, message: "Personal assistant deleted" };
|
|
3470
3480
|
}
|
|
3471
3481
|
|
|
3472
|
-
// src/controllers/tasks.ts
|
|
3473
|
-
import { getStoreLattice as getStoreLattice6 } from "@axiom-lattice/core";
|
|
3474
|
-
function getTenantId9(request) {
|
|
3475
|
-
const userTenantId = request.user?.tenantId;
|
|
3476
|
-
if (userTenantId) return userTenantId;
|
|
3477
|
-
return request.headers["x-tenant-id"] || "default";
|
|
3478
|
-
}
|
|
3479
|
-
function getUserId4(request) {
|
|
3480
|
-
const userId = request.user?.id || request.headers["x-user-id"];
|
|
3481
|
-
if (!userId) throw new Error("User ID is required");
|
|
3482
|
-
return userId;
|
|
3483
|
-
}
|
|
3484
|
-
function getStore() {
|
|
3485
|
-
return getStoreLattice6("default", "task").store;
|
|
3486
|
-
}
|
|
3487
|
-
async function listTasks(request, reply) {
|
|
3488
|
-
try {
|
|
3489
|
-
const tenantId = getTenantId9(request);
|
|
3490
|
-
const query = request.query;
|
|
3491
|
-
const store = getStore();
|
|
3492
|
-
const tasks = await store.list({
|
|
3493
|
-
tenantId,
|
|
3494
|
-
status: query.status,
|
|
3495
|
-
priority: query.priority,
|
|
3496
|
-
...query.ownerId && { ownerId: query.ownerId },
|
|
3497
|
-
...query.ownerType && { ownerType: query.ownerType },
|
|
3498
|
-
...query.parentId && { parentId: query.parentId },
|
|
3499
|
-
metadata: query.metadata ? JSON.parse(query.metadata) : void 0,
|
|
3500
|
-
projectId: query.projectId,
|
|
3501
|
-
limit: query.limit ? parseInt(query.limit) : void 0,
|
|
3502
|
-
offset: query.offset ? parseInt(query.offset) : void 0
|
|
3503
|
-
});
|
|
3504
|
-
return { success: true, data: tasks, count: tasks.length };
|
|
3505
|
-
} catch (error) {
|
|
3506
|
-
return { success: false, error: error.message };
|
|
3507
|
-
}
|
|
3508
|
-
}
|
|
3509
|
-
async function getTask(request, reply) {
|
|
3510
|
-
try {
|
|
3511
|
-
const tenantId = getTenantId9(request);
|
|
3512
|
-
const userId = getUserId4(request);
|
|
3513
|
-
const { id } = request.params;
|
|
3514
|
-
const store = getStore();
|
|
3515
|
-
const task = await store.getById(tenantId, id);
|
|
3516
|
-
if (!task) {
|
|
3517
|
-
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
3518
|
-
}
|
|
3519
|
-
if (task.ownerType === "user" && task.ownerId !== userId) {
|
|
3520
|
-
return reply.status(403).send({ success: false, error: "Access denied" });
|
|
3521
|
-
}
|
|
3522
|
-
return { success: true, data: task };
|
|
3523
|
-
} catch (error) {
|
|
3524
|
-
return { success: false, error: error.message };
|
|
3525
|
-
}
|
|
3526
|
-
}
|
|
3527
|
-
async function createTask(request, reply) {
|
|
3528
|
-
try {
|
|
3529
|
-
const tenantId = getTenantId9(request);
|
|
3530
|
-
const userId = getUserId4(request);
|
|
3531
|
-
const body = request.body;
|
|
3532
|
-
if (!body.title) {
|
|
3533
|
-
return reply.status(400).send({ success: false, error: "title is required" });
|
|
3534
|
-
}
|
|
3535
|
-
const store = getStore();
|
|
3536
|
-
const task = await store.create({
|
|
3537
|
-
...body,
|
|
3538
|
-
tenantId,
|
|
3539
|
-
ownerType: body.ownerType || "user",
|
|
3540
|
-
ownerId: body.ownerId || userId
|
|
3541
|
-
});
|
|
3542
|
-
return reply.status(201).send({ success: true, data: task });
|
|
3543
|
-
} catch (error) {
|
|
3544
|
-
return reply.status(500).send({ success: false, error: error.message });
|
|
3545
|
-
}
|
|
3546
|
-
}
|
|
3547
|
-
async function updateTask(request, reply) {
|
|
3548
|
-
try {
|
|
3549
|
-
const tenantId = getTenantId9(request);
|
|
3550
|
-
const userId = getUserId4(request);
|
|
3551
|
-
const { id } = request.params;
|
|
3552
|
-
const store = getStore();
|
|
3553
|
-
const existing = await store.getById(tenantId, id);
|
|
3554
|
-
if (!existing) {
|
|
3555
|
-
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
3556
|
-
}
|
|
3557
|
-
if (existing.ownerType === "user" && existing.ownerId !== userId) {
|
|
3558
|
-
return reply.status(403).send({ success: false, error: "Access denied" });
|
|
3559
|
-
}
|
|
3560
|
-
const task = await store.update(tenantId, id, request.body);
|
|
3561
|
-
if (!task) {
|
|
3562
|
-
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
3563
|
-
}
|
|
3564
|
-
return { success: true, data: task };
|
|
3565
|
-
} catch (error) {
|
|
3566
|
-
return reply.status(500).send({ success: false, error: error.message });
|
|
3567
|
-
}
|
|
3568
|
-
}
|
|
3569
|
-
async function deleteTask(request, reply) {
|
|
3570
|
-
try {
|
|
3571
|
-
const tenantId = getTenantId9(request);
|
|
3572
|
-
const userId = getUserId4(request);
|
|
3573
|
-
const { id } = request.params;
|
|
3574
|
-
const store = getStore();
|
|
3575
|
-
const existing = await store.getById(tenantId, id);
|
|
3576
|
-
if (!existing) {
|
|
3577
|
-
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
3578
|
-
}
|
|
3579
|
-
if (existing.ownerType === "user" && existing.ownerId !== userId) {
|
|
3580
|
-
return reply.status(403).send({ success: false, error: "Access denied" });
|
|
3581
|
-
}
|
|
3582
|
-
const deleted = await store.delete(tenantId, id);
|
|
3583
|
-
if (!deleted) {
|
|
3584
|
-
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
3585
|
-
}
|
|
3586
|
-
return { success: true, message: "Task deleted" };
|
|
3587
|
-
} catch (error) {
|
|
3588
|
-
return reply.status(500).send({ success: false, error: error.message });
|
|
3589
|
-
}
|
|
3590
|
-
}
|
|
3591
|
-
async function completeTask(request, reply) {
|
|
3592
|
-
try {
|
|
3593
|
-
const tenantId = getTenantId9(request);
|
|
3594
|
-
const userId = getUserId4(request);
|
|
3595
|
-
const { id } = request.params;
|
|
3596
|
-
const store = getStore();
|
|
3597
|
-
const existing = await store.getById(tenantId, id);
|
|
3598
|
-
if (!existing) {
|
|
3599
|
-
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
3600
|
-
}
|
|
3601
|
-
if (existing.ownerType === "user" && existing.ownerId !== userId) {
|
|
3602
|
-
return reply.status(403).send({ success: false, error: "Access denied" });
|
|
3603
|
-
}
|
|
3604
|
-
const task = await store.update(tenantId, id, { status: "completed" });
|
|
3605
|
-
if (!task) {
|
|
3606
|
-
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
3607
|
-
}
|
|
3608
|
-
return { success: true, data: task };
|
|
3609
|
-
} catch (error) {
|
|
3610
|
-
return reply.status(500).send({ success: false, error: error.message });
|
|
3611
|
-
}
|
|
3612
|
-
}
|
|
3613
|
-
function getTaskWorkItemStore() {
|
|
3614
|
-
return getStoreLattice6("default", "taskWorkItem").store;
|
|
3615
|
-
}
|
|
3616
|
-
async function listTaskWorkItems(request, reply) {
|
|
3617
|
-
const tenantId = getTenantId9(request);
|
|
3618
|
-
const { id } = request.params;
|
|
3619
|
-
const { action, limit, offset } = request.query;
|
|
3620
|
-
const store = getTaskWorkItemStore();
|
|
3621
|
-
const items = await store.list({
|
|
3622
|
-
tenantId,
|
|
3623
|
-
taskId: id,
|
|
3624
|
-
action,
|
|
3625
|
-
limit: limit ? Number(limit) : void 0,
|
|
3626
|
-
offset: offset ? Number(offset) : void 0
|
|
3627
|
-
});
|
|
3628
|
-
return reply.send({ success: true, data: items });
|
|
3629
|
-
}
|
|
3630
|
-
|
|
3631
3482
|
// src/services/ExportImportService.ts
|
|
3632
|
-
import { ExportableEntityRegistry, DependencyResolver, IdRemapper } from "@axiom-lattice/core";
|
|
3483
|
+
import { ExportableEntityRegistry, DependencyResolver, IdRemapper, isBuiltInSkill } from "@axiom-lattice/core";
|
|
3633
3484
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
3485
|
+
import * as fs from "fs/promises";
|
|
3486
|
+
import * as os from "os";
|
|
3487
|
+
import * as path from "path";
|
|
3488
|
+
import * as tar from "tar";
|
|
3489
|
+
import * as zlib from "zlib";
|
|
3634
3490
|
var exportJobs = /* @__PURE__ */ new Map();
|
|
3635
3491
|
var JOB_TTL_MS = 60 * 60 * 1e3;
|
|
3492
|
+
var MAX_ARCHIVE_ENTRIES = 1e4;
|
|
3493
|
+
var MAX_ARCHIVE_FILE_BYTES = 100 * 1024 * 1024;
|
|
3494
|
+
var MAX_ARCHIVE_TOTAL_BYTES = 512 * 1024 * 1024;
|
|
3636
3495
|
function cleanExpiredJobs() {
|
|
3637
3496
|
const now = Date.now();
|
|
3638
3497
|
for (const [id, entry] of exportJobs) {
|
|
@@ -3641,6 +3500,9 @@ function cleanExpiredJobs() {
|
|
|
3641
3500
|
}
|
|
3642
3501
|
}
|
|
3643
3502
|
}
|
|
3503
|
+
function archiveFileKey(entityType, entityId, relPath) {
|
|
3504
|
+
return `files/${entityType}/${entityId}/${relPath}`;
|
|
3505
|
+
}
|
|
3644
3506
|
var ExportImportService = class {
|
|
3645
3507
|
constructor() {
|
|
3646
3508
|
this.registry = ExportableEntityRegistry.getInstance();
|
|
@@ -3648,40 +3510,44 @@ var ExportImportService = class {
|
|
|
3648
3510
|
/**
|
|
3649
3511
|
* Preview an export: check which types would be auto-included, which dependencies are missing.
|
|
3650
3512
|
*/
|
|
3651
|
-
async exportPreview(tenantId, selectedTypes) {
|
|
3513
|
+
async exportPreview(tenantId, selectedTypes, entityIds, context) {
|
|
3652
3514
|
const allDefs = this.registry.getAll();
|
|
3653
3515
|
const expanded = DependencyResolver.expandCascade(allDefs, selectedTypes);
|
|
3654
|
-
const cascadeAdditions = expanded.filter((
|
|
3516
|
+
const cascadeAdditions = expanded.filter((t2) => !selectedTypes.includes(t2));
|
|
3655
3517
|
const deps = DependencyResolver.computeDependencies(allDefs, expanded);
|
|
3656
3518
|
const registeredTypes = new Set(allDefs.map((d) => d.entityType));
|
|
3657
|
-
const missingDependencies = deps.missing.filter((
|
|
3519
|
+
const missingDependencies = deps.missing.filter((t2) => registeredTypes.has(t2));
|
|
3520
|
+
await this.resolveExportSelection(tenantId, selectedTypes, entityIds, context);
|
|
3658
3521
|
return { missingDependencies, cascadeAdditions };
|
|
3659
3522
|
}
|
|
3660
3523
|
/**
|
|
3661
|
-
* Generate a full
|
|
3662
|
-
* Stores the
|
|
3524
|
+
* Generate a full export (bundle + file content) for the given tenant and entity types.
|
|
3525
|
+
* Stores the data in-memory and returns a jobId for download.
|
|
3663
3526
|
*/
|
|
3664
|
-
async export(tenantId, entityTypes, entityIds) {
|
|
3665
|
-
const
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3527
|
+
async export(tenantId, entityTypes, entityIds, context) {
|
|
3528
|
+
const { dependencyOrder, entities: rawEntities } = await this.resolveExportSelection(
|
|
3529
|
+
tenantId,
|
|
3530
|
+
entityTypes,
|
|
3531
|
+
entityIds,
|
|
3532
|
+
context
|
|
3670
3533
|
);
|
|
3671
3534
|
const entities = {};
|
|
3535
|
+
const files = /* @__PURE__ */ new Map();
|
|
3672
3536
|
let exportCounter = 0;
|
|
3673
3537
|
for (const type of dependencyOrder) {
|
|
3674
3538
|
const def = this.registry.get(type);
|
|
3675
|
-
|
|
3676
|
-
if (entityIds && entityIds[type] && entityIds[type].length > 0) {
|
|
3677
|
-
const idSet = new Set(entityIds[type]);
|
|
3678
|
-
raw = raw.filter((e) => idSet.has(e.data.id));
|
|
3679
|
-
}
|
|
3539
|
+
const raw = rawEntities[type];
|
|
3680
3540
|
entities[type] = raw.map((e) => ({
|
|
3681
3541
|
...e,
|
|
3682
3542
|
_exportId: `exp-${type}-${String(++exportCounter).padStart(3, "0")}`,
|
|
3683
3543
|
data: this.sanitizeEntityData(e.data)
|
|
3684
3544
|
}));
|
|
3545
|
+
if (def.listFilesForExport && entities[type].length > 0) {
|
|
3546
|
+
const entityFiles = await def.listFilesForExport(tenantId, entities[type], context);
|
|
3547
|
+
for (const f of entityFiles) {
|
|
3548
|
+
files.set(archiveFileKey(type, f.entityId, f.path), f.content);
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3685
3551
|
}
|
|
3686
3552
|
const rawIdToExportId = /* @__PURE__ */ new Map();
|
|
3687
3553
|
for (const [, entityList] of Object.entries(entities)) {
|
|
@@ -3716,31 +3582,193 @@ var ExportImportService = class {
|
|
|
3716
3582
|
_warning: "This file contains plaintext secrets (passwords, API keys, tokens). Store it securely and delete after import."
|
|
3717
3583
|
};
|
|
3718
3584
|
const jobId = randomUUID4();
|
|
3719
|
-
exportJobs.set(jobId, { bundle, tenantId, createdAt: Date.now() });
|
|
3720
|
-
return { jobId, bundle };
|
|
3585
|
+
exportJobs.set(jobId, { data: { bundle, files }, tenantId, createdAt: Date.now() });
|
|
3586
|
+
return { jobId, data: { bundle, files } };
|
|
3721
3587
|
}
|
|
3722
3588
|
/**
|
|
3723
|
-
* Retrieve a previously generated export
|
|
3589
|
+
* Retrieve a previously generated export job data by jobId with tenant validation.
|
|
3724
3590
|
*/
|
|
3725
3591
|
getExportJob(jobId, tenantId) {
|
|
3726
3592
|
cleanExpiredJobs();
|
|
3727
3593
|
const entry = exportJobs.get(jobId);
|
|
3728
3594
|
if (!entry || entry.tenantId !== tenantId) return void 0;
|
|
3729
|
-
return entry.
|
|
3595
|
+
return entry.data;
|
|
3596
|
+
}
|
|
3597
|
+
/**
|
|
3598
|
+
* Pack an ExportJobData into a tar.gz buffer.
|
|
3599
|
+
* Layout: bundle.json at root, files under files/{entityType}/{entityId}/{path}.
|
|
3600
|
+
*/
|
|
3601
|
+
async packTarGz(data) {
|
|
3602
|
+
const { bundle, files } = data;
|
|
3603
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "lattice-export-"));
|
|
3604
|
+
try {
|
|
3605
|
+
await fs.writeFile(path.join(tmpDir, "bundle.json"), JSON.stringify(bundle, null, 2), "utf-8");
|
|
3606
|
+
for (const [relPath, content] of files.entries()) {
|
|
3607
|
+
const fullPath = path.join(tmpDir, relPath);
|
|
3608
|
+
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
3609
|
+
await fs.writeFile(fullPath, content);
|
|
3610
|
+
}
|
|
3611
|
+
const stream = tar.c({ gzip: true, portable: true, cwd: tmpDir }, ["./bundle.json", ...Array.from(files.keys())]);
|
|
3612
|
+
const chunks = [];
|
|
3613
|
+
for await (const chunk of stream) {
|
|
3614
|
+
chunks.push(Buffer.from(chunk));
|
|
3615
|
+
}
|
|
3616
|
+
return Buffer.concat(chunks);
|
|
3617
|
+
} finally {
|
|
3618
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
/**
|
|
3622
|
+
* Unpack a tar.gz buffer into an ExportJobData (bundle + files).
|
|
3623
|
+
*/
|
|
3624
|
+
async unpackTarGz(buffer) {
|
|
3625
|
+
const files = /* @__PURE__ */ new Map();
|
|
3626
|
+
let bundle;
|
|
3627
|
+
const gunzip = zlib.createGunzip();
|
|
3628
|
+
await new Promise((resolve2, reject) => {
|
|
3629
|
+
let settled = false;
|
|
3630
|
+
let entryCount = 0;
|
|
3631
|
+
let declaredTotalBytes = 0;
|
|
3632
|
+
let decompressedBytes = 0;
|
|
3633
|
+
let activeEntry;
|
|
3634
|
+
const paths = /* @__PURE__ */ new Set();
|
|
3635
|
+
const fail = (reason) => {
|
|
3636
|
+
if (settled) return;
|
|
3637
|
+
settled = true;
|
|
3638
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
3639
|
+
reject(error);
|
|
3640
|
+
try {
|
|
3641
|
+
activeEntry?.destroy(error);
|
|
3642
|
+
} catch {
|
|
3643
|
+
}
|
|
3644
|
+
try {
|
|
3645
|
+
parser.abort(error);
|
|
3646
|
+
} catch {
|
|
3647
|
+
}
|
|
3648
|
+
gunzip.destroy(error);
|
|
3649
|
+
};
|
|
3650
|
+
const countEntry = () => {
|
|
3651
|
+
entryCount += 1;
|
|
3652
|
+
if (entryCount <= MAX_ARCHIVE_ENTRIES) return true;
|
|
3653
|
+
fail(new Error(`Invalid export archive: exceeds ${MAX_ARCHIVE_ENTRIES.toLocaleString("en-US")} entries`));
|
|
3654
|
+
return false;
|
|
3655
|
+
};
|
|
3656
|
+
const parser = tar.t({
|
|
3657
|
+
gzip: false,
|
|
3658
|
+
onentry: (entry) => {
|
|
3659
|
+
activeEntry = entry;
|
|
3660
|
+
if (!countEntry()) return;
|
|
3661
|
+
entry.on("error", fail);
|
|
3662
|
+
entry.on("end", () => {
|
|
3663
|
+
if (activeEntry === entry) activeEntry = void 0;
|
|
3664
|
+
});
|
|
3665
|
+
const isDirectory = entry.type === "Directory";
|
|
3666
|
+
if (!isDirectory && entry.type !== "File" && entry.type !== "OldFile") {
|
|
3667
|
+
fail(new Error(`Invalid export archive: unsupported entry type "${entry.type}"`));
|
|
3668
|
+
return;
|
|
3669
|
+
}
|
|
3670
|
+
const rawPath = entry.path;
|
|
3671
|
+
if (rawPath.startsWith("/") || rawPath.includes("\\") || rawPath.includes("\0")) {
|
|
3672
|
+
fail(new Error(`Invalid export archive path: "${rawPath}"`));
|
|
3673
|
+
return;
|
|
3674
|
+
}
|
|
3675
|
+
const entryPath = rawPath.startsWith("./") ? rawPath.slice(2) : rawPath;
|
|
3676
|
+
const pathToValidate = isDirectory && entryPath.endsWith("/") ? entryPath.slice(0, -1) : entryPath;
|
|
3677
|
+
const segments = pathToValidate.split("/");
|
|
3678
|
+
if (segments.some((segment) => segment === "" || segment === "." || segment === "..") || (isDirectory ? !(pathToValidate === "files" || pathToValidate.startsWith("files/")) : entryPath !== "bundle.json" && !(entryPath.startsWith("files/") && segments.length > 1))) {
|
|
3679
|
+
fail(new Error(`Invalid export archive path: "${rawPath}"`));
|
|
3680
|
+
return;
|
|
3681
|
+
}
|
|
3682
|
+
if (isDirectory) {
|
|
3683
|
+
entry.resume();
|
|
3684
|
+
return;
|
|
3685
|
+
}
|
|
3686
|
+
if (paths.has(entryPath)) {
|
|
3687
|
+
fail(new Error(`Invalid export archive: duplicate path "${entryPath}"`));
|
|
3688
|
+
return;
|
|
3689
|
+
}
|
|
3690
|
+
paths.add(entryPath);
|
|
3691
|
+
if (!Number.isSafeInteger(entry.size) || entry.size < 0 || entry.size > MAX_ARCHIVE_FILE_BYTES) {
|
|
3692
|
+
fail(new Error("Invalid export archive: regular file size exceeds 100 MB"));
|
|
3693
|
+
return;
|
|
3694
|
+
}
|
|
3695
|
+
declaredTotalBytes += entry.size;
|
|
3696
|
+
if (declaredTotalBytes > MAX_ARCHIVE_TOTAL_BYTES) {
|
|
3697
|
+
fail(new Error("Invalid export archive: total uncompressed size exceeds 512 MB"));
|
|
3698
|
+
return;
|
|
3699
|
+
}
|
|
3700
|
+
const chunks = [];
|
|
3701
|
+
let streamedEntryBytes = 0;
|
|
3702
|
+
entry.on("data", (chunk) => {
|
|
3703
|
+
if (settled) return;
|
|
3704
|
+
streamedEntryBytes += chunk.length;
|
|
3705
|
+
if (streamedEntryBytes > MAX_ARCHIVE_FILE_BYTES) {
|
|
3706
|
+
fail(new Error("Invalid export archive: regular file size exceeds 100 MB"));
|
|
3707
|
+
return;
|
|
3708
|
+
}
|
|
3709
|
+
chunks.push(chunk);
|
|
3710
|
+
});
|
|
3711
|
+
entry.on("end", () => {
|
|
3712
|
+
if (settled) return;
|
|
3713
|
+
const content = Buffer.concat(chunks, streamedEntryBytes);
|
|
3714
|
+
if (entryPath === "bundle.json") {
|
|
3715
|
+
try {
|
|
3716
|
+
bundle = JSON.parse(content.toString("utf-8"));
|
|
3717
|
+
} catch (error) {
|
|
3718
|
+
fail(new Error(
|
|
3719
|
+
`Invalid export archive: malformed bundle.json: ${error instanceof Error ? error.message : String(error)}`
|
|
3720
|
+
));
|
|
3721
|
+
}
|
|
3722
|
+
} else {
|
|
3723
|
+
files.set(entryPath, content);
|
|
3724
|
+
}
|
|
3725
|
+
});
|
|
3726
|
+
}
|
|
3727
|
+
});
|
|
3728
|
+
gunzip.on("data", (chunk) => {
|
|
3729
|
+
if (settled) return;
|
|
3730
|
+
decompressedBytes += chunk.length;
|
|
3731
|
+
if (decompressedBytes > MAX_ARCHIVE_TOTAL_BYTES) {
|
|
3732
|
+
fail(new Error("Invalid export archive: total uncompressed size exceeds 512 MB"));
|
|
3733
|
+
}
|
|
3734
|
+
});
|
|
3735
|
+
gunzip.on("error", fail);
|
|
3736
|
+
parser.on("ignoredEntry", (entry) => {
|
|
3737
|
+
if (!countEntry()) return;
|
|
3738
|
+
fail(new Error(`Invalid export archive: unsupported entry type "${entry.type}"`));
|
|
3739
|
+
});
|
|
3740
|
+
parser.on("meta", () => {
|
|
3741
|
+
if (!countEntry()) return;
|
|
3742
|
+
fail(new Error("Invalid export archive: unsupported metadata entry"));
|
|
3743
|
+
});
|
|
3744
|
+
parser.on("error", fail);
|
|
3745
|
+
parser.on("end", () => {
|
|
3746
|
+
if (settled) return;
|
|
3747
|
+
settled = true;
|
|
3748
|
+
resolve2();
|
|
3749
|
+
});
|
|
3750
|
+
gunzip.pipe(parser);
|
|
3751
|
+
gunzip.end(buffer);
|
|
3752
|
+
});
|
|
3753
|
+
if (!bundle) {
|
|
3754
|
+
throw new Error("Invalid export archive: missing bundle.json");
|
|
3755
|
+
}
|
|
3756
|
+
return { bundle, files };
|
|
3730
3757
|
}
|
|
3731
3758
|
/**
|
|
3732
3759
|
* Preview import: validate bundle and detect conflicts.
|
|
3733
3760
|
*/
|
|
3734
|
-
async previewImport(tenantId, bundle) {
|
|
3735
|
-
this.validateBundle(bundle);
|
|
3761
|
+
async previewImport(tenantId, bundle, context) {
|
|
3762
|
+
const dependencyOrder = this.validateBundle(bundle);
|
|
3736
3763
|
const allConflicts = [];
|
|
3737
3764
|
const allInsertions = [];
|
|
3738
3765
|
const allErrors = [];
|
|
3739
|
-
for (const
|
|
3766
|
+
for (const entityType of dependencyOrder) {
|
|
3767
|
+
const entities = bundle.entities[entityType];
|
|
3740
3768
|
if (entities.length === 0) continue;
|
|
3741
3769
|
try {
|
|
3742
3770
|
const def = this.registry.get(entityType);
|
|
3743
|
-
const result = await def.previewImport(tenantId, entities);
|
|
3771
|
+
const result = await def.previewImport(tenantId, entities, context);
|
|
3744
3772
|
allConflicts.push(...result.conflicts);
|
|
3745
3773
|
allInsertions.push(...result.insertions);
|
|
3746
3774
|
allErrors.push(...result.errors);
|
|
@@ -3758,25 +3786,132 @@ var ExportImportService = class {
|
|
|
3758
3786
|
}
|
|
3759
3787
|
/**
|
|
3760
3788
|
* Apply import: create entities in dependency order with ID remapping.
|
|
3789
|
+
* File content (if any) is passed to registrations via the `files` map.
|
|
3761
3790
|
*/
|
|
3762
|
-
async applyImport(tenantId, bundle, resolutions) {
|
|
3763
|
-
this.validateBundle(bundle);
|
|
3791
|
+
async applyImport(tenantId, bundle, resolutions, files, context) {
|
|
3792
|
+
const dependencyOrder = this.validateBundle(bundle);
|
|
3764
3793
|
const results = [];
|
|
3765
3794
|
const idMap = {};
|
|
3795
|
+
const unresolvedExportIds = /* @__PURE__ */ new Set();
|
|
3766
3796
|
const skillIdRemap = {};
|
|
3767
|
-
|
|
3797
|
+
const skillSourceToTarget = /* @__PURE__ */ new Map();
|
|
3798
|
+
const unresolvedSkillReferences = /* @__PURE__ */ new Map();
|
|
3799
|
+
const skillEntities = bundle.entities.skill ?? [];
|
|
3800
|
+
const skillConflicts = skillEntities.length > 0 ? await this.registry.get("skill").previewImport(tenantId, skillEntities, context) : { conflicts: [] };
|
|
3801
|
+
const skillConflictByExportId = new Map(
|
|
3802
|
+
skillConflicts.conflicts.map((conflict) => [conflict._exportId, conflict])
|
|
3803
|
+
);
|
|
3804
|
+
for (const entity of skillEntities) {
|
|
3805
|
+
const resolution = resolutions[entity._exportId];
|
|
3806
|
+
const sourceId = entity.data.id;
|
|
3807
|
+
const targetId = resolution?.action === "skip" ? skillConflictByExportId.get(entity._exportId)?.existingId : resolution?.action === "rename" && resolution.newId ? resolution.newId : sourceId;
|
|
3808
|
+
if (targetId) skillSourceToTarget.set(sourceId, targetId);
|
|
3809
|
+
}
|
|
3810
|
+
for (const entity of skillEntities) {
|
|
3811
|
+
const subSkills = entity.data.subSkills;
|
|
3812
|
+
if (!Array.isArray(subSkills)) continue;
|
|
3813
|
+
const unresolved = subSkills.find((id) => typeof id === "string" && !skillSourceToTarget.has(id) && !isBuiltInSkill(id));
|
|
3814
|
+
if (unresolved) unresolvedSkillReferences.set(entity._exportId, unresolved);
|
|
3815
|
+
}
|
|
3816
|
+
for (const entityType of dependencyOrder) {
|
|
3768
3817
|
const entities = bundle.entities[entityType];
|
|
3769
3818
|
if (!entities || entities.length === 0) continue;
|
|
3770
3819
|
const def = this.registry.get(entityType);
|
|
3771
3820
|
const remapper = new IdRemapper(idMap);
|
|
3821
|
+
let insertions = /* @__PURE__ */ new Set();
|
|
3822
|
+
let conflicts = /* @__PURE__ */ new Set();
|
|
3823
|
+
let conflictByExportId = /* @__PURE__ */ new Map();
|
|
3824
|
+
let previewError;
|
|
3825
|
+
try {
|
|
3826
|
+
const preview = await def.previewImport(tenantId, entities, context);
|
|
3827
|
+
insertions = new Set(preview.insertions.map((item) => item._exportId));
|
|
3828
|
+
conflicts = new Set(preview.conflicts.map((item) => item._exportId));
|
|
3829
|
+
conflictByExportId = new Map(preview.conflicts.map((item) => [item._exportId, item]));
|
|
3830
|
+
} catch (err) {
|
|
3831
|
+
previewError = err.message;
|
|
3832
|
+
}
|
|
3772
3833
|
for (const entity of entities) {
|
|
3773
|
-
|
|
3834
|
+
let resolution = resolutions[entity._exportId];
|
|
3774
3835
|
if (resolution?.action === "skip") {
|
|
3775
|
-
|
|
3836
|
+
const conflict = conflictByExportId.get(entity._exportId);
|
|
3837
|
+
if (conflict && !conflict.existingId) {
|
|
3838
|
+
unresolvedExportIds.add(entity._exportId);
|
|
3839
|
+
results.push({
|
|
3840
|
+
_exportId: entity._exportId,
|
|
3841
|
+
entityType,
|
|
3842
|
+
status: "failed",
|
|
3843
|
+
error: "Cannot skip conflict without an existing ID"
|
|
3844
|
+
});
|
|
3845
|
+
continue;
|
|
3846
|
+
}
|
|
3847
|
+
if (conflict?.existingId) {
|
|
3848
|
+
idMap[entity._exportId] = conflict.existingId;
|
|
3849
|
+
}
|
|
3850
|
+
results.push({
|
|
3851
|
+
_exportId: entity._exportId,
|
|
3852
|
+
entityType,
|
|
3853
|
+
status: "skipped",
|
|
3854
|
+
newId: conflict?.existingId
|
|
3855
|
+
});
|
|
3856
|
+
continue;
|
|
3857
|
+
}
|
|
3858
|
+
const pendingValues = [entity.data];
|
|
3859
|
+
let unresolvedReference;
|
|
3860
|
+
while (pendingValues.length > 0 && !unresolvedReference) {
|
|
3861
|
+
const value = pendingValues.pop();
|
|
3862
|
+
if (typeof value === "string") {
|
|
3863
|
+
const match = value.match(/^@\w+\/(.+)$/);
|
|
3864
|
+
if (match && unresolvedExportIds.has(match[1])) {
|
|
3865
|
+
unresolvedReference = match[1];
|
|
3866
|
+
}
|
|
3867
|
+
} else if (Array.isArray(value)) {
|
|
3868
|
+
pendingValues.push(...value);
|
|
3869
|
+
} else if (value && typeof value === "object") {
|
|
3870
|
+
pendingValues.push(...Object.values(value));
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
if (unresolvedReference) {
|
|
3874
|
+
unresolvedExportIds.add(entity._exportId);
|
|
3875
|
+
results.push({
|
|
3876
|
+
_exportId: entity._exportId,
|
|
3877
|
+
entityType,
|
|
3878
|
+
status: "failed",
|
|
3879
|
+
error: `Unresolved reference to failed import entity "${unresolvedReference}"`
|
|
3880
|
+
});
|
|
3776
3881
|
continue;
|
|
3777
3882
|
}
|
|
3883
|
+
if (!resolution) {
|
|
3884
|
+
if (previewError || conflicts.has(entity._exportId) || !insertions.has(entity._exportId)) {
|
|
3885
|
+
results.push({
|
|
3886
|
+
_exportId: entity._exportId,
|
|
3887
|
+
entityType,
|
|
3888
|
+
status: "failed",
|
|
3889
|
+
error: previewError ? `Unable to verify missing conflict resolution: ${previewError}` : "Missing conflict resolution"
|
|
3890
|
+
});
|
|
3891
|
+
continue;
|
|
3892
|
+
}
|
|
3893
|
+
resolution = {
|
|
3894
|
+
_exportId: entity._exportId,
|
|
3895
|
+
action: "insert"
|
|
3896
|
+
};
|
|
3897
|
+
}
|
|
3778
3898
|
try {
|
|
3779
3899
|
const remappedData = remapper.remapReferences(entity.data);
|
|
3900
|
+
if (entityType === "skill") {
|
|
3901
|
+
const unresolvedSkill = unresolvedSkillReferences.get(entity._exportId);
|
|
3902
|
+
if (unresolvedSkill) {
|
|
3903
|
+
results.push({
|
|
3904
|
+
_exportId: entity._exportId,
|
|
3905
|
+
entityType,
|
|
3906
|
+
status: "failed",
|
|
3907
|
+
error: `Unresolved subSkill reference "${unresolvedSkill}"`
|
|
3908
|
+
});
|
|
3909
|
+
continue;
|
|
3910
|
+
}
|
|
3911
|
+
if (Array.isArray(remappedData.subSkills)) {
|
|
3912
|
+
remappedData.subSkills = remappedData.subSkills.map((id) => typeof id === "string" ? skillSourceToTarget.get(id) ?? id : id);
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3780
3915
|
if (entityType === "agent" && Object.keys(skillIdRemap).length > 0) {
|
|
3781
3916
|
const skillRemapper = new IdRemapper({});
|
|
3782
3917
|
remappedData.graphDefinition = skillRemapper.remapSkillIds(
|
|
@@ -3784,19 +3919,26 @@ var ExportImportService = class {
|
|
|
3784
3919
|
skillIdRemap
|
|
3785
3920
|
);
|
|
3786
3921
|
}
|
|
3787
|
-
if (resolution
|
|
3922
|
+
if (resolution.action === "rename" && resolution.newId) {
|
|
3788
3923
|
remappedData.id = resolution.newId;
|
|
3789
3924
|
}
|
|
3925
|
+
const entityFiles = this.extractEntityFiles(
|
|
3926
|
+
entityType,
|
|
3927
|
+
entity.data.id,
|
|
3928
|
+
files
|
|
3929
|
+
);
|
|
3790
3930
|
const entityWithRemappedData = { ...entity, data: remappedData };
|
|
3791
3931
|
const { newId } = await def.applyImport(
|
|
3792
3932
|
tenantId,
|
|
3793
3933
|
entityWithRemappedData,
|
|
3794
|
-
resolution
|
|
3934
|
+
resolution,
|
|
3935
|
+
entityFiles,
|
|
3936
|
+
context
|
|
3795
3937
|
);
|
|
3796
3938
|
if (newId) {
|
|
3797
3939
|
idMap[entity._exportId] = newId;
|
|
3798
3940
|
if (entityType === "skill" && newId) {
|
|
3799
|
-
const oldSkillId =
|
|
3941
|
+
const oldSkillId = entity.data.id;
|
|
3800
3942
|
if (oldSkillId !== newId) {
|
|
3801
3943
|
skillIdRemap[oldSkillId] = newId;
|
|
3802
3944
|
}
|
|
@@ -3804,7 +3946,7 @@ var ExportImportService = class {
|
|
|
3804
3946
|
results.push({
|
|
3805
3947
|
_exportId: entity._exportId,
|
|
3806
3948
|
entityType,
|
|
3807
|
-
status: resolution
|
|
3949
|
+
status: resolution.action === "overwrite" ? "updated" : "created",
|
|
3808
3950
|
newId
|
|
3809
3951
|
});
|
|
3810
3952
|
}
|
|
@@ -3826,43 +3968,292 @@ var ExportImportService = class {
|
|
|
3826
3968
|
listExportableTypes() {
|
|
3827
3969
|
return this.registry.listTypes();
|
|
3828
3970
|
}
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
}
|
|
3836
|
-
|
|
3837
|
-
|
|
3971
|
+
/**
|
|
3972
|
+
* Extract file content for a single entity from the archive files map.
|
|
3973
|
+
* Keys are `files/{entityType}/{entityId}/{path}`; returns a Map of relative path → Buffer.
|
|
3974
|
+
*/
|
|
3975
|
+
extractEntityFiles(entityType, entityId, files) {
|
|
3976
|
+
if (!files || files.size === 0) return void 0;
|
|
3977
|
+
const prefix = `files/${entityType}/${entityId}/`;
|
|
3978
|
+
const result = /* @__PURE__ */ new Map();
|
|
3979
|
+
for (const [path4, content] of files.entries()) {
|
|
3980
|
+
if (path4.startsWith(prefix)) {
|
|
3981
|
+
result.set(path4.slice(prefix.length), content);
|
|
3982
|
+
}
|
|
3838
3983
|
}
|
|
3984
|
+
return result.size > 0 ? result : void 0;
|
|
3839
3985
|
}
|
|
3840
|
-
|
|
3841
|
-
const
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
}
|
|
3986
|
+
async resolveExportSelection(tenantId, entityTypes, entityIds, context) {
|
|
3987
|
+
const allDefs = this.registry.getAll();
|
|
3988
|
+
const registeredTypes = new Set(allDefs.map((def) => def.entityType));
|
|
3989
|
+
for (const type of entityTypes) {
|
|
3990
|
+
if (!registeredTypes.has(type)) {
|
|
3991
|
+
throw new Error(`Exportable entity type is not registered: "${type}"`);
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
const expanded = DependencyResolver.expandCascade(allDefs, entityTypes);
|
|
3995
|
+
const dependencies = DependencyResolver.computeDependencies(allDefs, expanded).missing;
|
|
3996
|
+
const finalTypeSet = /* @__PURE__ */ new Set([...expanded, ...dependencies]);
|
|
3997
|
+
const dependencyOrder = DependencyResolver.resolveOrder(
|
|
3998
|
+
allDefs.filter((def) => finalTypeSet.has(def.entityType))
|
|
3999
|
+
);
|
|
4000
|
+
const availableByType = /* @__PURE__ */ new Map();
|
|
4001
|
+
const selectedIds = /* @__PURE__ */ new Map();
|
|
4002
|
+
const processedIds = /* @__PURE__ */ new Map();
|
|
4003
|
+
for (const type of dependencyOrder) {
|
|
4004
|
+
const raw = await this.registry.get(type).listForExport(tenantId, context);
|
|
4005
|
+
availableByType.set(type, new Map(raw.map((entity) => [entity.data.id, entity])));
|
|
4006
|
+
if (entityIds && Object.prototype.hasOwnProperty.call(entityIds, type)) {
|
|
4007
|
+
selectedIds.set(type, new Set(entityIds[type]));
|
|
4008
|
+
} else if (expanded.includes(type)) {
|
|
4009
|
+
selectedIds.set(type, new Set(raw.map((entity) => entity.data.id)));
|
|
4010
|
+
} else {
|
|
4011
|
+
selectedIds.set(type, /* @__PURE__ */ new Set());
|
|
4012
|
+
}
|
|
4013
|
+
processedIds.set(type, /* @__PURE__ */ new Set());
|
|
4014
|
+
}
|
|
4015
|
+
let referencesAdded = true;
|
|
4016
|
+
while (referencesAdded) {
|
|
4017
|
+
referencesAdded = false;
|
|
4018
|
+
for (const type of dependencyOrder) {
|
|
4019
|
+
const def = this.registry.get(type);
|
|
4020
|
+
if (!def.getReferencedEntityIds) continue;
|
|
4021
|
+
const processed = processedIds.get(type);
|
|
4022
|
+
const available = availableByType.get(type);
|
|
4023
|
+
const unprocessed = [...selectedIds.get(type)].filter((id) => !processed.has(id)).map((id) => available.get(id)).filter((entity) => entity !== void 0);
|
|
4024
|
+
if (unprocessed.length === 0) continue;
|
|
4025
|
+
for (const entity of unprocessed) processed.add(entity.data.id);
|
|
4026
|
+
const references = await def.getReferencedEntityIds(tenantId, unprocessed, context);
|
|
4027
|
+
for (const [referencedType, ids] of Object.entries(references)) {
|
|
4028
|
+
if (!finalTypeSet.has(referencedType)) {
|
|
4029
|
+
throw new Error(
|
|
4030
|
+
`Unresolved reference from ${type}: entity type "${referencedType}" is not in its registered dependency closure`
|
|
4031
|
+
);
|
|
4032
|
+
}
|
|
4033
|
+
const targetSelection = selectedIds.get(referencedType);
|
|
4034
|
+
const targetEntities = availableByType.get(referencedType);
|
|
4035
|
+
const explicitSelection = entityIds && Object.prototype.hasOwnProperty.call(entityIds, referencedType) ? new Set(entityIds[referencedType]) : void 0;
|
|
4036
|
+
for (const id of new Set(ids)) {
|
|
4037
|
+
if (referencedType === "skill" && isBuiltInSkill(id)) continue;
|
|
4038
|
+
if (explicitSelection && !explicitSelection.has(id)) {
|
|
4039
|
+
throw new Error(
|
|
4040
|
+
`Required reference from ${type} to ${referencedType} "${id}" is omitted by entityIds.${referencedType}`
|
|
4041
|
+
);
|
|
4042
|
+
}
|
|
4043
|
+
if (!targetEntities.has(id)) {
|
|
4044
|
+
throw new Error(`Unresolved reference from ${type} to ${referencedType} "${id}"`);
|
|
4045
|
+
}
|
|
4046
|
+
if (!targetSelection.has(id)) {
|
|
4047
|
+
targetSelection.add(id);
|
|
4048
|
+
referencesAdded = true;
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
const entities = {};
|
|
4055
|
+
for (const type of dependencyOrder) {
|
|
4056
|
+
const ids = selectedIds.get(type);
|
|
4057
|
+
entities[type] = [...availableByType.get(type).values()].filter((entity) => ids.has(entity.data.id));
|
|
4058
|
+
}
|
|
4059
|
+
return { dependencyOrder, entities };
|
|
4060
|
+
}
|
|
4061
|
+
validateBundle(bundle) {
|
|
4062
|
+
if (bundle.version !== 1) {
|
|
4063
|
+
throw new Error(`Unsupported bundle version: ${bundle.version}. Expected: 1`);
|
|
4064
|
+
}
|
|
4065
|
+
if (!bundle.entities || typeof bundle.entities !== "object" || Array.isArray(bundle.entities)) {
|
|
4066
|
+
throw new Error("Invalid bundle: missing entities");
|
|
4067
|
+
}
|
|
4068
|
+
if (!Array.isArray(bundle.dependencyOrder)) {
|
|
4069
|
+
throw new Error("Invalid bundle: missing dependencyOrder");
|
|
4070
|
+
}
|
|
4071
|
+
const orderSet = new Set(bundle.dependencyOrder);
|
|
4072
|
+
if (orderSet.size !== bundle.dependencyOrder.length) {
|
|
4073
|
+
throw new Error("Invalid bundle: duplicate type in dependencyOrder");
|
|
4074
|
+
}
|
|
4075
|
+
const entityTypes = Object.keys(bundle.entities);
|
|
4076
|
+
for (const entityType of entityTypes) {
|
|
4077
|
+
if (!Array.isArray(bundle.entities[entityType])) {
|
|
4078
|
+
throw new Error(`Invalid bundle: entities.${entityType} must be an array`);
|
|
4079
|
+
}
|
|
4080
|
+
if (!orderSet.has(entityType)) {
|
|
4081
|
+
throw new Error(`Invalid bundle: dependencyOrder omits entities type "${entityType}"`);
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
for (const entityType of bundle.dependencyOrder) {
|
|
4085
|
+
if (!Object.prototype.hasOwnProperty.call(bundle.entities, entityType)) {
|
|
4086
|
+
throw new Error(`Invalid bundle: missing entities key for dependencyOrder type "${entityType}"`);
|
|
4087
|
+
}
|
|
4088
|
+
const def = this.registry.getAll().find((candidate) => candidate.entityType === entityType);
|
|
4089
|
+
if (!def) {
|
|
4090
|
+
throw new Error(`Invalid bundle: unregistered entity type "${entityType}"`);
|
|
4091
|
+
}
|
|
4092
|
+
for (const dependency of def.dependsOn) {
|
|
4093
|
+
const dependencyRegistered = this.registry.getAll().some((candidate) => candidate.entityType === dependency);
|
|
4094
|
+
if (dependencyRegistered && !orderSet.has(dependency)) {
|
|
4095
|
+
throw new Error(
|
|
4096
|
+
`Invalid bundle: missing registered dependency "${dependency}" required by "${entityType}"`
|
|
4097
|
+
);
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
const exportIds = /* @__PURE__ */ new Set();
|
|
4102
|
+
for (const entityType of entityTypes) {
|
|
4103
|
+
const ids = /* @__PURE__ */ new Set();
|
|
4104
|
+
for (const entity of bundle.entities[entityType]) {
|
|
4105
|
+
if (!entity || typeof entity !== "object" || typeof entity._exportId !== "string") {
|
|
4106
|
+
throw new Error(`Invalid bundle: malformed entity in "${entityType}"`);
|
|
4107
|
+
}
|
|
4108
|
+
if (exportIds.has(entity._exportId)) {
|
|
4109
|
+
throw new Error(`Invalid bundle: duplicate _exportId "${entity._exportId}"`);
|
|
4110
|
+
}
|
|
4111
|
+
exportIds.add(entity._exportId);
|
|
4112
|
+
const id = entity.data?.id;
|
|
4113
|
+
if (typeof id !== "string") {
|
|
4114
|
+
throw new Error(`Invalid bundle: entity ID in "${entityType}" must be a string`);
|
|
4115
|
+
}
|
|
4116
|
+
if (ids.has(id)) {
|
|
4117
|
+
throw new Error(`Invalid bundle: duplicate entity ID in "${entityType}": "${id}"`);
|
|
4118
|
+
}
|
|
4119
|
+
ids.add(id);
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4122
|
+
const typeSet = new Set(entityTypes);
|
|
4123
|
+
const canonicalOrder = DependencyResolver.resolveOrder(
|
|
4124
|
+
this.registry.getAll().filter((def) => typeSet.has(def.entityType))
|
|
4125
|
+
);
|
|
4126
|
+
if (canonicalOrder.length !== bundle.dependencyOrder.length || canonicalOrder.some((type, index) => bundle.dependencyOrder[index] !== type)) {
|
|
4127
|
+
throw new Error(
|
|
4128
|
+
`Invalid bundle: dependencyOrder is not canonical; expected ${canonicalOrder.join(", ")}`
|
|
4129
|
+
);
|
|
4130
|
+
}
|
|
4131
|
+
return canonicalOrder;
|
|
4132
|
+
}
|
|
4133
|
+
sanitizeEntityData(data) {
|
|
4134
|
+
const sanitized = { ...data };
|
|
4135
|
+
delete sanitized.tenantId;
|
|
4136
|
+
delete sanitized.createdAt;
|
|
4137
|
+
delete sanitized.updatedAt;
|
|
4138
|
+
return sanitized;
|
|
4139
|
+
}
|
|
4140
|
+
};
|
|
3848
4141
|
|
|
3849
4142
|
// src/controllers/export-import.ts
|
|
3850
4143
|
import { ExportableEntityRegistry as ExportableEntityRegistry2 } from "@axiom-lattice/core";
|
|
3851
4144
|
var service = new ExportImportService();
|
|
3852
|
-
|
|
3853
|
-
|
|
4145
|
+
var TenantScopeError = class extends Error {
|
|
4146
|
+
constructor() {
|
|
4147
|
+
super(...arguments);
|
|
4148
|
+
this.statusCode = 403;
|
|
4149
|
+
}
|
|
4150
|
+
};
|
|
4151
|
+
function resolveTenantId(request, routeTenantId) {
|
|
4152
|
+
const authenticatedTenantId = request.user?.tenantId;
|
|
4153
|
+
const header = request.headers["x-tenant-id"];
|
|
4154
|
+
const headerTenantId = Array.isArray(header) ? header[0] : header;
|
|
4155
|
+
if (authenticatedTenantId) {
|
|
4156
|
+
if (headerTenantId && headerTenantId !== authenticatedTenantId) {
|
|
4157
|
+
throw new TenantScopeError(
|
|
4158
|
+
`Tenant scope mismatch: header tenant "${headerTenantId}" does not match authenticated tenant "${authenticatedTenantId}"`
|
|
4159
|
+
);
|
|
4160
|
+
}
|
|
4161
|
+
if (routeTenantId && routeTenantId !== authenticatedTenantId) {
|
|
4162
|
+
throw new TenantScopeError(
|
|
4163
|
+
`Tenant scope mismatch: route tenant "${routeTenantId}" does not match authenticated tenant "${authenticatedTenantId}"`
|
|
4164
|
+
);
|
|
4165
|
+
}
|
|
4166
|
+
return authenticatedTenantId;
|
|
4167
|
+
}
|
|
4168
|
+
return headerTenantId || routeTenantId || "default";
|
|
4169
|
+
}
|
|
4170
|
+
function getContext(request) {
|
|
4171
|
+
return {
|
|
4172
|
+
workspaceId: request.headers["x-workspace-id"] || void 0,
|
|
4173
|
+
projectId: request.headers["x-project-id"] || void 0
|
|
4174
|
+
};
|
|
4175
|
+
}
|
|
4176
|
+
function safeArchiveFilename(sourceTenantId) {
|
|
4177
|
+
const safeTenantId = sourceTenantId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
4178
|
+
return `tenant-export-${safeTenantId}.tar.gz`;
|
|
4179
|
+
}
|
|
4180
|
+
function assertCompleteFile(file) {
|
|
4181
|
+
if (file.truncated) {
|
|
4182
|
+
throw new Error("Uploaded file was truncated because it exceeds the configured file size limit");
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
function parseResolutions(value) {
|
|
4186
|
+
let parsed = value;
|
|
4187
|
+
if (typeof value === "string") {
|
|
4188
|
+
try {
|
|
4189
|
+
parsed = JSON.parse(value);
|
|
4190
|
+
} catch {
|
|
4191
|
+
throw new Error("resolutions must be valid JSON");
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4195
|
+
throw new Error("resolutions must be a JSON object");
|
|
4196
|
+
}
|
|
4197
|
+
return parsed;
|
|
4198
|
+
}
|
|
4199
|
+
function requireJsonBundle(request, body) {
|
|
4200
|
+
const contentType = request.headers["content-type"]?.split(";", 1)[0]?.trim().toLowerCase();
|
|
4201
|
+
if (contentType !== "application/json" || body.bundle === void 0) {
|
|
4202
|
+
throw new Error('Provide a multipart tar.gz file or an application/json body with "bundle"');
|
|
4203
|
+
}
|
|
4204
|
+
return body.bundle;
|
|
4205
|
+
}
|
|
4206
|
+
async function readPreviewArchive(request) {
|
|
4207
|
+
const part = await request.file();
|
|
4208
|
+
if (!part || part.fieldname !== "file") {
|
|
4209
|
+
throw new Error('Multipart import requires a tar.gz file field named "file"');
|
|
4210
|
+
}
|
|
4211
|
+
const archive = await part.toBuffer();
|
|
4212
|
+
assertCompleteFile(part.file);
|
|
4213
|
+
return archive;
|
|
4214
|
+
}
|
|
4215
|
+
async function readApplyMultipart(request) {
|
|
4216
|
+
let archive;
|
|
4217
|
+
let resolutionsValue;
|
|
4218
|
+
for await (const part of request.parts()) {
|
|
4219
|
+
if (part.type === "file") {
|
|
4220
|
+
if (part.fieldname !== "file" || archive !== void 0) {
|
|
4221
|
+
part.file.resume();
|
|
4222
|
+
throw new Error('Multipart import requires exactly one tar.gz file field named "file"');
|
|
4223
|
+
}
|
|
4224
|
+
archive = await part.toBuffer();
|
|
4225
|
+
assertCompleteFile(part.file);
|
|
4226
|
+
} else if (part.fieldname === "resolutions") {
|
|
4227
|
+
if (resolutionsValue !== void 0) {
|
|
4228
|
+
throw new Error('Multipart import requires exactly one "resolutions" field');
|
|
4229
|
+
}
|
|
4230
|
+
resolutionsValue = part.value;
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
if (!archive) {
|
|
4234
|
+
throw new Error('Multipart import requires a tar.gz file field named "file"');
|
|
4235
|
+
}
|
|
4236
|
+
if (resolutionsValue === void 0) {
|
|
4237
|
+
throw new Error('Multipart import requires a "resolutions" JSON field');
|
|
4238
|
+
}
|
|
4239
|
+
return { archive, resolutions: parseResolutions(resolutionsValue) };
|
|
3854
4240
|
}
|
|
3855
4241
|
async function getExportableTypes(_request, reply) {
|
|
3856
4242
|
const types = service.listExportableTypes();
|
|
3857
4243
|
return reply.send({ success: true, data: types });
|
|
3858
4244
|
}
|
|
3859
4245
|
async function exportConfig(request, reply) {
|
|
3860
|
-
const tenantId =
|
|
4246
|
+
const tenantId = resolveTenantId(request, request.params.tenantId);
|
|
3861
4247
|
const { entityTypes, entityIds } = request.body;
|
|
3862
4248
|
if (!Array.isArray(entityTypes) || entityTypes.length === 0) {
|
|
3863
4249
|
return reply.status(400).send({ success: false, message: "entityTypes must be a non-empty array" });
|
|
3864
4250
|
}
|
|
3865
|
-
const preview = await service.exportPreview(
|
|
4251
|
+
const preview = await service.exportPreview(
|
|
4252
|
+
tenantId,
|
|
4253
|
+
entityTypes,
|
|
4254
|
+
entityIds,
|
|
4255
|
+
getContext(request)
|
|
4256
|
+
);
|
|
3866
4257
|
if (preview.missingDependencies.length > 0 || preview.cascadeAdditions.length > 0) {
|
|
3867
4258
|
return reply.send({
|
|
3868
4259
|
success: true,
|
|
@@ -3873,40 +4264,41 @@ async function exportConfig(request, reply) {
|
|
|
3873
4264
|
}
|
|
3874
4265
|
});
|
|
3875
4266
|
}
|
|
3876
|
-
const { jobId,
|
|
4267
|
+
const { jobId, data } = await service.export(tenantId, entityTypes, entityIds, getContext(request));
|
|
3877
4268
|
return reply.send({
|
|
3878
4269
|
success: true,
|
|
3879
4270
|
data: {
|
|
3880
4271
|
jobId,
|
|
3881
4272
|
summary: Object.fromEntries(
|
|
3882
|
-
Object.entries(bundle.entities).map(([type, entities]) => [type, entities.length])
|
|
4273
|
+
Object.entries(data.bundle.entities).map(([type, entities]) => [type, entities.length])
|
|
3883
4274
|
)
|
|
3884
4275
|
}
|
|
3885
4276
|
});
|
|
3886
4277
|
}
|
|
3887
4278
|
async function exportConfigConfirm(request, reply) {
|
|
3888
|
-
const tenantId =
|
|
4279
|
+
const tenantId = resolveTenantId(request, request.params.tenantId);
|
|
3889
4280
|
const { entityTypes, entityIds } = request.body;
|
|
3890
|
-
const { jobId,
|
|
4281
|
+
const { jobId, data } = await service.export(tenantId, entityTypes, entityIds, getContext(request));
|
|
3891
4282
|
return reply.send({
|
|
3892
4283
|
success: true,
|
|
3893
4284
|
data: {
|
|
3894
4285
|
jobId,
|
|
3895
4286
|
summary: Object.fromEntries(
|
|
3896
|
-
Object.entries(bundle.entities).map(([type, entities]) => [type, entities.length])
|
|
4287
|
+
Object.entries(data.bundle.entities).map(([type, entities]) => [type, entities.length])
|
|
3897
4288
|
)
|
|
3898
4289
|
}
|
|
3899
4290
|
});
|
|
3900
4291
|
}
|
|
3901
4292
|
async function previewEntities(request, reply) {
|
|
3902
|
-
const tenantId =
|
|
4293
|
+
const tenantId = resolveTenantId(request, request.params.tenantId);
|
|
3903
4294
|
const { entityTypes } = request.body;
|
|
4295
|
+
const context = getContext(request);
|
|
3904
4296
|
try {
|
|
3905
4297
|
const registry = ExportableEntityRegistry2.getInstance();
|
|
3906
4298
|
const result = {};
|
|
3907
4299
|
for (const type of entityTypes) {
|
|
3908
4300
|
const def = registry.get(type);
|
|
3909
|
-
const entities = await def.listForExport(tenantId);
|
|
4301
|
+
const entities = await def.listForExport(tenantId, context);
|
|
3910
4302
|
result[type] = entities.map((e) => ({
|
|
3911
4303
|
id: e.data.id,
|
|
3912
4304
|
name: e.data.name || e.data.id,
|
|
@@ -3919,21 +4311,22 @@ async function previewEntities(request, reply) {
|
|
|
3919
4311
|
}
|
|
3920
4312
|
}
|
|
3921
4313
|
async function downloadExport(request, reply) {
|
|
3922
|
-
const tenantId =
|
|
3923
|
-
const
|
|
3924
|
-
if (!
|
|
4314
|
+
const tenantId = resolveTenantId(request, request.params.tenantId);
|
|
4315
|
+
const data = service.getExportJob(request.params.jobId, tenantId);
|
|
4316
|
+
if (!data) {
|
|
3925
4317
|
return reply.status(404).send({ success: false, message: "Export job not found or expired" });
|
|
3926
4318
|
}
|
|
3927
|
-
|
|
4319
|
+
const tarGz = await service.packTarGz(data);
|
|
4320
|
+
const filename = safeArchiveFilename(data.bundle.sourceTenantId);
|
|
4321
|
+
reply.type("application/gzip");
|
|
4322
|
+
reply.header("Content-Disposition", `attachment; filename="${filename}"`);
|
|
4323
|
+
return reply.send(tarGz);
|
|
3928
4324
|
}
|
|
3929
4325
|
async function importPreview(request, reply) {
|
|
3930
|
-
const tenantId =
|
|
3931
|
-
const { bundle } = request.body;
|
|
3932
|
-
if (!bundle) {
|
|
3933
|
-
return reply.status(400).send({ success: false, message: "No bundle provided in request body" });
|
|
3934
|
-
}
|
|
4326
|
+
const tenantId = resolveTenantId(request, request.params.tenantId);
|
|
3935
4327
|
try {
|
|
3936
|
-
const
|
|
4328
|
+
const data = request.isMultipart() ? await service.unpackTarGz(await readPreviewArchive(request)) : { bundle: requireJsonBundle(request, request.body), files: void 0 };
|
|
4329
|
+
const preview = await service.previewImport(tenantId, data.bundle, getContext(request));
|
|
3937
4330
|
return reply.send({ success: true, data: preview });
|
|
3938
4331
|
} catch (err) {
|
|
3939
4332
|
return reply.status(400).send({
|
|
@@ -3943,10 +4336,12 @@ async function importPreview(request, reply) {
|
|
|
3943
4336
|
}
|
|
3944
4337
|
}
|
|
3945
4338
|
async function importApply(request, reply) {
|
|
3946
|
-
const tenantId =
|
|
4339
|
+
const tenantId = resolveTenantId(request, request.params.tenantId);
|
|
3947
4340
|
try {
|
|
3948
|
-
const
|
|
3949
|
-
const
|
|
4341
|
+
const multipart2 = request.isMultipart() ? await readApplyMultipart(request) : void 0;
|
|
4342
|
+
const data = multipart2 ? await service.unpackTarGz(multipart2.archive) : { bundle: requireJsonBundle(request, request.body), files: void 0 };
|
|
4343
|
+
const resolutions = multipart2?.resolutions ?? parseResolutions(request.body.resolutions);
|
|
4344
|
+
const result = await service.applyImport(tenantId, data.bundle, resolutions, data.files, getContext(request));
|
|
3950
4345
|
return reply.send({ success: true, data: result });
|
|
3951
4346
|
} catch (err) {
|
|
3952
4347
|
return reply.status(400).send({
|
|
@@ -4632,43 +5027,15 @@ function registerSandboxProxyRoutes(app2) {
|
|
|
4632
5027
|
// src/controllers/workspace.ts
|
|
4633
5028
|
import * as fs2 from "fs/promises";
|
|
4634
5029
|
import * as path2 from "path";
|
|
4635
|
-
import * as
|
|
4636
|
-
import { getStoreLattice as
|
|
5030
|
+
import * as os2 from "os";
|
|
5031
|
+
import { getStoreLattice as getStoreLattice6 } from "@axiom-lattice/core";
|
|
4637
5032
|
import { SandboxFilesystem, FilesystemBackend } from "@axiom-lattice/core";
|
|
4638
5033
|
import { getSandBoxManager as getSandBoxManager3 } from "@axiom-lattice/core";
|
|
4639
5034
|
import { v4 as uuidv42 } from "uuid";
|
|
4640
|
-
|
|
4641
|
-
// src/utils/path-containment.ts
|
|
4642
|
-
import * as fs from "fs/promises";
|
|
4643
|
-
import * as path from "path";
|
|
4644
|
-
var PathOutsideRootError = class extends Error {
|
|
4645
|
-
constructor(rootPath, targetPath) {
|
|
4646
|
-
super(`Path is outside the allowed root: ${targetPath}`);
|
|
4647
|
-
this.rootPath = rootPath;
|
|
4648
|
-
this.targetPath = targetPath;
|
|
4649
|
-
this.name = "PathOutsideRootError";
|
|
4650
|
-
}
|
|
4651
|
-
};
|
|
4652
|
-
function isPathWithinRoot(rootPath, targetPath) {
|
|
4653
|
-
const relative2 = path.relative(rootPath, targetPath);
|
|
4654
|
-
return relative2 === "" || relative2 !== ".." && !relative2.startsWith(`..${path.sep}`) && !path.isAbsolute(relative2);
|
|
4655
|
-
}
|
|
4656
|
-
async function resolvePathWithinRoot(rootPath, targetPath) {
|
|
4657
|
-
const [resolvedRoot, resolvedTarget] = await Promise.all([
|
|
4658
|
-
fs.realpath(rootPath),
|
|
4659
|
-
fs.realpath(targetPath)
|
|
4660
|
-
]);
|
|
4661
|
-
if (!isPathWithinRoot(resolvedRoot, resolvedTarget)) {
|
|
4662
|
-
throw new PathOutsideRootError(resolvedRoot, resolvedTarget);
|
|
4663
|
-
}
|
|
4664
|
-
return { rootPath: resolvedRoot, targetPath: resolvedTarget };
|
|
4665
|
-
}
|
|
4666
|
-
|
|
4667
|
-
// src/controllers/workspace.ts
|
|
4668
5035
|
var WorkspaceController = class {
|
|
4669
5036
|
constructor() {
|
|
4670
|
-
this.workspaceStore =
|
|
4671
|
-
this.projectStore =
|
|
5037
|
+
this.workspaceStore = getStoreLattice6("default", "workspace").store;
|
|
5038
|
+
this.projectStore = getStoreLattice6("default", "project").store;
|
|
4672
5039
|
}
|
|
4673
5040
|
getTenantId(request) {
|
|
4674
5041
|
const userTenantId = request.user?.tenantId;
|
|
@@ -4740,11 +5107,33 @@ var WorkspaceController = class {
|
|
|
4740
5107
|
async listProjects(request, reply) {
|
|
4741
5108
|
const tenantId = this.getTenantId(request);
|
|
4742
5109
|
const { workspaceId } = request.params;
|
|
4743
|
-
const
|
|
5110
|
+
const kindFilter = request.query.kind;
|
|
5111
|
+
await this.ensureTrainingProject(tenantId, workspaceId);
|
|
5112
|
+
const projects = kindFilter ? await this.projectStore.getProjectsByWorkspace(tenantId, workspaceId, {
|
|
5113
|
+
kind: kindFilter
|
|
5114
|
+
}) : await this.projectStore.getProjectsByWorkspace(tenantId, workspaceId);
|
|
5115
|
+
return { success: true, data: projects };
|
|
5116
|
+
}
|
|
5117
|
+
/**
|
|
5118
|
+
* Ensure at least one training project exists per tenant/workspace.
|
|
5119
|
+
*
|
|
5120
|
+
* Multiple training projects are allowed; this only creates the first one
|
|
5121
|
+
* when none exist so the frontend always has a default to select.
|
|
5122
|
+
*/
|
|
5123
|
+
async ensureTrainingProject(tenantId, workspaceId) {
|
|
5124
|
+
const existing = await this.projectStore.getProjectsByWorkspace(
|
|
4744
5125
|
tenantId,
|
|
4745
|
-
workspaceId
|
|
5126
|
+
workspaceId,
|
|
5127
|
+
{ kind: "training" }
|
|
4746
5128
|
);
|
|
4747
|
-
|
|
5129
|
+
if (existing.length > 0) {
|
|
5130
|
+
return;
|
|
5131
|
+
}
|
|
5132
|
+
await this.projectStore.createProject(tenantId, workspaceId, uuidv42(), {
|
|
5133
|
+
name: "Training",
|
|
5134
|
+
description: "Shared workspace for agent training",
|
|
5135
|
+
kind: "training"
|
|
5136
|
+
});
|
|
4748
5137
|
}
|
|
4749
5138
|
async createProject(request, reply) {
|
|
4750
5139
|
const tenantId = this.getTenantId(request);
|
|
@@ -4784,7 +5173,24 @@ var WorkspaceController = class {
|
|
|
4784
5173
|
}
|
|
4785
5174
|
async deleteProject(request, reply) {
|
|
4786
5175
|
const tenantId = this.getTenantId(request);
|
|
4787
|
-
const { projectId } = request.params;
|
|
5176
|
+
const { workspaceId, projectId } = request.params;
|
|
5177
|
+
const project = await this.projectStore.getProjectById(tenantId, projectId);
|
|
5178
|
+
if (!project) {
|
|
5179
|
+
return reply.status(404).send({ success: false, error: "Project not found" });
|
|
5180
|
+
}
|
|
5181
|
+
if (project.kind === "training") {
|
|
5182
|
+
const training = await this.projectStore.getProjectsByWorkspace(
|
|
5183
|
+
tenantId,
|
|
5184
|
+
workspaceId,
|
|
5185
|
+
{ kind: "training" }
|
|
5186
|
+
);
|
|
5187
|
+
if (training.length <= 1) {
|
|
5188
|
+
return reply.status(409).send({
|
|
5189
|
+
success: false,
|
|
5190
|
+
error: "The last training project cannot be deleted"
|
|
5191
|
+
});
|
|
5192
|
+
}
|
|
5193
|
+
}
|
|
4788
5194
|
const deleted = await this.projectStore.deleteProject(tenantId, projectId);
|
|
4789
5195
|
if (!deleted) {
|
|
4790
5196
|
return reply.status(404).send({ success: false, error: "Project not found" });
|
|
@@ -4826,7 +5232,7 @@ var WorkspaceController = class {
|
|
|
4826
5232
|
workspace
|
|
4827
5233
|
};
|
|
4828
5234
|
}
|
|
4829
|
-
const rootDir =
|
|
5235
|
+
const rootDir = filesystemRootDirectory(
|
|
4830
5236
|
tenantId,
|
|
4831
5237
|
workspaceId,
|
|
4832
5238
|
projectId
|
|
@@ -4840,41 +5246,6 @@ var WorkspaceController = class {
|
|
|
4840
5246
|
workspace
|
|
4841
5247
|
};
|
|
4842
5248
|
}
|
|
4843
|
-
async getFilesystemRootDirectory(tenantId, workspaceId, projectId) {
|
|
4844
|
-
return `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`;
|
|
4845
|
-
}
|
|
4846
|
-
isBinaryContentType(filename) {
|
|
4847
|
-
const ext = filename.split(".").pop()?.toLowerCase() || "";
|
|
4848
|
-
const binaryExtensions = /* @__PURE__ */ new Set([
|
|
4849
|
-
"pdf",
|
|
4850
|
-
"xlsx",
|
|
4851
|
-
"xls",
|
|
4852
|
-
"docx",
|
|
4853
|
-
"doc",
|
|
4854
|
-
"pptx",
|
|
4855
|
-
"ppt",
|
|
4856
|
-
"png",
|
|
4857
|
-
"jpg",
|
|
4858
|
-
"jpeg",
|
|
4859
|
-
"gif",
|
|
4860
|
-
"bmp",
|
|
4861
|
-
"ico",
|
|
4862
|
-
"webp",
|
|
4863
|
-
"mp3",
|
|
4864
|
-
"mp4",
|
|
4865
|
-
"webm",
|
|
4866
|
-
"ogg",
|
|
4867
|
-
"wav",
|
|
4868
|
-
"avi",
|
|
4869
|
-
"mov",
|
|
4870
|
-
"zip",
|
|
4871
|
-
"tar",
|
|
4872
|
-
"gz",
|
|
4873
|
-
"rar",
|
|
4874
|
-
"7z"
|
|
4875
|
-
]);
|
|
4876
|
-
return binaryExtensions.has(ext);
|
|
4877
|
-
}
|
|
4878
5249
|
async downloadFile(request, reply) {
|
|
4879
5250
|
const tenantId = this.getTenantId(request);
|
|
4880
5251
|
const { workspaceId, projectId } = request.params;
|
|
@@ -4951,7 +5322,7 @@ var WorkspaceController = class {
|
|
|
4951
5322
|
projectId
|
|
4952
5323
|
};
|
|
4953
5324
|
const filename2 = getFilenameFromPath(resolvedPath);
|
|
4954
|
-
const isBinary =
|
|
5325
|
+
const isBinary = isBinaryContentType(filename2);
|
|
4955
5326
|
const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, resolvedPath);
|
|
4956
5327
|
let buf;
|
|
4957
5328
|
if (volumeBackend) {
|
|
@@ -5082,80 +5453,29 @@ var WorkspaceController = class {
|
|
|
5082
5453
|
if (pathValue && !/^[a-zA-Z0-9_./~-]+$/.test(pathValue)) {
|
|
5083
5454
|
return reply.status(400).send({ success: false, error: "Invalid path parameter" });
|
|
5084
5455
|
}
|
|
5085
|
-
|
|
5086
|
-
const sandboxManager = getSandBoxManager3();
|
|
5087
|
-
const volumeConfig = {
|
|
5088
|
-
assistant_id: assistantId || "",
|
|
5089
|
-
thread_id: "",
|
|
5090
|
-
tenantId,
|
|
5091
|
-
workspaceId,
|
|
5092
|
-
projectId
|
|
5093
|
-
};
|
|
5094
|
-
const realPath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
|
|
5095
|
-
const isBinary = this.isBinaryContentType(filename);
|
|
5096
|
-
const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, realPath);
|
|
5097
|
-
if (volumeBackend && !isBinary) {
|
|
5098
|
-
await volumeBackend.write(realPath, buffer.toString("utf-8"));
|
|
5099
|
-
} else if (volumeBackend && volumeBackend.writeBinary) {
|
|
5100
|
-
await volumeBackend.writeBinary(realPath, buffer);
|
|
5101
|
-
} else {
|
|
5102
|
-
const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
|
|
5103
|
-
await sandbox.file.uploadFile({ file: realPath, data: buffer });
|
|
5104
|
-
}
|
|
5105
|
-
const relativePath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
|
|
5106
|
-
const result2 = {
|
|
5107
|
-
path: relativePath,
|
|
5108
|
-
name: filename,
|
|
5109
|
-
is_dir: false,
|
|
5110
|
-
size: buffer.length,
|
|
5111
|
-
modified_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5112
|
-
};
|
|
5113
|
-
return reply.status(200).send({ success: true, data: result2 });
|
|
5114
|
-
}
|
|
5115
|
-
const rootDir = await this.getFilesystemRootDirectory(
|
|
5456
|
+
const saved = await saveProjectFile({
|
|
5116
5457
|
tenantId,
|
|
5117
5458
|
workspaceId,
|
|
5118
|
-
projectId
|
|
5119
|
-
);
|
|
5120
|
-
await fs2.mkdir(rootDir, { recursive: true });
|
|
5121
|
-
const relativeDirectory = pathValue?.replace(/^[/\\]+/, "") || ".";
|
|
5122
|
-
const targetDir = path2.resolve(rootDir, relativeDirectory);
|
|
5123
|
-
if (!isPathWithinRoot(path2.resolve(rootDir), targetDir)) {
|
|
5124
|
-
return reply.status(400).send({
|
|
5125
|
-
success: false,
|
|
5126
|
-
error: "Upload path must stay within the project directory"
|
|
5127
|
-
});
|
|
5128
|
-
}
|
|
5129
|
-
await fs2.mkdir(targetDir, { recursive: true });
|
|
5130
|
-
const [resolvedRoot, resolvedTargetDir] = await Promise.all([
|
|
5131
|
-
fs2.realpath(rootDir),
|
|
5132
|
-
fs2.realpath(targetDir)
|
|
5133
|
-
]);
|
|
5134
|
-
if (!isPathWithinRoot(resolvedRoot, resolvedTargetDir)) {
|
|
5135
|
-
return reply.status(400).send({
|
|
5136
|
-
success: false,
|
|
5137
|
-
error: "Upload path must stay within the project directory"
|
|
5138
|
-
});
|
|
5139
|
-
}
|
|
5140
|
-
const safeFilename = path2.basename(filename);
|
|
5141
|
-
if (safeFilename !== filename) {
|
|
5142
|
-
return reply.status(400).send({
|
|
5143
|
-
success: false,
|
|
5144
|
-
error: "Invalid filename"
|
|
5145
|
-
});
|
|
5146
|
-
}
|
|
5147
|
-
const targetPath = path2.join(resolvedTargetDir, safeFilename);
|
|
5148
|
-
await fs2.writeFile(targetPath, buffer);
|
|
5149
|
-
const stat2 = await fs2.stat(targetPath);
|
|
5150
|
-
const result = {
|
|
5151
|
-
path: path2.posix.join("/", relativeDirectory, safeFilename),
|
|
5459
|
+
projectId,
|
|
5152
5460
|
name: filename,
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
};
|
|
5157
|
-
return reply.status(200).send({
|
|
5461
|
+
bytes: buffer,
|
|
5462
|
+
...pathValue ? { path: pathValue } : {},
|
|
5463
|
+
assistantId
|
|
5464
|
+
});
|
|
5465
|
+
return reply.status(200).send({
|
|
5466
|
+
success: true,
|
|
5467
|
+
data: {
|
|
5468
|
+
path: saved.uri,
|
|
5469
|
+
name: saved.name,
|
|
5470
|
+
is_dir: false,
|
|
5471
|
+
size: saved.size,
|
|
5472
|
+
modified_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5473
|
+
}
|
|
5474
|
+
});
|
|
5158
5475
|
} catch (error) {
|
|
5476
|
+
if (error instanceof ProjectFileValidationError) {
|
|
5477
|
+
return reply.status(400).send({ success: false, error: error.message });
|
|
5478
|
+
}
|
|
5159
5479
|
const message = error instanceof Error ? error.message : String(error);
|
|
5160
5480
|
return reply.status(500).send({
|
|
5161
5481
|
success: false,
|
|
@@ -5172,7 +5492,7 @@ var WorkspaceController = class {
|
|
|
5172
5492
|
* - Returns only directory names, no file contents or hidden files
|
|
5173
5493
|
*/
|
|
5174
5494
|
async browseFilesystem(request, reply) {
|
|
5175
|
-
const homeDir =
|
|
5495
|
+
const homeDir = os2.homedir();
|
|
5176
5496
|
const requestedPath = request.query.path || homeDir;
|
|
5177
5497
|
const SYSTEM_BLACKLIST = ["/etc", "/sys", "/proc", "/var", "/tmp", "/dev"];
|
|
5178
5498
|
try {
|
|
@@ -5281,11 +5601,11 @@ function registerWorkspaceRoutes(app2) {
|
|
|
5281
5601
|
|
|
5282
5602
|
// src/controllers/database-configs.ts
|
|
5283
5603
|
import {
|
|
5284
|
-
getStoreLattice as
|
|
5604
|
+
getStoreLattice as getStoreLattice7,
|
|
5285
5605
|
sqlDatabaseManager
|
|
5286
5606
|
} from "@axiom-lattice/core";
|
|
5287
5607
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
5288
|
-
function
|
|
5608
|
+
function getTenantId9(request) {
|
|
5289
5609
|
const userTenantId = request.user?.tenantId;
|
|
5290
5610
|
if (userTenantId) {
|
|
5291
5611
|
return userTenantId;
|
|
@@ -5293,9 +5613,9 @@ function getTenantId11(request) {
|
|
|
5293
5613
|
return request.headers["x-tenant-id"] || "default";
|
|
5294
5614
|
}
|
|
5295
5615
|
async function getDatabaseConfigList(request, reply) {
|
|
5296
|
-
const tenantId =
|
|
5616
|
+
const tenantId = getTenantId9(request);
|
|
5297
5617
|
try {
|
|
5298
|
-
const storeLattice =
|
|
5618
|
+
const storeLattice = getStoreLattice7("default", "database");
|
|
5299
5619
|
const store = storeLattice.store;
|
|
5300
5620
|
const configs = await store.getAllConfigs(tenantId);
|
|
5301
5621
|
console.log("Backend: getAllConfigs returned:", configs);
|
|
@@ -5323,10 +5643,10 @@ async function getDatabaseConfigList(request, reply) {
|
|
|
5323
5643
|
}
|
|
5324
5644
|
}
|
|
5325
5645
|
async function getDatabaseConfig(request, reply) {
|
|
5326
|
-
const tenantId =
|
|
5646
|
+
const tenantId = getTenantId9(request);
|
|
5327
5647
|
const { key } = request.params;
|
|
5328
5648
|
try {
|
|
5329
|
-
const storeLattice =
|
|
5649
|
+
const storeLattice = getStoreLattice7("default", "database");
|
|
5330
5650
|
const store = storeLattice.store;
|
|
5331
5651
|
const config = await store.getConfigByKey(tenantId, key);
|
|
5332
5652
|
if (!config) {
|
|
@@ -5349,10 +5669,10 @@ async function getDatabaseConfig(request, reply) {
|
|
|
5349
5669
|
}
|
|
5350
5670
|
}
|
|
5351
5671
|
async function createDatabaseConfig(request, reply) {
|
|
5352
|
-
const tenantId =
|
|
5672
|
+
const tenantId = getTenantId9(request);
|
|
5353
5673
|
const body = request.body;
|
|
5354
5674
|
try {
|
|
5355
|
-
const storeLattice =
|
|
5675
|
+
const storeLattice = getStoreLattice7("default", "database");
|
|
5356
5676
|
const store = storeLattice.store;
|
|
5357
5677
|
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
5358
5678
|
if (existing) {
|
|
@@ -5384,11 +5704,11 @@ async function createDatabaseConfig(request, reply) {
|
|
|
5384
5704
|
}
|
|
5385
5705
|
}
|
|
5386
5706
|
async function updateDatabaseConfig(request, reply) {
|
|
5387
|
-
const tenantId =
|
|
5707
|
+
const tenantId = getTenantId9(request);
|
|
5388
5708
|
const { key } = request.params;
|
|
5389
5709
|
const updates = request.body;
|
|
5390
5710
|
try {
|
|
5391
|
-
const storeLattice =
|
|
5711
|
+
const storeLattice = getStoreLattice7("default", "database");
|
|
5392
5712
|
const store = storeLattice.store;
|
|
5393
5713
|
const existing = await store.getConfigByKey(tenantId, key);
|
|
5394
5714
|
if (!existing) {
|
|
@@ -5426,10 +5746,10 @@ async function updateDatabaseConfig(request, reply) {
|
|
|
5426
5746
|
}
|
|
5427
5747
|
}
|
|
5428
5748
|
async function deleteDatabaseConfig(request, reply) {
|
|
5429
|
-
const tenantId =
|
|
5749
|
+
const tenantId = getTenantId9(request);
|
|
5430
5750
|
const { keyOrId } = request.params;
|
|
5431
5751
|
try {
|
|
5432
|
-
const storeLattice =
|
|
5752
|
+
const storeLattice = getStoreLattice7("default", "database");
|
|
5433
5753
|
const store = storeLattice.store;
|
|
5434
5754
|
console.log("Delete request - keyOrId:", keyOrId);
|
|
5435
5755
|
let config = await store.getConfigByKey(tenantId, keyOrId);
|
|
@@ -5475,10 +5795,10 @@ async function deleteDatabaseConfig(request, reply) {
|
|
|
5475
5795
|
}
|
|
5476
5796
|
}
|
|
5477
5797
|
async function testDatabaseConnection(request, reply) {
|
|
5478
|
-
const tenantId =
|
|
5798
|
+
const tenantId = getTenantId9(request);
|
|
5479
5799
|
const { key } = request.params;
|
|
5480
5800
|
try {
|
|
5481
|
-
const storeLattice =
|
|
5801
|
+
const storeLattice = getStoreLattice7("default", "database");
|
|
5482
5802
|
const store = storeLattice.store;
|
|
5483
5803
|
const config = await store.getConfigByKey(tenantId, key);
|
|
5484
5804
|
if (!config) {
|
|
@@ -5496,7 +5816,7 @@ async function testDatabaseConnection(request, reply) {
|
|
|
5496
5816
|
await db.connect();
|
|
5497
5817
|
await db.listTables();
|
|
5498
5818
|
const latency = Date.now() - startTime;
|
|
5499
|
-
await new Promise((
|
|
5819
|
+
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
5500
5820
|
await db.disconnect();
|
|
5501
5821
|
await sqlDatabaseManager.removeDatabase(tenantId, testKey);
|
|
5502
5822
|
return {
|
|
@@ -5563,12 +5883,12 @@ function registerDatabaseConfigRoutes(app2) {
|
|
|
5563
5883
|
|
|
5564
5884
|
// src/controllers/metrics-configs.ts
|
|
5565
5885
|
import {
|
|
5566
|
-
getStoreLattice as
|
|
5886
|
+
getStoreLattice as getStoreLattice8,
|
|
5567
5887
|
metricsServerManager as metricsServerManager2,
|
|
5568
5888
|
SemanticMetricsClient as SemanticMetricsClient2
|
|
5569
5889
|
} from "@axiom-lattice/core";
|
|
5570
5890
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
5571
|
-
function
|
|
5891
|
+
function getTenantId10(request) {
|
|
5572
5892
|
const userTenantId = request.user?.tenantId;
|
|
5573
5893
|
if (userTenantId) {
|
|
5574
5894
|
return userTenantId;
|
|
@@ -5576,9 +5896,9 @@ function getTenantId12(request) {
|
|
|
5576
5896
|
return request.headers["x-tenant-id"] || "default";
|
|
5577
5897
|
}
|
|
5578
5898
|
async function getMetricsServerConfigList(request, reply) {
|
|
5579
|
-
const tenantId =
|
|
5899
|
+
const tenantId = getTenantId10(request);
|
|
5580
5900
|
try {
|
|
5581
|
-
const storeLattice =
|
|
5901
|
+
const storeLattice = getStoreLattice8("default", "metrics");
|
|
5582
5902
|
const store = storeLattice.store;
|
|
5583
5903
|
const configs = await store.getAllConfigs(tenantId);
|
|
5584
5904
|
return {
|
|
@@ -5602,10 +5922,10 @@ async function getMetricsServerConfigList(request, reply) {
|
|
|
5602
5922
|
}
|
|
5603
5923
|
}
|
|
5604
5924
|
async function getMetricsServerConfig(request, reply) {
|
|
5605
|
-
const tenantId =
|
|
5925
|
+
const tenantId = getTenantId10(request);
|
|
5606
5926
|
const { key } = request.params;
|
|
5607
5927
|
try {
|
|
5608
|
-
const storeLattice =
|
|
5928
|
+
const storeLattice = getStoreLattice8("default", "metrics");
|
|
5609
5929
|
const store = storeLattice.store;
|
|
5610
5930
|
const config = await store.getConfigByKey(tenantId, key);
|
|
5611
5931
|
if (!config) {
|
|
@@ -5628,10 +5948,10 @@ async function getMetricsServerConfig(request, reply) {
|
|
|
5628
5948
|
}
|
|
5629
5949
|
}
|
|
5630
5950
|
async function createMetricsServerConfig(request, reply) {
|
|
5631
|
-
const tenantId =
|
|
5951
|
+
const tenantId = getTenantId10(request);
|
|
5632
5952
|
const body = request.body;
|
|
5633
5953
|
try {
|
|
5634
|
-
const storeLattice =
|
|
5954
|
+
const storeLattice = getStoreLattice8("default", "metrics");
|
|
5635
5955
|
const store = storeLattice.store;
|
|
5636
5956
|
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
5637
5957
|
if (existing) {
|
|
@@ -5679,11 +5999,11 @@ async function createMetricsServerConfig(request, reply) {
|
|
|
5679
5999
|
}
|
|
5680
6000
|
}
|
|
5681
6001
|
async function updateMetricsServerConfig(request, reply) {
|
|
5682
|
-
const tenantId =
|
|
6002
|
+
const tenantId = getTenantId10(request);
|
|
5683
6003
|
const { key } = request.params;
|
|
5684
6004
|
const updates = request.body;
|
|
5685
6005
|
try {
|
|
5686
|
-
const storeLattice =
|
|
6006
|
+
const storeLattice = getStoreLattice8("default", "metrics");
|
|
5687
6007
|
const store = storeLattice.store;
|
|
5688
6008
|
const existing = await store.getConfigByKey(tenantId, key);
|
|
5689
6009
|
if (!existing) {
|
|
@@ -5730,10 +6050,10 @@ async function updateMetricsServerConfig(request, reply) {
|
|
|
5730
6050
|
}
|
|
5731
6051
|
}
|
|
5732
6052
|
async function deleteMetricsServerConfig(request, reply) {
|
|
5733
|
-
const tenantId =
|
|
6053
|
+
const tenantId = getTenantId10(request);
|
|
5734
6054
|
const { keyOrId } = request.params;
|
|
5735
6055
|
try {
|
|
5736
|
-
const storeLattice =
|
|
6056
|
+
const storeLattice = getStoreLattice8("default", "metrics");
|
|
5737
6057
|
const store = storeLattice.store;
|
|
5738
6058
|
let config = await store.getConfigByKey(tenantId, keyOrId);
|
|
5739
6059
|
let configKey = keyOrId;
|
|
@@ -5777,10 +6097,10 @@ async function deleteMetricsServerConfig(request, reply) {
|
|
|
5777
6097
|
}
|
|
5778
6098
|
}
|
|
5779
6099
|
async function testMetricsServerConnection(request, reply) {
|
|
5780
|
-
const tenantId =
|
|
6100
|
+
const tenantId = getTenantId10(request);
|
|
5781
6101
|
const { key } = request.params;
|
|
5782
6102
|
try {
|
|
5783
|
-
const storeLattice =
|
|
6103
|
+
const storeLattice = getStoreLattice8("default", "metrics");
|
|
5784
6104
|
const store = storeLattice.store;
|
|
5785
6105
|
const config = await store.getConfigByKey(tenantId, key);
|
|
5786
6106
|
if (!config) {
|
|
@@ -5828,10 +6148,10 @@ async function testMetricsServerConnection(request, reply) {
|
|
|
5828
6148
|
}
|
|
5829
6149
|
}
|
|
5830
6150
|
async function listAvailableMetrics(request, reply) {
|
|
5831
|
-
const tenantId =
|
|
6151
|
+
const tenantId = getTenantId10(request);
|
|
5832
6152
|
const { key } = request.params;
|
|
5833
6153
|
try {
|
|
5834
|
-
const storeLattice =
|
|
6154
|
+
const storeLattice = getStoreLattice8("default", "metrics");
|
|
5835
6155
|
const store = storeLattice.store;
|
|
5836
6156
|
const config = await store.getConfigByKey(tenantId, key);
|
|
5837
6157
|
if (!config) {
|
|
@@ -5866,11 +6186,11 @@ async function listAvailableMetrics(request, reply) {
|
|
|
5866
6186
|
}
|
|
5867
6187
|
}
|
|
5868
6188
|
async function queryMetricsData(request, reply) {
|
|
5869
|
-
const tenantId =
|
|
6189
|
+
const tenantId = getTenantId10(request);
|
|
5870
6190
|
const { key } = request.params;
|
|
5871
6191
|
const { metricName, startTime, endTime, step, labels } = request.body;
|
|
5872
6192
|
try {
|
|
5873
|
-
const storeLattice =
|
|
6193
|
+
const storeLattice = getStoreLattice8("default", "metrics");
|
|
5874
6194
|
const store = storeLattice.store;
|
|
5875
6195
|
const config = await store.getConfigByKey(tenantId, key);
|
|
5876
6196
|
if (!config) {
|
|
@@ -5914,10 +6234,10 @@ async function queryMetricsData(request, reply) {
|
|
|
5914
6234
|
}
|
|
5915
6235
|
}
|
|
5916
6236
|
async function getDataSources(request, reply) {
|
|
5917
|
-
const tenantId =
|
|
6237
|
+
const tenantId = getTenantId10(request);
|
|
5918
6238
|
const { key } = request.params;
|
|
5919
6239
|
try {
|
|
5920
|
-
const storeLattice =
|
|
6240
|
+
const storeLattice = getStoreLattice8("default", "metrics");
|
|
5921
6241
|
const store = storeLattice.store;
|
|
5922
6242
|
const config = await store.getConfigByKey(tenantId, key);
|
|
5923
6243
|
if (!config) {
|
|
@@ -5955,10 +6275,10 @@ async function getDataSources(request, reply) {
|
|
|
5955
6275
|
}
|
|
5956
6276
|
}
|
|
5957
6277
|
async function getDatasourceMetrics(request, reply) {
|
|
5958
|
-
const tenantId =
|
|
6278
|
+
const tenantId = getTenantId10(request);
|
|
5959
6279
|
const { key, datasourceId } = request.params;
|
|
5960
6280
|
try {
|
|
5961
|
-
const storeLattice =
|
|
6281
|
+
const storeLattice = getStoreLattice8("default", "metrics");
|
|
5962
6282
|
const store = storeLattice.store;
|
|
5963
6283
|
const config = await store.getConfigByKey(tenantId, key);
|
|
5964
6284
|
if (!config) {
|
|
@@ -5992,11 +6312,11 @@ async function getDatasourceMetrics(request, reply) {
|
|
|
5992
6312
|
}
|
|
5993
6313
|
}
|
|
5994
6314
|
async function querySemanticMetrics(request, reply) {
|
|
5995
|
-
const tenantId =
|
|
6315
|
+
const tenantId = getTenantId10(request);
|
|
5996
6316
|
const { key } = request.params;
|
|
5997
6317
|
const body = request.body;
|
|
5998
6318
|
try {
|
|
5999
|
-
const storeLattice =
|
|
6319
|
+
const storeLattice = getStoreLattice8("default", "metrics");
|
|
6000
6320
|
const store = storeLattice.store;
|
|
6001
6321
|
const config = await store.getConfigByKey(tenantId, key);
|
|
6002
6322
|
if (!config) {
|
|
@@ -6307,13 +6627,13 @@ function connectionRoutes(fastify2) {
|
|
|
6307
6627
|
}
|
|
6308
6628
|
|
|
6309
6629
|
// src/controllers/eval.ts
|
|
6310
|
-
import { getStoreLattice as
|
|
6630
|
+
import { getStoreLattice as getStoreLattice10 } from "@axiom-lattice/core";
|
|
6311
6631
|
import { v4 as uuidv44 } from "uuid";
|
|
6312
6632
|
|
|
6313
6633
|
// src/services/eval-runner.ts
|
|
6314
6634
|
import { EventEmitter } from "events";
|
|
6315
6635
|
import {
|
|
6316
|
-
getStoreLattice as
|
|
6636
|
+
getStoreLattice as getStoreLattice9,
|
|
6317
6637
|
modelLatticeManager as modelLatticeManager2,
|
|
6318
6638
|
LatticeEvalProject
|
|
6319
6639
|
} from "@axiom-lattice/core";
|
|
@@ -6371,21 +6691,21 @@ var EvalRunner = class {
|
|
|
6371
6691
|
for (const suite of suites) {
|
|
6372
6692
|
let cases = await store.getCasesBySuite(tenantId, suite.id);
|
|
6373
6693
|
if (caseIds && caseIds.length > 0) {
|
|
6374
|
-
cases = cases.filter((
|
|
6694
|
+
cases = cases.filter((c2) => caseIds.includes(c2.id));
|
|
6375
6695
|
if (cases.length === 0) continue;
|
|
6376
6696
|
}
|
|
6377
6697
|
totalCases += cases.length;
|
|
6378
6698
|
evalSuites.push({
|
|
6379
6699
|
suiteName: suite.name,
|
|
6380
|
-
cases: cases.map((
|
|
6381
|
-
caseId:
|
|
6382
|
-
input: { message:
|
|
6383
|
-
steps:
|
|
6700
|
+
cases: cases.map((c2) => ({
|
|
6701
|
+
caseId: c2.id,
|
|
6702
|
+
input: { message: c2.inputMessage, files: c2.inputFiles },
|
|
6703
|
+
steps: c2.steps,
|
|
6384
6704
|
output: { type: "message_content" },
|
|
6385
|
-
interruptPolicy:
|
|
6705
|
+
interruptPolicy: c2.interruptPolicy,
|
|
6386
6706
|
eval: {
|
|
6387
|
-
content_assertion:
|
|
6388
|
-
eval_rubrics:
|
|
6707
|
+
content_assertion: c2.contentAssertion,
|
|
6708
|
+
eval_rubrics: c2.rubrics?.map((r) => ({
|
|
6389
6709
|
dimension: r.name,
|
|
6390
6710
|
weight: r.weight,
|
|
6391
6711
|
description: r.description
|
|
@@ -6557,13 +6877,13 @@ var EvalRunner = class {
|
|
|
6557
6877
|
return this.runs.has(runId);
|
|
6558
6878
|
}
|
|
6559
6879
|
getEvalStore() {
|
|
6560
|
-
return
|
|
6880
|
+
return getStoreLattice9("default", "eval").store;
|
|
6561
6881
|
}
|
|
6562
6882
|
};
|
|
6563
6883
|
var evalRunner = new EvalRunner();
|
|
6564
6884
|
|
|
6565
6885
|
// src/controllers/eval.ts
|
|
6566
|
-
function
|
|
6886
|
+
function getTenantId11(request) {
|
|
6567
6887
|
const userTenantId = request.user?.tenantId;
|
|
6568
6888
|
if (userTenantId) {
|
|
6569
6889
|
return userTenantId;
|
|
@@ -6571,11 +6891,11 @@ function getTenantId13(request) {
|
|
|
6571
6891
|
return request.headers["x-tenant-id"] || "default";
|
|
6572
6892
|
}
|
|
6573
6893
|
function getEvalStore() {
|
|
6574
|
-
return
|
|
6894
|
+
return getStoreLattice10("default", "eval").store;
|
|
6575
6895
|
}
|
|
6576
6896
|
async function createProject(request, reply) {
|
|
6577
6897
|
try {
|
|
6578
|
-
const tenantId =
|
|
6898
|
+
const tenantId = getTenantId11(request);
|
|
6579
6899
|
const store = getEvalStore();
|
|
6580
6900
|
const id = uuidv44();
|
|
6581
6901
|
const data = request.body;
|
|
@@ -6632,7 +6952,7 @@ async function createProject(request, reply) {
|
|
|
6632
6952
|
}
|
|
6633
6953
|
async function listProjects(request, reply) {
|
|
6634
6954
|
try {
|
|
6635
|
-
const tenantId =
|
|
6955
|
+
const tenantId = getTenantId11(request);
|
|
6636
6956
|
const store = getEvalStore();
|
|
6637
6957
|
let projects = await store.getProjectsByTenant(tenantId);
|
|
6638
6958
|
if (projects.length === 0) {
|
|
@@ -6666,7 +6986,7 @@ async function listProjects(request, reply) {
|
|
|
6666
6986
|
}
|
|
6667
6987
|
async function getProject(request, reply) {
|
|
6668
6988
|
try {
|
|
6669
|
-
const tenantId =
|
|
6989
|
+
const tenantId = getTenantId11(request);
|
|
6670
6990
|
const store = getEvalStore();
|
|
6671
6991
|
const { pid } = request.params;
|
|
6672
6992
|
const project = await store.getProjectById(tenantId, pid);
|
|
@@ -6686,7 +7006,7 @@ async function getProject(request, reply) {
|
|
|
6686
7006
|
}
|
|
6687
7007
|
async function updateProject(request, reply) {
|
|
6688
7008
|
try {
|
|
6689
|
-
const tenantId =
|
|
7009
|
+
const tenantId = getTenantId11(request);
|
|
6690
7010
|
const store = getEvalStore();
|
|
6691
7011
|
const { pid } = request.params;
|
|
6692
7012
|
const existing = await store.getProjectById(tenantId, pid);
|
|
@@ -6729,7 +7049,7 @@ async function updateProject(request, reply) {
|
|
|
6729
7049
|
}
|
|
6730
7050
|
async function deleteProject(request, reply) {
|
|
6731
7051
|
try {
|
|
6732
|
-
const tenantId =
|
|
7052
|
+
const tenantId = getTenantId11(request);
|
|
6733
7053
|
const store = getEvalStore();
|
|
6734
7054
|
const { pid } = request.params;
|
|
6735
7055
|
const existing = await store.getProjectById(tenantId, pid);
|
|
@@ -6748,7 +7068,7 @@ async function deleteProject(request, reply) {
|
|
|
6748
7068
|
}
|
|
6749
7069
|
async function createSuite(request, reply) {
|
|
6750
7070
|
try {
|
|
6751
|
-
const tenantId =
|
|
7071
|
+
const tenantId = getTenantId11(request);
|
|
6752
7072
|
const store = getEvalStore();
|
|
6753
7073
|
const { pid } = request.params;
|
|
6754
7074
|
const project = await store.getProjectById(tenantId, pid);
|
|
@@ -6770,7 +7090,7 @@ async function createSuite(request, reply) {
|
|
|
6770
7090
|
}
|
|
6771
7091
|
async function updateSuite(request, reply) {
|
|
6772
7092
|
try {
|
|
6773
|
-
const tenantId =
|
|
7093
|
+
const tenantId = getTenantId11(request);
|
|
6774
7094
|
const store = getEvalStore();
|
|
6775
7095
|
const { sid } = request.params;
|
|
6776
7096
|
const existing = await store.getSuiteById(tenantId, sid);
|
|
@@ -6790,7 +7110,7 @@ async function updateSuite(request, reply) {
|
|
|
6790
7110
|
}
|
|
6791
7111
|
async function deleteSuite(request, reply) {
|
|
6792
7112
|
try {
|
|
6793
|
-
const tenantId =
|
|
7113
|
+
const tenantId = getTenantId11(request);
|
|
6794
7114
|
const store = getEvalStore();
|
|
6795
7115
|
const { sid } = request.params;
|
|
6796
7116
|
const existing = await store.getSuiteById(tenantId, sid);
|
|
@@ -6809,7 +7129,7 @@ async function deleteSuite(request, reply) {
|
|
|
6809
7129
|
}
|
|
6810
7130
|
async function createCase(request, reply) {
|
|
6811
7131
|
try {
|
|
6812
|
-
const tenantId =
|
|
7132
|
+
const tenantId = getTenantId11(request);
|
|
6813
7133
|
const store = getEvalStore();
|
|
6814
7134
|
const { sid } = request.params;
|
|
6815
7135
|
const suite = await store.getSuiteById(tenantId, sid);
|
|
@@ -6839,7 +7159,7 @@ async function createCase(request, reply) {
|
|
|
6839
7159
|
}
|
|
6840
7160
|
async function listCasesForSuite(request, reply) {
|
|
6841
7161
|
try {
|
|
6842
|
-
const tenantId =
|
|
7162
|
+
const tenantId = getTenantId11(request);
|
|
6843
7163
|
const store = getEvalStore();
|
|
6844
7164
|
const { sid } = request.params;
|
|
6845
7165
|
const cases = await store.getCasesBySuite(tenantId, sid);
|
|
@@ -6855,7 +7175,7 @@ async function listCasesForSuite(request, reply) {
|
|
|
6855
7175
|
}
|
|
6856
7176
|
async function updateCase(request, reply) {
|
|
6857
7177
|
try {
|
|
6858
|
-
const tenantId =
|
|
7178
|
+
const tenantId = getTenantId11(request);
|
|
6859
7179
|
const store = getEvalStore();
|
|
6860
7180
|
const { cid } = request.params;
|
|
6861
7181
|
const existing = await store.getCaseById(tenantId, cid);
|
|
@@ -6875,7 +7195,7 @@ async function updateCase(request, reply) {
|
|
|
6875
7195
|
}
|
|
6876
7196
|
async function deleteCase(request, reply) {
|
|
6877
7197
|
try {
|
|
6878
|
-
const tenantId =
|
|
7198
|
+
const tenantId = getTenantId11(request);
|
|
6879
7199
|
const store = getEvalStore();
|
|
6880
7200
|
const { cid } = request.params;
|
|
6881
7201
|
const existing = await store.getCaseById(tenantId, cid);
|
|
@@ -6907,7 +7227,7 @@ function registerEvalRoutes(app2) {
|
|
|
6907
7227
|
app2.delete("/api/eval/projects/:pid/suites/:sid/cases/:cid", deleteCase);
|
|
6908
7228
|
app2.post("/api/eval/projects/:pid/runs", async (request, reply) => {
|
|
6909
7229
|
try {
|
|
6910
|
-
const tenantId =
|
|
7230
|
+
const tenantId = getTenantId11(request);
|
|
6911
7231
|
const { pid } = request.params;
|
|
6912
7232
|
const body = request.body ?? {};
|
|
6913
7233
|
const bodyWs = request.headers["x-workspace-id"] || "";
|
|
@@ -6926,7 +7246,7 @@ function registerEvalRoutes(app2) {
|
|
|
6926
7246
|
});
|
|
6927
7247
|
app2.get("/api/eval/runs", async (request, reply) => {
|
|
6928
7248
|
try {
|
|
6929
|
-
const tenantId =
|
|
7249
|
+
const tenantId = getTenantId11(request);
|
|
6930
7250
|
const query = request.query;
|
|
6931
7251
|
const runs = await getEvalStore().getRunsByTenant(tenantId, { projectId: query.project_id, status: query.status });
|
|
6932
7252
|
reply.send({ success: true, message: "Ok", data: { records: runs, total: runs.length } });
|
|
@@ -6936,7 +7256,7 @@ function registerEvalRoutes(app2) {
|
|
|
6936
7256
|
});
|
|
6937
7257
|
app2.get("/api/eval/runs/:rid", async (request, reply) => {
|
|
6938
7258
|
try {
|
|
6939
|
-
const tenantId =
|
|
7259
|
+
const tenantId = getTenantId11(request);
|
|
6940
7260
|
const { rid } = request.params;
|
|
6941
7261
|
const run = await getEvalStore().getRunById(tenantId, rid);
|
|
6942
7262
|
if (!run) return reply.status(404).send({ success: false, message: "Run not found" });
|
|
@@ -6958,7 +7278,7 @@ function registerEvalRoutes(app2) {
|
|
|
6958
7278
|
const emitter = evalRunner.getEventEmitter();
|
|
6959
7279
|
const eventKey = `run:${rid}`;
|
|
6960
7280
|
if (!evalRunner.isRunning(rid)) {
|
|
6961
|
-
const tenantId =
|
|
7281
|
+
const tenantId = getTenantId11(request);
|
|
6962
7282
|
const run = await getEvalStore().getRunById(tenantId, rid);
|
|
6963
7283
|
if (run) {
|
|
6964
7284
|
const eventType = run.status === "completed" ? "completed" : "error";
|
|
@@ -6997,7 +7317,7 @@ data: ${JSON.stringify(event.data)}
|
|
|
6997
7317
|
});
|
|
6998
7318
|
app2.delete("/api/eval/runs/:rid", async (request, reply) => {
|
|
6999
7319
|
try {
|
|
7000
|
-
const tenantId =
|
|
7320
|
+
const tenantId = getTenantId11(request);
|
|
7001
7321
|
const { rid } = request.params;
|
|
7002
7322
|
const deleted = await getEvalStore().deleteRun(tenantId, rid);
|
|
7003
7323
|
if (!deleted) return reply.status(404).send({ success: false, message: "Run not found" });
|
|
@@ -7008,7 +7328,7 @@ data: ${JSON.stringify(event.data)}
|
|
|
7008
7328
|
});
|
|
7009
7329
|
app2.get("/api/eval/reports/projects/:pid", async (request, reply) => {
|
|
7010
7330
|
try {
|
|
7011
|
-
const tenantId =
|
|
7331
|
+
const tenantId = getTenantId11(request);
|
|
7012
7332
|
const { pid } = request.params;
|
|
7013
7333
|
const report = await getEvalStore().getProjectReport(tenantId, pid);
|
|
7014
7334
|
if (!report) return reply.status(404).send({ success: false, message: "Project not found" });
|
|
@@ -7020,11 +7340,11 @@ data: ${JSON.stringify(event.data)}
|
|
|
7020
7340
|
}
|
|
7021
7341
|
|
|
7022
7342
|
// src/controllers/users.ts
|
|
7023
|
-
import { getStoreLattice as
|
|
7343
|
+
import { getStoreLattice as getStoreLattice11 } from "@axiom-lattice/core";
|
|
7024
7344
|
import { v4 as uuidv45 } from "uuid";
|
|
7025
7345
|
var UsersController = class {
|
|
7026
7346
|
constructor() {
|
|
7027
|
-
this.userStore =
|
|
7347
|
+
this.userStore = getStoreLattice11("default", "user").store;
|
|
7028
7348
|
}
|
|
7029
7349
|
async listUsers(request, reply) {
|
|
7030
7350
|
const { email } = request.query;
|
|
@@ -7102,11 +7422,11 @@ function registerUserRoutes(app2) {
|
|
|
7102
7422
|
}
|
|
7103
7423
|
|
|
7104
7424
|
// src/controllers/tenants.ts
|
|
7105
|
-
import { getStoreLattice as
|
|
7425
|
+
import { getStoreLattice as getStoreLattice12 } from "@axiom-lattice/core";
|
|
7106
7426
|
import { v4 as uuidv46 } from "uuid";
|
|
7107
7427
|
var TenantsController = class {
|
|
7108
7428
|
constructor() {
|
|
7109
|
-
this.tenantStore =
|
|
7429
|
+
this.tenantStore = getStoreLattice12("default", "tenant").store;
|
|
7110
7430
|
}
|
|
7111
7431
|
// ==================== Tenant CRUD ====================
|
|
7112
7432
|
async listTenants(request, reply) {
|
|
@@ -7170,8 +7490,9 @@ function registerTenantRoutes(app2) {
|
|
|
7170
7490
|
}
|
|
7171
7491
|
|
|
7172
7492
|
// src/controllers/auth.ts
|
|
7173
|
-
import { getStoreLattice as
|
|
7493
|
+
import { getStoreLattice as getStoreLattice13 } from "@axiom-lattice/core";
|
|
7174
7494
|
import { v4 as uuidv47 } from "uuid";
|
|
7495
|
+
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
7175
7496
|
var defaultAuthConfig = {
|
|
7176
7497
|
autoApproveUsers: true,
|
|
7177
7498
|
allowTenantRegistration: true,
|
|
@@ -7179,9 +7500,9 @@ var defaultAuthConfig = {
|
|
|
7179
7500
|
};
|
|
7180
7501
|
var AuthController = class {
|
|
7181
7502
|
constructor(config = {}) {
|
|
7182
|
-
this.userStore =
|
|
7183
|
-
this.tenantStore =
|
|
7184
|
-
this.userTenantLinkStore =
|
|
7503
|
+
this.userStore = getStoreLattice13("default", "user").store;
|
|
7504
|
+
this.tenantStore = getStoreLattice13("default", "tenant").store;
|
|
7505
|
+
this.userTenantLinkStore = getStoreLattice13("default", "userTenantLink").store;
|
|
7185
7506
|
this.config = { ...defaultAuthConfig, ...config };
|
|
7186
7507
|
}
|
|
7187
7508
|
async register(request, reply) {
|
|
@@ -7252,7 +7573,8 @@ var AuthController = class {
|
|
|
7252
7573
|
});
|
|
7253
7574
|
}
|
|
7254
7575
|
const tenantLinks = await this.userTenantLinkStore.getTenantsByUser(user.id);
|
|
7255
|
-
const
|
|
7576
|
+
const singleTenantId = tenantLinks.length === 1 ? tenantLinks[0].tenantId : void 0;
|
|
7577
|
+
const token = await this.generateToken(user.id, singleTenantId);
|
|
7256
7578
|
return {
|
|
7257
7579
|
success: true,
|
|
7258
7580
|
data: {
|
|
@@ -7339,7 +7661,7 @@ var AuthController = class {
|
|
|
7339
7661
|
const linkMeta = link?.metadata || {};
|
|
7340
7662
|
let personalAssistant = null;
|
|
7341
7663
|
if (linkMeta.personalAssistantId) {
|
|
7342
|
-
const assistantStore =
|
|
7664
|
+
const assistantStore = getStoreLattice13("default", "assistant").store;
|
|
7343
7665
|
const exists = await assistantStore.hasAssistant(tenantId, linkMeta.personalAssistantId);
|
|
7344
7666
|
if (exists) {
|
|
7345
7667
|
personalAssistant = {
|
|
@@ -7458,6 +7780,9 @@ var AuthController = class {
|
|
|
7458
7780
|
return hashedPassword === hash;
|
|
7459
7781
|
}
|
|
7460
7782
|
async generateToken(userId, tenantId) {
|
|
7783
|
+
if (!this.config.jwtSecret) {
|
|
7784
|
+
throw new Error("AUTH_TOKEN_SECRET is required to issue console sessions");
|
|
7785
|
+
}
|
|
7461
7786
|
const payload = {
|
|
7462
7787
|
userId,
|
|
7463
7788
|
exp: Date.now() + this.config.tokenExpiration * 1e3
|
|
@@ -7465,7 +7790,7 @@ var AuthController = class {
|
|
|
7465
7790
|
if (tenantId) {
|
|
7466
7791
|
payload.tenantId = tenantId;
|
|
7467
7792
|
}
|
|
7468
|
-
return
|
|
7793
|
+
return createConsoleSessionToken(payload, this.config.jwtSecret);
|
|
7469
7794
|
}
|
|
7470
7795
|
async changePassword(request, reply) {
|
|
7471
7796
|
const userId = request.user?.id;
|
|
@@ -7519,12 +7844,46 @@ var AuthController = class {
|
|
|
7519
7844
|
}
|
|
7520
7845
|
}
|
|
7521
7846
|
};
|
|
7522
|
-
function
|
|
7523
|
-
|
|
7847
|
+
function resolveConsoleTokenSecret(_authRequired) {
|
|
7848
|
+
const configured = process.env.AUTH_TOKEN_SECRET || process.env.JWT_SECRET;
|
|
7849
|
+
if (configured) return configured;
|
|
7850
|
+
devSessionSecret ?? (devSessionSecret = randomBytes(32).toString("hex"));
|
|
7851
|
+
console.warn(
|
|
7852
|
+
"[auth] AUTH_TOKEN_SECRET/JWT_SECRET is not set; using an in-process generated signing secret (sessions are invalidated on restart). Set AUTH_TOKEN_SECRET for production."
|
|
7853
|
+
);
|
|
7854
|
+
return devSessionSecret;
|
|
7855
|
+
}
|
|
7856
|
+
var devSessionSecret;
|
|
7857
|
+
function createConsoleSessionToken(payload, secret) {
|
|
7858
|
+
const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
7859
|
+
const signature = createHmac("sha256", secret).update(encodedPayload).digest("base64url");
|
|
7860
|
+
return `${encodedPayload}.${signature}`;
|
|
7861
|
+
}
|
|
7862
|
+
function isConsoleSessionPayload(value) {
|
|
7863
|
+
if (typeof value !== "object" || value === null) return false;
|
|
7864
|
+
const payload = value;
|
|
7865
|
+
return typeof payload.userId === "string" && payload.userId.length > 0 && typeof payload.exp === "number" && (payload.tenantId === void 0 || typeof payload.tenantId === "string");
|
|
7866
|
+
}
|
|
7867
|
+
function extractUserFromAuthHeader(authHeader, secret) {
|
|
7868
|
+
if (!secret || !authHeader?.startsWith("Bearer ")) return void 0;
|
|
7524
7869
|
const token = authHeader.substring(7);
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7870
|
+
const parts = token.split(".");
|
|
7871
|
+
if (parts.length !== 2) return void 0;
|
|
7872
|
+
const [encodedPayload, encodedSignature] = parts;
|
|
7873
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(encodedSignature)) return void 0;
|
|
7874
|
+
try {
|
|
7875
|
+
const actualSignature = Buffer.from(encodedSignature, "base64url");
|
|
7876
|
+
if (actualSignature.toString("base64url") !== encodedSignature) return void 0;
|
|
7877
|
+
const expectedSignature = createHmac("sha256", secret).update(encodedPayload).digest();
|
|
7878
|
+
if (actualSignature.length !== expectedSignature.length || !timingSafeEqual(actualSignature, expectedSignature)) {
|
|
7879
|
+
return void 0;
|
|
7880
|
+
}
|
|
7881
|
+
const payload = JSON.parse(
|
|
7882
|
+
Buffer.from(encodedPayload, "base64url").toString("utf8")
|
|
7883
|
+
);
|
|
7884
|
+
if (!isConsoleSessionPayload(payload) || payload.exp < Date.now()) {
|
|
7885
|
+
return void 0;
|
|
7886
|
+
}
|
|
7528
7887
|
return {
|
|
7529
7888
|
id: payload.userId,
|
|
7530
7889
|
tenantId: payload.tenantId
|
|
@@ -7533,10 +7892,43 @@ function extractUserFromAuthHeader(authHeader) {
|
|
|
7533
7892
|
return void 0;
|
|
7534
7893
|
}
|
|
7535
7894
|
}
|
|
7895
|
+
var PUBLIC_ROUTES = ["/api/auth/login", "/api/auth/register", "/health", "/api/stt/models"];
|
|
7896
|
+
function bypassesConsoleAuth(request) {
|
|
7897
|
+
const path4 = request.url.split("?")[0];
|
|
7898
|
+
if (request.method === "GET" && /^\/api\/a2a\/agents\/[^/]+\/\.well-known\/agent-card\.json$/.test(path4)) {
|
|
7899
|
+
return true;
|
|
7900
|
+
}
|
|
7901
|
+
return request.method === "POST" && /^\/api\/a2a\/agents\/[^/]+\/jsonrpc$/.test(path4);
|
|
7902
|
+
}
|
|
7903
|
+
function registerConsoleAuthHook(app2, options) {
|
|
7904
|
+
app2.addHook("preHandler", async (request, reply) => {
|
|
7905
|
+
if (bypassesConsoleAuth(request)) return;
|
|
7906
|
+
const secret = options.tokenSecret ?? resolveConsoleTokenSecret(options.authRequired);
|
|
7907
|
+
const user = extractUserFromAuthHeader(request.headers.authorization, secret);
|
|
7908
|
+
if (user) {
|
|
7909
|
+
request.user = user;
|
|
7910
|
+
return;
|
|
7911
|
+
}
|
|
7912
|
+
if (!options.authRequired || request.method === "OPTIONS") return;
|
|
7913
|
+
if (PUBLIC_ROUTES.some((route) => request.url === route)) return;
|
|
7914
|
+
if (request.url.startsWith("/s/")) return;
|
|
7915
|
+
const path4 = request.url.split("?")[0];
|
|
7916
|
+
if (path4.includes("/viewfile") || path4.includes("/downloadfile") || path4.includes("/uploadfile") || path4.startsWith("/f/") || path4 === "/f") {
|
|
7917
|
+
return;
|
|
7918
|
+
}
|
|
7919
|
+
await reply.status(401).send({
|
|
7920
|
+
success: false,
|
|
7921
|
+
error: "Unauthorized - Missing or invalid token"
|
|
7922
|
+
});
|
|
7923
|
+
});
|
|
7924
|
+
}
|
|
7536
7925
|
function registerAuthRoutes(app2, config) {
|
|
7537
7926
|
const controller = new AuthController(config);
|
|
7538
7927
|
const authHook = async (request, reply) => {
|
|
7539
|
-
const user = extractUserFromAuthHeader(
|
|
7928
|
+
const user = extractUserFromAuthHeader(
|
|
7929
|
+
request.headers.authorization,
|
|
7930
|
+
config?.jwtSecret
|
|
7931
|
+
);
|
|
7540
7932
|
if (!user) {
|
|
7541
7933
|
return reply.status(401).send({
|
|
7542
7934
|
success: false,
|
|
@@ -7889,16 +8281,32 @@ function setChannelControllerDeps(deps) {
|
|
|
7889
8281
|
_adapterRegistry = deps.adapterRegistry;
|
|
7890
8282
|
_router = deps.router;
|
|
7891
8283
|
}
|
|
7892
|
-
function
|
|
8284
|
+
function getTenantId12(request) {
|
|
7893
8285
|
const userTenantId = request.user?.tenantId;
|
|
7894
8286
|
if (userTenantId) {
|
|
7895
8287
|
return userTenantId;
|
|
7896
8288
|
}
|
|
7897
8289
|
return request.headers["x-tenant-id"] || "default";
|
|
7898
8290
|
}
|
|
8291
|
+
function snapshotPlainObject(value) {
|
|
8292
|
+
try {
|
|
8293
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
8294
|
+
const prototype = Object.getPrototypeOf(value);
|
|
8295
|
+
if (prototype !== Object.prototype && prototype !== null) return null;
|
|
8296
|
+
const snapshot = {};
|
|
8297
|
+
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
|
|
8298
|
+
if (!descriptor.enumerable) continue;
|
|
8299
|
+
if (!("value" in descriptor)) return null;
|
|
8300
|
+
snapshot[key] = descriptor.value;
|
|
8301
|
+
}
|
|
8302
|
+
return snapshot;
|
|
8303
|
+
} catch {
|
|
8304
|
+
return null;
|
|
8305
|
+
}
|
|
8306
|
+
}
|
|
7899
8307
|
async function getInstallationStore() {
|
|
7900
|
-
const { getStoreLattice:
|
|
7901
|
-
const store =
|
|
8308
|
+
const { getStoreLattice: getStoreLattice27 } = await import("@axiom-lattice/core");
|
|
8309
|
+
const store = getStoreLattice27("default", "channelInstallation").store;
|
|
7902
8310
|
if (store) return store;
|
|
7903
8311
|
const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
|
|
7904
8312
|
const databaseUrl = process.env.DATABASE_URL;
|
|
@@ -7910,7 +8318,7 @@ async function getInstallationStore() {
|
|
|
7910
8318
|
});
|
|
7911
8319
|
}
|
|
7912
8320
|
async function getChannelInstallationList(request, reply) {
|
|
7913
|
-
const tenantId =
|
|
8321
|
+
const tenantId = getTenantId12(request);
|
|
7914
8322
|
const { channel } = request.query;
|
|
7915
8323
|
try {
|
|
7916
8324
|
const store = await getInstallationStore();
|
|
@@ -7936,7 +8344,7 @@ async function getChannelInstallationList(request, reply) {
|
|
|
7936
8344
|
}
|
|
7937
8345
|
}
|
|
7938
8346
|
async function getChannelInstallation(request, reply) {
|
|
7939
|
-
const tenantId =
|
|
8347
|
+
const tenantId = getTenantId12(request);
|
|
7940
8348
|
const { installationId } = request.params;
|
|
7941
8349
|
try {
|
|
7942
8350
|
const store = await getInstallationStore();
|
|
@@ -7969,9 +8377,25 @@ async function getChannelInstallation(request, reply) {
|
|
|
7969
8377
|
}
|
|
7970
8378
|
}
|
|
7971
8379
|
async function createChannelInstallation(request, reply) {
|
|
7972
|
-
const tenantId =
|
|
7973
|
-
const
|
|
7974
|
-
try {
|
|
8380
|
+
const tenantId = getTenantId12(request);
|
|
8381
|
+
const bodySnapshot = snapshotPlainObject(request.body);
|
|
8382
|
+
try {
|
|
8383
|
+
const allowedKeys = /* @__PURE__ */ new Set([
|
|
8384
|
+
"channel",
|
|
8385
|
+
"name",
|
|
8386
|
+
"config",
|
|
8387
|
+
"enabled",
|
|
8388
|
+
"fallbackAgentId",
|
|
8389
|
+
"rejectWhenNoBinding"
|
|
8390
|
+
]);
|
|
8391
|
+
if (!bodySnapshot || Object.keys(bodySnapshot).some((key) => !allowedKeys.has(key))) {
|
|
8392
|
+
reply.code(400);
|
|
8393
|
+
return {
|
|
8394
|
+
success: false,
|
|
8395
|
+
message: "Invalid request body"
|
|
8396
|
+
};
|
|
8397
|
+
}
|
|
8398
|
+
const body = bodySnapshot;
|
|
7975
8399
|
if (!body.channel) {
|
|
7976
8400
|
reply.code(400);
|
|
7977
8401
|
return {
|
|
@@ -8004,7 +8428,7 @@ async function createChannelInstallation(request, reply) {
|
|
|
8004
8428
|
}
|
|
8005
8429
|
}
|
|
8006
8430
|
const store = await getInstallationStore();
|
|
8007
|
-
const installationId =
|
|
8431
|
+
const installationId = randomUUID7();
|
|
8008
8432
|
const installation = await store.createInstallation(
|
|
8009
8433
|
tenantId,
|
|
8010
8434
|
installationId,
|
|
@@ -8028,7 +8452,8 @@ async function createChannelInstallation(request, reply) {
|
|
|
8028
8452
|
};
|
|
8029
8453
|
} catch (error) {
|
|
8030
8454
|
console.error("Failed to create channel installation:", error);
|
|
8031
|
-
|
|
8455
|
+
const errorMessage2 = error.message?.toLowerCase();
|
|
8456
|
+
if (errorMessage2?.includes("duplicate") || errorMessage2?.includes("unique constraint") || error.code === "23505") {
|
|
8032
8457
|
reply.code(409);
|
|
8033
8458
|
return {
|
|
8034
8459
|
success: false,
|
|
@@ -8042,7 +8467,7 @@ async function createChannelInstallation(request, reply) {
|
|
|
8042
8467
|
}
|
|
8043
8468
|
}
|
|
8044
8469
|
async function updateChannelInstallation(request, reply) {
|
|
8045
|
-
const tenantId =
|
|
8470
|
+
const tenantId = getTenantId12(request);
|
|
8046
8471
|
const { installationId } = request.params;
|
|
8047
8472
|
const body = request.body;
|
|
8048
8473
|
try {
|
|
@@ -8096,7 +8521,7 @@ async function updateChannelInstallation(request, reply) {
|
|
|
8096
8521
|
}
|
|
8097
8522
|
}
|
|
8098
8523
|
async function deleteChannelInstallation(request, reply) {
|
|
8099
|
-
const tenantId =
|
|
8524
|
+
const tenantId = getTenantId12(request);
|
|
8100
8525
|
const { installationId } = request.params;
|
|
8101
8526
|
try {
|
|
8102
8527
|
const store = await getInstallationStore();
|
|
@@ -8157,13 +8582,13 @@ function registerChannelInstallationRoutes(app2) {
|
|
|
8157
8582
|
|
|
8158
8583
|
// src/controllers/channel-bindings.ts
|
|
8159
8584
|
import { getBindingRegistry } from "@axiom-lattice/core";
|
|
8160
|
-
function
|
|
8585
|
+
function getTenantId13(request) {
|
|
8161
8586
|
const userTenantId = request.user?.tenantId;
|
|
8162
8587
|
if (userTenantId) return userTenantId;
|
|
8163
8588
|
return request.headers["x-tenant-id"] || "default";
|
|
8164
8589
|
}
|
|
8165
8590
|
async function getBindingList(request, _reply) {
|
|
8166
|
-
const tenantId =
|
|
8591
|
+
const tenantId = getTenantId13(request);
|
|
8167
8592
|
const { channel, agentId, channelInstallationId, limit, offset } = request.query;
|
|
8168
8593
|
try {
|
|
8169
8594
|
const registry = getBindingRegistry();
|
|
@@ -8175,7 +8600,7 @@ async function getBindingList(request, _reply) {
|
|
|
8175
8600
|
}
|
|
8176
8601
|
}
|
|
8177
8602
|
async function getBinding(request, reply) {
|
|
8178
|
-
const tenantId =
|
|
8603
|
+
const tenantId = getTenantId13(request);
|
|
8179
8604
|
try {
|
|
8180
8605
|
const registry = getBindingRegistry();
|
|
8181
8606
|
const bindings = await registry.list({ tenantId });
|
|
@@ -8191,7 +8616,7 @@ async function getBinding(request, reply) {
|
|
|
8191
8616
|
}
|
|
8192
8617
|
}
|
|
8193
8618
|
async function createBinding(request, reply) {
|
|
8194
|
-
const tenantId =
|
|
8619
|
+
const tenantId = getTenantId13(request);
|
|
8195
8620
|
try {
|
|
8196
8621
|
const registry = getBindingRegistry();
|
|
8197
8622
|
const binding = await registry.create({ ...request.body, tenantId });
|
|
@@ -8205,7 +8630,7 @@ async function createBinding(request, reply) {
|
|
|
8205
8630
|
}
|
|
8206
8631
|
async function updateBinding(request, reply) {
|
|
8207
8632
|
try {
|
|
8208
|
-
const tenantId =
|
|
8633
|
+
const tenantId = getTenantId13(request);
|
|
8209
8634
|
const registry = getBindingRegistry();
|
|
8210
8635
|
const bindings = await registry.list({ tenantId });
|
|
8211
8636
|
const existing = bindings.find((b) => b.id === request.params.id);
|
|
@@ -8223,7 +8648,7 @@ async function updateBinding(request, reply) {
|
|
|
8223
8648
|
}
|
|
8224
8649
|
async function deleteBinding(request, reply) {
|
|
8225
8650
|
try {
|
|
8226
|
-
const tenantId =
|
|
8651
|
+
const tenantId = getTenantId13(request);
|
|
8227
8652
|
const registry = getBindingRegistry();
|
|
8228
8653
|
const bindings = await registry.list({ tenantId });
|
|
8229
8654
|
const existing = bindings.find((b) => b.id === request.params.id);
|
|
@@ -8240,7 +8665,7 @@ async function deleteBinding(request, reply) {
|
|
|
8240
8665
|
}
|
|
8241
8666
|
}
|
|
8242
8667
|
async function resolveBinding(request, _reply) {
|
|
8243
|
-
const tenantId =
|
|
8668
|
+
const tenantId = getTenantId13(request);
|
|
8244
8669
|
const { channel, senderId, channelInstallationId } = request.query;
|
|
8245
8670
|
try {
|
|
8246
8671
|
const registry = getBindingRegistry();
|
|
@@ -8267,7 +8692,7 @@ function registerChannelBindingRoutes(app2) {
|
|
|
8267
8692
|
|
|
8268
8693
|
// src/controllers/menu-items.ts
|
|
8269
8694
|
import { getMenuRegistry } from "@axiom-lattice/core";
|
|
8270
|
-
function
|
|
8695
|
+
function getTenantId14(request) {
|
|
8271
8696
|
const userTenantId = request.user?.tenantId;
|
|
8272
8697
|
if (userTenantId) return userTenantId;
|
|
8273
8698
|
return request.headers["x-tenant-id"] || "default";
|
|
@@ -8276,7 +8701,7 @@ function errorMessage(err) {
|
|
|
8276
8701
|
return err instanceof Error ? err.message : "Unexpected error";
|
|
8277
8702
|
}
|
|
8278
8703
|
async function getMenuItemList(request, _reply) {
|
|
8279
|
-
const tenantId =
|
|
8704
|
+
const tenantId = getTenantId14(request);
|
|
8280
8705
|
try {
|
|
8281
8706
|
const registry = getMenuRegistry();
|
|
8282
8707
|
const items = await registry.list({ tenantId, menuTarget: request.query.menuTarget });
|
|
@@ -8288,7 +8713,7 @@ async function getMenuItemList(request, _reply) {
|
|
|
8288
8713
|
}
|
|
8289
8714
|
}
|
|
8290
8715
|
async function getMenuItem(request, reply) {
|
|
8291
|
-
const tenantId =
|
|
8716
|
+
const tenantId = getTenantId14(request);
|
|
8292
8717
|
try {
|
|
8293
8718
|
const registry = getMenuRegistry();
|
|
8294
8719
|
const item = await registry.getById(request.params.id);
|
|
@@ -8305,7 +8730,7 @@ async function getMenuItem(request, reply) {
|
|
|
8305
8730
|
}
|
|
8306
8731
|
}
|
|
8307
8732
|
async function createMenuItem(request, reply) {
|
|
8308
|
-
const tenantId =
|
|
8733
|
+
const tenantId = getTenantId14(request);
|
|
8309
8734
|
try {
|
|
8310
8735
|
const registry = getMenuRegistry();
|
|
8311
8736
|
const item = await registry.create({ ...request.body, tenantId });
|
|
@@ -8320,7 +8745,7 @@ async function createMenuItem(request, reply) {
|
|
|
8320
8745
|
}
|
|
8321
8746
|
async function updateMenuItem(request, reply) {
|
|
8322
8747
|
try {
|
|
8323
|
-
const tenantId =
|
|
8748
|
+
const tenantId = getTenantId14(request);
|
|
8324
8749
|
const registry = getMenuRegistry();
|
|
8325
8750
|
const existing = await registry.getById(request.params.id);
|
|
8326
8751
|
if (!existing || existing.tenantId !== tenantId) {
|
|
@@ -8338,7 +8763,7 @@ async function updateMenuItem(request, reply) {
|
|
|
8338
8763
|
}
|
|
8339
8764
|
async function deleteMenuItem(request, reply) {
|
|
8340
8765
|
try {
|
|
8341
|
-
const tenantId =
|
|
8766
|
+
const tenantId = getTenantId14(request);
|
|
8342
8767
|
const registry = getMenuRegistry();
|
|
8343
8768
|
const existing = await registry.getById(request.params.id);
|
|
8344
8769
|
if (!existing || existing.tenantId !== tenantId) {
|
|
@@ -8488,12 +8913,12 @@ async function handleBridgeProxy(request, reply) {
|
|
|
8488
8913
|
};
|
|
8489
8914
|
const timeout = 3e5;
|
|
8490
8915
|
try {
|
|
8491
|
-
const response = await new Promise((
|
|
8916
|
+
const response = await new Promise((resolve2, reject) => {
|
|
8492
8917
|
const timer = setTimeout(() => {
|
|
8493
8918
|
pending.delete(requestId);
|
|
8494
8919
|
reject(new Error(`Bridge request timeout after ${timeout}ms`));
|
|
8495
8920
|
}, timeout);
|
|
8496
|
-
pending.set(requestId, { resolve:
|
|
8921
|
+
pending.set(requestId, { resolve: resolve2, reject, timer, agentId });
|
|
8497
8922
|
try {
|
|
8498
8923
|
if (conn.ws.readyState !== conn.ws.OPEN) {
|
|
8499
8924
|
pending.delete(requestId);
|
|
@@ -8617,11 +9042,11 @@ var registerSTTRoutes = (app2) => {
|
|
|
8617
9042
|
};
|
|
8618
9043
|
|
|
8619
9044
|
// src/controllers/local-a2a.ts
|
|
8620
|
-
import { getStoreLattice as
|
|
9045
|
+
import { getStoreLattice as getStoreLattice14 } from "@axiom-lattice/core";
|
|
8621
9046
|
import {
|
|
8622
9047
|
AgentType as AgentType2
|
|
8623
9048
|
} from "@axiom-lattice/protocols";
|
|
8624
|
-
function
|
|
9049
|
+
function getTenantId15(request) {
|
|
8625
9050
|
const userTenantId = request.user?.tenantId;
|
|
8626
9051
|
if (userTenantId) return userTenantId;
|
|
8627
9052
|
return request.headers["x-tenant-id"] || "default";
|
|
@@ -8633,7 +9058,7 @@ function getLocalRuntime(assistant) {
|
|
|
8633
9058
|
return graphDefinition.localRuntime;
|
|
8634
9059
|
}
|
|
8635
9060
|
async function getLocalA2AAssistants(tenantId) {
|
|
8636
|
-
const storeLattice =
|
|
9061
|
+
const storeLattice = getStoreLattice14("default", "assistant");
|
|
8637
9062
|
const assistants = await storeLattice.store.getAllAssistants(tenantId);
|
|
8638
9063
|
const results = [];
|
|
8639
9064
|
for (const assistant of assistants) {
|
|
@@ -8645,7 +9070,7 @@ async function getLocalA2AAssistants(tenantId) {
|
|
|
8645
9070
|
return results;
|
|
8646
9071
|
}
|
|
8647
9072
|
async function getLocalA2AAssistant(tenantId, assistantId) {
|
|
8648
|
-
const storeLattice =
|
|
9073
|
+
const storeLattice = getStoreLattice14("default", "assistant");
|
|
8649
9074
|
const assistant = await storeLattice.store.getAssistantById(tenantId, assistantId);
|
|
8650
9075
|
if (!assistant) return null;
|
|
8651
9076
|
const runtime = getLocalRuntime(assistant);
|
|
@@ -8658,7 +9083,7 @@ async function requireManagedAssistant(request, reply) {
|
|
|
8658
9083
|
reply.status(400).send({ success: false, error: "assistantId is required" });
|
|
8659
9084
|
return null;
|
|
8660
9085
|
}
|
|
8661
|
-
const registered = await getLocalA2AAssistant(
|
|
9086
|
+
const registered = await getLocalA2AAssistant(getTenantId15(request), assistantId);
|
|
8662
9087
|
if (!registered) {
|
|
8663
9088
|
reply.status(404).send({
|
|
8664
9089
|
success: false,
|
|
@@ -8669,7 +9094,7 @@ async function requireManagedAssistant(request, reply) {
|
|
|
8669
9094
|
return { assistantId, localRuntime: registered.localRuntime };
|
|
8670
9095
|
}
|
|
8671
9096
|
async function getLocalA2AStatus(request, reply) {
|
|
8672
|
-
const tenantId =
|
|
9097
|
+
const tenantId = getTenantId15(request);
|
|
8673
9098
|
const managed = await getLocalA2AAssistants(tenantId);
|
|
8674
9099
|
const states = await Promise.all(
|
|
8675
9100
|
managed.map(
|
|
@@ -8873,6 +9298,294 @@ function registerA2ATestConnectionRoute(app2) {
|
|
|
8873
9298
|
});
|
|
8874
9299
|
}
|
|
8875
9300
|
|
|
9301
|
+
// src/controllers/tasks.ts
|
|
9302
|
+
import {
|
|
9303
|
+
getStoreLattice as getStoreLattice15
|
|
9304
|
+
} from "@axiom-lattice/core";
|
|
9305
|
+
var AGENT_CREATE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
|
|
9306
|
+
var FORBIDDEN_AGENT_CREATE_KEYS = /* @__PURE__ */ new Set([
|
|
9307
|
+
"beliefImpact",
|
|
9308
|
+
"beliefState",
|
|
9309
|
+
"cancelledAt",
|
|
9310
|
+
"completedAt",
|
|
9311
|
+
"completionEvidence",
|
|
9312
|
+
"evidenceId",
|
|
9313
|
+
"failureReason",
|
|
9314
|
+
"failedAt",
|
|
9315
|
+
"interruption",
|
|
9316
|
+
"lifecycle_completion_audit",
|
|
9317
|
+
"lifecycle_resume_audit",
|
|
9318
|
+
"requireReview",
|
|
9319
|
+
"result",
|
|
9320
|
+
"reviewedAt",
|
|
9321
|
+
"status",
|
|
9322
|
+
"interruptedAt"
|
|
9323
|
+
]);
|
|
9324
|
+
function containsLifecycleField(value) {
|
|
9325
|
+
if (!value || typeof value !== "object") return false;
|
|
9326
|
+
if (Array.isArray(value)) return value.some(containsLifecycleField);
|
|
9327
|
+
return Object.entries(value).some(([key, nested]) => FORBIDDEN_AGENT_CREATE_KEYS.has(key) || key.startsWith("lifecycle_") || containsLifecycleField(nested));
|
|
9328
|
+
}
|
|
9329
|
+
function governedAgentCreateError(body) {
|
|
9330
|
+
if (body.ownerType !== "agent") return void 0;
|
|
9331
|
+
if (body.status && !AGENT_CREATE_STATUSES.has(body.status)) {
|
|
9332
|
+
return {
|
|
9333
|
+
code: "TASK_LIFECYCLE_TRANSITION_REQUIRED",
|
|
9334
|
+
error: "Public agent tasks can only be created pending."
|
|
9335
|
+
};
|
|
9336
|
+
}
|
|
9337
|
+
const raw = body;
|
|
9338
|
+
if (Object.prototype.hasOwnProperty.call(raw, "result") || Object.prototype.hasOwnProperty.call(raw, "failureReason") || Object.prototype.hasOwnProperty.call(raw, "requireReview") || Object.prototype.hasOwnProperty.call(raw, "createdAt") || Object.prototype.hasOwnProperty.call(raw, "updatedAt") || containsLifecycleField(body.context) || containsLifecycleField(body.metadata)) {
|
|
9339
|
+
return {
|
|
9340
|
+
code: "TASK_LIFECYCLE_CONTEXT_FORBIDDEN",
|
|
9341
|
+
error: "Agent lifecycle results, review policy, interruption state, and audit fields require a trusted lifecycle adapter."
|
|
9342
|
+
};
|
|
9343
|
+
}
|
|
9344
|
+
return void 0;
|
|
9345
|
+
}
|
|
9346
|
+
function getTenantId16(request) {
|
|
9347
|
+
const userTenantId = request.user?.tenantId;
|
|
9348
|
+
if (userTenantId) return userTenantId;
|
|
9349
|
+
return request.headers["x-tenant-id"] || "default";
|
|
9350
|
+
}
|
|
9351
|
+
function getUserId4(request) {
|
|
9352
|
+
const userId = request.user?.id || request.headers["x-user-id"];
|
|
9353
|
+
if (!userId) throw new Error("User ID is required");
|
|
9354
|
+
return userId;
|
|
9355
|
+
}
|
|
9356
|
+
function getStore() {
|
|
9357
|
+
return getStoreLattice15("default", "task").store;
|
|
9358
|
+
}
|
|
9359
|
+
function getAssistantStore2() {
|
|
9360
|
+
return getStoreLattice15("default", "assistant").store;
|
|
9361
|
+
}
|
|
9362
|
+
function governedAgentUpdateError(task, updates) {
|
|
9363
|
+
const has = (key) => Object.prototype.hasOwnProperty.call(updates, key);
|
|
9364
|
+
if (has("ownerType") && updates.ownerType !== task.ownerType || has("ownerId") && updates.ownerId !== task.ownerId) {
|
|
9365
|
+
return "TASK_OWNERSHIP_IMMUTABLE";
|
|
9366
|
+
}
|
|
9367
|
+
if (task.ownerType !== "agent") return void 0;
|
|
9368
|
+
if (has("parentId") && updates.parentId !== task.parentId || has("requireReview") && updates.requireReview !== task.requireReview || has("result") || has("failureReason") || has("status")) {
|
|
9369
|
+
return "TASK_LIFECYCLE_TRANSITION_REQUIRED";
|
|
9370
|
+
}
|
|
9371
|
+
if (has("context") || has("description")) {
|
|
9372
|
+
return "COMPLETION_CONTEXT_MUTATION";
|
|
9373
|
+
}
|
|
9374
|
+
return void 0;
|
|
9375
|
+
}
|
|
9376
|
+
async function listTasks(request) {
|
|
9377
|
+
try {
|
|
9378
|
+
const tenantId = getTenantId16(request);
|
|
9379
|
+
const userId = getUserId4(request);
|
|
9380
|
+
const query = request.query;
|
|
9381
|
+
const ownerType = query.ownerType;
|
|
9382
|
+
const mixedOwnership = ownerType === void 0;
|
|
9383
|
+
const limit = query.limit ? parseInt(query.limit) : void 0;
|
|
9384
|
+
const offset = query.offset ? parseInt(query.offset) : void 0;
|
|
9385
|
+
const store = getStore();
|
|
9386
|
+
const tasks = await store.list({
|
|
9387
|
+
tenantId,
|
|
9388
|
+
status: query.status,
|
|
9389
|
+
priority: query.priority,
|
|
9390
|
+
...ownerType === "user" ? { ownerType, ownerId: userId } : {},
|
|
9391
|
+
...ownerType === "agent" ? { ownerType, ...query.ownerId && { ownerId: query.ownerId } } : {},
|
|
9392
|
+
...mixedOwnership && query.ownerId ? { ownerId: query.ownerId } : {},
|
|
9393
|
+
...query.parentId && { parentId: query.parentId },
|
|
9394
|
+
metadata: query.metadata ? JSON.parse(query.metadata) : void 0,
|
|
9395
|
+
projectId: query.projectId,
|
|
9396
|
+
...!mixedOwnership && limit !== void 0 ? { limit } : {},
|
|
9397
|
+
...!mixedOwnership && offset !== void 0 ? { offset } : {}
|
|
9398
|
+
});
|
|
9399
|
+
const visibleTasks = mixedOwnership ? tasks.filter((task) => task.ownerType === "agent" || task.ownerType === "user" && task.ownerId === userId) : tasks;
|
|
9400
|
+
const pageOffset = mixedOwnership ? offset ?? 0 : 0;
|
|
9401
|
+
const page = mixedOwnership ? visibleTasks.slice(pageOffset, limit === void 0 ? void 0 : pageOffset + limit) : visibleTasks;
|
|
9402
|
+
return { success: true, data: page, count: page.length };
|
|
9403
|
+
} catch (error) {
|
|
9404
|
+
return { success: false, error: error.message };
|
|
9405
|
+
}
|
|
9406
|
+
}
|
|
9407
|
+
async function getTask(request, reply) {
|
|
9408
|
+
try {
|
|
9409
|
+
const tenantId = getTenantId16(request);
|
|
9410
|
+
const userId = getUserId4(request);
|
|
9411
|
+
const { id } = request.params;
|
|
9412
|
+
const store = getStore();
|
|
9413
|
+
const task = await store.getById(tenantId, id);
|
|
9414
|
+
if (!task) {
|
|
9415
|
+
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
9416
|
+
}
|
|
9417
|
+
if (task.ownerType === "user" && task.ownerId !== userId) {
|
|
9418
|
+
return reply.status(403).send({ success: false, error: "Access denied" });
|
|
9419
|
+
}
|
|
9420
|
+
return { success: true, data: task };
|
|
9421
|
+
} catch (error) {
|
|
9422
|
+
return { success: false, error: error.message };
|
|
9423
|
+
}
|
|
9424
|
+
}
|
|
9425
|
+
async function createTask(request, reply) {
|
|
9426
|
+
try {
|
|
9427
|
+
const tenantId = getTenantId16(request);
|
|
9428
|
+
const userId = getUserId4(request);
|
|
9429
|
+
const body = request.body;
|
|
9430
|
+
if (!body.title) {
|
|
9431
|
+
return reply.status(400).send({ success: false, error: "title is required" });
|
|
9432
|
+
}
|
|
9433
|
+
const lifecycleError = governedAgentCreateError(body);
|
|
9434
|
+
if (lifecycleError) {
|
|
9435
|
+
return reply.status(409).send({ success: false, ...lifecycleError });
|
|
9436
|
+
}
|
|
9437
|
+
const ownerType = body.ownerType || "user";
|
|
9438
|
+
const ownerId = body.ownerId || userId;
|
|
9439
|
+
if (ownerType === "agent") {
|
|
9440
|
+
const assistant = await getAssistantStore2().getAssistantById(tenantId, ownerId);
|
|
9441
|
+
if (!assistant || assistant.tenantId !== tenantId) {
|
|
9442
|
+
return reply.status(404).send({
|
|
9443
|
+
success: false,
|
|
9444
|
+
code: "AGENT_OWNER_NOT_FOUND",
|
|
9445
|
+
error: "Agent owner was not found in the current tenant."
|
|
9446
|
+
});
|
|
9447
|
+
}
|
|
9448
|
+
}
|
|
9449
|
+
const store = getStore();
|
|
9450
|
+
const task = await store.create({
|
|
9451
|
+
...body,
|
|
9452
|
+
tenantId,
|
|
9453
|
+
ownerType,
|
|
9454
|
+
ownerId
|
|
9455
|
+
});
|
|
9456
|
+
return reply.status(201).send({ success: true, data: task });
|
|
9457
|
+
} catch (error) {
|
|
9458
|
+
return reply.status(500).send({ success: false, error: error.message });
|
|
9459
|
+
}
|
|
9460
|
+
}
|
|
9461
|
+
async function updateTask(request, reply) {
|
|
9462
|
+
try {
|
|
9463
|
+
const tenantId = getTenantId16(request);
|
|
9464
|
+
const userId = getUserId4(request);
|
|
9465
|
+
const { id } = request.params;
|
|
9466
|
+
const store = getStore();
|
|
9467
|
+
const existing = await store.getById(tenantId, id);
|
|
9468
|
+
if (!existing) {
|
|
9469
|
+
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
9470
|
+
}
|
|
9471
|
+
if (existing.ownerType === "user" && existing.ownerId !== userId) {
|
|
9472
|
+
return reply.status(403).send({ success: false, error: "Access denied" });
|
|
9473
|
+
}
|
|
9474
|
+
const lifecycleError = governedAgentUpdateError(existing, request.body);
|
|
9475
|
+
if (lifecycleError) {
|
|
9476
|
+
return reply.status(409).send({
|
|
9477
|
+
success: false,
|
|
9478
|
+
code: lifecycleError,
|
|
9479
|
+
error: lifecycleError === "COMPLETION_CONTEXT_MUTATION" ? "Agent completion context is owned by the task lifecycle service." : lifecycleError === "TASK_OWNERSHIP_IMMUTABLE" ? "Task ownership is immutable after creation." : "Agent ownership, hierarchy, and lifecycle transitions require a trusted lifecycle adapter."
|
|
9480
|
+
});
|
|
9481
|
+
}
|
|
9482
|
+
const task = await store.update(tenantId, id, request.body);
|
|
9483
|
+
if (!task) {
|
|
9484
|
+
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
9485
|
+
}
|
|
9486
|
+
return { success: true, data: task };
|
|
9487
|
+
} catch (error) {
|
|
9488
|
+
return reply.status(500).send({ success: false, error: error.message });
|
|
9489
|
+
}
|
|
9490
|
+
}
|
|
9491
|
+
async function deleteTask(request, reply) {
|
|
9492
|
+
try {
|
|
9493
|
+
const tenantId = getTenantId16(request);
|
|
9494
|
+
const userId = getUserId4(request);
|
|
9495
|
+
const { id } = request.params;
|
|
9496
|
+
const store = getStore();
|
|
9497
|
+
const existing = await store.getById(tenantId, id);
|
|
9498
|
+
if (!existing) {
|
|
9499
|
+
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
9500
|
+
}
|
|
9501
|
+
if (existing.ownerType === "user" && existing.ownerId !== userId) {
|
|
9502
|
+
return reply.status(403).send({ success: false, error: "Access denied" });
|
|
9503
|
+
}
|
|
9504
|
+
if (existing.ownerType === "agent") {
|
|
9505
|
+
return reply.status(403).send({
|
|
9506
|
+
success: false,
|
|
9507
|
+
code: "AGENT_RUNTIME_REQUIRED",
|
|
9508
|
+
error: "Agent-owned tasks can only be deleted by a trusted agent runtime."
|
|
9509
|
+
});
|
|
9510
|
+
}
|
|
9511
|
+
const deleted = await store.delete(tenantId, id);
|
|
9512
|
+
if (!deleted) {
|
|
9513
|
+
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
9514
|
+
}
|
|
9515
|
+
return { success: true, message: "Task deleted" };
|
|
9516
|
+
} catch (error) {
|
|
9517
|
+
return reply.status(500).send({ success: false, error: error.message });
|
|
9518
|
+
}
|
|
9519
|
+
}
|
|
9520
|
+
async function completeTask(request, reply) {
|
|
9521
|
+
try {
|
|
9522
|
+
const tenantId = getTenantId16(request);
|
|
9523
|
+
const userId = getUserId4(request);
|
|
9524
|
+
const { id } = request.params;
|
|
9525
|
+
const store = getStore();
|
|
9526
|
+
const existing = await store.getById(tenantId, id);
|
|
9527
|
+
if (!existing) {
|
|
9528
|
+
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
9529
|
+
}
|
|
9530
|
+
if (existing.ownerType === "user" && existing.ownerId !== userId) {
|
|
9531
|
+
return reply.status(403).send({ success: false, error: "Access denied" });
|
|
9532
|
+
}
|
|
9533
|
+
const body = request.body ?? {};
|
|
9534
|
+
if (existing.ownerType === "user") {
|
|
9535
|
+
const task = await store.update(tenantId, id, {
|
|
9536
|
+
status: "completed",
|
|
9537
|
+
...body.result === void 0 ? {} : { result: body.result }
|
|
9538
|
+
});
|
|
9539
|
+
if (!task) return reply.status(404).send({ success: false, error: "Task not found" });
|
|
9540
|
+
return { success: true, data: task };
|
|
9541
|
+
}
|
|
9542
|
+
return reply.status(403).send({
|
|
9543
|
+
success: false,
|
|
9544
|
+
code: "AGENT_RUNTIME_REQUIRED",
|
|
9545
|
+
error: "Agent-owned tasks can only be completed by a trusted agent runtime."
|
|
9546
|
+
});
|
|
9547
|
+
} catch (error) {
|
|
9548
|
+
return reply.status(500).send({ success: false, error: error.message });
|
|
9549
|
+
}
|
|
9550
|
+
}
|
|
9551
|
+
function getTaskWorkItemStore() {
|
|
9552
|
+
return getStoreLattice15("default", "taskWorkItem").store;
|
|
9553
|
+
}
|
|
9554
|
+
async function listTaskWorkItems(request, reply) {
|
|
9555
|
+
const tenantId = getTenantId16(request);
|
|
9556
|
+
const userId = getUserId4(request);
|
|
9557
|
+
const { id } = request.params;
|
|
9558
|
+
const { action, limit, offset } = request.query;
|
|
9559
|
+
const task = await getStore().getById(tenantId, id);
|
|
9560
|
+
if (!task) {
|
|
9561
|
+
return reply.status(404).send({ success: false, error: "Task not found" });
|
|
9562
|
+
}
|
|
9563
|
+
if (task.ownerType === "user" && task.ownerId !== userId) {
|
|
9564
|
+
return reply.status(403).send({ success: false, error: "Access denied" });
|
|
9565
|
+
}
|
|
9566
|
+
const store = getTaskWorkItemStore();
|
|
9567
|
+
const items = await store.list({
|
|
9568
|
+
tenantId,
|
|
9569
|
+
taskId: id,
|
|
9570
|
+
action,
|
|
9571
|
+
limit: limit ? Number(limit) : void 0,
|
|
9572
|
+
offset: offset ? Number(offset) : void 0
|
|
9573
|
+
});
|
|
9574
|
+
return reply.send({ success: true, data: items });
|
|
9575
|
+
}
|
|
9576
|
+
|
|
9577
|
+
// src/routes/task-routes.ts
|
|
9578
|
+
function registerTaskRoutes(app2) {
|
|
9579
|
+
app2.get("/api/tasks", listTasks);
|
|
9580
|
+
app2.get("/api/tasks/:id", getTask);
|
|
9581
|
+
app2.post("/api/tasks", createTask);
|
|
9582
|
+
app2.put("/api/tasks/:id", updateTask);
|
|
9583
|
+
app2.patch("/api/tasks/:id", updateTask);
|
|
9584
|
+
app2.delete("/api/tasks/:id", deleteTask);
|
|
9585
|
+
app2.patch("/api/tasks/:id/complete", completeTask);
|
|
9586
|
+
app2.get("/api/tasks/:id/work-items", listTaskWorkItems);
|
|
9587
|
+
}
|
|
9588
|
+
|
|
8876
9589
|
// src/routes/index.ts
|
|
8877
9590
|
var registerLatticeRoutes = (app2, channelDeps) => {
|
|
8878
9591
|
registerA2ABridgeRoutes(app2);
|
|
@@ -8927,13 +9640,7 @@ var registerLatticeRoutes = (app2, channelDeps) => {
|
|
|
8927
9640
|
app2.post("/api/personal-assistant", createPersonalAssistant);
|
|
8928
9641
|
app2.get("/api/personal-assistant", getPersonalAssistant);
|
|
8929
9642
|
app2.delete("/api/personal-assistant", deletePersonalAssistant);
|
|
8930
|
-
app2
|
|
8931
|
-
app2.get("/api/tasks/:id", getTask);
|
|
8932
|
-
app2.post("/api/tasks", createTask);
|
|
8933
|
-
app2.put("/api/tasks/:id", updateTask);
|
|
8934
|
-
app2.delete("/api/tasks/:id", deleteTask);
|
|
8935
|
-
app2.patch("/api/tasks/:id/complete", completeTask);
|
|
8936
|
-
app2.get("/api/tasks/:id/work-items", listTaskWorkItems);
|
|
9643
|
+
registerTaskRoutes(app2);
|
|
8937
9644
|
app2.get(
|
|
8938
9645
|
"/api/assistants/:assistantId/graph",
|
|
8939
9646
|
{ schema: getAgentGraphSchema },
|
|
@@ -9042,7 +9749,8 @@ var registerLatticeRoutes = (app2, channelDeps) => {
|
|
|
9042
9749
|
registerTenantRoutes(app2);
|
|
9043
9750
|
registerAuthRoutes(app2, {
|
|
9044
9751
|
autoApproveUsers: process.env.AUTO_APPROVE_USERS !== "false",
|
|
9045
|
-
allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false"
|
|
9752
|
+
allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false",
|
|
9753
|
+
jwtSecret: resolveConsoleTokenSecret(process.env.AUTH_REQUIRED === "true")
|
|
9046
9754
|
});
|
|
9047
9755
|
registerChannelRoutes(app2, channelDeps);
|
|
9048
9756
|
registerChannelInstallationRoutes(app2);
|
|
@@ -9179,9 +9887,16 @@ function registerResourceRoutes(app2, controller) {
|
|
|
9179
9887
|
import { ExportableEntityRegistry as ExportableEntityRegistry3 } from "@axiom-lattice/core";
|
|
9180
9888
|
|
|
9181
9889
|
// src/export_registrations/skill.registration.ts
|
|
9182
|
-
import {
|
|
9183
|
-
function getStore2() {
|
|
9184
|
-
return
|
|
9890
|
+
import { SandboxSkillStore as SandboxSkillStore2, isBuiltInSkill as isBuiltInSkill2 } from "@axiom-lattice/core";
|
|
9891
|
+
function getStore2(context) {
|
|
9892
|
+
return new SandboxSkillStore2({
|
|
9893
|
+
workspaceId: context?.workspaceId,
|
|
9894
|
+
projectId: context?.projectId
|
|
9895
|
+
});
|
|
9896
|
+
}
|
|
9897
|
+
function toStoreContext(context) {
|
|
9898
|
+
if (!context) return void 0;
|
|
9899
|
+
return { workspaceId: context.workspaceId, projectId: context.projectId };
|
|
9185
9900
|
}
|
|
9186
9901
|
var skillRegistration = {
|
|
9187
9902
|
entityType: "skill",
|
|
@@ -9189,10 +9904,10 @@ var skillRegistration = {
|
|
|
9189
9904
|
category: "core",
|
|
9190
9905
|
dependsOn: [],
|
|
9191
9906
|
cascadeParents: [],
|
|
9192
|
-
async listForExport(tenantId) {
|
|
9193
|
-
const store = getStore2();
|
|
9194
|
-
const skills = await store.getAllSkills(tenantId);
|
|
9195
|
-
return skills.map((s) => ({
|
|
9907
|
+
async listForExport(tenantId, context) {
|
|
9908
|
+
const store = getStore2(context);
|
|
9909
|
+
const skills = await store.getAllSkills(tenantId, toStoreContext(context));
|
|
9910
|
+
return skills.filter((s) => !isBuiltInSkill2(s.id)).map((s) => ({
|
|
9196
9911
|
_exportId: "",
|
|
9197
9912
|
data: {
|
|
9198
9913
|
id: s.id,
|
|
@@ -9206,14 +9921,58 @@ var skillRegistration = {
|
|
|
9206
9921
|
}
|
|
9207
9922
|
}));
|
|
9208
9923
|
},
|
|
9209
|
-
async
|
|
9210
|
-
const
|
|
9211
|
-
const
|
|
9924
|
+
async getReferencedEntityIds(_tenantId, entities) {
|
|
9925
|
+
const subSkillIds = /* @__PURE__ */ new Set();
|
|
9926
|
+
for (const entity of entities) {
|
|
9927
|
+
const subSkills = entity.data.subSkills;
|
|
9928
|
+
if (!Array.isArray(subSkills)) continue;
|
|
9929
|
+
for (const id of subSkills) {
|
|
9930
|
+
if (typeof id === "string" && id) subSkillIds.add(id);
|
|
9931
|
+
}
|
|
9932
|
+
}
|
|
9933
|
+
return { skill: [...subSkillIds] };
|
|
9934
|
+
},
|
|
9935
|
+
async listFilesForExport(tenantId, entities, context) {
|
|
9936
|
+
const store = getStore2(context);
|
|
9937
|
+
const ctx = toStoreContext(context);
|
|
9938
|
+
const files = [];
|
|
9939
|
+
for (const entity of entities) {
|
|
9940
|
+
const skillId = entity.data.id;
|
|
9941
|
+
if (!skillId) continue;
|
|
9942
|
+
const content = entity.data.content;
|
|
9943
|
+
files.push({
|
|
9944
|
+
entityId: skillId,
|
|
9945
|
+
path: "SKILL.md",
|
|
9946
|
+
content: Buffer.from(content ?? "", "utf-8")
|
|
9947
|
+
});
|
|
9948
|
+
try {
|
|
9949
|
+
const resources = await store.listSkillResources?.(tenantId, skillId, ctx);
|
|
9950
|
+
if (resources) {
|
|
9951
|
+
for (const resourcePath of resources) {
|
|
9952
|
+
const resourceContent = typeof store.loadSkillResourceRaw === "function" ? await store.loadSkillResourceRaw(tenantId, skillId, resourcePath, ctx) : await store.loadSkillResource?.(tenantId, skillId, resourcePath, ctx);
|
|
9953
|
+
if (resourceContent !== null && resourceContent !== void 0) {
|
|
9954
|
+
files.push({
|
|
9955
|
+
entityId: skillId,
|
|
9956
|
+
path: `resources/${resourcePath}`,
|
|
9957
|
+
content: Buffer.isBuffer(resourceContent) ? resourceContent : Buffer.from(resourceContent, "utf-8")
|
|
9958
|
+
});
|
|
9959
|
+
}
|
|
9960
|
+
}
|
|
9961
|
+
}
|
|
9962
|
+
} catch {
|
|
9963
|
+
}
|
|
9964
|
+
}
|
|
9965
|
+
return files;
|
|
9966
|
+
},
|
|
9967
|
+
async previewImport(tenantId, entities, context) {
|
|
9968
|
+
const store = getStore2(context);
|
|
9969
|
+
const existing = await store.getAllSkills(tenantId, toStoreContext(context));
|
|
9212
9970
|
const existingIds = new Set(existing.map((s) => s.id));
|
|
9213
9971
|
const conflicts = [];
|
|
9214
9972
|
const insertions = [];
|
|
9215
9973
|
for (const entity of entities) {
|
|
9216
9974
|
const id = entity.data.id;
|
|
9975
|
+
if (isBuiltInSkill2(id)) continue;
|
|
9217
9976
|
if (existingIds.has(id)) {
|
|
9218
9977
|
conflicts.push({
|
|
9219
9978
|
_exportId: entity._exportId,
|
|
@@ -9232,33 +9991,49 @@ var skillRegistration = {
|
|
|
9232
9991
|
}
|
|
9233
9992
|
return { conflicts, insertions, errors: [] };
|
|
9234
9993
|
},
|
|
9235
|
-
async applyImport(tenantId, entity, resolution) {
|
|
9236
|
-
const store = getStore2();
|
|
9994
|
+
async applyImport(tenantId, entity, resolution, files, context) {
|
|
9995
|
+
const store = getStore2(context);
|
|
9996
|
+
const ctx = toStoreContext(context);
|
|
9237
9997
|
const data = entity.data;
|
|
9238
|
-
const
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
} catch {
|
|
9243
|
-
}
|
|
9998
|
+
const originalName = data.name;
|
|
9999
|
+
const targetName = resolution.action === "rename" ? resolution.newId : originalName;
|
|
10000
|
+
if (typeof targetName !== "string" || targetName.trim().length === 0) {
|
|
10001
|
+
throw new Error("Skill import target name must not be empty");
|
|
9244
10002
|
}
|
|
9245
|
-
await store.createSkill(tenantId,
|
|
9246
|
-
name:
|
|
10003
|
+
await store.createSkill(tenantId, targetName, {
|
|
10004
|
+
name: targetName,
|
|
9247
10005
|
description: data.description,
|
|
9248
10006
|
license: data.license,
|
|
9249
10007
|
compatibility: data.compatibility,
|
|
9250
10008
|
metadata: data.metadata,
|
|
9251
10009
|
content: data.content,
|
|
9252
10010
|
subSkills: data.subSkills
|
|
9253
|
-
});
|
|
9254
|
-
|
|
10011
|
+
}, ctx);
|
|
10012
|
+
if (files && files.size > 0) {
|
|
10013
|
+
for (const [relPath, content] of files.entries()) {
|
|
10014
|
+
if (relPath === "SKILL.md") {
|
|
10015
|
+
const fileContent = content.toString("utf-8");
|
|
10016
|
+
if (fileContent !== data.content && typeof store.updateSkill === "function") {
|
|
10017
|
+
await store.updateSkill(tenantId, targetName, { content: fileContent }, ctx);
|
|
10018
|
+
}
|
|
10019
|
+
} else if (relPath.startsWith("resources/")) {
|
|
10020
|
+
const resourcePath = relPath.slice("resources/".length);
|
|
10021
|
+
if (typeof store.writeSkillResourceRaw === "function") {
|
|
10022
|
+
await store.writeSkillResourceRaw(tenantId, targetName, resourcePath, content, ctx);
|
|
10023
|
+
} else if (typeof store.writeSkillResource === "function") {
|
|
10024
|
+
await store.writeSkillResource(tenantId, targetName, resourcePath, content.toString("utf-8"), ctx);
|
|
10025
|
+
}
|
|
10026
|
+
}
|
|
10027
|
+
}
|
|
10028
|
+
}
|
|
10029
|
+
return { newId: targetName };
|
|
9255
10030
|
}
|
|
9256
10031
|
};
|
|
9257
10032
|
|
|
9258
10033
|
// src/export_registrations/agent.registration.ts
|
|
9259
|
-
import { getStoreLattice as
|
|
10034
|
+
import { getStoreLattice as getStoreLattice16 } from "@axiom-lattice/core";
|
|
9260
10035
|
function getStore3() {
|
|
9261
|
-
return
|
|
10036
|
+
return getStoreLattice16("default", "assistant").store;
|
|
9262
10037
|
}
|
|
9263
10038
|
var agentRegistration = {
|
|
9264
10039
|
entityType: "agent",
|
|
@@ -9279,145 +10054,68 @@ var agentRegistration = {
|
|
|
9279
10054
|
}
|
|
9280
10055
|
}));
|
|
9281
10056
|
},
|
|
9282
|
-
async
|
|
9283
|
-
const
|
|
9284
|
-
const existing = await store.getAllAssistants(tenantId);
|
|
9285
|
-
const existingIds = new Set(existing.map((a) => a.id));
|
|
9286
|
-
const conflicts = [];
|
|
9287
|
-
const insertions = [];
|
|
10057
|
+
async getReferencedEntityIds(_tenantId, entities) {
|
|
10058
|
+
const skillIds = /* @__PURE__ */ new Set();
|
|
9288
10059
|
for (const entity of entities) {
|
|
9289
|
-
const
|
|
9290
|
-
if (
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
existingId: id,
|
|
9296
|
-
existingName: entity.data.name || id
|
|
9297
|
-
});
|
|
9298
|
-
} else {
|
|
9299
|
-
insertions.push({
|
|
9300
|
-
_exportId: entity._exportId,
|
|
9301
|
-
entityType: "agent",
|
|
9302
|
-
name: entity.data.name || id
|
|
9303
|
-
});
|
|
9304
|
-
}
|
|
9305
|
-
}
|
|
9306
|
-
return { conflicts, insertions, errors: [] };
|
|
9307
|
-
},
|
|
9308
|
-
async applyImport(tenantId, entity, resolution) {
|
|
9309
|
-
const store = getStore3();
|
|
9310
|
-
const data = entity.data;
|
|
9311
|
-
const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
|
|
9312
|
-
if (resolution.action === "overwrite") {
|
|
9313
|
-
try {
|
|
9314
|
-
await store.deleteAssistant(tenantId, id);
|
|
9315
|
-
} catch {
|
|
10060
|
+
const graphDefinition = entity.data.graphDefinition;
|
|
10061
|
+
if (!graphDefinition || typeof graphDefinition !== "object") continue;
|
|
10062
|
+
const ids = graphDefinition.skillIds;
|
|
10063
|
+
if (!Array.isArray(ids)) continue;
|
|
10064
|
+
for (const id of ids) {
|
|
10065
|
+
if (typeof id === "string" && id) skillIds.add(id);
|
|
9316
10066
|
}
|
|
9317
10067
|
}
|
|
9318
|
-
|
|
9319
|
-
name: data.name,
|
|
9320
|
-
description: data.description || "",
|
|
9321
|
-
graphDefinition: data.graphDefinition
|
|
9322
|
-
});
|
|
9323
|
-
return { newId: id };
|
|
9324
|
-
}
|
|
9325
|
-
};
|
|
9326
|
-
|
|
9327
|
-
// src/export_registrations/binding.registration.ts
|
|
9328
|
-
import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
|
|
9329
|
-
function getStore4() {
|
|
9330
|
-
return getStoreLattice18("default", "channelBinding").store;
|
|
9331
|
-
}
|
|
9332
|
-
var bindingRegistration = {
|
|
9333
|
-
entityType: "binding",
|
|
9334
|
-
label: "Binding(\u6E20\u9053\u7ED1\u5B9A)",
|
|
9335
|
-
category: "core",
|
|
9336
|
-
dependsOn: ["agent", "channel_installation"],
|
|
9337
|
-
cascadeParents: [],
|
|
9338
|
-
referenceFields: { agentId: "agent", channelInstallationId: "channel_installation" },
|
|
9339
|
-
async listForExport(tenantId) {
|
|
9340
|
-
const store = getStore4();
|
|
9341
|
-
const bindings = await store.list({ tenantId, limit: 1e4, offset: 0 });
|
|
9342
|
-
return bindings.map((b) => ({
|
|
9343
|
-
_exportId: "",
|
|
9344
|
-
data: {
|
|
9345
|
-
channel: b.channel,
|
|
9346
|
-
channelInstallationId: b.channelInstallationId,
|
|
9347
|
-
senderId: b.senderId,
|
|
9348
|
-
agentId: b.agentId,
|
|
9349
|
-
threadId: b.threadId,
|
|
9350
|
-
workspaceId: b.workspaceId,
|
|
9351
|
-
projectId: b.projectId,
|
|
9352
|
-
threadMode: b.threadMode,
|
|
9353
|
-
senderDisplayName: b.senderDisplayName,
|
|
9354
|
-
senderMetadata: b.senderMetadata,
|
|
9355
|
-
enabled: b.enabled
|
|
9356
|
-
}
|
|
9357
|
-
}));
|
|
10068
|
+
return { skill: [...skillIds] };
|
|
9358
10069
|
},
|
|
9359
10070
|
async previewImport(tenantId, entities) {
|
|
9360
|
-
const store =
|
|
9361
|
-
const existing = await store.
|
|
9362
|
-
const
|
|
9363
|
-
existing.map((b) => `${b.channel}:${b.channelInstallationId}:${b.senderId}`)
|
|
9364
|
-
);
|
|
10071
|
+
const store = getStore3();
|
|
10072
|
+
const existing = await store.getAllAssistants(tenantId);
|
|
10073
|
+
const existingIds = new Set(existing.map((a) => a.id));
|
|
9365
10074
|
const conflicts = [];
|
|
9366
10075
|
const insertions = [];
|
|
9367
10076
|
for (const entity of entities) {
|
|
9368
|
-
const
|
|
9369
|
-
|
|
9370
|
-
if (existingKeys.has(key)) {
|
|
10077
|
+
const id = entity.data.id;
|
|
10078
|
+
if (existingIds.has(id)) {
|
|
9371
10079
|
conflicts.push({
|
|
9372
10080
|
_exportId: entity._exportId,
|
|
9373
|
-
entityType: "
|
|
9374
|
-
conflictType: "
|
|
9375
|
-
|
|
9376
|
-
|
|
10081
|
+
entityType: "agent",
|
|
10082
|
+
conflictType: "id_exists",
|
|
10083
|
+
existingId: id,
|
|
10084
|
+
existingName: entity.data.name || id
|
|
9377
10085
|
});
|
|
9378
10086
|
} else {
|
|
9379
10087
|
insertions.push({
|
|
9380
10088
|
_exportId: entity._exportId,
|
|
9381
|
-
entityType: "
|
|
9382
|
-
name:
|
|
10089
|
+
entityType: "agent",
|
|
10090
|
+
name: entity.data.name || id
|
|
9383
10091
|
});
|
|
9384
10092
|
}
|
|
9385
10093
|
}
|
|
9386
10094
|
return { conflicts, insertions, errors: [] };
|
|
9387
10095
|
},
|
|
9388
10096
|
async applyImport(tenantId, entity, resolution) {
|
|
9389
|
-
|
|
9390
|
-
const
|
|
9391
|
-
const
|
|
10097
|
+
const store = getStore3();
|
|
10098
|
+
const data = entity.data;
|
|
10099
|
+
const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
|
|
9392
10100
|
if (resolution.action === "overwrite") {
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
);
|
|
9397
|
-
if (match) {
|
|
9398
|
-
await store.delete(match.id);
|
|
10101
|
+
try {
|
|
10102
|
+
await store.deleteAssistant(tenantId, id);
|
|
10103
|
+
} catch {
|
|
9399
10104
|
}
|
|
9400
10105
|
}
|
|
9401
|
-
await store.
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
senderId: d.senderId,
|
|
9406
|
-
agentId: d.agentId,
|
|
9407
|
-
threadMode: d.threadMode || "fixed",
|
|
9408
|
-
senderDisplayName: d.senderDisplayName,
|
|
9409
|
-
senderMetadata: d.senderMetadata,
|
|
9410
|
-
workspaceId: d.workspaceId,
|
|
9411
|
-
projectId: d.projectId
|
|
10106
|
+
await store.createAssistant(tenantId, id, {
|
|
10107
|
+
name: data.name,
|
|
10108
|
+
description: data.description || "",
|
|
10109
|
+
graphDefinition: data.graphDefinition
|
|
9412
10110
|
});
|
|
9413
|
-
return { newId:
|
|
10111
|
+
return { newId: id };
|
|
9414
10112
|
}
|
|
9415
10113
|
};
|
|
9416
10114
|
|
|
9417
10115
|
// src/export_registrations/database-config.registration.ts
|
|
9418
|
-
import { getStoreLattice as
|
|
9419
|
-
function
|
|
9420
|
-
return
|
|
10116
|
+
import { getStoreLattice as getStoreLattice17 } from "@axiom-lattice/core";
|
|
10117
|
+
function getStore4() {
|
|
10118
|
+
return getStoreLattice17("default", "database").store;
|
|
9421
10119
|
}
|
|
9422
10120
|
var databaseConfigRegistration = {
|
|
9423
10121
|
entityType: "database_config",
|
|
@@ -9426,24 +10124,24 @@ var databaseConfigRegistration = {
|
|
|
9426
10124
|
dependsOn: [],
|
|
9427
10125
|
cascadeParents: [],
|
|
9428
10126
|
async listForExport(tenantId) {
|
|
9429
|
-
const store =
|
|
10127
|
+
const store = getStore4();
|
|
9430
10128
|
const configs = await store.getAllConfigs(tenantId);
|
|
9431
|
-
return configs.map((
|
|
10129
|
+
return configs.map((c2) => ({
|
|
9432
10130
|
_exportId: "",
|
|
9433
10131
|
data: {
|
|
9434
|
-
id:
|
|
9435
|
-
key:
|
|
9436
|
-
name:
|
|
9437
|
-
description:
|
|
9438
|
-
config:
|
|
10132
|
+
id: c2.id,
|
|
10133
|
+
key: c2.key,
|
|
10134
|
+
name: c2.name,
|
|
10135
|
+
description: c2.description,
|
|
10136
|
+
config: c2.config
|
|
9439
10137
|
}
|
|
9440
10138
|
}));
|
|
9441
10139
|
},
|
|
9442
10140
|
async previewImport(tenantId, entities) {
|
|
9443
|
-
const store =
|
|
10141
|
+
const store = getStore4();
|
|
9444
10142
|
const existing = await store.getAllConfigs(tenantId);
|
|
9445
|
-
const existingIds = new Set(existing.map((
|
|
9446
|
-
const existingKeys = new Set(existing.map((
|
|
10143
|
+
const existingIds = new Set(existing.map((c2) => c2.id));
|
|
10144
|
+
const existingKeys = new Set(existing.map((c2) => c2.key));
|
|
9447
10145
|
const conflicts = [];
|
|
9448
10146
|
const insertions = [];
|
|
9449
10147
|
for (const entity of entities) {
|
|
@@ -9477,7 +10175,7 @@ var databaseConfigRegistration = {
|
|
|
9477
10175
|
return { conflicts, insertions, errors: [] };
|
|
9478
10176
|
},
|
|
9479
10177
|
async applyImport(tenantId, entity, resolution) {
|
|
9480
|
-
const store =
|
|
10178
|
+
const store = getStore4();
|
|
9481
10179
|
const data = entity.data;
|
|
9482
10180
|
const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
|
|
9483
10181
|
if (resolution.action === "overwrite") {
|
|
@@ -9497,9 +10195,9 @@ var databaseConfigRegistration = {
|
|
|
9497
10195
|
};
|
|
9498
10196
|
|
|
9499
10197
|
// src/export_registrations/metrics-config.registration.ts
|
|
9500
|
-
import { getStoreLattice as
|
|
9501
|
-
function
|
|
9502
|
-
return
|
|
10198
|
+
import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
|
|
10199
|
+
function getStore5() {
|
|
10200
|
+
return getStoreLattice18("default", "metrics").store;
|
|
9503
10201
|
}
|
|
9504
10202
|
var metricsConfigRegistration = {
|
|
9505
10203
|
entityType: "metrics_config",
|
|
@@ -9508,24 +10206,24 @@ var metricsConfigRegistration = {
|
|
|
9508
10206
|
dependsOn: [],
|
|
9509
10207
|
cascadeParents: [],
|
|
9510
10208
|
async listForExport(tenantId) {
|
|
9511
|
-
const store =
|
|
10209
|
+
const store = getStore5();
|
|
9512
10210
|
const configs = await store.getAllConfigs(tenantId);
|
|
9513
|
-
return configs.map((
|
|
10211
|
+
return configs.map((c2) => ({
|
|
9514
10212
|
_exportId: "",
|
|
9515
10213
|
data: {
|
|
9516
|
-
id:
|
|
9517
|
-
key:
|
|
9518
|
-
name:
|
|
9519
|
-
description:
|
|
9520
|
-
config:
|
|
10214
|
+
id: c2.id,
|
|
10215
|
+
key: c2.key,
|
|
10216
|
+
name: c2.name,
|
|
10217
|
+
description: c2.description,
|
|
10218
|
+
config: c2.config
|
|
9521
10219
|
}
|
|
9522
10220
|
}));
|
|
9523
10221
|
},
|
|
9524
10222
|
async previewImport(tenantId, entities) {
|
|
9525
|
-
const store =
|
|
10223
|
+
const store = getStore5();
|
|
9526
10224
|
const existing = await store.getAllConfigs(tenantId);
|
|
9527
|
-
const existingIds = new Set(existing.map((
|
|
9528
|
-
const existingKeys = new Set(existing.map((
|
|
10225
|
+
const existingIds = new Set(existing.map((c2) => c2.id));
|
|
10226
|
+
const existingKeys = new Set(existing.map((c2) => c2.key));
|
|
9529
10227
|
const conflicts = [];
|
|
9530
10228
|
const insertions = [];
|
|
9531
10229
|
for (const entity of entities) {
|
|
@@ -9559,7 +10257,7 @@ var metricsConfigRegistration = {
|
|
|
9559
10257
|
return { conflicts, insertions, errors: [] };
|
|
9560
10258
|
},
|
|
9561
10259
|
async applyImport(tenantId, entity, resolution) {
|
|
9562
|
-
const store =
|
|
10260
|
+
const store = getStore5();
|
|
9563
10261
|
const data = entity.data;
|
|
9564
10262
|
const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
|
|
9565
10263
|
if (resolution.action === "overwrite") {
|
|
@@ -9579,9 +10277,9 @@ var metricsConfigRegistration = {
|
|
|
9579
10277
|
};
|
|
9580
10278
|
|
|
9581
10279
|
// src/export_registrations/mcp-config.registration.ts
|
|
9582
|
-
import { getStoreLattice as
|
|
9583
|
-
function
|
|
9584
|
-
return
|
|
10280
|
+
import { getStoreLattice as getStoreLattice19 } from "@axiom-lattice/core";
|
|
10281
|
+
function getStore6() {
|
|
10282
|
+
return getStoreLattice19("default", "mcp").store;
|
|
9585
10283
|
}
|
|
9586
10284
|
var mcpConfigRegistration = {
|
|
9587
10285
|
entityType: "mcp_config",
|
|
@@ -9590,26 +10288,26 @@ var mcpConfigRegistration = {
|
|
|
9590
10288
|
dependsOn: [],
|
|
9591
10289
|
cascadeParents: [],
|
|
9592
10290
|
async listForExport(tenantId) {
|
|
9593
|
-
const store =
|
|
10291
|
+
const store = getStore6();
|
|
9594
10292
|
const configs = await store.getAllConfigs(tenantId);
|
|
9595
|
-
return configs.map((
|
|
10293
|
+
return configs.map((c2) => ({
|
|
9596
10294
|
_exportId: "",
|
|
9597
10295
|
data: {
|
|
9598
|
-
id:
|
|
9599
|
-
key:
|
|
9600
|
-
name:
|
|
9601
|
-
description:
|
|
9602
|
-
config:
|
|
9603
|
-
selectedTools:
|
|
9604
|
-
status:
|
|
10296
|
+
id: c2.id,
|
|
10297
|
+
key: c2.key,
|
|
10298
|
+
name: c2.name,
|
|
10299
|
+
description: c2.description,
|
|
10300
|
+
config: c2.config,
|
|
10301
|
+
selectedTools: c2.selectedTools,
|
|
10302
|
+
status: c2.status
|
|
9605
10303
|
}
|
|
9606
10304
|
}));
|
|
9607
10305
|
},
|
|
9608
10306
|
async previewImport(tenantId, entities) {
|
|
9609
|
-
const store =
|
|
10307
|
+
const store = getStore6();
|
|
9610
10308
|
const existing = await store.getAllConfigs(tenantId);
|
|
9611
|
-
const existingIds = new Set(existing.map((
|
|
9612
|
-
const existingKeys = new Set(existing.map((
|
|
10309
|
+
const existingIds = new Set(existing.map((c2) => c2.id));
|
|
10310
|
+
const existingKeys = new Set(existing.map((c2) => c2.key));
|
|
9613
10311
|
const conflicts = [];
|
|
9614
10312
|
const insertions = [];
|
|
9615
10313
|
for (const entity of entities) {
|
|
@@ -9643,7 +10341,7 @@ var mcpConfigRegistration = {
|
|
|
9643
10341
|
return { conflicts, insertions, errors: [] };
|
|
9644
10342
|
},
|
|
9645
10343
|
async applyImport(tenantId, entity, resolution) {
|
|
9646
|
-
const store =
|
|
10344
|
+
const store = getStore6();
|
|
9647
10345
|
const data = entity.data;
|
|
9648
10346
|
const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
|
|
9649
10347
|
if (resolution.action === "overwrite") {
|
|
@@ -9664,12 +10362,12 @@ var mcpConfigRegistration = {
|
|
|
9664
10362
|
};
|
|
9665
10363
|
|
|
9666
10364
|
// src/export_registrations/connection.registration.ts
|
|
9667
|
-
import { getStoreLattice as
|
|
9668
|
-
function
|
|
9669
|
-
return
|
|
10365
|
+
import { getStoreLattice as getStoreLattice20 } from "@axiom-lattice/core";
|
|
10366
|
+
function getStore7() {
|
|
10367
|
+
return getStoreLattice20("default", "connection").store;
|
|
9670
10368
|
}
|
|
9671
10369
|
async function listAllConnections(tenantId) {
|
|
9672
|
-
const store =
|
|
10370
|
+
const store = getStore7();
|
|
9673
10371
|
if (typeof store.listByTenant !== "function") {
|
|
9674
10372
|
throw new Error("ConnectionStore.listByTenant is not implemented. SQL bypass removed \u2014 upgrade your store adapter.");
|
|
9675
10373
|
}
|
|
@@ -9683,22 +10381,22 @@ var connectionRegistration = {
|
|
|
9683
10381
|
cascadeParents: [],
|
|
9684
10382
|
async listForExport(tenantId) {
|
|
9685
10383
|
const connections2 = await listAllConnections(tenantId);
|
|
9686
|
-
return connections2.map((
|
|
10384
|
+
return connections2.map((c2) => ({
|
|
9687
10385
|
_exportId: "",
|
|
9688
10386
|
data: {
|
|
9689
|
-
id:
|
|
9690
|
-
type:
|
|
9691
|
-
key:
|
|
9692
|
-
name:
|
|
9693
|
-
description:
|
|
9694
|
-
config:
|
|
10387
|
+
id: c2.id,
|
|
10388
|
+
type: c2.type,
|
|
10389
|
+
key: c2.key,
|
|
10390
|
+
name: c2.name,
|
|
10391
|
+
description: c2.description,
|
|
10392
|
+
config: c2.config
|
|
9695
10393
|
}
|
|
9696
10394
|
}));
|
|
9697
10395
|
},
|
|
9698
10396
|
async previewImport(tenantId, entities) {
|
|
9699
10397
|
const existing = await listAllConnections(tenantId);
|
|
9700
10398
|
const existingCompositeKeys = new Set(
|
|
9701
|
-
existing.map((
|
|
10399
|
+
existing.map((c2) => `${c2.type}:${c2.key}`)
|
|
9702
10400
|
);
|
|
9703
10401
|
const conflicts = [];
|
|
9704
10402
|
const insertions = [];
|
|
@@ -9725,7 +10423,7 @@ var connectionRegistration = {
|
|
|
9725
10423
|
},
|
|
9726
10424
|
async applyImport(tenantId, entity, resolution) {
|
|
9727
10425
|
if (resolution.action === "skip") return {};
|
|
9728
|
-
const store =
|
|
10426
|
+
const store = getStore7();
|
|
9729
10427
|
const d = entity.data;
|
|
9730
10428
|
const type = d.type;
|
|
9731
10429
|
const key = d.key;
|
|
@@ -9748,9 +10446,9 @@ var connectionRegistration = {
|
|
|
9748
10446
|
};
|
|
9749
10447
|
|
|
9750
10448
|
// src/export_registrations/collection.registration.ts
|
|
9751
|
-
import { getStoreLattice as
|
|
9752
|
-
function
|
|
9753
|
-
return
|
|
10449
|
+
import { getStoreLattice as getStoreLattice21 } from "@axiom-lattice/core";
|
|
10450
|
+
function getStore8() {
|
|
10451
|
+
return getStoreLattice21("default", "collection").store;
|
|
9754
10452
|
}
|
|
9755
10453
|
var collectionRegistration = {
|
|
9756
10454
|
entityType: "collection",
|
|
@@ -9759,23 +10457,23 @@ var collectionRegistration = {
|
|
|
9759
10457
|
dependsOn: [],
|
|
9760
10458
|
cascadeParents: [],
|
|
9761
10459
|
async listForExport(tenantId) {
|
|
9762
|
-
const store =
|
|
10460
|
+
const store = getStore8();
|
|
9763
10461
|
const collections = await store.getAllCollections(tenantId);
|
|
9764
|
-
return collections.map((
|
|
10462
|
+
return collections.map((c2) => ({
|
|
9765
10463
|
_exportId: "",
|
|
9766
10464
|
data: {
|
|
9767
|
-
id:
|
|
9768
|
-
name:
|
|
9769
|
-
label:
|
|
9770
|
-
embeddingKey:
|
|
9771
|
-
schema:
|
|
10465
|
+
id: c2.id,
|
|
10466
|
+
name: c2.name,
|
|
10467
|
+
label: c2.label,
|
|
10468
|
+
embeddingKey: c2.embeddingKey,
|
|
10469
|
+
schema: c2.schema
|
|
9772
10470
|
}
|
|
9773
10471
|
}));
|
|
9774
10472
|
},
|
|
9775
10473
|
async previewImport(tenantId, entities) {
|
|
9776
|
-
const store =
|
|
10474
|
+
const store = getStore8();
|
|
9777
10475
|
const existing = await store.getAllCollections(tenantId);
|
|
9778
|
-
const existingNames = new Set(existing.map((
|
|
10476
|
+
const existingNames = new Set(existing.map((c2) => c2.name));
|
|
9779
10477
|
const conflicts = [];
|
|
9780
10478
|
const insertions = [];
|
|
9781
10479
|
for (const entity of entities) {
|
|
@@ -9801,7 +10499,7 @@ var collectionRegistration = {
|
|
|
9801
10499
|
},
|
|
9802
10500
|
async applyImport(tenantId, entity, resolution) {
|
|
9803
10501
|
if (resolution.action === "skip") return {};
|
|
9804
|
-
const store =
|
|
10502
|
+
const store = getStore8();
|
|
9805
10503
|
const d = entity.data;
|
|
9806
10504
|
const name = d.name;
|
|
9807
10505
|
if (resolution.action === "overwrite") {
|
|
@@ -9821,9 +10519,11 @@ var collectionRegistration = {
|
|
|
9821
10519
|
};
|
|
9822
10520
|
|
|
9823
10521
|
// src/export_registrations/channel-installation.registration.ts
|
|
9824
|
-
import { getStoreLattice as
|
|
9825
|
-
|
|
9826
|
-
|
|
10522
|
+
import { getStoreLattice as getStoreLattice22 } from "@axiom-lattice/core";
|
|
10523
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
10524
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
10525
|
+
function getStore9() {
|
|
10526
|
+
return getStoreLattice22("default", "channelInstallation").store;
|
|
9827
10527
|
}
|
|
9828
10528
|
var channelInstallationRegistration = {
|
|
9829
10529
|
entityType: "channel_installation",
|
|
@@ -9833,7 +10533,7 @@ var channelInstallationRegistration = {
|
|
|
9833
10533
|
cascadeParents: [],
|
|
9834
10534
|
referenceFields: { fallbackAgentId: "agent" },
|
|
9835
10535
|
async listForExport(tenantId) {
|
|
9836
|
-
const store =
|
|
10536
|
+
const store = getStore9();
|
|
9837
10537
|
const installations = await store.getInstallationsByTenant(tenantId);
|
|
9838
10538
|
return installations.map((i) => ({
|
|
9839
10539
|
_exportId: "",
|
|
@@ -9849,21 +10549,20 @@ var channelInstallationRegistration = {
|
|
|
9849
10549
|
}));
|
|
9850
10550
|
},
|
|
9851
10551
|
async previewImport(tenantId, entities) {
|
|
9852
|
-
const store =
|
|
9853
|
-
const existing = await store.getInstallationsByTenant(tenantId);
|
|
9854
|
-
const existingIds = new Set(existing.map((i) => i.id));
|
|
10552
|
+
const store = getStore9();
|
|
9855
10553
|
const conflicts = [];
|
|
9856
10554
|
const insertions = [];
|
|
9857
10555
|
for (const entity of entities) {
|
|
9858
10556
|
const d = entity.data;
|
|
9859
10557
|
const id = d.id;
|
|
9860
|
-
|
|
10558
|
+
const existing = UUID_PATTERN.test(id) ? await store.getInstallationById(id) : null;
|
|
10559
|
+
if (existing) {
|
|
9861
10560
|
conflicts.push({
|
|
9862
10561
|
_exportId: entity._exportId,
|
|
9863
10562
|
entityType: "channel_installation",
|
|
9864
10563
|
conflictType: "id_exists",
|
|
9865
10564
|
existingId: id,
|
|
9866
|
-
existingName:
|
|
10565
|
+
existingName: existing.name || id
|
|
9867
10566
|
});
|
|
9868
10567
|
} else {
|
|
9869
10568
|
insertions.push({
|
|
@@ -9876,31 +10575,56 @@ var channelInstallationRegistration = {
|
|
|
9876
10575
|
return { conflicts, insertions, errors: [] };
|
|
9877
10576
|
},
|
|
9878
10577
|
async applyImport(tenantId, entity, resolution) {
|
|
9879
|
-
const store =
|
|
10578
|
+
const store = getStore9();
|
|
9880
10579
|
const data = entity.data;
|
|
9881
|
-
const
|
|
10580
|
+
const sourceId = data.id;
|
|
10581
|
+
const commonData = {
|
|
10582
|
+
name: data.name,
|
|
10583
|
+
enabled: data.enabled,
|
|
10584
|
+
fallbackAgentId: data.fallbackAgentId,
|
|
10585
|
+
rejectWhenNoBinding: data.rejectWhenNoBinding
|
|
10586
|
+
};
|
|
9882
10587
|
if (resolution.action === "overwrite") {
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
10588
|
+
const existing = await store.getInstallationById(sourceId);
|
|
10589
|
+
if (!existing || !UUID_PATTERN.test(existing.id)) {
|
|
10590
|
+
throw new Error("Channel installation overwrite target must be an existing UUID");
|
|
10591
|
+
}
|
|
10592
|
+
const updated = await store.updateInstallation(tenantId, existing.id, {
|
|
10593
|
+
...commonData,
|
|
10594
|
+
config: data.config
|
|
10595
|
+
});
|
|
10596
|
+
if (!updated) {
|
|
10597
|
+
throw new Error("Channel installation overwrite target is not available in the target tenant");
|
|
9886
10598
|
}
|
|
10599
|
+
return { newId: existing.id };
|
|
10600
|
+
}
|
|
10601
|
+
let id;
|
|
10602
|
+
if (resolution.action === "rename") {
|
|
10603
|
+
if (!resolution.newId || !UUID_PATTERN.test(resolution.newId)) {
|
|
10604
|
+
throw new Error("Channel installation rename ID must be a valid UUID");
|
|
10605
|
+
}
|
|
10606
|
+
id = resolution.newId;
|
|
10607
|
+
} else if (UUID_PATTERN.test(sourceId)) {
|
|
10608
|
+
if (await store.getInstallationById(sourceId)) {
|
|
10609
|
+
throw new Error(`Channel installation UUID already exists: ${sourceId}`);
|
|
10610
|
+
}
|
|
10611
|
+
id = sourceId;
|
|
10612
|
+
} else {
|
|
10613
|
+
id = randomUUID9();
|
|
9887
10614
|
}
|
|
9888
10615
|
await store.createInstallation(tenantId, id, {
|
|
9889
10616
|
channel: data.channel,
|
|
9890
|
-
|
|
9891
|
-
config: data.config
|
|
9892
|
-
enabled: data.enabled,
|
|
9893
|
-
fallbackAgentId: data.fallbackAgentId,
|
|
9894
|
-
rejectWhenNoBinding: data.rejectWhenNoBinding
|
|
10617
|
+
...commonData,
|
|
10618
|
+
config: data.config
|
|
9895
10619
|
});
|
|
9896
10620
|
return { newId: id };
|
|
9897
10621
|
}
|
|
9898
10622
|
};
|
|
9899
10623
|
|
|
9900
10624
|
// src/export_registrations/menu.registration.ts
|
|
9901
|
-
import { getStoreLattice as
|
|
9902
|
-
function
|
|
9903
|
-
return
|
|
10625
|
+
import { getStoreLattice as getStoreLattice23 } from "@axiom-lattice/core";
|
|
10626
|
+
function getStore10() {
|
|
10627
|
+
return getStoreLattice23("default", "menu").store;
|
|
9904
10628
|
}
|
|
9905
10629
|
var menuRegistration = {
|
|
9906
10630
|
entityType: "menu",
|
|
@@ -9909,7 +10633,7 @@ var menuRegistration = {
|
|
|
9909
10633
|
dependsOn: ["agent"],
|
|
9910
10634
|
cascadeParents: [],
|
|
9911
10635
|
async listForExport(tenantId) {
|
|
9912
|
-
const store =
|
|
10636
|
+
const store = getStore10();
|
|
9913
10637
|
const menus = await store.list({ tenantId });
|
|
9914
10638
|
return menus.map((m) => ({
|
|
9915
10639
|
_exportId: "",
|
|
@@ -9927,7 +10651,7 @@ var menuRegistration = {
|
|
|
9927
10651
|
}));
|
|
9928
10652
|
},
|
|
9929
10653
|
async previewImport(tenantId, entities) {
|
|
9930
|
-
const store =
|
|
10654
|
+
const store = getStore10();
|
|
9931
10655
|
const existing = await store.list({ tenantId });
|
|
9932
10656
|
const existingIds = new Set(existing.map((m) => m.id));
|
|
9933
10657
|
const conflicts = [];
|
|
@@ -9954,7 +10678,7 @@ var menuRegistration = {
|
|
|
9954
10678
|
return { conflicts, insertions, errors: [] };
|
|
9955
10679
|
},
|
|
9956
10680
|
async applyImport(tenantId, entity, resolution) {
|
|
9957
|
-
const store =
|
|
10681
|
+
const store = getStore10();
|
|
9958
10682
|
const d = entity.data;
|
|
9959
10683
|
const id = resolution.action === "rename" && resolution.newId ? resolution.newId : d.id;
|
|
9960
10684
|
if (resolution.action === "overwrite") {
|
|
@@ -9977,171 +10701,10 @@ var menuRegistration = {
|
|
|
9977
10701
|
}
|
|
9978
10702
|
};
|
|
9979
10703
|
|
|
9980
|
-
// src/export_registrations/task.registration.ts
|
|
9981
|
-
import { getStoreLattice as getStoreLattice26 } from "@axiom-lattice/core";
|
|
9982
|
-
function getStore12() {
|
|
9983
|
-
return getStoreLattice26("default", "task").store;
|
|
9984
|
-
}
|
|
9985
|
-
var taskRegistration = {
|
|
9986
|
-
entityType: "task",
|
|
9987
|
-
label: "Task(\u4EFB\u52A1)",
|
|
9988
|
-
category: "core",
|
|
9989
|
-
dependsOn: ["agent"],
|
|
9990
|
-
cascadeParents: [],
|
|
9991
|
-
referenceFields: { ownerId: "agent", parentId: "task" },
|
|
9992
|
-
async listForExport(tenantId) {
|
|
9993
|
-
const store = getStore12();
|
|
9994
|
-
const tasks = await store.list({ tenantId, limit: 1e4, offset: 0 });
|
|
9995
|
-
return tasks.map((t) => ({
|
|
9996
|
-
_exportId: "",
|
|
9997
|
-
data: {
|
|
9998
|
-
id: t.id,
|
|
9999
|
-
title: t.title,
|
|
10000
|
-
description: t.description,
|
|
10001
|
-
status: t.status,
|
|
10002
|
-
priority: t.priority,
|
|
10003
|
-
dueDate: t.dueDate,
|
|
10004
|
-
ownerType: t.ownerType,
|
|
10005
|
-
ownerId: t.ownerId,
|
|
10006
|
-
metadata: t.metadata,
|
|
10007
|
-
parentId: t.parentId,
|
|
10008
|
-
sourceId: t.sourceId,
|
|
10009
|
-
context: t.context
|
|
10010
|
-
}
|
|
10011
|
-
}));
|
|
10012
|
-
},
|
|
10013
|
-
async previewImport(tenantId, entities) {
|
|
10014
|
-
const store = getStore12();
|
|
10015
|
-
const existing = await store.list({ tenantId, limit: 1e4, offset: 0 });
|
|
10016
|
-
const existingIds = new Set(existing.map((t) => t.id));
|
|
10017
|
-
const conflicts = [];
|
|
10018
|
-
const insertions = [];
|
|
10019
|
-
for (const entity of entities) {
|
|
10020
|
-
const d = entity.data;
|
|
10021
|
-
const id = d.id;
|
|
10022
|
-
if (existingIds.has(id)) {
|
|
10023
|
-
conflicts.push({
|
|
10024
|
-
_exportId: entity._exportId,
|
|
10025
|
-
entityType: "task",
|
|
10026
|
-
conflictType: "id_exists",
|
|
10027
|
-
existingId: id,
|
|
10028
|
-
existingName: d.title || id
|
|
10029
|
-
});
|
|
10030
|
-
} else {
|
|
10031
|
-
insertions.push({
|
|
10032
|
-
_exportId: entity._exportId,
|
|
10033
|
-
entityType: "task",
|
|
10034
|
-
name: d.title || id
|
|
10035
|
-
});
|
|
10036
|
-
}
|
|
10037
|
-
}
|
|
10038
|
-
return { conflicts, insertions, errors: [] };
|
|
10039
|
-
},
|
|
10040
|
-
async applyImport(tenantId, entity, resolution) {
|
|
10041
|
-
const store = getStore12();
|
|
10042
|
-
const data = entity.data;
|
|
10043
|
-
const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
|
|
10044
|
-
if (resolution.action === "overwrite") {
|
|
10045
|
-
try {
|
|
10046
|
-
await store.delete(tenantId, id);
|
|
10047
|
-
} catch {
|
|
10048
|
-
}
|
|
10049
|
-
}
|
|
10050
|
-
await store.create({
|
|
10051
|
-
tenantId,
|
|
10052
|
-
ownerType: data.ownerType || "user",
|
|
10053
|
-
ownerId: data.ownerId || "",
|
|
10054
|
-
title: data.title,
|
|
10055
|
-
description: data.description,
|
|
10056
|
-
status: data.status,
|
|
10057
|
-
priority: data.priority,
|
|
10058
|
-
dueDate: data.dueDate,
|
|
10059
|
-
metadata: data.metadata,
|
|
10060
|
-
parentId: data.parentId,
|
|
10061
|
-
sourceId: data.sourceId,
|
|
10062
|
-
context: data.context
|
|
10063
|
-
});
|
|
10064
|
-
return { newId: id };
|
|
10065
|
-
}
|
|
10066
|
-
};
|
|
10067
|
-
|
|
10068
|
-
// src/export_registrations/a2a-api-key.registration.ts
|
|
10069
|
-
import { getStoreLattice as getStoreLattice27 } from "@axiom-lattice/core";
|
|
10070
|
-
function getStore13() {
|
|
10071
|
-
return getStoreLattice27("default", "a2aApiKey").store;
|
|
10072
|
-
}
|
|
10073
|
-
var a2aApiKeyRegistration = {
|
|
10074
|
-
entityType: "a2a_api_key",
|
|
10075
|
-
label: "A2A API Key(API\u5BC6\u94A5)",
|
|
10076
|
-
category: "core",
|
|
10077
|
-
dependsOn: [],
|
|
10078
|
-
cascadeParents: [],
|
|
10079
|
-
async listForExport(tenantId) {
|
|
10080
|
-
const store = getStore13();
|
|
10081
|
-
const keys = await store.list({ tenantId, limit: 1e4, offset: 0 });
|
|
10082
|
-
return keys.map((k) => ({
|
|
10083
|
-
_exportId: "",
|
|
10084
|
-
data: {
|
|
10085
|
-
id: k.id,
|
|
10086
|
-
tenantId: k.tenantId,
|
|
10087
|
-
projectId: k.projectId,
|
|
10088
|
-
workspaceId: k.workspaceId,
|
|
10089
|
-
label: k.label,
|
|
10090
|
-
enabled: k.enabled
|
|
10091
|
-
}
|
|
10092
|
-
}));
|
|
10093
|
-
},
|
|
10094
|
-
async previewImport(tenantId, entities) {
|
|
10095
|
-
const store = getStore13();
|
|
10096
|
-
const existing = await store.list({ tenantId, limit: 1e4, offset: 0 });
|
|
10097
|
-
const existingIds = new Set(existing.map((k) => k.id));
|
|
10098
|
-
const conflicts = [];
|
|
10099
|
-
const insertions = [];
|
|
10100
|
-
for (const entity of entities) {
|
|
10101
|
-
const d = entity.data;
|
|
10102
|
-
const id = d.id;
|
|
10103
|
-
if (existingIds.has(id)) {
|
|
10104
|
-
conflicts.push({
|
|
10105
|
-
_exportId: entity._exportId,
|
|
10106
|
-
entityType: "a2a_api_key",
|
|
10107
|
-
conflictType: "id_exists",
|
|
10108
|
-
existingId: id,
|
|
10109
|
-
existingName: d.label || id
|
|
10110
|
-
});
|
|
10111
|
-
} else {
|
|
10112
|
-
insertions.push({
|
|
10113
|
-
_exportId: entity._exportId,
|
|
10114
|
-
entityType: "a2a_api_key",
|
|
10115
|
-
name: d.label || id
|
|
10116
|
-
});
|
|
10117
|
-
}
|
|
10118
|
-
}
|
|
10119
|
-
return { conflicts, insertions, errors: [] };
|
|
10120
|
-
},
|
|
10121
|
-
async applyImport(tenantId, entity, resolution) {
|
|
10122
|
-
if (resolution.action === "skip") return {};
|
|
10123
|
-
const store = getStore13();
|
|
10124
|
-
const d = entity.data;
|
|
10125
|
-
if (resolution.action === "overwrite") {
|
|
10126
|
-
try {
|
|
10127
|
-
await store.delete(d.id);
|
|
10128
|
-
} catch {
|
|
10129
|
-
}
|
|
10130
|
-
}
|
|
10131
|
-
await store.create({
|
|
10132
|
-
tenantId,
|
|
10133
|
-
projectId: d.projectId,
|
|
10134
|
-
workspaceId: d.workspaceId,
|
|
10135
|
-
label: d.label
|
|
10136
|
-
});
|
|
10137
|
-
return { newId: void 0 };
|
|
10138
|
-
}
|
|
10139
|
-
};
|
|
10140
|
-
|
|
10141
10704
|
// src/export_registrations/eval.registration.ts
|
|
10142
|
-
import { getStoreLattice as
|
|
10143
|
-
function
|
|
10144
|
-
return
|
|
10705
|
+
import { getStoreLattice as getStoreLattice24 } from "@axiom-lattice/core";
|
|
10706
|
+
function getStore11() {
|
|
10707
|
+
return getStoreLattice24("default", "eval").store;
|
|
10145
10708
|
}
|
|
10146
10709
|
var evalRegistration = {
|
|
10147
10710
|
entityType: "eval",
|
|
@@ -10151,7 +10714,7 @@ var evalRegistration = {
|
|
|
10151
10714
|
cascadeParents: [],
|
|
10152
10715
|
referenceFields: { suiteId: "eval_suite", projectId: "eval_project", runId: "eval_run", caseId: "eval_case" },
|
|
10153
10716
|
async listForExport(tenantId) {
|
|
10154
|
-
const store =
|
|
10717
|
+
const store = getStore11();
|
|
10155
10718
|
const projects = await store.getProjectsByTenant(tenantId);
|
|
10156
10719
|
const entities = [];
|
|
10157
10720
|
for (const project of projects) {
|
|
@@ -10181,19 +10744,19 @@ var evalRegistration = {
|
|
|
10181
10744
|
}
|
|
10182
10745
|
});
|
|
10183
10746
|
const cases = await store.getCasesBySuite(tenantId, suite.id);
|
|
10184
|
-
for (const
|
|
10747
|
+
for (const c2 of cases) {
|
|
10185
10748
|
entities.push({
|
|
10186
10749
|
_exportId: "",
|
|
10187
10750
|
data: {
|
|
10188
10751
|
_subType: "eval_case",
|
|
10189
|
-
id:
|
|
10190
|
-
suiteId:
|
|
10191
|
-
inputMessage:
|
|
10192
|
-
inputFiles:
|
|
10193
|
-
steps:
|
|
10194
|
-
outputType:
|
|
10195
|
-
contentAssertion:
|
|
10196
|
-
rubrics:
|
|
10752
|
+
id: c2.id,
|
|
10753
|
+
suiteId: c2.suiteId,
|
|
10754
|
+
inputMessage: c2.inputMessage,
|
|
10755
|
+
inputFiles: c2.inputFiles,
|
|
10756
|
+
steps: c2.steps,
|
|
10757
|
+
outputType: c2.outputType,
|
|
10758
|
+
contentAssertion: c2.contentAssertion,
|
|
10759
|
+
rubrics: c2.rubrics
|
|
10197
10760
|
}
|
|
10198
10761
|
});
|
|
10199
10762
|
}
|
|
@@ -10241,7 +10804,7 @@ var evalRegistration = {
|
|
|
10241
10804
|
return entities;
|
|
10242
10805
|
},
|
|
10243
10806
|
async previewImport(tenantId, entities) {
|
|
10244
|
-
const store =
|
|
10807
|
+
const store = getStore11();
|
|
10245
10808
|
const existingProjects = await store.getProjectsByTenant(tenantId);
|
|
10246
10809
|
const existingProjectIds = new Set(existingProjects.map((p) => p.id));
|
|
10247
10810
|
const existingProjectNames = new Set(existingProjects.map((p) => p.name));
|
|
@@ -10257,7 +10820,7 @@ var evalRegistration = {
|
|
|
10257
10820
|
existingSuiteIds.add(s.id);
|
|
10258
10821
|
try {
|
|
10259
10822
|
const cases = await store.getCasesBySuite(tenantId, s.id);
|
|
10260
|
-
for (const
|
|
10823
|
+
for (const c2 of cases) existingCaseIds.add(c2.id);
|
|
10261
10824
|
} catch {
|
|
10262
10825
|
}
|
|
10263
10826
|
}
|
|
@@ -10374,7 +10937,7 @@ var evalRegistration = {
|
|
|
10374
10937
|
},
|
|
10375
10938
|
async applyImport(tenantId, entity, resolution) {
|
|
10376
10939
|
if (resolution.action === "skip") return {};
|
|
10377
|
-
const store =
|
|
10940
|
+
const store = getStore11();
|
|
10378
10941
|
const d = entity.data;
|
|
10379
10942
|
const subType = d._subType;
|
|
10380
10943
|
const id = resolution.action === "rename" && resolution.newId ? resolution.newId : d.id;
|
|
@@ -10470,7 +11033,6 @@ function registerAllBuiltinEntities() {
|
|
|
10470
11033
|
const registry = ExportableEntityRegistry3.getInstance();
|
|
10471
11034
|
registry.register(skillRegistration);
|
|
10472
11035
|
registry.register(agentRegistration);
|
|
10473
|
-
registry.register(bindingRegistration);
|
|
10474
11036
|
registry.register(databaseConfigRegistration);
|
|
10475
11037
|
registry.register(metricsConfigRegistration);
|
|
10476
11038
|
registry.register(mcpConfigRegistration);
|
|
@@ -10478,23 +11040,33 @@ function registerAllBuiltinEntities() {
|
|
|
10478
11040
|
registry.register(collectionRegistration);
|
|
10479
11041
|
registry.register(channelInstallationRegistration);
|
|
10480
11042
|
registry.register(menuRegistration);
|
|
10481
|
-
registry.register(taskRegistration);
|
|
10482
|
-
registry.register(a2aApiKeyRegistration);
|
|
10483
11043
|
registry.register(evalRegistration);
|
|
10484
11044
|
}
|
|
10485
11045
|
|
|
10486
11046
|
// src/router/MessageRouter.ts
|
|
10487
11047
|
import {
|
|
10488
|
-
getStoreLattice as
|
|
11048
|
+
getStoreLattice as getStoreLattice25,
|
|
10489
11049
|
agentInstanceManager as agentInstanceManager6
|
|
10490
11050
|
} from "@axiom-lattice/core";
|
|
10491
|
-
import { randomUUID as
|
|
11051
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
10492
11052
|
var BindingNotFoundError = class extends Error {
|
|
10493
11053
|
constructor(message) {
|
|
10494
11054
|
super(message);
|
|
10495
11055
|
this.name = "BindingNotFoundError";
|
|
10496
11056
|
}
|
|
10497
11057
|
};
|
|
11058
|
+
var ChannelInstallationNotFoundError = class extends Error {
|
|
11059
|
+
constructor() {
|
|
11060
|
+
super("Channel installation is unavailable");
|
|
11061
|
+
this.name = "ChannelInstallationNotFoundError";
|
|
11062
|
+
}
|
|
11063
|
+
};
|
|
11064
|
+
var InvalidChannelInstallationError = class extends Error {
|
|
11065
|
+
constructor(message = "Channel installation is not valid for this message") {
|
|
11066
|
+
super(message);
|
|
11067
|
+
this.name = "InvalidChannelInstallationError";
|
|
11068
|
+
}
|
|
11069
|
+
};
|
|
10498
11070
|
var MessageRouter = class {
|
|
10499
11071
|
constructor(config) {
|
|
10500
11072
|
/**
|
|
@@ -10539,20 +11111,20 @@ var MessageRouter = class {
|
|
|
10539
11111
|
let threadId;
|
|
10540
11112
|
let agentId;
|
|
10541
11113
|
try {
|
|
11114
|
+
const installation = await this.installationStore.getInstallationById(
|
|
11115
|
+
message.channelInstallationId
|
|
11116
|
+
);
|
|
11117
|
+
if (!installation) {
|
|
11118
|
+
throw new ChannelInstallationNotFoundError();
|
|
11119
|
+
}
|
|
11120
|
+
if (installation.enabled !== true) {
|
|
11121
|
+
throw new InvalidChannelInstallationError("Channel installation is disabled");
|
|
11122
|
+
}
|
|
11123
|
+
if (installation.channel !== message.channel || message.tenantId !== void 0 && message.tenantId !== installation.tenantId) {
|
|
11124
|
+
throw new InvalidChannelInstallationError();
|
|
11125
|
+
}
|
|
11126
|
+
const tenantId = installation.tenantId;
|
|
10542
11127
|
await this.runMiddlewares(ctx, async () => {
|
|
10543
|
-
const tenantId = message.tenantId || (await this.installationStore.getInstallationById(message.channelInstallationId))?.tenantId;
|
|
10544
|
-
if (!tenantId) {
|
|
10545
|
-
console.error({
|
|
10546
|
-
event: "dispatch:error",
|
|
10547
|
-
error: "tenantId missing",
|
|
10548
|
-
channel: message.channel,
|
|
10549
|
-
channelInstallationId: message.channelInstallationId,
|
|
10550
|
-
senderId: message.sender.id
|
|
10551
|
-
}, "tenantId is required");
|
|
10552
|
-
throw new Error(
|
|
10553
|
-
"tenantId is required: provide it in the message or ensure the channelInstallation has a tenantId"
|
|
10554
|
-
);
|
|
10555
|
-
}
|
|
10556
11128
|
console.log({ event: "dispatch:start", channel: message.channel, senderId: message.sender.id, tenantId }, "Message dispatch started");
|
|
10557
11129
|
const adapter = this.adapterRegistry.get(message.channel);
|
|
10558
11130
|
const hasAdapterThreadStrategy = !!adapter?.resolveThreadId;
|
|
@@ -10562,11 +11134,8 @@ var MessageRouter = class {
|
|
|
10562
11134
|
channelInstallationId: message.channelInstallationId,
|
|
10563
11135
|
tenantId
|
|
10564
11136
|
});
|
|
10565
|
-
const installation = await this.installationStore.getInstallationById(
|
|
10566
|
-
message.channelInstallationId
|
|
10567
|
-
);
|
|
10568
11137
|
if (!binding) {
|
|
10569
|
-
if (installation
|
|
11138
|
+
if (installation.rejectWhenNoBinding) {
|
|
10570
11139
|
console.warn({
|
|
10571
11140
|
event: "dispatch:no_binding",
|
|
10572
11141
|
channel: message.channel,
|
|
@@ -10578,7 +11147,7 @@ var MessageRouter = class {
|
|
|
10578
11147
|
`No binding for sender "${message.sender.id}" on channel "${message.channel}"`
|
|
10579
11148
|
);
|
|
10580
11149
|
}
|
|
10581
|
-
if (installation
|
|
11150
|
+
if (installation.fallbackAgentId) {
|
|
10582
11151
|
console.warn({
|
|
10583
11152
|
event: "dispatch:fallback",
|
|
10584
11153
|
channel: message.channel,
|
|
@@ -10633,7 +11202,7 @@ var MessageRouter = class {
|
|
|
10633
11202
|
);
|
|
10634
11203
|
}
|
|
10635
11204
|
}
|
|
10636
|
-
agentId = binding?.agentId ?? installation
|
|
11205
|
+
agentId = binding?.agentId ?? installation.fallbackAgentId;
|
|
10637
11206
|
if (!agentId) {
|
|
10638
11207
|
throw new BindingNotFoundError(
|
|
10639
11208
|
`No agent configured for sender "${message.sender.id}"`
|
|
@@ -10648,7 +11217,7 @@ var MessageRouter = class {
|
|
|
10648
11217
|
channel: message.channel,
|
|
10649
11218
|
adapterChannel: adapter.channel
|
|
10650
11219
|
}, "Thread resolved by adapter strategy");
|
|
10651
|
-
const threadStore =
|
|
11220
|
+
const threadStore = getStoreLattice25("default", "thread").store;
|
|
10652
11221
|
try {
|
|
10653
11222
|
await threadStore.createThread(
|
|
10654
11223
|
tenantId,
|
|
@@ -10685,8 +11254,8 @@ var MessageRouter = class {
|
|
|
10685
11254
|
}
|
|
10686
11255
|
}
|
|
10687
11256
|
if (!threadId) {
|
|
10688
|
-
const threadStore =
|
|
10689
|
-
const newThreadId =
|
|
11257
|
+
const threadStore = getStoreLattice25("default", "thread").store;
|
|
11258
|
+
const newThreadId = randomUUID10();
|
|
10690
11259
|
console.log({
|
|
10691
11260
|
event: "dispatch:thread:create",
|
|
10692
11261
|
agentId,
|
|
@@ -10736,78 +11305,73 @@ var MessageRouter = class {
|
|
|
10736
11305
|
const replySubKey = `${threadId}:${message.replyTarget.adapterChannel}:reply`;
|
|
10737
11306
|
const adapter2 = this.adapterRegistry.get(message.replyTarget.adapterChannel);
|
|
10738
11307
|
if (adapter2) {
|
|
10739
|
-
const
|
|
10740
|
-
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
11308
|
+
const existing = this._replySubs.get(replySubKey);
|
|
11309
|
+
if (!existing || existing.count === 0) {
|
|
11310
|
+
const timer = setTimeout(() => {
|
|
11311
|
+
const entry = this._replySubs.get(replySubKey);
|
|
11312
|
+
if (entry) {
|
|
11313
|
+
this._replySubs.delete(replySubKey);
|
|
11314
|
+
console.warn({
|
|
11315
|
+
event: "dispatch:reply:timeout",
|
|
11316
|
+
threadId,
|
|
11317
|
+
channel: message.replyTarget.adapterChannel
|
|
11318
|
+
}, "Reply subscription timed out \u2014 no reply:ready fired within 1h");
|
|
11319
|
+
}
|
|
11320
|
+
}, 36e5);
|
|
11321
|
+
this._replySubs.set(replySubKey, { count: 1, timer });
|
|
11322
|
+
console.log({
|
|
11323
|
+
event: "dispatch:reply:subscribed",
|
|
11324
|
+
threadId,
|
|
11325
|
+
channel: message.replyTarget.adapterChannel,
|
|
11326
|
+
senderId: message.sender.id
|
|
11327
|
+
}, "Subscribed to reply:ready for thread");
|
|
11328
|
+
agent.subscribeOnce("reply:ready", (data) => {
|
|
11329
|
+
const messages = data.state?.values?.messages;
|
|
11330
|
+
const lastAI = messages?.filter((m) => m.type === "ai" || m.getType?.() === "ai").pop();
|
|
11331
|
+
const replyText = lastAI?.content ?? "";
|
|
11332
|
+
const entry = this._replySubs.get(replySubKey);
|
|
11333
|
+
if (entry) {
|
|
11334
|
+
entry.count--;
|
|
11335
|
+
if (entry.count <= 0) {
|
|
11336
|
+
clearTimeout(entry.timer);
|
|
10748
11337
|
this._replySubs.delete(replySubKey);
|
|
10749
|
-
console.warn({
|
|
10750
|
-
event: "dispatch:reply:timeout",
|
|
10751
|
-
threadId,
|
|
10752
|
-
channel: message.replyTarget.adapterChannel
|
|
10753
|
-
}, "Reply subscription timed out \u2014 no reply:ready fired within 1h");
|
|
10754
11338
|
}
|
|
10755
|
-
}
|
|
10756
|
-
|
|
10757
|
-
|
|
10758
|
-
|
|
10759
|
-
|
|
10760
|
-
|
|
10761
|
-
|
|
10762
|
-
|
|
10763
|
-
|
|
10764
|
-
const messages = data.state?.values?.messages;
|
|
10765
|
-
const lastAI = messages?.filter((m) => m.type === "ai" || m.getType?.() === "ai").pop();
|
|
10766
|
-
const replyText = lastAI?.content ?? "";
|
|
10767
|
-
const entry = this._replySubs.get(replySubKey);
|
|
10768
|
-
if (entry) {
|
|
10769
|
-
entry.count--;
|
|
10770
|
-
if (entry.count <= 0) {
|
|
10771
|
-
clearTimeout(entry.timer);
|
|
10772
|
-
this._replySubs.delete(replySubKey);
|
|
10773
|
-
}
|
|
10774
|
-
}
|
|
10775
|
-
if (replyText) {
|
|
11339
|
+
}
|
|
11340
|
+
if (replyText) {
|
|
11341
|
+
console.log({
|
|
11342
|
+
event: "dispatch:reply:sending",
|
|
11343
|
+
threadId,
|
|
11344
|
+
channel: message.replyTarget.adapterChannel,
|
|
11345
|
+
replyLength: replyText.length
|
|
11346
|
+
}, "Sending channel reply");
|
|
11347
|
+
adapter2.sendReply(message.replyTarget, { text: replyText }, installation).then(() => {
|
|
10776
11348
|
console.log({
|
|
10777
|
-
event: "dispatch:reply:
|
|
10778
|
-
threadId,
|
|
10779
|
-
channel: message.replyTarget.adapterChannel,
|
|
10780
|
-
replyLength: replyText.length
|
|
10781
|
-
}, "Sending channel reply");
|
|
10782
|
-
adapter2.sendReply(message.replyTarget, { text: replyText }, installation2).then(() => {
|
|
10783
|
-
console.log({
|
|
10784
|
-
event: "dispatch:reply:sent",
|
|
10785
|
-
threadId,
|
|
10786
|
-
channel: message.replyTarget.adapterChannel
|
|
10787
|
-
}, "Channel reply sent successfully");
|
|
10788
|
-
}).catch((err) => console.error({
|
|
10789
|
-
event: "dispatch:reply:failed",
|
|
10790
|
-
threadId,
|
|
10791
|
-
channel: message.replyTarget.adapterChannel,
|
|
10792
|
-
error: err instanceof Error ? err.message : String(err)
|
|
10793
|
-
}, "Failed to send channel reply"));
|
|
10794
|
-
} else {
|
|
10795
|
-
console.warn({
|
|
10796
|
-
event: "dispatch:reply:empty",
|
|
11349
|
+
event: "dispatch:reply:sent",
|
|
10797
11350
|
threadId,
|
|
10798
11351
|
channel: message.replyTarget.adapterChannel
|
|
10799
|
-
}, "
|
|
10800
|
-
}
|
|
10801
|
-
|
|
10802
|
-
|
|
10803
|
-
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
|
|
10807
|
-
|
|
10808
|
-
|
|
10809
|
-
|
|
10810
|
-
|
|
11352
|
+
}, "Channel reply sent successfully");
|
|
11353
|
+
}).catch((err) => console.error({
|
|
11354
|
+
event: "dispatch:reply:failed",
|
|
11355
|
+
threadId,
|
|
11356
|
+
channel: message.replyTarget.adapterChannel,
|
|
11357
|
+
error: err instanceof Error ? err.message : String(err)
|
|
11358
|
+
}, "Failed to send channel reply"));
|
|
11359
|
+
} else {
|
|
11360
|
+
console.warn({
|
|
11361
|
+
event: "dispatch:reply:empty",
|
|
11362
|
+
threadId,
|
|
11363
|
+
channel: message.replyTarget.adapterChannel
|
|
11364
|
+
}, "Agent produced no text output \u2014 skipping reply");
|
|
11365
|
+
}
|
|
11366
|
+
});
|
|
11367
|
+
} else {
|
|
11368
|
+
existing.count++;
|
|
11369
|
+
console.log({
|
|
11370
|
+
event: "dispatch:reply:incremented",
|
|
11371
|
+
threadId,
|
|
11372
|
+
channel: message.replyTarget.adapterChannel,
|
|
11373
|
+
count: existing.count
|
|
11374
|
+
}, "Incremented reply counter for thread (already subscribed)");
|
|
10811
11375
|
}
|
|
10812
11376
|
}
|
|
10813
11377
|
}
|
|
@@ -11111,7 +11675,7 @@ ${summary}`
|
|
|
11111
11675
|
console.log(
|
|
11112
11676
|
`\u5C06\u5728 ${delayMs}ms \u540E\u91CD\u8BD5\u4EFB\u52A1 (${nextRetryCount}/${maxRetries})`
|
|
11113
11677
|
);
|
|
11114
|
-
await new Promise((
|
|
11678
|
+
await new Promise((resolve2) => setTimeout(resolve2, delayMs));
|
|
11115
11679
|
return handleAgentTask(taskRequest, nextRetryCount);
|
|
11116
11680
|
}
|
|
11117
11681
|
if (callback_event) {
|
|
@@ -11291,7 +11855,7 @@ import {
|
|
|
11291
11855
|
getLoggerLattice,
|
|
11292
11856
|
loggerLatticeManager,
|
|
11293
11857
|
sandboxLatticeManager as sandboxLatticeManager2,
|
|
11294
|
-
getStoreLattice as
|
|
11858
|
+
getStoreLattice as getStoreLattice26,
|
|
11295
11859
|
agentInstanceManager as agentInstanceManager8,
|
|
11296
11860
|
createSandboxProvider,
|
|
11297
11861
|
TokenCache,
|
|
@@ -11341,26 +11905,8 @@ var getHeaderValue = (header) => {
|
|
|
11341
11905
|
}
|
|
11342
11906
|
return header;
|
|
11343
11907
|
};
|
|
11344
|
-
var
|
|
11345
|
-
app
|
|
11346
|
-
const user = extractUserFromAuthHeader(request.headers.authorization);
|
|
11347
|
-
if (user) {
|
|
11348
|
-
request.user = user;
|
|
11349
|
-
return;
|
|
11350
|
-
}
|
|
11351
|
-
const authRequired = process.env.AUTH_REQUIRED === "true";
|
|
11352
|
-
if (!authRequired) return;
|
|
11353
|
-
if (request.method === "OPTIONS") return;
|
|
11354
|
-
if (PUBLIC_ROUTES.some((r) => request.url === r)) return;
|
|
11355
|
-
if (request.url.startsWith("/s/")) return;
|
|
11356
|
-
const urlPath = request.url.split("?")[0];
|
|
11357
|
-
if (urlPath.includes("/viewfile") || urlPath.includes("/downloadfile") || urlPath.includes("/uploadfile")) return;
|
|
11358
|
-
if (urlPath.startsWith("/f/") || urlPath === "/f") return;
|
|
11359
|
-
return reply.status(401).send({
|
|
11360
|
-
success: false,
|
|
11361
|
-
error: "Unauthorized - Missing or invalid token"
|
|
11362
|
-
});
|
|
11363
|
-
});
|
|
11908
|
+
var authRequired = process.env.AUTH_REQUIRED === "true";
|
|
11909
|
+
registerConsoleAuthHook(app, { authRequired });
|
|
11364
11910
|
app.addHook("onRequest", (request, reply, done) => {
|
|
11365
11911
|
if (!request.headers["x-project-id"]) {
|
|
11366
11912
|
request.headers["x-project-id"] = "default";
|
|
@@ -11390,7 +11936,7 @@ app.register(cors, {
|
|
|
11390
11936
|
origin: true,
|
|
11391
11937
|
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
|
|
11392
11938
|
allowedHeaders: "*",
|
|
11393
|
-
exposedHeaders: ["Content-Type"],
|
|
11939
|
+
exposedHeaders: ["Content-Type", "Content-Disposition"],
|
|
11394
11940
|
credentials: true
|
|
11395
11941
|
});
|
|
11396
11942
|
app.register(sensible);
|
|
@@ -11444,14 +11990,14 @@ function getConfiguredSandboxProvider() {
|
|
|
11444
11990
|
}
|
|
11445
11991
|
async function restoreMcpConnections() {
|
|
11446
11992
|
try {
|
|
11447
|
-
const storeLattice =
|
|
11993
|
+
const storeLattice = getStoreLattice26("default", "mcp");
|
|
11448
11994
|
const store = storeLattice.store;
|
|
11449
11995
|
if (!store) {
|
|
11450
11996
|
logger4.info("MCP store not configured, skipping connection restoration");
|
|
11451
11997
|
return;
|
|
11452
11998
|
}
|
|
11453
11999
|
const configs = await store.getAllConfigs("default");
|
|
11454
|
-
const connectedConfigs = configs.filter((
|
|
12000
|
+
const connectedConfigs = configs.filter((c2) => c2.status === "connected");
|
|
11455
12001
|
if (connectedConfigs.length === 0) return;
|
|
11456
12002
|
logger4.info(`Restoring ${connectedConfigs.length} MCP server connections...`);
|
|
11457
12003
|
const { convertToConnection } = await import("./mcp-configs-SHRMQHIL.mjs");
|
|
@@ -11496,9 +12042,9 @@ var start = async (config) => {
|
|
|
11496
12042
|
const { wechatChannelAdapter } = await import("./WechatChannelAdapter-WSDKR4OA.mjs");
|
|
11497
12043
|
adapterRegistry.register(wechatChannelAdapter);
|
|
11498
12044
|
try {
|
|
11499
|
-
const { getStoreLattice:
|
|
11500
|
-
const bindingStore =
|
|
11501
|
-
const installationStore =
|
|
12045
|
+
const { getStoreLattice: getStore12 } = await import("@axiom-lattice/core");
|
|
12046
|
+
const bindingStore = getStore12("default", "channelBinding").store;
|
|
12047
|
+
const installationStore = getStore12("default", "channelInstallation").store;
|
|
11502
12048
|
setBindingRegistry(bindingStore);
|
|
11503
12049
|
const router = new MessageRouter({
|
|
11504
12050
|
middlewares: [
|
|
@@ -11511,14 +12057,6 @@ var start = async (config) => {
|
|
|
11511
12057
|
installationStore
|
|
11512
12058
|
});
|
|
11513
12059
|
channelDeps = { router, installationStore };
|
|
11514
|
-
try {
|
|
11515
|
-
const a2aKeyStore = getStore15("default", "a2aApiKey").store;
|
|
11516
|
-
const a2a = await import("./a2a-QMHEH3ZT.mjs");
|
|
11517
|
-
a2a.setA2AKeyStore(a2aKeyStore);
|
|
11518
|
-
await a2a.refreshStoreKeyMap();
|
|
11519
|
-
logger4.info("A2A key store initialized");
|
|
11520
|
-
} catch {
|
|
11521
|
-
}
|
|
11522
12060
|
} catch (err) {
|
|
11523
12061
|
logger4.warn("Channel infrastructure unavailable", {
|
|
11524
12062
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -11526,7 +12064,7 @@ var start = async (config) => {
|
|
|
11526
12064
|
}
|
|
11527
12065
|
setEvalRunService(evalRunner);
|
|
11528
12066
|
try {
|
|
11529
|
-
const menuStore =
|
|
12067
|
+
const menuStore = getStoreLattice26("default", "menu").store;
|
|
11530
12068
|
setMenuRegistry(menuStore);
|
|
11531
12069
|
logger4.info("Menu registry initialized");
|
|
11532
12070
|
} catch {
|
|
@@ -11536,13 +12074,44 @@ var start = async (config) => {
|
|
|
11536
12074
|
setChannelControllerDeps({ adapterRegistry, router: channelDeps.router });
|
|
11537
12075
|
}
|
|
11538
12076
|
registerLatticeRoutes(app, channelDeps);
|
|
12077
|
+
try {
|
|
12078
|
+
const { A2AAuthService, parseEnvKeys } = await import("./A2AAuthService-DK5X6VIL.mjs");
|
|
12079
|
+
const { registerA2AStandardRoutes } = await import("./a2a-standard-U2XYG45L.mjs");
|
|
12080
|
+
const a2a = await import("./a2a-7N3WDAOP.mjs");
|
|
12081
|
+
const a2aKeyStore = getStoreLattice26("default", "a2aApiKey").store;
|
|
12082
|
+
const assistantStore = getStoreLattice26("default", "assistant").store;
|
|
12083
|
+
const projectStore = getStoreLattice26("default", "project").store;
|
|
12084
|
+
const taskStore = getStoreLattice26("default", "task").store;
|
|
12085
|
+
const authService = new A2AAuthService({
|
|
12086
|
+
keyStore: a2aKeyStore,
|
|
12087
|
+
assistantStore,
|
|
12088
|
+
projectStore,
|
|
12089
|
+
envKeyMap: parseEnvKeys(process.env.A2A_API_KEYS || ""),
|
|
12090
|
+
allowUnauthenticated: process.env.A2A_ALLOW_UNAUTHENTICATED === "true"
|
|
12091
|
+
});
|
|
12092
|
+
await authService.refresh();
|
|
12093
|
+
a2a.setA2AKeyStore(a2aKeyStore);
|
|
12094
|
+
a2a.setA2AAuthService(authService);
|
|
12095
|
+
registerA2AStandardRoutes(app, {
|
|
12096
|
+
authService,
|
|
12097
|
+
getTaskStoreLattice: () => taskStore,
|
|
12098
|
+
getAssistantStore: () => assistantStore,
|
|
12099
|
+
getProjectStore: () => projectStore
|
|
12100
|
+
});
|
|
12101
|
+
a2a.registerA2ARoutes(app);
|
|
12102
|
+
logger4.info("A2A standard routes registered");
|
|
12103
|
+
} catch (err) {
|
|
12104
|
+
logger4.warn("A2A standard routes unavailable", {
|
|
12105
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12106
|
+
});
|
|
12107
|
+
}
|
|
11539
12108
|
if (!sandboxLatticeManager2.hasLattice("default")) {
|
|
11540
12109
|
sandboxLatticeManager2.registerLattice("default", getConfiguredSandboxProvider());
|
|
11541
12110
|
logger4.info("Registered sandbox manager from env configuration");
|
|
11542
12111
|
}
|
|
11543
12112
|
try {
|
|
11544
12113
|
const { ResourceController } = await import("./resources-VA7LSDKN.mjs");
|
|
11545
|
-
const sharedResourceStore =
|
|
12114
|
+
const sharedResourceStore = getStoreLattice26("default", "sharedResource").store;
|
|
11546
12115
|
const cache = new TokenCache();
|
|
11547
12116
|
const resourceController = new ResourceController({
|
|
11548
12117
|
store: sharedResourceStore,
|