@otl-core/cli 1.0.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.
package/dist/index.js ADDED
@@ -0,0 +1,638 @@
1
+ #!/usr/bin/env node
2
+ #!/usr/bin/env node
3
+
4
+ // src/index.ts
5
+ import { Command } from "commander";
6
+
7
+ // src/commands/add.ts
8
+ import * as fs4 from "fs";
9
+ import * as path3 from "path";
10
+
11
+ // src/api/client.ts
12
+ function buildUrl(config, path5) {
13
+ const base = config.apiUrl.replace(/\/+$/, "");
14
+ return `${base}/api/v1/public/deployments/${encodeURIComponent(config.deploymentId)}${path5}`;
15
+ }
16
+ function authHeaders(config) {
17
+ return {
18
+ Authorization: `Bearer ${config.accessToken}`,
19
+ Accept: "application/json"
20
+ };
21
+ }
22
+ async function fetchJson(url, headers) {
23
+ const response = await fetch(url, { headers });
24
+ if (!response.ok) {
25
+ const body = await response.text().catch(() => "");
26
+ throw new Error(
27
+ `API request failed (${response.status} ${response.statusText}): ${body}`
28
+ );
29
+ }
30
+ return response.json();
31
+ }
32
+ async function fetchBlockSchemas(config) {
33
+ const url = buildUrl(config, "/schemas/blocks");
34
+ const result = await fetchJson(
35
+ url,
36
+ authHeaders(config)
37
+ );
38
+ return result.data.schemas;
39
+ }
40
+ async function findBlockSchemaByType(config, typeName) {
41
+ const schemas = await fetchBlockSchemas(config);
42
+ return schemas.find((s) => s.type === typeName);
43
+ }
44
+ async function fetchSectionSchemas(config) {
45
+ const url = buildUrl(config, "/schemas/sections");
46
+ const result = await fetchJson(
47
+ url,
48
+ authHeaders(config)
49
+ );
50
+ return result.data;
51
+ }
52
+ async function findSectionSchemaByType(config, typeName) {
53
+ const schemas = await fetchSectionSchemas(config);
54
+ return schemas.find((s) => s.type === typeName);
55
+ }
56
+
57
+ // src/utils/naming.ts
58
+ function kebabToPascal(kebab) {
59
+ return kebab.split("-").map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join("");
60
+ }
61
+ function fieldIdToCamel(id) {
62
+ return id.replace(/[-_]([a-z])/g, (_, char) => char.toUpperCase());
63
+ }
64
+
65
+ // src/codegen/type-mapper.ts
66
+ function createEmptyImports() {
67
+ return {
68
+ ColorReference: false,
69
+ MediaReference: false,
70
+ ResponsiveValue: false,
71
+ BlockInstance: false,
72
+ LocalizedString: false
73
+ };
74
+ }
75
+ function buildTypeImportLine(imports) {
76
+ const needed = [];
77
+ if (imports.ColorReference) needed.push("ColorReference");
78
+ if (imports.MediaReference) needed.push("MediaReference");
79
+ if (imports.ResponsiveValue) needed.push("ResponsiveValue");
80
+ if (imports.BlockInstance) needed.push("BlockInstance");
81
+ if (imports.LocalizedString) needed.push("LocalizedString");
82
+ if (needed.length === 0) {
83
+ return null;
84
+ }
85
+ return `import type { ${needed.join(", ")} } from "@otl-core/cms-types";`;
86
+ }
87
+ var SIMPLE_TYPE_MAP = {
88
+ text: "string",
89
+ textarea: "string",
90
+ url: "string",
91
+ markdown: "string",
92
+ html: "string",
93
+ code: "string",
94
+ richtext: "string",
95
+ date: "string",
96
+ number: "number",
97
+ boolean: "boolean",
98
+ color: "string",
99
+ "form-selector": "string",
100
+ "form-page": "string",
101
+ json: "Record<string, unknown>",
102
+ object: "Record<string, unknown>",
103
+ array: "unknown[]",
104
+ "container-behavior": '"boxed" | "edged" | "ignore"'
105
+ };
106
+ function resolveFieldType(field, imports) {
107
+ const simple = SIMPLE_TYPE_MAP[field.type];
108
+ if (simple !== void 0) {
109
+ return simple;
110
+ }
111
+ switch (field.type) {
112
+ // Select can be single or multi
113
+ case "select":
114
+ return field.multiple ? "string[]" : "string";
115
+ // Theme color types
116
+ case "theme-color":
117
+ case "theme-background-color":
118
+ case "theme-foreground-color":
119
+ imports.ColorReference = true;
120
+ return "ColorReference";
121
+ // Image / media
122
+ case "image":
123
+ imports.MediaReference = true;
124
+ return "MediaReference";
125
+ // Responsive value types
126
+ case "spacing":
127
+ case "css-value":
128
+ case "columns":
129
+ imports.ResponsiveValue = true;
130
+ return "ResponsiveValue<string>";
131
+ // Blocks
132
+ case "blocks":
133
+ imports.BlockInstance = true;
134
+ return "BlockInstance[]";
135
+ // Localized text
136
+ case "localized-text":
137
+ imports.LocalizedString = true;
138
+ return "LocalizedString";
139
+ // Group -- will be handled by the interface generator to produce a nested
140
+ // interface. At the type level we return the interface name.
141
+ case "group":
142
+ return "__GROUP__";
143
+ default:
144
+ return "unknown";
145
+ }
146
+ }
147
+ function generateInterface(interfaceName, fields) {
148
+ const imports = createEmptyImports();
149
+ const extraInterfaces = [];
150
+ const lines = buildInterfaceLines(
151
+ interfaceName,
152
+ fields,
153
+ imports,
154
+ extraInterfaces
155
+ );
156
+ const mainInterface = [`interface ${interfaceName} {`, ...lines, "}"].join(
157
+ "\n"
158
+ );
159
+ const declarations = [...extraInterfaces, mainInterface].join("\n\n");
160
+ return { name: interfaceName, declarations, imports };
161
+ }
162
+ function buildInterfaceLines(parentName, fields, imports, extraInterfaces) {
163
+ const lines = [];
164
+ for (const field of fields) {
165
+ const camelId = fieldIdToCamel(field.id);
166
+ let tsType = resolveFieldType(field, imports);
167
+ if (tsType === "__GROUP__" && field.fields && field.fields.length > 0) {
168
+ const nestedName = parentName + kebabToPascal(field.id);
169
+ const nestedLines = buildInterfaceLines(
170
+ nestedName,
171
+ field.fields,
172
+ imports,
173
+ extraInterfaces
174
+ );
175
+ const nestedDecl = [
176
+ `interface ${nestedName} {`,
177
+ ...nestedLines,
178
+ "}"
179
+ ].join("\n");
180
+ extraInterfaces.push(nestedDecl);
181
+ tsType = nestedName;
182
+ } else if (tsType === "__GROUP__") {
183
+ tsType = "Record<string, unknown>";
184
+ }
185
+ if (field.description) {
186
+ lines.push(` /** ${field.description} */`);
187
+ }
188
+ lines.push(` ${camelId}?: ${tsType};`);
189
+ }
190
+ return lines;
191
+ }
192
+
193
+ // src/codegen/generate-block.ts
194
+ function generateBlockComponent(typeName, fields) {
195
+ const pascal = kebabToPascal(typeName);
196
+ const configName = `${pascal}Config`;
197
+ const componentName = `${pascal}Block`;
198
+ const result = generateInterface(configName, fields);
199
+ const importLines = [];
200
+ const typeImportLine = buildTypeImportLine(result.imports);
201
+ if (typeImportLine) {
202
+ const typesInBraces = typeImportLine.replace("import type { ", "").replace(' } from "@otl-core/cms-types";', "");
203
+ importLines.push(
204
+ `import type { BlockComponentProps, ${typesInBraces} } from "@otl-core/cms-types";`
205
+ );
206
+ } else {
207
+ importLines.push(
208
+ 'import type { BlockComponentProps } from "@otl-core/cms-types";'
209
+ );
210
+ }
211
+ const fieldIds = fields.map((f) => fieldIdToCamel(f.id)).filter((id) => id.length > 0);
212
+ const destructuring = fieldIds.length > 0 ? ` const { ${fieldIds.join(", ")} } = config;
213
+ ` : "";
214
+ const parts = [
215
+ importLines.join("\n"),
216
+ "",
217
+ result.declarations,
218
+ "",
219
+ `export function ${componentName}({ config }: BlockComponentProps<${configName}>) {`,
220
+ destructuring,
221
+ " return (",
222
+ " <div>",
223
+ ` {/* TODO: Implement ${typeName} block */}`,
224
+ " </div>",
225
+ " );",
226
+ "}",
227
+ ""
228
+ ];
229
+ return parts.join("\n");
230
+ }
231
+
232
+ // src/codegen/generate-section.ts
233
+ function generateSectionComponent(typeName, fields) {
234
+ const pascal = kebabToPascal(typeName);
235
+ const configName = `${pascal}Config`;
236
+ const componentName = `${pascal}Section`;
237
+ const result = generateInterface(configName, fields);
238
+ const importLines = [];
239
+ const typeImportLine = buildTypeImportLine(result.imports);
240
+ if (typeImportLine) {
241
+ const typesInBraces = typeImportLine.replace("import type { ", "").replace(' } from "@otl-core/cms-types";', "");
242
+ importLines.push(
243
+ `import type { SectionComponentProps, ${typesInBraces} } from "@otl-core/cms-types";`
244
+ );
245
+ } else {
246
+ importLines.push(
247
+ 'import type { SectionComponentProps } from "@otl-core/cms-types";'
248
+ );
249
+ }
250
+ const fieldIds = fields.map((f) => fieldIdToCamel(f.id)).filter((id) => id.length > 0);
251
+ const destructuring = fieldIds.length > 0 ? ` const { ${fieldIds.join(", ")} } = config;
252
+ ` : "";
253
+ const parts = [
254
+ importLines.join("\n"),
255
+ "",
256
+ result.declarations,
257
+ "",
258
+ `export function ${componentName}({ config }: SectionComponentProps<${configName}>) {`,
259
+ destructuring,
260
+ " return (",
261
+ " <div>",
262
+ ` {/* TODO: Implement ${typeName} section */}`,
263
+ " </div>",
264
+ " );",
265
+ "}",
266
+ ""
267
+ ];
268
+ return parts.join("\n");
269
+ }
270
+
271
+ // src/codegen/registry-updater.ts
272
+ import * as fs from "fs";
273
+ function addToBlockRegistry(registryPath, typeName) {
274
+ const pascal = kebabToPascal(typeName);
275
+ const componentName = `${pascal}Block`;
276
+ const importPath = `@/components/blocks/${typeName}`;
277
+ const importLine = `import { ${componentName} } from "${importPath}";`;
278
+ const registerLine = `blockRegistry.register("${typeName}", ${componentName});`;
279
+ updateRegistryFile(registryPath, importLine, registerLine);
280
+ }
281
+ function addToSectionRegistry(registryPath, typeName) {
282
+ const pascal = kebabToPascal(typeName);
283
+ const componentName = `${pascal}Section`;
284
+ const importPath = `@/components/sections/${typeName}`;
285
+ const importLine = `import { ${componentName} } from "${importPath}";`;
286
+ const registerLine = `sectionRegistry.register("${typeName}", ${componentName});`;
287
+ updateRegistryFile(registryPath, importLine, registerLine);
288
+ }
289
+ function updateRegistryFile(filePath, importLine, registerLine) {
290
+ let content = fs.readFileSync(filePath, "utf-8");
291
+ if (content.includes(registerLine)) {
292
+ return;
293
+ }
294
+ const importRegex = /^import .+$/gm;
295
+ let lastImportMatch = null;
296
+ let match;
297
+ while ((match = importRegex.exec(content)) !== null) {
298
+ lastImportMatch = match;
299
+ }
300
+ if (lastImportMatch) {
301
+ const insertPos = lastImportMatch.index + lastImportMatch[0].length;
302
+ content = content.slice(0, insertPos) + "\n" + importLine + content.slice(insertPos);
303
+ } else {
304
+ content = importLine + "\n" + content;
305
+ }
306
+ const customCommentIndex = content.indexOf("/**\n * Custom ");
307
+ if (customCommentIndex !== -1) {
308
+ content = content.slice(0, customCommentIndex) + registerLine + "\n\n" + content.slice(customCommentIndex);
309
+ } else {
310
+ content = content.trimEnd() + "\n" + registerLine + "\n";
311
+ }
312
+ fs.writeFileSync(filePath, content, "utf-8");
313
+ }
314
+
315
+ // src/utils/env.ts
316
+ import * as dotenv from "dotenv";
317
+ import * as fs2 from "fs";
318
+ import * as path from "path";
319
+ function loadConfig() {
320
+ const envLocalPath = path.resolve(process.cwd(), ".env.local");
321
+ let fileEnv = {};
322
+ if (fs2.existsSync(envLocalPath)) {
323
+ const parsed = dotenv.parse(fs2.readFileSync(envLocalPath, "utf-8"));
324
+ fileEnv = parsed;
325
+ }
326
+ const deploymentId = fileEnv["DEPLOYMENT_ID"] || process.env["DEPLOYMENT_ID"] || "";
327
+ const accessToken = fileEnv["DEPLOYMENT_ACCESS_TOKEN"] || process.env["DEPLOYMENT_ACCESS_TOKEN"] || "";
328
+ const apiUrl = fileEnv["API_URL"] || process.env["API_URL"] || "http://localhost:8080";
329
+ if (!deploymentId || !accessToken) {
330
+ console.error(
331
+ "Error: Missing DEPLOYMENT_ID and/or DEPLOYMENT_ACCESS_TOKEN."
332
+ );
333
+ console.error(
334
+ "Run 'npx @otl-core/cli init' to configure your environment."
335
+ );
336
+ process.exit(1);
337
+ }
338
+ return { deploymentId, accessToken, apiUrl };
339
+ }
340
+
341
+ // src/utils/project.ts
342
+ import * as fs3 from "fs";
343
+ import * as path2 from "path";
344
+ var ENGINE_MARKERS = [
345
+ "@otl-core/block-registry",
346
+ "@otl-core/section-registry"
347
+ ];
348
+ function readPackageJson(dir) {
349
+ const filePath = path2.join(dir, "package.json");
350
+ if (!fs3.existsSync(filePath)) {
351
+ return null;
352
+ }
353
+ try {
354
+ const raw = fs3.readFileSync(filePath, "utf-8");
355
+ return JSON.parse(raw);
356
+ } catch {
357
+ return null;
358
+ }
359
+ }
360
+ function isEngineProject(dir) {
361
+ const pkg = readPackageJson(dir);
362
+ if (!pkg) {
363
+ return false;
364
+ }
365
+ const allDeps = {
366
+ ...pkg.dependencies,
367
+ ...pkg.devDependencies
368
+ };
369
+ return ENGINE_MARKERS.some((marker) => marker in allDeps);
370
+ }
371
+ function requireEngineProject() {
372
+ let current = process.cwd();
373
+ for (let i = 0; i < 10; i++) {
374
+ if (isEngineProject(current)) {
375
+ return {
376
+ root: current,
377
+ blocksDir: path2.join(current, "src", "components", "blocks"),
378
+ sectionsDir: path2.join(current, "src", "components", "sections"),
379
+ blockRegistryFile: path2.join(
380
+ current,
381
+ "src",
382
+ "lib",
383
+ "registries",
384
+ "block-registry.ts"
385
+ ),
386
+ sectionRegistryFile: path2.join(
387
+ current,
388
+ "src",
389
+ "lib",
390
+ "registries",
391
+ "section-registry.ts"
392
+ )
393
+ };
394
+ }
395
+ const parent = path2.dirname(current);
396
+ if (parent === current) {
397
+ break;
398
+ }
399
+ current = parent;
400
+ }
401
+ console.error("Error: Not in a OTL engine project.");
402
+ console.error("Could not find a package.json with @otl-core/ dependencies.");
403
+ console.error("Run this command from your engine project root.");
404
+ process.exit(1);
405
+ }
406
+
407
+ // src/commands/add.ts
408
+ function registerAddCommands(program2) {
409
+ const add = program2.command("add").description("Generate a component from a schema definition");
410
+ add.command("block <name>").description("Generate a block component from its schema").option("--force", "Overwrite existing files").action(async (name, opts) => {
411
+ try {
412
+ const paths = requireEngineProject();
413
+ const config = loadConfig();
414
+ const schema = await findBlockSchemaByType(config, name);
415
+ if (!schema) {
416
+ console.error(`Error: No block schema found with type "${name}".`);
417
+ console.error(
418
+ "Run 'npx @otl-core/cli list blocks' to see available schemas."
419
+ );
420
+ process.exit(1);
421
+ }
422
+ const outputFile = path3.join(paths.blocksDir, `${name}.tsx`);
423
+ if (fs4.existsSync(outputFile) && !opts.force) {
424
+ console.error(
425
+ `Error: ${path3.relative(paths.root, outputFile)} already exists.`
426
+ );
427
+ console.error("Use --force to overwrite.");
428
+ process.exit(1);
429
+ }
430
+ const content = generateBlockComponent(name, schema.fields);
431
+ fs4.mkdirSync(path3.dirname(outputFile), { recursive: true });
432
+ fs4.writeFileSync(outputFile, content, "utf-8");
433
+ const relPath = path3.relative(paths.root, outputFile);
434
+ console.log(`Created ${relPath}`);
435
+ if (fs4.existsSync(paths.blockRegistryFile)) {
436
+ addToBlockRegistry(paths.blockRegistryFile, name);
437
+ console.log(
438
+ `Updated ${path3.relative(paths.root, paths.blockRegistryFile)}`
439
+ );
440
+ }
441
+ console.log("");
442
+ console.log(`Next: implement your component in ${relPath}`);
443
+ } catch (err) {
444
+ if (err instanceof Error) {
445
+ console.error(`Error: ${err.message}`);
446
+ }
447
+ process.exit(1);
448
+ }
449
+ });
450
+ add.command("section <name>").description("Generate a section component from its schema").option("--force", "Overwrite existing files").action(async (name, opts) => {
451
+ try {
452
+ const paths = requireEngineProject();
453
+ const config = loadConfig();
454
+ const schema = await findSectionSchemaByType(config, name);
455
+ if (!schema) {
456
+ console.error(`Error: No section schema found with type "${name}".`);
457
+ console.error(
458
+ "Run 'npx @otl-core/cli list sections' to see available schemas."
459
+ );
460
+ process.exit(1);
461
+ }
462
+ const outputFile = path3.join(paths.sectionsDir, `${name}.tsx`);
463
+ if (fs4.existsSync(outputFile) && !opts.force) {
464
+ console.error(
465
+ `Error: ${path3.relative(paths.root, outputFile)} already exists.`
466
+ );
467
+ console.error("Use --force to overwrite.");
468
+ process.exit(1);
469
+ }
470
+ const content = generateSectionComponent(name, schema.fields);
471
+ fs4.mkdirSync(path3.dirname(outputFile), { recursive: true });
472
+ fs4.writeFileSync(outputFile, content, "utf-8");
473
+ const relPath = path3.relative(paths.root, outputFile);
474
+ console.log(`Created ${relPath}`);
475
+ if (fs4.existsSync(paths.sectionRegistryFile)) {
476
+ addToSectionRegistry(paths.sectionRegistryFile, name);
477
+ console.log(
478
+ `Updated ${path3.relative(paths.root, paths.sectionRegistryFile)}`
479
+ );
480
+ }
481
+ console.log("");
482
+ console.log(`Next: implement your component in ${relPath}`);
483
+ } catch (err) {
484
+ if (err instanceof Error) {
485
+ console.error(`Error: ${err.message}`);
486
+ }
487
+ process.exit(1);
488
+ }
489
+ });
490
+ }
491
+
492
+ // src/commands/init.ts
493
+ import * as fs5 from "fs";
494
+ import * as path4 from "path";
495
+ import * as readline from "readline";
496
+ function registerInitCommand(program2) {
497
+ program2.command("init").description("Configure deployment credentials in .env.local").action(async () => {
498
+ try {
499
+ const rl = readline.createInterface({
500
+ input: process.stdin,
501
+ output: process.stdout
502
+ });
503
+ const ask = (question) => new Promise((resolve3) => {
504
+ rl.question(question, (answer) => resolve3(answer.trim()));
505
+ });
506
+ console.log("OTL CMS -- Engine Configuration");
507
+ console.log("");
508
+ const deploymentId = await ask("Deployment ID: ");
509
+ if (!deploymentId) {
510
+ console.error("Error: Deployment ID is required.");
511
+ rl.close();
512
+ process.exit(1);
513
+ }
514
+ const accessToken = await ask("Deployment Access Token: ");
515
+ if (!accessToken) {
516
+ console.error("Error: Access token is required.");
517
+ rl.close();
518
+ process.exit(1);
519
+ }
520
+ const apiUrlInput = await ask("API URL [http://localhost:8080]: ");
521
+ const apiUrl = apiUrlInput || "http://localhost:8080";
522
+ rl.close();
523
+ const envPath = path4.resolve(process.cwd(), ".env.local");
524
+ let content = "";
525
+ if (fs5.existsSync(envPath)) {
526
+ content = fs5.readFileSync(envPath, "utf-8");
527
+ content = upsertEnvVar(content, "DEPLOYMENT_ID", deploymentId);
528
+ content = upsertEnvVar(
529
+ content,
530
+ "DEPLOYMENT_ACCESS_TOKEN",
531
+ accessToken
532
+ );
533
+ content = upsertEnvVar(content, "API_URL", apiUrl);
534
+ } else {
535
+ content = [
536
+ "# OTL CMS Engine Configuration",
537
+ `DEPLOYMENT_ID=${deploymentId}`,
538
+ `DEPLOYMENT_ACCESS_TOKEN=${accessToken}`,
539
+ `API_URL=${apiUrl}`,
540
+ ""
541
+ ].join("\n");
542
+ }
543
+ fs5.writeFileSync(envPath, content, "utf-8");
544
+ console.log("");
545
+ console.log("Written to .env.local");
546
+ } catch (err) {
547
+ if (err instanceof Error) {
548
+ console.error(`Error: ${err.message}`);
549
+ }
550
+ process.exit(1);
551
+ }
552
+ });
553
+ }
554
+ function upsertEnvVar(content, key, value) {
555
+ const regex = new RegExp(`^${key}=.*$`, "m");
556
+ if (regex.test(content)) {
557
+ return content.replace(regex, `${key}=${value}`);
558
+ }
559
+ const base = content.endsWith("\n") ? content : content + "\n";
560
+ return base + `${key}=${value}
561
+ `;
562
+ }
563
+
564
+ // src/commands/list.ts
565
+ function registerListCommands(program2) {
566
+ const list = program2.command("list").description("List available schemas for the deployment");
567
+ list.command("blocks").description("List all block schemas").action(async () => {
568
+ try {
569
+ const config = loadConfig();
570
+ const schemas = await fetchBlockSchemas(config);
571
+ if (schemas.length === 0) {
572
+ console.log("No block schemas found for this deployment.");
573
+ return;
574
+ }
575
+ const typeCol = 24;
576
+ const nameCol = 28;
577
+ const fieldsCol = 8;
578
+ console.log(
579
+ padRight("TYPE", typeCol) + padRight("NAME", nameCol) + padRight("FIELDS", fieldsCol)
580
+ );
581
+ console.log("-".repeat(typeCol + nameCol + fieldsCol));
582
+ for (const schema of schemas) {
583
+ const fieldCount = schema.fields ? schema.fields.length : 0;
584
+ console.log(
585
+ padRight(schema.type, typeCol) + padRight(schema.name, nameCol) + String(fieldCount)
586
+ );
587
+ }
588
+ } catch (err) {
589
+ if (err instanceof Error) {
590
+ console.error(`Error: ${err.message}`);
591
+ }
592
+ process.exit(1);
593
+ }
594
+ });
595
+ list.command("sections").description("List all section schemas").action(async () => {
596
+ try {
597
+ const config = loadConfig();
598
+ const schemas = await fetchSectionSchemas(config);
599
+ if (schemas.length === 0) {
600
+ console.log("No section schemas found for this deployment.");
601
+ return;
602
+ }
603
+ const typeCol = 24;
604
+ const nameCol = 28;
605
+ const fieldsCol = 8;
606
+ console.log(
607
+ padRight("TYPE", typeCol) + padRight("NAME", nameCol) + padRight("FIELDS", fieldsCol)
608
+ );
609
+ console.log("-".repeat(typeCol + nameCol + fieldsCol));
610
+ for (const schema of schemas) {
611
+ const fieldCount = schema.fields ? schema.fields.length : 0;
612
+ console.log(
613
+ padRight(schema.type, typeCol) + padRight(schema.name, nameCol) + String(fieldCount)
614
+ );
615
+ }
616
+ } catch (err) {
617
+ if (err instanceof Error) {
618
+ console.error(`Error: ${err.message}`);
619
+ }
620
+ process.exit(1);
621
+ }
622
+ });
623
+ }
624
+ function padRight(str, width) {
625
+ if (str.length >= width) {
626
+ return str + " ";
627
+ }
628
+ return str + " ".repeat(width - str.length);
629
+ }
630
+
631
+ // src/index.ts
632
+ var program = new Command();
633
+ program.name("otl-cli").description("OTL CMS CLI -- scaffold and manage engine components").version("0.1.0");
634
+ registerAddCommands(program);
635
+ registerListCommands(program);
636
+ registerInitCommand(program);
637
+ program.parse();
638
+ //# sourceMappingURL=index.js.map