@deadragdoll/tellymcp 0.0.13 → 0.0.14

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 (64) hide show
  1. package/.env.example.client +5 -1
  2. package/.env.example.gateway +16 -1
  3. package/CHANGELOG.md +30 -0
  4. package/README-ru.md +14 -0
  5. package/README.md +14 -0
  6. package/TOOLS.md +182 -10
  7. package/config/templates/env.both.template +15 -2
  8. package/config/templates/env.client.template +5 -2
  9. package/config/templates/env.gateway.template +15 -2
  10. package/dist/cli.js +28 -1
  11. package/dist/services/features/telegram-mcp/browser.service.js +18 -0
  12. package/dist/services/features/telegram-mcp/file-content.service.js +94 -0
  13. package/dist/services/features/telegram-mcp/gateway-delivery.service.js +5 -1
  14. package/dist/services/features/telegram-mcp/gateway-socket.service.js +43 -11
  15. package/dist/services/features/telegram-mcp/mcp-http.service.js +1 -0
  16. package/dist/services/features/telegram-mcp/mcp-server.service.js +18 -0
  17. package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +9 -1
  18. package/dist/services/features/telegram-mcp/src/app/config/env.js +119 -2
  19. package/dist/services/features/telegram-mcp/src/app/http.js +139 -99
  20. package/dist/services/features/telegram-mcp/src/app/oauthFacade.js +642 -0
  21. package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +151 -170
  22. package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +96 -99
  23. package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +62 -19
  24. package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachActiveTabTool.js +28 -0
  25. package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachTabTool.js +28 -0
  26. package/dist/services/features/telegram-mcp/src/features/browser/model/browserDetachTabTool.js +28 -0
  27. package/dist/services/features/telegram-mcp/src/features/browser/model/browserListAttachedInstancesTool.js +1 -1
  28. package/dist/services/features/telegram-mcp/src/features/browser/model/browserListTabsTool.js +1 -1
  29. package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +410 -27
  30. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/browserRecordingBundle.js +37 -3
  31. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachRegistry.js +7 -6
  32. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachServer.js +214 -37
  33. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/types.js +186 -0
  34. package/dist/services/features/telegram-mcp/src/features/collaboration/model/collaborationService.js +2 -0
  35. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +6 -3
  36. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +4 -1
  37. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +33 -35
  38. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileListTool.js +33 -0
  39. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileService.js +327 -0
  40. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileTool.js +81 -0
  41. package/dist/services/features/telegram-mcp/src/features/file-content/model/temporaryFileLinkStore.js +307 -0
  42. package/dist/services/features/telegram-mcp/src/features/file-content/model/workspaceFilePolicy.js +115 -0
  43. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +5 -1
  44. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +3 -0
  45. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +6 -3
  46. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +29 -6
  47. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +100 -2
  48. package/dist/services/features/telegram-mcp/src/shared/lib/bodyLimits.js +63 -0
  49. package/dist/services/features/telegram-mcp/src/shared/lib/gatewayAuth.js +13 -0
  50. package/dist/services/features/telegram-mcp/src/shared/lib/time/localTimestamp.js +21 -0
  51. package/docs/CHAT_CONNECTOR.md +134 -0
  52. package/docs/STANDALONE-ru.md +13 -0
  53. package/docs/STANDALONE.md +13 -0
  54. package/package.json +5 -3
  55. package/packages/chrome-attach-extension/dist/background.js +572 -163
  56. package/packages/chrome-attach-extension/dist/manifest.json +2 -1
  57. package/packages/chrome-attach-extension/dist/options.js +15 -2
  58. package/packages/chrome-attach-extension/dist/recorder-content.js +14 -1
  59. package/packages/chrome-attach-extension/dist/recorder-page.js +34 -18
  60. package/packages/firefox-attach-extension/dist/background.js +413 -33
  61. package/packages/firefox-attach-extension/dist/manifest.json +0 -12
  62. package/packages/firefox-attach-extension/dist/options.js +14 -1
  63. package/packages/firefox-attach-extension/dist/recorder-content.js +14 -1
  64. package/packages/firefox-attach-extension/dist/recorder-page.js +34 -18
@@ -33,6 +33,24 @@ const TelegramMcpBrowserService = {
33
33
  return this.getBrowserService().listTabs(ctx.params);
34
34
  },
35
35
  },
36
+ attachActiveTabRemote: {
37
+ params: { $$strict: false },
38
+ async handler(ctx) {
39
+ return this.getBrowserService().attachActiveTab(ctx.params);
40
+ },
41
+ },
42
+ attachTabRemote: {
43
+ params: { $$strict: false },
44
+ async handler(ctx) {
45
+ return this.getBrowserService().attachTab(ctx.params);
46
+ },
47
+ },
48
+ detachTabRemote: {
49
+ params: { $$strict: false },
50
+ async handler(ctx) {
51
+ return this.getBrowserService().detachTab(ctx.params);
52
+ },
53
+ },
36
54
  startRecordingRemote: {
37
55
  params: { $$strict: false },
38
56
  async handler(ctx) {
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TELEGRAM_MCP_FILE_CONTENT_SERVICE_NAME = void 0;
4
+ const runtime_service_1 = require("./runtime.service");
5
+ const remoteConsoleActionClient_1 = require("./src/features/distributed-gateway/model/remoteConsoleActionClient");
6
+ const getFileService_1 = require("./src/features/file-content/model/getFileService");
7
+ exports.TELEGRAM_MCP_FILE_CONTENT_SERVICE_NAME = "telegramMcp.fileContent";
8
+ const TelegramMcpFileContentService = {
9
+ name: exports.TELEGRAM_MCP_FILE_CONTENT_SERVICE_NAME,
10
+ dependencies: [runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME],
11
+ actions: {
12
+ listFilesRemote: {
13
+ params: {
14
+ $$strict: false,
15
+ session_id: { type: "string", optional: true, trim: true, min: 1 },
16
+ source: {
17
+ type: "enum",
18
+ optional: true,
19
+ values: ["telegram-upload", "browser-screenshot", "partner-artifact"],
20
+ },
21
+ limit: {
22
+ type: "number",
23
+ optional: true,
24
+ integer: true,
25
+ positive: true,
26
+ max: 200,
27
+ },
28
+ },
29
+ async handler(ctx) {
30
+ return this.getGetFileService().list(ctx.params);
31
+ },
32
+ },
33
+ getFileRemote: {
34
+ params: {
35
+ $$strict: false,
36
+ session_id: { type: "string", optional: true, trim: true, min: 1 },
37
+ file_path: { type: "string", optional: true, trim: true, min: 1 },
38
+ selector: {
39
+ type: "enum",
40
+ optional: true,
41
+ values: ["latest_screenshot"],
42
+ },
43
+ type: {
44
+ type: "enum",
45
+ optional: true,
46
+ values: ["text", "base64"],
47
+ },
48
+ },
49
+ async handler(ctx) {
50
+ return this.getGetFileService().get(ctx.params);
51
+ },
52
+ },
53
+ uploadFileRemote: {
54
+ params: {
55
+ $$strict: false,
56
+ session_id: { type: "string", optional: true, trim: true, min: 1 },
57
+ file_path: { type: "string", optional: true, trim: true, min: 1 },
58
+ selector: {
59
+ type: "enum",
60
+ optional: true,
61
+ values: ["latest_screenshot"],
62
+ },
63
+ type: { type: "enum", values: ["url"] },
64
+ upload_url: { type: "string", trim: true, min: 1 },
65
+ },
66
+ async handler(ctx) {
67
+ return this.getGetFileService().upload(ctx.params);
68
+ },
69
+ },
70
+ },
71
+ created() {
72
+ this.getFileService = null;
73
+ },
74
+ methods: {
75
+ getGetFileService() {
76
+ if (!this.getFileService) {
77
+ throw new Error("telegram_mcp file content service is not initialized yet");
78
+ }
79
+ return this.getFileService;
80
+ },
81
+ },
82
+ async started() {
83
+ await this.broker.waitForServices([runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME]);
84
+ const runtimeService = this.broker.getLocalService(runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME);
85
+ if (!runtimeService) {
86
+ throw new Error(`Local Moleculer service '${runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME}' is unavailable`);
87
+ }
88
+ const runtime = await runtimeService.waitUntilReady();
89
+ this.logger.info("Starting telegram_mcp file content service");
90
+ this.getFileService = new getFileService_1.GetFileService(runtime.config, runtime.sessionStore, runtime.stateStore, runtime.stateStore, runtime.logger, runtime.projectIdentityResolver, new remoteConsoleActionClient_1.RemoteConsoleActionClient((actionName, params) => this.broker.call(actionName, params, { meta: { internal_call: true } })), runtime.temporaryFileLinkStore);
91
+ this.logger.info("telegram_mcp file content service is ready");
92
+ },
93
+ };
94
+ exports.default = TelegramMcpFileContentService;
@@ -9,6 +9,7 @@ const runtime_service_1 = require("./runtime.service");
9
9
  const xchangeRecordHints_1 = require("./src/shared/lib/xchangeRecordHints");
10
10
  const client_1 = require("./src/shared/integrations/terminal/client");
11
11
  const sqliteRecordStore_1 = require("./src/shared/integrations/xchange/sqliteRecordStore");
12
+ const bodyLimits_1 = require("./src/shared/lib/bodyLimits");
12
13
  exports.TELEGRAM_MCP_GATEWAY_DELIVERY_SERVICE_NAME = "telegramMcp.gatewayDelivery";
13
14
  function renderYamlArray(values) {
14
15
  if (values.length === 0) {
@@ -319,7 +320,10 @@ const TelegramMcpGatewayDeliveryService = {
319
320
  `shares/files/${delivery.share_id}/${artifact.original_name}`;
320
321
  let localArtifactPath;
321
322
  if (artifact.content_base64) {
322
- localArtifactPath = await (0, client_1.writeXchangeRelativeFile)(runtime.config.terminal, workspaceDir, runtime.config.exchange.dir, relativePath, Buffer.from(artifact.content_base64, "base64"));
323
+ (0, bodyLimits_1.assertStringBodySize)(artifact.content_base64);
324
+ const decodedContent = Buffer.from(artifact.content_base64, "base64");
325
+ (0, bodyLimits_1.assertBodySize)(decodedContent.byteLength);
326
+ localArtifactPath = await (0, client_1.writeXchangeRelativeFile)(runtime.config.terminal, workspaceDir, runtime.config.exchange.dir, relativePath, decodedContent);
323
327
  }
324
328
  else {
325
329
  localArtifactPath = await runtime.objectStore.ensureLocalFile({
@@ -11,6 +11,8 @@ const auth_1 = require("./src/app/webapp/auth");
11
11
  const terminal_1 = require("./src/app/webapp/terminal");
12
12
  const client_1 = require("./src/shared/integrations/terminal/client");
13
13
  const gateway_loopback_1 = require("./gateway-loopback");
14
+ const gatewayAuth_1 = require("./src/shared/lib/gatewayAuth");
15
+ const bodyLimits_1 = require("./src/shared/lib/bodyLimits");
14
16
  const versionHandshake_1 = require("./src/shared/lib/version/versionHandshake");
15
17
  const wsLib = require("ws");
16
18
  const WebSocketServer = wsLib.WebSocketServer;
@@ -226,6 +228,7 @@ const TelegramMcpGatewaySocketService = {
226
228
  "bootstrap_validate",
227
229
  "view",
228
230
  "action",
231
+ "resize",
229
232
  "stream_subscribe",
230
233
  "stream_unsubscribe",
231
234
  ],
@@ -941,7 +944,6 @@ const TelegramMcpGatewaySocketService = {
941
944
  session_id: sessionId,
942
945
  session_label: session?.label ?? null,
943
946
  terminal_target: Boolean(session?.terminalTarget),
944
- poll_interval_ms: runtime.config.webapp.pollIntervalMs,
945
947
  telegram_user_id: telegramUserId,
946
948
  },
947
949
  };
@@ -1002,6 +1004,25 @@ const TelegramMcpGatewaySocketService = {
1002
1004
  },
1003
1005
  };
1004
1006
  }
1007
+ if (request.request_type === "resize") {
1008
+ const cols = typeof request.payload?.cols === "number" ? request.payload.cols : NaN;
1009
+ const rows = typeof request.payload?.rows === "number" ? request.payload.rows : NaN;
1010
+ if (!Number.isFinite(cols) || !Number.isFinite(rows)) {
1011
+ throw new Error("Terminal cols and rows are required");
1012
+ }
1013
+ const sessionId = request.local_session_id.trim();
1014
+ const session = await runtime.sessionStore.getSession(sessionId);
1015
+ if (!session?.terminalTarget || !(0, client_1.isStreamableTerminalTarget)(session.terminalTarget)) {
1016
+ throw new Error("Terminal target does not support resize");
1017
+ }
1018
+ (0, client_1.resizeForegroundTerminal)(session.terminalTarget, Math.max(20, Math.min(400, Math.round(cols))), Math.max(5, Math.min(200, Math.round(rows))));
1019
+ return {
1020
+ type: "live_response",
1021
+ request_id: request.request_id,
1022
+ ok: true,
1023
+ result: { ok: true },
1024
+ };
1025
+ }
1005
1026
  if (request.request_type === "stream_subscribe") {
1006
1027
  const streamId = typeof request.payload?.stream_id === "string"
1007
1028
  ? request.payload.stream_id.trim()
@@ -1582,7 +1603,9 @@ const TelegramMcpGatewaySocketService = {
1582
1603
  requestId,
1583
1604
  actionName,
1584
1605
  error: formattedError,
1585
- payload: parsed.payload ?? {},
1606
+ payload: actionName === "telegramMcp.fileContent.uploadFileRemote"
1607
+ ? { ...(parsed.payload ?? {}), upload_url: "[redacted]" }
1608
+ : (parsed.payload ?? {}),
1586
1609
  });
1587
1610
  this.wsClient?.send(JSON.stringify({
1588
1611
  type: "action_response",
@@ -1956,16 +1979,11 @@ const TelegramMcpGatewaySocketService = {
1956
1979
  }
1957
1980
  const httpServer = await this.waitForHttpServer?.();
1958
1981
  const wsPath = runtime.config.distributed.gatewayWsPath.replace(/\/+$/u, "") || "/";
1959
- const wsServer = new WebSocketServer({ noServer: true });
1982
+ const wsServer = new WebSocketServer({
1983
+ noServer: true,
1984
+ maxPayload: bodyLimits_1.MAX_BODY_SIZE_BYTES,
1985
+ });
1960
1986
  wsServer.on("connection", (socket, req) => {
1961
- if (runtime.config.distributed.gatewayAuthToken) {
1962
- const authorization = req.headers?.authorization;
1963
- if (authorization !==
1964
- `Bearer ${runtime.config.distributed.gatewayAuthToken}`) {
1965
- socket.close(1008, "Unauthorized");
1966
- return;
1967
- }
1968
- }
1969
1987
  runtime.logger.warn("Gateway WS client connected", {
1970
1988
  remoteAddress: req.socket.remoteAddress,
1971
1989
  path: req.url,
@@ -1977,6 +1995,11 @@ const TelegramMcpGatewaySocketService = {
1977
1995
  });
1978
1996
  });
1979
1997
  });
1998
+ socket.on("error", (error) => {
1999
+ runtime.logger.warn("Gateway WS server socket error", {
2000
+ error: error instanceof Error ? error.message : String(error),
2001
+ });
2002
+ });
1980
2003
  socket.on("close", () => {
1981
2004
  const hello = this.connectedClients?.get(socket);
1982
2005
  if (hello?.connection_id) {
@@ -2026,6 +2049,14 @@ const TelegramMcpGatewaySocketService = {
2026
2049
  if (requestPath !== wsPath) {
2027
2050
  return;
2028
2051
  }
2052
+ if (!(0, gatewayAuth_1.isGatewayAuthorizationValid)(req.headers.authorization, runtime.config.distributed.gatewayAuthToken)) {
2053
+ socket.write("HTTP/1.1 401 Unauthorized\r\n" +
2054
+ "Connection: close\r\n" +
2055
+ "Content-Length: 0\r\n" +
2056
+ "\r\n");
2057
+ socket.destroy();
2058
+ return;
2059
+ }
2029
2060
  wsServer.handleUpgrade(req, socket, head, (clientSocket) => {
2030
2061
  wsServer.emit("connection", clientSocket, req);
2031
2062
  });
@@ -2082,6 +2113,7 @@ const TelegramMcpGatewaySocketService = {
2082
2113
  const normalizedUrl = normalizeWebSocketUrl(wsUrl, runtime.config.distributed.gatewayWsPath);
2083
2114
  this.wsConnectionId = (0, node_crypto_1.randomUUID)();
2084
2115
  const socket = new wsLib.WebSocket(normalizedUrl, {
2116
+ maxPayload: bodyLimits_1.MAX_BODY_SIZE_BYTES,
2085
2117
  headers: runtime.config.distributed.gatewayAuthToken
2086
2118
  ? {
2087
2119
  authorization: `Bearer ${runtime.config.distributed.gatewayAuthToken}`,
@@ -69,6 +69,7 @@ const TelegramMcpHttpService = {
69
69
  path: runtime.config.mcp.httpPath,
70
70
  webappBasePath: runtime.config.webapp.basePath,
71
71
  bearerAuthEnabled: Boolean(runtime.config.mcp.bearerToken),
72
+ oauthConnectorEnabled: Boolean(runtime.config.oauth),
72
73
  });
73
74
  },
74
75
  async stopped() {
@@ -5,16 +5,20 @@ const server_1 = require("./src/app/providers/mcp/server");
5
5
  const approval_service_1 = require("./approval.service");
6
6
  const browser_service_1 = require("./browser.service");
7
7
  const collaboration_service_1 = require("./collaboration.service");
8
+ const file_content_service_1 = require("./file-content.service");
8
9
  const notify_service_1 = require("./notify.service");
9
10
  const session_context_service_1 = require("./session-context.service");
10
11
  const tools_sync_service_1 = require("./tools-sync.service");
11
12
  const xchange_service_1 = require("./xchange.service");
12
13
  const askUserTelegram_1 = require("./src/features/ask-user/model/askUserTelegram");
14
+ const browserAttachActiveTabTool_1 = require("./src/features/browser/model/browserAttachActiveTabTool");
15
+ const browserAttachTabTool_1 = require("./src/features/browser/model/browserAttachTabTool");
13
16
  const browserClickTool_1 = require("./src/features/browser/model/browserClickTool");
14
17
  const browserClearLogsTool_1 = require("./src/features/browser/model/browserClearLogsTool");
15
18
  const browserCloseTool_1 = require("./src/features/browser/model/browserCloseTool");
16
19
  const browserComputedStyleTool_1 = require("./src/features/browser/model/browserComputedStyleTool");
17
20
  const browserConsoleTool_1 = require("./src/features/browser/model/browserConsoleTool");
21
+ const browserDetachTabTool_1 = require("./src/features/browser/model/browserDetachTabTool");
18
22
  const browserDomTool_1 = require("./src/features/browser/model/browserDomTool");
19
23
  const browserErrorsTool_1 = require("./src/features/browser/model/browserErrorsTool");
20
24
  const browserFillTool_1 = require("./src/features/browser/model/browserFillTool");
@@ -34,6 +38,8 @@ const browserWaitForUrlTool_1 = require("./src/features/browser/model/browserWai
34
38
  const sendPartnerFileTool_1 = require("./src/features/collaboration/model/sendPartnerFileTool");
35
39
  const listGatewaySessionsTool_1 = require("./src/features/collaboration/model/listGatewaySessionsTool");
36
40
  const sendPartnerNoteTool_1 = require("./src/features/collaboration/model/sendPartnerNoteTool");
41
+ const getFileTool_1 = require("./src/features/file-content/model/getFileTool");
42
+ const getFileListTool_1 = require("./src/features/file-content/model/getFileListTool");
37
43
  const sendFileToTelegramTool_1 = require("./src/features/notify/model/sendFileToTelegramTool");
38
44
  const notifyTelegramTool_1 = require("./src/features/notify/model/notifyTelegramTool");
39
45
  const clearSessionContextTool_1 = require("./src/features/session-context/model/clearSessionContextTool");
@@ -53,6 +59,7 @@ const TelegramMcpMcpServerService = {
53
59
  approval_service_1.TELEGRAM_MCP_APPROVAL_SERVICE_NAME,
54
60
  browser_service_1.TELEGRAM_MCP_BROWSER_SERVICE_NAME,
55
61
  collaboration_service_1.TELEGRAM_MCP_COLLABORATION_SERVICE_NAME,
62
+ file_content_service_1.TELEGRAM_MCP_FILE_CONTENT_SERVICE_NAME,
56
63
  tools_sync_service_1.TELEGRAM_MCP_TOOLS_SYNC_SERVICE_NAME,
57
64
  xchange_service_1.TELEGRAM_MCP_XCHANGE_SERVICE_NAME,
58
65
  ],
@@ -63,6 +70,7 @@ const TelegramMcpMcpServerService = {
63
70
  const approvalService = this.broker.getLocalService(approval_service_1.TELEGRAM_MCP_APPROVAL_SERVICE_NAME);
64
71
  const browserService = this.broker.getLocalService(browser_service_1.TELEGRAM_MCP_BROWSER_SERVICE_NAME);
65
72
  const collaborationService = this.broker.getLocalService(collaboration_service_1.TELEGRAM_MCP_COLLABORATION_SERVICE_NAME);
73
+ const fileContentService = this.broker.getLocalService(file_content_service_1.TELEGRAM_MCP_FILE_CONTENT_SERVICE_NAME);
66
74
  const toolsSyncService = this.broker.getLocalService(tools_sync_service_1.TELEGRAM_MCP_TOOLS_SYNC_SERVICE_NAME);
67
75
  const xchangeService = this.broker.getLocalService(xchange_service_1.TELEGRAM_MCP_XCHANGE_SERVICE_NAME);
68
76
  if (!sessionContextService ||
@@ -70,6 +78,7 @@ const TelegramMcpMcpServerService = {
70
78
  !approvalService ||
71
79
  !browserService ||
72
80
  !collaborationService ||
81
+ !fileContentService ||
73
82
  !toolsSyncService ||
74
83
  !xchangeService) {
75
84
  throw new Error("telegram_mcp MCP server dependencies are not ready");
@@ -84,6 +93,9 @@ const TelegramMcpMcpServerService = {
84
93
  new askUserTelegram_1.AskUserTelegramTool(approvalService.getApprovalOrchestrator()),
85
94
  new browserListAttachedInstancesTool_1.BrowserListAttachedInstancesTool(browserService.getBrowserService()),
86
95
  new browserListTabsTool_1.BrowserListTabsTool(browserService.getBrowserService()),
96
+ new browserAttachActiveTabTool_1.BrowserAttachActiveTabTool(browserService.getBrowserService()),
97
+ new browserAttachTabTool_1.BrowserAttachTabTool(browserService.getBrowserService()),
98
+ new browserDetachTabTool_1.BrowserDetachTabTool(browserService.getBrowserService()),
87
99
  new browserRecordingStartTool_1.BrowserRecordingStartTool(browserService.getBrowserService()),
88
100
  new browserRecordingStopTool_1.BrowserRecordingStopTool(browserService.getBrowserService()),
89
101
  new browserRecordingStatusTool_1.BrowserRecordingStatusTool(browserService.getBrowserService()),
@@ -106,11 +118,16 @@ const TelegramMcpMcpServerService = {
106
118
  new sendPartnerNoteTool_1.SendPartnerNoteTool(collaborationService.getCollaborationService()),
107
119
  new listGatewaySessionsTool_1.ListGatewaySessionsTool(collaborationService.getGatewaySessionsService()),
108
120
  new sendPartnerFileTool_1.SendPartnerFileTool(collaborationService.getSendPartnerFileService()),
121
+ new getFileTool_1.GetFileTool(fileContentService.getGetFileService()),
122
+ new getFileListTool_1.GetFileListTool(fileContentService.getGetFileService()),
109
123
  new listXchangeRecordsTool_1.ListXchangeRecordsTool(xchangeService.getXchangeService()),
110
124
  new getXchangeRecordTool_1.GetXchangeRecordTool(xchangeService.getXchangeService()),
111
125
  new markXchangeRecordReadTool_1.MarkXchangeRecordReadTool(xchangeService.getXchangeService()),
112
126
  new refreshToolsMarkdownTool_1.RefreshToolsMarkdownTool(toolsSyncService.getRefreshToolsMarkdownService()),
113
127
  ];
128
+ this.logger.info("telegram_mcp MCP server instance created", {
129
+ toolCount: tools.length,
130
+ });
114
131
  return (0, server_1.createMcpServer)(tools);
115
132
  },
116
133
  },
@@ -121,6 +138,7 @@ const TelegramMcpMcpServerService = {
121
138
  approval_service_1.TELEGRAM_MCP_APPROVAL_SERVICE_NAME,
122
139
  browser_service_1.TELEGRAM_MCP_BROWSER_SERVICE_NAME,
123
140
  collaboration_service_1.TELEGRAM_MCP_COLLABORATION_SERVICE_NAME,
141
+ file_content_service_1.TELEGRAM_MCP_FILE_CONTENT_SERVICE_NAME,
124
142
  tools_sync_service_1.TELEGRAM_MCP_TOOLS_SYNC_SERVICE_NAME,
125
143
  xchange_service_1.TELEGRAM_MCP_XCHANGE_SERVICE_NAME,
126
144
  ]);
@@ -13,6 +13,7 @@ const minioExchangeStore_1 = require("../../shared/integrations/object-storage/m
13
13
  const gatewayHttpService_1 = require("../../features/distributed-gateway/model/gatewayHttpService");
14
14
  const gatewayClientAccess_1 = require("../../features/distributed-client/model/gatewayClientAccess");
15
15
  const firefoxAttachServer_1 = require("../../features/browser-attach/model/firefoxAttachServer");
16
+ const temporaryFileLinkStore_1 = require("../../features/file-content/model/temporaryFileLinkStore");
16
17
  const client_2 = require("../../shared/integrations/terminal/client");
17
18
  const ptyRegistry_1 = require("../../shared/integrations/terminal/ptyRegistry");
18
19
  async function createAppRuntime(input) {
@@ -34,6 +35,7 @@ async function createAppRuntime(input) {
34
35
  httpPort: config.mcp.httpPort,
35
36
  httpPath: config.mcp.httpPath,
36
37
  bearerAuthEnabled: Boolean(config.mcp.bearerToken),
38
+ oauthConnectorEnabled: Boolean(config.oauth),
37
39
  enableDebugRoutes: config.mcp.enableDebugRoutes,
38
40
  enablePruneRoute: config.mcp.enablePruneRoute,
39
41
  },
@@ -56,7 +58,6 @@ async function createAppRuntime(input) {
56
58
  initDataTtlSeconds: config.webapp.initDataTtlSeconds,
57
59
  sessionTtlSeconds: config.webapp.sessionTtlSeconds,
58
60
  launchMode: config.webapp.launchMode,
59
- pollIntervalMs: config.webapp.pollIntervalMs,
60
61
  actionCooldownMs: config.webapp.actionCooldownMs,
61
62
  },
62
63
  terminal: {
@@ -210,6 +211,11 @@ async function createAppRuntime(input) {
210
211
  await telegramTransport.sendStartupNotifications();
211
212
  logger.info("Startup Telegram notifications completed");
212
213
  const gatewayHttpService = new gatewayHttpService_1.GatewayHttpService(config, input.callBroker);
214
+ const temporaryFileLinkStore = config.distributed.mode !== "client" &&
215
+ config.distributed.gatewayPublicUrl?.trim()
216
+ ? new temporaryFileLinkStore_1.TemporaryFileLinkStore(config.distributed.gatewayPublicUrl, logger)
217
+ : null;
218
+ await temporaryFileLinkStore?.start();
213
219
  const firefoxAttachServer = new firefoxAttachServer_1.FirefoxAttachServer(config, logger, stateStore, stateStore);
214
220
  await firefoxAttachServer.start();
215
221
  return {
@@ -230,8 +236,10 @@ async function createAppRuntime(input) {
230
236
  webAppLaunchRegistry,
231
237
  gatewayHttpService,
232
238
  firefoxAttachServer,
239
+ temporaryFileLinkStore,
233
240
  shutdown: async () => {
234
241
  logger.info("Shutdown started");
242
+ await temporaryFileLinkStore?.stop();
235
243
  await firefoxAttachServer.stop();
236
244
  await telegramTransport.stop();
237
245
  (0, ptyRegistry_1.stopAllPtyTargets)();
@@ -93,6 +93,22 @@ const envSchema = z.object({
93
93
  MCP_HTTP_PORT: z.coerce.number().int().positive().default(8787),
94
94
  MCP_HTTP_PATH: z.string().min(1).default("/mcp"),
95
95
  MCP_HTTP_BEARER_TOKEN: optionalNonEmptyString,
96
+ TELLYMCP_PUBLIC_URL: optionalUrlString,
97
+ TELLYMCP_OAUTH_ISSUER: optionalUrlString,
98
+ TELLYMCP_OAUTH_AUDIENCE: optionalUrlString,
99
+ TELLYMCP_MAGIC_TOKEN: optionalNonEmptyString,
100
+ TELLYMCP_MAGIC_TOKEN_HASH: optionalNonEmptyString,
101
+ TELLYMCP_OAUTH_CLIENT_ID: optionalNonEmptyString,
102
+ TELLYMCP_OAUTH_CLIENT_SECRET: optionalNonEmptyString,
103
+ TELLYMCP_ALLOWED_REDIRECT_URIS: optionalNonEmptyString,
104
+ TELLYMCP_OAUTH_PRIVATE_KEY_PEM: optionalNonEmptyString,
105
+ TELLYMCP_AUTH_CODE_TTL_SECONDS: z.coerce
106
+ .number()
107
+ .int()
108
+ .positive()
109
+ .default(300),
110
+ TELLYMCP_OAUTH_SCOPES: z.string().min(1).default("tellymcp:read tellymcp:write"),
111
+ TELLYMCP_OAUTH_KEY_ID: z.string().min(1).default("tellymcp-oauth"),
96
112
  MCP_HTTP_ENABLE_DEBUG_ROUTES: z
97
113
  .string()
98
114
  .optional()
@@ -145,7 +161,6 @@ const envSchema = z.object({
145
161
  .enum(["default", "expand", "fullscreen"])
146
162
  .default("fullscreen"),
147
163
  WEBAPP_VISIBLE_SCREENS: z.coerce.number().int().positive().default(2),
148
- WEBAPP_POLL_INTERVAL_MS: z.coerce.number().int().positive().default(2000),
149
164
  WEBAPP_ACTION_COOLDOWN_MS: z.coerce.number().int().nonnegative().default(150),
150
165
  MCP_XCHANGE_DIR: z.string().min(1).default(".mcp-xchange"),
151
166
  TERMINAL_SHELL: z.string().min(1).default(process.env.SHELL || "bash"),
@@ -246,6 +261,16 @@ function loadConfig() {
246
261
  !parsed.TELEGRAM_BOT_TOKEN?.trim()) {
247
262
  throw new Error("TELEGRAM_BOT_TOKEN is required for gateway and both distributed modes.");
248
263
  }
264
+ const gatewayServerEnabled = parsed.DISTRIBUTED_MODE === "gateway" ||
265
+ parsed.DISTRIBUTED_MODE === "both";
266
+ const gatewayClientConfigured = parsed.DISTRIBUTED_MODE === "client" &&
267
+ Boolean(parsed.GATEWAY_PUBLIC_URL || parsed.GATEWAY_WS_URL);
268
+ if ((gatewayServerEnabled || gatewayClientConfigured) &&
269
+ !parsed.GATEWAY_AUTH_TOKEN?.trim()) {
270
+ throw new Error(gatewayServerEnabled
271
+ ? "GATEWAY_AUTH_TOKEN is required for gateway and both distributed modes."
272
+ : "GATEWAY_AUTH_TOKEN is required when a client connects to a gateway.");
273
+ }
249
274
  if (parsed.ADMIN_TOKEN?.trim() && !parsed.TELEGRAM_BOT_TOKEN?.trim()) {
250
275
  throw new Error("ADMIN_TOKEN requires TELEGRAM_BOT_TOKEN because admin mode runs through the gateway bot.");
251
276
  }
@@ -257,6 +282,69 @@ function loadConfig() {
257
282
  throw new Error("TELEGRAM_WEBHOOK_ENABLED=true requires TELEGRAM_WEBHOOK_SECRET.");
258
283
  }
259
284
  }
285
+ const oauthConfigured = Boolean(parsed.TELLYMCP_PUBLIC_URL ||
286
+ parsed.TELLYMCP_OAUTH_ISSUER ||
287
+ parsed.TELLYMCP_OAUTH_AUDIENCE ||
288
+ parsed.TELLYMCP_MAGIC_TOKEN ||
289
+ parsed.TELLYMCP_MAGIC_TOKEN_HASH ||
290
+ parsed.TELLYMCP_OAUTH_CLIENT_ID ||
291
+ parsed.TELLYMCP_OAUTH_CLIENT_SECRET ||
292
+ parsed.TELLYMCP_ALLOWED_REDIRECT_URIS ||
293
+ parsed.TELLYMCP_OAUTH_PRIVATE_KEY_PEM);
294
+ if (oauthConfigured && !parsed.TELLYMCP_PUBLIC_URL) {
295
+ throw new Error("TELLYMCP_PUBLIC_URL is required when OAuth connector settings are configured.");
296
+ }
297
+ if (oauthConfigured &&
298
+ !parsed.TELLYMCP_MAGIC_TOKEN &&
299
+ !parsed.TELLYMCP_MAGIC_TOKEN_HASH) {
300
+ throw new Error("TELLYMCP_MAGIC_TOKEN or TELLYMCP_MAGIC_TOKEN_HASH is required when OAuth is configured.");
301
+ }
302
+ if (parsed.TELLYMCP_MAGIC_TOKEN && parsed.TELLYMCP_MAGIC_TOKEN_HASH) {
303
+ throw new Error("Configure only one of TELLYMCP_MAGIC_TOKEN and TELLYMCP_MAGIC_TOKEN_HASH.");
304
+ }
305
+ if (parsed.TELLYMCP_MAGIC_TOKEN_HASH &&
306
+ !/^sha256:[a-f0-9]{64}$/iu.test(parsed.TELLYMCP_MAGIC_TOKEN_HASH)) {
307
+ throw new Error("TELLYMCP_MAGIC_TOKEN_HASH must use the format sha256:<64 hex characters>.");
308
+ }
309
+ if (parsed.TELLYMCP_OAUTH_CLIENT_SECRET && !parsed.TELLYMCP_OAUTH_CLIENT_ID) {
310
+ throw new Error("TELLYMCP_OAUTH_CLIENT_SECRET requires TELLYMCP_OAUTH_CLIENT_ID.");
311
+ }
312
+ for (const [name, value] of [
313
+ ["TELLYMCP_PUBLIC_URL", parsed.TELLYMCP_PUBLIC_URL],
314
+ ["TELLYMCP_OAUTH_ISSUER", parsed.TELLYMCP_OAUTH_ISSUER],
315
+ ["TELLYMCP_OAUTH_AUDIENCE", parsed.TELLYMCP_OAUTH_AUDIENCE],
316
+ ]) {
317
+ if (!value) {
318
+ continue;
319
+ }
320
+ const url = new URL(value);
321
+ if (url.search || url.hash) {
322
+ throw new Error(`${name} must not contain a query string or fragment.`);
323
+ }
324
+ }
325
+ const oauthScopes = parsed.TELLYMCP_OAUTH_SCOPES
326
+ .split(/[\s,]+/u)
327
+ .map((scope) => scope.trim())
328
+ .filter(Boolean);
329
+ if (oauthConfigured && oauthScopes.length === 0) {
330
+ throw new Error("TELLYMCP_OAUTH_SCOPES must contain at least one scope.");
331
+ }
332
+ const allowedRedirectUris = parsed.TELLYMCP_ALLOWED_REDIRECT_URIS
333
+ ? parsed.TELLYMCP_ALLOWED_REDIRECT_URIS.split(",")
334
+ .map((redirectUri) => redirectUri.trim())
335
+ .filter(Boolean)
336
+ : [];
337
+ for (const redirectUri of allowedRedirectUris) {
338
+ try {
339
+ const parsedRedirectUri = new URL(redirectUri);
340
+ if (parsedRedirectUri.hash) {
341
+ throw new Error("fragment");
342
+ }
343
+ }
344
+ catch {
345
+ throw new Error(`TELLYMCP_ALLOWED_REDIRECT_URIS contains an invalid URL: ${redirectUri}`);
346
+ }
347
+ }
260
348
  const telegramProxy = parsed.PROXY_USE === "http"
261
349
  ? parsed.HTTP_PROXY
262
350
  ? {
@@ -328,6 +416,36 @@ function loadConfig() {
328
416
  enableDebugRoutes: parsed.MCP_HTTP_ENABLE_DEBUG_ROUTES,
329
417
  enablePruneRoute: parsed.MCP_HTTP_ENABLE_PRUNE_ROUTE,
330
418
  },
419
+ ...(oauthConfigured && parsed.TELLYMCP_PUBLIC_URL
420
+ ? {
421
+ oauth: {
422
+ publicUrl: parsed.TELLYMCP_PUBLIC_URL.replace(/\/+$/u, ""),
423
+ issuer: (parsed.TELLYMCP_OAUTH_ISSUER ?? parsed.TELLYMCP_PUBLIC_URL).replace(/\/+$/u, ""),
424
+ audience: (parsed.TELLYMCP_OAUTH_AUDIENCE ?? parsed.TELLYMCP_PUBLIC_URL).replace(/\/+$/u, ""),
425
+ ...(parsed.TELLYMCP_MAGIC_TOKEN
426
+ ? { magicToken: parsed.TELLYMCP_MAGIC_TOKEN }
427
+ : {}),
428
+ ...(parsed.TELLYMCP_MAGIC_TOKEN_HASH
429
+ ? { magicTokenHash: parsed.TELLYMCP_MAGIC_TOKEN_HASH }
430
+ : {}),
431
+ ...(parsed.TELLYMCP_OAUTH_CLIENT_ID
432
+ ? { clientId: parsed.TELLYMCP_OAUTH_CLIENT_ID }
433
+ : {}),
434
+ ...(parsed.TELLYMCP_OAUTH_CLIENT_SECRET
435
+ ? { clientSecret: parsed.TELLYMCP_OAUTH_CLIENT_SECRET }
436
+ : {}),
437
+ allowedRedirectUris,
438
+ ...(parsed.TELLYMCP_OAUTH_PRIVATE_KEY_PEM
439
+ ? {
440
+ privateKeyPem: parsed.TELLYMCP_OAUTH_PRIVATE_KEY_PEM.replace(/\\n/gu, "\n"),
441
+ }
442
+ : {}),
443
+ authCodeTtlSeconds: parsed.TELLYMCP_AUTH_CODE_TTL_SECONDS,
444
+ scopes: oauthScopes,
445
+ keyId: parsed.TELLYMCP_OAUTH_KEY_ID,
446
+ },
447
+ }
448
+ : {}),
331
449
  distributed: {
332
450
  mode: parsed.DISTRIBUTED_MODE,
333
451
  ...(parsed.GATEWAY_PUBLIC_URL
@@ -386,7 +504,6 @@ function loadConfig() {
386
504
  sessionTtlSeconds: parsed.WEBAPP_SESSION_TTL_SECONDS,
387
505
  launchMode: parsed.WEBAPP_LAUNCH_MODE,
388
506
  visibleScreens: parsed.WEBAPP_VISIBLE_SCREENS,
389
- pollIntervalMs: parsed.WEBAPP_POLL_INTERVAL_MS,
390
507
  actionCooldownMs: parsed.WEBAPP_ACTION_COOLDOWN_MS,
391
508
  },
392
509
  exchange: {