blok0 0.1.2 → 0.1.4

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/dist/ui.js ADDED
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.log = exports.ProgressBar = exports.Spinner = exports.ciMode = exports.noEmoji = exports.noAnimation = exports.isTTY = exports.colors = exports.EMOJIS = void 0;
7
+ exports.setUIFlags = setUIFlags;
8
+ exports.withSpinner = withSpinner;
9
+ exports.showSection = showSection;
10
+ exports.showNextSteps = showNextSteps;
11
+ const ora_1 = __importDefault(require("ora"));
12
+ const chalk_1 = __importDefault(require("chalk"));
13
+ const cli_progress_1 = require("cli-progress");
14
+ // Emoji constants for consistent usage
15
+ exports.EMOJIS = {
16
+ SUCCESS: '✅',
17
+ ERROR: '❌',
18
+ WARNING: '⚠️',
19
+ INFO: 'ℹ️',
20
+ LOCK: '🔐',
21
+ PACKAGE: '📦',
22
+ FOLDER: '📁',
23
+ GEAR: '🔧',
24
+ SEARCH: '🔍',
25
+ ROCKET: '🚀',
26
+ DOWNLOAD: '📥',
27
+ PARTY: '🎉',
28
+ WRENCH: '🔧',
29
+ CHECK: '✅',
30
+ CROSS: '❌',
31
+ ARROW: '→',
32
+ };
33
+ // Color utilities
34
+ exports.colors = {
35
+ success: chalk_1.default.green,
36
+ error: chalk_1.default.red,
37
+ warning: chalk_1.default.yellow,
38
+ info: chalk_1.default.blue,
39
+ accent: chalk_1.default.cyan,
40
+ muted: chalk_1.default.gray,
41
+ };
42
+ // Check if we're in a TTY environment
43
+ exports.isTTY = process.stdout.isTTY;
44
+ // Global flags for disabling features
45
+ exports.noAnimation = false;
46
+ exports.noEmoji = false;
47
+ exports.ciMode = false;
48
+ // Set global flags
49
+ function setUIFlags(flags) {
50
+ exports.noAnimation = flags.noAnimation || exports.ciMode;
51
+ exports.noEmoji = flags.noEmoji || exports.ciMode;
52
+ exports.ciMode = flags.ci || false;
53
+ }
54
+ // Apply emoji settings to text
55
+ function applyEmoji(text, emoji) {
56
+ if (exports.noEmoji || !emoji)
57
+ return text;
58
+ return `${emoji} ${text}`;
59
+ }
60
+ // Spinner wrapper with TTY detection
61
+ class Spinner {
62
+ text;
63
+ emoji;
64
+ spinner = null;
65
+ startTime = 0;
66
+ constructor(text, emoji) {
67
+ this.text = text;
68
+ this.emoji = emoji;
69
+ }
70
+ start() {
71
+ if (!exports.isTTY || exports.noAnimation) {
72
+ console.log(applyEmoji(this.text, this.emoji));
73
+ this.startTime = Date.now();
74
+ return this;
75
+ }
76
+ this.spinner = (0, ora_1.default)({
77
+ text: applyEmoji(this.text, this.emoji),
78
+ spinner: 'dots',
79
+ }).start();
80
+ this.startTime = Date.now();
81
+ return this;
82
+ }
83
+ update(text, emoji) {
84
+ if (!this.spinner) {
85
+ if (exports.isTTY && !exports.noAnimation) {
86
+ this.spinner = (0, ora_1.default)(applyEmoji(text, emoji)).start();
87
+ }
88
+ else {
89
+ console.log(applyEmoji(text, emoji));
90
+ }
91
+ return this;
92
+ }
93
+ this.spinner.text = applyEmoji(text, emoji || this.emoji);
94
+ return this;
95
+ }
96
+ succeed(text) {
97
+ const duration = this.getDuration();
98
+ const successText = text || this.text;
99
+ const durationInfo = duration > 1000 ? ` (${duration}ms)` : '';
100
+ if (this.spinner) {
101
+ this.spinner.succeed(applyEmoji(successText, exports.EMOJIS.SUCCESS) + durationInfo);
102
+ }
103
+ else {
104
+ console.log(applyEmoji(successText + durationInfo, exports.EMOJIS.SUCCESS));
105
+ }
106
+ return this;
107
+ }
108
+ fail(text) {
109
+ const failText = text || this.text;
110
+ if (this.spinner) {
111
+ this.spinner.fail(applyEmoji(failText, exports.EMOJIS.ERROR));
112
+ }
113
+ else {
114
+ console.error(applyEmoji(failText, exports.EMOJIS.ERROR));
115
+ }
116
+ return this;
117
+ }
118
+ stop() {
119
+ if (this.spinner) {
120
+ this.spinner.stop();
121
+ }
122
+ return this;
123
+ }
124
+ getDuration() {
125
+ return Date.now() - this.startTime;
126
+ }
127
+ }
128
+ exports.Spinner = Spinner;
129
+ // Utility function to wrap async operations with spinner
130
+ async function withSpinner(text, operation, options = {}) {
131
+ const spinner = new Spinner(text, options.emoji);
132
+ spinner.start();
133
+ try {
134
+ const result = await operation();
135
+ spinner.succeed(options.successText);
136
+ return result;
137
+ }
138
+ catch (error) {
139
+ spinner.fail(options.failText);
140
+ throw error;
141
+ }
142
+ }
143
+ // Progress bar utilities
144
+ class ProgressBar {
145
+ options;
146
+ bar = null;
147
+ constructor(options) {
148
+ this.options = options;
149
+ }
150
+ start() {
151
+ if (!exports.isTTY || exports.noAnimation) {
152
+ if (this.options.title) {
153
+ console.log(this.options.title);
154
+ }
155
+ return this;
156
+ }
157
+ this.bar = new cli_progress_1.SingleBar({
158
+ format: this.options.format || '{bar} {percentage}% | {value}/{total} | {eta}s',
159
+ barCompleteChar: '\u2588',
160
+ barIncompleteChar: '\u2591',
161
+ hideCursor: true,
162
+ });
163
+ if (this.options.title) {
164
+ console.log(this.options.title);
165
+ }
166
+ this.bar.start(this.options.total, 0);
167
+ return this;
168
+ }
169
+ update(current) {
170
+ if (this.bar) {
171
+ this.bar.update(current);
172
+ }
173
+ else if (exports.isTTY && !exports.noAnimation) {
174
+ // Fallback to simple progress if bar not initialized
175
+ const percent = Math.round((current / this.options.total) * 100);
176
+ process.stdout.write(`\r${this.options.title || 'Progress'}: ${percent}% (${current}/${this.options.total})`);
177
+ }
178
+ return this;
179
+ }
180
+ increment(amount = 1) {
181
+ if (this.bar) {
182
+ this.bar.increment(amount);
183
+ }
184
+ return this;
185
+ }
186
+ stop() {
187
+ if (this.bar) {
188
+ this.bar.stop();
189
+ }
190
+ else if (exports.isTTY && !exports.noAnimation) {
191
+ process.stdout.write('\n');
192
+ }
193
+ return this;
194
+ }
195
+ }
196
+ exports.ProgressBar = ProgressBar;
197
+ // Enhanced console methods
198
+ exports.log = {
199
+ success: (text) => console.log(exports.colors.success(applyEmoji(text, exports.EMOJIS.SUCCESS))),
200
+ error: (text) => console.error(exports.colors.error(applyEmoji(text, exports.EMOJIS.ERROR))),
201
+ warning: (text) => console.warn(exports.colors.warning(applyEmoji(text, exports.EMOJIS.WARNING))),
202
+ info: (text) => console.log(exports.colors.info(applyEmoji(text, exports.EMOJIS.INFO))),
203
+ plain: (text) => console.log(text),
204
+ header: (text) => console.log(exports.colors.accent(`\n${text}\n`)),
205
+ step: (step, total, text) => {
206
+ const stepText = `${step}/${total}`;
207
+ console.log(exports.colors.muted(`[${stepText}]`) + ' ' + text);
208
+ },
209
+ };
210
+ // Utility to show a section header
211
+ function showSection(title, emoji) {
212
+ console.log('\n' + exports.colors.accent('='.repeat(50)));
213
+ console.log(applyEmoji(title, emoji));
214
+ console.log(exports.colors.accent('='.repeat(50)) + '\n');
215
+ }
216
+ // Utility to show next steps
217
+ function showNextSteps(steps) {
218
+ console.log(exports.colors.accent('\nNext steps:'));
219
+ steps.forEach((step, index) => {
220
+ console.log(` ${index + 1}. ${step}`);
221
+ });
222
+ }
package/package.json CHANGED
@@ -1,33 +1,37 @@
1
- {
2
- "name": "blok0",
3
- "version": "0.1.2",
4
- "description": "CLI tool for Payload CMS scaffolding",
5
- "main": "dist/index.js",
6
- "bin": {
7
- "blok0": "./dist/index.js"
8
- },
9
- "scripts": {
10
- "build": "bun run tsc",
11
- "dev": "bun run tsx src/index.ts"
12
- },
13
- "keywords": [
14
- "cli",
15
- "payload",
16
- "cms"
17
- ],
18
- "author": "Your Name",
19
- "license": "MIT",
20
- "dependencies": {
21
- "axios": "^1.13.2",
22
- "keytar": "^7.9.0",
23
- "open": "^11.0.0",
24
- "payload": "^2.0.0",
25
- "ts-morph": "^27.0.2"
26
- },
27
- "devDependencies": {
28
- "@types/node": "^20.19.27",
29
- "bun-types": "^1.0.0",
30
- "tsx": "^4.0.0",
31
- "typescript": "^5.0.0"
32
- }
33
- }
1
+ {
2
+ "name": "blok0",
3
+ "version": "0.1.4",
4
+ "description": "CLI tool for Payload CMS scaffolding",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "blok0": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "bun run tsc",
11
+ "dev": "bun run tsx src/index.ts"
12
+ },
13
+ "keywords": [
14
+ "cli",
15
+ "payload",
16
+ "cms"
17
+ ],
18
+ "author": "Your Name",
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "axios": "^1.13.2",
22
+ "chalk": "^5.6.2",
23
+ "cli-progress": "^3.12.0",
24
+ "keytar": "^7.9.0",
25
+ "open": "^11.0.0",
26
+ "ora": "^9.0.0",
27
+ "payload": "^2.0.0",
28
+ "ts-morph": "^27.0.2"
29
+ },
30
+ "devDependencies": {
31
+ "@types/cli-progress": "^3.11.6",
32
+ "@types/node": "^20.19.27",
33
+ "bun-types": "^1.0.0",
34
+ "tsx": "^4.0.0",
35
+ "typescript": "^5.0.0"
36
+ }
37
+ }
@@ -10,40 +10,47 @@ import {
10
10
  validateBlockDirectory
11
11
  } from '../blocks';
12
12
  import { updatePageCollectionConfig, updateRenderBlocksComponent, findPagesCollection, findRenderBlocksComponent } from '../ast';
13
+ import { withSpinner, log, showSection, showNextSteps, EMOJIS, ProgressBar } from '../ui';
13
14
 
14
15
  /**
15
16
  * Handle add block command
16
17
  */
17
18
  export async function handleAddBlock(blockUrl: string, options: { force?: boolean; dryRun?: boolean } = {}): Promise<void> {
18
- console.log('📦 Adding Blok0 Block');
19
- console.log('====================');
20
- console.log('');
19
+ showSection('📦 Adding Blok0 Block', EMOJIS.PACKAGE);
21
20
 
22
21
  try {
23
22
  // Step 1: Authentication check
24
- console.log('🔐 Checking authentication...');
25
- const authenticated = await isAuthenticated();
23
+ const authenticated = await withSpinner(
24
+ 'Checking authentication',
25
+ () => isAuthenticated(),
26
+ { emoji: EMOJIS.LOCK }
27
+ );
28
+
26
29
  if (!authenticated) {
27
- console.error('You are not logged in. Please run `blok0 login` first.');
30
+ log.error('You are not logged in. Please run `blok0 login` first.');
28
31
  process.exit(1);
29
32
  }
30
33
 
31
34
  // Step 2: Fetch block data from API
32
- console.log(`📡 Fetching block from: ${blockUrl}`);
33
- const { metadata, files } = await apiClient.fetchBlockData(blockUrl);
34
- console.log(`✅ Found block: "${metadata.name}" (${metadata.slug})`);
35
+ const { metadata, files } = await withSpinner(
36
+ `Fetching block from ${blockUrl}`,
37
+ () => apiClient.fetchBlockData(blockUrl),
38
+ { emoji: EMOJIS.SEARCH }
39
+ );
40
+
41
+ log.success(`Found block: "${metadata.name}" (${metadata.slug})`);
35
42
 
36
43
  // Step 3: Check if block is already registered
37
44
  if (isBlockRegistered(metadata.slug)) {
38
45
  if (!options.force) {
39
- console.error(`❌ Block "${metadata.slug}" is already installed. Use --force to reinstall.`);
46
+ log.error(`Block "${metadata.slug}" is already installed. Use --force to reinstall.`);
40
47
  process.exit(1);
41
48
  }
42
- console.log('⚠️ Block already exists, reinstalling...');
49
+ log.warning('Block already exists, reinstalling...');
43
50
  }
44
51
 
45
52
  if (options.dryRun) {
46
- console.log('🔍 Dry run mode - would perform the following actions:');
53
+ log.info('Dry run mode - would perform the following actions:');
47
54
  console.log(` - Create directory: src/blocks/${metadata.slug}`);
48
55
  console.log(` - Download ${files.length} files`);
49
56
  console.log(' - Update Payload config');
@@ -56,14 +63,13 @@ export async function handleAddBlock(blockUrl: string, options: { force?: boolea
56
63
  const blocksDir = ensureBlocksDirectory();
57
64
 
58
65
  // Step 5: Create block directory and files
59
- console.log('📁 Creating block directory and files...');
60
66
  const { dir, configPath, componentPath } = createBlockDirectory(blocksDir, metadata.slug, files);
61
- console.log(`✅ Created block directory: ${path.relative(process.cwd(), dir)}`);
67
+ log.success(`Created block directory: ${path.relative(process.cwd(), dir)}`);
62
68
 
63
69
  // Step 6: Validate created block
64
70
  const validation = validateBlockDirectory(dir);
65
71
  if (!validation.valid) {
66
- console.error('Block validation failed:');
72
+ log.error('Block validation failed:');
67
73
  validation.errors.forEach(error => console.error(` - ${error}`));
68
74
  // Cleanup on failure
69
75
  require('fs').rmSync(dir, { recursive: true, force: true });
@@ -90,43 +96,50 @@ export async function handleAddBlock(blockUrl: string, options: { force?: boolea
90
96
  // Step 9: Update Pages collection (AST manipulation)
91
97
  const pagesCollectionPath = findPagesCollection();
92
98
  if (pagesCollectionPath) {
93
- console.log('🔧 Updating Pages collection...');
94
- const blockIdentifier = slugToIdentifier(metadata.slug);
95
- const relativeConfigPath = `@/blocks/${metadata.slug}/config`;
96
-
97
- updatePageCollectionConfig(pagesCollectionPath, relativeConfigPath, blockIdentifier);
98
- console.log(`✅ Added ${blockIdentifier} to Pages collection`);
99
+ await withSpinner(
100
+ 'Updating Pages collection',
101
+ async () => {
102
+ const blockIdentifier = slugToIdentifier(metadata.slug);
103
+ const relativeConfigPath = `@/blocks/${metadata.slug}/config`;
104
+ updatePageCollectionConfig(pagesCollectionPath, relativeConfigPath, blockIdentifier);
105
+ },
106
+ { emoji: EMOJIS.GEAR, successText: `Added ${slugToIdentifier(metadata.slug)} to Pages collection` }
107
+ );
99
108
  } else {
100
- console.warn('⚠️ Could not find Pages collection file. You may need to manually add the block to your collections.');
109
+ log.warning('Could not find Pages collection file. You may need to manually add the block to your collections.');
101
110
  }
102
111
 
103
112
  // Step 10: Update RenderBlocks component (AST manipulation)
104
113
  const renderBlocksPath = findRenderBlocksComponent();
105
114
  if (renderBlocksPath) {
106
- console.log('🔧 Updating RenderBlocks component...');
107
- const relativeComponentPath = `./${metadata.slug}/Component`;
108
-
109
- updateRenderBlocksComponent(renderBlocksPath, metadata.slug, relativeComponentPath);
110
- console.log(`✅ Added ${metadata.slug} component to RenderBlocks`);
115
+ await withSpinner(
116
+ 'Updating RenderBlocks component',
117
+ async () => {
118
+ const relativeComponentPath = `./${metadata.slug}/Component`;
119
+ updateRenderBlocksComponent(renderBlocksPath, metadata.slug, relativeComponentPath);
120
+ },
121
+ { emoji: EMOJIS.GEAR, successText: `Added ${metadata.slug} component to RenderBlocks` }
122
+ );
111
123
  } else {
112
- console.warn('⚠️ Could not find RenderBlocks component. You may need to manually add the block component.');
124
+ log.warning('Could not find RenderBlocks component. You may need to manually add the block component.');
113
125
  }
114
126
 
115
127
  // Step 11: Register block in registry
116
- console.log('📝 Registering block...');
117
- addBlockToRegistry(blockEntry);
118
- console.log('✅ Block registered successfully');
119
-
120
- console.log('');
121
- console.log('🎉 Block installation complete!');
122
- console.log('');
123
- console.log('Next steps:');
124
- console.log('1. Review the installed files in src/blocks/' + metadata.slug);
125
- console.log('2. Test your application to ensure the block works correctly');
126
- console.log('3. Commit the changes to your repository');
128
+ await withSpinner(
129
+ 'Registering block',
130
+ async () => addBlockToRegistry(blockEntry),
131
+ { emoji: EMOJIS.CHECK, successText: 'Block registered successfully' }
132
+ );
133
+
134
+ log.success('Block installation complete!');
135
+ showNextSteps([
136
+ `Review the installed files in src/blocks/${metadata.slug}`,
137
+ 'Test your application to ensure the block works correctly',
138
+ 'Commit the changes to your repository'
139
+ ]);
127
140
 
128
141
  } catch (error) {
129
142
  console.error('❌ Failed to add block:', (error as Error).message);
130
143
  process.exit(1);
131
144
  }
132
- }
145
+ }
@@ -1,6 +1,7 @@
1
1
  import { createInterface } from 'readline';
2
2
  import { exec, spawn } from 'child_process';
3
3
  import { promisify } from 'util';
4
+ import { withSpinner, log, showNextSteps, EMOJIS } from '../ui';
4
5
 
5
6
  const execAsync = promisify(exec);
6
7
 
@@ -20,43 +21,39 @@ function prompt(question: string): Promise<boolean> {
20
21
  }
21
22
 
22
23
  export async function generateStarter(): Promise<void> {
23
- console.log('Cloning starter repository...');
24
- try {
25
- await execAsync(`git clone --depth 1 ${repoUrl} .`);
26
- console.log('Repository cloned successfully.');
27
- } catch (error) {
28
- throw new Error(`Failed to clone repository: ${error}`);
29
- }
24
+ log.header('🚀 Setting up Blok0 starter project...');
30
25
 
31
- // Prompt for bun install
32
- const installDeps = await prompt('Run \'bun install\' to install dependencies? (y/n): ');
33
- if (installDeps) {
34
- console.log('Installing dependencies...');
35
- try {
36
- await new Promise<void>((resolve, reject) => {
37
- const child = spawn('bun', ['install'], { stdio: 'inherit' });
38
- child.on('close', (code) => {
39
- if (code === 0) resolve();
40
- else reject(new Error('Failed to install dependencies'));
41
- });
42
- child.on('error', reject);
43
- });
44
- } catch (error) {
45
- console.error('Failed to install dependencies:', error);
26
+ // Clone repository with spinner
27
+ await withSpinner(
28
+ 'Cloning starter repository',
29
+ async () => {
30
+ await execAsync(`git clone --depth 1 ${repoUrl} .`);
31
+ },
32
+ {
33
+ emoji: EMOJIS.DOWNLOAD,
34
+ successText: 'Repository cloned successfully'
46
35
  }
47
- }
36
+ );
48
37
 
49
38
  // Prompt for git init
50
39
  const initGit = await prompt('Initialize git repository? (y/n): ');
51
40
  if (initGit) {
52
- console.log('Initializing git repository...');
53
- try {
54
- await execAsync('git init');
55
- console.log('Git repository initialized.');
56
- } catch (error) {
57
- console.error('Failed to initialize git:', error);
58
- }
41
+ await withSpinner(
42
+ 'Initializing git repository',
43
+ async () => {
44
+ await execAsync('git init');
45
+ },
46
+ {
47
+ emoji: EMOJIS.GEAR,
48
+ successText: 'Git repository initialized'
49
+ }
50
+ );
59
51
  }
60
52
 
61
- console.log('Blok0 starter project created successfully!');
53
+ log.success('Starter project ready!');
54
+ showNextSteps([
55
+ 'Run \'npm install\' or \'bun install\' to install dependencies',
56
+ 'Start developing your Blok0 x PayloadCMS project'
57
+ ]);
62
58
  }
59
+
@@ -1,6 +1,7 @@
1
1
  import { isAuthenticated, clearCredentials, storeAccessToken, AuthCallback } from '../auth';
2
2
  import { AuthServer } from '../auth/server';
3
3
  import open from 'open';
4
+ import { withSpinner, log, showSection, EMOJIS } from '../ui';
4
5
 
5
6
  // Add SIGINT handler for graceful cleanup
6
7
  process.on('SIGINT', () => {
@@ -15,13 +16,15 @@ export async function handleLogin(token?: string, manual?: boolean): Promise<voi
15
16
  // Direct token authentication (CI/CD)
16
17
  if (token) {
17
18
  try {
18
- console.log('🔐 Saving authentication token...');
19
- await storeAccessToken(token);
20
- console.log('✅ Successfully authenticated!');
19
+ await withSpinner(
20
+ 'Saving authentication token',
21
+ () => storeAccessToken(token),
22
+ { emoji: EMOJIS.LOCK, successText: 'Successfully authenticated!' }
23
+ );
21
24
  console.log('');
22
- console.log('You can now use blok0 commands that require authentication.');
25
+ log.info('You can now use blok0 commands that require authentication.');
23
26
  } catch (error) {
24
- console.error('Failed to save authentication token:', (error as Error).message);
27
+ log.error('Failed to save authentication token: ' + (error as Error).message);
25
28
  process.exit(1);
26
29
  }
27
30
  return;
@@ -49,36 +52,40 @@ export async function handleLogin(token?: string, manual?: boolean): Promise<voi
49
52
  * Handle browser-based authentication flow
50
53
  */
51
54
  async function handleBrowserLogin(): Promise<void> {
52
- console.log('🔐 Blok0 Authentication');
53
- console.log('======================');
54
- console.log('');
55
+ showSection('🔐 Blok0 Authentication', EMOJIS.LOCK);
55
56
 
56
57
  // Create authentication server
57
58
  const authServer = new AuthServer();
58
59
 
59
60
  try {
60
61
  // Initialize server (find available port)
61
- console.log('🚀 Starting authentication server...');
62
- await authServer.initialize();
62
+ await withSpinner(
63
+ 'Starting authentication server',
64
+ () => authServer.initialize(),
65
+ { emoji: EMOJIS.ROCKET }
66
+ );
63
67
 
64
68
  // Get the authorization URL (now port is available)
65
69
  const authUrl = authServer.getAuthorizationUrl();
66
70
 
67
- console.log('🌐 Opening browser for authentication...');
71
+ log.info('Opening browser for authentication...');
68
72
  await open(authUrl);
69
73
 
70
- console.log('📱 Please complete authentication in your browser.');
71
- console.log('⏳ Waiting for authentication to complete...');
74
+ log.info('Please complete authentication in your browser.');
75
+ log.plain('⏳ Waiting for authentication to complete...');
72
76
 
73
77
  // Start server and wait for callback
74
78
  const authCallback: AuthCallback = await authServer.start();
75
79
 
76
80
  // Store the token
77
- console.log('🔐 Saving authentication token...');
78
- await storeAccessToken(authCallback.token);
79
- console.log('✅ Successfully authenticated!');
81
+ await withSpinner(
82
+ 'Saving authentication token',
83
+ () => storeAccessToken(authCallback.token),
84
+ { emoji: EMOJIS.LOCK, successText: 'Successfully authenticated!' }
85
+ );
86
+
80
87
  console.log('');
81
- console.log('You can now use blok0 commands that require authentication.');
88
+ log.info('You can now use blok0 commands that require authentication.');
82
89
 
83
90
  } catch (error) {
84
91
  authServer.stop();
@@ -90,23 +97,21 @@ async function handleBrowserLogin(): Promise<void> {
90
97
  * Show manual authentication instructions
91
98
  */
92
99
  function showManualInstructions(): void {
93
- console.log('🔐 Blok0 Manual Authentication');
94
- console.log('==============================');
95
- console.log('');
100
+ showSection('🔐 Blok0 Manual Authentication', EMOJIS.LOCK);
96
101
  console.log('To authenticate with the Blok0 API, make a POST request to:');
97
102
  console.log('https://www.blok0.xyz/api/customers/login');
98
103
  console.log('');
99
- console.log('Example using curl:');
104
+ log.info('Example using curl:');
100
105
  console.log('curl -X POST https://www.blok0.xyz/api/customers/login \\');
101
106
  console.log(' -H "Content-Type: application/json" \\');
102
107
  console.log(' -d \'{"email": "your-email@example.com", "password": "your-password"}\'');
103
108
  console.log('');
104
- console.log('Then copy the access token and run:');
109
+ log.info('Then copy the access token and run:');
105
110
  console.log('blok0 login --token <your-token>');
106
111
  console.log('');
107
- console.log('For CI/CD environments, set the BLOK0_TOKEN environment variable.');
112
+ log.info('For CI/CD environments, set the BLOK0_TOKEN environment variable.');
108
113
  console.log('');
109
- console.log('💡 For browser-based login, run: blok0 login');
114
+ log.info('For browser-based login, run: blok0 login');
110
115
  }
111
116
 
112
117
  /**
@@ -114,17 +119,23 @@ function showManualInstructions(): void {
114
119
  */
115
120
  export async function handleLogout(): Promise<void> {
116
121
  try {
117
- const wasAuthenticated = await isAuthenticated();
122
+ const wasAuthenticated = await withSpinner(
123
+ 'Checking authentication status',
124
+ () => isAuthenticated()
125
+ );
118
126
 
119
127
  if (!wasAuthenticated) {
120
- console.log('You are not currently logged in.');
128
+ log.warning('You are not currently logged in.');
121
129
  return;
122
130
  }
123
131
 
124
- await clearCredentials();
125
- console.log(' Successfully logged out and cleared stored credentials.');
132
+ await withSpinner(
133
+ 'Clearing stored credentials',
134
+ () => clearCredentials(),
135
+ { emoji: EMOJIS.LOCK, successText: 'Successfully logged out and cleared stored credentials.' }
136
+ );
126
137
  } catch (error) {
127
- console.error('Failed to logout:', (error as Error).message);
138
+ log.error('Failed to logout: ' + (error as Error).message);
128
139
  process.exit(1);
129
140
  }
130
141
  }