blun-king-cli 5.2.1 → 5.2.3
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/blun-cli.js +45 -139
- package/package.json +1 -1
package/blun-cli.js
CHANGED
|
@@ -630,105 +630,12 @@ async function sendChat(message) {
|
|
|
630
630
|
dots++;
|
|
631
631
|
}, 300);
|
|
632
632
|
|
|
633
|
-
// Try streaming first, fall back to non-streaming
|
|
634
|
-
var useStreaming = true;
|
|
635
633
|
var fullAnswer = "";
|
|
636
634
|
var receivedFiles = [];
|
|
637
635
|
var meta = "";
|
|
638
|
-
var headerPrinted = false;
|
|
639
636
|
|
|
640
637
|
try {
|
|
641
|
-
//
|
|
642
|
-
var contextMsg = message;
|
|
643
|
-
if (lastCreatedFiles.length > 0) {
|
|
644
|
-
contextMsg = message + "\n\n[Kontext: Zuletzt erstellte Dateien im Ordner " + config.workdir + ": " +
|
|
645
|
-
lastCreatedFiles.map(function(f) { return f.name; }).join(", ") + "]";
|
|
646
|
-
}
|
|
647
|
-
var stream = await apiStreamCall("/chat/stream", {
|
|
648
|
-
message: contextMsg,
|
|
649
|
-
workdir: config.workdir,
|
|
650
|
-
history: chatHistory.slice(-10)
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
var buffer = "";
|
|
654
|
-
var streamMeta = {};
|
|
655
|
-
var headerShown = false;
|
|
656
|
-
|
|
657
|
-
await new Promise(function(resolve, reject) {
|
|
658
|
-
stream.on("data", function(chunk) {
|
|
659
|
-
buffer += chunk.toString();
|
|
660
|
-
var lines = buffer.split("\n");
|
|
661
|
-
buffer = lines.pop() || "";
|
|
662
|
-
|
|
663
|
-
for (var i = 0; i < lines.length; i++) {
|
|
664
|
-
var line = lines[i].trim();
|
|
665
|
-
if (line.startsWith("event: ")) {
|
|
666
|
-
var eventType = line.slice(7);
|
|
667
|
-
i++;
|
|
668
|
-
if (i < lines.length && lines[i].trim().startsWith("data: ")) {
|
|
669
|
-
var dataStr = lines[i].trim().slice(6);
|
|
670
|
-
try {
|
|
671
|
-
var eventData = JSON.parse(dataStr);
|
|
672
|
-
|
|
673
|
-
if (eventType === "meta") {
|
|
674
|
-
streamMeta = eventData;
|
|
675
|
-
// Stop thinking, show header
|
|
676
|
-
clearInterval(thinkTimer);
|
|
677
|
-
process.stdout.write("\r\x1b[2K");
|
|
678
|
-
console.log("");
|
|
679
|
-
console.log(C.green + C.bold + " " + BOX.bot + " BLUN King" + C.reset +
|
|
680
|
-
C.gray + " " + BOX.dot + " " + (eventData.task_type || "") + "/" + (eventData.role || "") + C.reset);
|
|
681
|
-
console.log(C.green + " " + BOX.h.repeat(40) + C.reset);
|
|
682
|
-
process.stdout.write(" ");
|
|
683
|
-
headerShown = true;
|
|
684
|
-
}
|
|
685
|
-
else if (eventType === "token") {
|
|
686
|
-
fullAnswer += eventData.text;
|
|
687
|
-
// Live output — write token directly
|
|
688
|
-
process.stdout.write(eventData.text);
|
|
689
|
-
}
|
|
690
|
-
else if (eventType === "retry") {
|
|
691
|
-
fullAnswer = "";
|
|
692
|
-
process.stdout.write("\n" + C.yellow + " \u21BB retry..." + C.reset + "\n ");
|
|
693
|
-
}
|
|
694
|
-
else if (eventType === "tool") {
|
|
695
|
-
process.stdout.write("\n" + C.cyan + " \u2699 " + eventData.name + C.reset);
|
|
696
|
-
if (eventData.args && eventData.args.name) process.stdout.write(C.dim + " " + eventData.args.name + C.reset);
|
|
697
|
-
process.stdout.write("\n ");
|
|
698
|
-
}
|
|
699
|
-
else if (eventType === "files") {
|
|
700
|
-
receivedFiles = eventData.files || [];
|
|
701
|
-
}
|
|
702
|
-
else if (eventType === "done") {
|
|
703
|
-
meta = (streamMeta.task_type || "") + "/" + (streamMeta.role || "") + " " + BOX.dot +
|
|
704
|
-
" score: " + ((eventData.quality || {}).score || "?") +
|
|
705
|
-
(eventData.status ? " " + BOX.dot + " " + eventData.status : "");
|
|
706
|
-
}
|
|
707
|
-
else if (eventType === "error") {
|
|
708
|
-
process.stdout.write("\n" + C.red + " ERROR: " + eventData.error + C.reset + "\n");
|
|
709
|
-
}
|
|
710
|
-
} catch(e) {}
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
stream.on("end", function() { resolve(); });
|
|
717
|
-
stream.on("error", function(e) { reject(e); });
|
|
718
|
-
});
|
|
719
|
-
|
|
720
|
-
// Finish streaming output
|
|
721
|
-
if (!headerShown) {
|
|
722
|
-
clearInterval(thinkTimer);
|
|
723
|
-
process.stdout.write("\r\x1b[2K");
|
|
724
|
-
}
|
|
725
|
-
console.log("\n");
|
|
726
|
-
if (meta) console.log(C.dim + " " + meta + C.reset);
|
|
727
|
-
console.log("");
|
|
728
|
-
|
|
729
|
-
} catch(streamErr) {
|
|
730
|
-
// Fallback to non-streaming
|
|
731
|
-
useStreaming = false;
|
|
638
|
+
// Non-streaming — reliable
|
|
732
639
|
clearInterval(thinkTimer);
|
|
733
640
|
process.stdout.write("\r\x1b[2K");
|
|
734
641
|
|
|
@@ -751,6 +658,11 @@ async function sendChat(message) {
|
|
|
751
658
|
(resp.data.status ? " " + BOX.dot + " " + resp.data.status : "");
|
|
752
659
|
|
|
753
660
|
await streamAnswer(fullAnswer, meta);
|
|
661
|
+
} catch(reqErr) {
|
|
662
|
+
clearInterval(thinkTimer);
|
|
663
|
+
process.stdout.write("\r\x1b[2K");
|
|
664
|
+
printError("Connection failed: " + reqErr.message);
|
|
665
|
+
return;
|
|
754
666
|
}
|
|
755
667
|
|
|
756
668
|
chatHistory.push({ role: "user", content: message });
|
|
@@ -2646,56 +2558,50 @@ async function main() {
|
|
|
2646
2558
|
}
|
|
2647
2559
|
}
|
|
2648
2560
|
|
|
2649
|
-
// API Key
|
|
2650
|
-
// Test if key actually works by hitting /health
|
|
2651
|
-
var hasKey = false;
|
|
2561
|
+
// API Key — ALWAYS prompt (like Claude Code login)
|
|
2652
2562
|
var existingKey = config.api.key || config.auth.api_key || "";
|
|
2563
|
+
console.log("");
|
|
2564
|
+
console.log(C.yellow + C.bold + " API Key" + C.reset);
|
|
2653
2565
|
if (existingKey.length > 3) {
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
} catch(e) { hasKey = false; }
|
|
2658
|
-
}
|
|
2659
|
-
if (!hasKey) {
|
|
2660
|
-
console.log("");
|
|
2661
|
-
console.log(C.yellow + C.bold + " API Key required" + C.reset);
|
|
2662
|
-
if (existingKey.length > 3) {
|
|
2663
|
-
console.log(C.red + " Saved key didn't work — please re-enter." + C.reset);
|
|
2664
|
-
}
|
|
2566
|
+
console.log(C.gray + " Current: " + C.green + existingKey.slice(0, 8) + "..." + C.reset);
|
|
2567
|
+
console.log(C.gray + " Press Enter to keep, or type a new key:" + C.reset);
|
|
2568
|
+
} else {
|
|
2665
2569
|
console.log(C.gray + " Enter your BLUN King API key:" + C.reset);
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
if (key === "\x7f" || key === "\b") {
|
|
2683
|
-
if (buf.length > 0) { buf = buf.slice(0, -1); process.stdout.write("\b \b"); }
|
|
2684
|
-
return;
|
|
2685
|
-
}
|
|
2686
|
-
buf += key;
|
|
2687
|
-
process.stdout.write("*");
|
|
2570
|
+
}
|
|
2571
|
+
process.stdout.write(C.brightBlue + " API Key: " + C.reset);
|
|
2572
|
+
var apiKeyAnswer = await new Promise(function(resolve) {
|
|
2573
|
+
var buf = "";
|
|
2574
|
+
process.stdin.setRawMode(true);
|
|
2575
|
+
process.stdin.resume();
|
|
2576
|
+
process.stdin.setEncoding("utf8");
|
|
2577
|
+
function onKeyApi(key) {
|
|
2578
|
+
if (key === "\x03") { process.exit(0); }
|
|
2579
|
+
if (key === "\r" || key === "\n") {
|
|
2580
|
+
process.stdin.removeListener("data", onKeyApi);
|
|
2581
|
+
process.stdin.setRawMode(false);
|
|
2582
|
+
process.stdin.pause();
|
|
2583
|
+
process.stdout.write("\n");
|
|
2584
|
+
resolve(buf.trim());
|
|
2585
|
+
return;
|
|
2688
2586
|
}
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
printSuccess("API key saved.");
|
|
2696
|
-
} else {
|
|
2697
|
-
printError("No API key — some features won't work.");
|
|
2587
|
+
if (key === "\x7f" || key === "\b") {
|
|
2588
|
+
if (buf.length > 0) { buf = buf.slice(0, -1); process.stdout.write("\b \b"); }
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2591
|
+
buf += key;
|
|
2592
|
+
process.stdout.write("*");
|
|
2698
2593
|
}
|
|
2594
|
+
process.stdin.on("data", onKeyApi);
|
|
2595
|
+
});
|
|
2596
|
+
if (apiKeyAnswer) {
|
|
2597
|
+
config.api.key = apiKeyAnswer;
|
|
2598
|
+
config.auth.api_key = apiKeyAnswer;
|
|
2599
|
+
saveConfig(config);
|
|
2600
|
+
printSuccess("API key saved.");
|
|
2601
|
+
} else if (existingKey.length > 3) {
|
|
2602
|
+
printSuccess("Using saved key.");
|
|
2603
|
+
} else {
|
|
2604
|
+
printError("No API key — some features won't work.");
|
|
2699
2605
|
}
|
|
2700
2606
|
|
|
2701
2607
|
printHeader();
|