blun-king-cli 5.2.4 → 5.3.0
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 +175 -52
- package/package.json +1 -1
package/blun-cli.js
CHANGED
|
@@ -534,7 +534,7 @@ async function handleCommand(input) {
|
|
|
534
534
|
break;
|
|
535
535
|
|
|
536
536
|
case "/login":
|
|
537
|
-
cmdLogin(args);
|
|
537
|
+
await cmdLogin(args);
|
|
538
538
|
break;
|
|
539
539
|
|
|
540
540
|
case "/logout":
|
|
@@ -2224,21 +2224,106 @@ function cmdModel(args) {
|
|
|
2224
2224
|
// ══════════════════════════════════════════════════
|
|
2225
2225
|
// ── 9. /login /logout ──
|
|
2226
2226
|
// ══════════════════════════════════════════════════
|
|
2227
|
-
function cmdLogin(args) {
|
|
2227
|
+
async function cmdLogin(args) {
|
|
2228
2228
|
if (args && args.startsWith("key ")) {
|
|
2229
2229
|
config.auth.api_key = args.slice(4).trim();
|
|
2230
2230
|
config.auth.type = "api_key";
|
|
2231
|
+
config.api.key = config.auth.api_key;
|
|
2231
2232
|
saveConfig(config);
|
|
2232
2233
|
printSuccess("Logged in with API key.");
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
// Device Flow (like Claude Code)
|
|
2238
|
+
console.log("");
|
|
2239
|
+
console.log(C.bold + C.brightWhite + " BLUN King Login" + C.reset);
|
|
2240
|
+
console.log(C.gray + " Connecting to " + config.api.base_url + "..." + C.reset);
|
|
2241
|
+
|
|
2242
|
+
try {
|
|
2243
|
+
// Step 1: Start device flow
|
|
2244
|
+
var deviceResp = await apiCall("POST", "/api/king/auth/device", {});
|
|
2245
|
+
if (deviceResp.status !== 200 || !deviceResp.data.user_code) {
|
|
2246
|
+
// Fallback: API key login
|
|
2247
|
+
printInfo("Device flow not available. Use API key:");
|
|
2248
|
+
printInfo(" /login key <your-api-key>");
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
var deviceCode = deviceResp.data.device_code;
|
|
2253
|
+
var userCode = deviceResp.data.user_code;
|
|
2254
|
+
var verifyUrl = deviceResp.data.verification_url;
|
|
2255
|
+
var pollInterval = (deviceResp.data.interval || 5) * 1000;
|
|
2256
|
+
var expiresIn = deviceResp.data.expires_in || 900;
|
|
2257
|
+
|
|
2237
2258
|
console.log("");
|
|
2238
|
-
console.log(C.bold + "
|
|
2239
|
-
console.log("
|
|
2240
|
-
console.log("
|
|
2259
|
+
console.log(C.yellow + C.bold + " Code: " + C.brightWhite + userCode + C.reset);
|
|
2260
|
+
console.log(C.gray + " Gib diesen Code im Browser ein:" + C.reset);
|
|
2261
|
+
console.log(C.brightCyan + " " + verifyUrl + C.reset);
|
|
2241
2262
|
console.log("");
|
|
2263
|
+
|
|
2264
|
+
// Try to open browser
|
|
2265
|
+
try {
|
|
2266
|
+
if (process.platform === "win32") {
|
|
2267
|
+
require("child_process").execSync('start "" "' + verifyUrl + '"', { stdio: "ignore" });
|
|
2268
|
+
} else if (process.platform === "darwin") {
|
|
2269
|
+
require("child_process").execSync('open "' + verifyUrl + '"', { stdio: "ignore" });
|
|
2270
|
+
} else {
|
|
2271
|
+
require("child_process").execSync('xdg-open "' + verifyUrl + '" 2>/dev/null || true', { stdio: "ignore" });
|
|
2272
|
+
}
|
|
2273
|
+
printInfo("Browser geöffnet.");
|
|
2274
|
+
} catch(e) {
|
|
2275
|
+
printInfo("Öffne den Link manuell im Browser.");
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
console.log(C.dim + " Warte auf Freigabe..." + C.reset);
|
|
2279
|
+
|
|
2280
|
+
// Step 2: Poll for authorization
|
|
2281
|
+
var maxPolls = Math.floor(expiresIn * 1000 / pollInterval);
|
|
2282
|
+
var authorized = false;
|
|
2283
|
+
for (var p = 0; p < maxPolls; p++) {
|
|
2284
|
+
await new Promise(function(r) { setTimeout(r, pollInterval); });
|
|
2285
|
+
try {
|
|
2286
|
+
var tokenResp = await apiCall("POST", "/api/king/auth/token", { device_code: deviceCode });
|
|
2287
|
+
if (tokenResp.status === 200 && tokenResp.data.access_token) {
|
|
2288
|
+
// Success!
|
|
2289
|
+
config.auth.type = "oauth";
|
|
2290
|
+
config.auth.oauth_token = tokenResp.data.access_token;
|
|
2291
|
+
config.auth.oauth_expires = new Date(Date.now() + (tokenResp.data.expires_in || 86400) * 1000).toISOString();
|
|
2292
|
+
config.auth.refresh_token = tokenResp.data.refresh_token || "";
|
|
2293
|
+
config.api.key = tokenResp.data.access_token;
|
|
2294
|
+
if (tokenResp.data.user) {
|
|
2295
|
+
config.auth.user = tokenResp.data.user;
|
|
2296
|
+
}
|
|
2297
|
+
saveConfig(config);
|
|
2298
|
+
console.log("");
|
|
2299
|
+
printSuccess("Eingeloggt!" + (tokenResp.data.user ? " (" + (tokenResp.data.user.email || tokenResp.data.user.name) + ")" : ""));
|
|
2300
|
+
if (tokenResp.data.user && tokenResp.data.user.plan) {
|
|
2301
|
+
printInfo("Plan: " + tokenResp.data.user.plan);
|
|
2302
|
+
}
|
|
2303
|
+
authorized = true;
|
|
2304
|
+
break;
|
|
2305
|
+
}
|
|
2306
|
+
// 428 = authorization_pending → keep polling
|
|
2307
|
+
if (tokenResp.status === 428) {
|
|
2308
|
+
process.stdout.write(".");
|
|
2309
|
+
continue;
|
|
2310
|
+
}
|
|
2311
|
+
// Other error
|
|
2312
|
+
if (tokenResp.data && tokenResp.data.error === "expired_token") {
|
|
2313
|
+
printError("Code abgelaufen. Starte /login erneut.");
|
|
2314
|
+
break;
|
|
2315
|
+
}
|
|
2316
|
+
} catch(pollErr) {
|
|
2317
|
+
// Network error, retry
|
|
2318
|
+
continue;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
if (!authorized) {
|
|
2322
|
+
printError("Login fehlgeschlagen oder abgelaufen.");
|
|
2323
|
+
}
|
|
2324
|
+
} catch(e) {
|
|
2325
|
+
printError("Login error: " + e.message);
|
|
2326
|
+
printInfo("Fallback: /login key <your-api-key>");
|
|
2242
2327
|
}
|
|
2243
2328
|
}
|
|
2244
2329
|
|
|
@@ -2558,50 +2643,86 @@ async function main() {
|
|
|
2558
2643
|
}
|
|
2559
2644
|
}
|
|
2560
2645
|
|
|
2561
|
-
//
|
|
2562
|
-
var existingKey = config.api.key || config.auth.api_key || "";
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
console.log(C.gray + "
|
|
2567
|
-
console.log(
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
var
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
process.stdin.pause();
|
|
2583
|
-
process.stdout.write("\n");
|
|
2584
|
-
resolve(buf.trim());
|
|
2585
|
-
return;
|
|
2646
|
+
// Auth check — if no valid key/token, prompt for login or API key
|
|
2647
|
+
var existingKey = config.api.key || config.auth.api_key || config.auth.oauth_token || "";
|
|
2648
|
+
if (existingKey.length < 3) {
|
|
2649
|
+
console.log("");
|
|
2650
|
+
console.log(C.yellow + C.bold + " Authentication required" + C.reset);
|
|
2651
|
+
console.log(C.gray + " Choose how to connect:" + C.reset);
|
|
2652
|
+
console.log("");
|
|
2653
|
+
|
|
2654
|
+
var authOptions = [
|
|
2655
|
+
{ label: "Login (opens browser)", action: "login" },
|
|
2656
|
+
{ label: "Enter API key", action: "key" }
|
|
2657
|
+
];
|
|
2658
|
+
var authSel = 0;
|
|
2659
|
+
var authChoice = await new Promise(function(resolve) {
|
|
2660
|
+
function render() {
|
|
2661
|
+
process.stdout.write("\x1b[2A\r");
|
|
2662
|
+
authOptions.forEach(function(opt, i) {
|
|
2663
|
+
var prefix = i === authSel ? "\x1b[32m\x1b[1m \u276F " : " ";
|
|
2664
|
+
var color = i === authSel ? "\x1b[97m\x1b[1m" : "\x1b[90m";
|
|
2665
|
+
process.stdout.write("\x1b[2K" + prefix + color + opt.label + "\x1b[0m\n");
|
|
2666
|
+
});
|
|
2586
2667
|
}
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2668
|
+
console.log(""); console.log("");
|
|
2669
|
+
render();
|
|
2670
|
+
process.stdin.setRawMode(true);
|
|
2671
|
+
process.stdin.resume();
|
|
2672
|
+
process.stdin.setEncoding("utf8");
|
|
2673
|
+
function onKey(key) {
|
|
2674
|
+
if (key === "\x1b[A") { authSel = Math.max(0, authSel - 1); render(); return; }
|
|
2675
|
+
if (key === "\x1b[B") { authSel = Math.min(1, authSel + 1); render(); return; }
|
|
2676
|
+
if (key === "\r" || key === "\n") {
|
|
2677
|
+
process.stdin.removeListener("data", onKey);
|
|
2678
|
+
process.stdin.setRawMode(false);
|
|
2679
|
+
process.stdin.pause();
|
|
2680
|
+
resolve(authOptions[authSel].action);
|
|
2681
|
+
return;
|
|
2682
|
+
}
|
|
2683
|
+
if (key === "\x03") { process.exit(0); }
|
|
2684
|
+
}
|
|
2685
|
+
process.stdin.on("data", onKey);
|
|
2686
|
+
});
|
|
2687
|
+
|
|
2688
|
+
if (authChoice === "login") {
|
|
2689
|
+
await cmdLogin("");
|
|
2690
|
+
} else {
|
|
2691
|
+
console.log("");
|
|
2692
|
+
process.stdout.write(C.brightBlue + " API Key: " + C.reset);
|
|
2693
|
+
var apiKeyAnswer = await new Promise(function(resolve) {
|
|
2694
|
+
var buf = "";
|
|
2695
|
+
process.stdin.setRawMode(true);
|
|
2696
|
+
process.stdin.resume();
|
|
2697
|
+
process.stdin.setEncoding("utf8");
|
|
2698
|
+
function onKeyApi(key) {
|
|
2699
|
+
if (key === "\x03") { process.exit(0); }
|
|
2700
|
+
if (key === "\r" || key === "\n") {
|
|
2701
|
+
process.stdin.removeListener("data", onKeyApi);
|
|
2702
|
+
process.stdin.setRawMode(false);
|
|
2703
|
+
process.stdin.pause();
|
|
2704
|
+
process.stdout.write("\n");
|
|
2705
|
+
resolve(buf.trim());
|
|
2706
|
+
return;
|
|
2707
|
+
}
|
|
2708
|
+
if (key === "\x7f" || key === "\b") {
|
|
2709
|
+
if (buf.length > 0) { buf = buf.slice(0, -1); process.stdout.write("\b \b"); }
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2712
|
+
buf += key;
|
|
2713
|
+
process.stdout.write("*");
|
|
2714
|
+
}
|
|
2715
|
+
process.stdin.on("data", onKeyApi);
|
|
2716
|
+
});
|
|
2717
|
+
if (apiKeyAnswer) {
|
|
2718
|
+
config.api.key = apiKeyAnswer;
|
|
2719
|
+
config.auth.api_key = apiKeyAnswer;
|
|
2720
|
+
saveConfig(config);
|
|
2721
|
+
printSuccess("API key saved.");
|
|
2722
|
+
} else {
|
|
2723
|
+
printError("No API key — some features won't work.");
|
|
2590
2724
|
}
|
|
2591
|
-
buf += key;
|
|
2592
|
-
process.stdout.write("*");
|
|
2593
2725
|
}
|
|
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.");
|
|
2605
2726
|
}
|
|
2606
2727
|
|
|
2607
2728
|
printHeader();
|
|
@@ -2812,8 +2933,10 @@ async function main() {
|
|
|
2812
2933
|
|
|
2813
2934
|
function detectIntent(text) {
|
|
2814
2935
|
var lower = text.toLowerCase();
|
|
2815
|
-
// Skip if text is long
|
|
2816
|
-
if (text.length >
|
|
2936
|
+
// Skip if text is long or looks like a chat message (not a command)
|
|
2937
|
+
if (text.length > 50) return null;
|
|
2938
|
+
// Skip if text has more than 4 words (likely a sentence, not a command)
|
|
2939
|
+
if (text.trim().split(/\s+/).length > 4) return null;
|
|
2817
2940
|
for (var i = 0; i < INTENTS.length; i++) {
|
|
2818
2941
|
var intent = INTENTS[i];
|
|
2819
2942
|
for (var j = 0; j < intent.patterns.length; j++) {
|