@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.
- package/README.md +2 -0
- package/README_zh.md +2 -0
- package/app/app/api/ask/route.ts +35 -2
- package/app/app/api/file/route.ts +27 -0
- package/app/app/api/mcp/install/route.ts +4 -1
- package/app/app/api/setup/check-path/route.ts +2 -7
- package/app/app/api/setup/check-port/route.ts +18 -13
- package/app/app/api/setup/ls/route.ts +3 -9
- package/app/app/api/setup/path-utils.ts +8 -0
- package/app/app/api/setup/route.ts +2 -7
- package/app/app/api/uninstall/route.ts +47 -0
- package/app/app/globals.css +11 -0
- package/app/components/ActivityBar.tsx +10 -3
- package/app/components/AskFab.tsx +7 -3
- package/app/components/CreateSpaceModal.tsx +1 -1
- package/app/components/DirView.tsx +1 -1
- package/app/components/FileTree.tsx +30 -23
- package/app/components/GuideCard.tsx +1 -1
- package/app/components/HomeContent.tsx +137 -109
- package/app/components/ImportModal.tsx +104 -60
- package/app/components/MarkdownView.tsx +3 -0
- package/app/components/OnboardingView.tsx +1 -1
- package/app/components/OrganizeToast.tsx +386 -0
- package/app/components/Panel.tsx +23 -2
- package/app/components/Sidebar.tsx +1 -1
- package/app/components/SidebarLayout.tsx +44 -1
- package/app/components/agents/AgentDetailContent.tsx +33 -12
- package/app/components/agents/AgentsMcpSection.tsx +1 -1
- package/app/components/agents/AgentsOverviewSection.tsx +3 -4
- package/app/components/agents/AgentsPrimitives.tsx +2 -2
- package/app/components/agents/AgentsSkillsSection.tsx +2 -2
- package/app/components/agents/SkillDetailPopover.tsx +24 -8
- package/app/components/ask/AskContent.tsx +124 -70
- package/app/components/ask/HighlightMatch.tsx +14 -0
- package/app/components/ask/MentionPopover.tsx +5 -3
- package/app/components/ask/MessageList.tsx +39 -11
- package/app/components/ask/SlashCommandPopover.tsx +4 -2
- package/app/components/changes/ChangesBanner.tsx +20 -2
- package/app/components/changes/ChangesContentPage.tsx +10 -2
- package/app/components/echo/EchoHero.tsx +1 -1
- package/app/components/echo/EchoInsightCollapsible.tsx +1 -1
- package/app/components/echo/EchoPageSections.tsx +1 -1
- package/app/components/explore/UseCaseCard.tsx +1 -1
- package/app/components/panels/DiscoverPanel.tsx +29 -25
- package/app/components/panels/ImportHistoryPanel.tsx +195 -0
- package/app/components/panels/PluginsPanel.tsx +2 -2
- package/app/components/settings/AiTab.tsx +24 -0
- package/app/components/settings/KnowledgeTab.tsx +1 -1
- package/app/components/settings/McpSkillCreateForm.tsx +1 -1
- package/app/components/settings/McpSkillRow.tsx +1 -1
- package/app/components/settings/McpSkillsSection.tsx +2 -2
- package/app/components/settings/McpTab.tsx +2 -2
- package/app/components/settings/PluginsTab.tsx +1 -1
- package/app/components/settings/Primitives.tsx +118 -6
- package/app/components/settings/SettingsContent.tsx +5 -2
- package/app/components/settings/UninstallTab.tsx +179 -0
- package/app/components/settings/UpdateTab.tsx +17 -5
- package/app/components/settings/types.ts +2 -1
- package/app/components/ui/dialog.tsx +1 -1
- package/app/hooks/useAiOrganize.ts +450 -0
- package/app/hooks/useFileImport.ts +39 -2
- package/app/hooks/useMention.ts +21 -3
- package/app/hooks/useSlashCommand.ts +18 -4
- package/app/lib/agent/reconnect.ts +40 -0
- package/app/lib/core/backlinks.ts +2 -2
- package/app/lib/core/git.ts +14 -10
- package/app/lib/fs.ts +2 -1
- package/app/lib/i18n-en.ts +85 -4
- package/app/lib/i18n-zh.ts +85 -4
- package/app/lib/organize-history.ts +74 -0
- package/app/lib/settings.ts +2 -0
- package/app/lib/types.ts +2 -0
- package/app/next-env.d.ts +1 -1
- package/app/next.config.ts +23 -5
- package/app/package.json +1 -1
- package/bin/cli.js +21 -18
- package/bin/lib/mcp-build.js +74 -0
- package/bin/lib/mcp-spawn.js +8 -5
- package/bin/lib/port.js +17 -2
- package/bin/lib/stop.js +12 -2
- package/mcp/dist/index.cjs +43 -43
- package/mcp/src/index.ts +58 -12
- package/package.json +1 -1
- package/scripts/release.sh +1 -1
- package/scripts/setup.js +2 -2
package/app/lib/fs.ts
CHANGED
|
@@ -585,6 +585,7 @@ export type { MindSpaceSummary } from './core';
|
|
|
585
585
|
export type { ContentChangeEvent, ContentChangeInput, ContentChangeSummary, ContentChangeSource } from './core';
|
|
586
586
|
|
|
587
587
|
export function findBacklinks(targetPath: string): BacklinkEntry[] {
|
|
588
|
-
|
|
588
|
+
const { allFiles } = ensureCache();
|
|
589
|
+
return coreFindBacklinks(getMindRoot(), targetPath, allFiles);
|
|
589
590
|
}
|
|
590
591
|
|
package/app/lib/i18n-en.ts
CHANGED
|
@@ -72,12 +72,15 @@ export const en = {
|
|
|
72
72
|
agents: 'Agents',
|
|
73
73
|
echo: 'Echo',
|
|
74
74
|
discover: 'Discover',
|
|
75
|
+
history: 'History',
|
|
75
76
|
help: 'Help',
|
|
76
77
|
syncLabel: 'Sync',
|
|
77
78
|
collapseTitle: 'Collapse sidebar',
|
|
78
79
|
expandTitle: 'Expand sidebar',
|
|
79
80
|
collapseLevel: 'Collapse one level',
|
|
80
81
|
expandLevel: 'Expand one level',
|
|
82
|
+
importFile: 'Import file',
|
|
83
|
+
newFile: 'New file',
|
|
81
84
|
sync: {
|
|
82
85
|
synced: 'Synced',
|
|
83
86
|
unpushed: 'awaiting push',
|
|
@@ -105,6 +108,7 @@ export const en = {
|
|
|
105
108
|
},
|
|
106
109
|
ask: {
|
|
107
110
|
title: 'MindOS Agent',
|
|
111
|
+
fabLabel: 'Ask AI',
|
|
108
112
|
placeholder: 'Ask a question... @ files, / skills',
|
|
109
113
|
emptyPrompt: 'Ask anything about your knowledge base',
|
|
110
114
|
send: 'send',
|
|
@@ -118,13 +122,17 @@ export const en = {
|
|
|
118
122
|
skillsHint: 'skills',
|
|
119
123
|
attachCurrent: 'attach current file',
|
|
120
124
|
stopTitle: 'Stop',
|
|
121
|
-
|
|
125
|
+
cancelReconnect: 'Cancel reconnect',
|
|
126
|
+
connecting: 'Thinking with you...',
|
|
122
127
|
thinking: 'Thinking...',
|
|
123
128
|
thinkingLabel: 'Thinking',
|
|
124
129
|
searching: 'Searching knowledge base...',
|
|
125
130
|
generating: 'Generating response...',
|
|
126
131
|
stopped: 'Generation stopped.',
|
|
127
132
|
errorNoResponse: 'No response from AI. Please check your API key and provider settings.',
|
|
133
|
+
reconnecting: (attempt: number, max: number) => `Connection lost. Reconnecting (${attempt}/${max})...`,
|
|
134
|
+
reconnectFailed: 'Connection failed after multiple attempts.',
|
|
135
|
+
retry: 'Retry',
|
|
128
136
|
suggestions: [
|
|
129
137
|
'Summarize this document',
|
|
130
138
|
'List all action items and TODOs',
|
|
@@ -637,6 +645,8 @@ export const en = {
|
|
|
637
645
|
},
|
|
638
646
|
detailSubtitle: '',
|
|
639
647
|
detailNotFound: 'Agent not found — it may have been removed or renamed.',
|
|
648
|
+
detailNotFoundHint: 'The agent may have disconnected or its configuration file was moved. Try restarting the agent or check the MCP configuration.',
|
|
649
|
+
detailNotFoundSuggestion: 'Connected agents you can explore:',
|
|
640
650
|
},
|
|
641
651
|
shortcutPanel: {
|
|
642
652
|
title: 'Keyboard Shortcuts',
|
|
@@ -718,10 +728,56 @@ export const en = {
|
|
|
718
728
|
dropOverlay: 'Drop files to import into knowledge base',
|
|
719
729
|
dropOverlayFormats: 'Supports .md .txt .pdf .csv .json .yaml .html',
|
|
720
730
|
onboardingHint: 'Already have notes? Import files →',
|
|
721
|
-
digestPromptSingle: (name: string) =>
|
|
722
|
-
|
|
731
|
+
digestPromptSingle: (name: string, targetSpace?: string) => {
|
|
732
|
+
const loc = targetSpace ? ` under the "${targetSpace}" space` : '';
|
|
733
|
+
return `The user uploaded "${name}". You MUST:\n1. Read the content from the "USER-UPLOADED FILES" section above\n2. Extract and reorganize the key information into well-structured Markdown notes\n3. Save the result${loc} in the knowledge base — create new files or update existing ones as appropriate\n\nDo NOT just reply with a text summary. You must actually write to the knowledge base.`;
|
|
734
|
+
},
|
|
735
|
+
digestPromptMulti: (n: number, targetSpace?: string) => {
|
|
736
|
+
const loc = targetSpace ? ` under the "${targetSpace}" space` : '';
|
|
737
|
+
return `The user uploaded ${n} files. You MUST:\n1. Read their content from the "USER-UPLOADED FILES" section above\n2. Extract and reorganize the key information into well-structured Markdown notes\n3. Save the results${loc} in the knowledge base — create new files or update existing ones as appropriate\n\nDo NOT just reply with a text summary. You must actually write to the knowledge base.`;
|
|
738
|
+
},
|
|
723
739
|
arrowTo: '→',
|
|
724
740
|
remove: 'Remove',
|
|
741
|
+
conflictsFound: (n: number) => `${n} file${n !== 1 ? 's' : ''} already exist${n === 1 ? 's' : ''}`,
|
|
742
|
+
organizeTitle: 'AI Organizing',
|
|
743
|
+
organizeProcessing: 'AI is analyzing and organizing your files...',
|
|
744
|
+
organizeConnecting: 'Connecting to AI...',
|
|
745
|
+
organizeAnalyzing: 'AI is analyzing your files...',
|
|
746
|
+
organizeReading: (detail?: string) => detail ? `Reading ${detail}...` : 'Reading files...',
|
|
747
|
+
organizeThinking: 'AI is thinking deeply...',
|
|
748
|
+
organizeWriting: (detail?: string) => detail ? `Writing ${detail}...` : 'Writing files...',
|
|
749
|
+
organizeElapsed: (seconds: number) => {
|
|
750
|
+
const m = Math.floor(seconds / 60);
|
|
751
|
+
const s = seconds % 60;
|
|
752
|
+
return `${m}:${s.toString().padStart(2, '0')}`;
|
|
753
|
+
},
|
|
754
|
+
organizeCancel: 'Cancel',
|
|
755
|
+
organizeMinimize: 'Continue browsing',
|
|
756
|
+
organizeExpand: 'View',
|
|
757
|
+
organizeCreating: (path: string) => `Creating ${path}`,
|
|
758
|
+
organizeUpdating: (path: string) => `Updating ${path}`,
|
|
759
|
+
organizeReviewTitle: 'Organization Complete',
|
|
760
|
+
organizeErrorTitle: 'Organization Failed',
|
|
761
|
+
organizeReviewDesc: (n: number) => `AI organized your files into ${n} change${n !== 1 ? 's' : ''}`,
|
|
762
|
+
organizeCreated: 'Created',
|
|
763
|
+
organizeUpdated: 'Updated',
|
|
764
|
+
organizeFailed: 'Failed',
|
|
765
|
+
organizeNoChanges: 'AI analyzed your files but made no changes.',
|
|
766
|
+
organizeToolCallsInfo: (n: number) => `AI executed ${n} operation${n > 1 ? 's' : ''} — check knowledge base for updates`,
|
|
767
|
+
organizeError: 'Organization failed',
|
|
768
|
+
organizeRetry: 'Retry',
|
|
769
|
+
organizeDone: 'Done',
|
|
770
|
+
organizeUndoAll: 'Undo All',
|
|
771
|
+
organizeUndoOne: 'Undo',
|
|
772
|
+
organizeUndone: 'Undone',
|
|
773
|
+
organizeViewFile: 'View file',
|
|
774
|
+
organizeUndoSuccess: (n: number) => `Reverted ${n} file${n !== 1 ? 's' : ''}`,
|
|
775
|
+
},
|
|
776
|
+
importHistory: {
|
|
777
|
+
title: 'Import History',
|
|
778
|
+
clearAll: 'Clear history',
|
|
779
|
+
emptyTitle: 'No import history yet',
|
|
780
|
+
emptyDesc: 'AI organize results will appear here',
|
|
725
781
|
},
|
|
726
782
|
dirView: {
|
|
727
783
|
gridView: 'Grid view',
|
|
@@ -741,7 +797,7 @@ export const en = {
|
|
|
741
797
|
},
|
|
742
798
|
settings: {
|
|
743
799
|
title: 'Settings',
|
|
744
|
-
tabs: { ai: 'AI', appearance: 'Appearance', knowledge: 'General', sync: 'Sync', mcp: 'MCP & Skills', plugins: 'Plugins', shortcuts: 'Shortcuts', monitoring: 'Monitoring', agents: 'Agents', update: 'Update' },
|
|
800
|
+
tabs: { ai: 'AI', appearance: 'Appearance', knowledge: 'General', sync: 'Sync', mcp: 'MCP & Skills', plugins: 'Plugins', shortcuts: 'Shortcuts', monitoring: 'Monitoring', agents: 'Agents', update: 'Update', uninstall: 'Uninstall' },
|
|
745
801
|
ai: {
|
|
746
802
|
provider: 'Provider',
|
|
747
803
|
model: 'Model',
|
|
@@ -776,6 +832,8 @@ export const en = {
|
|
|
776
832
|
thinkingHint: "Show Claude's reasoning process (uses more tokens)",
|
|
777
833
|
thinkingBudget: 'Thinking Budget',
|
|
778
834
|
thinkingBudgetHint: 'Max tokens for reasoning (1000-50000)',
|
|
835
|
+
reconnectRetries: 'Auto Reconnect',
|
|
836
|
+
reconnectRetriesHint: 'When connection drops, automatically retry this many times before giving up (0 = disabled)',
|
|
779
837
|
},
|
|
780
838
|
appearance: {
|
|
781
839
|
readingFont: 'Reading font',
|
|
@@ -1000,6 +1058,29 @@ export const en = {
|
|
|
1000
1058
|
desktopRestart: 'Restart Now',
|
|
1001
1059
|
desktopHint: 'Updates are delivered through the Desktop app auto-updater.',
|
|
1002
1060
|
},
|
|
1061
|
+
uninstall: {
|
|
1062
|
+
title: 'Uninstall MindOS',
|
|
1063
|
+
descCli: 'Remove MindOS CLI, background services, and configuration files from this machine.',
|
|
1064
|
+
descDesktop: 'Remove MindOS Desktop, background services, and configuration files from this machine.',
|
|
1065
|
+
warning: 'Select what to clean up. Your knowledge base files are always kept safe.',
|
|
1066
|
+
stopServices: 'Stop services & remove daemon',
|
|
1067
|
+
stopServicesDesc: 'Stop all running MindOS processes and remove the background daemon.',
|
|
1068
|
+
removeConfig: 'Remove configuration',
|
|
1069
|
+
removeConfigDesc: 'Delete ~/.mindos/ directory (config, logs, PID files).',
|
|
1070
|
+
removeNpm: 'Uninstall CLI package',
|
|
1071
|
+
removeNpmDesc: 'Run npm uninstall -g @geminilight/mindos.',
|
|
1072
|
+
removeApp: 'Move Desktop app to Trash',
|
|
1073
|
+
removeAppDesc: 'Move MindOS.app to Trash. You can restore it later if needed.',
|
|
1074
|
+
confirmTitle: 'Confirm Uninstall',
|
|
1075
|
+
confirmButton: 'Uninstall',
|
|
1076
|
+
cancelButton: 'Cancel',
|
|
1077
|
+
running: 'Uninstalling...',
|
|
1078
|
+
success: 'MindOS has been uninstalled.',
|
|
1079
|
+
successDesktop: 'MindOS has been uninstalled. The app will quit now.',
|
|
1080
|
+
error: 'Uninstall failed. You can run `mindos uninstall` in terminal manually.',
|
|
1081
|
+
nothingSelected: 'Select at least one item to uninstall.',
|
|
1082
|
+
kbSafe: 'Your knowledge base files are always safe — they are never deleted by this action.',
|
|
1083
|
+
},
|
|
1003
1084
|
},
|
|
1004
1085
|
onboarding: {
|
|
1005
1086
|
subtitle: 'Your knowledge base is empty. Pick a starter template to get going.',
|
package/app/lib/i18n-zh.ts
CHANGED
|
@@ -97,12 +97,15 @@ export const zh = {
|
|
|
97
97
|
agents: '智能体',
|
|
98
98
|
echo: '回响',
|
|
99
99
|
discover: '探索',
|
|
100
|
+
history: '历史',
|
|
100
101
|
help: '帮助',
|
|
101
102
|
syncLabel: '同步',
|
|
102
103
|
collapseTitle: '收起侧栏',
|
|
103
104
|
expandTitle: '展开侧栏',
|
|
104
105
|
collapseLevel: '折叠一级',
|
|
105
106
|
expandLevel: '展开一级',
|
|
107
|
+
importFile: '导入文件',
|
|
108
|
+
newFile: '新建文件',
|
|
106
109
|
sync: {
|
|
107
110
|
synced: '已同步',
|
|
108
111
|
unpushed: '待推送',
|
|
@@ -130,6 +133,7 @@ export const zh = {
|
|
|
130
133
|
},
|
|
131
134
|
ask: {
|
|
132
135
|
title: 'MindOS Agent',
|
|
136
|
+
fabLabel: 'AI 助手',
|
|
133
137
|
placeholder: '输入问题… @ 附加文件,/ 技能',
|
|
134
138
|
emptyPrompt: '可以问任何关于知识库的问题',
|
|
135
139
|
send: '发送',
|
|
@@ -143,13 +147,17 @@ export const zh = {
|
|
|
143
147
|
skillsHint: '技能',
|
|
144
148
|
attachCurrent: '附加当前文件',
|
|
145
149
|
stopTitle: '停止',
|
|
146
|
-
|
|
150
|
+
cancelReconnect: '取消重连',
|
|
151
|
+
connecting: '正在和你一起思考...',
|
|
147
152
|
thinking: '思考中...',
|
|
148
153
|
thinkingLabel: '思考中',
|
|
149
154
|
searching: '正在搜索知识库...',
|
|
150
155
|
generating: '正在生成回复...',
|
|
151
156
|
stopped: '已停止生成。',
|
|
152
157
|
errorNoResponse: 'AI 未返回响应,请检查 API Key 和服务商设置。',
|
|
158
|
+
reconnecting: (attempt: number, max: number) => `连接中断,正在重连 (${attempt}/${max})...`,
|
|
159
|
+
reconnectFailed: '多次重连失败,请检查网络后重试。',
|
|
160
|
+
retry: '重试',
|
|
153
161
|
suggestions: [
|
|
154
162
|
'总结这篇文档',
|
|
155
163
|
'列出所有待办事项',
|
|
@@ -661,6 +669,8 @@ export const zh = {
|
|
|
661
669
|
},
|
|
662
670
|
detailSubtitle: '',
|
|
663
671
|
detailNotFound: '未找到该 Agent,可能已移除或重命名。',
|
|
672
|
+
detailNotFoundHint: '该 Agent 可能已断开连接或配置文件已移动。请尝试重启 Agent 或检查 MCP 配置。',
|
|
673
|
+
detailNotFoundSuggestion: '已连接的 Agent:',
|
|
664
674
|
},
|
|
665
675
|
shortcutPanel: {
|
|
666
676
|
title: '快捷键',
|
|
@@ -742,10 +752,56 @@ export const zh = {
|
|
|
742
752
|
dropOverlay: '松开鼠标,导入文件到知识库',
|
|
743
753
|
dropOverlayFormats: '支持 .md .txt .pdf .csv .json .yaml .html',
|
|
744
754
|
onboardingHint: '已有笔记?导入文件到知识库 →',
|
|
745
|
-
digestPromptSingle: (name: string) =>
|
|
746
|
-
|
|
755
|
+
digestPromptSingle: (name: string, targetSpace?: string) => {
|
|
756
|
+
const loc = targetSpace ? `"${targetSpace}" 空间下` : '知识库中合适的位置';
|
|
757
|
+
return `用户上传了「${name}」。你必须:\n1. 从上方「USER-UPLOADED FILES」区域读取文件内容\n2. 提取和重新整理关键信息为结构清晰的 Markdown 笔记\n3. 将整理后的内容保存到${loc}——可以创建新文件,也可以更新已有文件\n\n不要只做文字回复。你必须实际写入知识库。`;
|
|
758
|
+
},
|
|
759
|
+
digestPromptMulti: (n: number, targetSpace?: string) => {
|
|
760
|
+
const loc = targetSpace ? `"${targetSpace}" 空间下` : '知识库中合适的位置';
|
|
761
|
+
return `用户上传了 ${n} 个文件。你必须:\n1. 从上方「USER-UPLOADED FILES」区域读取它们的内容\n2. 提取和重新整理关键信息为结构清晰的 Markdown 笔记\n3. 将整理后的内容保存到${loc}——可以创建新文件,也可以更新已有文件\n\n不要只做文字回复。你必须实际写入知识库。`;
|
|
762
|
+
},
|
|
747
763
|
arrowTo: '→',
|
|
748
764
|
remove: '移除',
|
|
765
|
+
conflictsFound: (n: number) => `${n} 个文件已存在`,
|
|
766
|
+
organizeTitle: 'AI 整理中',
|
|
767
|
+
organizeProcessing: 'AI 正在分析和整理你的文件...',
|
|
768
|
+
organizeConnecting: '正在连接 AI...',
|
|
769
|
+
organizeAnalyzing: 'AI 正在分析你的文件...',
|
|
770
|
+
organizeReading: (detail?: string) => detail ? `正在阅读 ${detail}...` : '正在阅读文件...',
|
|
771
|
+
organizeThinking: 'AI 正在深度思考...',
|
|
772
|
+
organizeWriting: (detail?: string) => detail ? `正在写入 ${detail}...` : '正在写入文件...',
|
|
773
|
+
organizeElapsed: (seconds: number) => {
|
|
774
|
+
const m = Math.floor(seconds / 60);
|
|
775
|
+
const s = seconds % 60;
|
|
776
|
+
return `${m}:${s.toString().padStart(2, '0')}`;
|
|
777
|
+
},
|
|
778
|
+
organizeCancel: '取消',
|
|
779
|
+
organizeMinimize: '继续浏览',
|
|
780
|
+
organizeExpand: '查看',
|
|
781
|
+
organizeCreating: (path: string) => `正在创建 ${path}`,
|
|
782
|
+
organizeUpdating: (path: string) => `正在更新 ${path}`,
|
|
783
|
+
organizeReviewTitle: '整理完成',
|
|
784
|
+
organizeErrorTitle: '整理失败',
|
|
785
|
+
organizeReviewDesc: (n: number) => `AI 整理了 ${n} 处变更`,
|
|
786
|
+
organizeCreated: '已创建',
|
|
787
|
+
organizeUpdated: '已更新',
|
|
788
|
+
organizeFailed: '失败',
|
|
789
|
+
organizeNoChanges: 'AI 分析了你的文件,但没有做任何更改。',
|
|
790
|
+
organizeToolCallsInfo: (n: number) => `AI 执行了 ${n} 个操作 — 请检查知识库查看更新`,
|
|
791
|
+
organizeError: '整理失败',
|
|
792
|
+
organizeRetry: '重试',
|
|
793
|
+
organizeDone: '完成',
|
|
794
|
+
organizeUndoAll: '撤销全部',
|
|
795
|
+
organizeUndoOne: '撤销',
|
|
796
|
+
organizeUndone: '已撤销',
|
|
797
|
+
organizeViewFile: '查看文件',
|
|
798
|
+
organizeUndoSuccess: (n: number) => `已撤销 ${n} 个文件`,
|
|
799
|
+
},
|
|
800
|
+
importHistory: {
|
|
801
|
+
title: '导入历史',
|
|
802
|
+
clearAll: '清空历史',
|
|
803
|
+
emptyTitle: '暂无导入记录',
|
|
804
|
+
emptyDesc: 'AI 整理的结果会出现在这里',
|
|
749
805
|
},
|
|
750
806
|
dirView: {
|
|
751
807
|
gridView: '网格视图',
|
|
@@ -765,7 +821,7 @@ export const zh = {
|
|
|
765
821
|
},
|
|
766
822
|
settings: {
|
|
767
823
|
title: '设置',
|
|
768
|
-
tabs: { ai: 'AI', appearance: '外观', knowledge: '通用', sync: '同步', mcp: 'MCP & Skills', plugins: '插件', shortcuts: '快捷键', monitoring: '监控', agents: 'Agents', update: '更新' },
|
|
824
|
+
tabs: { ai: 'AI', appearance: '外观', knowledge: '通用', sync: '同步', mcp: 'MCP & Skills', plugins: '插件', shortcuts: '快捷键', monitoring: '监控', agents: 'Agents', update: '更新', uninstall: '卸载' },
|
|
769
825
|
ai: {
|
|
770
826
|
provider: '服务商',
|
|
771
827
|
model: '模型',
|
|
@@ -800,6 +856,8 @@ export const zh = {
|
|
|
800
856
|
thinkingHint: '显示 Claude 的推理过程(消耗更多 token)',
|
|
801
857
|
thinkingBudget: '思考预算',
|
|
802
858
|
thinkingBudgetHint: '推理最大 token 数(1000-50000)',
|
|
859
|
+
reconnectRetries: '自动重连',
|
|
860
|
+
reconnectRetriesHint: '连接断开时自动重试次数,重试耗尽后停止(0 = 关闭)',
|
|
803
861
|
},
|
|
804
862
|
appearance: {
|
|
805
863
|
readingFont: '正文字体',
|
|
@@ -1024,6 +1082,29 @@ export const zh = {
|
|
|
1024
1082
|
desktopRestart: '立即重启',
|
|
1025
1083
|
desktopHint: '更新通过桌面端自动更新推送。',
|
|
1026
1084
|
},
|
|
1085
|
+
uninstall: {
|
|
1086
|
+
title: '卸载 MindOS',
|
|
1087
|
+
descCli: '从本机移除 MindOS CLI、后台服务和配置文件。',
|
|
1088
|
+
descDesktop: '从本机移除 MindOS Desktop、后台服务和配置文件。',
|
|
1089
|
+
warning: '选择要清理的内容。你的知识库文件始终是安全的。',
|
|
1090
|
+
stopServices: '停止服务并移除守护进程',
|
|
1091
|
+
stopServicesDesc: '停止所有运行中的 MindOS 进程并移除后台守护进程。',
|
|
1092
|
+
removeConfig: '移除配置',
|
|
1093
|
+
removeConfigDesc: '删除 ~/.mindos/ 目录(配置、日志、PID 文件)。',
|
|
1094
|
+
removeNpm: '卸载 CLI 包',
|
|
1095
|
+
removeNpmDesc: '执行 npm uninstall -g @geminilight/mindos。',
|
|
1096
|
+
removeApp: '将 Desktop 移入废纸篓',
|
|
1097
|
+
removeAppDesc: '将 MindOS.app 移入废纸篓,之后可以恢复。',
|
|
1098
|
+
confirmTitle: '确认卸载',
|
|
1099
|
+
confirmButton: '卸载',
|
|
1100
|
+
cancelButton: '取消',
|
|
1101
|
+
running: '正在卸载...',
|
|
1102
|
+
success: 'MindOS 已卸载。',
|
|
1103
|
+
successDesktop: 'MindOS 已卸载,应用即将退出。',
|
|
1104
|
+
error: '卸载失败,可在终端手动运行 `mindos uninstall`。',
|
|
1105
|
+
nothingSelected: '请至少选择一项要卸载的内容。',
|
|
1106
|
+
kbSafe: '你的知识库文件始终是安全的——此操作绝不会删除它们。',
|
|
1107
|
+
},
|
|
1027
1108
|
},
|
|
1028
1109
|
onboarding: {
|
|
1029
1110
|
subtitle: '知识库为空,选择一个模板快速开始。',
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Organize History — localStorage persistence for past AI organize operations
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
export interface OrganizeHistoryFile {
|
|
8
|
+
action: 'create' | 'update' | 'unknown';
|
|
9
|
+
path: string;
|
|
10
|
+
ok: boolean;
|
|
11
|
+
/** Undone by user (deleted for create / restored for update) */
|
|
12
|
+
undone?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface OrganizeHistoryEntry {
|
|
16
|
+
id: string;
|
|
17
|
+
/** Unix ms */
|
|
18
|
+
timestamp: number;
|
|
19
|
+
/** Original uploaded file names */
|
|
20
|
+
sourceFiles: string[];
|
|
21
|
+
files: OrganizeHistoryFile[];
|
|
22
|
+
status: 'completed' | 'partial' | 'undone';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const STORAGE_KEY = 'mindos:organize-history';
|
|
26
|
+
const MAX_ENTRIES = 50;
|
|
27
|
+
|
|
28
|
+
export function loadHistory(): OrganizeHistoryEntry[] {
|
|
29
|
+
if (typeof window === 'undefined') return [];
|
|
30
|
+
try {
|
|
31
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
32
|
+
if (!raw) return [];
|
|
33
|
+
return JSON.parse(raw) as OrganizeHistoryEntry[];
|
|
34
|
+
} catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function saveHistory(entries: OrganizeHistoryEntry[]): void {
|
|
40
|
+
if (typeof window === 'undefined') return;
|
|
41
|
+
try {
|
|
42
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(entries.slice(0, MAX_ENTRIES)));
|
|
43
|
+
} catch { /* quota exceeded — silently drop oldest */ }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function appendEntry(entry: OrganizeHistoryEntry): OrganizeHistoryEntry[] {
|
|
47
|
+
const all = loadHistory();
|
|
48
|
+
all.unshift(entry);
|
|
49
|
+
const trimmed = all.slice(0, MAX_ENTRIES);
|
|
50
|
+
saveHistory(trimmed);
|
|
51
|
+
return trimmed;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function updateEntry(id: string, patch: Partial<OrganizeHistoryEntry>): OrganizeHistoryEntry[] {
|
|
55
|
+
const all = loadHistory();
|
|
56
|
+
const idx = all.findIndex(e => e.id === id);
|
|
57
|
+
if (idx >= 0) {
|
|
58
|
+
all[idx] = { ...all[idx], ...patch };
|
|
59
|
+
saveHistory(all);
|
|
60
|
+
}
|
|
61
|
+
return all;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function clearHistory(): void {
|
|
65
|
+
if (typeof window === 'undefined') return;
|
|
66
|
+
try {
|
|
67
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
68
|
+
} catch { /* ignore */ }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let _idCounter = 0;
|
|
72
|
+
export function generateEntryId(): string {
|
|
73
|
+
return `org-${Date.now()}-${++_idCounter}`;
|
|
74
|
+
}
|
package/app/lib/settings.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface AgentConfig {
|
|
|
23
23
|
enableThinking?: boolean; // default false, Anthropic only
|
|
24
24
|
thinkingBudget?: number; // default 5000
|
|
25
25
|
contextStrategy?: 'auto' | 'off'; // default 'auto'
|
|
26
|
+
reconnectRetries?: number; // default 3, range 0-10 (0 = disabled)
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export interface GuideState {
|
|
@@ -128,6 +129,7 @@ function parseAgent(raw: unknown): AgentConfig | undefined {
|
|
|
128
129
|
if (typeof obj.enableThinking === 'boolean') result.enableThinking = obj.enableThinking;
|
|
129
130
|
if (typeof obj.thinkingBudget === 'number') result.thinkingBudget = Math.min(50000, Math.max(1000, obj.thinkingBudget));
|
|
130
131
|
if (obj.contextStrategy === 'auto' || obj.contextStrategy === 'off') result.contextStrategy = obj.contextStrategy;
|
|
132
|
+
if (typeof obj.reconnectRetries === 'number') result.reconnectRetries = Math.min(10, Math.max(0, obj.reconnectRetries));
|
|
131
133
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
132
134
|
}
|
|
133
135
|
|
package/app/lib/types.ts
CHANGED
|
@@ -41,6 +41,8 @@ export interface Message {
|
|
|
41
41
|
timestamp?: number;
|
|
42
42
|
/** Structured parts for assistant messages (tool calls + text segments) */
|
|
43
43
|
parts?: MessagePart[];
|
|
44
|
+
/** Skill name used for this user message (rendered as a capsule in the UI) */
|
|
45
|
+
skillName?: string;
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export interface LocalAttachment {
|
package/app/next-env.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
import "./.next/
|
|
3
|
+
import "./.next/types/routes.d.ts";
|
|
4
4
|
|
|
5
5
|
// NOTE: This file should not be edited
|
|
6
6
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
package/app/next.config.ts
CHANGED
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
import type { NextConfig } from "next";
|
|
2
2
|
import path from "path";
|
|
3
3
|
|
|
4
|
+
// When MindOS is installed globally via npm, the entire project lives
|
|
5
|
+
// under node_modules/@geminilight/mindos/. Next.js skips tsconfig path
|
|
6
|
+
// resolution and SWC TypeScript compilation for files inside node_modules.
|
|
7
|
+
// We detect this at config time and apply the necessary overrides.
|
|
8
|
+
const projectDir = path.resolve(__dirname);
|
|
9
|
+
const inNodeModules = projectDir.includes('node_modules');
|
|
10
|
+
|
|
4
11
|
const nextConfig: NextConfig = {
|
|
5
|
-
transpilePackages: [
|
|
12
|
+
transpilePackages: [
|
|
13
|
+
'github-slugger',
|
|
14
|
+
// Self-reference: ensures the SWC loader compiles our own TypeScript
|
|
15
|
+
// when the project is inside node_modules (global npm install).
|
|
16
|
+
...(inNodeModules ? ['@geminilight/mindos'] : []),
|
|
17
|
+
],
|
|
6
18
|
serverExternalPackages: ['chokidar', 'openai', '@mariozechner/pi-ai', '@mariozechner/pi-agent-core', '@mariozechner/pi-coding-agent', 'mcporter'],
|
|
7
19
|
output: 'standalone',
|
|
8
|
-
outputFileTracingRoot:
|
|
20
|
+
outputFileTracingRoot: projectDir,
|
|
9
21
|
turbopack: {
|
|
10
|
-
root:
|
|
22
|
+
root: projectDir,
|
|
11
23
|
},
|
|
12
|
-
// Disable client-side router cache for dynamic layouts so that
|
|
13
|
-
// router.refresh() always fetches a fresh file tree from the server.
|
|
14
24
|
experimental: {
|
|
15
25
|
staleTimes: {
|
|
16
26
|
dynamic: 0,
|
|
17
27
|
},
|
|
18
28
|
},
|
|
29
|
+
webpack: (config) => {
|
|
30
|
+
if (inNodeModules) {
|
|
31
|
+
config.resolve = config.resolve ?? {};
|
|
32
|
+
config.resolve.alias = config.resolve.alias ?? {};
|
|
33
|
+
(config.resolve.alias as Record<string, string>)['@'] = projectDir;
|
|
34
|
+
}
|
|
35
|
+
return config;
|
|
36
|
+
},
|
|
19
37
|
};
|
|
20
38
|
|
|
21
39
|
export default nextConfig;
|
package/app/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "next dev -p ${MINDOS_WEB_PORT:-3456}",
|
|
7
7
|
"prebuild": "node ../scripts/gen-renderer-index.js",
|
|
8
|
-
"build": "next build",
|
|
8
|
+
"build": "next build --webpack",
|
|
9
9
|
"start": "next start -p ${MINDOS_WEB_PORT:-3456}",
|
|
10
10
|
"lint": "eslint",
|
|
11
11
|
"test": "vitest run",
|
package/bin/cli.js
CHANGED
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
|
|
41
41
|
import { execSync, spawn as nodeSpawn } from 'node:child_process';
|
|
42
42
|
import { existsSync, readFileSync, writeFileSync, rmSync, cpSync } from 'node:fs';
|
|
43
|
-
import { resolve } from 'node:path';
|
|
43
|
+
import { dirname, resolve } from 'node:path';
|
|
44
44
|
import { homedir } from 'node:os';
|
|
45
45
|
|
|
46
46
|
import { ROOT, CONFIG_PATH, BUILD_STAMP, LOG_PATH, MINDOS_DIR } from './lib/constants.js';
|
|
@@ -57,6 +57,7 @@ import { stopMindos } from './lib/stop.js';
|
|
|
57
57
|
import { getPlatform, ensureMindosDir, waitForHttp, waitForPortFree, runGatewayCommand } from './lib/gateway.js';
|
|
58
58
|
import { printStartupInfo, getLocalIP } from './lib/startup.js';
|
|
59
59
|
import { spawnMcp } from './lib/mcp-spawn.js';
|
|
60
|
+
import { ensureMcpBundle } from './lib/mcp-build.js';
|
|
60
61
|
import { mcpInstall } from './lib/mcp-install.js';
|
|
61
62
|
import { initSync, startSyncDaemon, stopSyncDaemon, getSyncStatus, manualSync, listConflicts, setSyncEnabled } from './lib/sync.js';
|
|
62
63
|
|
|
@@ -140,7 +141,7 @@ if (cmd === '--version' || cmd === '-v') {
|
|
|
140
141
|
|
|
141
142
|
const isDaemon = process.argv.includes('--daemon') || (!cmd && isDaemonMode());
|
|
142
143
|
const isVerbose = process.argv.includes('--verbose');
|
|
143
|
-
const extra = process.argv.slice(3).filter(a => a !== '--daemon' && a !== '--verbose').join(' ');
|
|
144
|
+
const extra = process.argv.slice(3).filter(a => a !== '--daemon' && a !== '--verbose' && a !== '--turbo').join(' ');
|
|
144
145
|
|
|
145
146
|
const commands = {
|
|
146
147
|
// ── onboard ────────────────────────────────────────────────────────────────
|
|
@@ -267,10 +268,12 @@ const commands = {
|
|
|
267
268
|
// ── dev ────────────────────────────────────────────────────────────────────
|
|
268
269
|
dev: async () => {
|
|
269
270
|
loadConfig();
|
|
271
|
+
if (!process.env.MINDOS_WEB_PORT) process.env.MINDOS_WEB_PORT = '3456';
|
|
272
|
+
if (!process.env.MINDOS_MCP_PORT) process.env.MINDOS_MCP_PORT = '8781';
|
|
270
273
|
process.env.MINDOS_CLI_PATH = resolve(ROOT, 'bin', 'cli.js');
|
|
271
274
|
process.env.MINDOS_NODE_BIN = process.execPath;
|
|
272
|
-
const webPort = process.env.MINDOS_WEB_PORT
|
|
273
|
-
const mcpPort = process.env.MINDOS_MCP_PORT
|
|
275
|
+
const webPort = process.env.MINDOS_WEB_PORT;
|
|
276
|
+
const mcpPort = process.env.MINDOS_MCP_PORT;
|
|
274
277
|
await assertPortFree(Number(webPort), 'web');
|
|
275
278
|
await assertPortFree(Number(mcpPort), 'mcp');
|
|
276
279
|
ensureAppDeps();
|
|
@@ -303,8 +306,10 @@ const commands = {
|
|
|
303
306
|
console.warn(yellow('Warning: daemon mode not supported on this platform. Falling back to foreground.'));
|
|
304
307
|
} else {
|
|
305
308
|
loadConfig();
|
|
306
|
-
|
|
307
|
-
|
|
309
|
+
if (!process.env.MINDOS_WEB_PORT) process.env.MINDOS_WEB_PORT = '3456';
|
|
310
|
+
if (!process.env.MINDOS_MCP_PORT) process.env.MINDOS_MCP_PORT = '8781';
|
|
311
|
+
const webPort = process.env.MINDOS_WEB_PORT;
|
|
312
|
+
const mcpPort = process.env.MINDOS_MCP_PORT;
|
|
308
313
|
console.log(cyan(`Installing MindOS as a background service (${platform})...`));
|
|
309
314
|
await runGatewayCommand('install');
|
|
310
315
|
// install() already starts the service via launchctl bootstrap + RunAtLoad=true.
|
|
@@ -334,8 +339,10 @@ const commands = {
|
|
|
334
339
|
}
|
|
335
340
|
}
|
|
336
341
|
loadConfig();
|
|
337
|
-
|
|
338
|
-
|
|
342
|
+
if (!process.env.MINDOS_WEB_PORT) process.env.MINDOS_WEB_PORT = '3456';
|
|
343
|
+
if (!process.env.MINDOS_MCP_PORT) process.env.MINDOS_MCP_PORT = '8781';
|
|
344
|
+
const webPort = process.env.MINDOS_WEB_PORT;
|
|
345
|
+
const mcpPort = process.env.MINDOS_MCP_PORT;
|
|
339
346
|
|
|
340
347
|
// ── Auto-migrate user-rules.md to root user-skill-rules.md ─────────────
|
|
341
348
|
try {
|
|
@@ -382,7 +389,7 @@ const commands = {
|
|
|
382
389
|
console.log(yellow('Building MindOS (first run or new version detected)...\n'));
|
|
383
390
|
cleanNextDir();
|
|
384
391
|
run('node scripts/gen-renderer-index.js', ROOT);
|
|
385
|
-
run(`${NEXT_BIN} build`, resolve(ROOT, 'app'));
|
|
392
|
+
run(`${NEXT_BIN} build --webpack`, resolve(ROOT, 'app'));
|
|
386
393
|
writeBuildStamp();
|
|
387
394
|
}
|
|
388
395
|
const mcp = spawnMcp(isVerbose);
|
|
@@ -397,7 +404,7 @@ const commands = {
|
|
|
397
404
|
run(
|
|
398
405
|
`${NEXT_BIN} start -p ${webPort} ${extra}`,
|
|
399
406
|
resolve(ROOT, 'app'),
|
|
400
|
-
|
|
407
|
+
{ HOSTNAME: '127.0.0.1' }
|
|
401
408
|
);
|
|
402
409
|
},
|
|
403
410
|
|
|
@@ -406,7 +413,7 @@ const commands = {
|
|
|
406
413
|
ensureAppDeps();
|
|
407
414
|
cleanNextDir();
|
|
408
415
|
run('node scripts/gen-renderer-index.js', ROOT);
|
|
409
|
-
run(`${NEXT_BIN} build ${extra}`, resolve(ROOT, 'app'));
|
|
416
|
+
run(`${NEXT_BIN} build --webpack ${extra}`, resolve(ROOT, 'app'));
|
|
410
417
|
writeBuildStamp();
|
|
411
418
|
},
|
|
412
419
|
|
|
@@ -416,11 +423,7 @@ const commands = {
|
|
|
416
423
|
const hasInstallFlags = restArgs.some(a => ['-g', '--global', '-y', '--yes'].includes(a));
|
|
417
424
|
if (sub === 'install' || hasInstallFlags) { await mcpInstall(); return; }
|
|
418
425
|
loadConfig();
|
|
419
|
-
|
|
420
|
-
if (!existsSync(mcpSdk)) {
|
|
421
|
-
console.log(yellow('Installing MCP dependencies (first run)...\n'));
|
|
422
|
-
npmInstall(resolve(ROOT, 'mcp'), '--no-workspaces');
|
|
423
|
-
}
|
|
426
|
+
ensureMcpBundle();
|
|
424
427
|
// `mindos mcp` is the entry point for MCP clients (Claude Code, Cursor, etc.)
|
|
425
428
|
// which communicate over stdin/stdout. Default to stdio; HTTP is handled by
|
|
426
429
|
// `mindos start` via spawnMcp(). Callers can still override via env.
|
|
@@ -784,7 +787,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
784
787
|
const webPort = updateConfig.port ?? 3456;
|
|
785
788
|
const mcpPort = updateConfig.mcpPort ?? 8781;
|
|
786
789
|
console.log(dim(' (Waiting for Web UI to come back up — first run after update includes a rebuild...)'));
|
|
787
|
-
const ready = await waitForHttp(Number(webPort), { retries:
|
|
790
|
+
const ready = await waitForHttp(Number(webPort), { retries: 180, intervalMs: 2000, label: 'Web UI', logFile: LOG_PATH });
|
|
788
791
|
if (ready) {
|
|
789
792
|
const localIP = getLocalIP();
|
|
790
793
|
console.log(`\n${'─'.repeat(53)}`);
|
|
@@ -843,7 +846,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
843
846
|
child.unref();
|
|
844
847
|
|
|
845
848
|
console.log(dim(' (Waiting for Web UI to come back up...)'));
|
|
846
|
-
const ready = await waitForHttp(webPort, { retries:
|
|
849
|
+
const ready = await waitForHttp(webPort, { retries: 180, intervalMs: 2000, label: 'Web UI', logFile: LOG_PATH });
|
|
847
850
|
if (ready) {
|
|
848
851
|
const localIP = getLocalIP();
|
|
849
852
|
console.log(`\n${'─'.repeat(53)}`);
|