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