@geminilight/mindos 0.5.43 → 0.5.44

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.
@@ -25,6 +25,9 @@ export const en = {
25
25
  spaceLocation: 'Location',
26
26
  rootLevel: 'Root',
27
27
  optional: 'optional',
28
+ aiInit: 'AI initialize content',
29
+ aiInitHint: 'AI will generate README and INSTRUCTION for this space',
30
+ aiInitNoKey: 'Configure an API key in Settings → AI to enable',
28
31
  createSpace: 'Create',
29
32
  cancelCreate: 'Cancel',
30
33
  continueEditing: 'Continue editing',
@@ -108,6 +111,10 @@ export const en = {
108
111
  'What are the key points?',
109
112
  'Find related notes on this topic',
110
113
  ],
114
+ sessionHistory: 'Session History',
115
+ clearAll: 'Clear all',
116
+ confirmClear: 'Confirm clear?',
117
+ noSessions: 'No saved sessions.',
111
118
  },
112
119
  panels: {
113
120
  agents: {
@@ -161,6 +168,8 @@ export const en = {
161
168
  pluginMarketDesc: 'Extend how files are rendered and edited',
162
169
  skillMarket: 'Skill Market',
163
170
  skillMarketDesc: 'Add new abilities to your AI agents',
171
+ spaceTemplates: 'Space Templates',
172
+ spaceTemplatesDesc: 'Pre-built space structures for common workflows',
164
173
  comingSoon: 'Coming soon',
165
174
  viewAll: 'View all use cases',
166
175
  tryIt: 'Try',
@@ -439,7 +448,9 @@ export const en = {
439
448
  updatingHint: 'This may take 1–3 minutes. Do not close this page.',
440
449
  updated: 'Updated successfully! Reloading...',
441
450
  timeout: 'Update may still be in progress.',
442
- timeoutHint: 'Check your terminal:',
451
+ timeoutHint: 'The server may need more time to rebuild. Try refreshing.',
452
+ refreshButton: 'Refresh Page',
453
+ retryButton: 'Retry Update',
443
454
  error: 'Failed to check for updates. Check your network connection.',
444
455
  checkButton: 'Check for Updates',
445
456
  updateButton: (version: string) => `Update to v${version}`,
@@ -666,12 +677,22 @@ export const en = {
666
677
  subtitle: 'Discover what you can do with MindOS — pick a scenario and try it now.',
667
678
  tryIt: 'Try it',
668
679
  categories: {
669
- 'getting-started': 'Getting Started',
670
- 'cross-agent': 'Cross-Agent',
671
- 'knowledge-evolution': 'Knowledge Evolution',
680
+ 'knowledge-management': 'Knowledge Management',
681
+ 'memory-sync': 'Memory Sync',
682
+ 'auto-execute': 'Auto Execute',
683
+ 'experience-evolution': 'Experience Evolution',
684
+ 'human-insights': 'Human Insights',
685
+ 'audit-control': 'Audit & Control',
686
+ },
687
+ scenarios: {
688
+ 'first-day': 'First Day',
689
+ 'daily': 'Daily Work',
690
+ 'project': 'Project Work',
672
691
  'advanced': 'Advanced',
673
692
  },
674
693
  all: 'All',
694
+ byCapability: 'By Capability',
695
+ byScenario: 'By Scenario',
675
696
  c1: {
676
697
  title: 'Inject Your Identity',
677
698
  desc: 'Tell all AI agents who you are — preferences, tech stack, communication style — in one shot.',
@@ -50,6 +50,9 @@ export const zh = {
50
50
  spaceLocation: '位置',
51
51
  rootLevel: '根目录',
52
52
  optional: '可选',
53
+ aiInit: 'AI 初始化内容',
54
+ aiInitHint: 'AI 将为此空间生成 README 和 INSTRUCTION',
55
+ aiInitNoKey: '在 设置 → AI 中配置 API 密钥以启用',
53
56
  createSpace: '创建',
54
57
  cancelCreate: '取消',
55
58
  continueEditing: '继续编辑',
@@ -133,6 +136,10 @@ export const zh = {
133
136
  '这篇文档的核心要点是什么?',
134
137
  '查找与这个主题相关的笔记',
135
138
  ],
139
+ sessionHistory: '对话历史',
140
+ clearAll: '清除全部',
141
+ confirmClear: '确认清除?',
142
+ noSessions: '暂无历史对话。',
136
143
  },
137
144
  panels: {
138
145
  agents: {
@@ -186,6 +193,8 @@ export const zh = {
186
193
  pluginMarketDesc: '扩展文件的渲染和编辑方式',
187
194
  skillMarket: '技能市场',
188
195
  skillMarketDesc: '为 AI 智能体添加新能力',
196
+ spaceTemplates: '空间模板',
197
+ spaceTemplatesDesc: '预设的空间结构,适用于常见工作流场景',
189
198
  comingSoon: '即将推出',
190
199
  viewAll: '查看所有使用案例',
191
200
  tryIt: '试试',
@@ -464,7 +473,9 @@ export const zh = {
464
473
  updatingHint: '预计 1–3 分钟,请勿关闭此页面。',
465
474
  updated: '更新成功!正在刷新...',
466
475
  timeout: '更新可能仍在进行中。',
467
- timeoutHint: '请在终端查看:',
476
+ timeoutHint: '服务器可能需要更多时间重新构建,请尝试刷新页面。',
477
+ refreshButton: '刷新页面',
478
+ retryButton: '重试更新',
468
479
  error: '检查更新失败,请检查网络连接。',
469
480
  checkButton: '检查更新',
470
481
  updateButton: (version: string) => `更新到 v${version}`,
@@ -691,12 +702,22 @@ export const zh = {
691
702
  subtitle: '发现 MindOS 能帮你做什么 — 选一个场景,立即体验。',
692
703
  tryIt: '试一试',
693
704
  categories: {
694
- 'getting-started': '快速上手',
695
- 'cross-agent': '跨 Agent',
696
- 'knowledge-evolution': '知识演进',
705
+ 'knowledge-management': '知识管理',
706
+ 'memory-sync': '记忆同步',
707
+ 'auto-execute': '自动执行',
708
+ 'experience-evolution': '经验进化',
709
+ 'human-insights': '人类洞察',
710
+ 'audit-control': '审计纠错',
711
+ },
712
+ scenarios: {
713
+ 'first-day': '初次使用',
714
+ 'daily': '日常工作',
715
+ 'project': '项目协作',
697
716
  'advanced': '高级',
698
717
  },
699
718
  all: '全部',
719
+ byCapability: '按能力',
720
+ byScenario: '按场景',
700
721
  c1: {
701
722
  title: '注入身份',
702
723
  desc: '让所有 AI Agent 一次认识你 — 偏好、技术栈、沟通风格。',
package/app/lib/utils.ts CHANGED
@@ -32,3 +32,14 @@ export function relativeTime(mtime: number, labels: {
32
32
  if (days < 7) return labels.daysAgo(days);
33
33
  return new Date(mtime).toLocaleDateString();
34
34
  }
35
+
36
+ /** Extract leading emoji from a string, e.g. "📝 Notes" → "📝" */
37
+ export function extractEmoji(name: string): string {
38
+ const match = name.match(/^[\p{Emoji_Presentation}\p{Extended_Pictographic}]+/u);
39
+ return match?.[0] ?? '';
40
+ }
41
+
42
+ /** Strip leading emoji+space from a string, e.g. "📝 Notes" → "Notes" */
43
+ export function stripEmoji(name: string): string {
44
+ return name.replace(/^[\p{Emoji_Presentation}\p{Extended_Pictographic}\s]+/u, '') || name;
45
+ }
package/bin/cli.js CHANGED
@@ -38,7 +38,7 @@
38
38
  * mindos config validate — validate config file
39
39
  */
40
40
 
41
- import { execSync } from 'node:child_process';
41
+ import { execSync, spawn as nodeSpawn } from 'node:child_process';
42
42
  import { existsSync, readFileSync, writeFileSync, rmSync, cpSync } from 'node:fs';
43
43
  import { resolve } from 'node:path';
44
44
  import { homedir } from 'node:os';
@@ -697,32 +697,41 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
697
697
 
698
698
  // ── update ─────────────────────────────────────────────────────────────────
699
699
  update: async () => {
700
+ const { writeUpdateStatus, writeUpdateFailed, clearUpdateStatus } = await import('./lib/update-status.js');
700
701
  const currentVersion = (() => {
701
702
  try { return JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf-8')).version; } catch { return '?'; }
702
703
  })();
703
704
  console.log(`\n${bold('⬆ Updating MindOS...')} ${dim(`(current: ${currentVersion})`)}\n`);
705
+
706
+ // Stage 1: Download
707
+ writeUpdateStatus('downloading', { fromVersion: currentVersion });
704
708
  try {
705
709
  execSync('npm install -g @geminilight/mindos@latest', { stdio: 'inherit' });
706
710
  } catch {
711
+ writeUpdateFailed('downloading', 'npm install failed', { fromVersion: currentVersion });
707
712
  console.error(red('Update failed. Try: npm install -g @geminilight/mindos@latest'));
708
713
  process.exit(1);
709
714
  }
710
715
  if (existsSync(BUILD_STAMP)) rmSync(BUILD_STAMP);
711
716
 
712
- // Silently update installed skills to match the new package
717
+ // Resolve the new installation path (after npm install -g, ROOT is stale)
718
+ const updatedRoot = getUpdatedRoot();
719
+ const newVersion = (() => {
720
+ try { return JSON.parse(readFileSync(resolve(updatedRoot, 'package.json'), 'utf-8')).version; } catch { return '?'; }
721
+ })();
722
+ const vOpts = { fromVersion: currentVersion, toVersion: newVersion };
723
+
724
+ // Stage 2: Skills
725
+ writeUpdateStatus('skills', vOpts);
713
726
  try {
714
- const newRoot = getUpdatedRoot();
715
727
  const { checkSkillVersions, updateSkill } = await import('./lib/skill-check.js');
716
- const mismatches = checkSkillVersions(newRoot);
728
+ const mismatches = checkSkillVersions(updatedRoot);
717
729
  for (const m of mismatches) {
718
730
  updateSkill(m.bundledPath, m.installPath);
719
731
  console.log(` ${green('✓')} ${dim(`Skill ${m.name}: v${m.installed} → v${m.bundled}`)}`);
720
732
  }
721
733
  } catch { /* best-effort */ }
722
734
 
723
- const newVersion = (() => {
724
- try { return JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf-8')).version; } catch { return '?'; }
725
- })();
726
735
  if (newVersion !== currentVersion) {
727
736
  console.log(`\n${green(`✔ Updated ${currentVersion} → ${newVersion}`)}`);
728
737
  } else {
@@ -746,10 +755,12 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
746
755
  console.log(cyan('\n Daemon is running — stopping to apply the new version...'));
747
756
  await runGatewayCommand('stop');
748
757
 
749
- // After npm install -g, resolve the new installation path and pre-build
750
- const newRoot = getUpdatedRoot();
751
- buildIfNeeded(newRoot);
758
+ // Stage 3: Rebuild
759
+ writeUpdateStatus('rebuilding', vOpts);
760
+ buildIfNeeded(updatedRoot);
752
761
 
762
+ // Stage 4: Restart
763
+ writeUpdateStatus('restarting', vOpts);
753
764
  await runGatewayCommand('install');
754
765
  // install() starts the service:
755
766
  // - systemd: daemon-reload + enable + start
@@ -772,17 +783,78 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
772
783
  console.log(` ${green('●')} MCP ${cyan(`http://localhost:${mcpPort}/mcp`)}`);
773
784
  console.log(`\n ${dim('View changelog:')} ${cyan('https://github.com/GeminiLight/MindOS/releases')}`);
774
785
  console.log(`${'─'.repeat(53)}\n`);
786
+ writeUpdateStatus('done', vOpts);
775
787
  } else {
788
+ writeUpdateFailed('restarting', 'Server did not come back up in time', vOpts);
776
789
  console.error(red('✘ MindOS did not come back up in time. Check logs: mindos logs\n'));
777
790
  process.exit(1);
778
791
  }
779
792
  } else {
780
- // Non-daemon mode: build in foreground for better UX
781
- buildIfNeeded(getUpdatedRoot());
793
+ // Non-daemon mode: check if a MindOS instance is currently running
794
+ // (e.g. user started via `mindos start`, or GUI triggered this update).
795
+ // If so, stop it and restart from the NEW installation path.
796
+ const updateConfig = (() => {
797
+ try { return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch { return {}; }
798
+ })();
799
+ const webPort = Number(updateConfig.port ?? 3456);
800
+ const mcpPort = Number(updateConfig.mcpPort ?? 8781);
801
+
802
+ const wasRunning = await isPortInUse(webPort) || await isPortInUse(mcpPort);
803
+
804
+ if (wasRunning) {
805
+ console.log(cyan('\n MindOS is running — restarting to apply the new version...'));
806
+ stopMindos();
807
+ // Wait for ports to free (up to 15s)
808
+ const deadline = Date.now() + 15_000;
809
+ while (Date.now() < deadline) {
810
+ const busy = await isPortInUse(webPort) || await isPortInUse(mcpPort);
811
+ if (!busy) break;
812
+ await new Promise((r) => setTimeout(r, 500));
813
+ }
782
814
 
783
- console.log(`\n${green('✔')} ${bold(`Updated: ${currentVersion} → ${newVersion}`)}`);
784
- console.log(dim(' Run `mindos start` to start the updated version.'));
785
- console.log(` ${dim('View changelog:')} ${cyan('https://github.com/GeminiLight/MindOS/releases')}\n`);
815
+ // Stage 3: Rebuild
816
+ writeUpdateStatus('rebuilding', vOpts);
817
+ buildIfNeeded(updatedRoot);
818
+
819
+ // Stage 4: Restart
820
+ writeUpdateStatus('restarting', vOpts);
821
+ const newCliPath = resolve(updatedRoot, 'bin', 'cli.js');
822
+ const childEnv = { ...process.env };
823
+ delete childEnv.MINDOS_WEB_PORT;
824
+ delete childEnv.MINDOS_MCP_PORT;
825
+ delete childEnv.MIND_ROOT;
826
+ delete childEnv.AUTH_TOKEN;
827
+ delete childEnv.WEB_PASSWORD;
828
+ const child = nodeSpawn(
829
+ process.execPath, [newCliPath, 'start'],
830
+ { detached: true, stdio: 'ignore', env: childEnv },
831
+ );
832
+ child.unref();
833
+
834
+ console.log(dim(' (Waiting for Web UI to come back up...)'));
835
+ const ready = await waitForHttp(webPort, { retries: 120, intervalMs: 2000, label: 'Web UI', logFile: LOG_PATH });
836
+ if (ready) {
837
+ const localIP = getLocalIP();
838
+ console.log(`\n${'─'.repeat(53)}`);
839
+ console.log(`${green('✔')} ${bold(`MindOS updated: ${currentVersion} → ${newVersion}`)}\n`);
840
+ console.log(` ${green('●')} Web UI ${cyan(`http://localhost:${webPort}`)}`);
841
+ if (localIP) console.log(` ${cyan(`http://${localIP}:${webPort}`)}`);
842
+ console.log(` ${green('●')} MCP ${cyan(`http://localhost:${mcpPort}/mcp`)}`);
843
+ console.log(`\n ${dim('View changelog:')} ${cyan('https://github.com/GeminiLight/MindOS/releases')}`);
844
+ console.log(`${'─'.repeat(53)}\n`);
845
+ writeUpdateStatus('done', vOpts);
846
+ } else {
847
+ writeUpdateFailed('restarting', 'Server did not come back up in time', vOpts);
848
+ console.error(red('✘ MindOS did not come back up in time. Check logs: mindos logs\n'));
849
+ process.exit(1);
850
+ }
851
+ } else {
852
+ // No running instance — just build and tell user to start manually
853
+ buildIfNeeded(updatedRoot);
854
+ console.log(`\n${green('✔')} ${bold(`Updated: ${currentVersion} → ${newVersion}`)}`);
855
+ console.log(dim(' Run `mindos start` to start the updated version.'));
856
+ console.log(` ${dim('View changelog:')} ${cyan('https://github.com/GeminiLight/MindOS/releases')}\n`);
857
+ }
786
858
  }
787
859
  },
788
860
 
@@ -5,6 +5,7 @@ import { bold, dim, cyan, green, yellow } from './colors.js';
5
5
  import { getSyncStatus } from './sync.js';
6
6
  import { checkForUpdate, printUpdateHint } from './update-check.js';
7
7
  import { runSkillCheck } from './skill-check.js';
8
+ import { clearUpdateStatus } from './update-status.js';
8
9
 
9
10
  export function getLocalIP() {
10
11
  try {
@@ -18,6 +19,9 @@ export function getLocalIP() {
18
19
  }
19
20
 
20
21
  export async function printStartupInfo(webPort, mcpPort) {
22
+ // Clear stale update status from previous update cycles
23
+ clearUpdateStatus();
24
+
21
25
  // Fire update check immediately (non-blocking)
22
26
  const updatePromise = checkForUpdate().catch(() => null);
23
27
 
@@ -0,0 +1,115 @@
1
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { MINDOS_DIR } from './constants.js';
4
+
5
+ const STATUS_PATH = resolve(MINDOS_DIR, 'update-status.json');
6
+
7
+ const STAGE_ORDER = ['downloading', 'skills', 'rebuilding', 'restarting'];
8
+
9
+ /**
10
+ * Write update progress to ~/.mindos/update-status.json.
11
+ *
12
+ * @param {'downloading'|'skills'|'rebuilding'|'restarting'|'done'|'failed'} stage
13
+ * @param {{ error?: string, fromVersion?: string, toVersion?: string }} [opts]
14
+ */
15
+ export function writeUpdateStatus(stage, opts = {}) {
16
+ const stages = STAGE_ORDER.map((id) => {
17
+ const idx = STAGE_ORDER.indexOf(id);
18
+ const currentIdx = STAGE_ORDER.indexOf(stage);
19
+ let status = 'pending';
20
+ if (stage === 'done' || stage === 'failed') {
21
+ // done: all stages done; failed: stages up to current are done, current is failed
22
+ if (stage === 'done') {
23
+ status = 'done';
24
+ } else {
25
+ // For 'failed', we don't know which stage failed from just 'failed'
26
+ // So we keep whatever was last written — this function is called per-stage
27
+ status = 'pending';
28
+ }
29
+ } else if (idx < currentIdx) {
30
+ status = 'done';
31
+ } else if (idx === currentIdx) {
32
+ status = 'running';
33
+ }
34
+ return { id, status };
35
+ });
36
+
37
+ // For 'done', mark all as done
38
+ if (stage === 'done') {
39
+ stages.forEach((s) => { s.status = 'done'; });
40
+ }
41
+
42
+ const data = {
43
+ stage,
44
+ stages,
45
+ error: opts.error || null,
46
+ version: {
47
+ from: opts.fromVersion || null,
48
+ to: opts.toVersion || null,
49
+ },
50
+ startedAt: stage === 'downloading'
51
+ ? new Date().toISOString()
52
+ : readCurrentStartedAt(),
53
+ };
54
+
55
+ try {
56
+ writeFileSync(STATUS_PATH, JSON.stringify(data, null, 2), 'utf-8');
57
+ } catch { /* best-effort — directory may not exist yet */ }
58
+ }
59
+
60
+ /**
61
+ * Mark a specific stage as failed. Preserves progress of prior stages.
62
+ */
63
+ export function writeUpdateFailed(failedStage, errorMessage, opts = {}) {
64
+ const stages = STAGE_ORDER.map((id) => {
65
+ const idx = STAGE_ORDER.indexOf(id);
66
+ const failedIdx = STAGE_ORDER.indexOf(failedStage);
67
+ if (idx < failedIdx) return { id, status: 'done' };
68
+ if (idx === failedIdx) return { id, status: 'failed' };
69
+ return { id, status: 'pending' };
70
+ });
71
+
72
+ const data = {
73
+ stage: 'failed',
74
+ stages,
75
+ error: errorMessage,
76
+ version: {
77
+ from: opts.fromVersion || null,
78
+ to: opts.toVersion || null,
79
+ },
80
+ startedAt: readCurrentStartedAt(),
81
+ };
82
+
83
+ try {
84
+ writeFileSync(STATUS_PATH, JSON.stringify(data, null, 2), 'utf-8');
85
+ } catch { /* best-effort */ }
86
+ }
87
+
88
+ /**
89
+ * Read the current update status. Returns null if no status file.
90
+ */
91
+ export function readUpdateStatus() {
92
+ try {
93
+ return JSON.parse(readFileSync(STATUS_PATH, 'utf-8'));
94
+ } catch {
95
+ return null;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Remove the status file (called on startup to clear stale state).
101
+ */
102
+ export function clearUpdateStatus() {
103
+ try {
104
+ if (existsSync(STATUS_PATH)) unlinkSync(STATUS_PATH);
105
+ } catch { /* best-effort */ }
106
+ }
107
+
108
+ function readCurrentStartedAt() {
109
+ try {
110
+ const existing = JSON.parse(readFileSync(STATUS_PATH, 'utf-8'));
111
+ return existing.startedAt || new Date().toISOString();
112
+ } catch {
113
+ return new Date().toISOString();
114
+ }
115
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geminilight/mindos",
3
- "version": "0.5.43",
3
+ "version": "0.5.44",
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",
@@ -16,9 +16,11 @@ const { join } = require('path');
16
16
  const { execSync } = require('child_process');
17
17
 
18
18
  const postcssDir = join('node_modules', 'next', 'node_modules', 'postcss');
19
- const depsDir = join(postcssDir, 'node_modules');
19
+ // Check for an actual dependency, not just the node_modules directory
20
+ // (npm sometimes leaves an empty node_modules with only .bin and .package-lock.json)
21
+ const marker = join(postcssDir, 'node_modules', 'source-map-js');
20
22
 
21
- if (existsSync(postcssDir) && !existsSync(depsDir)) {
23
+ if (existsSync(postcssDir) && !existsSync(marker)) {
22
24
  try {
23
25
  execSync('npm install --no-save --install-strategy=nested', {
24
26
  cwd: postcssDir,