@deadragdoll/tellymcp 0.0.10 → 0.0.12

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 (154) hide show
  1. package/.env.example.client +42 -52
  2. package/.env.example.gateway +48 -62
  3. package/CHANGELOG.md +3 -3
  4. package/README-ru.md +226 -376
  5. package/README.md +213 -1184
  6. package/TOOLS.md +320 -377
  7. package/VERSION.md +13 -13
  8. package/config/codex/plugins/telly-workflows/.codex-plugin/plugin.json +18 -0
  9. package/config/codex/plugins/telly-workflows/references/invariants.md +10 -0
  10. package/config/codex/plugins/telly-workflows/skills/telly-browser-screenshot/SKILL.md +27 -0
  11. package/config/codex/plugins/telly-workflows/skills/telly-collab-artifact/SKILL.md +22 -0
  12. package/config/codex/plugins/telly-workflows/skills/telly-human-telegram/SKILL.md +31 -0
  13. package/config/codex/plugins/telly-workflows/skills/telly-partner-note/SKILL.md +36 -0
  14. package/config/templates/env.both.template +39 -20
  15. package/config/templates/env.client.template +34 -19
  16. package/config/templates/env.gateway.template +37 -22
  17. package/dist/cli.js +322 -75
  18. package/dist/codexPluginInstaller.js +215 -0
  19. package/dist/lib/mixins/session.errors.js +34 -1
  20. package/dist/lib/pinoTargets.js +2 -2
  21. package/dist/moleculer.config.js +7 -5
  22. package/dist/services/features/telegram-mcp/browser.service.js +94 -1
  23. package/dist/services/features/telegram-mcp/collaboration.service.js +40 -3
  24. package/dist/services/features/telegram-mcp/ensuredb.service.js +145 -26
  25. package/dist/services/features/telegram-mcp/gateway-delivery.service.js +285 -103
  26. package/dist/services/features/telegram-mcp/gateway-socket.service.js +907 -85
  27. package/dist/services/features/telegram-mcp/gateway.service.js +876 -81
  28. package/dist/services/features/telegram-mcp/mcp-http.service.js +8 -0
  29. package/dist/services/features/telegram-mcp/mcp-server.service.js +17 -26
  30. package/dist/services/features/telegram-mcp/notify.service.js +127 -1
  31. package/dist/services/features/telegram-mcp/session-context.service.js +28 -1
  32. package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +132 -10
  33. package/dist/services/features/telegram-mcp/src/app/config/env.js +158 -52
  34. package/dist/services/features/telegram-mcp/src/app/http.js +375 -42
  35. package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +386 -58
  36. package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +3 -0
  37. package/dist/services/features/telegram-mcp/src/app/webapp/terminal.js +12 -0
  38. package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +120 -104
  39. package/dist/services/features/telegram-mcp/src/entities/xchange/model/types.js +2 -0
  40. package/dist/services/features/telegram-mcp/src/features/ask-user/model/askUserTelegram.js +1 -1
  41. package/dist/services/features/telegram-mcp/src/features/browser/model/browserOpenTool.js +1 -1
  42. package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +1 -1
  43. package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +306 -26
  44. package/dist/services/features/telegram-mcp/src/features/collaboration/model/gatewaySessionsService.js +71 -0
  45. package/dist/services/features/telegram-mcp/src/features/collaboration/model/listGatewaySessionsTool.js +33 -0
  46. package/dist/services/features/telegram-mcp/src/features/collaboration/model/localCollaborationBackend.js +93 -82
  47. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +47 -5
  48. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileTool.js +1 -1
  49. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerNoteTool.js +1 -1
  50. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +82 -0
  51. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +22 -14
  52. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +697 -75
  53. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/remoteConsoleActionClient.js +76 -0
  54. package/dist/services/features/telegram-mcp/src/features/embedded-runtime/model/embeddedRuntimeBroker.js +92 -0
  55. package/dist/services/features/telegram-mcp/src/features/foreground-terminal/model/foregroundTerminalRuntime.js +192 -0
  56. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +382 -3
  57. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyTelegramTool.js +1 -1
  58. package/dist/services/features/telegram-mcp/src/features/notify/model/sendFileToTelegramTool.js +33 -0
  59. package/dist/services/features/telegram-mcp/src/features/session-context/model/clearSessionContextTool.js +1 -1
  60. package/dist/services/features/telegram-mcp/src/features/session-context/model/getSessionContextTool.js +1 -1
  61. package/dist/services/features/telegram-mcp/src/features/session-context/model/renameSessionTool.js +1 -1
  62. package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +42 -200
  63. package/dist/services/features/telegram-mcp/src/features/session-context/model/setSessionContextTool.js +1 -1
  64. package/dist/services/features/telegram-mcp/src/features/terminal-buffer/model/terminalBufferService.js +96 -0
  65. package/dist/services/features/telegram-mcp/src/features/terminal-input/model/terminalInputService.js +97 -0
  66. package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownService.js +107 -58
  67. package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownTool.js +1 -1
  68. package/dist/services/features/telegram-mcp/src/features/xchange/model/getXchangeRecordTool.js +28 -0
  69. package/dist/services/features/telegram-mcp/src/features/xchange/model/listXchangeRecordsTool.js +28 -0
  70. package/dist/services/features/telegram-mcp/src/features/xchange/model/markXchangeRecordReadTool.js +28 -0
  71. package/dist/services/features/telegram-mcp/src/features/xchange/model/xchangeService.js +169 -0
  72. package/dist/services/features/telegram-mcp/src/processes/human-approval/model/orchestrator.js +5 -4
  73. package/dist/services/features/telegram-mcp/src/shared/i18n/resources/en.js +143 -42
  74. package/dist/services/features/telegram-mcp/src/shared/i18n/resources/ru.js +139 -38
  75. package/dist/services/features/telegram-mcp/src/shared/integrations/memory/processLocalSessionStore.js +27 -0
  76. package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +11 -8
  77. package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +69 -70
  78. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/proxyFetch.js +21 -22
  79. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +444 -7093
  80. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportAttachmentStore.js +93 -0
  81. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportBroadcastActions.js +385 -0
  82. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConsoleRegistry.js +149 -0
  83. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConstructorWiring.js +642 -0
  84. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContent.js +84 -0
  85. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContext.js +78 -0
  86. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportDocumentActions.js +36 -0
  87. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportEventActions.js +292 -0
  88. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +352 -0
  89. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFormatting.js +75 -0
  90. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportGatewayActions.js +44 -0
  91. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLifecycleActions.js +161 -0
  92. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLive.js +56 -0
  93. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLiveActions.js +77 -0
  94. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuCallbacks.js +254 -0
  95. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFactories.js +538 -0
  96. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFingerprints.js +93 -0
  97. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFlow.js +344 -0
  98. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuShell.js +65 -0
  99. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuState.js +408 -0
  100. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuText.js +216 -0
  101. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMessageFlow.js +452 -0
  102. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportOutputActions.js +189 -0
  103. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPartnerActions.js +286 -0
  104. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPayloadState.js +108 -0
  105. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectActions.js +463 -0
  106. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEntryActions.js +202 -0
  107. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEvents.js +99 -0
  108. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectMenus.js +138 -0
  109. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectState.js +308 -0
  110. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectView.js +426 -0
  111. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportRequestFlow.js +278 -0
  112. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportSessionActions.js +143 -0
  113. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalActions.js +665 -0
  114. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalRuntime.js +226 -0
  115. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTypes.js +2 -0
  116. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportUtils.js +330 -0
  117. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportXchangeState.js +107 -0
  118. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +255 -0
  119. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +543 -0
  120. package/dist/services/features/telegram-mcp/src/shared/integrations/xchange/sqliteRecordStore.js +223 -0
  121. package/dist/services/features/telegram-mcp/src/shared/lib/gatewayScope.js +24 -0
  122. package/dist/services/features/telegram-mcp/src/shared/lib/logger/logger.js +6 -0
  123. package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +147 -76
  124. package/dist/services/features/telegram-mcp/src/shared/lib/telegramXchangeRecords.js +72 -0
  125. package/dist/services/features/telegram-mcp/src/shared/lib/terminalPromptDetection.js +409 -0
  126. package/dist/services/features/telegram-mcp/src/shared/lib/version/versionHandshake.js +129 -1
  127. package/dist/services/features/telegram-mcp/src/shared/lib/xchangeRecordHints.js +98 -0
  128. package/dist/services/features/telegram-mcp/standalone-http.service.js +12 -0
  129. package/dist/services/features/telegram-mcp/terminal-buffer.service.js +42 -0
  130. package/dist/services/features/telegram-mcp/terminal-input.service.js +41 -0
  131. package/dist/services/features/telegram-mcp/tools-sync.service.js +15 -1
  132. package/dist/services/features/telegram-mcp/xchange.service.js +123 -0
  133. package/docs/STANDALONE-ru.md +172 -0
  134. package/docs/STANDALONE.md +172 -0
  135. package/package.json +10 -5
  136. package/scripts/build-package-artifact.sh +27 -0
  137. package/scripts/deploy-gateway.sh +64 -0
  138. package/scripts/deploy-onebot-branch.sh +27 -0
  139. package/scripts/postinstall.js +11 -36
  140. package/STANDALONE-ru.md +0 -274
  141. package/STANDALONE.md +0 -274
  142. package/dist/services/features/telegram-mcp/inbox.service.js +0 -33
  143. package/dist/services/features/telegram-mcp/pair.service.js +0 -33
  144. package/dist/services/features/telegram-mcp/src/app/webapp/tmux.js +0 -10
  145. package/dist/services/features/telegram-mcp/src/features/inbox/model/deleteTelegramInboxMessageTool.js +0 -33
  146. package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxCountTool.js +0 -33
  147. package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxTool.js +0 -33
  148. package/dist/services/features/telegram-mcp/src/features/inbox/model/inboxService.js +0 -77
  149. package/dist/services/features/telegram-mcp/src/features/pair-session/model/clearSessionPairingTool.js +0 -33
  150. package/dist/services/features/telegram-mcp/src/features/pair-session/model/createSessionPairCodeTool.js +0 -33
  151. package/dist/services/features/telegram-mcp/src/features/pair-session/model/generatePairCode.js +0 -202
  152. package/dist/services/features/telegram-mcp/src/features/session-context/model/getTmuxTargetTool.js +0 -33
  153. package/dist/services/features/telegram-mcp/src/features/session-context/model/setTmuxTargetTool.js +0 -33
  154. package/dist/services/features/telegram-mcp/src/shared/integrations/tmux/client.js +0 -363
@@ -8,6 +8,7 @@ const node_crypto_1 = require("node:crypto");
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const db_1 = require("../../../lib/mixins/db");
10
10
  const gatewayReplyResolution_1 = require("./src/features/distributed-gateway/model/gatewayReplyResolution");
11
+ const gatewayScope_1 = require("./src/shared/lib/gatewayScope");
11
12
  const ensuredb_service_1 = require("./ensuredb.service");
12
13
  exports.TELEGRAM_MCP_GATEWAY_SERVICE_NAME = "telegramMcp.gateway";
13
14
  const MCP_SCHEMA = process.env.DB_SCHEME || "mcp";
@@ -63,12 +64,124 @@ const TelegramMcpGatewayService = {
63
64
  }
64
65
  return text;
65
66
  },
67
+ async resolveOwnerUserUuidFilter(input) {
68
+ const explicitOwnerUserUuid = this.normalizeOptionalText?.(input.owner_user_uuid);
69
+ if (explicitOwnerUserUuid) {
70
+ return explicitOwnerUserUuid;
71
+ }
72
+ const telegramUserId = this.normalizeOptionalText?.(input.telegram_user_id);
73
+ if (!telegramUserId) {
74
+ return null;
75
+ }
76
+ const user = await this.db
77
+ .withSchema(MCP_SCHEMA)
78
+ .table("gateway_users")
79
+ .where({ telegram_user_id: telegramUserId })
80
+ .first("gateway_user_uuid");
81
+ return user?.gateway_user_uuid
82
+ ? String(user.gateway_user_uuid)
83
+ : "__missing_gateway_user__";
84
+ },
85
+ async upsertGatewayUserRecord(input) {
86
+ const telegramUserId = this.requireText?.(input.telegram_user_id, "telegram_user_id");
87
+ const telegramChatId = this.normalizeOptionalText?.(input.telegram_chat_id);
88
+ const telegramUsername = this.normalizeOptionalText?.(input.telegram_username);
89
+ const telegramDisplayName = this.normalizeOptionalText?.(input.telegram_display_name);
90
+ const now = new Date().toISOString();
91
+ const existing = await this.db
92
+ .withSchema(MCP_SCHEMA)
93
+ .table("gateway_users")
94
+ .where({ telegram_user_id: telegramUserId })
95
+ .first();
96
+ if (existing) {
97
+ await this.db
98
+ .withSchema(MCP_SCHEMA)
99
+ .table("gateway_users")
100
+ .where({ gateway_user_uuid: existing.gateway_user_uuid })
101
+ .update({
102
+ ...(telegramChatId ? { telegram_chat_id: telegramChatId } : {}),
103
+ ...(telegramUsername ? { telegram_username: telegramUsername } : {}),
104
+ ...(telegramDisplayName ? { telegram_display_name: telegramDisplayName } : {}),
105
+ updated_at: now,
106
+ last_auth_at: now,
107
+ });
108
+ return {
109
+ gateway_user_uuid: String(existing.gateway_user_uuid),
110
+ created: false,
111
+ updated_at: now,
112
+ };
113
+ }
114
+ const gatewayUserUuid = (0, node_crypto_1.randomUUID)();
115
+ await this.db.withSchema(MCP_SCHEMA).table("gateway_users").insert({
116
+ gateway_user_uuid: gatewayUserUuid,
117
+ telegram_user_id: telegramUserId,
118
+ ...(telegramChatId ? { telegram_chat_id: telegramChatId } : {}),
119
+ ...(telegramUsername ? { telegram_username: telegramUsername } : {}),
120
+ ...(telegramDisplayName ? { telegram_display_name: telegramDisplayName } : {}),
121
+ created_at: now,
122
+ updated_at: now,
123
+ last_auth_at: now,
124
+ });
125
+ return {
126
+ gateway_user_uuid: gatewayUserUuid,
127
+ created: true,
128
+ updated_at: now,
129
+ };
130
+ },
131
+ async resolveGatewayUserRouteRecord(input) {
132
+ const explicitGatewayUserUuid = this.normalizeOptionalText?.(input.gateway_user_uuid);
133
+ const clientUuid = this.normalizeOptionalText?.(input.client_uuid);
134
+ let gatewayUserUuid = explicitGatewayUserUuid;
135
+ if (!gatewayUserUuid && clientUuid) {
136
+ const client = await this.db
137
+ .withSchema(MCP_SCHEMA)
138
+ .table("gateway_clients")
139
+ .where({ client_uuid: clientUuid })
140
+ .first("owner_user_uuid");
141
+ gatewayUserUuid =
142
+ client?.owner_user_uuid ? String(client.owner_user_uuid) : null;
143
+ }
144
+ if (!gatewayUserUuid) {
145
+ return null;
146
+ }
147
+ const user = await this.db
148
+ .withSchema(MCP_SCHEMA)
149
+ .table("gateway_users")
150
+ .where({ gateway_user_uuid: gatewayUserUuid })
151
+ .first("gateway_user_uuid", "telegram_user_id", "telegram_chat_id", "telegram_username", "telegram_display_name");
152
+ if (!user?.gateway_user_uuid || !user.telegram_user_id) {
153
+ return null;
154
+ }
155
+ return {
156
+ gateway_user_uuid: String(user.gateway_user_uuid),
157
+ telegram_user_id: Number(user.telegram_user_id),
158
+ telegram_chat_id: typeof user.telegram_chat_id === "number"
159
+ ? user.telegram_chat_id
160
+ : user.telegram_chat_id
161
+ ? Number(user.telegram_chat_id)
162
+ : null,
163
+ ...(user.telegram_username
164
+ ? { telegram_username: String(user.telegram_username) }
165
+ : {}),
166
+ ...(user.telegram_display_name
167
+ ? { telegram_display_name: String(user.telegram_display_name) }
168
+ : {}),
169
+ };
170
+ },
66
171
  async registerClientRecord(input) {
67
172
  const clientUuid = this.normalizeOptionalText?.(input.client_uuid) || (0, node_crypto_1.randomUUID)();
68
173
  const now = new Date().toISOString();
174
+ const scopeKey = (0, gatewayScope_1.resolveGatewayScopeKey)(input);
69
175
  const clientLabel = this.normalizeOptionalText?.(input.client_label);
70
176
  const botUsername = this.normalizeOptionalText?.(input.bot_username);
71
177
  const tokenFingerprint = this.normalizeOptionalText?.(input.bot_token_fingerprint);
178
+ const ownerUserUuid = this.normalizeOptionalText?.(input.owner_user_uuid) ||
179
+ (input.meta &&
180
+ typeof input.meta === "object" &&
181
+ !Array.isArray(input.meta) &&
182
+ typeof input.meta.gateway_user_uuid === "string"
183
+ ? this.normalizeOptionalText?.(input.meta.gateway_user_uuid)
184
+ : null);
72
185
  const meta = input.meta && typeof input.meta === "object" && !Array.isArray(input.meta)
73
186
  ? input.meta
74
187
  : {};
@@ -78,11 +191,17 @@ const TelegramMcpGatewayService = {
78
191
  .where({ client_uuid: clientUuid })
79
192
  .first();
80
193
  if (existing) {
194
+ const existingScopeKey = this.normalizeOptionalText?.(existing.scope_key);
195
+ if (existingScopeKey && scopeKey && existingScopeKey !== scopeKey) {
196
+ throw new Error("Client is already bound to a different gateway scope.");
197
+ }
81
198
  await this.db
82
199
  .withSchema(MCP_SCHEMA)
83
200
  .table("gateway_clients")
84
201
  .where({ client_uuid: clientUuid })
85
202
  .update({
203
+ ...(ownerUserUuid ? { owner_user_uuid: ownerUserUuid } : {}),
204
+ ...(scopeKey ? { scope_key: scopeKey } : {}),
86
205
  ...(clientLabel ? { client_label: clientLabel } : {}),
87
206
  ...(botUsername ? { bot_username: botUsername } : {}),
88
207
  ...(tokenFingerprint ? { bot_token_fingerprint: tokenFingerprint } : {}),
@@ -98,6 +217,8 @@ const TelegramMcpGatewayService = {
98
217
  }
99
218
  await this.db.withSchema(MCP_SCHEMA).table("gateway_clients").insert({
100
219
  client_uuid: clientUuid,
220
+ ...(ownerUserUuid ? { owner_user_uuid: ownerUserUuid } : {}),
221
+ ...(scopeKey ? { scope_key: scopeKey } : {}),
101
222
  ...(clientLabel ? { client_label: clientLabel } : {}),
102
223
  ...(botUsername ? { bot_username: botUsername } : {}),
103
224
  ...(tokenFingerprint ? { bot_token_fingerprint: tokenFingerprint } : {}),
@@ -112,9 +233,334 @@ const TelegramMcpGatewayService = {
112
233
  updated_at: now,
113
234
  };
114
235
  },
236
+ async clearLiveConsolesRecord(input = {}) {
237
+ const connectionId = this.normalizeOptionalText?.(input.connection_id);
238
+ const clientUuid = this.normalizeOptionalText?.(input.client_uuid);
239
+ const query = this.db.withSchema(MCP_SCHEMA).table("gateway_live_consoles");
240
+ if (connectionId) {
241
+ query.where({ connection_id: connectionId });
242
+ }
243
+ if (clientUuid) {
244
+ query.where({ client_uuid: clientUuid });
245
+ }
246
+ const deleted = await query.del();
247
+ return { deleted };
248
+ },
249
+ async removeLiveConsolesRecord(input) {
250
+ const clientUuid = this.normalizeOptionalText?.(input.client_uuid);
251
+ const connectionId = this.normalizeOptionalText?.(input.connection_id);
252
+ if (!clientUuid && !connectionId) {
253
+ throw new Error("client_uuid or connection_id is required");
254
+ }
255
+ const query = this.db.withSchema(MCP_SCHEMA).table("gateway_live_consoles");
256
+ if (clientUuid) {
257
+ query.where({ client_uuid: clientUuid });
258
+ }
259
+ if (connectionId) {
260
+ query.where({ connection_id: connectionId });
261
+ }
262
+ const deleted = await query.del();
263
+ return { deleted };
264
+ },
265
+ async pruneGatewayStateRecord() {
266
+ const deleted = {};
267
+ const truncate = async (tableName) => {
268
+ deleted[tableName] = await this.db.withSchema(MCP_SCHEMA).table(tableName).del();
269
+ };
270
+ await truncate("gateway_live_consoles");
271
+ await truncate("gateway_session_links");
272
+ await truncate("gateway_deliveries");
273
+ await truncate("gateway_message_artifacts");
274
+ await truncate("gateway_messages");
275
+ await truncate("gateway_project_consoles");
276
+ await truncate("gateway_sessions");
277
+ await truncate("gateway_project_members");
278
+ await truncate("gateway_projects");
279
+ await truncate("gateway_clients");
280
+ await truncate("gateway_users");
281
+ return { deleted };
282
+ },
283
+ async syncLiveConsolesRecord(input) {
284
+ const clientUuid = this.requireText?.(input.client_uuid, "client_uuid");
285
+ const connectionId = this.requireText?.(input.connection_id, "connection_id");
286
+ const now = new Date().toISOString();
287
+ const gatewayUserUuid = this.normalizeOptionalText?.(input.gateway_user_uuid);
288
+ const clientLabel = this.normalizeOptionalText?.(input.client_label);
289
+ const systemUsername = this.normalizeOptionalText?.(input.system_username);
290
+ const namespace = this.normalizeOptionalText?.(input.namespace);
291
+ const nodeId = this.normalizeOptionalText?.(input.node_id);
292
+ const packageVersion = this.normalizeOptionalText?.(input.package_version);
293
+ const protocolVersion = this.normalizeOptionalText?.(input.protocol_version);
294
+ const sessionTools = Array.isArray(input.session_tools)
295
+ ? input.session_tools.flatMap((item) => {
296
+ if (!item || typeof item !== "object") {
297
+ return [];
298
+ }
299
+ const record = item;
300
+ const localSessionId = this.normalizeOptionalText?.(record.local_session_id);
301
+ if (!localSessionId) {
302
+ return [];
303
+ }
304
+ return [
305
+ {
306
+ local_session_id: localSessionId,
307
+ session_label: this.normalizeOptionalText?.(record.session_label) ?? null,
308
+ tools_hash: this.normalizeOptionalText?.(record.tools_hash) ?? null,
309
+ cwd: this.normalizeOptionalText?.(record.cwd) ?? null,
310
+ },
311
+ ];
312
+ })
313
+ : [];
314
+ await this.db
315
+ .withSchema(MCP_SCHEMA)
316
+ .table("gateway_clients")
317
+ .where({ client_uuid: clientUuid })
318
+ .update({
319
+ ...(gatewayUserUuid ? { owner_user_uuid: gatewayUserUuid } : {}),
320
+ ...(clientLabel ? { client_label: clientLabel } : {}),
321
+ updated_at: now,
322
+ last_seen_at: now,
323
+ });
324
+ await this.db
325
+ .withSchema(MCP_SCHEMA)
326
+ .table("gateway_live_consoles")
327
+ .where({ connection_id: connectionId })
328
+ .del();
329
+ for (const sessionTool of sessionTools) {
330
+ const existing = await this.db
331
+ .withSchema(MCP_SCHEMA)
332
+ .table("gateway_live_consoles")
333
+ .where({
334
+ client_uuid: clientUuid,
335
+ local_session_id: sessionTool.local_session_id,
336
+ })
337
+ .first("live_console_uuid", "connected_at");
338
+ const payload = {
339
+ connection_id: connectionId,
340
+ client_uuid: clientUuid,
341
+ local_session_id: sessionTool.local_session_id,
342
+ ...(sessionTool.session_label
343
+ ? { session_label: sessionTool.session_label }
344
+ : {}),
345
+ ...(sessionTool.cwd ? { cwd: sessionTool.cwd } : {}),
346
+ ...(sessionTool.tools_hash ? { tools_hash: sessionTool.tools_hash } : {}),
347
+ ...(gatewayUserUuid ? { gateway_user_uuid: gatewayUserUuid } : {}),
348
+ ...(clientLabel ? { client_label: clientLabel } : {}),
349
+ ...(systemUsername ? { system_username: systemUsername } : {}),
350
+ ...(namespace ? { namespace } : {}),
351
+ ...(nodeId ? { node_id: nodeId } : {}),
352
+ ...(packageVersion ? { package_version: packageVersion } : {}),
353
+ ...(protocolVersion ? { protocol_version: protocolVersion } : {}),
354
+ meta: this.db.raw(`?::jsonb`, [
355
+ JSON.stringify({
356
+ ...(sessionTool.cwd ? { cwd: sessionTool.cwd } : {}),
357
+ }),
358
+ ]),
359
+ last_seen_at: now,
360
+ };
361
+ if (existing?.live_console_uuid) {
362
+ await this.db
363
+ .withSchema(MCP_SCHEMA)
364
+ .table("gateway_live_consoles")
365
+ .where({ live_console_uuid: existing.live_console_uuid })
366
+ .update(payload);
367
+ continue;
368
+ }
369
+ await this.db.withSchema(MCP_SCHEMA).table("gateway_live_consoles").insert({
370
+ live_console_uuid: (0, node_crypto_1.randomUUID)(),
371
+ ...payload,
372
+ connected_at: now,
373
+ });
374
+ }
375
+ return {
376
+ client_uuid: clientUuid,
377
+ connection_id: connectionId,
378
+ total: sessionTools.length,
379
+ };
380
+ },
381
+ async listLiveClientsRecord(input = {}) {
382
+ const scopeKey = (0, gatewayScope_1.resolveGatewayScopeKey)(input);
383
+ const ownerUserUuid = await this.resolveOwnerUserUuidFilter?.(input);
384
+ const rows = await this.db
385
+ .withSchema(MCP_SCHEMA)
386
+ .table("gateway_live_consoles as lc")
387
+ .join("gateway_clients as c", "c.client_uuid", "lc.client_uuid")
388
+ .modify((query) => {
389
+ if (scopeKey) {
390
+ query.where("c.scope_key", scopeKey);
391
+ }
392
+ if (ownerUserUuid) {
393
+ query.where("c.owner_user_uuid", ownerUserUuid);
394
+ }
395
+ })
396
+ .select("lc.client_uuid", "lc.local_session_id", "lc.session_label", "lc.tools_hash", "lc.gateway_user_uuid", "lc.client_label", "lc.system_username", "lc.namespace", "lc.node_id", "lc.package_version", "lc.protocol_version", "lc.last_seen_at")
397
+ .orderBy("lc.client_uuid", "asc")
398
+ .orderBy("lc.local_session_id", "asc");
399
+ const grouped = new Map();
400
+ for (const row of rows) {
401
+ const current = grouped.get(String(row.client_uuid)) ?? {
402
+ client_uuid: String(row.client_uuid),
403
+ ...(row.gateway_user_uuid
404
+ ? { gateway_user_uuid: String(row.gateway_user_uuid) }
405
+ : {}),
406
+ ...(row.client_label ? { client_label: String(row.client_label) } : {}),
407
+ ...(row.system_username
408
+ ? { system_username: String(row.system_username) }
409
+ : {}),
410
+ ...(row.namespace ? { namespace: String(row.namespace) } : {}),
411
+ ...(row.node_id ? { node_id: String(row.node_id) } : {}),
412
+ ...(row.package_version
413
+ ? { package_version: String(row.package_version) }
414
+ : {}),
415
+ ...(row.protocol_version
416
+ ? { protocol_version: String(row.protocol_version) }
417
+ : {}),
418
+ session_tools: [],
419
+ };
420
+ current.session_tools.push({
421
+ local_session_id: String(row.local_session_id),
422
+ ...(row.session_label ? { session_label: String(row.session_label) } : {}),
423
+ ...(row.tools_hash ? { tools_hash: String(row.tools_hash) } : {}),
424
+ });
425
+ grouped.set(current.client_uuid, current);
426
+ }
427
+ return {
428
+ clients: Array.from(grouped.values()).sort((left, right) => left.client_uuid.localeCompare(right.client_uuid)),
429
+ };
430
+ },
431
+ async listLiveConsolesRecord(input = {}) {
432
+ const scopeKey = (0, gatewayScope_1.resolveGatewayScopeKey)(input);
433
+ const ownerUserUuid = await this.resolveOwnerUserUuidFilter?.(input);
434
+ const filterClientUuid = this.normalizeOptionalText?.(input.client_uuid);
435
+ const rows = await this.db
436
+ .withSchema(MCP_SCHEMA)
437
+ .table("gateway_live_consoles as lc")
438
+ .join("gateway_clients as c", "c.client_uuid", "lc.client_uuid")
439
+ .leftJoin("gateway_users as u", "u.gateway_user_uuid", "c.owner_user_uuid")
440
+ .leftJoin("gateway_project_consoles as pc", function joinProjectConsole() {
441
+ this.on("pc.client_uuid", "=", "lc.client_uuid")
442
+ .andOn("pc.local_session_id", "=", "lc.local_session_id")
443
+ .andOnVal("pc.status", "=", "active");
444
+ })
445
+ .leftJoin("gateway_projects as p", "p.project_uuid", "pc.project_uuid")
446
+ .modify((query) => {
447
+ if (scopeKey) {
448
+ query.where("c.scope_key", scopeKey);
449
+ }
450
+ if (ownerUserUuid) {
451
+ query.where("c.owner_user_uuid", ownerUserUuid);
452
+ }
453
+ if (filterClientUuid) {
454
+ query.where("lc.client_uuid", filterClientUuid);
455
+ }
456
+ })
457
+ .select("lc.client_uuid", "lc.local_session_id", "lc.cwd", "lc.session_label", "lc.client_label", "lc.system_username", "lc.node_id", "lc.package_version", "lc.protocol_version", "c.bot_username", "pc.project_uuid", "p.name as project_name", this.db.raw("nullif(u.telegram_username, '') as telegram_username"), this.db.raw("nullif(u.telegram_display_name, '') as telegram_display_name"))
458
+ .orderBy("lc.client_uuid", "asc")
459
+ .orderBy("lc.local_session_id", "asc");
460
+ const merged = new Map();
461
+ for (const row of rows) {
462
+ const key = `${row.client_uuid}:${row.local_session_id}`;
463
+ const current = merged.get(key);
464
+ const projectUuids = new Set(current?.project_uuids ?? []);
465
+ const projectNames = new Set(current?.project_names ?? []);
466
+ if (row.project_uuid) {
467
+ projectUuids.add(String(row.project_uuid));
468
+ }
469
+ if (row.project_name) {
470
+ projectNames.add(String(row.project_name));
471
+ }
472
+ merged.set(key, {
473
+ session_id: key,
474
+ client_uuid: String(row.client_uuid),
475
+ local_session_id: String(row.local_session_id),
476
+ cwd: current?.cwd ?? (row.cwd ? String(row.cwd) : null),
477
+ session_label: current?.session_label ??
478
+ (row.session_label ? String(row.session_label) : null),
479
+ client_label: current?.client_label ??
480
+ (row.client_label ? String(row.client_label) : null),
481
+ system_username: current?.system_username ??
482
+ (row.system_username ? String(row.system_username) : null),
483
+ telegram_username: current?.telegram_username ??
484
+ (row.telegram_username ? String(row.telegram_username) : null),
485
+ telegram_display_name: current?.telegram_display_name ??
486
+ (row.telegram_display_name ? String(row.telegram_display_name) : null),
487
+ bot_username: current?.bot_username ??
488
+ (row.bot_username ? String(row.bot_username) : null),
489
+ node_id: current?.node_id ?? (row.node_id ? String(row.node_id) : null),
490
+ package_version: current?.package_version ??
491
+ (row.package_version ? String(row.package_version) : null),
492
+ protocol_version: current?.protocol_version ??
493
+ (row.protocol_version ? String(row.protocol_version) : null),
494
+ project_uuids: Array.from(projectUuids),
495
+ project_names: Array.from(projectNames),
496
+ connected: true,
497
+ registered: projectUuids.size > 0,
498
+ });
499
+ }
500
+ return {
501
+ sessions: Array.from(merged.values()).sort((left, right) => left.session_id.localeCompare(right.session_id)),
502
+ };
503
+ },
504
+ async resolveLiveConsoleRecord(input) {
505
+ const sessionId = this.requireText?.(input.sessionId ?? input.session_id, "sessionId");
506
+ if (!sessionId) {
507
+ throw new Error("sessionId is required");
508
+ }
509
+ const ownerUserUuid = await this.resolveOwnerUserUuidFilter?.(input);
510
+ const compositeSeparatorIndex = sessionId.indexOf(":");
511
+ const clientUuid = compositeSeparatorIndex > 0 ? sessionId.slice(0, compositeSeparatorIndex).trim() : "";
512
+ const localSessionId = compositeSeparatorIndex > 0
513
+ ? sessionId.slice(compositeSeparatorIndex + 1).trim()
514
+ : sessionId;
515
+ if (clientUuid && localSessionId) {
516
+ const row = await this.db
517
+ .withSchema(MCP_SCHEMA)
518
+ .table("gateway_live_consoles as lc")
519
+ .join("gateway_clients as c", "c.client_uuid", "lc.client_uuid")
520
+ .where("lc.client_uuid", clientUuid)
521
+ .where("lc.local_session_id", localSessionId)
522
+ .modify((query) => {
523
+ if (ownerUserUuid) {
524
+ query.where("c.owner_user_uuid", ownerUserUuid);
525
+ }
526
+ })
527
+ .first("lc.client_uuid", "lc.local_session_id", "lc.session_label");
528
+ return row
529
+ ? {
530
+ client_uuid: String(row.client_uuid),
531
+ local_session_id: String(row.local_session_id),
532
+ ...(row.session_label ? { session_label: String(row.session_label) } : {}),
533
+ }
534
+ : null;
535
+ }
536
+ const rows = await this.db
537
+ .withSchema(MCP_SCHEMA)
538
+ .table("gateway_live_consoles as lc")
539
+ .join("gateway_clients as c", "c.client_uuid", "lc.client_uuid")
540
+ .where("lc.local_session_id", localSessionId)
541
+ .modify((query) => {
542
+ if (ownerUserUuid) {
543
+ query.where("c.owner_user_uuid", ownerUserUuid);
544
+ }
545
+ })
546
+ .select("lc.client_uuid", "lc.local_session_id", "lc.session_label");
547
+ if (rows.length === 0) {
548
+ return null;
549
+ }
550
+ if (rows.length > 1) {
551
+ throw new Error(`Console session_id '${localSessionId}' is ambiguous across live clients. Use the canonical gateway session_id format client_uuid:local_session_id.`);
552
+ }
553
+ const row = rows[0];
554
+ return {
555
+ client_uuid: String(row.client_uuid),
556
+ local_session_id: String(row.local_session_id),
557
+ ...(row.session_label ? { session_label: String(row.session_label) } : {}),
558
+ };
559
+ },
115
560
  async createProjectRecord(input) {
116
561
  const clientUuid = this.requireText?.(input.client_uuid, "client_uuid");
117
562
  const name = this.requireText?.(input.name, "name");
563
+ const localSessionId = this.requireText?.(input.local_session_id, "local_session_id");
118
564
  const client = await this.db
119
565
  .withSchema(MCP_SCHEMA)
120
566
  .table("gateway_clients")
@@ -130,31 +576,68 @@ const TelegramMcpGatewayService = {
130
576
  const projectUuid = (0, node_crypto_1.randomUUID)();
131
577
  const inviteToken = (0, node_crypto_1.randomUUID)();
132
578
  const now = new Date().toISOString();
133
- await this.db.withSchema(MCP_SCHEMA).table("gateway_projects").insert({
134
- project_uuid: projectUuid,
135
- name,
136
- invite_token: inviteToken,
137
- created_by_client_uuid: clientUuid,
138
- ...(ownerTelegramUserId
139
- ? { owner_telegram_user_id: ownerTelegramUserId }
140
- : {}),
141
- ...(ownerTelegramUsername
142
- ? { owner_telegram_username: ownerTelegramUsername }
143
- : {}),
144
- ...(ownerDisplayName ? { owner_display_name: ownerDisplayName } : {}),
145
- is_active: true,
146
- created_at: now,
147
- updated_at: now,
579
+ await this.db.transaction(async (trx) => {
580
+ await trx.withSchema(MCP_SCHEMA).table("gateway_projects").insert({
581
+ project_uuid: projectUuid,
582
+ name,
583
+ invite_token: inviteToken,
584
+ created_by_client_uuid: clientUuid,
585
+ ...(ownerTelegramUserId
586
+ ? { owner_telegram_user_id: ownerTelegramUserId }
587
+ : {}),
588
+ ...(ownerTelegramUsername
589
+ ? { owner_telegram_username: ownerTelegramUsername }
590
+ : {}),
591
+ ...(ownerDisplayName ? { owner_display_name: ownerDisplayName } : {}),
592
+ is_active: true,
593
+ created_at: now,
594
+ updated_at: now,
595
+ });
596
+ await trx.withSchema(MCP_SCHEMA).table("gateway_project_members").insert({
597
+ project_uuid: projectUuid,
598
+ client_uuid: clientUuid,
599
+ role: "owner",
600
+ status: "active",
601
+ ...(ownerTelegramUserId ? { telegram_user_id: ownerTelegramUserId } : {}),
602
+ ...(ownerTelegramUsername ? { telegram_username: ownerTelegramUsername } : {}),
603
+ ...(ownerDisplayName ? { display_name: ownerDisplayName } : {}),
604
+ joined_at: now,
605
+ });
148
606
  });
149
- await this.db.withSchema(MCP_SCHEMA).table("gateway_project_members").insert({
150
- project_uuid: projectUuid,
151
- client_uuid: clientUuid,
152
- role: "owner",
153
- status: "active",
154
- ...(ownerTelegramUserId ? { telegram_user_id: ownerTelegramUserId } : {}),
155
- ...(ownerTelegramUsername ? { telegram_username: ownerTelegramUsername } : {}),
156
- ...(ownerDisplayName ? { display_name: ownerDisplayName } : {}),
157
- joined_at: now,
607
+ let registration;
608
+ try {
609
+ registration = await this.registerProjectConsoleForSession?.({
610
+ client_uuid: clientUuid,
611
+ project_uuid: projectUuid,
612
+ local_session_id: localSessionId,
613
+ ...(this.normalizeOptionalText?.(input.label)
614
+ ? { label: this.normalizeOptionalText?.(input.label) }
615
+ : {}),
616
+ ...(this.normalizeOptionalText?.(input.cwd)
617
+ ? { cwd: this.normalizeOptionalText?.(input.cwd) }
618
+ : {}),
619
+ status: "active",
620
+ ...(input.meta && typeof input.meta === "object" && !Array.isArray(input.meta)
621
+ ? { meta: input.meta }
622
+ : {}),
623
+ });
624
+ if (!registration?.session_uuid) {
625
+ throw new Error(`Project ${projectUuid} was created, but console registration for ${clientUuid}:${localSessionId} failed.`);
626
+ }
627
+ }
628
+ catch (error) {
629
+ await this.db
630
+ .withSchema(MCP_SCHEMA)
631
+ .table("gateway_projects")
632
+ .where({ project_uuid: projectUuid })
633
+ .del();
634
+ throw error;
635
+ }
636
+ this.logger.info("Gateway project created and console bound", {
637
+ projectUuid,
638
+ clientUuid,
639
+ localSessionId,
640
+ sessionUuid: registration.session_uuid,
158
641
  });
159
642
  return {
160
643
  project_uuid: projectUuid,
@@ -166,6 +649,7 @@ const TelegramMcpGatewayService = {
166
649
  async joinProjectRecord(input) {
167
650
  const clientUuid = this.requireText?.(input.client_uuid, "client_uuid");
168
651
  const inviteToken = this.requireText?.(input.invite_token, "invite_token");
652
+ const localSessionId = this.requireText?.(input.local_session_id, "local_session_id");
169
653
  const client = await this.db
170
654
  .withSchema(MCP_SCHEMA)
171
655
  .table("gateway_clients")
@@ -178,14 +662,21 @@ const TelegramMcpGatewayService = {
178
662
  const memberTelegramUserId = this.normalizeOptionalText?.(clientMeta.telegram_user_id);
179
663
  const memberTelegramUsername = this.normalizeOptionalText?.(clientMeta.telegram_username);
180
664
  const memberDisplayName = this.normalizeOptionalText?.(clientMeta.telegram_display_name);
665
+ const memberScopeKey = this.normalizeOptionalText?.(client.scope_key);
181
666
  const project = await this.db
182
667
  .withSchema(MCP_SCHEMA)
183
- .table("gateway_projects")
184
- .where({ invite_token: inviteToken, is_active: true })
668
+ .table("gateway_projects as p")
669
+ .leftJoin("gateway_clients as owner_client", "owner_client.client_uuid", "p.created_by_client_uuid")
670
+ .where({ "p.invite_token": inviteToken, "p.is_active": true })
671
+ .select("p.*", "owner_client.scope_key as owner_scope_key")
185
672
  .first();
186
673
  if (!project) {
187
674
  throw new Error("Project invite token is invalid or inactive");
188
675
  }
676
+ const ownerScopeKey = this.normalizeOptionalText?.(project.owner_scope_key);
677
+ if (memberScopeKey && ownerScopeKey && memberScopeKey !== ownerScopeKey) {
678
+ throw new Error("Project belongs to a different gateway scope.");
679
+ }
189
680
  const existing = await this.db
190
681
  .withSchema(MCP_SCHEMA)
191
682
  .table("gateway_project_members")
@@ -228,6 +719,42 @@ const TelegramMcpGatewayService = {
228
719
  joined_at: new Date().toISOString(),
229
720
  });
230
721
  }
722
+ try {
723
+ const registration = await this.registerProjectConsoleForSession?.({
724
+ client_uuid: clientUuid,
725
+ project_uuid: String(project.project_uuid),
726
+ local_session_id: localSessionId,
727
+ ...(this.normalizeOptionalText?.(input.label)
728
+ ? { label: this.normalizeOptionalText?.(input.label) }
729
+ : {}),
730
+ ...(this.normalizeOptionalText?.(input.cwd)
731
+ ? { cwd: this.normalizeOptionalText?.(input.cwd) }
732
+ : {}),
733
+ status: "active",
734
+ ...(input.meta && typeof input.meta === "object" && !Array.isArray(input.meta)
735
+ ? { meta: input.meta }
736
+ : {}),
737
+ });
738
+ if (!registration?.session_uuid) {
739
+ throw new Error(`Project ${String(project.project_uuid)} membership exists, but console registration for ${clientUuid}:${localSessionId} failed.`);
740
+ }
741
+ }
742
+ catch (error) {
743
+ if (existing.status !== "active") {
744
+ await this.db
745
+ .withSchema(MCP_SCHEMA)
746
+ .table("gateway_project_members")
747
+ .where({
748
+ project_uuid: project.project_uuid,
749
+ client_uuid: clientUuid,
750
+ })
751
+ .update({
752
+ status: existing.status,
753
+ joined_at: existing.joined_at,
754
+ });
755
+ }
756
+ throw error;
757
+ }
231
758
  return {
232
759
  project_uuid: project.project_uuid,
233
760
  invite_token: project.invite_token,
@@ -254,6 +781,37 @@ const TelegramMcpGatewayService = {
254
781
  ...(memberDisplayName ? { display_name: memberDisplayName } : {}),
255
782
  joined_at: new Date().toISOString(),
256
783
  });
784
+ try {
785
+ const registration = await this.registerProjectConsoleForSession?.({
786
+ client_uuid: clientUuid,
787
+ project_uuid: String(project.project_uuid),
788
+ local_session_id: localSessionId,
789
+ ...(this.normalizeOptionalText?.(input.label)
790
+ ? { label: this.normalizeOptionalText?.(input.label) }
791
+ : {}),
792
+ ...(this.normalizeOptionalText?.(input.cwd)
793
+ ? { cwd: this.normalizeOptionalText?.(input.cwd) }
794
+ : {}),
795
+ status: "active",
796
+ ...(input.meta && typeof input.meta === "object" && !Array.isArray(input.meta)
797
+ ? { meta: input.meta }
798
+ : {}),
799
+ });
800
+ if (!registration?.session_uuid) {
801
+ throw new Error(`Project ${String(project.project_uuid)} was joined, but console registration for ${clientUuid}:${localSessionId} failed.`);
802
+ }
803
+ }
804
+ catch (error) {
805
+ await this.db
806
+ .withSchema(MCP_SCHEMA)
807
+ .table("gateway_project_members")
808
+ .where({
809
+ project_uuid: project.project_uuid,
810
+ client_uuid: clientUuid,
811
+ })
812
+ .del();
813
+ throw error;
814
+ }
257
815
  const notifyRows = await this.db
258
816
  .withSchema(MCP_SCHEMA)
259
817
  .table("gateway_project_members")
@@ -302,38 +860,30 @@ const TelegramMcpGatewayService = {
302
860
  .withSchema(MCP_SCHEMA)
303
861
  .table("gateway_sessions")
304
862
  .where({
305
- project_uuid: projectUuid,
306
863
  client_uuid: clientUuid,
307
864
  local_session_id: localSessionId,
308
865
  })
309
866
  .first();
310
- const payload = {
867
+ const existingProjectConsole = await this.db
868
+ .withSchema(MCP_SCHEMA)
869
+ .table("gateway_project_consoles")
870
+ .where({
311
871
  project_uuid: projectUuid,
312
872
  client_uuid: clientUuid,
313
873
  local_session_id: localSessionId,
874
+ })
875
+ .first("project_console_uuid");
876
+ const payload = {
877
+ client_uuid: clientUuid,
878
+ local_session_id: localSessionId,
314
879
  ...(this.normalizeOptionalText?.(input.label)
315
880
  ? { label: this.normalizeOptionalText?.(input.label) }
316
881
  : {}),
317
882
  ...(this.normalizeOptionalText?.(input.cwd)
318
883
  ? { cwd: this.normalizeOptionalText?.(input.cwd) }
319
884
  : {}),
320
- ...(this.normalizeOptionalText?.(input.tmux_session_name)
321
- ? { tmux_session_name: this.normalizeOptionalText?.(input.tmux_session_name) }
322
- : {}),
323
- ...(this.normalizeOptionalText?.(input.tmux_window_name)
324
- ? { tmux_window_name: this.normalizeOptionalText?.(input.tmux_window_name) }
325
- : {}),
326
- ...(Number.isInteger(input.tmux_window_index)
327
- ? { tmux_window_index: input.tmux_window_index }
328
- : {}),
329
- ...(this.normalizeOptionalText?.(input.tmux_pane_id)
330
- ? { tmux_pane_id: this.normalizeOptionalText?.(input.tmux_pane_id) }
331
- : {}),
332
- ...(Number.isInteger(input.tmux_pane_index)
333
- ? { tmux_pane_index: input.tmux_pane_index }
334
- : {}),
335
- ...(this.normalizeOptionalText?.(input.tmux_target)
336
- ? { tmux_target: this.normalizeOptionalText?.(input.tmux_target) }
885
+ ...(this.normalizeOptionalText?.(input.terminal_target)
886
+ ? { terminal_target: this.normalizeOptionalText?.(input.terminal_target) }
337
887
  : {}),
338
888
  status: this.normalizeOptionalText?.(input.status) || "active",
339
889
  meta: this.db.raw(`?::jsonb`, [
@@ -349,6 +899,36 @@ const TelegramMcpGatewayService = {
349
899
  .table("gateway_sessions")
350
900
  .where({ session_uuid: existing.session_uuid })
351
901
  .update(payload);
902
+ if (existingProjectConsole?.project_console_uuid) {
903
+ await this.db
904
+ .withSchema(MCP_SCHEMA)
905
+ .table("gateway_project_consoles")
906
+ .where({ project_console_uuid: existingProjectConsole.project_console_uuid })
907
+ .update({
908
+ gateway_session_uuid: existing.session_uuid,
909
+ status: "active",
910
+ updated_at: now,
911
+ });
912
+ }
913
+ else {
914
+ await this.db.withSchema(MCP_SCHEMA).table("gateway_project_consoles").insert({
915
+ project_console_uuid: (0, node_crypto_1.randomUUID)(),
916
+ project_uuid: projectUuid,
917
+ client_uuid: clientUuid,
918
+ local_session_id: localSessionId,
919
+ gateway_session_uuid: existing.session_uuid,
920
+ status: "active",
921
+ joined_at: now,
922
+ updated_at: now,
923
+ });
924
+ }
925
+ this.logger.info("Gateway project console session registered", {
926
+ projectUuid,
927
+ clientUuid,
928
+ localSessionId,
929
+ sessionUuid: existing.session_uuid,
930
+ created: false,
931
+ });
352
932
  return {
353
933
  session_uuid: existing.session_uuid,
354
934
  created: false,
@@ -361,37 +941,118 @@ const TelegramMcpGatewayService = {
361
941
  ...payload,
362
942
  created_at: now,
363
943
  });
944
+ if (existingProjectConsole?.project_console_uuid) {
945
+ await this.db
946
+ .withSchema(MCP_SCHEMA)
947
+ .table("gateway_project_consoles")
948
+ .where({ project_console_uuid: existingProjectConsole.project_console_uuid })
949
+ .update({
950
+ gateway_session_uuid: sessionUuid,
951
+ status: "active",
952
+ updated_at: now,
953
+ });
954
+ }
955
+ else {
956
+ await this.db.withSchema(MCP_SCHEMA).table("gateway_project_consoles").insert({
957
+ project_console_uuid: (0, node_crypto_1.randomUUID)(),
958
+ project_uuid: projectUuid,
959
+ client_uuid: clientUuid,
960
+ local_session_id: localSessionId,
961
+ gateway_session_uuid: sessionUuid,
962
+ status: "active",
963
+ joined_at: now,
964
+ updated_at: now,
965
+ });
966
+ }
967
+ this.logger.info("Gateway project console session registered", {
968
+ projectUuid,
969
+ clientUuid,
970
+ localSessionId,
971
+ sessionUuid,
972
+ created: true,
973
+ });
364
974
  return {
365
975
  session_uuid: sessionUuid,
366
976
  created: true,
367
977
  updated_at: now,
368
978
  };
369
979
  },
370
- async unregisterSessionRecord(input) {
371
- const clientUuid = this.requireText?.(input.client_uuid, "client_uuid");
372
- const localSessionId = this.requireText?.(input.local_session_id, "local_session_id");
373
- const deleted = await this.db
980
+ async registerProjectConsoleForSession(input) {
981
+ const result = await this.registerSessionRecord?.(input);
982
+ if (!result?.session_uuid) {
983
+ throw new Error("Console registration returned no session_uuid.");
984
+ }
985
+ return result;
986
+ },
987
+ async listClientsRecord(input = {}) {
988
+ const scopeKey = (0, gatewayScope_1.resolveGatewayScopeKey)(input);
989
+ const ownerUserUuid = await this.resolveOwnerUserUuidFilter?.(input);
990
+ const rows = await this.db
374
991
  .withSchema(MCP_SCHEMA)
375
- .table("gateway_sessions")
376
- .where({
377
- client_uuid: clientUuid,
378
- local_session_id: localSessionId,
992
+ .table("gateway_clients as c")
993
+ .leftJoin("gateway_live_consoles as lc", "c.client_uuid", "lc.client_uuid")
994
+ .modify((query) => {
995
+ if (scopeKey) {
996
+ query.where("c.scope_key", scopeKey);
997
+ }
998
+ if (ownerUserUuid) {
999
+ query.where("c.owner_user_uuid", ownerUserUuid);
1000
+ }
379
1001
  })
380
- .del();
1002
+ .groupBy("c.client_uuid", "c.client_label", this.db.raw("nullif(c.meta->>'namespace', '')"), this.db.raw("nullif(c.meta->>'node_id', '')"), this.db.raw("nullif(c.meta->>'system_username', '')"), this.db.raw("nullif(c.meta->>'telegram_username', '')"), this.db.raw("nullif(c.meta->>'telegram_display_name', '')"), "c.bot_username", "c.last_seen_at", "c.updated_at")
1003
+ .select("c.client_uuid", "c.client_label", this.db.raw("nullif(c.meta->>'namespace', '') as namespace"), this.db.raw("nullif(c.meta->>'node_id', '') as node_id"), this.db.raw("nullif(c.meta->>'system_username', '') as system_username"), this.db.raw("nullif(c.meta->>'telegram_username', '') as telegram_username"), this.db.raw("nullif(c.meta->>'telegram_display_name', '') as telegram_display_name"), "c.bot_username", "c.last_seen_at", "c.updated_at", this.db.raw("count(lc.live_console_uuid) as session_count"))
1004
+ .orderBy("c.last_seen_at", "desc")
1005
+ .orderBy("c.client_uuid", "asc");
1006
+ this.logger.info("Gateway clients list queried", {
1007
+ count: rows.length,
1008
+ clientUuids: rows
1009
+ .map((row) => String(row.client_uuid ?? ""))
1010
+ .filter(Boolean),
1011
+ ...(scopeKey ? { scopeKey } : {}),
1012
+ });
381
1013
  return {
382
- local_session_id: localSessionId,
383
- deleted,
1014
+ clients: rows.map((row) => ({
1015
+ client_uuid: String(row.client_uuid),
1016
+ client_label: row.client_label ? String(row.client_label) : null,
1017
+ namespace: row.namespace ? String(row.namespace) : null,
1018
+ node_id: row.node_id ? String(row.node_id) : null,
1019
+ system_username: row.system_username ? String(row.system_username) : null,
1020
+ telegram_username: row.telegram_username ? String(row.telegram_username) : null,
1021
+ telegram_display_name: row.telegram_display_name ? String(row.telegram_display_name) : null,
1022
+ bot_username: row.bot_username ? String(row.bot_username) : null,
1023
+ ...(row.last_seen_at ? { last_seen_at: String(row.last_seen_at) } : {}),
1024
+ ...(row.updated_at ? { updated_at: String(row.updated_at) } : {}),
1025
+ session_count: Number(row.session_count || 0),
1026
+ })),
384
1027
  };
385
1028
  },
386
1029
  async listProjectsRecord(input) {
387
1030
  const clientUuid = this.requireText?.(input.client_uuid, "client_uuid");
1031
+ const localSessionId = this.normalizeOptionalText?.(input.local_session_id);
1032
+ const scopeKey = (0, gatewayScope_1.resolveGatewayScopeKey)(input);
388
1033
  const rows = await this.db
389
1034
  .withSchema(MCP_SCHEMA)
390
1035
  .table("gateway_project_members as m")
391
1036
  .join("gateway_projects as p", "p.project_uuid", "m.project_uuid")
1037
+ .join("gateway_clients as c", "c.client_uuid", "m.client_uuid")
392
1038
  .where("m.client_uuid", clientUuid)
393
1039
  .where("m.status", "active")
394
1040
  .where("p.is_active", true)
1041
+ .modify((query) => {
1042
+ if (localSessionId) {
1043
+ query.whereExists(function matchBoundConsole() {
1044
+ this.select(this.client.raw("1"))
1045
+ .from("mcp.gateway_project_consoles as pc")
1046
+ .whereRaw("pc.project_uuid = m.project_uuid")
1047
+ .whereRaw("pc.client_uuid = m.client_uuid")
1048
+ .where("pc.status", "active")
1049
+ .where("pc.local_session_id", localSessionId);
1050
+ });
1051
+ }
1052
+ if (scopeKey) {
1053
+ query.where("c.scope_key", scopeKey);
1054
+ }
1055
+ })
395
1056
  .select("p.project_uuid", "p.name", "p.invite_token", "m.role", "m.status", "m.joined_at")
396
1057
  .orderBy("p.name", "asc");
397
1058
  return {
@@ -443,6 +1104,19 @@ const TelegramMcpGatewayService = {
443
1104
  .update({
444
1105
  status: "left",
445
1106
  });
1107
+ if (updated > 0) {
1108
+ await this.db
1109
+ .withSchema(MCP_SCHEMA)
1110
+ .table("gateway_project_consoles")
1111
+ .where({
1112
+ client_uuid: clientUuid,
1113
+ project_uuid: projectUuid,
1114
+ })
1115
+ .update({
1116
+ status: "inactive",
1117
+ updated_at: new Date().toISOString(),
1118
+ });
1119
+ }
446
1120
  const notifyRows = updated > 0
447
1121
  ? await this.db
448
1122
  .withSchema(MCP_SCHEMA)
@@ -456,6 +1130,16 @@ const TelegramMcpGatewayService = {
456
1130
  })
457
1131
  .distinct("client_uuid")
458
1132
  : [];
1133
+ if (updated > 0 && notifyRows.length === 0) {
1134
+ await this.db
1135
+ .withSchema(MCP_SCHEMA)
1136
+ .table("gateway_projects")
1137
+ .where({ project_uuid: projectUuid })
1138
+ .update({
1139
+ is_active: false,
1140
+ updated_at: new Date().toISOString(),
1141
+ });
1142
+ }
459
1143
  return {
460
1144
  project_uuid: projectUuid,
461
1145
  left: updated > 0,
@@ -510,6 +1194,14 @@ const TelegramMcpGatewayService = {
510
1194
  .table("gateway_projects")
511
1195
  .where({ project_uuid: projectUuid })
512
1196
  .del();
1197
+ await this.db
1198
+ .withSchema(MCP_SCHEMA)
1199
+ .table("gateway_project_consoles")
1200
+ .where({ project_uuid: projectUuid })
1201
+ .update({
1202
+ status: "inactive",
1203
+ updated_at: new Date().toISOString(),
1204
+ });
513
1205
  return {
514
1206
  project_uuid: projectUuid,
515
1207
  deleted: deleted > 0,
@@ -540,27 +1232,37 @@ const TelegramMcpGatewayService = {
540
1232
  }
541
1233
  const rows = await this.db
542
1234
  .withSchema(MCP_SCHEMA)
543
- .table("gateway_sessions as s")
544
- .leftJoin("gateway_clients as c", "c.client_uuid", "s.client_uuid")
1235
+ .table("gateway_project_consoles as pc")
1236
+ .leftJoin("gateway_live_consoles as lc", function joinLiveConsole() {
1237
+ this.on("lc.client_uuid", "=", "pc.client_uuid").andOn("lc.local_session_id", "=", "pc.local_session_id");
1238
+ })
1239
+ .leftJoin("gateway_clients as c", "c.client_uuid", "pc.client_uuid")
1240
+ .leftJoin("gateway_users as u", "u.gateway_user_uuid", "c.owner_user_uuid")
545
1241
  .leftJoin("gateway_project_members as m", function joinMember() {
546
- this.on("m.project_uuid", "=", "s.project_uuid").andOn("m.client_uuid", "=", "s.client_uuid");
1242
+ this.on("m.project_uuid", "=", "pc.project_uuid").andOn("m.client_uuid", "=", "pc.client_uuid");
547
1243
  })
548
- .where("s.project_uuid", projectUuid)
549
- .where("s.status", "active")
1244
+ .where("pc.project_uuid", projectUuid)
1245
+ .where("pc.status", "active")
550
1246
  .where("m.status", "active")
551
- .select("s.session_uuid", "s.project_uuid", "s.client_uuid", "s.local_session_id", "s.label", "s.status", "s.updated_at", "c.client_label", this.db.raw("coalesce(nullif(m.telegram_username, ''), nullif(c.meta->>'telegram_username', '')) as telegram_username"), "c.bot_username", "m.joined_at")
552
- .orderByRaw("coalesce(s.label, s.local_session_id) asc")
553
- .orderBy("s.updated_at", "desc");
1247
+ .orderBy("pc.client_uuid", "asc")
1248
+ .orderBy("pc.joined_at", "asc")
1249
+ .orderBy("pc.updated_at", "desc")
1250
+ .select("pc.gateway_session_uuid as session_uuid", "pc.project_uuid", "pc.client_uuid", "pc.local_session_id", "pc.status", "pc.updated_at", "lc.session_label as label", "c.client_label", this.db.raw("coalesce(nullif(u.telegram_display_name, ''), nullif(m.display_name, ''), nullif(c.meta->>'telegram_display_name', '')) as display_name"), this.db.raw("coalesce(nullif(u.telegram_username, ''), nullif(m.telegram_username, ''), nullif(c.meta->>'telegram_username', '')) as telegram_username"), this.db.raw("nullif(c.meta->>'system_username', '') as system_username"), "c.bot_username", "m.joined_at")
1251
+ .orderByRaw("coalesce(lc.session_label, pc.local_session_id) asc")
1252
+ .orderBy("pc.updated_at", "desc");
554
1253
  return {
555
1254
  sessions: rows.map((row) => ({
556
- session_uuid: row.session_uuid,
1255
+ session_uuid: row.session_uuid ??
1256
+ `${String(row.client_uuid)}:${String(row.local_session_id)}`,
557
1257
  project_uuid: row.project_uuid,
558
1258
  client_uuid: row.client_uuid,
559
1259
  local_session_id: row.local_session_id,
560
1260
  label: row.label ?? null,
561
1261
  status: row.status,
562
1262
  client_label: row.client_label ?? null,
1263
+ display_name: row.display_name ?? null,
563
1264
  telegram_username: row.telegram_username ?? null,
1265
+ system_username: row.system_username ?? null,
564
1266
  bot_username: row.bot_username ?? null,
565
1267
  ...(row.joined_at ? { joined_at: String(row.joined_at) } : {}),
566
1268
  ...(row.updated_at ? { updated_at: String(row.updated_at) } : {}),
@@ -582,14 +1284,18 @@ const TelegramMcpGatewayService = {
582
1284
  : kind === "question" || kind === "request";
583
1285
  const targetSession = await this.db
584
1286
  .withSchema(MCP_SCHEMA)
585
- .table("gateway_sessions as s")
586
- .leftJoin("gateway_clients as c", "c.client_uuid", "s.client_uuid")
587
- .leftJoin("gateway_projects as p", "p.project_uuid", "s.project_uuid")
588
- .where({
589
- "s.session_uuid": targetSessionId,
590
- "s.status": "active",
1287
+ .table("gateway_project_consoles as pc")
1288
+ .join("gateway_sessions as s", "s.session_uuid", "pc.gateway_session_uuid")
1289
+ .leftJoin("gateway_clients as c", "c.client_uuid", "pc.client_uuid")
1290
+ .leftJoin("gateway_projects as p", "p.project_uuid", "pc.project_uuid")
1291
+ .where("pc.gateway_session_uuid", targetSessionId)
1292
+ .modify((query) => {
1293
+ if (requestedProjectUuid) {
1294
+ query.where("pc.project_uuid", requestedProjectUuid);
1295
+ }
591
1296
  })
592
- .select("s.*", "p.name as project_name", this.db.raw("coalesce(nullif(c.meta->>'telegram_display_name', ''), nullif(c.meta->>'telegram_username', ''), c.client_label, c.bot_username) as target_actor_label"))
1297
+ .where("pc.status", "active")
1298
+ .select("s.*", "pc.project_uuid", "p.name as project_name", this.db.raw("coalesce(nullif(c.meta->>'telegram_display_name', ''), nullif(c.meta->>'telegram_username', ''), c.client_label, c.bot_username) as target_actor_label"))
593
1299
  .first();
594
1300
  if (!targetSession) {
595
1301
  throw new Error(`Target project session ${targetSessionId} was not found.`);
@@ -598,18 +1304,26 @@ const TelegramMcpGatewayService = {
598
1304
  requestedProjectUuid !== String(targetSession.project_uuid)) {
599
1305
  throw new Error("Target session does not belong to the requested project.");
600
1306
  }
601
- const sourceSession = await this.db
1307
+ const sourceProjectConsole = await this.db
602
1308
  .withSchema(MCP_SCHEMA)
603
- .table("gateway_sessions")
1309
+ .table("gateway_project_consoles")
604
1310
  .where({
605
1311
  project_uuid: String(targetSession.project_uuid),
606
1312
  client_uuid: clientUuid,
607
1313
  local_session_id: localSessionId,
608
1314
  status: "active",
609
1315
  })
1316
+ .first("gateway_session_uuid");
1317
+ if (!sourceProjectConsole?.gateway_session_uuid) {
1318
+ throw new Error(`Active project session '${localSessionId}' is not registered for client ${clientUuid} in project ${targetSession.project_uuid}.`);
1319
+ }
1320
+ const sourceSession = await this.db
1321
+ .withSchema(MCP_SCHEMA)
1322
+ .table("gateway_sessions")
1323
+ .where({ session_uuid: String(sourceProjectConsole.gateway_session_uuid) })
610
1324
  .first();
611
1325
  if (!sourceSession) {
612
- throw new Error(`Active project session '${localSessionId}' is not registered for client ${clientUuid} in project ${targetSession.project_uuid}.`);
1326
+ throw new Error(`Gateway session anchor for '${localSessionId}' was not found in project ${targetSession.project_uuid}.`);
613
1327
  }
614
1328
  const shareId = (0, node_crypto_1.randomUUID)();
615
1329
  const messageUuid = (0, node_crypto_1.randomUUID)();
@@ -642,7 +1356,7 @@ const TelegramMcpGatewayService = {
642
1356
  });
643
1357
  await this.db.withSchema(MCP_SCHEMA).table("gateway_messages").insert({
644
1358
  message_uuid: messageUuid,
645
- project_uuid: sourceSession.project_uuid,
1359
+ project_uuid: String(targetSession.project_uuid),
646
1360
  from_session_uuid: sourceSession.session_uuid,
647
1361
  to_session_uuid: targetSession.session_uuid,
648
1362
  kind,
@@ -720,6 +1434,7 @@ const TelegramMcpGatewayService = {
720
1434
  ...(targetSession.project_name
721
1435
  ? { project_name: targetSession.project_name }
722
1436
  : {}),
1437
+ project_uuid: String(targetSession.project_uuid),
723
1438
  ...(targetSession.target_actor_label
724
1439
  ? { target_actor_label: targetSession.target_actor_label }
725
1440
  : {}),
@@ -730,7 +1445,7 @@ const TelegramMcpGatewayService = {
730
1445
  share_id: shareId,
731
1446
  delivery_status: "queued",
732
1447
  note_path: `gateway://shares/${shareId}.md`,
733
- share_index_path: "gateway://SHARED_INDEX.md",
1448
+ xchange_record_id: shareId,
734
1449
  copied_artifacts: artifactRefs.map((artifact) => trimOptionalText(artifact.original_name) ||
735
1450
  trimOptionalText(artifact.relative_path) ||
736
1451
  trimOptionalText(artifact.file_path) ||
@@ -746,6 +1461,7 @@ const TelegramMcpGatewayService = {
746
1461
  ...(targetSession.project_name
747
1462
  ? { project_name: targetSession.project_name }
748
1463
  : {}),
1464
+ project_uuid: String(targetSession.project_uuid),
749
1465
  source_actor_label: sourceSession.label ?? sourceSession.local_session_id,
750
1466
  kind,
751
1467
  summary,
@@ -761,7 +1477,6 @@ const TelegramMcpGatewayService = {
761
1477
  target_session_label: targetSession.label ?? targetSession.local_session_id,
762
1478
  created_at: now,
763
1479
  note_relative_path: `shares/${shareId}.md`,
764
- share_index_file_name: "SHARED_INDEX.md",
765
1480
  artifacts: queuedArtifacts,
766
1481
  },
767
1482
  };
@@ -844,7 +1559,6 @@ const TelegramMcpGatewayService = {
844
1559
  target_session_label: row.target_session_label ?? row.target_local_session_id,
845
1560
  created_at: String(row.created_at),
846
1561
  note_relative_path: `shares/${shareId}.md`,
847
- share_index_file_name: "SHARED_INDEX.md",
848
1562
  artifacts: artifactsByMessage.get(row.message_uuid) ?? [],
849
1563
  };
850
1564
  }),
@@ -1016,6 +1730,22 @@ const TelegramMcpGatewayService = {
1016
1730
  },
1017
1731
  },
1018
1732
  actions: {
1733
+ upsertGatewayUser: {
1734
+ async handler(ctx) {
1735
+ if (!GATEWAY_ENABLED) {
1736
+ throw new Error("Gateway service is disabled in client mode");
1737
+ }
1738
+ return this.upsertGatewayUserRecord?.(ctx.params);
1739
+ },
1740
+ },
1741
+ resolveGatewayUserRoute: {
1742
+ async handler(ctx) {
1743
+ if (!GATEWAY_ENABLED) {
1744
+ throw new Error("Gateway service is disabled in client mode");
1745
+ }
1746
+ return this.resolveGatewayUserRouteRecord?.(ctx.params);
1747
+ },
1748
+ },
1019
1749
  registerClient: {
1020
1750
  async handler(ctx) {
1021
1751
  if (!GATEWAY_ENABLED) {
@@ -1024,6 +1754,62 @@ const TelegramMcpGatewayService = {
1024
1754
  return this.registerClientRecord?.(ctx.params);
1025
1755
  },
1026
1756
  },
1757
+ syncLiveConsoles: {
1758
+ async handler(ctx) {
1759
+ if (!GATEWAY_ENABLED) {
1760
+ throw new Error("Gateway service is disabled in client mode");
1761
+ }
1762
+ return this.syncLiveConsolesRecord?.(ctx.params);
1763
+ },
1764
+ },
1765
+ clearLiveConsoles: {
1766
+ async handler(ctx) {
1767
+ if (!GATEWAY_ENABLED) {
1768
+ throw new Error("Gateway service is disabled in client mode");
1769
+ }
1770
+ return this.clearLiveConsolesRecord?.(ctx.params);
1771
+ },
1772
+ },
1773
+ removeLiveConsoles: {
1774
+ async handler(ctx) {
1775
+ if (!GATEWAY_ENABLED) {
1776
+ throw new Error("Gateway service is disabled in client mode");
1777
+ }
1778
+ return this.removeLiveConsolesRecord?.(ctx.params);
1779
+ },
1780
+ },
1781
+ pruneGatewayState: {
1782
+ async handler() {
1783
+ if (!GATEWAY_ENABLED) {
1784
+ throw new Error("Gateway service is disabled in client mode");
1785
+ }
1786
+ return this.pruneGatewayStateRecord?.();
1787
+ },
1788
+ },
1789
+ listLiveClients: {
1790
+ async handler(ctx) {
1791
+ if (!GATEWAY_ENABLED) {
1792
+ throw new Error("Gateway service is disabled in client mode");
1793
+ }
1794
+ return this.listLiveClientsRecord?.(ctx.params);
1795
+ },
1796
+ },
1797
+ listLiveConsoles: {
1798
+ async handler(ctx) {
1799
+ if (!GATEWAY_ENABLED) {
1800
+ throw new Error("Gateway service is disabled in client mode");
1801
+ }
1802
+ return this.listLiveConsolesRecord?.(ctx.params);
1803
+ },
1804
+ },
1805
+ resolveLiveConsole: {
1806
+ async handler(ctx) {
1807
+ if (!GATEWAY_ENABLED) {
1808
+ throw new Error("Gateway service is disabled in client mode");
1809
+ }
1810
+ return this.resolveLiveConsoleRecord?.(ctx.params);
1811
+ },
1812
+ },
1027
1813
  createProject: {
1028
1814
  async handler(ctx) {
1029
1815
  if (!GATEWAY_ENABLED) {
@@ -1048,12 +1834,12 @@ const TelegramMcpGatewayService = {
1048
1834
  return this.registerSessionRecord?.(ctx.params);
1049
1835
  },
1050
1836
  },
1051
- unregisterSession: {
1837
+ listClients: {
1052
1838
  async handler(ctx) {
1053
1839
  if (!GATEWAY_ENABLED) {
1054
1840
  throw new Error("Gateway service is disabled in client mode");
1055
1841
  }
1056
- return this.unregisterSessionRecord?.(ctx.params);
1842
+ return this.listClientsRecord?.(ctx.params);
1057
1843
  },
1058
1844
  },
1059
1845
  listProjects: {
@@ -1137,5 +1923,14 @@ const TelegramMcpGatewayService = {
1137
1923
  },
1138
1924
  },
1139
1925
  },
1926
+ async started() {
1927
+ if (!GATEWAY_ENABLED) {
1928
+ return;
1929
+ }
1930
+ const cleared = await this.clearLiveConsolesRecord?.();
1931
+ this.logger.info("Gateway live consoles registry cleared on startup", {
1932
+ deleted: cleared?.deleted ?? 0,
1933
+ });
1934
+ },
1140
1935
  };
1141
1936
  exports.default = TelegramMcpGatewayService;