@deadragdoll/tellymcp 0.0.2 → 0.0.4
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 +1 -1
- package/README.md +10 -1
- package/STANDALONE-ru.md +8 -0
- package/STANDALONE.md +8 -0
- package/TOOLS.md +9 -0
- package/VERSION.md +93 -0
- package/dist/cli.js +97 -2
- package/dist/services/features/telegram-mcp/gateway-socket.service.js +1 -1
- package/dist/services/features/telegram-mcp/runtime.service.js +9 -2
- package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +2 -0
- package/dist/services/features/telegram-mcp/src/app/http.js +1 -1
- package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +7 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +95 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/tmux/client.js +3 -1
- package/package.json +4 -1
- package/scripts/postinstall.js +5 -0
package/README-ru.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TellyMCP
|
|
2
2
|
|
|
3
|
-
[English](README.md) | [Русский](README-ru.md) | [Standalone Guide](STANDALONE.md) | [Standalone RU](STANDALONE-ru.md)
|
|
3
|
+
[English](README.md) | [Русский](README-ru.md) | [Standalone Guide](STANDALONE.md) | [Standalone RU](STANDALONE-ru.md) | [Release Notes](VERSION.md)
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@deadragdoll/tellymcp)
|
|
6
6
|
[](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) | [Standalone Guide](STANDALONE.md) | [Standalone RU](STANDALONE-ru.md)
|
|
3
|
+
[English](README.md) | [Русский](README-ru.md) | [Standalone Guide](STANDALONE.md) | [Standalone RU](STANDALONE-ru.md) | [Release Notes](VERSION.md)
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@deadragdoll/tellymcp)
|
|
6
6
|
[](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/VERSION.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# TellyMCP Release Notes
|
|
2
|
+
|
|
3
|
+
Public, user-facing release notes for published versions of `@deadragdoll/tellymcp`.
|
|
4
|
+
|
|
5
|
+
For detailed engineering history, refactors, and internal development notes, see [CHANGELOG.md](CHANGELOG.md).
|
|
6
|
+
|
|
7
|
+
## 0.0.3
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Standalone CLI workflow:
|
|
11
|
+
- `tellymcp init <client|gateway|both>`
|
|
12
|
+
- `tellymcp run`
|
|
13
|
+
- `tellymcp doctor`
|
|
14
|
+
- `tellymcp mcp --help`
|
|
15
|
+
- Standalone and public installation guides:
|
|
16
|
+
- [STANDALONE.md](STANDALONE.md)
|
|
17
|
+
- [STANDALONE-ru.md](STANDALONE-ru.md)
|
|
18
|
+
- Browser runtime helper:
|
|
19
|
+
- `tellymcp browser install`
|
|
20
|
+
- Public README set for GitHub and npm:
|
|
21
|
+
- [README.md](README.md)
|
|
22
|
+
- [README-ru.md](README-ru.md)
|
|
23
|
+
- Human-readable release notes in this file.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Default installation path is now npm-first:
|
|
27
|
+
- `npm install -g @deadragdoll/tellymcp`
|
|
28
|
+
- Standalone client mode is documented first, before gateway/both deployment.
|
|
29
|
+
- `tmux` is now documented as a strongly recommended prerequisite for the full experience:
|
|
30
|
+
- Live View
|
|
31
|
+
- nudges
|
|
32
|
+
- direct terminal control from Telegram
|
|
33
|
+
- Environment examples were split into dedicated client and gateway variants.
|
|
34
|
+
- Package build/publish flow now validates itself before packing/publishing.
|
|
35
|
+
|
|
36
|
+
### Collaboration
|
|
37
|
+
- Project collaboration works across local and remote sessions.
|
|
38
|
+
- `Collab` now includes:
|
|
39
|
+
- `Broadcast`
|
|
40
|
+
- `History`
|
|
41
|
+
- `Delete`
|
|
42
|
+
- `Ask` and `Share` semantics were clarified:
|
|
43
|
+
- `Ask` tells the selected session to do the work and reply back
|
|
44
|
+
- `Share` tells the current session to send something to the selected session
|
|
45
|
+
|
|
46
|
+
### Live View
|
|
47
|
+
- Telegram Mini App Live View supports:
|
|
48
|
+
- fullscreen/expand launch policy
|
|
49
|
+
- bottom toolbar layout
|
|
50
|
+
- `Esc`
|
|
51
|
+
- `Tab`
|
|
52
|
+
- `Ctrl+C`
|
|
53
|
+
- `Backspace`
|
|
54
|
+
- `Up`
|
|
55
|
+
- `Down`
|
|
56
|
+
- `Enter`
|
|
57
|
+
- Live approval flow was added for remote project sessions.
|
|
58
|
+
|
|
59
|
+
### Browser
|
|
60
|
+
- Browser tools use Playwright Chromium.
|
|
61
|
+
- Headless mode is the recommended default for remote and SSH-based environments.
|
|
62
|
+
- `doctor` now helps detect missing browser runtime and connectivity issues.
|
|
63
|
+
|
|
64
|
+
### Compatibility
|
|
65
|
+
- Gateway and clients now compare:
|
|
66
|
+
- package version
|
|
67
|
+
- protocol version
|
|
68
|
+
- capabilities
|
|
69
|
+
- `TOOLS.md` sync now detects outdated or missing local instructions and asks the session to refresh them.
|
|
70
|
+
|
|
71
|
+
### Removed
|
|
72
|
+
- Legacy Go/HTTP tmux proxy path was removed.
|
|
73
|
+
- Direct product path is now local `tmux` only.
|
|
74
|
+
|
|
75
|
+
## Next entry template
|
|
76
|
+
|
|
77
|
+
Copy this block for the next published version:
|
|
78
|
+
|
|
79
|
+
```md
|
|
80
|
+
## x.y.z
|
|
81
|
+
|
|
82
|
+
### Added
|
|
83
|
+
- ...
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
- ...
|
|
87
|
+
|
|
88
|
+
### Fixed
|
|
89
|
+
- ...
|
|
90
|
+
|
|
91
|
+
### Removed
|
|
92
|
+
- ...
|
|
93
|
+
```
|
package/dist/cli.js
CHANGED
|
@@ -11,8 +11,10 @@ const node_net_1 = __importDefault(require("node:net"));
|
|
|
11
11
|
const dotenv_1 = require("dotenv");
|
|
12
12
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
13
13
|
const ws_1 = __importDefault(require("ws"));
|
|
14
|
+
const versionHandshake_1 = require("./services/features/telegram-mcp/src/shared/lib/version/versionHandshake");
|
|
14
15
|
const distDir = __dirname;
|
|
15
16
|
const packageRoot = node_path_1.default.resolve(distDir, "..");
|
|
17
|
+
const cliPackageVersion = (0, versionHandshake_1.getTellyMcpPackageVersion)(__dirname);
|
|
16
18
|
function getTmuxStatus() {
|
|
17
19
|
const result = (0, node_child_process_1.spawnSync)("tmux", ["-V"], {
|
|
18
20
|
encoding: "utf8",
|
|
@@ -27,7 +29,7 @@ function getTmuxStatus() {
|
|
|
27
29
|
return { found: false };
|
|
28
30
|
}
|
|
29
31
|
function printBanner(title, subtitle) {
|
|
30
|
-
process.stdout.write(`${picocolors_1.default.bold(picocolors_1.default.cyan("TellyMCP"))} ${picocolors_1.default.dim(title)}\n`);
|
|
32
|
+
process.stdout.write(`${picocolors_1.default.bold(picocolors_1.default.cyan("TellyMCP"))} ${picocolors_1.default.bold(picocolors_1.default.white(`v${cliPackageVersion}`))} ${picocolors_1.default.dim(title)}\n`);
|
|
31
33
|
if (subtitle) {
|
|
32
34
|
process.stdout.write(`${picocolors_1.default.dim(subtitle)}\n`);
|
|
33
35
|
}
|
|
@@ -50,6 +52,34 @@ function getTmuxInstallHints() {
|
|
|
50
52
|
"Arch: sudo pacman -S tmux",
|
|
51
53
|
];
|
|
52
54
|
}
|
|
55
|
+
async function getPlaywrightBrowserStatus(browserEnabled) {
|
|
56
|
+
if (!browserEnabled) {
|
|
57
|
+
return { enabled: false };
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const playwright = await import("playwright");
|
|
61
|
+
const executablePath = playwright.chromium.executablePath();
|
|
62
|
+
if (executablePath && (0, node_fs_1.existsSync)(executablePath)) {
|
|
63
|
+
return {
|
|
64
|
+
enabled: true,
|
|
65
|
+
installed: true,
|
|
66
|
+
executablePath,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
enabled: true,
|
|
71
|
+
installed: false,
|
|
72
|
+
message: "Chromium browser binaries are missing.",
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return {
|
|
77
|
+
enabled: true,
|
|
78
|
+
installed: false,
|
|
79
|
+
message: error instanceof Error ? error.message : String(error),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
53
83
|
function printHelp() {
|
|
54
84
|
const tmux = getTmuxStatus();
|
|
55
85
|
printBanner("CLI", "Telegram Human-in-the-Loop MCP server");
|
|
@@ -58,6 +88,7 @@ function printHelp() {
|
|
|
58
88
|
" tellymcp run [--env <file>]",
|
|
59
89
|
" tellymcp run --env=<file>",
|
|
60
90
|
" tellymcp doctor [--env <file>]",
|
|
91
|
+
" tellymcp browser install",
|
|
61
92
|
" tellymcp mcp [--url <url>] [--bearer <token>] [--format claude|legacy]",
|
|
62
93
|
" tellymcp help",
|
|
63
94
|
]);
|
|
@@ -67,6 +98,7 @@ function printHelp() {
|
|
|
67
98
|
" tellymcp run",
|
|
68
99
|
" tellymcp run --env .env.client",
|
|
69
100
|
" tellymcp doctor --env .env.client",
|
|
101
|
+
" tellymcp browser install",
|
|
70
102
|
" tellymcp mcp --help",
|
|
71
103
|
]);
|
|
72
104
|
if (tmux.found) {
|
|
@@ -146,6 +178,17 @@ function printMcpHelp() {
|
|
|
146
178
|
" tellymcp mcp --url https://builder.undoo.ru/api/mcp --format legacy",
|
|
147
179
|
]);
|
|
148
180
|
}
|
|
181
|
+
function printBrowserHelp() {
|
|
182
|
+
printBanner("browser helper", "Manage Playwright browser binaries used by browser_* tools");
|
|
183
|
+
printSection("Usage", [
|
|
184
|
+
" tellymcp browser install",
|
|
185
|
+
]);
|
|
186
|
+
printSection("What this command does", [
|
|
187
|
+
" Installs the bundled Playwright Chromium browser.",
|
|
188
|
+
" Uses the Playwright dependency shipped with TellyMCP.",
|
|
189
|
+
" Avoids generic npx warnings about missing local project dependencies.",
|
|
190
|
+
]);
|
|
191
|
+
}
|
|
149
192
|
function fail(message) {
|
|
150
193
|
process.stderr.write(`${message}\n`);
|
|
151
194
|
process.exit(1);
|
|
@@ -439,6 +482,7 @@ async function runDoctor(args) {
|
|
|
439
482
|
const gatewayWsUrl = parsed.GATEWAY_WS_URL?.trim();
|
|
440
483
|
const publicWebappUrl = parsed.WEBAPP_PUBLIC_URL?.trim();
|
|
441
484
|
const mcpBearerToken = parsed.MCP_HTTP_BEARER_TOKEN?.trim();
|
|
485
|
+
const browserEnabled = (parsed.BROWSER_ENABLED || "true").trim().toLowerCase() !== "false";
|
|
442
486
|
const externalHealthUrl = deriveGatewayHealthUrlFromPublicUrl(publicGatewayUrl || "") ??
|
|
443
487
|
deriveGatewayHealthUrlFromPublicUrl(publicWebappUrl || "");
|
|
444
488
|
printSection("env", [
|
|
@@ -455,6 +499,19 @@ async function runDoctor(args) {
|
|
|
455
499
|
]);
|
|
456
500
|
const checks = [];
|
|
457
501
|
const capabilities = [];
|
|
502
|
+
const playwrightStatus = await getPlaywrightBrowserStatus(browserEnabled);
|
|
503
|
+
if (!playwrightStatus.enabled) {
|
|
504
|
+
checks.push(`${picocolors_1.default.dim(" SKIP")} playwright: browser tools are disabled`);
|
|
505
|
+
capabilities.push(`${picocolors_1.default.dim(" SKIP")} browser tools: disabled`);
|
|
506
|
+
}
|
|
507
|
+
else if (playwrightStatus.installed) {
|
|
508
|
+
checks.push(`${picocolors_1.default.green(" OK")} playwright chromium: ${playwrightStatus.executablePath}`);
|
|
509
|
+
capabilities.push(`${picocolors_1.default.green(" OK")} browser tools: available`);
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
checks.push(`${picocolors_1.default.red(" ERROR")} playwright chromium: ${playwrightStatus.message}`);
|
|
513
|
+
capabilities.push(`${picocolors_1.default.red(" ERROR")} browser tools: browsers are not installed`);
|
|
514
|
+
}
|
|
458
515
|
const redisHost = (parsed.REDIS_HOST || "127.0.0.1").trim();
|
|
459
516
|
const redisPort = Number(parsed.REDIS_PORT || 6379);
|
|
460
517
|
const redisCheck = await checkTcpPort(redisHost, redisPort);
|
|
@@ -560,6 +617,39 @@ async function runDoctor(args) {
|
|
|
560
617
|
else {
|
|
561
618
|
printSection("notes", [`${picocolors_1.default.green(" OK")} No obvious local config issues detected.`]);
|
|
562
619
|
}
|
|
620
|
+
if (browserEnabled && (!playwrightStatus.enabled || !playwrightStatus.installed)) {
|
|
621
|
+
printSection("playwright", [
|
|
622
|
+
`${picocolors_1.default.yellow(" ACTION")} Install browser binaries before using browser_* tools:`,
|
|
623
|
+
" tellymcp browser install",
|
|
624
|
+
]);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
function runBrowserCommand(args) {
|
|
628
|
+
const [subcommand] = args;
|
|
629
|
+
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
630
|
+
printBrowserHelp();
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
if (subcommand !== "install") {
|
|
634
|
+
fail("Supported browser subcommands: install");
|
|
635
|
+
}
|
|
636
|
+
const cliPath = node_path_1.default.join(packageRoot, "node_modules", "playwright", "cli.js");
|
|
637
|
+
if (!(0, node_fs_1.existsSync)(cliPath)) {
|
|
638
|
+
fail(`Missing bundled Playwright CLI: ${cliPath}`);
|
|
639
|
+
}
|
|
640
|
+
printBanner("browser install", "Installing bundled Playwright Chromium");
|
|
641
|
+
const child = (0, node_child_process_1.spawn)(process.execPath, [cliPath, "install", "chromium"], {
|
|
642
|
+
cwd: process.cwd(),
|
|
643
|
+
stdio: "inherit",
|
|
644
|
+
env: process.env,
|
|
645
|
+
});
|
|
646
|
+
child.on("exit", (code, signal) => {
|
|
647
|
+
if (signal) {
|
|
648
|
+
process.kill(process.pid, signal);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
process.exit(code ?? 0);
|
|
652
|
+
});
|
|
563
653
|
}
|
|
564
654
|
function runRuntime(args) {
|
|
565
655
|
const envPath = resolveRunEnvPath(args);
|
|
@@ -578,6 +668,7 @@ function runRuntime(args) {
|
|
|
578
668
|
if (!(0, node_fs_1.existsSync)(servicesPath)) {
|
|
579
669
|
fail(`Missing compiled services: ${servicesPath}`);
|
|
580
670
|
}
|
|
671
|
+
printBanner("run", "Starting packaged runtime");
|
|
581
672
|
const tmux = getTmuxStatus();
|
|
582
673
|
if (tmux.found) {
|
|
583
674
|
process.stdout.write(`${picocolors_1.default.green("tmux detected:")} ${tmux.version}\n`);
|
|
@@ -612,7 +703,7 @@ function runRuntime(args) {
|
|
|
612
703
|
}
|
|
613
704
|
async function main(argv) {
|
|
614
705
|
const [rawCommand, firstArg, secondArg] = argv;
|
|
615
|
-
const command = rawCommand === "init" || rawCommand === "run" || rawCommand === "help" || rawCommand === "mcp" || rawCommand === "doctor"
|
|
706
|
+
const command = rawCommand === "init" || rawCommand === "run" || rawCommand === "help" || rawCommand === "mcp" || rawCommand === "doctor" || rawCommand === "browser"
|
|
616
707
|
? rawCommand
|
|
617
708
|
: "help";
|
|
618
709
|
if (command === "help" || !rawCommand || rawCommand === "--help" || rawCommand === "-h") {
|
|
@@ -631,6 +722,10 @@ async function main(argv) {
|
|
|
631
722
|
await runDoctor(argv.slice(1));
|
|
632
723
|
return;
|
|
633
724
|
}
|
|
725
|
+
if (command === "browser") {
|
|
726
|
+
runBrowserCommand(argv.slice(1));
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
634
729
|
runRuntime(argv.slice(1));
|
|
635
730
|
}
|
|
636
731
|
void main(process.argv.slice(2));
|
|
@@ -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();
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TELEGRAM_MCP_RUNTIME_SERVICE_NAME = void 0;
|
|
4
4
|
const runtime_1 = require("./src/app/bootstrap/runtime");
|
|
5
|
+
const versionHandshake_1 = require("./src/shared/lib/version/versionHandshake");
|
|
5
6
|
exports.TELEGRAM_MCP_RUNTIME_SERVICE_NAME = "telegramMcp.runtime";
|
|
6
7
|
const TelegramMcpRuntimeService = {
|
|
7
8
|
name: exports.TELEGRAM_MCP_RUNTIME_SERVICE_NAME,
|
|
@@ -17,11 +18,17 @@ const TelegramMcpRuntimeService = {
|
|
|
17
18
|
},
|
|
18
19
|
},
|
|
19
20
|
async started() {
|
|
20
|
-
this.logger.info("Starting telegram_mcp runtime service"
|
|
21
|
+
this.logger.info("Starting telegram_mcp runtime service", {
|
|
22
|
+
packageVersion: (0, versionHandshake_1.getTellyMcpPackageVersion)(__dirname),
|
|
23
|
+
protocolVersion: versionHandshake_1.TELLYMCP_PROTOCOL_VERSION,
|
|
24
|
+
});
|
|
21
25
|
this.runtime = await (0, runtime_1.createAppRuntime)({
|
|
22
26
|
callBroker: (actionName, params, options) => this.broker.call(actionName, params, options),
|
|
23
27
|
});
|
|
24
|
-
this.logger.info("telegram_mcp runtime service is ready"
|
|
28
|
+
this.logger.info("telegram_mcp runtime service is ready", {
|
|
29
|
+
packageVersion: (0, versionHandshake_1.getTellyMcpPackageVersion)(__dirname),
|
|
30
|
+
protocolVersion: versionHandshake_1.TELLYMCP_PROTOCOL_VERSION,
|
|
31
|
+
});
|
|
25
32
|
},
|
|
26
33
|
async stopped() {
|
|
27
34
|
if (!this.runtime) {
|
|
@@ -77,6 +77,8 @@ async function createAppRuntime(input) {
|
|
|
77
77
|
logger.info("Telegram transport ready");
|
|
78
78
|
await telegramTransport.recoverPendingInboxNudges();
|
|
79
79
|
logger.info("Startup inbox nudge recovery completed");
|
|
80
|
+
await telegramTransport.sendStartupNotifications();
|
|
81
|
+
logger.info("Startup Telegram notifications completed");
|
|
80
82
|
const gatewayHttpService = new gatewayHttpService_1.GatewayHttpService(config, input.callBroker);
|
|
81
83
|
return {
|
|
82
84
|
config,
|
|
@@ -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>
|
|
@@ -17,6 +17,7 @@ const collabSemantics_1 = require("./collabSemantics");
|
|
|
17
17
|
const collabUi_1 = require("./collabUi");
|
|
18
18
|
const proxyFetch_1 = require("./proxyFetch");
|
|
19
19
|
const client_1 = require("../tmux/client");
|
|
20
|
+
const versionHandshake_1 = require("../../lib/version/versionHandshake");
|
|
20
21
|
const LOCAL_INDEX_FILE_NAME = "LOCAL_INDEX.md";
|
|
21
22
|
function trimTrailingSlashes(value) {
|
|
22
23
|
return value.replace(/\/+$/u, "");
|
|
@@ -25,6 +26,14 @@ function normalizeBasePath(value) {
|
|
|
25
26
|
const trimmed = trimTrailingSlashes(value.trim());
|
|
26
27
|
return trimmed.startsWith("/") ? trimmed || "/" : `/${trimmed || ""}`;
|
|
27
28
|
}
|
|
29
|
+
function joinHttpPath(prefix, suffix) {
|
|
30
|
+
const normalizedPrefix = prefix ? normalizeBasePath(prefix) : "";
|
|
31
|
+
const normalizedSuffix = normalizeBasePath(suffix);
|
|
32
|
+
if (!normalizedPrefix || normalizedPrefix === "/") {
|
|
33
|
+
return normalizedSuffix;
|
|
34
|
+
}
|
|
35
|
+
return `${normalizedPrefix}${normalizedSuffix}`.replace(/\/{2,}/gu, "/");
|
|
36
|
+
}
|
|
28
37
|
function resolveWebAppPublicBaseUrl(config) {
|
|
29
38
|
if (!config.webapp.publicUrl) {
|
|
30
39
|
return null;
|
|
@@ -755,6 +764,92 @@ class TelegramTransport {
|
|
|
755
764
|
recoveredSessions: recoveredCount,
|
|
756
765
|
});
|
|
757
766
|
}
|
|
767
|
+
async sendStartupNotifications() {
|
|
768
|
+
const packageVersion = (0, versionHandshake_1.getTellyMcpPackageVersion)(__dirname);
|
|
769
|
+
const sessions = await this.sessionStore.listSessions();
|
|
770
|
+
const groupedRecipients = new Map();
|
|
771
|
+
for (const session of sessions) {
|
|
772
|
+
const binding = await this.bindingStore.getBinding(session.sessionId);
|
|
773
|
+
if (!binding) {
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
const key = `${binding.telegramChatId}:${binding.telegramUserId}`;
|
|
777
|
+
const current = groupedRecipients.get(key);
|
|
778
|
+
if (current) {
|
|
779
|
+
current.sessionIds.push(session.sessionId);
|
|
780
|
+
current.sessionLabels.push(session.label ?? session.sessionId);
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
groupedRecipients.set(key, {
|
|
784
|
+
binding: {
|
|
785
|
+
telegramChatId: binding.telegramChatId,
|
|
786
|
+
telegramUserId: binding.telegramUserId,
|
|
787
|
+
},
|
|
788
|
+
sessionIds: [session.sessionId],
|
|
789
|
+
sessionLabels: [session.label ?? session.sessionId],
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
if (groupedRecipients.size === 0) {
|
|
793
|
+
this.logger.info("Skipping startup notifications because no Telegram sessions are paired");
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
const runtimePort = this.config.distributed.mode === "gateway" || this.config.distributed.mode === "both"
|
|
797
|
+
? Number(process.env.PORT || this.config.mcp.httpPort)
|
|
798
|
+
: this.config.mcp.httpPort;
|
|
799
|
+
const rootPrefix = this.config.distributed.mode === "gateway" || this.config.distributed.mode === "both"
|
|
800
|
+
? normalizeBasePath(process.env.ROOT_PREFIX || "/api")
|
|
801
|
+
: "";
|
|
802
|
+
const localMcpPath = this.config.distributed.mode === "gateway" || this.config.distributed.mode === "both"
|
|
803
|
+
? joinHttpPath(rootPrefix, this.config.mcp.httpPath)
|
|
804
|
+
: this.config.mcp.httpPath;
|
|
805
|
+
const localWebappPath = this.config.distributed.mode === "gateway" || this.config.distributed.mode === "both"
|
|
806
|
+
? joinHttpPath(rootPrefix, this.config.webapp.basePath)
|
|
807
|
+
: this.config.webapp.basePath;
|
|
808
|
+
const localMcpUrl = `http://${this.config.mcp.httpHost}:${runtimePort}${localMcpPath}`;
|
|
809
|
+
const localWebappUrl = `http://${this.config.mcp.httpHost}:${runtimePort}${localWebappPath}`;
|
|
810
|
+
for (const recipientGroup of groupedRecipients.values()) {
|
|
811
|
+
const primarySessionId = recipientGroup.sessionIds[0];
|
|
812
|
+
if (!primarySessionId) {
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
const uniqueSessionLabels = Array.from(new Set(recipientGroup.sessionLabels)).sort();
|
|
816
|
+
const startupMessage = [
|
|
817
|
+
"✅ TellyMCP запущен.",
|
|
818
|
+
`Версия: ${packageVersion}`,
|
|
819
|
+
`Протокол: ${versionHandshake_1.TELLYMCP_PROTOCOL_VERSION}`,
|
|
820
|
+
`Режим: ${this.config.distributed.mode}`,
|
|
821
|
+
...(this.config.telegram.botUsername
|
|
822
|
+
? [`Бот: @${this.config.telegram.botUsername.replace(/^@/u, "")}`]
|
|
823
|
+
: []),
|
|
824
|
+
`Сессии: ${uniqueSessionLabels.join(", ")}`,
|
|
825
|
+
`MCP: ${localMcpUrl}`,
|
|
826
|
+
...(this.config.webapp.enabled ? [`WebApp: ${localWebappUrl}`] : []),
|
|
827
|
+
...(this.config.distributed.gatewayPublicUrl
|
|
828
|
+
? [`Gateway: ${this.config.distributed.gatewayPublicUrl}`]
|
|
829
|
+
: []),
|
|
830
|
+
...(this.config.distributed.gatewayWsUrl
|
|
831
|
+
? [`Gateway WS: ${this.config.distributed.gatewayWsUrl}`]
|
|
832
|
+
: []),
|
|
833
|
+
`Browser: ${this.config.browser.enabled ? (this.config.browser.headless ? "enabled, headless" : "enabled, headed") : "disabled"}`,
|
|
834
|
+
"Напиши /menu, чтобы открыть меню сессий.",
|
|
835
|
+
].join("\n");
|
|
836
|
+
try {
|
|
837
|
+
await this.sendNotification({
|
|
838
|
+
sessionId: primarySessionId,
|
|
839
|
+
sessionLabel: "TellyMCP",
|
|
840
|
+
recipient: recipientGroup.binding,
|
|
841
|
+
message: startupMessage,
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
catch (error) {
|
|
845
|
+
this.logger.warn("Failed to deliver Telegram startup notification", {
|
|
846
|
+
telegramChatId: recipientGroup.binding.telegramChatId,
|
|
847
|
+
telegramUserId: recipientGroup.binding.telegramUserId,
|
|
848
|
+
error: error instanceof Error ? error.message : String(error),
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
758
853
|
async sendRequest(input) {
|
|
759
854
|
const text = (0, messageFormat_1.formatTelegramMessage)(input, {
|
|
760
855
|
maxQuestionChars: this.config.telegram.maxQuestionChars,
|
|
@@ -253,7 +253,9 @@ async function sendAllowedTmuxAction(config, target, action) {
|
|
|
253
253
|
? "Tab"
|
|
254
254
|
: action === "escape"
|
|
255
255
|
? "Escape"
|
|
256
|
-
: "
|
|
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.
|
|
3
|
+
"version": "0.0.4",
|
|
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": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"README-ru.md",
|
|
15
15
|
"STANDALONE.md",
|
|
16
16
|
"STANDALONE-ru.md",
|
|
17
|
+
"VERSION.md",
|
|
17
18
|
"CHANGELOG.md",
|
|
18
19
|
"TOOLS.md",
|
|
19
20
|
".env.example.client",
|
|
@@ -34,6 +35,8 @@
|
|
|
34
35
|
},
|
|
35
36
|
"scripts": {
|
|
36
37
|
"postinstall": "node ./scripts/postinstall.js",
|
|
38
|
+
"prepack": "yarn build",
|
|
39
|
+
"prepublishOnly": "yarn build && yarn lint && yarn test",
|
|
37
40
|
"mcp:local": "codex mcp add telegramHuman --url http://127.0.0.1:8787/mcp",
|
|
38
41
|
"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
42
|
"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\"",
|
package/scripts/postinstall.js
CHANGED
|
@@ -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")}`);
|