@geminilight/mindos 0.6.7 → 0.6.12

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.
Files changed (85) hide show
  1. package/README.md +2 -0
  2. package/README_zh.md +2 -0
  3. package/app/app/api/ask/route.ts +35 -2
  4. package/app/app/api/file/route.ts +27 -0
  5. package/app/app/api/mcp/install/route.ts +4 -1
  6. package/app/app/api/setup/check-path/route.ts +2 -7
  7. package/app/app/api/setup/check-port/route.ts +18 -13
  8. package/app/app/api/setup/ls/route.ts +3 -9
  9. package/app/app/api/setup/path-utils.ts +8 -0
  10. package/app/app/api/setup/route.ts +2 -7
  11. package/app/app/api/uninstall/route.ts +47 -0
  12. package/app/app/globals.css +11 -0
  13. package/app/components/ActivityBar.tsx +10 -3
  14. package/app/components/AskFab.tsx +7 -3
  15. package/app/components/CreateSpaceModal.tsx +1 -1
  16. package/app/components/DirView.tsx +1 -1
  17. package/app/components/FileTree.tsx +30 -23
  18. package/app/components/GuideCard.tsx +1 -1
  19. package/app/components/HomeContent.tsx +137 -109
  20. package/app/components/ImportModal.tsx +104 -60
  21. package/app/components/MarkdownView.tsx +3 -0
  22. package/app/components/OnboardingView.tsx +1 -1
  23. package/app/components/OrganizeToast.tsx +386 -0
  24. package/app/components/Panel.tsx +23 -2
  25. package/app/components/Sidebar.tsx +1 -1
  26. package/app/components/SidebarLayout.tsx +44 -1
  27. package/app/components/agents/AgentDetailContent.tsx +33 -12
  28. package/app/components/agents/AgentsMcpSection.tsx +1 -1
  29. package/app/components/agents/AgentsOverviewSection.tsx +3 -4
  30. package/app/components/agents/AgentsPrimitives.tsx +2 -2
  31. package/app/components/agents/AgentsSkillsSection.tsx +2 -2
  32. package/app/components/agents/SkillDetailPopover.tsx +24 -8
  33. package/app/components/ask/AskContent.tsx +124 -70
  34. package/app/components/ask/HighlightMatch.tsx +14 -0
  35. package/app/components/ask/MentionPopover.tsx +5 -3
  36. package/app/components/ask/MessageList.tsx +39 -11
  37. package/app/components/ask/SlashCommandPopover.tsx +4 -2
  38. package/app/components/changes/ChangesBanner.tsx +20 -2
  39. package/app/components/changes/ChangesContentPage.tsx +10 -2
  40. package/app/components/echo/EchoHero.tsx +1 -1
  41. package/app/components/echo/EchoInsightCollapsible.tsx +1 -1
  42. package/app/components/echo/EchoPageSections.tsx +1 -1
  43. package/app/components/explore/UseCaseCard.tsx +1 -1
  44. package/app/components/panels/DiscoverPanel.tsx +29 -25
  45. package/app/components/panels/ImportHistoryPanel.tsx +195 -0
  46. package/app/components/panels/PluginsPanel.tsx +2 -2
  47. package/app/components/settings/AiTab.tsx +24 -0
  48. package/app/components/settings/KnowledgeTab.tsx +1 -1
  49. package/app/components/settings/McpSkillCreateForm.tsx +1 -1
  50. package/app/components/settings/McpSkillRow.tsx +1 -1
  51. package/app/components/settings/McpSkillsSection.tsx +2 -2
  52. package/app/components/settings/McpTab.tsx +2 -2
  53. package/app/components/settings/PluginsTab.tsx +1 -1
  54. package/app/components/settings/Primitives.tsx +118 -6
  55. package/app/components/settings/SettingsContent.tsx +5 -2
  56. package/app/components/settings/UninstallTab.tsx +179 -0
  57. package/app/components/settings/UpdateTab.tsx +17 -5
  58. package/app/components/settings/types.ts +2 -1
  59. package/app/components/ui/dialog.tsx +1 -1
  60. package/app/hooks/useAiOrganize.ts +450 -0
  61. package/app/hooks/useFileImport.ts +39 -2
  62. package/app/hooks/useMention.ts +21 -3
  63. package/app/hooks/useSlashCommand.ts +18 -4
  64. package/app/lib/agent/reconnect.ts +40 -0
  65. package/app/lib/core/backlinks.ts +2 -2
  66. package/app/lib/core/git.ts +14 -10
  67. package/app/lib/fs.ts +2 -1
  68. package/app/lib/i18n-en.ts +85 -4
  69. package/app/lib/i18n-zh.ts +85 -4
  70. package/app/lib/organize-history.ts +74 -0
  71. package/app/lib/settings.ts +2 -0
  72. package/app/lib/types.ts +2 -0
  73. package/app/next-env.d.ts +1 -1
  74. package/app/next.config.ts +23 -5
  75. package/app/package.json +1 -1
  76. package/bin/cli.js +21 -18
  77. package/bin/lib/mcp-build.js +74 -0
  78. package/bin/lib/mcp-spawn.js +8 -5
  79. package/bin/lib/port.js +17 -2
  80. package/bin/lib/stop.js +12 -2
  81. package/mcp/dist/index.cjs +43 -43
  82. package/mcp/src/index.ts +58 -12
  83. package/package.json +1 -1
  84. package/scripts/release.sh +1 -1
  85. package/scripts/setup.js +2 -2
@@ -0,0 +1,74 @@
1
+ import { existsSync, readdirSync, statSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { ROOT } from './constants.js';
4
+ import { yellow } from './colors.js';
5
+ import { run, npmInstall } from './utils.js';
6
+
7
+ export const MCP_DIR = resolve(ROOT, 'mcp');
8
+ export const MCP_SRC_DIR = resolve(MCP_DIR, 'src');
9
+ export const MCP_BUNDLE = resolve(MCP_DIR, 'dist', 'index.cjs');
10
+
11
+ const MCP_PACKAGE_JSON = resolve(MCP_DIR, 'package.json');
12
+ const MCP_PACKAGE_LOCK = resolve(MCP_DIR, 'package-lock.json');
13
+ const MCP_SDK = resolve(MCP_DIR, 'node_modules', '@modelcontextprotocol', 'sdk', 'package.json');
14
+ const MCP_ESBUILD = resolve(MCP_DIR, 'node_modules', 'esbuild', 'package.json');
15
+
16
+ function safeMtime(filePath) {
17
+ try {
18
+ return statSync(filePath).mtimeMs;
19
+ } catch {
20
+ return 0;
21
+ }
22
+ }
23
+
24
+ function latestTreeMtime(dirPath) {
25
+ if (!existsSync(dirPath)) return 0;
26
+
27
+ let latest = safeMtime(dirPath);
28
+ for (const entry of readdirSync(dirPath, { withFileTypes: true })) {
29
+ const fullPath = resolve(dirPath, entry.name);
30
+ if (entry.isDirectory()) {
31
+ latest = Math.max(latest, latestTreeMtime(fullPath));
32
+ } else {
33
+ latest = Math.max(latest, safeMtime(fullPath));
34
+ }
35
+ }
36
+ return latest;
37
+ }
38
+
39
+ function hasBuildDeps() {
40
+ return existsSync(MCP_SDK) && existsSync(MCP_ESBUILD);
41
+ }
42
+
43
+ export function needsMcpBuild() {
44
+ if (!existsSync(MCP_BUNDLE)) return true;
45
+
46
+ const bundleMtime = safeMtime(MCP_BUNDLE);
47
+ const sourceMtime = Math.max(
48
+ latestTreeMtime(MCP_SRC_DIR),
49
+ safeMtime(MCP_PACKAGE_JSON),
50
+ safeMtime(MCP_PACKAGE_LOCK),
51
+ );
52
+
53
+ return sourceMtime > bundleMtime;
54
+ }
55
+
56
+ export function ensureMcpBundle() {
57
+ if (!needsMcpBuild()) return;
58
+
59
+ const hadBundle = existsSync(MCP_BUNDLE);
60
+
61
+ if (!hasBuildDeps()) {
62
+ console.log(yellow('Installing MCP build dependencies...\n'));
63
+ npmInstall(MCP_DIR, '--no-workspaces');
64
+ }
65
+
66
+ console.log(yellow(hadBundle
67
+ ? 'Rebuilding MCP bundle (source changed)...\n'
68
+ : 'Building MCP bundle (first run)...\n'));
69
+ run('npm run build', MCP_DIR);
70
+
71
+ if (!existsSync(MCP_BUNDLE)) {
72
+ throw new Error(`MCP bundle build did not produce ${MCP_BUNDLE}`);
73
+ }
74
+ }
@@ -1,17 +1,20 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { existsSync, readFileSync } from 'node:fs';
2
+ import { readFileSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import { ROOT, CONFIG_PATH } from './constants.js';
5
5
  import { bold, red } from './colors.js';
6
+ import { ensureMcpBundle, MCP_BUNDLE } from './mcp-build.js';
6
7
 
7
8
  export function spawnMcp(verbose = false) {
8
9
  const mcpPort = process.env.MINDOS_MCP_PORT || '8781';
9
10
  const webPort = process.env.MINDOS_WEB_PORT || '3456';
10
11
 
11
- const mcpBundle = resolve(ROOT, 'mcp', 'dist', 'index.cjs');
12
- if (!existsSync(mcpBundle)) {
12
+ try {
13
+ ensureMcpBundle();
14
+ } catch (err) {
15
+ const message = err instanceof Error ? err.message : String(err);
13
16
  throw new Error(
14
- `MCP bundle not found: ${mcpBundle}\n` +
17
+ `${message}\n` +
15
18
  `This MindOS installation may be corrupted. Try: npm install -g @geminilight/mindos@latest`,
16
19
  );
17
20
  }
@@ -31,7 +34,7 @@ export function spawnMcp(verbose = false) {
31
34
  ...(configAuthToken ? { AUTH_TOKEN: configAuthToken } : {}),
32
35
  ...(verbose ? { MCP_VERBOSE: '1' } : {}),
33
36
  };
34
- const child = spawn(process.execPath, [mcpBundle], {
37
+ const child = spawn(process.execPath, [MCP_BUNDLE], {
35
38
  cwd: resolve(ROOT, 'mcp'),
36
39
  stdio: 'inherit',
37
40
  env,
package/bin/lib/port.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createConnection } from 'node:net';
2
- import { bold, dim, red } from './colors.js';
2
+ import { bold, dim, red, yellow } from './colors.js';
3
3
 
4
4
  export function isPortInUse(port) {
5
5
  return new Promise((resolve) => {
@@ -16,10 +16,25 @@ export function isPortInUse(port) {
16
16
  }
17
17
 
18
18
  export async function assertPortFree(port, name) {
19
+ if (!(await isPortInUse(port))) return;
20
+
21
+ // Port is occupied — try to clean up orphaned processes from a previous
22
+ // installation (e.g. user deleted MindOS.app from Finder without quitting,
23
+ // leaving behind zombie Next.js / MCP processes on the default ports).
24
+ console.warn(`${yellow('⚠')} Port ${port} in use (${name}) — attempting cleanup...`);
25
+ try {
26
+ const { stopMindos } = await import('./stop.js');
27
+ stopMindos();
28
+ // Wait briefly for ports to release after SIGTERM
29
+ await new Promise(r => setTimeout(r, 1500));
30
+ } catch { /* stopMindos may fail if no config exists yet — that's fine */ }
31
+
32
+ // Re-check after cleanup
19
33
  if (await isPortInUse(port)) {
20
- console.error(`\n${red('\u2718')} ${bold(`Port ${port} is already in use`)} ${dim(`(${name})`)}`);
34
+ console.error(`\n${red('\u2718')} ${bold(`Port ${port} is still in use after cleanup`)} ${dim(`(${name})`)}`);
21
35
  console.error(`\n ${dim('Stop MindOS:')} mindos stop`);
22
36
  console.error(` ${dim('Find the process:')} lsof -i :${port}\n`);
23
37
  process.exit(1);
24
38
  }
39
+ console.log(` ${dim('Cleaned up orphaned processes on port')} ${port}`);
25
40
  }
package/bin/lib/stop.js CHANGED
@@ -74,6 +74,13 @@ function killTree(pid) {
74
74
  * read from the current config file.
75
75
  */
76
76
  export function stopMindos(opts = {}) {
77
+ // In test environment, skip all real process killing to avoid
78
+ // destroying dev servers or other unrelated processes.
79
+ if (process.env.NODE_ENV === 'test') {
80
+ console.log('(test mode: skipping real process stop)');
81
+ return;
82
+ }
83
+
77
84
  // Read ports from config for port-based cleanup
78
85
  let webPort = '3456', mcpPort = '8781';
79
86
  try {
@@ -110,8 +117,11 @@ export function stopMindos(opts = {}) {
110
117
 
111
118
  if (!pids.length && portKilled === 0) {
112
119
  // Last resort: pattern match (for envs without lsof)
113
- try { execSync('pkill -f "next start|next dev" 2>/dev/null || true', { stdio: ['ignore', 'inherit', 'inherit'] }); } catch {}
114
- try { execSync('pkill -f "mcp/(src/index|dist/index)" 2>/dev/null || true', { stdio: ['ignore', 'inherit', 'inherit'] }); } catch {}
120
+ // Skip in test environment to avoid killing unrelated dev servers
121
+ if (process.env.NODE_ENV !== 'test') {
122
+ try { execSync('pkill -f "next start|next dev" 2>/dev/null || true', { stdio: ['ignore', 'inherit', 'inherit'] }); } catch {}
123
+ try { execSync('pkill -f "mcp/(src/index|dist/index)" 2>/dev/null || true', { stdio: ['ignore', 'inherit', 'inherit'] }); } catch {}
124
+ }
115
125
  }
116
126
 
117
127
  if (!pids.length) console.log(green('\u2714 Done'));