xtra-cli 1.0.3 → 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 +3 -137
- 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 +7 -2
package/dist/commands/profile.js
CHANGED
|
@@ -1,184 +1,14 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
exports.profileCommand = new commander_1.Command("profile")
|
|
16
|
-
.description("Manage named configuration profiles (token, apiUrl, project per profile)")
|
|
17
|
-
.addHelpText("after", `
|
|
18
|
-
Examples:
|
|
19
|
-
$ xtra profile list # Show all profiles
|
|
20
|
-
$ xtra profile create work --url https://app.xtrasecurity.io/api
|
|
21
|
-
$ xtra profile use work # Switch active profile
|
|
22
|
-
$ xtra profile set work --project abc123 # Set a value in a profile
|
|
23
|
-
$ xtra profile delete old-profile # Remove a profile
|
|
24
|
-
$ xtra --profile work secrets list # Use profile for one command
|
|
25
|
-
`);
|
|
26
|
-
// ── LIST ──────────────────────────────────────────────────────────────────────
|
|
27
|
-
exports.profileCommand
|
|
28
|
-
.command("list")
|
|
29
|
-
.alias("ls")
|
|
30
|
-
.description("List all saved profiles")
|
|
31
|
-
.action(() => {
|
|
32
|
-
const profiles = (0, profiles_1.listProfiles)();
|
|
33
|
-
const active = (0, profiles_1.getActiveProfileName)();
|
|
34
|
-
if (Object.keys(profiles).length === 0) {
|
|
35
|
-
console.log(chalk_1.default.yellow("No profiles found."));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const rows = [
|
|
39
|
-
[chalk_1.default.bold("Profile"), chalk_1.default.bold("API URL"), chalk_1.default.bold("Project"), chalk_1.default.bold("Status")]
|
|
40
|
-
];
|
|
41
|
-
for (const [name, data] of Object.entries(profiles)) {
|
|
42
|
-
const isActive = name === active;
|
|
43
|
-
rows.push([
|
|
44
|
-
isActive ? chalk_1.default.green(`▶ ${name}`) : chalk_1.default.white(` ${name}`),
|
|
45
|
-
chalk_1.default.gray(data.apiUrl || "(default)"),
|
|
46
|
-
chalk_1.default.cyan(data.project || "(not set)"),
|
|
47
|
-
isActive ? chalk_1.default.green("active") : chalk_1.default.gray("inactive"),
|
|
48
|
-
]);
|
|
49
|
-
}
|
|
50
|
-
console.log(chalk_1.default.bold("\nProfiles:\n"));
|
|
51
|
-
console.log((0, table_1.table)(rows));
|
|
52
|
-
console.log(chalk_1.default.gray(`Tip: Use XTRA_PROFILE=<name> to override per-command.\n`));
|
|
53
|
-
});
|
|
54
|
-
// ── CREATE ────────────────────────────────────────────────────────────────────
|
|
55
|
-
exports.profileCommand
|
|
56
|
-
.command("create <name>")
|
|
57
|
-
.description("Create a new profile")
|
|
58
|
-
.option("--url <apiUrl>", "XtraSecurity API URL for this profile")
|
|
59
|
-
.option("--project <projectId>", "Default project ID for this profile")
|
|
60
|
-
.option("--token <token>", "Auth token for this profile")
|
|
61
|
-
.action(async (name, options) => {
|
|
62
|
-
try {
|
|
63
|
-
// If no options given, prompt interactively
|
|
64
|
-
let { url, project, token } = options;
|
|
65
|
-
if (!url && !project && !token) {
|
|
66
|
-
const answers = await inquirer_1.default.prompt([
|
|
67
|
-
{
|
|
68
|
-
type: "input",
|
|
69
|
-
name: "url",
|
|
70
|
-
message: "API URL:",
|
|
71
|
-
default: "https://www.xtrasecurity.in/api",
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
type: "input",
|
|
75
|
-
name: "project",
|
|
76
|
-
message: "Default Project ID (optional):",
|
|
77
|
-
default: "",
|
|
78
|
-
},
|
|
79
|
-
]);
|
|
80
|
-
url = answers.url;
|
|
81
|
-
project = answers.project || undefined;
|
|
82
|
-
}
|
|
83
|
-
(0, profiles_1.createProfile)(name, { apiUrl: url, project: project || undefined, token: token || undefined });
|
|
84
|
-
console.log(chalk_1.default.green(`✅ Profile '${name}' created!`));
|
|
85
|
-
console.log(chalk_1.default.gray(` Activate it with: xtra profile use ${name}`));
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
88
|
-
console.error(chalk_1.default.red(safeError(e)));
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
// ── USE ───────────────────────────────────────────────────────────────────────
|
|
92
|
-
exports.profileCommand
|
|
93
|
-
.command("use <name>")
|
|
94
|
-
.description("Switch to a different profile (persists across sessions)")
|
|
95
|
-
.action((name) => {
|
|
96
|
-
try {
|
|
97
|
-
(0, profiles_1.setActiveProfile)(name);
|
|
98
|
-
console.log(chalk_1.default.green(`✅ Switched to profile '${name}'.`));
|
|
99
|
-
console.log(chalk_1.default.gray(` All future commands will use this profile.`));
|
|
100
|
-
}
|
|
101
|
-
catch (e) {
|
|
102
|
-
console.error(chalk_1.default.red(safeError(e)));
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
// ── SET ───────────────────────────────────────────────────────────────────────
|
|
106
|
-
exports.profileCommand
|
|
107
|
-
.command("set <name>")
|
|
108
|
-
.description("Update a value in an existing profile")
|
|
109
|
-
.option("--url <apiUrl>", "Update the API URL")
|
|
110
|
-
.option("--project <projectId>", "Update the default project ID")
|
|
111
|
-
.option("--token <token>", "Update the auth token")
|
|
112
|
-
.option("--env <environment>", "Update the default environment")
|
|
113
|
-
.action((name, options) => {
|
|
114
|
-
const fieldMap = {
|
|
115
|
-
url: "apiUrl",
|
|
116
|
-
project: "project",
|
|
117
|
-
token: "token",
|
|
118
|
-
env: "env",
|
|
119
|
-
};
|
|
120
|
-
const updated = [];
|
|
121
|
-
try {
|
|
122
|
-
for (const [flag, field] of Object.entries(fieldMap)) {
|
|
123
|
-
if (options[flag]) {
|
|
124
|
-
(0, profiles_1.setProfileValue)(name, field, options[flag]);
|
|
125
|
-
updated.push(`${field} = ${field === "token" ? "****" : options[flag]}`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (updated.length === 0) {
|
|
129
|
-
console.log(chalk_1.default.yellow("No values provided. Use --url, --project, --token, or --env."));
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
console.log(chalk_1.default.green(`✅ Profile '${name}' updated:`));
|
|
133
|
-
updated.forEach(u => console.log(chalk_1.default.gray(` ${u}`)));
|
|
134
|
-
}
|
|
135
|
-
catch (e) {
|
|
136
|
-
console.error(chalk_1.default.red(safeError(e)));
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
// ── CURRENT ───────────────────────────────────────────────────────────────────
|
|
140
|
-
exports.profileCommand
|
|
141
|
-
.command("current")
|
|
142
|
-
.description("Show the currently active profile")
|
|
143
|
-
.action(() => {
|
|
144
|
-
const name = (0, profiles_1.getActiveProfileName)();
|
|
145
|
-
const profiles = (0, profiles_1.listProfiles)();
|
|
146
|
-
const data = profiles[name] || {};
|
|
147
|
-
console.log(chalk_1.default.bold(`\nActive Profile: ${chalk_1.default.green(name)}\n`));
|
|
148
|
-
const rows = [
|
|
149
|
-
[chalk_1.default.bold("Key"), chalk_1.default.bold("Value")],
|
|
150
|
-
["API URL", chalk_1.default.cyan(data.apiUrl || "(default)")],
|
|
151
|
-
["Project", chalk_1.default.cyan(data.project || "(not set)")],
|
|
152
|
-
["Branch", chalk_1.default.cyan(data.branch || "(not set)")],
|
|
153
|
-
["Env", chalk_1.default.cyan(data.env || "(not set)")],
|
|
154
|
-
["Token", data.token ? chalk_1.default.green("Set ✓") : chalk_1.default.gray("Not set")],
|
|
155
|
-
];
|
|
156
|
-
console.log((0, table_1.table)(rows));
|
|
157
|
-
});
|
|
158
|
-
// ── DELETE ────────────────────────────────────────────────────────────────────
|
|
159
|
-
exports.profileCommand
|
|
160
|
-
.command("delete <name>")
|
|
161
|
-
.alias("rm")
|
|
162
|
-
.description("Delete a profile")
|
|
163
|
-
.option("-y, --yes", "Skip confirmation prompt")
|
|
164
|
-
.action(async (name, options) => {
|
|
165
|
-
try {
|
|
166
|
-
if (!options.yes) {
|
|
167
|
-
const { confirm } = await inquirer_1.default.prompt([{
|
|
168
|
-
type: "confirm",
|
|
169
|
-
name: "confirm",
|
|
170
|
-
message: chalk_1.default.red(`Delete profile '${name}'? This cannot be undone.`),
|
|
171
|
-
default: false,
|
|
172
|
-
}]);
|
|
173
|
-
if (!confirm) {
|
|
174
|
-
console.log(chalk_1.default.gray("Cancelled."));
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
(0, profiles_1.deleteProfile)(name);
|
|
179
|
-
console.log(chalk_1.default.green(`✅ Profile '${name}' deleted.`));
|
|
180
|
-
}
|
|
181
|
-
catch (e) {
|
|
182
|
-
console.error(chalk_1.default.red(safeError(e)));
|
|
183
|
-
}
|
|
184
|
-
});
|
|
1
|
+
var U=Object.create;var f=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var A=Object.getPrototypeOf,R=Object.prototype.hasOwnProperty;var b=(e,r)=>{for(var t in r)f(e,t,{get:r[t],enumerable:!0})},w=(e,r,t,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of $(r))!R.call(e,n)&&n!==t&&f(e,n,{get:()=>r[n],enumerable:!(i=j(r,n))||i.enumerable});return e};var u=(e,r,t)=>(t=e!=null?U(A(e)):{},w(r||!e||!e.__esModule?f(t,"default",{value:e,enumerable:!0}):t,e)),I=e=>w(f({},"__esModule",{value:!0}),e);var D={};b(D,{profileCommand:()=>c});module.exports=I(D);var k=require("commander"),o=u(require("chalk")),g=require("table"),P=u(require("inquirer"));var m=u(require("conf")),s=new m.default({projectName:"xtra-cli",defaults:{activeProfile:"default",profiles:{default:{apiUrl:process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api"}}}});function a(){return s.get("profiles")||{}}function p(){return process.env.XTRA_PROFILE||s.get("activeProfile")||"default"}function h(e){if(!a()[e])throw new Error(`Profile '${e}' does not exist. Create it first with 'xtra profile create ${e}'.`);s.set("activeProfile",e)}function y(e,r={}){let t=a();if(t[e])throw new Error(`Profile '${e}' already exists. Use 'xtra profile set' to edit it.`);t[e]={apiUrl:process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api",...r},s.set("profiles",t)}function v(e){if(e==="default")throw new Error("Cannot delete the 'default' profile.");let r=a();if(!r[e])throw new Error(`Profile '${e}' not found.`);delete r[e],s.set("profiles",r),p()===e&&s.set("activeProfile","default")}function x(e,r,t){let i=a();if(!i[e])throw new Error(`Profile '${e}' not found.`);i[e][r]=t,s.set("profiles",i)}var d=e=>e?.message||"Unknown error",c=new k.Command("profile").description("Manage named configuration profiles (token, apiUrl, project per profile)").addHelpText("after",`
|
|
2
|
+
Examples:
|
|
3
|
+
$ xtra profile list # Show all profiles
|
|
4
|
+
$ xtra profile create work --url https://app.xtrasecurity.io/api
|
|
5
|
+
$ xtra profile use work # Switch active profile
|
|
6
|
+
$ xtra profile set work --project abc123 # Set a value in a profile
|
|
7
|
+
$ xtra profile delete old-profile # Remove a profile
|
|
8
|
+
$ xtra --profile work secrets list # Use profile for one command
|
|
9
|
+
`);c.command("list").alias("ls").description("List all saved profiles").action(()=>{let e=a(),r=p();if(Object.keys(e).length===0){console.log(o.default.yellow("No profiles found."));return}let t=[[o.default.bold("Profile"),o.default.bold("API URL"),o.default.bold("Project"),o.default.bold("Status")]];for(let[i,n]of Object.entries(e)){let l=i===r;t.push([l?o.default.green(`\u25B6 ${i}`):o.default.white(` ${i}`),o.default.gray(n.apiUrl||"(default)"),o.default.cyan(n.project||"(not set)"),l?o.default.green("active"):o.default.gray("inactive")])}console.log(o.default.bold(`
|
|
10
|
+
Profiles:
|
|
11
|
+
`)),console.log((0,g.table)(t)),console.log(o.default.gray(`Tip: Use XTRA_PROFILE=<name> to override per-command.
|
|
12
|
+
`))});c.command("create <name>").description("Create a new profile").option("--url <apiUrl>","XtraSecurity API URL for this profile").option("--project <projectId>","Default project ID for this profile").option("--token <token>","Auth token for this profile").action(async(e,r)=>{try{let{url:t,project:i,token:n}=r;if(!t&&!i&&!n){let l=await P.default.prompt([{type:"input",name:"url",message:"API URL:",default:"https://www.xtrasecurity.in/api"},{type:"input",name:"project",message:"Default Project ID (optional):",default:""}]);t=l.url,i=l.project||void 0}y(e,{apiUrl:t,project:i||void 0,token:n||void 0}),console.log(o.default.green(`\u2705 Profile '${e}' created!`)),console.log(o.default.gray(` Activate it with: xtra profile use ${e}`))}catch(t){console.error(o.default.red(d(t)))}});c.command("use <name>").description("Switch to a different profile (persists across sessions)").action(e=>{try{h(e),console.log(o.default.green(`\u2705 Switched to profile '${e}'.`)),console.log(o.default.gray(" All future commands will use this profile."))}catch(r){console.error(o.default.red(d(r)))}});c.command("set <name>").description("Update a value in an existing profile").option("--url <apiUrl>","Update the API URL").option("--project <projectId>","Update the default project ID").option("--token <token>","Update the auth token").option("--env <environment>","Update the default environment").action((e,r)=>{let t={url:"apiUrl",project:"project",token:"token",env:"env"},i=[];try{for(let[n,l]of Object.entries(t))r[n]&&(x(e,l,r[n]),i.push(`${l} = ${l==="token"?"****":r[n]}`));if(i.length===0){console.log(o.default.yellow("No values provided. Use --url, --project, --token, or --env."));return}console.log(o.default.green(`\u2705 Profile '${e}' updated:`)),i.forEach(n=>console.log(o.default.gray(` ${n}`)))}catch(n){console.error(o.default.red(d(n)))}});c.command("current").description("Show the currently active profile").action(()=>{let e=p(),t=a()[e]||{};console.log(o.default.bold(`
|
|
13
|
+
Active Profile: ${o.default.green(e)}
|
|
14
|
+
`));let i=[[o.default.bold("Key"),o.default.bold("Value")],["API URL",o.default.cyan(t.apiUrl||"(default)")],["Project",o.default.cyan(t.project||"(not set)")],["Branch",o.default.cyan(t.branch||"(not set)")],["Env",o.default.cyan(t.env||"(not set)")],["Token",t.token?o.default.green("Set \u2713"):o.default.gray("Not set")]];console.log((0,g.table)(i))});c.command("delete <name>").alias("rm").description("Delete a profile").option("-y, --yes","Skip confirmation prompt").action(async(e,r)=>{try{if(!r.yes){let{confirm:t}=await P.default.prompt([{type:"confirm",name:"confirm",message:o.default.red(`Delete profile '${e}'? This cannot be undone.`),default:!1}]);if(!t){console.log(o.default.gray("Cancelled."));return}}v(e),console.log(o.default.green(`\u2705 Profile '${e}' deleted.`))}catch(t){console.error(o.default.red(d(t)))}});0&&(module.exports={profileCommand});
|
package/dist/commands/project.js
CHANGED
|
@@ -1,165 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
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.projectCommand = void 0;
|
|
40
|
-
const commander_1 = require("commander");
|
|
41
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
-
const ora_1 = __importDefault(require("ora"));
|
|
43
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
44
|
-
const config_1 = require("../lib/config");
|
|
45
|
-
const api_1 = require("../lib/api");
|
|
46
|
-
const fs = __importStar(require("fs"));
|
|
47
|
-
const path = __importStar(require("path"));
|
|
48
|
-
exports.projectCommand = new commander_1.Command("project")
|
|
49
|
-
.alias("projects")
|
|
50
|
-
.description("Manage project context");
|
|
51
|
-
const updateConfigs = (projectId, projectName) => {
|
|
52
|
-
const oldProject = (0, config_1.getConfigValue)("project");
|
|
53
|
-
// Always update global
|
|
54
|
-
(0, config_1.setConfig)("project", projectId);
|
|
55
|
-
// Clear branch if project changed
|
|
56
|
-
if (oldProject !== projectId) {
|
|
57
|
-
(0, config_1.setConfig)("branch", "main"); // Reset to main or clear? Projects have different branches.
|
|
58
|
-
}
|
|
59
|
-
// Update/Create local .xtrarc to make project context "sticky" to the directory
|
|
60
|
-
const rcPath = path.join(process.cwd(), ".xtrarc");
|
|
61
|
-
let rc = {};
|
|
62
|
-
if (fs.existsSync(rcPath)) {
|
|
63
|
-
try {
|
|
64
|
-
rc = JSON.parse(fs.readFileSync(rcPath, "utf-8"));
|
|
65
|
-
}
|
|
66
|
-
catch (e) {
|
|
67
|
-
console.error(chalk_1.default.yellow(` ⚠ Could not parse existing local .xtrarc: ${e instanceof Error ? e.message : 'Unknown error'}`));
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
rc.project = projectId;
|
|
71
|
-
if (oldProject !== projectId) {
|
|
72
|
-
rc.branch = "main"; // Reset branch on project change
|
|
73
|
-
}
|
|
74
|
-
// Add other relevant defaults if it's a new file
|
|
75
|
-
if (!rc.env)
|
|
76
|
-
rc.env = "development";
|
|
77
|
-
if (!rc.apiUrl)
|
|
78
|
-
rc.apiUrl = (0, config_1.getConfigValue)("apiUrl") || "https://www.xtrasecurity.in/api";
|
|
79
|
-
try {
|
|
80
|
-
fs.writeFileSync(rcPath, JSON.stringify(rc, null, 2), "utf8");
|
|
81
|
-
console.log(chalk_1.default.gray(` ✔ Updated local .xtrarc (Project context locked to this folder)`));
|
|
82
|
-
}
|
|
83
|
-
catch (e) {
|
|
84
|
-
console.error(chalk_1.default.yellow(` ⚠ Could not write local .xtrarc: ${e instanceof Error ? e.message : 'Unknown error'}`));
|
|
85
|
-
}
|
|
86
|
-
console.log(chalk_1.default.green(`✔ Default project set to '${projectName}'`));
|
|
87
|
-
const currentBranch = (0, config_1.getConfigValue)("branch");
|
|
88
|
-
if (currentBranch) {
|
|
89
|
-
console.log(chalk_1.default.gray(` Active branch: ${currentBranch}`));
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
// SET
|
|
93
|
-
exports.projectCommand
|
|
94
|
-
.command("set")
|
|
95
|
-
.description("Set the default project ID")
|
|
96
|
-
.argument("[projectId]", "Project ID to set as default (optional - will show list if not provided)")
|
|
97
|
-
.action(async (projectId) => {
|
|
98
|
-
// If projectId is provided, set it directly
|
|
99
|
-
if (projectId) {
|
|
100
|
-
updateConfigs(projectId, projectId);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
// Otherwise, fetch projects and show interactive selector
|
|
104
|
-
const spinner = (0, ora_1.default)("Fetching your projects...").start();
|
|
105
|
-
try {
|
|
106
|
-
const projects = await api_1.api.getProjects();
|
|
107
|
-
spinner.stop();
|
|
108
|
-
if (!Array.isArray(projects) || projects.length === 0) {
|
|
109
|
-
console.log(chalk_1.default.yellow("No projects found."));
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
const choices = projects.map((p) => ({
|
|
113
|
-
name: `${p.name} (${p.id})`,
|
|
114
|
-
value: p.id,
|
|
115
|
-
short: p.name
|
|
116
|
-
}));
|
|
117
|
-
const { selectedProject } = await inquirer_1.default.prompt([{
|
|
118
|
-
type: "list",
|
|
119
|
-
name: "selectedProject",
|
|
120
|
-
message: "Select a project:",
|
|
121
|
-
choices
|
|
122
|
-
}]);
|
|
123
|
-
const selectedName = projects.find((p) => p.id === selectedProject)?.name || selectedProject;
|
|
124
|
-
updateConfigs(selectedProject, selectedName);
|
|
125
|
-
}
|
|
126
|
-
catch (error) {
|
|
127
|
-
spinner.fail("Failed to fetch projects");
|
|
128
|
-
if (error.response?.data?.error) {
|
|
129
|
-
console.error(chalk_1.default.red(`Error: ${error.response.data.error}`));
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
console.error(chalk_1.default.red(error.message));
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
// GET (show current)
|
|
137
|
-
exports.projectCommand
|
|
138
|
-
.command("current")
|
|
139
|
-
.description("Show the current default project")
|
|
140
|
-
.action(async () => {
|
|
141
|
-
const projectId = (0, config_1.getConfigValue)("project");
|
|
142
|
-
const branch = (0, config_1.getConfigValue)("branch");
|
|
143
|
-
if (projectId) {
|
|
144
|
-
// Try to fetch project name
|
|
145
|
-
try {
|
|
146
|
-
const projects = await api_1.api.getProjects();
|
|
147
|
-
const project = projects.find((p) => p.id === projectId);
|
|
148
|
-
if (project) {
|
|
149
|
-
console.log(chalk_1.default.cyan(`Project: ${project.name} (${projectId})`));
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
console.log(chalk_1.default.cyan(`Project: ${projectId}`));
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
catch {
|
|
156
|
-
console.log(chalk_1.default.cyan(`Project: ${projectId}`));
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
console.log(chalk_1.default.yellow("No default project set. Use 'xtra project set' to set one."));
|
|
161
|
-
}
|
|
162
|
-
if (branch) {
|
|
163
|
-
console.log(chalk_1.default.cyan(`Branch: ${branch}`));
|
|
164
|
-
}
|
|
165
|
-
});
|
|
1
|
+
var L=Object.create;var u=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var B=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty;var O=(e,t)=>{for(var s in t)u(e,s,{get:t[s],enumerable:!0})},S=(e,t,s,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of _(t))!J.call(e,n)&&n!==s&&u(e,n,{get:()=>t[n],enumerable:!(r=N(t,n))||r.enumerable});return e};var p=(e,t,s)=>(s=e!=null?L(B(e)):{},S(t||!e||!e.__esModule?u(s,"default",{value:e,enumerable:!0}):s,e)),T=e=>S(u({},"__esModule",{value:!0}),e);var D={};O(D,{projectCommand:()=>$});module.exports=T(D);var P=require("commander"),a=p(require("chalk")),I=p(require("ora")),A=p(require("inquirer"));var x=p(require("conf")),f=p(require("fs"));var h="https://www.xtrasecurity.in/api",g=new x.default({projectName:"xtra-cli",defaults:{apiUrl:h}}),m=g.get("apiUrl");m&&!process.env.XTRA_API_URL&&(m.includes("localhost")||m.includes("xtra-security.vercel.app"))&&g.set("apiUrl",h);var y=()=>({...g.store,apiUrl:process.env.XTRA_API_URL||g.get("apiUrl")||h}),l=e=>g.get(e),w=(e,t)=>{if(g.set(e,t),e==="token"&&process.platform!=="win32"&&f.default.existsSync(g.path))try{f.default.chmodSync(g.path,384)}catch{}};var v=()=>g.get("token");var b=p(require("axios"));var k=p(require("fs")),U=p(require("path")),q=JSON.parse(k.readFileSync(U.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:e}=y(),t=v();return b.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":q.version,...t?{Authorization:`Bearer ${t}`}:{}}})},j={login:async(e,t,s)=>(await o().post("/auth/cli-login",{email:e,password:t,apiKey:s})).data,getSecrets:async(e,t,s="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?branch=${s}`)).data,getSecretVersions:async(e,t,s="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(e,t,s,r,n="main")=>(await o().post(`/projects/${e}/envs/${t}/secrets`,{secrets:s,expectedVersions:r,branch:n})).data,getSecretDetails:async(e,t,s,r="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets/${s}?branch=${r}`)).data,syncLogs:async e=>(await o().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,s,r,n,i)=>(await o().post(`/projects/${e}/envs/${t}/secrets/link`,{key:s,sourceProjectId:r,sourceEnv:n,sourceKey:i})).data,rotateSecret:async(e,t,s,r,n)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/rotate`,{strategy:r,parsedNewValue:n})).data,promoteSecret:async(e,t,s)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await o().get("/audit/verify")).data,exportAuditLogs:async(e,t,s,r)=>{let n={format:e};return t&&(n.startDate=t),s&&(n.endDate=s),r&&(n.projectId=r),(await o().post("/audit/export",n,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,s,r)=>(await o().post("/access/request",{projectId:e,secretId:r,reason:t,duration:s})).data,approveAccess:async(e,t)=>(await o().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=y(),s=t?`&workspaceId=${t}`:"";return(await o().get(`/access/list?mode=${e}${s}`)).data},getAccessRequestStatus:async e=>(await o().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=y(),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,s)=>(await o().post("/branch",{projectId:e,name:t,description:s})).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,s)=>(await o().put("/admin/users/role",{email:e,role:t,teamId:s})).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 s=new URLSearchParams(t).toString();return(await o().get(`/projects/${e}/kubernetes?${s}`)).data},getSecretHistory:async(e,t,s)=>(await o().get(`/projects/${e}/envs/${t}/secrets/${s}/history`)).data,rollbackSecret:async(e,t,s,r)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/history`,{version:r})).data,cloneEnvironment:async(e,t,s,r,n)=>(await o().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:s,overwrite:r,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};var d=p(require("fs")),R=p(require("path")),$=new P.Command("project").alias("projects").description("Manage project context"),C=(e,t)=>{let s=l("project");w("project",e),s!==e&&w("branch","main");let r=R.join(process.cwd(),".xtrarc"),n={};if(d.existsSync(r))try{n=JSON.parse(d.readFileSync(r,"utf-8"))}catch(c){console.error(a.default.yellow(` \u26A0 Could not parse existing local .xtrarc: ${c instanceof Error?c.message:"Unknown error"}`))}n.project=e,s!==e&&(n.branch="main"),n.env||(n.env="development"),n.apiUrl||(n.apiUrl=l("apiUrl")||"https://www.xtrasecurity.in/api");try{d.writeFileSync(r,JSON.stringify(n,null,2),"utf8"),console.log(a.default.gray(" \u2714 Updated local .xtrarc (Project context locked to this folder)"))}catch(c){console.error(a.default.yellow(` \u26A0 Could not write local .xtrarc: ${c instanceof Error?c.message:"Unknown error"}`))}console.log(a.default.green(`\u2714 Default project set to '${t}'`));let i=l("branch");i&&console.log(a.default.gray(` Active branch: ${i}`))};$.command("set").description("Set the default project ID").argument("[projectId]","Project ID to set as default (optional - will show list if not provided)").action(async e=>{if(e){C(e,e);return}let t=(0,I.default)("Fetching your projects...").start();try{let s=await j.getProjects();if(t.stop(),!Array.isArray(s)||s.length===0){console.log(a.default.yellow("No projects found."));return}let r=s.map(c=>({name:`${c.name} (${c.id})`,value:c.id,short:c.name})),{selectedProject:n}=await A.default.prompt([{type:"list",name:"selectedProject",message:"Select a project:",choices:r}]),i=s.find(c=>c.id===n)?.name||n;C(n,i)}catch(s){t.fail("Failed to fetch projects"),s.response?.data?.error?console.error(a.default.red(`Error: ${s.response.data.error}`)):console.error(a.default.red(s.message))}});$.command("current").description("Show the current default project").action(async()=>{let e=l("project"),t=l("branch");if(e)try{let r=(await j.getProjects()).find(n=>n.id===e);console.log(r?a.default.cyan(`Project: ${r.name} (${e})`):a.default.cyan(`Project: ${e}`))}catch{console.log(a.default.cyan(`Project: ${e}`))}else console.log(a.default.yellow("No default project set. Use 'xtra project set' to set one."));t&&console.log(a.default.cyan(`Branch: ${t}`))});0&&(module.exports={projectCommand});
|
|
@@ -1,95 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.rollbackCommand = 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 inquirer_1 = __importDefault(require("inquirer"));
|
|
12
|
-
const crypto_1 = require("../lib/crypto");
|
|
13
|
-
const config_1 = require("../lib/config");
|
|
14
|
-
exports.rollbackCommand = new commander_1.Command("rollback")
|
|
15
|
-
.description("Rollback a secret to a previous version")
|
|
16
|
-
.argument("<key>", "Secret Key to rollback")
|
|
17
|
-
.option("-p, --project <projectId>", "Project ID")
|
|
18
|
-
.option("-e, --env <environment>", "Environment (dev, stg, prod)", "dev")
|
|
19
|
-
.action(async (key, options) => {
|
|
20
|
-
let { project, env } = options;
|
|
21
|
-
// Use config fallback
|
|
22
|
-
if (!project)
|
|
23
|
-
project = (0, config_1.getRcConfig)().project;
|
|
24
|
-
if (!project) {
|
|
25
|
-
console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
// Normalize Env
|
|
29
|
-
const envMap = { dev: "development", stg: "staging", prod: "production" };
|
|
30
|
-
env = envMap[env] || env;
|
|
31
|
-
const spinner = (0, ora_1.default)(`Fetching history for ${key}...`).start();
|
|
32
|
-
try {
|
|
33
|
-
const secret = await api_1.api.getSecretDetails(project, env, key);
|
|
34
|
-
spinner.stop();
|
|
35
|
-
if (!secret.history || !Array.isArray(secret.history) || secret.history.length === 0) {
|
|
36
|
-
console.log(chalk_1.default.yellow(`No history found for ${key}. Cannot rollback.`));
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
// Prepare choices
|
|
40
|
-
// Current version
|
|
41
|
-
const choices = [];
|
|
42
|
-
// History versions (reverse order to show latest first)
|
|
43
|
-
const history = [...secret.history].reverse();
|
|
44
|
-
history.forEach((h) => {
|
|
45
|
-
let displayValue = "********";
|
|
46
|
-
try {
|
|
47
|
-
const enc = JSON.parse(h.value[0]);
|
|
48
|
-
displayValue = (0, crypto_1.decrypt)(enc);
|
|
49
|
-
}
|
|
50
|
-
catch (e) {
|
|
51
|
-
displayValue = h.value[0];
|
|
52
|
-
}
|
|
53
|
-
choices.push({
|
|
54
|
-
name: `v${h.version} - ${new Date(h.updatedAt).toLocaleString()} (Value: ${displayValue.substring(0, 10)}...)`,
|
|
55
|
-
value: h
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
const { selectedVersion } = await inquirer_1.default.prompt([
|
|
59
|
-
{
|
|
60
|
-
type: "list",
|
|
61
|
-
name: "selectedVersion",
|
|
62
|
-
message: "Select version to restore:",
|
|
63
|
-
choices: choices
|
|
64
|
-
}
|
|
65
|
-
]);
|
|
66
|
-
// Restore
|
|
67
|
-
const confirmSpinner = (0, ora_1.default)(`Restoring version v${selectedVersion.version}...`).start();
|
|
68
|
-
// We use setSecrets to update it to the old value.
|
|
69
|
-
// This will create a NEW version in history (preserving the timeline).
|
|
70
|
-
let restoredValue = selectedVersion.value[0];
|
|
71
|
-
try {
|
|
72
|
-
const enc = JSON.parse(restoredValue);
|
|
73
|
-
restoredValue = (0, crypto_1.decrypt)(enc);
|
|
74
|
-
}
|
|
75
|
-
catch (e) { }
|
|
76
|
-
const payload = { [key]: restoredValue };
|
|
77
|
-
await api_1.api.setSecrets(project, env, payload);
|
|
78
|
-
confirmSpinner.succeed(`Successfully rolled back ${key} to v${selectedVersion.version}`);
|
|
79
|
-
// Log Audit
|
|
80
|
-
try {
|
|
81
|
-
const { logAudit } = require("../lib/audit");
|
|
82
|
-
logAudit("SECRET_ROLLBACK", project, env, { key, version: selectedVersion.version });
|
|
83
|
-
}
|
|
84
|
-
catch (e) { }
|
|
85
|
-
}
|
|
86
|
-
catch (error) {
|
|
87
|
-
spinner.fail("Failed to rollback.");
|
|
88
|
-
if (error.response && error.response.status === 404) {
|
|
89
|
-
console.error(chalk_1.default.red("Secret not found."));
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
console.error(chalk_1.default.red(error.message));
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
});
|
|
1
|
+
var it=Object.create;var w=Object.defineProperty;var ct=Object.getOwnPropertyDescriptor;var pt=Object.getOwnPropertyNames;var gt=Object.getPrototypeOf,dt=Object.prototype.hasOwnProperty;var S=(t,e)=>()=>(t&&(e=t(t=0)),e);var P=(t,e)=>{for(var s in e)w(t,s,{get:e[s],enumerable:!0})},T=(t,e,s,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of pt(e))!dt.call(t,n)&&n!==s&&w(t,n,{get:()=>e[n],enumerable:!(r=ct(e,n))||r.enumerable});return t};var i=(t,e,s)=>(s=t!=null?it(gt(t)):{},T(e||!t||!t.__esModule?w(s,"default",{value:t,enumerable:!0}):s,t)),_=t=>T(w({},"__esModule",{value:!0}),t);var J,C,F,R,g,A,l,V,q,j=S(()=>{J=i(require("conf")),C=i(require("fs")),F=i(require("path")),R="https://www.xtrasecurity.in/api",g=new J.default({projectName:"xtra-cli",defaults:{apiUrl:R}}),A=g.get("apiUrl");A&&!process.env.XTRA_API_URL&&(A.includes("localhost")||A.includes("xtra-security.vercel.app"))&&g.set("apiUrl",R);l=()=>({...g.store,apiUrl:process.env.XTRA_API_URL||g.get("apiUrl")||R}),V=()=>g.get("token"),q=()=>{let t={};try{let e=F.default.join(process.cwd(),".xtrarc");C.default.existsSync(e)&&(t=JSON.parse(C.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 G,K,z,ut,o,m,L=S(()=>{G=i(require("axios"));j();K=i(require("fs")),z=i(require("path")),ut=JSON.parse(K.readFileSync(z.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:t}=l(),e=V();return G.default.create({baseURL:t,headers:{"Content-Type":"application/json","X-CLI-Version":ut.version,...e?{Authorization:`Bearer ${e}`}:{}}})},m={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,a)=>(await o().post(`/projects/${t}/envs/${e}/secrets/link`,{key:s,sourceProjectId:r,sourceEnv:n,sourceKey:a})).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}=l(),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}=l(),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 W(){let t=H.get("masterKey");t||(t=y.default.randomBytes(32).toString("hex"),H.set("masterKey",t));let e=(0,X.machineIdSync)();return y.default.createHash("sha256").update(t+e).digest()}var y,M,X,Q,H,Y,h,U=S(()=>{y=i(require("crypto")),M=i(require("conf")),X=require("node-machine-id"),Q="aes-256-gcm",H=new M.default({projectName:"xtra-cli-crypto"});Y=t=>{let e=y.default.randomBytes(16),s=W(),r=y.default.createCipheriv(Q,s,e),n=r.update(t,"utf8","hex");n+=r.final("hex");let a=r.getAuthTag();return{iv:e.toString("hex"),content:n,tag:a.toString("hex")}},h=t=>{let e=W(),s=Buffer.from(t.iv,"hex"),r=Buffer.from(t.tag,"hex"),n=y.default.createDecipheriv(Q,e,s);n.setAuthTag(r);let a=n.update(t.content,"hex","utf8");return a+=n.final("utf8"),a}});var et={};P(et,{loadAuditLogs:()=>x,logAudit:()=>yt,saveAuditLogs:()=>$,syncAuditLogs:()=>ft});function lt(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 tt(t){try{if(!l().token)return!1;let s={action:t.action,entity:"cli-project",entityId:t.projectId||"global",details:lt(t.details),timestamp:t.timestamp,projectId:t.projectId};return await m.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 yt(t,e,s,r){let n={id:(0,Z.v4)(),timestamp:new Date().toISOString(),action:t,projectId:e,environment:s,details:r,synced:!1},a=x();a.push(n),$(a),tt(n).then(c=>{if(c){n.synced=!0;let d=x(),b=d.findIndex(u=>u.id===n.id);b!==-1&&(d[b].synced=!0,$(d))}}).catch(()=>{})}function x(){if(!f.default.existsSync(B))return[];try{let t=f.default.readFileSync(B,"utf-8"),e=JSON.parse(t),s=h(e);return JSON.parse(s)}catch{return[]}}function $(t){f.default.existsSync(O)||f.default.mkdirSync(O,{recursive:!0});let e=JSON.stringify(t),s=Y(e);f.default.writeFileSync(B,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 tt(r)&&(r.synced=!0,s++);return s>0&&$(t),s}var f,D,Z,O,B,st=S(()=>{f=i(require("fs")),D=i(require("path")),Z=require("uuid");U();j();L();O=D.default.join(process.cwd(),".xtra"),B=D.default.join(O,"audit.enc")});var ht={};P(ht,{rollbackCommand:()=>mt});module.exports=_(ht);var rt=require("commander");L();var v=i(require("chalk")),E=i(require("ora")),nt=i(require("inquirer"));U();j();var mt=new rt.Command("rollback").description("Rollback a secret to a previous version").argument("<key>","Secret Key to rollback").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (dev, stg, prod)","dev").action(async(t,e)=>{let{project:s,env:r}=e;s||(s=q().project),s||(console.error(v.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1)),r={dev:"development",stg:"staging",prod:"production"}[r]||r;let a=(0,E.default)(`Fetching history for ${t}...`).start();try{let c=await m.getSecretDetails(s,r,t);if(a.stop(),!c.history||!Array.isArray(c.history)||c.history.length===0){console.log(v.default.yellow(`No history found for ${t}. Cannot rollback.`));return}let d=[];[...c.history].reverse().forEach(p=>{let I="********";try{let N=JSON.parse(p.value[0]);I=h(N)}catch{I=p.value[0]}d.push({name:`v${p.version} - ${new Date(p.updatedAt).toLocaleString()} (Value: ${I.substring(0,10)}...)`,value:p})});let{selectedVersion:u}=await nt.default.prompt([{type:"list",name:"selectedVersion",message:"Select version to restore:",choices:d}]),ot=(0,E.default)(`Restoring version v${u.version}...`).start(),k=u.value[0];try{let p=JSON.parse(k);k=h(p)}catch{}let at={[t]:k};await m.setSecrets(s,r,at),ot.succeed(`Successfully rolled back ${t} to v${u.version}`);try{let{logAudit:p}=(st(),_(et));p("SECRET_ROLLBACK",s,r,{key:t,version:u.version})}catch{}}catch(c){a.fail("Failed to rollback."),c.response&&c.response.status===404?console.error(v.default.red("Secret not found.")):console.error(v.default.red(c.message))}});0&&(module.exports={rollbackCommand});
|
package/dist/commands/rotate.js
CHANGED
|
@@ -1,93 +1,3 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.rotateCommand = void 0;
|
|
7
|
-
const commander_1 = require("commander");
|
|
8
|
-
const api_1 = require("../lib/api");
|
|
9
|
-
const config_1 = require("../lib/config");
|
|
10
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
-
const ora_1 = __importDefault(require("ora"));
|
|
12
|
-
exports.rotateCommand = new commander_1.Command("rotate")
|
|
13
|
-
.description("Rotate a secret (Zero-Downtime Shadow Mode)")
|
|
14
|
-
.argument("<key>", "Key of the secret to rotate")
|
|
15
|
-
.option("-p, --project <projectId>", "Project ID")
|
|
16
|
-
.option("-e, --env <environment>", "Environment", "development")
|
|
17
|
-
.option("--strategy <strategy>", "Rotation strategy", "shadow")
|
|
18
|
-
.option("--promote", "Promote the shadow value to active", false)
|
|
19
|
-
.option("--value <value>", "New value for the secret (optional)")
|
|
20
|
-
.action(async (key, options) => {
|
|
21
|
-
let { project, env, strategy, promote, value } = options;
|
|
22
|
-
// Use config fallback
|
|
23
|
-
if (!project)
|
|
24
|
-
project = (0, config_1.getRcConfig)().project;
|
|
25
|
-
// Normalize Env
|
|
26
|
-
const envMap = { dev: "development", stg: "staging", prod: "production" };
|
|
27
|
-
env = envMap[env] || env;
|
|
28
|
-
if (!project) {
|
|
29
|
-
console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or checkout a branch."));
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
// Production confirmation gate
|
|
33
|
-
if (env === "production") {
|
|
34
|
-
const inquirer = require("inquirer");
|
|
35
|
-
const { confirmProd } = await inquirer.prompt([{
|
|
36
|
-
type: "confirm",
|
|
37
|
-
name: "confirmProd",
|
|
38
|
-
message: chalk_1.default.red(`âš You are about to rotate a secret in PRODUCTION (${key}). Proceed?`),
|
|
39
|
-
default: false,
|
|
40
|
-
}]);
|
|
41
|
-
if (!confirmProd) {
|
|
42
|
-
console.log(chalk_1.default.yellow("Aborted."));
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
if (promote) {
|
|
47
|
-
// PROMOTE FLOW
|
|
48
|
-
const spinner = (0, ora_1.default)(`Promoting secret '${key}' in ${env}...`).start();
|
|
49
|
-
try {
|
|
50
|
-
const result = await api_1.api.promoteSecret(project, env, key);
|
|
51
|
-
spinner.succeed(chalk_1.default.green(`Successfully promoted '${key}' to version ${result.version}`));
|
|
52
|
-
try {
|
|
53
|
-
const { logAudit } = require("../lib/audit");
|
|
54
|
-
logAudit("SECRET_PROMOTE", project, env, { key, newVersion: result.version });
|
|
55
|
-
}
|
|
56
|
-
catch (e) { }
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
spinner.fail("Promotion failed");
|
|
60
|
-
if (error.response && error.response.data && error.response.data.error) {
|
|
61
|
-
console.error(chalk_1.default.red(`Server Error: ${error.response.data.error}`));
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
console.error(chalk_1.default.red(error.message));
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
// ROTATE FLOW
|
|
70
|
-
const spinner = (0, ora_1.default)(`Starting rotation for '${key}' (Strategy: ${strategy})...`).start();
|
|
71
|
-
try {
|
|
72
|
-
const result = await api_1.api.rotateSecret(project, env, key, strategy, value);
|
|
73
|
-
spinner.succeed(chalk_1.default.green(`Rotation initiated for '${key}'`));
|
|
74
|
-
console.log(chalk_1.default.dim(`Shadow Value: ${result.shadowValue}`));
|
|
75
|
-
console.log(chalk_1.default.dim(`Expires At: ${result.expiresAt}`));
|
|
76
|
-
console.log(chalk_1.default.blue(`\nTo verify, run your app. To finalize, run:\nxtra rotate ${key} --promote -p ${project} -e ${env}`));
|
|
77
|
-
try {
|
|
78
|
-
const { logAudit } = require("../lib/audit");
|
|
79
|
-
logAudit("SECRET_ROTATE", project, env, { key, strategy, expiresAt: result.expiresAt });
|
|
80
|
-
}
|
|
81
|
-
catch (e) { }
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
spinner.fail("Rotation failed");
|
|
85
|
-
if (error.response && error.response.data && error.response.data.error) {
|
|
86
|
-
console.error(chalk_1.default.red(`Server Error: ${error.response.data.error}`));
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
console.error(chalk_1.default.red(error.message));
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
});
|
|
1
|
+
var et=Object.create;var w=Object.defineProperty;var rt=Object.getOwnPropertyDescriptor;var st=Object.getOwnPropertyNames;var nt=Object.getPrototypeOf,ot=Object.prototype.hasOwnProperty;var S=(t,e)=>()=>(t&&(e=t(t=0)),e);var L=(t,e)=>{for(var r in e)w(t,r,{get:e[r],enumerable:!0})},D=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of st(e))!ot.call(t,n)&&n!==r&&w(t,n,{get:()=>e[n],enumerable:!(s=rt(e,n))||s.enumerable});return t};var p=(t,e,r)=>(r=t!=null?et(nt(t)):{},D(e||!t||!t.__esModule?w(r,"default",{value:t,enumerable:!0}):r,t)),b=t=>D(w({},"__esModule",{value:!0}),t);var B,I,N,k,d,A,u,_,q,x=S(()=>{B=p(require("conf")),I=p(require("fs")),N=p(require("path")),k="https://www.xtrasecurity.in/api",d=new B.default({projectName:"xtra-cli",defaults:{apiUrl:k}}),A=d.get("apiUrl");A&&!process.env.XTRA_API_URL&&(A.includes("localhost")||A.includes("xtra-security.vercel.app"))&&d.set("apiUrl",k);u=()=>({...d.store,apiUrl:process.env.XTRA_API_URL||d.get("apiUrl")||k}),_=()=>d.get("token"),q=()=>{let t={};try{let e=N.default.join(process.cwd(),".xtrarc");I.default.existsSync(e)&&(t=JSON.parse(I.default.readFileSync(e,"utf-8")))}catch{}return{project:t.project||d.get("project")||"",env:t.env||d.get("env")||"development",branch:t.branch||d.get("branch")||"main"}}});var J,V,F,at,o,y,C=S(()=>{J=p(require("axios"));x();V=p(require("fs")),F=p(require("path")),at=JSON.parse(V.readFileSync(F.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:t}=u(),e=_();return J.default.create({baseURL:t,headers:{"Content-Type":"application/json","X-CLI-Version":at.version,...e?{Authorization:`Bearer ${e}`}:{}}})},y={login:async(t,e,r)=>(await o().post("/auth/cli-login",{email:t,password:e,apiKey:r})).data,getSecrets:async(t,e,r="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets?branch=${r}`)).data,getSecretVersions:async(t,e,r="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets?includeVersions=true&branch=${r}`)).data,setSecrets:async(t,e,r,s,n="main")=>(await o().post(`/projects/${t}/envs/${e}/secrets`,{secrets:r,expectedVersions:s,branch:n})).data,getSecretDetails:async(t,e,r,s="main")=>(await o().get(`/projects/${t}/envs/${e}/secrets/${r}?branch=${s}`)).data,syncLogs:async t=>(await o().post("/audit/cli-logs",{logs:t})).data,linkSecret:async(t,e,r,s,n,i)=>(await o().post(`/projects/${t}/envs/${e}/secrets/link`,{key:r,sourceProjectId:s,sourceEnv:n,sourceKey:i})).data,rotateSecret:async(t,e,r,s,n)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${r}/rotate`,{strategy:s,parsedNewValue:n})).data,promoteSecret:async(t,e,r)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${r}/promote`,{})).data,verifyAuditLogs:async()=>(await o().get("/audit/verify")).data,exportAuditLogs:async(t,e,r,s)=>{let n={format:t};return e&&(n.startDate=e),r&&(n.endDate=r),s&&(n.projectId=s),(await o().post("/audit/export",n,{responseType:t==="csv"?"text":"json"})).data},requestAccess:async(t,e,r,s)=>(await o().post("/access/request",{projectId:t,secretId:s,reason:e,duration:r})).data,approveAccess:async(t,e)=>(await o().post("/access/approve",{requestId:t,decision:e})).data,listAccessRequests:async t=>{let{workspace:e}=u(),r=e?`&workspaceId=${e}`:"";return(await o().get(`/access/list?mode=${t}${r}`)).data},getAccessRequestStatus:async t=>(await o().get(`/access/request?id=${t}`)).data,getProjects:async()=>{let{workspace:t}=u(),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,r)=>(await o().post("/branch",{projectId:t,name:e,description:r})).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,r)=>(await o().put("/admin/users/role",{email:t,role:e,teamId:r})).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 r=new URLSearchParams(e).toString();return(await o().get(`/projects/${t}/kubernetes?${r}`)).data},getSecretHistory:async(t,e,r)=>(await o().get(`/projects/${t}/envs/${e}/secrets/${r}/history`)).data,rollbackSecret:async(t,e,r,s)=>(await o().post(`/projects/${t}/envs/${e}/secrets/${r}/history`,{version:s})).data,cloneEnvironment:async(t,e,r,s,n)=>(await o().post(`/projects/${t}/envs/clone`,{fromEnv:e,toEnv:r,overwrite:s,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,M.machineIdSync)();return l.default.createHash("sha256").update(t+e).digest()}var l,K,M,z,G,X,Y,Z=S(()=>{l=p(require("crypto")),K=p(require("conf")),M=require("node-machine-id"),z="aes-256-gcm",G=new K.default({projectName:"xtra-cli-crypto"});X=t=>{let e=l.default.randomBytes(16),r=H(),s=l.default.createCipheriv(z,r,e),n=s.update(t,"utf8","hex");n+=s.final("hex");let i=s.getAuthTag();return{iv:e.toString("hex"),content:n,tag:i.toString("hex")}},Y=t=>{let e=H(),r=Buffer.from(t.iv,"hex"),s=Buffer.from(t.tag,"hex"),n=l.default.createDecipheriv(z,e,r);n.setAuthTag(s);let i=n.update(t.content,"hex","utf8");return i+=n.final("utf8"),i}});var T={};L(T,{loadAuditLogs:()=>$,logAudit:()=>ct,saveAuditLogs:()=>j,syncAuditLogs:()=>pt});function it(t){if(!t||typeof t!="object")return t;let e={...t},r=["password","token","secret","value","apiKey","secretValue"];for(let s of Object.keys(e))r.some(n=>s.toLowerCase().includes(n))&&(e[s]="[REDACTED]");return e}async function W(t){try{if(!u().token)return!1;let r={action:t.action,entity:"cli-project",entityId:t.projectId||"global",details:it(t.details),timestamp:t.timestamp,projectId:t.projectId};return await y.post("/audit/cli-logs",{logs:[r]}),!0}catch(e){return process.env.DEBUG&&console.error("Failed to send audit log to backend:",e),!1}}function ct(t,e,r,s){let n={id:(0,Q.v4)(),timestamp:new Date().toISOString(),action:t,projectId:e,environment:r,details:s,synced:!1},i=$();i.push(n),j(i),W(n).then(h=>{if(h){n.synced=!0;let v=$(),g=v.findIndex(a=>a.id===n.id);g!==-1&&(v[g].synced=!0,j(v))}}).catch(()=>{})}function $(){if(!f.default.existsSync(P))return[];try{let t=f.default.readFileSync(P,"utf-8"),e=JSON.parse(t),r=Y(e);return JSON.parse(r)}catch{return[]}}function j(t){f.default.existsSync(R)||f.default.mkdirSync(R,{recursive:!0});let e=JSON.stringify(t),r=X(e);f.default.writeFileSync(P,JSON.stringify(r),"utf-8")}async function pt(){let t=$(),e=t.filter(s=>!s.synced);if(e.length===0)return 0;let r=0;for(let s of e)await W(s)&&(s.synced=!0,r++);return r>0&&j(t),r}var f,E,Q,R,P,U=S(()=>{f=p(require("fs")),E=p(require("path")),Q=require("uuid");Z();x();C();R=E.default.join(process.cwd(),".xtra"),P=E.default.join(R,"audit.enc")});var dt={};L(dt,{rotateCommand:()=>gt});module.exports=b(dt);var tt=require("commander");C();x();var c=p(require("chalk")),O=p(require("ora")),gt=new tt.Command("rotate").description("Rotate a secret (Zero-Downtime Shadow Mode)").argument("<key>","Key of the secret to rotate").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment","development").option("--strategy <strategy>","Rotation strategy","shadow").option("--promote","Promote the shadow value to active",!1).option("--value <value>","New value for the secret (optional)").action(async(t,e)=>{let{project:r,env:s,strategy:n,promote:i,value:h}=e;if(r||(r=q().project),s={dev:"development",stg:"staging",prod:"production"}[s]||s,r||(console.error(c.default.red("Error: Project ID is required. Use -p <id> or checkout a branch.")),process.exit(1)),s==="production"){let g=require("inquirer"),{confirmProd:a}=await g.prompt([{type:"confirm",name:"confirmProd",message:c.default.red(`\u26A0 You are about to rotate a secret in PRODUCTION (${t}). Proceed?`),default:!1}]);if(!a){console.log(c.default.yellow("Aborted."));return}}if(i){let g=(0,O.default)(`Promoting secret '${t}' in ${s}...`).start();try{let a=await y.promoteSecret(r,s,t);g.succeed(c.default.green(`Successfully promoted '${t}' to version ${a.version}`));try{let{logAudit:m}=(U(),b(T));m("SECRET_PROMOTE",r,s,{key:t,newVersion:a.version})}catch{}}catch(a){g.fail("Promotion failed"),a.response&&a.response.data&&a.response.data.error?console.error(c.default.red(`Server Error: ${a.response.data.error}`)):console.error(c.default.red(a.message))}}else{let g=(0,O.default)(`Starting rotation for '${t}' (Strategy: ${n})...`).start();try{let a=await y.rotateSecret(r,s,t,n,h);g.succeed(c.default.green(`Rotation initiated for '${t}'`)),console.log(c.default.dim(`Shadow Value: ${a.shadowValue}`)),console.log(c.default.dim(`Expires At: ${a.expiresAt}`)),console.log(c.default.blue(`
|
|
2
|
+
To verify, run your app. To finalize, run:
|
|
3
|
+
xtra rotate ${t} --promote -p ${r} -e ${s}`));try{let{logAudit:m}=(U(),b(T));m("SECRET_ROTATE",r,s,{key:t,strategy:n,expiresAt:a.expiresAt})}catch{}}catch(a){g.fail("Rotation failed"),a.response&&a.response.data&&a.response.data.error?console.error(c.default.red(`Server Error: ${a.response.data.error}`)):console.error(c.default.red(a.message))}}});0&&(module.exports={rotateCommand});
|