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/secrets.js
CHANGED
|
@@ -1,305 +1,14 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}) : (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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.secretsCommand = void 0;
|
|
40
|
-
const commander_1 = require("commander");
|
|
41
|
-
const api_1 = require("../lib/api");
|
|
42
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
-
const ora_1 = __importDefault(require("ora"));
|
|
44
|
-
const table_1 = require("table");
|
|
45
|
-
const config_1 = require("../lib/config");
|
|
46
|
-
exports.secretsCommand = new commander_1.Command("secrets")
|
|
47
|
-
.description("Manage secrets (List, Set, Delete)")
|
|
48
|
-
.option("-p, --project <projectId>", "Project ID")
|
|
49
|
-
.option("-e, --env <environment>", "Environment (development, staging, production)", "development")
|
|
50
|
-
.option("-b, --branch <branchName>", "Branch Name");
|
|
51
|
-
// LIST
|
|
52
|
-
exports.secretsCommand
|
|
53
|
-
.command("list")
|
|
54
|
-
.description("List all secrets for a project/environment")
|
|
55
|
-
.option("--show", "Reveal secret values", false)
|
|
56
|
-
.addHelpText("after", `
|
|
57
|
-
Examples:
|
|
58
|
-
$ xtra secrets list
|
|
59
|
-
$ xtra secrets list -e production --show
|
|
60
|
-
`)
|
|
61
|
-
.action(async (options) => {
|
|
62
|
-
const parentOpts = exports.secretsCommand.opts();
|
|
63
|
-
let { project, env, branch } = parentOpts;
|
|
64
|
-
// Load .xtrarc from CWD (local project config has priority over global conf store)
|
|
65
|
-
const rc = (0, config_1.getRcConfig)();
|
|
66
|
-
if (!project)
|
|
67
|
-
project = rc.project;
|
|
68
|
-
if (!branch)
|
|
69
|
-
branch = rc.branch;
|
|
70
|
-
if (!env || env === "development")
|
|
71
|
-
env = rc.env; // only override if still at default
|
|
72
|
-
// Normalize Env
|
|
73
|
-
const envMap = { dev: "development", stg: "staging", prod: "production" };
|
|
74
|
-
env = envMap[env] || env;
|
|
75
|
-
const { show } = options;
|
|
76
|
-
if (!project) {
|
|
77
|
-
console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run `xtra init` first."));
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
80
|
-
const spinner = (0, ora_1.default)(`Fetching secrets for ${env} (branch: ${branch})...`).start();
|
|
81
|
-
try {
|
|
82
|
-
const secrets = await api_1.api.getSecrets(project, env, branch);
|
|
83
|
-
spinner.stop();
|
|
84
|
-
if (!secrets || Object.keys(secrets).length === 0) {
|
|
85
|
-
console.log(chalk_1.default.yellow("No secrets found."));
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (show) {
|
|
89
|
-
console.log(chalk_1.default.red("⚠ Warning: Secret values will be visible in your terminal and shell history!"));
|
|
90
|
-
}
|
|
91
|
-
const data = [
|
|
92
|
-
[chalk_1.default.bold("Key"), chalk_1.default.bold("Value"), chalk_1.default.bold("Env")]
|
|
93
|
-
];
|
|
94
|
-
Object.entries(secrets).forEach(([key, value]) => {
|
|
95
|
-
data.push([
|
|
96
|
-
key,
|
|
97
|
-
show ? value : "********",
|
|
98
|
-
env
|
|
99
|
-
]);
|
|
100
|
-
});
|
|
101
|
-
console.log((0, table_1.table)(data));
|
|
102
|
-
// Audit Log
|
|
103
|
-
try {
|
|
104
|
-
const { logAudit } = await Promise.resolve().then(() => __importStar(require("../lib/audit")));
|
|
105
|
-
logAudit("SECRET_LIST", project, env, { branch, count: Object.keys(secrets).length });
|
|
106
|
-
}
|
|
107
|
-
catch (e) { }
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
spinner.fail("Failed to fetch secrets");
|
|
111
|
-
console.error(chalk_1.default.red(error.message));
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
// SET
|
|
115
|
-
exports.secretsCommand
|
|
116
|
-
.command("set")
|
|
117
|
-
.description("Set one or more secrets (KEY=VALUE)")
|
|
118
|
-
.argument("<secrets...>", "Secrets to set (format: KEY=VALUE)")
|
|
119
|
-
.option("-f, --force", "Force update (overwrite remote changes without warning)", false)
|
|
120
|
-
.addHelpText("after", `
|
|
121
|
-
Examples:
|
|
122
|
-
$ xtra secrets set API_KEY=xyz
|
|
123
|
-
$ xtra secrets set DB_USER=admin DB_PASS=secret -e staging
|
|
124
|
-
`)
|
|
125
|
-
.action(async (args, options) => {
|
|
126
|
-
const parentOpts = exports.secretsCommand.opts();
|
|
127
|
-
let { project, env, branch } = parentOpts;
|
|
128
|
-
// Use active branch from config if not specified
|
|
129
|
-
if (!branch) {
|
|
130
|
-
branch = (0, config_1.getConfigValue)("branch") || "main";
|
|
131
|
-
}
|
|
132
|
-
// Normalize Env
|
|
133
|
-
const envMap = { dev: "development", stg: "staging", prod: "production" };
|
|
134
|
-
env = envMap[env] || env;
|
|
135
|
-
if (!project) {
|
|
136
|
-
project = (0, config_1.getConfigValue)("project");
|
|
137
|
-
}
|
|
138
|
-
if (!project) {
|
|
139
|
-
console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
|
|
140
|
-
process.exit(1);
|
|
141
|
-
}
|
|
142
|
-
// Parse key=value pairs
|
|
143
|
-
const payload = {};
|
|
144
|
-
args.forEach((arg) => {
|
|
145
|
-
const idx = arg.indexOf("=");
|
|
146
|
-
if (idx === -1) {
|
|
147
|
-
console.warn(chalk_1.default.yellow(`Skipping invalid format: ${arg} (expected KEY=VALUE)`));
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
const key = arg.substring(0, idx);
|
|
151
|
-
const value = arg.substring(idx + 1);
|
|
152
|
-
payload[key] = value;
|
|
153
|
-
});
|
|
154
|
-
if (Object.keys(payload).length === 0) {
|
|
155
|
-
console.error(chalk_1.default.red("No valid secrets provided."));
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
// 🔒 Production gate: require explicit confirmation before writing to production
|
|
159
|
-
if (env === "production" && !options.force) {
|
|
160
|
-
const inquirer = require("inquirer");
|
|
161
|
-
const { confirm } = await inquirer.prompt([{
|
|
162
|
-
type: "confirm",
|
|
163
|
-
name: "confirm",
|
|
164
|
-
message: chalk_1.default.red(`⚠ You are about to SET ${Object.keys(payload).length} secret(s) in PRODUCTION. Are you sure?`),
|
|
165
|
-
default: false,
|
|
166
|
-
}]);
|
|
167
|
-
if (!confirm) {
|
|
168
|
-
console.log(chalk_1.default.yellow("Aborted."));
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
const spinner = (0, ora_1.default)(`Setting ${Object.keys(payload).length} secrets for ${env} (branch: ${branch})...`);
|
|
173
|
-
if (options.force) {
|
|
174
|
-
spinner.start();
|
|
175
|
-
}
|
|
176
|
-
try {
|
|
177
|
-
let expectedVersions = undefined;
|
|
178
|
-
if (!options.force) {
|
|
179
|
-
// Fetch current versions for optimistic locking
|
|
180
|
-
spinner.text = "Fetching current versions...";
|
|
181
|
-
spinner.start();
|
|
182
|
-
try {
|
|
183
|
-
const remoteSecrets = await api_1.api.getSecretVersions(project, env, branch);
|
|
184
|
-
expectedVersions = {};
|
|
185
|
-
spinner.stop();
|
|
186
|
-
// Populate expected versions for keys we are updating
|
|
187
|
-
Object.keys(payload).forEach(key => {
|
|
188
|
-
if (remoteSecrets[key]) {
|
|
189
|
-
expectedVersions[key] = remoteSecrets[key].version;
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
catch (verErr) {
|
|
194
|
-
spinner.warn(chalk_1.default.yellow("Could not fetch remote versions. Conflict detection disabled."));
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
spinner.start(`Updating secrets...`);
|
|
198
|
-
await api_1.api.setSecrets(project, env, payload, expectedVersions, branch);
|
|
199
|
-
spinner.succeed("Secrets updated successfully.");
|
|
200
|
-
// Log Audit
|
|
201
|
-
try {
|
|
202
|
-
const { logAudit } = require("../lib/audit");
|
|
203
|
-
logAudit("SECRET_UPDATE", project, env, { keys: Object.keys(payload), branch });
|
|
204
|
-
}
|
|
205
|
-
catch (e) { }
|
|
206
|
-
}
|
|
207
|
-
catch (error) {
|
|
208
|
-
spinner.stop();
|
|
209
|
-
if (error.response && error.response.status === 409) {
|
|
210
|
-
// Conflict Detected
|
|
211
|
-
const conflicts = error.response.data.conflicts || [];
|
|
212
|
-
console.log(chalk_1.default.red("\n⚠ Conflict Detected! The following secrets have changed remotely:\n"));
|
|
213
|
-
const conflictTable = [[chalk_1.default.bold("Key"), chalk_1.default.bold("Remote Version"), chalk_1.default.bold("Remote Value")]];
|
|
214
|
-
conflicts.forEach((c) => {
|
|
215
|
-
conflictTable.push([c.key, c.actual, c.remoteValue]);
|
|
216
|
-
});
|
|
217
|
-
console.log((0, table_1.table)(conflictTable));
|
|
218
|
-
const inquirer = require("inquirer");
|
|
219
|
-
const { confirm } = await inquirer.prompt([{
|
|
220
|
-
type: "confirm",
|
|
221
|
-
name: "confirm",
|
|
222
|
-
message: "Do you want to overwrite these changes exactly as you specified?",
|
|
223
|
-
default: false
|
|
224
|
-
}]);
|
|
225
|
-
if (confirm) {
|
|
226
|
-
const retrySpinner = (0, ora_1.default)("Overwriting secrets...").start();
|
|
227
|
-
try {
|
|
228
|
-
// Start retry with Force (no expectedVersions)
|
|
229
|
-
await api_1.api.setSecrets(project, env, payload, undefined, branch);
|
|
230
|
-
retrySpinner.succeed("Secrets overwritten successfully.");
|
|
231
|
-
// Log Audit Force
|
|
232
|
-
try {
|
|
233
|
-
const { logAudit } = require("../lib/audit");
|
|
234
|
-
logAudit("SECRET_UPDATE_FORCE", project, env, { keys: Object.keys(payload), branch });
|
|
235
|
-
}
|
|
236
|
-
catch (e) { }
|
|
237
|
-
}
|
|
238
|
-
catch (retryErr) {
|
|
239
|
-
retrySpinner.fail("Failed to overwrite secrets.");
|
|
240
|
-
console.error(chalk_1.default.red(retryErr.message));
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
console.log(chalk_1.default.yellow("Update cancelled."));
|
|
245
|
-
}
|
|
246
|
-
return; // Handled
|
|
247
|
-
}
|
|
248
|
-
if (spinner.isSpinning)
|
|
249
|
-
spinner.fail("Failed to update secrets");
|
|
250
|
-
if (error.response && error.response.data && error.response.data.error) {
|
|
251
|
-
console.error(chalk_1.default.red(`Server Error: ${error.response.data.error}`));
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
console.error(chalk_1.default.red(error.message));
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
// LINK
|
|
259
|
-
exports.secretsCommand
|
|
260
|
-
.command("link")
|
|
261
|
-
.description("Link a secret to another secret (Reference)")
|
|
262
|
-
.argument("<key>", "Key of the secret to create (e.g. DB_URL)")
|
|
263
|
-
.requiredOption("--source <source>", "Source path (format: project/env/key)")
|
|
264
|
-
.addHelpText("after", `
|
|
265
|
-
Examples:
|
|
266
|
-
$ xtra secrets link SHARED_DB_URL --source shared-proj/production/DB_URL
|
|
267
|
-
`)
|
|
268
|
-
.action(async (key, options) => {
|
|
269
|
-
const parentOpts = exports.secretsCommand.opts();
|
|
270
|
-
let { project, env } = parentOpts;
|
|
271
|
-
// Normalize Env
|
|
272
|
-
const envMap = { dev: "development", stg: "staging", prod: "production" };
|
|
273
|
-
env = envMap[env] || env;
|
|
274
|
-
const { source } = options;
|
|
275
|
-
if (!project) {
|
|
276
|
-
project = (0, config_1.getConfigValue)("project");
|
|
277
|
-
}
|
|
278
|
-
if (!project) {
|
|
279
|
-
console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
|
|
280
|
-
process.exit(1);
|
|
281
|
-
}
|
|
282
|
-
// Parse Source: "proj-123/prod/DATABASE_URL"
|
|
283
|
-
const parts = source.split("/");
|
|
284
|
-
if (parts.length !== 3) {
|
|
285
|
-
console.error(chalk_1.default.red("Error: Source must be in format 'projectId/env/key'"));
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
const [sourceProjectId, sourceEnv, sourceKey] = parts;
|
|
289
|
-
const spinner = (0, ora_1.default)(`Linking ${key} to ${source}...`).start();
|
|
290
|
-
try {
|
|
291
|
-
await api_1.api.linkSecret(project, env, key, sourceProjectId, sourceEnv, sourceKey);
|
|
292
|
-
spinner.succeed(chalk_1.default.green(`Secret '${key}' successfully linked to '${sourceKey}'`));
|
|
293
|
-
// Audit log
|
|
294
|
-
try {
|
|
295
|
-
const { logAudit } = require("../lib/audit");
|
|
296
|
-
logAudit("SECRET_LINKED", project, env, { key, source });
|
|
297
|
-
}
|
|
298
|
-
catch (e) { }
|
|
299
|
-
}
|
|
300
|
-
catch (error) {
|
|
301
|
-
spinner.fail("Failed to link secret");
|
|
302
|
-
const safeErr = error?.response?.data?.error || error.message || "Unknown error";
|
|
303
|
-
console.error(chalk_1.default.red(`Server Error: ${safeErr}`));
|
|
304
|
-
}
|
|
305
|
-
});
|
|
1
|
+
var pe=Object.create;var I=Object.defineProperty;var ge=Object.getOwnPropertyDescriptor;var de=Object.getOwnPropertyNames;var le=Object.getPrototypeOf,ue=Object.prototype.hasOwnProperty;var A=(e,t)=>()=>(e&&(t=e(e=0)),t);var V=(e,t)=>{for(var r in t)I(e,r,{get:t[r],enumerable:!0})},J=(e,t,r,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of de(t))!ue.call(e,n)&&n!==r&&I(e,n,{get:()=>t[n],enumerable:!(s=ge(t,n))||s.enumerable});return e};var u=(e,t,r)=>(r=e!=null?pe(le(e)):{},J(t||!e||!e.__esModule?I(r,"default",{value:e,enumerable:!0}):r,e)),R=e=>J(I({},"__esModule",{value:!0}),e);var M,_,H,P,y,T,S,C,G,z,U=A(()=>{M=u(require("conf")),_=u(require("fs")),H=u(require("path")),P="https://www.xtrasecurity.in/api",y=new M.default({projectName:"xtra-cli",defaults:{apiUrl:P}}),T=y.get("apiUrl");T&&!process.env.XTRA_API_URL&&(T.includes("localhost")||T.includes("xtra-security.vercel.app"))&&y.set("apiUrl",P);S=()=>({...y.store,apiUrl:process.env.XTRA_API_URL||y.get("apiUrl")||P}),C=e=>y.get(e),G=()=>y.get("token"),z=()=>{let e={};try{let t=H.default.join(process.cwd(),".xtrarc");_.default.existsSync(t)&&(e=JSON.parse(_.default.readFileSync(t,"utf-8")))}catch{}return{project:e.project||y.get("project")||"",env:e.env||y.get("env")||"development",branch:e.branch||y.get("branch")||"main"}}});var Y,X,W,fe,o,h,B=A(()=>{Y=u(require("axios"));U();X=u(require("fs")),W=u(require("path")),fe=JSON.parse(X.readFileSync(W.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:e}=S(),t=G();return Y.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":fe.version,...t?{Authorization:`Bearer ${t}`}:{}}})},h={login:async(e,t,r)=>(await o().post("/auth/cli-login",{email:e,password:t,apiKey:r})).data,getSecrets:async(e,t,r="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?branch=${r}`)).data,getSecretVersions:async(e,t,r="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${r}`)).data,setSecrets:async(e,t,r,s,n="main")=>(await o().post(`/projects/${e}/envs/${t}/secrets`,{secrets:r,expectedVersions:s,branch:n})).data,getSecretDetails:async(e,t,r,s="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets/${r}?branch=${s}`)).data,syncLogs:async e=>(await o().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,r,s,n,c)=>(await o().post(`/projects/${e}/envs/${t}/secrets/link`,{key:r,sourceProjectId:s,sourceEnv:n,sourceKey:c})).data,rotateSecret:async(e,t,r,s,n)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${r}/rotate`,{strategy:s,parsedNewValue:n})).data,promoteSecret:async(e,t,r)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${r}/promote`,{})).data,verifyAuditLogs:async()=>(await o().get("/audit/verify")).data,exportAuditLogs:async(e,t,r,s)=>{let n={format:e};return t&&(n.startDate=t),r&&(n.endDate=r),s&&(n.projectId=s),(await o().post("/audit/export",n,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,r,s)=>(await o().post("/access/request",{projectId:e,secretId:s,reason:t,duration:r})).data,approveAccess:async(e,t)=>(await o().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=S(),r=t?`&workspaceId=${t}`:"";return(await o().get(`/access/list?mode=${e}${r}`)).data},getAccessRequestStatus:async e=>(await o().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=S(),t=e?`/project?workspaceId=${e}`:"/project";return(await o().get(t)).data},getBranches:async e=>(await o().get(`/branch?projectId=${e}`)).data,createBranch:async(e,t,r)=>(await o().post("/branch",{projectId:e,name:t,description:r})).data,deleteBranch:async e=>(await o().delete(`/branch/${e}`)).data,updateBranch:async(e,t)=>(await o().put("/branch",{id:e,...t})).data,getRoles:async()=>(await o().get("/admin/roles")).data,getUsers:async e=>{let t=e?`?teamId=${e}`:"";return(await o().get(`/admin/users${t}`)).data},setUserRole:async(e,t,r)=>(await o().put("/admin/users/role",{email:e,role:t,teamId:r})).data,getIntegrationStatus:async e=>(await o().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await o().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await o().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,t)=>{let r=new URLSearchParams(t).toString();return(await o().get(`/projects/${e}/kubernetes?${r}`)).data},getSecretHistory:async(e,t,r)=>(await o().get(`/projects/${e}/envs/${t}/secrets/${r}/history`)).data,rollbackSecret:async(e,t,r,s)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${r}/history`,{version:s})).data,cloneEnvironment:async(e,t,r,s,n)=>(await o().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:r,overwrite:s,branch:n})).data,generateJitLink:async e=>(await o().post("/jit/generate",e)).data,claimJitLink:async e=>(await o().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await o().get(`/jit/${e}`)).data,post:async(e,t)=>(await o().post(e,t)).data}});function re(){let e=Q.get("masterKey");e||(e=j.default.randomBytes(32).toString("hex"),Q.set("masterKey",e));let t=(0,ee.machineIdSync)();return j.default.createHash("sha256").update(e+t).digest()}var j,Z,ee,te,Q,se,ne,oe=A(()=>{j=u(require("crypto")),Z=u(require("conf")),ee=require("node-machine-id"),te="aes-256-gcm",Q=new Z.default({projectName:"xtra-cli-crypto"});se=e=>{let t=j.default.randomBytes(16),r=re(),s=j.default.createCipheriv(te,r,t),n=s.update(e,"utf8","hex");n+=s.final("hex");let c=s.getAuthTag();return{iv:t.toString("hex"),content:n,tag:c.toString("hex")}},ne=e=>{let t=re(),r=Buffer.from(e.iv,"hex"),s=Buffer.from(e.tag,"hex"),n=j.default.createDecipheriv(te,t,r);n.setAuthTag(s);let c=n.update(e.content,"hex","utf8");return c+=n.final("utf8"),c}});var b={};V(b,{loadAuditLogs:()=>O,logAudit:()=>me,saveAuditLogs:()=>D,syncAuditLogs:()=>he});function ye(e){if(!e||typeof e!="object")return e;let t={...e},r=["password","token","secret","value","apiKey","secretValue"];for(let s of Object.keys(t))r.some(n=>s.toLowerCase().includes(n))&&(t[s]="[REDACTED]");return t}async function ce(e){try{if(!S().token)return!1;let r={action:e.action,entity:"cli-project",entityId:e.projectId||"global",details:ye(e.details),timestamp:e.timestamp,projectId:e.projectId};return await h.post("/audit/cli-logs",{logs:[r]}),!0}catch(t){return process.env.DEBUG&&console.error("Failed to send audit log to backend:",t),!1}}function me(e,t,r,s){let n={id:(0,ie.v4)(),timestamp:new Date().toISOString(),action:e,projectId:t,environment:r,details:s,synced:!1},c=O();c.push(n),D(c),ce(n).then(m=>{if(m){n.synced=!0;let g=O(),d=g.findIndex(i=>i.id===n.id);d!==-1&&(g[d].synced=!0,D(g))}}).catch(()=>{})}function O(){if(!x.default.existsSync(q))return[];try{let e=x.default.readFileSync(q,"utf-8"),t=JSON.parse(e),r=ne(t);return JSON.parse(r)}catch{return[]}}function D(e){x.default.existsSync(N)||x.default.mkdirSync(N,{recursive:!0});let t=JSON.stringify(e),r=se(t);x.default.writeFileSync(q,JSON.stringify(r),"utf-8")}async function he(){let e=O(),t=e.filter(s=>!s.synced);if(t.length===0)return 0;let r=0;for(let s of t)await ce(s)&&(s.synced=!0,r++);return r>0&&D(e),r}var x,F,ie,N,q,$=A(()=>{x=u(require("fs")),F=u(require("path")),ie=require("uuid");oe();U();B();N=F.default.join(process.cwd(),".xtra"),q=F.default.join(N,"audit.enc")});var ve={};V(ve,{secretsCommand:()=>v});module.exports=R(ve);var ae=require("commander");B();var a=u(require("chalk")),k=u(require("ora")),K=require("table");U();var v=new ae.Command("secrets").description("Manage secrets (List, Set, Delete)").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (development, staging, production)","development").option("-b, --branch <branchName>","Branch Name");v.command("list").description("List all secrets for a project/environment").option("--show","Reveal secret values",!1).addHelpText("after",`
|
|
2
|
+
Examples:
|
|
3
|
+
$ xtra secrets list
|
|
4
|
+
$ xtra secrets list -e production --show
|
|
5
|
+
`).action(async e=>{let t=v.opts(),{project:r,env:s,branch:n}=t,c=z();r||(r=c.project),n||(n=c.branch),(!s||s==="development")&&(s=c.env),s={dev:"development",stg:"staging",prod:"production"}[s]||s;let{show:g}=e;r||(console.error(a.default.red("Error: Project ID is required. Use -p <id> or run `xtra init` first.")),process.exit(1));let d=(0,k.default)(`Fetching secrets for ${s} (branch: ${n})...`).start();try{let i=await h.getSecrets(r,s,n);if(d.stop(),!i||Object.keys(i).length===0){console.log(a.default.yellow("No secrets found."));return}g&&console.log(a.default.red("\u26A0 Warning: Secret values will be visible in your terminal and shell history!"));let p=[[a.default.bold("Key"),a.default.bold("Value"),a.default.bold("Env")]];Object.entries(i).forEach(([l,f])=>{p.push([l,g?f:"********",s])}),console.log((0,K.table)(p));try{let{logAudit:l}=await Promise.resolve().then(()=>($(),b));l("SECRET_LIST",r,s,{branch:n,count:Object.keys(i).length})}catch{}}catch(i){d.fail("Failed to fetch secrets"),console.error(a.default.red(i.message))}});v.command("set").description("Set one or more secrets (KEY=VALUE)").argument("<secrets...>","Secrets to set (format: KEY=VALUE)").option("-f, --force","Force update (overwrite remote changes without warning)",!1).addHelpText("after",`
|
|
6
|
+
Examples:
|
|
7
|
+
$ xtra secrets set API_KEY=xyz
|
|
8
|
+
$ xtra secrets set DB_USER=admin DB_PASS=secret -e staging
|
|
9
|
+
`).action(async(e,t)=>{let r=v.opts(),{project:s,env:n,branch:c}=r;c||(c=C("branch")||"main"),n={dev:"development",stg:"staging",prod:"production"}[n]||n,s||(s=C("project")),s||(console.error(a.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let g={};if(e.forEach(i=>{let p=i.indexOf("=");if(p===-1){console.warn(a.default.yellow(`Skipping invalid format: ${i} (expected KEY=VALUE)`));return}let l=i.substring(0,p),f=i.substring(p+1);g[l]=f}),Object.keys(g).length===0){console.error(a.default.red("No valid secrets provided."));return}if(n==="production"&&!t.force){let i=require("inquirer"),{confirm:p}=await i.prompt([{type:"confirm",name:"confirm",message:a.default.red(`\u26A0 You are about to SET ${Object.keys(g).length} secret(s) in PRODUCTION. Are you sure?`),default:!1}]);if(!p){console.log(a.default.yellow("Aborted."));return}}let d=(0,k.default)(`Setting ${Object.keys(g).length} secrets for ${n} (branch: ${c})...`);t.force&&d.start();try{let i;if(!t.force){d.text="Fetching current versions...",d.start();try{let p=await h.getSecretVersions(s,n,c);i={},d.stop(),Object.keys(g).forEach(l=>{p[l]&&(i[l]=p[l].version)})}catch{d.warn(a.default.yellow("Could not fetch remote versions. Conflict detection disabled."))}}d.start("Updating secrets..."),await h.setSecrets(s,n,g,i,c),d.succeed("Secrets updated successfully.");try{let{logAudit:p}=($(),R(b));p("SECRET_UPDATE",s,n,{keys:Object.keys(g),branch:c})}catch{}}catch(i){if(d.stop(),i.response&&i.response.status===409){let p=i.response.data.conflicts||[];console.log(a.default.red(`
|
|
10
|
+
\u26A0 Conflict Detected! The following secrets have changed remotely:
|
|
11
|
+
`));let l=[[a.default.bold("Key"),a.default.bold("Remote Version"),a.default.bold("Remote Value")]];p.forEach(w=>{l.push([w.key,w.actual,w.remoteValue])}),console.log((0,K.table)(l));let f=require("inquirer"),{confirm:L}=await f.prompt([{type:"confirm",name:"confirm",message:"Do you want to overwrite these changes exactly as you specified?",default:!1}]);if(L){let w=(0,k.default)("Overwriting secrets...").start();try{await h.setSecrets(s,n,g,void 0,c),w.succeed("Secrets overwritten successfully.");try{let{logAudit:E}=($(),R(b));E("SECRET_UPDATE_FORCE",s,n,{keys:Object.keys(g),branch:c})}catch{}}catch(E){w.fail("Failed to overwrite secrets."),console.error(a.default.red(E.message))}}else console.log(a.default.yellow("Update cancelled."));return}d.isSpinning&&d.fail("Failed to update secrets"),i.response&&i.response.data&&i.response.data.error?console.error(a.default.red(`Server Error: ${i.response.data.error}`)):console.error(a.default.red(i.message))}});v.command("link").description("Link a secret to another secret (Reference)").argument("<key>","Key of the secret to create (e.g. DB_URL)").requiredOption("--source <source>","Source path (format: project/env/key)").addHelpText("after",`
|
|
12
|
+
Examples:
|
|
13
|
+
$ xtra secrets link SHARED_DB_URL --source shared-proj/production/DB_URL
|
|
14
|
+
`).action(async(e,t)=>{let r=v.opts(),{project:s,env:n}=r;n={dev:"development",stg:"staging",prod:"production"}[n]||n;let{source:m}=t;s||(s=C("project")),s||(console.error(a.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let g=m.split("/");if(g.length!==3){console.error(a.default.red("Error: Source must be in format 'projectId/env/key'"));return}let[d,i,p]=g,l=(0,k.default)(`Linking ${e} to ${m}...`).start();try{await h.linkSecret(s,n,e,d,i,p),l.succeed(a.default.green(`Secret '${e}' successfully linked to '${p}'`));try{let{logAudit:f}=($(),R(b));f("SECRET_LINKED",s,n,{key:e,source:m})}catch{}}catch(f){l.fail("Failed to link secret");let L=f?.response?.data?.error||f.message||"Unknown error";console.error(a.default.red(`Server Error: ${L}`))}});0&&(module.exports={secretsCommand});
|
|
@@ -1,109 +1,6 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
exports
|
|
7
|
-
/**
|
|
8
|
-
* simulate.ts — Dry-run mode for xtra run
|
|
9
|
-
*
|
|
10
|
-
* Fetches secrets and shows exactly what `xtra run` would inject into
|
|
11
|
-
* the process environment — without actually executing the command.
|
|
12
|
-
* Safe to use in any environment.
|
|
13
|
-
*/
|
|
14
|
-
const commander_1 = require("commander");
|
|
15
|
-
const api_1 = require("../lib/api");
|
|
16
|
-
const config_1 = require("../lib/config");
|
|
17
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
18
|
-
const ora_1 = __importDefault(require("ora"));
|
|
19
|
-
const table_1 = require("table");
|
|
20
|
-
exports.simulateCommand = new commander_1.Command("simulate")
|
|
21
|
-
.description("Dry-run: show what 'xtra run' would inject without executing the command")
|
|
22
|
-
.option("-p, --project <id>", "Project ID")
|
|
23
|
-
.option("-e, --env <environment>", "Environment", "development")
|
|
24
|
-
.option("-b, --branch <branch>", "Branch", "main")
|
|
25
|
-
.argument("[command]", "Command that would be executed (display only)")
|
|
26
|
-
.option("--show-values", "Reveal secret values in output (default: masked)", false)
|
|
27
|
-
.option("--diff", "Highlight secrets that differ from local .env / process.env", false)
|
|
28
|
-
.action(async (command, options) => {
|
|
29
|
-
let { project, env, branch, showValues, diff: showDiff } = options;
|
|
30
|
-
const envMap = { dev: "development", stg: "staging", prod: "production" };
|
|
31
|
-
env = envMap[env] || env;
|
|
32
|
-
if (!project)
|
|
33
|
-
project = (0, config_1.getRcConfig)().project;
|
|
34
|
-
if (!project) {
|
|
35
|
-
console.error(chalk_1.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'."));
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
const spinner = (0, ora_1.default)(`Fetching secrets to simulate injection (${env}/${branch})...`).start();
|
|
39
|
-
let secrets;
|
|
40
|
-
try {
|
|
41
|
-
secrets = await api_1.api.getSecrets(project, env, branch);
|
|
42
|
-
spinner.stop();
|
|
43
|
-
}
|
|
44
|
-
catch (e) {
|
|
45
|
-
spinner.fail("Failed to fetch secrets.");
|
|
46
|
-
console.error(chalk_1.default.red(e?.response?.data?.error || e.message));
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
const count = Object.keys(secrets).length;
|
|
50
|
-
const termWidth = process.stdout.columns || 100;
|
|
51
|
-
const keyWidth = Math.floor(termWidth * 0.25);
|
|
52
|
-
const valWidth = Math.floor(termWidth * 0.4);
|
|
53
|
-
const localWidth = Math.floor(termWidth * 0.15);
|
|
54
|
-
console.log(chalk_1.default.bold.cyan("\n● Simulation") + chalk_1.default.bold(` — xtra run ${command ? chalk_1.default.cyan(command) : "(no command specified)"}\n`));
|
|
55
|
-
console.log(chalk_1.default.gray(` Project : ${chalk_1.default.white(project)}`));
|
|
56
|
-
console.log(chalk_1.default.gray(` Env : ${chalk_1.default.white(env)}`));
|
|
57
|
-
console.log(chalk_1.default.gray(` Branch : ${chalk_1.default.white(branch)}`));
|
|
58
|
-
console.log(chalk_1.default.gray(` Secrets : ${chalk_1.default.white(count)} would be injected\n`));
|
|
59
|
-
if (count === 0) {
|
|
60
|
-
console.log(chalk_1.default.yellow(" No secrets found for this environment/branch."));
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const rows = [
|
|
64
|
-
[chalk_1.default.bold("Key"), chalk_1.default.bold("Injected Value"), chalk_1.default.bold("Local .env"), chalk_1.default.bold("Status")]
|
|
65
|
-
];
|
|
66
|
-
for (const [key, value] of Object.entries(secrets)) {
|
|
67
|
-
const localVal = process.env[key];
|
|
68
|
-
const displayVal = showValues ? chalk_1.default.cyan(value) : chalk_1.default.gray("••••••••");
|
|
69
|
-
let status = chalk_1.default.green("Injected");
|
|
70
|
-
let localDisplay = "-";
|
|
71
|
-
if (showDiff && localVal !== undefined) {
|
|
72
|
-
localDisplay = showValues ? chalk_1.default.yellow(localVal) : chalk_1.default.gray("••••••••");
|
|
73
|
-
if (localVal === value) {
|
|
74
|
-
status = chalk_1.default.gray("Same");
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
status = chalk_1.default.yellow("⚡ Override");
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
else if (showDiff) {
|
|
81
|
-
status = chalk_1.default.blue("New");
|
|
82
|
-
localDisplay = chalk_1.default.gray("(not set)");
|
|
83
|
-
}
|
|
84
|
-
rows.push([chalk_1.default.white(key), displayVal, showDiff ? localDisplay : "-", status]);
|
|
85
|
-
}
|
|
86
|
-
console.log((0, table_1.table)(rows, {
|
|
87
|
-
columns: [
|
|
88
|
-
{ width: keyWidth, wrapWord: true },
|
|
89
|
-
{ width: valWidth, wrapWord: true },
|
|
90
|
-
{ width: localWidth, wrapWord: true },
|
|
91
|
-
{ width: 10 }
|
|
92
|
-
],
|
|
93
|
-
border: {
|
|
94
|
-
topBody: `─`, topJoin: `┬`, topLeft: `┌`, topRight: `┐`,
|
|
95
|
-
bottomBody: `─`, bottomJoin: `┴`, bottomLeft: `└`, bottomRight: `┘`,
|
|
96
|
-
bodyLeft: `│`, bodyRight: `│`, bodyJoin: `│`,
|
|
97
|
-
joinBody: `─`, joinLeft: `├`, joinRight: `┤`, joinJoin: `┼`
|
|
98
|
-
}
|
|
99
|
-
}));
|
|
100
|
-
// Summary
|
|
101
|
-
if (!showValues) {
|
|
102
|
-
console.log(chalk_1.default.gray(" Tip: use --show-values to reveal actual secret values"));
|
|
103
|
-
}
|
|
104
|
-
if (!showDiff) {
|
|
105
|
-
console.log(chalk_1.default.gray(" Tip: use --diff to compare against your local process.env"));
|
|
106
|
-
}
|
|
107
|
-
console.log(chalk_1.default.bold.green(`\n ✔ Simulation complete.`) + chalk_1.default.bold(` ${count} secret(s) would be injected.`));
|
|
108
|
-
console.log(chalk_1.default.gray(" Run without 'simulate' to actually execute the command.\n"));
|
|
109
|
-
});
|
|
1
|
+
var G=Object.create;var u=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var M=Object.getPrototypeOf,E=Object.prototype.hasOwnProperty;var H=(e,t)=>{for(var s in t)u(e,s,{get:t[s],enumerable:!0})},R=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of F(t))!E.call(e,a)&&a!==s&&u(e,a,{get:()=>t[a],enumerable:!(n=W(t,a))||n.enumerable});return e};var c=(e,t,s)=>(s=e!=null?G(M(e)):{},R(t||!e||!e.__esModule?u(s,"default",{value:e,enumerable:!0}):s,e)),X=e=>R(u({},"__esModule",{value:!0}),e);var Q={};H(Q,{simulateCommand:()=>z});module.exports=X(Q);var J=require("commander");var A=c(require("axios"));var I=c(require("conf")),j=c(require("fs")),U=c(require("path")),$="https://www.xtrasecurity.in/api",p=new I.default({projectName:"xtra-cli",defaults:{apiUrl:$}}),v=p.get("apiUrl");v&&!process.env.XTRA_API_URL&&(v.includes("localhost")||v.includes("xtra-security.vercel.app"))&&p.set("apiUrl",$);var y=()=>({...p.store,apiUrl:process.env.XTRA_API_URL||p.get("apiUrl")||$});var C=()=>p.get("token");var L=()=>{let e={};try{let t=U.default.join(process.cwd(),".xtrarc");j.default.existsSync(t)&&(e=JSON.parse(j.default.readFileSync(t,"utf-8")))}catch{}return{project:e.project||p.get("project")||"",env:e.env||p.get("env")||"development",branch:e.branch||p.get("branch")||"main"}};var P=c(require("fs")),B=c(require("path")),K=JSON.parse(P.readFileSync(B.join(__dirname,"../../package.json"),"utf-8")),r=()=>{let{apiUrl:e}=y(),t=C();return A.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":K.version,...t?{Authorization:`Bearer ${t}`}:{}}})},_={login:async(e,t,s)=>(await r().post("/auth/cli-login",{email:e,password:t,apiKey:s})).data,getSecrets:async(e,t,s="main")=>(await r().get(`/projects/${e}/envs/${t}/secrets?branch=${s}`)).data,getSecretVersions:async(e,t,s="main")=>(await r().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(e,t,s,n,a="main")=>(await r().post(`/projects/${e}/envs/${t}/secrets`,{secrets:s,expectedVersions:n,branch:a})).data,getSecretDetails:async(e,t,s,n="main")=>(await r().get(`/projects/${e}/envs/${t}/secrets/${s}?branch=${n}`)).data,syncLogs:async e=>(await r().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,s,n,a,i)=>(await r().post(`/projects/${e}/envs/${t}/secrets/link`,{key:s,sourceProjectId:n,sourceEnv:a,sourceKey:i})).data,rotateSecret:async(e,t,s,n,a)=>(await r().post(`/projects/${e}/envs/${t}/secrets/${s}/rotate`,{strategy:n,parsedNewValue:a})).data,promoteSecret:async(e,t,s)=>(await r().post(`/projects/${e}/envs/${t}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await r().get("/audit/verify")).data,exportAuditLogs:async(e,t,s,n)=>{let a={format:e};return t&&(a.startDate=t),s&&(a.endDate=s),n&&(a.projectId=n),(await r().post("/audit/export",a,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,s,n)=>(await r().post("/access/request",{projectId:e,secretId:n,reason:t,duration:s})).data,approveAccess:async(e,t)=>(await r().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=y(),s=t?`&workspaceId=${t}`:"";return(await r().get(`/access/list?mode=${e}${s}`)).data},getAccessRequestStatus:async e=>(await r().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=y(),t=e?`/project?workspaceId=${e}`:"/project";return(await r().get(t)).data},getBranches:async e=>(await r().get(`/branch?projectId=${e}`)).data,createBranch:async(e,t,s)=>(await r().post("/branch",{projectId:e,name:t,description:s})).data,deleteBranch:async e=>(await r().delete(`/branch/${e}`)).data,updateBranch:async(e,t)=>(await r().put("/branch",{id:e,...t})).data,getRoles:async()=>(await r().get("/admin/roles")).data,getUsers:async e=>{let t=e?`?teamId=${e}`:"";return(await r().get(`/admin/users${t}`)).data},setUserRole:async(e,t,s)=>(await r().put("/admin/users/role",{email:e,role:t,teamId:s})).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,t)=>{let s=new URLSearchParams(t).toString();return(await r().get(`/projects/${e}/kubernetes?${s}`)).data},getSecretHistory:async(e,t,s)=>(await r().get(`/projects/${e}/envs/${t}/secrets/${s}/history`)).data,rollbackSecret:async(e,t,s,n)=>(await r().post(`/projects/${e}/envs/${t}/secrets/${s}/history`,{version:n})).data,cloneEnvironment:async(e,t,s,n,a)=>(await r().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:s,overwrite:n,branch:a})).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,t)=>(await r().post(e,t)).data};var o=c(require("chalk")),T=c(require("ora")),D=require("table"),z=new J.Command("simulate").description("Dry-run: show what 'xtra run' would inject without executing the command").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment","development").option("-b, --branch <branch>","Branch","main").argument("[command]","Command that would be executed (display only)").option("--show-values","Reveal secret values in output (default: masked)",!1).option("--diff","Highlight secrets that differ from local .env / process.env",!1).action(async(e,t)=>{let{project:s,env:n,branch:a,showValues:i,diff:g}=t;n={dev:"development",stg:"staging",prod:"production"}[n]||n,s||(s=L().project),s||(console.error(o.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'.")),process.exit(1));let S=(0,T.default)(`Fetching secrets to simulate injection (${n}/${a})...`).start(),m;try{m=await _.getSecrets(s,n,a),S.stop()}catch(l){S.fail("Failed to fetch secrets."),console.error(o.default.red(l?.response?.data?.error||l.message)),process.exit(1)}let f=Object.keys(m).length,h=process.stdout.columns||100,O=Math.floor(h*.25),V=Math.floor(h*.4),N=Math.floor(h*.15);if(console.log(o.default.bold.cyan(`
|
|
2
|
+
\u25CF Simulation`)+o.default.bold(` \u2014 xtra run ${e?o.default.cyan(e):"(no command specified)"}
|
|
3
|
+
`)),console.log(o.default.gray(` Project : ${o.default.white(s)}`)),console.log(o.default.gray(` Env : ${o.default.white(n)}`)),console.log(o.default.gray(` Branch : ${o.default.white(a)}`)),console.log(o.default.gray(` Secrets : ${o.default.white(f)} would be injected
|
|
4
|
+
`)),f===0){console.log(o.default.yellow(" No secrets found for this environment/branch."));return}let x=[[o.default.bold("Key"),o.default.bold("Injected Value"),o.default.bold("Local .env"),o.default.bold("Status")]];for(let[l,k]of Object.entries(m)){let w=process.env[l],q=i?o.default.cyan(k):o.default.gray("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"),d=o.default.green("Injected"),b="-";g&&w!==void 0?(b=i?o.default.yellow(w):o.default.gray("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"),w===k?d=o.default.gray("Same"):d=o.default.yellow("\u26A1 Override")):g&&(d=o.default.blue("New"),b=o.default.gray("(not set)")),x.push([o.default.white(l),q,g?b:"-",d])}console.log((0,D.table)(x,{columns:[{width:O,wrapWord:!0},{width:V,wrapWord:!0},{width:N,wrapWord:!0},{width:10}],border:{topBody:"\u2500",topJoin:"\u252C",topLeft:"\u250C",topRight:"\u2510",bottomBody:"\u2500",bottomJoin:"\u2534",bottomLeft:"\u2514",bottomRight:"\u2518",bodyLeft:"\u2502",bodyRight:"\u2502",bodyJoin:"\u2502",joinBody:"\u2500",joinLeft:"\u251C",joinRight:"\u2524",joinJoin:"\u253C"}})),i||console.log(o.default.gray(" Tip: use --show-values to reveal actual secret values")),g||console.log(o.default.gray(" Tip: use --diff to compare against your local process.env")),console.log(o.default.bold.green(`
|
|
5
|
+
\u2714 Simulation complete.`)+o.default.bold(` ${f} secret(s) would be injected.`)),console.log(o.default.gray(` Run without 'simulate' to actually execute the command.
|
|
6
|
+
`))});0&&(module.exports={simulateCommand});
|
package/dist/commands/status.js
CHANGED
|
@@ -1,93 +1,2 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.statusCommand = 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 table_1 = require("table");
|
|
12
|
-
const manifest_1 = require("../lib/manifest");
|
|
13
|
-
const crypto_1 = require("../lib/crypto");
|
|
14
|
-
const config_1 = require("../lib/config");
|
|
15
|
-
exports.statusCommand = new commander_1.Command("status")
|
|
16
|
-
.description("Check synchronization status with XtraSync platform")
|
|
17
|
-
.option("-p, --project <projectId>", "Project ID")
|
|
18
|
-
.option("-e, --env <environment>", "Environment (dev, stg, prod)", "dev")
|
|
19
|
-
.option("-b, --branch <branchName>", "Branch Name")
|
|
20
|
-
.action(async (options) => {
|
|
21
|
-
let { project, env, branch } = options;
|
|
22
|
-
// Use config fallback
|
|
23
|
-
if (!project)
|
|
24
|
-
project = (0, config_1.getRcConfig)().project;
|
|
25
|
-
if (!branch) {
|
|
26
|
-
branch = (0, config_1.getRcConfig)().branch || "main";
|
|
27
|
-
}
|
|
28
|
-
if (!project) {
|
|
29
|
-
console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
// Normalize Env
|
|
33
|
-
const envMap = { dev: "development", stg: "staging", prod: "production" };
|
|
34
|
-
env = envMap[env] || env;
|
|
35
|
-
// Load Manifest
|
|
36
|
-
const manifest = (0, manifest_1.loadManifest)();
|
|
37
|
-
if (!manifest) {
|
|
38
|
-
console.log(chalk_1.default.yellow("No local manifest found. Run 'xtra run' or 'xtra generate' first."));
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
if (manifest.projectId !== project || manifest.environment !== env) {
|
|
42
|
-
console.log(chalk_1.default.yellow(`Manifest found for DIFFERENT project/env (${manifest.projectId}/${manifest.environment}).`));
|
|
43
|
-
console.log("Checking status against requested target anyway...");
|
|
44
|
-
}
|
|
45
|
-
const spinner = (0, ora_1.default)(`Fetching remote state for ${env} (branch: ${branch})...`).start();
|
|
46
|
-
try {
|
|
47
|
-
const secrets = await api_1.api.getSecrets(project, env, branch);
|
|
48
|
-
spinner.stop();
|
|
49
|
-
// Compare
|
|
50
|
-
const rows = [[chalk_1.default.bold("Secret Key"), chalk_1.default.bold("Status"), chalk_1.default.bold("Local Last Updated")]];
|
|
51
|
-
let diffCount = 0;
|
|
52
|
-
// Check Remote vs Local
|
|
53
|
-
Object.entries(secrets).forEach(([key, value]) => {
|
|
54
|
-
const remoteHash = (0, crypto_1.hash)(value);
|
|
55
|
-
const localEntry = manifest.secrets[key];
|
|
56
|
-
if (!localEntry) {
|
|
57
|
-
rows.push([key, chalk_1.default.green("NEW (Remote)"), "-"]);
|
|
58
|
-
diffCount++;
|
|
59
|
-
}
|
|
60
|
-
else if (localEntry.hash !== remoteHash) {
|
|
61
|
-
rows.push([key, chalk_1.default.yellow("MODIFIED"), localEntry.updatedAt]);
|
|
62
|
-
diffCount++;
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
rows.push([key, chalk_1.default.cyan("Synced"), localEntry.updatedAt]);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
// Check Local vs Remote (Deleted keys)
|
|
69
|
-
Object.keys(manifest.secrets).forEach(key => {
|
|
70
|
-
if (!secrets[key]) {
|
|
71
|
-
rows.push([key, chalk_1.default.red("DELETED (Remote)"), manifest.secrets[key].updatedAt]);
|
|
72
|
-
diffCount++;
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
console.log("");
|
|
76
|
-
console.log((0, table_1.table)(rows));
|
|
77
|
-
if (diffCount === 0) {
|
|
78
|
-
console.log(chalk_1.default.green("✔ Everything is in sync."));
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
console.log(chalk_1.default.yellow(`⚠ Found ${diffCount} difference(s).\n`));
|
|
82
|
-
console.log(chalk_1.default.bold(" Next steps:"));
|
|
83
|
-
console.log(chalk_1.default.gray(" xtra generate ") + chalk_1.default.white("# pull cloud secrets → .env (merge)"));
|
|
84
|
-
console.log(chalk_1.default.gray(" xtra generate -f json ") + chalk_1.default.white("# pull cloud secrets → secrets.json"));
|
|
85
|
-
console.log(chalk_1.default.gray(" xtra run node app.js ") + chalk_1.default.white("# inject secrets at runtime (no file)"));
|
|
86
|
-
console.log(chalk_1.default.gray(" xtra local sync ") + chalk_1.default.white("# pull cloud secrets → .env.local (offline mode)"));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
catch (error) {
|
|
90
|
-
spinner.fail("Failed to fetch remote secrets.");
|
|
91
|
-
console.error(chalk_1.default.red(error.message));
|
|
92
|
-
}
|
|
93
|
-
});
|
|
1
|
+
var q=Object.create;var m=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var H=Object.getPrototypeOf,K=Object.prototype.hasOwnProperty;var V=(t,e)=>{for(var s in e)m(t,s,{get:e[s],enumerable:!0})},b=(t,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of G(e))!K.call(t,a)&&a!==s&&m(t,a,{get:()=>e[a],enumerable:!(n=J(e,a))||n.enumerable});return t};var c=(t,e,s)=>(s=t!=null?q(H(t)):{},b(e||!t||!t.__esModule?m(s,"default",{value:t,enumerable:!0}):s,t)),X=t=>b(m({},"__esModule",{value:!0}),t);var Z={};V(Z,{statusCommand:()=>Y});module.exports=X(Z);var O=require("commander");var C=c(require("axios"));var I=c(require("conf")),v=c(require("fs")),A=c(require("path")),S="https://www.xtrasecurity.in/api",g=new I.default({projectName:"xtra-cli",defaults:{apiUrl:S}}),w=g.get("apiUrl");w&&!process.env.XTRA_API_URL&&(w.includes("localhost")||w.includes("xtra-security.vercel.app"))&&g.set("apiUrl",S);var y=()=>({...g.store,apiUrl:process.env.XTRA_API_URL||g.get("apiUrl")||S});var k=()=>g.get("token");var j=()=>{let t={};try{let e=A.default.join(process.cwd(),".xtrarc");v.default.existsSync(e)&&(t=JSON.parse(v.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 R=c(require("fs")),U=c(require("path")),z=JSON.parse(R.readFileSync(U.join(__dirname,"../../package.json"),"utf-8")),r=()=>{let{apiUrl:t}=y(),e=k();return C.default.create({baseURL:t,headers:{"Content-Type":"application/json","X-CLI-Version":z.version,...e?{Authorization:`Bearer ${e}`}:{}}})},E={login:async(t,e,s)=>(await r().post("/auth/cli-login",{email:t,password:e,apiKey:s})).data,getSecrets:async(t,e,s="main")=>(await r().get(`/projects/${t}/envs/${e}/secrets?branch=${s}`)).data,getSecretVersions:async(t,e,s="main")=>(await r().get(`/projects/${t}/envs/${e}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(t,e,s,n,a="main")=>(await r().post(`/projects/${t}/envs/${e}/secrets`,{secrets:s,expectedVersions:n,branch:a})).data,getSecretDetails:async(t,e,s,n="main")=>(await r().get(`/projects/${t}/envs/${e}/secrets/${s}?branch=${n}`)).data,syncLogs:async t=>(await r().post("/audit/cli-logs",{logs:t})).data,linkSecret:async(t,e,s,n,a,i)=>(await r().post(`/projects/${t}/envs/${e}/secrets/link`,{key:s,sourceProjectId:n,sourceEnv:a,sourceKey:i})).data,rotateSecret:async(t,e,s,n,a)=>(await r().post(`/projects/${t}/envs/${e}/secrets/${s}/rotate`,{strategy:n,parsedNewValue:a})).data,promoteSecret:async(t,e,s)=>(await r().post(`/projects/${t}/envs/${e}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await r().get("/audit/verify")).data,exportAuditLogs:async(t,e,s,n)=>{let a={format:t};return e&&(a.startDate=e),s&&(a.endDate=s),n&&(a.projectId=n),(await r().post("/audit/export",a,{responseType:t==="csv"?"text":"json"})).data},requestAccess:async(t,e,s,n)=>(await r().post("/access/request",{projectId:t,secretId:n,reason:e,duration:s})).data,approveAccess:async(t,e)=>(await r().post("/access/approve",{requestId:t,decision:e})).data,listAccessRequests:async t=>{let{workspace:e}=y(),s=e?`&workspaceId=${e}`:"";return(await r().get(`/access/list?mode=${t}${s}`)).data},getAccessRequestStatus:async t=>(await r().get(`/access/request?id=${t}`)).data,getProjects:async()=>{let{workspace:t}=y(),e=t?`/project?workspaceId=${t}`:"/project";return(await r().get(e)).data},getBranches:async t=>(await r().get(`/branch?projectId=${t}`)).data,createBranch:async(t,e,s)=>(await r().post("/branch",{projectId:t,name:e,description:s})).data,deleteBranch:async t=>(await r().delete(`/branch/${t}`)).data,updateBranch:async(t,e)=>(await r().put("/branch",{id:t,...e})).data,getRoles:async()=>(await r().get("/admin/roles")).data,getUsers:async t=>{let e=t?`?teamId=${t}`:"";return(await r().get(`/admin/users${e}`)).data},setUserRole:async(t,e,s)=>(await r().put("/admin/users/role",{email:t,role:e,teamId:s})).data,getIntegrationStatus:async t=>(await r().get(`/integrations/${t}`)).data,getIntegrationRepos:async t=>(await r().get(`/integrations/${t}/sync`)).data.repos,syncSecretsToGithub:async t=>(await r().post("/integrations/github/sync",t)).data,exportKubernetesSecret:async(t,e)=>{let s=new URLSearchParams(e).toString();return(await r().get(`/projects/${t}/kubernetes?${s}`)).data},getSecretHistory:async(t,e,s)=>(await r().get(`/projects/${t}/envs/${e}/secrets/${s}/history`)).data,rollbackSecret:async(t,e,s,n)=>(await r().post(`/projects/${t}/envs/${e}/secrets/${s}/history`,{version:n})).data,cloneEnvironment:async(t,e,s,n,a)=>(await r().post(`/projects/${t}/envs/clone`,{fromEnv:e,toEnv:s,overwrite:n,branch:a})).data,generateJitLink:async t=>(await r().post("/jit/generate",t)).data,claimJitLink:async t=>(await r().post("/jit/claim",{token:t})).data,getJitInfo:async t=>(await r().get(`/jit/${t}`)).data,post:async(t,e)=>(await r().post(t,e)).data};var o=c(require("chalk")),B=c(require("ora")),F=require("table");var x=c(require("fs")),$=c(require("path")),W=$.default.join(process.cwd(),".xtra"),N=$.default.join(W,"manifest.json");function D(){if(!x.default.existsSync(N))return null;try{let t=x.default.readFileSync(N,"utf-8");return JSON.parse(t)}catch{return null}}var M=c(require("crypto")),P=c(require("conf")),Q=require("node-machine-id");var ot=new P.default({projectName:"xtra-cli-crypto"});var L=t=>M.default.createHash("sha256").update(t).digest("hex");var Y=new O.Command("status").description("Check synchronization status with XtraSync platform").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (dev, stg, prod)","dev").option("-b, --branch <branchName>","Branch Name").action(async t=>{let{project:e,env:s,branch:n}=t;e||(e=j().project),n||(n=j().branch||"main"),e||(console.error(o.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1)),s={dev:"development",stg:"staging",prod:"production"}[s]||s;let i=D();if(!i){console.log(o.default.yellow("No local manifest found. Run 'xtra run' or 'xtra generate' first."));return}(i.projectId!==e||i.environment!==s)&&(console.log(o.default.yellow(`Manifest found for DIFFERENT project/env (${i.projectId}/${i.environment}).`)),console.log("Checking status against requested target anyway..."));let h=(0,B.default)(`Fetching remote state for ${s} (branch: ${n})...`).start();try{let u=await E.getSecrets(e,s,n);h.stop();let l=[[o.default.bold("Secret Key"),o.default.bold("Status"),o.default.bold("Local Last Updated")]],d=0;Object.entries(u).forEach(([p,T])=>{let _=L(T),f=i.secrets[p];f?f.hash!==_?(l.push([p,o.default.yellow("MODIFIED"),f.updatedAt]),d++):l.push([p,o.default.cyan("Synced"),f.updatedAt]):(l.push([p,o.default.green("NEW (Remote)"),"-"]),d++)}),Object.keys(i.secrets).forEach(p=>{u[p]||(l.push([p,o.default.red("DELETED (Remote)"),i.secrets[p].updatedAt]),d++)}),console.log(""),console.log((0,F.table)(l)),d===0?console.log(o.default.green("\u2714 Everything is in sync.")):(console.log(o.default.yellow(`\u26A0 Found ${d} difference(s).
|
|
2
|
+
`)),console.log(o.default.bold(" Next steps:")),console.log(o.default.gray(" xtra generate ")+o.default.white("# pull cloud secrets \xE2\u2020\u2019 .env (merge)")),console.log(o.default.gray(" xtra generate -f json ")+o.default.white("# pull cloud secrets \xE2\u2020\u2019 secrets.json")),console.log(o.default.gray(" xtra run node app.js ")+o.default.white("# inject secrets at runtime (no file)")),console.log(o.default.gray(" xtra local sync ")+o.default.white("# pull cloud secrets \xE2\u2020\u2019 .env.local (offline mode)")))}catch(u){h.fail("Failed to fetch remote secrets."),console.error(o.default.red(u.message))}});0&&(module.exports={statusCommand});
|