@deadragdoll/tellymcp 0.0.1 → 0.0.3

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.
package/README-ru.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TellyMCP
2
2
 
3
- [English](README.md) | [Русский](README-ru.md)
3
+ [English](README.md) | [Русский](README-ru.md) | [Standalone Guide](STANDALONE.md) | [Standalone RU](STANDALONE-ru.md)
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/%40deadragdoll%2Ftellymcp)](https://www.npmjs.com/package/@deadragdoll/tellymcp)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/%40deadragdoll%2Ftellymcp)](https://www.npmjs.com/package/@deadragdoll/tellymcp)
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # TellyMCP
2
2
 
3
- [English](README.md) | [Русский](README-ru.md)
3
+ [English](README.md) | [Русский](README-ru.md) | [Standalone Guide](STANDALONE.md) | [Standalone RU](STANDALONE-ru.md)
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/%40deadragdoll%2Ftellymcp)](https://www.npmjs.com/package/@deadragdoll/tellymcp)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/%40deadragdoll%2Ftellymcp)](https://www.npmjs.com/package/@deadragdoll/tellymcp)
@@ -60,6 +60,7 @@ Current tools:
60
60
  - a Telegram bot token from BotFather
61
61
  - for `gateway` / `both`: Postgres
62
62
  - optional for durable fanout on gateway: RabbitMQ
63
+ - for `browser_*` tools: Playwright Chromium browser binaries
63
64
 
64
65
  ## tmux is strongly recommended
65
66
 
@@ -145,6 +146,12 @@ Typical local MCP endpoint in `client` mode:
145
146
 
146
147
  - `http://127.0.0.1:8787/mcp`
147
148
 
149
+ If you plan to use `browser_*` tools, install Chromium once:
150
+
151
+ ```bash
152
+ tellymcp browser install
153
+ ```
154
+
148
155
  Detailed step-by-step guide:
149
156
 
150
157
  - [STANDALONE.md](STANDALONE.md)
@@ -519,6 +526,8 @@ Recommended local dev settings:
519
526
  - `BROWSER_ADDRESS=http://localhost:5173`
520
527
  - start your SPA dev server on `0.0.0.0:5173`
521
528
  - open it through `browser_open`
529
+ - install browser binaries once with `npx playwright install chromium`
530
+ - install browser binaries once with `tellymcp browser install`
522
531
 
523
532
  Recommended Docker settings:
524
533
 
package/STANDALONE-ru.md CHANGED
@@ -20,6 +20,7 @@
20
20
  2. `tmux`
21
21
  3. Redis
22
22
  4. Telegram bot token из BotFather
23
+ 5. опционально для `browser_*` tools: Playwright Chromium browser binaries
23
24
 
24
25
  Зачем это нужно:
25
26
 
@@ -46,6 +47,12 @@ redis-cli ping
46
47
  - `tmux -V` показывает версию
47
48
  - `redis-cli ping` возвращает `PONG`
48
49
 
50
+ Если планируешь использовать browser automation tools, один раз выполни ещё:
51
+
52
+ ```bash
53
+ tellymcp browser install
54
+ ```
55
+
49
56
  ## Шаг 2. Создать Telegram-бота
50
57
 
51
58
  В Telegram:
@@ -156,6 +163,7 @@ tellymcp doctor --env .env
156
163
  - `.env`
157
164
  - Redis
158
165
  - локальный MCP bind
166
+ - Playwright Chromium, если browser tools включены
159
167
 
160
168
  ## Шаг 8. Запустить сервис
161
169
 
package/STANDALONE.md CHANGED
@@ -20,6 +20,7 @@ Before installation, make sure you have:
20
20
  2. `tmux`
21
21
  3. Redis
22
22
  4. a Telegram bot token from BotFather
23
+ 5. optional for `browser_*` tools: Playwright Chromium browser binaries
23
24
 
24
25
  Why these matter:
25
26
 
@@ -46,6 +47,12 @@ Expected:
46
47
  - `tmux -V` prints a version
47
48
  - `redis-cli ping` returns `PONG`
48
49
 
50
+ If you plan to use browser automation tools later, also run:
51
+
52
+ ```bash
53
+ tellymcp browser install
54
+ ```
55
+
49
56
  ## Step 2. Create a Telegram bot
50
57
 
51
58
  In Telegram:
@@ -156,6 +163,7 @@ In standalone `client` mode it should verify:
156
163
  - `.env`
157
164
  - Redis
158
165
  - local MCP bind
166
+ - Playwright Chromium when browser tools are enabled
159
167
 
160
168
  ## Step 8. Run the service
161
169
 
package/TOOLS.md CHANGED
@@ -30,6 +30,12 @@ Browser tools:
30
30
  - `browser_screenshot`
31
31
  - `browser_close`
32
32
 
33
+ Browser runtime rule:
34
+
35
+ - Browser tools use Playwright Chromium.
36
+ - If browser tools fail because the Playwright browser runtime is missing, install it with `tellymcp browser install`.
37
+ - Do not stop at the installation error itself. Install the browser runtime first, then retry the browser tool.
38
+
33
39
  Collaboration tools:
34
40
 
35
41
  - `send_partner_note`
@@ -702,6 +708,9 @@ Input:
702
708
 
703
709
  Notes:
704
710
 
711
+ - Browser tools require Playwright Chromium browser binaries.
712
+ - If the runtime is missing, run `tellymcp browser install`, then retry `browser_open`.
713
+
705
714
  - the server always uses `TELEGRAM_INBOX_BATCH_SIZE`
706
715
  - the agent should not try to choose its own batch size
707
716
 
package/dist/cli.js CHANGED
@@ -50,6 +50,34 @@ function getTmuxInstallHints() {
50
50
  "Arch: sudo pacman -S tmux",
51
51
  ];
52
52
  }
53
+ async function getPlaywrightBrowserStatus(browserEnabled) {
54
+ if (!browserEnabled) {
55
+ return { enabled: false };
56
+ }
57
+ try {
58
+ const playwright = await import("playwright");
59
+ const executablePath = playwright.chromium.executablePath();
60
+ if (executablePath && (0, node_fs_1.existsSync)(executablePath)) {
61
+ return {
62
+ enabled: true,
63
+ installed: true,
64
+ executablePath,
65
+ };
66
+ }
67
+ return {
68
+ enabled: true,
69
+ installed: false,
70
+ message: "Chromium browser binaries are missing.",
71
+ };
72
+ }
73
+ catch (error) {
74
+ return {
75
+ enabled: true,
76
+ installed: false,
77
+ message: error instanceof Error ? error.message : String(error),
78
+ };
79
+ }
80
+ }
53
81
  function printHelp() {
54
82
  const tmux = getTmuxStatus();
55
83
  printBanner("CLI", "Telegram Human-in-the-Loop MCP server");
@@ -58,6 +86,7 @@ function printHelp() {
58
86
  " tellymcp run [--env <file>]",
59
87
  " tellymcp run --env=<file>",
60
88
  " tellymcp doctor [--env <file>]",
89
+ " tellymcp browser install",
61
90
  " tellymcp mcp [--url <url>] [--bearer <token>] [--format claude|legacy]",
62
91
  " tellymcp help",
63
92
  ]);
@@ -67,6 +96,7 @@ function printHelp() {
67
96
  " tellymcp run",
68
97
  " tellymcp run --env .env.client",
69
98
  " tellymcp doctor --env .env.client",
99
+ " tellymcp browser install",
70
100
  " tellymcp mcp --help",
71
101
  ]);
72
102
  if (tmux.found) {
@@ -146,6 +176,17 @@ function printMcpHelp() {
146
176
  " tellymcp mcp --url https://builder.undoo.ru/api/mcp --format legacy",
147
177
  ]);
148
178
  }
179
+ function printBrowserHelp() {
180
+ printBanner("browser helper", "Manage Playwright browser binaries used by browser_* tools");
181
+ printSection("Usage", [
182
+ " tellymcp browser install",
183
+ ]);
184
+ printSection("What this command does", [
185
+ " Installs the bundled Playwright Chromium browser.",
186
+ " Uses the Playwright dependency shipped with TellyMCP.",
187
+ " Avoids generic npx warnings about missing local project dependencies.",
188
+ ]);
189
+ }
149
190
  function fail(message) {
150
191
  process.stderr.write(`${message}\n`);
151
192
  process.exit(1);
@@ -439,6 +480,7 @@ async function runDoctor(args) {
439
480
  const gatewayWsUrl = parsed.GATEWAY_WS_URL?.trim();
440
481
  const publicWebappUrl = parsed.WEBAPP_PUBLIC_URL?.trim();
441
482
  const mcpBearerToken = parsed.MCP_HTTP_BEARER_TOKEN?.trim();
483
+ const browserEnabled = (parsed.BROWSER_ENABLED || "true").trim().toLowerCase() !== "false";
442
484
  const externalHealthUrl = deriveGatewayHealthUrlFromPublicUrl(publicGatewayUrl || "") ??
443
485
  deriveGatewayHealthUrlFromPublicUrl(publicWebappUrl || "");
444
486
  printSection("env", [
@@ -455,6 +497,19 @@ async function runDoctor(args) {
455
497
  ]);
456
498
  const checks = [];
457
499
  const capabilities = [];
500
+ const playwrightStatus = await getPlaywrightBrowserStatus(browserEnabled);
501
+ if (!playwrightStatus.enabled) {
502
+ checks.push(`${picocolors_1.default.dim(" SKIP")} playwright: browser tools are disabled`);
503
+ capabilities.push(`${picocolors_1.default.dim(" SKIP")} browser tools: disabled`);
504
+ }
505
+ else if (playwrightStatus.installed) {
506
+ checks.push(`${picocolors_1.default.green(" OK")} playwright chromium: ${playwrightStatus.executablePath}`);
507
+ capabilities.push(`${picocolors_1.default.green(" OK")} browser tools: available`);
508
+ }
509
+ else {
510
+ checks.push(`${picocolors_1.default.red(" ERROR")} playwright chromium: ${playwrightStatus.message}`);
511
+ capabilities.push(`${picocolors_1.default.red(" ERROR")} browser tools: browsers are not installed`);
512
+ }
458
513
  const redisHost = (parsed.REDIS_HOST || "127.0.0.1").trim();
459
514
  const redisPort = Number(parsed.REDIS_PORT || 6379);
460
515
  const redisCheck = await checkTcpPort(redisHost, redisPort);
@@ -560,6 +615,39 @@ async function runDoctor(args) {
560
615
  else {
561
616
  printSection("notes", [`${picocolors_1.default.green(" OK")} No obvious local config issues detected.`]);
562
617
  }
618
+ if (browserEnabled && (!playwrightStatus.enabled || !playwrightStatus.installed)) {
619
+ printSection("playwright", [
620
+ `${picocolors_1.default.yellow(" ACTION")} Install browser binaries before using browser_* tools:`,
621
+ " tellymcp browser install",
622
+ ]);
623
+ }
624
+ }
625
+ function runBrowserCommand(args) {
626
+ const [subcommand] = args;
627
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
628
+ printBrowserHelp();
629
+ return;
630
+ }
631
+ if (subcommand !== "install") {
632
+ fail("Supported browser subcommands: install");
633
+ }
634
+ const cliPath = node_path_1.default.join(packageRoot, "node_modules", "playwright", "cli.js");
635
+ if (!(0, node_fs_1.existsSync)(cliPath)) {
636
+ fail(`Missing bundled Playwright CLI: ${cliPath}`);
637
+ }
638
+ printBanner("browser install", "Installing bundled Playwright Chromium");
639
+ const child = (0, node_child_process_1.spawn)(process.execPath, [cliPath, "install", "chromium"], {
640
+ cwd: process.cwd(),
641
+ stdio: "inherit",
642
+ env: process.env,
643
+ });
644
+ child.on("exit", (code, signal) => {
645
+ if (signal) {
646
+ process.kill(process.pid, signal);
647
+ return;
648
+ }
649
+ process.exit(code ?? 0);
650
+ });
563
651
  }
564
652
  function runRuntime(args) {
565
653
  const envPath = resolveRunEnvPath(args);
@@ -612,7 +700,7 @@ function runRuntime(args) {
612
700
  }
613
701
  async function main(argv) {
614
702
  const [rawCommand, firstArg, secondArg] = argv;
615
- const command = rawCommand === "init" || rawCommand === "run" || rawCommand === "help" || rawCommand === "mcp" || rawCommand === "doctor"
703
+ const command = rawCommand === "init" || rawCommand === "run" || rawCommand === "help" || rawCommand === "mcp" || rawCommand === "doctor" || rawCommand === "browser"
616
704
  ? rawCommand
617
705
  : "help";
618
706
  if (command === "help" || !rawCommand || rawCommand === "--help" || rawCommand === "-h") {
@@ -631,6 +719,10 @@ async function main(argv) {
631
719
  await runDoctor(argv.slice(1));
632
720
  return;
633
721
  }
722
+ if (command === "browser") {
723
+ runBrowserCommand(argv.slice(1));
724
+ return;
725
+ }
634
726
  runRuntime(argv.slice(1));
635
727
  }
636
728
  void main(process.argv.slice(2));
@@ -278,7 +278,7 @@ const TelegramMcpGatewayDeliveryService = {
278
278
  try {
279
279
  await runtime.telegramTransport.sendNotification({
280
280
  sessionId: targetSession.sessionId,
281
- ...(targetSession.label ? { sessionLabel: targetSession.label } : {}),
281
+ sessionLabel: delivery.source_session_label,
282
282
  recipient: {
283
283
  telegramChatId: targetBinding.telegramChatId,
284
284
  telegramUserId: targetBinding.telegramUserId,
@@ -554,7 +554,7 @@ const TelegramMcpGatewaySocketService = {
554
554
  const action = typeof request.payload?.action === "string"
555
555
  ? request.payload.action
556
556
  : "";
557
- if (!["up", "down", "enter", "slash", "delete", "tab", "escape"].includes(action)) {
557
+ if (!["up", "down", "enter", "slash", "delete", "tab", "escape", "interrupt"].includes(action)) {
558
558
  throw new Error("Unsupported action");
559
559
  }
560
560
  const sessionId = request.local_session_id.trim();
@@ -428,7 +428,7 @@ function createMcpHttpHandler(runtime, input) {
428
428
  typeof Reflect.get(body, "action") === "string"
429
429
  ? String(Reflect.get(body, "action"))
430
430
  : "";
431
- if (!["up", "down", "enter", "slash", "delete", "tab", "escape"].includes(action)) {
431
+ if (!["up", "down", "enter", "slash", "delete", "tab", "escape", "interrupt"].includes(action)) {
432
432
  writeText(res, 400, "Unsupported action");
433
433
  return;
434
434
  }
@@ -168,6 +168,7 @@ const elements = {
168
168
  session: document.querySelector("[data-role=session]"),
169
169
  status: document.querySelector("[data-role=status]"),
170
170
  updated: document.querySelector("[data-role=updated]"),
171
+ interrupt: document.querySelector("[data-role=interrupt]"),
171
172
  esc: document.querySelector("[data-role=escape]"),
172
173
  tab: document.querySelector("[data-role=tab]"),
173
174
  slash: document.querySelector("[data-role=slash]"),
@@ -597,6 +598,10 @@ function startPolling() {
597
598
  }
598
599
 
599
600
  function bindUi() {
601
+ elements.interrupt.addEventListener("click", () => {
602
+ sendAction("interrupt").catch((error) => setStatus(error.message || String(error), true));
603
+ });
604
+
600
605
  elements.esc.addEventListener("click", () => {
601
606
  sendAction("escape").catch((error) => setStatus(error.message || String(error), true));
602
607
  });
@@ -664,6 +669,7 @@ async function main() {
664
669
  bootstrapPayload.session_label || bootstrapPayload.session_id;
665
670
 
666
671
  if (!bootstrapPayload.tmux_target) {
672
+ elements.interrupt.disabled = true;
667
673
  elements.esc.disabled = true;
668
674
  elements.tab.disabled = true;
669
675
  elements.slash.disabled = true;
@@ -704,6 +710,7 @@ function renderWebAppHtml(input) {
704
710
  <body>
705
711
  <div class="app">
706
712
  <div class="toolbar">
713
+ <button class="btn compact danger" data-role="interrupt" type="button">Ctrl+C</button>
707
714
  <button class="btn compact" data-role="escape" type="button">Esc</button>
708
715
  <button class="btn compact" data-role="tab" type="button">Tab</button>
709
716
  <button class="btn compact" data-role="slash" type="button">/</button>
@@ -248,7 +248,7 @@ class LocalCollaborationBackend {
248
248
  await this.inboxStore.createInboxMessage(inboxMessage);
249
249
  await this.telegramTransport.sendNotification({
250
250
  sessionId: targetSession.sessionId,
251
- ...(targetSession.label ? { sessionLabel: targetSession.label } : {}),
251
+ sessionLabel: sourceLabel,
252
252
  recipient: {
253
253
  telegramChatId: targetBinding.telegramChatId,
254
254
  telegramUserId: targetBinding.telegramUserId,
@@ -253,7 +253,9 @@ async function sendAllowedTmuxAction(config, target, action) {
253
253
  ? "Tab"
254
254
  : action === "escape"
255
255
  ? "Escape"
256
- : "Enter";
256
+ : action === "interrupt"
257
+ ? "C-c"
258
+ : "Enter";
257
259
  await execFileAsync("tmux", buildTmuxArgs(config, ["send-keys", "-t", target, key]));
258
260
  }
259
261
  async function sendTmuxLiteralLine(config, target, text) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deadragdoll/tellymcp",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "TellyMCP - Telegram Human-in-the-Loop MCP Server",
5
5
  "main": "dist/services/features/telegram-mcp/runtime.service.js",
6
6
  "bin": {
@@ -34,6 +34,8 @@
34
34
  },
35
35
  "scripts": {
36
36
  "postinstall": "node ./scripts/postinstall.js",
37
+ "prepack": "yarn build",
38
+ "prepublishOnly": "yarn build && yarn lint && yarn test",
37
39
  "mcp:local": "codex mcp add telegramHuman --url http://127.0.0.1:8787/mcp",
38
40
  "dev:gw": "TS_NODE_TRANSPILE_ONLY=1 TS_NODE_EXPERIMENTAL_RESOLVER=1 NODE_OPTIONS=--openssl-legacy-provider ts-node ./node_modules/moleculer/bin/moleculer-runner.js src/services --config src/moleculer.config.ts --envfile ./.env-dev --hot --mask \"**/*.service.ts\"",
39
41
  "dev:builder": "TS_NODE_TRANSPILE_ONLY=1 TS_NODE_EXPERIMENTAL_RESOLVER=1 NODE_OPTIONS=--openssl-legacy-provider ts-node ./node_modules/moleculer/bin/moleculer-runner.js src/services --config src/moleculer.config.ts --envfile ./.env-builder --hot --mask \"**/*.service.ts\"",
@@ -53,6 +53,11 @@ if (tmux.found) {
53
53
  }
54
54
  }
55
55
 
56
+ line();
57
+ line(`${pc.yellow("INFO")} Browser tools need Playwright browser binaries.`);
58
+ line("If you plan to use browser_* tools, run:");
59
+ line(` ${pc.bold("tellymcp browser install")}`);
60
+
56
61
  line();
57
62
  line(`Check your local setup: ${pc.bold("tellymcp doctor")}`);
58
63
  line(`General help: ${pc.bold("tellymcp help")}`);