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 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
- // Define important paths relative to where the user *runs* the app, NOT the package location
34
- // Assuming standard structure where config.json and data folders are in CWD
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
- 'config.json',
37
- 'memories', // Vector DB and others
38
- 'data', // SQLite or other data
39
- 'logs'
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(CWD, 'config.json');
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(CWD, 'logs', 'server.pid');
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: 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: 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(CWD, p));
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(CWD, p))) {
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: 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(CWD, EXPORT_FILENAME)}`);
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
- const backupPath = path.join(CWD, EXPORT_FILENAME);
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 the current directory.`);
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: EXPORT_FILENAME,
384
- cwd: 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(CWD, 'config.json');
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 = CWD;
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, CWD);
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: ${CWD}`);
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: ${CWD}`);
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 CWD
433
- const logDir = path.join(CWD, 'logs');
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: CWD, // Run in user's current directory
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('Usage: xiaozuoAssistant <command>');
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.47",
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
+