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/handlers/add-block.js +29 -36
- package/dist/handlers/generate.js +18 -35
- package/dist/handlers/login.js +20 -29
- package/dist/index.js +23 -16
- package/dist/ui.d.ts +77 -0
- package/dist/ui.js +222 -0
- package/package.json +37 -33
- package/src/handlers/add-block.ts +53 -40
- package/src/handlers/generate.ts +28 -31
- package/src/handlers/login.ts +40 -29
- package/src/index.ts +32 -16
- package/src/ui.ts +234 -0
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { generateStarter } from './handlers/generate';
|
|
|
7
7
|
import { handleLogin, handleLogout } from './handlers/login';
|
|
8
8
|
import { handleAddBlock } from './handlers/add-block';
|
|
9
9
|
import { createEmptyRegistry } from './registry';
|
|
10
|
+
import { setUIFlags } from './ui';
|
|
10
11
|
|
|
11
12
|
function prompt(question: string): Promise<string> {
|
|
12
13
|
return new Promise((resolve) => {
|
|
@@ -43,6 +44,9 @@ OPTIONS:
|
|
|
43
44
|
--version, -v Show version information
|
|
44
45
|
--verbose Enable verbose logging
|
|
45
46
|
--dry-run Preview changes without applying them
|
|
47
|
+
--no-animation Disable animations and spinners
|
|
48
|
+
--no-emoji Disable emoji in output
|
|
49
|
+
--ci Optimize for CI environments (implies --no-animation and --no-emoji)
|
|
46
50
|
|
|
47
51
|
EXAMPLES:
|
|
48
52
|
blok0 login
|
|
@@ -56,18 +60,30 @@ For more information, visit: https://github.com/blok0-payload/cli
|
|
|
56
60
|
async function main() {
|
|
57
61
|
const args = process.argv.slice(2);
|
|
58
62
|
|
|
59
|
-
|
|
63
|
+
// Parse global UI flags
|
|
64
|
+
const noAnimation = args.includes('--no-animation');
|
|
65
|
+
const noEmoji = args.includes('--no-emoji');
|
|
66
|
+
const ciMode = args.includes('--ci');
|
|
67
|
+
|
|
68
|
+
setUIFlags({ noAnimation, noEmoji, ci: ciMode });
|
|
69
|
+
|
|
70
|
+
// Filter out global flags from args
|
|
71
|
+
const filteredArgs = args.filter(arg =>
|
|
72
|
+
!['--no-animation', '--no-emoji', '--ci'].includes(arg)
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
if (filteredArgs.length === 0 || filteredArgs.includes('--help') || filteredArgs.includes('-h')) {
|
|
60
76
|
showHelp();
|
|
61
77
|
process.exit(0);
|
|
62
78
|
}
|
|
63
79
|
|
|
64
|
-
if (
|
|
80
|
+
if (filteredArgs.includes('--version') || filteredArgs.includes('-v')) {
|
|
65
81
|
const pkg = require('../package.json');
|
|
66
82
|
console.log(`blok0 v${pkg.version}`);
|
|
67
83
|
process.exit(0);
|
|
68
84
|
}
|
|
69
85
|
|
|
70
|
-
const [command, ...restArgs] =
|
|
86
|
+
const [command, ...restArgs] = filteredArgs;
|
|
71
87
|
|
|
72
88
|
try {
|
|
73
89
|
switch (command) {
|
|
@@ -107,7 +123,7 @@ async function main() {
|
|
|
107
123
|
case 'add':
|
|
108
124
|
const [addSubcommand, ...addRestArgs] = restArgs;
|
|
109
125
|
if (addSubcommand === 'block') {
|
|
110
|
-
const blockUrl = `https://www.blok0.
|
|
126
|
+
const blockUrl = `https://www.blok0.xyz/api/cli/sections/${addRestArgs[0]}`;
|
|
111
127
|
if (!blockUrl) {
|
|
112
128
|
console.error('Error: Block Slug is required. Use: blok0 add block <slug>');
|
|
113
129
|
process.exit(1);
|
|
@@ -118,7 +134,7 @@ async function main() {
|
|
|
118
134
|
};
|
|
119
135
|
await handleAddBlock(blockUrl, options);
|
|
120
136
|
} else {
|
|
121
|
-
console.error('Error: Invalid subcommand. Use: blok0 add block <
|
|
137
|
+
console.error('Error: Invalid subcommand. Use: blok0 add block <slug>');
|
|
122
138
|
process.exit(1);
|
|
123
139
|
}
|
|
124
140
|
break;
|
|
@@ -167,16 +183,16 @@ async function handleGenerateStarter(args: string[]) {
|
|
|
167
183
|
}
|
|
168
184
|
|
|
169
185
|
async function handleDebug() {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
186
|
+
const { showSection, log, withSpinner, EMOJIS } = await import('./ui');
|
|
187
|
+
|
|
188
|
+
showSection('๐ Blok0 CLI Debug Information', EMOJIS.SEARCH);
|
|
173
189
|
|
|
174
190
|
// Check stored token
|
|
175
191
|
const { getAccessToken, isAuthenticated } = await import('./auth');
|
|
176
192
|
const token = await getAccessToken();
|
|
177
193
|
const isAuth = await isAuthenticated();
|
|
178
194
|
|
|
179
|
-
|
|
195
|
+
log.header('๐ Authentication Status:');
|
|
180
196
|
console.log(` Authenticated: ${isAuth ? 'โ
Yes' : 'โ No'}`);
|
|
181
197
|
console.log(` Token Stored: ${token ? 'โ
Yes' : 'โ No'}`);
|
|
182
198
|
|
|
@@ -185,25 +201,25 @@ async function handleDebug() {
|
|
|
185
201
|
console.log(` Authorization Header: Bearer ${token}`);
|
|
186
202
|
}
|
|
187
203
|
|
|
188
|
-
|
|
189
|
-
console.log('๐ API Configuration:');
|
|
204
|
+
log.header('๐ API Configuration:');
|
|
190
205
|
console.log(' Base URL: https://www.blok0.xyz');
|
|
191
206
|
console.log(' User Agent: blok0-cli/1.0.0');
|
|
192
207
|
|
|
193
|
-
|
|
194
|
-
console.log('๐งช Test API Connection:');
|
|
208
|
+
log.header('๐งช Test API Connection:');
|
|
195
209
|
|
|
196
210
|
// Test API connection
|
|
197
211
|
const { apiClient } = await import('./api');
|
|
198
212
|
try {
|
|
199
|
-
const connectionTest = await
|
|
213
|
+
const connectionTest = await withSpinner(
|
|
214
|
+
'Testing API connection',
|
|
215
|
+
() => apiClient.testConnection()
|
|
216
|
+
);
|
|
200
217
|
console.log(` Connection Test: ${connectionTest ? 'โ
Passed' : 'โ Failed'}`);
|
|
201
218
|
} catch (error) {
|
|
202
219
|
console.log(` Connection Test: โ Failed - ${(error as Error).message}`);
|
|
203
220
|
}
|
|
204
221
|
|
|
205
|
-
|
|
206
|
-
console.log('๐ก Next Steps:');
|
|
222
|
+
log.header('๐ก Next Steps:');
|
|
207
223
|
console.log(' 1. If no token, run: blok0 login');
|
|
208
224
|
console.log(' 2. Test API with: blok0 add block <url>');
|
|
209
225
|
console.log(' 3. Check server logs for detailed request info');
|
package/src/ui.ts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import ora, { Ora } from 'ora';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { SingleBar } from 'cli-progress';
|
|
4
|
+
|
|
5
|
+
// Emoji constants for consistent usage
|
|
6
|
+
export const EMOJIS = {
|
|
7
|
+
SUCCESS: 'โ
',
|
|
8
|
+
ERROR: 'โ',
|
|
9
|
+
WARNING: 'โ ๏ธ',
|
|
10
|
+
INFO: 'โน๏ธ',
|
|
11
|
+
LOCK: '๐',
|
|
12
|
+
PACKAGE: '๐ฆ',
|
|
13
|
+
FOLDER: '๐',
|
|
14
|
+
GEAR: '๐ง',
|
|
15
|
+
SEARCH: '๐',
|
|
16
|
+
ROCKET: '๐',
|
|
17
|
+
DOWNLOAD: '๐ฅ',
|
|
18
|
+
PARTY: '๐',
|
|
19
|
+
WRENCH: '๐ง',
|
|
20
|
+
CHECK: 'โ
',
|
|
21
|
+
CROSS: 'โ',
|
|
22
|
+
ARROW: 'โ',
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
// Color utilities
|
|
26
|
+
export const colors = {
|
|
27
|
+
success: chalk.green,
|
|
28
|
+
error: chalk.red,
|
|
29
|
+
warning: chalk.yellow,
|
|
30
|
+
info: chalk.blue,
|
|
31
|
+
accent: chalk.cyan,
|
|
32
|
+
muted: chalk.gray,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Check if we're in a TTY environment
|
|
36
|
+
export const isTTY = process.stdout.isTTY;
|
|
37
|
+
|
|
38
|
+
// Global flags for disabling features
|
|
39
|
+
export let noAnimation = false;
|
|
40
|
+
export let noEmoji = false;
|
|
41
|
+
export let ciMode = false;
|
|
42
|
+
|
|
43
|
+
// Set global flags
|
|
44
|
+
export function setUIFlags(flags: { noAnimation?: boolean; noEmoji?: boolean; ci?: boolean }) {
|
|
45
|
+
noAnimation = flags.noAnimation || ciMode;
|
|
46
|
+
noEmoji = flags.noEmoji || ciMode;
|
|
47
|
+
ciMode = flags.ci || false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Apply emoji settings to text
|
|
51
|
+
function applyEmoji(text: string, emoji?: string): string {
|
|
52
|
+
if (noEmoji || !emoji) return text;
|
|
53
|
+
return `${emoji} ${text}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Spinner wrapper with TTY detection
|
|
57
|
+
export class Spinner {
|
|
58
|
+
private spinner: Ora | null = null;
|
|
59
|
+
private startTime: number = 0;
|
|
60
|
+
|
|
61
|
+
constructor(private text: string, private emoji?: string) {}
|
|
62
|
+
|
|
63
|
+
start(): this {
|
|
64
|
+
if (!isTTY || noAnimation) {
|
|
65
|
+
console.log(applyEmoji(this.text, this.emoji));
|
|
66
|
+
this.startTime = Date.now();
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.spinner = ora({
|
|
71
|
+
text: applyEmoji(this.text, this.emoji),
|
|
72
|
+
spinner: 'dots',
|
|
73
|
+
}).start();
|
|
74
|
+
this.startTime = Date.now();
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
update(text: string, emoji?: string): this {
|
|
79
|
+
if (!this.spinner) {
|
|
80
|
+
if (isTTY && !noAnimation) {
|
|
81
|
+
this.spinner = ora(applyEmoji(text, emoji)).start();
|
|
82
|
+
} else {
|
|
83
|
+
console.log(applyEmoji(text, emoji));
|
|
84
|
+
}
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.spinner.text = applyEmoji(text, emoji || this.emoji);
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
succeed(text?: string): this {
|
|
93
|
+
const duration = this.getDuration();
|
|
94
|
+
const successText = text || this.text;
|
|
95
|
+
const durationInfo = duration > 1000 ? ` (${duration}ms)` : '';
|
|
96
|
+
|
|
97
|
+
if (this.spinner) {
|
|
98
|
+
this.spinner.succeed(applyEmoji(successText, EMOJIS.SUCCESS) + durationInfo);
|
|
99
|
+
} else {
|
|
100
|
+
console.log(applyEmoji(successText + durationInfo, EMOJIS.SUCCESS));
|
|
101
|
+
}
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fail(text?: string): this {
|
|
106
|
+
const failText = text || this.text;
|
|
107
|
+
if (this.spinner) {
|
|
108
|
+
this.spinner.fail(applyEmoji(failText, EMOJIS.ERROR));
|
|
109
|
+
} else {
|
|
110
|
+
console.error(applyEmoji(failText, EMOJIS.ERROR));
|
|
111
|
+
}
|
|
112
|
+
return this;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
stop(): this {
|
|
116
|
+
if (this.spinner) {
|
|
117
|
+
this.spinner.stop();
|
|
118
|
+
}
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private getDuration(): number {
|
|
123
|
+
return Date.now() - this.startTime;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Utility function to wrap async operations with spinner
|
|
128
|
+
export async function withSpinner<T>(
|
|
129
|
+
text: string,
|
|
130
|
+
operation: () => Promise<T>,
|
|
131
|
+
options: { emoji?: string; successText?: string; failText?: string } = {}
|
|
132
|
+
): Promise<T> {
|
|
133
|
+
const spinner = new Spinner(text, options.emoji);
|
|
134
|
+
spinner.start();
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const result = await operation();
|
|
138
|
+
spinner.succeed(options.successText);
|
|
139
|
+
return result;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
spinner.fail(options.failText);
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Progress bar utilities
|
|
147
|
+
export class ProgressBar {
|
|
148
|
+
private bar: SingleBar | null = null;
|
|
149
|
+
|
|
150
|
+
constructor(private options: {
|
|
151
|
+
title?: string;
|
|
152
|
+
total: number;
|
|
153
|
+
format?: string;
|
|
154
|
+
}) {}
|
|
155
|
+
|
|
156
|
+
start(): this {
|
|
157
|
+
if (!isTTY || noAnimation) {
|
|
158
|
+
if (this.options.title) {
|
|
159
|
+
console.log(this.options.title);
|
|
160
|
+
}
|
|
161
|
+
return this;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
this.bar = new SingleBar({
|
|
165
|
+
format: this.options.format || '{bar} {percentage}% | {value}/{total} | {eta}s',
|
|
166
|
+
barCompleteChar: '\u2588',
|
|
167
|
+
barIncompleteChar: '\u2591',
|
|
168
|
+
hideCursor: true,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (this.options.title) {
|
|
172
|
+
console.log(this.options.title);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.bar.start(this.options.total, 0);
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
update(current: number): this {
|
|
180
|
+
if (this.bar) {
|
|
181
|
+
this.bar.update(current);
|
|
182
|
+
} else if (isTTY && !noAnimation) {
|
|
183
|
+
// Fallback to simple progress if bar not initialized
|
|
184
|
+
const percent = Math.round((current / this.options.total) * 100);
|
|
185
|
+
process.stdout.write(`\r${this.options.title || 'Progress'}: ${percent}% (${current}/${this.options.total})`);
|
|
186
|
+
}
|
|
187
|
+
return this;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
increment(amount: number = 1): this {
|
|
191
|
+
if (this.bar) {
|
|
192
|
+
this.bar.increment(amount);
|
|
193
|
+
}
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
stop(): this {
|
|
198
|
+
if (this.bar) {
|
|
199
|
+
this.bar.stop();
|
|
200
|
+
} else if (isTTY && !noAnimation) {
|
|
201
|
+
process.stdout.write('\n');
|
|
202
|
+
}
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Enhanced console methods
|
|
208
|
+
export const log = {
|
|
209
|
+
success: (text: string) => console.log(colors.success(applyEmoji(text, EMOJIS.SUCCESS))),
|
|
210
|
+
error: (text: string) => console.error(colors.error(applyEmoji(text, EMOJIS.ERROR))),
|
|
211
|
+
warning: (text: string) => console.warn(colors.warning(applyEmoji(text, EMOJIS.WARNING))),
|
|
212
|
+
info: (text: string) => console.log(colors.info(applyEmoji(text, EMOJIS.INFO))),
|
|
213
|
+
plain: (text: string) => console.log(text),
|
|
214
|
+
header: (text: string) => console.log(colors.accent(`\n${text}\n`)),
|
|
215
|
+
step: (step: number, total: number, text: string) => {
|
|
216
|
+
const stepText = `${step}/${total}`;
|
|
217
|
+
console.log(colors.muted(`[${stepText}]`) + ' ' + text);
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Utility to show a section header
|
|
222
|
+
export function showSection(title: string, emoji?: string) {
|
|
223
|
+
console.log('\n' + colors.accent('='.repeat(50)));
|
|
224
|
+
console.log(applyEmoji(title, emoji));
|
|
225
|
+
console.log(colors.accent('='.repeat(50)) + '\n');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Utility to show next steps
|
|
229
|
+
export function showNextSteps(steps: string[]) {
|
|
230
|
+
console.log(colors.accent('\nNext steps:'));
|
|
231
|
+
steps.forEach((step, index) => {
|
|
232
|
+
console.log(` ${index + 1}. ${step}`);
|
|
233
|
+
});
|
|
234
|
+
}
|