three-blocks-login 0.1.9 → 0.1.10

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/bin/login.js +114 -24
  2. package/package.json +1 -1
package/bin/login.js CHANGED
@@ -382,7 +382,7 @@ loadEnvFromDotfile( process.cwd() );
382
382
  const BROKER_URL =
383
383
  args.endpoint ||
384
384
  process.env.THREE_BLOCKS_BROKER_URL ||
385
- "https://www.threejs-blocks.com/api/npm/token"; // your Astro broker endpoint
385
+ "https://www.threejs-blocks.com/api/npm/token";
386
386
 
387
387
  const OAUTH_DEVICE_CODE_URL =
388
388
  process.env.THREE_BLOCKS_OAUTH_DEVICE_URL ||
@@ -429,6 +429,78 @@ const openBrowser = async ( url ) => {
429
429
 
430
430
  };
431
431
 
432
+ const createSpinner = ( { stream = process.stderr } = {} ) => {
433
+
434
+ const enabled = !! ( stream?.isTTY && ! QUIET && ! NON_INTERACTIVE );
435
+ const frames = [ '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' ];
436
+ let idx = 0;
437
+ let timer = null;
438
+ let text = '';
439
+
440
+ const clearLine = () => {
441
+
442
+ if ( ! enabled ) return;
443
+ try {
444
+
445
+ readline.clearLine( stream, 0 );
446
+ readline.cursorTo( stream, 0 );
447
+
448
+ } catch {
449
+
450
+ // best effort
451
+
452
+ }
453
+
454
+ };
455
+
456
+ const render = () => {
457
+
458
+ if ( ! enabled ) return;
459
+ const frame = frames[ idx ];
460
+ idx = ( idx + 1 ) % frames.length;
461
+ clearLine();
462
+ stream.write( `${cyan( frame )} ${text}` );
463
+
464
+ };
465
+
466
+ const stop = () => {
467
+
468
+ if ( timer ) clearInterval( timer );
469
+ timer = null;
470
+ clearLine();
471
+
472
+ };
473
+
474
+ const start = ( nextText ) => {
475
+
476
+ if ( ! enabled ) return;
477
+ text = String( nextText ?? '' );
478
+ idx = 0;
479
+ render();
480
+ timer = setInterval( render, 80 );
481
+
482
+ };
483
+
484
+ const succeed = ( msg ) => {
485
+
486
+ if ( ! enabled ) return;
487
+ stop();
488
+ stream.write( `${green( "✔" )} ${msg || text}\n` );
489
+
490
+ };
491
+
492
+ const fail = ( msg ) => {
493
+
494
+ if ( ! enabled ) return;
495
+ stop();
496
+ stream.write( `${red( "✖" )} ${msg || text}\n` );
497
+
498
+ };
499
+
500
+ return { enabled, start, stop, succeed, fail };
501
+
502
+ };
503
+
432
504
  const promptChoice = async ( label, choices ) => {
433
505
 
434
506
  return await new Promise( ( resolve ) => {
@@ -637,22 +709,33 @@ const pollForToken = async ( deviceCode, interval, expiresIn ) => {
637
709
  const browserLogin = async () => {
638
710
 
639
711
  log.info( bold( cyan( 'Three Blocks CLI' ) ) + ' ' + dim( `[mode: ${MODE}]` ) );
640
- log.info( dim( 'Opening browser to log in...' ) );
712
+ const openSpinner = createSpinner();
713
+ if ( openSpinner.enabled ) openSpinner.start( 'Opening browser to log in...' );
714
+ else log.info( dim( 'Opening browser to log in...' ) );
641
715
  console.log( '' );
642
716
 
643
717
  // Start device code flow
644
- const deviceData = await startDeviceCodeFlow();
645
- const { device_code, verification_uri, verification_uri_complete, expires_in, interval } = deviceData;
718
+ let deviceData;
719
+ let browserOpened = false;
720
+ try {
646
721
 
647
- // Try to open browser
648
- const browserOpened = await openBrowser( verification_uri_complete || verification_uri );
722
+ deviceData = await startDeviceCodeFlow();
723
+ const { verification_uri, verification_uri_complete } = deviceData;
724
+ // Try to open browser
725
+ browserOpened = await openBrowser( verification_uri_complete || verification_uri );
726
+ if ( openSpinner.enabled ) openSpinner.succeed( browserOpened ? 'Browser opened.' : 'Login URL ready.' );
649
727
 
650
- if ( ! browserOpened ) {
728
+ } catch ( error ) {
651
729
 
652
- log.warn( 'Could not open browser automatically.' );
730
+ if ( openSpinner.enabled ) openSpinner.fail( 'Failed to start browser login.' );
731
+ throw error;
653
732
 
654
733
  }
655
734
 
735
+ const { device_code, verification_uri, verification_uri_complete, expires_in, interval } = deviceData;
736
+
737
+ if ( ! browserOpened ) log.warn( 'Could not open browser automatically.' );
738
+
656
739
  console.log( '' );
657
740
  log.info( yellow( 'Browser didn\'t open? Use the URL below to sign in:' ) );
658
741
  console.log( '' );
@@ -660,29 +743,20 @@ const browserLogin = async () => {
660
743
  console.log( '' );
661
744
  log.info( dim( 'Waiting for authorization...' ) );
662
745
 
663
- // Show a simple spinner while waiting
664
- const spinnerFrames = [ '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' ];
665
- let spinnerIdx = 0;
666
- const spinnerInterval = setInterval( () => {
667
-
668
- process.stdout.write( `\r${cyan( spinnerFrames[ spinnerIdx ] )} Waiting for browser authorization...` );
669
- spinnerIdx = ( spinnerIdx + 1 ) % spinnerFrames.length;
670
-
671
- }, 100 );
746
+ const waitSpinner = createSpinner();
747
+ if ( waitSpinner.enabled ) waitSpinner.start( 'Waiting for browser authorization...' );
672
748
 
673
749
  try {
674
750
 
675
751
  const licenseKey = await pollForToken( device_code, interval || 2, expires_in || 900 );
676
- clearInterval( spinnerInterval );
677
- process.stdout.write( '\r' + ' '.repeat( 50 ) + '\r' ); // Clear spinner line
752
+ if ( waitSpinner.enabled ) waitSpinner.succeed( 'Authorization successful!' );
753
+ else log.ok( green( 'Authorization successful!' ) );
678
754
  console.log( '' );
679
- log.ok( green( 'Authorization successful!' ) );
680
755
  return licenseKey;
681
756
 
682
757
  } catch ( error ) {
683
758
 
684
- clearInterval( spinnerInterval );
685
- process.stdout.write( '\r' + ' '.repeat( 50 ) + '\r' );
759
+ if ( waitSpinner.enabled ) waitSpinner.fail( 'Authorization failed.' );
686
760
  throw error;
687
761
 
688
762
  }
@@ -876,7 +950,21 @@ const log = {
876
950
 
877
951
  }
878
952
 
879
- const tokenData = await fetchToken( BROKER_URL, LICENSE_CLEAN, CHANNEL );
953
+ const brokerSpinner = createSpinner();
954
+ if ( brokerSpinner.enabled ) brokerSpinner.start( 'Verifying secret key with broker...' );
955
+ let tokenData;
956
+ try {
957
+
958
+ tokenData = await fetchToken( BROKER_URL, LICENSE_CLEAN, CHANNEL );
959
+ if ( brokerSpinner.enabled ) brokerSpinner.succeed( 'Broker verified secret key.' );
960
+
961
+ } catch ( error ) {
962
+
963
+ if ( brokerSpinner.enabled ) brokerSpinner.fail( 'Broker verification failed.' );
964
+ throw error;
965
+
966
+ }
967
+
880
968
  const {
881
969
  registry,
882
970
  token,
@@ -1217,7 +1305,9 @@ async function fetchToken( endpoint, license, channel ) {
1217
1305
  let suggestion = "Request failed. Please retry.";
1218
1306
  if ( res.status === 401 || res.status === 403 ) {
1219
1307
 
1220
- suggestion = "Authentication failed. Verify your license or access rights.";
1308
+ suggestion = "Authentication failed. The broker did not accept your license key. "
1309
+ + "If THREE_BLOCKS_SECRET_KEY (or a local .env) is set, verify it contains your active tb_… key, "
1310
+ + "or unset it to be prompted again.";
1221
1311
 
1222
1312
  } else if ( res.status === 404 ) {
1223
1313
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-blocks-login",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Fetch a short-lived token from the three-blocks broker and configure npm for the current context.",
5
5
  "type": "module",
6
6
  "bin": {