xtra-cli 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/bin/xtra.js +2 -294
  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 +2 -2
@@ -1,188 +1,11 @@
1
- "use strict";
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.kubernetesCommand = exports.integrationCommand = void 0;
7
- const commander_1 = require("commander");
8
- const chalk_1 = __importDefault(require("chalk"));
9
- const api_1 = require("../lib/api");
10
- const config_1 = require("../lib/config");
11
- const inquirer_1 = __importDefault(require("inquirer"));
12
- const fs_1 = __importDefault(require("fs"));
13
- exports.integrationCommand = new commander_1.Command("integration");
14
- exports.integrationCommand
15
- .command("sync")
16
- .description("Sync secrets to external integrations (GitHub)")
17
- .option("-p, --project <id>", "Project ID (defaults to current directory config)")
18
- .option("-e, --env <environment>", "Environment (development, staging, production)", "development")
19
- .option("--github", "Sync to GitHub")
20
- .option("--repo <owner/repo>", "GitHub repository (e.g., owner/repo)")
21
- .option("--prefix <prefix>", "Prefix for secret keys")
22
- .action(async (options) => {
23
- try {
24
- let projectId = options.project;
25
- if (!projectId) {
26
- projectId = (0, config_1.getRcConfig)().project;
27
- if (!projectId) {
28
- console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
29
- process.exit(1);
30
- }
31
- }
32
- // Check if GitHub connected
33
- const status = await api_1.api.getIntegrationStatus("github");
34
- if (!status.connected) {
35
- console.log(chalk_1.default.yellow("\nGitHub not connected. Please connect via the web dashboard first."));
36
- console.log(chalk_1.default.blue("Visit: https://www.xtrasecurity.in/integrations\n"));
37
- process.exit(1);
38
- }
39
- console.log(chalk_1.default.green(`\nConnected as: ${status.username}`));
40
- let repo = options.repo;
41
- // If repo not provided, fetch list and ask user
42
- if (!repo) {
43
- console.log("Fetching repositories...");
44
- const repos = await api_1.api.getIntegrationRepos("github");
45
- const answer = await inquirer_1.default.prompt([
46
- {
47
- type: "list",
48
- name: "repo",
49
- message: "Select a repository to sync to:",
50
- choices: repos.map((r) => ({
51
- name: `${r.fullName} ${r.private ? "(🔒)" : ""}`,
52
- value: r.id.toString(),
53
- short: r.fullName
54
- }))
55
- }
56
- ]);
57
- // Find selected repo details
58
- const selectedRepo = repos.find((r) => r.id.toString() === answer.repo);
59
- if (selectedRepo) {
60
- repo = { owner: selectedRepo.owner, name: selectedRepo.name };
61
- }
62
- }
63
- else {
64
- // Parse repo string
65
- const [owner, name] = repo.split("/");
66
- if (!owner || !name) {
67
- console.error(chalk_1.default.red("Error: Invalid repository format. Use owner/repo"));
68
- process.exit(1);
69
- }
70
- repo = { owner, name };
71
- }
72
- console.log(chalk_1.default.blue(`\nSyncing secrets from ${options.env} to GitHub repo: ${repo.owner}/${repo.name}...`));
73
- const result = await api_1.api.syncSecretsToGithub({
74
- projectId,
75
- environment: options.env,
76
- repoOwner: repo.owner,
77
- repoName: repo.name,
78
- secretPrefix: options.prefix
79
- });
80
- console.log(chalk_1.default.green(`\nSuccessfully synced ${result.summary.synced} secrets!`));
81
- if (result.summary.failed > 0) {
82
- console.log(chalk_1.default.red(`${result.summary.failed} secrets failed to sync.`));
83
- }
84
- // Show details
85
- result.results.forEach((r) => {
86
- if (r.success) {
87
- console.log(chalk_1.default.green(`✓ ${r.key}`));
88
- }
89
- else {
90
- console.log(chalk_1.default.red(`✗ ${r.key}: ${r.error}`));
91
- }
92
- });
93
- }
94
- catch (error) {
95
- console.error(chalk_1.default.red("\nError syncing secrets:"), error.message || error);
96
- process.exit(1);
97
- }
98
- });
99
- exports.kubernetesCommand = new commander_1.Command("kubernetes");
100
- exports.kubernetesCommand
101
- .command("export")
102
- .description("Export secrets as Kubernetes manifest")
103
- .option("-p, --project <id>", "Project ID")
104
- .option("-e, --env <environment>", "Environment", "development")
105
- .option("-n, --namespace <namespace>", "Kubernetes namespace", "default")
106
- .option("-o, --output <file>", "Output file path")
107
- .option("--name <name>", "Secret resource name")
108
- .action(async (options) => {
109
- try {
110
- let projectId = options.project;
111
- if (!projectId) {
112
- projectId = (0, config_1.getRcConfig)().project;
113
- if (!projectId) {
114
- console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
115
- process.exit(1);
116
- }
117
- }
118
- console.log(chalk_1.default.blue(`Generating Kubernetes manifest for ${options.env}...`));
119
- const manifest = await api_1.api.exportKubernetesSecret(projectId, {
120
- environment: options.env,
121
- namespace: options.namespace,
122
- name: options.name
123
- });
124
- if (options.output) {
125
- fs_1.default.writeFileSync(options.output, manifest);
126
- console.log(chalk_1.default.green(`\nManifest saved to ${options.output}`));
127
- }
128
- else {
129
- console.log("\n" + manifest);
130
- }
131
- }
132
- catch (error) {
133
- console.error(chalk_1.default.red("\nError generating manifest:"), error.message || error);
134
- process.exit(1);
135
- }
136
- });
137
- exports.kubernetesCommand
138
- .command("apply")
139
- .description("Apply secrets directly to Kubernetes cluster (requires kubectl)")
140
- .option("-p, --project <id>", "Project ID")
141
- .option("-e, --env <environment>", "Environment", "development")
142
- .option("-n, --namespace <namespace>", "Kubernetes namespace", "default")
143
- .action(async (options) => {
144
- try {
145
- // Check for kubectl
146
- const { execSync } = require("child_process");
147
- try {
148
- execSync("kubectl version --client", { stdio: "ignore" });
149
- }
150
- catch (e) {
151
- console.error(chalk_1.default.red("Error: kubectl not found or not configured."));
152
- process.exit(1);
153
- }
154
- let projectId = options.project;
155
- if (!projectId) {
156
- projectId = (0, config_1.getRcConfig)().project;
157
- if (!projectId) {
158
- console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
159
- process.exit(1);
160
- }
161
- }
162
- console.log(chalk_1.default.blue(`Fetching secrets for ${options.env}...`));
163
- const manifest = await api_1.api.exportKubernetesSecret(projectId, {
164
- environment: options.env,
165
- namespace: options.namespace,
166
- // No custom name, let backend generate it
167
- });
168
- console.log(chalk_1.default.blue("Applying to Kubernetes..."));
169
- try {
170
- const child = require("child_process").spawn("kubectl", ["apply", "-f", "-"]);
171
- child.stdin.write(manifest);
172
- child.stdin.end();
173
- child.stdout.on("data", (data) => {
174
- console.log(data.toString());
175
- });
176
- child.stderr.on("data", (data) => {
177
- console.error(chalk_1.default.yellow(data.toString()));
178
- });
179
- }
180
- catch (e) {
181
- console.error(chalk_1.default.red("Failed to apply manifest:"), e.message);
182
- }
183
- }
184
- catch (error) {
185
- console.error(chalk_1.default.red("\nError:"), error.message || error);
186
- process.exit(1);
187
- }
188
- });
1
+ var E=Object.create;var d=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var L=Object.getPrototypeOf,_=Object.prototype.hasOwnProperty;var D=(e,t)=>{for(var r in t)d(e,r,{get:t[r],enumerable:!0})},$=(e,t,r,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of q(t))!_.call(e,n)&&n!==r&&d(e,n,{get:()=>t[n],enumerable:!(s=G(t,n))||s.enumerable});return e};var c=(e,t,r)=>(r=e!=null?E(L(e)):{},$(t||!e||!e.__esModule?d(r,"default",{value:e,enumerable:!0}):r,e)),K=e=>$(d({},"__esModule",{value:!0}),e);var T={};D(T,{integrationCommand:()=>A,kubernetesCommand:()=>j});module.exports=K(T);var v=require("commander"),a=c(require("chalk"));var I=c(require("axios"));var x=c(require("conf")),h=c(require("fs")),k=c(require("path")),b="https://www.xtrasecurity.in/api",p=new x.default({projectName:"xtra-cli",defaults:{apiUrl:b}}),w=p.get("apiUrl");w&&!process.env.XTRA_API_URL&&(w.includes("localhost")||w.includes("xtra-security.vercel.app"))&&p.set("apiUrl",b);var m=()=>({...p.store,apiUrl:process.env.XTRA_API_URL||p.get("apiUrl")||b});var S=()=>p.get("token");var y=()=>{let e={};try{let t=k.default.join(process.cwd(),".xtrarc");h.default.existsSync(t)&&(e=JSON.parse(h.default.readFileSync(t,"utf-8")))}catch{}return{project:e.project||p.get("project")||"",env:e.env||p.get("env")||"development",branch:e.branch||p.get("branch")||"main"}};var C=c(require("fs")),U=c(require("path")),N=JSON.parse(C.readFileSync(U.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:e}=m(),t=S();return I.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":N.version,...t?{Authorization:`Bearer ${t}`}:{}}})},l={login:async(e,t,r)=>(await o().post("/auth/cli-login",{email:e,password:t,apiKey:r})).data,getSecrets:async(e,t,r="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?branch=${r}`)).data,getSecretVersions:async(e,t,r="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${r}`)).data,setSecrets:async(e,t,r,s,n="main")=>(await o().post(`/projects/${e}/envs/${t}/secrets`,{secrets:r,expectedVersions:s,branch:n})).data,getSecretDetails:async(e,t,r,s="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets/${r}?branch=${s}`)).data,syncLogs:async e=>(await o().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,r,s,n,i)=>(await o().post(`/projects/${e}/envs/${t}/secrets/link`,{key:r,sourceProjectId:s,sourceEnv:n,sourceKey:i})).data,rotateSecret:async(e,t,r,s,n)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${r}/rotate`,{strategy:s,parsedNewValue:n})).data,promoteSecret:async(e,t,r)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${r}/promote`,{})).data,verifyAuditLogs:async()=>(await o().get("/audit/verify")).data,exportAuditLogs:async(e,t,r,s)=>{let n={format:e};return t&&(n.startDate=t),r&&(n.endDate=r),s&&(n.projectId=s),(await o().post("/audit/export",n,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,r,s)=>(await o().post("/access/request",{projectId:e,secretId:s,reason:t,duration:r})).data,approveAccess:async(e,t)=>(await o().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=m(),r=t?`&workspaceId=${t}`:"";return(await o().get(`/access/list?mode=${e}${r}`)).data},getAccessRequestStatus:async e=>(await o().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=m(),t=e?`/project?workspaceId=${e}`:"/project";return(await o().get(t)).data},getBranches:async e=>(await o().get(`/branch?projectId=${e}`)).data,createBranch:async(e,t,r)=>(await o().post("/branch",{projectId:e,name:t,description:r})).data,deleteBranch:async e=>(await o().delete(`/branch/${e}`)).data,updateBranch:async(e,t)=>(await o().put("/branch",{id:e,...t})).data,getRoles:async()=>(await o().get("/admin/roles")).data,getUsers:async e=>{let t=e?`?teamId=${e}`:"";return(await o().get(`/admin/users${t}`)).data},setUserRole:async(e,t,r)=>(await o().put("/admin/users/role",{email:e,role:t,teamId:r})).data,getIntegrationStatus:async e=>(await o().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await o().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await o().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,t)=>{let r=new URLSearchParams(t).toString();return(await o().get(`/projects/${e}/kubernetes?${r}`)).data},getSecretHistory:async(e,t,r)=>(await o().get(`/projects/${e}/envs/${t}/secrets/${r}/history`)).data,rollbackSecret:async(e,t,r,s)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${r}/history`,{version:s})).data,cloneEnvironment:async(e,t,r,s,n)=>(await o().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:r,overwrite:s,branch:n})).data,generateJitLink:async e=>(await o().post("/jit/generate",e)).data,claimJitLink:async e=>(await o().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await o().get(`/jit/${e}`)).data,post:async(e,t)=>(await o().post(e,t)).data};var P=c(require("inquirer")),R=c(require("fs")),A=new v.Command("integration");A.command("sync").description("Sync secrets to external integrations (GitHub)").option("-p, --project <id>","Project ID (defaults to current directory config)").option("-e, --env <environment>","Environment (development, staging, production)","development").option("--github","Sync to GitHub").option("--repo <owner/repo>","GitHub repository (e.g., owner/repo)").option("--prefix <prefix>","Prefix for secret keys").action(async e=>{try{let t=e.project;t||(t=y().project,t||(console.error(a.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1)));let r=await l.getIntegrationStatus("github");r.connected||(console.log(a.default.yellow(`
2
+ GitHub not connected. Please connect via the web dashboard first.`)),console.log(a.default.blue(`Visit: https://www.xtrasecurity.in/integrations
3
+ `)),process.exit(1)),console.log(a.default.green(`
4
+ Connected as: ${r.username}`));let s=e.repo;if(s){let[i,u]=s.split("/");(!i||!u)&&(console.error(a.default.red("Error: Invalid repository format. Use owner/repo")),process.exit(1)),s={owner:i,name:u}}else{console.log("Fetching repositories...");let i=await l.getIntegrationRepos("github"),u=await P.default.prompt([{type:"list",name:"repo",message:"Select a repository to sync to:",choices:i.map(g=>({name:`${g.fullName} ${g.private?"(\u{1F512})":""}`,value:g.id.toString(),short:g.fullName}))}]),f=i.find(g=>g.id.toString()===u.repo);f&&(s={owner:f.owner,name:f.name})}console.log(a.default.blue(`
5
+ Syncing secrets from ${e.env} to GitHub repo: ${s.owner}/${s.name}...`));let n=await l.syncSecretsToGithub({projectId:t,environment:e.env,repoOwner:s.owner,repoName:s.name,secretPrefix:e.prefix});console.log(a.default.green(`
6
+ Successfully synced ${n.summary.synced} secrets!`)),n.summary.failed>0&&console.log(a.default.red(`${n.summary.failed} secrets failed to sync.`)),n.results.forEach(i=>{i.success?console.log(a.default.green(`\u2713 ${i.key}`)):console.log(a.default.red(`\u2717 ${i.key}: ${i.error}`))})}catch(t){console.error(a.default.red(`
7
+ Error syncing secrets:`),t.message||t),process.exit(1)}});var j=new v.Command("kubernetes");j.command("export").description("Export secrets as Kubernetes manifest").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment","development").option("-n, --namespace <namespace>","Kubernetes namespace","default").option("-o, --output <file>","Output file path").option("--name <name>","Secret resource name").action(async e=>{try{let t=e.project;t||(t=y().project,t||(console.error(a.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1))),console.log(a.default.blue(`Generating Kubernetes manifest for ${e.env}...`));let r=await l.exportKubernetesSecret(t,{environment:e.env,namespace:e.namespace,name:e.name});e.output?(R.default.writeFileSync(e.output,r),console.log(a.default.green(`
8
+ Manifest saved to ${e.output}`))):console.log(`
9
+ `+r)}catch(t){console.error(a.default.red(`
10
+ Error generating manifest:`),t.message||t),process.exit(1)}});j.command("apply").description("Apply secrets directly to Kubernetes cluster (requires kubectl)").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment","development").option("-n, --namespace <namespace>","Kubernetes namespace","default").action(async e=>{try{let{execSync:t}=require("child_process");try{t("kubectl version --client",{stdio:"ignore"})}catch{console.error(a.default.red("Error: kubectl not found or not configured.")),process.exit(1)}let r=e.project;r||(r=y().project,r||(console.error(a.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1))),console.log(a.default.blue(`Fetching secrets for ${e.env}...`));let s=await l.exportKubernetesSecret(r,{environment:e.env,namespace:e.namespace});console.log(a.default.blue("Applying to Kubernetes..."));try{let n=require("child_process").spawn("kubectl",["apply","-f","-"]);n.stdin.write(s),n.stdin.end(),n.stdout.on("data",i=>{console.log(i.toString())}),n.stderr.on("data",i=>{console.error(a.default.yellow(i.toString()))})}catch(n){console.error(a.default.red("Failed to apply manifest:"),n.message)}}catch(t){console.error(a.default.red(`
11
+ Error:`),t.message||t),process.exit(1)}});0&&(module.exports={integrationCommand,kubernetesCommand});
@@ -0,0 +1,5 @@
1
+ var _=Object.create;var f=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var G=Object.getPrototypeOf,N=Object.prototype.hasOwnProperty;var D=(e,s)=>{for(var t in s)f(e,t,{get:s[t],enumerable:!0})},x=(e,s,t,r)=>{if(s&&typeof s=="object"||typeof s=="function")for(let o of B(s))!N.call(e,o)&&o!==t&&f(e,o,{get:()=>s[o],enumerable:!(r=O(s,o))||r.enumerable});return e};var i=(e,s,t)=>(t=e!=null?_(G(e)):{},x(s||!e||!e.__esModule?f(t,"default",{value:e,enumerable:!0}):t,e)),E=e=>x(f({},"__esModule",{value:!0}),e);var X={};D(X,{jitRunCommand:()=>F});module.exports=E(X);var U=require("commander");var A=i(require("axios"));var S=i(require("conf")),j=i(require("fs")),I=i(require("path")),v="https://www.xtrasecurity.in/api",c=new S.default({projectName:"xtra-cli",defaults:{apiUrl:v}}),$=c.get("apiUrl");$&&!process.env.XTRA_API_URL&&($.includes("localhost")||$.includes("xtra-security.vercel.app"))&&c.set("apiUrl",v);var w=()=>({...c.store,apiUrl:process.env.XTRA_API_URL||c.get("apiUrl")||v});var b=()=>c.get("token");var C=()=>{let e={};try{let s=I.default.join(process.cwd(),".xtrarc");j.default.existsSync(s)&&(e=JSON.parse(j.default.readFileSync(s,"utf-8")))}catch{}return{project:e.project||c.get("project")||"",env:e.env||c.get("env")||"development",branch:e.branch||c.get("branch")||"main"}};var R=i(require("fs")),T=i(require("path")),V=JSON.parse(R.readFileSync(T.join(__dirname,"../../package.json"),"utf-8")),n=()=>{let{apiUrl:e}=w(),s=b();return A.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":V.version,...s?{Authorization:`Bearer ${s}`}:{}}})},h={login:async(e,s,t)=>(await n().post("/auth/cli-login",{email:e,password:s,apiKey:t})).data,getSecrets:async(e,s,t="main")=>(await n().get(`/projects/${e}/envs/${s}/secrets?branch=${t}`)).data,getSecretVersions:async(e,s,t="main")=>(await n().get(`/projects/${e}/envs/${s}/secrets?includeVersions=true&branch=${t}`)).data,setSecrets:async(e,s,t,r,o="main")=>(await n().post(`/projects/${e}/envs/${s}/secrets`,{secrets:t,expectedVersions:r,branch:o})).data,getSecretDetails:async(e,s,t,r="main")=>(await n().get(`/projects/${e}/envs/${s}/secrets/${t}?branch=${r}`)).data,syncLogs:async e=>(await n().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,s,t,r,o,p)=>(await n().post(`/projects/${e}/envs/${s}/secrets/link`,{key:t,sourceProjectId:r,sourceEnv:o,sourceKey:p})).data,rotateSecret:async(e,s,t,r,o)=>(await n().post(`/projects/${e}/envs/${s}/secrets/${t}/rotate`,{strategy:r,parsedNewValue:o})).data,promoteSecret:async(e,s,t)=>(await n().post(`/projects/${e}/envs/${s}/secrets/${t}/promote`,{})).data,verifyAuditLogs:async()=>(await n().get("/audit/verify")).data,exportAuditLogs:async(e,s,t,r)=>{let o={format:e};return s&&(o.startDate=s),t&&(o.endDate=t),r&&(o.projectId=r),(await n().post("/audit/export",o,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,s,t,r)=>(await n().post("/access/request",{projectId:e,secretId:r,reason:s,duration:t})).data,approveAccess:async(e,s)=>(await n().post("/access/approve",{requestId:e,decision:s})).data,listAccessRequests:async e=>{let{workspace:s}=w(),t=s?`&workspaceId=${s}`:"";return(await n().get(`/access/list?mode=${e}${t}`)).data},getAccessRequestStatus:async e=>(await n().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=w(),s=e?`/project?workspaceId=${e}`:"/project";return(await n().get(s)).data},getBranches:async e=>(await n().get(`/branch?projectId=${e}`)).data,createBranch:async(e,s,t)=>(await n().post("/branch",{projectId:e,name:s,description:t})).data,deleteBranch:async e=>(await n().delete(`/branch/${e}`)).data,updateBranch:async(e,s)=>(await n().put("/branch",{id:e,...s})).data,getRoles:async()=>(await n().get("/admin/roles")).data,getUsers:async e=>{let s=e?`?teamId=${e}`:"";return(await n().get(`/admin/users${s}`)).data},setUserRole:async(e,s,t)=>(await n().put("/admin/users/role",{email:e,role:s,teamId:t})).data,getIntegrationStatus:async e=>(await n().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await n().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await n().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,s)=>{let t=new URLSearchParams(s).toString();return(await n().get(`/projects/${e}/kubernetes?${t}`)).data},getSecretHistory:async(e,s,t)=>(await n().get(`/projects/${e}/envs/${s}/secrets/${t}/history`)).data,rollbackSecret:async(e,s,t,r)=>(await n().post(`/projects/${e}/envs/${s}/secrets/${t}/history`,{version:r})).data,cloneEnvironment:async(e,s,t,r,o)=>(await n().post(`/projects/${e}/envs/clone`,{fromEnv:s,toEnv:t,overwrite:r,branch:o})).data,generateJitLink:async e=>(await n().post("/jit/generate",e)).data,claimJitLink:async e=>(await n().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await n().get(`/jit/${e}`)).data,post:async(e,s)=>(await n().post(e,s)).data};var J=require("child_process"),a=i(require("chalk")),q=i(require("ora"));var F=new U.Command("jit-run").description("Claim a JIT link and run a command once approved").requiredOption("-t, --token <token>","JIT Access Token").option("-e, --env <environment>","Environment (development, staging, production)","development").argument("<command>","Command to run").argument("[args...]","Command arguments").action(async(e,s,t)=>{let{token:r,env:o}=t,p=C(),g=(0,q.default)("Claiming JIT link...").start();try{let d=await h.claimJitLink(r),k=d.requestId;g.text=`Access request submitted (ID: ${k}). Waiting for admin approval...`;let u=d.status||"pending",l="";for(;u==="pending";){await new Promise(P=>setTimeout(P,5e3));let y=await h.getAccessRequestStatus(k);u=y.status,l=y.projectId,u==="rejected"&&(g.fail(a.default.red("Access request rejected by admin.")),process.exit(1)),(u==="revoked"||u==="expired")&&(g.fail(a.default.red(`Access request ${u}.`)),process.exit(1))}g.succeed(a.default.green("Access approved! Fetching secrets...")),l||(l=p.project),l||(g.fail("Error: Could not determine project ID for JIT session."),process.exit(1));let m=await h.getSecrets(l,o);!m||Object.keys(m).length===0?console.warn(a.default.yellow(`
2
+ Warning: No secrets returned. Ensure JIT link allowed specific secrets.`)):console.log(a.default.cyan(`
3
+ \u221A Loaded ${Object.keys(m).length} JIT-authorized secrets.`));let L={...process.env,...m};console.log(a.default.gray(`> ${e} ${s.join(" ")}`)),(0,J.spawn)(e,s,{env:L,stdio:"inherit",shell:process.platform==="win32"}).on("exit",y=>{process.exit(y??0)})}catch(d){g.fail("JIT Run failed."),d.response?console.error(a.default.red(`
4
+ Error: ${d.response.data.error||d.message}`)):console.error(a.default.red(`
5
+ Error: ${d.message}`)),process.exit(1)}});0&&(module.exports={jitRunCommand});
@@ -1,198 +1,16 @@
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.localCommand = void 0;
40
- /**
41
- * local.ts — Toggle between cloud/local-only mode for offline development
42
- *
43
- * In "local" mode:
44
- * - xtra run reads from .env.local instead of calling the API
45
- * - A flag XTRA_LOCAL_MODE=true is written to the CLI config
46
- * - All API calls are bypassed — completely offline capable
47
- *
48
- * Usage:
49
- * xtra local on # Enable local mode (reads from .env.local)
50
- * xtra local off # Disable local mode (back to cloud)
51
- * xtra local status # Show current mode
52
- * xtra local sync # Pull cloud secrets and write to .env.local
53
- */
54
- const commander_1 = require("commander");
55
- const api_1 = require("../lib/api");
56
- const config_1 = require("../lib/config");
57
- const chalk_1 = __importDefault(require("chalk"));
58
- const ora_1 = __importDefault(require("ora"));
59
- const fs = __importStar(require("fs"));
60
- const path = __importStar(require("path"));
61
- const readline = __importStar(require("readline"));
62
- const LOCAL_ENV_FILE = ".env.local";
63
- function isLocalMode() {
64
- return (0, config_1.getConfigValue)("localMode") === true
65
- || process.env.XTRA_LOCAL_MODE === "true";
66
- }
67
- exports.localCommand = new commander_1.Command("local")
68
- .description("Toggle cloud/local mode for offline development")
69
- .addHelpText("after", `
70
- In local mode, 'xtra run' reads secrets from .env.local instead of the cloud API.
71
- This allows fully offline development without any API calls.
72
-
73
- Examples:
74
- $ xtra local status # Check current mode
75
- $ xtra local on # Enable offline (local) mode
76
- $ xtra local off # Disable — back to cloud mode
77
- $ xtra local sync # Pull cloud secrets → .env.local
78
- $ xtra local sync -p proj -e production # Pull production to .env.local
79
- `);
80
- // ── status ────────────────────────────────────────────────────────────────────
81
- exports.localCommand
82
- .command("status")
83
- .description("Show current cloud/local mode")
84
- .action(() => {
85
- const mode = isLocalMode();
86
- const envFilePath = path.join(process.cwd(), LOCAL_ENV_FILE);
87
- const hasLocalFile = fs.existsSync(envFilePath);
88
- console.log(chalk_1.default.bold("\nMode Status:\n"));
89
- console.log(` Mode : ${mode ? chalk_1.default.yellow("🔌 LOCAL (offline)") : chalk_1.default.green("☁ CLOUD")}`);
90
- console.log(` .env.local : ${hasLocalFile ? chalk_1.default.green("Found") : chalk_1.default.gray("Not found")}`);
91
- console.log(` Config flag : ${chalk_1.default.gray((0, config_1.getConfigValue)("localMode") ? "true" : "false")}`);
92
- console.log(` Env var : ${chalk_1.default.gray(process.env.XTRA_LOCAL_MODE || "(not set)")}`);
93
- console.log();
94
- if (!mode) {
95
- console.log(chalk_1.default.gray(" Run 'xtra local on' to switch to offline mode."));
96
- }
97
- else {
98
- console.log(chalk_1.default.gray(" Run 'xtra local off' to switch back to cloud mode."));
99
- if (!hasLocalFile) {
100
- console.log(chalk_1.default.yellow(" ⚠ No .env.local file found. Run 'xtra local sync' to pull secrets."));
101
- }
102
- }
103
- console.log();
104
- });
105
- // ── on ────────────────────────────────────────────────────────────────────────
106
- exports.localCommand
107
- .command("on")
108
- .description("Enable local mode — secrets read from .env.local")
109
- .action(() => {
110
- (0, config_1.setConfig)("localMode", true);
111
- console.log(chalk_1.default.yellow("🔌 Local mode ENABLED."));
112
- console.log(chalk_1.default.gray(" 'xtra run' will now read from .env.local instead of the cloud."));
113
- const localFilePath = path.join(process.cwd(), LOCAL_ENV_FILE);
114
- if (!fs.existsSync(localFilePath)) {
115
- console.log(chalk_1.default.yellow(` ⚠ No .env.local file found. Run 'xtra local sync' to populate it.`));
116
- }
117
- });
118
- // ── off ───────────────────────────────────────────────────────────────────────
119
- exports.localCommand
120
- .command("off")
121
- .description("Disable local mode — secrets fetched from cloud again")
122
- .action(() => {
123
- (0, config_1.setConfig)("localMode", false);
124
- console.log(chalk_1.default.green("☁ Cloud mode ENABLED."));
125
- console.log(chalk_1.default.gray(" 'xtra run' will now fetch secrets from XtraSecurity Cloud."));
126
- });
127
- // ── sync ─────────────────────────────────────────────────────────────────────
128
- exports.localCommand
129
- .command("sync")
130
- .description("Pull cloud secrets to .env.local for offline use")
131
- .option("-p, --project <id>", "Project ID")
132
- .option("-e, --env <environment>", "Environment", "development")
133
- .option("-b, --branch <branch>", "Branch", "main")
134
- .option("-o, --output <file>", "Output file", LOCAL_ENV_FILE)
135
- .option("--overwrite", "Overwrite existing file without prompt", false)
136
- .action(async (options) => {
137
- let { project, env, branch, output, overwrite } = options;
138
- const envMap = { dev: "development", stg: "staging", prod: "production" };
139
- env = envMap[env] || env;
140
- if (!project)
141
- project = (0, config_1.getRcConfig)().project;
142
- if (!project) {
143
- console.error(chalk_1.default.red("Error: Project ID required. Use -p <id>."));
144
- process.exit(1);
145
- }
146
- const outputPath = path.resolve(process.cwd(), output);
147
- // Warn if production
148
- if (env === "production" && !overwrite) {
149
- console.log(chalk_1.default.red(`⚠ You are syncing PRODUCTION secrets to ${output}.`));
150
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
151
- const confirmed = await new Promise(resolve => {
152
- rl.question(chalk_1.default.yellow("Type 'yes' to confirm: "), (ans) => {
153
- rl.close();
154
- resolve(ans.trim().toLowerCase() === "yes");
155
- });
156
- });
157
- if (!confirmed) {
158
- console.log(chalk_1.default.gray("Sync cancelled."));
159
- return;
160
- }
161
- }
162
- const spinner = (0, ora_1.default)(`Fetching ${env} secrets from cloud...`).start();
163
- try {
164
- const secrets = await api_1.api.getSecrets(project, env, branch);
165
- spinner.stop();
166
- const count = Object.keys(secrets).length;
167
- if (count === 0) {
168
- console.log(chalk_1.default.yellow("No secrets found."));
169
- return;
170
- }
171
- // Write dotenv format
172
- const lines = [
173
- `# xtra local sync — ${env}/${branch}`,
174
- `# Generated: ${new Date().toISOString()}`,
175
- `# DO NOT COMMIT THIS FILE`,
176
- "",
177
- ...Object.entries(secrets).map(([k, v]) => `${k}=${v}`)
178
- ];
179
- fs.writeFileSync(outputPath, lines.join("\n") + "\n", "utf8");
180
- console.log(chalk_1.default.green(`✅ Synced ${count} secrets to ${output}`));
181
- console.log(chalk_1.default.gray(" Run 'xtra local on' to switch to local mode."));
182
- // Remind about .gitignore
183
- const gitignorePath = path.join(process.cwd(), ".gitignore");
184
- let gitignore = "";
185
- try {
186
- gitignore = fs.readFileSync(gitignorePath, "utf8");
187
- }
188
- catch (_) { }
189
- if (!gitignore.includes(output)) {
190
- console.log(chalk_1.default.yellow(`\n ⚠ Remember to add '${output}' to your .gitignore!`));
191
- }
192
- }
193
- catch (e) {
194
- spinner.fail("Sync failed.");
195
- console.error(chalk_1.default.red(e?.response?.data?.error || e.message));
196
- process.exit(1);
197
- }
198
- });
1
+ var G=Object.create;var m=Object.defineProperty;var J=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var X=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty;var z=(e,t)=>{for(var s in t)m(e,s,{get:t[s],enumerable:!0})},L=(e,t,s,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of V(t))!H.call(e,r)&&r!==s&&m(e,r,{get:()=>t[r],enumerable:!(o=J(t,r))||o.enumerable});return e};var i=(e,t,s)=>(s=e!=null?G(X(e)):{},L(t||!e||!e.__esModule?m(s,"default",{value:e,enumerable:!0}):s,e)),K=e=>L(m({},"__esModule",{value:!0}),e);var W={};z(W,{localCommand:()=>f});module.exports=K(W);var E=require("commander");var U=i(require("axios"));var R=i(require("conf")),u=i(require("fs")),P=i(require("path")),x="https://www.xtrasecurity.in/api",c=new R.default({projectName:"xtra-cli",defaults:{apiUrl:x}}),$=c.get("apiUrl");$&&!process.env.XTRA_API_URL&&($.includes("localhost")||$.includes("xtra-security.vercel.app"))&&c.set("apiUrl",x);var y=()=>({...c.store,apiUrl:process.env.XTRA_API_URL||c.get("apiUrl")||x}),j=e=>c.get(e),S=(e,t)=>{if(c.set(e,t),e==="token"&&process.platform!=="win32"&&u.default.existsSync(c.path))try{u.default.chmodSync(c.path,384)}catch{}};var A=()=>c.get("token");var O=()=>{let e={};try{let t=P.default.join(process.cwd(),".xtrarc");u.default.existsSync(t)&&(e=JSON.parse(u.default.readFileSync(t,"utf-8")))}catch{}return{project:e.project||c.get("project")||"",env:e.env||c.get("env")||"development",branch:e.branch||c.get("branch")||"main"}};var T=i(require("fs")),_=i(require("path")),Y=JSON.parse(T.readFileSync(_.join(__dirname,"../../package.json"),"utf-8")),n=()=>{let{apiUrl:e}=y(),t=A();return U.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":Y.version,...t?{Authorization:`Bearer ${t}`}:{}}})},D={login:async(e,t,s)=>(await n().post("/auth/cli-login",{email:e,password:t,apiKey:s})).data,getSecrets:async(e,t,s="main")=>(await n().get(`/projects/${e}/envs/${t}/secrets?branch=${s}`)).data,getSecretVersions:async(e,t,s="main")=>(await n().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(e,t,s,o,r="main")=>(await n().post(`/projects/${e}/envs/${t}/secrets`,{secrets:s,expectedVersions:o,branch:r})).data,getSecretDetails:async(e,t,s,o="main")=>(await n().get(`/projects/${e}/envs/${t}/secrets/${s}?branch=${o}`)).data,syncLogs:async e=>(await n().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,s,o,r,p)=>(await n().post(`/projects/${e}/envs/${t}/secrets/link`,{key:s,sourceProjectId:o,sourceEnv:r,sourceKey:p})).data,rotateSecret:async(e,t,s,o,r)=>(await n().post(`/projects/${e}/envs/${t}/secrets/${s}/rotate`,{strategy:o,parsedNewValue:r})).data,promoteSecret:async(e,t,s)=>(await n().post(`/projects/${e}/envs/${t}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await n().get("/audit/verify")).data,exportAuditLogs:async(e,t,s,o)=>{let r={format:e};return t&&(r.startDate=t),s&&(r.endDate=s),o&&(r.projectId=o),(await n().post("/audit/export",r,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,s,o)=>(await n().post("/access/request",{projectId:e,secretId:o,reason:t,duration:s})).data,approveAccess:async(e,t)=>(await n().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=y(),s=t?`&workspaceId=${t}`:"";return(await n().get(`/access/list?mode=${e}${s}`)).data},getAccessRequestStatus:async e=>(await n().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=y(),t=e?`/project?workspaceId=${e}`:"/project";return(await n().get(t)).data},getBranches:async e=>(await n().get(`/branch?projectId=${e}`)).data,createBranch:async(e,t,s)=>(await n().post("/branch",{projectId:e,name:t,description:s})).data,deleteBranch:async e=>(await n().delete(`/branch/${e}`)).data,updateBranch:async(e,t)=>(await n().put("/branch",{id:e,...t})).data,getRoles:async()=>(await n().get("/admin/roles")).data,getUsers:async e=>{let t=e?`?teamId=${e}`:"";return(await n().get(`/admin/users${t}`)).data},setUserRole:async(e,t,s)=>(await n().put("/admin/users/role",{email:e,role:t,teamId:s})).data,getIntegrationStatus:async e=>(await n().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await n().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await n().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,t)=>{let s=new URLSearchParams(t).toString();return(await n().get(`/projects/${e}/kubernetes?${s}`)).data},getSecretHistory:async(e,t,s)=>(await n().get(`/projects/${e}/envs/${t}/secrets/${s}/history`)).data,rollbackSecret:async(e,t,s,o)=>(await n().post(`/projects/${e}/envs/${t}/secrets/${s}/history`,{version:o})).data,cloneEnvironment:async(e,t,s,o,r)=>(await n().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:s,overwrite:o,branch:r})).data,generateJitLink:async e=>(await n().post("/jit/generate",e)).data,claimJitLink:async e=>(await n().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await n().get(`/jit/${e}`)).data,post:async(e,t)=>(await n().post(e,t)).data};var a=i(require("chalk")),N=i(require("ora")),g=i(require("fs")),d=i(require("path")),M=i(require("readline")),b=".env.local";function Q(){return j("localMode")===!0||process.env.XTRA_LOCAL_MODE==="true"}var f=new E.Command("local").description("Toggle cloud/local mode for offline development").addHelpText("after",`
2
+ In local mode, 'xtra run' reads secrets from .env.local instead of the cloud API.
3
+ This allows fully offline development without any API calls.
4
+
5
+ Examples:
6
+ $ xtra local status # Check current mode
7
+ $ xtra local on # Enable offline (local) mode
8
+ $ xtra local off # Disable \xE2\u20AC\u201D back to cloud mode
9
+ $ xtra local sync # Pull cloud secrets \xE2\u2020\u2019 .env.local
10
+ $ xtra local sync -p proj -e production # Pull production to .env.local
11
+ `);f.command("status").description("Show current cloud/local mode").action(()=>{let e=Q(),t=d.join(process.cwd(),b),s=g.existsSync(t);console.log(a.default.bold(`
12
+ Mode Status:
13
+ `)),console.log(` Mode : ${e?a.default.yellow("\u{1F50C} LOCAL (offline)"):a.default.green("\u2601 CLOUD")}`),console.log(` .env.local : ${s?a.default.green("Found"):a.default.gray("Not found")}`),console.log(` Config flag : ${a.default.gray(j("localMode")?"true":"false")}`),console.log(` Env var : ${a.default.gray(process.env.XTRA_LOCAL_MODE||"(not set)")}`),console.log(),e?(console.log(a.default.gray(" Run 'xtra local off' to switch back to cloud mode.")),s||console.log(a.default.yellow(" \u26A0 No .env.local file found. Run 'xtra local sync' to pull secrets."))):console.log(a.default.gray(" Run 'xtra local on' to switch to offline mode.")),console.log()});f.command("on").description("Enable local mode \xE2\u20AC\u201D secrets read from .env.local").action(()=>{S("localMode",!0),console.log(a.default.yellow("\u{1F50C} Local mode ENABLED.")),console.log(a.default.gray(" 'xtra run' will now read from .env.local instead of the cloud."));let e=d.join(process.cwd(),b);g.existsSync(e)||console.log(a.default.yellow(" \u26A0 No .env.local file found. Run 'xtra local sync' to populate it."))});f.command("off").description("Disable local mode \xE2\u20AC\u201D secrets fetched from cloud again").action(()=>{S("localMode",!1),console.log(a.default.green("\xE2\u02DC\x81 Cloud mode ENABLED.")),console.log(a.default.gray(" 'xtra run' will now fetch secrets from XtraSecurity Cloud."))});f.command("sync").description("Pull cloud secrets to .env.local for offline use").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment","development").option("-b, --branch <branch>","Branch","main").option("-o, --output <file>","Output file",b).option("--overwrite","Overwrite existing file without prompt",!1).action(async e=>{let{project:t,env:s,branch:o,output:r,overwrite:p}=e;s={dev:"development",stg:"staging",prod:"production"}[s]||s,t||(t=O().project),t||(console.error(a.default.red("Error: Project ID required. Use -p <id>.")),process.exit(1));let B=d.resolve(process.cwd(),r);if(s==="production"&&!p){console.log(a.default.red(`\u26A0 You are syncing PRODUCTION secrets to ${r}.`));let l=M.createInterface({input:process.stdin,output:process.stdout});if(!await new Promise(h=>{l.question(a.default.yellow("Type 'yes' to confirm: "),v=>{l.close(),h(v.trim().toLowerCase()==="yes")})})){console.log(a.default.gray("Sync cancelled."));return}}let C=(0,N.default)(`Fetching ${s} secrets from cloud...`).start();try{let l=await D.getSecrets(t,s,o);C.stop();let w=Object.keys(l).length;if(w===0){console.log(a.default.yellow("No secrets found."));return}let h=[`# xtra local sync \xE2\u20AC\u201D ${s}/${o}`,`# Generated: ${new Date().toISOString()}`,"# DO NOT COMMIT THIS FILE","",...Object.entries(l).map(([I,q])=>`${I}=${q}`)];g.writeFileSync(B,h.join(`
14
+ `)+`
15
+ `,"utf8"),console.log(a.default.green(`\u2705 Synced ${w} secrets to ${r}`)),console.log(a.default.gray(" Run 'xtra local on' to switch to local mode."));let v=d.join(process.cwd(),".gitignore"),k="";try{k=g.readFileSync(v,"utf8")}catch{}k.includes(r)||console.log(a.default.yellow(`
16
+ \u26A0 Remember to add '${r}' to your .gitignore!`))}catch(l){C.fail("Sync failed."),console.error(a.default.red(l?.response?.data?.error||l.message)),process.exit(1)}});0&&(module.exports={localCommand});