winter-super-cli 2026.6.14 → 2026.6.16
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/package.json +1 -1
- package/src/ai/providers.js +25 -1
- package/src/cli/repl.js +8 -3
package/package.json
CHANGED
package/src/ai/providers.js
CHANGED
|
@@ -614,11 +614,15 @@ export class AIProviderManager {
|
|
|
614
614
|
|
|
615
615
|
const decoder = new TextDecoder();
|
|
616
616
|
let buffer = '';
|
|
617
|
+
let rawText = '';
|
|
618
|
+
let yieldedAny = false;
|
|
617
619
|
|
|
618
620
|
for await (const chunk of response.body) {
|
|
619
621
|
if (timeout.resetTimer) timeout.resetTimer();
|
|
620
622
|
|
|
621
|
-
|
|
623
|
+
const decoded = decoder.decode(chunk, { stream: true });
|
|
624
|
+
rawText += decoded;
|
|
625
|
+
buffer += decoded;
|
|
622
626
|
const lines = buffer.split(/\r?\n/);
|
|
623
627
|
buffer = lines.pop() || '';
|
|
624
628
|
|
|
@@ -638,6 +642,7 @@ export class AIProviderManager {
|
|
|
638
642
|
|
|
639
643
|
const choice = data.choices?.[0] || {};
|
|
640
644
|
const content = choice.delta?.content ?? choice.message?.content ?? choice.text ?? '';
|
|
645
|
+
yieldedAny = true;
|
|
641
646
|
yield {
|
|
642
647
|
content,
|
|
643
648
|
usage: data.usage,
|
|
@@ -653,6 +658,7 @@ export class AIProviderManager {
|
|
|
653
658
|
try {
|
|
654
659
|
const data = JSON.parse(payload);
|
|
655
660
|
const choice = data.choices?.[0] || {};
|
|
661
|
+
yieldedAny = true;
|
|
656
662
|
yield {
|
|
657
663
|
content: choice.delta?.content ?? choice.message?.content ?? choice.text ?? '',
|
|
658
664
|
usage: data.usage,
|
|
@@ -661,6 +667,24 @@ export class AIProviderManager {
|
|
|
661
667
|
} catch {}
|
|
662
668
|
}
|
|
663
669
|
}
|
|
670
|
+
|
|
671
|
+
if (!yieldedAny) {
|
|
672
|
+
try {
|
|
673
|
+
const data = JSON.parse(rawText.trim());
|
|
674
|
+
const choice = data.choices?.[0] || {};
|
|
675
|
+
const content = choice.delta?.content ?? choice.message?.content ?? choice.text ?? '';
|
|
676
|
+
if (content || data.usage || choice.finish_reason) {
|
|
677
|
+
yield {
|
|
678
|
+
content,
|
|
679
|
+
usage: data.usage,
|
|
680
|
+
raw: data,
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
} catch {
|
|
684
|
+
// Some OpenAI-compatible servers return 200 with a non-SSE/non-JSON body
|
|
685
|
+
// when streaming is requested. Let the caller fall back to non-streaming.
|
|
686
|
+
}
|
|
687
|
+
}
|
|
664
688
|
} catch (error) {
|
|
665
689
|
throw normalizeFetchError(error, provider, timeoutMs, true, timeout.timedOut());
|
|
666
690
|
} finally {
|
package/src/cli/repl.js
CHANGED
|
@@ -662,8 +662,13 @@ export class WinterREPL {
|
|
|
662
662
|
const PASTE_DELAY = 80;
|
|
663
663
|
|
|
664
664
|
process.stdin.on('data', (chunk) => {
|
|
665
|
-
|
|
666
|
-
|
|
665
|
+
const text = chunk.toString('utf8');
|
|
666
|
+
const newlineCount = (text.match(/\r?\n/g) || []).length;
|
|
667
|
+
|
|
668
|
+
// A normal submitted line can arrive as one chunk like "hello\n".
|
|
669
|
+
// Treat only real multi-line chunks as paste, otherwise REPL input
|
|
670
|
+
// gets stuck in multiline mode and never reaches the AI.
|
|
671
|
+
if (newlineCount > 1) {
|
|
667
672
|
this._isPasteChunk = true;
|
|
668
673
|
if (this._pasteChunkTimer) clearTimeout(this._pasteChunkTimer);
|
|
669
674
|
this._pasteChunkTimer = setTimeout(() => {
|
|
@@ -676,7 +681,7 @@ export class WinterREPL {
|
|
|
676
681
|
this._pasteTimer = null;
|
|
677
682
|
if (this._pasteBuffer.length === 0) return;
|
|
678
683
|
|
|
679
|
-
const isSingleLineInput = this._pasteBuffer.length === 1 &&
|
|
684
|
+
const isSingleLineInput = this._pasteBuffer.length === 1 && this._multilineBuffer.length === 0;
|
|
680
685
|
const isJustEmptyEnter = this._pasteBuffer.length === 1 && this._pasteBuffer[0].trim() === '';
|
|
681
686
|
|
|
682
687
|
// Normal single-line submit
|