@dashnex/cli 0.5.27 → 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.
package/README.md CHANGED
@@ -24,7 +24,6 @@ dashnex <command> [options]
24
24
  | `login` | Log in to your DashNex account |
25
25
  | `logout` | Log out and remove local credentials |
26
26
  | `whoami` | Show current logged-in business and user |
27
- | `select` | Switch to a different business |
28
27
  | `app create` | Create a new DashNex application |
29
28
  | `app pull` | Pull existing application to specified folder |
30
29
  | `app push` | Push local application to DashNex |
@@ -36,4 +35,21 @@ dashnex <command> [options]
36
35
 
37
36
  ### Options
38
37
 
39
- - **app create**: `-t, --template <template>` — Template (name@branch-or-tag, default: webapp-base@live)
38
+ **login**:
39
+ - `--email <email>` — Email address
40
+ - `--password <password>` — Password
41
+ - `--code <code>` — 2FA code
42
+ - `--business <id>` — Business ID to select
43
+
44
+ **app create**:
45
+ - `--subdomain <subdomain>` — Subdomain
46
+ - `-t, --template <template>` — Template, optional (name@branch-or-tag)
47
+
48
+ **app pull**:
49
+ - `-y, --yes` — Skip prompts and child commands
50
+
51
+ **app push**:
52
+ - `--deploy` / `--no-deploy` — Auto-deploy or skip deploy after push
53
+
54
+ **app delete**:
55
+ - `-y, --yes` — Skip confirmation prompt
@@ -1 +1 @@
1
- import e from"inquirer";import t from"chalk";import{debug as o,debugError as s}from"../../lib/debug.js";import{getBusinessApiBase as a}from"../../lib/api.js";import{ensureLoggedIn as i}from"../../services/auth.js";import{createSpinner as r}from"../../lib/spinner.js";import{isUserInterrupt as n,INTERRUPTED_MESSAGE as l}from"../../lib/errors.js";import{PullCommand as c}from"./pull.js";const p=e=>Array.isArray(e.message)&&e.message.length>0?e.message.join("\n"):"string"==typeof e.message?e.message:"string"==typeof e.error?e.error:void 0,u=e=>new Promise(t=>setTimeout(t,e));class d{async pollApplicationCreated(e,t,a=5e3,i=6e4){const r=Date.now();let n=0;for(;Date.now()-r<i;){n++,o(`Polling application status (attempt ${n})...`);try{const s=await fetch(e,{headers:{Authorization:`Bearer ${t}`}});if(o(`Poll response: ${s.status}`),200===s.status){const e=await s.json();if(o(`Poll response body status: ${e.status}`),"creating"!==e.status)return o(`Application status is "${e.status}", creation complete`),{ready:!0,status:e.status};o('Application status is still "creating", continuing to poll...')}}catch(l){s(l),o(`Poll attempt ${n} failed with network error`)}await u(a)}return{ready:!1}}async execute(u={}){o("Create flow started");const d=await i();if(!d)return;o("Auth valid, checking if business already has application");const h=r(),m=`${a()}/business/v1/applications`;o(`GET ${m}`),h.start("Checking application status...");try{const e=await fetch(m,{headers:{Authorization:`Bearer ${d.token}`}});h.stop(),o(`Applications response: ${e.status}`);const s=await e.text();o(`Applications response body: ${s}`),404!==e.status&&(console.error(t.red("Business already has an application. Run 'dashnex app pull' to pull the existing one.")),process.exit(1))}catch(x){if(h.stop(),s(x),x instanceof Error&&x.message.startsWith("EXIT:"))throw x;if(n(x))return void console.log(t.yellow(l));console.error(t.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}let f;o("No existing application, proceeding to subdomain prompt");const g=e=>`${a()}/business/v1/applications/check-subdomain?subdomain=${encodeURIComponent(e)}`;let y=u.subdomain?.trim();for(;;){if(y)f=y,y=void 0,o(`Subdomain provided via flag: ${f}`);else{const{sub:t}=await e.prompt([{type:"input",name:"sub",message:"Pick your subdomain:",validate:e=>!!e.trim()||"Subdomain is required"}]);f=t.trim()}o(`Checking subdomain: ${f}`);const a=g(f);o(`GET ${a}`),h.start("Checking subdomain...");try{const e=await fetch(a,{headers:{Authorization:`Bearer ${d.token}`}});h.stop(),o(`Check subdomain response: ${e.status}`);const s=await e.text();if(o(`Check subdomain response body: ${s}`),!e.ok){const a=e.headers.get("content-type")??"";let i="Failed to check subdomain availability.";if(401!==e.status&&403!==e.status||(i="Please run 'dashnex login' to authenticate.",console.error(t.red(i)),process.exit(1)),a.includes("application/json")){const e=JSON.parse(s);o(`Check subdomain error body: ${JSON.stringify(e)}`);const t=p(e);t&&(i=t)}console.error(t.red(i));continue}const i=JSON.parse(s);if(o(`Check subdomain parsed body: ${JSON.stringify(i)}`),i.available){o(`Subdomain ${f} is available`);break}console.error(t.red("Subdomain is not available. Please choose a different one."))}catch(x){if(h.stop(),s(x),x instanceof Error&&x.message.startsWith("EXIT:"))throw x;if(n(x))return void console.log(t.yellow(l));console.error(t.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}const b=`${a()}/business/v1/cli/create`;o(`POST ${b}`);const $={subdomain:f};u.template&&($.template=u.template,o(`Template: ${u.template}`));const w=JSON.stringify($);o(`Create request body: ${w}`),h.start("Creating application...");try{const i=await fetch(b,{method:"POST",headers:{Authorization:`Bearer ${d.token}`,"Content-Type":"application/json"},body:w});h.stop(),o(`Create response: ${i.status}`);const r=await i.text();if(o(`Create response body: ${r}`),503===i.status){o("Create returned 503, polling for application creation..."),console.log(t.yellow("Application is being created, please wait..."));const e=`${a()}/business/v1/applications`,s=await this.pollApplicationCreated(e,d.token);s.ready||(console.error(t.red("Application creation is taking longer than expected. Try again later or run 'dashnex app pull' to check.")),process.exit(1)),"failed"===s.status&&(console.error(t.red("Application creation failed. Please try again.")),process.exit(1)),o("Application found after polling")}else if(!i.ok){const e=i.headers.get("content-type")??"";let s="Failed to create application.";if(401===i.status||403===i.status)s="Please run 'dashnex login' to authenticate.";else if(e.includes("application/json")){const e=JSON.parse(r);o(`Create error body parsed: ${JSON.stringify(e)}`);const t=p(e);t&&(s=t)}console.error(t.red(s)),process.exit(1)}let m;if(o("Application created successfully"),void 0!==u.pull)m=u.pull,o(`Pull decided via flag: ${m}`);else{m=(await e.prompt([{type:"confirm",name:"pull",message:"Pull the application locally?",default:!0}])).pull,o(`User chose to pull: ${m}`)}if(m){o("Running pull flow");try{const e=new c;await e.execute()}catch(x){if(s(x),x instanceof Error&&x.message.startsWith("EXIT:"))throw x;if(n(x))return void console.log(t.yellow(l));console.error(t.red("Failed to pull application."))}}else console.log(t.green("Application is created successfully. Run 'dashnex app pull' to pull it to your computer or 'dashnex app deploy' to deploy it to the cloud."))}catch(x){if(h.stop(),s(x),x instanceof Error&&x.message.startsWith("EXIT:"))throw x;if(n(x))return void console.log(t.yellow(l));console.error(t.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}}export{d as CreateCommand};
1
+ import e from"inquirer";import t from"chalk";import{debug as o,debugError as s}from"../../lib/debug.js";import{getBusinessApiBase as a}from"../../lib/api.js";import{ensureLoggedIn as i}from"../../services/auth.js";import{createSpinner as r}from"../../lib/spinner.js";import{isUserInterrupt as n,INTERRUPTED_MESSAGE as l}from"../../lib/errors.js";import{PullCommand as c}from"./pull.js";const p=e=>Array.isArray(e.message)&&e.message.length>0?e.message.join("\n"):"string"==typeof e.message?e.message:"string"==typeof e.error?e.error:void 0,u=e=>new Promise(t=>setTimeout(t,e));class d{async pollApplicationCreated(e,t,a=5e3,i=6e4){const r=Date.now();let n=0;for(;Date.now()-r<i;){n++,o(`Polling application status (attempt ${n})...`);try{const s=await fetch(e,{headers:{Authorization:`Bearer ${t}`}});if(o(`Poll response: ${s.status}`),200===s.status){const e=await s.json();if(o(`Poll response body status: ${e.status}`),"creating"!==e.status)return o(`Application status is "${e.status}", creation complete`),{ready:!0,status:e.status};o('Application status is still "creating", continuing to poll...')}}catch(l){s(l),o(`Poll attempt ${n} failed with network error`)}await u(a)}return{ready:!1}}async execute(u={}){o("Create flow started");const d=await i();if(!d)return;o("Auth valid, checking if business already has application");const h=r(),m=`${a()}/business/v1/applications`;o(`GET ${m}`),h.start("Checking application status...");try{const e=await fetch(m,{headers:{Authorization:`Bearer ${d.token}`}});h.stop(),o(`Applications response: ${e.status}`);const s=await e.text();o(`Applications response body: ${s}`),404!==e.status&&(console.error(t.red("Business already has an application. Run 'dashnex app pull' to pull the existing one.")),process.exit(1))}catch(k){if(h.stop(),s(k),k instanceof Error&&k.message.startsWith("EXIT:"))throw k;if(n(k))return void console.log(t.yellow(l));console.error(t.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}let f;o("No existing application, proceeding to subdomain prompt");const g=e=>`${a()}/business/v1/applications/check-subdomain?subdomain=${encodeURIComponent(e)}`,y=void 0!==u.subdomain;let b=u.subdomain?.trim();for(;;){if(b)f=b,b=void 0,o(`Subdomain provided via flag: ${f}`);else{if(y)return console.error(t.red("Subdomain is not available.")),void process.exit(1);{const{sub:t}=await e.prompt([{type:"input",name:"sub",message:"Pick your subdomain:",validate:e=>!!e.trim()||"Subdomain is required"}]);f=t.trim()}}o(`Checking subdomain: ${f}`);const a=g(f);o(`GET ${a}`),h.start("Checking subdomain...");try{const e=await fetch(a,{headers:{Authorization:`Bearer ${d.token}`}});h.stop(),o(`Check subdomain response: ${e.status}`);const s=await e.text();if(o(`Check subdomain response body: ${s}`),!e.ok){const a=e.headers.get("content-type")??"";let i="Failed to check subdomain availability.";if(401!==e.status&&403!==e.status||(i="Please run 'dashnex login' to authenticate.",console.error(t.red(i)),process.exit(1)),a.includes("application/json")){const e=JSON.parse(s);o(`Check subdomain error body: ${JSON.stringify(e)}`);const t=p(e);t&&(i=t)}console.error(t.red(i));continue}const i=JSON.parse(s);if(o(`Check subdomain parsed body: ${JSON.stringify(i)}`),i.available){o(`Subdomain ${f} is available`);break}console.error(t.red("Subdomain is not available. Please choose a different one."))}catch(k){if(h.stop(),s(k),k instanceof Error&&k.message.startsWith("EXIT:"))throw k;if(n(k))return void console.log(t.yellow(l));console.error(t.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}const w=`${a()}/business/v1/cli/create`;o(`POST ${w}`);const $={subdomain:f};u.template&&($.template=u.template,o(`Template: ${u.template}`));const x=JSON.stringify($);o(`Create request body: ${x}`),h.start("Creating application...");try{const i=await fetch(w,{method:"POST",headers:{Authorization:`Bearer ${d.token}`,"Content-Type":"application/json"},body:x});h.stop(),o(`Create response: ${i.status}`);const r=await i.text();if(o(`Create response body: ${r}`),503===i.status){o("Create returned 503, polling for application creation..."),console.log(t.yellow("Application is being created, please wait..."));const e=`${a()}/business/v1/applications`,s=await this.pollApplicationCreated(e,d.token);s.ready||(console.error(t.red("Application creation is taking longer than expected. Try again later or run 'dashnex app pull' to check.")),process.exit(1)),"failed"===s.status&&(console.error(t.red("Application creation failed. Please try again.")),process.exit(1)),o("Application found after polling")}else if(!i.ok){const e=i.headers.get("content-type")??"";let s="Failed to create application.";if(401===i.status||403===i.status)s="Please run 'dashnex login' to authenticate.";else if(e.includes("application/json")){const e=JSON.parse(r);o(`Create error body parsed: ${JSON.stringify(e)}`);const t=p(e);t&&(s=t)}console.error(t.red(s)),process.exit(1)}let u;if(o("Application created successfully"),y)u=!1,o("CI mode (--subdomain), skipping pull");else{u=(await e.prompt([{type:"confirm",name:"pull",message:"Pull the application locally?",default:!0}])).pull,o(`User chose to pull: ${u}`)}if(u){o("Running pull flow");try{const e=new c;await e.execute()}catch(k){if(s(k),k instanceof Error&&k.message.startsWith("EXIT:"))throw k;if(n(k))return void console.log(t.yellow(l));console.error(t.red("Failed to pull application."))}}else console.log(t.green("Application is created successfully. Run 'dashnex app pull' to pull it to your computer or 'dashnex app deploy' to deploy it to the cloud."))}catch(k){if(h.stop(),s(k),k instanceof Error&&k.message.startsWith("EXIT:"))throw k;if(n(k))return void console.log(t.yellow(l));console.error(t.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}}}export{d as CreateCommand};
@@ -1 +1 @@
1
- import{CreateCommand as e}from"./create.js";import{PullCommand as p}from"./pull.js";import{PushCommand as o}from"./push.js";import{DeleteCommand as i}from"./delete.js";import{DeployCommand as t}from"./deploy.js";import{StatusCommand as a}from"./status.js";const n={name:"app",description:"Manage DashNex applications",handler:{execute:async()=>{}},options:[],subcommands:[{name:"create",description:"Create a new DashNex application",handler:new e,options:[{flags:"-t, --template <template>",description:"Template (name@branch-or-tag)",defaultValue:"webapp-base@live"},{flags:"--subdomain <subdomain>",description:"Subdomain (skip prompt)"},{flags:"--pull",description:"Auto-pull after creation (skip prompt)"},{flags:"--no-pull",description:"Skip pull after creation"}]},{name:"pull",description:"Pull existing application to specified folder",handler:new p,options:[{flags:"-y, --yes",description:"Skip overwrite confirmation"},{flags:"--no-install",description:"Skip install dependencies prompt"},{flags:"--no-migrate",description:"Skip database migration prompt"},{flags:"--no-dev",description:"Skip dev server prompt"}]},{name:"push",description:"Push local application to DashNex",handler:new o,options:[{flags:"--deploy",description:"Auto-deploy after push (skip prompt)"},{flags:"--no-deploy",description:"Skip deploy prompt"}]},{name:"delete",description:"Delete application from DashNex",handler:new i,options:[{flags:"-y, --yes",description:"Skip confirmation prompt"}]},{name:"deploy",description:"Deploy application to DashNex",handler:new t,options:[]},{name:"status",description:"Show current application status",handler:new a,options:[]}]};export{n as default};
1
+ import{CreateCommand as e}from"./create.js";import{PullCommand as o}from"./pull.js";import{PushCommand as p}from"./push.js";import{DeleteCommand as a}from"./delete.js";import{DeployCommand as t}from"./deploy.js";import{StatusCommand as i}from"./status.js";const s={name:"app",description:"Manage DashNex applications",handler:{execute:async()=>{}},options:[],subcommands:[{name:"create",description:"Create a new DashNex application",handler:new e,options:[{flags:"-t, --template <template>",description:"Template (name@branch-or-tag)",defaultValue:"webapp-base@live"},{flags:"--subdomain <subdomain>",description:"Subdomain (CI mode, skip prompts)"}]},{name:"pull",description:"Pull existing application to specified folder",handler:new o,options:[{flags:"-y, --yes",description:"Skip prompts and child commands (CI mode)"}]},{name:"push",description:"Push local application to DashNex",handler:new p,options:[{flags:"--deploy",description:"Auto-deploy after push (skip prompt)"},{flags:"--no-deploy",description:"Skip deploy prompt"}]},{name:"delete",description:"Delete application from DashNex",handler:new a,options:[{flags:"-y, --yes",description:"Skip confirmation prompt"}]},{name:"deploy",description:"Deploy application to DashNex",handler:new t,options:[]},{name:"status",description:"Show current application status",handler:new i,options:[]}]};export{s as default};
@@ -1 +1 @@
1
- import e from"chalk";import t from"fs-extra";import r from"path";import o from"os";import{spawn as i}from"child_process";import n from"inquirer";import a from"adm-zip";import{debug as s,debugError as l}from"../../lib/debug.js";import{getBusinessApiBase as c}from"../../lib/api.js";import{ensureLoggedIn as d}from"../../services/auth.js";import{createSpinner as p}from"../../lib/spinner.js";import{isUserInterrupt as m,INTERRUPTED_MESSAGE as f}from"../../lib/errors.js";const u=(e,t)=>new Promise((r,o)=>{const n=i(t,{cwd:e,shell:!0,stdio:"inherit"});n.on("error",e=>{l(e),o(e)}),n.on("close",e=>{r(null===e?0:e)})}),g=async(t,o,a={})=>{let c;if(void 0!==a.install)c=a.install;else{c=(await n.prompt([{type:"confirm",name:"install",message:"Install dependencies?",default:!0}])).install}if(!c){if(r.resolve(t)!==r.resolve(o)){const i=r.relative(o,t);console.log(e.cyan(`Run "cd ${i}" to change to the application folder.`))}return}const d=await(async()=>{const e=e=>new Promise(t=>{const r=i(e,["--version"],{stdio:"ignore",shell:!0});r.on("error",()=>t(!1)),r.on("close",e=>t(0===e))});return await e("pnpm")?"pnpm":(await e("npm"),"npm")})();s(`Running ${d} install in ${t}`);try{const e=await u(t,`${d} install`);if(0!==e)return void s(`${d} install exited with code ${e}`)}catch(f){return l(f),void console.error(e.red(`Failed to run ${d} install.`))}let p,m;if(void 0!==a.migrate)p=a.migrate;else{p=(await n.prompt([{type:"confirm",name:"migrate",message:"Create local database?",default:!0}])).migrate}if(p){s(`Running dashnex db migrate in ${t}`);try{const e=await u(t,"dashnex db migrate");if(0!==e)return void s(`dashnex db migrate exited with code ${e}`)}catch(f){return l(f),void console.error(e.red("Failed to run dashnex db migrate."))}}if(void 0!==a.dev)m=a.dev;else{m=(await n.prompt([{type:"confirm",name:"dev",message:"Run development server?",default:!0}])).dev}if(m){s(`Running npm run dev in ${t}`);try{await u(t,"npm run dev")}catch(f){l(f),console.error(e.red("Failed to run dev server."))}}},h='Application is successfully pulled. Run "npm install" or "pnpm install" to install dependencies and then "dashnex dev" to run it';class v{async execute(i={}){s("Pull flow started");const u=await d();if(!u)return;const v=process.cwd();let w;if(i.folder)w=i.folder.trim()||".",s(`Folder argument provided: ${w}`);else{s("No folder argument, prompting user");const{folder:e}=await n.prompt([{type:"input",name:"folder",message:"Which folder to pull the application to?",default:"."}]);w=e.trim()||"."}const y=r.resolve(v,w);s(`Target folder resolved: ${y}`);const x=await t.pathExists(y);let $=!1;if(x)try{$=(await t.readdir(y)).length>0,s(`Folder exists: ${x}, has files: ${$}`)}catch(R){s(`Error reading folder: ${R}`)}if($&&!i.yes){const{confirm:t}=await n.prompt([{type:"confirm",name:"confirm",message:"Existing files will be overwritten. Continue?",default:!0}]);if(!t)return s("User declined confirmation, exiting"),void console.log(e.yellow("Pull cancelled."));s("User confirmed overwrite")}await t.ensureDir(y),s(`Target folder ensured: ${y}`);const b=`${c()}/business/v1/cli/pull`;s(`GET ${b}`);const j=r.join(o.tmpdir(),`dashnex-pull-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`),E=p();E.start("Pulling application...");try{const o=await fetch(b,{headers:{Authorization:`Bearer ${u.token}`}});if(s(`Response: ${o.status}`),!o.ok){E.stop();const t=o.headers.get("content-type")??"";let r="Failed to pull application.";if(401===o.status||403===o.status)r="Please run 'dashnex login' to authenticate.";else if(404===o.status)r="Business has no application. Run 'dashnex app create' to create one.";else if(t.includes("application/json")){const e=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(await o.json().catch(()=>({})));e&&(r=e)}console.error(e.red(r)),process.exit(1)}E.text("Downloading...");const n=Buffer.from(await o.arrayBuffer());await t.writeFile(j,n),s("Zip written to temp file"),E.text("Extracting...");new a(j).extractAllTo(y,!0),E.stop(),s("Extracted to target folder"),r.resolve(y)!==r.resolve(v)?(console.log(e.green(((e,t)=>r.resolve(e)===r.resolve(t)?h:`Application is successfully pulled into ${r.relative(t,e)}.`)(y,v))),process.chdir(y),s(`Changed working directory to ${y}`)):console.log(e.green(h)),await g(y,v,{install:i.install,migrate:i.migrate,dev:i.dev})}catch(R){if(E.stop(),l(R),R instanceof Error&&R.message.startsWith("EXIT:"))throw R;if(m(R))return void console.log(e.yellow(f));console.error(e.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}finally{await t.remove(j).catch(()=>{}),s("Temp zip removed")}}}export{v as PullCommand};
1
+ import e from"chalk";import t from"fs-extra";import o from"path";import r from"os";import{spawn as i}from"child_process";import n from"inquirer";import s from"adm-zip";import{debug as a,debugError as l}from"../../lib/debug.js";import{getBusinessApiBase as c}from"../../lib/api.js";import{ensureLoggedIn as d}from"../../services/auth.js";import{createSpinner as p}from"../../lib/spinner.js";import{isUserInterrupt as m,INTERRUPTED_MESSAGE as f}from"../../lib/errors.js";const u=(e,t)=>new Promise((o,r)=>{const n=i(t,{cwd:e,shell:!0,stdio:"inherit"});n.on("error",e=>{l(e),r(e)}),n.on("close",e=>{o(null===e?0:e)})}),g=async(t,r)=>{const{install:s}=await n.prompt([{type:"confirm",name:"install",message:"Install dependencies?",default:!0}]);if(!s){if(o.resolve(t)!==o.resolve(r)){const i=o.relative(r,t);console.log(e.cyan(`Run "cd ${i}" to change to the application folder.`))}return}const c=await(async()=>{const e=e=>new Promise(t=>{const o=i(e,["--version"],{stdio:"ignore",shell:!0});o.on("error",()=>t(!1)),o.on("close",e=>t(0===e))});return await e("pnpm")?"pnpm":(await e("npm"),"npm")})();a(`Running ${c} install in ${t}`);try{const e=await u(t,`${c} install`);if(0!==e)return void a(`${c} install exited with code ${e}`)}catch(m){return l(m),void console.error(e.red(`Failed to run ${c} install.`))}const{migrate:d}=await n.prompt([{type:"confirm",name:"migrate",message:"Create local database?",default:!0}]);if(d){a(`Running dashnex db migrate in ${t}`);try{const e=await u(t,"dashnex db migrate");if(0!==e)return void a(`dashnex db migrate exited with code ${e}`)}catch(m){return l(m),void console.error(e.red("Failed to run dashnex db migrate."))}}const{dev:p}=await n.prompt([{type:"confirm",name:"dev",message:"Run development server?",default:!0}]);if(p){a(`Running npm run dev in ${t}`);try{await u(t,"npm run dev")}catch(m){l(m),console.error(e.red("Failed to run dev server."))}}},h='Application is successfully pulled. Run "npm install" or "pnpm install" to install dependencies and then "dashnex dev" to run it';class w{async execute(i={}){a("Pull flow started");const u=await d();if(!u)return;const w=process.cwd();let v;if(i.folder)v=i.folder.trim()||".",a(`Folder argument provided: ${v}`);else{a("No folder argument, prompting user");const{folder:e}=await n.prompt([{type:"input",name:"folder",message:"Which folder to pull the application to?",default:"."}]);v=e.trim()||"."}const y=o.resolve(w,v);a(`Target folder resolved: ${y}`);const x=await t.pathExists(y);let $=!1;if(x)try{$=(await t.readdir(y)).length>0,a(`Folder exists: ${x}, has files: ${$}`)}catch(R){a(`Error reading folder: ${R}`)}if($&&!i.yes){const{confirm:t}=await n.prompt([{type:"confirm",name:"confirm",message:"Existing files will be overwritten. Continue?",default:!0}]);if(!t)return a("User declined confirmation, exiting"),void console.log(e.yellow("Pull cancelled."));a("User confirmed overwrite")}await t.ensureDir(y),a(`Target folder ensured: ${y}`);const b=`${c()}/business/v1/cli/pull`;a(`GET ${b}`);const j=o.join(r.tmpdir(),`dashnex-pull-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`),E=p();E.start("Pulling application...");try{const r=await fetch(b,{headers:{Authorization:`Bearer ${u.token}`}});if(a(`Response: ${r.status}`),!r.ok){E.stop();const t=r.headers.get("content-type")??"";let o="Failed to pull application.";if(401===r.status||403===r.status)o="Please run 'dashnex login' to authenticate.";else if(404===r.status)o="Business has no application. Run 'dashnex app create' to create one.";else if(t.includes("application/json")){const e=(e=>"string"==typeof e.error?e.error:"string"==typeof e.message?e.message:void 0)(await r.json().catch(()=>({})));e&&(o=e)}console.error(e.red(o)),process.exit(1)}E.text("Downloading...");const n=Buffer.from(await r.arrayBuffer());await t.writeFile(j,n),a("Zip written to temp file"),E.text("Extracting...");new s(j).extractAllTo(y,!0),E.stop(),a("Extracted to target folder"),o.resolve(y)!==o.resolve(w)?(console.log(e.green(((e,t)=>o.resolve(e)===o.resolve(t)?h:`Application is successfully pulled into ${o.relative(t,e)}.`)(y,w))),process.chdir(y),a(`Changed working directory to ${y}`)):console.log(e.green(h)),i.yes?a("CI mode (--yes), skipping post-pull child commands"):await g(y,w)}catch(R){if(E.stop(),l(R),R instanceof Error&&R.message.startsWith("EXIT:"))throw R;if(m(R))return void console.log(e.yellow(f));console.error(e.red("Could not reach DashNex API. Check your connection and try again.")),process.exit(1)}finally{await t.remove(j).catch(()=>{}),a("Temp zip removed")}}}export{w as PullCommand};
@@ -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.27",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};
@@ -2,7 +2,6 @@ import { CliCommand } from '@dashnex/types';
2
2
  export interface CreateCommandOptions {
3
3
  template?: string;
4
4
  subdomain?: string;
5
- pull?: boolean;
6
5
  }
7
6
  export declare class CreateCommand implements CliCommand {
8
7
  pollApplicationCreated(applicationsUrl: string, token: string, interval?: number, timeout?: number): Promise<{
@@ -2,9 +2,6 @@ import { CliCommand } from '@dashnex/types';
2
2
  export interface PullCommandOptions {
3
3
  folder?: string;
4
4
  yes?: boolean;
5
- install?: boolean;
6
- migrate?: boolean;
7
- dev?: boolean;
8
5
  }
9
6
  export declare class PullCommand implements CliCommand {
10
7
  execute(options?: PullCommandOptions): Promise<void>;
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.27",
4
+ "version": "0.5.33",
5
5
  "description": "Command-line interface for DashNex framework",
6
6
  "homepage": "https://dashnex.io",
7
7
  "type": "module",