winter-super-cli 2026.6.19 → 2026.6.21
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 +144 -641
- package/package.json +1 -1
- package/src/ai/provider-adapters.js +317 -0
- package/src/ai/providers.js +104 -122
- package/src/cli/repl.js +3484 -3484
- package/src/cli/tool-call-adapter.js +24 -2
- package/src/tools/executor.js +37 -36
package/src/ai/providers.js
CHANGED
|
@@ -11,6 +11,7 @@ import SuccessCriteria from './prompts/success-criteria.js';
|
|
|
11
11
|
import { ReasoningConfig, REASONING_LEVELS, complexityToReasoningLevel } from './reasoning.js';
|
|
12
12
|
import { buildResourceContext, getRelevantDesignGuide } from '../context/resource-loader.js';
|
|
13
13
|
import { classifyModelTier } from './model-capabilities.js';
|
|
14
|
+
import { buildProviderRequest, normalizeProviderResponse, normalizeProviderStreamChunk, getProviderPreset } from './provider-adapters.js';
|
|
14
15
|
|
|
15
16
|
const RESERVED_CONFIG_SECTIONS = new Set([
|
|
16
17
|
'analytics',
|
|
@@ -117,30 +118,50 @@ export class AIProviderManager {
|
|
|
117
118
|
// Load auth token from Claude Code's auth.json if available
|
|
118
119
|
this.authToken = await this.loadAuthToken();
|
|
119
120
|
|
|
120
|
-
if (claudeConfig?.baseURL || this.authToken) {
|
|
121
|
+
if (claudeConfig?.baseURL || claudeConfig?.apiKey || this.authToken) {
|
|
122
|
+
const claudeIsNativeAnthropic = Boolean(claudeConfig?.apiFormat === 'anthropic' || claudeConfig?.native === true);
|
|
123
|
+
const anthropicPreset = getProviderPreset('anthropic');
|
|
121
124
|
this.providers.claude = {
|
|
122
|
-
name: 'Claude-compatible API',
|
|
123
|
-
|
|
125
|
+
name: claudeIsNativeAnthropic ? 'Anthropic API' : 'Claude-compatible API',
|
|
126
|
+
providerName: claudeIsNativeAnthropic ? 'anthropic' : 'claude',
|
|
127
|
+
apiFormat: claudeConfig?.apiFormat || (claudeIsNativeAnthropic ? 'anthropic' : 'openai'),
|
|
128
|
+
baseURL: claudeConfig?.baseURL || (claudeIsNativeAnthropic ? anthropicPreset.baseURL : 'http://localhost:4000/v1'),
|
|
124
129
|
authToken: this.authToken,
|
|
125
130
|
apiKey: claudeConfig?.apiKey,
|
|
126
|
-
model: claudeConfig?.model || 'nvidia/moonshotai/kimi-k2.6',
|
|
131
|
+
model: claudeConfig?.model || (claudeIsNativeAnthropic ? anthropicPreset.model : 'nvidia/moonshotai/kimi-k2.6'),
|
|
127
132
|
ready: !!this.authToken || !!claudeConfig?.apiKey || claudeConfig?.apiKey === 'not-required',
|
|
128
133
|
};
|
|
129
134
|
}
|
|
130
135
|
|
|
131
|
-
|
|
136
|
+
const customConfig = cfg.custom || null;
|
|
137
|
+
if (this.isProviderConfigSection('custom', customConfig)) {
|
|
138
|
+
const customApiFormat = customConfig.apiFormat || customConfig.format || 'openai';
|
|
139
|
+
const customPreset = getProviderPreset(customApiFormat) || null;
|
|
140
|
+
const customBaseURL = customConfig.baseURL
|
|
141
|
+
|| customPreset?.baseURL
|
|
142
|
+
|| (customApiFormat === 'anthropic'
|
|
143
|
+
? getProviderPreset('anthropic')?.baseURL
|
|
144
|
+
: customApiFormat === 'gemini'
|
|
145
|
+
? getProviderPreset('gemini')?.baseURL
|
|
146
|
+
: 'http://localhost:4000/v1');
|
|
147
|
+
|
|
132
148
|
this.providers.custom = {
|
|
133
|
-
name: 'Custom API',
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
149
|
+
name: customConfig.name || 'Custom API',
|
|
150
|
+
providerName: 'custom',
|
|
151
|
+
apiFormat: customApiFormat,
|
|
152
|
+
baseURL: customBaseURL,
|
|
153
|
+
authToken: customConfig.authToken,
|
|
154
|
+
apiKey: customConfig.apiKey || 'not-required',
|
|
155
|
+
model: customConfig.model || customPreset?.model || 'gpt-4-turbo',
|
|
156
|
+
ready: Boolean(customConfig.authToken || customConfig.apiKey || customConfig.baseURL || customPreset?.baseURL),
|
|
138
157
|
};
|
|
139
158
|
}
|
|
140
159
|
|
|
141
160
|
if (cfg.ollama?.baseURL) {
|
|
142
161
|
this.providers.ollama = {
|
|
143
162
|
name: 'Ollama Local',
|
|
163
|
+
providerName: 'ollama',
|
|
164
|
+
apiFormat: cfg.ollama.apiFormat || 'openai',
|
|
144
165
|
apiKey: cfg.ollama.apiKey || 'not-required',
|
|
145
166
|
baseURL: cfg.ollama.baseURL,
|
|
146
167
|
model: cfg.ollama.model || 'llama3',
|
|
@@ -151,6 +172,8 @@ export class AIProviderManager {
|
|
|
151
172
|
if (cfg.openai?.apiKey) {
|
|
152
173
|
this.providers.openai = {
|
|
153
174
|
name: 'OpenAI',
|
|
175
|
+
providerName: 'openai',
|
|
176
|
+
apiFormat: cfg.openai.apiFormat || 'openai',
|
|
154
177
|
baseURL: cfg.openai.baseURL || 'https://api.openai.com/v1',
|
|
155
178
|
apiKey: cfg.openai.apiKey,
|
|
156
179
|
model: cfg.openai.model || 'gpt-4-turbo',
|
|
@@ -161,6 +184,8 @@ export class AIProviderManager {
|
|
|
161
184
|
if (cfg.groq?.apiKey) {
|
|
162
185
|
this.providers.groq = {
|
|
163
186
|
name: 'Groq',
|
|
187
|
+
providerName: 'groq',
|
|
188
|
+
apiFormat: cfg.groq.apiFormat || 'openai',
|
|
164
189
|
baseURL: cfg.groq.baseURL || 'https://api.groq.com/openai/v1',
|
|
165
190
|
apiKey: cfg.groq.apiKey,
|
|
166
191
|
model: cfg.groq.model || 'llama-3.1-70b-versatile',
|
|
@@ -254,26 +279,41 @@ export class AIProviderManager {
|
|
|
254
279
|
if (RESERVED_CONFIG_SECTIONS.has(providerName)) return false;
|
|
255
280
|
if (providerName === 'anthropic') return false;
|
|
256
281
|
if (!section || typeof section !== 'object' || Array.isArray(section)) return false;
|
|
282
|
+
if (providerName === 'custom') {
|
|
283
|
+
return Boolean(
|
|
284
|
+
section.baseURL ||
|
|
285
|
+
section.apiKey ||
|
|
286
|
+
section.authToken ||
|
|
287
|
+
section.apiFormat ||
|
|
288
|
+
section.format
|
|
289
|
+
);
|
|
290
|
+
}
|
|
257
291
|
|
|
258
292
|
return Boolean(
|
|
259
293
|
section.baseURL ||
|
|
260
294
|
section.apiKey ||
|
|
261
|
-
section.authToken
|
|
295
|
+
section.authToken ||
|
|
296
|
+
getProviderPreset(providerName)
|
|
262
297
|
);
|
|
263
298
|
}
|
|
264
299
|
|
|
265
300
|
buildProviderFromConfig(providerName, section) {
|
|
301
|
+
const preset = getProviderPreset(providerName);
|
|
266
302
|
return {
|
|
267
|
-
name: this.getProviderDisplayName(providerName),
|
|
268
|
-
|
|
303
|
+
name: section.name || preset?.name || this.getProviderDisplayName(providerName),
|
|
304
|
+
providerName,
|
|
305
|
+
apiFormat: section.apiFormat || section.format || preset?.apiFormat || 'openai',
|
|
306
|
+
baseURL: section.baseURL || preset?.baseURL || this.getProviderDefaultBaseURL(providerName),
|
|
269
307
|
authToken: section.authToken,
|
|
270
308
|
apiKey: section.apiKey || 'not-required',
|
|
271
|
-
model: section.model || this.getProviderDefaultModel(providerName),
|
|
272
|
-
ready: Boolean(section.authToken || section.apiKey || section.baseURL),
|
|
309
|
+
model: section.model || preset?.model || this.getProviderDefaultModel(providerName),
|
|
310
|
+
ready: Boolean(section.authToken || section.apiKey || section.baseURL || preset?.baseURL),
|
|
273
311
|
};
|
|
274
312
|
}
|
|
275
313
|
|
|
276
314
|
getProviderDisplayName(providerName) {
|
|
315
|
+
const preset = getProviderPreset(providerName);
|
|
316
|
+
if (preset?.name) return preset.name;
|
|
277
317
|
const labels = {
|
|
278
318
|
anthropic: 'Claude-compatible API',
|
|
279
319
|
claude: 'Claude-compatible API',
|
|
@@ -286,6 +326,8 @@ export class AIProviderManager {
|
|
|
286
326
|
}
|
|
287
327
|
|
|
288
328
|
getProviderDefaultBaseURL(providerName) {
|
|
329
|
+
const preset = getProviderPreset(providerName);
|
|
330
|
+
if (preset?.baseURL) return preset.baseURL;
|
|
289
331
|
if (providerName === 'openai') return 'https://api.openai.com/v1';
|
|
290
332
|
if (providerName === 'groq') return 'https://api.groq.com/openai/v1';
|
|
291
333
|
if (providerName === 'ollama') return 'http://localhost:11434/v1';
|
|
@@ -293,6 +335,8 @@ export class AIProviderManager {
|
|
|
293
335
|
}
|
|
294
336
|
|
|
295
337
|
getProviderDefaultModel(providerName) {
|
|
338
|
+
const preset = getProviderPreset(providerName);
|
|
339
|
+
if (preset?.model) return preset.model;
|
|
296
340
|
if (providerName === 'groq') return 'llama-3.1-70b-versatile';
|
|
297
341
|
if (providerName === 'ollama') return 'llama3';
|
|
298
342
|
return 'gpt-4-turbo';
|
|
@@ -490,46 +534,25 @@ export class AIProviderManager {
|
|
|
490
534
|
throw new Error('No active provider is configured');
|
|
491
535
|
}
|
|
492
536
|
const timeoutMs = getRequestTimeoutMs(options);
|
|
493
|
-
|
|
494
|
-
const body = {
|
|
495
|
-
model: options.model || provider.model,
|
|
496
|
-
messages,
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
// Apply reasoning configuration
|
|
500
537
|
const reasoningParam = options.reasoning || this._getReasoningParam(options, provider);
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
const tools = this.normalizeToolDefinitionsForApi(this.tools);
|
|
512
|
-
if (tools.length > 0) body.tools = tools;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
const headers = {
|
|
516
|
-
'Content-Type': 'application/json',
|
|
517
|
-
'Accept': 'application/json',
|
|
518
|
-
};
|
|
519
|
-
|
|
520
|
-
if (provider.authToken) {
|
|
521
|
-
headers['Authorization'] = `Bearer ${provider.authToken}`;
|
|
522
|
-
} else if (provider.apiKey && provider.apiKey !== 'not-required') {
|
|
523
|
-
headers['Authorization'] = `Bearer ${provider.apiKey}`;
|
|
524
|
-
}
|
|
538
|
+
const tools = this.tools.length > 0 && options.enableTools && !options.toolPromptOnly
|
|
539
|
+
? this.normalizeToolDefinitionsForApi(this.tools)
|
|
540
|
+
: [];
|
|
541
|
+
const request = buildProviderRequest(provider, messages, {
|
|
542
|
+
...options,
|
|
543
|
+
stream: false,
|
|
544
|
+
model: options.model || provider.model,
|
|
545
|
+
reasoning: reasoningParam,
|
|
546
|
+
tools,
|
|
547
|
+
});
|
|
525
548
|
|
|
526
549
|
const timeout = createTimeoutSignal(timeoutMs, options.signal || options.abortSignal);
|
|
527
550
|
let response;
|
|
528
551
|
try {
|
|
529
|
-
response = await fetch(
|
|
552
|
+
response = await fetch(request.url, {
|
|
530
553
|
method: 'POST',
|
|
531
|
-
headers,
|
|
532
|
-
body: JSON.stringify(body),
|
|
554
|
+
headers: request.headers,
|
|
555
|
+
body: JSON.stringify(request.body),
|
|
533
556
|
signal: timeout.signal,
|
|
534
557
|
});
|
|
535
558
|
} catch (error) {
|
|
@@ -545,7 +568,7 @@ export class AIProviderManager {
|
|
|
545
568
|
throw requestError;
|
|
546
569
|
}
|
|
547
570
|
|
|
548
|
-
return await response.json();
|
|
571
|
+
return normalizeProviderResponse(provider, await response.json());
|
|
549
572
|
}
|
|
550
573
|
|
|
551
574
|
async *streamRequestToProvider(provider, messages, options = {}) {
|
|
@@ -553,51 +576,25 @@ export class AIProviderManager {
|
|
|
553
576
|
throw new Error('No active provider is configured');
|
|
554
577
|
}
|
|
555
578
|
const timeoutMs = getRequestTimeoutMs(options);
|
|
556
|
-
|
|
557
|
-
const body = {
|
|
558
|
-
model: options.model || provider.model,
|
|
559
|
-
messages,
|
|
560
|
-
stream: true,
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
if (options.includeUsage !== false) {
|
|
564
|
-
body.stream_options = { include_usage: true };
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// Apply reasoning configuration
|
|
568
579
|
const reasoningParam = options.reasoning || this._getReasoningParam(options, provider);
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
const tools = this.normalizeToolDefinitionsForApi(this.tools);
|
|
580
|
-
if (tools.length > 0) body.tools = tools;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
const headers = {
|
|
584
|
-
'Content-Type': 'application/json',
|
|
585
|
-
'Accept': 'text/event-stream',
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
if (provider.authToken) {
|
|
589
|
-
headers['Authorization'] = `Bearer ${provider.authToken}`;
|
|
590
|
-
} else if (provider.apiKey && provider.apiKey !== 'not-required') {
|
|
591
|
-
headers['Authorization'] = `Bearer ${provider.apiKey}`;
|
|
592
|
-
}
|
|
580
|
+
const tools = this.tools.length > 0 && options.enableTools && !options.toolPromptOnly
|
|
581
|
+
? this.normalizeToolDefinitionsForApi(this.tools)
|
|
582
|
+
: [];
|
|
583
|
+
const request = buildProviderRequest(provider, messages, {
|
|
584
|
+
...options,
|
|
585
|
+
stream: true,
|
|
586
|
+
model: options.model || provider.model,
|
|
587
|
+
reasoning: reasoningParam,
|
|
588
|
+
tools,
|
|
589
|
+
});
|
|
593
590
|
|
|
594
591
|
const timeout = createTimeoutSignal(timeoutMs, options.signal || options.abortSignal);
|
|
595
592
|
let response;
|
|
596
593
|
try {
|
|
597
|
-
response = await fetch(
|
|
594
|
+
response = await fetch(request.url, {
|
|
598
595
|
method: 'POST',
|
|
599
|
-
headers,
|
|
600
|
-
body: JSON.stringify(body),
|
|
596
|
+
headers: request.headers,
|
|
597
|
+
body: JSON.stringify(request.body),
|
|
601
598
|
signal: timeout.signal,
|
|
602
599
|
});
|
|
603
600
|
|
|
@@ -640,14 +637,10 @@ export class AIProviderManager {
|
|
|
640
637
|
continue;
|
|
641
638
|
}
|
|
642
639
|
|
|
643
|
-
const
|
|
644
|
-
|
|
640
|
+
const chunk = normalizeProviderStreamChunk(provider, data);
|
|
641
|
+
if (!chunk.content && !chunk.usage && !chunk.raw) continue;
|
|
645
642
|
yieldedAny = true;
|
|
646
|
-
yield
|
|
647
|
-
content,
|
|
648
|
-
usage: data.usage,
|
|
649
|
-
raw: data,
|
|
650
|
-
};
|
|
643
|
+
yield chunk;
|
|
651
644
|
}
|
|
652
645
|
}
|
|
653
646
|
|
|
@@ -657,13 +650,11 @@ export class AIProviderManager {
|
|
|
657
650
|
if (payload && payload !== '[DONE]') {
|
|
658
651
|
try {
|
|
659
652
|
const data = JSON.parse(payload);
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
raw: data,
|
|
666
|
-
};
|
|
653
|
+
const chunk = normalizeProviderStreamChunk(provider, data);
|
|
654
|
+
if (chunk.content || chunk.usage || chunk.raw) {
|
|
655
|
+
yieldedAny = true;
|
|
656
|
+
yield chunk;
|
|
657
|
+
}
|
|
667
658
|
} catch {}
|
|
668
659
|
}
|
|
669
660
|
}
|
|
@@ -736,27 +727,18 @@ export class AIProviderManager {
|
|
|
736
727
|
while (iterations < maxIterations) {
|
|
737
728
|
iterations++;
|
|
738
729
|
|
|
739
|
-
const
|
|
730
|
+
const tools = this.tools.length > 0 ? this.normalizeToolDefinitionsForApi(this.tools) : [];
|
|
731
|
+
const request = buildProviderRequest(provider, currentMessages, {
|
|
732
|
+
...options,
|
|
733
|
+
stream: false,
|
|
740
734
|
model: options.model || provider.model,
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
};
|
|
744
|
-
|
|
745
|
-
const headers = {
|
|
746
|
-
'Content-Type': 'application/json',
|
|
747
|
-
'Accept': 'application/json',
|
|
748
|
-
};
|
|
749
|
-
|
|
750
|
-
if (provider.authToken) {
|
|
751
|
-
headers['Authorization'] = `Bearer ${provider.authToken}`;
|
|
752
|
-
} else if (provider.apiKey && provider.apiKey !== 'not-required') {
|
|
753
|
-
headers['Authorization'] = `Bearer ${provider.apiKey}`;
|
|
754
|
-
}
|
|
735
|
+
tools,
|
|
736
|
+
});
|
|
755
737
|
|
|
756
|
-
const response = await fetch(
|
|
738
|
+
const response = await fetch(request.url, {
|
|
757
739
|
method: 'POST',
|
|
758
|
-
headers,
|
|
759
|
-
body: JSON.stringify(body),
|
|
740
|
+
headers: request.headers,
|
|
741
|
+
body: JSON.stringify(request.body),
|
|
760
742
|
});
|
|
761
743
|
|
|
762
744
|
if (!response.ok) {
|
|
@@ -764,7 +746,7 @@ export class AIProviderManager {
|
|
|
764
746
|
throw new Error(`${this.activeProvider} error (${response.status}): ${error}`);
|
|
765
747
|
}
|
|
766
748
|
|
|
767
|
-
const data = await response.json();
|
|
749
|
+
const data = normalizeProviderResponse(provider, await response.json());
|
|
768
750
|
const assistantMsg = data.choices?.[0]?.message;
|
|
769
751
|
|
|
770
752
|
// Check for tool calls
|