@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/README.md +38 -4
- package/README.vi.md +38 -4
- package/dist/bot.js +100 -7
- package/dist/config.js +14 -2
- package/dist/i18n.js +53 -45
- package/dist/index.js +24 -2
- package/dist/runtime-manager.js +111 -6
- package/dist/runtime-state.js +30 -0
- package/dist/runtime.js +187 -5
- package/dist/session-controls.js +3 -3
- package/dist/terminal.js +14 -1
- package/dist/ui.js +295 -101
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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(`(${
|
|
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',
|
|
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 '
|
|
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 ✓
|
|
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 ❌
|
|
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' : '
|
|
292
|
-
: (isVi ? 'Bật runtime nền' : '
|
|
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:
|
|
366
|
-
{ label:
|
|
367
|
-
{ label:
|
|
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
|
|
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ý
|
|
445
|
-
console.log(chalk_1.default.gray(` ${isVi ? 'Chọn
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
479
|
-
{ label: isVi ? 'Xóa
|
|
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
|
|
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
|
|
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
|
|
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ữ
|
|
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
|
}
|