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.
Potentially problematic release.
This version of xtra-cli might be problematic. Click here for more details.
- 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/ui.js
CHANGED
|
@@ -41,178 +41,249 @@ const commander_1 = require("commander");
|
|
|
41
41
|
const chalk_1 = __importDefault(require("chalk"));
|
|
42
42
|
const readline = __importStar(require("readline"));
|
|
43
43
|
const api_1 = require("../lib/api");
|
|
44
|
+
const config_1 = require("../lib/config");
|
|
44
45
|
const ENVS = ["development", "staging", "production"];
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
const header = [
|
|
70
|
-
chalk_1.default.bold.yellow("KEY".padEnd(COLS[0])),
|
|
71
|
-
chalk_1.default.bold.yellow("VALUE".padEnd(COLS[1])),
|
|
72
|
-
chalk_1.default.bold.yellow("UPDATED".padEnd(COLS[2]))
|
|
73
|
-
].join(border("│"));
|
|
74
|
-
console.log(border("│") + header + border("│"));
|
|
75
|
-
console.log(border(`├${sep}┤`));
|
|
76
|
-
if (secrets.length === 0) {
|
|
77
|
-
console.log(border("│") + chalk_1.default.gray(" No secrets found.".padEnd(COLS[0] + COLS[1] + COLS[2] + 2)) + border("│"));
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
secrets.forEach((s, i) => {
|
|
81
|
-
const isSelected = i === selectedIdx;
|
|
82
|
-
const fmt = (t, w) => {
|
|
83
|
-
const str = t.length > w - 2 ? t.slice(0, w - 5) + "..." : t;
|
|
84
|
-
return isSelected && active ? chalk_1.default.bold.bgCyan.black(str.padEnd(w)) : isSelected ? chalk_1.default.cyan(str.padEnd(w)) : str.padEnd(w);
|
|
85
|
-
};
|
|
86
|
-
const row = [
|
|
87
|
-
fmt(s.key, COLS[0]),
|
|
88
|
-
fmt("*".repeat(8), COLS[1]),
|
|
89
|
-
fmt(new Date(s.updatedAt).toLocaleDateString(), COLS[2]),
|
|
90
|
-
].join(border("│"));
|
|
91
|
-
console.log(border("│") + row + border("│"));
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
console.log(border(`└${"─".repeat(COLS[0])}┴${"─".repeat(COLS[1])}┴${"─".repeat(COLS[2])}┘`));
|
|
95
|
-
}
|
|
96
|
-
function renderStatus(msg) {
|
|
97
|
-
console.log("\n" + chalk_1.default.gray(" ● ") + chalk_1.default.white(msg));
|
|
46
|
+
const ENV_COLORS = {
|
|
47
|
+
development: chalk_1.default.green,
|
|
48
|
+
staging: chalk_1.default.yellow,
|
|
49
|
+
production: chalk_1.default.red,
|
|
50
|
+
};
|
|
51
|
+
const W = process.stdout.columns || 120;
|
|
52
|
+
// ─── Box Drawing Helpers ─────────────────────────────────────────────────────
|
|
53
|
+
const B = {
|
|
54
|
+
tl: "╭", tr: "╮", bl: "╰", br: "╯",
|
|
55
|
+
h: "─", v: "│", x: "┼",
|
|
56
|
+
lt: "├", rt: "┤", tt: "┬", bt: "┴",
|
|
57
|
+
};
|
|
58
|
+
function hline(width, c = B.h) { return c.repeat(width); }
|
|
59
|
+
function box(title, lines, width, active) {
|
|
60
|
+
const border = active ? chalk_1.default.cyan : chalk_1.default.hex("#2d3a4d");
|
|
61
|
+
const inner = width - 2;
|
|
62
|
+
const pad = (s) => s.padEnd(inner).slice(0, inner);
|
|
63
|
+
const label = active
|
|
64
|
+
? chalk_1.default.bold.cyan(` ${title} `)
|
|
65
|
+
: chalk_1.default.hex("#4a5568")(` ${title} `);
|
|
66
|
+
const titleLine = border(B.tl) + label + border(hline(inner - title.length - 2)) + border(B.tr);
|
|
67
|
+
const body = lines.map(l => border(B.v) + l.slice(0, inner).padEnd(inner) + border(B.v));
|
|
68
|
+
const foot = border(B.bl) + border(hline(inner)) + border(B.br);
|
|
69
|
+
return [titleLine, ...body, foot];
|
|
98
70
|
}
|
|
99
|
-
|
|
71
|
+
function clear() { process.stdout.write("\x1B[H\x1B[2J\x1B[3J"); }
|
|
72
|
+
// ─── Main TUI ────────────────────────────────────────────────────────────────
|
|
100
73
|
async function runUI() {
|
|
101
|
-
let
|
|
74
|
+
let panel = "projects";
|
|
102
75
|
let projects = [];
|
|
103
|
-
let
|
|
76
|
+
let projIdx = 0;
|
|
104
77
|
let envIdx = 0;
|
|
105
78
|
let secrets = [];
|
|
106
|
-
let
|
|
107
|
-
let status = "Loading
|
|
79
|
+
let secIdx = 0;
|
|
80
|
+
let status = "Loading…";
|
|
108
81
|
let loading = false;
|
|
82
|
+
let showValues = false;
|
|
83
|
+
// ── Loaders ────────────────────────────────────────────────────────────────
|
|
109
84
|
async function loadProjects() {
|
|
85
|
+
status = "Loading projects…";
|
|
86
|
+
draw();
|
|
110
87
|
try {
|
|
111
|
-
|
|
88
|
+
const raw = await api_1.api.getProjects();
|
|
89
|
+
projects = Array.isArray(raw) ? raw : raw.projects ?? [];
|
|
112
90
|
status = projects.length > 0
|
|
113
|
-
?
|
|
114
|
-
: "No projects found.
|
|
91
|
+
? `${projects.length} project(s) found · ↑↓ navigate · Enter to load secrets`
|
|
92
|
+
: "No projects found. Run `xtra login` first.";
|
|
115
93
|
}
|
|
116
94
|
catch (e) {
|
|
117
|
-
status = chalk_1.default.red(
|
|
95
|
+
status = chalk_1.default.red(`✗ ${e.message}`);
|
|
118
96
|
}
|
|
119
97
|
draw();
|
|
120
98
|
}
|
|
121
99
|
async function loadSecrets() {
|
|
122
|
-
if (!projects[
|
|
100
|
+
if (!projects[projIdx])
|
|
123
101
|
return;
|
|
124
102
|
loading = true;
|
|
125
|
-
status = `Fetching secrets for ${projects[
|
|
103
|
+
status = `Fetching secrets for ${chalk_1.default.cyan(projects[projIdx].name)} / ${ENV_COLORS[ENVS[envIdx]](ENVS[envIdx])}…`;
|
|
126
104
|
draw();
|
|
127
105
|
try {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
106
|
+
const rc = (0, config_1.getRcConfig)();
|
|
107
|
+
const branch = rc.branch || "main";
|
|
108
|
+
const raw = await api_1.api.getSecrets(projects[projIdx].id, ENVS[envIdx], branch);
|
|
109
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
110
|
+
secrets = Object.entries(raw).map(([key, value]) => ({
|
|
111
|
+
id: key, key, value: String(value), updatedAt: new Date().toISOString(),
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
else if (Array.isArray(raw)) {
|
|
115
|
+
secrets = raw;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
secrets = [];
|
|
119
|
+
}
|
|
120
|
+
secIdx = 0;
|
|
121
|
+
status = secrets.length > 0
|
|
122
|
+
? `${secrets.length} secret(s) · V toggle values · Tab switch panel · Q quit`
|
|
123
|
+
: "No secrets in this environment.";
|
|
131
124
|
}
|
|
132
125
|
catch (e) {
|
|
133
126
|
secrets = [];
|
|
134
|
-
status = chalk_1.default.red(
|
|
127
|
+
status = chalk_1.default.red(`✗ ${e.message}`);
|
|
135
128
|
}
|
|
136
129
|
loading = false;
|
|
137
130
|
draw();
|
|
138
131
|
}
|
|
132
|
+
// ── Render ─────────────────────────────────────────────────────────────────
|
|
139
133
|
function draw() {
|
|
140
134
|
clear();
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
135
|
+
// ── Header ──────────────────────────────────────────────────────────────
|
|
136
|
+
const appTitle = chalk_1.default.bold.cyan(" 🔒 XtraSecurity") + chalk_1.default.hex("#4a5a6b")(" — Interactive Secrets Shell");
|
|
137
|
+
const env = ENV_COLORS[ENVS[envIdx]] ? ENV_COLORS[ENVS[envIdx]](ENVS[envIdx]) : chalk_1.default.white(ENVS[envIdx]);
|
|
138
|
+
const crumb = projects[projIdx]
|
|
139
|
+
? chalk_1.default.hex("#4a5568")(` ${projects[projIdx].name} `) + chalk_1.default.hex("#4a5568")("/") + chalk_1.default.hex("#4a5568")(` ${ENVS[envIdx]}`)
|
|
140
|
+
: chalk_1.default.hex("#4a5568")(" No project selected");
|
|
144
141
|
console.log();
|
|
145
|
-
|
|
142
|
+
console.log(appTitle + chalk_1.default.hex("#2d3a4d")(" │") + crumb);
|
|
143
|
+
console.log(chalk_1.default.hex("#1e293b")(hline(W)));
|
|
146
144
|
console.log();
|
|
145
|
+
// ── Left column: Projects (22 chr) + Env (22 chr) ─────────────────────
|
|
146
|
+
const LEFT_W = 26;
|
|
147
|
+
const RIGHT_W = W - LEFT_W - 3;
|
|
148
|
+
// Projects panel
|
|
149
|
+
const projLines = (projects.length > 0 ? projects : [{ id: "", name: "(loading…)" }]).map((p, i) => {
|
|
150
|
+
const active = i === projIdx;
|
|
151
|
+
const icon = active ? chalk_1.default.cyan("▶ ") : " ";
|
|
152
|
+
const name = active && panel === "projects"
|
|
153
|
+
? chalk_1.default.bold.bgCyan.black(` ${p.name} `.padEnd(LEFT_W - 4))
|
|
154
|
+
: active
|
|
155
|
+
? chalk_1.default.bold.cyan(p.name)
|
|
156
|
+
: chalk_1.default.hex("#6b7c93")(p.name);
|
|
157
|
+
return ` ${icon}${name}`;
|
|
158
|
+
});
|
|
159
|
+
const projBox = box("PROJECTS", projLines, LEFT_W, panel === "projects");
|
|
160
|
+
// Env panel
|
|
161
|
+
const envLines = ENVS.map((e, i) => {
|
|
162
|
+
const active = i === envIdx;
|
|
163
|
+
const icon = active ? chalk_1.default.cyan("▶ ") : " ";
|
|
164
|
+
const col = ENV_COLORS[e] ?? chalk_1.default.white;
|
|
165
|
+
const name = active && panel === "env"
|
|
166
|
+
? chalk_1.default.bold.bgCyan.black(` ${e} `.padEnd(LEFT_W - 4))
|
|
167
|
+
: active
|
|
168
|
+
? col.bold(e)
|
|
169
|
+
: chalk_1.default.hex("#6b7c93")(e);
|
|
170
|
+
return ` ${icon}${name}`;
|
|
171
|
+
});
|
|
172
|
+
const envBox = box("ENVIRONMENT", envLines, LEFT_W, panel === "env");
|
|
173
|
+
// Secrets panel
|
|
174
|
+
const KW = Math.floor((RIGHT_W - 3) * 0.45);
|
|
175
|
+
const VW = Math.floor((RIGHT_W - 3) * 0.35);
|
|
176
|
+
const DW = RIGHT_W - KW - VW - 4;
|
|
177
|
+
const secHeader = chalk_1.default.bold.hex("#64748b")("KEY".padEnd(KW)) + " " + chalk_1.default.bold.hex("#64748b")("VALUE".padEnd(VW)) + " " + chalk_1.default.bold.hex("#64748b")("UPDATED".padEnd(DW));
|
|
178
|
+
const divider = chalk_1.default.hex("#1e293b")(hline(KW) + " " + hline(VW) + " " + hline(DW));
|
|
179
|
+
let secLines;
|
|
147
180
|
if (loading) {
|
|
148
|
-
|
|
181
|
+
secLines = [` ${chalk_1.default.cyan("⠋")} Loading…`];
|
|
182
|
+
}
|
|
183
|
+
else if (secrets.length === 0) {
|
|
184
|
+
secLines = [` ${chalk_1.default.hex("#4a5568")("No secrets in this environment.")}`];
|
|
149
185
|
}
|
|
150
186
|
else {
|
|
151
|
-
|
|
187
|
+
secLines = [" " + secHeader, " " + divider, ...secrets.map((s, i) => {
|
|
188
|
+
const sel = i === secIdx && panel === "secrets";
|
|
189
|
+
const key = s.key.padEnd(KW).slice(0, KW);
|
|
190
|
+
const val = showValues ? s.value.padEnd(VW).slice(0, VW) : "●".repeat(Math.min(8, VW)).padEnd(VW).slice(0, VW);
|
|
191
|
+
const dt = new Date(s.updatedAt).toLocaleDateString("en-GB", { day: "2-digit", month: "short" }).padEnd(DW);
|
|
192
|
+
if (sel) {
|
|
193
|
+
return chalk_1.default.bold.bgHex("#0e243e")(" ▶ " + chalk_1.default.cyan(key) + " " + chalk_1.default.greenBright(val) + " " + chalk_1.default.hex("#4a9eff")(dt) + " ");
|
|
194
|
+
}
|
|
195
|
+
return " " + chalk_1.default.white(key) + " " + chalk_1.default.hex("#4a5568")(val) + " " + chalk_1.default.hex("#374151")(dt);
|
|
196
|
+
})];
|
|
197
|
+
}
|
|
198
|
+
const secBox = box(`SECRETS (${secrets.length})`, secLines, RIGHT_W, panel === "secrets");
|
|
199
|
+
// ── Print side-by-side ────────────────────────────────────────────────
|
|
200
|
+
const leftPanel = [...projBox, ...["", ""], ...envBox];
|
|
201
|
+
const maxRows = Math.max(leftPanel.length, secBox.length);
|
|
202
|
+
const gap = chalk_1.default.hex("#1e293b")(" │ ");
|
|
203
|
+
for (let r = 0; r < maxRows; r++) {
|
|
204
|
+
const l = leftPanel[r] ?? " ".repeat(LEFT_W);
|
|
205
|
+
const rr = secBox[r] ?? " ".repeat(RIGHT_W);
|
|
206
|
+
console.log(l + gap + rr);
|
|
152
207
|
}
|
|
153
|
-
|
|
208
|
+
// ── Footer ───────────────────────────────────────────────────────────
|
|
209
|
+
console.log();
|
|
210
|
+
console.log(chalk_1.default.hex("#1e293b")(hline(W)));
|
|
211
|
+
const keys = [
|
|
212
|
+
[chalk_1.default.hex("#64748b")("↑↓"), "Navigate"],
|
|
213
|
+
[chalk_1.default.hex("#64748b")("Enter"), "Select"],
|
|
214
|
+
[chalk_1.default.hex("#64748b")("Tab"), "Switch Panel"],
|
|
215
|
+
[chalk_1.default.hex("#64748b")("V"), showValues ? chalk_1.default.green("Hide values") : chalk_1.default.yellow("Show values")],
|
|
216
|
+
[chalk_1.default.hex("#64748b")("Q"), "Quit"],
|
|
217
|
+
].map(([k, a]) => `${k} ${chalk_1.default.hex("#374151")(a)}`).join(chalk_1.default.hex("#1e293b")(" · "));
|
|
218
|
+
console.log(" " + keys);
|
|
219
|
+
console.log();
|
|
220
|
+
console.log(" " + (status || ""));
|
|
154
221
|
}
|
|
155
|
-
//
|
|
222
|
+
// ── Keyboard ──────────────────────────────────────────────────────────────
|
|
156
223
|
readline.emitKeypressEvents(process.stdin);
|
|
157
224
|
if (process.stdin.isTTY)
|
|
158
225
|
process.stdin.setRawMode(true);
|
|
159
|
-
process.stdin.on("keypress", async (
|
|
226
|
+
process.stdin.on("keypress", async (_str, key) => {
|
|
160
227
|
if (!key)
|
|
161
228
|
return;
|
|
162
229
|
// Quit
|
|
163
|
-
if (
|
|
230
|
+
if (key.name === "q" || (key.ctrl && key.name === "c")) {
|
|
164
231
|
if (process.stdin.isTTY)
|
|
165
232
|
process.stdin.setRawMode(false);
|
|
166
233
|
clear();
|
|
167
|
-
console.log(chalk_1.default.cyan("Goodbye!
|
|
234
|
+
console.log(chalk_1.default.cyan(" Goodbye! 👋\n"));
|
|
168
235
|
process.exit(0);
|
|
169
236
|
}
|
|
170
|
-
//
|
|
237
|
+
// Toggle secret values with V
|
|
238
|
+
if (key.name === "v") {
|
|
239
|
+
showValues = !showValues;
|
|
240
|
+
draw();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
// Tab cycle panels
|
|
171
244
|
if (key.name === "tab") {
|
|
172
|
-
|
|
245
|
+
panel = panel === "projects" ? "env" : panel === "env" ? "secrets" : "projects";
|
|
173
246
|
draw();
|
|
174
247
|
return;
|
|
175
248
|
}
|
|
176
|
-
|
|
177
|
-
if (screen === "project") {
|
|
249
|
+
if (panel === "projects") {
|
|
178
250
|
if (key.name === "up")
|
|
179
|
-
|
|
251
|
+
projIdx = Math.max(0, projIdx - 1);
|
|
180
252
|
if (key.name === "down")
|
|
181
|
-
|
|
253
|
+
projIdx = Math.min(projects.length - 1, projIdx + 1);
|
|
182
254
|
if (key.name === "return") {
|
|
183
|
-
|
|
184
|
-
|
|
255
|
+
panel = "env";
|
|
256
|
+
draw();
|
|
185
257
|
return;
|
|
186
258
|
}
|
|
187
259
|
}
|
|
188
|
-
if (
|
|
260
|
+
if (panel === "env") {
|
|
189
261
|
if (key.name === "up")
|
|
190
262
|
envIdx = Math.max(0, envIdx - 1);
|
|
191
263
|
if (key.name === "down")
|
|
192
264
|
envIdx = Math.min(ENVS.length - 1, envIdx + 1);
|
|
193
265
|
if (key.name === "return") {
|
|
194
|
-
|
|
266
|
+
panel = "secrets";
|
|
195
267
|
await loadSecrets();
|
|
196
268
|
return;
|
|
197
269
|
}
|
|
198
270
|
}
|
|
199
|
-
if (
|
|
271
|
+
if (panel === "secrets") {
|
|
200
272
|
if (key.name === "up")
|
|
201
|
-
|
|
273
|
+
secIdx = Math.max(0, secIdx - 1);
|
|
202
274
|
if (key.name === "down")
|
|
203
|
-
|
|
275
|
+
secIdx = Math.min(secrets.length - 1, secIdx + 1);
|
|
204
276
|
}
|
|
205
277
|
draw();
|
|
206
278
|
});
|
|
279
|
+
// ── Boot ──────────────────────────────────────────────────────────────────
|
|
207
280
|
await loadProjects();
|
|
208
281
|
if (projects.length > 0)
|
|
209
282
|
await loadSecrets();
|
|
210
283
|
else
|
|
211
284
|
draw();
|
|
212
285
|
}
|
|
213
|
-
// ─── Commander
|
|
286
|
+
// ─── Commander ───────────────────────────────────────────────────────────────
|
|
214
287
|
exports.uiCommand = new commander_1.Command("ui")
|
|
215
|
-
.description("Launch interactive TUI secrets dashboard (arrow keys
|
|
216
|
-
.action(async () => {
|
|
217
|
-
await runUI();
|
|
218
|
-
});
|
|
288
|
+
.description("Launch interactive TUI secrets dashboard (arrow keys, Tab, Q to quit)")
|
|
289
|
+
.action(async () => { await runUI(); });
|
package/dist/commands/watch.js
CHANGED
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.watchCommand = void 0;
|
|
7
7
|
/**
|
|
8
|
-
* watch.ts
|
|
8
|
+
* watch.ts — Live reload secrets in dev mode
|
|
9
9
|
*
|
|
10
10
|
* Polls XtraSecurity Cloud for secret changes at a configurable interval.
|
|
11
11
|
* When a change is detected, restarts the child process with fresh secrets.
|
|
@@ -26,7 +26,7 @@ function hashSecrets(secrets) {
|
|
|
26
26
|
}
|
|
27
27
|
async function startProcess(command, args, secrets, useShell) {
|
|
28
28
|
if (child) {
|
|
29
|
-
process.stdout.write(chalk_1.default.yellow("\n [watch] Secret change detected
|
|
29
|
+
process.stdout.write(chalk_1.default.yellow("\n [watch] Secret change detected — restarting...\n"));
|
|
30
30
|
child.kill("SIGTERM");
|
|
31
31
|
// Give it 500ms to terminate gracefully
|
|
32
32
|
await new Promise(r => setTimeout(r, 500));
|
|
@@ -50,7 +50,7 @@ async function startProcess(command, args, secrets, useShell) {
|
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
exports.watchCommand = new commander_1.Command("watch")
|
|
53
|
-
.description("Live reload
|
|
53
|
+
.description("Live reload — auto-restart process when secrets change in cloud")
|
|
54
54
|
.option("-p, --project <id>", "Project ID")
|
|
55
55
|
.option("-e, --env <environment>", "Environment", "development")
|
|
56
56
|
.option("-b, --branch <branch>", "Branch", "main")
|
|
@@ -69,19 +69,19 @@ Examples:
|
|
|
69
69
|
const envMap = { dev: "development", stg: "staging", prod: "production" };
|
|
70
70
|
env = envMap[env] || env;
|
|
71
71
|
if (!project)
|
|
72
|
-
project = (0, config_1.
|
|
72
|
+
project = (0, config_1.getRcConfig)().project;
|
|
73
73
|
if (!project) {
|
|
74
74
|
console.error(chalk_1.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'."));
|
|
75
75
|
process.exit(1);
|
|
76
76
|
}
|
|
77
|
-
// Block production watch
|
|
77
|
+
// Block production watch — too dangerous
|
|
78
78
|
if (env === "production") {
|
|
79
|
-
console.error(chalk_1.default.red("
|
|
79
|
+
console.error(chalk_1.default.red("âš xtra watch is not allowed in PRODUCTION for safety reasons."));
|
|
80
80
|
console.error(chalk_1.default.gray(" Use xtra run for one-shot production injection."));
|
|
81
81
|
process.exit(1);
|
|
82
82
|
}
|
|
83
83
|
const pollMs = Math.max(3, parseInt(interval)) * 1000;
|
|
84
|
-
console.log(chalk_1.default.bold(`\n
|
|
84
|
+
console.log(chalk_1.default.bold(`\n👠xtra watch — watching ${env}/${branch} (every ${interval}s)\n`));
|
|
85
85
|
console.log(chalk_1.default.gray(` Press Ctrl+C to stop.\n`));
|
|
86
86
|
// Graceful shutdown
|
|
87
87
|
process.on("SIGINT", () => {
|
|
@@ -110,12 +110,12 @@ Examples:
|
|
|
110
110
|
await startProcess(command, args, secrets, useShell);
|
|
111
111
|
}
|
|
112
112
|
else {
|
|
113
|
-
process.stdout.write(chalk_1.default.gray(` [watch] ${new Date().toLocaleTimeString()}
|
|
113
|
+
process.stdout.write(chalk_1.default.gray(` [watch] ${new Date().toLocaleTimeString()} — no changes\r`));
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
catch (e) {
|
|
117
117
|
process.stdout.write(chalk_1.default.yellow(`\n [watch] Poll failed: ${e.message}\n`));
|
|
118
|
-
// Don't exit
|
|
118
|
+
// Don't exit — keep trying
|
|
119
119
|
}
|
|
120
120
|
}, pollMs);
|
|
121
121
|
});
|
package/dist/lib/config.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getProjectConfig = exports.getAuthToken = exports.clearConfig = exports.setConfig = exports.getConfigValue = exports.getConfig = void 0;
|
|
6
|
+
exports.getRcConfig = exports.getProjectConfig = exports.getAuthToken = exports.clearConfig = exports.setConfig = exports.getConfigValue = exports.getConfig = void 0;
|
|
7
7
|
const conf_1 = __importDefault(require("conf"));
|
|
8
8
|
const PRODUCTION_API_URL = "https://xtra-security.vercel.app/api";
|
|
9
9
|
const config = new conf_1.default({
|
|
@@ -47,3 +47,24 @@ const getProjectConfig = async () => {
|
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
49
|
exports.getProjectConfig = getProjectConfig;
|
|
50
|
+
/**
|
|
51
|
+
* Reads .xtrarc from the current working directory.
|
|
52
|
+
* Returns project/env/branch with fallback to the global conf store.
|
|
53
|
+
* All commands should use this instead of calling getConfigValue() directly.
|
|
54
|
+
*/
|
|
55
|
+
const getRcConfig = () => {
|
|
56
|
+
let rc = {};
|
|
57
|
+
try {
|
|
58
|
+
const rcPath = path_1.default.join(process.cwd(), ".xtrarc");
|
|
59
|
+
if (fs_1.default.existsSync(rcPath)) {
|
|
60
|
+
rc = JSON.parse(fs_1.default.readFileSync(rcPath, "utf-8"));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (_) { }
|
|
64
|
+
return {
|
|
65
|
+
project: rc.project || config.get("project") || "",
|
|
66
|
+
env: rc.env || config.get("env") || "development",
|
|
67
|
+
branch: rc.branch || config.get("branch") || "main",
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
exports.getRcConfig = getRcConfig;
|