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.
- package/dist/commands/access.js +2 -2
- package/dist/commands/branch.js +8 -12
- package/dist/commands/checkout.js +3 -4
- package/dist/commands/diff.js +4 -5
- package/dist/commands/doctor.js +50 -62
- package/dist/commands/env.js +2 -2
- package/dist/commands/export.js +4 -5
- package/dist/commands/generate.js +3 -4
- package/dist/commands/history.js +2 -2
- package/dist/commands/import.js +3 -4
- package/dist/commands/integration.js +6 -6
- package/dist/commands/local.js +20 -20
- package/dist/commands/rollback.js +2 -3
- package/dist/commands/rotate.js +3 -4
- package/dist/commands/run.js +9 -18
- package/dist/commands/secrets.js +9 -8
- package/dist/commands/simulate.js +8 -8
- package/dist/commands/status.js +8 -9
- package/dist/commands/template.js +21 -21
- package/dist/commands/ui.js +168 -97
- package/dist/commands/watch.js +9 -9
- package/dist/lib/config.js +22 -1
- package/package.json +1 -1
package/dist/commands/run.js
CHANGED
|
@@ -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
|
-
//
|
|
69
|
-
const
|
|
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 =
|
|
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 ||
|
|
84
|
-
|
|
85
|
-
|
|
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, //
|
|
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);
|
package/dist/commands/secrets.js
CHANGED
|
@@ -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
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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("
|
|
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
|
|
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
|
});
|
package/dist/commands/status.js
CHANGED
|
@@ -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.
|
|
25
|
-
}
|
|
23
|
+
if (!project)
|
|
24
|
+
project = (0, config_1.getRcConfig)().project;
|
|
26
25
|
if (!branch) {
|
|
27
|
-
branch = (0, config_1.
|
|
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("
|
|
78
|
+
console.log(chalk_1.default.green("✔ Everything is in sync."));
|
|
80
79
|
}
|
|
81
80
|
else {
|
|
82
|
-
console.log(chalk_1.default.yellow(
|
|
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
|
|
85
|
-
console.log(chalk_1.default.gray(" xtra generate -f json ") + chalk_1.default.white("# pull cloud secrets
|
|
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
|
|
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
|
|
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 }}
|
|
11
|
-
* {{ secrets.PORT | 3000 }}
|
|
12
|
-
* {{ env.NODE_ENV }}
|
|
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
|
-
//
|
|
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}
|
|
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
|
|
99
|
+
return `{{ ${source}.${key} }}`; // Leave unreplaced — user will see it clearly
|
|
100
100
|
});
|
|
101
101
|
return { output, replacedCount, missingKeys, usedDefaults };
|
|
102
102
|
}
|
|
103
|
-
//
|
|
103
|
+
// ─── Command ──────────────────────────────────────────────────────────────────
|
|
104
104
|
exports.templateCommand = new commander_1.Command("template")
|
|
105
|
-
.description("Secret templating engine
|
|
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
|
-
//
|
|
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.
|
|
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(
|
|
166
|
+
console.log(chalk_1.default.green(`✅ Replaced ${replacedCount} placeholder(s).`));
|
|
167
167
|
if (usedDefaults.length > 0) {
|
|
168
|
-
console.log(chalk_1.default.yellow(
|
|
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(
|
|
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(
|
|
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
|
-
//
|
|
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.
|
|
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(`
|
|
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
|
|
249
|
+
console.log(chalk_1.default.green("\n ✅ All placeholders are resolvable!"));
|
|
250
250
|
}
|
|
251
251
|
});
|
|
252
|
-
//
|
|
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`);
|