@deadragdoll/tellymcp 0.0.11 → 0.0.12
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 +29 -0
- package/README.md +28 -0
- package/TOOLS.md +26 -0
- package/VERSION.md +2 -2
- package/dist/services/features/telegram-mcp/gateway-socket.service.js +10 -0
- package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +4 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserOpenTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +58 -8
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +48 -0
- package/dist/services/features/telegram-mcp/src/shared/i18n/resources/en.js +5 -1
- package/dist/services/features/telegram-mcp/src/shared/i18n/resources/ru.js +5 -1
- package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +28 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +37 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConsoleRegistry.js +13 -7
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConstructorWiring.js +9 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuShell.js +3 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPayloadState.js +7 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalActions.js +231 -34
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalRuntime.js +61 -6
- package/dist/services/features/telegram-mcp/src/shared/lib/terminalPromptDetection.js +200 -28
- package/package.json +1 -1
|
@@ -16,6 +16,11 @@ const STRONG_PATTERNS = [
|
|
|
16
16
|
score: 5,
|
|
17
17
|
reason: "press_enter_prompt",
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
pattern: /press\s+enter\s+to\s+confirm\b.*\besc\s+to\s+cancel/iu,
|
|
21
|
+
score: 5,
|
|
22
|
+
reason: "confirm_cancel_prompt",
|
|
23
|
+
},
|
|
19
24
|
{
|
|
20
25
|
pattern: /waiting\s+for\s+(?:user\s+)?input|awaiting\s+(?:user\s+)?(?:input|confirmation)|requires?\s+approval|need\s+your\s+(?:input|permission|approval)|cannot\s+continue\s+without/iu,
|
|
21
26
|
score: 4,
|
|
@@ -46,6 +51,11 @@ const STRONG_PATTERNS = [
|
|
|
46
51
|
score: 4,
|
|
47
52
|
reason: "tool_continue_prompt",
|
|
48
53
|
},
|
|
54
|
+
{
|
|
55
|
+
pattern: /\bwould\s+you\s+like\s+to\s+run\s+the\s+following\s+command\b/iu,
|
|
56
|
+
score: 5,
|
|
57
|
+
reason: "command_approval_prompt",
|
|
58
|
+
},
|
|
49
59
|
];
|
|
50
60
|
const MEDIUM_PATTERNS = [
|
|
51
61
|
{
|
|
@@ -64,6 +74,17 @@ const MEDIUM_PATTERNS = [
|
|
|
64
74
|
reason: "tool_choice_keyword",
|
|
65
75
|
},
|
|
66
76
|
];
|
|
77
|
+
const NUMBERED_CHOICE_LINE_PATTERN = /^(?:[>›*•-]\s*)?\d+\.\s+.+$/u;
|
|
78
|
+
const CONFIRM_CANCEL_FOOTER_PATTERN = /\b(?:press\s+enter\s+to\s+confirm|enter\s+to\s+submit)\b.*\besc\s+to\s+cancel\b/iu;
|
|
79
|
+
const SUBMIT_CANCEL_FOOTER_PATTERN = /\benter\s+to\s+submit\b.*\besc\s+to\s+cancel\b/iu;
|
|
80
|
+
const CONTINUE_FOOTER_PATTERN = /\b(?:press\s+enter\s+to\s+continue|hit\s+enter\s+to\s+continue)\b/iu;
|
|
81
|
+
const NUMBERED_HOTKEY_FOOTER_PATTERN = /\bpress\s+(?:(?:\d+\s*,\s*)+\s*(?:or\s+)?\d+\s*,?\s*)?(?:enter\b.*\besc|esc\b.*\benter|\d+\b.*\benter|\d+\b.*\besc).*\b(?:quit|continue|cancel|confirm|submit)\b/iu;
|
|
82
|
+
const POSITIVE_CHOICE_PATTERN = /\b(?:yes|allow|approve|proceed|continue)\b/iu;
|
|
83
|
+
const NEGATIVE_CHOICE_PATTERN = /\b(?:no|cancel|deny|reject)\b/iu;
|
|
84
|
+
const STICKY_CHOICE_PATTERN = /\bdon'?t\s+ask\s+again|always\s+allow|for\s+this\s+session\b/iu;
|
|
85
|
+
const SKIP_CHOICE_PATTERN = /\bskip\b/iu;
|
|
86
|
+
const LEADING_SELECTION_MARKER_PATTERN = /^(?:[>›*•-]\s*)(?=\d+\.\s)/u;
|
|
87
|
+
const ACTION_HINT_PATTERN = /\b(?:press|input|choose|choice|select|option|enter|esc(?:ape)?|accept|decline|confirm|cancel|continue|proceed|yes|no)\b|(?:\[[Yy](?:es)?\/[Nn](?:o)?\])|(?:\([Yy](?:es)?\/[Nn](?:o)?\))/u;
|
|
67
88
|
function normalizeLine(line) {
|
|
68
89
|
return line
|
|
69
90
|
.replaceAll("\r", "")
|
|
@@ -119,25 +140,6 @@ function scoreMediumLine(line, strategy) {
|
|
|
119
140
|
function isStrongReason(reason) {
|
|
120
141
|
return STRONG_PATTERNS.some((candidate) => candidate.reason === reason);
|
|
121
142
|
}
|
|
122
|
-
function detectGroupedSignals(candidateLines, strategy) {
|
|
123
|
-
const signals = [];
|
|
124
|
-
const hasPrimaryAllowChoice = candidateLines.some((line) => /^>?[\s\d.]*allow\b/iu.test(line));
|
|
125
|
-
const hasSecondaryApprovalChoice = candidateLines.some((line) => /\b(?:allow\s+once|allow\s+for\s+this\s+session|always\s+allow|cancel)\b/iu.test(line));
|
|
126
|
-
const hasSubmitCancelHint = candidateLines.some((line) => /\benter\s+to\s+submit\b.*\besc\s+to\s+cancel\b/iu.test(line));
|
|
127
|
-
if (hasPrimaryAllowChoice && hasSecondaryApprovalChoice) {
|
|
128
|
-
signals.push({
|
|
129
|
-
score: 5,
|
|
130
|
-
reason: "approval_choice_group",
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
if (hasSubmitCancelHint) {
|
|
134
|
-
signals.push({
|
|
135
|
-
score: strategy === "balanced" ? 2 : 3,
|
|
136
|
-
reason: "submit_cancel_hint",
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
return signals;
|
|
140
|
-
}
|
|
141
143
|
function collectCandidateLines(rawText, maxLines) {
|
|
142
144
|
const normalized = rawText
|
|
143
145
|
.split("\n")
|
|
@@ -145,6 +147,168 @@ function collectCandidateLines(rawText, maxLines) {
|
|
|
145
147
|
.filter((line) => line.length > 0);
|
|
146
148
|
return normalized.slice(-Math.max(1, maxLines));
|
|
147
149
|
}
|
|
150
|
+
function normalizeFingerprintLine(line) {
|
|
151
|
+
return normalizeLine(line)
|
|
152
|
+
.replace(LEADING_SELECTION_MARKER_PATTERN, "")
|
|
153
|
+
.replace(/\s+/gu, " ")
|
|
154
|
+
.trim();
|
|
155
|
+
}
|
|
156
|
+
function buildDetectionFingerprint(strategy, matchedLines) {
|
|
157
|
+
const normalizedExcerpt = matchedLines
|
|
158
|
+
.map(normalizeFingerprintLine)
|
|
159
|
+
.join("\n");
|
|
160
|
+
return (0, node_crypto_1.createHash)("sha1")
|
|
161
|
+
.update(`${strategy}\n${normalizedExcerpt}`)
|
|
162
|
+
.digest("hex");
|
|
163
|
+
}
|
|
164
|
+
function buildExcerptWithContext(candidateLines, matchedLines) {
|
|
165
|
+
if (matchedLines.length === 0) {
|
|
166
|
+
return "";
|
|
167
|
+
}
|
|
168
|
+
const firstMatchedIndex = candidateLines.findIndex((line) => line === matchedLines[0]);
|
|
169
|
+
const lastMatchedIndex = matchedLines.length === 1
|
|
170
|
+
? firstMatchedIndex
|
|
171
|
+
: candidateLines.findIndex((line, index) => index >= Math.max(0, firstMatchedIndex) &&
|
|
172
|
+
line === matchedLines[matchedLines.length - 1]);
|
|
173
|
+
if (firstMatchedIndex < 0 || lastMatchedIndex < 0) {
|
|
174
|
+
return matchedLines.slice(-8).join("\n");
|
|
175
|
+
}
|
|
176
|
+
const excerptStart = Math.max(0, firstMatchedIndex - 2);
|
|
177
|
+
const excerptEnd = Math.min(candidateLines.length, lastMatchedIndex + 1);
|
|
178
|
+
return candidateLines.slice(excerptStart, excerptEnd).slice(-8).join("\n");
|
|
179
|
+
}
|
|
180
|
+
function findChoiceActionBlock(candidateLines) {
|
|
181
|
+
for (let startIndex = candidateLines.length - 1; startIndex >= 0; startIndex -= 1) {
|
|
182
|
+
const line = candidateLines[startIndex] ?? "";
|
|
183
|
+
if (!NUMBERED_CHOICE_LINE_PATTERN.test(line)) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const choiceLines = [];
|
|
187
|
+
let firstChoiceIndex = startIndex;
|
|
188
|
+
while (firstChoiceIndex >= 0) {
|
|
189
|
+
const currentLine = candidateLines[firstChoiceIndex] ?? "";
|
|
190
|
+
if (!NUMBERED_CHOICE_LINE_PATTERN.test(currentLine)) {
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
choiceLines.unshift(currentLine);
|
|
194
|
+
firstChoiceIndex -= 1;
|
|
195
|
+
}
|
|
196
|
+
firstChoiceIndex += 1;
|
|
197
|
+
if (choiceLines.length < 2) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const lastChoiceIndex = firstChoiceIndex + choiceLines.length - 1;
|
|
201
|
+
const headerLines = candidateLines.slice(Math.max(0, firstChoiceIndex - 5), firstChoiceIndex);
|
|
202
|
+
const tailLines = candidateLines.slice(lastChoiceIndex + 1, Math.min(candidateLines.length, lastChoiceIndex + 4));
|
|
203
|
+
const footerLine = tailLines[0] ?? null;
|
|
204
|
+
const nearbyLines = [...headerLines, ...tailLines];
|
|
205
|
+
const hasActionHint = nearbyLines.some((candidate) => ACTION_HINT_PATTERN.test(candidate));
|
|
206
|
+
const hasChoiceSemantics = POSITIVE_CHOICE_PATTERN.test(choiceLines.join("\n")) ||
|
|
207
|
+
NEGATIVE_CHOICE_PATTERN.test(choiceLines.join("\n")) ||
|
|
208
|
+
STICKY_CHOICE_PATTERN.test(choiceLines.join("\n")) ||
|
|
209
|
+
SKIP_CHOICE_PATTERN.test(choiceLines.join("\n"));
|
|
210
|
+
if (!hasActionHint && !hasChoiceSemantics) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
footerLine,
|
|
215
|
+
choiceLines,
|
|
216
|
+
headerLines,
|
|
217
|
+
tailLines,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
function detectChoiceFooterSignals(candidateLines, strategy) {
|
|
223
|
+
const block = findChoiceActionBlock(candidateLines);
|
|
224
|
+
if (!block) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const choiceText = block.choiceLines.join("\n");
|
|
228
|
+
const contextLines = [
|
|
229
|
+
...block.headerLines,
|
|
230
|
+
...block.choiceLines,
|
|
231
|
+
...(block.footerLine ? [block.footerLine] : []),
|
|
232
|
+
...block.tailLines,
|
|
233
|
+
];
|
|
234
|
+
const reasons = new Set(["numbered_choice_group"]);
|
|
235
|
+
const matchedLines = [
|
|
236
|
+
...block.choiceLines,
|
|
237
|
+
...(block.footerLine ? [block.footerLine] : []),
|
|
238
|
+
...block.tailLines,
|
|
239
|
+
];
|
|
240
|
+
let score = strategy === "balanced" ? 5 : 6;
|
|
241
|
+
const footerLine = block.footerLine ?? "";
|
|
242
|
+
if (CONFIRM_CANCEL_FOOTER_PATTERN.test(footerLine)) {
|
|
243
|
+
reasons.add("confirm_cancel_footer");
|
|
244
|
+
score += strategy === "balanced" ? 2 : 3;
|
|
245
|
+
}
|
|
246
|
+
if (CONTINUE_FOOTER_PATTERN.test(footerLine)) {
|
|
247
|
+
reasons.add("continue_footer");
|
|
248
|
+
score += strategy === "balanced" ? 2 : 3;
|
|
249
|
+
}
|
|
250
|
+
if (NUMBERED_HOTKEY_FOOTER_PATTERN.test(footerLine)) {
|
|
251
|
+
reasons.add("numbered_hotkey_footer");
|
|
252
|
+
score += strategy === "balanced" ? 2 : 3;
|
|
253
|
+
}
|
|
254
|
+
if (SUBMIT_CANCEL_FOOTER_PATTERN.test(footerLine)) {
|
|
255
|
+
reasons.add("submit_cancel_hint");
|
|
256
|
+
}
|
|
257
|
+
if (/press\s+enter\s+to\s+confirm\b/iu.test(footerLine)) {
|
|
258
|
+
reasons.add("confirm_cancel_hint");
|
|
259
|
+
}
|
|
260
|
+
if (contextLines.some((line) => ACTION_HINT_PATTERN.test(line))) {
|
|
261
|
+
reasons.add("action_hint_present");
|
|
262
|
+
score += 2;
|
|
263
|
+
}
|
|
264
|
+
if (POSITIVE_CHOICE_PATTERN.test(choiceText)) {
|
|
265
|
+
reasons.add("positive_choice_present");
|
|
266
|
+
score += 2;
|
|
267
|
+
}
|
|
268
|
+
if (NEGATIVE_CHOICE_PATTERN.test(choiceText)) {
|
|
269
|
+
reasons.add("negative_choice_present");
|
|
270
|
+
score += 2;
|
|
271
|
+
}
|
|
272
|
+
if (STICKY_CHOICE_PATTERN.test(choiceText)) {
|
|
273
|
+
reasons.add("sticky_choice_present");
|
|
274
|
+
score += 2;
|
|
275
|
+
}
|
|
276
|
+
if (SKIP_CHOICE_PATTERN.test(choiceText)) {
|
|
277
|
+
reasons.add("skip_choice_present");
|
|
278
|
+
score += 2;
|
|
279
|
+
}
|
|
280
|
+
if (reasons.has("positive_choice_present") && reasons.has("negative_choice_present")) {
|
|
281
|
+
reasons.add("yes_no_choice_group");
|
|
282
|
+
reasons.add("approval_choice_group");
|
|
283
|
+
score += 3;
|
|
284
|
+
}
|
|
285
|
+
if (reasons.has("positive_choice_present") && reasons.has("skip_choice_present")) {
|
|
286
|
+
reasons.add("choice_update_group");
|
|
287
|
+
score += 2;
|
|
288
|
+
}
|
|
289
|
+
if (/^(?:[>›*•-]\s*)?(?:\d+\.\s+)?yes,\s+proceed\b/imu.test(choiceText) &&
|
|
290
|
+
reasons.has("sticky_choice_present")) {
|
|
291
|
+
reasons.add("proceed_choice_group");
|
|
292
|
+
}
|
|
293
|
+
for (const line of contextLines) {
|
|
294
|
+
for (const contribution of scoreStrongLine(line)) {
|
|
295
|
+
reasons.add(contribution.reason);
|
|
296
|
+
score += contribution.score;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
score,
|
|
301
|
+
reasons: Array.from(reasons),
|
|
302
|
+
matchedLines,
|
|
303
|
+
promptActions: {
|
|
304
|
+
numberedOptions: block.choiceLines
|
|
305
|
+
.map((line) => line.match(/^(?:[>›*•-]\s*)?(\d+)\.\s+/u)?.[1] ?? null)
|
|
306
|
+
.filter((value) => Boolean(value)),
|
|
307
|
+
hasEnter: contextLines.some((line) => /\benter\b/iu.test(line)),
|
|
308
|
+
hasEscape: contextLines.some((line) => /\besc(?:ape)?\b/iu.test(line)),
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
}
|
|
148
312
|
function detectTerminalInteractivePrompt(rawText, options = {}) {
|
|
149
313
|
const strategy = options.strategy ?? "strict";
|
|
150
314
|
const minScore = options.minScore ?? (strategy === "balanced" ? 4 : 5);
|
|
@@ -152,6 +316,20 @@ function detectTerminalInteractivePrompt(rawText, options = {}) {
|
|
|
152
316
|
if (candidateLines.length === 0) {
|
|
153
317
|
return null;
|
|
154
318
|
}
|
|
319
|
+
const choiceFooterDetection = detectChoiceFooterSignals(candidateLines, strategy);
|
|
320
|
+
if (choiceFooterDetection && choiceFooterDetection.score >= minScore) {
|
|
321
|
+
const matchedLines = choiceFooterDetection.matchedLines.slice(-6);
|
|
322
|
+
const excerpt = buildExcerptWithContext(candidateLines, matchedLines);
|
|
323
|
+
const fingerprint = buildDetectionFingerprint(strategy, matchedLines);
|
|
324
|
+
return {
|
|
325
|
+
score: choiceFooterDetection.score,
|
|
326
|
+
fingerprint,
|
|
327
|
+
excerpt,
|
|
328
|
+
matchedLines,
|
|
329
|
+
reasons: choiceFooterDetection.reasons,
|
|
330
|
+
promptActions: choiceFooterDetection.promptActions,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
155
333
|
const scoredLines = [];
|
|
156
334
|
let score = 0;
|
|
157
335
|
const reasons = new Set();
|
|
@@ -174,12 +352,8 @@ function detectTerminalInteractivePrompt(rawText, options = {}) {
|
|
|
174
352
|
score += 3;
|
|
175
353
|
reasons.add("multiple_options");
|
|
176
354
|
}
|
|
177
|
-
for (const signal of detectGroupedSignals(candidateLines, strategy)) {
|
|
178
|
-
score += signal.score;
|
|
179
|
-
reasons.add(signal.reason);
|
|
180
|
-
}
|
|
181
355
|
const hasStrongReason = Array.from(reasons).some(isStrongReason);
|
|
182
|
-
const hasGroupedReason = reasons.has("
|
|
356
|
+
const hasGroupedReason = reasons.has("multiple_options");
|
|
183
357
|
const hasOnlyQuestionMarkReason = reasons.size === 1 && reasons.has("question_mark");
|
|
184
358
|
if (strategy === "strict") {
|
|
185
359
|
if (hasOnlyQuestionMarkReason) {
|
|
@@ -223,10 +397,8 @@ function detectTerminalInteractivePrompt(rawText, options = {}) {
|
|
|
223
397
|
const matchedLines = scoredLines
|
|
224
398
|
.map((entry) => entry.line)
|
|
225
399
|
.slice(-6);
|
|
226
|
-
const excerpt = matchedLines
|
|
227
|
-
const fingerprint = (
|
|
228
|
-
.update(`${strategy}\n${excerpt}`)
|
|
229
|
-
.digest("hex");
|
|
400
|
+
const excerpt = buildExcerptWithContext(candidateLines, matchedLines);
|
|
401
|
+
const fingerprint = buildDetectionFingerprint(strategy, matchedLines);
|
|
230
402
|
return {
|
|
231
403
|
score,
|
|
232
404
|
fingerprint,
|
package/package.json
CHANGED