@geminilight/mindos 0.3.0 → 0.4.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/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 +17 -2
- 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 +4 -4
- 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 +178 -10
- 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/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 +24 -0
- /package/app/components/renderers/{ConfigRenderer.tsx → config/ConfigRenderer.tsx} +0 -0
package/bin/lib/sync.js
CHANGED
|
@@ -139,52 +139,66 @@ let activePullInterval = null;
|
|
|
139
139
|
/**
|
|
140
140
|
* Interactive sync init — configure remote git repo
|
|
141
141
|
*/
|
|
142
|
-
export async function initSync(mindRoot) {
|
|
142
|
+
export async function initSync(mindRoot, opts = {}) {
|
|
143
143
|
if (!mindRoot) { console.error(red('No mindRoot configured.')); process.exit(1); }
|
|
144
144
|
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
const nonInteractive = opts.nonInteractive || false;
|
|
146
|
+
let remoteUrl = opts.remote || '';
|
|
147
|
+
let token = opts.token || '';
|
|
148
|
+
let branch = opts.branch || 'main';
|
|
148
149
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
150
|
+
if (nonInteractive) {
|
|
151
|
+
// Non-interactive mode: all params from opts
|
|
152
|
+
if (!remoteUrl) {
|
|
153
|
+
throw new Error('Remote URL is required in non-interactive mode');
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
// Interactive mode: prompt user
|
|
157
|
+
const readline = await import('node:readline');
|
|
158
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
159
|
+
const ask = (q) => new Promise(r => rl.question(q, r));
|
|
160
|
+
|
|
161
|
+
// 2. Remote URL
|
|
162
|
+
const currentRemote = getRemoteUrl(mindRoot);
|
|
163
|
+
const defaultUrl = currentRemote || '';
|
|
164
|
+
const urlPrompt = currentRemote
|
|
165
|
+
? `${bold('Remote URL')} ${dim(`[${currentRemote}]`)}: `
|
|
166
|
+
: `${bold('Remote URL')} ${dim('(HTTPS or SSH)')}: `;
|
|
167
|
+
remoteUrl = (await ask(urlPrompt)).trim() || defaultUrl;
|
|
168
|
+
|
|
169
|
+
if (!remoteUrl) {
|
|
170
|
+
console.error(red('Remote URL is required.'));
|
|
171
|
+
rl.close();
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
155
174
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
? `${bold('Remote URL')} ${dim(`[${currentRemote}]`)}: `
|
|
161
|
-
: `${bold('Remote URL')} ${dim('(HTTPS or SSH)')}: `;
|
|
162
|
-
let remoteUrl = (await ask(urlPrompt)).trim() || defaultUrl;
|
|
175
|
+
// 3. Token for HTTPS
|
|
176
|
+
if (remoteUrl.startsWith('https://')) {
|
|
177
|
+
token = (await ask(`${bold('Access Token')} ${dim('(GitHub PAT / GitLab PAT, leave empty if SSH)')}: `)).trim();
|
|
178
|
+
}
|
|
163
179
|
|
|
164
|
-
if (!remoteUrl) {
|
|
165
|
-
console.error(red('Remote URL is required.'));
|
|
166
180
|
rl.close();
|
|
167
|
-
process.exit(1);
|
|
168
181
|
}
|
|
169
182
|
|
|
170
|
-
//
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
// 1. Ensure git repo
|
|
184
|
+
if (!isGitRepo(mindRoot)) {
|
|
185
|
+
if (!nonInteractive) console.log(dim('Initializing git repository...'));
|
|
186
|
+
execSync('git init', { cwd: mindRoot, stdio: 'pipe' });
|
|
187
|
+
try { execSync('git checkout -b main', { cwd: mindRoot, stdio: 'pipe' }); } catch {}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Handle token for HTTPS
|
|
191
|
+
if (token && remoteUrl.startsWith('https://')) {
|
|
192
|
+
const urlObj = new URL(remoteUrl);
|
|
193
|
+
urlObj.username = 'oauth2';
|
|
194
|
+
urlObj.password = token;
|
|
195
|
+
// Configure credential helper
|
|
196
|
+
try { execSync(`git config credential.helper store`, { cwd: mindRoot, stdio: 'pipe' }); } catch {}
|
|
197
|
+
// Store the credential
|
|
198
|
+
try {
|
|
199
|
+
const credInput = `protocol=${urlObj.protocol.replace(':', '')}\nhost=${urlObj.host}\nusername=oauth2\npassword=${token}\n\n`;
|
|
200
|
+
execSync('git credential approve', { cwd: mindRoot, input: credInput, stdio: 'pipe' });
|
|
201
|
+
} catch {}
|
|
188
202
|
}
|
|
189
203
|
|
|
190
204
|
// 4. Set remote
|
|
@@ -195,50 +209,48 @@ export async function initSync(mindRoot) {
|
|
|
195
209
|
}
|
|
196
210
|
|
|
197
211
|
// 5. Test connection
|
|
198
|
-
console.log(dim('Testing connection...'));
|
|
212
|
+
if (!nonInteractive) console.log(dim('Testing connection...'));
|
|
199
213
|
try {
|
|
200
|
-
execSync('git ls-remote --exit-code origin', { cwd: mindRoot, stdio: 'pipe' });
|
|
201
|
-
console.log(green('✔ Connection successful'));
|
|
214
|
+
execSync('git ls-remote --exit-code origin', { cwd: mindRoot, stdio: 'pipe', timeout: 15000 });
|
|
215
|
+
if (!nonInteractive) console.log(green('✔ Connection successful'));
|
|
202
216
|
} catch {
|
|
217
|
+
const errMsg = 'Remote not reachable — check URL and credentials';
|
|
218
|
+
if (nonInteractive) throw new Error(errMsg);
|
|
203
219
|
console.error(red('✘ Could not connect to remote. Check your URL and credentials.'));
|
|
204
|
-
rl.close();
|
|
205
220
|
process.exit(1);
|
|
206
221
|
}
|
|
207
222
|
|
|
208
|
-
rl.close();
|
|
209
|
-
|
|
210
223
|
// 6. Save sync config
|
|
211
224
|
const syncConfig = {
|
|
212
225
|
enabled: true,
|
|
213
226
|
provider: 'git',
|
|
214
227
|
remote: 'origin',
|
|
215
|
-
branch: getBranch(mindRoot),
|
|
228
|
+
branch: branch || getBranch(mindRoot),
|
|
216
229
|
autoCommitInterval: 30,
|
|
217
230
|
autoPullInterval: 300,
|
|
218
231
|
};
|
|
219
232
|
saveSyncConfig(syncConfig);
|
|
220
|
-
console.log(green('✔ Sync configured'));
|
|
233
|
+
if (!nonInteractive) console.log(green('✔ Sync configured'));
|
|
221
234
|
|
|
222
235
|
// 7. First sync: pull if remote has content, push otherwise
|
|
223
236
|
try {
|
|
224
237
|
const refs = gitExec('git ls-remote --heads origin', mindRoot);
|
|
225
238
|
if (refs) {
|
|
226
|
-
console.log(dim('Pulling from remote...'));
|
|
239
|
+
if (!nonInteractive) console.log(dim('Pulling from remote...'));
|
|
227
240
|
try {
|
|
228
|
-
execSync(`git pull origin ${syncConfig.branch} --allow-unrelated-histories`, { cwd: mindRoot, stdio: 'inherit' });
|
|
241
|
+
execSync(`git pull origin ${syncConfig.branch} --allow-unrelated-histories`, { cwd: mindRoot, stdio: nonInteractive ? 'pipe' : 'inherit' });
|
|
229
242
|
} catch {
|
|
230
|
-
|
|
231
|
-
console.log(yellow('Pull completed with warnings. Check for conflicts.'));
|
|
243
|
+
if (!nonInteractive) console.log(yellow('Pull completed with warnings. Check for conflicts.'));
|
|
232
244
|
}
|
|
233
245
|
} else {
|
|
234
|
-
console.log(dim('Pushing to remote...'));
|
|
246
|
+
if (!nonInteractive) console.log(dim('Pushing to remote...'));
|
|
235
247
|
autoCommitAndPush(mindRoot);
|
|
236
248
|
}
|
|
237
249
|
} catch {
|
|
238
|
-
console.log(dim('Performing initial push...'));
|
|
250
|
+
if (!nonInteractive) console.log(dim('Performing initial push...'));
|
|
239
251
|
autoCommitAndPush(mindRoot);
|
|
240
252
|
}
|
|
241
|
-
console.log(green('✔ Initial sync complete\n'));
|
|
253
|
+
if (!nonInteractive) console.log(green('✔ Initial sync complete\n'));
|
|
242
254
|
}
|
|
243
255
|
|
|
244
256
|
/**
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { ROOT, UPDATE_CHECK_PATH } from './constants.js';
|
|
4
|
+
import { bold, dim, cyan, yellow } from './colors.js';
|
|
5
|
+
|
|
6
|
+
const TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
7
|
+
|
|
8
|
+
const REGISTRIES = [
|
|
9
|
+
'https://registry.npmmirror.com/@geminilight/mindos/latest',
|
|
10
|
+
'https://registry.npmjs.org/@geminilight/mindos/latest',
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
/** Simple semver "a > b" comparison (major.minor.patch only). */
|
|
14
|
+
function semverGt(a, b) {
|
|
15
|
+
const pa = a.split('.').map(Number);
|
|
16
|
+
const pb = b.split('.').map(Number);
|
|
17
|
+
for (let i = 0; i < 3; i++) {
|
|
18
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return true;
|
|
19
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return false;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getCurrentVersion() {
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf-8')).version;
|
|
27
|
+
} catch {
|
|
28
|
+
return '0.0.0';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function readCache() {
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(readFileSync(UPDATE_CHECK_PATH, 'utf-8'));
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function writeCache(latestVersion) {
|
|
41
|
+
try {
|
|
42
|
+
writeFileSync(UPDATE_CHECK_PATH, JSON.stringify({
|
|
43
|
+
lastCheck: new Date().toISOString(),
|
|
44
|
+
latestVersion,
|
|
45
|
+
}), 'utf-8');
|
|
46
|
+
} catch { /* best-effort */ }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function fetchLatest() {
|
|
50
|
+
for (const url of REGISTRIES) {
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(3000) });
|
|
53
|
+
if (res.ok) {
|
|
54
|
+
const data = await res.json();
|
|
55
|
+
return data.version;
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check for updates. Returns the latest version string if an update is
|
|
66
|
+
* available, or null if up-to-date / check fails.
|
|
67
|
+
*/
|
|
68
|
+
export async function checkForUpdate() {
|
|
69
|
+
if (process.env.MINDOS_NO_UPDATE_CHECK === '1') return null;
|
|
70
|
+
|
|
71
|
+
const current = getCurrentVersion();
|
|
72
|
+
const cache = readCache();
|
|
73
|
+
|
|
74
|
+
// Cache hit — still fresh
|
|
75
|
+
if (cache?.lastCheck) {
|
|
76
|
+
const age = Date.now() - new Date(cache.lastCheck).getTime();
|
|
77
|
+
if (age < TTL_MS) {
|
|
78
|
+
return (cache.latestVersion && semverGt(cache.latestVersion, current))
|
|
79
|
+
? cache.latestVersion
|
|
80
|
+
: null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Cache miss or expired — fetch
|
|
85
|
+
const latest = await fetchLatest();
|
|
86
|
+
if (latest) writeCache(latest);
|
|
87
|
+
return (latest && semverGt(latest, current)) ? latest : null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Print update hint line if an update is available. */
|
|
91
|
+
export function printUpdateHint(latestVersion) {
|
|
92
|
+
const current = getCurrentVersion();
|
|
93
|
+
console.log(`\n ${yellow('⬆')} ${bold(`MindOS v${latestVersion}`)} available ${dim(`(current: v${current})`)}. Run ${cyan('mindos update')} to upgrade.`);
|
|
94
|
+
}
|
package/bin/lib/utils.js
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Scans app/components/renderers/*/manifest.ts and generates
|
|
3
|
+
// app/lib/renderers/index.ts with auto-discovered imports.
|
|
4
|
+
//
|
|
5
|
+
// Run: node scripts/gen-renderer-index.js
|
|
6
|
+
// Hooked into: npm run build (via prebuild)
|
|
7
|
+
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const renderersDir = path.resolve(__dirname, '../app/components/renderers');
|
|
14
|
+
const outputFile = path.resolve(__dirname, '../app/lib/renderers/index.ts');
|
|
15
|
+
|
|
16
|
+
// Scan for manifest.ts in immediate subdirectories
|
|
17
|
+
const dirs = fs.readdirSync(renderersDir, { withFileTypes: true })
|
|
18
|
+
.filter(d => d.isDirectory())
|
|
19
|
+
.filter(d => fs.existsSync(path.join(renderersDir, d.name, 'manifest.ts')))
|
|
20
|
+
.map(d => d.name)
|
|
21
|
+
.sort();
|
|
22
|
+
|
|
23
|
+
if (dirs.length === 0) {
|
|
24
|
+
console.error('No manifest.ts files found in', renderersDir);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// kebab-case → camelCase
|
|
29
|
+
function toCamel(s) {
|
|
30
|
+
return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const imports = dirs.map(dir => {
|
|
34
|
+
const varName = toCamel(dir);
|
|
35
|
+
return `import { manifest as ${varName} } from '@/components/renderers/${dir}/manifest';`;
|
|
36
|
+
}).join('\n');
|
|
37
|
+
|
|
38
|
+
const varNames = dirs.map(toCamel);
|
|
39
|
+
|
|
40
|
+
const code = `/**
|
|
41
|
+
* AUTO-GENERATED by scripts/gen-renderer-index.js — do not edit manually.
|
|
42
|
+
* To regenerate: node scripts/gen-renderer-index.js
|
|
43
|
+
*/
|
|
44
|
+
import { registerRenderer } from './registry';
|
|
45
|
+
${imports}
|
|
46
|
+
|
|
47
|
+
const manifests = [
|
|
48
|
+
${varNames.join(', ')},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
for (const m of manifests) {
|
|
52
|
+
registerRenderer(m);
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
fs.writeFileSync(outputFile, code, 'utf-8');
|
|
57
|
+
console.log(`Generated ${path.relative(process.cwd(), outputFile)} with ${dirs.length} renderers: ${dirs.join(', ')}`);
|
package/scripts/setup.js
CHANGED
|
@@ -114,6 +114,8 @@ 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?' },
|
|
@@ -764,6 +766,28 @@ async function main() {
|
|
|
764
766
|
},
|
|
765
767
|
};
|
|
766
768
|
|
|
769
|
+
// ── Configuration Summary & Confirmation ──────────────────────────────────
|
|
770
|
+
const maskPw = (s) => s ? '•'.repeat(Math.min(s.length, 8)) : '';
|
|
771
|
+
const maskTk = (s) => s && s.length > 8 ? s.slice(0, 8) + '····' : (s ? s.slice(0, 4) + '····' : '');
|
|
772
|
+
const sep = '━'.repeat(40);
|
|
773
|
+
write(`\n${sep}\n`);
|
|
774
|
+
write(`${c.bold(uiLang === 'zh' ? '配置摘要' : 'Configuration Summary')}\n`);
|
|
775
|
+
write(`${sep}\n`);
|
|
776
|
+
write(` ${c.dim('Knowledge base:')} ${mindDir}\n`);
|
|
777
|
+
write(` ${c.dim('Web port:')} ${webPort}\n`);
|
|
778
|
+
write(` ${c.dim('MCP port:')} ${mcpPort}\n`);
|
|
779
|
+
write(` ${c.dim('Auth token:')} ${maskTk(authToken)}\n`);
|
|
780
|
+
if (webPassword) write(` ${c.dim('Web password:')} ${maskPw(webPassword)}\n`);
|
|
781
|
+
write(` ${c.dim('AI provider:')} ${config.ai.provider}\n`);
|
|
782
|
+
write(` ${c.dim('Start mode:')} ${startMode}\n`);
|
|
783
|
+
write(`${sep}\n`);
|
|
784
|
+
|
|
785
|
+
const confirmSave = await askYesNoDefault('cfgConfirm');
|
|
786
|
+
if (!confirmSave) {
|
|
787
|
+
console.log(c.red(t('cfgAborted')));
|
|
788
|
+
process.exit(0);
|
|
789
|
+
}
|
|
790
|
+
|
|
767
791
|
mkdirSync(MINDOS_DIR, { recursive: true });
|
|
768
792
|
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
|
|
769
793
|
console.log(`\n${c.green(t('cfgSaved'))}: ${c.dim(CONFIG_PATH)}`);
|
|
File without changes
|