@axiom-lattice/gateway 3.0.2 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +31 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +31 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -6
- package/.eslintrc.json +0 -22
- package/.turbo/turbo-build.log +0 -46
- package/AGENTS.md +0 -112
- package/CHANGELOG.md +0 -1190
- package/LOGGER_CONFIG.md +0 -135
- package/RESUME_STREAM_CONTENT_BASED.md +0 -326
- package/RESUME_STREAM_README.md +0 -388
- package/jest.config.js +0 -72
- package/public/sdk/README.md +0 -695
- package/public/sdk/dashboard-engine-skill.md +0 -1122
- package/public/sdk/dashboard-single-file-spec.md +0 -357
- package/public/sdk/data-query-sdk-skill.md +0 -307
- package/public/sdk/data-query-sdk.d.ts +0 -252
- package/public/sdk/data-query-sdk.js +0 -970
- package/public/sdk/occupancy-dashboard.html +0 -363
- package/public/sdk/test-dashboard.html +0 -690
- package/scripts/seed-lark-installation.ts +0 -54
- package/src/__tests__/__mocks__/e2b.ts +0 -1
- package/src/__tests__/a2a.test.ts +0 -428
- package/src/__tests__/channel-installations.test.ts +0 -199
- package/src/__tests__/data-query.test.ts +0 -77
- package/src/__tests__/sandbox-provider-registration.test.ts +0 -74
- package/src/__tests__/setup.ts +0 -5
- package/src/__tests__/workspace.test.ts +0 -400
- package/src/channels/__tests__/routes.test.ts +0 -50
- package/src/channels/lark/LarkChannelAdapter.ts +0 -217
- package/src/channels/lark/README.md +0 -187
- package/src/channels/lark/__tests__/controller.test.ts +0 -136
- package/src/channels/lark/__tests__/mapping-service.test.ts +0 -118
- package/src/channels/lark/__tests__/parser.test.ts +0 -72
- package/src/channels/lark/__tests__/sender.test.ts +0 -37
- package/src/channels/lark/__tests__/verification.test.ts +0 -157
- package/src/channels/lark/controller.ts +0 -48
- package/src/channels/lark/mapping-service.ts +0 -138
- package/src/channels/lark/parser.ts +0 -68
- package/src/channels/lark/routes.ts +0 -22
- package/src/channels/lark/sender.ts +0 -58
- package/src/channels/lark/types.ts +0 -20
- package/src/channels/lark/verification.ts +0 -32
- package/src/channels/registry.ts +0 -20
- package/src/channels/routes.ts +0 -30
- package/src/channels/wechat/WechatChannelAdapter.ts +0 -294
- package/src/channels/wechat/__tests__/context-store.test.ts +0 -148
- package/src/channels/wechat/context-store.ts +0 -54
- package/src/channels/wechat/controller.ts +0 -59
- package/src/channels/wechat/routes.ts +0 -7
- package/src/channels/wechat/types.ts +0 -62
- package/src/channels/wechat/wechat-client.ts +0 -162
- package/src/config/local-a2a-templates.ts +0 -59
- package/src/config.ts +0 -124
- package/src/controllers/__tests__/assistant-local-a2a.test.ts +0 -147
- package/src/controllers/__tests__/local-a2a-templates.test.ts +0 -112
- package/src/controllers/__tests__/mime.test.ts +0 -74
- package/src/controllers/__tests__/resources.test.ts +0 -122
- package/src/controllers/__tests__/run.test.ts +0 -115
- package/src/controllers/__tests__/tasks.test.ts +0 -215
- package/src/controllers/__tests__/workflow-tracking.test.ts +0 -113
- package/src/controllers/agent_task.ts +0 -72
- package/src/controllers/assistant.ts +0 -490
- package/src/controllers/auth.ts +0 -524
- package/src/controllers/channel-bindings.ts +0 -135
- package/src/controllers/channel-installations.ts +0 -409
- package/src/controllers/collections.ts +0 -176
- package/src/controllers/config.ts +0 -126
- package/src/controllers/connections.ts +0 -181
- package/src/controllers/data-query.ts +0 -236
- package/src/controllers/database-configs.ts +0 -440
- package/src/controllers/embeddings.ts +0 -22
- package/src/controllers/entries.ts +0 -167
- package/src/controllers/eval.ts +0 -513
- package/src/controllers/export-import.ts +0 -166
- package/src/controllers/health.ts +0 -36
- package/src/controllers/local-a2a-templates.ts +0 -18
- package/src/controllers/local-a2a.ts +0 -149
- package/src/controllers/mcp-configs.ts +0 -673
- package/src/controllers/memory.ts +0 -257
- package/src/controllers/menu-items.ts +0 -114
- package/src/controllers/metrics-configs.ts +0 -1061
- package/src/controllers/middleware-types.ts +0 -15
- package/src/controllers/models.ts +0 -199
- package/src/controllers/personal-assistant.ts +0 -191
- package/src/controllers/resources.ts +0 -477
- package/src/controllers/run.ts +0 -338
- package/src/controllers/sandbox.ts +0 -255
- package/src/controllers/schedules.ts +0 -336
- package/src/controllers/skills.ts +0 -256
- package/src/controllers/tasks.ts +0 -213
- package/src/controllers/tenants.ts +0 -121
- package/src/controllers/thread_status.ts +0 -66
- package/src/controllers/threads.ts +0 -260
- package/src/controllers/tools.ts +0 -139
- package/src/controllers/users.ts +0 -135
- package/src/controllers/workflow-tracking.ts +0 -693
- package/src/controllers/workspace.ts +0 -879
- package/src/export_registrations/a2a-api-key.registration.ts +0 -89
- package/src/export_registrations/agent.registration.ts +0 -80
- package/src/export_registrations/binding.registration.ts +0 -106
- package/src/export_registrations/channel-installation.registration.ts +0 -88
- package/src/export_registrations/collection.registration.ts +0 -85
- package/src/export_registrations/connection.registration.ts +0 -97
- package/src/export_registrations/database-config.registration.ts +0 -94
- package/src/export_registrations/eval.registration.ts +0 -334
- package/src/export_registrations/index.ts +0 -31
- package/src/export_registrations/mcp-config.registration.ts +0 -97
- package/src/export_registrations/menu.registration.ts +0 -91
- package/src/export_registrations/metrics-config.registration.ts +0 -94
- package/src/export_registrations/skill.registration.ts +0 -88
- package/src/export_registrations/task.registration.ts +0 -99
- package/src/index.ts +0 -497
- package/src/logger/Logger.ts +0 -186
- package/src/router/MessageContext.ts +0 -14
- package/src/router/MessageRouter.ts +0 -490
- package/src/router/middlewares/auditLogger.ts +0 -34
- package/src/router/middlewares/deduplication.ts +0 -38
- package/src/router/middlewares/index.ts +0 -3
- package/src/router/middlewares/rateLimit.ts +0 -46
- package/src/routes/__tests__/a2a-test-connection.test.ts +0 -177
- package/src/routes/a2a-bridge.ts +0 -266
- package/src/routes/a2a-test-connection.ts +0 -126
- package/src/routes/a2a.ts +0 -851
- package/src/routes/channel-bindings.ts +0 -11
- package/src/routes/channel-installations.ts +0 -33
- package/src/routes/index.ts +0 -640
- package/src/routes/menu-items.ts +0 -10
- package/src/routes/resource-routes.ts +0 -35
- package/src/routes/stt.ts +0 -102
- package/src/schemas/data-query.ts +0 -69
- package/src/schemas/index.ts +0 -852
- package/src/services/ExportImportService.ts +0 -296
- package/src/services/__tests__/ExportImportService.test.ts +0 -130
- package/src/services/__tests__/eval-runner.test.ts +0 -131
- package/src/services/__tests__/local-a2a.test.ts +0 -124
- package/src/services/agent_task_consumer.ts +0 -348
- package/src/services/collection_service.ts +0 -258
- package/src/services/eval-runner.ts +0 -283
- package/src/services/local-a2a.ts +0 -354
- package/src/services/queue_service.ts +0 -104
- package/src/services/sandbox_service.ts +0 -223
- package/src/services/skill_service.ts +0 -97
- package/src/services/supabase.ts +0 -60
- package/src/swagger.ts +0 -77
- package/src/types/index.ts +0 -118
- package/src/types/koa.d.ts +0 -7
- package/src/utils/__tests__/local-a2a-runtime.test.ts +0 -77
- package/src/utils/__tests__/path-containment.test.ts +0 -35
- package/src/utils/a2a-url.ts +0 -20
- package/src/utils/local-a2a-runtime.ts +0 -107
- package/src/utils/mime.ts +0 -44
- package/src/utils/path-containment.ts +0 -38
- package/tsconfig.json +0 -26
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
-
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
-
import type { SkillStore } from '@axiom-lattice/protocols';
|
|
4
|
-
|
|
5
|
-
function getStore(): SkillStore {
|
|
6
|
-
return getStoreLattice('default', 'skill').store as unknown as SkillStore;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const skillRegistration: ExportableEntityDefinition = {
|
|
10
|
-
entityType: 'skill',
|
|
11
|
-
label: 'Skill(技能)',
|
|
12
|
-
category: 'core',
|
|
13
|
-
dependsOn: [],
|
|
14
|
-
cascadeParents: [],
|
|
15
|
-
|
|
16
|
-
async listForExport(tenantId: string): Promise<ExportableEntity[]> {
|
|
17
|
-
const store = getStore();
|
|
18
|
-
const skills = await store.getAllSkills(tenantId);
|
|
19
|
-
return skills.map((s) => ({
|
|
20
|
-
_exportId: '',
|
|
21
|
-
data: {
|
|
22
|
-
id: s.id,
|
|
23
|
-
name: s.name,
|
|
24
|
-
description: s.description,
|
|
25
|
-
license: s.license,
|
|
26
|
-
compatibility: s.compatibility,
|
|
27
|
-
metadata: s.metadata,
|
|
28
|
-
content: s.content,
|
|
29
|
-
subSkills: s.subSkills,
|
|
30
|
-
},
|
|
31
|
-
}));
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
async previewImport(
|
|
35
|
-
tenantId: string,
|
|
36
|
-
entities: ExportableEntity[],
|
|
37
|
-
): Promise<ImportPreviewResult> {
|
|
38
|
-
const store = getStore();
|
|
39
|
-
const existing = await store.getAllSkills(tenantId);
|
|
40
|
-
const existingIds = new Set(existing.map((s) => s.id));
|
|
41
|
-
|
|
42
|
-
const conflicts: ImportPreviewResult['conflicts'] = [];
|
|
43
|
-
const insertions: ImportPreviewResult['insertions'] = [];
|
|
44
|
-
|
|
45
|
-
for (const entity of entities) {
|
|
46
|
-
const id = entity.data.id as string;
|
|
47
|
-
if (existingIds.has(id)) {
|
|
48
|
-
conflicts.push({
|
|
49
|
-
_exportId: entity._exportId,
|
|
50
|
-
entityType: 'skill',
|
|
51
|
-
conflictType: 'id_exists',
|
|
52
|
-
existingId: id,
|
|
53
|
-
existingName: (entity.data.name as string) || id,
|
|
54
|
-
});
|
|
55
|
-
} else {
|
|
56
|
-
insertions.push({
|
|
57
|
-
_exportId: entity._exportId,
|
|
58
|
-
entityType: 'skill',
|
|
59
|
-
name: (entity.data.name as string) || id,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return { conflicts, insertions, errors: [] };
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
async applyImport(tenantId, entity, resolution) {
|
|
68
|
-
const store = getStore();
|
|
69
|
-
const data = entity.data as Record<string, unknown>;
|
|
70
|
-
const id = resolution.action === 'rename' && resolution.newId ? resolution.newId : data.id as string;
|
|
71
|
-
|
|
72
|
-
if (resolution.action === 'overwrite') {
|
|
73
|
-
try { await store.deleteSkill(tenantId, id); } catch { /* may not exist */ }
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
await store.createSkill(tenantId, id, {
|
|
77
|
-
name: data.name as string,
|
|
78
|
-
description: data.description as string,
|
|
79
|
-
license: data.license as string | undefined,
|
|
80
|
-
compatibility: data.compatibility as string | undefined,
|
|
81
|
-
metadata: data.metadata as Record<string, string> | undefined,
|
|
82
|
-
content: data.content as string | undefined,
|
|
83
|
-
subSkills: data.subSkills as string[] | undefined,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
return { newId: id };
|
|
87
|
-
},
|
|
88
|
-
};
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
-
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
-
import type { TaskStore } from '@axiom-lattice/protocols';
|
|
4
|
-
|
|
5
|
-
function getStore(): TaskStore {
|
|
6
|
-
return getStoreLattice('default', 'task').store as unknown as TaskStore;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const taskRegistration: ExportableEntityDefinition = {
|
|
10
|
-
entityType: 'task',
|
|
11
|
-
label: 'Task(任务)',
|
|
12
|
-
category: 'core',
|
|
13
|
-
dependsOn: ['agent'],
|
|
14
|
-
cascadeParents: [],
|
|
15
|
-
referenceFields: { ownerId: 'agent', parentId: 'task' },
|
|
16
|
-
|
|
17
|
-
async listForExport(tenantId: string): Promise<ExportableEntity[]> {
|
|
18
|
-
const store = getStore();
|
|
19
|
-
const tasks = await store.list({ tenantId, limit: 10000, offset: 0 });
|
|
20
|
-
return tasks.map((t) => ({
|
|
21
|
-
_exportId: '',
|
|
22
|
-
data: {
|
|
23
|
-
id: t.id,
|
|
24
|
-
title: t.title,
|
|
25
|
-
description: t.description,
|
|
26
|
-
status: t.status,
|
|
27
|
-
priority: t.priority,
|
|
28
|
-
dueDate: t.dueDate,
|
|
29
|
-
ownerType: t.ownerType,
|
|
30
|
-
ownerId: t.ownerId,
|
|
31
|
-
metadata: t.metadata,
|
|
32
|
-
parentId: t.parentId,
|
|
33
|
-
sourceId: t.sourceId,
|
|
34
|
-
context: t.context,
|
|
35
|
-
},
|
|
36
|
-
}));
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
async previewImport(
|
|
40
|
-
tenantId: string,
|
|
41
|
-
entities: ExportableEntity[],
|
|
42
|
-
): Promise<ImportPreviewResult> {
|
|
43
|
-
const store = getStore();
|
|
44
|
-
const existing = await store.list({ tenantId, limit: 10000, offset: 0 });
|
|
45
|
-
const existingIds = new Set(existing.map((t) => t.id));
|
|
46
|
-
|
|
47
|
-
const conflicts: ImportPreviewResult['conflicts'] = [];
|
|
48
|
-
const insertions: ImportPreviewResult['insertions'] = [];
|
|
49
|
-
|
|
50
|
-
for (const entity of entities) {
|
|
51
|
-
const d = entity.data as Record<string, unknown>;
|
|
52
|
-
const id = d.id as string;
|
|
53
|
-
if (existingIds.has(id)) {
|
|
54
|
-
conflicts.push({
|
|
55
|
-
_exportId: entity._exportId,
|
|
56
|
-
entityType: 'task',
|
|
57
|
-
conflictType: 'id_exists',
|
|
58
|
-
existingId: id,
|
|
59
|
-
existingName: (d.title as string) || id,
|
|
60
|
-
});
|
|
61
|
-
} else {
|
|
62
|
-
insertions.push({
|
|
63
|
-
_exportId: entity._exportId,
|
|
64
|
-
entityType: 'task',
|
|
65
|
-
name: (d.title as string) || id,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return { conflicts, insertions, errors: [] };
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
async applyImport(tenantId, entity, resolution) {
|
|
74
|
-
const store = getStore();
|
|
75
|
-
const data = entity.data as Record<string, unknown>;
|
|
76
|
-
const id = resolution.action === 'rename' && resolution.newId ? resolution.newId : data.id as string;
|
|
77
|
-
|
|
78
|
-
if (resolution.action === 'overwrite') {
|
|
79
|
-
try { await store.delete(tenantId, id); } catch { /* may not exist */ }
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
await store.create({
|
|
83
|
-
tenantId,
|
|
84
|
-
ownerType: (data.ownerType as 'user' | 'agent') || 'user',
|
|
85
|
-
ownerId: data.ownerId as string || '',
|
|
86
|
-
title: data.title as string,
|
|
87
|
-
description: data.description as string | undefined,
|
|
88
|
-
status: data.status as 'pending' | 'in_progress' | 'completed' | 'cancelled' | undefined,
|
|
89
|
-
priority: data.priority as 'low' | 'medium' | 'high' | undefined,
|
|
90
|
-
dueDate: data.dueDate as string | undefined,
|
|
91
|
-
metadata: data.metadata as Record<string, unknown> | undefined,
|
|
92
|
-
parentId: data.parentId as string | undefined,
|
|
93
|
-
sourceId: data.sourceId as string | undefined,
|
|
94
|
-
context: data.context as Record<string, unknown> | undefined,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
return { newId: id };
|
|
98
|
-
},
|
|
99
|
-
};
|
package/src/index.ts
DELETED
|
@@ -1,497 +0,0 @@
|
|
|
1
|
-
import "dotenv/config";
|
|
2
|
-
import fastify from "fastify";
|
|
3
|
-
import cors from "@fastify/cors";
|
|
4
|
-
import multipart from "@fastify/multipart";
|
|
5
|
-
import sensible from "@fastify/sensible";
|
|
6
|
-
import websocket from "@fastify/websocket";
|
|
7
|
-
import staticPlugin from "@fastify/static";
|
|
8
|
-
import path from "path";
|
|
9
|
-
import { fileURLToPath } from "url";
|
|
10
|
-
import { registerLatticeRoutes } from "./routes";
|
|
11
|
-
import { registerResourceRoutes } from "./routes/resource-routes";
|
|
12
|
-
import { registerAllBuiltinEntities } from "./export_registrations";
|
|
13
|
-
import type { ChannelInstallationStore, BindingRegistry } from "@axiom-lattice/protocols";
|
|
14
|
-
import { MessageRouter } from "./router/MessageRouter";
|
|
15
|
-
import { ChannelAdapterRegistry } from "./channels/registry";
|
|
16
|
-
import { createDeduplicationMiddleware, createRateLimitMiddleware, createAuditLoggerMiddleware } from "./router/middlewares";
|
|
17
|
-
import { setBindingRegistry, setMenuRegistry, setEvalRunService } from "@axiom-lattice/core";
|
|
18
|
-
import { larkChannelAdapter } from "./channels/lark/LarkChannelAdapter";
|
|
19
|
-
import { extractUserFromAuthHeader } from "./controllers/auth";
|
|
20
|
-
import { setChannelControllerDeps } from "./controllers/channel-installations";
|
|
21
|
-
import { evalRunner } from "./services/eval-runner";
|
|
22
|
-
import { configureSwagger } from "./swagger";
|
|
23
|
-
import {
|
|
24
|
-
setQueueServiceType,
|
|
25
|
-
QueueServiceType,
|
|
26
|
-
} from "./services/queue_service";
|
|
27
|
-
import { AgentTaskConsumer } from "./services/agent_task_consumer";
|
|
28
|
-
import {
|
|
29
|
-
registerLoggerLattice,
|
|
30
|
-
getLoggerLattice,
|
|
31
|
-
loggerLatticeManager,
|
|
32
|
-
sandboxLatticeManager,
|
|
33
|
-
getStoreLattice,
|
|
34
|
-
agentInstanceManager,
|
|
35
|
-
createSandboxProvider,
|
|
36
|
-
type CreateSandboxProviderConfig,
|
|
37
|
-
TokenCache,
|
|
38
|
-
mcpManager,
|
|
39
|
-
} from "@axiom-lattice/core";
|
|
40
|
-
import {
|
|
41
|
-
LoggerType,
|
|
42
|
-
LoggerConfig,
|
|
43
|
-
PinoFileOptions,
|
|
44
|
-
McpServerConfigStore,
|
|
45
|
-
} from "@axiom-lattice/protocols";
|
|
46
|
-
process.on("unhandledRejection", (reason, promise) => {
|
|
47
|
-
console.error("未处理的Promise拒绝:", reason);
|
|
48
|
-
// 可以在这里进行日志记录或其他处理
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// Default logger configuration
|
|
52
|
-
const DEFAULT_LOGGER_CONFIG: LoggerConfig = {
|
|
53
|
-
name: "default",
|
|
54
|
-
description: "Default logger for lattice-gateway service",
|
|
55
|
-
type: LoggerType.PINO,
|
|
56
|
-
serviceName: "lattice/gateway",
|
|
57
|
-
loggerName: "lattice/gateway",
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
// Initialize logger with default config (can be overridden in start function)
|
|
61
|
-
let loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
|
|
62
|
-
let logger = loggerLattice.client;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Initialize logger lattice with given configuration
|
|
66
|
-
*/
|
|
67
|
-
function initializeLogger(config: LoggerConfig) {
|
|
68
|
-
// Remove existing logger if it exists
|
|
69
|
-
if (loggerLatticeManager.hasLattice("default")) {
|
|
70
|
-
loggerLatticeManager.removeLattice("default");
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Register logger with provided config
|
|
74
|
-
registerLoggerLattice("default", config);
|
|
75
|
-
|
|
76
|
-
// Get and return logger lattice instance
|
|
77
|
-
return getLoggerLattice("default");
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 创建 Fastify 应用
|
|
81
|
-
const app = fastify({
|
|
82
|
-
logger: false,
|
|
83
|
-
bodyLimit: Number(process.env.BODY_LIMIT) || 50 * 1024 * 1024,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// Custom content type parser to handle DELETE requests with Content-Type but no body
|
|
87
|
-
// This prevents "Body cannot be empty when content-type is set to 'application/json'" error
|
|
88
|
-
app.addContentTypeParser('application/json', { parseAs: 'string' }, function (request, body, done) {
|
|
89
|
-
// For DELETE requests or empty body, return empty object
|
|
90
|
-
if (request.method === 'DELETE' || !body || body.length === 0) {
|
|
91
|
-
done(null, {});
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
try {
|
|
95
|
-
const json = JSON.parse(body as string);
|
|
96
|
-
done(null, json);
|
|
97
|
-
} catch (err) {
|
|
98
|
-
done(err as Error, undefined);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// Helper: extract single header value (Fastify headers can be string | string[])
|
|
104
|
-
const getHeaderValue = (
|
|
105
|
-
header: string | string[] | undefined
|
|
106
|
-
): string | undefined => {
|
|
107
|
-
if (Array.isArray(header)) {
|
|
108
|
-
return header[0];
|
|
109
|
-
}
|
|
110
|
-
return header;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// Routes that don't require authentication even when AUTH_REQUIRED=true
|
|
114
|
-
const PUBLIC_ROUTES = ["/api/auth/login", "/api/auth/register", "/health", "/api/stt/models"];
|
|
115
|
-
|
|
116
|
-
// Global auth preHandler: extract user from Authorization header
|
|
117
|
-
// When AUTH_REQUIRED=true, reject unauthenticated requests (except public routes)
|
|
118
|
-
app.addHook("preHandler", async (request, reply) => {
|
|
119
|
-
const user = extractUserFromAuthHeader(request.headers.authorization);
|
|
120
|
-
if (user) {
|
|
121
|
-
(request as any).user = user;
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const authRequired = process.env.AUTH_REQUIRED === "true";
|
|
126
|
-
if (!authRequired) return;
|
|
127
|
-
|
|
128
|
-
// OPTIONS preflight should always pass
|
|
129
|
-
if (request.method === "OPTIONS") return;
|
|
130
|
-
|
|
131
|
-
// Public routes don't require authentication
|
|
132
|
-
if (PUBLIC_ROUTES.some((r) => request.url === r)) return;
|
|
133
|
-
|
|
134
|
-
// Public resource shares — /s/:token and /s/:token/*
|
|
135
|
-
if (request.url.startsWith("/s/")) return;
|
|
136
|
-
|
|
137
|
-
// Allow direct URL access for iframe endpoints (viewfile, downloadfile)
|
|
138
|
-
// These carry tenant context via query params and are accessed by iframes
|
|
139
|
-
const urlPath = request.url.split("?")[0];
|
|
140
|
-
if (urlPath.includes("/viewfile") || urlPath.includes("/downloadfile") || urlPath.includes("/uploadfile")) return;
|
|
141
|
-
|
|
142
|
-
// Internal file access — auth handled by controller (Referer check)
|
|
143
|
-
if (urlPath.startsWith("/f/") || urlPath === "/f") return;
|
|
144
|
-
|
|
145
|
-
return reply.status(401).send({
|
|
146
|
-
success: false,
|
|
147
|
-
error: "Unauthorized - Missing or invalid token",
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// Add default header values for project/workspace isolation
|
|
152
|
-
app.addHook("onRequest", (request, reply, done) => {
|
|
153
|
-
if (!request.headers["x-project-id"]) {
|
|
154
|
-
request.headers["x-project-id"] = "default";
|
|
155
|
-
}
|
|
156
|
-
done();
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Add custom logging hooks
|
|
160
|
-
app.addHook("onRequest", (request, reply, done) => {
|
|
161
|
-
const context = {
|
|
162
|
-
"x-tenant-id": getHeaderValue(request.headers["x-tenant-id"]),
|
|
163
|
-
"x-request-id": getHeaderValue(request.headers["x-request-id"]),
|
|
164
|
-
"x-user-id": getHeaderValue(request.headers["x-user-id"]),
|
|
165
|
-
};
|
|
166
|
-
// Update logger context for this request
|
|
167
|
-
if (loggerLattice.updateContext) {
|
|
168
|
-
loggerLattice.updateContext(context);
|
|
169
|
-
}
|
|
170
|
-
done();
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
app.addHook("onResponse", (request, reply, done) => {
|
|
174
|
-
const context = {
|
|
175
|
-
"x-tenant-id": getHeaderValue(request.headers["x-tenant-id"]),
|
|
176
|
-
"x-request-id": getHeaderValue(request.headers["x-request-id"]),
|
|
177
|
-
"x-user-id": getHeaderValue(request.headers["x-user-id"]),
|
|
178
|
-
};
|
|
179
|
-
//loggerLattice.info(`${request.method} ${request.url} - ${reply.statusCode}`);
|
|
180
|
-
done();
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// cors
|
|
184
|
-
app.register(cors, {
|
|
185
|
-
origin: true,
|
|
186
|
-
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
|
|
187
|
-
allowedHeaders: "*",
|
|
188
|
-
exposedHeaders: ["Content-Type"],
|
|
189
|
-
credentials: true,
|
|
190
|
-
});
|
|
191
|
-
app.register(sensible);
|
|
192
|
-
app.register(multipart, {
|
|
193
|
-
limits: {
|
|
194
|
-
fileSize: Number(process.env.BODY_LIMIT) || 50 * 1024 * 1024,
|
|
195
|
-
},
|
|
196
|
-
});
|
|
197
|
-
app.register(websocket);
|
|
198
|
-
|
|
199
|
-
// Register static file serving for SDK
|
|
200
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
201
|
-
const __dirname = path.dirname(__filename);
|
|
202
|
-
app.register(staticPlugin, {
|
|
203
|
-
root: path.join(__dirname, "../public"),
|
|
204
|
-
prefix: "/",
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// Error handler
|
|
208
|
-
app.setErrorHandler((error, request, reply) => {
|
|
209
|
-
const context = {
|
|
210
|
-
"x-tenant-id": getHeaderValue(request.headers["x-tenant-id"]),
|
|
211
|
-
"x-request-id": getHeaderValue(request.headers["x-request-id"]),
|
|
212
|
-
"x-user-id": getHeaderValue(request.headers["x-user-id"]),
|
|
213
|
-
};
|
|
214
|
-
logger.error(
|
|
215
|
-
`请求错误: ${request.method} ${request.url} error:${error.message}`,
|
|
216
|
-
{
|
|
217
|
-
...context,
|
|
218
|
-
error: error.message,
|
|
219
|
-
stack: error.stack,
|
|
220
|
-
statusCode: error.statusCode || 500,
|
|
221
|
-
}
|
|
222
|
-
);
|
|
223
|
-
reply.status(error.statusCode || 500).send({
|
|
224
|
-
success: false,
|
|
225
|
-
error: error.message || "服务器内部错误",
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
// Logger lattice will be decorated in start() function
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Logger configuration for gateway
|
|
233
|
-
*/
|
|
234
|
-
export interface GatewayLoggerConfig {
|
|
235
|
-
name?: string;
|
|
236
|
-
description?: string;
|
|
237
|
-
type?: LoggerType;
|
|
238
|
-
serviceName?: string;
|
|
239
|
-
loggerName?: string;
|
|
240
|
-
file?: string | PinoFileOptions;
|
|
241
|
-
context?: Record<string, any>;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Gateway configuration interface
|
|
245
|
-
export interface LatticeGatewayConfig {
|
|
246
|
-
port?: number;
|
|
247
|
-
queueServiceConfig?: {
|
|
248
|
-
type: QueueServiceType;
|
|
249
|
-
defaultStartPollingQueue: boolean;
|
|
250
|
-
};
|
|
251
|
-
loggerConfig?: Partial<GatewayLoggerConfig>; // Optional logger configuration to override defaults
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function getConfiguredSandboxProvider(): ReturnType<typeof createSandboxProvider> {
|
|
255
|
-
const sandboxProviderType =
|
|
256
|
-
(process.env.SANDBOX_PROVIDER_TYPE as CreateSandboxProviderConfig["type"] | undefined) ||
|
|
257
|
-
"microsandbox-remote";
|
|
258
|
-
|
|
259
|
-
return createSandboxProvider({
|
|
260
|
-
type: sandboxProviderType,
|
|
261
|
-
remoteBaseURL: process.env.SANDBOX_BASE_URL,
|
|
262
|
-
microsandboxServiceBaseURL: process.env.MICROSANDBOX_SERVICE_BASE_URL,
|
|
263
|
-
e2bApiKey: process.env.E2B_API_KEY,
|
|
264
|
-
e2bTemplate: process.env.E2B_TEMPLATE,
|
|
265
|
-
e2bTimeoutMs: process.env.E2B_TIMEOUT_MS
|
|
266
|
-
? parseInt(process.env.E2B_TIMEOUT_MS, 10)
|
|
267
|
-
: undefined,
|
|
268
|
-
daytonaApiKey: process.env.DAYTONA_API_KEY,
|
|
269
|
-
daytonaApiUrl: process.env.DAYTONA_API_URL,
|
|
270
|
-
daytonaTarget: process.env.DAYTONA_TARGET,
|
|
271
|
-
daytonaTimeout: process.env.DAYTONA_TIMEOUT
|
|
272
|
-
? parseInt(process.env.DAYTONA_TIMEOUT, 10)
|
|
273
|
-
: undefined,
|
|
274
|
-
daytonaVolumeName: process.env.DAYTONA_VOLUME_NAME,
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Restore MCP server connections that were connected before gateway shutdown.
|
|
280
|
-
* Loads configs with status "connected" from the store and reconnects them.
|
|
281
|
-
*/
|
|
282
|
-
async function restoreMcpConnections(): Promise<void> {
|
|
283
|
-
try {
|
|
284
|
-
const storeLattice = getStoreLattice("default", "mcp");
|
|
285
|
-
const store = storeLattice.store as McpServerConfigStore;
|
|
286
|
-
if (!store) {
|
|
287
|
-
logger.info("MCP store not configured, skipping connection restoration");
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const configs = await store.getAllConfigs("default");
|
|
292
|
-
const connectedConfigs = configs.filter((c) => c.status === "connected");
|
|
293
|
-
|
|
294
|
-
if (connectedConfigs.length === 0) return;
|
|
295
|
-
|
|
296
|
-
logger.info(`Restoring ${connectedConfigs.length} MCP server connections...`);
|
|
297
|
-
|
|
298
|
-
const { convertToConnection } = await import("./controllers/mcp-configs");
|
|
299
|
-
|
|
300
|
-
for (const config of connectedConfigs) {
|
|
301
|
-
try {
|
|
302
|
-
const connection = convertToConnection(config.config);
|
|
303
|
-
mcpManager.addServer(config.key, connection);
|
|
304
|
-
} catch (err) {
|
|
305
|
-
logger.warn(`Failed to prepare MCP server "${config.key}" for restoration`, { error: err });
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
await mcpManager.connect();
|
|
310
|
-
|
|
311
|
-
for (const config of connectedConfigs) {
|
|
312
|
-
try {
|
|
313
|
-
await mcpManager.registerToolsToToolLattice(config.key, config.selectedTools);
|
|
314
|
-
logger.info(`MCP server "${config.key}" restored with ${config.selectedTools.length} tools`);
|
|
315
|
-
} catch (err) {
|
|
316
|
-
logger.warn(`Failed to register tools for MCP server "${config.key}"`, { error: err });
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
logger.info(`MCP connection restoration complete: ${connectedConfigs.length} servers`);
|
|
321
|
-
} catch (err) {
|
|
322
|
-
logger.error("MCP connection restoration failed", { error: err });
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Start server
|
|
327
|
-
const start = async (config?: LatticeGatewayConfig) => {
|
|
328
|
-
try {
|
|
329
|
-
// Initialize or update logger configuration if provided
|
|
330
|
-
if (config?.loggerConfig) {
|
|
331
|
-
const loggerConfig: LoggerConfig = {
|
|
332
|
-
...DEFAULT_LOGGER_CONFIG,
|
|
333
|
-
...config.loggerConfig,
|
|
334
|
-
// Merge file config if provided
|
|
335
|
-
file: config.loggerConfig.file || DEFAULT_LOGGER_CONFIG.file,
|
|
336
|
-
};
|
|
337
|
-
loggerLattice = initializeLogger(loggerConfig);
|
|
338
|
-
logger = loggerLattice.client;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Decorate app with logger lattice (only once, in start function)
|
|
342
|
-
// Access via: request.server.loggerLattice or app.loggerLattice
|
|
343
|
-
app.decorate("loggerLattice", loggerLattice);
|
|
344
|
-
|
|
345
|
-
// Channel infrastructure: use stores from StoreLatticeManager.
|
|
346
|
-
// Applications (e.g. deep_research) register stores via configureStores()
|
|
347
|
-
// before calling startAsHttpEndpoint(). If no stores are registered,
|
|
348
|
-
// channel features are gracefully skipped.
|
|
349
|
-
let channelDeps: { router: MessageRouter; installationStore: ChannelInstallationStore } | undefined;
|
|
350
|
-
const adapterRegistry = new ChannelAdapterRegistry();
|
|
351
|
-
adapterRegistry.register(larkChannelAdapter);
|
|
352
|
-
const { wechatChannelAdapter } = await import("./channels/wechat/WechatChannelAdapter");
|
|
353
|
-
adapterRegistry.register(wechatChannelAdapter);
|
|
354
|
-
try {
|
|
355
|
-
const { getStoreLattice: getStore } = await import("@axiom-lattice/core");
|
|
356
|
-
const bindingStore = getStore("default", "channelBinding").store as BindingRegistry;
|
|
357
|
-
const installationStore = getStore("default", "channelInstallation").store as ChannelInstallationStore;
|
|
358
|
-
|
|
359
|
-
setBindingRegistry(bindingStore);
|
|
360
|
-
|
|
361
|
-
const router = new MessageRouter({
|
|
362
|
-
middlewares: [
|
|
363
|
-
createDeduplicationMiddleware(),
|
|
364
|
-
createRateLimitMiddleware(),
|
|
365
|
-
createAuditLoggerMiddleware(),
|
|
366
|
-
],
|
|
367
|
-
bindingRegistry: bindingStore,
|
|
368
|
-
adapterRegistry,
|
|
369
|
-
installationStore,
|
|
370
|
-
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
channelDeps = { router, installationStore };
|
|
374
|
-
|
|
375
|
-
// Initialize A2A API key store
|
|
376
|
-
try {
|
|
377
|
-
const a2aKeyStore = getStore("default", "a2aApiKey").store as import("@axiom-lattice/protocols").A2AApiKeyStore;
|
|
378
|
-
const a2a = await import("./routes/a2a");
|
|
379
|
-
a2a.setA2AKeyStore(a2aKeyStore);
|
|
380
|
-
await a2a.refreshStoreKeyMap();
|
|
381
|
-
logger.info("A2A key store initialized");
|
|
382
|
-
} catch {
|
|
383
|
-
// A2A key store optional — env-based keys still work
|
|
384
|
-
}
|
|
385
|
-
} catch (err) {
|
|
386
|
-
logger.warn("Channel infrastructure unavailable", {
|
|
387
|
-
error: err instanceof Error ? err.message : String(err),
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
setEvalRunService(evalRunner);
|
|
392
|
-
|
|
393
|
-
// Initialize MenuRegistry for custom menu items
|
|
394
|
-
try {
|
|
395
|
-
const menuStore = getStoreLattice("default", "menu").store;
|
|
396
|
-
setMenuRegistry(menuStore);
|
|
397
|
-
logger.info("Menu registry initialized");
|
|
398
|
-
} catch {
|
|
399
|
-
// Menu store optional — custom menus won't work without it
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Register export/import entity types before routes
|
|
403
|
-
registerAllBuiltinEntities();
|
|
404
|
-
|
|
405
|
-
// Register all routes (channel routes only active if channelDeps is set)
|
|
406
|
-
// Wire controller deps so enable/disable/delete can manage live connections
|
|
407
|
-
if (channelDeps?.router) {
|
|
408
|
-
setChannelControllerDeps({ adapterRegistry, router: channelDeps.router });
|
|
409
|
-
}
|
|
410
|
-
registerLatticeRoutes(app, channelDeps);
|
|
411
|
-
|
|
412
|
-
// Register sandbox manager if not already registered
|
|
413
|
-
if (!sandboxLatticeManager.hasLattice("default")) {
|
|
414
|
-
sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
|
|
415
|
-
logger.info("Registered sandbox manager from env configuration");
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Initialize resource share controller
|
|
419
|
-
try {
|
|
420
|
-
const { ResourceController } = await import("./controllers/resources");
|
|
421
|
-
const sharedResourceStore = getStoreLattice("default", "sharedResource").store;
|
|
422
|
-
const cache = new TokenCache();
|
|
423
|
-
const resourceController = new ResourceController({
|
|
424
|
-
store: sharedResourceStore,
|
|
425
|
-
sandboxManager: sandboxLatticeManager,
|
|
426
|
-
cache,
|
|
427
|
-
logger,
|
|
428
|
-
});
|
|
429
|
-
registerResourceRoutes(app, resourceController);
|
|
430
|
-
logger.info("Resource share routes registered");
|
|
431
|
-
} catch (err) {
|
|
432
|
-
logger.warn("Resource share infrastructure unavailable", {
|
|
433
|
-
error: err instanceof Error ? err.message : String(err),
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// Connect all enabled channel installations
|
|
438
|
-
if (channelDeps && process.env.CHANNELS_ENABLED !== "false") {
|
|
439
|
-
const { connectAllChannels } = await import("@axiom-lattice/core");
|
|
440
|
-
try {
|
|
441
|
-
await connectAllChannels(
|
|
442
|
-
(channel) => adapterRegistry.get(channel),
|
|
443
|
-
{ deps: { router: channelDeps.router } },
|
|
444
|
-
);
|
|
445
|
-
} catch (err) {
|
|
446
|
-
logger.error("Failed to start channel connections", {
|
|
447
|
-
error: err instanceof Error ? err.message : String(err),
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
const target_port = config?.port || Number(process.env.PORT) || 4001;
|
|
453
|
-
|
|
454
|
-
await app.listen({ port: target_port, host: "0.0.0.0" });
|
|
455
|
-
logger.info(`Lattice Gateway is running on port: ${target_port}`);
|
|
456
|
-
|
|
457
|
-
// Configure queue service
|
|
458
|
-
const queueServiceConfig = config?.queueServiceConfig;
|
|
459
|
-
if (queueServiceConfig) {
|
|
460
|
-
setQueueServiceType(queueServiceConfig.type);
|
|
461
|
-
if (queueServiceConfig.defaultStartPollingQueue) {
|
|
462
|
-
const agentTaskConsumer = new AgentTaskConsumer(target_port);
|
|
463
|
-
agentTaskConsumer.startPollingQueue();
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
// Restore agent instances with pending messages (fire-and-forget to avoid blocking startup)
|
|
468
|
-
// Temporarily disabled: do not auto-consume pending messages on startup.
|
|
469
|
-
if (process.env.AXIOM_RESTORE_PENDING_ON_STARTUP === "true") {
|
|
470
|
-
agentInstanceManager.restore()
|
|
471
|
-
.then((stats) => {
|
|
472
|
-
logger.info(`Agent recovery complete: ${stats.restored} threads restored, ${stats.errors} errors`);
|
|
473
|
-
})
|
|
474
|
-
.catch((error) => {
|
|
475
|
-
logger.error("Agent recovery failed", { error });
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// Restore MCP server connections that were connected before shutdown
|
|
480
|
-
restoreMcpConnections().catch((error) => {
|
|
481
|
-
logger.error("MCP connection restoration failed", { error });
|
|
482
|
-
});
|
|
483
|
-
} catch (err) {
|
|
484
|
-
logger.error("Server start failed", { error: err });
|
|
485
|
-
process.exit(1);
|
|
486
|
-
}
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
const LatticeGateway = {
|
|
490
|
-
startAsHttpEndpoint: start,
|
|
491
|
-
configureSwagger,
|
|
492
|
-
registerLatticeRoutes,
|
|
493
|
-
app,
|
|
494
|
-
AgentTaskConsumer,
|
|
495
|
-
};
|
|
496
|
-
|
|
497
|
-
export { LatticeGateway };
|