@zenstackhq/cli 3.0.0-alpha.1 → 3.0.0-alpha.11

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,78 +1,5 @@
1
1
  var __defProp = Object.defineProperty;
2
- var __getOwnPropNames = Object.getOwnPropertyNames;
3
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
4
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
- }) : x)(function(x) {
7
- if (typeof require !== "undefined") return require.apply(this, arguments);
8
- throw Error('Dynamic require of "' + x + '" is not supported');
9
- });
10
- var __commonJS = (cb, mod) => function __require2() {
11
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
12
- };
13
-
14
- // package.json
15
- var require_package = __commonJS({
16
- "package.json"(exports, module) {
17
- module.exports = {
18
- name: "@zenstackhq/cli",
19
- publisher: "zenstack",
20
- displayName: "ZenStack CLI",
21
- description: "FullStack database toolkit with built-in access control and automatic API generation.",
22
- version: "3.0.0-alpha.1",
23
- type: "module",
24
- author: {
25
- name: "ZenStack Team"
26
- },
27
- homepage: "https://zenstack.dev",
28
- license: "MIT",
29
- keywords: [
30
- "orm",
31
- "fullstack",
32
- "react",
33
- "typescript",
34
- "data modeling"
35
- ],
36
- bin: {
37
- zenstack: "bin/cli"
38
- },
39
- scripts: {
40
- build: "tsup-node",
41
- watch: "tsup-node --watch",
42
- lint: "eslint src --ext ts",
43
- test: "vitest",
44
- pack: "pnpm pack"
45
- },
46
- dependencies: {
47
- "@types/node": "^20.0.0",
48
- "@zenstackhq/language": "workspace:*",
49
- "@zenstackhq/sdk": "workspace:*",
50
- "async-exit-hook": "^2.0.1",
51
- colors: "1.4.0",
52
- commander: "^8.3.0",
53
- langium: "~3.3.0",
54
- ora: "^5.4.1",
55
- "package-manager-detector": "^1.3.0",
56
- "tiny-invariant": "^1.3.3",
57
- "ts-pattern": "^4.3.0"
58
- },
59
- peerDependencies: {
60
- prisma: "^6.0.0",
61
- typescript: "^5.0.0"
62
- },
63
- devDependencies: {
64
- "@types/async-exit-hook": "^2.0.0",
65
- "@types/better-sqlite3": "^7.6.13",
66
- "@types/semver": "^7.3.13",
67
- "@types/tmp": "^0.2.6",
68
- "@zenstackhq/runtime": "workspace:*",
69
- "@zenstackhq/testtools": "workspace:*",
70
- "better-sqlite3": "^11.8.1",
71
- tmp: "^0.2.3"
72
- }
73
- };
74
- }
75
- });
76
3
 
77
4
  // src/index.ts
78
5
  import { ZModelLanguageMetaData } from "@zenstackhq/language";
@@ -80,7 +7,7 @@ import colors5 from "colors";
80
7
  import { Command, Option } from "commander";
81
8
 
82
9
  // src/actions/db.ts
83
- import path2 from "node:path";
10
+ import fs2 from "fs";
84
11
 
85
12
  // src/utils/exec-utils.ts
86
13
  import { execSync as _exec } from "child_process";
@@ -105,7 +32,11 @@ function execPackage(cmd, options) {
105
32
  __name(execPackage, "execPackage");
106
33
 
107
34
  // src/actions/action-utils.ts
108
- import fs from "node:fs";
35
+ import { loadDocument } from "@zenstackhq/language";
36
+ import { PrismaSchemaGenerator } from "@zenstackhq/sdk";
37
+ import colors from "colors";
38
+ import fs from "fs";
39
+ import path from "path";
109
40
 
110
41
  // src/cli-error.ts
111
42
  var CliError = class extends Error {
@@ -115,8 +46,6 @@ var CliError = class extends Error {
115
46
  };
116
47
 
117
48
  // src/actions/action-utils.ts
118
- import { loadDocument } from "@zenstackhq/language";
119
- import colors from "colors";
120
49
  function getSchemaFile(file) {
121
50
  if (file) {
122
51
  if (!fs.existsSync(file)) {
@@ -124,6 +53,13 @@ function getSchemaFile(file) {
124
53
  }
125
54
  return file;
126
55
  }
56
+ const pkgJsonConfig = getPkgJsonConfig(process.cwd());
57
+ if (pkgJsonConfig.schema) {
58
+ if (!fs.existsSync(pkgJsonConfig.schema)) {
59
+ throw new CliError(`Schema file not found: ${pkgJsonConfig.schema}`);
60
+ }
61
+ return pkgJsonConfig.schema;
62
+ }
127
63
  if (fs.existsSync("./zenstack/schema.zmodel")) {
128
64
  return "./zenstack/schema.zmodel";
129
65
  } else if (fs.existsSync("./schema.zmodel")) {
@@ -153,37 +89,146 @@ function handleSubProcessError(err) {
153
89
  }
154
90
  }
155
91
  __name(handleSubProcessError, "handleSubProcessError");
92
+ async function generateTempPrismaSchema(zmodelPath, folder) {
93
+ const model = await loadSchemaDocument(zmodelPath);
94
+ const prismaSchema = await new PrismaSchemaGenerator(model).generate();
95
+ if (!folder) {
96
+ folder = path.dirname(zmodelPath);
97
+ }
98
+ const prismaSchemaFile = path.resolve(folder, "~schema.prisma");
99
+ fs.writeFileSync(prismaSchemaFile, prismaSchema);
100
+ return prismaSchemaFile;
101
+ }
102
+ __name(generateTempPrismaSchema, "generateTempPrismaSchema");
103
+ function getPkgJsonConfig(startPath) {
104
+ const result = {
105
+ schema: void 0,
106
+ output: void 0
107
+ };
108
+ const pkgJsonFile = findUp([
109
+ "package.json"
110
+ ], startPath, false);
111
+ if (!pkgJsonFile) {
112
+ return result;
113
+ }
114
+ let pkgJson = void 0;
115
+ try {
116
+ pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, "utf8"));
117
+ } catch {
118
+ return result;
119
+ }
120
+ if (pkgJson.zenstack && typeof pkgJson.zenstack === "object") {
121
+ result.schema = pkgJson.zenstack.schema && path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.schema);
122
+ result.output = pkgJson.zenstack.output && path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.output);
123
+ }
124
+ return result;
125
+ }
126
+ __name(getPkgJsonConfig, "getPkgJsonConfig");
127
+ function findUp(names, cwd = process.cwd(), multiple = false, result = []) {
128
+ if (!names.some((name) => !!name)) {
129
+ return void 0;
130
+ }
131
+ const target = names.find((name) => fs.existsSync(path.join(cwd, name)));
132
+ if (multiple === false && target) {
133
+ return path.join(cwd, target);
134
+ }
135
+ if (target) {
136
+ result.push(path.join(cwd, target));
137
+ }
138
+ const up = path.resolve(cwd, "..");
139
+ if (up === cwd) {
140
+ return multiple && result.length > 0 ? result : void 0;
141
+ }
142
+ return findUp(names, up, multiple, result);
143
+ }
144
+ __name(findUp, "findUp");
145
+
146
+ // src/actions/db.ts
147
+ async function run(command, options) {
148
+ switch (command) {
149
+ case "push":
150
+ await runPush(options);
151
+ break;
152
+ }
153
+ }
154
+ __name(run, "run");
155
+ async function runPush(options) {
156
+ const schemaFile = getSchemaFile(options.schema);
157
+ const prismaSchemaFile = await generateTempPrismaSchema(schemaFile);
158
+ try {
159
+ const cmd = [
160
+ "prisma db push",
161
+ ` --schema "${prismaSchemaFile}"`,
162
+ options.acceptDataLoss ? " --accept-data-loss" : "",
163
+ options.forceReset ? " --force-reset" : "",
164
+ " --skip-generate"
165
+ ].join("");
166
+ try {
167
+ await execPackage(cmd);
168
+ } catch (err) {
169
+ handleSubProcessError(err);
170
+ }
171
+ } finally {
172
+ if (fs2.existsSync(prismaSchemaFile)) {
173
+ fs2.unlinkSync(prismaSchemaFile);
174
+ }
175
+ }
176
+ }
177
+ __name(runPush, "runPush");
156
178
 
157
179
  // src/actions/generate.ts
180
+ import { invariant } from "@zenstackhq/common-helpers";
158
181
  import { isPlugin } from "@zenstackhq/language/ast";
159
- import { PrismaSchemaGenerator, TsSchemaGenerator } from "@zenstackhq/sdk";
182
+ import { PrismaSchemaGenerator as PrismaSchemaGenerator2, TsSchemaGenerator } from "@zenstackhq/sdk";
160
183
  import colors2 from "colors";
161
- import fs2 from "node:fs";
162
- import path from "node:path";
163
- import invariant from "tiny-invariant";
164
- async function run(options) {
184
+ import fs3 from "fs";
185
+ import path2 from "path";
186
+ async function run2(options) {
165
187
  const schemaFile = getSchemaFile(options.schema);
166
188
  const model = await loadSchemaDocument(schemaFile);
167
- const outputPath = options.output ?? path.dirname(schemaFile);
168
- const tsSchemaFile = path.join(outputPath, "schema.ts");
169
- await new TsSchemaGenerator().generate(schemaFile, [], tsSchemaFile);
189
+ const outputPath = getOutputPath(options, schemaFile);
190
+ const tsSchemaFile = path2.join(outputPath, "schema.ts");
191
+ await new TsSchemaGenerator().generate(schemaFile, [], outputPath);
170
192
  await runPlugins(model, outputPath, tsSchemaFile);
171
- const prismaSchema = await new PrismaSchemaGenerator(model).generate();
172
- fs2.writeFileSync(path.join(outputPath, "schema.prisma"), prismaSchema);
193
+ if (options.savePrismaSchema) {
194
+ const prismaSchema = await new PrismaSchemaGenerator2(model).generate();
195
+ let prismaSchemaFile = path2.join(outputPath, "schema.prisma");
196
+ if (typeof options.savePrismaSchema === "string") {
197
+ prismaSchemaFile = path2.resolve(outputPath, options.savePrismaSchema);
198
+ fs3.mkdirSync(path2.dirname(prismaSchemaFile), {
199
+ recursive: true
200
+ });
201
+ }
202
+ fs3.writeFileSync(prismaSchemaFile, prismaSchema);
203
+ }
173
204
  if (!options.silent) {
174
205
  console.log(colors2.green("Generation completed successfully."));
175
206
  console.log(`You can now create a ZenStack client with it.
176
207
 
177
- \`\`\`
208
+ \`\`\`ts
178
209
  import { ZenStackClient } from '@zenstackhq/runtime';
179
210
  import { schema } from '${outputPath}/schema';
180
211
 
181
- const client = new ZenStackClient(schema);
212
+ const client = new ZenStackClient(schema, {
213
+ dialectConfig: { ... }
214
+ });
182
215
  \`\`\`
183
216
  `);
184
217
  }
185
218
  }
186
- __name(run, "run");
219
+ __name(run2, "run");
220
+ function getOutputPath(options, schemaFile) {
221
+ if (options.output) {
222
+ return options.output;
223
+ }
224
+ const pkgJsonConfig = getPkgJsonConfig(process.cwd());
225
+ if (pkgJsonConfig.output) {
226
+ return pkgJsonConfig.output;
227
+ } else {
228
+ return path2.dirname(schemaFile);
229
+ }
230
+ }
231
+ __name(getOutputPath, "getOutputPath");
187
232
  async function runPlugins(model, outputPath, tsSchemaFile) {
188
233
  const plugins = model.declarations.filter(isPlugin);
189
234
  for (const plugin of plugins) {
@@ -205,36 +250,9 @@ async function runPlugins(model, outputPath, tsSchemaFile) {
205
250
  }
206
251
  __name(runPlugins, "runPlugins");
207
252
 
208
- // src/actions/db.ts
209
- async function run2(command, options) {
210
- const schemaFile = getSchemaFile(options.schema);
211
- await run({
212
- schema: schemaFile,
213
- silent: true
214
- });
215
- const prismaSchemaFile = path2.join(path2.dirname(schemaFile), "schema.prisma");
216
- switch (command) {
217
- case "push":
218
- await runPush(prismaSchemaFile, options);
219
- break;
220
- }
221
- }
222
- __name(run2, "run");
223
- async function runPush(prismaSchemaFile, options) {
224
- const cmd = `prisma db push --schema "${prismaSchemaFile}"${options.acceptDataLoss ? " --accept-data-loss" : ""}${options.forceReset ? " --force-reset" : ""} --skip-generate`;
225
- try {
226
- await execPackage(cmd, {
227
- stdio: "inherit"
228
- });
229
- } catch (err) {
230
- handleSubProcessError(err);
231
- }
232
- }
233
- __name(runPush, "runPush");
234
-
235
253
  // src/actions/info.ts
236
254
  import colors3 from "colors";
237
- import path3 from "node:path";
255
+ import path3 from "path";
238
256
  async function run3(projectPath) {
239
257
  const packages = await getZenStackPackages(projectPath);
240
258
  if (!packages) {
@@ -263,7 +281,7 @@ async function getZenStackPackages(projectPath) {
263
281
  type: "json"
264
282
  }
265
283
  })).default;
266
- } catch (err) {
284
+ } catch {
267
285
  return [];
268
286
  }
269
287
  const packages = Array.from(new Set([
@@ -294,8 +312,8 @@ __name(getZenStackPackages, "getZenStackPackages");
294
312
 
295
313
  // src/actions/init.ts
296
314
  import colors4 from "colors";
297
- import fs3 from "node:fs";
298
- import path4 from "node:path";
315
+ import fs4 from "fs";
316
+ import path4 from "path";
299
317
  import ora from "ora";
300
318
  import { detect, resolveCommand } from "package-manager-detector";
301
319
 
@@ -332,7 +350,7 @@ model Post {
332
350
  async function run4(projectPath) {
333
351
  const packages = [
334
352
  {
335
- name: "@zenstackhq@cli@next",
353
+ name: "@zenstackhq/cli@next",
336
354
  dev: true
337
355
  },
338
356
  {
@@ -370,11 +388,11 @@ async function run4(projectPath) {
370
388
  }
371
389
  }
372
390
  const generationFolder = "zenstack";
373
- if (!fs3.existsSync(path4.join(projectPath, generationFolder))) {
374
- fs3.mkdirSync(path4.join(projectPath, generationFolder));
391
+ if (!fs4.existsSync(path4.join(projectPath, generationFolder))) {
392
+ fs4.mkdirSync(path4.join(projectPath, generationFolder));
375
393
  }
376
- if (!fs3.existsSync(path4.join(projectPath, generationFolder, "schema.zmodel"))) {
377
- fs3.writeFileSync(path4.join(projectPath, generationFolder, "schema.zmodel"), STARTER_ZMODEL);
394
+ if (!fs4.existsSync(path4.join(projectPath, generationFolder, "schema.zmodel"))) {
395
+ fs4.writeFileSync(path4.join(projectPath, generationFolder, "schema.zmodel"), STARTER_ZMODEL);
378
396
  } else {
379
397
  console.log(colors4.yellow("Schema file already exists. Skipping generation of sample."));
380
398
  }
@@ -385,35 +403,44 @@ async function run4(projectPath) {
385
403
  __name(run4, "run");
386
404
 
387
405
  // src/actions/migrate.ts
388
- import path5 from "node:path";
406
+ import fs5 from "fs";
407
+ import path5 from "path";
389
408
  async function run5(command, options) {
390
409
  const schemaFile = getSchemaFile(options.schema);
391
- await run({
392
- schema: schemaFile,
393
- silent: true
394
- });
395
- const prismaSchemaFile = path5.join(path5.dirname(schemaFile), "schema.prisma");
396
- switch (command) {
397
- case "dev":
398
- await runDev(prismaSchemaFile, options);
399
- break;
400
- case "reset":
401
- await runReset(prismaSchemaFile, options);
402
- break;
403
- case "deploy":
404
- await runDeploy(prismaSchemaFile, options);
405
- break;
406
- case "status":
407
- await runStatus(prismaSchemaFile, options);
408
- break;
410
+ const prismaSchemaDir = options.migrations ? path5.dirname(options.migrations) : void 0;
411
+ const prismaSchemaFile = await generateTempPrismaSchema(schemaFile, prismaSchemaDir);
412
+ try {
413
+ switch (command) {
414
+ case "dev":
415
+ await runDev(prismaSchemaFile, options);
416
+ break;
417
+ case "reset":
418
+ await runReset(prismaSchemaFile, options);
419
+ break;
420
+ case "deploy":
421
+ await runDeploy(prismaSchemaFile, options);
422
+ break;
423
+ case "status":
424
+ await runStatus(prismaSchemaFile, options);
425
+ break;
426
+ }
427
+ } finally {
428
+ if (fs5.existsSync(prismaSchemaFile)) {
429
+ fs5.unlinkSync(prismaSchemaFile);
430
+ }
409
431
  }
410
432
  }
411
433
  __name(run5, "run");
412
- async function runDev(prismaSchemaFile, _options) {
434
+ async function runDev(prismaSchemaFile, options) {
413
435
  try {
414
- await execPackage(`prisma migrate dev --schema "${prismaSchemaFile}" --skip-generate`, {
415
- stdio: "inherit"
416
- });
436
+ const cmd = [
437
+ "prisma migrate dev",
438
+ ` --schema "${prismaSchemaFile}"`,
439
+ " --skip-generate",
440
+ options.name ? ` --name ${options.name}` : "",
441
+ options.createOnly ? " --create-only" : ""
442
+ ].join("");
443
+ await execPackage(cmd);
417
444
  } catch (err) {
418
445
  handleSubProcessError2(err);
419
446
  }
@@ -421,9 +448,12 @@ async function runDev(prismaSchemaFile, _options) {
421
448
  __name(runDev, "runDev");
422
449
  async function runReset(prismaSchemaFile, options) {
423
450
  try {
424
- await execPackage(`prisma migrate reset --schema "${prismaSchemaFile}"${options.force ? " --force" : ""}`, {
425
- stdio: "inherit"
426
- });
451
+ const cmd = [
452
+ "prisma migrate reset",
453
+ ` --schema "${prismaSchemaFile}"`,
454
+ options.force ? " --force" : ""
455
+ ].join("");
456
+ await execPackage(cmd);
427
457
  } catch (err) {
428
458
  handleSubProcessError2(err);
429
459
  }
@@ -431,9 +461,11 @@ async function runReset(prismaSchemaFile, options) {
431
461
  __name(runReset, "runReset");
432
462
  async function runDeploy(prismaSchemaFile, _options) {
433
463
  try {
434
- await execPackage(`prisma migrate deploy --schema "${prismaSchemaFile}"`, {
435
- stdio: "inherit"
436
- });
464
+ const cmd = [
465
+ "prisma migrate deploy",
466
+ ` --schema "${prismaSchemaFile}"`
467
+ ].join("");
468
+ await execPackage(cmd);
437
469
  } catch (err) {
438
470
  handleSubProcessError2(err);
439
471
  }
@@ -441,9 +473,7 @@ async function runDeploy(prismaSchemaFile, _options) {
441
473
  __name(runDeploy, "runDeploy");
442
474
  async function runStatus(prismaSchemaFile, _options) {
443
475
  try {
444
- await execPackage(`prisma migrate status --schema "${prismaSchemaFile}"`, {
445
- stdio: "inherit"
446
- });
476
+ await execPackage(`prisma migrate status --schema "${prismaSchemaFile}"`);
447
477
  } catch (err) {
448
478
  handleSubProcessError2(err);
449
479
  }
@@ -459,28 +489,28 @@ function handleSubProcessError2(err) {
459
489
  __name(handleSubProcessError2, "handleSubProcessError");
460
490
 
461
491
  // src/utils/version-utils.ts
492
+ import fs6 from "fs";
493
+ import path6 from "path";
494
+ import { fileURLToPath } from "url";
462
495
  function getVersion() {
463
496
  try {
464
- return __require("../package.json").version;
497
+ const _dirname = typeof __dirname !== "undefined" ? __dirname : path6.dirname(fileURLToPath(import.meta.url));
498
+ return JSON.parse(fs6.readFileSync(path6.join(_dirname, "../package.json"), "utf8")).version;
465
499
  } catch {
466
- try {
467
- return require_package().version;
468
- } catch {
469
- return void 0;
470
- }
500
+ return void 0;
471
501
  }
472
502
  }
473
503
  __name(getVersion, "getVersion");
474
504
 
475
505
  // src/index.ts
476
506
  var generateAction = /* @__PURE__ */ __name(async (options) => {
477
- await run(options);
507
+ await run2(options);
478
508
  }, "generateAction");
479
509
  var migrateAction = /* @__PURE__ */ __name(async (command, options) => {
480
510
  await run5(command, options);
481
511
  }, "migrateAction");
482
512
  var dbAction = /* @__PURE__ */ __name(async (command, options) => {
483
- await run2(command, options);
513
+ await run(command, options);
484
514
  }, "dbAction");
485
515
  var infoAction = /* @__PURE__ */ __name(async (projectPath) => {
486
516
  await run3(projectPath);
@@ -496,12 +526,13 @@ function createProgram() {
496
526
 
497
527
  Documentation: https://zenstack.dev.`).showHelpAfterError().showSuggestionAfterError();
498
528
  const schemaOption = new Option("--schema <file>", `schema file (with extension ${schemaExtensions}). Defaults to "schema.zmodel" unless specified in package.json.`);
499
- program2.command("generate").description("Run code generation.").addOption(schemaOption).addOption(new Option("-o, --output <path>", "default output directory for core plugins")).action(generateAction);
529
+ program2.command("generate").description("Run code generation.").addOption(schemaOption).addOption(new Option("--silent", "do not print any output")).addOption(new Option("--save-prisma-schema [path]", "save a Prisma schema file, by default into the output directory")).addOption(new Option("-o, --output <path>", "default output directory for core plugins")).action(generateAction);
500
530
  const migrateCommand = program2.command("migrate").description("Update the database schema with migrations.");
501
- migrateCommand.command("dev").addOption(schemaOption).addOption(new Option("-n, --name <name>", "migration name")).addOption(new Option("--create-only", "only create migration, do not apply")).description("Create a migration from changes in schema and apply it to the database.").action((options) => migrateAction("dev", options));
502
- migrateCommand.command("reset").addOption(schemaOption).addOption(new Option("--force", "skip the confirmation prompt")).description("Reset your database and apply all migrations, all data will be lost.").action((options) => migrateAction("reset", options));
503
- migrateCommand.command("deploy").addOption(schemaOption).description("Deploy your pending migrations to your production/staging database.").action((options) => migrateAction("deploy", options));
504
- migrateCommand.command("status").addOption(schemaOption).description("check the status of your database migrations.").action((options) => migrateAction("status", options));
531
+ const migrationsOption = new Option("--migrations <path>", "path for migrations");
532
+ migrateCommand.command("dev").addOption(schemaOption).addOption(new Option("-n, --name <name>", "migration name")).addOption(new Option("--create-only", "only create migration, do not apply")).addOption(migrationsOption).description("Create a migration from changes in schema and apply it to the database.").action((options) => migrateAction("dev", options));
533
+ migrateCommand.command("reset").addOption(schemaOption).addOption(new Option("--force", "skip the confirmation prompt")).addOption(migrationsOption).description("Reset your database and apply all migrations, all data will be lost.").action((options) => migrateAction("reset", options));
534
+ migrateCommand.command("deploy").addOption(schemaOption).addOption(migrationsOption).description("Deploy your pending migrations to your production/staging database.").action((options) => migrateAction("deploy", options));
535
+ migrateCommand.command("status").addOption(schemaOption).addOption(migrationsOption).description("check the status of your database migrations.").action((options) => migrateAction("status", options));
505
536
  const dbCommand = program2.command("db").description("Manage your database schema during development.");
506
537
  dbCommand.command("push").description("Push the state from your schema to your database").addOption(schemaOption).addOption(new Option("--accept-data-loss", "ignore data loss warnings")).addOption(new Option("--force-reset", "force a reset of the database before push")).action((options) => dbAction("push", options));
507
538
  program2.command("info").description("Get information of installed ZenStack and related packages.").argument("[path]", "project path", ".").action(infoAction);