@geminilight/mindos 0.5.11 → 0.5.13
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 +9 -9
- package/README_zh.md +9 -9
- package/app/README.md +2 -2
- package/app/app/api/ask/route.ts +191 -19
- package/app/app/api/mcp/install/route.ts +1 -1
- package/app/app/api/mcp/status/route.ts +11 -16
- package/app/app/api/settings/route.ts +3 -1
- package/app/app/api/setup/route.ts +7 -7
- package/app/app/api/sync/route.ts +18 -15
- package/app/components/AskModal.tsx +28 -32
- package/app/components/SettingsModal.tsx +7 -3
- package/app/components/ask/MessageList.tsx +65 -3
- package/app/components/ask/ThinkingBlock.tsx +55 -0
- package/app/components/ask/ToolCallBlock.tsx +97 -0
- package/app/components/settings/AiTab.tsx +76 -2
- package/app/components/settings/types.ts +8 -0
- package/app/components/setup/StepReview.tsx +31 -25
- package/app/components/setup/index.tsx +6 -3
- package/app/lib/agent/context.ts +317 -0
- package/app/lib/agent/index.ts +4 -0
- package/app/lib/agent/prompt.ts +46 -31
- package/app/lib/agent/stream-consumer.ts +212 -0
- package/app/lib/agent/tools.ts +159 -4
- package/app/lib/i18n.ts +28 -0
- package/app/lib/settings.ts +22 -0
- package/app/lib/types.ts +23 -0
- package/app/package.json +2 -3
- package/bin/cli.js +41 -21
- package/bin/lib/build.js +6 -2
- package/bin/lib/gateway.js +24 -3
- package/bin/lib/mcp-install.js +2 -2
- package/bin/lib/mcp-spawn.js +3 -3
- package/bin/lib/stop.js +1 -1
- package/bin/lib/sync.js +81 -40
- package/mcp/README.md +5 -5
- package/mcp/src/index.ts +2 -2
- package/package.json +3 -2
- package/scripts/setup.js +17 -12
- package/scripts/upgrade-prompt.md +6 -6
- package/skills/mindos/SKILL.md +47 -183
- package/skills/mindos-zh/SKILL.md +47 -183
- package/app/package-lock.json +0 -15615
package/bin/lib/sync.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { CONFIG_PATH, MINDOS_DIR } from './constants.js';
|
|
6
6
|
import { bold, dim, cyan, green, red, yellow } from './colors.js';
|
|
7
7
|
|
|
8
|
+
// ── Atomic write helper ────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
function atomicWriteJSON(filePath, data) {
|
|
11
|
+
const content = JSON.stringify(data, null, 2) + '\n';
|
|
12
|
+
const tmp = filePath + '.tmp';
|
|
13
|
+
writeFileSync(tmp, content, 'utf-8');
|
|
14
|
+
renameSync(tmp, filePath);
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
// ── Config helpers ──────────────────────────────────────────────────────────
|
|
9
18
|
|
|
10
19
|
function loadSyncConfig() {
|
|
@@ -20,7 +29,7 @@ function saveSyncConfig(syncConfig) {
|
|
|
20
29
|
let config = {};
|
|
21
30
|
try { config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch {}
|
|
22
31
|
config.sync = syncConfig;
|
|
23
|
-
|
|
32
|
+
atomicWriteJSON(CONFIG_PATH, config);
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
function getMindRoot() {
|
|
@@ -44,7 +53,7 @@ function loadSyncState() {
|
|
|
44
53
|
|
|
45
54
|
function saveSyncState(state) {
|
|
46
55
|
if (!existsSync(MINDOS_DIR)) mkdirSync(MINDOS_DIR, { recursive: true });
|
|
47
|
-
|
|
56
|
+
atomicWriteJSON(SYNC_STATE_PATH, state);
|
|
48
57
|
}
|
|
49
58
|
|
|
50
59
|
// ── Git helpers ─────────────────────────────────────────────────────────────
|
|
@@ -53,13 +62,13 @@ function isGitRepo(dir) {
|
|
|
53
62
|
return existsSync(resolve(dir, '.git'));
|
|
54
63
|
}
|
|
55
64
|
|
|
56
|
-
function gitExec(
|
|
57
|
-
return
|
|
65
|
+
function gitExec(args, cwd) {
|
|
66
|
+
return execFileSync('git', args, { cwd, encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
function getRemoteUrl(cwd) {
|
|
61
70
|
try {
|
|
62
|
-
return gitExec('
|
|
71
|
+
return gitExec(['remote', 'get-url', 'origin'], cwd);
|
|
63
72
|
} catch {
|
|
64
73
|
return null;
|
|
65
74
|
}
|
|
@@ -67,7 +76,7 @@ function getRemoteUrl(cwd) {
|
|
|
67
76
|
|
|
68
77
|
function getBranch(cwd) {
|
|
69
78
|
try {
|
|
70
|
-
return gitExec('
|
|
79
|
+
return gitExec(['rev-parse', '--abbrev-ref', 'HEAD'], cwd);
|
|
71
80
|
} catch {
|
|
72
81
|
return 'main';
|
|
73
82
|
}
|
|
@@ -75,7 +84,7 @@ function getBranch(cwd) {
|
|
|
75
84
|
|
|
76
85
|
function getUnpushedCount(cwd) {
|
|
77
86
|
try {
|
|
78
|
-
return gitExec('
|
|
87
|
+
return gitExec(['rev-list', '--count', '@{u}..HEAD'], cwd);
|
|
79
88
|
} catch {
|
|
80
89
|
return '?';
|
|
81
90
|
}
|
|
@@ -85,12 +94,12 @@ function getUnpushedCount(cwd) {
|
|
|
85
94
|
|
|
86
95
|
function autoCommitAndPush(mindRoot) {
|
|
87
96
|
try {
|
|
88
|
-
|
|
89
|
-
const status = gitExec('
|
|
97
|
+
execFileSync('git', ['add', '-A'], { cwd: mindRoot, stdio: 'pipe' });
|
|
98
|
+
const status = gitExec(['status', '--porcelain'], mindRoot);
|
|
90
99
|
if (!status) return;
|
|
91
100
|
const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
execFileSync('git', ['commit', '-m', `auto-sync: ${timestamp}`], { cwd: mindRoot, stdio: 'pipe' });
|
|
102
|
+
execFileSync('git', ['push', '-u', 'origin', 'HEAD'], { cwd: mindRoot, stdio: 'pipe' });
|
|
94
103
|
saveSyncState({ ...loadSyncState(), lastSync: new Date().toISOString(), lastError: null });
|
|
95
104
|
} catch (err) {
|
|
96
105
|
saveSyncState({ ...loadSyncState(), lastError: err.message, lastErrorTime: new Date().toISOString() });
|
|
@@ -99,31 +108,34 @@ function autoCommitAndPush(mindRoot) {
|
|
|
99
108
|
|
|
100
109
|
function autoPull(mindRoot) {
|
|
101
110
|
try {
|
|
102
|
-
|
|
111
|
+
execFileSync('git', ['pull', '--rebase', '--autostash'], { cwd: mindRoot, stdio: 'pipe' });
|
|
103
112
|
saveSyncState({ ...loadSyncState(), lastPull: new Date().toISOString() });
|
|
104
113
|
} catch {
|
|
105
114
|
// rebase conflict → abort → merge
|
|
106
|
-
try {
|
|
115
|
+
try { execFileSync('git', ['rebase', '--abort'], { cwd: mindRoot, stdio: 'pipe' }); } catch {}
|
|
107
116
|
try {
|
|
108
|
-
|
|
117
|
+
execFileSync('git', ['pull', '--no-rebase'], { cwd: mindRoot, stdio: 'pipe' });
|
|
109
118
|
saveSyncState({ ...loadSyncState(), lastPull: new Date().toISOString() });
|
|
110
119
|
} catch {
|
|
111
120
|
// merge conflict → keep both versions
|
|
112
121
|
try {
|
|
113
|
-
const conflicts = gitExec('
|
|
122
|
+
const conflicts = gitExec(['diff', '--name-only', '--diff-filter=U'], mindRoot).split('\n').filter(Boolean);
|
|
123
|
+
const conflictWarnings = [];
|
|
114
124
|
for (const file of conflicts) {
|
|
115
125
|
try {
|
|
116
|
-
const theirs =
|
|
126
|
+
const theirs = execFileSync('git', ['show', `:3:${file}`], { cwd: mindRoot, encoding: 'utf-8' });
|
|
117
127
|
writeFileSync(resolve(mindRoot, file + '.sync-conflict'), theirs, 'utf-8');
|
|
118
|
-
} catch {
|
|
119
|
-
|
|
128
|
+
} catch {
|
|
129
|
+
conflictWarnings.push(file);
|
|
130
|
+
}
|
|
131
|
+
try { execFileSync('git', ['checkout', '--ours', file], { cwd: mindRoot, stdio: 'pipe' }); } catch {}
|
|
120
132
|
}
|
|
121
|
-
|
|
122
|
-
|
|
133
|
+
execFileSync('git', ['add', '-A'], { cwd: mindRoot, stdio: 'pipe' });
|
|
134
|
+
execFileSync('git', ['commit', '-m', 'auto-sync: resolved conflicts (kept both versions)'], { cwd: mindRoot, stdio: 'pipe' });
|
|
123
135
|
saveSyncState({
|
|
124
136
|
...loadSyncState(),
|
|
125
137
|
lastPull: new Date().toISOString(),
|
|
126
|
-
conflicts: conflicts.map(f => ({ file: f, time: new Date().toISOString() })),
|
|
138
|
+
conflicts: conflicts.map(f => ({ file: f, time: new Date().toISOString(), noBackup: conflictWarnings.includes(f) })),
|
|
127
139
|
});
|
|
128
140
|
} catch (err) {
|
|
129
141
|
saveSyncState({ ...loadSyncState(), lastError: err.message, lastErrorTime: new Date().toISOString() });
|
|
@@ -133,9 +145,9 @@ function autoPull(mindRoot) {
|
|
|
133
145
|
|
|
134
146
|
// Retry any pending pushes (handles previous push failures)
|
|
135
147
|
try {
|
|
136
|
-
const unpushed = gitExec('
|
|
148
|
+
const unpushed = gitExec(['rev-list', '--count', '@{u}..HEAD'], mindRoot);
|
|
137
149
|
if (parseInt(unpushed) > 0) {
|
|
138
|
-
|
|
150
|
+
execFileSync('git', ['push'], { cwd: mindRoot, stdio: 'pipe' });
|
|
139
151
|
saveSyncState({ ...loadSyncState(), lastSync: new Date().toISOString(), lastError: null });
|
|
140
152
|
}
|
|
141
153
|
} catch {
|
|
@@ -196,8 +208,8 @@ export async function initSync(mindRoot, opts = {}) {
|
|
|
196
208
|
// 1. Ensure git repo
|
|
197
209
|
if (!isGitRepo(mindRoot)) {
|
|
198
210
|
if (!nonInteractive) console.log(dim('Initializing git repository...'));
|
|
199
|
-
|
|
200
|
-
try {
|
|
211
|
+
execFileSync('git', ['init'], { cwd: mindRoot, stdio: 'pipe' });
|
|
212
|
+
try { execFileSync('git', ['checkout', '-b', 'main'], { cwd: mindRoot, stdio: 'pipe' }); } catch {}
|
|
201
213
|
}
|
|
202
214
|
|
|
203
215
|
// 1b. Ensure .gitignore exists
|
|
@@ -226,35 +238,61 @@ export async function initSync(mindRoot, opts = {}) {
|
|
|
226
238
|
if (platform === 'darwin') helper = 'osxkeychain';
|
|
227
239
|
else if (platform === 'win32') helper = 'manager';
|
|
228
240
|
else helper = 'store';
|
|
229
|
-
try {
|
|
230
|
-
|
|
241
|
+
try { execFileSync('git', ['config', 'credential.helper', helper], { cwd: mindRoot, stdio: 'pipe' }); } catch (e) {
|
|
242
|
+
console.error(`[sync] credential.helper setup failed: ${e.message}`);
|
|
243
|
+
}
|
|
244
|
+
// Store the credential via git credential approve, then verify it stuck
|
|
245
|
+
let credentialStored = false;
|
|
231
246
|
try {
|
|
232
247
|
const credInput = `protocol=${urlObj.protocol.replace(':', '')}\nhost=${urlObj.host}\nusername=oauth2\npassword=${token}\n\n`;
|
|
233
|
-
|
|
234
|
-
|
|
248
|
+
execFileSync('git', ['credential', 'approve'], { cwd: mindRoot, input: credInput, stdio: 'pipe' });
|
|
249
|
+
// Verify: credential fill should return the password we just stored
|
|
250
|
+
try {
|
|
251
|
+
const fillInput = `protocol=${urlObj.protocol.replace(':', '')}\nhost=${urlObj.host}\nusername=oauth2\n\n`;
|
|
252
|
+
const fillResult = execFileSync('git', ['credential', 'fill'], {
|
|
253
|
+
cwd: mindRoot, input: fillInput, encoding: 'utf-8',
|
|
254
|
+
stdio: ['pipe', 'pipe', 'pipe'], timeout: 5000,
|
|
255
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
256
|
+
});
|
|
257
|
+
credentialStored = fillResult.includes(`password=${token}`);
|
|
258
|
+
} catch {
|
|
259
|
+
credentialStored = false;
|
|
260
|
+
}
|
|
261
|
+
} catch (e) {
|
|
262
|
+
if (!nonInteractive) console.error(`[sync] credential approve failed: ${e.message}`);
|
|
263
|
+
}
|
|
264
|
+
// If credential helper didn't actually persist, embed token in URL
|
|
265
|
+
if (!credentialStored) {
|
|
266
|
+
if (!nonInteractive) console.log(dim('Credential helper unavailable, using inline token'));
|
|
267
|
+
const fallbackUrl = new URL(remoteUrl);
|
|
268
|
+
fallbackUrl.username = 'oauth2';
|
|
269
|
+
fallbackUrl.password = token;
|
|
270
|
+
remoteUrl = fallbackUrl.toString();
|
|
271
|
+
}
|
|
235
272
|
// For 'store' helper, restrict file permissions AFTER credential file is created
|
|
236
273
|
if (helper === 'store') {
|
|
237
274
|
const credFile = resolve(process.env.HOME || homedir(), '.git-credentials');
|
|
238
|
-
try {
|
|
275
|
+
try { execFileSync('chmod', ['600', credFile], { stdio: 'pipe' }); } catch {}
|
|
239
276
|
}
|
|
240
277
|
}
|
|
241
278
|
|
|
242
279
|
// 4. Set remote
|
|
243
280
|
try {
|
|
244
|
-
|
|
281
|
+
execFileSync('git', ['remote', 'add', 'origin', remoteUrl], { cwd: mindRoot, stdio: 'pipe' });
|
|
245
282
|
} catch {
|
|
246
|
-
|
|
283
|
+
execFileSync('git', ['remote', 'set-url', 'origin', remoteUrl], { cwd: mindRoot, stdio: 'pipe' });
|
|
247
284
|
}
|
|
248
285
|
|
|
249
286
|
// 5. Test connection
|
|
250
287
|
if (!nonInteractive) console.log(dim('Testing connection...'));
|
|
251
288
|
try {
|
|
252
|
-
|
|
289
|
+
execFileSync('git', ['ls-remote', '--exit-code', 'origin'], { cwd: mindRoot, stdio: 'pipe', timeout: 15000 });
|
|
253
290
|
if (!nonInteractive) console.log(green('✔ Connection successful'));
|
|
254
|
-
} catch {
|
|
255
|
-
const
|
|
291
|
+
} catch (lsErr) {
|
|
292
|
+
const detail = lsErr.stderr ? lsErr.stderr.toString().trim() : '';
|
|
293
|
+
const errMsg = `Remote not reachable${detail ? ': ' + detail : ''} — check URL and credentials`;
|
|
256
294
|
if (nonInteractive) throw new Error(errMsg);
|
|
257
|
-
console.error(red(
|
|
295
|
+
console.error(red(`✘ ${errMsg}`));
|
|
258
296
|
process.exit(1);
|
|
259
297
|
}
|
|
260
298
|
|
|
@@ -272,11 +310,11 @@ export async function initSync(mindRoot, opts = {}) {
|
|
|
272
310
|
|
|
273
311
|
// 7. First sync: pull if remote has content, push otherwise
|
|
274
312
|
try {
|
|
275
|
-
const refs = gitExec('
|
|
313
|
+
const refs = gitExec(['ls-remote', '--heads', 'origin'], mindRoot);
|
|
276
314
|
if (refs) {
|
|
277
315
|
if (!nonInteractive) console.log(dim('Pulling from remote...'));
|
|
278
316
|
try {
|
|
279
|
-
|
|
317
|
+
execFileSync('git', ['pull', 'origin', syncConfig.branch, '--allow-unrelated-histories'], { cwd: mindRoot, stdio: nonInteractive ? 'pipe' : 'inherit' });
|
|
280
318
|
} catch {
|
|
281
319
|
if (!nonInteractive) console.log(yellow('Pull completed with warnings. Check for conflicts.'));
|
|
282
320
|
}
|
|
@@ -321,7 +359,10 @@ export async function startSyncDaemon(mindRoot) {
|
|
|
321
359
|
autoPull(mindRoot);
|
|
322
360
|
|
|
323
361
|
// Graceful shutdown: flush pending changes before exit
|
|
362
|
+
let shutdownInProgress = false;
|
|
324
363
|
const gracefulShutdown = () => {
|
|
364
|
+
if (shutdownInProgress) return;
|
|
365
|
+
shutdownInProgress = true;
|
|
325
366
|
if (commitTimer) { clearTimeout(commitTimer); commitTimer = null; }
|
|
326
367
|
try { autoCommitAndPush(mindRoot); } catch {}
|
|
327
368
|
stopSyncDaemon();
|
package/mcp/README.md
CHANGED
|
@@ -26,7 +26,7 @@ mindos start # starts app + MCP server together
|
|
|
26
26
|
Or run MCP server only:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
mindos mcp # HTTP mode (default, port
|
|
29
|
+
mindos mcp # HTTP mode (default, port 8781)
|
|
30
30
|
MCP_TRANSPORT=stdio mindos mcp # stdio mode
|
|
31
31
|
```
|
|
32
32
|
|
|
@@ -34,11 +34,11 @@ MCP_TRANSPORT=stdio mindos mcp # stdio mode
|
|
|
34
34
|
|
|
35
35
|
| Variable | Default | Description |
|
|
36
36
|
|----------|---------|-------------|
|
|
37
|
-
| `MINDOS_URL` | `http://localhost:
|
|
37
|
+
| `MINDOS_URL` | `http://localhost:3456` | App server base URL |
|
|
38
38
|
| `AUTH_TOKEN` | — | Optional: bearer token (must match App's `AUTH_TOKEN`) |
|
|
39
39
|
| `MCP_TRANSPORT` | `http` | Transport mode: `http` or `stdio` |
|
|
40
40
|
| `MCP_HOST` | `127.0.0.1` | HTTP bind address (`0.0.0.0` for remote access) |
|
|
41
|
-
| `MCP_PORT` | `
|
|
41
|
+
| `MCP_PORT` | `8781` | HTTP listen port (configurable via `mindos onboard`) |
|
|
42
42
|
| `MCP_ENDPOINT` | `/mcp` | HTTP endpoint path |
|
|
43
43
|
|
|
44
44
|
## MCP Tools (20)
|
|
@@ -75,7 +75,7 @@ Add to your Agent's MCP config (field names vary by client):
|
|
|
75
75
|
{
|
|
76
76
|
"mcpServers": {
|
|
77
77
|
"mindos": {
|
|
78
|
-
"url": "http://localhost:
|
|
78
|
+
"url": "http://localhost:8781/mcp",
|
|
79
79
|
"headers": { "Authorization": "Bearer your-token" }
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -101,7 +101,7 @@ Add to your Agent's MCP config (field names vary by client):
|
|
|
101
101
|
{
|
|
102
102
|
"mcpServers": {
|
|
103
103
|
"mindos": {
|
|
104
|
-
"url": "http://<server-ip>:
|
|
104
|
+
"url": "http://<server-ip>:8781/mcp",
|
|
105
105
|
"headers": { "Authorization": "Bearer your-token" }
|
|
106
106
|
}
|
|
107
107
|
}
|
package/mcp/src/index.ts
CHANGED
|
@@ -22,11 +22,11 @@ import { z } from "zod";
|
|
|
22
22
|
|
|
23
23
|
// ─── Config ──────────────────────────────────────────────────────────────────
|
|
24
24
|
|
|
25
|
-
const BASE_URL = process.env.MINDOS_URL ?? "http://localhost:
|
|
25
|
+
const BASE_URL = process.env.MINDOS_URL ?? "http://localhost:3456";
|
|
26
26
|
const AUTH_TOKEN = process.env.AUTH_TOKEN;
|
|
27
27
|
const MCP_TRANSPORT = process.env.MCP_TRANSPORT ?? "http"; // "http" | "stdio"
|
|
28
28
|
const MCP_HOST = process.env.MCP_HOST ?? "127.0.0.1";
|
|
29
|
-
const MCP_PORT = parseInt(process.env.MCP_PORT ?? "
|
|
29
|
+
const MCP_PORT = parseInt(process.env.MCP_PORT ?? "8781", 10);
|
|
30
30
|
const MCP_ENDPOINT = process.env.MCP_ENDPOINT ?? "/mcp";
|
|
31
31
|
const CHARACTER_LIMIT = 25_000;
|
|
32
32
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geminilight/mindos",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.13",
|
|
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",
|
|
@@ -57,7 +57,8 @@
|
|
|
57
57
|
"!assets/images",
|
|
58
58
|
"!mcp/node_modules",
|
|
59
59
|
"!mcp/dist",
|
|
60
|
-
"!mcp/package-lock.json"
|
|
60
|
+
"!mcp/package-lock.json",
|
|
61
|
+
"!app/package-lock.json"
|
|
61
62
|
],
|
|
62
63
|
"scripts": {
|
|
63
64
|
"setup": "node scripts/setup.js",
|
package/scripts/setup.js
CHANGED
|
@@ -844,11 +844,11 @@ async function startGuiSetup() {
|
|
|
844
844
|
if (isFirstTime) {
|
|
845
845
|
// First-time onboard: use a temporary port (scan from 9100) so the user's
|
|
846
846
|
// chosen port in Step 3 can differ without a mid-setup restart.
|
|
847
|
-
// 9100 is chosen to avoid conflicts with common services (5000=AirPlay,
|
|
847
|
+
// 9100 is chosen to avoid conflicts with common services (5000=AirPlay, 3456/8080=dev).
|
|
848
848
|
usePort = await findFreePort(9100);
|
|
849
849
|
} else {
|
|
850
850
|
// Re-onboard: service is already running on config.port — reuse it.
|
|
851
|
-
const existingPort = config.port ||
|
|
851
|
+
const existingPort = config.port || 3456;
|
|
852
852
|
if (await isSelfPort(existingPort)) {
|
|
853
853
|
// Service already running — just open the setup page, no need to spawn.
|
|
854
854
|
const url = `http://localhost:${existingPort}/setup`;
|
|
@@ -866,7 +866,7 @@ async function startGuiSetup() {
|
|
|
866
866
|
// stopMindos() sends SIGTERM synchronously — wait for both web and mcp
|
|
867
867
|
// ports to free, since `start` will assertPortFree on both.
|
|
868
868
|
const { waitForPortFree } = await import('../bin/lib/gateway.js');
|
|
869
|
-
const mcpPort = config.mcpPort ||
|
|
869
|
+
const mcpPort = config.mcpPort || 8781;
|
|
870
870
|
const [webFreed, mcpFreed] = await Promise.all([
|
|
871
871
|
waitForPortFree(existingPort),
|
|
872
872
|
waitForPortFree(mcpPort),
|
|
@@ -941,8 +941,8 @@ async function main() {
|
|
|
941
941
|
|
|
942
942
|
console.log(c.bold('\nExisting config:'));
|
|
943
943
|
console.log(row('Knowledge base:', c.cyan(existing.mindRoot || '(not set)')));
|
|
944
|
-
console.log(row('Web port:', c.cyan(String(existing.port || '
|
|
945
|
-
console.log(row('MCP port:', c.cyan(String(existing.mcpPort || '
|
|
944
|
+
console.log(row('Web port:', c.cyan(String(existing.port || '3456'))));
|
|
945
|
+
console.log(row('MCP port:', c.cyan(String(existing.mcpPort || '8781'))));
|
|
946
946
|
console.log(row('Auth token:', existing.authToken ? mask(existing.authToken) : c.dim('(not set)')));
|
|
947
947
|
console.log(row('Web password:', existing.webPassword ? '••••••••' : c.dim('(none)')));
|
|
948
948
|
console.log(row('AI provider:', c.cyan(existing.ai?.provider || '(not set)')));
|
|
@@ -953,7 +953,7 @@ async function main() {
|
|
|
953
953
|
const overwrite = await askYesNo('cfgExists', CONFIG_PATH);
|
|
954
954
|
if (!overwrite) {
|
|
955
955
|
const existingMode = existing.startMode || 'start';
|
|
956
|
-
const existingMcpPort = existing.mcpPort ||
|
|
956
|
+
const existingMcpPort = existing.mcpPort || 8781;
|
|
957
957
|
const existingAuth = existing.authToken || '';
|
|
958
958
|
const existingMindRoot = existing.mindRoot || resolve(homedir(), 'MindOS', 'mind');
|
|
959
959
|
console.log(`\n${c.green(t('cfgKept'))} ${c.dim(CONFIG_PATH)}`);
|
|
@@ -1072,8 +1072,8 @@ async function main() {
|
|
|
1072
1072
|
write('\n');
|
|
1073
1073
|
stepHeader(3);
|
|
1074
1074
|
const existingCfg = resumeCfg;
|
|
1075
|
-
const defaultWebPort = typeof existingCfg.port === 'number' ? existingCfg.port :
|
|
1076
|
-
const defaultMcpPort = typeof existingCfg.mcpPort === 'number' ? existingCfg.mcpPort : (defaultWebPort ===
|
|
1075
|
+
const defaultWebPort = typeof existingCfg.port === 'number' ? existingCfg.port : 3456;
|
|
1076
|
+
const defaultMcpPort = typeof existingCfg.mcpPort === 'number' ? existingCfg.mcpPort : (defaultWebPort === 8781 ? 8782 : 8781);
|
|
1077
1077
|
let webPort, mcpPort;
|
|
1078
1078
|
while (true) {
|
|
1079
1079
|
webPort = await askPort('webPortPrompt', defaultWebPort);
|
|
@@ -1178,8 +1178,8 @@ async function main() {
|
|
|
1178
1178
|
|
|
1179
1179
|
const isResuming = Object.keys(resumeCfg).length > 0;
|
|
1180
1180
|
const needsRestart = isResuming && (
|
|
1181
|
-
config.port !== (resumeCfg.port ??
|
|
1182
|
-
config.mcpPort !== (resumeCfg.mcpPort ??
|
|
1181
|
+
config.port !== (resumeCfg.port ?? 3456) ||
|
|
1182
|
+
config.mcpPort !== (resumeCfg.mcpPort ?? 8781) ||
|
|
1183
1183
|
config.mindRoot !== (resumeCfg.mindRoot ?? '') ||
|
|
1184
1184
|
config.authToken !== (resumeCfg.authToken ?? '') ||
|
|
1185
1185
|
config.webPassword !== (resumeCfg.webPassword ?? '')
|
|
@@ -1212,7 +1212,7 @@ async function main() {
|
|
|
1212
1212
|
}
|
|
1213
1213
|
|
|
1214
1214
|
const installDaemon = startMode === 'daemon' || process.argv.includes('--install-daemon');
|
|
1215
|
-
finish(mindDir, config.startMode, config.mcpPort, config.authToken, installDaemon, needsRestart, resumeCfg.port ??
|
|
1215
|
+
finish(mindDir, config.startMode, config.mcpPort, config.authToken, installDaemon, needsRestart, resumeCfg.port ?? 3456);
|
|
1216
1216
|
}
|
|
1217
1217
|
|
|
1218
1218
|
function getLocalIP() {
|
|
@@ -1224,7 +1224,12 @@ function getLocalIP() {
|
|
|
1224
1224
|
return null;
|
|
1225
1225
|
}
|
|
1226
1226
|
|
|
1227
|
-
async function finish(mindDir, startMode = 'start', mcpPort =
|
|
1227
|
+
async function finish(mindDir, startMode = 'start', mcpPort = 8781, authToken = '', installDaemon = false, needsRestart = false, oldPort = 3456) {
|
|
1228
|
+
// startMode 'daemon' stored in config is equivalent to installDaemon flag
|
|
1229
|
+
if (startMode === 'daemon') {
|
|
1230
|
+
installDaemon = true;
|
|
1231
|
+
startMode = 'start';
|
|
1232
|
+
}
|
|
1228
1233
|
if (needsRestart) {
|
|
1229
1234
|
const isRunning = await isSelfPort(oldPort);
|
|
1230
1235
|
if (isRunning) {
|
|
@@ -37,8 +37,8 @@ Help me upgrade my MindOS installation from the old source-based setup to the ne
|
|
|
37
37
|
```json
|
|
38
38
|
{
|
|
39
39
|
"mindRoot": "<value of MIND_ROOT>",
|
|
40
|
-
"port":
|
|
41
|
-
"mcpPort":
|
|
40
|
+
"port": 3456,
|
|
41
|
+
"mcpPort": 8781,
|
|
42
42
|
"authToken": "<value of AUTH_TOKEN or empty string>",
|
|
43
43
|
"webPassword": "",
|
|
44
44
|
"ai": {
|
|
@@ -69,7 +69,7 @@ Help me upgrade my MindOS installation from the old source-based setup to the ne
|
|
|
69
69
|
```
|
|
70
70
|
(First run will build automatically — this may take a minute.)
|
|
71
71
|
|
|
72
|
-
7. **Confirm** the app is accessible at http://localhost:
|
|
72
|
+
7. **Confirm** the app is accessible at http://localhost:3456 and MCP is running at http://localhost:8781/mcp.
|
|
73
73
|
|
|
74
74
|
Do not delete the old cloned repository — keep it as a backup. The `app/.env.local` file can also be kept as reference.
|
|
75
75
|
```
|
|
@@ -109,8 +109,8 @@ Do not delete the old cloned repository — keep it as a backup. The `app/.env.l
|
|
|
109
109
|
```json
|
|
110
110
|
{
|
|
111
111
|
"mindRoot": "<MIND_ROOT 的值>",
|
|
112
|
-
"port":
|
|
113
|
-
"mcpPort":
|
|
112
|
+
"port": 3456,
|
|
113
|
+
"mcpPort": 8781,
|
|
114
114
|
"authToken": "<AUTH_TOKEN 的值,没有则留空字符串>",
|
|
115
115
|
"webPassword": "",
|
|
116
116
|
"ai": {
|
|
@@ -141,7 +141,7 @@ Do not delete the old cloned repository — keep it as a backup. The `app/.env.l
|
|
|
141
141
|
```
|
|
142
142
|
(首次运行会自动构建,可能需要一两分钟。)
|
|
143
143
|
|
|
144
|
-
7. **确认** http://localhost:
|
|
144
|
+
7. **确认** http://localhost:3456 可以访问,http://localhost:8781/mcp 的 MCP 服务也在运行。
|
|
145
145
|
|
|
146
146
|
不要删除旧的克隆仓库,保留作为备份。`app/.env.local` 也可以保留作参考。
|
|
147
147
|
```
|