hydramcp 1.0.0 → 1.0.1
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/dist/setup.d.ts +5 -5
- package/dist/setup.js +126 -76
- package/package.json +1 -1
package/dist/setup.d.ts
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Run: npx hydramcp setup
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* 1. API keys
|
|
8
|
-
* 2.
|
|
9
|
-
* 3.
|
|
10
|
-
* 4.
|
|
6
|
+
* Flow:
|
|
7
|
+
* 1. Ask: API keys, subscriptions, or both?
|
|
8
|
+
* 2. Walk through selected path(s)
|
|
9
|
+
* 3. Auto-detect Ollama
|
|
10
|
+
* 4. Save config, show the one-liner for Claude Code
|
|
11
11
|
*
|
|
12
12
|
* Zero dependencies. Uses Node.js readline + child_process.
|
|
13
13
|
*/
|
package/dist/setup.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Run: npx hydramcp setup
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* 1. API keys
|
|
8
|
-
* 2.
|
|
9
|
-
* 3.
|
|
10
|
-
* 4.
|
|
6
|
+
* Flow:
|
|
7
|
+
* 1. Ask: API keys, subscriptions, or both?
|
|
8
|
+
* 2. Walk through selected path(s)
|
|
9
|
+
* 3. Auto-detect Ollama
|
|
10
|
+
* 4. Save config, show the one-liner for Claude Code
|
|
11
11
|
*
|
|
12
12
|
* Zero dependencies. Uses Node.js readline + child_process.
|
|
13
13
|
*/
|
|
@@ -30,10 +30,11 @@ const SUBS = [
|
|
|
30
30
|
{
|
|
31
31
|
name: "Gemini Advanced",
|
|
32
32
|
desc: "$20/mo — Gemini 2.5 Pro, Flash, etc.",
|
|
33
|
-
npmPkg: "@
|
|
33
|
+
npmPkg: "@google/gemini-cli",
|
|
34
34
|
command: "gemini",
|
|
35
35
|
authArgs: ["auth"],
|
|
36
36
|
authNote: "Browser will open for Google sign-in",
|
|
37
|
+
authTimeout: 120,
|
|
37
38
|
},
|
|
38
39
|
{
|
|
39
40
|
name: "Claude Pro / Max",
|
|
@@ -42,14 +43,16 @@ const SUBS = [
|
|
|
42
43
|
command: "claude",
|
|
43
44
|
authArgs: ["--version"],
|
|
44
45
|
authNote: "Opens browser on first interactive use",
|
|
46
|
+
authTimeout: 30,
|
|
45
47
|
},
|
|
46
48
|
{
|
|
47
49
|
name: "ChatGPT Plus / Pro",
|
|
48
50
|
desc: "$20–200/mo — GPT-5, o3, Codex",
|
|
49
51
|
npmPkg: "@openai/codex",
|
|
50
52
|
command: "codex",
|
|
51
|
-
authArgs: ["
|
|
52
|
-
authNote: "
|
|
53
|
+
authArgs: ["--version"],
|
|
54
|
+
authNote: "Run 'codex' interactively after setup to sign in",
|
|
55
|
+
authTimeout: 15,
|
|
53
56
|
},
|
|
54
57
|
];
|
|
55
58
|
// ---------------------------------------------------------------------------
|
|
@@ -70,14 +73,46 @@ function isOnPath(command) {
|
|
|
70
73
|
return false;
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Spawn a process with a timeout. Kills it if it doesn't exit in time.
|
|
78
|
+
* This prevents CLI tools from hanging the setup wizard.
|
|
79
|
+
*/
|
|
80
|
+
function spawnWithTimeout(command, args, timeoutSec) {
|
|
74
81
|
return new Promise((resolve, reject) => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
let child;
|
|
83
|
+
try {
|
|
84
|
+
child = nodeSpawn(command, args, {
|
|
85
|
+
stdio: "inherit",
|
|
86
|
+
shell: process.platform === "win32",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
reject(err);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let settled = false;
|
|
94
|
+
const timer = setTimeout(() => {
|
|
95
|
+
if (!settled) {
|
|
96
|
+
settled = true;
|
|
97
|
+
child.kill("SIGTERM");
|
|
98
|
+
// Treat timeout as success — the CLI tool ran, just didn't exit cleanly
|
|
99
|
+
resolve(0);
|
|
100
|
+
}
|
|
101
|
+
}, timeoutSec * 1000);
|
|
102
|
+
child.on("error", (err) => {
|
|
103
|
+
if (!settled) {
|
|
104
|
+
settled = true;
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
reject(err);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
child.on("close", (code) => {
|
|
110
|
+
if (!settled) {
|
|
111
|
+
settled = true;
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
resolve(code ?? 1);
|
|
114
|
+
}
|
|
78
115
|
});
|
|
79
|
-
child.on("error", reject);
|
|
80
|
-
child.on("close", (code) => resolve(code ?? 1));
|
|
81
116
|
});
|
|
82
117
|
}
|
|
83
118
|
function loadExistingEnv() {
|
|
@@ -122,79 +157,94 @@ export async function runSetup() {
|
|
|
122
157
|
console.log(` ${B}${C}HydraMCP Setup${X}`);
|
|
123
158
|
console.log(` ${D}Multi-model intelligence for Claude Code${X}`);
|
|
124
159
|
console.log("");
|
|
125
|
-
// ---
|
|
126
|
-
console.log(` ${B}
|
|
160
|
+
// --- Choose setup path ---
|
|
161
|
+
console.log(` ${B}How do you want to connect models?${X}`);
|
|
127
162
|
console.log("");
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
163
|
+
console.log(` ${B}1.${X} API Keys ${D}— pay-per-token, paste keys and go${X}`);
|
|
164
|
+
console.log(` ${B}2.${X} Subscriptions ${D}— use ChatGPT Plus, Claude Pro, Gemini Advanced${X}`);
|
|
165
|
+
console.log(` ${B}3.${X} Both`);
|
|
166
|
+
console.log("");
|
|
167
|
+
const pathChoice = await ask(` Choice ${D}(1/2/3)${X}: `);
|
|
168
|
+
const doApiKeys = pathChoice === "1" || pathChoice === "3";
|
|
169
|
+
const doSubscriptions = pathChoice === "2" || pathChoice === "3";
|
|
170
|
+
// --- API Keys ---
|
|
171
|
+
if (doApiKeys) {
|
|
172
|
+
console.log("");
|
|
173
|
+
console.log(` ${B}API Keys${X}`);
|
|
174
|
+
console.log("");
|
|
175
|
+
const keyDefs = [
|
|
176
|
+
{ env: "OPENAI_API_KEY", label: "OpenAI" },
|
|
177
|
+
{ env: "GOOGLE_API_KEY", label: "Google / Gemini" },
|
|
178
|
+
{ env: "ANTHROPIC_API_KEY", label: "Anthropic" },
|
|
179
|
+
];
|
|
180
|
+
for (const kd of keyDefs) {
|
|
181
|
+
const current = env[kd.env];
|
|
182
|
+
if (current) {
|
|
183
|
+
const change = await ask(` ${kd.label} ${D}[${mask(current)}]${X} — keep? ${D}(Enter=yes, or paste new key)${X}: `);
|
|
184
|
+
if (change)
|
|
185
|
+
env[kd.env] = change;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
const val = await ask(` ${kd.label} API key ${D}(Enter to skip)${X}: `);
|
|
189
|
+
if (val)
|
|
190
|
+
env[kd.env] = val;
|
|
191
|
+
}
|
|
192
|
+
if (env[kd.env])
|
|
193
|
+
results.push(`${G}+${X} ${kd.label} (API key)`);
|
|
144
194
|
}
|
|
145
|
-
if (env[kd.env])
|
|
146
|
-
results.push(`${G}+${X} ${kd.label} (API key)`);
|
|
147
195
|
}
|
|
148
196
|
// --- Subscriptions ---
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
for (let i = 0; i < SUBS.length; i++) {
|
|
153
|
-
const s = SUBS[i];
|
|
154
|
-
const installed = isOnPath(s.command);
|
|
155
|
-
const tag = installed ? `${G}installed${X}` : `${D}not installed${X}`;
|
|
156
|
-
console.log(` ${B}${i + 1}.${X} ${s.name} ${D}— ${s.desc}${X} [${tag}]`);
|
|
157
|
-
}
|
|
158
|
-
console.log("");
|
|
159
|
-
const subInput = await ask(` Which do you have? ${D}(e.g. 1,3 or Enter to skip)${X}: `);
|
|
160
|
-
const selectedIndexes = subInput
|
|
161
|
-
.split(",")
|
|
162
|
-
.map((s) => parseInt(s.trim(), 10) - 1)
|
|
163
|
-
.filter((i) => i >= 0 && i < SUBS.length);
|
|
164
|
-
for (const idx of selectedIndexes) {
|
|
165
|
-
const sub = SUBS[idx];
|
|
197
|
+
if (doSubscriptions) {
|
|
198
|
+
console.log("");
|
|
199
|
+
console.log(` ${B}Subscriptions${X}`);
|
|
166
200
|
console.log("");
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
201
|
+
for (let i = 0; i < SUBS.length; i++) {
|
|
202
|
+
const s = SUBS[i];
|
|
203
|
+
const installed = isOnPath(s.command);
|
|
204
|
+
const tag = installed ? `${G}installed${X}` : `${D}not installed${X}`;
|
|
205
|
+
console.log(` ${B}${i + 1}.${X} ${s.name} ${D}— ${s.desc}${X} [${tag}]`);
|
|
171
206
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
207
|
+
console.log("");
|
|
208
|
+
const subInput = await ask(` Which do you have? ${D}(e.g. 1,3 or Enter to skip)${X}: `);
|
|
209
|
+
const selectedIndexes = subInput
|
|
210
|
+
.split(",")
|
|
211
|
+
.map((s) => parseInt(s.trim(), 10) - 1)
|
|
212
|
+
.filter((i) => i >= 0 && i < SUBS.length);
|
|
213
|
+
for (const idx of selectedIndexes) {
|
|
214
|
+
const sub = SUBS[idx];
|
|
215
|
+
console.log("");
|
|
216
|
+
console.log(` ${C}${sub.name}${X}`);
|
|
217
|
+
// Check / install
|
|
218
|
+
if (isOnPath(sub.command)) {
|
|
219
|
+
console.log(` ${G}✓${X} ${sub.command} already installed`);
|
|
177
220
|
}
|
|
178
|
-
|
|
179
|
-
console.log(`
|
|
180
|
-
|
|
221
|
+
else {
|
|
222
|
+
console.log(` Installing ${sub.npmPkg}...`);
|
|
223
|
+
try {
|
|
224
|
+
execSync(`npm i -g ${sub.npmPkg}`, { stdio: "inherit" });
|
|
225
|
+
console.log(` ${G}✓${X} Installed`);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
console.log(` ${R}✗${X} Install failed. Try manually: ${Y}sudo npm i -g ${sub.npmPkg}${X}`);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
181
231
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
232
|
+
// Auth — with timeout to prevent hanging
|
|
233
|
+
console.log(` ${D}${sub.authNote}${X}`);
|
|
234
|
+
try {
|
|
235
|
+
const code = await spawnWithTimeout(sub.command, sub.authArgs, sub.authTimeout);
|
|
236
|
+
if (code === 0) {
|
|
237
|
+
console.log(` ${G}✓${X} Ready`);
|
|
238
|
+
results.push(`${G}+${X} ${sub.name} (subscription)`);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
console.log(` ${Y}!${X} Auth may need manual setup: ${Y}${sub.command} ${sub.authArgs.join(" ")}${X}`);
|
|
242
|
+
}
|
|
190
243
|
}
|
|
191
|
-
|
|
192
|
-
console.log(` ${Y}!${X}
|
|
244
|
+
catch {
|
|
245
|
+
console.log(` ${Y}!${X} Could not run auth. Try: ${Y}${sub.command} ${sub.authArgs.join(" ")}${X}`);
|
|
193
246
|
}
|
|
194
247
|
}
|
|
195
|
-
catch {
|
|
196
|
-
console.log(` ${Y}!${X} Could not run auth. Try: ${Y}${sub.command} ${sub.authArgs.join(" ")}${X}`);
|
|
197
|
-
}
|
|
198
248
|
}
|
|
199
249
|
// --- Ollama ---
|
|
200
250
|
console.log("");
|
package/package.json
CHANGED