@geminilight/mindos 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -122,9 +122,11 @@ npm link # registers the `mindos` command globally
122
122
  ### 2. Interactive Setup
123
123
 
124
124
  ```bash
125
- mindos onboard --install-daemon # setup + install & start as background OS service
125
+ mindos onboard --install-daemon
126
126
  ```
127
127
 
128
+ > `--install-daemon`: after setup, automatically installs and starts MindOS as a background OS service (survives terminal close, auto-restarts on crash).
129
+
128
130
  The setup wizard will guide you through:
129
131
  1. Knowledge base path → default `~/.mindos/my-mind`
130
132
  2. Choose template language (en / zh)
package/README_zh.md CHANGED
@@ -122,9 +122,11 @@ npm link # 将 mindos 命令注册为全局命令
122
122
  ### 2. 交互式配置
123
123
 
124
124
  ```bash
125
- mindos onboard --install-daemon # 初始化 + 安装并启动后台服务
125
+ mindos onboard --install-daemon
126
126
  ```
127
127
 
128
+ > `--install-daemon`:配置完成后,自动将 MindOS 安装为后台 OS 服务(关闭终端仍运行,崩溃自动重启)。
129
+
128
130
  配置向导将引导你完成:
129
131
  1. 知识库路径 → 默认 `~/.mindos/my-mind`
130
132
  2. 选择模板语言(en / zh / empty)
@@ -1,9 +1,8 @@
1
1
  import type { Metadata } from 'next';
2
2
  import { Geist, Geist_Mono, IBM_Plex_Mono, IBM_Plex_Sans, Lora } from 'next/font/google';
3
- import { headers } from 'next/headers';
4
3
  import './globals.css';
5
4
  import { getFileTree } from '@/lib/fs';
6
- import SidebarLayout from '@/components/SidebarLayout';
5
+ import ShellLayout from '@/components/ShellLayout';
7
6
  import { TooltipProvider } from '@/components/ui/tooltip';
8
7
  import { LocaleProvider } from '@/lib/LocaleContext';
9
8
  import ErrorBoundary from '@/components/ErrorBoundary';
@@ -49,7 +48,7 @@ export const viewport = {
49
48
  viewportFit: 'cover' as const,
50
49
  };
51
50
 
52
- export default async function RootLayout({
51
+ export default function RootLayout({
53
52
  children,
54
53
  }: Readonly<{
55
54
  children: React.ReactNode;
@@ -61,9 +60,6 @@ export default async function RootLayout({
61
60
  console.error('[RootLayout] Failed to load file tree:', err);
62
61
  }
63
62
 
64
- const headersList = await headers();
65
- const isLoginPage = headersList.get('x-pathname') === '/login';
66
-
67
63
  return (
68
64
  <html lang="en" suppressHydrationWarning>
69
65
  <head>
@@ -89,11 +85,9 @@ export default async function RootLayout({
89
85
  <LocaleProvider>
90
86
  <TooltipProvider delay={300}>
91
87
  <ErrorBoundary>
92
- {isLoginPage ? children : (
93
- <SidebarLayout fileTree={fileTree}>
94
- {children}
95
- </SidebarLayout>
96
- )}
88
+ <ShellLayout fileTree={fileTree}>
89
+ {children}
90
+ </ShellLayout>
97
91
  </ErrorBoundary>
98
92
  </TooltipProvider>
99
93
  </LocaleProvider>
@@ -55,7 +55,7 @@ export default function ViewPageClient({
55
55
  },
56
56
  () => {
57
57
  const saved = localStorage.getItem('mindos-use-raw');
58
- return saved !== null ? saved === 'true' : true;
58
+ return saved !== null ? saved === 'true' : false;
59
59
  },
60
60
  () => false,
61
61
  );
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useState, useEffect, useMemo } from 'react';
3
+ import { useState, useSyncExternalStore, useMemo } from 'react';
4
4
  import Link from 'next/link';
5
5
  import { FileText, Table, Folder, FolderOpen, LayoutGrid, List } from 'lucide-react';
6
6
  import Breadcrumb from '@/components/Breadcrumb';
@@ -33,16 +33,22 @@ function countFiles(node: FileNode): number {
33
33
  const DIR_VIEW_KEY = 'mindos-dir-view';
34
34
 
35
35
  function useDirViewPref() {
36
- const [view, setViewState] = useState<'grid' | 'list'>('grid');
37
-
38
- useEffect(() => {
39
- const saved = localStorage.getItem(DIR_VIEW_KEY);
40
- if (saved === 'list' || saved === 'grid') setViewState(saved);
41
- }, []);
36
+ const view = useSyncExternalStore(
37
+ (onStoreChange) => {
38
+ const listener = () => onStoreChange();
39
+ window.addEventListener('mindos-dir-view-change', listener);
40
+ return () => window.removeEventListener('mindos-dir-view-change', listener);
41
+ },
42
+ () => {
43
+ const saved = localStorage.getItem(DIR_VIEW_KEY);
44
+ return (saved === 'list' || saved === 'grid') ? saved : 'grid';
45
+ },
46
+ () => 'grid' as const,
47
+ );
42
48
 
43
49
  const setView = (v: 'grid' | 'list') => {
44
- setViewState(v);
45
50
  localStorage.setItem(DIR_VIEW_KEY, v);
51
+ window.dispatchEvent(new Event('mindos-dir-view-change'));
46
52
  };
47
53
 
48
54
  return [view, setView] as const;
@@ -26,20 +26,19 @@ export default function SettingsModal({ open, onClose }: SettingsModalProps) {
26
26
  const [status, setStatus] = useState<'idle' | 'saved' | 'error' | 'load-error'>('idle');
27
27
  const { t, locale, setLocale } = useLocale();
28
28
 
29
- // Appearance state (localStorage-based)
30
- const [font, setFont] = useState('lora');
31
- const [contentWidth, setContentWidth] = useState('780px');
32
- const [dark, setDark] = useState(true);
29
+ // Appearance state (localStorage-based) — read directly on mount; this component is client-only
30
+ const [font, setFont] = useState(() => localStorage.getItem('prose-font') ?? 'lora');
31
+ const [contentWidth, setContentWidth] = useState(() => localStorage.getItem('content-width') ?? '780px');
32
+ const [dark, setDark] = useState(() => {
33
+ const stored = localStorage.getItem('theme');
34
+ return stored ? stored === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches;
35
+ });
33
36
  // Plugin enabled state
34
37
  const [pluginStates, setPluginStates] = useState<Record<string, boolean>>({});
35
38
 
36
39
  useEffect(() => {
37
40
  if (!open) return;
38
41
  apiFetch<SettingsData>('/api/settings').then(setData).catch(() => setStatus('load-error'));
39
- setFont(localStorage.getItem('prose-font') ?? 'lora');
40
- setContentWidth(localStorage.getItem('content-width') ?? '780px');
41
- const stored = localStorage.getItem('theme');
42
- setDark(stored ? stored === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches);
43
42
  loadDisabledState();
44
43
  const initial: Record<string, boolean> = {};
45
44
  for (const r of getAllRenderers()) initial[r.id] = isRendererEnabled(r.id);
@@ -0,0 +1,16 @@
1
+ 'use client';
2
+
3
+ import { usePathname } from 'next/navigation';
4
+ import SidebarLayout from './SidebarLayout';
5
+ import { FileNode } from '@/lib/types';
6
+
7
+ interface ShellLayoutProps {
8
+ fileTree: FileNode[];
9
+ children: React.ReactNode;
10
+ }
11
+
12
+ export default function ShellLayout({ fileTree, children }: ShellLayoutProps) {
13
+ const pathname = usePathname();
14
+ if (pathname === '/login') return <>{children}</>;
15
+ return <SidebarLayout fileTree={fileTree}>{children}</SidebarLayout>;
16
+ }
@@ -1,25 +1,27 @@
1
1
  'use client';
2
2
 
3
- import { useEffect, useState } from 'react';
3
+ import { useSyncExternalStore } from 'react';
4
4
  import { Sun, Moon } from 'lucide-react';
5
5
 
6
6
  export default function ThemeToggle() {
7
- const [dark, setDark] = useState(true);
8
-
9
- useEffect(() => {
10
- const stored = localStorage.getItem('theme');
11
- const isDark = stored
12
- ? stored === 'dark'
13
- : window.matchMedia('(prefers-color-scheme: dark)').matches;
14
- setDark(isDark);
15
- document.documentElement.classList.toggle('dark', isDark);
16
- }, []);
7
+ const dark = useSyncExternalStore(
8
+ (onStoreChange) => {
9
+ const listener = () => onStoreChange();
10
+ window.addEventListener('mindos-theme-change', listener);
11
+ return () => window.removeEventListener('mindos-theme-change', listener);
12
+ },
13
+ () => {
14
+ const stored = localStorage.getItem('theme');
15
+ return stored ? stored === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches;
16
+ },
17
+ () => true,
18
+ );
17
19
 
18
20
  const toggle = () => {
19
21
  const next = !dark;
20
- setDark(next);
21
22
  document.documentElement.classList.toggle('dark', next);
22
23
  localStorage.setItem('theme', next ? 'dark' : 'light');
24
+ window.dispatchEvent(new Event('mindos-theme-change'));
23
25
  };
24
26
 
25
27
  return (
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useState } from 'react';
3
+ import { useState, useSyncExternalStore } from 'react';
4
4
  import { Copy, Check, RefreshCw, Trash2 } from 'lucide-react';
5
5
  import type { SettingsData } from './types';
6
6
  import { Field, Input, EnvBadge, SectionLabel } from './Primitives';
@@ -16,6 +16,12 @@ export function KnowledgeTab({ data, setData, t }: KnowledgeTabProps) {
16
16
  const env = data.envOverrides ?? {};
17
17
  const k = t.settings.knowledge;
18
18
 
19
+ const origin = useSyncExternalStore(
20
+ () => () => {},
21
+ () => `${window.location.protocol}//${window.location.hostname}`,
22
+ () => 'http://localhost',
23
+ );
24
+
19
25
  const [showPassword, setShowPassword] = useState(false);
20
26
  const isPasswordMasked = data.webPassword === '***set***';
21
27
 
@@ -118,7 +124,7 @@ export function KnowledgeTab({ data, setData, t }: KnowledgeTabProps) {
118
124
  {k.authTokenMcpPort}: <code className="font-mono">{data.mcpPort}</code>
119
125
  {displayToken && (
120
126
  <> &nbsp;·&nbsp; MCP URL: <code className="font-mono select-all">
121
- {typeof window !== 'undefined' ? `${window.location.protocol}//${window.location.hostname}:${data.mcpPort}/mcp` : `http://localhost:${data.mcpPort}/mcp`}
127
+ {`${origin}:${data.mcpPort}/mcp`}
122
128
  </code></>
123
129
  )}
124
130
  </p>
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
3
+ import { createContext, useContext, useSyncExternalStore, ReactNode } from 'react';
4
4
  import { Locale, messages, Messages } from './i18n';
5
5
 
6
6
  interface LocaleContextValue {
@@ -15,17 +15,25 @@ const LocaleContext = createContext<LocaleContextValue>({
15
15
  t: messages['en'],
16
16
  });
17
17
 
18
- export function LocaleProvider({ children }: { children: ReactNode }) {
19
- const [locale, setLocaleState] = useState<Locale>('en');
18
+ function getLocaleSnapshot(): Locale {
19
+ const saved = localStorage.getItem('locale');
20
+ return saved === 'zh' ? 'zh' : 'en';
21
+ }
20
22
 
21
- useEffect(() => {
22
- const saved = localStorage.getItem('locale') as Locale | null;
23
- if (saved === 'zh' || saved === 'en') setLocaleState(saved);
24
- }, []);
23
+ export function LocaleProvider({ children }: { children: ReactNode }) {
24
+ const locale = useSyncExternalStore(
25
+ (onStoreChange) => {
26
+ const listener = () => onStoreChange();
27
+ window.addEventListener('mindos-locale-change', listener);
28
+ return () => window.removeEventListener('mindos-locale-change', listener);
29
+ },
30
+ getLocaleSnapshot,
31
+ () => 'en' as Locale,
32
+ );
25
33
 
26
34
  const setLocale = (l: Locale) => {
27
- setLocaleState(l);
28
35
  localStorage.setItem('locale', l);
36
+ window.dispatchEvent(new Event('mindos-locale-change'));
29
37
  };
30
38
 
31
39
  return (
package/bin/cli.js CHANGED
@@ -126,6 +126,34 @@ function clearBuildLock() {
126
126
  }
127
127
  }
128
128
 
129
+ function ensureAppDeps() {
130
+ // When installed as a global npm package, app/node_modules may not exist.
131
+ // next (and other deps) must be resolvable from app/ for Turbopack to work.
132
+ const appNext = resolve(ROOT, 'app', 'node_modules', 'next', 'package.json');
133
+ if (!existsSync(appNext)) {
134
+ // Check npm is accessible before trying to run it.
135
+ try {
136
+ execSync('npm --version', { stdio: 'pipe' });
137
+ } catch {
138
+ console.error(red('\n✘ npm not found in PATH.\n'));
139
+ console.error(' MindOS needs npm to install its app dependencies on first run.');
140
+ console.error(' This usually means Node.js is installed via a version manager (nvm, fnm, volta, etc.)');
141
+ console.error(' that only loads in interactive shells, but not in /bin/sh.\n');
142
+ console.error(' Fix: add your Node.js bin directory to a profile that /bin/sh reads (~/.profile).');
143
+ console.error(' Example:');
144
+ console.error(dim(' echo \'export PATH="$HOME/.nvm/versions/node/$(node --version)/bin:$PATH"\' >> ~/.profile'));
145
+ console.error(dim(' source ~/.profile\n'));
146
+ console.error(' Then run `mindos start` again.\n');
147
+ process.exit(1);
148
+ }
149
+ console.log(yellow('Installing app dependencies (first run)...\n'));
150
+ // --no-workspaces: prevent npm from hoisting deps to monorepo root.
151
+ // When globally installed, deps must live in app/node_modules/ so that
152
+ // Turbopack can resolve next/package.json from the app/ project directory.
153
+ run('npm install --prefer-offline --no-workspaces', resolve(ROOT, 'app'));
154
+ }
155
+ }
156
+
129
157
  // ── Port check ────────────────────────────────────────────────────────────────
130
158
 
131
159
  function isPortInUse(port) {
@@ -237,8 +265,20 @@ const systemd = {
237
265
  console.log(green('✔ Service installed and enabled'));
238
266
  },
239
267
 
240
- start() {
268
+ async start() {
241
269
  execSync('systemctl --user start mindos', { stdio: 'inherit' });
270
+ // Wait up to 10s for the service to become active
271
+ const ok = await waitForService(() => {
272
+ try {
273
+ const out = execSync('systemctl --user is-active mindos', { encoding: 'utf-8' }).trim();
274
+ return out === 'active';
275
+ } catch { return false; }
276
+ });
277
+ if (!ok) {
278
+ console.error(red('\n✘ Service failed to start. Last log output:'));
279
+ try { execSync(`journalctl --user -u mindos -n 30 --no-pager`, { stdio: 'inherit' }); } catch {}
280
+ process.exit(1);
281
+ }
242
282
  console.log(green('✔ Service started'));
243
283
  },
244
284
 
@@ -314,8 +354,20 @@ const launchd = {
314
354
  console.log(green('✔ Service installed'));
315
355
  },
316
356
 
317
- start() {
357
+ async start() {
318
358
  execSync(`launchctl kickstart -k gui/${launchctlUid()}/${LAUNCHD_LABEL}`, { stdio: 'inherit' });
359
+ // Wait up to 10s for the service to become active
360
+ const ok = await waitForService(() => {
361
+ try {
362
+ const out = execSync(`launchctl print gui/${launchctlUid()}/${LAUNCHD_LABEL}`, { encoding: 'utf-8' });
363
+ return out.includes('state = running');
364
+ } catch { return false; }
365
+ });
366
+ if (!ok) {
367
+ console.error(red('\n✘ Service failed to start. Last log output:'));
368
+ try { execSync(`tail -n 30 ${LOG_PATH}`, { stdio: 'inherit' }); } catch {}
369
+ process.exit(1);
370
+ }
319
371
  console.log(green('✔ Service started'));
320
372
  },
321
373
 
@@ -352,6 +404,35 @@ const launchd = {
352
404
 
353
405
  // ── gateway dispatcher ────────────────────────────────────────────────────────
354
406
 
407
+ async function waitForService(check, { retries = 10, intervalMs = 1000 } = {}) {
408
+ for (let i = 0; i < retries; i++) {
409
+ if (check()) return true;
410
+ await new Promise(r => setTimeout(r, intervalMs));
411
+ }
412
+ return check();
413
+ }
414
+
415
+ async function waitForHttp(port, { retries = 120, intervalMs = 2000, label = 'service' } = {}) {
416
+ process.stdout.write(cyan(` Waiting for ${label} to be ready`));
417
+ for (let i = 0; i < retries; i++) {
418
+ try {
419
+ const { request } = await import('node:http');
420
+ const ok = await new Promise((resolve) => {
421
+ const req = request({ hostname: '127.0.0.1', port, path: '/', method: 'HEAD', timeout: 1500 },
422
+ (res) => { res.resume(); resolve(res.statusCode < 500); });
423
+ req.on('error', () => resolve(false));
424
+ req.on('timeout', () => { req.destroy(); resolve(false); });
425
+ req.end();
426
+ });
427
+ if (ok) { process.stdout.write(` ${green('✔')}\n`); return true; }
428
+ } catch { /* not ready yet */ }
429
+ process.stdout.write('.');
430
+ await new Promise(r => setTimeout(r, intervalMs));
431
+ }
432
+ process.stdout.write(` ${red('✘')}\n`);
433
+ return false;
434
+ }
435
+
355
436
  async function runGatewayCommand(sub) {
356
437
  const platform = getPlatform();
357
438
  if (!platform) {
@@ -396,7 +477,11 @@ function printStartupInfo(webPort, mcpPort) {
396
477
  console.log(`\n${'─'.repeat(53)}`);
397
478
  console.log(`${bold('🧠 MindOS is starting')}\n`);
398
479
  console.log(` ${green('●')} Web UI ${cyan(`http://localhost:${webPort}`)}`);
399
- console.log(` ${green('●')} MCP ${cyan(`http://localhost:${mcpPort}/mcp`)}\n`);
480
+ if (localIP) console.log(` ${cyan(`http://${localIP}:${webPort}`)}`);
481
+ console.log(` ${green('●')} MCP ${cyan(`http://localhost:${mcpPort}/mcp`)}`);
482
+ if (localIP) console.log(` ${cyan(`http://${localIP}:${mcpPort}/mcp`)}`);
483
+ if (localIP) console.log(dim(`\n 💡 Running on a remote server? Open the Network URL (${localIP}) in your browser,\n or use SSH port forwarding: ssh -L ${webPort}:localhost:${webPort} user@${localIP}`));
484
+ console.log();
400
485
  console.log(bold('Configure MCP in your Agent:'));
401
486
  console.log(dim(' Local (same machine):'));
402
487
  console.log(block('localhost'));
@@ -481,6 +566,7 @@ const commands = {
481
566
  const mcpPort = process.env.MINDOS_MCP_PORT || '8787';
482
567
  await assertPortFree(Number(webPort), 'web');
483
568
  await assertPortFree(Number(mcpPort), 'mcp');
569
+ ensureAppDeps();
484
570
  const mcp = spawnMcp(isVerbose);
485
571
  savePids(process.pid, mcp.pid);
486
572
  process.on('exit', clearPids);
@@ -501,6 +587,14 @@ const commands = {
501
587
  console.log(cyan(`Installing MindOS as a background service (${platform})...`));
502
588
  await runGatewayCommand('install');
503
589
  await runGatewayCommand('start');
590
+ console.log(dim(' (First run may take a few minutes to install dependencies and build the app.)'));
591
+ console.log(dim(' Follow live progress with: mindos logs\n'));
592
+ const ready = await waitForHttp(Number(webPort), { retries: 120, intervalMs: 2000, label: 'Web UI' });
593
+ if (!ready) {
594
+ console.error(red('\n✘ Service started but Web UI did not become ready in time.'));
595
+ console.error(dim(' Check logs with: mindos logs\n'));
596
+ process.exit(1);
597
+ }
504
598
  printStartupInfo(webPort, mcpPort);
505
599
  console.log(`${green('✔ MindOS is running as a background service')}`);
506
600
  console.log(dim(' View logs: mindos logs'));
@@ -514,6 +608,7 @@ const commands = {
514
608
  const mcpPort = process.env.MINDOS_MCP_PORT || '8787';
515
609
  await assertPortFree(Number(webPort), 'web');
516
610
  await assertPortFree(Number(mcpPort), 'mcp');
611
+ ensureAppDeps();
517
612
  if (needsBuild()) {
518
613
  console.log(yellow('Building MindOS (first run or new version detected)...\n'));
519
614
  clearBuildLock();
@@ -529,6 +624,7 @@ const commands = {
529
624
 
530
625
  // ── build ──────────────────────────────────────────────────────────────────
531
626
  build: () => {
627
+ ensureAppDeps();
532
628
  clearBuildLock();
533
629
  run(`npx next build ${extra}`, resolve(ROOT, 'app'));
534
630
  writeBuildStamp();
@@ -634,6 +730,17 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
634
730
  ok(`Node.js ${nodeVersion}`);
635
731
  }
636
732
 
733
+ // 4b. npm reachable from /bin/sh
734
+ try {
735
+ const npmVersion = execSync('npm --version', { stdio: 'pipe' }).toString().trim();
736
+ ok(`npm ${npmVersion} reachable`);
737
+ } catch {
738
+ err('npm not found in PATH — app dependencies cannot be installed');
739
+ console.log(dim(' Node.js may be installed via nvm/fnm/volta and not visible to /bin/sh.'));
740
+ console.log(dim(' Fix: add your Node.js bin path to ~/.profile so non-interactive shells can find it.'));
741
+ hasError = true;
742
+ }
743
+
637
744
  // 5. Build
638
745
  if (!existsSync(resolve(ROOT, 'app', '.next'))) {
639
746
  warn(`App not built yet — will build automatically on next ${dim('mindos start')}`);
@@ -684,7 +791,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
684
791
  },
685
792
 
686
793
  // ── update ─────────────────────────────────────────────────────────────────
687
- update: () => {
794
+ update: async () => {
688
795
  const currentVersion = (() => {
689
796
  try { return JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf-8')).version; } catch { return '?'; }
690
797
  })();
@@ -702,9 +809,35 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
702
809
  })();
703
810
  if (newVersion !== currentVersion) {
704
811
  console.log(`\n${green(`✔ Updated ${currentVersion} → ${newVersion}`)}`);
705
- console.log(dim(' Run `mindos start` — it will rebuild automatically.\n'));
706
812
  } else {
707
813
  console.log(`\n${green('✔ Already on the latest version')} ${dim(`(${currentVersion})`)}\n`);
814
+ return;
815
+ }
816
+
817
+ // If daemon is running, restart it so the new version takes effect immediately
818
+ const platform = getPlatform();
819
+ let daemonRunning = false;
820
+ if (platform === 'systemd') {
821
+ try { execSync('systemctl --user is-active mindos', { stdio: 'pipe' }); daemonRunning = true; } catch {}
822
+ } else if (platform === 'launchd') {
823
+ try { execSync(`launchctl print gui/${launchctlUid()}/com.mindos.app`, { stdio: 'pipe' }); daemonRunning = true; } catch {}
824
+ }
825
+
826
+ if (daemonRunning) {
827
+ console.log(cyan('\n Daemon is running — restarting to apply the new version...'));
828
+ await runGatewayCommand('stop');
829
+ await runGatewayCommand('start');
830
+ const webPort = process.env.MINDOS_WEB_PORT || '3000';
831
+ console.log(dim(' (Waiting for Web UI to come back up...)'));
832
+ const ready = await waitForHttp(Number(webPort), { retries: 120, intervalMs: 2000, label: 'Web UI' });
833
+ if (ready) {
834
+ console.log(green('✔ MindOS restarted and ready.\n'));
835
+ } else {
836
+ console.error(red('✘ MindOS did not come back up in time. Check logs: mindos logs\n'));
837
+ process.exit(1);
838
+ }
839
+ } else {
840
+ console.log(dim(' Run `mindos start` — it will rebuild automatically.\n'));
708
841
  }
709
842
  },
710
843
 
package/mcp/src/index.ts CHANGED
@@ -470,7 +470,8 @@ async function main() {
470
470
  }
471
471
 
472
472
  expressApp.all(MCP_ENDPOINT, async (req, res) => {
473
- await transport.handleRequest(req, res);
473
+ // Pass pre-parsed body: express.json() already parsed it, SDK >= 1.7 expects it as 3rd arg
474
+ await transport.handleRequest(req, res, req.body);
474
475
  });
475
476
 
476
477
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geminilight/mindos",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "MindOS — Human-Agent Collaborative Mind System. Local-first knowledge base that syncs your mind to all AI Agents via MCP.",
5
5
  "keywords": [
6
6
  "mindos",