@geminilight/mindos 0.3.0 → 0.5.0
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/app/app/api/mcp/agents/route.ts +72 -0
- package/app/app/api/mcp/install/route.ts +95 -0
- package/app/app/api/mcp/status/route.ts +47 -0
- package/app/app/api/setup/check-port/route.ts +41 -0
- package/app/app/api/skills/route.ts +208 -0
- package/app/app/api/sync/route.ts +54 -3
- package/app/app/api/update-check/route.ts +52 -0
- package/app/app/globals.css +12 -0
- package/app/app/layout.tsx +4 -2
- package/app/app/login/page.tsx +20 -13
- package/app/app/page.tsx +19 -2
- package/app/app/setup/page.tsx +2 -0
- package/app/app/view/[...path]/ViewPageClient.tsx +47 -21
- package/app/app/view/[...path]/loading.tsx +1 -1
- package/app/app/view/[...path]/not-found.tsx +101 -0
- package/app/components/AskFab.tsx +1 -1
- package/app/components/AskModal.tsx +1 -1
- package/app/components/Backlinks.tsx +1 -1
- package/app/components/Breadcrumb.tsx +13 -3
- package/app/components/CsvView.tsx +5 -6
- package/app/components/DirView.tsx +42 -21
- package/app/components/FindInPage.tsx +211 -0
- package/app/components/HomeContent.tsx +97 -44
- package/app/components/JsonView.tsx +1 -2
- package/app/components/MarkdownEditor.tsx +1 -2
- package/app/components/OnboardingView.tsx +6 -7
- package/app/components/SettingsModal.tsx +5 -2
- package/app/components/SetupWizard.tsx +499 -172
- package/app/components/Sidebar.tsx +1 -1
- package/app/components/UpdateBanner.tsx +101 -0
- package/app/components/renderers/{AgentInspectorRenderer.tsx → agent-inspector/AgentInspectorRenderer.tsx} +13 -11
- package/app/components/renderers/agent-inspector/manifest.ts +14 -0
- package/app/components/renderers/{BacklinksRenderer.tsx → backlinks/BacklinksRenderer.tsx} +6 -6
- package/app/components/renderers/backlinks/manifest.ts +14 -0
- package/app/components/renderers/config/manifest.ts +14 -0
- package/app/components/renderers/csv/BoardView.tsx +12 -12
- package/app/components/renderers/csv/ConfigPanel.tsx +7 -8
- package/app/components/renderers/{CsvRenderer.tsx → csv/CsvRenderer.tsx} +8 -9
- package/app/components/renderers/csv/GalleryView.tsx +3 -3
- package/app/components/renderers/csv/TableView.tsx +4 -5
- package/app/components/renderers/csv/manifest.ts +14 -0
- package/app/components/renderers/{DiffRenderer.tsx → diff/DiffRenderer.tsx} +10 -9
- package/app/components/renderers/diff/manifest.ts +14 -0
- package/app/components/renderers/{GraphRenderer.tsx → graph/GraphRenderer.tsx} +4 -5
- package/app/components/renderers/graph/manifest.ts +14 -0
- package/app/components/renderers/{SummaryRenderer.tsx → summary/SummaryRenderer.tsx} +6 -6
- package/app/components/renderers/summary/manifest.ts +14 -0
- package/app/components/renderers/{TimelineRenderer.tsx → timeline/TimelineRenderer.tsx} +6 -6
- package/app/components/renderers/timeline/manifest.ts +14 -0
- package/app/components/renderers/{TodoRenderer.tsx → todo/TodoRenderer.tsx} +2 -2
- package/app/components/renderers/todo/manifest.ts +14 -0
- package/app/components/renderers/{WorkflowRenderer.tsx → workflow/WorkflowRenderer.tsx} +13 -13
- package/app/components/renderers/workflow/manifest.ts +14 -0
- package/app/components/settings/McpTab.tsx +549 -0
- package/app/components/settings/SyncTab.tsx +139 -50
- package/app/components/settings/types.ts +1 -1
- package/app/data/pages/home.png +0 -0
- package/app/lib/i18n.ts +226 -19
- package/app/lib/renderers/index.ts +20 -89
- package/app/lib/renderers/registry.ts +4 -1
- package/app/lib/settings.ts +3 -0
- package/app/package.json +1 -0
- package/app/types/semver.d.ts +8 -0
- package/bin/cli.js +137 -24
- package/bin/lib/build.js +53 -18
- package/bin/lib/colors.js +3 -1
- package/bin/lib/config.js +4 -0
- package/bin/lib/constants.js +2 -0
- package/bin/lib/debug.js +10 -0
- package/bin/lib/mcp-install.js +4 -1
- package/bin/lib/port.js +8 -2
- package/bin/lib/startup.js +21 -20
- package/bin/lib/stop.js +41 -3
- package/bin/lib/sync.js +65 -53
- package/bin/lib/update-check.js +94 -0
- package/bin/lib/utils.js +2 -2
- package/package.json +1 -1
- package/scripts/gen-renderer-index.js +57 -0
- package/scripts/setup.js +205 -10
- /package/app/components/renderers/{ConfigRenderer.tsx → config/ConfigRenderer.tsx} +0 -0
package/scripts/setup.js
CHANGED
|
@@ -52,8 +52,8 @@ const T = {
|
|
|
52
52
|
// step labels
|
|
53
53
|
step: { en: (n, total) => `Step ${n}/${total}`, zh: (n, total) => `步骤 ${n}/${total}` },
|
|
54
54
|
stepTitles: {
|
|
55
|
-
en: ['Knowledge Base', 'Template', 'Ports', 'Auth Token', 'Web Password', 'AI Provider', 'Start Mode'],
|
|
56
|
-
zh: ['知识库', '模板', '端口', 'Auth Token', 'Web 密码', 'AI 服务商', '启动方式'],
|
|
55
|
+
en: ['Knowledge Base', 'Template', 'Ports', 'Auth Token', 'Web Password', 'AI Provider', 'Start Mode', 'Agent Tools'],
|
|
56
|
+
zh: ['知识库', '模板', '端口', 'Auth Token', 'Web 密码', 'AI 服务商', '启动方式', 'Agent 工具'],
|
|
57
57
|
},
|
|
58
58
|
|
|
59
59
|
// path
|
|
@@ -114,12 +114,23 @@ const T = {
|
|
|
114
114
|
cfgKept: { en: '✔ Keeping existing config', zh: '✔ 保留现有配置' },
|
|
115
115
|
cfgKeptNote: { en: ' Settings from this session were not saved', zh: ' 本次填写的设置未保存' },
|
|
116
116
|
cfgSaved: { en: '✔ Config saved', zh: '✔ 配置已保存' },
|
|
117
|
+
cfgConfirm: { en: 'Save this configuration?', zh: '保存此配置?' },
|
|
118
|
+
cfgAborted: { en: '✘ Setup cancelled. Run `mindos onboard` to try again.', zh: '✘ 设置已取消。运行 `mindos onboard` 重新开始。' },
|
|
117
119
|
yesNo: { en: '[y/N]', zh: '[y/N]' },
|
|
118
120
|
yesNoDefault: { en: '[Y/n]', zh: '[Y/n]' },
|
|
119
121
|
startNow: { en: 'Start MindOS now?', zh: '现在启动 MindOS?' },
|
|
120
122
|
syncSetup: { en: 'Set up cross-device sync via Git?', zh: '是否配置 Git 跨设备同步?' },
|
|
121
123
|
syncLater: { en: ' → Run `mindos sync init` anytime to set up sync later.', zh: ' → 随时运行 `mindos sync init` 配置同步。' },
|
|
122
124
|
|
|
125
|
+
// mcp install step
|
|
126
|
+
mcpStepTitle: { en: 'Agent Tools (MCP)', zh: 'Agent 工具 (MCP)' },
|
|
127
|
+
mcpStepHint: { en: 'Select AI agents to configure with MindOS MCP (Space to toggle, A for all, Enter to confirm).\nAgents not yet installed can be pre-configured — they will work once you install the app.', zh: '选择要配置 MindOS MCP 的 AI Agent(空格切换,A 全选,Enter 确认)。\n未安装的 Agent 可以预先配置,安装应用后即可生效。' },
|
|
128
|
+
mcpInstalling: { en: (n) => `⏳ Configuring ${n} agent(s)...`, zh: (n) => `⏳ 正在配置 ${n} 个 Agent...` },
|
|
129
|
+
mcpInstallOk: { en: (name, path) => ` ${c.green('✔')} ${name} ${c.dim('→ ' + path)}`, zh: (name, path) => ` ${c.green('✔')} ${name} ${c.dim('→ ' + path)}` },
|
|
130
|
+
mcpInstallFail: { en: (name, msg) => ` ${c.red('✘')} ${name} ${c.dim(msg)}`, zh: (name, msg) => ` ${c.red('✘')} ${name} ${c.dim(msg)}` },
|
|
131
|
+
mcpInstallDone: { en: (n) => `✔ ${n} agent(s) configured`, zh: (n) => `✔ 已配置 ${n} 个 Agent` },
|
|
132
|
+
mcpSkipped: { en: ' → Skipped. Run `mindos mcp install` anytime to configure agents.', zh: ' → 已跳过。随时运行 `mindos mcp install` 配置 Agent。' },
|
|
133
|
+
|
|
123
134
|
// next steps (onboard — keep it minimal, details shown on `mindos start`)
|
|
124
135
|
nextSteps: {
|
|
125
136
|
en: (cmd) => [
|
|
@@ -169,7 +180,7 @@ const tf = (key, ...args) => {
|
|
|
169
180
|
|
|
170
181
|
// ── Step header ───────────────────────────────────────────────────────────────
|
|
171
182
|
|
|
172
|
-
const TOTAL_STEPS =
|
|
183
|
+
const TOTAL_STEPS = 8;
|
|
173
184
|
function stepHeader(n) {
|
|
174
185
|
const title = T.stepTitles[uiLang][n - 1] ?? T.stepTitles.en[n - 1];
|
|
175
186
|
const stepLabel = tf('step', n, TOTAL_STEPS);
|
|
@@ -385,15 +396,21 @@ const askYesNoDefault = (labelKey, arg = '') => askYesNo(labelKey, arg, true);
|
|
|
385
396
|
function isPortInUse(port) {
|
|
386
397
|
return new Promise((resolve) => {
|
|
387
398
|
const sock = createConnection({ port, host: '127.0.0.1' });
|
|
388
|
-
|
|
389
|
-
sock.
|
|
399
|
+
const cleanup = (result) => { sock.destroy(); resolve(result); };
|
|
400
|
+
sock.setTimeout(500, () => cleanup(false));
|
|
401
|
+
sock.once('connect', () => cleanup(true));
|
|
402
|
+
sock.once('error', (err) => {
|
|
403
|
+
// ECONNREFUSED = nothing listening → free; other errors = treat as in-use
|
|
404
|
+
cleanup(err.code !== 'ECONNREFUSED');
|
|
405
|
+
});
|
|
390
406
|
});
|
|
391
407
|
}
|
|
392
408
|
|
|
393
409
|
async function findFreePort(from) {
|
|
394
|
-
let p = from;
|
|
395
|
-
|
|
396
|
-
|
|
410
|
+
for (let p = from; p <= 65535; p++) {
|
|
411
|
+
if (!await isPortInUse(p)) return p;
|
|
412
|
+
}
|
|
413
|
+
return from; // fallback (extremely unlikely)
|
|
397
414
|
}
|
|
398
415
|
|
|
399
416
|
async function askPort(labelKey, defaultPort) {
|
|
@@ -501,6 +518,150 @@ async function applyTemplate(tpl, mindDir) {
|
|
|
501
518
|
}
|
|
502
519
|
}
|
|
503
520
|
|
|
521
|
+
// ── MCP Agent definitions (mirrors bin/lib/mcp-install.js) ───────────────────
|
|
522
|
+
|
|
523
|
+
const MCP_AGENTS_SETUP = {
|
|
524
|
+
'claude-code': { name: 'Claude Code', project: '.mcp.json', global: '~/.claude.json', key: 'mcpServers' },
|
|
525
|
+
'claude-desktop': { name: 'Claude Desktop', project: null, global: process.platform === 'darwin' ? '~/Library/Application Support/Claude/claude_desktop_config.json' : '~/.config/Claude/claude_desktop_config.json', key: 'mcpServers' },
|
|
526
|
+
'cursor': { name: 'Cursor', project: '.cursor/mcp.json', global: '~/.cursor/mcp.json', key: 'mcpServers' },
|
|
527
|
+
'windsurf': { name: 'Windsurf', project: null, global: '~/.codeium/windsurf/mcp_config.json', key: 'mcpServers' },
|
|
528
|
+
'cline': { name: 'Cline', project: null, global: process.platform === 'darwin' ? '~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json' : '~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json', key: 'mcpServers' },
|
|
529
|
+
'trae': { name: 'Trae', project: '.trae/mcp.json', global: '~/.trae/mcp.json', key: 'mcpServers' },
|
|
530
|
+
'gemini-cli': { name: 'Gemini CLI', project: '.gemini/settings.json', global: '~/.gemini/settings.json', key: 'mcpServers' },
|
|
531
|
+
'openclaw': { name: 'OpenClaw', project: null, global: '~/.openclaw/mcp.json', key: 'mcpServers' },
|
|
532
|
+
'codebuddy': { name: 'CodeBuddy', project: null, global: '~/.claude-internal/.claude.json', key: 'mcpServers' },
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
function expandHomePath(p) {
|
|
536
|
+
return p.startsWith('~/') ? resolve(homedir(), p.slice(2)) : p;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/** Detect if an agent already has mindos configured (for pre-selection). */
|
|
540
|
+
function isAgentInstalled(agentKey) {
|
|
541
|
+
const agent = MCP_AGENTS_SETUP[agentKey];
|
|
542
|
+
if (!agent) return false;
|
|
543
|
+
for (const cfgPath of [agent.global, agent.project]) {
|
|
544
|
+
if (!cfgPath) continue;
|
|
545
|
+
const abs = expandHomePath(cfgPath);
|
|
546
|
+
if (!existsSync(abs)) continue;
|
|
547
|
+
try {
|
|
548
|
+
const config = JSON.parse(readFileSync(abs, 'utf-8'));
|
|
549
|
+
if (config[agent.key]?.mindos) return true;
|
|
550
|
+
} catch { /* ignore */ }
|
|
551
|
+
}
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Step 8: interactive multi-select of agents to configure, then install.
|
|
557
|
+
* Uses the same interactiveMultiSelect as mcp-install.js (re-implemented inline
|
|
558
|
+
* because this script uses its own raw-mode helpers).
|
|
559
|
+
*/
|
|
560
|
+
async function runMcpInstallStep(mcpPort, authToken) {
|
|
561
|
+
const keys = Object.keys(MCP_AGENTS_SETUP);
|
|
562
|
+
|
|
563
|
+
// Build options with installed status shown as hint
|
|
564
|
+
const options = keys.map(k => {
|
|
565
|
+
const installed = isAgentInstalled(k);
|
|
566
|
+
return {
|
|
567
|
+
label: MCP_AGENTS_SETUP[k].name,
|
|
568
|
+
hint: installed ? (uiLang === 'zh' ? '已安装' : 'installed') : (uiLang === 'zh' ? '未安装' : 'not installed'),
|
|
569
|
+
value: k,
|
|
570
|
+
preselect: installed,
|
|
571
|
+
};
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
// Multi-select using raw mode
|
|
575
|
+
const selected = await (async () => {
|
|
576
|
+
return new Promise((resolveSelected) => {
|
|
577
|
+
let cursor = 0;
|
|
578
|
+
const chosen = new Set(options.map((o, i) => o.preselect ? i : -1).filter(i => i >= 0));
|
|
579
|
+
|
|
580
|
+
const render = (first = false) => {
|
|
581
|
+
if (!first) write(`\x1b[${options.length + 2}A\x1b[J`);
|
|
582
|
+
write(`${c.bold(uiLang === 'zh' ? '选择 Agent:' : 'Select agents:')} ${c.dim(uiLang === 'zh' ? '(↑↓ 移动 空格 切换 A 全选 Enter 确认)' : '(↑↓ move Space toggle A all Enter confirm)')}\n`);
|
|
583
|
+
for (let i = 0; i < options.length; i++) {
|
|
584
|
+
const o = options[i];
|
|
585
|
+
const check = chosen.has(i) ? c.green('✔') : c.dim('○');
|
|
586
|
+
const pointer = i === cursor ? c.cyan('❯') : ' ';
|
|
587
|
+
const label = i === cursor ? (chosen.has(i) ? c.green(o.label) : c.cyan(o.label)) : (chosen.has(i) ? c.green(o.label) : o.label);
|
|
588
|
+
write(` ${pointer} ${check} ${label} ${c.dim('(' + o.hint + ')')}\n`);
|
|
589
|
+
}
|
|
590
|
+
write(c.dim(` ${chosen.size} ${uiLang === 'zh' ? '已选' : 'selected'}\n`));
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
write('\n');
|
|
594
|
+
render(true);
|
|
595
|
+
|
|
596
|
+
process.stdin.setRawMode(true);
|
|
597
|
+
process.stdin.resume();
|
|
598
|
+
process.stdin.setEncoding('utf8');
|
|
599
|
+
|
|
600
|
+
const onKey = (key) => {
|
|
601
|
+
if (key === '\x03') { cleanup(); process.exit(1); }
|
|
602
|
+
if (key === `${ESC}[A`) { cursor = (cursor - 1 + options.length) % options.length; render(); }
|
|
603
|
+
else if (key === `${ESC}[B`) { cursor = (cursor + 1) % options.length; render(); }
|
|
604
|
+
else if (key === ' ') {
|
|
605
|
+
if (chosen.has(cursor)) chosen.delete(cursor); else chosen.add(cursor);
|
|
606
|
+
render();
|
|
607
|
+
} else if (key === 'a' || key === 'A') {
|
|
608
|
+
if (chosen.size === options.length) chosen.clear();
|
|
609
|
+
else options.forEach((_, i) => chosen.add(i));
|
|
610
|
+
render();
|
|
611
|
+
} else if (key === '\r' || key === '\n') {
|
|
612
|
+
cleanup();
|
|
613
|
+
resolveSelected([...chosen].sort().map(i => options[i].value));
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
const cleanup = () => {
|
|
618
|
+
process.stdin.removeListener('data', onKey);
|
|
619
|
+
process.stdin.setRawMode(false);
|
|
620
|
+
process.stdin.pause();
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
process.stdin.on('data', onKey);
|
|
624
|
+
});
|
|
625
|
+
})();
|
|
626
|
+
|
|
627
|
+
if (selected.length === 0) {
|
|
628
|
+
write(c.dim(t('mcpSkipped') + '\n'));
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
write('\n' + c.dim(tf('mcpInstalling', selected.length) + '\n'));
|
|
633
|
+
|
|
634
|
+
// stdio entry (same as mcp-install.js)
|
|
635
|
+
const entry = { type: 'stdio', command: 'mindos', args: ['mcp'], env: { MCP_TRANSPORT: 'stdio' } };
|
|
636
|
+
let okCount = 0;
|
|
637
|
+
|
|
638
|
+
for (const agentKey of selected) {
|
|
639
|
+
const agent = MCP_AGENTS_SETUP[agentKey];
|
|
640
|
+
// prefer global scope; fall back to project
|
|
641
|
+
const cfgPath = agent.global || agent.project;
|
|
642
|
+
if (!cfgPath) {
|
|
643
|
+
write(tf('mcpInstallFail', agent.name, 'no config path') + '\n');
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
const abs = expandHomePath(cfgPath);
|
|
647
|
+
try {
|
|
648
|
+
let config = {};
|
|
649
|
+
if (existsSync(abs)) config = JSON.parse(readFileSync(abs, 'utf-8'));
|
|
650
|
+
if (!config[agent.key]) config[agent.key] = {};
|
|
651
|
+
config[agent.key].mindos = entry;
|
|
652
|
+
const dir = resolve(abs, '..');
|
|
653
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
654
|
+
writeFileSync(abs, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
655
|
+
write(tf('mcpInstallOk', agent.name, cfgPath) + '\n');
|
|
656
|
+
okCount++;
|
|
657
|
+
} catch (err) {
|
|
658
|
+
write(tf('mcpInstallFail', agent.name, String(err)) + '\n');
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
console.log(`\n${c.green(tf('mcpInstallDone', okCount))}`);
|
|
663
|
+
}
|
|
664
|
+
|
|
504
665
|
// ── GUI Setup ─────────────────────────────────────────────────────────────────
|
|
505
666
|
|
|
506
667
|
function openBrowser(url) {
|
|
@@ -669,8 +830,13 @@ async function main() {
|
|
|
669
830
|
// ── Step 3: Ports ─────────────────────────────────────────────────────────
|
|
670
831
|
write('\n');
|
|
671
832
|
stepHeader(3);
|
|
672
|
-
|
|
673
|
-
|
|
833
|
+
let webPort, mcpPort;
|
|
834
|
+
while (true) {
|
|
835
|
+
webPort = await askPort('webPortPrompt', 3000);
|
|
836
|
+
mcpPort = await askPort('mcpPortPrompt', webPort === 8787 ? 8788 : 8787);
|
|
837
|
+
if (webPort !== mcpPort) break;
|
|
838
|
+
write(c.yellow(` ⚠ ${uiLang === 'zh' ? 'Web 端口和 MCP 端口不能相同,请重新选择' : 'Web port and MCP port must be different — please choose again'}\n`));
|
|
839
|
+
}
|
|
674
840
|
|
|
675
841
|
// ── Step 4: Auth token ────────────────────────────────────────────────────
|
|
676
842
|
write('\n');
|
|
@@ -764,10 +930,39 @@ async function main() {
|
|
|
764
930
|
},
|
|
765
931
|
};
|
|
766
932
|
|
|
933
|
+
// ── Configuration Summary & Confirmation ──────────────────────────────────
|
|
934
|
+
const maskPw = (s) => s ? '•'.repeat(Math.min(s.length, 8)) : '';
|
|
935
|
+
const maskTk = (s) => s && s.length > 8 ? s.slice(0, 8) + '····' : (s ? s.slice(0, 4) + '····' : '');
|
|
936
|
+
const sep = '━'.repeat(40);
|
|
937
|
+
write(`\n${sep}\n`);
|
|
938
|
+
write(`${c.bold(uiLang === 'zh' ? '配置摘要' : 'Configuration Summary')}\n`);
|
|
939
|
+
write(`${sep}\n`);
|
|
940
|
+
write(` ${c.dim('Knowledge base:')} ${mindDir}\n`);
|
|
941
|
+
write(` ${c.dim('Web port:')} ${webPort}\n`);
|
|
942
|
+
write(` ${c.dim('MCP port:')} ${mcpPort}\n`);
|
|
943
|
+
write(` ${c.dim('Auth token:')} ${maskTk(authToken)}\n`);
|
|
944
|
+
if (webPassword) write(` ${c.dim('Web password:')} ${maskPw(webPassword)}\n`);
|
|
945
|
+
write(` ${c.dim('AI provider:')} ${config.ai.provider}\n`);
|
|
946
|
+
write(` ${c.dim('Start mode:')} ${startMode}\n`);
|
|
947
|
+
write(`${sep}\n`);
|
|
948
|
+
|
|
949
|
+
const confirmSave = await askYesNoDefault('cfgConfirm');
|
|
950
|
+
if (!confirmSave) {
|
|
951
|
+
console.log(c.red(t('cfgAborted')));
|
|
952
|
+
process.exit(0);
|
|
953
|
+
}
|
|
954
|
+
|
|
767
955
|
mkdirSync(MINDOS_DIR, { recursive: true });
|
|
768
956
|
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
|
|
769
957
|
console.log(`\n${c.green(t('cfgSaved'))}: ${c.dim(CONFIG_PATH)}`);
|
|
770
958
|
|
|
959
|
+
// ── Step 8: MCP Agent Install ──────────────────────────────────────────────
|
|
960
|
+
write('\n');
|
|
961
|
+
stepHeader(8);
|
|
962
|
+
write(c.dim(tf('mcpStepHint') + '\n\n'));
|
|
963
|
+
|
|
964
|
+
await runMcpInstallStep(mcpPort, authToken);
|
|
965
|
+
|
|
771
966
|
// ── Sync setup (optional) ──────────────────────────────────────────────────
|
|
772
967
|
const wantSync = await askYesNo('syncSetup');
|
|
773
968
|
if (wantSync) {
|
|
File without changes
|