@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
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TransportAttachmentStore = void 0;
4
+ const transportUtils_1 = require("./transportUtils");
5
+ class TransportAttachmentStore {
6
+ host;
7
+ constructor(host) {
8
+ this.host = host;
9
+ }
10
+ async storeTelegramUploadMetas(input) {
11
+ for (let index = 0; index < input.attachments.length; index += 1) {
12
+ const attachment = input.attachments[index];
13
+ if (!attachment) {
14
+ continue;
15
+ }
16
+ const descriptor = input.descriptors?.[index];
17
+ await this.host.xchangeFileMetaStore.setXchangeFileMeta({
18
+ sessionId: input.sessionId,
19
+ filePath: attachment.filePath,
20
+ relativePath: attachment.relativePath,
21
+ source: "telegram-upload",
22
+ sourceTelegramMessageId: input.sourceTelegramMessageId,
23
+ uploadedAt: input.uploadedAt,
24
+ storageRef: attachment.storageRef,
25
+ bucketName: attachment.bucketName,
26
+ objectName: attachment.objectName,
27
+ vfsNodeId: attachment.vfsNodeId,
28
+ vfsPublicUrl: attachment.vfsPublicUrl,
29
+ vfsParentId: attachment.vfsParentId,
30
+ mimeType: attachment.mimeType,
31
+ sizeBytes: attachment.sizeBytes,
32
+ ...(descriptor && !descriptor.preferredName.startsWith("photo-")
33
+ ? { originalName: descriptor.preferredName }
34
+ : {}),
35
+ ...(input.caption ? { caption: input.caption } : {}),
36
+ });
37
+ }
38
+ }
39
+ async ensureStoredXchangeFile(sessionId, filePath, source) {
40
+ const session = await this.host.sessionStore.getSession(sessionId);
41
+ const meta = await this.host.xchangeFileMetaStore.getXchangeFileMeta(sessionId, filePath);
42
+ if (!meta) {
43
+ return { session, filePath };
44
+ }
45
+ const materializedPath = await this.host.objectStore.ensureLocalFile({
46
+ sessionId,
47
+ session,
48
+ filePath,
49
+ relativePath: meta.relativePath,
50
+ storageRef: meta.storageRef,
51
+ source,
52
+ });
53
+ return {
54
+ session,
55
+ filePath: materializedPath,
56
+ };
57
+ }
58
+ async downloadIncomingAttachments(session, sessionId, sourceTelegramMessageId, attachments) {
59
+ if (attachments.length === 0) {
60
+ return [];
61
+ }
62
+ const savedFiles = [];
63
+ for (const attachment of attachments) {
64
+ const savedFile = await this.downloadTelegramFile(session, sessionId, attachment.fileId, sourceTelegramMessageId, attachment.preferredName, attachment.mimeType);
65
+ savedFiles.push(savedFile);
66
+ }
67
+ return savedFiles;
68
+ }
69
+ async downloadTelegramFile(session, sessionId, fileId, _sourceTelegramMessageId, preferredName, preferredMimeType) {
70
+ const telegramFile = await this.host.getTelegramFile(fileId);
71
+ if (!telegramFile.file_path) {
72
+ throw new Error("Telegram file path is missing");
73
+ }
74
+ const outputName = preferredName;
75
+ const fileUrl = `https://api.telegram.org/file/bot${this.host.getRequiredBotToken("download Telegram files")}/${telegramFile.file_path}`;
76
+ const response = await this.host.telegramFetch(fileUrl);
77
+ if (!response.ok) {
78
+ throw new Error(`Telegram file download failed with status ${response.status}`);
79
+ }
80
+ const buffer = Buffer.from(await response.arrayBuffer());
81
+ return this.host.objectStore.storeFile({
82
+ session,
83
+ sessionId,
84
+ source: "telegram-upload",
85
+ relativePath: (0, transportUtils_1.buildDatedRelativePath)(outputName),
86
+ content: buffer,
87
+ mimeType: preferredMimeType ||
88
+ response.headers.get("content-type") ||
89
+ undefined,
90
+ });
91
+ }
92
+ }
93
+ exports.TransportAttachmentStore = TransportAttachmentStore;
@@ -0,0 +1,385 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TransportBroadcastActions = void 0;
4
+ const grammy_1 = require("grammy");
5
+ const relay_1 = require("../../../app/webapp/relay");
6
+ const redactSecrets_1 = require("../../lib/redact-secrets/redactSecrets");
7
+ const telegramXchangeRecords_1 = require("../../lib/telegramXchangeRecords");
8
+ const transportFormatting_1 = require("./transportFormatting");
9
+ const transportUtils_1 = require("./transportUtils");
10
+ class TransportBroadcastActions {
11
+ host;
12
+ constructor(host) {
13
+ this.host = host;
14
+ }
15
+ async beginBroadcast(ctx) {
16
+ const locale = await this.host.resolveLocaleForContext(ctx);
17
+ const principal = this.host.getPrincipalFromContext(ctx);
18
+ if (!principal) {
19
+ await ctx.answerCallbackQuery({
20
+ text: this.host.t(locale, "common:errors.no_telegram_identity"),
21
+ show_alert: true,
22
+ });
23
+ return;
24
+ }
25
+ const sessionIds = await this.host.bindingStore.listBoundSessionIdsForPrincipal(principal);
26
+ if (sessionIds.length === 0) {
27
+ await ctx.answerCallbackQuery({
28
+ text: this.host.t(locale, "menu:broadcast.no_linked_sessions"),
29
+ show_alert: true,
30
+ });
31
+ return;
32
+ }
33
+ const principalKey = (0, transportUtils_1.buildPrincipalKey)(principal);
34
+ this.host.pendingRenames.delete(principalKey);
35
+ await ctx.answerCallbackQuery({
36
+ text: this.host.t(locale, "menu:broadcast.begin", { count: sessionIds.length }),
37
+ });
38
+ const sent = await this.host.replyText(ctx, [
39
+ this.host.t(locale, "menu:broadcast.title"),
40
+ "",
41
+ this.host.t(locale, "menu:broadcast.body", { count: sessionIds.length }),
42
+ this.host.t(locale, "menu:broadcast.hint"),
43
+ this.host.t(locale, "menu:broadcast.cancel_hint"),
44
+ ].join("\n"), { kind: "menu" }, { reply_markup: new grammy_1.InlineKeyboard().text("Cancel", "broadcast-cancel") });
45
+ this.host.pendingBroadcasts.set(principalKey, {
46
+ initiatedAt: new Date().toISOString(),
47
+ scope: "linked",
48
+ ...(sent ? { promptMessageId: sent.message_id } : {}),
49
+ ...(ctx.callbackQuery?.message?.message_id
50
+ ? { menuMessageId: ctx.callbackQuery.message.message_id }
51
+ : {}),
52
+ });
53
+ }
54
+ async beginProjectBroadcast(ctx) {
55
+ const locale = await this.host.resolveLocaleForContext(ctx);
56
+ const principal = this.host.getPrincipalFromContext(ctx);
57
+ if (!principal) {
58
+ await ctx.answerCallbackQuery({
59
+ text: this.host.t(locale, "common:errors.no_telegram_identity"),
60
+ show_alert: true,
61
+ });
62
+ return;
63
+ }
64
+ const sessionId = await this.host.bindingStore.getActiveSessionIdForPrincipal(principal);
65
+ if (!sessionId) {
66
+ await ctx.answerCallbackQuery({
67
+ text: this.host.t(locale, "common:errors.no_active_session"),
68
+ show_alert: true,
69
+ });
70
+ return;
71
+ }
72
+ const projects = await this.host.listGatewayProjects(principal);
73
+ if (projects.length === 0) {
74
+ await ctx.answerCallbackQuery({
75
+ text: this.host.t(locale, "menu:broadcast.no_projects_first"),
76
+ show_alert: true,
77
+ });
78
+ return;
79
+ }
80
+ const targets = await this.collectCollabBroadcastTargets(principal);
81
+ const totalTargets = targets.localTargetSessionIds.length + targets.remoteTargets.length;
82
+ if (totalTargets === 0) {
83
+ await ctx.answerCallbackQuery({
84
+ text: this.host.t(locale, "menu:broadcast.no_collab_targets"),
85
+ show_alert: true,
86
+ });
87
+ return;
88
+ }
89
+ const principalKey = (0, transportUtils_1.buildPrincipalKey)(principal);
90
+ this.host.pendingRenames.delete(principalKey);
91
+ await ctx.answerCallbackQuery({
92
+ text: this.host.t(locale, "menu:broadcast.collab_begin", { count: totalTargets }),
93
+ });
94
+ const sent = await this.host.replyText(ctx, [
95
+ this.host.t(locale, "menu:broadcast.collab_title"),
96
+ "",
97
+ this.host.t(locale, "menu:broadcast.collab_projects", { count: projects.length }),
98
+ this.host.t(locale, "menu:broadcast.collab_sessions", { count: totalTargets }),
99
+ "",
100
+ this.host.t(locale, "menu:broadcast.collab_body"),
101
+ this.host.t(locale, "menu:broadcast.collab_hint"),
102
+ this.host.t(locale, "menu:broadcast.cancel_hint"),
103
+ ].join("\n"), { kind: "menu", sessionId }, { reply_markup: new grammy_1.InlineKeyboard().text("Cancel", "broadcast-cancel") });
104
+ this.host.pendingBroadcasts.set(principalKey, {
105
+ initiatedAt: new Date().toISOString(),
106
+ scope: "project",
107
+ sessionId,
108
+ localTargetSessionIds: targets.localTargetSessionIds,
109
+ remoteTargets: targets.remoteTargets,
110
+ ...(sent ? { promptMessageId: sent.message_id } : {}),
111
+ ...(ctx.callbackQuery?.message?.message_id
112
+ ? { menuMessageId: ctx.callbackQuery.message.message_id }
113
+ : {}),
114
+ });
115
+ }
116
+ async cancelPendingBroadcast(ctx) {
117
+ const locale = await this.host.resolveLocaleForContext(ctx);
118
+ const principal = this.host.getPrincipalFromContext(ctx);
119
+ if (!principal) {
120
+ await ctx.answerCallbackQuery({
121
+ text: this.host.t(locale, "common:errors.no_telegram_identity"),
122
+ show_alert: true,
123
+ });
124
+ return;
125
+ }
126
+ const principalKey = (0, transportUtils_1.buildPrincipalKey)(principal);
127
+ const pending = this.host.pendingBroadcasts.get(principalKey);
128
+ if (!pending) {
129
+ await ctx.answerCallbackQuery({
130
+ text: this.host.t(locale, "menu:broadcast.mode_not_active"),
131
+ show_alert: true,
132
+ });
133
+ return;
134
+ }
135
+ this.host.pendingBroadcasts.delete(principalKey);
136
+ await this.deletePendingBroadcastArtifacts(ctx, pending, false);
137
+ await ctx.answerCallbackQuery({
138
+ text: this.host.t(locale, "menu:broadcast.cancelled"),
139
+ });
140
+ if (pending.scope === "project") {
141
+ await this.host.showCollabToolsMenu(ctx);
142
+ return;
143
+ }
144
+ await this.host.showDeveloperMenu(ctx);
145
+ }
146
+ async handlePendingBroadcast(ctx, text) {
147
+ const principal = this.host.getPrincipalFromContext(ctx);
148
+ if (!principal) {
149
+ return false;
150
+ }
151
+ const principalKey = (0, transportUtils_1.buildPrincipalKey)(principal);
152
+ const pending = this.host.pendingBroadcasts.get(principalKey);
153
+ if (!pending) {
154
+ return false;
155
+ }
156
+ if (text.startsWith("/")) {
157
+ this.host.pendingBroadcasts.delete(principalKey);
158
+ await this.deletePendingBroadcastArtifacts(ctx, pending, false);
159
+ return false;
160
+ }
161
+ const broadcastText = text.trim();
162
+ if (pending.scope === "linked" &&
163
+ (await this.host.bindingStore.listBoundSessionIdsForPrincipal(principal)).length === 0) {
164
+ this.host.pendingBroadcasts.delete(principalKey);
165
+ await this.deletePendingBroadcastArtifacts(ctx, pending, false);
166
+ await this.host.replyText(ctx, "Broadcast cancelled because no visible sessions were found.", { kind: "menu" });
167
+ return true;
168
+ }
169
+ const receivedAt = new Date(ctx.message?.date ? ctx.message.date * 1000 : Date.now()).toISOString();
170
+ let storedCount = 0;
171
+ let remoteCount = 0;
172
+ if (pending.scope === "project") {
173
+ const parsed = (0, transportFormatting_1.parsePartnerNoteText)(broadcastText);
174
+ const sourceSession = pending.sessionId
175
+ ? await this.host.sessionStore.getSession(pending.sessionId)
176
+ : null;
177
+ const sourceLabel = sourceSession?.label ?? pending.sessionId ?? "session";
178
+ for (const sessionId of pending.localTargetSessionIds ?? []) {
179
+ const session = await this.host.sessionStore.getSession(sessionId);
180
+ await (0, telegramXchangeRecords_1.writeTelegramMessageXchangeRecord)({
181
+ config: this.host.config,
182
+ session,
183
+ sessionId,
184
+ text: [
185
+ "Collab broadcast from Telegram user.",
186
+ `Source session: ${sourceLabel}`,
187
+ `Summary: ${parsed.summary}`,
188
+ "",
189
+ "Message:",
190
+ parsed.message,
191
+ ].join("\n"),
192
+ createdAt: receivedAt,
193
+ summary: parsed.summary,
194
+ tags: ["telegram", "broadcast", "collab", "human"],
195
+ });
196
+ storedCount += 1;
197
+ try {
198
+ this.host.scheduleTerminalNudgeForInboxMessage(sessionId, session);
199
+ }
200
+ catch (error) {
201
+ this.host.logger.error("terminal nudge failed after project broadcast xchange capture", {
202
+ sessionId,
203
+ error: error instanceof Error ? (error.stack ?? error.message) : String(error),
204
+ });
205
+ }
206
+ }
207
+ for (const target of pending.remoteTargets ?? []) {
208
+ await this.host.sendPartnerNote({
209
+ target_session_id: target.sessionUuid,
210
+ kind: "request",
211
+ summary: parsed.summary,
212
+ message: [
213
+ "Collab broadcast from Telegram user.",
214
+ ...(target.projectName ? [`Project: ${target.projectName}`] : []),
215
+ `Source session: ${sourceLabel}`,
216
+ "",
217
+ "Message:",
218
+ parsed.message,
219
+ ].join("\n"),
220
+ requires_reply: false,
221
+ ...(pending.sessionId ? { session_id: pending.sessionId } : {}),
222
+ ...(target.projectUuid ? { project_uuid: target.projectUuid } : {}),
223
+ });
224
+ remoteCount += 1;
225
+ }
226
+ }
227
+ else {
228
+ const sessionIds = await this.host.bindingStore.listBoundSessionIdsForPrincipal(principal);
229
+ for (const sessionId of sessionIds) {
230
+ const relayTarget = (0, relay_1.parseLiveRelaySessionId)(sessionId);
231
+ if (relayTarget) {
232
+ try {
233
+ await this.host.routeTelegramInboxToRelaySession({
234
+ ctx,
235
+ principal,
236
+ relayTarget,
237
+ sourceSessionId: sessionId,
238
+ messageText: broadcastText,
239
+ attachments: [],
240
+ });
241
+ remoteCount += 1;
242
+ this.host.logger.info("Telegram broadcast routed to gateway relay session", {
243
+ sessionId,
244
+ chatId: principal.telegramChatId,
245
+ userId: principal.telegramUserId,
246
+ targetClientUuid: relayTarget.clientUuid,
247
+ targetLocalSessionId: relayTarget.localSessionId,
248
+ text: (0, redactSecrets_1.redactSecrets)(broadcastText),
249
+ });
250
+ continue;
251
+ }
252
+ catch (error) {
253
+ this.host.logger.error("Failed to route Telegram broadcast to gateway relay session", {
254
+ sessionId,
255
+ chatId: principal.telegramChatId,
256
+ userId: principal.telegramUserId,
257
+ targetClientUuid: relayTarget.clientUuid,
258
+ targetLocalSessionId: relayTarget.localSessionId,
259
+ error: error instanceof Error ? (error.stack ?? error.message) : String(error),
260
+ });
261
+ continue;
262
+ }
263
+ }
264
+ const session = await this.host.sessionStore.getSession(sessionId);
265
+ await (0, telegramXchangeRecords_1.writeTelegramMessageXchangeRecord)({
266
+ config: this.host.config,
267
+ session,
268
+ sessionId,
269
+ text: broadcastText,
270
+ createdAt: receivedAt,
271
+ summary: (0, transportFormatting_1.parsePartnerNoteText)(broadcastText).summary,
272
+ tags: ["telegram", "broadcast", "human"],
273
+ });
274
+ storedCount += 1;
275
+ this.host.logger.info("Telegram broadcast message stored in xchange", {
276
+ sessionId,
277
+ chatId: principal.telegramChatId,
278
+ userId: principal.telegramUserId,
279
+ text: (0, redactSecrets_1.redactSecrets)(broadcastText),
280
+ });
281
+ try {
282
+ this.host.scheduleTerminalNudgeForInboxMessage(sessionId, session);
283
+ }
284
+ catch (error) {
285
+ this.host.logger.error("terminal nudge failed after broadcast xchange capture", {
286
+ sessionId,
287
+ error: error instanceof Error ? (error.stack ?? error.message) : String(error),
288
+ });
289
+ }
290
+ }
291
+ }
292
+ this.host.pendingBroadcasts.delete(principalKey);
293
+ await this.deletePendingBroadcastArtifacts(ctx, pending, false);
294
+ this.host.logger.info("Telegram broadcast completed", {
295
+ chatId: principal.telegramChatId,
296
+ userId: principal.telegramUserId,
297
+ scope: pending.scope,
298
+ storedCount,
299
+ remoteCount,
300
+ sessionCount: storedCount + remoteCount,
301
+ initiatedAt: pending.initiatedAt,
302
+ text: (0, redactSecrets_1.redactSecrets)(broadcastText),
303
+ });
304
+ const locale = await this.host.resolveLocaleForContext(ctx);
305
+ await this.host.replyText(ctx, pending.scope === "project"
306
+ ? [
307
+ this.host.t(locale, "menu:broadcast.completed_collab", {
308
+ count: storedCount + remoteCount,
309
+ }),
310
+ this.host.t(locale, "menu:broadcast.completed_collab_local", { count: storedCount }),
311
+ this.host.t(locale, "menu:broadcast.completed_collab_remote", { count: remoteCount }),
312
+ this.host.t(locale, "menu:broadcast.completed_collab_total", {
313
+ count: storedCount + remoteCount,
314
+ }),
315
+ ].join("\n")
316
+ : await this.host.tForContext(ctx, "menu:broadcast.completed_linked", {
317
+ count: storedCount + remoteCount,
318
+ }), { kind: "menu", ...(pending.sessionId ? { sessionId: pending.sessionId } : {}) });
319
+ return true;
320
+ }
321
+ async collectCollabBroadcastTargets(principal) {
322
+ const currentClientUuid = await this.host.ensureGatewayClientUuid(principal);
323
+ const projects = await this.host.listGatewayProjects(principal);
324
+ const visibleLocalSessionIds = new Set(await this.host.bindingStore.listBoundSessionIdsForPrincipal(principal));
325
+ const localTargetSessionIds = [];
326
+ const remoteTargets = [];
327
+ const seenLogicalTargets = new Set();
328
+ for (const project of projects) {
329
+ const projectSessions = await this.host.listGatewayProjectSessions(principal, project.project_uuid);
330
+ for (const item of projectSessions) {
331
+ const logicalTargetKey = `${item.client_uuid}:${item.local_session_id}`;
332
+ if (seenLogicalTargets.has(logicalTargetKey)) {
333
+ continue;
334
+ }
335
+ seenLogicalTargets.add(logicalTargetKey);
336
+ const isVisibleLocalSession = item.client_uuid === currentClientUuid &&
337
+ visibleLocalSessionIds.has(item.local_session_id);
338
+ if (isVisibleLocalSession) {
339
+ localTargetSessionIds.push(item.local_session_id);
340
+ continue;
341
+ }
342
+ remoteTargets.push({
343
+ sessionUuid: item.session_uuid,
344
+ sessionLabel: item.label?.trim() || item.local_session_id,
345
+ clientUuid: item.client_uuid,
346
+ localSessionId: item.local_session_id,
347
+ projectUuid: item.project_uuid,
348
+ ...(project.name ? { projectName: project.name } : {}),
349
+ });
350
+ }
351
+ }
352
+ return {
353
+ localTargetSessionIds: [...new Set(localTargetSessionIds)].sort(),
354
+ remoteTargets,
355
+ };
356
+ }
357
+ async listCollabBroadcastTargets(principal) {
358
+ return this.collectCollabBroadcastTargets(principal);
359
+ }
360
+ async deletePendingBroadcastArtifacts(ctx, pending, deleteMenuMessage) {
361
+ const chatId = ctx.chat?.id;
362
+ if (!chatId) {
363
+ return;
364
+ }
365
+ for (const messageId of [
366
+ pending.promptMessageId,
367
+ ...(deleteMenuMessage ? [pending.menuMessageId] : []),
368
+ ]) {
369
+ if (!messageId) {
370
+ continue;
371
+ }
372
+ try {
373
+ await this.host.deleteMessage(chatId, messageId);
374
+ }
375
+ catch (error) {
376
+ this.host.logger.warn("Failed to delete pending broadcast menu artifact", {
377
+ chatId,
378
+ messageId,
379
+ error: error instanceof Error ? (error.stack ?? error.message) : String(error),
380
+ });
381
+ }
382
+ }
383
+ }
384
+ }
385
+ exports.TransportBroadcastActions = TransportBroadcastActions;
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TransportConsoleRegistry = void 0;
4
+ const relay_1 = require("../../../app/webapp/relay");
5
+ class TransportConsoleRegistry {
6
+ host;
7
+ constructor(host) {
8
+ this.host = host;
9
+ }
10
+ labelSeparator = " · ";
11
+ async clearStaleRelaySessions(input) {
12
+ for (const sessionId of input.existingBound) {
13
+ if (!sessionId.startsWith("relay~")) {
14
+ continue;
15
+ }
16
+ if (input.liveRelaySessionIds.has(sessionId)) {
17
+ continue;
18
+ }
19
+ await this.host.bindingStore.clearBinding(sessionId);
20
+ await this.host.sessionStore.clearSession(sessionId);
21
+ }
22
+ }
23
+ async listScopedConsoles(input) {
24
+ const response = await this.host.callGatewayJson("/sessions/known", {
25
+ connected_only: true,
26
+ ...(input?.principal
27
+ ? { telegram_user_id: input.principal.telegramUserId }
28
+ : {}),
29
+ ...(this.host.config.distributed.gatewayToken
30
+ ? { gateway_token: this.host.config.distributed.gatewayToken }
31
+ : {}),
32
+ });
33
+ return Array.isArray(response.sessions) ? response.sessions : [];
34
+ }
35
+ async ensureScopedConsolesBound(input) {
36
+ const existingBound = new Set(await this.host.bindingStore.listBoundSessionIdsForPrincipal(input.principal));
37
+ const consoles = await this.listScopedConsoles({
38
+ principal: input.principal,
39
+ });
40
+ const liveRelaySessionIds = new Set(consoles.map((consoleSession) => (0, relay_1.buildLiveRelaySessionId)(consoleSession.client_uuid, consoleSession.local_session_id)));
41
+ await this.clearStaleRelaySessions({
42
+ existingBound,
43
+ liveRelaySessionIds,
44
+ });
45
+ if (liveRelaySessionIds.size === 0) {
46
+ const nextActive = await this.host.bindingStore.getActiveSessionIdForPrincipal(input.principal);
47
+ return {
48
+ sessionIds: [],
49
+ activeSessionId: nextActive,
50
+ };
51
+ }
52
+ const relaySessionIds = [];
53
+ for (const consoleSession of consoles) {
54
+ const relaySessionId = (0, relay_1.buildLiveRelaySessionId)(consoleSession.client_uuid, consoleSession.local_session_id);
55
+ relaySessionIds.push(relaySessionId);
56
+ await this.materializeRelaySession(input.ctx
57
+ ? {
58
+ ctx: input.ctx,
59
+ principal: input.principal,
60
+ relaySessionId,
61
+ consoleSession,
62
+ }
63
+ : {
64
+ principal: input.principal,
65
+ relaySessionId,
66
+ consoleSession,
67
+ });
68
+ }
69
+ const existingActive = await this.host.bindingStore.getActiveSessionIdForPrincipal(input.principal);
70
+ const nextBound = new Set(await this.host.bindingStore.listBoundSessionIdsForPrincipal(input.principal));
71
+ const nextActive = existingActive && nextBound.has(existingActive)
72
+ ? existingActive
73
+ : relaySessionIds[0] ?? existingActive ?? null;
74
+ if (nextActive) {
75
+ await this.host.bindingStore.setActiveSessionIdForPrincipal(input.principal, nextActive);
76
+ }
77
+ this.host.logger.info("Gateway scoped consoles synchronized for principal", {
78
+ chatId: input.principal.telegramChatId,
79
+ userId: input.principal.telegramUserId,
80
+ consoleCount: relaySessionIds.length,
81
+ activeSessionId: nextActive,
82
+ });
83
+ return {
84
+ sessionIds: relaySessionIds,
85
+ activeSessionId: nextActive,
86
+ };
87
+ }
88
+ async materializeRelaySession(input) {
89
+ const existingSession = await this.host.sessionStore.getSession(input.relaySessionId);
90
+ const label = this.buildConsoleLabel(input.consoleSession);
91
+ const projectName = input.consoleSession.project_names[0] ?? undefined;
92
+ const nextSession = {
93
+ sessionId: input.relaySessionId,
94
+ label,
95
+ activeProjectUuid: input.consoleSession.project_uuids[0] ?? undefined,
96
+ activeProjectName: projectName ?? undefined,
97
+ ...(input.consoleSession.cwd?.trim()
98
+ ? { cwd: input.consoleSession.cwd.trim() }
99
+ : {}),
100
+ updatedAt: new Date().toISOString(),
101
+ };
102
+ await this.host.sessionStore.setSession({
103
+ ...(existingSession ?? nextSession),
104
+ ...nextSession,
105
+ ...(nextSession.cwd
106
+ ? { cwd: nextSession.cwd }
107
+ : existingSession?.cwd
108
+ ? { cwd: existingSession.cwd }
109
+ : {}),
110
+ ...(existingSession?.task ? { task: existingSession.task } : {}),
111
+ ...(existingSession?.summary ? { summary: existingSession.summary } : {}),
112
+ ...(existingSession?.files ? { files: existingSession.files } : {}),
113
+ ...(existingSession?.decisions
114
+ ? { decisions: existingSession.decisions }
115
+ : {}),
116
+ ...(existingSession?.risks ? { risks: existingSession.risks } : {}),
117
+ ...(existingSession?.lastSeenToolsHash
118
+ ? { lastSeenToolsHash: existingSession.lastSeenToolsHash }
119
+ : {}),
120
+ ...(existingSession?.lastNotifiedToolsHash
121
+ ? { lastNotifiedToolsHash: existingSession.lastNotifiedToolsHash }
122
+ : {}),
123
+ });
124
+ await this.host.bindingStore.setBinding({
125
+ sessionId: input.relaySessionId,
126
+ telegramChatId: input.principal.telegramChatId,
127
+ telegramUserId: input.principal.telegramUserId,
128
+ ...(input.ctx?.from?.username
129
+ ? { telegramUsername: input.ctx.from.username }
130
+ : {}),
131
+ linkedAt: new Date().toISOString(),
132
+ });
133
+ return nextSession;
134
+ }
135
+ buildConsoleLabel(session) {
136
+ const base = session.session_label?.trim() || session.local_session_id.trim() || "console";
137
+ const owner = session.telegram_display_name?.trim() ||
138
+ session.telegram_username?.trim() ||
139
+ session.system_username?.trim() ||
140
+ session.client_label?.trim() ||
141
+ session.node_id?.trim() ||
142
+ session.client_uuid.slice(0, 8);
143
+ if (base.includes(this.labelSeparator)) {
144
+ return base;
145
+ }
146
+ return owner ? `${base} · ${owner}` : base;
147
+ }
148
+ }
149
+ exports.TransportConsoleRegistry = TransportConsoleRegistry;