@deadragdoll/tellymcp 0.0.12 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/.env.example.client +5 -1
  2. package/.env.example.gateway +16 -1
  3. package/CHANGELOG.md +30 -0
  4. package/README-ru.md +33 -0
  5. package/README.md +33 -0
  6. package/TOOLS.md +384 -9
  7. package/config/templates/env.both.template +15 -2
  8. package/config/templates/env.client.template +5 -2
  9. package/config/templates/env.gateway.template +15 -2
  10. package/dist/cli.js +137 -2
  11. package/dist/services/features/telegram-mcp/browser.service.js +56 -1
  12. package/dist/services/features/telegram-mcp/file-content.service.js +94 -0
  13. package/dist/services/features/telegram-mcp/gateway-delivery.service.js +5 -1
  14. package/dist/services/features/telegram-mcp/gateway-socket.service.js +43 -11
  15. package/dist/services/features/telegram-mcp/mcp-http.service.js +1 -0
  16. package/dist/services/features/telegram-mcp/mcp-server.service.js +30 -0
  17. package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +23 -1
  18. package/dist/services/features/telegram-mcp/src/app/config/env.js +132 -2
  19. package/dist/services/features/telegram-mcp/src/app/http.js +139 -99
  20. package/dist/services/features/telegram-mcp/src/app/oauthFacade.js +642 -0
  21. package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +151 -170
  22. package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +96 -99
  23. package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +205 -17
  24. package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachActiveTabTool.js +28 -0
  25. package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachTabTool.js +28 -0
  26. package/dist/services/features/telegram-mcp/src/features/browser/model/browserClickTool.js +1 -1
  27. package/dist/services/features/telegram-mcp/src/features/browser/model/browserDetachTabTool.js +28 -0
  28. package/dist/services/features/telegram-mcp/src/features/browser/model/browserDomTool.js +1 -1
  29. package/dist/services/features/telegram-mcp/src/features/browser/model/browserFillTool.js +1 -1
  30. package/dist/services/features/telegram-mcp/src/features/browser/model/browserInjectScriptTool.js +28 -0
  31. package/dist/services/features/telegram-mcp/src/features/browser/model/browserListAttachedInstancesTool.js +33 -0
  32. package/dist/services/features/telegram-mcp/src/features/browser/model/browserListTabsTool.js +33 -0
  33. package/dist/services/features/telegram-mcp/src/features/browser/model/browserPressTool.js +1 -1
  34. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStartTool.js +28 -0
  35. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStatusTool.js +28 -0
  36. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStopTool.js +28 -0
  37. package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +1 -1
  38. package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +890 -23
  39. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/browserRecordingBundle.js +536 -0
  40. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachRegistry.js +80 -0
  41. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachServer.js +736 -0
  42. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/types.js +188 -0
  43. package/dist/services/features/telegram-mcp/src/features/collaboration/model/collaborationService.js +2 -0
  44. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +6 -3
  45. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +4 -1
  46. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +33 -35
  47. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileListTool.js +33 -0
  48. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileService.js +327 -0
  49. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileTool.js +81 -0
  50. package/dist/services/features/telegram-mcp/src/features/file-content/model/temporaryFileLinkStore.js +307 -0
  51. package/dist/services/features/telegram-mcp/src/features/file-content/model/workspaceFilePolicy.js +115 -0
  52. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +5 -1
  53. package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +28 -0
  54. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +3 -0
  55. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +6 -3
  56. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +29 -6
  57. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +100 -2
  58. package/dist/services/features/telegram-mcp/src/shared/lib/bodyLimits.js +63 -0
  59. package/dist/services/features/telegram-mcp/src/shared/lib/gatewayAuth.js +13 -0
  60. package/dist/services/features/telegram-mcp/src/shared/lib/time/localTimestamp.js +21 -0
  61. package/docs/CHAT_CONNECTOR.md +134 -0
  62. package/docs/STANDALONE-ru.md +55 -6
  63. package/docs/STANDALONE.md +55 -6
  64. package/package.json +10 -5
  65. package/packages/chrome-attach-extension/dist/background.js +1735 -0
  66. package/packages/chrome-attach-extension/dist/icon.svg +6 -0
  67. package/packages/chrome-attach-extension/dist/manifest.json +37 -0
  68. package/packages/chrome-attach-extension/dist/options.html +312 -0
  69. package/packages/chrome-attach-extension/dist/options.js +606 -0
  70. package/packages/chrome-attach-extension/dist/popup.html +93 -0
  71. package/packages/chrome-attach-extension/dist/popup.js +79 -0
  72. package/packages/chrome-attach-extension/dist/recorder-content.js +96 -0
  73. package/packages/chrome-attach-extension/dist/recorder-page.js +282 -0
  74. package/packages/firefox-attach-extension/README.md +13 -0
  75. package/packages/firefox-attach-extension/dist/background.js +1622 -0
  76. package/packages/firefox-attach-extension/dist/icon.svg +6 -0
  77. package/packages/firefox-attach-extension/dist/manifest.json +44 -0
  78. package/packages/firefox-attach-extension/dist/options.html +312 -0
  79. package/packages/firefox-attach-extension/dist/options.js +540 -0
  80. package/packages/firefox-attach-extension/dist/popup.html +93 -0
  81. package/packages/firefox-attach-extension/dist/popup.js +64 -0
  82. package/packages/firefox-attach-extension/dist/recorder-content.js +90 -0
  83. package/packages/firefox-attach-extension/dist/recorder-page.js +318 -0
@@ -5,6 +5,7 @@ exports.renderWebAppHtml = renderWebAppHtml;
5
5
  const node_fs_1 = require("node:fs");
6
6
  const XTERM_WEBAPP_CSS = (0, node_fs_1.readFileSync)(require.resolve("@xterm/xterm/css/xterm.css"), "utf8");
7
7
  const XTERM_WEBAPP_JS = (0, node_fs_1.readFileSync)(require.resolve("@xterm/xterm/lib/xterm.js"), "utf8");
8
+ const XTERM_FIT_WEBAPP_JS = (0, node_fs_1.readFileSync)(require.resolve("@xterm/addon-fit/lib/addon-fit.js"), "utf8");
8
9
  exports.WEBAPP_STYLES_CSS = `
9
10
  :root {
10
11
  color-scheme: dark;
@@ -258,18 +259,19 @@ const config = window.__TELEGRAM_MCP_WEBAPP__;
258
259
  const state = {
259
260
  token: null,
260
261
  sessionId: null,
261
- timer: null,
262
262
  actionBusy: false,
263
- pollIntervalMs: 2000,
264
263
  wrapEnabled: true,
265
264
  recoverPromise: null,
266
265
  xterm: null,
266
+ xtermFitAddon: null,
267
267
  xtermInputBound: false,
268
268
  liveSocket: null,
269
269
  liveSocketConnected: false,
270
+ liveConnectPromise: null,
271
+ liveReconnectTimer: null,
272
+ liveReconnectAttempt: 0,
270
273
  lastRenderSignature: null,
271
274
  resizeObserver: null,
272
- fittedRows: null,
273
275
  };
274
276
 
275
277
  const elements = {
@@ -302,7 +304,10 @@ function setUpdated(text) {
302
304
  }
303
305
 
304
306
  function setLiveModeStatus(mode) {
305
- setStatus(mode === "stream" ? "Live (stream)" : "Live (poll)");
307
+ setStatus(
308
+ mode === "stream" ? "Live (stream)" : "Reconnecting live stream...",
309
+ mode !== "stream",
310
+ );
306
311
  }
307
312
 
308
313
  function createHttpError(message, status) {
@@ -372,47 +377,29 @@ function getXtermCtor() {
372
377
  return window.Terminal || window.XtermTerminal || null;
373
378
  }
374
379
 
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;
380
+ function getXtermFitAddonCtor() {
381
+ return window.FitAddon?.FitAddon || null;
389
382
  }
390
383
 
391
- function fitTerminalRows(notifyServer = true) {
384
+ function fitTerminal(notifyServer = true) {
392
385
  const terminal = state.xterm;
393
- if (!terminal || !elements.terminal) {
386
+ const fitAddon = state.xtermFitAddon;
387
+ if (!terminal || !fitAddon) {
394
388
  return;
395
389
  }
396
390
 
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) {
391
+ const previousCols = terminal.cols;
392
+ const previousRows = terminal.rows;
393
+ try {
394
+ fitAddon.fit();
395
+ } catch (_error) {
404
396
  return;
405
397
  }
406
398
 
407
- if (state.fittedRows === nextRows) {
399
+ if (terminal.cols === previousCols && terminal.rows === previousRows) {
408
400
  return;
409
401
  }
410
402
 
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
403
  if (
417
404
  notifyServer &&
418
405
  state.liveSocketConnected &&
@@ -420,7 +407,7 @@ function fitTerminalRows(notifyServer = true) {
420
407
  state.liveSocket.readyState === WebSocket.OPEN
421
408
  ) {
422
409
  state.liveSocket.send(
423
- JSON.stringify({ type: "resize", cols: currentCols, rows: nextRows }),
410
+ JSON.stringify({ type: "resize", cols: terminal.cols, rows: terminal.rows }),
424
411
  );
425
412
  }
426
413
  }
@@ -431,7 +418,8 @@ function ensureXterm() {
431
418
  }
432
419
 
433
420
  const TerminalCtor = getXtermCtor();
434
- if (!TerminalCtor || !elements.terminal) {
421
+ const FitAddonCtor = getXtermFitAddonCtor();
422
+ if (!TerminalCtor || !FitAddonCtor || !elements.terminal) {
435
423
  return null;
436
424
  }
437
425
 
@@ -453,6 +441,8 @@ function ensureXterm() {
453
441
  allowTransparency: true,
454
442
  scrollback: 4000,
455
443
  });
444
+ const fitAddon = new FitAddonCtor();
445
+ terminal.loadAddon(fitAddon);
456
446
  terminal.open(elements.terminal);
457
447
  terminal.onData((data) => {
458
448
  if (!state.liveSocketConnected || !state.liveSocket) {
@@ -470,52 +460,27 @@ function ensureXterm() {
470
460
  });
471
461
  if (!state.resizeObserver && typeof ResizeObserver !== "undefined") {
472
462
  state.resizeObserver = new ResizeObserver(() => {
473
- fitTerminalRows();
463
+ fitTerminal();
474
464
  });
475
465
  state.resizeObserver.observe(elements.terminal);
476
466
  }
477
467
  window.addEventListener("resize", () => {
478
- fitTerminalRows();
468
+ fitTerminal();
479
469
  });
480
470
  state.xterm = terminal;
471
+ state.xtermFitAddon = fitAddon;
481
472
  window.setTimeout(() => {
482
- fitTerminalRows(false);
473
+ fitTerminal();
483
474
  }, 0);
484
475
  return terminal;
485
476
  }
486
477
 
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
478
  function renderTerminalPayload(payload, options = {}) {
513
479
  const signature = JSON.stringify({
514
480
  ansi: payload.ansi || "",
515
481
  content: payload.content || "",
516
482
  cols: payload.cols || null,
517
483
  rows: payload.rows || null,
518
- preferViewportRows: options.preferViewportRows === true,
519
484
  });
520
485
  if (state.lastRenderSignature === signature) {
521
486
  return;
@@ -529,14 +494,13 @@ function renderTerminalPayload(payload, options = {}) {
529
494
  return;
530
495
  }
531
496
 
532
- const size = estimateTerminalSizeFromPayload(payload, options);
533
497
  const previousViewportY = terminal.buffer.active.viewportY;
534
498
  const previousBaseY = terminal.buffer.active.baseY;
535
499
  const wasNearBottom =
536
500
  previousBaseY <= 0 ||
537
501
  previousViewportY >= Math.max(0, previousBaseY - 2);
538
502
  terminal.reset();
539
- terminal.resize(size.cols, size.rows);
503
+ fitTerminal();
540
504
  terminal.write(payload.ansi || payload.content || "");
541
505
  if (options.forceScrollBottom === true || wasNearBottom) {
542
506
  terminal.scrollToBottom();
@@ -544,7 +508,7 @@ function renderTerminalPayload(payload, options = {}) {
544
508
  terminal.scrollToLine(Math.min(previousViewportY, terminal.buffer.active.baseY));
545
509
  }
546
510
  window.setTimeout(() => {
547
- fitTerminalRows(false);
511
+ fitTerminal();
548
512
  }, 0);
549
513
  state.lastRenderSignature = signature;
550
514
  }
@@ -888,7 +852,6 @@ async function bootstrap() {
888
852
  function applyBootstrapPayload(bootstrapPayload) {
889
853
  state.token = bootstrapPayload.token;
890
854
  state.sessionId = bootstrapPayload.session_id;
891
- state.pollIntervalMs = bootstrapPayload.poll_interval_ms || state.pollIntervalMs;
892
855
  elements.session.textContent =
893
856
  bootstrapPayload.session_label || bootstrapPayload.session_id;
894
857
  elements.session.hidden = false;
@@ -928,22 +891,6 @@ async function withRecoveredSession(operation) {
928
891
  }
929
892
  }
930
893
 
931
- async function fetchVisibleBuffer() {
932
- const response = await fetch(config.basePath + "/api/view", {
933
- method: "GET",
934
- headers: {
935
- authorization: "Bearer " + state.token,
936
- },
937
- });
938
-
939
- if (!response.ok) {
940
- const text = await response.text();
941
- throw createHttpError(text || "Failed to fetch visible buffer.", response.status);
942
- }
943
-
944
- return response.json();
945
- }
946
-
947
894
  async function sendAction(action) {
948
895
  if (state.actionBusy || !state.token) {
949
896
  return;
@@ -1036,74 +983,132 @@ function closeLiveSocket() {
1036
983
  }
1037
984
  }
1038
985
 
1039
- async function connectLiveSocket() {
1040
- closeLiveSocket();
1041
- if (!state.token) {
1042
- throw new Error("WebApp token is missing");
986
+ function clearLiveReconnectTimer() {
987
+ if (state.liveReconnectTimer) {
988
+ clearTimeout(state.liveReconnectTimer);
989
+ state.liveReconnectTimer = null;
1043
990
  }
991
+ }
1044
992
 
1045
- const socket = new WebSocket(getLiveWsUrl());
1046
- state.liveSocket = socket;
993
+ function scheduleLiveReconnect() {
994
+ if (
995
+ state.liveSocketConnected ||
996
+ state.liveReconnectTimer ||
997
+ !state.token
998
+ ) {
999
+ return;
1000
+ }
1001
+
1002
+ const delay = Math.min(
1003
+ 30000,
1004
+ 1000 * 2 ** Math.min(state.liveReconnectAttempt, 5),
1005
+ );
1006
+ state.liveReconnectAttempt += 1;
1007
+ state.liveReconnectTimer = setTimeout(async () => {
1008
+ state.liveReconnectTimer = null;
1009
+ try {
1010
+ await connectLiveSocket();
1011
+ } catch (_error) {
1012
+ setLiveModeStatus("reconnect");
1013
+ scheduleLiveReconnect();
1014
+ }
1015
+ }, delay);
1016
+ }
1017
+
1018
+ function connectLiveSocket() {
1019
+ if (
1020
+ state.liveSocketConnected &&
1021
+ state.liveSocket?.readyState === WebSocket.OPEN
1022
+ ) {
1023
+ return Promise.resolve();
1024
+ }
1025
+ if (state.liveConnectPromise) {
1026
+ return state.liveConnectPromise;
1027
+ }
1047
1028
 
1048
- await new Promise((resolve, reject) => {
1049
- let settled = false;
1029
+ state.liveConnectPromise = (async () => {
1030
+ closeLiveSocket();
1031
+ if (!state.token) {
1032
+ throw new Error("WebApp token is missing");
1033
+ }
1050
1034
 
1051
- socket.addEventListener("open", () => {
1052
- state.liveSocketConnected = true;
1053
- settled = true;
1054
- stopPolling();
1055
- resolve();
1056
- }, { once: true });
1035
+ const socket = new WebSocket(getLiveWsUrl());
1036
+ state.liveSocket = socket;
1057
1037
 
1058
- socket.addEventListener("error", () => {
1059
- if (settled) {
1038
+ socket.addEventListener("message", (event) => {
1039
+ if (state.liveSocket !== socket) {
1060
1040
  return;
1061
1041
  }
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();
1042
+ const payload = JSON.parse(String(event.data));
1043
+ if (payload.type === "snapshot") {
1044
+ state.liveReconnectAttempt = 0;
1045
+ renderTerminalPayload(payload, {
1046
+ forceScrollBottom: true,
1047
+ });
1048
+ setUpdated(formatCapturedAt(payload.captured_at));
1049
+ setLiveModeStatus("stream");
1050
+ return;
1051
+ }
1052
+ if (payload.type === "data") {
1053
+ const terminal = ensureXterm();
1054
+ if (terminal && typeof payload.data === "string") {
1055
+ const previousViewportY = terminal.buffer.active.viewportY;
1056
+ const previousBaseY = terminal.buffer.active.baseY;
1057
+ const wasNearBottom =
1058
+ previousBaseY <= 0 ||
1059
+ previousViewportY >= Math.max(0, previousBaseY - 2);
1060
+ terminal.write(payload.data);
1061
+ if (wasNearBottom) {
1062
+ terminal.scrollToBottom();
1063
+ }
1089
1064
  }
1065
+ return;
1090
1066
  }
1091
- return;
1092
- }
1093
- if (payload.type === "exit") {
1094
- setStatus("Terminal exited", true);
1095
- return;
1096
- }
1097
- });
1067
+ if (payload.type === "exit") {
1068
+ setStatus("Terminal exited", true);
1069
+ }
1070
+ });
1098
1071
 
1099
- socket.addEventListener("close", () => {
1100
- state.liveSocketConnected = false;
1101
- if (state.liveSocket === socket) {
1102
- state.liveSocket = null;
1103
- }
1104
- setLiveModeStatus("poll");
1105
- startPolling();
1072
+ await new Promise((resolve, reject) => {
1073
+ let settled = false;
1074
+
1075
+ socket.addEventListener("open", () => {
1076
+ if (state.liveSocket !== socket) {
1077
+ return;
1078
+ }
1079
+ state.liveSocketConnected = true;
1080
+ clearLiveReconnectTimer();
1081
+ settled = true;
1082
+ resolve();
1083
+ }, { once: true });
1084
+
1085
+ socket.addEventListener("error", () => {
1086
+ if (settled) {
1087
+ return;
1088
+ }
1089
+ settled = true;
1090
+ reject(new Error("Live stream connection failed"));
1091
+ }, { once: true });
1092
+
1093
+ socket.addEventListener("close", () => {
1094
+ if (state.liveSocket !== socket) {
1095
+ return;
1096
+ }
1097
+ state.liveSocket = null;
1098
+ state.liveSocketConnected = false;
1099
+ if (!settled) {
1100
+ settled = true;
1101
+ reject(new Error("Live stream connection closed"));
1102
+ }
1103
+ setLiveModeStatus("reconnect");
1104
+ scheduleLiveReconnect();
1105
+ });
1106
+ });
1107
+ })().finally(() => {
1108
+ state.liveConnectPromise = null;
1106
1109
  });
1110
+
1111
+ return state.liveConnectPromise;
1107
1112
  }
1108
1113
 
1109
1114
  function confirmInterrupt() {
@@ -1119,33 +1124,6 @@ function confirmInterrupt() {
1119
1124
  });
1120
1125
  }
1121
1126
 
1122
- async function refreshVisibleBuffer() {
1123
- const payload = await withRecoveredSession(fetchVisibleBuffer);
1124
- renderTerminalPayload(payload);
1125
- setUpdated(formatCapturedAt(payload.captured_at));
1126
- setLiveModeStatus("poll");
1127
- }
1128
-
1129
- function stopPolling() {
1130
- if (state.timer) {
1131
- clearTimeout(state.timer);
1132
- state.timer = null;
1133
- }
1134
- }
1135
-
1136
- function startPolling() {
1137
- stopPolling();
1138
- state.timer = setTimeout(async () => {
1139
- try {
1140
- await refreshVisibleBuffer();
1141
- } catch (error) {
1142
- setStatus(error.message || String(error), true);
1143
- } finally {
1144
- startPolling();
1145
- }
1146
- }, state.pollIntervalMs);
1147
- }
1148
-
1149
1127
  function bindUi() {
1150
1128
  elements.wrap.addEventListener("click", () => {
1151
1129
  toggleWrapMode();
@@ -1235,8 +1213,8 @@ async function main() {
1235
1213
  try {
1236
1214
  await connectLiveSocket();
1237
1215
  } catch (_error) {
1238
- await refreshVisibleBuffer();
1239
- startPolling();
1216
+ setLiveModeStatus("reconnect");
1217
+ scheduleLiveReconnect();
1240
1218
  }
1241
1219
  } catch (error) {
1242
1220
  const message = error instanceof Error ? error.message : String(error);
@@ -1298,6 +1276,9 @@ ${XTERM_WEBAPP_CSS}</style>
1298
1276
  ${XTERM_WEBAPP_JS}
1299
1277
  </script>
1300
1278
  <script>
1279
+ ${XTERM_FIT_WEBAPP_JS}
1280
+ </script>
1281
+ <script>
1301
1282
  (() => {
1302
1283
  const source = ${JSON.stringify(exports.WEBAPP_APP_JS)};
1303
1284
  (0, eval)(source);