@geminilight/mindos 0.6.23 → 0.6.27
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 +19 -3
- package/README_zh.md +19 -3
- package/app/app/.well-known/agent-card.json/route.ts +34 -0
- package/app/app/api/a2a/discover/route.ts +23 -0
- package/app/app/api/a2a/route.ts +100 -0
- package/app/components/Backlinks.tsx +2 -2
- package/app/components/Breadcrumb.tsx +1 -1
- package/app/components/CreateSpaceModal.tsx +1 -0
- package/app/components/CsvView.tsx +41 -19
- package/app/components/DirView.tsx +2 -2
- package/app/components/GuideCard.tsx +6 -2
- package/app/components/HomeContent.tsx +1 -1
- package/app/components/ImportModal.tsx +3 -0
- package/app/components/OnboardingView.tsx +1 -0
- package/app/components/RightAskPanel.tsx +4 -2
- package/app/components/SearchModal.tsx +3 -3
- package/app/components/SidebarLayout.tsx +11 -2
- package/app/components/SyncStatusBar.tsx +2 -2
- package/app/components/agents/DiscoverAgentModal.tsx +149 -0
- package/app/components/ask/AskContent.tsx +22 -10
- package/app/components/ask/MentionPopover.tsx +2 -2
- package/app/components/ask/SessionTabBar.tsx +70 -0
- package/app/components/ask/SlashCommandPopover.tsx +1 -1
- package/app/components/echo/EchoInsightCollapsible.tsx +4 -0
- package/app/components/explore/UseCaseCard.tsx +2 -2
- package/app/components/help/HelpContent.tsx +6 -1
- package/app/components/panels/AgentsPanel.tsx +25 -2
- package/app/components/panels/AgentsPanelAgentDetail.tsx +2 -2
- package/app/components/panels/DiscoverPanel.tsx +3 -3
- package/app/components/panels/PanelNavRow.tsx +2 -2
- package/app/components/panels/PluginsPanel.tsx +1 -1
- package/app/components/panels/SearchPanel.tsx +3 -3
- package/app/components/renderers/summary/SummaryRenderer.tsx +1 -1
- package/app/components/renderers/workflow/WorkflowRenderer.tsx +5 -0
- package/app/components/settings/AiTab.tsx +5 -4
- package/app/components/settings/KnowledgeTab.tsx +3 -1
- package/app/components/settings/McpTab.tsx +22 -4
- package/app/components/settings/SyncTab.tsx +2 -0
- package/app/components/settings/UpdateTab.tsx +1 -1
- package/app/components/setup/StepDots.tsx +5 -1
- package/app/components/setup/index.tsx +9 -3
- package/app/components/walkthrough/WalkthroughProvider.tsx +2 -2
- package/app/data/skills/mindos/SKILL.md +186 -0
- package/app/data/skills/mindos-zh/SKILL.md +185 -0
- package/app/hooks/useA2aRegistry.ts +53 -0
- package/app/hooks/useAskSession.ts +44 -25
- package/app/lib/a2a/a2a-tools.ts +212 -0
- package/app/lib/a2a/agent-card.ts +107 -0
- package/app/lib/a2a/client.ts +207 -0
- package/app/lib/a2a/index.ts +31 -0
- package/app/lib/a2a/orchestrator.ts +255 -0
- package/app/lib/a2a/task-handler.ts +228 -0
- package/app/lib/a2a/types.ts +212 -0
- package/app/lib/agent/tools.ts +6 -4
- package/app/lib/i18n-en.ts +52 -0
- package/app/lib/i18n-zh.ts +52 -0
- package/app/next-env.d.ts +1 -1
- package/bin/cli.js +183 -164
- package/bin/commands/agent.js +110 -0
- package/bin/commands/api.js +60 -0
- package/bin/commands/ask.js +3 -3
- package/bin/commands/file.js +13 -13
- package/bin/commands/search.js +51 -0
- package/bin/commands/space.js +64 -10
- package/bin/lib/command.js +10 -0
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -54,19 +54,44 @@ import { needsBuild, writeBuildStamp, cleanNextDir, ensureAppDeps } from './lib/
|
|
|
54
54
|
import { isPortInUse, assertPortFree } from './lib/port.js';
|
|
55
55
|
import { savePids, clearPids } from './lib/pid.js';
|
|
56
56
|
import { stopMindos } from './lib/stop.js';
|
|
57
|
-
import { getPlatform, ensureMindosDir, waitForHttp, waitForPortFree, runGatewayCommand } from './lib/gateway.js';
|
|
58
57
|
import { printStartupInfo, getLocalIP } from './lib/startup.js';
|
|
59
58
|
import { spawnMcp } from './lib/mcp-spawn.js';
|
|
60
|
-
import { ensureMcpBundle } from './lib/mcp-build.js';
|
|
61
|
-
import { mcpInstall } from './lib/mcp-install.js';
|
|
62
|
-
import { initSync, startSyncDaemon, stopSyncDaemon, getSyncStatus, manualSync, listConflicts, setSyncEnabled } from './lib/sync.js';
|
|
63
59
|
import { parseArgs } from './lib/command.js';
|
|
60
|
+
import { MCP_AGENTS, detectAgentPresence } from './lib/mcp-agents.js';
|
|
61
|
+
|
|
62
|
+
// Heavy modules — loaded lazily inside command handlers to speed up CLI cold start
|
|
63
|
+
// gateway.js (426 lines), mcp-install.js (335), sync.js (472), mcp-build.js (74)
|
|
64
|
+
const lazy = {
|
|
65
|
+
gateway: () => import('./lib/gateway.js'),
|
|
66
|
+
mcpInstall: () => import('./lib/mcp-install.js'),
|
|
67
|
+
sync: () => import('./lib/sync.js'),
|
|
68
|
+
mcpBuild: () => import('./lib/mcp-build.js'),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Thin wrappers for lazy modules — same API as before, but loaded on first call
|
|
72
|
+
async function getPlatform() { return (await lazy.gateway()).getPlatform(); }
|
|
73
|
+
async function runGatewayCommand(sub) { return (await lazy.gateway()).runGatewayCommand(sub); }
|
|
74
|
+
async function waitForHttp(...a) { return (await lazy.gateway()).waitForHttp(...a); }
|
|
75
|
+
async function waitForPortFree(...a) { return (await lazy.gateway()).waitForPortFree(...a); }
|
|
76
|
+
async function ensureMindosDir() { return (await lazy.gateway()).ensureMindosDir(); }
|
|
77
|
+
async function ensureMcpBundle() { return (await lazy.mcpBuild()).ensureMcpBundle(); }
|
|
78
|
+
async function mcpInstall() { return (await lazy.mcpInstall()).mcpInstall(); }
|
|
79
|
+
async function initSync(...a) { return (await lazy.sync()).initSync(...a); }
|
|
80
|
+
async function startSyncDaemon(...a) { return (await lazy.sync()).startSyncDaemon(...a); }
|
|
81
|
+
async function stopSyncDaemon() { return (await lazy.sync()).stopSyncDaemon(); }
|
|
82
|
+
async function getSyncStatus(...a) { return (await lazy.sync()).getSyncStatus(...a); }
|
|
83
|
+
async function manualSync(...a) { return (await lazy.sync()).manualSync(...a); }
|
|
84
|
+
async function listConflicts(...a) { return (await lazy.sync()).listConflicts(...a); }
|
|
85
|
+
async function setSyncEnabled(...a) { return (await lazy.sync()).setSyncEnabled(...a); }
|
|
64
86
|
|
|
65
87
|
// ── New modular commands ──────────────────────────────────────────────────────
|
|
66
88
|
import * as fileCmd from './commands/file.js';
|
|
67
89
|
import * as spaceCmd from './commands/space.js';
|
|
68
90
|
import * as askCmd from './commands/ask.js';
|
|
69
91
|
import * as statusCmd from './commands/status.js';
|
|
92
|
+
import * as apiCmd from './commands/api.js';
|
|
93
|
+
import * as agentCmd from './commands/agent.js';
|
|
94
|
+
import * as searchCmd from './commands/search.js';
|
|
70
95
|
|
|
71
96
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
72
97
|
|
|
@@ -136,24 +161,24 @@ function buildIfNeeded(newRoot) {
|
|
|
136
161
|
|
|
137
162
|
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
138
163
|
|
|
139
|
-
|
|
164
|
+
// ── Unified arg parsing ────────────────────────────────────────────────────
|
|
165
|
+
const { command: cmd, args: cliArgs, flags: cliFlags } = parseArgs(process.argv.slice(2));
|
|
140
166
|
|
|
141
|
-
|
|
142
|
-
// --help / -h is handled at entry section (resolvedCmd = null → help block)
|
|
143
|
-
if (cmd === '--version' || cmd === '-v') {
|
|
167
|
+
if (cliFlags.version || cliFlags.v) {
|
|
144
168
|
const version = JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf-8')).version;
|
|
145
169
|
console.log(`mindos/${version} node/${process.version} ${process.platform}-${process.arch}`);
|
|
146
170
|
process.exit(0);
|
|
147
171
|
}
|
|
148
172
|
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
const
|
|
173
|
+
// Backward compat: derive legacy variables from unified flags
|
|
174
|
+
const isDaemon = cliFlags.daemon === true || (!cmd && isDaemonMode());
|
|
175
|
+
const isVerbose = cliFlags.verbose === true;
|
|
176
|
+
const extra = cliArgs.filter(a => !a.startsWith('-')).join(' ');
|
|
152
177
|
|
|
153
178
|
const commands = {
|
|
154
179
|
// ── onboard ────────────────────────────────────────────────────────────────
|
|
155
180
|
onboard: async () => {
|
|
156
|
-
const daemonFlag =
|
|
181
|
+
const daemonFlag = cliFlags['install-daemon'] ? ' --install-daemon' : '';
|
|
157
182
|
run(`node ${resolve(ROOT, 'scripts/setup.js')}${daemonFlag}`);
|
|
158
183
|
},
|
|
159
184
|
init: async () => commands.onboard(),
|
|
@@ -201,74 +226,48 @@ const commands = {
|
|
|
201
226
|
}
|
|
202
227
|
const mcpPort = config.mcpPort || 8781;
|
|
203
228
|
const localIP = getLocalIP();
|
|
204
|
-
|
|
205
229
|
const localUrl = `http://localhost:${mcpPort}/mcp`;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
},
|
|
238
|
-
}, null, 2));
|
|
239
|
-
|
|
240
|
-
// Cursor
|
|
241
|
-
console.log(`\n${sep}`);
|
|
242
|
-
console.log(`${bold('Cursor')}`);
|
|
243
|
-
console.log(`${sep}`);
|
|
244
|
-
console.log(dim('Quick install:') + ` mindos mcp install cursor -g -y`);
|
|
245
|
-
console.log(dim('\nManual config (~/.cursor/mcp.json):'));
|
|
246
|
-
console.log(JSON.stringify({
|
|
247
|
-
mcpServers: {
|
|
248
|
-
mindos: {
|
|
249
|
-
url: localUrl,
|
|
250
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
251
|
-
},
|
|
252
|
-
},
|
|
253
|
-
}, null, 2));
|
|
254
|
-
|
|
255
|
-
// Remote
|
|
230
|
+
|
|
231
|
+
if (cliFlags.json) {
|
|
232
|
+
const data = { token, mcpPort, localUrl, remoteUrl: localIP ? `http://${localIP}:${mcpPort}/mcp` : null };
|
|
233
|
+
console.log(JSON.stringify(data, null, 2));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const sep = dim('━'.repeat(40));
|
|
238
|
+
const snippet = (url) => JSON.stringify({
|
|
239
|
+
mcpServers: { mindos: { url, headers: { Authorization: `Bearer ${token}` } } },
|
|
240
|
+
}, null, 2);
|
|
241
|
+
|
|
242
|
+
console.log(`\n${bold('Auth token:')} ${cyan(token)}\n`);
|
|
243
|
+
|
|
244
|
+
// Show installed agents, then up to 3 uninstalled popular ones
|
|
245
|
+
const installed = [];
|
|
246
|
+
const others = [];
|
|
247
|
+
for (const [key, agent] of Object.entries(MCP_AGENTS)) {
|
|
248
|
+
(detectAgentPresence(key) ? installed : others).push([key, agent]);
|
|
249
|
+
}
|
|
250
|
+
const toShow = [...installed.slice(0, 8), ...others.slice(0, Math.max(0, 3 - installed.length))];
|
|
251
|
+
|
|
252
|
+
for (const [key, agent] of toShow) {
|
|
253
|
+
console.log(sep);
|
|
254
|
+
console.log(bold(agent.name));
|
|
255
|
+
console.log(dim('Install:') + ` mindos mcp install ${key} -g -y`);
|
|
256
|
+
if (agent.global) console.log(dim(`Config: ${agent.global}`));
|
|
257
|
+
console.log(snippet(localUrl));
|
|
258
|
+
console.log();
|
|
259
|
+
}
|
|
260
|
+
|
|
256
261
|
if (localIP) {
|
|
257
|
-
|
|
258
|
-
console.log(
|
|
259
|
-
console.log(
|
|
260
|
-
console.log(
|
|
261
|
-
console.log(`URL: ${cyan(remoteUrl)}`);
|
|
262
|
-
console.log(JSON.stringify({
|
|
263
|
-
mcpServers: {
|
|
264
|
-
mindos: {
|
|
265
|
-
url: remoteUrl,
|
|
266
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
267
|
-
},
|
|
268
|
-
},
|
|
269
|
-
}, null, 2));
|
|
262
|
+
console.log(sep);
|
|
263
|
+
console.log(bold('Remote (other devices)'));
|
|
264
|
+
console.log(`URL: ${cyan(`http://${localIP}:${mcpPort}/mcp`)}`);
|
|
265
|
+
console.log(snippet(`http://${localIP}:${mcpPort}/mcp`));
|
|
270
266
|
}
|
|
271
267
|
|
|
268
|
+
if (toShow.length < installed.length) {
|
|
269
|
+
console.log(dim(`\n +${installed.length - toShow.length} more agents detected. Run \`mindos agent list\` to see all.`));
|
|
270
|
+
}
|
|
272
271
|
console.log(dim('\nRun `mindos onboard` to regenerate.\n'));
|
|
273
272
|
},
|
|
274
273
|
|
|
@@ -286,8 +285,7 @@ const commands = {
|
|
|
286
285
|
ensureAppDeps();
|
|
287
286
|
const mcp = spawnMcp(isVerbose);
|
|
288
287
|
savePids(process.pid, mcp.pid);
|
|
289
|
-
process.on('exit', () => { stopSyncDaemon(); clearPids(); });
|
|
290
|
-
// Start sync daemon if enabled
|
|
288
|
+
process.on('exit', () => { stopSyncDaemon().catch(() => {}); clearPids(); });
|
|
291
289
|
const devMindRoot = process.env.MIND_ROOT;
|
|
292
290
|
if (devMindRoot) {
|
|
293
291
|
startSyncDaemon(devMindRoot).catch(() => {});
|
|
@@ -308,7 +306,7 @@ const commands = {
|
|
|
308
306
|
} catch {}
|
|
309
307
|
}
|
|
310
308
|
if (isDaemon) {
|
|
311
|
-
const platform = getPlatform();
|
|
309
|
+
const platform = await getPlatform();
|
|
312
310
|
if (!platform) {
|
|
313
311
|
console.warn(yellow('Warning: daemon mode not supported on this platform. Falling back to foreground.'));
|
|
314
312
|
} else {
|
|
@@ -401,7 +399,7 @@ const commands = {
|
|
|
401
399
|
}
|
|
402
400
|
const mcp = spawnMcp(isVerbose);
|
|
403
401
|
savePids(process.pid, mcp.pid);
|
|
404
|
-
process.on('exit', () => { stopSyncDaemon(); clearPids(); });
|
|
402
|
+
process.on('exit', () => { stopSyncDaemon().catch(() => {}); clearPids(); });
|
|
405
403
|
// Start sync daemon if enabled
|
|
406
404
|
const mindRoot = process.env.MIND_ROOT;
|
|
407
405
|
if (mindRoot) {
|
|
@@ -425,12 +423,12 @@ const commands = {
|
|
|
425
423
|
},
|
|
426
424
|
|
|
427
425
|
mcp: async () => {
|
|
428
|
-
const sub =
|
|
429
|
-
const restArgs =
|
|
426
|
+
const sub = cliArgs[0];
|
|
427
|
+
const restArgs = cliArgs;
|
|
430
428
|
const hasInstallFlags = restArgs.some(a => ['-g', '--global', '-y', '--yes'].includes(a));
|
|
431
429
|
if (sub === 'install' || hasInstallFlags) { await mcpInstall(); return; }
|
|
432
430
|
loadConfig();
|
|
433
|
-
ensureMcpBundle();
|
|
431
|
+
await ensureMcpBundle();
|
|
434
432
|
// `mindos mcp` is the entry point for MCP clients (Claude Code, Cursor, etc.)
|
|
435
433
|
// which communicate over stdin/stdout. Default to stdio; HTTP is handled by
|
|
436
434
|
// `mindos start` via spawnMcp(). Callers can still override via env.
|
|
@@ -490,7 +488,7 @@ const commands = {
|
|
|
490
488
|
|
|
491
489
|
// ── gateway ────────────────────────────────────────────────────────────────
|
|
492
490
|
gateway: async () => {
|
|
493
|
-
const sub =
|
|
491
|
+
const sub = cliArgs[0];
|
|
494
492
|
if (!sub) {
|
|
495
493
|
const row = (c, d) => ` ${cyan(c.padEnd(32))}${dim(d)}`;
|
|
496
494
|
console.log(`
|
|
@@ -553,25 +551,28 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
553
551
|
|
|
554
552
|
// ── doctor ─────────────────────────────────────────────────────────────────
|
|
555
553
|
doctor: async () => {
|
|
556
|
-
const
|
|
557
|
-
const
|
|
558
|
-
const
|
|
554
|
+
const jsonMode = cliFlags.json === true;
|
|
555
|
+
const checks = [];
|
|
556
|
+
const ok = (msg, key) => { checks.push({ status: 'ok', key, msg }); if (!jsonMode) console.log(` ${green('✔')} ${msg}`); };
|
|
557
|
+
const err = (msg, key) => { checks.push({ status: 'error', key, msg }); if (!jsonMode) console.log(` ${red('✘')} ${msg}`); };
|
|
558
|
+
const warn= (msg, key) => { checks.push({ status: 'warn', key, msg }); if (!jsonMode) console.log(` ${yellow('!')} ${msg}`); };
|
|
559
559
|
|
|
560
|
-
console.log(`\n${bold('
|
|
560
|
+
if (!jsonMode) console.log(`\n${bold('MindOS Doctor')}\n`);
|
|
561
561
|
let hasError = false;
|
|
562
562
|
|
|
563
563
|
// 1. config file
|
|
564
564
|
if (!existsSync(CONFIG_PATH)) {
|
|
565
|
-
err(`Config not found at ${dim(CONFIG_PATH)}
|
|
566
|
-
console.log(`\n ${dim('Run `mindos onboard` to create it.')}\n`);
|
|
565
|
+
err(`Config not found at ${dim(CONFIG_PATH)}`, 'config');
|
|
566
|
+
if (!jsonMode) { console.log(`\n ${dim('Run `mindos onboard` to create it.')}\n`); }
|
|
567
|
+
if (jsonMode) { console.log(JSON.stringify({ ok: false, checks }, null, 2)); }
|
|
567
568
|
process.exit(1);
|
|
568
569
|
}
|
|
569
570
|
let config;
|
|
570
571
|
try {
|
|
571
572
|
config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
|
|
572
|
-
ok(`Config file found and valid JSON ${dim(CONFIG_PATH)}
|
|
573
|
+
ok(`Config file found and valid JSON ${dim(CONFIG_PATH)}`, 'config');
|
|
573
574
|
} catch {
|
|
574
|
-
err(`Config file exists but failed to parse ${dim(CONFIG_PATH)}
|
|
575
|
+
err(`Config file exists but failed to parse ${dim(CONFIG_PATH)}`, 'config');
|
|
575
576
|
hasError = true;
|
|
576
577
|
}
|
|
577
578
|
|
|
@@ -654,7 +655,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
654
655
|
}
|
|
655
656
|
|
|
656
657
|
// 7. Daemon status
|
|
657
|
-
const platform = getPlatform();
|
|
658
|
+
const platform = await getPlatform();
|
|
658
659
|
if (platform === 'systemd') {
|
|
659
660
|
try {
|
|
660
661
|
execSync('systemctl --user is-active mindos', { stdio: 'pipe' });
|
|
@@ -675,7 +676,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
675
676
|
// 8. Sync status
|
|
676
677
|
if (config?.mindRoot) {
|
|
677
678
|
try {
|
|
678
|
-
const syncStatus = getSyncStatus(config.mindRoot);
|
|
679
|
+
const syncStatus = await getSyncStatus(config.mindRoot);
|
|
679
680
|
if (!syncStatus.enabled) {
|
|
680
681
|
warn(`Cross-device sync is not configured ${dim('(run `mindos sync init` to set up)')}`);
|
|
681
682
|
} else if (syncStatus.lastError) {
|
|
@@ -710,9 +711,14 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
710
711
|
warn('Could not check for updates');
|
|
711
712
|
}
|
|
712
713
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
:
|
|
714
|
+
if (jsonMode) {
|
|
715
|
+
const hasErr = checks.some(c => c.status === 'error');
|
|
716
|
+
console.log(JSON.stringify({ ok: !hasErr, checks }, null, 2));
|
|
717
|
+
} else {
|
|
718
|
+
console.log(hasError
|
|
719
|
+
? `\n${red('Some checks failed.')} Run ${cyan('mindos onboard')} to reconfigure.\n`
|
|
720
|
+
: `\n${green('All checks passed.')}\n`);
|
|
721
|
+
}
|
|
716
722
|
if (hasError) process.exit(1);
|
|
717
723
|
},
|
|
718
724
|
|
|
@@ -760,7 +766,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
760
766
|
return;
|
|
761
767
|
}
|
|
762
768
|
|
|
763
|
-
const updatePlatform = getPlatform();
|
|
769
|
+
const updatePlatform = await getPlatform();
|
|
764
770
|
let daemonRunning = false;
|
|
765
771
|
if (updatePlatform === 'systemd') {
|
|
766
772
|
try { execSync('systemctl --user is-active mindos', { stdio: 'pipe' }); daemonRunning = true; } catch {}
|
|
@@ -974,7 +980,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
974
980
|
try { stopMindos(); } catch { /* may not be running */ }
|
|
975
981
|
|
|
976
982
|
// 2. Remove daemon (skip if platform unsupported)
|
|
977
|
-
if (getPlatform()) {
|
|
983
|
+
if (await getPlatform()) {
|
|
978
984
|
try {
|
|
979
985
|
await runGatewayCommand('uninstall');
|
|
980
986
|
} catch {
|
|
@@ -1037,14 +1043,14 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
1037
1043
|
},
|
|
1038
1044
|
|
|
1039
1045
|
// ── logs ───────────────────────────────────────────────────────────────────
|
|
1040
|
-
logs: () => {
|
|
1041
|
-
ensureMindosDir();
|
|
1046
|
+
logs: async () => {
|
|
1047
|
+
await ensureMindosDir();
|
|
1042
1048
|
if (!existsSync(LOG_PATH)) {
|
|
1043
1049
|
console.log(dim(`No log file yet at ${LOG_PATH}`));
|
|
1044
1050
|
console.log(dim('Logs are created when starting MindOS (mindos start, mindos onboard, or daemon mode).'));
|
|
1045
1051
|
process.exit(0);
|
|
1046
1052
|
}
|
|
1047
|
-
const noFollow =
|
|
1053
|
+
const noFollow = cliFlags['no-follow'] === true;
|
|
1048
1054
|
if (noFollow) {
|
|
1049
1055
|
execSync(`tail -n 100 ${LOG_PATH}`, { stdio: 'inherit' });
|
|
1050
1056
|
} else {
|
|
@@ -1054,7 +1060,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
1054
1060
|
|
|
1055
1061
|
// ── config ─────────────────────────────────────────────────────────────────
|
|
1056
1062
|
config: () => {
|
|
1057
|
-
const sub =
|
|
1063
|
+
const sub = cliArgs[0];
|
|
1058
1064
|
|
|
1059
1065
|
function maskKey(val) {
|
|
1060
1066
|
if (!val) return val;
|
|
@@ -1085,7 +1091,11 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
1085
1091
|
display.authToken = maskKey(display.authToken);
|
|
1086
1092
|
if (display.webPassword)
|
|
1087
1093
|
display.webPassword = maskKey(display.webPassword);
|
|
1088
|
-
|
|
1094
|
+
if (cliFlags.json) {
|
|
1095
|
+
console.log(JSON.stringify(display, null, 2));
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
console.log(`\n${bold('MindOS Config')} ${dim(`v${(() => { try { return JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf-8')).version; } catch { return '?'; } })()}`)} ${dim(CONFIG_PATH)}\n`);
|
|
1089
1099
|
console.log(JSON.stringify(display, null, 2));
|
|
1090
1100
|
console.log();
|
|
1091
1101
|
return;
|
|
@@ -1125,8 +1135,8 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
1125
1135
|
}
|
|
1126
1136
|
|
|
1127
1137
|
if (sub === 'set') {
|
|
1128
|
-
const key =
|
|
1129
|
-
const val =
|
|
1138
|
+
const key = cliArgs[1];
|
|
1139
|
+
const val = cliArgs[2];
|
|
1130
1140
|
if (!key || val === undefined) {
|
|
1131
1141
|
console.error(red('Usage: mindos config set <key> <value>'));
|
|
1132
1142
|
console.error(dim(' Examples:'));
|
|
@@ -1167,7 +1177,7 @@ ${dim('Shortcut: mindos start --daemon → install + start in one step')}
|
|
|
1167
1177
|
}
|
|
1168
1178
|
|
|
1169
1179
|
if (sub === 'unset') {
|
|
1170
|
-
const key =
|
|
1180
|
+
const key = cliArgs[1];
|
|
1171
1181
|
if (!key) {
|
|
1172
1182
|
console.error(red('Usage: mindos config unset <key>'));
|
|
1173
1183
|
process.exit(1);
|
|
@@ -1215,13 +1225,13 @@ ${bold('Examples:')}
|
|
|
1215
1225
|
|
|
1216
1226
|
// ── sync ──────────────────────────────────────────────────────────────────
|
|
1217
1227
|
sync: async () => {
|
|
1218
|
-
const sub =
|
|
1228
|
+
const sub = cliArgs[0];
|
|
1219
1229
|
loadConfig();
|
|
1220
1230
|
const mindRoot = process.env.MIND_ROOT;
|
|
1221
1231
|
|
|
1222
1232
|
if (sub === 'init') {
|
|
1223
1233
|
// Parse --non-interactive --remote <url> --branch <branch> --token <token>
|
|
1224
|
-
const args =
|
|
1234
|
+
const args = cliArgs.slice(1);
|
|
1225
1235
|
const flagIdx = (flag) => args.indexOf(flag);
|
|
1226
1236
|
const flagVal = (flag) => { const i = flagIdx(flag); return i >= 0 && i + 1 < args.length ? args[i + 1] : ''; };
|
|
1227
1237
|
const nonInteractive = args.includes('--non-interactive');
|
|
@@ -1242,7 +1252,7 @@ ${bold('Examples:')}
|
|
|
1242
1252
|
if (sub === 'now') {
|
|
1243
1253
|
try {
|
|
1244
1254
|
console.log(dim('Pulling...'));
|
|
1245
|
-
manualSync(mindRoot);
|
|
1255
|
+
await manualSync(mindRoot);
|
|
1246
1256
|
console.log(green('✔ Sync complete'));
|
|
1247
1257
|
} catch (err) {
|
|
1248
1258
|
console.error(red(err.message));
|
|
@@ -1252,18 +1262,18 @@ ${bold('Examples:')}
|
|
|
1252
1262
|
}
|
|
1253
1263
|
|
|
1254
1264
|
if (sub === 'conflicts') {
|
|
1255
|
-
listConflicts(mindRoot);
|
|
1265
|
+
await listConflicts(mindRoot);
|
|
1256
1266
|
return;
|
|
1257
1267
|
}
|
|
1258
1268
|
|
|
1259
1269
|
if (sub === 'on') {
|
|
1260
|
-
setSyncEnabled(true);
|
|
1270
|
+
await setSyncEnabled(true);
|
|
1261
1271
|
return;
|
|
1262
1272
|
}
|
|
1263
1273
|
|
|
1264
1274
|
if (sub === 'off') {
|
|
1265
|
-
setSyncEnabled(false);
|
|
1266
|
-
stopSyncDaemon();
|
|
1275
|
+
await setSyncEnabled(false);
|
|
1276
|
+
await stopSyncDaemon();
|
|
1267
1277
|
return;
|
|
1268
1278
|
}
|
|
1269
1279
|
|
|
@@ -1278,9 +1288,15 @@ ${bold('Examples:')}
|
|
|
1278
1288
|
}
|
|
1279
1289
|
|
|
1280
1290
|
// default: sync status
|
|
1281
|
-
const status = getSyncStatus(mindRoot);
|
|
1291
|
+
const status = await getSyncStatus(mindRoot);
|
|
1292
|
+
|
|
1293
|
+
if (cliFlags.json) {
|
|
1294
|
+
console.log(JSON.stringify(status, null, 2));
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1282
1298
|
if (!status.enabled) {
|
|
1283
|
-
console.log(`\n${bold('
|
|
1299
|
+
console.log(`\n${bold('Sync Status')}`);
|
|
1284
1300
|
console.log(dim(' Not configured. Run `mindos sync init` to set up.\n'));
|
|
1285
1301
|
return;
|
|
1286
1302
|
}
|
|
@@ -1293,7 +1309,7 @@ ${bold('Examples:')}
|
|
|
1293
1309
|
})()
|
|
1294
1310
|
: 'never';
|
|
1295
1311
|
|
|
1296
|
-
console.log(`\n${bold('
|
|
1312
|
+
console.log(`\n${bold('Sync Status')}`);
|
|
1297
1313
|
console.log(` ${dim('Provider:')} ${cyan(`${status.provider} (${status.remote})`)}`);
|
|
1298
1314
|
console.log(` ${dim('Branch:')} ${cyan(status.branch)}`);
|
|
1299
1315
|
console.log(` ${dim('Last sync:')} ${ago}`);
|
|
@@ -1307,61 +1323,64 @@ ${bold('Examples:')}
|
|
|
1307
1323
|
},
|
|
1308
1324
|
|
|
1309
1325
|
// ── New modular commands (knowledge operations) ──────────────────────────
|
|
1310
|
-
file: async () =>
|
|
1311
|
-
space: async () =>
|
|
1312
|
-
ask: async () =>
|
|
1313
|
-
status: async () =>
|
|
1326
|
+
file: async () => fileCmd.run(cliArgs, cliFlags),
|
|
1327
|
+
space: async () => spaceCmd.run(cliArgs, cliFlags),
|
|
1328
|
+
ask: async () => askCmd.run(cliArgs, cliFlags),
|
|
1329
|
+
status: async () => statusCmd.run(cliArgs, cliFlags),
|
|
1330
|
+
api: async () => apiCmd.run(cliArgs, cliFlags),
|
|
1331
|
+
agent: async () => agentCmd.run(cliArgs, cliFlags),
|
|
1332
|
+
search: async () => searchCmd.run(cliArgs, cliFlags),
|
|
1314
1333
|
};
|
|
1315
1334
|
|
|
1316
1335
|
// ── Entry ─────────────────────────────────────────────────────────────────────
|
|
1317
1336
|
|
|
1318
|
-
const resolvedCmd = (
|
|
1337
|
+
const resolvedCmd = (cliFlags.help || cliFlags.h) ? null : (cmd || (existsSync(CONFIG_PATH) ? getStartMode() : null));
|
|
1319
1338
|
|
|
1320
1339
|
if (!resolvedCmd || !commands[resolvedCmd]) {
|
|
1321
1340
|
const pkgVersion = (() => { try { return JSON.parse(readFileSync(resolve(ROOT, 'package.json'), 'utf-8')).version; } catch { return '?'; } })();
|
|
1322
1341
|
const row = (c, d) => ` ${cyan(c.padEnd(36))}${dim(d)}`;
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
${
|
|
1362
|
-
|
|
1363
|
-
`);
|
|
1364
|
-
const isHelp =
|
|
1342
|
+
|
|
1343
|
+
// Command registry — help is generated entirely from this array.
|
|
1344
|
+
// Modular commands provide meta via their exports; inline commands define meta here.
|
|
1345
|
+
const helpRegistry = [
|
|
1346
|
+
{ group: 'Core', usage: 'mindos onboard', summary: 'Interactive setup (aliases: init, setup)' },
|
|
1347
|
+
{ group: 'Core', usage: 'mindos start', summary: 'Start app + MCP server (production)' },
|
|
1348
|
+
{ group: 'Core', usage: 'mindos start --daemon', summary: 'Start as background OS service' },
|
|
1349
|
+
{ group: 'Core', usage: 'mindos dev', summary: 'Start in dev mode' },
|
|
1350
|
+
{ group: 'Core', usage: 'mindos stop', summary: 'Stop running processes' },
|
|
1351
|
+
{ group: 'Core', usage: 'mindos restart', summary: 'Stop then start again' },
|
|
1352
|
+
{ group: 'Core', usage: 'mindos build', summary: 'Build for production' },
|
|
1353
|
+
statusCmd.meta,
|
|
1354
|
+
{ group: 'Core', usage: 'mindos open', summary: 'Open Web UI in browser' },
|
|
1355
|
+
fileCmd.meta, spaceCmd.meta, searchCmd.meta, askCmd.meta, agentCmd.meta, apiCmd.meta,
|
|
1356
|
+
{ group: 'MCP', usage: 'mindos mcp', summary: 'Start MCP server only' },
|
|
1357
|
+
{ group: 'MCP', usage: 'mindos mcp install [agent]', summary: 'Install MCP config into Agent' },
|
|
1358
|
+
{ group: 'MCP', usage: 'mindos token', summary: 'Show auth token and MCP config' },
|
|
1359
|
+
{ group: 'Sync', usage: 'mindos sync', summary: 'Show sync status (init/now/conflicts/on/off)' },
|
|
1360
|
+
{ group: 'Gateway', usage: 'mindos gateway <sub>', summary: 'Manage service (install/start/stop/status/logs)' },
|
|
1361
|
+
{ group: 'Config', usage: 'mindos config <sub>', summary: 'View/update config (show/set/unset/validate)' },
|
|
1362
|
+
{ group: 'Config', usage: 'mindos doctor', summary: 'Health check' },
|
|
1363
|
+
{ group: 'Config', usage: 'mindos update', summary: 'Update to latest version' },
|
|
1364
|
+
{ group: 'Config', usage: 'mindos uninstall', summary: 'Fully uninstall MindOS' },
|
|
1365
|
+
{ group: 'Config', usage: 'mindos logs', summary: 'Tail service logs' },
|
|
1366
|
+
];
|
|
1367
|
+
|
|
1368
|
+
const groupLabels = [
|
|
1369
|
+
['Core', 'Core'], ['Knowledge', 'Knowledge'], ['MCP', 'MCP'],
|
|
1370
|
+
['Sync', 'Sync'], ['Gateway', 'Gateway (Background Service)'], ['Config', 'Config & Diagnostics'],
|
|
1371
|
+
];
|
|
1372
|
+
const groups = {};
|
|
1373
|
+
for (const e of helpRegistry) {
|
|
1374
|
+
const g = e.group || 'Other';
|
|
1375
|
+
if (!groups[g]) groups[g] = [];
|
|
1376
|
+
groups[g].push(row(e.usage || `mindos ${e.name}`, e.summary));
|
|
1377
|
+
}
|
|
1378
|
+
const sections = groupLabels
|
|
1379
|
+
.filter(([k]) => groups[k])
|
|
1380
|
+
.map(([k, label]) => `${bold(`${label}:`)}\n${groups[k].join('\n')}`);
|
|
1381
|
+
|
|
1382
|
+
console.log(`\n${bold('MindOS CLI')} ${dim(`v${pkgVersion}`)}\n\n${sections.join('\n\n')}\n\n${bold('Global Flags:')}\n${row('--json', 'Output in JSON (for AI agents)')}\n${row('--help, -h', 'Show help')}\n${row('--version, -v', 'Show version')}\n`);
|
|
1383
|
+
const isHelp = cliFlags.help || cliFlags.h;
|
|
1365
1384
|
process.exit((cmd && !isHelp) ? 1 : 0);
|
|
1366
1385
|
}
|
|
1367
1386
|
|