xtra-cli 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/xtra.js +2 -294
- package/dist/commands/access.js +1 -184
- package/dist/commands/admin.js +5 -118
- package/dist/commands/audit.js +1 -67
- package/dist/commands/branch.js +1 -212
- package/dist/commands/checkout.js +1 -80
- package/dist/commands/ci.js +31 -341
- package/dist/commands/completion.js +85 -224
- package/dist/commands/diff.js +6 -162
- package/dist/commands/doctor.js +2 -164
- package/dist/commands/env.js +2 -70
- package/dist/commands/export.js +2 -83
- package/dist/commands/generate.js +3 -179
- package/dist/commands/history.js +4 -77
- package/dist/commands/import.js +1 -121
- package/dist/commands/init.js +7 -205
- package/dist/commands/integration.js +11 -188
- package/dist/commands/jit-run.js +5 -0
- package/dist/commands/local.js +16 -198
- package/dist/commands/login.js +16 -198
- package/dist/commands/login.test.js +16 -51
- package/dist/commands/logout.js +1 -0
- package/dist/commands/logs.js +3 -121
- package/dist/commands/profile.js +14 -184
- package/dist/commands/project.js +1 -165
- package/dist/commands/rollback.js +1 -95
- package/dist/commands/rotate.js +3 -93
- package/dist/commands/run.js +11 -215
- package/dist/commands/scan.js +25 -127
- package/dist/commands/secrets.js +14 -305
- package/dist/commands/service-account.js +7 -0
- package/dist/commands/simulate.js +6 -109
- package/dist/commands/status.js +2 -93
- package/dist/commands/template.js +22 -276
- package/dist/commands/ui.js +2 -289
- package/dist/commands/watch.js +17 -123
- package/dist/lib/api.js +1 -187
- package/dist/lib/api.test.js +1 -89
- package/dist/lib/audit.js +1 -136
- package/dist/lib/config.js +1 -72
- package/dist/lib/config.test.js +1 -47
- package/dist/lib/crypto.js +1 -50
- package/dist/lib/manifest.js +1 -52
- package/dist/lib/profiles.js +1 -103
- package/package.json +2 -2
package/dist/bin/xtra.js
CHANGED
|
@@ -1,296 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var es=Object.create;var dt=Object.defineProperty;var ts=Object.getOwnPropertyDescriptor;var os=Object.getOwnPropertyNames;var rs=Object.getPrototypeOf,ns=Object.prototype.hasOwnProperty;var He=(e,t)=>()=>(e&&(t=e(e=0)),t);var We=(e,t)=>{for(var r in t)dt(e,r,{get:t[r],enumerable:!0})},Co=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of os(t))!ns.call(e,n)&&n!==r&&dt(e,n,{get:()=>t[n],enumerable:!(o=ts(t,n))||o.enumerable});return e};var m=(e,t,r)=>(r=e!=null?es(rs(e)):{},Co(t||!e||!e.__esModule?dt(r,"default",{value:e,enumerable:!0}):r,e)),L=e=>Co(dt({},"__esModule",{value:!0}),e);var Ye={};We(Ye,{clearConfig:()=>ss,deleteConfig:()=>mt,getAuthToken:()=>Xe,getConfig:()=>re,getConfigValue:()=>M,getProjectConfig:()=>as,getRcConfig:()=>y,setConfig:()=>U});var Eo,Se,Ft,Mt,Q,_t,re,M,U,ss,mt,Xe,as,y,C=He(()=>{Eo=m(require("conf")),Se=m(require("fs")),Ft=m(require("path")),Mt="https://www.xtrasecurity.in/api",Q=new Eo.default({projectName:"xtra-cli",defaults:{apiUrl:Mt}}),_t=Q.get("apiUrl");_t&&!process.env.XTRA_API_URL&&(_t.includes("localhost")||_t.includes("xtra-security.vercel.app"))&&Q.set("apiUrl",Mt);re=()=>({...Q.store,apiUrl:process.env.XTRA_API_URL||Q.get("apiUrl")||Mt}),M=e=>Q.get(e),U=(e,t)=>{if(Q.set(e,t),e==="token"&&process.platform!=="win32"&&Se.default.existsSync(Q.path))try{Se.default.chmodSync(Q.path,384)}catch{}},ss=()=>Q.clear(),mt=e=>Q.delete(e),Xe=()=>Q.get("token"),as=async()=>{try{let e=Ft.default.join(process.cwd(),"xtra.json");if(Se.default.existsSync(e)){let t=Se.default.readFileSync(e,"utf-8");return JSON.parse(t)}return null}catch{return null}},y=()=>{let e={};try{let t=Ft.default.join(process.cwd(),".xtrarc");Se.default.existsSync(t)&&(e=JSON.parse(Se.default.readFileSync(t,"utf-8")))}catch{}return{project:e.project||Q.get("project")||"",env:e.env||Q.get("env")||"development",branch:e.branch||Q.get("branch")||"main"}}});var ft={};We(ft,{api:()=>g});var ko,Io,Ao,is,E,g,I=He(()=>{ko=m(require("axios"));C();Io=m(require("fs")),Ao=m(require("path")),is=JSON.parse(Io.readFileSync(Ao.join(__dirname,"../../package.json"),"utf-8")),E=()=>{let{apiUrl:e}=re(),t=Xe();return ko.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":is.version,...t?{Authorization:`Bearer ${t}`}:{}}})},g={login:async(e,t,r)=>(await E().post("/auth/cli-login",{email:e,password:t,apiKey:r})).data,getSecrets:async(e,t,r="main")=>(await E().get(`/projects/${e}/envs/${t}/secrets?branch=${r}`)).data,getSecretVersions:async(e,t,r="main")=>(await E().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${r}`)).data,setSecrets:async(e,t,r,o,n="main")=>(await E().post(`/projects/${e}/envs/${t}/secrets`,{secrets:r,expectedVersions:o,branch:n})).data,getSecretDetails:async(e,t,r,o="main")=>(await E().get(`/projects/${e}/envs/${t}/secrets/${r}?branch=${o}`)).data,syncLogs:async e=>(await E().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,r,o,n,s)=>(await E().post(`/projects/${e}/envs/${t}/secrets/link`,{key:r,sourceProjectId:o,sourceEnv:n,sourceKey:s})).data,rotateSecret:async(e,t,r,o,n)=>(await E().post(`/projects/${e}/envs/${t}/secrets/${r}/rotate`,{strategy:o,parsedNewValue:n})).data,promoteSecret:async(e,t,r)=>(await E().post(`/projects/${e}/envs/${t}/secrets/${r}/promote`,{})).data,verifyAuditLogs:async()=>(await E().get("/audit/verify")).data,exportAuditLogs:async(e,t,r,o)=>{let n={format:e};return t&&(n.startDate=t),r&&(n.endDate=r),o&&(n.projectId=o),(await E().post("/audit/export",n,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,r,o)=>(await E().post("/access/request",{projectId:e,secretId:o,reason:t,duration:r})).data,approveAccess:async(e,t)=>(await E().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=re(),r=t?`&workspaceId=${t}`:"";return(await E().get(`/access/list?mode=${e}${r}`)).data},getAccessRequestStatus:async e=>(await E().get(`/access/request?id=${e}`)).data,getProjects:async()=>{let{workspace:e}=re(),t=e?`/project?workspaceId=${e}`:"/project";return(await E().get(t)).data},getBranches:async e=>(await E().get(`/branch?projectId=${e}`)).data,createBranch:async(e,t,r)=>(await E().post("/branch",{projectId:e,name:t,description:r})).data,deleteBranch:async e=>(await E().delete(`/branch/${e}`)).data,updateBranch:async(e,t)=>(await E().put("/branch",{id:e,...t})).data,getRoles:async()=>(await E().get("/admin/roles")).data,getUsers:async e=>{let t=e?`?teamId=${e}`:"";return(await E().get(`/admin/users${t}`)).data},setUserRole:async(e,t,r)=>(await E().put("/admin/users/role",{email:e,role:t,teamId:r})).data,getIntegrationStatus:async e=>(await E().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await E().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await E().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,t)=>{let r=new URLSearchParams(t).toString();return(await E().get(`/projects/${e}/kubernetes?${r}`)).data},getSecretHistory:async(e,t,r)=>(await E().get(`/projects/${e}/envs/${t}/secrets/${r}/history`)).data,rollbackSecret:async(e,t,r,o)=>(await E().post(`/projects/${e}/envs/${t}/secrets/${r}/history`,{version:o})).data,cloneEnvironment:async(e,t,r,o,n)=>(await E().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:r,overwrite:o,branch:n})).data,generateJitLink:async e=>(await E().post("/jit/generate",e)).data,claimJitLink:async e=>(await E().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await E().get(`/jit/${e}`)).data,post:async(e,t)=>(await E().post(e,t)).data}});var Vt={};We(Vt,{decrypt:()=>ne,encrypt:()=>Ge,hash:()=>qt});function To(){let e=Ro.get("masterKey");e||(e=Ce.default.randomBytes(32).toString("hex"),Ro.set("masterKey",e));let t=(0,Oo.machineIdSync)();return Ce.default.createHash("sha256").update(e+t).digest()}var Ce,Po,Oo,Do,Ro,Ge,ne,qt,ge=He(()=>{Ce=m(require("crypto")),Po=m(require("conf")),Oo=require("node-machine-id"),Do="aes-256-gcm",Ro=new Po.default({projectName:"xtra-cli-crypto"});Ge=e=>{let t=Ce.default.randomBytes(16),r=To(),o=Ce.default.createCipheriv(Do,r,t),n=o.update(e,"utf8","hex");n+=o.final("hex");let s=o.getAuthTag();return{iv:t.toString("hex"),content:n,tag:s.toString("hex")}},ne=e=>{let t=To(),r=Buffer.from(e.iv,"hex"),o=Buffer.from(e.tag,"hex"),n=Ce.default.createDecipheriv(Do,t,r);n.setAuthTag(o);let s=n.update(e.content,"hex","utf8");return s+=n.final("utf8"),s},qt=e=>Ce.default.createHash("sha256").update(e).digest("hex")});var V={};We(V,{loadAuditLogs:()=>_e,logAudit:()=>ls,saveAuditLogs:()=>ut,syncAuditLogs:()=>ps});function cs(e){if(!e||typeof e!="object")return e;let t={...e},r=["password","token","secret","value","apiKey","secretValue"];for(let o of Object.keys(t))r.some(n=>o.toLowerCase().includes(n))&&(t[o]="[REDACTED]");return t}async function Lo(e){try{if(!re().token)return!1;let r={action:e.action,entity:"cli-project",entityId:e.projectId||"global",details:cs(e.details),timestamp:e.timestamp,projectId:e.projectId};return await g.post("/audit/cli-logs",{logs:[r]}),!0}catch(t){return process.env.DEBUG&&console.error("Failed to send audit log to backend:",t),!1}}function ls(e,t,r,o){let n={id:(0,No.v4)(),timestamp:new Date().toISOString(),action:e,projectId:t,environment:r,details:o,synced:!1},s=_e();s.push(n),ut(s),Lo(n).then(a=>{if(a){n.synced=!0;let i=_e(),p=i.findIndex(c=>c.id===n.id);p!==-1&&(i[p].synced=!0,ut(i))}}).catch(()=>{})}function _e(){if(!Ue.default.existsSync(Kt))return[];try{let e=Ue.default.readFileSync(Kt,"utf-8"),t=JSON.parse(e),r=ne(t);return JSON.parse(r)}catch{return[]}}function ut(e){Ue.default.existsSync(Bt)||Ue.default.mkdirSync(Bt,{recursive:!0});let t=JSON.stringify(e),r=Ge(t);Ue.default.writeFileSync(Kt,JSON.stringify(r),"utf-8")}async function ps(){let e=_e(),t=e.filter(o=>!o.synced);if(t.length===0)return 0;let r=0;for(let o of t)await Lo(o)&&(o.synced=!0,r++);return r>0&&ut(e),r}var Ue,Jt,No,Bt,Kt,F=He(()=>{Ue=m(require("fs")),Jt=m(require("path")),No=require("uuid");ge();C();I();Bt=Jt.default.join(process.cwd(),".xtra"),Kt=Jt.default.join(Bt,"audit.enc")});var Xt={};We(Xt,{MANIFEST_DIR:()=>ht,MANIFEST_FILE:()=>yt,loadManifest:()=>vt,saveManifest:()=>Ko,updateManifest:()=>ds});function vt(){if(!Me.default.existsSync(yt))return null;try{let e=Me.default.readFileSync(yt,"utf-8");return JSON.parse(e)}catch{return null}}function Ko(e){Me.default.existsSync(ht)||Me.default.mkdirSync(ht,{recursive:!0}),Me.default.writeFileSync(yt,JSON.stringify(e,null,2),"utf-8")}function ds(e,t,r,o){let n=vt()||{projectId:e,environment:t,lastSyncedAt:new Date().toISOString(),secrets:{}};n.projectId=e,n.environment=t,n.lastSyncedAt=new Date().toISOString(),Object.entries(r).forEach(([s,a])=>{let i=o(a);n.secrets[s]={hash:i,updatedAt:new Date().toISOString()}}),Ko(n)}var Me,Wt,ht,yt,wt=He(()=>{Me=m(require("fs")),Wt=m(require("path")),ht=Wt.default.join(process.cwd(),".xtra"),yt=Wt.default.join(ht,"manifest.json")});var Kn=require("commander"),Jn=m(require("fs")),Hn=m(require("path")),Wn=m(require("update-notifier"));var Mo=require("commander"),ze=m(require("inquirer"));I();C();var Ee=m(require("chalk")),Ht=m(require("ora")),Fo=new Mo.Command("login").description("Authenticate with XtraSecurity").option("-k, --key <key>","Login using an Access Key").option("-e, --email <email>","Login using Email").option("--sso","Login via Web (SSO)").action(async e=>{try{if(!e.key&&!e.email&&!e.sso){let{method:t}=await ze.default.prompt([{type:"list",name:"method",message:"How would you like to login?",choices:[{name:"Browser Login (SSO)",value:"sso"},{name:"Access Key",value:"key"},{name:"Email & Password",value:"email"}]}]);if(t==="sso"){await _o();return}else if(t==="key"){let{key:r}=await ze.default.prompt([{type:"password",name:"key",message:"Enter Access Key:"}]);await Uo(r);return}}if(e.sso)await _o();else if(e.key)await Uo(e.key);else{let{email:t}=e;t||(t=(await ze.default.prompt([{type:"input",name:"email",message:"Email:"}])).email);let o=(await ze.default.prompt([{type:"password",name:"password",message:"Password:"}])).password,n=(0,Ht.default)("Authenticating...").start(),s=await g.login(t,o);U("token",s.token),n.succeed("Logged in successfully!");try{let{logAudit:a}=await Promise.resolve().then(()=>(F(),V));a("USER_LOGIN",null,null,{method:"email",email:t})}catch{}}}catch(t){t.response?console.error(Ee.default.red(`
|
|
3
|
-
Error: ${t.response.data.error||"Login failed"}`)):console.error(Ee.default.red(`
|
|
4
|
-
Error: ${t.message}`)),process.exit(1)}});async function Uo(e){let t=(0,Ht.default)("Authenticating with Access Key...").start(),r=await g.login(void 0,void 0,e);U("token",r.token),r.user&&console.log(Ee.default.green(`
|
|
5
|
-
Logged in as ${r.user.email}`)),t.succeed("Logged in successfully!");try{let{logAudit:o}=await Promise.resolve().then(()=>(F(),V));o("USER_LOGIN",null,null,{method:"api_key",email:r.user?.email})}catch{}}async function _o(){let e=require("http"),{default:t}=await import("open");return new Promise(r=>{let o=e.createServer(async(n,s)=>{let a=new URL(n.url,`http://${n.headers.host}`),i=a.searchParams.get("token"),p=a.searchParams.get("email"),c=a.searchParams.get("workspaceId"),l=a.searchParams.get("workspaceName");if(i){U("token",i),c&&U("workspace",c),s.writeHead(200,{"Content-Type":"text/html"}),s.end(`
|
|
6
|
-
<html>
|
|
7
|
-
<body style="background:#111;color:#eee;font-family:sans-serif;text-align:center;padding:50px;">
|
|
8
|
-
<h1>Login Successful</h1>
|
|
9
|
-
<p>You have successfully logged in to XtraSync.</p>
|
|
10
|
-
<p>You can now close this tab and return to your terminal.</p>
|
|
11
|
-
<script>
|
|
12
|
-
setTimeout(function() { window.close(); }, 1000);
|
|
13
|
-
</script>
|
|
14
|
-
</body>
|
|
15
|
-
</html>
|
|
16
|
-
`),console.log(Ee.default.green(`
|
|
17
|
-
\u2714 SS0 Login Successful! Logged in as ${p}`)),c&&console.log(Ee.default.dim(`Selected Workspace: ${l} (${c})`));try{let{logAudit:f}=await Promise.resolve().then(()=>(F(),V));f("USER_LOGIN",null,null,{method:"sso",email:p,workspaceId:c,workspaceName:l})}catch{}s.end(()=>{o.close(),setTimeout(()=>{process.exit(0)},1e3)})}else s.writeHead(400),s.end("Missing token")});o.listen(0,async()=>{let n=o.address(),a=`http://localhost:${typeof n=="string"?0:n?.port}`,{apiUrl:i}=(C(),L(Ye)).getConfig();i.includes("localhost")&&(i="https://www.xtrasecurity.in/api");let p=`${i}/auth/cli/sso?callbackUrl=${encodeURIComponent(a)}`;console.log(Ee.default.blue(`Waiting for browser login... (Opening ${p})`)),await t(p)})})}var qo=require("commander"),gt=m(require("chalk")),Vo=m(require("ora"));C();var Bo=new qo.Command("logout").description("Logout and clear all local credentials and caches").action(async()=>{let e=(0,Vo.default)("Logging out...").start();try{mt("token");let t=re(),o=Object.keys(t).filter(n=>n.startsWith("cache."));for(let n of o)mt(n);e.succeed(gt.default.green("\u2714 Successfully logged out.")),console.log(gt.default.gray("Local authentication token and secret caches have been purged."))}catch(t){e.fail(gt.default.red(`Logout failed: ${t.message}`))}});var Jo=require("commander");I();var Ho=require("child_process"),z=m(require("chalk")),Wo=m(require("ora"));C();ge();var bt=m(require("fs")),Xo=m(require("path")),Yo=m(require("dotenv")),Go=new Jo.Command("run").description("Run a command with injected secrets").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (development, staging, production)","development").option("-b, --branch <branchName>","Branch Name").option("--shell","Enable shell mode (needed for npm run, shell built-ins on Windows)",!1).argument("<command>","Command to run").argument("[args...]","Command arguments").addHelpText("after",`
|
|
18
|
-
Examples:
|
|
19
|
-
$ xtra run npm start
|
|
20
|
-
$ xtra run -e production -- npm run build
|
|
21
|
-
$ xtra run -p proj_123 -b feature-branch -- python script.py
|
|
22
|
-
$ xtra run --shell "echo $SECRET_KEY"
|
|
23
|
-
`).action(async(e,t,r)=>{let{project:o,env:n,branch:s,shell:a}=r,i=y();s||(s=i.branch),n={dev:"development",stg:"staging",prod:"production"}[n]||n||i.env,o||(o=i.project),o||(console.error(z.default.red("Error: Project ID is required. Use -p <id> or checkout a branch.")),process.exit(1));let c=(0,Wo.default)(`Fetching secrets for ${n} (branch: ${s})...`).start(),l=null;if(M("localMode")===!0||process.env.XTRA_LOCAL_MODE==="true"){let v=Xo.resolve(process.cwd(),".env.local");bt.existsSync(v)||(c.fail("Local mode is ON but .env.local not found. Run 'xtra local sync' first."),process.exit(1)),l=Yo.default.parse(bt.readFileSync(v,"utf8")),c.succeed(z.default.yellow(`\u{1F50C} Local mode: loaded ${Object.keys(l).length} secrets from .env.local`))}else try{if(l=await g.getSecrets(o,n,s),!l||Object.keys(l).length===0)c.warn(z.default.yellow("No secrets found for this environment."));else{c.succeed(`Loaded ${Object.keys(l).length} secrets (Online).`);try{let{hash:v}=(ge(),L(Vt)),{updateManifest:A}=(wt(),L(Xt));A(o,n,l,v)}catch{}try{let v=`cache.${o}.${n}.${s}`,A=Ge(JSON.stringify(l));U(v,A)}catch{console.error(z.default.yellow("Warning: Failed to cache secrets."))}}}catch(v){let A=`cache.${o}.${n}.${s}`;if(v.response&&[401,403,404].includes(v.response.status)){try{let{deleteConfig:X}=(C(),L(Ye));X&&X(A)}catch{}c.fail("Access Denied."),v.response.status===404?console.error(z.default.red(`
|
|
24
|
-
Error: Project '${o}' not found or your access has been revoked.`)):v.response.status===401?console.error(z.default.red(`
|
|
25
|
-
Error: Unauthorized. Please run 'xtra login' again.`)):console.error(z.default.red(`
|
|
26
|
-
Error: ${v.response.data.error||"You do not have permission to access these secrets."}`)),process.exit(1)}let G=M(A);if(G)try{let X=ne(G);l=JSON.parse(X),c.warn(z.default.yellow(`Offline Mode: Loaded ${Object.keys(l).length} secrets from cache.`))}catch{c.fail("Failed to fetch secrets and cache is corrupt."),process.exit(1)}else c.fail("Failed to fetch secrets and no local cache available."),console.error(z.default.dim(`
|
|
27
|
-
Details: ${v.message}`)),process.exit(1)}try{let{logAudit:v}=(F(),L(V));l&&Object.keys(l).length>0&&v("SECRET_ACCESS",o,n,{method:"run",keys:Object.keys(l),branch:s})}catch(v){console.error("Audit Error:",v)}let d={...process.env,...l};if(!a&&/[;&|`$<>\\\n]/g.test(e)&&(console.error(z.default.red("Error: Command contains unsafe characters. Use --shell if intentional.")),process.exit(1)),n==="production"){let v=require("inquirer"),{confirm:A}=await v.prompt([{type:"confirm",name:"confirm",message:z.default.red(`\u26A0 You are about to run a command in PRODUCTION with ${Object.keys(l||{}).length} injected secrets. Continue?`),default:!1}]);if(!A){console.log(z.default.yellow("Aborted."));return}}console.log(z.default.gray(`> ${e} ${t.join(" ")}`));let h=process.platform==="win32",w=(0,Ho.spawn)(e,t,{env:d,stdio:"inherit",shell:a||h}),j=()=>{if(l)for(let v in l)l[v]="**********"};process.on("SIGINT",()=>{j(),w&&w.kill("SIGINT"),process.exit(0)}),process.on("SIGTERM",()=>{j(),w&&w.kill("SIGTERM"),process.exit(0)}),w.on("exit",v=>{j(),process.exit(v??0)}),w.on("error",v=>{console.error(z.default.red(`Failed to start command: ${v.message}`)),j(),process.exit(1)})});var zo=require("commander");I();var R=m(require("chalk")),Qe=m(require("ora")),Yt=require("table");C();var he=new zo.Command("secrets").description("Manage secrets (List, Set, Delete)").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (development, staging, production)","development").option("-b, --branch <branchName>","Branch Name");he.command("list").description("List all secrets for a project/environment").option("--show","Reveal secret values",!1).addHelpText("after",`
|
|
28
|
-
Examples:
|
|
29
|
-
$ xtra secrets list
|
|
30
|
-
$ xtra secrets list -e production --show
|
|
31
|
-
`).action(async e=>{let t=he.opts(),{project:r,env:o,branch:n}=t,s=y();r||(r=s.project),n||(n=s.branch),(!o||o==="development")&&(o=s.env),o={dev:"development",stg:"staging",prod:"production"}[o]||o;let{show:i}=e;r||(console.error(R.default.red("Error: Project ID is required. Use -p <id> or run `xtra init` first.")),process.exit(1));let p=(0,Qe.default)(`Fetching secrets for ${o} (branch: ${n})...`).start();try{let c=await g.getSecrets(r,o,n);if(p.stop(),!c||Object.keys(c).length===0){console.log(R.default.yellow("No secrets found."));return}i&&console.log(R.default.red("\u26A0 Warning: Secret values will be visible in your terminal and shell history!"));let l=[[R.default.bold("Key"),R.default.bold("Value"),R.default.bold("Env")]];Object.entries(c).forEach(([f,d])=>{l.push([f,i?d:"********",o])}),console.log((0,Yt.table)(l));try{let{logAudit:f}=await Promise.resolve().then(()=>(F(),V));f("SECRET_LIST",r,o,{branch:n,count:Object.keys(c).length})}catch{}}catch(c){p.fail("Failed to fetch secrets"),console.error(R.default.red(c.message))}});he.command("set").description("Set one or more secrets (KEY=VALUE)").argument("<secrets...>","Secrets to set (format: KEY=VALUE)").option("-f, --force","Force update (overwrite remote changes without warning)",!1).addHelpText("after",`
|
|
32
|
-
Examples:
|
|
33
|
-
$ xtra secrets set API_KEY=xyz
|
|
34
|
-
$ xtra secrets set DB_USER=admin DB_PASS=secret -e staging
|
|
35
|
-
`).action(async(e,t)=>{let r=he.opts(),{project:o,env:n,branch:s}=r;s||(s=M("branch")||"main"),n={dev:"development",stg:"staging",prod:"production"}[n]||n,o||(o=M("project")),o||(console.error(R.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let i={};if(e.forEach(c=>{let l=c.indexOf("=");if(l===-1){console.warn(R.default.yellow(`Skipping invalid format: ${c} (expected KEY=VALUE)`));return}let f=c.substring(0,l),d=c.substring(l+1);i[f]=d}),Object.keys(i).length===0){console.error(R.default.red("No valid secrets provided."));return}if(n==="production"&&!t.force){let c=require("inquirer"),{confirm:l}=await c.prompt([{type:"confirm",name:"confirm",message:R.default.red(`\u26A0 You are about to SET ${Object.keys(i).length} secret(s) in PRODUCTION. Are you sure?`),default:!1}]);if(!l){console.log(R.default.yellow("Aborted."));return}}let p=(0,Qe.default)(`Setting ${Object.keys(i).length} secrets for ${n} (branch: ${s})...`);t.force&&p.start();try{let c;if(!t.force){p.text="Fetching current versions...",p.start();try{let l=await g.getSecretVersions(o,n,s);c={},p.stop(),Object.keys(i).forEach(f=>{l[f]&&(c[f]=l[f].version)})}catch{p.warn(R.default.yellow("Could not fetch remote versions. Conflict detection disabled."))}}p.start("Updating secrets..."),await g.setSecrets(o,n,i,c,s),p.succeed("Secrets updated successfully.");try{let{logAudit:l}=(F(),L(V));l("SECRET_UPDATE",o,n,{keys:Object.keys(i),branch:s})}catch{}}catch(c){if(p.stop(),c.response&&c.response.status===409){let l=c.response.data.conflicts||[];console.log(R.default.red(`
|
|
36
|
-
\u26A0 Conflict Detected! The following secrets have changed remotely:
|
|
37
|
-
`));let f=[[R.default.bold("Key"),R.default.bold("Remote Version"),R.default.bold("Remote Value")]];l.forEach(h=>{f.push([h.key,h.actual,h.remoteValue])}),console.log((0,Yt.table)(f));let d=require("inquirer"),{confirm:u}=await d.prompt([{type:"confirm",name:"confirm",message:"Do you want to overwrite these changes exactly as you specified?",default:!1}]);if(u){let h=(0,Qe.default)("Overwriting secrets...").start();try{await g.setSecrets(o,n,i,void 0,s),h.succeed("Secrets overwritten successfully.");try{let{logAudit:w}=(F(),L(V));w("SECRET_UPDATE_FORCE",o,n,{keys:Object.keys(i),branch:s})}catch{}}catch(w){h.fail("Failed to overwrite secrets."),console.error(R.default.red(w.message))}}else console.log(R.default.yellow("Update cancelled."));return}p.isSpinning&&p.fail("Failed to update secrets"),c.response&&c.response.data&&c.response.data.error?console.error(R.default.red(`Server Error: ${c.response.data.error}`)):console.error(R.default.red(c.message))}});he.command("link").description("Link a secret to another secret (Reference)").argument("<key>","Key of the secret to create (e.g. DB_URL)").requiredOption("--source <source>","Source path (format: project/env/key)").addHelpText("after",`
|
|
38
|
-
Examples:
|
|
39
|
-
$ xtra secrets link SHARED_DB_URL --source shared-proj/production/DB_URL
|
|
40
|
-
`).action(async(e,t)=>{let r=he.opts(),{project:o,env:n}=r;n={dev:"development",stg:"staging",prod:"production"}[n]||n;let{source:a}=t;o||(o=M("project")),o||(console.error(R.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let i=a.split("/");if(i.length!==3){console.error(R.default.red("Error: Source must be in format 'projectId/env/key'"));return}let[p,c,l]=i,f=(0,Qe.default)(`Linking ${e} to ${a}...`).start();try{await g.linkSecret(o,n,e,p,c,l),f.succeed(R.default.green(`Secret '${e}' successfully linked to '${l}'`));try{let{logAudit:d}=(F(),L(V));d("SECRET_LINKED",o,n,{key:e,source:a})}catch{}}catch(d){f.fail("Failed to link secret");let u=d?.response?.data?.error||d.message||"Unknown error";console.error(R.default.red(`Server Error: ${u}`))}});var Qo=require("commander");I();var ye=m(require("chalk")),Zo=m(require("ora")),jt=m(require("fs")),er=m(require("path")),tr=m(require("yaml"));C();function ms(e){let t={},r=e.split(/\r?\n/);for(let o of r){let n=o.trim();if(!n||n.startsWith("#"))continue;let s=n.indexOf("=");if(s===-1)continue;let a=n.substring(0,s).trim(),i=n.substring(s+1).trim();(i.startsWith('"')&&i.endsWith('"')||i.startsWith("'")&&i.endsWith("'"))&&(i=i.slice(1,-1)),t[a]=i}return t}function fs(e,t){let r=e.split(/\r?\n/),o=new Set,n=[],s=[],a=r.map(i=>{let p=i.trim();if(!p||p.startsWith("#"))return i;let c=p.indexOf("=");if(c===-1)return i;let l=p.substring(0,c).trim();return o.add(l),l in t?(ms(i)[l]!==t[l]&&s.push(l),`${l}="${t[l]}"`):i});for(let[i,p]of Object.entries(t))o.has(i)||(a.push(`${i}="${p}"`),n.push(i));return{content:a.join(`
|
|
41
|
-
`),added:n,updated:s}}var or=new Qo.Command("generate").description("Generate local configuration files from secrets").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (dev, stg, prod)","dev").option("-b, --branch <branchName>","Branch Name").option("-o, --output <path>","Output file path (forces complete overwrite)").option("-f, --format <format>","Output format (env, json, yaml)","env").option("--force","Skip confirmation prompts",!1).action(async e=>{let{project:t,env:r,branch:o,output:n,format:s,force:a}=e;t||(t=y().project),o||(o=y().branch||"main"),r={dev:"development",stg:"staging",prod:"production"}[r]||r,t||(console.error(ye.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let p=!n&&s==="env",c=n;if(!c)switch(s){case"json":c="secrets.json";break;case"yaml":c="secrets.yaml";break;default:c=".env";break}let l=er.default.resolve(process.cwd(),c),f=jt.default.existsSync(l),d=(0,Zo.default)(`Fetching secrets for ${r} (branch: ${o})...`).start();try{let u=await g.getSecrets(t,r,o);if(!u||Object.keys(u).length===0){d.warn(ye.default.yellow("No secrets found. File not modified."));return}let h="",w=[],j=[];if(p&&f){d.text="Merging with existing .env file...";let fe=jt.default.readFileSync(l,"utf-8"),G=fs(fe,u);if(h=G.content,w=G.added,j=G.updated,w.length===0&&j.length===0){d.succeed(ye.default.green("No changes needed - .env is already up to date."));return}}else switch(s.toLowerCase()){case"json":h=JSON.stringify(u,null,2);break;case"yaml":h=tr.default.stringify(u);break;default:h=Object.entries(u).map(([fe,G])=>`${fe}="${G}"`).join(`
|
|
42
|
-
`);break}jt.default.writeFileSync(l,h,"utf-8");let{hash:v}=(ge(),L(Vt)),{updateManifest:A}=(wt(),L(Xt));A(t,r,u,v),p&&f?(d.succeed(ye.default.green(`Updated ${c}`)),w.length>0&&console.log(ye.default.cyan(` + Added: ${w.join(", ")}`)),j.length>0&&console.log(ye.default.yellow(` ~ Updated: ${j.join(", ")}`))):d.succeed(`Generated ${c} (${Object.keys(u).length} secrets)`)}catch(u){d.fail("Failed to generate file."),console.error(ye.default.red(u.message)),process.exit(1)}});var rr=require("commander");I();var D=m(require("chalk")),nr=m(require("ora")),sr=require("table");wt();ge();C();var ar=new rr.Command("status").description("Check synchronization status with XtraSync platform").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (dev, stg, prod)","dev").option("-b, --branch <branchName>","Branch Name").action(async e=>{let{project:t,env:r,branch:o}=e;t||(t=y().project),o||(o=y().branch||"main"),t||(console.error(D.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1)),r={dev:"development",stg:"staging",prod:"production"}[r]||r;let s=vt();if(!s){console.log(D.default.yellow("No local manifest found. Run 'xtra run' or 'xtra generate' first."));return}(s.projectId!==t||s.environment!==r)&&(console.log(D.default.yellow(`Manifest found for DIFFERENT project/env (${s.projectId}/${s.environment}).`)),console.log("Checking status against requested target anyway..."));let a=(0,nr.default)(`Fetching remote state for ${r} (branch: ${o})...`).start();try{let i=await g.getSecrets(t,r,o);a.stop();let p=[[D.default.bold("Secret Key"),D.default.bold("Status"),D.default.bold("Local Last Updated")]],c=0;Object.entries(i).forEach(([l,f])=>{let d=qt(f),u=s.secrets[l];u?u.hash!==d?(p.push([l,D.default.yellow("MODIFIED"),u.updatedAt]),c++):p.push([l,D.default.cyan("Synced"),u.updatedAt]):(p.push([l,D.default.green("NEW (Remote)"),"-"]),c++)}),Object.keys(s.secrets).forEach(l=>{i[l]||(p.push([l,D.default.red("DELETED (Remote)"),s.secrets[l].updatedAt]),c++)}),console.log(""),console.log((0,sr.table)(p)),c===0?console.log(D.default.green("\u2714 Everything is in sync.")):(console.log(D.default.yellow(`\u26A0 Found ${c} difference(s).
|
|
43
|
-
`)),console.log(D.default.bold(" Next steps:")),console.log(D.default.gray(" xtra generate ")+D.default.white("# pull cloud secrets \xE2\u2020\u2019 .env (merge)")),console.log(D.default.gray(" xtra generate -f json ")+D.default.white("# pull cloud secrets \xE2\u2020\u2019 secrets.json")),console.log(D.default.gray(" xtra run node app.js ")+D.default.white("# inject secrets at runtime (no file)")),console.log(D.default.gray(" xtra local sync ")+D.default.white("# pull cloud secrets \xE2\u2020\u2019 .env.local (offline mode)")))}catch(i){a.fail("Failed to fetch remote secrets."),console.error(D.default.red(i.message))}});var cr=require("commander");I();var Y=m(require("chalk")),Gt=m(require("ora"));C();ge();var lr=m(require("diff")),pr=new cr.Command("diff").description("Show differences between local cache and remote secrets").argument("[env1]","Source Environment").argument("[env2]","Target Environment").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (dev, stg, prod)","dev").option("-b, --branch <branchName>","Branch Name").option("--show","Show actual value differences (sensitive)",!1).action(async(e,t,r)=>{let{project:o,env:n,branch:s,show:a}=r;o||(o=y().project),s||(s=y().branch||"main"),o||(console.error(Y.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let i={dev:"development",stg:"staging",prod:"production"};if(e&&t){let p=i[e]||e,c=i[t]||t,l=(0,Gt.default)(`Fetching secrets for ${p} and ${c} (branch: ${s})...`).start();try{let[f,d]=await Promise.all([g.getSecrets(o,p,s),g.getSecrets(o,c,s)]);l.stop(),console.log(Y.default.bold(`
|
|
44
|
-
Diff Report (${p} <-> ${c} @ ${s}):
|
|
45
|
-
`)),ir(f,d,a)}catch(f){l.fail("Failed to fetch secrets"),console.error(Y.default.red(f.message))}}else{let p=i[n]||n,c=(0,Gt.default)(`Fetching remote state for ${p} (branch: ${s})...`).start();try{let l=await g.getSecrets(o,p,s),f={};try{let d=`cache.${o}.${p}.${s}`,u=M(d);if(u){let h=ne(u);f=JSON.parse(h)}}catch{}c.stop(),console.log(Y.default.bold(`
|
|
46
|
-
Diff Report (Remote ${p} vs Local Cache):
|
|
47
|
-
`)),ir(l,f,a)}catch(l){c.fail("Failed to run diff."),console.error(Y.default.red(l.message))}}});function ir(e,t,r){let o=new Set([...Object.keys(e),...Object.keys(t)]),n=!1;o.forEach(s=>{let a=e[s],i=t[s];if(a!==i){if(n=!0,a===void 0)console.log(Y.default.green(`+ ${s} (Only in Target)`)),r&&console.log(Y.default.green(` + "${i}"`));else if(i===void 0)console.log(Y.default.red(`- ${s} (Only in Source)`)),r&&console.log(Y.default.red(` - "${a}"`));else if(console.log(Y.default.yellow(`~ ${s} (Modified)`)),r){let p=lr.diffChars(a,i),c=" ";p.forEach(l=>{let f=l.added?Y.default.green:l.removed?Y.default.red:Y.default.gray;c+=f(l.value)}),console.log(c)}}}),n?r||console.log(Y.default.gray(`
|
|
48
|
-
(Use --show to reveal secret values)`)):console.log(Y.default.green("\u2714 No differences found."))}var dr=require("commander");I();var Ze=m(require("chalk")),zt=m(require("ora")),mr=m(require("inquirer"));ge();C();var fr=new dr.Command("rollback").description("Rollback a secret to a previous version").argument("<key>","Secret Key to rollback").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (dev, stg, prod)","dev").action(async(e,t)=>{let{project:r,env:o}=t;r||(r=y().project),r||(console.error(Ze.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1)),o={dev:"development",stg:"staging",prod:"production"}[o]||o;let s=(0,zt.default)(`Fetching history for ${e}...`).start();try{let a=await g.getSecretDetails(r,o,e);if(s.stop(),!a.history||!Array.isArray(a.history)||a.history.length===0){console.log(Ze.default.yellow(`No history found for ${e}. Cannot rollback.`));return}let i=[];[...a.history].reverse().forEach(u=>{let h="********";try{let w=JSON.parse(u.value[0]);h=ne(w)}catch{h=u.value[0]}i.push({name:`v${u.version} - ${new Date(u.updatedAt).toLocaleString()} (Value: ${h.substring(0,10)}...)`,value:u})});let{selectedVersion:c}=await mr.default.prompt([{type:"list",name:"selectedVersion",message:"Select version to restore:",choices:i}]),l=(0,zt.default)(`Restoring version v${c.version}...`).start(),f=c.value[0];try{let u=JSON.parse(f);f=ne(u)}catch{}let d={[e]:f};await g.setSecrets(r,o,d),l.succeed(`Successfully rolled back ${e} to v${c.version}`);try{let{logAudit:u}=(F(),L(V));u("SECRET_ROLLBACK",r,o,{key:e,version:c.version})}catch{}}catch(a){s.fail("Failed to rollback."),a.response&&a.response.status===404?console.error(Ze.default.red("Secret not found.")):console.error(Ze.default.red(a.message))}});var ur=require("commander"),ve=m(require("chalk")),et=m(require("fs")),Qt=m(require("path")),xt=require("child_process"),gr=new ur.Command("scan").description("Scan project for secrets and configuration leaks").option("--staged","Scan only staged files (for pre-commit hooks)",!1).option("--install-hook","Install the git pre-commit hook").action(async e=>{let{staged:t,installHook:r}=e;if(r){us();return}console.log(ve.default.bold("XtraSync Secret Scan"));let o=0;gs()&&(console.error(ve.default.red("CRITICAL: .env file is tracked by git!")),o++);let n=t?hs():ys(),s=[{name:"Xtra API Key",regex:/xs_[a-zA-Z0-9]{10,}/}];n.forEach(a=>{try{if(!et.default.existsSync(a))return;let i=et.default.readFileSync(a,"utf-8");s.forEach(p=>{p.regex.test(i)&&(console.error(ve.default.red(`[${a}] Potential Secret Detected: ${p.name}`)),o++)})}catch{}}),o>0?(console.error(ve.default.red(`
|
|
49
|
-
\u2716 Scan failed. found ${o} issues.`)),process.exit(1)):console.log(ve.default.green(`
|
|
50
|
-
\u2714 Scan passed. No secrets found.`))});function us(){let e=Qt.default.join(process.cwd(),".git","hooks"),t=Qt.default.join(e,"pre-commit");if(!et.default.existsSync(e)){console.error(ve.default.red("Error: .git directory not found. Is this a git repository?"));return}et.default.writeFileSync(t,`#!/bin/sh
|
|
51
|
-
# XtraSync Secret Scan
|
|
52
|
-
echo "Running XtraSync Secret Scan..."
|
|
53
|
-
|
|
54
|
-
# Check if 'xtra' is globally installed or use local script
|
|
55
|
-
if command -v xtra >/dev/null 2>&1; then
|
|
56
|
-
xtra scan --staged
|
|
57
|
-
elif [ -f "./node_modules/.bin/xtra" ]; then
|
|
58
|
-
./node_modules/.bin/xtra scan --staged
|
|
59
|
-
else
|
|
60
|
-
# Using the binary directly if running in dev environment (unlikely in prod use case)
|
|
61
|
-
# For dev testing:
|
|
62
|
-
if [ -f "./dist/bin/xtra.js" ]; then
|
|
63
|
-
node ./dist/bin/xtra.js scan --staged
|
|
64
|
-
else
|
|
65
|
-
echo "Warning: XtraSync CLI not found. Skipping scan."
|
|
66
|
-
fi
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
# Exit code of scan is passed through
|
|
70
|
-
`,{mode:493}),console.log(ve.default.green("\u2714 Pre-commit hook installed successfully."))}function gs(){try{return(0,xt.execSync)("git ls-files --error-unmatch .env",{stdio:"ignore"}),!0}catch{return!1}}function hs(){try{return(0,xt.execSync)("git diff --cached --name-only",{encoding:"utf-8"}).split(`
|
|
71
|
-
`).filter(t=>t.trim().length>0)}catch{return[]}}function ys(){try{return(0,xt.execSync)("git ls-files",{encoding:"utf-8"}).split(`
|
|
72
|
-
`).filter(t=>t.trim().length>0)}catch{return[]}}var hr=require("commander");F();var H=m(require("chalk")),yr=require("table"),vr=new hr.Command("logs").description("View local audit logs").option("-n, --limit <number>","Number of logs to show","20").option("--sync","Sync logs to cloud",!1).option("--event <type>","Filter by event type (e.g. SECRET_UPDATE, SECRET_ROTATE)").option("--project <projectId>","Filter by project ID").option("--since <duration>","Show logs since (e.g. 1h, 24h, 7d, 30d)","").option("--json","Output raw JSON instead of table",!1).action(async e=>{let{limit:t,sync:r,event:o,project:n,since:s,json:a}=e;try{let i=_e();if(r){let d=i.filter(h=>!h.synced);if(d.length===0){console.log(H.default.green("All logs are already synced."));return}let u=require("ora")(`Syncing ${d.length} logs to cloud...`).start();try{let{api:h}=(I(),L(ft)),{saveAuditLogs:w}=(F(),L(V));await h.syncLogs(d),d.forEach(j=>j.synced=!0),w(i),u.succeed(`Successfully synced ${d.length} logs.`)}catch(h){u.fail("Sync failed."),console.error(H.default.red(h.message))}return}if(i.length===0){console.log(H.default.yellow("No local audit logs found."));return}if(o){let d=o.toUpperCase();i=i.filter(u=>u.action?.toUpperCase().includes(d))}if(n&&(i=i.filter(d=>d.projectId===n)),s){let d=s.match(/^(\d+)(h|d|m)$/i);d||(console.error(H.default.red(`Invalid --since format: '${s}'. Use e.g. 1h, 24h, 7d, 30d`)),process.exit(1));let u=parseInt(d[1]),h=d[2].toLowerCase(),w={h:36e5,d:864e5,m:6e4},j=new Date(Date.now()-u*w[h]);i=i.filter(v=>new Date(v.timestamp)>=j)}i.sort((d,u)=>new Date(u.timestamp).getTime()-new Date(d.timestamp).getTime());let p=i.slice(0,parseInt(t));if(p.length===0){console.log(H.default.yellow("No logs matched your filters."));return}if(a){process.stdout.write(JSON.stringify(p,null,2)+`
|
|
73
|
-
`);return}let c=[[H.default.bold("Timestamp"),H.default.bold("Action"),H.default.bold("Project"),H.default.bold("Sync"),H.default.bold("Details")]];p.forEach(d=>{let u="";d.details&&(d.details.keys?u=`Keys: ${d.details.keys.join(", ")}`:d.details.key?u=`Key: ${d.details.key}`:u=JSON.stringify(d.details));let h=d.projectId?d.projectId.substring(0,8)+"...":"-";c.push([new Date(d.timestamp).toLocaleString(),d.action,h,d.synced?H.default.green("\u2714"):H.default.yellow("\u2716"),u.substring(0,50)+(u.length>50?"...":"")])});let l=[];o&&l.push(`event: ${o.toUpperCase()}`),n&&l.push(`project: ${n}`),s&&l.push(`since: ${s}`);let f=l.length>0?H.default.gray(` [${l.join(" | ")}]`):"";console.log(H.default.bold(`
|
|
74
|
-
Local Audit Logs${f}`)),console.log((0,yr.table)(c)),console.log(H.default.gray(` Showing ${p.length} of ${i.length} total logs.`))}catch(i){console.error(H.default.red("Failed to load logs: "+i.message))}});var wr=require("commander");I();var Fe=m(require("chalk")),br=m(require("fs")),jr=m(require("path")),xr=require("csv-stringify/sync");C();var $r=new wr.Command("export").description("Export secrets to a file (JSON, Dotenv, CSV)").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (development, staging, production)","development").option("-b, --branch <branchName>","Branch Name").option("-f, --format <format>","Output format (json, dotenv, csv)","json").option("-o, --output <file>","Output file path (default: stdout)").action(async e=>{let{project:t,env:r,branch:o,format:n,output:s}=e;t||(t=y().project),o||(o=y().branch||"main"),r={dev:"development",stg:"staging",prod:"production"}[r]||r,t||(console.error(Fe.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let i=require("ora")(`Fetching secrets for export (${r} @ ${o})...`).start();try{let p=await g.getSecrets(t,r,o);if(i.stop(),!p||Object.keys(p).length===0){console.warn(Fe.default.yellow("No secrets found to export."));return}let c="";switch(n.toLowerCase()){case"json":c=JSON.stringify(p,null,2);break;case"dotenv":c=Object.entries(p).map(([f,d])=>`${f}="${String(d).replace(/"/g,'\\"')}"`).join(`
|
|
75
|
-
`);break;case"csv":let l=Object.entries(p).map(([f,d])=>({key:f,value:d}));c=(0,xr.stringify)(l,{header:!0});break;default:console.error(Fe.default.red(`Error: Unsupported format '${n}'. Use json, dotenv, or csv.`)),process.exit(1)}if(s){let l=jr.default.resolve(process.cwd(),s);br.default.writeFileSync(l,c,"utf-8"),console.log(Fe.default.green(`\u2714 Secrets exported to ${l}`))}else console.log(c);try{let{logAudit:l}=(F(),L(V));l("SECRET_EXPORT",t,r,{format:n,destination:s||"stdout",branch:o})}catch{}}catch(p){i.fail("Export failed.");let c=p?.response?.data?.error||p.message||"Unknown error";console.error(Fe.default.red(c)),process.exit(1)}});var Sr=require("commander");I();var tt=m(require("chalk")),Zt=m(require("fs")),$t=m(require("path")),Cr=m(require("dotenv")),Er=require("csv-parse/sync");C();var kr=new Sr.Command("import").description("Import secrets from a file (JSON, Dotenv, CSV)").argument("<file>","Path to the file to import").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment (development, staging, production)","development").option("-b, --branch <branchName>","Branch Name").option("-f, --format <format>","Input format (json, dotenv, csv). Auto-detected if omitted.").option("--prefix <prefix>","Add prefix to all imported keys").option("--overwrite","Overwrite existing secrets (default: merged, but API usually upserts)",!0).action(async(e,t)=>{let{project:r,env:o,branch:n,format:s,prefix:a}=t;r||(r=y().project),n||(n=y().branch||"main"),o={dev:"development",stg:"staging",prod:"production"}[o]||o,r||(console.error(tt.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let p=$t.default.resolve(process.cwd(),e);Zt.default.existsSync(p)||(console.error(tt.default.red(`Error: File not found: ${p}`)),process.exit(1));let c=s;if(!c){let u=$t.default.extname(p).toLowerCase();u===".json"?c="json":u===".env"?c="dotenv":u===".csv"?c="csv":(console.error(tt.default.red("Error: Could not detect format. Please specify -f <format>")),process.exit(1))}let l=require("ora")(`Reading ${c} file...`).start(),f=Zt.default.readFileSync(p,"utf-8"),d={};try{switch(c.toLowerCase()){case"json":d=JSON.parse(f);break;case"dotenv":d=Cr.default.parse(f);break;case"csv":(0,Er.parse)(f,{columns:!0,skip_empty_lines:!0}).forEach(h=>{let w=h.key||h.Key||h.KEY,j=h.value||h.Value||h.VALUE;w&&(d[w]=j||"")});break;default:l.fail(`Unsupported format: ${c}`),process.exit(1)}if(Object.keys(d).length===0){l.fail("No valid secrets found in file.");return}if(a){let u={};Object.entries(d).forEach(([h,w])=>{u[`${a}${h}`]=w}),d=u}l.text=`Importing ${Object.keys(d).length} secrets to ${o} (branch: ${n})...`,await g.setSecrets(r,o,d,void 0,n),l.succeed(`Successfully imported ${Object.keys(d).length} secrets.`);try{let{logAudit:u}=(F(),L(V));u("SECRET_IMPORT",r,o,{file:$t.default.basename(p),format:c,count:Object.keys(d).length,branch:n})}catch{}}catch(u){l.fail("Import failed."),console.error(tt.default.red(u.message)),process.exit(1)}});var Ir=require("commander");I();C();var ee=m(require("chalk")),eo=m(require("ora")),Ar=new Ir.Command("rotate").description("Rotate a secret (Zero-Downtime Shadow Mode)").argument("<key>","Key of the secret to rotate").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Environment","development").option("--strategy <strategy>","Rotation strategy","shadow").option("--promote","Promote the shadow value to active",!1).option("--value <value>","New value for the secret (optional)").action(async(e,t)=>{let{project:r,env:o,strategy:n,promote:s,value:a}=t;if(r||(r=y().project),o={dev:"development",stg:"staging",prod:"production"}[o]||o,r||(console.error(ee.default.red("Error: Project ID is required. Use -p <id> or checkout a branch.")),process.exit(1)),o==="production"){let p=require("inquirer"),{confirmProd:c}=await p.prompt([{type:"confirm",name:"confirmProd",message:ee.default.red(`\u26A0 You are about to rotate a secret in PRODUCTION (${e}). Proceed?`),default:!1}]);if(!c){console.log(ee.default.yellow("Aborted."));return}}if(s){let p=(0,eo.default)(`Promoting secret '${e}' in ${o}...`).start();try{let c=await g.promoteSecret(r,o,e);p.succeed(ee.default.green(`Successfully promoted '${e}' to version ${c.version}`));try{let{logAudit:l}=(F(),L(V));l("SECRET_PROMOTE",r,o,{key:e,newVersion:c.version})}catch{}}catch(c){p.fail("Promotion failed"),c.response&&c.response.data&&c.response.data.error?console.error(ee.default.red(`Server Error: ${c.response.data.error}`)):console.error(ee.default.red(c.message))}}else{let p=(0,eo.default)(`Starting rotation for '${e}' (Strategy: ${n})...`).start();try{let c=await g.rotateSecret(r,o,e,n,a);p.succeed(ee.default.green(`Rotation initiated for '${e}'`)),console.log(ee.default.dim(`Shadow Value: ${c.shadowValue}`)),console.log(ee.default.dim(`Expires At: ${c.expiresAt}`)),console.log(ee.default.blue(`
|
|
76
|
-
To verify, run your app. To finalize, run:
|
|
77
|
-
xtra rotate ${e} --promote -p ${r} -e ${o}`));try{let{logAudit:l}=(F(),L(V));l("SECRET_ROTATE",r,o,{key:e,strategy:n,expiresAt:c.expiresAt})}catch{}}catch(c){p.fail("Rotation failed"),c.response&&c.response.data&&c.response.data.error?console.error(ee.default.red(`Server Error: ${c.response.data.error}`)):console.error(ee.default.red(c.message))}}});var St=require("commander");I();var se=m(require("chalk")),to=m(require("ora")),Rr=m(require("fs")),Pr=new St.Command("audit").description("Manage and verify audit logs").addCommand(new St.Command("verify").description("Verify the integrity of the audit log chain (Tamper-Evident)").action(async()=>{let e=(0,to.default)("Verifying audit chain integrity...").start();try{let t=await g.verifyAuditLogs();t.valid?e.succeed(se.default.green(t.message)):(e.fail(se.default.red("Verification Failed!")),console.error(se.default.red(`Broken at Log ID: ${t.details?.brokenAtId}`)),console.error(se.default.red(`Reason: ${t.details?.reason}`)))}catch(t){e.fail("Verification failed"),t.response?.data?.details?(console.error(se.default.red(`Chain Broken: ${t.response.data.message}`)),console.error(se.default.yellow(`Details: ${JSON.stringify(t.response.data.details,null,2)}`))):console.error(se.default.red(t.message))}})).addCommand(new St.Command("export").description("Export audit logs for compliance (SOC2/ISO)").option("-f, --format <format>","Output format (json, csv)","json").option("--start <date>","Start date (YYYY-MM-DD)").option("--end <date>","End date (YYYY-MM-DD)").option("-o, --output <file>","Output file path").action(async e=>{let{format:t,start:r,end:o,output:n}=e,s=(0,to.default)(`Exporting audit logs (${t})...`).start();try{let a=await g.exportAuditLogs(t,r,o),i=typeof a=="object"?JSON.stringify(a,null,2):a;n?(Rr.default.writeFileSync(n,i),s.succeed(se.default.green(`Export saved to ${n}`))):(s.stop(),console.log(i))}catch(a){s.fail("Export failed"),console.error(se.default.red(a.message))}}));var ke=require("commander");I();var S=m(require("chalk")),rt=m(require("ora")),Or=require("table");C();var Ct=(e,t,r)=>{try{let{logAudit:o}=(F(),L(V));o(e,t,null,r)}catch{}},ot=e=>e?.response?.data?.error||e?.message||"Unknown error",Dr=new ke.Command("access").description("Manage Just-in-Time access requests").addCommand(new ke.Command("request").description("Request access to a secret or project").option("-p, --project <projectId>","Project ID").option("-s, --secret <secretId>","Specific Secret ID (optional)").requiredOption("-d, --duration <minutes>","Duration in minutes").requiredOption("-r, --reason <text>","Reason for access").action(async e=>{let{project:t,secret:r,duration:o,reason:n}=e;t||(t=y().project),t||(console.error(S.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let s=(0,rt.default)("Submitting access request...").start();try{let a=await g.requestAccess(t,n,parseInt(o),r);s.succeed(S.default.green(`Access request submitted! (ID: ${a.requestId})`)),console.log(S.default.yellow(`Status: ${a.status}`)),Ct("ACCESS_REQUESTED",t,{requestId:a.requestId,secretId:r,duration:o,reason:n})}catch(a){s.fail("Request failed"),console.error(S.default.red(ot(a)))}})).addCommand(new ke.Command("list").description("List access requests").option("--pending","Show pending approvals (for admins)",!1).action(async e=>{try{let t=e.pending?"pending":"my",r=await g.listAccessRequests(t);if(r.length===0){console.log(S.default.yellow("No requests found."));return}let o=[[S.default.bold("ID"),S.default.bold("User"),S.default.bold("Target"),S.default.bold("Duration"),S.default.bold("Reason"),S.default.bold("Status")]];r.forEach(n=>{let s=n.secret?`Secret: ${n.secret.key}`:`Project: ${n.projectId}`;o.push([n.id,n.user.email,s,`${n.duration}m`,n.reason,n.status==="pending"?S.default.yellow(n.status):n.status==="approved"?S.default.green(n.status):S.default.red(n.status)])}),console.log((0,Or.table)(o))}catch(t){console.error(S.default.red("Failed to list requests: "+ot(t)))}})).addCommand(new ke.Command("approve").description("Approve or reject a request").argument("<requestId>","Request ID").requiredOption("--decision <decision>","approved or rejected").action(async(e,t)=>{["approved","rejected"].includes(t.decision.toLowerCase())||(console.error(S.default.red(`Invalid decision: '${t.decision}'. Must be 'approved' or 'rejected'.`)),process.exit(1));let o=(0,rt.default)("Processing decision...").start();try{let n=await g.approveAccess(e,t.decision);o.succeed(S.default.green(`Request ${t.decision}!`)),n.expiresAt&&console.log(S.default.blue(`Access granted until: ${new Date(n.expiresAt).toLocaleString()}`)),Ct(t.decision==="approved"?"ACCESS_APPROVED":"ACCESS_REJECTED",null,{requestId:e,decision:t.decision,expiresAt:n.expiresAt})}catch(n){o.fail("Operation failed"),console.error(S.default.red(ot(n)))}})).addCommand(new ke.Command("jit-generate").description("Generate a shareable JIT access link for a project").option("-p, --project <projectId>","Project ID").requiredOption("-d, --duration <minutes>","Access duration in minutes").option("-b, --branch <branchId>","Scope to branch ID").option("-e, --env <environment>","Scope to environment (production, staging, development)").option("-s, --secrets <ids>","Comma-separated secret IDs to scope access").option("--label <text>","Label for the JIT link").option("--max-uses <count>","Maximum number of uses","1").option("--expires <hours>","Link expiry in hours","24").action(async e=>{let{project:t,duration:r,branch:o,env:n,secrets:s,label:a,maxUses:i,expires:p}=e;t||(t=y().project),t||(console.error(S.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let c=(0,rt.default)("Generating JIT link...").start();try{let l={projectId:t,duration:parseInt(r),maxUses:parseInt(i),expiresInHours:parseInt(p)};o&&(l.branchId=o),n&&(l.environment=n),s&&(l.secretIds=s.split(",").map(d=>d.trim())),a&&(l.label=a);let f=await g.generateJitLink(l);c.succeed(S.default.green("JIT link generated!")),console.log(),console.log(S.default.bold(" Shareable URL:")),console.log(S.default.cyan(` ${f.url}`)),console.log(),console.log(S.default.bold(" CLI Command:")),console.log(S.default.yellow(` ${f.cliCommand}`)),console.log(),console.log(S.default.dim(` Expires: ${new Date(f.expiresAt).toLocaleString()}`)),console.log(S.default.dim(` Duration: ${f.duration} minutes | Access: ${f.accessLevel}-only`)),Ct("JIT_LINK_GENERATED",t,{token:f.token,duration:r,env:n,branch:o})}catch(l){c.fail("Failed to generate JIT link"),console.error(S.default.red(ot(l)))}})).addCommand(new ke.Command("jit").description("Claim a JIT access link (request read-only access)").argument("<token>","JIT link token").action(async e=>{let t=(0,rt.default)("Claiming JIT link...").start();try{let r=await g.getJitInfo(e);t.text=`Requesting access to "${r.projectName}"...`;let o=await g.claimJitLink(e);t.succeed(S.default.green("Access request submitted!")),console.log(),console.log(S.default.yellow(" Status: Pending admin/owner approval")),console.log(S.default.dim(` Request ID: ${o.requestId}`)),console.log(S.default.dim(` Access: ${o.accessLevel}-only for ${o.duration} minutes`)),console.log(),console.log(S.default.dim(" Run 'xtra access list' to check request status.")),Ct("JIT_LINK_CLAIMED",null,{token:e,requestId:o.requestId})}catch(r){t.fail("Failed to claim JIT link"),console.error(S.default.red(ot(r)))}}));var Tr=require("commander");I();var T=m(require("chalk")),nt=m(require("ora")),Nr=require("table");C();var Lr=m(require("inquirer")),ae=new Tr.Command("branch").description("Manage branches").option("-p, --project <projectId>","Project ID");ae.command("list").description("List all branches").action(async e=>{let t=ae.opts(),{project:r}=t;r||(r=y().project),r||(console.error(T.default.red("Error: Project ID is required. Use -p <id> or checkout a branch.")),process.exit(1));let o=(0,nt.default)("Fetching branches...").start();try{let n=await g.getBranches(r);if(o.stop(),!n){console.log(T.default.yellow("No branches found."));return}if(!Array.isArray(n)){n.error?console.error(T.default.red(`Error: ${n.error}`)):console.error(T.default.red("Error: Unexpected API response format (not an array)."));return}if(n.length===0){console.log(T.default.yellow("No branches found."));return}let s=[[T.default.bold("Name"),T.default.bold("ID"),T.default.bold("Created By")]];n.forEach(a=>{let i=a.user?.email||a.user?.name||a.createdBy||"-";s.push([a.name,a.id,i])}),console.log((0,Nr.table)(s))}catch(n){o.fail("Failed to fetch branches"),n.response&&n.response.data&&n.response.data.error?console.error(T.default.red(`Server Error: ${n.response.data.error}`)):console.error(T.default.red(n.message))}});ae.command("create").description("Create a new branch").argument("<name>","Branch name").option("-d, --description <text>","Description").action(async(e,t)=>{let r=ae.opts(),{project:o}=r,{description:n}=t;o||(o=y().project),o||(console.error(T.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let s=(0,nt.default)(`Creating branch '${e}'...`).start();try{await g.createBranch(o,e,n),s.succeed(`Branch '${e}' created successfully.`)}catch(a){s.fail("Failed to create branch"),a.response&&a.response.data&&a.response.data.error?console.error(T.default.red(`Server Error: ${a.response.data.error}`)):console.error(T.default.red(a.message))}});ae.command("delete").description("Delete a branch").argument("<name>","Branch name").option("-y, --yes","Skip confirmation prompt",!1).action(async(e,t)=>{let r=ae.opts(),{project:o}=r,{yes:n}=t;if(o||(o=y().project),o||(console.error(T.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1)),e==="main"){console.error(T.default.red("Error: Cannot delete the 'main' branch."));return}let s=(0,nt.default)("Resolving branch...").start();try{let i=(await g.getBranches(o)).find(p=>p.name===e);if(!i){s.fail(`Branch '${e}' not found.`);return}if(s.stop(),!n){let{confirm:p}=await Lr.default.prompt([{type:"confirm",name:"confirm",message:`Are you sure you want to DELETE branch '${e}'? This action cannot be undone.`,default:!1}]);if(!p){console.log(T.default.gray("Operation cancelled."));return}}s.start("Deleting branch..."),await g.deleteBranch(i.id),s.succeed(`Branch '${e}' deleted successfully.`);try{let{logAudit:p}=(F(),L(V));p("BRANCH_DELETED",o,null,{branchName:e,branchId:i.id})}catch{}}catch(a){s.fail("Failed to delete branch"),a.response&&a.response.data&&a.response.data.error?console.error(T.default.red(`Server Error: ${a.response.data.error}`)):console.error(T.default.red(a.message))}});ae.command("update").description("Update a branch").argument("<name>","Current branch name").option("-n, --new-name <newName>","New branch name").option("-d, --description <description>","New description").action(async(e,t)=>{let r=ae.opts(),{project:o}=r,{newName:n,description:s}=t;if(o||(o=y().project),o||(console.error(T.default.red("Error: Project ID is required. Use -p <id> or checkout a branch.")),process.exit(1)),!n&&!s){console.log(T.default.yellow("No updates specified. Use --new-name or --description."));return}let a=(0,nt.default)("Resolving branch...").start();try{let p=(await g.getBranches(o)).find(c=>c.name===e);if(!p){a.fail(`Branch '${e}' not found.`);return}a.text="Updating branch...",await g.updateBranch(p.id,{name:n,description:s}),a.succeed("Branch updated successfully.")}catch(i){a.fail("Failed to update branch"),i.response&&i.response.data&&i.response.data.error?console.error(T.default.red(`Server Error: ${i.response.data.error}`)):console.error(T.default.red(i.message))}});var Ur=require("commander");I();C();var pe=m(require("chalk")),_r=m(require("ora")),Mr=m(require("inquirer")),Fr=new Ur.Command("checkout").description("Switch the active branch context").argument("[branchName]","Branch name to switch to (optional - will show list if not provided)").option("-p, --project <projectId>","Project ID").option("-e, --env <environment>","Set the active environment (e.g. development, staging, production)").action(async(e,t)=>{let{project:r}=t;r||(r=y().project),r||(console.error(pe.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1));let o=(0,_r.default)("Fetching branches...").start();try{let n=await g.getBranches(r);if(o.stop(),!Array.isArray(n)||n.length===0){console.log(pe.default.yellow("No branches found in this project."));return}let s=e;if(s){if(!n.find(i=>i.name===s)){console.error(pe.default.red(`Branch '${s}' does not exist.`)),console.log(pe.default.yellow(`Use 'xtra branch create ${s}' to create it.`));return}}else{let a=n.map(p=>({name:p.name,value:p.name})),{selected:i}=await Mr.default.prompt([{type:"list",name:"selected",message:"Select a branch:",choices:a}]);s=i}U("branch",s),U("project",r),t.env&&U("env",t.env),console.log(pe.default.green(`\u2714 Switched to branch '${s}'`)),t.env&&console.log(pe.default.green(`\u2714 Environment set to '${t.env}'`))}catch(n){o.fail("Failed to fetch branches"),n.response?.data?.error?console.error(pe.default.red(`Error: ${n.response.data.error}`)):console.error(pe.default.red(n.message))}});var Vr=require("commander"),Z=m(require("chalk")),Br=m(require("ora")),Kr=m(require("inquirer"));C();I();var qe=m(require("fs")),Jr=m(require("path")),Et=new Vr.Command("project").alias("projects").description("Manage project context"),qr=(e,t)=>{let r=M("project");U("project",e),r!==e&&U("branch","main");let o=Jr.join(process.cwd(),".xtrarc"),n={};if(qe.existsSync(o))try{n=JSON.parse(qe.readFileSync(o,"utf-8"))}catch(a){console.error(Z.default.yellow(` \u26A0 Could not parse existing local .xtrarc: ${a instanceof Error?a.message:"Unknown error"}`))}n.project=e,r!==e&&(n.branch="main"),n.env||(n.env="development"),n.apiUrl||(n.apiUrl=M("apiUrl")||"https://www.xtrasecurity.in/api");try{qe.writeFileSync(o,JSON.stringify(n,null,2),"utf8"),console.log(Z.default.gray(" \u2714 Updated local .xtrarc (Project context locked to this folder)"))}catch(a){console.error(Z.default.yellow(` \u26A0 Could not write local .xtrarc: ${a instanceof Error?a.message:"Unknown error"}`))}console.log(Z.default.green(`\u2714 Default project set to '${t}'`));let s=M("branch");s&&console.log(Z.default.gray(` Active branch: ${s}`))};Et.command("set").description("Set the default project ID").argument("[projectId]","Project ID to set as default (optional - will show list if not provided)").action(async e=>{if(e){qr(e,e);return}let t=(0,Br.default)("Fetching your projects...").start();try{let r=await g.getProjects();if(t.stop(),!Array.isArray(r)||r.length===0){console.log(Z.default.yellow("No projects found."));return}let o=r.map(a=>({name:`${a.name} (${a.id})`,value:a.id,short:a.name})),{selectedProject:n}=await Kr.default.prompt([{type:"list",name:"selectedProject",message:"Select a project:",choices:o}]),s=r.find(a=>a.id===n)?.name||n;qr(n,s)}catch(r){t.fail("Failed to fetch projects"),r.response?.data?.error?console.error(Z.default.red(`Error: ${r.response.data.error}`)):console.error(Z.default.red(r.message))}});Et.command("current").description("Show the current default project").action(async()=>{let e=M("project"),t=M("branch");if(e)try{let o=(await g.getProjects()).find(n=>n.id===e);console.log(o?Z.default.cyan(`Project: ${o.name} (${e})`):Z.default.cyan(`Project: ${e}`))}catch{console.log(Z.default.cyan(`Project: ${e}`))}else console.log(Z.default.yellow("No default project set. Use 'xtra project set' to set one."));t&&console.log(Z.default.cyan(`Branch: ${t}`))});var oo=require("commander");I();var q=m(require("chalk")),kt=m(require("ora")),ro=require("table"),Ve=new oo.Command("admin").description("Admin commands for user and role management"),Hr=new oo.Command("role").description("Manage user roles and permissions"),Wr=async()=>{let e=(0,kt.default)("Fetching roles from server...").start();try{let t=await g.getRoles();e.stop(),console.log(q.default.bold(`
|
|
78
|
-
Available Roles:
|
|
79
|
-
`));let r=[[q.default.bold("Role"),q.default.bold("Description"),q.default.bold("Permissions")]];t.forEach(o=>{r.push([q.default.cyan(o.name),o.description||"-",Array.isArray(o.permissions)?o.permissions.join(", "):o.permissions||"-"])}),console.log((0,ro.table)(r))}catch(t){e.fail("Failed to fetch roles");let r=t.response?.data?.error||t.message||"Unknown error";console.error(q.default.red(r))}};Hr.command("list").description("List all available roles").alias("ls").action(Wr);Ve.addCommand(Hr);Ve.command("roles").description("Alias for 'role list'").action(Wr);Ve.command("users").description("List all users with their roles").option("-t, --team <teamId>","Filter by team ID").action(async e=>{let t=(0,kt.default)("Fetching users...").start();try{let r=await g.getUsers(e.team);if(t.stop(),!r||r.length===0){console.log(q.default.yellow("No users found."));return}console.log(q.default.bold(`
|
|
80
|
-
Users:
|
|
81
|
-
`));let o=[[q.default.bold("Email"),q.default.bold("Name"),q.default.bold("Role"),q.default.bold("Status")]];r.forEach(n=>{let s=n.role==="owner"||n.role==="admin"?q.default.magenta:n.role==="viewer"?q.default.gray:q.default.cyan;o.push([n.email,n.name||"-",s(n.role),n.status||"active"])}),console.log((0,ro.table)(o))}catch(r){t.fail("Failed to fetch users"),console.error(q.default.red(r.message))}});Ve.command("set-role <email> <role>").description("Change a user's role (owner, admin, developer, viewer, guest)").option("-t, --team <teamId>","Team ID (for team-specific role)").action(async(e,t,r)=>{let o=["owner","admin","developer","viewer","guest"];o.includes(t.toLowerCase())||(console.error(q.default.red(`Invalid role: ${t}`)),console.log(q.default.yellow(`Valid roles: ${o.join(", ")}`)),process.exit(1));let n=(0,kt.default)(`Updating role for ${e}...`).start();try{await g.setUserRole(e,t.toLowerCase(),r.team),n.succeed(q.default.green(`Role updated: ${e} \u2192 ${t}`));try{let{logAudit:s}=(F(),L(V));s("ADMIN_ROLE_CHANGE",null,null,{email:e,newRole:t,teamId:r.team})}catch{}}catch(s){n.fail("Failed to update role");let a=s.response?.data?.error||s.message||"Unknown error";console.error(q.default.red(a))}});var no=require("commander"),N=m(require("chalk"));I();C();var Xr=m(require("inquirer")),Yr=m(require("fs")),so=new no.Command("integration");so.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(N.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1)));let r=await g.getIntegrationStatus("github");r.connected||(console.log(N.default.yellow(`
|
|
82
|
-
GitHub not connected. Please connect via the web dashboard first.`)),console.log(N.default.blue(`Visit: https://www.xtrasecurity.in/integrations
|
|
83
|
-
`)),process.exit(1)),console.log(N.default.green(`
|
|
84
|
-
Connected as: ${r.username}`));let o=e.repo;if(o){let[s,a]=o.split("/");(!s||!a)&&(console.error(N.default.red("Error: Invalid repository format. Use owner/repo")),process.exit(1)),o={owner:s,name:a}}else{console.log("Fetching repositories...");let s=await g.getIntegrationRepos("github"),a=await Xr.default.prompt([{type:"list",name:"repo",message:"Select a repository to sync to:",choices:s.map(p=>({name:`${p.fullName} ${p.private?"(\u{1F512})":""}`,value:p.id.toString(),short:p.fullName}))}]),i=s.find(p=>p.id.toString()===a.repo);i&&(o={owner:i.owner,name:i.name})}console.log(N.default.blue(`
|
|
85
|
-
Syncing secrets from ${e.env} to GitHub repo: ${o.owner}/${o.name}...`));let n=await g.syncSecretsToGithub({projectId:t,environment:e.env,repoOwner:o.owner,repoName:o.name,secretPrefix:e.prefix});console.log(N.default.green(`
|
|
86
|
-
Successfully synced ${n.summary.synced} secrets!`)),n.summary.failed>0&&console.log(N.default.red(`${n.summary.failed} secrets failed to sync.`)),n.results.forEach(s=>{s.success?console.log(N.default.green(`\u2713 ${s.key}`)):console.log(N.default.red(`\u2717 ${s.key}: ${s.error}`))})}catch(t){console.error(N.default.red(`
|
|
87
|
-
Error syncing secrets:`),t.message||t),process.exit(1)}});var It=new no.Command("kubernetes");It.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(N.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1))),console.log(N.default.blue(`Generating Kubernetes manifest for ${e.env}...`));let r=await g.exportKubernetesSecret(t,{environment:e.env,namespace:e.namespace,name:e.name});e.output?(Yr.default.writeFileSync(e.output,r),console.log(N.default.green(`
|
|
88
|
-
Manifest saved to ${e.output}`))):console.log(`
|
|
89
|
-
`+r)}catch(t){console.error(N.default.red(`
|
|
90
|
-
Error generating manifest:`),t.message||t),process.exit(1)}});It.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(N.default.red("Error: kubectl not found or not configured.")),process.exit(1)}let r=e.project;r||(r=y().project,r||(console.error(N.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1))),console.log(N.default.blue(`Fetching secrets for ${e.env}...`));let o=await g.exportKubernetesSecret(r,{environment:e.env,namespace:e.namespace});console.log(N.default.blue("Applying to Kubernetes..."));try{let n=require("child_process").spawn("kubectl",["apply","-f","-"]);n.stdin.write(o),n.stdin.end(),n.stdout.on("data",s=>{console.log(s.toString())}),n.stderr.on("data",s=>{console.error(N.default.yellow(s.toString()))})}catch(n){console.error(N.default.red("Failed to apply manifest:"),n.message)}}catch(t){console.error(N.default.red(`
|
|
91
|
-
Error:`),t.message||t),process.exit(1)}});var ao=require("commander"),te=m(require("chalk"));I();C();var io=new ao.Command("history");io.argument("<key>","Secret key").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment","development").description("View version history of a secret").action(async(e,t)=>{try{let r=t.project;r||(r=y().project,r||(console.error(te.default.red("Error: Project ID not found. Use -p <id> or run 'xtra project set' first.")),process.exit(1)));let o=await g.getSecretHistory(r,t.env,e);console.log(te.default.bold(`
|
|
92
|
-
History for ${e} (${t.env})`)),console.log(te.default.gray(`Current Version: ${o.currentVersion}
|
|
93
|
-
`));let n=o.history||[];n.length===0?console.log("No history found."):n.forEach(s=>{let a=new Date(s.updatedAt).toLocaleString();console.log(te.default.blue(`v${s.version}`)+` - ${a} by ${s.updatedBy}`),s.description&&console.log(te.default.gray(` ${s.description}`)),console.log("")})}catch(r){console.error(te.default.red("Error fetching history:"),r.message||r)}});var vs=new ao.Command("rollback");vs.argument("<key>","Secret key").argument("<version>","Version to rollback to").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment","development").description("Rollback a secret to a previous version").action(async(e,t,r)=>{try{let o=r.project;o||(o=y().project,o||(console.error(te.default.red("Error: Project ID not found. Use -p <id> or run 'xtra project set' first.")),process.exit(1))),console.log(te.default.yellow(`Rolling back ${e} to v${t}...`));let n=await g.rollbackSecret(o,r.env,e,t);n.success?console.log(te.default.green(`
|
|
94
|
-
Successfully rolled back to v${t} (New current version: v${n.version})`)):console.log(te.default.red("Rollback failed."))}catch(o){console.error(te.default.red("Error rolling back:"),o.message||o)}});var Gr=require("commander"),we=m(require("chalk"));I();C();var zr=m(require("inquirer")),co=new Gr.Command("env");co.command("clone").description("Clone secrets from one environment to another").requiredOption("--from <env>","Source environment (e.g. production)").requiredOption("--to <env>","Destination environment (e.g. staging)").option("-p, --project <id>","Project ID").option("-b, --branch <name>","Branch Name").option("--overwrite","Overwrite existing secrets",!1).action(async e=>{try{let t=e.project,r=e.branch||y().branch||"main";t||(t=y().project,t||(console.error(we.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first.")),process.exit(1)));let o=e.from.toLowerCase(),n=e.to.toLowerCase();if(o===n&&(console.error(we.default.red("Source and destination must be different.")),process.exit(1)),console.log(we.default.blue(`Cloning secrets from ${o} to ${n} (branch: ${r})...`)),e.overwrite&&console.log(we.default.yellow("Warning: Existing secrets in destination will be overwritten!")),!(await zr.default.prompt([{type:"confirm",name:"proceed",message:"Are you sure you want to proceed?",default:!1}])).proceed){console.log("Operation cancelled.");return}let a=await g.cloneEnvironment(t,o,n,e.overwrite,r);a.success&&(console.log(we.default.green(`
|
|
95
|
-
Clone successful!`)),console.log(`Copied: ${a.summary.copied}`),console.log(`Updated: ${a.summary.updated} (overwrite: ${e.overwrite})`),console.log(`Skipped: ${a.summary.skipped}`))}catch(t){t.response&&t.response.data&&t.response.data.error?console.error(we.default.red("Error cloning environment:"),t.response.data.error):console.error(we.default.red("Error cloning environment:"),t.message||t)}});var Zr=require("commander"),b=m(require("chalk")),en=m(require("readline"));I();C();var ie=["development","staging","production"],At={development:b.default.green,staging:b.default.yellow,production:b.default.red},lo=process.stdout.columns||120,Ie={tl:"\u256D",tr:"\u256E",bl:"\u2570",br:"\u256F",h:"\u2500",v:"\u2502",x:"\u253C",lt:"\u251C",rt:"\u2524",tt:"\u252C",bt:"\u2534"};function Ae(e,t=Ie.h){return t.repeat(e)}function po(e,t,r,o){let n=o?b.default.cyan:b.default.hex("#2d3a4d"),s=r-2,a=f=>f.padEnd(s).slice(0,s),i=o?b.default.bold.cyan(` ${e} `):b.default.hex("#4a5568")(` ${e} `),p=n(Ie.tl)+i+n(Ae(s-e.length-2))+n(Ie.tr),c=t.map(f=>n(Ie.v)+f.slice(0,s).padEnd(s)+n(Ie.v)),l=n(Ie.bl)+n(Ae(s))+n(Ie.br);return[p,...c,l]}function Qr(){process.stdout.write("\x1B[H\x1B[2J\x1B[3J")}async function ws(){let e="projects",t=[],r=0,o=0,n=[],s=0,a="Loading\u2026",i=!1,p=!1;async function c(){a="Loading projects\u2026",f();try{let d=await g.getProjects();t=Array.isArray(d)?d:d.projects??[],a=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(d){a=b.default.red(`\u2717 ${d.message}`)}f()}async function l(){if(t[r]){i=!0,a=`Fetching secrets for ${b.default.cyan(t[r].name)} / ${At[ie[o]](ie[o])}\u2026`,f();try{let u=y().branch||"main",h=await g.getSecrets(t[r].id,ie[o],u);h&&typeof h=="object"&&!Array.isArray(h)?n=Object.entries(h).map(([w,j])=>({id:w,key:w,value:String(j),updatedAt:new Date().toISOString()})):Array.isArray(h)?n=h:n=[],s=0,a=n.length>0?`${n.length} secret(s) \xB7 V toggle values \xB7 Tab switch panel \xB7 Q quit`:"No secrets in this environment."}catch(d){n=[],a=b.default.red(`\u2717 ${d.message}`)}i=!1,f()}}function f(){Qr();let d=b.default.bold.cyan(" \u{1F512} XtraSecurity")+b.default.hex("#4a5a6b")(" \u2014 Interactive Secrets Shell"),u=At[ie[o]]?At[ie[o]](ie[o]):b.default.white(ie[o]),h=t[r]?b.default.hex("#4a5568")(` ${t[r].name} `)+b.default.hex("#4a5568")("/")+b.default.hex("#4a5568")(` ${ie[o]}`):b.default.hex("#4a5568")(" No project selected");console.log(),console.log(d+b.default.hex("#2d3a4d")(" \u2502")+h),console.log(b.default.hex("#1e293b")(Ae(lo))),console.log();let w=26,j=lo-w-3,v=(t.length>0?t:[{id:"",name:"(loading\u2026)"}]).map((B,le)=>{let oe=le===r,Ne=oe?b.default.cyan("\u25B6 "):" ",Le=oe&&e==="projects"?b.default.bold.bgCyan.black(` ${B.name} `.padEnd(w-4)):oe?b.default.bold.cyan(B.name):b.default.hex("#6b7c93")(B.name);return` ${Ne}${Le}`}),A=po("PROJECTS",v,w,e==="projects"),fe=ie.map((B,le)=>{let oe=le===o,Ne=oe?b.default.cyan("\u25B6 "):" ",Le=At[B]??b.default.white,pt=oe&&e==="env"?b.default.bold.bgCyan.black(` ${B} `.padEnd(w-4)):oe?Le.bold(B):b.default.hex("#6b7c93")(B);return` ${Ne}${pt}`}),G=po("ENVIRONMENT",fe,w,e==="env"),X=Math.floor((j-3)*.45),ue=Math.floor((j-3)*.35),Ut=j-X-ue-4,Yn=b.default.bold.hex("#64748b")("KEY".padEnd(X))+" "+b.default.bold.hex("#64748b")("VALUE".padEnd(ue))+" "+b.default.bold.hex("#64748b")("UPDATED".padEnd(Ut)),Gn=b.default.hex("#1e293b")(Ae(X)+" "+Ae(ue)+" "+Ae(Ut)),lt;i?lt=[` ${b.default.cyan("\u280B")} Loading\u2026`]:n.length===0?lt=[` ${b.default.hex("#4a5568")("No secrets in this environment.")}`]:lt=[" "+Yn," "+Gn,...n.map((B,le)=>{let oe=le===s&&e==="secrets",Ne=B.key.padEnd(X).slice(0,X),Le=p?B.value.padEnd(ue).slice(0,ue):"\u25CF".repeat(Math.min(8,ue)).padEnd(ue).slice(0,ue),pt=new Date(B.updatedAt).toLocaleDateString("en-GB",{day:"2-digit",month:"short"}).padEnd(Ut);return oe?b.default.bold.bgHex("#0e243e")(" \u25B6 "+b.default.cyan(Ne)+" "+b.default.greenBright(Le)+" "+b.default.hex("#4a9eff")(pt)+" "):" "+b.default.white(Ne)+" "+b.default.hex("#4a5568")(Le)+" "+b.default.hex("#374151")(pt)})];let $o=po(`SECRETS (${n.length})`,lt,j,e==="secrets"),So=[...A,"","",...G],zn=Math.max(So.length,$o.length),Qn=b.default.hex("#1e293b")(" \u2502 ");for(let B=0;B<zn;B++){let le=So[B]??" ".repeat(w),oe=$o[B]??" ".repeat(j);console.log(le+Qn+oe)}console.log(),console.log(b.default.hex("#1e293b")(Ae(lo)));let Zn=[[b.default.hex("#64748b")("\u2191\u2193"),"Navigate"],[b.default.hex("#64748b")("Enter"),"Select"],[b.default.hex("#64748b")("Tab"),"Switch Panel"],[b.default.hex("#64748b")("V"),p?b.default.green("Hide values"):b.default.yellow("Show values")],[b.default.hex("#64748b")("Q"),"Quit"]].map(([B,le])=>`${B} ${b.default.hex("#374151")(le)}`).join(b.default.hex("#1e293b")(" \xB7 "));console.log(" "+Zn),console.log(),console.log(" "+(a||""))}en.emitKeypressEvents(process.stdin),process.stdin.isTTY&&process.stdin.setRawMode(!0),process.stdin.on("keypress",async(d,u)=>{if(u){if((u.name==="q"||u.ctrl&&u.name==="c")&&(process.stdin.isTTY&&process.stdin.setRawMode(!1),Qr(),console.log(b.default.cyan(` Goodbye! \u{1F44B}
|
|
96
|
-
`)),process.exit(0)),u.name==="v"){p=!p,f();return}if(u.name==="tab"){e=e==="projects"?"env":e==="env"?"secrets":"projects",f();return}if(e==="projects"&&(u.name==="up"&&(r=Math.max(0,r-1)),u.name==="down"&&(r=Math.min(t.length-1,r+1)),u.name==="return")){e="env",f();return}if(e==="env"&&(u.name==="up"&&(o=Math.max(0,o-1)),u.name==="down"&&(o=Math.min(ie.length-1,o+1)),u.name==="return")){e="secrets",await l();return}e==="secrets"&&(u.name==="up"&&(s=Math.max(0,s-1)),u.name==="down"&&(s=Math.min(n.length-1,s+1))),f()}}),await c(),t.length>0?await l():f()}var tn=new Zr.Command("ui").description("Launch interactive TUI secrets dashboard (arrow keys, Tab, Q to quit)").action(async()=>{await ws()});var on=require("commander"),Be=m(require("fs")),st=m(require("path")),rn=m(require("os")),be=m(require("chalk")),mo=["login","run","secrets","generate","status","diff","rollback","scan","logs","export","import","rotate","audit","access","branch","checkout","project","admin","integration","kubernetes","history","env","ui","completion"],fo={secrets:["list","set","link"],branch:["list","create","delete","update"],admin:["users","roles","role","set-role"],access:["request","list","approve"],audit:["verify","export"],integration:["sync","export","apply"],env:["clone"],project:["set","current"],completion:["bash","zsh","powershell","install"]};function nn(){let e=Object.entries(fo).map(([t,r])=>` ${t}) echo "${r.join(" ")}" ;;`).join(`
|
|
97
|
-
`);return`# xtra bash completion
|
|
98
|
-
# Add to ~/.bashrc: source <(xtra completion bash)
|
|
99
|
-
_xtra_completions() {
|
|
100
|
-
local cur prev words
|
|
101
|
-
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
102
|
-
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
103
|
-
|
|
104
|
-
if [ "\${COMP_CWORD}" -eq 1 ]; then
|
|
105
|
-
COMPREPLY=( $(compgen -W "${mo.join(" ")}" -- "$cur") )
|
|
106
|
-
return
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
|
-
local subcommands=""
|
|
110
|
-
case "\${COMP_WORDS[1]}" in
|
|
111
|
-
${e}
|
|
112
|
-
*) ;;
|
|
113
|
-
esac
|
|
114
|
-
|
|
115
|
-
if [ -n "$subcommands" ]; then
|
|
116
|
-
COMPREPLY=( $(compgen -W "$subcommands" -- "$cur") )
|
|
117
|
-
fi
|
|
118
|
-
}
|
|
119
|
-
complete -F _xtra_completions xtra
|
|
120
|
-
`}function sn(){let e=Object.entries(fo).map(([t,r])=>` (${t})
|
|
121
|
-
local subcmds=(${r.map(o=>`'${o}'`).join(" ")})
|
|
122
|
-
_describe 'subcommand' subcmds ;;`).join(`
|
|
123
|
-
`);return`#compdef xtra
|
|
124
|
-
# xtra zsh completion
|
|
125
|
-
# Add to ~/.zshrc: source <(xtra completion zsh)
|
|
126
|
-
_xtra() {
|
|
127
|
-
local state
|
|
128
|
-
_arguments \\
|
|
129
|
-
'1: :->command' \\
|
|
130
|
-
'*: :->args'
|
|
131
|
-
|
|
132
|
-
case $state in
|
|
133
|
-
command)
|
|
134
|
-
local commands=(${mo.map(t=>`'${t}'`).join(" ")})
|
|
135
|
-
_describe 'command' commands ;;
|
|
136
|
-
args)
|
|
137
|
-
case $words[2] in
|
|
138
|
-
${e}
|
|
139
|
-
esac ;;
|
|
140
|
-
esac
|
|
141
|
-
}
|
|
142
|
-
_xtra
|
|
143
|
-
`}function an(){let e=Object.entries(fo).map(([t,r])=>` '${t}' { $subCommands = @(${r.map(o=>`'${o}'`).join(", ")}) }`).join(`
|
|
144
|
-
`);return`# xtra PowerShell completion
|
|
145
|
-
# Add to $PROFILE: xtra completion powershell | Invoke-Expression
|
|
146
|
-
Register-ArgumentCompleter -Native -CommandName xtra -ScriptBlock {
|
|
147
|
-
param($wordToComplete, $commandAst, $cursorPosition)
|
|
148
|
-
$tokens = $commandAst.CommandElements
|
|
149
|
-
$topCommands = @(${mo.map(t=>`'${t}'`).join(", ")})
|
|
150
|
-
|
|
151
|
-
if ($tokens.Count -le 2) {
|
|
152
|
-
$topCommands | Where-Object { $_ -like "$wordToComplete*" } |
|
|
153
|
-
ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
|
|
154
|
-
return
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
$subCommands = @()
|
|
158
|
-
switch ($tokens[1]) {
|
|
159
|
-
${e}
|
|
160
|
-
}
|
|
161
|
-
$subCommands | Where-Object { $_ -like "$wordToComplete*" } |
|
|
162
|
-
ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
|
|
163
|
-
}
|
|
164
|
-
`}function bs(e){let t=rn.homedir(),r,o,n;switch(e){case"bash":r=st.join(t,".bashrc"),o=`
|
|
165
|
-
# xtra CLI completion
|
|
166
|
-
source <(xtra completion bash)
|
|
167
|
-
`,n=nn();break;case"zsh":r=st.join(t,".zshrc"),o=`
|
|
168
|
-
# xtra CLI completion
|
|
169
|
-
source <(xtra completion zsh)
|
|
170
|
-
`,n=sn();break;case"powershell":r=st.join(t,"Documents","WindowsPowerShell","Microsoft.PowerShell_profile.ps1"),o=`
|
|
171
|
-
# xtra CLI completion
|
|
172
|
-
xtra completion powershell | Invoke-Expression
|
|
173
|
-
`,n=an();break;default:console.error(be.default.red(`Unsupported shell: ${e}. Use bash, zsh, or powershell.`)),process.exit(1)}let s=st.join(t,`.xtra-completion.${e}`);Be.writeFileSync(s,n,"utf8");let a="";try{a=Be.readFileSync(r,"utf8")}catch{}a.includes("xtra completion")?console.log(be.default.yellow(`Completion already installed in ${r}. Skipping.`)):(Be.appendFileSync(r,o,"utf8"),console.log(be.default.green(`\u2705 Completion installed!
|
|
174
|
-
`)),console.log(` Script saved to: ${be.default.cyan(s)}`),console.log(` Sourced in: ${be.default.cyan(r)}`),console.log(`
|
|
175
|
-
Restart your shell or run: ${be.default.bold(`source ${r}`)}`))}var cn=new on.Command("completion").description("Generate shell completion scripts (bash, zsh, powershell)").argument("[shell]","Target shell: bash | zsh | powershell","bash").option("--install","Automatically install completion into your shell profile").addHelpText("after",`
|
|
176
|
-
Examples:
|
|
177
|
-
$ xtra completion bash # Print bash completion script
|
|
178
|
-
$ xtra completion zsh # Print zsh completion script
|
|
179
|
-
$ xtra completion powershell # Print PowerShell completion script
|
|
180
|
-
$ source <(xtra completion bash) # Activate in current session
|
|
181
|
-
$ xtra completion bash --install # Auto-install to ~/.bashrc
|
|
182
|
-
$ xtra completion powershell --install # Auto-install to $PROFILE
|
|
183
|
-
`).action((e,t)=>{let r=e.toLowerCase();if(t.install){bs(r);return}switch(r){case"bash":process.stdout.write(nn());break;case"zsh":process.stdout.write(sn());break;case"powershell":process.stdout.write(an());break;default:console.error(be.default.red(`Unknown shell: '${e}'. Use bash, zsh, or powershell.`)),process.exit(1)}});var gn=require("commander"),x=m(require("chalk")),uo=require("table"),go=m(require("inquirer"));var ln=m(require("conf")),Re=new ln.default({projectName:"xtra-cli",defaults:{activeProfile:"default",profiles:{default:{apiUrl:process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api"}}}});function je(){return Re.get("profiles")||{}}function Rt(){return process.env.XTRA_PROFILE||Re.get("activeProfile")||"default"}function pn(e){if(!je()[e])throw new Error(`Profile '${e}' does not exist. Create it first with 'xtra profile create ${e}'.`);Re.set("activeProfile",e)}function dn(e,t={}){let r=je();if(r[e])throw new Error(`Profile '${e}' already exists. Use 'xtra profile set' to edit it.`);r[e]={apiUrl:process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api",...t},Re.set("profiles",r)}function mn(e){if(e==="default")throw new Error("Cannot delete the 'default' profile.");let t=je();if(!t[e])throw new Error(`Profile '${e}' not found.`);delete t[e],Re.set("profiles",t),Rt()===e&&Re.set("activeProfile","default")}function fn(e,t,r){let o=je();if(!o[e])throw new Error(`Profile '${e}' not found.`);o[e][t]=r,Re.set("profiles",o)}function un(e){if(!je()[e])throw new Error(`Profile '${e}' does not exist. Run 'xtra profile create ${e}' first.`);process.env.XTRA_PROFILE=e}var Pt=e=>e?.message||"Unknown error",xe=new gn.Command("profile").description("Manage named configuration profiles (token, apiUrl, project per profile)").addHelpText("after",`
|
|
184
|
-
Examples:
|
|
185
|
-
$ xtra profile list # Show all profiles
|
|
186
|
-
$ xtra profile create work --url https://app.xtrasecurity.io/api
|
|
187
|
-
$ xtra profile use work # Switch active profile
|
|
188
|
-
$ xtra profile set work --project abc123 # Set a value in a profile
|
|
189
|
-
$ xtra profile delete old-profile # Remove a profile
|
|
190
|
-
$ xtra --profile work secrets list # Use profile for one command
|
|
191
|
-
`);xe.command("list").alias("ls").description("List all saved profiles").action(()=>{let e=je(),t=Rt();if(Object.keys(e).length===0){console.log(x.default.yellow("No profiles found."));return}let r=[[x.default.bold("Profile"),x.default.bold("API URL"),x.default.bold("Project"),x.default.bold("Status")]];for(let[o,n]of Object.entries(e)){let s=o===t;r.push([s?x.default.green(`\u25B6 ${o}`):x.default.white(` ${o}`),x.default.gray(n.apiUrl||"(default)"),x.default.cyan(n.project||"(not set)"),s?x.default.green("active"):x.default.gray("inactive")])}console.log(x.default.bold(`
|
|
192
|
-
Profiles:
|
|
193
|
-
`)),console.log((0,uo.table)(r)),console.log(x.default.gray(`Tip: Use XTRA_PROFILE=<name> to override per-command.
|
|
194
|
-
`))});xe.command("create <name>").description("Create a new profile").option("--url <apiUrl>","XtraSecurity API URL for this profile").option("--project <projectId>","Default project ID for this profile").option("--token <token>","Auth token for this profile").action(async(e,t)=>{try{let{url:r,project:o,token:n}=t;if(!r&&!o&&!n){let s=await go.default.prompt([{type:"input",name:"url",message:"API URL:",default:"https://www.xtrasecurity.in/api"},{type:"input",name:"project",message:"Default Project ID (optional):",default:""}]);r=s.url,o=s.project||void 0}dn(e,{apiUrl:r,project:o||void 0,token:n||void 0}),console.log(x.default.green(`\u2705 Profile '${e}' created!`)),console.log(x.default.gray(` Activate it with: xtra profile use ${e}`))}catch(r){console.error(x.default.red(Pt(r)))}});xe.command("use <name>").description("Switch to a different profile (persists across sessions)").action(e=>{try{pn(e),console.log(x.default.green(`\u2705 Switched to profile '${e}'.`)),console.log(x.default.gray(" All future commands will use this profile."))}catch(t){console.error(x.default.red(Pt(t)))}});xe.command("set <name>").description("Update a value in an existing profile").option("--url <apiUrl>","Update the API URL").option("--project <projectId>","Update the default project ID").option("--token <token>","Update the auth token").option("--env <environment>","Update the default environment").action((e,t)=>{let r={url:"apiUrl",project:"project",token:"token",env:"env"},o=[];try{for(let[n,s]of Object.entries(r))t[n]&&(fn(e,s,t[n]),o.push(`${s} = ${s==="token"?"****":t[n]}`));if(o.length===0){console.log(x.default.yellow("No values provided. Use --url, --project, --token, or --env."));return}console.log(x.default.green(`\u2705 Profile '${e}' updated:`)),o.forEach(n=>console.log(x.default.gray(` ${n}`)))}catch(n){console.error(x.default.red(Pt(n)))}});xe.command("current").description("Show the currently active profile").action(()=>{let e=Rt(),r=je()[e]||{};console.log(x.default.bold(`
|
|
195
|
-
Active Profile: ${x.default.green(e)}
|
|
196
|
-
`));let o=[[x.default.bold("Key"),x.default.bold("Value")],["API URL",x.default.cyan(r.apiUrl||"(default)")],["Project",x.default.cyan(r.project||"(not set)")],["Branch",x.default.cyan(r.branch||"(not set)")],["Env",x.default.cyan(r.env||"(not set)")],["Token",r.token?x.default.green("Set \u2713"):x.default.gray("Not set")]];console.log((0,uo.table)(o))});xe.command("delete <name>").alias("rm").description("Delete a profile").option("-y, --yes","Skip confirmation prompt").action(async(e,t)=>{try{if(!t.yes){let{confirm:r}=await go.default.prompt([{type:"confirm",name:"confirm",message:x.default.red(`Delete profile '${e}'? This cannot be undone.`),default:!1}]);if(!r){console.log(x.default.gray("Cancelled."));return}}mn(e),console.log(x.default.green(`\u2705 Profile '${e}' deleted.`))}catch(r){console.error(x.default.red(Pt(r)))}});var hn=require("commander"),yn=require("child_process"),vn=m(require("axios")),wn=m(require("fs"));C();function at(){let e=process.env.XTRA_MACHINE_TOKEN,t=process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api";return e||K("XTRA_MACHINE_TOKEN environment variable is required for CI mode."),vn.default.create({baseURL:t,headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`,"X-CI-Mode":"true"}})}function K(e,t){process.stderr.write(JSON.stringify({ok:!1,error:e,detail:t||null},null,2)+`
|
|
197
|
-
`),process.exit(1)}function ho(e){process.stdout.write(JSON.stringify(e,null,2)+`
|
|
198
|
-
`)}function it(){process.env.XTRA_MACHINE_TOKEN||K("XTRA_MACHINE_TOKEN is not set. Export it before running CI commands.",{hint:"export XTRA_MACHINE_TOKEN=your_service_account_token"})}function Ot(e){return e||K("--project <id> is required in CI mode. Set it explicitly (no config fallback)."),e}function Dt(e){return e||K("--env <environment> is required in CI mode."),e}var Pe=new hn.Command("ci").description("CI/CD headless mode \u2014 JSON output, no prompts, XTRA_MACHINE_TOKEN auth").addHelpText("after",`
|
|
199
|
-
Environment Variables:
|
|
200
|
-
XTRA_MACHINE_TOKEN Required. Service account token for authentication.
|
|
201
|
-
XTRA_API_URL Optional. Override the API base URL.
|
|
202
|
-
XTRA_PROFILE Optional. Use a specific named profile.
|
|
203
|
-
|
|
204
|
-
Subcommands:
|
|
205
|
-
xtra ci secrets Fetch secrets as JSON
|
|
206
|
-
xtra ci set Set one or more secrets KEY=VALUE
|
|
207
|
-
xtra ci export Export secrets in dotenv/json/yaml format to a file
|
|
208
|
-
xtra ci run Run a command with secrets injected as env vars
|
|
209
|
-
|
|
210
|
-
Examples:
|
|
211
|
-
# Uses project/env from .xtrarc if available
|
|
212
|
-
XTRA_MACHINE_TOKEN=tok_... xtra ci secrets
|
|
213
|
-
|
|
214
|
-
# Explicit flags (always prioritized)
|
|
215
|
-
XTRA_MACHINE_TOKEN=tok_... xtra ci secrets -p proj123 -e production
|
|
216
|
-
XTRA_MACHINE_TOKEN=tok_... xtra ci set -p proj123 -e staging DEPLOY_VERSION=1.2.3
|
|
217
|
-
XTRA_MACHINE_TOKEN=tok_... xtra ci run -- node app.js
|
|
218
|
-
`);Pe.command("secrets").description("Fetch secrets as JSON (pipe-friendly)").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment").option("-b, --branch <branch>","Branch name").option("--keys <keys>","Comma-separated list of keys to include (default: all)").action(async e=>{it();let t=y(),r=Ot(e.project||t.project),o=Dt(e.env||t.env),n=e.branch||t.branch||"main";try{let i=(await at().get(`/projects/${r}/envs/${o}/secrets?branch=${n}`)).data;if(e.keys){let p=e.keys.split(",").map(l=>l.trim()),c={};for(let l of p)i[l]!==void 0?c[l]=i[l]:process.stderr.write(`Warning: key '${l}' not found
|
|
219
|
-
`);i=c}ho({ok:!0,project:r,env:o,branch:e.branch,secrets:i})}catch(s){let a=s?.response?.status,i=s?.response?.data?.error||s.message;a===401&&K("Unauthorized. Check your XTRA_MACHINE_TOKEN.",{status:a}),a===403&&K("Forbidden. This token lacks permission to read secrets.",{status:a}),a===404&&K(`Project '${r}' or environment '${o}' not found.`,{status:a}),K(i,{status:a})}});Pe.command("set").description("Set one or more secrets in CI mode (KEY=VALUE ...)").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment").option("-b, --branch <branch>","Branch name").argument("<secrets...>","Secrets to set (format: KEY=VALUE)").action(async(e,t)=>{it();let r=y(),o=Ot(t.project||r.project),n=Dt(t.env||r.env),s=t.branch||r.branch||"main",a={};for(let i of e){let p=i.indexOf("=");p===-1&&K(`Invalid format: '${i}'. Expected KEY=VALUE`),a[i.substring(0,p)]=i.substring(p+1)}Object.keys(a).length===0&&K("No valid KEY=VALUE pairs provided.");try{await at().post(`/projects/${o}/envs/${n}/secrets`,{secrets:a,branch:s}),ho({ok:!0,project:o,env:n,branch:s,updated:Object.keys(a),count:Object.keys(a).length})}catch(i){let p=i?.response?.status;K(i?.response?.data?.error||i.message,{status:p})}});Pe.command("export").description("Export secrets to a file (dotenv, json, or github-actions format)").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment").option("-b, --branch <branch>","Branch name").option("-f, --format <format>","Output format: dotenv | json | github","dotenv").option("-o, --output <file>","Output file path (default: stdout)").action(async e=>{it();let t=y(),r=Ot(e.project||t.project),o=Dt(e.env||t.env),n=e.branch||t.branch||"main",{format:s,output:a}=e,i=["dotenv","json","github"];i.includes(s)||K(`Invalid format '${s}'. Must be one of: ${i.join(", ")}`);try{let l=(await at().get(`/projects/${r}/envs/${o}/secrets?branch=${n}`)).data,f;switch(s){case"dotenv":f=Object.entries(l).map(([d,u])=>`${d}=${u}`).join(`
|
|
220
|
-
`)+`
|
|
221
|
-
`;break;case"json":f=JSON.stringify(l,null,2)+`
|
|
222
|
-
`;break;case"github":f=Object.entries(l).map(([d,u])=>`${d}=${u}`).join(`
|
|
223
|
-
`)+`
|
|
224
|
-
`;break;default:K("Unreachable format error.")}a?(wn.writeFileSync(a,f,"utf8"),process.stderr.write(JSON.stringify({ok:!0,file:a,keys:Object.keys(l).length})+`
|
|
225
|
-
`)):process.stdout.write(f)}catch(p){let c=p?.response?.status;K(p?.response?.data?.error||p.message,{status:c})}});Pe.command("run").description("Run a command with secrets injected as environment variables").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment").option("-b, --branch <branch>","Branch name").argument("<command>","Command to execute").argument("[args...]","Command arguments").action(async(e,t,r)=>{it();let o=y(),n=Ot(r.project||o.project),s=Dt(r.env||o.env),a=r.branch||o.branch||"main";/[;&|`$<>\\\n]/g.test(e)&&K("Command contains unsafe characters. Aborting for security.");try{let l=(await at().get(`/projects/${n}/envs/${s}/secrets?branch=${a}`)).data;process.stderr.write(`[xtra ci] Injecting ${Object.keys(l).length} secrets into process env
|
|
226
|
-
`);let f={...process.env,...l},d=(0,yn.spawn)(e,t,{env:f,stdio:"inherit",shell:!1});d.on("exit",u=>process.exit(u??0)),d.on("error",u=>K(`Failed to start process: ${u.message}`))}catch(p){let c=p?.response?.status;K(p?.response?.data?.error||p.message,{status:c})}});Pe.command("validate").description("Validate that XTRA_MACHINE_TOKEN is set and working").action(async()=>{it();try{let t=await at().get("/project");ho({ok:!0,authenticated:!0,projects:t.data.length,message:"XTRA_MACHINE_TOKEN is valid and working."})}catch(e){let t=e?.response?.status;t===401&&K("XTRA_MACHINE_TOKEN is invalid or expired.",{status:t}),K(e?.response?.data?.error||e.message,{status:t})}});var bn=require("commander");I();C();var P=m(require("chalk")),yo=m(require("ora")),ce=m(require("fs")),Oe=m(require("path")),Tt=/\{\{\s*(secrets|env)\.([A-Z0-9_a-z]+)(?:\s*\|\s*([^}]*?))?\s*\}\}/g;function jn(e,t,r){let o=[],n=[],s=0;return{output:e.replace(Tt,(i,p,c,l)=>{let f;if(p==="secrets"?f=t[c]:p==="env"&&(f=r[c]),f!==void 0&&f!=="")return s++,f;if(l!==void 0){let d=l.trim();return n.push(`${p}.${c} \xE2\u2020\u2019 "${d}"`),s++,d}return o.push(`${p}.${c}`),`{{ ${p}.${c} }}`}),replacedCount:s,missingKeys:o,usedDefaults:n}}var ct=new bn.Command("template").description("Secret templating engine \xE2\u20AC\u201D inject secrets into config file templates").addHelpText("after",`
|
|
227
|
-
Template Syntax:
|
|
228
|
-
{{ secrets.KEY }} Replace with secret value
|
|
229
|
-
{{ secrets.KEY | default }} Replace with secret value, fall back to "default"
|
|
230
|
-
{{ env.KEY }} Replace with local environment variable
|
|
231
|
-
{{ env.KEY | default }} Replace with local env variable, fall back to "default"
|
|
232
|
-
|
|
233
|
-
Examples:
|
|
234
|
-
$ xtra template render config.yaml.tpl -p proj123 -e production -o config.yaml
|
|
235
|
-
$ xtra template render nginx.conf.tpl -p proj123 -e production | sudo tee /etc/nginx/nginx.conf
|
|
236
|
-
$ xtra template check config.yaml.tpl -p proj123 -e production
|
|
237
|
-
$ xtra template list config.yaml.tpl
|
|
238
|
-
`);ct.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:r,env:o,branch:n,output:s,strict:a}=t;o={dev:"development",stg:"staging",prod:"production"}[o]||o,r||(r=y().project),r||(console.error(P.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'.")),process.exit(1));let p=Oe.resolve(process.cwd(),e);ce.existsSync(p)||(console.error(P.default.red(`Error: Template file not found: ${p}`)),process.exit(1));let c=ce.readFileSync(p,"utf8");[...c.matchAll(Tt)].length===0&&console.warn(P.default.yellow("Warning: No {{ secrets.* }} or {{ env.* }} placeholders found in template."));let f=(0,yo.default)(`Fetching secrets for ${r}/${o}...`).start(),d={};try{d=await g.getSecrets(r,o,n),f.succeed(`Loaded ${Object.keys(d).length} secrets.`)}catch(v){f.fail("Failed to fetch secrets.");let A=v?.response?.data?.error||v.message;console.error(P.default.red(A)),process.exit(1)}let{output:u,replacedCount:h,missingKeys:w,usedDefaults:j}=jn(c,d,process.env);if(console.log(P.default.green(`\u2705 Replaced ${h} placeholder(s).`)),j.length>0&&console.log(P.default.yellow(`\u26A0 Used defaults for: ${j.join(", ")}`)),w.length>0&&(console.log(P.default.red(`\u2717 Unresolved placeholders: ${w.join(", ")}`)),a&&(console.error(P.default.red("Aborting due to --strict mode.")),process.exit(1))),s){let v=Oe.resolve(process.cwd(),s);ce.writeFileSync(v,u,"utf8"),console.log(P.default.blue(`\xE2\u2020\u2019 Written to: ${v}`))}else process.stdout.write(u);try{let{logAudit:v}=(F(),L(V));v("TEMPLATE_RENDERED",r,o,{template:e,output:s||"stdout",replacedCount:h,missingKeys:w})}catch{}});ct.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:r,env:o,branch:n}=t;o={dev:"development",stg:"staging",prod:"production"}[o]||o,r||(r=y().project),r||(console.error(P.default.red("Error: Project ID required.")),process.exit(1));let a=Oe.resolve(process.cwd(),e);ce.existsSync(a)||(console.error(P.default.red(`Template file not found: ${a}`)),process.exit(1));let i=ce.readFileSync(a,"utf8"),p=(0,yo.default)("Fetching secrets for check...").start(),c={};try{c=await g.getSecrets(r,o,n),p.stop()}catch{p.fail("Failed to fetch secrets."),process.exit(1)}let{replacedCount:l,missingKeys:f,usedDefaults:d}=jn(i,c,process.env),u=[...i.matchAll(Tt)].length;console.log(P.default.bold(`
|
|
239
|
-
Template Check: ${e}
|
|
240
|
-
`)),console.log(` Total placeholders : ${u}`),console.log(` Resolved : ${P.default.green(l)}`),console.log(` Using defaults : ${P.default.yellow(d.length)}`),console.log(` Unresolved : ${P.default.red(f.length)}`),d.length>0&&(console.log(P.default.yellow(`
|
|
241
|
-
Defaults used:`)),d.forEach(h=>console.log(` - ${h}`))),f.length>0?(console.log(P.default.red(`
|
|
242
|
-
Missing secrets (no default):`)),f.forEach(h=>console.log(P.default.red(` \u2717 ${h}`))),process.exit(1)):console.log(P.default.green(`
|
|
243
|
-
\u2705 All placeholders are resolvable!`))});ct.command("list <templateFile>").description("List all placeholders found in a template file (no API call needed)").action(e=>{let t=Oe.resolve(process.cwd(),e);ce.existsSync(t)||(console.error(P.default.red(`Template file not found: ${t}`)),process.exit(1));let o=[...ce.readFileSync(t,"utf8").matchAll(Tt)];if(o.length===0){console.log(P.default.yellow("No placeholders found in this template."));return}console.log(P.default.bold(`
|
|
244
|
-
Placeholders in ${Oe.basename(e)}:
|
|
245
|
-
`)),o.forEach(n=>{let[,s,a,i]=n,p=i?P.default.gray(` (default: "${i.trim()}")`):"";console.log(` ${s==="secrets"?"\u{1F512}":"\u{1F4E6}"} ${P.default.cyan(`{{ ${s}.${a} }}`)}${p}`)}),console.log(`
|
|
246
|
-
Total: ${P.default.bold(o.length)} placeholder(s)
|
|
247
|
-
`)});var $n=require("commander"),W=m(require("chalk")),Nt=m(require("fs")),wo=m(require("path")),Sn=m(require("axios"));C();var vo=65,De={tl:"\u256D",tr:"\u256E",bl:"\u2570",br:"\u256F",h:"\u2500",v:"\u2502"};function xn(e){return De.h.repeat(e)}var js=W.default.green("\u2713 "),xs=W.default.red("\u2717 "),$s=W.default.yellow("\u26A0 ");async function Ss(){let e=[],t=re(),r=parseInt(process.versions.node.split(".")[0]);e.push({name:"Node.js Version",status:r>=18?"pass":"fail",message:`v${process.versions.node} ${r>=18?"(OK)":"(requires >=18)"}`});let o=Xe();e.push({name:"Auth Token",status:o?"pass":"fail",message:o?`Set (${o.substring(0,8)}...)`:"Not set \u2014 run 'xtra login'"});let n=t.apiUrl||process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api";e.push({name:"API URL",status:t.apiUrl||process.env.XTRA_API_URL?"pass":"warn",message:n});try{let l=await Sn.default.get(`${n}/health`,{timeout:4e3});e.push({name:"API Connectivity",status:l.status===200?"pass":"warn",message:`${l.status} ${l.statusText}`})}catch(l){let f=l.code==="ECONNREFUSED"?"Connection refused (offline?)":l.code==="ETIMEDOUT"?"Timed out (unreachable)":l.response?.status===404?"API reachable (health endpoint missing)":l.message,d=l.response?.status===404?"pass":"fail";e.push({name:"API Connectivity",status:d,message:f})}let s=y().project;e.push({name:"Active Project",status:s?"pass":"warn",message:s||"Not set \u2014 run 'xtra project set <id>'"});let a=y().branch||"main";e.push({name:"Active Branch",status:"pass",message:a});let i=wo.join(process.cwd(),".xtrarc"),p=wo.join(process.cwd(),"xtra.json"),c=Nt.existsSync(i)||Nt.existsSync(p);return e.push({name:"Project Config",status:c?"pass":"warn",message:c?Nt.existsSync(i)?".xtrarc found":"xtra.json found":"Not found (run 'xtra init')"}),e}var Cn=new $n.Command("doctor").description("Diagnose CLI configuration and API connectivity").option("--json","Output results as JSON",!1).action(async e=>{e.json||(console.log(),console.log(W.default.bold.cyan(" \u2695 XtraSecurity Diagnostics")),console.log(W.default.hex("#4a5568")(" "+De.tl+xn(vo)+De.tr)));let t=await Ss();if(e.json){process.stdout.write(JSON.stringify(t,null,2)+`
|
|
248
|
-
`);return}let r=0,o=0;for(let n of t){n.status==="fail"&&r++,n.status==="warn"&&o++;let s=n.status==="pass"?js:n.status==="warn"?$s:xs,a=W.default.bold(W.default.white(n.name.padEnd(20))),i=n.status==="pass"?W.default.hex("#94a3b8")(n.message):n.status==="warn"?W.default.yellow(n.message):W.default.red(n.message);console.log(W.default.hex("#4a5568")(" "+De.v)+` ${s}${a} ${i}`.padEnd(vo)+W.default.hex("#4a5568")(De.v))}if(console.log(W.default.hex("#4a5568")(" "+De.bl+xn(vo)+De.br)),console.log(),r===0&&o===0)console.log(W.default.green(" \u2713 All systems operational. The CLI is ready to use."));else{let n=[];r>0&&n.push(W.default.red(`${r} error(s)`)),o>0&&n.push(W.default.yellow(`${o} warning(s)`)),console.log(` Found ${n.join(" and ")}.`)}console.log()});var En=require("commander"),J=m(require("chalk")),Lt=m(require("inquirer")),$e=m(require("fs")),bo=m(require("path"));C();var Cs=".xtrarc",kn=new En.Command("init").description("Bootstrap a new project \u2014 creates .xtrarc and recommended structure").option("--project <id>","Project ID (skip prompt)").option("--env <env>","Default environment","development").option("--branch <branch>","Default branch","main").option("-y, --yes","Accept all defaults without prompting",!1).action(async e=>{let t=process.cwd(),r=bo.join(t,Cs);if(console.log(J.default.bold(`
|
|
249
|
-
\u{1F680} xtra init \u2014 setting up your project
|
|
250
|
-
`)),$e.existsSync(r)){let{overwrite:i}=await Lt.default.prompt([{type:"confirm",name:"overwrite",message:J.default.yellow(".xtrarc already exists. Overwrite it?"),default:!1}]);if(!i){console.log(J.default.gray("Aborted."));return}}let{project:o,env:n,branch:s}=e;if(!e.yes){let{getAuthToken:i}=await Promise.resolve().then(()=>(C(),Ye)),p=i(),c=(await import("ora")).default,l=[];if(!p)console.log(J.default.yellow(" \u2139 Not logged in \u2014 project list unavailable. Run `xtra login` first.")),console.log(J.default.yellow(` You can still enter a Project ID manually below.
|
|
251
|
-
`));else{let j=c("Fetching your projects...").start();try{let{api:v}=await Promise.resolve().then(()=>(I(),ft)),A=await v.getProjects();l=Array.isArray(A)?A:A.projects??[],l.length===0?j.warn(J.default.yellow("No projects found in your workspace. Create one at www.xtrasecurity.in first.")):j.succeed(`Found ${l.length} project${l.length>1?"s":""}`)}catch(v){let A=v?.response?.status===401?"session expired \u2014 run `xtra login` again":v?.response?.data?.error||v?.message||"network error";j.warn(J.default.yellow(`Could not fetch projects (${A}). Enter the Project ID manually.`))}}let f=o||M("project")||"",{project:d}=await Lt.default.prompt([{type:l.length>0?"list":"input",name:"project",message:l.length>0?"Select a project:":"Project ID:",choices:l.length>0?l.map(j=>({name:`${j.name} (${j.id})`,value:j.id})):void 0,default:f,validate:j=>j.trim()?!0:"Project ID is required"}]);o=d.trim();let u=[],h=c("Fetching branches...").start();try{let{api:j}=await Promise.resolve().then(()=>(I(),ft));u=await j.getBranches(o),h.stop()}catch{h.warn(J.default.yellow("Could not fetch branches."))}let w=await Lt.default.prompt([{type:"list",name:"env",message:"Default environment:",choices:["development","staging","production"],default:n||"development"},{type:u.length>0?"list":"input",name:"branch",message:u.length>0?"Default branch:":"Default branch name:",choices:u.length>0?u.map(j=>({name:j.name,value:j.name})):void 0,default:s||(u.length>0?u[0].name:"main")},{type:"confirm",name:"addGitignore",message:"Add .xtrarc to .gitignore? (recommended)",default:!0}]);if(n=w.env,s=w.branch,w.addGitignore){let j=bo.join(t,".gitignore"),v="";try{v=$e.readFileSync(j,"utf8")}catch{}v.includes(".xtrarc")||($e.appendFileSync(j,`
|
|
252
|
-
# XtraSecurity config
|
|
253
|
-
.xtrarc
|
|
254
|
-
`),console.log(J.default.gray(" \u2714 Added .xtrarc to .gitignore")))}}o||(console.error(J.default.red("Error: Project ID is required.")),process.exit(1));let a={project:o,env:n,branch:s,apiUrl:process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api",createdAt:new Date().toISOString()};$e.writeFileSync(r,JSON.stringify(a,null,2),"utf8"),U("project",o),U("env",n),U("branch",s),console.log(J.default.gray(` Branch : ${J.default.white(s)}`)),console.log();try{let{logAudit:i}=await Promise.resolve().then(()=>(F(),V));i("PROJECT_INIT",o,n,{branch:s,rcPath:r})}catch{}console.log(J.default.bold(" Next steps:")),console.log(J.default.gray(" xtra login # authenticate")),console.log(J.default.gray(" xtra secrets list # view secrets")),console.log(J.default.gray(" xtra run node app.js # inject and run")),console.log(J.default.gray(" xtra doctor # verify everything works")),console.log()});var Rn=require("commander");I();C();var _=m(require("chalk")),Pn=require("child_process"),de=null,jo="";function In(e){return JSON.stringify(Object.keys(e).sort().map(t=>`${t}=${e[t]}`))}async function An(e,t,r,o){de&&(process.stdout.write(_.default.cyan(`
|
|
255
|
-
[watch] `)+_.default.yellow(`Secret change detected \u2014 restarting...
|
|
256
|
-
`)),de.kill("SIGTERM"),await new Promise(s=>setTimeout(s,500)));let n={...process.env,...r};process.stdout.write(_.default.cyan(" [watch] ")+_.default.green(`Starting: ${e} ${t.join(" ")}
|
|
257
|
-
`)),de=(0,Pn.spawn)(e,t,{env:n,stdio:"inherit",shell:o}),de.on("exit",s=>{s!==null&&s!==0&&process.stdout.write(_.default.cyan(" [watch] ")+_.default.red(`Process exited with code ${s}
|
|
258
|
-
`)),de=null}),de.on("error",s=>{process.stdout.write(_.default.cyan(" [watch] ")+_.default.red(`Failed to start: ${s.message}
|
|
259
|
-
`)),de=null})}var On=new Rn.Command("watch").description("Live reload \u2014 auto-restart process when secrets change in cloud").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment","development").option("-b, --branch <branch>","Branch","main").option("--interval <seconds>","Poll interval in seconds","5").option("--shell","Use shell mode for the child process (for npm run etc.)",!1).argument("<command>","Command to run").argument("[args...]","Command arguments").addHelpText("after",`
|
|
260
|
-
Examples:
|
|
261
|
-
$ xtra watch -p proj123 -e dev node app.js
|
|
262
|
-
$ xtra watch -p proj123 -e dev --interval 10 --shell npm run dev
|
|
263
|
-
$ xtra watch -p proj123 -e dev -- node -r dotenv/config server.js
|
|
264
|
-
`).action(async(e,t,r)=>{let{project:o,env:n,branch:s,interval:a,shell:i}=r;n={dev:"development",stg:"staging",prod:"production"}[n]||n,o||(o=y().project),o||(console.error(_.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'.")),process.exit(1)),n==="production"&&(console.error(_.default.red("\u26A0 xtra watch is not allowed in PRODUCTION for safety reasons.")),console.error(_.default.gray(" Use 'xtra run' for one-shot production injection.")),process.exit(1));let c=Math.max(3,parseInt(a))*1e3,l=process.platform==="win32",f=i||l;console.log(_.default.bold.cyan(`
|
|
265
|
-
\u25CF xtra watch`)+_.default.bold(` \u2014 watching ${n}/${s} (every ${a}s)
|
|
266
|
-
`)),console.log(_.default.gray(` Press Ctrl+C to stop.
|
|
267
|
-
`)),process.on("SIGINT",()=>{de&&de.kill("SIGTERM"),console.log(_.default.cyan(`
|
|
268
|
-
[watch] `)+_.default.gray("Stopped.")),process.exit(0)});try{let d=await g.getSecrets(o,n,s);jo=In(d),await An(e,t,d,f)}catch(d){console.error(_.default.red("Failed to fetch secrets: "+(d?.response?.data?.error||d.message))),process.exit(1)}setInterval(async()=>{try{let d=await g.getSecrets(o,n,s),u=In(d);u!==jo?(jo=u,await An(e,t,d,f)):process.stdout.write(_.default.cyan(" [watch] ")+_.default.gray(`${new Date().toLocaleTimeString()} \u2014 no changes \r`))}catch(d){process.stdout.write(_.default.cyan(`
|
|
269
|
-
[watch] `)+_.default.yellow(`Poll failed: ${d.message}
|
|
270
|
-
`))}},c)});var Dn=require("commander");I();C();var k=m(require("chalk")),Tn=m(require("ora")),Nn=require("table"),Ln=new Dn.Command("simulate").description("Dry-run: show what 'xtra run' would inject without executing the command").option("-p, --project <id>","Project ID").option("-e, --env <environment>","Environment","development").option("-b, --branch <branch>","Branch","main").argument("[command]","Command that would be executed (display only)").option("--show-values","Reveal secret values in output (default: masked)",!1).option("--diff","Highlight secrets that differ from local .env / process.env",!1).action(async(e,t)=>{let{project:r,env:o,branch:n,showValues:s,diff:a}=t;o={dev:"development",stg:"staging",prod:"production"}[o]||o,r||(r=y().project),r||(console.error(k.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'.")),process.exit(1));let p=(0,Tn.default)(`Fetching secrets to simulate injection (${o}/${n})...`).start(),c;try{c=await g.getSecrets(r,o,n),p.stop()}catch(j){p.fail("Failed to fetch secrets."),console.error(k.default.red(j?.response?.data?.error||j.message)),process.exit(1)}let l=Object.keys(c).length,f=process.stdout.columns||100,d=Math.floor(f*.25),u=Math.floor(f*.4),h=Math.floor(f*.15);if(console.log(k.default.bold.cyan(`
|
|
271
|
-
\u25CF Simulation`)+k.default.bold(` \u2014 xtra run ${e?k.default.cyan(e):"(no command specified)"}
|
|
272
|
-
`)),console.log(k.default.gray(` Project : ${k.default.white(r)}`)),console.log(k.default.gray(` Env : ${k.default.white(o)}`)),console.log(k.default.gray(` Branch : ${k.default.white(n)}`)),console.log(k.default.gray(` Secrets : ${k.default.white(l)} would be injected
|
|
273
|
-
`)),l===0){console.log(k.default.yellow(" No secrets found for this environment/branch."));return}let w=[[k.default.bold("Key"),k.default.bold("Injected Value"),k.default.bold("Local .env"),k.default.bold("Status")]];for(let[j,v]of Object.entries(c)){let A=process.env[j],fe=s?k.default.cyan(v):k.default.gray("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"),G=k.default.green("Injected"),X="-";a&&A!==void 0?(X=s?k.default.yellow(A):k.default.gray("\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"),A===v?G=k.default.gray("Same"):G=k.default.yellow("\u26A1 Override")):a&&(G=k.default.blue("New"),X=k.default.gray("(not set)")),w.push([k.default.white(j),fe,a?X:"-",G])}console.log((0,Nn.table)(w,{columns:[{width:d,wrapWord:!0},{width:u,wrapWord:!0},{width:h,wrapWord:!0},{width:10}],border:{topBody:"\u2500",topJoin:"\u252C",topLeft:"\u250C",topRight:"\u2510",bottomBody:"\u2500",bottomJoin:"\u2534",bottomLeft:"\u2514",bottomRight:"\u2518",bodyLeft:"\u2502",bodyRight:"\u2502",bodyJoin:"\u2502",joinBody:"\u2500",joinLeft:"\u251C",joinRight:"\u2524",joinJoin:"\u253C"}})),s||console.log(k.default.gray(" Tip: use --show-values to reveal actual secret values")),a||console.log(k.default.gray(" Tip: use --diff to compare against your local process.env")),console.log(k.default.bold.green(`
|
|
274
|
-
\u2714 Simulation complete.`)+k.default.bold(` ${l} secret(s) would be injected.`)),console.log(k.default.gray(` Run without 'simulate' to actually execute the command.
|
|
275
|
-
`))});var Un=require("commander");I();C();var O=m(require("chalk")),_n=m(require("ora")),Te=m(require("fs")),Ke=m(require("path")),Mn=m(require("readline")),xo=".env.local";function Es(){return M("localMode")===!0||process.env.XTRA_LOCAL_MODE==="true"}var Je=new Un.Command("local").description("Toggle cloud/local mode for offline development").addHelpText("after",`
|
|
276
|
-
In local mode, 'xtra run' reads secrets from .env.local instead of the cloud API.
|
|
277
|
-
This allows fully offline development without any API calls.
|
|
278
|
-
|
|
279
|
-
Examples:
|
|
280
|
-
$ xtra local status # Check current mode
|
|
281
|
-
$ xtra local on # Enable offline (local) mode
|
|
282
|
-
$ xtra local off # Disable \xE2\u20AC\u201D back to cloud mode
|
|
283
|
-
$ xtra local sync # Pull cloud secrets \xE2\u2020\u2019 .env.local
|
|
284
|
-
$ xtra local sync -p proj -e production # Pull production to .env.local
|
|
285
|
-
`);Je.command("status").description("Show current cloud/local mode").action(()=>{let e=Es(),t=Ke.join(process.cwd(),xo),r=Te.existsSync(t);console.log(O.default.bold(`
|
|
286
|
-
Mode Status:
|
|
287
|
-
`)),console.log(` Mode : ${e?O.default.yellow("\u{1F50C} LOCAL (offline)"):O.default.green("\u2601 CLOUD")}`),console.log(` .env.local : ${r?O.default.green("Found"):O.default.gray("Not found")}`),console.log(` Config flag : ${O.default.gray(M("localMode")?"true":"false")}`),console.log(` Env var : ${O.default.gray(process.env.XTRA_LOCAL_MODE||"(not set)")}`),console.log(),e?(console.log(O.default.gray(" Run 'xtra local off' to switch back to cloud mode.")),r||console.log(O.default.yellow(" \u26A0 No .env.local file found. Run 'xtra local sync' to pull secrets."))):console.log(O.default.gray(" Run 'xtra local on' to switch to offline mode.")),console.log()});Je.command("on").description("Enable local mode \xE2\u20AC\u201D secrets read from .env.local").action(()=>{U("localMode",!0),console.log(O.default.yellow("\u{1F50C} Local mode ENABLED.")),console.log(O.default.gray(" 'xtra run' will now read from .env.local instead of the cloud."));let e=Ke.join(process.cwd(),xo);Te.existsSync(e)||console.log(O.default.yellow(" \u26A0 No .env.local file found. Run 'xtra local sync' to populate it."))});Je.command("off").description("Disable local mode \xE2\u20AC\u201D secrets fetched from cloud again").action(()=>{U("localMode",!1),console.log(O.default.green("\xE2\u02DC\x81 Cloud mode ENABLED.")),console.log(O.default.gray(" 'xtra run' will now fetch secrets from XtraSecurity Cloud."))});Je.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",xo).option("--overwrite","Overwrite existing file without prompt",!1).action(async e=>{let{project:t,env:r,branch:o,output:n,overwrite:s}=e;r={dev:"development",stg:"staging",prod:"production"}[r]||r,t||(t=y().project),t||(console.error(O.default.red("Error: Project ID required. Use -p <id>.")),process.exit(1));let i=Ke.resolve(process.cwd(),n);if(r==="production"&&!s){console.log(O.default.red(`\u26A0 You are syncing PRODUCTION secrets to ${n}.`));let c=Mn.createInterface({input:process.stdin,output:process.stdout});if(!await new Promise(f=>{c.question(O.default.yellow("Type 'yes' to confirm: "),d=>{c.close(),f(d.trim().toLowerCase()==="yes")})})){console.log(O.default.gray("Sync cancelled."));return}}let p=(0,_n.default)(`Fetching ${r} secrets from cloud...`).start();try{let c=await g.getSecrets(t,r,o);p.stop();let l=Object.keys(c).length;if(l===0){console.log(O.default.yellow("No secrets found."));return}let f=[`# xtra local sync \xE2\u20AC\u201D ${r}/${o}`,`# Generated: ${new Date().toISOString()}`,"# DO NOT COMMIT THIS FILE","",...Object.entries(c).map(([h,w])=>`${h}=${w}`)];Te.writeFileSync(i,f.join(`
|
|
288
|
-
`)+`
|
|
289
|
-
`,"utf8"),console.log(O.default.green(`\u2705 Synced ${l} secrets to ${n}`)),console.log(O.default.gray(" Run 'xtra local on' to switch to local mode."));let d=Ke.join(process.cwd(),".gitignore"),u="";try{u=Te.readFileSync(d,"utf8")}catch{}u.includes(n)||console.log(O.default.yellow(`
|
|
290
|
-
\u26A0 Remember to add '${n}' to your .gitignore!`))}catch(c){p.fail("Sync failed."),console.error(O.default.red(c?.response?.data?.error||c.message)),process.exit(1)}});var Fn=require("commander");I();var qn=require("child_process"),me=m(require("chalk")),Vn=m(require("ora"));C();var Bn=new Fn.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,t,r)=>{let{token:o,env:n}=r,s=y(),a=(0,Vn.default)("Claiming JIT link...").start();try{let i=await g.claimJitLink(o),p=i.requestId;a.text=`Access request submitted (ID: ${p}). Waiting for admin approval...`;let c=i.status||"pending",l="";for(;c==="pending";){await new Promise(w=>setTimeout(w,5e3));let h=await g.getAccessRequestStatus(p);c=h.status,l=h.projectId,c==="rejected"&&(a.fail(me.default.red("Access request rejected by admin.")),process.exit(1)),(c==="revoked"||c==="expired")&&(a.fail(me.default.red(`Access request ${c}.`)),process.exit(1))}a.succeed(me.default.green("Access approved! Fetching secrets...")),l||(l=s.project),l||(a.fail("Error: Could not determine project ID for JIT session."),process.exit(1));let f=await g.getSecrets(l,n);!f||Object.keys(f).length===0?console.warn(me.default.yellow(`
|
|
291
|
-
Warning: No secrets returned. Ensure JIT link allowed specific secrets.`)):console.log(me.default.cyan(`
|
|
292
|
-
\u221A Loaded ${Object.keys(f).length} JIT-authorized secrets.`));let d={...process.env,...f};console.log(me.default.gray(`> ${e} ${t.join(" ")}`)),(0,qn.spawn)(e,t,{env:d,stdio:"inherit",shell:process.platform==="win32"}).on("exit",h=>{process.exit(h??0)})}catch(i){a.fail("JIT Run failed."),i.response?console.error(me.default.red(`
|
|
293
|
-
Error: ${i.response.data.error||i.message}`)):console.error(me.default.red(`
|
|
294
|
-
Error: ${i.message}`)),process.exit(1)}});var $=new Kn.Command,Xn=JSON.parse(Jn.readFileSync(Hn.join(__dirname,"../../package.json"),"utf-8"));(0,Wn.default)({pkg:Xn}).notify();$.name("xtra").description("XtraSync CLI - Secure Runtime Secret Injection").version(Xn.version).option("--profile <name>","Use a specific named profile for this command");var ks=new Set(["login","logout","completion","doctor","help","jit-run"]);$.hook("preSubcommand",(e,t)=>{let r=e.opts().profile;if(r)try{un(r)}catch(a){console.error(`\x1B[31mError: ${a.message}\x1B[0m`),process.exit(1)}let o=t.name();if(ks.has(o))return;let{getAuthToken:n}=(C(),L(Ye));n()||(console.error(`\x1B[31m\u2716 You are not logged in.\x1B[0m
|
|
2
|
+
var ge=Object.create;var S=Object.defineProperty;var ue=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var fe=Object.getPrototypeOf,ye=Object.prototype.hasOwnProperty;var x=(e,t)=>()=>(e&&(t=e(e=0)),t);var b=(e,t)=>{for(var s in t)S(e,s,{get:t[s],enumerable:!0})},N=(e,t,s,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of le(t))!ye.call(e,n)&&n!==s&&S(e,n,{get:()=>t[n],enumerable:!(r=ue(t,n))||r.enumerable});return e};var c=(e,t,s)=>(s=e!=null?ge(fe(e)):{},N(t||!e||!e.__esModule?S(s,"default",{value:e,enumerable:!0}):s,e)),C=e=>N(S({},"__esModule",{value:!0}),e);var M={};b(M,{createProfile:()=>xe,deleteProfile:()=>ve,getActiveProfile:()=>B,getActiveProfileName:()=>$,getProfileValue:()=>Se,listProfiles:()=>m,setActiveProfile:()=>he,setProfileValue:()=>we,useProfile:()=>Ce});function m(){return d.get("profiles")||{}}function $(){return process.env.XTRA_PROFILE||d.get("activeProfile")||"default"}function B(){let e=$();return m()[e]||{}}function he(e){if(!m()[e])throw new Error(`Profile '${e}' does not exist. Create it first with 'xtra profile create ${e}'.`);d.set("activeProfile",e)}function xe(e,t={}){let s=m();if(s[e])throw new Error(`Profile '${e}' already exists. Use 'xtra profile set' to edit it.`);s[e]={apiUrl:process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api",...t},d.set("profiles",s)}function ve(e){if(e==="default")throw new Error("Cannot delete the 'default' profile.");let t=m();if(!t[e])throw new Error(`Profile '${e}' not found.`);delete t[e],d.set("profiles",t),$()===e&&d.set("activeProfile","default")}function we(e,t,s){let r=m();if(!r[e])throw new Error(`Profile '${e}' not found.`);r[e][t]=s,d.set("profiles",r)}function Se(e){return B()[e]}function Ce(e){if(!m()[e])throw new Error(`Profile '${e}' does not exist. Run 'xtra profile create ${e}' first.`);process.env.XTRA_PROFILE=e}var O,d,J=x(()=>{O=c(require("conf")),d=new O.default({projectName:"xtra-cli",defaults:{activeProfile:"default",profiles:{default:{apiUrl:process.env.XTRA_API_URL||"https://www.xtrasecurity.in/api"}}}})});var L={};b(L,{clearConfig:()=>be,deleteConfig:()=>$e,getAuthToken:()=>P,getBreakGlassSession:()=>Ae,getConfig:()=>u,getConfigValue:()=>je,getProjectConfig:()=>Pe,getRcConfig:()=>Re,setBreakGlassSession:()=>Ie,setConfig:()=>ke});var q,g,R,I,i,A,u,je,ke,be,$e,P,Ae,Ie,Pe,Re,v=x(()=>{q=c(require("conf")),g=c(require("fs")),R=c(require("path")),I="https://www.xtrasecurity.in/api",i=new q.default({projectName:"xtra-cli",defaults:{apiUrl:I}}),A=i.get("apiUrl");A&&!process.env.XTRA_API_URL&&(A.includes("localhost")||A.includes("xtra-security.vercel.app"))&&i.set("apiUrl",I);u=()=>({...i.store,apiUrl:process.env.XTRA_API_URL||i.get("apiUrl")||I}),je=e=>i.get(e),ke=(e,t)=>{if(i.set(e,t),e==="token"&&process.platform!=="win32"&&g.default.existsSync(i.path))try{g.default.chmodSync(i.path,384)}catch{}},be=()=>i.clear(),$e=e=>i.delete(e),P=()=>i.get("token"),Ae=()=>i.get("breakGlassSessionId"),Ie=e=>{e?i.set("breakGlassSessionId",e):i.delete("breakGlassSessionId")},Pe=async()=>{try{let e=R.default.join(process.cwd(),"xtra.json");if(g.default.existsSync(e)){let t=g.default.readFileSync(e,"utf-8");return JSON.parse(t)}return null}catch{return null}},Re=()=>{let e={};try{let t=R.default.join(process.cwd(),".xtrarc");g.default.existsSync(t)&&(e=JSON.parse(g.default.readFileSync(t,"utf-8")))}catch{}return{project:e.project||i.get("project")||"",env:e.env||i.get("env")||"development",branch:e.branch||i.get("branch")||"main"}}});function X(){let e=G.get("masterKey");e||(e=l.default.randomBytes(32).toString("hex"),G.set("masterKey",e));let t=(0,K.machineIdSync)();return l.default.createHash("sha256").update(e+t).digest()}var l,F,K,V,G,z,H,W=x(()=>{l=c(require("crypto")),F=c(require("conf")),K=require("node-machine-id"),V="aes-256-gcm",G=new F.default({projectName:"xtra-cli-crypto"});z=e=>{let t=l.default.randomBytes(16),s=X(),r=l.default.createCipheriv(V,s,t),n=r.update(e,"utf8","hex");n+=r.final("hex");let a=r.getAuthTag();return{iv:t.toString("hex"),content:n,tag:a.toString("hex")}},H=e=>{let t=X(),s=Buffer.from(e.iv,"hex"),r=Buffer.from(e.tag,"hex"),n=l.default.createDecipheriv(V,t,s);n.setAuthTag(r);let a=n.update(e.content,"hex","utf8");return a+=n.final("utf8"),a}});var Y,Q,Z,Le,o,ee,te=x(()=>{Y=c(require("axios"));v();Q=c(require("fs")),Z=c(require("path")),Le=JSON.parse(Q.readFileSync(Z.join(__dirname,"../../package.json"),"utf-8")),o=()=>{let{apiUrl:e}=u(),t=P();return Y.default.create({baseURL:e,headers:{"Content-Type":"application/json","X-CLI-Version":Le.version,...t?{Authorization:`Bearer ${t}`}:{}}})},ee={login:async(e,t,s)=>(await o().post("/auth/cli-login",{email:e,password:t,apiKey:s})).data,getSecrets:async(e,t,s="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?branch=${s}`)).data,getSecretVersions:async(e,t,s="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets?includeVersions=true&branch=${s}`)).data,setSecrets:async(e,t,s,r,n="main")=>(await o().post(`/projects/${e}/envs/${t}/secrets`,{secrets:s,expectedVersions:r,branch:n})).data,getSecretDetails:async(e,t,s,r="main")=>(await o().get(`/projects/${e}/envs/${t}/secrets/${s}?branch=${r}`)).data,syncLogs:async e=>(await o().post("/audit/cli-logs",{logs:e})).data,linkSecret:async(e,t,s,r,n,a)=>(await o().post(`/projects/${e}/envs/${t}/secrets/link`,{key:s,sourceProjectId:r,sourceEnv:n,sourceKey:a})).data,rotateSecret:async(e,t,s,r,n)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/rotate`,{strategy:r,parsedNewValue:n})).data,promoteSecret:async(e,t,s)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/promote`,{})).data,verifyAuditLogs:async()=>(await o().get("/audit/verify")).data,exportAuditLogs:async(e,t,s,r)=>{let n={format:e};return t&&(n.startDate=t),s&&(n.endDate=s),r&&(n.projectId=r),(await o().post("/audit/export",n,{responseType:e==="csv"?"text":"json"})).data},requestAccess:async(e,t,s,r)=>(await o().post("/access/request",{projectId:e,secretId:r,reason:t,duration:s})).data,approveAccess:async(e,t)=>(await o().post("/access/approve",{requestId:e,decision:t})).data,listAccessRequests:async e=>{let{workspace:t}=u(),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}=u(),t=e?`/project?workspaceId=${e}`:"/project";return(await o().get(t)).data},getBranches:async e=>(await o().get(`/branch?projectId=${e}`)).data,createBranch:async(e,t,s)=>(await o().post("/branch",{projectId:e,name:t,description:s})).data,deleteBranch:async e=>(await o().delete(`/branch/${e}`)).data,updateBranch:async(e,t)=>(await o().put("/branch",{id:e,...t})).data,getRoles:async()=>(await o().get("/admin/roles")).data,getUsers:async e=>{let t=e?`?teamId=${e}`:"";return(await o().get(`/admin/users${t}`)).data},setUserRole:async(e,t,s)=>(await o().put("/admin/users/role",{email:e,role:t,teamId:s})).data,getIntegrationStatus:async e=>(await o().get(`/integrations/${e}`)).data,getIntegrationRepos:async e=>(await o().get(`/integrations/${e}/sync`)).data.repos,syncSecretsToGithub:async e=>(await o().post("/integrations/github/sync",e)).data,exportKubernetesSecret:async(e,t)=>{let s=new URLSearchParams(t).toString();return(await o().get(`/projects/${e}/kubernetes?${s}`)).data},getSecretHistory:async(e,t,s)=>(await o().get(`/projects/${e}/envs/${t}/secrets/${s}/history`)).data,rollbackSecret:async(e,t,s,r)=>(await o().post(`/projects/${e}/envs/${t}/secrets/${s}/history`,{version:r})).data,cloneEnvironment:async(e,t,s,r,n)=>(await o().post(`/projects/${e}/envs/clone`,{fromEnv:t,toEnv:s,overwrite:r,branch:n})).data,generateJitLink:async e=>(await o().post("/jit/generate",e)).data,claimJitLink:async e=>(await o().post("/jit/claim",{token:e})).data,getJitInfo:async e=>(await o().get(`/jit/${e}`)).data,getServiceAccounts:async e=>(await o().get(`/projects/${e}/service-accounts`)).data,createServiceAccount:async(e,t)=>(await o().post(`/projects/${e}/service-accounts`,t)).data,deleteServiceAccount:async(e,t)=>(await o().delete(`/projects/${e}/service-accounts/${t}`)).data,getServiceAccountKeys:async(e,t)=>(await o().get(`/projects/${e}/service-accounts/${t}/keys`)).data,createServiceAccountKey:async(e,t,s,r)=>(await o().post(`/projects/${e}/service-accounts/${t}/keys`,{label:s,expiresAt:r})).data,deleteServiceAccountKey:async(e,t,s)=>(await o().delete(`/projects/${e}/service-accounts/${t}/keys/${s}`)).data,post:async(e,t)=>(await o().post(e,t)).data}});var ne={};b(ne,{loadAuditLogs:()=>j,logAudit:()=>Te,saveAuditLogs:()=>k,syncAuditLogs:()=>_e});function Ue(e){if(!e||typeof e!="object")return e;let t={...e},s=["password","token","secret","value","apiKey","secretValue"];for(let r of Object.keys(t))s.some(n=>r.toLowerCase().includes(n))&&(t[r]="[REDACTED]");return t}async function re(e){try{if(!u().token)return!1;let s={action:e.action,entity:"cli-project",entityId:e.projectId||"global",details:Ue(e.details),timestamp:e.timestamp,projectId:e.projectId};return await ee.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 Te(e,t,s,r){let n={id:(0,se.v4)(),timestamp:new Date().toISOString(),action:e,projectId:t,environment:s,details:r,synced:!1},a=j();a.push(n),k(a),re(n).then(w=>{if(w){n.synced=!0;let h=j(),E=h.findIndex(me=>me.id===n.id);E!==-1&&(h[E].synced=!0,k(h))}}).catch(()=>{})}function j(){if(!f.default.existsSync(T))return[];try{let e=f.default.readFileSync(T,"utf-8"),t=JSON.parse(e),s=H(t);return JSON.parse(s)}catch{return[]}}function k(e){f.default.existsSync(U)||f.default.mkdirSync(U,{recursive:!0});let t=JSON.stringify(e),s=z(t);f.default.writeFileSync(T,JSON.stringify(s),"utf-8")}async function _e(){let e=j(),t=e.filter(r=>!r.synced);if(t.length===0)return 0;let s=0;for(let r of t)await re(r)&&(r.synced=!0,s++);return s>0&&k(e),s}var f,_,se,U,T,oe=x(()=>{f=c(require("fs")),_=c(require("path")),se=require("uuid");W();v();te();U=_.default.join(process.cwd(),".xtra"),T=_.default.join(U,"audit.enc")});var ie=require("commander"),ae=c(require("fs")),ce=c(require("path")),pe=c(require("update-notifier")),p=new ie.Command,de=JSON.parse(ae.readFileSync(ce.join(__dirname,"../../package.json"),"utf-8"));(0,pe.default)({pkg:de}).notify();p.name("xtra").description("XtraSync CLI - Secure Runtime Secret Injection").version(de.version).option("--profile <name>","Use a specific named profile for this command");var D={login:{module:"../commands/login",export:"loginCommand",desc:"Authenticate with XtraSecurity"},logout:{module:"../commands/logout",export:"logoutCommand",desc:"Logout and clear local token"},run:{module:"../commands/run",export:"runCommand",desc:"Run a command with injected secrets"},"jit-run":{module:"../commands/jit-run",export:"jitRunCommand",desc:"Run with temporary JIT access"},secrets:{module:"../commands/secrets",export:"secretsCommand",desc:"Manage secrets (List, Set, Delete)"},generate:{module:"../commands/generate",export:"generateCommand",desc:"Generate secret values"},status:{module:"../commands/status",export:"statusCommand",desc:"Check synchronization status"},diff:{module:"../commands/diff",export:"diffCommand",desc:"Show differences between environments"},rollback:{module:"../commands/rollback",export:"rollbackCommand",desc:"Rollback secrets to a previous version"},scan:{module:"../commands/scan",export:"scanCommand",desc:"Scan codebase for hardcoded secrets"},logs:{module:"../commands/logs",export:"logsCommand",desc:"View activity and audit logs"},export:{module:"../commands/export",export:"exportCommand",desc:"Export secrets to various formats"},import:{module:"../commands/import",export:"importCommand",desc:"Import secrets from files"},rotate:{module:"../commands/rotate",export:"rotateCommand",desc:"Trigger secret rotation"},audit:{module:"../commands/audit",export:"auditCommand",desc:"Manage audit configurations"},access:{module:"../commands/access",export:"accessCommand",desc:"Manage Just-in-Time access requests"},branch:{module:"../commands/branch",export:"branchCommand",desc:"Manage secret branches"},checkout:{module:"../commands/checkout",export:"checkoutCommand",desc:"Switch between branches"},project:{module:"../commands/project",export:"projectCommand",desc:"Manage project configuration"},admin:{module:"../commands/admin",export:"adminCommand",desc:"Administrative tasks (Roles, Teams)"},integration:{module:"../commands/integration",export:"integrationCommand",desc:"Sync secrets to external platforms"},kubernetes:{module:"../commands/integration",export:"kubernetesCommand",desc:"Kubernetes specific integrations"},history:{module:"../commands/history",export:"historyCommand",desc:"View secret version history"},env:{module:"../commands/env",export:"envCommand",desc:"Manage environments"},ui:{module:"../commands/ui",export:"uiCommand",desc:"Launch interactive TUI dashboard"},completion:{module:"../commands/completion",export:"completionCommand",desc:"Generate shell completion"},profile:{module:"../commands/profile",export:"profileCommand",desc:"Manage CLI profiles"},ci:{module:"../commands/ci",export:"ciCommand",desc:"CI/CD integration helpers"},"service-account":{module:"../commands/service-account",export:"serviceAccountCommand",desc:"Manage service accounts and machine tokens"},sa:{module:"../commands/service-account",export:"serviceAccountCommand",desc:"Manage service accounts and machine tokens"},template:{module:"../commands/template",export:"templateCommand",desc:"Manage secret templates"},doctor:{module:"../commands/doctor",export:"doctorCommand",desc:"Diagnose CLI configuration issues"},init:{module:"../commands/init",export:"initCommand",desc:"Initialize XtraSecurity in current directory"},watch:{module:"../commands/watch",export:"watchCommand",desc:"Watch for secret changes in real-time"},simulate:{module:"../commands/simulate",export:"simulateCommand",desc:"Simulate secret access behavior"},local:{module:"../commands/local",export:"localCommand",desc:"Manage local secret overrides"}},De=new Set(["login","logout","completion","doctor","help","jit-run"]);p.hook("preSubcommand",(e,t)=>{let s=e.opts().profile;if(s){let{useProfile:w}=(J(),C(M));try{w(s)}catch(h){console.error(`\x1B[31mError: ${h?.response?.data?.error||"An unexpected error occurred"}\x1B[0m`),process.exit(1)}}let r=t.name();if(De.has(r))return;let{getAuthToken:n}=(v(),C(L));n()||(console.error(`\x1B[31m\u2716 You are not logged in.\x1B[0m
|
|
295
3
|
Run \x1B[36mxtra login\x1B[0m to authenticate before using this command.
|
|
296
|
-
`),process.exit(1))})
|
|
4
|
+
`),process.exit(1))});p.hook("postAction",(e,t)=>{let{getBreakGlassSession:s}=(v(),C(L)),r=s();if(r){let{logAudit:n}=(oe(),C(ne));n("BREAK_GLASS_CLI_ACTIVITY",null,null,{command:t.name(),args:t.args,options:t.opts(),sessionId:r})}});var Ee=process.argv.slice(2),y=Ee.find(e=>!e.startsWith("-"));if(y&&D[y]){let e=D[y];try{let s=require(e.module)[e.export||`${y}Command`];s?p.addCommand(s):p.command(y).description(e.desc||"")}catch{p.command(y).description(e.desc||"")}}else Object.entries(D).forEach(([e,t])=>{p.command(e).description(t.desc||"")});p.parse(process.argv);
|