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.
Files changed (44) hide show
  1. package/dist/bin/xtra.js +3 -137
  2. package/dist/commands/access.js +1 -184
  3. package/dist/commands/admin.js +5 -118
  4. package/dist/commands/audit.js +1 -67
  5. package/dist/commands/branch.js +1 -212
  6. package/dist/commands/checkout.js +1 -80
  7. package/dist/commands/ci.js +31 -341
  8. package/dist/commands/completion.js +85 -224
  9. package/dist/commands/diff.js +6 -162
  10. package/dist/commands/doctor.js +2 -164
  11. package/dist/commands/env.js +2 -70
  12. package/dist/commands/export.js +2 -83
  13. package/dist/commands/generate.js +3 -179
  14. package/dist/commands/history.js +4 -77
  15. package/dist/commands/import.js +1 -121
  16. package/dist/commands/init.js +7 -205
  17. package/dist/commands/integration.js +11 -188
  18. package/dist/commands/jit-run.js +5 -0
  19. package/dist/commands/local.js +16 -198
  20. package/dist/commands/login.js +16 -198
  21. package/dist/commands/login.test.js +16 -51
  22. package/dist/commands/logout.js +1 -0
  23. package/dist/commands/logs.js +3 -121
  24. package/dist/commands/profile.js +14 -184
  25. package/dist/commands/project.js +1 -165
  26. package/dist/commands/rollback.js +1 -95
  27. package/dist/commands/rotate.js +3 -93
  28. package/dist/commands/run.js +11 -215
  29. package/dist/commands/scan.js +25 -127
  30. package/dist/commands/secrets.js +14 -305
  31. package/dist/commands/simulate.js +6 -109
  32. package/dist/commands/status.js +2 -93
  33. package/dist/commands/template.js +22 -276
  34. package/dist/commands/ui.js +2 -289
  35. package/dist/commands/watch.js +17 -123
  36. package/dist/lib/api.js +1 -187
  37. package/dist/lib/api.test.js +1 -89
  38. package/dist/lib/audit.js +1 -136
  39. package/dist/lib/config.js +1 -72
  40. package/dist/lib/config.test.js +1 -47
  41. package/dist/lib/crypto.js +1 -50
  42. package/dist/lib/manifest.js +1 -52
  43. package/dist/lib/profiles.js +1 -103
  44. package/package.json +7 -2
@@ -1,276 +1,22 @@
1
- "use strict";
2
- /**
3
- * template.ts — Secret Templating Engine for xtra-cli
4
- *
5
- * Reads a template file containing {{ secrets.KEY }} placeholders,
6
- * fetches secrets from XtraSecurity Cloud, substitutes all placeholders,
7
- * and writes the rendered output to the specified file (or stdout).
8
- *
9
- * Supported placeholder syntaxes:
10
- * {{ secrets.DATABASE_URL }} → fetches DATABASE_URL from secrets
11
- * {{ secrets.PORT | 3000 }} → with fallback default value "3000"
12
- * {{ env.NODE_ENV }} → reads from local process environment
13
- *
14
- * Example:
15
- * # config.yaml.tpl
16
- * database:
17
- * url: {{ secrets.DATABASE_URL }}
18
- * pool: {{ secrets.DB_POOL_SIZE | 10 }}
19
- * app:
20
- * port: {{ env.PORT | 3000 }}
21
- * env: {{ env.NODE_ENV | development }}
22
- *
23
- * Usage:
24
- * xtra template render config.yaml.tpl -o config.yaml -p proj123 -e production
25
- * xtra template render nginx.conf.tpl | tee /etc/nginx/nginx.conf
26
- * xtra template check config.yaml.tpl -p proj123 -e production
27
- */
28
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
29
- if (k2 === undefined) k2 = k;
30
- var desc = Object.getOwnPropertyDescriptor(m, k);
31
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
32
- desc = { enumerable: true, get: function() { return m[k]; } };
33
- }
34
- Object.defineProperty(o, k2, desc);
35
- }) : (function(o, m, k, k2) {
36
- if (k2 === undefined) k2 = k;
37
- o[k2] = m[k];
38
- }));
39
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
40
- Object.defineProperty(o, "default", { enumerable: true, value: v });
41
- }) : function(o, v) {
42
- o["default"] = v;
43
- });
44
- var __importStar = (this && this.__importStar) || (function () {
45
- var ownKeys = function(o) {
46
- ownKeys = Object.getOwnPropertyNames || function (o) {
47
- var ar = [];
48
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
49
- return ar;
50
- };
51
- return ownKeys(o);
52
- };
53
- return function (mod) {
54
- if (mod && mod.__esModule) return mod;
55
- var result = {};
56
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
57
- __setModuleDefault(result, mod);
58
- return result;
59
- };
60
- })();
61
- var __importDefault = (this && this.__importDefault) || function (mod) {
62
- return (mod && mod.__esModule) ? mod : { "default": mod };
63
- };
64
- Object.defineProperty(exports, "__esModule", { value: true });
65
- exports.templateCommand = void 0;
66
- const commander_1 = require("commander");
67
- const api_1 = require("../lib/api");
68
- const config_1 = require("../lib/config");
69
- const chalk_1 = __importDefault(require("chalk"));
70
- const ora_1 = __importDefault(require("ora"));
71
- const fs = __importStar(require("fs"));
72
- const path = __importStar(require("path"));
73
- // ─── Placeholder Regex ────────────────────────────────────────────────────────
74
- // Matches: {{ secrets.KEY }}, {{ secrets.KEY | default }}, {{ env.KEY | default }}
75
- const PLACEHOLDER_RE = /\{\{\s*(secrets|env)\.([A-Z0-9_a-z]+)(?:\s*\|\s*([^}]*?))?\s*\}\}/g;
76
- function renderTemplate(template, secrets, processEnv) {
77
- const missingKeys = [];
78
- const usedDefaults = [];
79
- let replacedCount = 0;
80
- const output = template.replace(PLACEHOLDER_RE, (_, source, key, defaultVal) => {
81
- let value;
82
- if (source === "secrets") {
83
- value = secrets[key];
84
- }
85
- else if (source === "env") {
86
- value = processEnv[key];
87
- }
88
- if (value !== undefined && value !== "") {
89
- replacedCount++;
90
- return value;
91
- }
92
- if (defaultVal !== undefined) {
93
- const trimmedDefault = defaultVal.trim();
94
- usedDefaults.push(`${source}.${key} → "${trimmedDefault}"`);
95
- replacedCount++;
96
- return trimmedDefault;
97
- }
98
- missingKeys.push(`${source}.${key}`);
99
- return `{{ ${source}.${key} }}`; // Leave unreplaced — user will see it clearly
100
- });
101
- return { output, replacedCount, missingKeys, usedDefaults };
102
- }
103
- // ─── Command ──────────────────────────────────────────────────────────────────
104
- exports.templateCommand = new commander_1.Command("template")
105
- .description("Secret templating engine — inject secrets into config file templates")
106
- .addHelpText("after", `
107
- Template Syntax:
108
- {{ secrets.KEY }} Replace with secret value
109
- {{ secrets.KEY | default }} Replace with secret value, fall back to "default"
110
- {{ env.KEY }} Replace with local environment variable
111
- {{ env.KEY | default }} Replace with local env variable, fall back to "default"
112
-
113
- Examples:
114
- $ xtra template render config.yaml.tpl -p proj123 -e production -o config.yaml
115
- $ xtra template render nginx.conf.tpl -p proj123 -e production | sudo tee /etc/nginx/nginx.conf
116
- $ xtra template check config.yaml.tpl -p proj123 -e production
117
- $ xtra template list config.yaml.tpl
118
- `);
119
- // ── render ────────────────────────────────────────────────────────────────────
120
- exports.templateCommand
121
- .command("render <templateFile>")
122
- .description("Render a template file by injecting secrets and environment variables")
123
- .option("-p, --project <id>", "Project ID")
124
- .option("-e, --env <environment>", "Environment (default: development)", "development")
125
- .option("-b, --branch <branch>", "Branch name", "main")
126
- .option("-o, --output <file>", "Output file (default: stdout)")
127
- .option("--strict", "Exit with error if any placeholder is unresolved", false)
128
- .action(async (templateFile, options) => {
129
- let { project, env, branch, output, strict } = options;
130
- // Normalize env shorthand
131
- const envMap = { dev: "development", stg: "staging", prod: "production" };
132
- env = envMap[env] || env;
133
- if (!project)
134
- project = (0, config_1.getRcConfig)().project;
135
- if (!project) {
136
- console.error(chalk_1.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'."));
137
- process.exit(1);
138
- }
139
- // Read template file
140
- const templatePath = path.resolve(process.cwd(), templateFile);
141
- if (!fs.existsSync(templatePath)) {
142
- console.error(chalk_1.default.red(`Error: Template file not found: ${templatePath}`));
143
- process.exit(1);
144
- }
145
- const template = fs.readFileSync(templatePath, "utf8");
146
- // Count how many placeholders exist
147
- const allPlaceholders = [...template.matchAll(PLACEHOLDER_RE)];
148
- if (allPlaceholders.length === 0) {
149
- console.warn(chalk_1.default.yellow("Warning: No {{ secrets.* }} or {{ env.* }} placeholders found in template."));
150
- }
151
- const spinner = (0, ora_1.default)(`Fetching secrets for ${project}/${env}...`).start();
152
- let secrets = {};
153
- try {
154
- secrets = await api_1.api.getSecrets(project, env, branch);
155
- spinner.succeed(`Loaded ${Object.keys(secrets).length} secrets.`);
156
- }
157
- catch (e) {
158
- spinner.fail("Failed to fetch secrets.");
159
- const safeErr = e?.response?.data?.error || e.message;
160
- console.error(chalk_1.default.red(safeErr));
161
- process.exit(1);
162
- }
163
- // Render
164
- const { output: rendered, replacedCount, missingKeys, usedDefaults } = renderTemplate(template, secrets, process.env);
165
- // Report
166
- console.log(chalk_1.default.green(`✅ Replaced ${replacedCount} placeholder(s).`));
167
- if (usedDefaults.length > 0) {
168
- console.log(chalk_1.default.yellow(`âš  Used defaults for: ${usedDefaults.join(", ")}`));
169
- }
170
- if (missingKeys.length > 0) {
171
- console.log(chalk_1.default.red(`✗ Unresolved placeholders: ${missingKeys.join(", ")}`));
172
- if (strict) {
173
- console.error(chalk_1.default.red("Aborting due to --strict mode."));
174
- process.exit(1);
175
- }
176
- }
177
- // Write output
178
- if (output) {
179
- const outPath = path.resolve(process.cwd(), output);
180
- fs.writeFileSync(outPath, rendered, "utf8");
181
- console.log(chalk_1.default.blue(`→ Written to: ${outPath}`));
182
- }
183
- else {
184
- // Print to stdout (so user can pipe it)
185
- process.stdout.write(rendered);
186
- }
187
- // Audit log
188
- try {
189
- const { logAudit } = require("../lib/audit");
190
- logAudit("TEMPLATE_RENDERED", project, env, {
191
- template: templateFile,
192
- output: output || "stdout",
193
- replacedCount,
194
- missingKeys,
195
- });
196
- }
197
- catch (e) { }
198
- });
199
- // ── check ─────────────────────────────────────────────────────────────────────
200
- exports.templateCommand
201
- .command("check <templateFile>")
202
- .description("Validate that all template placeholders have matching secrets (dry-run)")
203
- .option("-p, --project <id>", "Project ID")
204
- .option("-e, --env <environment>", "Environment", "development")
205
- .option("-b, --branch <branch>", "Branch name", "main")
206
- .action(async (templateFile, options) => {
207
- let { project, env, branch } = options;
208
- const envMap = { dev: "development", stg: "staging", prod: "production" };
209
- env = envMap[env] || env;
210
- if (!project)
211
- project = (0, config_1.getRcConfig)().project;
212
- if (!project) {
213
- console.error(chalk_1.default.red("Error: Project ID required."));
214
- process.exit(1);
215
- }
216
- const templatePath = path.resolve(process.cwd(), templateFile);
217
- if (!fs.existsSync(templatePath)) {
218
- console.error(chalk_1.default.red(`Template file not found: ${templatePath}`));
219
- process.exit(1);
220
- }
221
- const template = fs.readFileSync(templatePath, "utf8");
222
- const spinner = (0, ora_1.default)(`Fetching secrets for check...`).start();
223
- let secrets = {};
224
- try {
225
- secrets = await api_1.api.getSecrets(project, env, branch);
226
- spinner.stop();
227
- }
228
- catch (e) {
229
- spinner.fail("Failed to fetch secrets.");
230
- process.exit(1);
231
- }
232
- const { replacedCount, missingKeys, usedDefaults } = renderTemplate(template, secrets, process.env);
233
- const allCount = [...template.matchAll(PLACEHOLDER_RE)].length;
234
- console.log(chalk_1.default.bold(`\nTemplate Check: ${templateFile}\n`));
235
- console.log(` Total placeholders : ${allCount}`);
236
- console.log(` Resolved : ${chalk_1.default.green(replacedCount)}`);
237
- console.log(` Using defaults : ${chalk_1.default.yellow(usedDefaults.length)}`);
238
- console.log(` Unresolved : ${chalk_1.default.red(missingKeys.length)}`);
239
- if (usedDefaults.length > 0) {
240
- console.log(chalk_1.default.yellow(`\n Defaults used:`));
241
- usedDefaults.forEach(d => console.log(` - ${d}`));
242
- }
243
- if (missingKeys.length > 0) {
244
- console.log(chalk_1.default.red(`\n Missing secrets (no default):`));
245
- missingKeys.forEach(k => console.log(chalk_1.default.red(` ✗ ${k}`)));
246
- process.exit(1);
247
- }
248
- else {
249
- console.log(chalk_1.default.green("\n ✅ All placeholders are resolvable!"));
250
- }
251
- });
252
- // ── list ──────────────────────────────────────────────────────────────────────
253
- exports.templateCommand
254
- .command("list <templateFile>")
255
- .description("List all placeholders found in a template file (no API call needed)")
256
- .action((templateFile) => {
257
- const templatePath = path.resolve(process.cwd(), templateFile);
258
- if (!fs.existsSync(templatePath)) {
259
- console.error(chalk_1.default.red(`Template file not found: ${templatePath}`));
260
- process.exit(1);
261
- }
262
- const template = fs.readFileSync(templatePath, "utf8");
263
- const matches = [...template.matchAll(PLACEHOLDER_RE)];
264
- if (matches.length === 0) {
265
- console.log(chalk_1.default.yellow("No placeholders found in this template."));
266
- return;
267
- }
268
- console.log(chalk_1.default.bold(`\nPlaceholders in ${path.basename(templateFile)}:\n`));
269
- matches.forEach(m => {
270
- const [, source, key, def] = m;
271
- const defaultHint = def ? chalk_1.default.gray(` (default: "${def.trim()}")`) : "";
272
- const icon = source === "secrets" ? "🔒" : "📦";
273
- console.log(` ${icon} ${chalk_1.default.cyan(`{{ ${source}.${key} }}`)}${defaultHint}`);
274
- });
275
- console.log(`\n Total: ${chalk_1.default.bold(matches.length)} placeholder(s)\n`);
276
- });
1
+ var fe=Object.create;var R=Object.defineProperty;var me=Object.getOwnPropertyDescriptor;var ye=Object.getOwnPropertyNames;var he=Object.getPrototypeOf,ve=Object.prototype.hasOwnProperty;var A=(e,t)=>()=>(e&&(t=e(e=0)),t);var G=(e,t)=>{for(var s in t)R(e,s,{get:t[s],enumerable:!0})},M=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ye(t))!ve.call(e,r)&&r!==s&&R(e,r,{get:()=>t[r],enumerable:!(n=me(t,r))||n.enumerable});return e};var l=(e,t,s)=>(s=e!=null?fe(he(e)):{},M(t||!e||!e.__esModule?R(s,"default",{value:e,enumerable:!0}):s,e)),H=e=>M(R({},"__esModule",{value:!0}),e);var V,L,Y,B,h,U,w,z,O,I=A(()=>{V=l(require("conf")),L=l(require("fs")),Y=l(require("path")),B="https://www.xtrasecurity.in/api",h=new V.default({projectName:"xtra-cli",defaults:{apiUrl:B}}),U=h.get("apiUrl");U&&!process.env.XTRA_API_URL&&(U.includes("localhost")||U.includes("xtra-security.vercel.app"))&&h.set("apiUrl",B);w=()=>({...h.store,apiUrl:process.env.XTRA_API_URL||h.get("apiUrl")||B}),z=()=>h.get("token"),O=()=>{let e={};try{let t=Y.default.join(process.cwd(),".xtrarc");L.default.existsSync(t)&&(e=JSON.parse(L.default.readFileSync(t,"utf-8")))}catch{}return{project:e.project||h.get("project")||"",env:e.env||h.get("env")||"development",branch:e.branch||h.get("branch")||"main"}}});var X,W,Z,$e,o,S,_=A(()=>{X=l(require("axios"));I();W=l(require("fs")),Z=l(require("path")),$e=JSON.parse(W.readFileSync(Z.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:e}=w(),t=z();return X.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":$e.version,...t?{Authorization:`Bearer ${t}`}:{}}})},S={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,n,r="main")=>(await o().post(`/projects/${e}/envs/${t}/secrets`,{secrets:s,expectedVersions:n,branch:r})).data,getSecretDetails:async(e,t,s,n="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets/${s}?branch=${n}`)).data,syncLogs:async e=>(await o().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,s,n,r,i)=>(await o().post(`/projects/${e}/envs/${t}/secrets/link`,{key:s,sourceProjectId:n,sourceEnv:r,sourceKey:i})).data,rotateSecret:async(e,t,s,n,r)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/rotate`,{strategy:n,parsedNewValue:r})).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,n)=>{let r={format:e};return t&&(r.startDate=t),s&&(r.endDate=s),n&&(r.projectId=n),(await o().post("/audit/export",r,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,s,n)=>(await o().post("/access/request",{projectId:e,secretId:n,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}=w(),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}=w(),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,n)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/history`,{version:n})).data,cloneEnvironment:async(e,t,s,n,r)=>(await o().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:s,overwrite:n,branch:r})).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 ne(){let e=Q.get("masterKey");e||(e=x.default.randomBytes(32).toString("hex"),Q.set("masterKey",e));let t=(0,te.machineIdSync)();return x.default.createHash("sha256").update(e+t).digest()}var x,ee,te,se,Q,re,oe,ae=A(()=>{x=l(require("crypto")),ee=l(require("conf")),te=require("node-machine-id"),se="aes-256-gcm",Q=new ee.default({projectName:"xtra-cli-crypto"});re=e=>{let t=x.default.randomBytes(16),s=ne(),n=x.default.createCipheriv(se,s,t),r=n.update(e,"utf8","hex");r+=n.final("hex");let i=n.getAuthTag();return{iv:t.toString("hex"),content:r,tag:i.toString("hex")}},oe=e=>{let t=ne(),s=Buffer.from(e.iv,"hex"),n=Buffer.from(e.tag,"hex"),r=x.default.createDecipheriv(se,t,s);r.setAuthTag(n);let i=r.update(e.content,"hex","utf8");return i+=r.final("utf8"),i}});var pe={};G(pe,{loadAuditLogs:()=>E,logAudit:()=>xe,saveAuditLogs:()=>C,syncAuditLogs:()=>je});function we(e){if(!e||typeof e!="object")return e;let t={...e},s=["password","token","secret","value","apiKey","secretValue"];for(let n of Object.keys(t))s.some(r=>n.toLowerCase().includes(r))&&(t[n]="[REDACTED]");return t}async function ce(e){try{if(!w().token)return!1;let s={action:e.action,entity:"cli-project",entityId:e.projectId||"global",details:we(e.details),timestamp:e.timestamp,projectId:e.projectId};return await S.post("/audit/cli-logs",{logs:[s]}),!0}catch(t){return process.env.DEBUG&&console.error("Failed to send audit log to backend:",t),!1}}function xe(e,t,s,n){let r={id:(0,ie.v4)(),timestamp:new Date().toISOString(),action:e,projectId:t,environment:s,details:n,synced:!1},i=E();i.push(r),C(i),ce(r).then(d=>{if(d){r.synced=!0;let u=E(),c=u.findIndex(p=>p.id===r.id);c!==-1&&(u[c].synced=!0,C(u))}}).catch(()=>{})}function E(){if(!j.default.existsSync(N))return[];try{let e=j.default.readFileSync(N,"utf-8"),t=JSON.parse(e),s=oe(t);return JSON.parse(s)}catch{return[]}}function C(e){j.default.existsSync(F)||j.default.mkdirSync(F,{recursive:!0});let t=JSON.stringify(e),s=re(t);j.default.writeFileSync(N,JSON.stringify(s),"utf-8")}async function je(){let e=E(),t=e.filter(n=>!n.synced);if(t.length===0)return 0;let s=0;for(let n of t)await ce(n)&&(n.synced=!0,s++);return s>0&&C(e),s}var j,K,ie,F,N,le=A(()=>{j=l(require("fs")),K=l(require("path")),ie=require("uuid");ae();I();_();F=K.default.join(process.cwd(),".xtra"),N=K.default.join(F,"audit.enc")});var be={};G(be,{templateCommand:()=>D});module.exports=H(be);var ge=require("commander");_();I();var a=l(require("chalk")),J=l(require("ora")),m=l(require("fs")),$=l(require("path")),P=/\{\{\s*(secrets|env)\.([A-Z0-9_a-z]+)(?:\s*\|\s*([^}]*?))?\s*\}\}/g;function de(e,t,s){let n=[],r=[],i=0;return{output:e.replace(P,(u,c,p,b)=>{let g;if(c==="secrets"?g=t[p]:c==="env"&&(g=s[p]),g!==void 0&&g!=="")return i++,g;if(b!==void 0){let f=b.trim();return r.push(`${c}.${p} \xE2\u2020\u2019 "${f}"`),i++,f}return n.push(`${c}.${p}`),`{{ ${c}.${p} }}`}),replacedCount:i,missingKeys:n,usedDefaults:r}}var D=new ge.Command("template").description("Secret templating engine \xE2\u20AC\u201D inject secrets into config file templates").addHelpText("after",`
2
+ Template Syntax:
3
+ {{ secrets.KEY }} Replace with secret value
4
+ {{ secrets.KEY | default }} Replace with secret value, fall back to "default"
5
+ {{ env.KEY }} Replace with local environment variable
6
+ {{ env.KEY | default }} Replace with local env variable, fall back to "default"
7
+
8
+ Examples:
9
+ $ xtra template render config.yaml.tpl -p proj123 -e production -o config.yaml
10
+ $ xtra template render nginx.conf.tpl -p proj123 -e production | sudo tee /etc/nginx/nginx.conf
11
+ $ xtra template check config.yaml.tpl -p proj123 -e production
12
+ $ xtra template list config.yaml.tpl
13
+ `);D.command("render <templateFile>").description("Render a template file by injecting secrets and environment variables").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment (default: development)","development").option("-b, --branch <branch>","Branch name","main").option("-o, --output <file>","Output file (default: stdout)").option("--strict","Exit with error if any placeholder is unresolved",!1).action(async(e,t)=>{let{project:s,env:n,branch:r,output:i,strict:d}=t;n={dev:"development",stg:"staging",prod:"production"}[n]||n,s||(s=O().project),s||(console.error(a.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'.")),process.exit(1));let c=$.resolve(process.cwd(),e);m.existsSync(c)||(console.error(a.default.red(`Error: Template file not found: ${c}`)),process.exit(1));let p=m.readFileSync(c,"utf8");[...p.matchAll(P)].length===0&&console.warn(a.default.yellow("Warning: No {{ secrets.* }} or {{ env.* }} placeholders found in template."));let g=(0,J.default)(`Fetching secrets for ${s}/${n}...`).start(),f={};try{f=await S.getSecrets(s,n,r),g.succeed(`Loaded ${Object.keys(f).length} secrets.`)}catch(y){g.fail("Failed to fetch secrets.");let ue=y?.response?.data?.error||y.message;console.error(a.default.red(ue)),process.exit(1)}let{output:k,replacedCount:v,missingKeys:T,usedDefaults:q}=de(p,f,process.env);if(console.log(a.default.green(`\u2705 Replaced ${v} placeholder(s).`)),q.length>0&&console.log(a.default.yellow(`\u26A0 Used defaults for: ${q.join(", ")}`)),T.length>0&&(console.log(a.default.red(`\u2717 Unresolved placeholders: ${T.join(", ")}`)),d&&(console.error(a.default.red("Aborting due to --strict mode.")),process.exit(1))),i){let y=$.resolve(process.cwd(),i);m.writeFileSync(y,k,"utf8"),console.log(a.default.blue(`\xE2\u2020\u2019 Written to: ${y}`))}else process.stdout.write(k);try{let{logAudit:y}=(le(),H(pe));y("TEMPLATE_RENDERED",s,n,{template:e,output:i||"stdout",replacedCount:v,missingKeys:T})}catch{}});D.command("check <templateFile>").description("Validate that all template placeholders have matching secrets (dry-run)").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment","development").option("-b, --branch <branch>","Branch name","main").action(async(e,t)=>{let{project:s,env:n,branch:r}=t;n={dev:"development",stg:"staging",prod:"production"}[n]||n,s||(s=O().project),s||(console.error(a.default.red("Error: Project ID required.")),process.exit(1));let d=$.resolve(process.cwd(),e);m.existsSync(d)||(console.error(a.default.red(`Template file not found: ${d}`)),process.exit(1));let u=m.readFileSync(d,"utf8"),c=(0,J.default)("Fetching secrets for check...").start(),p={};try{p=await S.getSecrets(s,n,r),c.stop()}catch{c.fail("Failed to fetch secrets."),process.exit(1)}let{replacedCount:b,missingKeys:g,usedDefaults:f}=de(u,p,process.env),k=[...u.matchAll(P)].length;console.log(a.default.bold(`
14
+ Template Check: ${e}
15
+ `)),console.log(` Total placeholders : ${k}`),console.log(` Resolved : ${a.default.green(b)}`),console.log(` Using defaults : ${a.default.yellow(f.length)}`),console.log(` Unresolved : ${a.default.red(g.length)}`),f.length>0&&(console.log(a.default.yellow(`
16
+ Defaults used:`)),f.forEach(v=>console.log(` - ${v}`))),g.length>0?(console.log(a.default.red(`
17
+ Missing secrets (no default):`)),g.forEach(v=>console.log(a.default.red(` \u2717 ${v}`))),process.exit(1)):console.log(a.default.green(`
18
+ \u2705 All placeholders are resolvable!`))});D.command("list <templateFile>").description("List all placeholders found in a template file (no API call needed)").action(e=>{let t=$.resolve(process.cwd(),e);m.existsSync(t)||(console.error(a.default.red(`Template file not found: ${t}`)),process.exit(1));let n=[...m.readFileSync(t,"utf8").matchAll(P)];if(n.length===0){console.log(a.default.yellow("No placeholders found in this template."));return}console.log(a.default.bold(`
19
+ Placeholders in ${$.basename(e)}:
20
+ `)),n.forEach(r=>{let[,i,d,u]=r,c=u?a.default.gray(` (default: "${u.trim()}")`):"";console.log(` ${i==="secrets"?"\u{1F512}":"\u{1F4E6}"} ${a.default.cyan(`{{ ${i}.${d} }}`)}${c}`)}),console.log(`
21
+ Total: ${a.default.bold(n.length)} placeholder(s)
22
+ `)});0&&(module.exports={templateCommand});
@@ -1,289 +1,2 @@
1
- "use strict";
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.uiCommand = void 0;
40
- const commander_1 = require("commander");
41
- const chalk_1 = __importDefault(require("chalk"));
42
- const readline = __importStar(require("readline"));
43
- const api_1 = require("../lib/api");
44
- const config_1 = require("../lib/config");
45
- const ENVS = ["development", "staging", "production"];
46
- const ENV_COLORS = {
47
- development: chalk_1.default.green,
48
- staging: chalk_1.default.yellow,
49
- production: chalk_1.default.red,
50
- };
51
- const W = process.stdout.columns || 120;
52
- // ─── Box Drawing Helpers ─────────────────────────────────────────────────────
53
- const B = {
54
- tl: "╭", tr: "╮", bl: "╰", br: "╯",
55
- h: "─", v: "│", x: "┼",
56
- lt: "├", rt: "┤", tt: "┬", bt: "┴",
57
- };
58
- function hline(width, c = B.h) { return c.repeat(width); }
59
- function box(title, lines, width, active) {
60
- const border = active ? chalk_1.default.cyan : chalk_1.default.hex("#2d3a4d");
61
- const inner = width - 2;
62
- const pad = (s) => s.padEnd(inner).slice(0, inner);
63
- const label = active
64
- ? chalk_1.default.bold.cyan(` ${title} `)
65
- : chalk_1.default.hex("#4a5568")(` ${title} `);
66
- const titleLine = border(B.tl) + label + border(hline(inner - title.length - 2)) + border(B.tr);
67
- const body = lines.map(l => border(B.v) + l.slice(0, inner).padEnd(inner) + border(B.v));
68
- const foot = border(B.bl) + border(hline(inner)) + border(B.br);
69
- return [titleLine, ...body, foot];
70
- }
71
- function clear() { process.stdout.write("\x1B[H\x1B[2J\x1B[3J"); }
72
- // ─── Main TUI ────────────────────────────────────────────────────────────────
73
- async function runUI() {
74
- let panel = "projects";
75
- let projects = [];
76
- let projIdx = 0;
77
- let envIdx = 0;
78
- let secrets = [];
79
- let secIdx = 0;
80
- let status = "Loading…";
81
- let loading = false;
82
- let showValues = false;
83
- // ── Loaders ────────────────────────────────────────────────────────────────
84
- async function loadProjects() {
85
- status = "Loading projects…";
86
- draw();
87
- try {
88
- const raw = await api_1.api.getProjects();
89
- projects = Array.isArray(raw) ? raw : raw.projects ?? [];
90
- status = projects.length > 0
91
- ? `${projects.length} project(s) found · ↑↓ navigate · Enter to load secrets`
92
- : "No projects found. Run `xtra login` first.";
93
- }
94
- catch (e) {
95
- status = chalk_1.default.red(`✗ ${e.message}`);
96
- }
97
- draw();
98
- }
99
- async function loadSecrets() {
100
- if (!projects[projIdx])
101
- return;
102
- loading = true;
103
- status = `Fetching secrets for ${chalk_1.default.cyan(projects[projIdx].name)} / ${ENV_COLORS[ENVS[envIdx]](ENVS[envIdx])}…`;
104
- draw();
105
- try {
106
- const rc = (0, config_1.getRcConfig)();
107
- const branch = rc.branch || "main";
108
- const raw = await api_1.api.getSecrets(projects[projIdx].id, ENVS[envIdx], branch);
109
- if (raw && typeof raw === "object" && !Array.isArray(raw)) {
110
- secrets = Object.entries(raw).map(([key, value]) => ({
111
- id: key, key, value: String(value), updatedAt: new Date().toISOString(),
112
- }));
113
- }
114
- else if (Array.isArray(raw)) {
115
- secrets = raw;
116
- }
117
- else {
118
- secrets = [];
119
- }
120
- secIdx = 0;
121
- status = secrets.length > 0
122
- ? `${secrets.length} secret(s) · V toggle values · Tab switch panel · Q quit`
123
- : "No secrets in this environment.";
124
- }
125
- catch (e) {
126
- secrets = [];
127
- status = chalk_1.default.red(`✗ ${e.message}`);
128
- }
129
- loading = false;
130
- draw();
131
- }
132
- // ── Render ─────────────────────────────────────────────────────────────────
133
- function draw() {
134
- clear();
135
- // ── Header ──────────────────────────────────────────────────────────────
136
- const appTitle = chalk_1.default.bold.cyan(" 🔒 XtraSecurity") + chalk_1.default.hex("#4a5a6b")(" — Interactive Secrets Shell");
137
- const env = ENV_COLORS[ENVS[envIdx]] ? ENV_COLORS[ENVS[envIdx]](ENVS[envIdx]) : chalk_1.default.white(ENVS[envIdx]);
138
- const crumb = projects[projIdx]
139
- ? chalk_1.default.hex("#4a5568")(` ${projects[projIdx].name} `) + chalk_1.default.hex("#4a5568")("/") + chalk_1.default.hex("#4a5568")(` ${ENVS[envIdx]}`)
140
- : chalk_1.default.hex("#4a5568")(" No project selected");
141
- console.log();
142
- console.log(appTitle + chalk_1.default.hex("#2d3a4d")(" │") + crumb);
143
- console.log(chalk_1.default.hex("#1e293b")(hline(W)));
144
- console.log();
145
- // ── Left column: Projects (22 chr) + Env (22 chr) ─────────────────────
146
- const LEFT_W = 26;
147
- const RIGHT_W = W - LEFT_W - 3;
148
- // Projects panel
149
- const projLines = (projects.length > 0 ? projects : [{ id: "", name: "(loading…)" }]).map((p, i) => {
150
- const active = i === projIdx;
151
- const icon = active ? chalk_1.default.cyan("▶ ") : " ";
152
- const name = active && panel === "projects"
153
- ? chalk_1.default.bold.bgCyan.black(` ${p.name} `.padEnd(LEFT_W - 4))
154
- : active
155
- ? chalk_1.default.bold.cyan(p.name)
156
- : chalk_1.default.hex("#6b7c93")(p.name);
157
- return ` ${icon}${name}`;
158
- });
159
- const projBox = box("PROJECTS", projLines, LEFT_W, panel === "projects");
160
- // Env panel
161
- const envLines = ENVS.map((e, i) => {
162
- const active = i === envIdx;
163
- const icon = active ? chalk_1.default.cyan("▶ ") : " ";
164
- const col = ENV_COLORS[e] ?? chalk_1.default.white;
165
- const name = active && panel === "env"
166
- ? chalk_1.default.bold.bgCyan.black(` ${e} `.padEnd(LEFT_W - 4))
167
- : active
168
- ? col.bold(e)
169
- : chalk_1.default.hex("#6b7c93")(e);
170
- return ` ${icon}${name}`;
171
- });
172
- const envBox = box("ENVIRONMENT", envLines, LEFT_W, panel === "env");
173
- // Secrets panel
174
- const KW = Math.floor((RIGHT_W - 3) * 0.45);
175
- const VW = Math.floor((RIGHT_W - 3) * 0.35);
176
- const DW = RIGHT_W - KW - VW - 4;
177
- const secHeader = chalk_1.default.bold.hex("#64748b")("KEY".padEnd(KW)) + " " + chalk_1.default.bold.hex("#64748b")("VALUE".padEnd(VW)) + " " + chalk_1.default.bold.hex("#64748b")("UPDATED".padEnd(DW));
178
- const divider = chalk_1.default.hex("#1e293b")(hline(KW) + " " + hline(VW) + " " + hline(DW));
179
- let secLines;
180
- if (loading) {
181
- secLines = [` ${chalk_1.default.cyan("⠋")} Loading…`];
182
- }
183
- else if (secrets.length === 0) {
184
- secLines = [` ${chalk_1.default.hex("#4a5568")("No secrets in this environment.")}`];
185
- }
186
- else {
187
- secLines = [" " + secHeader, " " + divider, ...secrets.map((s, i) => {
188
- const sel = i === secIdx && panel === "secrets";
189
- const key = s.key.padEnd(KW).slice(0, KW);
190
- const val = showValues ? s.value.padEnd(VW).slice(0, VW) : "●".repeat(Math.min(8, VW)).padEnd(VW).slice(0, VW);
191
- const dt = new Date(s.updatedAt).toLocaleDateString("en-GB", { day: "2-digit", month: "short" }).padEnd(DW);
192
- if (sel) {
193
- return chalk_1.default.bold.bgHex("#0e243e")(" ▶ " + chalk_1.default.cyan(key) + " " + chalk_1.default.greenBright(val) + " " + chalk_1.default.hex("#4a9eff")(dt) + " ");
194
- }
195
- return " " + chalk_1.default.white(key) + " " + chalk_1.default.hex("#4a5568")(val) + " " + chalk_1.default.hex("#374151")(dt);
196
- })];
197
- }
198
- const secBox = box(`SECRETS (${secrets.length})`, secLines, RIGHT_W, panel === "secrets");
199
- // ── Print side-by-side ────────────────────────────────────────────────
200
- const leftPanel = [...projBox, ...["", ""], ...envBox];
201
- const maxRows = Math.max(leftPanel.length, secBox.length);
202
- const gap = chalk_1.default.hex("#1e293b")(" │ ");
203
- for (let r = 0; r < maxRows; r++) {
204
- const l = leftPanel[r] ?? " ".repeat(LEFT_W);
205
- const rr = secBox[r] ?? " ".repeat(RIGHT_W);
206
- console.log(l + gap + rr);
207
- }
208
- // ── Footer ───────────────────────────────────────────────────────────
209
- console.log();
210
- console.log(chalk_1.default.hex("#1e293b")(hline(W)));
211
- const keys = [
212
- [chalk_1.default.hex("#64748b")("↑↓"), "Navigate"],
213
- [chalk_1.default.hex("#64748b")("Enter"), "Select"],
214
- [chalk_1.default.hex("#64748b")("Tab"), "Switch Panel"],
215
- [chalk_1.default.hex("#64748b")("V"), showValues ? chalk_1.default.green("Hide values") : chalk_1.default.yellow("Show values")],
216
- [chalk_1.default.hex("#64748b")("Q"), "Quit"],
217
- ].map(([k, a]) => `${k} ${chalk_1.default.hex("#374151")(a)}`).join(chalk_1.default.hex("#1e293b")(" · "));
218
- console.log(" " + keys);
219
- console.log();
220
- console.log(" " + (status || ""));
221
- }
222
- // ── Keyboard ──────────────────────────────────────────────────────────────
223
- readline.emitKeypressEvents(process.stdin);
224
- if (process.stdin.isTTY)
225
- process.stdin.setRawMode(true);
226
- process.stdin.on("keypress", async (_str, key) => {
227
- if (!key)
228
- return;
229
- // Quit
230
- if (key.name === "q" || (key.ctrl && key.name === "c")) {
231
- if (process.stdin.isTTY)
232
- process.stdin.setRawMode(false);
233
- clear();
234
- console.log(chalk_1.default.cyan(" Goodbye! 👋\n"));
235
- process.exit(0);
236
- }
237
- // Toggle secret values with V
238
- if (key.name === "v") {
239
- showValues = !showValues;
240
- draw();
241
- return;
242
- }
243
- // Tab cycle panels
244
- if (key.name === "tab") {
245
- panel = panel === "projects" ? "env" : panel === "env" ? "secrets" : "projects";
246
- draw();
247
- return;
248
- }
249
- if (panel === "projects") {
250
- if (key.name === "up")
251
- projIdx = Math.max(0, projIdx - 1);
252
- if (key.name === "down")
253
- projIdx = Math.min(projects.length - 1, projIdx + 1);
254
- if (key.name === "return") {
255
- panel = "env";
256
- draw();
257
- return;
258
- }
259
- }
260
- if (panel === "env") {
261
- if (key.name === "up")
262
- envIdx = Math.max(0, envIdx - 1);
263
- if (key.name === "down")
264
- envIdx = Math.min(ENVS.length - 1, envIdx + 1);
265
- if (key.name === "return") {
266
- panel = "secrets";
267
- await loadSecrets();
268
- return;
269
- }
270
- }
271
- if (panel === "secrets") {
272
- if (key.name === "up")
273
- secIdx = Math.max(0, secIdx - 1);
274
- if (key.name === "down")
275
- secIdx = Math.min(secrets.length - 1, secIdx + 1);
276
- }
277
- draw();
278
- });
279
- // ── Boot ──────────────────────────────────────────────────────────────────
280
- await loadProjects();
281
- if (projects.length > 0)
282
- await loadSecrets();
283
- else
284
- draw();
285
- }
286
- // ─── Commander ───────────────────────────────────────────────────────────────
287
- exports.uiCommand = new commander_1.Command("ui")
288
- .description("Launch interactive TUI secrets dashboard (arrow keys, Tab, Q to quit)")
289
- .action(async () => { await runUI(); });
1
+ var le=Object.create;var T=Object.defineProperty;var de=Object.getOwnPropertyDescriptor;var ue=Object.getOwnPropertyNames;var he=Object.getPrototypeOf,me=Object.prototype.hasOwnProperty;var fe=(e,t)=>{for(var s in t)T(e,s,{get:t[s],enumerable:!0})},H=(e,t,s,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ue(t))!me.call(e,o)&&o!==s&&T(e,o,{get:()=>t[o],enumerable:!(r=de(t,o))||r.enumerable});return e};var w=(e,t,s)=>(s=e!=null?le(he(e)):{},H(t||!e||!e.__esModule?T(s,"default",{value:e,enumerable:!0}):s,e)),ye=e=>H(T({},"__esModule",{value:!0}),e);var ve={};fe(ve,{uiCommand:()=>xe});module.exports=ye(ve);var ee=require("commander"),n=w(require("chalk")),te=w(require("readline"));var X=w(require("axios"));var W=w(require("conf")),M=w(require("fs")),K=w(require("path")),O="https://www.xtrasecurity.in/api",x=new W.default({projectName:"xtra-cli",defaults:{apiUrl:O}}),_=x.get("apiUrl");_&&!process.env.XTRA_API_URL&&(_.includes("localhost")||_.includes("xtra-security.vercel.app"))&&x.set("apiUrl",O);var P=()=>({...x.store,apiUrl:process.env.XTRA_API_URL||x.get("apiUrl")||O});var F=()=>x.get("token");var Q=()=>{let e={};try{let t=K.default.join(process.cwd(),".xtrarc");M.default.existsSync(t)&&(e=JSON.parse(M.default.readFileSync(t,"utf-8")))}catch{}return{project:e.project||x.get("project")||"",env:e.env||x.get("env")||"development",branch:e.branch||x.get("branch")||"main"}};var Y=w(require("fs")),z=w(require("path")),be=JSON.parse(Y.readFileSync(z.join(__dirname,"../../package.json"),"utf-8")),a=()=>{let{apiUrl:e}=P(),t=F();return X.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":be.version,...t?{Authorization:`Bearer ${t}`}:{}}})},V={login:async(e,t,s)=>(await a().post("/auth/cli-login",{email:e,password:t,apiKey:s})).data,getSecrets:async(e,t,s="main")=>(await a().get(`/projects/${e}/envs/${t}/secrets?branch=${s}`)).data,getSecretVersions:async(e,t,s="main")=>(await a().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(e,t,s,r,o="main")=>(await a().post(`/projects/${e}/envs/${t}/secrets`,{secrets:s,expectedVersions:r,branch:o})).data,getSecretDetails:async(e,t,s,r="main")=>(await a().get(`/projects/${e}/envs/${t}/secrets/${s}?branch=${r}`)).data,syncLogs:async e=>(await a().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,s,r,o,c)=>(await a().post(`/projects/${e}/envs/${t}/secrets/link`,{key:s,sourceProjectId:r,sourceEnv:o,sourceKey:c})).data,rotateSecret:async(e,t,s,r,o)=>(await a().post(`/projects/${e}/envs/${t}/secrets/${s}/rotate`,{strategy:r,parsedNewValue:o})).data,promoteSecret:async(e,t,s)=>(await a().post(`/projects/${e}/envs/${t}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await a().get("/audit/verify")).data,exportAuditLogs:async(e,t,s,r)=>{let o={format:e};return t&&(o.startDate=t),s&&(o.endDate=s),r&&(o.projectId=r),(await a().post("/audit/export",o,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,s,r)=>(await a().post("/access/request",{projectId:e,secretId:r,reason:t,duration:s})).data,approveAccess:async(e,t)=>(await a().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=P(),s=t?`&workspaceId=${t}`:"";return(await a().get(`/access/list?mode=${e}${s}`)).data},getAccessRequestStatus:async e=>(await a().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=P(),t=e?`/project?workspaceId=${e}`:"/project";return(await a().get(t)).data},getBranches:async e=>(await a().get(`/branch?projectId=${e}`)).data,createBranch:async(e,t,s)=>(await a().post("/branch",{projectId:e,name:t,description:s})).data,deleteBranch:async e=>(await a().delete(`/branch/${e}`)).data,updateBranch:async(e,t)=>(await a().put("/branch",{id:e,...t})).data,getRoles:async()=>(await a().get("/admin/roles")).data,getUsers:async e=>{let t=e?`?teamId=${e}`:"";return(await a().get(`/admin/users${t}`)).data},setUserRole:async(e,t,s)=>(await a().put("/admin/users/role",{email:e,role:t,teamId:s})).data,getIntegrationStatus:async e=>(await a().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await a().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await a().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,t)=>{let s=new URLSearchParams(t).toString();return(await a().get(`/projects/${e}/kubernetes?${s}`)).data},getSecretHistory:async(e,t,s)=>(await a().get(`/projects/${e}/envs/${t}/secrets/${s}/history`)).data,rollbackSecret:async(e,t,s,r)=>(await a().post(`/projects/${e}/envs/${t}/secrets/${s}/history`,{version:r})).data,cloneEnvironment:async(e,t,s,r,o)=>(await a().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:s,overwrite:r,branch:o})).data,generateJitLink:async e=>(await a().post("/jit/generate",e)).data,claimJitLink:async e=>(await a().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await a().get(`/jit/${e}`)).data,post:async(e,t)=>(await a().post(e,t)).data};var u=["development","staging","production"],U={development:n.default.green,staging:n.default.yellow,production:n.default.red},D=process.stdout.columns||120,$={tl:"\u256D",tr:"\u256E",bl:"\u2570",br:"\u256F",h:"\u2500",v:"\u2502",x:"\u253C",lt:"\u251C",rt:"\u2524",tt:"\u252C",bt:"\u2534"};function j(e,t=$.h){return t.repeat(e)}function G(e,t,s,r){let o=r?n.default.cyan:n.default.hex("#2d3a4d"),c=s-2,h=g=>g.padEnd(c).slice(0,c),A=r?n.default.bold.cyan(` ${e} `):n.default.hex("#4a5568")(` ${e} `),S=o($.tl)+A+o(j(c-e.length-2))+o($.tr),B=t.map(g=>o($.v)+g.slice(0,c).padEnd(c)+o($.v)),k=o($.bl)+o(j(c))+o($.br);return[S,...B,k]}function Z(){process.stdout.write("\x1B[H\x1B[2J\x1B[3J")}async function we(){let e="projects",t=[],s=0,r=0,o=[],c=0,h="Loading\u2026",A=!1,S=!1;async function B(){h="Loading projects\u2026",g();try{let l=await V.getProjects();t=Array.isArray(l)?l:l.projects??[],h=t.length>0?`${t.length} project(s) found \xB7 \u2191\u2193 navigate \xB7 Enter to load secrets`:"No projects found. Run `xtra login` first."}catch(l){h=n.default.red(`\u2717 ${l.message}`)}g()}async function k(){if(t[s]){A=!0,h=`Fetching secrets for ${n.default.cyan(t[s].name)} / ${U[u[r]](u[r])}\u2026`,g();try{let p=Q().branch||"main",y=await V.getSecrets(t[s].id,u[r],p);y&&typeof y=="object"&&!Array.isArray(y)?o=Object.entries(y).map(([m,v])=>({id:m,key:m,value:String(v),updatedAt:new Date().toISOString()})):Array.isArray(y)?o=y:o=[],c=0,h=o.length>0?`${o.length} secret(s) \xB7 V toggle values \xB7 Tab switch panel \xB7 Q quit`:"No secrets in this environment."}catch(l){o=[],h=n.default.red(`\u2717 ${l.message}`)}A=!1,g()}}function g(){Z();let l=n.default.bold.cyan(" \u{1F512} XtraSecurity")+n.default.hex("#4a5a6b")(" \u2014 Interactive Secrets Shell"),p=U[u[r]]?U[u[r]](u[r]):n.default.white(u[r]),y=t[s]?n.default.hex("#4a5568")(` ${t[s].name} `)+n.default.hex("#4a5568")("/")+n.default.hex("#4a5568")(` ${u[r]}`):n.default.hex("#4a5568")(" No project selected");console.log(),console.log(l+n.default.hex("#2d3a4d")(" \u2502")+y),console.log(n.default.hex("#1e293b")(j(D))),console.log();let m=26,v=D-m-3,se=(t.length>0?t:[{id:"",name:"(loading\u2026)"}]).map((i,f)=>{let d=f===s,I=d?n.default.cyan("\u25B6 "):" ",R=d&&e==="projects"?n.default.bold.bgCyan.black(` ${i.name} `.padEnd(m-4)):d?n.default.bold.cyan(i.name):n.default.hex("#6b7c93")(i.name);return` ${I}${R}`}),ne=G("PROJECTS",se,m,e==="projects"),re=u.map((i,f)=>{let d=f===r,I=d?n.default.cyan("\u25B6 "):" ",R=U[i]??n.default.white,L=d&&e==="env"?n.default.bold.bgCyan.black(` ${i} `.padEnd(m-4)):d?R.bold(i):n.default.hex("#6b7c93")(i);return` ${I}${L}`}),oe=G("ENVIRONMENT",re,m,e==="env"),E=Math.floor((v-3)*.45),b=Math.floor((v-3)*.35),N=v-E-b-4,ae=n.default.bold.hex("#64748b")("KEY".padEnd(E))+" "+n.default.bold.hex("#64748b")("VALUE".padEnd(b))+" "+n.default.bold.hex("#64748b")("UPDATED".padEnd(N)),ce=n.default.hex("#1e293b")(j(E)+" "+j(b)+" "+j(N)),C;A?C=[` ${n.default.cyan("\u280B")} Loading\u2026`]:o.length===0?C=[` ${n.default.hex("#4a5568")("No secrets in this environment.")}`]:C=[" "+ae," "+ce,...o.map((i,f)=>{let d=f===c&&e==="secrets",I=i.key.padEnd(E).slice(0,E),R=S?i.value.padEnd(b).slice(0,b):"\u25CF".repeat(Math.min(8,b)).padEnd(b).slice(0,b),L=new Date(i.updatedAt).toLocaleDateString("en-GB",{day:"2-digit",month:"short"}).padEnd(N);return d?n.default.bold.bgHex("#0e243e")(" \u25B6 "+n.default.cyan(I)+" "+n.default.greenBright(R)+" "+n.default.hex("#4a9eff")(L)+" "):" "+n.default.white(I)+" "+n.default.hex("#4a5568")(R)+" "+n.default.hex("#374151")(L)})];let q=G(`SECRETS (${o.length})`,C,v,e==="secrets"),J=[...ne,"","",...oe],ie=Math.max(J.length,q.length),pe=n.default.hex("#1e293b")(" \u2502 ");for(let i=0;i<ie;i++){let f=J[i]??" ".repeat(m),d=q[i]??" ".repeat(v);console.log(f+pe+d)}console.log(),console.log(n.default.hex("#1e293b")(j(D)));let ge=[[n.default.hex("#64748b")("\u2191\u2193"),"Navigate"],[n.default.hex("#64748b")("Enter"),"Select"],[n.default.hex("#64748b")("Tab"),"Switch Panel"],[n.default.hex("#64748b")("V"),S?n.default.green("Hide values"):n.default.yellow("Show values")],[n.default.hex("#64748b")("Q"),"Quit"]].map(([i,f])=>`${i} ${n.default.hex("#374151")(f)}`).join(n.default.hex("#1e293b")(" \xB7 "));console.log(" "+ge),console.log(),console.log(" "+(h||""))}te.emitKeypressEvents(process.stdin),process.stdin.isTTY&&process.stdin.setRawMode(!0),process.stdin.on("keypress",async(l,p)=>{if(p){if((p.name==="q"||p.ctrl&&p.name==="c")&&(process.stdin.isTTY&&process.stdin.setRawMode(!1),Z(),console.log(n.default.cyan(` Goodbye! \u{1F44B}
2
+ `)),process.exit(0)),p.name==="v"){S=!S,g();return}if(p.name==="tab"){e=e==="projects"?"env":e==="env"?"secrets":"projects",g();return}if(e==="projects"&&(p.name==="up"&&(s=Math.max(0,s-1)),p.name==="down"&&(s=Math.min(t.length-1,s+1)),p.name==="return")){e="env",g();return}if(e==="env"&&(p.name==="up"&&(r=Math.max(0,r-1)),p.name==="down"&&(r=Math.min(u.length-1,r+1)),p.name==="return")){e="secrets",await k();return}e==="secrets"&&(p.name==="up"&&(c=Math.max(0,c-1)),p.name==="down"&&(c=Math.min(o.length-1,c+1))),g()}}),await B(),t.length>0?await k():g()}var xe=new ee.Command("ui").description("Launch interactive TUI secrets dashboard (arrow keys, Tab, Q to quit)").action(async()=>{await we()});0&&(module.exports={uiCommand});