@quangnv13/nonstop 1.0.13 → 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/ui.js CHANGED
@@ -39,6 +39,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.launchControlCenter = launchControlCenter;
40
40
  const fs = __importStar(require("fs"));
41
41
  const path = __importStar(require("path"));
42
+ const os = __importStar(require("os"));
43
+ const child_process_1 = require("child_process");
42
44
  const readline = __importStar(require("node:readline"));
43
45
  const promises_1 = require("node:readline/promises");
44
46
  const node_process_1 = require("node:process");
@@ -49,6 +51,8 @@ const i18n_js_1 = require("./i18n.js");
49
51
  const runtime_manager_js_1 = require("./runtime-manager.js");
50
52
  const startup_js_1 = require("./startup.js");
51
53
  const store_js_1 = require("./store.js");
54
+ const runtime_state_js_1 = require("./runtime-state.js");
55
+ const net = __importStar(require("net"));
52
56
  function clearScreen() {
53
57
  // \u001b[2J clears visible area, \u001b[3J clears scrollback buffer, \u001b[H moves cursor home
54
58
  node_process_1.stdout.write('\u001b[2J\u001b[3J\u001b[H');
@@ -77,16 +81,27 @@ async function askQuestion(query) {
77
81
  process.stdin.resume();
78
82
  }
79
83
  }
80
- async function pause() {
81
- await askQuestion(`\n${chalk_1.default.gray('Nhấn Enter để tiếp tục...')}`);
84
+ async function pause(language) {
85
+ const isVi = language !== 'en';
86
+ await askQuestion(`\n${chalk_1.default.gray(isVi ? 'Nhấn Enter để tiếp tục...' : 'Press Enter to continue...')}`);
82
87
  }
83
88
  async function askWithDefault(label, currentValue) {
84
89
  const prompt = `${chalk_1.default.bold(label)}${currentValue ? chalk_1.default.gray(` [${currentValue}]`) : ''}: `;
85
90
  const answer = await askQuestion(prompt);
86
91
  return answer.trim() || currentValue;
87
92
  }
88
- async function runSelectionMenu(headerRenderer, options, initialIndex = 0) {
93
+ async function runSelectionMenu(headerRenderer, options, initialIndex = 0, language) {
89
94
  let selectedIndex = Math.min(initialIndex, options.length - 1);
95
+ let resolvedLang = language;
96
+ if (!resolvedLang) {
97
+ try {
98
+ resolvedLang = (0, config_js_1.loadConfigFromDisk)().language;
99
+ }
100
+ catch {
101
+ resolvedLang = 'en';
102
+ }
103
+ }
104
+ const isVi = resolvedLang === 'vi';
90
105
  return new Promise((resolve, reject) => {
91
106
  const isTTY = process.stdin.isTTY;
92
107
  const wasRaw = process.stdin.isRaw;
@@ -113,7 +128,7 @@ async function runSelectionMenu(headerRenderer, options, initialIndex = 0) {
113
128
  }
114
129
  });
115
130
  console.log('');
116
- console.log(chalk_1.default.gray(' ↑↓ di chuyển Enter xác nhận'));
131
+ console.log(chalk_1.default.gray(isVi ? ' ↑↓ di chuyển Enter xác nhận' : ' ↑↓ navigate Enter to select'));
117
132
  }
118
133
  try {
119
134
  readline.emitKeypressEvents(process.stdin);
@@ -153,35 +168,129 @@ function renderDashboardHeader(config, snapshot) {
153
168
  const isRunning = !!snapshot;
154
169
  const runtimeLabel = isRunning ? chalk_1.default.bold.green(t('dashboard.running')) : chalk_1.default.bold.red(t('dashboard.stopped'));
155
170
  const session = snapshot?.activeSession;
156
- console.log(titleBox('nonstop client'));
171
+ const isVi = config.language === 'vi';
172
+ let modeLabel = '';
173
+ if (snapshot) {
174
+ if (snapshot.mode === 'background') {
175
+ modeLabel = isVi ? 'chạy nền' : 'background';
176
+ }
177
+ else if (snapshot.mode === 'foreground') {
178
+ modeLabel = isVi ? 'chạy trực tiếp' : 'foreground';
179
+ }
180
+ else {
181
+ modeLabel = snapshot.mode;
182
+ }
183
+ }
184
+ let startupModeLabel = '';
185
+ if (config.startupMode === 'disabled') {
186
+ startupModeLabel = isVi ? 'Tắt' : 'Disabled';
187
+ }
188
+ else if (config.startupMode === 'background') {
189
+ startupModeLabel = isVi ? 'Chạy nền' : 'Background';
190
+ }
191
+ else if (config.startupMode === 'open-ui') {
192
+ startupModeLabel = isVi ? 'Mở giao diện' : 'Open UI';
193
+ }
194
+ else {
195
+ startupModeLabel = config.startupMode;
196
+ }
197
+ console.log(titleBox(t('dashboard.title')));
157
198
  console.log('');
158
- console.log(infoRow('Trạng thái', isRunning ? `${runtimeLabel} ${chalk_1.default.gray(`(${snapshot.mode})`)}` : runtimeLabel));
199
+ console.log(infoRow(isVi ? 'Trạng thái' : 'Status', isRunning ? `${runtimeLabel} ${chalk_1.default.gray(`(${modeLabel})`)}` : runtimeLabel));
159
200
  console.log(infoRow('Client', config.clientName, chalk_1.default.white));
160
201
  console.log(infoRow('Admin', config.adminUsername || '-', chalk_1.default.white));
161
- console.log(infoRow('Ngôn ngữ', config.language === 'vi' ? 'Tiếng Việt' : 'English', chalk_1.default.white));
162
- console.log(infoRow('Khởi động', config.startupMode, chalk_1.default.white));
202
+ console.log(infoRow(isVi ? 'Ngôn ngữ' : 'Language', config.language === 'vi' ? 'Tiếng Việt' : 'English', chalk_1.default.white));
203
+ console.log(infoRow(isVi ? 'Khởi động' : 'Startup', startupModeLabel, chalk_1.default.white));
163
204
  if (snapshot?.startedAt) {
164
205
  const dt = new Date(snapshot.startedAt);
165
- console.log(infoRow('Bật lúc', dt.toLocaleTimeString('vi-VN'), chalk_1.default.white));
206
+ console.log(infoRow(isVi ? 'Bật lúc' : 'Started at', dt.toLocaleTimeString(isVi ? 'vi-VN' : 'en-US'), chalk_1.default.white));
166
207
  }
167
208
  if (session) {
168
209
  console.log(separator());
169
- console.log(infoRow('Session', `${session.preset}`, chalk_1.default.yellow));
210
+ console.log(infoRow(isVi ? 'Phiên' : 'Session', `${session.preset}`, chalk_1.default.yellow));
170
211
  const shortCwd = session.cwd.length > 40 ? '...' + session.cwd.slice(-38) : session.cwd;
171
- console.log(infoRow('Thư mục', shortCwd, chalk_1.default.gray));
212
+ console.log(infoRow(isVi ? 'Thư mục' : 'Directory', shortCwd, chalk_1.default.gray));
172
213
  }
173
214
  if (snapshot?.lastError) {
174
215
  console.log(separator());
175
- console.log(infoRow('Lỗi', snapshot.lastError, chalk_1.default.red));
216
+ console.log(infoRow(isVi ? 'Lỗi' : 'Error', snapshot.lastError, chalk_1.default.red));
176
217
  }
177
218
  console.log('');
178
219
  console.log(chalk_1.default.bold.blue(' ' + t('dashboard.menu')));
179
220
  }
221
+ async function executeUpgrade(latestVersion, isVi) {
222
+ clearScreen();
223
+ console.log(titleBox(isVi ? 'Đang nâng cấp nonstop' : 'Upgrading nonstop'));
224
+ console.log('');
225
+ const platform = os.platform();
226
+ if (platform === 'win32') {
227
+ console.log(chalk_1.default.yellow(isVi
228
+ ? ' Đang mở cửa sổ PowerShell mới để nâng cấp. Tiến trình hiện tại sẽ tự đóng...'
229
+ : ' Opening new PowerShell window for upgrade. Current process will exit...'));
230
+ const upgradingMsg = isVi
231
+ ? `Đang nâng cấp @quangnv13/nonstop lên phiên bản ${latestVersion}...`
232
+ : `Upgrading @quangnv13/nonstop to version ${latestVersion}...`;
233
+ const completeMsg = isVi
234
+ ? `Nâng cấp nonstop hoàn tất! Cửa sổ này sẽ tự đóng sau 3 giây...`
235
+ : `nonstop upgrade completed! This window will close in 3 seconds...`;
236
+ const cmd = 'cmd.exe';
237
+ const args = [
238
+ '/c',
239
+ 'start',
240
+ 'powershell',
241
+ '-NoProfile',
242
+ '-Command',
243
+ `Start-Sleep -Seconds 1; Write-Host '${upgradingMsg}'; npm install -g @quangnv13/nonstop@latest; Write-Host '${completeMsg}'; Start-Sleep -Seconds 3`
244
+ ];
245
+ (0, child_process_1.spawn)(cmd, args, {
246
+ detached: true,
247
+ stdio: 'ignore',
248
+ shell: true
249
+ }).unref();
250
+ process.exit(0);
251
+ }
252
+ else {
253
+ console.log(chalk_1.default.blue(isVi ? ' Đang chạy lệnh cài đặt...' : ' Running install command...'));
254
+ try {
255
+ (0, child_process_1.execSync)('npm install -g @quangnv13/nonstop@latest', { stdio: 'inherit' });
256
+ console.log(chalk_1.default.green(isVi ? '\n ✓ Nâng cấp nonstop thành công! Vui lòng khởi động lại nonstop.' : '\n ✓ nonstop upgrade successful! Please restart nonstop.'));
257
+ await pause(isVi ? 'vi' : 'en');
258
+ process.exit(0);
259
+ }
260
+ catch (error) {
261
+ console.error(chalk_1.default.red(isVi ? '\n ❌ Lỗi nâng cấp nonstop: ' : '\n ❌ nonstop upgrade failed: '), error);
262
+ await pause(isVi ? 'vi' : 'en');
263
+ }
264
+ }
265
+ }
180
266
  async function launchControlCenter() {
181
267
  (0, config_js_1.ensureEnvExampleFile)();
182
268
  let config = (0, config_js_1.loadConfigFromDisk)();
183
269
  const isTTY = process.stdin.isTTY;
184
270
  const wasRaw = process.stdin.isRaw;
271
+ // 1. Kiểm tra cập nhật khi khởi chạy
272
+ const currentVersion = (0, runtime_manager_js_1.getCurrentVersion)();
273
+ console.log(chalk_1.default.gray(`\n ${config.language === 'vi' ? 'Đang kiểm tra cập nhật' : 'Checking for updates'} (v${currentVersion})...`));
274
+ const latestVersion = await (0, runtime_manager_js_1.checkForUpdate)(currentVersion);
275
+ if (latestVersion) {
276
+ clearScreen();
277
+ const isVi = config.language === 'vi';
278
+ const upgradeChoice = await runSelectionMenu(() => {
279
+ console.log(titleBox(isVi ? 'Có bản cập nhật mới!' : 'Update Available!'));
280
+ console.log('');
281
+ console.log(` ${isVi ? 'Phiên bản hiện tại:' : 'Current version:'} ${chalk_1.default.yellow(currentVersion)}`);
282
+ console.log(` ${isVi ? 'Phiên bản mới nhất:' : 'Latest version:'} ${chalk_1.default.green(latestVersion)}`);
283
+ console.log('');
284
+ console.log(chalk_1.default.bold(` ${isVi ? 'Bạn có muốn nâng cấp ngay bây giờ không?' : 'Do you want to upgrade now?'}`));
285
+ }, [
286
+ { label: isVi ? 'Có, nâng cấp ngay' : 'Yes, upgrade now', value: true },
287
+ { label: isVi ? 'Không, để sau' : 'No, skip for now', value: false }
288
+ ], 0, config.language);
289
+ if (upgradeChoice) {
290
+ await executeUpgrade(latestVersion, isVi);
291
+ return;
292
+ }
293
+ }
185
294
  try {
186
295
  if ((0, config_js_1.getMissingConfigFields)(config).length > 0) {
187
296
  config = await runSetupWizard(config);
@@ -193,10 +302,11 @@ async function launchControlCenter() {
193
302
  const isRunning = (0, runtime_manager_js_1.getRuntimeStatus)().running;
194
303
  const isVi = config.language === 'vi';
195
304
  const toggleLabel = isRunning
196
- ? (isVi ? 'Tắt runtime nền' : 'Dừng background runtime')
197
- : (isVi ? 'Bật runtime nền' : 'Chạy background runtime');
305
+ ? (isVi ? 'Tắt runtime nền' : 'Stop background runtime')
306
+ : (isVi ? 'Bật runtime nền' : 'Start background runtime');
198
307
  const options = [
199
308
  { label: toggleLabel, value: 'toggle' },
309
+ { label: isVi ? 'Danh sách CLI đã spawn' : 'List of spawned CLIs', value: 'sessions' },
200
310
  { label: t('menu.settings'), value: 'settings' },
201
311
  { label: t('menu.workspaces'), value: 'workspaces' },
202
312
  { label: t('menu.startup'), value: 'startup' },
@@ -204,7 +314,7 @@ async function launchControlCenter() {
204
314
  { label: t('menu.logs'), value: 'logs' },
205
315
  { label: t('menu.exit'), value: 'exit' }
206
316
  ];
207
- const choice = await runSelectionMenu(() => renderDashboardHeader(config, (0, runtime_manager_js_1.getRuntimeStatus)().snapshot), options, lastSelection);
317
+ const choice = await runSelectionMenu(() => renderDashboardHeader(config, (0, runtime_manager_js_1.getRuntimeStatus)().snapshot), options, lastSelection, config.language);
208
318
  lastSelection = options.findIndex(opt => opt.value === choice);
209
319
  if (lastSelection < 0)
210
320
  lastSelection = 0;
@@ -214,6 +324,10 @@ async function launchControlCenter() {
214
324
  await handleToggleRuntime(config);
215
325
  continue;
216
326
  }
327
+ if (choice === 'sessions') {
328
+ await manageActiveSessions(config.language);
329
+ continue;
330
+ }
217
331
  if (choice === 'settings') {
218
332
  config = await editConfig(config);
219
333
  continue;
@@ -231,7 +345,7 @@ async function launchControlCenter() {
231
345
  continue;
232
346
  }
233
347
  if (choice === 'logs') {
234
- await showRecentLogs();
348
+ await showRecentLogs(config.language);
235
349
  continue;
236
350
  }
237
351
  }
@@ -245,17 +359,60 @@ async function launchControlCenter() {
245
359
  catch { /* ignore */ }
246
360
  }
247
361
  clearScreen();
248
- console.log(chalk_1.default.gray('Đã thoát nonstop client.'));
362
+ console.log(chalk_1.default.gray(config.language === 'vi' ? 'Đã thoát nonstop client.' : 'Exited nonstop client.'));
363
+ }
364
+ }
365
+ async function manageActiveSessions(language) {
366
+ const isVi = language === 'vi';
367
+ while (true) {
368
+ const status = (0, runtime_manager_js_1.getRuntimeStatus)();
369
+ const session = status.snapshot?.activeSession;
370
+ const options = [];
371
+ if (status.running && session && session.status === 'running') {
372
+ options.push({
373
+ label: `● [${session.preset.toUpperCase()}] ID: ${session.sessionId} | ${session.cwd}`,
374
+ value: { type: 'select', preset: session.preset, cwd: session.cwd }
375
+ });
376
+ }
377
+ options.push({
378
+ label: isVi ? '← Quay lại menu chính' : '← Back to main menu',
379
+ value: { type: 'back' }
380
+ });
381
+ const choice = await runSelectionMenu(() => {
382
+ console.log(titleBox(isVi ? 'Danh sách CLI đã spawn' : 'List of spawned CLIs'));
383
+ console.log('');
384
+ if (!status.running) {
385
+ console.log(chalk_1.default.yellow(isVi
386
+ ? ' ⚠ Runtime nền hiện không chạy.'
387
+ : ' ⚠ Background runtime is not running.'));
388
+ }
389
+ else if (!session || session.status !== 'running') {
390
+ console.log(chalk_1.default.gray(isVi
391
+ ? ' Không có session nào đang chạy.'
392
+ : ' No active sessions running.'));
393
+ }
394
+ else {
395
+ console.log(chalk_1.default.gray(isVi
396
+ ? ' Chọn session để tiếp tục trực tiếp:'
397
+ : ' Select a session to continue locally:'));
398
+ }
399
+ }, options, 0, language);
400
+ if (choice.type === 'back') {
401
+ return;
402
+ }
403
+ if (choice.type === 'select' && choice.preset && choice.cwd) {
404
+ await attachToBackgroundSession(choice.preset, choice.cwd, language);
405
+ }
249
406
  }
250
407
  }
251
408
  async function runSetupWizard(currentConfig) {
252
409
  const language = await runSelectionMenu(() => {
253
- console.log(titleBox('Thiết lập nonstop'));
254
- console.log(chalk_1.default.gray(' Chọn ngôn ngữ / Choose language:'));
410
+ console.log(titleBox(currentConfig.language === 'vi' ? 'Thiết lập nonstop' : 'nonstop Setup'));
411
+ console.log(chalk_1.default.gray(currentConfig.language === 'vi' ? ' Chọn ngôn ngữ / Choose language:' : ' Choose language / Chọn ngôn ngữ:'));
255
412
  }, [
256
413
  { label: 'Tiếng Việt (vi)', value: 'vi' },
257
414
  { label: 'English (en)', value: 'en' }
258
- ], currentConfig.language === 'en' ? 1 : 0);
415
+ ], currentConfig.language === 'en' ? 1 : 0, currentConfig.language);
259
416
  const t = (0, i18n_js_1.createTranslator)(language);
260
417
  clearScreen();
261
418
  console.log(titleBox(t('wizard.title')));
@@ -267,10 +424,10 @@ async function runSetupWizard(currentConfig) {
267
424
  console.log(titleBox(t('wizard.title')));
268
425
  console.log(chalk_1.default.gray(` ${t('wizard.startupMode')}:`));
269
426
  }, [
270
- { label: `Tắt (disabled)`, value: 'disabled' },
271
- { label: `Chạy nền (background)`, value: 'background' },
272
- { label: `Mở giao diện (open-ui)`, value: 'open-ui' }
273
- ], 0);
427
+ { label: t('startup.disabled'), value: 'disabled' },
428
+ { label: t('startup.background'), value: 'background' },
429
+ { label: t('startup.openUi'), value: 'open-ui' }
430
+ ], 0, language);
274
431
  const nextConfig = {
275
432
  ...currentConfig,
276
433
  language,
@@ -284,7 +441,7 @@ async function runSetupWizard(currentConfig) {
284
441
  clearScreen();
285
442
  console.log(titleBox(t('wizard.title')));
286
443
  console.log(`\n${chalk_1.default.green(t('wizard.complete'))}`);
287
- await pause();
444
+ await pause(language);
288
445
  return nextConfig;
289
446
  }
290
447
  async function handleToggleRuntime(config) {
@@ -293,11 +450,11 @@ async function handleToggleRuntime(config) {
293
450
  clearScreen();
294
451
  try {
295
452
  if (status.running) {
296
- const msg = (0, runtime_manager_js_1.stopBackgroundRuntime)(status.snapshot);
453
+ const msg = (0, runtime_manager_js_1.stopBackgroundRuntime)(status.snapshot, config.language);
297
454
  console.log(`\n${chalk_1.default.yellow(msg)}`);
298
455
  }
299
456
  else {
300
- const msg = (0, runtime_manager_js_1.startBackgroundRuntime)();
457
+ const msg = (0, runtime_manager_js_1.startBackgroundRuntime)(config.language);
301
458
  console.log(`\n${chalk_1.default.green(msg)}`);
302
459
  }
303
460
  // Polling: chờ trạng thái thực sự thay đổi (tối đa 3 giây)
@@ -310,13 +467,13 @@ async function handleToggleRuntime(config) {
310
467
  }
311
468
  catch (error) {
312
469
  console.log(`\n${chalk_1.default.red(error instanceof Error ? error.message : String(error))}`);
313
- await pause();
470
+ await pause(config.language);
314
471
  return;
315
472
  }
316
473
  }
317
474
  async function editConfig(config) {
318
475
  clearScreen();
319
- console.log(titleBox('Sửa cấu hình'));
476
+ console.log(titleBox(config.language === 'vi' ? 'Sửa cấu hình' : 'Edit config'));
320
477
  console.log('');
321
478
  const nextConfig = {
322
479
  ...config,
@@ -325,12 +482,14 @@ async function editConfig(config) {
325
482
  clientName: await askWithDefault('CLIENT_NAME', config.clientName),
326
483
  telegramUsername: await askWithDefault('TELEGRAM_USERNAME', config.telegramUsername),
327
484
  codexCmd: await askWithDefault('CODEX_CMD', config.codexCmd),
328
- antigravityCmd: await askWithDefault('ANTIGRAVITY_CMD', config.antigravityCmd)
485
+ antigravityCmd: await askWithDefault('ANTIGRAVITY_CMD', config.antigravityCmd),
486
+ claudeCmd: await askWithDefault('CLAUDE_CMD', config.claudeCmd),
487
+ dangerousCommandConfirm: await askWithDefault('DANGEROUS_COMMAND_CONFIRM', config.dangerousCommandConfirm)
329
488
  };
330
489
  (0, config_js_1.saveConfigToDisk)(nextConfig);
331
- console.log(`\n${chalk_1.default.green('✓ Đã lưu cấu hình.')}`);
332
- console.log(chalk_1.default.gray('Khởi động lại runtime nền nếu đang chạy để áp dụng thay đổi.'));
333
- await pause();
490
+ console.log(`\n${chalk_1.default.green(config.language === 'vi' ? '✓ Đã lưu cấu hình.' : '✓ Settings saved.')}`);
491
+ console.log(chalk_1.default.gray(config.language === 'vi' ? 'Khởi động lại runtime nền nếu đang chạy để áp dụng thay đổi.' : 'Restart the background runtime if running to apply changes.'));
492
+ await pause(config.language);
334
493
  return nextConfig;
335
494
  }
336
495
  async function manageWorkspaces(language) {
@@ -338,7 +497,7 @@ async function manageWorkspaces(language) {
338
497
  while (true) {
339
498
  const workspaces = (0, store_js_1.loadWorkspaces)();
340
499
  const options = [
341
- { label: isVi ? '+ Thêm workspace mới' : '+ Add new workspace', value: { type: 'add' } },
500
+ { label: isVi ? '+ Thêm không gian làm việc mới' : '+ Add new workspace', value: { type: 'add' } },
342
501
  ...workspaces.map((ws, i) => ({
343
502
  label: `● ${ws.name} ${chalk_1.default.gray(ws.path.length > 30 ? '...' + ws.path.slice(-28) : ws.path)}`,
344
503
  value: { type: 'workspace', index: i }
@@ -346,20 +505,20 @@ async function manageWorkspaces(language) {
346
505
  { label: isVi ? '← Quay lại menu chính' : '← Back', value: { type: 'back' } }
347
506
  ];
348
507
  const selection = await runSelectionMenu(() => {
349
- console.log(titleBox(isVi ? 'Quản lý Workspace' : 'Manage Workspaces'));
350
- console.log(chalk_1.default.gray(` ${isVi ? 'Chọn workspace hoặc thêm mới:' : 'Select workspace or add new:'}`));
351
- }, options);
508
+ console.log(titleBox(isVi ? 'Quản lý không gian làm việc' : 'Manage Workspaces'));
509
+ console.log(chalk_1.default.gray(` ${isVi ? 'Chọn không gian làm việc hoặc thêm mới:' : 'Select workspace or add new:'}`));
510
+ }, options, 0, language);
352
511
  if (selection.type === 'back')
353
512
  return;
354
513
  if (selection.type === 'add') {
355
514
  clearScreen();
356
- console.log(titleBox(isVi ? 'Thêm workspace mới' : 'Add workspace'));
515
+ console.log(titleBox(isVi ? 'Thêm không gian làm việc mới' : 'Add workspace'));
357
516
  console.log('');
358
- const name = (await askQuestion(chalk_1.default.bold(isVi ? 'Tên workspace: ' : 'Workspace name: '))).trim();
517
+ const name = (await askQuestion(chalk_1.default.bold(isVi ? 'Tên không gian làm việc: ' : 'Workspace name: '))).trim();
359
518
  const rawPath = (await askQuestion(chalk_1.default.bold(isVi ? 'Đường dẫn: ' : 'Path: '))).trim();
360
519
  if (!rawPath) {
361
520
  console.log(chalk_1.default.red(isVi ? ' Đường dẫn không được để trống.' : ' Path cannot be empty.'));
362
- await pause();
521
+ await pause(language);
363
522
  continue;
364
523
  }
365
524
  const resolvedPath = path.resolve(rawPath);
@@ -368,35 +527,35 @@ async function manageWorkspaces(language) {
368
527
  }
369
528
  workspaces.push({ id: (0, store_js_1.createWorkspaceId)(), name: name || 'Workspace', path: resolvedPath });
370
529
  (0, store_js_1.saveWorkspaces)(workspaces);
371
- console.log(chalk_1.default.green(`\n ✓ ${isVi ? 'Đã thêm workspace.' : 'Workspace added.'}`));
372
- await pause();
530
+ console.log(chalk_1.default.green(`\n ✓ ${isVi ? 'Đã thêm không gian làm việc.' : 'Workspace added.'}`));
531
+ await pause(language);
373
532
  continue;
374
533
  }
375
534
  if (selection.type === 'workspace' && typeof selection.index === 'number') {
376
535
  const idx = selection.index;
377
536
  const ws = workspaces[idx];
378
537
  const action = await runSelectionMenu(() => {
379
- console.log(titleBox(isVi ? 'Hành động Workspace' : 'Workspace Actions'));
538
+ console.log(titleBox(isVi ? 'Hành động không gian làm việc' : 'Workspace Actions'));
380
539
  console.log(chalk_1.default.gray(` ${isVi ? 'Đang chọn:' : 'Selected:'} `) + chalk_1.default.bold(ws.name));
381
540
  console.log(chalk_1.default.gray(` ${isVi ? 'Đường dẫn:' : 'Path:'} `) + ws.path);
382
541
  }, [
383
- { label: isVi ? 'Sửa workspace' : 'Edit workspace', value: 'edit' },
384
- { label: isVi ? 'Xóa workspace' : 'Delete workspace', value: 'delete' },
542
+ { label: isVi ? 'Sửa không gian làm việc' : 'Edit workspace', value: 'edit' },
543
+ { label: isVi ? 'Xóa không gian làm việc' : 'Delete workspace', value: 'delete' },
385
544
  { label: isVi ? '← Quay lại' : '← Back', value: 'back' }
386
- ]);
545
+ ], 0, language);
387
546
  if (action === 'back')
388
547
  continue;
389
548
  if (action === 'delete') {
390
549
  workspaces.splice(idx, 1);
391
550
  (0, store_js_1.saveWorkspaces)(workspaces);
392
551
  clearScreen();
393
- console.log(chalk_1.default.green(`\n ✓ ${isVi ? 'Đã xóa workspace.' : 'Workspace deleted.'}`));
394
- await pause();
552
+ console.log(chalk_1.default.green(`\n ✓ ${isVi ? 'Đã xóa không gian làm việc.' : 'Workspace deleted.'}`));
553
+ await pause(language);
395
554
  continue;
396
555
  }
397
556
  if (action === 'edit') {
398
557
  clearScreen();
399
- console.log(titleBox(isVi ? 'Sửa workspace' : 'Edit workspace'));
558
+ console.log(titleBox(isVi ? 'Sửa không gian làm việc' : 'Edit workspace'));
400
559
  console.log('');
401
560
  const newName = await askWithDefault(isVi ? 'Tên mới' : 'New name', ws.name);
402
561
  const newPath = await askWithDefault(isVi ? 'Đường dẫn mới' : 'New path', ws.path);
@@ -406,8 +565,8 @@ async function manageWorkspaces(language) {
406
565
  }
407
566
  workspaces[idx] = { ...ws, name: newName.trim() || ws.name, path: resolvedPath };
408
567
  (0, store_js_1.saveWorkspaces)(workspaces);
409
- console.log(chalk_1.default.green(`\n ✓ ${isVi ? 'Đã cập nhật workspace.' : 'Workspace updated.'}`));
410
- await pause();
568
+ console.log(chalk_1.default.green(`\n ✓ ${isVi ? 'Đã cập nhật không gian làm việc.' : 'Workspace updated.'}`));
569
+ await pause(language);
411
570
  continue;
412
571
  }
413
572
  }
@@ -422,39 +581,169 @@ async function configureStartup(config) {
422
581
  { label: isVi ? 'Tắt (disabled)' : 'Disabled', value: 'disabled' },
423
582
  { label: isVi ? 'Chạy nền (background)' : 'Background', value: 'background' },
424
583
  { label: isVi ? 'Mở giao diện (open-ui)' : 'Open UI', value: 'open-ui' }
425
- ], ['disabled', 'background', 'open-ui'].indexOf(config.startupMode));
584
+ ], ['disabled', 'background', 'open-ui'].indexOf(config.startupMode), config.language);
426
585
  const entryScriptPath = (0, runtime_manager_js_1.getEntryScriptPath)();
427
- const result = (0, startup_js_1.applyStartupMode)(nextMode, entryScriptPath, process.cwd());
586
+ const result = (0, startup_js_1.applyStartupMode)(nextMode, entryScriptPath, process.cwd(), config.language);
428
587
  const nextConfig = { ...config, startupMode: nextMode };
429
588
  (0, config_js_1.saveConfigToDisk)(nextConfig);
430
589
  clearScreen();
431
590
  console.log(titleBox(isVi ? 'Cấu hình khởi động' : 'Configure startup'));
432
591
  console.log(`\n${chalk_1.default.green(result)}`);
433
- await pause();
592
+ await pause(config.language);
434
593
  return nextConfig;
435
594
  }
436
595
  async function switchLanguage(config) {
437
596
  const language = await runSelectionMenu(() => {
438
- console.log(titleBox('Đổi ngôn ngữ / Switch language'));
439
- console.log(chalk_1.default.gray(` Hiện tại: ${config.language}`));
597
+ console.log(titleBox(config.language === 'vi' ? 'Đổi ngôn ngữ' : 'Switch Language'));
598
+ console.log(chalk_1.default.gray(config.language === 'vi' ? ` Hiện tại: ${config.language}` : ` Current: ${config.language}`));
440
599
  }, [
441
600
  { label: 'Tiếng Việt (vi)', value: 'vi' },
442
601
  { label: 'English (en)', value: 'en' }
443
- ], config.language === 'en' ? 1 : 0);
602
+ ], config.language === 'en' ? 1 : 0, config.language);
444
603
  const nextConfig = { ...config, language };
445
604
  (0, config_js_1.saveConfigToDisk)(nextConfig);
446
605
  return nextConfig;
447
606
  }
448
- async function showRecentLogs() {
607
+ async function showRecentLogs(language) {
608
+ const isVi = language === 'vi';
449
609
  clearScreen();
450
- console.log(titleBox('Nhật ký gần đây'));
610
+ console.log(titleBox(isVi ? 'Nhật ký gần đây' : 'Recent logs'));
451
611
  const logPath = path.join(process.cwd(), 'data', 'nonstop.log');
452
612
  if (!fs.existsSync(logPath)) {
453
- console.log(chalk_1.default.gray('\n Chưa có nhật ký.'));
454
- await pause();
613
+ console.log(chalk_1.default.gray(isVi ? '\n Chưa có nhật ký.' : '\n No logs found.'));
614
+ await pause(language);
455
615
  return;
456
616
  }
457
617
  const lines = fs.readFileSync(logPath, 'utf8').split(/\r?\n/).filter(Boolean).slice(-25);
458
618
  console.log('\n' + lines.map(l => chalk_1.default.gray(' ') + l).join('\n'));
459
- await pause();
619
+ await pause(language);
620
+ }
621
+ async function attachToBackgroundSession(preset, cwd, language) {
622
+ const isVi = language === 'vi';
623
+ const socketPath = (0, runtime_state_js_1.getIpcSocketPath)();
624
+ clearScreen();
625
+ console.log(chalk_1.default.blue(isVi
626
+ ? 'Đang kết nối tới phiên chạy nền...'
627
+ : 'Connecting to background session...'));
628
+ return new Promise((resolve) => {
629
+ const socket = net.createConnection(socketPath);
630
+ let isClosed = false;
631
+ const cleanup = () => {
632
+ if (isClosed)
633
+ return;
634
+ isClosed = true;
635
+ // Restore stdin & stdout
636
+ process.stdin.removeListener('data', onStdinData);
637
+ process.stdout.removeListener('resize', onResize);
638
+ if (process.stdin.isTTY) {
639
+ try {
640
+ process.stdin.setRawMode(false);
641
+ }
642
+ catch { /* ignore */ }
643
+ }
644
+ process.stdin.resume();
645
+ socket.destroy();
646
+ resolve();
647
+ };
648
+ socket.on('connect', () => {
649
+ clearScreen();
650
+ console.log(chalk_1.default.bold.green(isVi
651
+ ? `--- Đã kết nối tới phiên ${preset.toUpperCase()} | Gõ phím để tương tác ---`
652
+ : `--- Connected to ${preset.toUpperCase()} session | Start typing to interact ---`));
653
+ console.log(chalk_1.default.gray(isVi
654
+ ? '--- Nhấn Ctrl+B rồi nhấn D để ngắt kết nối (session vẫn chạy nền) ---'
655
+ : '--- Press Ctrl+B then D to detach (session will keep running) ---'));
656
+ console.log('');
657
+ if (process.stdin.isTTY) {
658
+ try {
659
+ process.stdin.setRawMode(true);
660
+ }
661
+ catch { /* ignore */ }
662
+ }
663
+ process.stdin.resume();
664
+ // Send initial size
665
+ if (process.stdout.isTTY) {
666
+ socket.write(JSON.stringify({
667
+ type: 'resize',
668
+ cols: process.stdout.columns || 80,
669
+ rows: process.stdout.rows || 24
670
+ }) + '\n');
671
+ }
672
+ });
673
+ let lastKeyWasCtrlB = false;
674
+ function onStdinData(data) {
675
+ if (isClosed)
676
+ return;
677
+ if (data.length === 1 && data[0] === 2) {
678
+ lastKeyWasCtrlB = true;
679
+ return;
680
+ }
681
+ if (lastKeyWasCtrlB) {
682
+ lastKeyWasCtrlB = false;
683
+ if (data.length === 1 && (data[0] === 100 || data[0] === 68)) {
684
+ console.log(chalk_1.default.yellow(isVi ? '\n\nĐang ngắt kết nối...' : '\n\nDetaching...'));
685
+ cleanup();
686
+ return;
687
+ }
688
+ socket.write(JSON.stringify({ type: 'input', data: '\u0002' }) + '\n');
689
+ }
690
+ socket.write(JSON.stringify({ type: 'input', data: data.toString('utf8') }) + '\n');
691
+ }
692
+ function onResize() {
693
+ if (isClosed)
694
+ return;
695
+ socket.write(JSON.stringify({
696
+ type: 'resize',
697
+ cols: process.stdout.columns || 80,
698
+ rows: process.stdout.rows || 24
699
+ }) + '\n');
700
+ }
701
+ process.stdin.on('data', onStdinData);
702
+ process.stdout.on('resize', onResize);
703
+ let buffer = '';
704
+ socket.on('data', (data) => {
705
+ if (isClosed)
706
+ return;
707
+ buffer += data.toString();
708
+ let boundary = buffer.indexOf('\n');
709
+ while (boundary !== -1) {
710
+ const line = buffer.slice(0, boundary).trim();
711
+ buffer = buffer.slice(boundary + 1);
712
+ if (line) {
713
+ try {
714
+ const msg = JSON.parse(line);
715
+ if (msg.type === 'output') {
716
+ process.stdout.write(msg.data);
717
+ }
718
+ else if (msg.type === 'exit') {
719
+ console.log(chalk_1.default.yellow(isVi
720
+ ? `\n\n[Phiên làm việc đã kết thúc với mã thoát ${msg.code}]`
721
+ : `\n\n[Session exited with code ${msg.code}]`));
722
+ cleanup();
723
+ }
724
+ }
725
+ catch (err) {
726
+ // Ignore
727
+ }
728
+ }
729
+ boundary = buffer.indexOf('\n');
730
+ }
731
+ });
732
+ socket.on('error', (err) => {
733
+ if (isClosed)
734
+ return;
735
+ console.log(chalk_1.default.red(isVi
736
+ ? `\nLỗi kết nối IPC: ${err.message}`
737
+ : `\nIPC connection error: ${err.message}`));
738
+ pause(language).then(cleanup);
739
+ });
740
+ socket.on('close', () => {
741
+ if (isClosed)
742
+ return;
743
+ console.log(chalk_1.default.gray(isVi
744
+ ? '\nĐã ngắt kết nối với phiên chạy nền.'
745
+ : '\nDisconnected from background session.'));
746
+ pause(language).then(cleanup);
747
+ });
748
+ });
460
749
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quangnv13/nonstop",
3
- "version": "1.0.13",
3
+ "version": "1.0.16",
4
4
  "preferGlobal": true,
5
5
  "publishConfig": {
6
6
  "access": "public"