xiaozuoassistant 0.1.47 → 0.1.48

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