@quangnv13/nonstop 1.0.0

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.
@@ -0,0 +1,622 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.NonstopRuntime = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const bot_js_1 = require("./bot.js");
41
+ const config_js_1 = require("./config.js");
42
+ const logger_js_1 = require("./logger.js");
43
+ const runtime_state_js_1 = require("./runtime-state.js");
44
+ const session_delivery_js_1 = require("./session-delivery.js");
45
+ const session_output_js_1 = require("./session-output.js");
46
+ const store_js_1 = require("./store.js");
47
+ const terminal_js_1 = require("./terminal.js");
48
+ class NonstopRuntime {
49
+ config;
50
+ mode;
51
+ startedAt = new Date().toISOString();
52
+ lastError = null;
53
+ workspaces = (0, store_js_1.loadWorkspaces)();
54
+ activeSession = null;
55
+ activeDriverRef = { current: null };
56
+ outputBuffer = { current: '' };
57
+ terminalState = createTerminalState();
58
+ outputTicker = null;
59
+ actionOutputTimeout = null;
60
+ heartbeatTicker = null;
61
+ onSessionOutputPush = null;
62
+ bot = null;
63
+ constructor(config, mode) {
64
+ this.config = config;
65
+ this.mode = mode;
66
+ }
67
+ getStatus() {
68
+ return {
69
+ pid: process.pid,
70
+ startedAt: this.startedAt,
71
+ lastHeartbeatAt: new Date().toISOString(),
72
+ mode: this.mode,
73
+ clientName: this.config.clientName || os.hostname() || 'LocalClient',
74
+ botRunning: this.bot !== null,
75
+ workspaceCount: this.workspaces.length,
76
+ activeSession: this.activeSession,
77
+ lastError: this.lastError
78
+ };
79
+ }
80
+ getWorkspaces() {
81
+ return this.workspaces;
82
+ }
83
+ saveWorkspaces(nextWorkspaces) {
84
+ this.workspaces = [...nextWorkspaces];
85
+ (0, store_js_1.saveWorkspaces)(this.workspaces);
86
+ this.writeHeartbeat();
87
+ }
88
+ getActiveSession() {
89
+ return this.activeSession;
90
+ }
91
+ getConfig() {
92
+ return this.config;
93
+ }
94
+ async saveConfig(nextConfig) {
95
+ const tokenChanged = this.config.telegramBotToken !== nextConfig.telegramBotToken;
96
+ this.config = nextConfig;
97
+ (0, config_js_1.saveConfigToDisk)(nextConfig);
98
+ (0, config_js_1.applyConfigToProcessEnv)(nextConfig);
99
+ this.writeHeartbeat();
100
+ if (tokenChanged) {
101
+ setTimeout(() => {
102
+ void this.restartBot();
103
+ }, 1000);
104
+ }
105
+ }
106
+ async restartBot() {
107
+ logger_js_1.logger.info('Restarting Telegram bot due to token change...');
108
+ await this.stopBot();
109
+ await this.startBot();
110
+ }
111
+ async startBot() {
112
+ if (this.bot) {
113
+ return;
114
+ }
115
+ logger_js_1.logger.info('nonstop runtime bootstrap complete', {
116
+ clientName: this.config.clientName,
117
+ telegramUsername: this.config.telegramUsername,
118
+ workspaceCount: this.workspaces.length,
119
+ supportedPresets: terminal_js_1.SUPPORTED_PRESETS,
120
+ mode: this.mode
121
+ });
122
+ this.bot = (0, bot_js_1.createBotRuntime)({
123
+ getConfig: () => this.getConfig(),
124
+ saveConfig: async (config) => {
125
+ await this.saveConfig(config);
126
+ },
127
+ getWorkspaces: () => this.getWorkspaces(),
128
+ saveWorkspaces: (workspaces) => this.saveWorkspaces(workspaces),
129
+ getActiveSession: () => this.getActiveSession(),
130
+ startSession: async (chatId, workspaceId, preset) => {
131
+ await this.startSession(chatId, this.resolveWorkspaceById(workspaceId), preset);
132
+ },
133
+ stopSession: async () => this.stopSession(),
134
+ sendInput: (data) => this.sendSessionInput(data),
135
+ sendKey: (key) => this.sendSessionKey(key),
136
+ setInputMode: (inputMode) => this.setSessionInputMode(inputMode),
137
+ setAutoEnter: (autoEnter) => this.setSessionAutoEnter(autoEnter)
138
+ });
139
+ this.setSessionOutputPushCallback(async (chatId, text, options) => {
140
+ await this.bot?.pushSessionOutput(chatId, text, options);
141
+ });
142
+ // Ghi heartbeat TRƯỚC khi bot connect để UI polling nhận ngay trạng thái RUNNING
143
+ this.startHeartbeat();
144
+ await this.bot.start({
145
+ onStart: async (botInfo) => {
146
+ logger_js_1.logger.info('Telegram bot đã khởi động', {
147
+ username: botInfo.username,
148
+ mode: this.mode
149
+ });
150
+ // Gửi thông báo hello tới Telegram
151
+ const lastChatId = (0, bot_js_1.loadLastChatId)();
152
+ if (lastChatId && this.bot) {
153
+ try {
154
+ await this.bot.pushSessionOutput(lastChatId, `✅ nonstop client đã khởi động thành công và đang chạy!\n🖥 Client: ${this.config.clientName}`);
155
+ }
156
+ catch {
157
+ // ignore
158
+ }
159
+ }
160
+ }
161
+ });
162
+ }
163
+ async stopBot() {
164
+ await this.stopSession();
165
+ if (this.bot) {
166
+ await this.bot.stop();
167
+ this.bot = null;
168
+ }
169
+ if (this.heartbeatTicker) {
170
+ clearInterval(this.heartbeatTicker);
171
+ this.heartbeatTicker = null;
172
+ }
173
+ (0, runtime_state_js_1.clearRuntimeState)();
174
+ }
175
+ setSessionInputMode(inputMode) {
176
+ if (this.activeSession) {
177
+ this.activeSession.inputMode = inputMode;
178
+ this.writeHeartbeat();
179
+ }
180
+ }
181
+ setSessionAutoEnter(autoEnter) {
182
+ if (this.activeSession) {
183
+ this.activeSession.autoEnter = autoEnter;
184
+ this.writeHeartbeat();
185
+ }
186
+ }
187
+ async startSession(chatId, workspace, preset) {
188
+ if (this.activeSession?.status === 'running') {
189
+ throw new Error(`Session "${this.activeSession.sessionId}" is already running.`);
190
+ }
191
+ const cwd = path.resolve(workspace.path);
192
+ if (!fs.existsSync(cwd)) {
193
+ throw new Error(`Workspace path "${cwd}" does not exist.`);
194
+ }
195
+ const { command, args } = (0, terminal_js_1.resolvePreset)(preset);
196
+ const sessionId = createSessionId(preset);
197
+ this.resetOutputRuntime();
198
+ const nextSession = {
199
+ sessionId,
200
+ preset,
201
+ cwd,
202
+ status: 'running',
203
+ listenerChatId: chatId,
204
+ lastSentFinalText: '',
205
+ inputMode: true,
206
+ autoEnter: true
207
+ };
208
+ logger_js_1.logger.info('Starting local session', {
209
+ sessionId,
210
+ preset,
211
+ chatId,
212
+ cwd,
213
+ command,
214
+ args
215
+ });
216
+ try {
217
+ const driver = new terminal_js_1.NodePtyTerminalDriver(command, args, cwd);
218
+ this.activeSession = nextSession;
219
+ this.activeDriverRef.current = driver;
220
+ this.writeHeartbeat();
221
+ driver.onData((chunk) => {
222
+ this.bufferOutput(chunk);
223
+ });
224
+ driver.onExit((code, signal) => {
225
+ void this.handleDriverExit(sessionId, code, signal);
226
+ });
227
+ }
228
+ catch (error) {
229
+ this.activeSession = null;
230
+ this.activeDriverRef.current = null;
231
+ this.resetOutputRuntime();
232
+ this.lastError = error instanceof Error ? error.message : String(error);
233
+ this.writeHeartbeat();
234
+ throw error;
235
+ }
236
+ }
237
+ async stopSession() {
238
+ const session = this.activeSession;
239
+ if (!session || session.status !== 'running') {
240
+ return;
241
+ }
242
+ session.status = 'stopped';
243
+ const driver = this.activeDriverRef.current;
244
+ this.activeDriverRef.current = null;
245
+ if (driver) {
246
+ driver.kill();
247
+ }
248
+ await this.flushOutput(true);
249
+ this.resetOutputRuntime();
250
+ this.activeSession = null;
251
+ this.writeHeartbeat();
252
+ logger_js_1.logger.info('Stopped local session', {
253
+ sessionId: session.sessionId
254
+ });
255
+ }
256
+ sendSessionInput(data) {
257
+ const driver = this.activeDriverRef.current;
258
+ if (!driver || this.activeSession?.status !== 'running') {
259
+ logger_js_1.logger.warn('Dropping session input because no active session is running', {
260
+ length: data.length
261
+ });
262
+ return;
263
+ }
264
+ driver.write(data);
265
+ }
266
+ sendSessionKey(key) {
267
+ const driver = this.activeDriverRef.current;
268
+ const session = this.activeSession;
269
+ if (!driver || !session || session.status !== 'running') {
270
+ logger_js_1.logger.warn('Dropping session key because no active session is running', { key });
271
+ return;
272
+ }
273
+ const input = resolveKeyInput(key, session.preset);
274
+ if (!input) {
275
+ logger_js_1.logger.warn('Ignoring unsupported session key', {
276
+ key,
277
+ preset: session.preset
278
+ });
279
+ return;
280
+ }
281
+ driver.write(input);
282
+ if (['send_escape', 'send_enter', 'send_up', 'send_down'].includes(key)) {
283
+ if (this.outputTicker) {
284
+ clearInterval(this.outputTicker);
285
+ this.outputTicker = null;
286
+ }
287
+ if (this.actionOutputTimeout) {
288
+ clearTimeout(this.actionOutputTimeout);
289
+ this.actionOutputTimeout = null;
290
+ }
291
+ this.actionOutputTimeout = setTimeout(async () => {
292
+ this.actionOutputTimeout = null;
293
+ await this.flushOutput(true);
294
+ this.ensureOutputTicker();
295
+ }, 5000);
296
+ }
297
+ }
298
+ resolveWorkspaceById(workspaceId) {
299
+ const workspace = this.workspaces.find((candidate) => candidate.id === workspaceId);
300
+ if (!workspace) {
301
+ throw new Error(`Workspace "${workspaceId}" not found.`);
302
+ }
303
+ return workspace;
304
+ }
305
+ setSessionOutputPushCallback(callback) {
306
+ this.onSessionOutputPush = callback;
307
+ }
308
+ startHeartbeat() {
309
+ this.writeHeartbeat();
310
+ if (this.heartbeatTicker) {
311
+ clearInterval(this.heartbeatTicker);
312
+ }
313
+ this.heartbeatTicker = setInterval(() => {
314
+ this.writeHeartbeat();
315
+ }, 2000);
316
+ }
317
+ writeHeartbeat() {
318
+ (0, runtime_state_js_1.saveRuntimeState)(this.getStatus());
319
+ }
320
+ async handleDriverExit(sessionId, code, signal) {
321
+ const session = this.activeSession;
322
+ if (!session || session.sessionId !== sessionId || session.status !== 'running') {
323
+ return;
324
+ }
325
+ session.status = 'stopped';
326
+ this.activeDriverRef.current = null;
327
+ await this.flushOutput(true);
328
+ this.resetOutputRuntime();
329
+ this.activeSession = null;
330
+ this.writeHeartbeat();
331
+ logger_js_1.logger.warn('Local PTY session exited', {
332
+ sessionId,
333
+ code,
334
+ signal
335
+ });
336
+ if (this.onSessionOutputPush) {
337
+ await this.onSessionOutputPush(session.listenerChatId, `Session \`${sessionId}\` exited with code \`${code}\`.`);
338
+ }
339
+ }
340
+ bufferOutput(chunk) {
341
+ if (!this.activeSession) {
342
+ return;
343
+ }
344
+ this.outputBuffer.current += chunk;
345
+ applyTerminalOutput(this.terminalState, chunk, this.config.maxRenderLines);
346
+ this.ensureOutputTicker();
347
+ }
348
+ async flushOutput(forceSnapshot = false) {
349
+ const session = this.activeSession;
350
+ if (!session) {
351
+ this.outputBuffer.current = '';
352
+ return;
353
+ }
354
+ const text = this.outputBuffer.current;
355
+ this.outputBuffer.current = '';
356
+ const promptDetectionText = stripAnsi(text);
357
+ const snapshot = renderTerminalSnapshot(this.terminalState, this.config.maxOutputLines);
358
+ const finalText = snapshot || limitLines(promptDetectionText, this.config.maxOutputLines);
359
+ if (!text && !forceSnapshot) {
360
+ return;
361
+ }
362
+ if (!finalText.trim()) {
363
+ return;
364
+ }
365
+ // Lọc loading spinner và window title residue
366
+ if (isSpinnerOrNoiseOutput(finalText)) {
367
+ return;
368
+ }
369
+ if ((0, session_delivery_js_1.shouldSkipSessionOutput)(session.lastSentFinalText, finalText)) {
370
+ return;
371
+ }
372
+ const messages = (0, session_output_js_1.buildSessionOutputMessages)({
373
+ sessionId: session.sessionId,
374
+ snapshot: finalText,
375
+ inputMode: session.inputMode,
376
+ autoEnter: session.autoEnter
377
+ });
378
+ if (!this.onSessionOutputPush) {
379
+ session.lastSentFinalText = finalText;
380
+ return;
381
+ }
382
+ for (const message of messages) {
383
+ await this.onSessionOutputPush(session.listenerChatId, message.text, message.options);
384
+ }
385
+ session.lastSentFinalText = finalText;
386
+ }
387
+ ensureOutputTicker() {
388
+ if (this.outputTicker || this.actionOutputTimeout) {
389
+ return;
390
+ }
391
+ this.outputTicker = setInterval(() => {
392
+ void this.flushOutput(true);
393
+ }, this.config.outputInterval);
394
+ }
395
+ resetOutputRuntime() {
396
+ if (this.outputTicker) {
397
+ clearInterval(this.outputTicker);
398
+ this.outputTicker = null;
399
+ }
400
+ if (this.actionOutputTimeout) {
401
+ clearTimeout(this.actionOutputTimeout);
402
+ this.actionOutputTimeout = null;
403
+ }
404
+ this.outputBuffer.current = '';
405
+ this.terminalState = createTerminalState();
406
+ if (this.activeSession) {
407
+ this.activeSession.lastSentFinalText = '';
408
+ }
409
+ }
410
+ }
411
+ exports.NonstopRuntime = NonstopRuntime;
412
+ function createSessionId(preset) {
413
+ return `${preset}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
414
+ }
415
+ function resolveKeyInput(key, preset) {
416
+ switch (key) {
417
+ case 'send_up':
418
+ case 'up':
419
+ return '\u001b[A';
420
+ case 'send_down':
421
+ case 'down':
422
+ return '\u001b[B';
423
+ case 'send_enter':
424
+ case 'enter':
425
+ return '\r';
426
+ case 'send_escape':
427
+ case 'escape':
428
+ case 'interrupt':
429
+ if (preset === 'codex' || preset === 'antigravity') {
430
+ return '\u001b';
431
+ }
432
+ return null;
433
+ default:
434
+ return null;
435
+ }
436
+ }
437
+ function limitLines(text, maxLines) {
438
+ const lines = text.split(/\r?\n/);
439
+ if (lines.length <= maxLines) {
440
+ return text;
441
+ }
442
+ return lines.slice(-maxLines).join('\n');
443
+ }
444
+ function stripAnsi(text) {
445
+ const ansiRegex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
446
+ return text.replace(ansiRegex, '');
447
+ }
448
+ function createTerminalState() {
449
+ return { lines: [''], row: 0, col: 0, savedRow: 0, savedCol: 0 };
450
+ }
451
+ function ensureLine(state, row) {
452
+ while (state.lines.length <= row) {
453
+ state.lines.push('');
454
+ }
455
+ }
456
+ function writeAt(state, char) {
457
+ ensureLine(state, state.row);
458
+ const current = state.lines[state.row];
459
+ const padded = current.padEnd(state.col, ' ');
460
+ state.lines[state.row] = padded.slice(0, state.col) + char + padded.slice(state.col + 1);
461
+ state.col += 1;
462
+ }
463
+ function clearLineFromCursor(state) {
464
+ ensureLine(state, state.row);
465
+ state.lines[state.row] = state.lines[state.row].slice(0, state.col);
466
+ }
467
+ function trimTerminalHistory(state, maxRenderLines) {
468
+ if (state.lines.length <= maxRenderLines) {
469
+ return;
470
+ }
471
+ const removeCount = state.lines.length - maxRenderLines;
472
+ state.lines = state.lines.slice(removeCount);
473
+ state.row = Math.max(0, state.row - removeCount);
474
+ state.savedRow = Math.max(0, state.savedRow - removeCount);
475
+ }
476
+ function handleCsiSequence(state, paramsRaw, command) {
477
+ const privateMode = paramsRaw.startsWith('?');
478
+ const normalized = privateMode ? paramsRaw.slice(1) : paramsRaw;
479
+ const params = normalized.length > 0
480
+ ? normalized.split(';').map((value) => {
481
+ const parsed = parseInt(value, 10);
482
+ return Number.isFinite(parsed) ? parsed : 0;
483
+ })
484
+ : [];
485
+ switch (command) {
486
+ case 'A':
487
+ state.row = Math.max(0, state.row - (params[0] || 1));
488
+ return;
489
+ case 'B':
490
+ state.row += params[0] || 1;
491
+ ensureLine(state, state.row);
492
+ return;
493
+ case 'C':
494
+ state.col += params[0] || 1;
495
+ return;
496
+ case 'D':
497
+ state.col = Math.max(0, state.col - (params[0] || 1));
498
+ return;
499
+ case 'G':
500
+ state.col = Math.max(0, (params[0] || 1) - 1);
501
+ return;
502
+ case 'H':
503
+ case 'f':
504
+ state.row = Math.max(0, (params[0] || 1) - 1);
505
+ state.col = Math.max(0, (params[1] || 1) - 1);
506
+ ensureLine(state, state.row);
507
+ return;
508
+ case 'J':
509
+ if ((params[0] || 0) === 2) {
510
+ state.lines = [''];
511
+ state.row = 0;
512
+ state.col = 0;
513
+ state.savedRow = 0;
514
+ state.savedCol = 0;
515
+ }
516
+ return;
517
+ case 'K':
518
+ clearLineFromCursor(state);
519
+ return;
520
+ case 's':
521
+ state.savedRow = state.row;
522
+ state.savedCol = state.col;
523
+ return;
524
+ case 'u':
525
+ state.row = state.savedRow;
526
+ state.col = state.savedCol;
527
+ ensureLine(state, state.row);
528
+ return;
529
+ default:
530
+ return;
531
+ }
532
+ }
533
+ function applyTerminalOutput(state, chunk, maxRenderLines) {
534
+ let index = 0;
535
+ while (index < chunk.length) {
536
+ const char = chunk[index];
537
+ if (char === '\u001b') {
538
+ const next = chunk[index + 1];
539
+ if (next === '[') {
540
+ const match = chunk.slice(index).match(/^\u001b\[([0-9;?]*)([@-~])/);
541
+ if (match) {
542
+ handleCsiSequence(state, match[1], match[2]);
543
+ index += match[0].length;
544
+ continue;
545
+ }
546
+ }
547
+ if (next === ']') {
548
+ const belIndex = chunk.indexOf('\u0007', index + 2);
549
+ if (belIndex !== -1) {
550
+ index = belIndex + 1;
551
+ continue;
552
+ }
553
+ }
554
+ index += 1;
555
+ continue;
556
+ }
557
+ if (char === '\r') {
558
+ state.col = 0;
559
+ index += 1;
560
+ continue;
561
+ }
562
+ if (char === '\n') {
563
+ state.row += 1;
564
+ state.col = 0;
565
+ ensureLine(state, state.row);
566
+ trimTerminalHistory(state, maxRenderLines);
567
+ index += 1;
568
+ continue;
569
+ }
570
+ if (char === '\b') {
571
+ state.col = Math.max(0, state.col - 1);
572
+ index += 1;
573
+ continue;
574
+ }
575
+ if (char === '\t') {
576
+ const spaces = 4 - (state.col % 4);
577
+ for (let i = 0; i < spaces; i += 1) {
578
+ writeAt(state, ' ');
579
+ }
580
+ index += 1;
581
+ continue;
582
+ }
583
+ if (char >= ' ') {
584
+ writeAt(state, char);
585
+ }
586
+ index += 1;
587
+ }
588
+ trimTerminalHistory(state, maxRenderLines);
589
+ }
590
+ function renderTerminalSnapshot(state, maxOutputLines) {
591
+ const rawText = state.lines
592
+ .map((line) => line.replace(/\s+$/g, ''))
593
+ .join('\n')
594
+ .replace(/\n{3,}/g, '\n\n')
595
+ .trim();
596
+ if (!rawText) {
597
+ return '';
598
+ }
599
+ const rawLines = rawText.split('\n');
600
+ const nonEmptyLines = rawLines.filter((line) => line.trim().length > 0);
601
+ const commonIndent = nonEmptyLines.length > 0
602
+ ? Math.min(...nonEmptyLines.map((line) => line.match(/^ */)?.[0].length ?? 0))
603
+ : 0;
604
+ return limitLines(rawLines.map((line) => line.slice(commonIndent)).join('\n').trim(), maxOutputLines);
605
+ }
606
+ /**
607
+ * Lọc bỏ output chỉ chứa loading spinner, window title, hoặc ký tự thừa trước khi gửi Telegram.
608
+ */
609
+ function isSpinnerOrNoiseOutput(text) {
610
+ const lines = text.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
611
+ if (lines.length === 0)
612
+ return true;
613
+ // Nếu tất cả các dòng đều là noise thì bỏ
614
+ const noisePatterns = [
615
+ /^[\u2800-\u28FF\s]+$/, // Braille spinner
616
+ /^\]0;/, // Window title sequence
617
+ /^q{1,4}\d?\w{0,5}$/, // "q", "q8", "qrk" etc.
618
+ /^[\s\u2800-\u28FF\]0;q\r\n]{1,20}$/ // Short mixed noise
619
+ ];
620
+ const allNoise = lines.every(line => noisePatterns.some(pattern => pattern.test(line)));
621
+ return allNoise;
622
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSessionActionMarkup = buildSessionActionMarkup;
4
+ function buildSessionActionMarkup(options) {
5
+ const inputMode = options.inputMode ?? true;
6
+ const autoEnter = options.autoEnter ?? true;
7
+ const rows = [
8
+ [
9
+ {
10
+ text: inputMode ? '⌨️ Input OFF' : '⌨️ Input ON',
11
+ callback_data: `session_cmd:${options.sessionId}:toggle_input`
12
+ },
13
+ {
14
+ text: autoEnter ? '⏎ AutoEnter OFF' : '⏎ AutoEnter ON',
15
+ callback_data: `session_cmd:${options.sessionId}:toggle_enter`
16
+ },
17
+ {
18
+ text: '🔄 Refresh',
19
+ callback_data: `session_cmd:${options.sessionId}:refresh`
20
+ }
21
+ ],
22
+ [
23
+ {
24
+ text: '⛔ Esc',
25
+ callback_data: `session_cmd:${options.sessionId}:send_escape`
26
+ },
27
+ {
28
+ text: '⬆️ Up',
29
+ callback_data: `session_cmd:${options.sessionId}:send_up`
30
+ },
31
+ {
32
+ text: '⬇️ Down',
33
+ callback_data: `session_cmd:${options.sessionId}:send_down`
34
+ }
35
+ ],
36
+ [
37
+ {
38
+ text: '⏎ Enter',
39
+ callback_data: `session_cmd:${options.sessionId}:send_enter`
40
+ },
41
+ {
42
+ text: '🛑 Stop',
43
+ callback_data: `session_cmd:${options.sessionId}:stop`
44
+ }
45
+ ]
46
+ ];
47
+ if (options.includeBackButton) {
48
+ rows.push([
49
+ {
50
+ text: '⬅️ Back',
51
+ callback_data: 'sessions_list'
52
+ }
53
+ ]);
54
+ }
55
+ return { inline_keyboard: rows };
56
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shouldSkipSessionOutput = shouldSkipSessionOutput;
4
+ function shouldSkipSessionOutput(previousFinalText, nextFinalText) {
5
+ return Boolean(previousFinalText) && previousFinalText === nextFinalText;
6
+ }