three-blocks-login 0.1.8 → 0.1.9
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/LICENSE +182 -0
- package/README.md +1 -1
- package/bin/login.js +349 -9
- package/package.json +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
Three.js Blocks Software License Agreement
|
|
2
|
+
|
|
3
|
+
Proprietary Software — All Rights Reserved
|
|
4
|
+
Copyright (c) 2025 three-blocks
|
|
5
|
+
|
|
6
|
+
================================================================================
|
|
7
|
+
|
|
8
|
+
LICENSE NOTICE
|
|
9
|
+
|
|
10
|
+
This software and all associated packages, including but not limited to:
|
|
11
|
+
- @three-blocks/core
|
|
12
|
+
- @three-blocks/starter
|
|
13
|
+
- create-three-blocks-starter
|
|
14
|
+
- All related tools, examples, and documentation code
|
|
15
|
+
|
|
16
|
+
are licensed exclusively to active, paid subscribers of the threejs-blocks
|
|
17
|
+
project. Without a valid and current subscription, you are NOT permitted to
|
|
18
|
+
use this software.
|
|
19
|
+
|
|
20
|
+
================================================================================
|
|
21
|
+
|
|
22
|
+
PERMITTED USAGE (Active Subscribers Only)
|
|
23
|
+
|
|
24
|
+
Subject to maintaining an active, paid subscription, you are granted a
|
|
25
|
+
non-exclusive, non-transferable license to:
|
|
26
|
+
|
|
27
|
+
✓ Use the software in personal and commercial projects via the public API
|
|
28
|
+
✓ Deploy applications incorporating the software to production environments
|
|
29
|
+
✓ Integrate the software into your applications as a functional component
|
|
30
|
+
✓ Access updates, bug fixes, and new features during active subscription
|
|
31
|
+
|
|
32
|
+
================================================================================
|
|
33
|
+
|
|
34
|
+
RESTRICTIONS
|
|
35
|
+
|
|
36
|
+
You may NOT:
|
|
37
|
+
|
|
38
|
+
✗ Reverse engineer, decompile, disassemble, or attempt to derive the source
|
|
39
|
+
code, underlying algorithms, or techniques employed in this software
|
|
40
|
+
✗ Study, analyze, inspect, or learn from the implementation details, code
|
|
41
|
+
structure, or internal workings of this software
|
|
42
|
+
✗ Modify, adapt, translate, or create derivative works
|
|
43
|
+
✗ Copy, share, distribute, publish, or resell any part of the software
|
|
44
|
+
✗ Sublicense or transfer rights to any third party
|
|
45
|
+
✗ Remove, alter, or obscure this license header or any copyright notices
|
|
46
|
+
✗ Use this software after your subscription has expired or been terminated
|
|
47
|
+
|
|
48
|
+
================================================================================
|
|
49
|
+
|
|
50
|
+
AI AND MACHINE LEARNING PROHIBITION
|
|
51
|
+
|
|
52
|
+
The use of this source code for artificial intelligence and machine learning
|
|
53
|
+
purposes is strictly prohibited. Specifically, you may NOT:
|
|
54
|
+
|
|
55
|
+
✗ Use this source code, in whole or in part, to train, fine-tune, or develop
|
|
56
|
+
any artificial intelligence models, machine learning systems, or large
|
|
57
|
+
language models (LLMs)
|
|
58
|
+
|
|
59
|
+
✗ Feed this code into AI-assisted development tools for the purpose of
|
|
60
|
+
generating derivative code or training said tools
|
|
61
|
+
|
|
62
|
+
✗ Use automated transformation tools or AI systems to create modified versions
|
|
63
|
+
or derivatives of this software
|
|
64
|
+
|
|
65
|
+
✗ Extract patterns, algorithms, or techniques from this code using automated
|
|
66
|
+
or AI-assisted analysis for incorporation into other software
|
|
67
|
+
|
|
68
|
+
✗ Use this code as training data for code completion, code generation, or
|
|
69
|
+
code suggestion tools
|
|
70
|
+
|
|
71
|
+
This prohibition extends to any form of automated learning, pattern extraction,
|
|
72
|
+
or knowledge transfer that involves processing this source code through machine
|
|
73
|
+
learning pipelines, regardless of whether the output is used commercially or
|
|
74
|
+
privately.
|
|
75
|
+
|
|
76
|
+
================================================================================
|
|
77
|
+
|
|
78
|
+
SUBSCRIPTION REQUIREMENT
|
|
79
|
+
|
|
80
|
+
This license is valid only while you maintain an active, paid subscription to
|
|
81
|
+
the threejs-blocks service. Upon expiration or termination of your subscription:
|
|
82
|
+
|
|
83
|
+
1. Your license to use this software immediately terminates
|
|
84
|
+
2. You must cease all use of the software
|
|
85
|
+
3. You must remove the software from all projects and systems
|
|
86
|
+
4. You must not deploy or distribute any applications containing this software
|
|
87
|
+
|
|
88
|
+
Failure to comply with these requirements constitutes copyright infringement
|
|
89
|
+
and breach of contract.
|
|
90
|
+
|
|
91
|
+
================================================================================
|
|
92
|
+
|
|
93
|
+
PACKAGE-SPECIFIC NOTES
|
|
94
|
+
|
|
95
|
+
@three-blocks/core:
|
|
96
|
+
- Core library with proprietary algorithms and implementations
|
|
97
|
+
- Protected by code obfuscation and this license
|
|
98
|
+
- UNLICENSED - Subscription required
|
|
99
|
+
|
|
100
|
+
@three-blocks/starter:
|
|
101
|
+
- Starter template for building Three.js applications
|
|
102
|
+
- Includes licensed code and dependencies on @three-blocks/core
|
|
103
|
+
- UNLICENSED - Subscription required
|
|
104
|
+
|
|
105
|
+
create-three-blocks-starter:
|
|
106
|
+
- Scaffolding tool for creating new projects
|
|
107
|
+
- Publicly available for convenience but proprietary
|
|
108
|
+
- UNLICENSED - Creates projects that require subscription
|
|
109
|
+
|
|
110
|
+
three-blocks-login:
|
|
111
|
+
- Authentication helper for accessing private packages
|
|
112
|
+
- MIT Licensed - May be used independently
|
|
113
|
+
|
|
114
|
+
================================================================================
|
|
115
|
+
|
|
116
|
+
ENFORCEMENT
|
|
117
|
+
|
|
118
|
+
Unauthorized use, reproduction, distribution, or violation of the AI/ML
|
|
119
|
+
prohibition is strictly prohibited and may result in:
|
|
120
|
+
|
|
121
|
+
• Immediate termination of your license and subscription
|
|
122
|
+
• Legal action for copyright infringement
|
|
123
|
+
• Claims for damages and recovery of costs
|
|
124
|
+
• Civil liability under applicable copyright law
|
|
125
|
+
• Criminal penalties under applicable law
|
|
126
|
+
|
|
127
|
+
We employ various technical and legal measures to detect unauthorized use,
|
|
128
|
+
including but not limited to code fingerprinting, usage analytics, and
|
|
129
|
+
regular compliance audits.
|
|
130
|
+
|
|
131
|
+
================================================================================
|
|
132
|
+
|
|
133
|
+
ACCEPTANCE OF TERMS
|
|
134
|
+
|
|
135
|
+
By using this software, you acknowledge that you have read, understood, and
|
|
136
|
+
agree to be bound by the complete terms of this license agreement.
|
|
137
|
+
|
|
138
|
+
If you do not agree to these terms, you are not authorized to use, access,
|
|
139
|
+
or download this software.
|
|
140
|
+
|
|
141
|
+
================================================================================
|
|
142
|
+
|
|
143
|
+
DISCLAIMER OF WARRANTY
|
|
144
|
+
|
|
145
|
+
THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
146
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
147
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
|
|
148
|
+
|
|
149
|
+
================================================================================
|
|
150
|
+
|
|
151
|
+
LIMITATION OF LIABILITY
|
|
152
|
+
|
|
153
|
+
IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR
|
|
154
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING
|
|
155
|
+
FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
156
|
+
IN THE SOFTWARE.
|
|
157
|
+
|
|
158
|
+
================================================================================
|
|
159
|
+
|
|
160
|
+
CONTACT AND COMPLIANCE
|
|
161
|
+
|
|
162
|
+
For licensing inquiries, subscription management, and compliance questions:
|
|
163
|
+
|
|
164
|
+
Web: https://threejs-blocks.com/license
|
|
165
|
+
Email: support@threejs-blocks.com
|
|
166
|
+
|
|
167
|
+
For license violations or to report unauthorized use:
|
|
168
|
+
Email: legal@threejs-blocks.com
|
|
169
|
+
|
|
170
|
+
================================================================================
|
|
171
|
+
|
|
172
|
+
GOVERNING LAW
|
|
173
|
+
|
|
174
|
+
This license shall be governed by and construed in accordance with the laws
|
|
175
|
+
of the jurisdiction in which three-blocks operates, without regard to its
|
|
176
|
+
conflict of law provisions.
|
|
177
|
+
|
|
178
|
+
================================================================================
|
|
179
|
+
|
|
180
|
+
Last Updated: January 2025
|
|
181
|
+
Version: 1.0.0
|
|
182
|
+
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Minimal, dependency-free CLI for authenticating to Three Blocks private npm regi
|
|
|
9
9
|
npx -y three-blocks-login@latest
|
|
10
10
|
|
|
11
11
|
# Using pnpm (recommended - no warnings)
|
|
12
|
-
|
|
12
|
+
npx -y three-blocks-login@latest@latest
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Authentication Modes
|
package/bin/login.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/*!
|
|
3
|
+
three-blocks-login
|
|
4
|
+
Copyright (c) 2025 three-blocks
|
|
5
|
+
MIT License - See https://threejs-blocks.com/license
|
|
6
|
+
*/
|
|
2
7
|
// Minimal, dependency-free CLI
|
|
3
8
|
// Node 18+ required (built-in fetch)
|
|
4
9
|
|
|
@@ -379,7 +384,311 @@ const BROKER_URL =
|
|
|
379
384
|
process.env.THREE_BLOCKS_BROKER_URL ||
|
|
380
385
|
"https://www.threejs-blocks.com/api/npm/token"; // your Astro broker endpoint
|
|
381
386
|
|
|
382
|
-
|
|
387
|
+
const OAUTH_DEVICE_CODE_URL =
|
|
388
|
+
process.env.THREE_BLOCKS_OAUTH_DEVICE_URL ||
|
|
389
|
+
"https://www.threejs-blocks.com/api/cli/device-code";
|
|
390
|
+
|
|
391
|
+
const OAUTH_TOKEN_URL =
|
|
392
|
+
process.env.THREE_BLOCKS_OAUTH_TOKEN_URL ||
|
|
393
|
+
"https://www.threejs-blocks.com/api/cli/token";
|
|
394
|
+
|
|
395
|
+
// Browser login method flag
|
|
396
|
+
const USE_BROWSER_LOGIN = args[ 'browser' ] || args.browser;
|
|
397
|
+
const USE_KEY_LOGIN = args[ 'key' ] || args.key;
|
|
398
|
+
|
|
399
|
+
const openBrowser = async ( url ) => {
|
|
400
|
+
|
|
401
|
+
const { exec } = await import( "node:child_process" );
|
|
402
|
+
const platform = process.platform;
|
|
403
|
+
let command;
|
|
404
|
+
|
|
405
|
+
if ( platform === 'darwin' ) {
|
|
406
|
+
|
|
407
|
+
command = `open "${url}"`;
|
|
408
|
+
|
|
409
|
+
} else if ( platform === 'win32' ) {
|
|
410
|
+
|
|
411
|
+
command = `start "" "${url}"`;
|
|
412
|
+
|
|
413
|
+
} else {
|
|
414
|
+
|
|
415
|
+
// Linux and others
|
|
416
|
+
command = `xdg-open "${url}" || sensible-browser "${url}" || x-www-browser "${url}"`;
|
|
417
|
+
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return new Promise( ( resolve ) => {
|
|
421
|
+
|
|
422
|
+
exec( command, ( error ) => {
|
|
423
|
+
|
|
424
|
+
resolve( ! error );
|
|
425
|
+
|
|
426
|
+
} );
|
|
427
|
+
|
|
428
|
+
} );
|
|
429
|
+
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const promptChoice = async ( label, choices ) => {
|
|
433
|
+
|
|
434
|
+
return await new Promise( ( resolve ) => {
|
|
435
|
+
|
|
436
|
+
const stdin = process.stdin;
|
|
437
|
+
const stdout = process.stdout;
|
|
438
|
+
let selected = 0;
|
|
439
|
+
const cleanup = () => {
|
|
440
|
+
|
|
441
|
+
stdin.removeListener( 'data', onData );
|
|
442
|
+
if ( stdin.isTTY ) stdin.setRawMode( false );
|
|
443
|
+
stdin.pause();
|
|
444
|
+
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
const render = () => {
|
|
448
|
+
|
|
449
|
+
// Clear previous lines and redraw
|
|
450
|
+
for ( let i = 0; i < choices.length; i ++ ) {
|
|
451
|
+
|
|
452
|
+
const prefix = i === selected ? cyan( '› ' ) : ' ';
|
|
453
|
+
const text = i === selected ? bold( choices[ i ] ) : dim( choices[ i ] );
|
|
454
|
+
stdout.write( `${prefix}${text}\n` );
|
|
455
|
+
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const clearLines = () => {
|
|
461
|
+
|
|
462
|
+
for ( let i = 0; i < choices.length; i ++ ) {
|
|
463
|
+
|
|
464
|
+
readline.moveCursor( stdout, 0, - 1 );
|
|
465
|
+
readline.clearLine( stdout, 0 );
|
|
466
|
+
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
const onData = ( chunk ) => {
|
|
472
|
+
|
|
473
|
+
const str = String( chunk );
|
|
474
|
+
for ( const ch of str ) {
|
|
475
|
+
|
|
476
|
+
if ( ch === '\u0003' ) {
|
|
477
|
+
|
|
478
|
+
cleanup();
|
|
479
|
+
process.exit( 1 );
|
|
480
|
+
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if ( ch === '\r' || ch === '\n' ) {
|
|
484
|
+
|
|
485
|
+
cleanup();
|
|
486
|
+
return resolve( selected );
|
|
487
|
+
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Arrow keys come as escape sequences
|
|
491
|
+
if ( ch === '\u001b' ) continue;
|
|
492
|
+
if ( ch === '[' ) continue;
|
|
493
|
+
if ( ch === 'A' || ch === 'k' ) {
|
|
494
|
+
|
|
495
|
+
// Up
|
|
496
|
+
clearLines();
|
|
497
|
+
selected = ( selected - 1 + choices.length ) % choices.length;
|
|
498
|
+
render();
|
|
499
|
+
|
|
500
|
+
} else if ( ch === 'B' || ch === 'j' ) {
|
|
501
|
+
|
|
502
|
+
// Down
|
|
503
|
+
clearLines();
|
|
504
|
+
selected = ( selected + 1 ) % choices.length;
|
|
505
|
+
render();
|
|
506
|
+
|
|
507
|
+
} else if ( ch === '\t' || ch === ' ' ) {
|
|
508
|
+
|
|
509
|
+
// Tab or Space to toggle
|
|
510
|
+
clearLines();
|
|
511
|
+
selected = ( selected + 1 ) % choices.length;
|
|
512
|
+
render();
|
|
513
|
+
|
|
514
|
+
} else if ( ch === '1' ) {
|
|
515
|
+
|
|
516
|
+
cleanup();
|
|
517
|
+
return resolve( 0 );
|
|
518
|
+
|
|
519
|
+
} else if ( ch === '2' ) {
|
|
520
|
+
|
|
521
|
+
cleanup();
|
|
522
|
+
return resolve( 1 );
|
|
523
|
+
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
stdout.write( `${label}\n` );
|
|
531
|
+
render();
|
|
532
|
+
stdin.setEncoding( 'utf8' );
|
|
533
|
+
if ( stdin.isTTY ) stdin.setRawMode( true );
|
|
534
|
+
stdin.resume();
|
|
535
|
+
stdin.on( 'data', onData );
|
|
536
|
+
|
|
537
|
+
} );
|
|
538
|
+
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const startDeviceCodeFlow = async () => {
|
|
542
|
+
|
|
543
|
+
logDebug( `POST ${OAUTH_DEVICE_CODE_URL}` );
|
|
544
|
+
const res = await fetch( OAUTH_DEVICE_CODE_URL, {
|
|
545
|
+
method: 'POST',
|
|
546
|
+
headers: {
|
|
547
|
+
'content-type': 'application/json',
|
|
548
|
+
'accept': 'application/json',
|
|
549
|
+
},
|
|
550
|
+
body: JSON.stringify( { client_id: 'three-blocks-cli' } ),
|
|
551
|
+
} );
|
|
552
|
+
|
|
553
|
+
if ( ! res.ok ) {
|
|
554
|
+
|
|
555
|
+
const text = await res.text().catch( () => '' );
|
|
556
|
+
throw new CliError(
|
|
557
|
+
`Failed to start browser login (HTTP ${res.status})`,
|
|
558
|
+
{ suggestion: 'Check your network connection and try again.', stdout: text }
|
|
559
|
+
);
|
|
560
|
+
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
return await res.json();
|
|
564
|
+
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
const pollForToken = async ( deviceCode, interval, expiresIn ) => {
|
|
568
|
+
|
|
569
|
+
const startTime = Date.now();
|
|
570
|
+
const expiresAt = startTime + ( expiresIn * 1000 );
|
|
571
|
+
let pollInterval = interval * 1000;
|
|
572
|
+
|
|
573
|
+
while ( Date.now() < expiresAt ) {
|
|
574
|
+
|
|
575
|
+
await new Promise( ( r ) => setTimeout( r, pollInterval ) );
|
|
576
|
+
|
|
577
|
+
logDebug( `Polling ${OAUTH_TOKEN_URL} for token...` );
|
|
578
|
+
const res = await fetch( OAUTH_TOKEN_URL, {
|
|
579
|
+
method: 'POST',
|
|
580
|
+
headers: {
|
|
581
|
+
'content-type': 'application/json',
|
|
582
|
+
'accept': 'application/json',
|
|
583
|
+
},
|
|
584
|
+
body: JSON.stringify( { device_code: deviceCode } ),
|
|
585
|
+
} );
|
|
586
|
+
|
|
587
|
+
const data = await res.json().catch( () => ( {} ) );
|
|
588
|
+
|
|
589
|
+
if ( res.ok && data.access_token ) {
|
|
590
|
+
|
|
591
|
+
return data.access_token;
|
|
592
|
+
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if ( data.error === 'authorization_pending' ) {
|
|
596
|
+
|
|
597
|
+
// User hasn't authorized yet, keep polling
|
|
598
|
+
continue;
|
|
599
|
+
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if ( data.error === 'slow_down' ) {
|
|
603
|
+
|
|
604
|
+
// Increase polling interval
|
|
605
|
+
pollInterval += 1000;
|
|
606
|
+
continue;
|
|
607
|
+
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if ( data.error === 'expired_token' ) {
|
|
611
|
+
|
|
612
|
+
throw new CliError(
|
|
613
|
+
'Browser login timed out. Please try again.',
|
|
614
|
+
{ suggestion: 'Run the login command again and complete authorization in your browser.' }
|
|
615
|
+
);
|
|
616
|
+
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if ( data.error ) {
|
|
620
|
+
|
|
621
|
+
throw new CliError(
|
|
622
|
+
`Browser login failed: ${data.error_description || data.error}`,
|
|
623
|
+
{ suggestion: 'Try again or use the secret key method instead.' }
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
throw new CliError(
|
|
631
|
+
'Browser login timed out.',
|
|
632
|
+
{ suggestion: 'Run the login command again and complete authorization within 15 minutes.' }
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
const browserLogin = async () => {
|
|
638
|
+
|
|
639
|
+
log.info( bold( cyan( 'Three Blocks CLI' ) ) + ' ' + dim( `[mode: ${MODE}]` ) );
|
|
640
|
+
log.info( dim( 'Opening browser to log in...' ) );
|
|
641
|
+
console.log( '' );
|
|
642
|
+
|
|
643
|
+
// Start device code flow
|
|
644
|
+
const deviceData = await startDeviceCodeFlow();
|
|
645
|
+
const { device_code, verification_uri, verification_uri_complete, expires_in, interval } = deviceData;
|
|
646
|
+
|
|
647
|
+
// Try to open browser
|
|
648
|
+
const browserOpened = await openBrowser( verification_uri_complete || verification_uri );
|
|
649
|
+
|
|
650
|
+
if ( ! browserOpened ) {
|
|
651
|
+
|
|
652
|
+
log.warn( 'Could not open browser automatically.' );
|
|
653
|
+
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
console.log( '' );
|
|
657
|
+
log.info( yellow( 'Browser didn\'t open? Use the URL below to sign in:' ) );
|
|
658
|
+
console.log( '' );
|
|
659
|
+
console.log( ` ${cyan( verification_uri_complete || verification_uri )}` );
|
|
660
|
+
console.log( '' );
|
|
661
|
+
log.info( dim( 'Waiting for authorization...' ) );
|
|
662
|
+
|
|
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 );
|
|
672
|
+
|
|
673
|
+
try {
|
|
674
|
+
|
|
675
|
+
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
|
|
678
|
+
console.log( '' );
|
|
679
|
+
log.ok( green( 'Authorization successful!' ) );
|
|
680
|
+
return licenseKey;
|
|
681
|
+
|
|
682
|
+
} catch ( error ) {
|
|
683
|
+
|
|
684
|
+
clearInterval( spinnerInterval );
|
|
685
|
+
process.stdout.write( '\r' + ' '.repeat( 50 ) + '\r' );
|
|
686
|
+
throw error;
|
|
687
|
+
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
};
|
|
691
|
+
|
|
383
692
|
const promptHidden = async ( label ) => {
|
|
384
693
|
|
|
385
694
|
return await new Promise( ( resolve ) => {
|
|
@@ -490,15 +799,46 @@ const log = {
|
|
|
490
799
|
|
|
491
800
|
console.log( '' );
|
|
492
801
|
log.info( bold( yellow( 'Three Blocks Login' ) ) + ' ' + dim( `[mode: ${MODE}]` ) );
|
|
493
|
-
log
|
|
494
|
-
log.info( dim( 'Tip: paste it here; input is hidden. Press Enter to submit.' ) );
|
|
495
|
-
LICENSE = await promptHidden( `${cyan( '›' )} License key ${dim( '(tb_…)' )}: ` );
|
|
496
|
-
if ( ! LICENSE ) {
|
|
802
|
+
console.log( '' );
|
|
497
803
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
804
|
+
// Determine login method
|
|
805
|
+
let useBrowserLogin = USE_BROWSER_LOGIN;
|
|
806
|
+
let useKeyLogin = USE_KEY_LOGIN;
|
|
807
|
+
|
|
808
|
+
// If neither flag is set and we're interactive, show menu
|
|
809
|
+
if ( ! useBrowserLogin && ! useKeyLogin ) {
|
|
810
|
+
|
|
811
|
+
log.info( dim( 'Select login method:' ) );
|
|
812
|
+
console.log( '' );
|
|
813
|
+
const choice = await promptChoice( '', [
|
|
814
|
+
'Log in with Three Blocks account (opens browser)',
|
|
815
|
+
'Paste Secret Key manually'
|
|
816
|
+
] );
|
|
817
|
+
console.log( '' );
|
|
818
|
+
useBrowserLogin = choice === 0;
|
|
819
|
+
useKeyLogin = choice === 1;
|
|
820
|
+
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
if ( useBrowserLogin ) {
|
|
824
|
+
|
|
825
|
+
// Browser-based OAuth login
|
|
826
|
+
LICENSE = await browserLogin();
|
|
827
|
+
|
|
828
|
+
} else {
|
|
829
|
+
|
|
830
|
+
// Manual secret key entry
|
|
831
|
+
log.info( dim( 'Enter your license key to retrieve a scoped npm token.' ) );
|
|
832
|
+
log.info( dim( 'Tip: paste it here; input is hidden. Press Enter to submit.' ) );
|
|
833
|
+
LICENSE = await promptHidden( `${cyan( '›' )} License key ${dim( '(tb_…)' )}: ` );
|
|
834
|
+
if ( ! LICENSE ) {
|
|
835
|
+
|
|
836
|
+
fail(
|
|
837
|
+
"License key is required to continue.",
|
|
838
|
+
{ suggestion: "Paste your tb_… key or rerun with --license." }
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
}
|
|
502
842
|
|
|
503
843
|
}
|
|
504
844
|
|