@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/ui.js CHANGED
@@ -51,6 +51,8 @@ const i18n_js_1 = require("./i18n.js");
51
51
  const runtime_manager_js_1 = require("./runtime-manager.js");
52
52
  const startup_js_1 = require("./startup.js");
53
53
  const store_js_1 = require("./store.js");
54
+ const runtime_state_js_1 = require("./runtime-state.js");
55
+ const net = __importStar(require("net"));
54
56
  function clearScreen() {
55
57
  // \u001b[2J clears visible area, \u001b[3J clears scrollback buffer, \u001b[H moves cursor home
56
58
  node_process_1.stdout.write('\u001b[2J\u001b[3J\u001b[H');
@@ -79,16 +81,27 @@ async function askQuestion(query) {
79
81
  process.stdin.resume();
80
82
  }
81
83
  }
82
- async function pause() {
83
- 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...')}`);
84
87
  }
85
88
  async function askWithDefault(label, currentValue) {
86
89
  const prompt = `${chalk_1.default.bold(label)}${currentValue ? chalk_1.default.gray(` [${currentValue}]`) : ''}: `;
87
90
  const answer = await askQuestion(prompt);
88
91
  return answer.trim() || currentValue;
89
92
  }
90
- async function runSelectionMenu(headerRenderer, options, initialIndex = 0) {
93
+ async function runSelectionMenu(headerRenderer, options, initialIndex = 0, language) {
91
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';
92
105
  return new Promise((resolve, reject) => {
93
106
  const isTTY = process.stdin.isTTY;
94
107
  const wasRaw = process.stdin.isRaw;
@@ -115,7 +128,7 @@ async function runSelectionMenu(headerRenderer, options, initialIndex = 0) {
115
128
  }
116
129
  });
117
130
  console.log('');
118
- 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'));
119
132
  }
120
133
  try {
121
134
  readline.emitKeypressEvents(process.stdin);
@@ -155,61 +168,56 @@ function renderDashboardHeader(config, snapshot) {
155
168
  const isRunning = !!snapshot;
156
169
  const runtimeLabel = isRunning ? chalk_1.default.bold.green(t('dashboard.running')) : chalk_1.default.bold.red(t('dashboard.stopped'));
157
170
  const session = snapshot?.activeSession;
158
- 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')));
159
198
  console.log('');
160
- 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));
161
200
  console.log(infoRow('Client', config.clientName, chalk_1.default.white));
162
201
  console.log(infoRow('Admin', config.adminUsername || '-', chalk_1.default.white));
163
- console.log(infoRow('Ngôn ngữ', config.language === 'vi' ? 'Tiếng Việt' : 'English', chalk_1.default.white));
164
- 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));
165
204
  if (snapshot?.startedAt) {
166
205
  const dt = new Date(snapshot.startedAt);
167
- 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));
168
207
  }
169
208
  if (session) {
170
209
  console.log(separator());
171
- console.log(infoRow('Session', `${session.preset}`, chalk_1.default.yellow));
210
+ console.log(infoRow(isVi ? 'Phiên' : 'Session', `${session.preset}`, chalk_1.default.yellow));
172
211
  const shortCwd = session.cwd.length > 40 ? '...' + session.cwd.slice(-38) : session.cwd;
173
- 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));
174
213
  }
175
214
  if (snapshot?.lastError) {
176
215
  console.log(separator());
177
- 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));
178
217
  }
179
218
  console.log('');
180
219
  console.log(chalk_1.default.bold.blue(' ' + t('dashboard.menu')));
181
220
  }
182
- function getCurrentVersion() {
183
- try {
184
- const pkgPath = path.join(__dirname, '..', 'package.json');
185
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
186
- return pkg.version;
187
- }
188
- catch {
189
- return '1.0.13';
190
- }
191
- }
192
- function checkForUpdate(currentVersion) {
193
- return new Promise((resolve) => {
194
- const timer = setTimeout(() => {
195
- resolve(null);
196
- }, 4000);
197
- (0, child_process_1.exec)('npm view @quangnv13/nonstop version', (error, stdout) => {
198
- clearTimeout(timer);
199
- if (error) {
200
- resolve(null);
201
- return;
202
- }
203
- const latest = stdout.trim();
204
- if (latest && latest !== currentVersion) {
205
- resolve(latest);
206
- }
207
- else {
208
- resolve(null);
209
- }
210
- });
211
- });
212
- }
213
221
  async function executeUpgrade(latestVersion, isVi) {
214
222
  clearScreen();
215
223
  console.log(titleBox(isVi ? 'Đang nâng cấp nonstop' : 'Upgrading nonstop'));
@@ -219,6 +227,12 @@ async function executeUpgrade(latestVersion, isVi) {
219
227
  console.log(chalk_1.default.yellow(isVi
220
228
  ? ' Đang mở cửa sổ PowerShell mới để nâng cấp. Tiến trình hiện tại sẽ tự đóng...'
221
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...`;
222
236
  const cmd = 'cmd.exe';
223
237
  const args = [
224
238
  '/c',
@@ -226,7 +240,7 @@ async function executeUpgrade(latestVersion, isVi) {
226
240
  'powershell',
227
241
  '-NoProfile',
228
242
  '-Command',
229
- `Start-Sleep -Seconds 1; Write-Host 'Đang nâng cấp @quangnv13/nonstop lên phiên bản ${latestVersion}...'; npm install -g @quangnv13/nonstop@latest; Write-Host 'Hoàn tất! Cửa sổ này sẽ tự đóng sau 3 giây...'; Start-Sleep -Seconds 3`
243
+ `Start-Sleep -Seconds 1; Write-Host '${upgradingMsg}'; npm install -g @quangnv13/nonstop@latest; Write-Host '${completeMsg}'; Start-Sleep -Seconds 3`
230
244
  ];
231
245
  (0, child_process_1.spawn)(cmd, args, {
232
246
  detached: true,
@@ -239,13 +253,13 @@ async function executeUpgrade(latestVersion, isVi) {
239
253
  console.log(chalk_1.default.blue(isVi ? ' Đang chạy lệnh cài đặt...' : ' Running install command...'));
240
254
  try {
241
255
  (0, child_process_1.execSync)('npm install -g @quangnv13/nonstop@latest', { stdio: 'inherit' });
242
- console.log(chalk_1.default.green(isVi ? '\n ✓ Nâng cấp thành công! Vui lòng khởi động lại nonstop.' : '\n ✓ Upgrade successful! Please restart nonstop.'));
243
- await pause();
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');
244
258
  process.exit(0);
245
259
  }
246
260
  catch (error) {
247
- console.error(chalk_1.default.red(isVi ? '\n ❌ Lỗi nâng cấp: ' : '\n ❌ Upgrade failed: '), error);
248
- await pause();
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');
249
263
  }
250
264
  }
251
265
  }
@@ -255,9 +269,9 @@ async function launchControlCenter() {
255
269
  const isTTY = process.stdin.isTTY;
256
270
  const wasRaw = process.stdin.isRaw;
257
271
  // 1. Kiểm tra cập nhật khi khởi chạy
258
- const currentVersion = getCurrentVersion();
272
+ const currentVersion = (0, runtime_manager_js_1.getCurrentVersion)();
259
273
  console.log(chalk_1.default.gray(`\n ${config.language === 'vi' ? 'Đang kiểm tra cập nhật' : 'Checking for updates'} (v${currentVersion})...`));
260
- const latestVersion = await checkForUpdate(currentVersion);
274
+ const latestVersion = await (0, runtime_manager_js_1.checkForUpdate)(currentVersion);
261
275
  if (latestVersion) {
262
276
  clearScreen();
263
277
  const isVi = config.language === 'vi';
@@ -271,7 +285,7 @@ async function launchControlCenter() {
271
285
  }, [
272
286
  { label: isVi ? 'Có, nâng cấp ngay' : 'Yes, upgrade now', value: true },
273
287
  { label: isVi ? 'Không, để sau' : 'No, skip for now', value: false }
274
- ], 0);
288
+ ], 0, config.language);
275
289
  if (upgradeChoice) {
276
290
  await executeUpgrade(latestVersion, isVi);
277
291
  return;
@@ -288,10 +302,11 @@ async function launchControlCenter() {
288
302
  const isRunning = (0, runtime_manager_js_1.getRuntimeStatus)().running;
289
303
  const isVi = config.language === 'vi';
290
304
  const toggleLabel = isRunning
291
- ? (isVi ? 'Tắt runtime nền' : 'Dừng background runtime')
292
- : (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');
293
307
  const options = [
294
308
  { label: toggleLabel, value: 'toggle' },
309
+ { label: isVi ? 'Danh sách CLI đã spawn' : 'List of spawned CLIs', value: 'sessions' },
295
310
  { label: t('menu.settings'), value: 'settings' },
296
311
  { label: t('menu.workspaces'), value: 'workspaces' },
297
312
  { label: t('menu.startup'), value: 'startup' },
@@ -299,7 +314,7 @@ async function launchControlCenter() {
299
314
  { label: t('menu.logs'), value: 'logs' },
300
315
  { label: t('menu.exit'), value: 'exit' }
301
316
  ];
302
- 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);
303
318
  lastSelection = options.findIndex(opt => opt.value === choice);
304
319
  if (lastSelection < 0)
305
320
  lastSelection = 0;
@@ -309,6 +324,10 @@ async function launchControlCenter() {
309
324
  await handleToggleRuntime(config);
310
325
  continue;
311
326
  }
327
+ if (choice === 'sessions') {
328
+ await manageActiveSessions(config.language);
329
+ continue;
330
+ }
312
331
  if (choice === 'settings') {
313
332
  config = await editConfig(config);
314
333
  continue;
@@ -326,7 +345,7 @@ async function launchControlCenter() {
326
345
  continue;
327
346
  }
328
347
  if (choice === 'logs') {
329
- await showRecentLogs();
348
+ await showRecentLogs(config.language);
330
349
  continue;
331
350
  }
332
351
  }
@@ -340,17 +359,60 @@ async function launchControlCenter() {
340
359
  catch { /* ignore */ }
341
360
  }
342
361
  clearScreen();
343
- 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
+ }
344
406
  }
345
407
  }
346
408
  async function runSetupWizard(currentConfig) {
347
409
  const language = await runSelectionMenu(() => {
348
- console.log(titleBox('Thiết lập nonstop'));
349
- 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ữ:'));
350
412
  }, [
351
413
  { label: 'Tiếng Việt (vi)', value: 'vi' },
352
414
  { label: 'English (en)', value: 'en' }
353
- ], currentConfig.language === 'en' ? 1 : 0);
415
+ ], currentConfig.language === 'en' ? 1 : 0, currentConfig.language);
354
416
  const t = (0, i18n_js_1.createTranslator)(language);
355
417
  clearScreen();
356
418
  console.log(titleBox(t('wizard.title')));
@@ -362,10 +424,10 @@ async function runSetupWizard(currentConfig) {
362
424
  console.log(titleBox(t('wizard.title')));
363
425
  console.log(chalk_1.default.gray(` ${t('wizard.startupMode')}:`));
364
426
  }, [
365
- { label: `Tắt (disabled)`, value: 'disabled' },
366
- { label: `Chạy nền (background)`, value: 'background' },
367
- { label: `Mở giao diện (open-ui)`, value: 'open-ui' }
368
- ], 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);
369
431
  const nextConfig = {
370
432
  ...currentConfig,
371
433
  language,
@@ -379,7 +441,7 @@ async function runSetupWizard(currentConfig) {
379
441
  clearScreen();
380
442
  console.log(titleBox(t('wizard.title')));
381
443
  console.log(`\n${chalk_1.default.green(t('wizard.complete'))}`);
382
- await pause();
444
+ await pause(language);
383
445
  return nextConfig;
384
446
  }
385
447
  async function handleToggleRuntime(config) {
@@ -388,11 +450,11 @@ async function handleToggleRuntime(config) {
388
450
  clearScreen();
389
451
  try {
390
452
  if (status.running) {
391
- const msg = (0, runtime_manager_js_1.stopBackgroundRuntime)(status.snapshot);
453
+ const msg = (0, runtime_manager_js_1.stopBackgroundRuntime)(status.snapshot, config.language);
392
454
  console.log(`\n${chalk_1.default.yellow(msg)}`);
393
455
  }
394
456
  else {
395
- const msg = (0, runtime_manager_js_1.startBackgroundRuntime)();
457
+ const msg = (0, runtime_manager_js_1.startBackgroundRuntime)(config.language);
396
458
  console.log(`\n${chalk_1.default.green(msg)}`);
397
459
  }
398
460
  // Polling: chờ trạng thái thực sự thay đổi (tối đa 3 giây)
@@ -405,13 +467,13 @@ async function handleToggleRuntime(config) {
405
467
  }
406
468
  catch (error) {
407
469
  console.log(`\n${chalk_1.default.red(error instanceof Error ? error.message : String(error))}`);
408
- await pause();
470
+ await pause(config.language);
409
471
  return;
410
472
  }
411
473
  }
412
474
  async function editConfig(config) {
413
475
  clearScreen();
414
- console.log(titleBox('Sửa cấu hình'));
476
+ console.log(titleBox(config.language === 'vi' ? 'Sửa cấu hình' : 'Edit config'));
415
477
  console.log('');
416
478
  const nextConfig = {
417
479
  ...config,
@@ -420,12 +482,14 @@ async function editConfig(config) {
420
482
  clientName: await askWithDefault('CLIENT_NAME', config.clientName),
421
483
  telegramUsername: await askWithDefault('TELEGRAM_USERNAME', config.telegramUsername),
422
484
  codexCmd: await askWithDefault('CODEX_CMD', config.codexCmd),
423
- 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)
424
488
  };
425
489
  (0, config_js_1.saveConfigToDisk)(nextConfig);
426
- console.log(`\n${chalk_1.default.green('✓ Đã lưu cấu hình.')}`);
427
- console.log(chalk_1.default.gray('Khởi động lại runtime nền nếu đang chạy để áp dụng thay đổi.'));
428
- 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);
429
493
  return nextConfig;
430
494
  }
431
495
  async function manageWorkspaces(language) {
@@ -433,7 +497,7 @@ async function manageWorkspaces(language) {
433
497
  while (true) {
434
498
  const workspaces = (0, store_js_1.loadWorkspaces)();
435
499
  const options = [
436
- { 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' } },
437
501
  ...workspaces.map((ws, i) => ({
438
502
  label: `● ${ws.name} ${chalk_1.default.gray(ws.path.length > 30 ? '...' + ws.path.slice(-28) : ws.path)}`,
439
503
  value: { type: 'workspace', index: i }
@@ -441,20 +505,20 @@ async function manageWorkspaces(language) {
441
505
  { label: isVi ? '← Quay lại menu chính' : '← Back', value: { type: 'back' } }
442
506
  ];
443
507
  const selection = await runSelectionMenu(() => {
444
- console.log(titleBox(isVi ? 'Quản lý Workspace' : 'Manage Workspaces'));
445
- console.log(chalk_1.default.gray(` ${isVi ? 'Chọn workspace hoặc thêm mới:' : 'Select workspace or add new:'}`));
446
- }, 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);
447
511
  if (selection.type === 'back')
448
512
  return;
449
513
  if (selection.type === 'add') {
450
514
  clearScreen();
451
- 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'));
452
516
  console.log('');
453
- 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();
454
518
  const rawPath = (await askQuestion(chalk_1.default.bold(isVi ? 'Đường dẫn: ' : 'Path: '))).trim();
455
519
  if (!rawPath) {
456
520
  console.log(chalk_1.default.red(isVi ? ' Đường dẫn không được để trống.' : ' Path cannot be empty.'));
457
- await pause();
521
+ await pause(language);
458
522
  continue;
459
523
  }
460
524
  const resolvedPath = path.resolve(rawPath);
@@ -463,35 +527,35 @@ async function manageWorkspaces(language) {
463
527
  }
464
528
  workspaces.push({ id: (0, store_js_1.createWorkspaceId)(), name: name || 'Workspace', path: resolvedPath });
465
529
  (0, store_js_1.saveWorkspaces)(workspaces);
466
- console.log(chalk_1.default.green(`\n ✓ ${isVi ? 'Đã thêm workspace.' : 'Workspace added.'}`));
467
- 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);
468
532
  continue;
469
533
  }
470
534
  if (selection.type === 'workspace' && typeof selection.index === 'number') {
471
535
  const idx = selection.index;
472
536
  const ws = workspaces[idx];
473
537
  const action = await runSelectionMenu(() => {
474
- 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'));
475
539
  console.log(chalk_1.default.gray(` ${isVi ? 'Đang chọn:' : 'Selected:'} `) + chalk_1.default.bold(ws.name));
476
540
  console.log(chalk_1.default.gray(` ${isVi ? 'Đường dẫn:' : 'Path:'} `) + ws.path);
477
541
  }, [
478
- { label: isVi ? 'Sửa workspace' : 'Edit workspace', value: 'edit' },
479
- { 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' },
480
544
  { label: isVi ? '← Quay lại' : '← Back', value: 'back' }
481
- ]);
545
+ ], 0, language);
482
546
  if (action === 'back')
483
547
  continue;
484
548
  if (action === 'delete') {
485
549
  workspaces.splice(idx, 1);
486
550
  (0, store_js_1.saveWorkspaces)(workspaces);
487
551
  clearScreen();
488
- console.log(chalk_1.default.green(`\n ✓ ${isVi ? 'Đã xóa workspace.' : 'Workspace deleted.'}`));
489
- 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);
490
554
  continue;
491
555
  }
492
556
  if (action === 'edit') {
493
557
  clearScreen();
494
- 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'));
495
559
  console.log('');
496
560
  const newName = await askWithDefault(isVi ? 'Tên mới' : 'New name', ws.name);
497
561
  const newPath = await askWithDefault(isVi ? 'Đường dẫn mới' : 'New path', ws.path);
@@ -501,8 +565,8 @@ async function manageWorkspaces(language) {
501
565
  }
502
566
  workspaces[idx] = { ...ws, name: newName.trim() || ws.name, path: resolvedPath };
503
567
  (0, store_js_1.saveWorkspaces)(workspaces);
504
- console.log(chalk_1.default.green(`\n ✓ ${isVi ? 'Đã cập nhật workspace.' : 'Workspace updated.'}`));
505
- 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);
506
570
  continue;
507
571
  }
508
572
  }
@@ -517,39 +581,169 @@ async function configureStartup(config) {
517
581
  { label: isVi ? 'Tắt (disabled)' : 'Disabled', value: 'disabled' },
518
582
  { label: isVi ? 'Chạy nền (background)' : 'Background', value: 'background' },
519
583
  { label: isVi ? 'Mở giao diện (open-ui)' : 'Open UI', value: 'open-ui' }
520
- ], ['disabled', 'background', 'open-ui'].indexOf(config.startupMode));
584
+ ], ['disabled', 'background', 'open-ui'].indexOf(config.startupMode), config.language);
521
585
  const entryScriptPath = (0, runtime_manager_js_1.getEntryScriptPath)();
522
- 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);
523
587
  const nextConfig = { ...config, startupMode: nextMode };
524
588
  (0, config_js_1.saveConfigToDisk)(nextConfig);
525
589
  clearScreen();
526
590
  console.log(titleBox(isVi ? 'Cấu hình khởi động' : 'Configure startup'));
527
591
  console.log(`\n${chalk_1.default.green(result)}`);
528
- await pause();
592
+ await pause(config.language);
529
593
  return nextConfig;
530
594
  }
531
595
  async function switchLanguage(config) {
532
596
  const language = await runSelectionMenu(() => {
533
- console.log(titleBox('Đổi ngôn ngữ / Switch language'));
534
- 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}`));
535
599
  }, [
536
600
  { label: 'Tiếng Việt (vi)', value: 'vi' },
537
601
  { label: 'English (en)', value: 'en' }
538
- ], config.language === 'en' ? 1 : 0);
602
+ ], config.language === 'en' ? 1 : 0, config.language);
539
603
  const nextConfig = { ...config, language };
540
604
  (0, config_js_1.saveConfigToDisk)(nextConfig);
541
605
  return nextConfig;
542
606
  }
543
- async function showRecentLogs() {
607
+ async function showRecentLogs(language) {
608
+ const isVi = language === 'vi';
544
609
  clearScreen();
545
- console.log(titleBox('Nhật ký gần đây'));
610
+ console.log(titleBox(isVi ? 'Nhật ký gần đây' : 'Recent logs'));
546
611
  const logPath = path.join(process.cwd(), 'data', 'nonstop.log');
547
612
  if (!fs.existsSync(logPath)) {
548
- console.log(chalk_1.default.gray('\n Chưa có nhật ký.'));
549
- 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);
550
615
  return;
551
616
  }
552
617
  const lines = fs.readFileSync(logPath, 'utf8').split(/\r?\n/).filter(Boolean).slice(-25);
553
618
  console.log('\n' + lines.map(l => chalk_1.default.gray(' ') + l).join('\n'));
554
- 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
+ });
555
749
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quangnv13/nonstop",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "preferGlobal": true,
5
5
  "publishConfig": {
6
6
  "access": "public"