@axiom-lattice/gateway 2.1.52 → 2.1.54

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 CHANGED
@@ -623,11 +623,13 @@ async function getThreadList(request, reply) {
623
623
  const tenantId = getTenantId2(request);
624
624
  const { assistantId } = request.params;
625
625
  const metadataFilter = {};
626
- if (request.query.workspaceId) {
627
- metadataFilter.workspaceId = request.query.workspaceId;
626
+ const workspaceId = request.headers["x-workspace-id"];
627
+ const projectId = request.headers["x-project-id"];
628
+ if (workspaceId) {
629
+ metadataFilter.workspaceId = workspaceId;
628
630
  }
629
- if (request.query.projectId) {
630
- metadataFilter.projectId = request.query.projectId;
631
+ if (projectId) {
632
+ metadataFilter.projectId = projectId;
631
633
  }
632
634
  const storeLattice = (0, import_core6.getStoreLattice)("default", "thread");
633
635
  const threadStore = storeLattice.store;
@@ -4892,6 +4894,695 @@ function registerAuthRoutes(app2, config) {
4892
4894
  );
4893
4895
  }
4894
4896
 
4897
+ // src/channels/lark/routes.ts
4898
+ var import_core27 = require("@axiom-lattice/core");
4899
+ var import_pg_stores = require("@axiom-lattice/pg-stores");
4900
+
4901
+ // src/channels/lark/parser.ts
4902
+ function parseLarkMessageEvent(payload) {
4903
+ const raw = payload;
4904
+ if (raw.header?.event_type !== "im.message.receive_v1") {
4905
+ return null;
4906
+ }
4907
+ const message = raw.event?.message;
4908
+ const openId = raw.event?.sender?.sender_id?.open_id;
4909
+ if (!message || !openId || message.message_type !== "text") {
4910
+ return null;
4911
+ }
4912
+ const parsedContent = parseLarkTextContent(message.content);
4913
+ if (!message.message_id || !message.chat_id || !parsedContent) {
4914
+ return null;
4915
+ }
4916
+ return {
4917
+ messageId: message.message_id,
4918
+ openId,
4919
+ chatId: message.chat_id,
4920
+ chatType: normalizeChatType(message.chat_type),
4921
+ text: parsedContent
4922
+ };
4923
+ }
4924
+ function parseLarkTextContent(content) {
4925
+ if (!content) {
4926
+ return null;
4927
+ }
4928
+ try {
4929
+ const parsed = JSON.parse(content);
4930
+ return typeof parsed.text === "string" ? parsed.text : null;
4931
+ } catch {
4932
+ return null;
4933
+ }
4934
+ }
4935
+ function normalizeChatType(chatType) {
4936
+ return chatType === "p2p" ? "direct" : "group";
4937
+ }
4938
+
4939
+ // src/channels/lark/controller.ts
4940
+ function createLarkEventHandler(dependencies) {
4941
+ return async function handleLarkEvent2(request, reply) {
4942
+ const installationId = request.params?.installationId;
4943
+ if (!installationId) {
4944
+ reply.status(400).send({ success: false, message: "Missing installationId" });
4945
+ return;
4946
+ }
4947
+ const config = await dependencies.getInstallationConfig(installationId);
4948
+ if (!config) {
4949
+ reply.status(404).send({ success: false, message: "Lark installation not found" });
4950
+ return;
4951
+ }
4952
+ const body = dependencies.parseRequestBody(
4953
+ request.body,
4954
+ config.encryptKey
4955
+ );
4956
+ if (!dependencies.verifyParsedBody(body, config)) {
4957
+ reply.status(401).send({ success: false, message: "Invalid Lark request" });
4958
+ return;
4959
+ }
4960
+ if (body.type === "url_verification" && body.challenge) {
4961
+ reply.status(200).send({ challenge: body.challenge });
4962
+ return;
4963
+ }
4964
+ const parsed = dependencies.parseEvent(request.body);
4965
+ if (!parsed) {
4966
+ reply.status(200).send({ success: true, ignored: true });
4967
+ return;
4968
+ }
4969
+ const receipt = await dependencies.claimInboundReceipt({
4970
+ channel: "lark",
4971
+ channelAppId: config.appId,
4972
+ externalMessageId: parsed.messageId,
4973
+ tenantId: config.tenantId
4974
+ });
4975
+ if (!receipt.accepted) {
4976
+ reply.status(200).send(
4977
+ receipt.status === "processing" ? { success: true, processing: true } : { success: true, duplicate: true }
4978
+ );
4979
+ return;
4980
+ }
4981
+ try {
4982
+ const { threadId } = await dependencies.resolveThread({
4983
+ channel: "lark",
4984
+ channelAppId: config.appId,
4985
+ tenantId: config.tenantId,
4986
+ assistantId: config.assistantId,
4987
+ mappingMode: config.mappingMode,
4988
+ openId: parsed.openId,
4989
+ chatId: parsed.chatId,
4990
+ chatType: parsed.chatType,
4991
+ messageId: parsed.messageId,
4992
+ workspaceId: config.workspaceId,
4993
+ projectId: config.projectId
4994
+ });
4995
+ const text = await dependencies.runAgentAndCollectText({
4996
+ tenantId: config.tenantId,
4997
+ assistantId: config.assistantId,
4998
+ threadId,
4999
+ text: parsed.text,
5000
+ workspaceId: config.workspaceId,
5001
+ projectId: config.projectId
5002
+ });
5003
+ await dependencies.sendTextReply({
5004
+ chatId: parsed.chatId,
5005
+ text,
5006
+ config
5007
+ });
5008
+ await dependencies.markInboundReceiptCompleted({
5009
+ channel: "lark",
5010
+ channelAppId: config.appId,
5011
+ externalMessageId: parsed.messageId,
5012
+ tenantId: config.tenantId,
5013
+ threadId
5014
+ });
5015
+ reply.status(200).send({ success: true, threadId });
5016
+ } catch (error) {
5017
+ await dependencies.markInboundReceiptFailed({
5018
+ channel: "lark",
5019
+ channelAppId: config.appId,
5020
+ externalMessageId: parsed.messageId,
5021
+ tenantId: config.tenantId
5022
+ });
5023
+ throw error;
5024
+ }
5025
+ };
5026
+ }
5027
+ var handleLarkEvent = createLarkEventHandler({
5028
+ getInstallationConfig: async () => null,
5029
+ parseRequestBody: (body) => body || {},
5030
+ verifyParsedBody: () => true,
5031
+ parseEvent: parseLarkMessageEvent,
5032
+ claimInboundReceipt: async () => ({ accepted: true, status: "processing" }),
5033
+ markInboundReceiptCompleted: async () => void 0,
5034
+ markInboundReceiptFailed: async () => void 0,
5035
+ resolveThread: async () => ({ threadId: "" }),
5036
+ runAgentAndCollectText: async () => "",
5037
+ sendTextReply: async () => void 0
5038
+ });
5039
+
5040
+ // src/channels/lark/config.ts
5041
+ function loadLarkIngressConfig() {
5042
+ return {
5043
+ enabled: process.env.LARK_ENABLED !== "false",
5044
+ appId: process.env.LARK_APP_ID || "",
5045
+ appSecret: process.env.LARK_APP_SECRET || "",
5046
+ verificationToken: process.env.LARK_VERIFICATION_TOKEN,
5047
+ encryptKey: process.env.LARK_ENCRYPT_KEY,
5048
+ tenantId: process.env.LARK_TENANT_ID || "default",
5049
+ assistantId: process.env.LARK_ASSISTANT_ID || "default_agent",
5050
+ workspaceId: process.env.LARK_WORKSPACE_ID,
5051
+ projectId: process.env.LARK_PROJECT_ID,
5052
+ mappingMode: process.env.LARK_MAPPING_MODE || "hybrid"
5053
+ };
5054
+ }
5055
+ function isLarkIngressEnabled(config) {
5056
+ return config.enabled;
5057
+ }
5058
+
5059
+ // src/channels/lark/mapping-service.ts
5060
+ var import_crypto6 = require("crypto");
5061
+ function createChannelThreadMappingService(deps) {
5062
+ return {
5063
+ async getOrCreateThread(input) {
5064
+ const externalSubjectKey = buildExternalSubjectKey(input);
5065
+ const existing = await deps.mappingStore.getMappingBySubject({
5066
+ channel: input.channel,
5067
+ channelAppId: input.channelAppId,
5068
+ tenantId: input.tenantId,
5069
+ assistantId: input.assistantId,
5070
+ externalSubjectKey
5071
+ });
5072
+ if (existing) {
5073
+ return { threadId: existing.threadId };
5074
+ }
5075
+ const threadId = (deps.uuid || import_crypto6.randomUUID)();
5076
+ await deps.threadStore.createThread(input.tenantId, input.assistantId, threadId, {
5077
+ metadata: {
5078
+ tenantId: input.tenantId,
5079
+ workspaceId: input.workspaceId,
5080
+ projectId: input.projectId,
5081
+ channel: input.channel,
5082
+ larkChatId: input.chatId,
5083
+ larkOpenId: input.openId
5084
+ }
5085
+ });
5086
+ try {
5087
+ await deps.mappingStore.createMapping({
5088
+ channel: input.channel,
5089
+ channelAppId: input.channelAppId,
5090
+ tenantId: input.tenantId,
5091
+ assistantId: input.assistantId,
5092
+ mappingMode: input.mappingMode,
5093
+ externalSubjectType: resolveSubjectType(input.mappingMode, input.chatType) === "user" ? "user" : "chat",
5094
+ externalSubjectKey,
5095
+ larkOpenId: input.openId,
5096
+ larkChatId: input.chatId,
5097
+ larkMessageId: input.messageId,
5098
+ threadId
5099
+ });
5100
+ } catch (error) {
5101
+ if (!isUniqueViolation(error)) {
5102
+ throw error;
5103
+ }
5104
+ const canonical = await deps.mappingStore.getMappingBySubject({
5105
+ channel: input.channel,
5106
+ channelAppId: input.channelAppId,
5107
+ tenantId: input.tenantId,
5108
+ assistantId: input.assistantId,
5109
+ externalSubjectKey
5110
+ });
5111
+ if (!canonical) {
5112
+ throw error;
5113
+ }
5114
+ await deps.threadStore.deleteThread(input.tenantId, threadId);
5115
+ return { threadId: canonical.threadId };
5116
+ }
5117
+ return { threadId };
5118
+ }
5119
+ };
5120
+ }
5121
+ function isUniqueViolation(error) {
5122
+ return typeof error === "object" && error !== null && "code" in error && error.code === "23505";
5123
+ }
5124
+ function buildExternalSubjectKey(input) {
5125
+ const subjectType = resolveSubjectType(input.mappingMode, input.chatType);
5126
+ const subjectValue = subjectType === "user" ? input.openId : input.chatId;
5127
+ return [
5128
+ input.channel,
5129
+ input.channelAppId,
5130
+ `tenant:${input.tenantId}`,
5131
+ `assistant:${input.assistantId}`,
5132
+ `${subjectType}:${subjectValue}`
5133
+ ].join(":");
5134
+ }
5135
+ function resolveSubjectType(mappingMode, chatType) {
5136
+ if (mappingMode === "user") {
5137
+ return "user";
5138
+ }
5139
+ if (mappingMode === "group") {
5140
+ return "chat";
5141
+ }
5142
+ return chatType === "direct" ? "user" : "chat";
5143
+ }
5144
+
5145
+ // src/channels/lark/runner.ts
5146
+ var import_core26 = require("@axiom-lattice/core");
5147
+ var import_protocols4 = require("@axiom-lattice/protocols");
5148
+
5149
+ // src/channels/lark/aggregator.ts
5150
+ var import_protocols3 = require("@axiom-lattice/protocols");
5151
+ function aggregateLarkReply(messageId, chunks) {
5152
+ return chunks.filter(
5153
+ (chunk) => chunk.type === import_protocols3.MessageChunkTypes.AI && chunk.data.id === messageId
5154
+ ).map((chunk) => chunk.data.content || "").join("").trim();
5155
+ }
5156
+
5157
+ // src/channels/lark/runner.ts
5158
+ async function runAgentAndCollectLarkReply(input) {
5159
+ const agent = import_core26.agentInstanceManager.getAgent({
5160
+ tenant_id: input.tenantId,
5161
+ assistant_id: input.assistantId,
5162
+ thread_id: input.threadId,
5163
+ workspace_id: input.workspaceId,
5164
+ project_id: input.projectId
5165
+ });
5166
+ const result = await agent.addMessage({
5167
+ input: {
5168
+ message: input.text
5169
+ }
5170
+ });
5171
+ const chunks = [];
5172
+ const stream = agent.chunkStream(result.messageId, [
5173
+ import_protocols4.MessageChunkTypes.MESSAGE_COMPLETED
5174
+ ]);
5175
+ for await (const chunk of stream) {
5176
+ chunks.push(chunk);
5177
+ }
5178
+ return aggregateLarkReply(result.messageId, chunks);
5179
+ }
5180
+
5181
+ // src/channels/lark/sender.ts
5182
+ function createLarkSender(config, client = createDefaultLarkClient(config)) {
5183
+ return {
5184
+ async sendTextReply(input) {
5185
+ const response = await client.im.v1.message.create({
5186
+ params: {
5187
+ receive_id_type: "chat_id"
5188
+ },
5189
+ data: {
5190
+ receive_id: input.chatId,
5191
+ msg_type: "text",
5192
+ content: JSON.stringify({ text: input.text })
5193
+ }
5194
+ });
5195
+ if (response.code && response.code !== 0) {
5196
+ throw new Error("Failed to send Lark reply");
5197
+ }
5198
+ }
5199
+ };
5200
+ }
5201
+ function createDefaultLarkClient(config) {
5202
+ const Lark = require("@larksuiteoapi/node-sdk");
5203
+ return new Lark.Client({
5204
+ appId: config.appId,
5205
+ appSecret: config.appSecret
5206
+ });
5207
+ }
5208
+
5209
+ // src/channels/lark/verification.ts
5210
+ var import_crypto7 = __toESM(require("crypto"));
5211
+ function parseLarkRequestBody(body, encryptKey) {
5212
+ const parsed = body || {};
5213
+ if (encryptKey && typeof parsed.encrypt === "string") {
5214
+ return decryptLarkPayload(encryptKey, parsed.encrypt);
5215
+ }
5216
+ return parsed;
5217
+ }
5218
+ function decryptLarkPayload(encryptKey, encryptedPayload) {
5219
+ const key = import_crypto7.default.createHash("sha256").update(encryptKey).digest();
5220
+ const buffer = Buffer.from(encryptedPayload, "base64");
5221
+ const iv = buffer.subarray(0, 16);
5222
+ const ciphertext = buffer.subarray(16);
5223
+ const decipher = import_crypto7.default.createDecipheriv("aes-256-cbc", key, iv);
5224
+ const plaintext = Buffer.concat([
5225
+ decipher.update(ciphertext),
5226
+ decipher.final()
5227
+ ]).toString("utf8");
5228
+ return JSON.parse(plaintext);
5229
+ }
5230
+ function createLarkRequestVerifier(config) {
5231
+ return function verifyRequest(request) {
5232
+ const body = parseLarkRequestBody(request.body, config.encryptKey);
5233
+ return verifyLarkParsedBody(body, config);
5234
+ };
5235
+ }
5236
+ function verifyLarkParsedBody(body, config) {
5237
+ if (!config.verificationToken) {
5238
+ return true;
5239
+ }
5240
+ return extractVerificationToken(body) === config.verificationToken;
5241
+ }
5242
+ function extractVerificationToken(body) {
5243
+ if (typeof body.token === "string") {
5244
+ return body.token;
5245
+ }
5246
+ if (typeof body.header?.token === "string") {
5247
+ return body.header.token;
5248
+ }
5249
+ return void 0;
5250
+ }
5251
+
5252
+ // src/channels/lark/routes.ts
5253
+ function registerLarkChannelRoutes(app2, dependencies) {
5254
+ const config = loadLarkIngressConfig();
5255
+ if (!dependencies && !isLarkIngressEnabled(config)) {
5256
+ return;
5257
+ }
5258
+ const handlerDependencies = dependencies || createDefaultLarkDependencies();
5259
+ app2.post(
5260
+ "/api/channels/lark/installations/:installationId/events",
5261
+ createLarkEventHandler({
5262
+ ...handlerDependencies
5263
+ })
5264
+ );
5265
+ }
5266
+ function createDefaultLarkDependencies() {
5267
+ const installationStore = new import_pg_stores.PostgreSQLChannelInstallationStore({
5268
+ poolConfig: getDatabaseUrl()
5269
+ });
5270
+ const threadStore = (0, import_core27.getStoreLattice)("default", "thread").store;
5271
+ const mappingStore = new import_pg_stores.ChannelIdentityMappingStore({
5272
+ poolConfig: getDatabaseUrl()
5273
+ });
5274
+ const mappingService = createChannelThreadMappingService({
5275
+ mappingStore,
5276
+ threadStore
5277
+ });
5278
+ return {
5279
+ getInstallationConfig: async (installationId) => {
5280
+ const installation = await installationStore.getInstallationById(
5281
+ installationId
5282
+ );
5283
+ if (!installation || installation.channel !== "lark") {
5284
+ return null;
5285
+ }
5286
+ return {
5287
+ enabled: true,
5288
+ installationId: installation.id,
5289
+ tenantId: installation.tenantId,
5290
+ assistantId: installation.config.assistantId,
5291
+ appId: installation.config.appId,
5292
+ appSecret: installation.config.appSecret,
5293
+ verificationToken: installation.config.verificationToken,
5294
+ encryptKey: installation.config.encryptKey,
5295
+ workspaceId: installation.config.workspaceId,
5296
+ projectId: installation.config.projectId,
5297
+ mappingMode: installation.config.mappingMode
5298
+ };
5299
+ },
5300
+ parseRequestBody: (body, encryptKey) => parseLarkRequestBody(body, encryptKey),
5301
+ verifyParsedBody: (body, config) => {
5302
+ if (!config.verificationToken) {
5303
+ return true;
5304
+ }
5305
+ return createLarkRequestVerifier(config)({
5306
+ body
5307
+ });
5308
+ },
5309
+ parseEvent: parseLarkMessageEvent,
5310
+ claimInboundReceipt: (input) => mappingStore.claimInboundReceipt(input),
5311
+ markInboundReceiptCompleted: (input) => mappingStore.markInboundReceiptCompleted(input),
5312
+ markInboundReceiptFailed: (input) => mappingStore.markInboundReceiptFailed(input),
5313
+ resolveThread: (input) => mappingService.getOrCreateThread(input),
5314
+ runAgentAndCollectText: ({ tenantId, assistantId, threadId, text, workspaceId, projectId }) => runAgentAndCollectLarkReply({
5315
+ tenantId,
5316
+ assistantId,
5317
+ threadId,
5318
+ text,
5319
+ workspaceId,
5320
+ projectId
5321
+ }),
5322
+ sendTextReply: async ({ chatId, text, config }) => {
5323
+ const sender = createLarkSender({
5324
+ appId: config.appId,
5325
+ appSecret: config.appSecret
5326
+ });
5327
+ await sender.sendTextReply({ chatId, text });
5328
+ }
5329
+ };
5330
+ }
5331
+ function getDatabaseUrl() {
5332
+ const databaseUrl = process.env.DATABASE_URL;
5333
+ if (!databaseUrl) {
5334
+ throw new Error("DATABASE_URL is required for Lark channel ingress");
5335
+ }
5336
+ return databaseUrl;
5337
+ }
5338
+
5339
+ // src/channels/routes.ts
5340
+ var channelRouteRegistrars = [
5341
+ (app2, dependencies) => registerLarkChannelRoutes(app2, dependencies.lark)
5342
+ ];
5343
+ function registerChannelRoutes(app2, dependencies = {}) {
5344
+ for (const registerRoutes of channelRouteRegistrars) {
5345
+ registerRoutes(app2, dependencies);
5346
+ }
5347
+ }
5348
+
5349
+ // src/controllers/channel-installations.ts
5350
+ var import_crypto8 = require("crypto");
5351
+ function getTenantId9(request) {
5352
+ const userTenantId = request.user?.tenantId;
5353
+ if (userTenantId) {
5354
+ return userTenantId;
5355
+ }
5356
+ return request.headers["x-tenant-id"] || "default";
5357
+ }
5358
+ function getInstallationStore() {
5359
+ const { PostgreSQLChannelInstallationStore: PostgreSQLChannelInstallationStore2 } = require("@axiom-lattice/pg-stores");
5360
+ const databaseUrl = process.env.DATABASE_URL;
5361
+ if (!databaseUrl) {
5362
+ throw new Error("DATABASE_URL is required for channel installation store");
5363
+ }
5364
+ return new PostgreSQLChannelInstallationStore2({
5365
+ poolConfig: databaseUrl
5366
+ });
5367
+ }
5368
+ async function getChannelInstallationList(request, reply) {
5369
+ const tenantId = getTenantId9(request);
5370
+ const { channel } = request.query;
5371
+ try {
5372
+ const store = getInstallationStore();
5373
+ const installations = await store.getInstallationsByTenant(tenantId, channel);
5374
+ return {
5375
+ success: true,
5376
+ message: "Channel installations retrieved successfully",
5377
+ data: {
5378
+ records: installations,
5379
+ total: installations.length
5380
+ }
5381
+ };
5382
+ } catch (error) {
5383
+ console.error("Failed to get channel installations:", error);
5384
+ return {
5385
+ success: false,
5386
+ message: "Failed to retrieve channel installations",
5387
+ data: {
5388
+ records: [],
5389
+ total: 0
5390
+ }
5391
+ };
5392
+ }
5393
+ }
5394
+ async function getChannelInstallation(request, reply) {
5395
+ const tenantId = getTenantId9(request);
5396
+ const { installationId } = request.params;
5397
+ try {
5398
+ const store = getInstallationStore();
5399
+ const installation = await store.getInstallationById(installationId);
5400
+ if (!installation) {
5401
+ reply.code(404);
5402
+ return {
5403
+ success: false,
5404
+ message: "Channel installation not found"
5405
+ };
5406
+ }
5407
+ if (installation.tenantId !== tenantId) {
5408
+ reply.code(403);
5409
+ return {
5410
+ success: false,
5411
+ message: "Access denied"
5412
+ };
5413
+ }
5414
+ return {
5415
+ success: true,
5416
+ message: "Channel installation retrieved successfully",
5417
+ data: installation
5418
+ };
5419
+ } catch (error) {
5420
+ console.error("Failed to get channel installation:", error);
5421
+ return {
5422
+ success: false,
5423
+ message: "Failed to retrieve channel installation"
5424
+ };
5425
+ }
5426
+ }
5427
+ async function createChannelInstallation(request, reply) {
5428
+ const tenantId = getTenantId9(request);
5429
+ const body = request.body;
5430
+ try {
5431
+ if (!body.channel) {
5432
+ reply.code(400);
5433
+ return {
5434
+ success: false,
5435
+ message: "Channel type is required"
5436
+ };
5437
+ }
5438
+ if (!body.config) {
5439
+ reply.code(400);
5440
+ return {
5441
+ success: false,
5442
+ message: "Configuration is required"
5443
+ };
5444
+ }
5445
+ if (body.channel === "lark") {
5446
+ const larkConfig = body.config;
5447
+ if (!larkConfig.appId || !larkConfig.appSecret) {
5448
+ reply.code(400);
5449
+ return {
5450
+ success: false,
5451
+ message: "appId and appSecret are required for Lark installations"
5452
+ };
5453
+ }
5454
+ if (!larkConfig.assistantId) {
5455
+ reply.code(400);
5456
+ return {
5457
+ success: false,
5458
+ message: "assistantId is required for Lark installations"
5459
+ };
5460
+ }
5461
+ }
5462
+ const store = getInstallationStore();
5463
+ const installationId = body.id || (0, import_crypto8.randomUUID)();
5464
+ const installation = await store.createInstallation(
5465
+ tenantId,
5466
+ installationId,
5467
+ body
5468
+ );
5469
+ reply.code(201);
5470
+ return {
5471
+ success: true,
5472
+ message: "Channel installation created successfully",
5473
+ data: installation
5474
+ };
5475
+ } catch (error) {
5476
+ console.error("Failed to create channel installation:", error);
5477
+ if (error.message?.includes("duplicate") || error.code === "23505") {
5478
+ reply.code(409);
5479
+ return {
5480
+ success: false,
5481
+ message: "Channel installation with this ID already exists"
5482
+ };
5483
+ }
5484
+ return {
5485
+ success: false,
5486
+ message: "Failed to create channel installation"
5487
+ };
5488
+ }
5489
+ }
5490
+ async function updateChannelInstallation(request, reply) {
5491
+ const tenantId = getTenantId9(request);
5492
+ const { installationId } = request.params;
5493
+ const body = request.body;
5494
+ try {
5495
+ const store = getInstallationStore();
5496
+ const existing = await store.getInstallationById(installationId);
5497
+ if (!existing) {
5498
+ reply.code(404);
5499
+ return {
5500
+ success: false,
5501
+ message: "Channel installation not found"
5502
+ };
5503
+ }
5504
+ if (existing.tenantId !== tenantId) {
5505
+ reply.code(403);
5506
+ return {
5507
+ success: false,
5508
+ message: "Access denied"
5509
+ };
5510
+ }
5511
+ const installation = await store.updateInstallation(
5512
+ tenantId,
5513
+ installationId,
5514
+ body
5515
+ );
5516
+ if (!installation) {
5517
+ reply.code(404);
5518
+ return {
5519
+ success: false,
5520
+ message: "Channel installation not found"
5521
+ };
5522
+ }
5523
+ return {
5524
+ success: true,
5525
+ message: "Channel installation updated successfully",
5526
+ data: installation
5527
+ };
5528
+ } catch (error) {
5529
+ console.error("Failed to update channel installation:", error);
5530
+ return {
5531
+ success: false,
5532
+ message: "Failed to update channel installation"
5533
+ };
5534
+ }
5535
+ }
5536
+ async function deleteChannelInstallation(request, reply) {
5537
+ const tenantId = getTenantId9(request);
5538
+ const { installationId } = request.params;
5539
+ try {
5540
+ const store = getInstallationStore();
5541
+ const existing = await store.getInstallationById(installationId);
5542
+ if (!existing) {
5543
+ reply.code(404);
5544
+ return {
5545
+ success: false,
5546
+ message: "Channel installation not found"
5547
+ };
5548
+ }
5549
+ if (existing.tenantId !== tenantId) {
5550
+ reply.code(403);
5551
+ return {
5552
+ success: false,
5553
+ message: "Access denied"
5554
+ };
5555
+ }
5556
+ const deleted = await store.deleteInstallation(tenantId, installationId);
5557
+ if (!deleted) {
5558
+ reply.code(404);
5559
+ return {
5560
+ success: false,
5561
+ message: "Channel installation not found"
5562
+ };
5563
+ }
5564
+ return {
5565
+ success: true,
5566
+ message: "Channel installation deleted successfully"
5567
+ };
5568
+ } catch (error) {
5569
+ console.error("Failed to delete channel installation:", error);
5570
+ return {
5571
+ success: false,
5572
+ message: "Failed to delete channel installation"
5573
+ };
5574
+ }
5575
+ }
5576
+
5577
+ // src/routes/channel-installations.ts
5578
+ function registerChannelInstallationRoutes(app2) {
5579
+ app2.get("/api/channel-installations", getChannelInstallationList);
5580
+ app2.get("/api/channel-installations/:installationId", getChannelInstallation);
5581
+ app2.post("/api/channel-installations", createChannelInstallation);
5582
+ app2.put("/api/channel-installations/:installationId", updateChannelInstallation);
5583
+ app2.delete("/api/channel-installations/:installationId", deleteChannelInstallation);
5584
+ }
5585
+
4895
5586
  // src/routes/index.ts
4896
5587
  var registerLatticeRoutes = (app2) => {
4897
5588
  app2.post("/api/runs", createRun);
@@ -5027,6 +5718,8 @@ var registerLatticeRoutes = (app2) => {
5027
5718
  autoApproveUsers: process.env.AUTO_APPROVE_USERS !== "false",
5028
5719
  allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false"
5029
5720
  });
5721
+ registerChannelRoutes(app2);
5722
+ registerChannelInstallationRoutes(app2);
5030
5723
  app2.delete(
5031
5724
  "/api/assistants/:assistant_id/threads/:thread_id/pending-messages/:message_id",
5032
5725
  removePendingMessageHandler
@@ -5096,7 +5789,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
5096
5789
  };
5097
5790
 
5098
5791
  // src/services/agent_task_consumer.ts
5099
- var import_core26 = require("@axiom-lattice/core");
5792
+ var import_core28 = require("@axiom-lattice/core");
5100
5793
  var handleAgentTask = async (taskRequest, retryCount = 0) => {
5101
5794
  const {
5102
5795
  assistant_id,
@@ -5111,18 +5804,18 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
5111
5804
  console.log(
5112
5805
  `\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
5113
5806
  );
5114
- const agent = import_core26.agentInstanceManager.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
5115
- await agent.addMessage({ input, command, custom_run_config: runConfig }, import_core26.QueueMode.STEER);
5807
+ const agent = import_core28.agentInstanceManager.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
5808
+ await agent.addMessage({ input, command, custom_run_config: runConfig }, import_core28.QueueMode.STEER);
5116
5809
  if (callback_event) {
5117
5810
  agent.subscribeOnce("message:completed", (evt) => {
5118
- import_core26.eventBus.publish(callback_event, {
5811
+ import_core28.eventBus.publish(callback_event, {
5119
5812
  success: true,
5120
5813
  state: evt.state,
5121
5814
  config: { assistant_id, thread_id, tenant_id }
5122
5815
  });
5123
5816
  });
5124
5817
  agent.subscribeOnce("message:interrupted", (evt) => {
5125
- import_core26.eventBus.publish(callback_event, {
5818
+ import_core28.eventBus.publish(callback_event, {
5126
5819
  success: true,
5127
5820
  state: evt.state,
5128
5821
  config: { assistant_id, thread_id, tenant_id }
@@ -5146,7 +5839,7 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
5146
5839
  return handleAgentTask(taskRequest, nextRetryCount);
5147
5840
  }
5148
5841
  if (callback_event) {
5149
- import_core26.eventBus.publish(callback_event, {
5842
+ import_core28.eventBus.publish(callback_event, {
5150
5843
  success: false,
5151
5844
  error: error instanceof Error ? error.message : String(error),
5152
5845
  config: { assistant_id, thread_id, tenant_id }
@@ -5184,7 +5877,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
5184
5877
  * 初始化事件监听和队列轮询
5185
5878
  */
5186
5879
  initialize() {
5187
- import_core26.eventBus.subscribe(import_core26.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
5880
+ import_core28.eventBus.subscribe(import_core28.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
5188
5881
  this.startPollingQueue();
5189
5882
  console.log("Agent\u4EFB\u52A1\u6D88\u8D39\u8005\u5DF2\u542F\u52A8\u5E76\u76D1\u542C\u4EFB\u52A1\u4E8B\u4EF6\u548C\u961F\u5217");
5190
5883
  }
@@ -5303,7 +5996,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
5303
5996
  handleAgentTask(taskRequest).catch((error) => {
5304
5997
  console.error("\u5904\u7406Agent\u4EFB\u52A1\u65F6\u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:", error);
5305
5998
  if (taskRequest.callback_event) {
5306
- import_core26.eventBus.publish(taskRequest.callback_event, {
5999
+ import_core28.eventBus.publish(taskRequest.callback_event, {
5307
6000
  success: false,
5308
6001
  error: error instanceof Error ? error.message : String(error),
5309
6002
  config: {
@@ -5323,26 +6016,26 @@ _AgentTaskConsumer.agent_run_endpoint = "http://localhost:4001/api/runs";
5323
6016
  var AgentTaskConsumer = _AgentTaskConsumer;
5324
6017
 
5325
6018
  // src/index.ts
5326
- var import_core27 = require("@axiom-lattice/core");
5327
- var import_protocols3 = require("@axiom-lattice/protocols");
6019
+ var import_core29 = require("@axiom-lattice/core");
6020
+ var import_protocols5 = require("@axiom-lattice/protocols");
5328
6021
  process.on("unhandledRejection", (reason, promise) => {
5329
6022
  console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
5330
6023
  });
5331
6024
  var DEFAULT_LOGGER_CONFIG = {
5332
6025
  name: "default",
5333
6026
  description: "Default logger for lattice-gateway service",
5334
- type: import_protocols3.LoggerType.PINO,
6027
+ type: import_protocols5.LoggerType.PINO,
5335
6028
  serviceName: "lattice/gateway",
5336
6029
  loggerName: "lattice/gateway"
5337
6030
  };
5338
6031
  var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
5339
6032
  var logger = loggerLattice.client;
5340
6033
  function initializeLogger(config) {
5341
- if (import_core27.loggerLatticeManager.hasLattice("default")) {
5342
- import_core27.loggerLatticeManager.removeLattice("default");
6034
+ if (import_core29.loggerLatticeManager.hasLattice("default")) {
6035
+ import_core29.loggerLatticeManager.removeLattice("default");
5343
6036
  }
5344
- (0, import_core27.registerLoggerLattice)("default", config);
5345
- return (0, import_core27.getLoggerLattice)("default");
6037
+ (0, import_core29.registerLoggerLattice)("default", config);
6038
+ return (0, import_core29.getLoggerLattice)("default");
5346
6039
  }
5347
6040
  var app = (0, import_fastify.default)({
5348
6041
  logger: false,
@@ -5449,16 +6142,16 @@ var start = async (config) => {
5449
6142
  app.decorate("loggerLattice", loggerLattice);
5450
6143
  registerLatticeRoutes(app);
5451
6144
  try {
5452
- const storeLattice = (0, import_core27.getStoreLattice)("default", "database");
6145
+ const storeLattice = (0, import_core29.getStoreLattice)("default", "database");
5453
6146
  const store = storeLattice.store;
5454
- import_core27.sqlDatabaseManager.setConfigStore(store);
6147
+ import_core29.sqlDatabaseManager.setConfigStore(store);
5455
6148
  logger.info("Database config store set for SqlDatabaseManager");
5456
6149
  } catch (error) {
5457
6150
  logger.warn("Failed to set database config store: " + (error instanceof Error ? error.message : String(error)));
5458
6151
  }
5459
- if (!import_core27.sandboxLatticeManager.hasLattice("default")) {
6152
+ if (!import_core29.sandboxLatticeManager.hasLattice("default")) {
5460
6153
  const sandboxBaseURL = process.env.SANDBOX_BASE_URL || "http://localhost:8080";
5461
- import_core27.sandboxLatticeManager.registerLattice("default", {
6154
+ import_core29.sandboxLatticeManager.registerLattice("default", {
5462
6155
  baseURL: sandboxBaseURL
5463
6156
  });
5464
6157
  logger.info(`Registered sandbox manager with baseURL: ${sandboxBaseURL}`);
@@ -5481,7 +6174,7 @@ var start = async (config) => {
5481
6174
  }
5482
6175
  try {
5483
6176
  logger.info("Starting agent instance recovery...");
5484
- const restoreStats = await import_core27.agentInstanceManager.restore();
6177
+ const restoreStats = await import_core29.agentInstanceManager.restore();
5485
6178
  logger.info(`Agent recovery complete: ${restoreStats.restored} threads restored, ${restoreStats.errors} errors`);
5486
6179
  } catch (error) {
5487
6180
  logger.error("Agent recovery failed", { error });