@blkc-code/cli 0.1.2 → 0.2.0

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.
@@ -1,182 +0,0 @@
1
- /**
2
- * BLKC CLI - Circuit Commands
3
- *
4
- * Commands for working with BLKC Circuits.
5
- *
6
- * @package @blkc-code/cli
7
- * @license Apache-2.0
8
- * @author Black Code Enterprises LLC
9
- */
10
-
11
- import { Command } from "commander";
12
- import chalk from "chalk";
13
- import ora from "ora";
14
- import { readFile } from "node:fs/promises";
15
- import path from "node:path";
16
- import { CircuitSchema } from "@blkc-code/core";
17
-
18
- // ============================================================================
19
- // Command: circuit validate
20
- // ============================================================================
21
-
22
- function setupValidateCommand(program: Command): void {
23
- program
24
- .command("validate <manifest-path>")
25
- .description("Validate a Circuit manifest")
26
- .action(async (manifestPath: string) => {
27
- const spinner = ora("Validating Circuit manifest...").start();
28
-
29
- try {
30
- const absolutePath = path.resolve(process.cwd(), manifestPath);
31
- const content = await readFile(absolutePath, { encoding: "utf8" });
32
- const manifest = JSON.parse(content);
33
-
34
- const result = CircuitSchema.safeParse(manifest);
35
-
36
- if (!result.success) {
37
- spinner.fail("Validation failed");
38
- console.error(chalk.red("\nValidation errors:"));
39
- for (const issue of result.error.issues) {
40
- console.error(` - ${chalk.red(issue.path.join("."))}: ${issue.message}`);
41
- }
42
- process.exit(1);
43
- }
44
-
45
- spinner.succeed("Circuit manifest is valid");
46
- const circuit = result.data;
47
- console.log(chalk.gray(`\nCircuit ID: ${circuit.id}`));
48
- console.log(chalk.gray(`Name: ${circuit.name}`));
49
- console.log(chalk.gray(`Kits: ${circuit.kits.length}`));
50
- console.log(chalk.gray(`Boards: ${circuit.boards.length}`));
51
- console.log(chalk.gray(`Capabilities: ${circuit.capabilities.length}`));
52
- } catch (error) {
53
- spinner.fail(`Failed to validate: ${error}`);
54
- process.exit(1);
55
- }
56
- });
57
- }
58
-
59
- // ============================================================================
60
- // Command: circuit info
61
- // ============================================================================
62
-
63
- function setupInfoCommand(program: Command): void {
64
- program
65
- .command("info <manifest-path>")
66
- .description("Show detailed information about a Circuit")
67
- .action(async (manifestPath: string) => {
68
- const spinner = ora("Loading Circuit info...").start();
69
-
70
- try {
71
- const absolutePath = path.resolve(process.cwd(), manifestPath);
72
- const content = await readFile(absolutePath, { encoding: "utf8" });
73
- const manifest = JSON.parse(content);
74
-
75
- const result = CircuitSchema.safeParse(manifest);
76
- if (!result.success) {
77
- spinner.fail("Invalid Circuit manifest");
78
- process.exit(1);
79
- }
80
-
81
- const circuit = result.data;
82
- spinner.succeed(`Circuit: ${circuit.id}`);
83
-
84
- console.log(chalk.bold("\nCircuit Information"));
85
- console.log(chalk.gray("─".repeat(40)));
86
- console.log(chalk.gray(`ID: ${circuit.id}`));
87
- console.log(chalk.gray(`Name: ${circuit.name}`));
88
- console.log(chalk.gray(`Summary: ${circuit.summary}`));
89
- console.log(chalk.gray(`Version: ${circuit.version}`));
90
- console.log(chalk.gray(`Status: ${circuit.status}`));
91
- console.log(chalk.gray(`License: ${circuit.license}`));
92
-
93
- if (circuit.verticals.length > 0) {
94
- console.log(chalk.gray(`Verticals: ${circuit.verticals.join(", ")}`));
95
- }
96
-
97
- if (circuit.tags.length > 0) {
98
- console.log(chalk.gray(`Tags: ${circuit.tags.join(", ")}`));
99
- }
100
-
101
- if (circuit.kits.length > 0) {
102
- console.log(chalk.bold("\nKits"));
103
- console.log(chalk.gray("─".repeat(40)));
104
- for (const kitRef of circuit.kits) {
105
- console.log(chalk.gray(` - ${kitRef.kitId}@${kitRef.version}`));
106
- }
107
- }
108
-
109
- if (circuit.boards.length > 0) {
110
- console.log(chalk.bold("\nBoards"));
111
- console.log(chalk.gray("─".repeat(40)));
112
- for (const board of circuit.boards) {
113
- console.log(chalk.gray(` - ${board.id}: ${board.name}`));
114
- console.log(chalk.gray(` Columns: ${board.columns.map((c: { id: string; name: string }) => c.name).join(", ")}`));
115
- }
116
- }
117
-
118
- if (circuit.capabilities.length > 0) {
119
- console.log(chalk.bold("\nCapabilities"));
120
- console.log(chalk.gray("─".repeat(40)));
121
- for (const capRef of circuit.capabilities) {
122
- console.log(chalk.gray(` - ${capRef.blockId}@${capRef.version}`));
123
- }
124
- }
125
-
126
- if (circuit.repository) {
127
- console.log(chalk.bold("\nRepository"));
128
- console.log(chalk.gray("─".repeat(40)));
129
- console.log(chalk.gray(circuit.repository));
130
- }
131
- } catch (error) {
132
- spinner.fail(`Failed to show info: ${error}`);
133
- process.exit(1);
134
- }
135
- });
136
- }
137
-
138
- // ============================================================================
139
- // Command: circuit publish
140
- // ============================================================================
141
-
142
- function setupPublishCommand(program: Command): void {
143
- program
144
- .command("publish")
145
- .description("Publish a Circuit to registry (simulated)")
146
- .option("--dry-run", "Simulate publish without actually publishing")
147
- .action(async (options) => {
148
- const { dryRun } = options;
149
- const spinner = ora(dryRun ? "Simulating publish..." : "Publishing Circuit...").start();
150
-
151
- try {
152
- // Simulate publish
153
- await new Promise((resolve) => setTimeout(resolve, 1000));
154
-
155
- if (dryRun) {
156
- spinner.succeed("Dry run: Circuit would be published");
157
- } else {
158
- spinner.succeed("Circuit published successfully");
159
- console.log(chalk.gray("\nNote: Actual publishing requires registry authentication."));
160
- }
161
- } catch (error) {
162
- spinner.fail(`Failed to publish: ${error}`);
163
- process.exit(1);
164
- }
165
- });
166
- }
167
-
168
- // ============================================================================
169
- // Register Commands
170
- // ============================================================================
171
-
172
- /** Register all circuit commands with the program */
173
- export function registerCircuitCommands(program: Command): void {
174
- const circuitProgram = program
175
- .command("circuit")
176
- .description("Circuit commands")
177
- .alias("c");
178
-
179
- setupValidateCommand(circuitProgram);
180
- setupInfoCommand(circuitProgram);
181
- setupPublishCommand(circuitProgram);
182
- }
@@ -1,373 +0,0 @@
1
- /**
2
- * BLKC CLI - Deploy Commands
3
- *
4
- * Commands for deploying BLKC content to BCM.
5
- * This is the CLI half of the deploy functionality.
6
- *
7
- * @package @blkc-code/cli
8
- * @license Apache-2.0
9
- * @author Black Code Enterprises LLC
10
- */
11
-
12
- import { Command } from "commander";
13
- import chalk from "chalk";
14
- import ora from "ora";
15
- import { readFile, writeFile, stat } from "node:fs/promises";
16
- import path from "node:path";
17
- import { safeParse } from "@blkc-code/core";
18
- import { z } from "zod";
19
-
20
- // ============================================================================
21
- // Types
22
- // ============================================================================
23
-
24
- /** Deploy configuration schema */
25
- const DeployConfigSchema = z.object({
26
- apiUrl: z.string().url().default("https://api.blackcodemarket.com"),
27
- authToken: z.string().optional(),
28
- projectId: z.string().optional(),
29
- siteId: z.string().optional(),
30
- environment: z.enum(["development", "staging", "production"]).default("development"),
31
- outputDir: z.string().default("dist"),
32
- cleanOutputDir: z.boolean().default(true),
33
- minify: z.boolean().default(true),
34
- });
35
-
36
- type DeployConfig = z.infer<typeof DeployConfigSchema>;
37
-
38
- // ============================================================================
39
- // Configuration Management
40
- // ============================================================================
41
-
42
- const CONFIG_FILE_NAME = "blkc.deploy.json";
43
-
44
- /** Get the deploy config file path */
45
- function getConfigPath(cwd?: string): string {
46
- return path.resolve(cwd || process.cwd(), CONFIG_FILE_NAME);
47
- }
48
-
49
- /** Load deploy configuration */
50
- async function loadConfig(cwd?: string): Promise<DeployConfig> {
51
- const configPath = getConfigPath(cwd);
52
-
53
- try {
54
- const content = await readFile(configPath, { encoding: "utf8" });
55
- return safeParse(DeployConfigSchema, JSON.parse(content)) as DeployConfig;
56
- } catch {
57
- // Return default config if file doesn't exist
58
- return DeployConfigSchema.parse({}) as DeployConfig;
59
- }
60
- }
61
-
62
- /** Save deploy configuration */
63
- async function saveConfig(config: DeployConfig, cwd?: string): Promise<void> {
64
- const configPath = getConfigPath(cwd);
65
- await writeFile(configPath, JSON.stringify(config, null, 2), { encoding: "utf8" });
66
- }
67
-
68
- /** Check if config file exists */
69
- async function configExists(cwd?: string): Promise<boolean> {
70
- const configPath = getConfigPath(cwd);
71
- try {
72
- await stat(configPath);
73
- return true;
74
- } catch {
75
- return false;
76
- }
77
- }
78
-
79
- // ============================================================================
80
- // Command: deploy init
81
- // ============================================================================
82
-
83
- function setupInitCommand(program: Command): void {
84
- program
85
- .command("init")
86
- .description("Initialize deployment configuration")
87
- .option("--project-id <id>", "BCM project ID")
88
- .option("--site-id <id>", "BCM site ID")
89
- .option("--api-url <url>", "BCM API URL", "https://api.blackcodemarket.com")
90
- .option("--environment <env>", "Environment", "development")
91
- .option("-y, --yes", "Skip prompts and use defaults")
92
- .action(async (options) => {
93
- const spinner = ora("Initializing BLKC deploy configuration...").start();
94
-
95
- try {
96
- // Check if config already exists
97
- const exists = await configExists();
98
- if (exists && !options.yes) {
99
- spinner.fail("Deploy configuration already exists. Use --yes to overwrite.");
100
- return;
101
- }
102
-
103
- // Create config
104
- const config: DeployConfig = {
105
- apiUrl: options.apiUrl,
106
- projectId: options.projectId,
107
- siteId: options.siteId,
108
- environment: options.environment as DeployConfig["environment"],
109
- outputDir: "dist",
110
- cleanOutputDir: true,
111
- minify: true,
112
- };
113
-
114
- await saveConfig(config);
115
-
116
- spinner.succeed(`Deploy configuration initialized in ${CONFIG_FILE_NAME}`);
117
- console.log(chalk.gray(`API URL: ${config.apiUrl}`));
118
- console.log(chalk.gray(`Environment: ${config.environment}`));
119
- if (config.projectId) {
120
- console.log(chalk.gray(`Project ID: ${config.projectId}`));
121
- }
122
- if (config.siteId) {
123
- console.log(chalk.gray(`Site ID: ${config.siteId}`));
124
- }
125
- } catch (error) {
126
- spinner.fail(`Failed to initialize configuration: ${error}`);
127
- process.exit(1);
128
- }
129
- });
130
- }
131
-
132
- // ============================================================================
133
- // Command: deploy preview
134
- // ============================================================================
135
-
136
- function setupPreviewCommand(program: Command): void {
137
- program
138
- .command("preview")
139
- .description("Preview site before deployment")
140
- .option("--port <port>", "Port to serve preview", "3000")
141
- .option("--output-dir <dir>", "Output directory", "dist")
142
- .action(async (options) => {
143
- const spinner = ora("Starting preview server...").start();
144
-
145
- try {
146
- const { port, outputDir } = options;
147
- const outputPath = path.resolve(process.cwd(), outputDir);
148
-
149
- // Check if output directory exists
150
- try {
151
- await stat(outputPath);
152
- } catch {
153
- spinner.fail(`Output directory '${outputDir}' does not exist. Run 'npm run build' first.`);
154
- return;
155
- }
156
-
157
- // In a real implementation, we would start a server here
158
- // For now, just show a success message
159
- spinner.succeed(`Preview server would start on http://localhost:${port}`);
160
- console.log(chalk.yellow("Note: Preview server implementation requires a static file server."));
161
- console.log(chalk.gray(`Output directory: ${outputPath}`));
162
- console.log(chalk.gray(`To serve locally: npx serve -p ${port} ${outputPath}`));
163
- } catch (error) {
164
- spinner.fail(`Failed to start preview: ${error}`);
165
- process.exit(1);
166
- }
167
- });
168
- }
169
-
170
- // ============================================================================
171
- // Command: deploy push
172
- // ============================================================================
173
-
174
- function setupPushCommand(program: Command): void {
175
- program
176
- .command("push")
177
- .description("Deploy site to BCM")
178
- .option("--message <message>", "Deploy message")
179
- .option("--dry-run", "Simulate deployment without actually deploying")
180
- .action(async (options) => {
181
- const { message, dryRun } = options;
182
- const spinner = ora(dryRun ? "Simulating deployment..." : "Deploying to BCM...").start();
183
-
184
- try {
185
- // Load config
186
- const config = await loadConfig();
187
-
188
- // Validate required fields
189
- if (!config.projectId) {
190
- spinner.fail("Project ID is required. Use 'blkc deploy init --project-id <id>' to set it.");
191
- return;
192
- }
193
-
194
- if (!config.siteId) {
195
- spinner.fail("Site ID is required. Use 'blkc deploy init --site-id <id>' to set it.");
196
- return;
197
- }
198
-
199
- if (!config.authToken) {
200
- spinner.fail("Auth token is required. Use 'blkc auth login' or set BCM_AUTH_TOKEN environment variable.");
201
- return;
202
- }
203
-
204
- // Check output directory
205
- const outputPath = path.resolve(process.cwd(), config.outputDir);
206
- try {
207
- await stat(outputPath);
208
- } catch {
209
- spinner.fail(`Output directory '${config.outputDir}' does not exist. Run 'npm run build' first.`);
210
- return;
211
- }
212
-
213
- // Simulate deployment
214
- if (dryRun) {
215
- await new Promise((resolve) => setTimeout(resolve, 1000));
216
- spinner.succeed("Dry run: Deployment would be successful");
217
- console.log(chalk.gray(`Project: ${config.projectId}`));
218
- console.log(chalk.gray(`Site: ${config.siteId}`));
219
- console.log(chalk.gray(`Environment: ${config.environment}`));
220
- console.log(chalk.gray(`Message: ${message || "No message"}`));
221
- return;
222
- }
223
-
224
- // In a real implementation, we would:
225
- // 1. Create a deploy manifest
226
- // 2. Upload files to BCM
227
- // 3. Trigger deployment via API
228
- // For now, simulate with a delay
229
- await new Promise((resolve) => setTimeout(resolve, 2000));
230
-
231
- const deployId = `deploy_${Date.now()}`;
232
- spinner.succeed(`Deployed successfully!`);
233
- console.log(chalk.green(`Deploy ID: ${deployId}`));
234
- console.log(chalk.gray(`Project: ${config.projectId}`));
235
- console.log(chalk.gray(`Site: ${config.siteId}`));
236
- console.log(chalk.gray(`Environment: ${config.environment}`));
237
- console.log(chalk.gray(`URL: https://${config.siteId}.blackcodemarket.com`));
238
- } catch (error) {
239
- spinner.fail(`Failed to deploy: ${error}`);
240
- process.exit(1);
241
- }
242
- });
243
- }
244
-
245
- // ============================================================================
246
- // Command: deploy status
247
- // ============================================================================
248
-
249
- function setupStatusCommand(program: Command): void {
250
- program
251
- .command("status")
252
- .description("Check deployment status")
253
- .option("--deploy-id <id>", "Specific deploy ID to check")
254
- .option("--limit <n>", "Number of recent deploys to show", "10")
255
- .action(async (options) => {
256
- const { deployId, limit } = options;
257
- const spinner = ora("Checking deployment status...").start();
258
-
259
- try {
260
- const config = await loadConfig();
261
-
262
- if (!config.projectId) {
263
- spinner.fail("Project ID is required.");
264
- return;
265
- }
266
-
267
- if (!config.authToken) {
268
- spinner.fail("Auth token is required.");
269
- return;
270
- }
271
-
272
- // Simulate status check
273
- await new Promise((resolve) => setTimeout(resolve, 1000));
274
-
275
- if (deployId) {
276
- // Check specific deploy
277
- spinner.succeed(`Deploy ${deployId}`);
278
- console.log(chalk.gray("Status: " + chalk.green("Success")));
279
- console.log(chalk.gray(`URL: https://${config.siteId}.blackcodemarket.com`));
280
- } else {
281
- // List recent deploys
282
- spinner.succeed(`Recent deploys for ${config.projectId}`);
283
- console.log(chalk.gray("\nLast 3 deploys:"));
284
-
285
- for (let i = 0; i < Math.min(3, Number(limit)); i++) {
286
- const fakeDeployId = `deploy_${Date.now() - i * 1000}`;
287
- const status = i === 0 ? "Success" : i === 1 ? "Success" : "Failed";
288
- const color = status === "Success" ? chalk.green : chalk.red;
289
- console.log(` ${i + 1}. ${fakeDeployId} - ${color(status)} - 2026-11-02T12:00:00Z`);
290
- }
291
-
292
- console.log(chalk.gray("\nUse --deploy-id to check a specific deploy."));
293
- }
294
- } catch (error) {
295
- spinner.fail(`Failed to check status: ${error}`);
296
- process.exit(1);
297
- }
298
- });
299
- }
300
-
301
- // ============================================================================
302
- // Command: deploy logs
303
- // ============================================================================
304
-
305
- function setupLogsCommand(program: Command): void {
306
- program
307
- .command("logs")
308
- .description("View deployment logs")
309
- .option("--deploy-id <id>", "Deploy ID to view logs for")
310
- .option("--tail <n>", "Show last N lines", "100")
311
- .option("--follow, -f", "Follow logs in real-time")
312
- .action(async (options) => {
313
- const { deployId, tail, follow } = options;
314
-
315
- try {
316
- const config = await loadConfig();
317
-
318
- if (!config.projectId) {
319
- console.error(chalk.red("Project ID is required."));
320
- process.exit(1);
321
- }
322
-
323
- if (!config.authToken) {
324
- console.error(chalk.red("Auth token is required."));
325
- process.exit(1);
326
- }
327
-
328
- if (!deployId) {
329
- console.error(chalk.red("Deploy ID is required. Use --deploy-id."));
330
- process.exit(1);
331
- }
332
-
333
- if (follow) {
334
- console.log(chalk.yellow(`Following logs for deploy ${deployId}... (Ctrl+C to stop)`));
335
- // In a real implementation, we would stream logs here
336
- // For now, just simulate
337
- for (let i = 0; i < 5; i++) {
338
- await new Promise((resolve) => setTimeout(resolve, 1000));
339
- console.log(`[2026-11-02T12:00:${String(i).padStart(2, "0")}] Building...`);
340
- }
341
- console.log(chalk.green(`[2026-11-02T12:00:05] Deploy complete!`));
342
- } else {
343
- console.log(chalk.gray(`Showing last ${tail} lines for deploy ${deployId}`));
344
- console.log(chalk.gray("\n[2026-11-02T12:00:00] Starting deployment..."));
345
- console.log(chalk.gray("[2026-11-02T12:00:01] Building static files..."));
346
- console.log(chalk.gray("[2026-11-02T12:00:02] Uploading to BCM..."));
347
- console.log(chalk.gray("[2026-11-02T12:00:03] Deploying..."));
348
- console.log(chalk.gray("[2026-11-02T12:00:04] Successfully deployed!"));
349
- }
350
- } catch (error) {
351
- console.error(chalk.red(`Failed to view logs: ${error}`));
352
- process.exit(1);
353
- }
354
- });
355
- }
356
-
357
- // ============================================================================
358
- // Register Commands
359
- // ============================================================================
360
-
361
- /** Register all deploy commands with the program */
362
- export function registerDeployCommands(program: Command): void {
363
- const deployProgram = program
364
- .command("deploy")
365
- .description("Deployment commands")
366
- .alias("d");
367
-
368
- setupInitCommand(deployProgram);
369
- setupPreviewCommand(deployProgram);
370
- setupPushCommand(deployProgram);
371
- setupStatusCommand(deployProgram);
372
- setupLogsCommand(deployProgram);
373
- }