xtra-cli 0.1.10 → 0.2.3

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.
@@ -65,26 +65,16 @@ Examples:
65
65
  .action(async (command, args, options) => {
66
66
  // console.log("Run Options:", options);
67
67
  let { project, env, branch, shell: useShell } = options;
68
- // ── Load .xtrarc from current directory (project-local config) ─────────
69
- const rcPath = path.resolve(process.cwd(), ".xtrarc");
70
- let rcConfig = {};
71
- if (fs.existsSync(rcPath)) {
72
- try {
73
- rcConfig = JSON.parse(fs.readFileSync(rcPath, "utf8"));
74
- }
75
- catch (_) { }
76
- }
68
+ // Load .xtrarc from CWD first (project-local config), then fall back to global conf store
69
+ const rc = (0, config_1.getRcConfig)();
77
70
  // Use active branch from config if not specified
78
- if (!branch) {
79
- branch = rcConfig.branch || (0, config_1.getConfigValue)("branch") || "main";
80
- }
71
+ if (!branch)
72
+ branch = rc.branch;
81
73
  // Normalize Env
82
74
  const envMap = { dev: "development", stg: "staging", prod: "production" };
83
- env = envMap[env] || env || rcConfig.env || (0, config_1.getConfigValue)("env") || "development";
84
- // We might want to support default project in config later
85
- if (!project) {
86
- project = rcConfig.project || (0, config_1.getConfigValue)("project");
87
- }
75
+ env = envMap[env] || env || rc.env;
76
+ if (!project)
77
+ project = rc.project;
88
78
  if (!project) {
89
79
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or checkout a branch."));
90
80
  process.exit(1);
@@ -209,10 +199,11 @@ Examples:
209
199
  }
210
200
  }
211
201
  console.log(chalk_1.default.gray(`> ${command} ${args.join(" ")}`));
202
+ const isWindows = process.platform === "win32";
212
203
  const child = (0, child_process_1.spawn)(command, args, {
213
204
  env: envVars,
214
205
  stdio: "inherit",
215
- shell: useShell, // false by default; --shell enables for npm scripts / Windows
206
+ shell: useShell || isWindows, // Auto-enable shell on Windows for better compatibility (e.g. npm, etc)
216
207
  });
217
208
  child.on("exit", (code) => {
218
209
  process.exit(code ?? 0);
@@ -28,19 +28,20 @@ Examples:
28
28
  .action(async (options) => {
29
29
  const parentOpts = exports.secretsCommand.opts();
30
30
  let { project, env, branch } = parentOpts;
31
- // Use active branch from config if not specified
32
- if (!branch) {
33
- branch = (0, config_1.getConfigValue)("branch") || "main";
34
- }
31
+ // Load .xtrarc from CWD (local project config has priority over global conf store)
32
+ const rc = (0, config_1.getRcConfig)();
33
+ if (!project)
34
+ project = rc.project;
35
+ if (!branch)
36
+ branch = rc.branch;
37
+ if (!env || env === "development")
38
+ env = rc.env; // only override if still at default
35
39
  // Normalize Env
36
40
  const envMap = { dev: "development", stg: "staging", prod: "production" };
37
41
  env = envMap[env] || env;
38
42
  const { show } = options;
39
43
  if (!project) {
40
- project = (0, config_1.getConfigValue)("project");
41
- }
42
- if (!project) {
43
- console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or checkout a branch."));
44
+ console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run `xtra init` first."));
44
45
  process.exit(1);
45
46
  }
46
47
  const spinner = (0, ora_1.default)(`Fetching secrets for ${env} (branch: ${branch})...`).start();
@@ -5,10 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.simulateCommand = void 0;
7
7
  /**
8
- * simulate.ts Dry-run mode for xtra run
8
+ * simulate.ts — Dry-run mode for xtra run
9
9
  *
10
10
  * Fetches secrets and shows exactly what `xtra run` would inject into
11
- * the process environment without actually executing the command.
11
+ * the process environment — without actually executing the command.
12
12
  * Safe to use in any environment.
13
13
  */
14
14
  const commander_1 = require("commander");
@@ -30,7 +30,7 @@ exports.simulateCommand = new commander_1.Command("simulate")
30
30
  const envMap = { dev: "development", stg: "staging", prod: "production" };
31
31
  env = envMap[env] || env;
32
32
  if (!project)
33
- project = (0, config_1.getConfigValue)("project");
33
+ project = (0, config_1.getRcConfig)().project;
34
34
  if (!project) {
35
35
  console.error(chalk_1.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'."));
36
36
  process.exit(1);
@@ -47,7 +47,7 @@ exports.simulateCommand = new commander_1.Command("simulate")
47
47
  process.exit(1);
48
48
  }
49
49
  const count = Object.keys(secrets).length;
50
- console.log(chalk_1.default.bold(`\n🔬 Simulation xtra run ${command ? chalk_1.default.cyan(command) : "(no command specified)"}\n`));
50
+ console.log(chalk_1.default.bold(`\n🔬 Simulation — xtra run ${command ? chalk_1.default.cyan(command) : "(no command specified)"}\n`));
51
51
  console.log(chalk_1.default.gray(` Project : ${project}`));
52
52
  console.log(chalk_1.default.gray(` Env : ${env}`));
53
53
  console.log(chalk_1.default.gray(` Branch : ${branch}`));
@@ -61,16 +61,16 @@ exports.simulateCommand = new commander_1.Command("simulate")
61
61
  ];
62
62
  for (const [key, value] of Object.entries(secrets)) {
63
63
  const localVal = process.env[key];
64
- const displayVal = showValues ? chalk_1.default.cyan(value) : chalk_1.default.gray("••••••••");
64
+ const displayVal = showValues ? chalk_1.default.cyan(value) : chalk_1.default.gray("••••••••");
65
65
  let status = chalk_1.default.green("Injected");
66
66
  let localDisplay = "-";
67
67
  if (showDiff && localVal !== undefined) {
68
- localDisplay = showValues ? chalk_1.default.yellow(localVal) : chalk_1.default.gray("••••••••");
68
+ localDisplay = showValues ? chalk_1.default.yellow(localVal) : chalk_1.default.gray("••••••••");
69
69
  if (localVal === value) {
70
70
  status = chalk_1.default.gray("Same");
71
71
  }
72
72
  else {
73
- status = chalk_1.default.yellow(" Override");
73
+ status = chalk_1.default.yellow("âš¡ Override");
74
74
  }
75
75
  }
76
76
  else if (showDiff) {
@@ -87,6 +87,6 @@ exports.simulateCommand = new commander_1.Command("simulate")
87
87
  if (!showDiff) {
88
88
  console.log(chalk_1.default.gray(" Tip: use --diff to compare against your local process.env"));
89
89
  }
90
- console.log(chalk_1.default.bold(`\n Simulation complete. ${count} secret(s) would be injected.`));
90
+ console.log(chalk_1.default.bold(`\n ✅ Simulation complete. ${count} secret(s) would be injected.`));
91
91
  console.log(chalk_1.default.gray(" Run without 'simulate' to actually execute the command.\n"));
92
92
  });
@@ -20,11 +20,10 @@ exports.statusCommand = new commander_1.Command("status")
20
20
  .action(async (options) => {
21
21
  let { project, env, branch } = options;
22
22
  // Use config fallback
23
- if (!project) {
24
- project = (0, config_1.getConfigValue)("project");
25
- }
23
+ if (!project)
24
+ project = (0, config_1.getRcConfig)().project;
26
25
  if (!branch) {
27
- branch = (0, config_1.getConfigValue)("branch") || "main";
26
+ branch = (0, config_1.getRcConfig)().branch || "main";
28
27
  }
29
28
  if (!project) {
30
29
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
@@ -76,15 +75,15 @@ exports.statusCommand = new commander_1.Command("status")
76
75
  console.log("");
77
76
  console.log((0, table_1.table)(rows));
78
77
  if (diffCount === 0) {
79
- console.log(chalk_1.default.green(" Everything is in sync."));
78
+ console.log(chalk_1.default.green("✔ Everything is in sync."));
80
79
  }
81
80
  else {
82
- console.log(chalk_1.default.yellow(`⚠ Found ${diffCount} difference(s).\n`));
81
+ console.log(chalk_1.default.yellow(`âš  Found ${diffCount} difference(s).\n`));
83
82
  console.log(chalk_1.default.bold(" Next steps:"));
84
- console.log(chalk_1.default.gray(" xtra generate ") + chalk_1.default.white("# pull cloud secrets .env (merge)"));
85
- console.log(chalk_1.default.gray(" xtra generate -f json ") + chalk_1.default.white("# pull cloud secrets secrets.json"));
83
+ console.log(chalk_1.default.gray(" xtra generate ") + chalk_1.default.white("# pull cloud secrets → .env (merge)"));
84
+ console.log(chalk_1.default.gray(" xtra generate -f json ") + chalk_1.default.white("# pull cloud secrets → secrets.json"));
86
85
  console.log(chalk_1.default.gray(" xtra run node app.js ") + chalk_1.default.white("# inject secrets at runtime (no file)"));
87
- console.log(chalk_1.default.gray(" xtra local sync ") + chalk_1.default.white("# pull cloud secrets .env.local (offline mode)"));
86
+ console.log(chalk_1.default.gray(" xtra local sync ") + chalk_1.default.white("# pull cloud secrets → .env.local (offline mode)"));
88
87
  }
89
88
  }
90
89
  catch (error) {
@@ -1,15 +1,15 @@
1
1
  "use strict";
2
2
  /**
3
- * template.ts Secret Templating Engine for xtra-cli
3
+ * template.ts — Secret Templating Engine for xtra-cli
4
4
  *
5
5
  * Reads a template file containing {{ secrets.KEY }} placeholders,
6
6
  * fetches secrets from XtraSecurity Cloud, substitutes all placeholders,
7
7
  * and writes the rendered output to the specified file (or stdout).
8
8
  *
9
9
  * Supported placeholder syntaxes:
10
- * {{ secrets.DATABASE_URL }} fetches DATABASE_URL from secrets
11
- * {{ secrets.PORT | 3000 }} with fallback default value "3000"
12
- * {{ env.NODE_ENV }} reads from local process environment
10
+ * {{ secrets.DATABASE_URL }} → fetches DATABASE_URL from secrets
11
+ * {{ secrets.PORT | 3000 }} → with fallback default value "3000"
12
+ * {{ env.NODE_ENV }} → reads from local process environment
13
13
  *
14
14
  * Example:
15
15
  * # config.yaml.tpl
@@ -70,7 +70,7 @@ const chalk_1 = __importDefault(require("chalk"));
70
70
  const ora_1 = __importDefault(require("ora"));
71
71
  const fs = __importStar(require("fs"));
72
72
  const path = __importStar(require("path"));
73
- // ─── Placeholder Regex ────────────────────────────────────────────────────────
73
+ // ─── Placeholder Regex ────────────────────────────────────────────────────────
74
74
  // Matches: {{ secrets.KEY }}, {{ secrets.KEY | default }}, {{ env.KEY | default }}
75
75
  const PLACEHOLDER_RE = /\{\{\s*(secrets|env)\.([A-Z0-9_a-z]+)(?:\s*\|\s*([^}]*?))?\s*\}\}/g;
76
76
  function renderTemplate(template, secrets, processEnv) {
@@ -91,18 +91,18 @@ function renderTemplate(template, secrets, processEnv) {
91
91
  }
92
92
  if (defaultVal !== undefined) {
93
93
  const trimmedDefault = defaultVal.trim();
94
- usedDefaults.push(`${source}.${key} "${trimmedDefault}"`);
94
+ usedDefaults.push(`${source}.${key} → "${trimmedDefault}"`);
95
95
  replacedCount++;
96
96
  return trimmedDefault;
97
97
  }
98
98
  missingKeys.push(`${source}.${key}`);
99
- return `{{ ${source}.${key} }}`; // Leave unreplaced user will see it clearly
99
+ return `{{ ${source}.${key} }}`; // Leave unreplaced — user will see it clearly
100
100
  });
101
101
  return { output, replacedCount, missingKeys, usedDefaults };
102
102
  }
103
- // ─── Command ──────────────────────────────────────────────────────────────────
103
+ // ─── Command ──────────────────────────────────────────────────────────────────
104
104
  exports.templateCommand = new commander_1.Command("template")
105
- .description("Secret templating engine inject secrets into config file templates")
105
+ .description("Secret templating engine — inject secrets into config file templates")
106
106
  .addHelpText("after", `
107
107
  Template Syntax:
108
108
  {{ secrets.KEY }} Replace with secret value
@@ -116,7 +116,7 @@ Examples:
116
116
  $ xtra template check config.yaml.tpl -p proj123 -e production
117
117
  $ xtra template list config.yaml.tpl
118
118
  `);
119
- // ── render ────────────────────────────────────────────────────────────────────
119
+ // ── render ────────────────────────────────────────────────────────────────────
120
120
  exports.templateCommand
121
121
  .command("render <templateFile>")
122
122
  .description("Render a template file by injecting secrets and environment variables")
@@ -131,7 +131,7 @@ exports.templateCommand
131
131
  const envMap = { dev: "development", stg: "staging", prod: "production" };
132
132
  env = envMap[env] || env;
133
133
  if (!project)
134
- project = (0, config_1.getConfigValue)("project");
134
+ project = (0, config_1.getRcConfig)().project;
135
135
  if (!project) {
136
136
  console.error(chalk_1.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'."));
137
137
  process.exit(1);
@@ -163,12 +163,12 @@ exports.templateCommand
163
163
  // Render
164
164
  const { output: rendered, replacedCount, missingKeys, usedDefaults } = renderTemplate(template, secrets, process.env);
165
165
  // Report
166
- console.log(chalk_1.default.green(`✅ Replaced ${replacedCount} placeholder(s).`));
166
+ console.log(chalk_1.default.green(`✅ Replaced ${replacedCount} placeholder(s).`));
167
167
  if (usedDefaults.length > 0) {
168
- console.log(chalk_1.default.yellow(`⚠ Used defaults for: ${usedDefaults.join(", ")}`));
168
+ console.log(chalk_1.default.yellow(`âš  Used defaults for: ${usedDefaults.join(", ")}`));
169
169
  }
170
170
  if (missingKeys.length > 0) {
171
- console.log(chalk_1.default.red(`✗ Unresolved placeholders: ${missingKeys.join(", ")}`));
171
+ console.log(chalk_1.default.red(`✗ Unresolved placeholders: ${missingKeys.join(", ")}`));
172
172
  if (strict) {
173
173
  console.error(chalk_1.default.red("Aborting due to --strict mode."));
174
174
  process.exit(1);
@@ -178,7 +178,7 @@ exports.templateCommand
178
178
  if (output) {
179
179
  const outPath = path.resolve(process.cwd(), output);
180
180
  fs.writeFileSync(outPath, rendered, "utf8");
181
- console.log(chalk_1.default.blue(`→ Written to: ${outPath}`));
181
+ console.log(chalk_1.default.blue(`→ Written to: ${outPath}`));
182
182
  }
183
183
  else {
184
184
  // Print to stdout (so user can pipe it)
@@ -196,7 +196,7 @@ exports.templateCommand
196
196
  }
197
197
  catch (e) { }
198
198
  });
199
- // ── check ─────────────────────────────────────────────────────────────────────
199
+ // ── check ─────────────────────────────────────────────────────────────────────
200
200
  exports.templateCommand
201
201
  .command("check <templateFile>")
202
202
  .description("Validate that all template placeholders have matching secrets (dry-run)")
@@ -208,7 +208,7 @@ exports.templateCommand
208
208
  const envMap = { dev: "development", stg: "staging", prod: "production" };
209
209
  env = envMap[env] || env;
210
210
  if (!project)
211
- project = (0, config_1.getConfigValue)("project");
211
+ project = (0, config_1.getRcConfig)().project;
212
212
  if (!project) {
213
213
  console.error(chalk_1.default.red("Error: Project ID required."));
214
214
  process.exit(1);
@@ -242,14 +242,14 @@ exports.templateCommand
242
242
  }
243
243
  if (missingKeys.length > 0) {
244
244
  console.log(chalk_1.default.red(`\n Missing secrets (no default):`));
245
- missingKeys.forEach(k => console.log(chalk_1.default.red(` ${k}`)));
245
+ missingKeys.forEach(k => console.log(chalk_1.default.red(` ✗ ${k}`)));
246
246
  process.exit(1);
247
247
  }
248
248
  else {
249
- console.log(chalk_1.default.green("\n All placeholders are resolvable!"));
249
+ console.log(chalk_1.default.green("\n ✅ All placeholders are resolvable!"));
250
250
  }
251
251
  });
252
- // ── list ──────────────────────────────────────────────────────────────────────
252
+ // ── list ──────────────────────────────────────────────────────────────────────
253
253
  exports.templateCommand
254
254
  .command("list <templateFile>")
255
255
  .description("List all placeholders found in a template file (no API call needed)")
@@ -269,7 +269,7 @@ exports.templateCommand
269
269
  matches.forEach(m => {
270
270
  const [, source, key, def] = m;
271
271
  const defaultHint = def ? chalk_1.default.gray(` (default: "${def.trim()}")`) : "";
272
- const icon = source === "secrets" ? "🔒" : "📦";
272
+ const icon = source === "secrets" ? "🔒" : "📦";
273
273
  console.log(` ${icon} ${chalk_1.default.cyan(`{{ ${source}.${key} }}`)}${defaultHint}`);
274
274
  });
275
275
  console.log(`\n Total: ${chalk_1.default.bold(matches.length)} placeholder(s)\n`);