@geminilight/mindos 0.5.3 → 0.5.5
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 +1 -0
- package/app/app/api/mcp/install/route.ts +62 -7
- package/app/app/api/mcp/install-skill/route.ts +96 -0
- package/app/app/api/setup/route.ts +5 -2
- package/app/app/view/[...path]/ViewPageClient.tsx +4 -20
- package/app/components/SetupWizard.tsx +135 -14
- package/app/components/renderers/config/manifest.ts +1 -0
- package/app/components/renderers/csv/CsvRenderer.tsx +6 -12
- package/app/components/renderers/csv/manifest.ts +1 -0
- package/app/components/renderers/todo/manifest.ts +1 -0
- package/app/components/settings/McpTab.tsx +85 -7
- package/app/components/settings/PluginsTab.tsx +31 -16
- package/app/lib/i18n.ts +36 -6
- package/app/lib/mcp-agents.ts +10 -9
- package/app/lib/renderers/registry.ts +7 -0
- package/app/lib/renderers/useRendererState.ts +114 -0
- package/bin/lib/mcp-agents.js +9 -9
- package/package.json +1 -1
- package/scripts/setup.js +121 -18
package/scripts/setup.js
CHANGED
|
@@ -133,6 +133,12 @@ const T = {
|
|
|
133
133
|
mcpInstallDone: { en: (n) => `✔ ${n} agent(s) configured`, zh: (n) => `✔ 已配置 ${n} 个 Agent` },
|
|
134
134
|
mcpSkipped: { en: ' → Skipped. Run `mindos mcp install` anytime to configure agents.', zh: ' → 已跳过。随时运行 `mindos mcp install` 配置 Agent。' },
|
|
135
135
|
|
|
136
|
+
// skill install step
|
|
137
|
+
skillInstalling: { en: (name) => `⏳ Installing Skill "${name}"...`, zh: (name) => `⏳ 正在安装 Skill "${name}"...` },
|
|
138
|
+
skillInstallOk: { en: (name) => ` ${c.green('✔')} Skill "${name}" installed`, zh: (name) => ` ${c.green('✔')} Skill "${name}" 已安装` },
|
|
139
|
+
skillInstallFail: { en: (name, msg) => ` ${c.red('✘')} Skill "${name}" failed: ${msg}`, zh: (name, msg) => ` ${c.red('✘')} Skill "${name}" 安装失败:${msg}` },
|
|
140
|
+
skillSkipped: { en: ' → No agents selected, skill install skipped.', zh: ' → 未选择 Agent,跳过 Skill 安装。' },
|
|
141
|
+
|
|
136
142
|
// restart prompts (re-onboard with config changes)
|
|
137
143
|
restartRequired: { en: 'Config changed. Service restart required.', zh: '配置已变更,需要重启服务。' },
|
|
138
144
|
restartNow: { en: 'Restart now?', zh: '立即重启?' },
|
|
@@ -441,8 +447,13 @@ async function isSelfPort(port) {
|
|
|
441
447
|
res.on('end', () => {
|
|
442
448
|
try {
|
|
443
449
|
const data = JSON.parse(body);
|
|
444
|
-
|
|
445
|
-
|
|
450
|
+
// 200 with service=mindos → definitely us.
|
|
451
|
+
// 401 Unauthorized → also us (webPassword is set).
|
|
452
|
+
resolve(data.service === 'mindos' || res.statusCode === 401);
|
|
453
|
+
} catch {
|
|
454
|
+
// Non-JSON but got a response on /api/health → likely us
|
|
455
|
+
resolve(res.statusCode === 401 || res.statusCode === 200);
|
|
456
|
+
}
|
|
446
457
|
});
|
|
447
458
|
});
|
|
448
459
|
req.on('error', () => resolve(false));
|
|
@@ -671,7 +682,7 @@ async function runMcpInstallStep(mcpPort, authToken) {
|
|
|
671
682
|
|
|
672
683
|
if (selected.length === 0) {
|
|
673
684
|
write(c.dim(t('mcpSkipped') + '\n'));
|
|
674
|
-
return;
|
|
685
|
+
return [];
|
|
675
686
|
}
|
|
676
687
|
|
|
677
688
|
write('\n' + c.dim(tf('mcpInstalling', selected.length) + '\n'));
|
|
@@ -705,6 +716,64 @@ async function runMcpInstallStep(mcpPort, authToken) {
|
|
|
705
716
|
}
|
|
706
717
|
|
|
707
718
|
console.log(`\n${c.green(tf('mcpInstallDone', okCount))}`);
|
|
719
|
+
return selected;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/* ── Skill auto-install ────────────────────────────────────────────────────── */
|
|
723
|
+
|
|
724
|
+
const UNIVERSAL_AGENTS = new Set([
|
|
725
|
+
'amp', 'cline', 'codex', 'cursor', 'gemini-cli',
|
|
726
|
+
'github-copilot', 'kimi-cli', 'opencode', 'warp',
|
|
727
|
+
]);
|
|
728
|
+
const SKILL_UNSUPPORTED = new Set(['claude-desktop']);
|
|
729
|
+
const AGENT_NAME_MAP = {
|
|
730
|
+
'claude-code': 'claude-code',
|
|
731
|
+
'windsurf': 'windsurf',
|
|
732
|
+
'trae': 'trae',
|
|
733
|
+
'openclaw': 'openclaw',
|
|
734
|
+
'codebuddy': 'codebuddy',
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Install the appropriate MindOS Skill to selected agents via `npx skills add`.
|
|
739
|
+
* @param {string} template - 'en' | 'zh' | 'empty' | 'custom'
|
|
740
|
+
* @param {string[]} selectedAgents - MCP agent keys from the multi-select step
|
|
741
|
+
*/
|
|
742
|
+
function runSkillInstallStep(template, selectedAgents) {
|
|
743
|
+
if (!selectedAgents || selectedAgents.length === 0) {
|
|
744
|
+
write(c.dim(t('skillSkipped') + '\n'));
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const skillName = template === 'zh' ? 'mindos-zh' : 'mindos';
|
|
749
|
+
const source = resolve(ROOT, 'skills');
|
|
750
|
+
|
|
751
|
+
// Filter to non-universal, skill-capable agents
|
|
752
|
+
const additionalAgents = selectedAgents
|
|
753
|
+
.filter(key => !UNIVERSAL_AGENTS.has(key) && !SKILL_UNSUPPORTED.has(key))
|
|
754
|
+
.map(key => AGENT_NAME_MAP[key] || key);
|
|
755
|
+
|
|
756
|
+
let cmd;
|
|
757
|
+
if (additionalAgents.length > 0) {
|
|
758
|
+
cmd = `npx skills add "${source}" -s ${skillName} -a ${additionalAgents.join(',')} -g -y`;
|
|
759
|
+
} else {
|
|
760
|
+
cmd = `npx skills add "${source}" -s ${skillName} -a universal -g -y`;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
write(tf('skillInstalling', skillName) + '\n');
|
|
764
|
+
|
|
765
|
+
try {
|
|
766
|
+
execSync(cmd, {
|
|
767
|
+
encoding: 'utf-8',
|
|
768
|
+
timeout: 30_000,
|
|
769
|
+
env: { ...process.env, NODE_ENV: 'production' },
|
|
770
|
+
stdio: 'pipe',
|
|
771
|
+
});
|
|
772
|
+
write(tf('skillInstallOk', skillName) + '\n');
|
|
773
|
+
} catch (err) {
|
|
774
|
+
const msg = err.stderr || err.message || 'Unknown error';
|
|
775
|
+
write(tf('skillInstallFail', skillName, msg.split('\n')[0]) + '\n');
|
|
776
|
+
}
|
|
708
777
|
}
|
|
709
778
|
|
|
710
779
|
// ── GUI Setup ─────────────────────────────────────────────────────────────────
|
|
@@ -739,16 +808,34 @@ async function startGuiSetup() {
|
|
|
739
808
|
// Read or create config, set setupPending
|
|
740
809
|
let config = {};
|
|
741
810
|
try { config = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8')); } catch { /* ignore */ }
|
|
811
|
+
|
|
812
|
+
const isFirstTime = !config.mindRoot;
|
|
742
813
|
config.setupPending = true;
|
|
743
814
|
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
|
|
744
815
|
|
|
745
|
-
//
|
|
746
|
-
|
|
747
|
-
if (
|
|
748
|
-
|
|
749
|
-
|
|
816
|
+
// Determine which port to use for the setup wizard
|
|
817
|
+
let usePort;
|
|
818
|
+
if (isFirstTime) {
|
|
819
|
+
// First-time onboard: use a temporary port (scan from 9100) so the user's
|
|
820
|
+
// chosen port in Step 3 can differ without a mid-setup restart.
|
|
821
|
+
// 9100 is chosen to avoid conflicts with common services (5000=AirPlay, 3000/8080=dev).
|
|
822
|
+
usePort = await findFreePort(9100);
|
|
823
|
+
} else {
|
|
824
|
+
// Re-onboard: service is already running on config.port — reuse it.
|
|
825
|
+
const existingPort = config.port || 3000;
|
|
826
|
+
if (await isSelfPort(existingPort)) {
|
|
827
|
+
// Service already running — just open the setup page, no need to spawn.
|
|
828
|
+
const url = `http://localhost:${existingPort}/setup`;
|
|
829
|
+
console.log(`\n${c.green(tf('guiReady', url))}\n`);
|
|
830
|
+
const opened = openBrowser(url);
|
|
831
|
+
if (!opened) console.log(c.dim(tf('guiOpenFailed', url)));
|
|
832
|
+
process.exit(0);
|
|
833
|
+
}
|
|
834
|
+
// Service not running — start on existing port
|
|
835
|
+
usePort = await isPortInUse(existingPort)
|
|
836
|
+
? await findFreePort(9100) // existing port occupied by another process
|
|
837
|
+
: existingPort;
|
|
750
838
|
}
|
|
751
|
-
const usePort = config.port || port;
|
|
752
839
|
|
|
753
840
|
write(c.yellow(t('guiStarting') + '\n'));
|
|
754
841
|
|
|
@@ -841,6 +928,15 @@ async function main() {
|
|
|
841
928
|
|
|
842
929
|
const { readdirSync } = await import('node:fs');
|
|
843
930
|
let mindDir;
|
|
931
|
+
let selectedTemplate = 'en'; // hoisted — set by template selection or inferred from existing config
|
|
932
|
+
// Infer template from existing config's disabledSkills or UI language
|
|
933
|
+
if (resumeCfg.disabledSkills?.includes('mindos')) {
|
|
934
|
+
selectedTemplate = 'zh';
|
|
935
|
+
} else if (resumeCfg.disabledSkills?.includes('mindos-zh')) {
|
|
936
|
+
selectedTemplate = 'en';
|
|
937
|
+
} else {
|
|
938
|
+
selectedTemplate = uiLang; // fallback to UI language for first-time existing KB
|
|
939
|
+
}
|
|
844
940
|
|
|
845
941
|
// Default KB path: existing mindRoot if set, otherwise ~/MindOS (same as GUI default)
|
|
846
942
|
const HOME = homedir();
|
|
@@ -875,9 +971,9 @@ async function main() {
|
|
|
875
971
|
} else {
|
|
876
972
|
// ── Template selection (part of Step 1) ─────────────────────────────
|
|
877
973
|
write('\n');
|
|
878
|
-
|
|
974
|
+
selectedTemplate = await select('tplPrompt', 'tplOptions', 'tplValues');
|
|
879
975
|
mkdirSync(mindDir, { recursive: true });
|
|
880
|
-
await applyTemplate(
|
|
976
|
+
await applyTemplate(selectedTemplate, mindDir);
|
|
881
977
|
break;
|
|
882
978
|
}
|
|
883
979
|
}
|
|
@@ -1000,12 +1096,13 @@ async function main() {
|
|
|
1000
1096
|
}
|
|
1001
1097
|
|
|
1002
1098
|
const config = {
|
|
1003
|
-
mindRoot:
|
|
1004
|
-
port:
|
|
1005
|
-
mcpPort:
|
|
1006
|
-
authToken:
|
|
1007
|
-
webPassword:
|
|
1008
|
-
startMode:
|
|
1099
|
+
mindRoot: mindDir,
|
|
1100
|
+
port: webPort,
|
|
1101
|
+
mcpPort: mcpPort,
|
|
1102
|
+
authToken: authToken,
|
|
1103
|
+
webPassword: webPassword || '',
|
|
1104
|
+
startMode: startMode,
|
|
1105
|
+
disabledSkills: selectedTemplate === 'zh' ? ['mindos'] : ['mindos-zh'],
|
|
1009
1106
|
ai: {
|
|
1010
1107
|
provider: isSkip ? existingAiProvider : (isAnthropic ? 'anthropic' : 'openai'),
|
|
1011
1108
|
providers: existingProviders,
|
|
@@ -1052,7 +1149,13 @@ async function main() {
|
|
|
1052
1149
|
stepHeader(7);
|
|
1053
1150
|
write(c.dim(tf('mcpStepHint') + '\n\n'));
|
|
1054
1151
|
|
|
1055
|
-
await runMcpInstallStep(mcpPort, authToken);
|
|
1152
|
+
const selectedAgents = await runMcpInstallStep(mcpPort, authToken);
|
|
1153
|
+
|
|
1154
|
+
// ── Skill auto-install ────────────────────────────────────────────────────
|
|
1155
|
+
if (selectedAgents && selectedAgents.length > 0) {
|
|
1156
|
+
write('\n');
|
|
1157
|
+
runSkillInstallStep(selectedTemplate, selectedAgents);
|
|
1158
|
+
}
|
|
1056
1159
|
|
|
1057
1160
|
// ── Sync setup (optional) ──────────────────────────────────────────────────
|
|
1058
1161
|
const wantSync = await askYesNo('syncSetup');
|