xiaozuoassistant 0.1.45 → 0.1.47
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/bin/cli.js +151 -0
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -23,6 +23,7 @@ const packageRoot = path.resolve(__dirname, '..');
|
|
|
23
23
|
|
|
24
24
|
const args = process.argv.slice(2);
|
|
25
25
|
const command = args[0];
|
|
26
|
+
const commandArgs = args.slice(1);
|
|
26
27
|
|
|
27
28
|
const EXPORT_FILENAME = 'xiaozuoAssistant-backup.tar.gz';
|
|
28
29
|
|
|
@@ -58,6 +59,22 @@ function getPidFilePath() {
|
|
|
58
59
|
return path.join(CWD, 'logs', 'server.pid');
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
function hasFlag(flag) {
|
|
63
|
+
return commandArgs.includes(flag);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getFlagValue(name) {
|
|
67
|
+
const idx = commandArgs.findIndex(a => a === name);
|
|
68
|
+
if (idx < 0) return null;
|
|
69
|
+
const next = commandArgs[idx + 1];
|
|
70
|
+
if (!next || next.startsWith('-')) return null;
|
|
71
|
+
return next;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getRegistry() {
|
|
75
|
+
return getFlagValue('--registry') || process.env.npm_config_registry || 'https://registry.npmjs.org';
|
|
76
|
+
}
|
|
77
|
+
|
|
61
78
|
function isProcessRunning(pid) {
|
|
62
79
|
try {
|
|
63
80
|
process.kill(pid, 0);
|
|
@@ -186,6 +203,131 @@ async function stopServer() {
|
|
|
186
203
|
console.error('[CLI] ❌ 停止失败:端口仍在占用。');
|
|
187
204
|
}
|
|
188
205
|
|
|
206
|
+
async function runCommand(cmd, cmdArgs, options = {}) {
|
|
207
|
+
const child = spawn(cmd, cmdArgs, { stdio: 'inherit', ...options });
|
|
208
|
+
const code = await new Promise((resolve) => child.on('close', resolve));
|
|
209
|
+
return typeof code === 'number' ? code : 1;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function runWithSudoIfNeeded(cmd, cmdArgs, options = {}) {
|
|
213
|
+
const code = await runCommand(cmd, cmdArgs, options);
|
|
214
|
+
if (code === 0) return 0;
|
|
215
|
+
|
|
216
|
+
const canSudo = process.platform !== 'win32' && process.stdin.isTTY;
|
|
217
|
+
if (!canSudo) return code;
|
|
218
|
+
|
|
219
|
+
console.log('[CLI] 权限不足,尝试使用 sudo 继续...');
|
|
220
|
+
return await runCommand('sudo', [cmd, ...cmdArgs], options);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function updateApp() {
|
|
224
|
+
const registry = getRegistry();
|
|
225
|
+
const port = getPortFromConfig();
|
|
226
|
+
const pidFile = getPidFilePath();
|
|
227
|
+
|
|
228
|
+
const runningByPid = fs.existsSync(pidFile) && (() => {
|
|
229
|
+
try {
|
|
230
|
+
const pid = Number(fs.readFileSync(pidFile, 'utf-8').trim());
|
|
231
|
+
return Number.isFinite(pid) && pid > 0 && isProcessRunning(pid);
|
|
232
|
+
} catch {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
})();
|
|
236
|
+
const runningByPort = await isPortOpen(port);
|
|
237
|
+
const wasRunning = runningByPid || runningByPort;
|
|
238
|
+
|
|
239
|
+
if (wasRunning) {
|
|
240
|
+
console.log('[CLI] 检测到服务正在运行,更新前将自动停止...');
|
|
241
|
+
await stopServer();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
console.log(`[CLI] 正在更新 xiaozuoassistant(registry=${registry})...`);
|
|
245
|
+
const code = await runWithSudoIfNeeded('npm', ['install', '-g', 'xiaozuoassistant@latest', `--registry=${registry}`]);
|
|
246
|
+
if (code !== 0) {
|
|
247
|
+
console.error('[CLI] ❌ 更新失败。');
|
|
248
|
+
if (wasRunning) {
|
|
249
|
+
console.log('[CLI] 更新失败,尝试恢复启动旧版本服务...');
|
|
250
|
+
await runCommand('node', [path.join(packageRoot, 'bin', 'cli.js'), 'start'], { cwd: CWD });
|
|
251
|
+
}
|
|
252
|
+
process.exit(code);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
console.log('[CLI] ✅ 更新完成。');
|
|
256
|
+
|
|
257
|
+
if (wasRunning) {
|
|
258
|
+
console.log('[CLI] 正在自动重启服务...');
|
|
259
|
+
const restartCode = await runCommand('xiaozuoAssistant', ['start'], { cwd: CWD });
|
|
260
|
+
process.exit(restartCode);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function removeApp() {
|
|
265
|
+
const registry = getRegistry();
|
|
266
|
+
const targets = ['config.json', 'memories', 'data', 'logs', 'sessions', 'workspace']
|
|
267
|
+
.map(p => path.join(CWD, p));
|
|
268
|
+
|
|
269
|
+
const doRemove = async () => {
|
|
270
|
+
await stopServer();
|
|
271
|
+
|
|
272
|
+
console.log('[CLI] 正在删除本地数据(当前目录)...');
|
|
273
|
+
for (const target of targets) {
|
|
274
|
+
try {
|
|
275
|
+
if (fs.existsSync(target)) {
|
|
276
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
277
|
+
console.log(`[CLI] 已删除:${target}`);
|
|
278
|
+
}
|
|
279
|
+
} catch (e) {
|
|
280
|
+
console.error(`[CLI] 删除失败:${target}`);
|
|
281
|
+
console.error(e);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
console.log('[CLI] 正在卸载全局包 xiaozuoassistant...');
|
|
286
|
+
const code = await runWithSudoIfNeeded('npm', ['uninstall', '-g', 'xiaozuoassistant', `--registry=${registry}`]);
|
|
287
|
+
if (code !== 0) {
|
|
288
|
+
console.error('[CLI] ❌ 卸载失败。');
|
|
289
|
+
process.exit(code);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
console.log('[CLI] ✅ 已卸载并清理数据。');
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
if (hasFlag('--yes') || hasFlag('-y')) {
|
|
296
|
+
await doRemove();
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
console.log('[CLI] remove 将执行以下操作:');
|
|
301
|
+
console.log(' 1) 停止后台服务');
|
|
302
|
+
console.log(' 2) 删除当前目录下的数据:');
|
|
303
|
+
for (const target of targets) console.log(` - ${target}`);
|
|
304
|
+
console.log(' 3) 卸载全局 npm 包:xiaozuoassistant');
|
|
305
|
+
process.stdout.write('确认继续?(y/N) ');
|
|
306
|
+
process.stdin.once('data', async (data) => {
|
|
307
|
+
const answer = data.toString().trim().toLowerCase();
|
|
308
|
+
if (answer === 'y' || answer === 'yes') {
|
|
309
|
+
await doRemove();
|
|
310
|
+
} else {
|
|
311
|
+
console.log('已取消。');
|
|
312
|
+
}
|
|
313
|
+
process.exit(0);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function printVersion() {
|
|
318
|
+
try {
|
|
319
|
+
const pkgPath = path.join(packageRoot, 'package.json');
|
|
320
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
321
|
+
console.log(`${pkg.name}@${pkg.version}`);
|
|
322
|
+
console.log('Package Root:', packageRoot);
|
|
323
|
+
console.log('Node Version:', process.version);
|
|
324
|
+
} catch {
|
|
325
|
+
console.log('xiaozuoassistant@unknown');
|
|
326
|
+
console.log('Package Root:', packageRoot);
|
|
327
|
+
console.log('Node Version:', process.version);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
189
331
|
async function exportData() {
|
|
190
332
|
console.log('📦 Starting data export...');
|
|
191
333
|
|
|
@@ -420,6 +562,12 @@ if (command === 'start') {
|
|
|
420
562
|
console.log('Package Root:', packageRoot);
|
|
421
563
|
console.log('Node Version:', process.version);
|
|
422
564
|
console.log('Doctor check complete.');
|
|
565
|
+
} else if (command === 'version') {
|
|
566
|
+
printVersion();
|
|
567
|
+
} else if (command === 'update') {
|
|
568
|
+
updateApp();
|
|
569
|
+
} else if (command === 'remove') {
|
|
570
|
+
removeApp();
|
|
423
571
|
} else if (command === 'export') {
|
|
424
572
|
exportData();
|
|
425
573
|
} else if (command === 'import') {
|
|
@@ -432,6 +580,9 @@ if (command === 'start') {
|
|
|
432
580
|
console.log(' start Start the xiaozuoAssistant server');
|
|
433
581
|
console.log(' stop Stop the xiaozuoAssistant server');
|
|
434
582
|
console.log(' doctor Check the health and configuration');
|
|
583
|
+
console.log(' version Print package version and environment');
|
|
584
|
+
console.log(' update Update xiaozuoassistant and auto-restart if running');
|
|
585
|
+
console.log(' remove Uninstall and delete data in current directory');
|
|
435
586
|
console.log(' export Backup local data (config, memories) to a file');
|
|
436
587
|
console.log(' import Restore data from a backup file');
|
|
437
588
|
}
|
package/package.json
CHANGED