xiaozuoassistant 0.1.58 → 0.1.60
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 +6 -2
- package/bin/cli.js +95 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ xiaozuoAssistant 是一个**本地优先(Local-first)**的个人 AI 助手
|
|
|
4
4
|
|
|
5
5
|
## 亮点能力
|
|
6
6
|
|
|
7
|
-
- **Web UI + 实时通信**:浏览器访问 `http://localhost:3001
|
|
7
|
+
- **Web UI + 实时通信**:浏览器访问 `http://localhost:3021`(生产环境)或 `http://localhost:3001`(开发环境),Socket.IO 实时收发消息。
|
|
8
8
|
- **多通道(Channels)**:内置 Web/Terminal,并可按配置启用 Telegram/飞书/钉钉/微信等。
|
|
9
9
|
- **技能(Skills)+ 工具调用**:模型可触发工具调用(OpenAI tools 兼容格式),技能注册表可扩展。
|
|
10
10
|
- **增强型记忆系统(Enhanced Memory)**:
|
|
@@ -24,7 +24,7 @@ npm install -g xiaozuoassistant --registry=https://registry.npmjs.org
|
|
|
24
24
|
xiaozuoAssistant start
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
然后浏览器打开:`http://localhost:
|
|
27
|
+
然后浏览器打开:`http://localhost:3021`
|
|
28
28
|
|
|
29
29
|
停止服务:
|
|
30
30
|
|
|
@@ -46,6 +46,8 @@ xiaozuoAssistant stop
|
|
|
46
46
|
|
|
47
47
|
### 开发模式(前后端联调)
|
|
48
48
|
|
|
49
|
+
端口默认为 `3001`。
|
|
50
|
+
|
|
49
51
|
```bash
|
|
50
52
|
npm install
|
|
51
53
|
npm run dev
|
|
@@ -53,6 +55,8 @@ npm run dev
|
|
|
53
55
|
|
|
54
56
|
### 生产模式(构建后运行)
|
|
55
57
|
|
|
58
|
+
端口默认为 `3021`。
|
|
59
|
+
|
|
56
60
|
```bash
|
|
57
61
|
npm install
|
|
58
62
|
npm run build
|
package/bin/cli.js
CHANGED
|
@@ -28,7 +28,9 @@ const args = process.argv.slice(2);
|
|
|
28
28
|
const command = args[0];
|
|
29
29
|
const commandArgs = args.slice(1);
|
|
30
30
|
|
|
31
|
-
const
|
|
31
|
+
const EXPORT_FILENAME_PREFIX = 'xiaozuoAssistant-backup';
|
|
32
|
+
const PRODUCTION_PORT = 3021; // 生产环境(CLI)默认端口
|
|
33
|
+
const DEV_PORT = 3001; // 开发环境默认端口
|
|
32
34
|
|
|
33
35
|
// Helper to get user's current working directory where they ran the command
|
|
34
36
|
// This is kept only for display/backward compatibility; runtime data is stored in APP_HOME.
|
|
@@ -50,7 +52,8 @@ const DATA_PATHS = [
|
|
|
50
52
|
'data',
|
|
51
53
|
'logs',
|
|
52
54
|
'sessions',
|
|
53
|
-
'workspace'
|
|
55
|
+
'workspace',
|
|
56
|
+
'plugins'
|
|
54
57
|
];
|
|
55
58
|
|
|
56
59
|
function ensureAppHome() {
|
|
@@ -65,11 +68,37 @@ function ensureAppHome() {
|
|
|
65
68
|
|
|
66
69
|
function ensureDefaultConfig() {
|
|
67
70
|
const configPath = path.join(APP_HOME, 'config.json');
|
|
68
|
-
|
|
71
|
+
|
|
72
|
+
// 如果配置文件已存在,检查是否需要迁移端口(例如从 3001 迁移到 3021)
|
|
73
|
+
if (fs.existsSync(configPath)) {
|
|
74
|
+
try {
|
|
75
|
+
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
76
|
+
const config = JSON.parse(configContent);
|
|
77
|
+
// 如果端口是旧的默认值 3001,自动更新为 3021
|
|
78
|
+
if (config.server && config.server.port === DEV_PORT) {
|
|
79
|
+
console.log(`[CLI] 检测到旧端口配置 (${DEV_PORT}),正在迁移到生产环境端口 (${PRODUCTION_PORT})...`);
|
|
80
|
+
config.server.port = PRODUCTION_PORT;
|
|
81
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
82
|
+
console.log(`[CLI] ✅ 端口已更新为 ${PRODUCTION_PORT}`);
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.warn('[CLI] 检查现有配置失败:', e);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 如果配置文件不存在,创建新的默认配置
|
|
69
91
|
const templatePath = path.join(packageRoot, 'config.json');
|
|
70
92
|
try {
|
|
71
93
|
if (fs.existsSync(templatePath)) {
|
|
72
|
-
|
|
94
|
+
// 复制模板,但强制修改端口为 3021
|
|
95
|
+
const templateContent = fs.readFileSync(templatePath, 'utf-8');
|
|
96
|
+
const templateConfig = JSON.parse(templateContent);
|
|
97
|
+
|
|
98
|
+
if (!templateConfig.server) templateConfig.server = {};
|
|
99
|
+
templateConfig.server.port = PRODUCTION_PORT;
|
|
100
|
+
|
|
101
|
+
fs.writeFileSync(configPath, JSON.stringify(templateConfig, null, 2));
|
|
73
102
|
return;
|
|
74
103
|
}
|
|
75
104
|
} catch (e) {
|
|
@@ -77,7 +106,7 @@ function ensureDefaultConfig() {
|
|
|
77
106
|
}
|
|
78
107
|
|
|
79
108
|
const fallback = {
|
|
80
|
-
server: { port:
|
|
109
|
+
server: { port: PRODUCTION_PORT, host: 'localhost' },
|
|
81
110
|
llm: { apiKey: '', baseURL: '', model: '', temperature: 0.7 },
|
|
82
111
|
logging: { level: 'info' },
|
|
83
112
|
channels: {},
|
|
@@ -103,7 +132,7 @@ function ensureDefaultDirs() {
|
|
|
103
132
|
}
|
|
104
133
|
|
|
105
134
|
function getPortFromConfig() {
|
|
106
|
-
let port =
|
|
135
|
+
let port = PRODUCTION_PORT;
|
|
107
136
|
const configPath = path.join(APP_HOME, 'config.json');
|
|
108
137
|
if (fs.existsSync(configPath)) {
|
|
109
138
|
try {
|
|
@@ -396,6 +425,11 @@ async function exportData() {
|
|
|
396
425
|
ensureDefaultDirs();
|
|
397
426
|
console.log('📦 Starting data export...');
|
|
398
427
|
|
|
428
|
+
// Generate filename with timestamp
|
|
429
|
+
const now = new Date();
|
|
430
|
+
const timestamp = now.toISOString().replace(/[-:T.]/g, '').slice(0, 14); // YYYYMMDDHHmmss
|
|
431
|
+
const exportFilename = `${EXPORT_FILENAME_PREFIX}-${timestamp}.tar.gz`;
|
|
432
|
+
|
|
399
433
|
const filesToArchive = [];
|
|
400
434
|
|
|
401
435
|
for (const p of DATA_PATHS) {
|
|
@@ -410,16 +444,19 @@ async function exportData() {
|
|
|
410
444
|
return;
|
|
411
445
|
}
|
|
412
446
|
|
|
447
|
+
const exportPath = path.join(APP_HOME, exportFilename);
|
|
448
|
+
|
|
413
449
|
try {
|
|
414
450
|
await tar.c(
|
|
415
451
|
{
|
|
416
452
|
gzip: true,
|
|
417
|
-
file:
|
|
453
|
+
file: exportPath,
|
|
418
454
|
cwd: APP_HOME
|
|
419
455
|
},
|
|
420
456
|
filesToArchive
|
|
421
457
|
);
|
|
422
|
-
console.log(`✅ Export successful
|
|
458
|
+
console.log(`✅ Export successful!`);
|
|
459
|
+
console.log(` Backup file: ${exportPath}`);
|
|
423
460
|
console.log(` Copy this file to your new machine to import.`);
|
|
424
461
|
} catch (err) {
|
|
425
462
|
console.error('❌ Export failed:', err);
|
|
@@ -428,15 +465,49 @@ async function exportData() {
|
|
|
428
465
|
|
|
429
466
|
async function importData() {
|
|
430
467
|
ensureAppHome();
|
|
431
|
-
|
|
468
|
+
|
|
469
|
+
// Find backup file
|
|
470
|
+
let backupFile = getFlagValue('--file');
|
|
471
|
+
|
|
472
|
+
if (!backupFile) {
|
|
473
|
+
// Try to find the latest backup file in APP_HOME
|
|
474
|
+
try {
|
|
475
|
+
const files = fs.readdirSync(APP_HOME)
|
|
476
|
+
.filter(f => f.startsWith(EXPORT_FILENAME_PREFIX) && f.endsWith('.tar.gz'))
|
|
477
|
+
.sort()
|
|
478
|
+
.reverse();
|
|
479
|
+
|
|
480
|
+
if (files.length > 0) {
|
|
481
|
+
backupFile = path.join(APP_HOME, files[0]);
|
|
482
|
+
console.log(`[CLI] No file specified, using latest found: ${files[0]}`);
|
|
483
|
+
} else {
|
|
484
|
+
// Fallback to old filename for compatibility
|
|
485
|
+
const oldFile = path.join(APP_HOME, 'xiaozuoAssistant-backup.tar.gz');
|
|
486
|
+
if (fs.existsSync(oldFile)) {
|
|
487
|
+
backupFile = oldFile;
|
|
488
|
+
console.log(`[CLI] Using legacy backup file: xiaozuoAssistant-backup.tar.gz`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
} catch (e) {
|
|
492
|
+
// ignore
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (!backupFile) {
|
|
497
|
+
console.error(`❌ No backup file found or specified.`);
|
|
498
|
+
console.log(` Use --file <path> to specify a backup file.`);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Resolve absolute path
|
|
503
|
+
const backupPath = path.resolve(process.cwd(), backupFile);
|
|
432
504
|
|
|
433
505
|
if (!fs.existsSync(backupPath)) {
|
|
434
506
|
console.error(`❌ Backup file not found: ${backupPath}`);
|
|
435
|
-
console.log(` Please ensure '${EXPORT_FILENAME}' is in ${APP_HOME}`);
|
|
436
507
|
return;
|
|
437
508
|
}
|
|
438
509
|
|
|
439
|
-
console.log(
|
|
510
|
+
console.log(`📦 Starting data import from: ${backupPath}`);
|
|
440
511
|
console.log('⚠️ Warning: This will overwrite existing data files (config.json, memories, etc.)');
|
|
441
512
|
|
|
442
513
|
// Simple prompt implementation for Node.js
|
|
@@ -445,6 +516,13 @@ async function importData() {
|
|
|
445
516
|
const answer = data.toString().trim().toLowerCase();
|
|
446
517
|
if (answer === 'y' || answer === 'yes') {
|
|
447
518
|
try {
|
|
519
|
+
// Stop server if running before import
|
|
520
|
+
const pidFile = getPidFilePath();
|
|
521
|
+
if (fs.existsSync(pidFile)) {
|
|
522
|
+
console.log('[CLI] Stopping server before import...');
|
|
523
|
+
await stopServer();
|
|
524
|
+
}
|
|
525
|
+
|
|
448
526
|
await tar.x({
|
|
449
527
|
file: backupPath,
|
|
450
528
|
cwd: APP_HOME
|
|
@@ -472,6 +550,12 @@ async function importData() {
|
|
|
472
550
|
}
|
|
473
551
|
}
|
|
474
552
|
|
|
553
|
+
// Ensure port is set to production port if it was dev port
|
|
554
|
+
if (config.server && config.server.port === DEV_PORT) {
|
|
555
|
+
config.server.port = PRODUCTION_PORT;
|
|
556
|
+
console.log(`[CLI] Updated port from ${DEV_PORT} to ${PRODUCTION_PORT}`);
|
|
557
|
+
}
|
|
558
|
+
|
|
475
559
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
476
560
|
console.log(`✅ Auto-configured workspace path to: ${APP_HOME}`);
|
|
477
561
|
} catch (e) {
|
package/package.json
CHANGED