xtra-cli 1.0.4 → 1.0.5
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/bin/xtra.js +2 -294
- package/dist/commands/access.js +1 -184
- package/dist/commands/admin.js +5 -118
- package/dist/commands/audit.js +1 -67
- package/dist/commands/branch.js +1 -212
- package/dist/commands/checkout.js +1 -80
- package/dist/commands/ci.js +31 -341
- package/dist/commands/completion.js +85 -224
- package/dist/commands/diff.js +6 -162
- package/dist/commands/doctor.js +2 -164
- package/dist/commands/env.js +2 -70
- package/dist/commands/export.js +2 -83
- package/dist/commands/generate.js +3 -179
- package/dist/commands/history.js +4 -77
- package/dist/commands/import.js +1 -121
- package/dist/commands/init.js +7 -205
- package/dist/commands/integration.js +11 -188
- package/dist/commands/jit-run.js +5 -0
- package/dist/commands/local.js +16 -198
- package/dist/commands/login.js +16 -198
- package/dist/commands/login.test.js +16 -51
- package/dist/commands/logout.js +1 -0
- package/dist/commands/logs.js +3 -121
- package/dist/commands/profile.js +14 -184
- package/dist/commands/project.js +1 -165
- package/dist/commands/rollback.js +1 -95
- package/dist/commands/rotate.js +3 -93
- package/dist/commands/run.js +11 -215
- package/dist/commands/scan.js +25 -127
- package/dist/commands/secrets.js +14 -305
- package/dist/commands/simulate.js +6 -109
- package/dist/commands/status.js +2 -93
- package/dist/commands/template.js +22 -276
- package/dist/commands/ui.js +2 -289
- package/dist/commands/watch.js +17 -123
- package/dist/lib/api.js +1 -187
- package/dist/lib/api.test.js +1 -89
- package/dist/lib/audit.js +1 -136
- package/dist/lib/config.js +1 -72
- package/dist/lib/config.test.js +1 -47
- package/dist/lib/crypto.js +1 -50
- package/dist/lib/manifest.js +1 -52
- package/dist/lib/profiles.js +1 -103
- package/package.json +2 -2
package/dist/commands/doctor.js
CHANGED
|
@@ -1,164 +1,2 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.doctorCommand = void 0;
|
|
40
|
-
/**
|
|
41
|
-
* doctor.ts — Diagnose common xtra-cli setup issues
|
|
42
|
-
*/
|
|
43
|
-
const commander_1 = require("commander");
|
|
44
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
45
|
-
const fs = __importStar(require("fs"));
|
|
46
|
-
const path = __importStar(require("path"));
|
|
47
|
-
const axios_1 = __importDefault(require("axios"));
|
|
48
|
-
const config_1 = require("../lib/config");
|
|
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("⚠ ");
|
|
56
|
-
async function runChecks() {
|
|
57
|
-
const checks = [];
|
|
58
|
-
const config = (0, config_1.getConfig)();
|
|
59
|
-
// 1. Node Version
|
|
60
|
-
const nodeMajor = parseInt(process.versions.node.split(".")[0]);
|
|
61
|
-
checks.push({
|
|
62
|
-
name: "Node.js Version",
|
|
63
|
-
status: nodeMajor >= 18 ? "pass" : "fail",
|
|
64
|
-
message: `v${process.versions.node} ${nodeMajor >= 18 ? "(OK)" : "(requires >=18)"}`,
|
|
65
|
-
});
|
|
66
|
-
// 2. Auth Token
|
|
67
|
-
const token = (0, config_1.getAuthToken)();
|
|
68
|
-
checks.push({
|
|
69
|
-
name: "Auth Token",
|
|
70
|
-
status: token ? "pass" : "fail",
|
|
71
|
-
message: token ? `Set (${token.substring(0, 8)}...)` : "Not set — run 'xtra login'",
|
|
72
|
-
});
|
|
73
|
-
// 3. API URL Configured
|
|
74
|
-
const apiUrl = config.apiUrl || process.env.XTRA_API_URL || "https://www.xtrasecurity.in/api";
|
|
75
|
-
checks.push({
|
|
76
|
-
name: "API URL",
|
|
77
|
-
status: config.apiUrl || process.env.XTRA_API_URL ? "pass" : "warn",
|
|
78
|
-
message: apiUrl,
|
|
79
|
-
});
|
|
80
|
-
// 4. API Reachability
|
|
81
|
-
try {
|
|
82
|
-
const res = await axios_1.default.get(`${apiUrl}/health`, { timeout: 4000 });
|
|
83
|
-
checks.push({
|
|
84
|
-
name: "API Connectivity",
|
|
85
|
-
status: res.status === 200 ? "pass" : "warn",
|
|
86
|
-
message: `${res.status} ${res.statusText}`,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
catch (e) {
|
|
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 });
|
|
97
|
-
}
|
|
98
|
-
// 5. Active Project
|
|
99
|
-
const project = (0, config_1.getRcConfig)().project;
|
|
100
|
-
checks.push({
|
|
101
|
-
name: "Active Project",
|
|
102
|
-
status: project ? "pass" : "warn",
|
|
103
|
-
message: project ? project : "Not set — run 'xtra project set <id>'",
|
|
104
|
-
});
|
|
105
|
-
// 6. Active Branch
|
|
106
|
-
const branch = (0, config_1.getRcConfig)().branch || "main";
|
|
107
|
-
checks.push({
|
|
108
|
-
name: "Active Branch",
|
|
109
|
-
status: "pass",
|
|
110
|
-
message: branch,
|
|
111
|
-
});
|
|
112
|
-
// 7. Config files
|
|
113
|
-
const rcPath = path.join(process.cwd(), ".xtrarc");
|
|
114
|
-
const jsonPath = path.join(process.cwd(), "xtra.json");
|
|
115
|
-
const hasConfig = fs.existsSync(rcPath) || fs.existsSync(jsonPath);
|
|
116
|
-
checks.push({
|
|
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')",
|
|
120
|
-
});
|
|
121
|
-
return checks;
|
|
122
|
-
}
|
|
123
|
-
exports.doctorCommand = new commander_1.Command("doctor")
|
|
124
|
-
.description("Diagnose CLI configuration and API connectivity")
|
|
125
|
-
.option("--json", "Output results as JSON", false)
|
|
126
|
-
.action(async (options) => {
|
|
127
|
-
if (!options.json) {
|
|
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));
|
|
131
|
-
}
|
|
132
|
-
const checks = await runChecks();
|
|
133
|
-
if (options.json) {
|
|
134
|
-
process.stdout.write(JSON.stringify(checks, null, 2) + "\n");
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
let failures = 0, warnings = 0;
|
|
138
|
-
for (const c of checks) {
|
|
139
|
-
if (c.status === "fail")
|
|
140
|
-
failures++;
|
|
141
|
-
if (c.status === "warn")
|
|
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));
|
|
149
|
-
}
|
|
150
|
-
console.log(chalk_1.default.hex("#4a5568")(" " + B.bl + hline(W) + B.br));
|
|
151
|
-
console.log();
|
|
152
|
-
if (failures === 0 && warnings === 0) {
|
|
153
|
-
console.log(chalk_1.default.green(" ✓ All systems operational. The CLI is ready to use."));
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
const parts = [];
|
|
157
|
-
if (failures > 0)
|
|
158
|
-
parts.push(chalk_1.default.red(`${failures} error(s)`));
|
|
159
|
-
if (warnings > 0)
|
|
160
|
-
parts.push(chalk_1.default.yellow(`${warnings} warning(s)`));
|
|
161
|
-
console.log(` Found ${parts.join(" and ")}.`);
|
|
162
|
-
}
|
|
163
|
-
console.log();
|
|
164
|
-
});
|
|
1
|
+
var _=Object.create;var u=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var L=Object.getPrototypeOf,E=Object.prototype.hasOwnProperty;var G=(s,e)=>{for(var o in e)u(s,o,{get:e[o],enumerable:!0})},j=(s,e,o,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of O(e))!E.call(s,t)&&t!==o&&u(s,t,{get:()=>e[t],enumerable:!(r=$(e,t))||r.enumerable});return s};var i=(s,e,o)=>(o=s!=null?_(L(s)):{},j(e||!s||!s.__esModule?u(o,"default",{value:s,enumerable:!0}):o,s)),D=s=>j(u({},"__esModule",{value:!0}),s);var V={};G(V,{doctorCommand:()=>M});module.exports=D(V);var P=require("commander"),n=i(require("chalk")),f=i(require("fs")),C=i(require("path")),U=i(require("axios"));var A=i(require("conf")),d=i(require("fs")),v=i(require("path")),x="https://www.xtrasecurity.in/api",c=new A.default({projectName:"xtra-cli",defaults:{apiUrl:x}}),m=c.get("apiUrl");m&&!process.env.XTRA_API_URL&&(m.includes("localhost")||m.includes("xtra-security.vercel.app"))&&c.set("apiUrl",x);var b=()=>({...c.store,apiUrl:process.env.XTRA_API_URL||c.get("apiUrl")||x});var k=()=>c.get("token");var y=()=>{let s={};try{let e=v.default.join(process.cwd(),".xtrarc");d.default.existsSync(e)&&(s=JSON.parse(d.default.readFileSync(e,"utf-8")))}catch{}return{project:s.project||c.get("project")||"",env:s.env||c.get("env")||"development",branch:s.branch||c.get("branch")||"main"}};var w=65,p={tl:"\u256D",tr:"\u256E",bl:"\u2570",br:"\u256F",h:"\u2500",v:"\u2502"};function I(s){return p.h.repeat(s)}var F=n.default.green("\u2713 "),X=n.default.red("\u2717 "),B=n.default.yellow("\u26A0 ");async function J(){let s=[],e=b(),o=parseInt(process.versions.node.split(".")[0]);s.push({name:"Node.js Version",status:o>=18?"pass":"fail",message:`v${process.versions.node} ${o>=18?"(OK)":"(requires >=18)"}`});let r=k();s.push({name:"Auth Token",status:r?"pass":"fail",message:r?`Set (${r.substring(0,8)}...)`:"Not set \u2014 run 'xtra login'"});let t=e.apiUrl||process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api";s.push({name:"API URL",status:e.apiUrl||process.env.XTRA_API_URL?"pass":"warn",message:t});try{let a=await U.default.get(`${t}/health`,{timeout:4e3});s.push({name:"API Connectivity",status:a.status===200?"pass":"warn",message:`${a.status} ${a.statusText}`})}catch(a){let N=a.code==="ECONNREFUSED"?"Connection refused (offline?)":a.code==="ETIMEDOUT"?"Timed out (unreachable)":a.response?.status===404?"API reachable (health endpoint missing)":a.message,T=a.response?.status===404?"pass":"fail";s.push({name:"API Connectivity",status:T,message:N})}let l=y().project;s.push({name:"Active Project",status:l?"pass":"warn",message:l||"Not set \u2014 run 'xtra project set <id>'"});let h=y().branch||"main";s.push({name:"Active Branch",status:"pass",message:h});let g=C.join(process.cwd(),".xtrarc"),R=C.join(process.cwd(),"xtra.json"),S=f.existsSync(g)||f.existsSync(R);return s.push({name:"Project Config",status:S?"pass":"warn",message:S?f.existsSync(g)?".xtrarc found":"xtra.json found":"Not found (run 'xtra init')"}),s}var M=new P.Command("doctor").description("Diagnose CLI configuration and API connectivity").option("--json","Output results as JSON",!1).action(async s=>{s.json||(console.log(),console.log(n.default.bold.cyan(" \u2695 XtraSecurity Diagnostics")),console.log(n.default.hex("#4a5568")(" "+p.tl+I(w)+p.tr)));let e=await J();if(s.json){process.stdout.write(JSON.stringify(e,null,2)+`
|
|
2
|
+
`);return}let o=0,r=0;for(let t of e){t.status==="fail"&&o++,t.status==="warn"&&r++;let l=t.status==="pass"?F:t.status==="warn"?B:X,h=n.default.bold(n.default.white(t.name.padEnd(20))),g=t.status==="pass"?n.default.hex("#94a3b8")(t.message):t.status==="warn"?n.default.yellow(t.message):n.default.red(t.message);console.log(n.default.hex("#4a5568")(" "+p.v)+` ${l}${h} ${g}`.padEnd(w)+n.default.hex("#4a5568")(p.v))}if(console.log(n.default.hex("#4a5568")(" "+p.bl+I(w)+p.br)),console.log(),o===0&&r===0)console.log(n.default.green(" \u2713 All systems operational. The CLI is ready to use."));else{let t=[];o>0&&t.push(n.default.red(`${o} error(s)`)),r>0&&t.push(n.default.yellow(`${r} warning(s)`)),console.log(` Found ${t.join(" and ")}.`)}console.log()});0&&(module.exports={doctorCommand});
|
package/dist/commands/env.js
CHANGED
|
@@ -1,70 +1,2 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.envCommand = void 0;
|
|
7
|
-
const commander_1 = require("commander");
|
|
8
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
-
const api_1 = require("../lib/api");
|
|
10
|
-
const config_1 = require("../lib/config");
|
|
11
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
12
|
-
exports.envCommand = new commander_1.Command("env");
|
|
13
|
-
exports.envCommand
|
|
14
|
-
.command("clone")
|
|
15
|
-
.description("Clone secrets from one environment to another")
|
|
16
|
-
.requiredOption("--from <env>", "Source environment (e.g. production)")
|
|
17
|
-
.requiredOption("--to <env>", "Destination environment (e.g. staging)")
|
|
18
|
-
.option("-p, --project <id>", "Project ID")
|
|
19
|
-
.option("-b, --branch <name>", "Branch Name")
|
|
20
|
-
.option("--overwrite", "Overwrite existing secrets", false)
|
|
21
|
-
.action(async (options) => {
|
|
22
|
-
try {
|
|
23
|
-
let projectId = options.project;
|
|
24
|
-
const branch = options.branch || (0, config_1.getRcConfig)().branch || "main";
|
|
25
|
-
if (!projectId) {
|
|
26
|
-
projectId = (0, config_1.getRcConfig)().project;
|
|
27
|
-
if (!projectId) {
|
|
28
|
-
console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
const fromEnv = options.from.toLowerCase();
|
|
33
|
-
const toEnv = options.to.toLowerCase();
|
|
34
|
-
if (fromEnv === toEnv) {
|
|
35
|
-
console.error(chalk_1.default.red("Source and destination must be different."));
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
console.log(chalk_1.default.blue(`Cloning secrets from ${fromEnv} to ${toEnv} (branch: ${branch})...`));
|
|
39
|
-
if (options.overwrite) {
|
|
40
|
-
console.log(chalk_1.default.yellow("Warning: Existing secrets in destination will be overwritten!"));
|
|
41
|
-
}
|
|
42
|
-
const confirm = await inquirer_1.default.prompt([
|
|
43
|
-
{
|
|
44
|
-
type: "confirm",
|
|
45
|
-
name: "proceed",
|
|
46
|
-
message: "Are you sure you want to proceed?",
|
|
47
|
-
default: false
|
|
48
|
-
}
|
|
49
|
-
]);
|
|
50
|
-
if (!confirm.proceed) {
|
|
51
|
-
console.log("Operation cancelled.");
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const result = await api_1.api.cloneEnvironment(projectId, fromEnv, toEnv, options.overwrite, branch);
|
|
55
|
-
if (result.success) {
|
|
56
|
-
console.log(chalk_1.default.green("\nClone successful!"));
|
|
57
|
-
console.log(`Copied: ${result.summary.copied}`);
|
|
58
|
-
console.log(`Updated: ${result.summary.updated} (overwrite: ${options.overwrite})`);
|
|
59
|
-
console.log(`Skipped: ${result.summary.skipped}`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
if (error.response && error.response.data && error.response.data.error) {
|
|
64
|
-
console.error(chalk_1.default.red("Error cloning environment:"), error.response.data.error);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
console.error(chalk_1.default.red("Error cloning environment:"), error.message || error);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
});
|
|
1
|
+
var U=Object.create;var d=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var L=Object.getPrototypeOf,P=Object.prototype.hasOwnProperty;var _=(e,s)=>{for(var t in s)d(e,t,{get:s[t],enumerable:!0})},w=(e,s,t,n)=>{if(s&&typeof s=="object"||typeof s=="function")for(let o of A(s))!P.call(e,o)&&o!==t&&d(e,o,{get:()=>s[o],enumerable:!(n=R(s,o))||n.enumerable});return e};var a=(e,s,t)=>(t=e!=null?U(L(e)):{},w(s||!e||!e.__esModule?d(t,"default",{value:e,enumerable:!0}):t,e)),q=e=>w(d({},"__esModule",{value:!0}),e);var B={};_(B,{envCommand:()=>I});module.exports=q(B);var k=require("commander"),c=a(require("chalk"));var j=a(require("axios"));var h=a(require("conf")),m=a(require("fs")),$=a(require("path")),y="https://www.xtrasecurity.in/api",i=new h.default({projectName:"xtra-cli",defaults:{apiUrl:y}}),u=i.get("apiUrl");u&&!process.env.XTRA_API_URL&&(u.includes("localhost")||u.includes("xtra-security.vercel.app"))&&i.set("apiUrl",y);var l=()=>({...i.store,apiUrl:process.env.XTRA_API_URL||i.get("apiUrl")||y});var v=()=>i.get("token");var f=()=>{let e={};try{let s=$.default.join(process.cwd(),".xtrarc");m.default.existsSync(s)&&(e=JSON.parse(m.default.readFileSync(s,"utf-8")))}catch{}return{project:e.project||i.get("project")||"",env:e.env||i.get("env")||"development",branch:e.branch||i.get("branch")||"main"}};var b=a(require("fs")),S=a(require("path")),O=JSON.parse(b.readFileSync(S.join(__dirname,"../../package.json"),"utf-8")),r=()=>{let{apiUrl:e}=l(),s=v();return j.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":O.version,...s?{Authorization:`Bearer ${s}`}:{}}})},x={login:async(e,s,t)=>(await r().post("/auth/cli-login",{email:e,password:s,apiKey:t})).data,getSecrets:async(e,s,t="main")=>(await r().get(`/projects/${e}/envs/${s}/secrets?branch=${t}`)).data,getSecretVersions:async(e,s,t="main")=>(await r().get(`/projects/${e}/envs/${s}/secrets?includeVersions=true&branch=${t}`)).data,setSecrets:async(e,s,t,n,o="main")=>(await r().post(`/projects/${e}/envs/${s}/secrets`,{secrets:t,expectedVersions:n,branch:o})).data,getSecretDetails:async(e,s,t,n="main")=>(await r().get(`/projects/${e}/envs/${s}/secrets/${t}?branch=${n}`)).data,syncLogs:async e=>(await r().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,s,t,n,o,p)=>(await r().post(`/projects/${e}/envs/${s}/secrets/link`,{key:t,sourceProjectId:n,sourceEnv:o,sourceKey:p})).data,rotateSecret:async(e,s,t,n,o)=>(await r().post(`/projects/${e}/envs/${s}/secrets/${t}/rotate`,{strategy:n,parsedNewValue:o})).data,promoteSecret:async(e,s,t)=>(await r().post(`/projects/${e}/envs/${s}/secrets/${t}/promote`,{})).data,verifyAuditLogs:async()=>(await r().get("/audit/verify")).data,exportAuditLogs:async(e,s,t,n)=>{let o={format:e};return s&&(o.startDate=s),t&&(o.endDate=t),n&&(o.projectId=n),(await r().post("/audit/export",o,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,s,t,n)=>(await r().post("/access/request",{projectId:e,secretId:n,reason:s,duration:t})).data,approveAccess:async(e,s)=>(await r().post("/access/approve",{requestId:e,decision:s})).data,listAccessRequests:async e=>{let{workspace:s}=l(),t=s?`&workspaceId=${s}`:"";return(await r().get(`/access/list?mode=${e}${t}`)).data},getAccessRequestStatus:async e=>(await r().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=l(),s=e?`/project?workspaceId=${e}`:"/project";return(await r().get(s)).data},getBranches:async e=>(await r().get(`/branch?projectId=${e}`)).data,createBranch:async(e,s,t)=>(await r().post("/branch",{projectId:e,name:s,description:t})).data,deleteBranch:async e=>(await r().delete(`/branch/${e}`)).data,updateBranch:async(e,s)=>(await r().put("/branch",{id:e,...s})).data,getRoles:async()=>(await r().get("/admin/roles")).data,getUsers:async e=>{let s=e?`?teamId=${e}`:"";return(await r().get(`/admin/users${s}`)).data},setUserRole:async(e,s,t)=>(await r().put("/admin/users/role",{email:e,role:s,teamId:t})).data,getIntegrationStatus:async e=>(await r().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await r().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await r().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,s)=>{let t=new URLSearchParams(s).toString();return(await r().get(`/projects/${e}/kubernetes?${t}`)).data},getSecretHistory:async(e,s,t)=>(await r().get(`/projects/${e}/envs/${s}/secrets/${t}/history`)).data,rollbackSecret:async(e,s,t,n)=>(await r().post(`/projects/${e}/envs/${s}/secrets/${t}/history`,{version:n})).data,cloneEnvironment:async(e,s,t,n,o)=>(await r().post(`/projects/${e}/envs/clone`,{fromEnv:s,toEnv:t,overwrite:n,branch:o})).data,generateJitLink:async e=>(await r().post("/jit/generate",e)).data,claimJitLink:async e=>(await r().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await r().get(`/jit/${e}`)).data,post:async(e,s)=>(await r().post(e,s)).data};var C=a(require("inquirer")),I=new k.Command("env");I.command("clone").description("Clone secrets from one environment to another").requiredOption("--from <env>","Source environment (e.g. production)").requiredOption("--to <env>","Destination environment (e.g. staging)").option("-p, --project <id>","Project ID").option("-b, --branch <name>","Branch Name").option("--overwrite","Overwrite existing secrets",!1).action(async e=>{try{let s=e.project,t=e.branch||f().branch||"main";s||(s=f().project,s||(console.error(c.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1)));let n=e.from.toLowerCase(),o=e.to.toLowerCase();if(n===o&&(console.error(c.default.red("Source and destination must be different.")),process.exit(1)),console.log(c.default.blue(`Cloning secrets from ${n} to ${o} (branch: ${t})...`)),e.overwrite&&console.log(c.default.yellow("Warning: Existing secrets in destination will be overwritten!")),!(await C.default.prompt([{type:"confirm",name:"proceed",message:"Are you sure you want to proceed?",default:!1}])).proceed){console.log("Operation cancelled.");return}let g=await x.cloneEnvironment(s,n,o,e.overwrite,t);g.success&&(console.log(c.default.green(`
|
|
2
|
+
Clone successful!`)),console.log(`Copied: ${g.summary.copied}`),console.log(`Updated: ${g.summary.updated} (overwrite: ${e.overwrite})`),console.log(`Skipped: ${g.summary.skipped}`))}catch(s){s.response&&s.response.data&&s.response.data.error?console.error(c.default.red("Error cloning environment:"),s.response.data.error):console.error(c.default.red("Error cloning environment:"),s.message||s)}});0&&(module.exports={envCommand});
|
package/dist/commands/export.js
CHANGED
|
@@ -1,83 +1,2 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.exportCommand = void 0;
|
|
7
|
-
const commander_1 = require("commander");
|
|
8
|
-
const api_1 = require("../lib/api");
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const fs_1 = __importDefault(require("fs"));
|
|
11
|
-
const path_1 = __importDefault(require("path"));
|
|
12
|
-
const sync_1 = require("csv-stringify/sync");
|
|
13
|
-
const config_1 = require("../lib/config");
|
|
14
|
-
exports.exportCommand = new commander_1.Command("export")
|
|
15
|
-
.description("Export secrets to a file (JSON, Dotenv, CSV)")
|
|
16
|
-
.option("-p, --project <projectId>", "Project ID")
|
|
17
|
-
.option("-e, --env <environment>", "Environment (development, staging, production)", "development")
|
|
18
|
-
.option("-b, --branch <branchName>", "Branch Name")
|
|
19
|
-
.option("-f, --format <format>", "Output format (json, dotenv, csv)", "json")
|
|
20
|
-
.option("-o, --output <file>", "Output file path (default: stdout)")
|
|
21
|
-
.action(async (options) => {
|
|
22
|
-
let { project, env, branch, format, output } = options;
|
|
23
|
-
// Use config fallback
|
|
24
|
-
if (!project)
|
|
25
|
-
project = (0, config_1.getRcConfig)().project;
|
|
26
|
-
if (!branch) {
|
|
27
|
-
branch = (0, config_1.getRcConfig)().branch || "main";
|
|
28
|
-
}
|
|
29
|
-
// Normalize Env
|
|
30
|
-
const envMap = { dev: "development", stg: "staging", prod: "production" };
|
|
31
|
-
env = envMap[env] || env;
|
|
32
|
-
if (!project) {
|
|
33
|
-
console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
const spinner = require("ora")(`Fetching secrets for export (${env} @ ${branch})...`).start();
|
|
37
|
-
try {
|
|
38
|
-
const secrets = await api_1.api.getSecrets(project, env, branch);
|
|
39
|
-
spinner.stop();
|
|
40
|
-
if (!secrets || Object.keys(secrets).length === 0) {
|
|
41
|
-
console.warn(chalk_1.default.yellow("No secrets found to export."));
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
let content = "";
|
|
45
|
-
switch (format.toLowerCase()) {
|
|
46
|
-
case "json":
|
|
47
|
-
content = JSON.stringify(secrets, null, 2);
|
|
48
|
-
break;
|
|
49
|
-
case "dotenv":
|
|
50
|
-
content = Object.entries(secrets)
|
|
51
|
-
.map(([key, value]) => `${key}="${String(value).replace(/"/g, '\\"')}"`)
|
|
52
|
-
.join("\n");
|
|
53
|
-
break;
|
|
54
|
-
case "csv":
|
|
55
|
-
const records = Object.entries(secrets).map(([key, value]) => ({ key, value }));
|
|
56
|
-
content = (0, sync_1.stringify)(records, { header: true });
|
|
57
|
-
break;
|
|
58
|
-
default:
|
|
59
|
-
console.error(chalk_1.default.red(`Error: Unsupported format '${format}'. Use json, dotenv, or csv.`));
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
if (output) {
|
|
63
|
-
const outputPath = path_1.default.resolve(process.cwd(), output);
|
|
64
|
-
fs_1.default.writeFileSync(outputPath, content, "utf-8");
|
|
65
|
-
console.log(chalk_1.default.green(`✔ Secrets exported to ${outputPath}`));
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
console.log(content);
|
|
69
|
-
}
|
|
70
|
-
// Log Audit
|
|
71
|
-
try {
|
|
72
|
-
const { logAudit } = require("../lib/audit");
|
|
73
|
-
logAudit("SECRET_EXPORT", project, env, { format, destination: output || "stdout", branch });
|
|
74
|
-
}
|
|
75
|
-
catch (e) { }
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
spinner.fail("Export failed.");
|
|
79
|
-
const safeErr = error?.response?.data?.error || error.message || "Unknown error";
|
|
80
|
-
console.error(chalk_1.default.red(safeErr));
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
1
|
+
var ot=Object.create;var h=Object.defineProperty;var it=Object.getOwnPropertyDescriptor;var at=Object.getOwnPropertyNames;var ct=Object.getPrototypeOf,pt=Object.prototype.hasOwnProperty;var v=(t,e)=>()=>(t&&(e=t(t=0)),e);var N=(t,e)=>{for(var s in e)h(t,s,{get:e[s],enumerable:!0})},P=(t,e,s,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of at(e))!pt.call(t,n)&&n!==s&&h(t,n,{get:()=>e[n],enumerable:!(r=it(e,n))||r.enumerable});return t};var a=(t,e,s)=>(s=t!=null?ot(ct(t)):{},P(e||!t||!t.__esModule?h(s,"default",{value:t,enumerable:!0}):s,t)),B=t=>P(h({},"__esModule",{value:!0}),t);var T,A,_,C,g,I,f,D,U,w=v(()=>{T=a(require("conf")),A=a(require("fs")),_=a(require("path")),C="https://www.xtrasecurity.in/api",g=new T.default({projectName:"xtra-cli",defaults:{apiUrl:C}}),I=g.get("apiUrl");I&&!process.env.XTRA_API_URL&&(I.includes("localhost")||I.includes("xtra-security.vercel.app"))&&g.set("apiUrl",C);f=()=>({...g.store,apiUrl:process.env.XTRA_API_URL||g.get("apiUrl")||C}),D=()=>g.get("token"),U=()=>{let t={};try{let e=_.default.join(process.cwd(),".xtrarc");A.default.existsSync(e)&&(t=JSON.parse(A.default.readFileSync(e,"utf-8")))}catch{}return{project:t.project||g.get("project")||"",env:t.env||g.get("env")||"development",branch:t.branch||g.get("branch")||"main"}}});var J,F,q,gt,o,j,O=v(()=>{J=a(require("axios"));w();F=a(require("fs")),q=a(require("path")),gt=JSON.parse(F.readFileSync(q.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:t}=f(),e=D();return J.default.create({baseURL:t,headers:{"Content-Type":"application/json","X-CLI-Version":gt.version,...e?{Authorization:`Bearer ${e}`}:{}}})},j={login:async(t,e,s)=>(await o().post("/auth/cli-login",{email:t,password:e,apiKey:s})).data,getSecrets:async(t,e,s="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets?branch=${s}`)).data,getSecretVersions:async(t,e,s="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(t,e,s,r,n="main")=>(await o().post(`/projects/${t}/envs/${e}/secrets`,{secrets:s,expectedVersions:r,branch:n})).data,getSecretDetails:async(t,e,s,r="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets/${s}?branch=${r}`)).data,syncLogs:async t=>(await o().post("/audit/cli-logs",{logs:t})).data,linkSecret:async(t,e,s,r,n,i)=>(await o().post(`/projects/${t}/envs/${e}/secrets/link`,{key:s,sourceProjectId:r,sourceEnv:n,sourceKey:i})).data,rotateSecret:async(t,e,s,r,n)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${s}/rotate`,{strategy:r,parsedNewValue:n})).data,promoteSecret:async(t,e,s)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await o().get("/audit/verify")).data,exportAuditLogs:async(t,e,s,r)=>{let n={format:t};return e&&(n.startDate=e),s&&(n.endDate=s),r&&(n.projectId=r),(await o().post("/audit/export",n,{responseType:t==="csv"?"text":"json"})).data},requestAccess:async(t,e,s,r)=>(await o().post("/access/request",{projectId:t,secretId:r,reason:e,duration:s})).data,approveAccess:async(t,e)=>(await o().post("/access/approve",{requestId:t,decision:e})).data,listAccessRequests:async t=>{let{workspace:e}=f(),s=e?`&workspaceId=${e}`:"";return(await o().get(`/access/list?mode=${t}${s}`)).data},getAccessRequestStatus:async t=>(await o().get(`/access/request?id=${t}`)).data,getProjects:async()=>{let{workspace:t}=f(),e=t?`/project?workspaceId=${t}`:"/project";return(await o().get(e)).data},getBranches:async t=>(await o().get(`/branch?projectId=${t}`)).data,createBranch:async(t,e,s)=>(await o().post("/branch",{projectId:t,name:e,description:s})).data,deleteBranch:async t=>(await o().delete(`/branch/${t}`)).data,updateBranch:async(t,e)=>(await o().put("/branch",{id:t,...e})).data,getRoles:async()=>(await o().get("/admin/roles")).data,getUsers:async t=>{let e=t?`?teamId=${t}`:"";return(await o().get(`/admin/users${e}`)).data},setUserRole:async(t,e,s)=>(await o().put("/admin/users/role",{email:t,role:e,teamId:s})).data,getIntegrationStatus:async t=>(await o().get(`/integrations/${t}`)).data,getIntegrationRepos:async t=>(await o().get(`/integrations/${t}/sync`)).data.repos,syncSecretsToGithub:async t=>(await o().post("/integrations/github/sync",t)).data,exportKubernetesSecret:async(t,e)=>{let s=new URLSearchParams(e).toString();return(await o().get(`/projects/${t}/kubernetes?${s}`)).data},getSecretHistory:async(t,e,s)=>(await o().get(`/projects/${t}/envs/${e}/secrets/${s}/history`)).data,rollbackSecret:async(t,e,s,r)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${s}/history`,{version:r})).data,cloneEnvironment:async(t,e,s,r,n)=>(await o().post(`/projects/${t}/envs/clone`,{fromEnv:e,toEnv:s,overwrite:r,branch:n})).data,generateJitLink:async t=>(await o().post("/jit/generate",t)).data,claimJitLink:async t=>(await o().post("/jit/claim",{token:t})).data,getJitInfo:async t=>(await o().get(`/jit/${t}`)).data,post:async(t,e)=>(await o().post(t,e)).data}});function H(){let t=G.get("masterKey");t||(t=l.default.randomBytes(32).toString("hex"),G.set("masterKey",t));let e=(0,K.machineIdSync)();return l.default.createHash("sha256").update(t+e).digest()}var l,V,K,z,G,M,X,Q=v(()=>{l=a(require("crypto")),V=a(require("conf")),K=require("node-machine-id"),z="aes-256-gcm",G=new V.default({projectName:"xtra-cli-crypto"});M=t=>{let e=l.default.randomBytes(16),s=H(),r=l.default.createCipheriv(z,s,e),n=r.update(t,"utf8","hex");n+=r.final("hex");let i=r.getAuthTag();return{iv:e.toString("hex"),content:n,tag:i.toString("hex")}},X=t=>{let e=H(),s=Buffer.from(t.iv,"hex"),r=Buffer.from(t.tag,"hex"),n=l.default.createDecipheriv(z,e,s);n.setAuthTag(r);let i=n.update(t.content,"hex","utf8");return i+=n.final("utf8"),i}});var Z={};N(Z,{loadAuditLogs:()=>x,logAudit:()=>ut,saveAuditLogs:()=>S,syncAuditLogs:()=>ft});function dt(t){if(!t||typeof t!="object")return t;let e={...t},s=["password","token","secret","value","apiKey","secretValue"];for(let r of Object.keys(e))s.some(n=>r.toLowerCase().includes(n))&&(e[r]="[REDACTED]");return e}async function Y(t){try{if(!f().token)return!1;let s={action:t.action,entity:"cli-project",entityId:t.projectId||"global",details:dt(t.details),timestamp:t.timestamp,projectId:t.projectId};return await j.post("/audit/cli-logs",{logs:[s]}),!0}catch(e){return process.env.DEBUG&&console.error("Failed to send audit log to backend:",e),!1}}function ut(t,e,s,r){let n={id:(0,W.v4)(),timestamp:new Date().toISOString(),action:t,projectId:e,environment:s,details:r,synced:!1},i=x();i.push(n),S(i),Y(n).then(b=>{if(b){n.synced=!0;let u=x(),c=u.findIndex(p=>p.id===n.id);c!==-1&&(u[c].synced=!0,S(u))}}).catch(()=>{})}function x(){if(!m.default.existsSync(E))return[];try{let t=m.default.readFileSync(E,"utf-8"),e=JSON.parse(t),s=X(e);return JSON.parse(s)}catch{return[]}}function S(t){m.default.existsSync(R)||m.default.mkdirSync(R,{recursive:!0});let e=JSON.stringify(t),s=M(e);m.default.writeFileSync(E,JSON.stringify(s),"utf-8")}async function ft(){let t=x(),e=t.filter(r=>!r.synced);if(e.length===0)return 0;let s=0;for(let r of e)await Y(r)&&(r.synced=!0,s++);return s>0&&S(t),s}var m,L,W,R,E,tt=v(()=>{m=a(require("fs")),L=a(require("path")),W=require("uuid");Q();w();O();R=L.default.join(process.cwd(),".xtra"),E=L.default.join(R,"audit.enc")});var mt={};N(mt,{exportCommand:()=>lt});module.exports=B(mt);var et=require("commander");O();var y=a(require("chalk")),st=a(require("fs")),rt=a(require("path")),nt=require("csv-stringify/sync");w();var lt=new et.Command("export").description("Export secrets to a file (JSON, Dotenv, CSV)").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (development, staging, production)","development").option("-b, --branch <branchName>","Branch Name").option("-f, --format <format>","Output format (json, dotenv, csv)","json").option("-o, --output <file>","Output file path (default: stdout)").action(async t=>{let{project:e,env:s,branch:r,format:n,output:i}=t;e||(e=U().project),r||(r=U().branch||"main"),s={dev:"development",stg:"staging",prod:"production"}[s]||s,e||(console.error(y.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let u=require("ora")(`Fetching secrets for export (${s} @ ${r})...`).start();try{let c=await j.getSecrets(e,s,r);if(u.stop(),!c||Object.keys(c).length===0){console.warn(y.default.yellow("No secrets found to export."));return}let p="";switch(n.toLowerCase()){case"json":p=JSON.stringify(c,null,2);break;case"dotenv":p=Object.entries(c).map(([$,k])=>`${$}="${String(k).replace(/"/g,'\\"')}"`).join(`
|
|
2
|
+
`);break;case"csv":let d=Object.entries(c).map(([$,k])=>({key:$,value:k}));p=(0,nt.stringify)(d,{header:!0});break;default:console.error(y.default.red(`Error: Unsupported format '${n}'. Use json, dotenv, or csv.`)),process.exit(1)}if(i){let d=rt.default.resolve(process.cwd(),i);st.default.writeFileSync(d,p,"utf-8"),console.log(y.default.green(`\u2714 Secrets exported to ${d}`))}else console.log(p);try{let{logAudit:d}=(tt(),B(Z));d("SECRET_EXPORT",e,s,{format:n,destination:i||"stdout",branch:r})}catch{}}catch(c){u.fail("Export failed.");let p=c?.response?.data?.error||c.message||"Unknown error";console.error(y.default.red(p)),process.exit(1)}});0&&(module.exports={exportCommand});
|
|
@@ -1,179 +1,3 @@
|
|
|
1
|
-
"
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.generateCommand = void 0;
|
|
7
|
-
const commander_1 = require("commander");
|
|
8
|
-
const api_1 = require("../lib/api");
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const ora_1 = __importDefault(require("ora"));
|
|
11
|
-
const fs_1 = __importDefault(require("fs"));
|
|
12
|
-
const path_1 = __importDefault(require("path"));
|
|
13
|
-
const yaml_1 = __importDefault(require("yaml"));
|
|
14
|
-
const config_1 = require("../lib/config");
|
|
15
|
-
// Helper: Parse .env file into key-value pairs
|
|
16
|
-
function parseEnvFile(content) {
|
|
17
|
-
const result = {};
|
|
18
|
-
const lines = content.split(/\r?\n/);
|
|
19
|
-
for (const line of lines) {
|
|
20
|
-
const trimmed = line.trim();
|
|
21
|
-
// Skip empty lines and comments
|
|
22
|
-
if (!trimmed || trimmed.startsWith('#'))
|
|
23
|
-
continue;
|
|
24
|
-
const idx = trimmed.indexOf('=');
|
|
25
|
-
if (idx === -1)
|
|
26
|
-
continue;
|
|
27
|
-
const key = trimmed.substring(0, idx).trim();
|
|
28
|
-
let value = trimmed.substring(idx + 1).trim();
|
|
29
|
-
// Remove surrounding quotes if present
|
|
30
|
-
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
31
|
-
(value.startsWith("'") && value.endsWith("'"))) {
|
|
32
|
-
value = value.slice(1, -1);
|
|
33
|
-
}
|
|
34
|
-
result[key] = value;
|
|
35
|
-
}
|
|
36
|
-
return result;
|
|
37
|
-
}
|
|
38
|
-
// Helper: Merge secrets into existing file content, preserving comments and order
|
|
39
|
-
function mergeEnvContent(existingContent, newSecrets) {
|
|
40
|
-
const lines = existingContent.split(/\r?\n/);
|
|
41
|
-
const existingKeys = new Set();
|
|
42
|
-
const added = [];
|
|
43
|
-
const updated = [];
|
|
44
|
-
// First pass: update existing keys
|
|
45
|
-
const updatedLines = lines.map(line => {
|
|
46
|
-
const trimmed = line.trim();
|
|
47
|
-
if (!trimmed || trimmed.startsWith('#'))
|
|
48
|
-
return line;
|
|
49
|
-
const idx = trimmed.indexOf('=');
|
|
50
|
-
if (idx === -1)
|
|
51
|
-
return line;
|
|
52
|
-
const key = trimmed.substring(0, idx).trim();
|
|
53
|
-
existingKeys.add(key);
|
|
54
|
-
if (key in newSecrets) {
|
|
55
|
-
const oldValue = parseEnvFile(line)[key];
|
|
56
|
-
if (oldValue !== newSecrets[key]) {
|
|
57
|
-
updated.push(key);
|
|
58
|
-
}
|
|
59
|
-
return `${key}="${newSecrets[key]}"`;
|
|
60
|
-
}
|
|
61
|
-
return line;
|
|
62
|
-
});
|
|
63
|
-
// Second pass: add new keys
|
|
64
|
-
for (const [key, value] of Object.entries(newSecrets)) {
|
|
65
|
-
if (!existingKeys.has(key)) {
|
|
66
|
-
updatedLines.push(`${key}="${value}"`);
|
|
67
|
-
added.push(key);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return { content: updatedLines.join('\n'), added, updated };
|
|
71
|
-
}
|
|
72
|
-
exports.generateCommand = new commander_1.Command("generate")
|
|
73
|
-
.description("Generate local configuration files from secrets")
|
|
74
|
-
.option("-p, --project <projectId>", "Project ID")
|
|
75
|
-
.option("-e, --env <environment>", "Environment (dev, stg, prod)", "dev")
|
|
76
|
-
.option("-b, --branch <branchName>", "Branch Name")
|
|
77
|
-
.option("-o, --output <path>", "Output file path (forces complete overwrite)")
|
|
78
|
-
.option("-f, --format <format>", "Output format (env, json, yaml)", "env")
|
|
79
|
-
.option("--force", "Skip confirmation prompts", false)
|
|
80
|
-
.action(async (options) => {
|
|
81
|
-
let { project, env, branch, output, format, force } = options;
|
|
82
|
-
// Use config fallback
|
|
83
|
-
if (!project)
|
|
84
|
-
project = (0, config_1.getRcConfig)().project;
|
|
85
|
-
if (!branch) {
|
|
86
|
-
branch = (0, config_1.getRcConfig)().branch || "main";
|
|
87
|
-
}
|
|
88
|
-
// Normalize Env
|
|
89
|
-
const envMap = { dev: "development", stg: "staging", prod: "production" };
|
|
90
|
-
env = envMap[env] || env;
|
|
91
|
-
if (!project) {
|
|
92
|
-
console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
|
|
93
|
-
process.exit(1);
|
|
94
|
-
}
|
|
95
|
-
// Determine if we should merge or overwrite
|
|
96
|
-
const shouldMerge = !output && format === "env";
|
|
97
|
-
// Determine output filename
|
|
98
|
-
let outputPath = output;
|
|
99
|
-
if (!outputPath) {
|
|
100
|
-
switch (format) {
|
|
101
|
-
case "json":
|
|
102
|
-
outputPath = "secrets.json";
|
|
103
|
-
break;
|
|
104
|
-
case "yaml":
|
|
105
|
-
outputPath = "secrets.yaml";
|
|
106
|
-
break;
|
|
107
|
-
default:
|
|
108
|
-
outputPath = ".env";
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
const fullPath = path_1.default.resolve(process.cwd(), outputPath);
|
|
113
|
-
const fileExists = fs_1.default.existsSync(fullPath);
|
|
114
|
-
const spinner = (0, ora_1.default)(`Fetching secrets for ${env} (branch: ${branch})...`).start();
|
|
115
|
-
try {
|
|
116
|
-
const secrets = await api_1.api.getSecrets(project, env, branch);
|
|
117
|
-
if (!secrets || Object.keys(secrets).length === 0) {
|
|
118
|
-
spinner.warn(chalk_1.default.yellow("No secrets found. File not modified."));
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
let content = "";
|
|
122
|
-
let added = [];
|
|
123
|
-
let updated = [];
|
|
124
|
-
// Handle merge for .env format when no -o flag
|
|
125
|
-
if (shouldMerge && fileExists) {
|
|
126
|
-
spinner.text = "Merging with existing .env file...";
|
|
127
|
-
const existingContent = fs_1.default.readFileSync(fullPath, 'utf-8');
|
|
128
|
-
const mergeResult = mergeEnvContent(existingContent, secrets);
|
|
129
|
-
content = mergeResult.content;
|
|
130
|
-
added = mergeResult.added;
|
|
131
|
-
updated = mergeResult.updated;
|
|
132
|
-
if (added.length === 0 && updated.length === 0) {
|
|
133
|
-
spinner.succeed(chalk_1.default.green("No changes needed - .env is already up to date."));
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
// Full overwrite mode
|
|
139
|
-
switch (format.toLowerCase()) {
|
|
140
|
-
case "json":
|
|
141
|
-
content = JSON.stringify(secrets, null, 2);
|
|
142
|
-
break;
|
|
143
|
-
case "yaml":
|
|
144
|
-
content = yaml_1.default.stringify(secrets);
|
|
145
|
-
break;
|
|
146
|
-
case "env":
|
|
147
|
-
default:
|
|
148
|
-
content = Object.entries(secrets)
|
|
149
|
-
.map(([key, value]) => `${key}="${value}"`)
|
|
150
|
-
.join("\n");
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
// Write file
|
|
155
|
-
fs_1.default.writeFileSync(fullPath, content, "utf-8");
|
|
156
|
-
// Update manifest
|
|
157
|
-
const { hash } = require("../lib/crypto");
|
|
158
|
-
const { updateManifest } = require("../lib/manifest");
|
|
159
|
-
updateManifest(project, env, secrets, hash);
|
|
160
|
-
// Show result
|
|
161
|
-
if (shouldMerge && fileExists) {
|
|
162
|
-
spinner.succeed(chalk_1.default.green(`Updated ${outputPath}`));
|
|
163
|
-
if (added.length > 0) {
|
|
164
|
-
console.log(chalk_1.default.cyan(` + Added: ${added.join(', ')}`));
|
|
165
|
-
}
|
|
166
|
-
if (updated.length > 0) {
|
|
167
|
-
console.log(chalk_1.default.yellow(` ~ Updated: ${updated.join(', ')}`));
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
spinner.succeed(`Generated ${outputPath} (${Object.keys(secrets).length} secrets)`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
catch (error) {
|
|
175
|
-
spinner.fail("Failed to generate file.");
|
|
176
|
-
console.error(chalk_1.default.red(error.message));
|
|
177
|
-
process.exit(1);
|
|
178
|
-
}
|
|
179
|
-
});
|
|
1
|
+
var pt=Object.create;var $=Object.defineProperty;var gt=Object.getOwnPropertyDescriptor;var dt=Object.getOwnPropertyNames;var ut=Object.getPrototypeOf,ft=Object.prototype.hasOwnProperty;var F=(t,e)=>()=>(t&&(e=t(t=0)),e);var O=(t,e)=>{for(var s in e)$(t,s,{get:e[s],enumerable:!0})},_=(t,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of dt(e))!ft.call(t,r)&&r!==s&&$(t,r,{get:()=>e[r],enumerable:!(n=gt(e,r))||n.enumerable});return t};var c=(t,e,s)=>(s=t!=null?pt(ut(t)):{},_(e||!t||!t.__esModule?$(s,"default",{value:t,enumerable:!0}):s,t)),U=t=>_($({},"__esModule",{value:!0}),t);var Y={};O(Y,{decrypt:()=>yt,encrypt:()=>mt,hash:()=>ht});function z(){let t=W.get("masterKey");t||(t=y.default.randomBytes(32).toString("hex"),W.set("masterKey",t));let e=(0,H.machineIdSync)();return y.default.createHash("sha256").update(t+e).digest()}var y,K,H,X,W,mt,yt,ht,Q=F(()=>{y=c(require("crypto")),K=c(require("conf")),H=require("node-machine-id"),X="aes-256-gcm",W=new K.default({projectName:"xtra-cli-crypto"});mt=t=>{let e=y.default.randomBytes(16),s=z(),n=y.default.createCipheriv(X,s,e),r=n.update(t,"utf8","hex");r+=n.final("hex");let i=n.getAuthTag();return{iv:e.toString("hex"),content:r,tag:i.toString("hex")}},yt=t=>{let e=z(),s=Buffer.from(t.iv,"hex"),n=Buffer.from(t.tag,"hex"),r=y.default.createDecipheriv(X,e,s);r.setAuthTag(n);let i=r.update(t.content,"hex","utf8");return i+=r.final("utf8"),i},ht=t=>y.default.createHash("sha256").update(t).digest("hex")});var et={};O(et,{MANIFEST_DIR:()=>k,MANIFEST_FILE:()=>I,loadManifest:()=>Z,saveManifest:()=>tt,updateManifest:()=>vt});function Z(){if(!v.default.existsSync(I))return null;try{let t=v.default.readFileSync(I,"utf-8");return JSON.parse(t)}catch{return null}}function tt(t){v.default.existsSync(k)||v.default.mkdirSync(k,{recursive:!0}),v.default.writeFileSync(I,JSON.stringify(t,null,2),"utf-8")}function vt(t,e,s,n){let r=Z()||{projectId:t,environment:e,lastSyncedAt:new Date().toISOString(),secrets:{}};r.projectId=t,r.environment=e,r.lastSyncedAt=new Date().toISOString(),Object.entries(s).forEach(([i,f])=>{let a=n(f);r.secrets[i]={hash:a,updatedAt:new Date().toISOString()}}),tt(r)}var v,B,k,I,st=F(()=>{v=c(require("fs")),B=c(require("path")),k=B.default.join(process.cwd(),".xtra"),I=B.default.join(k,"manifest.json")});var St={};O(St,{generateCommand:()=>xt});module.exports=U(St);var nt=require("commander");var q=c(require("axios"));var T=c(require("conf")),M=c(require("fs")),E=c(require("path")),N="https://www.xtrasecurity.in/api",l=new T.default({projectName:"xtra-cli",defaults:{apiUrl:N}}),P=l.get("apiUrl");P&&!process.env.XTRA_API_URL&&(P.includes("localhost")||P.includes("xtra-security.vercel.app"))&&l.set("apiUrl",N);var b=()=>({...l.store,apiUrl:process.env.XTRA_API_URL||l.get("apiUrl")||N});var D=()=>l.get("token");var L=()=>{let t={};try{let e=E.default.join(process.cwd(),".xtrarc");M.default.existsSync(e)&&(t=JSON.parse(M.default.readFileSync(e,"utf-8")))}catch{}return{project:t.project||l.get("project")||"",env:t.env||l.get("env")||"development",branch:t.branch||l.get("branch")||"main"}};var G=c(require("fs")),J=c(require("path")),lt=JSON.parse(G.readFileSync(J.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:t}=b(),e=D();return q.default.create({baseURL:t,headers:{"Content-Type":"application/json","X-CLI-Version":lt.version,...e?{Authorization:`Bearer ${e}`}:{}}})},V={login:async(t,e,s)=>(await o().post("/auth/cli-login",{email:t,password:e,apiKey:s})).data,getSecrets:async(t,e,s="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets?branch=${s}`)).data,getSecretVersions:async(t,e,s="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(t,e,s,n,r="main")=>(await o().post(`/projects/${t}/envs/${e}/secrets`,{secrets:s,expectedVersions:n,branch:r})).data,getSecretDetails:async(t,e,s,n="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets/${s}?branch=${n}`)).data,syncLogs:async t=>(await o().post("/audit/cli-logs",{logs:t})).data,linkSecret:async(t,e,s,n,r,i)=>(await o().post(`/projects/${t}/envs/${e}/secrets/link`,{key:s,sourceProjectId:n,sourceEnv:r,sourceKey:i})).data,rotateSecret:async(t,e,s,n,r)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${s}/rotate`,{strategy:n,parsedNewValue:r})).data,promoteSecret:async(t,e,s)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await o().get("/audit/verify")).data,exportAuditLogs:async(t,e,s,n)=>{let r={format:t};return e&&(r.startDate=e),s&&(r.endDate=s),n&&(r.projectId=n),(await o().post("/audit/export",r,{responseType:t==="csv"?"text":"json"})).data},requestAccess:async(t,e,s,n)=>(await o().post("/access/request",{projectId:t,secretId:n,reason:e,duration:s})).data,approveAccess:async(t,e)=>(await o().post("/access/approve",{requestId:t,decision:e})).data,listAccessRequests:async t=>{let{workspace:e}=b(),s=e?`&workspaceId=${e}`:"";return(await o().get(`/access/list?mode=${t}${s}`)).data},getAccessRequestStatus:async t=>(await o().get(`/access/request?id=${t}`)).data,getProjects:async()=>{let{workspace:t}=b(),e=t?`/project?workspaceId=${t}`:"/project";return(await o().get(e)).data},getBranches:async t=>(await o().get(`/branch?projectId=${t}`)).data,createBranch:async(t,e,s)=>(await o().post("/branch",{projectId:t,name:e,description:s})).data,deleteBranch:async t=>(await o().delete(`/branch/${t}`)).data,updateBranch:async(t,e)=>(await o().put("/branch",{id:t,...e})).data,getRoles:async()=>(await o().get("/admin/roles")).data,getUsers:async t=>{let e=t?`?teamId=${t}`:"";return(await o().get(`/admin/users${e}`)).data},setUserRole:async(t,e,s)=>(await o().put("/admin/users/role",{email:t,role:e,teamId:s})).data,getIntegrationStatus:async t=>(await o().get(`/integrations/${t}`)).data,getIntegrationRepos:async t=>(await o().get(`/integrations/${t}/sync`)).data.repos,syncSecretsToGithub:async t=>(await o().post("/integrations/github/sync",t)).data,exportKubernetesSecret:async(t,e)=>{let s=new URLSearchParams(e).toString();return(await o().get(`/projects/${t}/kubernetes?${s}`)).data},getSecretHistory:async(t,e,s)=>(await o().get(`/projects/${t}/envs/${e}/secrets/${s}/history`)).data,rollbackSecret:async(t,e,s,n)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${s}/history`,{version:n})).data,cloneEnvironment:async(t,e,s,n,r)=>(await o().post(`/projects/${t}/envs/clone`,{fromEnv:e,toEnv:s,overwrite:n,branch:r})).data,generateJitLink:async t=>(await o().post("/jit/generate",t)).data,claimJitLink:async t=>(await o().post("/jit/claim",{token:t})).data,getJitInfo:async t=>(await o().get(`/jit/${t}`)).data,post:async(t,e)=>(await o().post(t,e)).data};var m=c(require("chalk")),rt=c(require("ora")),A=c(require("fs")),ot=c(require("path")),it=c(require("yaml"));function wt(t){let e={},s=t.split(/\r?\n/);for(let n of s){let r=n.trim();if(!r||r.startsWith("#"))continue;let i=r.indexOf("=");if(i===-1)continue;let f=r.substring(0,i).trim(),a=r.substring(i+1).trim();(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))&&(a=a.slice(1,-1)),e[f]=a}return e}function jt(t,e){let s=t.split(/\r?\n/),n=new Set,r=[],i=[],f=s.map(a=>{let u=a.trim();if(!u||u.startsWith("#"))return a;let g=u.indexOf("=");if(g===-1)return a;let p=u.substring(0,g).trim();return n.add(p),p in e?(wt(a)[p]!==e[p]&&i.push(p),`${p}="${e[p]}"`):a});for(let[a,u]of Object.entries(e))n.has(a)||(f.push(`${a}="${u}"`),r.push(a));return{content:f.join(`
|
|
2
|
+
`),added:r,updated:i}}var xt=new nt.Command("generate").description("Generate local configuration files from secrets").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (dev, stg, prod)","dev").option("-b, --branch <branchName>","Branch Name").option("-o, --output <path>","Output file path (forces complete overwrite)").option("-f, --format <format>","Output format (env, json, yaml)","env").option("--force","Skip confirmation prompts",!1).action(async t=>{let{project:e,env:s,branch:n,output:r,format:i,force:f}=t;e||(e=L().project),n||(n=L().branch||"main"),s={dev:"development",stg:"staging",prod:"production"}[s]||s,e||(console.error(m.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let u=!r&&i==="env",g=r;if(!g)switch(i){case"json":g="secrets.json";break;case"yaml":g="secrets.yaml";break;default:g=".env";break}let p=ot.default.resolve(process.cwd(),g),C=A.default.existsSync(p),h=(0,rt.default)(`Fetching secrets for ${s} (branch: ${n})...`).start();try{let d=await V.getSecrets(e,s,n);if(!d||Object.keys(d).length===0){h.warn(m.default.yellow("No secrets found. File not modified."));return}let w="",x=[],S=[];if(u&&C){h.text="Merging with existing .env file...";let R=A.default.readFileSync(p,"utf-8"),j=jt(R,d);if(w=j.content,x=j.added,S=j.updated,x.length===0&&S.length===0){h.succeed(m.default.green("No changes needed - .env is already up to date."));return}}else switch(i.toLowerCase()){case"json":w=JSON.stringify(d,null,2);break;case"yaml":w=it.default.stringify(d);break;default:w=Object.entries(d).map(([R,j])=>`${R}="${j}"`).join(`
|
|
3
|
+
`);break}A.default.writeFileSync(p,w,"utf-8");let{hash:at}=(Q(),U(Y)),{updateManifest:ct}=(st(),U(et));ct(e,s,d,at),u&&C?(h.succeed(m.default.green(`Updated ${g}`)),x.length>0&&console.log(m.default.cyan(` + Added: ${x.join(", ")}`)),S.length>0&&console.log(m.default.yellow(` ~ Updated: ${S.join(", ")}`))):h.succeed(`Generated ${g} (${Object.keys(d).length} secrets)`)}catch(d){h.fail("Failed to generate file."),console.error(m.default.red(d.message)),process.exit(1)}});0&&(module.exports={generateCommand});
|