@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
@@ -1,18 +1,30 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.createMcpHttpHandler = createMcpHttpHandler;
4
7
  const node_crypto_1 = require("node:crypto");
5
8
  const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
9
+ const ws_1 = __importDefault(require("ws"));
6
10
  const auth_1 = require("./webapp/auth");
7
11
  const relay_1 = require("./webapp/relay");
8
12
  const assets_1 = require("./webapp/assets");
9
- const tmux_1 = require("./webapp/tmux");
10
- function formatTmuxHttpError(error, fallback) {
11
- if ((0, tmux_1.isTmuxUnavailableError)(error)) {
12
- return "tmux is unavailable";
13
+ const client_1 = require("../shared/integrations/terminal/client");
14
+ const terminal_1 = require("./webapp/terminal");
15
+ function formatTerminalHttpError(error, fallback) {
16
+ if ((0, terminal_1.isTerminalUnavailableError)(error)) {
17
+ return "terminal runtime is unavailable";
13
18
  }
14
19
  return fallback;
15
20
  }
21
+ function requireTelegramBotToken(runtime, purpose) {
22
+ const token = runtime.config.telegram.botToken?.trim();
23
+ if (!token) {
24
+ throw new Error(`Telegram bot token is unavailable on this node; cannot ${purpose}.`);
25
+ }
26
+ return token;
27
+ }
16
28
  function isInitializeRequest(body) {
17
29
  if (!body || typeof body !== "object") {
18
30
  return false;
@@ -117,22 +129,233 @@ function normalizeBasePath(value) {
117
129
  }
118
130
  return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
119
131
  }
120
- function isRelayTmuxUnavailableMessage(error) {
132
+ function readWebhookSecretHeader(req) {
133
+ const value = req.headers["x-telegram-bot-api-secret-token"];
134
+ if (Array.isArray(value)) {
135
+ return value[0];
136
+ }
137
+ return value;
138
+ }
139
+ function isRelayTerminalUnavailableMessage(error) {
121
140
  const text = error instanceof Error ? error.message : String(error);
122
- return text.includes("tmux is unavailable");
141
+ return text.includes("terminal runtime is unavailable");
123
142
  }
124
143
  function createMcpHttpHandler(runtime, input) {
125
144
  const transports = new Map();
126
145
  const webAppSessions = new auth_1.WebAppSessionRegistry();
146
+ const liveWsServer = new WebSocketServer({ noServer: true });
127
147
  const webAppBasePath = runtime.config.webapp.basePath.replace(/\/+$/u, "") || "/webapp";
128
148
  const rootPrefix = normalizeBasePath(process.env.ROOT_PREFIX || "/api");
129
149
  const publicWebAppBasePath = rootPrefix === "/"
130
150
  ? webAppBasePath
131
151
  : `${rootPrefix}${normalizeBasePath(webAppBasePath)}`;
152
+ const publicLiveWsPath = rootPrefix === "/" ? "/gateway/live/ws" : `${rootPrefix}/gateway/live/ws`;
132
153
  const webAppLivePrefix = `${webAppBasePath}/live/`;
154
+ const telegramWebhookPath = runtime.config.telegram.webhook.path.replace(/\/+$/u, "") ||
155
+ "/telegram/webhook";
133
156
  const closeSessionEntry = async (entry) => {
134
157
  await entry.close();
135
158
  };
159
+ const resolveAuthorizedWebAppSession = async (token) => {
160
+ const webAppSession = token ? webAppSessions.get(token) : null;
161
+ if (!webAppSession) {
162
+ return null;
163
+ }
164
+ const relayTarget = (0, relay_1.parseLiveRelaySessionId)(webAppSession.sessionId);
165
+ if (relayTarget) {
166
+ return {
167
+ webAppSession,
168
+ relayTarget,
169
+ session: null,
170
+ };
171
+ }
172
+ const binding = await runtime.bindingStore.getBinding(webAppSession.sessionId);
173
+ if (!binding || binding.telegramUserId !== webAppSession.telegramUserId) {
174
+ return null;
175
+ }
176
+ const session = await runtime.sessionStore.getSession(webAppSession.sessionId);
177
+ if (!session?.terminalTarget) {
178
+ return null;
179
+ }
180
+ return {
181
+ webAppSession,
182
+ relayTarget: null,
183
+ session,
184
+ };
185
+ };
186
+ liveWsServer.on("connection", (socket, req) => {
187
+ const requestUrl = new URL(req.url ?? "/", "http://gateway.local");
188
+ const token = requestUrl.searchParams.get("token")?.trim() ?? "";
189
+ void (async () => {
190
+ const resolved = await resolveAuthorizedWebAppSession(token);
191
+ if (!resolved) {
192
+ socket.close(1008, "Unauthorized");
193
+ return;
194
+ }
195
+ let closed = false;
196
+ let relayStreamClose = null;
197
+ let localUnsubscribe = null;
198
+ const cleanup = async () => {
199
+ if (closed) {
200
+ return;
201
+ }
202
+ closed = true;
203
+ try {
204
+ localUnsubscribe?.();
205
+ }
206
+ catch {
207
+ // ignore cleanup errors during socket shutdown
208
+ }
209
+ localUnsubscribe = null;
210
+ if (relayStreamClose) {
211
+ await relayStreamClose().catch(() => undefined);
212
+ relayStreamClose = null;
213
+ }
214
+ };
215
+ const sendJson = (payload) => {
216
+ if (socket.readyState === 1) {
217
+ socket.send(JSON.stringify(payload));
218
+ }
219
+ };
220
+ socket.on("close", () => {
221
+ void cleanup();
222
+ });
223
+ socket.on("message", (raw) => {
224
+ void (async () => {
225
+ const parsed = raw && typeof raw === "string"
226
+ ? JSON.parse(raw)
227
+ : JSON.parse(String(raw));
228
+ const type = parsed && typeof parsed === "object" && typeof parsed.type === "string"
229
+ ? parsed.type
230
+ : "";
231
+ if (type === "input") {
232
+ const data = typeof parsed.data === "string" ? parsed.data : "";
233
+ if (!data) {
234
+ return;
235
+ }
236
+ if (resolved.relayTarget) {
237
+ const gatewaySocketService = input.getGatewaySocketService?.();
238
+ await gatewaySocketService?.requestLiveRelay?.({
239
+ clientUuid: resolved.relayTarget.clientUuid,
240
+ localSessionId: resolved.relayTarget.localSessionId,
241
+ requestType: "action",
242
+ payload: {
243
+ action: "text",
244
+ text: data,
245
+ },
246
+ });
247
+ return;
248
+ }
249
+ if (resolved.session?.terminalTarget && (0, client_1.isStreamableTerminalTarget)(resolved.session.terminalTarget)) {
250
+ (0, client_1.sendForegroundTerminalInput)(resolved.session.terminalTarget, data);
251
+ }
252
+ return;
253
+ }
254
+ if (type === "action") {
255
+ const action = typeof parsed.action === "string" ? parsed.action : "";
256
+ if (![
257
+ "up",
258
+ "down",
259
+ "enter",
260
+ "slash",
261
+ "delete",
262
+ "tab",
263
+ "escape",
264
+ "interrupt",
265
+ ].includes(action)) {
266
+ return;
267
+ }
268
+ if (resolved.relayTarget) {
269
+ const gatewaySocketService = input.getGatewaySocketService?.();
270
+ await gatewaySocketService?.requestLiveRelay?.({
271
+ clientUuid: resolved.relayTarget.clientUuid,
272
+ localSessionId: resolved.relayTarget.localSessionId,
273
+ requestType: "action",
274
+ payload: { action },
275
+ });
276
+ return;
277
+ }
278
+ if (resolved.session?.terminalTarget) {
279
+ await (0, terminal_1.sendAllowedTerminalAction)(runtime.config.terminal, resolved.session.terminalTarget, action);
280
+ }
281
+ return;
282
+ }
283
+ if (type === "resize") {
284
+ const cols = typeof parsed.cols === "number" ? parsed.cols : NaN;
285
+ const rows = typeof parsed.rows === "number" ? parsed.rows : NaN;
286
+ if (!Number.isFinite(cols) || !Number.isFinite(rows)) {
287
+ return;
288
+ }
289
+ if (resolved.session?.terminalTarget && (0, client_1.isStreamableTerminalTarget)(resolved.session.terminalTarget)) {
290
+ (0, client_1.resizeForegroundTerminal)(resolved.session.terminalTarget, Math.max(20, Math.min(400, Math.round(cols))), Math.max(5, Math.min(200, Math.round(rows))));
291
+ }
292
+ }
293
+ })().catch((error) => {
294
+ runtime.logger.warn("Telegram WebApp live WS message handling failed", {
295
+ error: error instanceof Error ? (error.stack ?? error.message) : String(error),
296
+ });
297
+ });
298
+ });
299
+ if (resolved.relayTarget) {
300
+ const gatewaySocketService = input.getGatewaySocketService?.();
301
+ if (!gatewaySocketService?.openLiveRelayStream) {
302
+ socket.close(1011, "Relay live stream is unavailable");
303
+ return;
304
+ }
305
+ const relayStream = await gatewaySocketService.openLiveRelayStream({
306
+ clientUuid: resolved.relayTarget.clientUuid,
307
+ localSessionId: resolved.relayTarget.localSessionId,
308
+ onEvent: (event) => {
309
+ sendJson({
310
+ type: event.event,
311
+ ...event.payload,
312
+ });
313
+ },
314
+ });
315
+ relayStreamClose = relayStream.close;
316
+ sendJson({ type: "ready", mode: "stream" });
317
+ return;
318
+ }
319
+ if (!resolved.session?.terminalTarget ||
320
+ !(0, client_1.isStreamableTerminalTarget)(resolved.session.terminalTarget)) {
321
+ socket.close(1011, "Local live stream is not supported for this terminal");
322
+ return;
323
+ }
324
+ const terminalSize = await (0, terminal_1.getTerminalWindowSize)(runtime.config.terminal, resolved.session.terminalTarget);
325
+ const content = await (0, terminal_1.captureVisibleTerminal)(runtime.config.terminal, resolved.session.terminalTarget, runtime.config.terminal.captureLines, runtime.config.webapp.visibleScreens);
326
+ const ansi = await (0, terminal_1.captureVisibleTerminalAnsi)(runtime.config.terminal, resolved.session.terminalTarget, runtime.config.terminal.captureLines, runtime.config.webapp.visibleScreens);
327
+ sendJson({
328
+ type: "snapshot",
329
+ session_id: resolved.session.sessionId,
330
+ session_label: resolved.session.label ?? null,
331
+ captured_at: new Date().toISOString(),
332
+ content,
333
+ ansi,
334
+ ...(terminalSize ? terminalSize : {}),
335
+ });
336
+ localUnsubscribe = (0, client_1.subscribeForegroundTerminal)(resolved.session.terminalTarget, {
337
+ onData: (data) => {
338
+ sendJson({
339
+ type: "data",
340
+ data,
341
+ });
342
+ },
343
+ onExit: (info) => {
344
+ sendJson({
345
+ type: "exit",
346
+ exitCode: typeof info.exitCode === "number" ? info.exitCode : null,
347
+ signal: typeof info.signal === "number" ? info.signal : null,
348
+ });
349
+ },
350
+ });
351
+ sendJson({ type: "ready", mode: "stream" });
352
+ })().catch((error) => {
353
+ runtime.logger.warn("Telegram WebApp live WS bootstrap failed", {
354
+ error: error instanceof Error ? (error.stack ?? error.message) : String(error),
355
+ });
356
+ socket.close(1011, "Live stream bootstrap failed");
357
+ });
358
+ });
136
359
  const handleRequest = async (req, res, pathname) => {
137
360
  const method = req.method ?? "GET";
138
361
  const normalizedPathname = normalizePrefixedPathname(pathname);
@@ -146,6 +369,49 @@ function createMcpHttpHandler(runtime, input) {
146
369
  });
147
370
  return;
148
371
  }
372
+ if (runtime.config.telegram.webhook.enabled &&
373
+ requestUrl.pathname === telegramWebhookPath) {
374
+ if (method !== "POST") {
375
+ writeText(res, 405, "Method not allowed");
376
+ return;
377
+ }
378
+ const expectedSecret = runtime.config.telegram.webhook.secret?.trim();
379
+ const receivedSecret = readWebhookSecretHeader(req)?.trim();
380
+ if (!expectedSecret || receivedSecret !== expectedSecret) {
381
+ writeText(res, 401, "Unauthorized");
382
+ return;
383
+ }
384
+ const body = await readJsonBody(req);
385
+ if (!body || typeof body !== "object") {
386
+ writeText(res, 400, "Telegram update body is required");
387
+ return;
388
+ }
389
+ if (runtime.config.telegram.webhook.trace) {
390
+ runtime.logger.warn("Telegram webhook update received", {
391
+ path: requestUrl.pathname,
392
+ body,
393
+ });
394
+ }
395
+ else {
396
+ runtime.logger.info("Telegram webhook update received", {
397
+ path: requestUrl.pathname,
398
+ method,
399
+ });
400
+ }
401
+ try {
402
+ await runtime.telegramTransport.handleWebhookUpdate(body);
403
+ writeText(res, 200, "OK");
404
+ }
405
+ catch (error) {
406
+ runtime.logger.error("Telegram webhook update handling failed", {
407
+ error: error instanceof Error
408
+ ? (error.stack ?? error.message)
409
+ : String(error),
410
+ });
411
+ writeText(res, 500, error instanceof Error ? error.message : "Webhook handling failed");
412
+ }
413
+ return;
414
+ }
149
415
  if (await runtime.gatewayHttpService.handleRequest(req, res, requestUrl.pathname)) {
150
416
  return;
151
417
  }
@@ -161,6 +427,7 @@ function createMcpHttpHandler(runtime, input) {
161
427
  runtime.config.webapp.launchMode;
162
428
  writeHtml(res, 200, (0, assets_1.renderWebAppHtml)({
163
429
  basePath: publicWebAppBasePath,
430
+ liveWsPath: publicLiveWsPath,
164
431
  launchMode,
165
432
  }));
166
433
  return;
@@ -215,6 +482,7 @@ function createMcpHttpHandler(runtime, input) {
215
482
  if (relayTarget) {
216
483
  try {
217
484
  let trustedTelegramUserId = null;
485
+ let launchRecord = null;
218
486
  if (relayTarget.sourceClientUuid &&
219
487
  relayTarget.sourceClientUuid !== relayTarget.clientUuid) {
220
488
  const validation = await runtime.gatewayHttpService.requestLiveRelayBootstrapValidation({
@@ -226,9 +494,9 @@ function createMcpHttpHandler(runtime, input) {
226
494
  }
227
495
  else {
228
496
  try {
229
- const validated = (0, auth_1.validateTelegramWebAppInitData)(initDataRaw, initDataUnsafe, runtime.config.telegram.botToken, runtime.config.webapp.initDataTtlSeconds);
497
+ const validated = (0, auth_1.validateTelegramWebAppInitData)(initDataRaw, initDataUnsafe, requireTelegramBotToken(runtime, "validate local Telegram WebApp bootstrap"), runtime.config.webapp.initDataTtlSeconds);
230
498
  trustedTelegramUserId = validated.user.id;
231
- runtime.webAppLaunchRegistry.deleteByUserId(validated.user.id);
499
+ launchRecord = runtime.webAppLaunchRegistry.getByUserId(validated.user.id);
232
500
  }
233
501
  catch {
234
502
  trustedTelegramUserId = null;
@@ -240,22 +508,51 @@ function createMcpHttpHandler(runtime, input) {
240
508
  ...(trustedTelegramUserId !== null
241
509
  ? { telegramUserId: trustedTelegramUserId }
242
510
  : {}),
243
- ...(relayTarget.sourceClientUuid &&
244
- relayTarget.sourceClientUuid !== relayTarget.clientUuid
245
- ? { allowForeignBinding: true }
246
- : {}),
511
+ // Relay sessions are already gateway-bound to a Telegram principal.
512
+ // Do not rely on the per-user launch registry here because it can be
513
+ // overwritten by other menu interactions before the WebApp opens.
514
+ allowForeignBinding: true,
247
515
  initDataRaw,
248
516
  initDataUnsafe,
249
517
  });
518
+ runtime.logger.info("Telegram WebApp relay bootstrap forwarded", {
519
+ sessionId,
520
+ clientUuid: relayTarget.clientUuid,
521
+ localSessionId: relayTarget.localSessionId,
522
+ trustedTelegramUserId,
523
+ hasLaunchRecord: launchRecord !== null,
524
+ launchRecordAllowForeignBinding: launchRecord?.allowForeignBinding === true,
525
+ });
250
526
  const record = webAppSessions.create(sessionId, relayBootstrap.telegram_user_id, runtime.config.webapp.sessionTtlSeconds);
251
527
  writeJson(res, 200, {
252
528
  token: record.token,
253
529
  session_id: relayBootstrap.session_id,
254
530
  session_label: relayBootstrap.session_label,
255
- tmux_target: relayBootstrap.tmux_target,
531
+ terminal_target: relayBootstrap.terminal_target,
256
532
  poll_interval_ms: relayBootstrap.poll_interval_ms,
257
533
  expires_at: new Date(record.expiresAtMs).toISOString(),
258
534
  });
535
+ if (trustedTelegramUserId !== null) {
536
+ runtime.webAppLaunchRegistry.deleteByUserId(trustedTelegramUserId);
537
+ }
538
+ if (launchRecord?.telegramChatId !== undefined &&
539
+ launchRecord?.telegramMessageId !== undefined) {
540
+ void runtime.telegramTransport
541
+ .deleteMessage(launchRecord.telegramChatId, launchRecord.telegramMessageId)
542
+ .catch((deleteError) => {
543
+ runtime.logger.warn("Telegram WebApp relay launcher message deletion failed", {
544
+ sessionId,
545
+ clientUuid: relayTarget.clientUuid,
546
+ localSessionId: relayTarget.localSessionId,
547
+ telegramUserId: trustedTelegramUserId,
548
+ telegramChatId: launchRecord.telegramChatId,
549
+ telegramMessageId: launchRecord.telegramMessageId,
550
+ error: deleteError instanceof Error
551
+ ? (deleteError.stack ?? deleteError.message)
552
+ : String(deleteError),
553
+ });
554
+ });
555
+ }
259
556
  }
260
557
  catch (error) {
261
558
  runtime.logger.warn("Telegram WebApp relay bootstrap rejected", {
@@ -271,7 +568,7 @@ function createMcpHttpHandler(runtime, input) {
271
568
  return;
272
569
  }
273
570
  try {
274
- const validated = (0, auth_1.validateTelegramWebAppInitData)(initDataRaw, initDataUnsafe, runtime.config.telegram.botToken, runtime.config.webapp.initDataTtlSeconds);
571
+ const validated = (0, auth_1.validateTelegramWebAppInitData)(initDataRaw, initDataUnsafe, requireTelegramBotToken(runtime, "validate Telegram WebApp bootstrap"), runtime.config.webapp.initDataTtlSeconds);
275
572
  runtime.logger.info("Telegram WebApp initData validation debug", {
276
573
  sessionId: sessionId || null,
277
574
  telegramUserId: validated.user.id,
@@ -310,14 +607,14 @@ function createMcpHttpHandler(runtime, input) {
310
607
  runtime.logger.info("Telegram WebApp session bootstrapped", {
311
608
  sessionId,
312
609
  telegramUserId: validated.user.id,
313
- hasTmuxTarget: Boolean(session?.tmuxTarget),
610
+ hasTerminalTarget: Boolean(session?.terminalTarget),
314
611
  });
315
612
  runtime.webAppLaunchRegistry.deleteByUserId(validated.user.id);
316
613
  writeJson(res, 200, {
317
614
  token: record.token,
318
615
  session_id: sessionId,
319
616
  session_label: session?.label ?? null,
320
- tmux_target: Boolean(session?.tmuxTarget),
617
+ terminal_target: Boolean(session?.terminalTarget),
321
618
  poll_interval_ms: runtime.config.webapp.pollIntervalMs,
322
619
  expires_at: new Date(record.expiresAtMs).toISOString(),
323
620
  });
@@ -383,7 +680,7 @@ function createMcpHttpHandler(runtime, input) {
383
680
  ? (error.stack ?? error.message)
384
681
  : String(error),
385
682
  });
386
- writeText(res, isRelayTmuxUnavailableMessage(error) ? 503 : 500, error instanceof Error ? error.message : "Failed to capture relay tmux pane");
683
+ writeText(res, isRelayTerminalUnavailableMessage(error) ? 503 : 500, error instanceof Error ? error.message : "Failed to capture relay terminal buffer");
387
684
  }
388
685
  return;
389
686
  }
@@ -394,17 +691,21 @@ function createMcpHttpHandler(runtime, input) {
394
691
  return;
395
692
  }
396
693
  const session = await runtime.sessionStore.getSession(webAppSession.sessionId);
397
- if (!session?.tmuxTarget) {
398
- writeText(res, 409, "tmux target is not configured for this session");
694
+ if (!session?.terminalTarget) {
695
+ writeText(res, 409, "terminal target is not configured for this session");
399
696
  return;
400
697
  }
401
698
  try {
402
- const content = await (0, tmux_1.captureVisibleTmuxPane)(runtime.config.tmux, session.tmuxTarget, runtime.config.tmux.captureLines, runtime.config.webapp.visibleScreens);
699
+ const terminalSize = await (0, terminal_1.getTerminalWindowSize)(runtime.config.terminal, session.terminalTarget);
700
+ const content = await (0, terminal_1.captureVisibleTerminal)(runtime.config.terminal, session.terminalTarget, runtime.config.terminal.captureLines, runtime.config.webapp.visibleScreens);
701
+ const ansi = await (0, terminal_1.captureVisibleTerminalAnsi)(runtime.config.terminal, session.terminalTarget, runtime.config.terminal.captureLines, runtime.config.webapp.visibleScreens);
403
702
  writeJson(res, 200, {
404
703
  session_id: session.sessionId,
405
704
  session_label: session.label ?? null,
406
705
  captured_at: new Date().toISOString(),
407
706
  content,
707
+ ansi,
708
+ ...(terminalSize ? terminalSize : {}),
408
709
  });
409
710
  }
410
711
  catch (error) {
@@ -414,7 +715,7 @@ function createMcpHttpHandler(runtime, input) {
414
715
  ? (error.stack ?? error.message)
415
716
  : String(error),
416
717
  });
417
- writeText(res, (0, tmux_1.isTmuxUnavailableError)(error) ? 503 : 500, formatTmuxHttpError(error, "Failed to capture visible tmux pane"));
718
+ writeText(res, (0, terminal_1.isTerminalUnavailableError)(error) ? 503 : 500, formatTerminalHttpError(error, "Failed to capture visible terminal buffer"));
418
719
  }
419
720
  return;
420
721
  }
@@ -478,7 +779,7 @@ function createMcpHttpHandler(runtime, input) {
478
779
  ? (error.stack ?? error.message)
479
780
  : String(error),
480
781
  });
481
- writeText(res, isRelayTmuxUnavailableMessage(error) ? 503 : 500, error instanceof Error ? error.message : "Failed to send relay tmux action");
782
+ writeText(res, isRelayTerminalUnavailableMessage(error) ? 503 : 500, error instanceof Error ? error.message : "Failed to send relay terminal action");
482
783
  }
483
784
  return;
484
785
  }
@@ -489,19 +790,19 @@ function createMcpHttpHandler(runtime, input) {
489
790
  return;
490
791
  }
491
792
  const session = await runtime.sessionStore.getSession(webAppSession.sessionId);
492
- if (!session?.tmuxTarget) {
493
- writeText(res, 409, "tmux target is not configured for this session");
793
+ if (!session?.terminalTarget) {
794
+ writeText(res, 409, "terminal target is not configured for this session");
494
795
  return;
495
796
  }
496
797
  try {
497
798
  if (action === "text") {
498
- await (0, tmux_1.sendTmuxLiteralText)(runtime.config.tmux, session.tmuxTarget, text);
799
+ await (0, terminal_1.sendTerminalLiteralText)(runtime.config.terminal, session.terminalTarget, text);
499
800
  }
500
801
  else {
501
- await (0, tmux_1.sendAllowedTmuxAction)(runtime.config.tmux, session.tmuxTarget, action);
802
+ await (0, terminal_1.sendAllowedTerminalAction)(runtime.config.terminal, session.terminalTarget, action);
502
803
  }
503
804
  webAppSessions.touchAction(webAppSession.token, nowMs);
504
- runtime.logger.info("Telegram WebApp action sent to tmux", {
805
+ runtime.logger.info("Telegram WebApp action sent to terminal", {
505
806
  sessionId: webAppSession.sessionId,
506
807
  telegramUserId: webAppSession.telegramUserId,
507
808
  action,
@@ -520,7 +821,7 @@ function createMcpHttpHandler(runtime, input) {
520
821
  ? (error.stack ?? error.message)
521
822
  : String(error),
522
823
  });
523
- writeText(res, (0, tmux_1.isTmuxUnavailableError)(error) ? 503 : 500, formatTmuxHttpError(error, "Failed to send tmux action"));
824
+ writeText(res, (0, terminal_1.isTerminalUnavailableError)(error) ? 503 : 500, formatTerminalHttpError(error, "Failed to send terminal action"));
524
825
  }
525
826
  return;
526
827
  }
@@ -548,12 +849,10 @@ function createMcpHttpHandler(runtime, input) {
548
849
  .sort((left, right) => left.sessionId.localeCompare(right.sessionId))
549
850
  .map(async (session) => {
550
851
  const binding = await runtime.bindingStore.getBinding(session.sessionId);
551
- const inboxCount = await runtime.inboxStore.countInboxMessages(session.sessionId);
552
852
  return {
553
853
  session_id: session.sessionId,
554
854
  session_label: session.label ?? null,
555
855
  updated_at: session.updatedAt,
556
- inbox_count: inboxCount,
557
856
  binding: binding
558
857
  ? {
559
858
  telegram_chat_id: binding.telegramChatId,
@@ -561,18 +860,9 @@ function createMcpHttpHandler(runtime, input) {
561
860
  linked_at: binding.linkedAt,
562
861
  }
563
862
  : null,
564
- tmux: {
565
- tmux_session_name: session.tmuxSessionName ?? null,
566
- tmux_window_name: session.tmuxWindowName ?? null,
567
- tmux_window_index: typeof session.tmuxWindowIndex === "number"
568
- ? session.tmuxWindowIndex
569
- : null,
570
- tmux_pane_id: session.tmuxPaneId ?? null,
571
- tmux_pane_index: typeof session.tmuxPaneIndex === "number"
572
- ? session.tmuxPaneIndex
573
- : null,
574
- tmux_target: session.tmuxTarget ?? null,
575
- last_nudge_at: session.lastTmuxNudgeAt ?? null,
863
+ terminal: {
864
+ terminal_target: session.terminalTarget ?? null,
865
+ last_nudge_at: session.lastTerminalNudgeAt ?? null,
576
866
  },
577
867
  };
578
868
  }));
@@ -601,13 +891,33 @@ function createMcpHttpHandler(runtime, input) {
601
891
  return;
602
892
  }
603
893
  const result = await runtime.maintenanceStore.pruneAll();
894
+ let gatewayDeleted = null;
895
+ if (runtime.config.distributed.mode === "gateway" ||
896
+ runtime.config.distributed.mode === "both") {
897
+ try {
898
+ const gatewayResult = await runtime.callBroker("telegramMcp.gateway.pruneGatewayState", {}, {
899
+ meta: { internal_call: true },
900
+ });
901
+ gatewayDeleted =
902
+ gatewayResult?.deleted && typeof gatewayResult.deleted === "object"
903
+ ? gatewayResult.deleted
904
+ : null;
905
+ }
906
+ catch (error) {
907
+ runtime.logger.warn("Gateway DB prune failed through HTTP endpoint", {
908
+ error: error instanceof Error ? error.message : String(error),
909
+ });
910
+ }
911
+ }
604
912
  runtime.logger.warn("MCP service state pruned through HTTP endpoint", {
605
913
  deletedKeys: result.deletedKeys,
914
+ gatewayDeleted,
606
915
  remoteAddress: req.socket.remoteAddress,
607
916
  });
608
917
  writeJson(res, 200, {
609
918
  ok: true,
610
919
  deleted_keys: result.deletedKeys,
920
+ ...(gatewayDeleted ? { gateway_deleted: gatewayDeleted } : {}),
611
921
  });
612
922
  return;
613
923
  }
@@ -781,6 +1091,17 @@ function createMcpHttpHandler(runtime, input) {
781
1091
  }
782
1092
  };
783
1093
  let shuttingDown = false;
1094
+ const handleUpgrade = async (req, socket, head, pathname) => {
1095
+ const normalizedPathname = normalizePrefixedPathname(pathname);
1096
+ if (normalizedPathname !== `${webAppBasePath}/api/live/ws` &&
1097
+ normalizedPathname !== "/gateway/live/ws") {
1098
+ return false;
1099
+ }
1100
+ liveWsServer.handleUpgrade(req, socket, head, (clientSocket) => {
1101
+ liveWsServer.emit("connection", clientSocket, req);
1102
+ });
1103
+ return true;
1104
+ };
784
1105
  const shutdown = async () => {
785
1106
  if (shuttingDown) {
786
1107
  return;
@@ -789,9 +1110,21 @@ function createMcpHttpHandler(runtime, input) {
789
1110
  runtime.logger.info("MCP HTTP handler shutdown requested");
790
1111
  await Promise.all(Array.from(transports.values()).map((entry) => closeSessionEntry(entry)));
791
1112
  transports.clear();
1113
+ await new Promise((resolve, reject) => {
1114
+ liveWsServer.close((error) => {
1115
+ if (error) {
1116
+ reject(error);
1117
+ return;
1118
+ }
1119
+ resolve();
1120
+ });
1121
+ });
792
1122
  };
793
1123
  return {
794
1124
  handleRequest,
1125
+ handleUpgrade,
795
1126
  close: shutdown,
796
1127
  };
797
1128
  }
1129
+ const wsLib = ws_1.default;
1130
+ const WebSocketServer = wsLib.WebSocketServer;