blun-king-cli 5.0.0 → 5.0.2
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 +85 -4
- package/package.json +1 -1
package/blun-cli.js
CHANGED
|
@@ -12,7 +12,7 @@ const CONFIG_FILE = path.join(HOME, "config.json");
|
|
|
12
12
|
if (!fs.existsSync(HOME)) fs.mkdirSync(HOME, { recursive: true });
|
|
13
13
|
|
|
14
14
|
const DEFAULT_CONFIG = {
|
|
15
|
-
apiUrl: process.env.BLUN_API_URL || "http://
|
|
15
|
+
apiUrl: process.env.BLUN_API_URL || "http://176.9.158.30:3200",
|
|
16
16
|
token: process.env.BLUN_API_TOKEN || "",
|
|
17
17
|
streamLevel: "normal"
|
|
18
18
|
};
|
|
@@ -506,15 +506,96 @@ async function main() {
|
|
|
506
506
|
renderPrompt();
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
+
// ── Arrow-key menu helper ──
|
|
510
|
+
function arrowMenu(options, count) {
|
|
511
|
+
let sel = 0;
|
|
512
|
+
return new Promise((resolve) => {
|
|
513
|
+
function render() {
|
|
514
|
+
process.stdout.write(`\x1b[${count}A\r`);
|
|
515
|
+
options.forEach((opt, i) => {
|
|
516
|
+
const prefix = i === sel ? "\x1b[32m\x1b[1m \u276F " : " ";
|
|
517
|
+
const color = i === sel ? "\x1b[97m\x1b[1m" : "\x1b[90m";
|
|
518
|
+
process.stdout.write(`\x1b[2K${prefix}${color}${i + 1}. ${opt.label}\x1b[0m\n`);
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
for (let i = 0; i < count; i++) console.log("");
|
|
522
|
+
render();
|
|
523
|
+
process.stdin.setRawMode(true);
|
|
524
|
+
process.stdin.resume();
|
|
525
|
+
process.stdin.setEncoding("utf8");
|
|
526
|
+
function onKey(key) {
|
|
527
|
+
if (key === "\x1b[A") { sel = Math.max(0, sel - 1); render(); return; }
|
|
528
|
+
if (key === "\x1b[B") { sel = Math.min(options.length - 1, sel + 1); render(); return; }
|
|
529
|
+
for (let i = 0; i < options.length; i++) {
|
|
530
|
+
if (key === String(i + 1)) { process.stdin.removeListener("data", onKey); process.stdin.setRawMode(false); resolve(options[i]); return; }
|
|
531
|
+
}
|
|
532
|
+
if (key === "\r" || key === "\n") { process.stdin.removeListener("data", onKey); process.stdin.setRawMode(false); resolve(options[sel]); return; }
|
|
533
|
+
if (key === "\x03") { process.exit(0); }
|
|
534
|
+
}
|
|
535
|
+
process.stdin.on("data", onKey);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// ── Trust Prompt ──
|
|
540
|
+
const TRUST_FILE = path.join(HOME, "trusted.json");
|
|
541
|
+
let trustedDirs = [];
|
|
542
|
+
try { if (fs.existsSync(TRUST_FILE)) trustedDirs = JSON.parse(fs.readFileSync(TRUST_FILE, "utf8")); } catch {}
|
|
543
|
+
const isTrusted = trustedDirs.includes(workdir) || process.argv.includes("--trust");
|
|
544
|
+
|
|
545
|
+
if (!isTrusted && process.stdin.isTTY) {
|
|
546
|
+
console.log("");
|
|
547
|
+
console.log(` \x1b[33m\u{1F4C1} Working in: \x1b[97m${workdir}\x1b[0m`);
|
|
548
|
+
console.log(" \x1b[90mDo you trust this folder? BLUN King will read/write files here.\x1b[0m");
|
|
549
|
+
console.log("");
|
|
550
|
+
const trustChoice = await arrowMenu([
|
|
551
|
+
{ label: "Yes, trust this folder", action: "trust" },
|
|
552
|
+
{ label: "Always trust this folder", action: "always" },
|
|
553
|
+
{ label: "Choose another folder", action: "choose" }
|
|
554
|
+
], 3);
|
|
555
|
+
|
|
556
|
+
if (trustChoice.action === "always") {
|
|
557
|
+
trustedDirs.push(workdir);
|
|
558
|
+
fs.writeFileSync(TRUST_FILE, JSON.stringify(trustedDirs, null, 2));
|
|
559
|
+
console.log(" \x1b[32m\u2713 Folder trusted permanently.\x1b[0m");
|
|
560
|
+
} else if (trustChoice.action === "choose") {
|
|
561
|
+
if (process.platform === "win32") {
|
|
562
|
+
try {
|
|
563
|
+
const psCmd = 'powershell -NoProfile -Command "Add-Type -AssemblyName System.Windows.Forms; $f = New-Object System.Windows.Forms.FolderBrowserDialog; $f.Description = \'Choose BLUN King workspace\'; $f.ShowNewFolderButton = $true; if($f.ShowDialog() -eq \'OK\'){ Write-Output $f.SelectedPath } else { Write-Output \'CANCELLED\' }"';
|
|
564
|
+
const newDir = require("child_process").execSync(psCmd, { encoding: "utf8", timeout: 60000 }).trim();
|
|
565
|
+
if (newDir && newDir !== "CANCELLED" && fs.existsSync(newDir)) {
|
|
566
|
+
process.chdir(newDir);
|
|
567
|
+
console.log(` \x1b[32m\u2713 Workspace: ${newDir}\x1b[0m`);
|
|
568
|
+
}
|
|
569
|
+
} catch {}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
console.log("");
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// ── Mode Selection ──
|
|
576
|
+
if (!process.argv.includes("--agent") && !process.argv.includes("--chat") && process.stdin.isTTY) {
|
|
577
|
+
console.log(" \x1b[97m\x1b[1mHow do you want to work?\x1b[0m");
|
|
578
|
+
console.log("");
|
|
579
|
+
const modeChoice = await arrowMenu([
|
|
580
|
+
{ label: "Chat \u2014 talk back and forth", mode: "chat" },
|
|
581
|
+
{ label: "Agent \u2014 give a goal, I do the rest autonomously", mode: "agent" }
|
|
582
|
+
], 2);
|
|
583
|
+
mode = modeChoice.mode;
|
|
584
|
+
console.log(` \x1b[32m\u2713 Mode: ${mode === "agent" ? "Agent (autonomous)" : "Chat (interactive)"}\x1b[0m`);
|
|
585
|
+
console.log("");
|
|
586
|
+
}
|
|
587
|
+
if (process.argv.includes("--chat")) mode = "chat";
|
|
588
|
+
|
|
589
|
+
// ── Startup ──
|
|
509
590
|
try {
|
|
510
591
|
const health = await fetch(`${cfg.apiUrl}/health`).then((resp) => resp.json());
|
|
511
|
-
console.log(`Connected to BLUN King API (${health.model})`);
|
|
592
|
+
console.log(` Connected to BLUN King API (${health.model})`);
|
|
512
593
|
} catch (error) {
|
|
513
|
-
console.error(`API connection failed: ${error.message}`);
|
|
594
|
+
console.error(` API connection failed: ${error.message}`);
|
|
514
595
|
}
|
|
515
596
|
|
|
516
597
|
console.log("");
|
|
517
|
-
console.log(" BLUN KING CLI v5.0.
|
|
598
|
+
console.log(" BLUN KING CLI v5.0.1");
|
|
518
599
|
console.log(` API: ${cfg.apiUrl}`);
|
|
519
600
|
console.log(` Dir: ${workdir}`);
|
|
520
601
|
console.log(" Chat input stays available while jobs run.");
|