blun-king-cli 5.2.5 → 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.
Files changed (2) hide show
  1. package/blun-cli.js +171 -50
  2. 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
- } else if (args === "oauth") {
2234
- printInfo("OAuth login not yet implemented. Use API key for now:");
2235
- printInfo(" /login key <your-api-key>");
2236
- } else {
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 + " Login:" + C.reset);
2239
- console.log(" /login key <api-key> — Login with API key");
2240
- console.log(" /login oauth — Login with OAuth (coming soon)");
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
- // API KeyALWAYS prompt (like Claude Code login)
2562
- var existingKey = config.api.key || config.auth.api_key || "";
2563
- console.log("");
2564
- console.log(C.yellow + C.bold + " API Key" + C.reset);
2565
- if (existingKey.length > 3) {
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 {
2569
- console.log(C.gray + " Enter your BLUN King API key:" + C.reset);
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;
2646
+ // Auth checkif 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
- if (key === "\x7f" || key === "\b") {
2588
- if (buf.length > 0) { buf = buf.slice(0, -1); process.stdout.write("\b \b"); }
2589
- return;
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "5.2.5",
3
+ "version": "5.3.0",
4
4
  "description": "BLUN King CLI — Your local AI assistant powered by Gemma4",
5
5
  "type": "commonjs",
6
6
  "bin": {