@dsmrt/axiom-cli 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @dsmrt/axiom-cli
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6c846be: adding cli subcommand for init for config files
8
+
9
+ ## 1.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - b1a56bc: adding debug logging to the cli and config packages
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [b1a56bc]
18
+ - @dsmrt/axiom-config@1.1.0
19
+
3
20
  ## 1.0.0
4
21
 
5
22
  ### Major Changes
package/dist/bin/axiom.js CHANGED
@@ -28,6 +28,16 @@ var import_yargs = __toESM(require("yargs"));
28
28
 
29
29
  // src/commands/config.ts
30
30
  var import_axiom_config = require("@dsmrt/axiom-config");
31
+
32
+ // src/debug.ts
33
+ var isDebugEnabled = () => process.env.AXIOM_DEBUG === "true" || process.env.AXIOM_DEBUG === "1";
34
+ var debug = (message, ...args) => {
35
+ if (isDebugEnabled()) {
36
+ console.error(`[axiom:cli] ${message}`, ...args);
37
+ }
38
+ };
39
+
40
+ // src/commands/config.ts
31
41
  var Config = class {
32
42
  command = "config";
33
43
  describe = "print the config";
@@ -38,13 +48,230 @@ var Config = class {
38
48
  return args;
39
49
  };
40
50
  handler = async (args) => {
41
- console.log(JSON.stringify(await this.loadConfig(args)));
51
+ debug(`Config command handler called with env: ${args.env}`);
52
+ const config = await this.loadConfig(args);
53
+ debug(`Config loaded successfully, outputting as JSON`);
54
+ console.log(JSON.stringify(config));
42
55
  };
43
56
  loadConfig = async (args) => {
57
+ debug(`Loading config with env: ${args.env}`);
44
58
  return (0, import_axiom_config.loadConfig)(args);
45
59
  };
46
60
  };
47
61
 
62
+ // src/commands/init.ts
63
+ var import_node_fs = require("fs");
64
+ var import_node_path = require("path");
65
+ var import_inquirer = __toESM(require("inquirer"));
66
+ var Init = class {
67
+ command = "init";
68
+ describe = "Initialize Axiom configuration files";
69
+ builder = (args) => {
70
+ args.option("force", {
71
+ alias: "f",
72
+ type: "boolean",
73
+ description: "Overwrite existing config files",
74
+ default: false
75
+ }).option("name", {
76
+ type: "string",
77
+ description: "Project name"
78
+ }).option("account", {
79
+ type: "string",
80
+ description: "AWS account ID"
81
+ }).option("region", {
82
+ type: "string",
83
+ description: "AWS region"
84
+ }).option("profile", {
85
+ type: "string",
86
+ description: "AWS profile name"
87
+ }).option("custom-types", {
88
+ type: "boolean",
89
+ description: "Create custom types file for extending config"
90
+ });
91
+ return args;
92
+ };
93
+ handler = async (args) => {
94
+ debug(`Init command handler called with args: ${JSON.stringify(args)}`);
95
+ const cwd = process.cwd();
96
+ if (!(0, import_node_fs.existsSync)(cwd)) {
97
+ (0, import_node_fs.mkdirSync)(cwd, { recursive: true });
98
+ }
99
+ const configPath = (0, import_node_path.join)(cwd, ".axiom.ts");
100
+ const devConfigPath = (0, import_node_path.join)(cwd, ".axiom.dev.ts");
101
+ const typesPath = (0, import_node_path.join)(cwd, "axiom.config.d.ts");
102
+ if (!args.force) {
103
+ const existingFiles = [];
104
+ if ((0, import_node_fs.existsSync)(configPath)) existingFiles.push(".axiom.ts");
105
+ if ((0, import_node_fs.existsSync)(devConfigPath)) existingFiles.push(".axiom.dev.ts");
106
+ if ((0, import_node_fs.existsSync)(typesPath)) existingFiles.push("axiom.config.d.ts");
107
+ if (existingFiles.length > 0) {
108
+ console.error(
109
+ `\u274C Config files already exist: ${existingFiles.join(", ")}`
110
+ );
111
+ console.error(" Use --force to overwrite them.");
112
+ process.exit(1);
113
+ }
114
+ }
115
+ const answers = await import_inquirer.default.prompt([
116
+ {
117
+ type: "input",
118
+ name: "name",
119
+ message: "Project name:",
120
+ default: args.name || "my-app",
121
+ when: !args.name
122
+ },
123
+ {
124
+ type: "input",
125
+ name: "account",
126
+ message: "AWS account ID:",
127
+ default: args.account || "123456789012",
128
+ when: !args.account,
129
+ validate: (input) => {
130
+ if (/^\d{12}$/.test(input)) return true;
131
+ return "AWS account ID must be 12 digits";
132
+ }
133
+ },
134
+ {
135
+ type: "input",
136
+ name: "region",
137
+ message: "AWS region:",
138
+ default: args.region || "us-east-1",
139
+ when: !args.region
140
+ },
141
+ {
142
+ type: "input",
143
+ name: "profile",
144
+ message: "AWS profile name:",
145
+ default: args.profile || "default",
146
+ when: !args.profile
147
+ },
148
+ {
149
+ type: "confirm",
150
+ name: "createCustomTypes",
151
+ message: "Would you like to add custom config properties?",
152
+ default: true,
153
+ when: args.customTypes === void 0
154
+ }
155
+ ]);
156
+ const config = {
157
+ name: args.name || answers.name,
158
+ account: args.account || answers.account,
159
+ region: args.region || answers.region,
160
+ profile: args.profile || answers.profile,
161
+ createCustomTypes: args.customTypes !== void 0 ? args.customTypes : answers.createCustomTypes
162
+ };
163
+ debug(`Creating config files with config: ${JSON.stringify(config)}`);
164
+ if (config.createCustomTypes) {
165
+ const typesContent = this.generateTypesFile();
166
+ (0, import_node_fs.writeFileSync)(typesPath, typesContent);
167
+ console.log(`\u2705 Created ${typesPath}`);
168
+ debug(`Created types file: ${typesPath}`);
169
+ }
170
+ const baseConfigContent = this.generateBaseConfig(
171
+ config,
172
+ config.createCustomTypes
173
+ );
174
+ (0, import_node_fs.writeFileSync)(configPath, baseConfigContent);
175
+ console.log(`\u2705 Created ${configPath}`);
176
+ debug(`Created base config: ${configPath}`);
177
+ const devConfigContent = this.generateDevConfig(config.createCustomTypes);
178
+ (0, import_node_fs.writeFileSync)(devConfigPath, devConfigContent);
179
+ console.log(`\u2705 Created ${devConfigPath}`);
180
+ debug(`Created dev config: ${devConfigPath}`);
181
+ console.log("\n\u{1F389} Axiom configuration initialized successfully!");
182
+ console.log("\n\u{1F4DD} Next steps:");
183
+ console.log(" 1. Edit .axiom.ts to customize your base configuration");
184
+ console.log(
185
+ " 2. Edit .axiom.dev.ts to customize your development configuration"
186
+ );
187
+ if (config.createCustomTypes) {
188
+ console.log(
189
+ " 3. Edit axiom.config.d.ts to add your custom config properties"
190
+ );
191
+ }
192
+ console.log(
193
+ ` ${config.createCustomTypes ? "4" : "3"}. Run 'axiom config' to verify your configuration`
194
+ );
195
+ };
196
+ generateTypesFile() {
197
+ return `/**
198
+ * Custom Axiom Configuration Types
199
+ *
200
+ * Extend this interface to add custom properties to your Axiom config.
201
+ * These properties will be type-safe when using loadConfig() in your code.
202
+ *
203
+ * Example:
204
+ *
205
+ * export interface CustomConfig {
206
+ * apiUrl: string;
207
+ * apiKey: string;
208
+ * maxRetries: number;
209
+ * features: {
210
+ * authentication: boolean;
211
+ * analytics: boolean;
212
+ * };
213
+ * }
214
+ */
215
+
216
+ export interface CustomConfig {
217
+ // Add your custom properties here
218
+ // Example:
219
+ // apiUrl: string;
220
+ // maxRetries: number;
221
+ }
222
+ `;
223
+ }
224
+ generateBaseConfig(config, hasCustomTypes) {
225
+ const importStatement = hasCustomTypes ? `import type { Config } from "@dsmrt/axiom-config";
226
+ import type { CustomConfig } from "./axiom.config";
227
+
228
+ type AxiomConfig = Config & CustomConfig;
229
+
230
+ const config: AxiomConfig = {` : `import type { Config } from "@dsmrt/axiom-config";
231
+
232
+ const config: Config = {`;
233
+ return `${importStatement}
234
+ name: "${config.name}",
235
+ env: "prod",
236
+ aws: {
237
+ account: "${config.account}",
238
+ region: "${config.region}",
239
+ profile: "${config.profile}",
240
+ },
241
+ baseParameterPath: "/${config.name}/prod",${hasCustomTypes ? `
242
+
243
+ // Add your custom config properties here
244
+ // Example:
245
+ // apiUrl: "https://api.example.com",
246
+ // maxRetries: 3,` : ""}
247
+ };
248
+
249
+ export default config;
250
+ `;
251
+ }
252
+ generateDevConfig(hasCustomTypes) {
253
+ const importStatement = hasCustomTypes ? `import type { Config } from "@dsmrt/axiom-config";
254
+ import type { CustomConfig } from "./axiom.config";
255
+
256
+ type AxiomConfig = Config & CustomConfig;
257
+
258
+ const config: Partial<AxiomConfig> = {` : `import type { Config } from "@dsmrt/axiom-config";
259
+
260
+ const config: Partial<Config> = {`;
261
+ return `${importStatement}
262
+ env: "dev",
263
+ baseParameterPath: undefined, // Will be auto-generated as /{name}/dev${hasCustomTypes ? `,
264
+
265
+ // Override custom config properties for dev environment
266
+ // Example:
267
+ // apiUrl: "https://api.dev.example.com",` : ""}
268
+ };
269
+
270
+ export default config;
271
+ `;
272
+ }
273
+ };
274
+
48
275
  // src/commands/params/base.ts
49
276
  var ParamsCommand = class {
50
277
  command = "params";
@@ -67,10 +294,10 @@ var import_chalk = __toESM(require("chalk"));
67
294
  // src/aws/credentials-provider.ts
68
295
  var import_credential_providers = require("@aws-sdk/credential-providers");
69
296
  var import_client_sts = require("@aws-sdk/client-sts");
70
- var import_inquirer = __toESM(require("inquirer"));
297
+ var import_inquirer2 = __toESM(require("inquirer"));
71
298
 
72
299
  // src/cache.ts
73
- var import_node_fs = __toESM(require("fs"));
300
+ var import_node_fs2 = __toESM(require("fs"));
74
301
  var import_node_os = __toESM(require("os"));
75
302
  var DEFAULT_DIRECTORY = `${import_node_os.default.homedir()}/.axiom/cache`;
76
303
  var cache_default = class {
@@ -79,10 +306,10 @@ var cache_default = class {
79
306
  }
80
307
  get(name) {
81
308
  const file = `${this.cacheDir}/${name}`;
82
- if (!import_node_fs.default.existsSync(file)) {
309
+ if (!import_node_fs2.default.existsSync(file)) {
83
310
  return;
84
311
  }
85
- const buffer = import_node_fs.default.readFileSync(file);
312
+ const buffer = import_node_fs2.default.readFileSync(file);
86
313
  const item = JSON.parse(buffer.toString());
87
314
  if (item.expires === void 0) {
88
315
  return item.data;
@@ -95,16 +322,16 @@ var cache_default = class {
95
322
  return item.data;
96
323
  }
97
324
  delete(name) {
98
- import_node_fs.default.unlinkSync(`${this.cacheDir}/${name}`);
325
+ import_node_fs2.default.unlinkSync(`${this.cacheDir}/${name}`);
99
326
  }
100
327
  set(name, value, expires) {
101
- if (!import_node_fs.default.existsSync(this.cacheDir)) {
102
- import_node_fs.default.mkdirSync(this.cacheDir, {
328
+ if (!import_node_fs2.default.existsSync(this.cacheDir)) {
329
+ import_node_fs2.default.mkdirSync(this.cacheDir, {
103
330
  recursive: true,
104
331
  mode: 448
105
332
  });
106
333
  }
107
- import_node_fs.default.writeFileSync(
334
+ import_node_fs2.default.writeFileSync(
108
335
  `${this.cacheDir}/${name}`,
109
336
  JSON.stringify({
110
337
  expires,
@@ -159,7 +386,7 @@ var roleAssumerCallable = (config) => {
159
386
  };
160
387
  };
161
388
  var mfaCodeProvider = async (mfaSerial) => {
162
- const mfaCode = await import_inquirer.default.prompt({
389
+ const mfaCode = await import_inquirer2.default.prompt({
163
390
  name: "code",
164
391
  message: `Enter MFA code for ${mfaSerial}: `,
165
392
  type: "password"
@@ -215,7 +442,16 @@ Example:
215
442
  return args;
216
443
  };
217
444
  handler = async (args) => {
445
+ debug(
446
+ `Get command handler called with env: ${args.env}, path: ${args.path}`
447
+ );
218
448
  const config = await (0, import_axiom_config2.loadConfig)({ env: args.env });
449
+ debug(
450
+ `Config loaded successfully, base parameter path: ${config.aws?.baseParameterPath}`
451
+ );
452
+ debug(
453
+ `Creating SSM client with region: ${config.aws.region}, profile: ${config.aws.profile}`
454
+ );
219
455
  const collection = new import_axiom_aws_sdk.ParameterCollection(
220
456
  config.aws?.baseParameterPath,
221
457
  new import_client_ssm.SSMClient({
@@ -228,7 +464,9 @@ Example:
228
464
  })
229
465
  })
230
466
  );
467
+ debug(`Fetching parameters from SSM...`);
231
468
  const params = await collection.get();
469
+ debug(`Retrieved ${params.size} parameters`);
232
470
  params.forEach((parameter) => {
233
471
  console.log(
234
472
  import_chalk.default.gray(
@@ -244,7 +482,7 @@ Example:
244
482
  var import_client_ssm2 = require("@aws-sdk/client-ssm");
245
483
  var import_axiom_config3 = require("@dsmrt/axiom-config");
246
484
  var import_chalk2 = __toESM(require("chalk"));
247
- var import_inquirer2 = __toESM(require("inquirer"));
485
+ var import_inquirer3 = __toESM(require("inquirer"));
248
486
 
249
487
  // src/commands/params/utils.ts
250
488
  var buildPath = (config, path) => {
@@ -294,30 +532,50 @@ Example: "/root/myParam" or "service/secret"`
294
532
  });
295
533
  };
296
534
  handler = async (args) => {
535
+ debug(
536
+ `Set command handler called with path: ${args.path}, secure: ${args.secure}, overwrite: ${args.overwrite}, force: ${args.force}`
537
+ );
297
538
  const config = await (0, import_axiom_config3.loadConfig)({ env: args.env });
539
+ debug(
540
+ `Config loaded successfully, base parameter path: ${config.aws?.baseParameterPath}`
541
+ );
542
+ const fullPath = buildPath(config, args.path);
543
+ debug(`Full parameter path: ${fullPath}`);
298
544
  if (args.force !== true) {
299
- const res = await import_inquirer2.default.prompt({
545
+ debug(`Prompting user for confirmation...`);
546
+ const res = await import_inquirer3.default.prompt({
300
547
  type: "confirm",
301
548
  name: "setParam",
302
549
  message: `Are you sure you want to set '${args.path}'?`
303
550
  });
304
551
  if (!res.setParam) {
552
+ debug(`User declined, aborting`);
305
553
  console.log("Doing nothing.");
306
554
  return;
307
555
  }
556
+ debug(`User confirmed`);
557
+ } else {
558
+ debug(`Force flag set, skipping confirmation`);
308
559
  }
560
+ debug(
561
+ `Creating SSM client with region: ${config.aws.region}, profile: ${config.aws.profile}`
562
+ );
309
563
  const client = new import_client_ssm2.SSMClient({
310
564
  region: config.aws.region,
311
565
  credentials: await CachedCredentialProvider(config.aws)
312
566
  });
567
+ debug(
568
+ `Sending parameter to SSM with type: ${args.secure ? "SecureString" : "String"}`
569
+ );
313
570
  const params = await client.send(
314
571
  new import_client_ssm2.PutParameterCommand({
315
- Name: buildPath(config, args.path),
572
+ Name: fullPath,
316
573
  Value: args.value,
317
574
  Type: args.secure ? import_client_ssm2.ParameterType.SECURE_STRING : import_client_ssm2.ParameterType.STRING,
318
575
  Overwrite: args.overwrite
319
576
  })
320
577
  );
578
+ debug(`Parameter set successfully, version: ${params.Version}`);
321
579
  console.log(import_chalk2.default.green("Version: "), import_chalk2.default.white.bold(params.Version));
322
580
  };
323
581
  };
@@ -325,7 +583,7 @@ Example: "/root/myParam" or "service/secret"`
325
583
  // src/commands/params/delete.ts
326
584
  var import_client_ssm3 = require("@aws-sdk/client-ssm");
327
585
  var import_axiom_config4 = require("@dsmrt/axiom-config");
328
- var import_inquirer3 = __toESM(require("inquirer"));
586
+ var import_inquirer4 = __toESM(require("inquirer"));
329
587
  var log = console.log;
330
588
  var DeleteCommand = class {
331
589
  command = "delete <path>";
@@ -347,33 +605,51 @@ Example: "/root/myParam" or "service/secret"`,
347
605
  return args;
348
606
  };
349
607
  handler = async (args) => {
608
+ debug(
609
+ `Delete command handler called with path: ${args.path}, force: ${args.force}`
610
+ );
350
611
  const config = await (0, import_axiom_config4.loadConfig)({ env: args.env });
612
+ debug(
613
+ `Config loaded successfully, base parameter path: ${config.aws?.baseParameterPath}`
614
+ );
351
615
  const path = buildPath(args, args.path);
616
+ debug(`Full parameter path: ${path}`);
617
+ debug(
618
+ `Creating SSM client with region: ${config.aws?.region}, profile: ${config.aws?.profile}`
619
+ );
352
620
  const client = new import_client_ssm3.SSMClient({
353
621
  region: config.aws?.region,
354
622
  credentials: await CachedCredentialProvider(config.aws)
355
623
  });
356
624
  if (path === args.aws.baseParameterPath) {
625
+ debug(`Delete blocked: path matches base parameter path`);
357
626
  throw new Error(
358
627
  `Deleting the path (path: ${path}) that matches the base path (awsSsmParameterPath: ${args.awsSsmParameterPath}) is prohibited.`
359
628
  );
360
629
  }
361
630
  if (args.force !== true) {
362
- const res = await import_inquirer3.default.prompt({
631
+ debug(`Prompting user for confirmation...`);
632
+ const res = await import_inquirer4.default.prompt({
363
633
  type: "confirm",
364
634
  name: "delete",
365
635
  message: `Are you sure you want to delete '${path}'?`
366
636
  });
367
637
  if (!res.delete) {
638
+ debug(`User declined, aborting`);
368
639
  log("Doing nothing.");
369
640
  return;
370
641
  }
642
+ debug(`User confirmed`);
643
+ } else {
644
+ debug(`Force flag set, skipping confirmation`);
371
645
  }
646
+ debug(`Sending delete command to SSM for: ${path}`);
372
647
  await client.send(
373
648
  new import_client_ssm3.DeleteParameterCommand({
374
649
  Name: path
375
650
  })
376
651
  );
652
+ debug(`Parameter deleted successfully`);
377
653
  log("\u{1F44D}");
378
654
  };
379
655
  };
@@ -382,7 +658,17 @@ Example: "/root/myParam" or "service/secret"`,
382
658
  (0, import_yargs.default)(process.argv.slice(2)).env("AXIOM").scriptName("axiom").option("config", {
383
659
  alias: "c",
384
660
  string: true
385
- }).command(new Config()).command(new ParamsCommand()).strict().usage(
661
+ }).option("debug", {
662
+ alias: "d",
663
+ type: "boolean",
664
+ description: "Enable debug output",
665
+ default: false,
666
+ global: true
667
+ }).middleware((argv) => {
668
+ if (argv.debug) {
669
+ process.env.AXIOM_DEBUG = "true";
670
+ }
671
+ }).command(new Init()).command(new Config()).command(new ParamsCommand()).strict().usage(
386
672
  `
387
673
  Axiom - an AWS focused config cli
388
674
 
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import "../chunk-72DOPOLO.mjs";
2
+ import "../chunk-QRHY3BEA.mjs";