@dashnex/cli 0.5.32 → 0.5.33

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.
@@ -1 +1 @@
1
- import e from"inquirer";import s from"chalk";import r from"fs-extra";import o from"path";import{debug as t,debugJson as n,debugError as i}from"../lib/debug.js";import{getApiBase as a,apiFetch as d}from"../lib/api.js";import{ensureLoggedIn as l}from"../services/auth.js";import{createSpinner as c}from"../lib/spinner.js";import{isUserInterrupt as u,INTERRUPTED_MESSAGE as p}from"../lib/errors.js";const m=(e,r,o)=>{const t=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(r);let n=o;429===e.status?n="Too many requests. Please wait a moment and try again.":e.status>=500?n="DashNex API is temporarily unavailable. Please try again later.":t&&(n=t),console.error(s.red(n)),process.exit(1)};class h{async execute(r={}){t("Login flow started");const h=process.cwd(),f=o.join(h,".dashnex"),g=await l({exitOnFailure:!1,dashnexPath:f});if(g)console.log(s.green(`Already logged in as ${g.userName??"user"}`));else try{let o,l;if(r.email&&r.password)o=r.email.trim(),l=r.password,t("Email and password provided via flags");else{const{username:s,password:r}=await e.prompt([{type:"input",name:"username",message:"Email:",validate:e=>!!e.trim()||"Email is required"},{type:"password",name:"password",message:"Password:",mask:"*",validate:e=>!!e||"Password is required"}]);o=s.trim(),l=r}t("Email provided");const u=c();u.start("Logging in..."),t(`POST ${a()}/auth/v1/login`);const{response:p,body:h}=await d(`${a()}/auth/v1/login`,{method:"POST",body:JSON.stringify({username:o,password:l})});u.stop(),t(`Response: ${p.status}`),n("Login response",h),p.ok||(i(new Error(`Login failed: ${p.status} ${JSON.stringify(h)}`)),401===p.status&&(console.error(s.red("Invalid username or password.")),process.exit(1)),m(p,h,"Login failed. Please try again."));const g=h;if(g.two_fa_required&&g.token){t(`2FA required, type: ${g.type||"unknown"}`);const e=await this.handle2FA(g.token,g.type,r.code);e||process.exit(1),await this.completeLogin(e,f,r.business)}else g.token&&g.refreshToken?await this.completeLogin({token:g.token,refreshToken:g.refreshToken},f,r.business):(console.error(s.red("Invalid response from server. Please try again.")),process.exit(1))}catch(y){if(i(y),u(y))return void console.log(s.yellow(p));console.error(s.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}async handle2FA(r,o,l){if("sms"===o){t(`POST ${a()}/auth/v1/two-fa/sms`);try{const{response:e}=await d(`${a()}/auth/v1/two-fa/sms`,{method:"POST",headers:{Authorization:`Bearer ${r}`},body:JSON.stringify({})});t(`Response: ${e.status}`)}catch(f){return i(f),console.error(s.red("Could not send SMS code. Check your connection and try again.")),null}}const c="sms"===o?"Check your phone for the code":"Check your authenticator app for the code",h=l?1:3;for(let g=1;g<=h;g++){let o;if(l&&1===g)o=l,t("2FA code provided via flag");else{o=(await e.prompt([{type:"input",name:"auth_code",message:`Enter code (${c}):`,validate:e=>!!e.trim()||"Code is required"}])).auth_code}t(`POST ${a()}/auth/v1/2fa-check`);try{const{response:i,body:l}=await d(`${a()}/auth/v1/2fa-check`,{method:"POST",headers:{Authorization:`Bearer ${r}`},body:JSON.stringify({auth_code:o.trim()})});if(t(`Response: ${i.status}`),n("2FA check response",l),i.ok){const e=l;if(e.token&&e.refreshToken)return{token:e.token,refreshToken:e.refreshToken}}if(i.status>=400&&i.status<500){if(console.error(s.red("Invalid code. Please try again.")),g<h){const{retry:s}=await e.prompt([{type:"confirm",name:"retry",message:"Try again?",default:!0}]);if(!s)return null}continue}m(i,l,"2FA verification failed. Please try again.")}catch(f){return i(f),u(f)?(console.log(s.yellow(p)),null):(console.error(s.red("Could not reach DashNex API. Check your connection and try again.")),null)}}return null}async completeLogin(o,l,u){const p=c();p.start("Fetching businesses..."),t(`GET ${a()}/users/v1/business/my`);const{response:h,body:f}=await d(`${a()}/users/v1/business/my`,{headers:{Authorization:`Bearer ${o.token}`}});p.stop(),t(`Response: ${h.status}`),n("Businesses response",f),h.ok||(i(new Error(`Business fetch failed: ${h.status}`)),m(h,f,"Failed to fetch businesses. Please try again."));const g=this.parseBusinesses(f);let y;if(t(`Businesses: ${g.length}`),0===g.length&&(console.error(s.red("No businesses found for your account.")),process.exit(1)),u){const e=g.find(e=>e.id===u);e||(console.error(s.red(`Business "${u}" not found.`)),process.exit(1)),y=e,t(`Selected business via flag: ${y.id} ${y.name}`)}else if(1===g.length)y=g[0],t(`Selected business: ${y.id} ${y.name}`);else{const{businessId:s}=await e.prompt([{type:"select",name:"businessId",message:"Select business:",choices:g.map(e=>({name:e.name,value:e.id}))}]);y=g.find(e=>e.id===s)||g[0],t(`Selected business: ${y.id} ${y.name}`)}p.start("Switching business..."),t(`GET ${a()}/business/v1/login?_switch_business=${y.id}`);const{response:w,body:k}=await d(`${a()}/business/v1/login?_switch_business=${y.id}`,{headers:{Authorization:`Bearer ${o.token}`}});p.stop(),t(`Response: ${w.status}`),n("Business login response",k),w.ok||(i(new Error(`Business login failed: ${w.status}`)),m(w,k,"Failed to switch to business. Please try again."));const b=k;b.token&&b.refreshToken||(console.error(s.red("Invalid response from server. Please try again.")),process.exit(1));const $={token:b.token,refreshToken:b.refreshToken,businessId:y.id};await r.writeJson(l,$,{spaces:2}),t("Saved credentials to .dashnex"),this.ensureGitignore(),console.log(s.green(`Logged in as ${y.name}`))}parseBusinesses(e){const s=Array.isArray(e)?e:e.items??e.data??e.businesses??[];return Array.isArray(s)?s.map(e=>{if(e&&"object"==typeof e){const s=e,r=String(s.id??s.business_id??"").trim(),o=String(s.name??s.companyName??s.business_name??"Unknown").trim();if(r)return{id:r,name:o}}return null}).filter(e=>null!==e):[]}async ensureGitignore(){const e=o.join(process.cwd(),".gitignore");if(!(await r.pathExists(e)))return;let s=await r.readFile(e,"utf8");s.includes(".dashnex")||(s=s.trimEnd(),s.endsWith("\n")||(s+="\n"),s+="\n.dashnex\n",await r.writeFile(e,s))}}export{h as LoginCommand};
1
+ import e from"inquirer";import s from"chalk";import r from"fs-extra";import o from"path";import{debug as t,debugJson as n,debugError as i}from"../lib/debug.js";import{getApiBase as a,apiFetch as d}from"../lib/api.js";import{ensureLoggedIn as l}from"../services/auth.js";import{createSpinner as c}from"../lib/spinner.js";import{isUserInterrupt as u,INTERRUPTED_MESSAGE as p}from"../lib/errors.js";const m=(e,r,o)=>{const t=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(r);let n=o;429===e.status?n="Too many requests. Please wait a moment and try again.":e.status>=500?n="DashNex API is temporarily unavailable. Please try again later.":t&&(n=t),console.error(s.red(n)),process.exit(1)};class h{async execute(r={}){t("Login flow started");const h=process.cwd(),f=o.join(h,".dashnex"),g=await l({exitOnFailure:!1,dashnexPath:f});if(g)console.log(s.green(`Already logged in as ${g.userName??"user"}`));else try{let o,l;if(r.email)o=r.email.trim(),t("Email provided via flag");else{const{username:s}=await e.prompt([{type:"input",name:"username",message:"Email:",validate:e=>!!e.trim()||"Email is required"}]);o=s.trim()}if(r.password)l=r.password,t("Password provided via flag");else{const{password:s}=await e.prompt([{type:"password",name:"password",message:"Password:",mask:"*",validate:e=>!!e||"Password is required"}]);l=s}t("Email provided");const u=c();u.start("Logging in..."),t(`POST ${a()}/auth/v1/login`);const{response:p,body:h}=await d(`${a()}/auth/v1/login`,{method:"POST",body:JSON.stringify({username:o,password:l})});u.stop(),t(`Response: ${p.status}`),n("Login response",h),p.ok||(i(new Error(`Login failed: ${p.status} ${JSON.stringify(h)}`)),401===p.status&&(console.error(s.red("Invalid username or password.")),process.exit(1)),m(p,h,"Login failed. Please try again."));const g=h;if(g.two_fa_required&&g.token){t(`2FA required, type: ${g.type||"unknown"}`);const e=await this.handle2FA(g.token,g.type,r.code);e||process.exit(1),await this.completeLogin(e,f,r.business)}else g.token&&g.refreshToken?await this.completeLogin({token:g.token,refreshToken:g.refreshToken},f,r.business):(console.error(s.red("Invalid response from server. Please try again.")),process.exit(1))}catch(y){if(i(y),u(y))return void console.log(s.yellow(p));console.error(s.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}async handle2FA(r,o,l){if("sms"===o){t(`POST ${a()}/auth/v1/two-fa/sms`);try{const{response:e}=await d(`${a()}/auth/v1/two-fa/sms`,{method:"POST",headers:{Authorization:`Bearer ${r}`},body:JSON.stringify({})});t(`Response: ${e.status}`)}catch(f){return i(f),console.error(s.red("Could not send SMS code. Check your connection and try again.")),null}}const c="sms"===o?"Check your phone for the code":"Check your authenticator app for the code",h=l?1:3;for(let g=1;g<=h;g++){let o;if(l&&1===g)o=l,t("2FA code provided via flag");else{o=(await e.prompt([{type:"input",name:"auth_code",message:`Enter code (${c}):`,validate:e=>!!e.trim()||"Code is required"}])).auth_code}t(`POST ${a()}/auth/v1/2fa-check`);try{const{response:i,body:l}=await d(`${a()}/auth/v1/2fa-check`,{method:"POST",headers:{Authorization:`Bearer ${r}`},body:JSON.stringify({auth_code:o.trim()})});if(t(`Response: ${i.status}`),n("2FA check response",l),i.ok){const e=l;if(e.token&&e.refreshToken)return{token:e.token,refreshToken:e.refreshToken}}if(i.status>=400&&i.status<500){if(console.error(s.red("Invalid code. Please try again.")),g<h){const{retry:s}=await e.prompt([{type:"confirm",name:"retry",message:"Try again?",default:!0}]);if(!s)return null}continue}m(i,l,"2FA verification failed. Please try again.")}catch(f){return i(f),u(f)?(console.log(s.yellow(p)),null):(console.error(s.red("Could not reach DashNex API. Check your connection and try again.")),null)}}return null}async completeLogin(o,l,u){const p=c();p.start("Fetching businesses..."),t(`GET ${a()}/users/v1/business/my`);const{response:h,body:f}=await d(`${a()}/users/v1/business/my`,{headers:{Authorization:`Bearer ${o.token}`}});p.stop(),t(`Response: ${h.status}`),n("Businesses response",f),h.ok||(i(new Error(`Business fetch failed: ${h.status}`)),m(h,f,"Failed to fetch businesses. Please try again."));const g=this.parseBusinesses(f);let y;if(t(`Businesses: ${g.length}`),0===g.length&&(console.error(s.red("No businesses found for your account.")),process.exit(1)),u){const e=g.find(e=>e.id===u);e||(console.error(s.red(`Business "${u}" not found.`)),process.exit(1)),y=e,t(`Selected business via flag: ${y.id} ${y.name}`)}else if(1===g.length)y=g[0],t(`Selected business: ${y.id} ${y.name}`);else{const{businessId:s}=await e.prompt([{type:"select",name:"businessId",message:"Select business:",choices:g.map(e=>({name:e.name,value:e.id}))}]);y=g.find(e=>e.id===s)||g[0],t(`Selected business: ${y.id} ${y.name}`)}p.start("Switching business..."),t(`GET ${a()}/business/v1/login?_switch_business=${y.id}`);const{response:w,body:k}=await d(`${a()}/business/v1/login?_switch_business=${y.id}`,{headers:{Authorization:`Bearer ${o.token}`}});p.stop(),t(`Response: ${w.status}`),n("Business login response",k),w.ok||(i(new Error(`Business login failed: ${w.status}`)),m(w,k,"Failed to switch to business. Please try again."));const b=k;b.token&&b.refreshToken||(console.error(s.red("Invalid response from server. Please try again.")),process.exit(1));const $={token:b.token,refreshToken:b.refreshToken,businessId:y.id};await r.writeJson(l,$,{spaces:2}),t("Saved credentials to .dashnex"),this.ensureGitignore(),console.log(s.green(`Logged in as ${y.name}`))}parseBusinesses(e){const s=Array.isArray(e)?e:e.items??e.data??e.businesses??[];return Array.isArray(s)?s.map(e=>{if(e&&"object"==typeof e){const s=e,r=String(s.id??s.business_id??"").trim(),o=String(s.name??s.companyName??s.business_name??"Unknown").trim();if(r)return{id:r,name:o}}return null}).filter(e=>null!==e):[]}async ensureGitignore(){const e=o.join(process.cwd(),".gitignore");if(!(await r.pathExists(e)))return;let s=await r.readFile(e,"utf8");s.includes(".dashnex")||(s=s.trimEnd(),s.endsWith("\n")||(s+="\n"),s+="\n.dashnex\n",await r.writeFile(e,s))}}export{h as LoginCommand};
@@ -1 +1 @@
1
- const e="@dashnex/cli",a="0.5.32",n="Command-line interface for DashNex framework",o={name:e,version:a,description:n};export{o as default,n as description,e as name,a as version};
1
+ const e="@dashnex/cli",a="0.5.33",n="Command-line interface for DashNex framework",o={name:e,version:a,description:n};export{o as default,n as description,e as name,a as version};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@dashnex/cli",
4
- "version": "0.5.32",
4
+ "version": "0.5.33",
5
5
  "description": "Command-line interface for DashNex framework",
6
6
  "homepage": "https://dashnex.io",
7
7
  "type": "module",