@objectstack/cli 2.0.6 → 3.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 CHANGED
@@ -1,237 +1,26 @@
1
+ import {
2
+ collectMetadataStats,
3
+ createTimer,
4
+ formatZodErrors,
5
+ loadConfig,
6
+ printError,
7
+ printHeader,
8
+ printInfo,
9
+ printKV,
10
+ printMetadataStats,
11
+ printServerReady,
12
+ printStep,
13
+ printSuccess,
14
+ printWarning,
15
+ resolveConfigPath
16
+ } from "./chunk-CSHQEILI.js";
17
+
1
18
  // src/commands/compile.ts
2
19
  import { Command } from "commander";
3
- import path2 from "path";
4
- import fs2 from "fs";
5
- import chalk3 from "chalk";
6
- import { ObjectStackDefinitionSchema } from "@objectstack/spec";
7
-
8
- // src/utils/config.ts
9
20
  import path from "path";
10
21
  import fs from "fs";
11
- import chalk2 from "chalk";
12
- import { bundleRequire } from "bundle-require";
13
-
14
- // src/utils/format.ts
15
22
  import chalk from "chalk";
16
- function printHeader(title) {
17
- console.log(chalk.bold(`
18
- \u25C6 ${title}`));
19
- console.log(chalk.dim("\u2500".repeat(40)));
20
- }
21
- function printKV(key, value, icon) {
22
- const prefix = icon ? `${icon} ` : " ";
23
- console.log(`${prefix}${chalk.dim(key + ":")} ${chalk.white(String(value))}`);
24
- }
25
- function printSuccess(msg) {
26
- console.log(chalk.green(` \u2713 ${msg}`));
27
- }
28
- function printWarning(msg) {
29
- console.log(chalk.yellow(` \u26A0 ${msg}`));
30
- }
31
- function printError(msg) {
32
- console.log(chalk.red(` \u2717 ${msg}`));
33
- }
34
- function printInfo(msg) {
35
- console.log(chalk.blue(` \u2139 ${msg}`));
36
- }
37
- function printStep(msg) {
38
- console.log(chalk.yellow(` \u2192 ${msg}`));
39
- }
40
- function createTimer() {
41
- const start = Date.now();
42
- return {
43
- elapsed: () => Date.now() - start,
44
- display: () => `${Date.now() - start}ms`
45
- };
46
- }
47
- function formatZodErrors(error) {
48
- const issues = error.issues || error.errors || [];
49
- if (issues.length === 0) {
50
- console.log(chalk.red(" Unknown validation error"));
51
- return;
52
- }
53
- const grouped = /* @__PURE__ */ new Map();
54
- for (const issue of issues) {
55
- const topPath = issue.path?.[0] || "_root";
56
- if (!grouped.has(String(topPath))) {
57
- grouped.set(String(topPath), []);
58
- }
59
- grouped.get(String(topPath)).push(issue);
60
- }
61
- for (const [section, sectionIssues] of grouped) {
62
- console.log(chalk.bold.red(`
63
- ${section}:`));
64
- for (const issue of sectionIssues) {
65
- const path11 = issue.path?.join(".") || "";
66
- const code = issue.code || "";
67
- const msg = issue.message || "";
68
- console.log(chalk.red(` \u2717 ${path11}`));
69
- console.log(chalk.dim(` ${code}: ${msg}`));
70
- if (issue.expected) {
71
- console.log(chalk.dim(` expected: ${chalk.green(issue.expected)}`));
72
- }
73
- if (issue.received) {
74
- console.log(chalk.dim(` received: ${chalk.red(issue.received)}`));
75
- }
76
- }
77
- }
78
- console.log("");
79
- console.log(chalk.dim(` ${issues.length} validation error(s) total`));
80
- }
81
- function collectMetadataStats(config) {
82
- const count = (arr) => Array.isArray(arr) ? arr.length : 0;
83
- let fields = 0;
84
- if (Array.isArray(config.objects)) {
85
- for (const obj of config.objects) {
86
- if (obj.fields && typeof obj.fields === "object") {
87
- fields += Object.keys(obj.fields).length;
88
- }
89
- }
90
- }
91
- return {
92
- objects: count(config.objects),
93
- objectExtensions: count(config.objectExtensions),
94
- fields,
95
- views: count(config.views),
96
- pages: count(config.pages),
97
- apps: count(config.apps),
98
- dashboards: count(config.dashboards),
99
- reports: count(config.reports),
100
- actions: count(config.actions),
101
- flows: count(config.flows),
102
- workflows: count(config.workflows),
103
- approvals: count(config.approvals),
104
- agents: count(config.agents),
105
- apis: count(config.apis),
106
- roles: count(config.roles),
107
- permissions: count(config.permissions),
108
- themes: count(config.themes),
109
- datasources: count(config.datasources),
110
- translations: count(config.translations),
111
- plugins: count(config.plugins),
112
- devPlugins: count(config.devPlugins)
113
- };
114
- }
115
- function printServerReady(opts) {
116
- const base = `http://localhost:${opts.port}`;
117
- console.log("");
118
- console.log(chalk.bold.green(" \u2713 Server is ready"));
119
- console.log("");
120
- console.log(chalk.cyan(" \u279C") + chalk.bold(" API: ") + chalk.cyan(base + "/"));
121
- if (opts.uiEnabled && opts.studioPath) {
122
- console.log(chalk.cyan(" \u279C") + chalk.bold(" Studio: ") + chalk.cyan(base + opts.studioPath + "/"));
123
- }
124
- console.log("");
125
- console.log(chalk.dim(` Config: ${opts.configFile}`));
126
- console.log(chalk.dim(` Mode: ${opts.isDev ? "development" : "production"}`));
127
- console.log(chalk.dim(` Plugins: ${opts.pluginCount} loaded`));
128
- if (opts.pluginNames && opts.pluginNames.length > 0) {
129
- console.log(chalk.dim(` ${opts.pluginNames.join(", ")}`));
130
- }
131
- console.log("");
132
- console.log(chalk.dim(" Press Ctrl+C to stop"));
133
- console.log("");
134
- }
135
- function printMetadataStats(stats) {
136
- const sections = [
137
- {
138
- label: "Data",
139
- items: [
140
- ["Objects", stats.objects],
141
- ["Fields", stats.fields],
142
- ["Extensions", stats.objectExtensions],
143
- ["Datasources", stats.datasources]
144
- ]
145
- },
146
- {
147
- label: "UI",
148
- items: [
149
- ["Apps", stats.apps],
150
- ["Views", stats.views],
151
- ["Pages", stats.pages],
152
- ["Dashboards", stats.dashboards],
153
- ["Reports", stats.reports],
154
- ["Actions", stats.actions],
155
- ["Themes", stats.themes]
156
- ]
157
- },
158
- {
159
- label: "Logic",
160
- items: [
161
- ["Flows", stats.flows],
162
- ["Workflows", stats.workflows],
163
- ["Approvals", stats.approvals],
164
- ["Agents", stats.agents],
165
- ["APIs", stats.apis]
166
- ]
167
- },
168
- {
169
- label: "Security",
170
- items: [
171
- ["Roles", stats.roles],
172
- ["Permissions", stats.permissions]
173
- ]
174
- }
175
- ];
176
- for (const section of sections) {
177
- const nonZero = section.items.filter(([, v]) => v > 0);
178
- if (nonZero.length === 0) continue;
179
- const line = nonZero.map(([k, v]) => `${chalk.white(v)} ${chalk.dim(k)}`).join(" ");
180
- console.log(` ${chalk.bold(section.label + ":")} ${line}`);
181
- }
182
- if (stats.plugins > 0 || stats.devPlugins > 0) {
183
- const parts = [];
184
- if (stats.plugins > 0) parts.push(`${stats.plugins} plugins`);
185
- if (stats.devPlugins > 0) parts.push(`${stats.devPlugins} devPlugins`);
186
- console.log(` ${chalk.bold("Runtime:")} ${chalk.dim(parts.join(", "))}`);
187
- }
188
- }
189
-
190
- // src/utils/config.ts
191
- function resolveConfigPath(source) {
192
- if (source) {
193
- const abs = path.resolve(process.cwd(), source);
194
- if (!fs.existsSync(abs)) {
195
- printError(`Config file not found: ${chalk2.white(abs)}`);
196
- console.log("");
197
- console.log(chalk2.dim(" Hint: Run this command from a directory with objectstack.config.ts"));
198
- console.log(chalk2.dim(" Or specify the path: objectstack <command> path/to/config.ts"));
199
- process.exit(1);
200
- }
201
- return abs;
202
- }
203
- const candidates = [
204
- "objectstack.config.ts",
205
- "objectstack.config.js",
206
- "objectstack.config.mjs"
207
- ];
208
- for (const candidate of candidates) {
209
- const abs = path.resolve(process.cwd(), candidate);
210
- if (fs.existsSync(abs)) return abs;
211
- }
212
- printError("No objectstack.config.{ts,js,mjs} found in current directory");
213
- console.log("");
214
- console.log(chalk2.dim(" Hint: Run `objectstack init` to create a new project"));
215
- process.exit(1);
216
- }
217
- async function loadConfig(source) {
218
- const absolutePath = resolveConfigPath(source);
219
- const start = Date.now();
220
- const { mod } = await bundleRequire({
221
- filepath: absolutePath
222
- });
223
- const config = mod.default || mod;
224
- if (!config) {
225
- throw new Error(`No default export found in ${path.basename(absolutePath)}`);
226
- }
227
- return {
228
- config,
229
- absolutePath,
230
- duration: Date.now() - start
231
- };
232
- }
233
-
234
- // src/commands/compile.ts
23
+ import { ObjectStackDefinitionSchema } from "@objectstack/spec";
235
24
  var compileCommand = new Command("compile").description("Compile ObjectStack configuration to JSON artifact").argument("[config]", "Source configuration file").option("-o, --output <path>", "Output JSON file", "dist/objectstack.json").option("--json", "Output compile result as JSON (for CI)").action(async (configPath, options) => {
236
25
  const timer = createTimer();
237
26
  if (!options.json) {
@@ -241,7 +30,7 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
241
30
  if (!options.json) printStep("Loading configuration...");
242
31
  const { config, absolutePath, duration } = await loadConfig(configPath);
243
32
  if (!options.json) {
244
- printKV("Config", path2.relative(process.cwd(), absolutePath));
33
+ printKV("Config", path.relative(process.cwd(), absolutePath));
245
34
  printKV("Load time", `${duration}ms`);
246
35
  }
247
36
  if (!options.json) printStep("Validating protocol compliance...");
@@ -258,13 +47,13 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
258
47
  }
259
48
  if (!options.json) printStep("Writing artifact...");
260
49
  const output = options.output;
261
- const artifactPath = path2.resolve(process.cwd(), output);
262
- const artifactDir = path2.dirname(artifactPath);
263
- if (!fs2.existsSync(artifactDir)) {
264
- fs2.mkdirSync(artifactDir, { recursive: true });
50
+ const artifactPath = path.resolve(process.cwd(), output);
51
+ const artifactDir = path.dirname(artifactPath);
52
+ if (!fs.existsSync(artifactDir)) {
53
+ fs.mkdirSync(artifactDir, { recursive: true });
265
54
  }
266
55
  const jsonContent = JSON.stringify(result.data, null, 2);
267
- fs2.writeFileSync(artifactPath, jsonContent);
56
+ fs.writeFileSync(artifactPath, jsonContent);
268
57
  const sizeKB = (jsonContent.length / 1024).toFixed(1);
269
58
  const stats = collectMetadataStats(config);
270
59
  if (options.json) {
@@ -278,11 +67,11 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
278
67
  return;
279
68
  }
280
69
  console.log("");
281
- printSuccess(`Build complete ${chalk3.dim(`(${timer.display()})`)}`);
70
+ printSuccess(`Build complete ${chalk.dim(`(${timer.display()})`)}`);
282
71
  console.log("");
283
72
  printMetadataStats(stats);
284
73
  console.log("");
285
- printKV("Artifact", `${output} ${chalk3.dim(`(${sizeKB} KB`)})`);
74
+ printKV("Artifact", `${output} ${chalk.dim(`(${sizeKB} KB`)})`);
286
75
  console.log("");
287
76
  } catch (error) {
288
77
  if (options.json) {
@@ -297,7 +86,7 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
297
86
 
298
87
  // src/commands/validate.ts
299
88
  import { Command as Command2 } from "commander";
300
- import chalk4 from "chalk";
89
+ import chalk2 from "chalk";
301
90
  import { ObjectStackDefinitionSchema as ObjectStackDefinitionSchema2 } from "@objectstack/spec";
302
91
  var validateCommand = new Command2("validate").description("Validate ObjectStack configuration against the protocol schema").argument("[config]", "Configuration file path").option("--strict", "Treat warnings as errors").option("--json", "Output results as JSON").action(async (configPath, options) => {
303
92
  const timer = createTimer();
@@ -351,12 +140,12 @@ var validateCommand = new Command2("validate").description("Validate ObjectStack
351
140
  warnings.push("Missing manifest.namespace \u2014 required for multi-app hosting");
352
141
  }
353
142
  console.log("");
354
- printSuccess(`Validation passed ${chalk4.dim(`(${timer.display()})`)}`);
143
+ printSuccess(`Validation passed ${chalk2.dim(`(${timer.display()})`)}`);
355
144
  console.log("");
356
145
  if (config.manifest) {
357
- console.log(` ${chalk4.bold(config.manifest.name || config.manifest.id || "Unnamed")} ${chalk4.dim(`v${config.manifest.version || "0.0.0"}`)}`);
146
+ console.log(` ${chalk2.bold(config.manifest.name || config.manifest.id || "Unnamed")} ${chalk2.dim(`v${config.manifest.version || "0.0.0"}`)}`);
358
147
  if (config.manifest.description) {
359
- console.log(chalk4.dim(` ${config.manifest.description}`));
148
+ console.log(chalk2.dim(` ${config.manifest.description}`));
360
149
  }
361
150
  console.log("");
362
151
  }
@@ -364,7 +153,7 @@ var validateCommand = new Command2("validate").description("Validate ObjectStack
364
153
  if (warnings.length > 0) {
365
154
  console.log("");
366
155
  for (const w of warnings) {
367
- console.log(chalk4.yellow(` \u26A0 ${w}`));
156
+ console.log(chalk2.yellow(` \u26A0 ${w}`));
368
157
  }
369
158
  if (options.strict) {
370
159
  console.log("");
@@ -390,7 +179,7 @@ var validateCommand = new Command2("validate").description("Validate ObjectStack
390
179
 
391
180
  // src/commands/info.ts
392
181
  import { Command as Command3 } from "commander";
393
- import chalk5 from "chalk";
182
+ import chalk3 from "chalk";
394
183
  var infoCommand = new Command3("info").description("Display metadata summary of an ObjectStack configuration").argument("[config]", "Configuration file path").option("--json", "Output as JSON").action(async (configPath, options) => {
395
184
  const timer = createTimer();
396
185
  if (!options.json) {
@@ -416,9 +205,9 @@ var infoCommand = new Command3("info").description("Display metadata summary of
416
205
  if (config.manifest) {
417
206
  const m = config.manifest;
418
207
  console.log("");
419
- console.log(` ${chalk5.bold(m.name || m.id || "Unnamed")} ${chalk5.dim(`v${m.version || "0.0.0"}`)}`);
420
- if (m.id) console.log(chalk5.dim(` ${m.id}`));
421
- if (m.description) console.log(chalk5.dim(` ${m.description}`));
208
+ console.log(` ${chalk3.bold(m.name || m.id || "Unnamed")} ${chalk3.dim(`v${m.version || "0.0.0"}`)}`);
209
+ if (m.id) console.log(chalk3.dim(` ${m.id}`));
210
+ if (m.description) console.log(chalk3.dim(` ${m.description}`));
422
211
  if (m.namespace) printKV(" Namespace", m.namespace);
423
212
  if (m.type) printKV(" Type", m.type);
424
213
  }
@@ -426,35 +215,35 @@ var infoCommand = new Command3("info").description("Display metadata summary of
426
215
  printMetadataStats(stats);
427
216
  if (config.objects && config.objects.length > 0) {
428
217
  console.log("");
429
- console.log(chalk5.bold(" Objects:"));
218
+ console.log(chalk3.bold(" Objects:"));
430
219
  for (const obj of config.objects) {
431
220
  const fieldCount = obj.fields ? Object.keys(obj.fields).length : 0;
432
221
  const ownership = obj.ownership || "own";
433
222
  console.log(
434
- ` ${chalk5.cyan(obj.name || "?")}` + chalk5.dim(` (${fieldCount} fields, ${ownership})`) + (obj.label ? chalk5.dim(` \u2014 ${obj.label}`) : "")
223
+ ` ${chalk3.cyan(obj.name || "?")}` + chalk3.dim(` (${fieldCount} fields, ${ownership})`) + (obj.label ? chalk3.dim(` \u2014 ${obj.label}`) : "")
435
224
  );
436
225
  }
437
226
  }
438
227
  if (config.agents && config.agents.length > 0) {
439
228
  console.log("");
440
- console.log(chalk5.bold(" Agents:"));
229
+ console.log(chalk3.bold(" Agents:"));
441
230
  for (const agent of config.agents) {
442
231
  console.log(
443
- ` ${chalk5.magenta(agent.name || "?")}` + (agent.role ? chalk5.dim(` \u2014 ${agent.role}`) : "")
232
+ ` ${chalk3.magenta(agent.name || "?")}` + (agent.role ? chalk3.dim(` \u2014 ${agent.role}`) : "")
444
233
  );
445
234
  }
446
235
  }
447
236
  if (config.apps && config.apps.length > 0) {
448
237
  console.log("");
449
- console.log(chalk5.bold(" Apps:"));
238
+ console.log(chalk3.bold(" Apps:"));
450
239
  for (const app of config.apps) {
451
240
  console.log(
452
- ` ${chalk5.green(app.name || "?")}` + (app.label ? chalk5.dim(` \u2014 ${app.label}`) : "")
241
+ ` ${chalk3.green(app.name || "?")}` + (app.label ? chalk3.dim(` \u2014 ${app.label}`) : "")
453
242
  );
454
243
  }
455
244
  }
456
245
  console.log("");
457
- console.log(chalk5.dim(` Loaded in ${duration}ms`));
246
+ console.log(chalk3.dim(` Loaded in ${duration}ms`));
458
247
  console.log("");
459
248
  } catch (error) {
460
249
  if (options.json) {
@@ -469,9 +258,9 @@ var infoCommand = new Command3("info").description("Display metadata summary of
469
258
 
470
259
  // src/commands/init.ts
471
260
  import { Command as Command4 } from "commander";
472
- import chalk6 from "chalk";
473
- import fs3 from "fs";
474
- import path3 from "path";
261
+ import chalk4 from "chalk";
262
+ import fs2 from "fs";
263
+ import path2 from "path";
475
264
  var TEMPLATES = {
476
265
  app: {
477
266
  description: "Full application with objects, views, and actions",
@@ -636,16 +425,16 @@ function toTitleCase(str) {
636
425
  var initCommand = new Command4("init").description("Initialize a new ObjectStack project in the current directory").argument("[name]", "Project name (defaults to directory name)").option("-t, --template <template>", "Template: app, plugin, empty", "app").option("--no-install", "Skip dependency installation").action(async (name, options) => {
637
426
  printHeader("Init");
638
427
  const cwd = process.cwd();
639
- const projectName = name || path3.basename(cwd);
428
+ const projectName = name || path2.basename(cwd);
640
429
  const template = TEMPLATES[options.template];
641
430
  if (!template) {
642
431
  printError(`Unknown template: ${options.template}`);
643
- console.log(chalk6.dim(` Available: ${Object.keys(TEMPLATES).join(", ")}`));
432
+ console.log(chalk4.dim(` Available: ${Object.keys(TEMPLATES).join(", ")}`));
644
433
  process.exit(1);
645
434
  }
646
- if (fs3.existsSync(path3.join(cwd, "objectstack.config.ts"))) {
435
+ if (fs2.existsSync(path2.join(cwd, "objectstack.config.ts"))) {
647
436
  printError("objectstack.config.ts already exists in this directory");
648
- console.log(chalk6.dim(" Use `objectstack generate` to add metadata to an existing project"));
437
+ console.log(chalk4.dim(" Use `objectstack generate` to add metadata to an existing project"));
649
438
  process.exit(1);
650
439
  }
651
440
  printKV("Project", projectName);
@@ -654,8 +443,8 @@ var initCommand = new Command4("init").description("Initialize a new ObjectStack
654
443
  console.log("");
655
444
  const createdFiles = [];
656
445
  try {
657
- const pkgPath = path3.join(cwd, "package.json");
658
- if (!fs3.existsSync(pkgPath)) {
446
+ const pkgPath = path2.join(cwd, "package.json");
447
+ if (!fs2.existsSync(pkgPath)) {
659
448
  const pkg = {
660
449
  name: projectName,
661
450
  version: "0.1.0",
@@ -665,16 +454,16 @@ var initCommand = new Command4("init").description("Initialize a new ObjectStack
665
454
  dependencies: template.dependencies,
666
455
  devDependencies: template.devDependencies
667
456
  };
668
- fs3.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
457
+ fs2.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
669
458
  createdFiles.push("package.json");
670
459
  } else {
671
460
  printInfo("package.json already exists, skipping");
672
461
  }
673
462
  const configContent = template.configContent(projectName);
674
- fs3.writeFileSync(path3.join(cwd, "objectstack.config.ts"), configContent);
463
+ fs2.writeFileSync(path2.join(cwd, "objectstack.config.ts"), configContent);
675
464
  createdFiles.push("objectstack.config.ts");
676
- const tsconfigPath = path3.join(cwd, "tsconfig.json");
677
- if (!fs3.existsSync(tsconfigPath)) {
465
+ const tsconfigPath = path2.join(cwd, "tsconfig.json");
466
+ if (!fs2.existsSync(tsconfigPath)) {
678
467
  const tsconfig = {
679
468
  compilerOptions: {
680
469
  target: "ES2022",
@@ -690,30 +479,30 @@ var initCommand = new Command4("init").description("Initialize a new ObjectStack
690
479
  include: ["*.ts", "src/**/*"],
691
480
  exclude: ["dist", "node_modules"]
692
481
  };
693
- fs3.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n");
482
+ fs2.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n");
694
483
  createdFiles.push("tsconfig.json");
695
484
  }
696
485
  for (const [filePath, contentFn] of Object.entries(template.srcFiles)) {
697
486
  const resolvedPath = filePath.replace("__name__", projectName);
698
- const fullPath = path3.join(cwd, resolvedPath);
699
- const dir = path3.dirname(fullPath);
700
- if (!fs3.existsSync(dir)) {
701
- fs3.mkdirSync(dir, { recursive: true });
487
+ const fullPath = path2.join(cwd, resolvedPath);
488
+ const dir = path2.dirname(fullPath);
489
+ if (!fs2.existsSync(dir)) {
490
+ fs2.mkdirSync(dir, { recursive: true });
702
491
  }
703
- fs3.writeFileSync(fullPath, contentFn(projectName));
492
+ fs2.writeFileSync(fullPath, contentFn(projectName));
704
493
  createdFiles.push(resolvedPath);
705
494
  }
706
- const gitignorePath = path3.join(cwd, ".gitignore");
707
- if (!fs3.existsSync(gitignorePath)) {
708
- fs3.writeFileSync(gitignorePath, `node_modules/
495
+ const gitignorePath = path2.join(cwd, ".gitignore");
496
+ if (!fs2.existsSync(gitignorePath)) {
497
+ fs2.writeFileSync(gitignorePath, `node_modules/
709
498
  dist/
710
499
  *.tsbuildinfo
711
500
  `);
712
501
  createdFiles.push(".gitignore");
713
502
  }
714
- console.log(chalk6.bold(" Created files:"));
503
+ console.log(chalk4.bold(" Created files:"));
715
504
  for (const f of createdFiles) {
716
- console.log(chalk6.green(` + ${f}`));
505
+ console.log(chalk4.green(` + ${f}`));
717
506
  }
718
507
  console.log("");
719
508
  if (options.install !== false) {
@@ -727,10 +516,10 @@ dist/
727
516
  }
728
517
  printSuccess("Project initialized!");
729
518
  console.log("");
730
- console.log(chalk6.bold(" Next steps:"));
731
- console.log(chalk6.dim(" objectstack validate # Check configuration"));
732
- console.log(chalk6.dim(" objectstack dev # Start development server"));
733
- console.log(chalk6.dim(" objectstack generate # Add objects, views, etc."));
519
+ console.log(chalk4.bold(" Next steps:"));
520
+ console.log(chalk4.dim(" objectstack validate # Check configuration"));
521
+ console.log(chalk4.dim(" objectstack dev # Start development server"));
522
+ console.log(chalk4.dim(" objectstack generate # Add objects, views, etc."));
734
523
  console.log("");
735
524
  } catch (error) {
736
525
  printError(error.message || String(error));
@@ -738,14 +527,14 @@ dist/
738
527
  }
739
528
  });
740
529
  function printWarning2(msg) {
741
- console.log(chalk6.yellow(` \u26A0 ${msg}`));
530
+ console.log(chalk4.yellow(` \u26A0 ${msg}`));
742
531
  }
743
532
 
744
533
  // src/commands/generate.ts
745
534
  import { Command as Command5 } from "commander";
746
- import chalk7 from "chalk";
747
- import fs4 from "fs";
748
- import path4 from "path";
535
+ import chalk5 from "chalk";
536
+ import fs3 from "fs";
537
+ import path3 from "path";
749
538
  var GENERATORS = {
750
539
  object: {
751
540
  description: "Business data object",
@@ -926,81 +715,217 @@ function toTitleCase2(str) {
926
715
  function toSnakeCase(str) {
927
716
  return str.replace(/[-]/g, "_").replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`).replace(/^_/, "");
928
717
  }
929
- var generateCommand = new Command5("generate").alias("g").description("Generate metadata files (object, view, action, flow, agent, dashboard, app)").argument("<type>", "Metadata type to generate").argument("<name>", "Name for the metadata (use kebab-case)").option("-d, --dir <directory>", "Target directory (overrides default)").option("--dry-run", "Show what would be created without writing files").action(async (type, name, options) => {
718
+ var FIELD_TYPE_MAP = {
719
+ text: "string",
720
+ textarea: "string",
721
+ richtext: "string",
722
+ html: "string",
723
+ markdown: "string",
724
+ number: "number",
725
+ integer: "number",
726
+ currency: "number",
727
+ percent: "number",
728
+ boolean: "boolean",
729
+ date: "string",
730
+ datetime: "string",
731
+ time: "string",
732
+ email: "string",
733
+ phone: "string",
734
+ url: "string",
735
+ select: "string",
736
+ multiselect: "string[]",
737
+ lookup: "string",
738
+ master_detail: "string",
739
+ formula: "unknown",
740
+ autonumber: "string",
741
+ json: "Record<string, unknown>",
742
+ file: "string",
743
+ image: "string",
744
+ password: "string",
745
+ slug: "string",
746
+ uuid: "string",
747
+ ip_address: "string",
748
+ color: "string",
749
+ rating: "number",
750
+ geo_point: "{ lat: number; lng: number }",
751
+ vector: "number[]",
752
+ encrypted: "string"
753
+ };
754
+ function fieldTypeToTs(fieldType, multiple) {
755
+ const base = FIELD_TYPE_MAP[fieldType] || "unknown";
756
+ return multiple ? `${base}[]` : base;
757
+ }
758
+ function generateTypesFromConfig(config) {
759
+ const lines = [
760
+ "// Auto-generated by ObjectStack CLI \u2014 do not edit manually",
761
+ `// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`,
762
+ "",
763
+ "import type { Data } from '@objectstack/spec';",
764
+ ""
765
+ ];
766
+ const objects = [];
767
+ const rawObjects = config.objects ?? config.data?.objects ?? {};
768
+ if (Array.isArray(rawObjects)) {
769
+ objects.push(...rawObjects);
770
+ } else if (typeof rawObjects === "object") {
771
+ for (const val of Object.values(rawObjects)) {
772
+ if (val && typeof val === "object") objects.push(val);
773
+ }
774
+ }
775
+ if (objects.length === 0) {
776
+ lines.push("// No objects found in configuration");
777
+ return lines.join("\n") + "\n";
778
+ }
779
+ for (const obj of objects) {
780
+ const name = String(obj.name || "unknown");
781
+ const typeName = name.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
782
+ const fields = obj.fields ?? {};
783
+ lines.push(`/** ${String(obj.label || typeName)} record type */`);
784
+ lines.push(`export interface ${typeName}Record {`);
785
+ lines.push(" id: string;");
786
+ for (const [fieldName, fieldDef] of Object.entries(fields)) {
787
+ const fType = String(fieldDef.type || "text");
788
+ const tsType = fieldTypeToTs(fType, !!fieldDef.multiple);
789
+ const required = fieldDef.required ? "" : "?";
790
+ if (fieldDef.label) {
791
+ lines.push(` /** ${fieldDef.label} */`);
792
+ }
793
+ lines.push(` ${fieldName}${required}: ${tsType};`);
794
+ }
795
+ lines.push("}");
796
+ lines.push("");
797
+ }
798
+ return lines.join("\n") + "\n";
799
+ }
800
+ var generateMetadataCommand = new Command5("metadata").alias("m").description("Generate metadata scaffold (object, view, action, flow, agent, dashboard, app)").argument("<type>", "Metadata type to generate").argument("<name>", "Name for the metadata (use kebab-case)").option("-d, --dir <directory>", "Target directory (overrides default)").option("--dry-run", "Show what would be created without writing files").action(async (type, name, options) => {
930
801
  printHeader("Generate");
931
802
  const generator = GENERATORS[type];
932
803
  if (!generator) {
933
804
  printError(`Unknown type: ${type}`);
934
805
  console.log("");
935
- console.log(chalk7.bold(" Available types:"));
806
+ console.log(chalk5.bold(" Available types:"));
936
807
  for (const [key, gen] of Object.entries(GENERATORS)) {
937
- console.log(` ${chalk7.cyan(key.padEnd(12))} ${chalk7.dim(gen.description)}`);
808
+ console.log(` ${chalk5.cyan(key.padEnd(12))} ${chalk5.dim(gen.description)}`);
938
809
  }
939
810
  console.log("");
940
- console.log(chalk7.dim(" Usage: objectstack generate <type> <name>"));
941
- console.log(chalk7.dim(" Example: objectstack generate object project"));
942
- console.log(chalk7.dim(" Alias: os g object project"));
811
+ console.log(chalk5.dim(" Usage: objectstack generate <type> <name>"));
812
+ console.log(chalk5.dim(" Example: objectstack generate object project"));
813
+ console.log(chalk5.dim(" Alias: os g object project"));
943
814
  process.exit(1);
944
815
  }
945
816
  const dir = options.dir || generator.defaultDir;
946
817
  const fileName = `${toSnakeCase(name)}.ts`;
947
- const filePath = path4.join(process.cwd(), dir, fileName);
948
- console.log(` ${chalk7.dim("Type:")} ${chalk7.cyan(type)} \u2014 ${generator.description}`);
949
- console.log(` ${chalk7.dim("Name:")} ${chalk7.white(name)}`);
950
- console.log(` ${chalk7.dim("File:")} ${chalk7.white(path4.join(dir, fileName))}`);
818
+ const filePath = path3.join(process.cwd(), dir, fileName);
819
+ console.log(` ${chalk5.dim("Type:")} ${chalk5.cyan(type)} \u2014 ${generator.description}`);
820
+ console.log(` ${chalk5.dim("Name:")} ${chalk5.white(name)}`);
821
+ console.log(` ${chalk5.dim("File:")} ${chalk5.white(path3.join(dir, fileName))}`);
951
822
  console.log("");
952
823
  if (options.dryRun) {
953
824
  printInfo("Dry run \u2014 no files written");
954
825
  console.log("");
955
- console.log(chalk7.dim(" Content:"));
956
- console.log(chalk7.dim(" " + "-".repeat(38)));
826
+ console.log(chalk5.dim(" Content:"));
827
+ console.log(chalk5.dim(" " + "-".repeat(38)));
957
828
  const content = generator.generate(name);
958
829
  for (const line of content.split("\n")) {
959
- console.log(chalk7.dim(` ${line}`));
830
+ console.log(chalk5.dim(` ${line}`));
960
831
  }
961
832
  console.log("");
962
833
  return;
963
834
  }
964
- if (fs4.existsSync(filePath)) {
835
+ if (fs3.existsSync(filePath)) {
965
836
  printError(`File already exists: ${filePath}`);
966
837
  process.exit(1);
967
838
  }
968
839
  try {
969
- const fullDir = path4.dirname(filePath);
970
- if (!fs4.existsSync(fullDir)) {
971
- fs4.mkdirSync(fullDir, { recursive: true });
840
+ const fullDir = path3.dirname(filePath);
841
+ if (!fs3.existsSync(fullDir)) {
842
+ fs3.mkdirSync(fullDir, { recursive: true });
972
843
  }
973
844
  const content = generator.generate(name);
974
- fs4.writeFileSync(filePath, content);
975
- printSuccess(`Created ${path4.join(dir, fileName)}`);
976
- const indexPath = path4.join(process.cwd(), dir, "index.ts");
977
- if (fs4.existsSync(indexPath)) {
978
- const indexContent = fs4.readFileSync(indexPath, "utf-8");
845
+ fs3.writeFileSync(filePath, content);
846
+ printSuccess(`Created ${path3.join(dir, fileName)}`);
847
+ const indexPath = path3.join(process.cwd(), dir, "index.ts");
848
+ if (fs3.existsSync(indexPath)) {
849
+ const indexContent = fs3.readFileSync(indexPath, "utf-8");
979
850
  const exportLine = `export { default as ${toCamelCase2(name)} } from './${toSnakeCase(name)}';`;
980
851
  if (!indexContent.includes(toCamelCase2(name))) {
981
- fs4.appendFileSync(indexPath, exportLine + "\n");
852
+ fs3.appendFileSync(indexPath, exportLine + "\n");
982
853
  printSuccess(`Updated ${dir}/index.ts with export`);
983
854
  }
984
855
  } else {
985
856
  const exportLine = `export { default as ${toCamelCase2(name)} } from './${toSnakeCase(name)}';
986
857
  `;
987
- fs4.writeFileSync(indexPath, exportLine);
858
+ fs3.writeFileSync(indexPath, exportLine);
988
859
  printSuccess(`Created ${dir}/index.ts`);
989
860
  }
990
861
  console.log("");
991
- console.log(chalk7.dim(` Tip: Run \`objectstack validate\` to check your config`));
862
+ console.log(chalk5.dim(` Tip: Run \`objectstack validate\` to check your config`));
863
+ console.log("");
864
+ } catch (error) {
865
+ printError(error.message || String(error));
866
+ process.exit(1);
867
+ }
868
+ });
869
+ var generateTypesCommand = new Command5("types").description("Generate TypeScript type definitions from ObjectStack configuration").argument("[config]", "Configuration file path").option("-o, --output <file>", "Output file path", "src/types/objectstack.d.ts").option("--dry-run", "Show what would be generated without writing files").action(async (configPath, options) => {
870
+ printHeader("Generate Types");
871
+ try {
872
+ const { loadConfig: loadConfig2 } = await import("./config-UN34WBHT.js");
873
+ printInfo("Loading configuration...");
874
+ const { config, absolutePath } = await loadConfig2(configPath);
875
+ console.log(` ${chalk5.dim("Config:")} ${chalk5.white(absolutePath)}`);
876
+ console.log(` ${chalk5.dim("Output:")} ${chalk5.white(options.output)}`);
877
+ console.log("");
878
+ const content = generateTypesFromConfig(config);
879
+ if (options.dryRun) {
880
+ printInfo("Dry run \u2014 no files written");
881
+ console.log("");
882
+ for (const line of content.split("\n")) {
883
+ console.log(chalk5.dim(` ${line}`));
884
+ }
885
+ console.log("");
886
+ return;
887
+ }
888
+ const outPath = path3.resolve(process.cwd(), options.output);
889
+ const outDir = path3.dirname(outPath);
890
+ if (!fs3.existsSync(outDir)) {
891
+ fs3.mkdirSync(outDir, { recursive: true });
892
+ }
893
+ fs3.writeFileSync(outPath, content);
894
+ printSuccess(`Generated types at ${options.output}`);
992
895
  console.log("");
993
896
  } catch (error) {
994
897
  printError(error.message || String(error));
995
898
  process.exit(1);
996
899
  }
997
900
  });
901
+ var generateCommand = new Command5("generate").alias("g").description("Generate metadata files or TypeScript types").argument("[type]", "Metadata type to generate (object, view, action, flow, agent, dashboard, app)").argument("[name]", "Name for the metadata (use kebab-case)").option("-d, --dir <directory>", "Target directory (overrides default)").option("--dry-run", "Show what would be created without writing files").addCommand(generateTypesCommand).action(async (type, name, options) => {
902
+ if (!type) {
903
+ printHeader("Generate");
904
+ console.log(chalk5.bold(" Sub-commands:"));
905
+ console.log(` ${chalk5.cyan("types".padEnd(12))} Generate TypeScript type definitions from config`);
906
+ console.log("");
907
+ console.log(chalk5.bold(" Metadata types:"));
908
+ for (const [key, gen] of Object.entries(GENERATORS)) {
909
+ console.log(` ${chalk5.cyan(key.padEnd(12))} ${chalk5.dim(gen.description)}`);
910
+ }
911
+ console.log("");
912
+ console.log(chalk5.dim(" Usage: objectstack generate <type> <name>"));
913
+ console.log(chalk5.dim(" Usage: objectstack generate types [config]"));
914
+ return;
915
+ }
916
+ if (!name) {
917
+ printError("Missing required argument: <name>");
918
+ console.log(chalk5.dim(" Usage: objectstack generate <type> <name>"));
919
+ process.exit(1);
920
+ }
921
+ await generateMetadataCommand.parseAsync([type, name, ...process.argv.slice(4)], { from: "user" });
922
+ });
998
923
 
999
924
  // src/commands/create.ts
1000
925
  import { Command as Command6 } from "commander";
1001
- import chalk8 from "chalk";
1002
- import fs5 from "fs";
1003
- import path5 from "path";
926
+ import chalk6 from "chalk";
927
+ import fs4 from "fs";
928
+ import path4 from "path";
1004
929
  var templates = {
1005
930
  plugin: {
1006
931
  description: "Create a new ObjectStack plugin",
@@ -1175,76 +1100,321 @@ function toCamelCase3(str) {
1175
1100
  return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
1176
1101
  }
1177
1102
  var createCommand = new Command6("create").description("Create a new package, plugin, or example from template").argument("<type>", "Type of project to create (plugin, example)").argument("[name]", "Name of the project").option("-d, --dir <directory>", "Target directory").action(async (type, name, options) => {
1178
- console.log(chalk8.bold(`
1103
+ console.log(chalk6.bold(`
1179
1104
  \u{1F4E6} ObjectStack Project Creator`));
1180
- console.log(chalk8.dim(`-------------------------------`));
1105
+ console.log(chalk6.dim(`-------------------------------`));
1181
1106
  if (!templates[type]) {
1182
- console.error(chalk8.red(`
1107
+ console.error(chalk6.red(`
1183
1108
  \u274C Unknown type: ${type}`));
1184
- console.log(chalk8.dim("Available types: plugin, example"));
1109
+ console.log(chalk6.dim("Available types: plugin, example"));
1185
1110
  process.exit(1);
1186
1111
  }
1187
1112
  if (!name) {
1188
- console.error(chalk8.red("\n\u274C Project name is required"));
1189
- console.log(chalk8.dim(`Usage: objectstack create ${type} <name>`));
1113
+ console.error(chalk6.red("\n\u274C Project name is required"));
1114
+ console.log(chalk6.dim(`Usage: objectstack create ${type} <name>`));
1190
1115
  process.exit(1);
1191
1116
  }
1192
1117
  const template = templates[type];
1193
1118
  const cwd = process.cwd();
1194
1119
  let targetDir;
1195
1120
  if (options?.dir) {
1196
- targetDir = path5.resolve(cwd, options.dir);
1121
+ targetDir = path4.resolve(cwd, options.dir);
1197
1122
  } else {
1198
1123
  const baseDir = type === "plugin" ? "packages/plugins" : "examples";
1199
1124
  const projectName = type === "plugin" ? `plugin-${name}` : name;
1200
- targetDir = path5.join(cwd, baseDir, projectName);
1125
+ targetDir = path4.join(cwd, baseDir, projectName);
1201
1126
  }
1202
- if (fs5.existsSync(targetDir)) {
1203
- console.error(chalk8.red(`
1127
+ if (fs4.existsSync(targetDir)) {
1128
+ console.error(chalk6.red(`
1204
1129
  \u274C Directory already exists: ${targetDir}`));
1205
1130
  process.exit(1);
1206
1131
  }
1207
- console.log(`\u{1F4C1} Creating ${type}: ${chalk8.blue(name)}`);
1208
- console.log(`\u{1F4C2} Location: ${chalk8.dim(targetDir)}`);
1132
+ console.log(`\u{1F4C1} Creating ${type}: ${chalk6.blue(name)}`);
1133
+ console.log(`\u{1F4C2} Location: ${chalk6.dim(targetDir)}`);
1209
1134
  console.log("");
1210
1135
  try {
1211
- fs5.mkdirSync(targetDir, { recursive: true });
1136
+ fs4.mkdirSync(targetDir, { recursive: true });
1212
1137
  for (const [filePath, contentFn] of Object.entries(template.files)) {
1213
- const fullPath = path5.join(targetDir, filePath);
1214
- const dir = path5.dirname(fullPath);
1215
- if (!fs5.existsSync(dir)) {
1216
- fs5.mkdirSync(dir, { recursive: true });
1138
+ const fullPath = path4.join(targetDir, filePath);
1139
+ const dir = path4.dirname(fullPath);
1140
+ if (!fs4.existsSync(dir)) {
1141
+ fs4.mkdirSync(dir, { recursive: true });
1217
1142
  }
1218
1143
  const content = contentFn(name);
1219
1144
  const fileContent = typeof content === "string" ? content : JSON.stringify(content, null, 2);
1220
- fs5.writeFileSync(fullPath, fileContent);
1221
- console.log(chalk8.green(`\u2713 Created ${filePath}`));
1145
+ fs4.writeFileSync(fullPath, fileContent);
1146
+ console.log(chalk6.green(`\u2713 Created ${filePath}`));
1222
1147
  }
1223
1148
  console.log("");
1224
- console.log(chalk8.green("\u2705 Project created successfully!"));
1149
+ console.log(chalk6.green("\u2705 Project created successfully!"));
1225
1150
  console.log("");
1226
- console.log(chalk8.bold("Next steps:"));
1227
- console.log(chalk8.dim(` cd ${path5.relative(cwd, targetDir)}`));
1228
- console.log(chalk8.dim(" pnpm install"));
1229
- console.log(chalk8.dim(" pnpm build"));
1151
+ console.log(chalk6.bold("Next steps:"));
1152
+ console.log(chalk6.dim(` cd ${path4.relative(cwd, targetDir)}`));
1153
+ console.log(chalk6.dim(" pnpm install"));
1154
+ console.log(chalk6.dim(" pnpm build"));
1230
1155
  console.log("");
1231
1156
  } catch (error) {
1232
- console.error(chalk8.red("\n\u274C Failed to create project:"));
1157
+ console.error(chalk6.red("\n\u274C Failed to create project:"));
1233
1158
  console.error(error.message || error);
1234
- if (fs5.existsSync(targetDir)) {
1235
- fs5.rmSync(targetDir, { recursive: true });
1159
+ if (fs4.existsSync(targetDir)) {
1160
+ fs4.rmSync(targetDir, { recursive: true });
1236
1161
  }
1237
1162
  process.exit(1);
1238
1163
  }
1239
1164
  });
1240
1165
 
1241
- // src/commands/dev.ts
1166
+ // src/commands/plugin.ts
1242
1167
  import { Command as Command7 } from "commander";
1243
- import chalk9 from "chalk";
1168
+ import chalk7 from "chalk";
1169
+ import fs5 from "fs";
1170
+ import path5 from "path";
1171
+ function resolvePluginName(plugin) {
1172
+ if (typeof plugin === "string") return plugin;
1173
+ if (plugin && typeof plugin === "object") {
1174
+ const p = plugin;
1175
+ if (typeof p.name === "string") return p.name;
1176
+ if (p.constructor && p.constructor.name !== "Object") return p.constructor.name;
1177
+ }
1178
+ return "unknown";
1179
+ }
1180
+ function resolvePluginVersion(plugin) {
1181
+ if (plugin && typeof plugin === "object") {
1182
+ const p = plugin;
1183
+ if (typeof p.version === "string") return p.version;
1184
+ }
1185
+ return "-";
1186
+ }
1187
+ function resolvePluginType(plugin) {
1188
+ if (plugin && typeof plugin === "object") {
1189
+ const p = plugin;
1190
+ if (typeof p.type === "string") return p.type;
1191
+ }
1192
+ return "standard";
1193
+ }
1194
+ function readConfigText(configPath) {
1195
+ return fs5.readFileSync(configPath, "utf-8");
1196
+ }
1197
+ function addPluginToConfig(configPath, packageName) {
1198
+ let content = readConfigText(configPath);
1199
+ const shortName = packageName.replace(/^@[^/]+\//, "").replace(/^plugin-/, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
1200
+ const varName = shortName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) + "Plugin";
1201
+ const importLine = `import ${varName} from '${packageName}';
1202
+ `;
1203
+ if (content.includes(packageName)) {
1204
+ throw new Error(`Plugin '${packageName}' is already referenced in the config`);
1205
+ }
1206
+ const importRegex = /^import .+$/gm;
1207
+ let lastImportEnd = 0;
1208
+ let match;
1209
+ while ((match = importRegex.exec(content)) !== null) {
1210
+ lastImportEnd = match.index + match[0].length;
1211
+ }
1212
+ if (lastImportEnd > 0) {
1213
+ content = content.slice(0, lastImportEnd) + "\n" + importLine + content.slice(lastImportEnd);
1214
+ } else {
1215
+ content = importLine + "\n" + content;
1216
+ }
1217
+ if (/plugins\s*:\s*\[/.test(content)) {
1218
+ let replaced = false;
1219
+ content = content.replace(
1220
+ /(plugins\s*:\s*\[)/,
1221
+ (match2) => {
1222
+ if (replaced) return match2;
1223
+ replaced = true;
1224
+ return `${match2}
1225
+ ${varName},`;
1226
+ }
1227
+ );
1228
+ } else {
1229
+ content = content.replace(
1230
+ /(defineStack\(\{[\s\S]*?)(}\s*\))/,
1231
+ `$1 plugins: [
1232
+ ${varName},
1233
+ ],
1234
+ $2`
1235
+ );
1236
+ }
1237
+ fs5.writeFileSync(configPath, content);
1238
+ }
1239
+ function removePluginFromConfig(configPath, pluginName) {
1240
+ let content = readConfigText(configPath);
1241
+ const importRegex = new RegExp(`^import .+['"]${escapeRegex(pluginName)}['"]\\s*;?\\s*$\\n?`, "gm");
1242
+ const hadImport = importRegex.test(content);
1243
+ importRegex.lastIndex = 0;
1244
+ content = content.replace(importRegex, "");
1245
+ const shortName = pluginName.replace(/^@[^/]+\//, "").replace(/^plugin-/, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
1246
+ const varName = shortName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) + "Plugin";
1247
+ if (!hadImport) {
1248
+ const varImportRegex = new RegExp(`^import .* ${escapeRegex(varName)} .+$\\n?`, "gm");
1249
+ content = content.replace(varImportRegex, "");
1250
+ }
1251
+ const entryPatterns = [
1252
+ new RegExp(`\\s*${escapeRegex(varName)},?\\n?`, "g"),
1253
+ new RegExp(`\\s*['"]${escapeRegex(pluginName)}['"],?\\n?`, "g")
1254
+ ];
1255
+ for (const pattern of entryPatterns) {
1256
+ content = content.replace(pattern, "\n");
1257
+ }
1258
+ content = content.replace(/plugins\s*:\s*\[\s*\],?\n?/g, "");
1259
+ fs5.writeFileSync(configPath, content);
1260
+ }
1261
+ function escapeRegex(str) {
1262
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1263
+ }
1264
+ var listCommand = new Command7("list").alias("ls").description("List plugins defined in the configuration").argument("[config]", "Configuration file path").option("--json", "Output as JSON").action(async (configSource, options) => {
1265
+ try {
1266
+ const { config } = await loadConfig(configSource);
1267
+ const plugins = config.plugins || [];
1268
+ const devPlugins = config.devPlugins || [];
1269
+ if (options?.json) {
1270
+ const data = {
1271
+ plugins: plugins.map((p) => ({
1272
+ name: resolvePluginName(p),
1273
+ version: resolvePluginVersion(p),
1274
+ type: resolvePluginType(p),
1275
+ dev: false
1276
+ })),
1277
+ devPlugins: devPlugins.map((p) => ({
1278
+ name: resolvePluginName(p),
1279
+ version: resolvePluginVersion(p),
1280
+ type: resolvePluginType(p),
1281
+ dev: true
1282
+ }))
1283
+ };
1284
+ console.log(JSON.stringify(data, null, 2));
1285
+ return;
1286
+ }
1287
+ printHeader("Plugins");
1288
+ if (plugins.length === 0 && devPlugins.length === 0) {
1289
+ printInfo("No plugins configured");
1290
+ console.log("");
1291
+ console.log(chalk7.dim(" Hint: Add plugins to your objectstack.config.ts"));
1292
+ console.log(chalk7.dim(" Or run: os plugin add <package-name>"));
1293
+ console.log("");
1294
+ return;
1295
+ }
1296
+ if (plugins.length > 0) {
1297
+ console.log(chalk7.bold(`
1298
+ Plugins (${plugins.length}):`));
1299
+ for (const plugin of plugins) {
1300
+ const name = resolvePluginName(plugin);
1301
+ const version = resolvePluginVersion(plugin);
1302
+ const type = resolvePluginType(plugin);
1303
+ console.log(
1304
+ ` ${chalk7.cyan("\u25CF")} ${chalk7.white(name)}` + (version !== "-" ? chalk7.dim(` v${version}`) : "") + (type !== "standard" ? chalk7.dim(` [${type}]`) : "")
1305
+ );
1306
+ }
1307
+ }
1308
+ if (devPlugins.length > 0) {
1309
+ console.log(chalk7.bold(`
1310
+ Dev Plugins (${devPlugins.length}):`));
1311
+ for (const plugin of devPlugins) {
1312
+ const name = resolvePluginName(plugin);
1313
+ const version = resolvePluginVersion(plugin);
1314
+ console.log(
1315
+ ` ${chalk7.yellow("\u25CF")} ${chalk7.white(name)}` + (version !== "-" ? chalk7.dim(` v${version}`) : "") + chalk7.dim(" [dev]")
1316
+ );
1317
+ }
1318
+ }
1319
+ console.log("");
1320
+ } catch (error) {
1321
+ printError(error.message || String(error));
1322
+ process.exit(1);
1323
+ }
1324
+ });
1325
+ var infoSubCommand = new Command7("info").description("Show detailed information about a plugin").argument("<name>", "Plugin name or package name").argument("[config]", "Configuration file path").action(async (name, configSource) => {
1326
+ try {
1327
+ const { config } = await loadConfig(configSource);
1328
+ const allPlugins = [
1329
+ ...config.plugins || [],
1330
+ ...config.devPlugins || []
1331
+ ];
1332
+ const found = allPlugins.find((p) => {
1333
+ const pName = resolvePluginName(p);
1334
+ return pName === name || pName.includes(name);
1335
+ });
1336
+ if (!found) {
1337
+ printError(`Plugin '${name}' not found in configuration`);
1338
+ console.log("");
1339
+ console.log(chalk7.dim(" Available plugins:"));
1340
+ for (const p of allPlugins) {
1341
+ console.log(chalk7.dim(` - ${resolvePluginName(p)}`));
1342
+ }
1343
+ console.log("");
1344
+ process.exit(1);
1345
+ }
1346
+ printHeader(`Plugin: ${resolvePluginName(found)}`);
1347
+ printKV("Name", resolvePluginName(found));
1348
+ printKV("Version", resolvePluginVersion(found));
1349
+ printKV("Type", resolvePluginType(found));
1350
+ const isDev = (config.devPlugins || []).includes(found);
1351
+ printKV("Environment", isDev ? "development" : "production");
1352
+ if (found && typeof found === "object") {
1353
+ const p = found;
1354
+ if (typeof p.description === "string") {
1355
+ printKV("Description", p.description);
1356
+ }
1357
+ if (Array.isArray(p.dependencies) && p.dependencies.length > 0) {
1358
+ printKV("Dependencies", p.dependencies.join(", "));
1359
+ }
1360
+ if (typeof p.init === "function") {
1361
+ printInfo("This is a runtime plugin instance (has init function)");
1362
+ }
1363
+ }
1364
+ if (typeof found === "string") {
1365
+ printInfo("This is a string reference (will be imported at runtime)");
1366
+ }
1367
+ console.log("");
1368
+ } catch (error) {
1369
+ printError(error.message || String(error));
1370
+ process.exit(1);
1371
+ }
1372
+ });
1373
+ var addCommand = new Command7("add").description("Add a plugin to objectstack.config.ts").argument("<package>", "Plugin package name (e.g. @objectstack/plugin-auth)").option("-d, --dev", "Add as a dev-only plugin").option("-c, --config <path>", "Configuration file path").action(async (packageName, options) => {
1374
+ try {
1375
+ const configPath = resolveConfigPath(options?.config);
1376
+ printHeader("Add Plugin");
1377
+ console.log(` ${chalk7.dim("Package:")} ${chalk7.white(packageName)}`);
1378
+ console.log(` ${chalk7.dim("Config:")} ${chalk7.white(path5.relative(process.cwd(), configPath))}`);
1379
+ console.log("");
1380
+ addPluginToConfig(configPath, packageName);
1381
+ printSuccess(`Added ${chalk7.cyan(packageName)} to config`);
1382
+ console.log("");
1383
+ console.log(chalk7.dim(" Next steps:"));
1384
+ console.log(chalk7.dim(` 1. Install the package: pnpm add ${packageName}`));
1385
+ console.log(chalk7.dim(" 2. Run: os validate"));
1386
+ console.log("");
1387
+ } catch (error) {
1388
+ printError(error.message || String(error));
1389
+ process.exit(1);
1390
+ }
1391
+ });
1392
+ var removeCommand = new Command7("remove").alias("rm").description("Remove a plugin from objectstack.config.ts").argument("<name>", "Plugin name or package name to remove").option("-c, --config <path>", "Configuration file path").action(async (pluginName, options) => {
1393
+ try {
1394
+ const configPath = resolveConfigPath(options?.config);
1395
+ printHeader("Remove Plugin");
1396
+ console.log(` ${chalk7.dim("Plugin:")} ${chalk7.white(pluginName)}`);
1397
+ console.log(` ${chalk7.dim("Config:")} ${chalk7.white(path5.relative(process.cwd(), configPath))}`);
1398
+ console.log("");
1399
+ removePluginFromConfig(configPath, pluginName);
1400
+ printSuccess(`Removed ${chalk7.cyan(pluginName)} from config`);
1401
+ console.log("");
1402
+ console.log(chalk7.dim(" Tip: Run `pnpm remove " + pluginName + "` to uninstall the package"));
1403
+ console.log("");
1404
+ } catch (error) {
1405
+ printError(error.message || String(error));
1406
+ process.exit(1);
1407
+ }
1408
+ });
1409
+ var pluginCommand = new Command7("plugin").description("Manage plugins (list, info, add, remove)").addCommand(listCommand).addCommand(infoSubCommand).addCommand(addCommand).addCommand(removeCommand);
1410
+
1411
+ // src/commands/dev.ts
1412
+ import { Command as Command8 } from "commander";
1413
+ import chalk8 from "chalk";
1244
1414
  import { execSync, spawn } from "child_process";
1245
1415
  import fs6 from "fs";
1246
1416
  import path6 from "path";
1247
- var devCommand = new Command7("dev").description("Start development mode with hot-reload").argument("[package]", "Package name or filter pattern", "all").option("-w, --watch", "Enable watch mode (default)", true).option("--ui", "Enable Studio UI at /_studio/").option("-v, --verbose", "Verbose output").action(async (packageName, options) => {
1417
+ var devCommand = new Command8("dev").description("Start development mode with hot-reload").argument("[package]", "Package name or filter pattern", "all").option("-w, --watch", "Enable watch mode (default)", true).option("--ui", "Enable Studio UI at /_studio/").option("-v, --verbose", "Verbose output").action(async (packageName, options) => {
1248
1418
  printHeader("Development Mode");
1249
1419
  const configPath = path6.resolve(process.cwd(), "objectstack.config.ts");
1250
1420
  if (packageName === "all" && fs6.existsSync(configPath)) {
@@ -1263,14 +1433,14 @@ var devCommand = new Command7("dev").description("Start development mode with ho
1263
1433
  const isWorkspaceRoot = fs6.existsSync(workspaceConfigPath);
1264
1434
  if (packageName === "all" && !isWorkspaceRoot) {
1265
1435
  printError(`Config file not found in ${cwd}`);
1266
- console.error(chalk9.yellow(" Run in a directory with objectstack.config.ts, or from the monorepo root."));
1436
+ console.error(chalk8.yellow(" Run in a directory with objectstack.config.ts, or from the monorepo root."));
1267
1437
  process.exit(1);
1268
1438
  }
1269
1439
  const filter = packageName === "all" ? "" : `--filter ${packageName}`;
1270
1440
  printKV("Package", packageName === "all" ? "All packages" : packageName, "\u{1F4E6}");
1271
1441
  printKV("Watch", "enabled", "\u{1F504}");
1272
1442
  const command = `pnpm ${filter} dev`.trim();
1273
- console.log(chalk9.dim(`$ ${command}`));
1443
+ console.log(chalk8.dim(`$ ${command}`));
1274
1444
  console.log("");
1275
1445
  execSync(command, {
1276
1446
  stdio: "inherit",
@@ -1283,12 +1453,12 @@ var devCommand = new Command7("dev").description("Start development mode with ho
1283
1453
  });
1284
1454
 
1285
1455
  // src/commands/serve.ts
1286
- import { Command as Command8 } from "commander";
1456
+ import { Command as Command9 } from "commander";
1287
1457
  import path8 from "path";
1288
1458
  import fs8 from "fs";
1289
1459
  import net from "net";
1290
- import chalk10 from "chalk";
1291
- import { bundleRequire as bundleRequire2 } from "bundle-require";
1460
+ import chalk9 from "chalk";
1461
+ import { bundleRequire } from "bundle-require";
1292
1462
 
1293
1463
  // src/utils/studio.ts
1294
1464
  import path7 from "path";
@@ -1427,7 +1597,7 @@ var getAvailablePort = async (startPort) => {
1427
1597
  }
1428
1598
  return port;
1429
1599
  };
1430
- var serveCommand = new Command8("serve").description("Start ObjectStack server with plugins from configuration").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").option("--dev", "Run in development mode (load devPlugins)").option("--ui", "Enable Studio UI at /_studio/ (default: true in dev mode)").option("--no-server", "Skip starting HTTP server plugin").action(async (configPath, options) => {
1600
+ var serveCommand = new Command9("serve").description("Start ObjectStack server with plugins from configuration").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").option("--dev", "Run in development mode (load devPlugins)").option("--ui", "Enable Studio UI at /_studio/ (default: true in dev mode)").option("--no-server", "Skip starting HTTP server plugin").action(async (configPath, options) => {
1431
1601
  let port = parseInt(options.port);
1432
1602
  try {
1433
1603
  const availablePort = await getAvailablePort(port);
@@ -1441,11 +1611,11 @@ var serveCommand = new Command8("serve").description("Start ObjectStack server w
1441
1611
  const relativeConfig = path8.relative(process.cwd(), absolutePath);
1442
1612
  if (!fs8.existsSync(absolutePath)) {
1443
1613
  printError(`Configuration file not found: ${absolutePath}`);
1444
- console.log(chalk10.dim(" Hint: Run `objectstack init` to create a new project"));
1614
+ console.log(chalk9.dim(" Hint: Run `objectstack init` to create a new project"));
1445
1615
  process.exit(1);
1446
1616
  }
1447
1617
  console.log("");
1448
- console.log(chalk10.dim(` Loading ${relativeConfig}...`));
1618
+ console.log(chalk9.dim(` Loading ${relativeConfig}...`));
1449
1619
  const loadedPlugins = [];
1450
1620
  const shortPluginName = (raw) => {
1451
1621
  if (raw.includes("objectql")) return "ObjectQL";
@@ -1480,7 +1650,7 @@ var serveCommand = new Command8("serve").description("Start ObjectStack server w
1480
1650
  console.debug = (...args) => {
1481
1651
  if (!bootQuiet) originalConsoleDebug(...args);
1482
1652
  };
1483
- const { mod } = await bundleRequire2({
1653
+ const { mod } = await bundleRequire({
1484
1654
  filepath: absolutePath
1485
1655
  });
1486
1656
  const config = mod.default || mod;
@@ -1549,7 +1719,7 @@ var serveCommand = new Command8("serve").description("Start ObjectStack server w
1549
1719
  const pluginName = plugin.name || plugin.constructor?.name || "unnamed";
1550
1720
  trackPlugin(pluginName);
1551
1721
  } catch (e) {
1552
- console.error(chalk10.red(` \u2717 Failed to load plugin: ${e.message}`));
1722
+ console.error(chalk9.red(` \u2717 Failed to load plugin: ${e.message}`));
1553
1723
  }
1554
1724
  }
1555
1725
  }
@@ -1560,7 +1730,7 @@ var serveCommand = new Command8("serve").description("Start ObjectStack server w
1560
1730
  await kernel.use(serverPlugin);
1561
1731
  trackPlugin("HonoServer");
1562
1732
  } catch (e) {
1563
- console.warn(chalk10.yellow(` \u26A0 HTTP server plugin not available: ${e.message}`));
1733
+ console.warn(chalk9.yellow(` \u26A0 HTTP server plugin not available: ${e.message}`));
1564
1734
  }
1565
1735
  try {
1566
1736
  const { createRestApiPlugin } = await import("@objectstack/rest");
@@ -1579,13 +1749,13 @@ var serveCommand = new Command8("serve").description("Start ObjectStack server w
1579
1749
  if (enableUI) {
1580
1750
  const studioPath = resolveStudioPath();
1581
1751
  if (!studioPath) {
1582
- console.warn(chalk10.yellow(` \u26A0 @objectstack/studio not found \u2014 skipping UI`));
1752
+ console.warn(chalk9.yellow(` \u26A0 @objectstack/studio not found \u2014 skipping UI`));
1583
1753
  } else if (hasStudioDist(studioPath)) {
1584
1754
  const distPath = path8.join(studioPath, "dist");
1585
1755
  await kernel.use(createStudioStaticPlugin(distPath, { isDev }));
1586
1756
  trackPlugin("StudioUI");
1587
1757
  } else {
1588
- console.warn(chalk10.yellow(` \u26A0 Studio dist not found \u2014 run "pnpm --filter @objectstack/studio build" first`));
1758
+ console.warn(chalk9.yellow(` \u26A0 Studio dist not found \u2014 run "pnpm --filter @objectstack/studio build" first`));
1589
1759
  }
1590
1760
  }
1591
1761
  await runtime.start();
@@ -1601,25 +1771,25 @@ var serveCommand = new Command8("serve").description("Start ObjectStack server w
1601
1771
  studioPath: STUDIO_PATH
1602
1772
  });
1603
1773
  process.on("SIGINT", async () => {
1604
- console.warn(chalk10.yellow(`
1774
+ console.warn(chalk9.yellow(`
1605
1775
 
1606
1776
  \u23F9 Stopping server...`));
1607
1777
  await runtime.getKernel().shutdown();
1608
- console.log(chalk10.green(`\u2705 Server stopped`));
1778
+ console.log(chalk9.green(`\u2705 Server stopped`));
1609
1779
  process.exit(0);
1610
1780
  });
1611
1781
  } catch (error) {
1612
1782
  restoreOutput();
1613
1783
  console.log("");
1614
1784
  printError(error.message || String(error));
1615
- if (process.env.DEBUG) console.error(chalk10.dim(error.stack));
1785
+ if (process.env.DEBUG) console.error(chalk9.dim(error.stack));
1616
1786
  process.exit(1);
1617
1787
  }
1618
1788
  });
1619
1789
 
1620
1790
  // src/commands/test.ts
1621
- import { Command as Command9 } from "commander";
1622
- import chalk11 from "chalk";
1791
+ import { Command as Command10 } from "commander";
1792
+ import chalk10 from "chalk";
1623
1793
  import path9 from "path";
1624
1794
  import fs9 from "fs";
1625
1795
  import { QA as CoreQA } from "@objectstack/core";
@@ -1645,16 +1815,16 @@ function resolveGlob(pattern) {
1645
1815
  const entries = fs9.readdirSync(baseDir, { recursive: true, encoding: "utf-8" });
1646
1816
  return entries.filter((entry) => regex.test(entry.replace(/\\/g, "/"))).map((entry) => path9.join(baseDir, entry)).filter((fullPath) => fs9.statSync(fullPath).isFile());
1647
1817
  }
1648
- var testCommand = new Command9("test").description("Run Quality Protocol test scenarios against a running server").argument("[files]", 'Glob pattern for test files (e.g. "qa/*.test.json")', "qa/*.test.json").option("--url <url>", "Target base URL", "http://localhost:3000").option("--token <token>", "Authentication token").action(async (filesPattern, options) => {
1649
- console.log(chalk11.bold(`
1818
+ var testCommand = new Command10("test").description("Run Quality Protocol test scenarios against a running server").argument("[files]", 'Glob pattern for test files (e.g. "qa/*.test.json")', "qa/*.test.json").option("--url <url>", "Target base URL", "http://localhost:3000").option("--token <token>", "Authentication token").action(async (filesPattern, options) => {
1819
+ console.log(chalk10.bold(`
1650
1820
  \u{1F9EA} ObjectStack Quality Protocol Runner`));
1651
- console.log(chalk11.dim(`-------------------------------------`));
1652
- console.log(`Target: ${chalk11.blue(options.url)}`);
1821
+ console.log(chalk10.dim(`-------------------------------------`));
1822
+ console.log(`Target: ${chalk10.blue(options.url)}`);
1653
1823
  const adapter = new CoreQA.HttpTestAdapter(options.url, options.token);
1654
1824
  const runner = new CoreQA.TestRunner(adapter);
1655
1825
  const testFiles = resolveGlob(filesPattern);
1656
1826
  if (testFiles.length === 0) {
1657
- console.warn(chalk11.yellow(`No test files found matching: ${filesPattern}`));
1827
+ console.warn(chalk10.yellow(`No test files found matching: ${filesPattern}`));
1658
1828
  return;
1659
1829
  }
1660
1830
  console.log(`Found ${testFiles.length} test suites.`);
@@ -1662,7 +1832,7 @@ var testCommand = new Command9("test").description("Run Quality Protocol test sc
1662
1832
  let totalFailed = 0;
1663
1833
  for (const file of testFiles) {
1664
1834
  console.log(`
1665
- \u{1F4C4} Running suite: ${chalk11.bold(path9.basename(file))}`);
1835
+ \u{1F4C4} Running suite: ${chalk10.bold(path9.basename(file))}`);
1666
1836
  try {
1667
1837
  const content = fs9.readFileSync(file, "utf-8");
1668
1838
  const suite = JSON.parse(content);
@@ -1671,10 +1841,10 @@ var testCommand = new Command9("test").description("Run Quality Protocol test sc
1671
1841
  const icon = result.passed ? "\u2705" : "\u274C";
1672
1842
  console.log(` ${icon} Scenario: ${result.scenarioId} (${result.duration}ms)`);
1673
1843
  if (!result.passed) {
1674
- console.error(chalk11.red(` Error: ${result.error}`));
1844
+ console.error(chalk10.red(` Error: ${result.error}`));
1675
1845
  result.steps.forEach((step) => {
1676
1846
  if (!step.passed) {
1677
- console.error(chalk11.red(` Step Failed: ${step.stepName}`));
1847
+ console.error(chalk10.red(` Step Failed: ${step.stepName}`));
1678
1848
  if (step.output) console.error(` Output:`, step.output);
1679
1849
  if (step.error) console.error(` Error:`, step.error);
1680
1850
  }
@@ -1685,28 +1855,28 @@ var testCommand = new Command9("test").description("Run Quality Protocol test sc
1685
1855
  }
1686
1856
  }
1687
1857
  } catch (e) {
1688
- console.error(chalk11.red(`Failed to load or run suite ${file}: ${e}`));
1858
+ console.error(chalk10.red(`Failed to load or run suite ${file}: ${e}`));
1689
1859
  totalFailed++;
1690
1860
  }
1691
1861
  }
1692
- console.log(chalk11.dim(`
1862
+ console.log(chalk10.dim(`
1693
1863
  -------------------------------------`));
1694
1864
  if (totalFailed > 0) {
1695
- console.log(chalk11.red(`FAILED: ${totalFailed} scenarios failed. ${totalPassed} passed.`));
1865
+ console.log(chalk10.red(`FAILED: ${totalFailed} scenarios failed. ${totalPassed} passed.`));
1696
1866
  process.exit(1);
1697
1867
  } else {
1698
- console.log(chalk11.green(`SUCCESS: All ${totalPassed} scenarios passed.`));
1868
+ console.log(chalk10.green(`SUCCESS: All ${totalPassed} scenarios passed.`));
1699
1869
  process.exit(0);
1700
1870
  }
1701
1871
  });
1702
1872
 
1703
1873
  // src/commands/doctor.ts
1704
- import { Command as Command10 } from "commander";
1705
- import chalk12 from "chalk";
1874
+ import { Command as Command11 } from "commander";
1875
+ import chalk11 from "chalk";
1706
1876
  import { execSync as execSync2 } from "child_process";
1707
1877
  import fs10 from "fs";
1708
1878
  import path10 from "path";
1709
- var doctorCommand = new Command10("doctor").description("Check development environment health").option("-v, --verbose", "Show detailed information").action(async (options) => {
1879
+ var doctorCommand = new Command11("doctor").description("Check development environment health").option("-v, --verbose", "Show detailed information").action(async (options) => {
1710
1880
  printHeader("Environment Health Check");
1711
1881
  const results = [];
1712
1882
  try {
@@ -1823,24 +1993,111 @@ var doctorCommand = new Command10("doctor").description("Check development envir
1823
1993
  printError(`${padded} ${result.message}`);
1824
1994
  }
1825
1995
  if (result.fix && (options.verbose || result.status === "error")) {
1826
- console.log(chalk12.dim(` \u2192 ${result.fix}`));
1996
+ console.log(chalk11.dim(` \u2192 ${result.fix}`));
1827
1997
  }
1828
1998
  if (result.status === "error") hasErrors = true;
1829
1999
  if (result.status === "warning") hasWarnings = true;
1830
2000
  });
1831
2001
  console.log("");
1832
2002
  if (hasErrors) {
1833
- console.log(chalk12.red("\u274C Some critical issues found. Please fix them before continuing."));
1834
- results.filter((r) => r.status === "error" && r.fix).forEach((r) => console.log(chalk12.dim(` ${r.fix}`)));
2003
+ console.log(chalk11.red("\u274C Some critical issues found. Please fix them before continuing."));
2004
+ results.filter((r) => r.status === "error" && r.fix).forEach((r) => console.log(chalk11.dim(` ${r.fix}`)));
1835
2005
  process.exit(1);
1836
2006
  } else if (hasWarnings) {
1837
- console.log(chalk12.yellow("\u26A0\uFE0F Environment is functional but has some warnings."));
1838
- console.log(chalk12.dim(" Run with --verbose to see fix suggestions."));
2007
+ console.log(chalk11.yellow("\u26A0\uFE0F Environment is functional but has some warnings."));
2008
+ console.log(chalk11.dim(" Run with --verbose to see fix suggestions."));
1839
2009
  } else {
1840
- console.log(chalk12.green("\u2705 Environment is healthy and ready for development!"));
2010
+ console.log(chalk11.green("\u2705 Environment is healthy and ready for development!"));
1841
2011
  }
1842
2012
  console.log("");
1843
2013
  });
2014
+
2015
+ // src/utils/plugin-commands.ts
2016
+ import chalk12 from "chalk";
2017
+ async function loadPluginCommands(program) {
2018
+ let config;
2019
+ try {
2020
+ const loaded = await loadConfig();
2021
+ config = loaded.config;
2022
+ } catch {
2023
+ return;
2024
+ }
2025
+ const plugins = [
2026
+ ...config.plugins || [],
2027
+ ...config.devPlugins || []
2028
+ ];
2029
+ const contributions = [];
2030
+ for (const plugin of plugins) {
2031
+ if (!plugin || typeof plugin !== "object") continue;
2032
+ const p = plugin;
2033
+ const manifest = p.manifest;
2034
+ const contributes = manifest?.contributes ?? p.contributes;
2035
+ if (!contributes) continue;
2036
+ const commands = contributes.commands;
2037
+ if (!Array.isArray(commands)) continue;
2038
+ const pluginName = resolvePluginName2(p);
2039
+ for (const cmd of commands) {
2040
+ if (!cmd || typeof cmd.name !== "string") continue;
2041
+ contributions.push({
2042
+ name: cmd.name,
2043
+ description: typeof cmd.description === "string" ? cmd.description : void 0,
2044
+ module: typeof cmd.module === "string" ? cmd.module : void 0,
2045
+ pluginName
2046
+ });
2047
+ }
2048
+ }
2049
+ if (contributions.length === 0) return;
2050
+ for (const contribution of contributions) {
2051
+ try {
2052
+ const commands = await importPluginCommands(contribution);
2053
+ for (const cmd of commands) {
2054
+ program.addCommand(cmd);
2055
+ }
2056
+ } catch (error) {
2057
+ if (process.env.DEBUG) {
2058
+ const message = error instanceof Error ? error.message : String(error);
2059
+ console.error(
2060
+ chalk12.yellow(` \u26A0 Failed to load CLI command '${contribution.name}' from plugin '${contribution.pluginName}': ${message}`)
2061
+ );
2062
+ }
2063
+ }
2064
+ }
2065
+ }
2066
+ async function importPluginCommands(contribution) {
2067
+ const moduleId = contribution.module ? `${contribution.pluginName}/${contribution.module.replace(/^\.\//, "")}` : contribution.pluginName;
2068
+ const mod = await import(moduleId);
2069
+ if (Array.isArray(mod.commands)) {
2070
+ return mod.commands.filter(isCommandInstance);
2071
+ }
2072
+ const defaultExport = mod.default;
2073
+ if (defaultExport) {
2074
+ if (Array.isArray(defaultExport)) {
2075
+ return defaultExport.filter(isCommandInstance);
2076
+ }
2077
+ if (isCommandInstance(defaultExport)) {
2078
+ return [defaultExport];
2079
+ }
2080
+ }
2081
+ const commands = [];
2082
+ for (const key of Object.keys(mod)) {
2083
+ if (isCommandInstance(mod[key])) {
2084
+ commands.push(mod[key]);
2085
+ }
2086
+ }
2087
+ return commands;
2088
+ }
2089
+ function isCommandInstance(value) {
2090
+ if (value === null || typeof value !== "object") return false;
2091
+ const obj = value;
2092
+ return typeof obj.name === "function" && typeof obj.description === "function" && typeof obj.action === "function" && typeof obj.parse === "function";
2093
+ }
2094
+ function resolvePluginName2(plugin) {
2095
+ if (typeof plugin.name === "string") return plugin.name;
2096
+ const manifest = plugin.manifest;
2097
+ if (manifest && typeof manifest.name === "string") return manifest.name;
2098
+ if (plugin.constructor && plugin.constructor.name !== "Object") return plugin.constructor.name;
2099
+ return "unknown";
2100
+ }
1844
2101
  export {
1845
2102
  compileCommand,
1846
2103
  createCommand,
@@ -1849,6 +2106,8 @@ export {
1849
2106
  generateCommand,
1850
2107
  infoCommand,
1851
2108
  initCommand,
2109
+ loadPluginCommands,
2110
+ pluginCommand,
1852
2111
  serveCommand,
1853
2112
  templates,
1854
2113
  testCommand,