xiaozuoassistant 0.1.47 → 0.1.49
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 +104 -28
- package/package.json +5 -3
- package/scripts/init-app-home.cjs +43 -0
package/bin/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { spawn } from 'child_process';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import fs from 'fs';
|
|
7
|
+
import os from 'os';
|
|
7
8
|
import { createGzip } from 'zlib';
|
|
8
9
|
import { pipeline } from 'stream';
|
|
9
10
|
import { promisify } from 'util';
|
|
@@ -21,6 +22,8 @@ const __dirname = path.dirname(__filename);
|
|
|
21
22
|
// When installed globally: lib/node_modules/xiaozuoassistant/bin/cli.js -> ../
|
|
22
23
|
const packageRoot = path.resolve(__dirname, '..');
|
|
23
24
|
|
|
25
|
+
const binName = path.basename(process.argv[1] || 'xiaozuoAssistant');
|
|
26
|
+
|
|
24
27
|
const args = process.argv.slice(2);
|
|
25
28
|
const command = args[0];
|
|
26
29
|
const commandArgs = args.slice(1);
|
|
@@ -28,20 +31,80 @@ const commandArgs = args.slice(1);
|
|
|
28
31
|
const EXPORT_FILENAME = 'xiaozuoAssistant-backup.tar.gz';
|
|
29
32
|
|
|
30
33
|
// Helper to get user's current working directory where they ran the command
|
|
34
|
+
// This is kept only for display/backward compatibility; runtime data is stored in APP_HOME.
|
|
31
35
|
const CWD = process.cwd();
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
function getAppHome() {
|
|
38
|
+
const fromFlag = getFlagValue('--home');
|
|
39
|
+
const fromEnv = process.env.XIAOZUOASSISTANT_HOME;
|
|
40
|
+
const base = (fromFlag || fromEnv || path.join(os.homedir(), '.xiaozuoassistant')).trim();
|
|
41
|
+
return path.resolve(base);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const APP_HOME = getAppHome();
|
|
45
|
+
|
|
46
|
+
// Unified runtime data directory
|
|
35
47
|
const DATA_PATHS = [
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
'config.json',
|
|
49
|
+
'memories',
|
|
50
|
+
'data',
|
|
51
|
+
'logs',
|
|
52
|
+
'sessions',
|
|
53
|
+
'workspace'
|
|
40
54
|
];
|
|
41
55
|
|
|
56
|
+
function ensureAppHome() {
|
|
57
|
+
try {
|
|
58
|
+
fs.mkdirSync(APP_HOME, { recursive: true });
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.error(`[CLI] 无法创建数据目录:${APP_HOME}`);
|
|
61
|
+
console.error(e);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function ensureDefaultConfig() {
|
|
67
|
+
const configPath = path.join(APP_HOME, 'config.json');
|
|
68
|
+
if (fs.existsSync(configPath)) return;
|
|
69
|
+
const templatePath = path.join(packageRoot, 'config.json');
|
|
70
|
+
try {
|
|
71
|
+
if (fs.existsSync(templatePath)) {
|
|
72
|
+
fs.copyFileSync(templatePath, configPath);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
} catch (e) {
|
|
76
|
+
// ignore
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const fallback = {
|
|
80
|
+
server: { port: 3001, host: 'localhost' },
|
|
81
|
+
llm: { apiKey: '', baseURL: '', model: '', temperature: 0.7 },
|
|
82
|
+
logging: { level: 'info' },
|
|
83
|
+
channels: {},
|
|
84
|
+
systemPrompt: ''
|
|
85
|
+
};
|
|
86
|
+
try {
|
|
87
|
+
fs.writeFileSync(configPath, JSON.stringify(fallback, null, 2));
|
|
88
|
+
} catch (e) {
|
|
89
|
+
console.error(`[CLI] 无法写入默认配置:${configPath}`);
|
|
90
|
+
console.error(e);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function ensureDefaultDirs() {
|
|
96
|
+
for (const dir of ['logs', 'data', 'memories', 'sessions', 'workspace']) {
|
|
97
|
+
try {
|
|
98
|
+
fs.mkdirSync(path.join(APP_HOME, dir), { recursive: true });
|
|
99
|
+
} catch {
|
|
100
|
+
// ignore
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
42
105
|
function getPortFromConfig() {
|
|
43
106
|
let port = 3001;
|
|
44
|
-
const configPath = path.join(
|
|
107
|
+
const configPath = path.join(APP_HOME, 'config.json');
|
|
45
108
|
if (fs.existsSync(configPath)) {
|
|
46
109
|
try {
|
|
47
110
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
@@ -56,7 +119,7 @@ function getPortFromConfig() {
|
|
|
56
119
|
}
|
|
57
120
|
|
|
58
121
|
function getPidFilePath() {
|
|
59
|
-
return path.join(
|
|
122
|
+
return path.join(APP_HOME, 'logs', 'server.pid');
|
|
60
123
|
}
|
|
61
124
|
|
|
62
125
|
function hasFlag(flag) {
|
|
@@ -247,7 +310,7 @@ async function updateApp() {
|
|
|
247
310
|
console.error('[CLI] ❌ 更新失败。');
|
|
248
311
|
if (wasRunning) {
|
|
249
312
|
console.log('[CLI] 更新失败,尝试恢复启动旧版本服务...');
|
|
250
|
-
await runCommand('node', [path.join(packageRoot, 'bin', 'cli.js'), 'start'], { cwd:
|
|
313
|
+
await runCommand('node', [path.join(packageRoot, 'bin', 'cli.js'), 'start'], { cwd: APP_HOME });
|
|
251
314
|
}
|
|
252
315
|
process.exit(code);
|
|
253
316
|
}
|
|
@@ -256,7 +319,7 @@ async function updateApp() {
|
|
|
256
319
|
|
|
257
320
|
if (wasRunning) {
|
|
258
321
|
console.log('[CLI] 正在自动重启服务...');
|
|
259
|
-
const restartCode = await runCommand('xiaozuoAssistant', ['start'], { cwd:
|
|
322
|
+
const restartCode = await runCommand('xiaozuoAssistant', ['start'], { cwd: APP_HOME });
|
|
260
323
|
process.exit(restartCode);
|
|
261
324
|
}
|
|
262
325
|
}
|
|
@@ -264,7 +327,7 @@ async function updateApp() {
|
|
|
264
327
|
async function removeApp() {
|
|
265
328
|
const registry = getRegistry();
|
|
266
329
|
const targets = ['config.json', 'memories', 'data', 'logs', 'sessions', 'workspace']
|
|
267
|
-
.map(p => path.join(
|
|
330
|
+
.map(p => path.join(APP_HOME, p));
|
|
268
331
|
|
|
269
332
|
const doRemove = async () => {
|
|
270
333
|
await stopServer();
|
|
@@ -329,12 +392,14 @@ function printVersion() {
|
|
|
329
392
|
}
|
|
330
393
|
|
|
331
394
|
async function exportData() {
|
|
395
|
+
ensureAppHome();
|
|
396
|
+
ensureDefaultDirs();
|
|
332
397
|
console.log('📦 Starting data export...');
|
|
333
398
|
|
|
334
399
|
const filesToArchive = [];
|
|
335
400
|
|
|
336
401
|
for (const p of DATA_PATHS) {
|
|
337
|
-
if (fs.existsSync(path.join(
|
|
402
|
+
if (fs.existsSync(path.join(APP_HOME, p))) {
|
|
338
403
|
filesToArchive.push(p);
|
|
339
404
|
console.log(` - Found: ${p}`);
|
|
340
405
|
}
|
|
@@ -349,12 +414,12 @@ async function exportData() {
|
|
|
349
414
|
await tar.c(
|
|
350
415
|
{
|
|
351
416
|
gzip: true,
|
|
352
|
-
file: EXPORT_FILENAME,
|
|
353
|
-
cwd:
|
|
417
|
+
file: path.join(APP_HOME, EXPORT_FILENAME),
|
|
418
|
+
cwd: APP_HOME
|
|
354
419
|
},
|
|
355
420
|
filesToArchive
|
|
356
421
|
);
|
|
357
|
-
console.log(`✅ Export successful! Backup created at: ${path.join(
|
|
422
|
+
console.log(`✅ Export successful! Backup created at: ${path.join(APP_HOME, EXPORT_FILENAME)}`);
|
|
358
423
|
console.log(` Copy this file to your new machine to import.`);
|
|
359
424
|
} catch (err) {
|
|
360
425
|
console.error('❌ Export failed:', err);
|
|
@@ -362,11 +427,12 @@ async function exportData() {
|
|
|
362
427
|
}
|
|
363
428
|
|
|
364
429
|
async function importData() {
|
|
365
|
-
|
|
430
|
+
ensureAppHome();
|
|
431
|
+
const backupPath = path.join(APP_HOME, EXPORT_FILENAME);
|
|
366
432
|
|
|
367
433
|
if (!fs.existsSync(backupPath)) {
|
|
368
434
|
console.error(`❌ Backup file not found: ${backupPath}`);
|
|
369
|
-
console.log(` Please ensure '${EXPORT_FILENAME}' is in
|
|
435
|
+
console.log(` Please ensure '${EXPORT_FILENAME}' is in ${APP_HOME}`);
|
|
370
436
|
return;
|
|
371
437
|
}
|
|
372
438
|
|
|
@@ -380,13 +446,13 @@ async function importData() {
|
|
|
380
446
|
if (answer === 'y' || answer === 'yes') {
|
|
381
447
|
try {
|
|
382
448
|
await tar.x({
|
|
383
|
-
file:
|
|
384
|
-
cwd:
|
|
449
|
+
file: backupPath,
|
|
450
|
+
cwd: APP_HOME
|
|
385
451
|
});
|
|
386
452
|
console.log('✅ Import successful! Data restored.');
|
|
387
453
|
|
|
388
454
|
// Auto-configure workspace path in config.json
|
|
389
|
-
const configPath = path.join(
|
|
455
|
+
const configPath = path.join(APP_HOME, 'config.json');
|
|
390
456
|
if (fs.existsSync(configPath)) {
|
|
391
457
|
try {
|
|
392
458
|
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
@@ -394,20 +460,20 @@ async function importData() {
|
|
|
394
460
|
|
|
395
461
|
// Update workspace to current directory
|
|
396
462
|
const oldWorkspace = config.workspace;
|
|
397
|
-
config.workspace =
|
|
463
|
+
config.workspace = APP_HOME;
|
|
398
464
|
|
|
399
465
|
// Also update System Prompt if it contains the old workspace path
|
|
400
466
|
if (config.systemPrompt && typeof config.systemPrompt === 'string') {
|
|
401
467
|
if (oldWorkspace && config.systemPrompt.includes(oldWorkspace)) {
|
|
402
|
-
config.systemPrompt = config.systemPrompt.replace(oldWorkspace,
|
|
468
|
+
config.systemPrompt = config.systemPrompt.replace(oldWorkspace, APP_HOME);
|
|
403
469
|
} else if (config.systemPrompt.includes('Current Workspace:')) {
|
|
404
470
|
// Fallback regex replacement if exact string match fails
|
|
405
|
-
config.systemPrompt = config.systemPrompt.replace(/Current Workspace: .*/, `Current Workspace: ${
|
|
471
|
+
config.systemPrompt = config.systemPrompt.replace(/Current Workspace: .*/, `Current Workspace: ${APP_HOME}`);
|
|
406
472
|
}
|
|
407
473
|
}
|
|
408
474
|
|
|
409
475
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
410
|
-
console.log(`✅ Auto-configured workspace path to: ${
|
|
476
|
+
console.log(`✅ Auto-configured workspace path to: ${APP_HOME}`);
|
|
411
477
|
} catch (e) {
|
|
412
478
|
console.warn('⚠️ Failed to auto-update config.json path:', e);
|
|
413
479
|
}
|
|
@@ -425,12 +491,15 @@ async function importData() {
|
|
|
425
491
|
}
|
|
426
492
|
|
|
427
493
|
if (command === 'start') {
|
|
494
|
+
ensureAppHome();
|
|
495
|
+
ensureDefaultDirs();
|
|
496
|
+
ensureDefaultConfig();
|
|
428
497
|
console.log('Starting xiaozuoAssistant...');
|
|
429
498
|
|
|
430
499
|
const serverPath = path.join(packageRoot, 'dist', 'server', 'index.js');
|
|
431
500
|
|
|
432
|
-
// Ensure logs directory exists in
|
|
433
|
-
const logDir = path.join(
|
|
501
|
+
// Ensure logs directory exists in APP_HOME
|
|
502
|
+
const logDir = path.join(APP_HOME, 'logs');
|
|
434
503
|
if (!fs.existsSync(logDir)) {
|
|
435
504
|
try {
|
|
436
505
|
fs.mkdirSync(logDir, { recursive: true });
|
|
@@ -481,7 +550,7 @@ if (command === 'start') {
|
|
|
481
550
|
const child = spawn('node', [serverPath, ...args.slice(1)], {
|
|
482
551
|
detached: true, // Allow child to run independently
|
|
483
552
|
stdio: ['ignore', out, err], // Disconnect stdin, redirect stdout/stderr
|
|
484
|
-
cwd:
|
|
553
|
+
cwd: APP_HOME, // Run in unified app home
|
|
485
554
|
env: {
|
|
486
555
|
...process.env,
|
|
487
556
|
NODE_ENV: 'production'
|
|
@@ -550,6 +619,8 @@ if (command === 'start') {
|
|
|
550
619
|
process.exit(1);
|
|
551
620
|
}
|
|
552
621
|
} else if (command === 'doctor') {
|
|
622
|
+
ensureAppHome();
|
|
623
|
+
ensureDefaultDirs();
|
|
553
624
|
console.log('Running doctor check...');
|
|
554
625
|
try {
|
|
555
626
|
const pkgPath = path.join(packageRoot, 'package.json');
|
|
@@ -563,6 +634,7 @@ if (command === 'start') {
|
|
|
563
634
|
console.log('Node Version:', process.version);
|
|
564
635
|
console.log('Doctor check complete.');
|
|
565
636
|
} else if (command === 'version') {
|
|
637
|
+
ensureAppHome();
|
|
566
638
|
printVersion();
|
|
567
639
|
} else if (command === 'update') {
|
|
568
640
|
updateApp();
|
|
@@ -573,9 +645,10 @@ if (command === 'start') {
|
|
|
573
645
|
} else if (command === 'import') {
|
|
574
646
|
importData();
|
|
575
647
|
} else if (command === 'stop') {
|
|
648
|
+
ensureAppHome();
|
|
576
649
|
stopServer();
|
|
577
650
|
} else {
|
|
578
|
-
console.log(
|
|
651
|
+
console.log(`Usage: ${binName} <command>`);
|
|
579
652
|
console.log('Commands:');
|
|
580
653
|
console.log(' start Start the xiaozuoAssistant server');
|
|
581
654
|
console.log(' stop Stop the xiaozuoAssistant server');
|
|
@@ -585,4 +658,7 @@ if (command === 'start') {
|
|
|
585
658
|
console.log(' remove Uninstall and delete data in current directory');
|
|
586
659
|
console.log(' export Backup local data (config, memories) to a file');
|
|
587
660
|
console.log(' import Restore data from a backup file');
|
|
661
|
+
console.log('');
|
|
662
|
+
console.log(`Data Dir: ${APP_HOME}`);
|
|
663
|
+
console.log(' Use --home <path> or env XIAOZUOASSISTANT_HOME to override.');
|
|
588
664
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "xiaozuoassistant",
|
|
3
3
|
"private": false,
|
|
4
4
|
"description": "Your personal, locally-hosted AI assistant for office productivity.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.49",
|
|
6
6
|
"author": "mantle.lau",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
@@ -20,11 +20,13 @@
|
|
|
20
20
|
"type": "module",
|
|
21
21
|
"main": "dist/server/index.js",
|
|
22
22
|
"bin": {
|
|
23
|
-
"xiaozuoAssistant": "bin/cli.js"
|
|
23
|
+
"xiaozuoAssistant": "bin/cli.js",
|
|
24
|
+
"mybot": "bin/cli.js"
|
|
24
25
|
},
|
|
25
26
|
"files": [
|
|
26
27
|
"dist",
|
|
27
28
|
"bin",
|
|
29
|
+
"scripts",
|
|
28
30
|
"public",
|
|
29
31
|
"config.json",
|
|
30
32
|
"README.md"
|
|
@@ -37,7 +39,7 @@
|
|
|
37
39
|
"check": "tsc --noEmit",
|
|
38
40
|
"server:dev": "nodemon --watch src --watch config.json --exec tsx src/index.ts",
|
|
39
41
|
"dev": "concurrently \"npm run client:dev\" \"npm run server:dev\"",
|
|
40
|
-
"postinstall": "cp node_modules/@lancedb/lancedb-darwin-x64/lancedb.darwin-x64.node node_modules/@lancedb/lancedb/dist/ 2>/dev/null || true && npm rebuild better-sqlite3"
|
|
42
|
+
"postinstall": "node scripts/init-app-home.cjs || true && cp node_modules/@lancedb/lancedb-darwin-x64/lancedb.darwin-x64.node node_modules/@lancedb/lancedb/dist/ 2>/dev/null || true && npm rebuild better-sqlite3"
|
|
41
43
|
},
|
|
42
44
|
"dependencies": {
|
|
43
45
|
"@lancedb/lancedb": "0.22.3",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
function guessHomeFromSudoUser() {
|
|
6
|
+
const sudoUser = process.env.SUDO_USER;
|
|
7
|
+
if (!sudoUser) return null;
|
|
8
|
+
|
|
9
|
+
const candidates = [
|
|
10
|
+
path.join('/Users', sudoUser),
|
|
11
|
+
path.join('/home', sudoUser)
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
for (const p of candidates) {
|
|
15
|
+
try {
|
|
16
|
+
if (fs.existsSync(p)) return p;
|
|
17
|
+
} catch {
|
|
18
|
+
// ignore
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolveTargetHome() {
|
|
26
|
+
const guessed = guessHomeFromSudoUser();
|
|
27
|
+
if (guessed) return guessed;
|
|
28
|
+
return os.homedir();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function main() {
|
|
32
|
+
const home = resolveTargetHome();
|
|
33
|
+
const appHome = path.join(home, '.xiaozuoassistant');
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
fs.mkdirSync(appHome, { recursive: true });
|
|
37
|
+
} catch {
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
main();
|
|
43
|
+
|