@quangnv13/nonstop 1.0.14 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ const logger_js_1 = require("./logger.js");
6
6
  const runtime_js_1 = require("./runtime.js");
7
7
  const ui_js_1 = require("./ui.js");
8
8
  const runtime_manager_js_1 = require("./runtime-manager.js");
9
+ const runtime_state_js_1 = require("./runtime-state.js");
9
10
  async function main() {
10
11
  (0, config_js_1.ensureEnvExampleFile)();
11
12
  const args = new Set(process.argv.slice(2));
@@ -14,10 +15,11 @@ async function main() {
14
15
  const config = (0, config_js_1.loadConfigFromDisk)();
15
16
  (0, config_js_1.applyConfigToProcessEnv)(config);
16
17
  if (isStop) {
18
+ (0, runtime_state_js_1.saveShouldRunState)(false);
17
19
  const status = (0, runtime_manager_js_1.getRuntimeStatus)();
18
20
  if (status.running && status.snapshot) {
19
21
  try {
20
- const msg = (0, runtime_manager_js_1.stopBackgroundRuntime)(status.snapshot);
22
+ const msg = (0, runtime_manager_js_1.stopBackgroundRuntime)(status.snapshot, config.language);
21
23
  console.log(msg);
22
24
  }
23
25
  catch (error) {
@@ -26,7 +28,9 @@ async function main() {
26
28
  }
27
29
  }
28
30
  else {
29
- console.log('nonstop background runtime is not running.');
31
+ console.log(config.language === 'vi'
32
+ ? '⚠ Runtime nền của nonstop không đang chạy.'
33
+ : 'nonstop background runtime is not running.');
30
34
  }
31
35
  return;
32
36
  }
@@ -39,6 +43,8 @@ async function main() {
39
43
  process.exitCode = 1;
40
44
  return;
41
45
  }
46
+ (0, runtime_state_js_1.saveShouldRunState)(true);
47
+ void (0, runtime_manager_js_1.checkUpdateOnStartup)(true, config.language);
42
48
  const runtime = new runtime_js_1.NonstopRuntime(config, 'background');
43
49
  await runtime.startBot();
44
50
  const shutdown = async () => {
@@ -64,6 +70,22 @@ async function main() {
64
70
  });
65
71
  return;
66
72
  }
73
+ // Auto-restart if it was running but is not currently running (e.g. after system restart)
74
+ const status = (0, runtime_manager_js_1.getRuntimeStatus)();
75
+ if (!status.running && (0, runtime_state_js_1.loadShouldRunState)()) {
76
+ console.log(config.language === 'vi'
77
+ ? '↻ Phát hiện trạng thái trước đó đang chạy. Đang tự khởi động lại runtime nền...'
78
+ : '↻ Detected previous running state. Auto-restarting background runtime...');
79
+ try {
80
+ const msg = (0, runtime_manager_js_1.startBackgroundRuntime)(config.language);
81
+ console.log(msg);
82
+ // Chờ một chút để tiến trình nền khởi động và ghi state/heartbeat
83
+ await new Promise((resolve) => setTimeout(resolve, 1500));
84
+ }
85
+ catch (error) {
86
+ console.error(error instanceof Error ? error.message : String(error));
87
+ }
88
+ }
67
89
  await (0, ui_js_1.launchControlCenter)();
68
90
  }
69
91
  void main();
@@ -37,9 +37,13 @@ exports.getRuntimeStatus = getRuntimeStatus;
37
37
  exports.getEntryScriptPath = getEntryScriptPath;
38
38
  exports.startBackgroundRuntime = startBackgroundRuntime;
39
39
  exports.stopBackgroundRuntime = stopBackgroundRuntime;
40
+ exports.getCurrentVersion = getCurrentVersion;
41
+ exports.checkForUpdate = checkForUpdate;
42
+ exports.checkUpdateOnStartup = checkUpdateOnStartup;
40
43
  const child_process_1 = require("child_process");
41
44
  const fs = __importStar(require("fs"));
42
45
  const path = __importStar(require("path"));
46
+ const os = __importStar(require("os"));
43
47
  const runtime_state_js_1 = require("./runtime-state.js");
44
48
  function getRuntimeStatus() {
45
49
  const snapshot = (0, runtime_state_js_1.loadRuntimeState)();
@@ -68,25 +72,126 @@ function getEntryScriptPath() {
68
72
  }
69
73
  throw new Error('dist/index.js not found. Please ensure the project is built.');
70
74
  }
71
- function startBackgroundRuntime() {
75
+ function startBackgroundRuntime(language) {
72
76
  const entryScriptPath = getEntryScriptPath();
77
+ (0, runtime_state_js_1.saveShouldRunState)(true);
73
78
  const child = (0, child_process_1.spawn)(process.execPath, [entryScriptPath, '--background'], {
74
79
  cwd: process.cwd(),
75
80
  detached: true,
76
81
  stdio: 'ignore'
77
82
  });
78
83
  child.unref();
79
- return `Started nonstop background runtime (pid ${child.pid ?? 'unknown'}).`;
84
+ const pid = child.pid ?? 'unknown';
85
+ return language === 'vi'
86
+ ? `✓ Đã khởi chạy runtime nền của nonstop (pid ${pid}).`
87
+ : `✓ Started nonstop background runtime (pid ${pid}).`;
80
88
  }
81
- function stopBackgroundRuntime(snapshot) {
89
+ function stopBackgroundRuntime(snapshot, language) {
90
+ const isVi = language === 'vi';
82
91
  if (!snapshot || !(0, runtime_state_js_1.isPidRunning)(snapshot.pid)) {
83
- return 'Background runtime is not running.';
92
+ return isVi ? '⚠ Runtime nền không đang chạy.' : 'Background runtime is not running.';
84
93
  }
94
+ (0, runtime_state_js_1.saveShouldRunState)(false);
85
95
  try {
86
96
  process.kill(snapshot.pid);
87
- return `Stopped nonstop background runtime (${snapshot.pid}).`;
97
+ return isVi
98
+ ? `✓ Đã dừng runtime nền của nonstop (${snapshot.pid}).`
99
+ : `✓ Stopped nonstop background runtime (${snapshot.pid}).`;
88
100
  }
89
101
  catch (error) {
90
- throw new Error(`Failed to stop background runtime (${snapshot.pid}): ${error instanceof Error ? error.message : String(error)}`);
102
+ const errorMsg = error instanceof Error ? error.message : String(error);
103
+ throw new Error(isVi
104
+ ? `❌ Lỗi khi dừng runtime nền (${snapshot.pid}): ${errorMsg}`
105
+ : `Failed to stop background runtime (${snapshot.pid}): ${errorMsg}`);
106
+ }
107
+ }
108
+ function getCurrentVersion() {
109
+ try {
110
+ const pkgPath = path.join(__dirname, '..', 'package.json');
111
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
112
+ return pkg.version;
113
+ }
114
+ catch {
115
+ return '1.0.13';
116
+ }
117
+ }
118
+ function checkForUpdate(currentVersion) {
119
+ return new Promise((resolve) => {
120
+ const timer = setTimeout(() => {
121
+ resolve(null);
122
+ }, 4000);
123
+ (0, child_process_1.exec)('npm view @quangnv13/nonstop version', (error, stdout) => {
124
+ clearTimeout(timer);
125
+ if (error) {
126
+ resolve(null);
127
+ return;
128
+ }
129
+ const latest = stdout.trim();
130
+ if (latest && latest !== currentVersion) {
131
+ resolve(latest);
132
+ }
133
+ else {
134
+ resolve(null);
135
+ }
136
+ });
137
+ });
138
+ }
139
+ async function checkUpdateOnStartup(isBackground, language) {
140
+ const currentVersion = getCurrentVersion();
141
+ const latestVersion = await checkForUpdate(currentVersion);
142
+ if (latestVersion) {
143
+ if (isBackground) {
144
+ promptUpgradeBackground(currentVersion, latestVersion, language);
145
+ }
146
+ }
147
+ }
148
+ function promptUpgradeBackground(currentVersion, latestVersion, language) {
149
+ const platform = os.platform();
150
+ const isVi = language === 'vi';
151
+ if (platform === 'win32') {
152
+ const title = isVi ? 'Cập nhật nonstop' : 'nonstop Update';
153
+ const msg = isVi
154
+ ? `Có phiên bản mới của nonstop: ${latestVersion} (Hiện tại: ${currentVersion}). Bạn có muốn nâng cấp không? (y/n): `
155
+ : `A new version of nonstop is available: ${latestVersion} (Current: ${currentVersion}). Do you want to upgrade? (y/n): `;
156
+ const upgradingMsg = isVi ? 'Đang nâng cấp @quangnv13/nonstop lên phiên bản mới nhất...' : 'Upgrading @quangnv13/nonstop to the latest version...';
157
+ const successMsg = isVi ? 'Cập nhật nonstop thành công! Nhấn phím bất kỳ để đóng...' : 'nonstop update successful! Press any key to close...';
158
+ const failMsg = isVi ? 'Cập nhật nonstop thất bại.' : 'nonstop update failed.';
159
+ const skippedMsg = isVi ? 'Đã bỏ qua cập nhật nonstop.' : 'nonstop update skipped.';
160
+ const psCommand = `
161
+ $Host.UI.RawUI.WindowTitle = '${title}';
162
+ Write-Host '${msg}' -NoNewline -ForegroundColor Yellow;
163
+ $choice = Read-Host;
164
+ if ($choice -eq 'y' -or $choice -eq 'yes') {
165
+ Write-Host '${upgradingMsg}' -ForegroundColor Blue;
166
+ npm install -g @quangnv13/nonstop@latest;
167
+ if ($LASTEXITCODE -eq 0) {
168
+ Write-Host '${successMsg}' -ForegroundColor Green;
169
+ } else {
170
+ Write-Host '${failMsg}' -ForegroundColor Red;
171
+ Start-Sleep -Seconds 3;
172
+ }
173
+ $null = [Console]::ReadKey();
174
+ } else {
175
+ Write-Host '${skippedMsg}' -ForegroundColor Gray;
176
+ Start-Sleep -Seconds 1;
177
+ }
178
+ `.replace(/\r?\n/g, ' ').trim();
179
+ const cmd = 'cmd.exe';
180
+ const args = [
181
+ '/c',
182
+ 'start',
183
+ 'powershell',
184
+ '-NoProfile',
185
+ '-Command',
186
+ psCommand
187
+ ];
188
+ (0, child_process_1.spawn)(cmd, args, {
189
+ detached: true,
190
+ stdio: 'ignore',
191
+ shell: true
192
+ }).unref();
193
+ }
194
+ else {
195
+ console.log(`Update available: ${latestVersion} (Current: ${currentVersion})`);
91
196
  }
92
197
  }
@@ -34,17 +34,38 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.saveRuntimeState = saveRuntimeState;
37
+ exports.saveShouldRunState = saveShouldRunState;
38
+ exports.loadShouldRunState = loadShouldRunState;
37
39
  exports.loadRuntimeState = loadRuntimeState;
38
40
  exports.clearRuntimeState = clearRuntimeState;
39
41
  exports.isPidRunning = isPidRunning;
40
42
  exports.getRuntimeStatePath = getRuntimeStatePath;
43
+ exports.getIpcSocketPath = getIpcSocketPath;
41
44
  const fs = __importStar(require("fs"));
42
45
  const path = __importStar(require("path"));
46
+ const os = __importStar(require("os"));
43
47
  const RUNTIME_STATE_PATH = path.join(process.cwd(), 'data', 'runtime-state.json');
48
+ const SHOULD_RUN_PATH = path.join(process.cwd(), 'data', 'runtime-should-run.json');
44
49
  function saveRuntimeState(snapshot) {
45
50
  fs.mkdirSync(path.dirname(RUNTIME_STATE_PATH), { recursive: true });
46
51
  fs.writeFileSync(RUNTIME_STATE_PATH, JSON.stringify(snapshot, null, 2), 'utf8');
47
52
  }
53
+ function saveShouldRunState(shouldRun) {
54
+ fs.mkdirSync(path.dirname(SHOULD_RUN_PATH), { recursive: true });
55
+ fs.writeFileSync(SHOULD_RUN_PATH, JSON.stringify({ shouldRun }, null, 2), 'utf8');
56
+ }
57
+ function loadShouldRunState() {
58
+ if (!fs.existsSync(SHOULD_RUN_PATH)) {
59
+ return false;
60
+ }
61
+ try {
62
+ const data = JSON.parse(fs.readFileSync(SHOULD_RUN_PATH, 'utf8'));
63
+ return !!data.shouldRun;
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
48
69
  function loadRuntimeState() {
49
70
  if (!fs.existsSync(RUNTIME_STATE_PATH)) {
50
71
  return null;
@@ -76,3 +97,12 @@ function isPidRunning(pid) {
76
97
  function getRuntimeStatePath() {
77
98
  return RUNTIME_STATE_PATH;
78
99
  }
100
+ function getIpcSocketPath() {
101
+ const username = os.userInfo().username || 'user';
102
+ if (process.platform === 'win32') {
103
+ return `\\\\.\\pipe\\nonstop-ipc-${username}`;
104
+ }
105
+ else {
106
+ return path.join(process.cwd(), 'data', `nonstop-ipc-${username}.sock`);
107
+ }
108
+ }
package/dist/runtime.js CHANGED
@@ -41,6 +41,7 @@ const bot_js_1 = require("./bot.js");
41
41
  const config_js_1 = require("./config.js");
42
42
  const logger_js_1 = require("./logger.js");
43
43
  const runtime_state_js_1 = require("./runtime-state.js");
44
+ const net = __importStar(require("net"));
44
45
  const session_delivery_js_1 = require("./session-delivery.js");
45
46
  const session_output_js_1 = require("./session-output.js");
46
47
  const store_js_1 = require("./store.js");
@@ -60,6 +61,8 @@ class NonstopRuntime {
60
61
  heartbeatTicker = null;
61
62
  onSessionOutputPush = null;
62
63
  bot = null;
64
+ ipcServer = null;
65
+ activeIpcSockets = new Set();
63
66
  constructor(config, mode) {
64
67
  this.config = config;
65
68
  this.mode = mode;
@@ -112,6 +115,15 @@ class NonstopRuntime {
112
115
  if (this.bot) {
113
116
  return;
114
117
  }
118
+ const ipcPath = path.join(process.cwd(), 'data', 'ipc-command.json');
119
+ if (fs.existsSync(ipcPath)) {
120
+ try {
121
+ fs.unlinkSync(ipcPath);
122
+ }
123
+ catch {
124
+ // ignore
125
+ }
126
+ }
115
127
  logger_js_1.logger.info('nonstop runtime bootstrap complete', {
116
128
  clientName: this.config.clientName,
117
129
  telegramUsername: this.config.telegramUsername,
@@ -142,17 +154,21 @@ class NonstopRuntime {
142
154
  });
143
155
  // Ghi heartbeat TRƯỚC khi bot connect để UI polling nhận ngay trạng thái RUNNING
144
156
  this.startHeartbeat();
157
+ this.startIpcServer();
145
158
  await this.bot.start({
146
159
  onStart: async (botInfo) => {
147
- logger_js_1.logger.info('Telegram bot đã khởi động', {
160
+ logger_js_1.logger.info('Telegram bot started', {
148
161
  username: botInfo.username,
149
162
  mode: this.mode
150
163
  });
151
- // Gửi thông báo hello tới Telegram
164
+ // Send startup notification to Telegram
152
165
  const lastChatId = (0, bot_js_1.loadLastChatId)();
153
166
  if (lastChatId && this.bot) {
154
167
  try {
155
- await this.bot.pushSessionOutput(lastChatId, `✅ nonstop client đã khởi động thành công và đang chạy!\n🖥 Client: ${this.config.clientName}`);
168
+ const startupMsg = this.config.language === 'vi'
169
+ ? `✅ nonstop client đã khởi động thành công và đang chạy!\n🖥 Client: ${this.config.clientName}`
170
+ : `✅ nonstop client started successfully and is running!\n🖥 Client: ${this.config.clientName}`;
171
+ await this.bot.pushSessionOutput(lastChatId, startupMsg);
156
172
  }
157
173
  catch {
158
174
  // ignore
@@ -171,6 +187,7 @@ class NonstopRuntime {
171
187
  clearInterval(this.heartbeatTicker);
172
188
  this.heartbeatTicker = null;
173
189
  }
190
+ this.stopIpcServer();
174
191
  (0, runtime_state_js_1.clearRuntimeState)();
175
192
  }
176
193
  setSessionInputMode(inputMode) {
@@ -239,6 +256,7 @@ class NonstopRuntime {
239
256
  this.writeHeartbeat();
240
257
  driver.onData((chunk) => {
241
258
  this.bufferOutput(chunk);
259
+ this.broadcastToIpcClients({ type: 'output', data: chunk });
242
260
  });
243
261
  driver.onExit((code, signal) => {
244
262
  void this.handleDriverExit(sessionId, code, signal);
@@ -322,8 +340,36 @@ class NonstopRuntime {
322
340
  }
323
341
  this.heartbeatTicker = setInterval(() => {
324
342
  this.writeHeartbeat();
343
+ void this.checkIpcCommands();
325
344
  }, 2000);
326
345
  }
346
+ async checkIpcCommands() {
347
+ const ipcPath = path.join(process.cwd(), 'data', 'ipc-command.json');
348
+ if (!fs.existsSync(ipcPath)) {
349
+ return;
350
+ }
351
+ try {
352
+ const content = fs.readFileSync(ipcPath, 'utf8');
353
+ const cmd = JSON.parse(content);
354
+ if (cmd.action === 'stop-session') {
355
+ logger_js_1.logger.info('Received IPC command to stop session via file');
356
+ await this.stopSession();
357
+ }
358
+ }
359
+ catch (error) {
360
+ logger_js_1.logger.error('Error handling IPC command', {
361
+ error: error instanceof Error ? error.message : String(error)
362
+ });
363
+ }
364
+ finally {
365
+ try {
366
+ fs.unlinkSync(ipcPath);
367
+ }
368
+ catch {
369
+ // ignore
370
+ }
371
+ }
372
+ }
327
373
  writeHeartbeat() {
328
374
  (0, runtime_state_js_1.saveRuntimeState)(this.getStatus());
329
375
  }
@@ -334,6 +380,7 @@ class NonstopRuntime {
334
380
  }
335
381
  session.status = 'stopped';
336
382
  this.activeDriverRef.current = null;
383
+ this.broadcastToIpcClients({ type: 'exit', code });
337
384
  await this.flushOutput(true);
338
385
  this.resetOutputRuntime();
339
386
  this.activeSession = null;
@@ -366,6 +413,11 @@ class NonstopRuntime {
366
413
  const promptDetectionText = stripAnsi(text);
367
414
  const snapshot = renderTerminalSnapshot(this.terminalState, this.config.maxOutputLines);
368
415
  const finalText = snapshot || limitLines(promptDetectionText, this.config.maxOutputLines);
416
+ if (this.activeIpcSockets.size > 0) {
417
+ // User is active locally. Clear buffer, sync lastSentFinalText, but skip Telegram messaging.
418
+ session.lastSentFinalText = finalText;
419
+ return;
420
+ }
369
421
  if (!text && !forceSnapshot) {
370
422
  return;
371
423
  }
@@ -391,7 +443,14 @@ class NonstopRuntime {
391
443
  return;
392
444
  }
393
445
  for (const message of messages) {
394
- await this.onSessionOutputPush(session.listenerChatId, message.text, message.options);
446
+ try {
447
+ await this.onSessionOutputPush(session.listenerChatId, message.text, message.options);
448
+ }
449
+ catch (err) {
450
+ logger_js_1.logger.error('Failed to push session output to Telegram', {
451
+ error: err instanceof Error ? err.message : String(err)
452
+ });
453
+ }
395
454
  }
396
455
  session.lastSentFinalText = finalText;
397
456
  }
@@ -418,6 +477,129 @@ class NonstopRuntime {
418
477
  this.activeSession.lastSentFinalText = '';
419
478
  }
420
479
  }
480
+ startIpcServer() {
481
+ if (this.ipcServer)
482
+ return;
483
+ const socketPath = (0, runtime_state_js_1.getIpcSocketPath)();
484
+ if (process.platform !== 'win32') {
485
+ try {
486
+ const socketDir = path.dirname(socketPath);
487
+ if (!fs.existsSync(socketDir)) {
488
+ fs.mkdirSync(socketDir, { recursive: true });
489
+ }
490
+ if (fs.existsSync(socketPath)) {
491
+ fs.unlinkSync(socketPath);
492
+ }
493
+ }
494
+ catch (err) {
495
+ logger_js_1.logger.error('Failed to unlink existing IPC socket file', { err });
496
+ }
497
+ }
498
+ this.ipcServer = net.createServer((socket) => {
499
+ logger_js_1.logger.info('IPC client connected to background session');
500
+ this.activeIpcSockets.add(socket);
501
+ // Immediately send the current screen snapshot to the newly connected client
502
+ const snapshot = renderTerminalSnapshot(this.terminalState, this.config.maxRenderLines);
503
+ const initOutput = '\u001b[2J\u001b[H' + snapshot;
504
+ socket.write(JSON.stringify({ type: 'output', data: initOutput }) + '\n');
505
+ let buffer = '';
506
+ socket.on('data', (data) => {
507
+ buffer += data.toString();
508
+ let boundary = buffer.indexOf('\n');
509
+ while (boundary !== -1) {
510
+ const line = buffer.slice(0, boundary).trim();
511
+ buffer = buffer.slice(boundary + 1);
512
+ if (line) {
513
+ try {
514
+ const msg = JSON.parse(line);
515
+ this.handleIpcClientMessage(msg);
516
+ }
517
+ catch (err) {
518
+ logger_js_1.logger.error('Error parsing IPC client message', { err, line });
519
+ }
520
+ }
521
+ boundary = buffer.indexOf('\n');
522
+ }
523
+ });
524
+ socket.on('error', (err) => {
525
+ logger_js_1.logger.error('IPC client socket error', { err });
526
+ });
527
+ socket.on('close', () => {
528
+ logger_js_1.logger.info('IPC client disconnected');
529
+ this.activeIpcSockets.delete(socket);
530
+ if (this.activeIpcSockets.size === 0) {
531
+ // Immediately flush the final state of the session to Telegram
532
+ void this.flushOutput(true, true);
533
+ }
534
+ });
535
+ });
536
+ this.ipcServer.listen(socketPath, () => {
537
+ logger_js_1.logger.info(`IPC Server listening on ${socketPath}`);
538
+ });
539
+ this.ipcServer.on('error', (err) => {
540
+ logger_js_1.logger.error('IPC Server error', { err });
541
+ });
542
+ }
543
+ handleIpcClientMessage(msg) {
544
+ if (!msg || typeof msg !== 'object')
545
+ return;
546
+ switch (msg.type) {
547
+ case 'input':
548
+ if (typeof msg.data === 'string') {
549
+ this.sendSessionInput(msg.data);
550
+ }
551
+ break;
552
+ case 'resize':
553
+ if (typeof msg.cols === 'number' && typeof msg.rows === 'number') {
554
+ const driver = this.activeDriverRef.current;
555
+ if (driver && this.activeSession?.status === 'running') {
556
+ driver.resize(msg.cols, msg.rows);
557
+ }
558
+ }
559
+ break;
560
+ default:
561
+ logger_js_1.logger.warn('Unknown IPC message type', { msg });
562
+ }
563
+ }
564
+ stopIpcServer() {
565
+ if (this.ipcServer) {
566
+ for (const socket of this.activeIpcSockets) {
567
+ try {
568
+ socket.destroy();
569
+ }
570
+ catch {
571
+ // ignore
572
+ }
573
+ }
574
+ this.activeIpcSockets.clear();
575
+ this.ipcServer.close();
576
+ this.ipcServer = null;
577
+ const socketPath = (0, runtime_state_js_1.getIpcSocketPath)();
578
+ if (process.platform !== 'win32') {
579
+ try {
580
+ if (fs.existsSync(socketPath)) {
581
+ fs.unlinkSync(socketPath);
582
+ }
583
+ }
584
+ catch {
585
+ // ignore
586
+ }
587
+ }
588
+ }
589
+ }
590
+ broadcastToIpcClients(msg) {
591
+ if (this.activeIpcSockets.size === 0)
592
+ return;
593
+ const packet = JSON.stringify(msg) + '\n';
594
+ for (const socket of this.activeIpcSockets) {
595
+ try {
596
+ socket.write(packet);
597
+ }
598
+ catch (err) {
599
+ logger_js_1.logger.error('Failed to write to IPC client socket', { err });
600
+ }
601
+ }
602
+ }
421
603
  }
422
604
  exports.NonstopRuntime = NonstopRuntime;
423
605
  function createSessionId(preset) {
@@ -437,7 +619,7 @@ function resolveKeyInput(key, preset) {
437
619
  case 'send_escape':
438
620
  case 'escape':
439
621
  case 'interrupt':
440
- if (preset === 'codex' || preset === 'antigravity') {
622
+ if (preset === 'codex' || preset === 'antigravity' || preset === 'claude') {
441
623
  return '\u001b';
442
624
  }
443
625
  return null;
@@ -9,14 +9,14 @@ function buildSessionActionMarkup(options) {
9
9
  [
10
10
  {
11
11
  text: inputMode
12
- ? (isVi ? '⌨️ Tắt Input' : '⌨️ Input OFF')
13
- : (isVi ? '⌨️ Bật Input' : '⌨️ Input ON'),
12
+ ? (isVi ? '⌨️ Tắt Nhập' : '⌨️ Input OFF')
13
+ : (isVi ? '⌨️ Bật Nhập' : '⌨️ Input ON'),
14
14
  callback_data: `session_cmd:${options.sessionId}:toggle_input`
15
15
  },
16
16
  {
17
17
  text: autoEnter
18
18
  ? (isVi ? '⏎ Tắt AutoEnter' : '⏎ AutoEnter OFF')
19
- : (isVi ? '⏎ Bật AutoEnter' : '⏎ Bật AutoEnter ON'),
19
+ : (isVi ? '⏎ Bật AutoEnter' : '⏎ AutoEnter ON'),
20
20
  callback_data: `session_cmd:${options.sessionId}:toggle_enter`
21
21
  },
22
22
  {
package/dist/terminal.js CHANGED
@@ -92,7 +92,7 @@ class NodePtyTerminalDriver {
92
92
  }
93
93
  }
94
94
  exports.NodePtyTerminalDriver = NodePtyTerminalDriver;
95
- exports.SUPPORTED_PRESETS = ['powershell', 'bash', 'codex', 'antigravity'];
95
+ exports.SUPPORTED_PRESETS = ['powershell', 'bash', 'codex', 'antigravity', 'claude'];
96
96
  function resolvePreset(presetName) {
97
97
  const isWindows = process.platform === 'win32';
98
98
  switch (presetName) {
@@ -132,6 +132,19 @@ function resolvePreset(presetName) {
132
132
  }
133
133
  return { command, args };
134
134
  }
135
+ case 'claude': {
136
+ const command = process.env.CLAUDE_CMD || 'claude';
137
+ let args = [];
138
+ if (process.env.CLAUDE_ARGS) {
139
+ try {
140
+ args = JSON.parse(process.env.CLAUDE_ARGS);
141
+ }
142
+ catch {
143
+ args = process.env.CLAUDE_ARGS.split(/\s+/).filter(Boolean);
144
+ }
145
+ }
146
+ return { command, args };
147
+ }
135
148
  default:
136
149
  throw new Error(`Unsupported preset "${presetName}".`);
137
150
  }