@geminilight/mindos 0.2.0 → 0.3.0

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/scripts/setup.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * Usage: npm run setup OR mindos onboard
7
7
  *
8
8
  * Steps:
9
- * 1. Choose knowledge base path → default ~/.mindos/my-mind
9
+ * 1. Choose knowledge base path → default ~/MindOS
10
10
  * 2. Choose template (en / zh / empty / custom) → copy to knowledge base path
11
11
  * 3. Choose ports (web + mcp) — checked for conflicts upfront
12
12
  * 4. Auth token (auto-generated or passphrase-seeded)
@@ -26,7 +26,7 @@ import { homedir, tmpdir, networkInterfaces } from 'node:os';
26
26
  import { fileURLToPath } from 'node:url';
27
27
  import { createInterface } from 'node:readline';
28
28
  import { pipeline } from 'node:stream/promises';
29
- import { execSync } from 'node:child_process';
29
+ import { execSync, spawn } from 'node:child_process';
30
30
  import { randomBytes, createHash } from 'node:crypto';
31
31
  import { createConnection } from 'node:net';
32
32
 
@@ -41,6 +41,14 @@ const T = {
41
41
  title: { en: '🧠 MindOS Setup', zh: '🧠 MindOS 初始化' },
42
42
  langHint: { en: ' ← → switch language / 切换语言 ↑ ↓ navigate Enter confirm', zh: ' ← → switch language / 切换语言 ↑ ↓ 上下切换 Enter 确认' },
43
43
 
44
+ // mode selection
45
+ modePrompt: { en: 'Setup mode', zh: '配置方式' },
46
+ modeOpts: { en: ['CLI — terminal wizard', 'GUI — browser wizard (recommended)'], zh: ['CLI — 终端向导', 'GUI — 浏览器向导(推荐)'] },
47
+ modeVals: ['cli', 'gui'],
48
+ guiStarting: { en: '⏳ Starting server for GUI setup...', zh: '⏳ 正在启动服务...' },
49
+ guiReady: { en: (url) => `🌐 Complete setup in browser: ${url}`, zh: (url) => `🌐 在浏览器中完成配置: ${url}` },
50
+ guiOpenFailed: { en: (url) => ` Could not open browser automatically. Open this URL manually:\n ${url}`, zh: (url) => ` 无法自动打开浏览器,请手动访问:\n ${url}` },
51
+
44
52
  // step labels
45
53
  step: { en: (n, total) => `Step ${n}/${total}`, zh: (n, total) => `步骤 ${n}/${total}` },
46
54
  stepTitles: {
@@ -109,6 +117,8 @@ const T = {
109
117
  yesNo: { en: '[y/N]', zh: '[y/N]' },
110
118
  yesNoDefault: { en: '[Y/n]', zh: '[Y/n]' },
111
119
  startNow: { en: 'Start MindOS now?', zh: '现在启动 MindOS?' },
120
+ syncSetup: { en: 'Set up cross-device sync via Git?', zh: '是否配置 Git 跨设备同步?' },
121
+ syncLater: { en: ' → Run `mindos sync init` anytime to set up sync later.', zh: ' → 随时运行 `mindos sync init` 配置同步。' },
112
122
 
113
123
  // next steps (onboard — keep it minimal, details shown on `mindos start`)
114
124
  nextSteps: {
@@ -491,11 +501,95 @@ async function applyTemplate(tpl, mindDir) {
491
501
  }
492
502
  }
493
503
 
504
+ // ── GUI Setup ─────────────────────────────────────────────────────────────────
505
+
506
+ function openBrowser(url) {
507
+ try {
508
+ const platform = process.platform;
509
+ if (platform === 'darwin') {
510
+ execSync(`open "${url}"`, { stdio: 'ignore' });
511
+ } else if (platform === 'linux') {
512
+ // Check for WSL
513
+ const isWSL = existsSync('/proc/version') &&
514
+ readFileSync('/proc/version', 'utf-8').toLowerCase().includes('microsoft');
515
+ if (isWSL) {
516
+ execSync(`cmd.exe /c start "${url}"`, { stdio: 'ignore' });
517
+ } else {
518
+ execSync(`xdg-open "${url}"`, { stdio: 'ignore' });
519
+ }
520
+ } else {
521
+ execSync(`cmd.exe /c start "${url}"`, { stdio: 'ignore' });
522
+ }
523
+ return true;
524
+ } catch {
525
+ return false;
526
+ }
527
+ }
528
+
529
+ async function startGuiSetup() {
530
+ // Ensure ~/.mindos directory exists
531
+ mkdirSync(MINDOS_DIR, { recursive: true });
532
+
533
+ // Read or create config, set setupPending
534
+ let config = {};
535
+ try { config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch { /* ignore */ }
536
+ config.setupPending = true;
537
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
538
+
539
+ // Find a free port
540
+ const port = await findFreePort(3000);
541
+ if (config.port === undefined) {
542
+ config.port = port;
543
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
544
+ }
545
+ const usePort = config.port || port;
546
+
547
+ write(c.yellow(t('guiStarting') + '\n'));
548
+
549
+ // Start the server in the background
550
+ const cliPath = resolve(__dirname, '../bin/cli.js');
551
+ const child = spawn(process.execPath, [cliPath, 'start'], {
552
+ detached: true,
553
+ stdio: 'ignore',
554
+ env: { ...process.env, PORT: String(usePort) },
555
+ });
556
+ child.unref();
557
+
558
+ // Wait for the server to be ready
559
+ const { waitForHttp } = await import('../bin/lib/gateway.js');
560
+ const ready = await waitForHttp(usePort, { retries: 60, intervalMs: 1000, label: 'MindOS' });
561
+
562
+ if (!ready) {
563
+ write(c.red('\n✘ Server failed to start.\n'));
564
+ process.exit(1);
565
+ }
566
+
567
+ const url = `http://localhost:${usePort}/setup`;
568
+ console.log(`\n${c.green(tf('guiReady', url))}\n`);
569
+
570
+ const opened = openBrowser(url);
571
+ if (!opened) {
572
+ console.log(c.dim(tf('guiOpenFailed', url)));
573
+ }
574
+
575
+ process.exit(0);
576
+ }
577
+
494
578
  // ── Main ──────────────────────────────────────────────────────────────────────
495
579
 
496
580
  async function main() {
497
581
  console.log(`\n${c.bold(t('title'))}\n\n${c.dim(t('langHint'))}\n`);
498
582
 
583
+ // ── Mode selection: CLI or GUI ───────────────────────────────────────────
584
+ const mode = await select('modePrompt', 'modeOpts', 'modeVals');
585
+
586
+ if (mode === 'gui') {
587
+ await startGuiSetup();
588
+ return;
589
+ }
590
+
591
+ // ── CLI mode continues below ─────────────────────────────────────────────
592
+
499
593
  // ── Early overwrite check ─────────────────────────────────────────────────
500
594
  if (existsSync(CONFIG_PATH)) {
501
595
  let existing = {};
@@ -674,6 +768,15 @@ async function main() {
674
768
  writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
675
769
  console.log(`\n${c.green(t('cfgSaved'))}: ${c.dim(CONFIG_PATH)}`);
676
770
 
771
+ // ── Sync setup (optional) ──────────────────────────────────────────────────
772
+ const wantSync = await askYesNo('syncSetup');
773
+ if (wantSync) {
774
+ const { initSync } = await import('../bin/lib/sync.js');
775
+ await initSync(mindDir);
776
+ } else {
777
+ console.log(c.dim(t('syncLater')));
778
+ }
779
+
677
780
  const installDaemon = startMode === 'daemon' || process.argv.includes('--install-daemon');
678
781
  finish(mindDir, config.startMode, config.mcpPort, config.authToken, installDaemon);
679
782
  }
@@ -18,7 +18,7 @@ If you need to manually initialize:
18
18
  mindos onboard
19
19
 
20
20
  # 2) Or copy a preset manually to your knowledge base directory
21
- cp -r templates/en ~/.mindos/my-mind
21
+ cp -r templates/en ~/MindOS
22
22
  # then set mindRoot in ~/.mindos/config.json
23
23
 
24
24
  # 3) Start filling content from 👤 Profile (en) or 👤 画像 (zh)