@deadragdoll/tellymcp 0.0.14 → 0.0.16
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/.env.example.client +19 -39
- package/.env.example.gateway +40 -64
- package/CHANGELOG.md +31 -0
- package/README-ru.md +67 -6
- package/README.md +67 -6
- package/TOOLS.md +38 -1
- package/config/templates/env.both.template +4 -5
- package/config/templates/env.client.template +4 -17
- package/config/templates/env.gateway.template +4 -29
- package/dist/cli.js +232 -53
- package/dist/configureServer.js +966 -0
- package/dist/envMigration.js +316 -0
- package/dist/moleculer.config.js +1 -3
- package/dist/services/features/telegram-mcp/approval.service.js +1 -1
- package/dist/services/features/telegram-mcp/collaboration.service.js +2 -2
- package/dist/services/features/telegram-mcp/ensuredb.service.js +1 -1
- package/dist/services/features/telegram-mcp/gateway.service.js +1 -1
- package/dist/services/features/telegram-mcp/mcp-server.service.js +2 -0
- package/dist/services/features/telegram-mcp/session-context.service.js +25 -1
- package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +70 -66
- package/dist/services/features/telegram-mcp/src/app/config/env.js +10 -36
- package/dist/services/features/telegram-mcp/src/app/config/environmentContract.js +66 -0
- package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +28 -2
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +2 -2
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/gatewaySessionsService.js +5 -5
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +4 -4
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +0 -1
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/remoteConsoleActionClient.js +4 -3
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +4 -4
- package/dist/services/features/telegram-mcp/src/features/session-context/model/getRuntimeDiagnosticsTool.js +30 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +169 -7
- package/dist/services/features/telegram-mcp/src/shared/integrations/memory/processLocalStateStore.js +260 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +1 -1
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +1 -1
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConsoleRegistry.js +2 -2
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMessageFlow.js +1 -1
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectState.js +2 -2
- package/dist/services/features/telegram-mcp/src/shared/lib/gatewayScope.js +5 -5
- package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +10 -0
- package/docs/STANDALONE-ru.md +30 -4
- package/docs/STANDALONE.md +30 -4
- package/package.json +1 -1
- package/scripts/postinstall.js +36 -1
|
@@ -19,8 +19,7 @@ REDIS_HOST=127.0.0.1
|
|
|
19
19
|
REDIS_PORT=6379
|
|
20
20
|
REDIS_DB=1
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
PAIR_CODE_TTL_SECONDS=300
|
|
22
|
+
TELEGRAM_REQUEST_MODE=reject
|
|
24
23
|
|
|
25
24
|
MCP_HTTP_HOST=127.0.0.1
|
|
26
25
|
MCP_HTTP_PORT=8787
|
|
@@ -47,7 +46,7 @@ DISTRIBUTED_MODE=both
|
|
|
47
46
|
GATEWAY_PUBLIC_URL=
|
|
48
47
|
GATEWAY_WS_URL=
|
|
49
48
|
GATEWAY_WS_PATH=/api/gateway/ws
|
|
50
|
-
|
|
49
|
+
GATEWAY_SCOPE_TOKEN=
|
|
51
50
|
GATEWAY_USER_UUID=
|
|
52
51
|
GATEWAY_AUTH_TOKEN=
|
|
53
52
|
|
|
@@ -61,7 +60,7 @@ DB_PORT=5432
|
|
|
61
60
|
DB_USER=
|
|
62
61
|
DB_PASSWORD=
|
|
63
62
|
DB_NAME=
|
|
64
|
-
|
|
63
|
+
DB_SCHEMA=mcp
|
|
65
64
|
|
|
66
65
|
# RMQ_HOST=127.0.0.1
|
|
67
66
|
# RMQ_PORT=5672
|
|
@@ -113,4 +112,4 @@ BROWSER_SLOW_MO_MS=0
|
|
|
113
112
|
LOG_LEVEL=info
|
|
114
113
|
LOG_FILE_ENABLED=false
|
|
115
114
|
LOG_FILE_PATH=.tellymcp/log.jsonl
|
|
116
|
-
|
|
115
|
+
LOGFEED_ENABLED=0
|
|
@@ -4,12 +4,8 @@
|
|
|
4
4
|
# TELLYMCP_SESSION_ID=NEW
|
|
5
5
|
# TELLYMCP_SESSION_LABEL=NEW
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
REDIS_DB=1
|
|
10
|
-
|
|
11
|
-
MODE=reject
|
|
12
|
-
PAIR_CODE_TTL_SECONDS=300
|
|
7
|
+
# Client state is process-local. Redis is gateway-only.
|
|
8
|
+
TELEGRAM_REQUEST_MODE=reject
|
|
13
9
|
|
|
14
10
|
MCP_HTTP_HOST=127.0.0.1
|
|
15
11
|
MCP_HTTP_PORT=8787
|
|
@@ -22,7 +18,7 @@ DISTRIBUTED_MODE=client
|
|
|
22
18
|
GATEWAY_PUBLIC_URL=
|
|
23
19
|
GATEWAY_WS_URL=
|
|
24
20
|
GATEWAY_WS_PATH=/api/gateway/ws
|
|
25
|
-
|
|
21
|
+
GATEWAY_SCOPE_TOKEN=
|
|
26
22
|
GATEWAY_USER_UUID=
|
|
27
23
|
GATEWAY_AUTH_TOKEN=
|
|
28
24
|
|
|
@@ -44,15 +40,6 @@ TERMINAL_PROMPT_SCAN_INTERVAL_SECONDS=15
|
|
|
44
40
|
TERMINAL_PROMPT_SCAN_COOLDOWN_SECONDS=120
|
|
45
41
|
TERMINAL_PROMPT_SCAN_STRATEGY=strict
|
|
46
42
|
TERMINAL_PROMPT_SCAN_MIN_SCORE=5
|
|
47
|
-
WEBAPP_ENABLED=true
|
|
48
|
-
WEBAPP_BASE_PATH=/webapp
|
|
49
|
-
WEBAPP_PUBLIC_URL=
|
|
50
|
-
WEBAPP_INITDATA_TTL_SECONDS=300
|
|
51
|
-
WEBAPP_SESSION_TTL_SECONDS=900
|
|
52
|
-
WEBAPP_LAUNCH_MODE=fullscreen
|
|
53
|
-
WEBAPP_VISIBLE_SCREENS=10
|
|
54
|
-
WEBAPP_ACTION_COOLDOWN_MS=150
|
|
55
|
-
|
|
56
43
|
MCP_XCHANGE_DIR=.mcp-xchange
|
|
57
44
|
|
|
58
45
|
BROWSER_ENABLED=true
|
|
@@ -76,4 +63,4 @@ NODE_ID=agent
|
|
|
76
63
|
LOG_LEVEL=info
|
|
77
64
|
LOG_FILE_ENABLED=false
|
|
78
65
|
LOG_FILE_PATH=.tellymcp/log.jsonl
|
|
79
|
-
|
|
66
|
+
LOGFEED_ENABLED=0
|
|
@@ -15,8 +15,7 @@ REDIS_HOST=127.0.0.1
|
|
|
15
15
|
REDIS_PORT=6379
|
|
16
16
|
REDIS_DB=1
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
PAIR_CODE_TTL_SECONDS=300
|
|
18
|
+
TELEGRAM_REQUEST_MODE=reject
|
|
20
19
|
|
|
21
20
|
MCP_HTTP_HOST=127.0.0.1
|
|
22
21
|
MCP_HTTP_PORT=8787
|
|
@@ -43,7 +42,7 @@ DISTRIBUTED_MODE=gateway
|
|
|
43
42
|
GATEWAY_PUBLIC_URL=
|
|
44
43
|
GATEWAY_WS_URL=
|
|
45
44
|
GATEWAY_WS_PATH=/api/gateway/ws
|
|
46
|
-
|
|
45
|
+
GATEWAY_SCOPE_TOKEN=
|
|
47
46
|
GATEWAY_AUTH_TOKEN=
|
|
48
47
|
|
|
49
48
|
ROOT_PREFIX=/api
|
|
@@ -56,7 +55,7 @@ DB_PORT=5432
|
|
|
56
55
|
DB_USER=
|
|
57
56
|
DB_PASSWORD=
|
|
58
57
|
DB_NAME=
|
|
59
|
-
|
|
58
|
+
DB_SCHEMA=mcp
|
|
60
59
|
|
|
61
60
|
# RMQ_HOST=127.0.0.1
|
|
62
61
|
# RMQ_PORT=5672
|
|
@@ -76,36 +75,12 @@ WEBAPP_ACTION_COOLDOWN_MS=150
|
|
|
76
75
|
|
|
77
76
|
MCP_XCHANGE_DIR=.mcp-xchange
|
|
78
77
|
|
|
79
|
-
TERMINAL_SHELL=bash
|
|
80
|
-
TERMINAL_COLS=120
|
|
81
|
-
TERMINAL_ROWS=40
|
|
82
|
-
TERMINAL_SCROLLBACK_LINES=4000
|
|
83
|
-
|
|
84
|
-
TERMINAL_NUDGE_ENABLED=true
|
|
85
|
-
TERMINAL_NUDGE_DEBOUNCE_SECONDS=10
|
|
86
|
-
TERMINAL_NUDGE_COOLDOWN_SECONDS=30
|
|
87
|
-
TERMINAL_NUDGE_MESSAGE=получи newest telegram_message через list_xchange_records/get_xchange_record, выполни запрос и ответь человеку через notify_telegram; не останавливайся на анализе
|
|
88
|
-
TERMINAL_PARTNER_NUDGE_MESSAGE=получи newest partner_note через list_xchange_records/get_xchange_record, выполни задачу в текущей консоли и обязательно отправь результат через send_partner_note или send_partner_file; только потом mark_xchange_record_read
|
|
89
|
-
TERMINAL_PARTNER_REPLY_NUDGE_MESSAGE=получи newest partner_note через list_xchange_records/get_xchange_record; если newest note имеет kind=reply или не требует ответа, не отправляй новый send_partner_note/send_partner_file без явного нового запроса. Если этот reply завершает задачу от человека, сразу отправь финальный результат в Telegram через notify_telegram или send_file_to_telegram; только потом mark_xchange_record_read
|
|
90
|
-
TERMINAL_CAPTURE_MODE=visible
|
|
91
|
-
TERMINAL_CAPTURE_LINES=300
|
|
92
78
|
TERMINAL_PROMPT_SCAN_ENABLED=false
|
|
93
79
|
TERMINAL_PROMPT_SCAN_INTERVAL_SECONDS=15
|
|
94
80
|
TERMINAL_PROMPT_SCAN_COOLDOWN_SECONDS=120
|
|
95
81
|
TERMINAL_PROMPT_SCAN_STRATEGY=strict
|
|
96
82
|
TERMINAL_PROMPT_SCAN_MIN_SCORE=5
|
|
97
|
-
BROWSER_ENABLED=false
|
|
98
|
-
BROWSER_HEADLESS=false
|
|
99
|
-
BROWSER_DEVTOOLS=false
|
|
100
|
-
# BROWSER_ADDRESS=http://localhost:5173
|
|
101
|
-
BROWSER_TIMEOUT_MS=20000
|
|
102
|
-
BROWSER_MAX_EVENTS=200
|
|
103
|
-
BROWSER_WAIT_UNTIL=load
|
|
104
|
-
# BROWSER_EXECUTABLE_PATH=
|
|
105
|
-
# BROWSER_CHANNEL=chrome
|
|
106
|
-
BROWSER_SLOW_MO_MS=0
|
|
107
|
-
|
|
108
83
|
LOG_LEVEL=info
|
|
109
84
|
LOG_FILE_ENABLED=false
|
|
110
85
|
LOG_FILE_PATH=.tellymcp/log.jsonl
|
|
111
|
-
|
|
86
|
+
LOGFEED_ENABLED=0
|
package/dist/cli.js
CHANGED
|
@@ -16,10 +16,45 @@ const ws_1 = __importDefault(require("ws"));
|
|
|
16
16
|
const codexPluginInstaller_1 = require("./codexPluginInstaller");
|
|
17
17
|
const versionHandshake_1 = require("./services/features/telegram-mcp/src/shared/lib/version/versionHandshake");
|
|
18
18
|
const projectIdentity_1 = require("./services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity");
|
|
19
|
-
const
|
|
19
|
+
const envMigration_1 = require("./envMigration");
|
|
20
|
+
const configureServer_1 = require("./configureServer");
|
|
20
21
|
const distDir = __dirname;
|
|
21
22
|
const packageRoot = node_path_1.default.resolve(distDir, "..");
|
|
22
23
|
const cliPackageVersion = (0, versionHandshake_1.getTellyMcpPackageVersion)(__dirname);
|
|
24
|
+
function nativePtyRecoveryLines() {
|
|
25
|
+
return [
|
|
26
|
+
...(process.platform === "linux"
|
|
27
|
+
? [
|
|
28
|
+
"Install native build prerequisites (Debian/Ubuntu): sudo apt install -y python3 make g++",
|
|
29
|
+
]
|
|
30
|
+
: []),
|
|
31
|
+
"Ensure npm lifecycle scripts are enabled: npm config set ignore-scripts false",
|
|
32
|
+
"Remove the broken global package: npm uninstall -g @deadragdoll/tellymcp",
|
|
33
|
+
"Reinstall and compile native dependencies: npm install -g @deadragdoll/tellymcp@latest --foreground-scripts",
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
async function checkNativePtyRuntime() {
|
|
37
|
+
try {
|
|
38
|
+
const nodePty = await import("node-pty");
|
|
39
|
+
if (typeof nodePty.spawn !== "function") {
|
|
40
|
+
return {
|
|
41
|
+
available: false,
|
|
42
|
+
message: "node-pty loaded without a spawn function.",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return { available: true };
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
49
|
+
return {
|
|
50
|
+
available: false,
|
|
51
|
+
message: message.split("\n", 1)[0] || "node-pty failed to load.",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function isForegroundPtyClientMode(parsed) {
|
|
56
|
+
return (parsed.DISTRIBUTED_MODE || "client").trim() === "client";
|
|
57
|
+
}
|
|
23
58
|
function printBanner(title, subtitle) {
|
|
24
59
|
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`);
|
|
25
60
|
if (subtitle) {
|
|
@@ -66,6 +101,8 @@ function printHelp() {
|
|
|
66
101
|
printBanner("CLI", "Telegram control plane for MCP-connected coding agents");
|
|
67
102
|
printSection("Usage", [
|
|
68
103
|
" tellymcp init <client|gateway|both> [directory]",
|
|
104
|
+
" tellymcp configure [--no-open]",
|
|
105
|
+
" tellymcp migrate-env <input.env> > .migrated-env",
|
|
69
106
|
" tellymcp run [--env <file>]",
|
|
70
107
|
" tellymcp run --env=<file>",
|
|
71
108
|
" tellymcp run --env .env-client -s backendDev",
|
|
@@ -84,6 +121,8 @@ function printHelp() {
|
|
|
84
121
|
printSection("Examples", [
|
|
85
122
|
" tellymcp init client",
|
|
86
123
|
" tellymcp init gateway ./gateway-node",
|
|
124
|
+
" tellymcp configure",
|
|
125
|
+
" tellymcp migrate-env ./old.env > ./new.env",
|
|
87
126
|
" tellymcp run",
|
|
88
127
|
" tellymcp run --env .env.client",
|
|
89
128
|
" tellymcp run --env .env-client -s backendDev",
|
|
@@ -99,7 +138,7 @@ function printHelp() {
|
|
|
99
138
|
" tellymcp oauth key",
|
|
100
139
|
]);
|
|
101
140
|
printSection("terminal", [
|
|
102
|
-
|
|
141
|
+
" built-in PTY runtime (validated by tellymcp doctor and tellymcp run)",
|
|
103
142
|
" Live view, session nudges and browser flows use the built-in terminal runtime.",
|
|
104
143
|
]);
|
|
105
144
|
}
|
|
@@ -110,7 +149,9 @@ function formatMarkerEnvPath(envPath, cwd) {
|
|
|
110
149
|
const resolvedCwd = node_path_1.default.resolve(cwd);
|
|
111
150
|
const resolvedEnvPath = node_path_1.default.resolve(envPath);
|
|
112
151
|
const relativePath = node_path_1.default.relative(resolvedCwd, resolvedEnvPath);
|
|
113
|
-
return relativePath &&
|
|
152
|
+
return relativePath &&
|
|
153
|
+
!relativePath.startsWith("..") &&
|
|
154
|
+
!node_path_1.default.isAbsolute(relativePath)
|
|
114
155
|
? relativePath
|
|
115
156
|
: resolvedEnvPath;
|
|
116
157
|
}
|
|
@@ -157,7 +198,7 @@ function loadCliEnv(args) {
|
|
|
157
198
|
const sessionOverride = explicitSessionOverride ?? marker?.localSessionId ?? null;
|
|
158
199
|
const sessionLabelOverride = explicitSessionOverride
|
|
159
200
|
? explicitSessionOverride
|
|
160
|
-
: marker?.sessionLabel ?? sessionOverride ?? null;
|
|
201
|
+
: (marker?.sessionLabel ?? sessionOverride ?? null);
|
|
161
202
|
if (!(0, node_fs_1.existsSync)(envPath)) {
|
|
162
203
|
fail(`Missing env file: ${envPath}`);
|
|
163
204
|
}
|
|
@@ -263,9 +304,7 @@ function runOAuthCommand(args) {
|
|
|
263
304
|
}
|
|
264
305
|
function printBrowserHelp() {
|
|
265
306
|
printBanner("browser helper", "Manage Playwright browser binaries used by browser_* tools");
|
|
266
|
-
printSection("Usage", [
|
|
267
|
-
" tellymcp browser install",
|
|
268
|
-
]);
|
|
307
|
+
printSection("Usage", [" tellymcp browser install"]);
|
|
269
308
|
printSection("What this command does", [
|
|
270
309
|
" Installs the bundled Playwright Chromium browser.",
|
|
271
310
|
" Uses the Playwright dependency shipped with TellyMCP.",
|
|
@@ -323,12 +362,67 @@ function loadTemplate(mode) {
|
|
|
323
362
|
: "env.both.template";
|
|
324
363
|
const templatePath = node_path_1.default.join(packageRoot, templateName);
|
|
325
364
|
const nestedTemplatePath = node_path_1.default.join(packageRoot, "config", "templates", templateName);
|
|
326
|
-
const resolvedTemplatePath = (0, node_fs_1.existsSync)(templatePath)
|
|
365
|
+
const resolvedTemplatePath = (0, node_fs_1.existsSync)(templatePath)
|
|
366
|
+
? templatePath
|
|
367
|
+
: nestedTemplatePath;
|
|
327
368
|
if (!(0, node_fs_1.existsSync)(resolvedTemplatePath)) {
|
|
328
369
|
fail(`Missing packaged template: ${templateName}`);
|
|
329
370
|
}
|
|
330
371
|
return (0, node_fs_1.readFileSync)(resolvedTemplatePath, "utf8");
|
|
331
372
|
}
|
|
373
|
+
async function runConfigure(args) {
|
|
374
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
375
|
+
printBanner("configure", "Local web configurator for a TellyMCP dotenv file");
|
|
376
|
+
printSection("Usage", [
|
|
377
|
+
" tellymcp configure [--no-open]",
|
|
378
|
+
" tellymcp configure [--port 8790] [--no-open]",
|
|
379
|
+
]);
|
|
380
|
+
printSection("Safety", [
|
|
381
|
+
" The configurator binds only to 127.0.0.1 on a random free port.",
|
|
382
|
+
" Its URL contains a one-time access token.",
|
|
383
|
+
" The browser downloads .env-client or .env-gateway after validation.",
|
|
384
|
+
]);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
388
|
+
const value = args[index];
|
|
389
|
+
if (value === "--no-open" || value?.startsWith("--port=")) {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (value === "--port") {
|
|
393
|
+
index += 1;
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
fail("configure does not accept a role or output path; choose Client or Gateway in the browser wizard.");
|
|
397
|
+
}
|
|
398
|
+
const portArg = readFlagValue(args, "--port");
|
|
399
|
+
const port = portArg === null ? 0 : Number(portArg);
|
|
400
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
401
|
+
fail("--port must be an integer between 0 and 65535.");
|
|
402
|
+
}
|
|
403
|
+
printBanner("configure", "Choose Client or Gateway in the local browser wizard");
|
|
404
|
+
await (0, configureServer_1.startConfigureServer)({
|
|
405
|
+
templates: {
|
|
406
|
+
client: loadTemplate("client"),
|
|
407
|
+
gateway: loadTemplate("gateway"),
|
|
408
|
+
},
|
|
409
|
+
port,
|
|
410
|
+
open: !args.includes("--no-open"),
|
|
411
|
+
onListening(url) {
|
|
412
|
+
printSection("Configurator", [
|
|
413
|
+
` ${picocolors_1.default.green("OPEN")} ${url}`,
|
|
414
|
+
" Keep this terminal open until the file is saved.",
|
|
415
|
+
" Press Ctrl+C to cancel.",
|
|
416
|
+
]);
|
|
417
|
+
},
|
|
418
|
+
onDownloaded(filename) {
|
|
419
|
+
printSection("Download", [
|
|
420
|
+
` ${picocolors_1.default.green("OK")} ${filename} sent to the browser`,
|
|
421
|
+
` next: chmod 600 ${filename}`,
|
|
422
|
+
]);
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
}
|
|
332
426
|
function initWorkspace(mode, directoryArg) {
|
|
333
427
|
const targetDir = node_path_1.default.resolve(directoryArg ?? process.cwd());
|
|
334
428
|
(0, node_fs_1.mkdirSync)(targetDir, { recursive: true });
|
|
@@ -354,6 +448,40 @@ function initWorkspace(mode, directoryArg) {
|
|
|
354
448
|
" 3. tellymcp run",
|
|
355
449
|
]);
|
|
356
450
|
}
|
|
451
|
+
function runMigrateEnv(args) {
|
|
452
|
+
const [inputArg, extraArg] = args;
|
|
453
|
+
if (!inputArg || extraArg || inputArg === "--help" || inputArg === "-h") {
|
|
454
|
+
printBanner("migrate-env", "Normalize a legacy TellyMCP environment file");
|
|
455
|
+
printSection("Usage", [
|
|
456
|
+
" tellymcp migrate-env <input.env> > .migrated-env",
|
|
457
|
+
" The role is read from DISTRIBUTED_MODE or inferred from gateway-only keys.",
|
|
458
|
+
" Normalized dotenv is written to stdout; migration notes go to stderr.",
|
|
459
|
+
]);
|
|
460
|
+
if (!inputArg || inputArg === "--help" || inputArg === "-h") {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
fail("migrate-env expects exactly one input env file.");
|
|
464
|
+
}
|
|
465
|
+
const inputPath = node_path_1.default.resolve(process.cwd(), inputArg);
|
|
466
|
+
if (!(0, node_fs_1.existsSync)(inputPath)) {
|
|
467
|
+
fail(`Missing input env file: ${inputPath}`);
|
|
468
|
+
}
|
|
469
|
+
const result = (0, envMigration_1.migrateEnvironmentContent)((0, node_fs_1.readFileSync)(inputPath, "utf8"));
|
|
470
|
+
process.stdout.write(result.content);
|
|
471
|
+
const notes = [
|
|
472
|
+
`migrate-env: role=${result.role}`,
|
|
473
|
+
`migrate-env: kept ${result.keptKeys.length} keys from ${inputPath}`,
|
|
474
|
+
];
|
|
475
|
+
if (result.renamedKeys.length > 0) {
|
|
476
|
+
notes.push(`migrate-env: renamed ${result.renamedKeys
|
|
477
|
+
.map(({ from, to }) => `${from}->${to}`)
|
|
478
|
+
.join(", ")}`);
|
|
479
|
+
}
|
|
480
|
+
if (result.droppedKeys.length > 0) {
|
|
481
|
+
notes.push(`migrate-env: dropped ${result.droppedKeys.join(", ")}`);
|
|
482
|
+
}
|
|
483
|
+
process.stderr.write(`${notes.join("\n")}\n`);
|
|
484
|
+
}
|
|
357
485
|
function resolveRunEnvPath(args) {
|
|
358
486
|
const [firstArg, secondArg] = args;
|
|
359
487
|
const marker = getSessionMarkerForCwd(process.cwd());
|
|
@@ -424,9 +552,7 @@ function readFlagValue(args, flagName) {
|
|
|
424
552
|
return null;
|
|
425
553
|
}
|
|
426
554
|
function printMcpConfig(args) {
|
|
427
|
-
if (args.length === 0 ||
|
|
428
|
-
args.includes("--help") ||
|
|
429
|
-
args.includes("-h")) {
|
|
555
|
+
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
430
556
|
printMcpHelp();
|
|
431
557
|
return;
|
|
432
558
|
}
|
|
@@ -522,14 +648,13 @@ async function checkWebSocketUrl(url, timeoutMs = 3000) {
|
|
|
522
648
|
}
|
|
523
649
|
settled = true;
|
|
524
650
|
clearTimeout(timer);
|
|
525
|
-
|
|
526
|
-
|
|
651
|
+
resolve({ ok, message });
|
|
652
|
+
if (socket.readyState === ws_1.default.OPEN) {
|
|
527
653
|
socket.close();
|
|
528
654
|
}
|
|
529
|
-
|
|
530
|
-
|
|
655
|
+
else if (socket.readyState === ws_1.default.CONNECTING) {
|
|
656
|
+
socket.terminate();
|
|
531
657
|
}
|
|
532
|
-
resolve({ ok, message });
|
|
533
658
|
};
|
|
534
659
|
const socket = new ws_1.default(url);
|
|
535
660
|
const timer = setTimeout(() => {
|
|
@@ -542,23 +667,31 @@ async function checkWebSocketUrl(url, timeoutMs = 3000) {
|
|
|
542
667
|
socket.once("unexpected-response", (_req, response) => {
|
|
543
668
|
finish(false, `${url} returned HTTP ${response.statusCode ?? "unknown"} during WebSocket upgrade`);
|
|
544
669
|
});
|
|
545
|
-
socket.
|
|
670
|
+
socket.on("error", (error) => {
|
|
546
671
|
finish(false, `${url} failed: ${error.message}`);
|
|
547
672
|
});
|
|
548
673
|
socket.once("close", (code) => {
|
|
549
674
|
if (!opened) {
|
|
550
675
|
finish(false, `${url} closed before open (code ${code})`);
|
|
551
676
|
}
|
|
677
|
+
socket.removeAllListeners();
|
|
552
678
|
});
|
|
553
679
|
});
|
|
554
680
|
}
|
|
555
681
|
async function runDoctor(args) {
|
|
556
682
|
const { envPath, parsed } = loadCliEnv(args);
|
|
683
|
+
const nativePtyStatus = await checkNativePtyRuntime();
|
|
557
684
|
printBanner("doctor", "Local installation diagnostics");
|
|
558
685
|
printSection("terminal", [
|
|
559
|
-
|
|
686
|
+
nativePtyStatus.available
|
|
687
|
+
? `${picocolors_1.default.green(" OK")} built-in PTY runtime: native module loaded`
|
|
688
|
+
: `${picocolors_1.default.red(" ERROR")} built-in PTY runtime: ${nativePtyStatus.message}`,
|
|
689
|
+
` platform: ${process.platform}-${process.arch}, Node ${process.versions.node}`,
|
|
560
690
|
` shell: ${parsed.TERMINAL_SHELL?.trim() || process.env.SHELL || "bash"}`,
|
|
561
691
|
` size: ${parsed.TERMINAL_COLS?.trim() || "120"}x${parsed.TERMINAL_ROWS?.trim() || "40"}`,
|
|
692
|
+
...(!nativePtyStatus.available
|
|
693
|
+
? nativePtyRecoveryLines().map((line) => `${picocolors_1.default.yellow(" ACTION")} ${line}`)
|
|
694
|
+
: []),
|
|
562
695
|
]);
|
|
563
696
|
const mode = (parsed.DISTRIBUTED_MODE || "client").trim();
|
|
564
697
|
const httpHost = (parsed.MCP_HTTP_HOST || "0.0.0.0").trim();
|
|
@@ -617,10 +750,15 @@ async function runDoctor(args) {
|
|
|
617
750
|
checks.push(`${picocolors_1.default.red(" ERROR")} playwright chromium: ${playwrightStatus.message}`);
|
|
618
751
|
capabilities.push(`${picocolors_1.default.red(" ERROR")} browser tools: browsers are not installed`);
|
|
619
752
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
753
|
+
if (mode === "client") {
|
|
754
|
+
checks.push(`${picocolors_1.default.green(" OK")} local state: process-local (Redis is not required)`);
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
const redisHost = (parsed.REDIS_HOST || "127.0.0.1").trim();
|
|
758
|
+
const redisPort = Number(parsed.REDIS_PORT || 6379);
|
|
759
|
+
const redisCheck = await checkTcpPort(redisHost, redisPort);
|
|
760
|
+
checks.push(`${redisCheck.ok ? picocolors_1.default.green(" OK") : picocolors_1.default.red(" ERROR")} redis: ${redisCheck.message}`);
|
|
761
|
+
}
|
|
624
762
|
if (mode === "client") {
|
|
625
763
|
const gatewayPublicUrl = parsed.GATEWAY_PUBLIC_URL?.trim();
|
|
626
764
|
if (gatewayPublicUrl) {
|
|
@@ -720,9 +858,12 @@ async function runDoctor(args) {
|
|
|
720
858
|
printSection("notes", notes);
|
|
721
859
|
}
|
|
722
860
|
else {
|
|
723
|
-
printSection("notes", [
|
|
861
|
+
printSection("notes", [
|
|
862
|
+
`${picocolors_1.default.green(" OK")} No obvious local config issues detected.`,
|
|
863
|
+
]);
|
|
724
864
|
}
|
|
725
|
-
if (browserEnabled &&
|
|
865
|
+
if (browserEnabled &&
|
|
866
|
+
(!playwrightStatus.enabled || !playwrightStatus.installed)) {
|
|
726
867
|
printSection("playwright", [
|
|
727
868
|
`${picocolors_1.default.yellow(" ACTION")} Install browser binaries before using browser_* tools:`,
|
|
728
869
|
" tellymcp browser install",
|
|
@@ -742,7 +883,9 @@ function normalizeExtensionFlavor(rawValue) {
|
|
|
742
883
|
return null;
|
|
743
884
|
}
|
|
744
885
|
function getBundledExtensionDir(flavor) {
|
|
745
|
-
return node_path_1.default.join(packageRoot, "packages", flavor === "firefox"
|
|
886
|
+
return node_path_1.default.join(packageRoot, "packages", flavor === "firefox"
|
|
887
|
+
? "firefox-attach-extension"
|
|
888
|
+
: "chrome-attach-extension", "dist");
|
|
746
889
|
}
|
|
747
890
|
function getDefaultExtensionTargetDir(flavor) {
|
|
748
891
|
return node_path_1.default.resolve(process.cwd(), flavor === "firefox" ? "tellymcp-firefox-attach" : "tellymcp-chrome-attach");
|
|
@@ -783,7 +926,7 @@ async function runSystemPrune(args) {
|
|
|
783
926
|
const dbUser = parsed.DB_USER?.trim();
|
|
784
927
|
const dbPassword = parsed.DB_PASSWORD?.trim();
|
|
785
928
|
const dbName = parsed.DB_NAME?.trim();
|
|
786
|
-
const dbSchema = (parsed.
|
|
929
|
+
const dbSchema = (parsed.DB_SCHEMA || "mcp").trim();
|
|
787
930
|
const xchangeDir = node_path_1.default.resolve(process.cwd(), parsed.MCP_XCHANGE_DIR || ".mcp-xchange");
|
|
788
931
|
const sessionMarkerPath = node_path_1.default.resolve(process.cwd(), ".mcpsession.json");
|
|
789
932
|
const sqliteDbPath = node_path_1.default.join(xchangeDir, "xchange.sqlite3");
|
|
@@ -791,35 +934,39 @@ async function runSystemPrune(args) {
|
|
|
791
934
|
printSection("Target", [
|
|
792
935
|
` env: ${envPath}`,
|
|
793
936
|
` mode: ${mode}`,
|
|
794
|
-
|
|
937
|
+
...(mode === "client"
|
|
938
|
+
? [` state: process-local (Redis skipped)`]
|
|
939
|
+
: [` redis: ${redisHost}:${redisPort}/${redisDb}`]),
|
|
795
940
|
...(dbHost && dbUser && dbName
|
|
796
941
|
? [` postgres: ${dbHost}:${dbPort}/${dbName} schema ${dbSchema}`]
|
|
797
942
|
: [` postgres: ${picocolors_1.default.dim("skipped (not configured)")}`]),
|
|
798
943
|
` xchange dir: ${xchangeDir}`,
|
|
799
944
|
` session marker: ${sessionMarkerPath}`,
|
|
800
945
|
]);
|
|
801
|
-
const redis = new ioredis_1.default({
|
|
802
|
-
host: redisHost,
|
|
803
|
-
port: redisPort,
|
|
804
|
-
db: redisDb,
|
|
805
|
-
...(redisUsername ? { username: redisUsername } : {}),
|
|
806
|
-
...(redisPassword ? { password: redisPassword } : {}),
|
|
807
|
-
maxRetriesPerRequest: 1,
|
|
808
|
-
enableReadyCheck: true,
|
|
809
|
-
});
|
|
810
946
|
let deletedRedisKeys = 0;
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
947
|
+
if (mode !== "client") {
|
|
948
|
+
const redis = new ioredis_1.default({
|
|
949
|
+
host: redisHost,
|
|
950
|
+
port: redisPort,
|
|
951
|
+
db: redisDb,
|
|
952
|
+
...(redisUsername ? { username: redisUsername } : {}),
|
|
953
|
+
...(redisPassword ? { password: redisPassword } : {}),
|
|
954
|
+
maxRetriesPerRequest: 1,
|
|
955
|
+
enableReadyCheck: true,
|
|
956
|
+
});
|
|
957
|
+
try {
|
|
958
|
+
let cursor = "0";
|
|
959
|
+
do {
|
|
960
|
+
const [nextCursor, keys] = await redis.scan(cursor, "MATCH", "telegram-mcp:*", "COUNT", 500);
|
|
961
|
+
cursor = nextCursor;
|
|
962
|
+
if (keys.length > 0) {
|
|
963
|
+
deletedRedisKeys += await redis.del(...keys);
|
|
964
|
+
}
|
|
965
|
+
} while (cursor !== "0");
|
|
966
|
+
}
|
|
967
|
+
finally {
|
|
968
|
+
redis.disconnect();
|
|
969
|
+
}
|
|
823
970
|
}
|
|
824
971
|
let truncatedTables = [];
|
|
825
972
|
if (dbHost && dbUser && dbName) {
|
|
@@ -1013,8 +1160,19 @@ async function runRuntime(args) {
|
|
|
1013
1160
|
sessionId: parsed.TELLYMCP_SESSION_ID,
|
|
1014
1161
|
sessionLabel: parsed.TELLYMCP_SESSION_LABEL,
|
|
1015
1162
|
});
|
|
1016
|
-
|
|
1017
|
-
|
|
1163
|
+
const nativePtyStatus = await checkNativePtyRuntime();
|
|
1164
|
+
if (!nativePtyStatus.available) {
|
|
1165
|
+
fail([
|
|
1166
|
+
`Built-in PTY runtime is unavailable on ${process.platform}-${process.arch} with Node ${process.versions.node}.`,
|
|
1167
|
+
nativePtyStatus.message,
|
|
1168
|
+
"",
|
|
1169
|
+
...nativePtyRecoveryLines(),
|
|
1170
|
+
"Then rerun: tellymcp doctor --env <file>",
|
|
1171
|
+
].join("\n"));
|
|
1172
|
+
}
|
|
1173
|
+
if (isForegroundPtyClientMode(parsed)) {
|
|
1174
|
+
const { runForegroundPtyRuntime } = await import("./services/features/telegram-mcp/src/features/foreground-terminal/model/foregroundTerminalRuntime.js");
|
|
1175
|
+
await runForegroundPtyRuntime({
|
|
1018
1176
|
envPath,
|
|
1019
1177
|
packageRoot,
|
|
1020
1178
|
printBanner,
|
|
@@ -1068,11 +1226,24 @@ async function runRuntime(args) {
|
|
|
1068
1226
|
}
|
|
1069
1227
|
async function main(argv) {
|
|
1070
1228
|
const [rawCommand, firstArg, secondArg] = argv;
|
|
1071
|
-
const command = rawCommand === "init" ||
|
|
1072
|
-
|
|
1229
|
+
const command = rawCommand === "init" ||
|
|
1230
|
+
rawCommand === "configure" ||
|
|
1231
|
+
rawCommand === "migrate-env" ||
|
|
1232
|
+
rawCommand === "run" ||
|
|
1233
|
+
rawCommand === "help" ||
|
|
1234
|
+
rawCommand === "mcp" ||
|
|
1235
|
+
rawCommand === "oauth" ||
|
|
1236
|
+
rawCommand === "doctor" ||
|
|
1237
|
+
rawCommand === "browser" ||
|
|
1238
|
+
rawCommand === "system-prune" ||
|
|
1239
|
+
rawCommand === "codex-plugin" ||
|
|
1240
|
+
rawCommand === "extension"
|
|
1073
1241
|
? rawCommand
|
|
1074
1242
|
: "help";
|
|
1075
|
-
if (command === "help" ||
|
|
1243
|
+
if (command === "help" ||
|
|
1244
|
+
!rawCommand ||
|
|
1245
|
+
rawCommand === "--help" ||
|
|
1246
|
+
rawCommand === "-h") {
|
|
1076
1247
|
printHelp();
|
|
1077
1248
|
return;
|
|
1078
1249
|
}
|
|
@@ -1080,6 +1251,14 @@ async function main(argv) {
|
|
|
1080
1251
|
initWorkspace(ensureMode(firstArg), secondArg);
|
|
1081
1252
|
return;
|
|
1082
1253
|
}
|
|
1254
|
+
if (command === "configure") {
|
|
1255
|
+
await runConfigure(argv.slice(1));
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
if (command === "migrate-env") {
|
|
1259
|
+
runMigrateEnv(argv.slice(1));
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1083
1262
|
if (command === "mcp") {
|
|
1084
1263
|
printMcpConfig(argv.slice(1));
|
|
1085
1264
|
return;
|