@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/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 +351 -62
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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(`(${
|
|
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',
|
|
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' : '
|
|
197
|
-
: (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');
|
|
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:
|
|
271
|
-
{ label:
|
|
272
|
-
{ label:
|
|
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
|
|
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ý
|
|
350
|
-
console.log(chalk_1.default.gray(` ${isVi ? 'Chọn
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
384
|
-
{ 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' },
|
|
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
|
|
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
|
|
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
|
|
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ữ
|
|
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
|
}
|