xiaozuoassistant 0.1.45 → 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 +249 -24
- package/package.json +3 -2
- 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';
|
|
@@ -23,24 +24,85 @@ const packageRoot = path.resolve(__dirname, '..');
|
|
|
23
24
|
|
|
24
25
|
const args = process.argv.slice(2);
|
|
25
26
|
const command = args[0];
|
|
27
|
+
const commandArgs = args.slice(1);
|
|
26
28
|
|
|
27
29
|
const EXPORT_FILENAME = 'xiaozuoAssistant-backup.tar.gz';
|
|
28
30
|
|
|
29
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.
|
|
30
33
|
const CWD = process.cwd();
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
34
45
|
const DATA_PATHS = [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
'config.json',
|
|
47
|
+
'memories',
|
|
48
|
+
'data',
|
|
49
|
+
'logs',
|
|
50
|
+
'sessions',
|
|
51
|
+
'workspace'
|
|
39
52
|
];
|
|
40
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
|
+
|
|
41
103
|
function getPortFromConfig() {
|
|
42
104
|
let port = 3001;
|
|
43
|
-
const configPath = path.join(
|
|
105
|
+
const configPath = path.join(APP_HOME, 'config.json');
|
|
44
106
|
if (fs.existsSync(configPath)) {
|
|
45
107
|
try {
|
|
46
108
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
@@ -55,7 +117,23 @@ function getPortFromConfig() {
|
|
|
55
117
|
}
|
|
56
118
|
|
|
57
119
|
function getPidFilePath() {
|
|
58
|
-
return path.join(
|
|
120
|
+
return path.join(APP_HOME, 'logs', 'server.pid');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function hasFlag(flag) {
|
|
124
|
+
return commandArgs.includes(flag);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function getFlagValue(name) {
|
|
128
|
+
const idx = commandArgs.findIndex(a => a === name);
|
|
129
|
+
if (idx < 0) return null;
|
|
130
|
+
const next = commandArgs[idx + 1];
|
|
131
|
+
if (!next || next.startsWith('-')) return null;
|
|
132
|
+
return next;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getRegistry() {
|
|
136
|
+
return getFlagValue('--registry') || process.env.npm_config_registry || 'https://registry.npmjs.org';
|
|
59
137
|
}
|
|
60
138
|
|
|
61
139
|
function isProcessRunning(pid) {
|
|
@@ -186,13 +264,140 @@ async function stopServer() {
|
|
|
186
264
|
console.error('[CLI] ❌ 停止失败:端口仍在占用。');
|
|
187
265
|
}
|
|
188
266
|
|
|
267
|
+
async function runCommand(cmd, cmdArgs, options = {}) {
|
|
268
|
+
const child = spawn(cmd, cmdArgs, { stdio: 'inherit', ...options });
|
|
269
|
+
const code = await new Promise((resolve) => child.on('close', resolve));
|
|
270
|
+
return typeof code === 'number' ? code : 1;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function runWithSudoIfNeeded(cmd, cmdArgs, options = {}) {
|
|
274
|
+
const code = await runCommand(cmd, cmdArgs, options);
|
|
275
|
+
if (code === 0) return 0;
|
|
276
|
+
|
|
277
|
+
const canSudo = process.platform !== 'win32' && process.stdin.isTTY;
|
|
278
|
+
if (!canSudo) return code;
|
|
279
|
+
|
|
280
|
+
console.log('[CLI] 权限不足,尝试使用 sudo 继续...');
|
|
281
|
+
return await runCommand('sudo', [cmd, ...cmdArgs], options);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async function updateApp() {
|
|
285
|
+
const registry = getRegistry();
|
|
286
|
+
const port = getPortFromConfig();
|
|
287
|
+
const pidFile = getPidFilePath();
|
|
288
|
+
|
|
289
|
+
const runningByPid = fs.existsSync(pidFile) && (() => {
|
|
290
|
+
try {
|
|
291
|
+
const pid = Number(fs.readFileSync(pidFile, 'utf-8').trim());
|
|
292
|
+
return Number.isFinite(pid) && pid > 0 && isProcessRunning(pid);
|
|
293
|
+
} catch {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
})();
|
|
297
|
+
const runningByPort = await isPortOpen(port);
|
|
298
|
+
const wasRunning = runningByPid || runningByPort;
|
|
299
|
+
|
|
300
|
+
if (wasRunning) {
|
|
301
|
+
console.log('[CLI] 检测到服务正在运行,更新前将自动停止...');
|
|
302
|
+
await stopServer();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
console.log(`[CLI] 正在更新 xiaozuoassistant(registry=${registry})...`);
|
|
306
|
+
const code = await runWithSudoIfNeeded('npm', ['install', '-g', 'xiaozuoassistant@latest', `--registry=${registry}`]);
|
|
307
|
+
if (code !== 0) {
|
|
308
|
+
console.error('[CLI] ❌ 更新失败。');
|
|
309
|
+
if (wasRunning) {
|
|
310
|
+
console.log('[CLI] 更新失败,尝试恢复启动旧版本服务...');
|
|
311
|
+
await runCommand('node', [path.join(packageRoot, 'bin', 'cli.js'), 'start'], { cwd: APP_HOME });
|
|
312
|
+
}
|
|
313
|
+
process.exit(code);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
console.log('[CLI] ✅ 更新完成。');
|
|
317
|
+
|
|
318
|
+
if (wasRunning) {
|
|
319
|
+
console.log('[CLI] 正在自动重启服务...');
|
|
320
|
+
const restartCode = await runCommand('xiaozuoAssistant', ['start'], { cwd: APP_HOME });
|
|
321
|
+
process.exit(restartCode);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async function removeApp() {
|
|
326
|
+
const registry = getRegistry();
|
|
327
|
+
const targets = ['config.json', 'memories', 'data', 'logs', 'sessions', 'workspace']
|
|
328
|
+
.map(p => path.join(APP_HOME, p));
|
|
329
|
+
|
|
330
|
+
const doRemove = async () => {
|
|
331
|
+
await stopServer();
|
|
332
|
+
|
|
333
|
+
console.log('[CLI] 正在删除本地数据(当前目录)...');
|
|
334
|
+
for (const target of targets) {
|
|
335
|
+
try {
|
|
336
|
+
if (fs.existsSync(target)) {
|
|
337
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
338
|
+
console.log(`[CLI] 已删除:${target}`);
|
|
339
|
+
}
|
|
340
|
+
} catch (e) {
|
|
341
|
+
console.error(`[CLI] 删除失败:${target}`);
|
|
342
|
+
console.error(e);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
console.log('[CLI] 正在卸载全局包 xiaozuoassistant...');
|
|
347
|
+
const code = await runWithSudoIfNeeded('npm', ['uninstall', '-g', 'xiaozuoassistant', `--registry=${registry}`]);
|
|
348
|
+
if (code !== 0) {
|
|
349
|
+
console.error('[CLI] ❌ 卸载失败。');
|
|
350
|
+
process.exit(code);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
console.log('[CLI] ✅ 已卸载并清理数据。');
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
if (hasFlag('--yes') || hasFlag('-y')) {
|
|
357
|
+
await doRemove();
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
console.log('[CLI] remove 将执行以下操作:');
|
|
362
|
+
console.log(' 1) 停止后台服务');
|
|
363
|
+
console.log(' 2) 删除当前目录下的数据:');
|
|
364
|
+
for (const target of targets) console.log(` - ${target}`);
|
|
365
|
+
console.log(' 3) 卸载全局 npm 包:xiaozuoassistant');
|
|
366
|
+
process.stdout.write('确认继续?(y/N) ');
|
|
367
|
+
process.stdin.once('data', async (data) => {
|
|
368
|
+
const answer = data.toString().trim().toLowerCase();
|
|
369
|
+
if (answer === 'y' || answer === 'yes') {
|
|
370
|
+
await doRemove();
|
|
371
|
+
} else {
|
|
372
|
+
console.log('已取消。');
|
|
373
|
+
}
|
|
374
|
+
process.exit(0);
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function printVersion() {
|
|
379
|
+
try {
|
|
380
|
+
const pkgPath = path.join(packageRoot, 'package.json');
|
|
381
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
382
|
+
console.log(`${pkg.name}@${pkg.version}`);
|
|
383
|
+
console.log('Package Root:', packageRoot);
|
|
384
|
+
console.log('Node Version:', process.version);
|
|
385
|
+
} catch {
|
|
386
|
+
console.log('xiaozuoassistant@unknown');
|
|
387
|
+
console.log('Package Root:', packageRoot);
|
|
388
|
+
console.log('Node Version:', process.version);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
189
392
|
async function exportData() {
|
|
393
|
+
ensureAppHome();
|
|
394
|
+
ensureDefaultDirs();
|
|
190
395
|
console.log('📦 Starting data export...');
|
|
191
396
|
|
|
192
397
|
const filesToArchive = [];
|
|
193
398
|
|
|
194
399
|
for (const p of DATA_PATHS) {
|
|
195
|
-
if (fs.existsSync(path.join(
|
|
400
|
+
if (fs.existsSync(path.join(APP_HOME, p))) {
|
|
196
401
|
filesToArchive.push(p);
|
|
197
402
|
console.log(` - Found: ${p}`);
|
|
198
403
|
}
|
|
@@ -207,12 +412,12 @@ async function exportData() {
|
|
|
207
412
|
await tar.c(
|
|
208
413
|
{
|
|
209
414
|
gzip: true,
|
|
210
|
-
file: EXPORT_FILENAME,
|
|
211
|
-
cwd:
|
|
415
|
+
file: path.join(APP_HOME, EXPORT_FILENAME),
|
|
416
|
+
cwd: APP_HOME
|
|
212
417
|
},
|
|
213
418
|
filesToArchive
|
|
214
419
|
);
|
|
215
|
-
console.log(`✅ Export successful! Backup created at: ${path.join(
|
|
420
|
+
console.log(`✅ Export successful! Backup created at: ${path.join(APP_HOME, EXPORT_FILENAME)}`);
|
|
216
421
|
console.log(` Copy this file to your new machine to import.`);
|
|
217
422
|
} catch (err) {
|
|
218
423
|
console.error('❌ Export failed:', err);
|
|
@@ -220,11 +425,12 @@ async function exportData() {
|
|
|
220
425
|
}
|
|
221
426
|
|
|
222
427
|
async function importData() {
|
|
223
|
-
|
|
428
|
+
ensureAppHome();
|
|
429
|
+
const backupPath = path.join(APP_HOME, EXPORT_FILENAME);
|
|
224
430
|
|
|
225
431
|
if (!fs.existsSync(backupPath)) {
|
|
226
432
|
console.error(`❌ Backup file not found: ${backupPath}`);
|
|
227
|
-
console.log(` Please ensure '${EXPORT_FILENAME}' is in
|
|
433
|
+
console.log(` Please ensure '${EXPORT_FILENAME}' is in ${APP_HOME}`);
|
|
228
434
|
return;
|
|
229
435
|
}
|
|
230
436
|
|
|
@@ -238,13 +444,13 @@ async function importData() {
|
|
|
238
444
|
if (answer === 'y' || answer === 'yes') {
|
|
239
445
|
try {
|
|
240
446
|
await tar.x({
|
|
241
|
-
file:
|
|
242
|
-
cwd:
|
|
447
|
+
file: backupPath,
|
|
448
|
+
cwd: APP_HOME
|
|
243
449
|
});
|
|
244
450
|
console.log('✅ Import successful! Data restored.');
|
|
245
451
|
|
|
246
452
|
// Auto-configure workspace path in config.json
|
|
247
|
-
const configPath = path.join(
|
|
453
|
+
const configPath = path.join(APP_HOME, 'config.json');
|
|
248
454
|
if (fs.existsSync(configPath)) {
|
|
249
455
|
try {
|
|
250
456
|
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
@@ -252,20 +458,20 @@ async function importData() {
|
|
|
252
458
|
|
|
253
459
|
// Update workspace to current directory
|
|
254
460
|
const oldWorkspace = config.workspace;
|
|
255
|
-
config.workspace =
|
|
461
|
+
config.workspace = APP_HOME;
|
|
256
462
|
|
|
257
463
|
// Also update System Prompt if it contains the old workspace path
|
|
258
464
|
if (config.systemPrompt && typeof config.systemPrompt === 'string') {
|
|
259
465
|
if (oldWorkspace && config.systemPrompt.includes(oldWorkspace)) {
|
|
260
|
-
config.systemPrompt = config.systemPrompt.replace(oldWorkspace,
|
|
466
|
+
config.systemPrompt = config.systemPrompt.replace(oldWorkspace, APP_HOME);
|
|
261
467
|
} else if (config.systemPrompt.includes('Current Workspace:')) {
|
|
262
468
|
// Fallback regex replacement if exact string match fails
|
|
263
|
-
config.systemPrompt = config.systemPrompt.replace(/Current Workspace: .*/, `Current Workspace: ${
|
|
469
|
+
config.systemPrompt = config.systemPrompt.replace(/Current Workspace: .*/, `Current Workspace: ${APP_HOME}`);
|
|
264
470
|
}
|
|
265
471
|
}
|
|
266
472
|
|
|
267
473
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
268
|
-
console.log(`✅ Auto-configured workspace path to: ${
|
|
474
|
+
console.log(`✅ Auto-configured workspace path to: ${APP_HOME}`);
|
|
269
475
|
} catch (e) {
|
|
270
476
|
console.warn('⚠️ Failed to auto-update config.json path:', e);
|
|
271
477
|
}
|
|
@@ -283,12 +489,15 @@ async function importData() {
|
|
|
283
489
|
}
|
|
284
490
|
|
|
285
491
|
if (command === 'start') {
|
|
492
|
+
ensureAppHome();
|
|
493
|
+
ensureDefaultDirs();
|
|
494
|
+
ensureDefaultConfig();
|
|
286
495
|
console.log('Starting xiaozuoAssistant...');
|
|
287
496
|
|
|
288
497
|
const serverPath = path.join(packageRoot, 'dist', 'server', 'index.js');
|
|
289
498
|
|
|
290
|
-
// Ensure logs directory exists in
|
|
291
|
-
const logDir = path.join(
|
|
499
|
+
// Ensure logs directory exists in APP_HOME
|
|
500
|
+
const logDir = path.join(APP_HOME, 'logs');
|
|
292
501
|
if (!fs.existsSync(logDir)) {
|
|
293
502
|
try {
|
|
294
503
|
fs.mkdirSync(logDir, { recursive: true });
|
|
@@ -339,7 +548,7 @@ if (command === 'start') {
|
|
|
339
548
|
const child = spawn('node', [serverPath, ...args.slice(1)], {
|
|
340
549
|
detached: true, // Allow child to run independently
|
|
341
550
|
stdio: ['ignore', out, err], // Disconnect stdin, redirect stdout/stderr
|
|
342
|
-
cwd:
|
|
551
|
+
cwd: APP_HOME, // Run in unified app home
|
|
343
552
|
env: {
|
|
344
553
|
...process.env,
|
|
345
554
|
NODE_ENV: 'production'
|
|
@@ -408,6 +617,8 @@ if (command === 'start') {
|
|
|
408
617
|
process.exit(1);
|
|
409
618
|
}
|
|
410
619
|
} else if (command === 'doctor') {
|
|
620
|
+
ensureAppHome();
|
|
621
|
+
ensureDefaultDirs();
|
|
411
622
|
console.log('Running doctor check...');
|
|
412
623
|
try {
|
|
413
624
|
const pkgPath = path.join(packageRoot, 'package.json');
|
|
@@ -420,11 +631,19 @@ if (command === 'start') {
|
|
|
420
631
|
console.log('Package Root:', packageRoot);
|
|
421
632
|
console.log('Node Version:', process.version);
|
|
422
633
|
console.log('Doctor check complete.');
|
|
634
|
+
} else if (command === 'version') {
|
|
635
|
+
ensureAppHome();
|
|
636
|
+
printVersion();
|
|
637
|
+
} else if (command === 'update') {
|
|
638
|
+
updateApp();
|
|
639
|
+
} else if (command === 'remove') {
|
|
640
|
+
removeApp();
|
|
423
641
|
} else if (command === 'export') {
|
|
424
642
|
exportData();
|
|
425
643
|
} else if (command === 'import') {
|
|
426
644
|
importData();
|
|
427
645
|
} else if (command === 'stop') {
|
|
646
|
+
ensureAppHome();
|
|
428
647
|
stopServer();
|
|
429
648
|
} else {
|
|
430
649
|
console.log('Usage: xiaozuoAssistant <command>');
|
|
@@ -432,6 +651,12 @@ if (command === 'start') {
|
|
|
432
651
|
console.log(' start Start the xiaozuoAssistant server');
|
|
433
652
|
console.log(' stop Stop the xiaozuoAssistant server');
|
|
434
653
|
console.log(' doctor Check the health and configuration');
|
|
654
|
+
console.log(' version Print package version and environment');
|
|
655
|
+
console.log(' update Update xiaozuoassistant and auto-restart if running');
|
|
656
|
+
console.log(' remove Uninstall and delete data in current directory');
|
|
435
657
|
console.log(' export Backup local data (config, memories) to a file');
|
|
436
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.');
|
|
437
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.
|
|
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
|
+
|