@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
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TransportTerminalActions = void 0;
|
|
4
|
+
const grammy_1 = require("grammy");
|
|
4
5
|
const client_1 = require("../terminal/client");
|
|
6
|
+
const relay_1 = require("../../../app/webapp/relay");
|
|
5
7
|
const terminalPromptDetection_1 = require("../../lib/terminalPromptDetection");
|
|
6
8
|
const transportUtils_1 = require("./transportUtils");
|
|
7
9
|
const TERMINAL_NUDGE_FAILURE_NOTICE_COOLDOWN_MS = 5 * 60 * 1000;
|
|
@@ -11,6 +13,74 @@ class TransportTerminalActions {
|
|
|
11
13
|
constructor(host) {
|
|
12
14
|
this.host = host;
|
|
13
15
|
}
|
|
16
|
+
buildCapturePreview(capture) {
|
|
17
|
+
return capture
|
|
18
|
+
.split("\n")
|
|
19
|
+
.map((line) => line.trimEnd())
|
|
20
|
+
.filter((line) => line.trim().length > 0)
|
|
21
|
+
.slice(-6);
|
|
22
|
+
}
|
|
23
|
+
buildPromptActionTokens(detection) {
|
|
24
|
+
const actions = [];
|
|
25
|
+
const promptActions = detection.promptActions;
|
|
26
|
+
if (promptActions) {
|
|
27
|
+
actions.push(...promptActions.numberedOptions);
|
|
28
|
+
if (promptActions.hasEnter) {
|
|
29
|
+
actions.push("enter");
|
|
30
|
+
}
|
|
31
|
+
if (promptActions.hasEscape) {
|
|
32
|
+
actions.push("escape");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return Array.from(new Set(actions));
|
|
36
|
+
}
|
|
37
|
+
buildPromptActionKeyboard(payloadKey, actions) {
|
|
38
|
+
if (actions.length === 0) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const keyboard = new grammy_1.InlineKeyboard();
|
|
42
|
+
for (const action of actions) {
|
|
43
|
+
const label = action === "enter"
|
|
44
|
+
? "Enter"
|
|
45
|
+
: action === "escape"
|
|
46
|
+
? "Esc"
|
|
47
|
+
: action;
|
|
48
|
+
keyboard.text(label, `terminal-prompt-action:${action}:${payloadKey}`);
|
|
49
|
+
}
|
|
50
|
+
return keyboard;
|
|
51
|
+
}
|
|
52
|
+
async sendPromptAction(sessionId, action) {
|
|
53
|
+
if (action === "enter" || action === "escape") {
|
|
54
|
+
await this.host.callGatewayJson("/live/action", {
|
|
55
|
+
session_id: sessionId,
|
|
56
|
+
action,
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
await this.host.callGatewayJson("/live/action", {
|
|
61
|
+
session_id: sessionId,
|
|
62
|
+
action: "text",
|
|
63
|
+
text: action,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
isExpectedRelayCaptureMiss(error) {
|
|
67
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
68
|
+
return (/\bInvalid live relay view response\b/u.test(message) ||
|
|
69
|
+
/\bis not connected\b/u.test(message) ||
|
|
70
|
+
/\brequest is rejected\b/u.test(message) ||
|
|
71
|
+
/\brelay live capture did not return terminal content\b/u.test(message));
|
|
72
|
+
}
|
|
73
|
+
buildRelayTerminalTarget(sessionId) {
|
|
74
|
+
const relay = (0, relay_1.parseLiveRelaySessionId)(sessionId);
|
|
75
|
+
if (!relay) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return `relay:${relay.clientUuid}/${relay.localSessionId}`;
|
|
79
|
+
}
|
|
80
|
+
extractTerminalTextFromGatewayMarkdown(markdownContent) {
|
|
81
|
+
const match = markdownContent.match(/```text\n([\s\S]*?)\n```/u);
|
|
82
|
+
return match?.[1] ?? markdownContent;
|
|
83
|
+
}
|
|
14
84
|
async ensurePtyTargetForSession(sessionId, session) {
|
|
15
85
|
const ensuredTarget = (0, client_1.ensureTerminalTargetForSession)(this.host.config.terminal, {
|
|
16
86
|
sessionId,
|
|
@@ -243,17 +313,39 @@ class TransportTerminalActions {
|
|
|
243
313
|
}
|
|
244
314
|
}
|
|
245
315
|
async scanPromptForSession(session) {
|
|
246
|
-
|
|
316
|
+
const relayTerminalTarget = this.buildRelayTerminalTarget(session.sessionId);
|
|
317
|
+
if (!session.terminalTarget && !relayTerminalTarget) {
|
|
318
|
+
this.host.logger.debug("terminal prompt scan skipped", {
|
|
319
|
+
sessionId: session.sessionId,
|
|
320
|
+
sessionLabel: session.label,
|
|
321
|
+
isRelaySession: relayTerminalTarget !== null,
|
|
322
|
+
skipReason: "no_terminal_target",
|
|
323
|
+
});
|
|
247
324
|
this.host.terminalPromptNoticeState.delete(session.sessionId);
|
|
248
325
|
return;
|
|
249
326
|
}
|
|
250
327
|
const binding = await this.host.bindingStore.getBinding(session.sessionId);
|
|
251
328
|
if (!binding) {
|
|
329
|
+
this.host.logger.debug("terminal prompt scan skipped", {
|
|
330
|
+
sessionId: session.sessionId,
|
|
331
|
+
sessionLabel: session.label,
|
|
332
|
+
isRelaySession: relayTerminalTarget !== null,
|
|
333
|
+
terminalTarget: session.terminalTarget ?? relayTerminalTarget,
|
|
334
|
+
skipReason: "no_binding",
|
|
335
|
+
});
|
|
252
336
|
this.host.terminalPromptNoticeState.delete(session.sessionId);
|
|
253
337
|
return;
|
|
254
338
|
}
|
|
255
|
-
let terminalTarget = session.terminalTarget;
|
|
339
|
+
let terminalTarget = session.terminalTarget ?? relayTerminalTarget;
|
|
256
340
|
let capture;
|
|
341
|
+
this.host.logger.debug("terminal prompt scan started", {
|
|
342
|
+
sessionId: session.sessionId,
|
|
343
|
+
sessionLabel: session.label,
|
|
344
|
+
isRelaySession: relayTerminalTarget !== null,
|
|
345
|
+
terminalTarget,
|
|
346
|
+
strategy: this.host.config.terminal.promptScanStrategy,
|
|
347
|
+
minScore: this.host.config.terminal.promptScanMinScore,
|
|
348
|
+
});
|
|
257
349
|
try {
|
|
258
350
|
capture = await this.capturePromptBuffer(session);
|
|
259
351
|
}
|
|
@@ -261,6 +353,7 @@ class TransportTerminalActions {
|
|
|
261
353
|
if ((0, client_1.isTerminalUnavailableError)(error)) {
|
|
262
354
|
this.host.logger.debug("terminal prompt scan skipped because terminal is unavailable", {
|
|
263
355
|
sessionId: session.sessionId,
|
|
356
|
+
sessionLabel: session.label,
|
|
264
357
|
terminalTarget,
|
|
265
358
|
});
|
|
266
359
|
return;
|
|
@@ -270,6 +363,7 @@ class TransportTerminalActions {
|
|
|
270
363
|
if (!recoveredTarget) {
|
|
271
364
|
this.host.logger.debug("terminal prompt scan skipped because target is invalid", {
|
|
272
365
|
sessionId: session.sessionId,
|
|
366
|
+
sessionLabel: session.label,
|
|
273
367
|
terminalTarget,
|
|
274
368
|
});
|
|
275
369
|
return;
|
|
@@ -281,6 +375,19 @@ class TransportTerminalActions {
|
|
|
281
375
|
});
|
|
282
376
|
}
|
|
283
377
|
else {
|
|
378
|
+
if (relayTerminalTarget &&
|
|
379
|
+
this.isExpectedRelayCaptureMiss(error)) {
|
|
380
|
+
this.host.logger.debug("terminal prompt scan skipped because relay capture is unavailable", {
|
|
381
|
+
sessionId: session.sessionId,
|
|
382
|
+
sessionLabel: session.label,
|
|
383
|
+
terminalTarget,
|
|
384
|
+
error: error instanceof Error
|
|
385
|
+
? (error.stack ?? error.message)
|
|
386
|
+
: String(error),
|
|
387
|
+
});
|
|
388
|
+
this.host.terminalPromptNoticeState.delete(session.sessionId);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
284
391
|
this.host.logger.warn("terminal prompt scan capture failed", {
|
|
285
392
|
sessionId: session.sessionId,
|
|
286
393
|
terminalTarget,
|
|
@@ -291,6 +398,15 @@ class TransportTerminalActions {
|
|
|
291
398
|
return;
|
|
292
399
|
}
|
|
293
400
|
}
|
|
401
|
+
const capturePreview = this.buildCapturePreview(capture);
|
|
402
|
+
this.host.logger.debug("terminal prompt buffer captured", {
|
|
403
|
+
sessionId: session.sessionId,
|
|
404
|
+
sessionLabel: session.label,
|
|
405
|
+
terminalTarget,
|
|
406
|
+
captureChars: capture.length,
|
|
407
|
+
captureLines: capture.split("\n").length,
|
|
408
|
+
previewTail: capturePreview,
|
|
409
|
+
});
|
|
294
410
|
const detection = (0, terminalPromptDetection_1.detectTerminalInteractivePrompt)(capture, {
|
|
295
411
|
strategy: this.host.config.terminal.promptScanStrategy,
|
|
296
412
|
minScore: this.host.config.terminal.promptScanMinScore,
|
|
@@ -298,9 +414,11 @@ class TransportTerminalActions {
|
|
|
298
414
|
if (!detection) {
|
|
299
415
|
this.host.logger.debug("terminal prompt scan found no interactive prompt", {
|
|
300
416
|
sessionId: session.sessionId,
|
|
417
|
+
sessionLabel: session.label,
|
|
301
418
|
terminalTarget,
|
|
302
419
|
strategy: this.host.config.terminal.promptScanStrategy,
|
|
303
420
|
minScore: this.host.config.terminal.promptScanMinScore,
|
|
421
|
+
previewTail: capturePreview,
|
|
304
422
|
});
|
|
305
423
|
this.host.terminalPromptNoticeState.delete(session.sessionId);
|
|
306
424
|
return;
|
|
@@ -308,9 +426,35 @@ class TransportTerminalActions {
|
|
|
308
426
|
if (!this.shouldSendPromptNotice(session.sessionId, detection)) {
|
|
309
427
|
return;
|
|
310
428
|
}
|
|
311
|
-
await this.notifyPromptDetected(session, binding, detection, terminalTarget);
|
|
429
|
+
await this.notifyPromptDetected(session, binding, detection, terminalTarget ?? relayTerminalTarget ?? "unknown");
|
|
312
430
|
}
|
|
313
431
|
async capturePromptBuffer(session) {
|
|
432
|
+
const relay = (0, relay_1.parseLiveRelaySessionId)(session.sessionId);
|
|
433
|
+
if (relay) {
|
|
434
|
+
this.host.logger.debug("terminal prompt relay capture requested", {
|
|
435
|
+
sessionId: session.sessionId,
|
|
436
|
+
clientUuid: relay.clientUuid,
|
|
437
|
+
localSessionId: relay.localSessionId,
|
|
438
|
+
});
|
|
439
|
+
const output = await this.host.callGatewayJson("/live/capture-buffer", {
|
|
440
|
+
session_id: session.sessionId,
|
|
441
|
+
scope: {
|
|
442
|
+
mode: "visible",
|
|
443
|
+
},
|
|
444
|
+
});
|
|
445
|
+
const markdownContent = typeof output.markdown_content === "string"
|
|
446
|
+
? output.markdown_content
|
|
447
|
+
: "";
|
|
448
|
+
if (!markdownContent.trim()) {
|
|
449
|
+
this.host.logger.warn("terminal prompt relay capture returned empty content", {
|
|
450
|
+
sessionId: session.sessionId,
|
|
451
|
+
clientUuid: relay.clientUuid,
|
|
452
|
+
localSessionId: relay.localSessionId,
|
|
453
|
+
});
|
|
454
|
+
throw new Error("relay live capture did not return terminal content");
|
|
455
|
+
}
|
|
456
|
+
return this.extractTerminalTextFromGatewayMarkdown(markdownContent);
|
|
457
|
+
}
|
|
314
458
|
const target = session.terminalTarget;
|
|
315
459
|
if (!target) {
|
|
316
460
|
throw new Error("terminal target is not configured");
|
|
@@ -322,23 +466,18 @@ class TransportTerminalActions {
|
|
|
322
466
|
}
|
|
323
467
|
shouldSendPromptNotice(sessionId, detection) {
|
|
324
468
|
const existing = this.host.terminalPromptNoticeState.get(sessionId);
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
if (existing &&
|
|
328
|
-
existing.fingerprint === detection.fingerprint &&
|
|
329
|
-
nowMs - existing.sentAtMs < cooldownMs) {
|
|
330
|
-
this.host.logger.debug("terminal prompt detected but notification is on cooldown", {
|
|
469
|
+
if (existing && existing.fingerprint === detection.fingerprint) {
|
|
470
|
+
this.host.logger.debug("terminal prompt detected but fingerprint is unchanged", {
|
|
331
471
|
sessionId,
|
|
332
472
|
fingerprint: detection.fingerprint,
|
|
333
473
|
score: detection.score,
|
|
334
474
|
reasons: detection.reasons,
|
|
335
|
-
cooldownSeconds: this.host.config.terminal.promptScanCooldownSeconds,
|
|
336
475
|
});
|
|
337
476
|
return false;
|
|
338
477
|
}
|
|
339
478
|
this.host.terminalPromptNoticeState.set(sessionId, {
|
|
340
479
|
fingerprint: detection.fingerprint,
|
|
341
|
-
sentAtMs:
|
|
480
|
+
sentAtMs: Date.now(),
|
|
342
481
|
});
|
|
343
482
|
return true;
|
|
344
483
|
}
|
|
@@ -348,30 +487,25 @@ class TransportTerminalActions {
|
|
|
348
487
|
}
|
|
349
488
|
const locale = await this.host.resolveLocaleForTelegramUserId(binding.telegramUserId);
|
|
350
489
|
const sessionLabel = session.label ?? session.sessionId;
|
|
351
|
-
const excerpt = detection.
|
|
352
|
-
.
|
|
353
|
-
.
|
|
354
|
-
|
|
490
|
+
const excerpt = detection.excerpt
|
|
491
|
+
.split("\n")
|
|
492
|
+
.slice(-Math.max(TERMINAL_PROMPT_SCAN_MATCHED_LINES_LIMIT + 2, 8))
|
|
493
|
+
.join("\n") || detection.matchedLines.slice(-TERMINAL_PROMPT_SCAN_MATCHED_LINES_LIMIT).join("\n");
|
|
494
|
+
const promptActionTokens = this.buildPromptActionTokens(detection);
|
|
495
|
+
const payloadKey = promptActionTokens.length > 0
|
|
496
|
+
? await this.host.createTerminalPromptActionPayload(session.sessionId, promptActionTokens)
|
|
497
|
+
: null;
|
|
498
|
+
const replyMarkup = payloadKey
|
|
499
|
+
? this.buildPromptActionKeyboard(payloadKey, promptActionTokens)
|
|
500
|
+
: null;
|
|
501
|
+
await this.host.sendChatMessage(binding.telegramChatId, [
|
|
502
|
+
this.host.t(locale, "menu:notices.terminal.prompt_detected_title", {
|
|
503
|
+
sessionName: sessionLabel,
|
|
504
|
+
}),
|
|
505
|
+
excerpt,
|
|
506
|
+
].join("\n"), replyMarkup ? { reply_markup: replyMarkup } : {}, {
|
|
507
|
+
kind: "notification",
|
|
355
508
|
sessionId: session.sessionId,
|
|
356
|
-
sessionLabel: "TellyMCP",
|
|
357
|
-
recipient: {
|
|
358
|
-
telegramChatId: binding.telegramChatId,
|
|
359
|
-
telegramUserId: binding.telegramUserId,
|
|
360
|
-
},
|
|
361
|
-
message: [
|
|
362
|
-
this.host.t(locale, "menu:notices.terminal.prompt_detected_title", {
|
|
363
|
-
sessionName: sessionLabel,
|
|
364
|
-
}),
|
|
365
|
-
this.host.t(locale, "menu:notices.terminal.prompt_detected_score", {
|
|
366
|
-
score: detection.score,
|
|
367
|
-
}),
|
|
368
|
-
this.host.t(locale, "menu:notices.terminal.prompt_detected_target", {
|
|
369
|
-
terminalTarget: terminalTarget,
|
|
370
|
-
}),
|
|
371
|
-
this.host.t(locale, "menu:notices.terminal.prompt_detected_hint"),
|
|
372
|
-
this.host.t(locale, "menu:notices.terminal.prompt_detected_excerpt"),
|
|
373
|
-
excerpt,
|
|
374
|
-
].join("\n"),
|
|
375
509
|
});
|
|
376
510
|
try {
|
|
377
511
|
await this.host.sendLiveViewLauncherMessage({
|
|
@@ -407,6 +541,69 @@ class TransportTerminalActions {
|
|
|
407
541
|
excerpt,
|
|
408
542
|
});
|
|
409
543
|
}
|
|
544
|
+
async handlePromptActionCallback(ctx) {
|
|
545
|
+
const locale = await this.host.resolveLocaleForContext(ctx);
|
|
546
|
+
const data = ctx.callbackQuery?.data ?? "";
|
|
547
|
+
const match = data.match(/^terminal-prompt-action:(\d+|enter|escape):(.+)$/u);
|
|
548
|
+
if (!match) {
|
|
549
|
+
await ctx.answerCallbackQuery({
|
|
550
|
+
text: this.host.t(locale, "menu:notices.terminal.prompt_action_invalid"),
|
|
551
|
+
show_alert: true,
|
|
552
|
+
});
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const [, actionRaw, payloadKeyRaw] = match;
|
|
556
|
+
const payloadKey = payloadKeyRaw?.trim();
|
|
557
|
+
const action = actionRaw?.trim().toLowerCase();
|
|
558
|
+
if (!payloadKey || !action) {
|
|
559
|
+
await ctx.answerCallbackQuery({
|
|
560
|
+
text: this.host.t(locale, "menu:notices.terminal.prompt_action_invalid"),
|
|
561
|
+
show_alert: true,
|
|
562
|
+
});
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
const payload = await this.host.getMenuPayloadByKey(payloadKey);
|
|
566
|
+
const storedActions = Array.isArray(payload?.promptActions)
|
|
567
|
+
? payload.promptActions
|
|
568
|
+
.map((value) => (typeof value === "string" ? value.trim().toLowerCase() : ""))
|
|
569
|
+
.filter((value) => value.length > 0)
|
|
570
|
+
: [];
|
|
571
|
+
if (!payload ||
|
|
572
|
+
payload.kind !== "terminal-prompt-action" ||
|
|
573
|
+
!payload.sessionId ||
|
|
574
|
+
!storedActions.includes(action)) {
|
|
575
|
+
await ctx.answerCallbackQuery({
|
|
576
|
+
text: this.host.t(locale, "menu:notices.terminal.prompt_action_stale"),
|
|
577
|
+
show_alert: true,
|
|
578
|
+
});
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
try {
|
|
582
|
+
await this.sendPromptAction(String(payload.sessionId), action);
|
|
583
|
+
await ctx.answerCallbackQuery({
|
|
584
|
+
text: this.host.t(locale, "menu:notices.terminal.prompt_action_sent", {
|
|
585
|
+
action: action === "enter"
|
|
586
|
+
? "Enter"
|
|
587
|
+
: action === "escape"
|
|
588
|
+
? "Esc"
|
|
589
|
+
: action,
|
|
590
|
+
}),
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
catch (error) {
|
|
594
|
+
this.host.logger.warn("terminal prompt action callback failed", {
|
|
595
|
+
sessionId: String(payload.sessionId),
|
|
596
|
+
action,
|
|
597
|
+
error: error instanceof Error
|
|
598
|
+
? (error.stack ?? error.message)
|
|
599
|
+
: String(error),
|
|
600
|
+
});
|
|
601
|
+
await ctx.answerCallbackQuery({
|
|
602
|
+
text: this.host.t(locale, "menu:notices.terminal.prompt_action_failed"),
|
|
603
|
+
show_alert: true,
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
}
|
|
410
607
|
async captureBuffer(session, scope) {
|
|
411
608
|
const target = session.terminalTarget;
|
|
412
609
|
if (!target) {
|
|
@@ -25,9 +25,35 @@ class TransportTerminalRuntime {
|
|
|
25
25
|
}
|
|
26
26
|
startPromptScan() {
|
|
27
27
|
if (!this.host.config.terminal.promptScanEnabled) {
|
|
28
|
+
this.host.logger.debug("terminal prompt scan disabled", {
|
|
29
|
+
promptScanEnabled: this.host.config.terminal.promptScanEnabled,
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (this.host.config.distributed.mode === "gateway") {
|
|
34
|
+
this.host.logger.info("terminal prompt scan armed for live clients", {
|
|
35
|
+
intervalSeconds: this.host.config.terminal.promptScanIntervalSeconds,
|
|
36
|
+
cooldownSeconds: this.host.config.terminal.promptScanCooldownSeconds,
|
|
37
|
+
strategy: this.host.config.terminal.promptScanStrategy,
|
|
38
|
+
minScore: this.host.config.terminal.promptScanMinScore,
|
|
39
|
+
});
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
this.ensurePromptScanRunning();
|
|
43
|
+
}
|
|
44
|
+
clearTerminalPromptScanTimer() {
|
|
45
|
+
if (this.terminalPromptScanTimer) {
|
|
46
|
+
clearInterval(this.terminalPromptScanTimer);
|
|
47
|
+
this.terminalPromptScanTimer = undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
ensurePromptScanRunning() {
|
|
51
|
+
if (!this.host.config.terminal.promptScanEnabled) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (this.terminalPromptScanTimer) {
|
|
28
55
|
return;
|
|
29
56
|
}
|
|
30
|
-
this.clearTerminalPromptScanTimer();
|
|
31
57
|
const intervalMs = this.host.config.terminal.promptScanIntervalSeconds * 1000;
|
|
32
58
|
const timer = setInterval(() => {
|
|
33
59
|
void this.runTerminalPromptScanCycle().catch((error) => {
|
|
@@ -54,11 +80,12 @@ class TransportTerminalRuntime {
|
|
|
54
80
|
});
|
|
55
81
|
});
|
|
56
82
|
}
|
|
57
|
-
|
|
58
|
-
if (this.terminalPromptScanTimer) {
|
|
59
|
-
|
|
60
|
-
this.terminalPromptScanTimer = undefined;
|
|
83
|
+
pausePromptScan() {
|
|
84
|
+
if (!this.terminalPromptScanTimer) {
|
|
85
|
+
return;
|
|
61
86
|
}
|
|
87
|
+
this.clearTerminalPromptScanTimer();
|
|
88
|
+
this.host.logger.info("terminal prompt scan paused", {});
|
|
62
89
|
}
|
|
63
90
|
scheduleTerminalNudgeForInboxMessage(sessionId, session) {
|
|
64
91
|
if (!this.host.config.terminal.nudgeEnabled) {
|
|
@@ -153,15 +180,43 @@ class TransportTerminalRuntime {
|
|
|
153
180
|
}
|
|
154
181
|
}
|
|
155
182
|
async runTerminalPromptScanCycle() {
|
|
156
|
-
if (!this.host.config.terminal.promptScanEnabled
|
|
183
|
+
if (!this.host.config.terminal.promptScanEnabled) {
|
|
184
|
+
this.host.logger.debug("terminal prompt scan cycle skipped", {
|
|
185
|
+
skipReason: "disabled",
|
|
186
|
+
});
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (this.terminalPromptScanInFlight) {
|
|
190
|
+
this.host.logger.debug("terminal prompt scan cycle skipped", {
|
|
191
|
+
skipReason: "in_flight",
|
|
192
|
+
});
|
|
157
193
|
return;
|
|
158
194
|
}
|
|
159
195
|
this.terminalPromptScanInFlight = true;
|
|
160
196
|
try {
|
|
197
|
+
if (this.host.ensureGatewayScopedConsolesBoundForPrincipal) {
|
|
198
|
+
const principals = await this.host.bindingStore.listBoundPrincipals();
|
|
199
|
+
this.host.logger.debug("terminal prompt scan syncing bound gateway principals", {
|
|
200
|
+
principalCount: principals.length,
|
|
201
|
+
});
|
|
202
|
+
for (const principal of principals) {
|
|
203
|
+
await this.host.ensureGatewayScopedConsolesBoundForPrincipal(principal);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
161
206
|
const sessions = await this.host.sessionStore.listSessions();
|
|
207
|
+
this.host.logger.debug("terminal prompt scan cycle started", {
|
|
208
|
+
sessionCount: sessions.length,
|
|
209
|
+
});
|
|
210
|
+
if (sessions.length === 0) {
|
|
211
|
+
this.host.logger.debug("terminal prompt scan cycle found no sessions");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
162
214
|
for (const session of sessions) {
|
|
163
215
|
await this.actions.scanPromptForSession(session);
|
|
164
216
|
}
|
|
217
|
+
this.host.logger.debug("terminal prompt scan cycle completed", {
|
|
218
|
+
sessionCount: sessions.length,
|
|
219
|
+
});
|
|
165
220
|
}
|
|
166
221
|
finally {
|
|
167
222
|
this.terminalPromptScanInFlight = false;
|