@deadragdoll/tellymcp 0.0.9 → 0.0.10

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 (27) hide show
  1. package/.env.example.client +2 -0
  2. package/.env.example.gateway +2 -0
  3. package/VERSION.md +26 -0
  4. package/config/templates/env.both.template +1 -0
  5. package/config/templates/env.client.template +1 -0
  6. package/config/templates/env.gateway.template +1 -0
  7. package/dist/services/features/telegram-mcp/approval.service.js +1 -1
  8. package/dist/services/features/telegram-mcp/browser.service.js +1 -1
  9. package/dist/services/features/telegram-mcp/collaboration.service.js +1 -1
  10. package/dist/services/features/telegram-mcp/gateway-rmq.service.js +3 -2
  11. package/dist/services/features/telegram-mcp/gateway-socket.service.js +8 -2
  12. package/dist/services/features/telegram-mcp/inbox.service.js +1 -1
  13. package/dist/services/features/telegram-mcp/mcp-http.service.js +1 -1
  14. package/dist/services/features/telegram-mcp/notify.service.js +1 -1
  15. package/dist/services/features/telegram-mcp/pair.service.js +1 -1
  16. package/dist/services/features/telegram-mcp/runtime.service.js +27 -7
  17. package/dist/services/features/telegram-mcp/session-context.service.js +1 -1
  18. package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +2 -1
  19. package/dist/services/features/telegram-mcp/src/app/config/env.js +4 -0
  20. package/dist/services/features/telegram-mcp/src/shared/i18n/index.js +46 -0
  21. package/dist/services/features/telegram-mcp/src/shared/i18n/resources/en.js +555 -0
  22. package/dist/services/features/telegram-mcp/src/shared/i18n/resources/ru.js +555 -0
  23. package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +9 -0
  24. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +1148 -575
  25. package/dist/services/features/telegram-mcp/standalone-http.service.js +1 -1
  26. package/dist/services/features/telegram-mcp/tools-sync.service.js +1 -1
  27. package/package.json +2 -1
@@ -4,6 +4,8 @@
4
4
  TELEGRAM_BOT_TOKEN=
5
5
  # Username бота, с @ или без.
6
6
  TELEGRAM_BOT_USERNAME=
7
+ # Принудительный язык UI для отладки локализации: en или ru.
8
+ # DEBUG_LANGUAGE=ru
7
9
  # Необязательная подпись проекта/сессии в prompt и note.
8
10
  PROJECT_NAME=
9
11
 
@@ -4,6 +4,8 @@
4
4
  TELEGRAM_BOT_TOKEN=
5
5
  # Username бота, с @ или без.
6
6
  TELEGRAM_BOT_USERNAME=
7
+ # Принудительный язык UI для отладки локализации: en или ru.
8
+ # DEBUG_LANGUAGE=ru
7
9
  # Необязательная подпись проекта/сессии.
8
10
  PROJECT_NAME=
9
11
 
package/VERSION.md CHANGED
@@ -4,6 +4,32 @@ Public, user-facing release notes for published versions of `@deadragdoll/tellym
4
4
 
5
5
  For detailed engineering history, refactors, and internal development notes, see [CHANGELOG.md](CHANGELOG.md).
6
6
 
7
+ ## 0.0.10
8
+
9
+ ### Added
10
+ - Telegram UI localization for the bot layer:
11
+ - English and Russian catalogs for user-facing bot screens
12
+ - locale resolution from Telegram profile with persisted user preference
13
+ - `DEBUG_LANGUAGE=en|ru` override for fast testing without changing the Telegram profile
14
+
15
+ ### Changed
16
+ - Localization scope is now explicit:
17
+ - Telegram bot UI is localized
18
+ - MCP tools, logs, `TOOLS.md`, and the Live Mini App remain canonical English by design
19
+ - Telegram menus, callbacks, project/collaboration screens, startup notices, and operational bot notices now render through a shared translation layer instead of ad hoc inline strings
20
+
21
+ ### Fixed
22
+ - Builder startup is more robust:
23
+ - runtime-dependent services now wait for full runtime readiness instead of racing broker registration
24
+ - this avoids `telegram_mcp runtime is not initialized yet` failures during `yarn dev:builder`
25
+ - Locale resolution is safe for partial test mocks and startup paths
26
+ - Telegram UI text is now consistent across:
27
+ - main menus
28
+ - inbox/session flows
29
+ - project/collab flows
30
+ - live approval prompts
31
+ - tmux and operational bot notices
32
+
7
33
  ## 0.0.9
8
34
 
9
35
  ### Added
@@ -2,6 +2,7 @@
2
2
 
3
3
  TELEGRAM_BOT_TOKEN=
4
4
  TELEGRAM_BOT_USERNAME=
5
+ # DEBUG_LANGUAGE=ru
5
6
  PROJECT_NAME=
6
7
 
7
8
  REDIS_HOST=127.0.0.1
@@ -2,6 +2,7 @@
2
2
 
3
3
  TELEGRAM_BOT_TOKEN=
4
4
  TELEGRAM_BOT_USERNAME=
5
+ # DEBUG_LANGUAGE=ru
5
6
  PROJECT_NAME=
6
7
 
7
8
  REDIS_HOST=127.0.0.1
@@ -2,6 +2,7 @@
2
2
 
3
3
  TELEGRAM_BOT_TOKEN=
4
4
  TELEGRAM_BOT_USERNAME=
5
+ # DEBUG_LANGUAGE=ru
5
6
  PROJECT_NAME=
6
7
 
7
8
  REDIS_HOST=127.0.0.1
@@ -24,7 +24,7 @@ const TelegramMcpApprovalService = {
24
24
  if (!runtimeService) {
25
25
  throw new Error(`Local Moleculer service '${runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME}' is unavailable`);
26
26
  }
27
- const runtime = runtimeService.getRuntime();
27
+ const runtime = await runtimeService.waitUntilReady();
28
28
  this.logger.info("Starting telegram_mcp approval service");
29
29
  this.approvalOrchestrator = new orchestrator_1.HumanApprovalOrchestrator(runtime.config, runtime.stateStore, runtime.stateStore, runtime.stateStore, runtime.telegramTransport, runtime.logger, runtime.projectIdentityResolver);
30
30
  this.logger.info("telegram_mcp approval service is ready");
@@ -24,7 +24,7 @@ const TelegramMcpBrowserService = {
24
24
  if (!runtimeService) {
25
25
  throw new Error(`Local Moleculer service '${runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME}' is unavailable`);
26
26
  }
27
- const runtime = runtimeService.getRuntime();
27
+ const runtime = await runtimeService.waitUntilReady();
28
28
  this.logger.info("Starting telegram_mcp browser service");
29
29
  this.browserService = new browserService_1.BrowserService(runtime.config, runtime.stateStore, runtime.stateStore, runtime.stateStore, runtime.objectStore, runtime.telegramTransport, runtime.logger, runtime.projectIdentityResolver);
30
30
  this.logger.info("telegram_mcp browser service is ready");
@@ -34,7 +34,7 @@ const TelegramMcpCollaborationService = {
34
34
  if (!runtimeService) {
35
35
  throw new Error(`Local Moleculer service '${runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME}' is unavailable`);
36
36
  }
37
- const runtime = runtimeService.getRuntime();
37
+ const runtime = await runtimeService.waitUntilReady();
38
38
  this.logger.info("Starting telegram_mcp collaboration service");
39
39
  const localBackend = new localCollaborationBackend_1.LocalCollaborationBackend(runtime.config, runtime.stateStore, runtime.stateStore, runtime.stateStore, runtime.stateStore, runtime.objectStore, runtime.telegramTransport, runtime.logger);
40
40
  runtime.gatewayHttpService.setPartnerNoteRelayHandler(async (input) => {
@@ -276,10 +276,11 @@ const TelegramMcpGatewayRmqService = {
276
276
  throw new Error(`Local Moleculer service '${runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME}' is unavailable`);
277
277
  }
278
278
  this.runtimeService = runtimeService;
279
+ const runtime = await runtimeService.waitUntilReady();
279
280
  if (!this.isEnabled?.()) {
280
281
  this.logger.info("Gateway RMQ is disabled", {
281
- distributedMode: runtimeService.getRuntime().config.distributed.mode,
282
- rmqConfigured: Boolean(runtimeService.getRuntime().config.distributed.rmq?.host),
282
+ distributedMode: runtime.config.distributed.mode,
283
+ rmqConfigured: Boolean(runtime.config.distributed.rmq?.host),
283
284
  });
284
285
  return;
285
286
  }
@@ -1455,8 +1455,14 @@ const TelegramMcpGatewaySocketService = {
1455
1455
  },
1456
1456
  },
1457
1457
  async started() {
1458
- const runtime = this.getRuntimeOrThrow?.();
1459
- const mode = runtime?.config.distributed.mode;
1458
+ await this.broker.waitForServices([runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME]);
1459
+ const runtimeService = this.broker.getLocalService(runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME);
1460
+ if (!runtimeService) {
1461
+ throw new Error(`Local Moleculer service '${runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME}' is unavailable`);
1462
+ }
1463
+ this.runtimeService = runtimeService;
1464
+ const runtime = await runtimeService.waitUntilReady();
1465
+ const mode = runtime.config.distributed.mode;
1460
1466
  const gatewayEnabled = mode === "gateway" || mode === "both";
1461
1467
  const clientEnabled = mode === "client" || mode === "both";
1462
1468
  if (gatewayEnabled) {
@@ -24,7 +24,7 @@ const TelegramMcpInboxService = {
24
24
  if (!runtimeService) {
25
25
  throw new Error(`Local Moleculer service '${runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME}' is unavailable`);
26
26
  }
27
- const runtime = runtimeService.getRuntime();
27
+ const runtime = await runtimeService.waitUntilReady();
28
28
  this.logger.info("Starting telegram_mcp inbox service");
29
29
  this.inboxService = new inboxService_1.InboxService(runtime.config, runtime.stateStore, runtime.stateStore, runtime.logger, runtime.projectIdentityResolver);
30
30
  this.logger.info("telegram_mcp inbox service is ready");
@@ -52,7 +52,7 @@ const TelegramMcpHttpService = {
52
52
  if (!mcpServerService) {
53
53
  throw new Error(`Local Moleculer service '${mcp_server_service_1.TELEGRAM_MCP_MCP_SERVER_SERVICE_NAME}' is unavailable`);
54
54
  }
55
- const runtime = runtimeService.getRuntime();
55
+ const runtime = await runtimeService.waitUntilReady();
56
56
  this.logger.info("Starting telegram_mcp HTTP service");
57
57
  this.httpHandler = (0, http_1.createMcpHttpHandler)(runtime, {
58
58
  createMcpServer: () => mcpServerService.createServer(),
@@ -24,7 +24,7 @@ const TelegramMcpNotifyService = {
24
24
  if (!runtimeService) {
25
25
  throw new Error(`Local Moleculer service '${runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME}' is unavailable`);
26
26
  }
27
- const runtime = runtimeService.getRuntime();
27
+ const runtime = await runtimeService.waitUntilReady();
28
28
  this.logger.info("Starting telegram_mcp notify service");
29
29
  this.notifyService = new notifyService_1.NotifyService(runtime.config, runtime.stateStore, runtime.stateStore, runtime.telegramTransport, runtime.logger, runtime.projectIdentityResolver);
30
30
  this.logger.info("telegram_mcp notify service is ready");
@@ -24,7 +24,7 @@ const TelegramMcpPairService = {
24
24
  if (!runtimeService) {
25
25
  throw new Error(`Local Moleculer service '${runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME}' is unavailable`);
26
26
  }
27
- const runtime = runtimeService.getRuntime();
27
+ const runtime = await runtimeService.waitUntilReady();
28
28
  this.logger.info("Starting telegram_mcp pair service");
29
29
  this.pairSessionService = new generatePairCode_1.PairSessionService(runtime.config, runtime.stateStore, runtime.stateStore, runtime.stateStore, runtime.logger, runtime.projectIdentityResolver);
30
30
  this.logger.info("telegram_mcp pair service is ready");
@@ -8,6 +8,10 @@ const TelegramMcpRuntimeService = {
8
8
  name: exports.TELEGRAM_MCP_RUNTIME_SERVICE_NAME,
9
9
  created() {
10
10
  this.runtime = null;
11
+ this.readyPromise = new Promise((resolve, reject) => {
12
+ this.resolveReady = resolve;
13
+ this.rejectReady = reject;
14
+ });
11
15
  },
12
16
  methods: {
13
17
  getRuntime() {
@@ -16,19 +20,35 @@ const TelegramMcpRuntimeService = {
16
20
  }
17
21
  return this.runtime;
18
22
  },
23
+ waitUntilReady() {
24
+ if (this.runtime) {
25
+ return Promise.resolve(this.runtime);
26
+ }
27
+ if (!this.readyPromise) {
28
+ return Promise.reject(new Error("telegram_mcp runtime readiness promise is unavailable"));
29
+ }
30
+ return this.readyPromise;
31
+ },
19
32
  },
20
33
  async started() {
21
34
  this.logger.info("Starting telegram_mcp runtime service", {
22
35
  packageVersion: (0, versionHandshake_1.getTellyMcpPackageVersion)(__dirname),
23
36
  protocolVersion: versionHandshake_1.TELLYMCP_PROTOCOL_VERSION,
24
37
  });
25
- this.runtime = await (0, runtime_1.createAppRuntime)({
26
- callBroker: (actionName, params, options) => this.broker.call(actionName, params, options),
27
- });
28
- this.logger.info("telegram_mcp runtime service is ready", {
29
- packageVersion: (0, versionHandshake_1.getTellyMcpPackageVersion)(__dirname),
30
- protocolVersion: versionHandshake_1.TELLYMCP_PROTOCOL_VERSION,
31
- });
38
+ try {
39
+ this.runtime = await (0, runtime_1.createAppRuntime)({
40
+ callBroker: (actionName, params, options) => this.broker.call(actionName, params, options),
41
+ });
42
+ this.resolveReady?.(this.runtime);
43
+ this.logger.info("telegram_mcp runtime service is ready", {
44
+ packageVersion: (0, versionHandshake_1.getTellyMcpPackageVersion)(__dirname),
45
+ protocolVersion: versionHandshake_1.TELLYMCP_PROTOCOL_VERSION,
46
+ });
47
+ }
48
+ catch (error) {
49
+ this.rejectReady?.(error);
50
+ throw error;
51
+ }
32
52
  },
33
53
  async stopped() {
34
54
  if (!this.runtime) {
@@ -24,7 +24,7 @@ const TelegramMcpSessionContextService = {
24
24
  if (!runtimeService) {
25
25
  throw new Error(`Local Moleculer service '${runtime_service_1.TELEGRAM_MCP_RUNTIME_SERVICE_NAME}' is unavailable`);
26
26
  }
27
- const runtime = runtimeService.getRuntime();
27
+ const runtime = await runtimeService.waitUntilReady();
28
28
  this.logger.info("Starting telegram_mcp session-context service");
29
29
  this.sessionContextService = new sessionContextService_1.SessionContextService(runtime.stateStore, runtime.stateStore, runtime.logger, runtime.projectIdentityResolver);
30
30
  this.logger.info("telegram_mcp session-context service is ready");
@@ -72,7 +72,7 @@ async function createAppRuntime(input) {
72
72
  const objectStore = new minioExchangeStore_1.MinioExchangeStore(input.callBroker, stateStore, config.tmux, config.exchange.dir, config.mcp.vfsScope, logger, config.distributed.mode, config.distributed.gatewayPublicUrl, config.distributed.gatewayAuthToken);
73
73
  await stateStore.resetRuntimeState();
74
74
  logger.info("Runtime pending state reset");
75
- const telegramTransport = new transport_1.TelegramTransport(config, stateStore, stateStore, stateStore, stateStore, stateStore, stateStore, objectStore, webAppLaunchRegistry, logger);
75
+ const telegramTransport = new transport_1.TelegramTransport(config, stateStore, stateStore, stateStore, stateStore, stateStore, stateStore, stateStore, objectStore, webAppLaunchRegistry, logger);
76
76
  await telegramTransport.start();
77
77
  logger.info("Telegram transport ready");
78
78
  await telegramTransport.recoverPendingInboxNudges();
@@ -89,6 +89,7 @@ async function createAppRuntime(input) {
89
89
  sessionStore: stateStore,
90
90
  bindingStore: stateStore,
91
91
  inboxStore: stateStore,
92
+ localeStore: stateStore,
92
93
  xchangeFileMetaStore: stateStore,
93
94
  maintenanceStore: stateStore,
94
95
  objectStore,
@@ -59,6 +59,7 @@ const envSchema = z.object({
59
59
  .int()
60
60
  .positive()
61
61
  .default(300),
62
+ DEBUG_LANGUAGE: z.enum(["en", "ru"]).optional(),
62
63
  REDIS_HOST: z.string().min(1),
63
64
  REDIS_PORT: z.coerce.number().int().positive(),
64
65
  REDIS_DB: z.coerce.number().int().nonnegative(),
@@ -205,6 +206,9 @@ function loadConfig() {
205
206
  ...(parsed.TELEGRAM_BOT_USERNAME
206
207
  ? { botUsername: parsed.TELEGRAM_BOT_USERNAME }
207
208
  : {}),
209
+ ...(parsed.DEBUG_LANGUAGE
210
+ ? { debugLanguage: parsed.DEBUG_LANGUAGE }
211
+ : {}),
208
212
  pollIntervalMs: parsed.TELEGRAM_POLL_INTERVAL_MS,
209
213
  defaultTimeoutSeconds: parsed.TELEGRAM_DEFAULT_TIMEOUT_SECONDS,
210
214
  maxContextChars: parsed.TELEGRAM_MAX_CONTEXT_CHARS,
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeLocale = normalizeLocale;
4
+ exports.translate = translate;
5
+ const i18next_1 = require("i18next");
6
+ const en_1 = require("./resources/en");
7
+ const ru_1 = require("./resources/ru");
8
+ const instance = (0, i18next_1.createInstance)();
9
+ void instance.init({
10
+ lng: "en",
11
+ fallbackLng: "en",
12
+ ns: ["common", "menu"],
13
+ defaultNS: "common",
14
+ initAsync: false,
15
+ interpolation: {
16
+ escapeValue: false,
17
+ },
18
+ returnNull: false,
19
+ returnEmptyString: false,
20
+ resources: {
21
+ en: {
22
+ common: en_1.enCommon,
23
+ menu: en_1.enMenu,
24
+ },
25
+ ru: {
26
+ common: ru_1.ruCommon,
27
+ menu: ru_1.ruMenu,
28
+ },
29
+ },
30
+ });
31
+ function normalizeLocale(input) {
32
+ const normalized = input?.trim().toLowerCase() ?? "";
33
+ if (normalized === "ru" || normalized.startsWith("ru-")) {
34
+ return "ru";
35
+ }
36
+ if (normalized === "en" || normalized.startsWith("en-")) {
37
+ return "en";
38
+ }
39
+ return "en";
40
+ }
41
+ function translate(locale, key, options) {
42
+ return instance.t(key, {
43
+ lng: normalizeLocale(locale),
44
+ ...(options ?? {}),
45
+ });
46
+ }