@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/dist/index.js CHANGED
@@ -33,6 +33,7 @@ __export(index_exports, {
33
33
  Config: () => Config,
34
34
  DeleteCommand: () => DeleteCommand,
35
35
  GetCommand: () => GetCommand,
36
+ Init: () => Init,
36
37
  ParamsCommand: () => ParamsCommand,
37
38
  SetCommand: () => SetCommand,
38
39
  awsOptions: () => awsOptions,
@@ -45,6 +46,16 @@ var import_yargs = __toESM(require("yargs"));
45
46
 
46
47
  // src/commands/config.ts
47
48
  var import_axiom_config = require("@dsmrt/axiom-config");
49
+
50
+ // src/debug.ts
51
+ var isDebugEnabled = () => process.env.AXIOM_DEBUG === "true" || process.env.AXIOM_DEBUG === "1";
52
+ var debug = (message, ...args) => {
53
+ if (isDebugEnabled()) {
54
+ console.error(`[axiom:cli] ${message}`, ...args);
55
+ }
56
+ };
57
+
58
+ // src/commands/config.ts
48
59
  var Config = class {
49
60
  command = "config";
50
61
  describe = "print the config";
@@ -55,13 +66,230 @@ var Config = class {
55
66
  return args;
56
67
  };
57
68
  handler = async (args) => {
58
- console.log(JSON.stringify(await this.loadConfig(args)));
69
+ debug(`Config command handler called with env: ${args.env}`);
70
+ const config = await this.loadConfig(args);
71
+ debug(`Config loaded successfully, outputting as JSON`);
72
+ console.log(JSON.stringify(config));
59
73
  };
60
74
  loadConfig = async (args) => {
75
+ debug(`Loading config with env: ${args.env}`);
61
76
  return (0, import_axiom_config.loadConfig)(args);
62
77
  };
63
78
  };
64
79
 
80
+ // src/commands/init.ts
81
+ var import_node_fs = require("fs");
82
+ var import_node_path = require("path");
83
+ var import_inquirer = __toESM(require("inquirer"));
84
+ var Init = class {
85
+ command = "init";
86
+ describe = "Initialize Axiom configuration files";
87
+ builder = (args) => {
88
+ args.option("force", {
89
+ alias: "f",
90
+ type: "boolean",
91
+ description: "Overwrite existing config files",
92
+ default: false
93
+ }).option("name", {
94
+ type: "string",
95
+ description: "Project name"
96
+ }).option("account", {
97
+ type: "string",
98
+ description: "AWS account ID"
99
+ }).option("region", {
100
+ type: "string",
101
+ description: "AWS region"
102
+ }).option("profile", {
103
+ type: "string",
104
+ description: "AWS profile name"
105
+ }).option("custom-types", {
106
+ type: "boolean",
107
+ description: "Create custom types file for extending config"
108
+ });
109
+ return args;
110
+ };
111
+ handler = async (args) => {
112
+ debug(`Init command handler called with args: ${JSON.stringify(args)}`);
113
+ const cwd = process.cwd();
114
+ if (!(0, import_node_fs.existsSync)(cwd)) {
115
+ (0, import_node_fs.mkdirSync)(cwd, { recursive: true });
116
+ }
117
+ const configPath = (0, import_node_path.join)(cwd, ".axiom.ts");
118
+ const devConfigPath = (0, import_node_path.join)(cwd, ".axiom.dev.ts");
119
+ const typesPath = (0, import_node_path.join)(cwd, "axiom.config.d.ts");
120
+ if (!args.force) {
121
+ const existingFiles = [];
122
+ if ((0, import_node_fs.existsSync)(configPath)) existingFiles.push(".axiom.ts");
123
+ if ((0, import_node_fs.existsSync)(devConfigPath)) existingFiles.push(".axiom.dev.ts");
124
+ if ((0, import_node_fs.existsSync)(typesPath)) existingFiles.push("axiom.config.d.ts");
125
+ if (existingFiles.length > 0) {
126
+ console.error(
127
+ `\u274C Config files already exist: ${existingFiles.join(", ")}`
128
+ );
129
+ console.error(" Use --force to overwrite them.");
130
+ process.exit(1);
131
+ }
132
+ }
133
+ const answers = await import_inquirer.default.prompt([
134
+ {
135
+ type: "input",
136
+ name: "name",
137
+ message: "Project name:",
138
+ default: args.name || "my-app",
139
+ when: !args.name
140
+ },
141
+ {
142
+ type: "input",
143
+ name: "account",
144
+ message: "AWS account ID:",
145
+ default: args.account || "123456789012",
146
+ when: !args.account,
147
+ validate: (input) => {
148
+ if (/^\d{12}$/.test(input)) return true;
149
+ return "AWS account ID must be 12 digits";
150
+ }
151
+ },
152
+ {
153
+ type: "input",
154
+ name: "region",
155
+ message: "AWS region:",
156
+ default: args.region || "us-east-1",
157
+ when: !args.region
158
+ },
159
+ {
160
+ type: "input",
161
+ name: "profile",
162
+ message: "AWS profile name:",
163
+ default: args.profile || "default",
164
+ when: !args.profile
165
+ },
166
+ {
167
+ type: "confirm",
168
+ name: "createCustomTypes",
169
+ message: "Would you like to add custom config properties?",
170
+ default: true,
171
+ when: args.customTypes === void 0
172
+ }
173
+ ]);
174
+ const config = {
175
+ name: args.name || answers.name,
176
+ account: args.account || answers.account,
177
+ region: args.region || answers.region,
178
+ profile: args.profile || answers.profile,
179
+ createCustomTypes: args.customTypes !== void 0 ? args.customTypes : answers.createCustomTypes
180
+ };
181
+ debug(`Creating config files with config: ${JSON.stringify(config)}`);
182
+ if (config.createCustomTypes) {
183
+ const typesContent = this.generateTypesFile();
184
+ (0, import_node_fs.writeFileSync)(typesPath, typesContent);
185
+ console.log(`\u2705 Created ${typesPath}`);
186
+ debug(`Created types file: ${typesPath}`);
187
+ }
188
+ const baseConfigContent = this.generateBaseConfig(
189
+ config,
190
+ config.createCustomTypes
191
+ );
192
+ (0, import_node_fs.writeFileSync)(configPath, baseConfigContent);
193
+ console.log(`\u2705 Created ${configPath}`);
194
+ debug(`Created base config: ${configPath}`);
195
+ const devConfigContent = this.generateDevConfig(config.createCustomTypes);
196
+ (0, import_node_fs.writeFileSync)(devConfigPath, devConfigContent);
197
+ console.log(`\u2705 Created ${devConfigPath}`);
198
+ debug(`Created dev config: ${devConfigPath}`);
199
+ console.log("\n\u{1F389} Axiom configuration initialized successfully!");
200
+ console.log("\n\u{1F4DD} Next steps:");
201
+ console.log(" 1. Edit .axiom.ts to customize your base configuration");
202
+ console.log(
203
+ " 2. Edit .axiom.dev.ts to customize your development configuration"
204
+ );
205
+ if (config.createCustomTypes) {
206
+ console.log(
207
+ " 3. Edit axiom.config.d.ts to add your custom config properties"
208
+ );
209
+ }
210
+ console.log(
211
+ ` ${config.createCustomTypes ? "4" : "3"}. Run 'axiom config' to verify your configuration`
212
+ );
213
+ };
214
+ generateTypesFile() {
215
+ return `/**
216
+ * Custom Axiom Configuration Types
217
+ *
218
+ * Extend this interface to add custom properties to your Axiom config.
219
+ * These properties will be type-safe when using loadConfig() in your code.
220
+ *
221
+ * Example:
222
+ *
223
+ * export interface CustomConfig {
224
+ * apiUrl: string;
225
+ * apiKey: string;
226
+ * maxRetries: number;
227
+ * features: {
228
+ * authentication: boolean;
229
+ * analytics: boolean;
230
+ * };
231
+ * }
232
+ */
233
+
234
+ export interface CustomConfig {
235
+ // Add your custom properties here
236
+ // Example:
237
+ // apiUrl: string;
238
+ // maxRetries: number;
239
+ }
240
+ `;
241
+ }
242
+ generateBaseConfig(config, hasCustomTypes) {
243
+ const importStatement = hasCustomTypes ? `import type { Config } from "@dsmrt/axiom-config";
244
+ import type { CustomConfig } from "./axiom.config";
245
+
246
+ type AxiomConfig = Config & CustomConfig;
247
+
248
+ const config: AxiomConfig = {` : `import type { Config } from "@dsmrt/axiom-config";
249
+
250
+ const config: Config = {`;
251
+ return `${importStatement}
252
+ name: "${config.name}",
253
+ env: "prod",
254
+ aws: {
255
+ account: "${config.account}",
256
+ region: "${config.region}",
257
+ profile: "${config.profile}",
258
+ },
259
+ baseParameterPath: "/${config.name}/prod",${hasCustomTypes ? `
260
+
261
+ // Add your custom config properties here
262
+ // Example:
263
+ // apiUrl: "https://api.example.com",
264
+ // maxRetries: 3,` : ""}
265
+ };
266
+
267
+ export default config;
268
+ `;
269
+ }
270
+ generateDevConfig(hasCustomTypes) {
271
+ const importStatement = hasCustomTypes ? `import type { Config } from "@dsmrt/axiom-config";
272
+ import type { CustomConfig } from "./axiom.config";
273
+
274
+ type AxiomConfig = Config & CustomConfig;
275
+
276
+ const config: Partial<AxiomConfig> = {` : `import type { Config } from "@dsmrt/axiom-config";
277
+
278
+ const config: Partial<Config> = {`;
279
+ return `${importStatement}
280
+ env: "dev",
281
+ baseParameterPath: undefined, // Will be auto-generated as /{name}/dev${hasCustomTypes ? `,
282
+
283
+ // Override custom config properties for dev environment
284
+ // Example:
285
+ // apiUrl: "https://api.dev.example.com",` : ""}
286
+ };
287
+
288
+ export default config;
289
+ `;
290
+ }
291
+ };
292
+
65
293
  // src/commands/params/base.ts
66
294
  var ParamsCommand = class {
67
295
  command = "params";
@@ -84,10 +312,10 @@ var import_chalk = __toESM(require("chalk"));
84
312
  // src/aws/credentials-provider.ts
85
313
  var import_credential_providers = require("@aws-sdk/credential-providers");
86
314
  var import_client_sts = require("@aws-sdk/client-sts");
87
- var import_inquirer = __toESM(require("inquirer"));
315
+ var import_inquirer2 = __toESM(require("inquirer"));
88
316
 
89
317
  // src/cache.ts
90
- var import_node_fs = __toESM(require("fs"));
318
+ var import_node_fs2 = __toESM(require("fs"));
91
319
  var import_node_os = __toESM(require("os"));
92
320
  var DEFAULT_DIRECTORY = `${import_node_os.default.homedir()}/.axiom/cache`;
93
321
  var cache_default = class {
@@ -96,10 +324,10 @@ var cache_default = class {
96
324
  }
97
325
  get(name) {
98
326
  const file = `${this.cacheDir}/${name}`;
99
- if (!import_node_fs.default.existsSync(file)) {
327
+ if (!import_node_fs2.default.existsSync(file)) {
100
328
  return;
101
329
  }
102
- const buffer = import_node_fs.default.readFileSync(file);
330
+ const buffer = import_node_fs2.default.readFileSync(file);
103
331
  const item = JSON.parse(buffer.toString());
104
332
  if (item.expires === void 0) {
105
333
  return item.data;
@@ -112,16 +340,16 @@ var cache_default = class {
112
340
  return item.data;
113
341
  }
114
342
  delete(name) {
115
- import_node_fs.default.unlinkSync(`${this.cacheDir}/${name}`);
343
+ import_node_fs2.default.unlinkSync(`${this.cacheDir}/${name}`);
116
344
  }
117
345
  set(name, value, expires) {
118
- if (!import_node_fs.default.existsSync(this.cacheDir)) {
119
- import_node_fs.default.mkdirSync(this.cacheDir, {
346
+ if (!import_node_fs2.default.existsSync(this.cacheDir)) {
347
+ import_node_fs2.default.mkdirSync(this.cacheDir, {
120
348
  recursive: true,
121
349
  mode: 448
122
350
  });
123
351
  }
124
- import_node_fs.default.writeFileSync(
352
+ import_node_fs2.default.writeFileSync(
125
353
  `${this.cacheDir}/${name}`,
126
354
  JSON.stringify({
127
355
  expires,
@@ -176,7 +404,7 @@ var roleAssumerCallable = (config) => {
176
404
  };
177
405
  };
178
406
  var mfaCodeProvider = async (mfaSerial) => {
179
- const mfaCode = await import_inquirer.default.prompt({
407
+ const mfaCode = await import_inquirer2.default.prompt({
180
408
  name: "code",
181
409
  message: `Enter MFA code for ${mfaSerial}: `,
182
410
  type: "password"
@@ -232,7 +460,16 @@ Example:
232
460
  return args;
233
461
  };
234
462
  handler = async (args) => {
463
+ debug(
464
+ `Get command handler called with env: ${args.env}, path: ${args.path}`
465
+ );
235
466
  const config = await (0, import_axiom_config2.loadConfig)({ env: args.env });
467
+ debug(
468
+ `Config loaded successfully, base parameter path: ${config.aws?.baseParameterPath}`
469
+ );
470
+ debug(
471
+ `Creating SSM client with region: ${config.aws.region}, profile: ${config.aws.profile}`
472
+ );
236
473
  const collection = new import_axiom_aws_sdk.ParameterCollection(
237
474
  config.aws?.baseParameterPath,
238
475
  new import_client_ssm.SSMClient({
@@ -245,7 +482,9 @@ Example:
245
482
  })
246
483
  })
247
484
  );
485
+ debug(`Fetching parameters from SSM...`);
248
486
  const params = await collection.get();
487
+ debug(`Retrieved ${params.size} parameters`);
249
488
  params.forEach((parameter) => {
250
489
  console.log(
251
490
  import_chalk.default.gray(
@@ -261,7 +500,7 @@ Example:
261
500
  var import_client_ssm2 = require("@aws-sdk/client-ssm");
262
501
  var import_axiom_config3 = require("@dsmrt/axiom-config");
263
502
  var import_chalk2 = __toESM(require("chalk"));
264
- var import_inquirer2 = __toESM(require("inquirer"));
503
+ var import_inquirer3 = __toESM(require("inquirer"));
265
504
 
266
505
  // src/commands/params/utils.ts
267
506
  var buildPath = (config, path) => {
@@ -311,30 +550,50 @@ Example: "/root/myParam" or "service/secret"`
311
550
  });
312
551
  };
313
552
  handler = async (args) => {
553
+ debug(
554
+ `Set command handler called with path: ${args.path}, secure: ${args.secure}, overwrite: ${args.overwrite}, force: ${args.force}`
555
+ );
314
556
  const config = await (0, import_axiom_config3.loadConfig)({ env: args.env });
557
+ debug(
558
+ `Config loaded successfully, base parameter path: ${config.aws?.baseParameterPath}`
559
+ );
560
+ const fullPath = buildPath(config, args.path);
561
+ debug(`Full parameter path: ${fullPath}`);
315
562
  if (args.force !== true) {
316
- const res = await import_inquirer2.default.prompt({
563
+ debug(`Prompting user for confirmation...`);
564
+ const res = await import_inquirer3.default.prompt({
317
565
  type: "confirm",
318
566
  name: "setParam",
319
567
  message: `Are you sure you want to set '${args.path}'?`
320
568
  });
321
569
  if (!res.setParam) {
570
+ debug(`User declined, aborting`);
322
571
  console.log("Doing nothing.");
323
572
  return;
324
573
  }
574
+ debug(`User confirmed`);
575
+ } else {
576
+ debug(`Force flag set, skipping confirmation`);
325
577
  }
578
+ debug(
579
+ `Creating SSM client with region: ${config.aws.region}, profile: ${config.aws.profile}`
580
+ );
326
581
  const client = new import_client_ssm2.SSMClient({
327
582
  region: config.aws.region,
328
583
  credentials: await CachedCredentialProvider(config.aws)
329
584
  });
585
+ debug(
586
+ `Sending parameter to SSM with type: ${args.secure ? "SecureString" : "String"}`
587
+ );
330
588
  const params = await client.send(
331
589
  new import_client_ssm2.PutParameterCommand({
332
- Name: buildPath(config, args.path),
590
+ Name: fullPath,
333
591
  Value: args.value,
334
592
  Type: args.secure ? import_client_ssm2.ParameterType.SECURE_STRING : import_client_ssm2.ParameterType.STRING,
335
593
  Overwrite: args.overwrite
336
594
  })
337
595
  );
596
+ debug(`Parameter set successfully, version: ${params.Version}`);
338
597
  console.log(import_chalk2.default.green("Version: "), import_chalk2.default.white.bold(params.Version));
339
598
  };
340
599
  };
@@ -342,7 +601,7 @@ Example: "/root/myParam" or "service/secret"`
342
601
  // src/commands/params/delete.ts
343
602
  var import_client_ssm3 = require("@aws-sdk/client-ssm");
344
603
  var import_axiom_config4 = require("@dsmrt/axiom-config");
345
- var import_inquirer3 = __toESM(require("inquirer"));
604
+ var import_inquirer4 = __toESM(require("inquirer"));
346
605
  var log = console.log;
347
606
  var DeleteCommand = class {
348
607
  command = "delete <path>";
@@ -364,33 +623,51 @@ Example: "/root/myParam" or "service/secret"`,
364
623
  return args;
365
624
  };
366
625
  handler = async (args) => {
626
+ debug(
627
+ `Delete command handler called with path: ${args.path}, force: ${args.force}`
628
+ );
367
629
  const config = await (0, import_axiom_config4.loadConfig)({ env: args.env });
630
+ debug(
631
+ `Config loaded successfully, base parameter path: ${config.aws?.baseParameterPath}`
632
+ );
368
633
  const path = buildPath(args, args.path);
634
+ debug(`Full parameter path: ${path}`);
635
+ debug(
636
+ `Creating SSM client with region: ${config.aws?.region}, profile: ${config.aws?.profile}`
637
+ );
369
638
  const client = new import_client_ssm3.SSMClient({
370
639
  region: config.aws?.region,
371
640
  credentials: await CachedCredentialProvider(config.aws)
372
641
  });
373
642
  if (path === args.aws.baseParameterPath) {
643
+ debug(`Delete blocked: path matches base parameter path`);
374
644
  throw new Error(
375
645
  `Deleting the path (path: ${path}) that matches the base path (awsSsmParameterPath: ${args.awsSsmParameterPath}) is prohibited.`
376
646
  );
377
647
  }
378
648
  if (args.force !== true) {
379
- const res = await import_inquirer3.default.prompt({
649
+ debug(`Prompting user for confirmation...`);
650
+ const res = await import_inquirer4.default.prompt({
380
651
  type: "confirm",
381
652
  name: "delete",
382
653
  message: `Are you sure you want to delete '${path}'?`
383
654
  });
384
655
  if (!res.delete) {
656
+ debug(`User declined, aborting`);
385
657
  log("Doing nothing.");
386
658
  return;
387
659
  }
660
+ debug(`User confirmed`);
661
+ } else {
662
+ debug(`Force flag set, skipping confirmation`);
388
663
  }
664
+ debug(`Sending delete command to SSM for: ${path}`);
389
665
  await client.send(
390
666
  new import_client_ssm3.DeleteParameterCommand({
391
667
  Name: path
392
668
  })
393
669
  );
670
+ debug(`Parameter deleted successfully`);
394
671
  log("\u{1F44D}");
395
672
  };
396
673
  };
@@ -399,7 +676,17 @@ Example: "/root/myParam" or "service/secret"`,
399
676
  (0, import_yargs.default)(process.argv.slice(2)).env("AXIOM").scriptName("axiom").option("config", {
400
677
  alias: "c",
401
678
  string: true
402
- }).command(new Config()).command(new ParamsCommand()).strict().usage(
679
+ }).option("debug", {
680
+ alias: "d",
681
+ type: "boolean",
682
+ description: "Enable debug output",
683
+ default: false,
684
+ global: true
685
+ }).middleware((argv) => {
686
+ if (argv.debug) {
687
+ process.env.AXIOM_DEBUG = "true";
688
+ }
689
+ }).command(new Init()).command(new Config()).command(new ParamsCommand()).strict().usage(
403
690
  `
404
691
  Axiom - an AWS focused config cli
405
692
 
@@ -412,6 +699,7 @@ USAGE:
412
699
  Config,
413
700
  DeleteCommand,
414
701
  GetCommand,
702
+ Init,
415
703
  ParamsCommand,
416
704
  SetCommand,
417
705
  awsOptions,
package/dist/index.mjs CHANGED
@@ -2,15 +2,17 @@ import {
2
2
  Config,
3
3
  DeleteCommand,
4
4
  GetCommand,
5
+ Init,
5
6
  ParamsCommand,
6
7
  SetCommand,
7
8
  awsOptions,
8
9
  commonOptions
9
- } from "./chunk-72DOPOLO.mjs";
10
+ } from "./chunk-QRHY3BEA.mjs";
10
11
  export {
11
12
  Config,
12
13
  DeleteCommand,
13
14
  GetCommand,
15
+ Init,
14
16
  ParamsCommand,
15
17
  SetCommand,
16
18
  awsOptions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsmrt/axiom-cli",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "axiom cli for managing configs including secrets",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -49,7 +49,7 @@
49
49
  "inquirer": "^8.2.6",
50
50
  "yargs": "^17.7.2",
51
51
  "@dsmrt/axiom-aws-sdk": "^1.0.0",
52
- "@dsmrt/axiom-config": "^1.0.0"
52
+ "@dsmrt/axiom-config": "^1.1.0"
53
53
  },
54
54
  "scripts": {
55
55
  "lint": "biome lint",