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.
@@ -29,7 +29,7 @@ exports.accessCommand = new commander_1.Command("access")
29
29
  .action(async (options) => {
30
30
  let { project, secret, duration, reason } = options;
31
31
  if (!project) {
32
- project = (0, config_1.getConfigValue)("project");
32
+ project = (0, config_1.getRcConfig)().project;
33
33
  }
34
34
  if (!project) {
35
35
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
@@ -97,7 +97,7 @@ exports.accessCommand = new commander_1.Command("access")
97
97
  if (result.expiresAt) {
98
98
  console.log(chalk_1.default.blue(`Access granted until: ${new Date(result.expiresAt).toLocaleString()}`));
99
99
  }
100
- // Audit log privileged action
100
+ // Audit log — privileged action
101
101
  logAuditSafe(options.decision === "approved" ? "ACCESS_APPROVED" : "ACCESS_REJECTED", null, { requestId, decision: options.decision, expiresAt: result.expiresAt });
102
102
  }
103
103
  catch (error) {
@@ -22,9 +22,8 @@ exports.branchCommand
22
22
  // Access parent options (project ID)
23
23
  const parentOpts = exports.branchCommand.opts();
24
24
  let { project } = parentOpts;
25
- if (!project) {
26
- project = (0, config_1.getConfigValue)("project");
27
- }
25
+ if (!project)
26
+ project = (0, config_1.getRcConfig)().project;
28
27
  if (!project) {
29
28
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or checkout a branch."));
30
29
  process.exit(1);
@@ -78,9 +77,8 @@ exports.branchCommand
78
77
  const parentOpts = exports.branchCommand.opts();
79
78
  let { project } = parentOpts;
80
79
  const { description } = options;
81
- if (!project) {
82
- project = (0, config_1.getConfigValue)("project");
83
- }
80
+ if (!project)
81
+ project = (0, config_1.getRcConfig)().project;
84
82
  if (!project) {
85
83
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
86
84
  process.exit(1);
@@ -110,9 +108,8 @@ exports.branchCommand
110
108
  const parentOpts = exports.branchCommand.opts();
111
109
  let { project } = parentOpts;
112
110
  const { yes } = options;
113
- if (!project) {
114
- project = (0, config_1.getConfigValue)("project");
115
- }
111
+ if (!project)
112
+ project = (0, config_1.getRcConfig)().project;
116
113
  if (!project) {
117
114
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
118
115
  process.exit(1);
@@ -176,9 +173,8 @@ exports.branchCommand
176
173
  const parentOpts = exports.branchCommand.opts();
177
174
  let { project } = parentOpts;
178
175
  const { newName, description } = options;
179
- if (!project) {
180
- project = (0, config_1.getConfigValue)("project");
181
- }
176
+ if (!project)
177
+ project = (0, config_1.getRcConfig)().project;
182
178
  if (!project) {
183
179
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or checkout a branch."));
184
180
  process.exit(1);
@@ -17,9 +17,8 @@ exports.checkoutCommand = new commander_1.Command("checkout")
17
17
  .action(async (branchName, options) => {
18
18
  let { project } = options;
19
19
  // Try to get project from config if not provided
20
- if (!project) {
21
- project = (0, config_1.getConfigValue)("project");
22
- }
20
+ if (!project)
21
+ project = (0, config_1.getRcConfig)().project;
23
22
  if (!project) {
24
23
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
25
24
  process.exit(1);
@@ -60,7 +59,7 @@ exports.checkoutCommand = new commander_1.Command("checkout")
60
59
  // Save to config
61
60
  (0, config_1.setConfig)("branch", selectedBranch);
62
61
  (0, config_1.setConfig)("project", project);
63
- console.log(chalk_1.default.green(`✔ Switched to branch '${selectedBranch}'`));
62
+ console.log(chalk_1.default.green(`✔ Switched to branch '${selectedBranch}'`));
64
63
  }
65
64
  catch (error) {
66
65
  spinner.fail("Failed to fetch branches");
@@ -55,11 +55,10 @@ exports.diffCommand = new commander_1.Command("diff")
55
55
  .action(async (env1, env2, options) => {
56
56
  let { project, env, branch, show } = options;
57
57
  // Use config fallback
58
- if (!project) {
59
- project = (0, config_1.getConfigValue)("project");
60
- }
58
+ if (!project)
59
+ project = (0, config_1.getRcConfig)().project;
61
60
  if (!branch) {
62
- branch = (0, config_1.getConfigValue)("branch") || "main";
61
+ branch = (0, config_1.getRcConfig)().branch || "main";
63
62
  }
64
63
  if (!project) {
65
64
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
@@ -155,7 +154,7 @@ function compareSecrets(source, target, show) {
155
154
  }
156
155
  });
157
156
  if (!hasDiff) {
158
- console.log(chalk_1.default.green(" No differences found."));
157
+ console.log(chalk_1.default.green("✔ No differences found."));
159
158
  }
160
159
  else if (!show) {
161
160
  console.log(chalk_1.default.gray("\n(Use --show to reveal secret values)"));
@@ -39,9 +39,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.doctorCommand = void 0;
40
40
  /**
41
41
  * doctor.ts — Diagnose common xtra-cli setup issues
42
- *
43
- * Runs a series of self-diagnostic checks and reports
44
- * pass / warn / fail for each one.
45
42
  */
46
43
  const commander_1 = require("commander");
47
44
  const chalk_1 = __importDefault(require("chalk"));
@@ -49,128 +46,119 @@ const fs = __importStar(require("fs"));
49
46
  const path = __importStar(require("path"));
50
47
  const axios_1 = __importDefault(require("axios"));
51
48
  const config_1 = require("../lib/config");
52
- const PASS = chalk_1.default.green(" ✔");
53
- const FAIL = chalk_1.default.red(" ✗");
54
- const WARN = chalk_1.default.yellow("");
49
+ // ─── Box Drawing Helpers ─────────────────────────────────────────────────────
50
+ const W = 65;
51
+ const B = { tl: "", tr: "╮", bl: "╰", br: "╯", h: "─", v: "│" };
52
+ function hline(w) { return B.h.repeat(w); }
53
+ const PASS = chalk_1.default.green("✓ ");
54
+ const FAIL = chalk_1.default.red("✗ ");
55
+ const WARN = chalk_1.default.yellow("⚠ ");
55
56
  async function runChecks() {
56
57
  const checks = [];
57
58
  const config = (0, config_1.getConfig)();
58
- // ── 1. Node Version ─────────────────────────────────────────────────────────
59
+ // 1. Node Version
59
60
  const nodeMajor = parseInt(process.versions.node.split(".")[0]);
60
61
  checks.push({
61
62
  name: "Node.js Version",
62
63
  status: nodeMajor >= 18 ? "pass" : "fail",
63
64
  message: `v${process.versions.node} ${nodeMajor >= 18 ? "(OK)" : "(requires >=18)"}`,
64
65
  });
65
- // ── 2. Auth Token ────────────────────────────────────────────────────────────
66
+ // 2. Auth Token
66
67
  const token = (0, config_1.getAuthToken)();
67
68
  checks.push({
68
69
  name: "Auth Token",
69
70
  status: token ? "pass" : "fail",
70
71
  message: token ? `Set (${token.substring(0, 8)}...)` : "Not set — run 'xtra login'",
71
72
  });
72
- // ── 3. API URL Configured ────────────────────────────────────────────────────
73
- const apiUrl = config.apiUrl || process.env.XTRA_API_URL;
73
+ // 3. API URL Configured
74
+ const apiUrl = config.apiUrl || process.env.XTRA_API_URL || "https://xtra-security.vercel.app/api";
74
75
  checks.push({
75
76
  name: "API URL",
76
- status: apiUrl ? "pass" : "warn",
77
- message: apiUrl || "Using default (https://xtra-security.vercel.app/api)",
77
+ status: config.apiUrl || process.env.XTRA_API_URL ? "pass" : "warn",
78
+ message: apiUrl,
78
79
  });
79
- // ── 4. API Reachability ──────────────────────────────────────────────────────
80
+ // 4. API Reachability
80
81
  try {
81
- const res = await axios_1.default.get(`${config.apiUrl}/health`, { timeout: 4000 });
82
+ const res = await axios_1.default.get(`${apiUrl}/health`, { timeout: 4000 });
82
83
  checks.push({
83
84
  name: "API Connectivity",
84
85
  status: res.status === 200 ? "pass" : "warn",
85
- message: `${config.apiUrl} → ${res.status} ${res.statusText}`,
86
+ message: `${res.status} ${res.statusText}`,
86
87
  });
87
88
  }
88
89
  catch (e) {
89
- const msg = e.code === "ECONNREFUSED"
90
- ? "Connection refused is the server running?"
91
- : e.code === "ETIMEDOUT"
92
- ? "Timed out — server may be slow or unreachable"
93
- : e.message;
94
- checks.push({ name: "API Connectivity", status: "fail", message: msg });
90
+ const msg = e.code === "ECONNREFUSED" ? "Connection refused (offline?)"
91
+ : e.code === "ETIMEDOUT" ? "Timed out (unreachable)"
92
+ : e.response?.status === 404 ? "API reachable (health endpoint missing)"
93
+ : e.message;
94
+ // Special case: if it hits a 404 it means DNS and port work, but route is missing
95
+ const status = e.response?.status === 404 ? "pass" : "fail";
96
+ checks.push({ name: "API Connectivity", status, message: msg });
95
97
  }
96
- // ── 5. Active Project ────────────────────────────────────────────────────────
97
- const project = (0, config_1.getConfigValue)("project");
98
+ // 5. Active Project
99
+ const project = (0, config_1.getRcConfig)().project;
98
100
  checks.push({
99
101
  name: "Active Project",
100
102
  status: project ? "pass" : "warn",
101
103
  message: project ? project : "Not set — run 'xtra project set <id>'",
102
104
  });
103
- // ── 6. Active Branch ─────────────────────────────────────────────────────────
104
- const branch = (0, config_1.getConfigValue)("branch");
105
+ // 6. Active Branch
106
+ const branch = (0, config_1.getRcConfig)().branch || "main";
105
107
  checks.push({
106
108
  name: "Active Branch",
107
109
  status: "pass",
108
- message: branch || "main (default)",
110
+ message: branch,
109
111
  });
110
- // ── 7. .xtrarc / xtra.json ─────────────────────────────────────────────────
112
+ // 7. Config files
111
113
  const rcPath = path.join(process.cwd(), ".xtrarc");
112
114
  const jsonPath = path.join(process.cwd(), "xtra.json");
113
- const hasProjectConfig = fs.existsSync(rcPath) || fs.existsSync(jsonPath);
115
+ const hasConfig = fs.existsSync(rcPath) || fs.existsSync(jsonPath);
114
116
  checks.push({
115
- name: "Project Config (.xtrarc)",
116
- status: hasProjectConfig ? "pass" : "warn",
117
- message: hasProjectConfig
118
- ? fs.existsSync(rcPath) ? ".xtrarc found" : "xtra.json found"
119
- : "Not found — run 'xtra init' to create one",
120
- });
121
- // ── 8. .env file present ────────────────────────────────────────────────────
122
- const envPath = path.join(process.cwd(), ".env");
123
- checks.push({
124
- name: ".env File",
125
- status: "pass",
126
- message: fs.existsSync(envPath) ? ".env found" : "No .env (not required if using xtra run)",
127
- });
128
- // ── 9. XTRA_MACHINE_TOKEN (for CI) ─────────────────────────────────────────
129
- const machineToken = process.env.XTRA_MACHINE_TOKEN;
130
- checks.push({
131
- name: "CI Machine Token",
132
- status: "pass", // Always pass — optional
133
- message: machineToken ? "XTRA_MACHINE_TOKEN is set ✔" : "Not set (only needed for CI/CD pipelines)",
117
+ name: "Project Config",
118
+ status: hasConfig ? "pass" : "warn",
119
+ message: hasConfig ? (fs.existsSync(rcPath) ? ".xtrarc found" : "xtra.json found") : "Not found (run 'xtra init')",
134
120
  });
135
121
  return checks;
136
122
  }
137
123
  exports.doctorCommand = new commander_1.Command("doctor")
138
- .description("Diagnose common setup issues (connectivity, auth, config)")
124
+ .description("Diagnose CLI configuration and API connectivity")
139
125
  .option("--json", "Output results as JSON", false)
140
126
  .action(async (options) => {
141
127
  if (!options.json) {
142
- console.log(chalk_1.default.bold("\n🩺 xtra doctor — running diagnostics...\n"));
128
+ console.log();
129
+ console.log(chalk_1.default.bold.cyan(" ⚕ XtraSecurity Diagnostics"));
130
+ console.log(chalk_1.default.hex("#4a5568")(" " + B.tl + hline(W) + B.tr));
143
131
  }
144
132
  const checks = await runChecks();
145
133
  if (options.json) {
146
134
  process.stdout.write(JSON.stringify(checks, null, 2) + "\n");
147
135
  return;
148
136
  }
149
- let failures = 0;
150
- let warnings = 0;
137
+ let failures = 0, warnings = 0;
151
138
  for (const c of checks) {
152
- const icon = c.status === "pass" ? PASS : c.status === "warn" ? WARN : FAIL;
153
- const nameStr = chalk_1.default.bold(c.name.padEnd(22));
154
- const msgStr = c.status === "fail"
155
- ? chalk_1.default.red(c.message)
156
- : c.status === "warn"
157
- ? chalk_1.default.yellow(c.message)
158
- : chalk_1.default.gray(c.message);
159
- console.log(`${icon} ${nameStr}${msgStr}`);
160
139
  if (c.status === "fail")
161
140
  failures++;
162
141
  if (c.status === "warn")
163
142
  warnings++;
143
+ const icon = c.status === "pass" ? PASS : c.status === "warn" ? WARN : FAIL;
144
+ const name = chalk_1.default.bold(chalk_1.default.white(c.name.padEnd(20)));
145
+ const msg = c.status === "pass" ? chalk_1.default.hex("#94a3b8")(c.message)
146
+ : c.status === "warn" ? chalk_1.default.yellow(c.message)
147
+ : chalk_1.default.red(c.message);
148
+ console.log(chalk_1.default.hex("#4a5568")(" " + B.v) + ` ${icon}${name} ${msg}`.padEnd(W) + chalk_1.default.hex("#4a5568")(B.v));
164
149
  }
150
+ console.log(chalk_1.default.hex("#4a5568")(" " + B.bl + hline(W) + B.br));
165
151
  console.log();
166
152
  if (failures === 0 && warnings === 0) {
167
- console.log(chalk_1.default.green.bold(" All checks passed! Your CLI is ready to use."));
153
+ console.log(chalk_1.default.green(" All systems operational. The CLI is ready to use."));
168
154
  }
169
155
  else {
156
+ const parts = [];
170
157
  if (failures > 0)
171
- console.log(chalk_1.default.red(` ${failures} issue(s) need attention.`));
158
+ parts.push(chalk_1.default.red(`${failures} error(s)`));
172
159
  if (warnings > 0)
173
- console.log(chalk_1.default.yellow(` ${warnings} warning(s) found.`));
160
+ parts.push(chalk_1.default.yellow(`${warnings} warning(s)`));
161
+ console.log(` Found ${parts.join(" and ")}.`);
174
162
  }
175
163
  console.log();
176
164
  });
@@ -21,9 +21,9 @@ exports.envCommand
21
21
  .action(async (options) => {
22
22
  try {
23
23
  let projectId = options.project;
24
- const branch = options.branch || (0, config_1.getConfigValue)("branch") || "main";
24
+ const branch = options.branch || (0, config_1.getRcConfig)().branch || "main";
25
25
  if (!projectId) {
26
- projectId = (0, config_1.getConfigValue)("project");
26
+ projectId = (0, config_1.getRcConfig)().project;
27
27
  if (!projectId) {
28
28
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
29
29
  process.exit(1);
@@ -21,11 +21,10 @@ exports.exportCommand = new commander_1.Command("export")
21
21
  .action(async (options) => {
22
22
  let { project, env, branch, format, output } = options;
23
23
  // Use config fallback
24
- if (!project) {
25
- project = (0, config_1.getConfigValue)("project");
26
- }
24
+ if (!project)
25
+ project = (0, config_1.getRcConfig)().project;
27
26
  if (!branch) {
28
- branch = (0, config_1.getConfigValue)("branch") || "main";
27
+ branch = (0, config_1.getRcConfig)().branch || "main";
29
28
  }
30
29
  // Normalize Env
31
30
  const envMap = { dev: "development", stg: "staging", prod: "production" };
@@ -63,7 +62,7 @@ exports.exportCommand = new commander_1.Command("export")
63
62
  if (output) {
64
63
  const outputPath = path_1.default.resolve(process.cwd(), output);
65
64
  fs_1.default.writeFileSync(outputPath, content, "utf-8");
66
- console.log(chalk_1.default.green(`✔ Secrets exported to ${outputPath}`));
65
+ console.log(chalk_1.default.green(`✔ Secrets exported to ${outputPath}`));
67
66
  }
68
67
  else {
69
68
  console.log(content);
@@ -80,11 +80,10 @@ exports.generateCommand = new commander_1.Command("generate")
80
80
  .action(async (options) => {
81
81
  let { project, env, branch, output, format, force } = options;
82
82
  // Use config fallback
83
- if (!project) {
84
- project = (0, config_1.getConfigValue)("project");
85
- }
83
+ if (!project)
84
+ project = (0, config_1.getRcConfig)().project;
86
85
  if (!branch) {
87
- branch = (0, config_1.getConfigValue)("branch") || "main";
86
+ branch = (0, config_1.getRcConfig)().branch || "main";
88
87
  }
89
88
  // Normalize Env
90
89
  const envMap = { dev: "development", stg: "staging", prod: "production" };
@@ -18,7 +18,7 @@ exports.historyCommand
18
18
  try {
19
19
  let projectId = options.project;
20
20
  if (!projectId) {
21
- projectId = (0, config_1.getConfigValue)("project");
21
+ projectId = (0, config_1.getRcConfig)().project;
22
22
  if (!projectId) {
23
23
  console.error(chalk_1.default.red("Error: Project ID not found. Use -p <id> or run 'xtra project set' first."));
24
24
  process.exit(1);
@@ -56,7 +56,7 @@ exports.rollbackCommand
56
56
  try {
57
57
  let projectId = options.project;
58
58
  if (!projectId) {
59
- projectId = (0, config_1.getConfigValue)("project");
59
+ projectId = (0, config_1.getRcConfig)().project;
60
60
  if (!projectId) {
61
61
  console.error(chalk_1.default.red("Error: Project ID not found. Use -p <id> or run 'xtra project set' first."));
62
62
  process.exit(1);
@@ -24,11 +24,10 @@ exports.importCommand = new commander_1.Command("import")
24
24
  .action(async (file, options) => {
25
25
  let { project, env, branch, format, prefix } = options;
26
26
  // Use config fallback
27
- if (!project) {
28
- project = (0, config_1.getConfigValue)("project");
29
- }
27
+ if (!project)
28
+ project = (0, config_1.getRcConfig)().project;
30
29
  if (!branch) {
31
- branch = (0, config_1.getConfigValue)("branch") || "main";
30
+ branch = (0, config_1.getRcConfig)().branch || "main";
32
31
  }
33
32
  // Normalize Env
34
33
  const envMap = { dev: "development", stg: "staging", prod: "production" };
@@ -23,7 +23,7 @@ exports.integrationCommand
23
23
  try {
24
24
  let projectId = options.project;
25
25
  if (!projectId) {
26
- projectId = (0, config_1.getConfigValue)("project");
26
+ projectId = (0, config_1.getRcConfig)().project;
27
27
  if (!projectId) {
28
28
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
29
29
  process.exit(1);
@@ -48,7 +48,7 @@ exports.integrationCommand
48
48
  name: "repo",
49
49
  message: "Select a repository to sync to:",
50
50
  choices: repos.map((r) => ({
51
- name: `${r.fullName} ${r.private ? "(🔒)" : ""}`,
51
+ name: `${r.fullName} ${r.private ? "(🔒)" : ""}`,
52
52
  value: r.id.toString(),
53
53
  short: r.fullName
54
54
  }))
@@ -84,10 +84,10 @@ exports.integrationCommand
84
84
  // Show details
85
85
  result.results.forEach((r) => {
86
86
  if (r.success) {
87
- console.log(chalk_1.default.green(`✓ ${r.key}`));
87
+ console.log(chalk_1.default.green(`✓ ${r.key}`));
88
88
  }
89
89
  else {
90
- console.log(chalk_1.default.red(`✗ ${r.key}: ${r.error}`));
90
+ console.log(chalk_1.default.red(`✗ ${r.key}: ${r.error}`));
91
91
  }
92
92
  });
93
93
  }
@@ -109,7 +109,7 @@ exports.kubernetesCommand
109
109
  try {
110
110
  let projectId = options.project;
111
111
  if (!projectId) {
112
- projectId = (0, config_1.getConfigValue)("project");
112
+ projectId = (0, config_1.getRcConfig)().project;
113
113
  if (!projectId) {
114
114
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
115
115
  process.exit(1);
@@ -153,7 +153,7 @@ exports.kubernetesCommand
153
153
  }
154
154
  let projectId = options.project;
155
155
  if (!projectId) {
156
- projectId = (0, config_1.getConfigValue)("project");
156
+ projectId = (0, config_1.getRcConfig)().project;
157
157
  if (!projectId) {
158
158
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
159
159
  process.exit(1);
@@ -38,12 +38,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.localCommand = void 0;
40
40
  /**
41
- * local.ts Toggle between cloud/local-only mode for offline development
41
+ * local.ts — Toggle between cloud/local-only mode for offline development
42
42
  *
43
43
  * In "local" mode:
44
44
  * - xtra run reads from .env.local instead of calling the API
45
45
  * - A flag XTRA_LOCAL_MODE=true is written to the CLI config
46
- * - All API calls are bypassed completely offline capable
46
+ * - All API calls are bypassed — completely offline capable
47
47
  *
48
48
  * Usage:
49
49
  * xtra local on # Enable local mode (reads from .env.local)
@@ -73,11 +73,11 @@ This allows fully offline development without any API calls.
73
73
  Examples:
74
74
  $ xtra local status # Check current mode
75
75
  $ xtra local on # Enable offline (local) mode
76
- $ xtra local off # Disable back to cloud mode
77
- $ xtra local sync # Pull cloud secrets .env.local
76
+ $ xtra local off # Disable — back to cloud mode
77
+ $ xtra local sync # Pull cloud secrets → .env.local
78
78
  $ xtra local sync -p proj -e production # Pull production to .env.local
79
79
  `);
80
- // ── status ────────────────────────────────────────────────────────────────────
80
+ // ── status ────────────────────────────────────────────────────────────────────
81
81
  exports.localCommand
82
82
  .command("status")
83
83
  .description("Show current cloud/local mode")
@@ -86,7 +86,7 @@ exports.localCommand
86
86
  const envFilePath = path.join(process.cwd(), LOCAL_ENV_FILE);
87
87
  const hasLocalFile = fs.existsSync(envFilePath);
88
88
  console.log(chalk_1.default.bold("\nMode Status:\n"));
89
- console.log(` Mode : ${mode ? chalk_1.default.yellow("🔌 LOCAL (offline)") : chalk_1.default.green(" CLOUD")}`);
89
+ console.log(` Mode : ${mode ? chalk_1.default.yellow("🔌 LOCAL (offline)") : chalk_1.default.green("☁ CLOUD")}`);
90
90
  console.log(` .env.local : ${hasLocalFile ? chalk_1.default.green("Found") : chalk_1.default.gray("Not found")}`);
91
91
  console.log(` Config flag : ${chalk_1.default.gray((0, config_1.getConfigValue)("localMode") ? "true" : "false")}`);
92
92
  console.log(` Env var : ${chalk_1.default.gray(process.env.XTRA_LOCAL_MODE || "(not set)")}`);
@@ -97,34 +97,34 @@ exports.localCommand
97
97
  else {
98
98
  console.log(chalk_1.default.gray(" Run 'xtra local off' to switch back to cloud mode."));
99
99
  if (!hasLocalFile) {
100
- console.log(chalk_1.default.yellow(" No .env.local file found. Run 'xtra local sync' to pull secrets."));
100
+ console.log(chalk_1.default.yellow(" âš  No .env.local file found. Run 'xtra local sync' to pull secrets."));
101
101
  }
102
102
  }
103
103
  console.log();
104
104
  });
105
- // ── on ────────────────────────────────────────────────────────────────────────
105
+ // ── on ────────────────────────────────────────────────────────────────────────
106
106
  exports.localCommand
107
107
  .command("on")
108
- .description("Enable local mode secrets read from .env.local")
108
+ .description("Enable local mode — secrets read from .env.local")
109
109
  .action(() => {
110
110
  (0, config_1.setConfig)("localMode", true);
111
- console.log(chalk_1.default.yellow("🔌 Local mode ENABLED."));
111
+ console.log(chalk_1.default.yellow("🔌 Local mode ENABLED."));
112
112
  console.log(chalk_1.default.gray(" 'xtra run' will now read from .env.local instead of the cloud."));
113
113
  const localFilePath = path.join(process.cwd(), LOCAL_ENV_FILE);
114
114
  if (!fs.existsSync(localFilePath)) {
115
- console.log(chalk_1.default.yellow(` No .env.local file found. Run 'xtra local sync' to populate it.`));
115
+ console.log(chalk_1.default.yellow(` âš  No .env.local file found. Run 'xtra local sync' to populate it.`));
116
116
  }
117
117
  });
118
- // ── off ───────────────────────────────────────────────────────────────────────
118
+ // ── off ───────────────────────────────────────────────────────────────────────
119
119
  exports.localCommand
120
120
  .command("off")
121
- .description("Disable local mode secrets fetched from cloud again")
121
+ .description("Disable local mode — secrets fetched from cloud again")
122
122
  .action(() => {
123
123
  (0, config_1.setConfig)("localMode", false);
124
- console.log(chalk_1.default.green(" Cloud mode ENABLED."));
124
+ console.log(chalk_1.default.green("☁ Cloud mode ENABLED."));
125
125
  console.log(chalk_1.default.gray(" 'xtra run' will now fetch secrets from XtraSecurity Cloud."));
126
126
  });
127
- // ── sync ─────────────────────────────────────────────────────────────────────
127
+ // ── sync ─────────────────────────────────────────────────────────────────────
128
128
  exports.localCommand
129
129
  .command("sync")
130
130
  .description("Pull cloud secrets to .env.local for offline use")
@@ -138,7 +138,7 @@ exports.localCommand
138
138
  const envMap = { dev: "development", stg: "staging", prod: "production" };
139
139
  env = envMap[env] || env;
140
140
  if (!project)
141
- project = (0, config_1.getConfigValue)("project");
141
+ project = (0, config_1.getRcConfig)().project;
142
142
  if (!project) {
143
143
  console.error(chalk_1.default.red("Error: Project ID required. Use -p <id>."));
144
144
  process.exit(1);
@@ -146,7 +146,7 @@ exports.localCommand
146
146
  const outputPath = path.resolve(process.cwd(), output);
147
147
  // Warn if production
148
148
  if (env === "production" && !overwrite) {
149
- console.log(chalk_1.default.red(`⚠ You are syncing PRODUCTION secrets to ${output}.`));
149
+ console.log(chalk_1.default.red(`âš  You are syncing PRODUCTION secrets to ${output}.`));
150
150
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
151
151
  const confirmed = await new Promise(resolve => {
152
152
  rl.question(chalk_1.default.yellow("Type 'yes' to confirm: "), (ans) => {
@@ -170,14 +170,14 @@ exports.localCommand
170
170
  }
171
171
  // Write dotenv format
172
172
  const lines = [
173
- `# xtra local sync ${env}/${branch}`,
173
+ `# xtra local sync — ${env}/${branch}`,
174
174
  `# Generated: ${new Date().toISOString()}`,
175
175
  `# DO NOT COMMIT THIS FILE`,
176
176
  "",
177
177
  ...Object.entries(secrets).map(([k, v]) => `${k}=${v}`)
178
178
  ];
179
179
  fs.writeFileSync(outputPath, lines.join("\n") + "\n", "utf8");
180
- console.log(chalk_1.default.green(`✅ Synced ${count} secrets to ${output}`));
180
+ console.log(chalk_1.default.green(`✅ Synced ${count} secrets to ${output}`));
181
181
  console.log(chalk_1.default.gray(" Run 'xtra local on' to switch to local mode."));
182
182
  // Remind about .gitignore
183
183
  const gitignorePath = path.join(process.cwd(), ".gitignore");
@@ -187,7 +187,7 @@ exports.localCommand
187
187
  }
188
188
  catch (_) { }
189
189
  if (!gitignore.includes(output)) {
190
- console.log(chalk_1.default.yellow(`\n Remember to add '${output}' to your .gitignore!`));
190
+ console.log(chalk_1.default.yellow(`\n âš  Remember to add '${output}' to your .gitignore!`));
191
191
  }
192
192
  }
193
193
  catch (e) {
@@ -19,9 +19,8 @@ exports.rollbackCommand = new commander_1.Command("rollback")
19
19
  .action(async (key, options) => {
20
20
  let { project, env } = options;
21
21
  // Use config fallback
22
- if (!project) {
23
- project = (0, config_1.getConfigValue)("project");
24
- }
22
+ if (!project)
23
+ project = (0, config_1.getRcConfig)().project;
25
24
  if (!project) {
26
25
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
27
26
  process.exit(1);
@@ -20,9 +20,8 @@ exports.rotateCommand = new commander_1.Command("rotate")
20
20
  .action(async (key, options) => {
21
21
  let { project, env, strategy, promote, value } = 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
  // Normalize Env
27
26
  const envMap = { dev: "development", stg: "staging", prod: "production" };
28
27
  env = envMap[env] || env;
@@ -36,7 +35,7 @@ exports.rotateCommand = new commander_1.Command("rotate")
36
35
  const { confirmProd } = await inquirer.prompt([{
37
36
  type: "confirm",
38
37
  name: "confirmProd",
39
- message: chalk_1.default.red(`⚠ You are about to rotate a secret in PRODUCTION (${key}). Proceed?`),
38
+ message: chalk_1.default.red(`âš  You are about to rotate a secret in PRODUCTION (${key}). Proceed?`),
40
39
  default: false,
41
40
  }]);
42
41
  if (!confirmProd) {