@geminilight/mindos 0.5.43 → 0.5.45
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 +42 -257
- package/README_zh.md +43 -261
- package/app/app/api/ask-sessions/route.ts +6 -4
- package/app/app/api/update-status/route.ts +19 -0
- package/app/components/CreateSpaceModal.tsx +87 -13
- package/app/components/DirPicker.tsx +2 -1
- package/app/components/GuideCard.tsx +38 -26
- package/app/components/HomeContent.tsx +6 -13
- package/app/components/ask/AskContent.tsx +10 -3
- package/app/components/ask/SessionHistory.tsx +39 -3
- package/app/components/explore/ExploreContent.tsx +50 -19
- package/app/components/explore/use-cases.ts +41 -13
- package/app/components/panels/DiscoverPanel.tsx +89 -99
- package/app/components/settings/SettingsContent.tsx +1 -1
- package/app/components/settings/UpdateTab.tsx +145 -28
- package/app/hooks/useAskSession.ts +24 -0
- package/app/lib/i18n-en.ts +25 -4
- package/app/lib/i18n-zh.ts +25 -4
- package/app/lib/utils.ts +11 -0
- package/bin/cli.js +87 -15
- package/bin/lib/startup.js +4 -0
- package/bin/lib/update-status.js +115 -0
- package/package.json +1 -1
- package/scripts/fix-postcss-deps.cjs +4 -2
package/README_zh.md
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
<p align="center">
|
|
16
16
|
<a href="https://tianfuwang.tech/MindOS"><img src="https://img.shields.io/badge/Website-MindOS-0ea5e9.svg?style=for-the-badge" alt="Website"></a>
|
|
17
17
|
<a href="https://www.npmjs.com/package/@geminilight/mindos"><img src="https://img.shields.io/npm/v/@geminilight/mindos.svg?style=for-the-badge&color=f59e0b" alt="npm version"></a>
|
|
18
|
+
<a href="https://www.npmjs.com/package/@geminilight/mindos"><img src="https://img.shields.io/npm/dw/@geminilight/mindos.svg?style=for-the-badge&color=10b981" alt="npm downloads"></a>
|
|
19
|
+
<a href="https://github.com/GeminiLight/MindOS/stargazers"><img src="https://img.shields.io/github/stars/GeminiLight/MindOS.svg?style=for-the-badge&color=f59e0b" alt="GitHub Stars"></a>
|
|
18
20
|
<a href="#wechat"><img src="https://img.shields.io/badge/WeChat-群聊-07C160.svg?style=for-the-badge&logo=wechat&logoColor=white" alt="WeChat"></a>
|
|
19
21
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-6366f1.svg?style=for-the-badge" alt="MIT License"></a>
|
|
20
22
|
</p>
|
|
@@ -84,14 +86,15 @@ Agent 记了什么、记对没有,用户无从知晓。**MindOS 将每次读
|
|
|
84
86
|
|
|
85
87
|
- **安全防线**:Bearer Token 认证、路径沙箱、INSTRUCTION.md 写保护、原子写入。
|
|
86
88
|
- **知识图谱**:动态解析并可视化文件间的引用与依赖关系。
|
|
89
|
+
- **反向链接视图**:展示所有引用当前文件的反向链接,理解笔记在知识网络中的位置。
|
|
87
90
|
- **Git 时光机**:Git 自动同步(commit/push/pull),记录人类与 Agent 的每次编辑历史,一键回滚,跨设备同步。
|
|
91
|
+
- **桌面客户端**:原生 macOS/Windows/Linux 应用,系统托盘、开机自启、本地进程管理。
|
|
88
92
|
|
|
89
93
|
<details>
|
|
90
94
|
<summary><strong>即将到来</strong></summary>
|
|
91
95
|
|
|
92
96
|
- [ ] ACP(Agent Communication Protocol):连接外部 Agent(如 Claude Code、Cursor),让知识库成为多 Agent 协作的中枢
|
|
93
97
|
- [ ] RAG 深度集成:基于知识库内容的检索增强生成,让 AI 回答更精准、更有上下文
|
|
94
|
-
- [ ] 反向链接视图(Backlinks):展示所有引用当前文件的反向链接,理解笔记在知识网络中的位置
|
|
95
98
|
- [ ] Agent 审计面板(Agent Inspector):将 Agent 操作日志渲染为可筛选的时间线,审查每次工具调用的详情
|
|
96
99
|
|
|
97
100
|
</details>
|
|
@@ -128,89 +131,13 @@ npm link # 将 mindos 命令注册为全局命令
|
|
|
128
131
|
### 2. 交互式配置
|
|
129
132
|
|
|
130
133
|
```bash
|
|
131
|
-
mindos onboard
|
|
134
|
+
mindos onboard
|
|
132
135
|
```
|
|
133
136
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
配置向导将引导你完成:
|
|
137
|
-
1. 知识库路径 → 默认 `~/MindOS`
|
|
138
|
-
2. 选择模板(en / zh / empty / custom)
|
|
139
|
-
3. 端口配置(Web UI + MCP)
|
|
140
|
-
4. Auth token(自动生成或口令派生)
|
|
141
|
-
5. Web UI 访问密码(可选)
|
|
142
|
-
6. 配置 AI Provider(Anthropic / OpenAI)+ API Key — 或选择 **skip**,稍后通过 `mindos config set` 补填
|
|
143
|
-
7. 启动模式 — **后台服务**(推荐,开机自启)或前台运行
|
|
144
|
-
|
|
145
|
-
配置自动保存到 `~/.mindos/config.json`。
|
|
146
|
-
|
|
147
|
-
<!-- For Agents: 运行 onboard 前,如果用户未提供 LLM 配置,请询问:
|
|
148
|
-
1. AI Provider — `anthropic` 还是 `openai`(兼容)?
|
|
149
|
-
2. API Key — 对应 Provider 的密钥
|
|
150
|
-
3. 自定义 Base URL?— 仅代理或 OpenAI 兼容端点需要;使用官方 API 则跳过
|
|
151
|
-
4. Model ID — 或使用默认值
|
|
152
|
-
或跳过向导,手动编辑 `~/.mindos/config.json`(参考下方配置参考)。
|
|
153
|
-
-->
|
|
154
|
-
|
|
155
|
-
<details>
|
|
156
|
-
<summary>配置参考(~/.mindos/config.json)</summary>
|
|
157
|
-
|
|
158
|
-
```json
|
|
159
|
-
{
|
|
160
|
-
"mindRoot": "~/MindOS",
|
|
161
|
-
"port": 3456,
|
|
162
|
-
"mcpPort": 8781,
|
|
163
|
-
"authToken": "",
|
|
164
|
-
"webPassword": "",
|
|
165
|
-
"startMode": "daemon",
|
|
166
|
-
"ai": {
|
|
167
|
-
"provider": "anthropic",
|
|
168
|
-
"providers": {
|
|
169
|
-
"anthropic": { "apiKey": "sk-ant-...", "model": "claude-sonnet-4-6" },
|
|
170
|
-
"openai": { "apiKey": "sk-...", "model": "gpt-5.4", "baseUrl": "" }
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
"sync": {
|
|
174
|
-
"enabled": true,
|
|
175
|
-
"provider": "git",
|
|
176
|
-
"remote": "origin",
|
|
177
|
-
"branch": "main",
|
|
178
|
-
"autoCommitInterval": 30,
|
|
179
|
-
"autoPullInterval": 300
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
| 字段 | 默认值 | 说明 |
|
|
185
|
-
| :--- | :--- | :--- |
|
|
186
|
-
| `mindRoot` | `~/MindOS` | **必填**。知识库根目录的绝对路径 |
|
|
187
|
-
| `port` | `3456` | 可选。Web 服务端口 |
|
|
188
|
-
| `mcpPort` | `8781` | 可选。MCP 服务端口 |
|
|
189
|
-
| `authToken` | — | 可选。保护 App `/api/*` 和 MCP `/mcp` 的 Bearer Token 认证。供 Agent / MCP 客户端使用,暴露到网络时建议设置 |
|
|
190
|
-
| `webPassword` | — | 可选。为 Web UI 添加登录密码保护。供浏览器访问,与 `authToken` 相互独立 |
|
|
191
|
-
| `startMode` | `start` | 启动模式:`daemon`(后台服务,开机自启)、`start`(前台)或 `dev` |
|
|
192
|
-
| `ai.provider` | `anthropic` | 当前使用的 provider:`anthropic` 或 `openai` |
|
|
193
|
-
| `ai.providers.anthropic.apiKey` | — | Anthropic API Key |
|
|
194
|
-
| `ai.providers.anthropic.model` | `claude-sonnet-4-6` | Anthropic 模型 ID |
|
|
195
|
-
| `ai.providers.openai.apiKey` | — | OpenAI API Key |
|
|
196
|
-
| `ai.providers.openai.model` | `gpt-5.4` | OpenAI 模型 ID |
|
|
197
|
-
| `ai.providers.openai.baseUrl` | — | 可选。用于代理或 OpenAI 兼容 API 的自定义接口地址 |
|
|
198
|
-
| `sync.enabled` | `false` | 启用/禁用 Git 自动同步 |
|
|
199
|
-
| `sync.provider` | `git` | 同步方式(目前仅支持 `git`) |
|
|
200
|
-
| `sync.remote` | `origin` | Git 远程仓库名 |
|
|
201
|
-
| `sync.branch` | `main` | 同步分支 |
|
|
202
|
-
| `sync.autoCommitInterval` | `30` | 文件变更后自动 commit+push 的延迟秒数 |
|
|
203
|
-
| `sync.autoPullInterval` | `300` | 自动从远程 pull 的间隔秒数 |
|
|
204
|
-
|
|
205
|
-
多个 provider 可以同时配置,切换时只需修改 `ai.provider` 字段,无需重新填写 API Key。Shell 环境变量(`ANTHROPIC_API_KEY`、`OPENAI_API_KEY` 等)优先级高于配置文件。
|
|
206
|
-
|
|
207
|
-
</details>
|
|
208
|
-
|
|
209
|
-
> [!NOTE]
|
|
210
|
-
> 如果你希望其他设备也能访问 MindOS GUI,请确保端口已在防火墙/安全组中放行,并绑定到可访问的主机地址/网卡。
|
|
137
|
+
配置向导会引导你完成知识库路径、模板、端口、认证、AI 服务商、启动模式等配置——所有选项都有合理默认值。配置自动保存到 `~/.mindos/config.json`。完整字段说明见 **[docs/zh/configuration.md](docs/zh/configuration.md)**。
|
|
211
138
|
|
|
212
139
|
> [!TIP]
|
|
213
|
-
>
|
|
140
|
+
> 选择"后台服务"模式可开机自启。随时运行 `mindos update` 升级到最新版本。
|
|
214
141
|
|
|
215
142
|
在浏览器中打开 Web UI:
|
|
216
143
|
|
|
@@ -230,124 +157,24 @@ mindos open
|
|
|
230
157
|
|
|
231
158
|
### 4. 让任意 Agent 可用(MCP + Skills)
|
|
232
159
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
<!-- For Agents: MindOS MCP 默认注册为全局(user-level)配置,使所有项目共享同一知识库。-->
|
|
236
|
-
|
|
237
|
-
一条命令自动将 MCP 配置写入 Agent:
|
|
238
|
-
|
|
239
|
-
```bash
|
|
240
|
-
mindos mcp install
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
支持 Claude Code、Cursor、Windsurf、Cline、Trae、Gemini CLI 等主流 Agent,交互式引导完成 agent、scope、transport 和 token 的配置。
|
|
244
|
-
|
|
245
|
-
**本机访问(Agent 和 MindOS 在同一台机器)**
|
|
246
|
-
|
|
247
|
-
使用 `stdio` transport — 无需启动服务进程,最稳定:
|
|
248
|
-
|
|
249
|
-
```bash
|
|
250
|
-
# 交互式
|
|
251
|
-
mindos mcp install
|
|
252
|
-
|
|
253
|
-
# 一键安装,全局(所有项目共享)
|
|
254
|
-
mindos mcp install -g -y
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
**远程访问(Agent 在另一台机器)**
|
|
258
|
-
|
|
259
|
-
使用 `http` transport — 远程机器上需先运行 `mindos start`:
|
|
160
|
+
**MCP**(连接能力)— 一条命令自动安装:
|
|
260
161
|
|
|
261
162
|
```bash
|
|
262
|
-
mindos mcp install
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
> [!NOTE]
|
|
266
|
-
> 远程访问时,请确保端口 `8781` 已在防火墙/安全组中放行。
|
|
267
|
-
|
|
268
|
-
> 加 `-g` 表示全局安装 — MCP 配置写入用户级配置文件,所有项目共享,而非仅当前目录。
|
|
269
|
-
|
|
270
|
-
> MCP 端口默认为 `8781`。如需修改,运行 `mindos onboard` 设置 `mcpPort`。
|
|
271
|
-
|
|
272
|
-
<details>
|
|
273
|
-
<summary>手动配置(JSON 片段)</summary>
|
|
274
|
-
|
|
275
|
-
**本机 stdio**(无需启动服务进程):
|
|
276
|
-
|
|
277
|
-
```json
|
|
278
|
-
{
|
|
279
|
-
"mcpServers": {
|
|
280
|
-
"mindos": {
|
|
281
|
-
"type": "stdio",
|
|
282
|
-
"command": "mindos",
|
|
283
|
-
"args": ["mcp"],
|
|
284
|
-
"env": { "MCP_TRANSPORT": "stdio" }
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
**本机 URL:**
|
|
291
|
-
|
|
292
|
-
```json
|
|
293
|
-
{
|
|
294
|
-
"mcpServers": {
|
|
295
|
-
"mindos": {
|
|
296
|
-
"url": "http://localhost:8781/mcp",
|
|
297
|
-
"headers": { "Authorization": "Bearer your-token" }
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
163
|
+
mindos mcp install # 交互式
|
|
164
|
+
mindos mcp install -g -y # 一键全局安装
|
|
301
165
|
```
|
|
302
166
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
```json
|
|
306
|
-
{
|
|
307
|
-
"mcpServers": {
|
|
308
|
-
"mindos": {
|
|
309
|
-
"url": "http://<服务器IP>:8781/mcp",
|
|
310
|
-
"headers": { "Authorization": "Bearer your-token" }
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
各 Agent 的配置文件路径不同,详见下方 [支持的 Agent](#-支持的-agent) 表格中的 **MCP 配置文件路径** 列。
|
|
317
|
-
|
|
318
|
-
</details>
|
|
319
|
-
|
|
320
|
-
#### 4.2 安装 MindOS Skills
|
|
321
|
-
|
|
322
|
-
| Skill | 说明 |
|
|
323
|
-
|-------|------|
|
|
324
|
-
| `mindos` | 知识库操作指南(英文)— 读写笔记、搜索、管理 SOP、维护 Profile |
|
|
325
|
-
| `mindos-zh` | 知识库操作指南(中文)— 相同能力,中文交互 |
|
|
326
|
-
|
|
327
|
-
根据你的语言偏好选择其一安装即可:
|
|
167
|
+
**Skills**(工作流能力)— 根据语言偏好选择其一:
|
|
328
168
|
|
|
329
169
|
```bash
|
|
330
|
-
# 英文
|
|
331
|
-
npx skills add https://github.com/GeminiLight/MindOS --skill mindos -g -y
|
|
332
|
-
|
|
333
|
-
# 中文(可选)
|
|
334
|
-
npx skills add https://github.com/GeminiLight/MindOS --skill mindos-zh -g -y
|
|
170
|
+
npx skills add https://github.com/GeminiLight/MindOS --skill mindos -g -y # 英文
|
|
171
|
+
npx skills add https://github.com/GeminiLight/MindOS --skill mindos-zh -g -y # 中文
|
|
335
172
|
```
|
|
336
173
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
#### 4.3 常见误区
|
|
340
|
-
|
|
341
|
-
- 只配 MCP,不装 Skills:能调用工具,但缺少最佳实践指引。
|
|
342
|
-
- 只装 Skills,不配 MCP:有流程提示,但无法操作本地知识库。
|
|
343
|
-
- `MIND_ROOT` 不是绝对路径:MCP 工具调用会失败。
|
|
344
|
-
- 未设置 `authToken`:API 和 MCP 服务暴露在网络上,存在安全风险。
|
|
345
|
-
- 未设置 `webPassword`:任何能访问服务器的人都可以打开 Web UI。
|
|
174
|
+
> 远程配置、手动 JSON 片段、常见误区等详见 **[docs/zh/supported-agents.md](docs/zh/supported-agents.md)**。
|
|
346
175
|
|
|
347
176
|
## ⚙️ 运作机制
|
|
348
177
|
|
|
349
|
-
一个零散想法如何变成所有 Agent 共享的智慧——三个联动飞轮:
|
|
350
|
-
|
|
351
178
|
```mermaid
|
|
352
179
|
graph LR
|
|
353
180
|
H["👤 人类<br/><sub>思考 · 审查 · 进化</sub>"]
|
|
@@ -369,42 +196,30 @@ graph LR
|
|
|
369
196
|
|
|
370
197
|
> **双向进化。** 人类从积累的知识中获得新洞察;Agent 提炼 SOP 变得更强。MindOS 居中——随每次交互持续成长的共享第二大脑。
|
|
371
198
|
|
|
372
|
-
**协作闭环(人类 + 多 Agent)**
|
|
373
|
-
|
|
374
|
-
1. 人类在 MindOS GUI 中审阅并更新笔记/SOP(单一事实来源)。
|
|
375
|
-
2. 其他 Agent 客户端(OpenClaw、Claude Code、Cursor 等)通过 MCP 读取同一份记忆与上下文。
|
|
376
|
-
3. 启用 Skills 后,Agent 按工作流指引执行任务与 SOP。
|
|
377
|
-
4. 执行结果回写到 MindOS,供人类持续审查与迭代。
|
|
378
|
-
|
|
379
|
-
**适用人群:**
|
|
380
|
-
|
|
381
|
-
- **AI 独立开发者** — 将个人 SOP、技术栈偏好、项目上下文存入 MindOS,任何 Agent 即插即用你的工作习惯。
|
|
382
|
-
- **知识工作者** — 用双链笔记管理研究资料,AI 助手基于你的完整上下文回答问题,而非泛泛而谈。
|
|
383
|
-
- **团队协作** — 团队成员共享同一个 MindOS 知识库作为 Single Source of Truth,人与 Agent 读同一份剧本,保持对齐。
|
|
384
|
-
- **Agent 自动运维** — 将标准流程写成笔记即指令的文档,Agent 直接执行,人类审计结果。
|
|
385
|
-
|
|
386
199
|
---
|
|
387
200
|
|
|
388
201
|
## 🤝 支持的 Agent
|
|
389
202
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
|
393
|
-
|
|
394
|
-
|
|
|
395
|
-
|
|
|
396
|
-
|
|
|
397
|
-
|
|
|
398
|
-
|
|
|
399
|
-
|
|
|
400
|
-
|
|
|
401
|
-
|
|
|
402
|
-
|
|
|
403
|
-
|
|
|
203
|
+
> 完整列表及 MCP 配置路径、手动配置方法:**[docs/zh/supported-agents.md](docs/zh/supported-agents.md)**
|
|
204
|
+
|
|
205
|
+
| Agent | MCP | Skills |
|
|
206
|
+
|:------|:---:|:------:|
|
|
207
|
+
| MindOS Agent | ✅ | ✅ |
|
|
208
|
+
| OpenClaw | ✅ | ✅ |
|
|
209
|
+
| Claude Desktop / Code | ✅ | ✅ |
|
|
210
|
+
| CodeBuddy | ✅ | ✅ |
|
|
211
|
+
| Cursor | ✅ | ✅ |
|
|
212
|
+
| Windsurf | ✅ | ✅ |
|
|
213
|
+
| Cline | ✅ | ✅ |
|
|
214
|
+
| Trae | ✅ | ✅ |
|
|
215
|
+
| Gemini CLI | ✅ | ✅ |
|
|
216
|
+
| GitHub Copilot | ✅ | ✅ |
|
|
217
|
+
| iFlow | ✅ | ✅ |
|
|
404
218
|
|
|
405
219
|
---
|
|
406
220
|
|
|
407
|
-
|
|
221
|
+
<details>
|
|
222
|
+
<summary><strong>📁 项目架构</strong></summary>
|
|
408
223
|
|
|
409
224
|
```bash
|
|
410
225
|
MindOS/
|
|
@@ -422,57 +237,24 @@ MindOS/
|
|
|
422
237
|
└── my-mind/ # 你的私有知识库(默认路径,onboard 时可自定义)
|
|
423
238
|
```
|
|
424
239
|
|
|
425
|
-
|
|
240
|
+
</details>
|
|
426
241
|
|
|
427
242
|
## ⌨️ CLI 命令
|
|
428
243
|
|
|
244
|
+
> 完整命令参考:**[docs/zh/cli-commands.md](docs/zh/cli-commands.md)**
|
|
245
|
+
|
|
429
246
|
| 命令 | 说明 |
|
|
430
247
|
| :--- | :--- |
|
|
431
248
|
| `mindos onboard` | 交互式初始化(生成配置、选择模板) |
|
|
432
|
-
| `mindos
|
|
433
|
-
| `mindos start` |
|
|
434
|
-
| `mindos
|
|
435
|
-
| `mindos
|
|
436
|
-
| `mindos
|
|
437
|
-
| `mindos
|
|
438
|
-
| `mindos
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
| `mindos mcp` | 仅启动 MCP 服务 |
|
|
442
|
-
| `mindos token` | 查看当前 Auth token 及 MCP 配置片段 |
|
|
443
|
-
| `mindos sync` | 查看同步状态(`sync status` 的别名) |
|
|
444
|
-
| `mindos sync init` | 交互式配置 Git 远程同步 |
|
|
445
|
-
| `mindos sync status` | 查看同步状态:最后同步时间、未推送提交、冲突 |
|
|
446
|
-
| `mindos sync now` | 手动触发完整同步(commit + push + pull) |
|
|
447
|
-
| `mindos sync on` | 启用自动同步 |
|
|
448
|
-
| `mindos sync off` | 禁用自动同步 |
|
|
449
|
-
| `mindos sync conflicts` | 列出未解决的冲突文件 |
|
|
450
|
-
| `mindos gateway install` | 安装后台服务(Linux 用 systemd,macOS 用 LaunchAgent) |
|
|
451
|
-
| `mindos gateway uninstall` | 卸载后台服务 |
|
|
452
|
-
| `mindos gateway start` | 启动后台服务 |
|
|
453
|
-
| `mindos gateway stop` | 停止后台服务 |
|
|
454
|
-
| `mindos gateway status` | 查看后台服务状态 |
|
|
455
|
-
| `mindos gateway logs` | 实时查看服务日志 |
|
|
456
|
-
| `mindos doctor` | 健康检查(配置、端口、构建、daemon 状态) |
|
|
457
|
-
| `mindos update` | 更新 MindOS 到最新版本 |
|
|
458
|
-
| `mindos uninstall` | 完整卸载 MindOS(停止进程、移除 daemon、npm 卸载) |
|
|
459
|
-
| `mindos logs` | 实时查看服务日志(`~/.mindos/mindos.log`) |
|
|
460
|
-
| `mindos config show` | 查看当前配置(API Key 脱敏显示) |
|
|
461
|
-
| `mindos config validate` | 验证配置文件 |
|
|
462
|
-
| `mindos config set <key> <val>` | 更新单个配置字段 |
|
|
463
|
-
| `mindos` | 使用 `~/.mindos/config.json` 中保存的模式启动 |
|
|
464
|
-
|
|
465
|
-
---
|
|
466
|
-
|
|
467
|
-
## ⌨️ 快捷键指南
|
|
468
|
-
|
|
469
|
-
| 快捷键 | 功能 |
|
|
470
|
-
| :--- | :--- |
|
|
471
|
-
| `⌘ + K` | 全局搜索知识库 |
|
|
472
|
-
| `⌘ + /` | 唤起 AI 问答 / 侧边栏 |
|
|
473
|
-
| `E` | 在阅读界面按 `E` 快速进入编辑模式 |
|
|
474
|
-
| `⌘ + S` | 保存当前编辑 |
|
|
475
|
-
| `Esc` | 取消编辑 / 关闭弹窗 |
|
|
249
|
+
| `mindos start` | 前台启动 app + MCP 服务 |
|
|
250
|
+
| `mindos start --daemon` | 以后台 OS 服务方式启动 |
|
|
251
|
+
| `mindos open` | 在浏览器中打开 Web UI |
|
|
252
|
+
| `mindos mcp install` | 自动将 MCP 配置写入 Agent |
|
|
253
|
+
| `mindos sync init` | 配置 Git 远程同步 |
|
|
254
|
+
| `mindos update` | 更新到最新版本 |
|
|
255
|
+
| `mindos doctor` | 健康检查 |
|
|
256
|
+
|
|
257
|
+
**快捷键:** `⌘K` 搜索 · `⌘/` AI 助手 · `E` 编辑 · `⌘S` 保存 · `Esc` 关闭
|
|
476
258
|
|
|
477
259
|
---
|
|
478
260
|
|
|
@@ -74,17 +74,19 @@ export async function POST(req: NextRequest) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
export async function DELETE(req: NextRequest) {
|
|
77
|
-
let body: { id?: string };
|
|
77
|
+
let body: { id?: string; ids?: string[] };
|
|
78
78
|
try {
|
|
79
79
|
body = await req.json();
|
|
80
80
|
} catch {
|
|
81
81
|
return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 });
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
// Support both single id and bulk ids
|
|
85
|
+
const idsToDelete = body.ids ?? (body.id ? [body.id] : []);
|
|
86
|
+
if (idsToDelete.length === 0) return NextResponse.json({ error: 'id or ids is required' }, { status: 400 });
|
|
86
87
|
|
|
87
|
-
const
|
|
88
|
+
const deleteSet = new Set(idsToDelete);
|
|
89
|
+
const sessions = readSessions().filter((s) => !deleteSet.has(s.id));
|
|
88
90
|
writeSessions(sessions);
|
|
89
91
|
return NextResponse.json({ ok: true });
|
|
90
92
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const dynamic = 'force-dynamic';
|
|
2
|
+
import { NextResponse } from 'next/server';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
import { resolve } from 'path';
|
|
5
|
+
import { homedir } from 'os';
|
|
6
|
+
|
|
7
|
+
const STATUS_PATH = resolve(homedir(), '.mindos', 'update-status.json');
|
|
8
|
+
|
|
9
|
+
const IDLE_RESPONSE = { stage: 'idle', stages: [], error: null, version: null, startedAt: null };
|
|
10
|
+
|
|
11
|
+
export async function GET() {
|
|
12
|
+
try {
|
|
13
|
+
const raw = readFileSync(STATUS_PATH, 'utf-8');
|
|
14
|
+
const data = JSON.parse(raw);
|
|
15
|
+
return NextResponse.json(data);
|
|
16
|
+
} catch {
|
|
17
|
+
return NextResponse.json(IDLE_RESPONSE);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useMemo, useCallback, useRef } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
|
-
import { Folder, Loader2, X } from 'lucide-react';
|
|
5
|
+
import { Folder, Loader2, X, Sparkles, AlertTriangle } from 'lucide-react';
|
|
6
6
|
import { useRouter } from 'next/navigation';
|
|
7
7
|
import { useLocale } from '@/lib/LocaleContext';
|
|
8
8
|
import { encodePath } from '@/lib/utils';
|
|
9
9
|
import { createSpaceAction } from '@/lib/actions';
|
|
10
|
+
import { apiFetch } from '@/lib/api';
|
|
10
11
|
import DirPicker from './DirPicker';
|
|
11
12
|
|
|
12
13
|
/* ── Create Space Modal ── */
|
|
@@ -20,6 +21,8 @@ export default function CreateSpaceModal({ t, dirPaths }: { t: ReturnType<typeof
|
|
|
20
21
|
const [loading, setLoading] = useState(false);
|
|
21
22
|
const [error, setError] = useState('');
|
|
22
23
|
const [nameHint, setNameHint] = useState('');
|
|
24
|
+
const [aiAvailable, setAiAvailable] = useState<boolean | null>(null); // null = loading
|
|
25
|
+
const [useAi, setUseAi] = useState(true);
|
|
23
26
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
24
27
|
|
|
25
28
|
useEffect(() => {
|
|
@@ -33,6 +36,24 @@ export default function CreateSpaceModal({ t, dirPaths }: { t: ReturnType<typeof
|
|
|
33
36
|
return () => window.removeEventListener('mindos:create-space', handler);
|
|
34
37
|
}, []);
|
|
35
38
|
|
|
39
|
+
// Check AI availability when modal opens
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!open || aiAvailable !== null) return;
|
|
42
|
+
apiFetch<{ ai?: { provider?: string; providers?: Record<string, { apiKey?: string }> } }>('/api/settings')
|
|
43
|
+
.then(data => {
|
|
44
|
+
const provider = data.ai?.provider ?? '';
|
|
45
|
+
const providers = data.ai?.providers ?? {};
|
|
46
|
+
const activeProvider = providers[provider as keyof typeof providers];
|
|
47
|
+
const hasKey = !!(activeProvider?.apiKey);
|
|
48
|
+
setAiAvailable(hasKey);
|
|
49
|
+
setUseAi(hasKey);
|
|
50
|
+
})
|
|
51
|
+
.catch(() => {
|
|
52
|
+
setAiAvailable(false);
|
|
53
|
+
setUseAi(false);
|
|
54
|
+
});
|
|
55
|
+
}, [open, aiAvailable]);
|
|
56
|
+
|
|
36
57
|
const close = useCallback(() => {
|
|
37
58
|
setOpen(false);
|
|
38
59
|
setName('');
|
|
@@ -40,6 +61,7 @@ export default function CreateSpaceModal({ t, dirPaths }: { t: ReturnType<typeof
|
|
|
40
61
|
setParent('');
|
|
41
62
|
setError('');
|
|
42
63
|
setNameHint('');
|
|
64
|
+
setAiAvailable(null); // re-check on next open
|
|
43
65
|
}, []);
|
|
44
66
|
|
|
45
67
|
const validateName = useCallback((val: string) => {
|
|
@@ -64,9 +86,26 @@ export default function CreateSpaceModal({ t, dirPaths }: { t: ReturnType<typeof
|
|
|
64
86
|
setError('');
|
|
65
87
|
const trimmed = name.trim();
|
|
66
88
|
const result = await createSpaceAction(trimmed, description, parent);
|
|
67
|
-
setLoading(false);
|
|
68
89
|
if (result.success) {
|
|
69
90
|
const createdPath = result.path ?? trimmed;
|
|
91
|
+
|
|
92
|
+
// If AI is enabled, trigger AI initialization in background
|
|
93
|
+
if (useAi && aiAvailable) {
|
|
94
|
+
const isZh = document.documentElement.lang === 'zh';
|
|
95
|
+
const prompt = isZh
|
|
96
|
+
? `初始化新建的心智空间「${trimmed}」,路径为「${createdPath}/」。${description ? `描述:「${description}」。` : ''}请根据空间名称生成有意义的内容:\n1. README.md — 空间用途、结构概览、使用指南\n2. INSTRUCTION.md — AI Agent 在此空间的行为规则\n\n使用可用工具直接写入文件,内容简洁实用。`
|
|
97
|
+
: `Initialize this new Mind Space "${trimmed}" at "${createdPath}/". ${description ? `Description: "${description}". ` : ''}Generate meaningful content for:\n1. README.md — purpose, structure overview, usage guidelines\n2. INSTRUCTION.md — rules for AI agents operating in this space\n\nWrite directly to the files using available tools. Keep content concise and actionable.`;
|
|
98
|
+
// Fire and forget — don't block navigation
|
|
99
|
+
apiFetch('/api/ask', {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers: { 'Content-Type': 'application/json' },
|
|
102
|
+
body: JSON.stringify({
|
|
103
|
+
messages: [{ role: 'user', content: prompt }],
|
|
104
|
+
targetDir: createdPath,
|
|
105
|
+
}),
|
|
106
|
+
}).catch(() => { /* AI init is best-effort */ });
|
|
107
|
+
}
|
|
108
|
+
|
|
70
109
|
close();
|
|
71
110
|
router.refresh();
|
|
72
111
|
router.push(`/view/${encodePath(createdPath + '/')}`);
|
|
@@ -78,7 +117,8 @@ export default function CreateSpaceModal({ t, dirPaths }: { t: ReturnType<typeof
|
|
|
78
117
|
setError(t.home.spaceCreateFailed ?? 'Failed to create space');
|
|
79
118
|
}
|
|
80
119
|
}
|
|
81
|
-
|
|
120
|
+
setLoading(false);
|
|
121
|
+
}, [name, description, parent, loading, close, router, t, validateName, useAi, aiAvailable]);
|
|
82
122
|
|
|
83
123
|
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
|
|
84
124
|
if (e.key === 'Escape') close();
|
|
@@ -87,6 +127,8 @@ export default function CreateSpaceModal({ t, dirPaths }: { t: ReturnType<typeof
|
|
|
87
127
|
|
|
88
128
|
if (!open) return null;
|
|
89
129
|
|
|
130
|
+
const h = t.home;
|
|
131
|
+
|
|
90
132
|
return createPortal(
|
|
91
133
|
<div className="fixed inset-0 z-50 flex items-center justify-center" onKeyDown={handleKeyDown}>
|
|
92
134
|
{/* Backdrop */}
|
|
@@ -95,31 +137,31 @@ export default function CreateSpaceModal({ t, dirPaths }: { t: ReturnType<typeof
|
|
|
95
137
|
<div
|
|
96
138
|
role="dialog"
|
|
97
139
|
aria-modal="true"
|
|
98
|
-
aria-label={
|
|
140
|
+
aria-label={h.newSpace}
|
|
99
141
|
className="relative w-full max-w-md mx-4 rounded-2xl border border-border bg-card shadow-xl"
|
|
100
142
|
>
|
|
101
143
|
{/* Header */}
|
|
102
144
|
<div className="flex items-center justify-between px-5 pt-5 pb-3">
|
|
103
|
-
<h3 className="text-sm font-semibold font-display text-foreground">{
|
|
145
|
+
<h3 className="text-sm font-semibold font-display text-foreground">{h.newSpace}</h3>
|
|
104
146
|
<button onClick={close} className="p-1 rounded-md text-muted-foreground hover:bg-muted transition-colors">
|
|
105
147
|
<X size={14} />
|
|
106
148
|
</button>
|
|
107
149
|
</div>
|
|
108
150
|
{/* Body */}
|
|
109
151
|
<div className="px-5 pb-5 flex flex-col gap-3">
|
|
110
|
-
{/* Location
|
|
152
|
+
{/* Location */}
|
|
111
153
|
<div className="space-y-1">
|
|
112
|
-
<label className="text-xs font-medium text-muted-foreground">{
|
|
154
|
+
<label className="text-xs font-medium text-muted-foreground">{h.spaceLocation ?? 'Location'}</label>
|
|
113
155
|
<DirPicker
|
|
114
156
|
dirPaths={dirPaths}
|
|
115
157
|
value={parent}
|
|
116
158
|
onChange={setParent}
|
|
117
|
-
rootLabel={
|
|
159
|
+
rootLabel={h.rootLevel ?? 'Root'}
|
|
118
160
|
/>
|
|
119
161
|
</div>
|
|
120
162
|
{/* Name */}
|
|
121
163
|
<div className="space-y-1">
|
|
122
|
-
<label className="text-xs font-medium text-muted-foreground">{
|
|
164
|
+
<label className="text-xs font-medium text-muted-foreground">{h.spaceName}</label>
|
|
123
165
|
<input
|
|
124
166
|
ref={inputRef}
|
|
125
167
|
type="text"
|
|
@@ -138,17 +180,49 @@ export default function CreateSpaceModal({ t, dirPaths }: { t: ReturnType<typeof
|
|
|
138
180
|
{/* Description */}
|
|
139
181
|
<div className="space-y-1">
|
|
140
182
|
<label className="text-xs font-medium text-muted-foreground">
|
|
141
|
-
{
|
|
183
|
+
{h.spaceDescription} <span className="opacity-50">({h.optional ?? 'optional'})</span>
|
|
142
184
|
</label>
|
|
143
185
|
<input
|
|
144
186
|
type="text"
|
|
145
187
|
value={description}
|
|
146
188
|
onChange={e => setDescription(e.target.value)}
|
|
147
|
-
placeholder={
|
|
189
|
+
placeholder={h.spaceDescPlaceholder ?? 'Describe the purpose of this space'}
|
|
148
190
|
maxLength={200}
|
|
149
191
|
className="w-full px-3 py-2 text-sm rounded-lg border border-border bg-background text-muted-foreground outline-none focus-visible:ring-1 focus-visible:ring-ring transition-colors"
|
|
150
192
|
/>
|
|
151
193
|
</div>
|
|
194
|
+
{/* AI initialization toggle */}
|
|
195
|
+
<div className="flex items-start gap-2.5 px-1 py-1">
|
|
196
|
+
<button
|
|
197
|
+
type="button"
|
|
198
|
+
role="switch"
|
|
199
|
+
aria-checked={useAi}
|
|
200
|
+
disabled={!aiAvailable}
|
|
201
|
+
onClick={() => setUseAi(v => !v)}
|
|
202
|
+
className={`relative mt-0.5 inline-flex shrink-0 h-4 w-7 cursor-pointer rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 ${
|
|
203
|
+
useAi ? 'bg-amber-500' : 'bg-muted'
|
|
204
|
+
}`}
|
|
205
|
+
>
|
|
206
|
+
<span className={`pointer-events-none inline-block h-3 w-3 rounded-full bg-white shadow-sm transition-transform ${useAi ? 'translate-x-3' : 'translate-x-0'}`} />
|
|
207
|
+
</button>
|
|
208
|
+
<div className="flex-1 min-w-0">
|
|
209
|
+
<div className="flex items-center gap-1.5">
|
|
210
|
+
<Sparkles size={12} className="text-[var(--amber)] shrink-0" />
|
|
211
|
+
<span className="text-xs font-medium text-foreground">{h.aiInit ?? 'AI initialize content'}</span>
|
|
212
|
+
</div>
|
|
213
|
+
{aiAvailable === false && (
|
|
214
|
+
<p className="text-2xs text-muted-foreground mt-0.5 flex items-center gap-1">
|
|
215
|
+
<AlertTriangle size={10} className="text-amber-500 shrink-0" />
|
|
216
|
+
{h.aiInitNoKey ?? 'Configure an API key in Settings → AI to enable'}
|
|
217
|
+
</p>
|
|
218
|
+
)}
|
|
219
|
+
{aiAvailable && useAi && (
|
|
220
|
+
<p className="text-2xs text-muted-foreground mt-0.5">
|
|
221
|
+
{h.aiInitHint ?? 'AI will generate README and INSTRUCTION for this space'}
|
|
222
|
+
</p>
|
|
223
|
+
)}
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
152
226
|
{/* Path preview */}
|
|
153
227
|
{fullPathPreview && (
|
|
154
228
|
<div className="flex items-center gap-1.5 text-xs text-muted-foreground font-mono px-1">
|
|
@@ -163,7 +237,7 @@ export default function CreateSpaceModal({ t, dirPaths }: { t: ReturnType<typeof
|
|
|
163
237
|
onClick={close}
|
|
164
238
|
className="px-4 py-2 rounded-lg text-sm font-medium text-muted-foreground transition-colors hover:bg-muted"
|
|
165
239
|
>
|
|
166
|
-
{
|
|
240
|
+
{h.cancelCreate}
|
|
167
241
|
</button>
|
|
168
242
|
<button
|
|
169
243
|
onClick={handleCreate}
|
|
@@ -171,7 +245,7 @@ export default function CreateSpaceModal({ t, dirPaths }: { t: ReturnType<typeof
|
|
|
171
245
|
className="flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium bg-[var(--amber)] text-white transition-colors hover:opacity-90 disabled:opacity-40 disabled:cursor-not-allowed"
|
|
172
246
|
>
|
|
173
247
|
{loading && <Loader2 size={14} className="animate-spin" />}
|
|
174
|
-
{
|
|
248
|
+
{h.createSpace}
|
|
175
249
|
</button>
|
|
176
250
|
</div>
|
|
177
251
|
</div>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useMemo } from 'react';
|
|
4
4
|
import { Folder, ChevronDown, ChevronRight } from 'lucide-react';
|
|
5
|
+
import { stripEmoji } from '@/lib/utils';
|
|
5
6
|
|
|
6
7
|
interface DirPickerProps {
|
|
7
8
|
/** Flat list of all directory paths (e.g. ['Notes', 'Notes/Daily', 'Projects']) */
|
|
@@ -48,7 +49,7 @@ export default function DirPicker({ dirPaths, value, onChange, rootLabel = 'Root
|
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
const displayLabel = value
|
|
51
|
-
? value.split('/').map(s =>
|
|
52
|
+
? value.split('/').map(s => stripEmoji(s)).join(' / ')
|
|
52
53
|
: '/ ' + rootLabel;
|
|
53
54
|
|
|
54
55
|
if (!expanded) {
|