@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.
Files changed (109) hide show
  1. package/dist/cli/setup-wizard.d.ts +30 -0
  2. package/dist/cli/setup-wizard.d.ts.map +1 -0
  3. package/dist/cli/setup-wizard.js +1645 -0
  4. package/dist/cli/setup-wizard.js.map +1 -0
  5. package/dist/cli/test-connection.d.ts +76 -0
  6. package/dist/cli/test-connection.d.ts.map +1 -0
  7. package/dist/cli/test-connection.js +697 -0
  8. package/dist/cli/test-connection.js.map +1 -0
  9. package/dist/cli.d.ts +3 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +133 -5
  12. package/dist/cli.js.map +1 -1
  13. package/dist/providers/anthropic.d.ts +178 -0
  14. package/dist/providers/anthropic.d.ts.map +1 -0
  15. package/dist/providers/anthropic.js +514 -0
  16. package/dist/providers/anthropic.js.map +1 -0
  17. package/dist/providers/base.d.ts +154 -0
  18. package/dist/providers/base.d.ts.map +1 -0
  19. package/dist/providers/base.js +227 -0
  20. package/dist/providers/base.js.map +1 -0
  21. package/dist/providers/deepseek.d.ts +23 -0
  22. package/dist/providers/deepseek.d.ts.map +1 -0
  23. package/dist/providers/deepseek.js +31 -0
  24. package/dist/providers/deepseek.js.map +1 -0
  25. package/dist/providers/fireworks.d.ts +23 -0
  26. package/dist/providers/fireworks.d.ts.map +1 -0
  27. package/dist/providers/fireworks.js +31 -0
  28. package/dist/providers/fireworks.js.map +1 -0
  29. package/dist/providers/gemini.d.ts +85 -0
  30. package/dist/providers/gemini.d.ts.map +1 -0
  31. package/dist/providers/gemini.js +414 -0
  32. package/dist/providers/gemini.js.map +1 -0
  33. package/dist/providers/groq.d.ts +23 -0
  34. package/dist/providers/groq.d.ts.map +1 -0
  35. package/dist/providers/groq.js +31 -0
  36. package/dist/providers/groq.js.map +1 -0
  37. package/dist/providers/index.d.ts +23 -0
  38. package/dist/providers/index.d.ts.map +1 -0
  39. package/dist/providers/index.js +27 -0
  40. package/dist/providers/index.js.map +1 -0
  41. package/dist/providers/kimi-code.d.ts +32 -0
  42. package/dist/providers/kimi-code.d.ts.map +1 -0
  43. package/dist/providers/kimi-code.js +46 -0
  44. package/dist/providers/kimi-code.js.map +1 -0
  45. package/dist/providers/kimi.d.ts +19 -0
  46. package/dist/providers/kimi.d.ts.map +1 -0
  47. package/dist/providers/kimi.js +27 -0
  48. package/dist/providers/kimi.js.map +1 -0
  49. package/dist/providers/manager.d.ts +144 -0
  50. package/dist/providers/manager.d.ts.map +1 -0
  51. package/dist/providers/manager.js +279 -0
  52. package/dist/providers/manager.js.map +1 -0
  53. package/dist/providers/ollama.d.ts +83 -0
  54. package/dist/providers/ollama.d.ts.map +1 -0
  55. package/dist/providers/ollama.js +450 -0
  56. package/dist/providers/ollama.js.map +1 -0
  57. package/dist/providers/openai.d.ts +91 -0
  58. package/dist/providers/openai.d.ts.map +1 -0
  59. package/dist/providers/openai.js +445 -0
  60. package/dist/providers/openai.js.map +1 -0
  61. package/dist/providers/openrouter.d.ts +23 -0
  62. package/dist/providers/openrouter.d.ts.map +1 -0
  63. package/dist/providers/openrouter.js +31 -0
  64. package/dist/providers/openrouter.js.map +1 -0
  65. package/dist/providers/perplexity.d.ts +34 -0
  66. package/dist/providers/perplexity.d.ts.map +1 -0
  67. package/dist/providers/perplexity.js +58 -0
  68. package/dist/providers/perplexity.js.map +1 -0
  69. package/dist/providers/together.d.ts +23 -0
  70. package/dist/providers/together.d.ts.map +1 -0
  71. package/dist/providers/together.js +31 -0
  72. package/dist/providers/together.js.map +1 -0
  73. package/dist/providers/types.d.ts +229 -0
  74. package/dist/providers/types.d.ts.map +1 -0
  75. package/dist/providers/types.js +73 -0
  76. package/dist/providers/types.js.map +1 -0
  77. package/dist/providers/zai.d.ts +19 -0
  78. package/dist/providers/zai.d.ts.map +1 -0
  79. package/dist/providers/zai.js +27 -0
  80. package/dist/providers/zai.js.map +1 -0
  81. package/dist/services/provider.service.d.ts +28 -0
  82. package/dist/services/provider.service.d.ts.map +1 -1
  83. package/dist/services/provider.service.js +137 -13
  84. package/dist/services/provider.service.js.map +1 -1
  85. package/dist/tools/demerzel/engine.d.ts +67 -0
  86. package/dist/tools/demerzel/engine.d.ts.map +1 -0
  87. package/dist/tools/demerzel/engine.js +401 -0
  88. package/dist/tools/demerzel/engine.js.map +1 -0
  89. package/dist/tools/demerzel/enhanced-snapshot.d.ts +67 -0
  90. package/dist/tools/demerzel/enhanced-snapshot.d.ts.map +1 -0
  91. package/dist/tools/demerzel/enhanced-snapshot.js +481 -0
  92. package/dist/tools/demerzel/enhanced-snapshot.js.map +1 -0
  93. package/dist/tools/demerzel/index.d.ts +11 -0
  94. package/dist/tools/demerzel/index.d.ts.map +1 -1
  95. package/dist/tools/demerzel/index.js +656 -85
  96. package/dist/tools/demerzel/index.js.map +1 -1
  97. package/dist/tools/demerzel/prompts.d.ts +26 -0
  98. package/dist/tools/demerzel/prompts.d.ts.map +1 -0
  99. package/dist/tools/demerzel/prompts.js +181 -0
  100. package/dist/tools/demerzel/prompts.js.map +1 -0
  101. package/dist/tools/demerzel/semantic-search.d.ts +54 -0
  102. package/dist/tools/demerzel/semantic-search.d.ts.map +1 -0
  103. package/dist/tools/demerzel/semantic-search.js +205 -0
  104. package/dist/tools/demerzel/semantic-search.js.map +1 -0
  105. package/dist/tools/demerzel/snapshot.d.ts +30 -0
  106. package/dist/tools/demerzel/snapshot.d.ts.map +1 -0
  107. package/dist/tools/demerzel/snapshot.js +169 -0
  108. package/dist/tools/demerzel/snapshot.js.map +1 -0
  109. 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