@deadragdoll/tellymcp 0.0.10 → 0.0.11

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 (153) 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 +205 -384
  5. package/README.md +195 -1194
  6. package/TOOLS.md +294 -377
  7. package/VERSION.md +11 -11
  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 +897 -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 +116 -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/browserScreenshotTool.js +1 -1
  42. package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +249 -19
  43. package/dist/services/features/telegram-mcp/src/features/collaboration/model/gatewaySessionsService.js +71 -0
  44. package/dist/services/features/telegram-mcp/src/features/collaboration/model/listGatewaySessionsTool.js +33 -0
  45. package/dist/services/features/telegram-mcp/src/features/collaboration/model/localCollaborationBackend.js +93 -82
  46. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +47 -5
  47. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileTool.js +1 -1
  48. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerNoteTool.js +1 -1
  49. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +82 -0
  50. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +22 -14
  51. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +649 -75
  52. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/remoteConsoleActionClient.js +76 -0
  53. package/dist/services/features/telegram-mcp/src/features/embedded-runtime/model/embeddedRuntimeBroker.js +92 -0
  54. package/dist/services/features/telegram-mcp/src/features/foreground-terminal/model/foregroundTerminalRuntime.js +192 -0
  55. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +382 -3
  56. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyTelegramTool.js +1 -1
  57. package/dist/services/features/telegram-mcp/src/features/notify/model/sendFileToTelegramTool.js +33 -0
  58. package/dist/services/features/telegram-mcp/src/features/session-context/model/clearSessionContextTool.js +1 -1
  59. package/dist/services/features/telegram-mcp/src/features/session-context/model/getSessionContextTool.js +1 -1
  60. package/dist/services/features/telegram-mcp/src/features/session-context/model/renameSessionTool.js +1 -1
  61. package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +42 -200
  62. package/dist/services/features/telegram-mcp/src/features/session-context/model/setSessionContextTool.js +1 -1
  63. package/dist/services/features/telegram-mcp/src/features/terminal-buffer/model/terminalBufferService.js +96 -0
  64. package/dist/services/features/telegram-mcp/src/features/terminal-input/model/terminalInputService.js +97 -0
  65. package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownService.js +107 -58
  66. package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownTool.js +1 -1
  67. package/dist/services/features/telegram-mcp/src/features/xchange/model/getXchangeRecordTool.js +28 -0
  68. package/dist/services/features/telegram-mcp/src/features/xchange/model/listXchangeRecordsTool.js +28 -0
  69. package/dist/services/features/telegram-mcp/src/features/xchange/model/markXchangeRecordReadTool.js +28 -0
  70. package/dist/services/features/telegram-mcp/src/features/xchange/model/xchangeService.js +169 -0
  71. package/dist/services/features/telegram-mcp/src/processes/human-approval/model/orchestrator.js +5 -4
  72. package/dist/services/features/telegram-mcp/src/shared/i18n/resources/en.js +139 -42
  73. package/dist/services/features/telegram-mcp/src/shared/i18n/resources/ru.js +135 -38
  74. package/dist/services/features/telegram-mcp/src/shared/integrations/memory/processLocalSessionStore.js +27 -0
  75. package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +11 -8
  76. package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +42 -71
  77. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/proxyFetch.js +21 -22
  78. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +415 -7101
  79. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportAttachmentStore.js +93 -0
  80. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportBroadcastActions.js +385 -0
  81. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConsoleRegistry.js +143 -0
  82. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConstructorWiring.js +633 -0
  83. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContent.js +84 -0
  84. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContext.js +78 -0
  85. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportDocumentActions.js +36 -0
  86. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportEventActions.js +292 -0
  87. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +352 -0
  88. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFormatting.js +75 -0
  89. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportGatewayActions.js +44 -0
  90. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLifecycleActions.js +161 -0
  91. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLive.js +56 -0
  92. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLiveActions.js +77 -0
  93. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuCallbacks.js +254 -0
  94. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFactories.js +538 -0
  95. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFingerprints.js +93 -0
  96. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFlow.js +344 -0
  97. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuShell.js +62 -0
  98. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuState.js +408 -0
  99. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuText.js +216 -0
  100. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMessageFlow.js +452 -0
  101. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportOutputActions.js +189 -0
  102. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPartnerActions.js +286 -0
  103. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPayloadState.js +101 -0
  104. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectActions.js +463 -0
  105. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEntryActions.js +202 -0
  106. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEvents.js +99 -0
  107. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectMenus.js +138 -0
  108. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectState.js +308 -0
  109. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectView.js +426 -0
  110. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportRequestFlow.js +278 -0
  111. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportSessionActions.js +143 -0
  112. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalActions.js +468 -0
  113. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalRuntime.js +171 -0
  114. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTypes.js +2 -0
  115. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportUtils.js +330 -0
  116. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportXchangeState.js +107 -0
  117. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +255 -0
  118. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +543 -0
  119. package/dist/services/features/telegram-mcp/src/shared/integrations/xchange/sqliteRecordStore.js +223 -0
  120. package/dist/services/features/telegram-mcp/src/shared/lib/gatewayScope.js +24 -0
  121. package/dist/services/features/telegram-mcp/src/shared/lib/logger/logger.js +6 -0
  122. package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +147 -76
  123. package/dist/services/features/telegram-mcp/src/shared/lib/telegramXchangeRecords.js +72 -0
  124. package/dist/services/features/telegram-mcp/src/shared/lib/terminalPromptDetection.js +237 -0
  125. package/dist/services/features/telegram-mcp/src/shared/lib/version/versionHandshake.js +129 -1
  126. package/dist/services/features/telegram-mcp/src/shared/lib/xchangeRecordHints.js +98 -0
  127. package/dist/services/features/telegram-mcp/standalone-http.service.js +12 -0
  128. package/dist/services/features/telegram-mcp/terminal-buffer.service.js +42 -0
  129. package/dist/services/features/telegram-mcp/terminal-input.service.js +41 -0
  130. package/dist/services/features/telegram-mcp/tools-sync.service.js +15 -1
  131. package/dist/services/features/telegram-mcp/xchange.service.js +123 -0
  132. package/docs/STANDALONE-ru.md +172 -0
  133. package/docs/STANDALONE.md +172 -0
  134. package/package.json +10 -5
  135. package/scripts/build-package-artifact.sh +27 -0
  136. package/scripts/deploy-gateway.sh +64 -0
  137. package/scripts/deploy-onebot-branch.sh +27 -0
  138. package/scripts/postinstall.js +11 -36
  139. package/STANDALONE-ru.md +0 -274
  140. package/STANDALONE.md +0 -274
  141. package/dist/services/features/telegram-mcp/inbox.service.js +0 -33
  142. package/dist/services/features/telegram-mcp/pair.service.js +0 -33
  143. package/dist/services/features/telegram-mcp/src/app/webapp/tmux.js +0 -10
  144. package/dist/services/features/telegram-mcp/src/features/inbox/model/deleteTelegramInboxMessageTool.js +0 -33
  145. package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxCountTool.js +0 -33
  146. package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxTool.js +0 -33
  147. package/dist/services/features/telegram-mcp/src/features/inbox/model/inboxService.js +0 -77
  148. package/dist/services/features/telegram-mcp/src/features/pair-session/model/clearSessionPairingTool.js +0 -33
  149. package/dist/services/features/telegram-mcp/src/features/pair-session/model/createSessionPairCodeTool.js +0 -33
  150. package/dist/services/features/telegram-mcp/src/features/pair-session/model/generatePairCode.js +0 -202
  151. package/dist/services/features/telegram-mcp/src/features/session-context/model/getTmuxTargetTool.js +0 -33
  152. package/dist/services/features/telegram-mcp/src/features/session-context/model/setTmuxTargetTool.js +0 -33
  153. package/dist/services/features/telegram-mcp/src/shared/integrations/tmux/client.js +0 -363
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WEBAPP_APP_JS = exports.WEBAPP_STYLES_CSS = void 0;
4
4
  exports.renderWebAppHtml = renderWebAppHtml;
5
+ const node_fs_1 = require("node:fs");
6
+ const XTERM_WEBAPP_CSS = (0, node_fs_1.readFileSync)(require.resolve("@xterm/xterm/css/xterm.css"), "utf8");
7
+ const XTERM_WEBAPP_JS = (0, node_fs_1.readFileSync)(require.resolve("@xterm/xterm/lib/xterm.js"), "utf8");
5
8
  exports.WEBAPP_STYLES_CSS = `
6
9
  :root {
7
10
  color-scheme: dark;
@@ -22,9 +25,10 @@ exports.WEBAPP_STYLES_CSS = `
22
25
 
23
26
  html, body {
24
27
  margin: 0;
25
- min-height: 100%;
28
+ height: 100%;
26
29
  background: linear-gradient(180deg, #121620 0%, #0d1017 100%);
27
30
  color: var(--text);
31
+ overflow: hidden;
28
32
  }
29
33
 
30
34
  body {
@@ -32,15 +36,14 @@ body {
32
36
  }
33
37
 
34
38
  .app {
35
- min-height: 100vh;
39
+ height: 100dvh;
40
+ display: grid;
41
+ grid-template-rows: 1fr auto auto;
42
+ overflow: hidden;
36
43
  }
37
44
 
38
45
  .toolbar {
39
- position: fixed;
40
- left: 0;
41
- right: 0;
42
- bottom: calc(42px + env(safe-area-inset-bottom, 0px));
43
- z-index: 30;
46
+ grid-row: 2;
44
47
  display: flex;
45
48
  justify-content: flex-start;
46
49
  flex-wrap: wrap;
@@ -116,11 +119,7 @@ body {
116
119
  }
117
120
 
118
121
  .statusbar {
119
- position: fixed;
120
- left: 0;
121
- right: 0;
122
- bottom: 0;
123
- z-index: 25;
122
+ grid-row: 3;
124
123
  display: flex;
125
124
  flex-wrap: wrap;
126
125
  align-items: center;
@@ -173,15 +172,37 @@ body {
173
172
  }
174
173
 
175
174
  .terminal {
175
+ grid-row: 1;
176
176
  margin: 0;
177
- padding: 18px 14px calc(122px + env(safe-area-inset-bottom, 0px)) 14px;
178
- min-height: 100vh;
179
- overflow: auto;
177
+ min-height: 0;
178
+ padding: 18px 14px;
179
+ overflow: hidden;
180
180
  white-space: pre-wrap;
181
181
  word-break: break-word;
182
182
  font: 13px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
183
183
  }
184
184
 
185
+ .terminal.xterm-host {
186
+ position: relative;
187
+ min-width: 0;
188
+ padding: 0;
189
+ overflow-x: auto;
190
+ overflow-y: hidden;
191
+ }
192
+
193
+ .terminal.xterm-host .xterm {
194
+ height: 100%;
195
+ width: max-content;
196
+ min-width: 100%;
197
+ padding: 0;
198
+ }
199
+
200
+ .terminal.xterm-host .xterm-viewport {
201
+ overflow-x: hidden !important;
202
+ overflow-y: auto !important;
203
+ -webkit-overflow-scrolling: touch !important;
204
+ }
205
+
185
206
  .terminal.unwrap {
186
207
  white-space: pre;
187
208
  word-break: normal;
@@ -203,7 +224,6 @@ body {
203
224
 
204
225
  @media (max-width: 680px) {
205
226
  .toolbar {
206
- bottom: calc(46px + env(safe-area-inset-bottom, 0px));
207
227
  gap: 6px;
208
228
  padding: 8px 10px;
209
229
  }
@@ -223,7 +243,11 @@ body {
223
243
  }
224
244
 
225
245
  .terminal {
226
- padding: 14px 12px calc(146px + env(safe-area-inset-bottom, 0px)) 12px;
246
+ padding: 14px 12px;
247
+ }
248
+
249
+ .terminal.xterm-host .xterm {
250
+ padding: 0;
227
251
  }
228
252
  }
229
253
  `;
@@ -239,6 +263,13 @@ const state = {
239
263
  pollIntervalMs: 2000,
240
264
  wrapEnabled: true,
241
265
  recoverPromise: null,
266
+ xterm: null,
267
+ xtermInputBound: false,
268
+ liveSocket: null,
269
+ liveSocketConnected: false,
270
+ lastRenderSignature: null,
271
+ resizeObserver: null,
272
+ fittedRows: null,
242
273
  };
243
274
 
244
275
  const elements = {
@@ -270,6 +301,10 @@ function setUpdated(text) {
270
301
  elements.updated.textContent = text;
271
302
  }
272
303
 
304
+ function setLiveModeStatus(mode) {
305
+ setStatus(mode === "stream" ? "Live (stream)" : "Live (poll)");
306
+ }
307
+
273
308
  function createHttpError(message, status) {
274
309
  const error = new Error(message);
275
310
  error.status = status;
@@ -284,7 +319,7 @@ function shouldRecoverWebAppSession(error) {
284
319
  );
285
320
  }
286
321
 
287
- function applyTmuxAvailability(hasTarget) {
322
+ function applyTerminalAvailability(hasTarget) {
288
323
  elements.interrupt.disabled = !hasTarget;
289
324
  elements.type.disabled = !hasTarget;
290
325
  elements.esc.disabled = !hasTarget;
@@ -333,6 +368,187 @@ function toggleWrapMode() {
333
368
  }
334
369
  }
335
370
 
371
+ function getXtermCtor() {
372
+ return window.Terminal || window.XtermTerminal || null;
373
+ }
374
+
375
+ function getTerminalCellHeight(terminal) {
376
+ const cell =
377
+ terminal &&
378
+ terminal._core &&
379
+ terminal._core._renderService &&
380
+ terminal._core._renderService.dimensions &&
381
+ terminal._core._renderService.dimensions.css &&
382
+ terminal._core._renderService.dimensions.css.cell;
383
+
384
+ if (!cell || !cell.height) {
385
+ return null;
386
+ }
387
+
388
+ return cell.height;
389
+ }
390
+
391
+ function fitTerminalRows(notifyServer = true) {
392
+ const terminal = state.xterm;
393
+ if (!terminal || !elements.terminal) {
394
+ return;
395
+ }
396
+
397
+ const cellHeight = getTerminalCellHeight(terminal);
398
+ if (!cellHeight) {
399
+ return;
400
+ }
401
+
402
+ const nextRows = Math.max(5, Math.floor(elements.terminal.clientHeight / cellHeight));
403
+ if (!Number.isFinite(nextRows) || nextRows <= 0) {
404
+ return;
405
+ }
406
+
407
+ if (state.fittedRows === nextRows) {
408
+ return;
409
+ }
410
+
411
+ state.fittedRows = nextRows;
412
+ const currentCols =
413
+ typeof terminal.cols === "number" && terminal.cols > 0 ? terminal.cols : 80;
414
+ terminal.resize(currentCols, nextRows);
415
+
416
+ if (
417
+ notifyServer &&
418
+ state.liveSocketConnected &&
419
+ state.liveSocket &&
420
+ state.liveSocket.readyState === WebSocket.OPEN
421
+ ) {
422
+ state.liveSocket.send(
423
+ JSON.stringify({ type: "resize", cols: currentCols, rows: nextRows }),
424
+ );
425
+ }
426
+ }
427
+
428
+ function ensureXterm() {
429
+ if (state.xterm) {
430
+ return state.xterm;
431
+ }
432
+
433
+ const TerminalCtor = getXtermCtor();
434
+ if (!TerminalCtor || !elements.terminal) {
435
+ return null;
436
+ }
437
+
438
+ elements.terminal.classList.add("xterm-host");
439
+ elements.terminal.textContent = "";
440
+
441
+ const terminal = new TerminalCtor({
442
+ convertEol: true,
443
+ disableStdin: false,
444
+ cursorBlink: false,
445
+ theme: {
446
+ background: "#0f1115",
447
+ foreground: "#edf1f7",
448
+ },
449
+ fontFamily:
450
+ 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',
451
+ fontSize: 13,
452
+ lineHeight: 1,
453
+ allowTransparency: true,
454
+ scrollback: 4000,
455
+ });
456
+ terminal.open(elements.terminal);
457
+ terminal.onData((data) => {
458
+ if (!state.liveSocketConnected || !state.liveSocket) {
459
+ return;
460
+ }
461
+ if (state.liveSocket.readyState !== WebSocket.OPEN) {
462
+ return;
463
+ }
464
+ state.liveSocket.send(JSON.stringify({ type: "input", data }));
465
+ });
466
+ elements.terminal.addEventListener("pointerdown", (event) => {
467
+ if (event.pointerType === "mouse") {
468
+ terminal.focus();
469
+ }
470
+ });
471
+ if (!state.resizeObserver && typeof ResizeObserver !== "undefined") {
472
+ state.resizeObserver = new ResizeObserver(() => {
473
+ fitTerminalRows();
474
+ });
475
+ state.resizeObserver.observe(elements.terminal);
476
+ }
477
+ window.addEventListener("resize", () => {
478
+ fitTerminalRows();
479
+ });
480
+ state.xterm = terminal;
481
+ window.setTimeout(() => {
482
+ fitTerminalRows(false);
483
+ }, 0);
484
+ return terminal;
485
+ }
486
+
487
+ function estimateTerminalSizeFromPayload(payload, options = {}) {
488
+ const text = payload.ansi || payload.content || "";
489
+ const lines = text.split("\\n");
490
+ const useViewportRows =
491
+ options.preferViewportRows === true &&
492
+ Number.isFinite(payload.rows) &&
493
+ payload.rows > 0;
494
+ const rows = useViewportRows
495
+ ? Math.max(2, Math.min(120, payload.rows))
496
+ : Math.max(2, Math.min(400, lines.length || payload.rows || 24));
497
+ const baseCols = Number.isFinite(payload.cols) && payload.cols > 0
498
+ ? payload.cols
499
+ : 40;
500
+ const cols = Math.max(
501
+ baseCols,
502
+ Math.min(
503
+ 240,
504
+ Number.isFinite(payload.cols) && payload.cols > 0
505
+ ? payload.cols
506
+ : lines.reduce((max, line) => Math.max(max, line.replace(/\\u001b\\[[0-9;]*m/g, "").length), 0) || 80,
507
+ ),
508
+ );
509
+ return { cols, rows };
510
+ }
511
+
512
+ function renderTerminalPayload(payload, options = {}) {
513
+ const signature = JSON.stringify({
514
+ ansi: payload.ansi || "",
515
+ content: payload.content || "",
516
+ cols: payload.cols || null,
517
+ rows: payload.rows || null,
518
+ preferViewportRows: options.preferViewportRows === true,
519
+ });
520
+ if (state.lastRenderSignature === signature) {
521
+ return;
522
+ }
523
+
524
+ const terminal = ensureXterm();
525
+ if (!terminal) {
526
+ elements.terminal.innerHTML =
527
+ payload.html || renderAnsiToHtml(payload.content || "");
528
+ state.lastRenderSignature = signature;
529
+ return;
530
+ }
531
+
532
+ const size = estimateTerminalSizeFromPayload(payload, options);
533
+ const previousViewportY = terminal.buffer.active.viewportY;
534
+ const previousBaseY = terminal.buffer.active.baseY;
535
+ const wasNearBottom =
536
+ previousBaseY <= 0 ||
537
+ previousViewportY >= Math.max(0, previousBaseY - 2);
538
+ terminal.reset();
539
+ terminal.resize(size.cols, size.rows);
540
+ terminal.write(payload.ansi || payload.content || "");
541
+ if (options.forceScrollBottom === true || wasNearBottom) {
542
+ terminal.scrollToBottom();
543
+ } else {
544
+ terminal.scrollToLine(Math.min(previousViewportY, terminal.buffer.active.baseY));
545
+ }
546
+ window.setTimeout(() => {
547
+ fitTerminalRows(false);
548
+ }, 0);
549
+ state.lastRenderSignature = signature;
550
+ }
551
+
336
552
  function formatCapturedAt(value) {
337
553
  if (!value) {
338
554
  return "never";
@@ -677,9 +893,9 @@ function applyBootstrapPayload(bootstrapPayload) {
677
893
  bootstrapPayload.session_label || bootstrapPayload.session_id;
678
894
  elements.session.hidden = false;
679
895
 
680
- const hasTmuxTarget = Boolean(bootstrapPayload.tmux_target);
681
- applyTmuxAvailability(hasTmuxTarget);
682
- setStatus(hasTmuxTarget ? "Live" : "No tmux target", !hasTmuxTarget);
896
+ const hasTerminalTarget = Boolean(bootstrapPayload.terminal_target);
897
+ applyTerminalAvailability(hasTerminalTarget);
898
+ setStatus(hasTerminalTarget ? "Live" : "No terminal target", !hasTerminalTarget);
683
899
  }
684
900
 
685
901
  async function recoverWebAppSession() {
@@ -736,21 +952,24 @@ async function sendAction(action) {
736
952
  state.actionBusy = true;
737
953
  try {
738
954
  await withRecoveredSession(async () => {
739
- const response = await fetch(config.basePath + "/api/action", {
740
- method: "POST",
741
- headers: {
742
- "content-type": "application/json",
743
- authorization: "Bearer " + state.token,
744
- },
745
- body: JSON.stringify({ action }),
746
- });
747
-
748
- if (!response.ok) {
749
- const text = await response.text();
750
- throw createHttpError(text || "Failed to send action.", response.status);
955
+ if (state.liveSocketConnected && state.liveSocket?.readyState === WebSocket.OPEN) {
956
+ state.liveSocket.send(JSON.stringify({ type: "action", action }));
957
+ } else {
958
+ const response = await fetch(config.basePath + "/api/action", {
959
+ method: "POST",
960
+ headers: {
961
+ "content-type": "application/json",
962
+ authorization: "Bearer " + state.token,
963
+ },
964
+ body: JSON.stringify({ action }),
965
+ });
966
+
967
+ if (!response.ok) {
968
+ const text = await response.text();
969
+ throw createHttpError(text || "Failed to send action.", response.status);
970
+ }
751
971
  }
752
972
  });
753
- await withRecoveredSession(refreshVisibleBuffer);
754
973
  } finally {
755
974
  state.actionBusy = false;
756
975
  }
@@ -764,47 +983,147 @@ async function sendTextInput(text) {
764
983
  state.actionBusy = true;
765
984
  try {
766
985
  await withRecoveredSession(async () => {
767
- const response = await fetch(config.basePath + "/api/action", {
768
- method: "POST",
769
- headers: {
770
- "content-type": "application/json",
771
- authorization: "Bearer " + state.token,
772
- },
773
- body: JSON.stringify({ action: "text", text }),
774
- });
775
-
776
- if (!response.ok) {
777
- const textResponse = await response.text();
778
- throw createHttpError(
779
- textResponse || "Failed to send text.",
780
- response.status,
781
- );
986
+ if (state.liveSocketConnected && state.liveSocket?.readyState === WebSocket.OPEN) {
987
+ state.liveSocket.send(JSON.stringify({ type: "input", data: text }));
988
+ } else {
989
+ const response = await fetch(config.basePath + "/api/action", {
990
+ method: "POST",
991
+ headers: {
992
+ "content-type": "application/json",
993
+ authorization: "Bearer " + state.token,
994
+ },
995
+ body: JSON.stringify({ action: "text", text }),
996
+ });
997
+
998
+ if (!response.ok) {
999
+ const textResponse = await response.text();
1000
+ throw createHttpError(
1001
+ textResponse || "Failed to send text.",
1002
+ response.status,
1003
+ );
1004
+ }
782
1005
  }
783
1006
  });
784
1007
  setStatus("Text sent");
785
- await withRecoveredSession(refreshVisibleBuffer);
786
1008
  } finally {
787
1009
  state.actionBusy = false;
788
1010
  }
789
1011
  }
790
1012
 
1013
+ function getLiveWsUrl() {
1014
+ const liveWsPath =
1015
+ typeof config.liveWsPath === "string" && config.liveWsPath.trim()
1016
+ ? config.liveWsPath.trim()
1017
+ : config.basePath + "/api/live/ws";
1018
+ const url = new URL(liveWsPath, window.location.href);
1019
+ if (state.token) {
1020
+ url.searchParams.set("token", state.token);
1021
+ }
1022
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
1023
+ return url.toString();
1024
+ }
1025
+
1026
+ function closeLiveSocket() {
1027
+ if (!state.liveSocket) {
1028
+ return;
1029
+ }
1030
+ const socket = state.liveSocket;
1031
+ state.liveSocket = null;
1032
+ state.liveSocketConnected = false;
1033
+ try {
1034
+ socket.close();
1035
+ } catch (_error) {
1036
+ }
1037
+ }
1038
+
1039
+ async function connectLiveSocket() {
1040
+ closeLiveSocket();
1041
+ if (!state.token) {
1042
+ throw new Error("WebApp token is missing");
1043
+ }
1044
+
1045
+ const socket = new WebSocket(getLiveWsUrl());
1046
+ state.liveSocket = socket;
1047
+
1048
+ await new Promise((resolve, reject) => {
1049
+ let settled = false;
1050
+
1051
+ socket.addEventListener("open", () => {
1052
+ state.liveSocketConnected = true;
1053
+ settled = true;
1054
+ stopPolling();
1055
+ resolve();
1056
+ }, { once: true });
1057
+
1058
+ socket.addEventListener("error", () => {
1059
+ if (settled) {
1060
+ return;
1061
+ }
1062
+ settled = true;
1063
+ reject(new Error("Live stream connection failed"));
1064
+ }, { once: true });
1065
+ });
1066
+
1067
+ socket.addEventListener("message", (event) => {
1068
+ const payload = JSON.parse(String(event.data));
1069
+ if (payload.type === "snapshot") {
1070
+ renderTerminalPayload(payload, {
1071
+ preferViewportRows: true,
1072
+ forceScrollBottom: true,
1073
+ });
1074
+ setUpdated(formatCapturedAt(payload.captured_at));
1075
+ setLiveModeStatus("stream");
1076
+ return;
1077
+ }
1078
+ if (payload.type === "data") {
1079
+ const terminal = ensureXterm();
1080
+ if (terminal && typeof payload.data === "string") {
1081
+ const previousViewportY = terminal.buffer.active.viewportY;
1082
+ const previousBaseY = terminal.buffer.active.baseY;
1083
+ const wasNearBottom =
1084
+ previousBaseY <= 0 ||
1085
+ previousViewportY >= Math.max(0, previousBaseY - 2);
1086
+ terminal.write(payload.data);
1087
+ if (wasNearBottom) {
1088
+ terminal.scrollToBottom();
1089
+ }
1090
+ }
1091
+ return;
1092
+ }
1093
+ if (payload.type === "exit") {
1094
+ setStatus("Terminal exited", true);
1095
+ return;
1096
+ }
1097
+ });
1098
+
1099
+ socket.addEventListener("close", () => {
1100
+ state.liveSocketConnected = false;
1101
+ if (state.liveSocket === socket) {
1102
+ state.liveSocket = null;
1103
+ }
1104
+ setLiveModeStatus("poll");
1105
+ startPolling();
1106
+ });
1107
+ }
1108
+
791
1109
  function confirmInterrupt() {
792
1110
  return new Promise((resolve) => {
793
1111
  if (tg && typeof tg.showConfirm === "function") {
794
- tg.showConfirm("Send Ctrl+C to the tmux session? This can stop the running agent.", (ok) => {
1112
+ tg.showConfirm("Send Ctrl+C to the terminal session? This can stop the running agent.", (ok) => {
795
1113
  resolve(Boolean(ok));
796
1114
  });
797
1115
  return;
798
1116
  }
799
1117
 
800
- resolve(window.confirm("Send Ctrl+C to the tmux session? This can stop the running agent."));
1118
+ resolve(window.confirm("Send Ctrl+C to the terminal session? This can stop the running agent."));
801
1119
  });
802
1120
  }
803
1121
 
804
1122
  async function refreshVisibleBuffer() {
805
1123
  const payload = await withRecoveredSession(fetchVisibleBuffer);
806
- elements.terminal.innerHTML = renderAnsiToHtml(payload.content || "");
1124
+ renderTerminalPayload(payload);
807
1125
  setUpdated(formatCapturedAt(payload.captured_at));
1126
+ setLiveModeStatus("poll");
808
1127
  }
809
1128
 
810
1129
  function stopPolling() {
@@ -844,7 +1163,7 @@ function bindUi() {
844
1163
  });
845
1164
 
846
1165
  elements.type.addEventListener("click", () => {
847
- const value = window.prompt("Send text to tmux without Enter:", "");
1166
+ const value = window.prompt("Send text to terminal without Enter:", "");
848
1167
  if (value === null || value.length === 0) {
849
1168
  return;
850
1169
  }
@@ -913,8 +1232,12 @@ async function main() {
913
1232
  setStatus("Authorizing Mini App...");
914
1233
  const bootstrapPayload = await bootstrap();
915
1234
  applyBootstrapPayload(bootstrapPayload);
916
- await refreshVisibleBuffer();
917
- startPolling();
1235
+ try {
1236
+ await connectLiveSocket();
1237
+ } catch (_error) {
1238
+ await refreshVisibleBuffer();
1239
+ startPolling();
1240
+ }
918
1241
  } catch (error) {
919
1242
  const message = error instanceof Error ? error.message : String(error);
920
1243
  setStatus(message, true);
@@ -935,7 +1258,8 @@ function renderWebAppHtml(input) {
935
1258
  />
936
1259
  <title>Telegram MCP Live View</title>
937
1260
  <script src="https://telegram.org/js/telegram-web-app.js"></script>
938
- <link rel="stylesheet" href="${input.basePath}/styles.css" />
1261
+ <style>${exports.WEBAPP_STYLES_CSS}
1262
+ ${XTERM_WEBAPP_CSS}</style>
939
1263
  </head>
940
1264
  <body>
941
1265
  <div class="app">
@@ -951,7 +1275,7 @@ function renderWebAppHtml(input) {
951
1275
  <span class="toolbar-spacer" aria-hidden="true"></span>
952
1276
  <button class="btn compact danger" data-role="interrupt" type="button">Ctrl+C</button>
953
1277
  </div>
954
- <pre class="terminal" data-role="terminal">Waiting for tmux buffer…</pre>
1278
+ <div class="terminal" data-role="terminal">Waiting for terminal buffer…</div>
955
1279
  <div class="statusbar">
956
1280
  <div class="status-left">
957
1281
  <span data-role="status">Loading… - Live View</span>
@@ -966,10 +1290,14 @@ function renderWebAppHtml(input) {
966
1290
  <script>
967
1291
  window.__TELEGRAM_MCP_WEBAPP__ = ${JSON.stringify({
968
1292
  basePath: input.basePath,
1293
+ liveWsPath: input.liveWsPath,
969
1294
  launchMode: input.launchMode,
970
1295
  })};
971
1296
  </script>
972
1297
  <script>
1298
+ ${XTERM_WEBAPP_JS}
1299
+ </script>
1300
+ <script>
973
1301
  (() => {
974
1302
  const source = ${JSON.stringify(exports.WEBAPP_APP_JS)};
975
1303
  (0, eval)(source);
@@ -17,6 +17,9 @@ class WebAppLaunchRegistry {
17
17
  ...(details?.telegramMessageId !== undefined
18
18
  ? { telegramMessageId: details.telegramMessageId }
19
19
  : {}),
20
+ ...(details?.allowForeignBinding === true
21
+ ? { allowForeignBinding: true }
22
+ : {}),
20
23
  expiresAtMs: nowMs + ttlSeconds * 1000,
21
24
  createdAtMs: nowMs,
22
25
  };
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sendTerminalLiteralText = exports.sendAllowedTerminalAction = exports.isTerminalUnavailableError = exports.getTerminalWindowSize = exports.getTerminalWindowHeight = exports.captureVisibleTerminalAnsi = exports.captureVisibleTerminal = exports.captureTerminalPaneRange = void 0;
4
+ var client_1 = require("../../shared/integrations/terminal/client");
5
+ Object.defineProperty(exports, "captureTerminalPaneRange", { enumerable: true, get: function () { return client_1.captureTerminalPaneRange; } });
6
+ Object.defineProperty(exports, "captureVisibleTerminal", { enumerable: true, get: function () { return client_1.captureVisibleTerminal; } });
7
+ Object.defineProperty(exports, "captureVisibleTerminalAnsi", { enumerable: true, get: function () { return client_1.captureVisibleTerminalAnsi; } });
8
+ Object.defineProperty(exports, "getTerminalWindowHeight", { enumerable: true, get: function () { return client_1.getTerminalWindowHeight; } });
9
+ Object.defineProperty(exports, "getTerminalWindowSize", { enumerable: true, get: function () { return client_1.getTerminalWindowSize; } });
10
+ Object.defineProperty(exports, "isTerminalUnavailableError", { enumerable: true, get: function () { return client_1.isTerminalUnavailableError; } });
11
+ Object.defineProperty(exports, "sendAllowedTerminalAction", { enumerable: true, get: function () { return client_1.sendAllowedTerminalAction; } });
12
+ Object.defineProperty(exports, "sendTerminalLiteralText", { enumerable: true, get: function () { return client_1.sendTerminalLiteralText; } });