@deadragdoll/tellymcp 0.0.13 → 0.0.15
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 +23 -39
- package/.env.example.gateway +52 -61
- package/CHANGELOG.md +57 -0
- package/README-ru.md +80 -6
- package/README.md +80 -6
- package/TOOLS.md +219 -10
- package/config/templates/env.both.template +19 -7
- package/config/templates/env.client.template +9 -19
- package/config/templates/env.gateway.template +19 -31
- package/dist/cli.js +252 -47
- 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/browser.service.js +18 -0
- 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/file-content.service.js +94 -0
- package/dist/services/features/telegram-mcp/gateway-delivery.service.js +5 -1
- package/dist/services/features/telegram-mcp/gateway-socket.service.js +43 -11
- package/dist/services/features/telegram-mcp/gateway.service.js +1 -1
- package/dist/services/features/telegram-mcp/mcp-http.service.js +1 -0
- package/dist/services/features/telegram-mcp/mcp-server.service.js +20 -0
- package/dist/services/features/telegram-mcp/session-context.service.js +25 -1
- package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +79 -67
- package/dist/services/features/telegram-mcp/src/app/config/env.js +129 -38
- package/dist/services/features/telegram-mcp/src/app/config/environmentContract.js +66 -0
- package/dist/services/features/telegram-mcp/src/app/http.js +139 -99
- package/dist/services/features/telegram-mcp/src/app/oauthFacade.js +642 -0
- package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +151 -170
- package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +96 -99
- package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +88 -19
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachActiveTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserDetachTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserListAttachedInstancesTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserListTabsTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +412 -29
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/browserRecordingBundle.js +37 -3
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachRegistry.js +7 -6
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachServer.js +214 -37
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/types.js +186 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/collaborationService.js +2 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/gatewaySessionsService.js +5 -5
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +6 -3
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +5 -2
- 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 +33 -36
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/remoteConsoleActionClient.js +4 -3
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileListTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileService.js +327 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileTool.js +81 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/temporaryFileLinkStore.js +307 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/workspaceFilePolicy.js +115 -0
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +9 -5
- 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 +4 -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/transportFileHandoffActions.js +6 -3
- 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/integrations/terminal/client.js +29 -6
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +100 -2
- package/dist/services/features/telegram-mcp/src/shared/lib/bodyLimits.js +63 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/gatewayAuth.js +13 -0
- 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/dist/services/features/telegram-mcp/src/shared/lib/time/localTimestamp.js +21 -0
- package/docs/CHAT_CONNECTOR.md +134 -0
- package/docs/STANDALONE-ru.md +41 -3
- package/docs/STANDALONE.md +41 -3
- package/package.json +5 -3
- package/packages/chrome-attach-extension/dist/background.js +572 -163
- package/packages/chrome-attach-extension/dist/manifest.json +2 -1
- package/packages/chrome-attach-extension/dist/options.js +15 -2
- package/packages/chrome-attach-extension/dist/recorder-content.js +14 -1
- package/packages/chrome-attach-extension/dist/recorder-page.js +34 -18
- package/packages/firefox-attach-extension/dist/background.js +413 -33
- package/packages/firefox-attach-extension/dist/manifest.json +0 -12
- package/packages/firefox-attach-extension/dist/options.js +14 -1
- package/packages/firefox-attach-extension/dist/recorder-content.js +14 -1
- package/packages/firefox-attach-extension/dist/recorder-page.js +34 -18
- package/scripts/postinstall.js +33 -1
|
@@ -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
|
|
@@ -25,12 +24,26 @@ MCP_HTTP_PATH=/mcp
|
|
|
25
24
|
MCP_HTTP_ENABLE_DEBUG_ROUTES=false
|
|
26
25
|
MCP_HTTP_ENABLE_PRUNE_ROUTE=false
|
|
27
26
|
|
|
27
|
+
# ChatGPT / Claude OAuth connector. Setting any OAuth value enables the facade.
|
|
28
|
+
# TELLYMCP_PUBLIC_URL=https://your-domain.example/api
|
|
29
|
+
# TELLYMCP_OAUTH_ISSUER=https://your-domain.example/api
|
|
30
|
+
# TELLYMCP_OAUTH_AUDIENCE=https://your-domain.example/api
|
|
31
|
+
# TELLYMCP_MAGIC_TOKEN=change_me_private_connector_token
|
|
32
|
+
# TELLYMCP_MAGIC_TOKEN_HASH=sha256:<hex>
|
|
33
|
+
# TELLYMCP_OAUTH_CLIENT_ID=tellymcp
|
|
34
|
+
# TELLYMCP_OAUTH_CLIENT_SECRET=
|
|
35
|
+
# TELLYMCP_ALLOWED_REDIRECT_URIS=https://chatgpt.com/connector/oauth/...,https://claude.ai/api/mcp/auth_callback
|
|
36
|
+
# TELLYMCP_OAUTH_PRIVATE_KEY_PEM="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
|
|
37
|
+
# TELLYMCP_AUTH_CODE_TTL_SECONDS=300
|
|
38
|
+
# TELLYMCP_OAUTH_SCOPES=tellymcp:read tellymcp:write
|
|
39
|
+
# TELLYMCP_OAUTH_KEY_ID=tellymcp-oauth
|
|
40
|
+
|
|
28
41
|
DISTRIBUTED_MODE=gateway
|
|
29
42
|
GATEWAY_PUBLIC_URL=
|
|
30
43
|
GATEWAY_WS_URL=
|
|
31
44
|
GATEWAY_WS_PATH=/api/gateway/ws
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
GATEWAY_SCOPE_TOKEN=
|
|
46
|
+
GATEWAY_AUTH_TOKEN=
|
|
34
47
|
|
|
35
48
|
ROOT_PREFIX=/api
|
|
36
49
|
PORT=8080
|
|
@@ -42,7 +55,7 @@ DB_PORT=5432
|
|
|
42
55
|
DB_USER=
|
|
43
56
|
DB_PASSWORD=
|
|
44
57
|
DB_NAME=
|
|
45
|
-
|
|
58
|
+
DB_SCHEMA=mcp
|
|
46
59
|
|
|
47
60
|
# RMQ_HOST=127.0.0.1
|
|
48
61
|
# RMQ_PORT=5672
|
|
@@ -58,41 +71,16 @@ WEBAPP_INITDATA_TTL_SECONDS=300
|
|
|
58
71
|
WEBAPP_SESSION_TTL_SECONDS=900
|
|
59
72
|
WEBAPP_LAUNCH_MODE=fullscreen
|
|
60
73
|
WEBAPP_VISIBLE_SCREENS=10
|
|
61
|
-
WEBAPP_POLL_INTERVAL_MS=2000
|
|
62
74
|
WEBAPP_ACTION_COOLDOWN_MS=150
|
|
63
75
|
|
|
64
76
|
MCP_XCHANGE_DIR=.mcp-xchange
|
|
65
77
|
|
|
66
|
-
TERMINAL_SHELL=bash
|
|
67
|
-
TERMINAL_COLS=120
|
|
68
|
-
TERMINAL_ROWS=40
|
|
69
|
-
TERMINAL_SCROLLBACK_LINES=4000
|
|
70
|
-
|
|
71
|
-
TERMINAL_NUDGE_ENABLED=true
|
|
72
|
-
TERMINAL_NUDGE_DEBOUNCE_SECONDS=10
|
|
73
|
-
TERMINAL_NUDGE_COOLDOWN_SECONDS=30
|
|
74
|
-
TERMINAL_NUDGE_MESSAGE=получи newest telegram_message через list_xchange_records/get_xchange_record, выполни запрос и ответь человеку через notify_telegram; не останавливайся на анализе
|
|
75
|
-
TERMINAL_PARTNER_NUDGE_MESSAGE=получи newest partner_note через list_xchange_records/get_xchange_record, выполни задачу в текущей консоли и обязательно отправь результат через send_partner_note или send_partner_file; только потом mark_xchange_record_read
|
|
76
|
-
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
|
|
77
|
-
TERMINAL_CAPTURE_MODE=visible
|
|
78
|
-
TERMINAL_CAPTURE_LINES=300
|
|
79
78
|
TERMINAL_PROMPT_SCAN_ENABLED=false
|
|
80
79
|
TERMINAL_PROMPT_SCAN_INTERVAL_SECONDS=15
|
|
81
80
|
TERMINAL_PROMPT_SCAN_COOLDOWN_SECONDS=120
|
|
82
81
|
TERMINAL_PROMPT_SCAN_STRATEGY=strict
|
|
83
82
|
TERMINAL_PROMPT_SCAN_MIN_SCORE=5
|
|
84
|
-
BROWSER_ENABLED=false
|
|
85
|
-
BROWSER_HEADLESS=false
|
|
86
|
-
BROWSER_DEVTOOLS=false
|
|
87
|
-
# BROWSER_ADDRESS=http://localhost:5173
|
|
88
|
-
BROWSER_TIMEOUT_MS=20000
|
|
89
|
-
BROWSER_MAX_EVENTS=200
|
|
90
|
-
BROWSER_WAIT_UNTIL=load
|
|
91
|
-
# BROWSER_EXECUTABLE_PATH=
|
|
92
|
-
# BROWSER_CHANNEL=chrome
|
|
93
|
-
BROWSER_SLOW_MO_MS=0
|
|
94
|
-
|
|
95
83
|
LOG_LEVEL=info
|
|
96
84
|
LOG_FILE_ENABLED=false
|
|
97
85
|
LOG_FILE_PATH=.tellymcp/log.jsonl
|
|
98
|
-
|
|
86
|
+
LOGFEED_ENABLED=0
|
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
const node_fs_1 = require("node:fs");
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const node_child_process_1 = require("node:child_process");
|
|
10
|
+
const node_crypto_1 = require("node:crypto");
|
|
10
11
|
const node_net_1 = __importDefault(require("node:net"));
|
|
11
12
|
const dotenv_1 = require("dotenv");
|
|
12
13
|
const ioredis_1 = __importDefault(require("ioredis"));
|
|
@@ -15,10 +16,44 @@ const ws_1 = __importDefault(require("ws"));
|
|
|
15
16
|
const codexPluginInstaller_1 = require("./codexPluginInstaller");
|
|
16
17
|
const versionHandshake_1 = require("./services/features/telegram-mcp/src/shared/lib/version/versionHandshake");
|
|
17
18
|
const projectIdentity_1 = require("./services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity");
|
|
18
|
-
const
|
|
19
|
+
const envMigration_1 = require("./envMigration");
|
|
20
|
+
const configureServer_1 = require("./configureServer");
|
|
19
21
|
const distDir = __dirname;
|
|
20
22
|
const packageRoot = node_path_1.default.resolve(distDir, "..");
|
|
21
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
|
+
"Rebuild the global package: npm rebuild -g @deadragdoll/tellymcp --foreground-scripts",
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
async function checkNativePtyRuntime() {
|
|
36
|
+
try {
|
|
37
|
+
const nodePty = await import("node-pty");
|
|
38
|
+
if (typeof nodePty.spawn !== "function") {
|
|
39
|
+
return {
|
|
40
|
+
available: false,
|
|
41
|
+
message: "node-pty loaded without a spawn function.",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { available: true };
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
48
|
+
return {
|
|
49
|
+
available: false,
|
|
50
|
+
message: message.split("\n", 1)[0] || "node-pty failed to load.",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function isForegroundPtyClientMode(parsed) {
|
|
55
|
+
return (parsed.DISTRIBUTED_MODE || "client").trim() === "client";
|
|
56
|
+
}
|
|
22
57
|
function printBanner(title, subtitle) {
|
|
23
58
|
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`);
|
|
24
59
|
if (subtitle) {
|
|
@@ -65,6 +100,8 @@ function printHelp() {
|
|
|
65
100
|
printBanner("CLI", "Telegram control plane for MCP-connected coding agents");
|
|
66
101
|
printSection("Usage", [
|
|
67
102
|
" tellymcp init <client|gateway|both> [directory]",
|
|
103
|
+
" tellymcp configure [--no-open]",
|
|
104
|
+
" tellymcp migrate-env <input.env> > .migrated-env",
|
|
68
105
|
" tellymcp run [--env <file>]",
|
|
69
106
|
" tellymcp run --env=<file>",
|
|
70
107
|
" tellymcp run --env .env-client -s backendDev",
|
|
@@ -77,11 +114,14 @@ function printHelp() {
|
|
|
77
114
|
" tellymcp codex-plugin install",
|
|
78
115
|
" tellymcp codex-plugin status",
|
|
79
116
|
" tellymcp mcp [--url <url>] [--bearer <token>] [--format claude|legacy]",
|
|
117
|
+
" tellymcp oauth key",
|
|
80
118
|
" tellymcp help",
|
|
81
119
|
]);
|
|
82
120
|
printSection("Examples", [
|
|
83
121
|
" tellymcp init client",
|
|
84
122
|
" tellymcp init gateway ./gateway-node",
|
|
123
|
+
" tellymcp configure",
|
|
124
|
+
" tellymcp migrate-env ./old.env > ./new.env",
|
|
85
125
|
" tellymcp run",
|
|
86
126
|
" tellymcp run --env .env.client",
|
|
87
127
|
" tellymcp run --env .env-client -s backendDev",
|
|
@@ -94,9 +134,10 @@ function printHelp() {
|
|
|
94
134
|
" tellymcp codex-plugin install",
|
|
95
135
|
" tellymcp codex-plugin status",
|
|
96
136
|
" tellymcp mcp --help",
|
|
137
|
+
" tellymcp oauth key",
|
|
97
138
|
]);
|
|
98
139
|
printSection("terminal", [
|
|
99
|
-
|
|
140
|
+
" built-in PTY runtime (validated by tellymcp doctor and tellymcp run)",
|
|
100
141
|
" Live view, session nudges and browser flows use the built-in terminal runtime.",
|
|
101
142
|
]);
|
|
102
143
|
}
|
|
@@ -107,7 +148,9 @@ function formatMarkerEnvPath(envPath, cwd) {
|
|
|
107
148
|
const resolvedCwd = node_path_1.default.resolve(cwd);
|
|
108
149
|
const resolvedEnvPath = node_path_1.default.resolve(envPath);
|
|
109
150
|
const relativePath = node_path_1.default.relative(resolvedCwd, resolvedEnvPath);
|
|
110
|
-
return relativePath &&
|
|
151
|
+
return relativePath &&
|
|
152
|
+
!relativePath.startsWith("..") &&
|
|
153
|
+
!node_path_1.default.isAbsolute(relativePath)
|
|
111
154
|
? relativePath
|
|
112
155
|
: resolvedEnvPath;
|
|
113
156
|
}
|
|
@@ -154,7 +197,7 @@ function loadCliEnv(args) {
|
|
|
154
197
|
const sessionOverride = explicitSessionOverride ?? marker?.localSessionId ?? null;
|
|
155
198
|
const sessionLabelOverride = explicitSessionOverride
|
|
156
199
|
? explicitSessionOverride
|
|
157
|
-
: marker?.sessionLabel ?? sessionOverride ?? null;
|
|
200
|
+
: (marker?.sessionLabel ?? sessionOverride ?? null);
|
|
158
201
|
if (!(0, node_fs_1.existsSync)(envPath)) {
|
|
159
202
|
fail(`Missing env file: ${envPath}`);
|
|
160
203
|
}
|
|
@@ -238,11 +281,29 @@ function printMcpHelp() {
|
|
|
238
281
|
" tellymcp mcp --url https://builder.undoo.ru/api/mcp --format legacy",
|
|
239
282
|
]);
|
|
240
283
|
}
|
|
284
|
+
function runOAuthCommand(args) {
|
|
285
|
+
const [subcommand] = args;
|
|
286
|
+
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
287
|
+
printBanner("OAuth helper", "Generate connector signing material");
|
|
288
|
+
printSection("Usage", [" tellymcp oauth key"]);
|
|
289
|
+
printSection("Output", [
|
|
290
|
+
" Prints a dotenv-ready TELLYMCP_OAUTH_PRIVATE_KEY_PEM value.",
|
|
291
|
+
" Store it only on the gateway and never expose it to chat clients.",
|
|
292
|
+
]);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (subcommand !== "key") {
|
|
296
|
+
fail("Supported OAuth subcommands: key");
|
|
297
|
+
}
|
|
298
|
+
const privateKeyPem = (0, node_crypto_1.generateKeyPairSync)("rsa", { modulusLength: 2048 })
|
|
299
|
+
.privateKey.export({ type: "pkcs8", format: "pem" })
|
|
300
|
+
.toString()
|
|
301
|
+
.replace(/\n/gu, "\\n");
|
|
302
|
+
process.stdout.write(`TELLYMCP_OAUTH_PRIVATE_KEY_PEM="${privateKeyPem}"\n`);
|
|
303
|
+
}
|
|
241
304
|
function printBrowserHelp() {
|
|
242
305
|
printBanner("browser helper", "Manage Playwright browser binaries used by browser_* tools");
|
|
243
|
-
printSection("Usage", [
|
|
244
|
-
" tellymcp browser install",
|
|
245
|
-
]);
|
|
306
|
+
printSection("Usage", [" tellymcp browser install"]);
|
|
246
307
|
printSection("What this command does", [
|
|
247
308
|
" Installs the bundled Playwright Chromium browser.",
|
|
248
309
|
" Uses the Playwright dependency shipped with TellyMCP.",
|
|
@@ -300,12 +361,67 @@ function loadTemplate(mode) {
|
|
|
300
361
|
: "env.both.template";
|
|
301
362
|
const templatePath = node_path_1.default.join(packageRoot, templateName);
|
|
302
363
|
const nestedTemplatePath = node_path_1.default.join(packageRoot, "config", "templates", templateName);
|
|
303
|
-
const resolvedTemplatePath = (0, node_fs_1.existsSync)(templatePath)
|
|
364
|
+
const resolvedTemplatePath = (0, node_fs_1.existsSync)(templatePath)
|
|
365
|
+
? templatePath
|
|
366
|
+
: nestedTemplatePath;
|
|
304
367
|
if (!(0, node_fs_1.existsSync)(resolvedTemplatePath)) {
|
|
305
368
|
fail(`Missing packaged template: ${templateName}`);
|
|
306
369
|
}
|
|
307
370
|
return (0, node_fs_1.readFileSync)(resolvedTemplatePath, "utf8");
|
|
308
371
|
}
|
|
372
|
+
async function runConfigure(args) {
|
|
373
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
374
|
+
printBanner("configure", "Local web configurator for a TellyMCP dotenv file");
|
|
375
|
+
printSection("Usage", [
|
|
376
|
+
" tellymcp configure [--no-open]",
|
|
377
|
+
" tellymcp configure [--port 8790] [--no-open]",
|
|
378
|
+
]);
|
|
379
|
+
printSection("Safety", [
|
|
380
|
+
" The configurator binds only to 127.0.0.1 on a random free port.",
|
|
381
|
+
" Its URL contains a one-time access token.",
|
|
382
|
+
" The browser downloads .env-client or .env-gateway after validation.",
|
|
383
|
+
]);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
387
|
+
const value = args[index];
|
|
388
|
+
if (value === "--no-open" || value?.startsWith("--port=")) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (value === "--port") {
|
|
392
|
+
index += 1;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
fail("configure does not accept a role or output path; choose Client or Gateway in the browser wizard.");
|
|
396
|
+
}
|
|
397
|
+
const portArg = readFlagValue(args, "--port");
|
|
398
|
+
const port = portArg === null ? 0 : Number(portArg);
|
|
399
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
400
|
+
fail("--port must be an integer between 0 and 65535.");
|
|
401
|
+
}
|
|
402
|
+
printBanner("configure", "Choose Client or Gateway in the local browser wizard");
|
|
403
|
+
await (0, configureServer_1.startConfigureServer)({
|
|
404
|
+
templates: {
|
|
405
|
+
client: loadTemplate("client"),
|
|
406
|
+
gateway: loadTemplate("gateway"),
|
|
407
|
+
},
|
|
408
|
+
port,
|
|
409
|
+
open: !args.includes("--no-open"),
|
|
410
|
+
onListening(url) {
|
|
411
|
+
printSection("Configurator", [
|
|
412
|
+
` ${picocolors_1.default.green("OPEN")} ${url}`,
|
|
413
|
+
" Keep this terminal open until the file is saved.",
|
|
414
|
+
" Press Ctrl+C to cancel.",
|
|
415
|
+
]);
|
|
416
|
+
},
|
|
417
|
+
onDownloaded(filename) {
|
|
418
|
+
printSection("Download", [
|
|
419
|
+
` ${picocolors_1.default.green("OK")} ${filename} sent to the browser`,
|
|
420
|
+
` next: chmod 600 ${filename}`,
|
|
421
|
+
]);
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
}
|
|
309
425
|
function initWorkspace(mode, directoryArg) {
|
|
310
426
|
const targetDir = node_path_1.default.resolve(directoryArg ?? process.cwd());
|
|
311
427
|
(0, node_fs_1.mkdirSync)(targetDir, { recursive: true });
|
|
@@ -331,6 +447,40 @@ function initWorkspace(mode, directoryArg) {
|
|
|
331
447
|
" 3. tellymcp run",
|
|
332
448
|
]);
|
|
333
449
|
}
|
|
450
|
+
function runMigrateEnv(args) {
|
|
451
|
+
const [inputArg, extraArg] = args;
|
|
452
|
+
if (!inputArg || extraArg || inputArg === "--help" || inputArg === "-h") {
|
|
453
|
+
printBanner("migrate-env", "Normalize a legacy TellyMCP environment file");
|
|
454
|
+
printSection("Usage", [
|
|
455
|
+
" tellymcp migrate-env <input.env> > .migrated-env",
|
|
456
|
+
" The role is read from DISTRIBUTED_MODE or inferred from gateway-only keys.",
|
|
457
|
+
" Normalized dotenv is written to stdout; migration notes go to stderr.",
|
|
458
|
+
]);
|
|
459
|
+
if (!inputArg || inputArg === "--help" || inputArg === "-h") {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
fail("migrate-env expects exactly one input env file.");
|
|
463
|
+
}
|
|
464
|
+
const inputPath = node_path_1.default.resolve(process.cwd(), inputArg);
|
|
465
|
+
if (!(0, node_fs_1.existsSync)(inputPath)) {
|
|
466
|
+
fail(`Missing input env file: ${inputPath}`);
|
|
467
|
+
}
|
|
468
|
+
const result = (0, envMigration_1.migrateEnvironmentContent)((0, node_fs_1.readFileSync)(inputPath, "utf8"));
|
|
469
|
+
process.stdout.write(result.content);
|
|
470
|
+
const notes = [
|
|
471
|
+
`migrate-env: role=${result.role}`,
|
|
472
|
+
`migrate-env: kept ${result.keptKeys.length} keys from ${inputPath}`,
|
|
473
|
+
];
|
|
474
|
+
if (result.renamedKeys.length > 0) {
|
|
475
|
+
notes.push(`migrate-env: renamed ${result.renamedKeys
|
|
476
|
+
.map(({ from, to }) => `${from}->${to}`)
|
|
477
|
+
.join(", ")}`);
|
|
478
|
+
}
|
|
479
|
+
if (result.droppedKeys.length > 0) {
|
|
480
|
+
notes.push(`migrate-env: dropped ${result.droppedKeys.join(", ")}`);
|
|
481
|
+
}
|
|
482
|
+
process.stderr.write(`${notes.join("\n")}\n`);
|
|
483
|
+
}
|
|
334
484
|
function resolveRunEnvPath(args) {
|
|
335
485
|
const [firstArg, secondArg] = args;
|
|
336
486
|
const marker = getSessionMarkerForCwd(process.cwd());
|
|
@@ -401,9 +551,7 @@ function readFlagValue(args, flagName) {
|
|
|
401
551
|
return null;
|
|
402
552
|
}
|
|
403
553
|
function printMcpConfig(args) {
|
|
404
|
-
if (args.length === 0 ||
|
|
405
|
-
args.includes("--help") ||
|
|
406
|
-
args.includes("-h")) {
|
|
554
|
+
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
407
555
|
printMcpHelp();
|
|
408
556
|
return;
|
|
409
557
|
}
|
|
@@ -531,11 +679,18 @@ async function checkWebSocketUrl(url, timeoutMs = 3000) {
|
|
|
531
679
|
}
|
|
532
680
|
async function runDoctor(args) {
|
|
533
681
|
const { envPath, parsed } = loadCliEnv(args);
|
|
682
|
+
const nativePtyStatus = await checkNativePtyRuntime();
|
|
534
683
|
printBanner("doctor", "Local installation diagnostics");
|
|
535
684
|
printSection("terminal", [
|
|
536
|
-
|
|
685
|
+
nativePtyStatus.available
|
|
686
|
+
? `${picocolors_1.default.green(" OK")} built-in PTY runtime: native module loaded`
|
|
687
|
+
: `${picocolors_1.default.red(" ERROR")} built-in PTY runtime: ${nativePtyStatus.message}`,
|
|
688
|
+
` platform: ${process.platform}-${process.arch}, Node ${process.versions.node}`,
|
|
537
689
|
` shell: ${parsed.TERMINAL_SHELL?.trim() || process.env.SHELL || "bash"}`,
|
|
538
690
|
` size: ${parsed.TERMINAL_COLS?.trim() || "120"}x${parsed.TERMINAL_ROWS?.trim() || "40"}`,
|
|
691
|
+
...(!nativePtyStatus.available
|
|
692
|
+
? nativePtyRecoveryLines().map((line) => `${picocolors_1.default.yellow(" ACTION")} ${line}`)
|
|
693
|
+
: []),
|
|
539
694
|
]);
|
|
540
695
|
const mode = (parsed.DISTRIBUTED_MODE || "client").trim();
|
|
541
696
|
const httpHost = (parsed.MCP_HTTP_HOST || "0.0.0.0").trim();
|
|
@@ -594,10 +749,15 @@ async function runDoctor(args) {
|
|
|
594
749
|
checks.push(`${picocolors_1.default.red(" ERROR")} playwright chromium: ${playwrightStatus.message}`);
|
|
595
750
|
capabilities.push(`${picocolors_1.default.red(" ERROR")} browser tools: browsers are not installed`);
|
|
596
751
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
752
|
+
if (mode === "client") {
|
|
753
|
+
checks.push(`${picocolors_1.default.green(" OK")} local state: process-local (Redis is not required)`);
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
const redisHost = (parsed.REDIS_HOST || "127.0.0.1").trim();
|
|
757
|
+
const redisPort = Number(parsed.REDIS_PORT || 6379);
|
|
758
|
+
const redisCheck = await checkTcpPort(redisHost, redisPort);
|
|
759
|
+
checks.push(`${redisCheck.ok ? picocolors_1.default.green(" OK") : picocolors_1.default.red(" ERROR")} redis: ${redisCheck.message}`);
|
|
760
|
+
}
|
|
601
761
|
if (mode === "client") {
|
|
602
762
|
const gatewayPublicUrl = parsed.GATEWAY_PUBLIC_URL?.trim();
|
|
603
763
|
if (gatewayPublicUrl) {
|
|
@@ -697,9 +857,12 @@ async function runDoctor(args) {
|
|
|
697
857
|
printSection("notes", notes);
|
|
698
858
|
}
|
|
699
859
|
else {
|
|
700
|
-
printSection("notes", [
|
|
860
|
+
printSection("notes", [
|
|
861
|
+
`${picocolors_1.default.green(" OK")} No obvious local config issues detected.`,
|
|
862
|
+
]);
|
|
701
863
|
}
|
|
702
|
-
if (browserEnabled &&
|
|
864
|
+
if (browserEnabled &&
|
|
865
|
+
(!playwrightStatus.enabled || !playwrightStatus.installed)) {
|
|
703
866
|
printSection("playwright", [
|
|
704
867
|
`${picocolors_1.default.yellow(" ACTION")} Install browser binaries before using browser_* tools:`,
|
|
705
868
|
" tellymcp browser install",
|
|
@@ -719,7 +882,9 @@ function normalizeExtensionFlavor(rawValue) {
|
|
|
719
882
|
return null;
|
|
720
883
|
}
|
|
721
884
|
function getBundledExtensionDir(flavor) {
|
|
722
|
-
return node_path_1.default.join(packageRoot, "packages", flavor === "firefox"
|
|
885
|
+
return node_path_1.default.join(packageRoot, "packages", flavor === "firefox"
|
|
886
|
+
? "firefox-attach-extension"
|
|
887
|
+
: "chrome-attach-extension", "dist");
|
|
723
888
|
}
|
|
724
889
|
function getDefaultExtensionTargetDir(flavor) {
|
|
725
890
|
return node_path_1.default.resolve(process.cwd(), flavor === "firefox" ? "tellymcp-firefox-attach" : "tellymcp-chrome-attach");
|
|
@@ -760,7 +925,7 @@ async function runSystemPrune(args) {
|
|
|
760
925
|
const dbUser = parsed.DB_USER?.trim();
|
|
761
926
|
const dbPassword = parsed.DB_PASSWORD?.trim();
|
|
762
927
|
const dbName = parsed.DB_NAME?.trim();
|
|
763
|
-
const dbSchema = (parsed.
|
|
928
|
+
const dbSchema = (parsed.DB_SCHEMA || "mcp").trim();
|
|
764
929
|
const xchangeDir = node_path_1.default.resolve(process.cwd(), parsed.MCP_XCHANGE_DIR || ".mcp-xchange");
|
|
765
930
|
const sessionMarkerPath = node_path_1.default.resolve(process.cwd(), ".mcpsession.json");
|
|
766
931
|
const sqliteDbPath = node_path_1.default.join(xchangeDir, "xchange.sqlite3");
|
|
@@ -768,35 +933,39 @@ async function runSystemPrune(args) {
|
|
|
768
933
|
printSection("Target", [
|
|
769
934
|
` env: ${envPath}`,
|
|
770
935
|
` mode: ${mode}`,
|
|
771
|
-
|
|
936
|
+
...(mode === "client"
|
|
937
|
+
? [` state: process-local (Redis skipped)`]
|
|
938
|
+
: [` redis: ${redisHost}:${redisPort}/${redisDb}`]),
|
|
772
939
|
...(dbHost && dbUser && dbName
|
|
773
940
|
? [` postgres: ${dbHost}:${dbPort}/${dbName} schema ${dbSchema}`]
|
|
774
941
|
: [` postgres: ${picocolors_1.default.dim("skipped (not configured)")}`]),
|
|
775
942
|
` xchange dir: ${xchangeDir}`,
|
|
776
943
|
` session marker: ${sessionMarkerPath}`,
|
|
777
944
|
]);
|
|
778
|
-
const redis = new ioredis_1.default({
|
|
779
|
-
host: redisHost,
|
|
780
|
-
port: redisPort,
|
|
781
|
-
db: redisDb,
|
|
782
|
-
...(redisUsername ? { username: redisUsername } : {}),
|
|
783
|
-
...(redisPassword ? { password: redisPassword } : {}),
|
|
784
|
-
maxRetriesPerRequest: 1,
|
|
785
|
-
enableReadyCheck: true,
|
|
786
|
-
});
|
|
787
945
|
let deletedRedisKeys = 0;
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
946
|
+
if (mode !== "client") {
|
|
947
|
+
const redis = new ioredis_1.default({
|
|
948
|
+
host: redisHost,
|
|
949
|
+
port: redisPort,
|
|
950
|
+
db: redisDb,
|
|
951
|
+
...(redisUsername ? { username: redisUsername } : {}),
|
|
952
|
+
...(redisPassword ? { password: redisPassword } : {}),
|
|
953
|
+
maxRetriesPerRequest: 1,
|
|
954
|
+
enableReadyCheck: true,
|
|
955
|
+
});
|
|
956
|
+
try {
|
|
957
|
+
let cursor = "0";
|
|
958
|
+
do {
|
|
959
|
+
const [nextCursor, keys] = await redis.scan(cursor, "MATCH", "telegram-mcp:*", "COUNT", 500);
|
|
960
|
+
cursor = nextCursor;
|
|
961
|
+
if (keys.length > 0) {
|
|
962
|
+
deletedRedisKeys += await redis.del(...keys);
|
|
963
|
+
}
|
|
964
|
+
} while (cursor !== "0");
|
|
965
|
+
}
|
|
966
|
+
finally {
|
|
967
|
+
redis.disconnect();
|
|
968
|
+
}
|
|
800
969
|
}
|
|
801
970
|
let truncatedTables = [];
|
|
802
971
|
if (dbHost && dbUser && dbName) {
|
|
@@ -990,8 +1159,19 @@ async function runRuntime(args) {
|
|
|
990
1159
|
sessionId: parsed.TELLYMCP_SESSION_ID,
|
|
991
1160
|
sessionLabel: parsed.TELLYMCP_SESSION_LABEL,
|
|
992
1161
|
});
|
|
993
|
-
|
|
994
|
-
|
|
1162
|
+
const nativePtyStatus = await checkNativePtyRuntime();
|
|
1163
|
+
if (!nativePtyStatus.available) {
|
|
1164
|
+
fail([
|
|
1165
|
+
`Built-in PTY runtime is unavailable on ${process.platform}-${process.arch} with Node ${process.versions.node}.`,
|
|
1166
|
+
nativePtyStatus.message,
|
|
1167
|
+
"",
|
|
1168
|
+
...nativePtyRecoveryLines(),
|
|
1169
|
+
"Then rerun: tellymcp doctor --env <file>",
|
|
1170
|
+
].join("\n"));
|
|
1171
|
+
}
|
|
1172
|
+
if (isForegroundPtyClientMode(parsed)) {
|
|
1173
|
+
const { runForegroundPtyRuntime } = await import("./services/features/telegram-mcp/src/features/foreground-terminal/model/foregroundTerminalRuntime.js");
|
|
1174
|
+
await runForegroundPtyRuntime({
|
|
995
1175
|
envPath,
|
|
996
1176
|
packageRoot,
|
|
997
1177
|
printBanner,
|
|
@@ -1045,11 +1225,24 @@ async function runRuntime(args) {
|
|
|
1045
1225
|
}
|
|
1046
1226
|
async function main(argv) {
|
|
1047
1227
|
const [rawCommand, firstArg, secondArg] = argv;
|
|
1048
|
-
const command = rawCommand === "init" ||
|
|
1049
|
-
|
|
1228
|
+
const command = rawCommand === "init" ||
|
|
1229
|
+
rawCommand === "configure" ||
|
|
1230
|
+
rawCommand === "migrate-env" ||
|
|
1231
|
+
rawCommand === "run" ||
|
|
1232
|
+
rawCommand === "help" ||
|
|
1233
|
+
rawCommand === "mcp" ||
|
|
1234
|
+
rawCommand === "oauth" ||
|
|
1235
|
+
rawCommand === "doctor" ||
|
|
1236
|
+
rawCommand === "browser" ||
|
|
1237
|
+
rawCommand === "system-prune" ||
|
|
1238
|
+
rawCommand === "codex-plugin" ||
|
|
1239
|
+
rawCommand === "extension"
|
|
1050
1240
|
? rawCommand
|
|
1051
1241
|
: "help";
|
|
1052
|
-
if (command === "help" ||
|
|
1242
|
+
if (command === "help" ||
|
|
1243
|
+
!rawCommand ||
|
|
1244
|
+
rawCommand === "--help" ||
|
|
1245
|
+
rawCommand === "-h") {
|
|
1053
1246
|
printHelp();
|
|
1054
1247
|
return;
|
|
1055
1248
|
}
|
|
@@ -1057,10 +1250,22 @@ async function main(argv) {
|
|
|
1057
1250
|
initWorkspace(ensureMode(firstArg), secondArg);
|
|
1058
1251
|
return;
|
|
1059
1252
|
}
|
|
1253
|
+
if (command === "configure") {
|
|
1254
|
+
await runConfigure(argv.slice(1));
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
if (command === "migrate-env") {
|
|
1258
|
+
runMigrateEnv(argv.slice(1));
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1060
1261
|
if (command === "mcp") {
|
|
1061
1262
|
printMcpConfig(argv.slice(1));
|
|
1062
1263
|
return;
|
|
1063
1264
|
}
|
|
1265
|
+
if (command === "oauth") {
|
|
1266
|
+
runOAuthCommand(argv.slice(1));
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1064
1269
|
if (command === "doctor") {
|
|
1065
1270
|
await runDoctor(argv.slice(1));
|
|
1066
1271
|
return;
|