blok0 0.1.2 → 0.1.3
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 +22 -15
- 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 +31 -15
- package/src/ui.ts +234 -0
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.
|
|
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
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "blok0",
|
|
3
|
+
"version": "0.1.3",
|
|
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
|
-
|
|
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
|
-
|
|
25
|
-
|
|
23
|
+
const authenticated = await withSpinner(
|
|
24
|
+
'Checking authentication',
|
|
25
|
+
() => isAuthenticated(),
|
|
26
|
+
{ emoji: EMOJIS.LOCK }
|
|
27
|
+
);
|
|
28
|
+
|
|
26
29
|
if (!authenticated) {
|
|
27
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
46
|
+
log.error(`Block "${metadata.slug}" is already installed. Use --force to reinstall.`);
|
|
40
47
|
process.exit(1);
|
|
41
48
|
}
|
|
42
|
-
|
|
49
|
+
log.warning('Block already exists, reinstalling...');
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
if (options.dryRun) {
|
|
46
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
+
}
|
package/src/handlers/generate.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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
|
+
|
package/src/handlers/login.ts
CHANGED
|
@@ -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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
await withSpinner(
|
|
20
|
+
'Saving authentication token',
|
|
21
|
+
() => storeAccessToken(token),
|
|
22
|
+
{ emoji: EMOJIS.LOCK, successText: 'Successfully authenticated!' }
|
|
23
|
+
);
|
|
21
24
|
console.log('');
|
|
22
|
-
|
|
25
|
+
log.info('You can now use blok0 commands that require authentication.');
|
|
23
26
|
} catch (error) {
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
71
|
+
log.info('Opening browser for authentication...');
|
|
68
72
|
await open(authUrl);
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
109
|
+
log.info('Then copy the access token and run:');
|
|
105
110
|
console.log('blok0 login --token <your-token>');
|
|
106
111
|
console.log('');
|
|
107
|
-
|
|
112
|
+
log.info('For CI/CD environments, set the BLOK0_TOKEN environment variable.');
|
|
108
113
|
console.log('');
|
|
109
|
-
|
|
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
|
|
122
|
+
const wasAuthenticated = await withSpinner(
|
|
123
|
+
'Checking authentication status',
|
|
124
|
+
() => isAuthenticated()
|
|
125
|
+
);
|
|
118
126
|
|
|
119
127
|
if (!wasAuthenticated) {
|
|
120
|
-
|
|
128
|
+
log.warning('You are not currently logged in.');
|
|
121
129
|
return;
|
|
122
130
|
}
|
|
123
131
|
|
|
124
|
-
await
|
|
125
|
-
|
|
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
|
-
|
|
138
|
+
log.error('Failed to logout: ' + (error as Error).message);
|
|
128
139
|
process.exit(1);
|
|
129
140
|
}
|
|
130
141
|
}
|