@deadragdoll/tellymcp 0.0.14 → 0.0.15

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 (44) hide show
  1. package/.env.example.client +19 -39
  2. package/.env.example.gateway +40 -64
  3. package/CHANGELOG.md +27 -0
  4. package/README-ru.md +66 -6
  5. package/README.md +66 -6
  6. package/TOOLS.md +38 -1
  7. package/config/templates/env.both.template +4 -5
  8. package/config/templates/env.client.template +4 -17
  9. package/config/templates/env.gateway.template +4 -29
  10. package/dist/cli.js +225 -47
  11. package/dist/configureServer.js +966 -0
  12. package/dist/envMigration.js +316 -0
  13. package/dist/moleculer.config.js +1 -3
  14. package/dist/services/features/telegram-mcp/approval.service.js +1 -1
  15. package/dist/services/features/telegram-mcp/collaboration.service.js +2 -2
  16. package/dist/services/features/telegram-mcp/ensuredb.service.js +1 -1
  17. package/dist/services/features/telegram-mcp/gateway.service.js +1 -1
  18. package/dist/services/features/telegram-mcp/mcp-server.service.js +2 -0
  19. package/dist/services/features/telegram-mcp/session-context.service.js +25 -1
  20. package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +70 -66
  21. package/dist/services/features/telegram-mcp/src/app/config/env.js +10 -36
  22. package/dist/services/features/telegram-mcp/src/app/config/environmentContract.js +66 -0
  23. package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +28 -2
  24. package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +2 -2
  25. package/dist/services/features/telegram-mcp/src/features/collaboration/model/gatewaySessionsService.js +5 -5
  26. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +1 -1
  27. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +4 -4
  28. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +0 -1
  29. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/remoteConsoleActionClient.js +4 -3
  30. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +4 -4
  31. package/dist/services/features/telegram-mcp/src/features/session-context/model/getRuntimeDiagnosticsTool.js +30 -0
  32. package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +169 -7
  33. package/dist/services/features/telegram-mcp/src/shared/integrations/memory/processLocalStateStore.js +260 -0
  34. package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +1 -1
  35. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +1 -1
  36. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConsoleRegistry.js +2 -2
  37. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMessageFlow.js +1 -1
  38. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectState.js +2 -2
  39. package/dist/services/features/telegram-mcp/src/shared/lib/gatewayScope.js +5 -5
  40. package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +10 -0
  41. package/docs/STANDALONE-ru.md +29 -4
  42. package/docs/STANDALONE.md +29 -4
  43. package/package.json +1 -1
  44. package/scripts/postinstall.js +33 -1
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.loadConfig = loadConfig;
37
37
  const node_fs_1 = require("node:fs");
38
38
  const z = __importStar(require("zod/v4"));
39
+ const environmentContract_1 = require("./environmentContract");
39
40
  const emptyStringToUndefined = (value) => {
40
41
  if (typeof value !== "string") {
41
42
  return value;
@@ -79,13 +80,12 @@ const envSchema = z.object({
79
80
  .optional()
80
81
  .transform((value) => value === "true"),
81
82
  DEBUG_LANGUAGE: z.preprocess(emptyStringToUndefined, z.enum(["en", "ru"]).optional()),
82
- REDIS_HOST: z.string().min(1),
83
- REDIS_PORT: z.coerce.number().int().positive(),
84
- REDIS_DB: z.coerce.number().int().nonnegative(),
83
+ REDIS_HOST: z.string().min(1).default("127.0.0.1"),
84
+ REDIS_PORT: z.coerce.number().int().positive().default(6379),
85
+ REDIS_DB: z.coerce.number().int().nonnegative().default(1),
85
86
  REDIS_USERNAME: optionalNonEmptyString,
86
87
  REDIS_PASSWORD: optionalNonEmptyString,
87
- MODE: z.enum(["queue", "reject"]).default("queue"),
88
- PAIR_CODE_TTL_SECONDS: z.coerce.number().int().positive().default(600),
88
+ TELEGRAM_REQUEST_MODE: z.enum(["queue", "reject"]).default("queue"),
89
89
  PROJECT_NAME: optionalNonEmptyString,
90
90
  TELLYMCP_SESSION_ID: optionalNonEmptyString,
91
91
  TELLYMCP_SESSION_LABEL: optionalNonEmptyString,
@@ -117,24 +117,16 @@ const envSchema = z.object({
117
117
  .string()
118
118
  .optional()
119
119
  .transform((value) => value === "true"),
120
- MCP_VFS_SCOPE: z.string().min(1).default("mcp"),
121
120
  DISTRIBUTED_MODE: z.enum(["client", "gateway", "both"]).default("client"),
122
121
  GATEWAY_PUBLIC_URL: optionalUrlString,
123
- GATEWAY_BIND_HOST: z.string().min(1).default("127.0.0.1"),
124
- GATEWAY_BIND_PORT: z.coerce.number().int().positive().default(8790),
125
122
  GATEWAY_WS_URL: optionalUrlString,
126
123
  GATEWAY_WS_PATH: z
127
124
  .string()
128
125
  .min(1)
129
126
  .default(`${(process.env.ROOT_PREFIX || "/api").replace(/\/+$/u, "")}/gateway/ws`),
130
- GATEWAY_TOKEN: optionalNonEmptyString,
127
+ GATEWAY_SCOPE_TOKEN: optionalNonEmptyString,
131
128
  GATEWAY_USER_UUID: optionalNonEmptyString,
132
129
  GATEWAY_AUTH_TOKEN: optionalNonEmptyString,
133
- GATEWAY_DATABASE_URL: optionalNonEmptyString,
134
- GATEWAY_S3_ENDPOINT: optionalNonEmptyString,
135
- GATEWAY_S3_BUCKET: optionalNonEmptyString,
136
- GATEWAY_S3_ACCESS_KEY: optionalNonEmptyString,
137
- GATEWAY_S3_SECRET_KEY: optionalNonEmptyString,
138
130
  RMQ_HOST: optionalNonEmptyString,
139
131
  RMQ_PORT: z.coerce.number().int().positive().optional(),
140
132
  RMQ_USER: optionalNonEmptyString,
@@ -256,6 +248,7 @@ function loadConfig() {
256
248
  else if ((0, node_fs_1.existsSync)(".env")) {
257
249
  process.loadEnvFile(".env");
258
250
  }
251
+ (0, environmentContract_1.assertNoLegacyEnvironmentVariables)(process.env);
259
252
  const parsed = envSchema.parse(process.env);
260
253
  if (parsed.DISTRIBUTED_MODE !== "client" &&
261
254
  !parsed.TELEGRAM_BOT_TOKEN?.trim()) {
@@ -403,13 +396,11 @@ function loadConfig() {
403
396
  ...(parsed.REDIS_USERNAME ? { username: parsed.REDIS_USERNAME } : {}),
404
397
  ...(parsed.REDIS_PASSWORD ? { password: parsed.REDIS_PASSWORD } : {}),
405
398
  },
406
- mode: parsed.MODE,
407
- pairCodeTtlSeconds: parsed.PAIR_CODE_TTL_SECONDS,
399
+ mode: parsed.TELEGRAM_REQUEST_MODE,
408
400
  mcp: {
409
401
  httpHost: parsed.MCP_HTTP_HOST,
410
402
  httpPort: parsed.MCP_HTTP_PORT,
411
403
  httpPath: parsed.MCP_HTTP_PATH,
412
- vfsScope: parsed.MCP_VFS_SCOPE,
413
404
  ...(parsed.MCP_HTTP_BEARER_TOKEN
414
405
  ? { bearerToken: parsed.MCP_HTTP_BEARER_TOKEN }
415
406
  : {}),
@@ -451,14 +442,12 @@ function loadConfig() {
451
442
  ...(parsed.GATEWAY_PUBLIC_URL
452
443
  ? { gatewayPublicUrl: parsed.GATEWAY_PUBLIC_URL }
453
444
  : {}),
454
- gatewayBindHost: parsed.GATEWAY_BIND_HOST,
455
- gatewayBindPort: parsed.GATEWAY_BIND_PORT,
456
445
  ...(parsed.GATEWAY_WS_URL
457
446
  ? { gatewayWsUrl: parsed.GATEWAY_WS_URL }
458
447
  : {}),
459
448
  gatewayWsPath: parsed.GATEWAY_WS_PATH,
460
- ...(parsed.GATEWAY_TOKEN
461
- ? { gatewayToken: parsed.GATEWAY_TOKEN }
449
+ ...(parsed.GATEWAY_SCOPE_TOKEN
450
+ ? { gatewayScopeToken: parsed.GATEWAY_SCOPE_TOKEN }
462
451
  : {}),
463
452
  ...(parsed.GATEWAY_USER_UUID
464
453
  ? { gatewayUserUuid: parsed.GATEWAY_USER_UUID }
@@ -466,21 +455,6 @@ function loadConfig() {
466
455
  ...(parsed.GATEWAY_AUTH_TOKEN
467
456
  ? { gatewayAuthToken: parsed.GATEWAY_AUTH_TOKEN }
468
457
  : {}),
469
- ...(parsed.GATEWAY_DATABASE_URL
470
- ? { gatewayDatabaseUrl: parsed.GATEWAY_DATABASE_URL }
471
- : {}),
472
- ...(parsed.GATEWAY_S3_ENDPOINT
473
- ? { gatewayS3Endpoint: parsed.GATEWAY_S3_ENDPOINT }
474
- : {}),
475
- ...(parsed.GATEWAY_S3_BUCKET
476
- ? { gatewayS3Bucket: parsed.GATEWAY_S3_BUCKET }
477
- : {}),
478
- ...(parsed.GATEWAY_S3_ACCESS_KEY
479
- ? { gatewayS3AccessKey: parsed.GATEWAY_S3_ACCESS_KEY }
480
- : {}),
481
- ...(parsed.GATEWAY_S3_SECRET_KEY
482
- ? { gatewayS3SecretKey: parsed.GATEWAY_S3_SECRET_KEY }
483
- : {}),
484
458
  ...(parsed.RMQ_HOST
485
459
  ? {
486
460
  rmq: {
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REMOVED_ENV_KEYS = exports.LEGACY_ENV_RENAMES = void 0;
4
+ exports.getTmuxReplacement = getTmuxReplacement;
5
+ exports.assertNoLegacyEnvironmentVariables = assertNoLegacyEnvironmentVariables;
6
+ exports.LEGACY_ENV_RENAMES = {
7
+ MODE: "TELEGRAM_REQUEST_MODE",
8
+ GATEWAY_TOKEN: "GATEWAY_SCOPE_TOKEN",
9
+ DB_SCHEME: "DB_SCHEMA",
10
+ ENABLE_LOGFEED: "LOGFEED_ENABLED",
11
+ };
12
+ exports.REMOVED_ENV_KEYS = [
13
+ "APP_NAME",
14
+ "BROWSER_ATTACH_TOKEN",
15
+ "GATEWAY_BIND_HOST",
16
+ "GATEWAY_BIND_PORT",
17
+ "GATEWAY_DATABASE_URL",
18
+ "GATEWAY_S3_ACCESS_KEY",
19
+ "GATEWAY_S3_BUCKET",
20
+ "GATEWAY_S3_ENDPOINT",
21
+ "GATEWAY_S3_SECRET_KEY",
22
+ "MAX_BODY_SIZE",
23
+ "MCP_VFS_SCOPE",
24
+ "PAIR_CODE_TTL_SECONDS",
25
+ "SESSION_SECRET",
26
+ "TELEGRAM_INBOX_BATCH_SIZE",
27
+ "TERMINAL_TRANSPORT",
28
+ "TOKEN_BINDING_SECRET",
29
+ "WEBAPP_POLL_INTERVAL_MS",
30
+ ];
31
+ function getTmuxReplacement(name) {
32
+ if (!name.startsWith("TMUX_")) {
33
+ return null;
34
+ }
35
+ if (name === "TMUX_SOCKET_PATH") {
36
+ return null;
37
+ }
38
+ return `TERMINAL_${name.slice("TMUX_".length)}`;
39
+ }
40
+ function assertNoLegacyEnvironmentVariables(environment) {
41
+ const issues = [];
42
+ for (const [legacyName, replacement] of Object.entries(exports.LEGACY_ENV_RENAMES)) {
43
+ const value = environment[legacyName];
44
+ if (value !== undefined) {
45
+ // MODE is also set by tools such as Vite. Only the two former TellyMCP
46
+ // queue-policy values identify it as our legacy setting.
47
+ if (legacyName === "MODE" && value !== "queue" && value !== "reject") {
48
+ continue;
49
+ }
50
+ issues.push(`${legacyName} -> ${replacement}`);
51
+ }
52
+ }
53
+ for (const name of exports.REMOVED_ENV_KEYS) {
54
+ if (environment[name] !== undefined) {
55
+ issues.push(`${name} (remove)`);
56
+ }
57
+ }
58
+ for (const tmuxName of Object.keys(environment).filter((name) => name.startsWith("TMUX_"))) {
59
+ const replacement = getTmuxReplacement(tmuxName);
60
+ issues.push(replacement ? `${tmuxName} -> ${replacement}` : `${tmuxName} (remove)`);
61
+ }
62
+ if (issues.length > 0) {
63
+ throw new Error(`Environment migration is required: ${issues.sort().join(", ")}. ` +
64
+ "Run: tellymcp migrate-env <input.env> > .migrated-env");
65
+ }
66
+ }
@@ -33,8 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.browserWaitForInputSchema = exports.browserInjectScriptOutputSchema = exports.browserInjectScriptInputSchema = exports.browserPressOutputSchema = exports.browserPressInputSchema = exports.browserFillOutputSchema = exports.browserFillInputSchema = exports.browserClickOutputSchema = exports.browserClickInputSchema = exports.browserReloadOutputSchema = exports.browserReloadInputSchema = exports.browserRecordingStatusOutputSchema = exports.browserRecordingStatusInputSchema = exports.browserRecordingStopOutputSchema = exports.browserRecordingStopInputSchema = exports.browserRecordingStartOutputSchema = exports.browserRecordingStartInputSchema = exports.browserDetachTabOutputSchema = exports.browserDetachTabInputSchema = exports.browserAttachTabOutputSchema = exports.browserAttachTabInputSchema = exports.browserAttachActiveTabInputSchema = exports.browserListTabsOutputSchema = exports.browserListTabsInputSchema = exports.browserListAttachedInstancesOutputSchema = exports.browserListAttachedInstancesInputSchema = exports.browserOpenOutputSchema = exports.browserOpenInputSchema = exports.clearSessionContextOutputSchema = exports.clearSessionContextInputSchema = exports.getSessionContextOutputSchema = exports.getSessionContextInputSchema = exports.setSessionContextOutputSchema = exports.renameSessionOutputSchema = exports.renameSessionInputSchema = exports.setSessionContextInputSchema = exports.refreshToolsMarkdownOutputSchema = exports.refreshToolsMarkdownInputSchema = exports.listGatewaySessionsOutputSchema = exports.listGatewaySessionsInputSchema = exports.getFileListOutputSchema = exports.getFileListInputSchema = exports.getFileOutputSchema = exports.getFileInputSchema = exports.sendFileToTelegramOutputSchema = exports.sendFileToTelegramInputSchema = exports.notifyTelegramOutputSchema = exports.notifyTelegramInputSchema = exports.askUserTelegramOutputSchema = exports.askUserTelegramInputSchema = void 0;
37
- exports.markXchangeRecordReadOutputSchema = exports.markXchangeRecordReadInputSchema = exports.getXchangeRecordOutputSchema = exports.getXchangeRecordInputSchema = exports.listXchangeRecordsOutputSchema = exports.listXchangeRecordsInputSchema = exports.sendPartnerNoteOutputSchema = exports.sendPartnerFileInputSchema = exports.sendPartnerNoteInputSchema = exports.browserCloseOutputSchema = exports.browserCloseInputSchema = exports.browserScreenshotOutputSchema = exports.browserScreenshotInputSchema = exports.browserComputedStyleOutputSchema = exports.browserComputedStyleInputSchema = exports.browserDomOutputSchema = exports.browserDomInputSchema = exports.browserClearLogsOutputSchema = exports.browserClearLogsInputSchema = exports.browserNetworkFailuresOutputSchema = exports.browserNetworkFailuresInputSchema = exports.browserErrorsOutputSchema = exports.browserErrorsInputSchema = exports.browserConsoleOutputSchema = exports.browserConsoleInputSchema = exports.browserWaitForUrlOutputSchema = exports.browserWaitForUrlInputSchema = exports.browserWaitForOutputSchema = void 0;
36
+ exports.browserInjectScriptInputSchema = exports.browserPressOutputSchema = exports.browserPressInputSchema = exports.browserFillOutputSchema = exports.browserFillInputSchema = exports.browserClickOutputSchema = exports.browserClickInputSchema = exports.browserReloadOutputSchema = exports.browserReloadInputSchema = exports.browserRecordingStatusOutputSchema = exports.browserRecordingStatusInputSchema = exports.browserRecordingStopOutputSchema = exports.browserRecordingStopInputSchema = exports.browserRecordingStartOutputSchema = exports.browserRecordingStartInputSchema = exports.browserDetachTabOutputSchema = exports.browserDetachTabInputSchema = exports.browserAttachTabOutputSchema = exports.browserAttachTabInputSchema = exports.browserAttachActiveTabInputSchema = exports.browserListTabsOutputSchema = exports.browserListTabsInputSchema = exports.browserListAttachedInstancesOutputSchema = exports.browserListAttachedInstancesInputSchema = exports.browserOpenOutputSchema = exports.browserOpenInputSchema = exports.clearSessionContextOutputSchema = exports.clearSessionContextInputSchema = exports.getRuntimeDiagnosticsOutputSchema = exports.getRuntimeDiagnosticsInputSchema = exports.getSessionContextOutputSchema = exports.getSessionContextInputSchema = exports.setSessionContextOutputSchema = exports.renameSessionOutputSchema = exports.renameSessionInputSchema = exports.setSessionContextInputSchema = exports.refreshToolsMarkdownOutputSchema = exports.refreshToolsMarkdownInputSchema = exports.listGatewaySessionsOutputSchema = exports.listGatewaySessionsInputSchema = exports.getFileListOutputSchema = exports.getFileListInputSchema = exports.getFileOutputSchema = exports.getFileInputSchema = exports.sendFileToTelegramOutputSchema = exports.sendFileToTelegramInputSchema = exports.notifyTelegramOutputSchema = exports.notifyTelegramInputSchema = exports.askUserTelegramOutputSchema = exports.askUserTelegramInputSchema = void 0;
37
+ exports.markXchangeRecordReadOutputSchema = exports.markXchangeRecordReadInputSchema = exports.getXchangeRecordOutputSchema = exports.getXchangeRecordInputSchema = exports.listXchangeRecordsOutputSchema = exports.listXchangeRecordsInputSchema = exports.sendPartnerNoteOutputSchema = exports.sendPartnerFileInputSchema = exports.sendPartnerNoteInputSchema = exports.browserCloseOutputSchema = exports.browserCloseInputSchema = exports.browserScreenshotOutputSchema = exports.browserScreenshotInputSchema = exports.browserComputedStyleOutputSchema = exports.browserComputedStyleInputSchema = exports.browserDomOutputSchema = exports.browserDomInputSchema = exports.browserClearLogsOutputSchema = exports.browserClearLogsInputSchema = exports.browserNetworkFailuresOutputSchema = exports.browserNetworkFailuresInputSchema = exports.browserErrorsOutputSchema = exports.browserErrorsInputSchema = exports.browserConsoleOutputSchema = exports.browserConsoleInputSchema = exports.browserWaitForUrlOutputSchema = exports.browserWaitForUrlInputSchema = exports.browserWaitForOutputSchema = exports.browserWaitForInputSchema = exports.browserInjectScriptOutputSchema = void 0;
38
38
  const z = __importStar(require("zod/v4"));
39
39
  const bodyLimits_1 = require("../../../shared/lib/bodyLimits");
40
40
  const bodyStringSchema = z.string().max(bodyLimits_1.MAX_BODY_SIZE_BYTES);
@@ -224,6 +224,32 @@ exports.getSessionContextOutputSchema = z.object({
224
224
  })
225
225
  .optional(),
226
226
  });
227
+ const runtimeDiagnosticCheckSchema = z.object({
228
+ status: z.enum(["ok", "warn", "error"]),
229
+ message: z.string(),
230
+ });
231
+ exports.getRuntimeDiagnosticsInputSchema = z.object({
232
+ session_id: z.string().trim().min(1).optional(),
233
+ });
234
+ exports.getRuntimeDiagnosticsOutputSchema = z.object({
235
+ status: z.enum(["ok", "degraded"]),
236
+ checked_at: z.string(),
237
+ session_id: z.string(),
238
+ runtime: z.object({
239
+ mode: z.enum(["client", "gateway", "both"]),
240
+ package_version: z.string(),
241
+ protocol_version: z.string(),
242
+ node_id: z.string().optional(),
243
+ }),
244
+ checks: z.object({
245
+ configuration: runtimeDiagnosticCheckSchema,
246
+ redis: runtimeDiagnosticCheckSchema,
247
+ session_store: runtimeDiagnosticCheckSchema,
248
+ terminal: runtimeDiagnosticCheckSchema,
249
+ gateway_configuration: runtimeDiagnosticCheckSchema,
250
+ relay: runtimeDiagnosticCheckSchema,
251
+ }),
252
+ });
227
253
  exports.clearSessionContextInputSchema = z.object({
228
254
  session_id: z.string().trim().min(1).optional(),
229
255
  });
@@ -1752,8 +1752,8 @@ for (const __tellyKey of Object.getOwnPropertyNames(window)) {
1752
1752
  ...(this.config.distributed.gatewayAuthToken
1753
1753
  ? { gatewayAuthToken: this.config.distributed.gatewayAuthToken }
1754
1754
  : {}),
1755
- ...(this.config.distributed.gatewayToken
1756
- ? { gatewayToken: this.config.distributed.gatewayToken }
1755
+ ...(this.config.distributed.gatewayScopeToken
1756
+ ? { gatewayScopeToken: this.config.distributed.gatewayScopeToken }
1757
1757
  : {}),
1758
1758
  ...(this.config.project.name
1759
1759
  ? { projectName: this.config.project.name }
@@ -7,16 +7,16 @@ class GatewaySessionsService {
7
7
  maintenanceStore;
8
8
  gatewayPublicUrl;
9
9
  gatewayAuthToken;
10
- gatewayToken;
10
+ gatewayScopeToken;
11
11
  gatewayUserUuid;
12
12
  projectName;
13
13
  botUsername;
14
- constructor(logger, maintenanceStore, gatewayPublicUrl, gatewayAuthToken, gatewayToken, gatewayUserUuid, projectName, botUsername) {
14
+ constructor(logger, maintenanceStore, gatewayPublicUrl, gatewayAuthToken, gatewayScopeToken, gatewayUserUuid, projectName, botUsername) {
15
15
  this.logger = logger;
16
16
  this.maintenanceStore = maintenanceStore;
17
17
  this.gatewayPublicUrl = gatewayPublicUrl;
18
18
  this.gatewayAuthToken = gatewayAuthToken;
19
- this.gatewayToken = gatewayToken;
19
+ this.gatewayScopeToken = gatewayScopeToken;
20
20
  this.gatewayUserUuid = gatewayUserUuid;
21
21
  this.projectName = projectName;
22
22
  this.botUsername = botUsername;
@@ -31,7 +31,7 @@ class GatewaySessionsService {
31
31
  ...(this.gatewayAuthToken
32
32
  ? { gatewayAuthToken: this.gatewayAuthToken }
33
33
  : {}),
34
- ...(this.gatewayToken ? { gatewayToken: this.gatewayToken } : {}),
34
+ ...(this.gatewayScopeToken ? { gatewayScopeToken: this.gatewayScopeToken } : {}),
35
35
  ...(this.gatewayUserUuid ? { gatewayUserUuid: this.gatewayUserUuid } : {}),
36
36
  ...(this.projectName ? { projectName: this.projectName } : {}),
37
37
  ...(this.botUsername ? { botUsername: this.botUsername } : {}),
@@ -46,7 +46,7 @@ class GatewaySessionsService {
46
46
  ...(input.client_uuid?.trim()
47
47
  ? { client_uuid: input.client_uuid.trim() }
48
48
  : {}),
49
- ...(this.gatewayToken ? { gateway_token: this.gatewayToken } : {}),
49
+ ...(this.gatewayScopeToken ? { gateway_token: this.gatewayScopeToken } : {}),
50
50
  ...(this.gatewayUserUuid
51
51
  ? { owner_user_uuid: this.gatewayUserUuid }
52
52
  : {}),
@@ -68,7 +68,7 @@ async function ensureGatewayClientUuid(input) {
68
68
  input.botUsername ||
69
69
  "tellymcp client",
70
70
  ...(input.botUsername ? { bot_username: input.botUsername } : {}),
71
- ...(input.gatewayToken ? { gateway_token: input.gatewayToken } : {}),
71
+ ...(input.gatewayScopeToken ? { gateway_token: input.gatewayScopeToken } : {}),
72
72
  ...(input.gatewayUserUuid ? { owner_user_uuid: input.gatewayUserUuid } : {}),
73
73
  meta: {
74
74
  ...(namespace ? { namespace } : {}),
@@ -7,16 +7,16 @@ class GatewayCollaborationBackend {
7
7
  maintenanceStore;
8
8
  gatewayPublicUrl;
9
9
  gatewayAuthToken;
10
- gatewayToken;
10
+ gatewayScopeToken;
11
11
  gatewayUserUuid;
12
12
  projectName;
13
13
  botUsername;
14
- constructor(logger, maintenanceStore, gatewayPublicUrl, gatewayAuthToken, gatewayToken, gatewayUserUuid, projectName, botUsername) {
14
+ constructor(logger, maintenanceStore, gatewayPublicUrl, gatewayAuthToken, gatewayScopeToken, gatewayUserUuid, projectName, botUsername) {
15
15
  this.logger = logger;
16
16
  this.maintenanceStore = maintenanceStore;
17
17
  this.gatewayPublicUrl = gatewayPublicUrl;
18
18
  this.gatewayAuthToken = gatewayAuthToken;
19
- this.gatewayToken = gatewayToken;
19
+ this.gatewayScopeToken = gatewayScopeToken;
20
20
  this.gatewayUserUuid = gatewayUserUuid;
21
21
  this.projectName = projectName;
22
22
  this.botUsername = botUsername;
@@ -33,7 +33,7 @@ class GatewayCollaborationBackend {
33
33
  ...(this.gatewayAuthToken
34
34
  ? { gatewayAuthToken: this.gatewayAuthToken }
35
35
  : {}),
36
- ...(this.gatewayToken ? { gatewayToken: this.gatewayToken } : {}),
36
+ ...(this.gatewayScopeToken ? { gatewayScopeToken: this.gatewayScopeToken } : {}),
37
37
  ...(this.gatewayUserUuid ? { gatewayUserUuid: this.gatewayUserUuid } : {}),
38
38
  ...(this.projectName ? { projectName: this.projectName } : {}),
39
39
  ...(this.botUsername ? { botUsername: this.botUsername } : {}),
@@ -241,7 +241,6 @@ class GatewayHttpService {
241
241
  service: "tellymcp-gateway",
242
242
  mode: this.config.distributed.mode,
243
243
  databaseConfigured: Boolean(process.env.DB_HOST && process.env.DB_NAME),
244
- s3Configured: Boolean(this.config.distributed.gatewayS3Bucket),
245
244
  });
246
245
  return true;
247
246
  }
@@ -9,6 +9,7 @@ function isBackendErrorLike(value) {
9
9
  typeof value.code === "string");
10
10
  }
11
11
  function formatBackendErrorLike(value) {
12
+ const truncate = (text, maxLength = 2_000) => text.length > maxLength ? `${text.slice(0, maxLength - 3)}...` : text;
12
13
  const details = [];
13
14
  if (typeof value.code === "string" && value.code.trim()) {
14
15
  details.push(`code=${value.code.trim()}`);
@@ -18,14 +19,14 @@ function formatBackendErrorLike(value) {
18
19
  }
19
20
  if (value.data !== undefined) {
20
21
  try {
21
- details.push(`data=${JSON.stringify(value.data)}`);
22
+ details.push(`data=${truncate(JSON.stringify(value.data))}`);
22
23
  }
23
24
  catch {
24
- details.push(`data=${String(value.data)}`);
25
+ details.push(`data=${truncate(String(value.data))}`);
25
26
  }
26
27
  }
27
28
  const base = typeof value.message === "string" && value.message.trim()
28
- ? value.message.trim()
29
+ ? truncate(value.message.trim())
29
30
  : `${value.name ?? "BackendError"} (${value.code})`;
30
31
  return details.length > 0 ? `${base}\n${details.join("\n")}` : base;
31
32
  }
@@ -325,8 +325,8 @@ class NotifyService {
325
325
  ...(this._config.distributed.gatewayAuthToken
326
326
  ? { gatewayAuthToken: this._config.distributed.gatewayAuthToken }
327
327
  : {}),
328
- ...(this._config.distributed.gatewayToken
329
- ? { gatewayToken: this._config.distributed.gatewayToken }
328
+ ...(this._config.distributed.gatewayScopeToken
329
+ ? { gatewayScopeToken: this._config.distributed.gatewayScopeToken }
330
330
  : {}),
331
331
  ...(this._config.project.name
332
332
  ? { projectName: this._config.project.name }
@@ -382,8 +382,8 @@ class NotifyService {
382
382
  ...(this._config.distributed.gatewayAuthToken
383
383
  ? { gatewayAuthToken: this._config.distributed.gatewayAuthToken }
384
384
  : {}),
385
- ...(this._config.distributed.gatewayToken
386
- ? { gatewayToken: this._config.distributed.gatewayToken }
385
+ ...(this._config.distributed.gatewayScopeToken
386
+ ? { gatewayScopeToken: this._config.distributed.gatewayScopeToken }
387
387
  : {}),
388
388
  ...(this._config.project.name
389
389
  ? { projectName: this._config.project.name }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GetRuntimeDiagnosticsTool = void 0;
4
+ const schema_1 = require("../../../entities/request/model/schema");
5
+ class GetRuntimeDiagnosticsTool {
6
+ sessionContextService;
7
+ constructor(sessionContextService) {
8
+ this.sessionContextService = sessionContextService;
9
+ }
10
+ register(server) {
11
+ server.registerTool("get_runtime_diagnostics", {
12
+ title: "Get Runtime Diagnostics",
13
+ description: "Run safe, read-only health checks for a selected console: environment schema, package/protocol version, runtime state store, PTY state, gateway configuration, and gateway-to-client relay. Redis is probed only for gateway runtimes. Secrets and raw connection strings are never returned. In gateway mode, pass session_id exactly as returned by list_gateway_sessions.",
14
+ inputSchema: schema_1.getRuntimeDiagnosticsInputSchema,
15
+ outputSchema: schema_1.getRuntimeDiagnosticsOutputSchema,
16
+ }, async (args) => {
17
+ const output = await this.sessionContextService.getRuntimeDiagnostics(args);
18
+ return {
19
+ content: [
20
+ {
21
+ type: "text",
22
+ text: JSON.stringify(output, null, 2),
23
+ },
24
+ ],
25
+ structuredContent: output,
26
+ };
27
+ });
28
+ }
29
+ }
30
+ exports.GetRuntimeDiagnosticsTool = GetRuntimeDiagnosticsTool;
@@ -9,16 +9,174 @@ class SessionContextService {
9
9
  logger;
10
10
  projectIdentityResolver;
11
11
  remoteConsoleInvoker;
12
- constructor(sessionStore, bindingStore, logger, projectIdentityResolver, remoteConsoleInvoker) {
12
+ runtimeDiagnostics;
13
+ constructor(sessionStore, bindingStore, logger, projectIdentityResolver, remoteConsoleInvoker, runtimeDiagnostics) {
13
14
  this.sessionStore = sessionStore;
14
15
  this.bindingStore = bindingStore;
15
16
  this.logger = logger;
16
17
  this.projectIdentityResolver = projectIdentityResolver;
17
18
  this.remoteConsoleInvoker = remoteConsoleInvoker;
19
+ this.runtimeDiagnostics = runtimeDiagnostics;
20
+ }
21
+ getRemoteConsoleInvoker() {
22
+ return this.runtimeDiagnostics?.mode === "client"
23
+ ? undefined
24
+ : this.remoteConsoleInvoker;
25
+ }
26
+ formatDiagnosticError(error) {
27
+ const message = error instanceof Error ? error.message : String(error);
28
+ const redacted = (0, redactSecrets_1.redactSecrets)(message).replace(/\s+/gu, " ").trim();
29
+ return redacted.length > 500
30
+ ? `${redacted.slice(0, 497)}...`
31
+ : redacted || "Unknown error";
32
+ }
33
+ finalizeDiagnostics(output) {
34
+ const degraded = Object.values(output.checks).some((check) => check.status !== "ok");
35
+ return {
36
+ ...output,
37
+ status: degraded ? "degraded" : "ok",
38
+ };
39
+ }
40
+ async collectLocalDiagnostics(sessionId) {
41
+ const runtime = this.runtimeDiagnostics;
42
+ let session = null;
43
+ let sessionStoreCheck;
44
+ try {
45
+ session = await this.sessionStore.getSession(sessionId);
46
+ await this.bindingStore.getBinding(sessionId);
47
+ sessionStoreCheck = {
48
+ status: "ok",
49
+ message: session
50
+ ? "Session and route stores are readable; session metadata exists."
51
+ : "Session and route stores are readable; no saved metadata exists for this id.",
52
+ };
53
+ }
54
+ catch (error) {
55
+ sessionStoreCheck = {
56
+ status: "error",
57
+ message: `Session store check failed: ${this.formatDiagnosticError(error)}`,
58
+ };
59
+ }
60
+ let redisCheck;
61
+ if (runtime?.mode === "client") {
62
+ redisCheck = {
63
+ status: "ok",
64
+ message: "Redis is not required in client mode; process-local state is active.",
65
+ };
66
+ }
67
+ else if (!runtime?.pingRedis) {
68
+ redisCheck = {
69
+ status: "warn",
70
+ message: "Runtime Redis probe is unavailable.",
71
+ };
72
+ }
73
+ else {
74
+ try {
75
+ const reply = await runtime.pingRedis();
76
+ redisCheck = {
77
+ status: reply.trim().toUpperCase() === "PONG" ? "ok" : "warn",
78
+ message: `Redis probe returned ${reply.trim() || "an empty response"}.`,
79
+ };
80
+ }
81
+ catch (error) {
82
+ redisCheck = {
83
+ status: "error",
84
+ message: `Redis probe failed: ${this.formatDiagnosticError(error)}`,
85
+ };
86
+ }
87
+ }
88
+ const mode = runtime?.mode ?? "client";
89
+ const gatewayConfigured = Boolean(runtime?.gatewayWsUrlConfigured);
90
+ const gatewayConfiguration = mode === "gateway"
91
+ ? {
92
+ status: "ok",
93
+ message: "Gateway runtime does not require an outbound gateway WebSocket URL.",
94
+ }
95
+ : gatewayConfigured
96
+ ? {
97
+ status: "ok",
98
+ message: runtime?.gatewayAuthConfigured
99
+ ? "Gateway WebSocket URL and authentication are configured."
100
+ : "Gateway WebSocket URL is configured; authentication is not configured.",
101
+ }
102
+ : {
103
+ status: "error",
104
+ message: "Gateway WebSocket URL is not configured for this client runtime.",
105
+ };
106
+ return this.finalizeDiagnostics({
107
+ checked_at: new Date().toISOString(),
108
+ session_id: sessionId,
109
+ runtime: {
110
+ mode,
111
+ package_version: runtime?.packageVersion ?? "unknown",
112
+ protocol_version: runtime?.protocolVersion ?? "unknown",
113
+ ...(runtime?.nodeId ? { node_id: runtime.nodeId } : {}),
114
+ },
115
+ checks: {
116
+ configuration: runtime
117
+ ? {
118
+ status: "ok",
119
+ message: "Normalized environment schema was accepted at startup.",
120
+ }
121
+ : {
122
+ status: "warn",
123
+ message: "Runtime configuration metadata is unavailable.",
124
+ },
125
+ redis: redisCheck,
126
+ session_store: sessionStoreCheck,
127
+ terminal: session?.terminalTarget
128
+ ? {
129
+ status: "ok",
130
+ message: "A PTY terminal target is configured for this session.",
131
+ }
132
+ : {
133
+ status: "warn",
134
+ message: "No PTY terminal target is configured for this session.",
135
+ },
136
+ gateway_configuration: gatewayConfiguration,
137
+ relay: {
138
+ status: "ok",
139
+ message: "Local diagnostic action completed.",
140
+ },
141
+ },
142
+ });
143
+ }
144
+ async getRuntimeDiagnostics(input) {
145
+ const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
146
+ const remoteConsoleInvoker = this.getRemoteConsoleInvoker();
147
+ if (!remoteConsoleInvoker) {
148
+ return await this.collectLocalDiagnostics(resolved.sessionId);
149
+ }
150
+ try {
151
+ const remote = await remoteConsoleInvoker.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.getRuntimeDiagnosticsRemote", input);
152
+ return this.finalizeDiagnostics({
153
+ ...remote,
154
+ checks: {
155
+ ...remote.checks,
156
+ relay: {
157
+ status: "ok",
158
+ message: "Gateway-to-client relay completed successfully.",
159
+ },
160
+ },
161
+ });
162
+ }
163
+ catch (error) {
164
+ const local = await this.collectLocalDiagnostics(resolved.sessionId);
165
+ return this.finalizeDiagnostics({
166
+ ...local,
167
+ checks: {
168
+ ...local.checks,
169
+ relay: {
170
+ status: "error",
171
+ message: `Gateway-to-client relay failed: ${this.formatDiagnosticError(error)}`,
172
+ },
173
+ },
174
+ });
175
+ }
18
176
  }
19
177
  async setContext(input) {
20
178
  const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
21
- const remote = await this.remoteConsoleInvoker?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.setContextRemote", input);
179
+ const remote = await this.getRemoteConsoleInvoker()?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.setContextRemote", input);
22
180
  if (remote) {
23
181
  return remote;
24
182
  }
@@ -62,7 +220,9 @@ class SessionContextService {
62
220
  : existing?.risks
63
221
  ? { risks: existing.risks }
64
222
  : {}),
65
- ...(existing?.terminalTarget ? { terminalTarget: existing.terminalTarget } : {}),
223
+ ...(existing?.terminalTarget
224
+ ? { terminalTarget: existing.terminalTarget }
225
+ : {}),
66
226
  ...(existing?.lastTerminalNudgeAt
67
227
  ? { lastTerminalNudgeAt: existing.lastTerminalNudgeAt }
68
228
  : {}),
@@ -98,7 +258,7 @@ class SessionContextService {
98
258
  }
99
259
  async renameSession(input) {
100
260
  const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
101
- const remote = await this.remoteConsoleInvoker?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.renameSessionRemote", input);
261
+ const remote = await this.getRemoteConsoleInvoker()?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.renameSessionRemote", input);
102
262
  if (remote) {
103
263
  return remote;
104
264
  }
@@ -120,7 +280,9 @@ class SessionContextService {
120
280
  ...(existing?.files ? { files: existing.files } : {}),
121
281
  ...(existing?.decisions ? { decisions: existing.decisions } : {}),
122
282
  ...(existing?.risks ? { risks: existing.risks } : {}),
123
- ...(existing?.terminalTarget ? { terminalTarget: existing.terminalTarget } : {}),
283
+ ...(existing?.terminalTarget
284
+ ? { terminalTarget: existing.terminalTarget }
285
+ : {}),
124
286
  ...(existing?.lastTerminalNudgeAt
125
287
  ? { lastTerminalNudgeAt: existing.lastTerminalNudgeAt }
126
288
  : {}),
@@ -151,7 +313,7 @@ class SessionContextService {
151
313
  }
152
314
  async getContext(input) {
153
315
  const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
154
- const remote = await this.remoteConsoleInvoker?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.getContextRemote", input);
316
+ const remote = await this.getRemoteConsoleInvoker()?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.getContextRemote", input);
155
317
  if (remote) {
156
318
  return remote;
157
319
  }
@@ -224,7 +386,7 @@ class SessionContextService {
224
386
  }
225
387
  async clearContext(input) {
226
388
  const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
227
- const remote = await this.remoteConsoleInvoker?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.clearContextRemote", input);
389
+ const remote = await this.getRemoteConsoleInvoker()?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.clearContextRemote", input);
228
390
  if (remote) {
229
391
  return remote;
230
392
  }