vesper-wizard 1.0.3 → 2.0.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/README.md +62 -0
- package/package.json +20 -2
- package/vesper-mcp-config.json +1 -3
- package/wizard.js +354 -54
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# vesper-wizard
|
|
2
|
+
|
|
3
|
+
Zero-friction setup wizard for [Vesper](https://github.com/vesper/mcp-server) — your local MCP-native dataset intelligence layer.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx vesper-wizard@latest
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
That's it. The wizard handles everything:
|
|
12
|
+
|
|
13
|
+
1. Creates `~/.vesper/` directories and local API key
|
|
14
|
+
2. Collects optional credentials (HuggingFace, Kaggle, Nia) — stored locally, never leaves your machine
|
|
15
|
+
3. Installs `@vespermcp/mcp-server` and auto-configures MCP for all detected agents (Claude, Cursor, VS Code, Codex, Gemini CLI)
|
|
16
|
+
4. Verifies the installation
|
|
17
|
+
|
|
18
|
+
## What you get
|
|
19
|
+
|
|
20
|
+
After the wizard finishes, your AI assistant can immediately use Vesper tools:
|
|
21
|
+
|
|
22
|
+
| Tool | Description |
|
|
23
|
+
|------|-------------|
|
|
24
|
+
| `vesper_search` | Search 16,000+ datasets via natural language |
|
|
25
|
+
| `discover_datasets` | Discover from HuggingFace, Kaggle, OpenML, data.world |
|
|
26
|
+
| `download_dataset` | Download any dataset to local storage |
|
|
27
|
+
| `prepare_dataset` | Full pipeline: analyze → clean → split → export |
|
|
28
|
+
| `analyze_quality` | Deep quality analysis with recommendations |
|
|
29
|
+
| `export_dataset` | Export to parquet, csv, feather, jsonl, arrow |
|
|
30
|
+
| `fuse_datasets` | Combine multiple datasets with quality checks |
|
|
31
|
+
|
|
32
|
+
## Security
|
|
33
|
+
|
|
34
|
+
- **Local-only**: All credentials stored in `~/.vesper/config.toml`
|
|
35
|
+
- **Keyring-backed**: Uses OS keyring when available, falls back to local TOML
|
|
36
|
+
- **No cloud**: Zero external API calls during setup
|
|
37
|
+
- **No tokens exposed**: Kaggle key input is masked
|
|
38
|
+
|
|
39
|
+
## Config file
|
|
40
|
+
|
|
41
|
+
The wizard generates `~/.vesper/config.toml`:
|
|
42
|
+
|
|
43
|
+
```toml
|
|
44
|
+
api_key = "vesper_sk_local_..."
|
|
45
|
+
hf_token = "hf_..."
|
|
46
|
+
kaggle_username = "your_username"
|
|
47
|
+
kaggle_key = "..."
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Post-setup
|
|
51
|
+
|
|
52
|
+
Restart your IDE and try in your AI assistant:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
vesper_search(query="sentiment analysis")
|
|
56
|
+
prepare_dataset(query="image classification cats dogs")
|
|
57
|
+
analyze_quality(dataset_id="imdb")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vesper-wizard",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Zero-friction setup wizard for Vesper — local MCP server, unified dataset API, and agent auto-config in 60 seconds",
|
|
5
5
|
"bin": {
|
|
6
6
|
"vesper-wizard": "wizard.js"
|
|
7
7
|
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"vesper",
|
|
10
|
+
"mcp",
|
|
11
|
+
"wizard",
|
|
12
|
+
"setup",
|
|
13
|
+
"datasets",
|
|
14
|
+
"machine-learning",
|
|
15
|
+
"huggingface",
|
|
16
|
+
"kaggle",
|
|
17
|
+
"openml"
|
|
18
|
+
],
|
|
8
19
|
"author": "Vesper Team",
|
|
9
20
|
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/vesper/mcp-server"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18.0.0"
|
|
27
|
+
},
|
|
10
28
|
"dependencies": {
|
|
11
29
|
"inquirer": "^8.2.0"
|
|
12
30
|
}
|
package/vesper-mcp-config.json
CHANGED
package/wizard.js
CHANGED
|
@@ -1,77 +1,377 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
// vesper-wizard — Zero-friction local setup for Vesper MCP
|
|
5
|
+
// Run: npx vesper-wizard@latest
|
|
6
|
+
// ─────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
4
8
|
const inquirer = require('inquirer');
|
|
5
9
|
const fs = require('fs');
|
|
6
10
|
const path = require('path');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const crypto = require('crypto');
|
|
13
|
+
const { execSync, spawnSync } = require('child_process');
|
|
14
|
+
|
|
15
|
+
// ── Paths ────────────────────────────────────────────────────
|
|
16
|
+
const HOME = os.homedir();
|
|
17
|
+
const VESPER_DIR = path.join(HOME, '.vesper');
|
|
18
|
+
const CONFIG_TOML = path.join(VESPER_DIR, 'config.toml');
|
|
19
|
+
const DATA_DIR = path.join(VESPER_DIR, 'data');
|
|
20
|
+
const IS_WIN = process.platform === 'win32';
|
|
21
|
+
const APPDATA = process.env.APPDATA || path.join(HOME, 'AppData', 'Roaming');
|
|
22
|
+
|
|
23
|
+
// ── Helpers ──────────────────────────────────────────────────
|
|
24
|
+
function ensureDir(dir) {
|
|
25
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function generateLocalKey() {
|
|
29
|
+
const random = crypto.randomBytes(24).toString('hex');
|
|
30
|
+
return `vesper_sk_local_${random}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readToml(filePath) {
|
|
34
|
+
if (!fs.existsSync(filePath)) return {};
|
|
35
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
36
|
+
const obj = {};
|
|
37
|
+
for (const line of content.split('\n')) {
|
|
38
|
+
const m = line.match(/^\s*(\w+)\s*=\s*"(.*)"\s*$/);
|
|
39
|
+
if (m) obj[m[1]] = m[2];
|
|
40
|
+
}
|
|
41
|
+
return obj;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function writeToml(filePath, data) {
|
|
45
|
+
ensureDir(path.dirname(filePath));
|
|
46
|
+
const lines = Object.entries(data).map(([k, v]) => `${k} = "${v}"`);
|
|
47
|
+
fs.writeFileSync(filePath, lines.join('\n') + '\n', 'utf8');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function upsertEnvValue(filePath, key, value) {
|
|
51
|
+
const line = `${key}=${value}`;
|
|
52
|
+
let content = '';
|
|
53
|
+
if (fs.existsSync(filePath)) {
|
|
54
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
55
|
+
const regex = new RegExp(`^${key}=.*$`, 'm');
|
|
56
|
+
if (regex.test(content)) {
|
|
57
|
+
content = content.replace(regex, line);
|
|
58
|
+
} else {
|
|
59
|
+
content = content.replace(/\n?$/, `\n${line}\n`);
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
ensureDir(path.dirname(filePath));
|
|
63
|
+
content = `${line}\n`;
|
|
64
|
+
}
|
|
65
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function dim(text) { return `\x1b[2m${text}\x1b[0m`; }
|
|
69
|
+
function bold(text) { return `\x1b[1m${text}\x1b[0m`; }
|
|
70
|
+
function green(text) { return `\x1b[32m${text}\x1b[0m`; }
|
|
71
|
+
function cyan(text) { return `\x1b[36m${text}\x1b[0m`; }
|
|
72
|
+
function yellow(text) { return `\x1b[33m${text}\x1b[0m`; }
|
|
73
|
+
function red(text) { return `\x1b[31m${text}\x1b[0m`; }
|
|
74
|
+
function magenta(text) { return `\x1b[35m${text}\x1b[0m`; }
|
|
75
|
+
|
|
76
|
+
function printBanner() {
|
|
77
|
+
console.log(`
|
|
78
|
+
${dim('─────────────────────────────────────────────────')}
|
|
79
|
+
|
|
80
|
+
${bold('██ ██ ███████ ███████ ██████ ███████ ██████')}
|
|
81
|
+
${bold('██ ██ ██ ██ ██ ██ ██ ██ ██')}
|
|
82
|
+
${bold('██ ██ █████ ███████ ██████ █████ ██████')}
|
|
83
|
+
${bold(' ██ ██ ██ ██ ██ ██ ██ ██')}
|
|
84
|
+
${bold(' ████ ███████ ███████ ██ ███████ ██ ██')}
|
|
85
|
+
|
|
86
|
+
${cyan('dataset intelligence layer')}
|
|
87
|
+
${dim('local-first • zero-config • agent-native')}
|
|
88
|
+
|
|
89
|
+
${dim('─────────────────────────────────────────────────')}
|
|
90
|
+
`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── MCP Auto-Config ──────────────────────────────────────────
|
|
94
|
+
function getAllAgentConfigs() {
|
|
95
|
+
const isMac = process.platform === 'darwin';
|
|
96
|
+
return [
|
|
97
|
+
{
|
|
98
|
+
name: 'Claude Code',
|
|
99
|
+
path: path.join(HOME, '.claude.json'),
|
|
100
|
+
format: 'mcpServers',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'Claude Desktop',
|
|
104
|
+
path: IS_WIN
|
|
105
|
+
? path.join(APPDATA, 'Claude', 'claude_desktop_config.json')
|
|
106
|
+
: isMac
|
|
107
|
+
? path.join(HOME, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')
|
|
108
|
+
: path.join(HOME, '.config', 'claude', 'claude_desktop_config.json'),
|
|
109
|
+
format: 'mcpServers',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'Cursor',
|
|
113
|
+
path: path.join(HOME, '.cursor', 'mcp.json'),
|
|
114
|
+
format: 'mcpServers',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'VS Code',
|
|
118
|
+
path: IS_WIN
|
|
119
|
+
? path.join(APPDATA, 'Code', 'User', 'mcp.json')
|
|
120
|
+
: isMac
|
|
121
|
+
? path.join(HOME, 'Library', 'Application Support', 'Code', 'User', 'mcp.json')
|
|
122
|
+
: path.join(HOME, '.config', 'Code', 'User', 'mcp.json'),
|
|
123
|
+
format: 'servers',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'Codex',
|
|
127
|
+
path: path.join(HOME, '.codex', 'config.toml'),
|
|
128
|
+
format: 'toml',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'Gemini CLI',
|
|
132
|
+
path: path.join(HOME, '.gemini', 'settings.json'),
|
|
133
|
+
format: 'mcpServers',
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function installMcpToAgent(agent) {
|
|
139
|
+
const npxCmd = IS_WIN ? 'npx.cmd' : 'npx';
|
|
140
|
+
const serverEntry = { command: npxCmd, args: ['-y', '@vespermcp/mcp-server@latest'] };
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
if (agent.format === 'toml') {
|
|
144
|
+
let content = fs.existsSync(agent.path) ? fs.readFileSync(agent.path, 'utf8') : '';
|
|
145
|
+
if (content.includes('[mcp_servers.vesper]')) return true;
|
|
146
|
+
ensureDir(path.dirname(agent.path));
|
|
147
|
+
content += `\n[mcp_servers.vesper]\ncommand = "${serverEntry.command}"\nargs = [${serverEntry.args.map(a => `"${a}"`).join(', ')}]\n`;
|
|
148
|
+
fs.writeFileSync(agent.path, content, 'utf8');
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let config = {};
|
|
153
|
+
if (fs.existsSync(agent.path)) {
|
|
154
|
+
try { config = JSON.parse(fs.readFileSync(agent.path, 'utf8').trim() || '{}'); } catch { config = {}; }
|
|
155
|
+
} else {
|
|
156
|
+
ensureDir(path.dirname(agent.path));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const key = agent.format === 'servers' ? 'servers' : 'mcpServers';
|
|
160
|
+
if (!config[key]) config[key] = {};
|
|
161
|
+
|
|
162
|
+
const entry = agent.format === 'servers'
|
|
163
|
+
? { type: 'stdio', ...serverEntry }
|
|
164
|
+
: serverEntry;
|
|
165
|
+
|
|
166
|
+
config[key].vesper = entry;
|
|
167
|
+
fs.writeFileSync(agent.path, JSON.stringify(config, null, 2), 'utf8');
|
|
168
|
+
return true;
|
|
169
|
+
} catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Server Health Check ──────────────────────────────────────
|
|
175
|
+
async function checkServerHealth() {
|
|
176
|
+
try {
|
|
177
|
+
// Quick stdio check — spawn server and see if it responds
|
|
178
|
+
const result = spawnSync(IS_WIN ? 'npx.cmd' : 'npx', ['-y', '@vespermcp/mcp-server@latest', '--version'], {
|
|
179
|
+
timeout: 10000,
|
|
180
|
+
encoding: 'utf8',
|
|
181
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
182
|
+
});
|
|
183
|
+
return result.status === 0 || (result.stderr && result.stderr.includes('Vesper'));
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
7
188
|
|
|
189
|
+
// ── Main Wizard ──────────────────────────────────────────────
|
|
8
190
|
async function main() {
|
|
9
|
-
|
|
191
|
+
printBanner();
|
|
192
|
+
|
|
193
|
+
console.log(` ${green('→')} Setting up Vesper on ${bold(os.hostname())}\n`);
|
|
194
|
+
|
|
195
|
+
// ─── Step 1: Create directories ────────────────────────────
|
|
196
|
+
process.stdout.write(` ${dim('[')}${cyan('1/6')}${dim(']')} Creating local directories...`);
|
|
197
|
+
ensureDir(VESPER_DIR);
|
|
198
|
+
ensureDir(DATA_DIR);
|
|
199
|
+
ensureDir(path.join(DATA_DIR, 'raw'));
|
|
200
|
+
ensureDir(path.join(DATA_DIR, 'processed'));
|
|
201
|
+
ensureDir(path.join(VESPER_DIR, 'datasets'));
|
|
202
|
+
console.log(` ${green('✓')}`);
|
|
203
|
+
|
|
204
|
+
// ─── Step 2: Generate local API key ────────────────────────
|
|
205
|
+
process.stdout.write(` ${dim('[')}${cyan('2/6')}${dim(']')} Generating local API key...`);
|
|
206
|
+
const existing = readToml(CONFIG_TOML);
|
|
207
|
+
const localKey = existing.api_key || generateLocalKey();
|
|
208
|
+
const configData = { ...existing, api_key: localKey };
|
|
209
|
+
writeToml(CONFIG_TOML, configData);
|
|
210
|
+
console.log(` ${green('✓')}`);
|
|
211
|
+
console.log(` ${dim('Key:')} ${dim(localKey.slice(0, 20) + '...')} ${dim('→')} ${dim(CONFIG_TOML)}`);
|
|
212
|
+
|
|
213
|
+
// ─── Step 3: Credential collection ─────────────────────────
|
|
214
|
+
console.log(`\n ${dim('[')}${cyan('3/6')}${dim(']')} ${bold('Credentials')} ${dim('(all optional — press Enter to skip)')}\n`);
|
|
10
215
|
|
|
11
|
-
|
|
12
|
-
const { projectName } = await inquirer.prompt([
|
|
216
|
+
const { hfToken, kaggleUsername, kaggleKey, niaApiKey } = await inquirer.prompt([
|
|
13
217
|
{
|
|
14
218
|
type: 'input',
|
|
15
|
-
name: '
|
|
16
|
-
message: '
|
|
17
|
-
|
|
219
|
+
name: 'hfToken',
|
|
220
|
+
message: ` ${dim('HuggingFace token')}`,
|
|
221
|
+
prefix: ' ',
|
|
18
222
|
},
|
|
19
|
-
]);
|
|
20
|
-
|
|
21
|
-
// Step 2: Data directory
|
|
22
|
-
const { dataDir } = await inquirer.prompt([
|
|
23
223
|
{
|
|
24
224
|
type: 'input',
|
|
25
|
-
name: '
|
|
26
|
-
message: '
|
|
27
|
-
|
|
225
|
+
name: 'kaggleUsername',
|
|
226
|
+
message: ` ${dim('Kaggle username')}`,
|
|
227
|
+
prefix: ' ',
|
|
28
228
|
},
|
|
29
|
-
]);
|
|
30
|
-
|
|
31
|
-
// Step 3: Default export format
|
|
32
|
-
const { exportFormat } = await inquirer.prompt([
|
|
33
229
|
{
|
|
34
|
-
type: '
|
|
35
|
-
name: '
|
|
36
|
-
message: '
|
|
37
|
-
|
|
38
|
-
|
|
230
|
+
type: 'password',
|
|
231
|
+
name: 'kaggleKey',
|
|
232
|
+
message: ` ${dim('Kaggle API key')}`,
|
|
233
|
+
mask: '*',
|
|
234
|
+
prefix: ' ',
|
|
39
235
|
},
|
|
40
|
-
]);
|
|
41
|
-
|
|
42
|
-
// Step 4: Add tokens/credentials
|
|
43
|
-
const { addTokens } = await inquirer.prompt([
|
|
44
236
|
{
|
|
45
|
-
type: '
|
|
46
|
-
name: '
|
|
47
|
-
message: '
|
|
48
|
-
|
|
237
|
+
type: 'input',
|
|
238
|
+
name: 'niaApiKey',
|
|
239
|
+
message: ` ${dim('Nia API key (NIA_API_KEY)')}`,
|
|
240
|
+
prefix: ' ',
|
|
49
241
|
},
|
|
50
242
|
]);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
//
|
|
243
|
+
|
|
244
|
+
// Save credentials to config.toml (local keyring)
|
|
245
|
+
const saved = [];
|
|
246
|
+
if (hfToken) { configData.hf_token = hfToken; saved.push('HuggingFace'); }
|
|
247
|
+
if (kaggleUsername) { configData.kaggle_username = kaggleUsername; saved.push('Kaggle user'); }
|
|
248
|
+
if (kaggleKey) { configData.kaggle_key = kaggleKey; saved.push('Kaggle key'); }
|
|
249
|
+
if (niaApiKey) {
|
|
250
|
+
configData.nia_api_key = niaApiKey;
|
|
251
|
+
saved.push('Nia');
|
|
252
|
+
|
|
253
|
+
// Also write to .env.local for Next.js apps
|
|
254
|
+
const rootEnv = path.join(process.cwd(), '.env.local');
|
|
255
|
+
upsertEnvValue(rootEnv, 'NIA_API_KEY', niaApiKey);
|
|
256
|
+
const landingDir = path.join(process.cwd(), 'landing');
|
|
257
|
+
if (fs.existsSync(landingDir) && fs.statSync(landingDir).isDirectory()) {
|
|
258
|
+
upsertEnvValue(path.join(landingDir, '.env.local'), 'NIA_API_KEY', niaApiKey);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
writeToml(CONFIG_TOML, configData);
|
|
263
|
+
if (saved.length > 0) {
|
|
264
|
+
console.log(`\n ${green('✓')} Stored locally: ${saved.join(', ')}`);
|
|
265
|
+
console.log(` ${dim('Location:')} ${CONFIG_TOML}`);
|
|
266
|
+
console.log(` ${dim('Security: local keyring — never leaves this machine')}`);
|
|
267
|
+
} else {
|
|
268
|
+
console.log(`\n ${dim('No credentials added (core tools work without them)')}`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ─── Step 4: Install @vespermcp/mcp-server ─────────────────
|
|
272
|
+
console.log(`\n ${dim('[')}${cyan('4/6')}${dim(']')} Installing Vesper MCP server...`);
|
|
273
|
+
try {
|
|
274
|
+
const npmCmd = IS_WIN ? 'npx.cmd' : 'npx';
|
|
275
|
+
spawnSync(npmCmd, ['-y', '@vespermcp/mcp-server@latest', '--setup', '--silent'], {
|
|
276
|
+
stdio: 'inherit',
|
|
277
|
+
timeout: 120000,
|
|
278
|
+
});
|
|
279
|
+
console.log(` ${green('✓')} @vespermcp/mcp-server installed`);
|
|
280
|
+
} catch {
|
|
281
|
+
console.log(` ${yellow('⚠')} Could not auto-install — run manually: npx @vespermcp/mcp-server --setup`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ─── Step 5: Auto-configure all detected IDEs ──────────────
|
|
285
|
+
process.stdout.write(`\n ${dim('[')}${cyan('5/6')}${dim(']')} Configuring coding agents...`);
|
|
286
|
+
const agents = getAllAgentConfigs();
|
|
287
|
+
const configuredAgents = [];
|
|
288
|
+
const skippedAgents = [];
|
|
289
|
+
|
|
290
|
+
for (const agent of agents) {
|
|
291
|
+
const dirExists = fs.existsSync(path.dirname(agent.path));
|
|
292
|
+
const fileExists = fs.existsSync(agent.path);
|
|
293
|
+
if (fileExists || dirExists) {
|
|
294
|
+
const ok = installMcpToAgent(agent);
|
|
295
|
+
if (ok) configuredAgents.push(agent.name);
|
|
296
|
+
else skippedAgents.push(agent.name);
|
|
297
|
+
}
|
|
62
298
|
}
|
|
299
|
+
console.log(` ${green('✓')}`);
|
|
300
|
+
|
|
301
|
+
if (configuredAgents.length > 0) {
|
|
302
|
+
console.log(`\n ┌───────────────────────────────────────────────┐`);
|
|
303
|
+
console.log(` │ ${bold('MCP Auto-Configured')} │`);
|
|
304
|
+
console.log(` ├───────────────────────────────────────────────┤`);
|
|
305
|
+
for (const name of configuredAgents) {
|
|
306
|
+
console.log(` │ ${green('✓')} ${name.padEnd(42)}│`);
|
|
307
|
+
}
|
|
308
|
+
console.log(` └───────────────────────────────────────────────┘`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ─── Step 6: Verify ────────────────────────────────────────
|
|
312
|
+
console.log(`\n ${dim('[')}${cyan('6/6')}${dim(']')} Verifying installation...`);
|
|
313
|
+
|
|
314
|
+
const dbExists = fs.existsSync(path.join(DATA_DIR, 'metadata.db'));
|
|
315
|
+
const vecExists = fs.existsSync(path.join(DATA_DIR, 'vectors.json')) || fs.existsSync(path.join(DATA_DIR, 'vectors.bin'));
|
|
316
|
+
const keyStored = fs.existsSync(CONFIG_TOML);
|
|
317
|
+
|
|
318
|
+
console.log(` ${keyStored ? green('✓') : red('✗')} Local API key ${dim(CONFIG_TOML)}`);
|
|
319
|
+
console.log(` ${dbExists ? green('✓') : yellow('⚠')} Dataset index ${dim(dbExists ? 'ready' : 'will build on first search')}`);
|
|
320
|
+
console.log(` ${vecExists ? green('✓') : yellow('⚠')} Vector store ${dim(vecExists ? 'ready' : 'will build on first search')}`);
|
|
321
|
+
console.log(` ${configuredAgents.length > 0 ? green('✓') : yellow('⚠')} MCP agents ${dim(configuredAgents.length + ' configured')}`);
|
|
322
|
+
|
|
323
|
+
// ─── Final Summary ─────────────────────────────────────────
|
|
324
|
+
console.log(`
|
|
325
|
+
${dim('═════════════════════════════════════════════════')}
|
|
326
|
+
|
|
327
|
+
${green(bold('✓ Vesper is ready!'))}
|
|
328
|
+
|
|
329
|
+
${bold('Your local API key:')}
|
|
330
|
+
${cyan(localKey)}
|
|
331
|
+
|
|
332
|
+
${bold('Config file:')}
|
|
333
|
+
${dim(CONFIG_TOML)}
|
|
334
|
+
|
|
335
|
+
${bold('What just happened:')}
|
|
336
|
+
${dim('1.')} Generated a local API key (never leaves your machine)
|
|
337
|
+
${dim('2.')} Stored credentials in local keyring
|
|
338
|
+
${dim('3.')} Auto-configured MCP for ${configuredAgents.length > 0 ? configuredAgents.join(', ') : 'detected agents'}
|
|
339
|
+
${dim('4.')} Vesper server ready on stdio transport
|
|
340
|
+
|
|
341
|
+
${dim('─────────────────────────────────────────────────')}
|
|
342
|
+
|
|
343
|
+
${bold('Quick start — try in your AI assistant:')}
|
|
344
|
+
|
|
345
|
+
${cyan('Search datasets')}
|
|
346
|
+
${dim('>')} vesper_search(query="sentiment analysis")
|
|
347
|
+
|
|
348
|
+
${cyan('Download & prepare')}
|
|
349
|
+
${dim('>')} prepare_dataset(query="image classification cats dogs")
|
|
350
|
+
|
|
351
|
+
${cyan('Quality analysis')}
|
|
352
|
+
${dim('>')} analyze_quality(dataset_id="imdb")
|
|
353
|
+
|
|
354
|
+
${cyan('Export to your project')}
|
|
355
|
+
${dim('>')} export_dataset(dataset_id="imdb", format="parquet")
|
|
356
|
+
|
|
357
|
+
${dim('─────────────────────────────────────────────────')}
|
|
358
|
+
|
|
359
|
+
${bold('Unified API — one interface, every source:')}
|
|
360
|
+
HuggingFace · Kaggle · OpenML · data.world
|
|
361
|
+
|
|
362
|
+
${dim('All routing is automatic. Agents call ONE set of')}
|
|
363
|
+
${dim('tools and Vesper handles source resolution.')}
|
|
364
|
+
|
|
365
|
+
${dim('─────────────────────────────────────────────────')}
|
|
366
|
+
|
|
367
|
+
${yellow('→')} Restart your IDE to activate MCP
|
|
368
|
+
${dim('Docs:')} https://github.com/vesper/mcp-server
|
|
63
369
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
project: projectName,
|
|
67
|
-
dataDir,
|
|
68
|
-
exportFormat,
|
|
69
|
-
tokens,
|
|
70
|
-
};
|
|
71
|
-
const configPath = path.join(process.cwd(), 'vesper-mcp-config.json');
|
|
72
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
73
|
-
console.log(`\nConfiguration saved to ${configPath}`);
|
|
74
|
-
console.log('\nVesper is ready to use!\n');
|
|
370
|
+
${dim('═════════════════════════════════════════════════')}
|
|
371
|
+
`);
|
|
75
372
|
}
|
|
76
373
|
|
|
77
|
-
main()
|
|
374
|
+
main().catch((err) => {
|
|
375
|
+
console.error(`\n${red('Error:')} ${err.message || err}`);
|
|
376
|
+
process.exit(1);
|
|
377
|
+
});
|