@axiom-lattice/gateway 3.0.2 → 3.0.4

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.
Files changed (155) hide show
  1. package/dist/index.js +37 -9
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +37 -9
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +9 -6
  6. package/.eslintrc.json +0 -22
  7. package/.turbo/turbo-build.log +0 -46
  8. package/AGENTS.md +0 -112
  9. package/CHANGELOG.md +0 -1190
  10. package/LOGGER_CONFIG.md +0 -135
  11. package/RESUME_STREAM_CONTENT_BASED.md +0 -326
  12. package/RESUME_STREAM_README.md +0 -388
  13. package/jest.config.js +0 -72
  14. package/public/sdk/README.md +0 -695
  15. package/public/sdk/dashboard-engine-skill.md +0 -1122
  16. package/public/sdk/dashboard-single-file-spec.md +0 -357
  17. package/public/sdk/data-query-sdk-skill.md +0 -307
  18. package/public/sdk/data-query-sdk.d.ts +0 -252
  19. package/public/sdk/data-query-sdk.js +0 -970
  20. package/public/sdk/occupancy-dashboard.html +0 -363
  21. package/public/sdk/test-dashboard.html +0 -690
  22. package/scripts/seed-lark-installation.ts +0 -54
  23. package/src/__tests__/__mocks__/e2b.ts +0 -1
  24. package/src/__tests__/a2a.test.ts +0 -428
  25. package/src/__tests__/channel-installations.test.ts +0 -199
  26. package/src/__tests__/data-query.test.ts +0 -77
  27. package/src/__tests__/sandbox-provider-registration.test.ts +0 -74
  28. package/src/__tests__/setup.ts +0 -5
  29. package/src/__tests__/workspace.test.ts +0 -400
  30. package/src/channels/__tests__/routes.test.ts +0 -50
  31. package/src/channels/lark/LarkChannelAdapter.ts +0 -217
  32. package/src/channels/lark/README.md +0 -187
  33. package/src/channels/lark/__tests__/controller.test.ts +0 -136
  34. package/src/channels/lark/__tests__/mapping-service.test.ts +0 -118
  35. package/src/channels/lark/__tests__/parser.test.ts +0 -72
  36. package/src/channels/lark/__tests__/sender.test.ts +0 -37
  37. package/src/channels/lark/__tests__/verification.test.ts +0 -157
  38. package/src/channels/lark/controller.ts +0 -48
  39. package/src/channels/lark/mapping-service.ts +0 -138
  40. package/src/channels/lark/parser.ts +0 -68
  41. package/src/channels/lark/routes.ts +0 -22
  42. package/src/channels/lark/sender.ts +0 -58
  43. package/src/channels/lark/types.ts +0 -20
  44. package/src/channels/lark/verification.ts +0 -32
  45. package/src/channels/registry.ts +0 -20
  46. package/src/channels/routes.ts +0 -30
  47. package/src/channels/wechat/WechatChannelAdapter.ts +0 -294
  48. package/src/channels/wechat/__tests__/context-store.test.ts +0 -148
  49. package/src/channels/wechat/context-store.ts +0 -54
  50. package/src/channels/wechat/controller.ts +0 -59
  51. package/src/channels/wechat/routes.ts +0 -7
  52. package/src/channels/wechat/types.ts +0 -62
  53. package/src/channels/wechat/wechat-client.ts +0 -162
  54. package/src/config/local-a2a-templates.ts +0 -59
  55. package/src/config.ts +0 -124
  56. package/src/controllers/__tests__/assistant-local-a2a.test.ts +0 -147
  57. package/src/controllers/__tests__/local-a2a-templates.test.ts +0 -112
  58. package/src/controllers/__tests__/mime.test.ts +0 -74
  59. package/src/controllers/__tests__/resources.test.ts +0 -122
  60. package/src/controllers/__tests__/run.test.ts +0 -115
  61. package/src/controllers/__tests__/tasks.test.ts +0 -215
  62. package/src/controllers/__tests__/workflow-tracking.test.ts +0 -113
  63. package/src/controllers/agent_task.ts +0 -72
  64. package/src/controllers/assistant.ts +0 -490
  65. package/src/controllers/auth.ts +0 -524
  66. package/src/controllers/channel-bindings.ts +0 -135
  67. package/src/controllers/channel-installations.ts +0 -409
  68. package/src/controllers/collections.ts +0 -176
  69. package/src/controllers/config.ts +0 -126
  70. package/src/controllers/connections.ts +0 -181
  71. package/src/controllers/data-query.ts +0 -236
  72. package/src/controllers/database-configs.ts +0 -440
  73. package/src/controllers/embeddings.ts +0 -22
  74. package/src/controllers/entries.ts +0 -167
  75. package/src/controllers/eval.ts +0 -513
  76. package/src/controllers/export-import.ts +0 -166
  77. package/src/controllers/health.ts +0 -36
  78. package/src/controllers/local-a2a-templates.ts +0 -18
  79. package/src/controllers/local-a2a.ts +0 -149
  80. package/src/controllers/mcp-configs.ts +0 -673
  81. package/src/controllers/memory.ts +0 -257
  82. package/src/controllers/menu-items.ts +0 -114
  83. package/src/controllers/metrics-configs.ts +0 -1061
  84. package/src/controllers/middleware-types.ts +0 -15
  85. package/src/controllers/models.ts +0 -199
  86. package/src/controllers/personal-assistant.ts +0 -191
  87. package/src/controllers/resources.ts +0 -477
  88. package/src/controllers/run.ts +0 -338
  89. package/src/controllers/sandbox.ts +0 -255
  90. package/src/controllers/schedules.ts +0 -336
  91. package/src/controllers/skills.ts +0 -256
  92. package/src/controllers/tasks.ts +0 -213
  93. package/src/controllers/tenants.ts +0 -121
  94. package/src/controllers/thread_status.ts +0 -66
  95. package/src/controllers/threads.ts +0 -260
  96. package/src/controllers/tools.ts +0 -139
  97. package/src/controllers/users.ts +0 -135
  98. package/src/controllers/workflow-tracking.ts +0 -693
  99. package/src/controllers/workspace.ts +0 -879
  100. package/src/export_registrations/a2a-api-key.registration.ts +0 -89
  101. package/src/export_registrations/agent.registration.ts +0 -80
  102. package/src/export_registrations/binding.registration.ts +0 -106
  103. package/src/export_registrations/channel-installation.registration.ts +0 -88
  104. package/src/export_registrations/collection.registration.ts +0 -85
  105. package/src/export_registrations/connection.registration.ts +0 -97
  106. package/src/export_registrations/database-config.registration.ts +0 -94
  107. package/src/export_registrations/eval.registration.ts +0 -334
  108. package/src/export_registrations/index.ts +0 -31
  109. package/src/export_registrations/mcp-config.registration.ts +0 -97
  110. package/src/export_registrations/menu.registration.ts +0 -91
  111. package/src/export_registrations/metrics-config.registration.ts +0 -94
  112. package/src/export_registrations/skill.registration.ts +0 -88
  113. package/src/export_registrations/task.registration.ts +0 -99
  114. package/src/index.ts +0 -497
  115. package/src/logger/Logger.ts +0 -186
  116. package/src/router/MessageContext.ts +0 -14
  117. package/src/router/MessageRouter.ts +0 -490
  118. package/src/router/middlewares/auditLogger.ts +0 -34
  119. package/src/router/middlewares/deduplication.ts +0 -38
  120. package/src/router/middlewares/index.ts +0 -3
  121. package/src/router/middlewares/rateLimit.ts +0 -46
  122. package/src/routes/__tests__/a2a-test-connection.test.ts +0 -177
  123. package/src/routes/a2a-bridge.ts +0 -266
  124. package/src/routes/a2a-test-connection.ts +0 -126
  125. package/src/routes/a2a.ts +0 -851
  126. package/src/routes/channel-bindings.ts +0 -11
  127. package/src/routes/channel-installations.ts +0 -33
  128. package/src/routes/index.ts +0 -640
  129. package/src/routes/menu-items.ts +0 -10
  130. package/src/routes/resource-routes.ts +0 -35
  131. package/src/routes/stt.ts +0 -102
  132. package/src/schemas/data-query.ts +0 -69
  133. package/src/schemas/index.ts +0 -852
  134. package/src/services/ExportImportService.ts +0 -296
  135. package/src/services/__tests__/ExportImportService.test.ts +0 -130
  136. package/src/services/__tests__/eval-runner.test.ts +0 -131
  137. package/src/services/__tests__/local-a2a.test.ts +0 -124
  138. package/src/services/agent_task_consumer.ts +0 -348
  139. package/src/services/collection_service.ts +0 -258
  140. package/src/services/eval-runner.ts +0 -283
  141. package/src/services/local-a2a.ts +0 -354
  142. package/src/services/queue_service.ts +0 -104
  143. package/src/services/sandbox_service.ts +0 -223
  144. package/src/services/skill_service.ts +0 -97
  145. package/src/services/supabase.ts +0 -60
  146. package/src/swagger.ts +0 -77
  147. package/src/types/index.ts +0 -118
  148. package/src/types/koa.d.ts +0 -7
  149. package/src/utils/__tests__/local-a2a-runtime.test.ts +0 -77
  150. package/src/utils/__tests__/path-containment.test.ts +0 -35
  151. package/src/utils/a2a-url.ts +0 -20
  152. package/src/utils/local-a2a-runtime.ts +0 -107
  153. package/src/utils/mime.ts +0 -44
  154. package/src/utils/path-containment.ts +0 -38
  155. 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 };