@geminilight/mindos 0.6.34 → 0.6.36
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/.env.local.example +2 -2
- package/_standalone/.mindos-build-version +1 -1
- package/_standalone/.next/BUILD_ID +1 -1
- package/_standalone/.next/app-path-routes-manifest.json +15 -15
- package/_standalone/.next/build-manifest.json +2 -2
- package/_standalone/.next/cache/.previewinfo +1 -1
- package/_standalone/.next/cache/.rscinfo +1 -1
- package/_standalone/.next/cache/config.json +3 -3
- package/_standalone/.next/prerender-manifest.json +3 -3
- package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error.html +2 -2
- package/_standalone/.next/server/app/_global-error.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/_standalone/.next/server/app/_not-found/page.js +1 -1
- package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/page.js +1 -1
- package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask/route.js +5 -5
- package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route.js +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/export/route.js +1 -1
- package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/git/route.js +1 -1
- package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/recent-files/route.js +1 -1
- package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/route.js +1 -1
- package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/changes/page.js +1 -1
- package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/page.js +1 -1
- package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/explore/page.js +1 -1
- package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/help/page.js +1 -1
- package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/login/page.js +1 -1
- package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/page.js +1 -1
- package/_standalone/.next/server/app/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/setup/page.js +2 -2
- package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/trash/page.js +3 -3
- package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/view/[...path]/page.js +2 -2
- package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app-paths-manifest.json +15 -15
- package/_standalone/.next/server/chunks/4931.js +27 -27
- package/_standalone/.next/server/chunks/6539.js +1 -1
- package/_standalone/.next/server/chunks/7670.js +1 -1
- package/_standalone/.next/server/chunks/{1225.js → 9360.js} +2 -2
- package/_standalone/.next/server/pages/500.html +2 -2
- package/_standalone/.next/server/server-reference-manifest.js +1 -1
- package/_standalone/.next/server/server-reference-manifest.json +1 -1
- package/_standalone/.next/static/chunks/{1263-31c1efe3fd8f3f99.js → 1263-79beb8734dee7bbd.js} +2 -2
- package/_standalone/.next/static/chunks/app/{layout-1ad561312a9a5b5b.js → layout-d0f6dc4d3e6f0be4.js} +11 -11
- package/_standalone/.next/static/chunks/app/{page-e2a6e96831efa703.js → page-404b0bfb5721b7f8.js} +2 -2
- package/_standalone/.next/static/chunks/app/setup/page-01ab1f549d636057.js +1 -0
- package/_standalone/.next/static/chunks/app/trash/{page-c6e9de9ca4ab4bf7.js → page-a79804c1df44d3cc.js} +1 -1
- package/_standalone/.next/static/chunks/app/view/[...path]/{page-ff57a587c3f5490e.js → page-d3aceca36a1a9eb2.js} +2 -2
- package/_standalone/.next/trace +53 -53
- package/_standalone/components/SyncStatusBar.tsx +1 -1
- package/_standalone/components/TableOfContents.tsx +7 -0
- package/_standalone/components/setup/StepAI.tsx +66 -3
- package/_standalone/components/setup/StepDots.tsx +18 -10
- package/_standalone/components/setup/StepKB.tsx +26 -0
- package/_standalone/components/setup/constants.tsx +4 -3
- package/_standalone/data/skills/mindos/SKILL.md +4 -22
- package/_standalone/data/skills/mindos-zh/SKILL.md +2 -20
- package/_standalone/lib/agent/context.ts +29 -20
- package/_standalone/lib/i18n/modules/onboarding.ts +14 -6
- package/_standalone/tsconfig.tsbuildinfo +1 -1
- package/app/app/api/ask/route.ts +77 -24
- package/app/app/api/bootstrap/route.ts +4 -0
- package/app/app/api/export/route.ts +5 -0
- package/app/app/api/git/route.ts +2 -1
- package/app/app/api/recent-files/route.ts +3 -2
- package/app/app/api/setup/route.ts +1 -0
- package/app/components/SyncStatusBar.tsx +1 -1
- package/app/components/TableOfContents.tsx +7 -0
- package/app/components/setup/StepAI.tsx +66 -3
- package/app/components/setup/StepDots.tsx +18 -10
- package/app/components/setup/StepKB.tsx +26 -0
- package/app/components/setup/constants.tsx +4 -3
- package/app/components/setup/index.tsx +22 -32
- package/app/data/skills/mindos/SKILL.md +4 -22
- package/app/data/skills/mindos-zh/SKILL.md +2 -20
- package/app/lib/acp/subprocess.ts +3 -2
- package/app/lib/agent/context.ts +29 -20
- package/app/lib/fs.ts +77 -0
- package/app/lib/i18n/modules/onboarding.ts +14 -6
- package/app/lib/settings.ts +6 -0
- package/bin/cli.js +14 -4
- package/bin/lib/stop.js +5 -2
- package/package.json +6 -2
- package/scripts/setup.js +16 -0
- package/skills/mindos/SKILL.md +4 -22
- package/skills/mindos/references/write-supplement.md +35 -0
- package/skills/mindos-zh/SKILL.md +2 -20
- package/skills/mindos-zh/references/README.md +2 -1
- package/skills/mindos-zh/references/write-supplement.md +35 -0
- package/_standalone/.next/static/chunks/app/setup/page-f8a85accc3be554f.js +0 -1
- /package/_standalone/.next/static/{K1Y12u2jxCR7efUgJVpyl → VWZDVb8DL1Pbykh9EOj_Y}/_buildManifest.js +0 -0
- /package/_standalone/.next/static/{K1Y12u2jxCR7efUgJVpyl → VWZDVb8DL1Pbykh9EOj_Y}/_ssgManifest.js +0 -0
|
@@ -95,21 +95,12 @@ description: >
|
|
|
95
95
|
| 列出顶层心智空间 | `mindos_list_spaces` | 只需分区概览却拉整棵 `list_files` |
|
|
96
96
|
| 找文件 | `mindos_search_notes`(2-4 条并行关键词变体)| 单关键词搜索 |
|
|
97
97
|
| 读内容 | `mindos_read_file` 或 `mindos_read_lines`(大文件) | 只需 10 行却读整文件 |
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
| 整文件替换 | `mindos_write_file` | 用它做章节级编辑 |
|
|
101
|
-
| 新建文件 | `mindos_create_file` | 自动创建父目录,但不会生成空间脚手架文件 |
|
|
102
|
-
| 新建心智空间(目录 + README + INSTRUCTION)| `mindos_create_space` | 创建空间的唯一方式。`create_file` 只创建普通目录 |
|
|
103
|
-
| 重命名空间目录 | `mindos_rename_space` | `rename_file`(仅文件,不能重命名文件夹)|
|
|
104
|
-
| 追加 CSV | `mindos_append_csv`(校验表头)| 手动拼字符串不校验 |
|
|
105
|
-
| 重命名前查影响 | `mindos_get_backlinks` | 不查引用就重命名 |
|
|
106
|
-
| 查看近期变动 | `mindos_get_recent` | 猜最近改了什么 |
|
|
107
|
-
| 恢复历史版本 | `mindos_get_file_at_version` | 让用户回忆之前内容 |
|
|
98
|
+
|
|
99
|
+
写入、结构变更、历史工具 → [references/write-supplement.md](./references/write-supplement.md)。
|
|
108
100
|
|
|
109
101
|
### 回退
|
|
110
102
|
|
|
111
103
|
- `mindos_bootstrap` 不可用 → 手动读根 `INSTRUCTION.md` + `README.md`。
|
|
112
|
-
- 行级/章节级工具不可用 → 读 + 受限 `mindos_write_file`(模拟最小修改)。
|
|
113
104
|
- 搜索无结果 → 不放弃:(1) 扫上下文中的树;(2) 直接读候选文件;(3) `mindos_list_files` 细化子目录;(4) 用同义词/中英文变体重试。
|
|
114
105
|
|
|
115
106
|
---
|
|
@@ -119,15 +110,6 @@ description: >
|
|
|
119
110
|
| 模式 | 适用场景 | 关键步骤 |
|
|
120
111
|
|------|----------|----------|
|
|
121
112
|
| **只读问答** | 查找 / 总结 / 引用 | 目录树推断 → 搜索 → 读取 → 标注来源 → 明确说信息缺口 |
|
|
122
|
-
| **单文件编辑** | 目标文件明确 | 启动协议 → 读目标 + 局部约定 → 最小修改 → 验证 → 总结 |
|
|
123
|
-
| **多文件路由** | 非结构化输入,多个目的地 | 解析为语义单元 → 路由表 → 确认 → 编辑 → 汇总 |
|
|
124
|
-
| **对话复盘** | 提炼 / 沉淀会话 | 确认范围 → 抽取决策/踩坑/下一步 → 路由 → 记录变更 |
|
|
125
|
-
| **SOP 执行** | 可重复流程 | 完整读 SOP → 分步执行 → 更新过时段落 → 偏差则提议更新 |
|
|
126
|
-
| **结构变更** | 重命名 / 移动 / 删除 | `get_backlinks` → 影响报告 → 确认 → 执行 → 更新引用 → 同步 README |
|
|
127
|
-
| **CSV 追加** | 追加表格行 | 读表头 → 校验字段 → `mindos_append_csv` |
|
|
128
|
-
| **跨 Agent 接力** | 继续其他 Agent 的工作 | 读任务状态+决策 → 无需重新探索直接接续 → 回写进度 |
|
|
129
|
-
| **周期性回顾** | 汇总近期变动 | `get_recent`/`get_history` → 读变动文件 → 结构化总结 |
|
|
130
|
-
| **交接文档** | 创建简报 | 读来源 → 合成(背景、决策、状态、待办)→ 放项目目录 |
|
|
131
113
|
|
|
132
114
|
写入模式详细执行步骤 → [references/write-supplement.md](./references/write-supplement.md)。
|
|
133
115
|
|
|
@@ -451,14 +451,15 @@ export function installAutoApproval(acpProc: AcpProcess): () => void {
|
|
|
451
451
|
|
|
452
452
|
// ── Permission requests (auto-approve all) ──
|
|
453
453
|
case 'session/request_permission': {
|
|
454
|
-
|
|
454
|
+
// Auto-approve in production; log in dev for debugging
|
|
455
|
+
if (process.env.NODE_ENV === 'development') console.log(`[ACP] Auto-approving permission: ${JSON.stringify(params.toolCall ?? {}).slice(0, 200)}`);
|
|
455
456
|
sendResponse(acpProc, req.id, { outcome: { selected: { optionId: 'allow_once' } } });
|
|
456
457
|
return;
|
|
457
458
|
}
|
|
458
459
|
|
|
459
460
|
// ── Unknown methods: auto-approve for backwards compat ──
|
|
460
461
|
default: {
|
|
461
|
-
console.log(`[ACP] Auto-approving unknown agent request: ${method} (id=${req.id})`);
|
|
462
|
+
if (process.env.NODE_ENV === 'development') console.log(`[ACP] Auto-approving unknown agent request: ${method} (id=${req.id})`);
|
|
462
463
|
sendResponse(acpProc, req.id, {});
|
|
463
464
|
}
|
|
464
465
|
}
|
package/app/lib/agent/context.ts
CHANGED
|
@@ -9,6 +9,15 @@ import type { AgentMessage } from '@mariozechner/pi-agent-core';
|
|
|
9
9
|
import type { ToolResultMessage, AssistantMessage, UserMessage } from '@mariozechner/pi-ai';
|
|
10
10
|
import { countCjkChars } from '@/lib/core/cjk';
|
|
11
11
|
|
|
12
|
+
const DEV = process.env.NODE_ENV === 'development';
|
|
13
|
+
|
|
14
|
+
// AgentMessage is opaque; cast to access role/content at runtime.
|
|
15
|
+
interface AgentMessageFields {
|
|
16
|
+
role: string;
|
|
17
|
+
content: string | Array<{ type: string; text?: string }>;
|
|
18
|
+
}
|
|
19
|
+
function asMsg(m: AgentMessage): AgentMessageFields { return m as unknown as AgentMessageFields; }
|
|
20
|
+
|
|
12
21
|
// ---------------------------------------------------------------------------
|
|
13
22
|
// Token estimation — CJK-aware (CJK ~1.5 tokens/char, ASCII ~0.25 tokens/char)
|
|
14
23
|
// ---------------------------------------------------------------------------
|
|
@@ -28,7 +37,7 @@ export function estimateStringTokens(text: string): number {
|
|
|
28
37
|
/** Rough token count for a single AgentMessage */
|
|
29
38
|
function messageTokens(msg: AgentMessage): number {
|
|
30
39
|
if ('content' in msg) {
|
|
31
|
-
const content = (msg
|
|
40
|
+
const content = asMsg(msg).content;
|
|
32
41
|
if (typeof content === 'string') return estimateStringTokens(content);
|
|
33
42
|
if (Array.isArray(content)) {
|
|
34
43
|
let tokens = 0;
|
|
@@ -121,11 +130,11 @@ export function truncateToolOutputs(messages: AgentMessage[]): AgentMessage[] {
|
|
|
121
130
|
// Find the index of the last 'toolResult' role message
|
|
122
131
|
let lastToolIdx = -1;
|
|
123
132
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
124
|
-
if ((messages[i]
|
|
133
|
+
if (asMsg(messages[i]).role === 'toolResult') { lastToolIdx = i; break; }
|
|
125
134
|
}
|
|
126
135
|
|
|
127
136
|
return messages.map((msg, idx) => {
|
|
128
|
-
const m = msg
|
|
137
|
+
const m = asMsg(msg);
|
|
129
138
|
if (m.role !== 'toolResult' || idx === lastToolIdx) return msg;
|
|
130
139
|
|
|
131
140
|
const toolMsg = m as ToolResultMessage;
|
|
@@ -160,7 +169,7 @@ Be concise and factual. Output only the summary, no preamble.`;
|
|
|
160
169
|
|
|
161
170
|
/** Extract a short text representation from an AgentMessage for summarization */
|
|
162
171
|
function messageToText(m: AgentMessage): string {
|
|
163
|
-
const msg = m
|
|
172
|
+
const msg = asMsg(m);
|
|
164
173
|
const role = msg.role;
|
|
165
174
|
let content = '';
|
|
166
175
|
|
|
@@ -202,7 +211,7 @@ export async function compactMessages(
|
|
|
202
211
|
// Adjust split point to avoid cutting between an assistant (with tool calls)
|
|
203
212
|
// and its tool result.
|
|
204
213
|
let splitIdx = messages.length - 6;
|
|
205
|
-
while (splitIdx > 0 && (messages[splitIdx]
|
|
214
|
+
while (splitIdx > 0 && asMsg(messages[splitIdx]).role === 'toolResult') {
|
|
206
215
|
splitIdx--;
|
|
207
216
|
}
|
|
208
217
|
if (splitIdx < 2) {
|
|
@@ -230,17 +239,17 @@ export async function compactMessages(
|
|
|
230
239
|
|
|
231
240
|
const summaryText = summaryMessage.content
|
|
232
241
|
.filter(p => p.type === 'text')
|
|
233
|
-
.map(p => (p as
|
|
242
|
+
.map(p => (p as { text?: string }).text)
|
|
234
243
|
.join('');
|
|
235
244
|
|
|
236
|
-
console.log(`[ask] Compacted ${earlyMessages.length} early messages into summary (${summaryText.length} chars)`);
|
|
245
|
+
if (DEV) console.log(`[ask] Compacted ${earlyMessages.length} early messages into summary (${summaryText.length} chars)`);
|
|
237
246
|
|
|
238
247
|
const summaryContent = `[System Note: Older conversation history has been truncated due to context length limits, but here is an AI-generated summary of what was discussed so far.]\n\n${summaryText}`;
|
|
239
248
|
|
|
240
249
|
// If first recent message is also 'user', merge summary into it to avoid
|
|
241
250
|
// consecutive user messages (Anthropic rejects user→user sequences).
|
|
242
|
-
if ((recentMessages[0]
|
|
243
|
-
const merged = { ...(recentMessages[0] as
|
|
251
|
+
if (asMsg(recentMessages[0])?.role === 'user') {
|
|
252
|
+
const merged = { ...asMsg(recentMessages[0]) } as AgentMessageFields;
|
|
244
253
|
if (typeof merged.content === 'string') {
|
|
245
254
|
merged.content = `${summaryContent}\n\n---\n\n${merged.content}`;
|
|
246
255
|
} else if (Array.isArray(merged.content)) {
|
|
@@ -270,7 +279,7 @@ export async function compactMessages(
|
|
|
270
279
|
console.warn('[ask] Compact failed, applying hard prune as fallback:', err);
|
|
271
280
|
const pruned = hardPrune(messages, systemPrompt, modelName);
|
|
272
281
|
if (pruned.length < messages.length) {
|
|
273
|
-
console.log(`[ask] Hard prune fallback succeeded (${messages.length} → ${pruned.length} messages)`);
|
|
282
|
+
if (DEV) console.log(`[ask] Hard prune fallback succeeded (${messages.length} → ${pruned.length} messages)`);
|
|
274
283
|
return { messages: pruned, compacted: false };
|
|
275
284
|
}
|
|
276
285
|
// If pruning also can't help, let it bubble up so request fails safely
|
|
@@ -307,28 +316,28 @@ export function hardPrune(
|
|
|
307
316
|
}
|
|
308
317
|
|
|
309
318
|
// Ensure we don't cut between an assistant (with tool calls) and its tool result.
|
|
310
|
-
while (cutIdx < messages.length - 1 && (messages[cutIdx]
|
|
319
|
+
while (cutIdx < messages.length - 1 && asMsg(messages[cutIdx]).role === 'toolResult') {
|
|
311
320
|
total -= messageTokens(messages[cutIdx]);
|
|
312
321
|
cutIdx++;
|
|
313
322
|
}
|
|
314
323
|
|
|
315
324
|
// Ensure first message is 'user' (Anthropic requirement)
|
|
316
|
-
while (cutIdx < messages.length - 1 && (messages[cutIdx]
|
|
325
|
+
while (cutIdx < messages.length - 1 && asMsg(messages[cutIdx]).role !== 'user') {
|
|
317
326
|
total -= messageTokens(messages[cutIdx]);
|
|
318
327
|
cutIdx++;
|
|
319
328
|
}
|
|
320
329
|
|
|
321
330
|
// Fallback: if no user message found in remaining messages, inject a synthetic one
|
|
322
331
|
const pruned = cutIdx > 0 ? messages.slice(cutIdx) : messages;
|
|
323
|
-
if (pruned.length > 0 && (pruned[0]
|
|
324
|
-
console.log(`[ask] Hard pruned ${cutIdx} messages, injecting synthetic user message (${messages.length} → ${pruned.length + 1})`);
|
|
332
|
+
if (pruned.length > 0 && asMsg(pruned[0]).role !== 'user') {
|
|
333
|
+
if (DEV) console.log(`[ask] Hard pruned ${cutIdx} messages, injecting synthetic user message (${messages.length} → ${pruned.length + 1})`);
|
|
325
334
|
const syntheticUser: UserMessage = {
|
|
326
335
|
role: 'user',
|
|
327
336
|
content: '[System Note: Older conversation history has been truncated due to context length limits. The user may refer to things you can no longer see. If so, kindly ask them to repeat the context.]',
|
|
328
337
|
timestamp: Date.now(),
|
|
329
338
|
};
|
|
330
339
|
return [syntheticUser as AgentMessage, ...pruned];
|
|
331
|
-
} else if (cutIdx > 0 && pruned.length > 0 && (pruned[0]
|
|
340
|
+
} else if (cutIdx > 0 && pruned.length > 0 && asMsg(pruned[0]).role === 'user') {
|
|
332
341
|
// If we pruned and the first message IS a user message, prepend the warning to it
|
|
333
342
|
const firstMsg = { ...pruned[0] } as UserMessage;
|
|
334
343
|
firstMsg.content = `[System Note: Older conversation history has been truncated due to context length limits. The user may refer to things you can no longer see. If so, kindly ask them to repeat the context.]\n\n` + firstMsg.content;
|
|
@@ -336,7 +345,7 @@ export function hardPrune(
|
|
|
336
345
|
}
|
|
337
346
|
|
|
338
347
|
if (cutIdx > 0) {
|
|
339
|
-
console.log(`[ask] Hard pruned ${cutIdx} messages (${messages.length} → ${messages.length - cutIdx})`);
|
|
348
|
+
if (DEV) console.log(`[ask] Hard pruned ${cutIdx} messages (${messages.length} → ${messages.length - cutIdx})`);
|
|
340
349
|
return pruned;
|
|
341
350
|
}
|
|
342
351
|
|
|
@@ -365,11 +374,11 @@ export function createTransformContext(
|
|
|
365
374
|
const preTokens = estimateTokens(result);
|
|
366
375
|
const sysTokens = estimateStringTokens(systemPrompt);
|
|
367
376
|
const ctxLimit = getContextLimit(modelName);
|
|
368
|
-
console.log(`[ask] Context: ~${preTokens + sysTokens} tokens (messages=${preTokens}, system=${sysTokens}), limit=${ctxLimit}`);
|
|
377
|
+
if (DEV) console.log(`[ask] Context: ~${preTokens + sysTokens} tokens (messages=${preTokens}, system=${sysTokens}), limit=${ctxLimit}`);
|
|
369
378
|
|
|
370
379
|
// 2. Compact if >70% context limit (skip if user disabled)
|
|
371
380
|
if (contextStrategy === 'auto' && needsCompact(result, systemPrompt, modelName)) {
|
|
372
|
-
console.log('[ask] Context >70% limit, compacting...');
|
|
381
|
+
if (DEV) console.log('[ask] Context >70% limit, compacting...');
|
|
373
382
|
const compactResult = await compactMessages(
|
|
374
383
|
result,
|
|
375
384
|
getCompactModel(),
|
|
@@ -380,9 +389,9 @@ export function createTransformContext(
|
|
|
380
389
|
result = compactResult.messages;
|
|
381
390
|
if (compactResult.compacted) {
|
|
382
391
|
const postTokens = estimateTokens(result);
|
|
383
|
-
console.log(`[ask] After compact: ~${postTokens + sysTokens} tokens`);
|
|
392
|
+
if (DEV) console.log(`[ask] After compact: ~${postTokens + sysTokens} tokens`);
|
|
384
393
|
} else {
|
|
385
|
-
console.log('[ask] Compact skipped (too few messages or fallback used), hard prune will handle overflow if needed');
|
|
394
|
+
if (DEV) console.log('[ask] Compact skipped (too few messages or fallback used), hard prune will handle overflow if needed');
|
|
386
395
|
}
|
|
387
396
|
}
|
|
388
397
|
|
package/app/lib/fs.ts
CHANGED
|
@@ -323,10 +323,19 @@ export function listMindSpaces(): MindSpaceSummary[] {
|
|
|
323
323
|
return summarizeTopLevelSpaces(getMindRoot(), ensureCache().tree);
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
+
/** Appends a structured change event to the change log. */
|
|
326
327
|
export function appendContentChange(input: ContentChangeInput): ContentChangeEvent {
|
|
327
328
|
return coreAppendContentChange(getMindRoot(), input);
|
|
328
329
|
}
|
|
329
330
|
|
|
331
|
+
/**
|
|
332
|
+
* Lists content change events with optional filtering.
|
|
333
|
+
* @param options.path Filter by file path (prefix match)
|
|
334
|
+
* @param options.limit Max events to return (default: unlimited)
|
|
335
|
+
* @param options.source Filter by source: 'user' | 'agent' | 'system'
|
|
336
|
+
* @param options.op Filter by operation type (e.g. 'create', 'update', 'delete')
|
|
337
|
+
* @param options.q Free-text search within change descriptions
|
|
338
|
+
*/
|
|
330
339
|
export function listContentChanges(options: {
|
|
331
340
|
path?: string;
|
|
332
341
|
limit?: number;
|
|
@@ -337,10 +346,12 @@ export function listContentChanges(options: {
|
|
|
337
346
|
return coreListContentChanges(getMindRoot(), options);
|
|
338
347
|
}
|
|
339
348
|
|
|
349
|
+
/** Marks all unseen content changes as seen. */
|
|
340
350
|
export function markContentChangesSeen(): void {
|
|
341
351
|
coreMarkContentChangesSeen(getMindRoot());
|
|
342
352
|
}
|
|
343
353
|
|
|
354
|
+
/** Returns a summary of content changes (total, unseen count, latest timestamp). */
|
|
344
355
|
export function getContentChangeSummary(): ContentChangeSummary {
|
|
345
356
|
return coreGetContentChangeSummary(getMindRoot());
|
|
346
357
|
}
|
|
@@ -411,6 +422,10 @@ export function getDirEntries(dirPath: string): FileNode[] {
|
|
|
411
422
|
return nodes;
|
|
412
423
|
}
|
|
413
424
|
|
|
425
|
+
/**
|
|
426
|
+
* Returns the N most recently modified files.
|
|
427
|
+
* @param limit Max files to return (default: 10)
|
|
428
|
+
*/
|
|
414
429
|
export function getRecentlyModified(limit = 10): Array<{ path: string; mtime: number }> {
|
|
415
430
|
const root = getMindRoot();
|
|
416
431
|
const allFiles = collectAllFiles();
|
|
@@ -447,6 +462,10 @@ export function createFile(filePath: string, initialContent = ''): void {
|
|
|
447
462
|
invalidateCacheForNewFile(filePath);
|
|
448
463
|
}
|
|
449
464
|
|
|
465
|
+
/**
|
|
466
|
+
* Deletes a file and moves it to the trash.
|
|
467
|
+
* @returns Trash metadata for undo support
|
|
468
|
+
*/
|
|
450
469
|
export function deleteFile(filePath: string): void {
|
|
451
470
|
coreDeleteFile(getMindRoot(), filePath);
|
|
452
471
|
invalidateCacheForDeletedFile(filePath);
|
|
@@ -480,20 +499,41 @@ export function convertToSpace(dirPath: string): void {
|
|
|
480
499
|
|
|
481
500
|
// ─── Public API: Line-level operations (delegated to @mindos/core) ───────────
|
|
482
501
|
|
|
502
|
+
/**
|
|
503
|
+
* Reads all lines of a file as an array of strings.
|
|
504
|
+
* @param filePath Relative path from MIND_ROOT
|
|
505
|
+
*/
|
|
483
506
|
export function readLines(filePath: string): string[] {
|
|
484
507
|
return coreReadLines(getMindRoot(), filePath);
|
|
485
508
|
}
|
|
486
509
|
|
|
510
|
+
/**
|
|
511
|
+
* Inserts lines after the given index (0-based).
|
|
512
|
+
* @param filePath Relative path from MIND_ROOT
|
|
513
|
+
* @param afterIndex Insert after this line index (-1 = prepend)
|
|
514
|
+
* @param lines Lines to insert
|
|
515
|
+
*/
|
|
487
516
|
export function insertLines(filePath: string, afterIndex: number, lines: string[]): void {
|
|
488
517
|
coreInsertLines(getMindRoot(), filePath, afterIndex, lines);
|
|
489
518
|
invalidateCacheForFile(filePath);
|
|
490
519
|
}
|
|
491
520
|
|
|
521
|
+
/**
|
|
522
|
+
* Replaces lines in the range [startIndex, endIndex] (inclusive, 0-based).
|
|
523
|
+
* @param filePath Relative path from MIND_ROOT
|
|
524
|
+
* @param startIndex First line to replace
|
|
525
|
+
* @param endIndex Last line to replace
|
|
526
|
+
* @param newLines Replacement lines
|
|
527
|
+
*/
|
|
492
528
|
export function updateLines(filePath: string, startIndex: number, endIndex: number, newLines: string[]): void {
|
|
493
529
|
coreUpdateLines(getMindRoot(), filePath, startIndex, endIndex, newLines);
|
|
494
530
|
invalidateCacheForFile(filePath);
|
|
495
531
|
}
|
|
496
532
|
|
|
533
|
+
/**
|
|
534
|
+
* Deletes lines in the range [startIndex, endIndex] (inclusive, 0-based).
|
|
535
|
+
* @throws {MindOSError} If indices are out of range
|
|
536
|
+
*/
|
|
497
537
|
export function deleteLines(filePath: string, startIndex: number, endIndex: number): void {
|
|
498
538
|
const existing = readLines(filePath);
|
|
499
539
|
if (startIndex < 0 || endIndex < 0) throw new MindOSError(ErrorCodes.INVALID_RANGE, 'Invalid line index: indices must be >= 0', { startIndex, endIndex });
|
|
@@ -505,16 +545,19 @@ export function deleteLines(filePath: string, startIndex: number, endIndex: numb
|
|
|
505
545
|
|
|
506
546
|
// ─── Public API: High-level semantic operations (delegated to @mindos/core) ──
|
|
507
547
|
|
|
548
|
+
/** Appends content to the end of a file with a leading newline separator. */
|
|
508
549
|
export function appendToFile(filePath: string, content: string): void {
|
|
509
550
|
coreAppendToFile(getMindRoot(), filePath, content);
|
|
510
551
|
invalidateCacheForFile(filePath);
|
|
511
552
|
}
|
|
512
553
|
|
|
554
|
+
/** Inserts content after the first occurrence of a markdown heading. */
|
|
513
555
|
export function insertAfterHeading(filePath: string, heading: string, content: string): void {
|
|
514
556
|
coreInsertAfterHeading(getMindRoot(), filePath, heading, content);
|
|
515
557
|
invalidateCacheForFile(filePath);
|
|
516
558
|
}
|
|
517
559
|
|
|
560
|
+
/** Replaces the content of a markdown section (heading to next heading of same or higher level). */
|
|
518
561
|
export function updateSection(filePath: string, heading: string, newContent: string): void {
|
|
519
562
|
coreUpdateSection(getMindRoot(), filePath, heading, newContent);
|
|
520
563
|
invalidateCacheForFile(filePath);
|
|
@@ -668,6 +711,10 @@ function generateSnippet(
|
|
|
668
711
|
|
|
669
712
|
// ─── Public API: CSV (delegated to @mindos/core) ────────────────────────────
|
|
670
713
|
|
|
714
|
+
/**
|
|
715
|
+
* Appends a row to a CSV file.
|
|
716
|
+
* @returns Object with the new total row count
|
|
717
|
+
*/
|
|
671
718
|
export function appendCsvRow(filePath: string, row: string[]): { newRowCount: number } {
|
|
672
719
|
const result = coreAppendCsvRow(getMindRoot(), filePath, row);
|
|
673
720
|
invalidateCache();
|
|
@@ -676,6 +723,10 @@ export function appendCsvRow(filePath: string, row: string[]): { newRowCount: nu
|
|
|
676
723
|
|
|
677
724
|
// ─── Public API: Move file (delegated to @mindos/core) ──────────────────────
|
|
678
725
|
|
|
726
|
+
/**
|
|
727
|
+
* Moves a file from one path to another, updating internal wikilinks.
|
|
728
|
+
* @returns The new path and list of files whose links were updated
|
|
729
|
+
*/
|
|
679
730
|
export function moveFile(fromPath: string, toPath: string): { newPath: string; affectedFiles: string[] } {
|
|
680
731
|
const result = coreMoveFile(getMindRoot(), fromPath, toPath, coreFindBacklinks);
|
|
681
732
|
invalidateCache();
|
|
@@ -684,14 +735,25 @@ export function moveFile(fromPath: string, toPath: string): { newPath: string; a
|
|
|
684
735
|
|
|
685
736
|
// ─── Public API: Git operations (delegated to @mindos/core) ─────────────────
|
|
686
737
|
|
|
738
|
+
/** Returns whether the knowledge base root is a git repository. */
|
|
687
739
|
export function isGitRepo(): boolean {
|
|
688
740
|
return coreIsGitRepo(getMindRoot());
|
|
689
741
|
}
|
|
690
742
|
|
|
743
|
+
/**
|
|
744
|
+
* Returns git log entries for a file.
|
|
745
|
+
* @param filePath Relative path from MIND_ROOT
|
|
746
|
+
* @param limit Max entries (default: 10)
|
|
747
|
+
*/
|
|
691
748
|
export function gitLog(filePath: string, limit = 10): Array<{ hash: string; date: string; message: string; author: string }> {
|
|
692
749
|
return coreGitLog(getMindRoot(), filePath, limit);
|
|
693
750
|
}
|
|
694
751
|
|
|
752
|
+
/**
|
|
753
|
+
* Shows file content at a specific git commit.
|
|
754
|
+
* @param filePath Relative path from MIND_ROOT
|
|
755
|
+
* @param commit Git commit hash or ref
|
|
756
|
+
*/
|
|
695
757
|
export function gitShowFile(filePath: string, commit: string): string {
|
|
696
758
|
return coreGitShowFile(getMindRoot(), filePath, commit);
|
|
697
759
|
}
|
|
@@ -716,40 +778,55 @@ import {
|
|
|
716
778
|
} from './core/trash';
|
|
717
779
|
export type { TrashMeta } from './core/trash';
|
|
718
780
|
|
|
781
|
+
/** Moves a file to the .mindos/.trash/ directory for later recovery. */
|
|
719
782
|
export function moveToTrashFile(filePath: string) {
|
|
720
783
|
const result = coreMoveToTrash(getMindRoot(), filePath);
|
|
721
784
|
invalidateCache();
|
|
722
785
|
return result;
|
|
723
786
|
}
|
|
724
787
|
|
|
788
|
+
/**
|
|
789
|
+
* Restores a file from trash to its original path.
|
|
790
|
+
* @param trashId The trash entry ID
|
|
791
|
+
* @param overwrite If true, overwrite existing file at original path
|
|
792
|
+
*/
|
|
725
793
|
export function restoreFromTrash(trashId: string, overwrite = false) {
|
|
726
794
|
const result = coreRestoreFromTrash(getMindRoot(), trashId, overwrite);
|
|
727
795
|
invalidateCache();
|
|
728
796
|
return result;
|
|
729
797
|
}
|
|
730
798
|
|
|
799
|
+
/** Restores a file from trash as a copy (appends suffix to avoid conflict). */
|
|
731
800
|
export function restoreAsCopy(trashId: string) {
|
|
732
801
|
const result = coreRestoreAsCopy(getMindRoot(), trashId);
|
|
733
802
|
invalidateCache();
|
|
734
803
|
return result;
|
|
735
804
|
}
|
|
736
805
|
|
|
806
|
+
/** Permanently deletes a file from trash (no recovery possible). */
|
|
737
807
|
export function permanentlyDeleteFromTrash(trashId: string) {
|
|
738
808
|
corePermanentlyDelete(getMindRoot(), trashId);
|
|
739
809
|
}
|
|
740
810
|
|
|
811
|
+
/** Lists all items currently in the trash. */
|
|
741
812
|
export function listTrash() {
|
|
742
813
|
return coreListTrash(getMindRoot());
|
|
743
814
|
}
|
|
744
815
|
|
|
816
|
+
/** Permanently deletes all items in the trash. */
|
|
745
817
|
export function emptyTrashAll() {
|
|
746
818
|
return coreEmptyTrash(getMindRoot());
|
|
747
819
|
}
|
|
748
820
|
|
|
821
|
+
/** Removes trash items older than 30 days. Called automatically on listTrash. */
|
|
749
822
|
export function purgeExpiredTrash() {
|
|
750
823
|
return corePurgeExpired(getMindRoot());
|
|
751
824
|
}
|
|
752
825
|
|
|
826
|
+
/**
|
|
827
|
+
* Finds all files that link to the given target path via wikilinks.
|
|
828
|
+
* Uses the pre-built LinkIndex for O(1) source lookup.
|
|
829
|
+
*/
|
|
753
830
|
export function findBacklinks(targetPath: string): BacklinkEntry[] {
|
|
754
831
|
const mindRoot = getMindRoot();
|
|
755
832
|
// Use LinkIndex for O(1) source lookup, then only scan matching files
|
|
@@ -15,7 +15,7 @@ export const onboardingEn = {
|
|
|
15
15
|
dismiss: 'Dismiss',
|
|
16
16
|
},
|
|
17
17
|
setup: {
|
|
18
|
-
stepTitles: ['Knowledge Base', 'AI
|
|
18
|
+
stepTitles: ['Knowledge Base', 'AI Configuration', 'Agent Tools', 'Confirm'],
|
|
19
19
|
// Step 1
|
|
20
20
|
kbPath: 'Knowledge base path',
|
|
21
21
|
kbPathHint: 'Absolute path to your notes directory.',
|
|
@@ -56,8 +56,12 @@ export const onboardingEn = {
|
|
|
56
56
|
generateToken: 'Generate',
|
|
57
57
|
copyToken: 'Copy',
|
|
58
58
|
copiedToken: 'Copied!',
|
|
59
|
-
webPassword: 'Web
|
|
60
|
-
webPasswordHint: '
|
|
59
|
+
webPassword: 'Web Password',
|
|
60
|
+
webPasswordHint: 'Protect your knowledge base from unauthorized browser access.',
|
|
61
|
+
webPasswordRequired: 'Password is required to protect your data.',
|
|
62
|
+
advancedPorts: 'Advanced Settings',
|
|
63
|
+
tokenSectionTitle: 'MCP Auth Token (auto-generated)',
|
|
64
|
+
tokenSectionHint: 'Used by AI agents to connect to your MindOS server.',
|
|
61
65
|
// Step 5 — Agent Tools
|
|
62
66
|
agentToolsTitle: 'Agent Tools',
|
|
63
67
|
agentToolsHint: 'Select AI agents to configure with MindOS MCP. Agents marked "not installed" can be configured now — they will work once you install the app.',
|
|
@@ -210,7 +214,7 @@ export const onboardingZh = {
|
|
|
210
214
|
dismiss: '关闭',
|
|
211
215
|
},
|
|
212
216
|
setup: {
|
|
213
|
-
stepTitles: ['知识库', 'AI
|
|
217
|
+
stepTitles: ['知识库', 'AI 配置', 'Agent 工具', '确认'],
|
|
214
218
|
// Step 1
|
|
215
219
|
kbPath: '知识库路径',
|
|
216
220
|
kbPathHint: '笔记目录的绝对路径。',
|
|
@@ -251,8 +255,12 @@ export const onboardingZh = {
|
|
|
251
255
|
generateToken: '生成',
|
|
252
256
|
copyToken: '复制',
|
|
253
257
|
copiedToken: '已复制!',
|
|
254
|
-
webPassword: '
|
|
255
|
-
webPasswordHint: '
|
|
258
|
+
webPassword: '访问密码',
|
|
259
|
+
webPasswordHint: '保护你的知识库,防止未授权的浏览器访问。',
|
|
260
|
+
webPasswordRequired: '密码是必填项,用于保护你的数据。',
|
|
261
|
+
advancedPorts: '高级设置',
|
|
262
|
+
tokenSectionTitle: 'MCP 认证令牌(已自动生成)',
|
|
263
|
+
tokenSectionHint: 'AI Agent 连接你的 MindOS 服务器时使用。',
|
|
256
264
|
// Step 5 — Agent Tools
|
|
257
265
|
agentToolsTitle: 'Agent 工具',
|
|
258
266
|
agentToolsHint: '选择要与 MindOS MCP 配置的 AI Agent。标注「未安装」的 agent 可以先行配置,安装应用后即可生效。',
|
package/app/lib/settings.ts
CHANGED
|
@@ -48,6 +48,7 @@ export interface ServerSettings {
|
|
|
48
48
|
webPassword?: string;
|
|
49
49
|
startMode?: 'dev' | 'start' | 'daemon';
|
|
50
50
|
setupPending?: boolean; // true → / redirects to /setup
|
|
51
|
+
setupPort?: number; // temporary port used by GUI setup; cleared on completion
|
|
51
52
|
disabledSkills?: string[];
|
|
52
53
|
guideState?: GuideState;
|
|
53
54
|
/** Per-agent ACP overrides (command, args, env, enabled). Keyed by agent ID. */
|
|
@@ -204,6 +205,11 @@ export function writeSettings(settings: ServerSettings): void {
|
|
|
204
205
|
if (settings.setupPending) merged.setupPending = true;
|
|
205
206
|
else delete merged.setupPending;
|
|
206
207
|
}
|
|
208
|
+
// setupPort: clear when explicitly set to undefined/0 (setup completed)
|
|
209
|
+
if ('setupPort' in settings) {
|
|
210
|
+
if (settings.setupPort) merged.setupPort = settings.setupPort;
|
|
211
|
+
else delete merged.setupPort;
|
|
212
|
+
}
|
|
207
213
|
fs.writeFileSync(SETTINGS_PATH, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
|
|
208
214
|
}
|
|
209
215
|
|
package/bin/cli.js
CHANGED
|
@@ -53,7 +53,7 @@ import { loadConfig, getStartMode, isDaemonMode } from './lib/config.js';
|
|
|
53
53
|
import { needsBuild, writeBuildStamp, cleanNextDir, ensureAppDeps, hasPrebuiltStandalone } from './lib/build.js';
|
|
54
54
|
import { isPortInUse, assertPortFree } from './lib/port.js';
|
|
55
55
|
import { savePids, clearPids } from './lib/pid.js';
|
|
56
|
-
import { stopMindos } from './lib/stop.js';
|
|
56
|
+
import { stopMindos, killByPort } from './lib/stop.js';
|
|
57
57
|
import { printStartupInfo, getLocalIP } from './lib/startup.js';
|
|
58
58
|
import { spawnMcp } from './lib/mcp-spawn.js';
|
|
59
59
|
import { parseArgs, EXIT } from './lib/command.js';
|
|
@@ -360,12 +360,22 @@ const commands = {
|
|
|
360
360
|
const webPort = process.env.MINDOS_WEB_PORT;
|
|
361
361
|
const mcpPort = process.env.MINDOS_MCP_PORT;
|
|
362
362
|
|
|
363
|
+
// Clean up zombie processes from an abandoned GUI setup session.
|
|
364
|
+
// setup.js records a temporary port (setupPort) in config; if the user
|
|
365
|
+
// closed the browser without completing setup, that process is still
|
|
366
|
+
// running. Kill it before we proceed.
|
|
367
|
+
// Also read config for auto-migration below (avoids double readFileSync).
|
|
368
|
+
let startupCfg = {};
|
|
369
|
+
try { startupCfg = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch {}
|
|
370
|
+
if (startupCfg.setupPort && Number(startupCfg.setupPort) !== Number(webPort)) {
|
|
371
|
+
killByPort(Number(startupCfg.setupPort));
|
|
372
|
+
}
|
|
373
|
+
|
|
363
374
|
// ── Auto-migrate user-rules.md to root user-skill-rules.md ─────────────
|
|
364
375
|
try {
|
|
365
|
-
const
|
|
366
|
-
const mr = cfg.mindRoot;
|
|
376
|
+
const mr = startupCfg.mindRoot;
|
|
367
377
|
if (mr && existsSync(mr)) {
|
|
368
|
-
const isZh =
|
|
378
|
+
const isZh = startupCfg.disabledSkills?.includes('mindos');
|
|
369
379
|
const sName = isZh ? 'mindos-zh' : 'mindos';
|
|
370
380
|
const sDir = resolve(mr, '.agents', 'skills', sName);
|
|
371
381
|
const rootUserRules = resolve(mr, 'user-skill-rules.md');
|
package/bin/lib/stop.js
CHANGED
|
@@ -9,7 +9,7 @@ import { CONFIG_PATH } from './constants.js';
|
|
|
9
9
|
* Tries lsof first, then falls back to parsing `ss` output.
|
|
10
10
|
* Returns number of processes killed.
|
|
11
11
|
*/
|
|
12
|
-
function killByPort(port) {
|
|
12
|
+
export function killByPort(port) {
|
|
13
13
|
const pidsToKill = new Set();
|
|
14
14
|
|
|
15
15
|
// Method 1: lsof
|
|
@@ -82,11 +82,13 @@ export function stopMindos(opts = {}) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// Read ports from config for port-based cleanup
|
|
85
|
-
let webPort = '3456', mcpPort = '8781';
|
|
85
|
+
let webPort = '3456', mcpPort = '8781', setupPort = null;
|
|
86
86
|
try {
|
|
87
87
|
const config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
|
|
88
88
|
if (config.port) webPort = String(config.port);
|
|
89
89
|
if (config.mcpPort) mcpPort = String(config.mcpPort);
|
|
90
|
+
// Temporary port used by GUI setup — may still have a zombie process
|
|
91
|
+
if (config.setupPort) setupPort = String(config.setupPort);
|
|
90
92
|
} catch {}
|
|
91
93
|
|
|
92
94
|
const pids = loadPids();
|
|
@@ -106,6 +108,7 @@ export function stopMindos(opts = {}) {
|
|
|
106
108
|
// are not recorded in the PID file and would otherwise become orphaned.
|
|
107
109
|
// Include any extra ports (e.g. old ports from before a config change).
|
|
108
110
|
const portsToClean = new Set([webPort, mcpPort]);
|
|
111
|
+
if (setupPort) portsToClean.add(setupPort);
|
|
109
112
|
if (opts.extraPorts) {
|
|
110
113
|
for (const p of opts.extraPorts) portsToClean.add(String(p));
|
|
111
114
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geminilight/mindos",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.36",
|
|
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",
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
"type": "git",
|
|
20
20
|
"url": "https://github.com/GeminiLight/MindOS"
|
|
21
21
|
},
|
|
22
|
+
"homepage": "https://mindos.app",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/GeminiLight/MindOS/issues"
|
|
25
|
+
},
|
|
22
26
|
"bin": {
|
|
23
27
|
"mindos": "bin/cli.js"
|
|
24
28
|
},
|
|
@@ -58,7 +62,7 @@
|
|
|
58
62
|
"!app/package-lock.json"
|
|
59
63
|
],
|
|
60
64
|
"scripts": {
|
|
61
|
-
"prepack": "cd mcp && npm install --include=dev && npm run build && cd ../app && npm install --no-workspaces && ./node_modules/.bin/next build --webpack && cd .. && node scripts/prepare-standalone.mjs",
|
|
65
|
+
"prepack": "rm -rf _standalone && cd mcp && npm install --include=dev && npm run build && cd ../app && npm install --no-workspaces && ./node_modules/.bin/next build --webpack && cd .. && node scripts/prepare-standalone.mjs",
|
|
62
66
|
"setup": "node scripts/setup.js",
|
|
63
67
|
"dev": "mindos dev",
|
|
64
68
|
"build": "mindos build",
|
package/scripts/setup.js
CHANGED
|
@@ -843,6 +843,17 @@ async function startGuiSetup() {
|
|
|
843
843
|
try { config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch { /* ignore */ }
|
|
844
844
|
|
|
845
845
|
const isFirstTime = !config.mindRoot;
|
|
846
|
+
|
|
847
|
+
// Clean up zombie process from a previous abandoned setup session.
|
|
848
|
+
if (config.setupPort) {
|
|
849
|
+
try {
|
|
850
|
+
const { killByPort } = await import('../bin/lib/stop.js');
|
|
851
|
+
killByPort(Number(config.setupPort));
|
|
852
|
+
// Brief wait for port to free
|
|
853
|
+
await new Promise(r => setTimeout(r, 500));
|
|
854
|
+
} catch { /* best effort */ }
|
|
855
|
+
}
|
|
856
|
+
|
|
846
857
|
config.setupPending = true;
|
|
847
858
|
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
|
|
848
859
|
|
|
@@ -890,6 +901,11 @@ async function startGuiSetup() {
|
|
|
890
901
|
write(c.yellow(t('guiStarting') + '\n'));
|
|
891
902
|
|
|
892
903
|
// Start the server in the background
|
|
904
|
+
// Record the temporary setup port in config so stopMindos() can clean it up
|
|
905
|
+
// if the user abandons setup mid-way or closes the browser without completing.
|
|
906
|
+
config.setupPort = usePort;
|
|
907
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
|
|
908
|
+
|
|
893
909
|
// Pass MINDOS_WEB_PORT (not PORT) so loadConfig() won't override with the
|
|
894
910
|
// config file port — this is critical when we need a temporary port.
|
|
895
911
|
const cliPath = resolve(__dirname, '../bin/cli.js');
|