@sashabogi/foundation 0.1.5 → 0.1.7
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/cli/setup-wizard.d.ts +30 -0
- package/dist/cli/setup-wizard.d.ts.map +1 -0
- package/dist/cli/setup-wizard.js +1645 -0
- package/dist/cli/setup-wizard.js.map +1 -0
- package/dist/cli/test-connection.d.ts +76 -0
- package/dist/cli/test-connection.d.ts.map +1 -0
- package/dist/cli/test-connection.js +697 -0
- package/dist/cli/test-connection.js.map +1 -0
- package/dist/cli.d.ts +3 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +133 -5
- package/dist/cli.js.map +1 -1
- package/dist/providers/anthropic.d.ts +178 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +514 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/base.d.ts +154 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +227 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/deepseek.d.ts +23 -0
- package/dist/providers/deepseek.d.ts.map +1 -0
- package/dist/providers/deepseek.js +31 -0
- package/dist/providers/deepseek.js.map +1 -0
- package/dist/providers/fireworks.d.ts +23 -0
- package/dist/providers/fireworks.d.ts.map +1 -0
- package/dist/providers/fireworks.js +31 -0
- package/dist/providers/fireworks.js.map +1 -0
- package/dist/providers/gemini.d.ts +85 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +414 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/groq.d.ts +23 -0
- package/dist/providers/groq.d.ts.map +1 -0
- package/dist/providers/groq.js +31 -0
- package/dist/providers/groq.js.map +1 -0
- package/dist/providers/index.d.ts +23 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +27 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/kimi-code.d.ts +32 -0
- package/dist/providers/kimi-code.d.ts.map +1 -0
- package/dist/providers/kimi-code.js +46 -0
- package/dist/providers/kimi-code.js.map +1 -0
- package/dist/providers/kimi.d.ts +19 -0
- package/dist/providers/kimi.d.ts.map +1 -0
- package/dist/providers/kimi.js +27 -0
- package/dist/providers/kimi.js.map +1 -0
- package/dist/providers/manager.d.ts +144 -0
- package/dist/providers/manager.d.ts.map +1 -0
- package/dist/providers/manager.js +279 -0
- package/dist/providers/manager.js.map +1 -0
- package/dist/providers/ollama.d.ts +83 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +450 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai.d.ts +91 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +445 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openrouter.d.ts +23 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.js +31 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/perplexity.d.ts +34 -0
- package/dist/providers/perplexity.d.ts.map +1 -0
- package/dist/providers/perplexity.js +58 -0
- package/dist/providers/perplexity.js.map +1 -0
- package/dist/providers/together.d.ts +23 -0
- package/dist/providers/together.d.ts.map +1 -0
- package/dist/providers/together.js +31 -0
- package/dist/providers/together.js.map +1 -0
- package/dist/providers/types.d.ts +229 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +73 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/providers/zai.d.ts +19 -0
- package/dist/providers/zai.d.ts.map +1 -0
- package/dist/providers/zai.js +27 -0
- package/dist/providers/zai.js.map +1 -0
- package/dist/services/provider.service.d.ts +28 -0
- package/dist/services/provider.service.d.ts.map +1 -1
- package/dist/services/provider.service.js +137 -13
- package/dist/services/provider.service.js.map +1 -1
- package/dist/tools/demerzel/engine.d.ts +67 -0
- package/dist/tools/demerzel/engine.d.ts.map +1 -0
- package/dist/tools/demerzel/engine.js +401 -0
- package/dist/tools/demerzel/engine.js.map +1 -0
- package/dist/tools/demerzel/enhanced-snapshot.d.ts +67 -0
- package/dist/tools/demerzel/enhanced-snapshot.d.ts.map +1 -0
- package/dist/tools/demerzel/enhanced-snapshot.js +481 -0
- package/dist/tools/demerzel/enhanced-snapshot.js.map +1 -0
- package/dist/tools/demerzel/index.d.ts +11 -0
- package/dist/tools/demerzel/index.d.ts.map +1 -1
- package/dist/tools/demerzel/index.js +656 -85
- package/dist/tools/demerzel/index.js.map +1 -1
- package/dist/tools/demerzel/prompts.d.ts +26 -0
- package/dist/tools/demerzel/prompts.d.ts.map +1 -0
- package/dist/tools/demerzel/prompts.js +181 -0
- package/dist/tools/demerzel/prompts.js.map +1 -0
- package/dist/tools/demerzel/semantic-search.d.ts +54 -0
- package/dist/tools/demerzel/semantic-search.d.ts.map +1 -0
- package/dist/tools/demerzel/semantic-search.js +205 -0
- package/dist/tools/demerzel/semantic-search.js.map +1 -0
- package/dist/tools/demerzel/snapshot.d.ts +30 -0
- package/dist/tools/demerzel/snapshot.d.ts.map +1 -0
- package/dist/tools/demerzel/snapshot.js +169 -0
- package/dist/tools/demerzel/snapshot.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider connection testing utilities for Foundation
|
|
3
|
+
* Tests connectivity to LLM providers
|
|
4
|
+
*
|
|
5
|
+
* Updated February 2026 to include all providers.
|
|
6
|
+
*/
|
|
7
|
+
import * as p from "@clack/prompts";
|
|
8
|
+
import color from "picocolors";
|
|
9
|
+
/**
|
|
10
|
+
* Test Anthropic API connection
|
|
11
|
+
*/
|
|
12
|
+
export async function testAnthropicConnection(apiKey, baseUrl = "https://api.anthropic.com/v1") {
|
|
13
|
+
const startTime = Date.now();
|
|
14
|
+
try {
|
|
15
|
+
// Anthropic doesn't have a /models endpoint, so we do a minimal completion test
|
|
16
|
+
const response = await fetch(`${baseUrl}/messages`, {
|
|
17
|
+
method: "POST",
|
|
18
|
+
headers: {
|
|
19
|
+
"x-api-key": apiKey,
|
|
20
|
+
"anthropic-version": "2023-06-01",
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
},
|
|
23
|
+
body: JSON.stringify({
|
|
24
|
+
model: "claude-haiku-4-5-20251001",
|
|
25
|
+
max_tokens: 1,
|
|
26
|
+
messages: [{ role: "user", content: "Hi" }],
|
|
27
|
+
}),
|
|
28
|
+
signal: AbortSignal.timeout(15000),
|
|
29
|
+
});
|
|
30
|
+
const latencyMs = Date.now() - startTime;
|
|
31
|
+
if (response.ok) {
|
|
32
|
+
return { success: true, latencyMs };
|
|
33
|
+
}
|
|
34
|
+
await response.text(); // Consume body
|
|
35
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
36
|
+
if (response.status === 401) {
|
|
37
|
+
errorMessage = "Invalid API key - check your Anthropic console";
|
|
38
|
+
}
|
|
39
|
+
else if (response.status === 403) {
|
|
40
|
+
errorMessage = "API key lacks permission - check your Anthropic console";
|
|
41
|
+
}
|
|
42
|
+
else if (response.status === 429) {
|
|
43
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
44
|
+
}
|
|
45
|
+
else if (response.status >= 500) {
|
|
46
|
+
errorMessage = "Anthropic server error - try again later";
|
|
47
|
+
}
|
|
48
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
const latencyMs = Date.now() - startTime;
|
|
52
|
+
let errorMessage = "Unknown error";
|
|
53
|
+
if (error instanceof Error) {
|
|
54
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
55
|
+
errorMessage = "Connection timeout - server not responding";
|
|
56
|
+
}
|
|
57
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
58
|
+
errorMessage = "Connection refused - check Anthropic service status";
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
errorMessage = error.message;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Test OpenAI API connection
|
|
69
|
+
*/
|
|
70
|
+
export async function testOpenAIConnection(apiKey, baseUrl = "https://api.openai.com/v1") {
|
|
71
|
+
const startTime = Date.now();
|
|
72
|
+
try {
|
|
73
|
+
const response = await fetch(`${baseUrl}/models`, {
|
|
74
|
+
method: "GET",
|
|
75
|
+
headers: {
|
|
76
|
+
Authorization: `Bearer ${apiKey}`,
|
|
77
|
+
},
|
|
78
|
+
signal: AbortSignal.timeout(10000),
|
|
79
|
+
});
|
|
80
|
+
const latencyMs = Date.now() - startTime;
|
|
81
|
+
if (response.ok) {
|
|
82
|
+
return { success: true, latencyMs };
|
|
83
|
+
}
|
|
84
|
+
await response.text(); // Consume body
|
|
85
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
86
|
+
if (response.status === 401) {
|
|
87
|
+
errorMessage = "Invalid API key - check your OpenAI dashboard";
|
|
88
|
+
}
|
|
89
|
+
else if (response.status === 429) {
|
|
90
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
91
|
+
}
|
|
92
|
+
else if (response.status >= 500) {
|
|
93
|
+
errorMessage = "OpenAI server error - try again later";
|
|
94
|
+
}
|
|
95
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const latencyMs = Date.now() - startTime;
|
|
99
|
+
let errorMessage = "Unknown error";
|
|
100
|
+
if (error instanceof Error) {
|
|
101
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
102
|
+
errorMessage = "Connection timeout - server not responding";
|
|
103
|
+
}
|
|
104
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
105
|
+
errorMessage = "Connection refused - check OpenAI service status";
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
errorMessage = error.message;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Test Google Gemini API connection
|
|
116
|
+
*/
|
|
117
|
+
export async function testGeminiConnection(apiKey, baseUrl = "https://generativelanguage.googleapis.com/v1beta") {
|
|
118
|
+
const startTime = Date.now();
|
|
119
|
+
try {
|
|
120
|
+
const response = await fetch(`${baseUrl}/models?key=${apiKey}`, {
|
|
121
|
+
method: "GET",
|
|
122
|
+
signal: AbortSignal.timeout(10000),
|
|
123
|
+
});
|
|
124
|
+
const latencyMs = Date.now() - startTime;
|
|
125
|
+
if (response.ok) {
|
|
126
|
+
return { success: true, latencyMs };
|
|
127
|
+
}
|
|
128
|
+
await response.text(); // Consume body
|
|
129
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
130
|
+
if (response.status === 400 || response.status === 403) {
|
|
131
|
+
errorMessage = "Invalid API key - check your Google AI Studio dashboard";
|
|
132
|
+
}
|
|
133
|
+
else if (response.status === 429) {
|
|
134
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
135
|
+
}
|
|
136
|
+
else if (response.status >= 500) {
|
|
137
|
+
errorMessage = "Gemini server error - try again later";
|
|
138
|
+
}
|
|
139
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
const latencyMs = Date.now() - startTime;
|
|
143
|
+
let errorMessage = "Unknown error";
|
|
144
|
+
if (error instanceof Error) {
|
|
145
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
146
|
+
errorMessage = "Connection timeout - server not responding";
|
|
147
|
+
}
|
|
148
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
149
|
+
errorMessage = "Connection refused - check Google AI service status";
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
errorMessage = error.message;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Test DeepSeek API connection
|
|
160
|
+
*/
|
|
161
|
+
export async function testDeepSeekConnection(apiKey, baseUrl = "https://api.deepseek.com") {
|
|
162
|
+
const startTime = Date.now();
|
|
163
|
+
try {
|
|
164
|
+
const response = await fetch(`${baseUrl}/models`, {
|
|
165
|
+
method: "GET",
|
|
166
|
+
headers: {
|
|
167
|
+
Authorization: `Bearer ${apiKey}`,
|
|
168
|
+
},
|
|
169
|
+
signal: AbortSignal.timeout(10000),
|
|
170
|
+
});
|
|
171
|
+
const latencyMs = Date.now() - startTime;
|
|
172
|
+
if (response.ok) {
|
|
173
|
+
return { success: true, latencyMs };
|
|
174
|
+
}
|
|
175
|
+
await response.text(); // Consume body
|
|
176
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
177
|
+
if (response.status === 401) {
|
|
178
|
+
errorMessage = "Invalid API key - check your DeepSeek dashboard";
|
|
179
|
+
}
|
|
180
|
+
else if (response.status === 429) {
|
|
181
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
182
|
+
}
|
|
183
|
+
else if (response.status >= 500) {
|
|
184
|
+
errorMessage = "DeepSeek server error - try again later";
|
|
185
|
+
}
|
|
186
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
const latencyMs = Date.now() - startTime;
|
|
190
|
+
let errorMessage = "Unknown error";
|
|
191
|
+
if (error instanceof Error) {
|
|
192
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
193
|
+
errorMessage = "Connection timeout - server not responding";
|
|
194
|
+
}
|
|
195
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
196
|
+
errorMessage = "Connection refused - check DeepSeek service status";
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
errorMessage = error.message;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Test Z.AI (GLM) API connection
|
|
207
|
+
*/
|
|
208
|
+
export async function testZaiConnection(apiKey, baseUrl = "https://api.z.ai/api/paas/v4") {
|
|
209
|
+
const startTime = Date.now();
|
|
210
|
+
try {
|
|
211
|
+
// Z.AI uses a simple chat completion test
|
|
212
|
+
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
213
|
+
method: "POST",
|
|
214
|
+
headers: {
|
|
215
|
+
Authorization: `Bearer ${apiKey}`,
|
|
216
|
+
"Content-Type": "application/json",
|
|
217
|
+
},
|
|
218
|
+
body: JSON.stringify({
|
|
219
|
+
model: "glm-4.7-flash",
|
|
220
|
+
messages: [{ role: "user", content: "Hi" }],
|
|
221
|
+
max_tokens: 1,
|
|
222
|
+
}),
|
|
223
|
+
signal: AbortSignal.timeout(15000),
|
|
224
|
+
});
|
|
225
|
+
const latencyMs = Date.now() - startTime;
|
|
226
|
+
if (response.ok) {
|
|
227
|
+
return { success: true, latencyMs };
|
|
228
|
+
}
|
|
229
|
+
await response.text(); // Consume body
|
|
230
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
231
|
+
if (response.status === 401) {
|
|
232
|
+
errorMessage = "Invalid API key - check your Z.AI dashboard";
|
|
233
|
+
}
|
|
234
|
+
else if (response.status === 429) {
|
|
235
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
236
|
+
}
|
|
237
|
+
else if (response.status >= 500) {
|
|
238
|
+
errorMessage = "Z.AI server error - try again later";
|
|
239
|
+
}
|
|
240
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
const latencyMs = Date.now() - startTime;
|
|
244
|
+
let errorMessage = "Unknown error";
|
|
245
|
+
if (error instanceof Error) {
|
|
246
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
247
|
+
errorMessage = "Connection timeout - server not responding";
|
|
248
|
+
}
|
|
249
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
250
|
+
errorMessage = "Connection refused - check Z.AI service status";
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
errorMessage = error.message;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Test Moonshot (Kimi) API connection
|
|
261
|
+
*/
|
|
262
|
+
export async function testKimiConnection(apiKey, baseUrl = "https://api.moonshot.ai/v1") {
|
|
263
|
+
const startTime = Date.now();
|
|
264
|
+
try {
|
|
265
|
+
// Moonshot uses OpenAI-compatible API, test with models endpoint
|
|
266
|
+
const response = await fetch(`${baseUrl}/models`, {
|
|
267
|
+
method: "GET",
|
|
268
|
+
headers: {
|
|
269
|
+
Authorization: `Bearer ${apiKey}`,
|
|
270
|
+
},
|
|
271
|
+
signal: AbortSignal.timeout(10000),
|
|
272
|
+
});
|
|
273
|
+
const latencyMs = Date.now() - startTime;
|
|
274
|
+
if (response.ok) {
|
|
275
|
+
return { success: true, latencyMs };
|
|
276
|
+
}
|
|
277
|
+
await response.text(); // Consume body
|
|
278
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
279
|
+
if (response.status === 401) {
|
|
280
|
+
errorMessage = "Invalid API key - check your Moonshot console";
|
|
281
|
+
}
|
|
282
|
+
else if (response.status === 429) {
|
|
283
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
284
|
+
}
|
|
285
|
+
else if (response.status >= 500) {
|
|
286
|
+
errorMessage = "Moonshot server error - try again later";
|
|
287
|
+
}
|
|
288
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
const latencyMs = Date.now() - startTime;
|
|
292
|
+
let errorMessage = "Unknown error";
|
|
293
|
+
if (error instanceof Error) {
|
|
294
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
295
|
+
errorMessage = "Connection timeout - server not responding";
|
|
296
|
+
}
|
|
297
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
298
|
+
errorMessage = "Connection refused - check Moonshot service status";
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
errorMessage = error.message;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Test Kimi Code API connection
|
|
309
|
+
*
|
|
310
|
+
* Kimi Code is a separate endpoint from the regular Moonshot API.
|
|
311
|
+
* CRITICAL: Requires User-Agent: claude-code/1.0 header or requests are rejected.
|
|
312
|
+
*/
|
|
313
|
+
export async function testKimiCodeConnection(apiKey, baseUrl = "https://api.kimi.com/coding/v1") {
|
|
314
|
+
const startTime = Date.now();
|
|
315
|
+
try {
|
|
316
|
+
// Try the models endpoint first with required User-Agent header
|
|
317
|
+
const response = await fetch(`${baseUrl}/models`, {
|
|
318
|
+
method: "GET",
|
|
319
|
+
headers: {
|
|
320
|
+
Authorization: `Bearer ${apiKey}`,
|
|
321
|
+
"User-Agent": "claude-code/1.0",
|
|
322
|
+
},
|
|
323
|
+
signal: AbortSignal.timeout(10000),
|
|
324
|
+
});
|
|
325
|
+
const latencyMs = Date.now() - startTime;
|
|
326
|
+
if (response.ok) {
|
|
327
|
+
return { success: true, latencyMs };
|
|
328
|
+
}
|
|
329
|
+
// If models endpoint fails, try a minimal chat completion
|
|
330
|
+
if (response.status === 404) {
|
|
331
|
+
const chatResponse = await fetch(`${baseUrl}/chat/completions`, {
|
|
332
|
+
method: "POST",
|
|
333
|
+
headers: {
|
|
334
|
+
Authorization: `Bearer ${apiKey}`,
|
|
335
|
+
"Content-Type": "application/json",
|
|
336
|
+
"User-Agent": "claude-code/1.0",
|
|
337
|
+
},
|
|
338
|
+
body: JSON.stringify({
|
|
339
|
+
model: "kimi-for-coding",
|
|
340
|
+
messages: [{ role: "user", content: "Hi" }],
|
|
341
|
+
max_tokens: 1,
|
|
342
|
+
}),
|
|
343
|
+
signal: AbortSignal.timeout(15000),
|
|
344
|
+
});
|
|
345
|
+
const chatLatencyMs = Date.now() - startTime;
|
|
346
|
+
if (chatResponse.ok) {
|
|
347
|
+
return { success: true, latencyMs: chatLatencyMs };
|
|
348
|
+
}
|
|
349
|
+
await chatResponse.text(); // Consume body
|
|
350
|
+
let errorMessage = `HTTP ${chatResponse.status}`;
|
|
351
|
+
if (chatResponse.status === 401) {
|
|
352
|
+
errorMessage = "Invalid API key - check your Kimi subscription";
|
|
353
|
+
}
|
|
354
|
+
else if (chatResponse.status === 403) {
|
|
355
|
+
errorMessage = "Access denied - ensure you have Kimi Code access";
|
|
356
|
+
}
|
|
357
|
+
else if (chatResponse.status === 429) {
|
|
358
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
359
|
+
}
|
|
360
|
+
else if (chatResponse.status >= 500) {
|
|
361
|
+
errorMessage = "Kimi Code server error - try again later";
|
|
362
|
+
}
|
|
363
|
+
return { success: false, latencyMs: chatLatencyMs, error: errorMessage };
|
|
364
|
+
}
|
|
365
|
+
await response.text(); // Consume body
|
|
366
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
367
|
+
if (response.status === 401) {
|
|
368
|
+
errorMessage = "Invalid API key - check your Kimi subscription";
|
|
369
|
+
}
|
|
370
|
+
else if (response.status === 403) {
|
|
371
|
+
errorMessage = "Access denied - ensure you have Kimi Code access";
|
|
372
|
+
}
|
|
373
|
+
else if (response.status === 429) {
|
|
374
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
375
|
+
}
|
|
376
|
+
else if (response.status >= 500) {
|
|
377
|
+
errorMessage = "Kimi Code server error - try again later";
|
|
378
|
+
}
|
|
379
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
const latencyMs = Date.now() - startTime;
|
|
383
|
+
let errorMessage = "Unknown error";
|
|
384
|
+
if (error instanceof Error) {
|
|
385
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
386
|
+
errorMessage = "Connection timeout - server not responding";
|
|
387
|
+
}
|
|
388
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
389
|
+
errorMessage = "Connection refused - check Kimi Code service status";
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
errorMessage = error.message;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Test Perplexity API connection
|
|
400
|
+
* Note: Perplexity does NOT have a /models endpoint, so we use chat completion test
|
|
401
|
+
*/
|
|
402
|
+
export async function testPerplexityConnection(apiKey, baseUrl = "https://api.perplexity.ai") {
|
|
403
|
+
const startTime = Date.now();
|
|
404
|
+
try {
|
|
405
|
+
// Perplexity doesn't have a /models endpoint, so we do a minimal completion test
|
|
406
|
+
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
407
|
+
method: "POST",
|
|
408
|
+
headers: {
|
|
409
|
+
Authorization: `Bearer ${apiKey}`,
|
|
410
|
+
"Content-Type": "application/json",
|
|
411
|
+
},
|
|
412
|
+
body: JSON.stringify({
|
|
413
|
+
model: "sonar",
|
|
414
|
+
messages: [{ role: "user", content: "Hi" }],
|
|
415
|
+
max_tokens: 1,
|
|
416
|
+
}),
|
|
417
|
+
signal: AbortSignal.timeout(15000),
|
|
418
|
+
});
|
|
419
|
+
const latencyMs = Date.now() - startTime;
|
|
420
|
+
if (response.ok) {
|
|
421
|
+
return { success: true, latencyMs };
|
|
422
|
+
}
|
|
423
|
+
await response.text(); // Consume body
|
|
424
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
425
|
+
if (response.status === 401) {
|
|
426
|
+
errorMessage = "Invalid API key - check your Perplexity dashboard";
|
|
427
|
+
}
|
|
428
|
+
else if (response.status === 429) {
|
|
429
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
430
|
+
}
|
|
431
|
+
else if (response.status >= 500) {
|
|
432
|
+
errorMessage = "Perplexity server error - try again later";
|
|
433
|
+
}
|
|
434
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
const latencyMs = Date.now() - startTime;
|
|
438
|
+
let errorMessage = "Unknown error";
|
|
439
|
+
if (error instanceof Error) {
|
|
440
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
441
|
+
errorMessage = "Connection timeout - server not responding";
|
|
442
|
+
}
|
|
443
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
444
|
+
errorMessage = "Connection refused - check Perplexity service status";
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
errorMessage = error.message;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Test OpenRouter API connection
|
|
455
|
+
*/
|
|
456
|
+
export async function testOpenRouterConnection(apiKey, baseUrl = "https://openrouter.ai/api/v1") {
|
|
457
|
+
const startTime = Date.now();
|
|
458
|
+
try {
|
|
459
|
+
const response = await fetch(`${baseUrl}/models`, {
|
|
460
|
+
method: "GET",
|
|
461
|
+
headers: {
|
|
462
|
+
Authorization: `Bearer ${apiKey}`,
|
|
463
|
+
},
|
|
464
|
+
signal: AbortSignal.timeout(10000),
|
|
465
|
+
});
|
|
466
|
+
const latencyMs = Date.now() - startTime;
|
|
467
|
+
if (response.ok) {
|
|
468
|
+
return { success: true, latencyMs };
|
|
469
|
+
}
|
|
470
|
+
await response.text(); // Consume body
|
|
471
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
472
|
+
if (response.status === 401) {
|
|
473
|
+
errorMessage = "Invalid API key - check your OpenRouter dashboard";
|
|
474
|
+
}
|
|
475
|
+
else if (response.status === 429) {
|
|
476
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
477
|
+
}
|
|
478
|
+
else if (response.status >= 500) {
|
|
479
|
+
errorMessage = "OpenRouter server error - try again later";
|
|
480
|
+
}
|
|
481
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
482
|
+
}
|
|
483
|
+
catch (error) {
|
|
484
|
+
const latencyMs = Date.now() - startTime;
|
|
485
|
+
let errorMessage = "Unknown error";
|
|
486
|
+
if (error instanceof Error) {
|
|
487
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
488
|
+
errorMessage = "Connection timeout - server not responding";
|
|
489
|
+
}
|
|
490
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
491
|
+
errorMessage = "Connection refused - check OpenRouter service status";
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
errorMessage = error.message;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Test Groq API connection
|
|
502
|
+
*/
|
|
503
|
+
export async function testGroqConnection(apiKey, baseUrl = "https://api.groq.com/openai/v1") {
|
|
504
|
+
const startTime = Date.now();
|
|
505
|
+
try {
|
|
506
|
+
const response = await fetch(`${baseUrl}/models`, {
|
|
507
|
+
method: "GET",
|
|
508
|
+
headers: {
|
|
509
|
+
Authorization: `Bearer ${apiKey}`,
|
|
510
|
+
},
|
|
511
|
+
signal: AbortSignal.timeout(10000),
|
|
512
|
+
});
|
|
513
|
+
const latencyMs = Date.now() - startTime;
|
|
514
|
+
if (response.ok) {
|
|
515
|
+
return { success: true, latencyMs };
|
|
516
|
+
}
|
|
517
|
+
await response.text(); // Consume body
|
|
518
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
519
|
+
if (response.status === 401) {
|
|
520
|
+
errorMessage = "Invalid API key - check your Groq dashboard";
|
|
521
|
+
}
|
|
522
|
+
else if (response.status === 429) {
|
|
523
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
524
|
+
}
|
|
525
|
+
else if (response.status >= 500) {
|
|
526
|
+
errorMessage = "Groq server error - try again later";
|
|
527
|
+
}
|
|
528
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
529
|
+
}
|
|
530
|
+
catch (error) {
|
|
531
|
+
const latencyMs = Date.now() - startTime;
|
|
532
|
+
let errorMessage = "Unknown error";
|
|
533
|
+
if (error instanceof Error) {
|
|
534
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
535
|
+
errorMessage = "Connection timeout - server not responding";
|
|
536
|
+
}
|
|
537
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
538
|
+
errorMessage = "Connection refused - check Groq service status";
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
errorMessage = error.message;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Test Together AI API connection
|
|
549
|
+
*/
|
|
550
|
+
export async function testTogetherConnection(apiKey, baseUrl = "https://api.together.xyz/v1") {
|
|
551
|
+
const startTime = Date.now();
|
|
552
|
+
try {
|
|
553
|
+
const response = await fetch(`${baseUrl}/models`, {
|
|
554
|
+
method: "GET",
|
|
555
|
+
headers: {
|
|
556
|
+
Authorization: `Bearer ${apiKey}`,
|
|
557
|
+
},
|
|
558
|
+
signal: AbortSignal.timeout(10000),
|
|
559
|
+
});
|
|
560
|
+
const latencyMs = Date.now() - startTime;
|
|
561
|
+
if (response.ok) {
|
|
562
|
+
return { success: true, latencyMs };
|
|
563
|
+
}
|
|
564
|
+
await response.text(); // Consume body
|
|
565
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
566
|
+
if (response.status === 401) {
|
|
567
|
+
errorMessage = "Invalid API key - check your Together AI dashboard";
|
|
568
|
+
}
|
|
569
|
+
else if (response.status === 429) {
|
|
570
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
571
|
+
}
|
|
572
|
+
else if (response.status >= 500) {
|
|
573
|
+
errorMessage = "Together AI server error - try again later";
|
|
574
|
+
}
|
|
575
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
576
|
+
}
|
|
577
|
+
catch (error) {
|
|
578
|
+
const latencyMs = Date.now() - startTime;
|
|
579
|
+
let errorMessage = "Unknown error";
|
|
580
|
+
if (error instanceof Error) {
|
|
581
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
582
|
+
errorMessage = "Connection timeout - server not responding";
|
|
583
|
+
}
|
|
584
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
585
|
+
errorMessage = "Connection refused - check Together AI service status";
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
errorMessage = error.message;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Test Fireworks AI API connection
|
|
596
|
+
*/
|
|
597
|
+
export async function testFireworksConnection(apiKey, baseUrl = "https://api.fireworks.ai/inference/v1") {
|
|
598
|
+
const startTime = Date.now();
|
|
599
|
+
try {
|
|
600
|
+
const response = await fetch(`${baseUrl}/models`, {
|
|
601
|
+
method: "GET",
|
|
602
|
+
headers: {
|
|
603
|
+
Authorization: `Bearer ${apiKey}`,
|
|
604
|
+
},
|
|
605
|
+
signal: AbortSignal.timeout(10000),
|
|
606
|
+
});
|
|
607
|
+
const latencyMs = Date.now() - startTime;
|
|
608
|
+
if (response.ok) {
|
|
609
|
+
return { success: true, latencyMs };
|
|
610
|
+
}
|
|
611
|
+
await response.text(); // Consume body
|
|
612
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
613
|
+
if (response.status === 401) {
|
|
614
|
+
errorMessage = "Invalid API key - check your Fireworks AI dashboard";
|
|
615
|
+
}
|
|
616
|
+
else if (response.status === 429) {
|
|
617
|
+
errorMessage = "Rate limited - quota exceeded";
|
|
618
|
+
}
|
|
619
|
+
else if (response.status >= 500) {
|
|
620
|
+
errorMessage = "Fireworks AI server error - try again later";
|
|
621
|
+
}
|
|
622
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
623
|
+
}
|
|
624
|
+
catch (error) {
|
|
625
|
+
const latencyMs = Date.now() - startTime;
|
|
626
|
+
let errorMessage = "Unknown error";
|
|
627
|
+
if (error instanceof Error) {
|
|
628
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
629
|
+
errorMessage = "Connection timeout - server not responding";
|
|
630
|
+
}
|
|
631
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
632
|
+
errorMessage = "Connection refused - check Fireworks AI service status";
|
|
633
|
+
}
|
|
634
|
+
else {
|
|
635
|
+
errorMessage = error.message;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Test Ollama connection and fetch available models
|
|
643
|
+
*/
|
|
644
|
+
export async function testOllamaConnection(baseUrl = "http://localhost:11434") {
|
|
645
|
+
const startTime = Date.now();
|
|
646
|
+
try {
|
|
647
|
+
const response = await fetch(`${baseUrl}/api/tags`, {
|
|
648
|
+
method: "GET",
|
|
649
|
+
signal: AbortSignal.timeout(5000),
|
|
650
|
+
});
|
|
651
|
+
const latencyMs = Date.now() - startTime;
|
|
652
|
+
if (response.ok) {
|
|
653
|
+
const data = (await response.json());
|
|
654
|
+
const models = data.models?.map((m) => m.name) ?? [];
|
|
655
|
+
return { success: true, latencyMs, models };
|
|
656
|
+
}
|
|
657
|
+
return {
|
|
658
|
+
success: false,
|
|
659
|
+
latencyMs,
|
|
660
|
+
error: `HTTP ${response.status} - Ollama API error`,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
const latencyMs = Date.now() - startTime;
|
|
665
|
+
let errorMessage = "Unknown error";
|
|
666
|
+
if (error instanceof Error) {
|
|
667
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
668
|
+
errorMessage = "Connection timeout - is Ollama running?";
|
|
669
|
+
}
|
|
670
|
+
else if (error.message.includes("ECONNREFUSED")) {
|
|
671
|
+
errorMessage = "Connection refused - start Ollama with: ollama serve";
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
errorMessage = error.message;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return { success: false, latencyMs, error: errorMessage };
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Display spinner while testing connection
|
|
682
|
+
*/
|
|
683
|
+
export async function testConnectionWithSpinner(providerName, testFn) {
|
|
684
|
+
const spinner = p.spinner();
|
|
685
|
+
spinner.start(`Testing ${providerName} connection...`);
|
|
686
|
+
const result = await testFn();
|
|
687
|
+
if (result.success) {
|
|
688
|
+
spinner.stop(color.green(`Connected to ${providerName}`) +
|
|
689
|
+
color.dim(` (${result.latencyMs}ms)`));
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
spinner.stop(color.red(`Failed to connect to ${providerName}`));
|
|
693
|
+
p.log.error(color.dim(result.error ?? "Unknown error"));
|
|
694
|
+
}
|
|
695
|
+
return result;
|
|
696
|
+
}
|
|
697
|
+
//# sourceMappingURL=test-connection.js.map
|